18 KiB
18 KiB
出国行程变更 - 申请行程情况字段调整
📋 修改时间
2025-10-13
🎯 需求说明
调整"二、申请行程情况"和"三、实际行程情况"部分的字段显示:
原字段布局
- 申请出行时间
- 地点
- 在外停留时间
新字段布局
- 前往国家 - 对应数据库字段
original_destination - 出境事由 - 对应数据库字段
original_exit_reason - 行程安排 - 对应数据库字段
original_itinerary
✅ 确认事项
数据库表结构
数据库表 rs_overseas_itinerary_change 已有所需字段,无需修改:
-- 原记录信息(从出国记录表冗余存储)
`original_destination` varchar(100) DEFAULT NULL COMMENT '原目的地国家',
`original_destination_city` varchar(100) DEFAULT NULL COMMENT '原目的地城市',
`original_exit_reason` varchar(100) DEFAULT NULL COMMENT '原出境事由',
`original_start_date` date DEFAULT NULL COMMENT '原出发日期',
`original_end_date` date DEFAULT NULL COMMENT '原返回日期',
`original_duration` int(11) DEFAULT NULL COMMENT '原出国天数',
`original_itinerary` text COMMENT '原行程安排',
-- 变更信息
`new_destination` varchar(100) DEFAULT NULL COMMENT '新目的地国家',
`new_destination_city` varchar(100) DEFAULT NULL COMMENT '新目的地城市',
`new_exit_reason` varchar(100) DEFAULT NULL COMMENT '新出境事由',
`new_start_date` date DEFAULT NULL COMMENT '新出发日期',
`new_end_date` date DEFAULT NULL COMMENT '新返回日期',
`new_duration` int(11) DEFAULT NULL COMMENT '新出国天数',
`new_itinerary` text COMMENT '新行程安排',
后端代码
后端实体类、VO类、Bo类已有相应字段,无需修改。
前端API类型
前端API接口类型定义已有相应字段,无需修改:
export interface ItineraryChangeVO {
// ... 其他字段
originalDestination: string;
originalExitReason: string;
originalItinerary: string;
newDestination?: string;
newExitReason?: string;
newItinerary?: string;
}
📝 修改内容
一、前端表单UI修改
文件: web/src/views/rs/overseas/itinerary-change.vue
1. 二、申请行程情况(只读显示)
修改前:
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="申请出行时间">
<el-input :value="formatDate(form.originalStartDate)" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="地点">
<el-input v-model="form.originalDestination" disabled />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="在外停留时间">
<el-input :value="`${formatDate(form.originalStartDate)} 至 ${formatDate(form.originalEndDate)} (${form.originalDuration || 0}天)`" disabled />
</el-form-item>
</el-col>
</el-row>
修改后:
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="前往国家">
<el-input v-model="form.originalDestination" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="出境事由">
<el-input v-model="form.originalExitReason" disabled />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="申请出行时间">
<el-input :value="`${formatDate(form.originalStartDate)} 至 ${formatDate(form.originalEndDate)}`" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="在外停留天数">
<el-input :value="`${form.originalDuration || 0}天`" disabled />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="行程安排">
<el-input
v-model="form.originalItinerary"
type="textarea"
:rows="3"
disabled
placeholder="原行程安排"
/>
</el-form-item>
</el-col>
</el-row>
2. 三、实际行程情况(用户填写)
修改前:
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="实际出行时间" prop="newStartDate">
<el-date-picker v-model="form.newStartDate" ... />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="地点" prop="newDestination">
<el-input v-model="form.newDestination" ... />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="在外停留时间">
<!-- 日期范围选择器 -->
</el-form-item>
</el-col>
</el-row>
修改后:
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="前往国家" prop="newDestination">
<el-input v-model="form.newDestination" placeholder="请输入实际前往国家" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="出境事由" prop="newExitReason">
<el-input v-model="form.newExitReason" placeholder="请输入实际出境事由" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="实际出行时间" prop="newStartDate">
<el-date-picker
v-model="newDateRange"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="在外停留天数">
<el-input :value="`${form.newDuration || 0}天`" disabled />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="行程安排" prop="newItinerary">
<el-input
v-model="form.newItinerary"
type="textarea"
:rows="3"
placeholder="请输入实际行程安排"
maxlength="1000"
show-word-limit
/>
</el-form-item>
</el-col>
</el-row>
二、数据自动填充逻辑修改
文件: web/src/views/rs/overseas/itinerary-change.vue
修改前:
// 填充申请行程情况
state.form.originalDestination = selectedApplication.destination;
state.form.originalDestinationCity = selectedApplication.destinationCity;
state.form.originalExitReason = selectedApplication.exitReason || selectedApplication.purposeDetail;
state.form.originalStartDate = selectedApplication.startDate;
state.form.originalEndDate = selectedApplication.endDate;
state.form.originalDuration = selectedApplication.duration;
state.form.originalItinerary = selectedApplication.itinerary;
修改后:
// 填充申请行程情况
// 前往国家:优先使用destinationCountry,其次使用destination
state.form.originalDestination = selectedApplication.destinationCountry || selectedApplication.destination;
state.form.originalDestinationCity = selectedApplication.destinationCity;
// 出境事由:优先使用exitReason,其次使用purpose
state.form.originalExitReason = selectedApplication.exitReason || selectedApplication.purpose;
// 行程安排
state.form.originalItinerary = selectedApplication.itinerary;
state.form.originalStartDate = selectedApplication.startDate;
state.form.originalEndDate = selectedApplication.endDate;
state.form.originalDuration = selectedApplication.duration;
// 默认将原信息复制到新信息
state.form.newDestination = selectedApplication.destinationCountry || selectedApplication.destination;
state.form.newDestinationCity = selectedApplication.destinationCity;
state.form.newExitReason = selectedApplication.exitReason || selectedApplication.purpose;
state.form.newItinerary = selectedApplication.itinerary;
state.form.newStartDate = selectedApplication.startDate;
state.form.newEndDate = selectedApplication.endDate;
state.form.newDuration = selectedApplication.duration;
三、日期范围计算属性(新增)
文件: web/src/views/rs/overseas/itinerary-change.vue
新增计算属性(用于日期范围选择器的双向绑定):
// 新出行时间日期范围(用于日期选择器的双向绑定)
const newDateRange = computed({
get() {
if (state.form.newStartDate && state.form.newEndDate) {
return [state.form.newStartDate, state.form.newEndDate];
}
return null;
},
set(value: any) {
if (value && value.length === 2) {
state.form.newStartDate = value[0];
state.form.newEndDate = value[1];
} else {
state.form.newStartDate = '';
state.form.newEndDate = '';
}
}
});
说明:
- 日期选择器需要绑定一个数组
[startDate, endDate] - 计算属性的
get方法将两个独立的日期字段组合成数组 - 计算属性的
set方法将数组拆分成两个独立的日期字段 - 现有的
watch监听newStartDate和newEndDate自动计算天数
四、变更类型自动识别逻辑增强
文件: web/src/views/rs/overseas/itinerary-change.vue
修改前:
// 自动识别变更类型
const changeTypeList: string[] = [];
if (state.form.newDestination !== state.form.originalDestination) {
changeTypeList.push('目的地变更');
}
if (state.form.newStartDate !== state.form.originalStartDate ||
state.form.newEndDate !== state.form.originalEndDate) {
changeTypeList.push('时间变更');
}
if (state.form.newItinerary && state.form.newItinerary !== state.form.originalItinerary) {
changeTypeList.push('行程变更');
}
修改后:
// 自动识别变更类型
const changeTypeList: string[] = [];
if (state.form.newDestination !== state.form.originalDestination) {
changeTypeList.push('目的地变更');
}
if (state.form.newExitReason !== state.form.originalExitReason) {
changeTypeList.push('出境事由变更'); // 新增
}
if (state.form.newStartDate !== state.form.originalStartDate ||
state.form.newEndDate !== state.form.originalEndDate) {
changeTypeList.push('时间变更');
}
if (state.form.newItinerary && state.form.newItinerary !== state.form.originalItinerary) {
changeTypeList.push('行程变更');
}
四、表单验证规则调整
文件: web/src/views/rs/overseas/itinerary-change.vue
修改前:
rules: {
applicationId: [{ required: true, message: '请选择原出国申请', trigger: 'change' }],
newDestination: [{ required: true, message: '请输入实际目的地', trigger: 'blur' }],
newStartDate: [{ required: true, message: '请选择实际出行日期', trigger: 'change' }],
changeReason: [{ required: true, message: '请输入变更原因', trigger: 'blur' }]
}
修改后:
rules: {
applicationId: [{ required: true, message: '请选择原出国申请', trigger: 'change' }],
newDestination: [{ required: true, message: '请输入实际前往国家', trigger: 'blur' }],
newExitReason: [{ required: true, message: '请输入实际出境事由', trigger: 'blur' }], // 新增
newItinerary: [{ required: true, message: '请输入实际行程安排', trigger: 'blur' }], // 新增
changeReason: [{ required: true, message: '请输入变更原因', trigger: 'blur' }]
}
📦 涉及文件清单
前端代码(1个文件)
web/src/views/rs/overseas/itinerary-change.vue- 表单页面
数据库和后端
- 无需修改 - 字段已存在
🎨 新字段展示效果
二、申请行程情况(只读)
┌─────────────────────────────────────────────────────────────┐
│ 二、申请行程情况 │
├─────────────────────────────────────────────────────────────┤
│ 前往国家: [只读] 出境事由: [只读] │
│ 申请出行时间: [只读] 在外停留天数: [只读] │
│ 行程安排: [只读-多行文本框] │
└─────────────────────────────────────────────────────────────┘
三、实际行程情况(可编辑)
┌─────────────────────────────────────────────────────────────┐
│ 三、实际行程情况 │
├─────────────────────────────────────────────────────────────┤
│ 前往国家: [输入框] 出境事由: [输入框] │
│ 实际出行时间: [日期范围选择器] 在外停留天数: [自动计算] │
│ 行程安排: [多行文本框,1000字限制] │
└─────────────────────────────────────────────────────────────┘
🔄 数据流程
1. 用户选择原出国申请
↓
2. 调用 getOverseasApplication(applicationId) 获取申请详情
↓
3. handleApplicationChange 函数自动填充
- 前往国家:destinationCountry 或 destination
- 出境事由:exitReason 或 purpose
- 行程安排:itinerary
↓
4. 用户填写实际情况
- 前往国家(必填)
- 出境事由(必填)
- 行程安排(必填,最多1000字)
↓
5. 系统自动识别变更类型
- 目的地变更
- 出境事由变更 ← 新增
- 时间变更
- 行程变更
↓
6. 提交保存
📊 字段映射关系
出国申请表 → 行程变更表(原信息)
| 出国申请表字段 | 行程变更表字段 | 显示标签 | 说明 |
|---|---|---|---|
| destinationCountry / destination | original_destination | 前往国家 | 优先使用destinationCountry |
| exitReason / purpose | original_exit_reason | 出境事由 | 优先使用exitReason |
| itinerary | original_itinerary | 行程安排 | 多行文本 |
用户填写 → 行程变更表(新信息)
| 用户输入 | 行程变更表字段 | 显示标签 | 验证规则 |
|---|---|---|---|
| 前往国家 | new_destination | 前往国家 | 必填 |
| 出境事由 | new_exit_reason | 出境事由 | 必填 |
| 行程安排 | new_itinerary | 行程安排 | 必填,最多1000字 |
🆚 修改对比
修改前后对比表
| 项目 | 修改前 | 修改后 |
|---|---|---|
| 二、申请行程情况 | ||
| 第一行 | 申请出行时间、地点 | 前往国家、出境事由 |
| 第二行 | 在外停留时间 | 申请出行时间、在外停留天数 |
| 第三行 | - | 行程安排(多行) |
| 三、实际行程情况 | ||
| 第一行 | 实际出行时间、地点 | 前往国家、出境事由 |
| 第二行 | 在外停留时间 | 实际出行时间(日期选择器)、在外停留天数(自动计算) |
| 第三行 | - | 行程安排(多行) |
| 验证规则 | ||
| 必填字段 | 原申请、新目的地、新出行时间、变更原因 | 原申请、新国家、新事由、新行程、变更原因 |
| 变更类型识别 | ||
| 识别项目 | 目的地、时间、行程 | 目的地、出境事由(新增)、时间、行程 |
✅ 优化亮点
- 字段展示完整 - 保留了出行时间和停留天数,同时增加了前往国家、出境事由、行程安排
- 布局更合理 - 行程安排使用全宽多行文本框,便于显示完整内容
- 验证更完善 - 三个核心字段均为必填,确保信息完整性
- 变更识别增强 - 增加了"出境事由变更"的自动识别
- 字段映射灵活 - 兼容新旧两种API字段命名(destinationCountry/destination、exitReason/purpose)
- 用户体验优化 - 多行文本框带字数限制和计数器
🧪 测试要点
1. 数据自动填充测试
- 选择原出国申请后,"前往国家"正确填充(二、三部分)
- 选择原出国申请后,"出境事由"正确填充(二、三部分)
- 选择原出国申请后,"申请出行时间"正确填充(二部分,只读)
- 选择原出国申请后,"实际出行时间"正确填充(三部分,可编辑)
- 选择原出国申请后,"在外停留天数"正确填充(二、三部分)
- 选择原出国申请后,"行程安排"正确填充(二、三部分)
- 字段以只读方式显示
2. 用户输入测试
- "前往国家"输入框正常工作
- "出境事由"输入框正常工作
- "实际出行时间"日期范围选择器正常工作
- 修改日期后,"在外停留天数"自动更新
- 选择跨度天数计算正确(含起止日)
- "行程安排"多行文本框正常工作
- 字数限制(1000字)正常工作
- 字数计数器正常显示
3. 表单验证测试
- 未填写"前往国家"时显示错误提示
- 未填写"出境事由"时显示错误提示
- 未填写"行程安排"时显示错误提示
4. 变更类型识别测试
- 修改"前往国家"后,识别为"目的地变更"
- 修改"出境事由"后,识别为"出境事由变更"
- 修改"行程安排"后,识别为"行程变更"
- 同时修改多项时,正确组合变更类型
5. 数据保存测试
- 提交后,新信息正确保存到数据库
- 查看详情时,字段正确显示
- 编辑已有变更时,字段正确回显
📝 完成状态
✅ 前端表单UI修改完成 ✅ 数据自动填充逻辑更新完成 ✅ 变更类型识别逻辑增强完成 ✅ 表单验证规则调整完成 ✅ 无编译错误和警告
修改完成时间:2025-10-13
涉及文件数量:1个
测试状态:待测试