refactor(frontend): 重构声纹管理页面UI 优化AI模型页面布局

- 重写声纹注册页面的整体UI与代码逻辑,拆分我的声纹、他人声纹视图,优化录音上传与管理的交互体验
- 调整AI模型页面的样式结构,移除冗余样式并新增内容容器样式,修复代码缩进问题
- 统一前端页面的代码规范与样式命名规则,提升项目可维护性
dev_na
AlanPaine 2026-07-03 16:25:42 +08:00
parent 76410071e7
commit ec8bb466da
4 changed files with 1061 additions and 744 deletions

View File

@ -12,12 +12,7 @@
background: transparent;
}
.ai-models-page .section-card__tabs {
margin-bottom: 0;
padding: 8px 8px 0;
border-radius: 4px 4px 0 0;
background-color: #f9fafe;
}
.ai-models-page .section-card__content {
border-radius: 0 0 4px 4px;
@ -43,3 +38,7 @@
width: 100% !important;
}
}
.ai-models-content-inner {
padding: 0px;
}

View File

@ -537,7 +537,7 @@ const AiModels: React.FC = () => {
);
return (
<PageContainer title={null} className="ai-models-page">
<PageContainer title={null} className="ai-models-page">
<SectionCard
title="AI 模型配置"
description="管理 ASR 语音识别和 LLM 大语言模型。"
@ -557,42 +557,44 @@ const AiModels: React.FC = () => {
/>
}
>
<DataListPanel
className="ai-models-data-panel"
leftActions={leftActions}
rightActions={
<Input.Search
allowClear
placeholder="搜索模型名称"
prefix={<SearchOutlined />}
className="ai-models-search"
onSearch={(value) => {
setCurrent(1);
setSearchName(value.trim());
}}
<div className="tab-pane-content ai-models-content-inner">
<DataListPanel
className="ai-models-data-panel"
leftActions={leftActions}
rightActions={
<Input.Search
allowClear
placeholder="搜索模型名称"
prefix={<SearchOutlined />}
className="ai-models-search"
onSearch={(value) => {
setCurrent(1);
setSearchName(value.trim());
}}
/>
}
footer={
<AppPagination
current={current}
pageSize={size}
total={total}
onChange={(page, pageSize) => {
setCurrent(page);
setSize(pageSize);
}}
/>
}
>
<Table
rowKey="id"
columns={resolvedTableColumns}
dataSource={data}
loading={loading}
scroll={{ x: "max-content", y: "100%" }}
pagination={false}
/>
}
footer={
<AppPagination
current={current}
pageSize={size}
total={total}
onChange={(page, pageSize) => {
setCurrent(page);
setSize(pageSize);
}}
/>
}
>
<Table
rowKey="id"
columns={resolvedTableColumns}
dataSource={data}
loading={loading}
scroll={{ x: "max-content", y: "100%" }}
pagination={false}
/>
</DataListPanel>
</DataListPanel>
</div>
</SectionCard>
<Drawer

View File

@ -1,463 +1,639 @@
.speaker-reg-page {
/* Modern UI redesign for SpeakerReg based on mockup */
.speaker-reg-page-v2 {
padding: 8px;
min-width: 0;
height: 100%;
background: #f5f6fa;
}
.speaker-reg-page > .page-container__body {
.speaker-reg-page-v2 > .page-container__body {
padding: 0;
overflow: hidden;
border: none;
border-radius: 0;
background: transparent;
flex: 1;
min-height: 0;
overflow: hidden;
}
.speaker-reg-page-v2 .section-card__content {
border-radius: 0 0 4px 4px;
padding-top: 8px;
}
/* Tabs Container */
.speaker-reg-section-content {
overflow: hidden;
display: flex;
flex-direction: column;
height: 100%; /* Important to pass height down from SectionCard */
}
.speaker-reg-layout {
.speaker-reg-content {
flex: 1;
min-width: 0;
min-height: 0;
display: grid;
grid-template-columns: minmax(430px, 0.92fr) minmax(480px, 1.08fr);
gap: 8px;
}
.speaker-reg-card.ant-card {
min-width: 0;
min-height: 0;
display: flex;
flex-direction: column;
overflow: hidden;
border: 1px solid #e6e6e6;
height: 100%;
}
.tab-pane-content {
flex: 1;
overflow-y: auto;
height: 100%;
}
.speaker-reg-content-inner {
width: 100%;
height: 100%;
padding: 24px;
background-color: var(--app-surface-color, #fff);
border-radius: 4px;
box-shadow: none;
display: flex;
flex-direction: column;
box-sizing: border-box;
}
.speaker-reg-card > .ant-card-head {
min-height: 46px;
padding: 0 14px;
border-bottom: 1px solid #f0f0f0;
.speaker-reg-content-inner-others {
padding: 0;
background: transparent;
height: 100%;
display: flex;
flex-direction: column;
}
.speaker-reg-card > .ant-card-head .ant-card-head-title {
color: #333;
font-size: 15px;
.speaker-reg-content-inner-others .data-list-panel {
flex: 1;
}
/* --- My Voiceprint View --- */
.my-voice-view {
max-width: 600px;
margin: 0 auto;
width: 100%;
background: transparent;
padding: 0;
}
.my-voice-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
}
.my-voice-header h3 {
margin: 0 0 8px 0;
font-size: 20px;
font-weight: 600;
color: #1f1f1f;
}
.my-voice-header h3 {
margin: 0 0 8px 0;
font-size: 20px;
font-weight: 600;
}
.speaker-reg-card > .ant-card-body {
flex: 1;
min-height: 0;
.my-voice-header p {
margin: 0;
color: #8c8c8c;
font-size: 14px;
}
.my-voice-card {
border: 1px solid #f0f0f0;
border-radius: 12px;
padding: 32px;
margin-bottom: 24px;
box-shadow: 0 4px 12px rgba(0,0,0,0.02);
}
.my-voice-card-top {
display: flex;
flex-direction: column;
padding: 12px;
overflow: auto;
}
.speaker-reg-card-icon {
width: 28px;
height: 28px;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 4px;
border: 1px solid #b7cdfd;
background: #f3f6ff;
color: #3c70f5;
gap: 20px;
margin-bottom: 24px;
}
.speaker-reg-form .ant-form-item {
margin-bottom: 12px;
.my-avatar {
background: #1677ff;
color: white;
font-size: 24px;
font-weight: 600;
}
.speaker-reg-tabs {
flex-shrink: 0;
.my-info {
flex: 1;
}
.speaker-reg-tabs .ant-tabs-nav {
.my-name-row {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 8px;
}
.recording-area {
padding: 12px;
border: 1px solid #e6e6e6;
.my-name {
font-size: 20px;
font-weight: 600;
color: #1f1f1f;
}
.my-tag {
font-weight: normal;
border-radius: 4px;
background: #fff;
}
.script-box {
margin-bottom: 10px;
padding: 10px 12px;
border-left: 4px solid #3c70f5;
border-radius: 0 4px 4px 0;
background: #f9fafe;
}
.script-box__label.ant-typography {
display: block;
margin-bottom: 6px;
color: #9095a1;
font-size: 13px;
}
.script-box__content {
color: #333;
.my-desc {
margin: 0;
color: #8c8c8c;
font-size: 14px;
font-weight: 500;
line-height: 22px;
}
.record-controls {
.my-audio-wrapper {
margin-bottom: 32px;
}
.my-audio-wrapper .custom-audio {
width: 100%;
height: 48px;
outline: none;
}
.my-meta-grid {
display: flex;
gap: 48px;
margin-bottom: 32px;
padding: 16px 24px;
background: #fafafa;
border-radius: 8px;
}
.meta-col {
display: flex;
flex-direction: column;
gap: 8px;
}
.meta-label {
color: #8c8c8c;
font-size: 13px;
display: flex;
align-items: center;
justify-content: flex-start;
gap: 6px;
}
.meta-value {
color: #1f1f1f;
font-size: 15px;
font-weight: 500;
}
.my-actions {
display: flex;
gap: 16px;
}
.action-btn-primary {
flex: 1;
height: 44px;
font-weight: 600;
}
.action-btn-danger {
flex: 1;
height: 44px;
font-weight: 600;
}
.tips-box {
display: flex;
gap: 12px;
padding: 16px 20px;
background: #f0f5ff;
border-radius: 8px;
color: #595959;
}
.btn-record {
width: 40px;
height: 32px;
flex: 0 0 auto;
display: inline-flex;
.tips-icon {
font-size: 20px;
color: #1677ff;
margin-top: 2px;
}
.tips-content h4 {
margin: 0 0 6px 0;
color: #1f1f1f;
font-size: 15px;
}
.tips-content p {
margin: 0;
font-size: 13px;
line-height: 1.5;
color: #595959;
}
/* --- Recording Area --- */
.modern-recording-container {
max-width: 600px;
margin: 0 auto;
width: 100%;
display: flex;
flex-direction: column;
gap: 24px;
}
.back-nav {
margin-bottom: 0;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid transparent;
border-radius: 4px;
color: #fff;
cursor: pointer;
transition: background 0.2s ease, border-color 0.2s ease;
}
.btn-record .anticon {
.back-btn {
color: #1f1f1f;
padding: 6px 12px;
margin-left: -12px;
font-weight: 500;
font-size: 15px;
display: flex;
align-items: center;
gap: 4px;
}
.back-btn .anticon {
font-size: 16px;
}
.btn-record.idle {
background: #ff4d4f;
.back-btn:hover {
color: #1677ff !important;
background: rgba(22, 119, 255, 0.08) !important;
}
.btn-record.idle:hover {
background: #d9363e;
}
.btn-record.recording {
background: #3c70f5;
}
.btn-record.recording:hover {
background: #2458d9;
}
.btn-record:disabled {
cursor: not-allowed;
opacity: 0.6;
}
.record-progress {
flex: 1;
max-width: none;
min-width: 0;
}
.record-progress__head {
.input-type-switch {
display: flex;
justify-content: space-between;
gap: 12px;
margin-bottom: 4px;
justify-content: center;
}
.record-progress__head .is-recording {
color: #3c70f5;
/* Customizing Segmented Control to make it less bulky */
.input-type-switch .ant-segmented {
background-color: #f0f2f5;
padding: 4px;
border-radius: 8px;
}
.input-type-switch .ant-segmented-item-label {
padding: 0 24px !important;
min-height: 36px;
line-height: 36px;
font-weight: 500;
color: #595959;
}
.input-type-switch .ant-segmented-item-selected .ant-segmented-item-label {
color: #1677ff;
}
.modern-record-area {
display: flex;
flex-direction: column;
gap: 24px;
}
.script-card {
position: relative;
background: #f8f9fa;
padding: 16px 40px;
border-radius: 12px;
text-align: center;
}
.quote-icon {
position: absolute;
font-size: 48px;
color: #1677ff;
opacity: 0.2;
font-family: Georgia, serif;
line-height: 1;
}
.quote-icon.left {
top: 16px;
left: 20px;
}
.quote-icon.right {
bottom: -16px;
right: 20px;
}
.script-card p {
position: relative;
z-index: 1;
margin: 0;
color: #1f1f1f;
font-size: 16px;
font-weight: 500;
line-height: 1.6;
}
.record-action {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-top: 48px;
margin-bottom: 32px;
}
.mic-button-wrapper {
position: relative;
width: 140px;
height: 140px;
display: flex;
align-items: center;
justify-content: center;
}
.mic-ring {
position: absolute;
border-radius: 50%;
background: #e6f4ff;
transition: all 0.3s ease;
}
.mic-ring.outer {
width: 100%;
height: 100%;
background: #f0f5ff;
}
.mic-ring.inner {
width: 100px;
height: 100px;
background: #bae0ff;
}
.mic-button {
position: relative;
z-index: 10;
width: 72px;
height: 72px;
border-radius: 50%;
background: #1677ff;
color: white;
border: none;
font-size: 32px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
box-shadow: 0 4px 16px rgba(22, 119, 255, 0.3);
transition: transform 0.2s;
}
.mic-button:hover:not(:disabled) {
transform: scale(1.05);
}
.mic-button-wrapper.recording .mic-ring.outer {
animation: pulse-outer 1.5s infinite;
}
.mic-button-wrapper.recording .mic-ring.inner {
animation: pulse-inner 1.5s infinite;
}
.mic-button-wrapper.recording .mic-button {
background: #ffffff;
border: 2px solid #f0f0f0;
color: #ff4d4f;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
}
.stop-square {
width: 24px;
height: 24px;
background: #ff4d4f;
border-radius: 4px;
}
@keyframes pulse-outer {
0% { transform: scale(1); opacity: 0.8; }
100% { transform: scale(1.3); opacity: 0; }
}
@keyframes pulse-inner {
0% { transform: scale(1); opacity: 0.8; }
100% { transform: scale(1.2); opacity: 0; }
}
.record-status {
text-align: center;
margin-top: 24px;
margin-bottom: 16px;
}
.record-text {
font-size: 15px;
font-weight: 500;
color: #1f1f1f;
margin-bottom: 8px;
}
.record-time {
font-size: 14px;
color: #8c8c8c;
font-variant-numeric: tabular-nums;
}
.record-progress-bar {
width: 240px;
}
.submit-btn {
margin-top: 24px;
height: 48px;
font-size: 16px;
font-weight: 600;
}
.modern-upload-area {
margin-top: 24px;
}
.speaker-reg-upload,
.speaker-reg-upload .ant-upload {
width: 100%;
}
.upload-compact {
.upload-box {
width: 100%;
min-height: 104px;
height: 240px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 18px 16px;
border: 1px dashed #b7cdfd;
border-radius: 4px;
background: #fff;
border: 1px dashed #d9d9d9;
border-radius: 12px;
background: #fafafa;
cursor: pointer;
text-align: center;
transition: border-color 0.2s ease, background 0.2s ease;
transition: all 0.3s;
}
.upload-compact:hover {
border-color: #3c70f5;
background: #f9fafe;
.upload-box:hover {
border-color: #1677ff;
background: #f0f5ff;
}
.upload-compact__icon {
margin-bottom: 8px;
color: #3c70f5;
font-size: 24px;
.upload-icon {
font-size: 40px;
color: #1677ff;
margin-bottom: 16px;
}
.upload-compact__title {
margin-bottom: 4px;
.upload-box h4 {
margin: 0 0 8px 0;
font-size: 16px;
color: #1f1f1f;
}
.upload-box p {
margin: 0;
color: #8c8c8c;
font-size: 14px;
}
.speaker-reg-audio-ready {
margin-top: 12px;
padding: 10px 12px;
border: 1px solid #b7cdfd;
border-radius: 4px;
background: #f9fafe;
margin-top: 24px;
padding: 16px;
border-radius: 8px;
background: #f6ffed;
border: 1px solid #b7eb8f;
}
.speaker-reg-audio-ready__head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-bottom: 8px;
margin-bottom: 12px;
}
.speaker-reg-audio-ready__head .anticon {
color: #52c41a;
.success-text {
color: #389e0d;
}
.speaker-reg-audio-ready audio,
.speaker-card audio {
width: 100%;
height: 32px;
}
.speaker-reg-submit-area {
/* --- Others Voiceprint View --- */
.others-voice-view {
display: flex;
flex-direction: column;
gap: 8px;
margin-top: 12px;
height: 100%;
flex: 1;
}
.speaker-reg-submit-area .ant-btn {
height: 34px;
border-radius: 4px;
.item-user {
display: flex;
align-items: center;
gap: 16px;
}
.item-avatar {
background: #e6f4ff;
color: #1677ff;
font-weight: 600;
font-size: 18px;
}
.info-strip {
display: flex;
align-items: flex-start;
gap: 10px;
padding: 10px 12px;
border-radius: 4px;
border: 1px solid #e6e6e6;
background: #f9fafe;
color: #596275;
font-size: 12px;
line-height: 20px;
}
.info-strip .anticon {
flex: 0 0 auto;
margin-top: 2px;
color: #3c70f5;
font-size: 16px;
}
.speaker-reg-library > .ant-card-body {
padding-top: 10px;
}
.speaker-reg-library__tools .ant-input-search {
width: 220px;
}
.speaker-reg-library__tools .ant-badge-count {
background: #3c70f5;
}
.speaker-reg-library__hint.ant-typography {
flex-shrink: 0;
margin-bottom: 8px;
color: #9095a1;
font-size: 13px;
}
.speaker-list {
flex: 1;
min-height: 0;
overflow: auto;
border: 1px solid #e6e6e6;
border-radius: 4px;
background: #fff;
}
.speaker-card {
display: grid;
grid-template-columns: minmax(180px, 0.9fr) minmax(240px, 1.1fr) minmax(150px, 0.7fr);
align-items: center;
gap: 10px 14px;
padding: 10px 12px;
border: 0;
border-bottom: 1px solid #f0f0f0;
border-radius: 0;
background: #fff;
transition: border-color 0.2s ease, background 0.2s ease;
}
.speaker-card + .speaker-card {
margin-top: 0;
}
.speaker-card:hover {
border-color: #f0f0f0;
background: #f9fafe;
}
.speaker-card__head {
grid-column: 1;
grid-row: 1 / span 2;
display: flex;
justify-content: space-between;
gap: 12px;
margin-bottom: 0;
}
.speaker-card__identity {
flex: 1;
min-width: 0;
}
.speaker-card__identity > .ant-typography {
display: block;
font-size: 15px;
}
.speaker-card__meta {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 8px;
margin-top: 4px;
}
.speaker-card__meta .ant-tag {
margin: 0;
border-radius: 4px;
font-size: 11px;
}
.speaker-card__meta .ant-typography {
font-size: 11px;
}
.speaker-card__remark {
grid-column: 2;
grid-row: 1;
margin-bottom: 0;
padding: 6px 8px;
border-radius: 4px;
background: #f9fafe;
color: #596275;
font-size: 12px;
line-height: 20px;
}
.speaker-card > audio {
grid-column: 2;
grid-row: 2;
}
.speaker-card--no-remark > audio {
grid-row: 1 / span 2;
}
.speaker-card__footer {
grid-column: 3;
grid-row: 1 / span 2;
.item-name-box {
display: flex;
flex-direction: column;
align-items: flex-end;
justify-content: center;
gap: 12px;
margin-top: 0;
color: #9095a1;
font-size: 11px;
text-align: right;
}
.speaker-reg-empty {
margin-top: 40px;
.item-name {
font-size: 15px;
font-weight: 600;
color: #1f1f1f;
}
.speaker-reg-library .app-pagination-container {
flex-shrink: 0;
.item-id {
font-size: 13px;
color: #8c8c8c;
}
.item-time {
display: flex;
flex-direction: column;
}
.time-label {
font-size: 12px;
color: #8c8c8c;
margin-bottom: 2px;
}
.time-val {
font-size: 14px;
color: #1f1f1f;
}
.action-text-btn {
color: #8c8c8c;
}
.action-text-btn:hover {
background: #f0f5ff;
}
.play-btn {
color: #1677ff;
}
.hidden-audio {
display: none;
}
.others-drawer-content {
display: flex;
flex-direction: column;
height: 100%;
}
.drawer-record-area {
flex: 1;
display: flex;
flex-direction: column;
margin-top: 12px;
border-top: 1px solid #f0f0f0;
}
@media (max-width: 1100px) {
.speaker-reg-section-content {
overflow: auto;
}
.speaker-reg-layout {
display: flex;
flex-direction: column;
overflow: visible;
}
.speaker-reg-card.ant-card {
min-height: 420px;
}
}
/* Responsive */
@media (max-width: 768px) {
.speaker-reg-card > .ant-card-head {
align-items: flex-start;
.speaker-reg-page-v2 {
padding: 12px;
}
.speaker-reg-card > .ant-card-head .ant-card-head-wrapper {
.tab-pane-content {
padding: 16px 12px;
}
.my-meta-grid {
flex-direction: column;
gap: 12px;
gap: 16px;
}
.speaker-reg-card > .ant-card-head .ant-card-extra,
.speaker-reg-library__tools,
.speaker-reg-library__tools .ant-input-search {
width: 100%;
}
.record-controls {
align-items: stretch;
.my-actions {
flex-direction: column;
}
.record-progress {
max-width: none;
}
.speaker-card {
grid-template-columns: 1fr;
}
.speaker-card__head,
.speaker-card__remark,
.speaker-card > audio,
.speaker-card__footer,
.speaker-card--no-remark > audio {
grid-column: auto;
grid-row: auto;
}
.speaker-card__footer {
align-items: flex-start;
text-align: left;
}
.item-actions {
display: flex;
}
}

File diff suppressed because it is too large Load Diff