|
|
@@ -0,0 +1,443 @@
|
|
|
+<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" @change="handleCategory">
|
|
|
+ <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="needKnown">
|
|
|
+ <div class="w-32vw">
|
|
|
+ <el-input
|
|
|
+ v-model="formData.needKnown"
|
|
|
+ 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>
|
|
|
+ <el-form-item label="可入住日期" v-if="showDateRange">
|
|
|
+ <div class="w-32vw">
|
|
|
+ <el-date-picker
|
|
|
+ v-model="selectedDates"
|
|
|
+ type="dates"
|
|
|
+ range-separator="至"
|
|
|
+ start-placeholder="开始日期"
|
|
|
+ end-placeholder="结束日期"
|
|
|
+ value-format="YYYY-MM-DD"
|
|
|
+ placeholder="选择一个或多个日期"
|
|
|
+ style="width: 100%; margin-bottom: 10px"
|
|
|
+ />
|
|
|
+ <div v-if="selectedDates.length > 0" class="selected-dates">
|
|
|
+ <div class="date-tag" v-for="(date, index) in processedDates" :key="index">
|
|
|
+ {{ date }}
|
|
|
+ <el-icon @click="removeDate(date)" style="cursor: pointer; margin-left: 5px;"><Close /></el-icon>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </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="video">
|
|
|
+ <div class="w-32vw">
|
|
|
+ <material-picker v-model="formData.video" type="video" :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>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="xl:flex">
|
|
|
+<!-- <el-form-item label="商品详情" prop="detail">-->
|
|
|
+<!-- <div style="width: 100%">-->
|
|
|
+<!-- <editor v-model="formData.detail" :height="500"/>-->
|
|
|
+<!-- </div>-->
|
|
|
+<!-- </el-form-item>-->
|
|
|
+ <el-form-item label="商品详情图" prop="detailImages">
|
|
|
+ <div style="width: 100%">
|
|
|
+ <material-picker v-model="formData.detailImages" :limit="20"/>
|
|
|
+ </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="saleNum">
|
|
|
+ <div class="w-32vw">
|
|
|
+ <el-input-number
|
|
|
+ v-model="formData.saleNum"
|
|
|
+ :precision="2"
|
|
|
+ :min="0.01"
|
|
|
+ controls-position="right"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="商品推荐" prop="recommndType">
|
|
|
+ <div class="w-32vw">
|
|
|
+ <el-input-number
|
|
|
+ v-model="formData.recommndType"
|
|
|
+ :precision="2"
|
|
|
+ :min="0.01"
|
|
|
+ controls-position="right"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="会员专属" prop="onlyMember">
|
|
|
+ <div class="w-32vw">
|
|
|
+ <el-input-number
|
|
|
+ v-model="formData.onlyMember"
|
|
|
+ :precision="2"
|
|
|
+ :min="0.01"
|
|
|
+ controls-position="right"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="买就送" prop="buyToGift">
|
|
|
+ <div class="w-32vw">
|
|
|
+ <el-input-number
|
|
|
+ v-model="formData.buyToGift"
|
|
|
+ :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 useMultipleTabs from '@/hooks/useMultipleTabs'
|
|
|
+import Editor from '@/components/editor/index.vue'
|
|
|
+import {goodsCategoryPage} from "@/api/goods/category";
|
|
|
+import {goodsEdit, goodsDetail} from "@/api/goods/list";
|
|
|
+import { getShopId } from '@/utils/auth'
|
|
|
+import { Close } from '@element-plus/icons-vue'
|
|
|
+
|
|
|
+const route = useRoute()
|
|
|
+const router = useRouter()
|
|
|
+const formData = reactive<any>({
|
|
|
+ id: null,
|
|
|
+ title: '',
|
|
|
+ productCategoryId: null,
|
|
|
+ price: null,
|
|
|
+ originalPrice: null,
|
|
|
+ stock: 0,
|
|
|
+ sort: 0,
|
|
|
+ unit: 0,
|
|
|
+ tags: 0,
|
|
|
+ isShow: 1,
|
|
|
+ needKnown: null,
|
|
|
+ guarantee: null,
|
|
|
+ image: '',
|
|
|
+ mainResourceList: null,
|
|
|
+ video: '',
|
|
|
+ detail: '',
|
|
|
+ useDates: '',
|
|
|
+ shopId: getShopId()
|
|
|
+})
|
|
|
+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'}]
|
|
|
+})
|
|
|
+
|
|
|
+const selectedDates = ref<string[]>([])
|
|
|
+const showDateRange = ref(false)
|
|
|
+
|
|
|
+// 计算处理后的所有日期(包括单个日期和日期范围)
|
|
|
+const processedDates = computed(() => {
|
|
|
+ const dates = [...selectedDates.value]
|
|
|
+ return dates.sort()
|
|
|
+})
|
|
|
+
|
|
|
+// 移除日期
|
|
|
+const removeDate = (date: string) => {
|
|
|
+ const index = selectedDates.value.indexOf(date)
|
|
|
+ if (index > -1) {
|
|
|
+ selectedDates.value.splice(index, 1)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 在保存时将日期数据加入formData
|
|
|
+const handleSave = async () => {
|
|
|
+ await formRef.value?.validate()
|
|
|
+
|
|
|
+ const submitData = {
|
|
|
+ ...formData,
|
|
|
+ availableDates: processedDates.value // 添加可入住日期数据
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构建主资源列表(包括轮播图和详情图)
|
|
|
+ const mainResourceList: any[] = [];
|
|
|
+
|
|
|
+ if (formData.bannerImages) {
|
|
|
+ formData.bannerImages.forEach((item: any, index: number) => {
|
|
|
+ mainResourceList.push({
|
|
|
+ resourceType: 1, // 1轮播图
|
|
|
+ resourceName: `轮播图${index + 1}`,
|
|
|
+ resourceUrl: item,
|
|
|
+ sort: index + 1
|
|
|
+ });
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ if (formData.detailImages) {
|
|
|
+ formData.detailImages.forEach((item: any, index: number) => {
|
|
|
+ mainResourceList.push({
|
|
|
+ resourceType: 2, // 2详情图
|
|
|
+ resourceName: `详情图${index + 1}`,
|
|
|
+ resourceUrl: item,
|
|
|
+ sort: index + 1
|
|
|
+ });
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ submitData.mainResourceList = mainResourceList;
|
|
|
+ if(formData.tags){
|
|
|
+ submitData.tags = formData.tags.join(',')
|
|
|
+ }
|
|
|
+ if(selectedDates.value.length > 0){
|
|
|
+ const dates = [...selectedDates.value]
|
|
|
+ // 使用日期排序而不是字符串排序
|
|
|
+ const sortedDates = dates.sort((a, b) => new Date(a).getTime() - new Date(b).getTime())
|
|
|
+ submitData.startDate = sortedDates[0]
|
|
|
+ submitData.endDate = sortedDates[selectedDates.value.length - 1]
|
|
|
+ submitData.useDates = sortedDates.join(',')
|
|
|
+ }
|
|
|
+ feedback.msgSuccess('操作成功')
|
|
|
+ if (route.query.id) {
|
|
|
+ await goodsEdit(submitData)
|
|
|
+ } else {
|
|
|
+ await goodsEdit(submitData)
|
|
|
+ }
|
|
|
+ feedback.msgSuccess('操作成功')
|
|
|
+ removeTab()
|
|
|
+ router.back()
|
|
|
+}
|
|
|
+
|
|
|
+const handleCategory = (val:any) => {
|
|
|
+ const selectedCategory = optionsData.goodsCategory.find((item: any) => item.id === val);
|
|
|
+ showDateRange.value = false
|
|
|
+ selectedDates.value = []
|
|
|
+ if(selectedCategory && selectedCategory.choseDateState === 1){
|
|
|
+ showDateRange.value = true
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 在获取详情时处理日期数据
|
|
|
+const getDetails = async () => {
|
|
|
+ const data = await goodsDetail({
|
|
|
+ id: route.query.id
|
|
|
+ })
|
|
|
+ Object.keys(formData).forEach((key) => {
|
|
|
+ //@ts-ignore
|
|
|
+ formData[key] = data[key];
|
|
|
+ if (key == 'summary') {
|
|
|
+ formData[key] = Number(formData[key])
|
|
|
+ }
|
|
|
+ if(key === 'video' && !data['video']){
|
|
|
+ formData[key] = ''
|
|
|
+ }
|
|
|
+ if(key === 'mainResourceList' && data['mainResourceList']){
|
|
|
+ formData.bannerImages = data['mainResourceList'].filter((item:any) => item.resourceType === 1).map((item:any) => {
|
|
|
+ return item.resourceUrl
|
|
|
+ })
|
|
|
+ formData.detailImages = data['mainResourceList'].filter((item:any) => item.resourceType === 2).map((item:any) => {
|
|
|
+ return item.resourceUrl
|
|
|
+ })
|
|
|
+ }
|
|
|
+ if(key === 'tags' && data['tags']){
|
|
|
+ formData[key] = data[key].split(',')
|
|
|
+ }
|
|
|
+ console.log(key,data[key])
|
|
|
+ if(key === 'useDates' && data['useDates']){
|
|
|
+ console.log('显示 日期')
|
|
|
+ showDateRange.value = true
|
|
|
+ selectedDates.value = data['useDates'].split(',')
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+route.query.id && getDetails()
|
|
|
+
|
|
|
+</script>
|
|
|
+<style>
|
|
|
+.w-32vw {
|
|
|
+ width: 32vw;
|
|
|
+}
|
|
|
+
|
|
|
+.selected-dates {
|
|
|
+ display: flex;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ gap: 8px;
|
|
|
+ margin-top: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.date-tag {
|
|
|
+ background-color: #ecf5ff;
|
|
|
+ border: 1px solid #d9ecff;
|
|
|
+ border-radius: 4px;
|
|
|
+ padding: 4px 8px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ font-size: 12px;
|
|
|
+}
|
|
|
+</style>
|