From c80fa211393e9a72393c49d9d0659dd389ac4189 Mon Sep 17 00:00:00 2001 From: landaiqing Date: Fri, 28 Mar 2025 18:29:48 +0800 Subject: [PATCH] :lipstick: restructure and beautify some page --- components.d.ts | 26 +- default.conf | 4 + package.json | 14 +- src/api/storage/backup.ts | 17 +- src/assets/svgs/cleanup.svg | 6 + src/assets/svgs/endpoint.svg | 1 + src/assets/svgs/recycle.svg | 8 + src/components/HeatmapMax/HeatmapMax.vue | 539 +++++++++ src/components/HeatmapPro/HeatmapPro.vue | 55 +- src/layout/default/Header/Menu.vue | 47 +- src/router/modules/preview.ts | 9 + src/router/modules/user.ts | 18 + .../Admin/System/Pages/BasicSettings.vue | 90 +- .../Admin/System/Pages/PermissionSetting.vue | 2 +- .../Admin/System/Pages/RoleManagement.vue | 82 +- src/views/Admin/System/Pages/UserAnalysis.vue | 2 +- src/views/Admin/System/Pages/UserList.vue | 73 +- src/views/ImageBed/index.vue | 2 +- src/views/Preview/PreviewOCR/PreviewOCR.vue | 3 +- .../Preview/PreviewQRCode/PreviewQRCode.vue | 434 +++++++ .../AccountSettingBackup.vue | 187 ++- .../AccountSettingHome/AccountSettingHome.vue | 2 +- .../AccountSettingHome/PhoneModal.vue | 9 +- .../ThirdPartyLoginModal.vue | 73 +- .../AccountSettingLog/AccountSettingLog.vue | 1057 +++++++++++++++++ .../components/AccountSettingLog/index.scss | 126 ++ .../AccountSettingSidebar.vue | 14 + .../AccountSettingTask/AccountSettingTask.vue | 846 +++++++++++++ .../components/AccountSettingTask/index.scss | 721 +++++++++++ .../User/PersonalCenter/PersonalCenter.vue | 362 ++++-- .../UserCenterDynamic/UserCenterDynamic.vue | 380 ++++-- .../UserCenterHome/UserCenterHome.vue | 687 ++++++++--- .../UserCenterSetting/UserCenterSetting.vue | 624 +++++++--- vite.config.ts | 4 + 34 files changed, 5709 insertions(+), 815 deletions(-) create mode 100644 src/assets/svgs/cleanup.svg create mode 100644 src/assets/svgs/endpoint.svg create mode 100644 src/assets/svgs/recycle.svg create mode 100644 src/components/HeatmapMax/HeatmapMax.vue create mode 100644 src/views/Preview/PreviewQRCode/PreviewQRCode.vue create mode 100644 src/views/User/AccountSetting/components/AccountSettingLog/AccountSettingLog.vue create mode 100644 src/views/User/AccountSetting/components/AccountSettingLog/index.scss create mode 100644 src/views/User/AccountSetting/components/AccountSettingTask/AccountSettingTask.vue create mode 100644 src/views/User/AccountSetting/components/AccountSettingTask/index.scss 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 @@ + + + + + \ 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 @@ + + + + + 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 @@ + + + + + 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 @@