machines.vue 11 KB

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