addressBook.vue 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. <route lang="json5" type="page">
  2. {
  3. layout: 'default',
  4. style: {
  5. navigationBarTitleText: '%addressBook.title%',
  6. navigationBarBackgroundColor: '#fff',
  7. },
  8. }
  9. </route>
  10. <script lang="ts" setup>
  11. // 必须导入需要用到的页面生命周期(即使在当前页面上没有直接使用到)
  12. // eslint-disable-next-line unused-imports/no-unused-imports
  13. import { onPageScroll, onReachBottom } from '@dcloudio/uni-app'
  14. import useZPaging from 'z-paging/components/z-paging/js/hooks/useZPaging.js'
  15. import { addressDel, addressList } from '@/api/mine'
  16. import { bindingAddress } from '@/api/order'
  17. import { t } from '@/locale'
  18. import { getPageParams, goBack, toPage } from '@/utils/page'
  19. import { toast } from '@/utils/toast'
  20. defineOptions({
  21. name: 'AddressBook', // 地址簿
  22. })
  23. // 页面参数
  24. const pageParams = ref<any>({})
  25. const isSelectMode = computed(() => pageParams.value.selectMode === true)
  26. // z-paging
  27. const paging = ref(null)
  28. // 类似mixins,如果是页面滚动务必要写这一行,并传入当前ref绑定的paging,注意此处是paging,而非paging.value
  29. useZPaging(paging)
  30. // 搜索结果
  31. const dataList = ref([])
  32. async function queryList(pageNo: number, pageSize: number) {
  33. try {
  34. const res = await addressList({
  35. page: pageNo,
  36. size: pageSize,
  37. })
  38. paging.value.complete(res.data.list)
  39. }
  40. catch {
  41. paging.value.complete(false)
  42. }
  43. }
  44. // 处理地址项点击
  45. function handleAddressClick(item: any) {
  46. if (isSelectMode.value) {
  47. // 选择模式:选择地址并绑定到订单
  48. selectAddressForOrder(item)
  49. }
  50. else {
  51. // 普通模式:编辑地址
  52. editAddress(item.id)
  53. }
  54. }
  55. // 编辑地址
  56. function editAddress(id: any) {
  57. toPage('/pages/mine/addressBookOperate', { id })
  58. }
  59. // 选择地址并绑定到订单
  60. async function selectAddressForOrder(address: any) {
  61. try {
  62. uni.showLoading({
  63. title: t('addressBook.select.binding'),
  64. })
  65. const res = await bindingAddress({
  66. orderId: pageParams.value.orderId,
  67. addressId: address.id,
  68. })
  69. if (res.code === '200') {
  70. toast.success(t('addressBook.select.success'))
  71. // 返回上一页
  72. goBack()
  73. }
  74. else {
  75. toast.error(res.message || t('addressBook.select.failed'))
  76. }
  77. }
  78. catch (error: any) {
  79. console.error('Bind address error:', error)
  80. toast.error(t('addressBook.select.networkError'))
  81. }
  82. finally {
  83. uni.hideLoading()
  84. }
  85. }
  86. // 删除地址
  87. async function deleteAddress(id: any) {
  88. try {
  89. await uni.showLoading({
  90. title: t('addressBook.delete.deleting'),
  91. })
  92. const res = await addressDel({ id })
  93. if (res.code === '200') {
  94. toast.success(t('addressBook.delete.success'))
  95. // 刷新列表
  96. paging.value.reload()
  97. }
  98. }
  99. catch (error: any) {
  100. console.error('Delete address error:', error)
  101. }
  102. finally {
  103. uni.hideLoading()
  104. }
  105. }
  106. // 处理滑动操作
  107. function handleAction(action: string, item: any) {
  108. if (action === 'del') {
  109. uni.showModal({
  110. title: t('addressBook.delete.confirm'),
  111. content: t('addressBook.delete.message'),
  112. success: (res) => {
  113. if (res.confirm) {
  114. deleteAddress(item.id)
  115. }
  116. },
  117. })
  118. }
  119. }
  120. onShow(() => {
  121. paging.value.reload(true)
  122. })
  123. // 页面加载时获取参数
  124. onLoad((options) => {
  125. pageParams.value = getPageParams(options)
  126. })
  127. </script>
  128. <template>
  129. <z-paging ref="paging" v-model="dataList" use-page-scroll @query="queryList">
  130. <view class="py-20rpx">
  131. <wd-swipe-action v-for="item in dataList" :key="item.id">
  132. <view class="flex items-center justify-between bg-white px-22rpx py-18rpx" @click="handleAddressClick(item)">
  133. <view class="flex-1">
  134. <view class="mb-20rpx flex items-center justify-between text-24rpx">
  135. <view>
  136. <text class="mr-20rpx">
  137. {{ item.realName }}
  138. </text>
  139. <text>{{ item.phone }}</text>
  140. </view>
  141. <wd-text v-if="item.isDefault" type="primary" :text="t('addressBook.tag.default')" />
  142. </view>
  143. <view class="text-22rpx text-#3A444C">
  144. {{ [item.province, item.city, item.district, item.street].filter(Boolean).join(', ') }} {{ item.postCode }}
  145. </view>
  146. </view>
  147. <wd-icon name="arrow-right" custom-class="flex-shrink-0 ml-8rpx" color="#7D7D7D" size="24rpx" />
  148. </view>
  149. <template #right>
  150. <view class="action">
  151. <view class="button" style="background:var(--wot-color-theme);" @click="handleAction('del', item)">
  152. {{ $t('addressBook.delete.button') }}
  153. </view>
  154. </view>
  155. </template>
  156. </wd-swipe-action>
  157. </view>
  158. <template #bottom>
  159. <view class="bg-white/60 px-28rpx py-30rpx backdrop-blur-20">
  160. <wd-button plain block @click="toPage('/pages/mine/addressBookOperate')">
  161. {{ $t('addressBook.button.add') }}
  162. </wd-button>
  163. </view>
  164. </template>
  165. </z-paging>
  166. </template>
  167. <style lang="scss" scoped>
  168. .action {
  169. height: 100%;
  170. .button {
  171. display: flex;
  172. align-items: center;
  173. padding: 0 24rpx;
  174. height: 100%;
  175. color: white;
  176. }
  177. }
  178. </style>