fix:修复了日期选择的问题

This commit is contained in:
xuncha
2026-01-17 12:02:17 +08:00
parent 650de55202
commit 9402483d87
4 changed files with 102 additions and 14 deletions

View File

@@ -5,6 +5,7 @@ import { useChatStore } from '../stores/chatStore'
import type { ChatSession, Message } from '../types/models' import type { ChatSession, Message } from '../types/models'
import { getEmojiPath } from 'wechat-emojis' import { getEmojiPath } from 'wechat-emojis'
import { ImagePreview } from '../components/ImagePreview' import { ImagePreview } from '../components/ImagePreview'
import * as configService from '../services/config'
import './ChatPage.scss' import './ChatPage.scss'
interface ChatPageProps { interface ChatPageProps {
@@ -1317,6 +1318,16 @@ function MessageBubble({ message, session, showTime, myAvatarUrl, isGroupChat }:
const [voiceTranscriptError, setVoiceTranscriptError] = useState(false) const [voiceTranscriptError, setVoiceTranscriptError] = useState(false)
const voiceTranscriptRequestedRef = useRef(false) const voiceTranscriptRequestedRef = useRef(false)
const [showImagePreview, setShowImagePreview] = useState(false) const [showImagePreview, setShowImagePreview] = useState(false)
const [autoTranscribeVoice, setAutoTranscribeVoice] = useState(true)
// 加载自动转文字配置
useEffect(() => {
const loadConfig = async () => {
const enabled = await configService.getAutoTranscribeVoice()
setAutoTranscribeVoice(enabled)
}
loadConfig()
}, [])
// 从缓存获取表情包 data URL // 从缓存获取表情包 data URL
const cacheKey = message.emojiMd5 || message.emojiCdnUrl || '' const cacheKey = message.emojiMd5 || message.emojiCdnUrl || ''
@@ -1638,10 +1649,11 @@ function MessageBubble({ message, session, showTime, myAvatarUrl, isGroupChat }:
useEffect(() => { useEffect(() => {
if (!isVoice) return if (!isVoice) return
if (!voiceDataUrl) return if (!voiceDataUrl) return
if (!autoTranscribeVoice) return // 如果自动转文字已关闭,不自动转文字
if (voiceTranscriptError) return if (voiceTranscriptError) return
if (voiceTranscriptLoading || voiceTranscript !== undefined || voiceTranscriptRequestedRef.current) return if (voiceTranscriptLoading || voiceTranscript !== undefined || voiceTranscriptRequestedRef.current) return
void requestVoiceTranscript() void requestVoiceTranscript()
}, [isVoice, voiceDataUrl, voiceTranscript, voiceTranscriptError, voiceTranscriptLoading, requestVoiceTranscript]) }, [isVoice, voiceDataUrl, autoTranscribeVoice, voiceTranscript, voiceTranscriptError, voiceTranscriptLoading, requestVoiceTranscript])
if (isSystem) { if (isSystem) {
return ( return (

View File

@@ -215,18 +215,54 @@ function ExportPage() {
const year = calendarDate.getFullYear() const year = calendarDate.getFullYear()
const month = calendarDate.getMonth() const month = calendarDate.getMonth()
const selectedDate = new Date(year, month, day) const selectedDate = new Date(year, month, day)
// 设置时间为当天的开始或结束
selectedDate.setHours(selectingStart ? 0 : 23, selectingStart ? 0 : 59, selectingStart ? 0 : 59, selectingStart ? 0 : 999)
const now = new Date()
// 如果选择的日期晚于当前时间,限制为当前时间
if (selectedDate > now) {
selectedDate.setTime(now.getTime())
}
if (selectingStart) { if (selectingStart) {
setOptions({ // 选择开始日期
...options, const currentEnd = options.dateRange?.end || new Date()
dateRange: options.dateRange ? { ...options.dateRange, start: selectedDate } : { start: selectedDate, end: new Date() } // 如果选择的开始日期晚于结束日期,则同时更新结束日期
}) if (selectedDate > currentEnd) {
const newEnd = new Date(selectedDate)
newEnd.setHours(23, 59, 59, 999)
// 确保结束日期也不晚于当前时间
if (newEnd > now) {
newEnd.setTime(now.getTime())
}
setOptions({
...options,
dateRange: { start: selectedDate, end: newEnd }
})
} else {
setOptions({
...options,
dateRange: options.dateRange ? { ...options.dateRange, start: selectedDate } : { start: selectedDate, end: new Date() }
})
}
setSelectingStart(false) setSelectingStart(false)
} else { } else {
setOptions({ // 选择结束日期
...options, const currentStart = options.dateRange?.start || new Date(Date.now() - 7 * 24 * 60 * 60 * 1000)
dateRange: options.dateRange ? { ...options.dateRange, end: selectedDate } : { start: new Date(), end: selectedDate } // 如果选择的结束日期早于开始日期,则同时更新开始日期
}) if (selectedDate < currentStart) {
const newStart = new Date(selectedDate)
newStart.setHours(0, 0, 0, 0)
setOptions({
...options,
dateRange: { start: newStart, end: selectedDate }
})
} else {
setOptions({
...options,
dateRange: options.dateRange ? { ...options.dateRange, end: selectedDate } : { start: new Date(), end: selectedDate }
})
}
setSelectingStart(true) setSelectingStart(true)
} }
} }
@@ -547,6 +583,9 @@ function ExportPage() {
<div className="export-overlay" onClick={() => setShowDatePicker(false)}> <div className="export-overlay" onClick={() => setShowDatePicker(false)}>
<div className="date-picker-modal" onClick={e => e.stopPropagation()}> <div className="date-picker-modal" onClick={e => e.stopPropagation()}>
<h3></h3> <h3></h3>
<p style={{ fontSize: '13px', color: 'var(--text-secondary)', margin: '8px 0 16px 0' }}>
</p>
<div className="quick-select"> <div className="quick-select">
<button <button
className="quick-btn" className="quick-btn"
@@ -641,12 +680,16 @@ function ExportPage() {
const isStart = options.dateRange?.start.toDateString() === currentDate.toDateString() const isStart = options.dateRange?.start.toDateString() === currentDate.toDateString()
const isEnd = options.dateRange?.end.toDateString() === currentDate.toDateString() const isEnd = options.dateRange?.end.toDateString() === currentDate.toDateString()
const isInRange = options.dateRange && currentDate >= options.dateRange.start && currentDate <= options.dateRange.end const isInRange = options.dateRange && currentDate >= options.dateRange.start && currentDate <= options.dateRange.end
const today = new Date()
today.setHours(0, 0, 0, 0)
const isFuture = currentDate > today
return ( return (
<div <div
key={day} key={day}
className={`calendar-day ${isStart ? 'start' : ''} ${isEnd ? 'end' : ''} ${isInRange ? 'in-range' : ''}`} className={`calendar-day ${isStart ? 'start' : ''} ${isEnd ? 'end' : ''} ${isInRange ? 'in-range' : ''} ${isFuture ? 'disabled' : ''}`}
onClick={() => handleDateSelect(day)} onClick={() => !isFuture && handleDateSelect(day)}
style={{ cursor: isFuture ? 'not-allowed' : 'pointer', opacity: isFuture ? 0.3 : 1 }}
> >
{day} {day}
</div> </div>

View File

@@ -58,6 +58,7 @@ function SettingsPage() {
const [whisperModelName, setWhisperModelName] = useState('base') const [whisperModelName, setWhisperModelName] = useState('base')
const [whisperModelDir, setWhisperModelDir] = useState('') const [whisperModelDir, setWhisperModelDir] = useState('')
const [whisperDownloadSource, setWhisperDownloadSource] = useState('tsinghua') const [whisperDownloadSource, setWhisperDownloadSource] = useState('tsinghua')
const [autoTranscribeVoice, setAutoTranscribeVoice] = useState(true)
const [isWhisperDownloading, setIsWhisperDownloading] = useState(false) const [isWhisperDownloading, setIsWhisperDownloading] = useState(false)
const [whisperDownloadProgress, setWhisperDownloadProgress] = useState(0) const [whisperDownloadProgress, setWhisperDownloadProgress] = useState(0)
const [whisperModelStatus, setWhisperModelStatus] = useState<{ exists: boolean; path?: string } | null>(null) const [whisperModelStatus, setWhisperModelStatus] = useState<{ exists: boolean; path?: string } | null>(null)
@@ -125,6 +126,7 @@ function SettingsPage() {
const savedWhisperModelName = await configService.getWhisperModelName() const savedWhisperModelName = await configService.getWhisperModelName()
const savedWhisperModelDir = await configService.getWhisperModelDir() const savedWhisperModelDir = await configService.getWhisperModelDir()
const savedWhisperSource = await configService.getWhisperDownloadSource() const savedWhisperSource = await configService.getWhisperDownloadSource()
const savedAutoTranscribeVoice = await configService.getAutoTranscribeVoice()
if (savedKey) setDecryptKey(savedKey) if (savedKey) setDecryptKey(savedKey)
if (savedPath) setDbPath(savedPath) if (savedPath) setDbPath(savedPath)
@@ -138,6 +140,7 @@ function SettingsPage() {
if (savedWhisperModelName) setWhisperModelName(savedWhisperModelName) if (savedWhisperModelName) setWhisperModelName(savedWhisperModelName)
if (savedWhisperModelDir) setWhisperModelDir(savedWhisperModelDir) if (savedWhisperModelDir) setWhisperModelDir(savedWhisperModelDir)
if (savedWhisperSource) setWhisperDownloadSource(savedWhisperSource) if (savedWhisperSource) setWhisperDownloadSource(savedWhisperSource)
setAutoTranscribeVoice(savedAutoTranscribeVoice)
} catch (e) { } catch (e) {
console.error('加载配置失败:', e) console.error('加载配置失败:', e)
} }
@@ -762,8 +765,25 @@ function SettingsPage() {
<p className="section-desc"></p> <p className="section-desc"></p>
<div className="form-group whisper-section"> <div className="form-group whisper-section">
<label> (Whisper)</label> <label> (Whisper)</label>
<span className="form-hint"></span> <span className="form-hint"></span>
<div className="whisper-grid">
<label style={{ marginTop: '12px', marginBottom: '8px', display: 'block' }}></label>
<label className="switch" htmlFor="auto-transcribe-toggle">
<input
id="auto-transcribe-toggle"
className="switch-input"
type="checkbox"
checked={autoTranscribeVoice}
onChange={(e) => {
const enabled = e.target.checked
setAutoTranscribeVoice(enabled)
configService.setAutoTranscribeVoice(enabled)
}}
/>
<span className="switch-slider" />
</label>
<div className="whisper-grid" style={{ marginTop: '16px' }}>
<div className="whisper-field"> <div className="whisper-field">
<span className="field-label"></span> <span className="field-label"></span>
<select <select

View File

@@ -20,7 +20,8 @@ export const CONFIG_KEYS = {
IMAGE_AES_KEY: 'imageAesKey', IMAGE_AES_KEY: 'imageAesKey',
WHISPER_MODEL_NAME: 'whisperModelName', WHISPER_MODEL_NAME: 'whisperModelName',
WHISPER_MODEL_DIR: 'whisperModelDir', WHISPER_MODEL_DIR: 'whisperModelDir',
WHISPER_DOWNLOAD_SOURCE: 'whisperDownloadSource' WHISPER_DOWNLOAD_SOURCE: 'whisperDownloadSource',
AUTO_TRANSCRIBE_VOICE: 'autoTranscribeVoice'
} as const } as const
// 获取解密密钥 // 获取解密密钥
@@ -218,3 +219,15 @@ export async function getOnboardingDone(): Promise<boolean> {
export async function setOnboardingDone(done: boolean): Promise<void> { export async function setOnboardingDone(done: boolean): Promise<void> {
await config.set(CONFIG_KEYS.ONBOARDING_DONE, done) await config.set(CONFIG_KEYS.ONBOARDING_DONE, done)
} }
// 获取是否自动转文字
export async function getAutoTranscribeVoice(): Promise<boolean> {
const value = await config.get(CONFIG_KEYS.AUTO_TRANSCRIBE_VOICE)
// 默认为 true
return value !== false
}
// 设置是否自动转文字
export async function setAutoTranscribeVoice(enabled: boolean): Promise<void> {
await config.set(CONFIG_KEYS.AUTO_TRANSCRIBE_VOICE, enabled)
}