System.vue 4.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. <script setup lang="ts">
  2. /**
  3. * 维护巡检(system)——逐字对应重构前 `app.js` 的 `system()`(app.js:746-760)。
  4. *
  5. * 数据:`/api/maint_survey` → `{rows: [{层, 存在, 名, 覆盖, 条数, 末次更新, 频率, 责任, 位置, 摄入, 说明}], error?}`。
  6. * 三种分支:未取到("正在巡检…")· 出错 · 两层表(数据 / 机理)。
  7. */
  8. import { computed, ref, watch } from 'vue'
  9. import { PORTAL_RECALC_HTML } from '../lib/portalRecalc'
  10. import { PORTAL_CSS } from '../lib/portalSections'
  11. import { t } from '../i18n/zh'
  12. import { fmt } from '../lib/render'
  13. import { dv, esc } from '../lib/vib'
  14. const props = defineProps<{ apis?: Record<string, any> | null }>()
  15. /** 旧 `publicMeta(x, kind)`(app.js:815):中文口径 = 原样显示 */
  16. const publicMeta = (x: unknown): string => String(x == null ? '' : x)
  17. /** ★P68 用**门户 CSS + 该节原文**装进同源 iframe(srcdoc),与门户四节同一手法 ⇒ 渲染与 v2.9.2 一致 */
  18. const recalcBlobUrl = ref<string>('')
  19. const _recalcSrc = computed<string>(() => '<!doctype html><html lang="zh" data-theme="light"><head>'
  20. + '<meta charset="utf-8"><base target="_top"><style>' + PORTAL_CSS + '</style></head><body>'
  21. + PORTAL_RECALC_HTML + '</body></html>')
  22. watch(_recalcSrc, (html) => {
  23. const old = recalcBlobUrl.value
  24. recalcBlobUrl.value = URL.createObjectURL(new Blob([html], { type: 'text/html;charset=utf-8' }))
  25. if (old) { try { URL.revokeObjectURL(old) } catch { /* ignore */ } }
  26. }, { immediate: true })
  27. const md = computed<any>(() => props.apis?.['/detail/api/maint_survey'] ?? props.apis?.['/api/maint_survey'] ?? null)
  28. /** 旧 `LAYERS`(app.js:752):层字段中英值都认 */
  29. const LAYERS: [string[], string, string, string][] = [
  30. [['数据', 'Data'], 'mt.layer_data', 'mt.layer_data_note', 'var(--accent)'],
  31. [['机理', 'Domain model', 'Mechanism'], 'mt.layer_mech', 'mt.layer_mech_note', 'var(--warning)'],
  32. ]
  33. const rows = computed<any[]>(() => md.value?.rows || [])
  34. const groups = computed(() => LAYERS
  35. .map(([lvs, kn, kd, col]) => ({ kn, kd, col, rs: rows.value.filter((r) => lvs.includes(String(r.层))) }))
  36. .filter((g) => g.rs.length))
  37. </script>
  38. <template>
  39. <section>
  40. <div class="eyebrow">{{ t('mt.eyebrow') }}</div>
  41. <div v-if="!md" class="panel"><p class="small muted">{{ t('mt.surveying') }}</p></div>
  42. <div v-else-if="md.error" class="panel"><p class="small" style="color:var(--critical)">{{ dv(md.error) }}</p></div>
  43. <template v-else>
  44. <div class="panel mb">
  45. <h3>{{ t('mt.head', { n: rows.length }) }}</h3>
  46. <p class="small muted">{{ t('mt.note') }}</p>
  47. </div>
  48. <div v-for="g in groups" :key="g.kn" class="panel mb" :style="`border-left:3px solid ${g.col}`">
  49. <h3>{{ t(g.kn) }} ({{ g.rs.length }})</h3>
  50. <p class="small muted">{{ t(g.kd) }}</p>
  51. <div class="tblwrap">
  52. <table style="margin-top:10px">
  53. <tr>
  54. <th style="width:18%">{{ t('mt.src') }}</th><th style="width:20%">{{ t('mt.cov') }}</th>
  55. <th style="width:13%">{{ t('mt.last') }}</th><th style="width:10%">{{ t('mt.freq') }}</th>
  56. <th style="width:11%">{{ t('mt.resp') }}</th><th>{{ t('mt.how') }}</th>
  57. </tr>
  58. <tr v-for="(r, i) in g.rs" :key="i">
  59. <td class="small"><b>{{ r.存在 ? '' : '✗ ' }}{{ publicMeta(r.名) }}</b></td>
  60. <td class="small num">{{ publicMeta(r.覆盖) }}<span v-if="r.条数" class="muted">{{ t('mt.rows_n', { n: fmt(r.条数) }) }}</span></td>
  61. <td class="small muted">{{ esc(r.末次更新 || '—') }}</td>
  62. <td class="small">{{ publicMeta(r.频率) }}</td>
  63. <td class="small">{{ publicMeta(r.责任) }}</td>
  64. <td class="small">
  65. <div class="muted">{{ t('mt.put') }} <code>{{ publicMeta(r.位置) }}</code></div>
  66. <div style="margin-top:3px">{{ t('mt.run') }} <code>{{ publicMeta(r.摄入) }}</code></div>
  67. <div class="muted" style="margin-top:3px">{{ publicMeta(r.说明) }}</div>
  68. </td>
  69. </tr>
  70. </table>
  71. </div>
  72. </div>
  73. </template>
  74. <div class="eyebrow" style="margin-top:18px">{{ t('tab.recalc') }}</div>
  75. <Recalc />
  76. <iframe data-added="recalc" class="portal-recalc-frame" title="数据重算与产物"
  77. :src="recalcBlobUrl" style="width:100%;height:1420px;border:0;display:block;margin-top:18px"></iframe>
  78. </section>
  79. </template>