From ae5d1d95ab9c5078cc3d9758aaface0102cb654a Mon Sep 17 00:00:00 2001 From: Jason Date: Thu, 7 May 2026 23:00:24 +0800 Subject: [PATCH] refactor: polish UI for Export and Contacts pages --- .../Export/ExportDefaultsSettingsForm.scss | 122 ++++++++ src/pages/ContactsPage.scss | 189 ++++++++++++ src/pages/ContactsPage.tsx | 129 +++++---- src/pages/ExportPage.scss | 23 ++ src/pages/GroupAnalyticsPage.scss | 273 ++++++++++++++++++ 5 files changed, 685 insertions(+), 51 deletions(-) diff --git a/src/components/Export/ExportDefaultsSettingsForm.scss b/src/components/Export/ExportDefaultsSettingsForm.scss index c24b44f..8381c87 100644 --- a/src/components/Export/ExportDefaultsSettingsForm.scss +++ b/src/components/Export/ExportDefaultsSettingsForm.scss @@ -457,3 +457,125 @@ } } } + +// UI rebuild polish for the modal variant used by ExportPage. +.export-defaults-settings-form.layout-split { + display: grid; + gap: 12px; + + .form-group { + grid-template-columns: minmax(180px, 0.85fr) minmax(0, 1.15fr); + gap: 14px; + align-items: start; + padding: 14px; + border: 1px solid color-mix(in srgb, var(--border-color) 68%, transparent); + border-radius: 14px; + background: var(--bg-secondary); + } + + .form-group:first-child, + .form-group:last-child { + padding: 14px; + } + + .form-copy { + padding-top: 2px; + } + + label { + margin-bottom: 3px; + line-height: 1.35; + } + + .form-hint { + line-height: 1.45; + } + + .form-control { + width: 100%; + min-width: 0; + justify-content: stretch; + } + + .select-field, + .settings-time-range-field, + .log-toggle-line, + .media-default-grid, + .concurrency-inline-options { + max-width: none; + width: 100%; + } + + .select-trigger, + .settings-time-range-trigger { + border-radius: 12px; + background: var(--bg-primary); + } + + .log-toggle-line { + border-radius: 12px; + background: var(--bg-primary); + } + + .concurrency-inline-options { + grid-template-columns: repeat(6, minmax(42px, 1fr)); + gap: 6px; + } + + .concurrency-option { + min-width: 0; + min-height: 36px; + } + + .format-setting-group { + grid-template-columns: 1fr; + gap: 12px; + } + + .format-grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 8px; + width: 100%; + } + + .format-card { + min-height: 74px; + padding: 10px 12px; + border-radius: 12px; + background: var(--bg-primary); + } + + .format-label, + .format-desc { + max-width: 100%; + overflow-wrap: anywhere; + } + + .media-default-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(78px, 1fr)); + gap: 8px; + + label { + min-height: 36px; + padding: 8px 10px; + border: 1px solid var(--border-color); + border-radius: 12px; + background: var(--bg-primary); + } + } +} + +@media (max-width: 980px) { + .export-defaults-settings-form.layout-split { + .form-group { + grid-template-columns: 1fr; + gap: 10px; + } + + .format-grid { + grid-template-columns: repeat(auto-fit, minmax(156px, 1fr)); + } + } +} diff --git a/src/pages/ContactsPage.scss b/src/pages/ContactsPage.scss index d4a300e..e5bf4a6 100644 --- a/src/pages/ContactsPage.scss +++ b/src/pages/ContactsPage.scss @@ -834,3 +834,192 @@ transform: rotate(360deg); } } + +// UI rebuild polish for the contact detail surface. +.contacts-page { + background: var(--bg-primary); + + .contact-detail-panel { + background: var(--bg-primary); + } + + .contact-detail-scroll { + flex: 1; + min-height: 0; + overflow-y: auto; + padding: 24px; + display: flex; + flex-direction: column; + gap: 16px; + + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-thumb { + background: color-mix(in srgb, var(--text-tertiary) 55%, transparent); + border-radius: 3px; + } + } + + .contact-detail-hero { + display: flex; + align-items: center; + gap: 18px; + padding: 22px; + border-radius: 16px; + background: var(--bg-secondary); + border: 1px solid color-mix(in srgb, var(--border-color) 72%, transparent); + } + + .contact-detail-hero .detail-avatar { + width: 84px; + height: 84px; + border-radius: 16px; + background: var(--bg-tertiary); + overflow: hidden; + flex-shrink: 0; + display: flex; + align-items: center; + justify-content: center; + + img { + width: 100%; + height: 100%; + object-fit: cover; + } + + span { + color: var(--text-secondary); + font-size: 28px; + font-weight: 650; + } + } + + .contact-detail-heading { + min-width: 0; + display: flex; + flex-direction: column; + gap: 8px; + + h2 { + margin: 0; + color: var(--text-primary); + font-size: 24px; + line-height: 1.2; + font-weight: 650; + overflow-wrap: anywhere; + } + + p { + margin: 0; + color: var(--text-secondary); + font-size: 13px; + line-height: 1.5; + overflow-wrap: anywhere; + } + } + + .detail-type { + width: fit-content; + border-radius: 999px; + padding: 5px 10px; + } + + .contact-action-row { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); + gap: 10px; + } + + .contact-action-row .goto-chat-btn, + .contact-action-row .detail-entry-btn { + min-height: 44px; + width: 100%; + display: inline-flex; + align-items: center; + justify-content: center; + gap: 8px; + border-radius: 12px; + padding: 10px 14px; + font-size: 14px; + font-weight: 600; + cursor: pointer; + transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease; + } + + .contact-action-row .goto-chat-btn { + border: 1px solid var(--primary); + background: var(--primary); + color: var(--on-primary, #fff); + + &:hover { + background: var(--primary-hover); + border-color: var(--primary-hover); + } + } + + .contact-action-row .detail-entry-btn { + border: 1px solid var(--border-color); + background: var(--bg-secondary); + color: var(--text-primary); + + &:hover { + color: var(--primary); + border-color: color-mix(in srgb, var(--primary) 40%, var(--border-color)); + background: color-mix(in srgb, var(--primary) 7%, var(--bg-secondary)); + } + } + + .contact-detail-section { + padding: 18px; + border-radius: 16px; + background: var(--bg-secondary); + border: 1px solid color-mix(in srgb, var(--border-color) 72%, transparent); + } + + .section-title { + margin-bottom: 12px; + color: var(--text-secondary); + font-size: 12px; + font-weight: 650; + } + + .contact-detail-section .detail-info-list { + margin: 0; + display: grid; + gap: 8px; + } + + .contact-detail-section .detail-row { + display: grid; + grid-template-columns: 76px minmax(0, 1fr); + align-items: start; + gap: 12px; + padding: 12px 14px; + border: none; + border-radius: 12px; + background: var(--bg-primary); + font-size: 13px; + } + + .contact-detail-section .detail-label { + min-width: 0; + color: var(--text-tertiary); + } + + .contact-detail-section .detail-value { + color: var(--text-primary); + line-height: 1.55; + word-break: break-word; + overflow-wrap: anywhere; + user-select: text; + } + + @media (max-width: 860px) { + .contact-detail-hero { + align-items: flex-start; + flex-direction: column; + } + } +} diff --git a/src/pages/ContactsPage.tsx b/src/pages/ContactsPage.tsx index ff90aff..0af23b4 100644 --- a/src/pages/ContactsPage.tsx +++ b/src/pages/ContactsPage.tsx @@ -635,6 +635,39 @@ function ContactsPage() { return '朋友圈:统计中...' }, [selectedContactSupportsSns, selectedContactSnsCount, snsUserPostCountsStatus]) + const selectedContactTitle = useMemo(() => { + if (!selectedContact) return '' + return selectedContact.displayName || selectedContact.remark || selectedContact.nickname || selectedContact.username + }, [selectedContact]) + + const selectedContactSubtitle = useMemo(() => { + if (!selectedContact) return '' + const parts = [ + selectedContact.remark && selectedContact.remark !== selectedContactTitle ? `备注 ${selectedContact.remark}` : '', + selectedContact.alias ? `微信号 ${selectedContact.alias}` : '', + selectedContact.region || '' + ].filter(Boolean) + return parts.join(' · ') + }, [selectedContact, selectedContactTitle]) + + const selectedContactDetailRows = useMemo(() => { + if (!selectedContact) return [] + return [ + { key: 'username', label: '用户名', value: selectedContact.username }, + { key: 'nickname', label: '昵称', value: selectedContact.nickname || selectedContact.displayName }, + selectedContact.remark ? { key: 'remark', label: '备注', value: selectedContact.remark } : null, + selectedContact.alias ? { key: 'alias', label: '微信号', value: selectedContact.alias } : null, + selectedContact.labels && selectedContact.labels.length > 0 + ? { key: 'labels', label: '标签', value: selectedContact.labels.join('、') } + : null, + selectedContact.detailDescription + ? { key: 'signature', label: '个性签名', value: selectedContact.detailDescription } + : null, + selectedContact.region ? { key: 'region', label: '地区', value: selectedContact.region } : null, + { key: 'type', label: '类型', value: getContactTypeName(selectedContact.type) } + ].filter((row): row is { key: string; label: string; value: string } => Boolean(row && row.value)) + }, [selectedContact]) + const openSelectedContactSnsTimeline = useCallback(() => { if (!selectedContact || !selectedContactSupportsSns) return if (snsUserPostCountsStatus === 'idle') { @@ -1090,12 +1123,9 @@ function ContactsPage() { ) : selectedContact ? ( -
-
-

