diff --git a/frontend/src/components/business/MeetingCreateDrawer.css b/frontend/src/components/business/MeetingCreateDrawer.css new file mode 100644 index 0000000..4583eed --- /dev/null +++ b/frontend/src/components/business/MeetingCreateDrawer.css @@ -0,0 +1,362 @@ +.meeting-create-drawer-root .ant-drawer-content-wrapper { + max-width: 960px; +} + +.meeting-create-drawer .ant-drawer-body { + min-width: 0; +} + +.meeting-create-drawer__skeleton { + padding: 24px; +} + +.meeting-create-drawer__header { + flex-shrink: 0; + padding: 16px 24px; + border-bottom: 1px solid #e6e6e6; + background: #fff; +} + +.meeting-create-drawer__title-icon { + width: 40px; + height: 40px; + flex: 0 0 auto; + display: inline-flex; + align-items: center; + justify-content: center; + border: 1px solid #e6e6e6; + border-radius: 4px; + background: #f9fafe; + color: #333; + font-size: 20px; +} + +.meeting-create-drawer__title.ant-typography { + margin: 0 !important; + color: #333; + font-size: 18px; + font-weight: 600; + line-height: 28px; +} + +.meeting-create-drawer__subtitle.ant-typography { + display: block; + margin-top: 2px; + color: #9095a1; + font-size: 13px; + line-height: 20px; +} + +.meeting-create-drawer__type-switch.ant-radio-group { + display: inline-flex; +} + +.meeting-create-drawer__type-switch .ant-radio-button-wrapper { + height: 32px; + min-width: 112px; + display: inline-flex; + align-items: center; + justify-content: center; + padding-inline: 14px; + border-radius: 4px; + font-size: 14px; +} + +.meeting-create-drawer__type-switch .anticon { + margin-right: 6px; +} + +.meeting-create-drawer__body { + flex: 1; + min-height: 0; + overflow-y: auto; + padding: 24px; + background: #f5f6fa; +} + +.meeting-create-form { + max-width: 880px; + margin: 0 auto; +} + +.meeting-create-form .ant-form-item { + margin-bottom: 18px; +} + +.meeting-create-form .ant-form-item-label { + padding-bottom: 6px; +} + +.meeting-create-form .ant-form-item-label > label { + color: #333; + font-size: 14px; + font-weight: 500; + line-height: 22px; +} + +.meeting-create-form .ant-input:not(textarea), +.meeting-create-form .ant-picker, +.meeting-create-form .ant-select-selector, +.meeting-create-form .ant-btn { + min-height: 32px !important; + border-radius: 4px !important; + box-shadow: none; +} + +.meeting-create-form .ant-select-selector { + align-items: center; +} + +.meeting-create-form .ant-select-multiple .ant-select-selector { + height: auto !important; + min-height: 32px !important; +} + +.meeting-create-form textarea.ant-input { + border-radius: 4px; +} + +.meeting-create-section-title { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 16px; +} + +.meeting-create-section-title__bar { + width: 4px; + height: 16px; + flex: 0 0 auto; + border-radius: 1px; + background: #3c70f5; +} + +.meeting-create-section-title .ant-typography { + margin: 0; + color: #333; + font-size: 16px; + font-weight: 600; + line-height: 24px; +} + +.meeting-create-section-divider { + margin: 24px 0; + border-top: 1px solid #e6e6e6; +} + +.meeting-create-template-grid { + padding: 0; +} + +.meeting-create-template-grid .ant-col { + display: flex; +} + +.meeting-create-template-card { + position: relative; + width: 100%; + height: 48px; + display: flex; + align-items: center; + overflow: hidden; + padding: 0 16px; + border: 1px solid #e6e6e6; + border-radius: 4px; + background: #fff; + cursor: pointer; + transition: border-color 0.2s ease, background 0.2s ease; +} + +.meeting-create-template-card:hover { + border-color: #b7cdfd; + background: #f9fafe; +} + +.meeting-create-template-card.is-selected { + border-color: #3c70f5; + background: #eef4ff; +} + +.meeting-create-template-card__name { + min-width: 0; + overflow: hidden; + color: #333; + font-size: 14px; + font-weight: 500; + line-height: 22px; + text-overflow: ellipsis; + white-space: nowrap; +} + +.meeting-create-template-card.is-selected .meeting-create-template-card__name { + color: #3c70f5; +} + +.meeting-create-template-card__check { + position: absolute; + top: -1px; + right: -1px; + width: 20px; + height: 20px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 0 4px 0 4px; + background: #3c70f5; + color: #fff; + font-size: 12px; +} + +.meeting-create-radio-line { + min-height: 32px; + display: flex; + align-items: center; + gap: 12px; +} + +.meeting-create-advanced.ant-collapse { + overflow: hidden; + margin-bottom: 24px; + border: 1px solid #e6e6e6; + border-radius: 4px; + background: #fff; +} + +.meeting-create-advanced .ant-collapse-header { + min-height: 48px; + align-items: center !important; + padding: 8px 14px !important; +} + +.meeting-create-advanced .ant-collapse-content-box { + padding: 12px 14px 14px !important; +} + +.meeting-create-advanced__label { + width: 100%; + height: 32px; + display: flex; + align-items: center; +} + +.meeting-create-advanced__title { + display: flex; + align-items: center; + color: #333; + font-size: 15px; + font-weight: 600; +} + +.meeting-create-advanced__icon { + width: 28px; + height: 28px; + margin-right: 10px; + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: 4px; + background: #eef4ff; + color: #3c70f5; +} + +.meeting-create-advanced__body { + padding-top: 8px; + border-top: 1px dashed #e6e6e6; +} + +.meeting-create-upload.ant-upload-wrapper .ant-upload-drag { + border: 1px dashed #d9d9d9; + border-radius: 4px; + background: #fff; +} + +.meeting-create-upload .ant-upload { + padding: 28px 16px; +} + +.meeting-create-upload .ant-upload-drag-icon { + margin-bottom: 12px; +} + +.meeting-create-upload .ant-upload-drag-icon .anticon { + color: #3c70f5; + font-size: 44px; +} + +.meeting-create-upload .ant-upload-text { + margin: 0; + color: #333; + font-size: 15px; + font-weight: 500; +} + +.meeting-create-upload .ant-upload-hint { + margin-top: 8px; + color: #9095a1; + font-size: 13px; +} + +.meeting-create-upload__progress { + width: min(420px, 80%); + margin: 24px auto 0; +} + +.meeting-create-upload__progress > div:last-child { + margin-top: 8px; + color: #3c70f5; + font-size: 13px; +} + +.meeting-create-upload__file.ant-tag { + max-width: 90%; + display: inline-flex; + align-items: center; + margin-top: 16px; + padding: 4px 12px; + border-radius: 4px; + font-size: 13px; +} + +.meeting-create-upload__file span:last-child { + min-width: 0; + overflow: hidden; + margin-left: 4px; + text-overflow: ellipsis; + white-space: nowrap; +} + +.meeting-create-drawer__footer { + display: flex; + justify-content: flex-end; + padding: 12px 24px; + background: #fff; +} + +.meeting-create-drawer__footer .ant-btn { + min-width: 112px; + height: 32px; + border-radius: 4px; + box-shadow: none; +} + +@media (max-width: 768px) { + .meeting-create-drawer-root .ant-drawer-content-wrapper { + width: 100vw !important; + max-width: 100vw; + } + + .meeting-create-drawer__header, + .meeting-create-drawer__body, + .meeting-create-drawer__footer { + padding-inline: 16px; + } + + .meeting-create-drawer__type-switch { + width: 100%; + } + + .meeting-create-drawer__type-switch .ant-radio-button-wrapper { + flex: 1; + min-width: 0; + } +} diff --git a/frontend/src/components/business/MeetingCreateDrawer.tsx b/frontend/src/components/business/MeetingCreateDrawer.tsx index 2f4121c..86d1a5e 100644 --- a/frontend/src/components/business/MeetingCreateDrawer.tsx +++ b/frontend/src/components/business/MeetingCreateDrawer.tsx @@ -47,6 +47,7 @@ import { } from "../../api/business/meeting"; import { getPromptPage, type PromptTemplateVO } from "../../api/business/prompt"; import type { SysUser } from "../../types"; +import "./MeetingCreateDrawer.css"; const { Dragger } = Upload; const { Option } = Select; @@ -195,15 +196,19 @@ export const MeetingCreateDrawer: React.FC = ({ const nextConfig = createConfigRes.data.data || DEFAULT_CREATE_CONFIG; const nextType = resolveAvailableCreateType(initialType, nextConfig); - const activePrompts = promptRes.data.data.records.filter((item: PromptTemplateVO) => item.status === 1); + const promptRecords = promptRes.data?.data?.records || []; + const asrRecords = asrRes.data?.data?.records || []; + const llmRecords = llmRes.data?.data?.records || []; + const hotwordRecords = hotwordRes.data?.data?.records || []; + const activePrompts = promptRecords.filter((item: PromptTemplateVO) => item.status === 1); setCreateConfig(nextConfig); setConfigLoaded(true); setType(nextType); - setAsrModels(asrRes.data.data.records.filter((item: AiModelVO) => item.status === 1)); - setLlmModels(llmRes.data.data.records.filter((item: AiModelVO) => item.status === 1)); + setAsrModels(asrRecords.filter((item: AiModelVO) => item.status === 1)); + setLlmModels(llmRecords.filter((item: AiModelVO) => item.status === 1)); setPrompts(activePrompts); - setHotwordList(hotwordRes.data.data.records.filter((item: HotWordVO) => item.status === 1)); + setHotwordList(hotwordRecords.filter((item: HotWordVO) => item.status === 1)); setHotWordGroups((hotWordGroupRes.data.data || []).filter((item: HotWordGroupVO) => item.status === 1)); setUserList(users || []); @@ -387,20 +392,22 @@ export const MeetingCreateDrawer: React.FC = ({ return ( - - - + @@ -414,117 +421,120 @@ export const MeetingCreateDrawer: React.FC = ({ }} > {!configLoaded ? ( -
+
) : ( <> -
- - - -
+
+ + + +
{type === "upload" ? : }
- {type === "upload" ? "上传录音发起分析" : "创建实时会议"} - {type === "upload" ? "上传已有音频文件并由 AI 进行转写与总结分析" : "实时采集语音并进行流式转写与实时纪要生成"} + {type === "upload" ? "上传录音发起分析" : "创建实时会议"} + {type === "upload" ? "上传已有音频文件并由 AI 进行转写与总结分析" : "实时采集语音并进行流式转写与实时纪要生成"}
- - setType(e.target.value)} optionType="button" buttonStyle="solid" size="large"> + + setType(e.target.value)} optionType="button" buttonStyle="solid" className="meeting-create-drawer__type-switch"> {createConfig.offlineEnabled && ( - 上传录音 + 上传录音 )} {createConfig.realtimeEnabled && ( - 实时会议 + 实时会议 )}
-
-
-
-
- 基础信息 +
+ +
+ + 基础信息
- - + + - + - + - + - - + + - {userList.map(u => ())} - + - {userList.map(u => ())} - - + + - -
+
-
-
- 模型与纪要配置 +
+ + 模型与纪要配置
- - + + - + - + - + {prompts.length > 15 ? ( - {prompts.map(p => )} ) : ( -
+
{prompts.map(p => { const isSelected = watchedPromptId === p.id; return ( - -
form.setFieldsValue({ promptId: p.id })} style={{ padding: "12px 16px", borderRadius: 8, border: `1px solid ${isSelected ? "#1890ff" : "var(--app-border-color)"}`, background: isSelected ? "#e6f7ff" : "var(--app-bg-surface)", cursor: "pointer", position: "relative", transition: "all 0.2s", display: "flex", alignItems: "center", height: "100%" }}> -
{p.templateName}
- {isSelected &&
} + +
form.setFieldsValue({ promptId: p.id })} + className={isSelected ? "meeting-create-template-card is-selected" : "meeting-create-template-card"} + > +
{p.templateName}
+ {isSelected &&
}
); @@ -534,15 +544,15 @@ export const MeetingCreateDrawer: React.FC = ({ )} - - + + - ({ label: `${item.groupName} (${item.hotWordCount}/200)`, value: item.id }))]} /> - + - + 详细 标准 简洁 @@ -554,37 +564,37 @@ export const MeetingCreateDrawer: React.FC = ({ -
-
- +
+
+
+
高级设置
), children: ( -
- - +
+ + 说话人区分 } valuePropName="checked" getValueProps={(v) => ({ checked: !!v })} normalize={(v) => (v ? 1 : 0)}> - + 文本修正 } valuePropName="checked"> {type === "realtime" && ( <> - + @@ -611,10 +621,10 @@ export const MeetingCreateDrawer: React.FC = ({ {type === "upload" && ( <> -
-
-
- 上传录音文件 +
+
+ + 上传录音文件
= ({ customRequest={customUpload} onChange={info => setFileList(info.fileList.slice(-1))} maxCount={1} - style={{ borderRadius: 12, padding: "32px 0", background: "var(--app-bg-surface)", border: "1px dashed var(--app-border-color)" }} + className="meeting-create-upload" >
-

-

点击或拖拽录音文件到此处

-

支持 mp3、wav、m4a 等格式,大小不超过 {createConfig.offlineAudioMaxSizeMb}MB

+

+

点击或拖拽录音文件到此处

+

支持 mp3、wav、m4a 等格式,大小不超过 {createConfig.offlineAudioMaxSizeMb}MB

{uploadProgress > 0 && uploadProgress < 100 && ( -
+
-
文件上传中,请稍候...
+
文件上传中,请稍候...
)} {audioUrl && ( - + 已上传: - {audioUrl.split("/").pop()} + {audioUrl.split("/").pop()} )}
diff --git a/frontend/src/components/shared/DataListPanel/DataListPanel.css b/frontend/src/components/shared/DataListPanel/DataListPanel.css index f510d37..e098f3f 100644 --- a/frontend/src/components/shared/DataListPanel/DataListPanel.css +++ b/frontend/src/components/shared/DataListPanel/DataListPanel.css @@ -7,7 +7,7 @@ flex-direction: column; box-sizing: border-box; overflow: hidden; - padding: 8px 12px; + padding: 8px; border-radius: 4px; background-color: #fff; } @@ -78,10 +78,10 @@ flex-shrink: 0; flex-wrap: nowrap; min-width: 0; - min-height: 34px; - margin-bottom: 16px; + min-height: 40px; + margin-bottom: 14px; display: flex; - align-items: center; + align-items: flex-end; justify-content: space-between; gap: 12px; } @@ -97,10 +97,26 @@ .data-list-panel__right-actions { flex: 1; min-width: 0; + min-height: 32px; display: flex; + align-items: center; justify-content: flex-end; } +.data-list-panel__toolbar .ant-btn { + font-size: 14px; + font-weight: 400; + line-height: 22px; +} + +.data-list-panel__toolbar .ant-btn:not(.ant-btn-icon-only) { + padding-inline: 14px; +} + +.data-list-panel__toolbar .ant-btn .ant-btn-icon { + font-size: 14px; +} + .data-list-panel__right-actions .ant-space { min-width: 0; min-height: 32px; @@ -152,6 +168,11 @@ font-weight: 400; } +.data-list-panel__table-area .ant-table-tbody > tr:not(.row-selected):not(.ant-table-row-selected):not(.dict-type-row-selected):hover > td, +.data-list-panel__table-area .ant-table-tbody > tr:not(.row-selected):not(.ant-table-row-selected):not(.dict-type-row-selected) > td.ant-table-cell-row-hover { + background: #fff !important; +} + .data-list-panel__table-area .ant-table-cell { line-height: 22px; } @@ -170,7 +191,7 @@ .data-list-panel__footer { flex-shrink: 0; min-width: 0; - min-height: 56px; + min-height: 50px; } .data-list-panel__footer .app-pagination-container { @@ -179,6 +200,7 @@ border-radius: 0; background: #fff; box-sizing: border-box; + min-height: 50px; } .data-list-panel__footer .app-pagination-container .ant-pagination { diff --git a/frontend/src/components/shared/ListTable/ListTable.css b/frontend/src/components/shared/ListTable/ListTable.css index 1a57d25..a3b21fc 100644 --- a/frontend/src/components/shared/ListTable/ListTable.css +++ b/frontend/src/components/shared/ListTable/ListTable.css @@ -10,6 +10,10 @@ background: #fff; } +.list-table-container .ant-table-wrapper { + background: #fff; +} + /* 行选中样式 */ .list-table-container .row-selected > td { background-color: var(--item-hover-bg) !important; @@ -65,8 +69,16 @@ border-bottom: 1px solid #f0f0f0; } -.list-table-container .ant-table-tbody > tr:hover > td { - background: #f5f9ff !important; +.list-table-container .ant-table-tbody > tr:not(.row-selected):not(.ant-table-row-selected):hover > td { + background: #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; +} + +.list-table-container .ant-table-tbody > tr.row-selected > td.ant-table-cell-row-hover { + background-color: var(--item-hover-bg) !important; } .list-table-container .list-table-table--y-scroll.ant-table-wrapper, diff --git a/frontend/src/components/shared/PageContainer/PageContainer.css b/frontend/src/components/shared/PageContainer/PageContainer.css index 14de5f9..b03adee 100644 --- a/frontend/src/components/shared/PageContainer/PageContainer.css +++ b/frontend/src/components/shared/PageContainer/PageContainer.css @@ -84,7 +84,7 @@ min-height: 0; display: flex; flex-direction: column; - padding: 0 16px 16px; + padding: 0 16px 12px; border: 1px solid #e6e6e6; border-top: none; border-radius: 0 0 4px 4px; diff --git a/frontend/src/components/shared/PageHeader/PageHeader.css b/frontend/src/components/shared/PageHeader/PageHeader.css deleted file mode 100644 index 6ba9202..0000000 --- a/frontend/src/components/shared/PageHeader/PageHeader.css +++ /dev/null @@ -1,109 +0,0 @@ -.page-header-standard { - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - border-radius: 12px; - padding: 24px 28px; - margin-bottom: 24px; - display: flex; - justify-content: space-between; - align-items: center; - box-shadow: 0 4px 12px rgba(102, 126, 234, 0.15); - position: relative; - overflow: hidden; -} - -.page-header-standard::before { - content: ''; - position: absolute; - top: -50%; - right: -10%; - width: 300px; - height: 300px; - background: rgba(255, 255, 255, 0.1); - border-radius: 50%; -} - -.page-header-main { - display: flex; - align-items: center; - gap: 16px; - position: relative; - z-index: 1; -} - -.back-button { - width: 36px; - height: 36px; - border-radius: 8px; - background: rgba(255, 255, 255, 0.2); - border: 1px solid rgba(255, 255, 255, 0.3); - color: #ffffff; - display: flex; - align-items: center; - justify-content: center; - cursor: pointer; - transition: all 0.3s; - font-size: 16px; -} - -.back-button:hover { - background: rgba(255, 255, 255, 0.3); - transform: translateX(-2px); -} - -.page-header-content { - display: flex; - align-items: center; - gap: 16px; -} - -.page-header-icon { - width: 48px; - height: 48px; - border-radius: 12px; - background: rgba(255, 255, 255, 0.2); - backdrop-filter: blur(10px); - display: flex; - align-items: center; - justify-content: center; - font-size: 24px; - color: #ffffff; - border: 1px solid rgba(255, 255, 255, 0.3); -} - -.page-header-text { - display: flex; - flex-direction: column; - gap: 4px; -} - -.page-header-title { - font-size: 22px; - font-weight: 600; - color: #ffffff; - margin: 0; - letter-spacing: 0.3px; -} - -.page-header-description { - font-size: 14px; - color: rgba(255, 255, 255, 0.9); - margin: 0; - line-height: 1.5; -} - -.page-header-extra { - position: relative; - z-index: 1; -} - -@media (max-width: 768px) { - .page-header-standard { - flex-direction: column; - align-items: flex-start; - gap: 16px; - } - - .page-header-extra { - width: 100%; - } -} diff --git a/frontend/src/components/shared/PageHeader/PageHeader.jsx b/frontend/src/components/shared/PageHeader/PageHeader.jsx deleted file mode 100644 index b934376..0000000 --- a/frontend/src/components/shared/PageHeader/PageHeader.jsx +++ /dev/null @@ -1,35 +0,0 @@ -import { ArrowLeftOutlined } from '@ant-design/icons' -import './PageHeader.css' - -function PageHeader({ - title, - description, - icon, - showBack = false, - onBack, - extra -}) { - return ( -
-
- {showBack && ( - - )} -
- {icon &&
{icon}
} -
-

{title}

- {description && ( -

{description}

- )} -
-
-
- {extra &&
{extra}
} -
- ) -} - -export default PageHeader diff --git a/frontend/src/components/shared/PageHeader/index.tsx b/frontend/src/components/shared/PageHeader/index.tsx deleted file mode 100644 index 70cca0a..0000000 --- a/frontend/src/components/shared/PageHeader/index.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { Typography } from 'antd'; -import React from 'react'; - -const { Title, Text } = Typography; - -interface PageHeaderProps { - title: React.ReactNode; - subtitle?: React.ReactNode; - extra?: React.ReactNode; - className?: string; -} - -const PageHeader: React.FC = ({ title, subtitle, extra, className = '' }) => { - return ( -
-
- - {title} - - {subtitle && ( - - {subtitle} - - )} -
- {extra &&
{extra}
} -
- ); -}; - -export default PageHeader; diff --git a/frontend/src/components/shared/PageTitleBar/PageTitleBar.css b/frontend/src/components/shared/PageTitleBar/PageTitleBar.css deleted file mode 100644 index f362ffb..0000000 --- a/frontend/src/components/shared/PageTitleBar/PageTitleBar.css +++ /dev/null @@ -1,187 +0,0 @@ -.page-title-bar { - background: linear-gradient(135deg, #e0e7ff 0%, #f3e8ff 100%); - border-radius: 12px; - padding: 16px 24px; - margin-bottom: 16px; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06); - position: relative; - overflow: hidden; - border: 1px solid rgba(139, 92, 246, 0.1); -} - -.page-title-bar::before { - content: ''; - position: absolute; - top: -50%; - right: -5%; - width: 200px; - height: 200px; - background: rgba(139, 92, 246, 0.05); - border-radius: 50%; -} - -.title-bar-content { - position: relative; - z-index: 1; - display: flex; - justify-content: space-between; - align-items: center; -} - -.title-bar-left { - flex: 1; -} - -.title-row { - display: flex; - align-items: center; - gap: 16px; -} - -.title-group { - display: flex; - align-items: center; - gap: 12px; -} - -.page-title { - font-size: 20px; - font-weight: 600; - color: #1e293b; - margin: 0; - letter-spacing: 0.3px; -} - -.title-badge { - background: rgba(139, 92, 246, 0.15); - color: #7c3aed; - padding: 2px 10px; - border-radius: 10px; - font-size: 12px; - font-weight: 500; -} - -.page-description { - font-size: 13px; - color: #64748b; - margin: 0; - white-space: nowrap; -} - -.title-bar-right { - display: flex; - align-items: center; - gap: 12px; -} - -.title-actions { - display: flex; - gap: 10px; -} - -.title-actions button { - padding: 8px 16px; - border-radius: 6px; - font-size: 13px; - font-weight: 500; - cursor: pointer; - transition: all 0.3s; - border: none; - outline: none; -} - -.title-actions button.primary { - background: #7c3aed; - color: #ffffff; -} - -.title-actions button.primary:hover { - background: #6d28d9; - transform: translateY(-1px); - box-shadow: 0 4px 12px rgba(124, 58, 237, 0.25); -} - -.title-actions button.secondary { - background: rgba(139, 92, 246, 0.1); - color: #7c3aed; - border: 1px solid rgba(139, 92, 246, 0.2); -} - -.title-actions button.secondary:hover { - background: rgba(139, 92, 246, 0.15); - transform: translateY(-1px); -} - -.toggle-button { - width: 32px; - height: 32px; - border-radius: 6px; - background: rgba(139, 92, 246, 0.1); - border: 1px solid rgba(139, 92, 246, 0.2); - color: #7c3aed; - display: flex; - align-items: center; - justify-content: center; - cursor: pointer; - transition: all 0.3s; - font-size: 14px; -} - -.toggle-button:hover { - background: rgba(139, 92, 246, 0.2); - transform: translateY(-1px); -} - -/* 扩展内容区域 */ -.title-bar-expanded-content { - position: relative; - z-index: 1; - margin-top: 8px; - padding: 8px; - background: #ffffff; - border: 1px solid rgba(139, 92, 246, 0.1); - animation: expandContent 0.3s ease-out; -} - -@keyframes expandContent { - from { - opacity: 0; - transform: translateY(-10px); - } - to { - opacity: 1; - transform: translateY(0); - } -} - -/* 响应式适配 */ -@media (max-width: 768px) { - .title-bar-content { - flex-direction: column; - align-items: flex-start; - gap: 12px; - } - - .title-row { - flex-direction: column; - align-items: flex-start; - gap: 4px; - } - - .page-description { - white-space: normal; - } - - .title-bar-right { - width: 100%; - justify-content: space-between; - } - - .title-actions { - flex: 1; - } - - .title-actions button { - flex: 1; - } -} diff --git a/frontend/src/components/shared/PageTitleBar/PageTitleBar.jsx b/frontend/src/components/shared/PageTitleBar/PageTitleBar.jsx deleted file mode 100644 index a023b8f..0000000 --- a/frontend/src/components/shared/PageTitleBar/PageTitleBar.jsx +++ /dev/null @@ -1,53 +0,0 @@ -import { useState } from 'react' -import { UpOutlined, DownOutlined } from '@ant-design/icons' -import './PageTitleBar.css' - -function PageTitleBar({ - title, - badge, - description, - actions, - showToggle = false, - onToggle, - defaultExpanded = false, -}) { - const [expanded, setExpanded] = useState(defaultExpanded) - - const handleToggle = () => { - const newExpanded = !expanded - setExpanded(newExpanded) - if (onToggle) { - onToggle(newExpanded) - } - } - - return ( -
-
-
-
-
-

{title}

- {badge && {badge}} -
- {description &&

{description}

} -
-
-
- {actions &&
{actions}
} - {showToggle && ( - - )} -
-
-
- ) -} - -export default PageTitleBar diff --git a/frontend/src/components/shared/SectionCard/SectionCard.css b/frontend/src/components/shared/SectionCard/SectionCard.css index 9605e25..58de5d2 100644 --- a/frontend/src/components/shared/SectionCard/SectionCard.css +++ b/frontend/src/components/shared/SectionCard/SectionCard.css @@ -93,6 +93,10 @@ min-width: 0; } +.section-card__tabs:has(+ .section-card__content .data-list-panel) { + margin-bottom: 2px; +} + .section-card__tabs > .ant-tabs { width: 100%; } @@ -121,13 +125,20 @@ border: 0 solid transparent !important; border-radius: 0 !important; background-color: rgba(249, 250, 254, 0) !important; - transition: none !important; + transition: background-color 0.16s ease !important; } -.section-card__tabs .ant-tabs-tab.ant-tabs-tab-active { +.section-card__tabs .ant-tabs-tab:hover { + background-color: transparent !important; +} + +.section-card__tabs .ant-tabs-tab.ant-tabs-tab-active, +.section-card__tabs .ant-tabs-tab.ant-tabs-tab-active:hover, +.section-card__tabs .ant-tabs-tab.ant-tabs-tab-active:focus, +.section-card__tabs .ant-tabs-tab.ant-tabs-tab-active:active { border: none !important; border-radius: 0 !important; - background-color: #f9fafe !important; + background-color: #e9eef8 !important; } .section-card__tabs .ant-tabs-tab-btn { @@ -136,7 +147,7 @@ font-size: 14px; line-height: 22px; letter-spacing: 0; - transition: none !important; + transition: color 0.16s ease !important; } .section-card__tabs .ant-tabs-tab.ant-tabs-tab-active .ant-tabs-tab-btn { diff --git a/frontend/src/index.css b/frontend/src/index.css index fc78a3b..222b644 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -270,16 +270,53 @@ body::after { flex-wrap: wrap; } -.app-page__page-actions { +.app-page__content-toolbar { + flex-shrink: 0; + min-height: 40px; + margin-bottom: 14px; display: flex; - justify-content: flex-end; - align-items: center; - margin: -8px 0 16px; + align-items: flex-end; + justify-content: space-between; + gap: 12px; } -.app-page__page-actions .ant-btn { - min-width: 96px; - border-radius: 10px !important; +.app-page__content-toolbar-actions, +.app-page__content-toolbar-filters { + min-width: 0; + min-height: 32px; + display: flex; + align-items: center; +} + +.app-page__content-toolbar-actions { + flex: 0 0 auto; +} + +.app-page__content-toolbar-filters { + flex: 1; + justify-content: flex-end; +} + +.app-page__content-toolbar .ant-btn { + height: 32px; + border-radius: 4px !important; + box-shadow: none; + font-size: 14px; + font-weight: 400; + line-height: 22px; +} + +.app-page__content-toolbar .ant-btn:not(.ant-btn-icon-only) { + padding-inline: 14px; +} + +.app-page__content-toolbar .ant-input, +.app-page__content-toolbar .ant-input-affix-wrapper, +.app-page__content-toolbar .ant-select-selector, +.app-page__content-toolbar .ant-picker, +.app-page__content-toolbar .ant-input-number { + height: 32px !important; + border-radius: 4px !important; } .app-page__toolbar .ant-input, @@ -838,6 +875,21 @@ body::after { .app-page { padding: 16px; } + + .app-page__content-toolbar { + align-items: stretch; + flex-direction: column; + } + + .app-page__content-toolbar-actions, + .app-page__content-toolbar-filters, + .app-page__content-toolbar-filters .ant-space { + width: 100%; + } + + .app-page__content-toolbar-filters { + justify-content: flex-start; + } } @@ -884,6 +936,13 @@ body::after { overflow-y: auto !important; } +.app-page__table-wrap .ant-table-tbody > tr:not(.row-selected):not(.ant-table-row-selected):not(.dict-type-row-selected):hover > td, +.app-page__table-wrap .ant-table-tbody > tr:not(.row-selected):not(.ant-table-row-selected):not(.dict-type-row-selected) > td.ant-table-cell-row-hover, +.app-page__panel-card .ant-table-tbody > tr:not(.row-selected):not(.ant-table-row-selected):not(.dict-type-row-selected):hover > td, +.app-page__panel-card .ant-table-tbody > tr:not(.row-selected):not(.ant-table-row-selected):not(.dict-type-row-selected) > td.ant-table-cell-row-hover { + background: #fff !important; +} + .ant-table-wrapper .ant-table-pagination.ant-pagination.app-global-pagination, .app-global-pagination.ant-pagination { margin: auto 0 0 0 !important; @@ -1253,7 +1312,6 @@ body::after, .app-page__toolbar .ant-picker, .app-page__toolbar .ant-input-number, .app-page__toolbar .ant-btn, -.app-page__page-actions .ant-btn, .ant-btn, .ant-input, .ant-input-affix-wrapper, diff --git a/frontend/src/pages/access/permissions/index.less b/frontend/src/pages/access/permissions/index.less index f5b8154..deb91c5 100644 --- a/frontend/src/pages/access/permissions/index.less +++ b/frontend/src/pages/access/permissions/index.less @@ -1,7 +1,15 @@ .permissions-page { - display: flex; - flex-direction: column; - height: 100%; + padding: 8px; + min-width: 0; + background: #f5f6fa; +} + +.permissions-page > .page-container__body { + padding: 0; + overflow: hidden; + border: none; + border-radius: 0; + background: transparent; } .permissions-content-card { @@ -9,9 +17,9 @@ min-height: 0; display: flex; flex-direction: column; - border-radius: 18px !important; - border: 1px solid rgba(226, 232, 240, 0.8) !important; - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.04) !important; + border-radius: 4px !important; + border: 1px solid #e6e6e6 !important; + box-shadow: none !important; overflow: hidden; } @@ -73,7 +81,7 @@ } &::-webkit-scrollbar-thumb { background: #e2e8f0; - border-radius: 10px; + border-radius: 3px; &:hover { background: #cbd5e1; } diff --git a/frontend/src/pages/access/permissions/index.tsx b/frontend/src/pages/access/permissions/index.tsx index 6a020a2..092b444 100644 --- a/frontend/src/pages/access/permissions/index.tsx +++ b/frontend/src/pages/access/permissions/index.tsx @@ -10,8 +10,9 @@ import { CheckSquareOutlined, ClusterOutlined, DeleteOutlined, EditOutlined, Fol import { createPermission, deletePermission, listMyPermissions, updatePermission } from "@/api"; import { useDict } from "@/hooks/useDict"; import { usePermission } from "@/hooks/usePermission"; -import PageHeader from "@/components/shared/PageHeader"; import PageContainer from "@/components/shared/PageContainer"; +import DataListPanel from "@/components/shared/DataListPanel"; +import SectionCard from "@/components/shared/SectionCard"; import type { SysPermission } from "@/types"; import "./index.less"; @@ -419,18 +420,21 @@ export default function Permissions() { ]; return ( -