|
|
@@ -50,6 +50,20 @@ function seededRand(seed) {
|
|
|
return x - Math.floor(x)
|
|
|
}
|
|
|
|
|
|
+/** 根据路口 ID 生成稳定 seed(全字符加权,所有 API 共用) */
|
|
|
+function _idSeed(id) {
|
|
|
+ return id ? Array.from(id).reduce((s, c, i) => s + c.charCodeAt(0) * (i + 1), 0) : 0
|
|
|
+}
|
|
|
+
|
|
|
+/** 根据路口 ID 获取稳定的 cycleLength(优先 preset → crossingList → seed 兜底) */
|
|
|
+function _getCycleLength(id) {
|
|
|
+ const preset = DB.signalTimings[id]
|
|
|
+ if (preset) return preset.data.cycleLength
|
|
|
+ const crossing = DB.crossingList.find(r => r.id === id)
|
|
|
+ if (crossing && crossing.cycle) return crossing.cycle
|
|
|
+ return [100, 120, 130, 140, 150, 160][_idSeed(id) % 6]
|
|
|
+}
|
|
|
+
|
|
|
/** 当前时间 HH:MM:SS */
|
|
|
function nowTime() { return new Date().toLocaleTimeString() }
|
|
|
function nowDate() { return new Date().toLocaleDateString() }
|
|
|
@@ -156,7 +170,14 @@ function _makeIntersectionConfig(id, name, { fixedNsGreen, iconMode = 'default'
|
|
|
* @param {number} cycleLength 周期总时长
|
|
|
* @param {boolean} isTwoRows 是否生成上下双排 8 相位 (默认 true)
|
|
|
*/
|
|
|
-function _makePhaseData(cycleLength = 140, isTwoRows = true, iconMode = 'default') {
|
|
|
+// 相位数据缓存:同一路口 (cycleLength+iconMode) 只生成一次,列表和详情弹窗共享
|
|
|
+const _phaseDataCache = {};
|
|
|
+
|
|
|
+function _makePhaseData(cycleLength = 140, isTwoRows = true, iconMode = 'default', id = '') {
|
|
|
+ // 缓存 key:用路口 ID(有的话),保证同一路口列表和详情共享同一份数据
|
|
|
+ const cacheKey = id || `${cycleLength}_${iconMode}`;
|
|
|
+ if (_phaseDataCache[cacheKey]) return _phaseDataCache[cacheKey];
|
|
|
+
|
|
|
const n = 4; // 4个阶段 (S1-S4)
|
|
|
// 各阶段按比例分配时间,P1/P3较长,P2/P4较短
|
|
|
const ratios = [0.3, 0.2, 0.3, 0.2];
|
|
|
@@ -165,10 +186,6 @@ function _makePhaseData(cycleLength = 140, isTwoRows = true, iconMode = 'default
|
|
|
stageTimes[0] += cycleLength - stageTimes.reduce((a, b) => a + b, 0);
|
|
|
const pd = [];
|
|
|
|
|
|
- // ==========================================
|
|
|
- // 修改点:将单个图标改为用逗号分隔的"成对图标"字符串
|
|
|
- // 前端组件会按逗号切割并分别放到对角位置
|
|
|
- // ==========================================
|
|
|
// 固定4个阶段的图标和方向:P1南北直行、P2南北左转、P3东西直行、P4东西左转
|
|
|
const phaseConfigMap = {
|
|
|
default: [
|
|
|
@@ -218,11 +235,13 @@ function _makePhaseData(cycleLength = 140, isTwoRows = true, iconMode = 'default
|
|
|
|
|
|
pushTrackData(0, 'P'); // 生成第一排 (P1-P4)
|
|
|
if (isTwoRows) {
|
|
|
- pushTrackData(1, 'P'); // 生成第二排 (P5-P8,由于逻辑相同,名称可根据需要改为 i+5)
|
|
|
+ pushTrackData(1, 'P'); // 生成第二排
|
|
|
}
|
|
|
|
|
|
t = stageEnd;
|
|
|
}
|
|
|
+
|
|
|
+ _phaseDataCache[cacheKey] = pd;
|
|
|
return pd;
|
|
|
}
|
|
|
|
|
|
@@ -375,19 +394,11 @@ export async function apiGetIntersectionData(id, { fixedNsGreen } = {}) {
|
|
|
*/
|
|
|
export async function apiGetSignalTiming(id) {
|
|
|
await delay(300)
|
|
|
- const preset = DB.signalTimings[id]
|
|
|
- if (preset) {
|
|
|
- const cycleLength = preset.data.cycleLength
|
|
|
- return {
|
|
|
- code: 200, message: 'success',
|
|
|
- data: { ...preset.data, currentTime: Math.floor(Date.now() / 1000) % cycleLength }
|
|
|
- }
|
|
|
- }
|
|
|
- const cycleLength = [100, 120, 130, 140, 150, 160][Math.floor(Math.random() * 6)]
|
|
|
+ const cycleLength = _getCycleLength(id)
|
|
|
return ok({
|
|
|
cycleLength,
|
|
|
currentTime: Math.floor(Date.now() / 1000) % cycleLength,
|
|
|
- phaseData: _makePhaseData(cycleLength, false),
|
|
|
+ phaseData: _makePhaseData(cycleLength, false, 'simple', id),
|
|
|
})
|
|
|
}
|
|
|
|
|
|
@@ -661,12 +672,8 @@ export async function apiGetCrossingList(params = {}) {
|
|
|
const page = params.page || 1
|
|
|
const pageOffset = Math.floor(seededRand(page * 97) * 120)
|
|
|
let list = DB.crossingList.map((r, i) => {
|
|
|
- const preset = DB.signalTimings[r.id]
|
|
|
- const cycleLength = preset ? preset.data.cycleLength : r.cycle
|
|
|
- // const phaseData = preset ? preset.data.phaseData : _makePhaseData(cycleLength, false)
|
|
|
-
|
|
|
- // 强制全部用 _makePhaseData 动态生成成对箭头
|
|
|
- const phaseData = _makePhaseData(cycleLength, false)
|
|
|
+ const cycleLength = _getCycleLength(r.id)
|
|
|
+ const phaseData = _makePhaseData(cycleLength, false, 'simple', r.id)
|
|
|
return {
|
|
|
...r,
|
|
|
status: _getDeviceStatus(r.id),
|
|
|
@@ -850,15 +857,14 @@ export async function apiGetCrossingPanelData(id) {
|
|
|
await delay(300)
|
|
|
const point = DB.points.find(p => p.id === id)
|
|
|
const config = DB.intersectionConfigs[id] || _makeIntersectionConfig(id, null, { fixedNsGreen: false, iconMode: 'simple' })
|
|
|
- const seed = id ? id.charCodeAt(id.length - 1) : 0
|
|
|
+ const seed = _idSeed(id)
|
|
|
// 确保 config 有 status
|
|
|
if (!config.status) {
|
|
|
config.status = _getDeviceStatus(id)
|
|
|
}
|
|
|
|
|
|
- const preset = DB.signalTimings[id]
|
|
|
- const cycleLength = preset ? preset.data.cycleLength : [100, 120, 130, 140, 150, 160][Math.abs(seed) % 6]
|
|
|
- const phaseData = preset ? preset.data.phaseData : _makePhaseData(cycleLength, false, 'simple')
|
|
|
+ const cycleLength = _getCycleLength(id)
|
|
|
+ const phaseData = _makePhaseData(cycleLength, false, 'simple', id)
|
|
|
const currentTime = Math.floor(Date.now() / 1000) % cycleLength
|
|
|
|
|
|
return ok({
|
|
|
@@ -882,8 +888,7 @@ export async function apiGetCrossingDetailData(id, { iconMode = 'default' } = {}
|
|
|
const point = DB.points.find(p => p.id === id)
|
|
|
const config = DB.intersectionConfigs[id] || _makeIntersectionConfig(id, null, { fixedNsGreen: false, iconMode })
|
|
|
|
|
|
- // 用 id 的全部字符生成稳定 seed(加权位置避免 charCode 总和碰撞)
|
|
|
- const seed = id ? Array.from(id).reduce((s, c, i) => s + c.charCodeAt(0) * (i + 1), 0) : 0
|
|
|
+ const seed = _idSeed(id)
|
|
|
|
|
|
// 确保 config 有 status 字段(预存配置可能缺失)
|
|
|
if (!config.status) {
|
|
|
@@ -891,9 +896,8 @@ export async function apiGetCrossingDetailData(id, { iconMode = 'default' } = {}
|
|
|
}
|
|
|
|
|
|
// 从真实阶段数据推导周期和相位
|
|
|
- const preset = DB.signalTimings[id]
|
|
|
- const cycleLength = preset ? preset.data.cycleLength : [100, 120, 130, 140, 150, 160][seed % 6]
|
|
|
- const phaseData = preset ? preset.data.phaseData : _makePhaseData(cycleLength || 140, false, iconMode)
|
|
|
+ const cycleLength = _getCycleLength(id)
|
|
|
+ const phaseData = _makePhaseData(cycleLength, false, 'simple', id)
|
|
|
|
|
|
// 从相位数据中提取阶段列表(优先上轨道绿灯相位,单轨道时取 track 0,最多4个)
|
|
|
const hasTrack1 = phaseData.some(p => p[0] === 1)
|