| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711 |
- <template>
- <view class="page">
- <template v-if="data && priceInfo">
- <view class="block">
- <view class="station">
- <ChargeMachine
- :title="'NO.' + data.equipment.shortId"
- :list="data.equipment.connectorInfos"
- :time="priceInfo.useTime"
- :parkingNo="data.equipment.parkingNo"
- ></ChargeMachine>
- </view>
- <view class="pt-20 pb-20 pl-30 pr-30 flex-align-center">
- <image
- src="/pages-charge/static/machines-banner-address.png"
- mode="widthFix"
- class="flex-shrink mr-12 width-40"
- />
- <view class="fs-26 color-666">{{ data.station.address }}</view>
- </view>
- </view>
- <view class="mt-40 color-999 fs-32 fw-500">费用说明</view>
- <view
- class="mt-20 block height-96 flex-align-center pl-30 pr-30"
- @click="openPriceDesc"
- >
- <view class="fs-26 color-666">充电费用</view>
- <view class="ml-64 fs-26 color-333"
- >{{ priceInfo.minPrice }}~{{ priceInfo.maxPrice }}元/度</view
- >
- <view class="ml-auto">
- <uni-icons type="right" size="16" color="rgba(0,0,0,0.4)"></uni-icons>
- </view>
- </view>
- <template v-if="appointmentData">
- <view class="mt-40 color-999 fs-32 fw-500">已预约充电</view>
- <view class="mt-20 block flex-align-center" style="height: 180rpx">
- <image
- class="width-56 ml-30"
- src="/pages-charge/static/icon-alarm.png"
- mode="widthFix"
- ></image>
- <view class="ml-24">
- <view class="flex" v-if="appointmentCountDown">
- <view class="fs-28 color-000">将在</view>
- <view class="fs-44 lh-36 color-primary ml-8 mr-8">{{
- appointmentCountDown
- }}</view>
- <view class="fs-28 color-000">后开始充电</view>
- </view>
- <view class="fs-24 color-999 mt-8"
- >请在{{ appointmentData.startTime }}前到达充电桩并开始充电</view
- >
- </view>
- </view>
- <view class="mt-40 flex-center">
- <view
- class="flex-center height-68 br-68 fs-26 color-666"
- style="width: 184rpx; border: 1rpx solid rgba(0, 0, 0, 0.3)"
- @click="cancelAppointment"
- >取消预约</view
- >
- </view>
- </template>
- <template v-else>
- <view class="mt-40 color-999 fs-32 fw-500">选择充电方式</view>
- <view class="mt-20 block pl-30 pr-30">
- <view
- v-for="(type, index) in chargeTypes"
- :key="index"
- :class="['pt-40', 'pb-40', 'flex-align-center']"
- :style="{
- borderTop: index === 0 ? '' : '1rpx solid rgba(0, 0, 0, 0.10)',
- }"
- @click="changeType(index)"
- >
- <view>
- <view class="fs-28 lh-28 color-000 fw-500">{{ type.title }}</view>
- <view class="fs-24 color-999 lh-24 mt-16" v-if="type.tip">{{
- type.tip
- }}</view>
- </view>
- <view class="ml-auto">
- <style-checkbox :checked="chargeType === index"></style-checkbox>
- </view>
- </view>
- </view>
- <view class="mt-20 block pl-30 pr-30" v-if="chargeType === 0">
- <view
- class="pt-40 pb-40 flex-between"
- style="border-bottom: 1rpx solid rgba(0, 0, 0, 0.1)"
- @click="selectTime"
- >
- <view class="fs-28 color-000 fw-500">充电时间</view>
- <view class="flex-align-center lh-28">
- <view
- v-if="chargeTime.format"
- class="fs-28 color-333 fw-500 mr-16"
- >{{ chargeTime.format }}</view
- >
- <view v-else class="fs-26 mr-16" style="color: #cacaca"
- >请选择</view
- >
- <uni-icons type="right" size="16" color="#CACACA"></uni-icons>
- </view>
- </view>
- <view class="pt-40 pb-40 flex-between">
- <view class="fs-28 color-000 fw-500">预计费用</view>
- <view class="flex-align-center lh-28">
- <view
- v-if="chargeTime.formatPrice"
- class="fs-28 color-333 fw-500"
- >{{ chargeTime.formatPrice }}</view
- >
- <view v-else class="fs-26 mr-16" style="color: #cacaca"
- >选择时间后显示</view
- >
- </view>
- </view>
- </view>
- <view
- class="mt-20 block flex-align-center pl-30 pr-30 pt-30 pb-30"
- v-if="chargeType === 0"
- >
- <view class="flex-column">
- <view class="fs-28 lh-28 color-000 fw-500">省钱模式</view>
- <view class="fs-24 lh-30 color-999 mt-8"
- >处于00:00-08:00时,电费低至{{ priceInfo.minPrice }}元/kwh</view
- >
- </view>
- <view class="ml-auto">
- <switch
- :checked="chargeTime.isPowerSaving"
- color="#347DFF"
- @change="changePowerSaving"
- />
- </view>
- </view>
- </template>
- <view class="pt-60 pb-80"></view>
- <style-bottom-view>
- <view class="pt-20 pl-40 pr-40 pb-20 bg-fff flex">
- <template v-if="appointmentData">
- <view class="mr-10" style="width: 50%">
- <style-button size="small" @click="selectTime"
- >修改时间</style-button
- ></view
- >
- <view class="ml-10" style="width: 50%">
- <style-button type="primary" size="small" @click="submitNow"
- >立即开始</style-button
- >
- </view>
- </template>
- <style-button v-else type="primary" size="small" @click="submit">{{
- chargeType === 0 ? "提交预约" : "马上充电"
- }}</style-button>
- </view>
- </style-bottom-view>
- </template>
- <style-dialog
- v-if="chargeTimeDialog"
- @close="closeTime"
- title="请选择充电时间"
- >
- <view class="flex" style="border-top: 1rpx solid rgba(0, 0, 0, 0.1)">
- <view
- class="flex-shrink"
- style="height: 840rpx; width: 256rpx; background-color: #f0f0f0"
- >
- <view
- class="flex-align-center pl-32 height-90"
- v-for="(day, index) in timesInfo.day"
- :key="index"
- :style="{
- backgroundColor:
- day.format === chargeTime.day ? '#fff' : 'transparent',
- }"
- @click="changeDay(index)"
- >
- <view
- :class="[
- 'fs-32',
- 'fw-500',
- day.format === chargeTime.day ? 'color-primary' : 'color-000',
- ]"
- >{{ day.format }}</view
- >
- <view
- class="fs-24 color-666 ml-12 br-8 width-60 height-40 flex-center"
- style="background-color: rgba(0, 0, 0, 0.07)"
- v-if="day.year && day.format !== chargeTime.day"
- >{{ day.year }}年</view
- >
- </view>
- </view>
- <scroll-view
- scroll-y
- class="flex-grow"
- style="height: 840rpx"
- :scroll-into-view="chargeTime.scrollId"
- >
- <view
- :id="`hour-${hour.format}`"
- class="flex-align-center pl-40 height-90 fs-32"
- v-for="(hour, index) in timesInfo.hour"
- :key="index"
- :style="{
- color: hour.disabled
- ? '#999'
- : hour.format === chargeTime.hour
- ? 'var(--color-primary)'
- : '#000',
- }"
- @click="changeHour(index)"
- >
- {{ hour.format }}
- </view>
- </scroll-view>
- </view>
- </style-dialog>
- <PriceDesc
- v-if="priceDescVisible"
- :desc="priceInfo.policyInfos || []"
- @close="closePriceDesc"
- ></PriceDesc>
- </view>
- </template>
- <script setup lang="ts">
- import {
- fetchChargeStatus,
- fetchStationByConnectorIdOrShortId,
- fetchStationPriceDesc,
- startCharge,
- cancelAppointmentCharge,
- changeAppointmentTime,
- } from "@/api/charge";
- import { onLoad } from "@dcloudio/uni-app";
- import { ref } from "vue";
- import ChargeMachine from "../machines/charge-machine/charge-machine.vue";
- import PriceDesc from "../machines/price-desc/price-desc.vue";
- import { format } from "@/utils/date";
- const DAY = 24 * 60 * 60 * 1000;
- const options = ref<any>();
- const data = ref<any>();
- const priceInfo = ref();
- const priceDescVisible = ref(false);
- const timesInfo = ref<any>({
- day: [],
- time: [],
- });
- const chargeType = ref(1);
- const chargeTypes = ref([
- {
- title: "预约充电",
- tip: "预约特定时间点开始充电",
- },
- {
- title: "立即充电",
- },
- ]);
- const chargeTimeDialog = ref(false);
- const chargeTime = ref({
- time: 0, // 时间戳
- day: "",
- hour: "",
- format: "",
- formatPrice: "",
- scrollId: "",
- isPowerSaving: false, // 省电模式
- });
- const appointmentData = ref();
- const appointmentCountDown = ref();
- const changeType = (index: number) => {
- chargeType.value = index;
- };
- const isPassTime = (hour: string, time: number) => {
- const now = new Date();
- const _hour = Number(hour.substring(0, 2));
- if (typeof _hour !== "number") {
- return true;
- }
- return now.getHours() >= _hour && now.getDate() === Number(format("d", time));
- };
- const getHourPrice = (hour: string) => {
- let price = "";
- priceInfo.value.policyInfos.forEach((item: any) => {
- if (Number(hour) >= Number(item.startTime.substring(0, 2))) {
- price = item.totalPrice;
- }
- });
- return price;
- };
- const rebuildHours = (now: Date) => {
- timesInfo.value.hour = [];
- let hourTemp = "";
- let priceTemp = "";
- for (let index = 0; index < 24; index++) {
- hourTemp = index >= 10 ? `${index}` : `0${index}`;
- priceTemp = getHourPrice(hourTemp);
- timesInfo.value.hour.push({
- format: `${hourTemp}:00`,
- formatPrice: priceTemp,
- disabled: isPassTime(
- `${hourTemp}:00`,
- chargeTime.value.time ? chargeTime.value.time : now.getTime()
- ),
- });
- timesInfo.value.hour.push({
- format: `${hourTemp}:30`,
- formatPrice: priceTemp,
- disabled: isPassTime(
- `${hourTemp}:30`,
- chargeTime.value.time ? chargeTime.value.time : now.getTime()
- ),
- });
- }
- };
- const selectTime = () => {
- if (!appointmentData.value && chargeType.value === 1) {
- uni.showToast({
- title: "请选择预约模式",
- icon: "none",
- });
- return;
- }
- const now = appointmentData.value
- ? new Date(appointmentData.value.startTime.replace(/-/g, "/"))
- : new Date();
- const year = format("y", now.getTime());
- const nextyear = format("y", now.getTime() + DAY);
- // 天
- timesInfo.value.day = [
- {
- time: now.getTime(),
- format: format("M月d日", now.getTime()),
- year: "",
- },
- {
- time: now.getTime() + DAY,
- format: format("M月d日", now.getTime() + DAY),
- year: year === nextyear ? "" : nextyear.slice(2),
- },
- ];
- // 时间
- // timesInfo.value.hour = priceInfo.value.policyInfos.map((item: any) => {
- // return {
- // format: item.startTime,
- // formatPrice: item.totalPrice,
- // disabled: isPassTime(
- // item.startTime,
- // chargeTime.value.time ? chargeTime.value.time : now.getTime()
- // ),
- // };
- // });
- rebuildHours(now);
- // 预约时间
- if (!appointmentData.value) {
- if (!chargeTime.value.day) {
- chargeTime.value.time = timesInfo.value.day[0].time;
- chargeTime.value.day = timesInfo.value.day[0].format;
- }
- // 滚动到特定位置
- if (chargeTime.value.hour) {
- chargeTime.value.scrollId = `hour-${chargeTime.value.hour}`;
- } else {
- let hourNow = now.getHours();
- chargeTime.value.scrollId = `hour-${hourNow}:00`;
- }
- }
- // 修改时间
- if (appointmentData.value) {
- chargeTime.value.time = timesInfo.value.day[0].time;
- chargeTime.value.day = timesInfo.value.day[0].format;
- const findIndex = timesInfo.value.hour.findIndex(
- (item: any) => item.format === format("h:m:s", now.getTime())
- );
- if (findIndex >= 0) {
- chargeTime.value.hour = timesInfo.value.hour[findIndex].format;
- chargeTime.value.formatPrice =
- timesInfo.value.hour[findIndex].formatPrice;
- }
- }
- chargeTimeDialog.value = true;
- };
- const changeDay = (index: number) => {
- chargeTime.value.time = timesInfo.value.day[index].time;
- chargeTime.value.day = timesInfo.value.day[index].format;
- if (chargeTime.value.hour) {
- chargeTime.value.hour = "";
- chargeTime.value.formatPrice = "";
- }
- const now = new Date(chargeTime.value.time);
- rebuildHours(now);
- chargeTime.value.scrollId = "";
- };
- const changeHour = (index: number) => {
- if (timesInfo.value.hour[index].disabled) {
- uni.showToast({
- title: "无法选择过去的时间",
- icon: "none",
- });
- return;
- }
- if (appointmentData.value) {
- // 修改时间
- uni.showModal({
- title: "温馨提示",
- content: `确认修改为${timesInfo.value.hour[index].format}吗?`,
- confirmText: "确定",
- confirmColor: "#347DFF",
- success(modal) {
- if (modal.confirm) {
- uni.showLoading({
- title: "提交中",
- mask: true,
- });
- changeAppointmentTime(
- appointmentData.value.startChargeSeq,
- `${format("y-M-d", chargeTime.value.time)} ${
- timesInfo.value.hour[index].format
- }`
- )
- .then(() => {
- return fetchChargeStatus();
- })
- .then((res) => {
- uni.hideLoading();
- if (res && res.isBooking === 1) {
- uni.showToast({
- icon: "success",
- title: "修改成功",
- });
- closeTime();
- appointmentData.value = res;
- startAppointmentCountDown();
- }
- });
- }
- },
- });
- return;
- }
- chargeTime.value.hour = timesInfo.value.hour[index].format;
- chargeTime.value.format = `${format("y年M月d日", chargeTime.value.time)} ${
- chargeTime.value.hour
- }`;
- chargeTime.value.formatPrice = timesInfo.value.hour[index].formatPrice;
- closeTime();
- };
- const changePowerSaving = (e: any) => {
- chargeTime.value.isPowerSaving = e.detail.value;
- };
- const closeTime = () => {
- chargeTimeDialog.value = false;
- if (appointmentData.value) {
- return;
- }
- if (!chargeTime.value.hour) {
- chargeTime.value = {
- time: 0, // 时间戳
- day: "",
- hour: "",
- isPowerSaving: chargeTime.value.isPowerSaving, // 省电模式
- format: "",
- formatPrice: "",
- scrollId: "",
- };
- }
- };
- const openPriceDesc = () => {
- priceDescVisible.value = true;
- };
- const closePriceDesc = () => {
- priceDescVisible.value = false;
- };
- const cancelAppointment = () => {
- uni.showModal({
- title: "取消预约",
- content: "确定取消预约吗?",
- cancelText: "取消预约",
- cancelColor: "#999999",
- confirmText: "点错了",
- confirmColor: "#347DFF",
- success: (res) => {
- if (res.cancel) {
- uni.showLoading({
- title: "提交中",
- mask: true,
- });
- cancelAppointmentCharge()
- .then(() => {
- uni.hideLoading();
- uni.showToast({
- icon: "success",
- title: "已取消",
- });
- appointmentData.value = undefined;
- startAppointmentCountDown();
- })
- .catch((err) => {
- uni.hideLoading();
- });
- }
- },
- });
- };
- let appointmentCountDownTimer = 0;
- const startAppointmentCountDown = () => {
- appointmentCountDownTimer && clearTimeout(appointmentCountDownTimer);
- if (!appointmentData.value) {
- appointmentCountDown.value = "";
- return;
- }
- appointmentCountDown.value = formatAppointmentCountDown(
- appointmentData.value.startTime
- );
- appointmentCountDownTimer = setTimeout(() => {
- startAppointmentCountDown();
- }, 1000);
- };
- const formatAppointmentCountDown = (time: string) => {
- const now = new Date();
- const date = new Date(time.replace(/-/g, "/"));
- let secondTime = parseInt(`${(date.getTime() - now.getTime()) / 1000}`);
- let hourTime = 0;
- let minuteTime = 0; // 分
- if (secondTime >= 60) {
- minuteTime = parseInt(`${secondTime / 60}`);
- secondTime = parseInt(`${secondTime % 60}`);
- if (minuteTime >= 60) {
- hourTime = parseInt(`${minuteTime / 60}`);
- minuteTime = parseInt(`${minuteTime % 60}`);
- }
- }
- return `${hourTime < 10 ? "0" + hourTime : hourTime}:${
- minuteTime < 10 ? "0" + minuteTime : minuteTime
- }:${secondTime < 10 ? "0" + secondTime : secondTime}`;
- };
- const submit = () => {
- if (chargeType.value === 0 && !chargeTime.value.hour) {
- uni.showToast({
- title: "请选择充电时间",
- icon: "none",
- });
- return;
- }
- const now = new Date();
- let startTime = chargeTime.value.time;
- if (chargeTime.value.isPowerSaving) {
- // 省电模式
- if (
- now.getHours() >= 8 &&
- now.getDate() === Number(format("d", chargeTime.value.time))
- ) {
- startTime += DAY;
- }
- }
- let query = "";
- if (chargeType.value === 0) {
- query += "isBooking=true";
- query += `&startTime=${format("y-M-d", startTime)} ${
- chargeTime.value.isPowerSaving ? "00:00:00" : `${chargeTime.value.hour}:00`
- }`;
- if (chargeTime.value.isPowerSaving) {
- query += `&endTime=${format("y-M-d", startTime)} 08:00:00`;
- }
- }
- if (chargeType.value === 1) {
- query += "isBooking=false";
- }
- console.log(query);
- uni.showLoading({
- title: "提交中",
- mask: true,
- });
- startCharge(options.value.sn, query)
- .then(() => {
- if (chargeType.value === 0) {
- fetchChargeStatus()
- .then((res) => {
- if (res && res.isBooking === 1) {
- uni.hideLoading();
- uni.showToast({
- icon: "success",
- title: "预约成功",
- });
- appointmentData.value = res;
- startAppointmentCountDown();
- }
- })
- .catch(() => {
- uni.navigateBack();
- });
- }
- if (chargeType.value === 1) {
- uni.navigateTo({
- url: `/pages-charge/ordering/ordering?sn=${options.value.sn}&start=1`,
- });
- }
- })
- .catch((err) => {
- uni.hideLoading();
- uni.showModal({
- content: `${err.errMsg}`,
- showCancel: false,
- });
- });
- };
- const submitNow = () => {
- uni.showLoading({
- title: "提交中",
- mask: true,
- });
- cancelAppointmentCharge()
- .then(() => {
- return startCharge(options.value.sn, "isBooking=false");
- })
- .then(() => {
- uni.navigateTo({
- url: `/pages-charge/ordering/ordering?sn=${options.value.sn}&start=1`,
- });
- })
- .catch((err) => {
- uni.hideLoading();
- uni.showModal({
- content: `${err.errMsg}`,
- showCancel: false,
- });
- });
- };
- onLoad((_options: any) => {
- // sn=SN100523042860091 测试环境
- console.log("options", _options);
- let sn = _options.sn;
- uni.showLoading({
- title: "加载中",
- });
- fetchStationByConnectorIdOrShortId(sn)
- .then((res) => {
- console.log(res);
- if (
- res.equipment &&
- res.equipment.connectorInfos &&
- res.equipment.connectorInfos.length
- ) {
- sn = res.equipment.connectorInfos[0].connectorId;
- _options.sn = res.equipment.connectorInfos[0].connectorId;
- }
- options.value = _options;
- data.value = res;
- return fetchStationPriceDesc(sn);
- })
- .then((res) => {
- // console.log(res);
- uni.hideLoading();
- priceInfo.value = res;
- return fetchChargeStatus(false, true);
- })
- .then((res) => {
- if (res && res.isBooking === 1) {
- appointmentData.value = res;
- startAppointmentCountDown();
- }
- })
- .catch((err) => {
- console.log(err);
- uni.hideLoading();
- });
- });
- </script>
- <style lang="scss" scoped>
- .page {
- min-height: 100vh;
- width: 100%;
- padding: 20rpx;
- background-color: #f6f7fa;
- }
- .block {
- border-radius: 20rpx;
- background: #fff;
- }
- .station {
- height: 148rpx;
- border-bottom: 1rpx solid rgba(0, 0, 0, 0.1);
- }
- </style>
|