addForm.vue 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. <template>
  2. <ContentWrap v-loading="formLoading">
  3. <el-tabs v-model="activeName">
  4. <el-tab-pane label="商品信息" name="basicInfo">
  5. <BasicInfoForm
  6. ref="basicInfoRef"
  7. v-model:activeName="activeName"
  8. :propFormData="formData"
  9. />
  10. </el-tab-pane>
  11. <el-tab-pane label="商品详情" name="description">
  12. <DescriptionForm
  13. ref="descriptionRef"
  14. v-model:activeName="activeName"
  15. :propFormData="formData"
  16. />
  17. </el-tab-pane>
  18. <el-tab-pane label="其他设置" name="otherSettings">
  19. <OtherSettingsForm
  20. ref="otherSettingsRef"
  21. v-model:activeName="activeName"
  22. :propFormData="formData"
  23. />
  24. </el-tab-pane>
  25. </el-tabs>
  26. <el-form>
  27. <el-form-item style="float: right">
  28. <el-button :loading="formLoading" type="primary" @click="submitForm">保存</el-button>
  29. <el-button @click="close">返回</el-button>
  30. </el-form-item>
  31. </el-form>
  32. </ContentWrap>
  33. </template>
  34. <script lang="ts" name="ProductSpuForm" setup>
  35. import { cloneDeep } from 'lodash-es'
  36. import { useTagsViewStore } from '@/store/modules/tagsView'
  37. import { BasicInfoForm, DescriptionForm, OtherSettingsForm } from './components'
  38. // 业务api
  39. import * as ProductSpuApi from '@/api/mall/product/spu'
  40. import { convertToInteger, formatToFraction } from '@/utils'
  41. const { t } = useI18n() // 国际化
  42. const message = useMessage() // 消息弹窗
  43. const { push, currentRoute } = useRouter() // 路由
  44. const { params } = useRoute() // 查询参数
  45. const { delView } = useTagsViewStore() // 视图操作
  46. const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
  47. const activeName = ref('basicInfo') // Tag 激活的窗口
  48. const basicInfoRef = ref<ComponentRef<typeof BasicInfoForm>>() // 商品信息Ref
  49. const descriptionRef = ref<ComponentRef<typeof DescriptionForm>>() // 商品详情Ref
  50. const otherSettingsRef = ref<ComponentRef<typeof OtherSettingsForm>>() // 其他设置Ref
  51. // spu 表单数据
  52. const formData = ref<ProductSpuApi.SpuType>({
  53. name: '', // 商品名称
  54. categoryId: null, // 商品分类
  55. keyword: '', // 关键字
  56. unit: null, // 单位
  57. picUrl: '', // 商品封面图
  58. sliderPicUrls: [], // 商品轮播图
  59. introduction: '', // 商品简介
  60. deliveryTemplateId: 1, // 运费模版
  61. brandId: null, // 商品品牌
  62. specType: false, // 商品规格
  63. subCommissionType: false, // 分销类型
  64. skus: [
  65. {
  66. price: 0, // 商品价格
  67. marketPrice: 0, // 市场价
  68. costPrice: 0, // 成本价
  69. barCode: '', // 商品条码
  70. picUrl: '', // 图片地址
  71. stock: 0, // 库存
  72. weight: 0, // 商品重量
  73. volume: 0, // 商品体积
  74. subCommissionFirstPrice: 0, // 一级分销的佣金
  75. subCommissionSecondPrice: 0 // 二级分销的佣金
  76. }
  77. ],
  78. description: '', // 商品详情
  79. sort: 0, // 商品排序
  80. giveIntegral: 0, // 赠送积分
  81. virtualSalesCount: 0, // 虚拟销量
  82. recommendHot: false, // 是否热卖
  83. recommendBenefit: false, // 是否优惠
  84. recommendBest: false, // 是否精品
  85. recommendNew: false, // 是否新品
  86. recommendGood: false // 是否优品
  87. })
  88. /** 获得详情 */
  89. const getDetail = async () => {
  90. const id = params.spuId as number
  91. if (id) {
  92. formLoading.value = true
  93. try {
  94. const res = (await ProductSpuApi.getSpu(id)) as ProductSpuApi.SpuType
  95. res.skus.forEach((item) => {
  96. // 回显价格分转元
  97. item.price = formatToFraction(item.price)
  98. item.marketPrice = formatToFraction(item.marketPrice)
  99. item.costPrice = formatToFraction(item.costPrice)
  100. item.subCommissionFirstPrice = formatToFraction(item.subCommissionFirstPrice)
  101. item.subCommissionSecondPrice = formatToFraction(item.subCommissionSecondPrice)
  102. })
  103. formData.value = res
  104. } finally {
  105. formLoading.value = false
  106. }
  107. }
  108. }
  109. /** 提交按钮 */
  110. const submitForm = async () => {
  111. // 提交请求
  112. formLoading.value = true
  113. // 三个表单逐一校验,如果有一个表单校验不通过则切换到对应表单,如果有两个及以上的情况则切换到最前面的一个并弹出提示消息
  114. // 校验各表单
  115. try {
  116. await unref(basicInfoRef)?.validate()
  117. await unref(descriptionRef)?.validate()
  118. await unref(otherSettingsRef)?.validate()
  119. const deepCopyFormData = cloneDeep(unref(formData.value)) // 深拷贝一份 fix:这样最终 server 端不满足,不需要恢复,
  120. // TODO 兜底处理 sku 空数据
  121. formData.value.skus.forEach((sku) => {
  122. // 因为是空数据这里判断一下商品条码是否为空就行
  123. if (sku.barCode === '') {
  124. const index = deepCopyFormData.skus.findIndex(
  125. (item) => JSON.stringify(item.properties) === JSON.stringify(sku.properties)
  126. )
  127. // 删除这条 sku
  128. deepCopyFormData.skus.splice(index, 1)
  129. }
  130. })
  131. deepCopyFormData.skus.forEach((item) => {
  132. // 给sku name赋值
  133. item.name = deepCopyFormData.name
  134. // sku相关价格元转分
  135. item.price = convertToInteger(item.price)
  136. item.marketPrice = convertToInteger(item.marketPrice)
  137. item.costPrice = convertToInteger(item.costPrice)
  138. item.subCommissionFirstPrice = convertToInteger(item.subCommissionFirstPrice)
  139. item.subCommissionSecondPrice = convertToInteger(item.subCommissionSecondPrice)
  140. })
  141. // 处理轮播图列表
  142. const newSliderPicUrls = []
  143. deepCopyFormData.sliderPicUrls.forEach((item) => {
  144. // 如果是前端选的图
  145. // TODO @puhui999:疑问哈,为啥会是 object 呀?fix
  146. typeof item === 'object' ? newSliderPicUrls.push(item.url) : newSliderPicUrls.push(item)
  147. })
  148. deepCopyFormData.sliderPicUrls = newSliderPicUrls
  149. // 校验都通过后提交表单
  150. const data = deepCopyFormData as ProductSpuApi.SpuType
  151. const id = params.spuId as number
  152. if (!id) {
  153. await ProductSpuApi.createSpu(data)
  154. message.success(t('common.createSuccess'))
  155. } else {
  156. await ProductSpuApi.updateSpu(data)
  157. message.success(t('common.updateSuccess'))
  158. }
  159. close()
  160. } finally {
  161. formLoading.value = false
  162. }
  163. }
  164. /** 关闭按钮 */
  165. const close = () => {
  166. delView(unref(currentRoute))
  167. push('/product/product-spu')
  168. }
  169. /** 初始化 */
  170. onMounted(async () => {
  171. await getDetail()
  172. })
  173. </script>