431 lines
12 KiB
Vue
431 lines
12 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.price" />
|
|
</view>
|
|
<!-- 全局票号 -->
|
|
<view class="form-item">
|
|
<text class="label">联系电话</text>
|
|
<input class="input" v-model="ticketsInfo.ticketNumber" placeholder="用于掩码显示" />
|
|
</view>
|
|
<view class="form-item">
|
|
<text class="label">订单号</text>
|
|
<input class="input" v-model="ticketsInfo.orderInfo.orderNo" />
|
|
</view>
|
|
<uni-datetime-picker type="datetime" v-model="ticketsInfo.orderInfo.orderTime" :border="false">
|
|
<view class="form-item">
|
|
<text class="label">下单时间</text>
|
|
<view class="input">{{ ticketsInfo.orderInfo.orderTime }}</view>
|
|
</view>
|
|
</uni-datetime-picker>
|
|
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 航班信息 -->
|
|
<view class="section-container">
|
|
<view class="section-header" @click="toggleSection('flightInfo')">
|
|
<text class="section-title">航班信息</text>
|
|
<uni-icons :type="collapsed.flightInfo ? 'bottom' : 'top'" size="16" color="#666"></uni-icons>
|
|
</view>
|
|
<view class="card" v-show="!collapsed.flightInfo">
|
|
<picker mode="selector" :range="airLineList" range-key="name" @change="onAirlineChange">
|
|
<view class="form-item">
|
|
<text class="label">航空公司</text>
|
|
<view class="input">{{ ticketsInfo.flightInfo.airline }}</view>
|
|
</view>
|
|
</picker>
|
|
<view class="form-item">
|
|
<text class="label">航班号</text>
|
|
<input class="input" v-model="ticketsInfo.flightInfo.flightNumber" />
|
|
</view>
|
|
<picker mode="date" fields="day" :value="ticketsInfo.flightInfo.date" @change="onFlightDateChange">
|
|
<view class="form-item">
|
|
<text class="label">日期</text>
|
|
<view class="input">{{ ticketsInfo.flightInfo.date }}</view>
|
|
</view>
|
|
</picker>
|
|
<picker mode="multiSelector" :range="timeRange"
|
|
:value="getTimeIndex(ticketsInfo.flightInfo.startTime)" @change="onStartTimeChange">
|
|
<view class="form-item">
|
|
<text class="label">起飞时间</text>
|
|
<view class="input">{{ ticketsInfo.flightInfo.startTime }}</view>
|
|
</view>
|
|
</picker>
|
|
<picker mode="multiSelector" :range="timeRange"
|
|
:value="getTimeIndex(ticketsInfo.flightInfo.endTime)" @change="onEndTimeChange">
|
|
<view class="form-item">
|
|
<text class="label">到达时间</text>
|
|
<view class="input">{{ ticketsInfo.flightInfo.endTime }}</view>
|
|
</view>
|
|
</picker>
|
|
<view class="form-item">
|
|
<text class="label">出发城市</text>
|
|
<input class="input" v-model="ticketsInfo.flightInfo.startCity" />
|
|
</view>
|
|
<view class="form-item">
|
|
<text class="label">到达城市</text>
|
|
<input class="input" v-model="ticketsInfo.flightInfo.endCity" />
|
|
</view>
|
|
<view class="form-item">
|
|
<text class="label">出发机场</text>
|
|
<input class="input" v-model="ticketsInfo.flightInfo.startAirport" />
|
|
</view>
|
|
<view class="form-item">
|
|
<text class="label">到达机场</text>
|
|
<input class="input" v-model="ticketsInfo.flightInfo.endAirport" />
|
|
</view>
|
|
<!-- <view class="form-item">
|
|
<text class="label">时长</text>
|
|
<input class="input" v-model="ticketsInfo.flightInfo.duration" placeholder="例: 2时5分" />
|
|
</view> -->
|
|
<view class="form-item">
|
|
<text class="label">机型</text>
|
|
<input class="input" v-model="ticketsInfo.flightInfo.aircraftType" />
|
|
</view>
|
|
<view class="form-item">
|
|
<text class="label">舱位</text>
|
|
<input class="input" v-model="ticketsInfo.flightInfo.seatCategory" />
|
|
</view>
|
|
<view class="form-item">
|
|
<text class="label">准点率</text>
|
|
<input class="input" v-model="ticketsInfo.flightInfo.onTimeRate" />
|
|
</view>
|
|
<view class="form-item">
|
|
<text class="label">餐食</text>
|
|
<input class="input" v-model="ticketsInfo.flightInfo.meal" />
|
|
</view>
|
|
<view class="form-item">
|
|
<text class="label">行李额</text>
|
|
<input class="input" v-model="ticketsInfo.flightInfo.luggageCheckInQuota" />
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 乘客信息 -->
|
|
<view class="section-container">
|
|
<view class="section-header" @click="toggleSection('passengersInfo')">
|
|
<text class="section-title">乘客信息 ({{ ticketsInfo.passengersInfo.length }}人)</text>
|
|
<uni-icons :type="collapsed.passengersInfo ? 'bottom' : 'top'" size="16" color="#666"></uni-icons>
|
|
</view>
|
|
|
|
<view v-show="!collapsed.passengersInfo">
|
|
<view class="card" v-for="(passenger, index) in ticketsInfo.passengersInfo" :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>
|
|
<!-- <view class="form-item">
|
|
<text class="label">证件类型</text>
|
|
<input class="input" v-model="passenger.idType" />
|
|
</view> -->
|
|
<view class="form-item">
|
|
<text class="label">身份证号</text>
|
|
<input class="input" v-model="passenger.idNumber" />
|
|
</view>
|
|
<view class="form-item">
|
|
<text class="label">票号</text>
|
|
<input class="input" v-model="passenger.ticketNo" />
|
|
</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 class="placeholder"></view>
|
|
</scroll-view>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup>
|
|
import NavBar from '@/components/nav-bar/nav-bar.vue'
|
|
import { reactive, toRefs, onMounted } from 'vue';
|
|
import defualtData from '@/pages/other/air-tickets/commom/defualt.json';
|
|
import airlineJson from '@/static/json/air-line.json';
|
|
|
|
const airLineList = airlineJson.airLine;
|
|
|
|
const data = reactive({
|
|
ticketsInfo: JSON.parse(JSON.stringify(defualtData)),
|
|
collapsed: {
|
|
orderInfo: false,
|
|
flightInfo: false,
|
|
passengersInfo: false
|
|
}
|
|
})
|
|
|
|
const { ticketsInfo, collapsed } = 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;
|
|
}
|
|
}
|
|
|
|
onMounted(() => {
|
|
const stored = uni.getStorageSync('airTicketsInfo')
|
|
if (stored) {
|
|
// Deep merge or just assign if structure matches
|
|
Object.assign(data.ticketsInfo, stored)
|
|
}
|
|
})
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
console.log('data.ticketsInfo', data.ticketsInfo)
|
|
uni.setStorageSync('airTicketsInfo', data.ticketsInfo)
|
|
uni.showToast({
|
|
title: '保存成功',
|
|
icon: 'success'
|
|
})
|
|
uni.navigateBack()
|
|
}
|
|
|
|
const addPassenger = () => {
|
|
const lastP = data.ticketsInfo.passengersInfo[data.ticketsInfo.passengersInfo.length - 1]
|
|
data.ticketsInfo.passengersInfo.push({
|
|
name: '新乘客',
|
|
idType: lastP ? lastP.idType : '身份证',
|
|
idNumber: lastP ? lastP.idNumber : '123123********6352',
|
|
ticketNo: lastP ? lastP.ticketNo : '12345678901'
|
|
})
|
|
}
|
|
|
|
const removePassenger = (index) => {
|
|
uni.showModal({
|
|
title: '提示',
|
|
content: '确定要删除该乘客吗?',
|
|
success: (res) => {
|
|
if (res.confirm) {
|
|
data.ticketsInfo.passengersInfo.splice(index, 1)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
// 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>
|
|
|
|
<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;
|
|
|
|
|
|
|
|
.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>
|