巴青农资商城

index.vue 8.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. <template>
  2. <view class="mine-page">
  3. <view class="mine-header" @click="onHeaderClick">
  4. <view class="mine-header__row">
  5. <image class="mine-header__avatar" :src="avatarUrl" mode="aspectFill" />
  6. <view class="mine-header__info">
  7. <text class="mine-header__name">{{ headerTitle }}</text>
  8. <text v-if="loggedIn && mobileText" class="mine-header__sub">{{ mobileText }}</text>
  9. <text v-else-if="!loggedIn" class="mine-header__sub">登录后管理资料与地址</text>
  10. </view>
  11. <u-icon v-if="loggedIn" name="arrow-right" color="#fff" size="18" class="mine-header__arrow" />
  12. </view>
  13. </view>
  14. <scroll-view class="mine-scroll" scroll-y :style="{ height: scrollHeight }">
  15. <view class="mine-body">
  16. <view v-if="!loggedIn" class="mine-guest">
  17. <text class="mine-guest__title">登录后享受完整购物服务</text>
  18. <text class="mine-guest__tip">下单、加购需登录会员账号</text>
  19. <view class="mine-guest__btns">
  20. <button class="mine-btn-primary" @click="goLogin">登录</button>
  21. <button class="mine-btn-outline" @click="goRegister">注册会员</button>
  22. </view>
  23. </view>
  24. <template v-else>
  25. <view class="mine-order-card">
  26. <view class="mine-order-head" @click="goOrderListAll">
  27. <text class="mine-order-head__title">我的订单</text>
  28. <view class="mine-order-head__all">
  29. <text>全部</text>
  30. <u-icon name="arrow-right" color="#ccc" size="14" />
  31. </view>
  32. </view>
  33. <view class="mine-order-shortcuts">
  34. <view
  35. v-for="item in orderShortcuts"
  36. :key="item.key"
  37. class="mine-order-shortcut"
  38. @click="onOrderShortcut(item.key)"
  39. >
  40. <view class="mine-order-shortcut__icon-wrap">
  41. <u-icon :name="item.icon" color="#2e7d32" size="26" />
  42. <text
  43. v-if="getOrderBadge(item.key) > 0"
  44. class="mine-order-shortcut__badge"
  45. >{{ formatBadge(getOrderBadge(item.key)) }}</text>
  46. </view>
  47. <text class="mine-order-shortcut__label">{{ item.label }}</text>
  48. </view>
  49. </view>
  50. </view>
  51. <view v-for="section in menuSections" :key="section.title" class="mine-card">
  52. <text class="mine-card__title">{{ section.title }}</text>
  53. <view
  54. v-for="item in section.items"
  55. :key="item.path"
  56. class="mine-menu-item"
  57. @click="goPage(item.path)"
  58. >
  59. <u-icon :name="item.icon" color="#5c5652" size="22" />
  60. <text class="mine-menu-item__label">{{ item.label }}</text>
  61. <text
  62. v-if="item.showUnreadBadge && messageUnreadCount > 0"
  63. class="mine-menu-badge"
  64. >{{ formatMessageBadge(messageUnreadCount) }}</text>
  65. <u-icon name="arrow-right" color="#ccc" size="16" />
  66. </view>
  67. </view>
  68. <view class="mine-logout">
  69. <button class="mine-btn-outline" @click="handleLogout">退出登录</button>
  70. </view>
  71. </template>
  72. </view>
  73. </scroll-view>
  74. </view>
  75. </template>
  76. <script setup>
  77. import { ref, computed, onMounted } from 'vue'
  78. import { onShow } from '@dcloudio/uni-app'
  79. import { getToken } from '@/utils/auth'
  80. import { useUserStore } from '@/store/user'
  81. import { navigateMinePage } from '@/utils/mineNav'
  82. import { getOrderBadges } from '@/api/order'
  83. import { getMessageUnreadCount } from '@/api/message'
  84. import { formatMessageBadge } from '@/utils/messageDisplay'
  85. import { ORDER_MINE_SHORTCUTS, ORDER_TAB } from '@/constants/order'
  86. import { goOrderList, goAftersaleList } from '@/utils/orderNav'
  87. import {
  88. PAGE_LOGIN,
  89. PAGE_REGISTER,
  90. PAGE_PROFILE,
  91. PAGE_PASSWORD,
  92. PAGE_ADDRESS_LIST,
  93. PAGE_ENTRY_APPLY,
  94. PAGE_ENTRY_LIST,
  95. PAGE_SHOP_FOLLOW_LIST,
  96. PAGE_MESSAGE_LIST,
  97. PAGE_MESSAGE_DETAIL,
  98. PAGE_ABOUT_MALL
  99. } from '@/utils/pageRoute'
  100. const loggedIn = ref(false)
  101. const displayName = ref('点击登录')
  102. const mobileText = ref('')
  103. const avatarUrl = ref('/static/logo.png')
  104. const userStore = useUserStore()
  105. const orderShortcuts = ORDER_MINE_SHORTCUTS
  106. const orderBadges = ref({
  107. pendingPayCount: 0,
  108. pendingShipCount: 0,
  109. pendingReceiveCount: 0,
  110. aftersaleInProgressCount: 0
  111. })
  112. const messageUnreadCount = ref(0)
  113. const scrollHeight = ref('500px')
  114. const headerTitle = computed(() => {
  115. if (!loggedIn.value) return '未登录'
  116. return displayName.value
  117. })
  118. /** 我的服务菜单(对齐功能需求 §3) */
  119. const menuSections = [
  120. {
  121. title: '消息中心',
  122. items: [
  123. {
  124. label: '站内消息',
  125. path: PAGE_MESSAGE_LIST,
  126. icon: 'bell',
  127. showUnreadBadge: true
  128. }
  129. ]
  130. },
  131. {
  132. title: '账号管理',
  133. items: [
  134. { label: '编辑个人资料', path: PAGE_PROFILE, icon: 'account' },
  135. { label: '修改密码', path: PAGE_PASSWORD, icon: 'lock' }
  136. ]
  137. },
  138. {
  139. title: '收货地址',
  140. items: [{ label: '收货地址', path: PAGE_ADDRESS_LIST, icon: 'map' }]
  141. },
  142. {
  143. title: '店铺关注',
  144. items: [{ label: '我的店铺关注', path: PAGE_SHOP_FOLLOW_LIST, icon: 'heart' }]
  145. },
  146. {
  147. title: '商家入驻',
  148. items: [
  149. { label: '我要入驻', path: PAGE_ENTRY_APPLY, icon: 'home' },
  150. { label: '我的入驻申请', path: PAGE_ENTRY_LIST, icon: 'list' }
  151. ]
  152. },
  153. {
  154. title: '关于',
  155. items: [{ label: '关于商城', path: PAGE_ABOUT_MALL, icon: 'info-circle' }]
  156. }
  157. ]
  158. function calcScrollHeight() {
  159. try {
  160. const sys = uni.getSystemInfoSync()
  161. const windowH = sys.windowHeight || 600
  162. const tabBarH = uni.upx2px(188)
  163. const headerH = uni.upx2px(232)
  164. const overlapH = uni.upx2px(40)
  165. scrollHeight.value = `${Math.max(windowH - tabBarH - headerH + overlapH, 200)}px`
  166. } catch (e) {
  167. scrollHeight.value = '400px'
  168. }
  169. }
  170. onMounted(() => {
  171. calcScrollHeight()
  172. })
  173. onShow(() => {
  174. calcScrollHeight()
  175. loggedIn.value = !!getToken()
  176. if (!loggedIn.value) {
  177. displayName.value = '点击登录'
  178. mobileText.value = ''
  179. avatarUrl.value = '/static/logo.png'
  180. return
  181. }
  182. refreshUser()
  183. loadOrderBadges()
  184. loadMessageUnread()
  185. })
  186. function loadMessageUnread() {
  187. getMessageUnreadCount()
  188. .then((res) => {
  189. messageUnreadCount.value = Number((res.data && res.data.unreadCount) || 0)
  190. })
  191. .catch(() => {
  192. messageUnreadCount.value = 0
  193. })
  194. }
  195. function loadOrderBadges() {
  196. getOrderBadges()
  197. .then((res) => {
  198. const data = res.data || {}
  199. orderBadges.value = {
  200. pendingPayCount: Number(data.pendingPayCount) || 0,
  201. pendingShipCount: Number(data.pendingShipCount) || 0,
  202. pendingReceiveCount: Number(data.pendingReceiveCount) || 0,
  203. aftersaleInProgressCount: Number(data.aftersaleInProgressCount) || 0
  204. }
  205. })
  206. .catch(() => {
  207. orderBadges.value = {
  208. pendingPayCount: 0,
  209. pendingShipCount: 0,
  210. pendingReceiveCount: 0,
  211. aftersaleInProgressCount: 0
  212. }
  213. })
  214. }
  215. function getOrderBadge(key) {
  216. const map = {
  217. [ORDER_TAB.PENDING_PAY]: orderBadges.value.pendingPayCount,
  218. [ORDER_TAB.PENDING_SHIP]: orderBadges.value.pendingShipCount,
  219. [ORDER_TAB.PENDING_RECEIVE]: orderBadges.value.pendingReceiveCount,
  220. AFTERSALE: orderBadges.value.aftersaleInProgressCount
  221. }
  222. return map[key] || 0
  223. }
  224. function formatBadge(n) {
  225. return n > 99 ? '99+' : String(n)
  226. }
  227. function goOrderListAll() {
  228. goOrderList(ORDER_TAB.ALL)
  229. }
  230. function onOrderShortcut(key) {
  231. if (key === 'AFTERSALE') {
  232. goAftersaleList('IN_PROGRESS')
  233. return
  234. }
  235. goOrderList(key)
  236. }
  237. function refreshUser() {
  238. displayName.value = userStore.displayName() || '会员'
  239. mobileText.value = userStore.state.mobile || ''
  240. avatarUrl.value = userStore.state.avatar || '/static/logo.png'
  241. if (!userStore.state.memberId) {
  242. userStore.fetchUserInfo().then(() => {
  243. displayName.value = userStore.displayName() || '会员'
  244. mobileText.value = userStore.state.mobile || ''
  245. avatarUrl.value = userStore.state.avatar || '/static/logo.png'
  246. })
  247. }
  248. }
  249. function onHeaderClick() {
  250. if (!loggedIn.value) {
  251. goLogin()
  252. return
  253. }
  254. navigateMinePage(PAGE_PROFILE)
  255. }
  256. function goPage(path) {
  257. navigateMinePage(path)
  258. }
  259. function goLogin() {
  260. uni.navigateTo({ url: PAGE_LOGIN })
  261. }
  262. function goRegister() {
  263. uni.navigateTo({ url: PAGE_REGISTER })
  264. }
  265. function handleLogout() {
  266. uni.showModal({
  267. title: '提示',
  268. content: '确定退出当前账号吗?',
  269. success: (res) => {
  270. if (res.confirm) {
  271. userStore.logOut().then(() => {
  272. loggedIn.value = false
  273. displayName.value = '点击登录'
  274. mobileText.value = ''
  275. uni.showToast({ title: '已退出', icon: 'none' })
  276. })
  277. }
  278. }
  279. })
  280. }
  281. </script>
  282. <style lang="scss" scoped>
  283. @import '@/styles/mine.scss';
  284. .mine-menu-item u-icon:first-child {
  285. margin-right: 16rpx;
  286. }
  287. .mine-menu-item {
  288. gap: 16rpx;
  289. }
  290. </style>