index.vue 16 KB

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