address.vue 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. <template>
  2. <view class="container">
  3. <scroll-view
  4. scroll-y
  5. class="address-list"
  6. refresher-enabled
  7. :refresher-triggered="refreshing"
  8. @refresherrefresh="onRefresh"
  9. >
  10. <view v-if="loading && addressList.length === 0" class="loading-wrap">
  11. <text class="loading-text">加载中...</text>
  12. </view>
  13. <view v-else-if="addressList.length === 0" class="empty-wrap">
  14. <text class="empty-icon">📍</text>
  15. <text class="empty-text">暂无收货地址</text>
  16. </view>
  17. <view v-else>
  18. <view
  19. v-for="item in addressList"
  20. :key="item.id"
  21. class="address-card"
  22. @click="onSelect(item)"
  23. >
  24. <view class="card-top">
  25. <view class="name-phone">
  26. <text class="receiver-name">{{ item.receiverName }}</text>
  27. <text class="receiver-phone">{{ formatPhone(item.phone) }}</text>
  28. <text v-if="item.isDefault === 1" class="default-badge">默认</text>
  29. </view>
  30. </view>
  31. <text class="address-detail">
  32. {{ item.province }}{{ item.city }}{{ item.district }}{{ item.street }}
  33. </text>
  34. <view class="card-actions">
  35. <view class="set-default" v-if="item.isDefault !== 1">
  36. <text class="set-default-btn" @click.stop="onSetDefault(item.id)">设为默认</text>
  37. </view>
  38. <view class="action-right">
  39. <text class="action-btn edit-btn" @click.stop="onEdit(item.id)">✏️ 编辑</text>
  40. <text class="action-btn delete-btn" @click.stop="onDelete(item.id)">🗑 删除</text>
  41. </view>
  42. </view>
  43. </view>
  44. </view>
  45. </scroll-view>
  46. <view class="bottom-bar">
  47. <button class="add-btn" @click="onAdd">+ 新增地址</button>
  48. </view>
  49. </view>
  50. </template>
  51. <script>
  52. import config from '@/config.js'
  53. export default {
  54. data() {
  55. return {
  56. addressList: [],
  57. loading: false,
  58. refreshing: false,
  59. userId: null,
  60. selectMode: false
  61. }
  62. },
  63. onLoad(options) {
  64. this.userId = uni.getStorageSync('userId')
  65. if (options.selectMode) {
  66. this.selectMode = options.selectMode === '1'
  67. }
  68. this.loadAddressList()
  69. },
  70. onShow() {
  71. this.loadAddressList()
  72. },
  73. methods: {
  74. loadAddressList() {
  75. this.loading = true
  76. var that = this
  77. uni.request({
  78. url: config.api('/api/user/address/list'),
  79. method: 'POST',
  80. data: {},
  81. header: {
  82. 'Content-Type': 'application/json',
  83. 'Authorization': 'Bearer ' + uni.getStorageSync('token')
  84. },
  85. success: function(res) {
  86. that.loading = false
  87. that.refreshing = false
  88. if (res.data && res.data.code === 200) {
  89. that.addressList = res.data.data || []
  90. }
  91. },
  92. fail: function() {
  93. that.loading = false
  94. that.refreshing = false
  95. }
  96. })
  97. },
  98. onRefresh() {
  99. this.refreshing = true
  100. this.loadAddressList()
  101. },
  102. onSelect(item) {
  103. if (this.selectMode) {
  104. var pages = getCurrentPages()
  105. var prevPage = pages[pages.length - 2]
  106. if (prevPage) {
  107. prevPage.selectedAddress = item
  108. }
  109. uni.navigateBack()
  110. }
  111. },
  112. onEdit(id) {
  113. uni.navigateTo({ url: '/pages/shop/address-edit/address-edit?addressId=' + id })
  114. },
  115. onAdd() {
  116. uni.navigateTo({ url: '/pages/shop/address-edit/address-edit' })
  117. },
  118. onDelete(id) {
  119. var that = this
  120. uni.showModal({
  121. title: '确认删除',
  122. content: '确定删除该地址吗?',
  123. success: function(confirm) {
  124. if (confirm.confirm) {
  125. uni.request({
  126. url: config.api('/api/user/address/delete'),
  127. method: 'POST',
  128. data: { id: id },
  129. header: {
  130. 'Content-Type': 'application/json',
  131. 'Authorization': 'Bearer ' + uni.getStorageSync('token')
  132. },
  133. success: function(res) {
  134. if (res.data && res.data.code === 200) {
  135. uni.showToast({ title: '已删除', icon: 'success' })
  136. that.loadAddressList()
  137. } else {
  138. uni.showToast({ title: res.data.message || '删除失败', icon: 'none' })
  139. }
  140. }
  141. })
  142. }
  143. }
  144. })
  145. },
  146. onSetDefault(id) {
  147. var that = this
  148. uni.request({
  149. url: config.api('/api/user/address/setDefault'),
  150. method: 'POST',
  151. data: { id: id },
  152. header: {
  153. 'Content-Type': 'application/json',
  154. 'Authorization': 'Bearer ' + uni.getStorageSync('token')
  155. },
  156. success: function(res) {
  157. if (res.data && res.data.code === 200) {
  158. uni.showToast({ title: '已设为默认', icon: 'success' })
  159. that.loadAddressList()
  160. }
  161. }
  162. })
  163. },
  164. formatPhone(phone) {
  165. if (!phone) return ''
  166. if (phone.length === 11) {
  167. return phone.substr(0, 3) + '****' + phone.substr(7)
  168. }
  169. return phone
  170. }
  171. }
  172. }
  173. </script>
  174. <style scoped>
  175. .container {
  176. display: flex;
  177. flex-direction: column;
  178. height: 100vh;
  179. background: #f5f5f5;
  180. }
  181. .address-list {
  182. flex: 1;
  183. height: calc(100vh - 120rpx);
  184. }
  185. .loading-wrap,
  186. .empty-wrap {
  187. display: flex;
  188. flex-direction: column;
  189. align-items: center;
  190. padding-top: 200rpx;
  191. }
  192. .loading-text {
  193. font-size: 26rpx;
  194. color: #999;
  195. }
  196. .empty-icon {
  197. font-size: 100rpx;
  198. margin-bottom: 30rpx;
  199. }
  200. .empty-text {
  201. font-size: 28rpx;
  202. color: #999;
  203. }
  204. .address-card {
  205. background: #fff;
  206. margin: 20rpx;
  207. border-radius: 16rpx;
  208. padding: 24rpx;
  209. box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.06);
  210. }
  211. .card-top {
  212. margin-bottom: 10rpx;
  213. }
  214. .name-phone {
  215. display: flex;
  216. align-items: center;
  217. }
  218. .receiver-name {
  219. font-size: 30rpx;
  220. font-weight: bold;
  221. color: #333;
  222. margin-right: 20rpx;
  223. }
  224. .receiver-phone {
  225. font-size: 26rpx;
  226. color: #666;
  227. }
  228. .default-badge {
  229. font-size: 20rpx;
  230. color: #F97316;
  231. background: #fff7e6;
  232. padding: 2rpx 12rpx;
  233. border-radius: 20rpx;
  234. margin-left: 12rpx;
  235. }
  236. .address-detail {
  237. display: block;
  238. font-size: 26rpx;
  239. color: #999;
  240. line-height: 1.5;
  241. margin-bottom: 16rpx;
  242. }
  243. .card-actions {
  244. display: flex;
  245. justify-content: space-between;
  246. align-items: center;
  247. border-top: 1rpx solid #f0f0f0;
  248. padding-top: 16rpx;
  249. }
  250. .set-default-btn {
  251. font-size: 24rpx;
  252. color: #1890ff;
  253. padding: 4rpx 12rpx;
  254. }
  255. .action-right {
  256. display: flex;
  257. }
  258. .action-btn {
  259. font-size: 24rpx;
  260. padding: 4rpx 12rpx;
  261. margin-left: 12rpx;
  262. }
  263. .edit-btn {
  264. color: #666;
  265. }
  266. .delete-btn {
  267. color: #ff4d4f;
  268. }
  269. .bottom-bar {
  270. position: fixed;
  271. bottom: 0;
  272. left: 0;
  273. right: 0;
  274. height: 100rpx;
  275. background: #fff;
  276. border-top: 1rpx solid #eee;
  277. display: flex;
  278. align-items: center;
  279. justify-content: center;
  280. padding: 0 30rpx;
  281. z-index: 100;
  282. }
  283. .add-btn {
  284. width: 100%;
  285. height: 72rpx;
  286. line-height: 72rpx;
  287. background: #fff;
  288. color: #F97316;
  289. font-size: 28rpx;
  290. font-weight: bold;
  291. border: 2rpx solid #F97316;
  292. border-radius: 36rpx;
  293. text-align: center;
  294. }
  295. .add-btn::after {
  296. border: none;
  297. }
  298. </style>