machines.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  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="/pages-charge/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="/pages-charge/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(0,0,0,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"
  152. v-if="dialogVisible && dialogType === 'status'"
  153. >
  154. <view class="status-dialog">
  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="#2D284B"></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']"
  168. :style="{ color: status === index ? '#2d9e95' : '#2d284b' }"
  169. v-for="(statusItem, index) in statusList"
  170. :key="index"
  171. @click="changeStatus(index)"
  172. >
  173. {{ statusItem.title }}
  174. </view>
  175. </view>
  176. </view>
  177. </view>
  178. </view>
  179. <station-coupon ref="station_coupon_ref"></station-coupon>
  180. </view>
  181. </template>
  182. <script setup lang="ts">
  183. import {ref} from "vue";
  184. import {fetchStation, fetchStationDetail, fetchStationPriceDesc} from "../../api/charge";
  185. import {onLoad} from "@dcloudio/uni-app";
  186. import ChargeMachine from "./charge-machine/charge-machine.vue";
  187. import PriceDesc from "./price-desc/price-desc.vue";
  188. import StationCoupon from "@/components/station-coupon/station-coupon.vue";
  189. const dialogVisible = ref(false);
  190. const dialogType = ref("");
  191. const desc = ref<any[]>([]);
  192. const status = ref(0);
  193. const statusList = ref([
  194. {
  195. title: "全部",
  196. },
  197. {
  198. title: "空闲",
  199. },
  200. {
  201. title: "充电中",
  202. },
  203. ]);
  204. const totalCount = ref(0);
  205. const canUseCount = ref(0);
  206. const currentPrice = ref(0);
  207. const canUseTime = ref("");
  208. const canUsePrice = ref("");
  209. const title = ref("");
  210. const location = ref({
  211. address: "",
  212. latitude: "",
  213. longitude: "",
  214. });
  215. const station = ref();
  216. const stationEquipmentInfos = ref();
  217. const currentTime = ref();
  218. const station_coupon_ref = ref(null);
  219. onLoad((options: any) => {
  220. const _title = options.title || "";
  221. uni.setNavigationBarTitle({
  222. title: _title,
  223. });
  224. title.value = _title;
  225. if (getApp<any>().globalData.lastData.station) {
  226. const {address, latitude, longitude, activityList} =
  227. getApp<any>().globalData.lastData.station;
  228. location.value = {
  229. address,
  230. latitude,
  231. longitude,
  232. activityList
  233. };
  234. getApp<any>().globalData.lastData.station = undefined;
  235. }
  236. uni.showLoading({
  237. title: "加载中",
  238. mask: true,
  239. });
  240. fetchStationDetail(options.id)
  241. .then((res) => {
  242. console.log("station detail>>>",res);
  243. let ConnectorID = "";
  244. if (res.connectInfoList && res.connectInfoList.length) {
  245. res.connectInfoList.forEach((con: any) => {
  246. totalCount.value++;
  247. if (!ConnectorID) {
  248. ConnectorID = con.connectorId;
  249. }
  250. if (
  251. con.status &&
  252. con.status === 1
  253. ) {
  254. canUseCount.value++;
  255. }
  256. });
  257. }
  258. // res.totalFee = (Number(res.currentPolicyInfo?.servicePrice||0)+Number(res.currentPolicyInfo?.elecPrice||0)).toFixed(2)
  259. res.bannerImages = res.pictures.split(";");
  260. station.value = res;
  261. stationEquipmentInfos.value = res.connectInfoList?.map((item: any) => {
  262. return {
  263. ...item,
  264. };
  265. });
  266. if (ConnectorID) {
  267. return fetchStationPriceDesc(ConnectorID);
  268. } else {
  269. // eslint-disable-next-line promise/no-return-wrap
  270. return Promise.resolve({
  271. policyInfos: [],
  272. });
  273. }
  274. })
  275. .then((res) => {
  276. uni.hideLoading();
  277. currentPrice.value = res.currentPrice;
  278. currentTime.value = res.currentTime;
  279. canUseTime.value = res.useTime;
  280. canUsePrice.value = `${res.minPrice}~${res.maxPrice}`;
  281. desc.value = res.policyInfos || [];
  282. })
  283. .catch((err) => {
  284. // eslint-disable-next-line no-console
  285. console.log(err);
  286. uni.hideLoading();
  287. uni.showToast({
  288. title: "加载失败,请重试",
  289. icon: "none",
  290. });
  291. });
  292. });
  293. const openAddress = function () {
  294. uni.openLocation({
  295. latitude: Number(location.value.latitude),
  296. longitude: Number(location.value.longitude),
  297. scale: 18,
  298. name: title.value,
  299. address: location.value.address,
  300. });
  301. };
  302. const openDesc = function () {
  303. dialogVisible.value = true;
  304. dialogType.value = "desc";
  305. };
  306. const openStatus = function () {
  307. dialogVisible.value = true;
  308. dialogType.value = "status";
  309. };
  310. const closeDialog = function () {
  311. dialogVisible.value = false;
  312. };
  313. const changeStatus = function (index: number) {
  314. if (status.value === index) {
  315. closeDialog();
  316. return;
  317. }
  318. status.value = index;
  319. if (index === 0) {
  320. stationEquipmentInfos.value = station.value.connectInfoList?.map(
  321. (item: any) => {
  322. return {
  323. ...item,
  324. };
  325. }
  326. );
  327. closeDialog();
  328. return;
  329. }
  330. const STATUS_MAP = [-1, 1, 3];
  331. let newStationEquipmentInfos: any[] = [];
  332. station.value.connectInfoList?.forEach((connect: any) => {
  333. let check = false;
  334. if (
  335. connect.status &&
  336. Number(connect.status) === STATUS_MAP[status.value]
  337. ) {
  338. check = true;
  339. }
  340. if (check) {
  341. newStationEquipmentInfos.push(connect);
  342. }
  343. });
  344. stationEquipmentInfos.value = newStationEquipmentInfos;
  345. closeDialog();
  346. };
  347. const previewImage = (url: string) => {
  348. uni.previewImage({
  349. urls: station.value.bannerImages,
  350. current: url,
  351. });
  352. };
  353. const showStationCoupon = () => {
  354. // console.log(station.value)
  355. station_coupon_ref.value?.open(station.value.StationID);
  356. }
  357. </script>
  358. <style lang="scss">
  359. @import "../../styles/dialog.scss";
  360. page {
  361. background-color: #f5f5f5;
  362. }
  363. .banner {
  364. height: 440rpx;
  365. background-color: transparent;
  366. .banner-image {
  367. background-position: center;
  368. background-size: cover;
  369. background-repeat: no-repeat;
  370. }
  371. }
  372. .station {
  373. margin-top: -60rpx;
  374. border-radius: 20rpx;
  375. overflow: hidden;
  376. position: relative;
  377. background-color: #fff;
  378. min-height: 264rpx;
  379. padding: 0 40rpx;
  380. padding-top: 30rpx;
  381. .nav {
  382. flex-shrink: 0;
  383. width: 120rpx;
  384. height: 48rpx;
  385. border-radius: 40rpx;
  386. background: rgba(52, 125, 255, 0.1);
  387. image {
  388. width: 36rpx;
  389. }
  390. }
  391. .foot {
  392. border-top: 1px dashed rgba(0, 0, 0, 0.1);
  393. height: 96rpx;
  394. }
  395. }
  396. .desc {
  397. box-sizing: border-box;
  398. padding: 0rpx 30rpx;
  399. border-radius: 20rpx;
  400. background-color: #fff;
  401. & > view {
  402. height: 90rpx;
  403. border-top: 1px solid rgba(0, 0, 0, 0.1);
  404. &:first-child {
  405. border-top: none;
  406. }
  407. }
  408. }
  409. .status-dialog {
  410. background-color: transparent;
  411. width: 100%;
  412. overflow: hidden;
  413. border-radius: 20rpx 20rpx 0px 0px;
  414. &_head {
  415. padding: 24rpx 0px;
  416. position: relative;
  417. background-color: #fff;
  418. .close {
  419. position: absolute;
  420. right: 30rpx;
  421. top: 50%;
  422. transform: translateY(-50%);
  423. }
  424. }
  425. &_body {
  426. max-height: 860rpx;
  427. box-sizing: border-box;
  428. padding: 0rpx 30rpx 80rpx 30rpx;
  429. background-color: #fff;
  430. overflow-y: auto;
  431. .status {
  432. & > view {
  433. height: 92rpx;
  434. border-top: 1px solid rgba(0, 0, 0, 0.1);
  435. &:first-child {
  436. border-top: none;
  437. }
  438. }
  439. }
  440. }
  441. }
  442. .coupon-box {
  443. display: inline-flex;
  444. justify-content: space-between;
  445. align-items: center;
  446. color: #fff;
  447. background-color: #2d9e95;
  448. font-size: 14rpx;
  449. border-radius: 8rpx;
  450. padding: 0rpx 6rpx;
  451. text {
  452. font-size: 24rpx;
  453. }
  454. }
  455. </style>