| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189 |
- import config from '@/config.js'
- /**
- * useDimensionRegistry - 统一维度注册中心(前端只读封装)
- *
- * 健康页 5 张雷达图(身体 / 认知 / 心理 / 社会性 / 财富)原前端硬编码维度清单与取分字段,
- * 现改为从统一维度注册中心 `/api/dimension-registry/tree` 配置驱动取数画图
- * (落实产品要求「健康页面的雷达图也是按这个配置取数据画图」)。
- *
- * 设计要点:
- * 1. 模块级缓存:首次请求成功后整棵树缓存,tab 切换不再重复请求。
- * 2. ready 标志:请求成功或失败后均置位,供页面安全兜底(后端未上线 / 接口异常 → 回落硬编码)。
- * 3. 失败不抛错:ready=false、返回空,页面降级,不阻断渲染。
- * 4. 兼容 uni-app 硬约束:不使用可选链 `?.`、new Date(string)、CSS Grid。
- *
- * 契约(已与后端 DimensionNode / seed 对齐):
- * GET /api/dimension-registry/tree → Result<List<DimensionNode>>
- * DimensionNode: { id, code, name, parentId, nodeKind, sourceSystem, legacyAlias, wuxingRuleRef, children }
- * legacyAlias 是 JSON 数组字符串,如 '["opennessScore"]',取首个元素作为分数字段名 scoreField。
- */
- // 模块级缓存:跨组件 / 跨 tab 复用,避免重复请求
- var _cache = {
- tree: null, // DimensionNode[](5 个根,各带 children)
- ready: false, // 首次请求是否已结束(成功或失败均置位)
- pending: null // 进行中的 Promise(防并发重复请求)
- }
- // tab(code) → 注册中心根 code 映射
- // body→body,cognitive→wisdom,mental→mind,relationship→action,wealth→wealth
- var TAB_TO_ROOT = {
- body: 'body',
- cognitive: 'wisdom',
- mental: 'mind',
- relationship: 'action',
- wealth: 'wealth'
- }
- // 财富子维度在「成员(儿童)」语境下使用更贴近儿童的文案(与改造前字面量一致)
- var WEALTH_CHILD_LABEL = {
- 'wealth.skill': '学业能力',
- 'wealth.income': '积分积累'
- }
- /**
- * 解析 legacyAlias:JSON 数组字符串,取首个元素作为分数字段名 scoreField。
- * 例:'["opennessScore"]' → 'opennessScore';'["driveAutonomy"]' → 'driveAutonomy'
- * @param {string} alias
- * @returns {string}
- */
- function parseLegacyAlias(alias) {
- if (!alias || typeof alias !== 'string') return ''
- var arr = null
- try {
- arr = JSON.parse(alias)
- } catch (e) {
- return ''
- }
- if (!Array.isArray(arr) || arr.length === 0) return ''
- return typeof arr[0] === 'string' ? arr[0] : ''
- }
- /**
- * 触发(或复用)注册中心树拉取。幂等:已尝试过则直接 resolve,不重复请求。
- * @returns {Promise<void>}
- */
- function ensureDimensionRegistry() {
- if (_cache.ready) {
- return Promise.resolve()
- }
- if (_cache.pending) {
- return _cache.pending
- }
- _cache.pending = new Promise(function(resolve) {
- var token = uni.getStorageSync('token')
- var base = (config && config.API_BASE_URL) ? config.API_BASE_URL : ''
- var url = base + '/api/dimension-registry/tree'
- uni.request({
- url: url,
- method: 'GET',
- header: {
- 'Content-Type': 'application/json',
- 'Authorization': token ? 'Bearer ' + token : ''
- },
- success: function(res) {
- try {
- var data = res && res.data
- // Result<List<DimensionNode>> → data.data
- var list = (data && data.code === 200 && data.data) ? data.data : []
- if (Array.isArray(list) && list.length > 0) {
- _cache.tree = list
- }
- } catch (e) {
- // 解析异常:保持 tree 为空,回落兜底
- _cache.tree = null
- }
- // 无论成功 / 空数据 / 解析异常,均标记尝试结束(不抛错)
- _cache.ready = true
- resolve()
- },
- fail: function() {
- // 网络 / 接口异常:不抛错,回落硬编码兜底
- _cache.ready = true
- resolve()
- }
- })
- })
- // 请求结束后清空进行中引用(tree / ready 仍保留)
- _cache.pending.then(function() { _cache.pending = null })
- return _cache.pending
- }
- /**
- * 注册中心树是否就绪(成功取到数据)。
- * @returns {boolean}
- */
- function isRegistryReady() {
- return !!_cache.tree && _cache.tree.length > 0
- }
- /**
- * 取某 tab 的雷达配置:[{ label, code, scoreField }]
- * - label:注册中心节点 name(维度中文名)
- * - code:节点 code(body 类为 body.growth,用于 bodyData 按 dimension 字段匹配)
- * - scoreField:legacyAlias 首元素(取分读取器字段名)
- * @param {string} tabCode
- * @returns {Array}
- */
- function getRadarConfig(tabCode) {
- var rootCode = TAB_TO_ROOT[tabCode]
- if (!rootCode || !_cache.tree || !Array.isArray(_cache.tree)) return []
- var root = null
- for (var i = 0; i < _cache.tree.length; i++) {
- var node = _cache.tree[i]
- if (node && node.code === rootCode) {
- root = node
- break
- }
- }
- if (!root || !root.children || !root.children.length) return []
- var config = []
- for (var j = 0; j < root.children.length; j++) {
- var child = root.children[j]
- if (!child) continue
- // P1-T3 防御:指标节点(nodeKind=indicator)是维度树的混排子节点,
- // 不应作为健康页雷达图的轴(雷达轴应为 sub-dim 维度节点)。
- // 操作者将指标挂到某维度节点下后,该节点 children 会含指标,过滤掉保持雷达 5×7/7/10/5/4 不变。
- if (child.nodeKind === 'indicator') continue
- config.push({
- label: child.name || '',
- code: child.code || '',
- scoreField: parseLegacyAlias(child.legacyAlias)
- })
- }
- return config
- }
- /**
- * 取注册中心 5 根节点元信息(五行归属):[{ code, name, element: wuxingRuleRef }]。
- *
- * <p>供五行能量沙盘维度名称 / 五行元素兜底(统一 code + wuxing_rule_ref),
- * 未就绪返回空数组(调用方回落组件内 DIM_META)。</p>
- *
- * @returns {Array<{code:string,name:string,element:string}>}
- */
- function getRegistryRootMeta() {
- if (!_cache.tree || !Array.isArray(_cache.tree)) return []
- var out = []
- for (var i = 0; i < _cache.tree.length; i++) {
- var node = _cache.tree[i]
- if (!node || !node.code) continue
- // 仅维度根节点(nodeKind=root;旧数据无 nodeKind 的按「无父节点」视为根)
- if (node.nodeKind && node.nodeKind !== 'root') continue
- out.push({
- code: node.code,
- name: node.name || '',
- element: node.wuxingRuleRef || ''
- })
- }
- return out
- }
- export {
- ensureDimensionRegistry,
- getRadarConfig,
- getRegistryRootMeta,
- isRegistryReady,
- WEALTH_CHILD_LABEL
- }
|