Workbench.vue 4.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. <script setup lang="ts">
  2. /**
  3. * 工作台(P11):页签容器。页签清单与重构前 `app.js` 的 `TABS` **完全一致**(9 个)。
  4. *
  5. * 已迁移:overview(总览)。其余页签先如实标"迁移中",不做假界面 ——
  6. * 用户令要求"逐页签迁移、对拍通过再删 Python 源码",所以未迁移的页签必须一眼可见。
  7. */
  8. import { computed, ref, watch } from 'vue'
  9. import { t } from '../i18n/zh'
  10. import { DONE, TABS } from '../lib/tabs'
  11. import Documents from './Documents.vue'
  12. import PortalView from './PortalView.vue'
  13. import { winName } from '../lib/render'
  14. import Component from './Component.vue'
  15. import Energy from './Energy.vue'
  16. import Fault from './Fault.vue'
  17. import Vibration from './Vibration.vue'
  18. import Generation from './Generation.vue'
  19. import Decision from './Decision.vue'
  20. import Assistant from './Assistant.vue'
  21. import System from './System.vue'
  22. import Report from './Report.vue'
  23. import Overview from './Overview.vue'
  24. const props = defineProps<{ d: any; tab: string; sys?: string | null; sub?: string | null; cms?: any; curves?: any; apis?: any; win?: string }>()
  25. const emit = defineEmits<{ (e: 'tab', id: string): void; (e: 'sub', id: string): void; (e: 'win', w: string): void; (e: 'reselect'): void }>()
  26. /** 旧 app.js:60-70 —— 预设与 slug 映射(照抄) */
  27. const WIN_PRESETS: [string, string][] = [['近30日', 'win.30d'], ['近90日', 'win.90d'],
  28. ['2026年', 'win.2026'], ['2026H1', 'win.2026H1'], ['2025H2', 'win.2025H2'], ['全程', 'win.all']]
  29. const WIN_SLUG: Record<string, string> = {'近30日': '30d', '近90日': '90d', '2026年': '2026', '2026H1': '2026H1', '2025H2': '2025H2', '全程': 'all'}
  30. const winSlug = (v: string): string => WIN_SLUG[v] || v
  31. const sel = ref<string>('')
  32. const wa = ref<string>('')
  33. const wb2 = ref<string>('')
  34. const months = computed<string[]>(() => ((props.d && props.d.all_months) || []).slice().reverse())
  35. const isCustom = computed(() => /^\d{4}-\d{2}-\d{2}~\d{4}-\d{2}-\d{2}$/.test(sel.value))
  36. watch(() => props.win, (w) => { sel.value = w || ''; const p = String(w || '').split('~'); if (p.length === 2) { wa.value = p[0]; wb2.value = p[1] } }, { immediate: true })
  37. function pickWin(ev: Event) { const v = (ev.target as HTMLSelectElement).value; sel.value = v; emit('win', winSlug(v)) }
  38. function applyRange() { if (wa.value && wb2.value) emit('win', wa.value + '~' + wb2.value) }
  39. const pending = computed(() => !DONE.includes(props.tab))
  40. </script>
  41. <template>
  42. <div class="page-head">
  43. <h1>{{ t('tab.' + tab) }}</h1>
  44. <!-- 与旧 app.js:1066 逐字一致:`${t('win.label')} · ${winName(WIN)}`(对拍实逮:写成 win.l 第 1 项就错位) -->
  45. </div>
  46. <Overview v-if="tab === 'overview'" :d="d" />
  47. <Energy v-else-if="tab === 'energy'" :d="d" />
  48. <Component v-else-if="tab === 'component'" :d="d" :sys="sys" />
  49. <Fault v-else-if="tab === 'fault'" :d="d" />
  50. <Vibration v-else-if="tab === 'vibration'" :d="d" :sub="sub" :cms="cms" @sub="emit('sub', $event)" />
  51. <Generation v-else-if="tab === 'generation'" :d="d" :curves="curves" :pending="!!d.m9_pending" />
  52. <Decision v-else-if="tab === 'decision'" :d="d" :apis="apis" />
  53. <Assistant v-else-if="tab === 'assistant'" :d="d" />
  54. <System v-else-if="tab === 'system'" :apis="apis" />
  55. <Report v-else-if="tab === 'report'" :d="d" :apis="apis" />
  56. <Documents v-else-if="tab === 'documents'" />
  57. <PortalView v-else-if="tab === 'portal_src'" />
  58. <section v-else-if="pending" class="card">
  59. <h2>{{ t('tab.' + tab) }} <small>(本页签尚未迁移到 Vue)</small></h2>
  60. <p class="hint">
  61. 迁移纪律:页签逐个搬,每搬一个就用 <code>tools/render_parity.mjs</code> 与重构前页面
  62. (现网 <code>/detail/v2</code>)对拍文案/数值/类名序列,**通过后**才删对应的 Python 前端源码。
  63. 当前已迁移:{{ DONE.join('、') }}。
  64. </p>
  65. </section>
  66. </template>
  67. <style scoped>
  68. .wb-tabs { display: flex; flex-wrap: wrap; gap: 6px; padding: 10px 18px 0; }
  69. .wb-tabs button {
  70. padding: 6px 12px; border: 1px solid var(--line); background: #fff; border-radius: 4px 4px 0 0;
  71. cursor: pointer; color: var(--ink);
  72. }
  73. .wb-tabs button.on { background: var(--brand); border-color: var(--brand); color: #fff; }
  74. .todo { font-size: 10px; color: var(--muted); margin-left: 2px; }
  75. .wb-tabs button.on .todo { color: #ffe9a8; }
  76. .page-head { display: flex; justify-content: space-between; align-items: center; gap: 16px; padding: 0; }
  77. .page-head h1 { font-size: 18px; margin: 0; }
  78. .window-badge { color: var(--muted); font-size: 12.5px; }
  79. .win-bar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
  80. .win-select { max-width: 220px; }
  81. .winrng { display: inline-flex; align-items: center; gap: 4px; }
  82. .winrng input[type=date] { width: 132px; }
  83. </style>