feat(web): 站点编辑支持修改所属线路及编号级联更新

1. 在“站点编辑”弹窗中新增了“所属线路”的多选组件。现在可以在编辑站名和换乘时,一并把该站添加到新线路或从现有线路中移除了。
2. 重构了 saveStationSettings,不仅保存站点本身信息,还会对比前后的线路差异(增加、删除或站点编号变更),并自动调用 updateLineStations 接口保存,确保“站点管理”里的操作能实时反映到“线路规划”的可视化拓扑图中。
This commit is contained in:
2026-08-08 10:08:40 +08:00
parent cf0a49867c
commit 3d7fa994ae
3 changed files with 42 additions and 2 deletions
+1 -1
View File
@@ -7,7 +7,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>控制台|FSE铁路运输票务系统</title> <title>控制台|FSE铁路运输票务系统</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="stylesheet" href="style.css?v=20"> <link rel="stylesheet" href="style.css?v=21">
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script> <script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<script src="/socket.io/socket.io.js"></script> <script src="/socket.io/socket.io.js"></script>
</head> </head>
+10 -1
View File
@@ -8,7 +8,7 @@
<title>线路规划|FSE铁路运输票务系统</title> <title>线路规划|FSE铁路运输票务系统</title>
<link rel="icon" type="image/png" href="/FSE-ticket.png"> <link rel="icon" type="image/png" href="/FSE-ticket.png">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="stylesheet" href="/style.css?v=20"> <link rel="stylesheet" href="/style.css?v=21">
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script> <script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<script src="/socket.io/socket.io.js"></script> <script src="/socket.io/socket.io.js"></script>
</head> </head>
@@ -524,6 +524,15 @@
<label>英文名</label> <label>英文名</label>
<input v-model="stationForm.en_name" class="w-100"> <input v-model="stationForm.en_name" class="w-100">
</div> </div>
<div class="mb-4">
<label>所属线路</label>
<select v-model="stationForm.lines" multiple class="w-100" style="height: 100px;">
<option v-for="l in lines" :key="l.id" :value="l.id">
{{ l.name || l.id }} ({{ l.id }})
</option>
</select>
<p class="text-muted mt-1" style="font-size: 0.8em;">按住 Ctrl / Command 多选,保存后会自动同步到线路图中。</p>
</div>
<div class="mb-4"> <div class="mb-4">
<label class="switch-label"> <label class="switch-label">
<input type="checkbox" v-model="stationForm.transfer_enabled"> <input type="checkbox" v-model="stationForm.transfer_enabled">
+31
View File
@@ -749,6 +749,10 @@ createApp({
stationForm.en_name = s.en_name || s.enName || ''; stationForm.en_name = s.en_name || s.enName || '';
stationForm.transfer_enabled = !!s.transfer_enabled; stationForm.transfer_enabled = !!s.transfer_enabled;
stationForm.transfer_to = Array.isArray(s.transfer_to) ? [...s.transfer_to] : []; stationForm.transfer_to = Array.isArray(s.transfer_to) ? [...s.transfer_to] : [];
// 找出该站点当前所属的线路
stationForm.lines = lines.value.filter(l => l.stations && l.stations.includes(s.code || code)).map(l => l.id);
showStationModal.value = true; showStationModal.value = true;
}; };
@@ -777,7 +781,34 @@ createApp({
transfer_to: stationForm.transfer_enabled ? (Array.isArray(stationForm.transfer_to) ? stationForm.transfer_to : []) : [] transfer_to: stationForm.transfer_enabled ? (Array.isArray(stationForm.transfer_to) ? stationForm.transfer_to : []) : []
}; };
await runMutation(async () => { await runMutation(async () => {
// 1. 保存站点本身信息
await requestJson(`/api/stations/${encodeURIComponent(oldCode)}`, { method: 'PUT', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(payload) }, { expectOk: true }); await requestJson(`/api/stations/${encodeURIComponent(oldCode)}`, { method: 'PUT', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(payload) }, { expectOk: true });
// 2. 更新所属线路
const newLinesIds = Array.isArray(stationForm.lines) ? stationForm.lines : [];
for (const line of lines.value) {
const wasInLine = line.stations && line.stations.includes(oldCode);
const shouldBeInLine = newLinesIds.includes(line.id);
let lineStations = [...(line.stations || [])];
let changed = false;
if (wasInLine && !shouldBeInLine) {
lineStations = lineStations.filter(c => c !== oldCode);
changed = true;
} else if (!wasInLine && shouldBeInLine) {
lineStations.push(newCode);
changed = true;
} else if (wasInLine && shouldBeInLine && oldCode !== newCode) {
lineStations = lineStations.map(c => c === oldCode ? newCode : c);
changed = true;
}
if (changed) {
await updateLineStations(line.id, lineStations, { skipFetchData: true });
}
}
showStationModal.value = false; showStationModal.value = false;
await fetchData(); await fetchData();
}); });