ProductList.vue 5.1 KB

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