Browse Source

虚拟商品文件上传

Xiao_123 4 months ago
parent
commit
41fde1046e

+ 4 - 0
src/api/mall/product/spu.ts

@@ -17,6 +17,9 @@ export interface Sku {
   costPrice?: number | string // 成本价
   barCode?: string // 商品条码
   picUrl?: string // 图片地址
+  extend?: {
+    fileUrls: [] // 文件地址
+  },
   stock?: number // 库存
   weight?: number // 商品重量,单位:kg 千克
   volume?: number // 商品体积,单位:m^3 平米
@@ -45,6 +48,7 @@ export interface Spu {
   specType?: boolean // 商品规格
   subCommissionType?: boolean // 分销类型
   skus?: Sku[] // sku数组
+  type: number | string // 商品类型
   description?: string // 商品详情
   sort?: number // 商品排序
   giveIntegral?: number // 赠送积分

+ 2 - 1
src/components/UploadFile/src/UploadFile.vue

@@ -46,7 +46,7 @@ import { UploadFile } from 'element-plus/es/components/upload/src/upload'
 defineOptions({ name: 'UploadFile' })
 
 const message = useMessage() // 消息弹窗
-const emit = defineEmits(['update:modelValue'])
+const emit = defineEmits(['update:modelValue', 'clear'])
 
 const props = defineProps({
   modelValue: propTypes.oneOfType<string | string[]>([String, Array<String>]).isRequired,
@@ -125,6 +125,7 @@ const excelUploadError: UploadProps['onError'] = (): void => {
 }
 // 删除上传文件
 const handleRemove = (file: UploadFile) => {
+  emit('clear', file.url)
   const index = fileList.value.map((f) => f.name).indexOf(file.name)
   if (index > -1) {
     fileList.value.splice(index, 1)

+ 1 - 0
src/utils/dict.ts

@@ -165,6 +165,7 @@ export enum DICT_TYPE {
 
   // ========== MALL - 商品模块 ==========
   PRODUCT_SPU_STATUS = 'product_spu_status', //商品状态
+  PRODUCT_SPU_TYPE = 'product_spu_type', //商品类型
 
   // ========== MALL - 交易模块 ==========
   EXPRESS_CHARGE_MODE = 'trade_delivery_express_charge_mode', //快递的计费方式

+ 54 - 0
src/views/mall/product/spu/components/SkuList.vue

@@ -13,6 +13,12 @@
         <UploadImg v-model="row.picUrl" height="50px" width="50px" />
       </template>
     </el-table-column>
+    <el-table-column v-if="productType !== '0'" align="center" label="文件" min-width="90">
+      <template #default="{ row }">
+        <el-button v-if="row?.extend?.fileUrls?.length" type="primary" link @click.stop="handleUpload(row)">已上传</el-button>
+        <el-button v-else size="small" plain @click="handleUpload(row)">上传文件</el-button>
+      </template>
+    </el-table-column>
     <template v-if="formData!.specType && !isBatch">
       <!--  根据商品属性动态添加 -->
       <el-table-column
@@ -157,6 +163,12 @@
         />
       </template>
     </el-table-column>
+    <el-table-column v-if="productType !== '0'" align="center" label="文件" min-width="90">
+      <template #default="{ row }">
+        <el-button v-if="row?.extend?.fileUrls?.length" type="primary" link @click.stop="handleUpload(row)">已上传</el-button>
+        <div v-else>未上传</div>
+      </template>
+    </el-table-column>
     <template v-if="formData!.specType && !isBatch">
       <!--  根据商品属性动态添加 -->
       <el-table-column
@@ -281,7 +293,23 @@
     <!--  方便扩展每个活动配置的属性不一样  -->
     <slot name="extension"></slot>
   </el-table>
+
+  <Dialog title="文件上传" v-model="dialogVisible">
+    <UploadFile
+      v-model="fileUrl"
+      :file-type="['pdf', 'doc', 'docx', 'ppt']"
+      :limit="1"
+      :drag="true"
+      class="min-w-80px"
+      @clear="fileUrl = ''"
+    />
+    <template #footer>
+      <el-button @click="handleUploadSubmit" type="primary">确 定</el-button>
+      <el-button @click="dialogVisible = false, fileUrl = ''">取 消</el-button>
+    </template>
+  </Dialog>
 </template>
+
 <script lang="ts" setup>
 import { PropType, Ref } from 'vue'
 import { copyValueToTarget, formatToFraction } from '@/utils'
@@ -310,6 +338,7 @@ const props = defineProps({
     type: Array as PropType<RuleConfig[]>,
     default: () => []
   },
+  productType: propTypes.string.def(''), // 商品类型
   isBatch: propTypes.bool.def(false), // 是否作为批量操作组件
   isDetail: propTypes.bool.def(false), // 是否作为 sku 详情组件
   isComponent: propTypes.bool.def(false), // 是否作为 sku 选择组件
@@ -324,6 +353,9 @@ const skuList = ref<Sku[]>([
     barCode: '', // 商品条码
     picUrl: '', // 图片地址
     stock: 0, // 库存
+    extend: {
+      fileUrls: [] // 文件地址
+    },
     weight: 0, // 商品重量
     volume: 0, // 商品体积
     firstBrokeragePrice: 0, // 一级分销的佣金
@@ -339,6 +371,22 @@ const imagePreview = (imgUrl: string) => {
   })
 }
 
+// 文件上传
+const dialogVisible = ref(false)
+const fileUrl = ref('')
+const itemData = ref({})
+const handleUpload = (row) => {
+  itemData.value = row
+  if (row.extend.fileUrls?.length) fileUrl.value = row.extend.fileUrls[0]
+  dialogVisible.value = true
+}
+const handleUploadSubmit = () => {
+  if (!fileUrl.value) return message.warning('请上传文件')
+  itemData.value.extend.fileUrls = [fileUrl.value]
+  dialogVisible.value = false
+  fileUrl.value = ''
+}
+
 /** 批量添加 */
 const batchAdd = () => {
   validateProperty()
@@ -457,6 +505,9 @@ const generateTableData = (propertyList: any[]) => {
       picUrl: '',
       stock: 0,
       weight: 0,
+      extend: {
+        fileUrls: [] // 文件地址
+      },
       volume: 0,
       firstBrokeragePrice: 0,
       secondBrokeragePrice: 0
@@ -530,6 +581,9 @@ watch(
           costPrice: 0,
           barCode: '',
           picUrl: '',
+          extend: {
+            fileUrls: [] // 文件地址
+          },
           stock: 0,
           weight: 0,
           volume: 0,

+ 13 - 2
src/views/mall/product/spu/form/InfoForm.vue

@@ -1,6 +1,11 @@
 <!-- 商品发布 - 基础设置 -->
 <template>
   <el-form ref="formRef" :disabled="isDetail" :model="formData" :rules="rules" label-width="120px">
+    <el-form-item label="商品类型" prop="type">
+      <el-radio-group v-model="formData.type" @change="val => emit('changeProductType', val)">
+        <el-radio v-for="dict in getIntDictOptions(DICT_TYPE.PRODUCT_SPU_TYPE)" :key="dict.value" :value="dict.value.toString()">{{ dict.label }}</el-radio>
+      </el-radio-group>
+    </el-form-item>
     <el-form-item label="商品名称" prop="name">
       <el-input
         v-model="formData.name"
@@ -57,6 +62,7 @@
     </el-form-item>
   </el-form>
 </template>
+
 <script lang="ts" setup>
 import { PropType } from 'vue'
 import { copyValueToTarget } from '@/utils'
@@ -67,8 +73,10 @@ import * as ProductCategoryApi from '@/api/mall/product/category'
 import { CategoryVO } from '@/api/mall/product/category'
 import * as ProductBrandApi from '@/api/mall/product/brand'
 import { BrandVO } from '@/api/mall/product/brand'
+import { getIntDictOptions, DICT_TYPE } from '@/utils/dict'
 
 defineOptions({ name: 'ProductSpuInfoForm' })
+
 const props = defineProps({
   propFormData: {
     type: Object as PropType<Spu>,
@@ -87,6 +95,7 @@ const formData = reactive<Spu>({
   picUrl: '', // 商品封面图
   sliderPicUrls: [], // 商品轮播图
   introduction: '', // 商品简介
+  type: '', // 商品类型
   brandId: undefined // 商品品牌
 })
 const rules = reactive({
@@ -96,9 +105,11 @@ const rules = reactive({
   introduction: [required],
   picUrl: [required],
   sliderPicUrls: [required],
-  brandId: [required]
+  brandId: [required],
+  type: [required]
 })
 
+const emit = defineEmits(['update:activeName', 'changeProductType'])
 /** 将传进来的值赋值给 formData */
 watch(
   () => props.propFormData,
@@ -107,6 +118,7 @@ watch(
       return
     }
     copyValueToTarget(formData, data)
+    if (data.type) emit('changeProductType', data.type)
   },
   {
     immediate: true
@@ -114,7 +126,6 @@ watch(
 )
 
 /** 表单校验 */
-const emit = defineEmits(['update:activeName'])
 const validate = async () => {
   if (!formRef) return
   try {

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

@@ -29,6 +29,7 @@
       <SkuList
         ref="skuListRef"
         :prop-form-data="formData"
+        :productType="productType"
         :property-list="propertyList"
         :rule-config="ruleConfig"
       />
@@ -43,7 +44,7 @@
     </el-form-item>
     <template v-if="formData.specType && propertyList.length > 0">
       <el-form-item v-if="!isDetail" label="批量设置">
-        <SkuList :is-batch="true" :prop-form-data="formData" :property-list="propertyList" />
+        <SkuList :is-batch="true" :productType="productType" :prop-form-data="formData" :property-list="propertyList" />
       </el-form-item>
       <el-form-item label="规格列表">
         <SkuList
@@ -51,6 +52,7 @@
           :is-detail="isDetail"
           :prop-form-data="formData"
           :property-list="propertyList"
+          :productType="productType"
           :rule-config="ruleConfig"
         />
       </el-form-item>
@@ -107,6 +109,7 @@ const props = defineProps({
     type: Object as PropType<Spu>,
     default: () => {}
   },
+  productType: propTypes.string.def(''), // 商品类型
   isDetail: propTypes.bool.def(false) // 是否作为详情组件
 })
 const attributesAddFormRef = ref() // 添加商品属性表单
@@ -180,6 +183,9 @@ const onChangeSpec = () => {
       picUrl: '',
       stock: 0,
       weight: 0,
+      extend: {
+        fileUrls: []
+      },
       volume: 0,
       firstBrokeragePrice: 0,
       secondBrokeragePrice: 0

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

@@ -7,6 +7,7 @@
           v-model:activeName="activeName"
           :is-detail="isDetail"
           :propFormData="formData"
+          @change-product-type="handleChangeProductType"
         />
       </el-tab-pane>
       <el-tab-pane label="价格库存" name="sku">
@@ -14,10 +15,11 @@
           ref="skuRef"
           v-model:activeName="activeName"
           :is-detail="isDetail"
+          :productType="productType"
           :propFormData="formData"
         />
       </el-tab-pane>
-      <el-tab-pane label="物流设置" name="delivery">
+      <el-tab-pane v-if="productType === '0'" label="物流设置" name="delivery">
         <DeliveryForm
           ref="deliveryRef"
           v-model:activeName="activeName"
@@ -100,6 +102,9 @@ const formData = ref<ProductSpuApi.Spu>({
       barCode: '', // 商品条码
       picUrl: '', // 图片地址
       stock: 0, // 库存
+      extend: {
+        fileUrls: [] // 文件
+      },
       weight: 0, // 商品重量
       volume: 0, // 商品体积
       firstBrokeragePrice: 0, // 一级分销的佣金
@@ -112,6 +117,11 @@ const formData = ref<ProductSpuApi.Spu>({
   virtualSalesCount: 0 // 虚拟销量
 })
 
+const productType = ref('') // 是否显示物流设置
+const handleChangeProductType = (val: string) => {
+  productType.value = val
+}
+
 /** 获得详情 */
 const getDetail = async () => {
   if ('ProductSpuDetail' === name) {
@@ -153,7 +163,7 @@ const submitForm = async () => {
     // 校验各表单
     await unref(infoRef)?.validate()
     await unref(skuRef)?.validate()
-    await unref(deliveryRef)?.validate()
+    if (productType.value === '0') await unref(deliveryRef)?.validate()
     await unref(descriptionRef)?.validate()
     await unref(otherRef)?.validate()
     // 深拷贝一份, 这样最终 server 端不满足,不需要影响原始数据