addForm.vue 6.4 KB

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