machines.vue 11 KB

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