smart-admin-web 10.10.0 → 10.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +1 -0
  2. package/cli/icon-subset.mjs +132 -0
  3. package/cli/smart-admin-icons.mjs +57 -0
  4. package/dist/api/index.d.ts +52 -1
  5. package/dist/api/index.js +151 -84
  6. package/dist/api/index.js.map +1 -1
  7. package/dist/api/schema.d.ts +936 -15
  8. package/dist/assets/icons/ph-subset.json.js +7 -7
  9. package/dist/createSmartAdmin.d.ts +3 -0
  10. package/dist/createSmartAdmin.js +36 -36
  11. package/dist/createSmartAdmin.js.map +1 -1
  12. package/dist/index.d.ts +1 -1
  13. package/dist/index.js +268 -252
  14. package/dist/index.js.map +1 -1
  15. package/dist/lib/icons.d.ts +5 -2
  16. package/dist/lib/icons.js +15 -13
  17. package/dist/lib/icons.js.map +1 -1
  18. package/dist/lib/runtime.js +1 -1
  19. package/dist/locales/en-US.d.ts +143 -0
  20. package/dist/locales/en-US.js +145 -0
  21. package/dist/locales/en-US.js.map +1 -1
  22. package/dist/locales/zh-CN.d.ts +143 -0
  23. package/dist/locales/zh-CN.js +145 -0
  24. package/dist/locales/zh-CN.js.map +1 -1
  25. package/dist/router/viewRegistry.js +1 -1
  26. package/dist/router/viewRegistry.js.map +1 -1
  27. package/dist/style.css +1 -1
  28. package/dist/types/api.d.ts +166 -0
  29. package/dist/types/api.js +12 -9
  30. package/dist/types/api.js.map +1 -1
  31. package/dist/views/system/ai-model/components/ProviderCard.vue.d.ts +13 -0
  32. package/dist/views/system/ai-model/components/ProviderCard.vue.js +8 -0
  33. package/dist/views/system/ai-model/components/ProviderCard.vue.js.map +1 -0
  34. package/dist/views/system/ai-model/components/ProviderCard2.vue.js +230 -0
  35. package/dist/views/system/ai-model/components/ProviderCard2.vue.js.map +1 -0
  36. package/dist/views/system/ai-model/components/ProviderForm.vue.d.ts +18 -0
  37. package/dist/views/system/ai-model/components/ProviderForm.vue.js +8 -0
  38. package/dist/views/system/ai-model/components/ProviderForm.vue.js.map +1 -0
  39. package/dist/views/system/ai-model/components/ProviderForm2.vue.js +507 -0
  40. package/dist/views/system/ai-model/components/ProviderForm2.vue.js.map +1 -0
  41. package/dist/views/system/ai-model/index.vue.d.ts +3 -0
  42. package/dist/views/system/ai-model/index.vue.js +8 -0
  43. package/dist/views/system/ai-model/index.vue.js.map +1 -0
  44. package/dist/views/system/ai-model/index2.vue.js +134 -0
  45. package/dist/views/system/ai-model/index2.vue.js.map +1 -0
  46. package/dist/views/system/ai-model/presets.d.ts +14 -0
  47. package/dist/views/system/ai-model/presets.js +22 -0
  48. package/dist/views/system/ai-model/presets.js.map +1 -0
  49. package/dist/views/system/ai-usage/index.vue.d.ts +3 -0
  50. package/dist/views/system/ai-usage/index.vue.js +8 -0
  51. package/dist/views/system/ai-usage/index.vue.js.map +1 -0
  52. package/dist/views/system/ai-usage/index2.vue.js +423 -0
  53. package/dist/views/system/ai-usage/index2.vue.js.map +1 -0
  54. package/package.json +6 -2
