fix:优化了设置中下拉菜单的视觉表现

This commit is contained in:
xuncha
2026-01-21 19:23:11 +08:00
parent 21d785dd3c
commit 076c008329
2 changed files with 252 additions and 50 deletions

View File

@@ -221,6 +221,100 @@
} }
} }
.select-field {
position: relative;
margin-bottom: 10px;
}
.select-trigger {
width: 100%;
padding: 10px 16px;
border: 1px solid var(--border-color);
border-radius: 9999px;
font-size: 14px;
background: var(--bg-primary);
color: var(--text-primary);
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
cursor: pointer;
transition: all 0.2s;
&:hover {
border-color: var(--text-tertiary);
}
&.open {
border-color: var(--primary);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 15%, transparent);
}
}
.select-value {
flex: 1;
min-width: 0;
text-align: left;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.select-dropdown {
position: absolute;
top: calc(100% + 6px);
left: 0;
right: 0;
background: color-mix(in srgb, var(--bg-primary) 85%, var(--bg-secondary));
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 6px;
box-shadow: var(--shadow-md);
z-index: 20;
max-height: 320px;
overflow-y: auto;
backdrop-filter: blur(14px);
-webkit-backdrop-filter: blur(14px);
}
.select-option {
width: 100%;
text-align: left;
display: flex;
flex-direction: column;
gap: 4px;
padding: 10px 12px;
border: none;
border-radius: 10px;
background: transparent;
cursor: pointer;
transition: all 0.15s;
color: var(--text-primary);
font-size: 14px;
&:hover {
background: var(--bg-tertiary);
}
&.active {
background: color-mix(in srgb, var(--primary) 12%, transparent);
color: var(--primary);
}
}
.option-label {
font-weight: 500;
}
.option-desc {
font-size: 12px;
color: var(--text-tertiary);
}
.select-option.active .option-desc {
color: var(--primary);
}
.input-with-toggle { .input-with-toggle {
position: relative; position: relative;
display: flex; display: flex;
@@ -1096,13 +1190,15 @@
left: 0; left: 0;
right: 0; right: 0;
margin-top: 4px; margin-top: 4px;
background: var(--bg-secondary); background: color-mix(in srgb, var(--bg-primary) 85%, var(--bg-secondary));
border: 1px solid var(--border-primary); border: 1px solid var(--border-primary);
border-radius: 8px; border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
z-index: 100; z-index: 100;
max-height: 200px; max-height: 200px;
overflow-y: auto; overflow-y: auto;
backdrop-filter: blur(14px);
-webkit-backdrop-filter: blur(14px);
} }
.wxid-option { .wxid-option {

View File

@@ -41,6 +41,12 @@ function SettingsPage() {
const [wxidOptions, setWxidOptions] = useState<WxidOption[]>([]) const [wxidOptions, setWxidOptions] = useState<WxidOption[]>([])
const [showWxidSelect, setShowWxidSelect] = useState(false) const [showWxidSelect, setShowWxidSelect] = useState(false)
const wxidDropdownRef = useRef<HTMLDivElement>(null) const wxidDropdownRef = useRef<HTMLDivElement>(null)
const [showExportFormatSelect, setShowExportFormatSelect] = useState(false)
const [showExportDateRangeSelect, setShowExportDateRangeSelect] = useState(false)
const [showExportExcelColumnsSelect, setShowExportExcelColumnsSelect] = useState(false)
const exportFormatDropdownRef = useRef<HTMLDivElement>(null)
const exportDateRangeDropdownRef = useRef<HTMLDivElement>(null)
const exportExcelColumnsDropdownRef = useRef<HTMLDivElement>(null)
const [cachePath, setCachePath] = useState('') const [cachePath, setCachePath] = useState('')
const [logEnabled, setLogEnabled] = useState(false) const [logEnabled, setLogEnabled] = useState(false)
const [whisperModelName, setWhisperModelName] = useState('base') const [whisperModelName, setWhisperModelName] = useState('base')
@@ -85,13 +91,23 @@ function SettingsPage() {
// 点击外部关闭下拉框 // 点击外部关闭下拉框
useEffect(() => { useEffect(() => {
const handleClickOutside = (e: MouseEvent) => { const handleClickOutside = (e: MouseEvent) => {
if (showWxidSelect && wxidDropdownRef.current && !wxidDropdownRef.current.contains(e.target as Node)) { const target = e.target as Node
if (showWxidSelect && wxidDropdownRef.current && !wxidDropdownRef.current.contains(target)) {
setShowWxidSelect(false) setShowWxidSelect(false)
} }
if (showExportFormatSelect && exportFormatDropdownRef.current && !exportFormatDropdownRef.current.contains(target)) {
setShowExportFormatSelect(false)
}
if (showExportDateRangeSelect && exportDateRangeDropdownRef.current && !exportDateRangeDropdownRef.current.contains(target)) {
setShowExportDateRangeSelect(false)
}
if (showExportExcelColumnsSelect && exportExcelColumnsDropdownRef.current && !exportExcelColumnsDropdownRef.current.contains(target)) {
setShowExportExcelColumnsSelect(false)
}
} }
document.addEventListener('mousedown', handleClickOutside) document.addEventListener('mousedown', handleClickOutside)
return () => document.removeEventListener('mousedown', handleClickOutside) return () => document.removeEventListener('mousedown', handleClickOutside)
}, [showWxidSelect]) }, [showWxidSelect, showExportFormatSelect, showExportDateRangeSelect, showExportExcelColumnsSelect])
useEffect(() => { useEffect(() => {
const removeDb = window.electronAPI.key.onDbKeyStatus((payload) => { const removeDb = window.electronAPI.key.onDbKeyStatus((payload) => {
@@ -863,48 +879,114 @@ function SettingsPage() {
</div> </div>
) )
const renderExportTab = () => ( const exportFormatOptions = [
{ value: 'excel', label: 'Excel', desc: '电子表格,适合统计分析' },
{ value: 'chatlab', label: 'ChatLab', desc: '标准格式,支持其他软件导入' },
{ value: 'chatlab-jsonl', label: 'ChatLab JSONL', desc: '流式格式,适合大量消息' },
{ value: 'json', label: 'JSON', desc: '详细格式,包含完整消息信息' },
{ value: 'html', label: 'HTML', desc: '网页格式,可直接浏览' },
{ value: 'txt', label: 'TXT', desc: '纯文本,通用格式' },
{ value: 'sql', label: 'PostgreSQL', desc: '数据库脚本,便于导入到数据库' }
]
const exportDateRangeOptions = [
{ value: 'today', label: '今天' },
{ value: '7d', label: '最近7天' },
{ value: '30d', label: '最近30天' },
{ value: '90d', label: '最近90天' },
{ value: 'all', label: '全部时间' }
]
const exportExcelColumnOptions = [
{ value: 'compact', label: '精简列', desc: '序号、时间、发送者身份、消息类型、内容' },
{ value: 'full', label: '完整列', desc: '含发送者昵称/微信ID/备注' }
]
const getOptionLabel = (options: { value: string; label: string }[], value: string) => {
return options.find((option) => option.value === value)?.label ?? value
}
const renderExportTab = () => {
const exportExcelColumnsValue = exportDefaultExcelCompactColumns ? 'compact' : 'full'
const exportFormatLabel = getOptionLabel(exportFormatOptions, exportDefaultFormat)
const exportDateRangeLabel = getOptionLabel(exportDateRangeOptions, exportDefaultDateRange)
const exportExcelColumnsLabel = getOptionLabel(exportExcelColumnOptions, exportExcelColumnsValue)
return (
<div className="tab-content"> <div className="tab-content">
<div className="form-group"> <div className="form-group">
<label></label> <label></label>
<span className="form-hint"></span> <span className="form-hint"></span>
<select <div className="select-field" ref={exportFormatDropdownRef}>
value={exportDefaultFormat} <button
onChange={async (e) => { type="button"
const value = e.target.value className={`select-trigger ${showExportFormatSelect ? 'open' : ''}`}
setExportDefaultFormat(value) onClick={() => {
await configService.setExportDefaultFormat(value) setShowExportFormatSelect(!showExportFormatSelect)
showMessage('已更新导出格式默认值', true) setShowExportDateRangeSelect(false)
}} setShowExportExcelColumnsSelect(false)
> }}
<option value="excel">Excel</option> >
<option value="chatlab">ChatLab</option> <span className="select-value">{exportFormatLabel}</span>
<option value="chatlab-jsonl">ChatLab JSONL</option> <ChevronDown size={16} />
<option value="json">JSON</option> </button>
<option value="html">HTML</option> {showExportFormatSelect && (
<option value="txt">TXT</option> <div className="select-dropdown">
<option value="sql">PostgreSQL</option> {exportFormatOptions.map((option) => (
</select> <button
key={option.value}
type="button"
className={`select-option ${exportDefaultFormat === option.value ? 'active' : ''}`}
onClick={async () => {
setExportDefaultFormat(option.value)
await configService.setExportDefaultFormat(option.value)
showMessage('已更新导出格式默认值', true)
setShowExportFormatSelect(false)
}}
>
<span className="option-label">{option.label}</span>
{option.desc && <span className="option-desc">{option.desc}</span>}
</button>
))}
</div>
)}
</div>
</div> </div>
<div className="form-group"> <div className="form-group">
<label></label> <label></label>
<span className="form-hint"></span> <span className="form-hint"></span>
<select <div className="select-field" ref={exportDateRangeDropdownRef}>
value={exportDefaultDateRange} <button
onChange={async (e) => { type="button"
const value = e.target.value className={`select-trigger ${showExportDateRangeSelect ? 'open' : ''}`}
setExportDefaultDateRange(value) onClick={() => {
await configService.setExportDefaultDateRange(value) setShowExportDateRangeSelect(!showExportDateRangeSelect)
showMessage('已更新默认导出时间范围', true) setShowExportFormatSelect(false)
}} setShowExportExcelColumnsSelect(false)
> }}
<option value="today"></option> >
<option value="7d">7</option> <span className="select-value">{exportDateRangeLabel}</span>
<option value="30d">30</option> <ChevronDown size={16} />
<option value="90d">90</option> </button>
<option value="all"></option> {showExportDateRangeSelect && (
</select> <div className="select-dropdown">
{exportDateRangeOptions.map((option) => (
<button
key={option.value}
type="button"
className={`select-option ${exportDefaultDateRange === option.value ? 'active' : ''}`}
onClick={async () => {
setExportDefaultDateRange(option.value)
await configService.setExportDefaultDateRange(option.value)
showMessage('已更新默认导出时间范围', true)
setShowExportDateRangeSelect(false)
}}
>
<span className="option-label">{option.label}</span>
</button>
))}
</div>
)}
</div>
</div> </div>
<div className="form-group"> <div className="form-group">
@@ -956,21 +1038,45 @@ function SettingsPage() {
<div className="form-group"> <div className="form-group">
<label>Excel </label> <label>Excel </label>
<span className="form-hint"> Excel </span> <span className="form-hint"> Excel </span>
<select <div className="select-field" ref={exportExcelColumnsDropdownRef}>
value={exportDefaultExcelCompactColumns ? 'compact' : 'full'} <button
onChange={async (e) => { type="button"
const compact = e.target.value === 'compact' className={`select-trigger ${showExportExcelColumnsSelect ? 'open' : ''}`}
setExportDefaultExcelCompactColumns(compact) onClick={() => {
await configService.setExportDefaultExcelCompactColumns(compact) setShowExportExcelColumnsSelect(!showExportExcelColumnsSelect)
showMessage(compact ? '已启用精简列' : '已启用完整列', true) setShowExportFormatSelect(false)
}} setShowExportDateRangeSelect(false)
> }}
<option value="compact"></option> >
<option value="full">/ID/</option> <span className="select-value">{exportExcelColumnsLabel}</span>
</select> <ChevronDown size={16} />
</button>
{showExportExcelColumnsSelect && (
<div className="select-dropdown">
{exportExcelColumnOptions.map((option) => (
<button
key={option.value}
type="button"
className={`select-option ${exportExcelColumnsValue === option.value ? 'active' : ''}`}
onClick={async () => {
const compact = option.value === 'compact'
setExportDefaultExcelCompactColumns(compact)
await configService.setExportDefaultExcelCompactColumns(compact)
showMessage(compact ? '已启用精简列' : '已启用完整列', true)
setShowExportExcelColumnsSelect(false)
}}
>
<span className="option-label">{option.label}</span>
{option.desc && <span className="option-desc">{option.desc}</span>}
</button>
))}
</div>
)}
</div>
</div> </div>
</div> </div>
) )
}
const renderCacheTab = () => ( const renderCacheTab = () => (
<div className="tab-content"> <div className="tab-content">
<p className="section-desc"></p> <p className="section-desc"></p>