index.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. <script setup lang="ts">
  2. import chargeSearch from '../components/search.vue'
  3. import chargeFooter from '../components/charge-tab.vue'
  4. import router from '@/router'
  5. import { StaticUrl } from '@/config'
  6. import { createGlobalLoadingMiddleware } from '@/api/core/middleware'
  7. const { statusBarHeight, MenuButtonHeight, opcity } = storeToRefs(useSysStore())
  8. const { token, userInfo } = storeToRefs(useUserStore())
  9. const { Location } = storeToRefs(useAddressStore())
  10. definePage({
  11. name: 'charge-index',
  12. islogin: false,
  13. style: {
  14. navigationStyle: 'custom',
  15. navigationBarTitleText: '',
  16. backgroundColorBottom: '#fff',
  17. },
  18. })
  19. const activeFilter = ref<number>(1)
  20. const loading = ref(true)
  21. const filterOptions = [
  22. { key: 1, label: '离我最近', widthClass: 'w-152rpx' },
  23. { key: 2, label: '空闲最多', widthClass: '' },
  24. { key: 3, label: '电费最低', widthClass: '' },
  25. ]
  26. /**
  27. * 获取充电站列表
  28. */
  29. const { data: stationList, isLastPage, page, refresh, reload } = usePagination((pageNum, pageSize) =>
  30. Apis.charge.getStationInfoPage({ data: { pageNum, pageSize, sortType: activeFilter.value, longitude: Location.value.longitude || 106.620256, latitude: Location.value.latitude || 26.648327, userId: userInfo.value.id } }), {
  31. data: (resp) => {
  32. return resp.data?.list
  33. },
  34. initialData: [],
  35. initialPage: 1,
  36. initialPageSize: 10,
  37. append: true,
  38. immediate: true,
  39. middleware: createGlobalLoadingMiddleware(),
  40. }).onSuccess(() => {
  41. loading.value = false
  42. })
  43. onMounted(() => {
  44. getUserAccountInfo()
  45. opcity.value = 0
  46. })
  47. onShow(() => {
  48. refresh()
  49. getUserAccountInfo()
  50. getDefaultVehicle()
  51. queryVehicleList()
  52. })
  53. onPageScroll((e) => {
  54. const calculatedOpacity = e.scrollTop / 100
  55. opcity.value = Math.min(1, Math.max(0.1, calculatedOpacity))
  56. })
  57. onReachBottom(() => {
  58. if (!isLastPage.value) {
  59. page.value++
  60. }
  61. })
  62. /**
  63. * 获取用户账户信息
  64. */
  65. const userAccountInfo = ref()
  66. async function getUserAccountInfo() {
  67. const res = await Apis.charge.getMemberInfo({})
  68. userAccountInfo.value = res.data
  69. }
  70. /**
  71. * 获取用户默认车辆
  72. */
  73. const defaultVehicle = ref<Api.UserVehicleVO>()
  74. async function getDefaultVehicle() {
  75. const res = await Apis.charge.default({})
  76. defaultVehicle.value = res.data
  77. }
  78. // 获取车辆列表判断是否提示用户添加车辆
  79. async function queryVehicleList() {
  80. const res = await Apis.charge.vehicleList({})
  81. if (res.data.length === 0) {
  82. useGlobalMessage().confirm({
  83. title: '充电车主专享',
  84. msg: '绑定车牌可享充电停车费减免及更多权益',
  85. success: () => {
  86. router.push({ name: 'charge-plate-list' })
  87. },
  88. })
  89. }
  90. }
  91. // 处理筛选项点击的方法
  92. function handleFilterClick(filterKey: number) {
  93. activeFilter.value = filterKey
  94. reload()
  95. }
  96. /**
  97. * 申请退款
  98. */
  99. function refund() {
  100. useGlobalMessage().confirm({
  101. title: '提示',
  102. msg: '退款按照购券记录进行逐笔退款,可能产生多笔退款到账记录,请注意查收。',
  103. success: async () => {
  104. const res = await Apis.charge.userCouponRefund({})
  105. if (res.code === '00000') {
  106. useGlobalMessage().confirm({
  107. title: '提示',
  108. msg: '申请退款成功,预计3个工作日内分一笔或多笔退还,到期如未到账请联系客服!',
  109. success: () => {
  110. refresh()
  111. },
  112. fail: () => {
  113. refresh()
  114. },
  115. })
  116. }
  117. },
  118. })
  119. }
  120. </script>
  121. <template>
  122. <view class="min-h-screen from-[#E2FF91] to-[rgba(158,214,5,0)] bg-gradient-to-b">
  123. <wd-navbar
  124. title="" :custom-style="`background-color: rgba(226, 255, 145, ${opcity})`" :bordered="false"
  125. :z-index="99" safe-area-inset-top left-arrow fixed @click-left="router.back()"
  126. >
  127. <template #left>
  128. <view class="flex items-center">
  129. <wd-icon name="arrow-left" size="22px" color="#000" />
  130. <view class="relative z-10 h62rpx w-full w180rpx opacity-100">
  131. <image class="absolute left-0 top-0 h62rpx w180rpx" :src="`${StaticUrl}/charge-logo.png`" />
  132. </view>
  133. </view>
  134. </template>
  135. </wd-navbar>
  136. <view :style="{ paddingTop: `${(Number(statusBarHeight) || 44) + MenuButtonHeight + 12}px` }" class="px24rpx">
  137. <charge-search map-mode-text-value="地图模式" @map-mode-click="router.replace({ name: 'charge-map' })" />
  138. <view class="userInfo-card">
  139. <view class="flex items-center gap-20rpx">
  140. <image
  141. class="h-100rpx w-100rpx"
  142. :src="`${userInfo && userInfo?.avatarUrl ? userInfo.avatarUrl : `${StaticUrl}/charge-default-avatar.png`}`"
  143. />
  144. <view>
  145. <view class="flex items-center gap-16rpx">
  146. <view class="text-32rpx text-#2B303A font-bold">
  147. {{ userInfo?.nickName || '暂未登录' }}
  148. </view>
  149. <view v-if="!token" class="text-24rpx text-#9ED605" @click="router.replace({ name: 'smqjh-login' })">
  150. 授权登录
  151. </view>
  152. <view v-else class="rounded-8rpx bg-#E9FFAC px12rpx py4rpx text-24rpx text-#9ED605">
  153. {{ userInfo.channelName }}
  154. </view>
  155. </view>
  156. <view class="mt-16rpx text-28rpx font-bold">
  157. {{ userInfo?.mobile }}
  158. </view>
  159. </view>
  160. </view>
  161. <view class="mt-20rpx rounded-16rpx bg-#F3FFD1 px-24rpx py-30rpx">
  162. <view class="relative flex items-center justify-between">
  163. <view class="flex items-center gap-16rpx">
  164. <view class="h-40rpx w-40rpx">
  165. <image
  166. class="h-40rpx w-40rpx"
  167. :src="`${StaticUrl}/charge-mine-wallet.png`"
  168. mode="scaleToFill"
  169. />
  170. </view>
  171. <view class="text-32rpx font-bold">
  172. 我的钱包
  173. </view>
  174. </view>
  175. <view class="absolute right-[-24rpx] h-44rpx w-120rpx rounded-[22rpx_0_0_22rpx] bg-#9ED605 text-center text-28rpx text-#FFF line-height-[44rpx]" @click="refund">
  176. 退还
  177. </view>
  178. </view>
  179. <view class="mt-24rpx flex items-center justify-around">
  180. <view class="text-center">
  181. <view class="text-28rpx">
  182. 企业积分
  183. </view>
  184. <view class="mt-16rpx text-40rpx text-#9ED605 font-bold">
  185. {{ userAccountInfo?.availablePoints || '0.00' }}
  186. </view>
  187. </view>
  188. <view class="h-92rpx w-2rpx bg-#9ED605" />
  189. <view class="text-center">
  190. <view class="text-28rpx">
  191. 平台券
  192. </view>
  193. <view class="mt-16rpx text-40rpx text-#9ED605 font-bold">
  194. <text class="text-24rpx">
  195. </text>{{ userAccountInfo?.couponBalance || '0.00' }}
  196. </view>
  197. </view>
  198. </view>
  199. </view>
  200. </view>
  201. <view class="mt-24rpx flex justify-between">
  202. <view class="flex items-center gap-18rpx rounded-16rpx bg-#FFF px-24rpx py-28rpx">
  203. <view>
  204. <view class="flex items-center gap-16rpx" @click="router.push({ name: 'charge-order-list' })">
  205. <text class="text-32rpx font-bold">
  206. 充电订单
  207. </text>
  208. <view class="h-30rpx w-30rpx rounded-50% bg-#000 text-center line-height-[30rpx]">
  209. <wd-icon name="arrow-right" color="#FFFFFF" size="30rpx" />
  210. </view>
  211. </view>
  212. <view class="mt-16rpx text-24rpx">
  213. 查看充电订单
  214. </view>
  215. </view>
  216. <view class="h-100rpx w-100rpx">
  217. <image
  218. class="h-100rpx w-100rpx"
  219. :src="`${StaticUrl}/charge-mine-order.png`"
  220. mode="scaleToFill"
  221. />
  222. </view>
  223. </view>
  224. <view class="flex items-center gap-18rpx rounded-16rpx bg-#FFF px-24rpx py-28rpx" @click="router.push({ name: 'charge-voucher', params: { couponBalance: userAccountInfo?.couponBalance } })">
  225. <view>
  226. <view class="flex items-center gap-16rpx">
  227. <text class="text-32rpx font-bold">
  228. 购券中心
  229. </text>
  230. <view class="h-30rpx w-30rpx rounded-50% bg-#000 text-center line-height-[30rpx]">
  231. <wd-icon name="arrow-right" color="#FFFFFF" size="30rpx" />
  232. </view>
  233. </view>
  234. <view class="mt-16rpx text-24rpx">
  235. 平台券充值
  236. </view>
  237. </view>
  238. <view class="h-100rpx w-100rpx">
  239. <image
  240. class="h-100rpx w-100rpx"
  241. :src="`${StaticUrl}/charge-mine-voucher.png`"
  242. mode="scaleToFill"
  243. />
  244. </view>
  245. </view>
  246. </view>
  247. <view class="mt-24rpx flex items-center justify-between rounded-16rpx bg-#FFF p-20rpx">
  248. <view class="text-32rpx font-bold">
  249. 我的车辆
  250. </view>
  251. <!-- 无车辆时显示添加提示 -->
  252. <view v-if="!defaultVehicle" class="flex items-center gap-10rpx" @click="router.push({ name: 'charge-plate-list' })">
  253. <view class="text-26rpx">
  254. 添加车辆,享更多权益
  255. </view>
  256. <view class="h-50rpx w-50rpx rounded-50% bg-[linear-gradient(90deg,#DBFC81_0%,#9ED605_100%)] text-center text-30rpx text-#FFF font-bold line-height-[50rpx]">
  257. +
  258. </view>
  259. </view>
  260. <!-- 有车辆时显示车牌和管理入口 -->
  261. <view v-else class="flex items-center gap-50rpx text-26rpx text-#9ED605" @click="router.push({ name: 'charge-plate-list' })">
  262. <view>{{ defaultVehicle.licensePlate }}</view>
  263. <view>管理>></view>
  264. </view>
  265. </view>
  266. <view class="mt-24rpx flex items-center gap-20rpx">
  267. <view
  268. v-for="option in filterOptions"
  269. :key="option.key"
  270. class="select-item h-60rpx w-152rpx text-28rpx line-height-[60rpx]" :class="[
  271. option.widthClass,
  272. { 'select-item-active': activeFilter === option.key },
  273. ]"
  274. @click="handleFilterClick(option.key)"
  275. >
  276. {{ option.label }}
  277. </view>
  278. </view>
  279. <view :class="[loading ? 'pt-24rpx' : '']">
  280. <wd-skeleton theme="paragraph" animation="gradient" :loading="loading" :row-col="[{ height: '100px', width: '100%' }, { height: '100px', width: '100%' }, { height: '100px', width: '100%' }]">
  281. <view v-for="item in stationList" :key="item.stationId" class="relative mt-24rpx rounded-16rpx bg-#FFFFFF p-24rpx" @click="router.push({ name: 'charge-site-detail', params: { stationId: String(item.stationId) } })">
  282. <image
  283. class="absolute right-0 top-0 h-52rpx w-186rpx"
  284. :src="`${StaticUrl}/charge-firm-tag.png`"
  285. />
  286. <view class="text-32rpx text-#2B303A font-bold">
  287. {{ item.stationName }}
  288. </view>
  289. <view class="mt-20rpx text-24rpx text-#aaa">
  290. {{ item.tips || '暂无提示' }}
  291. </view>
  292. <view class="mt-24rpx flex items-center rounded-16rpx bg-[linear-gradient(90deg,rgba(170,255,235,0.3)_0%,rgba(175,247,252,0.2)_43.57%,rgba(139,232,252,0)_100%)] p-20rpx">
  293. <view class="flex items-center gap-16rpx">
  294. <view class="h-40rpx w-40rpx rounded-8rpx bg-[linear-gradient(180deg,#4FEF86_0%,#00AA3A_100%)] text-center text-28rpx text-#FFF font-bold line-height-[40rpx]">
  295. </view>
  296. <view class="flex items-center">
  297. <text class="text-32rpx font-bold">
  298. {{ item.fastCharging }}
  299. </text>
  300. </view>
  301. </view>
  302. <view class="ml-40rpx flex items-center gap-16rpx">
  303. <view class="h-40rpx w-40rpx rounded-8rpx bg-[linear-gradient(180deg,#8EB1FF_0%,#3071FF_100%)] text-center text-28rpx text-#FFF font-bold line-height-[40rpx]">
  304. </view>
  305. <view class="flex items-center">
  306. <text class="text-32rpx font-bold">
  307. {{ item.slowCharging }}
  308. </text>
  309. </view>
  310. </view>
  311. <view class="ml-150rpx h-44rpx w-148rpx flex items-center border-2rpx border-#3EB6F8 rounded-34rpx border-solid line-height-[44rpx]">
  312. <view class="w-44rpx rounded-[34rpx_0rpx_0rpx_34rpx] bg-#3EB6F8 text-center">
  313. <wd-icon name="location" size="16px" color="#FFF" />
  314. </view>
  315. <view class="text-24rpx text-#3EB6F8">
  316. {{ item.distance }}km
  317. </view>
  318. </view>
  319. </view>
  320. <view class="mt-28rpx flex items-center justify-between">
  321. <view class="relative flex">
  322. <view class="absolute left-30rpx z-10 flex items-center -top-26rpx">
  323. <text class="text-40rpx text-#FF6464 font-bold">
  324. {{ item.platformPrice }}
  325. </text>
  326. <text class="w-100rpx text-24rpx">
  327. 元/度
  328. </text>
  329. </view>
  330. <image
  331. class="absolute h-60rpx w-365rpx -top-30rpx"
  332. :src="`${StaticUrl}/to-charge-tag.png`"
  333. />
  334. </view>
  335. <view class="text-24rpx text-#2B303A">
  336. {{ item.peakValue }}:{{ item.peakTime }}
  337. </view>
  338. </view>
  339. </view>
  340. </wd-skeleton>
  341. </view>
  342. <view class="h-160rpx" />
  343. </view>
  344. <chargeFooter />
  345. </view>
  346. </template>
  347. <style scoped lang="scss">
  348. .userInfo-card {
  349. background: #FFFFFF;
  350. box-shadow: inset 0rpx 20rpx 40rpx 2rpx rgba(158, 214, 5, 0.2);
  351. border-radius: 16rpx;
  352. padding: 24rpx;
  353. margin-top: 24rpx;
  354. }
  355. .select-item{
  356. background: #FFFFFF;
  357. border-radius: 16rpx;
  358. color: #2B303A;
  359. text-align: center;
  360. }
  361. .select-item-active{
  362. background: #9ED605;
  363. box-shadow: inset 0rpx 20rpx 40rpx 2rpx rgba(100,255,218,0.26);
  364. border-radius: 0rpx 16rpx 0rpx 16rpx;
  365. font-weight: bold;
  366. color: #FFFFFF;
  367. }
  368. </style>