AgentView.vue 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. <!-- 组件:AgentView;功能:五节点统计、配置版本与真实连接测试;props:无;emit:无。管理员改配置,审计员可查看与测试。 -->
  2. <script setup lang="ts">
  3. import { onMounted, ref } from "vue";
  4. import { ElMessage } from "element-plus";
  5. import { Cpu } from "@element-plus/icons-vue";
  6. import { api, jsonObject, pretty, type Row } from "../api";
  7. import { useSession } from "../store";
  8. import PageHeading from "../components/PageHeading.vue";
  9. import JsonEvidence from "../components/JsonEvidence.vue";
  10. const session = useSession();
  11. const rows = ref<Row[]>([]),
  12. busy = ref(false),
  13. dialog = ref(false),
  14. saving = ref(false),
  15. current = ref<Row>({}),
  16. config = ref("{}"),
  17. enabled = ref(true),
  18. history = ref<Row[]>([]),
  19. testing = ref("");
  20. async function load() {
  21. busy.value = true;
  22. try {
  23. rows.value = await api<Row[]>("/agents");
  24. } catch (error) {
  25. ElMessage.error((error as Error).message);
  26. } finally {
  27. busy.value = false;
  28. }
  29. }
  30. async function edit(row: Row) {
  31. try {
  32. const data = await api(`/agents/${row.node_name}`);
  33. current.value = data.current;
  34. history.value = data.history;
  35. config.value = pretty(data.current.config);
  36. enabled.value = data.current.enabled;
  37. dialog.value = true;
  38. } catch (error) {
  39. ElMessage.error((error as Error).message);
  40. }
  41. }
  42. async function save() {
  43. saving.value = true;
  44. try {
  45. await api(`/agents/${current.value.node_name}`, "PATCH", {
  46. expected_version: current.value.version,
  47. enabled: enabled.value,
  48. config: jsonObject(config.value),
  49. });
  50. dialog.value = false;
  51. await load();
  52. ElMessage.success("已生成新配置版本,历史审核继续使用原版本");
  53. } catch (error) {
  54. ElMessage.error((error as Error).message);
  55. } finally {
  56. saving.value = false;
  57. }
  58. }
  59. async function test(row: Row) {
  60. testing.value = row.node_name;
  61. try {
  62. const data = await api(`/agents/${row.node_name}/test`, "POST");
  63. data.status === "success"
  64. ? ElMessage.success(data.message)
  65. : ElMessage.warning(data.message);
  66. } catch (error) {
  67. ElMessage.error((error as Error).message);
  68. } finally {
  69. testing.value = "";
  70. }
  71. }
  72. onMounted(load);
  73. </script>
  74. <template>
  75. <PageHeading
  76. title="Agent 管理"
  77. description="四个自动审核节点与一个人工反馈节点,执行数据来自真实审核记录。"
  78. ><el-button @click="load">刷新</el-button></PageHeading
  79. ><el-alert
  80. type="info"
  81. :closable="false"
  82. title="节点超时、并发、重试和阈值在此配置;模型地址与密钥集中放在后端 .env,不能由浏览器任意指定。"
  83. />
  84. <div class="agent-grid" v-loading="busy">
  85. <section v-for="row in rows" :key="row.node_name" class="card agent-card">
  86. <div class="agent-top">
  87. <span class="agent-icon"
  88. ><el-icon><Cpu /></el-icon
  89. ></span>
  90. <div>
  91. <h2>{{ row.name }}</h2>
  92. <span class="muted mono"
  93. >{{ row.node_name }} · v{{ row.version }}</span
  94. >
  95. </div>
  96. </div>
  97. <el-tag :type="row.enabled ? 'success' : 'info'" round>{{
  98. row.enabled ? "已启用" : "已停用可选能力"
  99. }}</el-tag>
  100. <div class="agent-stats">
  101. <span class="muted"
  102. >执行次数<strong>{{ row.executions }}</strong></span
  103. ><span class="muted"
  104. >降级次数<strong>{{ row.degraded_count }}</strong></span
  105. ><span class="muted"
  106. >平均耗时<strong style="font-size: 18px">{{
  107. row.average_ms == null ? "—" : `${row.average_ms} ms`
  108. }}</strong></span
  109. >
  110. </div>
  111. <JsonEvidence :value="row.config" title="运行参数" />
  112. <div class="agent-actions">
  113. <el-button @click="edit(row)">{{
  114. session.level >= 4 ? "编辑配置" : "查看版本"
  115. }}</el-button
  116. ><el-button
  117. plain
  118. type="primary"
  119. :loading="testing === row.node_name"
  120. @click="test(row)"
  121. >测试节点</el-button
  122. >
  123. </div>
  124. </section>
  125. </div>
  126. <el-dialog
  127. v-model="dialog"
  128. :title="`${current.name || ''} · 配置 v${current.version || ''}`"
  129. width="min(670px,94vw)"
  130. ><el-form label-position="top"
  131. ><el-form-item label="启用可选能力"
  132. ><el-switch
  133. v-model="enabled"
  134. :disabled="session.level < 4" /></el-form-item
  135. ><el-form-item label="节点参数(JSON 白名单)"
  136. ><el-input
  137. v-model="config"
  138. class="json-editor"
  139. type="textarea"
  140. :rows="12"
  141. :readonly="session.level < 4" /></el-form-item></el-form
  142. ><JsonEvidence :value="history" title="历史不可变配置版本" /><template
  143. #footer
  144. ><el-button @click="dialog = false">关闭</el-button
  145. ><el-button
  146. v-if="session.level >= 4"
  147. type="primary"
  148. :loading="saving"
  149. @click="save"
  150. >发布新版本</el-button
  151. ></template
  152. ></el-dialog
  153. >
  154. </template>