836 lines
21 KiB
Vue
836 lines
21 KiB
Vue
<template>
|
||
<view class="container">
|
||
<NavBar title="修改车票信息" bgColor="#F5F5F5" isRightButton @right-click="handleRightButtonClick"></NavBar>
|
||
|
||
<scroll-view scroll-y class="form-content">
|
||
<!-- 订单信息 -->
|
||
<view class="section-container">
|
||
<view class="section-header" @click="toggleSection('orderInfo')">
|
||
<text class="section-title">订单信息</text>
|
||
<uni-icons :type="collapsed.orderInfo ? 'bottom' : 'top'" size="16" color="#666"></uni-icons>
|
||
</view>
|
||
<view class="card" v-show="!collapsed.orderInfo">
|
||
<view class="form-item">
|
||
<text class="label">订单号</text>
|
||
<input class="input" v-model="ticketsInfo.orderInfo.orderNo" />
|
||
</view>
|
||
<picker v-if="app == '12306'" mode="date" fields="day"
|
||
:value="getPickerDate(ticketsInfo.orderInfo.orderTime)" @change="onOrderTimeChange">
|
||
<view class="form-item">
|
||
<text class="label">下单时间</text>
|
||
<view class="input">{{ ticketsInfo.orderInfo.orderTime }}</view>
|
||
</view>
|
||
</picker>
|
||
<view v-if="app != '12306' && app != 'fliggy'" class="form-item">
|
||
<text class="label">订单总价</text>
|
||
<input class="input" type="number" v-model="ticketsInfo.orderInfo.price" />
|
||
</view>
|
||
<view v-if="app == 'fliggy'" class="form-item">
|
||
<text class="label">已购服务</text>
|
||
<input class="input" v-model="ticketsInfo.serviceText" />
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 车票信息 -->
|
||
<view class="section-container">
|
||
<view class="section-header" @click="toggleSection('ticketInfo')">
|
||
<text class="section-title">车票信息</text>
|
||
<uni-icons :type="collapsed.ticketInfo ? 'bottom' : 'top'" size="16" color="#666"></uni-icons>
|
||
</view>
|
||
<view class="card" v-show="!collapsed.ticketInfo">
|
||
<view class="form-item">
|
||
<text class="label">车次</text>
|
||
<input class="input" v-model="ticketsInfo.ticketInfo.trainNo" />
|
||
</view>
|
||
<picker mode="date" fields="day" :value="getPickerDate(ticketsInfo.ticketInfo.date)"
|
||
@change="onTicketDateChange">
|
||
<view class="form-item" style="border-bottom: 1rpx solid #F5F5F5;">
|
||
<text class="label">日期</text>
|
||
<view class="input">{{ ticketsInfo.ticketInfo.date }}</view>
|
||
</view>
|
||
</picker>
|
||
<view class="form-item">
|
||
<text class="label">检票口</text>
|
||
<input class="input" v-model="ticketsInfo.ticketInfo.gate" />
|
||
</view>
|
||
<view class="form-item">
|
||
<text class="label">出发站</text>
|
||
<input class="input" v-model="ticketsInfo.ticketInfo.departureStation" />
|
||
</view>
|
||
|
||
<picker mode="multiSelector" :range="departureTimeRange" :value="departureTimeIndex"
|
||
@change="onDepartureTimeChange">
|
||
<view class="form-item" style="border-bottom: 1rpx solid #F5F5F5;">
|
||
<text class="label">出发时间</text>
|
||
<view class="input">{{ ticketsInfo.ticketInfo.departureTime }}</view>
|
||
</view>
|
||
</picker>
|
||
|
||
<view class="form-item">
|
||
<text class="label">到达站</text>
|
||
<input class="input" v-model="ticketsInfo.ticketInfo.arrivalStation" />
|
||
</view>
|
||
|
||
<!-- Arrival Time (Multi-Selector Picker) -->
|
||
<picker mode="multiSelector" :range="arrivalRange" :value="arrivalIndex" @change="onArrivalChange">
|
||
<view class="form-item">
|
||
<text class="label">到达时间</text>
|
||
<view class="input">{{ ticketsInfo.ticketInfo.arrivalTime }}</view>
|
||
</view>
|
||
</picker>
|
||
<view class="form-item">
|
||
<text class="label">历时</text>
|
||
<input class="input" v-model="ticketsInfo.ticketInfo.duration" />
|
||
</view>
|
||
<view v-if="app == 'ctrip'" class="form-item">
|
||
<text class="label">火车名称</text>
|
||
<input class="input" v-model="ticketsInfo.ticketInfo.trainName" />
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 乘客信息 -->
|
||
<view class="section-container">
|
||
<view class="section-header" @click="toggleSection('passengerList')">
|
||
<text class="section-title">乘客信息 ({{ ticketsInfo.passengerList.length }}人)</text>
|
||
<uni-icons :type="collapsed.passengerList ? 'bottom' : 'top'" size="16" color="#666"></uni-icons>
|
||
</view>
|
||
|
||
<view v-show="!collapsed.passengerList">
|
||
<view class="card" v-for="(passenger, index) in ticketsInfo.passengerList" :key="index">
|
||
<view class="card-header-row">
|
||
<text class="card-header">乘客 {{ index + 1 }}</text>
|
||
<text class="delete-btn" @click="removePassenger(index)">删除</text>
|
||
</view>
|
||
<view class="form-item">
|
||
<text class="label">姓名</text>
|
||
<input class="input" v-model="passenger.name" />
|
||
</view>
|
||
<picker v-if="app != 'fliggy'" :range="ticketType" range-key="label"
|
||
@change="(e) => onTicketTypeChange(e, index)">
|
||
<view class="form-item" style="border-bottom: 1rpx solid #F5F5F5;">
|
||
<text class="label">票种</text>
|
||
<view class="input">{{ passenger.type }}</view>
|
||
</view>
|
||
</picker>
|
||
<view class="form-item">
|
||
<text class="label">席别</text>
|
||
<input class="input" v-model="passenger.seatType" />
|
||
</view>
|
||
<view class="form-item">
|
||
<text class="label">车厢</text>
|
||
<input class="input" v-model="passenger.carriage" />
|
||
</view>
|
||
<view class="form-item">
|
||
<text class="label">座位号</text>
|
||
<input class="input" v-model="passenger.seatNo" />
|
||
</view>
|
||
|
||
<view class="form-item">
|
||
<text class="label">票价</text>
|
||
<input class="input" type="number" v-model="passenger.price" @input="onPriceInput" />
|
||
</view>
|
||
<view v-if="app == 'ctrip'" class="form-item">
|
||
<text class="label">积分</text>
|
||
<input class="input" type="number" v-model="passenger.points" />
|
||
</view>
|
||
<view v-if="app != 'ctrip'" class="form-item">
|
||
<text class="label">证件类型</text>
|
||
<input class="input" v-model="passenger.idType" />
|
||
</view>
|
||
<view v-if="app != 'ctrip'" class="form-item">
|
||
<text class="label">证件号</text>
|
||
<input class="input" v-model="passenger.idNumber" />
|
||
</view>
|
||
<view v-if="app != 'qunar'" class="form-item">
|
||
<text class="label">是否本人</text>
|
||
<switch :checked="passenger.isMe" @change="(e) => passenger.isMe = e.detail.value" />
|
||
</view>
|
||
</view>
|
||
|
||
<view class="add-btn-box" @click="addPassenger">
|
||
<uni-icons type="plusempty" size="20" color="#1677FF"></uni-icons>
|
||
<text class="add-text">添加乘客</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 酒店广告 -->
|
||
<view v-if="app == '12306' || app == 'ctrip'" class="section-container">
|
||
<view class="section-header" @click="toggleSection('hotelInfo')">
|
||
<text class="section-title">{{ app == '12306' ? '酒店广告' : '返现任务' }}</text>
|
||
<uni-icons :type="collapsed.hotelInfo ? 'bottom' : 'top'" size="16" color="#666"></uni-icons>
|
||
</view>
|
||
<view class="card" v-show="!collapsed.hotelInfo">
|
||
<view v-if="app == '12306'" class="form-item">
|
||
<text class="label">城市</text>
|
||
<input class="input" v-model="ticketsInfo.hotelInfo.city" />
|
||
</view>
|
||
<view v-if="app == 'ctrip'" class="form-item">
|
||
<text class="label">返现金额</text>
|
||
<input class="input" type="number" v-model="ticketsInfo.hotelInfo.cashback" />
|
||
</view>
|
||
<uni-datetime-picker type="daterange" v-model="hotelDateRange" :border="false">
|
||
<view class="form-item">
|
||
<text class="label">入住/离店日期</text>
|
||
<view class="input">{{ ticketsInfo.hotelInfo.startDay }} 至 {{ ticketsInfo.hotelInfo.endDay
|
||
}}</view>
|
||
</view>
|
||
</uni-datetime-picker>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="placeholder"></view>
|
||
</scroll-view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import NavBar from '@/components/nav-bar/nav-bar.vue'
|
||
import {
|
||
reactive,
|
||
toRefs,
|
||
onMounted,
|
||
computed
|
||
} from 'vue';
|
||
import { onLoad } from '@dcloudio/uni-app';
|
||
|
||
const defaultData = {
|
||
"orderInfo": {
|
||
"orderNo": "EJ66223536",
|
||
"orderTime": "2026.01.01",
|
||
"price": "4440"
|
||
},
|
||
"ticketInfo": {
|
||
"departureTime": "01-01 09:19",
|
||
"departureStation": "北京南",
|
||
"arrivalTime": "01-01 14:04",
|
||
"arrivalStation": "上海虹桥",
|
||
"trainNo": "G905",
|
||
"duration": "4时45分",
|
||
"date": "2026.01.01",
|
||
"weekDay": "星期四",
|
||
"gate": "6B",
|
||
"trainName": "复兴号"
|
||
},
|
||
"passengerList": [{
|
||
"name": "张元英",
|
||
"type": "成人票",
|
||
"seatType": "商务座",
|
||
"carriage": "01",
|
||
"seatNo": "03C",
|
||
"idType": "外国护照(KR)",
|
||
"idNumber": "123456789012345678",
|
||
"price": "2110",
|
||
"status": "已支付",
|
||
"isMe": true,
|
||
"points": 9632
|
||
}],
|
||
"hotelInfo": {
|
||
"city": "上海",
|
||
"cashback": "25",
|
||
"startDay": "01-01",
|
||
"endDay": "01-02"
|
||
},
|
||
serviceText: "多人连坐 ¥10x2份"
|
||
}
|
||
// 车票类型
|
||
const ticketType = [{
|
||
label: '成人票',
|
||
value: '1'
|
||
},
|
||
{
|
||
label: '儿童票',
|
||
value: '2'
|
||
},
|
||
{
|
||
label: '学生票',
|
||
value: '3'
|
||
},
|
||
{
|
||
label: '残疾军人票',
|
||
value: '4'
|
||
}
|
||
]
|
||
|
||
const data = reactive({
|
||
ticketsInfo: JSON.parse(JSON.stringify(defaultData)),
|
||
collapsed: {
|
||
orderInfo: true,
|
||
ticketInfo: false, // Default open ticket info as it is most likely to be edited
|
||
passengerList: false,
|
||
hotelInfo: true
|
||
},
|
||
app: '12306',
|
||
STORAGE_KEY: 'ticketsInfo'
|
||
})
|
||
|
||
let { app } = toRefs(data)
|
||
|
||
|
||
const {
|
||
ticketsInfo,
|
||
collapsed
|
||
} = toRefs(data)
|
||
|
||
onLoad((options) => {
|
||
console.log("options", options)
|
||
if (options.app) {
|
||
data.app = options.app
|
||
}
|
||
if (options.storageKey) {
|
||
data.STORAGE_KEY = options.storageKey
|
||
}
|
||
})
|
||
|
||
const ticketYear = computed(() => {
|
||
const dateStr = data.ticketsInfo.ticketInfo.date;
|
||
if (dateStr && dateStr.length >= 4) {
|
||
return dateStr.substring(0, 4);
|
||
}
|
||
return new Date().getFullYear().toString();
|
||
})
|
||
|
||
|
||
|
||
/**
|
||
* 获取酒店日期范围
|
||
*/
|
||
const hotelDateRange = computed({
|
||
get() {
|
||
const year = ticketYear.value;
|
||
const start = data.ticketsInfo.hotelInfo.startDay ?
|
||
`${year}-${data.ticketsInfo.hotelInfo.startDay}` : '';
|
||
const end = data.ticketsInfo.hotelInfo.endDay ? `${year}-${data.ticketsInfo.hotelInfo.endDay}` :
|
||
'';
|
||
if (start && end) {
|
||
return [start, end];
|
||
}
|
||
return [];
|
||
},
|
||
set(val) {
|
||
if (Array.isArray(val) && val.length === 2) {
|
||
data.ticketsInfo.hotelInfo.startDay = val[0].substring(5);
|
||
data.ticketsInfo.hotelInfo.endDay = val[1].substring(5);
|
||
}
|
||
}
|
||
})
|
||
|
||
onMounted(() => {
|
||
const stored = uni.getStorageSync(data.STORAGE_KEY)
|
||
if (stored) {
|
||
Object.assign(data.ticketsInfo, stored)
|
||
}
|
||
updateDuration();
|
||
})
|
||
/**
|
||
* 更新总价
|
||
*/
|
||
const onPriceInput = () => {
|
||
setTimeout(() => {
|
||
let total = 0;
|
||
data.ticketsInfo.passengerList.forEach(item => {
|
||
const price = Number(item.price) || 0;
|
||
total += price;
|
||
});
|
||
data.ticketsInfo.orderInfo.price = total.toString();
|
||
}, 50);
|
||
}
|
||
|
||
/**
|
||
* 确认
|
||
*/
|
||
const handleRightButtonClick = () => {
|
||
console.log("handleRightButtonClick", data.ticketsInfo)
|
||
const orderTimeStr = data.ticketsInfo.orderInfo.orderTime;
|
||
const ticketDateStr = data.ticketsInfo.ticketInfo.date;
|
||
|
||
if (orderTimeStr && ticketDateStr) {
|
||
if (orderTimeStr > ticketDateStr) {
|
||
uni.showToast({
|
||
title: '下单时间不能晚于出发日期',
|
||
icon: 'none'
|
||
});
|
||
return;
|
||
}
|
||
}
|
||
|
||
if (data.ticketsInfo.passengerList.length === 0) {
|
||
uni.showToast({
|
||
title: '请至少添加一名乘客',
|
||
icon: 'none'
|
||
});
|
||
return;
|
||
} else {
|
||
const passengerList = data.ticketsInfo.passengerList.filter(item => item.isMe)
|
||
if (passengerList.length > 1) {
|
||
uni.showToast({
|
||
title: '至多添加一名乘客为本人',
|
||
icon: 'none'
|
||
});
|
||
return;
|
||
}
|
||
}
|
||
|
||
uni.setStorageSync(data.STORAGE_KEY, data.ticketsInfo)
|
||
uni.navigateBack()
|
||
}
|
||
|
||
/**
|
||
* 切换折叠状态
|
||
* @param {string} key
|
||
*/
|
||
const toggleSection = (key) => {
|
||
data.collapsed[key] = !data.collapsed[key]
|
||
}
|
||
|
||
/**
|
||
* 删除乘客
|
||
* @param {number} index
|
||
*/
|
||
const removePassenger = (index) => {
|
||
uni.showModal({
|
||
title: '提示',
|
||
content: '确定要删除该乘客吗?',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
data.ticketsInfo.passengerList.splice(index, 1);
|
||
onPriceInput();
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 添加乘客
|
||
*/
|
||
const addPassenger = () => {
|
||
const oldPassenger = data.ticketsInfo.passengerList[data.ticketsInfo.passengerList.length - 1]
|
||
const newPassenger = {
|
||
name: '新乘客',
|
||
type: oldPassenger.type || '成人票',
|
||
seatType: oldPassenger.seatType || '二等座',
|
||
carriage: oldPassenger.carriage || '01',
|
||
seatNo: '01A',
|
||
idType: '中国居民身份证',
|
||
price: oldPassenger.price || '0',
|
||
status: '已支付',
|
||
isMe: false,
|
||
points: oldPassenger.points || '2898',
|
||
}
|
||
data.ticketsInfo.passengerList.push(newPassenger);
|
||
onPriceInput();
|
||
}
|
||
|
||
/**
|
||
* 获取选择器日期
|
||
* @param {string} dateStr
|
||
* @returns {string}
|
||
*/
|
||
const getPickerDate = (dateStr) => {
|
||
if (!dateStr) return '';
|
||
// Handle YYYY.MM.DD format
|
||
if (dateStr.includes('.')) {
|
||
return dateStr.replace(/\./g, '-');
|
||
}
|
||
// Handle MM-DD (prepend year) - logic from before, but mainly for Hotel.
|
||
// Ticket date is full date YYYY.MM.DD
|
||
if (dateStr.length <= 5) {
|
||
const year = new Date().getFullYear();
|
||
return `${year}-${dateStr}`;
|
||
}
|
||
return dateStr;
|
||
}
|
||
|
||
/**
|
||
* 切换下单时间
|
||
* @param {*} e
|
||
*/
|
||
const onOrderTimeChange = (e) => {
|
||
const val = e.detail.value;
|
||
if (val) {
|
||
data.ticketsInfo.orderInfo.orderTime = val.replace(/-/g, '.');
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 切换乘客类型
|
||
* @param {*} e
|
||
* @param {*} index
|
||
*/
|
||
const onTicketTypeChange = (e, index) => {
|
||
const val = e.detail.value;
|
||
data.ticketsInfo.passengerList[index].type = ticketType[val].label;
|
||
}
|
||
|
||
/**
|
||
* 切换出发日期
|
||
* @param {*} e
|
||
*/
|
||
const onTicketDateChange = (e) => {
|
||
const val = e.detail.value; // YYYY-MM-DD
|
||
if (val) {
|
||
// Update Date: YYYY.MM.DD
|
||
data.ticketsInfo.ticketInfo.date = val.replace(/-/g, '.');
|
||
|
||
// Update WeekDay
|
||
const dateObj = new Date(val.replace(/-/g, '/')); // Compatible
|
||
const days = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
|
||
data.ticketsInfo.ticketInfo.weekDay = days[dateObj.getDay()];
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取时间
|
||
* @param {string} fullStr
|
||
* @returns {string}
|
||
*/
|
||
const getTimeHHMM = (fullStr) => {
|
||
if (!fullStr) return '';
|
||
if (fullStr.length > 5) return fullStr.split(' ')[1];
|
||
return fullStr;
|
||
}
|
||
|
||
/**
|
||
* 获取出发时间
|
||
*/
|
||
const departureTimeHHMM = computed(() => {
|
||
return getTimeHHMM(data.ticketsInfo.ticketInfo.departureTime);
|
||
})
|
||
|
||
/**
|
||
* 获取出发时间选择器范围
|
||
*/
|
||
const departureTimeRange = computed(() => {
|
||
const hours = Array.from({
|
||
length: 24
|
||
}, (_, i) => i < 10 ? '0' + i : '' + i);
|
||
const minutes = Array.from({
|
||
length: 60
|
||
}, (_, i) => i < 10 ? '0' + i : '' + i);
|
||
return [hours, minutes];
|
||
})
|
||
|
||
/**
|
||
* 获取出发时间索引
|
||
*/
|
||
const departureTimeIndex = computed(() => {
|
||
const timeStr = getTimeHHMM(data.ticketsInfo.ticketInfo.departureTime);
|
||
if (!timeStr) return [0, 0];
|
||
const [h, m] = timeStr.split(':');
|
||
const hours = departureTimeRange.value[0];
|
||
const minutes = departureTimeRange.value[1];
|
||
let hIdx = hours.indexOf(h);
|
||
if (hIdx === -1) hIdx = 0;
|
||
let mIdx = minutes.indexOf(m);
|
||
if (mIdx === -1) mIdx = 0;
|
||
return [hIdx, mIdx];
|
||
})
|
||
|
||
/**
|
||
* 获取到达时间日期范围
|
||
*/
|
||
const arrivalRange = computed(() => {
|
||
const dateStr = data.ticketsInfo.ticketInfo.date; // YYYY.MM.DD
|
||
const dates = [];
|
||
if (dateStr) {
|
||
const baseDate = new Date(dateStr.replace(/\./g, '-').replace(/-/g, '/') + ' 00:00:00');
|
||
for (let i = 0; i < 4; i++) { // Ticket Date + 3 days
|
||
const d = new Date(baseDate);
|
||
d.setDate(d.getDate() + i);
|
||
const pad = n => n < 10 ? '0' + n : n;
|
||
dates.push(`${pad(d.getMonth() + 1)}-${pad(d.getDate())}`);
|
||
}
|
||
} else {
|
||
dates.push('MM-DD');
|
||
}
|
||
const hours = Array.from({
|
||
length: 24
|
||
}, (_, i) => i < 10 ? '0' + i : '' + i);
|
||
const minutes = Array.from({
|
||
length: 60
|
||
}, (_, i) => i < 10 ? '0' + i : '' + i);
|
||
return [dates, hours, minutes];
|
||
})
|
||
|
||
/**
|
||
* 获取到达时间索引
|
||
*/
|
||
const arrivalIndex = computed(() => {
|
||
const arrTime = data.ticketsInfo.ticketInfo.arrivalTime;
|
||
if (!arrTime || arrTime.length < 5) return [0, 0, 0];
|
||
// Might be HH:mm or MM-DD HH:mm
|
||
const parts = arrTime.split(' ');
|
||
let datePart = parts[0];
|
||
let timePart = parts[1];
|
||
|
||
// Handle case where only HH:mm (old data)
|
||
if (!timePart && datePart.includes(':')) {
|
||
timePart = datePart;
|
||
datePart = arrivalRange.value[0][0];
|
||
}
|
||
|
||
if (!timePart) return [0, 0, 0];
|
||
const [h, m] = timePart.split(':');
|
||
|
||
const dates = arrivalRange.value[0];
|
||
const hours = arrivalRange.value[1];
|
||
const minutes = arrivalRange.value[2];
|
||
|
||
let dateIdx = dates.indexOf(datePart);
|
||
if (dateIdx === -1) dateIdx = 0;
|
||
|
||
let hIdx = hours.indexOf(h);
|
||
if (hIdx === -1) hIdx = 0;
|
||
|
||
let mIdx = minutes.indexOf(m);
|
||
if (mIdx === -1) mIdx = 0;
|
||
|
||
return [dateIdx, hIdx, mIdx];
|
||
})
|
||
|
||
/**
|
||
* 切换到达时间
|
||
* @param {*} e
|
||
*/
|
||
const onArrivalChange = (e) => {
|
||
const idxs = e.detail.value;
|
||
const range = arrivalRange.value;
|
||
if (!range[0][idxs[0]]) return;
|
||
|
||
const dateStr = range[0][idxs[0]];
|
||
const hStr = range[1][idxs[1]];
|
||
const mStr = range[2][idxs[2]];
|
||
|
||
const newArrTime = `${dateStr} ${hStr}:${mStr}`;
|
||
|
||
// Validate: >= Departure
|
||
const getTs = (str) => {
|
||
if (!str || str.length <= 5) return 0;
|
||
const year = new Date().getFullYear();
|
||
// Robust format: YYYY/MM/DD HH:mm:00
|
||
return new Date(`${year}/${str.replace(/-/g, '/')}:00`).getTime();
|
||
}
|
||
|
||
const startTs = getTs(data.ticketsInfo.ticketInfo.departureTime);
|
||
const endTs = getTs(newArrTime);
|
||
|
||
if (startTs > 0 && endTs < startTs) {
|
||
uni.showToast({
|
||
title: '到达时间不能早于出发时间',
|
||
icon: 'none'
|
||
});
|
||
return;
|
||
}
|
||
|
||
data.ticketsInfo.ticketInfo.arrivalTime = newArrTime;
|
||
updateDuration();
|
||
}
|
||
|
||
/**
|
||
* 切换出发时间
|
||
* @param e
|
||
*/
|
||
/**
|
||
* 切换出发时间
|
||
* @param e
|
||
*/
|
||
const onDepartureTimeChange = (e) => {
|
||
let val = e.detail.value; // Array [hIdx, mIdx]
|
||
|
||
// Convert array to HH:mm string
|
||
if (Array.isArray(val)) {
|
||
const h = departureTimeRange.value[0][val[0]];
|
||
const m = departureTimeRange.value[1][val[1]];
|
||
val = `${h}:${m}`;
|
||
}
|
||
|
||
if (!val) return;
|
||
|
||
// Construct New Departure Timestamp
|
||
// Departure uses Ticket Date
|
||
const ticketDate = data.ticketsInfo.ticketInfo.date; // YYYY.MM.DD
|
||
if (!ticketDate) return;
|
||
|
||
// Assuming format YYYY.MM.DD
|
||
const depDateStr = ticketDate.replace(/\./g, '/'); // YYYY/MM/DD
|
||
const newDepTs = new Date(`${depDateStr} ${val}:00`).getTime();
|
||
|
||
// Get Arrival Timestamp
|
||
const arrStr = data.ticketsInfo.ticketInfo.arrivalTime; // MM-DD HH:mm
|
||
if (arrStr && arrStr.length > 5) {
|
||
// Use Ticket Year as base.
|
||
const ticketYear = ticketDate.split('.')[0];
|
||
const arrDatePart = arrStr.split(' ')[0]; // MM-DD
|
||
const arrTimePart = arrStr.split(' ')[1]; // HH:mm
|
||
|
||
// Handle Cross Year if Ticket Date is Dec and Arrival is Jan
|
||
let arrYear = parseInt(ticketYear);
|
||
const ticketMonth = parseInt(ticketDate.split('.')[1]);
|
||
const arrMonth = parseInt(arrDatePart.split('-')[0]);
|
||
|
||
if (ticketMonth === 12 && arrMonth === 1) {
|
||
arrYear++;
|
||
}
|
||
|
||
const arrTs = new Date(`${arrYear}/${arrDatePart.replace(/-/g, '/')} ${arrTimePart}:00`).getTime();
|
||
|
||
if (newDepTs > arrTs) {
|
||
uni.showToast({
|
||
title: '出发时间不能晚于到达时间',
|
||
icon: 'none'
|
||
});
|
||
return;
|
||
}
|
||
}
|
||
|
||
const dateParts = ticketDate.split('.');
|
||
let mmdd = `${dateParts[1]}-${dateParts[2]}`;
|
||
data.ticketsInfo.ticketInfo.departureTime = `${mmdd} ${val}`;
|
||
updateDuration();
|
||
}
|
||
|
||
/**
|
||
* 更新时长
|
||
*/
|
||
const updateDuration = () => {
|
||
// Helper to parse MM-DD HH:mm to timestamp (using current year)
|
||
// Safer to use "/" for cross-platform compatibility
|
||
const getTs = (str) => {
|
||
if (!str || str.length <= 5) return 0;
|
||
const year = new Date().getFullYear();
|
||
// Format: "YYYY/MM/DD HH:mm:00"
|
||
return new Date(`${year}/${str.replace(/-/g, '/')}:00`).getTime();
|
||
}
|
||
|
||
const start = getTs(data.ticketsInfo.ticketInfo.departureTime);
|
||
const end = getTs(data.ticketsInfo.ticketInfo.arrivalTime);
|
||
|
||
if (start && end && end >= start) {
|
||
const diffMs = end - start;
|
||
const diffHrs = Math.floor(diffMs / (1000 * 60 * 60));
|
||
const diffMins = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60));
|
||
data.ticketsInfo.ticketInfo.duration = `${diffHrs}时${diffMins}分`;
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style>
|
||
@import "@/common/main.css";
|
||
|
||
page {
|
||
background-color: #F8F8F8;
|
||
height: 100vh;
|
||
overflow: hidden;
|
||
}
|
||
</style>
|
||
|
||
<style lang="less" scoped>
|
||
.container {
|
||
display: flex;
|
||
flex-direction: column;
|
||
height: 100vh;
|
||
}
|
||
|
||
.form-content {
|
||
flex: 1;
|
||
height: 0;
|
||
padding: 24rpx;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.section-container {
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.section-header {
|
||
display: flex;
|
||
flex-direction: row;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 24rpx 12rpx 16rpx;
|
||
background-color: transparent;
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.card {
|
||
background-color: #fff;
|
||
border-radius: 16rpx;
|
||
padding: 0 24rpx;
|
||
margin-bottom: 24rpx;
|
||
overflow: hidden;
|
||
|
||
.card-header-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 24rpx 0 12rpx;
|
||
border-bottom: 1rpx solid #f5f5f5;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.card-header {
|
||
font-size: 30rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
}
|
||
|
||
.delete-btn {
|
||
font-size: 26rpx;
|
||
color: #FF4D4F;
|
||
padding: 4rpx 12rpx;
|
||
}
|
||
}
|
||
|
||
.form-item {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 24rpx 0;
|
||
border-bottom: 1rpx solid #F5F5F5;
|
||
|
||
&:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.label {
|
||
font-size: 30rpx;
|
||
color: #333;
|
||
width: 240rpx;
|
||
}
|
||
|
||
.input {
|
||
flex: 1;
|
||
font-size: 30rpx;
|
||
color: #333;
|
||
text-align: right;
|
||
}
|
||
}
|
||
|
||
.add-btn-box {
|
||
background-color: #fff;
|
||
border-radius: 16rpx;
|
||
padding: 24rpx;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
border: 2rpx dashed #1677FF;
|
||
margin-bottom: 24rpx;
|
||
|
||
.add-text {
|
||
color: #1677FF;
|
||
font-size: 30rpx;
|
||
margin-left: 8rpx;
|
||
}
|
||
}
|
||
|
||
.placeholder {
|
||
height: 60rpx;
|
||
}
|
||
</style> |