mirror of
https://github.com/Kuingsmile/PicList.git
synced 2026-05-06 20:42:57 +08:00
✨ Feature(custom): support duplicate config and the UI of confirm/input box is optimized
This commit is contained in:
@@ -7,6 +7,7 @@ import deleteRoutes from '~/events/rpc/routes/picbed/delete'
|
||||
import { IRPCActionType, IRPCType } from '~/utils/enum'
|
||||
import {
|
||||
deleteUploaderConfig,
|
||||
duplicateUploaderConfig,
|
||||
getUploaderConfigList,
|
||||
resetUploaderConfig,
|
||||
selectUploaderConfig,
|
||||
@@ -45,6 +46,15 @@ const picbedRoutes = [
|
||||
},
|
||||
type: IRPCType.INVOKE,
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.PICBED_DUPLICATE_CONFIG,
|
||||
handler: async (_: IIPCEvent, args: [type: string, id: string, newName: string]) => {
|
||||
const [type, id, newName] = args
|
||||
const config = duplicateUploaderConfig(type, id, newName)
|
||||
return config
|
||||
},
|
||||
type: IRPCType.INVOKE,
|
||||
},
|
||||
{
|
||||
action: IRPCActionType.UPLOADER_SELECT,
|
||||
handler: async (_: IIPCEvent, args: [type: string, id: string]) => {
|
||||
|
||||
@@ -92,6 +92,7 @@ export const IRPCActionType = {
|
||||
PICBED_GET_PICBED_CONFIG: 'PICBED_GET_PICBED_CONFIG',
|
||||
PICBED_GET_CONFIG_LIST: 'PICBED_GET_CONFIG_LIST',
|
||||
PICBED_DELETE_CONFIG: 'PICBED_DELETE_CONFIG',
|
||||
PICBED_DUPLICATE_CONFIG: 'PICBED_DUPLICATE_CONFIG',
|
||||
UPLOADER_CHANGE_CURRENT: 'UPLOADER_CHANGE_CURRENT',
|
||||
UPLOADER_SELECT: 'UPLOADER_SELECT',
|
||||
UPLOADER_UPDATE_CONFIG: 'UPLOADER_UPDATE_CONFIG',
|
||||
|
||||
@@ -151,6 +151,34 @@ export const deleteUploaderConfig = (type: string, id: string): IUploaderConfigI
|
||||
}
|
||||
}
|
||||
|
||||
export const duplicateUploaderConfig = (type: string, id: string, newName: string): IUploaderConfigItem | void => {
|
||||
const { configList, defaultId } = getUploaderConfigList(type)
|
||||
const originalConfig = configList.find((item: IStringKeyMap) => item._id === id)
|
||||
if (!originalConfig) {
|
||||
return
|
||||
}
|
||||
|
||||
const duplicatedConfig: IUploaderConfigListItem = {
|
||||
...originalConfig,
|
||||
_configName: newName,
|
||||
_id: uuid(),
|
||||
_createdAt: Date.now(),
|
||||
_updatedAt: Date.now(),
|
||||
}
|
||||
|
||||
const updatedConfigList = [...configList, duplicatedConfig]
|
||||
console.log('updatedConfigList', updatedConfigList)
|
||||
|
||||
picgo.saveConfig({
|
||||
[`uploader.${type}.configList`]: updatedConfigList,
|
||||
})
|
||||
|
||||
return {
|
||||
configList: updatedConfigList,
|
||||
defaultId,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* upgrade old uploader config to new format
|
||||
*/
|
||||
|
||||
@@ -1,51 +1,61 @@
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<div v-if="showInputBoxVisible" class="inputbox-overlay">
|
||||
<div class="inputbox-container" @click.stop>
|
||||
<div class="inputbox-header">
|
||||
<h3 class="inputbox-title">
|
||||
{{ inputBoxOptions.title || t('pages.inputBox.title') }}
|
||||
</h3>
|
||||
<button class="inputbox-close" @click="handleInputBoxCancel">
|
||||
<X :size="20" />
|
||||
</button>
|
||||
</div>
|
||||
<div class="inputbox-content">
|
||||
<textarea
|
||||
v-if="inputBoxOptions.multiLine"
|
||||
v-model="inputBoxValue"
|
||||
:placeholder="inputBoxOptions.placeholder"
|
||||
class="inputbox-textarea"
|
||||
rows="4"
|
||||
@keyup.ctrl.enter="handleInputBoxConfirm"
|
||||
@keyup.escape="handleInputBoxCancel"
|
||||
/>
|
||||
<input
|
||||
v-else
|
||||
v-model="inputBoxValue"
|
||||
:placeholder="inputBoxOptions.placeholder"
|
||||
class="inputbox-input"
|
||||
type="text"
|
||||
@keyup.enter="handleInputBoxConfirm"
|
||||
@keyup.escape="handleInputBoxCancel"
|
||||
/>
|
||||
</div>
|
||||
<div class="inputbox-actions">
|
||||
<button class="inputbox-btn cancel-btn" @click="handleInputBoxCancel">
|
||||
{{ t('common.cancel') }}
|
||||
</button>
|
||||
<button class="inputbox-btn confirm-btn primary" @click="handleInputBoxConfirm">
|
||||
{{ t('common.confirm') }}
|
||||
</button>
|
||||
</div>
|
||||
<Transition name="inputbox-fade">
|
||||
<div v-if="showInputBoxVisible" class="inputbox-overlay" @click="handleInputBoxCancel">
|
||||
<Transition name="inputbox-scale">
|
||||
<div v-if="showInputBoxVisible" class="inputbox-container" @click.stop>
|
||||
<button class="inputbox-close" @click="handleInputBoxCancel">
|
||||
<X :size="20" />
|
||||
</button>
|
||||
|
||||
<div class="inputbox-body">
|
||||
<h3 class="inputbox-title">
|
||||
{{ inputBoxOptions.title || t('pages.inputBox.title') }}
|
||||
</h3>
|
||||
|
||||
<div class="inputbox-content">
|
||||
<textarea
|
||||
v-if="inputBoxOptions.multiLine"
|
||||
ref="textareaRef"
|
||||
v-model="inputBoxValue"
|
||||
:placeholder="inputBoxOptions.placeholder"
|
||||
class="inputbox-textarea"
|
||||
rows="4"
|
||||
@keyup.ctrl.enter="handleInputBoxConfirm"
|
||||
@keyup.meta.enter="handleInputBoxConfirm"
|
||||
@keyup.escape="handleInputBoxCancel"
|
||||
/>
|
||||
<input
|
||||
v-else
|
||||
ref="inputRef"
|
||||
v-model="inputBoxValue"
|
||||
:placeholder="inputBoxOptions.placeholder"
|
||||
class="inputbox-input"
|
||||
type="text"
|
||||
@keyup.enter="handleInputBoxConfirm"
|
||||
@keyup.escape="handleInputBoxCancel"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="inputbox-actions">
|
||||
<button class="inputbox-btn cancel-btn" @click="handleInputBoxCancel">
|
||||
{{ t('common.cancel') }}
|
||||
</button>
|
||||
<button class="inputbox-btn confirm-btn" :disabled="!inputBoxValue.trim()" @click="handleInputBoxConfirm">
|
||||
{{ t('common.confirm') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { X } from 'lucide-vue-next'
|
||||
import { onBeforeMount, onBeforeUnmount, reactive, ref } from 'vue'
|
||||
import { nextTick, onBeforeMount, onBeforeUnmount, reactive, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
import $bus from '@/utils/bus'
|
||||
@@ -55,6 +65,8 @@ import type { IShowInputBoxOption } from '#/types/types'
|
||||
const { t } = useI18n()
|
||||
const inputBoxValue = ref('')
|
||||
const showInputBoxVisible = ref(false)
|
||||
const inputRef = ref<HTMLInputElement>()
|
||||
const textareaRef = ref<HTMLTextAreaElement>()
|
||||
const inputBoxOptions = reactive({
|
||||
title: '',
|
||||
placeholder: '',
|
||||
@@ -67,12 +79,21 @@ function handleIpcInputBoxEvent(options: IShowInputBoxOption) {
|
||||
initInputBoxValue(options)
|
||||
}
|
||||
|
||||
function initInputBoxValue(options: IShowInputBoxOption) {
|
||||
async function initInputBoxValue(options: IShowInputBoxOption) {
|
||||
inputBoxValue.value = options.value || ''
|
||||
inputBoxOptions.title = options.title || ''
|
||||
inputBoxOptions.placeholder = options.placeholder || ''
|
||||
inputBoxOptions.multiLine = options.multiLine || false
|
||||
showInputBoxVisible.value = true
|
||||
|
||||
await nextTick()
|
||||
if (inputBoxOptions.multiLine) {
|
||||
textareaRef.value?.focus()
|
||||
textareaRef.value?.select()
|
||||
} else {
|
||||
inputRef.value?.focus()
|
||||
inputRef.value?.select()
|
||||
}
|
||||
}
|
||||
|
||||
function handleInputBoxCancel() {
|
||||
@@ -106,6 +127,36 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* Transitions */
|
||||
.inputbox-fade-enter-active,
|
||||
.inputbox-fade-leave-active {
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.inputbox-fade-enter-from,
|
||||
.inputbox-fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.inputbox-scale-enter-active {
|
||||
transition: all 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
}
|
||||
|
||||
.inputbox-scale-leave-active {
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.inputbox-scale-enter-from {
|
||||
opacity: 0;
|
||||
transform: scale(0.9) translateY(-10px);
|
||||
}
|
||||
|
||||
.inputbox-scale-leave-to {
|
||||
opacity: 0;
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
/* Overlay */
|
||||
.inputbox-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
@@ -113,15 +164,19 @@ export default {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: rgb(0 0 0 / 50%);
|
||||
padding: 1rem;
|
||||
background: rgb(0 0 0 / 40%);
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
/* Container */
|
||||
.inputbox-container {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-radius: 0.75rem;
|
||||
width: 90%;
|
||||
max-width: 32rem;
|
||||
max-height: 80vh;
|
||||
border: 1px solid rgb(229 231 235);
|
||||
border-radius: 1rem;
|
||||
width: 100%;
|
||||
max-width: 28rem;
|
||||
background: white;
|
||||
box-shadow:
|
||||
0 20px 25px -5px rgb(0 0 0 / 10%),
|
||||
@@ -130,45 +185,30 @@ export default {
|
||||
|
||||
:root.dark .inputbox-container,
|
||||
:root.auto.dark .inputbox-container {
|
||||
border: 1px solid rgb(55 65 81);
|
||||
border-color: rgb(55 65 81);
|
||||
background: rgb(31 41 55);
|
||||
}
|
||||
|
||||
.inputbox-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1.5rem 1.5rem 0;
|
||||
}
|
||||
|
||||
.inputbox-title {
|
||||
margin: 0;
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
color: rgb(17 24 39);
|
||||
}
|
||||
|
||||
:root.dark .inputbox-title,
|
||||
:root.auto.dark .inputbox-title {
|
||||
color: rgb(243 244 246);
|
||||
}
|
||||
|
||||
/* Close Button */
|
||||
.inputbox-close {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: none;
|
||||
border-radius: 0.25rem;
|
||||
padding: 0.25rem;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 0.5rem;
|
||||
padding: 0.375rem;
|
||||
color: rgb(107 114 128);
|
||||
background: none;
|
||||
background: transparent;
|
||||
transition: all 0.15s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.inputbox-close:hover {
|
||||
color: rgb(17 24 39);
|
||||
color: rgb(75 85 99);
|
||||
background: rgb(243 244 246);
|
||||
}
|
||||
|
||||
@@ -179,29 +219,55 @@ export default {
|
||||
|
||||
:root.dark .inputbox-close:hover,
|
||||
:root.auto.dark .inputbox-close:hover {
|
||||
color: rgb(243 244 246);
|
||||
color: rgb(209 213 219);
|
||||
background: rgb(55 65 81);
|
||||
}
|
||||
|
||||
.inputbox-content {
|
||||
padding: 1rem 1.5rem;
|
||||
/* Body */
|
||||
.inputbox-body {
|
||||
padding: 2rem 2rem 1.5rem;
|
||||
}
|
||||
|
||||
.inputbox-title {
|
||||
margin: 0 0 1.25rem;
|
||||
padding-right: 2rem;
|
||||
font-size: 1.125rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.4;
|
||||
color: rgb(17 24 39);
|
||||
}
|
||||
|
||||
:root.dark .inputbox-title,
|
||||
:root.auto.dark .inputbox-title {
|
||||
color: rgb(243 244 246);
|
||||
}
|
||||
|
||||
.inputbox-content {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Input */
|
||||
.inputbox-input {
|
||||
border: 1px solid rgb(209 213 219);
|
||||
border-radius: 0.5rem;
|
||||
border: 1.5px solid rgb(229 231 235);
|
||||
border-radius: 0.625rem;
|
||||
padding: 0.75rem 1rem;
|
||||
width: 100%;
|
||||
font-size: 0.875rem;
|
||||
font-size: 0.9375rem;
|
||||
font-family: inherit;
|
||||
color: rgb(17 24 39);
|
||||
background: white;
|
||||
background: rgb(249 250 251);
|
||||
outline: none;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.inputbox-input:hover {
|
||||
border-color: rgb(209 213 219);
|
||||
background: white;
|
||||
}
|
||||
|
||||
.inputbox-input:focus {
|
||||
border-color: rgb(59 130 246);
|
||||
background: white;
|
||||
box-shadow: 0 0 0 3px rgb(59 130 246 / 10%);
|
||||
}
|
||||
|
||||
@@ -209,23 +275,31 @@ export default {
|
||||
color: rgb(156 163 175);
|
||||
}
|
||||
|
||||
/* Textarea */
|
||||
.inputbox-textarea {
|
||||
border: 1px solid rgb(209 213 219);
|
||||
border-radius: 0.375rem;
|
||||
padding: 0.5rem 0.75rem;
|
||||
border: 1.5px solid rgb(229 231 235);
|
||||
border-radius: 0.625rem;
|
||||
padding: 0.75rem 1rem;
|
||||
width: 100%;
|
||||
min-height: 4rem;
|
||||
font-size: 0.875rem;
|
||||
min-height: 6rem;
|
||||
font-size: 0.9375rem;
|
||||
font-family: inherit;
|
||||
line-height: 1.6;
|
||||
color: rgb(17 24 39);
|
||||
background: white;
|
||||
background: rgb(249 250 251);
|
||||
outline: none;
|
||||
resize: vertical;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.inputbox-textarea:hover {
|
||||
border-color: rgb(209 213 219);
|
||||
background: white;
|
||||
}
|
||||
|
||||
.inputbox-textarea:focus {
|
||||
border-color: rgb(59 130 246);
|
||||
background: white;
|
||||
box-shadow: 0 0 0 3px rgb(59 130 246 / 10%);
|
||||
}
|
||||
|
||||
@@ -233,16 +307,24 @@ export default {
|
||||
color: rgb(156 163 175);
|
||||
}
|
||||
|
||||
/* Dark Mode - Input */
|
||||
:root.dark .inputbox-input,
|
||||
:root.auto.dark .inputbox-input {
|
||||
border-color: rgb(75 85 99);
|
||||
border-color: rgb(55 65 81);
|
||||
color: rgb(243 244 246);
|
||||
background: rgb(55 65 81);
|
||||
}
|
||||
|
||||
:root.dark .inputbox-input:hover,
|
||||
:root.auto.dark .inputbox-input:hover {
|
||||
border-color: rgb(75 85 99);
|
||||
background: rgb(55 65 81);
|
||||
}
|
||||
|
||||
:root.dark .inputbox-input:focus,
|
||||
:root.auto.dark .inputbox-input:focus {
|
||||
border-color: rgb(59 130 246);
|
||||
background: rgb(55 65 81);
|
||||
box-shadow: 0 0 0 3px rgb(59 130 246 / 10%);
|
||||
}
|
||||
|
||||
@@ -251,16 +333,24 @@ export default {
|
||||
color: rgb(107 114 128);
|
||||
}
|
||||
|
||||
/* Dark Mode - Textarea */
|
||||
:root.dark .inputbox-textarea,
|
||||
:root.auto.dark .inputbox-textarea {
|
||||
border-color: rgb(75 85 99);
|
||||
border-color: rgb(55 65 81);
|
||||
color: rgb(243 244 246);
|
||||
background: rgb(55 65 81);
|
||||
}
|
||||
|
||||
:root.dark .inputbox-textarea:hover,
|
||||
:root.auto.dark .inputbox-textarea:hover {
|
||||
border-color: rgb(75 85 99);
|
||||
background: rgb(55 65 81);
|
||||
}
|
||||
|
||||
:root.dark .inputbox-textarea:focus,
|
||||
:root.auto.dark .inputbox-textarea:focus {
|
||||
border-color: rgb(59 130 246);
|
||||
background: rgb(55 65 81);
|
||||
box-shadow: 0 0 0 3px rgb(59 130 246 / 10%);
|
||||
}
|
||||
|
||||
@@ -269,51 +359,103 @@ export default {
|
||||
color: rgb(107 114 128);
|
||||
}
|
||||
|
||||
/* Actions */
|
||||
.inputbox-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 0 1.5rem 1.5rem;
|
||||
border-top: 1px solid rgb(243 244 246);
|
||||
padding: 1rem 1.5rem;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
:root.dark .inputbox-actions,
|
||||
:root.auto.dark .inputbox-actions {
|
||||
border-top-color: rgb(55 65 81);
|
||||
}
|
||||
|
||||
.inputbox-btn {
|
||||
flex: 1;
|
||||
border: none;
|
||||
border-radius: 0.375rem;
|
||||
padding: 0.5rem 1rem;
|
||||
min-width: 4rem;
|
||||
border-radius: 0.5rem;
|
||||
padding: 0.625rem 1.25rem;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
transition: all 0.15s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.inputbox-btn:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.inputbox-btn:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.inputbox-btn:disabled:active {
|
||||
transform: none;
|
||||
}
|
||||
|
||||
/* Cancel Button */
|
||||
.cancel-btn {
|
||||
border: 1px solid rgb(209 213 219);
|
||||
border: 1px solid rgb(229 231 235);
|
||||
color: rgb(75 85 99);
|
||||
background: rgb(243 244 246);
|
||||
background: white;
|
||||
}
|
||||
|
||||
.cancel-btn:hover {
|
||||
background: rgb(229 231 235);
|
||||
border-color: rgb(209 213 219);
|
||||
background: rgb(249 250 251);
|
||||
}
|
||||
|
||||
:root.dark .cancel-btn,
|
||||
:root.auto.dark .cancel-btn {
|
||||
border-color: rgb(75 85 99);
|
||||
border-color: rgb(55 65 81);
|
||||
color: rgb(209 213 219);
|
||||
background: rgb(55 65 81);
|
||||
}
|
||||
|
||||
:root.dark .cancel-btn:hover,
|
||||
:root.auto.dark .cancel-btn:hover {
|
||||
border-color: rgb(75 85 99);
|
||||
background: rgb(75 85 99);
|
||||
}
|
||||
|
||||
.confirm-btn.primary {
|
||||
/* Confirm Button */
|
||||
.confirm-btn {
|
||||
border: none;
|
||||
color: white;
|
||||
background: rgb(59 130 246);
|
||||
background: linear-gradient(135deg, rgb(59 130 246) 0%, rgb(37 99 235) 100%);
|
||||
box-shadow: 0 1px 2px 0 rgb(0 0 0 / 5%);
|
||||
}
|
||||
|
||||
.confirm-btn.primary:hover {
|
||||
background: rgb(37 99 235);
|
||||
.confirm-btn:hover:not(:disabled) {
|
||||
background: linear-gradient(135deg, rgb(37 99 235) 0%, rgb(29 78 216) 100%);
|
||||
box-shadow: 0 4px 12px rgb(59 130 246 / 40%);
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (width <= 640px) {
|
||||
.inputbox-overlay {
|
||||
align-items: flex-end;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.inputbox-container {
|
||||
border-radius: 1rem 1rem 0 0;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.inputbox-body {
|
||||
padding: 1.75rem 1.5rem 1.25rem;
|
||||
}
|
||||
|
||||
.inputbox-actions {
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
.inputbox-btn {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,42 +1,41 @@
|
||||
<template>
|
||||
<div v-if="isOpen" class="messagebox-overlay" @click="onCancel">
|
||||
<div class="messagebox-container" @click.stop>
|
||||
<div class="messagebox-header">
|
||||
<h3 class="messagebox-title">
|
||||
{{ title }}
|
||||
</h3>
|
||||
<button v-if="showClose" class="messagebox-close" @click="onCancel">×</button>
|
||||
</div>
|
||||
<div class="messagebox-content">
|
||||
<div v-if="type" class="messagebox-icon">
|
||||
<component :is="iconComponent" :size="48" />
|
||||
<Transition name="messagebox-fade">
|
||||
<div v-if="isOpen" class="messagebox-overlay" @click="onCancel">
|
||||
<Transition name="messagebox-scale">
|
||||
<div v-if="isOpen" class="messagebox-container" @click.stop>
|
||||
<button v-if="showClose" class="messagebox-close" @click="onCancel">
|
||||
<XIcon :size="20" />
|
||||
</button>
|
||||
|
||||
<div class="messagebox-body">
|
||||
<div class="messagebox-main">
|
||||
<div v-if="type" class="messagebox-icon-wrapper" :class="`messagebox-icon-${type}`">
|
||||
<component :is="iconComponent" :size="24" :stroke-width="2.5" />
|
||||
</div>
|
||||
|
||||
<div class="messagebox-content">
|
||||
<h3 class="messagebox-title">{{ title }}</h3>
|
||||
<p class="messagebox-message">{{ message }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="messagebox-actions" :class="{ center }">
|
||||
<button class="messagebox-btn cancel-btn" @click="onCancel">
|
||||
{{ cancelButtonText }}
|
||||
</button>
|
||||
<button class="messagebox-btn confirm-btn" :class="confirmButtonClass" @click="onConfirm">
|
||||
{{ confirmButtonText }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="messagebox-message">
|
||||
<p>{{ message }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="center" class="messagebox-actions center">
|
||||
<button class="messagebox-btn cancel-btn" @click="onCancel">
|
||||
{{ cancelButtonText }}
|
||||
</button>
|
||||
<button class="messagebox-btn confirm-btn" :class="confirmButtonClass" @click="onConfirm">
|
||||
{{ confirmButtonText }}
|
||||
</button>
|
||||
</div>
|
||||
<div v-else class="messagebox-actions">
|
||||
<button class="messagebox-btn confirm-btn" :class="confirmButtonClass" @click="onConfirm">
|
||||
{{ confirmButtonText }}
|
||||
</button>
|
||||
<button class="messagebox-btn cancel-btn" @click="onCancel">
|
||||
{{ cancelButtonText }}
|
||||
</button>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { AlertTriangle, CheckCircle, Info, XCircle } from 'lucide-vue-next'
|
||||
import { AlertTriangle, CheckCircle, Info, X as XIcon, XCircle } from 'lucide-vue-next'
|
||||
import { computed } from 'vue'
|
||||
|
||||
interface Props {
|
||||
@@ -109,6 +108,36 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* Transitions */
|
||||
.messagebox-fade-enter-active,
|
||||
.messagebox-fade-leave-active {
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.messagebox-fade-enter-from,
|
||||
.messagebox-fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.messagebox-scale-enter-active {
|
||||
transition: all 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
}
|
||||
|
||||
.messagebox-scale-leave-active {
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.messagebox-scale-enter-from {
|
||||
opacity: 0;
|
||||
transform: scale(0.9) translateY(-10px);
|
||||
}
|
||||
|
||||
.messagebox-scale-leave-to {
|
||||
opacity: 0;
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
/* Overlay */
|
||||
.messagebox-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
@@ -116,15 +145,19 @@ export default {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: rgb(0 0 0 / 50%);
|
||||
padding: 1rem;
|
||||
background: rgb(0 0 0 / 40%);
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
|
||||
/* Container */
|
||||
.messagebox-container {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-radius: 0.75rem;
|
||||
width: 90%;
|
||||
max-width: 32rem;
|
||||
max-height: 80vh;
|
||||
border: 1px solid rgb(229 231 235);
|
||||
border-radius: 1rem;
|
||||
width: 100%;
|
||||
max-width: 26rem;
|
||||
background: white;
|
||||
box-shadow:
|
||||
0 20px 25px -5px rgb(0 0 0 / 10%),
|
||||
@@ -133,46 +166,30 @@ export default {
|
||||
|
||||
:root.dark .messagebox-container,
|
||||
:root.auto.dark .messagebox-container {
|
||||
border: 1px solid rgb(55 65 81);
|
||||
border-color: rgb(55 65 81);
|
||||
background: rgb(31 41 55);
|
||||
}
|
||||
|
||||
.messagebox-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 1.5rem 1.5rem 0;
|
||||
}
|
||||
|
||||
.messagebox-title {
|
||||
margin: 0;
|
||||
font-size: 1.25rem;
|
||||
font-weight: 600;
|
||||
color: rgb(17 24 39);
|
||||
}
|
||||
|
||||
:root.dark .messagebox-title,
|
||||
:root.auto.dark .messagebox-title {
|
||||
color: rgb(243 244 246);
|
||||
}
|
||||
|
||||
/* Close Button */
|
||||
.messagebox-close {
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
right: 1rem;
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: none;
|
||||
border-radius: 0.25rem;
|
||||
padding: 0;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
font-size: 1.5rem;
|
||||
border-radius: 0.5rem;
|
||||
padding: 0.375rem;
|
||||
color: rgb(107 114 128);
|
||||
background: none;
|
||||
background: transparent;
|
||||
transition: all 0.15s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.messagebox-close:hover {
|
||||
color: rgb(17 24 39);
|
||||
color: rgb(75 85 99);
|
||||
background: rgb(243 244 246);
|
||||
}
|
||||
|
||||
@@ -183,120 +200,246 @@ export default {
|
||||
|
||||
:root.dark .messagebox-close:hover,
|
||||
:root.auto.dark .messagebox-close:hover {
|
||||
color: rgb(243 244 246);
|
||||
color: rgb(209 213 219);
|
||||
background: rgb(55 65 81);
|
||||
}
|
||||
|
||||
.messagebox-content {
|
||||
/* Body */
|
||||
.messagebox-body {
|
||||
padding: 1.75rem 2rem 1.5rem;
|
||||
}
|
||||
|
||||
.messagebox-main {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
padding: 1rem 1.5rem;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.messagebox-icon {
|
||||
/* Icon Wrapper */
|
||||
.messagebox-icon-wrapper {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
color: rgb(107 114 128);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: 0.625rem;
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
animation: icon-pop 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
}
|
||||
|
||||
.messagebox-icon svg[data-lucide='alert-triangle'] {
|
||||
@keyframes icon-pop {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: scale(0);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.messagebox-icon-warning {
|
||||
color: rgb(245 158 11);
|
||||
background: rgb(254 243 199);
|
||||
}
|
||||
|
||||
.messagebox-icon svg[data-lucide='info'] {
|
||||
:root.dark .messagebox-icon-warning,
|
||||
:root.auto.dark .messagebox-icon-warning {
|
||||
color: rgb(251 191 36);
|
||||
background: rgb(120 53 15 / 30%);
|
||||
}
|
||||
|
||||
.messagebox-icon-info {
|
||||
color: rgb(59 130 246);
|
||||
background: rgb(219 234 254);
|
||||
}
|
||||
|
||||
.messagebox-icon svg[data-lucide='check-circle'] {
|
||||
:root.dark .messagebox-icon-info,
|
||||
:root.auto.dark .messagebox-icon-info {
|
||||
color: rgb(96 165 250);
|
||||
background: rgb(30 58 138 / 30%);
|
||||
}
|
||||
|
||||
.messagebox-icon-success {
|
||||
color: rgb(34 197 94);
|
||||
background: rgb(220 252 231);
|
||||
}
|
||||
|
||||
.messagebox-icon svg[data-lucide='x-circle'] {
|
||||
:root.dark .messagebox-icon-success,
|
||||
:root.auto.dark .messagebox-icon-success {
|
||||
color: rgb(74 222 128);
|
||||
background: rgb(20 83 45 / 30%);
|
||||
}
|
||||
|
||||
.messagebox-icon-error {
|
||||
color: rgb(239 68 68);
|
||||
background: rgb(254 226 226);
|
||||
}
|
||||
|
||||
:root.dark .messagebox-icon-error,
|
||||
:root.auto.dark .messagebox-icon-error {
|
||||
color: rgb(248 113 113);
|
||||
background: rgb(127 29 29 / 30%);
|
||||
}
|
||||
|
||||
/* Content */
|
||||
.messagebox-content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.messagebox-title {
|
||||
margin: 0 0 0.375rem;
|
||||
font-size: 1.0625rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.4;
|
||||
color: rgb(17 24 39);
|
||||
}
|
||||
|
||||
:root.dark .messagebox-title,
|
||||
:root.auto.dark .messagebox-title {
|
||||
color: rgb(243 244 246);
|
||||
}
|
||||
|
||||
.messagebox-message {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.messagebox-message p {
|
||||
margin: 0;
|
||||
font-size: 0.9375rem;
|
||||
line-height: 1.5;
|
||||
color: rgb(107 114 128);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
:root.dark .messagebox-message p,
|
||||
:root.auto.dark .messagebox-message p {
|
||||
:root.dark .messagebox-message,
|
||||
:root.auto.dark .messagebox-message {
|
||||
color: rgb(156 163 175);
|
||||
}
|
||||
|
||||
/* Actions */
|
||||
.messagebox-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 0 1.5rem 1.5rem;
|
||||
border-top: 1px solid rgb(243 244 246);
|
||||
padding: 1rem 1.5rem;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
:root.dark .messagebox-actions,
|
||||
:root.auto.dark .messagebox-actions {
|
||||
border-top-color: rgb(55 65 81);
|
||||
}
|
||||
|
||||
.messagebox-actions.center {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.messagebox-btn {
|
||||
flex: 1;
|
||||
border: none;
|
||||
border-radius: 0.375rem;
|
||||
padding: 0.5rem 1rem;
|
||||
min-width: 4rem;
|
||||
border-radius: 0.5rem;
|
||||
padding: 0.625rem 1.25rem;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
transition: all 0.15s ease;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.messagebox-btn:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
/* Cancel Button */
|
||||
.cancel-btn {
|
||||
border: 1px solid rgb(209 213 219);
|
||||
border: 1px solid rgb(229 231 235);
|
||||
color: rgb(75 85 99);
|
||||
background: rgb(243 244 246);
|
||||
background: white;
|
||||
}
|
||||
|
||||
.cancel-btn:hover {
|
||||
background: rgb(229 231 235);
|
||||
border-color: rgb(209 213 219);
|
||||
background: rgb(249 250 251);
|
||||
}
|
||||
|
||||
:root.dark .cancel-btn,
|
||||
:root.auto.dark .cancel-btn {
|
||||
border-color: rgb(75 85 99);
|
||||
border-color: rgb(55 65 81);
|
||||
color: rgb(209 213 219);
|
||||
background: rgb(55 65 81);
|
||||
}
|
||||
|
||||
:root.dark .cancel-btn:hover,
|
||||
:root.auto.dark .cancel-btn:hover {
|
||||
border-color: rgb(75 85 99);
|
||||
background: rgb(75 85 99);
|
||||
}
|
||||
|
||||
.confirm-btn.primary {
|
||||
/* Confirm Buttons */
|
||||
.confirm-btn {
|
||||
border: none;
|
||||
color: white;
|
||||
background: rgb(59 130 246);
|
||||
box-shadow: 0 1px 2px 0 rgb(0 0 0 / 5%);
|
||||
}
|
||||
|
||||
.confirm-btn.primary {
|
||||
background: linear-gradient(135deg, rgb(59 130 246) 0%, rgb(37 99 235) 100%);
|
||||
}
|
||||
|
||||
.confirm-btn.primary:hover {
|
||||
background: rgb(37 99 235);
|
||||
background: linear-gradient(135deg, rgb(37 99 235) 0%, rgb(29 78 216) 100%);
|
||||
box-shadow: 0 4px 12px rgb(59 130 246 / 40%);
|
||||
}
|
||||
|
||||
.confirm-btn.danger {
|
||||
color: white;
|
||||
background: rgb(239 68 68);
|
||||
background: linear-gradient(135deg, rgb(239 68 68) 0%, rgb(220 38 38) 100%);
|
||||
}
|
||||
|
||||
.confirm-btn.danger:hover {
|
||||
background: rgb(220 38 38);
|
||||
background: linear-gradient(135deg, rgb(220 38 38) 0%, rgb(185 28 28) 100%);
|
||||
box-shadow: 0 4px 12px rgb(239 68 68 / 40%);
|
||||
}
|
||||
|
||||
.confirm-btn.success {
|
||||
color: white;
|
||||
background: rgb(34 197 94);
|
||||
background: linear-gradient(135deg, rgb(34 197 94) 0%, rgb(22 163 74) 100%);
|
||||
}
|
||||
|
||||
.confirm-btn.success:hover {
|
||||
background: rgb(22 163 74);
|
||||
background: linear-gradient(135deg, rgb(22 163 74) 0%, rgb(21 128 61) 100%);
|
||||
box-shadow: 0 4px 12px rgb(34 197 94 / 40%);
|
||||
}
|
||||
|
||||
/* Responsive */
|
||||
@media (width <= 640px) {
|
||||
.messagebox-overlay {
|
||||
align-items: flex-end;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.messagebox-container {
|
||||
border-radius: 1rem 1rem 0 0;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.messagebox-body {
|
||||
padding: 1.5rem 1.5rem 1.25rem;
|
||||
}
|
||||
|
||||
.messagebox-main {
|
||||
gap: 0.875rem;
|
||||
}
|
||||
|
||||
.messagebox-icon-wrapper {
|
||||
width: 2.75rem;
|
||||
height: 2.75rem;
|
||||
}
|
||||
|
||||
.messagebox-actions {
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
.messagebox-btn {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -194,7 +194,6 @@ export default {
|
||||
color: rgb(75 85 99);
|
||||
flex: 1;
|
||||
line-height: 1.25rem;
|
||||
word-break: break-word;
|
||||
overflow-wrap: break-word;
|
||||
hyphens: auto;
|
||||
}
|
||||
|
||||
@@ -918,10 +918,16 @@
|
||||
},
|
||||
"uploaderConfig": {
|
||||
"addNew": "Add New",
|
||||
"copy": "Copy",
|
||||
"delete": "Delete",
|
||||
"deleteConfirm": "Are you sure you want to delete this PicBed config?",
|
||||
"deleteSuccess": "Delete Success",
|
||||
"deleteTitle": "Notification",
|
||||
"duplicate": "Duplicate",
|
||||
"duplicateError": "Duplicate Failed",
|
||||
"duplicatePlaceholder": "Enter new configuration name",
|
||||
"duplicateSuccess": "Duplicate Success",
|
||||
"duplicateTitle": "Duplicate Configuration",
|
||||
"edit": "Edit",
|
||||
"selected": "Selected",
|
||||
"setAsDefault": "Set as Default PicBed",
|
||||
|
||||
@@ -913,10 +913,16 @@
|
||||
},
|
||||
"uploaderConfig": {
|
||||
"addNew": "新增",
|
||||
"copy": "副本",
|
||||
"delete": "删除",
|
||||
"deleteConfirm": "确认删除图床配置吗?",
|
||||
"deleteSuccess": "删除成功",
|
||||
"deleteTitle": "通知",
|
||||
"duplicate": "拷贝",
|
||||
"duplicateError": "拷贝失败",
|
||||
"duplicatePlaceholder": "请输入新配置名称",
|
||||
"duplicateSuccess": "拷贝成功",
|
||||
"duplicateTitle": "拷贝配置",
|
||||
"edit": "编辑",
|
||||
"selected": "已选中",
|
||||
"setAsDefault": "设为默认图床",
|
||||
|
||||
@@ -913,10 +913,16 @@
|
||||
},
|
||||
"uploaderConfig": {
|
||||
"addNew": "新增",
|
||||
"copy": "副本",
|
||||
"delete": "刪除",
|
||||
"deleteConfirm": "確認刪除圖床配置嗎?",
|
||||
"deleteSuccess": "刪除成功",
|
||||
"deleteTitle": "通知",
|
||||
"duplicate": "拷贝",
|
||||
"duplicateError": "拷贝失败",
|
||||
"duplicatePlaceholder": "請輸入新配置名稱",
|
||||
"duplicateSuccess": "拷贝成功",
|
||||
"duplicateTitle": "拷贝配置",
|
||||
"edit": "編輯",
|
||||
"selected": "已選取",
|
||||
"setAsDefault": "設為預設圖床",
|
||||
|
||||
@@ -107,10 +107,7 @@ export default { name: 'MainPage' }
|
||||
--color-primary-hover: #818cf8;
|
||||
--color-accent: #0a84ff;
|
||||
--color-accent-hover: #409cff;
|
||||
}
|
||||
|
||||
:root.dark,
|
||||
:root.auto.dark {
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
@@ -203,7 +200,6 @@ body {
|
||||
|
||||
.content-container {
|
||||
margin: 0;
|
||||
padding: 0.3 rem;
|
||||
max-width: none;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@@ -667,7 +667,7 @@
|
||||
}
|
||||
|
||||
.file-actions-dropdown-content {
|
||||
position: absolute;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
right: 0;
|
||||
z-index: 3000;
|
||||
@@ -676,8 +676,10 @@
|
||||
margin-top: 0.25rem;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
min-width: 140px;
|
||||
min-width: 120px;
|
||||
max-width: 200px;
|
||||
|
||||
/* Ensure dropdown is never clipped */
|
||||
max-height: 300px;
|
||||
white-space: nowrap;
|
||||
background: var(--color-surface);
|
||||
@@ -728,12 +730,16 @@
|
||||
}
|
||||
|
||||
.file-actions-dropdown-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid var(--color-border-secondary);
|
||||
padding: 0.5rem 0.75rem;
|
||||
font-size: 0.875rem;
|
||||
color: var(--color-text-primary);
|
||||
background: var(--color-background-primary);
|
||||
transition: var(--transition-fast);
|
||||
cursor: pointer;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.file-actions-dropdown-item:last-child {
|
||||
@@ -741,7 +747,7 @@
|
||||
}
|
||||
|
||||
.file-actions-dropdown-item:hover {
|
||||
color: var(--color-accent);
|
||||
color: var(--color-blue-common);
|
||||
background: var(--color-surface-elevated);
|
||||
}
|
||||
|
||||
@@ -1138,55 +1144,6 @@ input:checked + .switch-slider::before {
|
||||
}
|
||||
|
||||
/* File Actions Dropdown */
|
||||
.file-actions-dropdown {
|
||||
position: relative;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.file-actions-dropdown-content {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
right: 0;
|
||||
z-index: 3000;
|
||||
overflow: visible;
|
||||
overflow-y: auto;
|
||||
margin-top: 0.25rem;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
min-width: 120px;
|
||||
max-width: 200px;
|
||||
|
||||
/* Ensure dropdown is never clipped */
|
||||
max-height: 300px;
|
||||
white-space: nowrap;
|
||||
background: var(--color-surface);
|
||||
box-shadow: var(--shadow-lg);
|
||||
transform-origin: top right;
|
||||
animation: dropdown-appear 0.15s ease-out;
|
||||
}
|
||||
|
||||
.file-actions-dropdown-content.position-left {
|
||||
right: auto;
|
||||
left: 0;
|
||||
transform-origin: top left;
|
||||
}
|
||||
|
||||
.file-actions-dropdown-content.position-up {
|
||||
top: auto;
|
||||
bottom: 100%;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.25rem;
|
||||
transform-origin: bottom right;
|
||||
}
|
||||
|
||||
.file-actions-dropdown-content.position-up.position-left {
|
||||
transform-origin: bottom left;
|
||||
}
|
||||
|
||||
.content-fullscreen .file-actions-dropdown-content {
|
||||
z-index: 4000;
|
||||
}
|
||||
|
||||
@media (width <= 768px) {
|
||||
.file-actions-dropdown-content {
|
||||
min-width: 100px;
|
||||
@@ -1194,22 +1151,6 @@ input:checked + .switch-slider::before {
|
||||
}
|
||||
}
|
||||
|
||||
.file-actions-dropdown-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.5rem 0.75rem;
|
||||
font-size: 0.875rem;
|
||||
color: var(--color-text-primary);
|
||||
background: var(--color-background-primary);
|
||||
transition: var(--transition-fast);
|
||||
cursor: pointer;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.file-actions-dropdown-item:hover {
|
||||
color: var(--color-blue-common);
|
||||
}
|
||||
|
||||
/* Empty State */
|
||||
.empty-state {
|
||||
display: flex;
|
||||
|
||||
@@ -308,6 +308,9 @@
|
||||
margin-top: 0.75rem;
|
||||
border: 1px solid var(--color-border-secondary);
|
||||
border-radius: var(--radius-md);
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.table-row {
|
||||
@@ -590,11 +593,6 @@ input:checked + .switch-slider::before {
|
||||
border-radius: var(--radius-md);
|
||||
}
|
||||
|
||||
.config-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.config-table th {
|
||||
border-bottom: 1px solid var(--color-border-secondary);
|
||||
|
||||
@@ -212,7 +212,7 @@
|
||||
min-width: 0;
|
||||
font-weight: 500;
|
||||
line-height: 1.2;
|
||||
word-break: break-word;
|
||||
overflow-wrap: break-word;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,13 @@
|
||||
>
|
||||
<Edit :size="16" />
|
||||
</button>
|
||||
<button
|
||||
class="action-btn duplicate-btn"
|
||||
:title="t('pages.uploaderConfig.duplicate')"
|
||||
@click.stop="() => duplicateConfig(item._id)"
|
||||
>
|
||||
<Copy :size="16" />
|
||||
</button>
|
||||
<button
|
||||
class="action-btn delete-btn"
|
||||
:class="curConfigList.length <= 1 ? 'disabled' : ''"
|
||||
@@ -73,7 +80,7 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import dayjs from 'dayjs'
|
||||
import { DatabaseIcon, Edit, Plus, Trash2 } from 'lucide-vue-next'
|
||||
import { Copy, DatabaseIcon, Edit, Plus, Trash2 } from 'lucide-vue-next'
|
||||
import { onBeforeMount, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router'
|
||||
@@ -82,7 +89,9 @@ import useConfirm from '@/hooks/useConfirm'
|
||||
import useMessage from '@/hooks/useMessage'
|
||||
import { useStore } from '@/hooks/useStore'
|
||||
import { PICBEDS_PAGE, UPLOADER_CONFIG_PAGE } from '@/router/config'
|
||||
import $bus from '@/utils/bus'
|
||||
import { configPaths } from '@/utils/configPaths'
|
||||
import { SHOW_INPUT_BOX, SHOW_INPUT_BOX_RESPONSE } from '@/utils/constant'
|
||||
import { saveConfig } from '@/utils/dataSender'
|
||||
import { IRPCActionType } from '@/utils/enum'
|
||||
import type { IStringKeyMap, IUploaderConfigItem } from '#/types/types'
|
||||
@@ -148,6 +157,49 @@ function formatTime(time: number): string {
|
||||
return dayjs(time).format('YYYY-MM-DD HH:mm')
|
||||
}
|
||||
|
||||
async function duplicateConfig(id: string) {
|
||||
const originalConfig = curConfigList.value.find(item => item._id === id)
|
||||
if (!originalConfig) return
|
||||
|
||||
return new Promise<void>(resolve => {
|
||||
$bus.emit(SHOW_INPUT_BOX, {
|
||||
title: t('pages.uploaderConfig.duplicateTitle'),
|
||||
placeholder: t('pages.uploaderConfig.duplicatePlaceholder'),
|
||||
value: `${originalConfig._configName} - ${t('pages.uploaderConfig.copy')}`,
|
||||
})
|
||||
|
||||
const handleResponse = async (newName: string) => {
|
||||
$bus.off(SHOW_INPUT_BOX_RESPONSE, handleResponse)
|
||||
|
||||
if (!newName) {
|
||||
resolve()
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await window.electron.triggerRPC<IUploaderConfigItem>(
|
||||
IRPCActionType.PICBED_DUPLICATE_CONFIG,
|
||||
type.value,
|
||||
id,
|
||||
newName,
|
||||
)
|
||||
if (!res) {
|
||||
resolve()
|
||||
return
|
||||
}
|
||||
curConfigList.value = res.configList
|
||||
defaultConfigId.value = res.defaultId
|
||||
message.success(t('pages.uploaderConfig.duplicateSuccess'))
|
||||
} catch (error) {
|
||||
message.error(t('pages.uploaderConfig.duplicateError'))
|
||||
}
|
||||
resolve()
|
||||
}
|
||||
|
||||
$bus.on(SHOW_INPUT_BOX_RESPONSE, handleResponse)
|
||||
})
|
||||
}
|
||||
|
||||
async function deleteConfig(id: string) {
|
||||
const result = await confirm({
|
||||
title: t('pages.uploaderConfig.deleteTitle'),
|
||||
|
||||
@@ -160,6 +160,11 @@
|
||||
color: var(--color-accent);
|
||||
}
|
||||
|
||||
.duplicate-btn:hover {
|
||||
border-color: var(--color-info);
|
||||
color: var(--color-info);
|
||||
}
|
||||
|
||||
.delete-btn:hover:not(.disabled) {
|
||||
border-color: var(--color-danger);
|
||||
color: var(--color-danger);
|
||||
|
||||
@@ -39,6 +39,7 @@ export const IRPCActionType = {
|
||||
PICBED_GET_PICBED_CONFIG: 'PICBED_GET_PICBED_CONFIG',
|
||||
PICBED_GET_CONFIG_LIST: 'PICBED_GET_CONFIG_LIST',
|
||||
PICBED_DELETE_CONFIG: 'PICBED_DELETE_CONFIG',
|
||||
PICBED_DUPLICATE_CONFIG: 'PICBED_DUPLICATE_CONFIG',
|
||||
UPLOADER_CHANGE_CURRENT: 'UPLOADER_CHANGE_CURRENT',
|
||||
UPLOADER_SELECT: 'UPLOADER_SELECT',
|
||||
UPLOADER_UPDATE_CONFIG: 'UPLOADER_UPDATE_CONFIG',
|
||||
|
||||
Reference in New Issue
Block a user