feat: 优化代码结构(在Claude的帮助下)

This commit is contained in:
Lan
2025-09-04 12:01:26 +08:00
parent ca7f576b36
commit 97595f2a00
28 changed files with 2504 additions and 318 deletions
+144
View File
@@ -0,0 +1,144 @@
import { ref, computed } from 'vue'
import { FileService } from '@/services'
import { useAlertStore } from '@/stores/alertStore'
import type { FileInfo } from '@/types'
import { saveAs } from 'file-saver'
export function useFileDownload() {
const alertStore = useAlertStore()
// 状态管理
const isLoading = ref(false)
const fileInfo = ref<FileInfo | null>(null)
const downloadCode = ref('')
// 计算属性
const hasFileInfo = computed(() => fileInfo.value !== null)
const canDownload = computed(() => hasFileInfo.value && !isLoading.value)
// 获取文件信息
const getFileInfo = async (code: string): Promise<FileInfo | null> => {
if (!code.trim()) {
alertStore.showAlert('请输入取件码', 'warning')
return null
}
try {
isLoading.value = true
downloadCode.value = code
const response = await FileService.getFile(code)
if (response.code === 200 && response.detail) {
fileInfo.value = response.detail
return response.detail
} else {
throw new Error(response.message || '文件不存在或已过期')
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : '获取文件信息失败'
alertStore.showAlert(errorMessage, 'error')
fileInfo.value = null
return null
} finally {
isLoading.value = false
}
}
// 下载文件
const downloadFile = async (code?: string): Promise<boolean> => {
const targetCode = code || downloadCode.value
if (!targetCode.trim()) {
alertStore.showAlert('请输入取件码', 'warning')
return false
}
try {
isLoading.value = true
// 如果没有文件信息,先获取
if (!fileInfo.value || downloadCode.value !== targetCode) {
const info = await getFileInfo(targetCode)
if (!info) {
return false
}
}
const blob = await FileService.downloadFile(targetCode)
// 使用 file-saver 保存文件
if (fileInfo.value?.name) {
saveAs(blob, fileInfo.value.name)
alertStore.showAlert('文件下载成功!', 'success')
return true
} else {
throw new Error('文件名获取失败')
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : '下载失败'
alertStore.showAlert(errorMessage, 'error')
return false
} finally {
isLoading.value = false
}
}
// 重置状态
const resetDownload = () => {
isLoading.value = false
fileInfo.value = null
downloadCode.value = ''
}
// 格式化文件大小
const formatFileSize = (bytes: number): string => {
if (bytes === 0) return '0 B'
const k = 1024
const sizes = ['B', 'KB', 'MB', 'GB']
const i = Math.floor(Math.log(bytes) / Math.log(k))
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
}
// 格式化时间
const formatTime = (timeString: string): string => {
try {
const date = new Date(timeString)
return date.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
})
} catch {
return timeString
}
}
// 检查文件是否过期
const isFileExpired = computed(() => {
if (!fileInfo.value?.expireTime) return false
return new Date(fileInfo.value.expireTime) < new Date()
})
return {
// 状态
isLoading,
fileInfo,
downloadCode,
// 计算属性
hasFileInfo,
canDownload,
isFileExpired,
// 方法
getFileInfo,
downloadFile,
resetDownload,
formatFileSize,
formatTime
}
}
+139
View File
@@ -0,0 +1,139 @@
import { ref, computed, readonly } from 'vue'
import { FileService } from '@/services'
import { useAlertStore } from '@/stores/alertStore'
import type { UploadProgress, UploadStatus } from '@/types'
import { UPLOAD_STATUS, FILE_SIZE_LIMITS } from '@/constants'
export function useFileUpload() {
const alertStore = useAlertStore()
// 状态管理
const uploadStatus = ref<UploadStatus>(UPLOAD_STATUS.IDLE)
const uploadProgress = ref<UploadProgress>({
loaded: 0,
total: 0,
percentage: 0
})
const uploadedCode = ref<string>('')
const currentFile = ref<File | null>(null)
// 计算属性
const isUploading = computed(() => uploadStatus.value === UPLOAD_STATUS.UPLOADING)
const isSuccess = computed(() => uploadStatus.value === UPLOAD_STATUS.SUCCESS)
const isError = computed(() => uploadStatus.value === UPLOAD_STATUS.ERROR)
const isIdle = computed(() => uploadStatus.value === UPLOAD_STATUS.IDLE)
// 文件验证
const validateFile = (file: File): boolean => {
if (file.size > FILE_SIZE_LIMITS.MAX_FILE_SIZE) {
alertStore.showAlert(
`文件大小不能超过 ${Math.round(FILE_SIZE_LIMITS.MAX_FILE_SIZE / 1024 / 1024)}MB`,
'error'
)
return false
}
return true
}
// 上传文件
const uploadFile = async (file: File): Promise<string | null> => {
if (!validateFile(file)) {
return null
}
try {
uploadStatus.value = UPLOAD_STATUS.UPLOADING
currentFile.value = file
uploadedCode.value = ''
const response = await FileService.uploadFile(file, (progress) => {
uploadProgress.value = progress
})
if (response.code === 200 && response.detail?.code) {
uploadStatus.value = UPLOAD_STATUS.SUCCESS
uploadedCode.value = String(response.detail.code)
alertStore.showAlert('文件上传成功!', 'success')
return String(response.detail.code)
} else {
throw new Error(response.message || '上传失败')
}
} catch (error) {
uploadStatus.value = UPLOAD_STATUS.ERROR
const errorMessage = error instanceof Error ? error.message : '上传失败'
alertStore.showAlert(errorMessage, 'error')
return null
}
}
// 上传文本
const uploadText = async (text: string): Promise<string | null> => {
if (!text.trim()) {
alertStore.showAlert('请输入要发送的文本内容', 'warning')
return null
}
try {
uploadStatus.value = UPLOAD_STATUS.UPLOADING
uploadedCode.value = ''
const response = await FileService.uploadText(text)
if (response.code === 200 && response.detail?.code) {
uploadStatus.value = UPLOAD_STATUS.SUCCESS
uploadedCode.value = String(response.detail.code)
alertStore.showAlert('文本发送成功!', 'success')
return String(response.detail.code)
} else {
throw new Error(response.message || '发送失败')
}
} catch (error) {
uploadStatus.value = UPLOAD_STATUS.ERROR
const errorMessage = error instanceof Error ? error.message : '发送失败'
alertStore.showAlert(errorMessage, 'error')
return null
}
}
// 重置状态
const resetUpload = () => {
uploadStatus.value = UPLOAD_STATUS.IDLE
uploadProgress.value = {
loaded: 0,
total: 0,
percentage: 0
}
uploadedCode.value = ''
currentFile.value = null
}
// 格式化文件大小
const formatFileSize = (bytes: number): string => {
if (bytes === 0) return '0 B'
const k = 1024
const sizes = ['B', 'KB', 'MB', 'GB']
const i = Math.floor(Math.log(bytes) / Math.log(k))
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
}
return {
// 状态
uploadStatus: readonly(uploadStatus),
uploadProgress: readonly(uploadProgress),
uploadedCode: readonly(uploadedCode),
currentFile: readonly(currentFile),
// 计算属性
isUploading,
isSuccess,
isError,
isIdle,
// 方法
uploadFile,
uploadText,
resetUpload,
validateFile,
formatFileSize
}
}
+142
View File
@@ -0,0 +1,142 @@
import { ref, computed } from 'vue'
import { ConfigService } from '@/services'
import { useAlertStore } from '@/stores/alertStore'
import type { SystemConfig } from '@/types'
import { STORAGE_KEYS, DEFAULT_CONFIG } from '@/constants'
export function useSystemConfig() {
const alertStore = useAlertStore()
// 状态管理
const config = ref<SystemConfig>({ ...DEFAULT_CONFIG })
const isLoading = ref(false)
// 从本地存储获取配置
const getStoredConfig = (): SystemConfig | null => {
try {
const storedConfig = localStorage.getItem(STORAGE_KEYS.CONFIG)
if (storedConfig) {
return JSON.parse(storedConfig)
}
} catch (error) {
console.error('解析本地配置失败:', error)
}
return null
}
// 保存配置到本地存储
const saveConfigToStorage = (configData: SystemConfig) => {
try {
localStorage.setItem(STORAGE_KEYS.CONFIG, JSON.stringify(configData))
} catch (error) {
console.error('保存配置到本地存储失败:', error)
}
}
// 获取系统配置
const fetchConfig = async (): Promise<SystemConfig | null> => {
try {
isLoading.value = true
const response = await ConfigService.getConfig()
if (response.code === 200 && response.detail) {
config.value = { ...DEFAULT_CONFIG, ...response.detail }
saveConfigToStorage(config.value)
// 处理通知
if (response.detail.notify_title && response.detail.notify_content) {
const notifyKey = response.detail.notify_title + response.detail.notify_content
const lastNotify = localStorage.getItem(STORAGE_KEYS.NOTIFY)
if (lastNotify !== notifyKey) {
localStorage.setItem(STORAGE_KEYS.NOTIFY, notifyKey)
alertStore.showAlert(
`${response.detail.notify_title}: ${response.detail.notify_content}`,
'success'
)
}
}
return config.value
} else {
throw new Error(response.message || '获取配置失败')
}
} catch (error) {
// 如果网络请求失败,尝试使用本地存储的配置
const storedConfig = getStoredConfig()
if (storedConfig) {
config.value = storedConfig
return config.value
}
const errorMessage = error instanceof Error ? error.message : '获取配置失败'
alertStore.showAlert(errorMessage, 'error')
return null
} finally {
isLoading.value = false
}
}
// 更新系统配置
const updateConfig = async (newConfig: Partial<SystemConfig>): Promise<boolean> => {
try {
isLoading.value = true
const response = await ConfigService.updateConfig(newConfig)
if (response.code === 200) {
config.value = { ...config.value, ...newConfig }
saveConfigToStorage(config.value)
alertStore.showAlert('配置更新成功!', 'success')
return true
} else {
throw new Error(response.message || '更新配置失败')
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : '更新配置失败'
alertStore.showAlert(errorMessage, 'error')
return false
} finally {
isLoading.value = false
}
}
// 初始化配置
const initConfig = async () => {
// 先尝试从本地存储加载
const storedConfig = getStoredConfig()
if (storedConfig) {
config.value = storedConfig
}
// 然后从服务器获取最新配置
await fetchConfig()
}
// 计算属性
const maxFileSizeMB = computed(() => {
return Math.round(config.value.maxFileSize / 1024 / 1024)
})
const isConfigLoaded = computed(() => {
return config.value.name !== DEFAULT_CONFIG.name || !isLoading.value
})
return {
// 状态
config,
isLoading,
// 计算属性
maxFileSizeMB,
isConfigLoaded,
// 方法
fetchConfig,
updateConfig,
initConfig,
getStoredConfig,
saveConfigToStorage
}
}
+138
View File
@@ -0,0 +1,138 @@
import { ref, computed } from 'vue'
import { STORAGE_KEYS, THEME_MODES } from '@/constants'
import type { ThemeMode } from '@/types'
export function useTheme() {
// 状态管理
const isDarkMode = ref(false)
const themeMode = ref<ThemeMode>(THEME_MODES.SYSTEM)
// 检查系统颜色模式
const checkSystemColorScheme = (): boolean => {
return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
}
// 从本地存储获取用户之前的选择
const getUserPreference = (): ThemeMode | null => {
const storedPreference = localStorage.getItem(STORAGE_KEYS.COLOR_MODE)
if (storedPreference && Object.values(THEME_MODES).includes(storedPreference as ThemeMode)) {
return storedPreference as ThemeMode
}
return null
}
// 设置颜色模式
const setThemeMode = (mode: ThemeMode) => {
themeMode.value = mode
localStorage.setItem(STORAGE_KEYS.COLOR_MODE, mode)
// 根据模式设置实际的暗色模式状态
if (mode === THEME_MODES.SYSTEM) {
isDarkMode.value = checkSystemColorScheme()
} else {
isDarkMode.value = mode === THEME_MODES.DARK
}
// 更新 HTML 类名
updateHtmlClass()
}
// 更新 HTML 元素的类名
const updateHtmlClass = () => {
const html = document.documentElement
if (isDarkMode.value) {
html.classList.add('dark')
} else {
html.classList.remove('dark')
}
}
// 切换主题
const toggleTheme = () => {
if (themeMode.value === THEME_MODES.LIGHT) {
setThemeMode(THEME_MODES.DARK)
} else if (themeMode.value === THEME_MODES.DARK) {
setThemeMode(THEME_MODES.SYSTEM)
} else {
setThemeMode(THEME_MODES.LIGHT)
}
}
// 监听系统主题变化
const setupSystemThemeListener = () => {
if (window.matchMedia) {
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
const handleChange = (e: MediaQueryListEvent) => {
if (themeMode.value === THEME_MODES.SYSTEM) {
isDarkMode.value = e.matches
updateHtmlClass()
}
}
mediaQuery.addEventListener('change', handleChange)
// 返回清理函数
return () => {
mediaQuery.removeEventListener('change', handleChange)
}
}
return () => {}
}
// 初始化主题
const initTheme = () => {
const userPreference = getUserPreference()
if (userPreference) {
setThemeMode(userPreference)
} else {
setThemeMode(THEME_MODES.SYSTEM)
}
// 设置系统主题监听
return setupSystemThemeListener()
}
// 计算属性
const themeIcon = computed(() => {
switch (themeMode.value) {
case THEME_MODES.LIGHT:
return 'sun'
case THEME_MODES.DARK:
return 'moon'
case THEME_MODES.SYSTEM:
return 'monitor'
default:
return 'monitor'
}
})
const themeLabel = computed(() => {
switch (themeMode.value) {
case THEME_MODES.LIGHT:
return '浅色模式'
case THEME_MODES.DARK:
return '深色模式'
case THEME_MODES.SYSTEM:
return '跟随系统'
default:
return '跟随系统'
}
})
return {
// 状态
isDarkMode,
themeMode,
// 计算属性
themeIcon,
themeLabel,
// 方法
setThemeMode,
toggleTheme,
initTheme,
checkSystemColorScheme
}
}