feat: 界面优化
parent
b0a3dedb99
commit
3337285135
|
|
@ -10,6 +10,7 @@ lerna-debug.log*
|
||||||
node_modules
|
node_modules
|
||||||
dist
|
dist
|
||||||
dist-ssr
|
dist-ssr
|
||||||
|
*.tsbuildinfo
|
||||||
*.local
|
*.local
|
||||||
|
|
||||||
# Editor directories and files
|
# Editor directories and files
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@
|
||||||
"zustand": "^4.5.2"
|
"zustand": "^4.5.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/node": "^24.0.10",
|
||||||
"@types/react": "^18.2.55",
|
"@types/react": "^18.2.55",
|
||||||
"@types/react-dom": "^18.2.19",
|
"@types/react-dom": "^18.2.19",
|
||||||
"@vitejs/plugin-react": "^4.2.1",
|
"@vitejs/plugin-react": "^4.2.1",
|
||||||
|
|
@ -1580,6 +1581,16 @@
|
||||||
"integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
|
"integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/node": {
|
||||||
|
"version": "24.13.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@types/node/-/node-24.13.2.tgz",
|
||||||
|
"integrity": "sha512-fRa09kZTgu8o71KFcDjUFuc7F+dEbZYZmkI0mg5YBTRs0yMKjYHsq/c0urDKeDb+D5qVgXOdFcuu+DZPKOITwA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~7.18.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/pako": {
|
"node_modules/@types/pako": {
|
||||||
"version": "2.0.4",
|
"version": "2.0.4",
|
||||||
"resolved": "https://registry.npmmirror.com/@types/pako/-/pako-2.0.4.tgz",
|
"resolved": "https://registry.npmmirror.com/@types/pako/-/pako-2.0.4.tgz",
|
||||||
|
|
@ -4666,6 +4677,13 @@
|
||||||
"node": ">=14.17"
|
"node": ">=14.17"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/undici-types": {
|
||||||
|
"version": "7.18.2",
|
||||||
|
"resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-7.18.2.tgz",
|
||||||
|
"integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/unified": {
|
"node_modules/unified": {
|
||||||
"version": "11.0.5",
|
"version": "11.0.5",
|
||||||
"resolved": "https://registry.npmmirror.com/unified/-/unified-11.0.5.tgz",
|
"resolved": "https://registry.npmmirror.com/unified/-/unified-11.0.5.tgz",
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@
|
||||||
"zustand": "^4.5.2"
|
"zustand": "^4.5.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/node": "^24.0.10",
|
||||||
"@types/react": "^18.2.55",
|
"@types/react": "^18.2.55",
|
||||||
"@types/react-dom": "^18.2.19",
|
"@types/react-dom": "^18.2.19",
|
||||||
"@vitejs/plugin-react": "^4.2.1",
|
"@vitejs/plugin-react": "^4.2.1",
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo } from "react";
|
||||||
import { ConfigProvider, theme, App as AntdApp } from "antd";
|
import { ConfigProvider, theme, App as AntdApp } from "antd";
|
||||||
import zhCN from "antd/locale/zh_CN";
|
import zhCN from "antd/locale/zh_CN";
|
||||||
import enUS from "antd/locale/en_US";
|
import enUS from "antd/locale/en_US";
|
||||||
|
|
@ -6,10 +6,8 @@ import { useTranslation } from "react-i18next";
|
||||||
import AppRoutes from "./routes";
|
import AppRoutes from "./routes";
|
||||||
import { getOpenPlatformConfig } from "./api";
|
import { getOpenPlatformConfig } from "./api";
|
||||||
import { useThemeStore } from "./store/themeStore";
|
import { useThemeStore } from "./store/themeStore";
|
||||||
import type { SysPlatformConfig } from "./types";
|
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const [config, setConfig] = useState<SysPlatformConfig | null>(null);
|
|
||||||
const { colorPrimary, themeMode, initTheme } = useThemeStore();
|
const { colorPrimary, themeMode, initTheme } = useThemeStore();
|
||||||
const { i18n } = useTranslation();
|
const { i18n } = useTranslation();
|
||||||
const antdLocale = useMemo(() => (i18n.language === "en-US" ? enUS : zhCN), [i18n.language]);
|
const antdLocale = useMemo(() => (i18n.language === "en-US" ? enUS : zhCN), [i18n.language]);
|
||||||
|
|
@ -19,7 +17,6 @@ export default function App() {
|
||||||
const fetchConfig = async () => {
|
const fetchConfig = async () => {
|
||||||
try {
|
try {
|
||||||
const data = await getOpenPlatformConfig();
|
const data = await getOpenPlatformConfig();
|
||||||
setConfig(data);
|
|
||||||
if (data.projectName) {
|
if (data.projectName) {
|
||||||
document.title = data.projectName;
|
document.title = data.projectName;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 52px;
|
min-height: 52px;
|
||||||
padding: 8px 0;
|
padding: 8px 0;
|
||||||
background: #fff;
|
background: var(--app-surface-color, #fff);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
.app-pagination-total {
|
.app-pagination-total {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
color: #333;
|
color: var(--app-text-main, #333);
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -97,7 +97,7 @@
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
align-self: center;
|
align-self: center;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
color: #333;
|
color: var(--app-text-main, #333);
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 32px;
|
line-height: 32px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|
@ -119,31 +119,31 @@
|
||||||
width: 48px;
|
width: 48px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
padding: 0 8px;
|
padding: 0 8px;
|
||||||
color: #333;
|
color: var(--app-text-main, #333);
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 30px;
|
line-height: 30px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background-color: #fff;
|
background-color: var(--app-surface-color, #fff);
|
||||||
border: 1px solid #d9d9d9;
|
border: 1px solid var(--app-border-color, #d9d9d9);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
outline: none;
|
outline: none;
|
||||||
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-pagination-quick-jumper input:hover {
|
.app-pagination-quick-jumper input:hover {
|
||||||
border-color: #1677ff;
|
border-color: var(--app-primary-color, #1677ff);
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-pagination-quick-jumper input:focus {
|
.app-pagination-quick-jumper input:focus {
|
||||||
border-color: #1677ff;
|
border-color: var(--app-primary-color, #1677ff);
|
||||||
box-shadow: 0 0 0 2px rgba(5, 145, 255, 0.1);
|
box-shadow: 0 0 0 2px rgba(var(--app-primary-rgb, 22, 119, 255), 0.12);
|
||||||
}
|
}
|
||||||
|
|
||||||
.app-pagination-quick-jumper input:disabled {
|
.app-pagination-quick-jumper input:disabled {
|
||||||
color: rgba(0, 0, 0, 0.25);
|
color: rgba(0, 0, 0, 0.25);
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
background-color: rgba(0, 0, 0, 0.04);
|
background-color: rgba(0, 0, 0, 0.04);
|
||||||
border-color: #d9d9d9;
|
border-color: var(--app-border-color, #d9d9d9);
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { Pagination, PaginationProps } from 'antd';
|
import { Pagination, type PaginationProps } from 'antd';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { getMinPageSize, getPageSizeOptions, type PaginationVariant } from '@/utils/pagination';
|
import { getMinPageSize, getPageSizeOptions, type PaginationVariant } from '@/utils/pagination';
|
||||||
import './index.css';
|
import './index.css';
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,11 @@
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
background: #fff;
|
background: var(--app-surface-color, #fff);
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-table-container .ant-table-wrapper {
|
.list-table-container .ant-table-wrapper {
|
||||||
background: #fff;
|
background: var(--app-surface-color, #fff);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 行选中样式 */
|
/* 行选中样式 */
|
||||||
|
|
@ -32,7 +32,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.selection-count {
|
.selection-count {
|
||||||
color: #9095a1;
|
color: var(--app-text-secondary, #9095a1);
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -56,26 +56,26 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-table-container .ant-table {
|
.list-table-container .ant-table {
|
||||||
color: #333;
|
color: var(--app-text-main, #333);
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-table-container .ant-table-thead > tr > th {
|
.list-table-container .ant-table-thead > tr > th {
|
||||||
background: #fafafa !important;
|
background: var(--app-bg-surface-soft, #fafafa) !important;
|
||||||
color: #333;
|
color: var(--app-text-main, #333);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
border-bottom: 1px solid #f0f0f0;
|
border-bottom: 1px solid var(--app-border-color, #f0f0f0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-table-container .ant-table-tbody > tr > td {
|
.list-table-container .ant-table-tbody > tr > td {
|
||||||
border-bottom: 1px solid #f0f0f0;
|
border-bottom: 1px solid var(--app-border-color, #f0f0f0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-table-container .ant-table-tbody > tr:not(.row-selected):not(.ant-table-row-selected):hover > td {
|
.list-table-container .ant-table-tbody > tr:not(.row-selected):not(.ant-table-row-selected):hover > td {
|
||||||
background: #fff !important;
|
background: var(--app-surface-color, #fff) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-table-container .ant-table-tbody > tr:not(.row-selected):not(.ant-table-row-selected) > td.ant-table-cell-row-hover {
|
.list-table-container .ant-table-tbody > tr:not(.row-selected):not(.ant-table-row-selected) > td.ant-table-cell-row-hover {
|
||||||
background: #fff !important;
|
background: var(--app-surface-color, #fff) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-table-container .ant-table-tbody > tr.row-selected > td.ant-table-cell-row-hover {
|
.list-table-container .ant-table-tbody > tr.row-selected > td.ant-table-cell-row-hover {
|
||||||
|
|
@ -153,7 +153,7 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
background: #fff;
|
background: var(--app-surface-color, #fff);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
gap: 0;
|
gap: 0;
|
||||||
background: #fafafa;
|
background: var(--app-bg-layout, #f5f6fa);
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-container.page-container {
|
.page-container.page-container {
|
||||||
|
|
@ -20,10 +20,10 @@
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
padding: 16px 16px 0;
|
padding: 16px 16px 0;
|
||||||
border: 1px solid #e6e6e6;
|
border: 1px solid var(--app-border-color, #e6e6e6);
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
border-radius: 4px 4px 0 0;
|
border-radius: 4px 4px 0 0;
|
||||||
background: #fff;
|
background: var(--app-surface-color, #fff);
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-container__header--actions-only {
|
.page-container__header--actions-only {
|
||||||
|
|
@ -41,7 +41,7 @@
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
line-height: 28px;
|
line-height: 28px;
|
||||||
color: #333333;
|
color: var(--app-text-main, #333333);
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -52,7 +52,7 @@
|
||||||
top: 6px;
|
top: 6px;
|
||||||
width: 4px;
|
width: 4px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
background: #3c70f5;
|
background: var(--app-primary-color, #3c70f5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-container__subtitle.ant-typography {
|
.page-container__subtitle.ant-typography {
|
||||||
|
|
@ -61,7 +61,7 @@
|
||||||
padding-bottom: 16px;
|
padding-bottom: 16px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
color: #9095a1;
|
color: var(--app-text-secondary, #9095a1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-container__header-extra {
|
.page-container__header-extra {
|
||||||
|
|
@ -78,9 +78,9 @@
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0 16px 8px;
|
padding: 0 16px 8px;
|
||||||
border-left: 1px solid #e6e6e6;
|
border-left: 1px solid var(--app-border-color, #e6e6e6);
|
||||||
border-right: 1px solid #e6e6e6;
|
border-right: 1px solid var(--app-border-color, #e6e6e6);
|
||||||
background: #fff;
|
background: var(--app-surface-color, #fff);
|
||||||
}
|
}
|
||||||
|
|
||||||
.page-container__body {
|
.page-container__body {
|
||||||
|
|
@ -89,10 +89,10 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 0 16px 12px;
|
padding: 0 16px 12px;
|
||||||
border: 1px solid #e6e6e6;
|
border: 1px solid var(--app-border-color, #e6e6e6);
|
||||||
border-top: none;
|
border-top: none;
|
||||||
border-radius: 0 0 4px 4px;
|
border-radius: 0 0 4px 4px;
|
||||||
background: #fff;
|
background: var(--app-surface-color, #fff);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,9 @@
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
border: 1px solid #e6e6e6;
|
border: 1px solid var(--app-border-color, #e6e6e6);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
background-color: #fff;
|
background-color: var(--app-surface-color, #fff);
|
||||||
background-image: url("../../../assets/home/mask.png");
|
background-image: url("../../../assets/home/mask.png");
|
||||||
background-position: right top;
|
background-position: right top;
|
||||||
background-size: contain;
|
background-size: contain;
|
||||||
|
|
@ -43,7 +43,7 @@
|
||||||
.section-card__title {
|
.section-card__title {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding-bottom: 8px;
|
padding-bottom: 8px;
|
||||||
color: #333;
|
color: var(--app-text-main, #333);
|
||||||
font-family: "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;
|
font-family: "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
|
@ -63,12 +63,12 @@
|
||||||
width: 4px;
|
width: 4px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
border-radius: 1px;
|
border-radius: 1px;
|
||||||
background: #3c70f5;
|
background: var(--app-primary-color, #3c70f5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-card__description {
|
.section-card__description {
|
||||||
padding: 0 0 16px 12px;
|
padding: 0 0 16px 12px;
|
||||||
color: #9095a1;
|
color: var(--app-text-secondary, #9095a1);
|
||||||
font-family: "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;
|
font-family: "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
|
|
@ -138,11 +138,11 @@
|
||||||
.section-card__tabs .ant-tabs-tab.ant-tabs-tab-active:active {
|
.section-card__tabs .ant-tabs-tab.ant-tabs-tab-active:active {
|
||||||
border: none !important;
|
border: none !important;
|
||||||
border-radius: 0 !important;
|
border-radius: 0 !important;
|
||||||
background-color: #e9eef8 !important;
|
background-color: var(--app-bg-surface-soft, #e9eef8) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-card__tabs .ant-tabs-tab-btn {
|
.section-card__tabs .ant-tabs-tab-btn {
|
||||||
color: #333;
|
color: var(--app-text-main, #333);
|
||||||
font-family: "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;
|
font-family: "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
line-height: 22px;
|
line-height: 22px;
|
||||||
|
|
@ -151,7 +151,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-card__tabs .ant-tabs-tab.ant-tabs-tab-active .ant-tabs-tab-btn {
|
.section-card__tabs .ant-tabs-tab.ant-tabs-tab-active .ant-tabs-tab-btn {
|
||||||
color: #1677ff !important;
|
color: var(--app-primary-color, #1677ff) !important;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -167,7 +167,7 @@
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
background-color: #f9fafe;
|
background-color: var(--app-bg-surface-soft, #f9fafe);
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-card--auto .section-card__content {
|
.section-card--auto .section-card__content {
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { fetchDictItemsByTypeCode } from '../api/dict';
|
import { fetchDictItemsByTypeCode } from '../api/dict';
|
||||||
import { SysDictItem } from '../types';
|
import type { SysDictItem } from '../types';
|
||||||
|
|
||||||
const dictCache: Record<string, SysDictItem[]> = {};
|
const dictCache: Record<string, SysDictItem[]> = {};
|
||||||
const pendingRequests: Record<string, Promise<SysDictItem[]>[]> = {};
|
|
||||||
|
|
||||||
export function useDict(typeCode: string) {
|
export function useDict(typeCode: string) {
|
||||||
const [items, setItems] = useState<SysDictItem[]>(dictCache[typeCode] || []);
|
const [items, setItems] = useState<SysDictItem[]>(dictCache[typeCode] || []);
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,12 @@
|
||||||
--item-hover-bg: rgba(22, 119, 255, 0.08);
|
--item-hover-bg: rgba(22, 119, 255, 0.08);
|
||||||
--text-color-secondary: #66758f;
|
--text-color-secondary: #66758f;
|
||||||
--link-color: #1677ff;
|
--link-color: #1677ff;
|
||||||
|
--list-table-scroll-y: 100%;
|
||||||
|
--meeting-status-border: rgba(22, 119, 255, 0.2);
|
||||||
|
--meeting-status-bg: rgba(22, 119, 255, 0.08);
|
||||||
|
--meeting-status-color: #1677ff;
|
||||||
|
--meeting-source-color: #3b82f6;
|
||||||
|
--meeting-progress-color: #1677ff;
|
||||||
--app-form-drawer-width: 600px;
|
--app-form-drawer-width: 600px;
|
||||||
--app-form-drawer-max-width: calc(100vw - 48px);
|
--app-form-drawer-max-width: calc(100vw - 48px);
|
||||||
}
|
}
|
||||||
|
|
@ -51,6 +57,12 @@
|
||||||
--item-hover-bg: rgba(22, 119, 255, 0.08);
|
--item-hover-bg: rgba(22, 119, 255, 0.08);
|
||||||
--text-color-secondary: #5b6474;
|
--text-color-secondary: #5b6474;
|
||||||
--link-color: #1677ff;
|
--link-color: #1677ff;
|
||||||
|
--list-table-scroll-y: 100%;
|
||||||
|
--meeting-status-border: rgba(22, 119, 255, 0.2);
|
||||||
|
--meeting-status-bg: rgba(22, 119, 255, 0.08);
|
||||||
|
--meeting-status-color: #1677ff;
|
||||||
|
--meeting-source-color: #3b82f6;
|
||||||
|
--meeting-progress-color: #1677ff;
|
||||||
}
|
}
|
||||||
|
|
||||||
:root[data-theme="tech"] {
|
:root[data-theme="tech"] {
|
||||||
|
|
@ -76,6 +88,12 @@
|
||||||
--item-hover-bg: rgba(88, 151, 255, 0.18);
|
--item-hover-bg: rgba(88, 151, 255, 0.18);
|
||||||
--text-color-secondary: rgba(190, 206, 229, 0.74);
|
--text-color-secondary: rgba(190, 206, 229, 0.74);
|
||||||
--link-color: #60a5fa;
|
--link-color: #60a5fa;
|
||||||
|
--list-table-scroll-y: 100%;
|
||||||
|
--meeting-status-border: rgba(96, 165, 250, 0.28);
|
||||||
|
--meeting-status-bg: rgba(96, 165, 250, 0.14);
|
||||||
|
--meeting-status-color: #60a5fa;
|
||||||
|
--meeting-source-color: #60a5fa;
|
||||||
|
--meeting-progress-color: #60a5fa;
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
|
|
@ -1325,6 +1343,12 @@ body::after {
|
||||||
--app-text-muted: #9095a1;
|
--app-text-muted: #9095a1;
|
||||||
--text-color-secondary: #9095a1;
|
--text-color-secondary: #9095a1;
|
||||||
--link-color: #1677ff;
|
--link-color: #1677ff;
|
||||||
|
--list-table-scroll-y: 100%;
|
||||||
|
--meeting-status-border: rgba(22, 119, 255, 0.18);
|
||||||
|
--meeting-status-bg: rgba(22, 119, 255, 0.08);
|
||||||
|
--meeting-status-color: #1677ff;
|
||||||
|
--meeting-source-color: #3b82f6;
|
||||||
|
--meeting-progress-color: #1677ff;
|
||||||
--app-form-drawer-width: 600px;
|
--app-form-drawer-width: 600px;
|
||||||
--app-form-drawer-max-width: calc(100vw - 48px);
|
--app-form-drawer-max-width: calc(100vw - 48px);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -329,16 +329,6 @@ export default function AppLayout() {
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
let profile: { isPlatformAdmin?: boolean } = {};
|
|
||||||
try {
|
|
||||||
const stored = sessionStorage.getItem("userProfile");
|
|
||||||
if (stored) {
|
|
||||||
profile = JSON.parse(stored) || {};
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
profile = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
items.push({ type: "divider", key: "divider" });
|
items.push({ type: "divider", key: "divider" });
|
||||||
items.push({
|
items.push({
|
||||||
key: "logout",
|
key: "logout",
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ import PageContainer from "@/components/shared/PageContainer";
|
||||||
import DataListPanel from "@/components/shared/DataListPanel";
|
import DataListPanel from "@/components/shared/DataListPanel";
|
||||||
import SectionCard from "@/components/shared/SectionCard";
|
import SectionCard from "@/components/shared/SectionCard";
|
||||||
import { getStandardPagination } from "@/utils/pagination";
|
import { getStandardPagination } from "@/utils/pagination";
|
||||||
import type { RoleDataScope, SysOrg, SysPermission, SysRole, SysTenant, SysUser } from "@/types";
|
import type { SysOrg, SysPermission, SysRole, SysTenant, SysUser } from "@/types";
|
||||||
import "./index.less";
|
import "./index.less";
|
||||||
|
|
||||||
const { Text, Title } = Typography;
|
const { Text, Title } = Typography;
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ import {
|
||||||
Select,
|
Select,
|
||||||
Space,
|
Space,
|
||||||
Switch,
|
Switch,
|
||||||
Table,
|
|
||||||
Tag,
|
Tag,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
TreeSelect,
|
TreeSelect,
|
||||||
|
|
@ -267,8 +266,6 @@ export default function Users() {
|
||||||
);
|
);
|
||||||
}, [data, searchText]);
|
}, [data, searchText]);
|
||||||
|
|
||||||
const activeFilterLabel = filterTenantId ? tenantMap[filterTenantId] || `Tenant ${filterTenantId}` : "全部租户";
|
|
||||||
|
|
||||||
const openCreate = () => {
|
const openCreate = () => {
|
||||||
setEditing(null);
|
setEditing(null);
|
||||||
setRoleSelectOpen(false);
|
setRoleSelectOpen(false);
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,6 @@ const AiModels: React.FC = () => {
|
||||||
const [fetchLoading, setFetchLoading] = useState(false);
|
const [fetchLoading, setFetchLoading] = useState(false);
|
||||||
const [connectivityLoading, setConnectivityLoading] = useState(false);
|
const [connectivityLoading, setConnectivityLoading] = useState(false);
|
||||||
const [remoteModels, setRemoteModels] = useState<string[]>([]);
|
const [remoteModels, setRemoteModels] = useState<string[]>([]);
|
||||||
const [speakerModels, setSpeakerModels] = useState<string[]>([]);
|
|
||||||
const [createConfig, setCreateConfig] = useState<MeetingCreateConfig>(DEFAULT_CREATE_CONFIG);
|
const [createConfig, setCreateConfig] = useState<MeetingCreateConfig>(DEFAULT_CREATE_CONFIG);
|
||||||
const modelNameAutoFilledRef = useRef(false);
|
const modelNameAutoFilledRef = useRef(false);
|
||||||
const localProfileLoadedRef = useRef(false);
|
const localProfileLoadedRef = useRef(false);
|
||||||
|
|
@ -151,7 +150,6 @@ const AiModels: React.FC = () => {
|
||||||
|
|
||||||
const openDrawer = (record?: AiModelVO) => {
|
const openDrawer = (record?: AiModelVO) => {
|
||||||
setRemoteModels([]);
|
setRemoteModels([]);
|
||||||
setSpeakerModels([]);
|
|
||||||
modelNameAutoFilledRef.current = false;
|
modelNameAutoFilledRef.current = false;
|
||||||
localProfileLoadedRef.current = false;
|
localProfileLoadedRef.current = false;
|
||||||
|
|
||||||
|
|
@ -181,7 +179,6 @@ const AiModels: React.FC = () => {
|
||||||
setRemoteModels([record.modelCode]);
|
setRemoteModels([record.modelCode]);
|
||||||
}
|
}
|
||||||
if (speakerModel) {
|
if (speakerModel) {
|
||||||
setSpeakerModels([String(speakerModel)]);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setEditingId(null);
|
setEditingId(null);
|
||||||
|
|
@ -255,9 +252,7 @@ const AiModels: React.FC = () => {
|
||||||
|
|
||||||
const applyLocalProfile = (profile: AiLocalProfileVO, baseUrl: string) => {
|
const applyLocalProfile = (profile: AiLocalProfileVO, baseUrl: string) => {
|
||||||
const nextRemoteModels = Array.isArray(profile.asrModels) ? profile.asrModels : [];
|
const nextRemoteModels = Array.isArray(profile.asrModels) ? profile.asrModels : [];
|
||||||
const nextSpeakerModels = Array.isArray(profile.speakerModels) ? profile.speakerModels : [];
|
|
||||||
setRemoteModels(nextRemoteModels);
|
setRemoteModels(nextRemoteModels);
|
||||||
setSpeakerModels(nextSpeakerModels);
|
|
||||||
|
|
||||||
const nextValues: Record<string, unknown> = {};
|
const nextValues: Record<string, unknown> = {};
|
||||||
if (profile.activeAsrModel) {
|
if (profile.activeAsrModel) {
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
import { App, Button, Input, Space, Tag, Typography, Upload } from "antd";
|
import { App, Button, Input, Space, Tag, Typography } from "antd";
|
||||||
import type { ColumnsType } from "antd/es/table";
|
import type { ColumnsType } from "antd/es/table";
|
||||||
import { CheckCircleOutlined, ClockCircleOutlined, KeyOutlined, LinkOutlined, ReloadOutlined, SearchOutlined, UploadOutlined } from "@ant-design/icons";
|
import { CheckCircleOutlined, ClockCircleOutlined, KeyOutlined, LinkOutlined, ReloadOutlined, SearchOutlined } from "@ant-design/icons";
|
||||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import PageContainer from "@/components/shared/PageContainer";
|
import PageContainer from "@/components/shared/PageContainer";
|
||||||
import AppPagination from "@/components/shared/AppPagination";
|
import AppPagination from "@/components/shared/AppPagination";
|
||||||
import DataListPanel from "@/components/shared/DataListPanel";
|
import DataListPanel from "@/components/shared/DataListPanel";
|
||||||
import ListTable from "@/components/shared/ListTable/ListTable";
|
import ListTable from "@/components/shared/ListTable/ListTable";
|
||||||
import SectionCard from "@/components/shared/SectionCard";
|
import SectionCard from "@/components/shared/SectionCard";
|
||||||
import { importLicenses, listLicenses, type LicenseImportResultVO, type LicenseVO } from "@/api/business/license";
|
import { listLicenses, type LicenseVO } from "@/api/business/license";
|
||||||
import "./LicenseManagement.css";
|
import "./LicenseManagement.css";
|
||||||
|
|
||||||
const { Text } = Typography;
|
const { Text } = Typography;
|
||||||
|
|
@ -38,9 +38,8 @@ function formatDateTime(value?: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function LicenseManagement() {
|
export default function LicenseManagement() {
|
||||||
const { message } = App.useApp();
|
App.useApp();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [uploading, setUploading] = useState(false);
|
|
||||||
const [records, setRecords] = useState<LicenseVO[]>([]);
|
const [records, setRecords] = useState<LicenseVO[]>([]);
|
||||||
const [searchValue, setSearchValue] = useState("");
|
const [searchValue, setSearchValue] = useState("");
|
||||||
const [page, setPage] = useState(1);
|
const [page, setPage] = useState(1);
|
||||||
|
|
@ -93,17 +92,6 @@ export default function LicenseManagement() {
|
||||||
available: records.filter((item) => item.licenseStatus === 1).length,
|
available: records.filter((item) => item.licenseStatus === 1).length,
|
||||||
}), [records]);
|
}), [records]);
|
||||||
|
|
||||||
const handleImport = async (file: File) => {
|
|
||||||
setUploading(true);
|
|
||||||
try {
|
|
||||||
const result: LicenseImportResultVO = await importLicenses(file);
|
|
||||||
message.success(`导入完成:${result.totalCount} 条,替换 ${result.replacedCount} 条`);
|
|
||||||
await loadData();
|
|
||||||
} finally {
|
|
||||||
setUploading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const columns: ColumnsType<LicenseVO> = [
|
const columns: ColumnsType<LicenseVO> = [
|
||||||
{
|
{
|
||||||
title: "授权标识",
|
title: "授权标识",
|
||||||
|
|
@ -190,11 +178,6 @@ export default function LicenseManagement() {
|
||||||
<Button icon={<ReloadOutlined />} onClick={() => void loadData()} loading={loading}>
|
<Button icon={<ReloadOutlined />} onClick={() => void loadData()} loading={loading}>
|
||||||
刷新
|
刷新
|
||||||
</Button>
|
</Button>
|
||||||
{/*<Upload showUploadList={false} beforeUpload={(file) => { void handleImport(file as File); return Upload.LIST_IGNORE; }}>*/}
|
|
||||||
{/* <Button type="primary" icon={<UploadOutlined />} loading={uploading}>*/}
|
|
||||||
{/* 导入正式授权*/}
|
|
||||||
{/* </Button>*/}
|
|
||||||
{/*</Upload>*/}
|
|
||||||
</Space>
|
</Space>
|
||||||
}
|
}
|
||||||
footer={
|
footer={
|
||||||
|
|
|
||||||
|
|
@ -14,10 +14,8 @@ import {
|
||||||
LinkOutlined,
|
LinkOutlined,
|
||||||
LoadingOutlined,
|
LoadingOutlined,
|
||||||
PauseOutlined,
|
PauseOutlined,
|
||||||
RobotOutlined,
|
|
||||||
SyncOutlined,
|
SyncOutlined,
|
||||||
UserOutlined,
|
UserOutlined,
|
||||||
PlusOutlined,
|
|
||||||
CheckCircleFilled,
|
CheckCircleFilled,
|
||||||
FilePdfOutlined,
|
FilePdfOutlined,
|
||||||
FileWordOutlined,
|
FileWordOutlined,
|
||||||
|
|
@ -372,13 +370,6 @@ type MeetingProgressPhase = 'queued' | 'asr' | 'chapter' | 'summary' | 'terminal
|
||||||
|
|
||||||
const meetingProgressTerminalRefreshCache = new Map<number, string>();
|
const meetingProgressTerminalRefreshCache = new Map<number, string>();
|
||||||
const meetingProgressPhaseRefreshCache = new Map<number, MeetingProgressPhase>();
|
const meetingProgressPhaseRefreshCache = new Map<number, MeetingProgressPhase>();
|
||||||
const DETAIL_STAGE_STEP_ITEMS = [
|
|
||||||
{ code: 'INITIALIZING', label: '数据初始化', hint: '完成会议数据准备' },
|
|
||||||
{ code: 'TRANSCRIBING', label: '转译音频', hint: '完成语音转写' },
|
|
||||||
{ code: 'SUMMARIZING', label: '生成总结', hint: '完成 AI 内容处理' },
|
|
||||||
{ code: 'COMPLETED', label: '处理完成', hint: '已全部完成' },
|
|
||||||
] as const;
|
|
||||||
|
|
||||||
const resolveProgressPhase = (progress: MeetingProgress | null | undefined): MeetingProgressPhase => {
|
const resolveProgressPhase = (progress: MeetingProgress | null | undefined): MeetingProgressPhase => {
|
||||||
const unifiedStatusCode = progress?.unifiedStatus?.statusCode;
|
const unifiedStatusCode = progress?.unifiedStatus?.statusCode;
|
||||||
if (unifiedStatusCode?.startsWith('FAILED_') || unifiedStatusCode === 'COMPLETED') {
|
if (unifiedStatusCode?.startsWith('FAILED_') || unifiedStatusCode === 'COMPLETED') {
|
||||||
|
|
@ -489,7 +480,6 @@ const MeetingProgressDisplay: React.FC<{
|
||||||
const isError = percent < 0;
|
const isError = percent < 0;
|
||||||
const unifiedStatusText = progress?.unifiedStatus?.statusText;
|
const unifiedStatusText = progress?.unifiedStatus?.statusText;
|
||||||
const unifiedStatusMessage = progress?.unifiedStatus?.message;
|
const unifiedStatusMessage = progress?.unifiedStatus?.message;
|
||||||
const primaryStatusText = unifiedStatusText || (isError ? '处理失败' : '处理中');
|
|
||||||
|
|
||||||
const formatEta = (seconds?: number) => {
|
const formatEta = (seconds?: number) => {
|
||||||
if (!seconds || seconds <= 0) return '计算中';
|
if (!seconds || seconds <= 0) return '计算中';
|
||||||
|
|
@ -701,8 +691,6 @@ const UnifiedMeetingProgressDisplay: React.FC<{
|
||||||
const isFailedStage = unifiedStatusCode?.startsWith('FAILED_') || isError;
|
const isFailedStage = unifiedStatusCode?.startsWith('FAILED_') || isError;
|
||||||
const isCompletedStage = unifiedStatusCode === 'COMPLETED' || percent === 100;
|
const isCompletedStage = unifiedStatusCode === 'COMPLETED' || percent === 100;
|
||||||
const progressText = isError ? '--' : `${Math.max(percent, 0)}%`;
|
const progressText = isError ? '--' : `${Math.max(percent, 0)}%`;
|
||||||
const currentStageLabel = DETAIL_STAGE_STEP_DISPLAY_ITEMS[currentStageIndex]?.label || primaryStatusText;
|
|
||||||
const currentStageDisplay = isFailedStage ? `${currentStageLabel}失败` : currentStageLabel;
|
|
||||||
const helperText = unifiedStatusMessage || progress?.message || (isError ? '当前阶段执行失败,请稍后重试。' : '阶段状态已与安卓端统一。');
|
const helperText = unifiedStatusMessage || progress?.message || (isError ? '当前阶段执行失败,请稍后重试。' : '阶段状态已与安卓端统一。');
|
||||||
|
|
||||||
const renderStageTimeline = (options?: {
|
const renderStageTimeline = (options?: {
|
||||||
|
|
@ -1223,8 +1211,6 @@ const MeetingDetail: React.FC = () => {
|
||||||
const [downloadLoading, setDownloadLoading] = useState<'pdf' | 'word' | 'transcript' | null>(null);
|
const [downloadLoading, setDownloadLoading] = useState<'pdf' | 'word' | 'transcript' | null>(null);
|
||||||
const [isEditingSummary, setIsEditingSummary] = useState(false);
|
const [isEditingSummary, setIsEditingSummary] = useState(false);
|
||||||
const [summaryDraft, setSummaryDraft] = useState('');
|
const [summaryDraft, setSummaryDraft] = useState('');
|
||||||
const [expandKeywords, setExpandKeywords] = useState(false);
|
|
||||||
const [expandSummary, setExpandSummary] = useState(false);
|
|
||||||
const [selectedKeywords, setSelectedKeywords] = useState<string[]>([]);
|
const [selectedKeywords, setSelectedKeywords] = useState<string[]>([]);
|
||||||
const [workspaceTab, setWorkspaceTab] = useState<WorkspaceTab>('transcript');
|
const [workspaceTab, setWorkspaceTab] = useState<WorkspaceTab>('transcript');
|
||||||
const [addingHotwords, setAddingHotwords] = useState(false);
|
const [addingHotwords, setAddingHotwords] = useState(false);
|
||||||
|
|
@ -1286,14 +1272,7 @@ const MeetingDetail: React.FC = () => {
|
||||||
() => buildMeetingAnalysis(meeting?.analysis, meeting?.summaryContent, meeting?.tags || ''),
|
() => buildMeetingAnalysis(meeting?.analysis, meeting?.summaryContent, meeting?.tags || ''),
|
||||||
[meeting?.analysis, meeting?.summaryContent, meeting?.tags],
|
[meeting?.analysis, meeting?.summaryContent, meeting?.tags],
|
||||||
);
|
);
|
||||||
const hasAnalysis = !!(
|
const expandKeywords = false;
|
||||||
analysis.overview ||
|
|
||||||
analysis.keywords.length ||
|
|
||||||
analysis.chapters.length ||
|
|
||||||
analysis.speakerSummaries.length ||
|
|
||||||
analysis.keyPoints.length ||
|
|
||||||
analysis.todos.length
|
|
||||||
);
|
|
||||||
const visibleKeywords = expandKeywords ? analysis.keywords : analysis.keywords.slice(0, 9);
|
const visibleKeywords = expandKeywords ? analysis.keywords : analysis.keywords.slice(0, 9);
|
||||||
const meetingTags = useMemo(
|
const meetingTags = useMemo(
|
||||||
() => (meeting?.tags?.split(',').map((item) => item.trim()).filter(Boolean) || []),
|
() => (meeting?.tags?.split(',').map((item) => item.trim()).filter(Boolean) || []),
|
||||||
|
|
@ -1386,10 +1365,6 @@ const MeetingDetail: React.FC = () => {
|
||||||
const meetingId = meeting?.id ?? (id ? Number(id) : NaN);
|
const meetingId = meeting?.id ?? (id ? Number(id) : NaN);
|
||||||
return buildMeetingPreviewUrl(meetingShareBaseUrl, meetingId);
|
return buildMeetingPreviewUrl(meetingShareBaseUrl, meetingId);
|
||||||
}, [meetingShareBaseUrl, meeting?.id, id]);
|
}, [meetingShareBaseUrl, meeting?.id, id]);
|
||||||
const meetingPreviewUrl = useMemo(() => {
|
|
||||||
const meetingId = meeting?.id ?? (id ? Number(id) : NaN);
|
|
||||||
return buildMeetingPreviewUrl(meetingShareBaseUrl, meetingId, previewAccessPassword);
|
|
||||||
}, [meetingShareBaseUrl, meeting?.id, id, previewAccessPassword]);
|
|
||||||
const summaryModelDisplayName = useMemo(() => {
|
const summaryModelDisplayName = useMemo(() => {
|
||||||
const matchedModel = llmModels.find((item) => item.id === meeting?.summaryModelId);
|
const matchedModel = llmModels.find((item) => item.id === meeting?.summaryModelId);
|
||||||
if (matchedModel?.modelName) {
|
if (matchedModel?.modelName) {
|
||||||
|
|
@ -1825,23 +1800,6 @@ const MeetingDetail: React.FC = () => {
|
||||||
audioRef.current.play();
|
audioRef.current.play();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleKeywordClick = useCallback((keyword: string) => {
|
|
||||||
const firstMatch = transcripts.find((item) =>
|
|
||||||
item.content.toLowerCase().includes(keyword.toLowerCase())
|
|
||||||
);
|
|
||||||
|
|
||||||
if (firstMatch) {
|
|
||||||
setWorkspaceTab('transcript');
|
|
||||||
setLinkedTranscriptIds([]);
|
|
||||||
setLinkedChapterKey(null);
|
|
||||||
setHighlightKeyword(keyword);
|
|
||||||
seekTo(firstMatch.startTime);
|
|
||||||
message.info(`已跳转至关键词 "${keyword}" 所在位置`);
|
|
||||||
} else {
|
|
||||||
message.warning(`在转录原文中未找到关键词 "${keyword}"`);
|
|
||||||
}
|
|
||||||
}, [transcripts, seekTo, message]);
|
|
||||||
|
|
||||||
const handleTranscriptRowPlay = useCallback((timeMs: number) => {
|
const handleTranscriptRowPlay = useCallback((timeMs: number) => {
|
||||||
setLinkedTranscriptIds([]);
|
setLinkedTranscriptIds([]);
|
||||||
setLinkedChapterKey(null);
|
setLinkedChapterKey(null);
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import { useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { Alert, Button, Empty, Input, Result, Skeleton, Tabs, message } from "antd";
|
import { Alert, Button, Empty, Input, Result, Skeleton, Tabs, message } from "antd";
|
||||||
import { useParams, useSearchParams } from "react-router-dom";
|
import { useParams, useSearchParams } from "react-router-dom";
|
||||||
import {
|
import {
|
||||||
AudioOutlined,
|
|
||||||
CalendarOutlined,
|
CalendarOutlined,
|
||||||
CaretRightFilled,
|
CaretRightFilled,
|
||||||
ClockCircleOutlined,
|
ClockCircleOutlined,
|
||||||
|
|
@ -32,7 +31,6 @@ import {
|
||||||
import { buildMeetingAnalysis } from "./meetingAnalysis";
|
import { buildMeetingAnalysis } from "./meetingAnalysis";
|
||||||
import "./MeetingPreview.css";
|
import "./MeetingPreview.css";
|
||||||
|
|
||||||
type AnalysisTab = "chapters" | "speakers" | "actions" | "todos";
|
|
||||||
type PreviewPageTab = "summary" | "catalog" | "transcript";
|
type PreviewPageTab = "summary" | "catalog" | "transcript";
|
||||||
|
|
||||||
const TEXT = {
|
const TEXT = {
|
||||||
|
|
@ -203,7 +201,6 @@ export default function MeetingPreview() {
|
||||||
const [meetingChapters, setMeetingChapters] = useState<MeetingChapterVO[]>([]);
|
const [meetingChapters, setMeetingChapters] = useState<MeetingChapterVO[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [error, setError] = useState("");
|
const [error, setError] = useState("");
|
||||||
const [analysisTab, setAnalysisTab] = useState<AnalysisTab>("speakers");
|
|
||||||
const [pageTab, setPageTab] = useState<PreviewPageTab>("summary");
|
const [pageTab, setPageTab] = useState<PreviewPageTab>("summary");
|
||||||
const [activeTranscriptId, setActiveTranscriptId] = useState<number | null>(null);
|
const [activeTranscriptId, setActiveTranscriptId] = useState<number | null>(null);
|
||||||
const [passwordRequired, setPasswordRequired] = useState(false);
|
const [passwordRequired, setPasswordRequired] = useState(false);
|
||||||
|
|
|
||||||
|
|
@ -1,33 +1,24 @@
|
||||||
import {
|
import {
|
||||||
AppstoreOutlined,
|
AppstoreOutlined,
|
||||||
AudioOutlined,
|
|
||||||
CalendarOutlined,
|
CalendarOutlined,
|
||||||
CheckOutlined,
|
CheckOutlined,
|
||||||
CloudUploadOutlined,
|
CloudUploadOutlined,
|
||||||
DeleteOutlined,
|
DeleteOutlined,
|
||||||
EditOutlined,
|
|
||||||
FilterOutlined,
|
FilterOutlined,
|
||||||
InfoCircleOutlined,
|
InfoCircleOutlined,
|
||||||
PauseCircleOutlined,
|
PauseCircleOutlined,
|
||||||
PlusOutlined,
|
PlusOutlined,
|
||||||
SearchOutlined,
|
|
||||||
SettingOutlined,
|
|
||||||
SyncOutlined,
|
SyncOutlined,
|
||||||
TeamOutlined,
|
|
||||||
UnorderedListOutlined,
|
UnorderedListOutlined,
|
||||||
UserOutlined,
|
UserOutlined,
|
||||||
} from "@ant-design/icons";
|
} from "@ant-design/icons";
|
||||||
import {
|
import {
|
||||||
App,
|
App,
|
||||||
Avatar,
|
|
||||||
Button,
|
Button,
|
||||||
Card,
|
Card,
|
||||||
Dropdown,
|
|
||||||
Empty,
|
Empty,
|
||||||
Form,
|
|
||||||
Input,
|
Input,
|
||||||
List,
|
List,
|
||||||
Modal,
|
|
||||||
Popconfirm,
|
Popconfirm,
|
||||||
Radio,
|
Radio,
|
||||||
Select,
|
Select,
|
||||||
|
|
@ -39,7 +30,6 @@ import {
|
||||||
} from "antd";
|
} from "antd";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import React, { useEffect, useRef, useState } from "react";
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import { useNavigate, useSearchParams } from "react-router-dom";
|
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||||
|
|
||||||
import { listUsers } from "../../api";
|
import { listUsers } from "../../api";
|
||||||
|
|
@ -55,19 +45,16 @@ import {
|
||||||
type MeetingVO,
|
type MeetingVO,
|
||||||
type RealtimeMeetingSessionStatus,
|
type RealtimeMeetingSessionStatus,
|
||||||
retryScheduleMeeting,
|
retryScheduleMeeting,
|
||||||
updateMeetingParticipants,
|
|
||||||
} from "../../api/business/meeting";
|
} from "../../api/business/meeting";
|
||||||
import { MeetingCreateDrawer, type MeetingCreateType } from "../../components/business/MeetingCreateDrawer";
|
import { MeetingCreateDrawer, type MeetingCreateType } from "../../components/business/MeetingCreateDrawer";
|
||||||
import AppPagination from "../../components/shared/AppPagination";
|
import AppPagination from "../../components/shared/AppPagination";
|
||||||
import DataListPanel from "../../components/shared/DataListPanel";
|
import DataListPanel from "../../components/shared/DataListPanel";
|
||||||
import SectionCard from "../../components/shared/SectionCard";
|
import SectionCard from "../../components/shared/SectionCard";
|
||||||
import { usePermission } from "../../hooks/usePermission";
|
|
||||||
import type { SysUser } from "../../types";
|
import type { SysUser } from "../../types";
|
||||||
import PageContainer from "../../components/shared/PageContainer";
|
import PageContainer from "../../components/shared/PageContainer";
|
||||||
import "./Meetings.css";
|
import "./Meetings.css";
|
||||||
|
|
||||||
const { Title, Text } = Typography;
|
const { Title, Text } = Typography;
|
||||||
const { Option } = Select;
|
|
||||||
const { Search } = Input;
|
const { Search } = Input;
|
||||||
|
|
||||||
const CURRENT_PLATFORM = "WEB" as const;
|
const CURRENT_PLATFORM = "WEB" as const;
|
||||||
|
|
@ -132,11 +119,6 @@ const isUnifiedTerminalProgress = (progress?: MeetingProgress | null) =>
|
||||||
|| progress.unifiedStatus?.statusCode?.startsWith("FAILED_")
|
|| progress.unifiedStatus?.statusCode?.startsWith("FAILED_")
|
||||||
);
|
);
|
||||||
|
|
||||||
const shouldPollMeetingCard = (item: MeetingVO) =>
|
|
||||||
shouldTrackGenerationProgress(item)
|
|
||||||
|| item.realtimeSessionStatus === "ACTIVE"
|
|
||||||
|| isPausedRealtimeSessionStatus(item.realtimeSessionStatus);
|
|
||||||
|
|
||||||
const getUnifiedStatusCode = (progress: MeetingProgress | null | undefined) =>
|
const getUnifiedStatusCode = (progress: MeetingProgress | null | undefined) =>
|
||||||
progress?.unifiedStatus?.statusCode;
|
progress?.unifiedStatus?.statusCode;
|
||||||
|
|
||||||
|
|
@ -422,9 +404,7 @@ const MeetingCardItem: React.FC<{
|
||||||
|
|
||||||
const Meetings: React.FC = () => {
|
const Meetings: React.FC = () => {
|
||||||
const { message } = App.useApp();
|
const { message } = App.useApp();
|
||||||
const { t } = useTranslation();
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { can } = usePermission();
|
|
||||||
const [searchParams, setSearchParams] = useSearchParams();
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [data, setData] = useState<MeetingVO[]>([]);
|
const [data, setData] = useState<MeetingVO[]>([]);
|
||||||
|
|
@ -445,7 +425,7 @@ const Meetings: React.FC = () => {
|
||||||
realtimeEnabled: false,
|
realtimeEnabled: false,
|
||||||
offlineAudioMaxSizeMb: 1024,
|
offlineAudioMaxSizeMb: 1024,
|
||||||
});
|
});
|
||||||
const [userList, setUserList] = useState<SysUser[]>([]);
|
const [, setUserList] = useState<SysUser[]>([]);
|
||||||
const progressTerminalRefreshRef = useRef<Map<number, string>>(new Map());
|
const progressTerminalRefreshRef = useRef<Map<number, string>>(new Map());
|
||||||
const [retryingMeetingIds, setRetryingMeetingIds] = useState<Record<number, boolean>>({});
|
const [retryingMeetingIds, setRetryingMeetingIds] = useState<Record<number, boolean>>({});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { useEffect, useMemo, useRef, useState } from "react";
|
import { useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { Alert, Avatar, Badge, Button, Card, Empty, Space, Typography, App } from 'antd';
|
import { Alert, Avatar, Badge, Button, Card, Empty, Space, App } from 'antd';
|
||||||
import {
|
import {
|
||||||
AudioOutlined,
|
AudioOutlined,
|
||||||
AudioMutedOutlined,
|
AudioMutedOutlined,
|
||||||
|
|
@ -26,7 +26,6 @@ import {
|
||||||
type RealtimeMeetingSessionStatus,
|
type RealtimeMeetingSessionStatus,
|
||||||
type RealtimeSocketSessionVO,
|
type RealtimeSocketSessionVO,
|
||||||
} from "../../api/business/meeting";
|
} from "../../api/business/meeting";
|
||||||
const { Text } = Typography;
|
|
||||||
const SAMPLE_RATE = 16000;
|
const SAMPLE_RATE = 16000;
|
||||||
const CHUNK_SIZE = 1280;
|
const CHUNK_SIZE = 1280;
|
||||||
const CURRENT_PLATFORM = "WEB" as const;
|
const CURRENT_PLATFORM = "WEB" as const;
|
||||||
|
|
@ -252,7 +251,6 @@ export function RealtimeAsrSession() {
|
||||||
const sessionStartedRef = useRef(false);
|
const sessionStartedRef = useRef(false);
|
||||||
const elapsedOffsetRef = useRef(0);
|
const elapsedOffsetRef = useRef(0);
|
||||||
|
|
||||||
const finalTranscriptCount = transcripts.length;
|
|
||||||
const totalTranscriptChars = useMemo(
|
const totalTranscriptChars = useMemo(
|
||||||
() => transcripts.reduce((sum, item) => sum + item.text.length, 0) + streamingText.length,
|
() => transcripts.reduce((sum, item) => sum + item.text.length, 0) + streamingText.length,
|
||||||
[streamingText, transcripts],
|
[streamingText, transcripts],
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
.dashboard-monitor-page__content {
|
.dashboard-monitor-page__content {
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
padding: 8px 8px 0;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dashboard-monitor-page__stats {
|
.dashboard-monitor-page__stats {
|
||||||
|
|
@ -48,6 +48,14 @@
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dashboard-monitor-page__task-panel .data-list-panel__table-area .app-page__table-wrap {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.dashboard-monitor-page__task-list,
|
.dashboard-monitor-page__task-list,
|
||||||
.dashboard-monitor-page__task-list .ant-list,
|
.dashboard-monitor-page__task-list .ant-list,
|
||||||
.dashboard-monitor-page__task-list .ant-list-items,
|
.dashboard-monitor-page__task-list .ant-list-items,
|
||||||
|
|
@ -57,16 +65,34 @@
|
||||||
|
|
||||||
.dashboard-monitor-page__task-list {
|
.dashboard-monitor-page__task-list {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 100%;
|
height: auto;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
overflow-y: auto;
|
display: flex;
|
||||||
overflow-x: hidden;
|
flex-direction: column;
|
||||||
padding: 0 6px 0 0;
|
overflow: hidden;
|
||||||
|
padding: 0;
|
||||||
overscroll-behavior: contain;
|
overscroll-behavior: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dashboard-monitor-page__task-item {
|
.dashboard-monitor-page__task-list .ant-spin-nested-loading,
|
||||||
padding: 18px 0;
|
.dashboard-monitor-page__task-list .ant-spin-container,
|
||||||
|
.dashboard-monitor-page__task-list .ant-list {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-monitor-page__task-list .ant-list-items {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
padding-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard-monitor-page__task-list .dashboard-monitor-page__task-item {
|
||||||
|
padding: 6px 0;
|
||||||
border-bottom: 1px solid #f0f2f5;
|
border-bottom: 1px solid #f0f2f5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -90,7 +116,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.dashboard-monitor-page__task-tags {
|
.dashboard-monitor-page__task-tags {
|
||||||
margin-top: 8px;
|
margin-top: 4px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import React from "react";
|
|
||||||
import "./RightVisual.less";
|
import "./RightVisual.less";
|
||||||
|
|
||||||
export default function RightVisual() {
|
export default function RightVisual() {
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,9 @@
|
||||||
import { Button, Modal, Slider, Typography, App } from "antd";
|
import { Button, Modal, Slider, App } from "antd";
|
||||||
import { ScissorOutlined } from "@ant-design/icons";
|
import { ScissorOutlined } from "@ant-design/icons";
|
||||||
import { useEffect, useMemo, useRef, useState } from "react";
|
import { useEffect, useMemo, useRef, useState } from "react";
|
||||||
import "./AvatarCropDialog.css";
|
import "./AvatarCropDialog.css";
|
||||||
|
|
||||||
const { Text } = Typography;
|
|
||||||
|
|
||||||
const VIEWPORT_SIZE = 300;
|
const VIEWPORT_SIZE = 300;
|
||||||
const ALLOWED_TYPES = new Map([
|
|
||||||
["image/jpeg", "jpg"],
|
|
||||||
["image/jpg", "jpg"],
|
|
||||||
["image/png", "png"],
|
|
||||||
]);
|
|
||||||
|
|
||||||
export type CropModalState = {
|
export type CropModalState = {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,12 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"composite": true,
|
"target": "ES2021",
|
||||||
|
"lib": ["ES2021"],
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"moduleResolution": "Bundler",
|
"moduleResolution": "Bundler",
|
||||||
"skipLibCheck": true
|
"noEmit": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"types": ["node"]
|
||||||
},
|
},
|
||||||
"include": ["vite.config.ts"]
|
"include": ["vite.config.ts"]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue