machines.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  1. <template>
  2. <!-- #ifdef MP-WEIXIN -->
  3. <page-meta page-style="overflow: visible"/>
  4. <!-- #endif -->
  5. <view :class="['pb-40']" v-if="station">
  6. <view class="banner">
  7. <swiper
  8. class="full-percent"
  9. circular
  10. :indicator-dots="true"
  11. :autoplay="true"
  12. :interval="3000"
  13. >
  14. <swiper-item
  15. class="full-percent"
  16. v-for="(item, index) in station.bannerImages"
  17. :key="index"
  18. >
  19. <view
  20. class="full-percent banner-image"
  21. :style="{
  22. backgroundImage: `url(${item})`,
  23. }"
  24. @click="previewImage(item)"
  25. ></view>
  26. </swiper-item>
  27. </swiper>
  28. </view>
  29. <view class="pl-20 pr-20">
  30. <view class="station flex-column" v-if="canUseCount >= 0">
  31. <view style="display: inline-flex;align-items: center;">
  32. <view class="fs-40 fw-600 color-000">{{ title }}</view>
  33. <image class="ml-10"
  34. v-if="location.activityList&&location.activityList.length>0"
  35. src="/static/images/coupon-center.png" mode="widthFix" style="width:32rpx"/>
  36. <view class="coupon-box ml-5"
  37. v-if="location.activityList&&location.activityList.length>0"
  38. @tap.stop="toCouponCenter"
  39. >
  40. <text @tap.stop="showStationCoupon">活动中</text>
  41. </view>
  42. </view>
  43. <view class="flex-align-center mt-10 height-48 relative flex-shrink">
  44. <image
  45. src="../static/machines-banner-address.png"
  46. mode="widthFix"
  47. style="width: 16px; display: block; flex-shrink: 0"
  48. />
  49. <view class="ml-12 fs-26 color-666">{{ station.address }}</view>
  50. <view class="flex-center ml-auto nav" @click="openAddress">
  51. <image
  52. src="../static/machines-banner-nav.png"
  53. mode="widthFix"
  54. />
  55. <view class="fs-26" style="color: #2d9e95">导航</view>
  56. </view>
  57. </view>
  58. <view
  59. class="foot mt-30 flex-align-center flex-shrink"
  60. @click="openDesc"
  61. >
  62. <view class="fs-28 color-666">空闲充电桩:</view>
  63. <view class="fs-36 fw-500 color-primary">{{ canUseCount }}</view>
  64. <view class="fs-28 color-primary ml-6">个</view>
  65. <view class="fs-28 color-666 ml-auto">{{ canUseTime }}</view>
  66. </view>
  67. </view>
  68. <!-- <view class="pt-40 pb-20 fs-32 fw-500 color-999">详细说明</view>-->
  69. <view class="desc" v-if="station">
  70. <view class="flex-align-center" @click="openDesc">
  71. <view class="width-168 fs-26 color-666">充电费用</view>
  72. <view class="fs-26 color-000">{{ canUsePrice }}元/度</view>
  73. <view class="ml-auto lh-0">
  74. <uni-icons
  75. type="right"
  76. size="18"
  77. color="rgba(28,43,41,0.4)"
  78. ></uni-icons>
  79. </view>
  80. </view>
  81. <view class="flex-align-center">
  82. <view class="width-168 fs-26 color-666">停车费用</view>
  83. <view class="fs-26 color-000">{{
  84. Number(station.parkFee) > 0
  85. ? station.parkFee
  86. : "以停车场公示信息为准"
  87. }}
  88. </view>
  89. </view>
  90. <view
  91. class="flex-align-center"
  92. v-if="
  93. station &&
  94. station.connectInfoList &&
  95. station.connectInfoList.length
  96. "
  97. >
  98. <view class="width-168 fs-26 color-666">充电桩类型</view>
  99. <view class="fs-26 color-000"
  100. >{{
  101. station.connectInfoList[0].connectorType === 1
  102. ? "直流"
  103. : "交流"
  104. }}{{ station.connectInfoList[0].power }}kw
  105. </view
  106. >
  107. </view>
  108. </view>
  109. <view class="pt-40 flex-align-center">
  110. <!-- <view class="fs-32 fw-500 color-999">充电桩</view>-->
  111. <view class="fs-26 ml-30 color-999" v-if="station"
  112. >设备数量(共{{ totalCount }}台)
  113. </view
  114. >
  115. <view class="ml-auto flex-align-center" @click="openStatus">
  116. <view class="fs-28 color-333 mr-16">{{
  117. statusList[status].title
  118. }}
  119. </view>
  120. <view
  121. class="mr-20"
  122. style="
  123. width: 0;
  124. height: 0;
  125. border: 8rpx solid;
  126. border-color: #333 transparent transparent transparent;
  127. margin-top: 8rpx;
  128. "
  129. ></view>
  130. </view>
  131. </view>
  132. <template
  133. v-if="station"
  134. v-for="(item, index) in stationEquipmentInfos"
  135. :key="index"
  136. >
  137. <ChargeMachine
  138. :title="'NO.' + item.shortId"
  139. :price="currentPrice"
  140. :list="[item]"
  141. :parkingNo="item.parkingNo"
  142. ></ChargeMachine>
  143. </template>
  144. </view>
  145. <PriceDesc
  146. v-if="dialogVisible && dialogType === 'desc'"
  147. :desc="desc"
  148. @close="closeDialog"
  149. ></PriceDesc>
  150. <view
  151. class="dialog flex-align-end anim-fade-in"
  152. v-if="dialogVisible && dialogType === 'status'"
  153. >
  154. <view class="status-dialog anim-slide-up">
  155. <view class="status-dialog_head flex-center">
  156. <view class="fw-500 color-000" style="font-size: 16px"
  157. >选择充电桩状态
  158. </view
  159. >
  160. <view class="close" @click="closeDialog">
  161. <uni-icons type="closeempty" size="24" color="#1c2b29"></uni-icons>
  162. </view>
  163. </view>
  164. <view class="status-dialog_body">
  165. <view class="status">
  166. <view
  167. :class="['fs-32', 'flex-align-center', 'pressable']"
  168. hover-class="pressed"
  169. hover-stay-time="80"
  170. :style="{ color: status === index ? 'var(--color-primary)' : '#1c2b29' }"
  171. v-for="(statusItem, index) in statusList"
  172. :key="index"
  173. @click="changeStatus(index)"
  174. >
  175. {{ statusItem.title }}
  176. </view>
  177. </view>
  178. </view>
  179. </view>
  180. </view>
  181. <station-coupon ref="station_coupon_ref"></station-coupon>
  182. </view>
  183. </template>
  184. <script setup lang="ts">
  185. import {ref} from "vue";
  186. import {fetchStation, fetchStationDetail, fetchStationPriceDesc} from "../../api/charge";
  187. import {onLoad} from "@dcloudio/uni-app";
  188. import ChargeMachine from "./charge-machine/charge-machine.vue";
  189. import PriceDesc from "./price-desc/price-desc.vue";
  190. import StationCoupon from "@/components/station-coupon/station-coupon.vue";
  191. const dialogVisible = ref(false);
  192. const dialogType = ref("");
  193. const desc = ref<any[]>([]);
  194. const status = ref(0);
  195. const statusList = ref([
  196. {
  197. title: "全部",
  198. },
  199. {
  200. title: "空闲",
  201. },
  202. {
  203. title: "充电中",
  204. },
  205. ]);
  206. const totalCount = ref(0);
  207. const canUseCount = ref(0);
  208. const currentPrice = ref(0);
  209. const canUseTime = ref("");
  210. const canUsePrice = ref("");
  211. const title = ref("");
  212. const location = ref({
  213. address: "",
  214. latitude: "",
  215. longitude: "",
  216. });
  217. const station = ref();
  218. const stationEquipmentInfos = ref();
  219. const currentTime = ref();
  220. const station_coupon_ref = ref(null);
  221. onLoad((options: any) => {
  222. const _title = options.title || "";
  223. uni.setNavigationBarTitle({
  224. title: _title,
  225. });
  226. title.value = _title;
  227. if (getApp<any>().globalData.lastData.station) {
  228. const {address, latitude, longitude, activityList} =
  229. getApp<any>().globalData.lastData.station;
  230. location.value = {
  231. address,
  232. latitude,
  233. longitude,
  234. activityList
  235. };
  236. getApp<any>().globalData.lastData.station = undefined;
  237. }
  238. uni.showLoading({
  239. title: "加载中",
  240. mask: true,
  241. });
  242. fetchStationDetail(options.id)
  243. .then((res) => {
  244. console.log("station detail>>>",res);
  245. let ConnectorID = "";
  246. if (res.connectInfoList && res.connectInfoList.length) {
  247. res.connectInfoList.forEach((con: any) => {
  248. totalCount.value++;
  249. if (!ConnectorID) {
  250. ConnectorID = con.connectorId;
  251. }
  252. if (
  253. con.status &&
  254. con.status === 1
  255. ) {
  256. canUseCount.value++;
  257. }
  258. });
  259. }
  260. // res.totalFee = (Number(res.currentPolicyInfo?.servicePrice||0)+Number(res.currentPolicyInfo?.elecPrice||0)).toFixed(2)
  261. res.bannerImages = res.pictures.split(";");
  262. station.value = res;
  263. stationEquipmentInfos.value = res.connectInfoList?.map((item: any) => {
  264. return {
  265. ...item,
  266. };
  267. });
  268. if (ConnectorID) {
  269. return fetchStationPriceDesc(ConnectorID);
  270. } else {
  271. // eslint-disable-next-line promise/no-return-wrap
  272. return Promise.resolve({
  273. policyInfos: [],
  274. });
  275. }
  276. })
  277. .then((res) => {
  278. uni.hideLoading();
  279. currentPrice.value = res.currentPrice;
  280. currentTime.value = res.currentTime;
  281. canUseTime.value = res.useTime;
  282. canUsePrice.value = `${res.minPrice}~${res.maxPrice}`;
  283. desc.value = res.policyInfos || [];
  284. })
  285. .catch((err) => {
  286. // eslint-disable-next-line no-console
  287. console.log(err);
  288. uni.hideLoading();
  289. uni.showToast({
  290. title: "加载失败,请重试",
  291. icon: "none",
  292. });
  293. });
  294. });
  295. const openAddress = function () {
  296. uni.openLocation({
  297. latitude: Number(location.value.latitude),
  298. longitude: Number(location.value.longitude),
  299. scale: 18,
  300. name: title.value,
  301. address: location.value.address,
  302. });
  303. };
  304. const openDesc = function () {
  305. dialogVisible.value = true;
  306. dialogType.value = "desc";
  307. };
  308. const openStatus = function () {
  309. dialogVisible.value = true;
  310. dialogType.value = "status";
  311. };
  312. const closeDialog = function () {
  313. dialogVisible.value = false;
  314. };
  315. const changeStatus = function (index: number) {
  316. if (status.value === index) {
  317. closeDialog();
  318. return;
  319. }
  320. status.value = index;
  321. if (index === 0) {
  322. stationEquipmentInfos.value = station.value.connectInfoList?.map(
  323. (item: any) => {
  324. return {
  325. ...item,
  326. };
  327. }
  328. );
  329. closeDialog();
  330. return;
  331. }
  332. const STATUS_MAP = [-1, 1, 3];
  333. let newStationEquipmentInfos: any[] = [];
  334. station.value.connectInfoList?.forEach((connect: any) => {
  335. let check = false;
  336. if (
  337. connect.status &&
  338. Number(connect.status) === STATUS_MAP[status.value]
  339. ) {
  340. check = true;
  341. }
  342. if (check) {
  343. newStationEquipmentInfos.push(connect);
  344. }
  345. });
  346. stationEquipmentInfos.value = newStationEquipmentInfos;
  347. closeDialog();
  348. };
  349. const previewImage = (url: string) => {
  350. uni.previewImage({
  351. urls: station.value.bannerImages,
  352. current: url,
  353. });
  354. };
  355. const showStationCoupon = () => {
  356. // console.log(station.value)
  357. station_coupon_ref.value?.open(station.value.StationID);
  358. }
  359. </script>
  360. <style lang="scss">
  361. @use "../../styles/dialog.scss";
  362. page {
  363. background-color: var(--bg-page);
  364. }
  365. .banner {
  366. height: 440rpx;
  367. background-color: transparent;
  368. .banner-image {
  369. background-position: center;
  370. background-size: cover;
  371. background-repeat: no-repeat;
  372. }
  373. }
  374. .station {
  375. margin-top: -60rpx;
  376. border-radius: 20rpx;
  377. overflow: hidden;
  378. position: relative;
  379. background-color: #fff;
  380. min-height: 264rpx;
  381. padding: 0 40rpx;
  382. padding-top: 30rpx;
  383. .nav {
  384. flex-shrink: 0;
  385. width: 120rpx;
  386. height: 48rpx;
  387. border-radius: 40rpx;
  388. background: rgba(45, 158, 149, 0.1);
  389. image {
  390. width: 36rpx;
  391. }
  392. }
  393. .foot {
  394. border-top: 1rpx dashed var(--divider);
  395. height: 96rpx;
  396. }
  397. }
  398. .desc {
  399. box-sizing: border-box;
  400. padding: 0rpx 30rpx;
  401. border-radius: 20rpx;
  402. background-color: #fff;
  403. & > view {
  404. height: 90rpx;
  405. border-top: 1rpx solid var(--divider);
  406. &:first-child {
  407. border-top: none;
  408. }
  409. }
  410. }
  411. .status-dialog {
  412. background-color: #fff;
  413. width: 100%;
  414. overflow: hidden;
  415. border-radius: 24rpx 24rpx 0px 0px;
  416. &_head {
  417. padding: 24rpx 0px;
  418. position: relative;
  419. background-color: #fff;
  420. .close {
  421. position: absolute;
  422. right: 30rpx;
  423. top: 50%;
  424. transform: translateY(-50%);
  425. }
  426. }
  427. &_body {
  428. max-height: 860rpx;
  429. box-sizing: border-box;
  430. padding: 0rpx 30rpx 80rpx 30rpx;
  431. background-color: #fff;
  432. overflow-y: auto;
  433. .status {
  434. & > view {
  435. height: 92rpx;
  436. border-top: 1rpx solid var(--divider);
  437. &:first-child {
  438. border-top: none;
  439. }
  440. }
  441. }
  442. }
  443. }
  444. .coupon-box {
  445. display: inline-flex;
  446. justify-content: space-between;
  447. align-items: center;
  448. color: #fff;
  449. background-color: var(--color-primary);
  450. font-size: 14rpx;
  451. border-radius: 12rpx;
  452. padding: 0rpx 6rpx;
  453. text {
  454. font-size: 24rpx;
  455. }
  456. }
  457. </style>