index.vue 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  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. <Loading :visible="loading" />
  51. </template>
  52. <script setup>
  53. defineOptions({ name: 'personal-personCenter-address-index' })
  54. import { ref } from 'vue'
  55. import { getMallUserAddressList, deleteMallUserAddress, createMallUserAddress, updateMallUserAddress } from '@/api/mall/address'
  56. import Confirm from '@/plugins/confirm'
  57. import { getDict } from '@/hooks/web/useDictionaries'
  58. import Snackbar from '@/plugins/snackbar'
  59. const props = defineProps({
  60. showSelect: {
  61. type: Boolean,
  62. default: false,
  63. }
  64. })
  65. const isAdd = ref(true)
  66. const editId = ref(null)
  67. const showDialog = ref(false)
  68. const items = ref([])
  69. const headers = [
  70. { title: '收货人', key: 'name', sortable: false },
  71. { title: '联系电话', key: 'mobile', sortable: false },
  72. { title: '所在地区', key: 'areaName', sortable: false },
  73. { title: '详细地址', key: 'detailAddress', sortable: false },
  74. { title: '是否默认', key: 'defaultStatus', sortable: false, value: item => item.defaultStatus ? '是' : '否' },
  75. { title: '操作', key: 'actions', sortable: false }
  76. ]
  77. const cascaderAddr = ref()
  78. const CtFormRef = ref()
  79. const formItems = ref({
  80. options: [
  81. {
  82. type: 'text',
  83. key: 'name',
  84. value: null,
  85. default: localStorage.getItem('baseInfo') ? JSON.parse(localStorage.getItem('baseInfo')).name : '',
  86. hide: false,
  87. label: '收货人姓名 *',
  88. rules: [v => !!v || '请输入收货人姓名'],
  89. },
  90. {
  91. type: 'text',
  92. key: 'mobile',
  93. value: null,
  94. default: localStorage.getItem('userInfo') ? JSON.parse(localStorage.getItem('userInfo')).phone : '',
  95. label: '收货人联系电话 *',
  96. outlined: true,
  97. rules: [v => !!v || '请填写收货人联系电话']
  98. },
  99. {
  100. slotName: 'areaId',
  101. key: 'areaId',
  102. labelValue: '',
  103. value: null,
  104. default: null,
  105. items: []
  106. },
  107. {
  108. type: 'textarea',
  109. key: 'detailAddress',
  110. value: '',
  111. default: null,
  112. label: '详细地址 *',
  113. rules: [ v => !!v || '请填写详细地址' ]
  114. },
  115. {
  116. slotName: 'defaultStatus',
  117. key: 'defaultStatus',
  118. value: false,
  119. default: false,
  120. }
  121. ]
  122. })
  123. getDict('areaTreeData', null, 'areaTreeData').then(({ data }) => {
  124. data = data?.length && data || []
  125. formItems.value.options.find(e => e.key === 'areaId').items = data
  126. })
  127. const singleSelectObj = ref([])
  128. const handleSelected = (e) => {
  129. singleSelectObj.value = e?.length ? items.value.find(item => item.id === e[0]) : null
  130. }
  131. const getSelected = () => {
  132. return singleSelectObj.value
  133. }
  134. // 获取地址列表
  135. const getAddressList = async () => {
  136. const data = await getMallUserAddressList()
  137. items.value = data
  138. }
  139. getAddressList()
  140. // 新增
  141. const handleAdd = () => {
  142. formItems.value.options.forEach(e => e.value = e.default)
  143. showDialog.value = true
  144. isAdd.value = true
  145. }
  146. // 地区选择
  147. const handleChangeArea = (item) => {
  148. const node = cascaderAddr.value.getCheckedNodes() ? cascaderAddr.value.getCheckedNodes()[0] : null
  149. if (!node) return
  150. item.labelValue = node.pathLabels.join(' ')
  151. }
  152. // 设置为默认地址
  153. const handleSetDefault = async (item) => {
  154. Confirm('系统提示', '是否确定设置该地址为默认收货地址?').then(async () => {
  155. await updateMallUserAddress({ ...item, defaultStatus: true })
  156. Snackbar.success('设置成功')
  157. await getAddressList()
  158. })
  159. }
  160. // 编辑
  161. const handleEdit = (item) => {
  162. isAdd.value = false
  163. editId.value = item.id
  164. formItems.value.options.forEach(e => e.value = item[e.key])
  165. showDialog.value = true
  166. }
  167. // 提交
  168. const handleClose = () => {
  169. showDialog.value = false
  170. editId.value = null
  171. }
  172. const loading = ref(false)
  173. const handleSubmit = async () => {
  174. const { valid } = await CtFormRef.value.formRef.validate()
  175. if (!valid) return
  176. const obj = {}
  177. formItems.value.options.forEach(e => {
  178. if (e.key === 'areaId') {
  179. obj.areaId = e.value
  180. obj.areaName = e.labelValue
  181. } else obj[e.key] = e.value
  182. })
  183. if (!obj.areaId && !obj.areaName) return Snackbar.warning('请将必填项填写完整')
  184. loading.value = true
  185. try {
  186. isAdd.value ? await createMallUserAddress(obj) : await updateMallUserAddress(obj)
  187. Snackbar.success(isAdd.value ? '新增成功' : '编辑成功')
  188. handleClose()
  189. getAddressList()
  190. } finally {
  191. loading.value = false
  192. }
  193. }
  194. // 删除
  195. const handleDelete = (item) => {
  196. Confirm('系统提示', '是否确认删除该收货地址?').then(async () => {
  197. await deleteMallUserAddress(item.id)
  198. Snackbar.success('删除成功')
  199. await getAddressList()
  200. })
  201. }
  202. defineExpose({
  203. getSelected
  204. })
  205. </script>
  206. <style scoped lang="scss">
  207. </style>