parent
							
								
									9e7fe34ae8
								
							
						
					
					
						commit
						d35ac547ac
					
				|  | @ -37,7 +37,7 @@ export interface Spu { | |||
|   brandId?: number | null // 商品品牌编号
 | ||||
|   specType?: boolean // 商品规格
 | ||||
|   subCommissionType?: boolean // 分销类型
 | ||||
|   skus: Sku[] // sku数组
 | ||||
|   skus?: Sku[] // sku数组
 | ||||
|   description?: string // 商品详情
 | ||||
|   sort?: number // 商品排序
 | ||||
|   giveIntegral?: number // 赠送积分
 | ||||
|  |  | |||
|  | @ -379,6 +379,19 @@ const remainingRouter: AppRouteRecordRaw[] = [ | |||
|           title: '编辑商品', | ||||
|           activeMenu: '/product/product-spu' | ||||
|         } | ||||
|       }, | ||||
|       { | ||||
|         path: 'productSpuDetail/:spuId(\\d+)', | ||||
|         component: () => import('@/views/mall/product/spu/addForm.vue'), | ||||
|         name: 'productSpuDetail', | ||||
|         meta: { | ||||
|           noCache: true, | ||||
|           hidden: true, | ||||
|           canTo: true, | ||||
|           icon: 'ep:view', | ||||
|           title: '商品详情', | ||||
|           activeMenu: '/product/product-spu' | ||||
|         } | ||||
|       } | ||||
|     ] | ||||
|   } | ||||
|  |  | |||
|  | @ -310,26 +310,30 @@ export const handleTree2 = (data, id, parentId, children, rootId) => { | |||
|  * @param nodeId 需要判断在什么层级的数据 | ||||
|  * @param level 检查的级别, 默认检查到二级 | ||||
|  */ | ||||
| export const checkSelectedNode = (tree: any[], nodeId, level = 2) => { | ||||
| export const checkSelectedNode = (tree: any[], nodeId: any, level = 2): boolean => { | ||||
|   if (typeof tree === 'undefined' || !Array.isArray(tree) || tree.length === 0) { | ||||
|     console.warn('tree must be an array') | ||||
|     return false | ||||
|   } | ||||
| 
 | ||||
|   // 校验是否是一级节点
 | ||||
|   if (tree.some((item) => item.id === nodeId)) { | ||||
|     return false | ||||
|   } | ||||
| 
 | ||||
|   // 递归计数
 | ||||
|   let count = 1 | ||||
| 
 | ||||
|   // 深层次校验
 | ||||
|   function performAThoroughValidation(arr) { | ||||
|   function performAThoroughValidation(arr: any[]): boolean { | ||||
|     count += 1 | ||||
|     for (const item of arr) { | ||||
|       if (item.id === nodeId) { | ||||
|         return true | ||||
|       } else if (typeof item.children !== 'undefined' && item.children.length !== 0) { | ||||
|         performAThoroughValidation(item.children) | ||||
|         if (performAThoroughValidation(item.children)) { | ||||
|           return true | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     return false | ||||
|  | @ -339,11 +343,15 @@ export const checkSelectedNode = (tree: any[], nodeId, level = 2) => { | |||
|     count = 1 | ||||
|     if (performAThoroughValidation(item.children)) { | ||||
|       // 找到后对比是否是期望的层级
 | ||||
|       if (count >= level) return true | ||||
|       if (count >= level) { | ||||
|         return true | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   return false | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * 获取节点的完整结构 | ||||
|  * @param tree 树数据 | ||||
|  | @ -367,7 +375,10 @@ export const treeToString = (tree: any[], nodeId) => { | |||
|         str += `/${item.name}` | ||||
|         return true | ||||
|       } else if (typeof item.children !== 'undefined' && item.children.length !== 0) { | ||||
|         performAThoroughValidation(item.children) | ||||
|         str += `/${item.name}` | ||||
|         if (performAThoroughValidation(item.children)) { | ||||
|           return true | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     return false | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ | |||
|         <BasicInfoForm | ||||
|           ref="basicInfoRef" | ||||
|           v-model:activeName="activeName" | ||||
|           :is-detail="isDetail" | ||||
|           :propFormData="formData" | ||||
|         /> | ||||
|       </el-tab-pane> | ||||
|  | @ -12,6 +13,7 @@ | |||
|         <DescriptionForm | ||||
|           ref="descriptionRef" | ||||
|           v-model:activeName="activeName" | ||||
|           :is-detail="isDetail" | ||||
|           :propFormData="formData" | ||||
|         /> | ||||
|       </el-tab-pane> | ||||
|  | @ -19,6 +21,7 @@ | |||
|         <OtherSettingsForm | ||||
|           ref="otherSettingsRef" | ||||
|           v-model:activeName="activeName" | ||||
|           :is-detail="isDetail" | ||||
|           :propFormData="formData" | ||||
|         /> | ||||
|       </el-tab-pane> | ||||
|  | @ -42,11 +45,12 @@ import { convertToInteger, formatToFraction } from '@/utils' | |||
| const { t } = useI18n() // 国际化 | ||||
| const message = useMessage() // 消息弹窗 | ||||
| const { push, currentRoute } = useRouter() // 路由 | ||||
| const { params } = useRoute() // 查询参数 | ||||
| const { params, name } = useRoute() // 查询参数 | ||||
| const { delView } = useTagsViewStore() // 视图操作 | ||||
| 
 | ||||
| const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 | ||||
| const activeName = ref('basicInfo') // Tag 激活的窗口 | ||||
| const isDetail = ref(false) // 是否查看详情 | ||||
| const basicInfoRef = ref<ComponentRef<typeof BasicInfoForm>>() // 商品信息Ref | ||||
| const descriptionRef = ref<ComponentRef<typeof DescriptionForm>>() // 商品详情Ref | ||||
| const otherSettingsRef = ref<ComponentRef<typeof OtherSettingsForm>>() // 其他设置Ref | ||||
|  | @ -90,12 +94,13 @@ const formData = ref<ProductSpuApi.Spu>({ | |||
| 
 | ||||
| /** 获得详情 */ | ||||
| const getDetail = async () => { | ||||
|   console.log(name) | ||||
|   const id = params.spuId as number | ||||
|   if (id) { | ||||
|     formLoading.value = true | ||||
|     try { | ||||
|       const res = (await ProductSpuApi.getSpu(id)) as ProductSpuApi.Spu | ||||
|       res.skus.forEach((item) => { | ||||
|       res.skus!.forEach((item) => { | ||||
|         // 回显价格分转元 | ||||
|         item.price = formatToFraction(item.price) | ||||
|         item.marketPrice = formatToFraction(item.marketPrice) | ||||
|  | @ -123,7 +128,7 @@ const submitForm = async () => { | |||
|     // 深拷贝一份, 这样最终 server 端不满足,不需要恢复, | ||||
|     const deepCopyFormData = cloneDeep(unref(formData.value)) | ||||
|     // 兜底处理 sku 空数据 | ||||
|     formData.value.skus.forEach((sku) => { | ||||
|     formData.value.skus!.forEach((sku) => { | ||||
|       // 因为是空数据这里判断一下商品条码是否为空就行 | ||||
|       if (sku.barCode === '') { | ||||
|         const index = deepCopyFormData.skus.findIndex( | ||||
|  | @ -171,7 +176,6 @@ const close = () => { | |||
|   delView(unref(currentRoute)) | ||||
|   push('/product/product-spu') | ||||
| } | ||||
| 
 | ||||
| /** 初始化 */ | ||||
| onMounted(async () => { | ||||
|   await getDetail() | ||||
|  |  | |||
|  | @ -1,5 +1,11 @@ | |||
| <template> | ||||
|   <el-form ref="productSpuBasicInfoRef" :model="formData" :rules="rules" label-width="120px"> | ||||
|   <el-form | ||||
|     v-if="!isDetail" | ||||
|     ref="productSpuBasicInfoRef" | ||||
|     :model="formData" | ||||
|     :rules="rules" | ||||
|     label-width="120px" | ||||
|   > | ||||
|     <el-row> | ||||
|       <el-col :span="12"> | ||||
|         <el-form-item label="商品名称" prop="name"> | ||||
|  | @ -115,18 +121,72 @@ | |||
|     </el-row> | ||||
|   </el-form> | ||||
|   <ProductAttributesAddForm ref="attributesAddFormRef" :propertyList="propertyList" /> | ||||
|   <!-- 详情跟表单放在一块可以共用已有功能,再抽离成组件有点过度封装的感觉 --> | ||||
|   <Descriptions v-if="isDetail" :data="formData" :schema="allSchemas.detailSchema"> | ||||
|     <template #categoryId="{ row }"> {{ categoryString(row.categoryId) }}</template> | ||||
|     <template #brandId="{ row }"> | ||||
|       {{ brandList.find((item) => item.id === row.brandId)?.name }} | ||||
|     </template> | ||||
|     <template #specType="{ row }"> | ||||
|       {{ row.specType ? '多规格' : '单规格' }} | ||||
|     </template> | ||||
|     <template #subCommissionType="{ row }"> | ||||
|       {{ row.subCommissionType ? '自行设置' : '默认设置' }} | ||||
|     </template> | ||||
|     <template #picUrl="{ row }"> | ||||
|       <el-image :src="row.picUrl" class="w-60px h-60px" @click="imagePreview(row.picUrl)" /> | ||||
|     </template> | ||||
|     <template #sliderPicUrls="{ row }"> | ||||
|       <el-image | ||||
|         v-for="(item, index) in row.sliderPicUrls" | ||||
|         :key="index" | ||||
|         :src="item.url" | ||||
|         class="w-60px h-60px mr-10px" | ||||
|         @click="imagePreview(row.sliderPicUrls)" | ||||
|       /> | ||||
|     </template> | ||||
|     <template #skus> | ||||
|       <SkuList | ||||
|         ref="skuDetailListRef" | ||||
|         :is-detail="isDetail" | ||||
|         :prop-form-data="formData" | ||||
|         :propertyList="propertyList" | ||||
|       /> | ||||
|     </template> | ||||
|   </Descriptions> | ||||
| </template> | ||||
| <script lang="ts" name="ProductSpuBasicInfoForm" setup> | ||||
| import { PropType } from 'vue' | ||||
| import { copyValueToTarget } from '@/utils' | ||||
| import { propTypes } from '@/utils/propTypes' | ||||
| import { checkSelectedNode, defaultProps, handleTree } from '@/utils/tree' | ||||
| import { checkSelectedNode, defaultProps, handleTree, treeToString } from '@/utils/tree' | ||||
| import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' | ||||
| import type { Spu } from '@/api/mall/product/spu' | ||||
| import { UploadImg, UploadImgs } from '@/components/UploadFile' | ||||
| import { ProductAttributes, ProductAttributesAddForm, SkuList } from './index' | ||||
| import { basicInfoSchema } from './spu.data' | ||||
| import { createImageViewer } from '@/components/ImageViewer' | ||||
| import * as ProductCategoryApi from '@/api/mall/product/category' | ||||
| import { getSimpleBrandList } from '@/api/mall/product/brand' | ||||
| import { isArray } from '@/utils/is' | ||||
| 
 | ||||
| // ====== 商品详情相关操作 ====== | ||||
| const { allSchemas } = useCrudSchemas(basicInfoSchema) | ||||
| /** 商品图预览 */ | ||||
| const imagePreview = (args) => { | ||||
|   const urlList = [] | ||||
|   if (isArray(args)) { | ||||
|     args.forEach((item) => { | ||||
|       urlList.push(item.url) | ||||
|     }) | ||||
|   } else { | ||||
|     urlList.push(args) | ||||
|   } | ||||
|   createImageViewer({ | ||||
|     urlList | ||||
|   }) | ||||
| } | ||||
| // ====== end ====== | ||||
| 
 | ||||
| const message = useMessage() // 消息弹窗 | ||||
| 
 | ||||
|  | @ -135,7 +195,8 @@ const props = defineProps({ | |||
|     type: Object as PropType<Spu>, | ||||
|     default: () => {} | ||||
|   }, | ||||
|   activeName: propTypes.string.def('') | ||||
|   activeName: propTypes.string.def(''), | ||||
|   isDetail: propTypes.bool.def(false) // 是否作为详情组件 | ||||
| }) | ||||
| const attributesAddFormRef = ref() // 添加商品属性表单 | ||||
| const productSpuBasicInfoRef = ref() // 表单 Ref | ||||
|  | @ -149,11 +210,11 @@ const formData = reactive<Spu>({ | |||
|   name: '', // 商品名称 | ||||
|   categoryId: null, // 商品分类 | ||||
|   keyword: '', // 关键字 | ||||
|   unit: '', // 单位 | ||||
|   unit: null, // 单位 | ||||
|   picUrl: '', // 商品封面图 | ||||
|   sliderPicUrls: [], // 商品轮播图 | ||||
|   introduction: '', // 商品简介 | ||||
|   deliveryTemplateId: 1, // 运费模版 | ||||
|   deliveryTemplateId: null, // 运费模版 | ||||
|   brandId: null, // 商品品牌 | ||||
|   specType: false, // 商品规格 | ||||
|   subCommissionType: false, // 分销类型 | ||||
|  | @ -273,9 +334,16 @@ const categoryList = ref([]) // 分类树 | |||
| const nodeClick = () => { | ||||
|   if (!checkSelectedNode(categoryList.value, formData.categoryId)) { | ||||
|     formData.categoryId = null | ||||
|     message.warning('必须选择二级节点!!') | ||||
|     message.warning('必须选择二级及以下节点!!') | ||||
|   } | ||||
| } | ||||
| /** | ||||
|  * 获取分类的节点的完整结构 | ||||
|  * @param categoryId 分类id | ||||
|  */ | ||||
| const categoryString = (categoryId) => { | ||||
|   return treeToString(categoryList.value, categoryId) | ||||
| } | ||||
| const brandList = ref([]) // 精简商品品牌列表 | ||||
| onMounted(async () => { | ||||
|   // 获得分类树 | ||||
|  |  | |||
|  | @ -1,10 +1,27 @@ | |||
| <template> | ||||
|   <el-form ref="descriptionFormRef" :model="formData" :rules="rules" label-width="120px"> | ||||
|   <el-form | ||||
|     v-if="!isDetail" | ||||
|     ref="descriptionFormRef" | ||||
|     :model="formData" | ||||
|     :rules="rules" | ||||
|     label-width="120px" | ||||
|   > | ||||
|     <!--富文本编辑器组件--> | ||||
|     <el-form-item label="商品详情" prop="description"> | ||||
|       <Editor v-model:modelValue="formData.description" /> | ||||
|     </el-form-item> | ||||
|   </el-form> | ||||
|   <Descriptions | ||||
|     v-if="isDetail" | ||||
|     :data="formData" | ||||
|     :schema="allSchemas.detailSchema" | ||||
|     class="descriptionFormDescriptions" | ||||
|   > | ||||
|     <!-- 展示 HTML 内容 --> | ||||
|     <template #description="{ row }"> | ||||
|       <div style="width: 600px" v-html="row.description"></div> | ||||
|     </template> | ||||
|   </Descriptions> | ||||
| </template> | ||||
| <script lang="ts" name="DescriptionForm" setup> | ||||
| import type { Spu } from '@/api/mall/product/spu' | ||||
|  | @ -12,6 +29,9 @@ import { Editor } from '@/components/Editor' | |||
| import { PropType } from 'vue' | ||||
| import { propTypes } from '@/utils/propTypes' | ||||
| import { copyValueToTarget } from '@/utils' | ||||
| import { descriptionSchema } from './spu.data' | ||||
| 
 | ||||
| const { allSchemas } = useCrudSchemas(descriptionSchema) | ||||
| 
 | ||||
| const message = useMessage() // 消息弹窗 | ||||
| const props = defineProps({ | ||||
|  | @ -19,7 +39,8 @@ const props = defineProps({ | |||
|     type: Object as PropType<Spu>, | ||||
|     default: () => {} | ||||
|   }, | ||||
|   activeName: propTypes.string.def('') | ||||
|   activeName: propTypes.string.def(''), | ||||
|   isDetail: propTypes.bool.def(false) // 是否作为详情组件 | ||||
| }) | ||||
| const descriptionFormRef = ref() // 表单Ref | ||||
| const formData = ref<Spu>({ | ||||
|  |  | |||
|  | @ -1,5 +1,11 @@ | |||
| <template> | ||||
|   <el-form ref="otherSettingsFormRef" :model="formData" :rules="rules" label-width="120px"> | ||||
|   <el-form | ||||
|     v-if="!isDetail" | ||||
|     ref="otherSettingsFormRef" | ||||
|     :model="formData" | ||||
|     :rules="rules" | ||||
|     label-width="120px" | ||||
|   > | ||||
|     <el-row> | ||||
|       <el-col :span="24"> | ||||
|         <el-row :gutter="20"> | ||||
|  | @ -50,12 +56,38 @@ | |||
|       </el-col> | ||||
|     </el-row> | ||||
|   </el-form> | ||||
|   <Descriptions v-if="isDetail" :data="formData" :schema="allSchemas.detailSchema"> | ||||
|     <template #recommendHot="{ row }"> | ||||
|       {{ row.recommendHot ? '是' : '否' }} | ||||
|     </template> | ||||
|     <template #recommendBenefit="{ row }"> | ||||
|       {{ row.recommendBenefit ? '是' : '否' }} | ||||
|     </template> | ||||
|     <template #recommendBest="{ row }"> | ||||
|       {{ row.recommendBest ? '是' : '否' }} | ||||
|     </template> | ||||
|     <template #recommendNew="{ row }"> | ||||
|       {{ row.recommendNew ? '是' : '否' }} | ||||
|     </template> | ||||
|     <template #recommendGood="{ row }"> | ||||
|       {{ row.recommendGood ? '是' : '否' }} | ||||
|     </template> | ||||
|     <template #activityOrders> | ||||
|       <el-tag>默认</el-tag> | ||||
|       <el-tag class="ml-2" type="success">秒杀</el-tag> | ||||
|       <el-tag class="ml-2" type="info">砍价</el-tag> | ||||
|       <el-tag class="ml-2" type="warning">拼团</el-tag> | ||||
|     </template> | ||||
|   </Descriptions> | ||||
| </template> | ||||
| <script lang="ts" name="OtherSettingsForm" setup> | ||||
| import type { Spu } from '@/api/mall/product/spu' | ||||
| import { PropType } from 'vue' | ||||
| import { propTypes } from '@/utils/propTypes' | ||||
| import { copyValueToTarget } from '@/utils' | ||||
| import { otherSettingsSchema } from './spu.data' | ||||
| 
 | ||||
| const { allSchemas } = useCrudSchemas(otherSettingsSchema) | ||||
| 
 | ||||
| const message = useMessage() // 消息弹窗 | ||||
| 
 | ||||
|  | @ -64,7 +96,8 @@ const props = defineProps({ | |||
|     type: Object as PropType<Spu>, | ||||
|     default: () => {} | ||||
|   }, | ||||
|   activeName: propTypes.string.def('') | ||||
|   activeName: propTypes.string.def(''), | ||||
|   isDetail: propTypes.bool.def(false) // 是否作为详情组件 | ||||
| }) | ||||
| 
 | ||||
| const otherSettingsFormRef = ref() // 表单Ref | ||||
|  |  | |||
|  | @ -1,5 +1,6 @@ | |||
| <template> | ||||
|   <el-table | ||||
|     v-if="!isDetail" | ||||
|     :data="isBatch ? skuList : formData!.skus" | ||||
|     border | ||||
|     class="tabNumWidth" | ||||
|  | @ -21,8 +22,8 @@ | |||
|         min-width="120" | ||||
|       > | ||||
|         <template #default="{ row }"> | ||||
|           <!-- TODO puhui999:展示成蓝色,有点区分度哈 fix: 字体加粗,颜色使用 #99a9bf 蓝色有点不好看哈哈--> | ||||
|           <span style="font-weight: bold; color: #99a9bf"> | ||||
|           <!-- TODO puhui999:展示成蓝色,有点区分度哈 fix--> | ||||
|           <span style="font-weight: bold; color: #40aaff"> | ||||
|             {{ row.properties[index]?.valueName }} | ||||
|           </span> | ||||
|         </template> | ||||
|  | @ -108,6 +109,84 @@ | |||
|       </template> | ||||
|     </el-table-column> | ||||
|   </el-table> | ||||
|   <el-table | ||||
|     v-if="isDetail" | ||||
|     :data="formData!.skus" | ||||
|     border | ||||
|     max-height="500" | ||||
|     size="small" | ||||
|     style="width: 99%" | ||||
|   > | ||||
|     <el-table-column align="center" label="图片" min-width="80"> | ||||
|       <template #default="{ row }"> | ||||
|         <el-image :src="row.picUrl" class="w-60px h-60px" @click="imagePreview(row.picUrl)" /> | ||||
|       </template> | ||||
|     </el-table-column> | ||||
|     <template v-if="formData!.specType && !isBatch"> | ||||
|       <!--  根据商品属性动态添加 --> | ||||
|       <el-table-column | ||||
|         v-for="(item, index) in tableHeaders" | ||||
|         :key="index" | ||||
|         :label="item.label" | ||||
|         align="center" | ||||
|         min-width="80" | ||||
|       > | ||||
|         <template #default="{ row }"> | ||||
|           <!-- TODO puhui999:展示成蓝色,有点区分度哈 fix--> | ||||
|           <span style="font-weight: bold; color: #40aaff"> | ||||
|             {{ row.properties[index]?.valueName }} | ||||
|           </span> | ||||
|         </template> | ||||
|       </el-table-column> | ||||
|     </template> | ||||
|     <el-table-column align="center" label="商品条码" min-width="100"> | ||||
|       <template #default="{ row }"> | ||||
|         {{ row.barCode }} | ||||
|       </template> | ||||
|     </el-table-column> | ||||
|     <el-table-column align="center" label="销售价(元)" min-width="80"> | ||||
|       <template #default="{ row }"> | ||||
|         {{ row.price }} | ||||
|       </template> | ||||
|     </el-table-column> | ||||
|     <el-table-column align="center" label="市场价(元)" min-width="80"> | ||||
|       <template #default="{ row }"> | ||||
|         {{ row.marketPrice }} | ||||
|       </template> | ||||
|     </el-table-column> | ||||
|     <el-table-column align="center" label="成本价(元)" min-width="80"> | ||||
|       <template #default="{ row }"> | ||||
|         {{ row.costPrice }} | ||||
|       </template> | ||||
|     </el-table-column> | ||||
|     <el-table-column align="center" label="库存" min-width="80"> | ||||
|       <template #default="{ row }"> | ||||
|         {{ row.stock }} | ||||
|       </template> | ||||
|     </el-table-column> | ||||
|     <el-table-column align="center" label="重量(kg)" min-width="80"> | ||||
|       <template #default="{ row }"> | ||||
|         {{ row.weight }} | ||||
|       </template> | ||||
|     </el-table-column> | ||||
|     <el-table-column align="center" label="体积(m^3)" min-width="80"> | ||||
|       <template #default="{ row }"> | ||||
|         {{ row.volume }} | ||||
|       </template> | ||||
|     </el-table-column> | ||||
|     <template v-if="formData!.subCommissionType"> | ||||
|       <el-table-column align="center" label="一级返佣(元)" min-width="80"> | ||||
|         <template #default="{ row }"> | ||||
|           {{ row.subCommissionFirstPrice }} | ||||
|         </template> | ||||
|       </el-table-column> | ||||
|       <el-table-column align="center" label="二级返佣(元)" min-width="80"> | ||||
|         <template #default="{ row }"> | ||||
|           {{ row.subCommissionSecondPrice }} | ||||
|         </template> | ||||
|       </el-table-column> | ||||
|     </template> | ||||
|   </el-table> | ||||
| </template> | ||||
| <script lang="ts" name="SkuList" setup> | ||||
| import { PropType, Ref } from 'vue' | ||||
|  | @ -115,6 +194,7 @@ import { copyValueToTarget } from '@/utils' | |||
| import { propTypes } from '@/utils/propTypes' | ||||
| import { UploadImg } from '@/components/UploadFile' | ||||
| import type { Property, Sku, Spu } from '@/api/mall/product/spu' | ||||
| import { createImageViewer } from '@/components/ImageViewer' | ||||
| 
 | ||||
| const props = defineProps({ | ||||
|   propFormData: { | ||||
|  | @ -125,7 +205,8 @@ const props = defineProps({ | |||
|     type: Array, | ||||
|     default: () => [] | ||||
|   }, | ||||
|   isBatch: propTypes.bool.def(false) // 是否作为批量操作组件 | ||||
|   isBatch: propTypes.bool.def(false), // 是否作为批量操作组件 | ||||
|   isDetail: propTypes.bool.def(false) // 是否作为 sku 详情组件 | ||||
| }) | ||||
| const formData: Ref<Spu | undefined> = ref<Spu>() // 表单数据 | ||||
| const skuList = ref<Sku[]>([ | ||||
|  | @ -143,20 +224,27 @@ const skuList = ref<Sku[]>([ | |||
|   } | ||||
| ]) // 批量添加时的临时数据 | ||||
| 
 | ||||
| /** 商品图预览 */ | ||||
| const imagePreview = (imgUrl: string) => { | ||||
|   createImageViewer({ | ||||
|     urlList: [imgUrl] | ||||
|   }) | ||||
| } | ||||
| 
 | ||||
| /** 批量添加 */ | ||||
| const batchAdd = () => { | ||||
|   formData.value!.skus.forEach((item) => { | ||||
|   formData.value!.skus!.forEach((item) => { | ||||
|     copyValueToTarget(item, skuList.value[0]) | ||||
|   }) | ||||
| } | ||||
| 
 | ||||
| /** 删除 sku */ | ||||
| const deleteSku = (row) => { | ||||
|   const index = formData.value!.skus.findIndex( | ||||
|   const index = formData.value!.skus!.findIndex( | ||||
|     // 直接把列表转成字符串比较 | ||||
|     (sku) => JSON.stringify(sku.properties) === JSON.stringify(row.properties) | ||||
|   ) | ||||
|   formData.value!.skus.splice(index, 1) | ||||
|   formData.value!.skus!.splice(index, 1) | ||||
| } | ||||
| const tableHeaders = ref<{ prop: string; label: string }[]>([]) // 多属性表头 | ||||
| /** | ||||
|  | @ -221,13 +309,13 @@ const generateTableData = (propertyList: any[]) => { | |||
|       subCommissionSecondPrice: 0 | ||||
|     } | ||||
|     // 如果存在属性相同的 sku 则不做处理 | ||||
|     const index = formData.value!.skus.findIndex( | ||||
|     const index = formData.value!.skus!.findIndex( | ||||
|       (sku) => JSON.stringify(sku.properties) === JSON.stringify(row.properties) | ||||
|     ) | ||||
|     if (index !== -1) { | ||||
|       continue | ||||
|     } | ||||
|     formData.value!.skus.push(row) | ||||
|     formData.value!.skus!.push(row) | ||||
|   } | ||||
| } | ||||
| 
 | ||||
|  | @ -236,7 +324,7 @@ const generateTableData = (propertyList: any[]) => { | |||
|  */ | ||||
| const validateData = (propertyList: any[]) => { | ||||
|   const skuPropertyIds = [] | ||||
|   formData.value!.skus.forEach((sku) => | ||||
|   formData.value!.skus!.forEach((sku) => | ||||
|     sku.properties | ||||
|       ?.map((property) => property.propertyId) | ||||
|       .forEach((propertyId) => { | ||||
|  |  | |||
|  | @ -0,0 +1,105 @@ | |||
| import { CrudSchema } from '@/hooks/web/useCrudSchemas' | ||||
| 
 | ||||
| export const basicInfoSchema = reactive<CrudSchema[]>([ | ||||
|   { | ||||
|     label: '商品名称', | ||||
|     field: 'name' | ||||
|   }, | ||||
|   { | ||||
|     label: '关键字', | ||||
|     field: 'keyword' | ||||
|   }, | ||||
|   { | ||||
|     label: '商品简介', | ||||
|     field: 'introduction' | ||||
|   }, | ||||
|   { | ||||
|     label: '商品分类', | ||||
|     field: 'categoryId' | ||||
|   }, | ||||
|   { | ||||
|     label: '商品品牌', | ||||
|     field: 'brandId' | ||||
|   }, | ||||
|   { | ||||
|     label: '商品封面图', | ||||
|     field: 'picUrl' | ||||
|   }, | ||||
|   { | ||||
|     label: '商品轮播图', | ||||
|     field: 'sliderPicUrls' | ||||
|   }, | ||||
|   { | ||||
|     label: '商品视频', | ||||
|     field: 'videoUrl' | ||||
|   }, | ||||
|   { | ||||
|     label: '单位', | ||||
|     field: 'unit', | ||||
|     dictType: DICT_TYPE.PRODUCT_UNIT | ||||
|   }, | ||||
|   { | ||||
|     label: '规格类型', | ||||
|     field: 'specType' | ||||
|   }, | ||||
|   { | ||||
|     label: '分销类型', | ||||
|     field: 'subCommissionType' | ||||
|   }, | ||||
|   { | ||||
|     label: '物流模版', | ||||
|     field: 'deliveryTemplateId' | ||||
|   }, | ||||
|   { | ||||
|     label: '商品属性列表', | ||||
|     field: 'skus' | ||||
|   } | ||||
| ]) | ||||
| export const descriptionSchema = reactive<CrudSchema[]>([ | ||||
|   { | ||||
|     label: '商品详情', | ||||
|     field: 'description' | ||||
|   } | ||||
| ]) | ||||
| export const otherSettingsSchema = reactive<CrudSchema[]>([ | ||||
|   { | ||||
|     label: '商品排序', | ||||
|     field: 'sort' | ||||
|   }, | ||||
|   { | ||||
|     label: '赠送积分', | ||||
|     field: 'giveIntegral' | ||||
|   }, | ||||
|   { | ||||
|     label: '虚拟销量', | ||||
|     field: 'virtualSalesCount' | ||||
|   }, | ||||
|   { | ||||
|     label: '是否热卖推荐', | ||||
|     field: 'recommendHot' | ||||
|   }, | ||||
|   { | ||||
|     label: '是否优惠推荐', | ||||
|     field: 'recommendBenefit' | ||||
|   }, | ||||
|   { | ||||
|     label: '是否精品推荐', | ||||
|     field: 'recommendBest' | ||||
|   }, | ||||
|   { | ||||
|     label: '是否新品推荐', | ||||
|     field: 'recommendNew' | ||||
|   }, | ||||
|   { | ||||
|     label: '是否优品推荐', | ||||
|     field: 'recommendGood' | ||||
|   }, | ||||
|   { | ||||
|     label: '赠送的优惠劵', | ||||
|     field: 'giveCouponTemplateIds' | ||||
|   }, | ||||
|   { | ||||
|     label: '活动显示排序', | ||||
|     field: 'activityOrders' | ||||
|   } | ||||
| ]) | ||||
|  | @ -171,8 +171,13 @@ | |||
|       </el-table-column> | ||||
|       <el-table-column align="center" fixed="right" label="操作" min-width="200"> | ||||
|         <template #default="{ row }"> | ||||
|           <!-- TODO @puhui999:【详情】,可以后面点做哈 --> | ||||
|           <el-button v-hasPermi="['product:spu:update']" link type="primary" @click="openDetail"> | ||||
|           <!-- TODO @puhui999:【详情】,可以后面点做哈 fix--> | ||||
|           <el-button | ||||
|             v-hasPermi="['product:spu:update']" | ||||
|             link | ||||
|             type="primary" | ||||
|             @click="openDetail(row.id)" | ||||
|           > | ||||
|             详情 | ||||
|           </el-button> | ||||
|           <template v-if="queryParams.tabType === 4"> | ||||
|  | @ -284,12 +289,14 @@ const getTabsCount = async () => { | |||
| const queryParams = ref({ | ||||
|   pageNo: 1, | ||||
|   pageSize: 10, | ||||
|   tabType: 0 | ||||
|   tabType: 0, | ||||
|   name: '', | ||||
|   categoryId: null | ||||
| }) // 查询参数 | ||||
| const queryFormRef = ref() // 搜索的表单Ref | ||||
| 
 | ||||
| const handleTabClick = (tab: TabsPaneContext) => { | ||||
|   queryParams.value.tabType = tab.paneName | ||||
|   queryParams.value.tabType = tab.paneName as number | ||||
|   getList() | ||||
| } | ||||
| 
 | ||||
|  | @ -400,8 +407,8 @@ const openForm = (id?: number) => { | |||
| /** | ||||
|  * 查看商品详情 | ||||
|  */ | ||||
| const openDetail = () => { | ||||
|   message.alert('查看详情未完善!!!') | ||||
| const openDetail = (id?: number) => { | ||||
|   push('/product/productSpuDetail' + id) | ||||
| } | ||||
| 
 | ||||
| /** 导出按钮操作 */ | ||||
|  | @ -436,12 +443,12 @@ const categoryString = (categoryId) => { | |||
|   return treeToString(categoryList.value, categoryId) | ||||
| } | ||||
| /** | ||||
|  * 校验所选是否为二级节点 | ||||
|  * 校验所选是否为二级及以下节点 | ||||
|  */ | ||||
| const nodeClick = () => { | ||||
|   if (!checkSelectedNode(categoryList.value, queryParams.value.categoryId)) { | ||||
|     queryParams.value.categoryId = null | ||||
|     message.warning('必须选择二级节点!!') | ||||
|     message.warning('必须选择二级及以下节点!!') | ||||
|   } | ||||
| } | ||||
| /** 初始化 **/ | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 puhui999
						puhui999