机票时长默认自动计算,可修改
This commit is contained in:
parent
c6dcc7ab4d
commit
3da8a73f1d
|
|
@ -86,10 +86,10 @@
|
||||||
<text class="label">到达机场</text>
|
<text class="label">到达机场</text>
|
||||||
<input class="input" v-model="ticketsInfo.flightInfo.endAirport" />
|
<input class="input" v-model="ticketsInfo.flightInfo.endAirport" />
|
||||||
</view>
|
</view>
|
||||||
<!-- <view class="form-item">
|
<view class="form-item">
|
||||||
<text class="label">时长</text>
|
<text class="label">时长</text>
|
||||||
<input class="input" v-model="ticketsInfo.flightInfo.duration" placeholder="例: 2时5分" />
|
<input class="input" v-model="ticketsInfo.flightInfo.duration" placeholder="例: 2时5分" />
|
||||||
</view> -->
|
</view>
|
||||||
<view class="form-item">
|
<view class="form-item">
|
||||||
<text class="label">机型</text>
|
<text class="label">机型</text>
|
||||||
<input class="input" v-model="ticketsInfo.flightInfo.aircraftType" />
|
<input class="input" v-model="ticketsInfo.flightInfo.aircraftType" />
|
||||||
|
|
@ -218,7 +218,8 @@
|
||||||
<text class="label">等级</text>
|
<text class="label">等级</text>
|
||||||
<view class="input"
|
<view class="input"
|
||||||
:style="{ color: !ticketsInfo.ctripOrderInfo.level ? '#999' : '#333' }">
|
:style="{ color: !ticketsInfo.ctripOrderInfo.level ? '#999' : '#333' }">
|
||||||
{{ getLevelLabel(ticketsInfo.ctripOrderInfo.level) || '请选择等级' }}</view>
|
{{ getLevelLabel(ticketsInfo.ctripOrderInfo.level) || '请选择等级' }}
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</picker>
|
</picker>
|
||||||
<view class="tips-container">
|
<view class="tips-container">
|
||||||
|
|
@ -255,498 +256,515 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import NavBar from '@/components/nav-bar/nav-bar.vue'
|
import NavBar from '@/components/nav-bar/nav-bar.vue'
|
||||||
import { reactive, toRefs, onMounted } from 'vue';
|
import {
|
||||||
import { onLoad } from '@dcloudio/uni-app';
|
reactive,
|
||||||
import defualtData from '@/pages/other/air-tickets/commom/defualt.json';
|
toRefs,
|
||||||
import airlineJson from '@/static/json/air-line.json';
|
onMounted
|
||||||
|
} from 'vue';
|
||||||
|
import {
|
||||||
|
onLoad
|
||||||
|
} from '@dcloudio/uni-app';
|
||||||
|
import defualtData from '@/pages/other/air-tickets/commom/defualt.json';
|
||||||
|
import airlineJson from '@/static/json/air-line.json';
|
||||||
|
|
||||||
const airLineList = airlineJson.airLine;
|
const airLineList = airlineJson.airLine;
|
||||||
|
|
||||||
const data = reactive({
|
const data = reactive({
|
||||||
ticketsInfo: JSON.parse(JSON.stringify(defualtData)),
|
ticketsInfo: JSON.parse(JSON.stringify(defualtData)),
|
||||||
collapsed: {
|
collapsed: {
|
||||||
orderInfo: false,
|
orderInfo: false,
|
||||||
flightInfo: false,
|
flightInfo: false,
|
||||||
passengersInfo: false
|
passengersInfo: false
|
||||||
},
|
},
|
||||||
storageKey: 'airTicketsInfo',
|
storageKey: 'airTicketsInfo',
|
||||||
dragInfo: {
|
dragInfo: {
|
||||||
index: -1,
|
index: -1,
|
||||||
listType: '', // 'ctrip' or 'fliggy'
|
listType: '', // 'ctrip' or 'fliggy'
|
||||||
startY: 0,
|
startY: 0,
|
||||||
offsetY: 0,
|
offsetY: 0,
|
||||||
itemHeight: 0
|
itemHeight: 0
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|
||||||
const { ticketsInfo, collapsed, dragInfo } = toRefs(data)
|
|
||||||
|
|
||||||
const onAirlineChange = (e) => {
|
|
||||||
const index = e.detail.value;
|
|
||||||
const selected = airLineList[index];
|
|
||||||
if (selected) {
|
|
||||||
data.ticketsInfo.flightInfo.airline = selected.name;
|
|
||||||
data.ticketsInfo.flightInfo.airlineCode = selected.code;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//携程会员等级
|
|
||||||
const ctripLevelList = [
|
|
||||||
{
|
|
||||||
label: "白银",
|
|
||||||
value: 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "黄金",
|
|
||||||
value: 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "铂金",
|
|
||||||
value: 3
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "钻石",
|
|
||||||
value: 4
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
const onLevelChange = (e) => {
|
|
||||||
const index = e.detail.value;
|
|
||||||
ticketsInfo.value.ctripOrderInfo.level = ctripLevelList[index].value;
|
|
||||||
}
|
|
||||||
|
|
||||||
const getLevelLabel = (value) => {
|
|
||||||
const item = ctripLevelList.find(i => i.value == value);
|
|
||||||
return item ? item.label : '';
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const addTip = () => {
|
|
||||||
if (!ticketsInfo.value.ctripOrderInfo.tips) {
|
|
||||||
ticketsInfo.value.ctripOrderInfo.tips = []
|
|
||||||
}
|
|
||||||
ticketsInfo.value.ctripOrderInfo.tips.push({
|
|
||||||
id: Date.now().toString(),
|
|
||||||
content: ''
|
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
const removeTip = (index) => {
|
const {
|
||||||
ticketsInfo.value.ctripOrderInfo.tips.splice(index, 1)
|
ticketsInfo,
|
||||||
}
|
collapsed,
|
||||||
|
dragInfo
|
||||||
|
} = toRefs(data)
|
||||||
|
|
||||||
const addFliggyTip = () => {
|
const onAirlineChange = (e) => {
|
||||||
if (!ticketsInfo.value.fligggyOrderInfo.tips) {
|
const index = e.detail.value;
|
||||||
ticketsInfo.value.fligggyOrderInfo.tips = []
|
const selected = airLineList[index];
|
||||||
}
|
if (selected) {
|
||||||
ticketsInfo.value.fligggyOrderInfo.tips.push({
|
data.ticketsInfo.flightInfo.airline = selected.name;
|
||||||
id: Date.now().toString(),
|
data.ticketsInfo.flightInfo.airlineCode = selected.code;
|
||||||
content: ''
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const removeFliggyTip = (index) => {
|
|
||||||
ticketsInfo.value.fligggyOrderInfo.tips.splice(index, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Drag Sorting Logic
|
|
||||||
let dragTimer = null
|
|
||||||
const onDragStart = (e, index, type) => {
|
|
||||||
const touch = e.touches[0]
|
|
||||||
// Get item height first - assuming fixed height or query first item
|
|
||||||
// Better to query the specific item height
|
|
||||||
// For simplicity, let's assume item height is consistent or query dynamically
|
|
||||||
const query = uni.createSelectorQuery().in(this)
|
|
||||||
query.selectAll('.tip-item').boundingClientRect(data => {
|
|
||||||
if (data && data.length > 0) {
|
|
||||||
const itemRect = data[index]
|
|
||||||
dragInfo.value.itemHeight = itemRect.height
|
|
||||||
dragInfo.value.index = index
|
|
||||||
dragInfo.value.listType = type
|
|
||||||
dragInfo.value.startY = touch.clientY
|
|
||||||
dragInfo.value.offsetY = 0
|
|
||||||
}
|
|
||||||
}).exec()
|
|
||||||
}
|
|
||||||
|
|
||||||
const onDragMove = (e) => {
|
|
||||||
if (dragInfo.value.index === -1) return
|
|
||||||
const touch = e.touches[0]
|
|
||||||
const deltaY = touch.clientY - dragInfo.value.startY
|
|
||||||
dragInfo.value.offsetY = deltaY
|
|
||||||
|
|
||||||
// Calculate if we moved enough to swap
|
|
||||||
const itemHeight = dragInfo.value.itemHeight || 50 // fallback height
|
|
||||||
const moveSteps = Math.round(deltaY / itemHeight)
|
|
||||||
|
|
||||||
if (moveSteps !== 0) {
|
|
||||||
const newIndex = dragInfo.value.index + moveSteps
|
|
||||||
let tips = []
|
|
||||||
if (dragInfo.value.listType === 'ctrip') {
|
|
||||||
tips = ticketsInfo.value.ctripOrderInfo.tips
|
|
||||||
} else if (dragInfo.value.listType === 'fliggy') {
|
|
||||||
tips = ticketsInfo.value.fligggyOrderInfo.tips
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newIndex >= 0 && newIndex < tips.length) {
|
|
||||||
// Debounce swap or swap immediately if safe?
|
|
||||||
// Immediate swap might feel jittery if not careful
|
|
||||||
// Let's swap data and update startY to reflect new position
|
|
||||||
// Logic: Swap data, reset offset (because the item moved in DOM)
|
|
||||||
|
|
||||||
// Simple swap
|
|
||||||
const temp = tips[dragInfo.value.index]
|
|
||||||
tips[dragInfo.value.index] = tips[newIndex]
|
|
||||||
tips[newIndex] = temp
|
|
||||||
|
|
||||||
// Update current index to new index
|
|
||||||
dragInfo.value.index = newIndex
|
|
||||||
// Reset startY to current touch position relative to new item position
|
|
||||||
dragInfo.value.startY = touch.clientY
|
|
||||||
dragInfo.value.offsetY = 0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const onDragEnd = () => {
|
//携程会员等级
|
||||||
dragInfo.value.index = -1
|
const ctripLevelList = [{
|
||||||
dragInfo.value.listType = ''
|
label: "白银",
|
||||||
dragInfo.value.offsetY = 0
|
value: 1
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
label: "黄金",
|
||||||
|
value: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "铂金",
|
||||||
|
value: 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "钻石",
|
||||||
|
value: 4
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
// 深度合并函数
|
const onLevelChange = (e) => {
|
||||||
const deepMerge = (target, source) => {
|
const index = e.detail.value;
|
||||||
for (const key in source) {
|
ticketsInfo.value.ctripOrderInfo.level = ctripLevelList[index].value;
|
||||||
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
|
}
|
||||||
if (!target[key] || typeof target[key] !== 'object') {
|
|
||||||
target[key] = {};
|
const getLevelLabel = (value) => {
|
||||||
|
const item = ctripLevelList.find(i => i.value == value);
|
||||||
|
return item ? item.label : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const addTip = () => {
|
||||||
|
if (!ticketsInfo.value.ctripOrderInfo.tips) {
|
||||||
|
ticketsInfo.value.ctripOrderInfo.tips = []
|
||||||
|
}
|
||||||
|
ticketsInfo.value.ctripOrderInfo.tips.push({
|
||||||
|
id: Date.now().toString(),
|
||||||
|
content: ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeTip = (index) => {
|
||||||
|
ticketsInfo.value.ctripOrderInfo.tips.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const addFliggyTip = () => {
|
||||||
|
if (!ticketsInfo.value.fligggyOrderInfo.tips) {
|
||||||
|
ticketsInfo.value.fligggyOrderInfo.tips = []
|
||||||
|
}
|
||||||
|
ticketsInfo.value.fligggyOrderInfo.tips.push({
|
||||||
|
id: Date.now().toString(),
|
||||||
|
content: ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const removeFliggyTip = (index) => {
|
||||||
|
ticketsInfo.value.fligggyOrderInfo.tips.splice(index, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Drag Sorting Logic
|
||||||
|
let dragTimer = null
|
||||||
|
const onDragStart = (e, index, type) => {
|
||||||
|
const touch = e.touches[0]
|
||||||
|
// Get item height first - assuming fixed height or query first item
|
||||||
|
// Better to query the specific item height
|
||||||
|
// For simplicity, let's assume item height is consistent or query dynamically
|
||||||
|
const query = uni.createSelectorQuery().in(this)
|
||||||
|
query.selectAll('.tip-item').boundingClientRect(data => {
|
||||||
|
if (data && data.length > 0) {
|
||||||
|
const itemRect = data[index]
|
||||||
|
dragInfo.value.itemHeight = itemRect.height
|
||||||
|
dragInfo.value.index = index
|
||||||
|
dragInfo.value.listType = type
|
||||||
|
dragInfo.value.startY = touch.clientY
|
||||||
|
dragInfo.value.offsetY = 0
|
||||||
}
|
}
|
||||||
deepMerge(target[key], source[key]);
|
}).exec()
|
||||||
|
}
|
||||||
|
|
||||||
|
const onDragMove = (e) => {
|
||||||
|
if (dragInfo.value.index === -1) return
|
||||||
|
const touch = e.touches[0]
|
||||||
|
const deltaY = touch.clientY - dragInfo.value.startY
|
||||||
|
dragInfo.value.offsetY = deltaY
|
||||||
|
|
||||||
|
// Calculate if we moved enough to swap
|
||||||
|
const itemHeight = dragInfo.value.itemHeight || 50 // fallback height
|
||||||
|
const moveSteps = Math.round(deltaY / itemHeight)
|
||||||
|
|
||||||
|
if (moveSteps !== 0) {
|
||||||
|
const newIndex = dragInfo.value.index + moveSteps
|
||||||
|
let tips = []
|
||||||
|
if (dragInfo.value.listType === 'ctrip') {
|
||||||
|
tips = ticketsInfo.value.ctripOrderInfo.tips
|
||||||
|
} else if (dragInfo.value.listType === 'fliggy') {
|
||||||
|
tips = ticketsInfo.value.fligggyOrderInfo.tips
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newIndex >= 0 && newIndex < tips.length) {
|
||||||
|
// Debounce swap or swap immediately if safe?
|
||||||
|
// Immediate swap might feel jittery if not careful
|
||||||
|
// Let's swap data and update startY to reflect new position
|
||||||
|
// Logic: Swap data, reset offset (because the item moved in DOM)
|
||||||
|
|
||||||
|
// Simple swap
|
||||||
|
const temp = tips[dragInfo.value.index]
|
||||||
|
tips[dragInfo.value.index] = tips[newIndex]
|
||||||
|
tips[newIndex] = temp
|
||||||
|
|
||||||
|
// Update current index to new index
|
||||||
|
dragInfo.value.index = newIndex
|
||||||
|
// Reset startY to current touch position relative to new item position
|
||||||
|
dragInfo.value.startY = touch.clientY
|
||||||
|
dragInfo.value.offsetY = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const onDragEnd = () => {
|
||||||
|
dragInfo.value.index = -1
|
||||||
|
dragInfo.value.listType = ''
|
||||||
|
dragInfo.value.offsetY = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// 深度合并函数
|
||||||
|
const deepMerge = (target, source) => {
|
||||||
|
for (const key in source) {
|
||||||
|
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
|
||||||
|
if (!target[key] || typeof target[key] !== 'object') {
|
||||||
|
target[key] = {};
|
||||||
|
}
|
||||||
|
deepMerge(target[key], source[key]);
|
||||||
|
} else {
|
||||||
|
target[key] = source[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return target;
|
||||||
|
};
|
||||||
|
|
||||||
|
onLoad((options) => {
|
||||||
|
console.log('options', options)
|
||||||
|
if (options.storageKey) {
|
||||||
|
data.storageKey = options.storageKey
|
||||||
|
}
|
||||||
|
const stored = uni.getStorageSync(data.storageKey)
|
||||||
|
if (stored) {
|
||||||
|
// 使用默认数据作为基础,将存储的数据合并进去
|
||||||
|
// 这样可以确保新增的默认字段不会被旧的存储数据覆盖丢失
|
||||||
|
// 注意:这里 data.ticketsInfo 已经是 reactive 的 defualtData 的副本
|
||||||
|
// 但为了保险,我们重新基于 defaultData 做合并,或者直接对 ticketsInfo 做深度合并
|
||||||
|
// 鉴于 ticketsInfo 已经在 setup 中被初始化为 defaultData 的深拷贝
|
||||||
|
// 我们直接将 stored merge 进 ticketsInfo 即可,但要用 deepMerge
|
||||||
|
|
||||||
|
// 重新从 defaultData 获取一份纯净的基础数据,以防万一
|
||||||
|
const baseData = JSON.parse(JSON.stringify(defualtData));
|
||||||
|
const mergedData = deepMerge(baseData, stored);
|
||||||
|
|
||||||
|
// 将合并后的数据赋值给 reactive 对象
|
||||||
|
//由于 ticketsInfo 是 reactive 的属性,我们可以直接 Object.assign 或逐个属性赋值
|
||||||
|
Object.assign(data.ticketsInfo, mergedData)
|
||||||
|
|
||||||
|
// 再次检查 tips 是否存在,如果不存在(比如 stored 是旧数据且 defaultData 也没这个字段 - 虽然 defaultData 应该有),初始化为空数组
|
||||||
|
if (data.storageKey === 'fliggyAirTicketsInfo' && !data.ticketsInfo.fligggyOrderInfo.tips) {
|
||||||
|
data.ticketsInfo.fligggyOrderInfo.tips = []
|
||||||
|
}
|
||||||
|
if (data.storageKey === 'ctripAirTicketsInfo' && !data.ticketsInfo.ctripOrderInfo.tips) {
|
||||||
|
data.ticketsInfo.ctripOrderInfo.tips = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const toggleSection = (key) => {
|
||||||
|
data.collapsed[key] = !data.collapsed[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleRightButtonClick = () => {
|
||||||
|
const orderInfo = data.ticketsInfo.orderInfo;
|
||||||
|
const flightInfo = data.ticketsInfo.flightInfo;
|
||||||
|
|
||||||
|
if (orderInfo.orderTime && flightInfo.date && flightInfo.startTime) {
|
||||||
|
const orderDate = new Date(orderInfo.orderTime.replace(/-/g, '/'));
|
||||||
|
const flightDate = new Date((flightInfo.date + ' ' + flightInfo.startTime).replace(/-/g, '/'));
|
||||||
|
|
||||||
|
if (orderDate > flightDate) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '下单时间不能晚于起飞时间',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (data.storageKey === 'ctripAirTicketsInfo') {
|
||||||
|
delete data.ticketsInfo.fligggyOrderInfo
|
||||||
|
} else if (data.storageKey === 'fliggyAirTicketsInfo') {
|
||||||
|
delete data.ticketsInfo.ctripOrderInfo
|
||||||
} else {
|
} else {
|
||||||
target[key] = source[key];
|
delete data.ticketsInfo.fligggyOrderInfo
|
||||||
|
delete data.ticketsInfo.ctripOrderInfo
|
||||||
}
|
}
|
||||||
|
console.log('data.ticketsInfo', data.ticketsInfo)
|
||||||
|
uni.setStorageSync(data.storageKey, data.ticketsInfo)
|
||||||
|
uni.showToast({
|
||||||
|
title: '保存成功',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
uni.navigateBack()
|
||||||
}
|
}
|
||||||
return target;
|
|
||||||
};
|
|
||||||
|
|
||||||
onLoad((options) => {
|
const addPassenger = () => {
|
||||||
console.log('options', options)
|
const lastP = data.ticketsInfo.passengersInfo[data.ticketsInfo.passengersInfo.length - 1]
|
||||||
if (options.storageKey) {
|
data.ticketsInfo.passengersInfo.push({
|
||||||
data.storageKey = options.storageKey
|
name: '新乘客',
|
||||||
|
idType: lastP ? lastP.idType : '身份证',
|
||||||
|
idNumber: lastP ? lastP.idNumber : '123123********6352',
|
||||||
|
ticketNo: lastP ? lastP.ticketNo : '12345678901'
|
||||||
|
})
|
||||||
}
|
}
|
||||||
const stored = uni.getStorageSync(data.storageKey)
|
|
||||||
if (stored) {
|
|
||||||
// 使用默认数据作为基础,将存储的数据合并进去
|
|
||||||
// 这样可以确保新增的默认字段不会被旧的存储数据覆盖丢失
|
|
||||||
// 注意:这里 data.ticketsInfo 已经是 reactive 的 defualtData 的副本
|
|
||||||
// 但为了保险,我们重新基于 defaultData 做合并,或者直接对 ticketsInfo 做深度合并
|
|
||||||
// 鉴于 ticketsInfo 已经在 setup 中被初始化为 defaultData 的深拷贝
|
|
||||||
// 我们直接将 stored merge 进 ticketsInfo 即可,但要用 deepMerge
|
|
||||||
|
|
||||||
// 重新从 defaultData 获取一份纯净的基础数据,以防万一
|
const removePassenger = (index) => {
|
||||||
const baseData = JSON.parse(JSON.stringify(defualtData));
|
uni.showModal({
|
||||||
const mergedData = deepMerge(baseData, stored);
|
title: '提示',
|
||||||
|
content: '确定要删除该乘客吗?',
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
data.ticketsInfo.passengersInfo.splice(index, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 将合并后的数据赋值给 reactive 对象
|
// Helper to extract YYYY-MM-DD from YYYY-MM-DD HH:mm or similar
|
||||||
//由于 ticketsInfo 是 reactive 的属性,我们可以直接 Object.assign 或逐个属性赋值
|
const getDateFromStr = (str) => {
|
||||||
Object.assign(data.ticketsInfo, mergedData)
|
if (!str) return ''
|
||||||
|
// Try to match date pattern
|
||||||
|
const dateMatch = str.match(/\d{4}[-.]\d{2}[-.]\d{2}/)
|
||||||
|
if (dateMatch) return dateMatch[0].replace(/\./g, '-')
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
// 再次检查 tips 是否存在,如果不存在(比如 stored 是旧数据且 defaultData 也没这个字段 - 虽然 defaultData 应该有),初始化为空数组
|
const onOrderDateChange = (e) => {
|
||||||
if (data.storageKey === 'fliggyAirTicketsInfo' && !data.ticketsInfo.fligggyOrderInfo.tips) {
|
const val = e.detail.value // YYYY-MM-DD
|
||||||
data.ticketsInfo.fligggyOrderInfo.tips = []
|
// orderTime format in default is "YYYY-MM-DD HH:mm"
|
||||||
}
|
// We need to keep the time part if possible, or default to current time
|
||||||
if (data.storageKey === 'ctripAirTicketsInfo' && !data.ticketsInfo.ctripOrderInfo.tips) {
|
let oldTime = '00:00'
|
||||||
data.ticketsInfo.ctripOrderInfo.tips = []
|
if (data.ticketsInfo.orderInfo.orderTime && data.ticketsInfo.orderInfo.orderTime.includes(' ')) {
|
||||||
|
oldTime = data.ticketsInfo.orderInfo.orderTime.split(' ')[1]
|
||||||
}
|
}
|
||||||
|
data.ticketsInfo.orderInfo.orderTime = val + ' ' + oldTime
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
|
||||||
const toggleSection = (key) => {
|
const onFlightDateChange = (e) => {
|
||||||
data.collapsed[key] = !data.collapsed[key]
|
data.ticketsInfo.flightInfo.date = e.detail.value
|
||||||
}
|
|
||||||
|
|
||||||
const handleRightButtonClick = () => {
|
|
||||||
const orderInfo = data.ticketsInfo.orderInfo;
|
|
||||||
const flightInfo = data.ticketsInfo.flightInfo;
|
|
||||||
|
|
||||||
if (orderInfo.orderTime && flightInfo.date && flightInfo.startTime) {
|
|
||||||
const orderDate = new Date(orderInfo.orderTime.replace(/-/g, '/'));
|
|
||||||
const flightDate = new Date((flightInfo.date + ' ' + flightInfo.startTime).replace(/-/g, '/'));
|
|
||||||
|
|
||||||
if (orderDate > flightDate) {
|
|
||||||
uni.showToast({
|
|
||||||
title: '下单时间不能晚于起飞时间',
|
|
||||||
icon: 'none'
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (data.storageKey === 'ctripAirTicketsInfo') {
|
|
||||||
delete data.ticketsInfo.fligggyOrderInfo
|
// Custom Time Picker Data
|
||||||
} else if (data.storageKey === 'fliggyAirTicketsInfo') {
|
const hours = Array.from({
|
||||||
delete data.ticketsInfo.ctripOrderInfo
|
length: 24
|
||||||
} else {
|
}, (_, i) => i.toString().padStart(2, '0'));
|
||||||
delete data.ticketsInfo.fligggyOrderInfo
|
const minutes = Array.from({
|
||||||
delete data.ticketsInfo.ctripOrderInfo
|
length: 60
|
||||||
|
}, (_, i) => i.toString().padStart(2, '0'));
|
||||||
|
const timeRange = [hours, minutes];
|
||||||
|
|
||||||
|
const getTimeIndex = (timeStr) => {
|
||||||
|
if (!timeStr) return [0, 0];
|
||||||
|
const [h, m] = timeStr.split(':');
|
||||||
|
const hIndex = hours.findIndex(item => item === h);
|
||||||
|
const mIndex = minutes.findIndex(item => item === m);
|
||||||
|
return [hIndex === -1 ? 0 : hIndex, mIndex === -1 ? 0 : mIndex];
|
||||||
|
};
|
||||||
|
|
||||||
|
const onStartTimeChange = (e) => {
|
||||||
|
const [hIndex, mIndex] = e.detail.value;
|
||||||
|
const time = `${hours[hIndex]}:${minutes[mIndex]}`;
|
||||||
|
data.ticketsInfo.flightInfo.startTime = time;
|
||||||
|
updateDuration();
|
||||||
}
|
}
|
||||||
console.log('data.ticketsInfo', data.ticketsInfo)
|
|
||||||
uni.setStorageSync(data.storageKey, data.ticketsInfo)
|
|
||||||
uni.showToast({
|
|
||||||
title: '保存成功',
|
|
||||||
icon: 'success'
|
|
||||||
})
|
|
||||||
uni.navigateBack()
|
|
||||||
}
|
|
||||||
|
|
||||||
const addPassenger = () => {
|
const onEndTimeChange = (e) => {
|
||||||
const lastP = data.ticketsInfo.passengersInfo[data.ticketsInfo.passengersInfo.length - 1]
|
const [hIndex, mIndex] = e.detail.value;
|
||||||
data.ticketsInfo.passengersInfo.push({
|
const time = `${hours[hIndex]}:${minutes[mIndex]}`;
|
||||||
name: '新乘客',
|
data.ticketsInfo.flightInfo.endTime = time;
|
||||||
idType: lastP ? lastP.idType : '身份证',
|
updateDuration();
|
||||||
idNumber: lastP ? lastP.idNumber : '123123********6352',
|
}
|
||||||
ticketNo: lastP ? lastP.ticketNo : '12345678901'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const removePassenger = (index) => {
|
const updateDuration = () => {
|
||||||
uni.showModal({
|
// Simple calc if same day or we can just leave it to user to input manually (which we support via input)
|
||||||
title: '提示',
|
// But let's try a best effort calc
|
||||||
content: '确定要删除该乘客吗?',
|
const ft = data.ticketsInfo.flightInfo
|
||||||
success: (res) => {
|
if (ft.startTime && ft.endTime) {
|
||||||
if (res.confirm) {
|
const [sh, sm] = ft.startTime.split(':').map(Number)
|
||||||
data.ticketsInfo.passengersInfo.splice(index, 1)
|
const [eh, em] = ft.endTime.split(':').map(Number)
|
||||||
|
let diffMin = (eh * 60 + em) - (sh * 60 + sm)
|
||||||
|
if (diffMin < 0) diffMin += 24 * 60 // Assume next day if cross midnight
|
||||||
|
|
||||||
|
const h = Math.floor(diffMin / 60)
|
||||||
|
const m = diffMin % 60
|
||||||
|
if (h > 0) {
|
||||||
|
ft.duration = `${h}时${m}分`
|
||||||
|
} else {
|
||||||
|
ft.duration = `${m}分`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper to extract YYYY-MM-DD from YYYY-MM-DD HH:mm or similar
|
|
||||||
const getDateFromStr = (str) => {
|
|
||||||
if (!str) return ''
|
|
||||||
// Try to match date pattern
|
|
||||||
const dateMatch = str.match(/\d{4}[-.]\d{2}[-.]\d{2}/)
|
|
||||||
if (dateMatch) return dateMatch[0].replace(/\./g, '-')
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
|
|
||||||
const onOrderDateChange = (e) => {
|
|
||||||
const val = e.detail.value // YYYY-MM-DD
|
|
||||||
// orderTime format in default is "YYYY-MM-DD HH:mm"
|
|
||||||
// We need to keep the time part if possible, or default to current time
|
|
||||||
let oldTime = '00:00'
|
|
||||||
if (data.ticketsInfo.orderInfo.orderTime && data.ticketsInfo.orderInfo.orderTime.includes(' ')) {
|
|
||||||
oldTime = data.ticketsInfo.orderInfo.orderTime.split(' ')[1]
|
|
||||||
}
|
}
|
||||||
data.ticketsInfo.orderInfo.orderTime = val + ' ' + oldTime
|
|
||||||
}
|
|
||||||
|
|
||||||
const onFlightDateChange = (e) => {
|
|
||||||
data.ticketsInfo.flightInfo.date = e.detail.value
|
|
||||||
}
|
|
||||||
|
|
||||||
// Custom Time Picker Data
|
|
||||||
const hours = Array.from({ length: 24 }, (_, i) => i.toString().padStart(2, '0'));
|
|
||||||
const minutes = Array.from({ length: 60 }, (_, i) => i.toString().padStart(2, '0'));
|
|
||||||
const timeRange = [hours, minutes];
|
|
||||||
|
|
||||||
const getTimeIndex = (timeStr) => {
|
|
||||||
if (!timeStr) return [0, 0];
|
|
||||||
const [h, m] = timeStr.split(':');
|
|
||||||
const hIndex = hours.findIndex(item => item === h);
|
|
||||||
const mIndex = minutes.findIndex(item => item === m);
|
|
||||||
return [hIndex === -1 ? 0 : hIndex, mIndex === -1 ? 0 : mIndex];
|
|
||||||
};
|
|
||||||
|
|
||||||
const onStartTimeChange = (e) => {
|
|
||||||
const [hIndex, mIndex] = e.detail.value;
|
|
||||||
const time = `${hours[hIndex]}:${minutes[mIndex]}`;
|
|
||||||
data.ticketsInfo.flightInfo.startTime = time;
|
|
||||||
updateDuration();
|
|
||||||
}
|
|
||||||
|
|
||||||
const onEndTimeChange = (e) => {
|
|
||||||
const [hIndex, mIndex] = e.detail.value;
|
|
||||||
const time = `${hours[hIndex]}:${minutes[mIndex]}`;
|
|
||||||
data.ticketsInfo.flightInfo.endTime = time;
|
|
||||||
updateDuration();
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateDuration = () => {
|
|
||||||
// Simple calc if same day or we can just leave it to user to input manually (which we support via input)
|
|
||||||
// But let's try a best effort calc
|
|
||||||
const ft = data.ticketsInfo.flightInfo
|
|
||||||
if (ft.startTime && ft.endTime) {
|
|
||||||
const [sh, sm] = ft.startTime.split(':').map(Number)
|
|
||||||
const [eh, em] = ft.endTime.split(':').map(Number)
|
|
||||||
let diffMin = (eh * 60 + em) - (sh * 60 + sm)
|
|
||||||
if (diffMin < 0) diffMin += 24 * 60 // Assume next day if cross midnight
|
|
||||||
|
|
||||||
const h = Math.floor(diffMin / 60)
|
|
||||||
const m = diffMin % 60
|
|
||||||
ft.duration = `${h}时${m}分`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@import "@/common/main.css";
|
@import "@/common/main.css";
|
||||||
|
|
||||||
page {
|
page {
|
||||||
background-color: #F8F8F8;
|
background-color: #F8F8F8;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.container {
|
.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;
|
display: flex;
|
||||||
justify-content: space-between;
|
flex-direction: column;
|
||||||
align-items: center;
|
height: 100vh;
|
||||||
padding: 24rpx 0 12rpx;
|
|
||||||
border-bottom: 1rpx solid #f5f5f5;
|
|
||||||
margin-bottom: 12rpx;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-header {
|
.form-content {
|
||||||
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;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.label {
|
|
||||||
font-size: 30rpx;
|
|
||||||
color: #333;
|
|
||||||
width: 240rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input {
|
|
||||||
flex: 1;
|
flex: 1;
|
||||||
font-size: 30rpx;
|
height: 0;
|
||||||
color: #333;
|
padding: 24rpx;
|
||||||
text-align: right;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.add-btn-box {
|
.section-container {
|
||||||
background-color: #fff;
|
margin-bottom: 24rpx;
|
||||||
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 {
|
.section-header {
|
||||||
height: 60rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tips-container {
|
|
||||||
padding: 24rpx 0;
|
|
||||||
border-bottom: 1rpx solid #F5F5F5;
|
|
||||||
|
|
||||||
.tips-header {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-bottom: 16rpx;
|
padding: 24rpx 12rpx 16rpx;
|
||||||
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tip-list {
|
.section-title {
|
||||||
.tip-item {
|
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;
|
display: flex;
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
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;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tips-container {
|
||||||
|
padding: 24rpx 0;
|
||||||
|
border-bottom: 1rpx solid #F5F5F5;
|
||||||
|
|
||||||
|
.tips-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
margin-bottom: 16rpx;
|
margin-bottom: 16rpx;
|
||||||
background-color: #F8F8F8;
|
}
|
||||||
padding: 12rpx 16rpx;
|
|
||||||
border-radius: 8rpx;
|
|
||||||
|
|
||||||
.tip-input {
|
.tip-list {
|
||||||
flex: 1;
|
.tip-item {
|
||||||
font-size: 26rpx;
|
|
||||||
color: #333;
|
|
||||||
margin-right: 16rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tip-actions {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 16rpx;
|
justify-content: space-between;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
background-color: #F8F8F8;
|
||||||
|
padding: 12rpx 16rpx;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
|
||||||
|
.tip-input {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #333;
|
||||||
|
margin-right: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tip-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sort-active {
|
||||||
|
background-color: #E6F2FF;
|
||||||
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
|
||||||
|
transition: transform 0.1s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.sort-active {
|
|
||||||
background-color: #E6F2FF;
|
|
||||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
|
|
||||||
transition: transform 0.1s;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
</style>
|
||||||
</style>
|
|
||||||
Loading…
Reference in New Issue