Browse Source

📖 MALL:商品编辑 => 将 detail 还是使用 form 复用,减少维护成本

YunaiV 1 năm trước cách đây
mục cha
commit
2c76d3aeee

+ 2 - 2
src/api/mall/product/spu.ts

@@ -33,14 +33,14 @@ export interface GiveCouponTemplate {
 export interface Spu {
   id?: number
   name?: string // 商品名称
-  categoryId?: number | undefined // 商品分类
+  categoryId?: number // 商品分类
   keyword?: string // 关键字
   unit?: number | undefined // 单位
   picUrl?: string // 商品封面图
   sliderPicUrls?: string[] // 商品轮播图
   introduction?: string // 商品简介
   deliveryTemplateId?: number | undefined // 运费模版
-  brandId?: number | undefined // 商品品牌编号
+  brandId?: number // 商品品牌编号
   specType?: boolean // 商品规格
   subCommissionType?: boolean // 分销类型
   skus?: Sku[] // sku数组

+ 28 - 67
src/views/mall/product/spu/form/BasicInfoForm.vue

@@ -1,13 +1,5 @@
 <template>
-  <!-- 情况一:添加/修改 -->
-  <el-form
-    v-if="!isDetail"
-    ref="productSpuBasicInfoRef"
-    :model="formData"
-    :rules="rules"
-    label-width="120px"
-  >
-    <!-- TODO 芋艿:宽度!! -->
+  <el-form ref="formRef" :model="formData" :rules="rules" label-width="120px" :disabled="isDetail">
     <el-form-item label="商品名称" prop="name">
       <el-input
         v-model="formData.name"
@@ -17,6 +9,7 @@
         maxlength="64"
         :show-word-limit="true"
         :clearable="true"
+        class="w-80!"
       />
     </el-form-item>
     <el-form-item label="商品分类" prop="categoryId">
@@ -24,21 +17,35 @@
         v-model="formData.categoryId"
         :options="categoryList"
         :props="defaultProps"
-        class="w-1/1"
+        class="w-80"
         clearable
         placeholder="请选择商品分类"
         filterable
       />
     </el-form-item>
+    <el-form-item label="商品品牌" prop="brandId">
+      <el-select v-model="formData.brandId" placeholder="请选择商品品牌" class="w-80">
+        <el-option
+          v-for="item in brandList"
+          :key="item.id"
+          :label="item.name"
+          :value="item.id as number"
+        />
+      </el-select>
+    </el-form-item>
     <el-form-item label="商品关键字" prop="keyword">
-      <el-input v-model="formData.keyword" placeholder="请输入商品关键字" />
+      <el-input v-model="formData.keyword" placeholder="请输入商品关键字" class="w-80!" />
     </el-form-item>
     <el-form-item label="商品简介" prop="introduction">
       <el-input
         v-model="formData.introduction"
-        :rows="3"
-        placeholder="请输入商品简介"
+        placeholder="请输入商品名称"
         type="textarea"
+        :autosize="{ minRows: 2, maxRows: 2 }"
+        maxlength="128"
+        :show-word-limit="true"
+        :clearable="true"
+        class="w-80!"
       />
     </el-form-item>
     <el-form-item label="商品封面图" prop="picUrl">
@@ -47,67 +54,20 @@
     <el-form-item label="商品轮播图" prop="sliderPicUrls">
       <UploadImgs v-model:modelValue="formData.sliderPicUrls" />
     </el-form-item>
-    <el-form-item label="品牌" prop="brandId">
-      <el-select v-model="formData.brandId" placeholder="请选择">
-        <el-option v-for="item in brandList" :key="item.id" :label="item.name" :value="item.id" />
-      </el-select>
-    </el-form-item>
   </el-form>
-
-  <!-- 情况二:详情 -->
-  <Descriptions v-if="isDetail" :data="formData" :schema="allSchemas.detailSchema">
-    <template #categoryId="{ row }"> {{ formatCategoryName(row.categoryId) }}</template>
-    <template #brandId="{ row }">
-      {{ brandList.find((item) => item.id === row.brandId)?.name }}
-    </template>
-    <template #picUrl="{ row }">
-      <el-image :src="row.picUrl" class="h-60px w-60px" @click="imagePreview(row.picUrl)" />
-    </template>
-    <template #sliderPicUrls="{ row }">
-      <el-image
-        v-for="(item, index) in row.sliderPicUrls"
-        :key="index"
-        :src="item.url"
-        class="mr-10px h-60px w-60px"
-        @click="imagePreview(row.sliderPicUrls)"
-      />
-    </template>
-  </Descriptions>
 </template>
 <script lang="ts" setup>
 import { PropType } from 'vue'
-import { isArray } from '@/utils/is'
 import { copyValueToTarget } from '@/utils'
 import { propTypes } from '@/utils/propTypes'
 import { defaultProps, handleTree, treeToString } from '@/utils/tree'
-import { createImageViewer } from '@/components/ImageViewer'
-import { basicInfoSchema } from './spu.data'
 import type { Spu } from '@/api/mall/product/spu'
 import * as ProductCategoryApi from '@/api/mall/product/category'
 import * as ProductBrandApi from '@/api/mall/product/brand'
-import * as ExpressTemplateApi from '@/api/mall/trade/delivery/expressTemplate'
+import { BrandVO } from '@/api/mall/product/brand'
 
 defineOptions({ name: 'ProductSpuBasicInfoForm' })
 
-// ====== 商品详情相关操作 ======
-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() // 消息弹窗
 
 const props = defineProps({
@@ -118,15 +78,16 @@ const props = defineProps({
   activeName: propTypes.string.def(''),
   isDetail: propTypes.bool.def(false) // 是否作为详情组件
 })
-const productSpuBasicInfoRef = ref() // 表单 Ref
+
+const formRef = ref() // 表单 Ref
 const formData = reactive<Spu>({
   name: '', // 商品名称
-  categoryId: null, // 商品分类
+  categoryId: undefined, // 商品分类
   keyword: '', // 关键字
   picUrl: '', // 商品封面图
   sliderPicUrls: [], // 商品轮播图
   introduction: '', // 商品简介
-  brandId: null // 商品品牌
+  brandId: undefined // 商品品牌
 })
 const rules = reactive({
   name: [required],
@@ -163,8 +124,8 @@ watch(
 const emit = defineEmits(['update:activeName'])
 const validate = async () => {
   // 校验表单
-  if (!productSpuBasicInfoRef) return
-  return await unref(productSpuBasicInfoRef).validate((valid) => {
+  if (!formRef) return
+  return await unref(formRef).validate((valid) => {
     if (!valid) {
       message.warning('商品信息未完善!!')
       emit('update:activeName', 'basicInfo')
@@ -184,7 +145,7 @@ const formatCategoryName = (categoryId: number) => {
   return treeToString(categoryList.value, categoryId)
 }
 
-const brandList = ref([]) // 精简商品品牌列表
+const brandList = ref<BrandVO[]>([]) // 精简商品品牌列表
 onMounted(async () => {
   // 获得分类树
   const data = await ProductCategoryApi.getCategoryList({})

+ 2 - 16
src/views/mall/product/spu/form/DeliveryForm.vue

@@ -1,11 +1,10 @@
 <template>
-  <!-- 情况一:添加/修改 -->
   <el-form
-    v-if="!isDetail"
     ref="productSpuBasicInfoRef"
     :model="formData"
     :rules="rules"
     label-width="120px"
+    :disabled="isDetail"
   >
     <!-- TODO 芋艿:宽度!! -->
     <!-- TODO 芋艿:这里要挪出去 -->
@@ -20,29 +19,16 @@
       </el-select>
     </el-form-item>
   </el-form>
-
-  <!-- 情况二:详情 -->
-  <Descriptions v-if="isDetail" :data="formData" :schema="allSchemas.detailSchema">
-    <template #deliveryTemplateId="{ row }">
-      {{ deliveryTemplateList.find((item) => item.id === row.deliveryTemplateId)?.name }}
-    </template>
-  </Descriptions>
 </template>
 <script lang="ts" setup>
 import { PropType } from 'vue'
 import { copyValueToTarget } from '@/utils'
 import { propTypes } from '@/utils/propTypes'
-import { basicInfoSchema } from './spu.data'
 import type { Spu } from '@/api/mall/product/spu'
 import * as ExpressTemplateApi from '@/api/mall/trade/delivery/expressTemplate'
 
 defineOptions({ name: 'ProductSpuBasicInfoForm' })
 
-// ====== 商品详情相关操作 ======
-const { allSchemas } = useCrudSchemas(basicInfoSchema)
-
-// ====== end ======
-
 const message = useMessage() // 消息弹窗
 
 const props = defineProps({
@@ -55,7 +41,7 @@ const props = defineProps({
 })
 const productSpuBasicInfoRef = ref() // 表单 Ref
 const formData = reactive<Spu>({
-  deliveryTemplateId: null // 运费模版
+  deliveryTemplateId: undefined // 运费模版
 })
 const rules = reactive({
   deliveryTemplateId: [required]

+ 1 - 17
src/views/mall/product/spu/form/DescriptionForm.vue

@@ -1,30 +1,16 @@
 <template>
-  <!-- 情况一:添加/修改 -->
   <el-form
-    v-if="!isDetail"
     ref="descriptionFormRef"
     :model="formData"
     :rules="rules"
     label-width="120px"
+    :disabled="isDetail"
   >
     <!--富文本编辑器组件-->
     <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 v-dompurify-html="row.description" style="width: 600px"></div>
-    </template>
-  </Descriptions>
 </template>
 <script lang="ts" setup>
 import type { Spu } from '@/api/mall/product/spu'
@@ -32,13 +18,11 @@ import { Editor } from '@/components/Editor'
 import { PropType } from 'vue'
 import { propTypes } from '@/utils/propTypes'
 import { copyValueToTarget } from '@/utils'
-import { descriptionSchema } from './spu.data'
 
 defineOptions({ name: 'DescriptionForm' })
 
 const message = useMessage() // 消息弹窗
 
-const { allSchemas } = useCrudSchemas(descriptionSchema)
 const props = defineProps({
   propFormData: {
     type: Object as PropType<Spu>,

+ 1 - 8
src/views/mall/product/spu/form/OtherSettingsForm.vue

@@ -1,11 +1,10 @@
 <template>
-  <!-- 情况一:添加/修改 -->
   <el-form
-    v-if="!isDetail"
     ref="otherSettingsFormRef"
     :model="formData"
     :rules="rules"
     label-width="120px"
+    :disabled="isDetail"
   >
     <el-form-item label="商品排序" prop="sort">
       <el-input-number v-model="formData.sort" :min="0" />
@@ -17,23 +16,17 @@
       <el-input-number v-model="formData.virtualSalesCount" :min="0" placeholder="请输入虚拟销量" />
     </el-form-item>
   </el-form>
-
-  <!-- 情况二:详情 -->
-  <Descriptions v-if="isDetail" :data="formData" :schema="allSchemas.detailSchema" />
 </template>
 <script lang="ts" 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'
 
 defineOptions({ name: 'OtherSettingsForm' })
 
 const message = useMessage() // 消息弹窗
 
-const { allSchemas } = useCrudSchemas(otherSettingsSchema)
-
 const props = defineProps({
   propFormData: {
     type: Object as PropType<Spu>,

+ 1 - 50
src/views/mall/product/spu/form/SkuForm.vue

@@ -1,11 +1,11 @@
 <template>
   <!-- 情况一:添加/修改 -->
   <el-form
-    v-if="!isDetail"
     ref="productSpuSkuRef"
     :model="formData"
     :rules="rules"
     label-width="120px"
+    :disabled="isDetail"
   >
     <el-row>
       <el-col :span="12">
@@ -55,46 +55,16 @@
     </el-row>
   </el-form>
 
-  <!-- 情况二:详情 -->
-  <Descriptions v-if="isDetail" :data="formData" :schema="allSchemas.detailSchema">
-    <template #specType="{ row }">
-      {{ row.specType ? '多规格' : '单规格' }}
-    </template>
-    <template #subCommissionType="{ row }">
-      {{ row.subCommissionType ? '单独设置' : '默认设置' }}
-    </template>
-    <template #sliderPicUrls="{ row }">
-      <el-image
-        v-for="(item, index) in row.sliderPicUrls"
-        :key="index"
-        :src="item.url"
-        class="mr-10px h-60px w-60px"
-        @click="imagePreview(row.sliderPicUrls)"
-      />
-    </template>
-    <template #skus>
-      <SkuList
-        ref="skuDetailListRef"
-        :is-detail="isDetail"
-        :prop-form-data="formData"
-        :propertyList="propertyList"
-      />
-    </template>
-  </Descriptions>
-
   <!-- 商品属性添加 Form 表单 -->
   <ProductPropertyAddForm ref="attributesAddFormRef" :propertyList="propertyList" />
 </template>
 <script lang="ts" setup>
 import { PropType } from 'vue'
-import { isArray } from '@/utils/is'
 import { copyValueToTarget } from '@/utils'
 import { propTypes } from '@/utils/propTypes'
-import { createImageViewer } from '@/components/ImageViewer'
 import { getPropertyList, RuleConfig, SkuList } from '@/views/mall/product/spu/components/index.ts'
 import ProductAttributes from './ProductAttributes.vue'
 import ProductPropertyAddForm from './ProductPropertyAddForm.vue'
-import { basicInfoSchema } from './spu.data'
 import type { Spu } from '@/api/mall/product/spu'
 
 defineOptions({ name: 'ProductSpuSkuForm' })
@@ -123,25 +93,6 @@ const ruleConfig: RuleConfig[] = [
   }
 ]
 
-// ====== 商品详情相关操作 ======
-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() // 消息弹窗
 
 const props = defineProps({

+ 1 - 2
src/views/mall/product/spu/form/index.vue

@@ -33,8 +33,7 @@
           :propFormData="formData"
         />
       </el-tab-pane>
-      <!-- TODO 芋艿:物流设置 -->
-      <el-tab-pane label="其他设置" name="otherSettings">
+      <el-tab-pane label="其它设置" name="otherSettings">
         <OtherSettingsForm
           ref="otherSettingsRef"
           v-model:activeName="activeName"

+ 0 - 72
src/views/mall/product/spu/form/spu.data.ts

@@ -1,72 +0,0 @@
-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: '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'
-  }
-])