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

View File

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

View File

@@ -2,131 +2,148 @@
<div class="component-container">
<div class="component-container__main">
<el-form
ref="dataFormRef"
:rules="rules"
:model="modelValue"
label-width="120px"
ref="dataFormRef"
:rules="rules"
:model="modelValue"
label-width="120px"
>
<el-form-item label="商品品牌" prop="brandId">
<el-select
v-model="modelValue.brandId"
style="width:400px"
clearable
v-model="modelValue.brandId"
style="width: 400px"
clearable
>
<el-option
v-for="item in brandOptions"
:key="item.id"
:label="item.name"
:value="item.id"
v-for="item in brandOptions"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
<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 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 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 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 label="商品相册">
<el-card v-for="(item,index) in pictures" style="width: 170px;display: inline-block;margin-left: 10px"
:body-style="{ padding: '10px' }">
<el-card
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 class="bottom" v-if="item.url">
<el-button type="text" class="button" v-if="item.main==true" style="color:#ff4d51">商品主图</el-button>
<el-button type="text" class="button" v-else @click="changeMainPicture(index)">设为主图</el-button>
<el-button type="text" class="button" @click="removePicture(index)">删除图片</el-button>
<div v-if="item.url">
<el-button
type="text"
class="button"
v-if="item.main == true"
style="color: #ff4d51"
>商品主图</el-button
>
<el-button
type="text"
class="button"
v-else
@click="changeMainPicture(index)"
>设为主图</el-button
>
</div>
<div class="bottom" v-else>
<div v-else>
<!-- 占位 -->
<el-button type="text"/>
<el-button type="text" />
</div>
</el-card>
</el-form-item>
<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>
</div>
<div class="component-container__footer">
<el-button @click="handlePrev">上一步选择商品分类</el-button>
<el-button type="primary" @click="handleNext">下一步设置商品属性</el-button>
<el-button type="primary" @click="handleNext"
>下一步设置商品属性</el-button
>
</div>
</div>
</template>
<script setup lang="ts">
import {onMounted, reactive, ref, toRefs, unref} from "vue"
import {ElForm} from "element-plus"
import { onMounted, reactive, ref, toRefs } from "vue";
import { ElForm } from "element-plus";
// 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 dataFormRef = ref(ElForm)
const emit = defineEmits(["prev", "next"]);
const dataFormRef = ref(ElForm);
const props = defineProps({
modelValue: {
type: Object,
default: {}
}
})
default: {},
},
});
const state = reactive({
brandOptions: [] as Array<any>,
// 商品图册
pictures: [
{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: true }, // main = true 代表主图,可切换
{ url: undefined, main: false },
{ url: undefined, main: false },
{ url: undefined, main: false },
{ url: undefined, main: false },
] as Array<any>,
rules: {
name: [{required: true, message: '请填写商品名称', trigger: 'blur'}],
originPrice: [{required: true, message: '请填写原价', trigger: 'blur'}],
price: [{required: true, message: '请填写现价', trigger: 'blur'}],
brandId: [{required: true, message: '请选择商品品牌', trigger: 'blur'}],
}
})
name: [{ required: true, message: "请填写商品名称", trigger: "blur" }],
originPrice: [{ required: true, message: "请填写原价", trigger: "blur" }],
price: [{ 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() {
listBrands({}).then(response => {
state.brandOptions = response.data
})
const goodsInfo = props.modelValue
console.log('商品信息', goodsInfo)
const goodsId = goodsInfo.id
listBrands().then(({data}) => {
state.brandOptions = data;
});
const goodsInfo = props.modelValue;
const goodsId = goodsInfo.id;
if (goodsId) {
const mainPicUrl = goodsInfo.picUrl
const mainPicUrl = goodsInfo.picUrl;
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) {
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() {
state.pictures = [
{url: undefined, main: true}, // main 代表主图,可以切换
{url: undefined, main: false},
{url: undefined, main: false},
{url: undefined, main: false},
{url: undefined, main: false}
]
{ url: undefined, main: true }, // main 代表主图,可以切换
{ url: undefined, main: false },
{ url: undefined, main: false },
{ url: undefined, main: false },
{ url: undefined, main: false },
];
}
/**
* 切换主图
*/
function changeMainPicture(changeIndex: number) {
const currMainPicture = JSON.parse(JSON.stringify(state.pictures[0]))
const nextMainPicture = JSON.parse(JSON.stringify(state.pictures[changeIndex]))
const currMainPicture = JSON.parse(JSON.stringify(state.pictures[0]));
const nextMainPicture = JSON.parse(
JSON.stringify(state.pictures[changeIndex])
);
state.pictures[0].url = nextMainPicture.url
state.pictures[changeIndex].url = currMainPicture.url
}
function removePicture(index: number) {
state.pictures[index].url = undefined
state.pictures[0].url = nextMainPicture.url;
state.pictures[changeIndex].url = currMainPicture.url;
}
function handlePrev() {
emit('prev')
emit("prev");
}
function handleNext() {
dataFormRef.value.validate((valid: any) => {
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) {
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) {
props.modelValue.subPicUrls = subPicUrl
props.modelValue.subPicUrls = subPicUrl;
}
emit('next')
emit("next");
}
})
});
}
onMounted(() => {
loadData()
})
loadData();
});
</script>
<style lang="scss" scoped>
.component-container {
&__main {
margin: 20px auto;
.button {
margin-left: 10px;
}
}
@@ -198,6 +217,7 @@ onMounted(() => {
position: fixed;
bottom: 20px;
right: 20px;
}
}
</style>

View File

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

View File

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

View File

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