index.vue 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. <template>
  2. <CtTable
  3. class="mt-3"
  4. :items="items"
  5. :headers="headers"
  6. :loading="false"
  7. :elevation="0"
  8. :isTools="true"
  9. :showPage="false"
  10. :showSelect="props.showSelect"
  11. selectStrategy="single"
  12. itemKey="id"
  13. @add="handleAdd"
  14. @selected="handleSelected"
  15. >
  16. <template #actions="{ item }">
  17. <v-btn color="primary" @click.stop="handleEdit(item)" variant="text">编辑</v-btn>
  18. <v-btn color="error" @click.stop="handleDelete(item)" variant="text">删除</v-btn>
  19. <v-btn v-if="!item.defaultStatus" color="success" @click.stop="handleSetDefault(item)" variant="text">设为默认</v-btn>
  20. </template>
  21. </CtTable>
  22. <CtDialog :visible="showDialog" titleClass="text-h6" :footer="true" :widthType="3" :title="isAdd ? '新增收货地址' : '编辑收货地址'" @submit="handleSubmit" @close="handleClose">
  23. <CtForm ref="CtFormRef" :items="formItems">
  24. <template #areaId="{ item }">
  25. <div class="d-flex" style="width: 100%;">
  26. <div class="mt-2" style="color: #777; width: 100px;">省市区 *</div>
  27. <el-cascader
  28. ref="cascaderAddr"
  29. v-model="item.value"
  30. size="large"
  31. clearable
  32. class="mb-5"
  33. placeholder="省市区 *"
  34. style="flex: 1;"
  35. :props="{ value: 'id', label: 'name', emitPath: false }"
  36. :options="item.items"
  37. @change="handleChangeArea(item)"
  38. @clear="item.labelValue = ''"
  39. ></el-cascader>
  40. </div>
  41. </template>
  42. <template #defaultStatus="{ item }">
  43. <div class="d-flex align-center">
  44. <span class="color-666 mr-5">设为默认地址</span>
  45. <v-switch v-model="item.value" hide-details color="primary"></v-switch>
  46. </div>
  47. </template>
  48. </CtForm>
  49. </CtDialog>
  50. </template>
  51. <script setup>
  52. defineOptions({ name: 'personal-personCenter-address-index' })
  53. import { ref } from 'vue'
  54. import { getMallUserAddressList, deleteMallUserAddress, createMallUserAddress, updateMallUserAddress } from '@/api/mall/address'
  55. import Confirm from '@/plugins/confirm'
  56. import { getDict } from '@/hooks/web/useDictionaries'
  57. import Snackbar from '@/plugins/snackbar'
  58. const props = defineProps({
  59. showSelect: {
  60. type: Boolean,
  61. default: false,
  62. }
  63. })
  64. const isAdd = ref(true)
  65. const editId = ref(null)
  66. const showDialog = ref(false)
  67. const items = ref([])
  68. const headers = [
  69. { title: '收货人', key: 'name', sortable: false },
  70. { title: '联系电话', key: 'mobile', sortable: false },
  71. { title: '所在地区', key: 'areaName', sortable: false },
  72. { title: '详细地址', key: 'detailAddress', sortable: false },
  73. { title: '是否默认', key: 'defaultStatus', sortable: false, value: item => item.defaultStatus ? '是' : '否' },
  74. { title: '操作', key: 'actions', sortable: false }
  75. ]
  76. const cascaderAddr = ref()
  77. const CtFormRef = ref()
  78. const formItems = ref({
  79. options: [
  80. {
  81. type: 'text',
  82. key: 'name',
  83. value: null,
  84. default: localStorage.getItem('baseInfo') ? JSON.parse(localStorage.getItem('baseInfo')).name : '',
  85. hide: false,
  86. label: '收货人姓名 *',
  87. rules: [v => !!v || '请输入收货人姓名'],
  88. },
  89. {
  90. type: 'text',
  91. key: 'mobile',
  92. value: null,
  93. default: localStorage.getItem('userInfo') ? JSON.parse(localStorage.getItem('userInfo')).phone : '',
  94. label: '收货人联系电话 *',
  95. outlined: true,
  96. rules: [v => !!v || '请填写收货人联系电话']
  97. },
  98. {
  99. slotName: 'areaId',
  100. key: 'areaId',
  101. labelValue: '',
  102. value: null,
  103. default: null,
  104. items: []
  105. },
  106. {
  107. type: 'textarea',
  108. key: 'detailAddress',
  109. value: '',
  110. default: null,
  111. label: '详细地址 *',
  112. rules: [ v => !!v || '请填写详细地址' ]
  113. },
  114. {
  115. slotName: 'defaultStatus',
  116. key: 'defaultStatus',
  117. value: false,
  118. default: false,
  119. }
  120. ]
  121. })
  122. getDict('areaTreeData', null, 'areaTreeData').then(({ data }) => {
  123. data = data?.length && data || []
  124. formItems.value.options.find(e => e.key === 'areaId').items = data
  125. })
  126. const singleSelectObj = ref([])
  127. const handleSelected = (e) => {
  128. singleSelectObj.value = e?.length ? items.value.find(item => item.id === e[0]) : null
  129. }
  130. const getSelected = () => {
  131. return singleSelectObj.value
  132. }
  133. // 获取地址列表
  134. const getAddressList = async () => {
  135. const data = await getMallUserAddressList()
  136. items.value = data
  137. }
  138. getAddressList()
  139. // 新增
  140. const handleAdd = () => {
  141. formItems.value.options.forEach(e => e.value = e.default)
  142. showDialog.value = true
  143. isAdd.value = true
  144. }
  145. // 地区选择
  146. const handleChangeArea = (item) => {
  147. const node = cascaderAddr.value.getCheckedNodes() ? cascaderAddr.value.getCheckedNodes()[0] : null
  148. if (!node) return
  149. item.labelValue = node.pathLabels.join(' ')
  150. }
  151. // 设置为默认地址
  152. const handleSetDefault = async (item) => {
  153. Confirm('系统提示', '是否确定设置该地址为默认收货地址?').then(async () => {
  154. await updateMallUserAddress({ ...item, defaultStatus: true })
  155. Snackbar.success('设置成功')
  156. await getAddressList()
  157. })
  158. }
  159. // 编辑
  160. const handleEdit = (item) => {
  161. isAdd.value = false
  162. editId.value = item.id
  163. formItems.value.options.forEach(e => e.value = item[e.key])
  164. showDialog.value = true
  165. }
  166. // 提交
  167. const handleClose = () => {
  168. showDialog.value = false
  169. editId.value = null
  170. }
  171. const handleSubmit = async () => {
  172. const { valid } = await CtFormRef.value.formRef.validate()
  173. if (!valid) return
  174. const obj = {}
  175. formItems.value.options.forEach(e => {
  176. if (e.key === 'areaId') {
  177. obj.areaId = e.value
  178. obj.areaName = e.labelValue
  179. } else obj[e.key] = e.value
  180. })
  181. if (!obj.areaId && !obj.areaName) return Snackbar.warning('请将必填项填写完整')
  182. try {
  183. isAdd.value ? await createMallUserAddress(obj) : await updateMallUserAddress(obj)
  184. Snackbar.success(isAdd.value ? '新增成功' : '编辑成功')
  185. handleClose()
  186. getAddressList()
  187. } catch {}
  188. }
  189. // 删除
  190. const handleDelete = (item) => {
  191. Confirm('系统提示', '是否确认删除该收货地址?').then(async () => {
  192. await deleteMallUserAddress(item.id)
  193. Snackbar.success('删除成功')
  194. await getAddressList()
  195. })
  196. }
  197. defineExpose({
  198. getSelected
  199. })
  200. </script>
  201. <style scoped lang="scss">
  202. </style>