ProductList.vue 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. <!-- 合同 Form 表单下的 Product 列表 -->
  2. <template>
  3. <el-row justify="end">
  4. <el-button plain type="primary" @click="openForm">添加产品</el-button>
  5. </el-row>
  6. <el-table :data="list" :show-overflow-tooltip="true" :stripe="true">
  7. <el-table-column align="center" label="产品名称" prop="name" width="120" />
  8. <el-table-column
  9. :formatter="fenToYuanFormat"
  10. align="center"
  11. label="价格"
  12. prop="price"
  13. width="100"
  14. />
  15. <el-table-column align="center" label="产品类型" prop="categoryName" width="100" />
  16. <el-table-column align="center" label="产品单位" prop="unit">
  17. <template #default="scope">
  18. <dict-tag :type="DICT_TYPE.CRM_PRODUCT_UNIT" :value="scope.row.unit" />
  19. </template>
  20. </el-table-column>
  21. <el-table-column align="center" label="产品编码" prop="no" />
  22. <el-table-column align="center" fixed="right" label="数量" prop="count" width="100">
  23. <template #default="{ row }: { row: ProductApi.ProductExpandVO }">
  24. <el-input-number
  25. v-model="row.count"
  26. controls-position="right"
  27. :min="0"
  28. :precision="0"
  29. class="!w-100%"
  30. />
  31. </template>
  32. </el-table-column>
  33. <el-table-column
  34. align="center"
  35. fixed="right"
  36. label="折扣(%)"
  37. prop="discountPercent"
  38. width="120"
  39. >
  40. <template #default="{ row }: { row: ProductApi.ProductExpandVO }">
  41. <el-input-number
  42. v-model="row.discountPercent"
  43. controls-position="right"
  44. :min="0"
  45. :max="100"
  46. :precision="0"
  47. class="!w-100%"
  48. />
  49. </template>
  50. </el-table-column>
  51. <el-table-column align="center" fixed="right" label="合计" prop="totalPrice" width="100">
  52. <template #default="{ row }: { row: ProductApi.ProductExpandVO }">
  53. {{ fenToYuan(getTotalPrice(row)) }}
  54. </template>
  55. </el-table-column>
  56. <el-table-column align="center" fixed="right" label="操作" width="60">
  57. <template #default="scope">
  58. <el-button link type="danger" @click="handleDelete(scope.row.id)"> 移除</el-button>
  59. </template>
  60. </el-table-column>
  61. </el-table>
  62. <!-- table 选择表单 -->
  63. <TableSelectForm ref="tableSelectFormRef" v-model="multipleSelection" title="选择产品">
  64. <el-table-column align="center" label="产品名称" prop="name" width="160" />
  65. <el-table-column align="center" label="产品类型" prop="categoryName" width="160" />
  66. <el-table-column align="center" label="产品单位" prop="unit">
  67. <template #default="scope">
  68. <dict-tag :type="DICT_TYPE.CRM_PRODUCT_UNIT" :value="scope.row.unit" />
  69. </template>
  70. </el-table-column>
  71. <el-table-column align="center" label="产品编码" prop="no" />
  72. <el-table-column
  73. :formatter="fenToYuanFormat"
  74. align="center"
  75. label="价格(元)"
  76. prop="price"
  77. width="100"
  78. />
  79. </TableSelectForm>
  80. </template>
  81. <script lang="ts" setup>
  82. import * as ProductApi from '@/api/crm/product'
  83. import { DICT_TYPE } from '@/utils/dict'
  84. import { fenToYuanFormat } from '@/utils/formatter'
  85. import { TableSelectForm } from '@/components/Table/index'
  86. import { fenToYuan, floatToFixed2, yuanToFen } from '@/utils'
  87. defineOptions({ name: 'ProductList' })
  88. const props = withDefaults(defineProps<{ modelValue: ProductApi.ProductExpandVO[] }>(), {
  89. modelValue: () => []
  90. })
  91. const emits = defineEmits<{
  92. (e: 'update:modelValue', v: any[]): void
  93. }>()
  94. const list = ref<ProductApi.ProductExpandVO[]>([]) // 已添加的产品列表
  95. const multipleSelection = ref<ProductApi.ProductExpandVO[]>([]) // 多选
  96. /** 处理删除 */
  97. const handleDelete = (id: number) => {
  98. const index = list.value.findIndex((item) => item.id === id)
  99. if (index !== -1) {
  100. list.value.splice(index, 1)
  101. }
  102. }
  103. /** 打开 Product 弹窗 */
  104. const tableSelectFormRef = ref<InstanceType<typeof TableSelectForm>>()
  105. const openForm = () => {
  106. tableSelectFormRef.value?.open(ProductApi.getProductPage)
  107. }
  108. /** 计算 totalPrice */
  109. const getTotalPrice = computed(() => (row: ProductApi.ProductExpandVO) => {
  110. const totalPrice =
  111. (Number(row.price) / 100) * Number(row.count) * (1 - Number(row.discountPercent) / 100)
  112. row.totalPrice = isNaN(totalPrice) ? 0 : yuanToFen(totalPrice)
  113. return isNaN(totalPrice) ? 0 : totalPrice.toFixed(2)
  114. })
  115. /** 编辑时合同产品回显 */
  116. const isSetListValue = ref(false) // 判断是否已经给 list 赋值过,用于编辑表单产品回显
  117. watch(
  118. () => props.modelValue,
  119. (val) => {
  120. if (!val || val.length === 0 || isSetListValue.value) {
  121. return
  122. }
  123. list.value = [
  124. ...props.modelValue.map((item) => {
  125. item.totalPrice = floatToFixed2(item.totalPrice) as unknown as number
  126. return item
  127. })
  128. ]
  129. isSetListValue.value = true
  130. },
  131. { immediate: true, deep: true }
  132. )
  133. /** 监听列表变化,动态更新合同产品列表 */
  134. watch(
  135. list,
  136. (val) => {
  137. if (!val || val.length === 0) {
  138. return
  139. }
  140. emits('update:modelValue', list.value)
  141. },
  142. { deep: true }
  143. )
  144. // 监听产品选择结果动态添加产品到列表中,如果产品存在则不放入列表中
  145. watch(
  146. multipleSelection,
  147. (val) => {
  148. if (!val || val.length === 0) {
  149. return
  150. }
  151. // 过滤出不在列表中的产品
  152. const ids = list.value.map((item) => item.id)
  153. const productList = multipleSelection.value.filter((item) => ids.indexOf(item.id) === -1)
  154. if (!productList || productList.length === 0) {
  155. return
  156. }
  157. list.value.push(...productList)
  158. },
  159. { deep: true }
  160. )
  161. </script>