mirror of
https://github.com/Kuingsmile/PicList.git
synced 2026-05-06 20:42:57 +08:00
✨ Feature(custom): rewrite titlebar and navigation UI
This commit is contained in:
15
.vscode/settings.json
vendored
15
.vscode/settings.json
vendored
@@ -8,10 +8,17 @@
|
|||||||
"githubPullRequests.ignoredPullRequestBranches": [
|
"githubPullRequests.ignoredPullRequestBranches": [
|
||||||
"dev"
|
"dev"
|
||||||
],
|
],
|
||||||
"[go]": {
|
"[json]": {
|
||||||
"editor.codeActionsOnSave": {
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
"source.fixAll.eslint": "explicit"
|
"editor.formatOnSave": true
|
||||||
}
|
},
|
||||||
|
"[html]": {
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
|
"editor.formatOnSave": true
|
||||||
|
},
|
||||||
|
"[css]": {
|
||||||
|
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||||
|
"editor.formatOnSave": true
|
||||||
},
|
},
|
||||||
"typescript.tsdk": "node_modules\\typescript\\lib"
|
"typescript.tsdk": "node_modules\\typescript\\lib"
|
||||||
}
|
}
|
||||||
@@ -44,6 +44,7 @@
|
|||||||
"@aws-sdk/s3-request-presigner": "^3.857.0",
|
"@aws-sdk/s3-request-presigner": "^3.857.0",
|
||||||
"@electron-toolkit/preload": "^3.0.2",
|
"@electron-toolkit/preload": "^3.0.2",
|
||||||
"@element-plus/icons-vue": "^2.3.1",
|
"@element-plus/icons-vue": "^2.3.1",
|
||||||
|
"@headlessui/vue": "^1.7.23",
|
||||||
"@highlightjs/vue-plugin": "^2.1.2",
|
"@highlightjs/vue-plugin": "^2.1.2",
|
||||||
"@nodelib/fs.walk": "^3.0.1",
|
"@nodelib/fs.walk": "^3.0.1",
|
||||||
"@octokit/rest": "^22.0.0",
|
"@octokit/rest": "^22.0.0",
|
||||||
@@ -67,6 +68,7 @@
|
|||||||
"hpagent": "^1.2.0",
|
"hpagent": "^1.2.0",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
|
"lucide-vue-next": "^0.535.0",
|
||||||
"marked": "^16.1.1",
|
"marked": "^16.1.1",
|
||||||
"mime-types": "^2.1.35",
|
"mime-types": "^2.1.35",
|
||||||
"mitt": "^3.0.1",
|
"mitt": "^3.0.1",
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import windowManager from 'apis/app/window/windowManager'
|
|||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { BrowserWindow, clipboard, ipcMain, IpcMainEvent, Notification, WebContents } from 'electron'
|
import { BrowserWindow, clipboard, ipcMain, IpcMainEvent, Notification, WebContents } from 'electron'
|
||||||
import fs from 'fs-extra'
|
import fs from 'fs-extra'
|
||||||
import { IPicGo } from 'piclist'
|
import type { IPicGo } from 'piclist'
|
||||||
import writeFile from 'write-file-atomic'
|
import writeFile from 'write-file-atomic'
|
||||||
|
|
||||||
import { GET_RENAME_FILE_NAME, RENAME_FILE_NAME } from '#/events/constants'
|
import { GET_RENAME_FILE_NAME, RENAME_FILE_NAME } from '#/events/constants'
|
||||||
|
|||||||
@@ -2,21 +2,21 @@ const isDevelopment = process.env.NODE_ENV !== 'production'
|
|||||||
|
|
||||||
export const MANUAL_WINDOW_URL =
|
export const MANUAL_WINDOW_URL =
|
||||||
process.env.NODE_ENV === 'development'
|
process.env.NODE_ENV === 'development'
|
||||||
? 'http://localhost:3000#documents'
|
? 'http://localhost:3000/documents'
|
||||||
: 'index.html#documents'
|
: 'index.html/documents'
|
||||||
|
|
||||||
export const MINI_WINDOW_URL = isDevelopment
|
export const MINI_WINDOW_URL = isDevelopment
|
||||||
? 'http://localhost:3000#mini-page'
|
? 'http://localhost:3000/mini-page'
|
||||||
: 'index.html#mini-page'
|
: 'index.html/mini-page'
|
||||||
|
|
||||||
export const RENAME_WINDOW_URL =
|
export const RENAME_WINDOW_URL =
|
||||||
process.env.NODE_ENV === 'development'
|
process.env.NODE_ENV === 'development'
|
||||||
? 'http://localhost:3000#rename-page'
|
? 'http://localhost:3000/rename-page'
|
||||||
: 'index.html#rename-page'
|
: 'index.html/rename-page'
|
||||||
|
|
||||||
export const SETTING_WINDOW_URL = isDevelopment
|
export const SETTING_WINDOW_URL = isDevelopment
|
||||||
? 'http://localhost:3000#main-page/upload'
|
? 'http://localhost:3000/main-page/upload'
|
||||||
: 'index.html#main-page/upload'
|
: 'index.html/main-page/upload'
|
||||||
|
|
||||||
export const TRAY_WINDOW_URL = isDevelopment ? 'http://localhost:3000' : 'index.html'
|
export const TRAY_WINDOW_URL = isDevelopment ? 'http://localhost:3000' : 'index.html'
|
||||||
|
|
||||||
@@ -24,5 +24,5 @@ console.log(TRAY_WINDOW_URL)
|
|||||||
|
|
||||||
export const TOOLBOX_WINDOW_URL =
|
export const TOOLBOX_WINDOW_URL =
|
||||||
process.env.NODE_ENV === 'development'
|
process.env.NODE_ENV === 'development'
|
||||||
? 'http://localhost:3000#toolbox-page'
|
? 'http://localhost:3000/toolbox-page'
|
||||||
: 'index.html#toolbox-page'
|
: 'index.html/toolbox-page'
|
||||||
|
|||||||
@@ -24,8 +24,6 @@ import {
|
|||||||
|
|
||||||
const windowList = new Map<IWindowList, IWindowListItem>()
|
const windowList = new Map<IWindowList, IWindowListItem>()
|
||||||
|
|
||||||
const handleWindowParams = (windowURL: string) => windowURL
|
|
||||||
|
|
||||||
const getDefaultWindowSizes = (): { width: number; height: number } => {
|
const getDefaultWindowSizes = (): { width: number; height: number } => {
|
||||||
const [mainWindowWidth, mainWindowHeight] = db.get([
|
const [mainWindowWidth, mainWindowHeight] = db.get([
|
||||||
configPaths.settings.mainWindowWidth,
|
configPaths.settings.mainWindowWidth,
|
||||||
@@ -92,8 +90,8 @@ const settingWindowOptions = {
|
|||||||
fullscreenable: true,
|
fullscreenable: true,
|
||||||
resizable: true,
|
resizable: true,
|
||||||
title: 'PicList',
|
title: 'PicList',
|
||||||
vibrancy: 'ultra-dark',
|
transparent: false,
|
||||||
transparent: true,
|
backgroundColor: '#ebeef5',
|
||||||
titleBarStyle: 'hidden',
|
titleBarStyle: 'hidden',
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
sandbox: false,
|
sandbox: false,
|
||||||
@@ -108,10 +106,7 @@ const settingWindowOptions = {
|
|||||||
} as IBrowserWindowOptions
|
} as IBrowserWindowOptions
|
||||||
|
|
||||||
if (process.platform !== 'darwin') {
|
if (process.platform !== 'darwin') {
|
||||||
settingWindowOptions.show = false
|
|
||||||
settingWindowOptions.frame = false
|
settingWindowOptions.frame = false
|
||||||
settingWindowOptions.backgroundColor = '#3f3c37'
|
|
||||||
settingWindowOptions.transparent = false
|
|
||||||
settingWindowOptions.icon = '../../../../../resources/logo.png'
|
settingWindowOptions.icon = '../../../../../resources/logo.png'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,7 +191,7 @@ windowList.set(IWindowList.TRAY_WINDOW, {
|
|||||||
multiple: false,
|
multiple: false,
|
||||||
options: () => trayWindowOptions,
|
options: () => trayWindowOptions,
|
||||||
callback (window) {
|
callback (window) {
|
||||||
window.loadURL(handleWindowParams(TRAY_WINDOW_URL))
|
window.loadURL(TRAY_WINDOW_URL)
|
||||||
window.on('blur', () => {
|
window.on('blur', () => {
|
||||||
window.hide()
|
window.hide()
|
||||||
})
|
})
|
||||||
@@ -208,7 +203,7 @@ windowList.set(IWindowList.MANUAL_WINDOW, {
|
|||||||
multiple: false,
|
multiple: false,
|
||||||
options: () => manualWindowOptions,
|
options: () => manualWindowOptions,
|
||||||
callback (window) {
|
callback (window) {
|
||||||
window.loadURL(handleWindowParams(MANUAL_WINDOW_URL))
|
window.loadURL(MANUAL_WINDOW_URL)
|
||||||
window.focus()
|
window.focus()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -218,7 +213,7 @@ windowList.set(IWindowList.SETTING_WINDOW, {
|
|||||||
multiple: false,
|
multiple: false,
|
||||||
options: () => settingWindowOptions,
|
options: () => settingWindowOptions,
|
||||||
callback (window, windowManager) {
|
callback (window, windowManager) {
|
||||||
window.loadURL(handleWindowParams(SETTING_WINDOW_URL))
|
window.loadURL(SETTING_WINDOW_URL)
|
||||||
window.webContents.openDevTools({ mode: 'detach' })
|
window.webContents.openDevTools({ mode: 'detach' })
|
||||||
window.on('closed', () => {
|
window.on('closed', () => {
|
||||||
bus.emit(TOGGLE_SHORTKEY_MODIFIED_MODE, false)
|
bus.emit(TOGGLE_SHORTKEY_MODIFIED_MODE, false)
|
||||||
@@ -238,7 +233,7 @@ windowList.set(IWindowList.MINI_WINDOW, {
|
|||||||
multiple: false,
|
multiple: false,
|
||||||
options: () => miniWindowOptions,
|
options: () => miniWindowOptions,
|
||||||
callback (window) {
|
callback (window) {
|
||||||
window.loadURL(handleWindowParams(MINI_WINDOW_URL))
|
window.loadURL(MINI_WINDOW_URL)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -247,7 +242,7 @@ windowList.set(IWindowList.RENAME_WINDOW, {
|
|||||||
multiple: true,
|
multiple: true,
|
||||||
options: () => renameWindowOptions,
|
options: () => renameWindowOptions,
|
||||||
async callback (window, windowManager) {
|
async callback (window, windowManager) {
|
||||||
window.loadURL(handleWindowParams(RENAME_WINDOW_URL))
|
window.loadURL(RENAME_WINDOW_URL)
|
||||||
const currentWindow = windowManager.getAvailableWindow(true)
|
const currentWindow = windowManager.getAvailableWindow(true)
|
||||||
if (currentWindow && currentWindow.isVisible()) {
|
if (currentWindow && currentWindow.isVisible()) {
|
||||||
const { x, y, width, height } = currentWindow.getBounds()
|
const { x, y, width, height } = currentWindow.getBounds()
|
||||||
|
|||||||
@@ -9,15 +9,18 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import type { IConfig } from 'piclist'
|
import type { IConfig } from 'piclist'
|
||||||
import { onBeforeMount } from 'vue'
|
import { onBeforeMount, onMounted } from 'vue'
|
||||||
|
|
||||||
import { useATagClick } from '@/hooks/useATagClick'
|
import { useATagClick } from '@/hooks/useATagClick'
|
||||||
import { useStore } from '@/hooks/useStore'
|
import { useStore } from '@/hooks/useStore'
|
||||||
import { getConfig } from '@/utils/dataSender'
|
import { getConfig } from '@/utils/dataSender'
|
||||||
import { pageReloadCount } from '@/utils/global'
|
import { pageReloadCount } from '@/utils/global'
|
||||||
|
|
||||||
|
import { useAppStore } from './hooks/appStore'
|
||||||
|
|
||||||
useATagClick()
|
useATagClick()
|
||||||
const store = useStore()
|
const store = useStore()
|
||||||
|
const appStore = useAppStore()
|
||||||
|
|
||||||
onBeforeMount(async () => {
|
onBeforeMount(async () => {
|
||||||
const config = await getConfig<IConfig>()
|
const config = await getConfig<IConfig>()
|
||||||
@@ -25,6 +28,15 @@ onBeforeMount(async () => {
|
|||||||
store?.setDefaultPicBed(config?.picBed?.uploader || config?.picBed?.current || 'smms')
|
store?.setDefaultPicBed(config?.picBed?.uploader || config?.picBed?.current || 'smms')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
try {
|
||||||
|
appStore.init()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to load settings:', error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|||||||
746
src/renderer/components/NavigationPage.vue
Normal file
746
src/renderer/components/NavigationPage.vue
Normal file
@@ -0,0 +1,746 @@
|
|||||||
|
<template>
|
||||||
|
<nav class="navigation">
|
||||||
|
<div class="title-bar">
|
||||||
|
<div class="app-title">
|
||||||
|
<div class="app-text">
|
||||||
|
{{ $t('app.title') }}
|
||||||
|
</div>
|
||||||
|
<div class="app-version">
|
||||||
|
v{{ version }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="theme-section">
|
||||||
|
<ThemeSwitcher />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="nav-menu">
|
||||||
|
<router-link
|
||||||
|
v-for="item in navigationItems"
|
||||||
|
:key="item.path"
|
||||||
|
:to="item.path"
|
||||||
|
class="nav-item"
|
||||||
|
:title="`${item.name}`"
|
||||||
|
>
|
||||||
|
<div class="nav-icon-container">
|
||||||
|
<component
|
||||||
|
:is="item.icon"
|
||||||
|
:size="18"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<span class="nav-label">{{ item.name }}</span>
|
||||||
|
</router-link>
|
||||||
|
<Disclosure
|
||||||
|
v-slot="{ open }"
|
||||||
|
as="div"
|
||||||
|
class="nav-submenu"
|
||||||
|
>
|
||||||
|
<DisclosureButton class="nav-item submenu-trigger">
|
||||||
|
<div class="nav-icon-container">
|
||||||
|
<DatabaseIcon :size="18" />
|
||||||
|
</div>
|
||||||
|
<span class="nav-label">{{ $t('navigation.picbed') }}</span>
|
||||||
|
<ChevronDownIcon
|
||||||
|
:size="16"
|
||||||
|
class="submenu-arrow"
|
||||||
|
:class="{ 'rotate-180': open }"
|
||||||
|
/>
|
||||||
|
</DisclosureButton>
|
||||||
|
<DisclosurePanel class="submenu-panel">
|
||||||
|
<router-link
|
||||||
|
v-for="item in visiblePicBeds"
|
||||||
|
:key="item.type"
|
||||||
|
:to="{ name: routerConfig.UPLOADER_CONFIG_PAGE, params: { type: item.type } }"
|
||||||
|
class="submenu-item"
|
||||||
|
>
|
||||||
|
<span>{{ item.name }}</span>
|
||||||
|
</router-link>
|
||||||
|
</DisclosurePanel>
|
||||||
|
</Disclosure>
|
||||||
|
</div>
|
||||||
|
<div class="sidebar-footer">
|
||||||
|
<button
|
||||||
|
class="footer-button"
|
||||||
|
:title="$t('navigation.moreOptions')"
|
||||||
|
@click="openMenu"
|
||||||
|
>
|
||||||
|
<BadgeInfoIcon :size="20" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<TransitionRoot
|
||||||
|
appear
|
||||||
|
:show="qrcodeVisible"
|
||||||
|
as="template"
|
||||||
|
>
|
||||||
|
<Dialog
|
||||||
|
as="div"
|
||||||
|
class="qr-dialog"
|
||||||
|
@close="qrcodeVisible = false"
|
||||||
|
>
|
||||||
|
<TransitionChild
|
||||||
|
as="template"
|
||||||
|
enter="duration-300 ease-out"
|
||||||
|
enter-from="opacity-0"
|
||||||
|
enter-to="opacity-100"
|
||||||
|
leave="duration-200 ease-in"
|
||||||
|
leave-from="opacity-100"
|
||||||
|
leave-to="opacity-0"
|
||||||
|
>
|
||||||
|
<div class="dialog-overlay" />
|
||||||
|
</TransitionChild>
|
||||||
|
|
||||||
|
<div class="dialog-container">
|
||||||
|
<TransitionChild
|
||||||
|
as="template"
|
||||||
|
enter="duration-300 ease-out"
|
||||||
|
enter-from="opacity-0 scale-95"
|
||||||
|
enter-to="opacity-100 scale-100"
|
||||||
|
leave="duration-200 ease-in"
|
||||||
|
leave-from="opacity-100 scale-100"
|
||||||
|
leave-to="opacity-0 scale-95"
|
||||||
|
>
|
||||||
|
<DialogPanel class="dialog-panel">
|
||||||
|
<DialogTitle class="dialog-title">
|
||||||
|
{{ $t('navigation.picBedQrCode') }}
|
||||||
|
</DialogTitle>
|
||||||
|
|
||||||
|
<div class="dialog-content">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">{{ $t('navigation.choosePicBed') }}</label>
|
||||||
|
<Listbox
|
||||||
|
v-model="choosedPicBedForQRCode"
|
||||||
|
multiple
|
||||||
|
>
|
||||||
|
<div class="listbox-container">
|
||||||
|
<ListboxButton class="listbox-button">
|
||||||
|
<span
|
||||||
|
v-if="choosedPicBedForQRCode.length === 0"
|
||||||
|
class="placeholder"
|
||||||
|
>
|
||||||
|
{{ $t('navigation.selectPicBeds') }}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
v-else
|
||||||
|
class="selected-count"
|
||||||
|
>
|
||||||
|
{{ choosedPicBedForQRCode.length }} {{ $t('navigation.selected') }}
|
||||||
|
</span>
|
||||||
|
<ChevronDownIcon
|
||||||
|
:size="16"
|
||||||
|
class="listbox-arrow"
|
||||||
|
/>
|
||||||
|
</ListboxButton>
|
||||||
|
|
||||||
|
<transition
|
||||||
|
leave-active-class="transition duration-100 ease-in"
|
||||||
|
leave-from-class="opacity-100"
|
||||||
|
leave-to-class="opacity-0"
|
||||||
|
>
|
||||||
|
<ListboxOptions class="listbox-options">
|
||||||
|
<ListboxOption
|
||||||
|
v-for="picbed in picBedGlobal"
|
||||||
|
:key="picbed.type"
|
||||||
|
v-slot="{ active, selected }"
|
||||||
|
:value="picbed.type"
|
||||||
|
>
|
||||||
|
<li
|
||||||
|
class="listbox-option"
|
||||||
|
:class="{ active, selected }"
|
||||||
|
>
|
||||||
|
<span>{{ picbed.name }}</span>
|
||||||
|
<CheckIcon
|
||||||
|
v-if="selected"
|
||||||
|
:size="16"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
</ListboxOption>
|
||||||
|
</ListboxOptions>
|
||||||
|
</transition>
|
||||||
|
</div>
|
||||||
|
</Listbox>
|
||||||
|
|
||||||
|
<button
|
||||||
|
v-if="choosedPicBedForQRCode.length > 0"
|
||||||
|
class="copy-button"
|
||||||
|
@click="handleCopyPicBedConfig"
|
||||||
|
>
|
||||||
|
<CopyIcon :size="16" />
|
||||||
|
{{ $t('navigation.copyPicBedConfig') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="choosedPicBedForQRCode.length > 0"
|
||||||
|
class="qr-container"
|
||||||
|
>
|
||||||
|
<qrcode-vue
|
||||||
|
:size="280"
|
||||||
|
:value="picBedConfigString"
|
||||||
|
class="qr-code"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="dialog-actions">
|
||||||
|
<button
|
||||||
|
class="cancel-button"
|
||||||
|
@click="qrcodeVisible = false"
|
||||||
|
>
|
||||||
|
{{ $t('navigation.close') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</DialogPanel>
|
||||||
|
</TransitionChild>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
</TransitionRoot>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogPanel,
|
||||||
|
DialogTitle,
|
||||||
|
Disclosure,
|
||||||
|
DisclosureButton,
|
||||||
|
DisclosurePanel,
|
||||||
|
Listbox,
|
||||||
|
ListboxButton,
|
||||||
|
ListboxOption,
|
||||||
|
ListboxOptions,
|
||||||
|
TransitionChild,
|
||||||
|
TransitionRoot
|
||||||
|
} from '@headlessui/vue'
|
||||||
|
import { ElMessage as $message } from 'element-plus'
|
||||||
|
import { pick } from 'lodash-es'
|
||||||
|
import { BadgeInfoIcon, CheckIcon, ChevronDownIcon, CopyIcon, DatabaseIcon, FolderIcon, PieChartIcon, PlugIcon, Settings, UploadIcon } from 'lucide-vue-next'
|
||||||
|
import QrcodeVue from 'qrcode.vue'
|
||||||
|
import pkg from 'root/package.json'
|
||||||
|
import { SHOW_MAIN_PAGE_QRCODE } from 'root/src/universal/events/constants'
|
||||||
|
import { computed, nextTick, onBeforeMount, reactive, Ref, ref, watch } from 'vue'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
import * as config from '@/router/config'
|
||||||
|
import { getConfig } from '@/utils/dataSender'
|
||||||
|
import { picBedGlobal, updatePicBedGlobal } from '@/utils/global'
|
||||||
|
import { IRPCActionType } from '#/types/enum'
|
||||||
|
|
||||||
|
import ThemeSwitcher from './ui/ThemeSwitcher.vue'
|
||||||
|
const version = ref(pkg.version)
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
const routerConfig = reactive(config)
|
||||||
|
const qrcodeVisible = ref(false)
|
||||||
|
const choosedPicBedForQRCode: Ref<string[]> = ref([])
|
||||||
|
const picBedConfigString = ref('')
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => choosedPicBedForQRCode,
|
||||||
|
val => {
|
||||||
|
if (val.value.length > 0) {
|
||||||
|
nextTick(async () => {
|
||||||
|
const picBedConfig = await getConfig('picBed')
|
||||||
|
const config = pick(picBedConfig, ...choosedPicBedForQRCode.value)
|
||||||
|
picBedConfigString.value = JSON.stringify(config)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
const visiblePicBeds = computed(() =>
|
||||||
|
picBedGlobal.value.filter(item => item.visible)
|
||||||
|
)
|
||||||
|
|
||||||
|
const qrCodeHandler = () => {
|
||||||
|
qrcodeVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function openMenu () {
|
||||||
|
window.electron.sendRPC(IRPCActionType.SHOW_MAIN_PAGE_MENU)
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCopyPicBedConfig () {
|
||||||
|
window.electron.clipboard.writeText(picBedConfigString.value)
|
||||||
|
$message.success(t('COPY_PICBED_CONFIG_SUCCEED'))
|
||||||
|
}
|
||||||
|
|
||||||
|
const navigationItems = computed(() => [
|
||||||
|
{ name: t('navigation.upload'), path: '/main-page/upload', icon: UploadIcon },
|
||||||
|
{ name: t('navigation.manage'), path: '/main-page/manage-login-page', icon: PieChartIcon },
|
||||||
|
{ name: t('navigation.gallery'), path: '/main-page/gallery', icon: FolderIcon },
|
||||||
|
{ name: t('navigation.settings'), path: '/main-page/settings', icon: Settings },
|
||||||
|
{
|
||||||
|
name: t('navigation.plugins'),
|
||||||
|
path: '/main-page/plugins',
|
||||||
|
icon: PlugIcon
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
updatePicBedGlobal()
|
||||||
|
window.electron.ipcRendererOn(SHOW_MAIN_PAGE_QRCODE, qrCodeHandler)
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.navigation {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 150px;
|
||||||
|
height: 100vh;
|
||||||
|
background: var(--color-background-secondary);
|
||||||
|
border-right: 1px solid rgb(229 231 235);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root.dark .navigation,
|
||||||
|
:root.auto.dark .navigation {
|
||||||
|
background: var(--color-background-secondary);
|
||||||
|
border-right-color: var(--color-background-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-bar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 1.25rem 1rem;
|
||||||
|
border-bottom: 1px solid var(--color-border);
|
||||||
|
background: var(--color-background-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
:root.dark .title-bar,
|
||||||
|
:root.auto.dark .title-bar {
|
||||||
|
border-bottom-color: var(--color-border);
|
||||||
|
background: var(--color-background-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-title {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-text {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
letter-spacing: -0.025em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-version {
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
background: var(--color-surface-elevated);
|
||||||
|
padding: 3px 8px;
|
||||||
|
border-radius: 12px;
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-section {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0.75rem;
|
||||||
|
border-bottom: 1px solid var(--color-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
:root.dark .theme-section,
|
||||||
|
:root.auto.dark .theme-section {
|
||||||
|
border-bottom-color: var(--color-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-menu {
|
||||||
|
flex: 1;
|
||||||
|
padding: 1rem 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
color: rgb(75 85 99);
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root.dark .nav-item,
|
||||||
|
:root.auto.dark .nav-item {
|
||||||
|
color: rgb(209 213 219);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item:hover {
|
||||||
|
background: rgb(243 244 246);
|
||||||
|
color: rgb(17 24 39);
|
||||||
|
}
|
||||||
|
|
||||||
|
:root.dark .nav-item:hover,
|
||||||
|
:root.auto.dark .nav-item:hover {
|
||||||
|
background: rgb(55 65 81);
|
||||||
|
color: rgb(243 244 246);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item.router-link-active {
|
||||||
|
background: rgb(239 246 255);
|
||||||
|
color: rgb(99 102 241);
|
||||||
|
border-right: 3px solid rgb(99 102 241);
|
||||||
|
}
|
||||||
|
|
||||||
|
:root.dark .nav-item.router-link-active,
|
||||||
|
:root.auto.dark .nav-item.router-link-active {
|
||||||
|
background: rgb(30 58 138 / 0.2);
|
||||||
|
color: rgb(129 140 248);
|
||||||
|
border-right-color: rgb(129 140 248);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-icon-container {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.sidebar-footer {
|
||||||
|
padding: 12px;
|
||||||
|
border-top: 1px solid var(--color-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-button {
|
||||||
|
cursor: pointer;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 4px;
|
||||||
|
left: 4px;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-button:hover {
|
||||||
|
background: var(--color-surface-elevated);
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-submenu {
|
||||||
|
margin-top: 4px;
|
||||||
|
justify-content: center;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submenu-trigger {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
color: rgb(75 85 99);
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
width: 100%;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
:root.dark .submenu-trigger,
|
||||||
|
:root.auto.dark .submenu-trigger {
|
||||||
|
color: rgb(209 213 219);
|
||||||
|
}
|
||||||
|
|
||||||
|
.submenu-trigger:hover {
|
||||||
|
background: rgb(243 244 246);
|
||||||
|
color: rgb(17 24 39);
|
||||||
|
}
|
||||||
|
|
||||||
|
:root.dark .submenu-trigger:hover,
|
||||||
|
:root.auto.dark .submenu-trigger:hover {
|
||||||
|
background: rgb(55 65 81);
|
||||||
|
color: rgb(243 244 246);
|
||||||
|
}
|
||||||
|
|
||||||
|
.submenu-trigger .nav-icon-container {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submenu-trigger span {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submenu-arrow {
|
||||||
|
position: absolute;
|
||||||
|
right: 1rem;
|
||||||
|
transition: transform 0.2s ease;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rotate-180 {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.submenu-panel {
|
||||||
|
margin-top: 2px;
|
||||||
|
padding-left: 2.75rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submenu-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 0.8125rem;
|
||||||
|
font-weight: 500;
|
||||||
|
border-radius: 6px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.submenu-item:hover {
|
||||||
|
background: var(--color-surface-elevated);
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.submenu-item.router-link-active {
|
||||||
|
background: rgb(239 246 255);
|
||||||
|
color: rgb(99 102 241);
|
||||||
|
}
|
||||||
|
|
||||||
|
:root.dark .submenu-item.router-link-active,
|
||||||
|
:root.auto.dark .submenu-item.router-link-active {
|
||||||
|
background: rgb(30 58 138 / 0.2);
|
||||||
|
color: rgb(129 140 248);
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-dialog {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 50;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-overlay {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-container {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 50;
|
||||||
|
overflow-y: auto;
|
||||||
|
display: flex;
|
||||||
|
min-height: 100vh;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-panel {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 500px;
|
||||||
|
background: var(--color-surface);
|
||||||
|
border-radius: 16px;
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
box-shadow: var(--shadow-md);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-title {
|
||||||
|
padding: 20px 24px 0;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-content {
|
||||||
|
padding: 20px 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.listbox-container {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.listbox-button {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px 16px;
|
||||||
|
background: var(--color-surface);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: var(--transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
.listbox-button:hover {
|
||||||
|
border-color: var(--color-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.placeholder {
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected-count {
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.listbox-arrow {
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.listbox-options {
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 10;
|
||||||
|
margin-top: 4px;
|
||||||
|
background: var(--color-surface);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
box-shadow: var(--shadow-md);
|
||||||
|
max-height: 200px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.listbox-option {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 12px 16px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: var(--transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
.listbox-option.active {
|
||||||
|
background: var(--color-surface-elevated);
|
||||||
|
}
|
||||||
|
|
||||||
|
.listbox-option.selected {
|
||||||
|
background: var(--color-accent);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-button {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 12px;
|
||||||
|
padding: 10px 16px;
|
||||||
|
background: var(--color-accent);
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: var(--transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-button:hover {
|
||||||
|
background: var(--color-accent-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-code {
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: var(--shadow-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-actions {
|
||||||
|
padding: 0 24px 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cancel-button {
|
||||||
|
padding: 10px 20px;
|
||||||
|
background: var(--color-surface-elevated);
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: var(--border-radius);
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: var(--transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cancel-button:hover {
|
||||||
|
background: var(--color-border);
|
||||||
|
}
|
||||||
|
/* Responsive Design */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.sidebar {
|
||||||
|
width: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-label {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scrollbar Styling */
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--color-border);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
94
src/renderer/components/ui/ThemeSwitcher.vue
Normal file
94
src/renderer/components/ui/ThemeSwitcher.vue
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { Monitor, Moon, Sun } from 'lucide-vue-next'
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
import { useAppStore } from '@/hooks/appStore'
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
const appStore = useAppStore()
|
||||||
|
|
||||||
|
const currentTheme = computed(() => appStore.settings.app.theme || 'light')
|
||||||
|
|
||||||
|
const themeOptions = computed(() => [
|
||||||
|
{
|
||||||
|
value: 'light',
|
||||||
|
label: t('settings.theme.light'),
|
||||||
|
icon: Sun,
|
||||||
|
description: t('settings.theme.lightDesc')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'dark',
|
||||||
|
label: t('settings.theme.dark'),
|
||||||
|
icon: Moon,
|
||||||
|
description: t('settings.theme.darkDesc')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'auto',
|
||||||
|
label: t('settings.theme.auto'),
|
||||||
|
icon: Monitor,
|
||||||
|
description: t('settings.theme.autoDesc')
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
const currentThemeOption = computed(
|
||||||
|
() => themeOptions.value.find(option => option.value === currentTheme.value) || themeOptions.value[0]
|
||||||
|
)
|
||||||
|
|
||||||
|
const toggleTheme = () => {
|
||||||
|
appStore.toggleTheme()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="theme-switcher">
|
||||||
|
<button
|
||||||
|
class="theme-toggle-btn"
|
||||||
|
:title="t('settings.theme.toggle')"
|
||||||
|
@click="toggleTheme"
|
||||||
|
>
|
||||||
|
<component
|
||||||
|
:is="currentThemeOption.icon"
|
||||||
|
:size="18"
|
||||||
|
/>
|
||||||
|
<span class="theme-label">{{ currentThemeOption.label }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.theme-switcher {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-toggle-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
padding: 0.5rem 0.75rem;
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-toggle-btn:hover {
|
||||||
|
background: var(--color-surface-elevated);
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.theme-label {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile responsive */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.theme-label {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
241
src/renderer/components/ui/TitleBar.vue
Normal file
241
src/renderer/components/ui/TitleBar.vue
Normal file
@@ -0,0 +1,241 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="title-bar"
|
||||||
|
data-drag-region
|
||||||
|
>
|
||||||
|
<div class="title-bar-content">
|
||||||
|
<div class="title-left">
|
||||||
|
<div class="app-icon">
|
||||||
|
<img
|
||||||
|
src="/roundLogo.png"
|
||||||
|
alt="App Icon"
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="title-center">
|
||||||
|
<!-- Progress bar in title bar -->
|
||||||
|
<div
|
||||||
|
v-if="isShowprogress"
|
||||||
|
class="progress-container"
|
||||||
|
>
|
||||||
|
<div class="progress-bar">
|
||||||
|
<div
|
||||||
|
class="progress-fill"
|
||||||
|
:style="{ width: `${progress}%` }"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<span class="progress-text">{{ progress }}%</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="title-right"
|
||||||
|
>
|
||||||
|
<div class="window-controls">
|
||||||
|
<button
|
||||||
|
class="control-button pin-button"
|
||||||
|
:class="{ active: isAlwaysOnTop }"
|
||||||
|
:title="$t('titleBar.alwaysOnTop')"
|
||||||
|
@click="setAlwaysOnTop"
|
||||||
|
>
|
||||||
|
<PinIcon
|
||||||
|
:color="isAlwaysOnTop ? '#CE6769' : '#000'"
|
||||||
|
:size="14"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="control-button minimize-button"
|
||||||
|
:title="$t('titleBar.minimize')"
|
||||||
|
@click="minimizeWindow"
|
||||||
|
>
|
||||||
|
<MinusIcon :size="14" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="control-button mini-button"
|
||||||
|
:title="$t('titleBar.miniWindow')"
|
||||||
|
@click="openMiniWindow"
|
||||||
|
>
|
||||||
|
<ShrinkIcon :size="14" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="control-button close-button"
|
||||||
|
:title="$t('titleBar.close')"
|
||||||
|
@click="closeWindow"
|
||||||
|
>
|
||||||
|
<XIcon :size="14" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { IpcRendererEvent } from 'electron'
|
||||||
|
import { MinusIcon, PinIcon, ShrinkIcon, XIcon } from 'lucide-vue-next'
|
||||||
|
import { onBeforeMount, onBeforeUnmount, ref } from 'vue'
|
||||||
|
|
||||||
|
import { IRPCActionType } from '#/types/enum'
|
||||||
|
|
||||||
|
const isShowprogress = ref(false)
|
||||||
|
const progress = ref(0)
|
||||||
|
const isAlwaysOnTop = ref(false)
|
||||||
|
|
||||||
|
function setAlwaysOnTop () {
|
||||||
|
isAlwaysOnTop.value = !isAlwaysOnTop.value
|
||||||
|
window.electron.sendRPC(IRPCActionType.MAIN_WINDOW_ON_TOP)
|
||||||
|
}
|
||||||
|
|
||||||
|
function minimizeWindow () {
|
||||||
|
window.electron.sendRPC(IRPCActionType.MINIMIZE_WINDOW)
|
||||||
|
}
|
||||||
|
|
||||||
|
function openMiniWindow () {
|
||||||
|
window.electron.sendRPC(IRPCActionType.OPEN_MINI_WINDOW)
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeWindow () {
|
||||||
|
window.electron.sendRPC(IRPCActionType.CLOSE_WINDOW)
|
||||||
|
}
|
||||||
|
const uploadProcessHandler = (_event: IpcRendererEvent, data: { progress: number }) => {
|
||||||
|
isShowprogress.value = data.progress !== 100 && data.progress !== 0
|
||||||
|
progress.value = data.progress
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
window.electron.ipcRendererOn('updateProgress', uploadProcessHandler)
|
||||||
|
})
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
window.electron.ipcRendererRemoveListener('updateProgress', uploadProcessHandler)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.title-bar {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 32px;
|
||||||
|
background: var(--color-background-secondary);
|
||||||
|
border-bottom: 1px solid var(--color-border);
|
||||||
|
z-index: 1000;
|
||||||
|
-webkit-app-region: drag;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-bar-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
height: 100%;
|
||||||
|
padding: 0 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
-webkit-app-region: no-drag;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-icon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
color: var(--color-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-title {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-version {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
background: var(--color-border);
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-center {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
-webkit-app-region: no-drag;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
max-width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar {
|
||||||
|
flex: 1;
|
||||||
|
height: 4px;
|
||||||
|
background: var(--color-border);
|
||||||
|
border-radius: 2px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-fill {
|
||||||
|
height: 100%;
|
||||||
|
background: var(--color-success);
|
||||||
|
border-radius: 2px;
|
||||||
|
transition: width 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-text {
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
min-width: 35px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
-webkit-app-region: no-drag;
|
||||||
|
}
|
||||||
|
|
||||||
|
.window-controls {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-button {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 28px;
|
||||||
|
height: 20px;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
border-radius: 4px;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: var(--transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-button:hover {
|
||||||
|
background: var(--color-surface-elevated);
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pin-button.active {
|
||||||
|
color: var(--color-accent);
|
||||||
|
background: var(--color-accent)20;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-button:hover {
|
||||||
|
background: var(--color-danger);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
83
src/renderer/hooks/appStore.ts
Normal file
83
src/renderer/hooks/appStore.ts
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import { defineStore } from 'pinia'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
import { IStringKeyMap } from '#/types/types'
|
||||||
|
|
||||||
|
export const useAppStore = defineStore('app', () => {
|
||||||
|
const settings = ref<IStringKeyMap>({
|
||||||
|
app: {
|
||||||
|
theme: 'light'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const loading = ref(false)
|
||||||
|
const error = ref<string | undefined>()
|
||||||
|
|
||||||
|
function clearError () {
|
||||||
|
error.value = undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadSettings = () => {
|
||||||
|
const savedTheme = localStorage.getItem('theme')
|
||||||
|
if (savedTheme) {
|
||||||
|
settings.value.app.theme = savedTheme
|
||||||
|
}
|
||||||
|
applyTheme(settings.value.app.theme || 'light')
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyTheme (theme: string) {
|
||||||
|
const root = document.documentElement
|
||||||
|
root.classList.remove('light', 'dark', 'auto')
|
||||||
|
|
||||||
|
if (theme === 'auto') {
|
||||||
|
root.classList.add('auto')
|
||||||
|
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||||
|
root.classList.add(prefersDark ? 'dark' : 'light')
|
||||||
|
|
||||||
|
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
|
||||||
|
mediaQuery.addEventListener('change', e => {
|
||||||
|
if (settings.value.app.theme === 'auto') {
|
||||||
|
root.classList.remove('light', 'dark')
|
||||||
|
root.classList.add(e.matches ? 'dark' : 'light')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
root.classList.add(theme)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setTheme (theme: 'light' | 'dark' | 'auto') {
|
||||||
|
settings.value.app.theme = theme
|
||||||
|
localStorage.setItem('theme', theme)
|
||||||
|
applyTheme(theme)
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleTheme () {
|
||||||
|
const currentTheme = settings.value.app.theme || 'light'
|
||||||
|
const themes = ['light', 'dark', 'auto'] as const
|
||||||
|
const currentIndex = themes.indexOf(currentTheme as any)
|
||||||
|
const nextTheme = themes[(currentIndex + 1) % themes.length]
|
||||||
|
setTheme(nextTheme)
|
||||||
|
}
|
||||||
|
|
||||||
|
function init () {
|
||||||
|
try {
|
||||||
|
loadSettings()
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Application initialization failed:', err)
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
init,
|
||||||
|
loadSettings,
|
||||||
|
settings,
|
||||||
|
loading,
|
||||||
|
error,
|
||||||
|
clearError,
|
||||||
|
setTheme,
|
||||||
|
toggleTheme,
|
||||||
|
applyTheme
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -1,4 +1,39 @@
|
|||||||
{
|
{
|
||||||
|
"app": {
|
||||||
|
"title": "PicList"
|
||||||
|
},
|
||||||
|
"titleBar": {
|
||||||
|
"alwaysOnTop": "Always On Top",
|
||||||
|
"close": "Close",
|
||||||
|
"minimize": "Minimize",
|
||||||
|
"miniWindow": "Mini Window"
|
||||||
|
},
|
||||||
|
"navigation": {
|
||||||
|
"upload": "Upload",
|
||||||
|
"manage": "Manage",
|
||||||
|
"gallery": "Gallery",
|
||||||
|
"settings": "Settings",
|
||||||
|
"plugins": "Plugins",
|
||||||
|
"picbed": "PicBed",
|
||||||
|
"close": "Close",
|
||||||
|
"copyPicBedConfig": "Copy PicBed Config",
|
||||||
|
"selected": "Selected",
|
||||||
|
"moreOptions": "More Options",
|
||||||
|
"picBedQrCode": "PicBed QR Code",
|
||||||
|
"choosePicBed": "Choose PicBed",
|
||||||
|
"selectPicBeds": "Select PicBeds"
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"theme": {
|
||||||
|
"light": "Light",
|
||||||
|
"dark": "Dark",
|
||||||
|
"auto": "Auto",
|
||||||
|
"lightDesc": "Light Mode",
|
||||||
|
"darkDesc": "Dark Mode",
|
||||||
|
"autoDesc": "Auto Mode",
|
||||||
|
"toggle": "Toggle Theme"
|
||||||
|
}
|
||||||
|
},
|
||||||
"OPEN_MAIN_WINDOW": "Open Main Window",
|
"OPEN_MAIN_WINDOW": "Open Main Window",
|
||||||
"OPERATION_SUCCEED": "Operation Succeed",
|
"OPERATION_SUCCEED": "Operation Succeed",
|
||||||
"QUICK_UPLOAD": "Quick Upload",
|
"QUICK_UPLOAD": "Quick Upload",
|
||||||
@@ -21,7 +56,6 @@
|
|||||||
"MANUAL_PAGE_OPEN_BY_BROWSER": "Browser",
|
"MANUAL_PAGE_OPEN_BY_BROWSER": "Browser",
|
||||||
"MANUAL_PAGE_OPEN_BY_BUILD_IN": "Built-in Window",
|
"MANUAL_PAGE_OPEN_BY_BUILD_IN": "Built-in Window",
|
||||||
"MANUAL_PAGE_OPEN_SETTING_TIP": "Select the way to open the manual",
|
"MANUAL_PAGE_OPEN_SETTING_TIP": "Select the way to open the manual",
|
||||||
"UPLOAD_AREA": "Upload",
|
|
||||||
"UPLOAD_VIEW_HINT": "Click to open picbeds settings",
|
"UPLOAD_VIEW_HINT": "Click to open picbeds settings",
|
||||||
"MANAGE_PAGE": "Manage",
|
"MANAGE_PAGE": "Manage",
|
||||||
"GALLERY": "Gallery",
|
"GALLERY": "Gallery",
|
||||||
@@ -432,8 +466,8 @@
|
|||||||
"MANAGE_CONSTANT_GITHUB_PROXY_TIPS": "If the access speed is slow, you can try configuring a proxy",
|
"MANAGE_CONSTANT_GITHUB_PROXY_TIPS": "If the access speed is slow, you can try configuring a proxy",
|
||||||
"MANAGE_CONSTANT_GITHUB_PAGING_DESC": "Enable pagination",
|
"MANAGE_CONSTANT_GITHUB_PAGING_DESC": "Enable pagination",
|
||||||
"MANAGE_CONSTANT_GITHUB_CUSTOM_URL_DESC": "CDN acceleration domain name - Optional",
|
"MANAGE_CONSTANT_GITHUB_CUSTOM_URL_DESC": "CDN acceleration domain name - Optional",
|
||||||
"MANAGE_CONSTANT_GITHUB_CUSTOM_URL_PLACEHOLDER": "Support using {username}, {repo}, {branch}, and {path} as replacement placeholders",
|
"MANAGE_CONSTANT_GITHUB_CUSTOM_URL_PLACEHOLDER": "Support using {'{'}username{'}'}, {'{'}repo{'}'}, {'{'}branch{'}'}, and {'{'}path{'}'} as replacement placeholders",
|
||||||
"MANAGE_CONSTANT_GITHUB_CUSTOM_URL_TIPS": "For example: https://cdn.staticaly.com/gh/{username}/{repo}@{branch}/{path}",
|
"MANAGE_CONSTANT_GITHUB_CUSTOM_URL_TIPS": "eg. https://cdn.staticaly.com/gh/{'{'}username{'}'}/{'{'}repo{'}'}{'@'}{'{'}branch{'}'}/{'{'}path{'}'}",
|
||||||
"MANAGE_CONSTANT_GITHUB_CUSTOM_URL_RULE_MESSAGE_A": "The acceleration domain name must start with http:// or https://",
|
"MANAGE_CONSTANT_GITHUB_CUSTOM_URL_RULE_MESSAGE_A": "The acceleration domain name must start with http:// or https://",
|
||||||
"MANAGE_CONSTANT_GITHUB_CUSTOM_URL_RULE_MESSAGE_B": "The braces in the acceleration domain name must appear in pairs",
|
"MANAGE_CONSTANT_GITHUB_CUSTOM_URL_RULE_MESSAGE_B": "The braces in the acceleration domain name must appear in pairs",
|
||||||
"MANAGE_CONSTANT_GITHUB_EXPLAIN": "There is an hourly limit for API calls, and uploading files larger than 100M is not supported",
|
"MANAGE_CONSTANT_GITHUB_EXPLAIN": "There is an hourly limit for API calls, and uploading files larger than 100M is not supported",
|
||||||
@@ -873,4 +907,4 @@
|
|||||||
"MANAGE_NEW_BUCKET_S3PLIST_ACL_AUTHENTICATED_READ": "Authenticated Read",
|
"MANAGE_NEW_BUCKET_S3PLIST_ACL_AUTHENTICATED_READ": "Authenticated Read",
|
||||||
"PLUGIN_UPDATE_SUCCEED": "Plugin update succeed",
|
"PLUGIN_UPDATE_SUCCEED": "Plugin update succeed",
|
||||||
"TIPS_NOTICE": "Tips"
|
"TIPS_NOTICE": "Tips"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,40 @@
|
|||||||
{
|
{
|
||||||
|
"app": {
|
||||||
|
"title": "PicList"
|
||||||
|
},
|
||||||
|
"titleBar": {
|
||||||
|
"alwaysOnTop": "置顶",
|
||||||
|
"close": "关闭",
|
||||||
|
"minimize": "最小化",
|
||||||
|
"miniWindow": "迷你窗口"
|
||||||
|
},
|
||||||
|
"navigation": {
|
||||||
|
"upload": "上传",
|
||||||
|
"manage": "管理",
|
||||||
|
"gallery": "相册",
|
||||||
|
"settings": "设置",
|
||||||
|
"plugins": "插件",
|
||||||
|
"manual": "手册",
|
||||||
|
"picbed": "图床",
|
||||||
|
"close": "关闭",
|
||||||
|
"copyPicBedConfig": "复制图床设置",
|
||||||
|
"selected": "已选中",
|
||||||
|
"moreOptions": "更多选项",
|
||||||
|
"picBedQrCode": "图床配置二维码",
|
||||||
|
"choosePicBed": "选择图床",
|
||||||
|
"selectPicBeds": "请选择图床"
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"theme": {
|
||||||
|
"light": "浅色",
|
||||||
|
"dark": "深色",
|
||||||
|
"auto": "自动",
|
||||||
|
"lightDesc": "明亮模式",
|
||||||
|
"darkDesc": "黑暗模式",
|
||||||
|
"autoDesc": "自动模式",
|
||||||
|
"toggle": "切换主题"
|
||||||
|
}
|
||||||
|
},
|
||||||
"OPEN_MAIN_WINDOW": "打开主窗口",
|
"OPEN_MAIN_WINDOW": "打开主窗口",
|
||||||
"OPERATION_SUCCEED": "操作成功",
|
"OPERATION_SUCCEED": "操作成功",
|
||||||
"QUICK_UPLOAD": "快捷上传",
|
"QUICK_UPLOAD": "快捷上传",
|
||||||
@@ -21,7 +57,6 @@
|
|||||||
"MANUAL_PAGE_OPEN_BY_BROWSER": "浏览器",
|
"MANUAL_PAGE_OPEN_BY_BROWSER": "浏览器",
|
||||||
"MANUAL_PAGE_OPEN_BY_BUILD_IN": "内置窗口",
|
"MANUAL_PAGE_OPEN_BY_BUILD_IN": "内置窗口",
|
||||||
"MANUAL_PAGE_OPEN_SETTING_TIP": "选择手册打开方式",
|
"MANUAL_PAGE_OPEN_SETTING_TIP": "选择手册打开方式",
|
||||||
"UPLOAD_AREA": "上传",
|
|
||||||
"UPLOAD_VIEW_HINT": "点击打开图床设置",
|
"UPLOAD_VIEW_HINT": "点击打开图床设置",
|
||||||
"MANAGE_PAGE": "管理",
|
"MANAGE_PAGE": "管理",
|
||||||
"GALLERY": "相册",
|
"GALLERY": "相册",
|
||||||
@@ -432,8 +467,8 @@
|
|||||||
"MANAGE_CONSTANT_GITHUB_PROXY_TIPS": "如果访问速度较慢,可以尝试配置代理",
|
"MANAGE_CONSTANT_GITHUB_PROXY_TIPS": "如果访问速度较慢,可以尝试配置代理",
|
||||||
"MANAGE_CONSTANT_GITHUB_PAGING_DESC": "是否开启分页",
|
"MANAGE_CONSTANT_GITHUB_PAGING_DESC": "是否开启分页",
|
||||||
"MANAGE_CONSTANT_GITHUB_CUSTOM_URL_DESC": "CDN加速域名-可选",
|
"MANAGE_CONSTANT_GITHUB_CUSTOM_URL_DESC": "CDN加速域名-可选",
|
||||||
"MANAGE_CONSTANT_GITHUB_CUSTOM_URL_PLACEHOLDER": "支持使用{username}、{repo}、{branch}和{path}作为替换占位符,用于适配不同仓库和分支",
|
"MANAGE_CONSTANT_GITHUB_CUSTOM_URL_PLACEHOLDER": "支持使用{'{'}username{'}'}、{'{'}repo{'}'}、{'{'}branch{'}'}和{'{'}path{'}'}作为替换占位符,用于适配不同仓库和分支",
|
||||||
"MANAGE_CONSTANT_GITHUB_CUSTOM_URL_TIPS": "例如: https://cdn.staticaly.com/gh/{username}/{repo}@{branch}/{path}",
|
"MANAGE_CONSTANT_GITHUB_CUSTOM_URL_TIPS": "例如:`https://cdn.staticaly.com/gh/{'{'}username{'}'}/{'{'}repo{'}'}{'@'}{'{'}branch{'}'}/{'{'}path{'}'}`",
|
||||||
"MANAGE_CONSTANT_GITHUB_CUSTOM_URL_RULE_MESSAGE_A": "加速域名请以http://或https://开头",
|
"MANAGE_CONSTANT_GITHUB_CUSTOM_URL_RULE_MESSAGE_A": "加速域名请以http://或https://开头",
|
||||||
"MANAGE_CONSTANT_GITHUB_CUSTOM_URL_RULE_MESSAGE_B": "加速域名中的大括号必须成对出现",
|
"MANAGE_CONSTANT_GITHUB_CUSTOM_URL_RULE_MESSAGE_B": "加速域名中的大括号必须成对出现",
|
||||||
"MANAGE_CONSTANT_GITHUB_EXPLAIN": "API调用有每小时上限,此外不支持上传超过100M的文件",
|
"MANAGE_CONSTANT_GITHUB_EXPLAIN": "API调用有每小时上限,此外不支持上传超过100M的文件",
|
||||||
@@ -873,4 +908,4 @@
|
|||||||
"MANAGE_NEW_BUCKET_S3PLIST_ACL_AUTHENTICATED_READ": "授权读",
|
"MANAGE_NEW_BUCKET_S3PLIST_ACL_AUTHENTICATED_READ": "授权读",
|
||||||
"PLUGIN_UPDATE_SUCCEED": "插件更新成功",
|
"PLUGIN_UPDATE_SUCCEED": "插件更新成功",
|
||||||
"TIPS_NOTICE": "注意"
|
"TIPS_NOTICE": "注意"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,40 @@
|
|||||||
{
|
{
|
||||||
|
"app": {
|
||||||
|
"title": "PicList"
|
||||||
|
},
|
||||||
|
"titleBar": {
|
||||||
|
"alwaysOnTop": "置頂",
|
||||||
|
"close": "關閉",
|
||||||
|
"minimize": "最小化",
|
||||||
|
"miniWindow": "迷你視窗"
|
||||||
|
},
|
||||||
|
"navigation": {
|
||||||
|
"upload": "上傳",
|
||||||
|
"manage": "管理",
|
||||||
|
"gallery": "相簿",
|
||||||
|
"settings": "設定",
|
||||||
|
"plugins": "插件",
|
||||||
|
"manual": "手冊",
|
||||||
|
"picbed": "圖床",
|
||||||
|
"close": "關閉",
|
||||||
|
"copyPicBedConfig": "複製圖床設定",
|
||||||
|
"selected": "已選中",
|
||||||
|
"moreOptions": "更多選項",
|
||||||
|
"picBedQrCode": "圖床配置 QRCODE",
|
||||||
|
"choosePicBed": "選擇圖床",
|
||||||
|
"selectPicBeds": "請選擇圖床"
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"theme": {
|
||||||
|
"light": "淺色",
|
||||||
|
"dark": "深色",
|
||||||
|
"auto": "自動",
|
||||||
|
"lightDesc": "明亮模式",
|
||||||
|
"darkDesc": "黑暗模式",
|
||||||
|
"autoDesc": "自動模式",
|
||||||
|
"toggle": "切換主題"
|
||||||
|
}
|
||||||
|
},
|
||||||
"OPEN_MAIN_WINDOW": "打開主視窗",
|
"OPEN_MAIN_WINDOW": "打開主視窗",
|
||||||
"OPERATION_SUCCEED": "操作成功",
|
"OPERATION_SUCCEED": "操作成功",
|
||||||
"QUICK_UPLOAD": "快速上傳",
|
"QUICK_UPLOAD": "快速上傳",
|
||||||
@@ -21,7 +57,6 @@
|
|||||||
"MANUAL_PAGE_OPEN_BY_BROWSER": "瀏覽器",
|
"MANUAL_PAGE_OPEN_BY_BROWSER": "瀏覽器",
|
||||||
"MANUAL_PAGE_OPEN_BY_BUILD_IN": "內置窗口",
|
"MANUAL_PAGE_OPEN_BY_BUILD_IN": "內置窗口",
|
||||||
"MANUAL_PAGE_OPEN_SETTING_TIP": "選擇打開手冊方式",
|
"MANUAL_PAGE_OPEN_SETTING_TIP": "選擇打開手冊方式",
|
||||||
"UPLOAD_AREA": "上傳",
|
|
||||||
"UPLOAD_VIEW_HINT": "點擊打開圖床設定",
|
"UPLOAD_VIEW_HINT": "點擊打開圖床設定",
|
||||||
"MANAGE_PAGE": "管理",
|
"MANAGE_PAGE": "管理",
|
||||||
"GALLERY": "相簿",
|
"GALLERY": "相簿",
|
||||||
@@ -432,8 +467,8 @@
|
|||||||
"MANAGE_CONSTANT_GITHUB_PROXY_TIPS": "如果訪問速度較慢,可以嘗試配置代理",
|
"MANAGE_CONSTANT_GITHUB_PROXY_TIPS": "如果訪問速度較慢,可以嘗試配置代理",
|
||||||
"MANAGE_CONSTANT_GITHUB_PAGING_DESC": "是否開啟分頁",
|
"MANAGE_CONSTANT_GITHUB_PAGING_DESC": "是否開啟分頁",
|
||||||
"MANAGE_CONSTANT_GITHUB_CUSTOM_URL_DESC": "CDN加速域名-可選",
|
"MANAGE_CONSTANT_GITHUB_CUSTOM_URL_DESC": "CDN加速域名-可選",
|
||||||
"MANAGE_CONSTANT_GITHUB_CUSTOM_URL_PLACEHOLDER": "支持使用{username}、{repo}、{branch}和{path}作為替換占位符,用於適配不同倉庫和分支",
|
"MANAGE_CONSTANT_GITHUB_CUSTOM_URL_PLACEHOLDER": "支持使用{'{'}username{'}'}、{'{'}repo{'}'}、{'{'}branch{'}'}和{'{'}path{'}'}作為替換占位符,用於適配不同倉庫和分支",
|
||||||
"MANAGE_CONSTANT_GITHUB_CUSTOM_URL_TIPS": "例如: https://cdn.staticaly.com/gh/{username}/{repo}@{branch}/{path}",
|
"MANAGE_CONSTANT_GITHUB_CUSTOM_URL_TIPS": "例如:`https://cdn.staticaly.com/gh/{'{'}username{'}'}/{'{'}repo{'}'}{'@'}{'{'}branch{'}'}/{'{'}path{'}'}`",
|
||||||
"MANAGE_CONSTANT_GITHUB_CUSTOM_URL_RULE_MESSAGE_A": "加速域名請以http://或https://開頭",
|
"MANAGE_CONSTANT_GITHUB_CUSTOM_URL_RULE_MESSAGE_A": "加速域名請以http://或https://開頭",
|
||||||
"MANAGE_CONSTANT_GITHUB_CUSTOM_URL_RULE_MESSAGE_B": "加速域名中的大括號必須成對出現",
|
"MANAGE_CONSTANT_GITHUB_CUSTOM_URL_RULE_MESSAGE_B": "加速域名中的大括號必須成對出現",
|
||||||
"MANAGE_CONSTANT_GITHUB_EXPLAIN": "API調用有每小時上限,此外不支持上傳超過100M的文件",
|
"MANAGE_CONSTANT_GITHUB_EXPLAIN": "API調用有每小時上限,此外不支持上傳超過100M的文件",
|
||||||
@@ -873,4 +908,4 @@
|
|||||||
"MANAGE_NEW_BUCKET_S3PLIST_ACL_AUTHENTICATED_READ": "授權讀",
|
"MANAGE_NEW_BUCKET_S3PLIST_ACL_AUTHENTICATED_READ": "授權讀",
|
||||||
"PLUGIN_UPDATE_SUCCEED": "插件更新成功",
|
"PLUGIN_UPDATE_SUCCEED": "插件更新成功",
|
||||||
"TIPS_NOTICE": "注意"
|
"TIPS_NOTICE": "注意"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,519 +1,297 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="main-page">
|
<div
|
||||||
<div class="fake-title-bar">
|
id="main"
|
||||||
<div class="fake-title-bar__title">
|
class="app-container"
|
||||||
PicList - {{ version }}
|
>
|
||||||
</div>
|
<TitleBar />
|
||||||
<div
|
<div class="app-background">
|
||||||
v-if="osGlobal !== 'darwin'"
|
<div class="bg-gradient-primary" />
|
||||||
class="handle-bar"
|
<div class="bg-gradient-secondary" />
|
||||||
>
|
|
||||||
<el-icon
|
|
||||||
class="minus"
|
|
||||||
:color="isAlwaysOnTop ? '#409EFF' : '#fff'"
|
|
||||||
size="20"
|
|
||||||
style="margin-right: 10px"
|
|
||||||
@click="setAlwaysOnTop"
|
|
||||||
>
|
|
||||||
<ArrowUpBold />
|
|
||||||
</el-icon>
|
|
||||||
<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"
|
|
||||||
>
|
|
||||||
<ArrowDownBold />
|
|
||||||
</el-icon>
|
|
||||||
<el-icon
|
|
||||||
class="close"
|
|
||||||
color="#fff"
|
|
||||||
size="20"
|
|
||||||
@click="closeWindow"
|
|
||||||
>
|
|
||||||
<CloseBold />
|
|
||||||
</el-icon>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<el-progress
|
<Navigation />
|
||||||
v-if="isShowprogress"
|
<main class="main-content">
|
||||||
:percentage="progress"
|
<div class="content-container">
|
||||||
:stroke-width="7"
|
<router-view v-slot="{ Component, route }">
|
||||||
:text-inside="true"
|
|
||||||
:show-text="false"
|
|
||||||
status="success"
|
|
||||||
class="progress-bar"
|
|
||||||
/>
|
|
||||||
<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-item :index="routerConfig.UPLOAD_PAGE">
|
|
||||||
<el-icon>
|
|
||||||
<UploadFilled />
|
|
||||||
</el-icon>
|
|
||||||
<span>{{ $t('UPLOAD_AREA') }}</span>
|
|
||||||
</el-menu-item>
|
|
||||||
<el-menu-item :index="routerConfig.MANAGE_LOGIN_PAGE">
|
|
||||||
<el-icon>
|
|
||||||
<PieChart />
|
|
||||||
</el-icon>
|
|
||||||
<span>{{ $t('MANAGE_PAGE') }}</span>
|
|
||||||
</el-menu-item>
|
|
||||||
<el-menu-item :index="routerConfig.GALLERY_PAGE">
|
|
||||||
<el-icon>
|
|
||||||
<PictureFilled />
|
|
||||||
</el-icon>
|
|
||||||
<span>{{ $t('GALLERY') }}</span>
|
|
||||||
</el-menu-item>
|
|
||||||
<el-sub-menu
|
|
||||||
index="sub-menu"
|
|
||||||
:show-timeout="0"
|
|
||||||
:hide-timeout="0"
|
|
||||||
:popper-offset="0"
|
|
||||||
>
|
|
||||||
<template #title>
|
|
||||||
<el-icon>
|
|
||||||
<Menu />
|
|
||||||
</el-icon>
|
|
||||||
<span>{{ $t('PICBEDS_SETTINGS') }}</span>
|
|
||||||
</template>
|
|
||||||
<template v-for="item in picBedGlobal">
|
|
||||||
<el-menu-item
|
|
||||||
v-if="item.visible"
|
|
||||||
:key="item.type"
|
|
||||||
:index="`${routerConfig.UPLOADER_CONFIG_PAGE}-${item.type}`"
|
|
||||||
>
|
|
||||||
<span>{{ item.name }}</span>
|
|
||||||
</el-menu-item>
|
|
||||||
</template>
|
|
||||||
</el-sub-menu>
|
|
||||||
<el-menu-item :index="routerConfig.SETTING_PAGE">
|
|
||||||
<el-icon>
|
|
||||||
<Tools />
|
|
||||||
</el-icon>
|
|
||||||
<span>{{ $t('PICLIST_SETTINGS') }}</span>
|
|
||||||
</el-menu-item>
|
|
||||||
<el-menu-item :index="routerConfig.PLUGIN_PAGE">
|
|
||||||
<el-icon>
|
|
||||||
<Share />
|
|
||||||
</el-icon>
|
|
||||||
<span>{{ $t('PLUGIN_SETTINGS') }}</span>
|
|
||||||
</el-menu-item>
|
|
||||||
<el-menu-item :index="routerConfig.DocumentPage">
|
|
||||||
<el-icon>
|
|
||||||
<Link />
|
|
||||||
</el-icon>
|
|
||||||
<span>{{ $t('MANUAL') }}</span>
|
|
||||||
</el-menu-item>
|
|
||||||
</el-menu>
|
|
||||||
<el-icon
|
|
||||||
class="info-window"
|
|
||||||
@click="openMenu"
|
|
||||||
>
|
|
||||||
<InfoFilled />
|
|
||||||
</el-icon>
|
|
||||||
</el-col>
|
|
||||||
<el-col
|
|
||||||
:span="21"
|
|
||||||
:offset="3"
|
|
||||||
style="height: 100%"
|
|
||||||
class="main-wrapper"
|
|
||||||
>
|
|
||||||
<router-view v-slot="{ Component }">
|
|
||||||
<transition
|
<transition
|
||||||
name="picgo-fade"
|
name="page"
|
||||||
mode="out-in"
|
mode="out-in"
|
||||||
>
|
>
|
||||||
<keep-alive :include="keepAlivePages">
|
<keep-alive :include="keepAlivePages">
|
||||||
<component :is="Component" />
|
<component
|
||||||
|
:is="Component"
|
||||||
|
:key="route.path"
|
||||||
|
/>
|
||||||
</keep-alive>
|
</keep-alive>
|
||||||
</transition>
|
</transition>
|
||||||
</router-view>
|
</router-view>
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-dialog
|
|
||||||
v-model="qrcodeVisible"
|
|
||||||
class="qrcode-dialog"
|
|
||||||
top="3vh"
|
|
||||||
width="60%"
|
|
||||||
:title="$t('PICBED_QRCODE')"
|
|
||||||
:modal-append-to-body="false"
|
|
||||||
lock-scroll
|
|
||||||
append-to-body
|
|
||||||
>
|
|
||||||
<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>
|
|
||||||
<el-button
|
|
||||||
v-show="choosedPicBedForQRCode.length > 0"
|
|
||||||
type="primary"
|
|
||||||
round
|
|
||||||
class="copy-picbed-config"
|
|
||||||
@click="handleCopyPicBedConfig"
|
|
||||||
>
|
|
||||||
{{ $t('COPY_PICBED_CONFIG') }}
|
|
||||||
</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
<div class="qrcode-container">
|
|
||||||
<qrcode-vue
|
|
||||||
v-show="choosedPicBedForQRCode.length > 0"
|
|
||||||
:size="280"
|
|
||||||
:value="picBedConfigString"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</main>
|
||||||
<input-box-dialog />
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import {
|
import { useRouter } from 'vue-router'
|
||||||
ArrowDownBold,
|
|
||||||
ArrowUpBold,
|
|
||||||
CloseBold,
|
|
||||||
InfoFilled,
|
|
||||||
Link,
|
|
||||||
Menu,
|
|
||||||
PictureFilled,
|
|
||||||
PieChart,
|
|
||||||
SemiSelect,
|
|
||||||
Share,
|
|
||||||
Tools,
|
|
||||||
UploadFilled
|
|
||||||
} from '@element-plus/icons-vue'
|
|
||||||
import type { IpcRendererEvent } from 'electron'
|
|
||||||
import { ElMessage as $message, ElMessageBox } from 'element-plus'
|
|
||||||
import { pick } from 'lodash-es'
|
|
||||||
import QrcodeVue from 'qrcode.vue'
|
|
||||||
import pkg from 'root/package.json'
|
|
||||||
import { nextTick, onBeforeMount, onBeforeUnmount, reactive, Ref, ref, watch } from 'vue'
|
|
||||||
import { useI18n } from 'vue-i18n'
|
|
||||||
import { onBeforeRouteUpdate, useRouter } from 'vue-router'
|
|
||||||
|
|
||||||
import InputBoxDialog from '@/components/InputBoxDialog.vue'
|
import Navigation from '@/components/NavigationPage.vue'
|
||||||
import * as config from '@/router/config'
|
import TitleBar from '@/components/ui/TitleBar.vue'
|
||||||
import { getConfig, saveConfig } from '@/utils/dataSender'
|
|
||||||
import { osGlobal, picBedGlobal, updatePicBedGlobal } from '@/utils/global'
|
|
||||||
import { SHOW_MAIN_PAGE_QRCODE } from '#/events/constants'
|
|
||||||
import { II18nLanguage, IRPCActionType } from '#/types/enum'
|
|
||||||
import { configPaths, manualPageOpenType } from '#/utils/configPaths'
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
|
||||||
const version = ref(pkg.version)
|
|
||||||
const routerConfig = reactive(config)
|
|
||||||
const defaultActive = ref(routerConfig.UPLOAD_PAGE)
|
|
||||||
const $router = useRouter()
|
const $router = useRouter()
|
||||||
const qrcodeVisible = ref(false)
|
|
||||||
const picBedConfigString = ref('')
|
|
||||||
const choosedPicBedForQRCode: Ref<string[]> = ref([])
|
|
||||||
const isAlwaysOnTop = ref(false)
|
|
||||||
const keepAlivePages = $router
|
const keepAlivePages = $router
|
||||||
.getRoutes()
|
.getRoutes()
|
||||||
.filter(item => item.meta.keepAlive)
|
.filter(item => item.meta.keepAlive)
|
||||||
.map(item => item.name as string)
|
.map(item => item.name as string)
|
||||||
|
|
||||||
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()
|
|
||||||
window.electron.ipcRendererOn(SHOW_MAIN_PAGE_QRCODE, qrCodeHandler)
|
|
||||||
window.electron.ipcRendererOn('updateProgress', uploadProcessHandler)
|
|
||||||
})
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => choosedPicBedForQRCode,
|
|
||||||
val => {
|
|
||||||
if (val.value.length > 0) {
|
|
||||||
nextTick(async () => {
|
|
||||||
const picBedConfig = await getConfig('picBed')
|
|
||||||
const config = pick(picBedConfig, ...choosedPicBedForQRCode.value)
|
|
||||||
picBedConfigString.value = JSON.stringify(config)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ deep: true }
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleSelect = async (index: string) => {
|
|
||||||
defaultActive.value = index
|
|
||||||
if (index === routerConfig.DocumentPage) {
|
|
||||||
const manualPageOpenSetting = await getConfig<manualPageOpenType>(configPaths.settings.manualPageOpen)
|
|
||||||
const lang = (await getConfig(configPaths.settings.language)) || II18nLanguage.ZH_CN
|
|
||||||
const openManual = () => window.electron.sendRPC(IRPCActionType.OPEN_MANUAL_WINDOW)
|
|
||||||
const openExternal = () =>
|
|
||||||
window.electron.sendRPC(
|
|
||||||
IRPCActionType.OPEN_URL,
|
|
||||||
lang === II18nLanguage.ZH_CN ? 'https://piclist.cn/app.html' : 'https://piclist.cn/en/app.html'
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!manualPageOpenSetting) {
|
|
||||||
ElMessageBox.confirm(t('MANUAL_PAGE_OPEN_TIP'), t('MANUAL_PAGE_OPEN_TIP_TITLE'), {
|
|
||||||
confirmButtonText: t('MANUAL_PAGE_OPEN_BY_BROWSER'),
|
|
||||||
cancelButtonText: t('MANUAL_PAGE_OPEN_BY_BUILD_IN'),
|
|
||||||
type: 'info',
|
|
||||||
center: true
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
saveConfig(configPaths.settings.manualPageOpen, 'browser')
|
|
||||||
openExternal()
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
saveConfig(configPaths.settings.manualPageOpen, 'window')
|
|
||||||
openManual()
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
manualPageOpenSetting === 'window' ? openManual() : openExternal()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const type = index.match(routerConfig.UPLOADER_CONFIG_PAGE)
|
|
||||||
if (type === null) {
|
|
||||||
$router.push({
|
|
||||||
name: index
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
const type = index.replace(`${routerConfig.UPLOADER_CONFIG_PAGE}-`, '')
|
|
||||||
$router.push({
|
|
||||||
name: routerConfig.UPLOADER_CONFIG_PAGE,
|
|
||||||
params: {
|
|
||||||
type
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function minimizeWindow () {
|
|
||||||
window.electron.sendRPC(IRPCActionType.MINIMIZE_WINDOW)
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeWindow () {
|
|
||||||
window.electron.sendRPC(IRPCActionType.CLOSE_WINDOW)
|
|
||||||
}
|
|
||||||
|
|
||||||
function openMenu () {
|
|
||||||
window.electron.sendRPC(IRPCActionType.SHOW_MAIN_PAGE_MENU)
|
|
||||||
}
|
|
||||||
|
|
||||||
function openMiniWindow () {
|
|
||||||
window.electron.sendRPC(IRPCActionType.OPEN_MINI_WINDOW)
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleCopyPicBedConfig () {
|
|
||||||
window.electron.clipboard.writeText(picBedConfigString.value)
|
|
||||||
$message.success(t('COPY_PICBED_CONFIG_SUCCEED'))
|
|
||||||
}
|
|
||||||
|
|
||||||
function setAlwaysOnTop () {
|
|
||||||
isAlwaysOnTop.value = !isAlwaysOnTop.value
|
|
||||||
window.electron.sendRPC(IRPCActionType.MAIN_WINDOW_ON_TOP)
|
|
||||||
}
|
|
||||||
|
|
||||||
onBeforeRouteUpdate(async to => {
|
|
||||||
if (to.params.type) {
|
|
||||||
defaultActive.value = `${routerConfig.UPLOADER_CONFIG_PAGE}-${to.params.type}`
|
|
||||||
} else {
|
|
||||||
defaultActive.value = to.name as string
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
window.electron.ipcRendererRemoveListener(SHOW_MAIN_PAGE_QRCODE, qrCodeHandler)
|
|
||||||
window.electron.ipcRendererRemoveListener('updateProgress', uploadProcessHandler)
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export default {
|
export default {
|
||||||
name: 'MainPage'
|
name: 'MainPage'
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style lang="stylus">
|
|
||||||
$darwinBg = transparentify(#172426, #000, 0.7)
|
<style>
|
||||||
.setting-list-scroll
|
*,
|
||||||
height 800px
|
*::before,
|
||||||
overflow-y auto
|
*::after {
|
||||||
overflow-x hidden
|
margin: 0;
|
||||||
margin-right 0!important
|
padding: 0;
|
||||||
.picgo-fade
|
box-sizing: border-box;
|
||||||
&-enter,
|
}
|
||||||
&-leave,
|
|
||||||
&-leave-active
|
:root {
|
||||||
opacity 0
|
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||||
&-enter-active,
|
font-size: 14px;
|
||||||
&-leave-active
|
line-height: 1.5;
|
||||||
transition all 150ms linear
|
font-weight: 400;
|
||||||
.view-title
|
|
||||||
color #eee
|
--color-text-primary: #1d1d1f;
|
||||||
font-size 20px
|
--color-text-secondary: #6e6e73;
|
||||||
text-align center
|
--color-text-tertiary: #86868b;
|
||||||
margin 10px auto
|
--color-background-primary: #ffffff;
|
||||||
#main-page
|
--color-background-secondary: #f5f5f7;
|
||||||
height 100%
|
--color-background-tertiary: #fbfbfd;
|
||||||
.qrcode-dialog
|
--color-surface: rgba(255, 255, 255, 0.8);
|
||||||
.qrcode-container
|
--color-surface-elevated: rgba(255, 255, 255, 0.95);
|
||||||
display flex
|
--color-border: rgba(0, 0, 0, 0.1);
|
||||||
justify-content center
|
--color-border-secondary: rgba(0, 0, 0, 0.05);
|
||||||
.el-dialog__body
|
--color-primary: #6366f1;
|
||||||
padding-top 10px
|
--color-primary-hover: #4f46e5;
|
||||||
.copy-picbed-config
|
--color-accent: #007aff;
|
||||||
margin-left 10px
|
--color-accent-hover: #0056b3;
|
||||||
.fake-title-bar
|
--color-success: #34c759;
|
||||||
-webkit-app-region drag
|
--color-warning: #ff9500;
|
||||||
height h = 22px
|
--color-danger: #ff3b30;
|
||||||
width 100%
|
|
||||||
text-align center
|
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.04), 0 1px 2px rgba(0, 0, 0, 0.06);
|
||||||
color #eee
|
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.05), 0 2px 4px rgba(0, 0, 0, 0.06);
|
||||||
font-size 12px
|
--shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.08), 0 4px 6px rgba(0, 0, 0, 0.05);
|
||||||
line-height h
|
--shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.1), 0 10px 10px rgba(0, 0, 0, 0.04);
|
||||||
position fixed
|
|
||||||
z-index 100
|
--radius-sm: 6px;
|
||||||
&.darwin
|
--radius-md: 8px;
|
||||||
background transparent
|
--radius-lg: 12px;
|
||||||
background-image linear-gradient(
|
--radius-xl: 16px;
|
||||||
to right,
|
--radius-2xl: 20px;
|
||||||
transparent 0%,
|
|
||||||
transparent 167px,
|
--transition-fast: 0.15s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
$darwinBg 167px,
|
--transition-medium: 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
$darwinBg 100%
|
--transition-slow: 0.35s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
)
|
|
||||||
.fake-title-bar__title
|
-webkit-font-smoothing: antialiased;
|
||||||
padding-left 167px
|
-moz-osx-font-smoothing: grayscale;
|
||||||
.handle-bar
|
text-rendering: optimizeLegibility;
|
||||||
position absolute
|
}
|
||||||
top 2px
|
|
||||||
right 4px
|
:root.dark,
|
||||||
z-index 10000
|
:root.auto.dark {
|
||||||
-webkit-app-region no-drag
|
--color-text-primary: #f5f5f7;
|
||||||
.el-icon
|
--color-text-secondary: #a1a1a6;
|
||||||
cursor pointer
|
--color-text-tertiary: #86868b;
|
||||||
font-size 16px
|
--color-background-primary: #000000;
|
||||||
margin-left 5px
|
--color-background-secondary: #1c1c1e;
|
||||||
.el-icon.minus
|
--color-background-tertiary: #2c2c2e;
|
||||||
&:hover
|
--color-surface: rgba(28, 28, 30, 0.8);
|
||||||
color #409EFF
|
--color-surface-elevated: rgba(44, 44, 46, 0.95);
|
||||||
.el-icon.close
|
--color-border: rgba(255, 255, 255, 0.1);
|
||||||
&:hover
|
--color-border-secondary: rgba(255, 255, 255, 0.05);
|
||||||
color #F15140
|
--color-primary: #6366f1;
|
||||||
.el-icon.plus
|
--color-primary-hover: #818cf8;
|
||||||
&:hover
|
--color-accent: #0a84ff;
|
||||||
color #69C282
|
--color-accent-hover: #409cff;
|
||||||
.main-wrapper
|
}
|
||||||
&.darwin
|
|
||||||
background $darwinBg
|
:root.dark,
|
||||||
.side-bar-menu
|
:root.auto.dark {
|
||||||
position fixed
|
.nav-item {
|
||||||
height calc(100vh - 22px)
|
color: var(--color-text-secondary);
|
||||||
overflow-x hidden
|
}
|
||||||
overflow-y auto
|
|
||||||
width 142px
|
.nav-item.active {
|
||||||
.info-window
|
color: var(--color-accent);
|
||||||
cursor pointer
|
}
|
||||||
position fixed
|
|
||||||
bottom 4px
|
h1,
|
||||||
left 4px
|
h2,
|
||||||
cursor poiter
|
h3,
|
||||||
color #878d99
|
h4,
|
||||||
transition .2s all ease-in-out
|
h5,
|
||||||
&:hover
|
h6 {
|
||||||
color #409EFF
|
color: var(--color-text-primary);
|
||||||
.el-menu
|
}
|
||||||
border-right none
|
|
||||||
background transparent
|
p,
|
||||||
width 142px
|
span,
|
||||||
&-item
|
div {
|
||||||
color #eee
|
color: inherit;
|
||||||
position relative
|
}
|
||||||
&:focus,
|
|
||||||
&:hover
|
svg {
|
||||||
color #fff
|
color: inherit;
|
||||||
background transparent
|
}
|
||||||
&.is-active
|
|
||||||
color active-color = #409EFF
|
input,
|
||||||
&:before
|
select,
|
||||||
content ''
|
textarea {
|
||||||
position absolute
|
background: var(--color-surface);
|
||||||
width 1px
|
border-color: var(--color-border);
|
||||||
height 20px
|
color: var(--color-text-primary);
|
||||||
right 0
|
}
|
||||||
top 18px
|
|
||||||
background active-color
|
input::placeholder,
|
||||||
.el-sub-menu__title
|
textarea::placeholder {
|
||||||
color #eee
|
color: var(--color-text-tertiary);
|
||||||
&:hover
|
}
|
||||||
background transparent
|
|
||||||
span
|
button {
|
||||||
color #fff
|
color: var(--color-text-primary);
|
||||||
.el-sub-menu
|
border-color: var(--color-border);
|
||||||
.el-menu-item
|
}
|
||||||
min-width 142px
|
|
||||||
&.is-active
|
button:hover {
|
||||||
&:before
|
background: var(--color-surface-elevated);
|
||||||
top 16px
|
}
|
||||||
.main-content
|
}
|
||||||
padding-top 22px
|
|
||||||
position relative
|
body {
|
||||||
height calc(100vh - 22px)
|
color: var(--color-text-primary);
|
||||||
z-index 10
|
background-color: var(--color-background-primary);
|
||||||
.el-dialog__body
|
font-family: inherit;
|
||||||
padding 20px
|
overflow: hidden;
|
||||||
.support
|
}
|
||||||
text-align center
|
|
||||||
&-title
|
.app-container {
|
||||||
text-align center
|
position: relative;
|
||||||
color #878d99
|
height: 100vh;
|
||||||
.align-center
|
display: flex;
|
||||||
input
|
overflow: hidden;
|
||||||
text-align center
|
background-color: var(--color-background-primary);
|
||||||
*::-webkit-scrollbar
|
padding-top: 32px;
|
||||||
width 2px
|
}
|
||||||
height 8px
|
|
||||||
*::-webkit-scrollbar-thumb
|
.app-background {
|
||||||
border-radius 4px
|
position: absolute;
|
||||||
background #6f6f6f
|
inset: 0;
|
||||||
*::-webkit-scrollbar-track
|
z-index: 0;
|
||||||
background-color transparent
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-gradient-primary {
|
||||||
|
position: absolute;
|
||||||
|
top: -50%;
|
||||||
|
right: -30%;
|
||||||
|
width: 80%;
|
||||||
|
height: 80%;
|
||||||
|
background: radial-gradient(circle, rgba(0, 122, 255, 0.05) 0%, transparent 70%);
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bg-gradient-secondary {
|
||||||
|
position: absolute;
|
||||||
|
bottom: -40%;
|
||||||
|
left: -20%;
|
||||||
|
width: 60%;
|
||||||
|
height: 60%;
|
||||||
|
background: radial-gradient(circle, rgba(175, 82, 222, 0.03) 0%, transparent 70%);
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
flex: 1;
|
||||||
|
height: 100vh;
|
||||||
|
overflow: scroll;
|
||||||
|
background-color: var(--color-background-secondary);
|
||||||
|
scrollbar-width: none;
|
||||||
|
-ms-overflow-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-container {
|
||||||
|
height: 100%;
|
||||||
|
padding: 0.3 rem;
|
||||||
|
max-width: none;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-enter-from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(24px) scale(0.98);
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-leave-to {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-8px) scale(1.02);
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background-color: var(--color-border);
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 3px solid var(--color-background-primary);
|
||||||
|
transition: background-color var(--transition-fast);
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
background-color: var(--color-text-tertiary);
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-corner {
|
||||||
|
background: var(--color-background-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
::selection {
|
||||||
|
background-color: rgba(0, 122, 255, 0.2);
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
:focus-visible {
|
||||||
|
outline: 2px solid var(--color-accent);
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
*,
|
||||||
|
*::before,
|
||||||
|
*::after {
|
||||||
|
animation-duration: 0.01ms !important;
|
||||||
|
animation-iteration-count: 1 !important;
|
||||||
|
transition-duration: 0.01ms !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ app.config.globalProperties.sendRPC = window.electron.sendRPC
|
|||||||
app.config.globalProperties.sendToMain = window.electron.sendToMain
|
app.config.globalProperties.sendToMain = window.electron.sendToMain
|
||||||
|
|
||||||
const i18n = createI18n<MessageSchema, 'en' | 'zh-CN' | 'zh-TW'>({
|
const i18n = createI18n<MessageSchema, 'en' | 'zh-CN' | 'zh-TW'>({
|
||||||
|
legacy: false,
|
||||||
locale: localStorage.getItem('currentLanguage') || 'zh-CN',
|
locale: localStorage.getItem('currentLanguage') || 'zh-CN',
|
||||||
fallbackLocale: 'zh-CN',
|
fallbackLocale: 'zh-CN',
|
||||||
messages: {
|
messages: {
|
||||||
@@ -58,3 +59,7 @@ app.use(pinia)
|
|||||||
app.use(hljsVuePlugin)
|
app.use(hljsVuePlugin)
|
||||||
app.use(VueVideoPlayer)
|
app.use(VueVideoPlayer)
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
|
|
||||||
|
export {
|
||||||
|
i18n
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,8 +1,22 @@
|
|||||||
import { useI18n } from 'vue-i18n'
|
import { createI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
import en from '@/i18n/locales/en.json'
|
||||||
|
import zhCN from '@/i18n/locales/zh-CN.json'
|
||||||
|
import zhTW from '@/i18n/locales/zh-TW.json'
|
||||||
import { IStringKeyMap } from '#/types/types'
|
import { IStringKeyMap } from '#/types/types'
|
||||||
|
type MessageSchema = typeof en
|
||||||
|
|
||||||
const { t } = useI18n()
|
const i18n = createI18n<MessageSchema, 'en' | 'zh-CN' | 'zh-TW'>({
|
||||||
|
legacy: false,
|
||||||
|
locale: localStorage.getItem('currentLanguage') || 'zh-CN',
|
||||||
|
fallbackLocale: 'zh-CN',
|
||||||
|
messages: {
|
||||||
|
en,
|
||||||
|
'zh-CN': zhCN,
|
||||||
|
'zh-TW': zhTW
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const { t } = i18n.global
|
||||||
const defaultBaseRule = (name: string) => {
|
const defaultBaseRule = (name: string) => {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,10 +1,24 @@
|
|||||||
import { useI18n } from 'vue-i18n'
|
import { createI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
import en from '@/i18n/locales/en.json'
|
||||||
|
import zhCN from '@/i18n/locales/zh-CN.json'
|
||||||
|
import zhTW from '@/i18n/locales/zh-TW.json'
|
||||||
import { IStringKeyMap } from '#/types/types'
|
import { IStringKeyMap } from '#/types/types'
|
||||||
|
|
||||||
import { AliyunAreaCodeName, QiniuAreaCodeName, TencentAreaCodeName } from './bucketConfigCons'
|
import { AliyunAreaCodeName, QiniuAreaCodeName, TencentAreaCodeName } from './bucketConfigCons'
|
||||||
|
type MessageSchema = typeof en
|
||||||
|
|
||||||
const { t } = useI18n()
|
const i18n = createI18n<MessageSchema, 'en' | 'zh-CN' | 'zh-TW'>({
|
||||||
|
legacy: false,
|
||||||
|
locale: localStorage.getItem('currentLanguage') || 'zh-CN',
|
||||||
|
fallbackLocale: 'zh-CN',
|
||||||
|
messages: {
|
||||||
|
en,
|
||||||
|
'zh-CN': zhCN,
|
||||||
|
'zh-TW': zhTW
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const { t } = i18n.global
|
||||||
|
|
||||||
export const newBucketConfig: IStringKeyMap = {
|
export const newBucketConfig: IStringKeyMap = {
|
||||||
tcyun: {
|
tcyun: {
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
<template>
|
|
||||||
<webview
|
|
||||||
:src="srcUrl"
|
|
||||||
disablewebsecurity
|
|
||||||
allowpopups
|
|
||||||
autosize="on"
|
|
||||||
scrollbars="none"
|
|
||||||
style="width: 100%; height: 100%"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { onMounted, ref } from 'vue'
|
|
||||||
|
|
||||||
import { getConfig } from '@/utils/dataSender'
|
|
||||||
import { II18nLanguage } from '#/types/enum'
|
|
||||||
import { configPaths } from '#/utils/configPaths'
|
|
||||||
|
|
||||||
const srcUrl = ref('https://piclist.cn/app.html')
|
|
||||||
|
|
||||||
const updateUrl = async () => {
|
|
||||||
const lang = (await getConfig(configPaths.settings.language)) || II18nLanguage.ZH_CN
|
|
||||||
srcUrl.value = lang === II18nLanguage.ZH_CN ? 'https://piclist.cn/app.html' : 'https://piclist.cn/en/app.html'
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
updateUrl()
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
export default {
|
|
||||||
name: 'DocumentPage'
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,110 +1,122 @@
|
|||||||
import { createRouter, createWebHashHistory } from 'vue-router'
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
|
|
||||||
|
import MainPage from '@/layouts/Main.vue'
|
||||||
|
import ManageBucketPage from '@/manage/pages/BucketPage.vue'
|
||||||
|
import ManageEmptyPage from '@/manage/pages/EmptyPage.vue'
|
||||||
|
import ManageLoginPage from '@/manage/pages/LogInPage.vue'
|
||||||
|
import ManageMainPage from '@/manage/pages/ManageMain.vue'
|
||||||
|
import ManageSettingPage from '@/manage/pages/ManageSetting.vue'
|
||||||
|
import GalleryPage from '@/pages/Gallery.vue'
|
||||||
|
import MiniPage from '@/pages/MiniPage.vue'
|
||||||
|
import PicBedsPage from '@/pages/picbeds/index.vue'
|
||||||
|
import SettingPage from '@/pages/PicGoSetting.vue'
|
||||||
|
import PluginPage from '@/pages/Plugin.vue'
|
||||||
|
import RenamePage from '@/pages/RenamePage.vue'
|
||||||
|
import ShortKeyPage from '@/pages/ShortKey.vue'
|
||||||
|
import Toolbox from '@/pages/Toolbox.vue'
|
||||||
|
import TrayPage from '@/pages/TrayPage.vue'
|
||||||
|
import UploadPage from '@/pages/Upload.vue'
|
||||||
|
import UploaderConfigPage from '@/pages/UploaderConfigPage.vue'
|
||||||
import * as config from '@/router/config'
|
import * as config from '@/router/config'
|
||||||
|
|
||||||
export default createRouter({
|
export default createRouter({
|
||||||
history: createWebHashHistory(),
|
history: createWebHistory(),
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
name: config.TRAY_PAGE,
|
name: config.TRAY_PAGE,
|
||||||
component: () => import('@/pages/TrayPage.vue')
|
component: TrayPage
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/rename-page',
|
path: '/rename-page',
|
||||||
name: config.RENAME_PAGE,
|
name: config.RENAME_PAGE,
|
||||||
component: () => import('@/pages/RenamePage.vue')
|
component: RenamePage
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/mini-page',
|
path: '/mini-page',
|
||||||
name: config.MINI_PAGE,
|
name: config.MINI_PAGE,
|
||||||
component: () => import('@/pages/MiniPage.vue')
|
component: MiniPage
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/main-page',
|
path: '/main-page',
|
||||||
name: config.MAIN_PAGE,
|
name: config.MAIN_PAGE,
|
||||||
component: () => import('@/layouts/Main.vue'),
|
component: MainPage,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'upload',
|
path: 'upload',
|
||||||
component: () => import('@/pages/Upload.vue'),
|
component: UploadPage,
|
||||||
name: config.UPLOAD_PAGE
|
name: config.UPLOAD_PAGE
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'manage-main-page',
|
path: 'manage-main-page',
|
||||||
name: config.MANAGE_MAIN_PAGE,
|
name: config.MANAGE_MAIN_PAGE,
|
||||||
component: () => import('@/manage/pages/ManageMain.vue'),
|
component: ManageMainPage,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
name: config.MANAGE_EMPTY_PAGE,
|
name: config.MANAGE_EMPTY_PAGE,
|
||||||
component: () => import('@/manage/pages/EmptyPage.vue')
|
component: ManageEmptyPage
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'manage-setting-page',
|
path: 'manage-setting-page',
|
||||||
name: config.MANAGE_SETTING_PAGE,
|
name: config.MANAGE_SETTING_PAGE,
|
||||||
component: () => import('@/manage/pages/ManageSetting.vue')
|
component: ManageSettingPage
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'manage-bucket-page',
|
path: 'manage-bucket-page',
|
||||||
name: config.MANAGE_BUCKET_PAGE,
|
name: config.MANAGE_BUCKET_PAGE,
|
||||||
component: () => import('@/manage/pages/BucketPage.vue')
|
component: ManageBucketPage
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'manage-login-page',
|
path: 'manage-login-page',
|
||||||
name: config.MANAGE_LOGIN_PAGE,
|
name: config.MANAGE_LOGIN_PAGE,
|
||||||
component: () => import('@/manage/pages/LogInPage.vue')
|
component: ManageLoginPage
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'picbeds/:type/:configId?',
|
path: 'picbeds/:type/:configId?',
|
||||||
name: config.PICBEDS_PAGE,
|
name: config.PICBEDS_PAGE,
|
||||||
component: () => import('@/pages/picbeds/index.vue')
|
component: PicBedsPage
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'gallery',
|
path: 'gallery',
|
||||||
component: () => import('@/pages/Gallery.vue'),
|
component: GalleryPage,
|
||||||
name: config.GALLERY_PAGE,
|
name: config.GALLERY_PAGE,
|
||||||
meta: {
|
meta: {
|
||||||
keepAlive: true
|
keepAlive: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'setting',
|
path: 'settings',
|
||||||
name: config.SETTING_PAGE,
|
name: config.SETTING_PAGE,
|
||||||
component: () => import('@/pages/PicGoSetting.vue')
|
component: SettingPage
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'plugin',
|
path: 'plugins',
|
||||||
component: () => import('@/pages/Plugin.vue'),
|
component: PluginPage,
|
||||||
name: config.PLUGIN_PAGE
|
name: config.PLUGIN_PAGE
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'shortKey',
|
path: 'shortKey',
|
||||||
component: () => import('@/pages/ShortKey.vue'),
|
component: ShortKeyPage,
|
||||||
name: config.SHORTKEY_PAGE
|
name: config.SHORTKEY_PAGE
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'uploader-config-page/:type',
|
path: 'uploader-config-page/:type',
|
||||||
component: () => import('@/pages/UploaderConfigPage.vue'),
|
component: UploaderConfigPage,
|
||||||
name: config.UPLOADER_CONFIG_PAGE
|
name: config.UPLOADER_CONFIG_PAGE
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: '/documents',
|
|
||||||
component: () => import('@/pages/DocumentPage.vue'),
|
|
||||||
name: config.DocumentPage
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: '/toolbox-page',
|
path: '/toolbox-page',
|
||||||
name: config.TOOLBOX_CONFIG_PAGE,
|
name: config.TOOLBOX_CONFIG_PAGE,
|
||||||
component: () => import('@/pages/Toolbox.vue')
|
component: Toolbox
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/:pathMatch(.*)*',
|
path: '/:pathMatch(.*)*',
|
||||||
redirect: '/'
|
redirect: '/main-page/upload'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|||||||
24
yarn.lock
24
yarn.lock
@@ -1808,6 +1808,13 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6"
|
resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6"
|
||||||
integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==
|
integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==
|
||||||
|
|
||||||
|
"@headlessui/vue@^1.7.23":
|
||||||
|
version "1.7.23"
|
||||||
|
resolved "https://registry.yarnpkg.com/@headlessui/vue/-/vue-1.7.23.tgz#7fe19dbeca35de9e6270c82c78c4864e6a6f7391"
|
||||||
|
integrity sha512-JzdCNqurrtuu0YW6QaDtR2PIYCKPUWq28csDyMvN4zmGccmE7lz40Is6hc3LA4HFeCI7sekZ/PQMTNmn9I/4Wg==
|
||||||
|
dependencies:
|
||||||
|
"@tanstack/vue-virtual" "^3.0.0-beta.60"
|
||||||
|
|
||||||
"@highlightjs/vue-plugin@^2.1.2":
|
"@highlightjs/vue-plugin@^2.1.2":
|
||||||
version "2.1.2"
|
version "2.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/@highlightjs/vue-plugin/-/vue-plugin-2.1.2.tgz#b7deaaa03452da659a39859437ae0c4bca037600"
|
resolved "https://registry.yarnpkg.com/@highlightjs/vue-plugin/-/vue-plugin-2.1.2.tgz#b7deaaa03452da659a39859437ae0c4bca037600"
|
||||||
@@ -3207,6 +3214,18 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
defer-to-connect "^2.0.1"
|
defer-to-connect "^2.0.1"
|
||||||
|
|
||||||
|
"@tanstack/virtual-core@3.13.12":
|
||||||
|
version "3.13.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@tanstack/virtual-core/-/virtual-core-3.13.12.tgz#1dff176df9cc8f93c78c5e46bcea11079b397578"
|
||||||
|
integrity sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA==
|
||||||
|
|
||||||
|
"@tanstack/vue-virtual@^3.0.0-beta.60":
|
||||||
|
version "3.13.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@tanstack/vue-virtual/-/vue-virtual-3.13.12.tgz#a66daac9e6822ce4bcba76a3954937440697c264"
|
||||||
|
integrity sha512-vhF7kEU9EXWXh+HdAwKJ2m3xaOnTTmgcdXcF2pim8g4GvI7eRrk2YRuV5nUlZnd/NbCIX4/Ja2OZu5EjJL06Ww==
|
||||||
|
dependencies:
|
||||||
|
"@tanstack/virtual-core" "3.13.12"
|
||||||
|
|
||||||
"@tokenizer/inflate@^0.2.7":
|
"@tokenizer/inflate@^0.2.7":
|
||||||
version "0.2.7"
|
version "0.2.7"
|
||||||
resolved "https://registry.yarnpkg.com/@tokenizer/inflate/-/inflate-0.2.7.tgz#32dd9dfc9abe457c89b3d9b760fc0690c85a103b"
|
resolved "https://registry.yarnpkg.com/@tokenizer/inflate/-/inflate-0.2.7.tgz#32dd9dfc9abe457c89b3d9b760fc0690c85a103b"
|
||||||
@@ -8272,6 +8291,11 @@ lru-cache@^7.14.1, lru-cache@^7.7.1:
|
|||||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89"
|
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89"
|
||||||
integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==
|
integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==
|
||||||
|
|
||||||
|
lucide-vue-next@^0.535.0:
|
||||||
|
version "0.535.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/lucide-vue-next/-/lucide-vue-next-0.535.0.tgz#4ba16582f6d5f5388e17b77b4c6f23430d4a8a38"
|
||||||
|
integrity sha512-B79vTrPyVgp+yfnQe0xU0OcA5+xa9fz24psR8z01NZhbmn5VoxfYMwRm5E4iSk9mCRYgyFWqtx8DMp/bydiTcg==
|
||||||
|
|
||||||
m3u8-parser@^6.0.0:
|
m3u8-parser@^6.0.0:
|
||||||
version "6.0.0"
|
version "6.0.0"
|
||||||
resolved "https://registry.npmjs.org/m3u8-parser/-/m3u8-parser-6.0.0.tgz#e9143313b44f07bb25fdea1c8aac1098d9ada192"
|
resolved "https://registry.npmjs.org/m3u8-parser/-/m3u8-parser-6.0.0.tgz#e9143313b44f07bb25fdea1c8aac1098d9ada192"
|
||||||
|
|||||||
Reference in New Issue
Block a user