🚧 WIP(custom): v3.0.0 migrate to vite and esm

This commit is contained in:
Kuingsmile
2025-07-31 17:37:30 +08:00
parent cd76bc7c10
commit 054f4b4cff
597 changed files with 197292 additions and 13329 deletions

View File

@@ -1,5 +1,8 @@
<template>
<div id="app" :key="pageReloadCount">
<div
id="app"
:key="pageReloadCount"
>
<router-view />
</div>
</template>
@@ -8,8 +11,8 @@
import type { IConfig } from 'piclist'
import { onBeforeMount } from 'vue'
import { useStore } from '@/hooks/useStore'
import { useATagClick } from '@/hooks/useATagClick'
import { useStore } from '@/hooks/useStore'
import { getConfig } from '@/utils/dataSender'
import { pageReloadCount } from '@/utils/global'

View File

@@ -1,5 +1,4 @@
import axios from 'axios'
import path from 'path'
import { deleteFailedLog, deleteLog } from '#/utils/deleteLog'
@@ -14,7 +13,7 @@ interface IConfigMap {
}
export default class AlistApi {
static async delete(configMap: IConfigMap): Promise<boolean> {
static async delete (configMap: IConfigMap): Promise<boolean> {
const { fileName, config } = configMap
try {
const { version, url, uploadPath, token } = config
@@ -30,8 +29,8 @@ export default class AlistApi {
Authorization: token
},
data: {
dir: path.join('/', uploadPath, path.dirname(fileName)),
names: [path.basename(fileName)]
dir: window.node.path.join('/', uploadPath, window.node.path.dirname(fileName)),
names: [window.node.path.basename(fileName)]
}
})
if (result.data.code === 200) {

View File

@@ -1,5 +1,4 @@
import axios from 'axios'
import path from 'path'
import { deleteFailedLog, deleteLog } from '#/utils/deleteLog'
@@ -25,7 +24,7 @@ const getAListToken = async (url: string, username: string, password: string) =>
}
export default class AListplistApi {
static async delete(configMap: IConfigMap): Promise<boolean> {
static async delete (configMap: IConfigMap): Promise<boolean> {
const { fileName, config } = configMap
try {
const { url, username, password, uploadPath } = config
@@ -45,8 +44,8 @@ export default class AListplistApi {
Authorization: token
},
data: {
dir: path.join('/', uploadPath, path.dirname(fileName)),
names: [path.basename(fileName)]
dir: window.node.path.join('/', uploadPath, window.node.path.dirname(fileName)),
names: [window.node.path.basename(fileName)]
}
})
if (result.data.code === 200) {

View File

@@ -1,5 +1,4 @@
import OSS from 'ali-oss'
import { IAliYunConfig, PartialKeys } from '#/types/types'
import { deleteFailedLog, deleteLog } from '#/utils/deleteLog'
interface IConfigMap {
@@ -8,14 +7,14 @@ interface IConfigMap {
}
export default class AliyunApi {
static #getKey(fileName: string, path?: string): string {
static #getKey (fileName: string, path?: string): string {
return path && path !== '/' ? `${path.replace(/^\/+|\/+$/, '')}/${fileName}` : fileName
}
static async delete(configMap: IConfigMap): Promise<boolean> {
static async delete (configMap: IConfigMap): Promise<boolean> {
const { fileName, config } = configMap
try {
const client = new OSS({ ...config, region: config.area })
const client = new window.node.OSS({ ...config, region: config.area })
const key = AliyunApi.#getKey(fileName, config.path)
const result = await client.delete(key)
if (result.res.status === 204) {

View File

@@ -15,6 +15,7 @@ import SmmsApi from '@/apis/smms'
import TcyunApi from '@/apis/tcyun'
import UpyunApi from '@/apis/upyun'
import WebdavApi from '@/apis/webdav'
import { IStringKeyMap } from '#/types/types'
const apiMap: IStringKeyMap = {
alist: AlistApi,
@@ -38,7 +39,7 @@ const apiMap: IStringKeyMap = {
}
export default class ALLApi {
static async delete(configMap: IStringKeyMap): Promise<boolean> {
static async delete (configMap: IStringKeyMap): Promise<boolean> {
const api = apiMap[configMap.type]
return api ? await api.delete(configMap) : false
}

View File

@@ -1,16 +1,12 @@
import { ipcRenderer } from 'electron'
import { getRawData, triggerRPC } from '@/utils/common'
import { removeFileFromS3InMain } from '~/utils/deleteFunc'
import { deleteFailedLog } from '#/utils/deleteLog'
import { getRawData } from '@/utils/common'
import { IRPCActionType } from '#/types/enum'
import { IStringKeyMap } from '#/types/types'
import { deleteFailedLog } from '#/utils/deleteLog'
export default class AwsS3Api {
static async delete(configMap: IStringKeyMap): Promise<boolean> {
static async delete (configMap: IStringKeyMap): Promise<boolean> {
try {
return ipcRenderer
? (await triggerRPC(IRPCActionType.GALLERY_DELETE_AWS_S3_FILE, getRawData(configMap))) || false
: await removeFileFromS3InMain(getRawData(configMap))
return (await window.electron.triggerRPC(IRPCActionType.GALLERY_DELETE_AWS_S3_FILE, getRawData(configMap))) || false
} catch (error: any) {
deleteFailedLog(configMap.fileName, 'AWS S3', error)
return false

View File

@@ -1,17 +1,12 @@
import { ipcRenderer } from 'electron'
import { removeFileFromDogeInMain } from '~/utils/deleteFunc'
import { getRawData, triggerRPC } from '@/utils/common'
import { deleteFailedLog } from '#/utils/deleteLog'
import { getRawData } from '@/utils/common'
import { IRPCActionType } from '#/types/enum'
import { IStringKeyMap } from '#/types/types'
import { deleteFailedLog } from '#/utils/deleteLog'
export default class AwsS3Api {
static async delete(configMap: IStringKeyMap): Promise<boolean> {
static async delete (configMap: IStringKeyMap): Promise<boolean> {
try {
return ipcRenderer
? (await triggerRPC(IRPCActionType.GALLERY_DELETE_DOGE_FILE, getRawData(configMap))) || false
: await removeFileFromDogeInMain(getRawData(configMap))
return (await window.electron.triggerRPC(IRPCActionType.GALLERY_DELETE_DOGE_FILE, getRawData(configMap))) || false
} catch (error: any) {
deleteFailedLog(configMap.fileName, 'DogeCloud', error)
return false

View File

@@ -1,5 +1,4 @@
import { Octokit } from '@octokit/rest'
import { IGitHubConfig, PartialKeys } from '#/types/types'
import { deleteFailedLog, deleteLog } from '#/utils/deleteLog'
interface IConfigMap {
@@ -9,18 +8,18 @@ interface IConfigMap {
}
export default class GithubApi {
static #createOctokit(token: string) {
return new Octokit({
static #createOctokit (token: string) {
return new window.node.Octokit({
auth: token
})
}
static #createKey(path: string | undefined, fileName: string): string {
static #createKey (path: string | undefined, fileName: string): string {
const formatedFileName = fileName.replace(/%2F/g, '/')
return path && path !== '/' ? `${path.replace(/^\/+|\/+$/, '')}/${formatedFileName}` : formatedFileName
}
static async delete(configMap: IConfigMap): Promise<boolean> {
static async delete (configMap: IConfigMap): Promise<boolean> {
const {
fileName,
hash,

View File

@@ -1,17 +1,12 @@
import { ipcRenderer } from 'electron'
import { removeFileFromHuaweiInMain } from '~/utils/deleteFunc'
import { getRawData, triggerRPC } from '@/utils/common'
import { deleteFailedLog } from '#/utils/deleteLog'
import { getRawData } from '@/utils/common'
import { IRPCActionType } from '#/types/enum'
import { IStringKeyMap } from '#/types/types'
import { deleteFailedLog } from '#/utils/deleteLog'
export default class HuaweicloudApi {
static async delete(configMap: IStringKeyMap): Promise<boolean> {
static async delete (configMap: IStringKeyMap): Promise<boolean> {
try {
return ipcRenderer
? (await triggerRPC(IRPCActionType.GALLERY_DELETE_HUAWEI_OSS_FILE, getRawData(configMap))) || false
: await removeFileFromHuaweiInMain(getRawData(configMap))
return (await window.electron.triggerRPC(IRPCActionType.GALLERY_DELETE_HUAWEI_OSS_FILE, getRawData(configMap))) || false
} catch (error: any) {
deleteFailedLog(configMap.fileName, 'HuaweiCloud', error)
return false

View File

@@ -1,5 +1,6 @@
import axios, { AxiosResponse } from 'axios'
import { IImgurConfig } from '#/types/types'
import { deleteFailedLog, deleteLog } from '#/utils/deleteLog'
interface IConfigMap {
@@ -10,7 +11,7 @@ interface IConfigMap {
export default class ImgurApi {
static #baseUrl = 'https://api.imgur.com/3'
static async delete(configMap: IConfigMap): Promise<boolean> {
static async delete (configMap: IConfigMap): Promise<boolean> {
const { config: { clientId = '', username = '', accessToken = '' } = {}, hash = '' } = configMap
let Authorization: string, apiUrl: string

View File

@@ -1,5 +1,3 @@
import fs from 'fs-extra'
import { deleteFailedLog, deleteLog } from '#/utils/deleteLog'
interface IConfigMap {
@@ -7,7 +5,7 @@ interface IConfigMap {
}
export default class LocalApi {
static async delete(configMap: IConfigMap): Promise<boolean> {
static async delete (configMap: IConfigMap): Promise<boolean> {
const { hash } = configMap
if (!hash) {
deleteLog(hash, 'Local', false, 'Local.delete: invalid params')
@@ -15,7 +13,7 @@ export default class LocalApi {
}
try {
await fs.remove(hash)
await window.node.fs.remove(hash)
deleteLog(hash, 'Local')
return true
} catch (error: any) {

View File

@@ -1,10 +1,8 @@
import axios, { AxiosResponse } from 'axios'
import https from 'https'
import { IStringKeyMap } from '#/types/types'
import { deleteFailedLog, deleteLog } from '#/utils/deleteLog'
export default class LskyplistApi {
static async delete(configMap: IStringKeyMap): Promise<boolean> {
static async delete (configMap: IStringKeyMap): Promise<boolean> {
const { hash, config } = configMap
if (!hash || !config || !config.token) {
deleteLog(hash, 'Lskyplist', false, 'LskyplistApi.delete: invalid params')
@@ -22,11 +20,11 @@ export default class LskyplistApi {
Authorization: token || undefined
}
const requestAgent = new https.Agent({
const requestAgent = new window.node.https.Agent({
rejectUnauthorized: false
})
try {
const response: AxiosResponse = await axios.delete(`${host}/api/v1/images/${hash}`, {
const response: any = await window.node.axios.delete(`${host}/api/v1/images/${hash}`, {
headers: v2Headers,
timeout: 30000,
httpsAgent: requestAgent

View File

@@ -1,9 +1,8 @@
import axios, { AxiosResponse } from 'axios'
import { deleteFailedLog, deleteLog } from '#/utils/deleteLog'
import { IStringKeyMap } from '#/types/types'
import { deleteFailedLog, deleteLog } from '~/utils/deleteLog'
export default class PiclistApi {
static async delete(configMap: IStringKeyMap): Promise<boolean> {
static async delete (configMap: IStringKeyMap): Promise<boolean> {
const { config, fullResult } = configMap
const { host, port } = config
if (!fullResult) return true
@@ -16,7 +15,7 @@ export default class PiclistApi {
const url = `http://${host || '127.0.0.1'}:${port || 36677}/delete`
try {
const response: AxiosResponse = await axios.post(url, {
const response: any = await window.node.axios.post(url, {
list: [fullResult]
})
if (response.status === 200 && response.data?.success) {

View File

@@ -1,6 +1,5 @@
import Qiniu from 'qiniu'
import { deleteFailedLog, deleteLog } from '#/utils/deleteLog'
import { IQiniuConfig, PartialKeys } from '#/types/types'
import { deleteFailedLog, deleteLog } from '~/utils/deleteLog'
interface IConfigMap {
fileName: string
@@ -8,15 +7,15 @@ interface IConfigMap {
}
export default class QiniuApi {
static async delete(configMap: IConfigMap): Promise<boolean> {
static async delete (configMap: IConfigMap): Promise<boolean> {
const {
fileName,
config: { accessKey, secretKey, bucket, path }
} = configMap
const mac = new Qiniu.auth.digest.Mac(accessKey, secretKey)
const qiniuConfig = new Qiniu.conf.Config()
const mac = new window.node.qiniu.auth.digest.Mac(accessKey, secretKey)
const qiniuConfig = new window.node.qiniu.conf.Config()
try {
const bucketManager = new Qiniu.rs.BucketManager(mac, qiniuConfig)
const bucketManager = new window.node.qiniu.rs.BucketManager(mac, qiniuConfig)
const formattedPath = path?.replace(/^\/+|\/+$/, '') || ''
const key = path === '/' || !path ? fileName : `${formattedPath}/${fileName}`
const res = (await new Promise((resolve, reject) => {

View File

@@ -1,18 +1,13 @@
import { ipcRenderer } from 'electron'
import { removeFileFromSFTPInMain } from '~/utils/deleteFunc'
import { getRawData, triggerRPC } from '@/utils/common'
import { deleteFailedLog } from '#/utils/deleteLog'
import { getRawData } from '@/utils/common'
import { IRPCActionType } from '#/types/enum'
import { IStringKeyMap } from '#/types/types'
import { deleteFailedLog } from '#/utils/deleteLog'
export default class SftpPlistApi {
static async delete(configMap: IStringKeyMap): Promise<boolean> {
static async delete (configMap: IStringKeyMap): Promise<boolean> {
const { fileName, config } = configMap
try {
return ipcRenderer
? (await triggerRPC(IRPCActionType.GALLERY_DELETE_SFTP_FILE, getRawData(config), fileName)) || false
: await removeFileFromSFTPInMain(getRawData(config), fileName)
return (await window.electron.triggerRPC(IRPCActionType.GALLERY_DELETE_SFTP_FILE, getRawData(config), fileName)) || false
} catch (error: any) {
deleteFailedLog(fileName, 'SFTP', error)
return false

View File

@@ -1,5 +1,4 @@
import axios, { AxiosResponse } from 'axios'
import { ISMMSConfig } from '#/types/types'
import { deleteFailedLog, deleteLog } from '#/utils/deleteLog'
interface IConfigMap {
@@ -10,7 +9,7 @@ interface IConfigMap {
export default class SmmsApi {
static readonly #baseUrl = 'https://smms.app/api/v2'
static async delete(configMap: IConfigMap): Promise<boolean> {
static async delete (configMap: IConfigMap): Promise<boolean> {
const { hash, config } = configMap
if (!hash || !config || !config.token) {
deleteLog(hash, 'Smms', false, 'SmmsApi.delete: invalid params')
@@ -20,7 +19,7 @@ export default class SmmsApi {
const { token } = config
try {
const response: AxiosResponse = await axios.get(`${SmmsApi.#baseUrl}/delete/${hash}`, {
const response: any = await window.node.axios.get(`${SmmsApi.#baseUrl}/delete/${hash}`, {
headers: {
Authorization: token
},

View File

@@ -1,21 +1,19 @@
import COS from 'cos-nodejs-sdk-v5'
import { ITcYunConfig, PartialKeys } from '#/types/types'
import { deleteFailedLog, deleteLog } from '#/utils/deleteLog'
interface IConfigMap {
fileName: string
config: PartialKeys<ITcYunConfig, 'path'>
}
export default class TcyunApi {
static #createCOS(SecretId: string, SecretKey: string): COS {
return new COS({
static #createCOS (SecretId: string, SecretKey: string): any {
return new window.node.COS({
SecretId,
SecretKey
})
}
static async delete(configMap: IConfigMap): Promise<boolean> {
static async delete (configMap: IConfigMap): Promise<boolean> {
const {
fileName,
config: { secretId, secretKey, bucket, area, path }

View File

@@ -1,5 +1,4 @@
import Upyun from 'upyun'
import { IUpYunConfig, PartialKeys } from '#/types/types'
import { deleteFailedLog, deleteLog } from '#/utils/deleteLog'
interface IConfigMap {
@@ -8,14 +7,14 @@ interface IConfigMap {
}
export default class UpyunApi {
static async delete(configMap: IConfigMap): Promise<boolean> {
static async delete (configMap: IConfigMap): Promise<boolean> {
const {
fileName,
config: { bucket, operator, password, path }
} = configMap
try {
const service = new Upyun.Service(bucket, operator, password)
const client = new Upyun.Client(service)
const service = new window.node.Upyun.Service(bucket, operator, password)
const client = new window.node.Upyun.Client(service)
let key
if (path === '/' || !path) {
key = fileName

View File

@@ -1,7 +1,6 @@
import { AuthType, WebDAVClientOptions, createClient } from 'webdav'
import { deleteFailedLog, deleteLog } from '#/utils/deleteLog'
import { IWebdavPlistConfig, PartialKeys } from '#/types/types'
import { formatEndpoint } from '#/utils/common'
import { deleteFailedLog, deleteLog } from '#/utils/deleteLog'
interface IConfigMap {
fileName: string
@@ -9,20 +8,20 @@ interface IConfigMap {
}
export default class WebdavApi {
static async delete(configMap: IConfigMap): Promise<boolean> {
static async delete (configMap: IConfigMap): Promise<boolean> {
const {
fileName,
config: { host, username, password, path, sslEnabled, authType }
} = configMap
const endpoint = formatEndpoint(host, sslEnabled)
const options: WebDAVClientOptions = {
const options: any = {
username,
password
}
if (authType === 'digest') {
options.authType = AuthType.Digest
options.authType = window.node.webdav.AuthType.Digest
}
const ctx = createClient(endpoint, options)
const ctx = window.node.webdav.createClient(endpoint, options)
let key
if (path === '/' || !path) {
key = fileName

View File

@@ -1,8 +1,26 @@
<!-- eslint-disable vue/no-v-html -->
<template>
<div id="config-form" :class="props.colorMode === 'white' ? 'white' : ''">
<el-form ref="$form" label-position="left" label-width="50%" :model="ruleForm" size="small">
<el-form-item :label="$T('UPLOADER_CONFIG_NAME')" required prop="_configName">
<el-input v-model="ruleForm._configName" type="input" :placeholder="$T('UPLOADER_CONFIG_PLACEHOLDER')" />
<div
id="config-form"
:class="props.colorMode === 'white' ? 'white' : ''"
>
<el-form
ref="$form"
label-position="left"
label-width="50%"
:model="ruleForm"
size="small"
>
<el-form-item
:label="$T('UPLOADER_CONFIG_NAME')"
required
prop="_configName"
>
<el-input
v-model="ruleForm._configName"
type="input"
:placeholder="$T('UPLOADER_CONFIG_PLACEHOLDER')"
/>
</el-form-item>
<!-- dynamic config -->
<el-form-item
@@ -15,9 +33,18 @@
<el-row align="middle">
{{ item.alias || item.name }}
<template v-if="item.tips">
<el-tooltip class="item" effect="dark" placement="right" :persistent="false" teleported>
<el-tooltip
class="item"
effect="dark"
placement="right"
:persistent="false"
teleported
>
<template #content>
<span class="config-form-common-tips" v-html="transformMarkdownToHTML(item.tips)" />
<span
class="config-form-common-tips"
v-html="transformMarkdownToHTML(item.tips)"
/>
</template>
<el-icon class="ml-[4px] cursor-pointer hover:text-blue">
<InfoFilled />
@@ -75,14 +102,16 @@
</template>
<script lang="ts" setup>
import type { FormInstance } from 'element-plus'
import { cloneDeep, union } from 'lodash'
import { marked } from 'marked'
import { reactive, ref, watch, toRefs } from 'vue'
import { useRoute } from 'vue-router'
import { InfoFilled } from '@element-plus/icons-vue'
import type { FormInstance } from 'element-plus'
import { cloneDeep, union } from 'lodash-es'
import { marked } from 'marked'
import { reactive, ref, toRefs, watch } from 'vue'
import { useRoute } from 'vue-router'
import { T as $T } from '@/i18n/index'
import { getConfig } from '@/utils/dataSender'
import { IPicGoPluginConfig, IStringKeyMap } from '#/types/types'
interface IProps {
config: any[]
@@ -109,11 +138,11 @@ watch(
}
)
function handleConfigChange(val: any) {
function handleConfigChange (val: any) {
handleConfig(val)
}
async function validate(): Promise<IStringKeyMap | false> {
async function validate (): Promise<IStringKeyMap | false> {
return new Promise(resolve => {
$form.value?.validate((valid: boolean) => {
if (valid) {
@@ -125,7 +154,7 @@ async function validate(): Promise<IStringKeyMap | false> {
})
}
function transformMarkdownToHTML(markdown: string) {
function transformMarkdownToHTML (markdown: string) {
try {
return marked.parse(markdown)
} catch (e) {
@@ -133,7 +162,7 @@ function transformMarkdownToHTML(markdown: string) {
}
}
function getConfigType() {
function getConfigType () {
switch (props.type) {
case 'plugin': {
return props.id
@@ -149,7 +178,7 @@ function getConfigType() {
}
}
async function handleConfig(val: IPicGoPluginConfig[]) {
async function handleConfig (val: IPicGoPluginConfig[]) {
const config = await getCurConfigFormData()
const configId = $route.params.configId
Object.assign(ruleForm, config)
@@ -175,13 +204,13 @@ async function handleConfig(val: IPicGoPluginConfig[]) {
}
}
async function getCurConfigFormData() {
async function getCurConfigFormData () {
const configId = $route.params.configId
const curTypeConfigList = (await getConfig<IStringKeyMap[]>(`uploader.${props.id}.configList`)) || []
return curTypeConfigList.find(i => i._id === configId) || {}
}
function updateRuleForm(key: string, value: any) {
function updateRuleForm (key: string, value: any) {
try {
ruleForm[key] = value
} catch (e) {

View File

@@ -1,8 +1,25 @@
<template>
<div id="config-form" :class="props.colorMode === 'white' ? 'white' : ''">
<el-form ref="$form" label-position="left" label-width="50%" :model="ruleForm" size="small">
<el-form-item :label="$T('UPLOADER_CONFIG_NAME')" required prop="_configName">
<el-input v-model="ruleForm._configName" type="input" :placeholder="$T('UPLOADER_CONFIG_PLACEHOLDER')" />
<div
id="config-form"
:class="props.colorMode === 'white' ? 'white' : ''"
>
<el-form
ref="$form"
label-position="left"
label-width="50%"
:model="ruleForm"
size="small"
>
<el-form-item
:label="$T('UPLOADER_CONFIG_NAME')"
required
prop="_configName"
>
<el-input
v-model="ruleForm._configName"
type="input"
:placeholder="$T('UPLOADER_CONFIG_PLACEHOLDER')"
/>
</el-form-item>
<!-- dynamic config -->
<el-form-item
@@ -62,11 +79,12 @@
<script lang="ts" setup>
import type { FormInstance } from 'element-plus'
import { cloneDeep, union } from 'lodash'
import { cloneDeep, union } from 'lodash-es'
import { reactive, ref, watch } from 'vue'
import { getConfig } from '@/utils/dataSender'
import { T as $T } from '@/i18n'
import { getConfig } from '@/utils/dataSender'
import { IPicGoPluginConfig, IStringKeyMap } from '#/types/types'
interface IProps {
config: any[]
@@ -92,11 +110,11 @@ watch(
}
)
function handleConfigChange(val: any) {
function handleConfigChange (val: any) {
handleConfig(val)
}
async function validate(): Promise<IStringKeyMap | false> {
async function validate (): Promise<IStringKeyMap | false> {
return new Promise(resolve => {
$form.value?.validate((valid: boolean) => {
if (valid) {
@@ -108,7 +126,7 @@ async function validate(): Promise<IStringKeyMap | false> {
})
}
function getConfigType() {
function getConfigType () {
switch (props.type) {
case 'plugin': {
return props.id
@@ -124,7 +142,7 @@ function getConfigType() {
}
}
async function handleConfig(val: IPicGoPluginConfig[]) {
async function handleConfig (val: IPicGoPluginConfig[]) {
const config = await getCurConfigFormData()
Object.assign(ruleForm, config)
if (val.length > 0) {
@@ -148,11 +166,11 @@ async function handleConfig(val: IPicGoPluginConfig[]) {
}
}
async function getCurConfigFormData() {
async function getCurConfigFormData () {
return (await getConfig<IStringKeyMap>(`${props.id}`)) || {}
}
function updateRuleForm(key: string, value: any) {
function updateRuleForm (key: string, value: any) {
try {
ruleForm[key] = value
} catch (e) {

View File

@@ -3,7 +3,7 @@
:src="
isShowThumbnail && item.isImage
? base64Image
: require(`../manage/pages/assets/icons/${getFileIconPath(item.fileName ?? '')}`)
: `/assets/icons/${getFileIconPath(item.fileName ?? '')}`
"
fit="contain"
style="height: 100px; width: 100%; margin: 0 auto"
@@ -15,7 +15,7 @@
</template>
<template #error>
<el-image
:src="require(`../manage/pages/assets/icons/${getFileIconPath(item.fileName ?? '')}`)"
:src="`/assets/icons/${getFileIconPath(item.fileName ?? '')}`"
fit="contain"
style="height: 100px; width: 100%; margin: 0 auto"
/>
@@ -24,11 +24,9 @@
</template>
<script lang="ts" setup>
import fs from 'fs-extra'
import mime from 'mime-types'
import path from 'path'
import { ref, onBeforeMount } from 'vue'
import { Loading } from '@element-plus/icons-vue'
import { onBeforeMount, ref } from 'vue'
import { getFileIconPath } from '@/manage/utils/common'
@@ -43,9 +41,9 @@ const props = defineProps<{
}>()
const createBase64Image = async () => {
const filePath = path.normalize(props.localPath)
const base64 = await fs.readFile(filePath, 'base64')
base64Image.value = `data:${mime.lookup(filePath) || 'image/png'};base64,${base64}`
const filePath = window.node.path.normalize(props.localPath)
const base64 = await window.node.fs.readFile(filePath, 'base64')
base64Image.value = `data:${window.node.mime.lookup(filePath) || 'image/png'};base64,${base64}`
}
onBeforeMount(async () => {

View File

@@ -1,23 +1,30 @@
<template>
<el-image :src="imageSource" fit="contain" style="height: 100px; width: 100%; margin: 0 auto">
<el-image
:src="imageSource"
fit="contain"
style="height: 100px; width: 100%; margin: 0 auto"
>
<template #placeholder>
<el-icon>
<Loading />
</el-icon>
</template>
<template #error>
<el-image :src="iconPath" fit="contain" style="height: 100px; width: 100%; margin: 0 auto" />
<el-image
:src="iconPath"
fit="contain"
style="height: 100px; width: 100%; margin: 0 auto"
/>
</template>
</el-image>
</template>
<script lang="ts" setup>
import { ref, onMounted, watch, computed } from 'vue'
import { Loading } from '@element-plus/icons-vue'
import { computed, onMounted, ref, watch } from 'vue'
import { getFileIconPath } from '@/manage/utils/common'
import { IRPCActionType } from '#/types/enum'
import { triggerRPC } from '@/utils/common'
const preSignedUrl = ref('')
@@ -36,13 +43,13 @@ const props = defineProps<{
const imageSource = computed(() => {
return props.isShowThumbnail && props.item.isImage
? preSignedUrl.value
: require(`../manage/pages/assets/icons/${getFileIconPath(props.item.fileName ?? '')}`)
: `/assets/icons/${getFileIconPath(props.item.fileName ?? '')}`
})
const iconPath = computed(() => require(`../manage/pages/assets/icons/${getFileIconPath(props.item.fileName ?? '')}`))
const iconPath = computed(() => `/assets/icons/${getFileIconPath(props.item.fileName ?? '')}`)
async function getUrl() {
preSignedUrl.value = await triggerRPC<any>(IRPCActionType.MANAGE_GET_PRE_SIGNED_URL, props.alias, props.config)
async function getUrl () {
preSignedUrl.value = await window.electron.triggerRPC<any>(IRPCActionType.MANAGE_GET_PRE_SIGNED_URL, props.alias, props.config)
}
watch(() => [props.url, props.item], getUrl, { deep: true })

View File

@@ -1,10 +1,9 @@
import { ElImage, ElIcon } from 'element-plus'
import { defineComponent, ref, onMounted, watch, computed } from 'vue'
import { Loading } from '@element-plus/icons-vue'
import { ElIcon, ElImage } from 'element-plus'
import { computed, defineComponent, onMounted, ref, watch } from 'vue'
import { getFileIconPath } from '@/manage/utils/common'
import { IRPCActionType } from '#/types/enum'
import { triggerRPC } from '@/utils/common'
export default defineComponent({
props: {
@@ -30,20 +29,20 @@ export default defineComponent({
}
},
setup(props) {
setup (props) {
const preSignedUrl = ref('')
const imageSource = computed(() => {
return props.isShowThumbnail && props.item.isImage
? preSignedUrl.value
: require(`../manage/pages/assets/icons/${getFileIconPath(props.item.fileName ?? '')}`)
: `/assets/icons/${getFileIconPath(props.item.fileName ?? '')}`
})
const iconPath = computed(() =>
require(`../manage/pages/assets/icons/${getFileIconPath(props.item.fileName ?? '')}`)
`/assets/icons/${getFileIconPath(props.item.fileName ?? '')}`
)
async function getUrl() {
preSignedUrl.value = await triggerRPC<any>(IRPCActionType.MANAGE_GET_PRE_SIGNED_URL, props.alias, props.config)
async function getUrl () {
preSignedUrl.value = await window.electron.triggerRPC<any>(IRPCActionType.MANAGE_GET_PRE_SIGNED_URL, props.alias, props.config)
}
watch(() => [props.url, props.item], getUrl, { deep: true })

View File

@@ -7,15 +7,25 @@
:model="waterMarkForm"
>
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_SKIP_PROCESS_EXT_LIST')">
<el-input v-model="skipProcessForm.skipProcessExtList" type="textarea" :autosize="{ minRows: 2, maxRows: 4 }" />
<el-input
v-model="skipProcessForm.skipProcessExtList"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4 }"
/>
<div class="text-xs text-gray-500">
{{ $T('UPLOAD_PAGE_IMAGE_PROCESS_SKIP_PROCESS_EXT_LIST_TIPS') }}
</div>
</el-form-item>
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ISADDWM')">
<el-switch v-model="waterMarkForm.isAddWatermark" :style="switchStyle" />
<el-switch
v-model="waterMarkForm.isAddWatermark"
:style="switchStyle"
/>
</el-form-item>
<el-form-item v-show="waterMarkForm.isAddWatermark" :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_WMTYPE')">
<el-form-item
v-show="waterMarkForm.isAddWatermark"
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_WMTYPE')"
>
<el-radio-group v-model="waterMarkForm.watermarkType">
<el-radio value="text">
{{ $T('UPLOAD_PAGE_IMAGE_PROCESS_WMTYPE_TEXT') }}
@@ -25,11 +35,23 @@
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-show="waterMarkForm.isAddWatermark" :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ISFULLSCREEN_WM')">
<el-switch v-model="waterMarkForm.isFullScreenWatermark" :style="switchStyle" />
<el-form-item
v-show="waterMarkForm.isAddWatermark"
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ISFULLSCREEN_WM')"
>
<el-switch
v-model="waterMarkForm.isFullScreenWatermark"
:style="switchStyle"
/>
</el-form-item>
<el-form-item v-show="waterMarkForm.isAddWatermark" :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_WMDEGREE')">
<el-input-number v-model="waterMarkForm.watermarkDegree" :step="1" />
<el-form-item
v-show="waterMarkForm.isAddWatermark"
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_WMDEGREE')"
>
<el-input-number
v-model="waterMarkForm.watermarkDegree"
:step="1"
/>
</el-form-item>
<el-form-item
v-show="waterMarkForm.isAddWatermark && waterMarkForm.watermarkType === 'text'"
@@ -43,14 +65,25 @@
>
<el-input v-model="waterMarkForm.watermarkFontPath" />
</el-form-item>
<el-form-item v-show="waterMarkForm.isAddWatermark" :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_WMRATIO')">
<el-input-number v-model="waterMarkForm.watermarkScaleRatio" :min="0" :max="1" :step="0.01" />
<el-form-item
v-show="waterMarkForm.isAddWatermark"
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_WMRATIO')"
>
<el-input-number
v-model="waterMarkForm.watermarkScaleRatio"
:min="0"
:max="1"
:step="0.01"
/>
</el-form-item>
<el-form-item
v-show="waterMarkForm.isAddWatermark && waterMarkForm.watermarkType === 'text'"
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_WMCOLOR')"
>
<el-color-picker v-model="waterMarkForm.watermarkColor" show-alpha />
<el-color-picker
v-model="waterMarkForm.watermarkColor"
show-alpha
/>
</el-form-item>
<el-form-item
v-show="waterMarkForm.isAddWatermark && waterMarkForm.watermarkType === 'image'"
@@ -58,76 +91,157 @@
>
<el-input v-model="waterMarkForm.watermarkImagePath" />
</el-form-item>
<el-form-item v-show="waterMarkForm.isAddWatermark" :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_WMPOSITION')">
<el-form-item
v-show="waterMarkForm.isAddWatermark"
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_WMPOSITION')"
>
<el-radio-group v-model="waterMarkForm.watermarkPosition">
<el-radio v-for="item in waterMarkPositionMap" :key="item[0]" :value="item[0]">
<el-radio
v-for="item in waterMarkPositionMap"
:key="item[0]"
:value="item[0]"
>
{{ item[1] }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ISREMOVEEXIF')">
<el-switch v-model="compressForm.isRemoveExif" :style="switchStyle" />
<el-switch
v-model="compressForm.isRemoveExif"
:style="switchStyle"
/>
</el-form-item>
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_QUALITY')">
<el-input-number v-model="compressForm.quality" :min="1" :max="100" :step="1" />
<el-input-number
v-model="compressForm.quality"
:min="1"
:max="100"
:step="1"
/>
</el-form-item>
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ISCONVERT')">
<el-switch v-model="compressForm.isConvert" :style="switchStyle" />
<el-switch
v-model="compressForm.isConvert"
:style="switchStyle"
/>
</el-form-item>
<el-form-item v-show="compressForm.isConvert" :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_CONVERTFORMAT')">
<el-select v-model="compressForm.convertFormat" :persistent="false" teleported>
<el-option v-for="item in availableFormat" :key="item" :label="item" :value="item" />
<el-form-item
v-show="compressForm.isConvert"
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_CONVERTFORMAT')"
>
<el-select
v-model="compressForm.convertFormat"
:persistent="false"
teleported
>
<el-option
v-for="item in availableFormat"
:key="item"
:label="item"
:value="item"
/>
</el-select>
</el-form-item>
<el-form-item v-show="compressForm.isConvert" :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_CONVERTFORMAT_SPECIFIC')">
<el-form-item
v-show="compressForm.isConvert"
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_CONVERTFORMAT_SPECIFIC')"
>
<el-input
v-model="formatConvertObj"
placeholder='{"jpg": "png", "png": "jpg"}'
placeholder="{&quot;jpg&quot;: &quot;png&quot;, &quot;png&quot;: &quot;jpg&quot;}"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4 }"
/>
</el-form-item>
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ISFLIP')">
<el-switch v-model="compressForm.isFlip" :style="switchStyle" />
<el-switch
v-model="compressForm.isFlip"
:style="switchStyle"
/>
</el-form-item>
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ISFLOP')">
<el-switch v-model="compressForm.isFlop" :style="switchStyle" />
<el-switch
v-model="compressForm.isFlop"
:style="switchStyle"
/>
</el-form-item>
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ISRESIZE')">
<el-switch v-model="compressForm.isReSize" :style="switchStyle" />
<el-switch
v-model="compressForm.isReSize"
:style="switchStyle"
/>
</el-form-item>
<el-form-item v-show="compressForm.isReSize" :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_RESIZEWIDTH')">
<el-input-number v-model="compressForm.reSizeWidth" :min="0" />
<el-form-item
v-show="compressForm.isReSize"
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_RESIZEWIDTH')"
>
<el-input-number
v-model="compressForm.reSizeWidth"
:min="0"
/>
</el-form-item>
<el-form-item v-show="compressForm.isReSize" :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_RESIZEHEIGHT')">
<el-input-number v-model="compressForm.reSizeHeight" :min="0" />
<el-form-item
v-show="compressForm.isReSize"
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_RESIZEHEIGHT')"
>
<el-input-number
v-model="compressForm.reSizeHeight"
:min="0"
/>
</el-form-item>
<el-form-item
v-show="compressForm.isReSize && compressForm.reSizeHeight! > 0 && compressForm.reSizeWidth === 0"
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_SKIPRESIZEOfSMALLIMG_HEIGHT')"
>
<el-switch v-model="compressForm.skipReSizeOfSmallImg" :style="switchStyle" />
<el-switch
v-model="compressForm.skipReSizeOfSmallImg"
:style="switchStyle"
/>
</el-form-item>
<el-form-item
v-show="compressForm.isReSize && compressForm.reSizeWidth! > 0 && compressForm.reSizeHeight === 0"
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_SKIPRESIZEOfSMALLIMG_WIDTH')"
>
<el-switch v-model="compressForm.skipReSizeOfSmallImg" :style="switchStyle" />
<el-switch
v-model="compressForm.skipReSizeOfSmallImg"
:style="switchStyle"
/>
</el-form-item>
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ISRESIZEBYPERCENT')">
<el-switch v-model="compressForm.isReSizeByPercent" :style="switchStyle" />
<el-switch
v-model="compressForm.isReSizeByPercent"
:style="switchStyle"
/>
</el-form-item>
<el-form-item v-show="compressForm.isReSizeByPercent" :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_RESIZEPERCENT')">
<el-input-number v-model="compressForm.reSizePercent" :min="0" />
<el-form-item
v-show="compressForm.isReSizeByPercent"
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_RESIZEPERCENT')"
>
<el-input-number
v-model="compressForm.reSizePercent"
:min="0"
/>
</el-form-item>
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ISROTATE')">
<el-switch v-model="compressForm.isRotate" :style="switchStyle" />
<el-switch
v-model="compressForm.isRotate"
:style="switchStyle"
/>
</el-form-item>
<el-form-item v-show="compressForm.isRotate" :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ROTATEDEGREE')">
<el-input-number v-model="compressForm.rotateDegree" :step="1" />
<el-form-item
v-show="compressForm.isRotate"
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ROTATEDEGREE')"
>
<el-input-number
v-model="compressForm.rotateDegree"
:step="1"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSaveConfig">
<el-button
type="primary"
@click="handleSaveConfig"
>
{{ $T('UPLOAD_PAGE_IMAGE_PROCESS_CONFIRM') }}
</el-button>
<el-button @click="closeDialog">
@@ -138,7 +252,7 @@
</template>
<script lang="ts" setup>
import { IBuildInCompressOptions, IBuildInWaterMarkOptions } from 'piclist'
import type { IBuildInCompressOptions, IBuildInWaterMarkOptions } from 'piclist'
import { onBeforeMount, reactive, ref, toRaw } from 'vue'
import { T as $T } from '@/i18n/index'
@@ -224,7 +338,7 @@ const skipProcessFormKeys = Object.keys(skipProcessForm) as (keyof typeof skipPr
const switchStyle = '--el-switch-on-color: #13ce66; --el-switch-off-color: #ff4949;'
function handleSaveConfig() {
function handleSaveConfig () {
let iformatConvertObj = {}
try {
iformatConvertObj = JSON.parse(formatConvertObj.value)
@@ -242,7 +356,7 @@ function handleSaveConfig() {
closeDialog()
}
async function initData() {
async function initData () {
const compress = await getConfig<any>(configPaths.buildIn.compress)
const watermark = await getConfig<any>(configPaths.buildIn.watermark)
const skipProcess = await getConfig<any>(configPaths.buildIn.skipProcess)
@@ -273,7 +387,7 @@ async function initData() {
}
}
function closeDialog() {
function closeDialog () {
imageProcessDialogVisible.value = false
}

View File

@@ -1,23 +1,30 @@
<template>
<el-image :src="imageSource" fit="contain" style="height: 100px; width: 100%; margin: 0 auto">
<el-image
:src="imageSource"
fit="contain"
style="height: 100px; width: 100%; margin: 0 auto"
>
<template #placeholder>
<el-icon>
<Loading />
</el-icon>
</template>
<template #error>
<el-image :src="iconPath" fit="contain" style="height: 100px; width: 100%; margin: 0 auto" />
<el-image
:src="iconPath"
fit="contain"
style="height: 100px; width: 100%; margin: 0 auto"
/>
</template>
</el-image>
</template>
<script lang="ts" setup>
import { ref, onMounted, watch, computed } from 'vue'
import { Loading } from '@element-plus/icons-vue'
import { computed, onMounted, ref, watch } from 'vue'
import { getAuthHeader } from '@/manage/utils/digestAuth'
import { getFileIconPath } from '@/manage/utils/common'
import { getAuthHeader } from '@/manage/utils/digestAuth'
import { formatEndpoint } from '#/utils/common'
const base64Url = ref('')
@@ -37,12 +44,12 @@ const props = defineProps<{
const imageSource = computed(() => {
return props.isShowThumbnail && props.item.isImage && success.value
? base64Url.value
: require(`../manage/pages/assets/icons/${getFileIconPath(props.item.fileName ?? '')}`)
: `/assets/icons/${getFileIconPath(props.item.fileName ?? '')}`
})
const iconPath = computed(() => require(`../manage/pages/assets/icons/${getFileIconPath(props.item.fileName ?? '')}`))
const iconPath = computed(() => `/assets/icons/${getFileIconPath(props.item.fileName ?? '')}`)
async function getWebdavHeader(key: string) {
async function getWebdavHeader (key: string) {
let headers = {} as any
if (props.config.authType === 'digest') {
const authHeader = await getAuthHeader(

View File

@@ -1,10 +1,9 @@
import { ElImage, ElIcon } from 'element-plus'
import { defineComponent, ref, onMounted, watch, computed } from 'vue'
import { Loading } from '@element-plus/icons-vue'
import { ElIcon, ElImage } from 'element-plus'
import { computed, defineComponent, onMounted, ref, watch } from 'vue'
import { getFileIconPath } from '@/manage/utils/common'
import { getAuthHeader } from '@/manage/utils/digestAuth'
import { formatEndpoint } from '#/utils/common'
export default defineComponent({
@@ -27,20 +26,20 @@ export default defineComponent({
}
},
setup(props) {
setup (props) {
const base64Url = ref('')
const success = ref(false)
const imageSource = computed(() => {
return props.isShowThumbnail && props.item.isImage && success.value
? base64Url.value
: require(`../manage/pages/assets/icons/${getFileIconPath(props.item.fileName ?? '')}`)
: `/assets/icons/${getFileIconPath(props.item.fileName ?? '')}`
})
const iconPath = computed(() =>
require(`../manage/pages/assets/icons/${getFileIconPath(props.item.fileName ?? '')}`)
`/assets/icons/${getFileIconPath(props.item.fileName ?? '')}`
)
async function getWebdavHeader(key: string) {
async function getWebdavHeader (key: string) {
let headers = {} as any
if (props.config.authType === 'digest') {
const authHeader = await getAuthHeader(

View File

@@ -5,12 +5,22 @@
:modal-append-to-body="false"
append-to-body
>
<el-input v-model="inputBoxValue" :placeholder="inputBoxOptions.placeholder" />
<el-input
v-model="inputBoxValue"
:placeholder="inputBoxOptions.placeholder"
/>
<template #footer>
<el-button round @click="handleInputBoxCancel">
<el-button
round
@click="handleInputBoxCancel"
>
{{ $T('CANCEL') }}
</el-button>
<el-button type="primary" round @click="handleInputBoxConfirm">
<el-button
type="primary"
round
@click="handleInputBoxConfirm"
>
{{ $T('CONFIRM') }}
</el-button>
</template>
@@ -18,14 +28,13 @@
</template>
<script lang="ts" setup>
import { ipcRenderer, IpcRendererEvent } from 'electron'
import { ref, reactive, onBeforeUnmount, onBeforeMount } from 'vue'
import type { IpcRendererEvent } from 'electron'
import { onBeforeMount, onBeforeUnmount, reactive, ref } from 'vue'
import { T as $T } from '@/i18n/index'
import $bus from '@/utils/bus'
import { sendToMain } from '@/utils/common'
import { SHOW_INPUT_BOX, SHOW_INPUT_BOX_RESPONSE } from '#/events/constants'
import { IShowInputBoxOption } from '#/types/types'
const inputBoxValue = ref('')
const showInputBoxVisible = ref(false)
@@ -35,36 +44,36 @@ const inputBoxOptions = reactive({
})
onBeforeMount(() => {
ipcRenderer.on(SHOW_INPUT_BOX, ipcEventHandler)
window.electron.ipcRendererOn(SHOW_INPUT_BOX, ipcEventHandler)
$bus.on(SHOW_INPUT_BOX, initInputBoxValue)
})
function ipcEventHandler(_: IpcRendererEvent, options: IShowInputBoxOption) {
function ipcEventHandler (_: IpcRendererEvent, options: IShowInputBoxOption) {
initInputBoxValue(options)
}
function initInputBoxValue(options: IShowInputBoxOption) {
function initInputBoxValue (options: IShowInputBoxOption) {
inputBoxValue.value = options.value || ''
inputBoxOptions.title = options.title || ''
inputBoxOptions.placeholder = options.placeholder || ''
showInputBoxVisible.value = true
}
function handleInputBoxCancel() {
function handleInputBoxCancel () {
// TODO: RPCServer
showInputBoxVisible.value = false
sendToMain(SHOW_INPUT_BOX, '')
window.electron.sendToMain(SHOW_INPUT_BOX, '')
$bus.emit(SHOW_INPUT_BOX_RESPONSE, '')
}
function handleInputBoxConfirm() {
function handleInputBoxConfirm () {
showInputBoxVisible.value = false
sendToMain(SHOW_INPUT_BOX, inputBoxValue.value)
window.electron.sendToMain(SHOW_INPUT_BOX, inputBoxValue.value)
$bus.emit(SHOW_INPUT_BOX_RESPONSE, inputBoxValue.value)
}
onBeforeUnmount(() => {
ipcRenderer.removeListener(SHOW_INPUT_BOX, ipcEventHandler)
window.electron.ipcRendererRemoveListener(SHOW_INPUT_BOX, ipcEventHandler)
$bus.off(SHOW_INPUT_BOX)
})
</script>

View File

@@ -1,6 +1,10 @@
<template>
<div class="toolbox-handler">
<ElButton type="primary" :link="true" @click="() => props.handler(value)">
<ElButton
type="primary"
:link="true"
@click="() => props.handler(value)"
>
{{ props.handlerText }}
</ElButton>
</div>

View File

@@ -1,5 +1,8 @@
<template>
<el-icon :color="color" class="toolbox-status-icon">
<el-icon
:color="color"
class="toolbox-status-icon"
>
<template v-if="props.status === IToolboxItemCheckStatus.SUCCESS">
<SuccessFilled />
</template>

View File

@@ -1,14 +1,12 @@
import { IRPCActionType } from 'root/src/universal/types/enum'
import { onMounted, onUnmounted } from 'vue'
import { sendRPC } from '@/utils/common'
import { IRPCActionType } from 'root/src/universal/types/enum'
export function useATagClick() {
export function useATagClick () {
const handleATagClick = (e: MouseEvent) => {
if (e.target instanceof HTMLAnchorElement) {
if (e.target.href) {
e.preventDefault()
sendRPC(IRPCActionType.OPEN_URL, e.target.href)
window.electron.sendRPC(IRPCActionType.OPEN_URL, e.target.href)
}
}
}

View File

@@ -1,30 +0,0 @@
import { ipcRenderer } from 'electron'
import { onUnmounted } from 'vue'
import { IRPCActionType } from '#/types/enum'
export const useIPCOn = (channel: string, listener: IpcRendererListener) => {
ipcRenderer.on(channel, listener)
onUnmounted(() => {
ipcRenderer.removeListener(channel, listener)
})
}
export const useIPCOnce = (channel: string, listener: IpcRendererListener) => {
ipcRenderer.once(channel, listener)
onUnmounted(() => {
ipcRenderer.removeListener(channel, listener)
})
}
/**
* will auto removeListener when component unmounted
*/
export const useIPC = () => {
return {
on: (channel: IRPCActionType, listener: IpcRendererListener) => useIPCOn(channel, listener),
once: (channel: IRPCActionType, listener: IpcRendererListener) => useIPCOnce(channel, listener)
}
}

View File

@@ -1,4 +1,5 @@
import { inject } from 'vue'
import { storeKey } from '@/store'
export const useStore = () => {

View File

@@ -1,54 +1,45 @@
import { ipcRenderer } from 'electron'
import { ObjectAdapter, I18n } from '@picgo/i18n'
import { sendRPC, sendRpcSync } from '@/utils/common'
import { ILocales, ILocalesKey } from 'root/src/universal/types/i18n'
import { updatePageReloadCount } from '@/utils/global'
import { SET_CURRENT_LANGUAGE } from '#/events/constants'
import { builtinI18nList } from '#/i18n'
import { IRPCActionType } from '#/types/enum'
import { updatePageReloadCount } from '@/utils/global'
import { II18nItem, IStringKeyMap } from '#/types/types'
export class I18nManager {
#i18n: I18n | null = null
#i18nFileList: II18nItem[] = builtinI18nList
constructor() {
constructor () {
this.#getCurrentLanguage()
this.#getLanguageList()
ipcRenderer.on(SET_CURRENT_LANGUAGE, (_, lang: string, locales: ILocales) => {
window.electron.ipcRendererOn(SET_CURRENT_LANGUAGE, (_, lang: string, locales: ILocales) => {
this.#setLocales(lang, locales)
updatePageReloadCount()
})
}
#getLanguageList() {
this.#i18nFileList = sendRpcSync(IRPCActionType.GET_LANGUAGE_LIST)
#getLanguageList () {
this.#i18nFileList = window.electron.sendRpcSync(IRPCActionType.GET_LANGUAGE_LIST)
}
#getCurrentLanguage() {
const [lang, locales] = sendRpcSync(IRPCActionType.GET_CURRENT_LANGUAGE)
#getCurrentLanguage () {
const [lang, locales] = window.electron.sendRpcSync(IRPCActionType.GET_CURRENT_LANGUAGE)
this.#setLocales(lang, locales)
}
#setLocales(lang: string, locales: ILocales) {
const objectAdapter = new ObjectAdapter({
[lang]: locales
})
this.#i18n = new I18n({
adapter: objectAdapter,
defaultLanguage: lang
})
#setLocales (lang: string, locales: ILocales) {
window.i18n.setLocales(lang, locales)
}
T(key: ILocalesKey, args: IStringKeyMap = {}): string {
return this.#i18n?.translate(key, args) || key
T (key: ILocalesKey, args: IStringKeyMap = {}): string {
return window.i18n?.translate(key, args) || key
}
setCurrentLanguage(lang: string) {
sendRPC(IRPCActionType.SET_CURRENT_LANGUAGE, lang)
setCurrentLanguage (lang: string) {
window.electron.sendRPC(IRPCActionType.SET_CURRENT_LANGUAGE, lang)
}
get languageList() {
get languageList () {
return this.#i18nFileList
}
}

12
src/renderer/index.html Normal file
View File

@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PicList</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="./main.ts"></script>
</body>
</html>

View File

@@ -1,8 +1,13 @@
<template>
<div id="main-page">
<div class="fake-title-bar">
<div class="fake-title-bar__title">PicList - {{ version }}</div>
<div v-if="osGlobal !== 'darwin'" class="handle-bar">
<div class="fake-title-bar__title">
PicList - {{ version }}
</div>
<div
v-if="osGlobal !== 'darwin'"
class="handle-bar"
>
<el-icon
class="minus"
:color="isAlwaysOnTop ? '#409EFF' : '#fff'"
@@ -12,13 +17,30 @@
>
<ArrowUpBold />
</el-icon>
<el-icon class="minus" color="#fff" size="20" style="margin-right: 10px" @click="minimizeWindow">
<el-icon
class="minus"
color="#fff"
size="20"
style="margin-right: 10px"
@click="minimizeWindow"
>
<SemiSelect />
</el-icon>
<el-icon class="plus" color="orange" size="20" style="margin-right: 10px" @click="openMiniWindow">
<el-icon
class="plus"
color="orange"
size="20"
style="margin-right: 10px"
@click="openMiniWindow"
>
<ArrowDownBold />
</el-icon>
<el-icon class="close" color="#fff" size="20" @click="closeWindow">
<el-icon
class="close"
color="#fff"
size="20"
@click="closeWindow"
>
<CloseBold />
</el-icon>
</div>
@@ -32,9 +54,17 @@
status="success"
class="progress-bar"
/>
<el-row style="padding-top: 22px" class="main-content">
<el-row
style="padding-top: 22px"
class="main-content"
>
<el-col class="side-bar-menu">
<el-menu class="picgo-sidebar" :default-active="defaultActive" :unique-opened="true" @select="handleSelect">
<el-menu
class="picgo-sidebar"
:default-active="defaultActive"
:unique-opened="true"
@select="handleSelect"
>
<el-menu-item :index="routerConfig.UPLOAD_PAGE">
<el-icon>
<UploadFilled />
@@ -53,7 +83,12 @@
</el-icon>
<span>{{ $T('GALLERY') }}</span>
</el-menu-item>
<el-sub-menu index="sub-menu" :show-timeout="0" :hide-timeout="0" :popper-offset="0">
<el-sub-menu
index="sub-menu"
:show-timeout="0"
:hide-timeout="0"
:popper-offset="0"
>
<template #title>
<el-icon>
<Menu />
@@ -89,13 +124,24 @@
<span>{{ $T('MANUAL') }}</span>
</el-menu-item>
</el-menu>
<el-icon class="info-window" @click="openMenu">
<el-icon
class="info-window"
@click="openMenu"
>
<InfoFilled />
</el-icon>
</el-col>
<el-col :span="21" :offset="3" style="height: 100%" class="main-wrapper">
<el-col
:span="21"
:offset="3"
style="height: 100%"
class="main-wrapper"
>
<router-view v-slot="{ Component }">
<transition name="picgo-fade" mode="out-in">
<transition
name="picgo-fade"
mode="out-in"
>
<keep-alive :include="keepAlivePages">
<component :is="Component" />
</keep-alive>
@@ -113,10 +159,25 @@
lock-scroll
append-to-body
>
<el-form label-position="left" label-width="70px" size="small">
<el-form
label-position="left"
label-width="70px"
size="small"
>
<el-form-item :label="$T('CHOOSE_PICBED')">
<el-select v-model="choosedPicBedForQRCode" multiple collapse-tags :persistent="false" teleported>
<el-option v-for="item in picBedGlobal" :key="item.type" :label="item.name" :value="item.type" />
<el-select
v-model="choosedPicBedForQRCode"
multiple
collapse-tags
:persistent="false"
teleported
>
<el-option
v-for="item in picBedGlobal"
:key="item.type"
:label="item.name"
:value="item.type"
/>
</el-select>
<el-button
v-show="choosedPicBedForQRCode.length > 0"
@@ -130,7 +191,11 @@
</el-form-item>
</el-form>
<div class="qrcode-container">
<qrcode-vue v-show="choosedPicBedForQRCode.length > 0" :size="280" :value="picBedConfigString" />
<qrcode-vue
v-show="choosedPicBedForQRCode.length > 0"
:size="280"
:value="picBedConfigString"
/>
</div>
</el-dialog>
<input-box-dialog />
@@ -139,40 +204,37 @@
<script lang="ts" setup>
import {
Tools,
UploadFilled,
PictureFilled,
Menu,
Share,
InfoFilled,
SemiSelect,
ArrowDownBold,
ArrowUpBold,
CloseBold,
PieChart,
InfoFilled,
Link,
ArrowUpBold
Menu,
PictureFilled,
PieChart,
SemiSelect,
Share,
Tools,
UploadFilled
} from '@element-plus/icons-vue'
import { ipcRenderer, IpcRendererEvent, clipboard } from 'electron'
import type { IpcRendererEvent } from 'electron'
import { ElMessage as $message, ElMessageBox } from 'element-plus'
import pick from 'lodash/pick'
import { pick } from 'lodash-es'
import QrcodeVue from 'qrcode.vue'
import { ref, onBeforeUnmount, Ref, onBeforeMount, watch, nextTick, reactive } from 'vue'
import pkg from 'root/package.json'
import { nextTick, onBeforeMount, onBeforeUnmount, reactive, Ref, ref, watch } from 'vue'
import { onBeforeRouteUpdate, useRouter } from 'vue-router'
import InputBoxDialog from '@/components/InputBoxDialog.vue'
import { T as $T } from '@/i18n/index'
import * as config from '@/router/config'
import { getConfig, saveConfig } from '@/utils/dataSender'
import { sendRPC } from '@/utils/common'
import { osGlobal, picBedGlobal, updatePicBedGlobal } from '@/utils/global'
import { SHOW_MAIN_PAGE_QRCODE } from '#/events/constants'
import { configPaths, manualPageOpenType } from '#/utils/configPaths'
import { II18nLanguage, IRPCActionType } from '#/types/enum'
import { configPaths, manualPageOpenType } from '#/utils/configPaths'
import pkg from 'root/package.json'
const version = ref(process.env.NODE_ENV === 'production' ? pkg.version : 'Dev')
const version = ref(pkg.version)
const routerConfig = reactive(config)
const defaultActive = ref(routerConfig.UPLOAD_PAGE)
const $router = useRouter()
@@ -188,15 +250,18 @@ const keepAlivePages = $router
const isShowprogress = ref(false)
const progress = ref(0)
const qrCodeHandler = () => {
qrcodeVisible.value = true
}
const uploadProcessHandler = (_event: IpcRendererEvent, data: { progress: number }) => {
isShowprogress.value = data.progress !== 100 && data.progress !== 0
progress.value = data.progress
}
onBeforeMount(() => {
updatePicBedGlobal()
ipcRenderer.on(SHOW_MAIN_PAGE_QRCODE, () => {
qrcodeVisible.value = true
})
ipcRenderer.on('updateProgress', (_event: IpcRendererEvent, data: { progress: number }) => {
isShowprogress.value = data.progress !== 100 && data.progress !== 0
progress.value = data.progress
})
window.electron.ipcRendererOn(SHOW_MAIN_PAGE_QRCODE, qrCodeHandler)
window.electron.ipcRendererOn('updateProgress', uploadProcessHandler)
})
watch(
@@ -218,9 +283,9 @@ const handleSelect = async (index: string) => {
if (index === routerConfig.DocumentPage) {
const manualPageOpenSetting = await getConfig<manualPageOpenType>(configPaths.settings.manualPageOpen)
const lang = (await getConfig(configPaths.settings.language)) || II18nLanguage.ZH_CN
const openManual = () => sendRPC(IRPCActionType.OPEN_MANUAL_WINDOW)
const openManual = () => window.electron.sendRPC(IRPCActionType.OPEN_MANUAL_WINDOW)
const openExternal = () =>
sendRPC(
window.electron.sendRPC(
IRPCActionType.OPEN_URL,
lang === II18nLanguage.ZH_CN ? 'https://piclist.cn/app.html' : 'https://piclist.cn/en/app.html'
)
@@ -261,30 +326,30 @@ const handleSelect = async (index: string) => {
}
}
function minimizeWindow() {
sendRPC(IRPCActionType.MINIMIZE_WINDOW)
function minimizeWindow () {
window.electron.sendRPC(IRPCActionType.MINIMIZE_WINDOW)
}
function closeWindow() {
sendRPC(IRPCActionType.CLOSE_WINDOW)
function closeWindow () {
window.electron.sendRPC(IRPCActionType.CLOSE_WINDOW)
}
function openMenu() {
sendRPC(IRPCActionType.SHOW_MAIN_PAGE_MENU)
function openMenu () {
window.electron.sendRPC(IRPCActionType.SHOW_MAIN_PAGE_MENU)
}
function openMiniWindow() {
sendRPC(IRPCActionType.OPEN_MINI_WINDOW)
function openMiniWindow () {
window.electron.sendRPC(IRPCActionType.OPEN_MINI_WINDOW)
}
function handleCopyPicBedConfig() {
clipboard.writeText(picBedConfigString.value)
function handleCopyPicBedConfig () {
window.electron.clipboard.writeText(picBedConfigString.value)
$message.success($T('COPY_PICBED_CONFIG_SUCCEED'))
}
function setAlwaysOnTop() {
function setAlwaysOnTop () {
isAlwaysOnTop.value = !isAlwaysOnTop.value
sendRPC(IRPCActionType.MAIN_WINDOW_ON_TOP)
window.electron.sendRPC(IRPCActionType.MAIN_WINDOW_ON_TOP)
}
onBeforeRouteUpdate(async to => {
@@ -296,8 +361,8 @@ onBeforeRouteUpdate(async to => {
})
onBeforeUnmount(() => {
ipcRenderer.removeAllListeners(SHOW_MAIN_PAGE_QRCODE)
ipcRenderer.removeAllListeners('updateProgress')
window.electron.ipcRendererRemoveListener(SHOW_MAIN_PAGE_QRCODE, qrCodeHandler)
window.electron.ipcRendererRemoveListener('updateProgress', uploadProcessHandler)
})
</script>
<script lang="ts">

49
src/renderer/main.ts Normal file
View File

@@ -0,0 +1,49 @@
import 'element-plus/dist/index.css'
import 'vue3-photo-preview/dist/index.css'
import 'video.js/dist/video-js.css'
import 'highlight.js/styles/stackoverflow-light.css'
import 'highlight.js/lib/common'
import hljsVuePlugin from '@highlightjs/vue-plugin'
import VueVideoPlayer from '@videojs-player/vue'
import ElementUI from 'element-plus'
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
import { createApp } from 'vue'
import VueLazyLoad from 'vue3-lazyload'
import vue3PhotoPreview from 'vue3-photo-preview'
import App from '@/App.vue'
import { T } from '@/i18n/index'
import router from '@/router'
import { store } from '@/store'
import { initTalkingData } from '@/utils/analytic'
import db from '@/utils/db'
window.electron.setVisualZoomLevelLimits(1, 1)
const app = createApp(App)
app.config.globalProperties.$$db = db
app.config.globalProperties.$T = T
app.config.globalProperties.triggerRPC = window.electron.triggerRPC
app.config.globalProperties.sendRPC = window.electron.sendRPC
app.config.globalProperties.sendToMain = window.electron.sendToMain
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
app.use(VueLazyLoad, {
loading: 'file://loading.jpg',
error: 'file://unknown-file-type.svg',
delay: 500
})
app.use(ElementUI)
app.use(router)
app.use(store)
app.use(vue3PhotoPreview)
app.use(pinia)
app.use(hljsVuePlugin)
app.use(VueVideoPlayer)
app.mount('#app')
initTalkingData()

View File

@@ -2,10 +2,21 @@
<el-form-item>
<template #label>
<span style="position: absolute; left: 0">
<span v-for="(segment, index) in segments" :key="index" :style="segment.style">
<span
v-for="(segment, index) in segments"
:key="index"
:style="segment.style"
>
{{ segment.text }}
</span>
<el-tooltip v-if="tooltip" :content="tooltip" effect="dark" placement="right" :persistent="false" teleported>
<el-tooltip
v-if="tooltip"
:content="tooltip"
effect="dark"
placement="right"
:persistent="false"
teleported
>
<el-icon>
<InfoFilled />
</el-icon>

File diff suppressed because it is too large Load Diff

View File

@@ -16,9 +16,27 @@
lazy
>
<el-row>
<el-col v-for="item in sortedAllConfigAliasMap" :key="item" :xs="24" :sm="12" :md="8" :lg="6" :xl="4">
<el-card class="box-card" style="margin: 10px 0" shadow="hover">
<el-popover placement="top" :width="300" trigger="click" :persistent="false" teleported>
<el-col
v-for="item in sortedAllConfigAliasMap"
:key="item"
:xs="24"
:sm="12"
:md="8"
:lg="6"
:xl="4"
>
<el-card
class="box-card"
style="margin: 10px 0"
shadow="hover"
>
<el-popover
placement="top"
:width="300"
trigger="click"
:persistent="false"
teleported
>
<el-table
:data="formObjToTableData(item.config)"
style="width: 100%"
@@ -26,8 +44,15 @@
:header-cell-style="{ 'text-align': 'center' }"
:cell-style="{ 'text-align': 'center' }"
>
<el-table-column prop="key" :label="$T('MANAGE_LOGIN_PAGE_PANE_KEY_NAME')" width="100" />
<el-table-column prop="value" :label="$T('MANAGE_LOGIN_PAGE_PANE_KEY_VALUE')" />
<el-table-column
prop="key"
:label="$T('MANAGE_LOGIN_PAGE_PANE_KEY_NAME')"
width="100"
/>
<el-table-column
prop="value"
:label="$T('MANAGE_LOGIN_PAGE_PANE_KEY_VALUE')"
/>
</el-table>
<template #reference>
<el-button
@@ -40,7 +65,10 @@
"
>
<template #icon>
<img :src="require(`./assets/${item.picBedName}.webp`)" style="width: 25px; height: 25px" />
<img
:src="`/assets/${item.picBedName}.webp`"
style="width: 25px; height: 25px"
>
</template>
<el-tooltip
effect="light"
@@ -55,13 +83,23 @@
</el-button>
</template>
</el-popover>
<br />
<br />
<br>
<br>
<el-button-group>
<el-button type="primary" :icon="Pointer" plain @click="handleConfigClick(item)">
<el-button
type="primary"
:icon="Pointer"
plain
@click="handleConfigClick(item)"
>
{{ $T('MANAGE_LOGIN_PAGE_PANE_ENTER') }}
</el-button>
<el-button type="warning" :icon="Delete" plain @click="handleConfigRemove(item.alias)">
<el-button
type="warning"
:icon="Delete"
plain
@click="handleConfigRemove(item.alias)"
>
{{ $T('MANAGE_LOGIN_PAGE_PANE_DELETE') }}
</el-button>
</el-button-group>
@@ -78,11 +116,23 @@
lazy
style="width: 100%; overflow-y: scroll; height: calc(100vh - 50px)"
>
<el-alert :title="item.explain" type="info" show-icon center :closable="false" />
<el-alert center :closable="false">
<el-alert
:title="item.explain"
type="info"
show-icon
center
:closable="false"
/>
<el-alert
center
:closable="false"
>
<div>
{{ item.referenceText }}
<a style="color: blue; cursor: pointer" @click="handleReferenceClick(item.refLink)">{{ item.refLink }}</a>
<a
style="color: blue; cursor: pointer"
@click="handleReferenceClick(item.refLink)"
>{{ item.refLink }}</a>
</div>
</el-alert>
<el-form
@@ -156,20 +206,41 @@
>
{{ $T('MANAGE_LOGIN_PAGE_PANE_IMPORT') }}
<template #dropdown>
<el-dropdown-item v-for="i in currentAliasList" :key="i" @click="handleConfigImport(i)">
<el-dropdown-item
v-for="i in currentAliasList"
:key="i"
@click="handleConfigImport(i)"
>
{{ i }}
</el-dropdown-item>
</template>
</el-dropdown>
<el-button type="primary" style="margin-left: 10vw" :icon="Edit" plain @click="handleConfigChange(item.icon)">
<el-button
type="primary"
style="margin-left: 10vw"
:icon="Edit"
plain
@click="handleConfigChange(item.icon)"
>
{{ $T('MANAGE_LOGIN_PAGE_PANE_SAVE') }}
</el-button>
<el-button type="danger" style="margin-left: 10vw" :icon="Delete" plain @click="handleConfigReset(item.icon)">
<el-button
type="danger"
style="margin-left: 10vw"
:icon="Delete"
plain
@click="handleConfigReset(item.icon)"
>
{{ $T('MANAGE_LOGIN_PAGE_PANE_RESET') }}
</el-button>
</div>
<br />
<el-alert :title="$T('MANAGE_LOGIN_PAGE_PANE_TABLE_TITLE')" type="success" center :closable="false" />
<br>
<el-alert
:title="$T('MANAGE_LOGIN_PAGE_PANE_TABLE_TITLE')"
type="success"
center
:closable="false"
/>
<el-table
:data="dataForTable"
style="width: 100%; margin-top: 10px"
@@ -192,20 +263,19 @@
</template>
<script lang="ts" setup>
import { shell } from 'electron'
import { Delete, Edit, InfoFilled, Pointer } from '@element-plus/icons-vue'
import { ElMessage, ElMessageBox, ElNotification } from 'element-plus'
import { Delete, Edit, Pointer, InfoFilled } from '@element-plus/icons-vue'
import { reactive, ref, onMounted, computed } from 'vue'
import { computed, onMounted, reactive, ref } from 'vue'
import { useRouter } from 'vue-router'
import { useManageStore } from '@/manage/store/manageStore'
import { supportedPicBedList } from '@/manage/utils/constants'
import { getConfig, saveConfig, removeConfig } from '@/manage/utils/dataSender'
import { formObjToTableData } from '@/manage/utils/common'
import { getConfig as getPicBedsConfig } from '@/utils/dataSender'
import { T as $T } from '@/i18n'
import { useManageStore } from '@/manage/store/manageStore'
import { formObjToTableData } from '@/manage/utils/common'
import { supportedPicBedList } from '@/manage/utils/constants'
import { getConfig, removeConfig, saveConfig } from '@/manage/utils/dataSender'
import { getConfig as getPicBedsConfig } from '@/utils/dataSender'
import { IRPCActionType } from '#/types/enum'
import { IStringKeyMap, IUploaderConfigListItem } from '#/types/types'
import { formatEndpoint, isNeedToShorten, safeSliceF } from '#/utils/common'
const manageStore = useManageStore()
@@ -228,7 +298,7 @@ const sortedAllConfigAliasMap = computed(() => {
const importedNewConfig: IStringKeyMap = {}
function ruleMap(options: IStringKeyMap) {
function ruleMap (options: IStringKeyMap) {
const rule: any = {}
Object.keys(options).forEach(key => {
const item = options[key].options
@@ -246,13 +316,13 @@ function ruleMap(options: IStringKeyMap) {
return rule
}
function getDataForTable() {
function getDataForTable () {
for (const key in existingConfiguration) {
dataForTable.push({ ...(existingConfiguration[key] as IStringKeyMap) })
}
}
async function getExistingConfig(name: string) {
async function getExistingConfig (name: string) {
if (name === 'login') {
getAllConfigAliasArray()
return
@@ -278,11 +348,11 @@ async function getExistingConfig(name: string) {
handleConfigImport(currentAliasList[0])
}
function getAliasList() {
function getAliasList () {
return Object.values(existingConfiguration).map(item => item.alias)
}
async function handleConfigChange(name: string) {
async function handleConfigChange (name: string) {
const aliasList = getAliasList()
const allKeys = Object.keys(supportedPicBedList[name].configOptions)
const resultMap: IStringKeyMap = {}
@@ -456,7 +526,7 @@ const handleCellClick = (row: any, column: any) => {
ElMessage.success(`${$T('MANAGE_LOGIN_PAGE_PANE_CONFIG_CHANGE_COPY_SUCCESS')}${row[column.property]}`)
}
const handleReferenceClick = (url: string) => shell.openExternal(url)
const handleReferenceClick = (url: string) => window.electron.sendRPC(IRPCActionType.OPEN_URL, url)
const handleConfigClick = async (item: any) => {
const alias = item.alias
@@ -474,7 +544,7 @@ const handleConfigClick = async (item: any) => {
})
}
function handleConfigImport(alias: string) {
function handleConfigImport (alias: string) {
const selectedConfig = existingConfiguration[alias]
if (!selectedConfig) return
@@ -485,7 +555,7 @@ function handleConfigImport(alias: string) {
})
}
async function getCurrentConfigList() {
async function getCurrentConfigList () {
await manageStore.refreshConfig()
const configList = (await getPicBedsConfig<any>('uploader')) ?? {}
const pbList = [
@@ -526,18 +596,18 @@ async function getCurrentConfigList() {
await getAllConfigAliasArray()
}
function isImported(alias: string) {
function isImported (alias: string) {
return Object.values(allConfigAliasMap).some(item => item.alias === alias)
}
function initArray(arrayT: string | string[], defaultValue: string[]) {
function initArray (arrayT: string | string[], defaultValue: string[]) {
if (!Array.isArray(arrayT)) {
arrayT = arrayT ? [arrayT] : defaultValue
}
return arrayT
}
async function transUpToManage(config: IUploaderConfigListItem, picBedName: string, autoImportPicBed: string[]) {
async function transUpToManage (config: IUploaderConfigListItem, picBedName: string, autoImportPicBed: string[]) {
const alias = `${
picBedName === 'webdavplist'
? 'webdav'

View File

@@ -2,15 +2,22 @@
<div class="layout">
<div class="layout__menu">
<div class="layout__menu__button">
<span class="layout__menu__button__item" @click="openPicBedUrl">
<span
class="layout__menu__button__item"
@click="openPicBedUrl"
>
<img
:src="require(`./assets/${currentPagePicBedConfig.picBedName}.webp`)"
:src="`/assets/${currentPagePicBedConfig.picBedName}.webp`"
class="layout__menu__button__item__icon"
/>
>
{{ supportedPicBedList[currentPagePicBedConfig.picBedName].name }}
</span>
</div>
<el-divider content-position="left" class="layout__menu__button__divider" border-style="none">
<el-divider
content-position="left"
class="layout__menu__button__divider"
border-style="none"
>
<span style="font-size: 14px; color: #909399">
{{ menuTitleMap[currentPicBedName] }}
<el-tooltip
@@ -42,7 +49,11 @@
active-text-color="#409EFF"
@select="handleSelectMenu"
>
<el-menu-item v-for="item of bucketNameList" :key="item" :index="item">
<el-menu-item
v-for="item of bucketNameList"
:key="item"
:index="item"
>
<span
class="layout__menu__list__item"
:style="{
@@ -77,8 +88,15 @@
</span>
</el-menu-item>
</el-menu>
<el-menu class="layout__menu__setting" style="width: 120px">
<el-menu-item index="changePicBed" style="height: 40px" @click="switchPicBed('main')">
<el-menu
class="layout__menu__setting"
style="width: 120px"
>
<el-menu-item
index="changePicBed"
style="height: 40px"
@click="switchPicBed('main')"
>
<span class="layout__menu__setting__item">
<el-icon class="layout__menu__setting__item__icon">
<HomeFilled />
@@ -86,7 +104,11 @@
{{ $T('MANAGE_MAIN_PAGE_BACK_TO_HOME') }}
</span>
</el-menu-item>
<el-menu-item index="changePicBed" style="height: 40px" @click="changePicBed">
<el-menu-item
index="changePicBed"
style="height: 40px"
@click="changePicBed"
>
<span class="layout__menu__setting__item">
<el-icon class="layout__menu__setting__item__icon">
<Switch />
@@ -94,7 +116,11 @@
{{ $T('MANAGE_MAIN_PAGE_SWITCH_PICBED') }}
</span>
</el-menu-item>
<el-menu-item index="bucketPageSetting" style="height: 40px" @click="openBucketPageSetting">
<el-menu-item
index="bucketPageSetting"
style="height: 40px"
@click="openBucketPageSetting"
>
<span class="layout__menu__setting__item">
<el-icon class="layout__menu__setting__item__icon">
<Tools />
@@ -104,17 +130,31 @@
</el-menu-item>
</el-menu>
</div>
<div class="layout__content" style="height: 100%; background-color: transparent; flex: 1; width: 0">
<div
class="layout__content"
style="height: 100%; background-color: transparent; flex: 1; width: 0"
>
<router-view />
</div>
<el-dialog v-model="picBedSwitchDialogVisible" top="30vh" append-to-body>
<el-dialog
v-model="picBedSwitchDialogVisible"
top="30vh"
append-to-body
>
<div
class="choice-cos"
style="display: flex; flex-direction: row; flex-wrap: wrap; justify-content: space-around"
>
<el-card shadow="hover">
<div style="text-align: center; display: flex; flex-direction: column" @click="switchPicBed('main')">
<el-icon color="red" size="25px" style="margin: 0 auto">
<div
style="text-align: center; display: flex; flex-direction: column"
@click="switchPicBed('main')"
>
<el-icon
color="red"
size="25px"
style="margin: 0 auto"
>
<ChromeFilled />
</el-icon>
<span style="font-size: 13px; margin-top: 5px; color: red">
@@ -122,10 +162,17 @@
</span>
</div>
</el-card>
<el-card v-for="item in allPicBedConfigure" :key="item" shadow="hover">
<div style="text-align: center; display: flex; flex-direction: column" @click="switchPicBed(item.alias)">
<el-card
v-for="item in allPicBedConfigure"
:key="item"
shadow="hover"
>
<div
style="text-align: center; display: flex; flex-direction: column"
@click="switchPicBed(item.alias)"
>
<el-image
:src="require(`./assets/${item.picBedName}.webp`)"
:src="`/assets/${item.picBedName}.webp`"
class="layout__addNewBucket__icon"
style="width: 25px; height: 25px; margin: 0 auto"
/>
@@ -136,7 +183,11 @@
</el-card>
</div>
</el-dialog>
<el-drawer v-model="nweBucketDrawerVisible" class="layout__addNewBucket" append-to-body>
<el-drawer
v-model="nweBucketDrawerVisible"
class="layout__addNewBucket"
append-to-body
>
<el-form
label-position="top"
require-asterisk-position="right"
@@ -147,7 +198,7 @@
>
<div style="position: relative; height: 10vh; width: 100%">
<el-image
:src="require(`./assets/${currentPicBedName}.webp`)"
:src="`/assets/${currentPicBedName}.webp`"
class="layout__addNewBucket__icon"
style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%)"
/>
@@ -162,7 +213,7 @@
<el-input
v-if="
newBucketConfig[currentPicBedName].configOptions[option].component === 'input' &&
currentPicBedName !== 'tcyun'
currentPicBedName !== 'tcyun'
"
v-model.trim="newBucketConfigResult[currentPicBedName + '.' + option]"
:placeholder="newBucketConfig[currentPicBedName].configOptions[option].placeholder"
@@ -170,7 +221,7 @@
<el-input
v-if="
currentPicBedName === 'tcyun' &&
newBucketConfig[currentPicBedName].configOptions[option].component === 'input'
newBucketConfig[currentPicBedName].configOptions[option].component === 'input'
"
v-model.trim="newBucketConfigResult[currentPicBedName + '.' + option]"
:placeholder="newBucketConfig[currentPicBedName].configOptions[option].placeholder"
@@ -216,29 +267,27 @@
</template>
<script lang="ts" setup>
import { shell } from 'electron'
import { ElNotification } from 'element-plus'
import {
CirclePlus,
SuccessFilled,
Folder,
Switch,
Tools,
ChromeFilled,
HomeFilled,
FolderOpened
} from '@element-plus/icons-vue'
import path from 'path'
import { ref, reactive, computed, onBeforeMount, watch } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { supportedPicBedList } from '@/manage/utils/constants'
import { useManageStore } from '@/manage/store/manageStore'
import { newBucketConfig } from '@/manage/utils/newBucketConfig'
import {
ChromeFilled,
CirclePlus,
Folder,
FolderOpened,
HomeFilled,
SuccessFilled,
Switch,
Tools
} from '@element-plus/icons-vue'
import { ElNotification } from 'element-plus'
import { IRPCActionType } from 'root/src/universal/types/enum'
import { computed, onBeforeMount, reactive, ref, watch } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { T as $T } from '@/i18n'
import { triggerRPC } from '@/utils/common'
import { IRPCActionType } from 'root/src/universal/types/enum'
import { useManageStore } from '@/manage/store/manageStore'
import { supportedPicBedList } from '@/manage/utils/constants'
import { newBucketConfig } from '@/manage/utils/newBucketConfig'
import { IStringKeyMap } from '#/types/types'
const manageStore = useManageStore() as any
const route = useRoute()
@@ -307,9 +356,9 @@ const menuTitleMap: IStringKeyMap = {
const rules = ruleMap(newBucketConfig)
const openPicBedUrl = () => shell.openExternal(urlMap[currentPagePicBedConfig.picBedName])
const openPicBedUrl = () => window.electron.sendRPC(IRPCActionType.OPEN_URL, urlMap[currentPagePicBedConfig.picBedName])
function ruleMap(options: IStringKeyMap) {
function ruleMap (options: IStringKeyMap) {
return Object.keys(options).reduce((result, key) => {
options[key].options.forEach((option: string) => {
const keyName = `${key}.${option}`
@@ -325,11 +374,11 @@ function ruleMap(options: IStringKeyMap) {
}, {} as IStringKeyMap)
}
function openNewBucketDrawer() {
function openNewBucketDrawer () {
nweBucketDrawerVisible.value = true
}
function createNewBucket(picBedName: string) {
function createNewBucket (picBedName: string) {
const configOptions = newBucketConfig[picBedName].configOptions
const resultMap: IStringKeyMap = Object.keys(configOptions).reduce((result, key) => {
const resultKey = `${picBedName}.${key}`
@@ -349,7 +398,7 @@ function createNewBucket(picBedName: string) {
resultMap.BucketName = `${resultMap.BucketName}-${currentPagePicBedConfig.appId}`
}
resultMap.endpoint = currentPagePicBedConfig.endpoint
triggerRPC(IRPCActionType.MANAGE_CREATE_BUCKET, currentAlias, resultMap).then((result: any) => {
window.electron.triggerRPC(IRPCActionType.MANAGE_CREATE_BUCKET, currentAlias, resultMap).then((result: any) => {
if (result) {
ElNotification({
title: $T('MANAGE_MAIN_PAGE_TIPS'),
@@ -370,12 +419,12 @@ function createNewBucket(picBedName: string) {
})
}
async function getBucketList() {
async function getBucketList () {
bucketList.value = {}
bucketNameList.value = []
isLoadingBucketList.value = true
const result = await triggerRPC<any>(IRPCActionType.MANAGE_GET_BUCKET_LIST, currentAlias.value)
const result = await window.electron.triggerRPC<any>(IRPCActionType.MANAGE_GET_BUCKET_LIST, currentAlias.value)
isLoadingBucketList.value = false
if (result.length > 0) {
@@ -386,17 +435,17 @@ async function getBucketList() {
}
}
function transPathToUnix(filePath: string | undefined) {
function transPathToUnix (filePath: string | undefined) {
if (!filePath) return ''
return process.platform === 'win32'
return window.electron.sendRpcSync(IRPCActionType.GET_PLATFORM) === 'win32'
? filePath
.split(path.sep)
.join(path.posix.sep)
.split(window.node.path.sep)
.join(window.node.path.posix.sep)
.replace(/^\/+|\/+$/g, '')
: filePath.replace(/^\/+|\/+$/g, '')
}
function handleSelectMenu(bucketName: string) {
function handleSelectMenu (bucketName: string) {
const currentPicBedConfig = manageStore.config.picBed[currentAlias.value]
const transformedConfig = JSON.parse(currentPicBedConfig.transformedConfig ?? '{}')
@@ -429,7 +478,7 @@ function handleSelectMenu(bucketName: string) {
})
}
function switchPicBed(picBedAlias: string) {
function switchPicBed (picBedAlias: string) {
if (picBedAlias === 'main') {
router.push({
path: '/main-page/manage-login-page'
@@ -460,11 +509,11 @@ function switchPicBed(picBedAlias: string) {
}
}
function changePicBed() {
function changePicBed () {
picBedSwitchDialogVisible.value = true
}
function openBucketPageSetting() {
function openBucketPageSetting () {
router.push({
path: '/main-page/manage-main-page/manage-setting-page'
})

View File

@@ -1,12 +1,25 @@
<template>
<div id="manage-setting">
<el-row class="view-title" align="middle" justify="center" style="font-size: 20px; color: black">
<el-row
class="view-title"
align="middle"
justify="center"
style="font-size: 20px; color: black"
>
{{ $T('MANAGE_SETTING_TITLE') }}
</el-row>
<el-row class="setting-list">
<el-col :span="20" :offset="2">
<el-col
:span="20"
:offset="2"
>
<el-row style="width: 100%">
<el-form label-position="left" label-width="50%" size="default" style="position: relative; width: 100%">
<el-form
label-position="left"
label-width="50%"
size="default"
style="position: relative; width: 100%"
>
<el-form-item>
<template #label>
<span style="position: absolute; left: 0">
@@ -37,7 +50,11 @@
@confirm="handleClearDb"
>
<template #reference>
<el-button type="primary" plain style="position: absolute; right: 0">
<el-button
type="primary"
plain
style="position: absolute; right: 0"
>
{{ $T('MANAGE_SETTING_CLEAR_CACHE_BUTTON') }}
</el-button>
</template>
@@ -82,7 +99,7 @@
width="150"
/>
</el-table>
<br v-if="form.customRename" />
<br v-if="form.customRename">
<DynamicSwitch
v-for="item in switchFieldsSpecialList"
:key="item.configName"
@@ -142,12 +159,19 @@
:step="1"
/>
</el-form-item>
<el-link style="margin-top: 10px; margin-bottom: 10px; color: #409eff" :underline="false">
<el-link
style="margin-top: 10px; margin-bottom: 10px; color: #409eff"
:underline="false"
>
{{ $T('MANAGE_SETTING_CHOOSE_COPY_FORMAT_TITLE') }}
</el-link>
<br />
<br>
<el-radio-group v-model="form.pasteFormat">
<el-radio v-for="item in pasteFormatList" :key="item" :value="item">
<el-radio
v-for="item in pasteFormatList"
:key="item"
:value="item"
>
{{ $T(`MANAGE_SETTING_CHOOSE_COPY_FORMAT_${item.toUpperCase().replace(/-/g, '_')}` as any) }}
</el-radio>
</el-radio-group>
@@ -165,7 +189,10 @@
style="width: 100%"
/>
<div>
<el-link style="margin-top: 10px; margin-bottom: 10px; color: #409eff" :underline="false">
<el-link
style="margin-top: 10px; margin-bottom: 10px; color: #409eff"
:underline="false"
>
{{ $T('MANAGE_SETTING_CHOOSE_DOWNLOAD_FOLDER_TITLE') }}
</el-link>
</div>
@@ -176,7 +203,10 @@
style="width: 100%; margin-top: 10px"
>
<template #append>
<el-button type="primary" @click="handleDownloadDirClick">
<el-button
type="primary"
@click="handleDownloadDirClick"
>
<el-icon>
<Folder />
</el-icon>
@@ -193,19 +223,17 @@
</template>
<script lang="ts" setup>
import { Folder, InfoFilled } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
import { InfoFilled, Folder } from '@element-plus/icons-vue'
import { ref, onBeforeMount, watch } from 'vue'
import DynamicSwitch from '@/manage/components/DynamicSwitch.vue'
import { fileCacheDbInstance } from '@/manage/store/bucketFileDb'
import { formatFileSize, customRenameFormatTable } from '@/manage/utils/common'
import { getConfig, saveConfig } from '@/manage/utils/dataSender'
import { IRPCActionType } from 'root/src/universal/types/enum'
import { onBeforeMount, ref, watch } from 'vue'
import { T as $T } from '@/i18n'
import { triggerRPC } from '@/utils/common'
import { IRPCActionType } from 'root/src/universal/types/enum'
import DynamicSwitch from '@/manage/components/DynamicSwitch.vue'
import { fileCacheDbInstance } from '@/manage/store/bucketFileDb'
import { customRenameFormatTable, formatFileSize } from '@/manage/utils/common'
import { getConfig, saveConfig } from '@/manage/utils/dataSender'
import { IStringKeyMap } from '#/types/types'
const form = ref<IStringKeyMap>({
timestampRename: false,
@@ -315,15 +343,15 @@ const switchFieldsSpecialList = [
}
]
async function initData() {
async function initData () {
const config = (await getConfig()) as IStringKeyMap
settingsKeys.forEach(key => {
form.value[key] = config.settings[key] ?? form.value[key]
})
}
async function handleDownloadDirClick() {
const result = await triggerRPC<any>(IRPCActionType.MANAGE_SELECT_DOWNLOAD_FOLDER)
async function handleDownloadDirClick () {
const result = await window.electron.triggerRPC<any>(IRPCActionType.MANAGE_SELECT_DOWNLOAD_FOLDER)
if (result) {
form.value.downloadDir = result
}
@@ -334,7 +362,7 @@ const handleCellClick = (row: any, column: any) => {
ElMessage.success(`${$T('MANAGE_SETTING_COPY_MESSAGE')}${row[column.property]}`)
}
function handleClearDb() {
function handleClearDb () {
fileCacheDbInstance
.delete()
.then(() => {
@@ -346,7 +374,7 @@ function handleClearDb() {
})
}
async function getIndexDbSize() {
async function getIndexDbSize () {
const size = (await navigator.storage.estimate()).usage ?? 0
const quota = (await navigator.storage.estimate()).quota ?? 0
dbSize.value = size

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

Some files were not shown because too many files have changed in this diff Show More