From 3337285135abec2e55bad8e02f2674d02e9f4a80 Mon Sep 17 00:00:00 2001 From: puz <13060209078@163.com> Date: Thu, 2 Jul 2026 09:30:48 +0800 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9A=20=E7=95=8C=E9=9D=A2=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/.gitignore | 1 + frontend/package-lock.json | 18 ++++++++ frontend/package.json | 1 + frontend/src/App.tsx | 5 +-- .../components/shared/AppPagination/index.css | 20 ++++----- .../components/shared/AppPagination/index.tsx | 4 +- .../components/shared/ListTable/ListTable.css | 22 +++++----- .../shared/PageContainer/PageContainer.css | 22 +++++----- .../shared/SectionCard/SectionCard.css | 18 ++++---- frontend/src/hooks/useDict.ts | 3 +- frontend/src/index.css | 24 ++++++++++ frontend/src/layouts/AppLayout.tsx | 10 ----- frontend/src/pages/access/roles/index.tsx | 2 +- frontend/src/pages/access/users/index.tsx | 3 -- frontend/src/pages/business/AiModels.tsx | 5 --- .../src/pages/business/LicenseManagement.tsx | 25 ++--------- frontend/src/pages/business/MeetingDetail.tsx | 44 +------------------ .../src/pages/business/MeetingPreview.tsx | 3 -- frontend/src/pages/business/Meetings.tsx | 22 +--------- .../src/pages/business/RealtimeAsrSession.tsx | 4 +- frontend/src/pages/dashboard/index.css | 42 ++++++++++++++---- frontend/src/pages/home/RightVisual.tsx | 1 - .../src/pages/profile/AvatarCropDialog.tsx | 9 +--- frontend/tsconfig.node.json | 7 ++- 24 files changed, 137 insertions(+), 178 deletions(-) diff --git a/frontend/.gitignore b/frontend/.gitignore index 602cbea..6b2fdc2 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -10,6 +10,7 @@ lerna-debug.log* node_modules dist dist-ssr +*.tsbuildinfo *.local # Editor directories and files diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 509177c..2150455 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -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", diff --git a/frontend/package.json b/frontend/package.json index 252909d..ef03324 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -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", diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index ecc5be5..feba21d 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -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(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; } diff --git a/frontend/src/components/shared/AppPagination/index.css b/frontend/src/components/shared/AppPagination/index.css index bac975d..2e37dce 100644 --- a/frontend/src/components/shared/AppPagination/index.css +++ b/frontend/src/components/shared/AppPagination/index.css @@ -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; } diff --git a/frontend/src/components/shared/AppPagination/index.tsx b/frontend/src/components/shared/AppPagination/index.tsx index 1ecaab0..24711b0 100644 --- a/frontend/src/components/shared/AppPagination/index.tsx +++ b/frontend/src/components/shared/AppPagination/index.tsx @@ -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'; diff --git a/frontend/src/components/shared/ListTable/ListTable.css b/frontend/src/components/shared/ListTable/ListTable.css index 9662e53..1f2d1d9 100644 --- a/frontend/src/components/shared/ListTable/ListTable.css +++ b/frontend/src/components/shared/ListTable/ListTable.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; } diff --git a/frontend/src/components/shared/PageContainer/PageContainer.css b/frontend/src/components/shared/PageContainer/PageContainer.css index 830fd43..b552d4a 100644 --- a/frontend/src/components/shared/PageContainer/PageContainer.css +++ b/frontend/src/components/shared/PageContainer/PageContainer.css @@ -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) { diff --git a/frontend/src/components/shared/SectionCard/SectionCard.css b/frontend/src/components/shared/SectionCard/SectionCard.css index 58de5d2..f8bc7e2 100644 --- a/frontend/src/components/shared/SectionCard/SectionCard.css +++ b/frontend/src/components/shared/SectionCard/SectionCard.css @@ -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 { diff --git a/frontend/src/hooks/useDict.ts b/frontend/src/hooks/useDict.ts index 47f76e1..55726f1 100644 --- a/frontend/src/hooks/useDict.ts +++ b/frontend/src/hooks/useDict.ts @@ -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 = {}; -const pendingRequests: Record[]> = {}; export function useDict(typeCode: string) { const [items, setItems] = useState(dictCache[typeCode] || []); diff --git a/frontend/src/index.css b/frontend/src/index.css index 3117af9..d2f97e6 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -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); } diff --git a/frontend/src/layouts/AppLayout.tsx b/frontend/src/layouts/AppLayout.tsx index 1e2bc29..70b9754 100644 --- a/frontend/src/layouts/AppLayout.tsx +++ b/frontend/src/layouts/AppLayout.tsx @@ -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", diff --git a/frontend/src/pages/access/roles/index.tsx b/frontend/src/pages/access/roles/index.tsx index ea7fd58..9ab32d9 100644 --- a/frontend/src/pages/access/roles/index.tsx +++ b/frontend/src/pages/access/roles/index.tsx @@ -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; diff --git a/frontend/src/pages/access/users/index.tsx b/frontend/src/pages/access/users/index.tsx index 823f6f0..6d13325 100644 --- a/frontend/src/pages/access/users/index.tsx +++ b/frontend/src/pages/access/users/index.tsx @@ -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); diff --git a/frontend/src/pages/business/AiModels.tsx b/frontend/src/pages/business/AiModels.tsx index 7c4a831..5a0b487 100644 --- a/frontend/src/pages/business/AiModels.tsx +++ b/frontend/src/pages/business/AiModels.tsx @@ -73,7 +73,6 @@ const AiModels: React.FC = () => { const [fetchLoading, setFetchLoading] = useState(false); const [connectivityLoading, setConnectivityLoading] = useState(false); const [remoteModels, setRemoteModels] = useState([]); - const [speakerModels, setSpeakerModels] = useState([]); const [createConfig, setCreateConfig] = useState(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 = {}; if (profile.activeAsrModel) { diff --git a/frontend/src/pages/business/LicenseManagement.tsx b/frontend/src/pages/business/LicenseManagement.tsx index 27ec664..f000303 100644 --- a/frontend/src/pages/business/LicenseManagement.tsx +++ b/frontend/src/pages/business/LicenseManagement.tsx @@ -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([]); 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 = [ { title: "授权标识", @@ -190,11 +178,6 @@ export default function LicenseManagement() { - {/* { void handleImport(file as File); return Upload.LIST_IGNORE; }}>*/} - {/* */} - {/**/} } footer={ diff --git a/frontend/src/pages/business/MeetingDetail.tsx b/frontend/src/pages/business/MeetingDetail.tsx index cd79d4c..046fae0 100644 --- a/frontend/src/pages/business/MeetingDetail.tsx +++ b/frontend/src/pages/business/MeetingDetail.tsx @@ -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(); const meetingProgressPhaseRefreshCache = new Map(); -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([]); const [workspaceTab, setWorkspaceTab] = useState('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); diff --git a/frontend/src/pages/business/MeetingPreview.tsx b/frontend/src/pages/business/MeetingPreview.tsx index 5823aa9..be5119c 100644 --- a/frontend/src/pages/business/MeetingPreview.tsx +++ b/frontend/src/pages/business/MeetingPreview.tsx @@ -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([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(""); - const [analysisTab, setAnalysisTab] = useState("speakers"); const [pageTab, setPageTab] = useState("summary"); const [activeTranscriptId, setActiveTranscriptId] = useState(null); const [passwordRequired, setPasswordRequired] = useState(false); diff --git a/frontend/src/pages/business/Meetings.tsx b/frontend/src/pages/business/Meetings.tsx index 366b350..ff04ef4 100644 --- a/frontend/src/pages/business/Meetings.tsx +++ b/frontend/src/pages/business/Meetings.tsx @@ -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([]); @@ -445,7 +425,7 @@ const Meetings: React.FC = () => { realtimeEnabled: false, offlineAudioMaxSizeMb: 1024, }); - const [userList, setUserList] = useState([]); + const [, setUserList] = useState([]); const progressTerminalRefreshRef = useRef>(new Map()); const [retryingMeetingIds, setRetryingMeetingIds] = useState>({}); diff --git a/frontend/src/pages/business/RealtimeAsrSession.tsx b/frontend/src/pages/business/RealtimeAsrSession.tsx index 59d4be0..807fcaa 100644 --- a/frontend/src/pages/business/RealtimeAsrSession.tsx +++ b/frontend/src/pages/business/RealtimeAsrSession.tsx @@ -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], diff --git a/frontend/src/pages/dashboard/index.css b/frontend/src/pages/dashboard/index.css index 8d45df5..9d3ccb1 100644 --- a/frontend/src/pages/dashboard/index.css +++ b/frontend/src/pages/dashboard/index.css @@ -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; diff --git a/frontend/src/pages/home/RightVisual.tsx b/frontend/src/pages/home/RightVisual.tsx index aabeda7..106d542 100644 --- a/frontend/src/pages/home/RightVisual.tsx +++ b/frontend/src/pages/home/RightVisual.tsx @@ -1,4 +1,3 @@ -import React from "react"; import "./RightVisual.less"; export default function RightVisual() { diff --git a/frontend/src/pages/profile/AvatarCropDialog.tsx b/frontend/src/pages/profile/AvatarCropDialog.tsx index 83937f5..cdcf733 100644 --- a/frontend/src/pages/profile/AvatarCropDialog.tsx +++ b/frontend/src/pages/profile/AvatarCropDialog.tsx @@ -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; diff --git a/frontend/tsconfig.node.json b/frontend/tsconfig.node.json index ec03920..f53c2c1 100644 --- a/frontend/tsconfig.node.json +++ b/frontend/tsconfig.node.json @@ -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"] }