Browse Source

fix: 优化相关逻辑,修复遗留bug

puhui999 1 year ago
parent
commit
38545e89c6

+ 1 - 21
src/views/mall/product/spu/addForm.vue

@@ -37,7 +37,6 @@ import { useTagsViewStore } from '@/store/modules/tagsView'
 import { BasicInfoForm, DescriptionForm, OtherSettingsForm } from './components'
 // 业务api
 import * as ProductSpuApi from '@/api/mall/product/spu'
-import * as PropertyApi from '@/api/mall/product/property'
 import { convertToInteger, formatToFraction } from '@/utils'
 
 const { t } = useI18n() // 国际化
@@ -105,25 +104,6 @@ const getDetail = async () => {
         item.subCommissionSecondPrice = formatToFraction(item.subCommissionSecondPrice)
       })
       formData.value = res
-      // 只有是多规格才处理
-      if (res.specType) {
-        // TODO @puhui999:可以直接拿 propertyName 拼接处规格 id + 属性,可以看下商品 uniapp 详情的做法
-        // fix: 考虑到 sku 数量和通过属性算出来的sku不一致的情况
-        const propertyIds = []
-        res.skus.forEach((sku) =>
-          sku.properties
-            ?.map((property) => property.propertyId)
-            .forEach((propertyId) => {
-              if (propertyIds.indexOf(propertyId) === -1) {
-                propertyIds.push(propertyId)
-              }
-            })
-        )
-        const properties = await PropertyApi.getPropertyListAndValue({ propertyIds })
-        await nextTick()
-        // 回显商品属性
-        basicInfoRef.value.addAttribute(properties)
-      }
     } finally {
       formLoading.value = false
     }
