hookehuyr

fix(tools): 修复时间格式化函数处理时区的问题

重构 formatDatetime 函数,增加对时区后缀的处理,确保时间显示为当地时间
统一使用 dayjs 进行时间格式化,提高代码可维护性
<!--
* @Date: 2024-01-15 16:25:51
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2026-01-12 22:23:03
* @LastEditTime: 2026-01-16 15:12:09
* @FilePath: /xyxBooking-weapp/src/pages/submit/index.vue
* @Description: 预约人员信息
-->
......@@ -9,10 +9,15 @@
<view class="submit-page">
<view @tap="goToBooking" class="visit-time">
<view>参访时间</view>
<view><text style="font-size: 30rpx;">{{ date }} {{ time }}</text>&nbsp;<IconFont name="rect-right" /></view>
<view class="flex items-center">
<text style="font-size: 30rpx;">{{ date }} {{ time }}</text>
<IconFont name="rect-right" class="ml-1" />
</view>
</view>
<view @tap="goToVisitor" class="add-visitors">
<view><IconFont name="plus" /> 添加参观者</view>
<view>
<IconFont name="plus" /> 添加参观者
</view>
</view>
<view v-if="visitorList.length" class="visitors-list">
<view v-for="(item, index) in visitorList" :key="index" @tap="addVisitor(item)" class="visitor-item">
......@@ -23,12 +28,14 @@
<view>
<view style="color: #A67939;">{{ item.name }}</view>
<view>证件号:{{ formatId(item.id_number) }}</view>
<view v-if="item.is_reserve === RESERVE_STATUS.ENABLE" style="color: #9C9A9A; font-size: 26rpx;">*已预约过{{ date }}参观,请不要重复预约</view>
<view v-if="item.is_reserve === RESERVE_STATUS.ENABLE" style="color: #9C9A9A; font-size: 26rpx;">*已预约过{{ date
}}参观,请不要重复预约</view>
</view>
</view>
</view>
<view v-else class="no-visitors-list">
<image src="https://cdn.ipadbiz.cn/xys/booking/%E6%9A%82%E6%97%A0@2x.png" style="width: 320rpx; height: 320rpx;" />
<image src="https://cdn.ipadbiz.cn/xys/booking/%E6%9A%82%E6%97%A0@2x.png"
style="width: 320rpx; height: 320rpx;" />
<view class="no-visitors-list-title">您还没有添加过参观者</view>
</view>
<view style="height: 160rpx;"></view>
......@@ -36,7 +43,9 @@
<view class="control-wrapper">
<view class="left">
<view style="margin-left: 32rpx; display: flex;align-items: center;">
订单金额&nbsp;&nbsp;<view style="color: #FF1919;display: inline-block;">¥<view style="font-size: 48rpx;display: inline-block;">&nbsp;{{ total }}</view></view>
订单金额&nbsp;&nbsp;<view style="color: #FF1919;display: inline-block;">¥<view
style="font-size: 48rpx;display: inline-block;">&nbsp;{{ total }}</view>
</view>
</view>
</view>
<view @tap="submitBtn" class="right">提交订单</view>
......@@ -157,10 +166,10 @@ const submitBtn = async () => {
package: pay_params.package,
signType: pay_params.signType,
paySign: pay_params.paySign,
success (res) {
success(res) {
go('/success', { pay_id });
},
fail (res) {
fail(res) {
// 支付取消或失败,保留 pending_pay_id,允许用户再次点击按钮尝试支付同一订单
// 只有当 wxPayAPI 获取支付参数失败时(如下面的 else 分支),才重置 ID 以便重新创建订单
Taro.showToast({ title: '支付失败,请重试', icon: 'none' });
......@@ -195,6 +204,7 @@ useDidShow(async () => {
.submit-page {
margin: 32rpx;
position: relative;
.visit-time {
background-color: #FFF;
display: flex;
......@@ -203,6 +213,7 @@ useDidShow(async () => {
padding: 24rpx;
border-radius: 16rpx;
}
.add-visitors {
border: 2rpx dashed #A67939;
color: #A67939;
......@@ -212,6 +223,7 @@ useDidShow(async () => {
margin: 32rpx 0;
font-size: 37rpx;
}
.visitors-list {
.visitor-item {
background-color: #FFF;
......@@ -222,21 +234,25 @@ useDidShow(async () => {
align-items: center;
}
}
.no-visitors-list {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
img {
margin-top: 32rpx;
margin-bottom: 32rpx;
width: 320rpx;
}
.no-visitors-list-title {
color: #A67939;
font-size: 34rpx;
}
}
.submit-wrapper {
position: fixed;
bottom: 0;
......@@ -247,19 +263,21 @@ useDidShow(async () => {
// padding: 32rpx;
justify-content: space-between;
flex-direction: column;
box-shadow: 0 -10rpx 8rpx 0 rgba(0,0,0,0.12);
box-shadow: 0 -10rpx 8rpx 0 rgba(0, 0, 0, 0.12);
.control-wrapper {
display: flex;
justify-content: space-between;
align-items: center;
}
.left {
display: flex;
justify-content: center;
align-items: center;
flex-wrap: nowrap;
}
.right {
background-color: #A67939;
color: #FFF;
......
......@@ -87,20 +87,27 @@ const strExist = (array, str) => {
*/
const formatDatetime = (data) => {
// 格式化日期
if (!data) return '';
if (!data || !data.begin_time || !data.end_time) return '';
// 截取时间字符串最后6位(去除毫秒/时区等冗余部分)
const begin_time = data?.begin_time?.slice(0, -6) || '';
const end_time = data?.end_time?.slice(0, -6) || '';
// 预处理函数:移除时区后缀,统一格式为 ISO 8601 (YYYY-MM-DDTHH:mm:ss)
// 这样做是为了让 dayjs 把它当作本地时间处理,避免时区转换导致的时间偏差(始终显示景点当地时间/字面时间)
const normalize = (timeStr) => {
if (!timeStr) return '';
// 移除 +08, +08:00 等时区后缀
let clean = timeStr.split('+')[0];
// 移除 Z 后缀
clean = clean.split('Z')[0];
// 将空格替换为 T,确保兼容性
clean = clean.trim().replace(/\s+/, 'T');
return clean;
};
// 拆分时间片段并拼接目标格式:日期 开始时间-结束时间
const [date, beginTime] = begin_time.split('T');
const [, endTime] = end_time.split('T');
const start = dayjs(normalize(data.begin_time));
const end = dayjs(normalize(data.end_time));
// 兼容时间拆分失败的情况,避免返回 NaN/-undefined 等异常
if (!date || !beginTime || !endTime) return '';
if (!start.isValid() || !end.isValid()) return '';
return `${date} ${beginTime}-${endTime}`;
return `${start.format('YYYY-MM-DD')} ${start.format('HH:mm')}-${end.format('HH:mm')}`;
};
export { formatDate, wxInfo, parseQueryString, strExist, formatDatetime };
......