address_list.vue 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. <template>
  2. <view class="address-page">
  3. <!-- 地址列表 -->
  4. <view class="address-list">
  5. <!-- 地址项组件 -->
  6. <address-item v-if="addressList.length > 0" v-for="(item, index) in addressList" :key="item.id"
  7. :address="item" :is-default="item.isDefault" @set-default="handleSetDefault(index)"
  8. @edit="handleEdit(index)" @delete="handleDelete(index)" @click="onSelectAddress(item)" />
  9. <!-- 空状态 -->
  10. <view class="empty-state" v-if="addressList.length === 0 && !loadState">
  11. <u-icon class="empty-icon" name="list" size="60" color="#ccc"></u-icon>
  12. <text class="empty-text">暂无数据</text>
  13. </view>
  14. <!-- 加载状态提示 -->
  15. <view class="load-more-status" v-if="loadState">
  16. <text>加载中...</text>
  17. </view>
  18. <view class="load-more-status" v-if="loadFinished && addressList.length !== 0">
  19. <text>没有更多数据了</text>
  20. </view>
  21. </view>
  22. <!-- 添加新地址按钮 -->
  23. <view class="add-btn-container">
  24. <button class="add-btn" @click="handleAddAddress">
  25. <text class="add-btn-text">添加新地址</text>
  26. </button>
  27. </view>
  28. </view>
  29. </template>
  30. <script setup>
  31. import {
  32. ref
  33. } from 'vue'
  34. import {
  35. onLoad,
  36. onShow,
  37. onPullDownRefresh,
  38. onReachBottom
  39. } from '@dcloudio/uni-app'
  40. import AddressItem from '@/components/AddressItem.vue'
  41. import {
  42. listBook,
  43. delBook,
  44. updateBook
  45. } from '../../api/address'
  46. const pageNum = ref(1)
  47. const pageSize = ref(10)
  48. const recordTotal = ref(0)
  49. const loadState = ref(false)
  50. const loadFinished = ref(false)
  51. // 模拟地址数据
  52. const addressList = ref([])
  53. const pages = ref(0) // 总页数
  54. // 当前要操作的地址索引
  55. const currentIndex = ref(-1)
  56. const addType = ref('')
  57. onLoad((option) => {
  58. addType.value = option.addType
  59. })
  60. onShow(() => {
  61. pageNum.value = 1
  62. getAddressList()
  63. })
  64. // 下拉刷新
  65. onPullDownRefresh(() => {
  66. pageNum.value = 1
  67. getAddressList()
  68. })
  69. // 触底加载更多
  70. onReachBottom(() => {
  71. if (pageNum.value < pages.value) {
  72. pageNum.value++;
  73. getAddressList()
  74. }
  75. })
  76. const onSelectAddress = (address) => {
  77. console.log('-------address---', address)
  78. if (!addType.value) {
  79. return
  80. }
  81. uni.$emit('addressSelected', {
  82. type: addType.value, // 'sender' 或 'receiver'
  83. address // 选中的地址对象
  84. })
  85. uni.navigateBack() // 返回上一页
  86. }
  87. // 设置默认地址
  88. const handleSetDefault = async (index) => {
  89. const currentAddress = addressList.value[index];
  90. // 如果已经是默认地址,则无需操作
  91. if (currentAddress.defaultFlag === 1) {
  92. uni.showToast({
  93. title: '已是默认地址',
  94. icon: 'none'
  95. });
  96. return;
  97. }
  98. uni.showLoading({
  99. title: '设置中...',
  100. mask: true
  101. });
  102. try {
  103. // 调用接口设置为默认地址(后端应自动处理其他地址的非默认状态)
  104. const res = await updateBook({
  105. ...currentAddress,
  106. defaultFlag: 1
  107. });
  108. if (res.code === 200) {
  109. // 更新本地数据:将所有地址的 defaultFlag 设为 0,再将当前项设为 1
  110. addressList.value.forEach(item => item.defaultFlag = 0);
  111. currentAddress.defaultFlag = 1;
  112. uni.showToast({
  113. title: '设置成功',
  114. icon: 'success'
  115. });
  116. } else {
  117. uni.showToast({
  118. title: res.msg || '设置失败',
  119. icon: 'none'
  120. });
  121. }
  122. } catch (error) {
  123. uni.showToast({
  124. title: '网络错误,请重试',
  125. icon: 'none'
  126. });
  127. console.error('设置默认地址失败', error);
  128. } finally {
  129. uni.hideLoading();
  130. }
  131. }
  132. // 编辑地址
  133. const handleEdit = (index) => {
  134. // 跳转到编辑页面
  135. uni.navigateTo({
  136. url: '/pages/address/edit?id=' + addressList.value[index].addressId
  137. })
  138. }
  139. // 删除地址
  140. const handleDelete = (index) => {
  141. currentIndex.value = index
  142. uni.showModal({
  143. title: '确认删除',
  144. content: '是否确认删除这个地址',
  145. success: async (res) => {
  146. if (res.confirm) {
  147. uni.showLoading({
  148. title: '删除中...',
  149. mask: true
  150. });
  151. try {
  152. const delRes = await delBook(addressList.value[index].addressId)
  153. if (delRes.code == 200) {
  154. // 移除本地数据
  155. addressList.value.splice(currentIndex.value, 1)
  156. currentIndex.value = -1
  157. // 如果删除的是默认地址,且列表不为空,可考虑自动将第一个地址设为默认(根据业务需求可选)
  158. // 此处不做自动处理,由用户手动设置
  159. uni.showToast({
  160. title: '删除成功',
  161. icon: 'success'
  162. })
  163. } else {
  164. uni.showToast({
  165. title: delRes.msg || '删除失败',
  166. icon: 'none'
  167. })
  168. }
  169. } catch (error) {
  170. uni.showToast({
  171. title: '网络错误',
  172. icon: 'none'
  173. })
  174. } finally {
  175. uni.hideLoading()
  176. }
  177. } else if (res.cancel) {
  178. console.log('用户点击取消');
  179. }
  180. }
  181. });
  182. }
  183. // 添加新地址
  184. const handleAddAddress = () => {
  185. uni.navigateTo({
  186. url: '/pages/address/edit'
  187. })
  188. }
  189. const loadMore = () => {
  190. pageNum.value++
  191. getAddressList(true)
  192. }
  193. // 获取地址列表
  194. const getAddressList = () => {
  195. const params = {
  196. pageNum: pageNum.value,
  197. pageSize: pageSize.value
  198. }
  199. uni.showLoading({
  200. mask: true
  201. })
  202. listBook(params).then(res => {
  203. uni.hideLoading()
  204. uni.stopPullDownRefresh()
  205. if (res.code === 200) {
  206. const list = res.rows || []
  207. addressList.value = pageNum.value == 1 ? list : [...addressList.value, ...list]
  208. pages.value = Math.ceil(res.total / pageSize.value) || 0
  209. }
  210. }, err => {
  211. uni.hideLoading()
  212. uni.stopPullDownRefresh()
  213. uni.showToast({
  214. title: '加载失败',
  215. icon: 'none'
  216. })
  217. })
  218. }
  219. </script>
  220. <style scoped>
  221. .address-page {
  222. display: flex;
  223. flex-direction: column;
  224. height: 100vh;
  225. background-color: #F5F7FA;
  226. }
  227. .address-list {
  228. padding: 20rpx 30rpx 152rpx;
  229. box-sizing: border-box;
  230. }
  231. .load-more-status {
  232. text-align: center;
  233. padding: 20rpx;
  234. font-size: 24rpx;
  235. color: #999;
  236. }
  237. .empty-state {
  238. display: flex;
  239. flex-direction: column;
  240. align-items: center;
  241. justify-content: center;
  242. padding: 120rpx 40rpx;
  243. color: #999999;
  244. }
  245. .empty-text {
  246. margin-top: 20rpx;
  247. font-size: 28rpx;
  248. }
  249. .add-btn-container {
  250. width: 100%;
  251. position: fixed;
  252. bottom: 0rpx;
  253. padding: 32rpx;
  254. background-color: #fff;
  255. border-top: 1rpx solid #eee;
  256. box-sizing: border-box;
  257. }
  258. .add-btn {
  259. height: 88rpx;
  260. background: #1B64F0;
  261. border-radius: 44rpx;
  262. border: none;
  263. }
  264. .add-btn-text {
  265. color: #fff;
  266. font-size: 32rpx;
  267. font-weight: 500;
  268. height: 88rpx;
  269. line-height: 88rpx;
  270. }
  271. /* 按钮激活效果 */
  272. .add-btn:active {
  273. opacity: 0.8;
  274. }
  275. </style>