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 ? ( -
{selectedContactSubtitle}
} +