联系人详情

-
-
-
+
+
+
{selectedContact.avatarUrl ? ( @@ -1103,53 +1133,50 @@ function ContactsPage() { {getAvatarLetter(selectedContact.displayName)} )}
-
{selectedContact.displayName}
-
- {getContactTypeIcon(selectedContact.type)} - {getContactTypeName(selectedContact.type)} -
-
- -
-
用户名{selectedContact.username}
-
昵称{selectedContact.nickname || selectedContact.displayName}
- {selectedContact.remark &&
备注{selectedContact.remark}
} - {selectedContact.alias &&
微信号{selectedContact.alias}
} - {selectedContact.labels && selectedContact.labels.length > 0 && ( -
标签{selectedContact.labels.join('、')}
- )} - {selectedContact.detailDescription && ( -
个性签名{selectedContact.detailDescription}
- )} - {selectedContact.region && ( -
地区{selectedContact.region}
- )} -
类型{getContactTypeName(selectedContact.type)}
- {selectedContactSupportsSns && ( -
- 朋友圈 - +
+
+ {getContactTypeIcon(selectedContact.type)} + {getContactTypeName(selectedContact.type)}
- )} -
+

{selectedContactTitle}

+ {selectedContactSubtitle &&

{selectedContactSubtitle}

} +
+ - +
+ + {selectedContactSupportsSns && ( + + )} +
+ +
+
基础资料
+
+ {selectedContactDetailRows.map(row => ( +
+ {row.label} + {row.value} +
+ ))} +
+
) : ( diff --git a/src/pages/ExportPage.scss b/src/pages/ExportPage.scss index 6616b9a..50a5953 100644 --- a/src/pages/ExportPage.scss +++ b/src/pages/ExportPage.scss @@ -1157,6 +1157,29 @@ padding: 0 16px 16px; } +.export-defaults-modal { + width: min(760px, calc(100vw - 40px)); + max-height: min(82vh, 820px); + border-radius: 16px; + background: var(--bg-primary); + border-color: color-mix(in srgb, var(--border-color) 80%, transparent); + box-shadow: var(--shadow-md); +} + +.export-defaults-modal-header { + padding: 16px 18px 12px; +} + +.export-defaults-modal-body { + padding: 14px 18px 16px; + overflow-x: hidden; +} + +.export-defaults-modal-actions { + padding: 0 18px 16px; + background: var(--bg-primary); +} + .task-center-card-label { line-height: 1; white-space: nowrap; diff --git a/src/pages/GroupAnalyticsPage.scss b/src/pages/GroupAnalyticsPage.scss index e55c30f..31afc9e 100644 --- a/src/pages/GroupAnalyticsPage.scss +++ b/src/pages/GroupAnalyticsPage.scss @@ -1955,3 +1955,276 @@ opacity: 0.92; } } + +// UI rebuild polish: align group analysis with the shared ChatGPT-style tokens. +.group-analytics-shell { + gap: 14px; + background: transparent; +} + +.group-analytics-page { + gap: 14px; +} + +.group-sidebar, +.detail-area { + border-radius: 16px; + background: var(--bg-secondary); + border: 1px solid color-mix(in srgb, var(--border-color) 72%, transparent); + box-shadow: var(--shadow-sm); +} + +.group-sidebar { + .sidebar-header { + min-height: 58px; + padding: 14px; + } + + .search-box { + border-radius: 12px; + border: 1px solid transparent; + background: var(--bg-primary); + + &:focus-within { + border-color: color-mix(in srgb, var(--primary) 40%, var(--border-color)); + box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 12%, transparent); + } + } + + .refresh-btn { + border-radius: 10px; + background: var(--bg-primary); + } +} + +.group-item { + margin: 0 8px 4px; + border-bottom: none; + border-radius: 12px; + + &.active { + background: color-mix(in srgb, var(--primary) 12%, var(--bg-secondary)); + color: var(--text-primary); + } + + .group-avatar { + border-radius: 10px; + } +} + +.detail-drag-region { + height: 14px; +} + +.function-menu { + padding: 18px 22px 24px; + + .selected-group-info { + border-radius: 16px; + border-color: color-mix(in srgb, var(--border-color) 70%, transparent); + background: var(--bg-primary); + box-shadow: none; + } + + .function-grid { + grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); + gap: 14px; + } + + .function-card { + min-height: 136px; + border-radius: 16px; + border-color: color-mix(in srgb, var(--border-color) 76%, transparent); + background: var(--bg-primary); + box-shadow: none; + transform: none; + + &:hover { + transform: none; + border-color: color-mix(in srgb, var(--primary) 35%, var(--border-color)); + background: color-mix(in srgb, var(--primary) 5%, var(--bg-primary)); + box-shadow: var(--shadow-sm); + } + } +} + +.function-content { + .content-header { + padding: 16px 22px 12px; + background: var(--bg-secondary); + border-bottom-color: color-mix(in srgb, var(--border-color) 70%, transparent); + + .back-btn, + .refresh-btn, + .export-btn { + border-radius: 10px; + background: var(--bg-primary); + } + } + + .content-body { + padding: 18px 22px 22px; + } +} + +.members-grid { + grid-template-columns: repeat(auto-fill, minmax(108px, 1fr)); + gap: 10px; + + .member-card { + border-radius: 12px; + background: var(--bg-primary); + border: 1px solid transparent; + + &:hover { + background: color-mix(in srgb, var(--primary) 5%, var(--bg-primary)); + border-color: color-mix(in srgb, var(--primary) 28%, var(--border-color)); + } + } +} + +.member-export-panel, +.member-messages-panel, +.member-analytics-panel { + .select-trigger { + border-radius: 12px; + background: var(--bg-primary); + } + + .member-export-options, + .member-message-item, + .member-message-summary-card { + border-radius: 12px; + background: var(--bg-primary); + box-shadow: none; + } +} + +.rankings-list { + .ranking-item { + border-radius: 12px; + background: var(--bg-primary); + border: 1px solid transparent; + + &:hover { + border-color: var(--border-color); + background: color-mix(in srgb, var(--primary) 4%, var(--bg-primary)); + } + } +} + +.media-stats { + .media-layout .media-legend, + .stat-card, + .charts-grid .chart-card { + border-radius: 12px; + border: 1px solid color-mix(in srgb, var(--border-color) 76%, transparent); + background: var(--bg-primary); + box-shadow: none; + } +} + +.member-modal-overlay { + background: rgba(0, 0, 0, 0.42); + backdrop-filter: blur(8px); +} + +.member-modal, +.member-export-modal, +.member-result-modal { + border-radius: 16px; + border: 1px solid color-mix(in srgb, var(--border-color) 80%, transparent); + background: color-mix(in srgb, var(--bg-primary) 96%, transparent); + box-shadow: var(--shadow-md); +} + +.member-modal { + width: min(380px, calc(100vw - 32px)); + padding: 28px 32px 32px; + + .member-display-name { + margin-bottom: 20px; + } + + .detail-row { + border-radius: 12px; + background: var(--bg-secondary); + } + + .member-modal-actions { + margin-top: 18px; + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); + gap: 8px; + } + + .member-modal-primary-btn, + .member-modal-secondary-btn { + width: 100%; + min-width: 0; + min-height: 44px; + display: inline-flex; + align-items: center; + justify-content: center; + gap: 7px; + padding: 10px 12px; + border-radius: 12px; + font-size: 13px; + font-weight: 600; + line-height: 1.25; + cursor: pointer; + transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease; + + span { + min-width: 0; + white-space: normal; + text-align: center; + } + + svg { + flex-shrink: 0; + } + } + + .member-modal-primary-btn { + border: 1px solid var(--primary); + background: var(--primary); + color: var(--on-primary, #fff); + + &:hover { + background: var(--primary-hover); + border-color: var(--primary-hover); + opacity: 1; + } + } + + .member-modal-secondary-btn { + border: 1px solid var(--border-color); + background: var(--bg-secondary); + color: var(--text-primary); + + &:hover { + border-color: color-mix(in srgb, var(--primary) 40%, var(--border-color)); + background: color-mix(in srgb, var(--primary) 8%, var(--bg-secondary)); + color: var(--primary); + } + } +} + +[data-mode="dark"] { + .member-modal, + .member-export-modal, + .member-result-modal { + background: color-mix(in srgb, var(--bg-secondary) 94%, transparent); + border-color: color-mix(in srgb, var(--border-color) 85%, transparent); + } + + .members-grid .member-card, + .rankings-list .ranking-item, + .member-export-panel .member-message-item, + .media-stats .media-layout .media-legend, + .media-stats .stat-card, + .media-stats .charts-grid .chart-card { + background: var(--bg-secondary); + } +}