machines.vue 13 KB

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