ordering.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  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 (res.isStarted) {
  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 (res.isStarted) {
  210. // 当前充电中
  211. const start = res.startTime
  212. ? new Date(res.startTime.replace(/-/g, "/"))
  213. : new Date();
  214. const end = new Date();
  215. const diff = parseInt(`${(end.getTime() - start.getTime()) / 1000}`);
  216. const min = parseInt(`${diff / 60}`);
  217. const time =
  218. min >= 60
  219. ? `${parseInt(`${min / 60}`)}小时${parseInt(
  220. `${min - parseInt(`${min / 60}`) * 60}`
  221. )}分钟`
  222. : `${parseInt(`${diff / 60}`)}分钟`;
  223. const endFormat = format("y-M-d h:m:s");
  224. const totalMoney = (res.totalMoney / 100).toFixed(2);
  225. const _chargeInfo = [
  226. {
  227. label: "累计充电量",
  228. value: `${res.totalPower}度`,
  229. },
  230. {
  231. label: "累计费用",
  232. value: `${totalMoney}元`,
  233. },
  234. {
  235. label: "开始时间",
  236. value: res.startTime || format("y-M-d h:m:s"),
  237. },
  238. {
  239. label: "结束时间",
  240. value: endFormat,
  241. },
  242. {
  243. label: "累计用时",
  244. value: time,
  245. },
  246. ];
  247. if (res.soc) {
  248. _chargeInfo.unshift({
  249. label: "剩余电量",
  250. value: `${res.soc}%`,
  251. });
  252. }
  253. chargeInfo.value = _chargeInfo;
  254. cancelCharge(options.value && options.value.sn ? options.value.sn : "")
  255. .then(() => {
  256. uni.hideLoading();
  257. if (res.failReason) {
  258. uni.showModal({
  259. content: res.failReason,
  260. showCancel: false,
  261. success() {
  262. uni.navigateBack();
  263. },
  264. });
  265. return;
  266. }
  267. setTimeout(() => {
  268. step.value = 3;
  269. }, 3000);
  270. })
  271. .catch((err) => {
  272. console.log(err);
  273. uni.hideLoading();
  274. uni.showToast({
  275. title: "已停止充电",
  276. icon: "none",
  277. });
  278. setTimeout(() => {
  279. uni.navigateBack();
  280. }, 2000);
  281. });
  282. }
  283. })
  284. .catch((err) => {
  285. console.log(err);
  286. uni.hideLoading();
  287. uni.showModal({
  288. title: "温馨提示",
  289. content: "出现错误,请重试",
  290. showCancel: false,
  291. confirmText: "重试",
  292. confirmColor: "#347DFF",
  293. success: () => {
  294. finish();
  295. },
  296. });
  297. });
  298. };
  299. const setChargeData = (res: any) => {
  300. const start = res.startTime
  301. ? new Date(res.startTime.replace(/-/g, "/"))
  302. : new Date();
  303. const end = new Date();
  304. const diff = parseInt(`${(end.getTime() - start.getTime()) / 1000}`);
  305. const min = parseInt(`${diff / 60}`);
  306. const time =
  307. min >= 60
  308. ? `${parseInt(`${min / 60}`)}小时${parseInt(
  309. `${min - parseInt(`${min / 60}`) * 60}`
  310. )}分钟`
  311. : `${parseInt(`${diff / 60}`)}分钟`;
  312. const totalMoney = (res.totalMoney / 100).toFixed(2);
  313. const _chargeInfo = [
  314. {
  315. label: "累计充电量",
  316. value: `${res.totalPower}度`,
  317. },
  318. {
  319. label: "累计费用",
  320. value: `${totalMoney}元`,
  321. },
  322. {
  323. label: "开始时间",
  324. value: res.startTime || format("y-M-d h:m:s"),
  325. },
  326. {
  327. label: "累计用时",
  328. value: time,
  329. },
  330. ];
  331. if (res.soc) {
  332. _chargeInfo.unshift({
  333. label: "剩余电量",
  334. value: `${res.soc}%`,
  335. });
  336. }
  337. chargeInfo.value = _chargeInfo;
  338. };
  339. const fetchStatus = (data?: any) => {
  340. if (status.value.cancel) {
  341. return;
  342. }
  343. const promise = data ? Promise.resolve(data) : fetchChargeStatus();
  344. promise
  345. .then((res) => {
  346. if (res.isStarted) {
  347. // 当前充电中
  348. status.value.start = true;
  349. step.value = 2;
  350. setChargeData(res);
  351. startStatusTimer();
  352. }
  353. })
  354. .catch((err) => {
  355. console.log(err);
  356. setTimeout(() => {
  357. fetchStatus(data);
  358. }, 1000);
  359. });
  360. };
  361. const onImgLoad = () => {
  362. fetchChargeStatus()
  363. .then((res) => {
  364. if (res && res.isAppointment) {
  365. to(`/pages-charge/appointment/appointment?sn=${options.value.sn}`);
  366. return;
  367. }
  368. if (res && res.isStarted) {
  369. if (options.value && options.value.sn === res.connectorId) {
  370. fetchStatus(res);
  371. return;
  372. }
  373. // 当前充电中
  374. uni.showModal({
  375. title: "温馨提示",
  376. content: "当前有正在充电中的订单",
  377. showCancel: false,
  378. confirmText: "查看订单",
  379. confirmColor: "#347DFF",
  380. success: () => {
  381. fetchStatus(res);
  382. },
  383. });
  384. } else {
  385. start();
  386. }
  387. })
  388. .catch((err) => {
  389. console.log(err);
  390. start();
  391. });
  392. };
  393. onLoad((_options: any) => {
  394. options.value = _options;
  395. });
  396. onHide(() => {
  397. timer && clearTimeout(timer);
  398. statusTimer && clearTimeout(statusTimer);
  399. });
  400. onShow(() => {
  401. startStartTimer();
  402. });
  403. </script>
  404. <style lang="scss">
  405. .container {
  406. height: 100vh;
  407. width: 100%;
  408. background-color: #fff;
  409. position: relative;
  410. overflow: hidden;
  411. .bg {
  412. width: 100%;
  413. position: absolute;
  414. left: 50%;
  415. top: 50%;
  416. transform: translate(-50%, -50%);
  417. transform-origin: center;
  418. }
  419. .body {
  420. position: absolute;
  421. left: 0;
  422. top: 0;
  423. height: 100%;
  424. width: 100%;
  425. .iphonex-placeholder {
  426. box-sizing: content-box;
  427. padding-bottom: constant(safe-area-inset-bottom);
  428. padding-bottom: env(safe-area-inset-bottom);
  429. }
  430. .status {
  431. width: 420rpx;
  432. height: 420rpx;
  433. border-radius: 50%;
  434. position: relative;
  435. margin: 0 auto;
  436. margin-top: 130rpx;
  437. .border {
  438. width: 100%;
  439. height: 100%;
  440. }
  441. .border-animation {
  442. animation: rot 3s linear infinite;
  443. }
  444. .timer {
  445. position: absolute;
  446. width: 340rpx;
  447. height: 340rpx;
  448. left: 50%;
  449. top: 50%;
  450. transform: translate(-50%, -50%);
  451. background: linear-gradient(180deg, #ffffff 0%, #ecf3ff 100%);
  452. box-shadow: 0px 18rpx 30rpx rgba(0, 0, 0, 0.1);
  453. border-radius: 50%;
  454. .icon {
  455. width: 152rpx;
  456. height: 152rpx;
  457. }
  458. }
  459. }
  460. }
  461. }
  462. @keyframes rot {
  463. 0% {
  464. transform: rotate(0deg);
  465. }
  466. 100% {
  467. transform: rotate(360deg);
  468. }
  469. }
  470. </style>