/** * 设施点位与管线图层状态(票 24)。 * * 与监测点摘要(`mapStore`)**分开持有**:两者是不同业务对象、不同读取路径、不同统计口径 * (管线不进地图摘要总数,监测点也不当管线渲染)。合并成一个 store 会让两种口径互相污染。 * * 失败纪律沿用全舱:几何取不到只让**设施/管线图层**降级,不牵连监测点、设备与面板, * 也绝不回退 mock(票 04 的 real 纪律)。 */ import { computed, ref } from 'vue' import { defineStore } from 'pinia' import { SECTION_STATE, section } from '../domain/sectionState.js' import { REQUEST_REASON } from '../domain/requestReason.js' import { filterPipeNetwork } from '../domain/pipeNetwork.js' import { fetchPipeNetwork } from '../services/pipeLayerService.js' /** 未取数时的分区(与其它 store 同形,`loaded:false` 表示"还没请求过")。 */ function pending() { return { state: SECTION_STATE.EMPTY, reason: '', mode: '', data: null, missing: [], freshness: '', retained: false, loaded: false } } const EMPTY_LAYER = { pipes: [], components: [], blocked: [], coverage: {}, counts: {}, districtApplied: false, districtUnresolvedCount: 0 } export const usePipeNetworkStore = defineStore('pipeNetworkStore', () => { const layer = ref(pending()) const industry = ref('all') /** * 当前生效的片区筛选(前端键;票 67 更正)。 * * 与行业同层、同一套纪律:几何是**全量取回、本地裁剪**,所以切片区只改本地筛选条件、 * 不重新发请求(切换必须即时)。片区的来源是**设施编码前缀**(片区+行业+时间编码), * 见 `domain/pipeNetwork.js` 的 `pipeDistrictOf`。 */ const scope = ref('all') let requestOptions = {} /** 请求注入(config / fetchImpl):取一次让后续取数共用。 */ function configure(options = {}) { requestOptions = { ...requestOptions, ...options } } /** 只读写入分区(走查与单测注入用)。 */ function setLayer(value) { layer.value = { ...value, loaded: true } } /** 当前行业 + 片区筛选下的图层(图层过滤只此一处,引擎与组件都不做第二次过滤)。 */ const filtered = computed(() => filterPipeNetwork(layer.value.data || EMPTY_LAYER, { industry: industry.value, scope: scope.value }), ) /** 取数成功(含真实为空)才交数组;**失败或还没取过一律交 `null`**。 */ function layerOrNull(list) { const current = layer.value const settled = current.loaded === true && [SECTION_STATE.SUCCESS, SECTION_STATE.EMPTY, SECTION_STATE.STALE].includes(current.state) return settled ? list : null } /** 给 `modeLayerPlan` 的两段(与计划里的 `facilities`/`pipelines` 同形)。 */ const facilities = computed(() => layerOrNull(filtered.value.components)) const pipelines = computed(() => layerOrNull(filtered.value.pipes)) /** 被拦下的对象(「无地图位置」清单的来源,带中文原因)。 */ const blocked = computed(() => filtered.value.blocked) /** 行业覆盖:哪个行业有几何、哪个没有,由后端 `coverage` 表达,前端只转述。 */ const coverage = computed(() => filtered.value.coverage) const loaded = computed(() => layer.value.loaded === true) const state = computed(() => layer.value.state) const reason = computed(() => layer.value.reason) /** 只改行业筛选(几何已全量取回,切行业不重新发请求)。 */ function setIndustry(next) { industry.value = typeof next === 'string' && next ? next : 'all' return industry.value } /** 只改片区筛选(同上:本地裁剪,不重新发请求)。 */ function setScope(next) { scope.value = typeof next === 'string' && next ? next : 'all' return scope.value } async function load(options = {}) { if (options.industry !== undefined) industry.value = options.industry if (options.scope !== undefined) scope.value = options.scope if (Object.keys(options).some((key) => !['industry', 'scope'].includes(key))) configure(options) let result = null try { result = await fetchPipeNetwork(requestOptions) } catch { // 适配层不抛;真抛了也要收成失败分区,不能让一次几何失败掀掉整个外壳。 result = section(SECTION_STATE.FAILED, { reason: REQUEST_REASON.UNREACHABLE, data: null }) } layer.value = { ...result, loaded: true } return layer.value } return { layer, loaded, state, reason, industry, scope, filtered, facilities, pipelines, blocked, coverage, load, setIndustry, setScope, configure, setLayer } })