- {aiCatalogEnabled && (
+
+
+ {aiCatalogEnabled && (
+
+ )}
- )}
-
+
@@ -2920,6 +2934,7 @@ const MeetingDetail: React.FC = () => {
max={audioDuration || 0}
step={0.1}
value={Math.min(audioCurrentTime, audioDuration || 0)}
+ style={{ '--player-progress': `${playerProgressPercent}%` } as React.CSSProperties}
onChange={handleAudioProgressChange}
/>
@@ -2956,11 +2971,10 @@ const MeetingDetail: React.FC = () => {
}
/* 当转录行处于活动状态时,调整高亮样式以保持可读性 */
.ant-list-item.transcript-row.active .highlight-text {
- background: rgba(255, 255, 255, 0.2);
- border-bottom-color: #fff;
- color: #fff;
- text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
- animation: none; /* 活动行内不需要闪烁,避免视觉混乱 */
+ background: linear-gradient(120deg, rgba(95, 81, 255, 0.15) 0%, rgba(108, 140, 255, 0.1) 100%);
+ border-bottom-color: #5f51ff;
+ color: #4335eb;
+ text-shadow: none;
}
.summary-keyword-link {
color: #5f51ff;
@@ -3905,16 +3919,17 @@ const MeetingDetail: React.FC = () => {
}
.ant-list-item.transcript-row.linked .transcript-bubble,
.ant-list-item.transcript-row.linked .transcript-bubble-editing {
- background: linear-gradient(135deg, rgba(95, 81, 255, 0.08), rgba(108, 140, 255, 0.06));
- border-color: rgba(95, 81, 255, 0.2);
- box-shadow: 0 10px 24px rgba(95, 81, 255, 0.06);
+ background: #ffffff;
+ border-color: rgba(60, 112, 245, 0.28);
+ color: #2d3553;
+ box-shadow: 0 10px 24px rgba(60, 112, 245, 0.08);
}
.ant-list-item.transcript-row.active .transcript-bubble,
.ant-list-item.transcript-row.active .transcript-bubble-editing {
- border-color: rgba(95, 81, 255, 0.16);
- background: linear-gradient(135deg, #5b41ff, #6a5cff);
- color: #ffffff;
- box-shadow: 0 12px 28px rgba(95, 81, 255, 0.2);
+ border-color: rgba(60, 112, 245, 0.34);
+ background: #ffffff;
+ color: #2d3553;
+ box-shadow: 0 12px 28px rgba(60, 112, 245, 0.12);
}
.transcript-entry {
flex: 1;
diff --git a/frontend/src/pages/business/Meetings.css b/frontend/src/pages/business/Meetings.css
index 84580fb..6f1173e 100644
--- a/frontend/src/pages/business/Meetings.css
+++ b/frontend/src/pages/business/Meetings.css
@@ -92,7 +92,7 @@
min-height: 0;
overflow-x: hidden;
overflow-y: auto;
- padding: 4px;
+ padding: 6px 8px 12px;
}
.meetings-card-scroll .ant-list {
@@ -122,29 +122,33 @@
display: flex;
flex-direction: column;
overflow: hidden;
- border: 1px solid #e6e6e6 !important;
+ border: none !important;
border-radius: 16px !important;
background: #fff !important;
- box-shadow: 0 2px 8px rgba(24, 39, 75, 0.04) !important;
+ box-shadow:
+ 0 10px 26px rgba(24, 39, 75, 0.07),
+ 0 2px 8px rgba(24, 39, 75, 0.05) !important;
cursor: pointer;
transform: translateY(0);
will-change: transform, box-shadow;
transition:
- border-color 0.18s ease,
background 0.18s ease,
box-shadow 0.18s ease,
transform 0.18s ease;
}
.meeting-card-v2.ant-card:hover {
- border-color: #8fb0fb !important;
- background: #f9fafe !important;
- box-shadow: 0 8px 20px rgba(60, 112, 245, 0.14) !important;
+ background: linear-gradient(180deg, #ffffff 0%, #fbfdff 100%) !important;
+ box-shadow:
+ 0 16px 34px rgba(24, 39, 75, 0.12),
+ 0 6px 14px rgba(60, 112, 245, 0.10) !important;
transform: translateY(-3px);
}
.meeting-card-v2.ant-card:active {
- box-shadow: 0 4px 12px rgba(60, 112, 245, 0.12) !important;
+ box-shadow:
+ 0 8px 18px rgba(24, 39, 75, 0.10),
+ 0 2px 8px rgba(60, 112, 245, 0.10) !important;
transform: translateY(-1px);
}
diff --git a/frontend/src/pages/business/Meetings.tsx b/frontend/src/pages/business/Meetings.tsx
index ff04ef4..fb9755f 100644
--- a/frontend/src/pages/business/Meetings.tsx
+++ b/frontend/src/pages/business/Meetings.tsx
@@ -249,11 +249,13 @@ const TableStatusCell: React.FC<{ meeting: MeetingVO; progress: MeetingProgress
};
const getMeetingTagList = (tags: unknown) => {
+ const normalizeMeetingTag = (tag: unknown) => String(tag).replace(/^#+\s*/, '').trim();
+
if (Array.isArray(tags)) {
- return tags.map((tag) => String(tag).trim()).filter(Boolean);
+ return tags.map(normalizeMeetingTag).filter(Boolean);
}
if (typeof tags === "string") {
- return tags.split(",").map((tag) => tag.trim()).filter(Boolean);
+ return tags.split(",").map(normalizeMeetingTag).filter(Boolean);
}
return [];
};
@@ -382,7 +384,7 @@ const MeetingCardItem: React.FC<{
{tags.length > 0 ? (
tags.map(tag => (
- #{tag}
+ {tag}
))
) : (
diff --git a/frontend/src/pages/dashboard/index.css b/frontend/src/pages/dashboard/index.css
index 2f2374f..387a328 100644
--- a/frontend/src/pages/dashboard/index.css
+++ b/frontend/src/pages/dashboard/index.css
@@ -17,27 +17,6 @@
padding: 8px;
}
-.dashboard-monitor-page__stats {
- flex-shrink: 0;
- min-width: 0;
-}
-
-.dashboard-monitor-page__stat-card {
- height: 100%;
- border: 1px solid #e6e6e6;
- border-radius: 4px;
- background: #fff;
- box-shadow: none;
-}
-
-.dashboard-monitor-page__stat-card .ant-card-body {
- padding: 18px 24px;
-}
-
-.dashboard-monitor-page__stat-label {
- font-size: 13px;
-}
-
.dashboard-monitor-page__task-panel {
flex: 1;
min-height: 0;
diff --git a/frontend/src/pages/dashboard/index.tsx b/frontend/src/pages/dashboard/index.tsx
index 42246c9..b53ff8e 100644
--- a/frontend/src/pages/dashboard/index.tsx
+++ b/frontend/src/pages/dashboard/index.tsx
@@ -3,7 +3,8 @@ import PageContainer from "@/components/shared/PageContainer";
import AppPagination from '@/components/shared/AppPagination';
import DataListPanel from "@/components/shared/DataListPanel";
import SectionCard from "@/components/shared/SectionCard";
-import { Row, Col, Card, Statistic, List, Tag, Typography, Button, Space, Empty, Steps, Progress, Divider } from 'antd';
+import SummaryStatCards from "@/components/shared/SummaryStatCards";
+import { Row, Col, List, Tag, Typography, Button, Space, Empty, Steps, Progress, Divider } from 'antd';
import {
HistoryOutlined,
CheckCircleOutlined,
@@ -159,15 +160,16 @@ export const Dashboard: React.FC = () => {
};
const statCards = [
- { label: '累计会议记录', value: stats?.totalMeetings, icon:
, color: '#1890ff' },
+ { key: 'totalMeetings', label: '累计会议记录', value: stats?.totalMeetings ?? 0, icon:
, color: '#1890ff' },
{
+ key: 'processingTasks',
label: '当前分析中任务',
- value: stats?.processingTasks,
+ value: stats?.processingTasks ?? 0,
icon: processingCount > 0 ?
:
,
color: '#faad14'
},
- { label: '今日新增分析', value: stats?.todayNew, icon:
, color: '#52c41a' },
- { label: 'AI 处理成功率', value: `${stats?.successRate || 100}%`, icon:
, color: '#13c2c2' },
+ { key: 'todayNew', label: '今日新增分析', value: stats?.todayNew ?? 0, icon:
, color: '#52c41a' },
+ { key: 'successRate', label: 'AI 处理成功率', value: `${stats?.successRate || 100}%`, icon:
, color: '#13c2c2' },
];
return (
@@ -180,22 +182,7 @@ export const Dashboard: React.FC = () => {
description="系统运行概览与最近任务动态。"
contentClassName="dashboard-monitor-page__content"
>
-
-
- {statCards.map((s, idx) => (
-
-
- {s.label}}
- value={s.value || 0}
- valueStyle={{ color: s.color, fontWeight: 700 }}
- prefix={React.cloneElement(s.icon as React.ReactElement, { style: { marginRight: 8 } })}
- />
-
-
- ))}
-
-
+
[
+ { key: "total", label: t("devicesExt.totalDevices"), value: stats.total, icon: , color: "#1890ff" },
+ { key: "online", label: t("devicesExt.onlineDevices"), value: stats.online, icon: , color: "#faad14" },
+ { key: "enabled", label: t("devicesExt.enabledDevices"), value: stats.enabled, icon: , color: "#1677ff" },
+ ], [stats, t]);
+
const openEdit = (record: DeviceInfo) => {
setEditing(record);
form.setFieldsValue({
@@ -305,27 +312,10 @@ export default function Devices() {
+
-
-
- {t("devicesExt.totalDevices")}
- {stats.total}
-
-
-
- {t("devicesExt.onlineDevices")}
- {stats.online}
-
-
-
- {t("devicesExt.enabledDevices")}
- {stats.enabled}
-
-
- }
rightActions={