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": [
|
||||
"dev"
|
||||
],
|
||||
"[go]": {
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit"
|
||||
}
|
||||
"[json]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"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"
|
||||
}
|
||||
@@ -44,6 +44,7 @@
|
||||
"@aws-sdk/s3-request-presigner": "^3.857.0",
|
||||
"@electron-toolkit/preload": "^3.0.2",
|
||||
"@element-plus/icons-vue": "^2.3.1",
|
||||
"@headlessui/vue": "^1.7.23",
|
||||
"@highlightjs/vue-plugin": "^2.1.2",
|
||||
"@nodelib/fs.walk": "^3.0.1",
|
||||
"@octokit/rest": "^22.0.0",
|
||||
@@ -67,6 +68,7 @@
|
||||
"hpagent": "^1.2.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"lucide-vue-next": "^0.535.0",
|
||||
"marked": "^16.1.1",
|
||||
"mime-types": "^2.1.35",
|
||||
"mitt": "^3.0.1",
|
||||
|
||||
@@ -8,7 +8,7 @@ import windowManager from 'apis/app/window/windowManager'
|
||||
import dayjs from 'dayjs'
|
||||
import { BrowserWindow, clipboard, ipcMain, IpcMainEvent, Notification, WebContents } from 'electron'
|
||||
import fs from 'fs-extra'
|
||||
import { IPicGo } from 'piclist'
|
||||
import type { IPicGo } from 'piclist'
|
||||
import writeFile from 'write-file-atomic'
|
||||
|
||||
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 =
|
||||
process.env.NODE_ENV === 'development'
|
||||
? 'http://localhost:3000#documents'
|
||||
: 'index.html#documents'
|
||||
? 'http://localhost:3000/documents'
|
||||
: 'index.html/documents'
|
||||
|
||||
export const MINI_WINDOW_URL = isDevelopment
|
||||
? 'http://localhost:3000#mini-page'
|
||||
: 'index.html#mini-page'
|
||||
? 'http://localhost:3000/mini-page'
|
||||
: 'index.html/mini-page'
|
||||
|
||||
export const RENAME_WINDOW_URL =
|
||||
process.env.NODE_ENV === 'development'
|
||||
? 'http://localhost:3000#rename-page'
|
||||
: 'index.html#rename-page'
|
||||
? 'http://localhost:3000/rename-page'
|
||||
: 'index.html/rename-page'
|
||||
|
||||
export const SETTING_WINDOW_URL = isDevelopment
|
||||
? 'http://localhost:3000#main-page/upload'
|
||||
: 'index.html#main-page/upload'
|
||||
? 'http://localhost:3000/main-page/upload'
|
||||
: 'index.html/main-page/upload'
|
||||
|
||||
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 =
|
||||
process.env.NODE_ENV === 'development'
|
||||
? 'http://localhost:3000#toolbox-page'
|
||||
: 'index.html#toolbox-page'
|
||||
? 'http://localhost:3000/toolbox-page'
|
||||
: 'index.html/toolbox-page'
|
||||
|
||||
@@ -24,8 +24,6 @@ import {
|
||||
|
||||
const windowList = new Map<IWindowList, IWindowListItem>()
|
||||
|
||||
const handleWindowParams = (windowURL: string) => windowURL
|
||||
|
||||
const getDefaultWindowSizes = (): { width: number; height: number } => {
|
||||
const [mainWindowWidth, mainWindowHeight] = db.get([
|
||||
configPaths.settings.mainWindowWidth,
|
||||
@@ -92,8 +90,8 @@ const settingWindowOptions = {
|
||||
fullscreenable: true,
|
||||
resizable: true,
|
||||
title: 'PicList',
|
||||
vibrancy: 'ultra-dark',
|
||||
transparent: true,
|
||||
transparent: false,
|
||||
backgroundColor: '#ebeef5',
|
||||
titleBarStyle: 'hidden',
|
||||
webPreferences: {
|
||||
sandbox: false,
|
||||
@@ -108,10 +106,7 @@ const settingWindowOptions = {
|
||||
} as IBrowserWindowOptions
|
||||
|
||||
if (process.platform !== 'darwin') {
|
||||
settingWindowOptions.show = false
|
||||
settingWindowOptions.frame = false
|
||||
settingWindowOptions.backgroundColor = '#3f3c37'
|
||||
settingWindowOptions.transparent = false
|
||||
settingWindowOptions.icon = '../../../../../resources/logo.png'
|
||||
}
|
||||
|
||||
@@ -196,7 +191,7 @@ windowList.set(IWindowList.TRAY_WINDOW, {
|
||||
multiple: false,
|
||||
options: () => trayWindowOptions,
|
||||
callback (window) {
|
||||
window.loadURL(handleWindowParams(TRAY_WINDOW_URL))
|
||||
window.loadURL(TRAY_WINDOW_URL)
|
||||
window.on('blur', () => {
|
||||
window.hide()
|
||||
})
|
||||
@@ -208,7 +203,7 @@ windowList.set(IWindowList.MANUAL_WINDOW, {
|
||||
multiple: false,
|
||||
options: () => manualWindowOptions,
|
||||
callback (window) {
|
||||
window.loadURL(handleWindowParams(MANUAL_WINDOW_URL))
|
||||
window.loadURL(MANUAL_WINDOW_URL)
|
||||
window.focus()
|
||||
}
|
||||
})
|
||||
@@ -218,7 +213,7 @@ windowList.set(IWindowList.SETTING_WINDOW, {
|
||||
multiple: false,
|
||||
options: () => settingWindowOptions,
|
||||
callback (window, windowManager) {
|
||||
window.loadURL(handleWindowParams(SETTING_WINDOW_URL))
|
||||
window.loadURL(SETTING_WINDOW_URL)
|
||||
window.webContents.openDevTools({ mode: 'detach' })
|
||||
window.on('closed', () => {
|
||||
bus.emit(TOGGLE_SHORTKEY_MODIFIED_MODE, false)
|
||||
@@ -238,7 +233,7 @@ windowList.set(IWindowList.MINI_WINDOW, {
|
||||
multiple: false,
|
||||
options: () => miniWindowOptions,
|
||||
callback (window) {
|
||||
window.loadURL(handleWindowParams(MINI_WINDOW_URL))
|
||||
window.loadURL(MINI_WINDOW_URL)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -247,7 +242,7 @@ windowList.set(IWindowList.RENAME_WINDOW, {
|
||||
multiple: true,
|
||||
options: () => renameWindowOptions,
|
||||
async callback (window, windowManager) {
|
||||
window.loadURL(handleWindowParams(RENAME_WINDOW_URL))
|
||||
window.loadURL(RENAME_WINDOW_URL)
|
||||
const currentWindow = windowManager.getAvailableWindow(true)
|
||||
if (currentWindow && currentWindow.isVisible()) {
|
||||
const { x, y, width, height } = currentWindow.getBounds()
|
||||
|
||||
@@ -9,15 +9,18 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import type { IConfig } from 'piclist'
|
||||
import { onBeforeMount } from 'vue'
|
||||
import { onBeforeMount, onMounted } from 'vue'
|
||||
|
||||
import { useATagClick } from '@/hooks/useATagClick'
|
||||
import { useStore } from '@/hooks/useStore'
|
||||
import { getConfig } from '@/utils/dataSender'
|
||||
import { pageReloadCount } from '@/utils/global'
|
||||
|
||||
import { useAppStore } from './hooks/appStore'
|
||||
|
||||
useATagClick()
|
||||
const store = useStore()
|
||||
const appStore = useAppStore()
|
||||
|
||||
onBeforeMount(async () => {
|
||||
const config = await getConfig<IConfig>()
|
||||
@@ -25,6 +28,15 @@ onBeforeMount(async () => {
|
||||
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 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",
|
||||
"OPERATION_SUCCEED": "Operation Succeed",
|
||||
"QUICK_UPLOAD": "Quick Upload",
|
||||
@@ -21,7 +56,6 @@
|
||||
"MANUAL_PAGE_OPEN_BY_BROWSER": "Browser",
|
||||
"MANUAL_PAGE_OPEN_BY_BUILD_IN": "Built-in Window",
|
||||
"MANUAL_PAGE_OPEN_SETTING_TIP": "Select the way to open the manual",
|
||||
"UPLOAD_AREA": "Upload",
|
||||
"UPLOAD_VIEW_HINT": "Click to open picbeds settings",
|
||||
"MANAGE_PAGE": "Manage",
|
||||
"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_PAGING_DESC": "Enable pagination",
|
||||
"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_TIPS": "For example: https://cdn.staticaly.com/gh/{username}/{repo}@{branch}/{path}",
|
||||
"MANAGE_CONSTANT_GITHUB_CUSTOM_URL_PLACEHOLDER": "Support using {'{'}username{'}'}, {'{'}repo{'}'}, {'{'}branch{'}'}, and {'{'}path{'}'} as replacement placeholders",
|
||||
"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_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",
|
||||
|
||||
@@ -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": "打开主窗口",
|
||||
"OPERATION_SUCCEED": "操作成功",
|
||||
"QUICK_UPLOAD": "快捷上传",
|
||||
@@ -21,7 +57,6 @@
|
||||
"MANUAL_PAGE_OPEN_BY_BROWSER": "浏览器",
|
||||
"MANUAL_PAGE_OPEN_BY_BUILD_IN": "内置窗口",
|
||||
"MANUAL_PAGE_OPEN_SETTING_TIP": "选择手册打开方式",
|
||||
"UPLOAD_AREA": "上传",
|
||||
"UPLOAD_VIEW_HINT": "点击打开图床设置",
|
||||
"MANAGE_PAGE": "管理",
|
||||
"GALLERY": "相册",
|
||||
@@ -432,8 +467,8 @@
|
||||
"MANAGE_CONSTANT_GITHUB_PROXY_TIPS": "如果访问速度较慢,可以尝试配置代理",
|
||||
"MANAGE_CONSTANT_GITHUB_PAGING_DESC": "是否开启分页",
|
||||
"MANAGE_CONSTANT_GITHUB_CUSTOM_URL_DESC": "CDN加速域名-可选",
|
||||
"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_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_RULE_MESSAGE_A": "加速域名请以http://或https://开头",
|
||||
"MANAGE_CONSTANT_GITHUB_CUSTOM_URL_RULE_MESSAGE_B": "加速域名中的大括号必须成对出现",
|
||||
"MANAGE_CONSTANT_GITHUB_EXPLAIN": "API调用有每小时上限,此外不支持上传超过100M的文件",
|
||||
|
||||
@@ -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": "打開主視窗",
|
||||
"OPERATION_SUCCEED": "操作成功",
|
||||
"QUICK_UPLOAD": "快速上傳",
|
||||
@@ -21,7 +57,6 @@
|
||||
"MANUAL_PAGE_OPEN_BY_BROWSER": "瀏覽器",
|
||||
"MANUAL_PAGE_OPEN_BY_BUILD_IN": "內置窗口",
|
||||
"MANUAL_PAGE_OPEN_SETTING_TIP": "選擇打開手冊方式",
|
||||
"UPLOAD_AREA": "上傳",
|
||||
"UPLOAD_VIEW_HINT": "點擊打開圖床設定",
|
||||
"MANAGE_PAGE": "管理",
|
||||
"GALLERY": "相簿",
|
||||
@@ -432,8 +467,8 @@
|
||||
"MANAGE_CONSTANT_GITHUB_PROXY_TIPS": "如果訪問速度較慢,可以嘗試配置代理",
|
||||
"MANAGE_CONSTANT_GITHUB_PAGING_DESC": "是否開啟分頁",
|
||||
"MANAGE_CONSTANT_GITHUB_CUSTOM_URL_DESC": "CDN加速域名-可選",
|
||||
"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_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_RULE_MESSAGE_A": "加速域名請以http://或https://開頭",
|
||||
"MANAGE_CONSTANT_GITHUB_CUSTOM_URL_RULE_MESSAGE_B": "加速域名中的大括號必須成對出現",
|
||||
"MANAGE_CONSTANT_GITHUB_EXPLAIN": "API調用有每小時上限,此外不支持上傳超過100M的文件",
|
||||
|
||||
@@ -1,519 +1,297 @@
|
||||
<template>
|
||||
<div id="main-page">
|
||||
<div class="fake-title-bar">
|
||||
<div class="fake-title-bar__title">
|
||||
PicList - {{ version }}
|
||||
</div>
|
||||
<div
|
||||
v-if="osGlobal !== 'darwin'"
|
||||
class="handle-bar"
|
||||
id="main"
|
||||
class="app-container"
|
||||
>
|
||||
<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>
|
||||
<TitleBar />
|
||||
<div class="app-background">
|
||||
<div class="bg-gradient-primary" />
|
||||
<div class="bg-gradient-secondary" />
|
||||
</div>
|
||||
</div>
|
||||
<el-progress
|
||||
v-if="isShowprogress"
|
||||
:percentage="progress"
|
||||
:stroke-width="7"
|
||||
: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 }">
|
||||
<Navigation />
|
||||
<main class="main-content">
|
||||
<div class="content-container">
|
||||
<router-view v-slot="{ Component, route }">
|
||||
<transition
|
||||
name="picgo-fade"
|
||||
name="page"
|
||||
mode="out-in"
|
||||
>
|
||||
<keep-alive :include="keepAlivePages">
|
||||
<component :is="Component" />
|
||||
<component
|
||||
:is="Component"
|
||||
:key="route.path"
|
||||
/>
|
||||
</keep-alive>
|
||||
</transition>
|
||||
</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>
|
||||
</el-dialog>
|
||||
<input-box-dialog />
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
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 { useRouter } from 'vue-router'
|
||||
|
||||
import InputBoxDialog from '@/components/InputBoxDialog.vue'
|
||||
import * as config from '@/router/config'
|
||||
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'
|
||||
import Navigation from '@/components/NavigationPage.vue'
|
||||
import TitleBar from '@/components/ui/TitleBar.vue'
|
||||
|
||||
const { t } = useI18n()
|
||||
const version = ref(pkg.version)
|
||||
const routerConfig = reactive(config)
|
||||
const defaultActive = ref(routerConfig.UPLOAD_PAGE)
|
||||
const $router = useRouter()
|
||||
const qrcodeVisible = ref(false)
|
||||
const picBedConfigString = ref('')
|
||||
const choosedPicBedForQRCode: Ref<string[]> = ref([])
|
||||
const isAlwaysOnTop = ref(false)
|
||||
const keepAlivePages = $router
|
||||
.getRoutes()
|
||||
.filter(item => item.meta.keepAlive)
|
||||
.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 lang="ts">
|
||||
export default {
|
||||
name: 'MainPage'
|
||||
}
|
||||
</script>
|
||||
<style lang="stylus">
|
||||
$darwinBg = transparentify(#172426, #000, 0.7)
|
||||
.setting-list-scroll
|
||||
height 800px
|
||||
overflow-y auto
|
||||
overflow-x hidden
|
||||
margin-right 0!important
|
||||
.picgo-fade
|
||||
&-enter,
|
||||
&-leave,
|
||||
&-leave-active
|
||||
opacity 0
|
||||
&-enter-active,
|
||||
&-leave-active
|
||||
transition all 150ms linear
|
||||
.view-title
|
||||
color #eee
|
||||
font-size 20px
|
||||
text-align center
|
||||
margin 10px auto
|
||||
#main-page
|
||||
height 100%
|
||||
.qrcode-dialog
|
||||
.qrcode-container
|
||||
display flex
|
||||
justify-content center
|
||||
.el-dialog__body
|
||||
padding-top 10px
|
||||
.copy-picbed-config
|
||||
margin-left 10px
|
||||
.fake-title-bar
|
||||
-webkit-app-region drag
|
||||
height h = 22px
|
||||
width 100%
|
||||
text-align center
|
||||
color #eee
|
||||
font-size 12px
|
||||
line-height h
|
||||
position fixed
|
||||
z-index 100
|
||||
&.darwin
|
||||
background transparent
|
||||
background-image linear-gradient(
|
||||
to right,
|
||||
transparent 0%,
|
||||
transparent 167px,
|
||||
$darwinBg 167px,
|
||||
$darwinBg 100%
|
||||
)
|
||||
.fake-title-bar__title
|
||||
padding-left 167px
|
||||
.handle-bar
|
||||
position absolute
|
||||
top 2px
|
||||
right 4px
|
||||
z-index 10000
|
||||
-webkit-app-region no-drag
|
||||
.el-icon
|
||||
cursor pointer
|
||||
font-size 16px
|
||||
margin-left 5px
|
||||
.el-icon.minus
|
||||
&:hover
|
||||
color #409EFF
|
||||
.el-icon.close
|
||||
&:hover
|
||||
color #F15140
|
||||
.el-icon.plus
|
||||
&:hover
|
||||
color #69C282
|
||||
.main-wrapper
|
||||
&.darwin
|
||||
background $darwinBg
|
||||
.side-bar-menu
|
||||
position fixed
|
||||
height calc(100vh - 22px)
|
||||
overflow-x hidden
|
||||
overflow-y auto
|
||||
width 142px
|
||||
.info-window
|
||||
cursor pointer
|
||||
position fixed
|
||||
bottom 4px
|
||||
left 4px
|
||||
cursor poiter
|
||||
color #878d99
|
||||
transition .2s all ease-in-out
|
||||
&:hover
|
||||
color #409EFF
|
||||
.el-menu
|
||||
border-right none
|
||||
background transparent
|
||||
width 142px
|
||||
&-item
|
||||
color #eee
|
||||
position relative
|
||||
&:focus,
|
||||
&:hover
|
||||
color #fff
|
||||
background transparent
|
||||
&.is-active
|
||||
color active-color = #409EFF
|
||||
&:before
|
||||
content ''
|
||||
position absolute
|
||||
width 1px
|
||||
height 20px
|
||||
right 0
|
||||
top 18px
|
||||
background active-color
|
||||
.el-sub-menu__title
|
||||
color #eee
|
||||
&:hover
|
||||
background transparent
|
||||
span
|
||||
color #fff
|
||||
.el-sub-menu
|
||||
.el-menu-item
|
||||
min-width 142px
|
||||
&.is-active
|
||||
&:before
|
||||
top 16px
|
||||
.main-content
|
||||
padding-top 22px
|
||||
position relative
|
||||
height calc(100vh - 22px)
|
||||
z-index 10
|
||||
.el-dialog__body
|
||||
padding 20px
|
||||
.support
|
||||
text-align center
|
||||
&-title
|
||||
text-align center
|
||||
color #878d99
|
||||
.align-center
|
||||
input
|
||||
text-align center
|
||||
*::-webkit-scrollbar
|
||||
width 2px
|
||||
height 8px
|
||||
*::-webkit-scrollbar-thumb
|
||||
border-radius 4px
|
||||
background #6f6f6f
|
||||
*::-webkit-scrollbar-track
|
||||
background-color transparent
|
||||
|
||||
<style>
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:root {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
font-weight: 400;
|
||||
|
||||
--color-text-primary: #1d1d1f;
|
||||
--color-text-secondary: #6e6e73;
|
||||
--color-text-tertiary: #86868b;
|
||||
--color-background-primary: #ffffff;
|
||||
--color-background-secondary: #f5f5f7;
|
||||
--color-background-tertiary: #fbfbfd;
|
||||
--color-surface: rgba(255, 255, 255, 0.8);
|
||||
--color-surface-elevated: rgba(255, 255, 255, 0.95);
|
||||
--color-border: rgba(0, 0, 0, 0.1);
|
||||
--color-border-secondary: rgba(0, 0, 0, 0.05);
|
||||
--color-primary: #6366f1;
|
||||
--color-primary-hover: #4f46e5;
|
||||
--color-accent: #007aff;
|
||||
--color-accent-hover: #0056b3;
|
||||
--color-success: #34c759;
|
||||
--color-warning: #ff9500;
|
||||
--color-danger: #ff3b30;
|
||||
|
||||
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.04), 0 1px 2px rgba(0, 0, 0, 0.06);
|
||||
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.05), 0 2px 4px rgba(0, 0, 0, 0.06);
|
||||
--shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.08), 0 4px 6px rgba(0, 0, 0, 0.05);
|
||||
--shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.1), 0 10px 10px rgba(0, 0, 0, 0.04);
|
||||
|
||||
--radius-sm: 6px;
|
||||
--radius-md: 8px;
|
||||
--radius-lg: 12px;
|
||||
--radius-xl: 16px;
|
||||
--radius-2xl: 20px;
|
||||
|
||||
--transition-fast: 0.15s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
--transition-medium: 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
--transition-slow: 0.35s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
|
||||
:root.dark,
|
||||
:root.auto.dark {
|
||||
--color-text-primary: #f5f5f7;
|
||||
--color-text-secondary: #a1a1a6;
|
||||
--color-text-tertiary: #86868b;
|
||||
--color-background-primary: #000000;
|
||||
--color-background-secondary: #1c1c1e;
|
||||
--color-background-tertiary: #2c2c2e;
|
||||
--color-surface: rgba(28, 28, 30, 0.8);
|
||||
--color-surface-elevated: rgba(44, 44, 46, 0.95);
|
||||
--color-border: rgba(255, 255, 255, 0.1);
|
||||
--color-border-secondary: rgba(255, 255, 255, 0.05);
|
||||
--color-primary: #6366f1;
|
||||
--color-primary-hover: #818cf8;
|
||||
--color-accent: #0a84ff;
|
||||
--color-accent-hover: #409cff;
|
||||
}
|
||||
|
||||
:root.dark,
|
||||
:root.auto.dark {
|
||||
.nav-item {
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
|
||||
.nav-item.active {
|
||||
color: var(--color-accent);
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
p,
|
||||
span,
|
||||
div {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
svg {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
background: var(--color-surface);
|
||||
border-color: var(--color-border);
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
input::placeholder,
|
||||
textarea::placeholder {
|
||||
color: var(--color-text-tertiary);
|
||||
}
|
||||
|
||||
button {
|
||||
color: var(--color-text-primary);
|
||||
border-color: var(--color-border);
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: var(--color-surface-elevated);
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
color: var(--color-text-primary);
|
||||
background-color: var(--color-background-primary);
|
||||
font-family: inherit;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.app-container {
|
||||
position: relative;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
background-color: var(--color-background-primary);
|
||||
padding-top: 32px;
|
||||
}
|
||||
|
||||
.app-background {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 0;
|
||||
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>
|
||||
|
||||
@@ -34,6 +34,7 @@ app.config.globalProperties.sendRPC = window.electron.sendRPC
|
||||
app.config.globalProperties.sendToMain = window.electron.sendToMain
|
||||
|
||||
const i18n = createI18n<MessageSchema, 'en' | 'zh-CN' | 'zh-TW'>({
|
||||
legacy: false,
|
||||
locale: localStorage.getItem('currentLanguage') || 'zh-CN',
|
||||
fallbackLocale: 'zh-CN',
|
||||
messages: {
|
||||
@@ -58,3 +59,7 @@ app.use(pinia)
|
||||
app.use(hljsVuePlugin)
|
||||
app.use(VueVideoPlayer)
|
||||
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'
|
||||
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) => {
|
||||
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 { 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 = {
|
||||
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'
|
||||
|
||||
export default createRouter({
|
||||
history: createWebHashHistory(),
|
||||
history: createWebHistory(),
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
name: config.TRAY_PAGE,
|
||||
component: () => import('@/pages/TrayPage.vue')
|
||||
component: TrayPage
|
||||
},
|
||||
{
|
||||
path: '/rename-page',
|
||||
name: config.RENAME_PAGE,
|
||||
component: () => import('@/pages/RenamePage.vue')
|
||||
component: RenamePage
|
||||
},
|
||||
{
|
||||
path: '/mini-page',
|
||||
name: config.MINI_PAGE,
|
||||
component: () => import('@/pages/MiniPage.vue')
|
||||
component: MiniPage
|
||||
},
|
||||
{
|
||||
path: '/main-page',
|
||||
name: config.MAIN_PAGE,
|
||||
component: () => import('@/layouts/Main.vue'),
|
||||
component: MainPage,
|
||||
children: [
|
||||
{
|
||||
path: 'upload',
|
||||
component: () => import('@/pages/Upload.vue'),
|
||||
component: UploadPage,
|
||||
name: config.UPLOAD_PAGE
|
||||
},
|
||||
{
|
||||
path: 'manage-main-page',
|
||||
name: config.MANAGE_MAIN_PAGE,
|
||||
component: () => import('@/manage/pages/ManageMain.vue'),
|
||||
component: ManageMainPage,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: config.MANAGE_EMPTY_PAGE,
|
||||
component: () => import('@/manage/pages/EmptyPage.vue')
|
||||
component: ManageEmptyPage
|
||||
},
|
||||
{
|
||||
path: 'manage-setting-page',
|
||||
name: config.MANAGE_SETTING_PAGE,
|
||||
component: () => import('@/manage/pages/ManageSetting.vue')
|
||||
component: ManageSettingPage
|
||||
},
|
||||
{
|
||||
path: 'manage-bucket-page',
|
||||
name: config.MANAGE_BUCKET_PAGE,
|
||||
component: () => import('@/manage/pages/BucketPage.vue')
|
||||
component: ManageBucketPage
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'manage-login-page',
|
||||
name: config.MANAGE_LOGIN_PAGE,
|
||||
component: () => import('@/manage/pages/LogInPage.vue')
|
||||
component: ManageLoginPage
|
||||
},
|
||||
{
|
||||
path: 'picbeds/:type/:configId?',
|
||||
name: config.PICBEDS_PAGE,
|
||||
component: () => import('@/pages/picbeds/index.vue')
|
||||
component: PicBedsPage
|
||||
},
|
||||
{
|
||||
path: 'gallery',
|
||||
component: () => import('@/pages/Gallery.vue'),
|
||||
component: GalleryPage,
|
||||
name: config.GALLERY_PAGE,
|
||||
meta: {
|
||||
keepAlive: true
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'setting',
|
||||
path: 'settings',
|
||||
name: config.SETTING_PAGE,
|
||||
component: () => import('@/pages/PicGoSetting.vue')
|
||||
component: SettingPage
|
||||
},
|
||||
{
|
||||
path: 'plugin',
|
||||
component: () => import('@/pages/Plugin.vue'),
|
||||
path: 'plugins',
|
||||
component: PluginPage,
|
||||
name: config.PLUGIN_PAGE
|
||||
},
|
||||
{
|
||||
path: 'shortKey',
|
||||
component: () => import('@/pages/ShortKey.vue'),
|
||||
component: ShortKeyPage,
|
||||
name: config.SHORTKEY_PAGE
|
||||
},
|
||||
{
|
||||
path: 'uploader-config-page/:type',
|
||||
component: () => import('@/pages/UploaderConfigPage.vue'),
|
||||
component: UploaderConfigPage,
|
||||
name: config.UPLOADER_CONFIG_PAGE
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/documents',
|
||||
component: () => import('@/pages/DocumentPage.vue'),
|
||||
name: config.DocumentPage
|
||||
},
|
||||
{
|
||||
path: '/toolbox-page',
|
||||
name: config.TOOLBOX_CONFIG_PAGE,
|
||||
component: () => import('@/pages/Toolbox.vue')
|
||||
component: Toolbox
|
||||
},
|
||||
{
|
||||
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"
|
||||
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":
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@highlightjs/vue-plugin/-/vue-plugin-2.1.2.tgz#b7deaaa03452da659a39859437ae0c4bca037600"
|
||||
@@ -3207,6 +3214,18 @@
|
||||
dependencies:
|
||||
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":
|
||||
version "0.2.7"
|
||||
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"
|
||||
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:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.npmjs.org/m3u8-parser/-/m3u8-parser-6.0.0.tgz#e9143313b44f07bb25fdea1c8aac1098d9ada192"
|
||||
|
||||
Reference in New Issue
Block a user