feat: 界面优化
parent
b0a3dedb99
commit
3337285135
|
|
@ -10,6 +10,7 @@ lerna-debug.log*
|
|||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.tsbuildinfo
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
"zustand": "^4.5.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.0.10",
|
||||
"@types/react": "^18.2.55",
|
||||
"@types/react-dom": "^18.2.19",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
|
|
@ -1580,6 +1581,16 @@
|
|||
"integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
|
||||
"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": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmmirror.com/@types/pako/-/pako-2.0.4.tgz",
|
||||
|
|
@ -4666,6 +4677,13 @@
|
|||
"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": {
|
||||
"version": "11.0.5",
|
||||
"resolved": "https://registry.npmmirror.com/unified/-/unified-11.0.5.tgz",
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
"zustand": "^4.5.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.0.10",
|
||||
"@types/react": "^18.2.55",
|
||||
"@types/react-dom": "^18.2.19",
|
||||
"@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 zhCN from "antd/locale/zh_CN";
|
||||
import enUS from "antd/locale/en_US";
|
||||
|
|
@ -6,10 +6,8 @@ import { useTranslation } from "react-i18next";
|
|||
import AppRoutes from "./routes";
|
||||
import { getOpenPlatformConfig } from "./api";
|
||||
import { useThemeStore } from "./store/themeStore";
|
||||
import type { SysPlatformConfig } from "./types";
|
||||
|
||||
export default function App() {
|
||||
const [config, setConfig] = useState<SysPlatformConfig | null>(null);
|
||||
const { colorPrimary, themeMode, initTheme } = useThemeStore();
|
||||
const { i18n } = useTranslation();
|
||||
const antdLocale = useMemo(() => (i18n.language === "en-US" ? enUS : zhCN), [i18n.language]);
|
||||
|
|
@ -19,7 +17,6 @@ export default function App() {
|
|||
const fetchConfig = async () => {
|
||||
try {
|
||||
const data = await getOpenPlatformConfig();
|
||||
setConfig(data);
|
||||
if (data.projectName) {
|
||||
document.title = data.projectName;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
width: 100%;
|
||||
min-height: 52px;
|
||||
padding: 8px 0;
|
||||
background: #fff;
|
||||
background: var(--app-surface-color, #fff);
|
||||
box-sizing: border-box;
|
||||
min-width: 0;
|
||||
overflow: visible;
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
.app-pagination-total {
|
||||
flex: 0 0 auto;
|
||||
min-width: 0;
|
||||
color: #333;
|
||||
color: var(--app-text-main, #333);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
|
|
@ -97,7 +97,7 @@
|
|||
align-items: stretch;
|
||||
align-self: center;
|
||||
gap: 6px;
|
||||
color: #333;
|
||||
color: var(--app-text-main, #333);
|
||||
font-size: 14px;
|
||||
line-height: 32px;
|
||||
white-space: nowrap;
|
||||
|
|
@ -119,31 +119,31 @@
|
|||
width: 48px;
|
||||
height: 32px;
|
||||
padding: 0 8px;
|
||||
color: #333;
|
||||
color: var(--app-text-main, #333);
|
||||
font-size: 14px;
|
||||
line-height: 30px;
|
||||
text-align: center;
|
||||
background-color: #fff;
|
||||
border: 1px solid #d9d9d9;
|
||||
background-color: var(--app-surface-color, #fff);
|
||||
border: 1px solid var(--app-border-color, #d9d9d9);
|
||||
border-radius: 4px;
|
||||
outline: none;
|
||||
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.app-pagination-quick-jumper input:hover {
|
||||
border-color: #1677ff;
|
||||
border-color: var(--app-primary-color, #1677ff);
|
||||
}
|
||||
|
||||
.app-pagination-quick-jumper input:focus {
|
||||
border-color: #1677ff;
|
||||
box-shadow: 0 0 0 2px rgba(5, 145, 255, 0.1);
|
||||
border-color: var(--app-primary-color, #1677ff);
|
||||
box-shadow: 0 0 0 2px rgba(var(--app-primary-rgb, 22, 119, 255), 0.12);
|
||||
}
|
||||
|
||||
.app-pagination-quick-jumper input:disabled {
|
||||
color: rgba(0, 0, 0, 0.25);
|
||||
cursor: not-allowed;
|
||||
background-color: rgba(0, 0, 0, 0.04);
|
||||
border-color: #d9d9d9;
|
||||
border-color: var(--app-border-color, #d9d9d9);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { Pagination, PaginationProps } from 'antd';
|
||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { Pagination, type PaginationProps } from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { getMinPageSize, getPageSizeOptions, type PaginationVariant } from '@/utils/pagination';
|
||||
import './index.css';
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@
|
|||
flex: 1;
|
||||
min-height: 0;
|
||||
min-width: 0;
|
||||
background: #fff;
|
||||
background: var(--app-surface-color, #fff);
|
||||
}
|
||||
|
||||
.list-table-container .ant-table-wrapper {
|
||||
background: #fff;
|
||||
background: var(--app-surface-color, #fff);
|
||||
}
|
||||
|
||||
/* 行选中样式 */
|
||||
|
|
@ -32,7 +32,7 @@
|
|||
}
|
||||
|
||||
.selection-count {
|
||||
color: #9095a1;
|
||||
color: var(--app-text-secondary, #9095a1);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
|
|
@ -56,26 +56,26 @@
|
|||
}
|
||||
|
||||
.list-table-container .ant-table {
|
||||
color: #333;
|
||||
color: var(--app-text-main, #333);
|
||||
}
|
||||
|
||||
.list-table-container .ant-table-thead > tr > th {
|
||||
background: #fafafa !important;
|
||||
color: #333;
|
||||
background: var(--app-bg-surface-soft, #fafafa) !important;
|
||||
color: var(--app-text-main, #333);
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
|
|
@ -153,7 +153,7 @@
|
|||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 0;
|
||||
background: #fff;
|
||||
background: var(--app-surface-color, #fff);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
min-height: 0;
|
||||
padding: 16px;
|
||||
gap: 0;
|
||||
background: #fafafa;
|
||||
background: var(--app-bg-layout, #f5f6fa);
|
||||
}
|
||||
|
||||
.page-container.page-container {
|
||||
|
|
@ -20,10 +20,10 @@
|
|||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
padding: 16px 16px 0;
|
||||
border: 1px solid #e6e6e6;
|
||||
border: 1px solid var(--app-border-color, #e6e6e6);
|
||||
border-bottom: none;
|
||||
border-radius: 4px 4px 0 0;
|
||||
background: #fff;
|
||||
background: var(--app-surface-color, #fff);
|
||||
}
|
||||
|
||||
.page-container__header--actions-only {
|
||||
|
|
@ -41,7 +41,7 @@
|
|||
font-weight: 600;
|
||||
font-size: 18px;
|
||||
line-height: 28px;
|
||||
color: #333333;
|
||||
color: var(--app-text-main, #333333);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
|
|
@ -52,7 +52,7 @@
|
|||
top: 6px;
|
||||
width: 4px;
|
||||
height: 16px;
|
||||
background: #3c70f5;
|
||||
background: var(--app-primary-color, #3c70f5);
|
||||
}
|
||||
|
||||
.page-container__subtitle.ant-typography {
|
||||
|
|
@ -61,7 +61,7 @@
|
|||
padding-bottom: 16px;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
color: #9095a1;
|
||||
color: var(--app-text-secondary, #9095a1);
|
||||
}
|
||||
|
||||
.page-container__header-extra {
|
||||
|
|
@ -78,9 +78,9 @@
|
|||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
padding: 0 16px 8px;
|
||||
border-left: 1px solid #e6e6e6;
|
||||
border-right: 1px solid #e6e6e6;
|
||||
background: #fff;
|
||||
border-left: 1px solid var(--app-border-color, #e6e6e6);
|
||||
border-right: 1px solid var(--app-border-color, #e6e6e6);
|
||||
background: var(--app-surface-color, #fff);
|
||||
}
|
||||
|
||||
.page-container__body {
|
||||
|
|
@ -89,10 +89,10 @@
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0 16px 12px;
|
||||
border: 1px solid #e6e6e6;
|
||||
border: 1px solid var(--app-border-color, #e6e6e6);
|
||||
border-top: none;
|
||||
border-radius: 0 0 4px 4px;
|
||||
background: #fff;
|
||||
background: var(--app-surface-color, #fff);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@
|
|||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
padding: 16px;
|
||||
border: 1px solid #e6e6e6;
|
||||
border: 1px solid var(--app-border-color, #e6e6e6);
|
||||
border-radius: 4px;
|
||||
background-color: #fff;
|
||||
background-color: var(--app-surface-color, #fff);
|
||||
background-image: url("../../../assets/home/mask.png");
|
||||
background-position: right top;
|
||||
background-size: contain;
|
||||
|
|
@ -43,7 +43,7 @@
|
|||
.section-card__title {
|
||||
margin: 0;
|
||||
padding-bottom: 8px;
|
||||
color: #333;
|
||||
color: var(--app-text-main, #333);
|
||||
font-family: "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
|
|
@ -63,12 +63,12 @@
|
|||
width: 4px;
|
||||
height: 16px;
|
||||
border-radius: 1px;
|
||||
background: #3c70f5;
|
||||
background: var(--app-primary-color, #3c70f5);
|
||||
}
|
||||
|
||||
.section-card__description {
|
||||
padding: 0 0 16px 12px;
|
||||
color: #9095a1;
|
||||
color: var(--app-text-secondary, #9095a1);
|
||||
font-family: "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
|
|
@ -138,11 +138,11 @@
|
|||
.section-card__tabs .ant-tabs-tab.ant-tabs-tab-active:active {
|
||||
border: none !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 {
|
||||
color: #333;
|
||||
color: var(--app-text-main, #333);
|
||||
font-family: "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
|
|
@ -151,7 +151,7 @@
|
|||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
|
|
@ -167,7 +167,7 @@
|
|||
overflow: hidden;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
background-color: #f9fafe;
|
||||
background-color: var(--app-bg-surface-soft, #f9fafe);
|
||||
}
|
||||
|
||||
.section-card--auto .section-card__content {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
import { useState, useEffect } from 'react';
|
||||
import { fetchDictItemsByTypeCode } from '../api/dict';
|
||||
import { SysDictItem } from '../types';
|
||||
import type { SysDictItem } from '../types';
|
||||
|
||||
const dictCache: Record<string, SysDictItem[]> = {};
|
||||
const pendingRequests: Record<string, Promise<SysDictItem[]>[]> = {};
|
||||
|
||||
export function useDict(typeCode: string) {
|
||||
const [items, setItems] = useState<SysDictItem[]>(dictCache[typeCode] || []);
|
||||
|
|
|
|||
|
|
@ -25,6 +25,12 @@
|
|||
--item-hover-bg: rgba(22, 119, 255, 0.08);
|
||||
--text-color-secondary: #66758f;
|
||||
--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-max-width: calc(100vw - 48px);
|
||||
}
|
||||
|
|
@ -51,6 +57,12 @@
|
|||
--item-hover-bg: rgba(22, 119, 255, 0.08);
|
||||
--text-color-secondary: #5b6474;
|
||||
--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"] {
|
||||
|
|
@ -76,6 +88,12 @@
|
|||
--item-hover-bg: rgba(88, 151, 255, 0.18);
|
||||
--text-color-secondary: rgba(190, 206, 229, 0.74);
|
||||
--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 {
|
||||
|
|
@ -1325,6 +1343,12 @@ body::after {
|
|||
--app-text-muted: #9095a1;
|
||||
--text-color-secondary: #9095a1;
|
||||
--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-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({
|
||||
key: "logout",
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ import PageContainer from "@/components/shared/PageContainer";
|
|||
import DataListPanel from "@/components/shared/DataListPanel";
|
||||
import SectionCard from "@/components/shared/SectionCard";
|
||||
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";
|
||||
|
||||
const { Text, Title } = Typography;
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import {
|
|||
Select,
|
||||
Space,
|
||||
Switch,
|
||||
Table,
|
||||
Tag,
|
||||
Tooltip,
|
||||
TreeSelect,
|
||||
|
|
@ -267,8 +266,6 @@ export default function Users() {
|
|||
);
|
||||
}, [data, searchText]);
|
||||
|
||||
const activeFilterLabel = filterTenantId ? tenantMap[filterTenantId] || `Tenant ${filterTenantId}` : "全部租户";
|
||||
|
||||
const openCreate = () => {
|
||||
setEditing(null);
|
||||
setRoleSelectOpen(false);
|
||||
|
|
|
|||
|
|
@ -73,7 +73,6 @@ const AiModels: React.FC = () => {
|
|||
const [fetchLoading, setFetchLoading] = useState(false);
|
||||
const [connectivityLoading, setConnectivityLoading] = useState(false);
|
||||
const [remoteModels, setRemoteModels] = useState<string[]>([]);
|
||||
const [speakerModels, setSpeakerModels] = useState<string[]>([]);
|
||||
const [createConfig, setCreateConfig] = useState<MeetingCreateConfig>(DEFAULT_CREATE_CONFIG);
|
||||
const modelNameAutoFilledRef = useRef(false);
|
||||
const localProfileLoadedRef = useRef(false);
|
||||
|
|
@ -151,7 +150,6 @@ const AiModels: React.FC = () => {
|
|||
|
||||
const openDrawer = (record?: AiModelVO) => {
|
||||
setRemoteModels([]);
|
||||
setSpeakerModels([]);
|
||||
modelNameAutoFilledRef.current = false;
|
||||
localProfileLoadedRef.current = false;
|
||||
|
||||
|
|
@ -181,7 +179,6 @@ const AiModels: React.FC = () => {
|
|||
setRemoteModels([record.modelCode]);
|
||||
}
|
||||
if (speakerModel) {
|
||||
setSpeakerModels([String(speakerModel)]);
|
||||
}
|
||||
} else {
|
||||
setEditingId(null);
|
||||
|
|
@ -255,9 +252,7 @@ const AiModels: React.FC = () => {
|
|||
|
||||
const applyLocalProfile = (profile: AiLocalProfileVO, baseUrl: string) => {
|
||||
const nextRemoteModels = Array.isArray(profile.asrModels) ? profile.asrModels : [];
|
||||
const nextSpeakerModels = Array.isArray(profile.speakerModels) ? profile.speakerModels : [];
|
||||
setRemoteModels(nextRemoteModels);
|
||||
setSpeakerModels(nextSpeakerModels);
|
||||
|
||||
const nextValues: Record<string, unknown> = {};
|
||||
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 { 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 PageContainer from "@/components/shared/PageContainer";
|
||||
import AppPagination from "@/components/shared/AppPagination";
|
||||
import DataListPanel from "@/components/shared/DataListPanel";
|
||||
import ListTable from "@/components/shared/ListTable/ListTable";
|
||||
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";
|
||||
|
||||
const { Text } = Typography;
|
||||
|
|
@ -38,9 +38,8 @@ function formatDateTime(value?: string) {
|
|||
}
|
||||
|
||||
export default function LicenseManagement() {
|
||||
const { message } = App.useApp();
|
||||
App.useApp();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [uploading, setUploading] = useState(false);
|
||||
const [records, setRecords] = useState<LicenseVO[]>([]);
|
||||
const [searchValue, setSearchValue] = useState("");
|
||||
const [page, setPage] = useState(1);
|
||||
|
|
@ -93,17 +92,6 @@ export default function LicenseManagement() {
|
|||
available: records.filter((item) => item.licenseStatus === 1).length,
|
||||
}), [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> = [
|
||||
{
|
||||
title: "授权标识",
|
||||
|
|
@ -190,11 +178,6 @@ export default function LicenseManagement() {
|
|||
<Button icon={<ReloadOutlined />} onClick={() => void loadData()} loading={loading}>
|
||||
刷新
|
||||
</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>
|
||||
}
|
||||
footer={
|
||||
|
|
|
|||
|
|
@ -14,10 +14,8 @@ import {
|
|||
LinkOutlined,
|
||||
LoadingOutlined,
|
||||
PauseOutlined,
|
||||
RobotOutlined,
|
||||
SyncOutlined,
|
||||
UserOutlined,
|
||||
PlusOutlined,
|
||||
CheckCircleFilled,
|
||||
FilePdfOutlined,
|
||||
FileWordOutlined,
|
||||
|
|
@ -372,13 +370,6 @@ type MeetingProgressPhase = 'queued' | 'asr' | 'chapter' | 'summary' | 'terminal
|
|||
|
||||
const meetingProgressTerminalRefreshCache = new Map<number, string>();
|
||||
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 unifiedStatusCode = progress?.unifiedStatus?.statusCode;
|
||||
if (unifiedStatusCode?.startsWith('FAILED_') || unifiedStatusCode === 'COMPLETED') {
|
||||
|
|
@ -489,7 +480,6 @@ const MeetingProgressDisplay: React.FC<{
|
|||
const isError = percent < 0;
|
||||
const unifiedStatusText = progress?.unifiedStatus?.statusText;
|
||||
const unifiedStatusMessage = progress?.unifiedStatus?.message;
|
||||
const primaryStatusText = unifiedStatusText || (isError ? '处理失败' : '处理中');
|
||||
|
||||
const formatEta = (seconds?: number) => {
|
||||
if (!seconds || seconds <= 0) return '计算中';
|
||||
|
|
@ -701,8 +691,6 @@ const UnifiedMeetingProgressDisplay: React.FC<{
|
|||
const isFailedStage = unifiedStatusCode?.startsWith('FAILED_') || isError;
|
||||
const isCompletedStage = unifiedStatusCode === 'COMPLETED' || percent === 100;
|
||||
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 renderStageTimeline = (options?: {
|
||||
|
|
@ -1223,8 +1211,6 @@ const MeetingDetail: React.FC = () => {
|
|||
const [downloadLoading, setDownloadLoading] = useState<'pdf' | 'word' | 'transcript' | null>(null);
|
||||
const [isEditingSummary, setIsEditingSummary] = useState(false);
|
||||
const [summaryDraft, setSummaryDraft] = useState('');
|
||||
const [expandKeywords, setExpandKeywords] = useState(false);
|
||||
const [expandSummary, setExpandSummary] = useState(false);
|
||||
const [selectedKeywords, setSelectedKeywords] = useState<string[]>([]);
|
||||
const [workspaceTab, setWorkspaceTab] = useState<WorkspaceTab>('transcript');
|
||||
const [addingHotwords, setAddingHotwords] = useState(false);
|
||||
|
|
@ -1286,14 +1272,7 @@ const MeetingDetail: React.FC = () => {
|
|||
() => buildMeetingAnalysis(meeting?.analysis, meeting?.summaryContent, meeting?.tags || ''),
|
||||
[meeting?.analysis, meeting?.summaryContent, meeting?.tags],
|
||||
);
|
||||
const hasAnalysis = !!(
|
||||
analysis.overview ||
|
||||
analysis.keywords.length ||
|
||||
analysis.chapters.length ||
|
||||
analysis.speakerSummaries.length ||
|
||||
analysis.keyPoints.length ||
|
||||
analysis.todos.length
|
||||
);
|
||||
const expandKeywords = false;
|
||||
const visibleKeywords = expandKeywords ? analysis.keywords : analysis.keywords.slice(0, 9);
|
||||
const meetingTags = useMemo(
|
||||
() => (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);
|
||||
return buildMeetingPreviewUrl(meetingShareBaseUrl, meetingId);
|
||||
}, [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 matchedModel = llmModels.find((item) => item.id === meeting?.summaryModelId);
|
||||
if (matchedModel?.modelName) {
|
||||
|
|
@ -1825,23 +1800,6 @@ const MeetingDetail: React.FC = () => {
|
|||
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) => {
|
||||
setLinkedTranscriptIds([]);
|
||||
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 { useParams, useSearchParams } from "react-router-dom";
|
||||
import {
|
||||
AudioOutlined,
|
||||
CalendarOutlined,
|
||||
CaretRightFilled,
|
||||
ClockCircleOutlined,
|
||||
|
|
@ -32,7 +31,6 @@ import {
|
|||
import { buildMeetingAnalysis } from "./meetingAnalysis";
|
||||
import "./MeetingPreview.css";
|
||||
|
||||
type AnalysisTab = "chapters" | "speakers" | "actions" | "todos";
|
||||
type PreviewPageTab = "summary" | "catalog" | "transcript";
|
||||
|
||||
const TEXT = {
|
||||
|
|
@ -203,7 +201,6 @@ export default function MeetingPreview() {
|
|||
const [meetingChapters, setMeetingChapters] = useState<MeetingChapterVO[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState("");
|
||||
const [analysisTab, setAnalysisTab] = useState<AnalysisTab>("speakers");
|
||||
const [pageTab, setPageTab] = useState<PreviewPageTab>("summary");
|
||||
const [activeTranscriptId, setActiveTranscriptId] = useState<number | null>(null);
|
||||
const [passwordRequired, setPasswordRequired] = useState(false);
|
||||
|
|
|
|||
|
|
@ -1,33 +1,24 @@
|
|||
import {
|
||||
AppstoreOutlined,
|
||||
AudioOutlined,
|
||||
CalendarOutlined,
|
||||
CheckOutlined,
|
||||
CloudUploadOutlined,
|
||||
DeleteOutlined,
|
||||
EditOutlined,
|
||||
FilterOutlined,
|
||||
InfoCircleOutlined,
|
||||
PauseCircleOutlined,
|
||||
PlusOutlined,
|
||||
SearchOutlined,
|
||||
SettingOutlined,
|
||||
SyncOutlined,
|
||||
TeamOutlined,
|
||||
UnorderedListOutlined,
|
||||
UserOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import {
|
||||
App,
|
||||
Avatar,
|
||||
Button,
|
||||
Card,
|
||||
Dropdown,
|
||||
Empty,
|
||||
Form,
|
||||
Input,
|
||||
List,
|
||||
Modal,
|
||||
Popconfirm,
|
||||
Radio,
|
||||
Select,
|
||||
|
|
@ -39,7 +30,6 @@ import {
|
|||
} from "antd";
|
||||
import dayjs from "dayjs";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||
|
||||
import { listUsers } from "../../api";
|
||||
|
|
@ -55,19 +45,16 @@ import {
|
|||
type MeetingVO,
|
||||
type RealtimeMeetingSessionStatus,
|
||||
retryScheduleMeeting,
|
||||
updateMeetingParticipants,
|
||||
} from "../../api/business/meeting";
|
||||
import { MeetingCreateDrawer, type MeetingCreateType } from "../../components/business/MeetingCreateDrawer";
|
||||
import AppPagination from "../../components/shared/AppPagination";
|
||||
import DataListPanel from "../../components/shared/DataListPanel";
|
||||
import SectionCard from "../../components/shared/SectionCard";
|
||||
import { usePermission } from "../../hooks/usePermission";
|
||||
import type { SysUser } from "../../types";
|
||||
import PageContainer from "../../components/shared/PageContainer";
|
||||
import "./Meetings.css";
|
||||
|
||||
const { Title, Text } = Typography;
|
||||
const { Option } = Select;
|
||||
const { Search } = Input;
|
||||
|
||||
const CURRENT_PLATFORM = "WEB" as const;
|
||||
|
|
@ -132,11 +119,6 @@ const isUnifiedTerminalProgress = (progress?: MeetingProgress | null) =>
|
|||
|| progress.unifiedStatus?.statusCode?.startsWith("FAILED_")
|
||||
);
|
||||
|
||||
const shouldPollMeetingCard = (item: MeetingVO) =>
|
||||
shouldTrackGenerationProgress(item)
|
||||
|| item.realtimeSessionStatus === "ACTIVE"
|
||||
|| isPausedRealtimeSessionStatus(item.realtimeSessionStatus);
|
||||
|
||||
const getUnifiedStatusCode = (progress: MeetingProgress | null | undefined) =>
|
||||
progress?.unifiedStatus?.statusCode;
|
||||
|
||||
|
|
@ -422,9 +404,7 @@ const MeetingCardItem: React.FC<{
|
|||
|
||||
const Meetings: React.FC = () => {
|
||||
const { message } = App.useApp();
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const { can } = usePermission();
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [data, setData] = useState<MeetingVO[]>([]);
|
||||
|
|
@ -445,7 +425,7 @@ const Meetings: React.FC = () => {
|
|||
realtimeEnabled: false,
|
||||
offlineAudioMaxSizeMb: 1024,
|
||||
});
|
||||
const [userList, setUserList] = useState<SysUser[]>([]);
|
||||
const [, setUserList] = useState<SysUser[]>([]);
|
||||
const progressTerminalRefreshRef = useRef<Map<number, string>>(new Map());
|
||||
const [retryingMeetingIds, setRetryingMeetingIds] = useState<Record<number, boolean>>({});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
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 {
|
||||
AudioOutlined,
|
||||
AudioMutedOutlined,
|
||||
|
|
@ -26,7 +26,6 @@ import {
|
|||
type RealtimeMeetingSessionStatus,
|
||||
type RealtimeSocketSessionVO,
|
||||
} from "../../api/business/meeting";
|
||||
const { Text } = Typography;
|
||||
const SAMPLE_RATE = 16000;
|
||||
const CHUNK_SIZE = 1280;
|
||||
const CURRENT_PLATFORM = "WEB" as const;
|
||||
|
|
@ -252,7 +251,6 @@ export function RealtimeAsrSession() {
|
|||
const sessionStartedRef = useRef(false);
|
||||
const elapsedOffsetRef = useRef(0);
|
||||
|
||||
const finalTranscriptCount = transcripts.length;
|
||||
const totalTranscriptChars = useMemo(
|
||||
() => transcripts.reduce((sum, item) => sum + item.text.length, 0) + streamingText.length,
|
||||
[streamingText, transcripts],
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
.dashboard-monitor-page__content {
|
||||
gap: 12px;
|
||||
padding: 8px 8px 0;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.dashboard-monitor-page__stats {
|
||||
|
|
@ -48,6 +48,14 @@
|
|||
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 .ant-list,
|
||||
.dashboard-monitor-page__task-list .ant-list-items,
|
||||
|
|
@ -57,16 +65,34 @@
|
|||
|
||||
.dashboard-monitor-page__task-list {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
height: auto;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
padding: 0 6px 0 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
overscroll-behavior: contain;
|
||||
}
|
||||
|
||||
.dashboard-monitor-page__task-item {
|
||||
padding: 18px 0;
|
||||
.dashboard-monitor-page__task-list .ant-spin-nested-loading,
|
||||
.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;
|
||||
}
|
||||
|
||||
|
|
@ -90,7 +116,7 @@
|
|||
}
|
||||
|
||||
.dashboard-monitor-page__task-tags {
|
||||
margin-top: 8px;
|
||||
margin-top: 4px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import React from "react";
|
||||
import "./RightVisual.less";
|
||||
|
||||
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 { useEffect, useMemo, useRef, useState } from "react";
|
||||
import "./AvatarCropDialog.css";
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
const VIEWPORT_SIZE = 300;
|
||||
const ALLOWED_TYPES = new Map([
|
||||
["image/jpeg", "jpg"],
|
||||
["image/jpg", "jpg"],
|
||||
["image/png", "png"],
|
||||
]);
|
||||
|
||||
export type CropModalState = {
|
||||
open: boolean;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"target": "ES2021",
|
||||
"lib": ["ES2021"],
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"skipLibCheck": true
|
||||
"noEmit": true,
|
||||
"skipLibCheck": true,
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue