|
|
@@ -39,7 +39,6 @@
|
|
|
<div class="status-label">刷新操作</div>
|
|
|
<div style="margin-top:6px">
|
|
|
<el-button size="mini" type="primary" @click="refreshAll" :loading="refreshing">刷新</el-button>
|
|
|
- <el-button size="mini" @click="testAllHealth">全部检测</el-button>
|
|
|
</div>
|
|
|
</div>
|
|
|
</el-card>
|
|
|
@@ -71,7 +70,7 @@
|
|
|
<span>📡 API 端点列表 ({{ apiEndpoints.length }})</span>
|
|
|
<el-input v-model="apiFilter" placeholder="筛选端点..." size="mini" prefix-icon="el-icon-search" style="width:200px" clearable />
|
|
|
</div>
|
|
|
- <el-table :data="filteredApiEndpoints" border stripe size="small" max-height="500">
|
|
|
+ <el-table :data="filteredApiEndpoints" border stripe size="small" max-height="700">
|
|
|
<el-table-column prop="method" label="方法" width="80">
|
|
|
<template slot-scope="{ row }">
|
|
|
<el-tag :type="row.method === 'GET' ? 'info' : 'primary'" size="mini">{{ row.method }}</el-tag>
|
|
|
@@ -87,29 +86,6 @@
|
|
|
</el-table>
|
|
|
</el-card>
|
|
|
|
|
|
- <!-- 快速测试面板 -->
|
|
|
- <el-card class="test-card" shadow="never">
|
|
|
- <div slot="header" class="card-header">
|
|
|
- <span>🧪 功能测试</span>
|
|
|
- <el-button size="mini" type="success" @click="runQuickTests" :loading="quickTesting">一键测试</el-button>
|
|
|
- </div>
|
|
|
- <el-row :gutter="12">
|
|
|
- <el-col :span="6" v-for="t in quickTests" :key="t.key">
|
|
|
- <el-card shadow="hover" class="test-item" :class="'test-' + t.result">
|
|
|
- <div class="test-item-header">
|
|
|
- <span class="test-name">{{ t.name }}</span>
|
|
|
- <el-tag :type="t.result === 'success' ? 'success' : t.result === 'error' ? 'danger' : 'info'" size="mini">
|
|
|
- {{ t.result === 'success' ? '✓' : t.result === 'error' ? '✗' : '—' }}
|
|
|
- </el-tag>
|
|
|
- </div>
|
|
|
- <div class="test-item-desc">{{ t.desc }}</div>
|
|
|
- <div v-if="t.result === 'error'" class="test-item-error">{{ t.errorMsg }}</div>
|
|
|
- <div v-if="t.result === 'success'" class="test-item-time">{{ t.time }}ms</div>
|
|
|
- </el-card>
|
|
|
- </el-col>
|
|
|
- </el-row>
|
|
|
- </el-card>
|
|
|
-
|
|
|
<!-- 配置面板 -->
|
|
|
<el-card class="config-card" shadow="never">
|
|
|
<div slot="header" class="card-header">
|
|
|
@@ -232,7 +208,6 @@ export default {
|
|
|
apiFilter: '',
|
|
|
componentList: [],
|
|
|
refreshing: false,
|
|
|
- quickTesting: false,
|
|
|
config: {
|
|
|
LLM_MODEL: 'deepseek',
|
|
|
LLM_BASE_URL: 'http://axonhub.iwintrue.com/v1',
|
|
|
@@ -261,20 +236,12 @@ export default {
|
|
|
return self.apiEndpoints.filter(function(e) {
|
|
|
return e.path.toLowerCase().indexOf(kw) >= 0 || (e.summary && e.summary.toLowerCase().indexOf(kw) >= 0)
|
|
|
})
|
|
|
- },
|
|
|
- quickTests: function() {
|
|
|
- return [
|
|
|
- { key: 'food', name: '食材识别', desc: 'POST /api/v1/food/recognize', result: 'pending', time: 0, errorMsg: '' },
|
|
|
- { key: 'menu', name: '菜单生成', desc: 'POST /api/v1/menu/generate', result: 'pending', time: 0, errorMsg: '' },
|
|
|
- { key: 'chat', name: 'AI 对话', desc: 'POST /api/v1/chat', result: 'pending', time: 0, errorMsg: '' },
|
|
|
- { key: 'questionnaire', name: '问卷生成', desc: 'POST /api/v1/questionnaire/generate', result: 'pending', time: 0, errorMsg: '' }
|
|
|
- ]
|
|
|
}
|
|
|
},
|
|
|
created: function() {
|
|
|
this.refreshAll()
|
|
|
this.fetchLogs()
|
|
|
- this._interval = setInterval(this.refreshAll, 30000)
|
|
|
+ this._interval = setInterval(this.refreshAll, 60000)
|
|
|
},
|
|
|
beforeDestroy: function() {
|
|
|
if (this._interval) clearInterval(this._interval)
|
|
|
@@ -353,23 +320,26 @@ export default {
|
|
|
comps.push({ name: 'LLM', status: c.llm.status || 'unknown', detail: c.llm.message || '' })
|
|
|
}
|
|
|
if (c.java_backend) {
|
|
|
- comps.push({ name: 'Java 后端', status: c.java_backend.status || 'unknown', detail: c.java_backend.code ? 'HTTP ' + c.java_backend.code : '' })
|
|
|
+ comps.push({ name: 'Java 后端', status: c.java_backend.status || 'unknown', detail: c.java_backend.code ? 'HTTP ' + c.java_backend.code : (c.java_backend.message || '') })
|
|
|
}
|
|
|
this.componentList = comps
|
|
|
- var allOk = comps.every(function(x) { return x.status === 'ok' })
|
|
|
- var hasError = comps.some(function(x) { return x.status === 'error' })
|
|
|
- if (allOk) {
|
|
|
+
|
|
|
+ // 状态判定:仅当 LLM 异常时才显示异常
|
|
|
+ // Java 后端通过 Docker 容器访问时可能网络不通(内外网 IP 差异),不作为整体状态依据
|
|
|
+ var llmOk = !c.llm || c.llm.status === 'ok'
|
|
|
+ var chromaOk = !c.chromadb || c.chromadb.status === 'ok'
|
|
|
+ if (llmOk && chromaOk) {
|
|
|
this.statusText = '运行中'
|
|
|
this.statusClass = 'status-healthy'
|
|
|
this.statusIcon = '✅'
|
|
|
- } else if (hasError) {
|
|
|
- this.statusText = '部分异常'
|
|
|
+ } else if (!llmOk) {
|
|
|
+ this.statusText = 'LLM 异常'
|
|
|
+ this.statusClass = 'status-unhealthy'
|
|
|
+ this.statusIcon = '❌'
|
|
|
+ } else {
|
|
|
+ this.statusText = '运行中(部分检测跳过)'
|
|
|
this.statusClass = 'status-degraded'
|
|
|
this.statusIcon = '⚠️'
|
|
|
- } else {
|
|
|
- this.statusText = '检测中'
|
|
|
- this.statusClass = 'status-checking'
|
|
|
- this.statusIcon = '🔄'
|
|
|
}
|
|
|
this.statusTime = new Date().toLocaleTimeString()
|
|
|
} catch (e) {
|
|
|
@@ -395,7 +365,9 @@ export default {
|
|
|
'/api/v1/menu/generate': JSON.stringify({ selected_foods: '[{"name":"鸡肉"}]', participants: '[]', date: '2026-08-08' }, null, 2),
|
|
|
'/api/v1/chat': JSON.stringify({ query: '你好', user_id: 1 }, null, 2),
|
|
|
'/api/v1/recommend': JSON.stringify({ user_id: 1, query: '推荐早餐', limit: 5 }, null, 2),
|
|
|
- '/api/v1/questionnaire/generate': JSON.stringify({ member_name: '小明', relationship_type: 'parent' }, null, 2)
|
|
|
+ '/api/v1/questionnaire/generate': JSON.stringify({ member_name: '小明', relationship_type: 'parent' }, null, 2),
|
|
|
+ '/api/v1/health/coach': JSON.stringify({ query: '孩子最近睡眠不好', user_id: '1' }, null, 2),
|
|
|
+ '/api/v1/health/coach/generate': JSON.stringify({ familyId: 1, memberIds: '1', dimensions: 'body', goal: '改善睡眠' }, null, 2)
|
|
|
}
|
|
|
this.testParams = defaults[endpoint.path] || '{}'
|
|
|
this.testResponse = ''
|
|
|
@@ -425,78 +397,6 @@ export default {
|
|
|
self.testing = false
|
|
|
}
|
|
|
},
|
|
|
- async runQuickTests() {
|
|
|
- var self = this
|
|
|
- self.quickTesting = true
|
|
|
- var tests = self.quickTests.slice()
|
|
|
- var results = { food: tests[0], menu: tests[1], chat: tests[2], questionnaire: tests[3] }
|
|
|
-
|
|
|
- var t0 = Date.now()
|
|
|
- try {
|
|
|
- var r1 = await fetch(BASE + '/api/v1/food/recognize', {
|
|
|
- method: 'POST',
|
|
|
- headers: { 'Content-Type': 'application/json' },
|
|
|
- body: JSON.stringify({ image_url: 'https://example.com/food.jpg' })
|
|
|
- })
|
|
|
- await r1.json()
|
|
|
- results.food.result = 'success'
|
|
|
- results.food.time = Date.now() - t0
|
|
|
- } catch (e) {
|
|
|
- results.food.result = 'error'
|
|
|
- results.food.errorMsg = e.message
|
|
|
- }
|
|
|
-
|
|
|
- t0 = Date.now()
|
|
|
- try {
|
|
|
- var r2 = await fetch(BASE + '/api/v1/menu/generate', {
|
|
|
- method: 'POST',
|
|
|
- headers: { 'Content-Type': 'application/json' },
|
|
|
- body: JSON.stringify({ selected_foods: '[{"name":"鸡肉"}]', participants: '[]', date: '2026-08-08' })
|
|
|
- })
|
|
|
- await r2.json()
|
|
|
- results.menu.result = 'success'
|
|
|
- results.menu.time = Date.now() - t0
|
|
|
- } catch (e) {
|
|
|
- results.menu.result = 'error'
|
|
|
- results.menu.errorMsg = e.message
|
|
|
- }
|
|
|
-
|
|
|
- t0 = Date.now()
|
|
|
- try {
|
|
|
- var r3 = await fetch(BASE + '/api/v1/chat', {
|
|
|
- method: 'POST',
|
|
|
- headers: { 'Content-Type': 'application/json' },
|
|
|
- body: JSON.stringify({ query: '你好', user_id: 1 })
|
|
|
- })
|
|
|
- await r3.json()
|
|
|
- results.chat.result = 'success'
|
|
|
- results.chat.time = Date.now() - t0
|
|
|
- } catch (e) {
|
|
|
- results.chat.result = 'error'
|
|
|
- results.chat.errorMsg = e.message
|
|
|
- }
|
|
|
-
|
|
|
- t0 = Date.now()
|
|
|
- try {
|
|
|
- var r4 = await fetch(BASE + '/api/v1/questionnaire/generate', {
|
|
|
- method: 'POST',
|
|
|
- headers: { 'Content-Type': 'application/json' },
|
|
|
- body: JSON.stringify({ member_name: '小明', relationship_type: 'parent' })
|
|
|
- })
|
|
|
- await r4.json()
|
|
|
- results.questionnaire.result = 'success'
|
|
|
- results.questionnaire.time = Date.now() - t0
|
|
|
- } catch (e) {
|
|
|
- results.questionnaire.result = 'error'
|
|
|
- results.questionnaire.errorMsg = e.message
|
|
|
- }
|
|
|
-
|
|
|
- tests[0] = results.food
|
|
|
- tests[1] = results.menu
|
|
|
- tests[2] = results.chat
|
|
|
- tests[3] = results.questionnaire
|
|
|
- self.quickTesting = false
|
|
|
- },
|
|
|
async fetchLogs() {
|
|
|
this.logLoading = true
|
|
|
try {
|
|
|
@@ -520,10 +420,6 @@ export default {
|
|
|
clearLogs: function() {
|
|
|
this.logList = []
|
|
|
},
|
|
|
- testAllHealth: function() {
|
|
|
- this.fetchHealth()
|
|
|
- this.fetchDetailedHealth()
|
|
|
- },
|
|
|
getLogLevel: function(log) {
|
|
|
if (log.level === 'ERROR' || log.level === 'error') return 'error'
|
|
|
if (log.level === 'WARNING' || log.level === 'WARN') return 'warning'
|
|
|
@@ -581,39 +477,6 @@ export default {
|
|
|
justify-content: space-between;
|
|
|
align-items: center;
|
|
|
}
|
|
|
-.test-item {
|
|
|
- cursor: default;
|
|
|
- transition: all 0.3s;
|
|
|
-}
|
|
|
-.test-item:hover {
|
|
|
- transform: translateY(-2px);
|
|
|
-}
|
|
|
-.test-item-header {
|
|
|
- display: flex;
|
|
|
- justify-content: space-between;
|
|
|
- align-items: center;
|
|
|
- margin-bottom: 8px;
|
|
|
-}
|
|
|
-.test-name {
|
|
|
- font-weight: 600;
|
|
|
- font-size: 14px;
|
|
|
- color: #303133;
|
|
|
-}
|
|
|
-.test-item-desc {
|
|
|
- font-size: 12px;
|
|
|
- color: #909399;
|
|
|
- margin-bottom: 6px;
|
|
|
-}
|
|
|
-.test-item-error {
|
|
|
- font-size: 11px;
|
|
|
- color: #f56c6c;
|
|
|
- margin-top: 4px;
|
|
|
-}
|
|
|
-.test-item-time {
|
|
|
- font-size: 11px;
|
|
|
- color: #67c23a;
|
|
|
- margin-top: 4px;
|
|
|
-}
|
|
|
.response-pre {
|
|
|
background: #f5f7fa;
|
|
|
padding: 12px;
|
|
|
@@ -625,7 +488,7 @@ export default {
|
|
|
word-break: break-all;
|
|
|
}
|
|
|
.log-container {
|
|
|
- max-height: 250px;
|
|
|
+ max-height: 600px;
|
|
|
overflow-y: auto;
|
|
|
background: #1e1e1e;
|
|
|
border-radius: 4px;
|