|
@@ -0,0 +1,402 @@
|
|
|
|
|
+<template>
|
|
|
|
|
+ <div class="article-edit">
|
|
|
|
|
+ <el-card class="!border-none" shadow="never">
|
|
|
|
|
+ <el-page-header content="商品编辑" @back="$router.back()"/>
|
|
|
|
|
+ </el-card>
|
|
|
|
|
+ <el-card class="mt-4 !border-none" shadow="never">
|
|
|
|
|
+ <el-form
|
|
|
|
|
+ ref="formRef"
|
|
|
|
|
+ class="ls-form"
|
|
|
|
|
+ :inline="true"
|
|
|
|
|
+ :model="formData"
|
|
|
|
|
+ label-width="100px"
|
|
|
|
|
+ :rules="rules"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-divider content-position="left"><span style="font-size: 18px">基础信息</span></el-divider>
|
|
|
|
|
+ <div class="xl:flex">
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <el-form-item label="商品名称" prop="title">
|
|
|
|
|
+ <div class="w-32vw">
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ v-model="formData.title"
|
|
|
|
|
+ placeholder="请输入商品名称"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="商品分类" prop="title">
|
|
|
|
|
+ <div class="w-32vw">
|
|
|
|
|
+ <el-select class="w-[100%]" v-model="formData.productCategoryId">
|
|
|
|
|
+ <el-option label="全部" value/>
|
|
|
|
|
+ <el-option
|
|
|
|
|
+ v-for="item in optionsData.goodsCategory"
|
|
|
|
|
+ :key="item.id"
|
|
|
|
|
+ :label="item.name"
|
|
|
|
|
+ :value="item.id"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="商品售价" prop="price">
|
|
|
|
|
+ <div class="w-32vw">
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ v-model="formData.price"
|
|
|
|
|
+ placeholder="请输入商品售价"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="划线价格" prop="originalPrice">
|
|
|
|
|
+ <div class="w-32vw">
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ v-model="formData.originalPrice"
|
|
|
|
|
+ placeholder="请输入划线价格"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="商品库存" prop="stock">
|
|
|
|
|
+ <div class="w-32vw">
|
|
|
|
|
+ <el-input-number
|
|
|
|
|
+ v-model="formData.stock"
|
|
|
|
|
+ placeholder="请输入商品库存"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="商品排序" prop="sort">
|
|
|
|
|
+ <div class="w-32vw">
|
|
|
|
|
+ <el-input-number
|
|
|
|
|
+ v-model="formData.sort"
|
|
|
|
|
+ placeholder="请输入商品排序"
|
|
|
|
|
+ show-word-limit
|
|
|
|
|
+ clearable
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="商品单位" prop="unit">
|
|
|
|
|
+ <div class="w-32vw">
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ v-model="formData.unit"
|
|
|
|
|
+ placeholder="请输入商品单位"
|
|
|
|
|
+ show-word-limit
|
|
|
|
|
+ clearable
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="商品标签" prop="tags">
|
|
|
|
|
+ <div class="w-32vw">
|
|
|
|
|
+ <el-select
|
|
|
|
|
+ style="width: 100%"
|
|
|
|
|
+ v-model="formData.tags"
|
|
|
|
|
+ multiple
|
|
|
|
|
+ filterable
|
|
|
|
|
+ allow-create
|
|
|
|
|
+ default-first-option
|
|
|
|
|
+ placeholder="选择或创建商品标签">
|
|
|
|
|
+ <el-option
|
|
|
|
|
+ v-for="item in goodsTagOption"
|
|
|
|
|
+ :key="item.value"
|
|
|
|
|
+ :label="item.label"
|
|
|
|
|
+ :value="item.value">
|
|
|
|
|
+ </el-option>
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="购买须知" prop="purchaseNotice">
|
|
|
|
|
+ <div class="w-32vw">
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ v-model="formData.purchaseNotice"
|
|
|
|
|
+ placeholder="请输入购买须知"
|
|
|
|
|
+ show-word-limit
|
|
|
|
|
+ clearable
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="商品保障" prop="guarantee">
|
|
|
|
|
+ <div class="w-32vw">
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ v-model="formData.guarantee"
|
|
|
|
|
+ placeholder="请输入商品保障"
|
|
|
|
|
+ show-word-limit
|
|
|
|
|
+ clearable
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <el-divider content-position="left"><span style="font-size: 18px">详细信息</span></el-divider>
|
|
|
|
|
+ <div class="xl:flex">
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <el-form-item label="商品主图" prop="image">
|
|
|
|
|
+ <div class="w-32vw">
|
|
|
|
|
+ <material-picker v-model="formData.image" :limit="1"/>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="商品轮播图" prop="bannerImages">
|
|
|
|
|
+ <div class="w-32vw">
|
|
|
|
|
+ <material-picker v-model="formData.bannerImages" :limit="3"/>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="商品视频" prop="video">
|
|
|
|
|
+ <div class="w-32vw">
|
|
|
|
|
+ <material-picker v-model="formData.video" :limit="3"/>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="xl:flex">
|
|
|
|
|
+ <el-form-item label="商品详情" prop="content">
|
|
|
|
|
+ <div style="width: 100%">
|
|
|
|
|
+ <editor v-model="formData.content" :height="500"/>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <!--<el-divider content-position="left"><span style="font-size: 18px">其他</span></el-divider>
|
|
|
|
|
+ <div class="xl:flex">
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <el-form-item label="赠送优惠券" prop="title">
|
|
|
|
|
+ <div class="w-32vw">
|
|
|
|
|
+ <el-input-number
|
|
|
|
|
+ v-model="formData.summary"
|
|
|
|
|
+ :precision="2"
|
|
|
|
|
+ :min="0.01"
|
|
|
|
|
+ controls-position="right"
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="已售数量" prop="title">
|
|
|
|
|
+ <div class="w-32vw">
|
|
|
|
|
+ <el-input-number
|
|
|
|
|
+ v-model="formData.summary"
|
|
|
|
|
+ :precision="2"
|
|
|
|
|
+ :min="0.01"
|
|
|
|
|
+ controls-position="right"
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="商品推荐" prop="title">
|
|
|
|
|
+ <div class="w-32vw">
|
|
|
|
|
+ <el-input-number
|
|
|
|
|
+ v-model="formData.summary"
|
|
|
|
|
+ :precision="2"
|
|
|
|
|
+ :min="0.01"
|
|
|
|
|
+ controls-position="right"
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="会员专属" prop="title">
|
|
|
|
|
+ <div class="w-32vw">
|
|
|
|
|
+ <el-input-number
|
|
|
|
|
+ v-model="formData.summary"
|
|
|
|
|
+ :precision="2"
|
|
|
|
|
+ :min="0.01"
|
|
|
|
|
+ controls-position="right"
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="买就送" prop="title">
|
|
|
|
|
+ <div class="w-32vw">
|
|
|
|
|
+ <el-input-number
|
|
|
|
|
+ v-model="formData.summary"
|
|
|
|
|
+ :precision="2"
|
|
|
|
|
+ :min="0.01"
|
|
|
|
|
+ controls-position="right"
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>-->
|
|
|
|
|
+ </el-form>
|
|
|
|
|
+ </el-card>
|
|
|
|
|
+ <footer-btns>
|
|
|
|
|
+ <el-button type="primary" @click="handleSave">保存</el-button>
|
|
|
|
|
+ </footer-btns>
|
|
|
|
|
+ </div>
|
|
|
|
|
+</template>
|
|
|
|
|
+
|
|
|
|
|
+<script lang="ts" setup name="articleListsEdit">
|
|
|
|
|
+import type {FormInstance} from 'element-plus'
|
|
|
|
|
+import feedback from '@/utils/feedback'
|
|
|
|
|
+import {useDictOptions} from '@/hooks/useDictOptions'
|
|
|
|
|
+import {articleAdd, articleCateAll, articleDetail, articleEdit} from '@/api/article'
|
|
|
|
|
+import useMultipleTabs from '@/hooks/useMultipleTabs'
|
|
|
|
|
+import Editor from '@/components/editor/index.vue'
|
|
|
|
|
+import {goodsCategoryPage} from "@/api/goods/category";
|
|
|
|
|
+
|
|
|
|
|
+const route = useRoute()
|
|
|
|
|
+const router = useRouter()
|
|
|
|
|
+const formData = reactive<any>({
|
|
|
|
|
+ id: '',
|
|
|
|
|
+ title: '',
|
|
|
|
|
+ image: '',
|
|
|
|
|
+ cid: '',
|
|
|
|
|
+ intro: '',
|
|
|
|
|
+ author: '',
|
|
|
|
|
+ content: '',
|
|
|
|
|
+ visit: 0,
|
|
|
|
|
+ sort: 0,
|
|
|
|
|
+ isShow: 1,
|
|
|
|
|
+ summary: 0,
|
|
|
|
|
+ specs: [] // 规格
|
|
|
|
|
+})
|
|
|
|
|
+const goodsTagOption = [{
|
|
|
|
|
+ value: '日用品',
|
|
|
|
|
+ label: '日用品'
|
|
|
|
|
+}]
|
|
|
|
|
+
|
|
|
|
|
+const {optionsData} = useDictOptions<{
|
|
|
|
|
+ goodsCategory: any[]
|
|
|
|
|
+}>({
|
|
|
|
|
+ goodsCategory: {
|
|
|
|
|
+ api: goodsCategoryPage,
|
|
|
|
|
+ params: {pageNo: 1, pageSize: 100},
|
|
|
|
|
+ transformData(data: any) {
|
|
|
|
|
+ return data.lists
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+const {removeTab} = useMultipleTabs()
|
|
|
|
|
+const formRef = shallowRef<FormInstance>()
|
|
|
|
|
+const rules = reactive({
|
|
|
|
|
+ title: [{required: true, message: '请输入商品名称', trigger: 'blur'}],
|
|
|
|
|
+ cid: [{required: true, message: '请选择商品分类', trigger: 'blur'}],
|
|
|
|
|
+ summary: [{required: true, message: '请输入价格', trigger: 'blur'}],
|
|
|
|
|
+ image: [{required: true, message: '添加商品图片', trigger: 'change'}]
|
|
|
|
|
+})
|
|
|
|
|
+
|
|
|
|
|
+// getDetails 函数修改
|
|
|
|
|
+const getDetails = async () => {
|
|
|
|
|
+ const data = await articleDetail({
|
|
|
|
|
+ id: route.query.id
|
|
|
|
|
+ })
|
|
|
|
|
+ Object.keys(formData).forEach((key) => {
|
|
|
|
|
+ //@ts-ignore
|
|
|
|
|
+ formData[key] = data[key];
|
|
|
|
|
+ if (key == 'summary') {
|
|
|
|
|
+ formData[key] = Number(formData[key])
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ // 转换规格数据
|
|
|
|
|
+ if (data.specsList && data.specsList.length > 0) {
|
|
|
|
|
+ const specsMap = new Map()
|
|
|
|
|
+ data.specsList.forEach((spec: { name: string; value: string; id: number }) => {
|
|
|
|
|
+ if (!specsMap.has(spec.name)) {
|
|
|
|
|
+ specsMap.set(spec.name, {
|
|
|
|
|
+ name: spec.name,
|
|
|
|
|
+ items: [],
|
|
|
|
|
+ inputVisible: false,
|
|
|
|
|
+ inputValue: ''
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ specsMap.get(spec.name).items.push({
|
|
|
|
|
+ id: spec.id,
|
|
|
|
|
+ tag: spec.value
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+ formData.specs = Array.from(specsMap.values())
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const handleSave = async () => {
|
|
|
|
|
+ await formRef.value?.validate()
|
|
|
|
|
+ if (formData.specs && formData.specs.length > 5) {
|
|
|
|
|
+ feedback.msgWarning('最多添加5个规格')
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ const specsList: Array<{
|
|
|
|
|
+ name: string;
|
|
|
|
|
+ value: string;
|
|
|
|
|
+ status: number;
|
|
|
|
|
+ articleId: number | null;
|
|
|
|
|
+ id: number | null;
|
|
|
|
|
+ img: string | null;
|
|
|
|
|
+ }> = [];
|
|
|
|
|
+ formData.specs?.forEach((spec: { name: string; items: Array<{ id: number | null, tag: string }> }) => {
|
|
|
|
|
+ spec.items.forEach(item => {
|
|
|
|
|
+ specsList.push({
|
|
|
|
|
+ name: spec.name,
|
|
|
|
|
+ value: item.tag,
|
|
|
|
|
+ status: 1,
|
|
|
|
|
+ articleId: formData.id || null,
|
|
|
|
|
+ id: item.id || null,
|
|
|
|
|
+ img: null
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ const submitData = {
|
|
|
|
|
+ ...formData,
|
|
|
|
|
+ specsList
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (route.query.id) {
|
|
|
|
|
+ await articleEdit(submitData)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ await articleAdd(submitData)
|
|
|
|
|
+ }
|
|
|
|
|
+ feedback.msgSuccess('操作成功')
|
|
|
|
|
+ removeTab()
|
|
|
|
|
+ router.back()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+route.query.id && getDetails()
|
|
|
|
|
+
|
|
|
|
|
+// 添加规格
|
|
|
|
|
+const handleSpecs = () => {
|
|
|
|
|
+ if (!formData.specs) {
|
|
|
|
|
+ formData.specs = []
|
|
|
|
|
+ }
|
|
|
|
|
+ formData.specs.push({
|
|
|
|
|
+ name: '',
|
|
|
|
|
+ items: [],
|
|
|
|
|
+ inputVisible: false,
|
|
|
|
|
+ inputValue: ''
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 删除规格
|
|
|
|
|
+const removeSpec = (index: number) => {
|
|
|
|
|
+ formData.specs.splice(index, 1)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 显示输入框
|
|
|
|
|
+const showInput = (specIndex: number) => {
|
|
|
|
|
+ formData.specs[specIndex].inputVisible = true
|
|
|
|
|
+ nextTick(() => {
|
|
|
|
|
+ InputRef.value?.input?.focus()
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 确认输入
|
|
|
|
|
+const handleInputConfirm = (specIndex: number) => {
|
|
|
|
|
+ const spec = formData.specs[specIndex]
|
|
|
|
|
+ if (spec.inputValue) {
|
|
|
|
|
+ spec.items.push({
|
|
|
|
|
+ id: null,
|
|
|
|
|
+ tag: spec.inputValue
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ spec.inputVisible = false
|
|
|
|
|
+ spec.inputValue = ''
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 删除标签
|
|
|
|
|
+const handleClose = (specIndex: number, tagIndex: number) => {
|
|
|
|
|
+ formData.specs[specIndex].items.splice(tagIndex, 1)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 添加规格项
|
|
|
|
|
+const handleAddSpecItem = (index: number) => {
|
|
|
|
|
+ showInput(index)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const InputRef = ref()
|
|
|
|
|
+</script>
|
|
|
|
|
+<style>
|
|
|
|
|
+.w-32vw {
|
|
|
|
|
+ width: 32vw;
|
|
|
|
|
+}
|
|
|
|
|
+</style>
|