ordering.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  1. <template>
  2. <view class="container">
  3. <image
  4. class="bg"
  5. src="/pages-charge/static/charge-ordering-bg.png"
  6. mode="widthFix"
  7. />
  8. <view class="body">
  9. <view class="iphonex-placeholder"></view>
  10. <view
  11. class="status flex-center transition"
  12. :style="{
  13. opacity: step >= 1 ? '1' : '0',
  14. }"
  15. >
  16. <image
  17. :class="['border', `${step <= 2 ? 'border-animation' : ''}`]"
  18. src="/pages-charge/static/charge-ordering-border.png"
  19. @load="onImgLoad"
  20. />
  21. <view class="timer flex-column flex-center">
  22. <block v-if="step === 1">
  23. <view>
  24. <text class="fw-600" style="font-size: 100rpx">{{
  25. status.time
  26. }}</text>
  27. <text class="fs-40 fw-500 ml-10">s</text>
  28. </view>
  29. <view class="fs-26">充电启动中</view>
  30. </block>
  31. <block v-if="step === 2">
  32. <image
  33. class="icon"
  34. src="/pages-charge/static/charge-ordering-icon.png"
  35. />
  36. <view class="fs-26 mt-10">充电中...</view>
  37. </block>
  38. <block v-if="step === 3">
  39. <image
  40. class="icon"
  41. src="/pages-charge/static/charge-ordering-finish.png"
  42. />
  43. <view class="fs-26 mt-10">充电结束</view>
  44. </block>
  45. </view>
  46. </view>
  47. </view>
  48. </view>
  49. <style-bottom-view>
  50. <view
  51. class="transition pl-60 pr-60 pb-40"
  52. :style="{
  53. opacity: step >= 1 ? '1' : '0',
  54. }"
  55. >
  56. <view class="mb-40" v-if="step === 2 || step === 3">
  57. <shadow-card :list="chargeInfo"></shadow-card>
  58. </view>
  59. <style-button
  60. v-if="step === 1 || step === 2"
  61. type="warning"
  62. size="small"
  63. @click="cancel"
  64. >取消充电</style-button
  65. >
  66. <style-button
  67. v-if="step === 3"
  68. type="primary"
  69. size="small"
  70. @click="reLaunch"
  71. >返回首页</style-button
  72. >
  73. </view>
  74. </style-bottom-view>
  75. </template>
  76. <script setup lang="ts">
  77. import { onHide, onLoad, onShow } from "@dcloudio/uni-app";
  78. import { cancelCharge, fetchChargeStatus, startCharge } from "../../api/charge";
  79. import { ref } from "vue";
  80. import { format } from "../../utils/date";
  81. import { to, reLaunch } from "@/utils/navigate";
  82. let timer: any;
  83. let statusTimer: any;
  84. const step = ref(0);
  85. const options = ref<any>();
  86. const status = ref({
  87. time: 30,
  88. start: false,
  89. cancel: false,
  90. error: false,
  91. });
  92. const chargeInfo = ref<any[]>([]);
  93. const startStartTimer = () => {
  94. if (status.value.start || status.value.cancel || status.value.error) {
  95. timer && clearTimeout(timer);
  96. return;
  97. }
  98. timer = setTimeout(() => {
  99. if (status.value.time <= 1 && !status.value.start) {
  100. uni.showModal({
  101. title: "充电错误",
  102. content: "出现问题,请重试",
  103. showCancel: false,
  104. confirmText: "知道了",
  105. confirmColor: "#347DFF",
  106. success() {
  107. uni.navigateBack();
  108. },
  109. });
  110. return;
  111. }
  112. status.value.time = status.value.time - 1;
  113. startStartTimer();
  114. }, 1000);
  115. };
  116. const startStatusTimer = () => {
  117. statusTimer = setTimeout(() => {
  118. fetchChargeStatus()
  119. .then((res) => {
  120. if (status.value.cancel) {
  121. return;
  122. }
  123. if ([1, 2].includes(res.chargeStatus)) {
  124. setChargeData(res);
  125. }
  126. startStatusTimer();
  127. })
  128. .catch(() => {
  129. startStatusTimer();
  130. });
  131. }, 30000);
  132. };
  133. const start = () => {
  134. step.value = 1;
  135. startStartTimer();
  136. startCharge(options.value && options.value.sn ? options.value.sn : "")
  137. .then(() => {
  138. fetchStatus();
  139. })
  140. .catch((err) => {
  141. setTimeout(() => {
  142. status.value.error = true;
  143. timer && clearTimeout(timer);
  144. uni.showModal({
  145. content: `${err.errMsg}`,
  146. showCancel: false,
  147. success: () => {
  148. uni.navigateBack();
  149. },
  150. });
  151. }, 300);
  152. });
  153. };
  154. const cancel = () => {
  155. uni.showModal({
  156. title: "停止充电",
  157. content: "是否停止充电",
  158. cancelText: "停止",
  159. cancelColor: "#999999",
  160. confirmText: "点错了",
  161. confirmColor: "#347DFF",
  162. success: (res) => {
  163. if (res.cancel) {
  164. clearTimeout(timer);
  165. status.value.cancel = true;
  166. if ([1, 2].includes(step.value)) {
  167. if (status.value.start) {
  168. finish();
  169. } else {
  170. uni.showLoading({
  171. title: "正在取消",
  172. });
  173. cancelCharge(
  174. options.value && options.value.sn ? options.value.sn : ""
  175. )
  176. .then(() => {
  177. // console.log(res)
  178. uni.hideLoading();
  179. uni.showToast({
  180. title: "已取消充电",
  181. icon: "none",
  182. });
  183. setTimeout(() => {
  184. uni.navigateBack();
  185. }, 2000);
  186. })
  187. .catch(() => {
  188. uni.hideLoading();
  189. uni.showToast({
  190. title: "已取消充电",
  191. icon: "none",
  192. });
  193. setTimeout(() => {
  194. uni.navigateBack();
  195. }, 2000);
  196. });
  197. }
  198. }
  199. }
  200. },
  201. });
  202. };
  203. const finish = () => {
  204. uni.showLoading({
  205. title: "正在结束中",
  206. });
  207. fetchChargeStatus()
  208. .then((res) => {
  209. if ([1, 2].includes(res.chargeStatus)) {
  210. // 当前充电中
  211. const start = new Date(res.startTime.replace(/-/g, "/"));
  212. const end = new Date();
  213. const diff = parseInt(`${(end.getTime() - start.getTime()) / 1000}`);
  214. const min = parseInt(`${diff / 60}`);
  215. const time =
  216. min >= 60
  217. ? `${parseInt(`${min / 60}`)}小时${parseInt(
  218. `${min - parseInt(`${min / 60}`) * 60}`
  219. )}分钟`
  220. : `${parseInt(`${diff / 60}`)}分钟`;
  221. const endFormat = format("y-M-d h:m:s");
  222. const totalMoney = (res.totalMoney / 100).toFixed(2);
  223. const _chargeInfo = [
  224. {
  225. label: "累计充电量",
  226. value: `${res.totalPower}度`,
  227. },
  228. {
  229. label: "累计费用",
  230. value: `${totalMoney}元`,
  231. },
  232. {
  233. label: "开始时间",
  234. value: res.startTime,
  235. },
  236. {
  237. label: "结束时间",
  238. value: endFormat,
  239. },
  240. {
  241. label: "累计用时",
  242. value: time,
  243. },
  244. ];
  245. if (res.soc) {
  246. _chargeInfo.unshift({
  247. label: "剩余电量",
  248. value: `${res.soc}%`,
  249. });
  250. }
  251. chargeInfo.value = _chargeInfo;
  252. cancelCharge(options.value && options.value.sn ? options.value.sn : "")
  253. .then(() => {
  254. uni.hideLoading();
  255. if (res.failReason) {
  256. uni.showModal({
  257. content: res.failReason,
  258. showCancel: false,
  259. success() {
  260. uni.navigateBack();
  261. },
  262. });
  263. return;
  264. }
  265. step.value = 3;
  266. })
  267. .catch((err) => {
  268. console.log(err);
  269. uni.hideLoading();
  270. uni.showToast({
  271. title: "已取消充电",
  272. icon: "none",
  273. });
  274. setTimeout(() => {
  275. uni.navigateBack();
  276. }, 2000);
  277. });
  278. }
  279. })
  280. .catch((err) => {
  281. console.log(err);
  282. uni.hideLoading();
  283. uni.showModal({
  284. title: "温馨提示",
  285. content: "出现错误,请重试",
  286. showCancel: false,
  287. confirmText: "重试",
  288. confirmColor: "#347DFF",
  289. success: () => {
  290. finish();
  291. },
  292. });
  293. });
  294. };
  295. const setChargeData = (res: any) => {
  296. const start = new Date(res.startTime.replace(/-/g, "/"));
  297. const end = new Date();
  298. const diff = parseInt(`${(end.getTime() - start.getTime()) / 1000}`);
  299. const min = parseInt(`${diff / 60}`);
  300. const time =
  301. min >= 60
  302. ? `${parseInt(`${min / 60}`)}小时${parseInt(
  303. `${min - parseInt(`${min / 60}`) * 60}`
  304. )}分钟`
  305. : `${parseInt(`${diff / 60}`)}分钟`;
  306. const totalMoney = (res.totalMoney / 100).toFixed(2);
  307. const _chargeInfo = [
  308. {
  309. label: "累计充电量",
  310. value: `${res.totalPower}度`,
  311. },
  312. {
  313. label: "累计费用",
  314. value: `${totalMoney}元`,
  315. },
  316. {
  317. label: "开始时间",
  318. value: res.startTime,
  319. },
  320. {
  321. label: "累计用时",
  322. value: time,
  323. },
  324. ];
  325. if (res.soc) {
  326. _chargeInfo.unshift({
  327. label: "剩余电量",
  328. value: `${res.soc}%`,
  329. });
  330. }
  331. chargeInfo.value = _chargeInfo;
  332. };
  333. const fetchStatus = (data?: any) => {
  334. if (status.value.cancel) {
  335. return;
  336. }
  337. const promise = data ? Promise.resolve(data) : fetchChargeStatus();
  338. promise
  339. .then((res) => {
  340. if ([1, 2].includes(res.chargeStatus)) {
  341. // 当前充电中
  342. status.value.start = true;
  343. step.value = 2;
  344. setChargeData(res);
  345. startStatusTimer();
  346. }
  347. })
  348. .catch((err) => {
  349. console.log(err);
  350. setTimeout(() => {
  351. fetchStatus(data);
  352. }, 1000);
  353. });
  354. };
  355. const onImgLoad = () => {
  356. fetchChargeStatus()
  357. .then((res) => {
  358. if (res && res.isBooking === 1) {
  359. to(`/pages-charge/appointment/appointment?sn=${options.value.sn}`);
  360. return;
  361. }
  362. if ([1, 2].includes(res.chargeStatus)) {
  363. if (options.value && options.value.sn === res.connectorId) {
  364. fetchStatus(res);
  365. return;
  366. }
  367. // 当前充电中
  368. uni.showModal({
  369. title: "温馨提示",
  370. content: "当前有正在充电中的订单",
  371. showCancel: false,
  372. confirmText: "查看订单",
  373. confirmColor: "#347DFF",
  374. success: () => {
  375. fetchStatus(res);
  376. },
  377. });
  378. } else {
  379. start();
  380. }
  381. })
  382. .catch((err) => {
  383. console.log(err);
  384. start();
  385. });
  386. };
  387. onLoad((_options: any) => {
  388. options.value = _options;
  389. });
  390. onHide(() => {
  391. timer && clearTimeout(timer);
  392. statusTimer && clearTimeout(statusTimer);
  393. });
  394. onShow(() => {
  395. startStartTimer();
  396. });
  397. </script>
  398. <style lang="scss">
  399. .container {
  400. height: 100vh;
  401. width: 100%;
  402. background-color: #fff;
  403. position: relative;
  404. overflow: hidden;
  405. .bg {
  406. width: 100%;
  407. position: absolute;
  408. left: 50%;
  409. top: 50%;
  410. transform: translate(-50%, -50%);
  411. transform-origin: center;
  412. }
  413. .body {
  414. position: absolute;
  415. left: 0;
  416. top: 0;
  417. height: 100%;
  418. width: 100%;
  419. .iphonex-placeholder {
  420. box-sizing: content-box;
  421. padding-bottom: constant(safe-area-inset-bottom);
  422. padding-bottom: env(safe-area-inset-bottom);
  423. }
  424. .status {
  425. width: 420rpx;
  426. height: 420rpx;
  427. border-radius: 50%;
  428. position: relative;
  429. margin: 0 auto;
  430. margin-top: 130rpx;
  431. .border {
  432. width: 100%;
  433. height: 100%;
  434. }
  435. .border-animation {
  436. animation: rot 3s linear infinite;
  437. }
  438. .timer {
  439. position: absolute;
  440. width: 340rpx;
  441. height: 340rpx;
  442. left: 50%;
  443. top: 50%;
  444. transform: translate(-50%, -50%);
  445. background: linear-gradient(180deg, #ffffff 0%, #ecf3ff 100%);
  446. box-shadow: 0px 18rpx 30rpx rgba(0, 0, 0, 0.1);
  447. border-radius: 50%;
  448. .icon {
  449. width: 152rpx;
  450. height: 152rpx;
  451. }
  452. }
  453. }
  454. }
  455. }
  456. @keyframes rot {
  457. 0% {
  458. transform: rotate(0deg);
  459. }
  460. 100% {
  461. transform: rotate(360deg);
  462. }
  463. }
  464. </style>