diff --git a/components.d.ts b/components.d.ts index cee4f7e..4ca78d3 100644 --- a/components.d.ts +++ b/components.d.ts @@ -14,14 +14,15 @@ declare module 'vue' { ABadge: typeof import('ant-design-vue/es')['Badge'] AButton: typeof import('ant-design-vue/es')['Button'] ACard: typeof import('ant-design-vue/es')['Card'] - ACardMeta: typeof import('ant-design-vue/es')['CardMeta'] ACascader: typeof import('ant-design-vue/es')['Cascader'] AccountSetting: typeof import('./src/views/User/AccountSetting/AccountSetting.vue')['default'] AccountSettingBackup: typeof import('./src/views/User/AccountSetting/components/AccountSettingBackup/AccountSettingBackup.vue')['default'] AccountSettingHome: typeof import('./src/views/User/AccountSetting/components/AccountSettingHome/AccountSettingHome.vue')['default'] AccountSettingInfo: typeof import('./src/views/User/AccountSetting/components/AccountSettingInfo/AccountSettingInfo.vue')['default'] + AccountSettingLog: typeof import('./src/views/User/AccountSetting/components/AccountSettingLog/AccountSettingLog.vue')['default'] AccountSettingSidebar: typeof import('./src/views/User/AccountSetting/components/AccountSettingSidebar/AccountSettingSidebar.vue')['default'] AccountSettingStorage: typeof import('./src/views/User/AccountSetting/components/AccountSettingStorage/AccountSettingStorage.vue')['default'] + AccountSettingTask: typeof import('./src/views/User/AccountSetting/components/AccountSettingTask/AccountSettingTask.vue')['default'] ACheckbox: typeof import('ant-design-vue/es')['Checkbox'] ACheckboxGroup: typeof import('ant-design-vue/es')['CheckboxGroup'] ACol: typeof import('ant-design-vue/es')['Col'] @@ -67,7 +68,6 @@ declare module 'vue' { ASelect: typeof import('ant-design-vue/es')['Select'] ASelectOption: typeof import('ant-design-vue/es')['SelectOption'] ASkeleton: typeof import('ant-design-vue/es')['Skeleton'] - ASlider: typeof import('ant-design-vue/es')['Slider'] ASpace: typeof import('ant-design-vue/es')['Space'] ASpin: typeof import('ant-design-vue/es')['Spin'] AStatistic: typeof import('ant-design-vue/es')['Statistic'] @@ -78,9 +78,10 @@ declare module 'vue' { ATabs: typeof import('ant-design-vue/es')['Tabs'] ATag: typeof import('ant-design-vue/es')['Tag'] ATextarea: typeof import('ant-design-vue/es')['Textarea'] + ATimeline: typeof import('ant-design-vue/es')['Timeline'] + ATimelineItem: typeof import('ant-design-vue/es')['TimelineItem'] + ATimePicker: typeof import('ant-design-vue/es')['TimePicker'] ATooltip: typeof import('ant-design-vue/es')['Tooltip'] - ATree: typeof import('ant-design-vue/es')['Tree'] - ATreeSelect: typeof import('ant-design-vue/es')['TreeSelect'] ATypography: typeof import('ant-design-vue/es')['Typography'] ATypographyParagraph: typeof import('ant-design-vue/es')['TypographyParagraph'] AUpload: typeof import('ant-design-vue/es')['Upload'] @@ -94,6 +95,7 @@ declare module 'vue' { CheckCard: typeof import('./src/components/CheckCard/CheckCard.vue')['default'] CheckCircleOutlined: typeof import('@ant-design/icons-vue')['CheckCircleOutlined'] CheckOutlined: typeof import('@ant-design/icons-vue')['CheckOutlined'] + ClockCircleOutlined: typeof import('@ant-design/icons-vue')['ClockCircleOutlined'] CloseCircleOutlined: typeof import('@ant-design/icons-vue')['CloseCircleOutlined'] CloseOutlined: typeof import('@ant-design/icons-vue')['CloseOutlined'] Clouds: typeof import('./src/components/Clouds/Clouds.vue')['default'] @@ -106,31 +108,30 @@ declare module 'vue' { CommonPhoneUpload: typeof import('./src/views/Phone/CommonPhoneUpload/CommonPhoneUpload.vue')['default'] CompareImage: typeof import('./src/views/Upscale/CompareImage.vue')['default'] Dashboard: typeof import('./src/views/Admin/System/Pages/Dashboard.vue')['default'] + DeleteConfirmModal: typeof import('./src/views/User/AccountSetting/components/AccountSettingTask/components/DeleteConfirmModal.vue')['default'] DeleteOutlined: typeof import('@ant-design/icons-vue')['DeleteOutlined'] DownloadOutlined: typeof import('@ant-design/icons-vue')['DownloadOutlined'] DownOutlined: typeof import('@ant-design/icons-vue')['DownOutlined'] DynamicTitle: typeof import('./src/components/DynamicTitle/DynamicTitle.vue')['default'] EditOutlined: typeof import('@ant-design/icons-vue')['EditOutlined'] EmailModal: typeof import('./src/views/User/AccountSetting/components/AccountSettingHome/EmailModal.vue')['default'] + ExclamationCircleOutlined: typeof import('@ant-design/icons-vue')['ExclamationCircleOutlined'] EyeInvisibleOutlined: typeof import('@ant-design/icons-vue')['EyeInvisibleOutlined'] EyeOutlined: typeof import('@ant-design/icons-vue')['EyeOutlined'] FileImageOutlined: typeof import('@ant-design/icons-vue')['FileImageOutlined'] FilerobotImageEditor: typeof import('./src/components/FilerobotImageEditor/FilerobotImageEditor.vue')['default'] Folder: typeof import('./src/components/Folder/Folder.vue')['default'] - FolderOutlined: typeof import('@ant-design/icons-vue')['FolderOutlined'] ForgetPage: typeof import('./src/views/Forget/ForgetPage.vue')['default'] GradientText: typeof import('./src/components/MyUI/GradientText/GradientText.vue')['default'] + HeatmapMax: typeof import('./src/components/HeatmapMax/HeatmapMax.vue')['default'] HeatmapPro: typeof import('./src/components/HeatmapPro/HeatmapPro.vue')['default'] ImageBed: typeof import('./src/views/ImageBed/index.vue')['default'] ImageEnhancer: typeof import('./src/components/ImageEnhancer/ImageEnhancer.vue')['default'] - ImageList: typeof import('./src/views/Photograph/PrivacySpace/ImageList.vue')['default'] ImageShare: typeof import('./src/views/Share/ImageShare/ImageShare.vue')['default'] ImageToolbar: typeof import('./src/components/ImageToolbar/ImageToolbar.vue')['default'] ImageUpload: typeof import('./src/components/ImageUpload/ImageUpload.vue')['default'] ImageWaterfallList: typeof import('./src/components/ImageWaterfallList/ImageWaterfallList.vue')['default'] - InboxOutlined: typeof import('@ant-design/icons-vue')['InboxOutlined'] Index: typeof import('./src/views/Admin/System/Index.vue')['default'] - InfoCircleOutlined: typeof import('@ant-design/icons-vue')['InfoCircleOutlined'] LandingPage: typeof import('./src/views/Landing/LandingPage.vue')['default'] LeftOutlined: typeof import('@ant-design/icons-vue')['LeftOutlined'] LinkOutlined: typeof import('@ant-design/icons-vue')['LinkOutlined'] @@ -147,7 +148,6 @@ declare module 'vue' { LogoutOutlined: typeof import('@ant-design/icons-vue')['LogoutOutlined'] MainPage: typeof import('./src/views/Main/MainPage.vue')['default'] MessageReport: typeof import('./src/components/CommentReply/src/MessageReport/MessageReport.vue')['default'] - MinusOutlined: typeof import('@ant-design/icons-vue')['MinusOutlined'] NotFound: typeof import('./src/views/404/NotFound.vue')['default'] OrderedListOutlined: typeof import('@ant-design/icons-vue')['OrderedListOutlined'] PageError403: typeof import('./src/views/Admin/Error/PageError403.vue')['default'] @@ -171,6 +171,7 @@ declare module 'vue' { Popover: typeof import('./src/components/MyUI/Popover/Popover.vue')['default'] PreviewBlurDetect: typeof import('./src/views/Preview/PreviewBlurDetect/PreviewBlurDetect.vue')['default'] PreviewOCR: typeof import('./src/views/Preview/PreviewOCR/PreviewOCR.vue')['default'] + PreviewQRCode: typeof import('./src/views/Preview/PreviewQRCode/PreviewQRCode.vue')['default'] PrivacyImageList: typeof import('./src/views/Photograph/PrivacySpace/PrivacyImageList.vue')['default'] PrivacySpace: typeof import('./src/views/Photograph/PrivacySpace/PrivacySpace.vue')['default'] QrcodeOutlined: typeof import('@ant-design/icons-vue')['QrcodeOutlined'] @@ -200,12 +201,14 @@ declare module 'vue' { StarButton: typeof import('./src/components/StarButton/StarButton.vue')['default'] StorageCard: typeof import('./src/views/User/AccountSetting/components/AccountSettingStorage/StorageCard.vue')['default'] StorageManagement: typeof import('./src/views/Admin/System/Pages/StorageManagement.vue')['default'] - System: typeof import('./src/views/Admin/System/index.vue')['default'] SystemHeader: typeof import('./src/views/Admin/System/Components/SystemHeader.vue')['default'] SystemLogs: typeof import('./src/views/Admin/System/Pages/SystemLogs.vue')['default'] SystemSidebar: typeof import('./src/views/Admin/System/Components/SystemSidebar.vue')['default'] TabletOutlined: typeof import('@ant-design/icons-vue')['TabletOutlined'] - TestView: typeof import('./src/views/Preview/TestView.vue')['default'] + TaskCard: typeof import('./src/views/User/AccountSetting/components/AccountSettingTask/components/TaskCard.vue')['default'] + TaskForm: typeof import('./src/views/User/AccountSetting/components/AccountSettingTask/components/TaskForm.vue')['default'] + TaskSchedule: typeof import('./src/components/TaskSchedule/TaskSchedule.vue')['default'] + TaskTypeSelector: typeof import('./src/views/User/AccountSetting/components/AccountSettingTask/components/TaskTypeSelector.vue')['default'] ThingAlbumDetail: typeof import('./src/views/Album/ThingAlbum/ThingAlbumDetail.vue')['default'] ThingAlbumIndex: typeof import('./src/views/Album/ThingAlbum/ThingAlbumIndex.vue')['default'] ThingAlbumList: typeof import('./src/views/Album/ThingAlbum/ThingAlbumList.vue')['default'] @@ -223,7 +226,6 @@ declare module 'vue' { UserList: typeof import('./src/views/Admin/System/Pages/UserList.vue')['default'] UserOutlined: typeof import('@ant-design/icons-vue')['UserOutlined'] VisitStatistics: typeof import('./src/views/Admin/System/Pages/VisitStatistics.vue')['default'] - VueCompareImage: typeof import('./src/components/VueCompareImage/VueCompareImage.vue')['default'] WarningOutlined: typeof import('@ant-design/icons-vue')['WarningOutlined'] } } diff --git a/default.conf b/default.conf index 57e2c2e..a345862 100644 --- a/default.conf +++ b/default.conf @@ -12,6 +12,8 @@ server { proxy_set_header Connection "upgrade"; access_log /var/log/nginx/host.access.log main; error_log /var/log/nginx/error.log error; + client_max_body_size 120M; + client_body_buffer_size 10m; location / { @@ -39,6 +41,8 @@ server { proxy_send_timeout 3600s; # 设置为1小时 send_timeout 3600s; # 设置为1小时 keepalive_timeout 3600s; # 设置为1小时 + client_max_body_size 120M; + client_body_buffer_size 10m; } error_page 500 502 503 504 /50x.html; diff --git a/package.json b/package.json index 2658b43..1fefefe 100644 --- a/package.json +++ b/package.json @@ -33,8 +33,8 @@ "@types/crypto-js": "^4.2.2", "@types/file-saver": "^2.0.7", "@types/json-stringify-safe": "^5.0.3", - "@types/leaflet": "^1.9.16", - "@types/node": "^22.13.11", + "@types/leaflet": "^1.9.17", + "@types/node": "^22.13.14", "@types/nprogress": "^0.2.3", "@vladmandic/face-api": "^1.7.15", "@vuepic/vue-datepicker": "^11.0.2", @@ -66,12 +66,12 @@ "nsfwjs": "^4.2.1", "opencv-qr": "^0.7.0", "pinia": "^3.0.1", - "pinia-plugin-persistedstate-2": "^2.0.29", + "pinia-plugin-persistedstate-2": "^2.0.30", "qr-scanner-wechat": "^0.1.3", "rimraf": "^6.0.1", "seedrandom": "^3.0.5", "swiper": "^11.2.6", - "unplugin-auto-import": "^19.1.1", + "unplugin-auto-import": "^19.1.2", "upscaler": "^1.0.0-beta.19", "vite-plugin-compression": "^0.5.1", "vite-plugin-html": "^3.2.2", @@ -91,16 +91,16 @@ "globals": "^16.0.0", "sass": "^1.86.0", "typescript": "^5.8.2", - "typescript-eslint": "^8.27.0", + "typescript-eslint": "^8.28.0", "unplugin-vue-components": "^28.4.1", - "vite": "^6.2.2", + "vite": "^6.2.3", "vite-plugin-bundle-obfuscator": "1.4.2", "vite-plugin-chunk-split": "^0.5.0", "vue-tsc": "2.2.8" }, "overrides": { "vite-plugin-chunk-split": { - "vite": "^6.2.0" + "vite": "^6.2.3" } } } diff --git a/src/api/storage/backup.ts b/src/api/storage/backup.ts index e3ff2c1..8b3b845 100644 --- a/src/api/storage/backup.ts +++ b/src/api/storage/backup.ts @@ -2,17 +2,14 @@ import {service} from "@/utils/alova/service.ts"; /** * 从一个存储桶备份到另一个存储桶 - * @param sourceProvider 源存储商 - * @param sourceBucket 源存储桶 - * @param targetProvider 目标存储商 - * @param targetBucket 目标存储桶 + * @param sourceStorage 源存储商 + * @param targetStorage 目标存储商 */ -export const backupStorageApi = (sourceProvider: string, sourceBucket: string, targetProvider: string, targetBucket: string) => { +export const backupStorageApi = (sourceStorage: string, targetStorage: string) => { return service.Post('/api/auth/storage/backup', { - source_provider: sourceProvider, - source_bucket: sourceBucket, - target_provider: targetProvider, - target_bucket: targetBucket, + source_storage: sourceStorage, + target_storage: targetStorage, + }, { meta: { ignoreToken: false, @@ -52,4 +49,4 @@ export const cancelBackupTaskApi = (taskId: string) => { }, name: "cancel-backup-task", }); -}; \ No newline at end of file +}; diff --git a/src/assets/svgs/cleanup.svg b/src/assets/svgs/cleanup.svg new file mode 100644 index 0000000..ef52d5b --- /dev/null +++ b/src/assets/svgs/cleanup.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/assets/svgs/endpoint.svg b/src/assets/svgs/endpoint.svg new file mode 100644 index 0000000..a490c12 --- /dev/null +++ b/src/assets/svgs/endpoint.svg @@ -0,0 +1 @@ + diff --git a/src/assets/svgs/recycle.svg b/src/assets/svgs/recycle.svg new file mode 100644 index 0000000..3e9b9e5 --- /dev/null +++ b/src/assets/svgs/recycle.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/src/components/HeatmapMax/HeatmapMax.vue b/src/components/HeatmapMax/HeatmapMax.vue new file mode 100644 index 0000000..9cf785b --- /dev/null +++ b/src/components/HeatmapMax/HeatmapMax.vue @@ -0,0 +1,539 @@ + + + + + {{ currentMonthTitle }} + + 低 + + + + + + 高 + + + + + + + + + + + {{ weekday }} + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/components/HeatmapPro/HeatmapPro.vue b/src/components/HeatmapPro/HeatmapPro.vue index 67ec26f..9fbcbd0 100644 --- a/src/components/HeatmapPro/HeatmapPro.vue +++ b/src/components/HeatmapPro/HeatmapPro.vue @@ -63,7 +63,7 @@ - diff --git a/src/views/User/AccountSetting/components/AccountSettingBackup/AccountSettingBackup.vue b/src/views/User/AccountSetting/components/AccountSettingBackup/AccountSettingBackup.vue index 2705994..8b22168 100644 --- a/src/views/User/AccountSetting/components/AccountSettingBackup/AccountSettingBackup.vue +++ b/src/views/User/AccountSetting/components/AccountSettingBackup/AccountSettingBackup.vue @@ -19,14 +19,14 @@ @@ -41,17 +41,39 @@ {{ selectedSourceStorage.bucket }} - {{ ProviderNameMap[selectedSourceStorage.provider] }} + + {{ ProviderNameMap[selectedSourceStorage.provider] }} + - {{ selectedSourceStorage.capacity }}GB + + 存储容量 + {{ selectedSourceStorage.capacity }}GB + - {{ AliRegionMap[selectedSourceStorage.region] || selectedSourceStorage.region }} + + 存储区域 + {{ AliRegionMap[selectedSourceStorage.region] || selectedSourceStorage.region }} + + + + + + 访问端点 + {{ selectedSourceStorage.endpoint }} + + + + + + 创建时间 + {{ formatDate(selectedSourceStorage.created_at) }} + @@ -71,15 +93,15 @@ @@ -94,17 +116,39 @@ {{ selectedTargetStorage.bucket }} - {{ ProviderNameMap[selectedTargetStorage.provider] }} + + {{ ProviderNameMap[selectedTargetStorage.provider] }} + - {{ selectedTargetStorage.capacity }}GB + + 存储容量 + {{ selectedTargetStorage.capacity }}GB + - {{ AliRegionMap[selectedTargetStorage.region] || selectedTargetStorage.region }} + + 存储区域 + {{ AliRegionMap[selectedTargetStorage.region] || selectedTargetStorage.region }} + + + + + + 访问端点 + {{ selectedTargetStorage.endpoint }} + + + + + + 创建时间 + {{ formatDate(selectedTargetStorage.created_at) }} + @@ -114,11 +158,11 @@ 开始备份 @@ -154,8 +198,8 @@ import targetIcon from "@/assets/svgs/target-storage.svg"; const router = useRouter(); const storageList = ref([]); -const sourceStorage = ref({ provider: '', bucket: '' }); -const targetStorage = ref({ provider: '', bucket: '' }); +const sourceStorage = ref({ id: null }); +const targetStorage = ref({ id: null }); const selectedSourceStorage = ref(null); const selectedTargetStorage = ref(null); const backupModalVisible = ref(false); @@ -164,14 +208,25 @@ const backupStatus = ref('准备开始备份...'); const backupInProgress = ref(false); const backupTaskId = ref(''); +import dateIcon from "@/assets/svgs/time.svg"; +import endpointIcon from "@/assets/svgs/endpoint.svg"; + +const formatDate = (dateString) => { + return new Date(dateString).toLocaleDateString('zh-CN', { + year: 'numeric', + month: '2-digit', + day: '2-digit' + }); +}; + // 获取存储列表 async function getStorageList() { const res: any = await listUserStorageConfigApi(); if (res && res.code === 200) { storageList.value = res.data.records; // 重置选择 - sourceStorage.value = { provider: '', bucket: '' }; - targetStorage.value = { provider: '', bucket: '' }; + sourceStorage.value = { id: null }; + targetStorage.value = { id: null }; selectedSourceStorage.value = null; selectedTargetStorage.value = null; } @@ -179,12 +234,8 @@ async function getStorageList() { // 计算可用的目标存储(排除已选择的源存储) const availableTargetStorages = computed(() => { - if (!sourceStorage.value.provider) return []; - return storageList.value.filter(item => { - const sourceKey = sourceStorage.value.provider; - const itemKey = item.provider + '-' + item.bucket; - return sourceKey !== itemKey; - }); + if (!sourceStorage.value.id) return []; + return storageList.value.filter(item => item.id !== sourceStorage.value.id); }); // 判断是否可以开始备份 @@ -199,22 +250,17 @@ function handleSourceProviderChange(value) { return; } - const [provider, bucket] = value.split('-'); - const selected = storageList.value.find(item => - item.provider === provider && item.bucket === bucket - ); + const selected = storageList.value.find(item => item.id === value); if (selected) { selectedSourceStorage.value = selected; - // 如果目标存储与源存储相同,则清空目标存储 - if (targetStorage.value.provider) { - const [targetProvider, targetBucket] = targetStorage.value.provider.split('-'); - if (targetProvider === provider && targetBucket === bucket) { - targetStorage.value.provider = ''; - selectedTargetStorage.value = null; - } + if (targetStorage.value.id === value) { + targetStorage.value.id = null; + selectedTargetStorage.value = null; } + } else { + selectedSourceStorage.value = null; } } @@ -225,13 +271,12 @@ function handleTargetProviderChange(value) { return; } - const [provider, bucket] = value.split('-'); - const selected = storageList.value.find(item => - item.provider === provider && item.bucket === bucket - ); + const selected = storageList.value.find(item => item.id === value); if (selected) { selectedTargetStorage.value = selected; + } else { + selectedTargetStorage.value = null; } } @@ -245,17 +290,9 @@ async function startBackup() { backupStatus.value = '正在准备备份...'; try { - // 调用备份API - const sourceProviderInfo = selectedSourceStorage.value.provider; - const sourceBucketInfo = selectedSourceStorage.value.bucket; - const targetProviderInfo = selectedTargetStorage.value.provider; - const targetBucketInfo = selectedTargetStorage.value.bucket; - const res: any = await backupStorageApi( - sourceProviderInfo, - sourceBucketInfo, - targetProviderInfo, - targetBucketInfo + selectedSourceStorage.value.id, + selectedTargetStorage.value.id ); if (res && res.code === 200) { @@ -343,10 +380,11 @@ onMounted(() => { \ No newline at end of file + diff --git a/src/views/User/AccountSetting/components/AccountSettingLog/AccountSettingLog.vue b/src/views/User/AccountSetting/components/AccountSettingLog/AccountSettingLog.vue new file mode 100644 index 0000000..cce2c6b --- /dev/null +++ b/src/views/User/AccountSetting/components/AccountSettingLog/AccountSettingLog.vue @@ -0,0 +1,1057 @@ + + + + 执行记录 + + + + + + + + + + + + + + 全部记录 + 备份记录 + 任务记录 + + + + + + + 全部状态 + 成功 + 失败 + 执行中 + + + + + + + + + + + + + + + + + + + + + + + + {{ filteredBackupLogs.length }}条记录 + + + + + + + {{ record.task_id }} + + + + + + + {{ + getStorageById(record.source_storage_id) ? + `${ProviderNameMap[getStorageById(record.source_storage_id)?.provider]} - ${getStorageById(record.source_storage_id)?.bucket}` : '未知' + }} + + + + + {{ + getStorageById(record.target_storage_id) ? + `${ProviderNameMap[getStorageById(record.target_storage_id)?.provider]} - ${getStorageById(record.target_storage_id)?.bucket}` : '未知' + }} + + + + + + + {{ getContentItemName(item) }} + + + + + + + + + {{ getStatusText(record.status) }} + + + + + + 开始:{{ formatDateTime(record.start_time) }} + 结束:{{ formatDateTime(record.end_time) }} + + 耗时:{{ calculateDuration(record.start_time, record.end_time) }} + + + + + + + + + + 查看详情 + + + + + + + + + + {{ filteredTaskLogs.length }}条记录 + + + + + + + + + {{ getTaskTypeName(record.task_type) }} + + + + + + + + + 备份存储: + + + {{ + getStorageById(record.storage_id) ? + `${ProviderNameMap[getStorageById(record.storage_id)?.provider]} - ${getStorageById(record.storage_id)?.bucket}` : '未知' + }} + + + + 备份内容: + + {{ + getContentItemName(item) + }} + + + + + + + + 清理策略: + {{ + getCleanupStrategyText(record.strategy) + }} + + + + 未使用阈值: + {{ record.unused_threshold }}天 + + + + + + 保留策略: + + {{ getRetentionPolicyText(record.retention_policy) }} + + + + 保留时间: + {{ record.retention_days }}天 + + + + + + + + + + + {{ getStatusText(record.status) }} + + + + + + + 备份文件:{{ record.files_count || 0 }}个 + 总大小:{{ formatFileSize(record.total_size || 0) }} + + + 清理文件:{{ record.files_count || 0 }}个 + 释放空间:{{ formatFileSize(record.freed_space || 0) }} + + + 清空文件:{{ record.files_count || 0 }}个 + 释放空间:{{ formatFileSize(record.freed_space || 0) }} + + + + {{ record.error_message || '执行失败' }} + + + {{ record.status === 'running' ? '执行中...' : '未知' }} + + + + + + 开始:{{ formatDateTime(record.start_time) }} + 结束:{{ formatDateTime(record.end_time) }} + + 耗时:{{ calculateDuration(record.start_time, record.end_time) }} + + + + + + + + + + 查看详情 + + + + + + + + + + + + + + {{ currentLogDetail.id }} + {{ currentLogDetail.task_id }} + + + + {{ + getStorageById(currentLogDetail.source_storage_id) ? + `${ProviderNameMap[getStorageById(currentLogDetail.source_storage_id)?.provider]} - ${getStorageById(currentLogDetail.source_storage_id)?.bucket}` : '未知' + }} + + + + + + {{ + getStorageById(currentLogDetail.target_storage_id) ? + `${ProviderNameMap[getStorageById(currentLogDetail.target_storage_id)?.provider]} - ${getStorageById(currentLogDetail.target_storage_id)?.bucket}` : '未知' + }} + + + + + {{ + getContentItemName(item) + }} + + + + + + {{ getStatusText(currentLogDetail.status) }} + + + {{ formatDateTime(currentLogDetail.start_time) }} + + {{ formatDateTime(currentLogDetail.end_time) }} + + + {{ calculateDuration(currentLogDetail.start_time, currentLogDetail.end_time) }} + + + {{ currentLogDetail.files_count || 0 }}个 + + + {{ formatFileSize(currentLogDetail.total_size || 0) }} + + + {{ currentLogDetail.error_message || '未知错误' }} + + + + + + 备份文件列表 + + + + {{ formatFileSize(record.size) }} + + + {{ formatDateTime(record.backup_time) }} + + + + + + + + + + {{ currentLogDetail.id }} + + + + + {{ getTaskTypeName(currentLogDetail.task_type) }} + + + + + + + + + + {{ + getStorageById(currentLogDetail.storage_id) ? + `${ProviderNameMap[getStorageById(currentLogDetail.storage_id)?.provider]} - ${getStorageById(currentLogDetail.storage_id)?.bucket}` : '未知' + }} + + + + + {{ + getContentItemName(item) + }} + + + + + + + + + + {{ getCleanupStrategyText(currentLogDetail.strategy) }} + + + + {{ currentLogDetail.unused_threshold }}天 + + + + + + + + {{ getRetentionPolicyText(currentLogDetail.retention_policy) }} + + + + {{ currentLogDetail.retention_days }}天 + + + + + + {{ getStatusText(currentLogDetail.status) }} + + + {{ formatDateTime(currentLogDetail.start_time) }} + + {{ formatDateTime(currentLogDetail.end_time) }} + + + {{ calculateDuration(currentLogDetail.start_time, currentLogDetail.end_time) }} + + + + + + {{ currentLogDetail.files_count || 0 }}个 + {{ + formatFileSize(currentLogDetail.total_size || 0) + }} + + + + {{ currentLogDetail.files_count || 0 }}个 + {{ + formatFileSize(currentLogDetail.freed_space || 0) + }} + + + + {{ currentLogDetail.files_count || 0 }}个 + {{ + formatFileSize(currentLogDetail.freed_space || 0) + }} + + + + + + {{ currentLogDetail.error_message || '未知错误' }} + + + + + + 执行日志 + + + + + + + {{ formatDateTime(log.timestamp) }} + {{ log.message }} + + + + + + + + + + + + + diff --git a/src/views/User/AccountSetting/components/AccountSettingLog/index.scss b/src/views/User/AccountSetting/components/AccountSettingLog/index.scss new file mode 100644 index 0000000..c59d3a6 --- /dev/null +++ b/src/views/User/AccountSetting/components/AccountSettingLog/index.scss @@ -0,0 +1,126 @@ +// 执行记录页面样式 +.account-setting-log { + width: 100%; + + .account-setting-log-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px; + + span { + font-size: 20px; + font-weight: bold; + } + } + + .account-setting-log-body { + .log-filter-section { + background-color: white; + padding: 16px; + border-radius: 8px; + margin-bottom: 20px; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + } + + .log-card { + margin-bottom: 20px; + border-radius: 8px; + overflow: hidden; + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); + + .storage-info { + display: flex; + align-items: center; + } + + .content-tags { + display: flex; + flex-wrap: wrap; + gap: 4px; + } + + .time-info { + font-size: 12px; + line-height: 1.5; + } + + .error-message { + color: #ff4d4f; + } + + .status-dot { + display: inline-block; + width: 6px; + height: 6px; + border-radius: 50%; + background-color: #d9d9d9; + margin-right: 4px; + + &.active { + background-color: currentColor; + } + } + + .task-detail-info { + .detail-item { + margin-bottom: 8px; + display: flex; + align-items: center; + + .label { + color: rgba(0, 0, 0, 0.65); + margin-right: 8px; + min-width: 80px; + } + } + } + } + } + + .log-detail { + .section-title { + font-size: 16px; + font-weight: bold; + margin: 24px 0 16px; + padding-bottom: 8px; + border-bottom: 1px solid #f0f0f0; + } + + .files-list, .task-logs { + margin-top: 24px; + } + + .log-item { + margin-bottom: 8px; + + .log-time { + font-size: 12px; + color: rgba(0, 0, 0, 0.45); + margin-bottom: 4px; + } + + .log-message { + padding: 8px 12px; + background-color: #f5f5f5; + border-radius: 4px; + + &.log-info { + background-color: #e6f7ff; + } + + &.log-success { + background-color: #f6ffed; + } + + &.log-warning { + background-color: #fffbe6; + } + + &.log-error { + background-color: #fff2f0; + } + } + } + } +} \ No newline at end of file diff --git a/src/views/User/AccountSetting/components/AccountSettingSidebar/AccountSettingSidebar.vue b/src/views/User/AccountSetting/components/AccountSettingSidebar/AccountSettingSidebar.vue index 27dd5cf..54f65ea 100644 --- a/src/views/User/AccountSetting/components/AccountSettingSidebar/AccountSettingSidebar.vue +++ b/src/views/User/AccountSetting/components/AccountSettingSidebar/AccountSettingSidebar.vue @@ -35,6 +35,18 @@ 图像备份 + + + + + 定时任务 + + + + + + 执行记录 + @@ -46,6 +58,8 @@ import home from "@/assets/svgs/home.svg"; import peopleAlbum from "@/assets/svgs/people-album.svg"; import storage from "@/assets/svgs/storage.svg"; import backup from "@/assets/svgs/source-storage.svg"; +import time from "@/assets/svgs/time.svg"; +import logIcon from "@/assets/svgs/data_analysis.svg"; const menuStore = useStore().menu; const menuCSSStyle: any = reactive({ diff --git a/src/views/User/AccountSetting/components/AccountSettingTask/AccountSettingTask.vue b/src/views/User/AccountSetting/components/AccountSettingTask/AccountSettingTask.vue new file mode 100644 index 0000000..4c0fceb --- /dev/null +++ b/src/views/User/AccountSetting/components/AccountSettingTask/AccountSettingTask.vue @@ -0,0 +1,846 @@ + + + + 定时任务 + + + + + 新增任务 + + + + + + + + + + + {{ getTaskTypeName(task.type) }} + {{ getTaskTypeShortName(task.type) }} + + + + + + + 编辑 + + + + + + 删除 + + toggleTaskStatus(index, checked)" class="task-switch"/> + + + + + + + 执行频率: + + {{ getFrequencyText(task.frequency) }} + + + + 执行时间: + + {{ formatTime(task.time) }} + + + + + + + + 备份配置 + + 备份存储: + + + + {{ + getStorageById(task.storageId) ? `${ProviderNameMap[getStorageById(task.storageId)?.provider]} - ${getStorageById(task.storageId)?.bucket}` : '未设置' + }} + + + + + 备份内容: + + + {{ getContentItemName(item) }} + + 无 + + + + + + + + + 清理配置 + + 清理策略: + + {{ getCleanupStrategyText(task.strategy) }} + + + + 未使用阈值: + + {{ task.unusedThreshold }}天 + + + + + + + + + 回收站配置 + + 保留策略: + + {{ getRetentionPolicyText(task.retentionPolicy) }} + + + + 保留时间: + + {{ task.retentionDays }}天 + + + + 清空前通知: + + {{ task.notifyBeforeEmpty ? '是' : '否' }} + + + + + + + + + + + {{ task.enabled ? '已启用' : '已禁用' }} + + + + + + + {{ formatDate(task.lastRun) }} + + + + + + + + + + + + + 请选择要创建的任务类型: + + + + + 定时备份 + 定期备份您的照片和相册数据 + + + + + + 定时清理 + 定期清理重复或未使用的文件 + + + + + + 回收站管理 + 定期清空回收站中的文件 + + + + + 下一步 + + + + + + + + + + 每天 + 每周 + 每月 + + + + + + + + + + + + + {{ ProviderNameMap[item.provider] }} - {{ item.bucket }} + + + + + + + + 照片 + 相册 + 设置 + + + + + + + + + 每天 + 每周 + 每月 + + + + + + + + + + 仅清理重复文件 + 清理长期未使用文件 + 两者都清理 + + + + + + + + + + + + + 每周 + 每两周 + 每月 + + + + + + + + + + 清空所有文件 + 按时间清空 + + + + + + + + + + + + + + + + + + + + + 确定要删除这个定时任务吗? + 此操作不可恢复,请谨慎操作。 + + + + + + + + diff --git a/src/views/User/AccountSetting/components/AccountSettingTask/index.scss b/src/views/User/AccountSetting/components/AccountSettingTask/index.scss new file mode 100644 index 0000000..57576b3 --- /dev/null +++ b/src/views/User/AccountSetting/components/AccountSettingTask/index.scss @@ -0,0 +1,721 @@ +.account-setting-task { + width: calc(100% - 72px); + background-color: var(--white-color, #fff); + border-radius: 16px; + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08); + transition: all 0.3s ease; + + &:hover { + box-shadow: 0 12px 28px rgba(0, 0, 0, 0.12); + } + + .account-setting-task-header { + padding: 0 36px; + width: 100%; + height: 70px; + display: flex; + align-items: center; + justify-content: space-between; + border-bottom: 1px solid #eaeaea; + background: linear-gradient(135deg, #f8f9fa 0%, #e9f2ff 100%); + border-radius: 16px 16px 0 0; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); + + span { + font-size: 22px; + font-weight: 600; + color: #222222; + position: relative; + padding-left: 16px; + letter-spacing: 0.5px; + + &::before { + content: ''; + position: absolute; + left: 0; + top: 50%; + transform: translateY(-50%); + width: 5px; + height: 24px; + background: linear-gradient(to bottom, #1890ff, #36cfc9); + border-radius: 3px; + } + } + } + + .account-setting-task-body { + padding: 36px; + width: 100%; + display: flex; + flex-direction: column; + gap: 24px; + background-color: var(--white-color, #fff); + border-radius: 0 0 16px 16px; + + .task-list { + display: flex; + flex-direction: column; + gap: 20px; + } + + .task-card { + background-color: #fafafa; + border-radius: 12px; + overflow: hidden; + box-shadow: 0 6px 16px rgba(0, 0, 0, 0.08); + transition: all 0.3s ease; + border: 1px solid #f0f0f0; + position: relative; + margin-bottom: 8px; + + &:hover { + box-shadow: 0 10px 24px rgba(0, 0, 0, 0.12); + transform: translateY(-4px); + } + + &.backup-task { + border-left: 4px solid #1890ff; + &::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 4px; + background: linear-gradient(90deg, #1890ff, #36cfc9); + } + } + + &.cleanup-task { + border-left: 4px solid #52c41a; + &::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 4px; + background: linear-gradient(90deg, #52c41a, #b7eb8f); + } + } + + &.recycle-task { + border-left: 4px solid #fa8c16; + &::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 4px; + background: linear-gradient(90deg, #fa8c16, #ffd666); + } + } + + .task-card-header { + padding: 16px 20px; + display: flex; + justify-content: space-between; + align-items: center; + background-color: #fff; + border-bottom: 1px solid #f0f0f0; + + .task-card-title { + display: flex; + align-items: center; + gap: 10px; + + .task-icon { + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); + border-radius: 4px; + } + + span { + font-size: 16px; + font-weight: 600; + color: #333; + } + + .task-type-tag { + margin-left: 4px; + font-size: 12px; + border-radius: 4px; + } + } + + .task-card-actions { + display: flex; + align-items: center; + gap: 12px; + + .action-btn { + border-radius: 4px; + transition: all 0.2s; + padding: 0 8px; + + &.edit-btn:hover { + color: #1890ff; + background-color: #e6f7ff; + } + + &.delete-btn:hover { + color: #ff4d4f; + background-color: #fff1f0; + } + } + + .task-switch { + margin-left: 4px; + } + } + } + + .task-card-content { + padding: 20px; + background-color: #fff; + display: flex; + justify-content: space-between; + + &.disabled { + opacity: 0.7; + background-color: #fafafa; + position: relative; + + &::after { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.02); + pointer-events: none; + } + } + + .task-info { + flex: 1; + display: flex; + flex-direction: column; + gap: 16px; + + .task-info-group { + display: flex; + flex-direction: column; + gap: 12px; + padding-bottom: 12px; + position: relative; + + &:not(:last-child) { + border-bottom: 1px dashed #f0f0f0; + margin-bottom: 4px; + } + + .group-title { + font-size: 14px; + font-weight: 500; + color: #666; + margin-bottom: 4px; + position: relative; + padding-left: 12px; + + &::before { + content: ''; + position: absolute; + left: 0; + top: 50%; + transform: translateY(-50%); + width: 4px; + height: 14px; + border-radius: 2px; + } + } + + &.backup-info .group-title::before { + background: linear-gradient(to bottom, #1890ff, #36cfc9); + } + + &.cleanup-info .group-title::before { + background: linear-gradient(to bottom, #52c41a, #b7eb8f); + } + + &.recycle-info .group-title::before { + background: linear-gradient(to bottom, #fa8c16, #ffd666); + } + } + + .task-info-item { + display: flex; + align-items: flex-start; + gap: 8px; + + .label { + font-weight: 500; + color: #666; + min-width: 80px; + } + + .value { + color: #333; + flex: 1; + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 6px; + + .info-tag { + margin: 0; + border-radius: 4px; + } + + .time-tag { + font-family: monospace; + } + + .content-tags { + display: flex; + flex-wrap: wrap; + gap: 6px; + + .content-tag { + margin: 0; + border-radius: 4px; + } + } + + .storage-info { + background-color: #f5f5f5; + padding: 4px 8px; + border-radius: 4px; + } + } + } + } + + .task-status { + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 12px; + margin-left: 16px; + + .status-tag { + padding: 4px 8px; + border-radius: 4px; + font-weight: 500; + + .status-dot { + display: inline-block; + width: 8px; + height: 8px; + border-radius: 50%; + background-color: #d9d9d9; + margin-right: 6px; + + &.active { + background-color: #52c41a; + box-shadow: 0 0 6px rgba(82, 196, 26, 0.5); + animation: pulse 1.5s infinite; + } + } + } + + .task-last-run { + .last-run-tag { + font-size: 12px; + padding: 2px 8px; + border-radius: 4px; + } + } + } + } + } + } + + // 任务类型选择样式 + .task-type-selection { + padding: 32px 20px; + background: linear-gradient(135deg, #ffffff 0%, #f0f2f5 100%); + border-radius: 16px; + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.06); + + .selection-tip { + font-size: 22px; + font-weight: 600; + margin-bottom: 32px; + color: #1a1a1a; + text-align: center; + letter-spacing: 0.5px; + animation: fadeInDown 0.6s ease-out; + } + + .task-type-options { + display: flex; + gap: 24px; + margin-bottom: 40px; + animation: fadeInUp 0.6s ease-out; + + .task-type-card { + flex: 1; + height: 100%; + transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1); + + .task-type-content { + padding: 32px 24px; + display: flex; + flex-direction: column; + align-items: center; + gap: 20px; + width: 100%; + position: relative; + overflow: hidden; + + &::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 6px; + opacity: 0; + transition: all 0.4s ease; + } + + &:nth-child(1)::before { + background: linear-gradient(90deg, #1890ff, #36cfc9); + } + + &:nth-child(2)::before { + background: linear-gradient(90deg, #52c41a, #b7eb8f); + } + + &:nth-child(3)::before { + background: linear-gradient(90deg, #fa8c16, #ffd666); + } + } + + &:hover .task-type-content::before { + opacity: 1; + } + + &:hover { + transform: translateY(-4px); + } + + &.selected .task-type-content::before { + opacity: 1; + } + } + + .check-card.selected { + box-shadow: 0 12px 28px rgba(24, 144, 255, 0.2); + background: linear-gradient(145deg, #e6f7ff 0%, #ffffff 100%); + } + + .task-type-option { + flex: 1; + padding: 32px 24px; + border-radius: 16px; + background: linear-gradient(145deg, #ffffff 0%, #f8f9fa 100%); + border: 2px solid transparent; + display: flex; + flex-direction: column; + align-items: center; + gap: 20px; + cursor: pointer; + transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1); + position: relative; + overflow: hidden; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); + + &::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 6px; + opacity: 0; + transition: all 0.4s ease; + } + + &::after { + content: ''; + position: absolute; + top: 50%; + left: 50%; + width: 0; + height: 0; + background: radial-gradient(circle, rgba(24, 144, 255, 0.2) 0%, rgba(24, 144, 255, 0) 60%); + border-radius: 50%; + transform: translate(-50%, -50%); + transition: all 0.6s cubic-bezier(0.4, 0, 0.2, 1); + } + + &:hover { + transform: translateY(-4px); + box-shadow: 0 8px 24px rgba(24, 144, 255, 0.15); + background: linear-gradient(145deg, #ffffff 0%, #f0f7ff 100%); + + &::before { + opacity: 1; + } + + span { + color: #1890ff; + transform: scale(1.05); + } + + p { + color: #4096ff; + } + } + + &:active::after { + width: 200%; + height: 200%; + opacity: 0; + transition: 0s; + } + + &.selected { + border-color: #1890ff; + background: linear-gradient(145deg, #e6f7ff 0%, #ffffff 100%); + box-shadow: 0 12px 28px rgba(24, 144, 255, 0.2); + transform: translateY(-4px) scale(1.02); + animation: pulse 2s infinite; + + &::before { + opacity: 1; + background: linear-gradient(90deg, #1890ff, #36cfc9); + } + + span { + color: #1890ff; + font-weight: 600; + } + + p { + color: #4096ff; + } + } + + span { + font-size: 16px; + font-weight: 600; + color: #333; + transition: all 0.3s ease; + } + + p { + color: #666; + transition: all 0.3s ease; + font-size: 14px; + margin-top: 8px; + text-align: center; + } + + .task-type-tag { + margin-left: 4px; + font-size: 12px; + border-radius: 4px; + } + + @keyframes fadeInDown { + from { + opacity: 0; + transform: translateY(-20px); + } + to { + opacity: 1; + transform: translateY(0); + } + } + + @keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } + } + + @keyframes pulse { + 0% { + box-shadow: 0 12px 28px rgba(24, 144, 255, 0.2); + } + 50% { + box-shadow: 0 12px 28px rgba(24, 144, 255, 0.4); + } + 100% { + box-shadow: 0 12px 28px rgba(24, 144, 255, 0.2); + } + } + } + + .task-type-actions { + margin-top: 16px; + text-align: center; + + .ant-btn { + height: 44px; + padding: 0 32px; + font-size: 16px; + font-weight: 500; + border-radius: 22px; + background: linear-gradient(90deg, #1890ff, #36cfc9); + border: none; + box-shadow: 0 4px 12px rgba(24, 144, 255, 0.3); + transition: all 0.3s ease; + + &:hover { + transform: translateY(-2px); + box-shadow: 0 6px 16px rgba(24, 144, 255, 0.4); + } + + &:disabled { + background: #f5f5f5; + color: #bfbfbf; + box-shadow: none; + } + } + } + + &:nth-child(1)::before { + background: linear-gradient(90deg, #1890ff, #36cfc9); + } + + &:nth-child(2)::before { + background: linear-gradient(90deg, #52c41a, #b7eb8f); + } + + &:nth-child(3)::before { + background: linear-gradient(90deg, #fa8c16, #ffd666); + } + + span { + font-size: 18px; + font-weight: 600; + color: #333; + } + + p { + font-size: 14px; + color: #666; + text-align: center; + margin: 0; + } + } + } + + .task-type-actions { + display: flex; + justify-content: center; + margin-top: 32px; + animation: fadeInUp 0.8s ease-out; + + .ant-btn { + min-width: 120px; + height: 40px; + font-size: 16px; + border-radius: 8px; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + background: linear-gradient(135deg, #1890ff 0%, #36cfc9 100%); + border: none; + color: white; + box-shadow: 0 4px 12px rgba(24, 144, 255, 0.3); + + &:hover { + transform: translateY(-2px); + box-shadow: 0 8px 16px rgba(24, 144, 255, 0.4); + background: linear-gradient(135deg, #40a9ff 0%, #5cdbd3 100%); + } + + &:active { + transform: translateY(0); + box-shadow: 0 4px 8px rgba(24, 144, 255, 0.2); + } + + &:disabled { + background: linear-gradient(135deg, #bfbfbf 0%, #d9d9d9 100%); + box-shadow: none; + cursor: not-allowed; + opacity: 0.7; + } + } + } + } + +// 删除确认对话框样式 +.delete-confirm-modal { + .delete-confirm-content { + padding: 12px 0; + + p { + margin-bottom: 8px; + font-size: 15px; + } + + .warning-text { + color: #ff4d4f; + font-size: 13px; + } + } +} + +// 任务模态框样式 +.task-modal { + :deep(.ant-modal-content) { + border-radius: 12px; + overflow: hidden; + } + + :deep(.ant-modal-header) { + padding: 16px 24px; + border-bottom: 1px solid #f0f0f0; + background: linear-gradient(135deg, #f8f9fa 0%, #e9f2ff 100%); + } + + :deep(.ant-modal-title) { + font-size: 18px; + font-weight: 600; + color: #222; + } + + :deep(.ant-modal-body) { + padding: 24px; + } + + :deep(.ant-modal-footer) { + border-top: 1px solid #f0f0f0; + padding: 12px 24px; + } + + :deep(.ant-form-item-label) { + font-weight: 500; + } + + :deep(.ant-select), :deep(.ant-input-number), :deep(.ant-picker) { + width: 100%; + border-radius: 6px; + } + + :deep(.ant-radio-wrapper), :deep(.ant-checkbox-wrapper) { + margin-bottom: 8px; + } +} + +@keyframes pulse { + 0% { + box-shadow: 0 0 0 0 rgba(82, 196, 26, 0.5); + } + 70% { + box-shadow: 0 0 0 6px rgba(82, 196, 26, 0); + } + 100% { + box-shadow: 0 0 0 0 rgba(82, 196, 26, 0); + } +} diff --git a/src/views/User/PersonalCenter/PersonalCenter.vue b/src/views/User/PersonalCenter/PersonalCenter.vue index 9193aad..1c4cb8d 100644 --- a/src/views/User/PersonalCenter/PersonalCenter.vue +++ b/src/views/User/PersonalCenter/PersonalCenter.vue @@ -1,49 +1,74 @@ - - - - - - - - - - {{ userStore.user.nickname }} - + + + + + + + + + - - 描述信息 + + + {{ userStore.user.nickname }} + 描述信息 + + + + + 128 + 照片 + + + 56 + 分享 + + + 24 + 收藏 + + + + + + + 主页 + + + + + + 动态 + + + + + + 设置 + + - - - - - - - - 主页 - - - - - - 动态 - - - - - - 设置 - - - - - + + + + + {{ getPageTitle() }} + + + + @@ -55,106 +80,227 @@ import home from "@/assets/svgs/home.svg"; import dynamic from "@/assets/svgs/dynamic.svg"; import setting from "@/assets/svgs/setting.svg"; - const userStore = useStore().user; const menuStore = useStore().menu; const router = useRouter(); -const menuCSSStyle: any = reactive({ - display: 'flex', - alignItems: 'center', -}); + +// 页面标题映射 +const pageTitles = { + 'home': '个人主页', + 'dynamic': '我的动态', + 'setting': '账户设置' +}; + +// 获取当前页面标题 +function getPageTitle() { + return pageTitles[menuStore.userCenterMenu] || '个人中心'; +} function handleClick({key}) { menuStore.userCenterMenu = key; router.push(`/main/user/center/${key}`); - } diff --git a/src/views/User/PersonalCenter/components/UserCenterDynamic/UserCenterDynamic.vue b/src/views/User/PersonalCenter/components/UserCenterDynamic/UserCenterDynamic.vue index c7cf124..c7a88e8 100644 --- a/src/views/User/PersonalCenter/components/UserCenterDynamic/UserCenterDynamic.vue +++ b/src/views/User/PersonalCenter/components/UserCenterDynamic/UserCenterDynamic.vue @@ -1,6 +1,38 @@ - + + + + + + + + + + + + + {{ totalVisits }} + 总访问量 + + + + + + + {{ totalVisitors }} + 总访客数 + + + + + + + {{ totalPublishes }} + 总发布数 + + + + diff --git a/src/views/User/PersonalCenter/components/UserCenterHome/UserCenterHome.vue b/src/views/User/PersonalCenter/components/UserCenterHome/UserCenterHome.vue index a001013..0e08537 100644 --- a/src/views/User/PersonalCenter/components/UserCenterHome/UserCenterHome.vue +++ b/src/views/User/PersonalCenter/components/UserCenterHome/UserCenterHome.vue @@ -1,206 +1,613 @@ - - - - - - - - - 图片总数 - {{ chartData.image_count }} - - - - 今日上传 - +{{ chartData.today_upload_count }} - + + + + + - - - - - - - 分享总数 - {{ chartData.share_count }} - - - - 今日上传 - +{{ chartData.today_share_count }} - - - - - - - - - 文件总量 - {{ - bytesToSize(chartData.file_size_count) - }} - - - - 今日上传 - +{{ - bytesToSize(chartData.today_file_size_count) - }} + + 图片总数 + {{ chartData.image_count }} + - - 文件上传热力图 - + + + + + + + 分享总数 + {{ chartData.share_count }} + + + + + + + + + + 文件总量 + {{ bytesToSize(chartData.file_size_count) }} + + - - - + + + + + + 月度上传数量统计 + + + + + + + + + + 最近活动 + + 查看全部 + + + + + + + + {{ item.title }} + {{ item.time }} + + + + +
请选择要创建的任务类型:
定期备份您的照片和相册数据
定期清理重复或未使用的文件
定期清空回收站中的文件
确定要删除这个定时任务吗?
此操作不可恢复,请谨慎操作。
描述信息