smart-admin-web 10.20.0 → 10.21.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.
- package/dist/api/index.d.ts +19 -1
- package/dist/api/index.js +85 -66
- package/dist/api/index.js.map +1 -1
- package/dist/api/schema.d.ts +448 -1
- package/dist/assets/icons/ph-subset.json.js +1 -1
- package/dist/components/FormContainer/index.vue.js +87 -75
- package/dist/components/FormContainer/index.vue.js.map +1 -1
- package/dist/components/UserPicker/UserAvatar.vue.d.ts +8 -0
- package/dist/components/UserPicker/UserAvatar.vue.js +8 -0
- package/dist/components/UserPicker/UserAvatar.vue.js.map +1 -0
- package/dist/components/UserPicker/UserAvatar2.vue.js +38 -0
- package/dist/components/UserPicker/UserAvatar2.vue.js.map +1 -0
- package/dist/components/UserPicker/index.vue.js +1 -1
- package/dist/components/UserPicker/index2.vue.js +328 -186
- package/dist/components/UserPicker/index2.vue.js.map +1 -1
- package/dist/index.js +248 -246
- package/dist/layouts/NoticeBell.vue.js +2 -2
- package/dist/layouts/NoticeBell2.vue.js +18 -17
- package/dist/layouts/NoticeBell2.vue.js.map +1 -1
- package/dist/lib/runtime.js +1 -1
- package/dist/lib/watermark.d.ts +1 -1
- package/dist/locales/en-US.d.ts +62 -3
- package/dist/locales/en-US.js +66 -7
- package/dist/locales/en-US.js.map +1 -1
- package/dist/locales/kernelMenuTitles.js +2 -1
- package/dist/locales/kernelMenuTitles.js.map +1 -1
- package/dist/locales/zh-CN.d.ts +62 -3
- package/dist/locales/zh-CN.js +66 -7
- package/dist/locales/zh-CN.js.map +1 -1
- package/dist/router/viewRegistry.js +6 -6
- package/dist/router/viewRegistry.js.map +1 -1
- package/dist/style.css +1 -1
- package/dist/theme/naive-theme.js +72 -67
- package/dist/theme/naive-theme.js.map +1 -1
- package/dist/types/api.d.ts +91 -0
- package/dist/types/api.js +17 -15
- package/dist/types/api.js.map +1 -1
- package/dist/utils/shake.d.ts +1 -0
- package/dist/utils/shake.js +30 -0
- package/dist/utils/shake.js.map +1 -0
- package/dist/views/login/LoginForm.vue.js +2 -2
- package/dist/views/login/LoginForm2.vue.js +135 -135
- package/dist/views/login/LoginForm2.vue.js.map +1 -1
- package/dist/views/login/components/LoginDesk.vue.js +1 -1
- package/dist/views/login/components/LoginDesk2.vue.js +59 -47
- package/dist/views/login/components/LoginDesk2.vue.js.map +1 -1
- package/dist/views/personal/notice2.vue.js +1 -1
- package/dist/views/personal/notice2.vue.js.map +1 -1
- package/dist/views/personal/sessions.vue.js +2 -2
- package/dist/views/personal/sessions2.vue.js +41 -40
- package/dist/views/personal/sessions2.vue.js.map +1 -1
- package/dist/views/system/ai-usage/index.vue.js +3 -3
- package/dist/views/system/ai-usage/index2.vue.js +13 -12
- package/dist/views/system/ai-usage/index2.vue.js.map +1 -1
- package/dist/views/system/config/components/AdvancedSection.vue.js +3 -3
- package/dist/views/system/config/components/AdvancedSection2.vue.js +2 -1
- package/dist/views/system/config/components/AdvancedSection2.vue.js.map +1 -1
- package/dist/views/system/dict/index.vue.js +2 -2
- package/dist/views/system/dict/index2.vue.js +12 -1
- package/dist/views/system/dict/index2.vue.js.map +1 -1
- package/dist/views/system/file/index2.vue.js +1 -0
- package/dist/views/system/file/index2.vue.js.map +1 -1
- package/dist/views/system/job/index.vue.js +3 -3
- package/dist/views/system/job/index2.vue.js +182 -181
- package/dist/views/system/job/index2.vue.js.map +1 -1
- package/dist/views/system/job-log/components/AttemptTable2.vue.js +8 -3
- package/dist/views/system/job-log/components/AttemptTable2.vue.js.map +1 -1
- package/dist/views/system/job-log/index.vue.js +1 -1
- package/dist/views/system/job-log/index2.vue.js +5 -5
- package/dist/views/system/job-log/index2.vue.js.map +1 -1
- package/dist/views/system/log/exception/index.vue.js +2 -2
- package/dist/views/system/log/exception/index2.vue.js +8 -5
- package/dist/views/system/log/exception/index2.vue.js.map +1 -1
- package/dist/views/system/log/login/index2.vue.js +21 -12
- package/dist/views/system/log/login/index2.vue.js.map +1 -1
- package/dist/views/system/log/op/index.vue.js +2 -2
- package/dist/views/system/log/op/index2.vue.js +50 -40
- package/dist/views/system/log/op/index2.vue.js.map +1 -1
- package/dist/views/system/menu/components/ButtonManager.vue.js +2 -2
- package/dist/views/system/menu/components/ButtonManager2.vue.js +45 -36
- package/dist/views/system/menu/components/ButtonManager2.vue.js.map +1 -1
- package/dist/views/system/menu/index.vue.js +2 -2
- package/dist/views/system/menu/index2.vue.js +11 -11
- package/dist/views/system/menu/index2.vue.js.map +1 -1
- package/dist/views/system/module/index.vue.js +1 -1
- package/dist/views/system/module/index2.vue.js +171 -117
- package/dist/views/system/module/index2.vue.js.map +1 -1
- package/dist/views/system/notice/index.vue.js +3 -3
- package/dist/views/system/notice/index2.vue.js +1 -0
- package/dist/views/system/notice/index2.vue.js.map +1 -1
- package/dist/views/system/org/index.vue.js +2 -2
- package/dist/views/system/org/index2.vue.js +70 -67
- package/dist/views/system/org/index2.vue.js.map +1 -1
- package/dist/views/system/position/index.vue.js +2 -2
- package/dist/views/system/position/index2.vue.js +57 -48
- package/dist/views/system/position/index2.vue.js.map +1 -1
- package/dist/views/system/recycle/index.vue.js +1 -1
- package/dist/views/system/recycle/index2.vue.js +3 -1
- package/dist/views/system/recycle/index2.vue.js.map +1 -1
- package/dist/views/system/role/components/GrantMenuSheet.vue.d.ts +2 -0
- package/dist/views/system/role/components/GrantMenuSheet.vue.js +2 -2
- package/dist/views/system/role/components/GrantMenuSheet2.vue.js +54 -49
- package/dist/views/system/role/components/GrantMenuSheet2.vue.js.map +1 -1
- package/dist/views/system/role/components/GrantMenuTable.vue.js +1 -1
- package/dist/views/system/role/components/GrantMenuTable2.vue.js +159 -195
- package/dist/views/system/role/components/GrantMenuTable2.vue.js.map +1 -1
- package/dist/views/system/role/components/grantMenuGroups.d.ts +54 -0
- package/dist/views/system/role/components/grantMenuGroups.js +116 -76
- package/dist/views/system/role/components/grantMenuGroups.js.map +1 -1
- package/dist/views/system/role/index.vue.js +2 -2
- package/dist/views/system/role/index2.vue.js +152 -145
- package/dist/views/system/role/index2.vue.js.map +1 -1
- package/dist/views/system/session/index2.vue.js +39 -35
- package/dist/views/system/session/index2.vue.js.map +1 -1
- package/dist/views/system/user/components/UserGrantEffectiveTable.vue.d.ts +10 -0
- package/dist/views/system/user/components/UserGrantEffectiveTable.vue.js +5 -0
- package/dist/views/system/user/components/UserGrantEffectiveTable.vue.js.map +1 -0
- package/dist/views/system/user/components/UserGrantEffectiveTable2.vue.js +96 -0
- package/dist/views/system/user/components/UserGrantEffectiveTable2.vue.js.map +1 -0
- package/dist/views/system/user/components/UserGrantMenuSheet.vue.d.ts +13 -0
- package/dist/views/system/user/components/UserGrantMenuSheet.vue.js +8 -0
- package/dist/views/system/user/components/UserGrantMenuSheet.vue.js.map +1 -0
- package/dist/views/system/user/components/UserGrantMenuSheet2.vue.js +216 -0
- package/dist/views/system/user/components/UserGrantMenuSheet2.vue.js.map +1 -0
- package/dist/views/system/user/components/UserGrantMenuTable.vue.d.ts +13 -0
- package/dist/views/system/user/components/UserGrantMenuTable.vue.js +8 -0
- package/dist/views/system/user/components/UserGrantMenuTable.vue.js.map +1 -0
- package/dist/views/system/user/components/UserGrantMenuTable2.vue.js +186 -0
- package/dist/views/system/user/components/UserGrantMenuTable2.vue.js.map +1 -0
- package/dist/views/system/user/components/UserGrantNodeRow.vue.d.ts +31 -0
- package/dist/views/system/user/components/UserGrantNodeRow.vue.js +8 -0
- package/dist/views/system/user/components/UserGrantNodeRow.vue.js.map +1 -0
- package/dist/views/system/user/components/UserGrantNodeRow2.vue.js +171 -0
- package/dist/views/system/user/components/UserGrantNodeRow2.vue.js.map +1 -0
- package/dist/views/system/user/components/UserGrantOverviewDrawer.vue.d.ts +21 -0
- package/dist/views/system/user/components/UserGrantOverviewDrawer.vue.js +5 -0
- package/dist/views/system/user/components/UserGrantOverviewDrawer.vue.js.map +1 -0
- package/dist/views/system/user/components/UserGrantOverviewDrawer2.vue.js +178 -0
- package/dist/views/system/user/components/UserGrantOverviewDrawer2.vue.js.map +1 -0
- package/dist/views/system/user/components/userGrantState.d.ts +64 -0
- package/dist/views/system/user/components/userGrantState.js +117 -0
- package/dist/views/system/user/components/userGrantState.js.map +1 -0
- package/dist/views/system/user/index.vue.js +2 -2
- package/dist/views/system/user/index2.vue.js +250 -207
- package/dist/views/system/user/index2.vue.js.map +1 -1
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notice2.vue.js","sources":["../../../src/views/personal/notice.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// 我的通知(个人页,非管理端):列出发给自己的通知并读正文、标记已读。\n// 走 [ActiveSession] 的 notice/mine —— 任何登录用户可调,故本页注册为静态路由,不进菜单/权限系统。\n// 正文随列表一起返回(NoticeMineItem.content),点\"查看\"不另发请求。\n// 通知带动作列表时,正文弹层底部渲染成按钮(useNoticeAction),与顶栏铃铛同一套行为。\nimport { h, ref } from 'vue'\nimport { NButton, NModal, NSpace, NTag, useMessage } from 'naive-ui'\nimport { useI18n } from 'vue-i18n'\nimport { SmartTable, type SmartTableColumn, type SmartTableInst } from 'smart-naive-table'\nimport AppIcon from '#/components/AppIcon.vue'\nimport MarkdownView from '#/components/MarkdownEditor/MarkdownView.vue'\nimport { useConfirm } from '#/composables/useConfirm'\nimport { useNoticeAction } from '#/composables/useNoticeAction'\nimport { noticeApi } from '#/api'\nimport { NoticeType, type NoticeAction, type NoticeMineItem } from '#/types/api'\nimport { translateError } from '#/utils/error'\nimport { fmtDateTime } from '#/utils/format'\nimport { TABLE_MIN_ROW_HEIGHT, TABLE_TOOLBAR } from '#/utils/tableToolbar'\nimport TableTotal from '#/components/TableTotal/index.vue'\nimport {\n createClientFilterFetcher,\n passthroughFilterSerializer,\n deriveHeaderFilters,\n} from '#/utils/tableFilter'\n\nconst { t } = useI18n()\nconst message = useMessage()\nconst { run } = useConfirm()\nconst noticeAction = useNoticeAction()\nconst tableRef = ref<SmartTableInst<NoticeMineItem>>()\n// 后端没有过滤能力,搜索由前端求值;fields 要与下面声明了 search 的列 key 一致\nconst fetcher = createClientFilterFetcher(noticeApi.mine, ['title', 'type', 'isRead'])\n\nconst columns: SmartTableColumn<NoticeMineItem>[] = [\n { type: 'index', title: () => t('common.rowNo'), width: 64, align: 'center' },\n {\n key: 'isRead',\n title: () => t('notice.status'),\n width: 80,\n search: {},\n // 布尔值走下拉;render 仍负责标签样式(render 优先于 options)\n options: [\n { label: () => t('notice.read'), value: true },\n { label: () => t('notice.unread'), value: false },\n ],\n render: r =>\n h(NTag, { size: 'small', type: r.isRead ? 'default' : 'success', bordered: false }, () =>\n r.isRead ? t('notice.read') : t('notice.unread'),\n ),\n },\n { key: 'title', title: () => t('notice.noticeTitle'), search: {} },\n {\n key: 'type',\n title: () => t('notice.type'),\n width: 90,\n search: {},\n options: [\n { label: () => t('notice.typeNotice'), value: NoticeType.Notice },\n { label: () => t('notice.typeAnnouncement'), value: NoticeType.Announcement },\n { label: () => t('notice.typeMessage'), value: NoticeType.Message },\n ],\n // 三种类型各有各的文案与颜色(与系统通知管理页一致)。必须三类分开渲染:\n // 若「消息」被显示成「通知」,选筛选下拉里的「消息」就会筛出界面上写着「通知」的行。\n render: r =>\n h(\n NTag,\n {\n size: 'small',\n type:\n r.type === NoticeType.Announcement\n ? 'warning'\n : r.type === NoticeType.Message\n ? 'success'\n : 'info',\n bordered: false,\n },\n () =>\n r.type === NoticeType.Announcement\n ? t('notice.typeAnnouncement')\n : r.type === NoticeType.Message\n ? t('notice.typeMessage')\n : t('notice.typeNotice'),\n ),\n },\n {\n key: 'publishTime',\n title: () => t('notice.publishTime'),\n width: 170,\n render: r => fmtDateTime(r.publishTime),\n },\n {\n key: 'op',\n title: () => t('common.operation'),\n width: 80,\n fixed: 'right',\n hideInSetting: true,\n render: r =>\n h(\n NButton,\n { size: 'small', quaternary: true, type: 'primary', onClick: () => openView(r) },\n () => t('notice.view'),\n ),\n },\n]\nderiveHeaderFilters(columns)\n\n// ── 查看正文(行里已带 content,不发请求)+ 顺手标记已读 ──\nconst showView = ref(false)\nconst viewRow = ref<NoticeMineItem | null>(null)\nasync function openView(r: NoticeMineItem) {\n viewRow.value = r\n showView.value = true\n if (r.isRead) return\n try {\n await noticeApi.markRead(r.id)\n await tableRef.value?.refresh() // 顶栏未读角标由其自身的 30s 轮询自愈\n } catch (e) {\n message.error(translateError(e))\n }\n}\n\nconst running = ref<number | null>(null)\nasync function onAction(a: NoticeAction, index: number) {\n running.value = index\n try {\n if (await noticeAction.run(a)) showView.value = false\n } finally {\n running.value = null\n }\n}\n\nasync function markAll() {\n if (await run(() => noticeApi.markAllRead(), t('notice.allRead'))) await tableRef.value?.refresh()\n}\n</script>\n\n<template>\n <!-- .fill-page:高度链在 styles/layout.css,卡片吃满内容区,滚动只发生在表体里。 -->\n <div class=\"fill-page\">\n <SmartTable\n ref=\"tableRef\"\n :columns=\"columns\"\n :fetcher=\"fetcher\"\n :toolbar=\"TABLE_TOOLBAR\"\n :search=\"{ container: 'table' }\"\n :filter-serializer=\"passthroughFilterSerializer\"\n fill-height\n :min-row-height=\"TABLE_MIN_ROW_HEIGHT\"\n card-on-narrow\n storage-key=\"personal-notice\"\n @error=\"e => message.error(translateError(e))\"\n >\n <template #pagination-prefix=\"{ itemCount }\">\n <TableTotal :count=\"itemCount\" />\n </template>\n <template #toolbar-right>\n <n-button @click=\"markAll\">\n <template #icon><AppIcon icon=\"ph:checks\" :size=\"16\" /></template>\n {{ t('app.notice.markAllRead') }}\n </n-button>\n </template>\n </SmartTable>\n\n <n-modal\n v-model:show=\"showView\"\n preset=\"card\"\n :bordered=\"false\"\n :segmented=\"{ content: true, footer: 'soft' }\"\n :title=\"viewRow?.title || t('notice.detailTitle')\"\n style=\"width: 720px; max-width: 92vw\"\n >\n <MarkdownView :value=\"viewRow?.content\" />\n <template v-if=\"viewRow?.actions?.length\" #footer>\n <n-space justify=\"end\">\n <n-button\n v-for=\"(a, i) in viewRow.actions\"\n :key=\"i\"\n size=\"small\"\n :type=\"noticeAction.buttonType(a)\"\n :loading=\"running === i\"\n :disabled=\"running !== null && running !== i\"\n @click=\"onAction(a, i)\"\n >\n {{ noticeAction.label(a) }}\n </n-button>\n </n-space>\n </template>\n </n-modal>\n </div>\n</template>\n"],"names":["useI18n","message","useMessage","run","useConfirm","noticeAction","useNoticeAction","tableRef","ref","fetcher","createClientFilterFetcher","noticeApi","columns","r","NTag","NoticeType","fmtDateTime","NButton","openView","deriveHeaderFilters","showView","viewRow","_a","e","translateError","running","onAction","a","index","markAll","_openBlock","_createElementBlock","_hoisted_1","_createVNode","_unref","SmartTable","TABLE_TOOLBAR","passthroughFilterSerializer","TABLE_MIN_ROW_HEIGHT","_cache","_withCtx","itemCount","TableTotal","AppIcon","_createTextVNode","NModal","$event","MarkdownView","_c","_b","NSpace","_Fragment","i","_createBlock"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"notice2.vue.js","sources":["../../../src/views/personal/notice.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// 我的通知(个人页,非管理端):列出发给自己的通知并读正文、标记已读。\n// 走 [ActiveSession] 的 notice/mine —— 任何登录用户可调,故本页注册为静态路由,不进菜单/权限系统。\n// 正文随列表一起返回(NoticeMineItem.content),点\"查看\"不另发请求。\n// 通知带动作列表时,正文弹层底部渲染成按钮(useNoticeAction),与顶栏铃铛同一套行为。\nimport { h, ref } from 'vue'\nimport { NButton, NModal, NSpace, NTag, useMessage } from 'naive-ui'\nimport { useI18n } from 'vue-i18n'\nimport { SmartTable, type SmartTableColumn, type SmartTableInst } from 'smart-naive-table'\nimport AppIcon from '#/components/AppIcon.vue'\nimport MarkdownView from '#/components/MarkdownEditor/MarkdownView.vue'\nimport { useConfirm } from '#/composables/useConfirm'\nimport { useNoticeAction } from '#/composables/useNoticeAction'\nimport { noticeApi } from '#/api'\nimport { NoticeType, type NoticeAction, type NoticeMineItem } from '#/types/api'\nimport { translateError } from '#/utils/error'\nimport { fmtDateTime } from '#/utils/format'\nimport { TABLE_MIN_ROW_HEIGHT, TABLE_TOOLBAR } from '#/utils/tableToolbar'\nimport TableTotal from '#/components/TableTotal/index.vue'\nimport {\n createClientFilterFetcher,\n passthroughFilterSerializer,\n deriveHeaderFilters,\n} from '#/utils/tableFilter'\n\nconst { t } = useI18n()\nconst message = useMessage()\nconst { run } = useConfirm()\nconst noticeAction = useNoticeAction()\nconst tableRef = ref<SmartTableInst<NoticeMineItem>>()\n// 后端没有过滤能力,搜索由前端求值;fields 要与下面声明了 search 的列 key 一致\nconst fetcher = createClientFilterFetcher(noticeApi.mine, ['title', 'type', 'isRead'])\n\nconst columns: SmartTableColumn<NoticeMineItem>[] = [\n { type: 'index', title: () => t('common.rowNo'), width: 64, align: 'center' },\n {\n key: 'isRead',\n title: () => t('notice.status'),\n width: 80,\n search: {},\n // 布尔值走下拉;render 仍负责标签样式(render 优先于 options)\n options: [\n { label: () => t('notice.read'), value: true },\n { label: () => t('notice.unread'), value: false },\n ],\n render: r =>\n h(NTag, { size: 'small', type: r.isRead ? 'default' : 'success', bordered: false }, () =>\n r.isRead ? t('notice.read') : t('notice.unread'),\n ),\n },\n { key: 'title', ellipsis: { tooltip: true }, title: () => t('notice.noticeTitle'), search: {} },\n {\n key: 'type',\n title: () => t('notice.type'),\n width: 90,\n search: {},\n options: [\n { label: () => t('notice.typeNotice'), value: NoticeType.Notice },\n { label: () => t('notice.typeAnnouncement'), value: NoticeType.Announcement },\n { label: () => t('notice.typeMessage'), value: NoticeType.Message },\n ],\n // 三种类型各有各的文案与颜色(与系统通知管理页一致)。必须三类分开渲染:\n // 若「消息」被显示成「通知」,选筛选下拉里的「消息」就会筛出界面上写着「通知」的行。\n render: r =>\n h(\n NTag,\n {\n size: 'small',\n type:\n r.type === NoticeType.Announcement\n ? 'warning'\n : r.type === NoticeType.Message\n ? 'success'\n : 'info',\n bordered: false,\n },\n () =>\n r.type === NoticeType.Announcement\n ? t('notice.typeAnnouncement')\n : r.type === NoticeType.Message\n ? t('notice.typeMessage')\n : t('notice.typeNotice'),\n ),\n },\n {\n key: 'publishTime',\n title: () => t('notice.publishTime'),\n width: 170,\n render: r => fmtDateTime(r.publishTime),\n },\n {\n key: 'op',\n title: () => t('common.operation'),\n width: 80,\n fixed: 'right',\n hideInSetting: true,\n render: r =>\n h(\n NButton,\n { size: 'small', quaternary: true, type: 'primary', onClick: () => openView(r) },\n () => t('notice.view'),\n ),\n },\n]\nderiveHeaderFilters(columns)\n\n// ── 查看正文(行里已带 content,不发请求)+ 顺手标记已读 ──\nconst showView = ref(false)\nconst viewRow = ref<NoticeMineItem | null>(null)\nasync function openView(r: NoticeMineItem) {\n viewRow.value = r\n showView.value = true\n if (r.isRead) return\n try {\n await noticeApi.markRead(r.id)\n await tableRef.value?.refresh() // 顶栏未读角标由其自身的 30s 轮询自愈\n } catch (e) {\n message.error(translateError(e))\n }\n}\n\nconst running = ref<number | null>(null)\nasync function onAction(a: NoticeAction, index: number) {\n running.value = index\n try {\n if (await noticeAction.run(a)) showView.value = false\n } finally {\n running.value = null\n }\n}\n\nasync function markAll() {\n if (await run(() => noticeApi.markAllRead(), t('notice.allRead'))) await tableRef.value?.refresh()\n}\n</script>\n\n<template>\n <!-- .fill-page:高度链在 styles/layout.css,卡片吃满内容区,滚动只发生在表体里。 -->\n <div class=\"fill-page\">\n <SmartTable\n ref=\"tableRef\"\n :columns=\"columns\"\n :fetcher=\"fetcher\"\n :toolbar=\"TABLE_TOOLBAR\"\n :search=\"{ container: 'table' }\"\n :filter-serializer=\"passthroughFilterSerializer\"\n fill-height\n :min-row-height=\"TABLE_MIN_ROW_HEIGHT\"\n card-on-narrow\n storage-key=\"personal-notice\"\n @error=\"e => message.error(translateError(e))\"\n >\n <template #pagination-prefix=\"{ itemCount }\">\n <TableTotal :count=\"itemCount\" />\n </template>\n <template #toolbar-right>\n <n-button @click=\"markAll\">\n <template #icon><AppIcon icon=\"ph:checks\" :size=\"16\" /></template>\n {{ t('app.notice.markAllRead') }}\n </n-button>\n </template>\n </SmartTable>\n\n <n-modal\n v-model:show=\"showView\"\n preset=\"card\"\n :bordered=\"false\"\n :segmented=\"{ content: true, footer: 'soft' }\"\n :title=\"viewRow?.title || t('notice.detailTitle')\"\n style=\"width: 720px; max-width: 92vw\"\n >\n <MarkdownView :value=\"viewRow?.content\" />\n <template v-if=\"viewRow?.actions?.length\" #footer>\n <n-space justify=\"end\">\n <n-button\n v-for=\"(a, i) in viewRow.actions\"\n :key=\"i\"\n size=\"small\"\n :type=\"noticeAction.buttonType(a)\"\n :loading=\"running === i\"\n :disabled=\"running !== null && running !== i\"\n @click=\"onAction(a, i)\"\n >\n {{ noticeAction.label(a) }}\n </n-button>\n </n-space>\n </template>\n </n-modal>\n </div>\n</template>\n"],"names":["useI18n","message","useMessage","run","useConfirm","noticeAction","useNoticeAction","tableRef","ref","fetcher","createClientFilterFetcher","noticeApi","columns","r","NTag","NoticeType","fmtDateTime","NButton","openView","deriveHeaderFilters","showView","viewRow","_a","e","translateError","running","onAction","a","index","markAll","_openBlock","_createElementBlock","_hoisted_1","_createVNode","_unref","SmartTable","TABLE_TOOLBAR","passthroughFilterSerializer","TABLE_MIN_ROW_HEIGHT","_cache","_withCtx","itemCount","TableTotal","AppIcon","_createTextVNode","NModal","$event","MarkdownView","_c","_b","NSpace","_Fragment","i","_createBlock"],"mappings":"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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import o from "./sessions2.vue.js";
|
|
2
2
|
/* empty css */
|
|
3
3
|
import s from "../../_virtual/_plugin-vue_export-helper.js";
|
|
4
|
-
const
|
|
4
|
+
const p = /* @__PURE__ */ s(o, [["__scopeId", "data-v-69d8850d"]]);
|
|
5
5
|
export {
|
|
6
|
-
|
|
6
|
+
p as default
|
|
7
7
|
};
|
|
8
8
|
//# sourceMappingURL=sessions.vue.js.map
|
|
@@ -1,99 +1,100 @@
|
|
|
1
|
-
import { defineComponent as
|
|
2
|
-
import { useMessage as
|
|
3
|
-
import { useI18n as
|
|
4
|
-
import { SmartTable as
|
|
1
|
+
import { defineComponent as y, ref as c, onMounted as w, openBlock as _, createElementBlock as T, createVNode as m, unref as s, withCtx as v, h as i } from "vue";
|
|
2
|
+
import { useMessage as x, NTag as A, NButton as b } from "naive-ui";
|
|
3
|
+
import { useI18n as C } from "vue-i18n";
|
|
4
|
+
import { SmartTable as B } from "smart-naive-table";
|
|
5
5
|
import { personalApi as d } from "../../api/index.js";
|
|
6
|
-
import { uaSummary as
|
|
6
|
+
import { uaSummary as p } from "../../utils/ua.js";
|
|
7
7
|
import { useConfirm as I } from "../../composables/useConfirm.js";
|
|
8
8
|
import { translateError as E } from "../../utils/error.js";
|
|
9
|
-
import { fmtDateTime as
|
|
9
|
+
import { fmtDateTime as u } from "../../utils/format.js";
|
|
10
10
|
import { TABLE_MIN_ROW_HEIGHT as M, TABLE_TOOLBAR as N } from "../../utils/tableToolbar.js";
|
|
11
11
|
import S from "../../components/TableTotal/index.vue.js";
|
|
12
12
|
import { deriveHeaderFilters as H } from "../../utils/tableFilter.js";
|
|
13
|
-
const L = { class: "fill-page" }, Q = /* @__PURE__ */
|
|
13
|
+
const L = { class: "fill-page" }, Q = /* @__PURE__ */ y({
|
|
14
14
|
__name: "sessions",
|
|
15
15
|
setup(O) {
|
|
16
|
-
const { t
|
|
16
|
+
const { t } = C(), f = x(), { confirm: g } = I(), o = c(!0), r = c([]);
|
|
17
17
|
async function n() {
|
|
18
|
-
|
|
18
|
+
o.value = !0;
|
|
19
19
|
try {
|
|
20
|
-
|
|
20
|
+
r.value = await d.sessions();
|
|
21
21
|
} catch (e) {
|
|
22
|
-
|
|
22
|
+
f.error(E(e));
|
|
23
23
|
} finally {
|
|
24
|
-
|
|
24
|
+
o.value = !1;
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
|
-
|
|
28
|
-
function
|
|
29
|
-
|
|
27
|
+
w(n);
|
|
28
|
+
function h(e) {
|
|
29
|
+
g({
|
|
30
30
|
type: "warning",
|
|
31
|
-
content:
|
|
31
|
+
content: t("session.kickMineConfirm"),
|
|
32
32
|
action: () => d.kickSession(e.sessionId),
|
|
33
|
-
successMsg:
|
|
33
|
+
successMsg: t("session.kicked")
|
|
34
34
|
}).then((l) => {
|
|
35
35
|
l && n();
|
|
36
36
|
});
|
|
37
37
|
}
|
|
38
38
|
const a = [
|
|
39
|
-
{ type: "index", title: () =>
|
|
39
|
+
{ type: "index", title: () => t("common.rowNo"), width: 64, align: "center" },
|
|
40
40
|
{
|
|
41
41
|
// 搜索按原始 UA 字符串「包含」求值(输入 Chrome / Windows 都能命中);单元格显示解析后的「浏览器 · 系统」
|
|
42
42
|
key: "userAgent",
|
|
43
|
-
title: () =>
|
|
43
|
+
title: () => t("session.device"),
|
|
44
|
+
width: 240,
|
|
44
45
|
search: {},
|
|
45
|
-
render: (e) =>
|
|
46
|
-
|
|
47
|
-
e.isCurrent ?
|
|
46
|
+
render: (e) => i("div", { class: "device-cell" }, [
|
|
47
|
+
i("span", { class: "device-text", title: p(e.userAgent) }, p(e.userAgent)),
|
|
48
|
+
e.isCurrent ? i(A, { type: "success", size: "small", bordered: !1 }, () => t("session.current")) : null
|
|
48
49
|
])
|
|
49
50
|
},
|
|
50
|
-
{ key: "ip", title: () =>
|
|
51
|
+
{ key: "ip", title: () => t("session.ip"), width: 140, search: {}, render: (e) => e.ip || "—" },
|
|
51
52
|
{
|
|
52
53
|
key: "loginTime",
|
|
53
|
-
title: () =>
|
|
54
|
+
title: () => t("session.loginTime"),
|
|
54
55
|
width: 180,
|
|
55
|
-
render: (e) =>
|
|
56
|
+
render: (e) => u(e.loginTime)
|
|
56
57
|
},
|
|
57
58
|
{
|
|
58
59
|
key: "expiresAt",
|
|
59
|
-
title: () =>
|
|
60
|
+
title: () => t("session.expiresAt"),
|
|
60
61
|
width: 180,
|
|
61
|
-
render: (e) =>
|
|
62
|
+
render: (e) => u(e.expiresAt)
|
|
62
63
|
},
|
|
63
64
|
{
|
|
64
65
|
key: "op",
|
|
65
|
-
title: () =>
|
|
66
|
+
title: () => t("common.operation"),
|
|
66
67
|
width: 110,
|
|
67
68
|
fixed: "right",
|
|
68
69
|
hideInSetting: !0,
|
|
69
|
-
render: (e) =>
|
|
70
|
-
|
|
70
|
+
render: (e) => i(
|
|
71
|
+
b,
|
|
71
72
|
{
|
|
72
73
|
size: "small",
|
|
73
74
|
quaternary: !0,
|
|
74
75
|
type: "error",
|
|
75
76
|
disabled: e.isCurrent,
|
|
76
|
-
onClick: () =>
|
|
77
|
+
onClick: () => h(e)
|
|
77
78
|
},
|
|
78
|
-
() =>
|
|
79
|
+
() => t("session.kick")
|
|
79
80
|
)
|
|
80
81
|
}
|
|
81
82
|
];
|
|
82
|
-
return H(a), (e, l) => (_(),
|
|
83
|
-
m(
|
|
83
|
+
return H(a), (e, l) => (_(), T("div", L, [
|
|
84
|
+
m(s(B), {
|
|
84
85
|
columns: a,
|
|
85
|
-
data:
|
|
86
|
+
data: r.value,
|
|
86
87
|
"row-key": "sessionId",
|
|
87
|
-
loading:
|
|
88
|
-
toolbar:
|
|
88
|
+
loading: o.value,
|
|
89
|
+
toolbar: s(N),
|
|
89
90
|
search: { container: "table" },
|
|
90
91
|
"fill-height": "",
|
|
91
|
-
"min-row-height":
|
|
92
|
+
"min-row-height": s(M),
|
|
92
93
|
"card-on-narrow": "",
|
|
93
94
|
"storage-key": "personal-sessions"
|
|
94
95
|
}, {
|
|
95
|
-
"pagination-prefix":
|
|
96
|
-
m(S, { count:
|
|
96
|
+
"pagination-prefix": v(({ itemCount: k }) => [
|
|
97
|
+
m(S, { count: k }, null, 8, ["count"])
|
|
97
98
|
]),
|
|
98
99
|
_: 1
|
|
99
100
|
}, 8, ["data", "loading", "toolbar", "min-row-height"])
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sessions2.vue.js","sources":["../../../src/views/personal/sessions.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// 我的会话(个人视角的\"登录设备\"):[ActiveSession] 人人可用,静态路由不进菜单(理由同 notice)。\n// 数据量受会话并发上限约束(个位数)→ onMounted 拉一次,交给 SmartTable 的静态数据模式\n// (前端分页 / 前端求值搜索 / 窄档卡片列表都是它内置的)。\n// 踢当前设备置灰:那等于自杀,请走\"退出登录\";管理端全量视角在 system/session。\nimport { h, onMounted, ref } from 'vue'\nimport { NButton, NTag, useMessage } from 'naive-ui'\nimport { useI18n } from 'vue-i18n'\nimport { SmartTable, type SmartTableColumn } from 'smart-naive-table'\nimport { personalApi } from '#/api'\nimport type { MySessionItem } from '#/types/api'\nimport { uaSummary } from '#/utils/ua'\nimport { useConfirm } from '#/composables/useConfirm'\nimport { translateError } from '#/utils/error'\nimport { fmtDateTime } from '#/utils/format'\nimport { TABLE_MIN_ROW_HEIGHT, TABLE_TOOLBAR } from '#/utils/tableToolbar'\nimport TableTotal from '#/components/TableTotal/index.vue'\n\nimport { deriveHeaderFilters } from '#/utils/tableFilter'\nconst { t } = useI18n()\nconst message = useMessage()\nconst { confirm } = useConfirm()\n\nconst loading = ref(true)\nconst rows = ref<MySessionItem[]>([])\n\nasync function load() {\n loading.value = true\n try {\n rows.value = await personalApi.sessions()\n } catch (e) {\n message.error(translateError(e))\n } finally {\n loading.value = false\n }\n}\nonMounted(load)\n\nfunction kick(r: MySessionItem) {\n confirm({\n type: 'warning',\n content: t('session.kickMineConfirm'),\n action: () => personalApi.kickSession(r.sessionId),\n successMsg: t('session.kicked'),\n }).then(ok => {\n if (ok) load()\n })\n}\n\nconst columns: SmartTableColumn<MySessionItem>[] = [\n { type: 'index', title: () => t('common.rowNo'), width: 64, align: 'center' },\n {\n // 搜索按原始 UA 字符串「包含」求值(输入 Chrome / Windows 都能命中);单元格显示解析后的「浏览器 · 系统」\n key: 'userAgent',\n title: () => t('session.device'),\n search: {},\n render: r =>\n h('div', { class: 'device-cell' }, [\n h('span', uaSummary(r.userAgent)),\n r.isCurrent\n ? h(NTag, { type: 'success', size: 'small', bordered: false }, () => t('session.current'))\n : null,\n ]),\n },\n { key: 'ip', title: () => t('session.ip'), width: 140, search: {}, render: r => r.ip || '—' },\n {\n key: 'loginTime',\n title: () => t('session.loginTime'),\n width: 180,\n render: r => fmtDateTime(r.loginTime),\n },\n {\n key: 'expiresAt',\n title: () => t('session.expiresAt'),\n width: 180,\n render: r => fmtDateTime(r.expiresAt),\n },\n {\n key: 'op',\n title: () => t('common.operation'),\n width: 110,\n fixed: 'right',\n hideInSetting: true,\n render: r =>\n h(\n NButton,\n {\n size: 'small',\n quaternary: true,\n type: 'error',\n disabled: r.isCurrent,\n onClick: () => kick(r),\n },\n () => t('session.kick'),\n ),\n },\n]\nderiveHeaderFilters(columns)\n</script>\n\n<template>\n <!-- .fill-page:高度链在 styles/layout.css,卡片吃满内容区,滚动只发生在表体里。 -->\n <div class=\"fill-page\">\n <SmartTable\n :columns=\"columns\"\n :data=\"rows\"\n row-key=\"sessionId\"\n :loading=\"loading\"\n :toolbar=\"TABLE_TOOLBAR\"\n :search=\"{ container: 'table' }\"\n fill-height\n :min-row-height=\"TABLE_MIN_ROW_HEIGHT\"\n card-on-narrow\n storage-key=\"personal-sessions\"\n >\n <template #pagination-prefix=\"{ itemCount }\">\n <TableTotal :count=\"itemCount\" />\n </template>\n </SmartTable>\n </div>\n</template>\n\n<style scoped>\n:deep(.device-cell) {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n</style>\n"],"names":["
|
|
1
|
+
{"version":3,"file":"sessions2.vue.js","sources":["../../../src/views/personal/sessions.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// 我的会话(个人视角的\"登录设备\"):[ActiveSession] 人人可用,静态路由不进菜单(理由同 notice)。\n// 数据量受会话并发上限约束(个位数)→ onMounted 拉一次,交给 SmartTable 的静态数据模式\n// (前端分页 / 前端求值搜索 / 窄档卡片列表都是它内置的)。\n// 踢当前设备置灰:那等于自杀,请走\"退出登录\";管理端全量视角在 system/session。\nimport { h, onMounted, ref } from 'vue'\nimport { NButton, NTag, useMessage } from 'naive-ui'\nimport { useI18n } from 'vue-i18n'\nimport { SmartTable, type SmartTableColumn } from 'smart-naive-table'\nimport { personalApi } from '#/api'\nimport type { MySessionItem } from '#/types/api'\nimport { uaSummary } from '#/utils/ua'\nimport { useConfirm } from '#/composables/useConfirm'\nimport { translateError } from '#/utils/error'\nimport { fmtDateTime } from '#/utils/format'\nimport { TABLE_MIN_ROW_HEIGHT, TABLE_TOOLBAR } from '#/utils/tableToolbar'\nimport TableTotal from '#/components/TableTotal/index.vue'\n\nimport { deriveHeaderFilters } from '#/utils/tableFilter'\nconst { t } = useI18n()\nconst message = useMessage()\nconst { confirm } = useConfirm()\n\nconst loading = ref(true)\nconst rows = ref<MySessionItem[]>([])\n\nasync function load() {\n loading.value = true\n try {\n rows.value = await personalApi.sessions()\n } catch (e) {\n message.error(translateError(e))\n } finally {\n loading.value = false\n }\n}\nonMounted(load)\n\nfunction kick(r: MySessionItem) {\n confirm({\n type: 'warning',\n content: t('session.kickMineConfirm'),\n action: () => personalApi.kickSession(r.sessionId),\n successMsg: t('session.kicked'),\n }).then(ok => {\n if (ok) load()\n })\n}\n\nconst columns: SmartTableColumn<MySessionItem>[] = [\n { type: 'index', title: () => t('common.rowNo'), width: 64, align: 'center' },\n {\n // 搜索按原始 UA 字符串「包含」求值(输入 Chrome / Windows 都能命中);单元格显示解析后的「浏览器 · 系统」\n key: 'userAgent',\n title: () => t('session.device'),\n width: 240,\n search: {},\n render: r =>\n h('div', { class: 'device-cell' }, [\n h('span', { class: 'device-text', title: uaSummary(r.userAgent) }, uaSummary(r.userAgent)),\n r.isCurrent\n ? h(NTag, { type: 'success', size: 'small', bordered: false }, () => t('session.current'))\n : null,\n ]),\n },\n { key: 'ip', title: () => t('session.ip'), width: 140, search: {}, render: r => r.ip || '—' },\n {\n key: 'loginTime',\n title: () => t('session.loginTime'),\n width: 180,\n render: r => fmtDateTime(r.loginTime),\n },\n {\n key: 'expiresAt',\n title: () => t('session.expiresAt'),\n width: 180,\n render: r => fmtDateTime(r.expiresAt),\n },\n {\n key: 'op',\n title: () => t('common.operation'),\n width: 110,\n fixed: 'right',\n hideInSetting: true,\n render: r =>\n h(\n NButton,\n {\n size: 'small',\n quaternary: true,\n type: 'error',\n disabled: r.isCurrent,\n onClick: () => kick(r),\n },\n () => t('session.kick'),\n ),\n },\n]\nderiveHeaderFilters(columns)\n</script>\n\n<template>\n <!-- .fill-page:高度链在 styles/layout.css,卡片吃满内容区,滚动只发生在表体里。 -->\n <div class=\"fill-page\">\n <SmartTable\n :columns=\"columns\"\n :data=\"rows\"\n row-key=\"sessionId\"\n :loading=\"loading\"\n :toolbar=\"TABLE_TOOLBAR\"\n :search=\"{ container: 'table' }\"\n fill-height\n :min-row-height=\"TABLE_MIN_ROW_HEIGHT\"\n card-on-narrow\n storage-key=\"personal-sessions\"\n >\n <template #pagination-prefix=\"{ itemCount }\">\n <TableTotal :count=\"itemCount\" />\n </template>\n </SmartTable>\n </div>\n</template>\n\n<style scoped>\n:deep(.device-cell) {\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n}\n/* 设备名放不下时收成省略号,「当前设备」标签始终完整显示 */\n:deep(.device-text) {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n:deep(.device-cell .n-tag) {\n flex: none;\n}\n</style>\n"],"names":["useI18n","message","useMessage","confirm","useConfirm","loading","ref","rows","load","personalApi","translateError","onMounted","kick","r","ok","columns","h","uaSummary","NTag","fmtDateTime","NButton","deriveHeaderFilters","_openBlock","_createElementBlock","_hoisted_1","_createVNode","_unref","SmartTable","TABLE_TOOLBAR","TABLE_MIN_ROW_HEIGHT","_withCtx","itemCount","TableTotal"],"mappings":"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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import o from "./index2.vue.js";
|
|
2
2
|
/* empty css */
|
|
3
|
-
import
|
|
4
|
-
const
|
|
3
|
+
import t from "../../../_virtual/_plugin-vue_export-helper.js";
|
|
4
|
+
const p = /* @__PURE__ */ t(o, [["__scopeId", "data-v-e5271b14"]]);
|
|
5
5
|
export {
|
|
6
|
-
|
|
6
|
+
p as default
|
|
7
7
|
};
|
|
8
8
|
//# sourceMappingURL=index.vue.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { defineComponent as ce, ref as o, onMounted as de, computed as d, watch as
|
|
2
|
-
import { useMessage as me, NProgress as ve, NTag as
|
|
1
|
+
import { defineComponent as ce, ref as o, onMounted as de, computed as d, watch as Y, h as n, openBlock as u, createElementBlock as v, createVNode as r, unref as l, withCtx as c, createElementVNode as i, toDisplayString as p, createTextVNode as N, Fragment as $, renderList as j, createBlock as A, createCommentVNode as pe } from "vue";
|
|
2
|
+
import { useMessage as me, NProgress as ve, NTag as J, NCard as R, NDatePicker as ge, NButton as q, NSelect as W, NInput as fe, NSkeleton as X, NNumberAnimation as ye, NTabs as he, NTabPane as ke } from "naive-ui";
|
|
3
3
|
import { useI18n as be } from "vue-i18n";
|
|
4
4
|
import { SmartTable as Z } from "smart-naive-table";
|
|
5
5
|
import Ue from "../../../components/AppIcon.vue.js";
|
|
@@ -77,7 +77,7 @@ const Me = { class: "view" }, Ne = { class: "filter-bar" }, $e = { class: "filte
|
|
|
77
77
|
e === C && (x.value = !1);
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
|
-
|
|
80
|
+
Y([U, g], le, { immediate: !0 });
|
|
81
81
|
const f = d(() => {
|
|
82
82
|
var e;
|
|
83
83
|
return ((e = P.value) == null ? void 0 : e.totals) ?? null;
|
|
@@ -123,7 +123,8 @@ const Me = { class: "view" }, Ne = { class: "filter-bar" }, $e = { class: "filte
|
|
|
123
123
|
{
|
|
124
124
|
title: () => re.value,
|
|
125
125
|
key: "label",
|
|
126
|
-
|
|
126
|
+
width: 160,
|
|
127
|
+
ellipsis: { tooltip: !0 },
|
|
127
128
|
search: {},
|
|
128
129
|
// 静态数据:条件由 SmartTable 前端求值
|
|
129
130
|
// 厂商 / 模型 / 场景是编码类文本,等宽;用户维度是姓名,保持正文
|
|
@@ -164,7 +165,7 @@ const Me = { class: "view" }, Ne = { class: "filter-bar" }, $e = { class: "filte
|
|
|
164
165
|
{
|
|
165
166
|
title: () => a("aiUsage.share"),
|
|
166
167
|
key: "sharePercent",
|
|
167
|
-
|
|
168
|
+
width: 200,
|
|
168
169
|
render: (e) => n("div", { class: "share-cell" }, [
|
|
169
170
|
n("div", { class: "share-bar" }, [
|
|
170
171
|
n(ve, {
|
|
@@ -193,7 +194,7 @@ const Me = { class: "view" }, Ne = { class: "filter-bar" }, $e = { class: "filte
|
|
|
193
194
|
e === M && (I.value = !1);
|
|
194
195
|
}
|
|
195
196
|
}
|
|
196
|
-
|
|
197
|
+
Y(U, ie, { immediate: !0 });
|
|
197
198
|
const oe = d(() => S.value.map((e) => e.date)), ue = d(() => [
|
|
198
199
|
{ name: a("aiUsage.trend.inputTokens"), data: S.value.map((e) => e.inputTokens) },
|
|
199
200
|
{ name: a("aiUsage.trend.outputTokens"), data: S.value.map((e) => e.outputTokens) }
|
|
@@ -270,7 +271,7 @@ const Me = { class: "view" }, Ne = { class: "filter-bar" }, $e = { class: "filte
|
|
|
270
271
|
// 失败行用原生 title 兜底展示 errorMessage,不必为一个只读明细表再引入 NTooltip
|
|
271
272
|
render: (e) => n("span", { title: !e.success && e.errorMessage ? e.errorMessage : void 0 }, [
|
|
272
273
|
n(
|
|
273
|
-
|
|
274
|
+
J,
|
|
274
275
|
{ size: "small", bordered: !1, type: e.success ? "success" : "error" },
|
|
275
276
|
() => e.success ? a("aiUsage.table.success") : a("aiUsage.table.failed")
|
|
276
277
|
)
|
|
@@ -281,7 +282,7 @@ const Me = { class: "view" }, Ne = { class: "filter-bar" }, $e = { class: "filte
|
|
|
281
282
|
title: () => a("aiUsage.table.streamed"),
|
|
282
283
|
width: 80,
|
|
283
284
|
render: (e) => n(
|
|
284
|
-
|
|
285
|
+
J,
|
|
285
286
|
{ size: "small", bordered: !1 },
|
|
286
287
|
() => e.streamed ? a("common.yes") : a("common.no")
|
|
287
288
|
)
|
|
@@ -351,7 +352,7 @@ const Me = { class: "view" }, Ne = { class: "filter-bar" }, $e = { class: "filte
|
|
|
351
352
|
]),
|
|
352
353
|
i("div", Le, [
|
|
353
354
|
i("span", Pe, p(l(a)("aiUsage.filter.provider")), 1),
|
|
354
|
-
r(l(
|
|
355
|
+
r(l(W), {
|
|
355
356
|
value: _.value,
|
|
356
357
|
"onUpdate:value": t[3] || (t[3] = (s) => _.value = s),
|
|
357
358
|
options: G.value,
|
|
@@ -363,7 +364,7 @@ const Me = { class: "view" }, Ne = { class: "filter-bar" }, $e = { class: "filte
|
|
|
363
364
|
]),
|
|
364
365
|
i("div", De, [
|
|
365
366
|
i("span", Be, p(l(a)("aiUsage.filter.model")), 1),
|
|
366
|
-
r(l(
|
|
367
|
+
r(l(W), {
|
|
367
368
|
value: T.value,
|
|
368
369
|
"onUpdate:value": t[4] || (t[4] = (s) => T.value = s),
|
|
369
370
|
options: [],
|
|
@@ -389,7 +390,7 @@ const Me = { class: "view" }, Ne = { class: "filter-bar" }, $e = { class: "filte
|
|
|
389
390
|
_: 1
|
|
390
391
|
}),
|
|
391
392
|
i("div", Ee, [
|
|
392
|
-
(u(!0), v($, null,
|
|
393
|
+
(u(!0), v($, null, j(se.value, (s) => (u(), A(l(R), {
|
|
393
394
|
key: s.key,
|
|
394
395
|
class: "a-card",
|
|
395
396
|
size: "small",
|
|
@@ -463,7 +464,7 @@ const Me = { class: "view" }, Ne = { class: "filter-bar" }, $e = { class: "filte
|
|
|
463
464
|
animated: ""
|
|
464
465
|
}, {
|
|
465
466
|
default: c(() => [
|
|
466
|
-
(u(), v($, null,
|
|
467
|
+
(u(), v($, null, j(H, (s) => r(l(ke), {
|
|
467
468
|
key: s.value,
|
|
468
469
|
name: s.value,
|
|
469
470
|
tab: l(a)(`aiUsage.groupBy.${s.labelKey}`),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index2.vue.js","sources":["../../../../src/views/system/ai-usage/index.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// AI 用量统计 = 顶部统一筛选(日期/厂商/模型/场景)+ 四格指标 + 分组维度 Tab/表 + 趋势图 + 明细表。\n// 筛选只在 filterParams 这一处收口,四个区块都从它派生,避免各自拼参数导致竞态或口径不一致\n// (SmartTable 走 :params 自动联动,summary/trend 走 watch(filterParams, ...) 手动重拉 + 请求号防竞态)。\n// 本页只有一颗查询权限(GET:/api/v1/sys/ai/usage/summary),菜单可见性已由后端权限树控管,不需要 v-auth。\n// 版式:块顺序为 筛选 → 指标 → 趋势 → 分组 Tab → 明细;块间距 8px、指标格带图标底块、四格列数按内容区宽度分档(容器查询,窄 1 / 中 2 / 宽 4),\n// 分组表与明细表都是 SmartTable(分组表走静态数据模式),窄档都用内置的 card-on-narrow 卡片列表。图表用 ECharts(LineChart)。\nimport { computed, h, onMounted, ref, watch } from 'vue'\nimport {\n NCard,\n NSkeleton,\n NNumberAnimation,\n NDatePicker,\n NSelect,\n NInput,\n NButton,\n NTabs,\n NTabPane,\n NProgress,\n NTag,\n useMessage,\n} from 'naive-ui'\nimport { useI18n } from 'vue-i18n'\nimport { SmartTable, type SmartTableColumn } from 'smart-naive-table'\nimport AppIcon from '#/components/AppIcon.vue'\nimport LineChart from '#/components/Chart/LineChart.vue'\nimport { aiUsageApi, aiProviderApi } from '#/api'\nimport {\n AI_USAGE_GROUP_BY,\n AI_USAGE_SOURCE,\n type AiUsageGroupBy,\n type AiUsageGroupRow,\n type AiUsageSummary,\n type AiUsageTrendPoint,\n type SysAiUsageLog,\n} from '#/types/api'\nimport { translateError } from '#/utils/error'\nimport { fmtDateTime } from '#/utils/format'\nimport { SEARCH_ACTIONS, deriveHeaderFilters } from '#/utils/tableFilter'\nimport { TABLE_TOOLBAR } from '#/utils/tableToolbar'\nimport TableTotal from '#/components/TableTotal/index.vue'\n\nconst { t } = useI18n()\nconst message = useMessage()\n\n// ── 日期范围:三个快捷按钮直接设值(shortcuts prop 类型较绕,按钮法更直白),默认近 7 天 ──\nfunction fmtDate(d: Date): string {\n const y = d.getFullYear()\n const m = String(d.getMonth() + 1).padStart(2, '0')\n const day = String(d.getDate()).padStart(2, '0')\n return `${y}-${m}-${day}`\n}\nfunction daysAgo(n: number): Date {\n const d = new Date()\n d.setDate(d.getDate() - n)\n return d\n}\nfunction quickRangeValue(days: number): [string, string] {\n return [fmtDate(daysAgo(days - 1)), fmtDate(daysAgo(0))]\n}\n\nconst dateRange = ref<[string, string]>(quickRangeValue(7))\nconst activeQuickRange = ref<number | null>(7)\n\nfunction setQuickRange(days: number) {\n dateRange.value = quickRangeValue(days)\n activeQuickRange.value = days\n}\n// 不加 clearable:没有清空按钮,formatted-value 就不会回传 null,ref 全程非空,少一层判空。\nfunction onDateRangeUpdate(v: [string, string] | null) {\n if (!v) return\n dateRange.value = v\n activeQuickRange.value = null // 手动挑日期后取消快捷按钮高亮\n}\n\n// ── 厂商 / 模型 / 场景筛选 ──\n// 厂商:只读消费 aiProviderApi 拉一页选项(不改厂商模块任何文件)。模型/场景后端无枚举接口,\n// 模型做成 filterable+tag 的自由输入 select(参考 menu/MenuFormModal 的 component 字段写法),\n// 场景干脆用文本框模糊过滤——两者都是自由字符串,不是字典。\nconst providerCode = ref<string | null>(null)\nconst model = ref<string | null>(null)\nconst scene = ref<string | null>(null)\n\nconst providerOptions = ref<{ label: string; value: string }[]>([])\nonMounted(async () => {\n try {\n const { items } = await aiProviderApi.page({ page: 1, pageSize: 100 })\n providerOptions.value = items.map(p => ({ label: p.name, value: p.code }))\n } catch {\n // 静默:厂商下拉是筛选辅助,拉取失败不打断本页其余区块\n }\n})\n\n/**\n * 统一收口的筛选参数——四格指标 / 分组表 / 趋势图 / 明细表都从这一份派生,不各自拼 from/to,\n * 避免四处逻辑不一致或漏改。结束日补到 23:59:59(同 job-log/操作日志页 daterange 的规矩),\n * 否则\"筛到今天\"会把今天已发生的调用漏掉(纯日期串等价于当天 00:00:00)。\n */\nconst filterParams = computed(() => {\n const p: {\n from: string\n to: string\n providerCode?: string\n model?: string\n scene?: string\n } = {\n from: `${dateRange.value[0]}T00:00:00`,\n to: `${dateRange.value[1]}T23:59:59`,\n }\n if (providerCode.value) p.providerCode = providerCode.value\n if (model.value) p.model = model.value\n if (scene.value) p.scene = scene.value\n return p\n})\n\n// ── 分组维度 Tab ──\nconst groupTabs: { value: AiUsageGroupBy; labelKey: 'provider' | 'model' | 'scene' | 'user' }[] = [\n { value: AI_USAGE_GROUP_BY.Provider, labelKey: 'provider' },\n { value: AI_USAGE_GROUP_BY.Model, labelKey: 'model' },\n { value: AI_USAGE_GROUP_BY.Scene, labelKey: 'scene' },\n { value: AI_USAGE_GROUP_BY.User, labelKey: 'user' },\n]\nconst activeGroupBy = ref<AiUsageGroupBy>(AI_USAGE_GROUP_BY.Provider)\n\n// ── summary(四格指标 totals + 当前维度 groups,同一次请求共用)──\nconst summary = ref<AiUsageSummary | null>(null)\nconst summaryLoading = ref(true)\nlet summaryReq = 0\nasync function fetchSummary() {\n const reqId = ++summaryReq\n summaryLoading.value = true\n try {\n const res = await aiUsageApi.summary({ ...filterParams.value, groupBy: activeGroupBy.value })\n if (reqId !== summaryReq) return // 旧请求的响应,丢弃(防竞态:快速切筛选/切 Tab 时先发的可能后回)\n summary.value = res\n } catch (e) {\n if (reqId !== summaryReq) return\n message.error(translateError(e))\n } finally {\n if (reqId === summaryReq) summaryLoading.value = false\n }\n}\nwatch([filterParams, activeGroupBy], fetchSummary, { immediate: true })\n\nconst totals = computed(() => summary.value?.totals ?? null)\nconst groups = computed(() => summary.value?.groups ?? [])\n\n/**\n * 平均耗时卡片的算法选择:后端 AiUsageTotals 没有整体的 avgLatencyMs 字段,只有当前 groupBy\n * 维度下每组的 avgLatencyMs。这里按各组 callCount 加权平均还原总体平均耗时——分组维度对区间内\n * 全部调用做了完整划分(每次调用恰好落在一个分组里,无重叠无遗漏),所以\n * sum(avgLatencyMs_i * callCount_i) / sum(callCount_i) 就精确等于对全部原始调用耗时取平均,\n * 不是近似值(除各组均值本身的取整/浮点误差外)。选它而不是\"失败率\"是因为卡片标题已经明确是\n * aiUsage.metric.avgLatency(平均耗时),不宜文不对题;比另开一个后端聚合字段/接口更省事,\n * 也顺带让这张卡片跟着 groupBy 切换、筛选变化实时更新,不需要额外请求。\n */\nconst avgLatencyMs = computed(() => {\n const totalCalls = groups.value.reduce((sum, g) => sum + g.callCount, 0)\n if (!totalCalls) return null\n const weighted = groups.value.reduce((sum, g) => sum + g.avgLatencyMs * g.callCount, 0)\n return weighted / totalCalls\n})\n\nconst metrics = computed(() => [\n {\n key: 'totalTokens',\n icon: 'ph:coins',\n value: totals.value?.totalTokens ?? 0,\n empty: !totals.value,\n },\n {\n key: 'totalCalls',\n icon: 'ph:lightning',\n value: totals.value?.callCount ?? 0,\n empty: !totals.value,\n },\n {\n key: 'failureCount',\n icon: 'ph:warning-circle',\n value: totals.value?.failureCount ?? 0,\n empty: !totals.value,\n },\n {\n key: 'avgLatency',\n icon: 'ph:timer',\n value: avgLatencyMs.value != null ? Math.round(avgLatencyMs.value) : 0,\n suffix: 'ms',\n empty: avgLatencyMs.value == null,\n },\n])\n\n// ── 分组表(四个 Tab 共用同一张表,只是绑定的 groups/列标题随 activeGroupBy 变)──\nconst groupDimensionLabel = computed(() => {\n const tab = groupTabs.find(g => g.value === activeGroupBy.value)\n return t(`aiUsage.groupBy.${tab?.labelKey ?? 'provider'}`)\n})\n\nconst numCell = (n: number) => h('span', { class: 'num' }, n.toLocaleString())\n\nconst groupColumns = computed<SmartTableColumn<AiUsageGroupRow>[]>(() => [\n {\n title: () => groupDimensionLabel.value,\n key: 'label',\n minWidth: 160,\n search: {}, // 静态数据:条件由 SmartTable 前端求值\n // 厂商 / 模型 / 场景是编码类文本,等宽;用户维度是姓名,保持正文\n render: r =>\n h(\n 'span',\n { class: activeGroupBy.value === AI_USAGE_GROUP_BY.User ? '' : 'mono' },\n r.label ?? r.key,\n ),\n },\n {\n title: () => t('aiUsage.trend.inputTokens'),\n key: 'inputTokens',\n width: 120,\n align: 'right',\n render: r => numCell(r.inputTokens),\n },\n {\n title: () => t('aiUsage.trend.outputTokens'),\n key: 'outputTokens',\n width: 120,\n align: 'right',\n render: r => numCell(r.outputTokens),\n },\n {\n title: () => t('aiUsage.metric.totalCalls'),\n key: 'callCount',\n width: 110,\n align: 'right',\n render: r => numCell(r.callCount),\n },\n {\n title: () => t('aiUsage.table.latency'),\n key: 'avgLatencyMs',\n width: 100,\n align: 'right',\n render: r => h('span', { class: 'num' }, `${Math.round(r.avgLatencyMs)} ms`),\n },\n {\n title: () => t('aiUsage.share'),\n key: 'sharePercent',\n minWidth: 200,\n render: r =>\n h('div', { class: 'share-cell' }, [\n h('div', { class: 'share-bar' }, [\n h(NProgress, {\n type: 'line',\n percentage: Math.round(r.sharePercent),\n showIndicator: false,\n height: 6,\n }),\n ]),\n h('span', { class: 'share-text num muted' }, `${r.sharePercent.toFixed(1)}%`),\n ]),\n },\n])\n\n// ── 趋势图 ──\nconst trendPoints = ref<AiUsageTrendPoint[]>([])\nconst trendLoading = ref(true)\nlet trendReq = 0\nasync function fetchTrend() {\n const reqId = ++trendReq\n trendLoading.value = true\n try {\n const res = await aiUsageApi.trend(filterParams.value)\n if (reqId !== trendReq) return\n trendPoints.value = res\n } catch (e) {\n if (reqId !== trendReq) return\n message.error(translateError(e))\n } finally {\n if (reqId === trendReq) trendLoading.value = false\n }\n}\nwatch(filterParams, fetchTrend, { immediate: true })\n\nconst trendCategories = computed(() => trendPoints.value.map(p => p.date))\nconst trendSeries = computed(() => [\n { name: t('aiUsage.trend.inputTokens'), data: trendPoints.value.map(p => p.inputTokens) },\n { name: t('aiUsage.trend.outputTokens'), data: trendPoints.value.map(p => p.outputTokens) },\n])\n\n// ── 明细表(SmartTable;日期/厂商/模型/场景已在页面顶部统一做并经 :params 联动,\n// 内置条件搜索只放顶部没有的「结果」,避免同一个条件两处入口)──\nconst detailColumns: SmartTableColumn<SysAiUsageLog>[] = [\n {\n key: 'createTime',\n title: () => t('aiUsage.table.createTime'),\n width: 170,\n render: r =>\n h('span', { class: 'num muted', style: 'white-space: nowrap' }, fmtDateTime(r.createTime)),\n },\n {\n key: 'providerCode',\n title: () => t('aiUsage.table.provider'),\n width: 120,\n render: r => h('span', { class: 'mono' }, r.providerCode),\n },\n {\n key: 'model',\n title: () => t('aiUsage.table.model'),\n width: 140,\n ellipsis: { tooltip: true },\n render: r => h('span', { class: 'mono' }, r.model),\n },\n {\n key: 'scene',\n title: () => t('aiUsage.table.scene'),\n width: 120,\n render: r => h('span', { class: 'mono muted' }, r.scene || '—'),\n },\n {\n key: 'userId',\n title: () => t('aiUsage.table.user'),\n width: 90,\n render: r => h('span', { class: 'num' }, r.userId != null ? String(r.userId) : '—'),\n },\n {\n key: 'inputTokens',\n title: () => t('aiUsage.table.inputTokens'),\n width: 110,\n align: 'right',\n render: r => numCell(r.inputTokens),\n },\n {\n key: 'outputTokens',\n title: () => t('aiUsage.table.outputTokens'),\n width: 110,\n align: 'right',\n render: r => numCell(r.outputTokens),\n },\n {\n key: 'totalTokens',\n title: () => t('aiUsage.table.totalTokens'),\n width: 110,\n align: 'right',\n render: r => numCell(r.totalTokens),\n },\n {\n key: 'latencyMs',\n title: () => t('aiUsage.table.latency'),\n width: 100,\n align: 'right',\n render: r => h('span', { class: 'num' }, `${r.latencyMs.toLocaleString()} ms`),\n },\n {\n key: 'success',\n title: () => t('aiUsage.table.status'),\n width: 90,\n // options 只给搜索控件当下拉选项(单元格仍走下面的 render);后端 Success == 精确匹配,所以只开「等于」\n options: [\n { label: () => t('aiUsage.table.success'), value: true },\n { label: () => t('aiUsage.table.failed'), value: false },\n ],\n search: { actions: SEARCH_ACTIONS.exact },\n // 失败行用原生 title 兜底展示 errorMessage,不必为一个只读明细表再引入 NTooltip\n render: r =>\n h('span', { title: !r.success && r.errorMessage ? r.errorMessage : undefined }, [\n h(NTag, { size: 'small', bordered: false, type: r.success ? 'success' : 'error' }, () =>\n r.success ? t('aiUsage.table.success') : t('aiUsage.table.failed'),\n ),\n ]),\n },\n {\n key: 'streamed',\n title: () => t('aiUsage.table.streamed'),\n width: 80,\n render: r =>\n h(NTag, { size: 'small', bordered: false }, () =>\n r.streamed ? t('common.yes') : t('common.no'),\n ),\n },\n {\n key: 'usageSource',\n title: () => t('aiUsage.table.usageSource'),\n width: 110,\n render: r =>\n r.usageSource === AI_USAGE_SOURCE.Reported\n ? t('aiUsage.table.usageSourceReported')\n : t('aiUsage.table.usageSourceMissing'),\n },\n]\nderiveHeaderFilters(detailColumns)\n</script>\n\n<template>\n <div class=\"view\">\n <!-- 筛选区:日期范围 + 快捷按钮 + 厂商/模型/场景,变化后经 filterParams 联动下面四块 -->\n <n-card class=\"a-card\" size=\"small\" :bordered=\"true\" content-style=\"padding: 12px 14px\">\n <div class=\"filter-bar\">\n <div class=\"filter-item filter-item--range\">\n <span class=\"filter-label faint\">{{ t('aiUsage.filter.dateRange') }}</span>\n <div class=\"date-filter-row\">\n <n-date-picker\n :formatted-value=\"dateRange\"\n type=\"daterange\"\n value-format=\"yyyy-MM-dd\"\n class=\"date-picker\"\n @update:formatted-value=\"onDateRangeUpdate\"\n />\n <div class=\"quick-range\">\n <n-button\n size=\"small\"\n quaternary\n :type=\"activeQuickRange === 1 ? 'primary' : 'default'\"\n @click=\"setQuickRange(1)\"\n >\n {{ t('aiUsage.filter.today') }}\n </n-button>\n <n-button\n size=\"small\"\n quaternary\n :type=\"activeQuickRange === 7 ? 'primary' : 'default'\"\n @click=\"setQuickRange(7)\"\n >\n {{ t('aiUsage.filter.last7Days') }}\n </n-button>\n <n-button\n size=\"small\"\n quaternary\n :type=\"activeQuickRange === 30 ? 'primary' : 'default'\"\n @click=\"setQuickRange(30)\"\n >\n {{ t('aiUsage.filter.last30Days') }}\n </n-button>\n </div>\n </div>\n </div>\n <div class=\"filter-item\">\n <span class=\"filter-label faint\">{{ t('aiUsage.filter.provider') }}</span>\n <n-select\n v-model:value=\"providerCode\"\n :options=\"providerOptions\"\n clearable\n filterable\n class=\"filter-ctl\"\n :placeholder=\"t('aiUsage.filter.providerAll')\"\n />\n </div>\n <div class=\"filter-item\">\n <span class=\"filter-label faint\">{{ t('aiUsage.filter.model') }}</span>\n <n-select\n v-model:value=\"model\"\n :options=\"[]\"\n filterable\n tag\n clearable\n class=\"filter-ctl\"\n :placeholder=\"t('aiUsage.filter.modelAll')\"\n />\n </div>\n <div class=\"filter-item\">\n <span class=\"filter-label faint\">{{ t('aiUsage.filter.scene') }}</span>\n <n-input\n v-model:value=\"scene\"\n clearable\n class=\"filter-ctl\"\n :placeholder=\"t('aiUsage.filter.scenePlaceholder')\"\n />\n </div>\n </div>\n </n-card>\n\n <!-- 四格指标:totals 与分组表共用同一次 summary 请求,不重复请求 -->\n <div class=\"metrics\">\n <n-card\n v-for=\"m in metrics\"\n :key=\"m.key\"\n class=\"a-card\"\n size=\"small\"\n :bordered=\"true\"\n content-style=\"padding: 12px 14px\"\n >\n <div class=\"metric-body\">\n <div class=\"metric-main\">\n <n-skeleton v-if=\"summaryLoading\" text width=\"72px\" height=\"34px\" />\n <div v-else class=\"metric-val tabular\">\n <template v-if=\"m.empty\">—</template>\n <template v-else>\n <n-number-animation :from=\"0\" :to=\"m.value\" show-separator />\n <small v-if=\"m.suffix\" class=\"metric-suffix\">{{ ' ' + m.suffix }}</small>\n </template>\n </div>\n <div class=\"metric-label muted\">{{ t(`aiUsage.metric.${m.key}`) }}</div>\n </div>\n <div class=\"metric-icon\" aria-hidden=\"true\"><AppIcon :icon=\"m.icon\" :size=\"15\" /></div>\n </div>\n </n-card>\n </div>\n\n <!-- 趋势图:区间内无数据的日期后端已补 0,直接画连续折线 -->\n <n-card\n :title=\"t('aiUsage.trend.title')\"\n class=\"a-card\"\n size=\"small\"\n :bordered=\"true\"\n content-style=\"padding: 8px 14px 12px\"\n >\n <n-skeleton v-if=\"trendLoading\" height=\"320px\" :sharp=\"false\" />\n <LineChart v-else :categories=\"trendCategories\" :series=\"trendSeries\" />\n </n-card>\n\n <!-- 分组维度 Tab + 分组表:切 Tab 换 groupBy 重新拉 summary;窄档换成卡片列表 -->\n <n-card class=\"a-card\" size=\"small\" :bordered=\"true\" content-style=\"padding: 4px 14px 12px\">\n <n-tabs v-model:value=\"activeGroupBy\" type=\"line\" animated>\n <n-tab-pane\n v-for=\"tab in groupTabs\"\n :key=\"tab.value\"\n :name=\"tab.value\"\n :tab=\"t(`aiUsage.groupBy.${tab.labelKey}`)\"\n display-directive=\"show:lazy\"\n >\n <SmartTable\n :columns=\"groupColumns\"\n :data=\"groups\"\n row-key=\"key\"\n :loading=\"summaryLoading\"\n :search=\"{ container: 'table' }\"\n :toolbar=\"TABLE_TOOLBAR\"\n :card-props=\"{ bordered: false }\"\n :scroll-x=\"720\"\n card-on-narrow\n storage-key=\"sys-ai-usage-group\"\n >\n <template #pagination-prefix=\"{ itemCount }\">\n <TableTotal :count=\"itemCount\" />\n </template>\n </SmartTable>\n </n-tab-pane>\n </n-tabs>\n </n-card>\n\n <!-- 明细表:顶部筛选走 :params(与条件构造器的 success 键不冲突),「结果」走内置条件搜索 -->\n <SmartTable\n :columns=\"detailColumns\"\n :fetcher=\"aiUsageApi.page\"\n :params=\"filterParams\"\n :search=\"{ container: 'table' }\"\n :toolbar=\"TABLE_TOOLBAR\"\n :default-page-size=\"20\"\n card-on-narrow\n storage-key=\"sys-ai-usage\"\n @error=\"(e: unknown) => message.error(translateError(e))\"\n >\n <template #pagination-prefix=\"{ itemCount }\">\n <TableTotal :count=\"itemCount\" />\n </template>\n </SmartTable>\n </div>\n</template>\n\n<style scoped>\n/* 容器查询的容器:四格指标的列数按内容区宽度分档(窄 1 / 中 2 / 宽 4),不看视口 */\n.view {\n display: flex;\n flex-direction: column;\n gap: var(--gap-card, 8px);\n container-type: inline-size;\n}\n.filter-bar {\n display: flex;\n flex-wrap: wrap;\n align-items: flex-end;\n gap: 12px 20px;\n}\n.filter-item {\n display: flex;\n flex-direction: column;\n gap: 6px;\n min-width: 0;\n}\n.filter-label {\n font-size: 13px;\n}\n.date-filter-row {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 8px;\n}\n.date-picker {\n width: 290px;\n}\n.filter-ctl {\n width: 180px;\n}\n.quick-range {\n display: flex;\n gap: 4px;\n}\n/* 窄档:每个筛选项独占一行,控件撑满 */\n@container (max-width: 603px) {\n .filter-item {\n flex: 1 1 100%;\n }\n .date-picker,\n .filter-ctl {\n width: 100%;\n }\n .date-picker {\n flex: 1 1 100%;\n }\n}\n.metrics {\n display: grid;\n gap: 8px;\n grid-template-columns: minmax(0, 1fr);\n}\n@container (min-width: 480px) {\n .metrics {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n }\n}\n@container (min-width: 1000px) {\n .metrics {\n grid-template-columns: repeat(4, minmax(0, 1fr));\n }\n}\n.metric-body {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 8px;\n}\n.metric-main {\n min-width: 0;\n}\n.metric-val {\n font-size: 28px;\n font-weight: 600;\n letter-spacing: -0.04em;\n line-height: 1;\n color: var(--text-1);\n white-space: nowrap;\n}\n.metric-suffix {\n margin-left: 4px;\n font-size: 15px;\n letter-spacing: 0;\n color: var(--text-3);\n}\n.metric-label {\n margin-top: 6px;\n font-size: 14px;\n}\n/* 指标图标底块:中性玻璃小方块 */\n.metric-icon {\n flex: none;\n display: grid;\n place-items: center;\n width: 28px;\n height: 28px;\n border-radius: var(--radius-md);\n color: var(--text-1);\n background: var(--glass-strong);\n box-shadow: inset 0 0 0 1px var(--hairline);\n}\n.share-cell {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n.share-bar {\n flex: 1;\n min-width: 60px;\n}\n.share-text {\n flex: none;\n width: 58px;\n text-align: right;\n font-size: 13px;\n white-space: nowrap;\n}\n</style>\n"],"names":["t","useI18n","message","useMessage","fmtDate","d","y","m","day","daysAgo","n","quickRangeValue","days","dateRange","ref","activeQuickRange","setQuickRange","onDateRangeUpdate","v","providerCode","model","scene","providerOptions","onMounted","items","aiProviderApi","p","filterParams","computed","groupTabs","AI_USAGE_GROUP_BY","activeGroupBy","summary","summaryLoading","summaryReq","fetchSummary","reqId","res","aiUsageApi","e","translateError","watch","totals","_a","groups","avgLatencyMs","totalCalls","sum","g","metrics","_b","_c","groupDimensionLabel","tab","numCell","h","groupColumns","r","NProgress","trendPoints","trendLoading","trendReq","fetchTrend","trendCategories","trendSeries","detailColumns","fmtDateTime","SEARCH_ACTIONS","NTag","AI_USAGE_SOURCE","deriveHeaderFilters","_openBlock","_createElementBlock","_hoisted_1","_createVNode","_unref","NCard","_createElementVNode","_hoisted_2","_hoisted_3","_hoisted_4","_toDisplayString","_hoisted_5","NDatePicker","_hoisted_6","NButton","_hoisted_7","_hoisted_8","NSelect","$event","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","NInput","_hoisted_13","_Fragment","_renderList","_createBlock","_hoisted_14","_hoisted_15","NSkeleton","_hoisted_16","NNumberAnimation","_hoisted_17","_hoisted_18","_hoisted_19","AppIcon","LineChart","NTabs","NTabPane","SmartTable","TABLE_TOOLBAR","_withCtx","itemCount","TableTotal","_cache"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index2.vue.js","sources":["../../../../src/views/system/ai-usage/index.vue"],"sourcesContent":["<script setup lang=\"ts\">\n// AI 用量统计 = 顶部统一筛选(日期/厂商/模型/场景)+ 四格指标 + 分组维度 Tab/表 + 趋势图 + 明细表。\n// 筛选只在 filterParams 这一处收口,四个区块都从它派生,避免各自拼参数导致竞态或口径不一致\n// (SmartTable 走 :params 自动联动,summary/trend 走 watch(filterParams, ...) 手动重拉 + 请求号防竞态)。\n// 本页只有一颗查询权限(GET:/api/v1/sys/ai/usage/summary),菜单可见性已由后端权限树控管,不需要 v-auth。\n// 版式:块顺序为 筛选 → 指标 → 趋势 → 分组 Tab → 明细;块间距 8px、指标格带图标底块、四格列数按内容区宽度分档(容器查询,窄 1 / 中 2 / 宽 4),\n// 分组表与明细表都是 SmartTable(分组表走静态数据模式),窄档都用内置的 card-on-narrow 卡片列表。图表用 ECharts(LineChart)。\nimport { computed, h, onMounted, ref, watch } from 'vue'\nimport {\n NCard,\n NSkeleton,\n NNumberAnimation,\n NDatePicker,\n NSelect,\n NInput,\n NButton,\n NTabs,\n NTabPane,\n NProgress,\n NTag,\n useMessage,\n} from 'naive-ui'\nimport { useI18n } from 'vue-i18n'\nimport { SmartTable, type SmartTableColumn } from 'smart-naive-table'\nimport AppIcon from '#/components/AppIcon.vue'\nimport LineChart from '#/components/Chart/LineChart.vue'\nimport { aiUsageApi, aiProviderApi } from '#/api'\nimport {\n AI_USAGE_GROUP_BY,\n AI_USAGE_SOURCE,\n type AiUsageGroupBy,\n type AiUsageGroupRow,\n type AiUsageSummary,\n type AiUsageTrendPoint,\n type SysAiUsageLog,\n} from '#/types/api'\nimport { translateError } from '#/utils/error'\nimport { fmtDateTime } from '#/utils/format'\nimport { SEARCH_ACTIONS, deriveHeaderFilters } from '#/utils/tableFilter'\nimport { TABLE_TOOLBAR } from '#/utils/tableToolbar'\nimport TableTotal from '#/components/TableTotal/index.vue'\n\nconst { t } = useI18n()\nconst message = useMessage()\n\n// ── 日期范围:三个快捷按钮直接设值(shortcuts prop 类型较绕,按钮法更直白),默认近 7 天 ──\nfunction fmtDate(d: Date): string {\n const y = d.getFullYear()\n const m = String(d.getMonth() + 1).padStart(2, '0')\n const day = String(d.getDate()).padStart(2, '0')\n return `${y}-${m}-${day}`\n}\nfunction daysAgo(n: number): Date {\n const d = new Date()\n d.setDate(d.getDate() - n)\n return d\n}\nfunction quickRangeValue(days: number): [string, string] {\n return [fmtDate(daysAgo(days - 1)), fmtDate(daysAgo(0))]\n}\n\nconst dateRange = ref<[string, string]>(quickRangeValue(7))\nconst activeQuickRange = ref<number | null>(7)\n\nfunction setQuickRange(days: number) {\n dateRange.value = quickRangeValue(days)\n activeQuickRange.value = days\n}\n// 不加 clearable:没有清空按钮,formatted-value 就不会回传 null,ref 全程非空,少一层判空。\nfunction onDateRangeUpdate(v: [string, string] | null) {\n if (!v) return\n dateRange.value = v\n activeQuickRange.value = null // 手动挑日期后取消快捷按钮高亮\n}\n\n// ── 厂商 / 模型 / 场景筛选 ──\n// 厂商:只读消费 aiProviderApi 拉一页选项(不改厂商模块任何文件)。模型/场景后端无枚举接口,\n// 模型做成 filterable+tag 的自由输入 select(参考 menu/MenuFormModal 的 component 字段写法),\n// 场景干脆用文本框模糊过滤——两者都是自由字符串,不是字典。\nconst providerCode = ref<string | null>(null)\nconst model = ref<string | null>(null)\nconst scene = ref<string | null>(null)\n\nconst providerOptions = ref<{ label: string; value: string }[]>([])\nonMounted(async () => {\n try {\n const { items } = await aiProviderApi.page({ page: 1, pageSize: 100 })\n providerOptions.value = items.map(p => ({ label: p.name, value: p.code }))\n } catch {\n // 静默:厂商下拉是筛选辅助,拉取失败不打断本页其余区块\n }\n})\n\n/**\n * 统一收口的筛选参数——四格指标 / 分组表 / 趋势图 / 明细表都从这一份派生,不各自拼 from/to,\n * 避免四处逻辑不一致或漏改。结束日补到 23:59:59(同 job-log/操作日志页 daterange 的规矩),\n * 否则\"筛到今天\"会把今天已发生的调用漏掉(纯日期串等价于当天 00:00:00)。\n */\nconst filterParams = computed(() => {\n const p: {\n from: string\n to: string\n providerCode?: string\n model?: string\n scene?: string\n } = {\n from: `${dateRange.value[0]}T00:00:00`,\n to: `${dateRange.value[1]}T23:59:59`,\n }\n if (providerCode.value) p.providerCode = providerCode.value\n if (model.value) p.model = model.value\n if (scene.value) p.scene = scene.value\n return p\n})\n\n// ── 分组维度 Tab ──\nconst groupTabs: { value: AiUsageGroupBy; labelKey: 'provider' | 'model' | 'scene' | 'user' }[] = [\n { value: AI_USAGE_GROUP_BY.Provider, labelKey: 'provider' },\n { value: AI_USAGE_GROUP_BY.Model, labelKey: 'model' },\n { value: AI_USAGE_GROUP_BY.Scene, labelKey: 'scene' },\n { value: AI_USAGE_GROUP_BY.User, labelKey: 'user' },\n]\nconst activeGroupBy = ref<AiUsageGroupBy>(AI_USAGE_GROUP_BY.Provider)\n\n// ── summary(四格指标 totals + 当前维度 groups,同一次请求共用)──\nconst summary = ref<AiUsageSummary | null>(null)\nconst summaryLoading = ref(true)\nlet summaryReq = 0\nasync function fetchSummary() {\n const reqId = ++summaryReq\n summaryLoading.value = true\n try {\n const res = await aiUsageApi.summary({ ...filterParams.value, groupBy: activeGroupBy.value })\n if (reqId !== summaryReq) return // 旧请求的响应,丢弃(防竞态:快速切筛选/切 Tab 时先发的可能后回)\n summary.value = res\n } catch (e) {\n if (reqId !== summaryReq) return\n message.error(translateError(e))\n } finally {\n if (reqId === summaryReq) summaryLoading.value = false\n }\n}\nwatch([filterParams, activeGroupBy], fetchSummary, { immediate: true })\n\nconst totals = computed(() => summary.value?.totals ?? null)\nconst groups = computed(() => summary.value?.groups ?? [])\n\n/**\n * 平均耗时卡片的算法选择:后端 AiUsageTotals 没有整体的 avgLatencyMs 字段,只有当前 groupBy\n * 维度下每组的 avgLatencyMs。这里按各组 callCount 加权平均还原总体平均耗时——分组维度对区间内\n * 全部调用做了完整划分(每次调用恰好落在一个分组里,无重叠无遗漏),所以\n * sum(avgLatencyMs_i * callCount_i) / sum(callCount_i) 就精确等于对全部原始调用耗时取平均,\n * 不是近似值(除各组均值本身的取整/浮点误差外)。选它而不是\"失败率\"是因为卡片标题已经明确是\n * aiUsage.metric.avgLatency(平均耗时),不宜文不对题;比另开一个后端聚合字段/接口更省事,\n * 也顺带让这张卡片跟着 groupBy 切换、筛选变化实时更新,不需要额外请求。\n */\nconst avgLatencyMs = computed(() => {\n const totalCalls = groups.value.reduce((sum, g) => sum + g.callCount, 0)\n if (!totalCalls) return null\n const weighted = groups.value.reduce((sum, g) => sum + g.avgLatencyMs * g.callCount, 0)\n return weighted / totalCalls\n})\n\nconst metrics = computed(() => [\n {\n key: 'totalTokens',\n icon: 'ph:coins',\n value: totals.value?.totalTokens ?? 0,\n empty: !totals.value,\n },\n {\n key: 'totalCalls',\n icon: 'ph:lightning',\n value: totals.value?.callCount ?? 0,\n empty: !totals.value,\n },\n {\n key: 'failureCount',\n icon: 'ph:warning-circle',\n value: totals.value?.failureCount ?? 0,\n empty: !totals.value,\n },\n {\n key: 'avgLatency',\n icon: 'ph:timer',\n value: avgLatencyMs.value != null ? Math.round(avgLatencyMs.value) : 0,\n suffix: 'ms',\n empty: avgLatencyMs.value == null,\n },\n])\n\n// ── 分组表(四个 Tab 共用同一张表,只是绑定的 groups/列标题随 activeGroupBy 变)──\nconst groupDimensionLabel = computed(() => {\n const tab = groupTabs.find(g => g.value === activeGroupBy.value)\n return t(`aiUsage.groupBy.${tab?.labelKey ?? 'provider'}`)\n})\n\nconst numCell = (n: number) => h('span', { class: 'num' }, n.toLocaleString())\n\nconst groupColumns = computed<SmartTableColumn<AiUsageGroupRow>[]>(() => [\n {\n title: () => groupDimensionLabel.value,\n key: 'label',\n width: 160,\n ellipsis: { tooltip: true },\n search: {}, // 静态数据:条件由 SmartTable 前端求值\n // 厂商 / 模型 / 场景是编码类文本,等宽;用户维度是姓名,保持正文\n render: r =>\n h(\n 'span',\n { class: activeGroupBy.value === AI_USAGE_GROUP_BY.User ? '' : 'mono' },\n r.label ?? r.key,\n ),\n },\n {\n title: () => t('aiUsage.trend.inputTokens'),\n key: 'inputTokens',\n width: 120,\n align: 'right',\n render: r => numCell(r.inputTokens),\n },\n {\n title: () => t('aiUsage.trend.outputTokens'),\n key: 'outputTokens',\n width: 120,\n align: 'right',\n render: r => numCell(r.outputTokens),\n },\n {\n title: () => t('aiUsage.metric.totalCalls'),\n key: 'callCount',\n width: 110,\n align: 'right',\n render: r => numCell(r.callCount),\n },\n {\n title: () => t('aiUsage.table.latency'),\n key: 'avgLatencyMs',\n width: 100,\n align: 'right',\n render: r => h('span', { class: 'num' }, `${Math.round(r.avgLatencyMs)} ms`),\n },\n {\n title: () => t('aiUsage.share'),\n key: 'sharePercent',\n width: 200,\n render: r =>\n h('div', { class: 'share-cell' }, [\n h('div', { class: 'share-bar' }, [\n h(NProgress, {\n type: 'line',\n percentage: Math.round(r.sharePercent),\n showIndicator: false,\n height: 6,\n }),\n ]),\n h('span', { class: 'share-text num muted' }, `${r.sharePercent.toFixed(1)}%`),\n ]),\n },\n])\n\n// ── 趋势图 ──\nconst trendPoints = ref<AiUsageTrendPoint[]>([])\nconst trendLoading = ref(true)\nlet trendReq = 0\nasync function fetchTrend() {\n const reqId = ++trendReq\n trendLoading.value = true\n try {\n const res = await aiUsageApi.trend(filterParams.value)\n if (reqId !== trendReq) return\n trendPoints.value = res\n } catch (e) {\n if (reqId !== trendReq) return\n message.error(translateError(e))\n } finally {\n if (reqId === trendReq) trendLoading.value = false\n }\n}\nwatch(filterParams, fetchTrend, { immediate: true })\n\nconst trendCategories = computed(() => trendPoints.value.map(p => p.date))\nconst trendSeries = computed(() => [\n { name: t('aiUsage.trend.inputTokens'), data: trendPoints.value.map(p => p.inputTokens) },\n { name: t('aiUsage.trend.outputTokens'), data: trendPoints.value.map(p => p.outputTokens) },\n])\n\n// ── 明细表(SmartTable;日期/厂商/模型/场景已在页面顶部统一做并经 :params 联动,\n// 内置条件搜索只放顶部没有的「结果」,避免同一个条件两处入口)──\nconst detailColumns: SmartTableColumn<SysAiUsageLog>[] = [\n {\n key: 'createTime',\n title: () => t('aiUsage.table.createTime'),\n width: 170,\n render: r =>\n h('span', { class: 'num muted', style: 'white-space: nowrap' }, fmtDateTime(r.createTime)),\n },\n {\n key: 'providerCode',\n title: () => t('aiUsage.table.provider'),\n width: 120,\n render: r => h('span', { class: 'mono' }, r.providerCode),\n },\n {\n key: 'model',\n title: () => t('aiUsage.table.model'),\n width: 140,\n ellipsis: { tooltip: true },\n render: r => h('span', { class: 'mono' }, r.model),\n },\n {\n key: 'scene',\n title: () => t('aiUsage.table.scene'),\n width: 120,\n render: r => h('span', { class: 'mono muted' }, r.scene || '—'),\n },\n {\n key: 'userId',\n title: () => t('aiUsage.table.user'),\n width: 90,\n render: r => h('span', { class: 'num' }, r.userId != null ? String(r.userId) : '—'),\n },\n {\n key: 'inputTokens',\n title: () => t('aiUsage.table.inputTokens'),\n width: 110,\n align: 'right',\n render: r => numCell(r.inputTokens),\n },\n {\n key: 'outputTokens',\n title: () => t('aiUsage.table.outputTokens'),\n width: 110,\n align: 'right',\n render: r => numCell(r.outputTokens),\n },\n {\n key: 'totalTokens',\n title: () => t('aiUsage.table.totalTokens'),\n width: 110,\n align: 'right',\n render: r => numCell(r.totalTokens),\n },\n {\n key: 'latencyMs',\n title: () => t('aiUsage.table.latency'),\n width: 100,\n align: 'right',\n render: r => h('span', { class: 'num' }, `${r.latencyMs.toLocaleString()} ms`),\n },\n {\n key: 'success',\n title: () => t('aiUsage.table.status'),\n width: 90,\n // options 只给搜索控件当下拉选项(单元格仍走下面的 render);后端 Success == 精确匹配,所以只开「等于」\n options: [\n { label: () => t('aiUsage.table.success'), value: true },\n { label: () => t('aiUsage.table.failed'), value: false },\n ],\n search: { actions: SEARCH_ACTIONS.exact },\n // 失败行用原生 title 兜底展示 errorMessage,不必为一个只读明细表再引入 NTooltip\n render: r =>\n h('span', { title: !r.success && r.errorMessage ? r.errorMessage : undefined }, [\n h(NTag, { size: 'small', bordered: false, type: r.success ? 'success' : 'error' }, () =>\n r.success ? t('aiUsage.table.success') : t('aiUsage.table.failed'),\n ),\n ]),\n },\n {\n key: 'streamed',\n title: () => t('aiUsage.table.streamed'),\n width: 80,\n render: r =>\n h(NTag, { size: 'small', bordered: false }, () =>\n r.streamed ? t('common.yes') : t('common.no'),\n ),\n },\n {\n key: 'usageSource',\n title: () => t('aiUsage.table.usageSource'),\n width: 110,\n render: r =>\n r.usageSource === AI_USAGE_SOURCE.Reported\n ? t('aiUsage.table.usageSourceReported')\n : t('aiUsage.table.usageSourceMissing'),\n },\n]\nderiveHeaderFilters(detailColumns)\n</script>\n\n<template>\n <div class=\"view\">\n <!-- 筛选区:日期范围 + 快捷按钮 + 厂商/模型/场景,变化后经 filterParams 联动下面四块 -->\n <n-card class=\"a-card\" size=\"small\" :bordered=\"true\" content-style=\"padding: 12px 14px\">\n <div class=\"filter-bar\">\n <div class=\"filter-item filter-item--range\">\n <span class=\"filter-label faint\">{{ t('aiUsage.filter.dateRange') }}</span>\n <div class=\"date-filter-row\">\n <n-date-picker\n :formatted-value=\"dateRange\"\n type=\"daterange\"\n value-format=\"yyyy-MM-dd\"\n class=\"date-picker\"\n @update:formatted-value=\"onDateRangeUpdate\"\n />\n <div class=\"quick-range\">\n <n-button\n size=\"small\"\n quaternary\n :type=\"activeQuickRange === 1 ? 'primary' : 'default'\"\n @click=\"setQuickRange(1)\"\n >\n {{ t('aiUsage.filter.today') }}\n </n-button>\n <n-button\n size=\"small\"\n quaternary\n :type=\"activeQuickRange === 7 ? 'primary' : 'default'\"\n @click=\"setQuickRange(7)\"\n >\n {{ t('aiUsage.filter.last7Days') }}\n </n-button>\n <n-button\n size=\"small\"\n quaternary\n :type=\"activeQuickRange === 30 ? 'primary' : 'default'\"\n @click=\"setQuickRange(30)\"\n >\n {{ t('aiUsage.filter.last30Days') }}\n </n-button>\n </div>\n </div>\n </div>\n <div class=\"filter-item\">\n <span class=\"filter-label faint\">{{ t('aiUsage.filter.provider') }}</span>\n <n-select\n v-model:value=\"providerCode\"\n :options=\"providerOptions\"\n clearable\n filterable\n class=\"filter-ctl\"\n :placeholder=\"t('aiUsage.filter.providerAll')\"\n />\n </div>\n <div class=\"filter-item\">\n <span class=\"filter-label faint\">{{ t('aiUsage.filter.model') }}</span>\n <n-select\n v-model:value=\"model\"\n :options=\"[]\"\n filterable\n tag\n clearable\n class=\"filter-ctl\"\n :placeholder=\"t('aiUsage.filter.modelAll')\"\n />\n </div>\n <div class=\"filter-item\">\n <span class=\"filter-label faint\">{{ t('aiUsage.filter.scene') }}</span>\n <n-input\n v-model:value=\"scene\"\n clearable\n class=\"filter-ctl\"\n :placeholder=\"t('aiUsage.filter.scenePlaceholder')\"\n />\n </div>\n </div>\n </n-card>\n\n <!-- 四格指标:totals 与分组表共用同一次 summary 请求,不重复请求 -->\n <div class=\"metrics\">\n <n-card\n v-for=\"m in metrics\"\n :key=\"m.key\"\n class=\"a-card\"\n size=\"small\"\n :bordered=\"true\"\n content-style=\"padding: 12px 14px\"\n >\n <div class=\"metric-body\">\n <div class=\"metric-main\">\n <n-skeleton v-if=\"summaryLoading\" text width=\"72px\" height=\"34px\" />\n <div v-else class=\"metric-val tabular\">\n <template v-if=\"m.empty\">—</template>\n <template v-else>\n <n-number-animation :from=\"0\" :to=\"m.value\" show-separator />\n <small v-if=\"m.suffix\" class=\"metric-suffix\">{{ ' ' + m.suffix }}</small>\n </template>\n </div>\n <div class=\"metric-label muted\">{{ t(`aiUsage.metric.${m.key}`) }}</div>\n </div>\n <div class=\"metric-icon\" aria-hidden=\"true\"><AppIcon :icon=\"m.icon\" :size=\"15\" /></div>\n </div>\n </n-card>\n </div>\n\n <!-- 趋势图:区间内无数据的日期后端已补 0,直接画连续折线 -->\n <n-card\n :title=\"t('aiUsage.trend.title')\"\n class=\"a-card\"\n size=\"small\"\n :bordered=\"true\"\n content-style=\"padding: 8px 14px 12px\"\n >\n <n-skeleton v-if=\"trendLoading\" height=\"320px\" :sharp=\"false\" />\n <LineChart v-else :categories=\"trendCategories\" :series=\"trendSeries\" />\n </n-card>\n\n <!-- 分组维度 Tab + 分组表:切 Tab 换 groupBy 重新拉 summary;窄档换成卡片列表 -->\n <n-card class=\"a-card\" size=\"small\" :bordered=\"true\" content-style=\"padding: 4px 14px 12px\">\n <n-tabs v-model:value=\"activeGroupBy\" type=\"line\" animated>\n <n-tab-pane\n v-for=\"tab in groupTabs\"\n :key=\"tab.value\"\n :name=\"tab.value\"\n :tab=\"t(`aiUsage.groupBy.${tab.labelKey}`)\"\n display-directive=\"show:lazy\"\n >\n <SmartTable\n :columns=\"groupColumns\"\n :data=\"groups\"\n row-key=\"key\"\n :loading=\"summaryLoading\"\n :search=\"{ container: 'table' }\"\n :toolbar=\"TABLE_TOOLBAR\"\n :card-props=\"{ bordered: false }\"\n :scroll-x=\"720\"\n card-on-narrow\n storage-key=\"sys-ai-usage-group\"\n >\n <template #pagination-prefix=\"{ itemCount }\">\n <TableTotal :count=\"itemCount\" />\n </template>\n </SmartTable>\n </n-tab-pane>\n </n-tabs>\n </n-card>\n\n <!-- 明细表:顶部筛选走 :params(与条件构造器的 success 键不冲突),「结果」走内置条件搜索 -->\n <SmartTable\n :columns=\"detailColumns\"\n :fetcher=\"aiUsageApi.page\"\n :params=\"filterParams\"\n :search=\"{ container: 'table' }\"\n :toolbar=\"TABLE_TOOLBAR\"\n :default-page-size=\"20\"\n card-on-narrow\n storage-key=\"sys-ai-usage\"\n @error=\"(e: unknown) => message.error(translateError(e))\"\n >\n <template #pagination-prefix=\"{ itemCount }\">\n <TableTotal :count=\"itemCount\" />\n </template>\n </SmartTable>\n </div>\n</template>\n\n<style scoped>\n/* 容器查询的容器:四格指标的列数按内容区宽度分档(窄 1 / 中 2 / 宽 4),不看视口 */\n.view {\n display: flex;\n flex-direction: column;\n gap: var(--gap-card, 8px);\n container-type: inline-size;\n}\n.filter-bar {\n display: flex;\n flex-wrap: wrap;\n align-items: flex-end;\n gap: 12px 20px;\n}\n.filter-item {\n display: flex;\n flex-direction: column;\n gap: 6px;\n min-width: 0;\n}\n.filter-label {\n font-size: 13px;\n}\n.date-filter-row {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 8px;\n}\n.date-picker {\n width: 290px;\n}\n.filter-ctl {\n width: 180px;\n}\n.quick-range {\n display: flex;\n gap: 4px;\n}\n/* 窄档:每个筛选项独占一行,控件撑满 */\n@container (max-width: 603px) {\n .filter-item {\n flex: 1 1 100%;\n }\n .date-picker,\n .filter-ctl {\n width: 100%;\n }\n .date-picker {\n flex: 1 1 100%;\n }\n}\n.metrics {\n display: grid;\n gap: 8px;\n grid-template-columns: minmax(0, 1fr);\n}\n@container (min-width: 480px) {\n .metrics {\n grid-template-columns: repeat(2, minmax(0, 1fr));\n }\n}\n@container (min-width: 1000px) {\n .metrics {\n grid-template-columns: repeat(4, minmax(0, 1fr));\n }\n}\n.metric-body {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n gap: 8px;\n}\n.metric-main {\n min-width: 0;\n}\n.metric-val {\n font-size: 28px;\n font-weight: 600;\n letter-spacing: -0.04em;\n line-height: 1;\n color: var(--text-1);\n white-space: nowrap;\n}\n.metric-suffix {\n margin-left: 4px;\n font-size: 15px;\n letter-spacing: 0;\n color: var(--text-3);\n}\n.metric-label {\n margin-top: 6px;\n font-size: 14px;\n}\n/* 指标图标底块:中性玻璃小方块 */\n.metric-icon {\n flex: none;\n display: grid;\n place-items: center;\n width: 28px;\n height: 28px;\n border-radius: var(--radius-md);\n color: var(--text-1);\n background: var(--glass-strong);\n box-shadow: inset 0 0 0 1px var(--hairline);\n}\n.share-cell {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n.share-bar {\n flex: 1;\n min-width: 60px;\n}\n.share-text {\n flex: none;\n width: 58px;\n text-align: right;\n font-size: 13px;\n white-space: nowrap;\n}\n</style>\n"],"names":["t","useI18n","message","useMessage","fmtDate","d","y","m","day","daysAgo","n","quickRangeValue","days","dateRange","ref","activeQuickRange","setQuickRange","onDateRangeUpdate","v","providerCode","model","scene","providerOptions","onMounted","items","aiProviderApi","p","filterParams","computed","groupTabs","AI_USAGE_GROUP_BY","activeGroupBy","summary","summaryLoading","summaryReq","fetchSummary","reqId","res","aiUsageApi","e","translateError","watch","totals","_a","groups","avgLatencyMs","totalCalls","sum","g","metrics","_b","_c","groupDimensionLabel","tab","numCell","h","groupColumns","r","NProgress","trendPoints","trendLoading","trendReq","fetchTrend","trendCategories","trendSeries","detailColumns","fmtDateTime","SEARCH_ACTIONS","NTag","AI_USAGE_SOURCE","deriveHeaderFilters","_openBlock","_createElementBlock","_hoisted_1","_createVNode","_unref","NCard","_createElementVNode","_hoisted_2","_hoisted_3","_hoisted_4","_toDisplayString","_hoisted_5","NDatePicker","_hoisted_6","NButton","_hoisted_7","_hoisted_8","NSelect","$event","_hoisted_9","_hoisted_10","_hoisted_11","_hoisted_12","NInput","_hoisted_13","_Fragment","_renderList","_createBlock","_hoisted_14","_hoisted_15","NSkeleton","_hoisted_16","NNumberAnimation","_hoisted_17","_hoisted_18","_hoisted_19","AppIcon","LineChart","NTabs","NTabPane","SmartTable","TABLE_TOOLBAR","_withCtx","itemCount","TableTotal","_cache"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AA0CA,UAAM,EAAE,GAAAA,EAAA,IAAMC,GAAA,GACRC,IAAUC,GAAA;AAGhB,aAASC,EAAQC,GAAiB;AAChC,YAAMC,IAAID,EAAE,YAAA,GACNE,IAAI,OAAOF,EAAE,SAAA,IAAa,CAAC,EAAE,SAAS,GAAG,GAAG,GAC5CG,IAAM,OAAOH,EAAE,QAAA,CAAS,EAAE,SAAS,GAAG,GAAG;AAC/C,aAAO,GAAGC,CAAC,IAAIC,CAAC,IAAIC,CAAG;AAAA,IACzB;AACA,aAASC,EAAQC,GAAiB;AAChC,YAAML,wBAAQ,KAAA;AACd,aAAAA,EAAE,QAAQA,EAAE,QAAA,IAAYK,CAAC,GAClBL;AAAA,IACT;AACA,aAASM,EAAgBC,GAAgC;AACvD,aAAO,CAACR,EAAQK,EAAQG,IAAO,CAAC,CAAC,GAAGR,EAAQK,EAAQ,CAAC,CAAC,CAAC;AAAA,IACzD;AAEA,UAAMI,IAAYC,EAAsBH,EAAgB,CAAC,CAAC,GACpDI,IAAmBD,EAAmB,CAAC;AAE7C,aAASE,EAAcJ,GAAc;AACnC,MAAAC,EAAU,QAAQF,EAAgBC,CAAI,GACtCG,EAAiB,QAAQH;AAAA,IAC3B;AAEA,aAASK,GAAkBC,GAA4B;AACrD,MAAKA,MACLL,EAAU,QAAQK,GAClBH,EAAiB,QAAQ;AAAA,IAC3B;AAMA,UAAMI,IAAeL,EAAmB,IAAI,GACtCM,IAAQN,EAAmB,IAAI,GAC/BO,IAAQP,EAAmB,IAAI,GAE/BQ,IAAkBR,EAAwC,EAAE;AAClE,IAAAS,GAAU,YAAY;AACpB,UAAI;AACF,cAAM,EAAE,OAAAC,MAAU,MAAMC,GAAc,KAAK,EAAE,MAAM,GAAG,UAAU,KAAK;AACrE,QAAAH,EAAgB,QAAQE,EAAM,IAAI,CAAAE,OAAM,EAAE,OAAOA,EAAE,MAAM,OAAOA,EAAE,KAAA,EAAO;AAAA,MAC3E,QAAQ;AAAA,MAER;AAAA,IACF,CAAC;AAOD,UAAMC,IAAeC,EAAS,MAAM;AAClC,YAAMF,IAMF;AAAA,QACF,MAAM,GAAGb,EAAU,MAAM,CAAC,CAAC;AAAA,QAC3B,IAAI,GAAGA,EAAU,MAAM,CAAC,CAAC;AAAA,MAAA;AAE3B,aAAIM,EAAa,UAAOO,EAAE,eAAeP,EAAa,QAClDC,EAAM,UAAOM,EAAE,QAAQN,EAAM,QAC7BC,EAAM,UAAOK,EAAE,QAAQL,EAAM,QAC1BK;AAAA,IACT,CAAC,GAGKG,IAA4F;AAAA,MAChG,EAAE,OAAOC,EAAkB,UAAU,UAAU,WAAA;AAAA,MAC/C,EAAE,OAAOA,EAAkB,OAAO,UAAU,QAAA;AAAA,MAC5C,EAAE,OAAOA,EAAkB,OAAO,UAAU,QAAA;AAAA,MAC5C,EAAE,OAAOA,EAAkB,MAAM,UAAU,OAAA;AAAA,IAAO,GAE9CC,IAAgBjB,EAAoBgB,EAAkB,QAAQ,GAG9DE,IAAUlB,EAA2B,IAAI,GACzCmB,IAAiBnB,EAAI,EAAI;AAC/B,QAAIoB,IAAa;AACjB,mBAAeC,KAAe;AAC5B,YAAMC,IAAQ,EAAEF;AAChB,MAAAD,EAAe,QAAQ;AACvB,UAAI;AACF,cAAMI,IAAM,MAAMC,EAAW,QAAQ,EAAE,GAAGX,EAAa,OAAO,SAASI,EAAc,OAAO;AAC5F,YAAIK,MAAUF,EAAY;AAC1B,QAAAF,EAAQ,QAAQK;AAAA,MAClB,SAASE,GAAG;AACV,YAAIH,MAAUF,EAAY;AAC1B,QAAAhC,EAAQ,MAAMsC,EAAeD,CAAC,CAAC;AAAA,MACjC,UAAA;AACE,QAAIH,MAAUF,MAAYD,EAAe,QAAQ;AAAA,MACnD;AAAA,IACF;AACA,IAAAQ,EAAM,CAACd,GAAcI,CAAa,GAAGI,IAAc,EAAE,WAAW,IAAM;AAEtE,UAAMO,IAASd,EAAS,MAAA;;AAAM,eAAAe,IAAAX,EAAQ,UAAR,gBAAAW,EAAe,WAAU;AAAA,KAAI,GACrDC,IAAShB,EAAS,MAAA;;AAAM,eAAAe,IAAAX,EAAQ,UAAR,gBAAAW,EAAe,WAAU;KAAE,GAWnDE,IAAejB,EAAS,MAAM;AAClC,YAAMkB,IAAaF,EAAO,MAAM,OAAO,CAACG,GAAKC,MAAMD,IAAMC,EAAE,WAAW,CAAC;AACvE,aAAKF,IACYF,EAAO,MAAM,OAAO,CAACG,GAAKC,MAAMD,IAAMC,EAAE,eAAeA,EAAE,WAAW,CAAC,IACpEF,IAFM;AAAA,IAG1B,CAAC,GAEKG,KAAUrB,EAAS,MAAA;;AAAM;AAAA,QAC7B;AAAA,UACE,KAAK;AAAA,UACL,MAAM;AAAA,UACN,SAAOe,IAAAD,EAAO,UAAP,gBAAAC,EAAc,gBAAe;AAAA,UACpC,OAAO,CAACD,EAAO;AAAA,QAAA;AAAA,QAEjB;AAAA,UACE,KAAK;AAAA,UACL,MAAM;AAAA,UACN,SAAOQ,IAAAR,EAAO,UAAP,gBAAAQ,EAAc,cAAa;AAAA,UAClC,OAAO,CAACR,EAAO;AAAA,QAAA;AAAA,QAEjB;AAAA,UACE,KAAK;AAAA,UACL,MAAM;AAAA,UACN,SAAOS,IAAAT,EAAO,UAAP,gBAAAS,EAAc,iBAAgB;AAAA,UACrC,OAAO,CAACT,EAAO;AAAA,QAAA;AAAA,QAEjB;AAAA,UACE,KAAK;AAAA,UACL,MAAM;AAAA,UACN,OAAOG,EAAa,SAAS,OAAO,KAAK,MAAMA,EAAa,KAAK,IAAI;AAAA,UACrE,QAAQ;AAAA,UACR,OAAOA,EAAa,SAAS;AAAA,QAAA;AAAA,MAC/B;AAAA,KACD,GAGKO,KAAsBxB,EAAS,MAAM;AACzC,YAAMyB,IAAMxB,EAAU,KAAK,OAAKmB,EAAE,UAAUjB,EAAc,KAAK;AAC/D,aAAO/B,EAAE,oBAAmBqD,KAAA,gBAAAA,EAAK,aAAY,UAAU,EAAE;AAAA,IAC3D,CAAC,GAEKC,IAAU,CAAC5C,MAAc6C,EAAE,QAAQ,EAAE,OAAO,MAAA,GAAS7C,EAAE,gBAAgB,GAEvE8C,KAAe5B,EAA8C,MAAM;AAAA,MACvE;AAAA,QACE,OAAO,MAAMwB,GAAoB;AAAA,QACjC,KAAK;AAAA,QACL,OAAO;AAAA,QACP,UAAU,EAAE,SAAS,GAAA;AAAA,QACrB,QAAQ,CAAA;AAAA;AAAA;AAAA,QAER,QAAQ,CAAAK,MACNF;AAAA,UACE;AAAA,UACA,EAAE,OAAOxB,EAAc,UAAUD,EAAkB,OAAO,KAAK,OAAA;AAAA,UAC/D2B,EAAE,SAASA,EAAE;AAAA,QAAA;AAAA,MACf;AAAA,MAEJ;AAAA,QACE,OAAO,MAAMzD,EAAE,2BAA2B;AAAA,QAC1C,KAAK;AAAA,QACL,OAAO;AAAA,QACP,OAAO;AAAA,QACP,QAAQ,CAAAyD,MAAKH,EAAQG,EAAE,WAAW;AAAA,MAAA;AAAA,MAEpC;AAAA,QACE,OAAO,MAAMzD,EAAE,4BAA4B;AAAA,QAC3C,KAAK;AAAA,QACL,OAAO;AAAA,QACP,OAAO;AAAA,QACP,QAAQ,CAAAyD,MAAKH,EAAQG,EAAE,YAAY;AAAA,MAAA;AAAA,MAErC;AAAA,QACE,OAAO,MAAMzD,EAAE,2BAA2B;AAAA,QAC1C,KAAK;AAAA,QACL,OAAO;AAAA,QACP,OAAO;AAAA,QACP,QAAQ,CAAAyD,MAAKH,EAAQG,EAAE,SAAS;AAAA,MAAA;AAAA,MAElC;AAAA,QACE,OAAO,MAAMzD,EAAE,uBAAuB;AAAA,QACtC,KAAK;AAAA,QACL,OAAO;AAAA,QACP,OAAO;AAAA,QACP,QAAQ,CAAAyD,MAAKF,EAAE,QAAQ,EAAE,OAAO,MAAA,GAAS,GAAG,KAAK,MAAME,EAAE,YAAY,CAAC,KAAK;AAAA,MAAA;AAAA,MAE7E;AAAA,QACE,OAAO,MAAMzD,EAAE,eAAe;AAAA,QAC9B,KAAK;AAAA,QACL,OAAO;AAAA,QACP,QAAQ,CAAAyD,MACNF,EAAE,OAAO,EAAE,OAAO,gBAAgB;AAAA,UAChCA,EAAE,OAAO,EAAE,OAAO,eAAe;AAAA,YAC/BA,EAAEG,IAAW;AAAA,cACX,MAAM;AAAA,cACN,YAAY,KAAK,MAAMD,EAAE,YAAY;AAAA,cACrC,eAAe;AAAA,cACf,QAAQ;AAAA,YAAA,CACT;AAAA,UAAA,CACF;AAAA,UACDF,EAAE,QAAQ,EAAE,OAAO,uBAAA,GAA0B,GAAGE,EAAE,aAAa,QAAQ,CAAC,CAAC,GAAG;AAAA,QAAA,CAC7E;AAAA,MAAA;AAAA,IACL,CACD,GAGKE,IAAc7C,EAAyB,EAAE,GACzC8C,IAAe9C,EAAI,EAAI;AAC7B,QAAI+C,IAAW;AACf,mBAAeC,KAAa;AAC1B,YAAM1B,IAAQ,EAAEyB;AAChB,MAAAD,EAAa,QAAQ;AACrB,UAAI;AACF,cAAMvB,IAAM,MAAMC,EAAW,MAAMX,EAAa,KAAK;AACrD,YAAIS,MAAUyB,EAAU;AACxB,QAAAF,EAAY,QAAQtB;AAAA,MACtB,SAASE,GAAG;AACV,YAAIH,MAAUyB,EAAU;AACxB,QAAA3D,EAAQ,MAAMsC,EAAeD,CAAC,CAAC;AAAA,MACjC,UAAA;AACE,QAAIH,MAAUyB,MAAUD,EAAa,QAAQ;AAAA,MAC/C;AAAA,IACF;AACA,IAAAnB,EAAMd,GAAcmC,IAAY,EAAE,WAAW,IAAM;AAEnD,UAAMC,KAAkBnC,EAAS,MAAM+B,EAAY,MAAM,IAAI,CAAAjC,MAAKA,EAAE,IAAI,CAAC,GACnEsC,KAAcpC,EAAS,MAAM;AAAA,MACjC,EAAE,MAAM5B,EAAE,2BAA2B,GAAG,MAAM2D,EAAY,MAAM,IAAI,CAAAjC,MAAKA,EAAE,WAAW,EAAA;AAAA,MACtF,EAAE,MAAM1B,EAAE,4BAA4B,GAAG,MAAM2D,EAAY,MAAM,IAAI,CAAAjC,MAAKA,EAAE,YAAY,EAAA;AAAA,IAAE,CAC3F,GAIKuC,IAAmD;AAAA,MACvD;AAAA,QACE,KAAK;AAAA,QACL,OAAO,MAAMjE,EAAE,0BAA0B;AAAA,QACzC,OAAO;AAAA,QACP,QAAQ,CAAAyD,MACNF,EAAE,QAAQ,EAAE,OAAO,aAAa,OAAO,sBAAA,GAAyBW,GAAYT,EAAE,UAAU,CAAC;AAAA,MAAA;AAAA,MAE7F;AAAA,QACE,KAAK;AAAA,QACL,OAAO,MAAMzD,EAAE,wBAAwB;AAAA,QACvC,OAAO;AAAA,QACP,QAAQ,OAAKuD,EAAE,QAAQ,EAAE,OAAO,UAAUE,EAAE,YAAY;AAAA,MAAA;AAAA,MAE1D;AAAA,QACE,KAAK;AAAA,QACL,OAAO,MAAMzD,EAAE,qBAAqB;AAAA,QACpC,OAAO;AAAA,QACP,UAAU,EAAE,SAAS,GAAA;AAAA,QACrB,QAAQ,OAAKuD,EAAE,QAAQ,EAAE,OAAO,UAAUE,EAAE,KAAK;AAAA,MAAA;AAAA,MAEnD;AAAA,QACE,KAAK;AAAA,QACL,OAAO,MAAMzD,EAAE,qBAAqB;AAAA,QACpC,OAAO;AAAA,QACP,QAAQ,CAAAyD,MAAKF,EAAE,QAAQ,EAAE,OAAO,aAAA,GAAgBE,EAAE,SAAS,GAAG;AAAA,MAAA;AAAA,MAEhE;AAAA,QACE,KAAK;AAAA,QACL,OAAO,MAAMzD,EAAE,oBAAoB;AAAA,QACnC,OAAO;AAAA,QACP,QAAQ,CAAAyD,MAAKF,EAAE,QAAQ,EAAE,OAAO,MAAA,GAASE,EAAE,UAAU,OAAO,OAAOA,EAAE,MAAM,IAAI,GAAG;AAAA,MAAA;AAAA,MAEpF;AAAA,QACE,KAAK;AAAA,QACL,OAAO,MAAMzD,EAAE,2BAA2B;AAAA,QAC1C,OAAO;AAAA,QACP,OAAO;AAAA,QACP,QAAQ,CAAAyD,MAAKH,EAAQG,EAAE,WAAW;AAAA,MAAA;AAAA,MAEpC;AAAA,QACE,KAAK;AAAA,QACL,OAAO,MAAMzD,EAAE,4BAA4B;AAAA,QAC3C,OAAO;AAAA,QACP,OAAO;AAAA,QACP,QAAQ,CAAAyD,MAAKH,EAAQG,EAAE,YAAY;AAAA,MAAA;AAAA,MAErC;AAAA,QACE,KAAK;AAAA,QACL,OAAO,MAAMzD,EAAE,2BAA2B;AAAA,QAC1C,OAAO;AAAA,QACP,OAAO;AAAA,QACP,QAAQ,CAAAyD,MAAKH,EAAQG,EAAE,WAAW;AAAA,MAAA;AAAA,MAEpC;AAAA,QACE,KAAK;AAAA,QACL,OAAO,MAAMzD,EAAE,uBAAuB;AAAA,QACtC,OAAO;AAAA,QACP,OAAO;AAAA,QACP,QAAQ,CAAAyD,MAAKF,EAAE,QAAQ,EAAE,OAAO,MAAA,GAAS,GAAGE,EAAE,UAAU,eAAA,CAAgB,KAAK;AAAA,MAAA;AAAA,MAE/E;AAAA,QACE,KAAK;AAAA,QACL,OAAO,MAAMzD,EAAE,sBAAsB;AAAA,QACrC,OAAO;AAAA;AAAA,QAEP,SAAS;AAAA,UACP,EAAE,OAAO,MAAMA,EAAE,uBAAuB,GAAG,OAAO,GAAA;AAAA,UAClD,EAAE,OAAO,MAAMA,EAAE,sBAAsB,GAAG,OAAO,GAAA;AAAA,QAAM;AAAA,QAEzD,QAAQ,EAAE,SAASmE,GAAe,MAAA;AAAA;AAAA,QAElC,QAAQ,CAAAV,MACNF,EAAE,QAAQ,EAAE,OAAO,CAACE,EAAE,WAAWA,EAAE,eAAeA,EAAE,eAAe,UAAa;AAAA,UAC9EF;AAAA,YAAEa;AAAA,YAAM,EAAE,MAAM,SAAS,UAAU,IAAO,MAAMX,EAAE,UAAU,YAAY,QAAA;AAAA,YAAW,MACjFA,EAAE,UAAUzD,EAAE,uBAAuB,IAAIA,EAAE,sBAAsB;AAAA,UAAA;AAAA,QACnE,CACD;AAAA,MAAA;AAAA,MAEL;AAAA,QACE,KAAK;AAAA,QACL,OAAO,MAAMA,EAAE,wBAAwB;AAAA,QACvC,OAAO;AAAA,QACP,QAAQ,CAAAyD,MACNF;AAAA,UAAEa;AAAA,UAAM,EAAE,MAAM,SAAS,UAAU,GAAA;AAAA,UAAS,MAC1CX,EAAE,WAAWzD,EAAE,YAAY,IAAIA,EAAE,WAAW;AAAA,QAAA;AAAA,MAC9C;AAAA,MAEJ;AAAA,QACE,KAAK;AAAA,QACL,OAAO,MAAMA,EAAE,2BAA2B;AAAA,QAC1C,OAAO;AAAA,QACP,QAAQ,CAAAyD,MACNA,EAAE,gBAAgBY,GAAgB,WAC9BrE,EAAE,mCAAmC,IACrCA,EAAE,kCAAkC;AAAA,MAAA;AAAA,IAC5C;AAEF,WAAAsE,GAAoBL,CAAa,cAI/BM,EAAA,GAAAC,EAkKM,OAlKNC,IAkKM;AAAA,MAhKJC,EAyESC,EAAAC,CAAA,GAAA;AAAA,QAzED,OAAM;AAAA,QAAS,MAAK;AAAA,QAAS,UAAU;AAAA,QAAM,iBAAc;AAAA,MAAA;mBACjE,MAuEM;AAAA,UAvENC,EAuEM,OAvENC,IAuEM;AAAA,YAtEJD,EAqCM,OArCNE,IAqCM;AAAA,cApCJF,EAA2E,QAA3EG,IAA2EC,EAAvCN,EAAA3E,CAAA,EAAC,0BAAA,CAAA,GAAA,CAAA;AAAA,cACrC6E,EAkCM,OAlCNK,IAkCM;AAAA,gBAjCJR,EAMEC,EAAAQ,EAAA,GAAA;AAAA,kBALC,mBAAiBtE,EAAA;AAAA,kBAClB,MAAK;AAAA,kBACL,gBAAa;AAAA,kBACb,OAAM;AAAA,kBACL,2BAAwBI;AAAA,gBAAA;gBAE3B4D,EAyBM,OAzBNO,IAyBM;AAAA,kBAxBJV,EAOWC,EAAAU,CAAA,GAAA;AAAA,oBANT,MAAK;AAAA,oBACL,YAAA;AAAA,oBACC,MAAMtE,EAAA,UAAgB,IAAA,YAAA;AAAA,oBACtB,gCAAOC,EAAa,CAAA;AAAA,kBAAA;+BAErB,MAA+B;AAAA,0BAA5B2D,EAAA3E,CAAA,EAAC,sBAAA,CAAA,GAAA,CAAA;AAAA,oBAAA;;;kBAEN0E,EAOWC,EAAAU,CAAA,GAAA;AAAA,oBANT,MAAK;AAAA,oBACL,YAAA;AAAA,oBACC,MAAMtE,EAAA,UAAgB,IAAA,YAAA;AAAA,oBACtB,gCAAOC,EAAa,CAAA;AAAA,kBAAA;+BAErB,MAAmC;AAAA,0BAAhC2D,EAAA3E,CAAA,EAAC,0BAAA,CAAA,GAAA,CAAA;AAAA,oBAAA;;;kBAEN0E,EAOWC,EAAAU,CAAA,GAAA;AAAA,oBANT,MAAK;AAAA,oBACL,YAAA;AAAA,oBACC,MAAMtE,EAAA,UAAgB,KAAA,YAAA;AAAA,oBACtB,gCAAOC,EAAa,EAAA;AAAA,kBAAA;+BAErB,MAAoC;AAAA,0BAAjC2D,EAAA3E,CAAA,EAAC,2BAAA,CAAA,GAAA,CAAA;AAAA,oBAAA;;;;;;YAKZ6E,EAUM,OAVNS,IAUM;AAAA,cATJT,EAA0E,QAA1EU,IAA0EN,EAAtCN,EAAA3E,CAAA,EAAC,yBAAA,CAAA,GAAA,CAAA;AAAA,cACrC0E,EAOEC,EAAAa,CAAA,GAAA;AAAA,gBANQ,OAAOrE,EAAA;AAAA,yDAAAA,EAAY,QAAAsE;AAAA,gBAC1B,SAASnE,EAAA;AAAA,gBACV,WAAA;AAAA,gBACA,YAAA;AAAA,gBACA,OAAM;AAAA,gBACL,aAAaqD,EAAA3E,CAAA,EAAC,4BAAA;AAAA,cAAA;;YAGnB6E,EAWM,OAXNa,IAWM;AAAA,cAVJb,EAAuE,QAAvEc,IAAuEV,EAAnCN,EAAA3E,CAAA,EAAC,sBAAA,CAAA,GAAA,CAAA;AAAA,cACrC0E,EAQEC,EAAAa,CAAA,GAAA;AAAA,gBAPQ,OAAOpE,EAAA;AAAA,yDAAAA,EAAK,QAAAqE;AAAA,gBACnB,SAAS,CAAA;AAAA,gBACV,YAAA;AAAA,gBACA,KAAA;AAAA,gBACA,WAAA;AAAA,gBACA,OAAM;AAAA,gBACL,aAAad,EAAA3E,CAAA,EAAC,yBAAA;AAAA,cAAA;;YAGnB6E,EAQM,OARNe,IAQM;AAAA,cAPJf,EAAuE,QAAvEgB,IAAuEZ,EAAnCN,EAAA3E,CAAA,EAAC,sBAAA,CAAA,GAAA,CAAA;AAAA,cACrC0E,EAKEC,EAAAmB,EAAA,GAAA;AAAA,gBAJQ,OAAOzE,EAAA;AAAA,yDAAAA,EAAK,QAAAoE;AAAA,gBACpB,WAAA;AAAA,gBACA,OAAM;AAAA,gBACL,aAAad,EAAA3E,CAAA,EAAC,iCAAA;AAAA,cAAA;;;;;;MAOvB6E,EAwBM,OAxBNkB,IAwBM;AAAA,gBAvBJvB,EAsBSwB,GAAA,MAAAC,EArBKhD,GAAA,OAAO,CAAZ1C,YADT2F,EAsBSvB,EAAAC,CAAA,GAAA;AAAA,UApBN,KAAKrE,EAAE;AAAA,UACR,OAAM;AAAA,UACN,MAAK;AAAA,UACJ,UAAU;AAAA,UACX,iBAAc;AAAA,QAAA;qBAEd,MAaM;AAAA,YAbNsE,EAaM,OAbNsB,IAaM;AAAA,cAZJtB,EAUM,OAVNuB,IAUM;AAAA,gBATcnE,EAAA,cAAlBiE,EAAoEvB,EAAA0B,CAAA,GAAA;AAAA;kBAAlC,MAAA;AAAA,kBAAK,OAAM;AAAA,kBAAO,QAAO;AAAA,gBAAA,OAC3D9B,EAAA,GAAAC,EAMM,OANN8B,IAMM;AAAA,kBALY/F,EAAE,cAAlBiE,EAAqCwB,GAAA,EAAA,KAAA,KAAA;AAAA,sBAAZ,GAAC;AAAA,kBAAA,gBAC1BxB,EAGWwB,GAAA,EAAA,KAAA,KAAA;AAAA,oBAFTtB,EAA6DC,EAAA4B,EAAA,GAAA;AAAA,sBAAxC,MAAM;AAAA,sBAAI,IAAIhG,EAAE;AAAA,sBAAO,kBAAA;AAAA,oBAAA;oBAC/BA,EAAE,UAAfgE,EAAA,GAAAC,EAAyE,SAAzEgC,IAAyEvB,EAAA,MAAnB1E,EAAE,MAAM,GAAA,CAAA;;;gBAGlEsE,EAAwE,OAAxE4B,IAAwExB,EAArCN,KAAC,kBAAmBpE,EAAE,GAAG,EAAA,CAAA,GAAA,CAAA;AAAA,cAAA;cAE9DsE,EAAuF,OAAvF6B,IAAuF;AAAA,gBAA3ChC,EAAqCiC,IAAA;AAAA,kBAA3B,MAAMpG,EAAE;AAAA,kBAAO,MAAM;AAAA,gBAAA;;;;;;;MAMjFmE,EASSC,EAAAC,CAAA,GAAA;AAAA,QARN,OAAOD,EAAA3E,CAAA,EAAC,qBAAA;AAAA,QACT,OAAM;AAAA,QACN,MAAK;AAAA,QACJ,UAAU;AAAA,QACX,iBAAc;AAAA,MAAA;mBAEd,MAAgE;AAAA,UAA9C4D,EAAA,cAAlBsC,EAAgEvB,EAAA0B,CAAA,GAAA;AAAA;YAAhC,QAAO;AAAA,YAAS,OAAO;AAAA,UAAA,YACvDH,EAAwEU,IAAA;AAAA;YAArD,YAAY7C,GAAA;AAAA,YAAkB,QAAQC,GAAA;AAAA,UAAA;;;;MAI3DU,EA2BSC,EAAAC,CAAA,GAAA;AAAA,QA3BD,OAAM;AAAA,QAAS,MAAK;AAAA,QAAS,UAAU;AAAA,QAAM,iBAAc;AAAA,MAAA;mBACjE,MAyBS;AAAA,UAzBTF,EAyBSC,EAAAkC,EAAA,GAAA;AAAA,YAzBO,OAAO9E,EAAA;AAAA,qDAAAA,EAAa,QAAA0D;AAAA,YAAE,MAAK;AAAA,YAAO,UAAA;AAAA,UAAA;uBAE9C,MAAwB;AAAA,oBAD1BjB,EAuBawB,GAAA,MAAAC,EAtBGpE,GAAS,CAAhBwB,MADTqB,EAuBaC,EAAAmC,EAAA,GAAA;AAAA,gBArBV,KAAKzD,EAAI;AAAA,gBACT,MAAMA,EAAI;AAAA,gBACV,KAAKsB,EAAA3E,CAAA,EAAC,mBAAoBqD,EAAI,QAAQ,EAAA;AAAA,gBACvC,qBAAkB;AAAA,cAAA;2BAElB,MAea;AAAA,kBAfbqB,EAeaC,EAAAoC,CAAA,GAAA;AAAA,oBAdV,SAASvD,GAAA;AAAA,oBACT,MAAMZ,EAAA;AAAA,oBACP,WAAQ;AAAA,oBACP,SAASX,EAAA;AAAA,oBACT,QAAQ,EAAA,WAAA,QAAA;AAAA,oBACR,SAAS0C,EAAAqC,EAAA;AAAA,oBACT,cAAY,EAAA,UAAA,GAAA;AAAA,oBACZ,YAAU;AAAA,oBACX,kBAAA;AAAA,oBACA,eAAY;AAAA,kBAAA;oBAED,qBAAiBC,EAC1B,CAAiC,EADH,WAAAC,QAAS;AAAA,sBACvCxC,EAAiCyC,IAAA,EAApB,OAAOD,EAAA,GAAS,MAAA,GAAA,CAAA,OAAA,CAAA;AAAA,oBAAA;;;;;;;;;;;;MAQvCxC,EAcaC,EAAAoC,CAAA,GAAA;AAAA,QAbV,SAAS9C;AAAA,QACT,SAASU,EAAArC,CAAA,EAAW;AAAA,QACpB,QAAQX,EAAA;AAAA,QACR,QAAQ,EAAA,WAAA,QAAA;AAAA,QACR,SAASgD,EAAAqC,EAAA;AAAA,QACT,qBAAmB;AAAA,QACpB,kBAAA;AAAA,QACA,eAAY;AAAA,QACX,SAAKI,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA,CAAG7E,MAAeoC,EAAAzE,CAAA,EAAQ,MAAMyE,EAAAnC,CAAA,EAAeD,CAAC,CAAA;AAAA,MAAA;QAE3C,qBAAiB0E,EAC1B,CAAiC,EADH,WAAAC,QAAS;AAAA,UACvCxC,EAAiCyC,IAAA,EAApB,OAAOD,EAAA,GAAS,MAAA,GAAA,CAAA,OAAA,CAAA;AAAA,QAAA;;;;;;"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import o from "./AdvancedSection2.vue.js";
|
|
2
2
|
/* empty css */
|
|
3
|
-
import
|
|
4
|
-
const
|
|
3
|
+
import a from "../../../../_virtual/_plugin-vue_export-helper.js";
|
|
4
|
+
const c = /* @__PURE__ */ a(o, [["__scopeId", "data-v-dd8a1a0a"]]);
|
|
5
5
|
export {
|
|
6
|
-
|
|
6
|
+
c as default
|
|
7
7
|
};
|
|
8
8
|
//# sourceMappingURL=AdvancedSection.vue.js.map
|
|
@@ -45,12 +45,13 @@ const Te = /* @__PURE__ */ z({
|
|
|
45
45
|
},
|
|
46
46
|
{
|
|
47
47
|
key: "groupCode",
|
|
48
|
+
ellipsis: { tooltip: !0 },
|
|
48
49
|
title: () => e("config.group"),
|
|
49
50
|
search: { actions: d.exact },
|
|
50
51
|
render: (o) => o.groupCode || "—"
|
|
51
52
|
},
|
|
52
53
|
{ key: "sort", title: () => e("config.sort"), width: 80 },
|
|
53
|
-
{ key: "createTime", title: () => e("common.createTime"), format: "datetime" },
|
|
54
|
+
{ key: "createTime", width: 170, title: () => e("common.createTime"), format: "datetime" },
|
|
54
55
|
{
|
|
55
56
|
key: "op",
|
|
56
57
|
title: () => e("common.operation"),
|