refactor: 所有业务线的接口调用添加TypeScript类型声明描述

This commit is contained in:
郝先瑞
2022-03-13 22:22:08 +08:00
parent 65035f584e
commit f2ca77992c
36 changed files with 1558 additions and 1214 deletions

View File

@@ -1,4 +1,4 @@
import { ClientFormData, ClientPageResult, ClientQueryParam } from '@/types/api/client' import { ClientFormData, ClientPageResult, ClientQueryParam } from '@/types/api/system/client'
import request from '@/utils/request' import request from '@/utils/request'
import { AxiosPromise } from 'axios' import { AxiosPromise } from 'axios'

View File

@@ -5,7 +5,7 @@ import request from '@/utils/request'
* *
* @param file * @param file
*/ */
export function uploadFile(file: any) { export function uploadFile(file: File) {
let formData = new FormData() let formData = new FormData()
formData.append('file', file) formData.append('file', file)
return request( return request(
@@ -24,7 +24,7 @@ export function uploadFile(file: any) {
* *
* @param path * @param path
*/ */
export function deleteFile(path: string) { export function deleteFile(path?: string) {
return request({ return request({
url: '/youlai-admin/api/v1/files', url: '/youlai-admin/api/v1/files',
method: 'delete', method: 'delete',

View File

@@ -1,6 +1,6 @@
import { PermFormData, PermItem, PermPageResult, PermQueryParam } from '@/types' import { PermFormData, PermItem, PermPageResult, PermQueryParam } from '@/types'
import request from '@/utils/request' import request from '@/utils/request'
import { Axios, AxiosPromise } from 'axios' import { AxiosPromise } from 'axios'
/** /**
* 获取权限分页列表 * 获取权限分页列表

View File

@@ -1,11 +1,13 @@
import { MemberPageResult, MemberQueryParam } from '@/types'
import request from '@/utils/request' import request from '@/utils/request'
import { AxiosPromise } from 'axios'
/** /**
* 获取会员分页列表 * 获取会员分页列表
* *
* @param queryParams * @param queryParams
*/ */
export function listMembersWithPage(queryParams:object) { export function listMemeberPages(queryParams: MemberQueryParam): AxiosPromise<MemberPageResult> {
return request({ return request({
url: '/mall-ums/api/v1/members', url: '/mall-ums/api/v1/members',
method: 'get', method: 'get',
@@ -18,7 +20,7 @@ export function listMembersWithPage(queryParams:object) {
* *
* @param id * @param id
*/ */
export function getMemberDetail(id:number) { export function getMemberFormDetail(id: number) {
return request({ return request({
url: '/mall-ums/api/v1/members/' + id, url: '/mall-ums/api/v1/members/' + id,
method: 'get' method: 'get'
@@ -30,7 +32,7 @@ export function getMemberDetail(id:number) {
* *
* @param data * @param data
*/ */
export function addMember(data:object) { export function addMember(data: object) {
return request({ return request({
url: '/mall-ums/api/v1/members', url: '/mall-ums/api/v1/members',
method: 'post', method: 'post',
@@ -44,7 +46,7 @@ export function addMember(data:object) {
* @param id * @param id
* @param data * @param data
*/ */
export function updateMember(id:number, data:object) { export function updateMember(id: number, data: object) {
return request({ return request({
url: '/mall-ums/api/v1/members/' + id, url: '/mall-ums/api/v1/members/' + id,
method: 'put', method: 'put',

View File

@@ -1,61 +1,70 @@
<template> <template>
<div class="component-container"> <div>
<!-- 上传组件 --> <!-- 上传组件 -->
<el-upload <el-upload
ref="uploadRef" ref="singleUploadRef"
action=""
class="single-uploader" class="single-uploader"
list-type="picture-card"
:show-file-list="false" :show-file-list="false"
:before-upload="handleBeforeUpload" :before-upload="handleBeforeUpload"
:on-exceed="handleExceed" :on-exceed="handleExceed"
:limit="1"
:http-request="uploadImage" :http-request="uploadImage"
> >
<img v-if="imgUrl" :src="imgUrl" class="single-uploader-image"/> <img v-if="imgUrl" :src="imgUrl" class="single-uploader__image" />
<el-icon v-else class="single-uploader-icon"> <el-icon v-else class="single-uploader__plus">
<Plus/> <Plus />
</el-icon> </el-icon>
<!-- 删除图标 --> <!-- 删除图标 -->
<el-icon <el-icon
v-if="imgUrl" v-if="props.showClose && imgUrl"
class="single-uploader-remove-icon" class="single-uploader__remove"
@click.stop="handleRemove(imgUrl)" @click.stop="handleRemove(imgUrl)"
> >
<Close/> <Close />
</el-icon> </el-icon>
</el-upload> </el-upload>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import {computed, ref} from "vue"; import { computed, ref } from "vue";
import {Plus, Close} from '@element-plus/icons-vue' import { Plus, Close } from "@element-plus/icons-vue";
import {ElMessage, ElUpload, UploadFile, UploadRequestOptions} from "element-plus" import {
import {uploadFile, deleteFile} from "@/api/system/file"; ElMessage,
ElUpload,
UploadFile,
UploadRawFile,
UploadRequestOptions,
} from "element-plus";
import { uploadFile, deleteFile } from "@/api/system/file";
const uploadRef = ref(ElUpload) const uploadRef = ref(ElUpload);
const emit = defineEmits(['update:modelValue']); const emit = defineEmits(["update:modelValue"]);
const props = defineProps({ const props = defineProps({
modelValue: { modelValue: {
type: String, type: String,
default: '' default: "",
} },
}) /**
* 是否显示右上角的删除图片按钮
*/
showClose: {
type: Boolean,
default: false,
},
});
const imgUrl = computed<string | null>({ const imgUrl = computed<string | null>({
get() { get() {
return props.modelValue return props.modelValue;
}, },
set(val) { set(val) {
// imgUrl改变时触发修改父组件绑定的v-model的值 // imgUrl改变时触发修改父组件绑定的v-model的值
emit('update:modelValue', val) emit("update:modelValue", val);
} },
}) });
/** /**
* 自定义图片上传 * 自定义图片上传
@@ -63,24 +72,8 @@ const imgUrl = computed<string | null>({
* @param params * @param params
*/ */
async function uploadImage(options: UploadRequestOptions): Promise<any> { async function uploadImage(options: UploadRequestOptions): Promise<any> {
const response=await uploadFile(options.file); const response = await uploadFile(options.file);
imgUrl.value=response.data; imgUrl.value = response.data;
}
/**
* 后选择文件覆盖前面的文件
*
* Set limit and on-exceed to automatically replace the previous file when select a new file.
*
* @param files
*/
function handleExceed(files: File[], uploadFiles: UploadFile[]) {
uploadRef.value.clearFiles()
uploadRef.value.handleStart(files[0])
uploadFile(files[0]).then(response => {
imgUrl.value = response.data
})
} }
/** /**
@@ -88,53 +81,55 @@ function handleExceed(files: File[], uploadFiles: UploadFile[]) {
* *
* @param file * @param file
*/ */
function handleRemove(file: string | null) { function handleRemove(file: UploadFile, fileList: UploadFile[]) {
if (file) { if (file) {
deleteFile(file) deleteFile(file.url);
imgUrl.value = null // 这里会触发imgUrl的computed的set方法 imgUrl.value = null; // 这里会触发imgUrl的computed的set方法
} }
} }
/**
function handleBeforeUpload(file: any) { * 在 before-upload 钩子中限制用户上传文件的格式和大小
const isJPG = file.type === 'image/jpeg' *
const isLt2M = file.size / 1024 / 1024 < 2 */
/* if (!isJPG) { function handleBeforeUpload(file: UploadRawFile) {
ElMessage.warning("此文件非图片文件") const isJPG = file.type === "image/jpeg";
return false const isLt2M = file.size / 1024 / 1024 < 2;
}*/
if (!isLt2M) { if (!isLt2M) {
ElMessage.warning("上传图片不能大于2M") ElMessage.warning("上传图片不能大于2M");
} }
return true return true;
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.single-uploader {
.component-container { border: 1px dashed #d9d9d9;
.single-uploader { border-radius: 6px;
cursor: pointer; cursor: pointer;
position: relative; position: relative;
overflow: hidden; overflow: hidden;
transition: var(--el-transition-duration-fast);
&:hover {
border-color: #409EFF;
}
&-icon {
font-size: 28px;
color: #8c939d;
text-align: center; text-align: center;
&:hover {
border-color: var(--el-color-primary);
} }
&-image { &__image {
width: 146px; width: 146px;
height: 146px; height: 146px;
display: block; display: block;
} }
&-remove-icon { &__plus {
width: 146px;
height: 157px;
font-size: 28px;
color: #8c939d;
text-align: center;
}
&__remove {
font-size: 12px; font-size: 12px;
color: #ff4d51 !important; color: #ff4d51 !important;
margin-top: 0px !important; margin-top: 0px !important;
@@ -143,6 +138,5 @@ function handleBeforeUpload(file: any) {
top: 0; top: 0;
color: #409eff; color: #409eff;
} }
}
} }
</style> </style>

6
src/types/api/index.d.ts vendored Normal file
View File

@@ -0,0 +1,6 @@
export * from './system'
export * from './pms'
export * from './sms'
export * from './ums'
export * from './oms'

58
src/types/api/oms/order.d.ts vendored Normal file
View File

@@ -0,0 +1,58 @@
import { PageQueryParam, PageResult } from "../base"
/**
* 订单查询参数类型声明
*/
export interface OrderQueryParam extends PageQueryParam {
orderSn: string | undefined;
status: number | undefined;
}
/**
* 订单分页列表项声明
*/
export interface Order {
id: string;
orderSn: string;
totalAmount: string;
payAmount: string;
payType: number;
status: number;
totalQuantity: number;
gmtCreate: string;
memberId: string;
sourceType: number;
orderItems: OrderItem[];
}
export interface OrderItem {
id: string;
orderId: string;
skuId: string;
skuName: string;
picUrl: string;
price: string;
count: number;
totalAmount: number;
}
/**
* 订单分页项类型声明
*/
export interface OrderPageResult extends PageResult<Order[]> {
}
/**
* 订单表单类型声明
*/
export interface OrderDetail {
id: number | undefined;
title: string;
picUrl: string;
beginTime: string;
endTime: string;
status: number;
sort: number;
url: string;
remark: string;
}

36
src/types/api/pms/brand.d.ts vendored Normal file
View File

@@ -0,0 +1,36 @@
import { PageQueryParam, PageResult } from "../base"
/**
* 品牌查询参数类型声明
*/
export interface BrandQueryParam extends PageQueryParam {
name: String | undefined
}
/**
* 品牌分页列表项声明
*/
export interface BrandItem {
id: string;
name: string;
logoUrl: string;
sort: number;
}
/**
* 品牌分页项类型声明
*/
export interface BrandPageResult extends PageResult<BrandItem[]> {
}
/**
* 品牌表单类型声明
*/
export interface BrandFormData {
id: number | undefined,
name: string,
logoUrl: string,
sort: number
}

73
src/types/api/pms/goods.d.ts vendored Normal file
View File

@@ -0,0 +1,73 @@
import { PageQueryParam, PageResult } from "../base"
/**
* 商品查询参数类型声明
*/
export interface GoodsQueryParam extends PageQueryParam {
name: stirng | undefined,
categoryId: number | undefined
}
/**
* 商品列表项类型声明
*/
export interface GoodsItem {
id: string;
name: string;
categoryId?: any;
brandId?: any;
originPrice: string;
price: string;
sales: number;
picUrl?: any;
album?: any;
unit?: any;
description: string;
detail: string;
status?: any;
categoryName: string;
brandName: string;
skuList: SkuItem[];
}
/**
* 商品规格项类型声明
*/
export interface SkuItem {
id: string;
skuSn?: any;
name: string;
spuId?: any;
specIds: string;
price: string;
stockNum: number;
lockedStockNum?: any;
picUrl?: any;
}
/**
* 商品分页项类型声明
*/
export interface GoodsPageResult extends PageResult<GoodsItem[]> {
}
/**
* 商品表单数据类型声明
*/
export interface GoodsFormData {
id: number|undefined,
deptId: number,
username: string,
nickname: string,
password: string,
mobile: string,
email: string,
gender: number,
status: number,
remark: string,
roleIds: number[]
}

40
src/types/api/sms/advert.d.ts vendored Normal file
View File

@@ -0,0 +1,40 @@
import { PageQueryParam, PageResult } from "../base"
/**
* 广告查询参数类型声明
*/
export interface AdvertQueryParam extends PageQueryParam {
name: String | undefined
}
/**
* 广告分页列表项声明
*/
export interface AdvertItem {
id: string;
name: string;
logoUrl: string;
sort: number;
}
/**
* 广告分页项类型声明
*/
export interface AdvertPageResult extends PageResult<AdvertItem[]> {
}
/**
* 广告表单类型声明
*/
export interface AdvertFormData {
id: number | undefined;
title: string;
picUrl: string;
beginTime: string;
endTime: string;
status: number;
sort: number;
url: string;
remark: string;
}

View File

@@ -1,4 +1,4 @@
import { PageQueryParam, PageResult } from "./base" import { PageQueryParam, PageResult } from "../base"
/** /**
* *

View File

@@ -28,7 +28,7 @@ export interface DeptItem {
* *
*/ */
export interface DeptFormData { export interface DeptFormData {
id: number, id: number|undefined,
parentId: number, parentId: number,
name: string, name: string,
sort: number, sort: number,

View File

@@ -1,4 +1,4 @@
import { PageQueryParam, PageResult } from "./base" import { PageQueryParam, PageResult } from "../base"
/** /**
* *

View File

@@ -1,4 +1,4 @@
import { PageQueryParam, PageResult } from "./base" import { PageQueryParam, PageResult } from "../base"
/** /**
* *
@@ -31,7 +31,7 @@ export interface PermPageResult extends PageResult<PermItem[]> {
* *
*/ */
export interface PermFormData { export interface PermFormData {
id: number, id: number|undefined,
name: string, name: string,
urlPerm: string, urlPerm: string,
btnPerm: string, btnPerm: string,

View File

@@ -1,4 +1,4 @@
import { PageQueryParam, PageResult } from "./base" import { PageQueryParam, PageResult } from "../base"
/** /**
* *
@@ -33,7 +33,7 @@ export interface RolePageResult extends PageResult<RoleItem[]> {
* *
*/ */
export interface RoleFormData { export interface RoleFormData {
id: number, id: number|undefined,
name: string, name: string,
code: string, code: string,
sort: number, sort: number,

View File

@@ -1,4 +1,4 @@
import { PageQueryParam, PageResult } from "./base" import { PageQueryParam, PageResult } from "../base"
/** /**
* *
@@ -48,7 +48,7 @@ export interface UserPageResult extends PageResult<UserItem[]> {
* *
*/ */
export interface UserFormData { export interface UserFormData {
id: number, id: number | undefined,
deptId: number, deptId: number,
username: string, username: string,
nickname: string, nickname: string,

1
src/types/api/ums/index.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
export * from './member'

66
src/types/api/ums/member.d.ts vendored Normal file
View File

@@ -0,0 +1,66 @@
import { PageQueryParam, PageResult } from "../base"
/**
* 会员查询参数类型声明
*/
export interface MemberQueryParam extends PageQueryParam {
nickName: String | undefined
}
/**
* 会员分页列表项声明
*/
export interface MemberItem {
id: string;
gender: number;
nickName: string;
mobile: string;
birthday?: any;
avatarUrl: string;
openid: string;
sessionKey?: any;
city: string;
country: string;
language: string;
province: string;
status: number;
balance: string;
deleted: number;
point: number;
addressList: AddressItem[];
}
export interface AddressItem {
id: string;
memberId: string;
consigneeName: string;
consigneeMobile: string;
province: string;
city: string;
area: string;
detailAddress: string;
zipCode?: any;
defaulted: number;
}
/**
* 会员分页项类型声明
*/
export interface MemberPageResult extends PageResult<MemberItem[]> {
}
/**
* 会员表单类型声明
*/
export interface MemberFormData {
id: number | undefined;
title: string;
picUrl: string;
beginTime: string;
endTime: string;
status: number;
sort: number;
url: string;
remark: string;
}

27
src/types/index.d.ts vendored
View File

@@ -1,12 +1,21 @@
export * from './api/login'
export * from './api/user' export * from './api/system/login'
export * from './api/role' export * from './api/system/user'
export * from './api/menu' export * from './api/system/role'
export * from './api/dept' export * from './api/system/menu'
export * from './api/dict' export * from './api/system/dept'
export * from './api/perm' export * from './api/system/dict'
export * from './api/client' export * from './api/system/perm'
export * from './api/system/client'
export * from './component' export * from './api/pms/goods'
export * from './api/pms/goods'
export * from './api/sms/advert'
export * from './api/oms/order'
export * from './common'

View File

@@ -1,13 +1,9 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<!-- 搜索表单 --> <!-- 搜索表单 -->
<el-form <el-form ref="queryFormRef" :model="queryParams" :inline="true">
ref="queryForm"
:model="queryParams"
:inline="true"
>
<el-form-item prop="orderSn"> <el-form-item prop="orderSn">
<el-input v-model="queryParams.orderSn" placeholder="订单号"/> <el-input v-model="queryParams.orderSn" placeholder="订单号" />
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
@@ -30,50 +26,45 @@
> >
<el-option <el-option
v-for="(key, value) in orderStatusMap" v-for="(key, value) in orderStatusMap"
:key="key"
:label="key" :label="key"
:value="value" :value="value"
/> />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" :icon="Search" @click="handleQuery">查询</el-button> <el-button type="primary" :icon="Search" @click="handleQuery"
>查询</el-button
>
<el-button :icon="Refresh" @click="resetQuery">重置</el-button> <el-button :icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<el-table <el-table ref="dataTable" v-loading="loading" :data="orderList" border>
ref="dataTable"
v-loading="loading"
:data="pageList"
border
>
<el-table-column type="expand" width="100" label="订单商品"> <el-table-column type="expand" width="100" label="订单商品">
<template #default="scope"> <template #default="scope">
<el-table <el-table :data="scope.row.orderItems" border>
:data="scope.row.orderItems" <el-table-column label="序号" type="index" width="100" />
border <el-table-column label="商品编号" align="center" prop="skuSn" />
> <el-table-column label="商品规格" align="center" prop="skuName" />
<el-table-column label="序号" type="index" width="100"/>
<el-table-column label="商品编号" align="center" prop="skuSn"/>
<el-table-column label="商品规格" align="center" prop="skuName"/>
<el-table-column label="图片" prop="picUrl"> <el-table-column label="图片" prop="picUrl">
<template slot-scope="scope"> <template #default="scope">
<img :src="scope.row.picUrl" width="40"> <img :src="scope.row.picUrl" width="40" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column align="center" label="单价" prop="price"> <el-table-column align="center" label="单价" prop="price">
<template slot-scope="scope">{{ scope.row.price}}</template> <template #default="scope">{{ scope.row.price }}</template>
</el-table-column> </el-table-column>
<el-table-column align="center" label="数量" prop="count"> <el-table-column align="center" label="数量" prop="count">
<template slot-scope="scope">{{ scope.row.count }}</template> <template #default="scope">{{ scope.row.count }}</template>
</el-table-column> </el-table-column>
</el-table> </el-table>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column align="center" prop="orderSn" label="订单编号"/> <el-table-column align="center" prop="orderSn" label="订单编号" />
<el-table-column align="center" prop="memberId" label="会员ID"/> <el-table-column align="center" prop="memberId" label="会员ID" />
<el-table-column align="center" label="订单来源"> <el-table-column align="center" label="订单来源">
<template #default="scope"> <template #default="scope">
@@ -105,7 +96,7 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column align="center" prop="gmtCreate" label="创建时间"/> <el-table-column align="center" prop="gmtCreate" label="创建时间" />
<el-table-column align="center" label="操作"> <el-table-column align="center" label="操作">
<template #default="scope"> <template #default="scope">
@@ -116,7 +107,7 @@
<!-- 分页工具条 --> <!-- 分页工具条 -->
<pagination <pagination
v-show="total>0" v-show="total > 0"
:total="total" :total="total"
v-model:page="queryParams.pageNum" v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize" v-model:limit="queryParams.pageSize"
@@ -126,40 +117,39 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, reactive, ref, toRefs } from "vue";
import { ElForm, ElMessage, ElMessageBox } from "element-plus";
import { Dialog, Order, OrderQueryParam } from "@/types";
import { listOrderPages, getOrderDetail } from "@/api/oms/order";
import { Search, Plus, Edit, Refresh, Delete } from "@element-plus/icons-vue";
import {listOrdersWithPage, getOrderDetail} from '@/api/oms/order' const queryFormRef = ref(ElForm);
import {onMounted, reactive, ref, toRefs} from "vue"
import {ElForm, ElMessage, ElMessageBox} from "element-plus"
import {Search, Plus, Edit, Refresh, Delete} from '@element-plus/icons-vue'
const dataForm = ref(ElForm) // 属性名必须和元素的ref属性值一致
const orderSourceMap = { const orderSourceMap = {
1: '微信小程序', 1: "微信小程序",
2: 'APP', 2: "APP",
3: 'PC' 3: "PC",
} };
const orderStatusMap = { const orderStatusMap = {
101: '待付款', 101: "待付款",
102: '用户取消', 102: "用户取消",
103: '系统取消', 103: "系统取消",
201: '已付款', 201: "已付款",
202: '申请退款', 202: "申请退款",
203: '已退款', 203: "已退款",
301: '待发货', 301: "待发货",
401: '已发货', 401: "已发货",
501: '用户收货', 501: "用户收货",
502: '系统收货', 502: "系统收货",
901: '已完成' 901: "已完成",
} };
const payTypeMap = { const payTypeMap = {
1: '支付宝', 1: "支付宝",
2: '微信', 2: "微信",
3: '会员余额' 3: "会员余额",
} };
const state = reactive({ const state = reactive({
loading: false, loading: false,
@@ -170,15 +160,13 @@ const state = reactive({
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
orderSn: undefined, } as OrderQueryParam,
status:undefined orderList: [] as Order[],
},
pageList: [],
total: 0, total: 0,
dialog: { dialog: {
title: '订单详情', title: "订单详情",
visible: false visible: false,
}, } as Dialog,
dialogVisible: false, dialogVisible: false,
orderDetail: { orderDetail: {
order: { order: {
@@ -193,57 +181,60 @@ const state = reactive({
gmtPay: undefined, gmtPay: undefined,
integralPrice: undefined, integralPrice: undefined,
payChannel: undefined, payChannel: undefined,
skuPrice : undefined, skuPrice: undefined,
couponPrice: undefined, couponPrice: undefined,
freightPrice: undefined, freightPrice: undefined,
orderPrice : undefined orderPrice: undefined,
}, },
member: {}, member: {},
orderItems: [] orderItems: [],
}, },
orderSourceMap, orderSourceMap,
orderStatusMap, orderStatusMap,
payTypeMap payTypeMap,
}) });
const {loading, single, multiple, queryParams, pageList, total, dialog,dateRange, orderDetail} = toRefs(state) const {
loading,
single,
multiple,
queryParams,
orderList,
total,
dialog,
dateRange,
orderDetail,
} = toRefs(state);
function handleQuery() { function handleQuery() {
state.loading = true state.loading = true;
listOrdersWithPage(state.queryParams).then(response => { listOrderPages(state.queryParams).then(({ data }) => {
const {data, total} = response as any state.orderList = data.list;
state.pageList = data state.total = data.total;
state.total = total state.loading = false;
state.loading = false });
})
} }
function resetQuery() { function resetQuery() {
state.queryParams = { queryFormRef.value.resetFields();
pageNum: 1, handleQuery();
pageSize: 10,
orderSn: undefined,
status: undefined
}
handleQuery()
} }
function viewDetail(row: any) { function viewDetail(row: any) {
state.dialog.visible = true state.dialog.visible = true;
getOrderDetail(row.id).then(response => { getOrderDetail(row.id).then((response: any) => {
state.orderDetail = response.data state.orderDetail = response.data;
}) });
} }
function cancel() { function cancel() {
state.dialog.visible = false state.dialog.visible = false;
} }
onMounted(() => { onMounted(() => {
handleQuery() handleQuery();
}) });
</script> </script>
<style scoped> <style scoped>
</style> </style>

View File

@@ -1,71 +1,59 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<!-- 搜索表单 --> <!-- 搜索表单 -->
<el-form <el-form ref="queryFormRef" :model="queryParams" :inline="true">
ref="queryFormRef"
:model="queryParams"
:inline="true"
>
<el-form-item> <el-form-item>
<el-button type="success" :icon="Plus" @click="handleAdd">新增</el-button> <el-button type="success" :icon="Plus" @click="handleAdd"
<el-button type="danger" :icon='Delete' click="handleDelete" :disabled="multiple">删除</el-button> >新增</el-button
>
<el-button
type="danger"
:icon="Delete"
click="handleDelete"
:disabled="multiple"
>删除</el-button
>
</el-form-item> </el-form-item>
<el-form-item prop="name"> <el-form-item prop="name">
<el-input v-model="queryParams.name" placeholder="品牌名称"/> <el-input v-model="queryParams.name" placeholder="品牌名称" />
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" :icon="Search" @click="handleQuery">搜索</el-button> <el-button type="primary" :icon="Search" @click="handleQuery"
>搜索</el-button
>
<el-button :icon="Refresh" @click="resetForm">重置</el-button> <el-button :icon="Refresh" @click="resetForm">重置</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<!-- 数据表格 --> <!-- 数据表格 -->
<el-table <el-table
ref="dataTable"
v-loading="loading" v-loading="loading"
:data="pageList" :data="brandList"
@selection-change="handleSelectionChange" @selection-change="handleSelectionChange"
@row-click="handleRowClick" @row-click="handleRowClick"
border border
> >
<el-table-column <el-table-column type="selection" min-width="5%" />
type="selection" <el-table-column prop="name" label="品牌名称" min-width="10" />
min-width="5%" <el-table-column prop="logoUrl" label="LOGO" min-width="10">
/>
<el-table-column
prop="name"
label="品牌名称"
min-width="10"
/>
<el-table-column
prop="logoUrl"
label="LOGO"
min-width="10"
>
<template #default="scope"> <template #default="scope">
<el-popover <el-popover placement="right" :width="400" trigger="hover">
placement="right" <img :src="scope.row.logoUrl" width="400" height="400" />
:width="400"
trigger="hover">
<img :src="scope.row.logoUrl" width="400" height="400"/>
<template #reference> <template #reference>
<img :src="scope.row.logoUrl" style="max-height: 60px;max-width: 60px"/> <img
:src="scope.row.logoUrl"
style="max-height: 60px; max-width: 60px"
/>
</template> </template>
</el-popover> </el-popover>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column prop="sort" label="排序" min-width="10" />
prop="sort"
label="排序"
min-width="10"
/>
<el-table-column <el-table-column label="操作" width="150">
label="操作"
width="150">
<template #default="scope"> <template #default="scope">
<el-button <el-button
@click="handleUpdate(scope.row)" @click="handleUpdate(scope.row)"
@@ -87,7 +75,7 @@
<!-- 分页工具条 --> <!-- 分页工具条 -->
<pagination <pagination
v-show="total>0" v-show="total > 0"
:total="total" :total="total"
v-model:page="queryParams.pageNum" v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize" v-model:limit="queryParams.pageSize"
@@ -108,15 +96,15 @@
label-width="100px" label-width="100px"
> >
<el-form-item label="品牌名称" prop="name"> <el-form-item label="品牌名称" prop="name">
<el-input v-model="formData.name" auto-complete="off"/> <el-input v-model="formData.name" auto-complete="off" />
</el-form-item> </el-form-item>
<el-form-item label="LOGO" prop="logoUrl"> <el-form-item label="LOGO" prop="logoUrl">
<single-upload v-model="formData.logoUrl"/> <single-upload v-model="formData.logoUrl" />
</el-form-item> </el-form-item>
<el-form-item label="排序" prop="sort"> <el-form-item label="排序" prop="sort">
<el-input v-model="formData.sort"/> <el-input v-model="formData.sort" />
</el-form-item> </el-form-item>
</el-form> </el-form>
@@ -131,15 +119,21 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import {listBrandsWithPage, getBrandDetail, updateBrand, addBrand, deleteBrands} from '@/api/pms/brand' import { onMounted, reactive, ref, toRefs, unref } from "vue";
import SingleUpload from "@/components/Upload/SingleUpload.vue" import { ElForm, ElTable, ElMessage, ElMessageBox } from "element-plus";
import {onMounted, reactive, ref, toRefs, unref} from "vue"; import { Search, Plus, Edit, Refresh, Delete } from "@element-plus/icons-vue";
import {ElForm, ElTable, ElMessage, ElMessageBox} from "element-plus"; import { BrandFormData, BrandItem, BrandQueryParam, Dialog } from "@/types";
import {Search, Plus, Edit, Refresh, Delete} from '@element-plus/icons-vue' import {
listBrandPages,
getBrandFormDetail,
updateBrand,
addBrand,
deleteBrands,
} from "@/api/pms/brand";
import SingleUpload from "@/components/Upload/SingleUpload.vue";
const dataTableRef = ref(ElTable) const queryFormRef = ref(ElForm); // 属性名必须和元素的ref属性值一致
const queryFormRef = ref(ElForm) // 属性名必须和元素的ref属性值一致 const dataFormRef = ref(ElForm); // 属性名必须和元素的ref属性值一致
const dataFormRef = ref(ElForm) // 属性名必须和元素的ref属性值一致
const state = reactive({ const state = reactive({
loading: true, loading: true,
@@ -152,130 +146,124 @@ const state = reactive({
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
name: undefined } as BrandQueryParam,
}, brandList: [] as BrandItem[],
pageList: [],
total: 0, total: 0,
dialog: { dialog: {} as Dialog,
title: '', formData: { sort: 1 } as BrandFormData,
visible: false
},
formData: {
id: undefined,
name: undefined,
logoUrl: undefined,
sort: 1
},
rules: { rules: {
name: [{ name: [
required: true, message: '请输入品牌名称', trigger: 'blur' {
}] required: true,
} message: "请输入品牌名称",
}) trigger: "blur",
},
],
},
});
const {loading, single, multiple, queryParams, pageList, total, dialog, formData, rules} = toRefs(state) const {
loading,
single,
multiple,
queryParams,
brandList,
total,
dialog,
formData,
rules,
} = toRefs(state);
function handleQuery() { function handleQuery() {
state.loading = true state.loading = true;
listBrandsWithPage(state.queryParams).then(response => { listBrandPages(state.queryParams).then(({ data }) => {
const {data, total} = response as any state.brandList = data.list;
state.pageList = data state.total = data.total;
state.total = total state.loading = false;
state.loading = false });
})
} }
function resetQuery() { function resetQuery() {
const dataTable = unref(dataTableRef) queryFormRef.value.resetFields();
dataTable.resetFields() handleQuery();
handleQuery()
}
function handleRowClick(row: any) {
const dataTable = unref(dataTableRef)
dataTable.toggleRowSelection(row);
} }
function handleSelectionChange(selection: any) { function handleSelectionChange(selection: any) {
state.ids = selection.map((item: any) => item.id) state.ids = selection.map((item: any) => item.id);
state.single = selection.length !== 1 state.single = selection.length !== 1;
state.multiple = !selection.length state.multiple = !selection.length;
} }
function handleAdd() { function handleAdd() {
state.dialog = { state.dialog = {
title: '添加品牌', title: "添加品牌",
visible: true visible: true,
} };
} }
function handleUpdate(row: any) { function handleUpdate(row: any) {
state.dialog = { state.dialog = {
title: '修改品牌', title: "修改品牌",
visible: true, visible: true,
} };
const advertId = row.id || state.ids const brandId = row.id || state.ids;
getBrandDetail(advertId).then((response) => { getBrandFormDetail(brandId).then(({ data }) => {
state.formData = response.data state.formData = data;
}) });
} }
function submitForm() { function submitForm() {
const dataForm = unref(dataFormRef) dataFormRef.value.validate((isValid: boolean) => {
dataForm.validate((valid: any) => { if (isValid) {
if (valid) {
if (state.formData.id) { if (state.formData.id) {
updateBrand(state.formData.id as any, state.formData).then(response => { updateBrand(state.formData.id, state.formData).then((response) => {
ElMessage.success('修改成功') ElMessage.success("修改成功");
state.dialog.visible = false cancel();
resetForm() handleQuery();
handleQuery() });
})
} else { } else {
addBrand(state.formData).then(response => { addBrand(state.formData).then((response) => {
ElMessage.success('新增成功') ElMessage.success("新增成功");
state.dialog.visible = false cancel();
resetForm() handleQuery();
handleQuery() });
})
} }
} }
}) });
} }
/** /**
* 重置表单 * 重置表单
*/ */
function resetForm() { function resetForm() {
const dataForm = unref(dataFormRef) state.formData.id = undefined;
dataForm.resetFields() dataFormRef.value.resetFields();
} }
function cancel() { function cancel() {
state.dialog.visible = false state.dialog.visible = false;
resetForm() resetForm();
} }
function handleDelete(row: any) { function handleDelete(row: any) {
const ids = [row.id || state.ids].join(',') const ids = [row.id || state.ids].join(",");
ElMessageBox.confirm('确认删除已选中的数据项?', '警告', { ElMessageBox.confirm("确认删除已选中的数据项?", "警告", {
confirmButtonText: '确定', confirmButtonText: "确定",
cancelButtonText: '取消', cancelButtonText: "取消",
type: 'warning' type: "warning",
}).then(() => {
deleteBrands(ids).then(() => {
ElMessage.success('删除成功')
handleQuery()
}) })
}).catch(() => .then(() => {
ElMessage.info('已取消删除') deleteBrands(ids).then(() => {
) ElMessage.success("删除成功");
handleQuery();
});
})
.catch(() => ElMessage.info("已取消删除"));
} }
onMounted(() => { onMounted(() => {
handleQuery() handleQuery();
}) });
</script> </script>
<style scoped> <style scoped>

View File

@@ -5,7 +5,7 @@
<template #header> <template #header>
<span>商品属性</span> <span>商品属性</span>
<el-button <el-button
style="float: right;" style="float: right"
type="success" type="success"
:icon="Plus" :icon="Plus"
size="small" size="small"
@@ -33,7 +33,7 @@
:prop="'attrList[' + scope.$index + '].name'" :prop="'attrList[' + scope.$index + '].name'"
:rules="rules.name" :rules="rules.name"
> >
<el-input v-model="scope.row.name"/> <el-input v-model="scope.row.name" />
</el-form-item> </el-form-item>
</template> </template>
</el-table-column> </el-table-column>
@@ -44,7 +44,7 @@
:prop="'attrList[' + scope.$index + '].value'" :prop="'attrList[' + scope.$index + '].value'"
:rules="rules.value" :rules="rules.value"
> >
<el-input v-model="scope.row.value"/> <el-input v-model="scope.row.value" />
</el-form-item> </el-form-item>
</template> </template>
</el-table-column> </el-table-column>
@@ -53,11 +53,13 @@
<template #default="scope"> <template #default="scope">
<el-form-item> <el-form-item>
<el-button <el-button
v-if="scope.$index>0" v-if="scope.$index > 0"
type="danger" type="danger"
icon="Minus" :icon="Minus"
size="small"
circle circle
@click="handleRemove(scope.$index)" plain
@click.stop="handleRemove(scope.$index)"
/> />
</el-form-item> </el-form-item>
</template> </template>
@@ -65,37 +67,39 @@
</el-table> </el-table>
</el-form> </el-form>
</el-card> </el-card>
</div> </div>
<div class="component-container__footer"> <div class="component-container__footer">
<el-button @click="handlePrev">上一步填写商品信息</el-button> <el-button @click="handlePrev">上一步填写商品信息</el-button>
<el-button type="primary" @click="handleNext">下一步设置商品库存</el-button> <el-button type="primary" @click="handleNext"
>下一步设置商品库存</el-button
>
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { listAttributes } from "@/api/pms/attribute";
import { computed, nextTick, reactive, ref, toRefs, unref, watch } from "vue";
import { ElForm } from "element-plus";
import { Plus, Minus } from "@element-plus/icons-vue";
import {listAttributes} from "@/api/pms/attribute"; const emit = defineEmits(["prev", "next"]);
import {computed, nextTick, reactive, ref, toRefs, unref, watch} from "vue"; const dataForm = ref(ElForm);
import {ElForm} from "element-plus";
import {Minus, Plus} from '@element-plus/icons-vue'
const emit = defineEmits(['prev', 'next'])
const dataForm = ref(ElForm)
const props = defineProps({ const props = defineProps({
modelValue: { modelValue: {
type: Object, type: Object,
default: {} default: {},
} },
}) });
const categoryId = computed(() => props.modelValue.categoryId); const categoryId = computed(() => props.modelValue.categoryId);
watch(categoryId, (newVal) => { watch(
categoryId,
(newVal) => {
// 商品编辑不加载分类下的属性 // 商品编辑不加载分类下的属性
const spuId = props.modelValue.id const spuId = props.modelValue.id;
if (spuId) { if (spuId) {
return false; return false;
} }
@@ -103,65 +107,59 @@ watch(categoryId, (newVal) => {
// 商品新增加载默认分类下的属性 // 商品新增加载默认分类下的属性
if (newVal) { if (newVal) {
// type=2 商品分类下的属性 // type=2 商品分类下的属性
listAttributes({categoryId: newVal, type: 2}).then(response => { listAttributes({ categoryId: newVal, type: 2 }).then((response) => {
const attrList = response.data const attrList = response.data;
if (attrList && attrList.length > 0) { if (attrList && attrList.length > 0) {
props.modelValue.attrList = attrList props.modelValue.attrList = attrList;
} else { } else {
props.modelValue.attrList = [{}] props.modelValue.attrList = [{}];
} }
}) });
} else { } else {
props.modelValue.attrList = [{}] props.modelValue.attrList = [{}];
} }
}, },
{ {
immediate: true, immediate: true,
deep: true deep: true,
} }
) );
const state = reactive({ const state = reactive({
rules: { rules: {
name: [ name: [{ required: true, message: "请填写属性名称", trigger: "blur" }],
{required: true, message: '请填写属性名称', trigger: 'blur'} value: [{ required: true, message: "请填写属性值", trigger: "blur" }],
], },
value: [ });
{required: true, message: '请填写属性值', trigger: 'blur'}
]
}
})
const {rules} = toRefs(state) const { rules } = toRefs(state);
function handleAdd() { function handleAdd() {
props.modelValue.attrList.push({}) props.modelValue.attrList.push({});
} }
function handleRemove(index: number) { function handleRemove(index: number) {
props.modelValue.attrList.splice(index, 1) props.modelValue.attrList.splice(index, 1);
} }
function handlePrev() { function handlePrev() {
emit('prev') emit("prev");
} }
function handleNext() { function handleNext() {
const form = unref(dataForm) const form = unref(dataForm);
form.validate((valid: any) => { form.validate((valid: any) => {
if (valid) { if (valid) {
emit('next') emit("next");
} }
}) });
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.component-container { .component-container {
&__main { &__main {
margin: 20px auto margin: 20px auto;
} }
&__footer { &__footer {

View File

@@ -10,7 +10,7 @@
<el-form-item label="商品品牌" prop="brandId"> <el-form-item label="商品品牌" prop="brandId">
<el-select <el-select
v-model="modelValue.brandId" v-model="modelValue.brandId"
style="width:400px" style="width: 400px"
clearable clearable
> >
<el-option <el-option
@@ -23,110 +23,127 @@
</el-form-item> </el-form-item>
<el-form-item label="商品名称" prop="name"> <el-form-item label="商品名称" prop="name">
<el-input style="width: 400px" v-model="modelValue.name"/> <el-input style="width: 400px" v-model="modelValue.name" />
</el-form-item> </el-form-item>
<el-form-item label="原价" prop="originPrice"> <el-form-item label="原价" prop="originPrice">
<el-input style="width: 400px" v-model="modelValue.originPrice"/> <el-input style="width: 400px" v-model="modelValue.originPrice" />
</el-form-item> </el-form-item>
<el-form-item label="现价" prop="price"> <el-form-item label="现价" prop="price">
<el-input style="width: 400px" v-model="modelValue.price"/> <el-input style="width: 400px" v-model="modelValue.price" />
</el-form-item> </el-form-item>
<el-form-item label="商品简介"> <el-form-item label="商品简介">
<el-input type="textarea" :autosize="{ minRows: 2, maxRows: 6 }" v-model="modelValue.description"/> <el-input
type="textarea"
:autosize="{ minRows: 3, maxRows: 6 }"
v-model="modelValue.description"
/>
</el-form-item> </el-form-item>
<el-form-item label="商品相册"> <el-form-item label="商品相册">
<el-card v-for="(item,index) in pictures" style="width: 170px;display: inline-block;margin-left: 10px" <el-card
:body-style="{ padding: '10px' }"> v-for="(item, index) in pictures"
:key="index"
style="width: 170px; display: inline-block; margin-left: 10px;text-align:center"
:body-style="{ padding: '10px' }"
>
<single-upload v-model="item.url" :show-close="true" />
<single-upload v-model="item.url"/> <div v-if="item.url">
<el-button
<div class="bottom" v-if="item.url"> type="text"
<el-button type="text" class="button" v-if="item.main==true" style="color:#ff4d51">商品主图</el-button> class="button"
<el-button type="text" class="button" v-else @click="changeMainPicture(index)">设为主图</el-button> v-if="item.main == true"
<el-button type="text" class="button" @click="removePicture(index)">删除图片</el-button> style="color: #ff4d51"
>商品主图</el-button
>
<el-button
type="text"
class="button"
v-else
@click="changeMainPicture(index)"
>设为主图</el-button
>
</div> </div>
<div class="bottom" v-else> <div v-else>
<!-- 占位 --> <!-- 占位 -->
<el-button type="text"/> <el-button type="text" />
</div> </div>
</el-card> </el-card>
</el-form-item> </el-form-item>
<el-form-item label="商品详情" prop="detail"> <el-form-item label="商品详情" prop="detail">
<editor v-model="modelValue.detail" style="height: 600px"/> <editor v-model="modelValue.detail" style="height: 600px" />
</el-form-item> </el-form-item>
</el-form> </el-form>
</div> </div>
<div class="component-container__footer"> <div class="component-container__footer">
<el-button @click="handlePrev">上一步选择商品分类</el-button> <el-button @click="handlePrev">上一步选择商品分类</el-button>
<el-button type="primary" @click="handleNext">下一步设置商品属性</el-button> <el-button type="primary" @click="handleNext"
>下一步设置商品属性</el-button
>
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, reactive, ref, toRefs } from "vue";
import {onMounted, reactive, ref, toRefs, unref} from "vue" import { ElForm } from "element-plus";
import {ElForm} from "element-plus"
// API 依赖 // API 依赖
import {listBrands} from "@/api/pms/brand" import { listBrands } from "@/api/pms/brand";
// 自定义组件依赖 // 自定义组件依赖
import SingleUpload from '@/components/Upload/SingleUpload.vue' import Editor from "@/components/WangEditor/index.vue";
import Editor from '@/components/WangEditor/index.vue' import SingleUpload from "@/components/Upload/SingleUpload.vue";
const emit = defineEmits(['prev', 'next']) const emit = defineEmits(["prev", "next"]);
const dataFormRef = ref(ElForm) const dataFormRef = ref(ElForm);
const props = defineProps({ const props = defineProps({
modelValue: { modelValue: {
type: Object, type: Object,
default: {} default: {},
} },
}) });
const state = reactive({ const state = reactive({
brandOptions: [] as Array<any>, brandOptions: [] as Array<any>,
// 商品图册 // 商品图册
pictures: [ pictures: [
{url: undefined, main: true}, // main = true 代表主图,可切换 { url: undefined, main: true }, // main = true 代表主图,可切换
{url: undefined, main: false}, { url: undefined, main: false },
{url: undefined, main: false}, { url: undefined, main: false },
{url: undefined, main: false}, { url: undefined, main: false },
{url: undefined, main: false} { url: undefined, main: false },
] as Array<any>, ] as Array<any>,
rules: { rules: {
name: [{required: true, message: '请填写商品名称', trigger: 'blur'}], name: [{ required: true, message: "请填写商品名称", trigger: "blur" }],
originPrice: [{required: true, message: '请填写原价', trigger: 'blur'}], originPrice: [{ required: true, message: "请填写原价", trigger: "blur" }],
price: [{required: true, message: '请填写现价', trigger: 'blur'}], price: [{ required: true, message: "请填写现价", trigger: "blur" }],
brandId: [{required: true, message: '请选择商品品牌', trigger: 'blur'}], brandId: [{ required: true, message: "请选择商品品牌", trigger: "blur" }],
} },
}) });
const {brandOptions, pictures, rules} = toRefs(state) const { brandOptions, pictures, rules } = toRefs(state);
function loadData() { function loadData() {
listBrands({}).then(response => { listBrands().then(({data}) => {
state.brandOptions = response.data state.brandOptions = data;
}) });
const goodsInfo = props.modelValue const goodsInfo = props.modelValue;
console.log('商品信息', goodsInfo) const goodsId = goodsInfo.id;
const goodsId = goodsInfo.id
if (goodsId) { if (goodsId) {
const mainPicUrl = goodsInfo.picUrl const mainPicUrl = goodsInfo.picUrl;
if (mainPicUrl) { if (mainPicUrl) {
state.pictures.filter(item => item.main)[0].url = mainPicUrl state.pictures.filter((item) => item.main)[0].url = mainPicUrl;
} }
const subPicUrls = goodsInfo.subPicUrls const subPicUrls = goodsInfo.subPicUrls;
if (subPicUrls && subPicUrls.length > 0) { if (subPicUrls && subPicUrls.length > 0) {
for (let i = 1; i <= subPicUrls.length; i++) { for (let i = 1; i <= subPicUrls.length; i++) {
state.pictures[i].url = subPicUrls[i - 1] state.pictures[i].url = subPicUrls[i - 1];
} }
} }
} }
@@ -134,63 +151,65 @@ function loadData() {
function resetForm() { function resetForm() {
state.pictures = [ state.pictures = [
{url: undefined, main: true}, // main 代表主图,可以切换 { url: undefined, main: true }, // main 代表主图,可以切换
{url: undefined, main: false}, { url: undefined, main: false },
{url: undefined, main: false}, { url: undefined, main: false },
{url: undefined, main: false}, { url: undefined, main: false },
{url: undefined, main: false} { url: undefined, main: false },
] ];
} }
/** /**
* 切换主图 * 切换主图
*/ */
function changeMainPicture(changeIndex: number) { function changeMainPicture(changeIndex: number) {
const currMainPicture = JSON.parse(JSON.stringify(state.pictures[0])) const currMainPicture = JSON.parse(JSON.stringify(state.pictures[0]));
const nextMainPicture = JSON.parse(JSON.stringify(state.pictures[changeIndex])) const nextMainPicture = JSON.parse(
JSON.stringify(state.pictures[changeIndex])
);
state.pictures[0].url = nextMainPicture.url state.pictures[0].url = nextMainPicture.url;
state.pictures[changeIndex].url = currMainPicture.url state.pictures[changeIndex].url = currMainPicture.url;
}
function removePicture(index: number) {
state.pictures[index].url = undefined
} }
function handlePrev() { function handlePrev() {
emit('prev') emit("prev");
} }
function handleNext() { function handleNext() {
dataFormRef.value.validate((valid: any) => { dataFormRef.value.validate((valid: any) => {
if (valid) { if (valid) {
// 商品图片 // 商品图片
const mainPicUrl = state.pictures.filter(item => item.main == true && item.url).map(item => item.url) const mainPicUrl = state.pictures
.filter((item) => item.main == true && item.url)
.map((item) => item.url);
if (mainPicUrl && mainPicUrl.length > 0) { if (mainPicUrl && mainPicUrl.length > 0) {
props.modelValue.picUrl = mainPicUrl[0] props.modelValue.picUrl = mainPicUrl[0];
} }
const subPicUrl = state.pictures.filter(item => item.main == false && item.url).map(item => item.url) const subPicUrl = state.pictures
.filter((item) => item.main == false && item.url)
.map((item) => item.url);
if (subPicUrl && subPicUrl.length > 0) { if (subPicUrl && subPicUrl.length > 0) {
props.modelValue.subPicUrls = subPicUrl props.modelValue.subPicUrls = subPicUrl;
} }
emit('next') emit("next");
} }
}) });
} }
onMounted(() => { onMounted(() => {
loadData() loadData();
}) });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.component-container { .component-container {
&__main { &__main {
margin: 20px auto; margin: 20px auto;
.button { .button {
margin-left: 10px; margin-left: 10px;
} }
} }
@@ -198,6 +217,7 @@ onMounted(() => {
position: fixed; position: fixed;
bottom: 20px; bottom: 20px;
right: 20px; right: 20px;
} }
} }
</style> </style>

View File

@@ -9,7 +9,7 @@
type="success" type="success"
@click="handleSpecAdd" @click="handleSpecAdd"
size="small" size="small"
style="float: right;" style="float: right"
> >
添加规格项 添加规格项
</el-button> </el-button>
@@ -29,7 +29,7 @@
> >
<el-table-column align="center" width="50"> <el-table-column align="center" width="50">
<template> <template>
<svg-icon class="drag-handler" icon-class="drag"/> <svg-icon class="drag-handler" icon-class="drag" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="规格名" width="200"> <el-table-column label="规格名" width="200">
@@ -50,34 +50,41 @@
<el-table-column> <el-table-column>
<template #header> <template #header>
规格值 规格值
<el-link type="danger" style="font-size:12px" :underline="false">默认第一条规格包含图片</el-link> <el-link
type="danger"
style="font-size: 12px"
:underline="false"
>默认第一条规格包含图片</el-link
>
</template> </template>
<template #default="scope"> <template #default="scope">
<div v-for="item in scope.row.values" <div
style="margin-right:15px;display: inline-block" v-for="item in scope.row.values"
:key="item.id"
style="margin-right: 15px; display: inline-block"
> >
<el-tag <el-tag
size="small" size="small"
closable closable
:type="colors[scope.$index%colors.length]" :type="colors[scope.$index % colors.length]"
@close="handleSpecValueRemove(scope.$index,item.id)" @close="handleSpecValueRemove(scope.$index, item.id)"
> >
{{ item.value }} {{ item.value }}
</el-tag> </el-tag>
<single-upload <single-upload
v-model="item.picUrl" v-model="item.picUrl"
v-if="scope.$index==0" v-if="scope.$index == 0"
style="margin-top: 5px" style="margin-top: 5px"
/> />
</div> </div>
<el-input <el-input
v-if="tagInputs.length>0 &&tagInputs[scope.$index].visible" v-if="tagInputs.length > 0 && tagInputs[scope.$index].visible"
v-model="tagInputs[scope.$index].value" v-model="tagInputs[scope.$index].value"
@keyup.enter.native="handleSpecValueInput(scope.$index)" @keyup.enter="handleSpecValueInput(scope.$index)"
@blur="handleSpecValueInput(scope.$index)" @blur="handleSpecValueInput(scope.$index)"
style="width: 80px;vertical-align: top" style="width: 80px; vertical-align: top"
size="small" size="small"
/> />
<el-button <el-button
@@ -100,7 +107,8 @@
size="small" size="small"
circle circle
plain plain
@click.stop="handleSpecRemove(scope.$index)"/> @click.stop="handleSpecRemove(scope.$index)"
/>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@@ -111,12 +119,7 @@
<template #header> <template #header>
<span>商品库存</span> <span>商品库存</span>
</template> </template>
<el-form <el-form ref="skuFormRef" :model="skuForm" size="small" :inline="true">
ref="skuFormRef"
:model="skuForm"
size="small"
:inline="true"
>
<el-table <el-table
:data="skuForm.skuList" :data="skuForm.skuList"
:span-method="objectSpanMethod" :span-method="objectSpanMethod"
@@ -124,43 +127,48 @@
size="small" size="small"
border border
> >
<el-table-column <el-table-column
v-for="(title,index) in specTitles" v-for="(title, index) in specTitles"
:key="index"
align="center" align="center"
:prop="'specValue'+(index+1)" :prop="'specValue' + (index + 1)"
:label="title"> :label="title"
>
</el-table-column> </el-table-column>
<el-table-column <el-table-column label="商品编码" align="center">
label="商品编码"
align="center"
>
<template #default="scope"> <template #default="scope">
<el-form-item :prop="'skuList['+scope.$index+'].skuSn'" :rules="rules.sku.skuSn"> <el-form-item
<el-input v-model="scope.row.skuSn"/> :prop="'skuList[' + scope.$index + '].skuSn'"
:rules="rules.sku.skuSn"
>
<el-input v-model="scope.row.skuSn" />
</el-form-item> </el-form-item>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="价格" align="center"> <el-table-column label="价格" align="center">
<template #default="scope"> <template #default="scope">
<el-form-item :prop="'skuList['+scope.$index+'].price'" :rules="rules.sku.price"> <el-form-item
<el-input v-model="scope.row.price"/> :prop="'skuList[' + scope.$index + '].price'"
:rules="rules.sku.price"
>
<el-input v-model="scope.row.price" />
</el-form-item> </el-form-item>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="库存" align="center"> <el-table-column label="库存" align="center">
<template #default="scope"> <template #default="scope">
<el-form-item :prop="'skuList['+scope.$index+'].stockNum'" :rules="rules.sku.stockNum"> <el-form-item
<el-input v-model="scope.row.stockNum"/> :prop="'skuList[' + scope.$index + '].stockNum'"
:rules="rules.sku.stockNum"
>
<el-input v-model="scope.row.stockNum" />
</el-form-item> </el-form-item>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
</el-form> </el-form>
</el-card> </el-card>
</div> </div>
@@ -168,144 +176,159 @@
<el-button @click="handlePrev">上一步设置商品属性</el-button> <el-button @click="handlePrev">上一步设置商品属性</el-button>
<el-button type="primary" @click="submitForm">提交</el-button> <el-button type="primary" @click="submitForm">提交</el-button>
</div> </div>
</div> </div>
</template> </template>
<script setup> <script setup>
import {computed, getCurrentInstance, nextTick, onMounted, reactive, ref, toRefs, unref, watch} from "vue"; import {
import {useRouter} from "vue-router"; computed,
import {Plus, Minus} from '@element-plus/icons-vue' getCurrentInstance,
import {ElNotification, ElMessage, ElTable, ElForm} from "element-plus" nextTick,
onMounted,
reactive,
ref,
toRefs,
unref,
watch,
} from "vue";
import { useRouter } from "vue-router";
import { Plus, Minus } from "@element-plus/icons-vue";
import { ElNotification, ElMessage, ElTable, ElForm } from "element-plus";
// API 引用 // API 引用
import {listAttributes} from "@/api/pms/attribute"; import { listAttributes } from "@/api/pms/attribute";
import {addGoods, updateGoods} from "@/api/pms/goods"; import { addGoods, updateGoods } from "@/api/pms/goods";
// 自定义组件引用 // 自定义组件引用
import SvgIcon from '@/components/SvgIcon/index.vue' import SvgIcon from "@/components/SvgIcon/index.vue";
import SingleUpload from '@/components/Upload/SingleUpload.vue' import SingleUpload from "@/components/Upload/SingleUpload.vue";
// import Sortable from 'sortablejs' // import Sortable from 'sortablejs'
const categoryId = computed(() => props.modelValue.categoryId); const categoryId = computed(() => props.modelValue.categoryId);
const emit = defineEmits(['prev', 'next']) const emit = defineEmits(["prev", "next"]);
const proxy = getCurrentInstance() const proxy = getCurrentInstance();
const router = useRouter() const router = useRouter();
const specTableRef = ref(ElTable) const specTableRef = ref(ElTable);
const specFormRef = ref(ElForm) const specFormRef = ref(ElForm);
const skuFormRef = ref(ElForm) const skuFormRef = ref(ElForm);
const props = defineProps({ const props = defineProps({
modelValue: { modelValue: {
type: Object, type: Object,
default: {} default: {},
} },
}) });
const state = reactive({ const state = reactive({
specForm: { specForm: {
specList: [] specList: [],
}, },
skuForm: { skuForm: {
skuList: [] skuList: [],
}, },
// 规格项表格标题 // 规格项表格标题
specTitles: [], specTitles: [],
rules: { rules: {
spec: { spec: {
name: [{required: true, message: '请输入规格名称', trigger: 'blur'}], name: [{ required: true, message: "请输入规格名称", trigger: "blur" }],
value: [{required: true, message: '请输入规格值', trigger: 'blur'}] value: [{ required: true, message: "请输入规格值", trigger: "blur" }],
}, },
sku: { sku: {
skuSn: [{required: true, message: '请输入商品编号', trigger: 'blur'}], skuSn: [{ required: true, message: "请输入商品编号", trigger: "blur" }],
price: [{required: true, message: '请输入商品价格', trigger: 'blur'}], price: [{ required: true, message: "请输入商品价格", trigger: "blur" }],
stockNum: [{required: true, message: '请输入商品库存', trigger: 'blur'}], stockNum: [
} { required: true, message: "请输入商品库存", trigger: "blur" },
],
}, },
colors: ['', 'success', 'warning', 'danger'], },
tagInputs: [{value: undefined, visible: false}], // 规格值标签临时值和显隐控制 colors: ["", "success", "warning", "danger"],
loading: undefined tagInputs: [{ value: undefined, visible: false }], // 规格值标签临时值和显隐控制
}) loading: undefined,
});
const {specForm, skuForm, specTitles, rules, colors, tagInputs, loading} = toRefs(state) const { specForm, skuForm, specTitles, rules, colors, tagInputs, loading } =
toRefs(state);
watch(categoryId, (newVal, oldVal) => { watch(
categoryId,
(newVal, oldVal) => {
// 商品编辑不加载分类下的规格 // 商品编辑不加载分类下的规格
const spuId = props.modelValue.id const spuId = props.modelValue.id;
if (spuId) { if (spuId) {
return false; return false;
} }
if (newVal) { if (newVal) {
// type=1 商品分类下的规格 // type=1 商品分类下的规格
listAttributes({categoryId: newVal, type: 1}).then(response => { listAttributes({ categoryId: newVal, type: 1 }).then((response) => {
const specList = response.data const specList = response.data;
if (specList && specList.length > 0) { if (specList && specList.length > 0) {
specList.forEach((item) => { specList.forEach((item) => {
state.specForm.specList.push({ state.specForm.specList.push({
name: item.name, name: item.name,
values: [] values: [],
}) });
}) });
loadData() loadData();
} }
}) });
} }
}, },
{ {
immediate: true, immediate: true,
deep: true deep: true,
} }
) );
function loadData() { function loadData() {
props.modelValue.specList.forEach((specItem) => { props.modelValue.specList.forEach((specItem) => {
const specIndex = state.specForm.specList.findIndex(item => item.name == specItem.name) const specIndex = state.specForm.specList.findIndex(
(item) => item.name == specItem.name
);
if (specIndex > -1) { if (specIndex > -1) {
state.specForm.specList[specIndex].values.push({ state.specForm.specList[specIndex].values.push({
id: specItem.id, id: specItem.id,
value: specItem.value, value: specItem.value,
picUrl: specItem.picUrl picUrl: specItem.picUrl,
}) });
} else { } else {
state.specForm.specList.push({ state.specForm.specList.push({
name: specItem.name, name: specItem.name,
values: [{id: specItem.id, value: specItem.value, picUrl: specItem.picUrl}] values: [
}) { id: specItem.id, value: specItem.value, picUrl: specItem.picUrl },
],
});
} }
}) });
// 每个规格项追加一个添加规格值按钮 // 每个规格项追加一个添加规格值按钮
for (let i = 0; i < state.specForm.specList.length; i++) { for (let i = 0; i < state.specForm.specList.length; i++) {
state.tagInputs.push({'value': undefined, 'visible': false}) state.tagInputs.push({ value: undefined, visible: false });
} }
// SKU规格ID拼接字符串处理 // SKU规格ID拼接字符串处理
props.modelValue.skuList.forEach((sku) => { props.modelValue.skuList.forEach((sku) => {
sku.specIdArr = sku.specIds.split('_') sku.specIdArr = sku.specIds.split("_");
}) });
generateSkuList() generateSkuList();
handleSpecChange() handleSpecChange();
handleSpecReorder() handleSpecReorder();
nextTick(() => { nextTick(() => {
// registerSpecDragSortEvent() // registerSpecDragSortEvent()
}) });
} }
/** /**
* 生成SKU列表的title * 生成SKU列表的title
*/ */
function handleSpecChange() { function handleSpecChange() {
const specList = JSON.parse(JSON.stringify(state.specForm.specList)) const specList = JSON.parse(JSON.stringify(state.specForm.specList));
state.specTitles = specList.map((item) => item.name) state.specTitles = specList.map((item) => item.name);
} }
/** /**
@@ -313,8 +336,8 @@ function handleSpecChange() {
*/ */
function handleSpecReorder() { function handleSpecReorder() {
state.specForm.specList.forEach((item, index) => { state.specForm.specList.forEach((item, index) => {
item.index = index item.index = index;
}) });
} }
/** /**
@@ -349,56 +372,71 @@ function handleSpecReorder() {
* ] * ]
*/ */
function generateSkuList() { function generateSkuList() {
const specList = JSON.parse(JSON.stringify(state.specForm.specList.filter(item => item.values.length > 0))) // 深拷贝取有属性的规格项否则笛卡尔积运算得到的SKU列表值为空 const specList = JSON.parse(
JSON.stringify(
state.specForm.specList.filter((item) => item.values.length > 0)
)
); // 深拷贝取有属性的规格项否则笛卡尔积运算得到的SKU列表值为空
// 如果规格为空生成SKU列表为空 // 如果规格为空生成SKU列表为空
if (specList.length === 0) { if (specList.length === 0) {
state.skuForm.skuList = [] state.skuForm.skuList = [];
return return;
} }
const skuList = specList.reduce((acc, curr) => { const skuList = specList.reduce(
let result = [] (acc, curr) => {
let result = [];
acc.forEach((item) => { acc.forEach((item) => {
// curr => { 'id':1,'name':'颜色','values':[{id:1,value:'白色'},{id:2,value:'黑色'},{id:3,value:'蓝色'}] } // curr => { 'id':1,'name':'颜色','values':[{id:1,value:'白色'},{id:2,value:'黑色'},{id:3,value:'蓝色'}] }
curr.values.forEach((v) => { // v=>{id:1,value:'白色'} curr.values.forEach((v) => {
let temp = Object.assign({}, item) // v=>{id:1,value:'白色'}
temp.specValues += v.value + '_' // 规格值拼接 let temp = Object.assign({}, item);
temp.specIds += v.id + '|' // 规格ID拼接 temp.specValues += v.value + "_"; // 规格拼接
result.push(temp) temp.specIds += v.id + "|"; // 规格ID拼接
}) result.push(temp);
}) });
return result });
}, [{specValues: '', specIds: ''}]) return result;
},
[{ specValues: "", specIds: "" }]
);
skuList.forEach((item) => { skuList.forEach((item) => {
item.specIds = item.specIds.substring(0, item.specIds.length - 1) item.specIds = item.specIds.substring(0, item.specIds.length - 1);
item.name = item.specValues.substring(0, item.specValues.length - 1).replaceAll('_', ' ') item.name = item.specValues
const specIdArr = item.specIds.split('|') .substring(0, item.specValues.length - 1)
const skus = props.modelValue.skuList.filter((sku) => .replaceAll("_", " ");
const specIdArr = item.specIds.split("|");
const skus = props.modelValue.skuList.filter(
(sku) =>
sku.specIdArr.length === specIdArr.length && sku.specIdArr.length === specIdArr.length &&
sku.specIdArr.every((a) => specIdArr.some((b) => a === b)) && sku.specIdArr.every((a) => specIdArr.some((b) => a === b)) &&
specIdArr.every((x) => sku.specIdArr.some((y) => x === y)) specIdArr.every((x) => sku.specIdArr.some((y) => x === y))
) // 数据库的SKU列表 ); // 数据库的SKU列表
if (skus && skus.length > 0) { if (skus && skus.length > 0) {
const sku = skus[0] const sku = skus[0];
item.id = sku.id item.id = sku.id;
item.skuSn = sku.skuSn item.skuSn = sku.skuSn;
item.price = sku.price / 100 item.price = sku.price / 100;
item.stockNum = sku.stockNum item.stockNum = sku.stockNum;
} }
const specValueArr = item.specValues.substring(0, item.specValues.length - 1).split('_') // ['黑','6+128G','官方标配'] const specValueArr = item.specValues
.substring(0, item.specValues.length - 1)
.split("_"); // ['黑','6+128G','官方标配']
specValueArr.forEach((v, i) => { specValueArr.forEach((v, i) => {
const key = 'specValue' + (i + 1) const key = "specValue" + (i + 1);
item[key] = v item[key] = v;
if (i == 0 && state.specForm.specList.length > 0) { if (i == 0 && state.specForm.specList.length > 0) {
const valueIndex = state.specForm.specList[0].values.findIndex((specValue) => specValue.value == v) const valueIndex = state.specForm.specList[0].values.findIndex(
(specValue) => specValue.value == v
);
if (valueIndex > -1) { if (valueIndex > -1) {
item.picUrl = state.specForm.specList[0].values[valueIndex].picUrl item.picUrl = state.specForm.specList[0].values[valueIndex].picUrl;
} }
} }
}) });
}) });
state.skuForm.skuList = JSON.parse(JSON.stringify(skuList)) state.skuForm.skuList = JSON.parse(JSON.stringify(skuList));
} }
/** /**
@@ -406,12 +444,12 @@ function generateSkuList() {
*/ */
function handleSpecAdd() { function handleSpecAdd() {
if (state.specForm.specList.length >= 3) { if (state.specForm.specList.length >= 3) {
ElMessage.warning('最多支持3组规格') ElMessage.warning("最多支持3组规格");
return return;
} }
state.specForm.specList.push({}) state.specForm.specList.push({});
state.tagInputs.push({'value': undefined, 'visible': false}) state.tagInputs.push({ value: undefined, visible: false });
handleSpecReorder() handleSpecReorder();
} }
/** /**
@@ -419,11 +457,11 @@ function handleSpecAdd() {
* @param index * @param index
*/ */
function handleSpecRemove(index) { function handleSpecRemove(index) {
state.specForm.specList.splice(index, 1) state.specForm.specList.splice(index, 1);
state.tagInputs.splice(index, 1) state.tagInputs.splice(index, 1);
generateSkuList() generateSkuList();
handleSpecReorder() handleSpecReorder();
handleSpecChange() handleSpecChange();
} }
/** /**
@@ -432,7 +470,7 @@ function handleSpecRemove(index) {
* @param specIndex * @param specIndex
*/ */
function handleSpecValueAdd(specIndex) { function handleSpecValueAdd(specIndex) {
state.tagInputs[specIndex].visible = true state.tagInputs[specIndex].visible = true;
} }
/** /**
@@ -442,85 +480,96 @@ function handleSpecValueAdd(specIndex) {
* @param specValueId * @param specValueId
*/ */
function handleSpecValueRemove(rowIndex, specValueId) { function handleSpecValueRemove(rowIndex, specValueId) {
const specList = JSON.parse(JSON.stringify(state.specForm.specList)) const specList = JSON.parse(JSON.stringify(state.specForm.specList));
const removeIndex = specList[rowIndex].values.map((item) => item.id).indexOf(specValueId) const removeIndex = specList[rowIndex].values
specList[rowIndex].values.splice(removeIndex, 1) .map((item) => item.id)
state.specForm.specList = specList .indexOf(specValueId);
generateSkuList() specList[rowIndex].values.splice(removeIndex, 1);
handleSpecChange() state.specForm.specList = specList;
handleSpecReorder() generateSkuList();
handleSpecChange();
handleSpecReorder();
} }
/** /**
* 规格值输入 * 规格值输入
*/ */
function handleSpecValueInput(rowIndex) { function handleSpecValueInput(rowIndex) {
const currSpecValue = state.tagInputs[rowIndex].value const currSpecValue = state.tagInputs[rowIndex].value;
const specValues = state.specForm.specList[rowIndex].values const specValues = state.specForm.specList[rowIndex].values;
if (specValues && specValues.length > 0 && specValues.map((item) => item.value).includes(currSpecValue)) { if (
ElMessage.warning("规格值重复,请重新输入") specValues &&
return false specValues.length > 0 &&
specValues.map((item) => item.value).includes(currSpecValue)
) {
ElMessage.warning("规格值重复,请重新输入");
return false;
} }
if (currSpecValue) { if (currSpecValue) {
if (specValues && specValues.length > 0) { if (specValues && specValues.length > 0) {
// 临时规格值ID tid_1_1 // 临时规格值ID tid_1_1
let maxSpecValueIndex = specValues.filter((item) => item.id.includes('tid_')).map((item) => item.id.split('_')[2]).reduce((acc, curr) => { let maxSpecValueIndex = specValues
return acc > curr ? acc : curr .filter((item) => item.id.includes("tid_"))
}, 0) .map((item) => item.id.split("_")[2])
console.log('maxSpecValueIndex', maxSpecValueIndex) .reduce((acc, curr) => {
return acc > curr ? acc : curr;
}, 0);
console.log("maxSpecValueIndex", maxSpecValueIndex);
state.specForm.specList[rowIndex].values[specValues.length] = { state.specForm.specList[rowIndex].values[specValues.length] = {
'value': currSpecValue, value: currSpecValue,
'id': 'tid_' + (rowIndex + 1) + '_' + ++maxSpecValueIndex id: "tid_" + (rowIndex + 1) + "_" + ++maxSpecValueIndex,
} };
} else { } else {
state.specForm.specList[rowIndex].values = [{'value': currSpecValue, 'id': 'tid_' + (rowIndex + 1) + '_1'}] state.specForm.specList[rowIndex].values = [
{ value: currSpecValue, id: "tid_" + (rowIndex + 1) + "_1" },
];
} }
} }
state.tagInputs[rowIndex].value = undefined state.tagInputs[rowIndex].value = undefined;
state.tagInputs[rowIndex].visible = false state.tagInputs[rowIndex].visible = false;
generateSkuList() generateSkuList();
} }
/** /**
* 合并规格单元格 * 合并规格单元格
* *
* @param cellObj 单元格对象 * @param cellObj 单元格对象
*/ */
const objectSpanMethod = ({ const objectSpanMethod = ({ row, column, rowIndex, columnIndex }) => {
row, let mergeRows = [1, 1, 1]; // 分别对应规格1、规格2、规格3列合并的行数
column, const specLen = state.specForm.specList.filter(
rowIndex, (item) => item.values && item.values.length > 0
columnIndex, ).length;
}) => {
let mergeRows = [1, 1, 1] // 分别对应规格1、规格2、规格3列合并的行数
const specLen = state.specForm.specList.filter(item => item.values && item.values.length > 0).length
if (specLen == 2) { if (specLen == 2) {
const values_len_2 = state.specForm.specList[1].values ? state.specForm.specList[1].values.length : 1 // 第2个规格项的规格值的数量 const values_len_2 = state.specForm.specList[1].values
mergeRows = [values_len_2, 1, 1] ? state.specForm.specList[1].values.length
: 1; // 第2个规格项的规格值的数量
mergeRows = [values_len_2, 1, 1];
} else if (specLen == 3) { } else if (specLen == 3) {
const values_len_2 = state.specForm.specList[1].values ? state.specForm.specList[1].values.length : 1 // 第2个规格项的规格值的数量 const values_len_2 = state.specForm.specList[1].values
const values_len_3 = state.specForm.specList[2].values ? state.specForm.specList[2].values.length : 1 // 第3个规格项的规格值的数量 ? state.specForm.specList[1].values.length
mergeRows = [values_len_2 * values_len_3, values_len_3, 1] : 1; // 第2个规格项的规格值的数量
const values_len_3 = state.specForm.specList[2].values
? state.specForm.specList[2].values.length
: 1; // 第3个规格项的规格值的数量
mergeRows = [values_len_2 * values_len_3, values_len_3, 1];
} }
if (columnIndex == 0) { if (columnIndex == 0) {
if (rowIndex % mergeRows[0] === 0) { if (rowIndex % mergeRows[0] === 0) {
return [mergeRows[0], 1]// 合并单元格 return [mergeRows[0], 1]; // 合并单元格
} else { } else {
return [0, 0] // 隐藏单元格 return [0, 0]; // 隐藏单元格
} }
} }
if (columnIndex == 1) { if (columnIndex == 1) {
if (rowIndex % mergeRows[1] === 0) { if (rowIndex % mergeRows[1] === 0) {
return [mergeRows[1], 1]// 合并单元格 return [mergeRows[1], 1]; // 合并单元格
} else { } else {
return [0, 0] // 隐藏单元格 return [0, 0]; // 隐藏单元格
} }
} }
} };
/** /**
* 商品表单提交 * 商品表单提交
@@ -528,7 +577,7 @@ const objectSpanMethod = ({
function submitForm() { function submitForm() {
// 判断商品SKU列表是否为空 // 判断商品SKU列表是否为空
if (!state.skuForm.skuList || state.skuForm.skuList.length === 0) { if (!state.skuForm.skuList || state.skuForm.skuList.length === 0) {
ElMessage.warning("未添加商品库存") ElMessage.warning("未添加商品库存");
return false; return false;
} }
specFormRef.value.validate((specValid) => { specFormRef.value.validate((specValid) => {
@@ -538,96 +587,101 @@ function submitForm() {
// openFullScreen() // openFullScreen()
// 重组商品的规格和SKU列表 // 重组商品的规格和SKU列表
let submitsData = Object.assign({}, props.modelValue) let submitsData = Object.assign({}, props.modelValue);
delete submitsData.specList delete submitsData.specList;
delete submitsData.skuList delete submitsData.skuList;
let specList = [] let specList = [];
state.specForm.specList.forEach(item => { state.specForm.specList.forEach((item) => {
item.values.forEach((value) => { item.values.forEach((value) => {
value.name = item.name value.name = item.name;
}) });
specList = specList.concat(item.values) specList = specList.concat(item.values);
}) });
submitsData.specList = specList // 规格列表 submitsData.specList = specList; // 规格列表
submitsData.price *= 100 // 金额转成分保存至数据库 submitsData.price *= 100; // 金额转成分保存至数据库
submitsData.originPrice *= 100 submitsData.originPrice *= 100;
let skuList = JSON.parse(JSON.stringify(state.skuForm.skuList)) let skuList = JSON.parse(JSON.stringify(state.skuForm.skuList));
skuList.map((item) => { skuList.map((item) => {
item.price *= 100 item.price *= 100;
return item return item;
}) });
submitsData.skuList = skuList submitsData.skuList = skuList;
console.log('提交数据', submitsData) console.log("提交数据", submitsData);
const goodsId = props.modelValue.id const goodsId = props.modelValue.id;
if (goodsId) { // 编辑商品提交 if (goodsId) {
updateGoods(goodsId, submitsData).then((res) => { // 编辑商品提交
router.push({path: '/pms/goods'}) updateGoods(goodsId, submitsData).then(
(res) => {
router.push({ path: "/pms/goods" });
ElNotification({ ElNotification({
title: '提示', title: "提示",
message: '编辑商品成功', message: "编辑商品成功",
type: 'success', type: "success",
}) });
//closeFullScreen() //closeFullScreen()
}, (err) => { },
(err) => {
//closeFullScreen() //closeFullScreen()
} }
) );
} else { // 新增商品提交 } else {
addGoods(submitsData).then(response => { // 新增商品提交
router.push({path: '/pms/goods'}) addGoods(submitsData).then(
(response) => {
router.push({ path: "/pms/goods" });
ElNotification({ ElNotification({
title: '提示', title: "提示",
message: '新增商品成功', message: "新增商品成功",
type: 'success', type: "success",
}) });
// closeFullScreen() // closeFullScreen()
}, (err) => { },
(err) => {
// closeFullScreen() // closeFullScreen()
}) }
);
} }
} }
}) });
} }
}) });
} }
function openFullScreen() { function openFullScreen() {
state.loading = proxy.$loading({ state.loading = proxy.$loading({
lock: true, lock: true,
text: '商品信息提交中,请等待...', text: "商品信息提交中,请等待...",
spinner: 'el-icon-loading', spinner: "el-icon-loading",
background: 'rgba(0, 0, 0, 0.7)' background: "rgba(0, 0, 0, 0.7)",
}); });
} }
function closeFullScreen() { function closeFullScreen() {
if (state.loading) { if (state.loading) {
state.loading.close() state.loading.close();
} }
} }
function handlePrev() { function handlePrev() {
emit('prev') emit("prev");
} }
function handNext() { function handNext() {
emit('next') emit("next");
} }
onMounted(() => { onMounted(() => {
loadData() loadData();
}) });
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.component-container { .component-container {
&__main { &__main {
margin: 20px auto margin: 20px auto;
} }
&__footer { &__footer {

View File

@@ -46,7 +46,7 @@ import GoodsInfo from "./components/GoodsInfo.vue";
import GoodsAttribute from "./components/GoodsAttribute.vue"; import GoodsAttribute from "./components/GoodsAttribute.vue";
import GoodsStock from "./components/GoodsStock.vue"; import GoodsStock from "./components/GoodsStock.vue";
import {getGoodsDetail} from "@/api/pms/goods"; import {getGoodsFormDetail} from "@/api/pms/goods";
export default { export default {
name: "goods-detail", name: "goods-detail",
@@ -81,7 +81,7 @@ export default {
const goodsId = this.$route.query.goodsId const goodsId = this.$route.query.goodsId
console.log('goodsId',goodsId) console.log('goodsId',goodsId)
if (goodsId) { if (goodsId) {
getGoodsDetail(goodsId).then(response => { getGoodsFormDetail(goodsId).then(response => {
this.goods = response.data this.goods = response.data
this.goods.originPrice = this.goods.originPrice / 100 this.goods.originPrice = this.goods.originPrice / 100
this.goods.price = this.goods.price / 100 this.goods.price = this.goods.price / 100

View File

@@ -1,91 +1,105 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<el-form <el-form ref="queryForm" :inline="true">
ref="queryForm"
:inline="true"
>
<el-form-item> <el-form-item>
<el-button type="success" :icon="Plus" @click="handleAdd">发布商品</el-button> <el-button type="success" :icon="Plus" @click="handleAdd"
<el-button type="danger" :icon="Delete" @click="handleDelete" :disabled="multiple">删除</el-button> >发布商品</el-button
>
<el-button
type="danger"
:icon="Delete"
@click="handleDelete"
:disabled="multiple"
>删除</el-button
>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-input v-model="queryParams.name" placeholder="商品名称" clearable></el-input> <el-input
v-model="queryParams.name"
placeholder="商品名称"
clearable
></el-input>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-cascader <el-cascader
v-model="queryParams.categoryId" v-model="queryParams.categoryId"
placeholder="商品分类" placeholder="商品分类"
:props="{emitPath: false, expandTrigger: 'hover'}" :props="{ emitPath: false, expandTrigger: 'hover' }"
:options="categoryOptions" :options="categoryOptions"
clearable clearable
style="width: 300px" style="width: 300px"
/> />
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" :icon="Search" @click="handleQuery">查询</el-button> <el-button type="primary" :icon="Search" @click="handleQuery"
>查询</el-button
>
<el-button :icon="Refresh" @click="resetQuery">重置</el-button> <el-button :icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<el-table <el-table
v-loading="loading"
ref="dataTableRef" ref="dataTableRef"
:data="pageList" v-loading="loading"
:data="goodsList"
@selection-change="handleSelectionChange" @selection-change="handleSelectionChange"
@row-click="handleRowClick" @row-click="handleRowClick"
border border
> >
<el-table-column type="selection" min-width="5%" center/> <el-table-column type="selection" min-width="5%" center />
<el-table-column type="expand" width="120" label="库存信息"> <el-table-column type="expand" width="120" label="库存信息">
<template #default="props"> <template #default="props">
<el-table <el-table :data="props.row.skuList" border>
:data="props.row.skuList" <el-table-column align="center" label="商品编码" prop="skuSn" />
border> <el-table-column align="center" label="商品规格" prop="name" />
<el-table-column align="center" label="商品编码" prop="skuSn"/>
<el-table-column align="center" label="商品规格" prop="name"/>
<el-table-column label="图片" prop="picUrl"> <el-table-column label="图片" prop="picUrl">
<template #default="scope"> <template #default="scope">
<img :src="scope.row.picUrl" width="40"> <img :src="scope.row.picUrl" width="40" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column align="center" label="现价" prop="price"> <el-table-column align="center" label="现价" prop="price">
<template #default="scope">{{ moneyFormatter(scope.row.price) }}</template> <template #default="scope">{{
moneyFormatter(scope.row.price)
}}</template>
</el-table-column> </el-table-column>
<el-table-column align="center" label="库存" prop="stockNum"/> <el-table-column align="center" label="库存" prop="stockNum" />
</el-table> </el-table>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="商品名称" prop="name" min-width="140"/> <el-table-column label="商品名称" prop="name" min-width="140" />
<el-table-column label="商品图片"> <el-table-column label="商品图片">
<template #default="scope"> <template #default="scope">
<el-popover <el-popover placement="right" :width="400" trigger="hover">
placement="right" <img :src="scope.row.picUrl" width="400" height="400" />
:width="400"
trigger="hover">
<img :src="scope.row.picUrl" width="400" height="400"/>
<template #reference> <template #reference>
<img :src="scope.row.picUrl" style="max-height: 60px;max-width: 60px"/> <img
:src="scope.row.picUrl"
style="max-height: 60px; max-width: 60px"
/>
</template> </template>
</el-popover> </el-popover>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="商品类目" prop="categoryName" min-width="100"/> <el-table-column label="商品类目" prop="categoryName" min-width="100" />
<el-table-column label="商品品牌" prop="brandName" min-width="100"/> <el-table-column label="商品品牌" prop="brandName" min-width="100" />
<el-table-column align="center" label="零售价" prop="originalPrice"> <el-table-column align="center" label="零售价" prop="originalPrice">
<template #default="scope">{{ moneyFormatter(scope.row.originPrice) }}</template> <template #default="scope">{{
moneyFormatter(scope.row.originPrice)
}}</template>
</el-table-column> </el-table-column>
<el-table-column align="center" label="促销价" prop="price"> <el-table-column align="center" label="促销价" prop="price">
<template #default="scope">{{ moneyFormatter(scope.row.price) }}</template> <template #default="scope">{{
moneyFormatter(scope.row.price)
}}</template>
</el-table-column> </el-table-column>
<el-table-column label="销量" prop="sales" min-width="100"/> <el-table-column label="销量" prop="sales" min-width="100" />
<el-table-column label="单位" prop="unit" min-width="100"/> <el-table-column label="单位" prop="unit" min-width="100" />
<el-table-column label="描述" prop="description" min-width="100"/> <el-table-column label="描述" prop="description" min-width="100" />
<el-table-column label="详情" prop="detail"> <el-table-column label="详情" prop="detail">
<template #default="scope"> <template #default="scope">
<el-button type="primary" @click="handleGoodsView(scope.row.detail)">查看</el-button> <el-button type="primary" @click="handleGoodsView(scope.row.detail)"
>查看</el-button
>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" width="120"> <el-table-column label="操作" width="120">
@@ -110,30 +124,31 @@
<!-- 分页工具条 --> <!-- 分页工具条 -->
<pagination <pagination
v-show="total>0" v-show="total > 0"
:total="total" :total="total"
v-model:page="queryParams.pageNum" v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize" v-model:limit="queryParams.pageSize"
@pagination="handleQuery" @pagination="handleQuery"
/> />
<el-dialog v-model="dialogVisible" title="商品详情"> <el-dialog v-model="dialogVisible" title="商品详情">
<div class="goods-detail-box" v-html="goodDetail"/> <div class="goods-detail-box" v-html="goodDetail" />
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import {Search, Plus, Edit, Refresh, Delete} from '@element-plus/icons-vue' import { reactive, ref, onMounted, toRefs } from "vue";
import {listGoodsWithPage, deleteGoods} from '@/api/pms/goods' import { ElTable, ElMessage, ElMessageBox } from "element-plus";
import {listCascadeCategories} from '@/api/pms/category' import { useRouter } from "vue-router";
import {reactive, ref, onMounted, toRefs} from 'vue'
import {ElTable, ElMessage, ElMessageBox} from 'element-plus'
import {getCurrentInstance} from 'vue'
import {moneyFormatter} from '@/utils/filter'
import {useRouter} from "vue-router"
const dataTableRef = ref(ElTable) import { Search, Plus, Edit, Refresh, Delete } from "@element-plus/icons-vue";
const router=useRouter() import { listGoodsPages, deleteGoods } from "@/api/pms/goods";
import { listCascadeCategories } from "@/api/pms/category";
import { GoodsItem, GoodsQueryParam } from "@/types";
import {moneyFormatter} from '@/utils/filter'
const dataTableRef = ref(ElTable);
const router = useRouter();
const state = reactive({ const state = reactive({
// 遮罩层 // 遮罩层
@@ -148,15 +163,12 @@ const state = reactive({
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
name: undefined, } as GoodsQueryParam,
categoryId: undefined goodsList: [] as GoodsItem[],
},
pageList: [],
categoryOptions: [], categoryOptions: [],
goodDetail: undefined, goodDetail: undefined,
dialogVisible: false dialogVisible: false,
}) });
const { const {
loading, loading,
@@ -164,22 +176,20 @@ const {
single, single,
multiple, multiple,
queryParams, queryParams,
pageList, goodsList,
categoryOptions, categoryOptions,
goodDetail, goodDetail,
total, total,
dialogVisible dialogVisible,
} = toRefs(state); } = toRefs(state);
function handleQuery() { function handleQuery() {
state.loading = true state.loading = true;
listGoodsWithPage(state.queryParams).then(response => { listGoodsPages(state.queryParams).then(({ data }) => {
const {data, total} = response as any state.goodsList = data.list;
state.pageList = data state.total = data.total;
state.total = total state.loading = false;
state.loading = false });
})
} }
function resetQuery() { function resetQuery() {
@@ -187,36 +197,41 @@ function resetQuery() {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
name: undefined, name: undefined,
categoryId: undefined categoryId: undefined,
} };
handleQuery() handleQuery();
} }
function handleGoodsView(detail: any) { function handleGoodsView(detail: any) {
state.goodDetail = detail state.goodDetail = detail;
state.dialogVisible = true state.dialogVisible = true;
} }
function handleAdd() { function handleAdd() {
router.push({path: 'goods-detail'}) router.push({ path: "goods-detail" });
} }
function handleUpdate(row: any) { function handleUpdate(row: any) {
router.push({path: 'goods-detail', query: {goodsId: row.id, categoryId: row.categoryId}}) router.push({
path: "goods-detail",
query: { goodsId: row.id, categoryId: row.categoryId },
});
} }
function handleDelete(row: any) { function handleDelete(row: any) {
const ids = row.id || state.ids.join(',') const ids = row.id || state.ids.join(",");
ElMessageBox.confirm('是否确认删除选中的数据项?', "警告", { ElMessageBox.confirm("是否确认删除选中的数据项?", "警告", {
confirmButtonText: "确定", confirmButtonText: "确定",
cancelButtonText: "取消", cancelButtonText: "取消",
type: "warning" type: "warning",
}).then(function () {
return deleteGoods(ids)
}).then(() => {
ElMessage.success("删除成功")
handleQuery()
}) })
.then(function () {
return deleteGoods(ids);
})
.then(() => {
ElMessage.success("删除成功");
handleQuery();
});
} }
function handleRowClick(row: any) { function handleRowClick(row: any) {
@@ -224,19 +239,18 @@ function handleRowClick(row: any) {
} }
function handleSelectionChange(selection: any) { function handleSelectionChange(selection: any) {
state.ids = selection.map((item: { id: any }) => item.id) state.ids = selection.map((item: { id: any }) => item.id);
state.single = selection.length != 1 state.single = selection.length != 1;
state.multiple = !selection.length state.multiple = !selection.length;
} }
onMounted(() => { onMounted(() => {
listCascadeCategories({}).then(response => { listCascadeCategories({}).then((response) => {
state.categoryOptions = ref(response.data) state.categoryOptions = ref(response.data);
}) });
handleQuery() handleQuery();
}) });
</script> </script>
<style scoped> <style scoped>
</style> </style>

View File

@@ -1,14 +1,18 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<!-- 搜索表单 --> <!-- 搜索表单 -->
<el-form <el-form ref="queryFormRef" :model="queryParams" :inline="true">
ref="queryForm"
:model="queryParams"
:inline="true"
>
<el-form-item> <el-form-item>
<el-button type="success" :icon="Plus" @click="handleAdd">新增</el-button> <el-button type="success" :icon="Plus" @click="handleAdd"
<el-button type="danger" :icon="Delete" :disabled="multiple" @click="handleDelete">删除</el-button> >新增</el-button
>
<el-button
type="danger"
:icon="Delete"
:disabled="multiple"
@click="handleDelete"
>删除</el-button
>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
@@ -16,47 +20,48 @@
v-model="queryParams.title" v-model="queryParams.title"
placeholder="广告标题" placeholder="广告标题"
clearable clearable
@keyup.enter.native="handleQuery" @keyup.enter="handleQuery"
/> />
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" :icon="Search" @click="handleQuery">搜索</el-button> <el-button type="primary" :icon="Search" @click="handleQuery"
>搜索</el-button
>
<el-button :icon="Refresh" @click="resetQuery">重置</el-button> <el-button :icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<el-table <el-table
ref="dataTable"
v-loading="loading" v-loading="loading"
:data="pageList" :data="advertList"
@selection-change="handleSelectionChange" @selection-change="handleSelectionChange"
border border
> >
<el-table-column type="selection" min-width="5" align="center"/> <el-table-column type="selection" min-width="5" align="center" />
<el-table-column type="index" label="序号" width="80" align="center"/> <el-table-column type="index" label="序号" width="80" align="center" />
<el-table-column prop="title" min-width="100" label="广告标题" /> <el-table-column prop="title" min-width="100" label="广告标题" />
<el-table-column label="广告图片" width="100"> <el-table-column label="广告图片" width="100">
<template #default="scope"> <template #default="scope">
<el-popover <el-popover placement="right" :width="400" trigger="hover">
placement="right" <img :src="scope.row.picUrl" width="400" height="400" />
:width="400"
trigger="hover">
<img :src="scope.row.picUrl" width="400" height="400"/>
<template #reference> <template #reference>
<img :src="scope.row.picUrl" style="max-height: 60px;max-width: 60px"/> <img
:src="scope.row.picUrl"
style="max-height: 60px; max-width: 60px"
/>
</template> </template>
</el-popover> </el-popover>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="beginTime" label="开始时间" width="150"/> <el-table-column prop="beginTime" label="开始时间" width="150" />
<el-table-column prop="endTime" label="结束时间" width="150"/> <el-table-column prop="endTime" label="结束时间" width="150" />
<el-table-column prop="status" label="状态" width="100"> <el-table-column prop="status" label="状态" width="100">
<template #default="scope"> <template #default="scope">
<el-tag v-if="scope.row.status===1" type="success" >开启</el-tag> <el-tag v-if="scope.row.status === 1" type="success">开启</el-tag>
<el-tag v-else type="info">关闭</el-tag> <el-tag v-else type="info">关闭</el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="sort" label="排序" width="80"/> <el-table-column prop="sort" label="排序" width="80" />
<el-table-column label="操作" align="center" width="150"> <el-table-column label="操作" align="center" width="150">
<template #default="scope"> <template #default="scope">
<el-button <el-button
@@ -79,7 +84,7 @@
<!-- 分页工具条 --> <!-- 分页工具条 -->
<pagination <pagination
v-show="total>0" v-show="total > 0"
:total="total" :total="total"
v-model:page="queryParams.pageNum" v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize" v-model:limit="queryParams.pageSize"
@@ -87,11 +92,7 @@
/> />
<!-- 表单弹窗 --> <!-- 表单弹窗 -->
<el-dialog <el-dialog :title="dialog.title" v-model="dialog.visible" width="700px">
:title="dialog.title"
v-model="dialog.visible"
width="700px"
>
<el-form <el-form
ref="dataFormRef" ref="dataFormRef"
:model="formData" :model="formData"
@@ -99,7 +100,7 @@
label-width="100px" label-width="100px"
> >
<el-form-item label="广告标题" prop="title"> <el-form-item label="广告标题" prop="title">
<el-input v-model="formData.title"/> <el-input v-model="formData.title" />
</el-form-item> </el-form-item>
<el-form-item label="有效期" prop="beginTime"> <el-form-item label="有效期" prop="beginTime">
@@ -117,11 +118,11 @@
</el-form-item> </el-form-item>
<el-form-item label="广告图片" prop="picUrl"> <el-form-item label="广告图片" prop="picUrl">
<single-upload v-model="formData.picUrl"/> <single-upload v-model="formData.picUrl" />
</el-form-item> </el-form-item>
<el-form-item label="排序" prop="sort"> <el-form-item label="排序" prop="sort">
<el-input v-model="formData.sort" style="width: 200px"/> <el-input v-model="formData.sort" style="width: 200px" />
</el-form-item> </el-form-item>
<el-form-item label="状态" prop="status"> <el-form-item label="状态" prop="status">
@@ -132,11 +133,11 @@
</el-form-item> </el-form-item>
<el-form-item label="跳转链接" prop="url"> <el-form-item label="跳转链接" prop="url">
<el-input v-model="formData.url"/> <el-input v-model="formData.url" />
</el-form-item> </el-form-item>
<el-form-item label="备注" prop="remark"> <el-form-item label="备注" prop="remark">
<el-input type="textarea" v-model="formData.remark"/> <el-input type="textarea" v-model="formData.remark" />
</el-form-item> </el-form-item>
</el-form> </el-form>
@@ -146,19 +147,26 @@
<el-button @click="cancel"> </el-button> <el-button @click="cancel"> </el-button>
</div> </div>
</template> </template>
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import {listAdvertsWithPage, getAdvertDetail, updateAdvert, addAdvert, deleteAdverts} from '@/api/sms/advert' import { onMounted, reactive, ref, toRefs, unref } from "vue";
import { ElForm, ElMessage, ElMessageBox } from "element-plus";
import { Search, Plus, Edit, Refresh, Delete } from "@element-plus/icons-vue";
import SingleUpload from "@/components/Upload/SingleUpload.vue"; import SingleUpload from "@/components/Upload/SingleUpload.vue";
import {onMounted, reactive, ref, toRefs, unref} from "vue"; import {
import {ElForm, ElMessage, ElMessageBox} from "element-plus"; listAdvertPages,
import {Search, Plus, Edit, Refresh, Delete} from '@element-plus/icons-vue' getAdvertFormDetail,
updateAdvert,
addAdvert,
deleteAdverts,
} from "@/api/sms/advert";
import { AdvertFormData, AdvertItem, AdvertQueryParam, Dialog } from "@/types";
const dataFormRef = ref(ElForm) // 属性名必须和元素的ref属性值一致 const queryFormRef = ref(ElForm); // 属性名必须和元素的ref属性值一致
const dataFormRef = ref(ElForm); // 属性名必须和元素的ref属性值一致
const state = reactive({ const state = reactive({
loading: true, loading: true,
@@ -168,150 +176,124 @@ const state = reactive({
single: true, single: true,
// 非多个禁用 // 非多个禁用
multiple: true, multiple: true,
queryParams: { queryParams: { pageNum: 1, pageSize: 10 } as AdvertQueryParam,
pageNum: 1, advertList: [] as AdvertItem[],
pageSize: 10,
title: undefined
},
pageList: [],
total: 0, total: 0,
dialog: { dialog: {} as Dialog,
title: '',
visible: false
},
formData: { formData: {
id: undefined,
title: '',
picUrl: '',
beginTime: undefined,
endTime: undefined,
status: 1, status: 1,
sort: 100, sort: 100,
url: undefined, } as AdvertFormData,
remark: undefined
},
rules: { rules: {
title: [ title: [{ required: true, message: "请输入广告名称", trigger: "blur" }],
{required: true, message: '请输入广告名称', trigger: 'blur'} beginTime: [{ required: true, message: "请填写开始时间", trigger: "blur" }],
], endTime: [{ required: true, message: "请填写结束时间", trigger: "blur" }],
beginTime: [ picUrl: [{ required: true, message: "请上传广告图片", trigger: "blur" }],
{required: true, message: '请填写开始时间', trigger: 'blur'} },
], });
endTime: [
{required: true, message: '请填写结束时间', trigger: 'blur'}
],
picUrl: [
{required: true, message: '请上传广告图片', trigger: 'blur'}
]
}
})
const {loading, single, multiple, queryParams, pageList, total, dialog, formData, rules} = toRefs(state) const {
loading,
single,
multiple,
queryParams,
advertList,
total,
dialog,
formData,
rules,
} = toRefs(state);
function handleQuery() { function handleQuery() {
state.loading = true state.loading = true;
listAdvertsWithPage(state.queryParams).then(response => { listAdvertPages(state.queryParams).then(({data}) => {
const {data, total} = response as any state.advertList = data.list;
state.pageList = data state.total = data.total;
state.total = total state.loading = false;
state.loading = false });
})
} }
function resetQuery() { function resetQuery() {
state.queryParams = { queryFormRef.value.resetFields();
pageNum: 1, handleQuery();
pageSize: 10,
title: undefined
}
handleQuery()
} }
function handleSelectionChange(selection: any) { function handleSelectionChange(selection: any) {
state.ids = selection.map((item: any) => item.id) state.ids = selection.map((item: any) => item.id);
state.single = selection.length !== 1 state.single = selection.length !== 1;
state.multiple = !selection.length state.multiple = !selection.length;
} }
function handleAdd() { function handleAdd() {
resetForm() resetForm();
state.dialog = { state.dialog = {
title: '添加广告', title: "添加广告",
visible: true visible: true,
} };
} }
function handleUpdate(row: any) { function handleUpdate(row: any) {
resetForm()
state.dialog = { state.dialog = {
title: '修改广告', title: "修改广告",
visible: true, visible: true,
} };
const advertId = row.id || state.ids const advertId = row.id || state.ids;
getAdvertDetail(advertId).then((response) => { getAdvertFormDetail(advertId).then((response) => {
state.formData = response.data state.formData = response.data;
}) });
} }
function submitForm() { function submitForm() {
const dataForm = unref(dataFormRef) dataFormRef.value.validate((valid: any) => {
dataForm.validate((valid: any) => {
if (valid) { if (valid) {
const avertId = state.formData.id const avertId = state.formData.id;
if (avertId) { if (avertId) {
updateAdvert(avertId, state.formData).then(response => { updateAdvert(avertId, state.formData).then((response) => {
ElMessage.success('修改成功') ElMessage.success("修改成功");
state.dialog.visible = false cancel();
handleQuery() handleQuery();
}) });
} else { } else {
addAdvert(state.formData).then(response => { addAdvert(state.formData).then((response) => {
ElMessage.success('新增成功') ElMessage.success("新增成功");
state.dialog.visible = false cancel();
handleQuery() handleQuery();
}) });
} }
} }
}) });
} }
/**
* 重置表单
*/
function resetForm() { function resetForm() {
state.formData = { state.formData.id = undefined;
id: undefined, dataFormRef.value.resetFields();
title: '',
picUrl: '',
beginTime: undefined,
endTime: undefined,
status: 1,
sort: 100,
url: undefined,
remark: undefined
}
} }
function cancel() { function cancel() {
resetForm() resetForm();
state.dialog.visible = false state.dialog.visible = false;
} }
function handleDelete(row: any) { function handleDelete(row: any) {
const ids = [row.id || state.ids].join(',') const ids = [row.id || state.ids].join(",");
ElMessageBox.confirm('确认删除已选中的数据项?', '警告', { ElMessageBox.confirm("确认删除已选中的数据项?", "警告", {
confirmButtonText: '确定', confirmButtonText: "确定",
cancelButtonText: '取消', cancelButtonText: "取消",
type: 'warning' type: "warning",
}).then(() => {
deleteAdverts(ids).then(() => {
ElMessage.success('删除成功')
handleQuery()
}) })
}).catch(() => .then(() => {
ElMessage.info('已取消删除') deleteAdverts(ids).then(() => {
) ElMessage.success("删除成功");
handleQuery();
});
})
.catch(() => ElMessage.info("已取消删除"));
} }
onMounted(() => { onMounted(() => {
handleQuery() handleQuery();
}) });
</script> </script>

View File

@@ -261,6 +261,7 @@ async function loadDeptOptions() {
* 表单重置 * 表单重置
**/ **/
function resetForm() { function resetForm() {
state.formData.id = undefined;
dataFormRef.value.resetFields(); dataFormRef.value.resetFields();
} }

View File

@@ -238,6 +238,7 @@ function submitForm() {
} }
function resetForm() { function resetForm() {
state.formData.id = undefined;
dataFormRef.value.resetFields(); dataFormRef.value.resetFields();
} }

View File

@@ -277,6 +277,7 @@ function submitForm() {
} }
function resetForm() { function resetForm() {
state.formData.id = undefined;
dataFormRef.value.resetFields(); dataFormRef.value.resetFields();
} }

View File

@@ -403,6 +403,7 @@ function handleDelete(row: any) {
* 重置表单 * 重置表单
*/ */
function resetForm() { function resetForm() {
state.formData.id = undefined;
dataFormRef.value.resetFields(); dataFormRef.value.resetFields();
} }

View File

@@ -303,7 +303,7 @@ import {
UserFormData, UserFormData,
Option, Option,
RoleItem, RoleItem,
Dialog, Dialog
} from "@/types"; } from "@/types";
// DOM元素的引用声明定义 // DOM元素的引用声明定义
@@ -559,6 +559,7 @@ function submitForm() {
* 重置表单 * 重置表单
*/ */
function resetForm() { function resetForm() {
state.formData.id = undefined;
dataFormRef.value.resetFields(); dataFormRef.value.resetFields();
} }

View File

@@ -1,85 +1,94 @@
<template> <template>
<div class="component-container"> <div class="app-container">
<el-form <el-form ref="queryFormRef" :model="queryParams" :inline="true">
ref="queryForm"
:model="queryParams"
:inline="true"
>
<el-form-item> <el-form-item>
<el-input <el-input
v-model="queryParams.nickName" v-model="queryParams.nickName"
placeholder="会员昵称" placeholder="会员昵称"
clearable clearable
@keyup.enter.native="handleQuery"/> @keyup.enter="handleQuery"
/>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" :icon="Search" @click="handleQuery">搜索</el-button> <el-button type="primary" :icon="Search" @click="handleQuery"
>搜索</el-button
>
<el-button :icon="Refresh" @click="resetQuery">重置</el-button> <el-button :icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<el-table v-loading="loading" <el-table
:data="pageList" v-loading="loading"
:data="memberList"
border border
@selection-change="handleSelectionChange" @selection-change="handleSelectionChange"
> >
<el-table-column type="selection" align="center"/> <el-table-column type="selection" align="center" />
<el-table-column type="expand" width="80" label="会员地址"> <el-table-column type="expand" width="120" label="会员地址">
<template #default="scope"> <template #default="scope">
<el-table <el-table :data="scope.row.addressList" size="small" border>
:data="scope.row.addressList" <el-table-column
size="small" type="index"
border> label="序号"
<el-table-column type="index" label="序号" width="80" align="center"/> width="100"
<el-table-column align="center" label="收货人" prop="name"/> align="center"
<el-table-column align="center" label="联系方式" prop="mobile"/> />
<el-table-column align="center" label="收货人" prop="name" />
<el-table-column align="center" label="联系方式" prop="mobile" />
<el-table-column align="center" label="收货地址"> <el-table-column align="center" label="收货地址">
<template #default="scope"> <template #default="scope">
{{ scope.row.province + scope.row.city + scope.row.area + scope.row.address }} {{
scope.row.province +
scope.row.city +
scope.row.area +
scope.row.address
}}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column align="center" label="邮编" prop="zipCode"/> <el-table-column align="center" label="邮编" prop="zipCode" />
<el-table-column align="center" label="是否默认"> <el-table-column align="center" label="是否默认">
<template #default="scope"> <template #default="scope">
<el-tag v-if="scope.row.defaulted==1" type="success"></el-tag> <el-tag v-if="scope.row.defaulted == 1" type="success"
<el-tag v-if="scope.row.defaulted==0" type="info"></el-tag> ></el-tag
>
<el-tag v-if="scope.row.defaulted == 0" type="info"></el-tag>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column type="index" label="序号" width="50" align="center"/> <el-table-column type="index" label="序号" width="100" align="center" />
<el-table-column prop="nickName" label="昵称"/> <el-table-column prop="nickName" label="昵称" />
<el-table-column label="性别" width="80"> <el-table-column label="性别" width="80">
<template #default="scope"> <template #default="scope">
<span v-if="scope.row.gender===0">未知</span> <span v-if="scope.row.gender === 0">未知</span>
<span v-if="scope.row.gender===1"></span> <span v-if="scope.row.gender === 1"></span>
<span v-if="scope.row.gender===2"></span> <span v-if="scope.row.gender === 2"></span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="头像" width="100"> <el-table-column label="头像" width="100">
<template #default="scope"> <template #default="scope">
<el-popover <el-popover placement="right" :width="400" trigger="hover">
placement="right" <img :src="scope.row.avatarUrl" width="400" height="400" />
:width="400"
trigger="hover">
<img :src="scope.row.avatarUrl" width="400" height="400"/>
<template #reference> <template #reference>
<img :src="scope.row.avatarUrl" style="max-height: 60px;max-width: 60px"/> <img
:src="scope.row.avatarUrl"
style="max-height: 60px; max-width: 60px"
/>
</template> </template>
</el-popover> </el-popover>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="mobile" label="手机号码"/> <el-table-column prop="mobile" label="手机号码" />
<el-table-column prop="birthday" label="出生日期"/> <el-table-column prop="birthday" label="出生日期" />
<el-table-column prop="status" width="80" label="状态"> <el-table-column prop="status" width="80" label="状态">
<template #default="scope"> <template #default="scope">
<el-tag v-if="scope.row.status===1" type="success" >正常</el-tag> <el-tag v-if="scope.row.status === 1" type="success">正常</el-tag>
<el-tag v-else type="info" >禁用</el-tag> <el-tag v-else type="info">禁用</el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="gmtCreate" label="注册时间"/> <el-table-column prop="gmtCreate" label="注册时间" />
<el-table-column label="账户余额"> <el-table-column label="账户余额">
<template #default="scope"> <template #default="scope">
@@ -90,7 +99,7 @@
<!-- 分页工具条 --> <!-- 分页工具条 -->
<pagination <pagination
v-show="total>0" v-show="total > 0"
:total="total" :total="total"
v-model:page="queryParams.pageNum" v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize" v-model:limit="queryParams.pageSize"
@@ -100,11 +109,12 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import {reactive, onMounted, toRefs} from 'vue' import { reactive, onMounted, toRefs } from "vue";
import {ElTable, ElMessage, ElMessageBox} from 'element-plus' import { ElTable, ElMessage, ElMessageBox } from "element-plus";
import {Search, Plus, Edit, Refresh, Delete} from '@element-plus/icons-vue' import { Search, Plus, Edit, Refresh, Delete } from "@element-plus/icons-vue";
import {listMembersWithPage} from '@/api/ums/member' import { listMemeberPages } from "@/api/ums/member";
import { MemberQueryParam,MemberItem } from "@/types";
const state = reactive({ const state = reactive({
// 遮罩层 // 遮罩层
@@ -119,21 +129,20 @@ const state = reactive({
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
nickName: undefined } as MemberQueryParam,
}, memberList: [] as MemberItem[]
pageList: [] });
})
const { loading,ids,single, multiple, queryParams, pageList,total} = toRefs(state); const { loading, ids, single, multiple, queryParams, memberList, total } =
toRefs(state);
function handleQuery() { function handleQuery() {
state.loading = true state.loading = true;
listMembersWithPage(state.queryParams).then(response => { listMemeberPages(state.queryParams).then(({data}) => {
const {data, total} = response as any state.memberList = data.list;
state.pageList = data state.total = data.total;
state.total = total state.loading = false;
state.loading = false });
})
} }
function resetQuery() { function resetQuery() {
@@ -141,21 +150,19 @@ function resetQuery() {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
nickName: undefined, nickName: undefined,
} };
handleQuery() handleQuery();
} }
function handleSelectionChange(selection: any) { function handleSelectionChange(selection: any) {
state.ids = selection.map((item: { id: any }) => item.id) state.ids = selection.map((item: { id: any }) => item.id);
state.single = selection.length != 1 state.single = selection.length != 1;
state.multiple = !selection.length state.multiple = !selection.length;
} }
onMounted(() => { onMounted(() => {
handleQuery() handleQuery();
}) });
</script> </script>
<style scoped> <style scoped>