@@ -0,0 +1,134 @@
1
+ import { defineComponent as E, ref as d, onMounted as D, resolveDirective as P, openBlock as l, createElementBlock as g, createElementVNode as h, createVNode as o, unref as e, withCtx as r, withKeys as S, withDirectives as b, createBlock as p, createTextVNode as C, toDisplayString as z, Fragment as B, renderList as M } from "vue";
2
+ import { useMessage as V, NButton as w, NInput as A, NSpin as F, NEmpty as G, NGrid as I, NGi as K } from "naive-ui";
3
+ import { watchDebounced as T } from "@vueuse/core";
4
+ import { useI18n as U } from "vue-i18n";
5
+ import y from "../../../components/AppIcon.vue.js";
6
+ import { aiProviderApi as $ } from "../../../api/index.js";
7
+ import { translateError as q } from "../../../utils/error.js";
8
+ import L from "./components/ProviderCard.vue.js";
9
+ import O from "./components/ProviderForm.vue.js";
10
+ const j = { class: "view" }, H = { class: "toolbar" }, ie = /* @__PURE__ */ E({
11
+ __name: "index",
12
+ setup(J) {
13
+ const { t: v } = U(), _ = V(), s = d(!1), u = d(""), m = d([]);
14
+ async function i() {
15
+ s.value = !0;
16
+ try {
17
+ const { items: a } = await $.page({
18
+ page: 1,
19
+ pageSize: 100,
20
+ keyword: u.value.trim() || void 0
21
+ });
22
+ m.value = a;
23
+ } catch (a) {
24
+ _.error(q(a));
25
+ } finally {
26
+ s.value = !1;
27
+ }
28
+ }
29
+ D(i), T(u, i, { debounce: 400 });
30
+ const c = d(!1), f = d(null);
31
+ function k() {
32
+ f.value = null, c.value = !0;
33
+ }
34
+ function N(a) {
35
+ f.value = a, c.value = !0;
36
+ }
37
+ return (a, n) => {
38
+ const x = P("auth");
39
+ return l(), g("div", j, [
40
+ h("div", H, [
41
+ o(e(w), {
42
+ quaternary: "",
43
+ loading: s.value,
44
+ onClick: i
45
+ }, {
46
+ icon: r(() => [
47
+ o(y, {
48
+ icon: "ph:arrow-clockwise",
49
+ size: 16
50
+ })
51
+ ]),
52
+ _: 1
53
+ }, 8, ["loading"]),
54
+ o(e(A), {
55
+ value: u.value,
56
+ "onUpdate:value": n[0] || (n[0] = (t) => u.value = t),
57
+ clearable: "",
58
+ style: { width: "240px" },
59
+ placeholder: e(v)("aiModel.searchPlaceholder"),
60
+ onKeyup: S(i, ["enter"])
61
+ }, {
62
+ prefix: r(() => [
63
+ o(y, {
64
+ icon: "ph:magnifying-glass",
65
+ size: 14
66
+ })
67
+ ]),
68
+ _: 1
69
+ }, 8, ["value", "placeholder"]),
70
+ n[2] || (n[2] = h("div", { class: "toolbar-spacer" }, null, -1)),
71
+ b((l(), p(e(w), {
72
+ type: "primary",
73
+ onClick: k
74
+ }, {
75
+ icon: r(() => [
76
+ o(y, {
77
+ icon: "ph:plus",
78
+ size: 16
79
+ })
80
+ ]),
81
+ default: r(() => [
82
+ C(" " + z(e(v)("aiModel.addProvider")), 1)
83
+ ]),
84
+ _: 1
85
+ })), [
86
+ [x, "POST:/api/v1/sys/ai/provider/add"]
87
+ ])
88
+ ]),
89
+ o(e(F), { show: s.value }, {
90
+ default: r(() => [
91
+ !s.value && m.value.length === 0 ? (l(), p(e(G), {
92
+ key: 0,
93
+ description: e(v)("common.noData"),
94
+ class: "empty"
95
+ }, null, 8, ["description"])) : (l(), p(e(I), {
96
+ key: 1,
97
+ cols: "1 s:2 l:3",
98
+ responsive: "screen",
99
+ "x-gap": 16,
100
+ "y-gap": 16
101
+ }, {
102
+ default: r(() => [
103
+ (l(!0), g(B, null, M(m.value, (t) => (l(), p(e(K), {
104
+ key: t.id
105
+ }, {
106
+ default: r(() => [
107
+ o(L, {
108
+ provider: t,
109
+ onEdit: (Q) => N(t),
110
+ onDeleted: i
111
+ }, null, 8, ["provider", "onEdit"])
112
+ ]),
113
+ _: 2
114
+ }, 1024))), 128))
115
+ ]),
116
+ _: 1
117
+ }))
118
+ ]),
119
+ _: 1
120
+ }, 8, ["show"]),
121
+ o(O, {
122
+ show: c.value,
123
+ "onUpdate:show": n[1] || (n[1] = (t) => c.value = t),
124
+ editing: f.value,
125
+ onSaved: i
126
+ }, null, 8, ["show", "editing"])
127
+ ]);
128
+ };
129
+ }
130
+ });
131
+ export {
132
+ ie as default
133
+ };
134
+ //# sourceMappingURL=index2.vue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index2.vue.js","sources":["../../../../src/views/system/ai-model/index.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// AI 模型管理:厂商是卡片网格,不是 SmartTable —— 厂商数量级很小(十几个封顶),卡片能把\n// 徽标/协议/Base URL/模型数/启停/测试连接一次性摆开,比表格行更直观。\n// 列表本身一次拉大页(pageSize 100)铺成网格,不做真分页;关键字过滤(厂商 code/name)交给\n// 后端 keyword 参数,输入防抖 400ms 后自动重拉,回车立即拉一次。\n// 交互都收进 ProviderCard(启停/编辑入口/删除/测试连接)与 ProviderForm(新增/编辑抽屉),\n// 本页只管拉列表、开表单、增删改后 reload。\nimport { onMounted, ref } from 'vue'\nimport { NButton, NEmpty, NGi, NGrid, NInput, NSpin, useMessage } from 'naive-ui'\nimport { watchDebounced } from '@vueuse/core'\nimport { useI18n } from 'vue-i18n'\nimport AppIcon from '#/components/AppIcon.vue'\nimport { aiProviderApi } from '#/api'\nimport { translateError } from '#/utils/error'\nimport type { AiProviderView } from '#/types/api'\nimport ProviderCard from './components/ProviderCard.vue'\nimport ProviderForm from './components/ProviderForm.vue'\n\nconst { t } = useI18n()\nconst message = useMessage()\n\nconst loading = ref(false)\nconst keyword = ref('')\nconst providers = ref<AiProviderView[]>([])\n\nasync function load() {\n loading.value = true\n try {\n const { items } = await aiProviderApi.page({\n page: 1,\n pageSize: 100,\n keyword: keyword.value.trim() || undefined,\n })\n providers.value = items\n } catch (e) {\n message.error(translateError(e))\n } finally {\n loading.value = false\n }\n}\nonMounted(load)\n// 关键字防抖重拉;回车走 @keyup.enter 立即触发,两者共用同一个 load()。\nwatchDebounced(keyword, load, { debounce: 400 })\n\n// 新增/编辑抽屉:show/editing 走 v-model + prop,与 config 页 ConfigFormModal 同一协议\n// (defineModel('show') + `editing: AiProviderView | null`,null = 新增)。\nconst formShow = ref(false)\nconst editing = ref<AiProviderView | null>(null)\nfunction openAdd() {\n editing.value = null\n formShow.value = true\n}\nfunction openEdit(p: AiProviderView) {\n editing.value = p\n formShow.value = true\n}\n</script>\n\n<template>\n <div class=\"view\">\n <div class=\"toolbar\">\n <n-button quaternary :loading=\"loading\" @click=\"load\">\n <template #icon><AppIcon icon=\"ph:arrow-clockwise\" :size=\"16\" /></template>\n </n-button>\n <n-input\n v-model:value=\"keyword\"\n clearable\n style=\"width: 240px\"\n :placeholder=\"t('aiModel.searchPlaceholder')\"\n @keyup.enter=\"load\"\n >\n <template #prefix><AppIcon icon=\"ph:magnifying-glass\" :size=\"14\" /></template>\n </n-input>\n <div class=\"toolbar-spacer\" />\n <n-button v-auth=\"'POST:/api/v1/sys/ai/provider/add'\" type=\"primary\" @click=\"openAdd\">\n <template #icon><AppIcon icon=\"ph:plus\" :size=\"16\" /></template>\n {{ t('aiModel.addProvider') }}\n </n-button>\n </div>\n\n <n-spin :show=\"loading\">\n <n-empty\n v-if=\"!loading && providers.length === 0\"\n :description=\"t('common.noData')\"\n class=\"empty\"\n />\n <n-grid v-else :cols=\"'1 s:2 l:3'\" responsive=\"screen\" :x-gap=\"16\" :y-gap=\"16\">\n <n-gi v-for=\"p in providers\" :key=\"p.id\">\n <ProviderCard :provider=\"p\" @edit=\"openEdit(p)\" @deleted=\"load\" />\n </n-gi>\n </n-grid>\n </n-spin>\n\n <ProviderForm v-model:show=\"formShow\" :editing=\"editing\" @saved=\"load\" />\n </div>\n</template>\n\n<style scoped>\n.view {\n display: flex;\n flex-direction: column;\n gap: var(--gap-card, 16px);\n}\n.toolbar {\n display: flex;\n align-items: center;\n gap: 10px;\n flex-wrap: wrap;\n}\n.toolbar-spacer {\n flex: 1;\n}\n.empty {\n padding: 64px 0;\n}\n</style>\n"],"names":["t","useI18n","message","useMessage","loading","ref","keyword","providers","load","items","aiProviderApi","e","translateError","onMounted","watchDebounced","formShow","editing","openAdd","openEdit","p","_openBlock","_createElementBlock","_hoisted_1","_createElementVNode","_hoisted_2","_createVNode","_unref","NButton","AppIcon","NInput","$event","_createBlock","_createTextVNode","NSpin","NEmpty","NGrid","_Fragment","_renderList","NGi","ProviderCard","ProviderForm"],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ export interface AiProviderPresetMeta {
2
+ /** 卡片角标用的极短缩写(拉丁字母,2 个字符左右,深色徽标上的文字) */
3
+ abbr: string;
4
+ /** Iconify 图标名(ph:*,已随内核种子图标一并登记进 ph-subset.json) */
5
+ icon: string;
6
+ /** 强调色(卡片描边 / 图标底色),16 进制 */
7
+ color: string;
8
+ }
9
+ /** 厂商预设 code → 展示元数据。key 必须与后端 AiProviderPresets.cs 的 code 一致。 */
10
+ export declare const AI_PROVIDER_PRESET_META: Readonly<Record<string, AiProviderPresetMeta>>;
11
+ /** 未登记 code 的兜底视觉(等同 custom)。 */
12
+ export declare const FALLBACK_PRESET_META: AiProviderPresetMeta;
13
+ /** 按 code 取展示元数据;查无此 code(新预设未同步维护)时兜底成 custom,不抛错。 */
14
+ export declare function resolvePresetMeta(code: string | null | undefined): AiProviderPresetMeta;
@@ -0,0 +1,22 @@
1
+ const e = {
2
+ openai: { abbr: "AI", icon: "ph:asterisk-duotone", color: "#10A37F" },
3
+ "azure-openai": { abbr: "Az", icon: "ph:cloud-duotone", color: "#0078D4" },
4
+ anthropic: { abbr: "An", icon: "ph:mountains-duotone", color: "#CC785C" },
5
+ deepseek: { abbr: "DS", icon: "ph:compass-duotone", color: "#4D6BFE" },
6
+ qwen: { abbr: "Qw", icon: "ph:wind-duotone", color: "#615CED" },
7
+ zhipu: { abbr: "Zh", icon: "ph:diamond-duotone", color: "#3A5CE6" },
8
+ moonshot: { abbr: "Ms", icon: "ph:moon-stars-duotone", color: "#16171A" },
9
+ doubao: { abbr: "Db", icon: "ph:planet-duotone", color: "#F2A93B" },
10
+ gemini: { abbr: "Ge", icon: "ph:globe-duotone", color: "#4285F4" },
11
+ ollama: { abbr: "Ol", icon: "ph:terminal-window-duotone", color: "#4B5563" },
12
+ custom: { abbr: "Cu", icon: "ph:puzzle-piece-duotone", color: "#8A8F98" }
13
+ }, n = e.custom;
14
+ function r(o) {
15
+ return o ? e[o] ?? n : n;
16
+ }
17
+ export {
18
+ e as AI_PROVIDER_PRESET_META,
19
+ n as FALLBACK_PRESET_META,
20
+ r as resolvePresetMeta
21
+ };
22
+ //# sourceMappingURL=presets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presets.js","sources":["../../../../src/views/system/ai-model/presets.ts"],"sourcesContent":["// AI 厂商预设的展示元数据:纯静态表,不发请求、不依赖后端。\n// 后端 AiProviderPresets.cs 落定了 11 个预置 code(见 GET /api/v1/sys/ai/provider/presets 的 AiProviderPreset.code);\n// 这里只补一份「按 code 取卡片缩写/图标/强调色」的视觉层,厂商预设下拉与厂商卡片(ProviderCard.vue/ProviderForm.vue)据此渲染,\n// 厂商的展示名/协议/Base URL 等业务字段仍以后端返回的 AiProviderPreset 为准,不在这里重复。\n// 新增预设 code 时后端与这里要同步维护;未登记的 code 统一兜底成 custom 的视觉,不会渲染不出来。\n\nexport interface AiProviderPresetMeta {\n /** 卡片角标用的极短缩写(拉丁字母,2 个字符左右,深色徽标上的文字) */\n abbr: string\n /** Iconify 图标名(ph:*,已随内核种子图标一并登记进 ph-subset.json) */\n icon: string\n /** 强调色(卡片描边 / 图标底色),16 进制 */\n color: string\n}\n\n/** 厂商预设 code → 展示元数据。key 必须与后端 AiProviderPresets.cs 的 code 一致。 */\nexport const AI_PROVIDER_PRESET_META: Readonly<Record<string, AiProviderPresetMeta>> = {\n openai: { abbr: 'AI', icon: 'ph:asterisk-duotone', color: '#10A37F' },\n 'azure-openai': { abbr: 'Az', icon: 'ph:cloud-duotone', color: '#0078D4' },\n anthropic: { abbr: 'An', icon: 'ph:mountains-duotone', color: '#CC785C' },\n deepseek: { abbr: 'DS', icon: 'ph:compass-duotone', color: '#4D6BFE' },\n qwen: { abbr: 'Qw', icon: 'ph:wind-duotone', color: '#615CED' },\n zhipu: { abbr: 'Zh', icon: 'ph:diamond-duotone', color: '#3A5CE6' },\n moonshot: { abbr: 'Ms', icon: 'ph:moon-stars-duotone', color: '#16171A' },\n doubao: { abbr: 'Db', icon: 'ph:planet-duotone', color: '#F2A93B' },\n gemini: { abbr: 'Ge', icon: 'ph:globe-duotone', color: '#4285F4' },\n ollama: { abbr: 'Ol', icon: 'ph:terminal-window-duotone', color: '#4B5563' },\n custom: { abbr: 'Cu', icon: 'ph:puzzle-piece-duotone', color: '#8A8F98' },\n}\n\n/** 未登记 code 的兜底视觉(等同 custom)。 */\nexport const FALLBACK_PRESET_META: AiProviderPresetMeta = AI_PROVIDER_PRESET_META.custom!\n\n/** 按 code 取展示元数据;查无此 code(新预设未同步维护)时兜底成 custom,不抛错。 */\nexport function resolvePresetMeta(code: string | null | undefined): AiProviderPresetMeta {\n if (!code) return FALLBACK_PRESET_META\n return AI_PROVIDER_PRESET_META[code] ?? FALLBACK_PRESET_META\n}\n"],"names":["AI_PROVIDER_PRESET_META","FALLBACK_PRESET_META","resolvePresetMeta","code"],"mappings":"AAgBO,MAAMA,IAA0E;AAAA,EACrF,QAAQ,EAAE,MAAM,MAAM,MAAM,uBAAuB,OAAO,UAAA;AAAA,EAC1D,gBAAgB,EAAE,MAAM,MAAM,MAAM,oBAAoB,OAAO,UAAA;AAAA,EAC/D,WAAW,EAAE,MAAM,MAAM,MAAM,wBAAwB,OAAO,UAAA;AAAA,EAC9D,UAAU,EAAE,MAAM,MAAM,MAAM,sBAAsB,OAAO,UAAA;AAAA,EAC3D,MAAM,EAAE,MAAM,MAAM,MAAM,mBAAmB,OAAO,UAAA;AAAA,EACpD,OAAO,EAAE,MAAM,MAAM,MAAM,sBAAsB,OAAO,UAAA;AAAA,EACxD,UAAU,EAAE,MAAM,MAAM,MAAM,yBAAyB,OAAO,UAAA;AAAA,EAC9D,QAAQ,EAAE,MAAM,MAAM,MAAM,qBAAqB,OAAO,UAAA;AAAA,EACxD,QAAQ,EAAE,MAAM,MAAM,MAAM,oBAAoB,OAAO,UAAA;AAAA,EACvD,QAAQ,EAAE,MAAM,MAAM,MAAM,8BAA8B,OAAO,UAAA;AAAA,EACjE,QAAQ,EAAE,MAAM,MAAM,MAAM,2BAA2B,OAAO,UAAA;AAChE,GAGaC,IAA6CD,EAAwB;AAG3E,SAASE,EAAkBC,GAAuD;AACvF,SAAKA,IACEH,EAAwBG,CAAI,KAAKF,IADtBA;AAEpB;"}
@@ -0,0 +1,3 @@
1
+ declare const __VLS_export: import('vue').DefineComponent<{}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
2
+ declare const _default: typeof __VLS_export;
3
+ export default _default;
@@ -0,0 +1,8 @@
1
+ import o from "./index2.vue.js";
2
+ /* empty css */
3
+ import t from "../../../_virtual/_plugin-vue_export-helper.js";
4
+ const m = /* @__PURE__ */ t(o, [["__scopeId", "data-v-d81fc5a2"]]);
5
+ export {
6
+ m as default
7
+ };
8
+ //# sourceMappingURL=index.vue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.vue.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
@@ -0,0 +1,423 @@
1
+ import { defineComponent as oe, ref as n, onMounted as ne, computed as d, watch as Q, h as p, openBlock as i, createElementBlock as m, createVNode as r, unref as l, withCtx as u, createElementVNode as o, toDisplayString as c, createTextVNode as N, Fragment as M, renderList as Y, createBlock as L, createCommentVNode as ie } from "vue";
2
+ import { useMessage as ue, NProgress as de, NCard as $, NDatePicker as ce, NButton as z, NSelect as j, NInput as ve, NGrid as pe, NGi as me, NSkeleton as H, NNumberAnimation as ye, NTabs as ge, NTabPane as fe, NDataTable as ke, NTag as J } from "naive-ui";
3
+ import { useI18n as he } from "vue-i18n";
4
+ import { SmartTable as be } from "smart-naive-table";
5
+ import Ue from "../../../components/Chart/LineChart.vue.js";
6
+ import { aiProviderApi as _e, aiUsageApi as B } from "../../../api/index.js";
7
+ import { AI_USAGE_GROUP_BY as b, AI_USAGE_SOURCE as Te } from "../../../types/api.js";
8
+ import { translateError as E } from "../../../utils/error.js";
9
+ import { fmtDateTime as we } from "../../../utils/format.js";
10
+ const Se = { class: "view" }, Ce = { class: "filter-bar" }, xe = { class: "filter-item" }, Ne = { class: "filter-label" }, Me = { class: "date-filter-row" }, Le = { class: "quick-range" }, $e = { class: "filter-item" }, De = { class: "filter-label" }, Pe = { class: "filter-item" }, Re = { class: "filter-label" }, Ae = { class: "filter-item" }, Ie = { class: "filter-label" }, qe = { class: "metric-body" }, ze = {
11
+ key: 1,
12
+ class: "metric-val tabular"
13
+ }, Be = {
14
+ key: 0,
15
+ class: "metric-suffix"
16
+ }, Ee = { class: "metric-label" }, We = /* @__PURE__ */ oe({
17
+ __name: "index",
18
+ setup(Ge) {
19
+ const { t } = he(), D = ue();
20
+ function G(e) {
21
+ const a = e.getFullYear(), s = String(e.getMonth() + 1).padStart(2, "0"), v = String(e.getDate()).padStart(2, "0");
22
+ return `${a}-${s}-${v}`;
23
+ }
24
+ function K(e) {
25
+ const a = /* @__PURE__ */ new Date();
26
+ return a.setDate(a.getDate() - e), a;
27
+ }
28
+ function V(e) {
29
+ return [G(K(e - 1)), G(K(0))];
30
+ }
31
+ const g = n(V(7)), f = n(7);
32
+ function P(e) {
33
+ g.value = V(e), f.value = e;
34
+ }
35
+ function W(e) {
36
+ e && (g.value = e, f.value = null);
37
+ }
38
+ const U = n(null), _ = n(null), T = n(null), F = n([]);
39
+ ne(async () => {
40
+ try {
41
+ const { items: e } = await _e.page({ page: 1, pageSize: 100 });
42
+ F.value = e.map((a) => ({ label: a.name, value: a.code }));
43
+ } catch {
44
+ }
45
+ });
46
+ const k = d(() => {
47
+ const e = {
48
+ from: `${g.value[0]}T00:00:00`,
49
+ to: `${g.value[1]}T23:59:59`
50
+ };
51
+ return U.value && (e.providerCode = U.value), _.value && (e.model = _.value), T.value && (e.scene = T.value), e;
52
+ }), O = [
53
+ { value: b.Provider, labelKey: "provider" },
54
+ { value: b.Model, labelKey: "model" },
55
+ { value: b.Scene, labelKey: "scene" },
56
+ { value: b.User, labelKey: "user" }
57
+ ], h = n(b.Provider), R = n(null), w = n(!0);
58
+ let S = 0;
59
+ async function X() {
60
+ const e = ++S;
61
+ w.value = !0;
62
+ try {
63
+ const a = await B.summary({ ...k.value, groupBy: h.value });
64
+ if (e !== S) return;
65
+ R.value = a;
66
+ } catch (a) {
67
+ if (e !== S) return;
68
+ D.error(E(a));
69
+ } finally {
70
+ e === S && (w.value = !1);
71
+ }
72
+ }
73
+ Q([k, h], X, { immediate: !0 });
74
+ const y = d(() => {
75
+ var e;
76
+ return ((e = R.value) == null ? void 0 : e.totals) ?? null;
77
+ }), A = d(() => {
78
+ var e;
79
+ return ((e = R.value) == null ? void 0 : e.groups) ?? [];
80
+ }), I = d(() => {
81
+ const e = A.value.reduce((s, v) => s + v.callCount, 0);
82
+ return e ? A.value.reduce((s, v) => s + v.avgLatencyMs * v.callCount, 0) / e : null;
83
+ }), Z = d(() => {
84
+ var e, a, s;
85
+ return [
86
+ { key: "totalTokens", value: ((e = y.value) == null ? void 0 : e.totalTokens) ?? 0, empty: !y.value },
87
+ { key: "totalCalls", value: ((a = y.value) == null ? void 0 : a.callCount) ?? 0, empty: !y.value },
88
+ { key: "failureCount", value: ((s = y.value) == null ? void 0 : s.failureCount) ?? 0, empty: !y.value },
89
+ {
90
+ key: "avgLatency",
91
+ value: I.value != null ? Math.round(I.value) : 0,
92
+ suffix: "ms",
93
+ empty: I.value == null
94
+ }
95
+ ];
96
+ }), ee = d(() => {
97
+ const e = O.find((a) => a.value === h.value);
98
+ return t(`aiUsage.groupBy.${(e == null ? void 0 : e.labelKey) ?? "provider"}`);
99
+ }), te = d(() => [
100
+ { title: () => ee.value, key: "label", render: (e) => e.label ?? e.key },
101
+ {
102
+ title: () => t("aiUsage.trend.inputTokens"),
103
+ key: "inputTokens",
104
+ render: (e) => e.inputTokens.toLocaleString()
105
+ },
106
+ {
107
+ title: () => t("aiUsage.trend.outputTokens"),
108
+ key: "outputTokens",
109
+ render: (e) => e.outputTokens.toLocaleString()
110
+ },
111
+ {
112
+ title: () => t("aiUsage.metric.totalCalls"),
113
+ key: "callCount",
114
+ render: (e) => e.callCount.toLocaleString()
115
+ },
116
+ {
117
+ title: () => t("aiUsage.table.latency"),
118
+ key: "avgLatencyMs",
119
+ render: (e) => `${Math.round(e.avgLatencyMs)} ms`
120
+ },
121
+ {
122
+ title: () => t("aiUsage.share"),
123
+ key: "sharePercent",
124
+ render: (e) => p("div", { class: "share-cell" }, [
125
+ p("div", { class: "share-bar" }, [
126
+ p(de, {
127
+ type: "line",
128
+ percentage: Math.round(e.sharePercent),
129
+ showIndicator: !1,
130
+ height: 6
131
+ })
132
+ ]),
133
+ p("span", { class: "share-text" }, `${e.sharePercent.toFixed(1)}%`)
134
+ ])
135
+ }
136
+ ]), C = n([]), q = n(!0);
137
+ let x = 0;
138
+ async function ae() {
139
+ const e = ++x;
140
+ q.value = !0;
141
+ try {
142
+ const a = await B.trend(k.value);
143
+ if (e !== x) return;
144
+ C.value = a;
145
+ } catch (a) {
146
+ if (e !== x) return;
147
+ D.error(E(a));
148
+ } finally {
149
+ e === x && (q.value = !1);
150
+ }
151
+ }
152
+ Q(k, ae, { immediate: !0 });
153
+ const le = d(() => C.value.map((e) => e.date)), se = d(() => [
154
+ { name: t("aiUsage.trend.inputTokens"), data: C.value.map((e) => e.inputTokens) },
155
+ { name: t("aiUsage.trend.outputTokens"), data: C.value.map((e) => e.outputTokens) }
156
+ ]), re = [
157
+ {
158
+ key: "createTime",
159
+ title: () => t("aiUsage.table.createTime"),
160
+ width: 170,
161
+ render: (e) => we(e.createTime)
162
+ },
163
+ { key: "providerCode", title: () => t("aiUsage.table.provider"), width: 120 },
164
+ { key: "model", title: () => t("aiUsage.table.model"), width: 140, ellipsis: { tooltip: !0 } },
165
+ { key: "scene", title: () => t("aiUsage.table.scene"), width: 120, render: (e) => e.scene || "—" },
166
+ {
167
+ key: "userId",
168
+ title: () => t("aiUsage.table.user"),
169
+ width: 90,
170
+ render: (e) => e.userId != null ? String(e.userId) : "—"
171
+ },
172
+ {
173
+ key: "inputTokens",
174
+ title: () => t("aiUsage.table.inputTokens"),
175
+ width: 110,
176
+ render: (e) => e.inputTokens.toLocaleString()
177
+ },
178
+ {
179
+ key: "outputTokens",
180
+ title: () => t("aiUsage.table.outputTokens"),
181
+ width: 110,
182
+ render: (e) => e.outputTokens.toLocaleString()
183
+ },
184
+ {
185
+ key: "totalTokens",
186
+ title: () => t("aiUsage.table.totalTokens"),
187
+ width: 110,
188
+ render: (e) => e.totalTokens.toLocaleString()
189
+ },
190
+ {
191
+ key: "latencyMs",
192
+ title: () => t("aiUsage.table.latency"),
193
+ width: 90,
194
+ render: (e) => `${e.latencyMs} ms`
195
+ },
196
+ {
197
+ key: "success",
198
+ title: () => t("aiUsage.table.status"),
199
+ width: 90,
200
+ // 失败行用原生 title 兜底展示 errorMessage,不必为一个只读明细表再引入 NTooltip
201
+ render: (e) => p("span", { title: !e.success && e.errorMessage ? e.errorMessage : void 0 }, [
202
+ p(
203
+ J,
204
+ { size: "small", bordered: !1, type: e.success ? "success" : "error" },
205
+ () => e.success ? t("aiUsage.table.success") : t("aiUsage.table.failed")
206
+ )
207
+ ])
208
+ },
209
+ {
210
+ key: "streamed",
211
+ title: () => t("aiUsage.table.streamed"),
212
+ width: 80,
213
+ render: (e) => p(
214
+ J,
215
+ { size: "small", bordered: !1 },
216
+ () => e.streamed ? t("common.yes") : t("common.no")
217
+ )
218
+ },
219
+ {
220
+ key: "usageSource",
221
+ title: () => t("aiUsage.table.usageSource"),
222
+ width: 110,
223
+ render: (e) => e.usageSource === Te.Reported ? t("aiUsage.table.usageSourceReported") : t("aiUsage.table.usageSourceMissing")
224
+ }
225
+ ];
226
+ return (e, a) => (i(), m("div", Se, [
227
+ r(l($), { bordered: !0 }, {
228
+ default: u(() => [
229
+ o("div", Ce, [
230
+ o("div", xe, [
231
+ o("span", Ne, c(l(t)("aiUsage.filter.dateRange")), 1),
232
+ o("div", Me, [
233
+ r(l(ce), {
234
+ "formatted-value": g.value,
235
+ type: "daterange",
236
+ "value-format": "yyyy-MM-dd",
237
+ style: { width: "240px" },
238
+ "onUpdate:formattedValue": W
239
+ }, null, 8, ["formatted-value"]),
240
+ o("div", Le, [
241
+ r(l(z), {
242
+ size: "small",
243
+ quaternary: "",
244
+ type: f.value === 1 ? "primary" : "default",
245
+ onClick: a[0] || (a[0] = (s) => P(1))
246
+ }, {
247
+ default: u(() => [
248
+ N(c(l(t)("aiUsage.filter.today")), 1)
249
+ ]),
250
+ _: 1
251
+ }, 8, ["type"]),
252
+ r(l(z), {
253
+ size: "small",
254
+ quaternary: "",
255
+ type: f.value === 7 ? "primary" : "default",
256
+ onClick: a[1] || (a[1] = (s) => P(7))
257
+ }, {
258
+ default: u(() => [
259
+ N(c(l(t)("aiUsage.filter.last7Days")), 1)
260
+ ]),
261
+ _: 1
262
+ }, 8, ["type"]),
263
+ r(l(z), {
264
+ size: "small",
265
+ quaternary: "",
266
+ type: f.value === 30 ? "primary" : "default",
267
+ onClick: a[2] || (a[2] = (s) => P(30))
268
+ }, {
269
+ default: u(() => [
270
+ N(c(l(t)("aiUsage.filter.last30Days")), 1)
271
+ ]),
272
+ _: 1
273
+ }, 8, ["type"])
274
+ ])
275
+ ])
276
+ ]),
277
+ o("div", $e, [
278
+ o("span", De, c(l(t)("aiUsage.filter.provider")), 1),
279
+ r(l(j), {
280
+ value: U.value,
281
+ "onUpdate:value": a[3] || (a[3] = (s) => U.value = s),
282
+ options: F.value,
283
+ clearable: "",
284
+ filterable: "",
285
+ style: { width: "180px" },
286
+ placeholder: l(t)("aiUsage.filter.providerAll")
287
+ }, null, 8, ["value", "options", "placeholder"])
288
+ ]),
289
+ o("div", Pe, [
290
+ o("span", Re, c(l(t)("aiUsage.filter.model")), 1),
291
+ r(l(j), {
292
+ value: _.value,
293
+ "onUpdate:value": a[4] || (a[4] = (s) => _.value = s),
294
+ options: [],
295
+ filterable: "",
296
+ tag: "",
297
+ clearable: "",
298
+ style: { width: "180px" },
299
+ placeholder: l(t)("aiUsage.filter.modelAll")
300
+ }, null, 8, ["value", "placeholder"])
301
+ ]),
302
+ o("div", Ae, [
303
+ o("span", Ie, c(l(t)("aiUsage.filter.scene")), 1),
304
+ r(l(ve), {
305
+ value: T.value,
306
+ "onUpdate:value": a[5] || (a[5] = (s) => T.value = s),
307
+ clearable: "",
308
+ style: { width: "180px" },
309
+ placeholder: l(t)("aiUsage.filter.scenePlaceholder")
310
+ }, null, 8, ["value", "placeholder"])
311
+ ])
312
+ ])
313
+ ]),
314
+ _: 1
315
+ }),
316
+ r(l(pe), {
317
+ cols: "1 s:2 l:4",
318
+ responsive: "screen",
319
+ "x-gap": 16,
320
+ "y-gap": 16
321
+ }, {
322
+ default: u(() => [
323
+ (i(!0), m(M, null, Y(Z.value, (s) => (i(), L(l(me), {
324
+ key: s.key
325
+ }, {
326
+ default: u(() => [
327
+ r(l($), { bordered: !0 }, {
328
+ default: u(() => [
329
+ o("div", qe, [
330
+ w.value ? (i(), L(l(H), {
331
+ key: 0,
332
+ text: "",
333
+ width: "72px",
334
+ height: "34px"
335
+ })) : (i(), m("div", ze, [
336
+ s.empty ? (i(), m(M, { key: 0 }, [
337
+ N("—")
338
+ ], 64)) : (i(), m(M, { key: 1 }, [
339
+ r(l(ye), {
340
+ from: 0,
341
+ to: s.value,
342
+ "show-separator": ""
343
+ }, null, 8, ["to"]),
344
+ s.suffix ? (i(), m("span", Be, c(" " + s.suffix), 1)) : ie("", !0)
345
+ ], 64))
346
+ ])),
347
+ o("div", Ee, c(l(t)(`aiUsage.metric.${s.key}`)), 1)
348
+ ])
349
+ ]),
350
+ _: 2
351
+ }, 1024)
352
+ ]),
353
+ _: 2
354
+ }, 1024))), 128))
355
+ ]),
356
+ _: 1
357
+ }),
358
+ r(l($), { bordered: !0 }, {
359
+ default: u(() => [
360
+ r(l(ge), {
361
+ value: h.value,
362
+ "onUpdate:value": a[6] || (a[6] = (s) => h.value = s),
363
+ type: "line",
364
+ animated: ""
365
+ }, {
366
+ default: u(() => [
367
+ (i(), m(M, null, Y(O, (s) => r(l(fe), {
368
+ key: s.value,
369
+ name: s.value,
370
+ tab: l(t)(`aiUsage.groupBy.${s.labelKey}`),
371
+ "display-directive": "show:lazy"
372
+ }, {
373
+ default: u(() => [
374
+ r(l(ke), {
375
+ columns: te.value,
376
+ data: A.value,
377
+ loading: w.value,
378
+ "row-key": (v) => v.key,
379
+ bordered: !1,
380
+ size: "small"
381
+ }, null, 8, ["columns", "data", "loading", "row-key"])
382
+ ]),
383
+ _: 1
384
+ }, 8, ["name", "tab"])), 64))
385
+ ]),
386
+ _: 1
387
+ }, 8, ["value"])
388
+ ]),
389
+ _: 1
390
+ }),
391
+ r(l($), {
392
+ title: l(t)("aiUsage.trend.title"),
393
+ bordered: !0
394
+ }, {
395
+ default: u(() => [
396
+ q.value ? (i(), L(l(H), {
397
+ key: 0,
398
+ height: "320px",
399
+ sharp: !1
400
+ })) : (i(), L(Ue, {
401
+ key: 1,
402
+ categories: le.value,
403
+ series: se.value
404
+ }, null, 8, ["categories", "series"]))
405
+ ]),
406
+ _: 1
407
+ }, 8, ["title"]),
408
+ r(l(be), {
409
+ columns: re,
410
+ fetcher: l(B).page,
411
+ params: k.value,
412
+ search: !1,
413
+ "default-page-size": 20,
414
+ "storage-key": "sys-ai-usage",
415
+ onError: a[7] || (a[7] = (s) => l(D).error(l(E)(s)))
416
+ }, null, 8, ["fetcher", "params"])
417
+ ]));
418
+ }
419
+ });
420
+ export {
421
+ We as default
422
+ };
423
+ //# sourceMappingURL=index2.vue.js.map