@@ -141,7 +121,7 @@ const submitForm = async () => {
     await unref(descriptionRef)?.validate()
     await unref(otherSettingsRef)?.validate()
     const deepCopyFormData = cloneDeep(unref(formData.value)) // 深拷贝一份 fix:这样最终 server 端不满足,不需要恢复,
-    // TODO 兜底处理 sku 空数据详见 SkuList TODO
+    // TODO 兜底处理 sku 空数据
     formData.value.skus.forEach((sku) => {
       // 因为是空数据这里判断一下商品条码是否为空就行
       if (sku.barCode === '') {

+ 28 - 12
src/views/mall/product/spu/components/BasicInfoForm.vue

@@ -98,14 +98,14 @@
         <el-form-item v-if="formData.specType" label="商品属性">
           <!-- TODO @puhui999:参考 https://admin.java.crmeb.net/store/list/creatProduct 添加规格好做么?添加的时候,不用输入备注哈 fix-->
           <el-button class="mr-15px mb-10px" @click="attributesAddFormRef.open">添加规格</el-button>
-          <ProductAttributes :propertyList="propertyList" />
+          <ProductAttributes :propertyList="propertyList" @success="generateSkus" />
         </el-form-item>
         <template v-if="formData.specType && propertyList.length > 0">
           <el-form-item label="批量设置">
             <SkuList :is-batch="true" :prop-form-data="formData" :propertyList="propertyList" />
           </el-form-item>
           <el-form-item label="属性列表">
-            <SkuList :prop-form-data="formData" :propertyList="propertyList" />
+            <SkuList ref="skuListRef" :prop-form-data="formData" :propertyList="propertyList" />
           </el-form-item>
         </template>
         <el-form-item v-if="!formData.specType">
@@ -114,7 +114,7 @@
       </el-col>
     </el-row>
   </el-form>
-  <ProductAttributesAddForm ref="attributesAddFormRef" @success="addAttribute" />
+  <ProductAttributesAddForm ref="attributesAddFormRef" :propertyList="propertyList" />
 </template>
 <script lang="ts" name="ProductSpuBasicInfoForm" setup>
 import { PropType } from 'vue'
@@ -141,15 +141,11 @@ const attributesAddFormRef = ref() // 添加商品属性表单 TODO @puhui999:
 const productSpuBasicInfoRef = ref() // 表单Ref TODO @puhui999:小写开头哈  fix
 // TODO @puhui999:attributeList 改成 propertyList,会更统一一点 fix
 const propertyList = ref([]) // 商品属性列表
-/** 添加商品属性 */
-// TODO @puhui999:propFormData 算出来 fix: 因为ProductAttributesAddForm添加属性成功回调得使用不能完全依赖于propFormData
-const addAttribute = (property: any) => {
-  Array.isArray(property) ? (propertyList.value = property) : propertyList.value.push(property)
-}
+const skuListRef = ref() // 商品属性列表Ref
 /** 调用 SkuList generateTableData 方法*/
-// const generateSkus(propertyList){
-//   skuList.value.generateTableData()
-// }
+const generateSkus = (propertyList) => {
+  skuListRef.value.generateTableData(propertyList)
+}
 const formData = reactive<SpuType>({
   name: '', // 商品名称
   categoryId: null, // 商品分类
@@ -191,6 +187,26 @@ watch(
     formData.sliderPicUrls = data['sliderPicUrls'].map((item) => ({
       url: item
     }))
+    // 只有是多规格才处理
+    if (formData.specType) {
+      // TODO @puhui999:可以直接拿 propertyName 拼接处规格 id + 属性,可以看下商品 uniapp 详情的做法
+      // fix: 直接拿返回的 skus 属性逆向生成出 propertyList
+      const properties = []
+      formData.skus.forEach((sku) => {
+        sku.properties.forEach(({ propertyId, propertyName, valueId, valueName }) => {
+          // 添加属性
+          if (!properties.some((item) => item.id === propertyId)) {
+            properties.push({ id: propertyId, name: propertyName, values: [] })
+          }
+          // 添加属性值
+          const index = properties.findIndex((item) => item.id === propertyId)
+          if (!properties[index].values.some((value) => value.id === valueId)) {
+            properties[index].values.push({ id: valueId, name: valueName })
+          }
+        })
+      })
+      propertyList.value = properties
+    }
   },
   {
     // fix: 去掉深度监听只有对象引用发生改变的时候才执行,解决改一动多的问题
@@ -217,7 +233,7 @@ const validate = async () => {
     }
   })
 }
-defineExpose({ validate, addAttribute })
+defineExpose({ validate })
 
 /** 分销类型 */
 const changeSubCommissionType = () => {

+ 25 - 11
src/views/mall/product/spu/components/ProductAttributes.vue

@@ -2,23 +2,25 @@
   <el-col v-for="(item, index) in attributeList" :key="index">
     <div>
       <el-text class="mx-1">属性名:</el-text>
-      <el-text class="mx-1">{{ item.name }}</el-text>
+      <el-tag class="mx-1" closable type="success" @close="handleCloseProperty(index)"
+        >{{ item.name }}
+      </el-tag>
     </div>
     <div>
       <el-text class="mx-1">属性值:</el-text>
       <el-tag
         v-for="(value, valueIndex) in item.values"
         :key="value.id"
-        :disable-transitions="false"
         class="mx-1"
         closable
-        @close="handleClose(index, valueIndex)"
+        @close="handleCloseValue(index, valueIndex)"
       >
         {{ value.name }}
       </el-tag>
       <el-input
         v-show="inputVisible(index)"
-        ref="InputRef"
+        :id="`input${index}`"
+        :ref="setInputRef"
         v-model="inputValue"
         class="!w-20"
         size="small"
@@ -51,7 +53,15 @@ const inputVisible = computed(() => (index) => {
   if (attributeIndex.value === null) return false
   if (attributeIndex.value === index) return true
 })
-const InputRef = ref() //标签输入框Ref
+const inputRef = ref([]) //标签输入框Ref
+/** 解决 ref 在 v-for 中的获取问题*/
+const setInputRef = (el) => {
+  if (el === null || typeof el === 'undefined') return
+  // 如果不存在id相同的元素才添加
+  if (!inputRef.value.some((item) => item.input?.attributes.id === el.input?.attributes.id)) {
+    inputRef.value.push(el)
+  }
+}
 const attributeList = ref([]) // 商品属性列表
 const props = defineProps({
   propertyList: {
@@ -72,19 +82,22 @@ watch(
   }
 )
 
-/** 删除标签 tagValue 标签值*/
-const handleClose = (index, valueIndex) => {
+/** 删除属性值*/
+const handleCloseValue = (index, valueIndex) => {
   attributeList.value[index].values?.splice(valueIndex, 1)
 }
-
+/** 删除属性*/
+const handleCloseProperty = (index) => {
+  attributeList.value?.splice(index, 1)
+}
 /** 显示输入框并获取焦点 */
 const showInput = async (index) => {
   attributeIndex.value = index
-  // TODO 嗯!!!自动获取焦点还是有点问题,后续继续改进
-  // 因为组件在ref中所以需要用索引获取对应的Ref
-  InputRef.value[index]!.input!.focus()
+  inputRef.value[index].focus()
 }
 
+const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
+
 /** 输入框失去焦点或点击回车时触发 */
 const handleInputConfirm = async (index, propertyId) => {
   if (inputValue.value) {
@@ -93,6 +106,7 @@ const handleInputConfirm = async (index, propertyId) => {
       const id = await PropertyApi.createPropertyValue({ propertyId, name: inputValue.value })
       attributeList.value[index].values.push({ id, name: inputValue.value })
       message.success(t('common.createSuccess'))
+      emit('success', attributeList.value)
     } catch {
       message.error('添加失败,请重试') // TODO 缺少国际化
     }

+ 20 - 3
src/views/mall/product/spu/components/ProductAttributesAddForm.vue

@@ -33,7 +33,25 @@ const formRules = reactive({
   name: [{ required: true, message: '名称不能为空', trigger: 'blur' }]
 })
 const formRef = ref() // 表单 Ref
+const attributeList = ref([]) // 商品属性列表
+const props = defineProps({
+  propertyList: {
+    type: Array,
+    default: () => {}
+  }
+})
 
+watch(
+  () => props.propertyList,
+  (data) => {
+    if (!data) return
+    attributeList.value = data
+  },
+  {
+    deep: true,
+    immediate: true
+  }
+)
 /** 打开弹窗 */
 const open = async () => {
   dialogVisible.value = true
@@ -42,7 +60,6 @@ const open = async () => {
 defineExpose({ open }) // 提供 open 方法,用于打开弹窗
 
 /** 提交表单 */
-const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
 const submitForm = async () => {
   // 校验表单
   if (!formRef) return
@@ -56,12 +73,12 @@ const submitForm = async () => {
     const res = await PropertyApi.getPropertyListAndValue({ name: data.name })
     if (res.length === 0) {
       const propertyId = await PropertyApi.createProperty(data)
-      emit('success', { id: propertyId, ...formData.value, values: [] })
+      attributeList.value.push({ id: propertyId, ...formData.value, values: [] })
     } else {
       if (res[0].values === null) {
         res[0].values = []
       }
-      emit('success', res[0]) // 因为只用一个
+      attributeList.value.push(res[0]) // 因为只用一个
     }
     message.success(t('common.createSuccess'))
     dialogVisible.value = false

+ 2 - 4
src/views/mall/product/spu/components/SkuList.vue

@@ -212,10 +212,6 @@ const generateTableData = (propertyList: any[]) => {
     if (index !== -1) {
       continue
     }
-    /**
-     * TODO 嗯。。有一个问题回显数据时已删除的 sku 会被重新添加暂时没想到好办法,保存时先手动重新删除一下因为是一条空数据很好辨别 不手动删也没是提交表单时会检测删除空sku来兜底
-     *
-     */
     formData.value.skus.push(row)
   }
 }
@@ -302,4 +298,6 @@ watch(
     immediate: true
   }
 )
+// 暴露出生成 sku 方法给添加属性成功时调用 fix: 为了在只有一个属性下 spu 回显 skus 属性和和商品属性个数一致的情况下 添加属性值时添加 sku
+defineExpose({ generateTableData })
 </script>