From 41e3cb571c88e2a0b47b25b60697bc39eb5805b2 Mon Sep 17 00:00:00 2001 From: XPoet Date: Thu, 28 Dec 2023 15:54:14 +0800 Subject: [PATCH 01/32] chore: dev dir contextmenu temp --- src/common/directive/contextmenu.ts | 60 ++++++++++++++- src/common/directive/get-dir-sha.ts | 55 ++++++++++++++ src/common/directive/remove-dir.ts | 7 ++ src/common/directive/rename-dir.ts | 75 +++++++++++++++++++ src/styles/base.styl | 8 ++ .../github-authorize/github-authorize.styl | 0 .../github-authorize/github-authorize.vue | 22 ------ 7 files changed, 201 insertions(+), 26 deletions(-) create mode 100644 src/common/directive/get-dir-sha.ts create mode 100644 src/common/directive/remove-dir.ts create mode 100644 src/common/directive/rename-dir.ts delete mode 100644 src/views/github-authorize/github-authorize.styl delete mode 100644 src/views/github-authorize/github-authorize.vue diff --git a/src/common/directive/contextmenu.ts b/src/common/directive/contextmenu.ts index ec4f7d9e..81e91869 100644 --- a/src/common/directive/contextmenu.ts +++ b/src/common/directive/contextmenu.ts @@ -4,6 +4,8 @@ import { store } from '@/stores' import { ContextmenuEnum, DirModeEnum } from '@/common/model' import { copyImageLink } from '@/utils' import i18n from '@/plugins/vue/i18n' +import { renameFolder } from '@/common/directive/rename-dir' +import { removeDir } from '@/common/directive/remove-dir' const menuClass = 'custom-contextmenu-container' let menuEle: any = null @@ -28,25 +30,45 @@ const contextmenuDirective: Directive = { menuEle.setAttribute('class', menuClass) menuEle.style.position = 'fixed' menuEle.style.zIndex = '1000' - menuEle.innerHTML = `
  • ` + menuEle.innerHTML = `
  • +
  • + ${i18n.global.t('upload.rename')} +
  • +
  • + ${i18n.global.t('delete')} +
  • + ` document.body.appendChild(menuEle) } const uploadItem = menuEle?.querySelector('.upload-image') const copyItem = menuEle?.querySelector('.copy-link') + const renameItem = menuEle?.querySelector('.rename-dir') + const removeItem = menuEle?.querySelector('.remove-dir') if (type === ContextmenuEnum.img) { copyItem.style.display = 'block' uploadItem.innerHTML = i18n.global.t('management.contextmenu_1') - } else { + } + + if (type === ContextmenuEnum.parentDir) { copyItem.style.display = 'none' uploadItem.innerHTML = i18n.global.t('management.contextmenu_2', { dir: selectedDir === '/' ? i18n.global.t('management.contextmenu_3') : selectedDir }) } + if (type === ContextmenuEnum.childDir) { + copyItem.style.display = 'none' + renameItem.style.display = 'block' + removeItem.style.display = 'block' + uploadItem.innerHTML = i18n.global.t('management.contextmenu_2', { + dir: selectedDir + }) + } + let setLeft = e.clientX let setTop = e.clientY const menuWidth = menuEle.clientWidth @@ -69,6 +91,7 @@ const contextmenuDirective: Directive = { if (!isAddEventListenerOfContextmenu) { isAddEventListenerOfContextmenu = true + // 上传图片 uploadItem?.addEventListener('click', async () => { const dirMode = selectedDir === '/' ? DirModeEnum.rootDir : DirModeEnum.repoDir const selectedDirList = selectedDir === '/' ? [] : selectedDir.split('/') @@ -80,11 +103,40 @@ const contextmenuDirective: Directive = { await router.push('/upload') }) + // 复制图片链接 copyItem?.addEventListener('click', async () => { const userConfigInfo = computed(() => store.getters.getUserConfigInfo).value const userSettings = computed(() => store.getters.getUserSettings).value copyImageLink(img, userConfigInfo, userSettings) }) + + // 重命名目录 + renameItem?.addEventListener('click', async () => { + ElMessageBox.prompt('请输入新的名称', i18n.global.t('tips'), { + confirmButtonText: i18n.global.t('confirm'), + cancelButtonText: i18n.global.t('cancel') + }).then(async ({ value }) => { + if (!value) { + return + } + + const { + owner, + selectedRepo: repo, + token, + selectedBranch: branch + } = computed(() => store.getters.getUserConfigInfo).value + + const res2 = await renameFolder(owner, repo, selectedDir, value, token, branch) + console.log('res2 : ', res2) + }) + }) + + // 删除目录 + removeItem?.addEventListener('click', async () => { + const userConfigInfo = computed(() => store.getters.getUserConfigInfo).value + await removeDir(userConfigInfo, selectedDir) + }) } const closeMenu = () => { diff --git a/src/common/directive/get-dir-sha.ts b/src/common/directive/get-dir-sha.ts new file mode 100644 index 00000000..6836faca --- /dev/null +++ b/src/common/directive/get-dir-sha.ts @@ -0,0 +1,55 @@ +import axios from 'axios' +import { UserConfigInfoModel } from '@/common/model' +import request from '@/utils/request' + +// eslint-disable-next-line consistent-return +export async function getDirSha(userConfigInfo: UserConfigInfoModel, path: string) { + const { owner, selectedRepo: repo, token, selectedBranch: branch } = userConfigInfo + try { + // 设置 axios 请求头,使用 GitHub Token 进行身份验证 + axios.defaults.headers.common.Authorization = `Bearer ${token}` + const pathArr = path.split('/') + + if (pathArr.length > 1) { + pathArr.pop() // 删除数组最后一项 + const resList = await request({ + method: 'GET', + url: `repos/${owner}/${repo}/contents/${pathArr.join('/')}` + }) + return { + sha: resList.find((x: any) => x.path === path)?.sha || null + } + } + + // 获取分支的引用 + const branchResponse = await axios.get( + `https://api.github.com/repos/${owner}/${repo}/git/refs/heads/${branch}` + ) + + const latestCommitSha = branchResponse.data.object.sha + + // 获取最新提交的树对象 SHA + const commitResponse = await axios.get( + `https://api.github.com/repos/${owner}/${repo}/git/commits/${latestCommitSha}` + ) + + const treeSha = commitResponse.data.tree.sha + + // 获取树对象的信息 + const treeResponse = await axios.get( + `https://api.github.com/repos/${owner}/${repo}/git/trees/${treeSha}` + ) + + // 获取旧文件夹的信息 + const oldFolderInfo = treeResponse.data.tree.find((item: any) => item.path === path) + + return { + sha: oldFolderInfo?.sha || null + } + } catch (error: any) { + console.error('Error:', error.response.data) + return { + sha: null + } + } +} diff --git a/src/common/directive/remove-dir.ts b/src/common/directive/remove-dir.ts new file mode 100644 index 00000000..8da89435 --- /dev/null +++ b/src/common/directive/remove-dir.ts @@ -0,0 +1,7 @@ +import { UserConfigInfoModel } from '@/common/model' + +// eslint-disable-next-line no-unused-vars +export async function removeDir(userConfigInfo: UserConfigInfoModel, path: string) { + // TODO + console.log('removeDir') +} diff --git a/src/common/directive/rename-dir.ts b/src/common/directive/rename-dir.ts new file mode 100644 index 00000000..b6d19ab4 --- /dev/null +++ b/src/common/directive/rename-dir.ts @@ -0,0 +1,75 @@ +import axios from 'axios' + +export async function renameFolder(owner, repo, oldPath, newPath, token, branch) { + try { + // 设置 axios 请求头,使用 GitHub Token 进行身份验证 + axios.defaults.headers.common.Authorization = `Bearer ${token}` + + // 获取分支的引用 + const branchResponse = await axios.get( + `https://api.github.com/repos/${owner}/${repo}/git/refs/heads/${branch}` + ) + + const latestCommitSha = branchResponse.data.object.sha + + // 获取最新提交的树对象 SHA + const commitResponse = await axios.get( + `https://api.github.com/repos/${owner}/${repo}/git/commits/${latestCommitSha}` + ) + + const treeSha = commitResponse.data.tree.sha + + // 获取树对象的信息 + const treeResponse = await axios.get( + `https://api.github.com/repos/${owner}/${repo}/git/trees/${treeSha}` + ) + + console.log('treeResponse: ', treeResponse) + + // 获取旧文件夹的信息 + const oldFolderInfo = treeResponse.data.tree.find((item) => item.path === oldPath) + + if (!oldFolderInfo) { + throw new Error('The specified path is not a directory.') + } + + // 创建一个新的树对象,将旧文件夹的内容更新为新的路径 + const newTreeResponse = await axios.post( + `https://api.github.com/repos/${owner}/${repo}/git/trees`, + { + base_tree: treeSha, + tree: treeResponse.data.tree.map((item) => { + if (item.path === oldPath) { + return { + path: newPath, + mode: item.mode, + type: item.type, + sha: item.sha + } + } + return item + }) + } + ) + + // 创建一个新的提交,将树对象更新到仓库 + const commitMessage = `Move folder from ${oldPath} to ${newPath}` + const commitInfo = await axios.post( + `https://api.github.com/repos/${owner}/${repo}/git/commits`, + { + message: commitMessage, + tree: newTreeResponse.data.sha, + parents: [latestCommitSha] // 使用最新提交的 SHA 作为父提交 + } + ) + + // 更新分支的引用,将提交应用到仓库 + await axios.patch(`https://api.github.com/repos/${owner}/${repo}/git/refs/heads/${branch}`, { + sha: commitInfo.data.sha + }) + + console.log('Folder renamed successfully!') + } catch (error) { + console.error('Error:', error.response.data) + } +} diff --git a/src/styles/base.styl b/src/styles/base.styl index e4f30f16..d89b1b9c 100644 --- a/src/styles/base.styl +++ b/src/styles/base.styl @@ -137,6 +137,14 @@ li { display none } + &.rename-dir { + display none + } + + &.remove-dir { + display none + } + &:hover { color var(--el-color-primary) background var(--el-color-primary-light-9) diff --git a/src/views/github-authorize/github-authorize.styl b/src/views/github-authorize/github-authorize.styl deleted file mode 100644 index e69de29b..00000000 diff --git a/src/views/github-authorize/github-authorize.vue b/src/views/github-authorize/github-authorize.vue deleted file mode 100644 index 8a7216cf..00000000 --- a/src/views/github-authorize/github-authorize.vue +++ /dev/null @@ -1,22 +0,0 @@ - - - - - From 8188a0820882df83fec5cac4d70987bbd5c3fd4a Mon Sep 17 00:00:00 2001 From: XPoet Date: Thu, 4 Jan 2024 11:37:49 +0800 Subject: [PATCH 02/32] perf: optimize language toggle tips --- src/App.vue | 131 +--------------------------- src/common/constant/storage.ts | 1 + src/views/app-wrap/app-wrap.vue | 149 ++++++++++++++++++++++++++++++++ 3 files changed, 152 insertions(+), 129 deletions(-) create mode 100644 src/views/app-wrap/app-wrap.vue diff --git a/src/App.vue b/src/App.vue index 6634dda1..d27477d1 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,136 +1,9 @@ diff --git a/src/components/repo-dir-cascader/repo-dir-cascader.vue b/src/components/repo-dir-cascader/repo-dir-cascader.vue index a2879d3e..3da79d99 100644 --- a/src/components/repo-dir-cascader/repo-dir-cascader.vue +++ b/src/components/repo-dir-cascader/repo-dir-cascader.vue @@ -9,7 +9,7 @@ :key="elKey" v-model="userConfigInfo.selectedDirList" filterable - :placeholder="$t('config.placeholder5')" + :placeholder="$t('config_page.placeholder_5')" :clearable="elClearable" @change="cascaderChange" /> @@ -49,13 +49,13 @@ const cascaderProps = { checkStrictly: true, async lazyLoad(node: any, resolve: any) { const { level, pathLabels } = node - let dirs: any + let dirs: any[] if (level === 0) { dirs = userConfigInfo.dirList } else { dirs = await getDirInfoList(userConfigInfo, pathLabels.join('/')) } - if (dirs) { + if (dirs.length) { resolve( dirs.map((x: any) => ({ value: x.value, diff --git a/src/components/selected-info-bar/selected-info-bar.vue b/src/components/selected-info-bar/selected-info-bar.vue index 1a302e96..ef083663 100644 --- a/src/components/selected-info-bar/selected-info-bar.vue +++ b/src/components/selected-info-bar/selected-info-bar.vue @@ -6,12 +6,6 @@ {{ userConfigInfo.selectedRepo }} - - {{ $t('branch') }}: - - {{ userConfigInfo.selectedBranch }} - - {{ $t('dir') }}: - + - + - + - + - + - {{ $t('settings.img_watermark.position_1') }} + {{ $t('settings_page.img_watermark.position_1') }} - {{ $t('settings.img_watermark.position_2') }} + {{ $t('settings_page.img_watermark.position_2') }} - {{ $t('settings.img_watermark.position_3') }} + {{ $t('settings_page.img_watermark.position_3') }} - {{ $t('settings.img_watermark.position_4') }} + {{ $t('settings_page.img_watermark.position_4') }} diff --git a/src/locales/en.json b/src/locales/en.json index 5f2de2f5..2b8309ef 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -2,61 +2,93 @@ "repo": "Repository", "branch": "Branch", "dir": "Directory", - "tips": "Tips", + "tip": "Tip", "delete": "Delete", "reset": "Reset", "confirm": "Confirm", "cancel": "Cancel", "author": "Author", "document": "Document", - "shortcut_key": "Shortcut key", - "copy_link": "Copy image link", - "copy_success_1": "The image link has been automatically copied to the system clipboard", + "shortcut_key": "Shortcut Key", + "username": "Username", + "email": "Email", + "login": "Login", + "logout": "Logout", + "language": "Language", + "uploaded": "Uploaded", + "upload": "Upload", + "rename": "Rename", + "copy_link": "Copy Image Link", + "copy_success_1": "Image link has been automatically copied to the system clipboard", "copy_success_2": "Image link copied successfully", "copy_fail_1": "Copy failed", "header": { - "not_login": "Not log in", - "login": "Log in", - "logout": "Log out", - "language": "Language", + "not_login": "Not Logged In", "theme": "Theme", - "usage_count": "Usage count" + "usage_count": "Usage Count" }, "nav": { - "login": "Log in", - "config": "Image Hosting Config", + "config": "Image Hosting Configuration", "upload": "Upload Image", "management": "Image Hosting Management", "settings": "My Settings", "toolbox": "Toolbox", "feedback": "Feedback", - "actions": "Quick actions" + "actions": "Quick Actions" }, "actions": { - "compress": "Compress Image", "watermark": "Add Watermark", - "transform": "Transform " + "compress": "Compress Image", + "transform": "Transform" }, - "settings": { + "config_page": { + "input_token": "Please enter GitHub Token...", + "one_click_config": "One-Click Configuration", + "loading_1": "Loading user information...", + "loading_4": "Loading repository directory data...", + "loading_5": "Loading directory information...", + "loading_6": "Auto-configuring...", + "placeholder_4": "Please enter the name of the new directory...", + "placeholder_5": "Please select a directory...", + "message_1": "GitHub Token cannot be empty", + "message_2": "Failed to retrieve user information. Please check if the Token is correct", + "message_3": "Auto-creation of GitHub repository failed. Please try again later", + "message_4": "Auto-configuration successful", + "message_5": "One-click auto-configuration failed. Please retry", + "message_6": "Directory cannot be empty", + "message_7": "Please enter a new directory", + "message_8": "Please select a directory in the repository", + "message_9": "Failed to retrieve repository information. Please try again later", + "dir_mode": "Directory Mode", + "create_new_dir": "Create New Directory", + "input_new_dir": "Manually input a new directory", + "root_dir": "Root Directory", + "date_dir": "Date Directory", + "date_dir_tip": "Automatically create directories in the format yyyyMMdd", + "repo_dir": "Repository Directory", + "select_dir": "Select Directory", + "reload": "Reload" + }, + "settings_page": { "img_name": { "title": "Image Name Settings", - "hash_switch_name": "Hashing", - "hash_switch_desc": "Automatically add a hash code to the image name before uploading to ensure uniqueness. It is strongly recommended to turn on this feature.", + "hash_switch_name": "Auto Hashing", + "hash_switch_desc": "Automatically adds a hash code to the image name before uploading to ensure uniqueness. Highly recommended to enable.", "prefix_switch_name": "Add Prefix", - "prefix_switch_desc": "Automatically add a prefix to the image name before uploading. For example: abc-image.jpg, where abc- is the prefix.", - "prefix_input_placeholder": "Please enter the name prefix...", - "timestamp_switch_name": "Automatically timestamp naming" + "prefix_switch_desc": "Automatically adds a prefix to the image name before uploading, e.g., abc-image.jpg, where abc- is the prefix.", + "prefix_input_placeholder": "Enter the naming prefix...", + "timestamp_switch_name": "Auto Timestamp Naming" }, "img_watermark": { "title": "Image Watermark Settings", "switch_name": "Add Watermark", - "switch_desc": "After turning on this feature, you can customize the watermark text, font size, position, color, and transparency.", - "text": "Text", - "text_input_placeholder": "Please enter the watermark text, limited to 20 characters.", - "size": "Size", - "color": "Color", - "opacity": "Opacity", - "position": "Position", + "switch_desc": "When enabled, you can customize watermark text, font size, position, color, and opacity.", + "text": "Watermark Text", + "text_input_placeholder": "Enter watermark text, limit 20 characters", + "size": "Watermark Size", + "color": "Watermark Color", + "opacity": "Watermark Opacity", + "position": "Watermark Position", "position_1": "Top Left", "position_2": "Bottom Left", "position_3": "Top Right", @@ -65,215 +97,165 @@ "img_compress": { "title": "Image Compression Settings", "switch_name": "Compress Images", - "switch_desc": "After enable, the image will be auto compressed before uploading, which can effectively shorten the image upload and loading time. It is strongly recommended enable.", - "radio_group_title": "Select image compression algorithm", - "radio_1": "WebP", - "radio_1_desc": "The compressed image format is webp, which has a higher compression ratio and is supported by most browsers.", - "radio_2": "MozJPEG", - "radio_2_desc": "The compressed image format is jpg, which has the best compatibility and is supported by all browsers.", - "radio_3": "AVIF", - "radio_3_desc": "The compressed image format is avif, which has an extremely high compression ratio and is supported by some modern browsers." + "switch_desc": "When enabled, images will be automatically compressed before uploading, effectively reducing upload and load times. Highly recommended to enable.", + "radio_group_title": "Select Image Compression Algorithm", + "radio_1": "webP", + "radio_1_desc": "Compressed images will be in webp format, with a high compression ratio supported by most browsers.", + "radio_2": "mozJPEG", + "radio_2_desc": "Compressed images will be in jpg format, providing the best compatibility with all browsers.", + "radio_3": "avif", + "radio_3_desc": "Compressed images will be in avif format, with an extremely high compression ratio supported by some modern browsers." }, "link_rule": { - "title": "Image Link Rules Configuration", + "title": "Image Link Rule Configuration", "select_title": "Select Image Link Rule", "card_title": "Configure Custom Image Link Rules", "card_table_col_title_1": "Type", "card_table_col_title_2": "Image Link Rule", "card_table_col_title_3": "Action", - "input_name_1": "Link Type", - "input_name_1_rule": "The Image Link Type Name cannot be empty.", - "input_name_2": "Link Rule", - "input_name_2_rule": "The Image Link Rule cannot be empty.", + "input_name_1": "Image Link Type", + "input_name_1_rule": "Image link type name cannot be empty", + "input_name_2": "Image Link Rule", + "input_name_2_rule": "Image link rule cannot be empty", "btn_name_1": "Add Image Link Rule", - "error_msg_1": "Failed to add, the image link rule already exists", - "error_msg_2": "{action} failed, image link rule must contain {path}", - "error_msg_3": "The image link rule is missing {rules}. Do you want to confirm {action}?", - "add": "add", - "edit": "edit" + "error_msg_1": "Failed to add. This image link rule already exists.", + "error_msg_2": "Failed to {action}. Image link rule must include {path}.", + "error_msg_3": "Image link rule is missing {rules}. Confirm {action}?", + "add": "Add", + "edit": "Edit" }, "link_format": { "title": "Image Link Format Settings", - "switch_name": "Automatically Convert Image Link Format", - "switch_desc": "After a successful upload, the copied image link will be automatically converted to the {type} format.", + "switch_name": "Auto Convert Image Link Format", + "switch_desc": "Copy-pasted image links will be automatically converted to {type} format after successful upload.", "select_title": "Select Image Link Format", - "delete_tips": "This operation will permanently delete the image link rule" + "delete_tips": "This action will permanently delete the image link rule." }, "image_hosting_deploy": { - "title": "Image hosting deployment", - "one_click_deploy": "One-click deployment", + "title": "Image Hosting Deployment", + "one_click_deploy": "One-Click Deploy", "deploy_to": "Deploy to {server}", "success": "Deployment successful", "fail": "Deployment failed", - "fail2": "Deployment failed, please try again later", - "deploying": "Deploying to GitHub Pages, this process will take about 1 minute, please wait patiently...", - "not_deployed": "Not deployed" + "fail2": "Deployment failed. Please try again later.", + "deploying": "Deploying to GitHub Pages, this process takes about 1 minute, please be patient...", + "not_deployed": "Not Deployed" }, "theme": { "title": "Theme Settings", - "system": "System", - "dark": "Dark", - "light": "Light" + "system": "System Default", + "dark": "Dark Mode", + "light": "Light Mode" }, "cloud_settings": { - "tip_1": "It is detected that your settings data is not saved to the cloud repository. Do you want to save it?", - "tip_2": "It is detected that your cloud repository has a configuration file. Do you want to use it?", - "tip_3": "It is detected that your settings data has changed. Do you want to synchronize it to the cloud repository?", - "tip_4": "The local settings data is consistent with the cloud repository settings data", - "success_msg_1": "Save successfully", - "success_msg_2": "Update successful" + "tip_1": "Your settings data has not been saved to the cloud repository. Would you like to save now?", + "tip_2": "Settings files found in your cloud repository. Would you like to use them?", + "tip_3": "Changes detected in your settings data. Would you like to sync with the cloud repository?", + "tip_4": "Local and cloud repository settings data are consistent.", + "success_msg_1": "Saved successfully", + "success_msg_2": "Updated successfully" } }, - "config": { - "inputToken": "Please input GitHub Token ...", - "manualConfiguration1": "Manual Configuration", - "manualConfiguration2": "Reconfigure Manually", - "manualConfiguration3": "Configure using an existing GitHub repository step by step", - "autoConfiguration1": "One-click Automatic Configuration", - "autoConfiguration2": "Reconfigure Automatically", - "autoConfiguration3": "Automatically create a GitHub repository (Recommend)", - "username": "Username", - "email": "Email", - "selectRepo": "Select Repo", - "selectBranch": "Select Branch", - "loading1": "Loading user information ...", - "loading2": "Loading branch information ...", - "loading3": "Loading user information ...", - "loading4": "Loading {type} data ...", - "loading5": "Loading directory information ...", - "loading6": "Automatically configuring ...", - "placeholder1": "Please select image hosting repository ...", - "placeholder2": "Please select branch ...", - "placeholder3": "Please enter the name of new branch ...", - "placeholder4": "Please enter the newly created directory ...", - "placeholder5": "Please select a directory ...", - "message1": "GitHub token cannot be empty", - "message2": "Failed to retrieve user information, please verify that the token is correct", - "message3": "Failed to automatically create GitHub repository, please try again later", - "message4": "Automated configuration successful", - "message5": "One-click automated configuration failed, please try again.", - "message6": "Directory cannot be empty", - "message7": "Please enter a new directory", - "message8": "Please select a directory in the {repo} repository", - "message9": "Failed to retrieve repository information, please try again later", - "message11": "Failed to retrieve user information, please verify that the GitHub Token is valid", - "reload": "Reload all {type} data", - "dirMode": "Dir Mode", - "inputNewDir": "Manually input a new directory", - "createNewDir": "New Dir", - "rootDir": "Root Dir", - "rootDir2": "Images are stored in the root directory of {branch} branch", - "autoDir": "Auto Dir", - "autoDir2": "Automatically create a directory with format yyyyMMdd", - "repoDir": "Select directory in {repo} repository", - "repoDir2": "Choose a directory in the {branch} branch", - "selectDir": "Select Dir" - }, - "upload": { - "uploadAreaTips": "Drag / Paste / Click here to select images", - "uploaded": "Uploaded", - "upload": "Upload", - "message1": "Please complete the image hosting configuration first", + "upload_page": { + "upload_area_text": "Drag / Paste / Click here to select images", + "message1": "Please complete the image bed configuration first", "message2": "Please select a repository", "message3": "Directory cannot be empty", - "message4": "Please select an image to upload", + "message4": "Please select the images to upload", "message5": "Image uploaded successfully", - "message6": "Images uploaded successfully in batch", + "message6": "Batch upload of images successful", "message7": "Upload failed, please try again later", "message8": "Image uploaded successfully", - "fold": "Fold", + "fold": "Collapse", "expand": "Expand", - "hash": "Hashing", - "rename": "Rename", - "timestamp-naming": "Timestamp Naming", - "prefixNaming": "Prefix Naming", - "delete": "Delete", - "copyLink": "Copy image link", - "loading1": "Uploading ..." + "hash": "Auto Hashing", + "timestamp_naming": "Auto Timestamp Naming", + "prefix_naming": "Prefix Naming", + "uploading": "Uploading..." }, - "management": { + "management_page": { "reload": "Reload all data in the {dir} directory", - "loadingTxt1": "Loading ...", - "loadingTxt2": "Renaming ...", - "loadingTxt3": "Deleting ...", - "back": "Double-click back", + "loadingTxt1": "Loading...", + "loadingTxt2": "Renaming...", + "loadingTxt3": "Deleting...", + "back": "Double-click to go back", "toNextDir": "Double-click to enter the next level directory", - "property": "Property", - "delTips": "This operation will permanently delete the image", - "delTips2": "Are you sure you want to delete the selected {total} images in bulk?", - "renameTips": "Rename this picture to {name}?", + "property": "Properties", + "delTips": "This action will permanently delete the image", + "delTips2": "Selected {total} images, do you want to delete them in batch?", + "renameTips": "Rename this image to {name}?", "message1": "Image name cannot be empty", - "message2": "Image name unchanged", + "message2": "No changes in the image name", "message3": "Rename failed", - "message4": "Update successfully", - "message5": "Delete successfully", - "message6": "Batch delete successfully", + "message4": "Update successful", + "message5": "Deletion successful", + "message6": "Batch deletion successful", "message7": "Deletion failed, please try again later", - "imageName": "Image name", - "imageSize": "Image size", - "selectAll": "Select all", - "deselectAll": "Deselect all", + "imageName": "Image Name", + "imageSize": "Image Size", + "selectAll": "Select All", + "deselectAll": "Deselect All", "unselect": "Unselect", - "batchCopy": "Batch copy image link", - "batchDelete": "Batch delete image", + "batchCopy": "Batch Copy Image Links", + "batchDelete": "Batch Delete Images", "selectTotal": "Selected {total} images", - "contextmenu_1": "Upload new image from current location", - "contextmenu_2": "Upload image to < {dir} >", - "contextmenu_3": "Root directory" + "contextmenu_1": "Upload new images from the current location", + "contextmenu_2": "Upload images to < {dir} >", + "contextmenu_3": "Root Directory" }, "toolbox": { - "tool_1": "Image Compressor", - "tool_1_desc": "Offline ultimate compression without size or quantity limit, and without uploading to server", - "tool_2": "Image to Base64", - "tool_2_desc": "Online conversion of images to Base64 encoding without size or quantity limit", - "tool_3": "Image Watermark", - "tool_3_desc": "Customize watermark text, font size, position, color and opacity", - "copy_base64": "Click to copy Base64 encoding", - "copy_base64_success": "Base64 encoding copied successfully", - "click_download": "Click to download", - "compress": "Compress", - "batch_download": "Batch Download", - "add_watermark": "Add Watermark" + "tool_1": "图片压缩", + "tool_1_desc": "不限制图片大小和数量,不上传至服务器的离线极致压缩", + "tool_2": "图片转 Base64", + "tool_2_desc": "不限制图片大小和数量,在线转换成 Base64 编码", + "tool_3": "图片水印 ", + "tool_3_desc": "自定义水印文字、字体大小、位置、颜色和透明度", + "copy_base64": "点击复制 Base64 编码", + "copy_base64_success": "Base64 编码复制成功", + "click_download": "点击下载", + "compress": "压缩", + "batch_download": "批量下载", + "add_watermark": "添加水印" }, "feedback": { - "text_1": "PicX is a GitHub API based image hosting tool, providing image upload and hosting, generating image links, and a set of commonly used image tools.", - "text_2": "We suggest adding our website to your browser bookmarks for easy future access.", - "text_3": "If PicX is helpful to you, please consider donating to the author to support open source development.", - "text_4": "Disclaimer: Do not upload images that violate the laws in your local jurisdiction. The author is not responsible for any consequences arising from such actions." + "text_1": "PicX is an image hosting tool developed based on the GitHub API, providing services for image upload hosting, generating image links, and common image tools.", + "text_2": "It is recommended to add this site to your browser bookmarks for convenient use in the future.", + "text_3": "If PicX is helpful to you, feel free to appreciate the author and support open source.", + "text_4": "Disclaimer: Do not use PicX to upload images that violate local laws. The author is not responsible for any consequences." }, "region": { "CN": "Mainland China", "HK": "Hong Kong, China", - "MO": "Macao, China", + "MO": "Macau, China", "TW": "Taiwan, China", "SG": "Singapore", "JP": "Japan", "US": "United States", "NL": "Netherlands" }, - "language": { + "languages": { "zh-CN": "Simplified Chinese", "zh-TW": "Traditional Chinese", "en": "English" }, - "toggle_language_msg": "We detected that your IP is located in {region}. Do you want to switch to {language}?", + "toggle_language_msg": "Detected that your IP belongs to {region}. Do you want to switch to {language}?", "authorization": { - "msg_1": "GitHub OAuth authorization login has expired, please re-authorize", - "msg_2": "PicX APP is not installed on GitHub and has no operation permission yet", - "msg_3": "PicX GitHub APP is installed successfully. Do you want to authorize the login?", - "msg_4": "Authorization failed, try again later", - "btn_1": "Install now", + "msg_1": "GitHub OAuth authorization has expired. Please reauthorize.", + "msg_2": "PicX APP is not installed on GitHub, no operational permissions currently.", + "msg_3": "PicX GitHub APP installed successfully. Would you like to authorize and log in?", + "msg_4": "Authorization failed. Please try again later.", + "btn_1": "Install Now", "loading_1": "Authorizing login...", - "text_1": "GitHub OAuth authorization login", - "text_2": "Fill in GitHub Token to log in", - "text_3": "Login using GitHub OAuth authorization", - "text_4": "GitHub Token has expired, please authorize your login again", + "text_1": "GitHub OAuth Authorization Login", + "text_2": "Login with GitHub Token", + "text_3": "Logging in using GitHub OAuth Authorization", + "text_4": "GitHub Token has expired. Please reauthorize login.", "text_5": "Logging in using GitHub Token", - "text_6": "Return to login page", + "text_6": "Return to Login Page", "text_7": "Switch", - "text_8": "PicX GitHub APP must be installed to log in using GitHub OAuth authorization", - "text_9": "Filling in the GitHub Token to log in must generate a Token with operation permissions", - "text_10": "View tutorial", + "text_8": "GitHub OAuth Authorization Login requires PicX GitHub APP installation.", + "text_9": "Login with GitHub Token requires generating a Token with operational permissions.", + "text_10": "View Tutorial", "text_11": "Install PicX GitHub APP", "text_12": "Create GitHub Token" } diff --git a/src/locales/zh-CN.json b/src/locales/zh-CN.json index efc1e381..d2f508b1 100644 --- a/src/locales/zh-CN.json +++ b/src/locales/zh-CN.json @@ -2,7 +2,7 @@ "repo": "仓库", "branch": "分支", "dir": "目录", - "tips": "提示", + "tip": "提示", "delete": "删除", "reset": "重置", "confirm": "确定", @@ -10,20 +10,24 @@ "author": "作者", "document": "文档", "shortcut_key": "快捷键", + "username": "用户名", + "email": "邮箱", + "login": "登录", + "logout": "退出登录", + "language": "语言", + "uploaded": "已上传", + "upload": "上传", + "rename": "重命名", "copy_link": "复制图片链接", "copy_success_1": "图片链接已自动复制到系统剪贴板", "copy_success_2": "图片链接复制成功", "copy_fail_1": "复制失败", "header": { "not_login": "未登录", - "login": "登录", - "logout": "退出登录", - "language": "语言", "theme": "主题", "usage_count": "使用次数" }, "nav": { - "login": "登录", "config": "图床配置", "upload": "上传图片", "management": "图床管理", @@ -37,7 +41,35 @@ "compress": "压缩图片", "transform": "转换 " }, - "settings": { + "config_page": { + "input_token": "请输入 GitHub Token ...", + "one_click_config": "一键配置", + "loading_1": "正在加载用户信息 ...", + "loading_4": "正在加载仓库目录数据 ...", + "loading_5": "正在加载目录信息 ...", + "loading_6": "正在自动配置 ...", + "placeholder_4": "请输入新建的目录 ...", + "placeholder_5": "请选择一个目录 ...", + "message_1": "GitHub Token 不能为空", + "message_2": "用户信息获取失败,请确认 Token 是否正确", + "message_3": "自动创建 GitHub 仓库失败,请稍后再试", + "message_4": "自动配置成功", + "message_5": "一键自动配置失败,请重试", + "message_6": "目录不能为空", + "message_7": "请输入一个新目录", + "message_8": "请选择仓库里的一个目录", + "message_9": "仓库信息获取失败,请稍后重试", + "dir_mode": "目录模式", + "create_new_dir": "新建目录", + "input_new_dir": "手动输入一个新目录", + "root_dir": "根目录", + "date_dir": "日期目录", + "date_dir_tip": "自动创建 yyyyMMdd 格式的日期目录", + "repo_dir": "仓库目录", + "select_dir": "选择目录", + "reload": "重新加载" + }, + "settings_page": { "img_name": { "title": "图片名称设置", "hash_switch_name": "自动哈希化", @@ -124,56 +156,8 @@ "success_msg_2": "更新成功" } }, - "config": { - "inputToken": "请输入 GitHub Token ...", - "manualConfiguration1": "手动配置", - "manualConfiguration2": "重新手动配置", - "manualConfiguration3": "使用已有的 GitHub 仓库,逐步完成配置", - "autoConfiguration1": "一键自动配置", - "autoConfiguration2": "重新一键自动配置", - "autoConfiguration3": "自动创建 GitHub 仓库(推荐)", - "username": "用户名", - "email": "邮箱", - "selectRepo": "选择仓库", - "selectBranch": "选择分支", - "loading1": "正在加载用户信息 ...", - "loading2": "正在加载分支信息 ...", - "loading3": "正在加载用户信息 ...", - "loading4": "正在加载{type}数据 ...", - "loading5": "正在加载目录信息 ...", - "loading6": "正在自动配置 ...", - "placeholder1": "请选择图床仓库 ...", - "placeholder2": "请选择分支 ...", - "placeholder3": "请输入新建的分支 ...", - "placeholder4": "请输入新建的目录 ...", - "placeholder5": "请选择一个目录 ...", - "message1": "GitHub Token 不能为空", - "message2": "用户信息获取失败,请确认 Token 是否正确", - "message3": "自动创建 GitHub 仓库失败,请稍后再试", - "message4": "自动配置成功", - "message5": "一键自动配置失败,请重试", - "message6": "目录不能为空", - "message7": "请输入一个新目录", - "message8": "请选择 {repo} 仓库里的一个目录", - "message9": "仓库信息获取失败,请稍后重试", - "message11": "用户信息获取失败,请确认 GitHub Token 是否有效", - "selectBranch2": "选择 {repo}} 仓库里的分支", - "reload": "重新载入所有{type}数据", - "dirMode": "目录模式", - "inputNewDir": "手动输入一个新目录", - "createNewDir": "新建目录", - "rootDir": "根目录", - "rootDir2": "图片存储在 {branch} 分支的根目录下", - "autoDir": "自动目录", - "autoDir2": "根据日期自动创建格式 yyyyMMdd 的目录", - "repoDir": "选择 {repo} 仓库目录", - "repoDir2": "选择 {branch} 分支里的一个目录", - "selectDir": "选择目录" - }, - "upload": { - "uploadAreaTips": "拖拽 / 粘贴 / 点击此处选择图片", - "uploaded": "已上传", - "upload": "上传", + "upload_page": { + "upload_area_text": "拖拽 / 粘贴 / 点击此处选择图片", "message1": "请先完成图床配置", "message2": "请选择一个仓库", "message3": "目录不能为空", @@ -185,14 +169,11 @@ "fold": "折叠", "expand": "展开", "hash": "哈希化", - "rename": "重命名", - "timestamp-naming": "时间戳命名", - "prefixNaming": "前缀命名", - "delete": "删除", - "copyLink": "复制图片链接", - "loading1": "正在上传 ..." + "timestamp_naming": "时间戳命名", + "prefix_naming": "前缀命名", + "uploading": "正在上传 ..." }, - "management": { + "management_page": { "reload": "重新加载 {dir} 目录所有数据", "loadingTxt1": "加载中 ...", "loadingTxt2": "正在重命名 ...", @@ -222,6 +203,7 @@ "contextmenu_2": "上传图片到 < {dir} >", "contextmenu_3": "根目录" }, + "toolbox": { "tool_1": "图片压缩", "tool_1_desc": "不限制图片大小和数量,不上传至服务器的离线极致压缩", @@ -252,7 +234,7 @@ "US": "美国", "NL": "荷兰" }, - "language": { + "languages": { "zh-CN": "中文简体", "zh-TW": "中文繁体", "en": "英文" diff --git a/src/locales/zh-TW.json b/src/locales/zh-TW.json index 217b2098..adfffabf 100644 --- a/src/locales/zh-TW.json +++ b/src/locales/zh-TW.json @@ -1,103 +1,135 @@ { - "repo": "倉庫", + "repo": "倉儲", "branch": "分支", "dir": "目錄", - "tips": "提示", + "tip": "提示", "delete": "刪除", "reset": "重置", "confirm": "確定", "cancel": "取消", "author": "作者", - "document": "文檔", - "shortcut_key": "快捷鍵", - "copy_link": "復製圖片鏈接", - "copy_success_1": "圖片鏈接已自動復製到系統剪貼板", - "copy_success_2": "圖片鏈接復製成功", - "copy_fail_1": "復製失敗", + "document": "文件", + "shortcut_key": "快速鍵", + "username": "名稱", + "email": "信箱", + "login": "登入", + "logout": "退出登入", + "language": "語言", + "uploaded": "已上傳", + "upload": "上傳", + "rename": "重新命名", + "copy_link": "複製圖片連結", + "copy_success_1": "圖片連結已自動複製到系統剪貼簿", + "copy_success_2": "圖片連結複製成功", + "copy_fail_1": "複製失敗", "header": { - "not_login": "未登錄", - "login": "登錄", - "logout": "退出登錄", - "language": "語言", + "not_login": "未登入", "theme": "主題", "usage_count": "使用次數" }, "nav": { - "login": "登錄", "config": "圖床配置", "upload": "上傳圖片", "management": "圖床管理", - "settings": "我的設置", + "settings": "我的設定", "toolbox": "工具箱", - "feedback": "幫助反饋", - "actions": "快捷操作" + "feedback": "幫助回饋", + "actions": "快速操作" }, "actions": { - "watermark": "添加水印", + "watermark": "新增浮水印", "compress": "壓縮圖片", "transform": "轉換 " }, - "settings": { + "config_page": { + "input_token": "請輸入 GitHub Token ...", + "one_click_config": "一鍵配置", + "loading_1": "正在載入使用者資訊 ...", + "loading_4": "正在載入倉庫目錄資料 ...", + "loading_5": "正在載入目錄資訊 ...", + "loading_6": "正在自動設定 ...", + "placeholder_4": "請輸入新建的目錄 ...", + "placeholder_5": "請選擇目錄 ...", + "message_1": "GitHub Token 不能為空", + "message_2": "使用者資訊取得失敗,請確認 Token 是否正確", + "message_3": "自動建立 GitHub 倉儲失敗,請稍後再試", + "message_4": "自動設定成功", + "message_5": "一鍵自動設定失敗,請重試", + "message_6": "目錄不能為空", + "message_7": "請輸入一個新目錄", + "message_8": "請選擇倉庫裡的目錄", + "message_9": "倉庫資訊取得失敗,請稍後重試", + "dir_mode": "目錄模式", + "create_new_dir": "新目錄", + "input_new_dir": "手動輸入一個新目錄", + "root_dir": "根目錄", + "date_dir": "日期目錄", + "date_dir_tip": "自動建立 yyyyMMdd 格式的日期目錄", + "repo_dir": "倉儲目錄", + "select_dir": "選擇目錄", + "reload": "重新載入" + }, + "settings_page": { "img_name": { - "title": "圖片名稱設置", + "title": "圖片名稱設定", "hash_switch_name": "自動哈希化", - "hash_switch_desc": "上傳前自動給圖片名增加哈希碼,確保圖片名唯一,強烈建議開啟", - "prefix_switch_name": "添加前綴命名", - "prefix_switch_desc": "上傳前自動給圖片名增加前綴,例如:abc-image.jpg,abc- 為前綴", + "hash_switch_desc": "上傳前自動給圖片名稱增加哈希碼,確保圖片名稱唯一,強烈建議開啟", + "prefix_switch_name": "加上前綴命名", + "prefix_switch_desc": "上傳前自動為圖片名稱增加前綴,例如:abc-image.jpg,abc- 為前綴", "prefix_input_placeholder": "請輸入命名前綴 ...", - "timestamp_switch_name": "自動時間戳命名" + "timestamp_switch_name": "自動時間戳記命名" }, "img_watermark": { - "title": "圖片水印設置", - "switch_name": "是否添加水印", - "switch_desc": "開啟後可以自定義水印文字、字體大小、位置、顏色和透明度", + "title": "圖片浮水印設定", + "switch_name": "是否新增浮水印", + "switch_desc": "開啟後可以自訂浮水印文字、字體大小、位置、顏色和透明度", "text": "水印文字", - "text_input_placeholder": "請輸入水印文字,限製 20 字", - "size": "水印大小", + "text_input_placeholder": "請輸入浮水印文字,限制 20 字", + "size": "浮水印大小", "color": "水印顏色", - "opacity": "水印透明度", - "position": "水印位置", + "opacity": "浮水印透明度", + "position": "浮水印位置", "position_1": "左上角", "position_2": "左下角", "position_3": "右上角", "position_4": "右下角" }, "img_compress": { - "title": "圖片壓縮設置", + "title": "圖片壓縮設定", "switch_name": "是否壓縮圖片", - "switch_desc": "開啟後上傳前會自動壓縮圖片,有效縮短圖片上傳和加載時間,強烈建議開啟", - "radio_group_title": "選擇圖片壓縮算法", + "switch_desc": "開啟後上傳前會自動壓縮圖片,有效縮短圖片上傳與載入時間,強烈建議開啟", + "radio_group_title": "選擇圖片壓縮演算法", "radio_1": "webP", - "radio_1_desc": "壓縮後圖片格式為 webp,壓縮率較高,大多數瀏覽器支持", + "radio_1_desc": "壓縮後圖片格式為 webp,壓縮率較高,大多數瀏覽器支援", "radio_2": "mozJPEG", - "radio_2_desc": "壓縮後圖片格式為 jpg,兼容性最好,所有瀏覽器支持", + "radio_2_desc": "壓縮後圖片格式為 jpg,相容性最好,所有瀏覽器支援", "radio_3": "avif", - "radio_3_desc": "壓縮後圖片格式為 avif,壓縮率極高,部分現代瀏覽器支持" + "radio_3_desc": "壓縮後圖片格式為 avif,壓縮率極高,部分現代瀏覽器支援" }, "link_rule": { - "title": "圖片鏈接規則配置", - "select_title": "選擇圖片鏈接規則", - "card_title": "配置自定義圖片鏈接規則", - "card_table_col_title_1": "類型", - "card_table_col_title_2": "圖片鏈接規則", + "title": "圖片連結規則配置", + "select_title": "選擇圖片連結規則", + "card_title": "配置自訂圖片連結規則", + "card_table_col_title_1": "型別", + "card_table_col_title_2": "圖片連結規則", "card_table_col_title_3": "操作", - "input_name_1": "圖片鏈接類型", - "input_name_1_rule": "圖片鏈接類型名稱不能為空", - "input_name_2": "圖片鏈接規則", - "input_name_2_rule": "圖片鏈接規則不能為空", - "btn_name_1": "新增圖片鏈接規則", - "error_msg_1": "新增失敗,該圖片連結規則已存在", + "input_name_1": "圖片連結類型", + "input_name_1_rule": "圖片連結類型名稱不能為空", + "input_name_2": "圖片連結規則", + "input_name_2_rule": "圖片連結規則不能為空", + "btn_name_1": "新增圖片連結規則", + "error_msg_1": "新增失敗,此圖片連結規則已存在", "error_msg_2": "{action}失敗,圖片連結規則必須包含 {path}", "error_msg_3": "圖片連結規則缺少 {rules},是否確認{action}?", "add": "新增", "edit": "編輯" }, "link_format": { - "title": "圖片鏈接格式設置", - "switch_name": "自動轉換圖片鏈接格式", - "switch_desc": "上傳成功後復製的圖片鏈接自動轉換成 {type} 格式", - "select_title": "選擇圖片鏈接格式", - "delete_tips": "此操作將永久刪除圖片鏈接規則" + "title": "圖片連結格式設定", + "switch_name": "自動轉換圖片連結格式", + "switch_desc": "上傳成功後複製的圖片連結會自動轉換成 {type} 格式", + "select_title": "選擇圖片連結格式", + "delete_tips": "此動作將永久刪除圖片連結規則" }, "image_hosting_deploy": { "title": "圖床部署", @@ -110,9 +142,9 @@ "not_deployed": "未部署" }, "theme": { - "title": "主題設置", + "title": "主題設定", "system": "跟隨系統", - "dark": "暗夜", + "dark": "黑夜", "light": "白晝" }, "cloud_settings": { @@ -120,144 +152,93 @@ "tip_2": "偵測到你的雲端倉庫有設定文件,是否使用?", "tip_3": "偵測到你的設定資料有變化,是否同步到雲端倉庫?", "tip_4": "本地設定資料與雲端倉庫設定資料一致", - "success_msg_1": "儲存成功", + "success_msg_1": "保存成功", "success_msg_2": "更新成功" } }, - "config": { - "inputToken": "請輸入 GitHub Token ...", - "manualConfiguration1": "手動配置", - "manualConfiguration2": "重新手動配置", - "manualConfiguration3": "使用已有的 GitHub 倉庫,逐步完成配置", - "autoConfiguration1": "一鍵自動配置", - "autoConfiguration2": "重新一鍵自動配置", - "autoConfiguration3": "自動創建 GitHub 倉庫(推薦)", - "username": "用戶名", - "email": "郵箱", - "selectRepo": "選擇倉庫", - "selectBranch": "選擇分支", - "loading1": "正在加載用戶信息 ...", - "loading2": "正在加載分支信息 ...", - "loading3": "正在加載用戶信息 ...", - "loading4": "正在加載{type}數據 ...", - "loading5": "正在加載目錄信息 ...", - "loading6": "正在自動配置 ...", - "placeholder1": "請選擇圖床倉庫 ...", - "placeholder2": "請選擇分支 ...", - "placeholder3": "請輸入新建的分支 ...", - "placeholder4": "請輸入新建的目錄 ...", - "placeholder5": "請選擇一個目錄 ...", - "message1": "GitHub Token 不能為空", - "message2": "用戶信息獲取失敗,請確認 Token 是否正確", - "message3": "自動創建 GitHub 倉庫失敗,請稍後再試", - "message4": "自動配置成功", - "message5": "一鍵自動配置失敗,請重試", - "message6": "目錄不能為空", - "message7": "請輸入一個新目錄", - "message8": "請選擇 {repo} 倉庫裏的一個目錄", - "message9": "倉庫信息獲取失敗,請稍後重試", - "message11": "用戶信息獲取失敗,請確認 GitHub Token 是否有效", - "selectBranch2": "選擇 {repo}} 倉庫裏的分支", - "reload": "重新載入所有{type}數據", - "dirMode": "目錄模式", - "inputNewDir": "手動輸入一個新目錄", - "createNewDir": "新建目錄", - "rootDir": "根目錄", - "rootDir2": "圖片存儲在 {branch} 分支的根目錄下", - "autoDir": "自動目錄", - "autoDir2": "根據日期自動創建格式 yyyyMMdd 的目錄", - "repoDir": "選擇 {repo} 倉庫目錄", - "repoDir2": "選擇 {branch} 分支裏的一個目錄", - "selectDir": "選擇目錄" - }, - "upload": { - "uploadAreaTips": "拖拽 / 粘貼 / 點擊此處選擇圖片", - "uploaded": "已上傳", - "upload": "上傳", + "upload_page": { + "upload_area_text": "拖曳 / 貼上 / 點擊此處選擇圖片", "message1": "請先完成圖床配置", "message2": "請選擇一個倉庫", "message3": "目錄不能為空", "message4": "請選擇要上傳的圖片", "message5": "圖片上傳成功", - "message6": "圖片批量上傳成功", + "message6": "圖片批次上傳成功", "message7": "上傳失敗,請稍後重試", "message8": "圖片上傳成功", "fold": "折疊", "expand": "展開", "hash": "哈希化", - "rename": "重命名", - "timestamp-naming": "時間戳命名", - "prefixNaming": "前綴命名", - "delete": "刪除", - "copyLink": "復製圖片鏈接", - "loading1": "正在上傳 ..." + "timestamp_naming": "時間戳記命名", + "prefix_naming": "前綴命名", + "uploading": "正在上傳 ..." }, "management": { - "reload": "重新加載 {dir} 目錄所有數據", - "loadingTxt1": "加載中 ...", - "loadingTxt2": "正在重命名 ...", + "reload": "重新載入 {dir} 目錄所有資料", + "loadingTxt1": "載入中 ...", + "loadingTxt2": "正在重新命名 ...", "loadingTxt3": "刪除中 ...", "back": "雙擊後退", "toNextDir": "雙擊進入下一級目錄", "property": "屬性", "delTips": "此操作將會永久刪除圖片", - "delTips2": "已選中 {total} 張圖片,是否批量刪除?", - "renameTips": "該圖片重命名為 {name} ?", - "message1": "圖片名不能為空", - "message2": "圖片名無改變", - "message3": "重命名失敗", + "delTips2": "已選取 {total} 張圖片,是否已批次刪除?", + "renameTips": "該圖片重新命名為 {name} ?", + "message1": "圖片名稱不能為空", + "message2": "圖片名稱無改變", + "message3": "重新命名失敗", "message4": "更新成功", "message5": "刪除成功", - "message6": "批量刪除成功", + "message6": "批次刪除成功", "message7": "刪除失敗,請稍後重試", "imageName": "圖片名稱", "imageSize": "圖片大小", "selectAll": "全選", "deselectAll": "取消全選", "unselect": "取消選擇", - "batchCopy": "批量復製圖片鏈接", - "batchDelete": "批量刪除圖片", + "batchCopy": "批次複製圖片連結", + "batchDelete": "批次刪除圖片", "selectTotal": "已選擇 {total} 張圖片", - "contextmenu_1": "從當前位置上傳新圖片", + "contextmenu_1": "從目前位置上傳新圖片", "contextmenu_2": "上傳圖片到 < {dir} >", "contextmenu_3": "根目錄" }, "toolbox": { "tool_1": "圖片壓縮", - "tool_1_desc": "不限製圖片大小和數量,不上傳至服務器的離線極致壓縮", + "tool_1_desc": "不限制圖片大小和數量,不上傳至伺服器的離線極致壓縮", "tool_2": "圖片轉 Base64", - "tool_2_desc": "不限製圖片大小和數量,在線轉換成 Base64 編碼", - "tool_3": "圖片水印 ", - "tool_3_desc": "自定義水印文字、字體大小、位置、顏色和透明度", - "copy_base64": "點擊復製 Base64 編碼", - "copy_base64_success": "Base64 編碼復製成功", - "click_download": "點擊下載", + "tool_2_desc": "不限制圖片大小和數量,線上轉換成 Base64 編碼", + "tool_3": "圖片浮水印 ", + "tool_3_desc": "自訂浮水印文字、字體大小、位置、顏色和透明度", + "copy_base64": "點選複製 Base64 編碼", + "copy_base64_success": "Base64 編碼複製成功", + "click_download": "點選下載", "compress": "壓縮", - "batch_download": "批量下載", - "add_watermark": "添加水印" + "batch_download": "批次下載", + "add_watermark": "新增浮水印" }, "feedback": { - "text_1": "PicX 是一款基於 GitHub API 開發的圖床工具,提供圖片上傳托管、生成圖片鏈接和常用圖片工具箱服務。", - "text_2": "建議將本站添加到瀏覽器收藏夾,方便下次使用。", - "text_3": "如果 PicX 對你有幫助,歡迎贊賞作者,支持開源。", + "text_1": "PicX 是一款基於 GitHub API 開發的圖床工具,提供圖片上傳託管、產生圖片連結和常用圖片工具箱服務。", + "text_2": "建議將本站新增至瀏覽器收藏夾,方便下次使用。", + "text_3": "如果 PicX 對你有幫助,歡迎讚賞作者,支持開源。", "text_4": "鄭重聲明:請勿使用 PicX 上傳違反你當地法律的圖片,所造成的一切後果與作者無關。" }, "region": { "CN": "中國大陸", "HK": "中國香港", - "MO": "中国澳门", - "TW": "中國臺灣", + "MO": "中國澳門", + "TW": "中國台灣", "SG": "新加坡", "JP": "日本", "US": "美國", "NL": "荷蘭" }, - "language": { + "languages": { "zh-CN": "中文簡體", "zh-TW": "中文繁體", "en": "英文" }, - "toggle_language_msg": "檢測到你的 IP 所屬地為{region},是否切換{language}?", + "toggle_language_msg": "偵測到你的 IP 所屬地為{region},是否切換{language}?", "authorization": { "msg_1": "GitHub OAuth 授權登入已過期,請重新授權", "msg_2": "未在 GitHub 安裝 PicX APP,暫無操作權限", diff --git a/src/router/index.ts b/src/router/index.ts index 01ba7b02..105b6bd1 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -16,15 +16,15 @@ const routes: Array = [ name: 'login', component: login, meta: { - title: `nav.login` + title: 'login' } }, { path: '/config', name: 'config', - component: () => import('@/views/my-config/my-config.vue'), + component: () => import('@/views/picx-config/picx-config.vue'), meta: { - title: `nav.config` + title: 'nav.config' } }, { @@ -32,7 +32,7 @@ const routes: Array = [ name: 'upload', component: upload, meta: { - title: `nav.upload` + title: 'nav.upload' } }, { @@ -40,7 +40,7 @@ const routes: Array = [ name: 'Management', component: management, meta: { - title: `nav.management` + title: 'nav.management' } }, { @@ -48,7 +48,7 @@ const routes: Array = [ name: 'settings', component: settings, meta: { - title: `nav.settings` + title: 'nav.settings' } }, { @@ -56,7 +56,7 @@ const routes: Array = [ name: 'Toolbox', component: toolbox, meta: { - title: `nav.toolbox` + title: 'nav.toolbox' }, children: [ { @@ -81,7 +81,7 @@ const routes: Array = [ name: 'feedback', component: feedback, meta: { - title: `nav.feedback` + title: 'nav.feedback' } }, { diff --git a/src/stores/modules/user-config-info/index.ts b/src/stores/modules/user-config-info/index.ts index a0198029..f787d6dd 100644 --- a/src/stores/modules/user-config-info/index.ts +++ b/src/stores/modules/user-config-info/index.ts @@ -1,5 +1,5 @@ import { Module } from 'vuex' -import { BranchModeEnum, UserConfigInfoModel, DirModeEnum } from '@/common/model' +import { UserConfigInfoModel, DirModeEnum } from '@/common/model' import { deepAssignObject, cleanObject, formatDatetime } from '@/utils' import UserConfigInfoStateTypes from '@/stores/modules/user-config-info/types' import RootStateTypes from '@/stores/types' @@ -14,16 +14,14 @@ const initUserConfigInfo = (): UserConfigInfoModel => { name: '', avatarUrl: '', selectedRepo: '', - repoList: [], - branchMode: BranchModeEnum.repoBranch, - branchList: [], selectedBranch: '', selectedDir: '', dirMode: DirModeEnum.repoDir, dirList: [], logined: false, selectedDirList: [], - viewDir: '' + viewDir: '', + repoPrivate: false } const LSConfig: string | null = localStorage.getItem(LS_PICX_CONFIG) @@ -32,16 +30,7 @@ const initUserConfigInfo = (): UserConfigInfoModel => { // Assign: oldConfig -> initConfig deepAssignObject(initConfig, JSON.parse(LSConfig)) - if (initConfig.selectedBranch && !initConfig.branchList.length) { - initConfig.branchList = [ - { - value: initConfig.selectedBranch, - label: initConfig.selectedBranch - } - ] - } - - if (initConfig.dirMode === DirModeEnum.autoDir) { + if (initConfig.dirMode === DirModeEnum.dateDir) { initConfig.selectedDir = formatDatetime('yyyyMMdd') } diff --git a/src/stores/modules/user-settings/index.ts b/src/stores/modules/user-settings/index.ts index c4d301a5..7a370d9d 100644 --- a/src/stores/modules/user-settings/index.ts +++ b/src/stores/modules/user-settings/index.ts @@ -144,7 +144,7 @@ const userSettingsModule: Module = { } }) } else { - ElMessage.error($t('settings.link_rule.error_msg_1')) + ElMessage.error($t('settings_page.link_rule.error_msg_1')) } }, diff --git a/src/stores/modules/user-settings/utils.ts b/src/stores/modules/user-settings/utils.ts index 11bd6887..f3c15dea 100644 --- a/src/stores/modules/user-settings/utils.ts +++ b/src/stores/modules/user-settings/utils.ts @@ -9,12 +9,14 @@ export const imgLinkRuleVerification = ( ) => { const typeTxt = type === ImgLinkRuleActionsEnum.add - ? $t('settings.link_rule.add') - : $t('settings.link_rule.edit') + ? $t('settings_page.link_rule.add') + : $t('settings_page.link_rule.edit') const tmpList = [] if (!rule.rule.includes('{{path}}')) { - ElMessage.error($t('settings.link_rule.error_msg_2', { action: typeTxt, path: '{{path}}' })) + ElMessage.error( + $t('settings_page.link_rule.error_msg_2', { action: typeTxt, path: '{{path}}' }) + ) callback(false) return } @@ -32,7 +34,7 @@ export const imgLinkRuleVerification = ( } if (tmpList.length) { - const confirmTxt = $t('settings.link_rule.error_msg_3', { + const confirmTxt = $t('settings_page.link_rule.error_msg_3', { action: typeTxt, rules: tmpList.join('、') }) diff --git a/src/utils/request/index.ts b/src/utils/request/index.ts index dc16613f..46a138af 100644 --- a/src/utils/request/index.ts +++ b/src/utils/request/index.ts @@ -1,7 +1,18 @@ import axios from './axios' -import { CustomAxiosRequestConfig } from '@/common/model' +import { CustomAxiosRequestConfig } from './types' export default function request(config: CustomAxiosRequestConfig): Promise { + const isNoCache = Boolean(config?.noCache) + if (isNoCache) { + config.cache = { + maxAge: 0 // 设置缓存的最大寿命为 0,禁用缓存 + } + config.params = config.params ? config.params : {} + config.params.timestamp = Date.now() // 添加时间戳参数,防止获取缓存的数据 + // config.params['no-cache'] = Date.now() + delete config.noCache + } + const requestConfig: CustomAxiosRequestConfig = {} // @ts-ignore diff --git a/src/utils/request/types.ts b/src/utils/request/types.ts new file mode 100644 index 00000000..10262a2f --- /dev/null +++ b/src/utils/request/types.ts @@ -0,0 +1,8 @@ +import { AxiosRequestConfig } from 'axios' + +export interface CustomAxiosRequestConfig extends AxiosRequestConfig { + success422?: boolean + noShowErrorMsg?: boolean + cache?: any + noCache?: boolean +} diff --git a/src/views/app-wrap/app-wrap.vue b/src/views/app-wrap/app-wrap.vue index 5d03c99f..9c7dda7f 100644 --- a/src/views/app-wrap/app-wrap.vue +++ b/src/views/app-wrap/app-wrap.vue @@ -80,7 +80,7 @@ const setLanguageByIP = () => { const confirmTxt = instance?.proxy?.$t(`confirm`, language) const msgTxt = instance?.proxy?.$t(`toggle_language_msg`, language, { region: instance?.proxy?.$t(`region.${region}`, language), - language: instance?.proxy?.$t(`language.${language}`, language) + language: instance?.proxy?.$t(`languages.${language}`, language) }) const msgInstance = ElMessage({ diff --git a/src/views/imgs-management/imgs-management.vue b/src/views/imgs-management/imgs-management.vue index e3a9bcac..3804d340 100644 --- a/src/views/imgs-management/imgs-management.vue +++ b/src/views/imgs-management/imgs-management.vue @@ -8,7 +8,7 @@
    @@ -20,7 +20,7 @@
    { url: `/repos/${owner}/${repo}/contents/${filename}`, method: 'GET', noShowErrorMsg: true, - cache: { - maxAge: 0 - }, + noCache: true, params: { - branch, - timestamp: Date.now() + branch } }) diff --git a/src/views/my-config/my-config.vue b/src/views/my-config/my-config.vue deleted file mode 100644 index 48158ea6..00000000 --- a/src/views/my-config/my-config.vue +++ /dev/null @@ -1,421 +0,0 @@ - - - - - diff --git a/src/views/my-settings/my-settings.vue b/src/views/my-settings/my-settings.vue index 9327ccb2..778ff536 100644 --- a/src/views/my-settings/my-settings.vue +++ b/src/views/my-settings/my-settings.vue @@ -4,29 +4,29 @@ - +
    • - {{ $t('settings.img_name.hash_switch_desc') }} + {{ $t('settings_page.img_name.hash_switch_desc') }}
    • - {{ $t('settings.img_name.prefix_switch_desc') }} + {{ $t('settings_page.img_name.prefix_switch_desc') }}
    - +
    • - {{ $t('settings.img_watermark.switch_desc') }} + {{ $t('settings_page.img_watermark.switch_desc') }}
    • @@ -65,15 +65,15 @@ - +
      • - {{ $t('settings.img_compress.switch_desc') }} + {{ $t('settings_page.img_compress.switch_desc') }}
      • @@ -89,10 +89,10 @@ - +
        • - {{ $t('settings.link_rule.select_title') }}: + {{ $t('settings_page.link_rule.select_title') }}: - +
          • {{ - $t('settings.link_format.switch_desc', { + $t('settings_page.link_format.switch_desc', { type: userSettings.imageLinkFormat.selected }) }}
          • - {{ $t('settings.link_format.select_title') }}: + {{ $t('settings_page.link_format.select_title') }}: - + - +
            • {{ $t('header.theme') }}: - +
            diff --git a/src/views/my-config/my-config.styl b/src/views/picx-config/picx-config.styl similarity index 62% rename from src/views/my-config/my-config.styl rename to src/views/picx-config/picx-config.styl index 23a9a418..61469ea8 100644 --- a/src/views/my-config/my-config.styl +++ b/src/views/picx-config/picx-config.styl @@ -12,4 +12,12 @@ } } } + + .refresh-icon { + display flex + align-items center + justify-content flex-end + font-size 20rem + cursor pointer + } } diff --git a/src/views/my-config/my-config.util.ts b/src/views/picx-config/picx-config.util.ts similarity index 65% rename from src/views/my-config/my-config.util.ts rename to src/views/picx-config/picx-config.util.ts index f4f35f77..4d8cfda1 100644 --- a/src/views/my-config/my-config.util.ts +++ b/src/views/picx-config/picx-config.util.ts @@ -1,15 +1,14 @@ import { computed } from 'vue' import { store } from '@/stores' +import { DirModeEnum, ElementPlusSizeEnum, LanguageEnum, UserSettingsModel } from '@/common/model' import { - BranchModeEnum, - DirModeEnum, - ElementPlusSizeEnum, - LanguageEnum, - UserSettingsModel -} from '@/common/model' -import { createRepo, getGitHubUserInfo, initEmptyRepo } from '@/common/api' + createRepo, + getDirInfoList, + getGitHubUserInfo, + getRepoInfo, + initRepoREADME +} from '@/common/api' import { INIT_REPO_BARNCH, INIT_REPO_NAME } from '@/common/constant' -import { formatDatetime } from '@/utils' import router from '@/router' import i18n from '@/plugins/vue/i18n' @@ -34,44 +33,31 @@ export const persistUserConfigInfo = async () => { * @param userInfo */ export async function saveUserInfo(userInfo: any) { - userConfigInfo.logined = true - userConfigInfo.id = userInfo.id - userConfigInfo.owner = userInfo.login - userConfigInfo.name = userInfo.name - userConfigInfo.email = userInfo.email - userConfigInfo.avatarUrl = userInfo.avatar_url - await persistUserConfigInfo() -} - -/** - * 重新手动配置图床,清空之前的配置信息 - */ -export const initReHandConfig = () => { - userConfigInfo.selectedRepo = '' - userConfigInfo.repoList = [] - userConfigInfo.selectedBranch = '' - userConfigInfo.branchMode = BranchModeEnum.repoBranch - userConfigInfo.branchList = [] - userConfigInfo.selectedDir = '' - userConfigInfo.dirMode = DirModeEnum.repoDir - userConfigInfo.dirList = [] + await store.dispatch('SET_USER_CONFIG_INFO', { + logined: true, + id: userInfo.id, + owner: userInfo.login, + name: userInfo.name, + email: userInfo.email, + avatarUrl: userInfo.avatar_url + }) } /** - * 前往 上传图片 页面 + * 前往 [上传图片] 页面 */ export const goUploadPage = async () => { const { selectedDir, dirMode } = userConfigInfo - let warningMessage: string = i18n.global.t('config.message6') + let warningMessage: string = i18n.global.t('config_page.message_6') if (selectedDir === '') { // eslint-disable-next-line default-case switch (dirMode) { case DirModeEnum.newDir: - warningMessage = i18n.global.t('config.message7') + warningMessage = i18n.global.t('config_page.message_7') break case DirModeEnum.repoDir: - warningMessage = i18n.global.t('config.message8', { repo: userConfigInfo.selectedRepo }) + warningMessage = i18n.global.t('config_page.message_8') break } ElMessage.warning({ message: warningMessage }) @@ -81,7 +67,7 @@ export const goUploadPage = async () => { } /** - * GitHub APP 安装状态处理 + * PicX GitHub APP 安装状态处理 * @param repoInfo * @param authorized * @param token @@ -124,68 +110,88 @@ export const installedStatusHandle = async (repoInfo: any, authorized: boolean, /** * 一键自动配置图床 */ -export const oneClickAutoConfig = async () => { +export const oneClickAutoConfig = async (tokenInput: any) => { const { token } = userConfigInfo if (!token) { - ElMessage.error({ message: i18n.global.t('config.message1') }) + ElMessage.error({ message: i18n.global.t('config_page.message_1') }) + tokenInput?.focus() return } const loading = ElLoading.service({ lock: true, - text: i18n.global.t('config.loading6') + text: i18n.global.t('config_page.loading_6') }) try { + // 获取用户信息 const userInfo = await getGitHubUserInfo(userConfigInfo.token) console.log('getGitHubUserInfo >> ', userInfo) if (!userInfo) { loading.close() - ElMessage.error({ message: i18n.global.t('config.message2') }) + ElMessage.error({ message: i18n.global.t('config_page.message_2') }) return } + // 保存 Token 到授权信息 store if (!store.getters.getGitHubAuthorizationInfo.isAutoAuthorize) { await store.dispatch('SET_GITHUB_AUTHORIZATION_INFO', { manualToken: userConfigInfo.token }) } + // 保存用户信息 await saveUserInfo(userInfo) + // 判断是否已存在 PicX 图床仓库 + let isExistInitRepo: boolean = false + const initRepoInfo = await getRepoInfo(userConfigInfo.owner, INIT_REPO_NAME) + console.log('initRepoInfo : ', initRepoInfo) + if (initRepoInfo) { + isExistInitRepo = true + await store.dispatch('SET_USER_CONFIG_INFO', { + repoPrivate: initRepoInfo.private + }) + } + const repoInfo = await createRepo(userConfigInfo.token) console.log('createRepo >> ', repoInfo) + // ---- PicX GitHub APP 安装状态处理 const authorizationInfo = computed(() => store.getters.getGitHubAuthorizationInfo).value const { token, authorized } = authorizationInfo - await installedStatusHandle(repoInfo, authorized, token) - if (!repoInfo) { loading.close() if (!(authorized && token)) { - ElMessage.error({ message: i18n.global.t('config.message3') }) + ElMessage.error({ message: i18n.global.t('config_page.message_3') }) } return } + // -------------------------------- - userConfigInfo.repoList = [{ value: INIT_REPO_NAME, label: INIT_REPO_NAME }] userConfigInfo.selectedRepo = INIT_REPO_NAME - userConfigInfo.branchList = [{ value: INIT_REPO_BARNCH, label: INIT_REPO_BARNCH }] userConfigInfo.selectedBranch = INIT_REPO_BARNCH - userConfigInfo.branchMode = BranchModeEnum.repoBranch - userConfigInfo.selectedDir = formatDatetime('yyyyMMdd') - userConfigInfo.dirMode = DirModeEnum.autoDir - userConfigInfo.dirList = [] + + // 获取目录列表 + userConfigInfo.dirList = await getDirInfoList(userConfigInfo) + + userConfigInfo.dirMode = DirModeEnum.rootDir + userConfigInfo.selectedDir = '/' + await persistUserConfigInfo() - await initEmptyRepo(userConfigInfo, false) + + if (!isExistInitRepo) { + await initRepoREADME(userConfigInfo) + } + loading.close() - ElMessage.success({ message: i18n.global.t('config.message4') }) - await router.push('/upload') + ElMessage.success({ message: i18n.global.t('config_page.message_4') }) + // await router.push('/upload') } catch (err) { - ElMessage.error({ message: i18n.global.t('config.message5') }) + ElMessage.error({ message: i18n.global.t('config_page.message_5') }) console.error('oneClickAutoConfig >> ', err) } } diff --git a/src/views/picx-config/picx-config.vue b/src/views/picx-config/picx-config.vue new file mode 100644 index 00000000..5bd27a92 --- /dev/null +++ b/src/views/picx-config/picx-config.vue @@ -0,0 +1,266 @@ + + + + + diff --git a/src/views/picx-login/picx-login.util.ts b/src/views/picx-login/picx-login.util.ts index 7d478c18..32a50498 100644 --- a/src/views/picx-login/picx-login.util.ts +++ b/src/views/picx-login/picx-login.util.ts @@ -41,7 +41,7 @@ export const githubAppAuthorizeCallback = async () => { installationId: installation_id }) - ElMessageBox.confirm(i18n.global.t('authorization.msg_3'), i18n.global.t('tips'), { + ElMessageBox.confirm(i18n.global.t('authorization.msg_3'), i18n.global.t('tip'), { confirmButtonText: i18n.global.t('confirm'), cancelButtonText: i18n.global.t('cancel'), type: 'success', diff --git a/src/views/upload-image/components/upload-image-card/upload-image-card.vue b/src/views/upload-image/components/upload-image-card/upload-image-card.vue index 2c1bafbd..bbce4a05 100644 --- a/src/views/upload-image/components/upload-image-card/upload-image-card.vue +++ b/src/views/upload-image/components/upload-image-card/upload-image-card.vue @@ -41,7 +41,7 @@ @@ -58,7 +58,7 @@
            @@ -67,7 +67,7 @@
            @@ -86,7 +86,7 @@
            @@ -102,7 +102,7 @@ " > @@ -131,14 +131,14 @@ v-if="imgObj.uploadStatus.progress === 100" @click="copyImageLink(imgObj.uploadedImg, userConfigInfo, userSettings)" > - {{ $t('upload.copyLink') }} + {{ $t('copy_link') }}
            @@ -267,7 +267,7 @@ watch( () => props.imgObj!.uploadStatus, (nv) => { if (nv.uploading) { - loadingText.value = instance!.proxy!.$t('upload.loading1') + loadingText.value = instance!.proxy!.$t('upload_page.uploading') } }, { diff --git a/src/views/upload-image/upload-image.vue b/src/views/upload-image/upload-image.vue index eb649ec3..161e4ab4 100644 --- a/src/views/upload-image/upload-image.vue +++ b/src/views/upload-image/upload-image.vue @@ -31,7 +31,7 @@
            - {{ $t('upload.uploaded') }}:{{ + {{ $t('uploaded') }}:{{ uploadImageList.filter((x) => x.uploadStatus.progress === 100).length }} / @@ -54,7 +54,7 @@ {{ $t('reset') }} [ {{ shortcutKey }} + A ] - {{ $t('upload.upload') }} [ {{ shortcutKey }} + S ] + {{ $t('upload') }} [ {{ shortcutKey }} + S ]
            @@ -144,19 +144,19 @@ const uploadImage = async () => { const { token, selectedRepo, selectedDir } = userConfigInfo if (!token) { - ElMessage.error({ message: instance?.proxy?.$t('upload.message1') }) + ElMessage.error({ message: instance?.proxy?.$t('upload_page.message1') }) await router.push('/config') return } if (!selectedRepo) { - ElMessage.error({ message: instance?.proxy?.$t('upload.message2') }) + ElMessage.error({ message: instance?.proxy?.$t('upload_page.message2') }) await router.push('/config') return } if (!selectedDir) { - ElMessage.error({ message: instance?.proxy?.$t('upload.message3') }) + ElMessage.error({ message: instance?.proxy?.$t('upload_page.message3') }) await router.push('/config') return } @@ -164,7 +164,7 @@ const uploadImage = async () => { const notYetUploadList = uploadImageList.value.filter((x) => x.uploadStatus.progress === 0) if (notYetUploadList.length === 0) { - ElMessage.error({ message: instance?.proxy?.$t('upload.message4') }) + ElMessage.error({ message: instance?.proxy?.$t('upload_page.message4') }) return } @@ -178,19 +178,19 @@ const uploadImage = async () => { switch (uploadRes) { // 单张图片上传成功 case UploadStatusEnum.uploaded: - ElMessage.success({ message: instance?.proxy?.$t('upload.message5') }) + ElMessage.success({ message: instance?.proxy?.$t('upload_page.message5') }) await afterUploadSuccess(uploadedImg) break // 多张图片上传成功 case UploadStatusEnum.allUploaded: - ElMessage.success({ message: instance?.proxy?.$t('upload.message6') }) + ElMessage.success({ message: instance?.proxy?.$t('upload_page.message6') }) await afterUploadSuccess(uploadedImg, true) break // 上传失败(网络错误等原因) case UploadStatusEnum.uploadFail: - ElMessage.error({ message: instance?.proxy?.$t('upload.message7') }) + ElMessage.error({ message: instance?.proxy?.$t('upload_page.message7') }) } } From ad6d0d5e6e2986fc97f1660718ee8990bbc688b3 Mon Sep 17 00:00:00 2001 From: XPoet Date: Wed, 10 Jan 2024 17:34:45 +0800 Subject: [PATCH 04/32] fix: optimize settings data initialization --- .../image-link-rule-config.vue | 5 ++--- src/stores/modules/user-settings/index.ts | 13 +++++++------ src/stores/modules/user-settings/utils.ts | 10 +++++----- src/utils/common-utils.ts | 14 ++++++-------- 4 files changed, 20 insertions(+), 22 deletions(-) diff --git a/src/components/image-link-rule-config/image-link-rule-config.vue b/src/components/image-link-rule-config/image-link-rule-config.vue index 66075ac1..c079bf74 100644 --- a/src/components/image-link-rule-config/image-link-rule-config.vue +++ b/src/components/image-link-rule-config/image-link-rule-config.vue @@ -106,7 +106,7 @@ const imageLinkRuleForm: ImageLinkRuleModel = reactive({ const editImageLinkRule = (newRule: string, ruleObj: ImageLinkRuleModel) => { const tmpRuleObj = JSON.parse(JSON.stringify(ruleObj)) tmpRuleObj.rule = newRule - store.dispatch('UPDATE_IMAGE_LINK_TYPE_RULE', { rule: tmpRuleObj, $t: instance?.proxy?.$t }) + store.dispatch('UPDATE_IMAGE_LINK_TYPE_RULE', { rule: tmpRuleObj }) } const removeImageLinkRule = (obj: ImageLinkRuleModel) => { @@ -132,8 +132,7 @@ const addImageLinkRule = (formEl: FormInstance | undefined) => { if (valid) { imageLinkRuleForm.id = getUuid() store.dispatch('ADD_IMAGE_LINK_TYPE_RULE', { - rule: JSON.parse(JSON.stringify(imageLinkRuleForm)), - $t: instance?.proxy?.$t + rule: JSON.parse(JSON.stringify(imageLinkRuleForm)) }) } }) diff --git a/src/stores/modules/user-settings/index.ts b/src/stores/modules/user-settings/index.ts index 7a370d9d..4abde342 100644 --- a/src/stores/modules/user-settings/index.ts +++ b/src/stores/modules/user-settings/index.ts @@ -18,6 +18,7 @@ import UserSettingsStateTypes, { import { LS_PICX_SETTINGS } from '@/common/constant' import { DeployServerEnum } from '@/components/deploy-bar/deploy-bar.model' import { imgLinkRuleVerification } from '@/stores/modules/user-settings/utils' +import i18n from '@/plugins/vue/i18n' const initSettings: UserSettingsModel = { imageName: { @@ -34,7 +35,7 @@ const initSettings: UserSettingsModel = { }, elementPlusSize: ElementPlusSizeEnum.default, imageLinkType: { - selected: ImageLinkTypeEnum.jsDelivr, + selected: ImageLinkTypeEnum.GitHub, presetList: { // GitHubPages [`${ImageLinkTypeEnum.GitHubPages}`]: { @@ -134,23 +135,23 @@ const userSettingsModule: Module = { }, // 图片链接类型 - 增加规则 - ADD_IMAGE_LINK_TYPE_RULE({ state, dispatch }, { rule, $t }) { + ADD_IMAGE_LINK_TYPE_RULE({ state, dispatch }, { rule }) { const ruleObjs = state.userSettings.imageLinkType.presetList if (!Object.hasOwn(ruleObjs, rule.name)) { - imgLinkRuleVerification(rule, ImgLinkRuleActionsEnum.add, $t, (e: boolean) => { + imgLinkRuleVerification(rule, ImgLinkRuleActionsEnum.add, (e: boolean) => { if (e) { state.userSettings.imageLinkType.presetList[rule.name] = rule dispatch('USER_SETTINGS_PERSIST') } }) } else { - ElMessage.error($t('settings_page.link_rule.error_msg_1')) + ElMessage.error(i18n.global.t('settings_page.link_rule.error_msg_1')) } }, // 图片链接类型 - 修改规则 - UPDATE_IMAGE_LINK_TYPE_RULE({ state, dispatch }, { rule, $t }) { - imgLinkRuleVerification(rule, ImgLinkRuleActionsEnum.edit, $t, (e: boolean) => { + UPDATE_IMAGE_LINK_TYPE_RULE({ state, dispatch }, { rule }) { + imgLinkRuleVerification(rule, ImgLinkRuleActionsEnum.edit, (e: boolean) => { if (e) { state.userSettings.imageLinkType.presetList[rule.name].rule = rule.rule dispatch('USER_SETTINGS_PERSIST') diff --git a/src/stores/modules/user-settings/utils.ts b/src/stores/modules/user-settings/utils.ts index f3c15dea..8d4b18a8 100644 --- a/src/stores/modules/user-settings/utils.ts +++ b/src/stores/modules/user-settings/utils.ts @@ -1,21 +1,21 @@ import { ImageLinkRuleModel } from '@/common/model' import { ImgLinkRuleActionsEnum } from '@/stores/modules/user-settings/types' +import i18n from '@/plugins/vue/i18n' export const imgLinkRuleVerification = ( rule: ImageLinkRuleModel, type: ImgLinkRuleActionsEnum, - $t: any, callback: any ) => { const typeTxt = type === ImgLinkRuleActionsEnum.add - ? $t('settings_page.link_rule.add') - : $t('settings_page.link_rule.edit') + ? i18n.global.t('settings_page.link_rule.add') + : i18n.global.t('settings_page.link_rule.edit') const tmpList = [] if (!rule.rule.includes('{{path}}')) { ElMessage.error( - $t('settings_page.link_rule.error_msg_2', { action: typeTxt, path: '{{path}}' }) + i18n.global.t('settings_page.link_rule.error_msg_2', { action: typeTxt, path: '{{path}}' }) ) callback(false) return @@ -34,7 +34,7 @@ export const imgLinkRuleVerification = ( } if (tmpList.length) { - const confirmTxt = $t('settings_page.link_rule.error_msg_3', { + const confirmTxt = i18n.global.t('settings_page.link_rule.error_msg_3', { action: typeTxt, rules: tmpList.join('、') }) diff --git a/src/utils/common-utils.ts b/src/utils/common-utils.ts index 2911db5c..1837f959 100644 --- a/src/utils/common-utils.ts +++ b/src/utils/common-utils.ts @@ -72,17 +72,15 @@ export const deepAssignObject = (obj1: object, obj2: object) => { for (const key in obj2) { // @ts-ignore if (getType(obj2[key]) !== 'object') { - if (obj1) { - // @ts-ignore - obj1[key] = obj2[key] - } - } else { // @ts-ignore - // eslint-disable-next-line no-lonely-if - if (obj1[key]) { + obj1[key] = obj2[key] + } else { + if (!Object.hasOwn(obj1, key)) { // @ts-ignore - deepAssignObject(obj1[key], obj2[key]) + obj1[key] = {} } + // @ts-ignore + deepAssignObject(obj1[key], obj2[key]) } } } From 8583b97df70478cee471bdb860b0d97a92bfcee4 Mon Sep 17 00:00:00 2001 From: XPoet Date: Wed, 10 Jan 2024 21:30:40 +0800 Subject: [PATCH 05/32] refactor: refactor deploy status info bar --- src/common/model/user-settings.ts | 11 ------- src/components.d.ts | 3 +- .../cloud-settings-bar/cloud-settings-bar.vue | 2 +- src/components/deploy-bar/deploy-bar.util.ts | 12 -------- .../deploy-status-bar.model.ts} | 0 .../deploy-status-bar.styl} | 0 .../deploy-status-bar.util.ts} | 22 ++++++++++---- .../deploy-status-bar.vue} | 27 ++++++++--------- src/stores/index.ts | 14 ++++----- src/stores/modules/deploy-status/index.ts | 29 +++++++++++++++++++ src/stores/modules/deploy-status/types.ts | 12 ++++++++ src/stores/modules/user-settings/index.ts | 9 ------ src/stores/types.ts | 2 ++ src/views/main-container/main-container.vue | 8 +++-- src/views/my-settings/my-settings.vue | 9 +++--- src/views/upload-image/upload-image.vue | 2 +- 16 files changed, 92 insertions(+), 70 deletions(-) delete mode 100644 src/components/deploy-bar/deploy-bar.util.ts rename src/components/{deploy-bar/deploy-bar.model.ts => deploy-status-bar/deploy-status-bar.model.ts} (100%) rename src/components/{deploy-bar/deploy-bar.styl => deploy-status-bar/deploy-status-bar.styl} (100%) rename src/{views/main-container/main-container.util.ts => components/deploy-status-bar/deploy-status-bar.util.ts} (67%) rename src/components/{deploy-bar/deploy-bar.vue => deploy-status-bar/deploy-status-bar.vue} (78%) create mode 100644 src/stores/modules/deploy-status/index.ts create mode 100644 src/stores/modules/deploy-status/types.ts diff --git a/src/common/model/user-settings.ts b/src/common/model/user-settings.ts index e04785f0..b230b13d 100644 --- a/src/common/model/user-settings.ts +++ b/src/common/model/user-settings.ts @@ -1,5 +1,4 @@ import { CompressEncoderEnum, ImageLinkFormatModel, ImageLinkRuleModel } from '@/common/model' -import { DeployServerEnum } from '@/components/deploy-bar/deploy-bar.model' export enum ElementPlusSizeEnum { // eslint-disable-next-line no-unused-vars @@ -39,13 +38,6 @@ export enum LanguageEnum { en = 'en' } -export interface DeployStatusInfo { - uuid: string - status: boolean | null - latestTime: number | null - type: DeployServerEnum -} - export interface UserSettingsModel { imageName: { autoAddHash: boolean @@ -83,8 +75,5 @@ export interface UserSettingsModel { textColor: string opacity: number } - deploy: { - [key: string]: DeployStatusInfo - } language: LanguageEnum } diff --git a/src/components.d.ts b/src/components.d.ts index a2952cbe..46ed54a1 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -15,7 +15,8 @@ declare module '@vue/runtime-core' { CompressConfigBox: typeof import('./components/compress-config-box/compress-config-box.vue')['default'] CompressTool: typeof import('./components/tools/compress-tool/compress-tool.vue')['default'] CopyImageLink: typeof import('./components/copy-image-link/copy-image-link.vue')['default'] - DeployBar: typeof import('./components/deploy-bar/deploy-bar.vue')['default'] + DeployBar: typeof import('./components/deploy-status-bar/deploy-bar.vue')['default'] + DeployStatusBar: typeof import('./components/deploy-status-bar/deploy-status-bar.vue')['default'] ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb'] ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem'] ElButton: typeof import('element-plus/es')['ElButton'] diff --git a/src/components/cloud-settings-bar/cloud-settings-bar.vue b/src/components/cloud-settings-bar/cloud-settings-bar.vue index a5245f5f..8907e648 100644 --- a/src/components/cloud-settings-bar/cloud-settings-bar.vue +++ b/src/components/cloud-settings-bar/cloud-settings-bar.vue @@ -129,7 +129,7 @@ watch( selectedAction.value = CloudSettingsActions.use } - // 判断 云端设置 和本地设置 是否相等,相等则禁止点击 + // 判断 云端设置 和 本地设置 是否相等,相等则禁止点击 if (settings && deepObjectEqual(settings, userSettings)) { saveDisabled.value = true selectedAction.value = CloudSettingsActions.equal diff --git a/src/components/deploy-bar/deploy-bar.util.ts b/src/components/deploy-bar/deploy-bar.util.ts deleted file mode 100644 index 83356140..00000000 --- a/src/components/deploy-bar/deploy-bar.util.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { DeployServerEnum } from '@/components/deploy-bar/deploy-bar.model' - -export const getDeployServerName = (server: DeployServerEnum) => { - switch (server) { - case DeployServerEnum.githubPages: - return 'GitHub Pages' - case DeployServerEnum.vervel: - return 'Vercel' - default: - return 'GitHub Pages' - } -} diff --git a/src/components/deploy-bar/deploy-bar.model.ts b/src/components/deploy-status-bar/deploy-status-bar.model.ts similarity index 100% rename from src/components/deploy-bar/deploy-bar.model.ts rename to src/components/deploy-status-bar/deploy-status-bar.model.ts diff --git a/src/components/deploy-bar/deploy-bar.styl b/src/components/deploy-status-bar/deploy-status-bar.styl similarity index 100% rename from src/components/deploy-bar/deploy-bar.styl rename to src/components/deploy-status-bar/deploy-status-bar.styl diff --git a/src/views/main-container/main-container.util.ts b/src/components/deploy-status-bar/deploy-status-bar.util.ts similarity index 67% rename from src/views/main-container/main-container.util.ts rename to src/components/deploy-status-bar/deploy-status-bar.util.ts index 3190364d..0d5d219f 100644 --- a/src/views/main-container/main-container.util.ts +++ b/src/components/deploy-status-bar/deploy-status-bar.util.ts @@ -1,10 +1,11 @@ import { computed } from 'vue' +import { DeployServerEnum } from '@/components/deploy-status-bar/deploy-status-bar.model' import request from '@/utils/request' import { PICX_INIT_DEPLOY_MSG, PICX_UPDATE_DEPLOY_MSG } from '@/common/constant' import { store } from '@/stores' -const userSettings = computed(() => store.getters.getUserSettings).value const userConfigInfo = computed(() => store.getters.getUserConfigInfo).value +const deployStatusInfo = computed(() => store.getters.getDeployStatusInfo).value const filename = '.deploy' @@ -36,7 +37,7 @@ export const saveCloudDeployInfo = async () => { const data: any = { message: res ? PICX_UPDATE_DEPLOY_MSG : PICX_INIT_DEPLOY_MSG, - content: window.btoa(JSON.stringify(userSettings.deploy)) + content: window.btoa(JSON.stringify(deployStatusInfo)) } if (res) { @@ -59,8 +60,17 @@ export const saveCloudDeployInfo = async () => { * 设置云端仓库的部署状态到本地 * @param content */ -export const setCloudDeployInfo = (content: string) => { - store.dispatch('SET_USER_SETTINGS', { - deploy: JSON.parse(window.atob(content)) - }) +export const setCloudDeployInfo = async (content: string) => { + await store.dispatch('SET_DEPLOY_STATUS_INFO', JSON.parse(window.atob(content))) +} + +export const getDeployServerName = (server: DeployServerEnum) => { + switch (server) { + case DeployServerEnum.githubPages: + return 'GitHub Pages' + case DeployServerEnum.vervel: + return 'Vercel' + default: + return 'GitHub Pages' + } } diff --git a/src/components/deploy-bar/deploy-bar.vue b/src/components/deploy-status-bar/deploy-status-bar.vue similarity index 78% rename from src/components/deploy-bar/deploy-bar.vue rename to src/components/deploy-status-bar/deploy-status-bar.vue index 17abc775..9e6bd82f 100644 --- a/src/components/deploy-bar/deploy-bar.vue +++ b/src/components/deploy-status-bar/deploy-status-bar.vue @@ -1,10 +1,6 @@ diff --git a/src/components/selected-info-bar/selected-info-bar.model.ts b/src/components/selected-info-bar/selected-info-bar.model.ts deleted file mode 100644 index d1f390cc..00000000 --- a/src/components/selected-info-bar/selected-info-bar.model.ts +++ /dev/null @@ -1,6 +0,0 @@ -export enum SelectedInfoBarType { - // eslint-disable-next-line no-unused-vars - upload, - // eslint-disable-next-line no-unused-vars - management -} diff --git a/src/components/selected-info-bar/selected-info-bar.vue b/src/components/selected-info-bar/selected-info-bar.vue deleted file mode 100644 index 30b4ba93..00000000 --- a/src/components/selected-info-bar/selected-info-bar.vue +++ /dev/null @@ -1,64 +0,0 @@ - - - - - diff --git a/src/components/site-count/site-count.vue b/src/components/site-count/site-count.vue index 1fc8ea7f..5671cce8 100644 --- a/src/components/site-count/site-count.vue +++ b/src/components/site-count/site-count.vue @@ -1,7 +1,11 @@ @@ -20,7 +24,7 @@ defineProps({ }) const siteCountDom: Ref = ref(null) -const isShow: Ref = ref(true) +const isShow: Ref = ref(false) // const isProd = computed(() => import.meta.env.MODE === 'production') @@ -35,28 +39,26 @@ onMounted(() => { siteCountDom.value.appendChild(script) script.onload = () => { - const tempT = setTimeout(() => { + setTimeout(() => { if (getInnerText()) { isShow.value = true } - clearTimeout(tempT) }, 1500) } }) diff --git a/src/components/user-avatar-v2/user-avatar-v2.styl b/src/components/user-avatar-v2/user-avatar-v2.styl new file mode 100644 index 00000000..9e052d88 --- /dev/null +++ b/src/components/user-avatar-v2/user-avatar-v2.styl @@ -0,0 +1,36 @@ +.user-avatar-v2 { + display flex + align-items center + justify-content space-between + cursor pointer + user-select none + + .avatar { + display flex + align-items center + justify-content center + box-sizing border-box + width 38rem + height 38rem + padding 1rem + color var(--text-color) + border 1rem solid var(--text-color) + border-radius 50% + + img { + width 100% + height 100% + border-radius 50% + } + + .user-filled-icon { + font-size 20rem + } + } + + + .popover-tip-icon { + margin-left 6rem + font-size 22rem + } +} diff --git a/src/components/user-avatar-v2/user-avatar-v2.vue b/src/components/user-avatar-v2/user-avatar-v2.vue new file mode 100644 index 00000000..ae291a41 --- /dev/null +++ b/src/components/user-avatar-v2/user-avatar-v2.vue @@ -0,0 +1,135 @@ + + + + + diff --git a/src/components/user-avatar/user-avatar.styl b/src/components/user-avatar/user-avatar.styl index 18e8ecf5..f1595efc 100644 --- a/src/components/user-avatar/user-avatar.styl +++ b/src/components/user-avatar/user-avatar.styl @@ -8,6 +8,7 @@ border 1rem solid var(--border-color) border-radius $box-border-radius cursor pointer + user-select none &.folded { padding 0 diff --git a/src/components/user-avatar/user-avatar.vue b/src/components/user-avatar/user-avatar.vue index 55b6c006..afbb5097 100644 --- a/src/components/user-avatar/user-avatar.vue +++ b/src/components/user-avatar/user-avatar.vue @@ -34,43 +34,20 @@
            -
              -
            • - {{ $t('language') }} - - - - - +
                + - -
              • - {{ $t('header.theme') }} - - - - - -
              • - -
              • + +
              • {{ $t('logout') }}
              • -
              • +
              • {{ $t('login') }}
              @@ -81,22 +58,20 @@ import { computed } from 'vue' import { useRouter } from 'vue-router' import { useStore } from '@/stores' -import { LanguageEnum, ThemeModeEnum } from '@/common/model' import { getGitHubOwnerURL } from '@/utils' const router = useRouter() const store = useStore() const userConfigInfo = computed(() => store.getters.getUserConfigInfo).value -const userSettings = computed(() => store.getters.getUserSettings).value -const globalSettings = computed(() => store.getters.getGlobalSettings).value -const persistUserSettings = () => { - store.dispatch('USER_SETTINGS_PERSIST') +const onLogin = () => { + router.push('/login') + document.body.click() } // 退出登录 -const logout = () => { +const onLogout = () => { store.dispatch('LOGOUT') router.push('/login') document.body.click() diff --git a/src/locales/en.json b/src/locales/en.json index e223e791..f6f8d0a7 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -12,8 +12,8 @@ "shortcut_key": "Shortcut Key", "username": "Username", "email": "Email", - "login": "Login", - "logout": "Logout", + "login": "Log In", + "logout": "Log Out", "language": "Language", "uploaded": "Uploaded", "upload": "Upload", @@ -179,8 +179,6 @@ "loadingTxt1": "Loading...", "loadingTxt2": "Renaming...", "loadingTxt3": "Deleting...", - "back": "Double-click to go back", - "toNextDir": "Double-click to enter the next-level directory", "property": "Properties", "delTips": "This action will permanently delete the image", "delTips2": "Selected {total} images, do you want to delete them in bulk?", @@ -201,7 +199,7 @@ "batchDelete": "Batch Delete Images", "selectTotal": "Selected {total} images", "contextmenu_1": "Upload new images from the current location", - "contextmenu_2": "Upload images to < {dir} >", + "contextmenu_2": "Upload images to {dir}", "contextmenu_3": "Root Directory" }, "toolbox": { @@ -258,7 +256,8 @@ "text_9": "Login with GitHub Token requires generating a Token with operational permissions.", "text_10": "View Tutorial", "text_11": "Install PicX GitHub APP", - "text_12": "Create GitHub Token" + "text_12": "Create GitHub Token", + "text_13": "Authorize now" }, "copy_repo_img": { "text_1": "Copy repository images", diff --git a/src/locales/zh-CN.json b/src/locales/zh-CN.json index 8ab9a96b..67dd3268 100644 --- a/src/locales/zh-CN.json +++ b/src/locales/zh-CN.json @@ -179,8 +179,6 @@ "loadingTxt1": "加载中 ...", "loadingTxt2": "正在重命名 ...", "loadingTxt3": "删除中 ...", - "back": "双击后退", - "toNextDir": "双击进入下一级目录", "property": "属性", "delTips": "此操作将会永久删除图片", "delTips2": "已选中 {total} 张图片,是否批量删除?", @@ -201,7 +199,7 @@ "batchDelete": "批量删除图片", "selectTotal": "已选择 {total} 张图片", "contextmenu_1": "从当前位置上传新图片", - "contextmenu_2": "上传图片到 < {dir} >", + "contextmenu_2": "上传图片到 {dir}", "contextmenu_3": "根目录" }, "toolbox": { @@ -258,7 +256,8 @@ "text_9": "填写 GitHub Token 登录必须生成具有操作权限的 Token", "text_10": "查看教程", "text_11": "安装 PicX GitHub APP", - "text_12": "创建 GitHub Token" + "text_12": "创建 GitHub Token", + "text_13": "立即授权" }, "copy_repo_img": { "text_1": "复制仓库图片", diff --git a/src/locales/zh-TW.json b/src/locales/zh-TW.json index 3be2a7a5..fd9b1232 100644 --- a/src/locales/zh-TW.json +++ b/src/locales/zh-TW.json @@ -179,8 +179,6 @@ "loadingTxt1": "載入中 ...", "loadingTxt2": "正在重新命名 ...", "loadingTxt3": "刪除中 ...", - "back": "雙擊後退", - "toNextDir": "雙擊進入下一級目錄", "property": "屬性", "delTips": "此操作將會永久刪除圖片", "delTips2": "已選取 {total} 張圖片,是否已批次刪除?", @@ -201,7 +199,7 @@ "batchDelete": "批次刪除圖片", "selectTotal": "已選擇 {total} 張圖片", "contextmenu_1": "從目前位置上傳新圖片", - "contextmenu_2": "上傳圖片到 < {dir} >", + "contextmenu_2": "上傳圖片到 {dir}", "contextmenu_3": "根目錄" }, "toolbox": { @@ -258,7 +256,8 @@ "text_9": "填寫 GitHub Token 登入必須產生具有操作權限的 Token", "text_10": "檢視教學", "text_11": "安裝 PicX GitHub APP", - "text_12": "建立 GitHub Token" + "text_12": "建立 GitHub Token", + "text_13": "立即授權" }, "copy_repo_img": { "text_1": "複製倉庫圖片", diff --git a/src/stores/modules/dir-image-list/index.ts b/src/stores/modules/dir-image-list/index.ts index 6d0c2899..09c53018 100644 --- a/src/stores/modules/dir-image-list/index.ts +++ b/src/stores/modules/dir-image-list/index.ts @@ -1,6 +1,6 @@ import { Module } from 'vuex' import { UploadedImageModel } from '@/common/model' -import { LS_PICX_MANAGEMENT } from '@/common/constant' +import { LS_MANAGEMENT } from '@/common/constant' import DirImageListStateTypes, { DirObject } from './types' import RootStateTypes from '../../types' import { @@ -12,7 +12,7 @@ import { getDirContent } from '@/views/imgs-management/imgs-management.util' import { setLocal } from '@/utils' const initDirObject = () => { - const dirObj = localStorage.getItem(LS_PICX_MANAGEMENT) + const dirObj = localStorage.getItem(LS_MANAGEMENT) return dirObj ? JSON.parse(dirObj) : createDirObject('/', '/') } @@ -261,7 +261,7 @@ const dirImageListModule: Module = { // 图床管理 - 持久化存储 DIR_IMAGE_LIST_PERSIST({ state }) { - setLocal(LS_PICX_MANAGEMENT, state.dirObject) + setLocal(LS_MANAGEMENT, state.dirObject) }, // 图床管理 - 退出登录 diff --git a/src/stores/modules/github-authorize/index.ts b/src/stores/modules/github-authorize/index.ts index fbc2da75..06e81223 100644 --- a/src/stores/modules/github-authorize/index.ts +++ b/src/stores/modules/github-authorize/index.ts @@ -2,7 +2,7 @@ import { Module } from 'vuex' import RootStateTypes from '@/stores/types' import GitHubAuthorizeStateTypes, { GitHubAuthorizationInfo } from './types' import { deepAssignObject, getLocal, setLocal } from '@/utils' -import { LS_PICX_AUTHORIZATION } from '@/common/constant' +import { LS_AUTHORIZATION } from '@/common/constant' const initAuthorizationInfo = (): GitHubAuthorizationInfo => { const initInfo: GitHubAuthorizationInfo = { @@ -14,10 +14,11 @@ const initAuthorizationInfo = (): GitHubAuthorizationInfo => { codeCreateTime: 0, installationId: '', manualToken: '', - isAutoAuthorize: false + isAutoAuthorize: false, + authorizing: false } - const LSInfo = getLocal(LS_PICX_AUTHORIZATION) + const LSInfo = getLocal(LS_AUTHORIZATION) if (LSInfo) { deepAssignObject(initInfo, LSInfo) @@ -47,12 +48,12 @@ const githubAuthorizeModule: Module = // 持久化存储 GitHub APP 授权状态信息 GITHUB_AUTHORIZATION_INFO_PERSIST({ state }) { - setLocal(LS_PICX_AUTHORIZATION, state.authorizationInfo) + setLocal(LS_AUTHORIZATION, state.authorizationInfo) } }, getters: { - getGitHubAuthorizationInfo: (state): GitHubAuthorizationInfo => state.authorizationInfo + getGitHubAuthorizationInfo: (state) => state.authorizationInfo } } diff --git a/src/stores/modules/github-authorize/types.ts b/src/stores/modules/github-authorize/types.ts index d80f2936..06788c7d 100644 --- a/src/stores/modules/github-authorize/types.ts +++ b/src/stores/modules/github-authorize/types.ts @@ -8,6 +8,7 @@ export interface GitHubAuthorizationInfo { installationId: string manualToken: string isAutoAuthorize: boolean + authorizing: boolean } export default interface GitHubAuthorizeStateTypes { diff --git a/src/stores/modules/user-config-info/index.ts b/src/stores/modules/user-config-info/index.ts index a88e360f..60a5a91d 100644 --- a/src/stores/modules/user-config-info/index.ts +++ b/src/stores/modules/user-config-info/index.ts @@ -3,7 +3,7 @@ import { UserConfigInfoModel, DirModeEnum } from '@/common/model' import { deepAssignObject, cleanObject, formatDatetime } from '@/utils' import UserConfigInfoStateTypes from '@/stores/modules/user-config-info/types' import RootStateTypes from '@/stores/types' -import { LS_PICX_CONFIG, NEW_DIR_COUNT_MAX } from '@/common/constant' +import { LS_CONFIG, NEW_DIR_COUNT_MAX } from '@/common/constant' const initUserConfigInfo = (): UserConfigInfoModel => { const initConfig: UserConfigInfoModel = { @@ -24,7 +24,7 @@ const initUserConfigInfo = (): UserConfigInfoModel => { repoPrivate: false } - const LSConfig: string | null = localStorage.getItem(LS_PICX_CONFIG) + const LSConfig: string | null = localStorage.getItem(LS_CONFIG) if (LSConfig) { // Assign: oldConfig -> initConfig @@ -110,7 +110,7 @@ const userConfigInfoModule: Module = { // 持久化用户配置信息 USER_CONFIG_INFO_PERSIST({ state }) { convertSpecialCharacter(state) - localStorage.setItem(LS_PICX_CONFIG, JSON.stringify(state.userConfigInfo)) + localStorage.setItem(LS_CONFIG, JSON.stringify(state.userConfigInfo)) }, // 退出登录 diff --git a/src/stores/modules/user-settings/index.ts b/src/stores/modules/user-settings/index.ts index b17d3b9b..af651c07 100644 --- a/src/stores/modules/user-settings/index.ts +++ b/src/stores/modules/user-settings/index.ts @@ -9,12 +9,13 @@ import { UserSettingsModel, WatermarkPositionEnum } from '@/common/model' -import { deepAssignObject, getLocal, getUuid } from '@/utils' +import { deepAssignObject, getLocal, getSession, getUuid, setLocal, setSession } from '@/utils' import RootStateTypes from '@/stores/types' import UserSettingsStateTypes, { + GlobalSettingsModel, ImgLinkRuleActionsEnum } from '@/stores/modules/user-settings/types' -import { LS_PICX_SETTINGS } from '@/common/constant' +import { LS_SETTINGS, SS_GLOBAL_SETTINGS } from '@/common/constant' import { imgLinkRuleVerification } from '@/stores/modules/user-settings/utils' import i18n from '@/plugins/vue/i18n' @@ -95,22 +96,33 @@ const initSettings: UserSettingsModel = { } const initUserSettings = (): UserSettingsModel => { - const LSSettings = getLocal(LS_PICX_SETTINGS) + const LSSettings = getLocal(LS_SETTINGS) if (LSSettings) { deepAssignObject(initSettings, LSSettings) } return initSettings } +const initGlobalSettings = (): GlobalSettingsModel => { + const globalSettings: GlobalSettingsModel = { + folded: false, + elementPlusSize: ElementPlusSizeEnum.default, + language: LanguageEnum.zhCN, + languageToggleTip: true + } + + const SSSettings = getSession(SS_GLOBAL_SETTINGS) + if (SSSettings) { + deepAssignObject(globalSettings, SSSettings) + } + return globalSettings +} + const userSettingsModule: Module = { state: { userSettings: initUserSettings(), cloudSettings: null, - globalSettings: { - folded: false, - elementPlusSize: ElementPlusSizeEnum.default, - language: LanguageEnum.zhCN - } + globalSettings: initGlobalSettings() }, actions: { @@ -138,6 +150,7 @@ const userSettingsModule: Module = { // @ts-ignore state.globalSettings[key] = globalSettings[key] } + setSession(SS_GLOBAL_SETTINGS, state.globalSettings) }, // 图片链接类型 - 增加规则 @@ -171,9 +184,14 @@ const userSettingsModule: Module = { dispatch('USER_SETTINGS_PERSIST') }, - // 持久化 + // 持久化用户设置数据 USER_SETTINGS_PERSIST({ state }) { - localStorage.setItem(LS_PICX_SETTINGS, JSON.stringify(state.userSettings)) + setLocal(LS_SETTINGS, state.userSettings) + }, + + // 持久化全局设置数据 + USER_GLOBAL_PERSIST({ state }) { + setSession(SS_GLOBAL_SETTINGS, state.globalSettings) }, // 退出登录 @@ -183,7 +201,7 @@ const userSettingsModule: Module = { }, getters: { - getUserSettings: (state): UserSettingsModel => state.userSettings, + getUserSettings: (state) => state.userSettings, getCloudSettings: (state) => state.cloudSettings, getGlobalSettings: (state) => state.globalSettings } diff --git a/src/stores/modules/user-settings/types.ts b/src/stores/modules/user-settings/types.ts index da97b0e5..eaa06572 100644 --- a/src/stores/modules/user-settings/types.ts +++ b/src/stores/modules/user-settings/types.ts @@ -1,4 +1,4 @@ -import { ElementPlusSizeEnum, LanguageEnum, UserSettingsModel } from '@/common/model' +import { ElementPlusSizeEnum, LanguageEnum, ThemeModeEnum, UserSettingsModel } from '@/common/model' export enum ImgLinkRuleActionsEnum { // eslint-disable-next-line no-unused-vars @@ -11,6 +11,10 @@ export interface GlobalSettingsModel { folded: boolean elementPlusSize: ElementPlusSizeEnum language: LanguageEnum + languageToggleTip: boolean + theme?: { + mode: ThemeModeEnum + } } export default interface UserSettingsStateTypes { diff --git a/src/styles/base.styl b/src/styles/base.styl index 72650770..b0a5d28a 100644 --- a/src/styles/base.styl +++ b/src/styles/base.styl @@ -83,6 +83,12 @@ li { justify-content flex-start } +.flex-end { + display flex + align-items center + justify-content flex-end +} + .border-box { position relative @@ -168,28 +174,66 @@ li { .personal-center-popover { - position relative - box-sizing border-box width 100% padding 10rem 0 background var(--background-color) + .user-info { + width 100% + padding 6rem 12rem + + .info-item { + margin-bottom 2rem + } + + .owner { + color var(--text-color-3) + font-size 16rem + } + + .name { + color var(--text-color-4) + font-size 13rem + } + } + + .content-item { - position relative display flex align-items center justify-content space-between - box-sizing border-box width 100% - height 36rem - padding 0 12rem + padding 8rem 12rem color var(--text-color-3) font-size 14rem cursor pointer - .el-icon { - margin-right 3rem + &:hover { + background var(--background-color-2) } + } + + .el-divider { + margin 0 + } +} + + +.global-settings-popover { + width 100% + padding 10rem 0 + background var(--background-color) + + .content-item { + display flex + align-items center + justify-content space-between + width 100% + height 36rem + padding 0 12rem + color var(--text-color-3) + font-size 14rem + cursor pointer &:hover { background var(--background-color-2) diff --git a/src/styles/variables.styl b/src/styles/variables.styl index d2292a38..53a1ee62 100644 --- a/src/styles/variables.styl +++ b/src/styles/variables.styl @@ -57,7 +57,7 @@ $z-index-9 = 1009 // light mode color // ======================================================================================== $background-color = #fff -$background-color-2 = darken($background-color, 5%) +$background-color-2 = #f6f8fb $background-color-3 = darken($background-color, 10%) $text-color = #50505c diff --git a/src/utils/request/axios.ts b/src/utils/request/axios.ts index a2393cb8..f01b8c88 100644 --- a/src/utils/request/axios.ts +++ b/src/utils/request/axios.ts @@ -1,5 +1,5 @@ import Axios from 'axios' -import { LS_PICX_CONFIG, AXIOS_BASE_URL, AXIOS_TIMEOUT } from '@/common/constant' +import { LS_CONFIG, AXIOS_BASE_URL, AXIOS_TIMEOUT } from '@/common/constant' import { getLocal } from '@/utils/storage' const baseURL = AXIOS_BASE_URL @@ -14,7 +14,7 @@ axios.defaults.headers['Content-Type'] = 'application/json' // 发起请求之前的拦截器(前置拦截) axios.interceptors.request.use( (config) => { - const userConfig = getLocal(LS_PICX_CONFIG) + const userConfig = getLocal(LS_CONFIG) if (userConfig) { const { token } = userConfig if (config.baseURL?.includes(baseURL) && token) { diff --git a/src/views/app-wrap/app-wrap.vue b/src/views/app-wrap/app-wrap.vue index 6d062dda..0f248e6a 100644 --- a/src/views/app-wrap/app-wrap.vue +++ b/src/views/app-wrap/app-wrap.vue @@ -12,19 +12,11 @@ import zhTW from 'element-plus/lib/locale/lang/zh-tw' import en from 'element-plus/lib/locale/lang/en' import setThemeMode from '@/utils/set-theme-mode' import { useStore } from '@/stores' -import { - getLanguageByRegion, - getRegionByIP, - getSession, - setSession, - setWindowTitle, - throttle -} from '@/utils' +import { getLanguageByRegion, getRegionByIP, setWindowTitle, throttle } from '@/utils' import { ElementPlusSizeEnum, LanguageEnum } from '@/common/model' import MainContainer from '@/views/main-container/main-container.vue' import router from '@/router' import { initGithubAuthorize } from '@/views/picx-login/picx-login.util' -import { SS_PICX_LANG_TOGGLE_TIP } from '@/common/constant' const instance = getCurrentInstance() const store = useStore() @@ -35,17 +27,20 @@ const elementPlusLocale = ref(zhCN) // zhCN | zhTW | en const elementPlusSizeHandle = (width: number) => { if (width <= 700) { store?.dispatch('SET_GLOBAL_SETTINGS', { - elementPlusSize: ElementPlusSizeEnum.small + elementPlusSize: ElementPlusSizeEnum.small, + folded: true }) elementPlusSize.value = ElementPlusSizeEnum.small } else if (width <= 1000) { store?.dispatch('SET_GLOBAL_SETTINGS', { - elementPlusSize: ElementPlusSizeEnum.default + elementPlusSize: ElementPlusSizeEnum.default, + folded: false }) elementPlusSize.value = ElementPlusSizeEnum.default } else { store?.dispatch('SET_GLOBAL_SETTINGS', { - elementPlusSize: ElementPlusSizeEnum.large + elementPlusSize: ElementPlusSizeEnum.large, + folded: false }) elementPlusSize.value = ElementPlusSizeEnum.large } @@ -69,7 +64,7 @@ const setLanguage = (language: LanguageEnum) => { } const setLanguageByIP = () => { - if (getSession(SS_PICX_LANG_TOGGLE_TIP) === false) { + if (!store.getters.getGlobalSettings.languageToggleTip) { return } @@ -97,7 +92,9 @@ const setLanguageByIP = () => { dangerouslyUseHTMLString: true, showClose: true, onClose() { - setSession(SS_PICX_LANG_TOGGLE_TIP, false) + store.dispatch('SET_GLOBAL_SETTINGS', { + languageToggleTip: false + }) } }) diff --git a/src/components/folder-card/folder-card.styl b/src/views/imgs-management/components/folder-card/folder-card.styl similarity index 100% rename from src/components/folder-card/folder-card.styl rename to src/views/imgs-management/components/folder-card/folder-card.styl diff --git a/src/views/imgs-management/components/folder-card/folder-card.vue b/src/views/imgs-management/components/folder-card/folder-card.vue new file mode 100644 index 00000000..854527a6 --- /dev/null +++ b/src/views/imgs-management/components/folder-card/folder-card.vue @@ -0,0 +1,51 @@ + + + + + diff --git a/src/views/imgs-management/components/tools-bar/tools-bar.styl b/src/views/imgs-management/components/tools-bar/tools-bar.styl new file mode 100644 index 00000000..a53194d0 --- /dev/null +++ b/src/views/imgs-management/components/tools-bar/tools-bar.styl @@ -0,0 +1,29 @@ +.tools-bar { + display flex + align-items center + justify-content space-between + width 100% + height 100% + + .left { + .dir-info { + margin-left 2rem + + .el-icon { + margin-right 4rem + color var(--text-color-3) + font-size 16rem + } + } + } + + + .right { + .el-icon { + margin-left 10rem + color var(--text-color-3) + font-size 22rem + cursor pointer + } + } +} diff --git a/src/views/imgs-management/components/tools-bar/tools-bar.vue b/src/views/imgs-management/components/tools-bar/tools-bar.vue new file mode 100644 index 00000000..bf0f7282 --- /dev/null +++ b/src/views/imgs-management/components/tools-bar/tools-bar.vue @@ -0,0 +1,63 @@ + + + + + diff --git a/src/views/imgs-management/imgs-management.styl b/src/views/imgs-management/imgs-management.styl index 2c77bb5d..f2e8a315 100644 --- a/src/views/imgs-management/imgs-management.styl +++ b/src/views/imgs-management/imgs-management.styl @@ -1,72 +1,56 @@ -$infoBarHeight = 50rem +$top-box-height = 50rem .management-page-container { + display flex + flex-direction column + justify-content space-between + padding-top 10rem !important padding-bottom 0 !important - .content-container { - position relative - box-sizing border-box + .top-box { + display flex + align-items center + justify-content space-between width 100% - height 100% - padding-top $infoBarHeight + height $top-box-height - .top { - position absolute - top 0 - left 0 - display flex - align-items center - justify-content space-between - box-sizing border-box - width 100% - height $infoBarHeight - padding-bottom 20rem - font-size 14rem - .right { - .btn-icon { - margin-left 10rem - color var(--text-color-3) - font-size 22rem - cursor pointer - } + .right { + .btn-icon { + margin-left 10rem + color var(--text-color-3) + font-size 22rem + cursor pointer } } + } - .bottom { - position relative - box-sizing border-box - width 100% - height 100% - border 1rem solid var(--border-color) - border-bottom none - border-top-left-radius 6rem - border-top-right-radius 6rem + .bottom-box { + width 100% + height 'calc(100% - %s)' % $top-box-height + border 1rem solid var(--border-color) + border-bottom none + .image-management-list { + display flex + flex-wrap wrap + align-content flex-start + width 100% + padding 2rem + overflow-y auto - .image-management-list { + li.image-management-item { position relative - display flex - flex-wrap wrap - align-content flex-start box-sizing border-box - width 100% - padding 2rem - overflow-y auto + padding 10rem - li.image-management-item { - position relative - box-sizing border-box - padding 10rem - - &.image { - width 230rem - } + &.image { + width 230rem + } - &:last-child { - margin-right 0 - } + &:last-child { + margin-right 0 } } } diff --git a/src/views/imgs-management/imgs-management.vue b/src/views/imgs-management/imgs-management.vue index 31f5a126..be80191d 100644 --- a/src/views/imgs-management/imgs-management.vue +++ b/src/views/imgs-management/imgs-management.vue @@ -1,65 +1,44 @@ @@ -72,11 +51,10 @@ import { getRepoPathContent } from '@/common/api' import { filterDirContent, getDirContent } from '@/views/imgs-management/imgs-management.util' import ImageCard from '@/components/image-card/image-card.vue' -import SelectedInfoBar from '@/components/selected-info-bar/selected-info-bar.vue' -import FolderCard from '@/components/folder-card/folder-card.vue' import ImageSelector from '@/components/image-selector/image-selector.vue' import { ContextmenuEnum, DirModeEnum, UploadedImageModel } from '@/common/model' -import { SelectedInfoBarType } from '@/components/selected-info-bar/selected-info-bar.model' +import ToolsBar from '@/views/imgs-management/components/tools-bar/tools-bar.vue' +import FolderCard from '@/views/imgs-management/components/folder-card/folder-card.vue' const store = useStore() const router = useRouter() diff --git a/src/views/main-container/main-container.styl b/src/views/main-container/main-container.styl index ba4c9c3c..eb9b9d0f 100644 --- a/src/views/main-container/main-container.styl +++ b/src/views/main-container/main-container.styl @@ -1,3 +1,6 @@ +$layout-padding = $left-side-padding * 0.8 +$header-margin-bottom = $left-side-padding * 0.5 + .main-container { position absolute display flex @@ -12,6 +15,7 @@ flex-shrink 0 width 100% height $header-height + margin-bottom $header-margin-bottom } @@ -19,7 +23,7 @@ display flex justify-content space-between width 100% - height 'calc(100% - %s)' % $header-height + height 'calc(100% - %s)' % ($header-height + $header-margin-bottom) .bottom-left-box { @@ -36,7 +40,7 @@ .bottom-right-box { width 100% height 100% - padding-right $left-side-padding + padding-right $layout-padding } } } diff --git a/src/views/picx-login/picx-login.util.ts b/src/views/picx-login/picx-login.util.ts index 32a50498..190dfa5d 100644 --- a/src/views/picx-login/picx-login.util.ts +++ b/src/views/picx-login/picx-login.util.ts @@ -122,21 +122,43 @@ export const initGithubAuthorize = async () => { () => store.getters.getGitHubAuthorizationInfo ).value - const tmpGoLogin = async () => { - await router.push({ path: '/login', query: { jump: '0' } }) + const goLoginPage = async (cb?: any) => { + router.push({ path: '/login', query: { jump: '0' } }).then(() => { + // eslint-disable-next-line no-unused-expressions + cb && cb() + }) } if (isAutoAuthorize && authorized && installed) { if (token && isAuthorizeExpire()) { - ElMessage.error({ - message: i18n.global.t('authorization.msg_1'), + const msgInstance = ElMessage.error({ + customClass: 'custom-message-container', duration: 0, showClose: true, + message: `
              + ${i18n.global.t('authorization.msg_1')} + + ${i18n.global.t('authorization.text_13')} + +
              `, + dangerouslyUseHTMLString: true, onClose: () => { - tmpGoLogin() + goLoginPage() } }) - await tmpGoLogin() + + document + .querySelector('.custom-message-container .authorization .confirm') + ?.addEventListener('click', () => { + msgInstance.close() + goLoginPage(() => { + store.dispatch('SET_GITHUB_AUTHORIZATION_INFO', { + authorizing: true + }) + }) + }) + + await goLoginPage() } } } diff --git a/src/views/picx-login/picx-login.vue b/src/views/picx-login/picx-login.vue index 39968fbb..3ca09bf4 100644 --- a/src/views/picx-login/picx-login.vue +++ b/src/views/picx-login/picx-login.vue @@ -45,7 +45,7 @@ diff --git a/src/views/upload-image/upload-image.vue b/src/views/upload-image/upload-image.vue index fda88630..8755e9d1 100644 --- a/src/views/upload-image/upload-image.vue +++ b/src/views/upload-image/upload-image.vue @@ -22,7 +22,7 @@
              - + {{ $t('uploaded') }}:{{ uploadImageList.filter((x) => x.uploadStatus.progress === 100).length @@ -68,8 +68,8 @@ import { import { batchCopyImageLinks, copyImageLink, getOSName } from '@/utils' import { generateUploadImageObject, starred } from './upload-image.util' import { uploadImagesToGitHub, uploadImageToGitHub } from '@/utils/upload-utils' -import { SelectedInfoBarType } from '@/components/selected-info-bar/selected-info-bar.model' import UploadImageCard from './components/upload-image-card/upload-image-card.vue' +import SelectedInfoBar from '@/views/upload-image/components/dir-info-bar/dir-info-bar.vue' const instance = getCurrentInstance() From ad3507dff0086dd11d039ad229e98d039bbdc8cc Mon Sep 17 00:00:00 2001 From: XPoet Date: Mon, 22 Jan 2024 14:40:20 +0800 Subject: [PATCH 18/32] feat: add site announcement --- src/common/model/user-settings.ts | 1 + src/components.d.ts | 3 + .../header-content/header-content.styl | 4 +- .../header-content/header-content.vue | 6 +- .../site-announcement/site-announcement.styl | 25 +++++++ .../site-announcement/site-announcement.vue | 66 +++++++++++++++++++ src/locales/en.json | 7 +- src/locales/zh-CN.json | 8 ++- src/locales/zh-TW.json | 8 ++- src/stores/modules/user-settings/index.ts | 4 +- src/stores/modules/user-settings/types.ts | 1 + 11 files changed, 126 insertions(+), 7 deletions(-) create mode 100644 src/components/site-announcement/site-announcement.styl create mode 100644 src/components/site-announcement/site-announcement.vue diff --git a/src/common/model/user-settings.ts b/src/common/model/user-settings.ts index 204d0548..a00aaeed 100644 --- a/src/common/model/user-settings.ts +++ b/src/common/model/user-settings.ts @@ -73,4 +73,5 @@ export interface UserSettingsModel { textColor: string opacity: number } + showAnnouncement?: boolean } diff --git a/src/components.d.ts b/src/components.d.ts index 94697c53..affaeebd 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -19,6 +19,7 @@ declare module '@vue/runtime-core' { DeployBar: typeof import('./components/deploy-status-bar/deploy-bar.vue')['default'] DeployStatusBar: typeof import('./components/deploy-status-bar/deploy-status-bar.vue')['default'] ElAlert: typeof import('element-plus/es')['ElAlert'] + ElBadge: typeof import('element-plus/es')['ElBadge'] ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb'] ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem'] ElButton: typeof import('element-plus/es')['ElButton'] @@ -66,6 +67,7 @@ declare module '@vue/runtime-core' { IEpAim: typeof import('~icons/ep/aim')['default'] IEpArrowLeftBold: typeof import('~icons/ep/arrow-left-bold')['default'] IEpArrowRightBold: typeof import('~icons/ep/arrow-right-bold')['default'] + IEpBell: typeof import('~icons/ep/bell')['default'] IEpCaretBottom: typeof import('~icons/ep/caret-bottom')['default'] IEpCaretLeft: typeof import('~icons/ep/caret-left')['default'] IEpCaretRight: typeof import('~icons/ep/caret-right')['default'] @@ -106,6 +108,7 @@ declare module '@vue/runtime-core' { RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] SelectedInfoBar: typeof import('./components/selected-info-bar/selected-info-bar.vue')['default'] + SiteAnnouncement: typeof import('./components/site-announcement/site-announcement.vue')['default'] SiteCount: typeof import('./components/site-count/site-count.vue')['default'] UserAvatar: typeof import('./components/user-avatar/user-avatar.vue')['default'] UserAvatarV2: typeof import('./components/user-avatar-v2/user-avatar-v2.vue')['default'] diff --git a/src/components/header-content/header-content.styl b/src/components/header-content/header-content.styl index f9f4bd0c..a5632456 100644 --- a/src/components/header-content/header-content.styl +++ b/src/components/header-content/header-content.styl @@ -53,8 +53,8 @@ align-items center justify-content flex-end - .btn-item { - margin-left 10rem + .avatar { + margin-left 6rem } } } diff --git a/src/components/header-content/header-content.vue b/src/components/header-content/header-content.vue index 364fd93c..444e28ab 100644 --- a/src/components/header-content/header-content.vue +++ b/src/components/header-content/header-content.vue @@ -10,10 +10,13 @@
              +
              + +
              -
              +
              @@ -24,6 +27,7 @@ import { computed } from 'vue' import { store } from '@/stores' import router from '@/router' +import SiteAnnouncement from '@/components/site-announcement/site-announcement.vue' const userConfigInfo = computed(() => store.getters.getUserConfigInfo) diff --git a/src/components/site-announcement/site-announcement.styl b/src/components/site-announcement/site-announcement.styl new file mode 100644 index 00000000..d1d1a1fc --- /dev/null +++ b/src/components/site-announcement/site-announcement.styl @@ -0,0 +1,25 @@ +$dot-size = 8rem + +.site-announcement-btn { + &.dot { + &::before { + position absolute + top 3rem + right 3rem + width $dot-size + height $dot-size + background var(--el-color-danger) + border-radius 50% + content '' + } + } +} + +.announcement-item { + margin-bottom 10rem + + + &:last-child { + margin-bottom 0 + } +} diff --git a/src/components/site-announcement/site-announcement.vue b/src/components/site-announcement/site-announcement.vue new file mode 100644 index 00000000..d1f87788 --- /dev/null +++ b/src/components/site-announcement/site-announcement.vue @@ -0,0 +1,66 @@ + + + + + diff --git a/src/locales/en.json b/src/locales/en.json index f6f8d0a7..36e24417 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -25,7 +25,12 @@ "header": { "not_login": "Not log in", "theme": "Theme", - "usage_count": "Usage Count" + "announcement": { + "text_1": "Website Announcement", + "text_2": "1. PicX v3.0 further simplifies user operations and uniformly uses PicX's built-in repository and branch", + "text_3": "2. If you need to continue to use customized repository and branch, please use", + "text_4": "Don't remind again" + } }, "nav": { "config": "Image Hosting Configuration", diff --git a/src/locales/zh-CN.json b/src/locales/zh-CN.json index 67dd3268..dcf62182 100644 --- a/src/locales/zh-CN.json +++ b/src/locales/zh-CN.json @@ -25,7 +25,13 @@ "header": { "not_login": "未登录", "theme": "主题", - "usage_count": "使用次数" + "usage_count": "使用次数", + "announcement": { + "text_1": "网站公告", + "text_2": "1、PicX v3.0 进一步简化用户操作,统一使用 PicX 内置的仓库和分支", + "text_3": "2、如果你需要继续使用自定义的仓库和分支,请使用", + "text_4": "不再提示" + } }, "nav": { "config": "图床配置", diff --git a/src/locales/zh-TW.json b/src/locales/zh-TW.json index fd9b1232..8fff9d6f 100644 --- a/src/locales/zh-TW.json +++ b/src/locales/zh-TW.json @@ -25,7 +25,13 @@ "header": { "not_login": "未登入", "theme": "主題", - "usage_count": "使用次數" + "usage_count": "使用次數", + "announcement": { + "text_1": "網站公告", + "text_2": "1、PicX v3.0 進一步簡化使用者操作,統一使用 PicX 內建的倉庫和分支", + "text_3": "2、如果你需要繼續使用自訂的倉庫和分支,請使用", + "text_4": "不再提示" + } }, "nav": { "config": "圖床配置", diff --git a/src/stores/modules/user-settings/index.ts b/src/stores/modules/user-settings/index.ts index af651c07..78d40228 100644 --- a/src/stores/modules/user-settings/index.ts +++ b/src/stores/modules/user-settings/index.ts @@ -92,7 +92,8 @@ const initSettings: UserSettingsModel = { position: WatermarkPositionEnum.rightBottom, textColor: '#FFFFFF', opacity: 0.5 - } + }, + showAnnouncement: true } const initUserSettings = (): UserSettingsModel => { @@ -105,6 +106,7 @@ const initUserSettings = (): UserSettingsModel => { const initGlobalSettings = (): GlobalSettingsModel => { const globalSettings: GlobalSettingsModel = { + showAnnouncement: true, folded: false, elementPlusSize: ElementPlusSizeEnum.default, language: LanguageEnum.zhCN, diff --git a/src/stores/modules/user-settings/types.ts b/src/stores/modules/user-settings/types.ts index eaa06572..9f5e7def 100644 --- a/src/stores/modules/user-settings/types.ts +++ b/src/stores/modules/user-settings/types.ts @@ -15,6 +15,7 @@ export interface GlobalSettingsModel { theme?: { mode: ThemeModeEnum } + showAnnouncement: boolean } export default interface UserSettingsStateTypes { From af794b0ce1a50ae399f6568c6fe09f78e1ba1f7e Mon Sep 17 00:00:00 2001 From: XPoet Date: Mon, 22 Jan 2024 23:29:24 +0800 Subject: [PATCH 19/32] perf: optimize contextmenu directive --- src/common/directive/contextmenu.ts | 74 ++++++++++-------- src/common/directive/get-dir-sha.ts | 2 +- src/common/directive/remove-dir.ts | 7 -- src/common/directive/rename-dir.ts | 75 ------------------- .../contextmenu.ts => directive/types.ts} | 8 +- src/common/model/index.ts | 1 - .../getting-images/getting-images.vue | 9 ++- src/locales/en.json | 1 + src/locales/zh-CN.json | 1 + src/locales/zh-TW.json | 1 + src/stores/index.ts | 4 +- .../modules/upload-area-active/index.ts | 24 ------ .../modules/upload-area-active/types.ts | 3 - src/stores/modules/upload-area/index.ts | 28 +++++++ src/stores/modules/upload-area/types.ts | 4 + src/stores/types.ts | 4 +- src/styles/base.styl | 18 +---- src/views/imgs-management/imgs-management.vue | 7 +- 18 files changed, 103 insertions(+), 168 deletions(-) delete mode 100644 src/common/directive/remove-dir.ts delete mode 100644 src/common/directive/rename-dir.ts rename src/common/{model/contextmenu.ts => directive/types.ts} (66%) delete mode 100644 src/stores/modules/upload-area-active/index.ts delete mode 100644 src/stores/modules/upload-area-active/types.ts create mode 100644 src/stores/modules/upload-area/index.ts create mode 100644 src/stores/modules/upload-area/types.ts diff --git a/src/common/directive/contextmenu.ts b/src/common/directive/contextmenu.ts index 81e91869..01fbbbc1 100644 --- a/src/common/directive/contextmenu.ts +++ b/src/common/directive/contextmenu.ts @@ -1,16 +1,16 @@ import { computed, Directive } from 'vue' import router from '@/router' import { store } from '@/stores' -import { ContextmenuEnum, DirModeEnum } from '@/common/model' +import { ContextmenuEnum } from './types' +import { DirModeEnum } from '@/common/model' import { copyImageLink } from '@/utils' import i18n from '@/plugins/vue/i18n' -import { renameFolder } from '@/common/directive/rename-dir' -import { removeDir } from '@/common/directive/remove-dir' const menuClass = 'custom-contextmenu-container' let menuEle: any = null let isAddEventListenerOfContextmenu: boolean = false +// 右键菜单指令 const contextmenuDirective: Directive = { mounted(el: any, binding: any) { el.addEventListener('contextmenu', (e: any) => { @@ -30,9 +30,13 @@ const contextmenuDirective: Directive = { menuEle.setAttribute('class', menuClass) menuEle.style.position = 'fixed' menuEle.style.zIndex = '1000' - menuEle.innerHTML = `
            • + menuEle.innerHTML = `
            • +
            • +
            • + ${i18n.global.t('paste_image')} +
            • - ${i18n.global.t('upload.rename')} + ${i18n.global.t('rename')}
            • ${i18n.global.t('delete')} @@ -43,32 +47,42 @@ const contextmenuDirective: Directive = { document.body.appendChild(menuEle) } - const uploadItem = menuEle?.querySelector('.upload-image') const copyItem = menuEle?.querySelector('.copy-link') + const uploadItem = menuEle?.querySelector('.upload-image') const renameItem = menuEle?.querySelector('.rename-dir') const removeItem = menuEle?.querySelector('.remove-dir') + const pasteItem = menuEle?.querySelector('.paste-image') + // 图片 if (type === ContextmenuEnum.img) { - copyItem.style.display = 'block' - uploadItem.innerHTML = i18n.global.t('management.contextmenu_1') + copyItem.style.display = 'flex' + uploadItem.style.display = 'flex' + uploadItem.innerHTML = i18n.global.t('management_page.contextmenu_1') } - if (type === ContextmenuEnum.parentDir) { - copyItem.style.display = 'none' - uploadItem.innerHTML = i18n.global.t('management.contextmenu_2', { - dir: selectedDir === '/' ? i18n.global.t('management.contextmenu_3') : selectedDir + // 目录 + if (type === ContextmenuEnum.dir) { + // renameItem.style.display = 'flex' + // removeItem.style.display = 'flex' + uploadItem.style.display = 'flex' + uploadItem.innerHTML = i18n.global.t('management_page.contextmenu_2', { + dir: selectedDir }) } - if (type === ContextmenuEnum.childDir) { - copyItem.style.display = 'none' - renameItem.style.display = 'block' - removeItem.style.display = 'block' - uploadItem.innerHTML = i18n.global.t('management.contextmenu_2', { - dir: selectedDir + // 目录区域 + if (type === ContextmenuEnum.dirArea) { + uploadItem.style.display = 'flex' + uploadItem.innerHTML = i18n.global.t('management_page.contextmenu_2', { + dir: selectedDir === '/' ? i18n.global.t('management_page.contextmenu_3') : selectedDir }) } + // 上传区域 + if (type === ContextmenuEnum.uploadArea) { + pasteItem.style.display = 'flex' + } + let setLeft = e.clientX let setTop = e.clientY const menuWidth = menuEle.clientWidth @@ -112,7 +126,7 @@ const contextmenuDirective: Directive = { // 重命名目录 renameItem?.addEventListener('click', async () => { - ElMessageBox.prompt('请输入新的名称', i18n.global.t('tips'), { + ElMessageBox.prompt(i18n.global.t('config_page.message_7'), i18n.global.t('tip'), { confirmButtonText: i18n.global.t('confirm'), cancelButtonText: i18n.global.t('cancel') }).then(async ({ value }) => { @@ -120,22 +134,22 @@ const contextmenuDirective: Directive = { return } - const { - owner, - selectedRepo: repo, - token, - selectedBranch: branch - } = computed(() => store.getters.getUserConfigInfo).value - - const res2 = await renameFolder(owner, repo, selectedDir, value, token, branch) - console.log('res2 : ', res2) + // TODO + console.log('new dir: ', value) }) }) // 删除目录 removeItem?.addEventListener('click', async () => { - const userConfigInfo = computed(() => store.getters.getUserConfigInfo).value - await removeDir(userConfigInfo, selectedDir) + // TODO + console.log('删除目录') + }) + + // 上传区域粘贴图片 + pasteItem?.addEventListener('click', () => { + store.commit('SET_UPLOAD_AREA_STATE', { + isPaste: true + }) }) } diff --git a/src/common/directive/get-dir-sha.ts b/src/common/directive/get-dir-sha.ts index 6836faca..19c85c00 100644 --- a/src/common/directive/get-dir-sha.ts +++ b/src/common/directive/get-dir-sha.ts @@ -4,7 +4,7 @@ import request from '@/utils/request' // eslint-disable-next-line consistent-return export async function getDirSha(userConfigInfo: UserConfigInfoModel, path: string) { - const { owner, selectedRepo: repo, token, selectedBranch: branch } = userConfigInfo + const { owner, repo, token, branch } = userConfigInfo try { // 设置 axios 请求头,使用 GitHub Token 进行身份验证 axios.defaults.headers.common.Authorization = `Bearer ${token}` diff --git a/src/common/directive/remove-dir.ts b/src/common/directive/remove-dir.ts deleted file mode 100644 index 8da89435..00000000 --- a/src/common/directive/remove-dir.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { UserConfigInfoModel } from '@/common/model' - -// eslint-disable-next-line no-unused-vars -export async function removeDir(userConfigInfo: UserConfigInfoModel, path: string) { - // TODO - console.log('removeDir') -} diff --git a/src/common/directive/rename-dir.ts b/src/common/directive/rename-dir.ts deleted file mode 100644 index b6d19ab4..00000000 --- a/src/common/directive/rename-dir.ts +++ /dev/null @@ -1,75 +0,0 @@ -import axios from 'axios' - -export async function renameFolder(owner, repo, oldPath, newPath, token, branch) { - try { - // 设置 axios 请求头,使用 GitHub Token 进行身份验证 - axios.defaults.headers.common.Authorization = `Bearer ${token}` - - // 获取分支的引用 - const branchResponse = await axios.get( - `https://api.github.com/repos/${owner}/${repo}/git/refs/heads/${branch}` - ) - - const latestCommitSha = branchResponse.data.object.sha - - // 获取最新提交的树对象 SHA - const commitResponse = await axios.get( - `https://api.github.com/repos/${owner}/${repo}/git/commits/${latestCommitSha}` - ) - - const treeSha = commitResponse.data.tree.sha - - // 获取树对象的信息 - const treeResponse = await axios.get( - `https://api.github.com/repos/${owner}/${repo}/git/trees/${treeSha}` - ) - - console.log('treeResponse: ', treeResponse) - - // 获取旧文件夹的信息 - const oldFolderInfo = treeResponse.data.tree.find((item) => item.path === oldPath) - - if (!oldFolderInfo) { - throw new Error('The specified path is not a directory.') - } - - // 创建一个新的树对象,将旧文件夹的内容更新为新的路径 - const newTreeResponse = await axios.post( - `https://api.github.com/repos/${owner}/${repo}/git/trees`, - { - base_tree: treeSha, - tree: treeResponse.data.tree.map((item) => { - if (item.path === oldPath) { - return { - path: newPath, - mode: item.mode, - type: item.type, - sha: item.sha - } - } - return item - }) - } - ) - - // 创建一个新的提交,将树对象更新到仓库 - const commitMessage = `Move folder from ${oldPath} to ${newPath}` - const commitInfo = await axios.post( - `https://api.github.com/repos/${owner}/${repo}/git/commits`, - { - message: commitMessage, - tree: newTreeResponse.data.sha, - parents: [latestCommitSha] // 使用最新提交的 SHA 作为父提交 - } - ) - - // 更新分支的引用,将提交应用到仓库 - await axios.patch(`https://api.github.com/repos/${owner}/${repo}/git/refs/heads/${branch}`, { - sha: commitInfo.data.sha - }) - - console.log('Folder renamed successfully!') - } catch (error) { - console.error('Error:', error.response.data) - } -} diff --git a/src/common/model/contextmenu.ts b/src/common/directive/types.ts similarity index 66% rename from src/common/model/contextmenu.ts rename to src/common/directive/types.ts index d1e7b305..43cf072b 100644 --- a/src/common/model/contextmenu.ts +++ b/src/common/directive/types.ts @@ -1,8 +1,10 @@ export enum ContextmenuEnum { // eslint-disable-next-line no-unused-vars - parentDir, + dirArea, // eslint-disable-next-line no-unused-vars - childDir, + dir, // eslint-disable-next-line no-unused-vars - img + img, + // eslint-disable-next-line no-unused-vars + uploadArea } diff --git a/src/common/model/index.ts b/src/common/model/index.ts index 8755106e..d93b49c5 100644 --- a/src/common/model/index.ts +++ b/src/common/model/index.ts @@ -2,5 +2,4 @@ export * from './image' export * from './user-config' export * from './user-settings' export * from './vite-config' -export * from './contextmenu' export * from './tool' diff --git a/src/components/getting-images/getting-images.vue b/src/components/getting-images/getting-images.vue index 12186be4..2ec873ef 100644 --- a/src/components/getting-images/getting-images.vue +++ b/src/components/getting-images/getting-images.vue @@ -1,7 +1,7 @@