diff --git a/README.md b/README.md index e7d7e578..aa9e76f5 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,11 @@ # PicX -[![Author](https://img.shields.io/badge/author-XPoet-violet.svg)](https://github.com/XPoet) -[![Release](https://img.shields.io/github/release/XPoet/picx.svg)](https://github.com/XPoet/picx/releases) -[![License](https://img.shields.io/github/license/XPoet/picx.svg)](https://github.com/XPoet/picx/blob/master/LICENSE) -[![Stars](https://img.shields.io/github/stars/XPoet/picx)](https://github.com/XPoet/picx) -[![Issues](https://img.shields.io/github/issues/XPoet/picx)](https://github.com/XPoet/picx/issues) +[![Release](https://img.shields.io/github/release/XPoet/picx?style=flat&logo=github)](https://github.com/XPoet/picx/releases) +[![License](https://img.shields.io/github/license/XPoet/picx?style=flat&logo=github)](https://github.com/XPoet/picx/blob/master/LICENSE) +[![Stars](https://img.shields.io/github/stars/XPoet/picx?style=flat&logo=github&color=3366cc)](https://github.com/XPoet/picx) +[![Issues](https://img.shields.io/github/issues/XPoet/picx?style=flat&logo=github)](https://github.com/XPoet/picx/issues) [![Deploy](https://github.com/XPoet/picx/workflows/deploy/badge.svg)](https://github.com/XPoet/picx/actions/workflows/deploy.yml) -[![JavaScript Style Guide](https://img.shields.io/badge/code_style-Airbnb-hotpink.svg)](https://github.com/lin-123/javascript) **[PicX](https://picx.xpoet.cn)** 是一款基于 GitHub API 开发的图床工具,提供图片上传托管、生成图片链接和常用图片工具箱服务。 @@ -28,6 +26,10 @@ **在线使用入口 https://picx.xpoet.cn** +> **重要提示:** +> - 为进一步简化用户操作,PicX 自 `v3.0` 起,不再支持自由选择仓库和分支,统一使用内置的仓库和分支。 +> - 如需继续使用自定义的仓库和分支,请使用 [PicX v2.0](https://v2.picx.xpoet.cn)。 + ## 文档 | Documents **官方文档 https://picx-docs.xpoet.cn** diff --git a/auto-imports.d.ts b/auto-imports.d.ts deleted file mode 100644 index 53132acc..00000000 --- a/auto-imports.d.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* eslint-disable */ -/* prettier-ignore */ -// @ts-nocheck -// Generated by unplugin-auto-import -export {} -declare global { - const ElLoading: typeof import('element-plus/es')['ElLoading'] - const ElMessage: typeof import('element-plus/es')['ElMessage'] - const ElMessageBox: typeof import('element-plus/es')['ElMessageBox'] -} diff --git a/components.d.ts b/components.d.ts deleted file mode 100644 index f1e90022..00000000 --- a/components.d.ts +++ /dev/null @@ -1,65 +0,0 @@ -/* eslint-disable */ -/* prettier-ignore */ -// @ts-nocheck -// Generated by unplugin-vue-components -// Read more: https://github.com/vuejs/core/pull/3399 -import '@vue/runtime-core' - -export {} - -declare module '@vue/runtime-core' { - export interface GlobalComponents { - Base64Tool: typeof import('./src/components/tools/base64-tool/base64-tool.vue')['default'] - CompressConfigBox: typeof import('./src/components/compress-config-box/compress-config-box.vue')['default'] - CompressTool: typeof import('./src/components/tools/compress-tool/compress-tool.vue')['default'] - CopyImageLink: typeof import('./src/components/copy-image-link/copy-image-link.vue')['default'] - ElButton: typeof import('element-plus/es')['ElButton'] - ElCard: typeof import('element-plus/es')['ElCard'] - ElCascader: typeof import('element-plus/es')['ElCascader'] - ElCheckbox: typeof import('element-plus/es')['ElCheckbox'] - ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider'] - ElDivider: typeof import('element-plus/es')['ElDivider'] - ElDropdown: typeof import('element-plus/es')['ElDropdown'] - ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem'] - ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu'] - ElForm: typeof import('element-plus/es')['ElForm'] - ElFormItem: typeof import('element-plus/es')['ElFormItem'] - ElIcon: typeof import('element-plus/es')['ElIcon'] - ElInput: typeof import('element-plus/es')['ElInput'] - ElLink: typeof import('element-plus/es')['ElLink'] - ElOption: typeof import('element-plus/es')['ElOption'] - ElRadio: typeof import('element-plus/es')['ElRadio'] - ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup'] - ElSelect: typeof import('element-plus/es')['ElSelect'] - ElStep: typeof import('element-plus/es')['ElStep'] - ElSteps: typeof import('element-plus/es')['ElSteps'] - ElSwitch: typeof import('element-plus/es')['ElSwitch'] - ElTable: typeof import('element-plus/es')['ElTable'] - ElTableColumn: typeof import('element-plus/es')['ElTableColumn'] - ElTag: typeof import('element-plus/es')['ElTag'] - ElTimeSelect: typeof import('element-plus/es')['ElTimeSelect'] - ElTooltip: typeof import('element-plus/es')['ElTooltip'] - FolderCard: typeof import('./src/components/folder-card/folder-card.vue')['default'] - GettingImages: typeof import('./src/components/getting-images/getting-images.vue')['default'] - HeaderContent: typeof import('./src/components/header-content/header-content.vue')['default'] - ImageCard: typeof import('./src/components/image-card/image-card.vue')['default'] - ImageLinkRuleConfig: typeof import('./src/components/image-link-rule-config/image-link-rule-config.vue')['default'] - ImageSelector: typeof import('./src/components/image-selector/image-selector.vue')['default'] - ImgProcessStateCard: typeof import('./src/components/tools/img-process-state-card/img-process-state-card.vue')['default'] - MainContainer: typeof import('./src/components/main-container/main-container.vue')['default'] - NavContent: typeof import('./src/components/nav-content/nav-content.vue')['default'] - RefreshConfig: typeof import('./src/components/refresh-config/refresh-config.vue')['default'] - RepoDirCascader: typeof import('./src/components/repo-dir-cascader/repo-dir-cascader.vue')['default'] - RouterLink: typeof import('vue-router')['RouterLink'] - RouterView: typeof import('vue-router')['RouterView'] - SelectedInfoBar: typeof import('./src/components/selected-info-bar/selected-info-bar.vue')['default'] - SiteCount: typeof import('./src/components/site-count/site-count.vue')['default'] - ToUploadImageCard: typeof import('./src/components/to-upload-image-card/to-upload-image-card.vue')['default'] - UploadArea: typeof import('./src/components/upload-area/upload-area.vue')['default'] - WatermarkConfigBox: typeof import('./src/components/watermark-config-box/watermark-config-box.vue')['default'] - WatermarkTool: typeof import('./src/components/tools/watermark-tool/watermark-tool.vue')['default'] - } - export interface ComponentCustomProperties { - vLoading: typeof import('element-plus/es')['ElLoadingDirective'] - } -} diff --git a/package.json b/package.json index 41756659..77d5399e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "picx", - "version": "2.5.0", + "version": "3.0.0", "private": false, "author": "XPoet ", "license": "AGPL-3.0", 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/copy-source-repo/copy-source-repo.styl b/src/components/copy-source-repo/copy-source-repo.styl new file mode 100644 index 00000000..e54ed87c --- /dev/null +++ b/src/components/copy-source-repo/copy-source-repo.styl @@ -0,0 +1,22 @@ +.copy-source-repo-icon { + color var(--text-color-4) + font-size 18rem + cursor pointer + + &.management { + color var(--text-color-3) + font-size 20rem + } +} + + +.copy-source-repo { + .copy-result-tip { + margin-bottom 20rem + } + + .copy-progress { + margin-bottom 20rem + } +} + diff --git a/src/components/copy-source-repo/copy-source-repo.util.ts b/src/components/copy-source-repo/copy-source-repo.util.ts new file mode 100644 index 00000000..9e91d375 --- /dev/null +++ b/src/components/copy-source-repo/copy-source-repo.util.ts @@ -0,0 +1,157 @@ +import { computed } from 'vue' +import request from '@/utils/request' +import { store } from '@/stores' +import { getBase64ByImageUrl, getFileSuffix, isImage } from '@/utils' +import { + createCommit, + createRef, + createTree, + getBranchInfo, + getFileBlob, + getRepoPathContent +} from '@/common/api' +import i18n from '@/plugins/vue/i18n' + +let idx = 0 +let count = 0 +const repoImgList: any[] = [] + +/** + * 获取原图床仓库的图片资源 + * @param sourceRepo + * @param sourceRepoBranch + * @param path + * @param cb + */ +export const getSourceRepoImgContent = async ( + sourceRepo: string, + sourceRepoBranch: string, + path: string, + cb?: any +) => { + const { owner } = computed(() => store.getters.getUserConfigInfo).value + + const res = await request({ + method: 'GET', + noShowErrMsg: true, + url: `repos/${owner}/${sourceRepo}/contents/${path}`, + params: { + ref: sourceRepoBranch + } + }) + + idx += 1 + + if (!res) { + // eslint-disable-next-line no-unused-expressions + cb && cb({ status: false, imgList: repoImgList }) + return + } + + if (Array.isArray(res) && res.length) { + count += res.length + // eslint-disable-next-line no-restricted-syntax + for (const ri of res) { + await getSourceRepoImgContent(sourceRepo, sourceRepoBranch, ri.path) + } + } + + if (res?.type === 'dir') { + await getSourceRepoImgContent(sourceRepo, sourceRepoBranch, res.path) + } + + if (res?.type === 'file') { + if (isImage(res.name)) { + let content: string = res?.content + if (!content) { + const tmpRes: any = await getBase64ByImageUrl(res.download_url, getFileSuffix(res.name)) + const base64 = tmpRes?.split(',')[1] + if (base64) { + content = base64 + } + } + repoImgList.push({ + name: res.name, + path: res.path, + content, + sha: res.sha, + download_url: res.download_url + }) + } + } + + if (idx > count) { + // eslint-disable-next-line no-unused-expressions + cb && cb({ status: true, imgList: repoImgList }) + } +} + +/** + * 上传原图床仓库的图片到当前仓库 + * @param imgs + * @param sourceRepo + */ +export const uploadSourceRepoImages = async (imgs: any[], sourceRepo: string) => { + const { branch, repo, owner } = computed(() => store.getters.getUserConfigInfo).value + + const blobs = [] + // eslint-disable-next-line no-restricted-syntax + for (const img of imgs) { + const blobRes: any = await getFileBlob(img.content, owner, repo) + if (blobRes) { + blobs.push({ img, ...blobRes }) + } else { + ElMessage.error(i18n.global.t('upload_page.tip_11', { name: img.name })) + } + } + + const branchRes: any = await getBranchInfo(owner, repo, branch) + if (!branchRes) { + return Promise.resolve(false) + } + + const treeRes: any = await createTree( + owner, + repo, + blobs.map((x: any) => ({ + sha: x.sha, + path: x.img.path + })), + branchRes + ) + if (!treeRes) { + return Promise.resolve(false) + } + + const commitRes: any = await createCommit( + owner, + repo, + treeRes, + branchRes, + `Copy \`${sourceRepo}\` image${imgs.length ? 's' : ''} via PicX (https://github.com/XPoet/picx)` + ) + if (!commitRes) { + return Promise.resolve(false) + } + + const refRes = await createRef(owner, repo, branch, commitRes.sha) + if (!refRes) { + return Promise.resolve(false) + } + + return Promise.resolve(true) +} + +/** + * 刷新图床管理页面,获取最新数据 + */ +export const refreshManagementPage = async () => { + const userConfigInfo = computed(() => store.getters.getUserConfigInfo).value + + const viewDir = '/' + await store.dispatch('SET_USER_CONFIG_INFO', { + viewDir + }) + await store.dispatch('DIR_IMAGE_LIST_INIT_DIR', viewDir) + await getRepoPathContent(userConfigInfo, viewDir) +} diff --git a/src/components/copy-source-repo/copy-source-repo.vue b/src/components/copy-source-repo/copy-source-repo.vue new file mode 100644 index 00000000..5654ce4e --- /dev/null +++ b/src/components/copy-source-repo/copy-source-repo.vue @@ -0,0 +1,182 @@ + + + + + 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 58% rename from src/components/deploy-bar/deploy-bar.styl rename to src/components/deploy-status-bar/deploy-status-bar.styl index 3af2000b..d0bd8493 100644 --- a/src/components/deploy-bar/deploy-bar.styl +++ b/src/components/deploy-status-bar/deploy-status-bar.styl @@ -1,34 +1,27 @@ -.deploy-box { +.deploy-status-bar-box { width 100% .deploy-item { display flex align-items center justify-content space-between - margin-bottom 12rem - padding 2rem 0 2rem 10rem - border 1rem solid var(--text-color-4) - border-radius 6rem + margin-bottom 10rem + &:last-child { margin-bottom 0 } .left-wrap { - display flex - align-items center - justify-content flex-start - } - - .info-item { - margin-right 10rem - font-size 14rem + .info-item { + margin-right 10rem + } } .deploy-status-icon { display inline-block - width 12rem - height 12rem + width 10rem + height 10rem background var(--el-color-info) border-radius 50% diff --git a/src/views/main-container/main-container.util.ts b/src/components/deploy-status-bar/deploy-status-bar.util.ts similarity index 55% rename from src/views/main-container/main-container.util.ts rename to src/components/deploy-status-bar/deploy-status-bar.util.ts index 207c9e50..1b230d1a 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' @@ -12,17 +13,19 @@ const filename = '.deploy' * 获取云端仓库存储的部署状态信息 */ export const getCloudDeployInfo = async () => { - const { owner, selectedRepo: repo, selectedBranch: branch } = userConfigInfo + const { owner, repo, branch } = userConfigInfo + + if (!owner || !repo || !branch) { + return null + } + const res = await request({ url: `/repos/${owner}/${repo}/contents/${filename}`, method: 'GET', - noShowErrorMsg: true, - cache: { - maxAge: 0 - }, + noShowErrMsg: true, + noCache: true, params: { - branch, - timestamp: Date.now() + branch } }) @@ -33,13 +36,13 @@ export const getCloudDeployInfo = async () => { * 保存部署状态信息到云端仓库 */ export const saveCloudDeployInfo = async () => { - const { owner, selectedRepo: repo, selectedBranch: branch } = userConfigInfo + const { owner, repo, branch } = userConfigInfo const res = await getCloudDeployInfo() 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) { @@ -52,7 +55,7 @@ export const saveCloudDeployInfo = async () => { url: `/repos/${owner}/${repo}/contents/${filename}`, method: 'PUT', data, - noShowErrorMsg: true + noShowErrMsg: true }) return Promise.resolve(res2) @@ -62,8 +65,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 56% rename from src/components/deploy-bar/deploy-bar.vue rename to src/components/deploy-status-bar/deploy-status-bar.vue index add4171b..c452fafb 100644 --- a/src/components/deploy-bar/deploy-bar.vue +++ b/src/components/deploy-status-bar/deploy-status-bar.vue @@ -1,11 +1,11 @@