feat(web): 订票地图升级为静态SVG并叠加动态坐标交互;完善断点支持

This commit is contained in:
2026-07-11 21:15:48 +08:00
parent 690173c522
commit aede84f164
21 changed files with 316 additions and 262 deletions
+66 -2
View File
@@ -1139,6 +1139,43 @@ createApp({
const zoomFareMapOut = () => { fareMapScale.value = Math.max(0.3, Math.round((fareMapScale.value - 0.1) * 100) / 100); };
const zoomFareMapReset = () => { fareMapScale.value = 1; };
const mapBindingMode = ref(false);
const mapBindingStation = ref('');
const mapImageRef = ref(null);
const onMapImageClick = async (e) => {
if (!mapBindingMode.value || !mapBindingStation.value) return;
const img = mapImageRef.value;
if (!img) return;
const rect = img.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
const percentX = (x / rect.width) * 100;
const percentY = (y / rect.height) * 100;
const station = stations.value.find(s => s.code === mapBindingStation.value);
if (!station) return;
station.svg_x = Number(percentX.toFixed(2));
station.svg_y = Number(percentY.toFixed(2));
try {
await requestJson(`/api/stations/${encodeURIComponent(station.code)}`, {
method: 'PUT',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(station)
}, { expectOk: true });
// Optional: find the next station without coordinates and select it
const nextStation = stations.value.find(s => s.svg_x == null && s.svg_y == null);
if (nextStation) {
mapBindingStation.value = nextStation.code;
}
} catch (err) {
alert('保存坐标失败:' + err.message);
}
};
// --- Management Actions ---
const createStation = async () => {
@@ -1229,6 +1266,22 @@ createApp({
await updateLineStations(selectedLine.value.id, newStations);
};
const removeStationFromLineIndex = async (index) => {
if (!selectedLine.value) return;
const newStations = [...(selectedLine.value.stations || [])];
newStations.splice(index, 1);
await updateLineStations(selectedLine.value.id, newStations);
};
const addBreakpointToLine = async () => {
if (!selectedLine.value) return;
await runMutation(async () => {
const newStations = [...(selectedLine.value.stations || [])];
newStations.push('-');
await updateLineStations(selectedLine.value.id, newStations, { skipFetchData: true });
});
};
const updateLineStations = async (lineId, stationsList, { skipFetchData } = {}) => {
// Need to update the whole line object usually, or a specific endpoint
// Assuming PUT /api/lines/:id updates fields provided
@@ -1336,11 +1389,21 @@ createApp({
});
};
const handleStationClick = async (code) => {
const handleStationClick = async (code, index) => {
if (lineViewportPan.moved) {
lineViewportPan.moved = false;
return;
}
if (code === '-') {
if (await appDialog.confirm({
title: '移除断点',
message: `确定移除该断点?`,
confirmText: '确认移除'
})) {
await removeStationFromLineIndex(index);
}
return;
}
if (stationEditMode.value) {
openStationModal(code);
return;
@@ -1730,12 +1793,13 @@ createApp({
showAddLine, showAddStation, newLine, newStation, fareMapSvg, ticketSearch,
assetsManifest, assetsFarePreview, assetsRouteMapUrl, assetsFareTableUrl,
uploadRouteMap, uploadFareTable, deleteRouteMap, deleteFareTable,
mapBindingMode, mapBindingStation, mapImageRef, onMapImageClick,
// Management
selectedLine, fareMode, stationEditMode, fareSelection, showFareModal, currentFare, availableStations,
visualLineViewport, lineViewportPan, lineEditorSvgWidth, startLineViewportPan, moveLineViewportPan,
selectLine, createLine, deleteLine, deleteStation, updateLineInfo, getFareText,
isStationInLine, addStationToLine, removeStationFromLine,
isStationInLine, addStationToLine, removeStationFromLine, removeStationFromLineIndex, addBreakpointToLine,
handleStationClick, isStationSelected,
onStationDragStart, onStationDragOver, onStationDrop, draggingStationIndex,
showStationModal, stationForm, stationFormOriginalCode, transferTargets, saveStationSettings, closeStationModal,