appointment.vue 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981
  1. <template>
  2. <view :class="['page']">
  3. <view v-if="station && priceInfo">
  4. <view class="block">
  5. <view class="station">
  6. <ChargeMachine
  7. :title="'NO.' + equipment.shortId"
  8. :list="[equipment]"
  9. :time="priceInfo.useTime"
  10. :parkingNo="equipment.parkingNo"
  11. ></ChargeMachine>
  12. </view>
  13. <view class="pt-20 pb-20 pl-30 pr-30 flex-align-center">
  14. <image
  15. src="/pages-charge/static/machines-banner-address.png"
  16. mode="widthFix"
  17. class="flex-shrink mr-12 width-40"
  18. />
  19. <view class="fs-26 color-666">{{ station.address }}</view>
  20. </view>
  21. </view>
  22. <!-- <view class="mt-40 ml-30 color-999 fs-32 fw-500">费用说明</view>-->
  23. <view
  24. class="mt-20 block height-96 flex-align-center pl-30 pr-30"
  25. @click="openPriceDesc"
  26. >
  27. <view class="fs-28 color-000">充电费用</view>
  28. <view class="ml-64 fs-26 color-333"
  29. >{{ priceInfo.minPrice }}~{{ priceInfo.maxPrice }}元/度
  30. </view
  31. >
  32. <view class="ml-auto">
  33. <uni-icons type="right" size="16" color="rgba(0,0,0,0.4)"></uni-icons>
  34. </view>
  35. </view>
  36. <template v-if="appointmentData">
  37. <view class="mt-40 color-999 fs-32 fw-500">已预约充电</view>
  38. <view class="mt-20 block flex-align-center" style="height: 180rpx">
  39. <image
  40. class="width-56 ml-30"
  41. src="/pages-charge/static/icon-alarm.png"
  42. mode="widthFix"
  43. ></image>
  44. <view class="ml-24">
  45. <view class="flex" v-if="appointmentCountDown">
  46. <view class="fs-28 color-000">将在</view>
  47. <view class="fs-44 lh-36 color-primary ml-8 mr-8">{{
  48. appointmentCountDown
  49. }}
  50. </view>
  51. <view class="fs-28 color-000">后开始充电</view>
  52. </view>
  53. <view class="fs-24 color-999 mt-8"
  54. >注意:启动充电前请勿拔插充电枪!
  55. </view
  56. >
  57. </view>
  58. </view>
  59. <view class="mt-40 flex-center">
  60. <view
  61. class="flex-center height-68 br-68 fs-26 color-666"
  62. style="width: 184rpx; border: 1px solid rgba(0, 0, 0, 0.3)"
  63. @click="cancelAppointment"
  64. >取消预约
  65. </view
  66. >
  67. </view>
  68. </template>
  69. <template v-else>
  70. <!-- <view class="mt-40 ml-30 color-999 fs-32 fw-500">选择充电方式</view>-->
  71. <view class="mt-20 block pl-30 pr-30">
  72. <view
  73. v-for="(type, index) in chargeTypes"
  74. :key="index"
  75. :class="['pt-40', 'pb-40', 'flex-align-center']"
  76. :style="{
  77. borderTop: index === 0 ? '' : '1rpx solid rgba(0, 0, 0, 0.10)',
  78. }"
  79. @click="changeType(index)"
  80. >
  81. <view>
  82. <view class="fs-28 lh-28 color-000 fw-500">{{ type.title }}</view>
  83. <view
  84. class="fs-24 lh-24 mt-16"
  85. style="color: #f8386a"
  86. v-if="chargeType === index"
  87. >{{ type.checkTip }}
  88. </view
  89. >
  90. <view class="fs-24 color-999 lh-24 mt-16" v-else>{{
  91. type.tip
  92. }}
  93. </view>
  94. </view>
  95. <view class="ml-auto">
  96. <style-checkbox :checked="chargeType === index"></style-checkbox>
  97. </view>
  98. </view>
  99. </view>
  100. <view class="mt-20 block pl-30 pr-30" v-if="chargeType === 0">
  101. <view
  102. class="pt-40 pb-40 flex-between"
  103. style="border-bottom: 1rpx solid rgba(0, 0, 0, 0.1)"
  104. @click="selectTime"
  105. >
  106. <view class="fs-28 color-000 fw-500">充电时间</view>
  107. <view class="flex-align-center lh-28">
  108. <block v-if="chargeTime.isPowerSaving">
  109. <view class="fs-28 color-333 fw-500 mr-16">00:00 - 08:00</view>
  110. </block>
  111. <block v-else>
  112. <view
  113. v-if="chargeTime.format"
  114. class="fs-28 color-333 fw-500 mr-16"
  115. >{{ chargeTime.format }}
  116. </view
  117. >
  118. <view v-else class="fs-26 mr-16" style="color: #cacaca"
  119. >请选择
  120. </view
  121. >
  122. <uni-icons type="right" size="16" color="#CACACA"></uni-icons>
  123. </block>
  124. </view>
  125. </view>
  126. <view class="pt-40 pb-40 flex-between">
  127. <view class="fs-28 color-000 fw-500">预计费用</view>
  128. <view class="flex-align-center lh-28">
  129. <block v-if="chargeTime.isPowerSaving">
  130. <view class="fs-28 color-333 fw-500"
  131. >低至{{ priceInfo.minPrice }}元/kwh
  132. </view
  133. >
  134. </block>
  135. <block v-else>
  136. <view
  137. v-if="chargeTime.formatPrice"
  138. class="fs-28 color-333 fw-500"
  139. >{{ chargeTime.formatPrice }}
  140. </view
  141. >
  142. <view v-else class="fs-26 mr-16" style="color: #cacaca"
  143. >选择时间后显示
  144. </view
  145. >
  146. </block>
  147. </view>
  148. </view>
  149. </view>
  150. <view
  151. class="mt-20 block flex-align-center pl-30 pr-30 pt-30 pb-30"
  152. @click="handleShowStationChargeCoupon"
  153. >
  154. <!-- to(-->
  155. <!-- `/pages-charge/discount/discountV2?rightsId=${userRechargeRightsId}&couponId=${userCouponId}&stationId=${stationId}`-->
  156. <!-- )-->
  157. <view class="fs-28 lh-28 color-000 fw-500">充电服务费优惠</view>
  158. <block v-if="checkedUserRightsOrCoupon&&checkedUserRightsOrCoupon.rightsId">
  159. <view class="fs-28 lh-28 ml-auto" style="color: #f43636"
  160. >{{ ((checkedUserRightsOrCoupon?.userRights?.discount || 0) / 10).toFixed(1) }}折
  161. </view
  162. >
  163. <uni-icons type="right" size="16" color="#CACACA"></uni-icons>
  164. </block>
  165. <block v-else-if="checkedUserRightsOrCoupon&&checkedUserRightsOrCoupon.couponId">
  166. <view class="fs-28 lh-28 ml-auto" style="color: #f43636" v-if="checkedUserRightsOrCoupon.userCoupon.couponType=='FullDiscount'">
  167. {{ ((checkedUserRightsOrCoupon?.userCoupon?.discount || 0) / 100).toFixed(2) }}元
  168. </view>
  169. <view class="fs-28 lh-28 ml-auto" style="color: #f43636" v-else>
  170. {{ ((checkedUserRightsOrCoupon?.userCoupon?.discount || 0) / 10).toFixed(1) }}折
  171. </view>
  172. <!-- <view class="fs-28 lh-28 color-666 ml-8">权益待领取</view>-->
  173. <uni-icons type="right" size="16" color="#CACACA"></uni-icons>
  174. </block>
  175. <view v-else class="fs-28 lh-28 color-666 ml-auto">无</view>
  176. </view>
  177. <view
  178. class="mt-20 block flex-align-center pl-30 pr-30 pt-30 pb-30"
  179. v-if="chargeType === 0"
  180. >
  181. <view class="flex-column">
  182. <view class="fs-28 lh-28 color-000 fw-500">省钱模式</view>
  183. <view class="fs-24 lh-30 color-999 mt-8"
  184. >处于00:00-08:00时,电费低至{{ priceInfo.minPrice }}元/kwh
  185. </view
  186. >
  187. </view>
  188. <view class="ml-auto">
  189. <switch
  190. :checked="chargeTime.isPowerSaving"
  191. color="#2d9e95"
  192. @change="changePowerSaving"
  193. />
  194. </view>
  195. </view>
  196. </template>
  197. <view class="pt-60 pb-80"></view>
  198. <style-bottom-view>
  199. <view class="pt-20 pl-40 pr-40 pb-20 bg-fff flex">
  200. <template v-if="appointmentData">
  201. <view class="mr-10" style="width: 50%">
  202. <style-button size="small" @click="selectTime"
  203. >修改时间
  204. </style-button
  205. >
  206. </view
  207. >
  208. <view class="ml-10" style="width: 50%">
  209. <style-button type="primary" size="small" @click="submitNow"
  210. >立即开始
  211. </style-button
  212. >
  213. </view>
  214. </template>
  215. <template v-else>
  216. <style-button v-if="balance>0" type="primary" size="small" @click="submit">{{
  217. chargeType === 0 ? "提交预约" : "立即充电"
  218. }}
  219. </style-button>
  220. <style-button v-else type="primary" size="small" @click="to('/pages-user/wallet-recharge/wallet-recharge')">
  221. 余额不足,请充值
  222. </style-button>
  223. </template>
  224. </view>
  225. </style-bottom-view>
  226. </view>
  227. <style-dialog
  228. v-if="chargeTimeDialog"
  229. @close="closeTime"
  230. :title="
  231. '请选择' + (chargeTime.isPowerSaving ? '省电模式' : '') + '充电时间'
  232. "
  233. >
  234. <view class="flex" style="border-top: 1rpx solid rgba(0, 0, 0, 0.1)">
  235. <view
  236. class="flex-shrink"
  237. style="height: 840rpx; width: 256rpx; background-color: #f0f0f0"
  238. >
  239. <view
  240. class="flex-align-center pl-32 height-90"
  241. v-for="(day, index) in timesInfo.day"
  242. :key="index"
  243. :style="{
  244. backgroundColor:
  245. day.format === chargeTime.day ? '#fff' : 'transparent',
  246. }"
  247. @click="changeDay(index)"
  248. >
  249. <view
  250. :class="[
  251. 'fs-32',
  252. 'fw-500',
  253. day.format === chargeTime.day ? 'color-primary' : 'color-000',
  254. ]"
  255. >{{ day.format }}
  256. </view
  257. >
  258. <view
  259. class="fs-24 color-666 ml-12 br-8 width-60 height-40 flex-center"
  260. style="background-color: rgba(0, 0, 0, 0.07)"
  261. v-if="day.year && day.format !== chargeTime.day"
  262. >{{ day.year }}年
  263. </view
  264. >
  265. </view>
  266. </view>
  267. <scroll-view
  268. scroll-y
  269. class="flex-grow"
  270. style="height: 840rpx"
  271. :scroll-into-view="chargeTime.scrollId"
  272. >
  273. <view
  274. :id="`hour-${hour.format}`"
  275. class="flex-align-center pl-40 height-90 fs-32"
  276. v-for="(hour, index) in timesInfo.hour"
  277. :key="index"
  278. :style="{
  279. color: hour.disabled
  280. ? '#999'
  281. : hour.format === chargeTime.hour
  282. ? 'var(--color-primary)'
  283. : '#000',
  284. }"
  285. @click="changeHour(index)"
  286. >
  287. {{ hour.format }}
  288. </view>
  289. <view class="hour-placeholder"></view>
  290. </scroll-view>
  291. </view>
  292. </style-dialog>
  293. <PriceDesc
  294. v-if="priceDescVisible"
  295. :desc="priceInfo.policyInfos || []"
  296. @close="closePriceDesc"
  297. ></PriceDesc>
  298. <StationChargeCoupon ref="station_charge_coupon_ref" @on-change="handleCheckedUserRightsAndCoupon"></StationChargeCoupon>
  299. </view>
  300. </template>
  301. <script setup lang="ts">
  302. import {
  303. cancelAppointmentCharge,
  304. changeAppointmentTime,
  305. fetchChargeStatus,
  306. fetchStationByConnectorIdOrShortId,
  307. fetchStationDetail, fetchStationDetailByShortId, fetchStationDetailByShortIdOrConnectorIdOrEquipmentId,
  308. fetchStationPriceDesc,
  309. startAppointmentCharge,
  310. startCharge,
  311. } from "@/api/charge";
  312. import {fetchProfile, listStationAvailableRightsAndCoupons} from "@/api/user";
  313. import {onLoad, onShow} from "@dcloudio/uni-app";
  314. import {ref} from "vue";
  315. import ChargeMachine from "../machines/charge-machine/charge-machine.vue";
  316. import PriceDesc from "../machines/price-desc/price-desc.vue";
  317. import StationChargeCoupon from "@/components/station-charge-coupon/station-charge-coupon.vue";
  318. import {format} from "@/utils/date";
  319. import {redirect, to} from "../../utils/navigate";
  320. const DAY = 24 * 60 * 60 * 1000;
  321. const options = ref<any>();
  322. const data = ref<any>();
  323. const equipment = ref<any>(null);
  324. const station = ref<any>();
  325. const stationId = ref<any>();
  326. const priceInfo = ref();
  327. const priceDescVisible = ref(false);
  328. const timesInfo = ref<any>({
  329. day: [],
  330. time: [],
  331. });
  332. const checkedUserRightsOrCoupon = ref<any>({
  333. rightsId: null,
  334. userRechargeRightsId: null,
  335. couponId: null,
  336. userCouponId: null,
  337. stationId: null,
  338. userRights: null,
  339. userCoupon: null
  340. });
  341. const station_charge_coupon_ref = ref();
  342. const chargeType = ref(1);
  343. const chargeTypes = ref([
  344. {
  345. title: "预约充电",
  346. tip: "预约特定时间点开始充电",
  347. checkTip: "部分车辆可能进入休眠状态无法唤醒,将导致充电失败",
  348. },
  349. {
  350. title: "立即充电",
  351. },
  352. ]);
  353. const chargeTimeDialog = ref(false);
  354. const chargeTime = ref({
  355. time: 0, // 时间戳
  356. day: "",
  357. hour: "",
  358. format: "",
  359. formatPrice: "",
  360. scrollId: "",
  361. isPowerSaving: false, // 省电模式
  362. });
  363. const appointmentData = ref();
  364. const appointmentCountDown = ref();
  365. const discountIndex = ref(-1);
  366. const discountList = ref<any[]>([]);
  367. const activity = ref();
  368. const balance = ref(0);
  369. const refreshUserBalance = () => {
  370. let user = getApp<any>().globalData.user
  371. console.log(user)
  372. balance.value = user?.balance || 0;
  373. }
  374. const handleShowStationChargeCoupon = () => {
  375. let {rightsId, couponId} = checkedUserRightsOrCoupon.value;
  376. station_charge_coupon_ref.value?.open(stationId.value, rightsId, couponId);
  377. }
  378. const handleCheckedUserRightsAndCoupon = (data: any) => {
  379. // console.log("handleCheckedUserRightsAndCoupon", data);
  380. checkedUserRightsOrCoupon.value = data;
  381. }
  382. const changeType = (index: number) => {
  383. chargeType.value = index;
  384. };
  385. const isPassTime = (hour: string, day: number) => {
  386. const now = new Date();
  387. const time = new Date(format("y/M/d", day) + " " + hour + ":00");
  388. return now > time;
  389. };
  390. const getHourPrice = (hour: string) => {
  391. let price = "";
  392. priceInfo.value.policyInfos.forEach((item: any) => {
  393. if (Number(hour) >= Number(item.startTime.substring(0, 2))) {
  394. price = item.totalPrice;
  395. }
  396. });
  397. return price;
  398. };
  399. const rebuildHours = (day: Date) => {
  400. timesInfo.value.hour = [];
  401. const nowHour = format("h");
  402. const isToday = format("d") === format("d", day.getTime());
  403. // 当前时间的24小时内
  404. let startHour = isToday ? Number(nowHour) : 0;
  405. let endHour = isToday ? 24 : Number(nowHour) + 1;
  406. if (chargeTime.value.isPowerSaving) {
  407. startHour = 0;
  408. endHour = 8;
  409. }
  410. for (let index = startHour; index < endHour; index++) {
  411. timesInfo.value.hour = [...timesInfo.value.hour, ...buildHours(index, day)];
  412. }
  413. };
  414. const buildHours = (hour: number, day: Date) => {
  415. let res: any[] = [];
  416. let hourTemp = hour >= 10 ? `${hour}` : `0${hour}`;
  417. let priceTemp = getHourPrice(hourTemp);
  418. for (let index = 0; index < 12; index++) {
  419. res.push({
  420. format: `${hourTemp}:${index * 5 >= 10 ? index * 5 : `0${index * 5}`}`,
  421. formatPrice: priceTemp,
  422. disabled: isPassTime(
  423. `${hourTemp}:${index * 5 >= 10 ? index * 5 : `0${index * 5}`}`,
  424. chargeTime.value.time ? chargeTime.value.time : day.getTime()
  425. ),
  426. });
  427. }
  428. return res;
  429. };
  430. const selectTime = () => {
  431. if (chargeTime.value.isPowerSaving) {
  432. uni.showToast({
  433. title: "省电模式将在00:00-08:00进行充电",
  434. icon: "none",
  435. });
  436. return;
  437. }
  438. if (!appointmentData.value && chargeType.value === 1) {
  439. uni.showToast({
  440. title: "请选择预约模式",
  441. icon: "none",
  442. });
  443. return;
  444. }
  445. const now = appointmentData.value
  446. ? new Date(appointmentData.value.startTime.replace(/-/g, "/"))
  447. : new Date();
  448. const year = format("y", now.getTime());
  449. const nextyear = format("y", now.getTime() + DAY);
  450. // 天
  451. timesInfo.value.day = [
  452. {
  453. time: now.getTime(),
  454. format: format("M月d日", now.getTime()),
  455. year: "",
  456. },
  457. {
  458. time: now.getTime() + DAY,
  459. format: format("M月d日", now.getTime() + DAY),
  460. year: year === nextyear ? "" : nextyear.slice(2),
  461. },
  462. ];
  463. rebuildHours(now);
  464. // 预约时间
  465. if (!appointmentData.value) {
  466. if (!chargeTime.value.day) {
  467. chargeTime.value.time = timesInfo.value.day[0].time;
  468. chargeTime.value.day = timesInfo.value.day[0].format;
  469. }
  470. // 滚动到特定位置
  471. if (chargeTime.value.hour) {
  472. chargeTime.value.scrollId = `hour-${chargeTime.value.hour}`;
  473. } else {
  474. let hourNow = now.getHours();
  475. chargeTime.value.scrollId = `hour-${hourNow}:00`;
  476. }
  477. }
  478. // 修改时间
  479. if (appointmentData.value) {
  480. chargeTime.value.time = timesInfo.value.day[0].time;
  481. chargeTime.value.day = timesInfo.value.day[0].format;
  482. const findIndex = timesInfo.value.hour.findIndex(
  483. (item: any) => item.format === format("h:m:s", now.getTime())
  484. );
  485. if (findIndex >= 0) {
  486. chargeTime.value.hour = timesInfo.value.hour[findIndex].format;
  487. chargeTime.value.formatPrice =
  488. timesInfo.value.hour[findIndex].formatPrice;
  489. }
  490. }
  491. chargeTimeDialog.value = true;
  492. };
  493. const changeDay = (index: number) => {
  494. chargeTime.value.time = timesInfo.value.day[index].time;
  495. chargeTime.value.day = timesInfo.value.day[index].format;
  496. if (chargeTime.value.hour) {
  497. chargeTime.value.hour = "";
  498. chargeTime.value.formatPrice = "";
  499. }
  500. const now = new Date(chargeTime.value.time);
  501. rebuildHours(now);
  502. setTimeout(() => {
  503. chargeTime.value.scrollId = `hour-${timesInfo.value.hour[0].format}`;
  504. }, 0);
  505. };
  506. const changeHour = (index: number) => {
  507. if (timesInfo.value.hour[index].disabled) {
  508. uni.showToast({
  509. title: "无法选择过去的时间",
  510. icon: "none",
  511. });
  512. return;
  513. }
  514. if (appointmentData.value) {
  515. // 修改时间
  516. uni.showModal({
  517. title: "温馨提示",
  518. content: `确认修改为${timesInfo.value.hour[index].format}吗?`,
  519. confirmText: "确定",
  520. confirmColor: "#2d9e95",
  521. success(modal) {
  522. if (modal.confirm) {
  523. uni.showLoading({
  524. title: "提交中",
  525. mask: true,
  526. });
  527. changeAppointmentTime(
  528. appointmentData.value.startChargeSeq,
  529. `${format("y-M-d", chargeTime.value.time)} ${
  530. timesInfo.value.hour[index].format
  531. }:00`
  532. )
  533. .then(() => {
  534. return fetchChargeStatus();
  535. })
  536. .then((res) => {
  537. uni.hideLoading();
  538. if (res && res.isAppointment) {
  539. uni.showToast({
  540. icon: "success",
  541. title: "修改成功",
  542. });
  543. closeTime();
  544. appointmentData.value = res;
  545. startAppointmentCountDown();
  546. }
  547. });
  548. }
  549. },
  550. });
  551. return;
  552. }
  553. chargeTime.value.hour = timesInfo.value.hour[index].format;
  554. chargeTime.value.format = `${format("y年M月d日", chargeTime.value.time)} ${
  555. chargeTime.value.hour
  556. }`;
  557. chargeTime.value.formatPrice = timesInfo.value.hour[index].formatPrice;
  558. closeTime();
  559. };
  560. const changePowerSaving = (e: any) => {
  561. chargeTime.value = {
  562. time: 0, // 时间戳
  563. day: "",
  564. hour: "",
  565. isPowerSaving: e.detail.value,
  566. format: "",
  567. formatPrice: "",
  568. scrollId: "",
  569. };
  570. };
  571. const closeTime = () => {
  572. chargeTimeDialog.value = false;
  573. if (appointmentData.value) {
  574. return;
  575. }
  576. if (!chargeTime.value.hour) {
  577. chargeTime.value = {
  578. time: 0, // 时间戳
  579. day: "",
  580. hour: "",
  581. isPowerSaving: chargeTime.value.isPowerSaving, // 省电模式
  582. format: "",
  583. formatPrice: "",
  584. scrollId: "",
  585. };
  586. }
  587. };
  588. const openPriceDesc = () => {
  589. priceDescVisible.value = true;
  590. };
  591. const closePriceDesc = () => {
  592. priceDescVisible.value = false;
  593. };
  594. const cancelAppointment = () => {
  595. uni.showModal({
  596. title: "取消预约",
  597. content: "确定取消预约吗?",
  598. cancelText: "取消预约",
  599. cancelColor: "#999999",
  600. confirmText: "点错了",
  601. confirmColor: "#2d9e95",
  602. success: (res) => {
  603. if (res.cancel) {
  604. uni.showLoading({
  605. title: "提交中",
  606. mask: true,
  607. });
  608. cancelAppointmentCharge()
  609. .then(() => {
  610. uni.hideLoading();
  611. uni.showToast({
  612. icon: "success",
  613. title: "已取消",
  614. });
  615. appointmentData.value = undefined;
  616. startAppointmentCountDown();
  617. })
  618. .catch((err) => {
  619. uni.hideLoading();
  620. });
  621. }
  622. },
  623. });
  624. };
  625. let appointmentCountDownTimer = 0;
  626. const startAppointmentCountDown = () => {
  627. appointmentCountDownTimer && clearTimeout(appointmentCountDownTimer);
  628. if (!appointmentData.value) {
  629. appointmentCountDown.value = "";
  630. return;
  631. }
  632. appointmentCountDown.value = formatAppointmentCountDown(
  633. appointmentData.value.startTime
  634. );
  635. if (appointmentCountDown.value) {
  636. appointmentCountDownTimer = setTimeout(() => {
  637. startAppointmentCountDown();
  638. }, 1000);
  639. } else {
  640. uni.showModal({
  641. content: "当前预约时间已到,将开始进行充电",
  642. showCancel: false,
  643. success() {
  644. uni.switchTab({
  645. url: "/pages/map/map",
  646. });
  647. },
  648. });
  649. }
  650. };
  651. const formatAppointmentCountDown = (time: string) => {
  652. const now = new Date();
  653. const date = new Date(time.replace(/-/g, "/"));
  654. let secondTime = parseInt(`${(date.getTime() - now.getTime()) / 1000}`);
  655. if (secondTime <= 0) {
  656. return "";
  657. }
  658. let hourTime = 0;
  659. let minuteTime = 0; // 分
  660. if (secondTime >= 60) {
  661. minuteTime = parseInt(`${secondTime / 60}`);
  662. secondTime = parseInt(`${secondTime % 60}`);
  663. if (minuteTime >= 60) {
  664. hourTime = parseInt(`${minuteTime / 60}`);
  665. minuteTime = parseInt(`${minuteTime % 60}`);
  666. }
  667. }
  668. return `${hourTime < 10 ? "0" + hourTime : hourTime}:${
  669. minuteTime < 10 ? "0" + minuteTime : minuteTime
  670. }:${secondTime < 10 ? "0" + secondTime : secondTime}`;
  671. };
  672. const submit = () => {
  673. if (
  674. chargeType.value === 0 &&
  675. !chargeTime.value.isPowerSaving &&
  676. !chargeTime.value.hour
  677. ) {
  678. uni.showToast({
  679. title: "请选择充电时间",
  680. icon: "none",
  681. });
  682. return;
  683. }
  684. const now = new Date();
  685. let startTime = chargeTime.value.time;
  686. if (chargeTime.value.isPowerSaving) {
  687. startTime = now.getTime();
  688. // 省电模式
  689. if (
  690. now.getHours() >= 8 &&
  691. now.getDate() === Number(format("d", chargeTime.value.time))
  692. ) {
  693. startTime += DAY;
  694. }
  695. }
  696. let query = "";
  697. if (chargeType.value === 0) {
  698. query += "isBooking=true";
  699. query += `&startTime=${format("y-M-d", startTime)} ${
  700. chargeTime.value.isPowerSaving
  701. ? "00:00:00"
  702. : `${chargeTime.value.hour}:00`
  703. }`;
  704. if (chargeTime.value.isPowerSaving) {
  705. query += `&endTime=${format("y-M-d", startTime)} 08:00:00`;
  706. }
  707. }
  708. if (chargeType.value === 1) {
  709. query += "isBooking=false";
  710. }
  711. let {userRechargeRightsId, userCouponId} = checkedUserRightsOrCoupon.value;
  712. console.log("userRechargeRightsId")
  713. if (userRechargeRightsId && userRechargeRightsId >= 0) {
  714. query += `&userRechargeRightsId=${userRechargeRightsId}`;
  715. }
  716. if (userCouponId && userCouponId >= 0) {
  717. query += `&userCouponId=${userCouponId}`;
  718. }
  719. console.log(query)
  720. uni.showLoading({
  721. title: "提交中",
  722. mask: true,
  723. });
  724. startCharge(equipment.value.connectorId, query)
  725. .then(() => {
  726. if (chargeType.value === 0) {
  727. fetchChargeStatus()
  728. .then((res) => {
  729. if (res && res.isAppointment) {
  730. uni.hideLoading();
  731. uni.showToast({
  732. icon: "success",
  733. title: "预约成功",
  734. });
  735. appointmentData.value = res;
  736. startAppointmentCountDown();
  737. }
  738. })
  739. .catch(() => {
  740. uni.navigateBack();
  741. });
  742. }
  743. if (chargeType.value === 1) {
  744. redirect(`/pages-charge/ordering/ordering?connectorId=${options.value.connectorId}&start=1`)
  745. }
  746. })
  747. .catch((err) => {
  748. uni.hideLoading();
  749. uni.showModal({
  750. content: `${err.errMsg}`,
  751. showCancel: false,
  752. });
  753. });
  754. };
  755. const submitNow = () => {
  756. uni.showLoading({
  757. title: "提交中",
  758. mask: true,
  759. });
  760. startAppointmentCharge(options.value.connectorId)
  761. .then(() => {
  762. _checkStartCharge();
  763. })
  764. .catch((err) => {
  765. uni.hideLoading();
  766. uni.showModal({
  767. content: `${err.errMsg}`,
  768. showCancel: false,
  769. });
  770. });
  771. };
  772. const _checkStartCharge = () => {
  773. fetchChargeStatus().then((res) => {
  774. if (res && [2].includes(res.chargeStatus)) {
  775. uni.hideLoading();
  776. redirect(`/pages-charge/ordering/ordering?connectorId=${options.value.connectorId}&start=1`)
  777. } else {
  778. _checkStartCharge();
  779. }
  780. });
  781. };
  782. /**
  783. * 设置当前用户在该站点的默认充电权益或优惠券
  784. */
  785. const fetchUserStationDefaultRightsAndCoupon = () => {
  786. listStationAvailableRightsAndCoupons(stationId.value).then((res) => {
  787. let {userRechargeRightsList, userCouponList} = res;
  788. let couponList = (userCouponList || []).sort((a: any, b: any) => new Date(a.endTime).getTime() - new Date(b.endTime).getTime());
  789. let rightsList = (userRechargeRightsList || []).sort((a: any, b: any) => new Date(a.endTime).getTime() - new Date(b.endTime).getTime());
  790. checkedUserRightsOrCoupon.value.stationId = stationId.value;
  791. if (rightsList && rightsList.length > 0) {
  792. checkedUserRightsOrCoupon.value.userRights = rightsList[0];
  793. checkedUserRightsOrCoupon.value.rightsId = rightsList[0].rightsId;
  794. checkedUserRightsOrCoupon.value.userRechargeRightsId = rightsList[0].id;
  795. } else if (couponList && couponList.length > 0) {
  796. checkedUserRightsOrCoupon.value.userCoupon = couponList[0];
  797. checkedUserRightsOrCoupon.value.couponId = couponList[0].couponId;
  798. checkedUserRightsOrCoupon.value.userCouponId = couponList[0].id;
  799. }
  800. console.log(res)
  801. });
  802. }
  803. onLoad((_options: any) => {
  804. // sn=SN100523042860091 测试环境
  805. console.log("options", _options);
  806. let {sn, connectorId, equipmentId} = _options
  807. let _sn = _options.sn;
  808. let _stationId = _options.stationId;
  809. if (_stationId) {
  810. stationId.value = _stationId;
  811. }
  812. options.value = {..._options}
  813. uni.showLoading({
  814. title: "加载中",
  815. });
  816. fetchStationDetailByShortIdOrConnectorIdOrEquipmentId(sn, connectorId, equipmentId)
  817. .then((res) => {
  818. console.log("1111111111", res);
  819. //充电桩需要再次过滤
  820. stationId.value = res.stationId;
  821. fetchUserStationDefaultRightsAndCoupon();
  822. if (sn) {
  823. let cns = res.connectInfoList?.filter(k => k.shortId === sn)
  824. if (cns && cns.length > 0) {
  825. equipment.value = cns[0]
  826. }
  827. }
  828. if (connectorId) {
  829. let cns = res.connectInfoList?.filter(k => k.connectorId === connectorId)
  830. if (cns && cns.length > 0) {
  831. equipment.value = cns[0]
  832. }
  833. }
  834. if (equipmentId) {
  835. let cns = res.connectInfoList?.filter(k => k.equipmentId === equipmentId)
  836. if (cns && cns.length > 0) {
  837. equipment.value = cns[0]
  838. }
  839. }
  840. // if (equipment && equipment.connectorInfos && equipment.connectorInfos.length) {
  841. // sn = equipment.connectorInfos[0].connectorId;
  842. // _options.sn = equipment.connectorInfos[0].connectorId;
  843. // }
  844. // options.value = _options;
  845. // data.value = res;
  846. station.value = res;
  847. if(!equipment.value){
  848. uni.showToast({
  849. titile:'none',
  850. content:"服务异常,请稍后再试。"
  851. })
  852. // return Promise.resolve({});
  853. throw Error;
  854. }
  855. return fetchStationPriceDesc(equipment.value.connectorId, res.stationId)
  856. })
  857. .then((res) => {
  858. console.log("xxxx price", res);
  859. uni.hideLoading();
  860. priceInfo.value = res;
  861. return fetchChargeStatus(false, true);
  862. })
  863. .then((res) => {
  864. console.log("charge status", res)
  865. if (res && res.isAppointment) {
  866. appointmentData.value = res;
  867. startAppointmentCountDown();
  868. }
  869. })
  870. .catch((err) => {
  871. console.log(err);
  872. uni.hideLoading();
  873. });
  874. fetchProfile().then((res) => {
  875. if (res.userRechargeRightsList.length) {
  876. res.userRechargeRightsList.forEach((item: any) => {
  877. item.discountFormat = Number((Number(item.discount) / 10).toFixed(2));
  878. });
  879. discountList.value = res.userRechargeRightsList;
  880. discountIndex.value = 0;
  881. }
  882. if (res.activityList && res.activityList.length) {
  883. res.activityList[0].rechargeRightsList.sort((a: any, b: any) => {
  884. return a.discount - b.discount;
  885. });
  886. activity.value = res.activityList[0];
  887. activity.value.minDiscount = Number(
  888. (
  889. Number(res.activityList[0].rechargeRightsList[0]?.discount) / 10
  890. ).toFixed(1)
  891. );
  892. }
  893. refreshUserBalance();
  894. });
  895. refreshUserBalance();
  896. });
  897. onShow(() => {
  898. if (getApp<any>().globalData.lastData.discount) {
  899. discountIndex.value = getApp<any>().globalData.lastData.discount.index;
  900. getApp<any>().globalData.lastData.discount = undefined;
  901. }
  902. refreshUserBalance();
  903. });
  904. </script>
  905. <style lang="scss" scoped>
  906. .page {
  907. min-height: 100vh;
  908. width: 100%;
  909. padding: 20rpx;
  910. background-color: #f6f7fa;
  911. }
  912. .block {
  913. border-radius: 20rpx;
  914. background: #fff;
  915. }
  916. .station {
  917. height: 148rpx;
  918. border-bottom: 1rpx solid rgba(0, 0, 0, 0.1);
  919. }
  920. .hour-placeholder {
  921. box-sizing: content-box;
  922. padding-bottom: constant(safe-area-inset-bottom);
  923. padding-bottom: env(safe-area-inset-bottom);
  924. }
  925. </style>