mirror of
https://github.com/Kuingsmile/PicList.git
synced 2026-09-07 00:17:03 +08:00
✨ Feature(custom): optimize picbed config page
This commit is contained in:
@@ -110,6 +110,7 @@
|
|||||||
"dpdm": "^3.14.0",
|
"dpdm": "^3.14.0",
|
||||||
"electron": "^39.2.7",
|
"electron": "^39.2.7",
|
||||||
"electron-builder": "^26.0.12",
|
"electron-builder": "^26.0.12",
|
||||||
|
"electron-devtools-installer": "^4.0.0",
|
||||||
"electron-vite": "^5.0.0",
|
"electron-vite": "^5.0.0",
|
||||||
"eslint": "^9.39.2",
|
"eslint": "^9.39.2",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { createTray, setDockMenu } from 'apis/app/system'
|
|||||||
import { uploadChoosedFiles, uploadClipboardFiles } from 'apis/app/uploader/apis'
|
import { uploadChoosedFiles, uploadClipboardFiles } from 'apis/app/uploader/apis'
|
||||||
import windowManager from 'apis/app/window/windowManager'
|
import windowManager from 'apis/app/window/windowManager'
|
||||||
import { app, globalShortcut, net, Notification, protocol, screen } from 'electron'
|
import { app, globalShortcut, net, Notification, protocol, screen } from 'electron'
|
||||||
|
import { installExtension, VUEJS_DEVTOOLS_BETA } from 'electron-devtools-installer'
|
||||||
import fs from 'fs-extra'
|
import fs from 'fs-extra'
|
||||||
|
|
||||||
import { themesDir } from '~/apis/core/datastore/dirs'
|
import { themesDir } from '~/apis/core/datastore/dirs'
|
||||||
@@ -36,7 +37,6 @@ import { notificationList } from '~/utils/notification'
|
|||||||
import { MemoryMonitor } from '~/utils/performanceOptimizer'
|
import { MemoryMonitor } from '~/utils/performanceOptimizer'
|
||||||
import { CLIPBOARD_IMAGE_FOLDER } from '~/utils/static'
|
import { CLIPBOARD_IMAGE_FOLDER } from '~/utils/static'
|
||||||
import updateChecker from '~/utils/updateChecker'
|
import updateChecker from '~/utils/updateChecker'
|
||||||
|
|
||||||
const isDevelopment = process.env.NODE_ENV !== 'production'
|
const isDevelopment = process.env.NODE_ENV !== 'production'
|
||||||
process.noDeprecation = true
|
process.noDeprecation = true
|
||||||
|
|
||||||
@@ -83,6 +83,13 @@ class LifeCycle {
|
|||||||
|
|
||||||
#onReady() {
|
#onReady() {
|
||||||
const readyFunction = async () => {
|
const readyFunction = async () => {
|
||||||
|
if (isDevelopment) {
|
||||||
|
try {
|
||||||
|
await installExtension(VUEJS_DEVTOOLS_BETA)
|
||||||
|
} catch (e: any) {
|
||||||
|
logger.error('Vue Devtools failed to install:', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
protocol.handle('theme', request => {
|
protocol.handle('theme', request => {
|
||||||
const requestUrl = request.url
|
const requestUrl = request.url
|
||||||
const urlObj = new URL(requestUrl)
|
const urlObj = new URL(requestUrl)
|
||||||
|
|||||||
@@ -1,153 +1,151 @@
|
|||||||
<!-- eslint-disable vue/no-v-html -->
|
<!-- eslint-disable vue/no-v-html -->
|
||||||
<template>
|
<template>
|
||||||
<div id="config-form" :class="[{ white: colorMode === 'white' }]">
|
<div id="config-form" class="no-scrollbar flex h-full w-full flex-1 overflow-auto">
|
||||||
<form class="config-form" @submit.prevent>
|
<SettingSection clas="h-full flex-1" only-one-row>
|
||||||
<!-- Config Name Field -->
|
<SettingCard>
|
||||||
<div class="form-group required">
|
<CustomInput
|
||||||
<label class="form-label">{{ t('pages.configForm.configName') }}</label>
|
|
||||||
<div class="form-control">
|
|
||||||
<input
|
|
||||||
v-model="ruleForm._configName"
|
v-model="ruleForm._configName"
|
||||||
type="text"
|
:title="t('pages.configForm.configName')"
|
||||||
class="form-input"
|
|
||||||
:placeholder="t('pages.configForm.configNamePlaceholder')"
|
:placeholder="t('pages.configForm.configNamePlaceholder')"
|
||||||
:class="{ error: validationErrors._configName }"
|
required
|
||||||
@input="clearFieldError('_configName')"
|
@input="clearFieldError('_configName')"
|
||||||
/>
|
/>
|
||||||
<div v-if="validationErrors._configName" class="error-message">
|
<template v-if="validationErrors._configName" #extra>
|
||||||
|
<div class="mt-1 text-xs text-error">
|
||||||
{{ validationErrors._configName }}
|
{{ validationErrors._configName }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
</div>
|
</SettingCard>
|
||||||
|
|
||||||
<!-- Dynamic Config Fields -->
|
<!-- Dynamic Config Fields -->
|
||||||
<div
|
<SettingCard
|
||||||
v-for="(item, index) in configList"
|
v-for="(item, index) in configList"
|
||||||
:key="item.name + index"
|
:key="item.name + index"
|
||||||
class="form-group"
|
|
||||||
:class="{ required: item.required }"
|
:class="{ required: item.required }"
|
||||||
|
:p1="item.type === 'confirm'"
|
||||||
>
|
>
|
||||||
<div class="form-label-wrapper">
|
<CustomInput
|
||||||
<label class="form-label">{{ item.alias || item.name }}</label>
|
|
||||||
<div v-if="showTooltips && item.tips" class="tooltip-wrapper">
|
|
||||||
<div class="info-icon" @click="toggleTooltip(item.name + index)">
|
|
||||||
<Info :size="20" />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-show="visibleTooltips[item.name + index]"
|
|
||||||
class="tooltip-content"
|
|
||||||
v-html="transformMarkdownToHTML(item.tips)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-control">
|
|
||||||
<!-- Text/Password Input -->
|
|
||||||
<input
|
|
||||||
v-if="item.type === 'input' || item.type === 'password'"
|
v-if="item.type === 'input' || item.type === 'password'"
|
||||||
v-model="ruleForm[item.name]"
|
v-model="ruleForm[item.name]"
|
||||||
type="text"
|
type="text"
|
||||||
class="form-input"
|
|
||||||
:placeholder="item.message || item.name"
|
:placeholder="item.message || item.name"
|
||||||
:class="{ error: validationErrors[item.name] }"
|
:class="{ 'border-error!': validationErrors[item.name] }"
|
||||||
|
:title="item.alias || item.name"
|
||||||
@input="clearFieldError(item.name)"
|
@input="clearFieldError(item.name)"
|
||||||
|
>
|
||||||
|
<template #title-extra>
|
||||||
|
<div v-if="showTooltips && item.tips" class="relative">
|
||||||
|
<div class="info-icon" @click="toggleTooltip(item.name + index)">
|
||||||
|
<Info :size="15" />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-show="visibleTooltips[item.name + index]"
|
||||||
|
class="absolute top-full left-0 z-1000 max-w-[300px] min-w-[200px] rounded-md border border-border bg-bg-secondary p-3 text-xs leading-[1.4] text-main shadow-lg max-md:max-w-[250px] max-md:min-w-[150px]"
|
||||||
|
v-html="transformMarkdownToHTML(item.tips)"
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
<!-- Select (Single) -->
|
</template>
|
||||||
<div v-else-if="item.type === 'list' && item.choices" class="select-wrapper">
|
</CustomInput>
|
||||||
<select
|
<CustomSwitch
|
||||||
|
v-if="item.type === 'confirm'"
|
||||||
v-model="ruleForm[item.name]"
|
v-model="ruleForm[item.name]"
|
||||||
class="form-select"
|
:title="item.alias || item.name"
|
||||||
:class="{ error: validationErrors[item.name] }"
|
:description="item.message || ''"
|
||||||
|
no-border
|
||||||
|
small
|
||||||
@change="clearFieldError(item.name)"
|
@change="clearFieldError(item.name)"
|
||||||
>
|
>
|
||||||
|
<template #switch-text>
|
||||||
|
<span class="text-[0.925rem] font-semibold text-secondary">
|
||||||
|
{{ ruleForm[item.name] ? item.confirmText || 'Yes' : item.cancelText || 'No' }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<template #title-extra>
|
||||||
|
<div v-if="showTooltips && item.tips" class="relative">
|
||||||
|
<div class="info-icon" @click="toggleTooltip(item.name + index)">
|
||||||
|
<Info :size="15" />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-show="visibleTooltips[item.name + index]"
|
||||||
|
class="absolute top-full left-0 z-1000 max-w-[300px] min-w-[200px] rounded-md border border-border bg-bg-secondary p-3 text-xs leading-[1.4] text-main shadow-lg max-md:max-w-[250px] max-md:min-w-[150px]"
|
||||||
|
v-html="transformMarkdownToHTML(item.tips)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</CustomSwitch>
|
||||||
|
<CustomSelect
|
||||||
|
v-if="item.type === 'list' && item.choices"
|
||||||
|
v-model="ruleForm[item.name]"
|
||||||
|
:title="item.alias || item.name"
|
||||||
|
:placeholder="item.message || item.name"
|
||||||
|
:class="{ 'border-danger': validationErrors[item.name] }"
|
||||||
|
:select-list="
|
||||||
|
item.choices.map(choice => ({
|
||||||
|
value: choice.value || choice,
|
||||||
|
label: choice.name || choice.value || choice,
|
||||||
|
}))
|
||||||
|
"
|
||||||
|
:icon="null"
|
||||||
|
@change="clearFieldError(item.name)"
|
||||||
|
>
|
||||||
|
<template #pre-info>
|
||||||
<option value="" disabled>
|
<option value="" disabled>
|
||||||
{{ item.message || item.name }}
|
{{ item.message || item.name }}
|
||||||
</option>
|
</option>
|
||||||
<option
|
</template>
|
||||||
v-for="choice in item.choices"
|
</CustomSelect>
|
||||||
:key="choice.name || choice.value || choice"
|
<MultiSelect
|
||||||
:value="choice.value || choice"
|
v-if="item.type === 'checkbox' && item.choices"
|
||||||
>
|
v-model:choosed="ruleForm[item.name]"
|
||||||
{{ choice.name || choice.value || choice }}
|
:title="item.alias || item.name"
|
||||||
</option>
|
:zero-placeholder="item.message || item.name"
|
||||||
</select>
|
:icon="null"
|
||||||
<div class="select-arrow">
|
:all-list="
|
||||||
<ChevronDownIcon :size="20" />
|
item.choices.map(choice => ({
|
||||||
</div>
|
type: choice.value || choice,
|
||||||
</div>
|
name: choice.name || choice.value || choice,
|
||||||
|
}))
|
||||||
<!-- Multi-Select (Checkbox style) -->
|
"
|
||||||
<div v-else-if="item.type === 'checkbox' && item.choices" class="checkbox-group">
|
|
||||||
<div v-for="choice in item.choices" :key="choice.value || choice" class="checkbox-item">
|
|
||||||
<label class="checkbox-label">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
:value="choice.value || choice"
|
|
||||||
:checked="Array.isArray(ruleForm[item.name]) && ruleForm[item.name].includes(choice.value || choice)"
|
|
||||||
class="checkbox-input"
|
|
||||||
@change="handleCheckboxChange(item.name, choice.value || choice, $event)"
|
|
||||||
/>
|
|
||||||
<span class="checkbox-custom" />
|
|
||||||
<span class="checkbox-text">{{ choice.name || choice.value || choice }}</span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Switch/Toggle -->
|
|
||||||
<label v-else-if="item.type === 'confirm'" class="switch-label">
|
|
||||||
<input
|
|
||||||
v-model="ruleForm[item.name]"
|
|
||||||
type="checkbox"
|
|
||||||
class="switch-input"
|
|
||||||
@change="clearFieldError(item.name)"
|
@change="clearFieldError(item.name)"
|
||||||
/>
|
/>
|
||||||
<span class="switch-slider">
|
|
||||||
<span class="switch-button" />
|
|
||||||
</span>
|
|
||||||
<span class="switch-text">
|
|
||||||
{{ ruleForm[item.name] ? item.confirmText || 'Yes' : item.cancelText || 'No' }}
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<!-- Validation Error -->
|
<!-- Validation Error -->
|
||||||
<div v-if="validationErrors[item.name]" class="error-message">
|
<template v-if="validationErrors[item.name]" #extra>
|
||||||
|
<div class="mt-1 text-xs text-error">
|
||||||
{{ validationErrors[item.name] }}
|
{{ validationErrors[item.name] }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
</div>
|
</SettingCard>
|
||||||
<slot />
|
<slot />
|
||||||
</form>
|
</SettingSection>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { cloneDeep, union } from 'lodash-es'
|
import { cloneDeep, union } from 'lodash-es'
|
||||||
import { ChevronDownIcon, Info } from 'lucide-vue-next'
|
import { Info } from 'lucide-vue-next'
|
||||||
import { marked } from 'marked'
|
import { marked } from 'marked'
|
||||||
import { reactive, ref, watch } from 'vue'
|
import { reactive, ref, watch } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
|
|
||||||
|
import CustomInput from '@/components/common/CustomInput.vue'
|
||||||
|
import CustomSelect from '@/components/common/CustomSelect.vue'
|
||||||
|
import CustomSwitch from '@/components/common/CustomSwitch.vue'
|
||||||
|
import MultiSelect from '@/components/common/MultiSelect.vue'
|
||||||
|
import SettingCard from '@/components/common/SettingCard.vue'
|
||||||
|
import SettingSection from '@/components/common/SettingSection.vue'
|
||||||
import { getConfig } from '@/utils/dataSender'
|
import { getConfig } from '@/utils/dataSender'
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
config: IPicGoPluginConfig[]
|
config: IPicGoPluginConfig[]
|
||||||
type: 'uploader' | 'transformer' | 'plugin'
|
type: 'uploader' | 'transformer' | 'plugin'
|
||||||
id: string
|
id: string
|
||||||
colorMode?: 'white' | 'dark'
|
|
||||||
mode?: 'picbed' | 'plugin'
|
mode?: 'picbed' | 'plugin'
|
||||||
showTooltips?: boolean
|
showTooltips?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const { config: configProp, type, id, mode = 'picbed', showTooltips = true } = defineProps<IProps>()
|
||||||
config: configProp,
|
|
||||||
type,
|
|
||||||
id,
|
|
||||||
colorMode = undefined,
|
|
||||||
mode = 'picbed',
|
|
||||||
showTooltips = true,
|
|
||||||
} = defineProps<IProps>()
|
|
||||||
|
|
||||||
const $route = useRoute()
|
const $route = useRoute()
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
@@ -172,7 +170,7 @@ watch(
|
|||||||
function validateField(fieldName: string, value: any, _?: IPicGoPluginConfig): string | null {
|
function validateField(fieldName: string, value: any, _?: IPicGoPluginConfig): string | null {
|
||||||
if (fieldName === '_configName') {
|
if (fieldName === '_configName') {
|
||||||
if (!value || value.trim() === '') {
|
if (!value || value.trim() === '') {
|
||||||
return 'Configuration name is required'
|
return t('pages.configForm.configNameRequired')
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -202,6 +200,7 @@ function validateForm(): boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function clearFieldError(fieldName: string) {
|
function clearFieldError(fieldName: string) {
|
||||||
|
console.log('Clearing error for field:', fieldName)
|
||||||
if (validationErrors[fieldName]) {
|
if (validationErrors[fieldName]) {
|
||||||
delete validationErrors[fieldName]
|
delete validationErrors[fieldName]
|
||||||
}
|
}
|
||||||
@@ -217,25 +216,6 @@ function toggleTooltip(key: string) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleCheckboxChange(fieldName: string, value: any, event: Event) {
|
|
||||||
const target = event.target as HTMLInputElement
|
|
||||||
const currentValues = Array.isArray(ruleForm[fieldName]) ? [...ruleForm[fieldName]] : []
|
|
||||||
|
|
||||||
if (target.checked) {
|
|
||||||
if (!currentValues.includes(value)) {
|
|
||||||
currentValues.push(value)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const index = currentValues.indexOf(value)
|
|
||||||
if (index > -1) {
|
|
||||||
currentValues.splice(index, 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ruleForm[fieldName] = currentValues
|
|
||||||
clearFieldError(fieldName)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function validate(): Promise<IStringKeyMap | false> {
|
async function validate(): Promise<IStringKeyMap | false> {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const isValid = validateForm()
|
const isValid = validateForm()
|
||||||
@@ -338,5 +318,3 @@ defineExpose({
|
|||||||
getConfigType,
|
getConfigType,
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped src="./css/UnifiedConfigForm.css"></style>
|
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<label class="mb-2 text-sm font-semibold text-secondary">{{ title }}</label>
|
<div class="flex items-center gap-2">
|
||||||
|
<label class="mb-2 text-sm font-semibold text-secondary"
|
||||||
|
>{{ title }}
|
||||||
|
<span v-if="required" class="ml-1 text-red-400">*</span>
|
||||||
|
</label>
|
||||||
|
<slot name="title-extra"></slot>
|
||||||
|
</div>
|
||||||
<div class="relative w-full">
|
<div class="relative w-full">
|
||||||
<input
|
<input
|
||||||
v-model="modelValue"
|
v-model="modelValue"
|
||||||
@@ -27,20 +32,28 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { EyeClosedIcon, EyeIcon } from 'lucide-vue-next'
|
import { EyeClosedIcon, EyeIcon } from 'lucide-vue-next'
|
||||||
import { onMounted, ref } from 'vue'
|
import { onMounted, ref } from 'vue'
|
||||||
|
|
||||||
const modelValue = defineModel<any>()
|
const modelValue = defineModel<any>()
|
||||||
const type = ref('text')
|
const type = ref('text')
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isPassword = false,
|
isPassword = false,
|
||||||
title,
|
title,
|
||||||
inputType = 'text',
|
inputType = 'text',
|
||||||
placeholder,
|
placeholder,
|
||||||
|
required = false,
|
||||||
} = defineProps<{
|
} = defineProps<{
|
||||||
isPassword?: boolean
|
isPassword?: boolean
|
||||||
title: string
|
title: string
|
||||||
inputType?: string
|
inputType?: string
|
||||||
placeholder: string
|
placeholder: string
|
||||||
|
required?: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
inheritAttrs: false,
|
||||||
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (isPassword) {
|
if (isPassword) {
|
||||||
type.value = 'password'
|
type.value = 'password'
|
||||||
|
|||||||
@@ -8,7 +8,9 @@
|
|||||||
<select
|
<select
|
||||||
v-model="modelValue"
|
v-model="modelValue"
|
||||||
class="border-box w-full rounded-md border border-border bg-bg-tertiary p-3 text-sm text-main transition-all duration-200 ease-apple focus:border-accent focus:outline-none"
|
class="border-box w-full rounded-md border border-border bg-bg-tertiary p-3 text-sm text-main transition-all duration-200 ease-apple focus:border-accent focus:outline-none"
|
||||||
|
v-bind="$attrs"
|
||||||
>
|
>
|
||||||
|
<slot name="pre-info"></slot>
|
||||||
<template v-if="selectList.length > 0">
|
<template v-if="selectList.length > 0">
|
||||||
<option v-for="item in selectList" :key="item.value" :value="item.value">
|
<option v-for="item in selectList" :key="item.value" :value="item.value">
|
||||||
{{ item.label }}
|
{{ item.label }}
|
||||||
|
|||||||
@@ -12,9 +12,13 @@
|
|||||||
: 'h-[28px] w-[52px] before:top-[3px] before:left-[3px] before:h-[22px] before:w-[22px]'
|
: 'h-[28px] w-[52px] before:top-[3px] before:left-[3px] before:h-[22px] before:w-[22px]'
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
|
<div class="flex flex-row items-center gap-1">
|
||||||
<div class="flex flex-1 flex-col gap-1">
|
<div class="flex flex-1 flex-col gap-1">
|
||||||
<span class="text-[0.925rem] leading-[1.4] font-semibold text-secondary">{{ title }}</span>
|
<span class="text-[0.925rem] leading-[1.4] font-semibold text-secondary">{{ title }}</span>
|
||||||
<span class="text-xs text-secondary/90">{{ description }}</span>
|
<span v-if="!!description" class="text-xs text-secondary/90">{{ description }}</span>
|
||||||
|
</div>
|
||||||
|
<slot name="switch-text"></slot>
|
||||||
|
<slot name="title-extra"></slot>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
:key="item.type"
|
:key="item.type"
|
||||||
class="flex min-h-[unset] cursor-pointer items-center justify-between px-2 py-1 text-sm leading-[1.4] transition-all duration-fast ease-apple hover:bg-accent/50"
|
class="flex min-h-[unset] cursor-pointer items-center justify-between px-2 py-1 text-sm leading-[1.4] transition-all duration-fast ease-apple hover:bg-accent/50"
|
||||||
>
|
>
|
||||||
<input v-model="choosed" type="checkbox" :value="item.type" class="m-0" />
|
<input v-bind="$attrs" v-model="choosed" type="checkbox" :value="item.type" class="m-0" />
|
||||||
{{ item.name }}
|
{{ item.name }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onClickOutside } from '@vueuse/core'
|
import { onClickOutside } from '@vueuse/core'
|
||||||
import { ChevronDownIcon } from 'lucide-vue-next'
|
import { ChevronDownIcon } from 'lucide-vue-next'
|
||||||
import { nextTick, ref } from 'vue'
|
import { nextTick, onMounted, ref } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
const choosed = defineModel<string[]>('choosed')
|
const choosed = defineModel<string[]>('choosed')
|
||||||
@@ -81,4 +81,10 @@ const {
|
|||||||
zeroPlaceholder: string
|
zeroPlaceholder: string
|
||||||
allList: any
|
allList: any
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (!Array.isArray(choosed.value)) {
|
||||||
|
choosed.value = []
|
||||||
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="relative rounded-lg border border-border bg-bg-secondary shadow-sm" :class="p1 ? 'p-1' : 'p-4'">
|
<div class="relative rounded-lg border border-border bg-bg-secondary shadow-sm" :class="p1 ? 'p-1' : 'p-4'">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
|
<slot name="extra"></slot>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -1,334 +0,0 @@
|
|||||||
#config-form {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.config-form {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Form Groups */
|
|
||||||
.form-group {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group.required .form-label::after {
|
|
||||||
content: ' *';
|
|
||||||
color: var(--color-error);
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-label-wrapper {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-label {
|
|
||||||
font-size: 0.875rem;
|
|
||||||
font-weight: 500;
|
|
||||||
color: var(--color-text-primary);
|
|
||||||
line-height: 1.25;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-control {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tooltip Styles */
|
|
||||||
.tooltip-wrapper {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-icon {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
border-radius: var(--radius-round);
|
|
||||||
padding: 2px;
|
|
||||||
color: var(--color-text-secondary);
|
|
||||||
transition: var(--transition-fast);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info-icon:hover {
|
|
||||||
color: var(--color-accent);
|
|
||||||
background: rgb(0 122 255 / 10%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tooltip-content {
|
|
||||||
position: absolute;
|
|
||||||
top: 100%;
|
|
||||||
left: 0;
|
|
||||||
z-index: 1000;
|
|
||||||
border: 1px solid var(--color-border);
|
|
||||||
border-radius: var(--radius-md);
|
|
||||||
padding: 0.75rem;
|
|
||||||
min-width: 200px;
|
|
||||||
max-width: 300px;
|
|
||||||
font-size: 0.75rem;
|
|
||||||
color: var(--color-text-primary);
|
|
||||||
background: var(--color-background-secondary);
|
|
||||||
box-shadow: var(--shadow-lg);
|
|
||||||
line-height: 1.4;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Input Styles */
|
|
||||||
.form-input {
|
|
||||||
border: 1px solid var(--color-border);
|
|
||||||
border-radius: var(--radius-md);
|
|
||||||
padding: 0.75rem 1rem;
|
|
||||||
width: 100%;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
font-family: inherit;
|
|
||||||
color: var(--color-text-primary);
|
|
||||||
background: var(--color-background-secondary);
|
|
||||||
transition: var(--transition-fast);
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-input:focus {
|
|
||||||
border-color: var(--color-accent);
|
|
||||||
background: var(--color-surface);
|
|
||||||
outline: none;
|
|
||||||
box-shadow: 0 0 0 2px rgb(0 122 255 / 20%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-input::placeholder {
|
|
||||||
color: var(--color-text-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-input.error {
|
|
||||||
border-color: var(--color-error);
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-input.error:focus {
|
|
||||||
box-shadow: 0 0 0 2px rgb(239 68 68 / 20%);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Select Styles */
|
|
||||||
.select-wrapper {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-select {
|
|
||||||
border: 1px solid var(--color-border);
|
|
||||||
border-radius: var(--radius-md);
|
|
||||||
padding: 0.75rem 2.5rem 0.75rem 1rem;
|
|
||||||
width: 100%;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
font-family: inherit;
|
|
||||||
color: var(--color-text-primary);
|
|
||||||
background: var(--color-surface-elevated);
|
|
||||||
transition: var(--transition-fast);
|
|
||||||
appearance: none;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-select:focus {
|
|
||||||
border-color: var(--color-accent);
|
|
||||||
outline: none;
|
|
||||||
box-shadow: 0 0 0 2px rgb(0 122 255 / 20%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-select.error {
|
|
||||||
border-color: var(--color-error);
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-select.error:focus {
|
|
||||||
box-shadow: 0 0 0 2px rgb(239 68 68 / 20%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.select-arrow {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
right: 1rem;
|
|
||||||
color: var(--color-text-secondary);
|
|
||||||
transition: var(--transition-fast);
|
|
||||||
transform: translateY(-50%);
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.select-wrapper:hover .select-arrow,
|
|
||||||
.form-select:focus + .select-arrow {
|
|
||||||
color: var(--color-accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Checkbox Group Styles */
|
|
||||||
.checkbox-group {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 0.75rem;
|
|
||||||
padding: 0.5rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-label {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.75rem;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
color: var(--color-text-primary);
|
|
||||||
transition: var(--transition-fast);
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-label:hover {
|
|
||||||
color: var(--color-accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-input {
|
|
||||||
position: absolute;
|
|
||||||
opacity: 0;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-custom {
|
|
||||||
position: relative;
|
|
||||||
border: 2px solid var(--color-border);
|
|
||||||
border-radius: var(--radius-sm);
|
|
||||||
width: 1.25rem;
|
|
||||||
height: 1.25rem;
|
|
||||||
background: var(--color-surface-elevated);
|
|
||||||
transition: var(--transition-fast);
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-custom::after {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 3px;
|
|
||||||
border: solid white;
|
|
||||||
border-width: 0 2px 2px 0;
|
|
||||||
width: 6px;
|
|
||||||
height: 10px;
|
|
||||||
opacity: 0;
|
|
||||||
transition: var(--transition-fast);
|
|
||||||
content: '';
|
|
||||||
transform: rotate(45deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-input:checked + .checkbox-custom {
|
|
||||||
border-color: var(--color-accent);
|
|
||||||
background: var(--color-accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-input:checked + .checkbox-custom::after {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-input:focus + .checkbox-custom {
|
|
||||||
box-shadow: 0 0 0 2px rgb(0 122 255 / 20%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-text {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Switch Styles */
|
|
||||||
.switch-label {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 1rem;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
color: var(--color-text-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.switch-input {
|
|
||||||
position: absolute;
|
|
||||||
opacity: 0;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.switch-slider {
|
|
||||||
position: relative;
|
|
||||||
border-radius: 0.75rem;
|
|
||||||
width: 3rem;
|
|
||||||
height: 1.5rem;
|
|
||||||
background: linear-gradient(180deg, #d0d3d9 0%, #c0c4cc 100%);
|
|
||||||
transition: var(--transition-fast);
|
|
||||||
transition: all var(--transition-medium);
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.switch-button {
|
|
||||||
position: absolute;
|
|
||||||
top: 2px;
|
|
||||||
left: 2px;
|
|
||||||
border-radius: var(--radius-round);
|
|
||||||
width: 1.25rem;
|
|
||||||
height: 1.25rem;
|
|
||||||
background: linear-gradient(180deg, #ffffff 0%, #f5f5f5 100%);
|
|
||||||
box-shadow: var(--shadow-sm);
|
|
||||||
transition: var(--transition-fast);
|
|
||||||
}
|
|
||||||
|
|
||||||
.switch-input:checked + .switch-slider {
|
|
||||||
background: var(--color-accent);
|
|
||||||
box-shadow:
|
|
||||||
inset 0 1px 3px rgb(0 0 0 / 10%),
|
|
||||||
0 2px 8px rgb(64 158 255 / 30%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.switch-input:checked + .switch-slider .switch-button {
|
|
||||||
transform: translateX(1.5rem);
|
|
||||||
}
|
|
||||||
|
|
||||||
.switch-input:focus + .switch-slider {
|
|
||||||
box-shadow: 0 0 0 2px rgb(0 122 255 / 20%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.switch-text {
|
|
||||||
font-weight: 500;
|
|
||||||
color: var(--color-text-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.switch-input:checked ~ .switch-text {
|
|
||||||
color: var(--color-accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Error Message */
|
|
||||||
.error-message {
|
|
||||||
margin-top: 0.25rem;
|
|
||||||
font-size: 0.75rem;
|
|
||||||
color: var(--color-error);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Responsive Design */
|
|
||||||
@media (width <= 768px) {
|
|
||||||
.config-form {
|
|
||||||
gap: 1.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-input,
|
|
||||||
.form-select {
|
|
||||||
padding: 0.625rem 0.875rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-select {
|
|
||||||
padding-right: 2.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tooltip-content {
|
|
||||||
min-width: 150px;
|
|
||||||
max-width: 250px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Focus styles for accessibility */
|
|
||||||
.form-input:focus-visible,
|
|
||||||
.form-select:focus-visible,
|
|
||||||
.checkbox-input:focus-visible + .checkbox-custom,
|
|
||||||
.switch-input:focus-visible + .switch-slider,
|
|
||||||
.info-icon:focus-visible {
|
|
||||||
outline: 2px solid var(--color-accent);
|
|
||||||
outline-offset: 2px;
|
|
||||||
}
|
|
||||||
@@ -75,7 +75,8 @@
|
|||||||
"pages": {
|
"pages": {
|
||||||
"configForm": {
|
"configForm": {
|
||||||
"configName": "Config Name",
|
"configName": "Config Name",
|
||||||
"configNamePlaceholder": "Please enter config name"
|
"configNamePlaceholder": "Please enter config name",
|
||||||
|
"configNameRequired": "Configure name is required"
|
||||||
},
|
},
|
||||||
"gallery": {
|
"gallery": {
|
||||||
"batchEditUrl": "Batch Edit URL",
|
"batchEditUrl": "Batch Edit URL",
|
||||||
|
|||||||
@@ -75,7 +75,8 @@
|
|||||||
"pages": {
|
"pages": {
|
||||||
"configForm": {
|
"configForm": {
|
||||||
"configName": "配置名",
|
"configName": "配置名",
|
||||||
"configNamePlaceholder": "请输入配置名称"
|
"configNamePlaceholder": "请输入配置名称",
|
||||||
|
"configNameRequired": "配置名称为必填项"
|
||||||
},
|
},
|
||||||
"gallery": {
|
"gallery": {
|
||||||
"batchEditUrl": "批量修改URL",
|
"batchEditUrl": "批量修改URL",
|
||||||
|
|||||||
@@ -75,7 +75,8 @@
|
|||||||
"pages": {
|
"pages": {
|
||||||
"configForm": {
|
"configForm": {
|
||||||
"configName": "配置名稱",
|
"configName": "配置名稱",
|
||||||
"configNamePlaceholder": "請輸入配置名稱"
|
"configNamePlaceholder": "請輸入配置名稱",
|
||||||
|
"configNameRequired": "配置名稱為必填項"
|
||||||
},
|
},
|
||||||
"gallery": {
|
"gallery": {
|
||||||
"batchEditUrl": "批量修改URL",
|
"batchEditUrl": "批量修改URL",
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<div
|
<div
|
||||||
class="flex w-full items-center justify-between gap-4 rounded-2xl border border-border-secondary px-6 py-2 shadow-md max-md:items-stretch max-md:p-5"
|
class="flex w-full items-center justify-between gap-4 rounded-2xl border border-border-secondary px-6 py-2 shadow-md max-md:items-stretch max-md:p-5"
|
||||||
>
|
>
|
||||||
<div class="flex flex-1 items-center gap-4">
|
<div class="flex flex-1 items-center gap-4 p-1">
|
||||||
<ImagesIcon :size="24" class="text-accent" />
|
<ImagesIcon :size="24" class="text-accent" />
|
||||||
<div>
|
<div>
|
||||||
<h1 class="m-0 text-2xl font-semibold tracking-tight text-main">{{ t('pages.gallery.title') }}</h1>
|
<h1 class="m-0 text-2xl font-semibold tracking-tight text-main">{{ t('pages.gallery.title') }}</h1>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<div
|
<div
|
||||||
class="flex w-full items-center justify-between gap-4 rounded-2xl border border-border-secondary px-6 py-2 shadow-md max-md:items-stretch max-md:p-5"
|
class="flex w-full items-center justify-between gap-4 rounded-2xl border border-border-secondary px-6 py-2 shadow-md max-md:items-stretch max-md:p-5"
|
||||||
>
|
>
|
||||||
<div class="flex flex-1 flex-wrap items-center gap-4">
|
<div class="flex flex-1 flex-wrap items-center gap-4 p-2">
|
||||||
<Settings :size="24" class="text-accent" />
|
<Settings :size="24" class="text-accent" />
|
||||||
<div>
|
<div>
|
||||||
<h1 class="m-0 text-2xl font-semibold tracking-tight text-main">{{ t('pages.settings.title') }}</h1>
|
<h1 class="m-0 text-2xl font-semibold tracking-tight text-main">{{ t('pages.settings.title') }}</h1>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<div
|
<div
|
||||||
class="flex w-full items-center justify-between gap-4 overflow-visible rounded-2xl border border-border-secondary px-6 py-2 shadow-md max-md:items-stretch max-md:p-5"
|
class="flex w-full items-center justify-between gap-4 overflow-visible rounded-2xl border border-border-secondary px-6 py-2 shadow-md max-md:items-stretch max-md:p-5"
|
||||||
>
|
>
|
||||||
<div class="flex flex-1 flex-wrap items-center gap-4">
|
<div class="flex flex-1 flex-wrap items-center gap-4 p-1">
|
||||||
<DatabaseIcon :size="24" class="text-accent" />
|
<DatabaseIcon :size="24" class="text-accent" />
|
||||||
<div>
|
<div>
|
||||||
<h1 class="m-0 text-2xl font-semibold tracking-tight text-main">{{ t('pages.plugin.title') }}</h1>
|
<h1 class="m-0 text-2xl font-semibold tracking-tight text-main">{{ t('pages.plugin.title') }}</h1>
|
||||||
@@ -113,7 +113,7 @@
|
|||||||
<div
|
<div
|
||||||
v-for="item in pluginList"
|
v-for="item in pluginList"
|
||||||
:key="item.fullName"
|
:key="item.fullName"
|
||||||
class="relative flex h-auto min-h-[200px] flex-col rounded-xl border-2 border-border-secondary p-6 shadow-md transition-all duration-200 ease-apple hover:border-accent hover:shadow-xl [.disabled]:opacity-70"
|
class="relative flex h-auto flex-col rounded-xl border-2 border-border-secondary p-6 shadow-md transition-all duration-200 ease-apple hover:border-accent hover:shadow-xl [.disabled]:opacity-70"
|
||||||
:class="{ disabled: !item.enabled && !searchText }"
|
:class="{ disabled: !item.enabled && !searchText }"
|
||||||
>
|
>
|
||||||
<!-- Plugin Badge -->
|
<!-- Plugin Badge -->
|
||||||
@@ -275,7 +275,6 @@
|
|||||||
ref="$configForm"
|
ref="$configForm"
|
||||||
:config="config"
|
:config="config"
|
||||||
:type="currentType"
|
:type="currentType"
|
||||||
color-mode="white"
|
|
||||||
mode="plugin"
|
mode="plugin"
|
||||||
:show-tooltips="false"
|
:show-tooltips="false"
|
||||||
/>
|
/>
|
||||||
@@ -324,7 +323,7 @@
|
|||||||
<div
|
<div
|
||||||
v-for="item in filteredBrowsePlugins"
|
v-for="item in filteredBrowsePlugins"
|
||||||
:key="item.fullName"
|
:key="item.fullName"
|
||||||
class="relative flex h-auto min-h-[200px] flex-col rounded-xl border-2 border-border-secondary p-6 shadow-md transition-all duration-200 ease-apple hover:border-accent hover:shadow-xl [.disabled]:opacity-70"
|
class="relative flex h-auto flex-col rounded-xl border-2 border-border-secondary p-6 shadow-md transition-all duration-200 ease-apple hover:border-accent hover:shadow-xl [.disabled]:opacity-70"
|
||||||
>
|
>
|
||||||
<div class="mb-4 flex items-start gap-4">
|
<div class="mb-4 flex items-start gap-4">
|
||||||
<img
|
<img
|
||||||
|
|||||||
@@ -5,14 +5,10 @@
|
|||||||
<div
|
<div
|
||||||
class="flex w-full items-center justify-between gap-4 rounded-2xl border border-border-secondary px-6 py-2 shadow-md"
|
class="flex w-full items-center justify-between gap-4 rounded-2xl border border-border-secondary px-6 py-2 shadow-md"
|
||||||
>
|
>
|
||||||
<div class="flex flex-wrap items-center gap-4 max-md:justify-center max-md:text-center">
|
<div class="flex flex-wrap items-center gap-4 p-2 max-md:justify-center max-md:text-center">
|
||||||
<div
|
<Settings2 :size="24" class="text-accent" />
|
||||||
class="border-xl flex h-[56px] w-[56px] items-center justify-center rounded-2xl border-none text-accent shadow-sm max-md:h-[35px] max-md:w-[35px]"
|
|
||||||
>
|
|
||||||
<Settings2 :size="28" />
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-col gap-1 max-md:text-center">
|
<div class="flex flex-col gap-1 max-md:text-center">
|
||||||
<h1 class="m-0 text-2xl font-bold tracking-tight text-main">
|
<h1 class="m-0 text-xl font-bold tracking-tight text-main">
|
||||||
{{ `${picBedName || type} ${t('pages.uploaderConfig.title')}` }}
|
{{ `${picBedName || type} ${t('pages.uploaderConfig.title')}` }}
|
||||||
</h1>
|
</h1>
|
||||||
<p class="m-0 text-sm text-secondary">
|
<p class="m-0 text-sm text-secondary">
|
||||||
|
|||||||
@@ -1,72 +1,54 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="picbeds-page">
|
<div class="relative flex h-full w-full items-center justify-center">
|
||||||
<div class="page-container">
|
<div class="relative z-1 flex h-full w-full flex-col items-center justify-start gap-4 rounded-xl border-none p-4">
|
||||||
<header class="page-header">
|
<header
|
||||||
<div class="header-content">
|
class="flex w-full items-center justify-between gap-4 overflow-visible rounded-2xl border border-border-secondary px-6 py-2 shadow-md max-md:items-stretch max-md:p-5"
|
||||||
<div class="header-icon">
|
>
|
||||||
<Cloud :size="38" :stroke-width="1.5" />
|
<div class="flex flex-1 flex-wrap items-center gap-4 p-2">
|
||||||
</div>
|
<Cloud :size="24" class="text-accent" />
|
||||||
<div class="header-text">
|
<h1 class="m-0 text-2xl font-semibold tracking-tight text-main">
|
||||||
<div class="title-row">
|
|
||||||
<h1 class="page-title">
|
|
||||||
{{ picBedName }}
|
{{ picBedName }}
|
||||||
</h1>
|
</h1>
|
||||||
<button class="doc-link-btn" :title="t('pages.picBedConfigs.viewDoc')" @click="handleNameClick">
|
<CustomButton
|
||||||
<ExternalLink :size="16" />
|
type="secondary"
|
||||||
<span>{{ t('pages.picBedConfigs.viewDoc') }}</span>
|
:icon="ExternalLink"
|
||||||
</button>
|
:text="t('pages.picBedConfigs.viewDoc')"
|
||||||
|
@click="handleNameClick"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="flex flex-wrap gap-3 overflow-visible">
|
||||||
</div>
|
<CustomButton
|
||||||
<div class="header-actions">
|
type="secondary"
|
||||||
<button class="btn btn-secondary btn-glow" @click="imageProcessDialogVisible = true">
|
:icon="Settings"
|
||||||
<Settings :size="16" />
|
:text="t('pages.upload.imageProcessNameSingle')"
|
||||||
<span>{{ t('pages.upload.imageProcessNameSingle') }}</span>
|
@click="imageProcessDialogVisible = true"
|
||||||
</button>
|
/>
|
||||||
<button class="btn btn-secondary btn-glow" @click="handleCopyApi">
|
<CustomButton type="primary" :icon="Copy" :text="t('pages.picBedConfigs.copyAPI')" @click="handleCopyApi" />
|
||||||
<Copy :size="16" />
|
|
||||||
<span>{{ t('pages.picBedConfigs.copyAPI') }}</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<!-- Main Content Card -->
|
<!-- Main Content Card -->
|
||||||
<main class="main-content">
|
<div
|
||||||
<div v-if="config.length > 0" class="config-card">
|
class="relative flex h-full w-full flex-1 items-center justify-center overflow-hidden rounded-2xl border border-border-secondary shadow-md"
|
||||||
<!-- Card Header -->
|
>
|
||||||
<div class="card-header">
|
<div class="no-scrollbar flex h-full w-full flex-1 overflow-auto rounded-sm">
|
||||||
<div class="card-header-icon">
|
<div v-if="config.length > 0" class="flex h-full w-full">
|
||||||
<Settings :size="18" />
|
|
||||||
</div>
|
|
||||||
<h2 class="card-title">{{ t('pages.picBedConfigs.configSettings') }}</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Config Form -->
|
<!-- Config Form -->
|
||||||
<div class="card-body">
|
<config-form :id="type" ref="$configForm" :config="config" type="uploader">
|
||||||
<config-form :id="type" ref="$configForm" :config="config" type="uploader" color-mode="white">
|
|
||||||
<!-- Action Buttons -->
|
<!-- Action Buttons -->
|
||||||
<div class="action-buttons">
|
<div class="mb-4 flex flex-wrap gap-3 rounded-xl border border-border bg-accent/10 p-4">
|
||||||
<button class="btn btn-outline" type="button" @click="handleReset">
|
<CustomButton type="secondary" :icon="RotateCcw" :text="t('common.reset')" @click="handleReset" />
|
||||||
<RotateCcw :size="16" />
|
<CustomButton type="primary" :icon="Check" :text="t('common.confirm')" @click="handleConfirm" />
|
||||||
<span>{{ t('common.reset') }}</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button class="btn btn-success btn-glow" type="button" @click="handleConfirm">
|
|
||||||
<Check :size="16" />
|
|
||||||
<span>{{ t('common.confirm') }}</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<div v-if="picBedConfigList.length > 0" class="dropdown-wrapper">
|
<div v-if="picBedConfigList.length > 0" class="dropdown-wrapper">
|
||||||
<button
|
<CustomButton
|
||||||
class="btn btn-warning btn-glow dropdown-trigger"
|
type="primary"
|
||||||
type="button"
|
:icon="Import"
|
||||||
|
:text="t('common.import')"
|
||||||
|
class="bg-warning!"
|
||||||
@click="toggleDropdown"
|
@click="toggleDropdown"
|
||||||
@blur="handleDropdownBlur"
|
@blur="handleDropdownBlur"
|
||||||
>
|
/>
|
||||||
<Import :size="16" />
|
|
||||||
<span>{{ t('common.import') }}</span>
|
|
||||||
<ChevronDown :size="14" class="dropdown-chevron" :class="{ rotated: dropdownVisible }" />
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<Transition name="dropdown">
|
<Transition name="dropdown">
|
||||||
<div v-show="dropdownVisible" class="dropdown-menu">
|
<div v-show="dropdownVisible" class="dropdown-menu">
|
||||||
@@ -90,7 +72,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</config-form>
|
</config-form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Empty State -->
|
<!-- Empty State -->
|
||||||
<div v-else class="empty-state-card">
|
<div v-else class="empty-state-card">
|
||||||
@@ -102,7 +83,8 @@
|
|||||||
<p class="empty-description">{{ t('pages.picBedConfigs.noConfigOptionsDesc') }}</p>
|
<p class="empty-description">{{ t('pages.picBedConfigs.noConfigOptionsDesc') }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Loading Overlay -->
|
<!-- Loading Overlay -->
|
||||||
@@ -119,48 +101,28 @@
|
|||||||
</Transition>
|
</Transition>
|
||||||
|
|
||||||
<transition name="modal">
|
<transition name="modal">
|
||||||
<div v-if="imageProcessDialogVisible" class="modal-overlay" :class="advancedAnimation" @click.stop>
|
<CustomModal
|
||||||
<div class="modal-container" @click.stop>
|
v-if="imageProcessDialogVisible"
|
||||||
<div class="modal-header">
|
v-model:visible="imageProcessDialogVisible"
|
||||||
<h3 class="modal-title">
|
:title="t('pages.imageProcess.title')"
|
||||||
{{ t('pages.imageProcess.title') }}
|
:description="t('pages.imageProcess.subtitle-PerPicbed')"
|
||||||
</h3>
|
>
|
||||||
<span class="modal-subtitle">
|
|
||||||
{{ t('pages.imageProcess.subtitle-PerPicbed') }}
|
|
||||||
</span>
|
|
||||||
<button class="modal-close" @click="imageProcessDialogVisible = false">
|
|
||||||
<XIcon :size="20" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-content">
|
|
||||||
<ImageProcessSetting :config-id="uuidValue" :current-picbed-name="currentPicbedType" />
|
<ImageProcessSetting :config-id="uuidValue" :current-picbed-name="currentPicbedType" />
|
||||||
</div>
|
</CustomModal>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import {
|
import { Check, Cloud, Copy, ExternalLink, FileJson, FolderOpen, Import, RotateCcw, Settings } from 'lucide-vue-next'
|
||||||
Check,
|
|
||||||
ChevronDown,
|
|
||||||
Cloud,
|
|
||||||
Copy,
|
|
||||||
ExternalLink,
|
|
||||||
FileJson,
|
|
||||||
FolderOpen,
|
|
||||||
Import,
|
|
||||||
RotateCcw,
|
|
||||||
Settings,
|
|
||||||
XIcon,
|
|
||||||
} from 'lucide-vue-next'
|
|
||||||
import { v4 as uuid } from 'uuid'
|
import { v4 as uuid } from 'uuid'
|
||||||
import { computed, onBeforeMount, ref, useTemplateRef } from 'vue'
|
import { onBeforeMount, ref, useTemplateRef } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
|
||||||
|
import CustomButton from '@/components/common/CustomButton.vue'
|
||||||
|
import CustomModal from '@/components/common/CustomModal.vue'
|
||||||
import ImageProcessSetting from '@/components/ImageProcessSetting.vue'
|
import ImageProcessSetting from '@/components/ImageProcessSetting.vue'
|
||||||
import ConfigForm from '@/components/UnifiedConfigForm.vue'
|
import ConfigForm from '@/components/UnifiedConfigForm.vue'
|
||||||
import useMessage from '@/hooks/useMessage'
|
import useMessage from '@/hooks/useMessage'
|
||||||
@@ -180,7 +142,6 @@ const picBedName = ref('')
|
|||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const dropdownVisible = ref(false)
|
const dropdownVisible = ref(false)
|
||||||
const imageProcessDialogVisible = ref(false)
|
const imageProcessDialogVisible = ref(false)
|
||||||
const enableAdvancedAnimation = ref(false)
|
|
||||||
const $route = useRoute()
|
const $route = useRoute()
|
||||||
const $router = useRouter()
|
const $router = useRouter()
|
||||||
const $configForm = useTemplateRef('$configForm')
|
const $configForm = useTemplateRef('$configForm')
|
||||||
@@ -189,18 +150,9 @@ const currentPicbedType = $route.params.type as string
|
|||||||
|
|
||||||
type.value = $route.params.type as string
|
type.value = $route.params.type as string
|
||||||
|
|
||||||
async function initConf() {
|
|
||||||
enableAdvancedAnimation.value = (await getConfig<boolean>(configPaths.settings.isCustomMiniIcon)) || false
|
|
||||||
}
|
|
||||||
|
|
||||||
const advancedAnimation = computed(() => ({
|
|
||||||
advancedAnimation: enableAdvancedAnimation.value,
|
|
||||||
}))
|
|
||||||
|
|
||||||
onBeforeMount(async () => {
|
onBeforeMount(async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
initConf()
|
|
||||||
await getPicBeds()
|
await getPicBeds()
|
||||||
await getPicBedConfigList()
|
await getPicBedConfigList()
|
||||||
} finally {
|
} finally {
|
||||||
@@ -208,7 +160,7 @@ onBeforeMount(async () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
function toggleDropdown(_: Event) {
|
function toggleDropdown() {
|
||||||
dropdownVisible.value = !dropdownVisible.value
|
dropdownVisible.value = !dropdownVisible.value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5993,6 +5993,13 @@ electron-builder@^26.0.12:
|
|||||||
simple-update-notifier "2.0.0"
|
simple-update-notifier "2.0.0"
|
||||||
yargs "^17.6.2"
|
yargs "^17.6.2"
|
||||||
|
|
||||||
|
electron-devtools-installer@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/electron-devtools-installer/-/electron-devtools-installer-4.0.0.tgz#6556a6a326cddea18194cb6d97d85c8ae329dedf"
|
||||||
|
integrity sha512-9Tntu/jtfSn0n6N/ZI6IdvRqXpDyLQiDuuIbsBI+dL+1Ef7C8J2JwByw58P3TJiNeuqyV3ZkphpNWuZK5iSY2w==
|
||||||
|
dependencies:
|
||||||
|
unzip-crx-3 "^0.2.0"
|
||||||
|
|
||||||
electron-publish@26.0.11:
|
electron-publish@26.0.11:
|
||||||
version "26.0.11"
|
version "26.0.11"
|
||||||
resolved "https://registry.yarnpkg.com/electron-publish/-/electron-publish-26.0.11.tgz#92c9329a101af2836d9d228c82966eca1eee9a7b"
|
resolved "https://registry.yarnpkg.com/electron-publish/-/electron-publish-26.0.11.tgz#92c9329a101af2836d9d228c82966eca1eee9a7b"
|
||||||
@@ -7725,6 +7732,11 @@ image-size@^2.0.2:
|
|||||||
resolved "https://registry.yarnpkg.com/image-size/-/image-size-2.0.2.tgz#84a7b43704db5736f364bf0d1b029821299b4bdc"
|
resolved "https://registry.yarnpkg.com/image-size/-/image-size-2.0.2.tgz#84a7b43704db5736f364bf0d1b029821299b4bdc"
|
||||||
integrity sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w==
|
integrity sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w==
|
||||||
|
|
||||||
|
immediate@~3.0.5:
|
||||||
|
version "3.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
|
||||||
|
integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==
|
||||||
|
|
||||||
import-fresh@^3.0.0, import-fresh@^3.2.1, import-fresh@^3.3.0:
|
import-fresh@^3.0.0, import-fresh@^3.2.1, import-fresh@^3.3.0:
|
||||||
version "3.3.0"
|
version "3.3.0"
|
||||||
resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
|
resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
|
||||||
@@ -8235,6 +8247,16 @@ jstoxml@^2.0.0:
|
|||||||
resolved "https://registry.npmmirror.com/jstoxml/-/jstoxml-2.2.9.tgz#2eebd5e55383fe66a375022ca0aa88f77bc4fb84"
|
resolved "https://registry.npmmirror.com/jstoxml/-/jstoxml-2.2.9.tgz#2eebd5e55383fe66a375022ca0aa88f77bc4fb84"
|
||||||
integrity sha512-OYWlK0j+roh+eyaMROlNbS5cd5R25Y+IUpdl7cNdB8HNrkgwQzIS7L9MegxOiWNBj9dQhA/yAxiMwCC5mwNoBw==
|
integrity sha512-OYWlK0j+roh+eyaMROlNbS5cd5R25Y+IUpdl7cNdB8HNrkgwQzIS7L9MegxOiWNBj9dQhA/yAxiMwCC5mwNoBw==
|
||||||
|
|
||||||
|
jszip@^3.1.0:
|
||||||
|
version "3.10.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.10.1.tgz#34aee70eb18ea1faec2f589208a157d1feb091c2"
|
||||||
|
integrity sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==
|
||||||
|
dependencies:
|
||||||
|
lie "~3.3.0"
|
||||||
|
pako "~1.0.2"
|
||||||
|
readable-stream "~2.3.6"
|
||||||
|
setimmediate "^1.0.5"
|
||||||
|
|
||||||
keycode@2.2.0:
|
keycode@2.2.0:
|
||||||
version "2.2.0"
|
version "2.2.0"
|
||||||
resolved "https://registry.npmjs.org/keycode/-/keycode-2.2.0.tgz#3d0af56dc7b8b8e5cba8d0a97f107204eec22b04"
|
resolved "https://registry.npmjs.org/keycode/-/keycode-2.2.0.tgz#3d0af56dc7b8b8e5cba8d0a97f107204eec22b04"
|
||||||
@@ -8301,6 +8323,13 @@ libheif-js@^1.19.8:
|
|||||||
resolved "https://registry.yarnpkg.com/libheif-js/-/libheif-js-1.19.8.tgz#fcbf3571ef28b6199dd052bc4d2cb7cce56ddf06"
|
resolved "https://registry.yarnpkg.com/libheif-js/-/libheif-js-1.19.8.tgz#fcbf3571ef28b6199dd052bc4d2cb7cce56ddf06"
|
||||||
integrity sha512-vQJWusIxO7wavpON1dusciL8Go9jsIQ+EUrckauFYAiSTjcmLAsuJh3SszLpvkwPci3JcL41ek2n+LUZGFpPIQ==
|
integrity sha512-vQJWusIxO7wavpON1dusciL8Go9jsIQ+EUrckauFYAiSTjcmLAsuJh3SszLpvkwPci3JcL41ek2n+LUZGFpPIQ==
|
||||||
|
|
||||||
|
lie@~3.3.0:
|
||||||
|
version "3.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a"
|
||||||
|
integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==
|
||||||
|
dependencies:
|
||||||
|
immediate "~3.0.5"
|
||||||
|
|
||||||
lightningcss-android-arm64@1.30.2:
|
lightningcss-android-arm64@1.30.2:
|
||||||
version "1.30.2"
|
version "1.30.2"
|
||||||
resolved "https://registry.yarnpkg.com/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz#6966b7024d39c94994008b548b71ab360eb3a307"
|
resolved "https://registry.yarnpkg.com/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz#6966b7024d39c94994008b548b71ab360eb3a307"
|
||||||
@@ -9401,6 +9430,11 @@ package-json-from-dist@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505"
|
resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505"
|
||||||
integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==
|
integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==
|
||||||
|
|
||||||
|
pako@~1.0.2:
|
||||||
|
version "1.0.11"
|
||||||
|
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
|
||||||
|
integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==
|
||||||
|
|
||||||
parent-module@^1.0.0:
|
parent-module@^1.0.0:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
|
resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
|
||||||
@@ -9860,6 +9894,19 @@ readable-stream@^3.4.0, readable-stream@^3.5.0:
|
|||||||
string_decoder "^1.1.1"
|
string_decoder "^1.1.1"
|
||||||
util-deprecate "^1.0.1"
|
util-deprecate "^1.0.1"
|
||||||
|
|
||||||
|
readable-stream@~2.3.6:
|
||||||
|
version "2.3.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b"
|
||||||
|
integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==
|
||||||
|
dependencies:
|
||||||
|
core-util-is "~1.0.0"
|
||||||
|
inherits "~2.0.3"
|
||||||
|
isarray "~1.0.0"
|
||||||
|
process-nextick-args "~2.0.0"
|
||||||
|
safe-buffer "~5.1.1"
|
||||||
|
string_decoder "~1.1.1"
|
||||||
|
util-deprecate "~1.0.1"
|
||||||
|
|
||||||
regenerator-runtime@^0.13.11:
|
regenerator-runtime@^0.13.11:
|
||||||
version "0.13.11"
|
version "0.13.11"
|
||||||
resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9"
|
resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9"
|
||||||
@@ -10250,6 +10297,11 @@ serialize-error@^7.0.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
type-fest "^0.13.1"
|
type-fest "^0.13.1"
|
||||||
|
|
||||||
|
setimmediate@^1.0.5:
|
||||||
|
version "1.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
|
||||||
|
integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==
|
||||||
|
|
||||||
sharp@0.34.4:
|
sharp@0.34.4:
|
||||||
version "0.34.4"
|
version "0.34.4"
|
||||||
resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.34.4.tgz#73c2c5a425e98250b8b927e5537f711da8966e38"
|
resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.34.4.tgz#73c2c5a425e98250b8b927e5537f711da8966e38"
|
||||||
@@ -11252,6 +11304,15 @@ unplugin@^2.3.4:
|
|||||||
picomatch "^4.0.3"
|
picomatch "^4.0.3"
|
||||||
webpack-virtual-modules "^0.6.2"
|
webpack-virtual-modules "^0.6.2"
|
||||||
|
|
||||||
|
unzip-crx-3@^0.2.0:
|
||||||
|
version "0.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/unzip-crx-3/-/unzip-crx-3-0.2.0.tgz#d5324147b104a8aed9ae8639c95521f6f7cda292"
|
||||||
|
integrity sha512-0+JiUq/z7faJ6oifVB5nSwt589v1KCduqIJupNVDoWSXZtWDmjDGO3RAEOvwJ07w90aoXoP4enKsR7ecMrJtWQ==
|
||||||
|
dependencies:
|
||||||
|
jszip "^3.1.0"
|
||||||
|
mkdirp "^0.5.1"
|
||||||
|
yaku "^0.16.6"
|
||||||
|
|
||||||
update-browserslist-db@^1.1.3:
|
update-browserslist-db@^1.1.3:
|
||||||
version "1.1.3"
|
version "1.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz#348377dd245216f9e7060ff50b15a1b740b75420"
|
resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz#348377dd245216f9e7060ff50b15a1b740b75420"
|
||||||
@@ -11788,6 +11849,11 @@ y18n@^5.0.5:
|
|||||||
resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
|
resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
|
||||||
integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
|
integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
|
||||||
|
|
||||||
|
yaku@^0.16.6:
|
||||||
|
version "0.16.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/yaku/-/yaku-0.16.7.tgz#1d195c78aa9b5bf8479c895b9504fd4f0847984e"
|
||||||
|
integrity sha512-Syu3IB3rZvKvYk7yTiyl1bo/jiEFaaStrgv1V2TIJTqYPStSMQVO8EQjg/z+DRzLq/4LIIharNT3iH1hylEIRw==
|
||||||
|
|
||||||
yallist@^3.0.2:
|
yallist@^3.0.2:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
|
resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
|
||||||
|
|||||||
Reference in New Issue
Block a user