| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677 |
- <script setup lang="ts">
- /**
- * 观澜前端外壳(P11)—— Vue 3 + Vite + TypeScript。
- *
- * 两种模式:
- * · 工作台:与重构前 `/detail/v2` 同构(页签清单一致),数据取 `/api/fleet?win=`(与 app.js 的 load() 同一个接口);
- * · 契约自检:把冻契约里的关键接口各取一次,显示 状态/耗时/顶层键。
- *
- * 迁移纪律(用户令 2026-09-29):页签逐个搬,**与重构前页面对拍通过后**才删对应 Python 前端源码。
- */
- import { onMounted, ref, watch } from 'vue'
- import ContractSmoke from './views/ContractSmoke.vue'
- import Workbench from './views/Workbench.vue'
- import { callApi } from './api/contract'
- type Mode = 'workbench' | 'smoke' | 'about'
- const mode = ref<Mode>('workbench')
- const tab = ref<string>('overview')
- const sub = ref<string | null>(null)
- const d = ref<any>(null)
- const win = ref<string>('')
- const loading = ref(false)
- const err = ref('')
- async function load() {
- loading.value = true
- err.value = ''
- const q = win.value ? `?win=${encodeURIComponent(win.value)}` : ''
- const r = await callApi<any>('fleet', { method: 'GET' })
- // callApi 走的是契约里的固定路径;窗参数在这里补上(契约只约定路径与方法)
- if (q) {
- try {
- const resp = await fetch('/api/fleet' + q)
- d.value = await resp.json()
- } catch (e) {
- err.value = e instanceof Error ? e.message : String(e)
- }
- } else if (r.ok) {
- d.value = r.data
- if (d.value?.win) win.value = d.value.win
- } else {
- err.value = r.error || `HTTP ${r.status}`
- }
- loading.value = false
- }
- watch(win, (v) => { if (v && v !== d.value?.win) void load() })
- onMounted(load)
- </script>
- <template>
- <header class="top">
- <div class="brand">观澜 · 风电数据分析与决策支持</div>
- <nav>
- <button :class="{ on: mode === 'workbench' }" @click="mode = 'workbench'">工作台</button>
- <button :class="{ on: mode === 'smoke' }" @click="mode = 'smoke'">契约自检</button>
- <button :class="{ on: mode === 'about' }" @click="mode = 'about'">关于本前端</button>
- </nav>
- </header>
- <main>
- <template v-if="mode === 'workbench'">
- <p v-if="loading" class="hint" style="padding:0 18px">取数中(/api/fleet)…</p>
- <p v-else-if="err" class="hint" style="padding:0 18px;color:var(--bad)">取数失败:{{ err }}</p>
- <Workbench v-else-if="d" :d="d" :tab="tab" :sub="sub" @tab="tab = $event" @sub="sub = $event" />
- </template>
- <ContractSmoke v-else-if="mode === 'smoke'" />
- <section v-else class="card">
- <h2>关于本前端</h2>
- <ul>
- <li>技术栈:<b>Vue 3 + Vite + TypeScript + Node.js</b>(用户令 2026-09-29)</li>
- <li>与后端交互:只走网关一个源站,路径与形状以 <code>app_backEnd/app_backEnd_guanlan/contract/http_api_v1.json</code> 冻契约为准</li>
- <li>数据来源:与重构前 <code>/detail/v2</code> 同一个接口 <code>/api/fleet?win=</code></li>
- <li>迁移纪律:页签逐个搬,与重构前页面对拍通过后才删对应 Python 源码</li>
- </ul>
- </section>
- </main>
- </template>
|