ExpressTemplateForm.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. <template>
  2. <Dialog :title="dialogTitle" v-model="dialogVisible" width="1300px">
  3. <el-form
  4. ref="formRef"
  5. :model="formData"
  6. :rules="formRules"
  7. label-width="80px"
  8. v-loading="formLoading"
  9. >
  10. <el-form-item label="模板名称" prop="name">
  11. <el-input v-model="formData.name" placeholder="请输入模板名称" />
  12. </el-form-item>
  13. <el-form-item label="计费方式" prop="chargeMode">
  14. <el-radio-group v-model="formData.chargeMode" @change="changeChargeMode">
  15. <el-radio
  16. v-for="dict in getIntDictOptions(DICT_TYPE.EXPRESS_CHARGE_MODE)"
  17. :key="dict.value"
  18. :label="dict.value"
  19. >
  20. {{ dict.label }}
  21. </el-radio>
  22. </el-radio-group>
  23. </el-form-item>
  24. <el-form-item label="运费" prop="charges">
  25. <el-table border style="width: 100%" :data="formData.charges">
  26. <el-table-column align="center" label="区域" width="360">
  27. <template #default="{ row }">
  28. <el-cascader
  29. v-model="row.areaIds"
  30. :options="areaTree"
  31. :props="defaultProps2"
  32. class="w-1/1"
  33. clearable
  34. placeholder="请选择商品分类"
  35. filterable
  36. collapse-tags
  37. />
  38. </template>
  39. </el-table-column>
  40. <el-table-column
  41. align="center"
  42. :label="columnTitle.startCountTitle"
  43. width="180"
  44. prop="startCount"
  45. >
  46. <template #default="{ row }">
  47. <el-input-number v-model="row.startCount" :min="1" />
  48. </template>
  49. </el-table-column>
  50. <el-table-column width="180" align="center" label="运费(元)" prop="startPrice">
  51. <template #default="{ row }">
  52. <el-input-number v-model="row.startPrice" :min="1" />
  53. </template>
  54. </el-table-column>
  55. <el-table-column
  56. width="180"
  57. align="center"
  58. :label="columnTitle.extraCountTitle"
  59. prop="extraCount"
  60. >
  61. <template #default="{ row }">
  62. <el-input-number v-model="row.extraCount" :min="1" />
  63. </template>
  64. </el-table-column>
  65. <el-table-column width="180" align="center" label="续费(元)" prop="extraPrice">
  66. <template #default="{ row }">
  67. <el-input-number v-model="row.extraPrice" :min="1" />
  68. </template>
  69. </el-table-column>
  70. <el-table-column label="操作" align="center">
  71. <template #default="scope">
  72. <el-button link type="danger" @click="deleteChargeArea(scope.$index)">
  73. 删除
  74. </el-button>
  75. </template>
  76. </el-table-column>
  77. </el-table>
  78. </el-form-item>
  79. <el-form-item>
  80. <el-button type="primary" plain @click="addChargeArea()">
  81. <Icon icon="ep:plus" class="mr-5px" /> 添加区域
  82. </el-button>
  83. </el-form-item>
  84. <el-form-item label="包邮区域" prop="frees">
  85. <el-table border style="width: 100%" :data="formData.frees">
  86. <el-table-column align="center" label="区域" width="360">
  87. <template #default="{ row }">
  88. <el-cascader
  89. v-model="row.areaIds"
  90. :options="areaTree"
  91. :props="defaultProps2"
  92. class="w-1/1"
  93. clearable
  94. placeholder="请选择商品分类"
  95. filterable
  96. collapse-tags
  97. />
  98. </template>
  99. </el-table-column>
  100. <el-table-column align="center" :label="columnTitle.freeCountTitle" prop="freeCount">
  101. <template #default="{ row }">
  102. <el-input-number v-model="row.freeCount" :min="1" />
  103. </template>
  104. </el-table-column>
  105. <el-table-column align="center" label="包邮金额(元)" prop="freePrice">
  106. <template #default="{ row }">
  107. <el-input-number v-model="row.freePrice" :min="1" />
  108. </template>
  109. </el-table-column>
  110. <el-table-column label="操作" align="center">
  111. <template #default="scope">
  112. <el-button link type="danger" @click="deleteFreeArea(scope.$index)"> 删除 </el-button>
  113. </template>
  114. </el-table-column>
  115. </el-table>
  116. </el-form-item>
  117. <el-form-item>
  118. <el-button type="primary" plain @click="addFreeArea()">
  119. <Icon icon="ep:plus" class="mr-5px" /> 添加区域
  120. </el-button>
  121. </el-form-item>
  122. <el-form-item label="排序" prop="sort">
  123. <el-input-number v-model="formData.sort" controls-position="right" :min="0" />
  124. </el-form-item>
  125. </el-form>
  126. <template #footer>
  127. <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
  128. <el-button @click="dialogVisible = false">取 消</el-button>
  129. </template>
  130. </Dialog>
  131. </template>
  132. <script lang="ts" setup>
  133. import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
  134. import * as DeliveryExpressTemplateApi from '@/api/mall/trade/delivery/expressTemplate'
  135. import * as AreaApi from '@/api/system/area'
  136. import { defaultProps } from '@/utils/tree'
  137. import { yuanToFen, fenToYuan } from '@/utils'
  138. import { cloneDeep } from 'lodash-es'
  139. const { t } = useI18n() // 国际化
  140. const message = useMessage() // 消息弹窗
  141. const defaultProps2 = {
  142. ...defaultProps,
  143. multiple: true
  144. }
  145. const dialogVisible = ref(false) // 弹窗的是否展示
  146. const dialogTitle = ref('') // 弹窗的标题
  147. const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
  148. const formType = ref('') // 表单的类型:create - 新增;update - 修改
  149. const formData = ref({
  150. id: undefined,
  151. name: '',
  152. chargeMode: 1,
  153. sort: 0,
  154. charges: [],
  155. frees: []
  156. })
  157. const columnTitleMap = new Map()
  158. const columnTitle = ref({
  159. startCountTitle: '首件',
  160. extraCountTitle: '续件',
  161. freeCountTitle: '包邮件数'
  162. })
  163. const formRules = reactive({
  164. name: [{ required: true, message: '模板名称不能为空', trigger: 'blur' }],
  165. chargeMode: [{ required: true, message: '配送计费方式不能为空', trigger: 'blur' }],
  166. sort: [{ required: true, message: '分类排序不能为空', trigger: 'blur' }]
  167. })
  168. const formRef = ref() // 表单 Ref
  169. /** 打开弹窗 */
  170. const open = async (type: string, id?: number) => {
  171. dialogVisible.value = true
  172. dialogTitle.value = t('action.' + type)
  173. formType.value = type
  174. resetForm()
  175. try {
  176. // 修改时,设置数据
  177. if (id) {
  178. formLoading.value = true
  179. formData.value = await DeliveryExpressTemplateApi.getDeliveryExpressTemplate(id)
  180. columnTitle.value = columnTitleMap.get(formData.value.chargeMode)
  181. formData.value.charges.forEach((item) => {
  182. // 前端价格以元展示
  183. item.startPrice = fenToYuan(item.startPrice)
  184. item.extraPrice = fenToYuan(item.extraPrice)
  185. })
  186. formData.value.frees.forEach((item) => {
  187. item.freePrice = fenToYuan(item.freePrice)
  188. })
  189. }
  190. } finally {
  191. formLoading.value = false
  192. }
  193. }
  194. defineExpose({ open }) // 提供 open 方法,用于打开弹窗
  195. /** 提交表单 */
  196. const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
  197. const submitForm = async () => {
  198. // 校验表单
  199. if (!formRef) return
  200. const valid = await formRef.value.validate()
  201. if (!valid) return
  202. // 提交请求
  203. formLoading.value = true
  204. try {
  205. const data = cloneDeep(formData.value) as DeliveryExpressTemplateApi.DeliveryExpressTemplateVO
  206. // 前端价格以元展示,提交到后端。用分计算
  207. data.charges.forEach((item) => {
  208. item.startPrice = yuanToFen(item.startPrice)
  209. item.extraPrice = yuanToFen(item.extraPrice)
  210. })
  211. data.frees.forEach((item) => {
  212. item.freePrice = yuanToFen(item.freePrice)
  213. })
  214. if (formType.value === 'create') {
  215. await DeliveryExpressTemplateApi.createDeliveryExpressTemplate(data)
  216. message.success(t('common.createSuccess'))
  217. } else {
  218. await DeliveryExpressTemplateApi.updateDeliveryExpressTemplate(data)
  219. message.success(t('common.updateSuccess'))
  220. }
  221. dialogVisible.value = false
  222. // 发送操作成功的事件
  223. emit('success')
  224. } finally {
  225. formLoading.value = false
  226. }
  227. }
  228. /** 重置表单 */
  229. const resetForm = () => {
  230. formData.value = {
  231. id: undefined,
  232. name: '',
  233. chargeMode: 1,
  234. charges: [
  235. {
  236. areaIds: [1],
  237. startCount: 2,
  238. startPrice: 5,
  239. extraCount: 5,
  240. extraPrice: 10
  241. }
  242. ],
  243. frees: [],
  244. sort: 0
  245. }
  246. columnTitle.value = columnTitleMap.get(1)
  247. formRef.value?.resetFields()
  248. }
  249. /** 配送计费方法改变 */
  250. const changeChargeMode = (chargeMode: number) => {
  251. columnTitle.value = columnTitleMap.get(chargeMode)
  252. }
  253. /** 初始化数据 */
  254. const areaTree = ref([])
  255. const initData = async () => {
  256. // 表头标题和计费方式的映射
  257. columnTitleMap.set(1, {
  258. startCountTitle: '首件',
  259. extraCountTitle: '续件',
  260. freeCountTitle: '包邮件数'
  261. })
  262. columnTitleMap.set(2, {
  263. startCountTitle: '首件重量(kg)',
  264. extraCountTitle: '续件重量(kg)',
  265. freeCountTitle: '包邮重量(kg)'
  266. })
  267. columnTitleMap.set(3, {
  268. startCountTitle: '首件体积(m³)',
  269. extraCountTitle: '续件体积(m³)',
  270. freeCountTitle: '包邮体积(m³)'
  271. })
  272. // 加载区域数据
  273. areaTree.value = await AreaApi.getAreaTree()
  274. }
  275. /** 添加计费区域 */
  276. const addChargeArea = () => {
  277. const data = formData.value
  278. data.charges.push({
  279. areaIds: [],
  280. startCount: 1,
  281. startPrice: 1,
  282. extraCount: 1,
  283. extraPrice: 1
  284. })
  285. }
  286. /** 删除计费区域 */
  287. const deleteChargeArea = (index) => {
  288. const data = formData.value
  289. data.charges.splice(index, 1)
  290. }
  291. /** 添加包邮区域 */
  292. const addFreeArea = () => {
  293. const data = formData.value
  294. data.frees.push({
  295. areaIds: [],
  296. freeCount: 1,
  297. freePrice: 1
  298. })
  299. }
  300. /** 删除包邮区域 */
  301. const deleteFreeArea = (index) => {
  302. const data = formData.value
  303. data.frees.splice(index, 1)
  304. }
  305. /** 初始化 **/
  306. onMounted(() => {
  307. initData()
  308. })
  309. </script>