addressMixin.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635
  1. import { getList, getAddressList, add, update, getDetail, addAddress, updateAddress, deleteAddress } from '@/api/order/address'
  2. import { getCustomerList } from '@/api/common/index'
  3. import { mapGetters } from 'vuex'
  4. import RegionCascader from '@/components/region-cascader/index.vue'
  5. // @ts-check
  6. /**
  7. * @typedef {import('./types').OrderAddressMixinComponent} OrderAddressMixinComponent
  8. * @typedef {import('./types').CustomerAddressForm} CustomerAddressForm
  9. * @typedef {import('./types').CustomerAddressQueryParams} CustomerAddressQueryParams
  10. * @typedef {import('./types').CustomerAddressItem} CustomerAddressItem
  11. * @typedef {import('./types').RegionData} RegionData
  12. */
  13. export default {
  14. name: 'OrderAddress',
  15. components: {
  16. RegionCascader
  17. },
  18. /**
  19. * @returns {Object}
  20. */
  21. data() {
  22. return {
  23. customerOptions: [],
  24. customerLoading: false,
  25. customerSearchKeyword: '',
  26. customerSearchTimer: null,
  27. form: {},
  28. query: {},
  29. loading: true,
  30. regionCascaderValue: [],
  31. selectionList: [],
  32. data: [],
  33. option: {
  34. height: 'auto',
  35. calcHeight: 30,
  36. tip: false,
  37. searchShow: true,
  38. searchMenuSpan: 6,
  39. border: true,
  40. index: true,
  41. selection: false,
  42. viewBtn: true,
  43. delBtn: true,
  44. dialogClickModal: false,
  45. dialogWidth: 900,
  46. page: false,
  47. column: [
  48. {
  49. label: '客户',
  50. prop: 'customerId',
  51. type: 'select',
  52. width: 200,
  53. filterable: true,
  54. remote: false,
  55. reserveKeyword: true,
  56. placeholder: '请选择客户',
  57. dicData: [],
  58. props: {
  59. label: 'label',
  60. value: 'value'
  61. },
  62. rules: [
  63. {
  64. required: true,
  65. message: '请选择客户',
  66. trigger: 'change'
  67. }
  68. ],
  69. change: 'handleCustomerChange',
  70. addDisplay: false,
  71. editDisplay: false
  72. },
  73. {
  74. label: '客户编码',
  75. prop: 'customerCode',
  76. width: 120,
  77. rules: [
  78. {
  79. required: true,
  80. message: '请输入客户编码',
  81. trigger: 'blur'
  82. },
  83. {
  84. pattern: /^[A-Z0-9_-]+$/,
  85. message: '客户编码只能包含大写字母、数字、下划线和中横线',
  86. trigger: 'blur'
  87. },
  88. ],
  89. addDisplay: false,
  90. editDisplay: false
  91. },
  92. {
  93. label: '客户名称',
  94. prop: 'customerName',
  95. width: 200,
  96. rules: [
  97. {
  98. required: true,
  99. message: '请输入客户名称',
  100. trigger: 'blur'
  101. },
  102. {
  103. max: 50,
  104. message: '客户名称不能超过50个字符',
  105. trigger: 'blur'
  106. }
  107. ],
  108. addDisplay: false,
  109. editDisplay: false
  110. },
  111. {
  112. label: '收货人姓名',
  113. prop: 'receiverName',
  114. search: true,
  115. width: 120,
  116. rules: [
  117. {
  118. required: true,
  119. message: '请输入收货人姓名',
  120. trigger: 'blur'
  121. },
  122. ]
  123. },
  124. {
  125. label: '收货人电话',
  126. prop: 'receiverPhone',
  127. search: true,
  128. width: 130,
  129. rules: [
  130. {
  131. required: true,
  132. message: '请输入收货人电话',
  133. trigger: 'blur'
  134. },
  135. {
  136. pattern: /^1[3-9]\d{9}$/,
  137. message: '请输入正确的手机号码',
  138. trigger: 'blur'
  139. }
  140. ]
  141. },
  142. {
  143. label: '地区',
  144. prop: 'regionName',
  145. search: true,
  146. width: 150,
  147. formSlot: true, // 启用表单插槽
  148. rules: [
  149. {
  150. required: true,
  151. message: '请选择地区',
  152. trigger: 'change'
  153. }
  154. ]
  155. },
  156. {
  157. label: '地区编码',
  158. prop: 'regionCode',
  159. hide: true,
  160. rules: [
  161. {
  162. required: true,
  163. message: '请输入地区编码',
  164. trigger: 'blur'
  165. }
  166. ]
  167. },
  168. {
  169. label: '详细地址',
  170. prop: 'detailAddress',
  171. width: 250,
  172. overHidden: true,
  173. span: 24,
  174. rules: [
  175. {
  176. required: true,
  177. message: '请输入详细地址',
  178. trigger: 'blur'
  179. },
  180. {
  181. min: 5,
  182. max: 200,
  183. message: '详细地址长度在5到200个字符',
  184. trigger: 'blur'
  185. }
  186. ]
  187. },
  188. {
  189. label: '邮政编码',
  190. prop: 'postalCode',
  191. width: 100,
  192. rules: [
  193. {
  194. pattern: /^\d{6}$/,
  195. message: '请输入正确的邮政编码',
  196. trigger: 'blur'
  197. }
  198. ]
  199. },
  200. {
  201. label: '默认地址',
  202. prop: 'isDefault',
  203. type: 'select',
  204. dicData: [
  205. { label: '否', value: 0 },
  206. { label: '是', value: 1 }
  207. ],
  208. search: true,
  209. slot: true,
  210. width: 100,
  211. value: 0
  212. },
  213. {
  214. label: '状态',
  215. prop: 'isActive',
  216. type: 'select',
  217. dicData: [
  218. { label: '禁用', value: 0 },
  219. { label: '启用', value: 1 }
  220. ],
  221. search: true,
  222. slot: true,
  223. width: 100,
  224. value: 1
  225. },
  226. {
  227. label: '创建时间',
  228. prop: 'createTime',
  229. type: 'datetime',
  230. format: 'yyyy-MM-dd HH:mm:ss',
  231. valueFormat: 'yyyy-MM-dd HH:mm:ss',
  232. width: 160,
  233. addDisplay: false,
  234. editDisplay: false
  235. },
  236. {
  237. label: '更新时间',
  238. prop: 'updateTime',
  239. type: 'datetime',
  240. format: 'yyyy-MM-dd HH:mm:ss',
  241. valueFormat: 'yyyy-MM-dd HH:mm:ss',
  242. width: 160,
  243. addDisplay: false,
  244. editDisplay: false
  245. }
  246. ]
  247. }
  248. }
  249. },
  250. computed: {
  251. ...mapGetters(['permission']),
  252. /**
  253. * 权限配置
  254. * @this {OrderAddressMixinComponent & Vue}
  255. */
  256. permissionList() {
  257. return {
  258. // addBtn: this.vaildData(this.permission.order_address_add, false),
  259. // viewBtn: this.vaildData(this.permission.order_address_view, false),
  260. // delBtn: this.vaildData(this.permission.order_address_delete, false),
  261. // editBtn: this.vaildData(this.permission.order_address_edit, false)
  262. addBtn: true,
  263. viewBtn: true,
  264. delBtn: true,
  265. editBtn: true
  266. }
  267. },
  268. /**
  269. * 选中的ID字符串
  270. * @this {OrderAddressMixinComponent & Vue}
  271. * @returns {string}
  272. */
  273. ids() {
  274. /** @type {(string|number)[]} */
  275. const ids = []
  276. this.selectionList.forEach(ele => {
  277. ids.push(ele.id)
  278. })
  279. return ids.join(',')
  280. }
  281. },
  282. methods: {
  283. /**
  284. * 处理客户选择变化
  285. * @param {Object} params - 参数对象
  286. * @param {string|number} params.value - 选中的值
  287. * @param {Object} params.column - 列配置
  288. * @returns {void}
  289. * @this {OrderAddressMixinComponent & Vue}
  290. */
  291. handleCustomerChange({ value, column }) {
  292. if (value) {
  293. const selectedCustomer = this.customerOptions.find(option => option.value === value)
  294. if (selectedCustomer) {
  295. // 自动填充客户信息
  296. this.form.customerId = Number(selectedCustomer.customerId)
  297. this.form.customerCode = selectedCustomer.customerCode
  298. this.form.customerName = selectedCustomer.customerName
  299. } else {
  300. this.clearCustomerData()
  301. }
  302. } else {
  303. this.clearCustomerData()
  304. }
  305. },
  306. /**
  307. * 加载客户选项列表
  308. * @this {OrderAddressMixinComponent & Vue}
  309. */
  310. /**
  311. * 加载客户选项
  312. * @param {string} keyword - 搜索关键词
  313. * @returns {Promise<void>}
  314. * @this {OrderAddressMixinComponent & Vue}
  315. */
  316. async loadCustomerOptions(keyword = '') {
  317. try {
  318. /** @type {Record<string, string>} */
  319. const params = {}
  320. // 如果有搜索关键词,添加到查询参数中
  321. if (keyword.trim()) {
  322. params['Customer_CODE'] = keyword
  323. params['Customer_NAME'] = keyword
  324. }
  325. const response = await getCustomerList(1, 50, params)
  326. if (response.data && response.data.success) {
  327. const customers = response.data.data.records || []
  328. this.customerOptions = customers.map(customer => ({
  329. value: customer['Customer_ID'],
  330. label: `${customer['Customer_CODE']} - ${customer['Customer_NAME']}`,
  331. customerCode: customer['Customer_CODE'],
  332. customerName: customer['Customer_NAME'],
  333. customerId: String(customer['Customer_ID'])
  334. }))
  335. // 更新表格配置中的选项数据
  336. this.updateCustomerOptionsInColumn()
  337. } else {
  338. this.customerOptions = []
  339. this.$message.warning('获取客户列表失败')
  340. }
  341. } catch (error) {
  342. this.customerOptions = []
  343. console.error('加载客户选项失败:', error)
  344. this.$message.error('加载客户选项失败,请稍后重试')
  345. } finally {
  346. this.customerLoading = false
  347. }
  348. },
  349. /**
  350. * 搜索客户(防抖处理)
  351. * @param {string} query - 搜索查询字符串
  352. * @returns {void}
  353. * @this {OrderAddressMixinComponent & Vue}
  354. */
  355. searchCustomers(query) {
  356. // 清除之前的定时器
  357. if (this.customerSearchTimer) {
  358. clearTimeout(this.customerSearchTimer)
  359. }
  360. if (query !== '') {
  361. this.customerLoading = true
  362. // 设置新的定时器,300ms后执行搜索
  363. this.customerSearchTimer = Number(setTimeout(() => {
  364. this.loadCustomerOptions(query)
  365. }, 300))
  366. } else {
  367. this.customerOptions = []
  368. }
  369. },
  370. /**
  371. * 更新表格配置中的客户选项数据
  372. * @returns {void}
  373. * @this {OrderAddressMixinComponent & Vue}
  374. */
  375. updateCustomerOptionsInColumn() {
  376. if (this.option && this.option.column) {
  377. const customerColumn = this.option.column.find(col => col.prop === 'customerId')
  378. if (customerColumn) {
  379. customerColumn.dicData = this.customerOptions
  380. }
  381. }
  382. },
  383. /**
  384. * 清除客户相关数据
  385. * @returns {void}
  386. * @this {OrderAddressMixinComponent & Vue}
  387. */
  388. clearCustomerData() {
  389. this.form.customerCode = ''
  390. this.form.customerName = ''
  391. this.form.customerId = null
  392. },
  393. /**
  394. * 地区选择改变事件
  395. * @param {RegionData} data - 地区数据
  396. * @returns {void}
  397. * @this {OrderAddressMixinComponent & Vue}
  398. */
  399. handleRegionChange(data) {
  400. const { regionName } = data
  401. // 只设置regionName,regionCode保持独立
  402. this.form.regionName = regionName
  403. },
  404. /**
  405. * 新增前的回调
  406. * @this {OrderAddressMixinComponent & Vue}
  407. * @param {Function} done - 完成回调
  408. * @param {string} type - 操作类型
  409. * @returns {Promise<void>}
  410. */
  411. async beforeOpen(done, type) {
  412. // 确保客户选项已加载
  413. if (this.customerOptions.length === 0) {
  414. await this.loadCustomerOptions()
  415. }
  416. if (['edit', 'view'].includes(type)) {
  417. try {
  418. if (this.form.id) {
  419. const res = await getDetail(this.form.id)
  420. this.form = res.data.data
  421. // 如果有客户信息,确保客户选项中包含当前客户
  422. if (this.form.customerId && this.form.customerCode && this.form.customerName) {
  423. const existingCustomer = this.customerOptions.find(option => option.value === this.form.customerId?.toString())
  424. if (!existingCustomer) {
  425. this.customerOptions.unshift({
  426. value: this.form.customerId,
  427. label: `${this.form.customerCode} - ${this.form.customerName}`,
  428. customerCode: this.form.customerCode,
  429. customerName: this.form.customerName,
  430. customerId: String(this.form.customerId)
  431. })
  432. this.updateCustomerOptionsInColumn()
  433. }
  434. }
  435. }
  436. } catch (error) {
  437. window.console.log(error)
  438. }
  439. }
  440. done()
  441. },
  442. /**
  443. * 获取数据
  444. * @param {Record<string, any>} params - 查询参数
  445. * @returns {Promise<void>}
  446. * @this {OrderAddressMixinComponent & Vue}
  447. */
  448. async onLoad(params = {}) {
  449. this.loading = true
  450. try {
  451. // const customerId = params['customerId'] || this.query['customerId']
  452. const res = await getAddressList()
  453. this.data = res.data.data || []
  454. this.loading = false
  455. this.selectionClear()
  456. } catch (error) {
  457. this.loading = false
  458. window.console.log(error)
  459. }
  460. },
  461. /**
  462. * 新增保存
  463. * @this {OrderAddressMixinComponent & Vue}
  464. * @param {CustomerAddressForm} row - 表单数据
  465. * @param {Function} done - 完成回调
  466. * @param {Function} loading - 加载状态回调
  467. * @returns {Promise<void>}
  468. */
  469. async rowSave(row, done, loading) {
  470. try {
  471. // 转换为API期望的类型
  472. const apiData = {
  473. ...row,
  474. customerId: row.customerId || 0
  475. }
  476. await addAddress(apiData)
  477. done()
  478. this.onLoad(this.query)
  479. this.$message({
  480. type: 'success',
  481. message: '操作成功!'
  482. })
  483. } catch (error) {
  484. loading()
  485. window.console.log(error)
  486. }
  487. },
  488. /**
  489. * 编辑保存
  490. * @this {OrderAddressMixinComponent & Vue}
  491. * @param {CustomerAddressForm} row - 表单数据
  492. * @param {number} index - 行索引
  493. * @param {Function} done - 完成回调
  494. * @param {Function} loading - 加载状态回调
  495. * @returns {Promise<void>}
  496. */
  497. async rowUpdate(row, index, done, loading) {
  498. try {
  499. // 转换为API期望的类型
  500. const apiData = {
  501. ...row,
  502. customerId: row.customerId || 0
  503. }
  504. await updateAddress(apiData)
  505. done()
  506. this.onLoad(this.query)
  507. this.$message({
  508. type: 'success',
  509. message: '操作成功!'
  510. })
  511. } catch (error) {
  512. loading()
  513. window.console.log(error)
  514. }
  515. },
  516. /**
  517. * 删除行数据
  518. * @param {CustomerAddressItem} row - 行数据
  519. * @param {number} index - 行索引
  520. * @returns {Promise<void>}
  521. * @this {OrderAddressMixinComponent & Vue}
  522. */
  523. async rowDel(row, index) {
  524. try {
  525. await deleteAddress(row.id || 0)
  526. this.onLoad(this.query)
  527. this.$message({
  528. type: 'success',
  529. message: '删除成功!'
  530. })
  531. } catch (error) {
  532. this.$message({
  533. type: 'error',
  534. message: '删除失败!'
  535. })
  536. window.console.log(error)
  537. }
  538. },
  539. /**
  540. * 搜索回调
  541. * @this {OrderAddressMixinComponent & Vue}
  542. * @param {CustomerAddressQueryParams} params - 查询参数
  543. * @param {Function} done - 完成回调
  544. * @returns {void}
  545. */
  546. searchChange(params, done) {
  547. this.query = params
  548. this.onLoad(params)
  549. done()
  550. },
  551. /**
  552. * 搜索重置回调
  553. * @this {OrderAddressMixinComponent & Vue}
  554. */
  555. searchReset() {
  556. this.query = {}
  557. this.onLoad()
  558. },
  559. /**
  560. * 选择改变时的回调
  561. * @this {OrderAddressMixinComponent & Vue}
  562. * @param {CustomerAddressItem[]} list - 选中的项目列表
  563. * @returns {void}
  564. */
  565. selectionChange(list) {
  566. this.selectionList = list
  567. },
  568. /**
  569. * 清空选中
  570. * @returns {void}
  571. * @this {OrderAddressMixinComponent & Vue}
  572. */
  573. selectionClear() {
  574. this.selectionList = []
  575. /** @type {any} */
  576. const crudRef = this.$refs.crud
  577. if (crudRef && crudRef.toggleSelection) {
  578. crudRef.toggleSelection()
  579. }
  580. },
  581. /**
  582. * 刷新回调
  583. * @returns {void}
  584. * @this {OrderAddressMixinComponent & Vue}
  585. */
  586. refreshChange() {
  587. this.onLoad(this.query)
  588. }
  589. },
  590. /**
  591. * 组件创建时加载客户选项
  592. * @this {OrderAddressMixinComponent & Vue}
  593. */
  594. async created() {
  595. await this.loadCustomerOptions()
  596. },
  597. /**
  598. * 组件销毁前清理定时器
  599. * @this {OrderAddressMixinComponent & Vue}
  600. */
  601. beforeDestroy() {
  602. if (this.customerSearchTimer) {
  603. clearTimeout(this.customerSearchTimer)
  604. }
  605. }
  606. }