🔙 Revert(custom): revert UI chage, delayed to next major version

This commit is contained in:
Kuingsmile
2025-06-07 21:08:49 +08:00
parent e17b578685
commit 8b925019ba
7 changed files with 791 additions and 2053 deletions

View File

@@ -2,281 +2,138 @@
<el-form
label-position="top"
require-asterisk-position="right"
label-width="10vw"
size="default"
:model="waterMarkForm"
class="image-process-form"
>
<div class="settings-grid">
<!-- Left Column -->
<div class="settings-column">
<!-- Watermark Settings Section -->
<section class="settings-section">
<div class="section-header">
<el-icon><Picture /></el-icon>
<h3>{{ $T('UPLOAD_PAGE_IMAGE_PROCESS_ISADDWM') }}</h3>
</div>
<div class="section-content">
<el-form-item class="toggle-control">
<el-switch v-model="waterMarkForm.isAddWatermark" :style="switchStyle" />
<span class="toggle-label">{{ waterMarkForm.isAddWatermark ? $T('ENABLE') : $T('DISABLE') }}</span>
</el-form-item>
<div v-show="waterMarkForm.isAddWatermark" class="subsection">
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_WMTYPE')">
<el-radio-group v-model="waterMarkForm.watermarkType" class="radio-group">
<el-radio value="text">{{ $T('UPLOAD_PAGE_IMAGE_PROCESS_WMTYPE_TEXT') }}</el-radio>
<el-radio value="image">{{ $T('UPLOAD_PAGE_IMAGE_PROCESS_WMTYPE_IMAGE') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ISFULLSCREEN_WM')" class="toggle-control">
<el-switch v-model="waterMarkForm.isFullScreenWatermark" :style="switchStyle" />
</el-form-item>
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_WMPOSITION')">
<el-radio-group v-model="waterMarkForm.watermarkPosition" class="position-grid">
<el-radio v-for="item in waterMarkPositionMap" :key="item[0]" :value="item[0]">
{{ item[1] }}
</el-radio>
</el-radio-group>
</el-form-item>
<div class="form-row">
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_WMDEGREE')">
<el-input-number v-model="waterMarkForm.watermarkDegree" :step="1" controls-position="right" />
</el-form-item>
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_WMRATIO')">
<el-input-number
v-model="waterMarkForm.watermarkScaleRatio"
:min="0"
:max="1"
:step="0.01"
controls-position="right"
/>
</el-form-item>
</div>
<!-- Text Watermark Options -->
<div v-show="waterMarkForm.watermarkType === 'text'" class="subsection variant-subsection">
<h4 class="subsection-title">{{ $T('UPLOAD_PAGE_IMAGE_PROCESS_WMTYPE_TEXT') }}</h4>
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_WMTEXT')">
<el-input v-model="waterMarkForm.watermarkText" />
</el-form-item>
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_WMTEXT_FONT_PATH')">
<el-input v-model="waterMarkForm.watermarkFontPath" placeholder="/path/to/font.ttf" />
</el-form-item>
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_WMCOLOR')">
<div class="color-picker-container">
<el-color-picker v-model="waterMarkForm.watermarkColor" show-alpha />
<span class="color-value">{{ waterMarkForm.watermarkColor }}</span>
</div>
</el-form-item>
</div>
<!-- Image Watermark Options -->
<div v-show="waterMarkForm.watermarkType === 'image'" class="subsection variant-subsection">
<h4 class="subsection-title">{{ $T('UPLOAD_PAGE_IMAGE_PROCESS_WMTYPE_IMAGE') }}</h4>
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_WMPATH')">
<el-input v-model="waterMarkForm.watermarkImagePath" placeholder="/path/to/watermark.png" />
</el-form-item>
</div>
</div>
</div>
</section>
<!-- Format Conversion Settings Section -->
<section class="settings-section">
<div class="section-header">
<el-icon><Document /></el-icon>
<h3>{{ $T('UPLOAD_PAGE_IMAGE_PROCESS_ISCONVERT') }}</h3>
</div>
<div class="section-content">
<el-form-item class="toggle-control">
<el-switch v-model="compressForm.isConvert" :style="switchStyle" />
<span class="toggle-label">{{ compressForm.isConvert ? $T('ENABLE') : $T('DISABLE') }}</span>
</el-form-item>
<div v-show="compressForm.isConvert" class="subsection">
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_CONVERTFORMAT')">
<el-select v-model="compressForm.convertFormat" :persistent="false" teleported class="full-width">
<el-option v-for="item in availableFormat" :key="item" :label="item" :value="item" />
</el-select>
</el-form-item>
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_CONVERTFORMAT_SPECIFIC')">
<el-input
v-model="formatConvertObj"
placeholder='{"jpg": "png", "png": "jpg"}'
type="textarea"
:autosize="{ minRows: 2, maxRows: 4 }"
class="code-input"
/>
</el-form-item>
</div>
</div>
</section>
</div>
<!-- Right Column -->
<div class="settings-column">
<!-- Image Processing Settings Section -->
<section class="settings-section">
<div class="section-header">
<el-icon><Edit /></el-icon>
<h3>{{ $T('UPLOAD_PAGE_IMAGE_PROCESS_QUALITY') }}</h3>
</div>
<div class="section-content">
<el-form-item class="toggle-control">
<el-switch v-model="compressForm.isRemoveExif" :style="switchStyle" />
<span class="toggle-label">{{ $T('UPLOAD_PAGE_IMAGE_PROCESS_ISREMOVEEXIF') }}</span>
</el-form-item>
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_QUALITY')">
<el-slider v-model="compressForm.quality" :min="1" :max="100" :step="1" show-input class="full-width" />
</el-form-item>
</div>
</section>
<!-- Resize Settings Section -->
<section class="settings-section">
<div class="section-header">
<el-icon><ZoomIn /></el-icon>
<h3>{{ $T('UPLOAD_PAGE_IMAGE_PROCESS_ISRESIZE') }}</h3>
</div>
<div class="section-content">
<el-form-item class="toggle-control">
<el-switch v-model="compressForm.isReSize" :style="switchStyle" />
<span class="toggle-label">{{ compressForm.isReSize ? $T('ENABLE') : $T('DISABLE') }}</span>
</el-form-item>
<div v-show="compressForm.isReSize" class="subsection">
<div class="form-row">
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_RESIZEWIDTH')">
<el-input-number v-model="compressForm.reSizeWidth" :min="0" controls-position="right" />
</el-form-item>
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_RESIZEHEIGHT')">
<el-input-number v-model="compressForm.reSizeHeight" :min="0" controls-position="right" />
</el-form-item>
</div>
<el-form-item
v-show="compressForm.reSizeHeight! > 0 && compressForm.reSizeWidth === 0"
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_SKIPRESIZEOfSMALLIMG_HEIGHT')"
class="toggle-control"
>
<el-switch v-model="compressForm.skipReSizeOfSmallImg" :style="switchStyle" />
</el-form-item>
<el-form-item
v-show="compressForm.reSizeWidth! > 0 && compressForm.reSizeHeight === 0"
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_SKIPRESIZEOfSMALLIMG_WIDTH')"
class="toggle-control"
>
<el-switch v-model="compressForm.skipReSizeOfSmallImg" :style="switchStyle" />
</el-form-item>
</div>
</div>
</section>
<!-- Resize by Percent Section -->
<section class="settings-section">
<div class="section-header">
<el-icon><ScaleToOriginal /></el-icon>
<h3>{{ $T('UPLOAD_PAGE_IMAGE_PROCESS_ISRESIZEBYPERCENT') }}</h3>
</div>
<div class="section-content">
<el-form-item class="toggle-control">
<el-switch v-model="compressForm.isReSizeByPercent" :style="switchStyle" />
<span class="toggle-label">{{ compressForm.isReSizeByPercent ? $T('ENABLE') : $T('DISABLE') }}</span>
</el-form-item>
<div v-show="compressForm.isReSizeByPercent" class="subsection">
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_RESIZEPERCENT')">
<el-slider
v-model="compressForm.reSizePercent"
:min="1"
:max="100"
:step="1"
show-input
class="full-width"
/>
</el-form-item>
</div>
</div>
</section>
<!-- Flip and Rotation Section -->
<section class="settings-section">
<div class="section-header">
<el-icon><Refresh /></el-icon>
<h3>{{ $T('UPLOAD_PAGE_IMAGE_PROCESS_TRANSFORMATION') }}</h3>
</div>
<div class="section-content">
<div class="form-row">
<el-form-item class="toggle-control">
<el-switch v-model="compressForm.isFlip" :style="switchStyle" />
<span class="toggle-label">{{ $T('UPLOAD_PAGE_IMAGE_PROCESS_ISFLIP') }}</span>
</el-form-item>
<el-form-item class="toggle-control">
<el-switch v-model="compressForm.isFlop" :style="switchStyle" />
<span class="toggle-label">{{ $T('UPLOAD_PAGE_IMAGE_PROCESS_ISFLOP') }}</span>
</el-form-item>
</div>
<el-form-item class="toggle-control">
<el-switch v-model="compressForm.isRotate" :style="switchStyle" />
<span class="toggle-label">{{
compressForm.isRotate
? $T('UPLOAD_PAGE_IMAGE_PROCESS_ISROTATE')
: $T('UPLOAD_PAGE_IMAGE_PROCESS_ISROTATE')
}}</span>
</el-form-item>
<div v-show="compressForm.isRotate" class="subsection">
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ROTATEDEGREE')">
<div class="rotation-control">
<el-input-number v-model="compressForm.rotateDegree" :step="1" controls-position="right" />
<div class="rotation-buttons">
<el-button size="small" @click="compressForm.rotateDegree = 0">0°</el-button>
<el-button size="small" @click="compressForm.rotateDegree = 90">90°</el-button>
<el-button size="small" @click="compressForm.rotateDegree = 180">180°</el-button>
<el-button size="small" @click="compressForm.rotateDegree = 270">270°</el-button>
</div>
</div>
</el-form-item>
</div>
</div>
</section>
</div>
</div>
<!-- Action Buttons -->
<div class="form-actions">
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ISADDWM')">
<el-switch v-model="waterMarkForm.isAddWatermark" :style="switchStyle" />
</el-form-item>
<el-form-item v-show="waterMarkForm.isAddWatermark" :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_WMTYPE')">
<el-radio-group v-model="waterMarkForm.watermarkType">
<el-radio value="text">
{{ $T('UPLOAD_PAGE_IMAGE_PROCESS_WMTYPE_TEXT') }}
</el-radio>
<el-radio value="image">
{{ $T('UPLOAD_PAGE_IMAGE_PROCESS_WMTYPE_IMAGE') }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-show="waterMarkForm.isAddWatermark" :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ISFULLSCREEN_WM')">
<el-switch v-model="waterMarkForm.isFullScreenWatermark" :style="switchStyle" />
</el-form-item>
<el-form-item v-show="waterMarkForm.isAddWatermark" :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_WMDEGREE')">
<el-input-number v-model="waterMarkForm.watermarkDegree" :step="1" />
</el-form-item>
<el-form-item
v-show="waterMarkForm.isAddWatermark && waterMarkForm.watermarkType === 'text'"
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_WMTEXT')"
>
<el-input v-model="waterMarkForm.watermarkText" />
</el-form-item>
<el-form-item
v-show="waterMarkForm.isAddWatermark && waterMarkForm.watermarkType === 'text'"
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_WMTEXT_FONT_PATH')"
>
<el-input v-model="waterMarkForm.watermarkFontPath" />
</el-form-item>
<el-form-item v-show="waterMarkForm.isAddWatermark" :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_WMRATIO')">
<el-input-number v-model="waterMarkForm.watermarkScaleRatio" :min="0" :max="1" :step="0.01" />
</el-form-item>
<el-form-item
v-show="waterMarkForm.isAddWatermark && waterMarkForm.watermarkType === 'text'"
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_WMCOLOR')"
>
<el-color-picker v-model="waterMarkForm.watermarkColor" show-alpha />
</el-form-item>
<el-form-item
v-show="waterMarkForm.isAddWatermark && waterMarkForm.watermarkType === 'image'"
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_WMPATH')"
>
<el-input v-model="waterMarkForm.watermarkImagePath" />
</el-form-item>
<el-form-item v-show="waterMarkForm.isAddWatermark" :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_WMPOSITION')">
<el-radio-group v-model="waterMarkForm.watermarkPosition">
<el-radio v-for="item in waterMarkPositionMap" :key="item[0]" :value="item[0]">
{{ item[1] }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ISREMOVEEXIF')">
<el-switch v-model="compressForm.isRemoveExif" :style="switchStyle" />
</el-form-item>
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_QUALITY')">
<el-input-number v-model="compressForm.quality" :min="1" :max="100" :step="1" />
</el-form-item>
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ISCONVERT')">
<el-switch v-model="compressForm.isConvert" :style="switchStyle" />
</el-form-item>
<el-form-item v-show="compressForm.isConvert" :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_CONVERTFORMAT')">
<el-select v-model="compressForm.convertFormat" :persistent="false" teleported>
<el-option v-for="item in availableFormat" :key="item" :label="item" :value="item" />
</el-select>
</el-form-item>
<el-form-item v-show="compressForm.isConvert" :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_CONVERTFORMAT_SPECIFIC')">
<el-input
v-model="formatConvertObj"
placeholder='{"jpg": "png", "png": "jpg"}'
type="textarea"
:autosize="{ minRows: 2, maxRows: 4 }"
/>
</el-form-item>
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ISFLIP')">
<el-switch v-model="compressForm.isFlip" :style="switchStyle" />
</el-form-item>
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ISFLOP')">
<el-switch v-model="compressForm.isFlop" :style="switchStyle" />
</el-form-item>
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ISRESIZE')">
<el-switch v-model="compressForm.isReSize" :style="switchStyle" />
</el-form-item>
<el-form-item v-show="compressForm.isReSize" :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_RESIZEWIDTH')">
<el-input-number v-model="compressForm.reSizeWidth" :min="0" />
</el-form-item>
<el-form-item v-show="compressForm.isReSize" :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_RESIZEHEIGHT')">
<el-input-number v-model="compressForm.reSizeHeight" :min="0" />
</el-form-item>
<el-form-item
v-show="compressForm.isReSize && compressForm.reSizeHeight! > 0 && compressForm.reSizeWidth === 0"
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_SKIPRESIZEOfSMALLIMG_HEIGHT')"
>
<el-switch v-model="compressForm.skipReSizeOfSmallImg" :style="switchStyle" />
</el-form-item>
<el-form-item
v-show="compressForm.isReSize && compressForm.reSizeWidth! > 0 && compressForm.reSizeHeight === 0"
:label="$T('UPLOAD_PAGE_IMAGE_PROCESS_SKIPRESIZEOfSMALLIMG_WIDTH')"
>
<el-switch v-model="compressForm.skipReSizeOfSmallImg" :style="switchStyle" />
</el-form-item>
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ISRESIZEBYPERCENT')">
<el-switch v-model="compressForm.isReSizeByPercent" :style="switchStyle" />
</el-form-item>
<el-form-item v-show="compressForm.isReSizeByPercent" :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_RESIZEPERCENT')">
<el-input-number v-model="compressForm.reSizePercent" :min="0" />
</el-form-item>
<el-form-item :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ISROTATE')">
<el-switch v-model="compressForm.isRotate" :style="switchStyle" />
</el-form-item>
<el-form-item v-show="compressForm.isRotate" :label="$T('UPLOAD_PAGE_IMAGE_PROCESS_ROTATEDEGREE')">
<el-input-number v-model="compressForm.rotateDegree" :step="1" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSaveConfig">
{{ $T('UPLOAD_PAGE_IMAGE_PROCESS_CONFIRM') }}
</el-button>
<el-button @click="closeDialog">
{{ $T('UPLOAD_PAGE_IMAGE_PROCESS_CANCEL') }}
</el-button>
</div>
</el-form-item>
</el-form>
</template>
<script lang="ts" setup>
import { IBuildInCompressOptions, IBuildInWaterMarkOptions } from 'piclist'
import { onBeforeMount, reactive, ref, toRaw } from 'vue'
import { Picture, Edit, Document, ZoomIn, ScaleToOriginal, Refresh } from '@element-plus/icons-vue'
import { T as $T } from '@/i18n/index'
import { getConfig, saveConfig } from '@/utils/dataSender'
@@ -285,15 +142,15 @@ import { configPaths } from '#/utils/configPaths'
const imageProcessDialogVisible = defineModel<boolean>()
const waterMarkPositionMap = new Map([
['northwest', $T('UPLOAD_PAGE_IMAGE_PROCESS_POSITION_TOP_LEFT')],
['north', $T('UPLOAD_PAGE_IMAGE_PROCESS_POSITION_TOP')],
['northeast', $T('UPLOAD_PAGE_IMAGE_PROCESS_POSITION_TOP_RIGHT')],
['west', $T('UPLOAD_PAGE_IMAGE_PROCESS_POSITION_LEFT')],
['centre', $T('UPLOAD_PAGE_IMAGE_PROCESS_POSITION_CENTER')],
['east', $T('UPLOAD_PAGE_IMAGE_PROCESS_POSITION_RIGHT')],
['southwest', $T('UPLOAD_PAGE_IMAGE_PROCESS_POSITION_BOTTOM_LEFT')],
['southeast', $T('UPLOAD_PAGE_IMAGE_PROCESS_POSITION_BOTTOM_RIGHT')],
['south', $T('UPLOAD_PAGE_IMAGE_PROCESS_POSITION_BOTTOM')],
['southeast', $T('UPLOAD_PAGE_IMAGE_PROCESS_POSITION_BOTTOM_RIGHT')]
['southwest', $T('UPLOAD_PAGE_IMAGE_PROCESS_POSITION_BOTTOM_LEFT')],
['northwest', $T('UPLOAD_PAGE_IMAGE_PROCESS_POSITION_TOP_LEFT')],
['west', $T('UPLOAD_PAGE_IMAGE_PROCESS_POSITION_LEFT')],
['east', $T('UPLOAD_PAGE_IMAGE_PROCESS_POSITION_RIGHT')],
['centre', $T('UPLOAD_PAGE_IMAGE_PROCESS_POSITION_CENTER')]
])
const imageExtList = ['jpg', 'jpeg', 'png', 'webp', 'bmp', 'tiff', 'tif', 'svg', 'ico', 'avif', 'heif', 'heic']
const availableFormat = [
@@ -405,233 +262,3 @@ onBeforeMount(async () => {
await initData()
})
</script>
<style lang="stylus" scoped>
.image-process-form {
max-height: 85vh;
overflow-y: auto;
padding: 16px;
scrollbar-width: thin;
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar-thumb {
background: rgba(144, 147, 153, 0.3);
border-radius: 6px;
}
&::-webkit-scrollbar-track {
background: transparent;
}
}
.settings-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
}
.settings-column {
display: flex;
flex-direction: column;
gap: 20px;
}
.settings-section {
background: var(--el-bg-color-overlay, #ffffff);
border-radius: 10px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.06);
overflow: hidden;
transition: all 0.25s ease;
border: 1px solid var(--el-border-color-lighter, #EBEEF5);
&:hover {
box-shadow: 0 4px 16px 0 rgba(0, 0, 0, 0.08);
transform: translateY(-2px);
}
}
.section-header {
display: flex;
align-items: center;
gap: 10px;
padding: 14px 18px;
background: linear-gradient(90deg, var(--el-color-primary-light-9, #ecf5ff) 0%, transparent 100%);
border-bottom: 1px solid var(--el-border-color-light, #E4E7ED);
border-left: 4px solid var(--el-color-primary, #409eff);
.el-icon {
color: var(--el-color-primary, #409eff);
font-size: 18px;
}
h3 {
margin: 0;
font-size: 16px;
font-weight: 600;
color: var(--el-text-color-primary, #303133);
}
}
.section-content {
padding: 18px;
}
.subsection {
background: var(--el-fill-color-light, #f5f7fa);
border-radius: 8px;
padding: 16px;
margin-top: 10px;
animation: fade-in 0.3s ease;
}
.variant-subsection {
background: var(--el-color-primary-light-9, #ecf5ff);
border-left: 3px solid var(--el-color-primary-light-5, #a0cfff);
}
.subsection-title {
font-size: 14px;
font-weight: 500;
margin: 0 0 12px 0;
color: var(--el-color-primary, #409eff);
border-bottom: 1px dashed var(--el-border-color-light, #E4E7ED);
padding-bottom: 8px;
}
.toggle-control {
display: flex;
align-items: center;
margin-bottom: 14px;
:deep(.el-form-item__content) {
display: flex;
align-items: center;
}
.toggle-label {
margin-left: 10px;
font-size: 14px;
color: var(--el-text-color-regular, #606266);
}
}
.form-row {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 16px;
}
.position-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 8px;
:deep(.el-radio) {
margin-right: 0;
margin-bottom: 8px;
.el-radio__label {
font-size: 13px;
}
}
}
.color-picker-container {
display: flex;
align-items: center;
gap: 12px;
}
.color-value {
font-family: monospace;
background: var(--el-fill-color-lighter, #f5f7fa);
padding: 4px 8px;
border-radius: 4px;
font-size: 13px;
}
.rotation-control {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 12px;
}
.rotation-buttons {
display: flex;
gap: 8px;
.el-button {
transition: transform 0.2s;
&:hover {
transform: scale(1.05);
}
}
}
.code-input {
font-family: monospace;
font-size: 13px;
}
.helper-text {
font-size: 12px;
color: var(--el-text-color-secondary, #909399);
margin-top: 4px;
padding-left: 2px;
}
.full-width {
width: 100%;
}
.form-actions {
display: flex;
justify-content: center;
gap: 16px;
margin-top: 28px;
padding: 16px 0;
background: linear-gradient(to top, var(--el-bg-color, #ffffff) 60%, transparent);
position: sticky;
bottom: 0;
z-index: 10;
.el-button {
min-width: 120px;
transition: all 0.3s;
&:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
}
}
@keyframes fade-in {
from { opacity: 0; transform: translateY(-5px); }
to { opacity: 1; transform: translateY(0); }
}
@media (max-width: 768px) {
.settings-grid {
grid-template-columns: 1fr;
}
.position-grid {
grid-template-columns: repeat(2, 1fr);
}
.form-actions {
flex-direction: column;
align-items: stretch;
.el-button {
width: 100%;
}
}
}
</style>

View File

@@ -4,20 +4,21 @@
<div class="fake-title-bar__title">PicList - {{ version }}</div>
<div v-if="osGlobal !== 'darwin'" class="handle-bar">
<el-icon
class="control-icon always-on-top"
class="minus"
:color="isAlwaysOnTop ? '#409EFF' : '#fff'"
size="20"
style="margin-right: 10px"
@click="setAlwaysOnTop"
>
<ArrowUpBold />
</el-icon>
<el-icon class="control-icon minimize" color="#fff" size="20" @click="minimizeWindow">
<el-icon class="minus" color="#fff" size="20" style="margin-right: 10px" @click="minimizeWindow">
<SemiSelect />
</el-icon>
<el-icon class="control-icon mini-window" color="#FFA500" size="20" @click="openMiniWindow">
<el-icon class="plus" color="orange" size="20" style="margin-right: 10px" @click="openMiniWindow">
<ArrowDownBold />
</el-icon>
<el-icon class="control-icon close" color="#fff" size="20" @click="closeWindow">
<el-icon class="close" color="#fff" size="20" @click="closeWindow">
<CloseBold />
</el-icon>
</div>
@@ -25,30 +26,38 @@
<el-progress
v-if="isShowprogress"
:percentage="progress"
:stroke-width="4"
:stroke-width="7"
:text-inside="true"
:show-text="false"
status="success"
class="progress-bar"
/>
<el-row class="main-content">
<el-row style="padding-top: 22px" class="main-content">
<el-col class="side-bar-menu">
<el-menu class="picgo-sidebar" :default-active="defaultActive" :unique-opened="true" @select="handleSelect">
<el-menu-item :index="routerConfig.UPLOAD_PAGE" class="side-menu-item">
<el-icon class="menu-icon"><UploadFilled /></el-icon>
<el-menu-item :index="routerConfig.UPLOAD_PAGE">
<el-icon>
<UploadFilled />
</el-icon>
<span>{{ $T('UPLOAD_AREA') }}</span>
</el-menu-item>
<el-menu-item :index="routerConfig.MANAGE_LOGIN_PAGE" class="side-menu-item">
<el-icon class="menu-icon"><PieChart /></el-icon>
<el-menu-item :index="routerConfig.MANAGE_LOGIN_PAGE">
<el-icon>
<PieChart />
</el-icon>
<span>{{ $T('MANAGE_PAGE') }}</span>
</el-menu-item>
<el-menu-item :index="routerConfig.GALLERY_PAGE" class="side-menu-item">
<el-icon class="menu-icon"><PictureFilled /></el-icon>
<el-menu-item :index="routerConfig.GALLERY_PAGE">
<el-icon>
<PictureFilled />
</el-icon>
<span>{{ $T('GALLERY') }}</span>
</el-menu-item>
<el-sub-menu index="sub-menu" :show-timeout="0" :hide-timeout="0" :popper-offset="0" class="side-menu-sub">
<el-sub-menu index="sub-menu" :show-timeout="0" :hide-timeout="0" :popper-offset="0">
<template #title>
<el-icon class="menu-icon"><Menu /></el-icon>
<el-icon>
<Menu />
</el-icon>
<span>{{ $T('PICBEDS_SETTINGS') }}</span>
</template>
<template v-for="item in picBedGlobal">
@@ -56,32 +65,35 @@
v-if="item.visible"
:key="item.type"
:index="`${routerConfig.UPLOADER_CONFIG_PAGE}-${item.type}`"
class="side-submenu-item"
>
<span>{{ item.name }}</span>
</el-menu-item>
</template>
</el-sub-menu>
<el-menu-item :index="routerConfig.SETTING_PAGE" class="side-menu-item">
<el-icon class="menu-icon"><Tools /></el-icon>
<el-menu-item :index="routerConfig.SETTING_PAGE">
<el-icon>
<Tools />
</el-icon>
<span>{{ $T('PICLIST_SETTINGS') }}</span>
</el-menu-item>
<el-menu-item :index="routerConfig.PLUGIN_PAGE" class="side-menu-item">
<el-icon class="menu-icon"><Share /></el-icon>
<el-menu-item :index="routerConfig.PLUGIN_PAGE">
<el-icon>
<Share />
</el-icon>
<span>{{ $T('PLUGIN_SETTINGS') }}</span>
</el-menu-item>
<el-menu-item :index="routerConfig.DocumentPage" class="side-menu-item">
<el-icon class="menu-icon"><Link /></el-icon>
<el-menu-item :index="routerConfig.DocumentPage">
<el-icon>
<Link />
</el-icon>
<span>{{ $T('MANUAL') }}</span>
</el-menu-item>
</el-menu>
</el-col>
<div class="info-window-container">
<el-icon class="info-window" @click="openMenu">
<InfoFilled />
</el-icon>
</div>
<el-col :span="21" :offset="3" class="main-wrapper">
</el-col>
<el-col :span="21" :offset="3" style="height: 100%" class="main-wrapper">
<router-view v-slot="{ Component }">
<transition name="picgo-fade" mode="out-in">
<keep-alive :include="keepAlivePages">
@@ -293,20 +305,13 @@ export default {
name: 'MainPage'
}
</script>
<style lang="stylus">
$darwinBg = transparentify(#172426, #000, 0.7)
$menuActiveBg = rgba(64, 158, 255, 0.15)
$menuHoverBg = rgba(255, 255, 255, 0.08)
$borderRadius = 8px
$transitionDefault = all 0.25s cubic-bezier(0.4, 0, 0.2, 1)
.setting-list-scroll
height 800px
overflow-y auto
overflow-x hidden
margin-right 0!important
.picgo-fade
&-enter,
&-leave,
@@ -314,44 +319,14 @@ $transitionDefault = all 0.25s cubic-bezier(0.4, 0, 0.2, 1)
opacity 0
&-enter-active,
&-leave-active
transition all 200ms cubic-bezier(0.4, 0, 0.2, 1)
transition all 150ms linear
.view-title
color #eee
font-size 20px
text-align center
margin 10px auto
#main-page
height 100%
background linear-gradient(135deg, rgba(40, 44, 52, 0.85) 0%, rgba(30, 34, 42, 0.9) 100%)
display flex
flex-direction column
overflow hidden
.info-window-container
position fixed
bottom 4px
left 4px
z-index 50
width 40px
height 40px
.info-window
cursor pointer
color #878d99
padding 6px
background rgba(40, 44, 52, 0.5)
border-radius 50%
box-shadow 0 2px 8px rgba(0, 0, 0, 0.15)
transition $transitionDefault
&:hover
color #409EFF
background rgba(64, 158, 255, 0.15)
transform translateY(-2px)
box-shadow 0 4px 12px rgba(0, 0, 0, 0.25)
.qrcode-dialog
.qrcode-container
display flex
@@ -360,26 +335,16 @@ $transitionDefault = all 0.25s cubic-bezier(0.4, 0, 0.2, 1)
padding-top 10px
.copy-picbed-config
margin-left 10px
transition $transitionDefault
&:hover
transform translateY(-2px)
box-shadow 0 4px 12px rgba(0, 0, 0, 0.2)
.fake-title-bar
-webkit-app-region drag
height h = 28px
height h = 22px
width 100%
text-align center
color #eee
font-size 13px
font-weight 500
font-size 12px
line-height h
position fixed
z-index 100
backdrop-filter blur(5px)
background rgba(30, 34, 42, 0.7)
box-shadow 0 1px 3px rgba(0, 0, 0, 0.2)
&.darwin
background transparent
background-image linear-gradient(
@@ -391,167 +356,98 @@ $transitionDefault = all 0.25s cubic-bezier(0.4, 0, 0.2, 1)
)
.fake-title-bar__title
padding-left 167px
.fake-title-bar__title
letter-spacing 0.5px
.handle-bar
position absolute
top 4px
right 8px
top 2px
right 4px
z-index 10000
-webkit-app-region no-drag
display flex
gap 12px
.control-icon
.el-icon
cursor pointer
font-size 16px
transition $transitionDefault
border-radius 4px
padding 2px
&.always-on-top
&:hover
color #409EFF
background rgba(64, 158, 255, 0.15)
&.minimize
&:hover
color #409EFF
background $menuHoverBg
&.close
&:hover
color #F15140
background rgba(241, 81, 64, 0.15)
&.mini-window
&:hover
color #FFC107
background rgba(255, 193, 7, 0.15)
.progress-bar
position fixed
top 28px
width 100%
z-index 90
transition opacity 0.3s ease
margin-left 5px
.el-icon.minus
&:hover
color #409EFF
.el-icon.close
&:hover
color #F15140
.el-icon.plus
&:hover
color #69C282
.main-wrapper
&.darwin
background $darwinBg
padding 10px
border-radius $borderRadius
background rgba(30, 34, 42, 0.5)
.side-bar-menu
position fixed
height calc(100vh - 28px)
height calc(100vh - 22px)
overflow-x hidden
overflow-y auto
width 142px
padding-top 35px
padding-bottom 40px
transition $transitionDefault
background rgba(30, 34, 42, 0.5)
backdrop-filter blur(10px)
&:hover
width 145px
.info-window
cursor pointer
position fixed
bottom 4px
left 4px
cursor poiter
color #878d99
transition .2s all ease-in-out
&:hover
color #409EFF
.el-menu
border-right none
background transparent
width 142px
&-item
color #eee
position relative
border-radius $borderRadius
margin 4px 8px
height 40px
line-height 40px
transition $transitionDefault
&:focus,
&:hover
color #fff
background $menuHoverBg
background transparent
&.is-active
color active-color = #409EFF
background $menuActiveBg
font-weight 500
.menu-icon
color active-color
.side-menu-item
padding-left 16px!important
.menu-icon
margin-right 8px
transition $transitionDefault
&:before
content ''
position absolute
width 1px
height 20px
right 0
top 18px
background active-color
.el-sub-menu__title
color #eee
border-radius $borderRadius
margin 4px 8px
height 40px
line-height 40px
transition $transitionDefault
&:hover
background $menuHoverBg
background transparent
span
color #fff
.menu-icon
margin-right 8px
.el-sub-menu
.el-menu-item
min-width 142px
&.is-active
background $menuActiveBg
.side-submenu-item
margin-left 0!important
border-radius $borderRadius
padding-left 38px!important
&:before
top 16px
.main-content
padding-top 28px
padding-top 22px
position relative
height calc(100vh - 28px)
height calc(100vh - 22px)
z-index 10
.el-dialog__body
padding 20px
.support
text-align center
&-title
text-align center
color #878d99
.align-center
input
text-align center
*::-webkit-scrollbar
width 4px
width 2px
height 8px
*::-webkit-scrollbar-thumb
border-radius 10px
background rgba(111, 111, 111, 0.5)
&:hover
background rgba(140, 140, 140, 0.7)
border-radius 4px
background #6f6f6f
*::-webkit-scrollbar-track
background-color transparent
</style>

View File

@@ -1,27 +1,19 @@
<template>
<div id="gallery-view" :style="handleBarActive ? 'height: 95%;' : 'height: 95%;'">
<div id="gallery-view" :style="handleBarActive ? 'height: 85%;' : 'height: 95%;'">
<div class="view-title">
<div class="left-controls">
<span class="gallery-sync-delete">
{{ $T('GALLERY_SYNC_DELETE') }}
<el-switch
v-model="deleteCloud"
:active-text="$T('SETTINGS_OPEN')"
:inactive-text="$T('SETTINGS_CLOSE')"
@change="handleDeleteCloudFile"
/>
</span>
</div>
<div class="title-content">
{{ $T('GALLERY') }} - {{ filterList.length }}
<el-icon class="cursor-pointer toggle-icon" @click="toggleHandleBar">
<CaretBottom v-show="!handleBarActive" />
<CaretTop v-show="handleBarActive" />
</el-icon>
</div>
<div class="right-controls">
{{ $T('GALLERY') }} - {{ filterList.length }}
<el-icon style="margin-left: 4px" class="cursor-pointer" @click="toggleHandleBar">
<CaretBottom v-show="!handleBarActive" />
<CaretTop v-show="handleBarActive" />
</el-icon>
<span style="position: absolute; right: 0; top: 0; margin-right: 20px; font-size: 0.8em; color: #fff">
{{ $T('GALLERY_SYNC_DELETE') }}
<el-switch
v-model="deleteCloud"
:active-text="$T('SETTINGS_OPEN')"
:inactive-text="$T('SETTINGS_CLOSE')"
@change="handleDeleteCloudFile"
/>
<el-button type="primary" :link="true" @click="refreshPage">
<el-tooltip
class="item"
@@ -31,12 +23,12 @@
:persistent="false"
teleported
>
<el-icon size="22">
<el-icon size="25" style="cursor: pointer; margin-left: 10px">
<Refresh />
</el-icon>
</el-tooltip>
</el-button>
</div>
</span>
</div>
<transition name="el-zoom-in-top">
<el-row v-show="handleBarActive">
@@ -976,96 +968,41 @@ export default {
display none
&__Counter
margin-top 20px
font-weight 500
color #f5f5f7
.view-title
color #f5f5f7
font-size 22px
color #eee
font-size 20px
text-align center
margin 16px auto 22px
font-weight 500
position relative
display flex
justify-content space-between
align-items center
padding 0 20px
.title-content
display flex
align-items center
justify-content center
flex-grow 1
.toggle-icon
margin-left 8px
transition all .3s cubic-bezier(0.4, 0, 0.2, 1)
&:hover
color #49B1F5
transform scale(1.1)
.left-controls, .right-controls
min-width 200px
display flex
align-items center
.left-controls
justify-content flex-start
.right-controls
justify-content flex-end
.gallery-sync-delete
display flex
align-items center
margin 10px auto
.sub-title
font-size 14px
background rgba(48, 48, 48, 0.7)
padding 6px 12px
border-radius 8px
backdrop-filter blur(5px)
box-shadow 0 2px 8px rgba(0, 0, 0, 0.15)
.el-switch
margin-left 10px
.el-icon-caret-bottom
cursor: pointer
transition all .2s ease-in-out
&.active
transform: rotate(180deg)
#gallery-view
position absolute
left 142px
right 0
height 95%
transition all .3s cubic-bezier(0.4, 0, 0.2, 1)
height 85%
.cursor-pointer
cursor pointer
.item-base
background #363636
background #2E2E2E
text-align center
padding 5px 0
cursor pointer
font-size 13px
transition all .3s cubic-bezier(0.4, 0, 0.2, 1)
height 28px
box-sizing border-box
border-radius 16px
font-weight 500
box-shadow 0 2px 8px rgba(0, 0, 0, 0.15)
display flex
align-items center
justify-content center
width 100%
padding 0 10px
transition all .2s ease-in-out
height: 28px
box-sizing: border-box
&.copy
cursor not-allowed
background #49b1f5
background #49B1F5
&.active
cursor pointer
background #1B9EF3
color #fff
box-shadow 0 4px 12px rgba(27, 158, 243, 0.3)
transform translateY(-1px)
&.delete
cursor not-allowed
background #F47466
@@ -1073,9 +1010,6 @@ export default {
cursor pointer
background #F15140
color #fff
box-shadow 0 4px 12px rgba(241, 81, 64, 0.3)
transform translateY(-1px)
&.all-pick
cursor not-allowed
background #69C282
@@ -1083,60 +1017,37 @@ export default {
cursor pointer
background #44B363
color #fff
box-shadow 0 4px 12px rgba(68, 179, 99, 0.3)
transform translateY(-1px)
.handle-bar
.item-base
min-width 80px
white-space nowrap
overflow hidden
text-overflow ellipsis
.el-col:nth-child(n+4):nth-child(-n+7)
padding-left: 6px
padding-right: 6px
#gallery-view
.round
border-radius 16px
border-radius 14px
.pull-right
float right
.gallery-list
height calc(100% - 60px)
height 100%
box-sizing border-box
padding 12px 0
padding 8px 0
overflow-y auto
overflow-x hidden
overflow-x auto
position absolute
top 60px
transition all .3s cubic-bezier(0.4, 0, 0.2, 1)
top: 38px
transition all .2s ease-in-out .1s
width 100%
&.small
height calc(100% - 180px)
top 180px
margin-top 0
bottom 0 // Ensure it extends to the bottom
height: 100%
top: 113px
&__img
// height 150px
position relative
margin-bottom 16px
margin-bottom 8px
&__item
width 100%
height 130px
transition all .3s cubic-bezier(0.4, 0, 0.2, 1)
height 120px
transition all .2s ease-in-out
cursor pointer
margin-bottom 8px
margin-bottom 4px
overflow hidden
display flex
border-radius 8px
box-shadow 0 3px 12px rgba(0, 0, 0, 0.15)
background #2a2a2a
margin-bottom 6px
&-fake
position absolute
top 0
@@ -1144,209 +1055,40 @@ export default {
opacity 0
width 100%
z-index -1
&:hover
transform scale(1.05)
box-shadow 0 6px 16px rgba(0, 0, 0, 0.2)
transform scale(1.1)
&-img
width 100%
object-fit contain
padding 4px
&__tool-panel
color #f0f0f0
margin 0 2px 8px 2px
color #ddd
margin-bottom 4px
display flex
align-items center
background rgba(42, 42, 42, 0.7)
border-radius 6px
padding 4px 8px
backdrop-filter blur(5px)
box-shadow 0 2px 8px rgba(0, 0, 0, 0.1)
.el-checkbox
height 16px
i
cursor pointer
transition all .2s ease-in-out
margin-right 8px
font-size 16px
margin-right 4px
&.document
&:hover
color #49B1F5
transform scale(1.2)
&.edit
&:hover
color #69C282
transform scale(1.2)
&.delete
&:hover
color #F15140
transform scale(1.2)
&__file-name
overflow hidden
text-overflow ellipsis
white-space nowrap
color #f0f0f0
color #ddd
font-size 14px
margin-bottom 6px
margin-bottom 4px
text-align center
align-self center
font-weight 500
padding 0 4px
.handle-bar
color #f0f0f0
margin-bottom 16px
background rgba(48, 48, 48, 0.7)
border-radius 10px
padding 8px 12px 10px
backdrop-filter blur(10px)
box-shadow 0 4px 16px rgba(0, 0, 0, 0.2)
position relative
z-index 2
align-items center
.el-row
margin-bottom 0 !important
.el-col
display flex
align-items center
min-height 28px
padding-top: 2px
padding-bottom: 2px
.el-input, .el-select, .el-date-picker
.el-input__wrapper
padding 0 8px
line-height 1.4
height 28px
.el-button
height 28px
padding 6px 12px
.item-base
height 28px
display flex
align-items center
justify-content center
padding 0
&:last-child
margin-bottom 30px
&:after
content ""
position absolute
left 0
right 0
bottom -20px
height 8px
.handle-bar + .handle-bar
margin-top 0
margin-bottom 14px
padding-top 0
border-top none
position relative
&:before
content ""
position absolute
left 10%
right 10%
top -6px
height 1px
background rgba(255, 255, 255, 0.1)
border-radius 1px
.handle-bar-container
position relative
&:after
content ""
position absolute
left 5%
right 5%
bottom -10px
height 2px
background linear-gradient(to right, transparent, rgba(255, 255, 255, 0.1), transparent)
border-radius 2px
z-index 2
.el-select,
.el-input,
.el-date-editor
.el-input__wrapper
background rgba(60, 60, 60, 0.7) !important
border-radius 8px !important
box-shadow none !important
border 1px solid rgba(100, 100, 100, 0.3) !important
&:hover, &:focus
border-color rgba(73, 177, 245, 0.5) !important
box-shadow 0 0 0 1px rgba(73, 177, 245, 0.1) !important
.el-button
border-radius 8px
transition all .3s cubic-bezier(0.4, 0, 0.2, 1)
&:hover
transform translateY(-1px)
box-shadow 0 4px 12px rgba(0, 0, 0, 0.15)
.el-dialog
border-radius 12px
overflow hidden
box-shadow 0 20px 40px rgba(0, 0, 0, 0.3)
.el-dialog__header
background #363636
margin 0
padding 16px 20px
.el-dialog__body
padding 24px
.el-dialog__footer
padding 16px 20px
background #2a2a2a
::-webkit-scrollbar
width 8px
height 8px
::-webkit-scrollbar-track
background rgba(40, 40, 40, 0.8)
border-radius 4px
::-webkit-scrollbar-thumb
background rgba(120, 120, 120, 0.8)
border-radius 4px
&:hover
background rgba(140, 140, 140, 0.8)
// Ensure transitions are smooth for the gallery area adjusting to handle bar visibility
.el-zoom-in-top-enter-active,
.el-zoom-in-top-leave-active
transition all .3s cubic-bezier(0.4, 0, 0.2, 1)
position relative
z-index 3
.el-zoom-in-top-enter-from,
.el-zoom-in-top-leave-to
opacity 0
transform translateY(-20px)
// Optimize the transition when toggling the handle bar
.gallery-list
transition all .35s cubic-bezier(0.4, 0, 0.2, 1) 0.05s // Slight delay to allow handle bar to animate first
color #ddd
margin-bottom 10px
</style>

View File

@@ -1,144 +1,122 @@
<template>
<div id="plugin-view">
<div class="view-header">
<div class="view-title">
<span>{{ $T('PLUGIN_SETTINGS') }}</span>
<div class="view-actions">
<el-tooltip :content="pluginListToolTip" placement="top" :persistent="false" teleported>
<el-button class="action-button" circle @click="goAwesomeList">
<el-icon><Goods /></el-icon>
</el-button>
</el-tooltip>
<el-tooltip :content="updateAllToolTip" placement="top" :persistent="false" teleported>
<el-button class="action-button" circle @click="handleUpdateAllPlugin">
<el-icon><Refresh /></el-icon>
</el-button>
</el-tooltip>
<el-tooltip :content="importLocalPluginToolTip" placement="top" :persistent="false" teleported>
<el-button class="action-button" circle @click="handleImportLocalPlugin">
<el-icon><Download /></el-icon>
</el-button>
</el-tooltip>
</div>
</div>
<div class="search-bar">
<el-input v-model="searchText" :placeholder="$T('PLUGIN_SEARCH_PLACEHOLDER')" size="large" class="search-input">
<template #prefix>
<el-icon class="search-icon"><Search /></el-icon>
</template>
<template #suffix>
<el-icon v-if="searchText" class="clear-icon" @click="cleanSearch">
<Close />
</el-icon>
</template>
</el-input>
</div>
<div class="view-title">
{{ $T('PLUGIN_SETTINGS') }} -
<el-tooltip :content="pluginListToolTip" placement="right" :persistent="false" teleported>
<el-icon class="el-icon-goods" @click="goAwesomeList">
<Goods />
</el-icon>
</el-tooltip>
<el-tooltip :content="updateAllToolTip" placement="left" :persistent="false" teleported>
<el-icon class="el-icon-update" @click="handleUpdateAllPlugin">
<Refresh />
</el-icon>
</el-tooltip>
<el-tooltip :content="importLocalPluginToolTip" placement="left">
<el-icon class="el-icon-download" :persistent="false" teleported @click="handleImportLocalPlugin">
<Download />
</el-icon>
</el-tooltip>
</div>
<div id="pluginList" v-loading="loading" class="plugin-list" element-loading-text="Loading...">
<el-row :gutter="16">
<el-col
v-for="item in pluginList"
:key="item.fullName"
class="plugin-item__container"
:xs="24"
:sm="12"
:md="8"
:lg="8"
:xl="6"
>
<div class="plugin-card" :class="{ disabled: !item.enabled, darwin: osGlobal === 'darwin' }">
<div v-if="!item.gui" class="cli-only-badge">CLI</div>
<div class="plugin-header">
<img class="plugin-logo" :src="item.logo" :onerror="defaultLogo" />
<div class="plugin-title" @click="openHomepage(item.homepage)">
<div class="plugin-name">
{{ item.name }}
<el-tag
v-if="latestVersionMap[item.fullName] && latestVersionMap[item.fullName] !== item.version"
type="success"
size="small"
effect="dark"
class="version-tag"
>
new
</el-tag>
</div>
<div class="plugin-version">v{{ item.version }}</div>
</div>
<el-row class="handle-bar" :class="{ 'cut-width': pluginList.length > 6 }">
<el-input v-model="searchText" :placeholder="$T('PLUGIN_SEARCH_PLACEHOLDER')" size="small">
<template #suffix>
<el-icon class="el-input__icon" style="cursor: pointer" @click="cleanSearch">
<close />
</el-icon>
</template>
</el-input>
</el-row>
<el-row id="pluginList" v-loading="loading" :gutter="10" class="plugin-list">
<el-col
v-for="item in pluginList"
:key="item.fullName"
class="plugin-item__container"
:xs="24"
:sm="pluginList.length === 1 ? 24 : 12"
:md="pluginList.length === 1 ? 24 : 12"
:lg="pluginList.length === 1 ? 24 : 12"
:xl="pluginList.length === 1 ? 24 : 12"
>
<div class="plugin-item" :class="{ darwin: osGlobal === 'darwin' }">
<div v-if="!item.gui" class="cli-only-badge" title="CLI only">CLI</div>
<img class="plugin-item__logo" :src="item.logo" :onerror="defaultLogo" />
<div class="plugin-item__content" :class="{ disabled: !item.enabled }">
<div class="plugin-item__name" @click="openHomepage(item.homepage)">
{{ item.name }} <small>{{ ' ' + item.version }}</small> &nbsp;
<!-- 升级提示 -->
<el-tag
v-if="latestVersionMap[item.fullName] && latestVersionMap[item.fullName] !== item.version"
type="success"
size="small"
round
effect="plain"
>
new
</el-tag>
</div>
<div class="plugin-description" :title="item.description">
<div class="plugin-item__desc" :title="item.description">
{{ item.description }}
</div>
<div class="plugin-footer">
<div class="plugin-author">
<el-icon><User /></el-icon>
<div class="plugin-item__info-bar">
<span class="plugin-item__author">
{{ item.author.replace(/<.*>/, '') }}
</div>
<div class="plugin-actions">
</span>
<span class="plugin-item__config">
<template v-if="searchText">
<template v-if="!item.hasInstall">
<el-button
v-if="!item.ing"
type="primary"
size="small"
:loading="item.ing"
@click="installPlugin(item)"
>
<span v-if="!item.ing" class="config-button install" @click="installPlugin(item)">
{{ $T('PLUGIN_INSTALL') }}
</el-button>
<el-button v-else type="info" size="small" loading>
</span>
<span v-else-if="item.ing" class="config-button ing">
{{ $T('PLUGIN_INSTALLING') }}
</el-button>
</span>
</template>
<el-button v-else type="success" size="small" disabled>
<span v-else class="config-button ing">
{{ $T('PLUGIN_INSTALLED') }}
</el-button>
</span>
</template>
<template v-else>
<el-button v-if="item.ing" type="info" size="small" loading>
<span v-if="item.ing" class="config-button ing">
{{ $T('PLUGIN_DOING_SOMETHING') }}
</el-button>
</span>
<template v-else>
<el-button v-if="item.enabled" type="primary" size="small" circle @click="buildContextMenu(item)">
<el-icon><Tools /></el-icon>
</el-button>
<el-button v-else type="danger" size="small" circle @click="buildContextMenu(item)">
<el-icon><Remove /></el-icon>
</el-button>
<el-icon v-if="item.enabled" class="el-icon-setting" @click="buildContextMenu(item)">
<Tools />
</el-icon>
<el-icon v-else class="el-icon-remove-outline" @click="buildContextMenu(item)">
<Remove />
</el-icon>
</template>
</template>
</div>
</span>
</div>
</div>
</el-col>
</el-row>
</div>
<div v-show="needReload" class="reload-mask">
<el-button type="primary" size="large" @click="reloadApp">
<el-icon class="reload-icon"><Refresh /></el-icon>
</div>
</el-col>
</el-row>
<el-row v-show="needReload" class="reload-mask" :class="{ 'cut-width': pluginList.length > 6 }" justify="center">
<el-button type="primary" size="small" round @click="reloadApp">
{{ $T('TIPS_NEED_RELOAD') }}
</el-button>
</div>
</el-row>
<el-dialog
v-model="dialogVisible"
:modal-append-to-body="false"
:title="$T('CONFIG_THING', { c: configName })"
:title="
$T('CONFIG_THING', {
c: configName
})
"
width="70%"
class="config-dialog"
append-to-body
>
<config-form :id="configName" ref="$configForm" :config="config" :type="currentType" color-mode="white" />
<template #footer>
<el-button @click="dialogVisible = false">
<el-button round @click="dialogVisible = false">
{{ $T('CANCEL') }}
</el-button>
<el-button type="primary" @click="handleConfirmConfig">
<el-button type="primary" round @click="handleConfirmConfig">
{{ $T('CONFIRM') }}
</el-button>
</template>
@@ -151,7 +129,7 @@ import axios from 'axios'
import { ipcRenderer, IpcRendererEvent } from 'electron'
import { ElMessageBox } from 'element-plus'
import { debounce, DebouncedFunc } from 'lodash'
import { Close, Download, Refresh, Goods, Remove, Tools, Search, User } from '@element-plus/icons-vue'
import { Close, Download, Refresh, Goods, Remove, Tools } from '@element-plus/icons-vue'
import { computed, ref, onBeforeMount, onBeforeUnmount, watch, onMounted, reactive, toRaw } from 'vue'
import ConfigForm from '@/components/ConfigFormForPlugin.vue'
@@ -331,8 +309,7 @@ async function buildContextMenu(plugin: IPicGoPlugin) {
function handleResize() {
const myDiv = document.getElementById('pluginList') as HTMLElement
const windowHeight = window.innerHeight
const headerHeight = 120 // Adjusted for new header layout
const newHeight = windowHeight - headerHeight - 30
const newHeight = windowHeight * 0.75
myDiv.style.height = newHeight + 'px'
}
@@ -515,283 +492,171 @@ export default {
}
</script>
<style lang="stylus">
$primaryColor = #409EFF
$dangerColor = #F56C6C
$successColor = #67C23A
$warningColor = #E6A23C
$infoColor = #909399
$textColor = #FFFFFF
$subtextColor = #E8EAED
$disabledColor = #A0A0A0
$cardBg = rgba(55, 60, 70, 0.55)
$headerBg = rgba(35, 40, 50, 0.9)
$hoverBg = rgba(65, 70, 85, 0.9)
$borderRadius = 8px
$transitionEase = all 0.3s cubic-bezier(0.4, 0, 0.2, 1)
$boxShadow = 0 4px 12px rgba(0, 0, 0, 0.2)
$hoverShadow = 0 8px 16px rgba(0, 0, 0, 0.3)
$darwinBg = #172426
#plugin-view
position absolute
left 142px
right 0
height 100%
padding 0 16px
box-sizing border-box
overflow hidden
color $textColor
.view-header
position sticky
top 0
z-index 90
padding 16px 0
background linear-gradient(to bottom, rgba(30, 34, 42, 0.95), rgba(30, 34, 42, 0.85))
backdrop-filter blur(8px)
border-bottom 1px solid rgba(255, 255, 255, 0.1)
.view-title
display flex
justify-content space-between
align-items center
margin-bottom 16px
font-size 24px
font-weight 600
span
flex 1
.view-actions
display flex
gap 8px
.action-button
background rgba(255, 255, 255, 0.1)
border none
color $textColor
transition $transitionEase
&:hover
background rgba(255, 255, 255, 0.2)
transform translateY(-2px)
.search-bar
margin-bottom 8px
.search-input
.el-input__wrapper
background rgba(255, 255, 255, 0.1)
border-radius $borderRadius
border none
box-shadow none !important
transition $transitionEase
&:hover, &:focus-within
background rgba(255, 255, 255, 0.15)
.el-input__inner
color $textColor
.search-icon
color $disabledColor
.clear-icon
color $disabledColor
cursor pointer
transition $transitionEase
&:hover
color $textColor
.el-loading-mask
background-color rgba(0, 0, 0, 0.8)
.plugin-list
height calc(100vh - 175px)
overflow-y auto
overflow-x hidden
padding 16px 0 // Increased padding from 8px to 16px
scroll-behavior smooth
.el-row
margin-bottom 0 !important // Ensure el-row doesn't add its own margins
align-content flex-start
height: 600px;
box-sizing: border-box;
padding: 8px 15px;
overflow-y: auto;
overflow-x: hidden;
position: absolute;
top: 70px;
left: 5px;
transition: all 0.2s ease-in-out 0.1s;
width: 100%
.el-loading-mask
background-color rgba(0, 0, 0, 0.8)
backdrop-filter blur(4px)
.plugin-item__container
margin-bottom 24px // Increased from 16px to provide more space between rows
.plugin-card
background $cardBg
border-radius $borderRadius
padding 16px
min-height 220px
box-shadow $boxShadow
transition $transitionEase
display flex
flex-direction column
position relative
overflow hidden
border 1px solid rgba(255, 255, 255, 0.1) // Added subtle border
left: 20px
width: calc(100% - 40px)
.view-title
color #eee
font-size 20px
text-align center
margin 10px auto
position relative
i.el-icon-goods
margin-left 4px
font-size 20px
vertical-align middle
cursor pointer
transition color .2s ease-in-out
&:hover
transform translateY(-3px)
box-shadow $hoverShadow
background $hoverBg
border-color rgba(255, 255, 255, 0.2) // Brighter border on hover
&.disabled
opacity 0.7
&:before
content ""
position absolute
top 0
left 0
right 0
bottom 0
background rgba(0, 0, 0, 0.3)
z-index 1
pointer-events none
color #49B1F5
i.el-icon-update
position absolute
right 35px
top 8px
font-size 20px
vertical-align middle
cursor pointer
transition color .2s ease-in-out
&:hover
color #49B1F5
i.el-icon-download
position absolute
right 5px
top 8px
font-size 20px
vertical-align middle
cursor pointer
transition color .2s ease-in-out
&:hover
color #49B1F5
.handle-bar
margin-bottom 20px
&.cut-width
padding-right: 8px
.el-input__inner
border-radius 0
.plugin-item
box-sizing border-box
height 80px
background #444
padding 8px
user-select text
transition all .2s ease-in-out
position relative
&__container
height 80px
margin-bottom 10px
.cli-only-badge
position absolute
right 0
right 0px
top 0
font-size 11px
font-weight 600
padding 4px 10px
background $warningColor
color #fff
z-index 5
border-bottom-left-radius $borderRadius
.plugin-header
display flex
align-items center
margin-bottom 12px
.plugin-logo
width 48px
height 48px
border-radius 12px
object-fit cover
background rgba(0, 0, 0, 0.2)
box-shadow 0 2px 6px rgba(0, 0, 0, 0.2)
.plugin-title
margin-left 12px
cursor pointer
transition $transitionEase
font-size 12px
padding 3px 8px
background #49B1F5
color #eee
&.darwin
background transparentify($darwinBg, #000, 0.75)
&:hover
color $primaryColor
.plugin-name
font-size 16px
font-weight 600
display flex
align-items center
max-width 200px
overflow hidden
text-overflow ellipsis
white-space nowrap
.version-tag
margin-left 8px
font-size 10px
height 18px
line-height 1
padding 2px 6px
border-radius 10px
.plugin-version
font-size 12px
color $disabledColor
margin-top 2px
.plugin-description
flex 1
font-size 14px
line-height 1.5
margin-bottom 16px
display -webkit-box
-webkit-line-clamp 3
-webkit-box-orient vertical
overflow hidden
color rgba(255, 255, 255, 0.95)
font-weight 400
.plugin-footer
display flex
justify-content space-between
align-items center
margin-top auto
padding-top 12px
border-top 1px solid rgba(255, 255, 255, 0.1)
.plugin-author
font-size 13px
color $disabledColor
background transparentify($darwinBg, #000, 0.85)
&:hover
background #333
&__logo
width 64px
height 64px
float left
&__content
float left
width calc(100% - 72px)
height 64px
color #ddd
margin-left 8px
display flex
align-items center
max-width 60%
flex-direction column
justify-content space-between
&.disabled
color #aaa
&__name
font-size 16px
height 22px
line-height 22px
font-weight 600
cursor pointer
text-overflow ellipsis
white-space nowrap
overflow hidden
transition all .2s ease-in-out
&:hover
color: #1B9EF3
&__desc
font-size 14px
height 21px
line-height 21px
overflow hidden
text-overflow ellipsis
white-space nowrap
.el-icon
margin-right 6px
font-size 14px
.plugin-actions
display flex
gap 8px
&__info-bar
font-size 14px
height 21px
line-height 28px
position relative
&__author
overflow hidden
text-overflow ellipsis
white-space nowrap
&__config
float right
font-size 16px
cursor pointer
transition all .2s ease-in-out
&:hover
color: #1B9EF3
.config-button
font-size 12px
color #ddd
background #222
padding 1px 8px
height 18px
line-height 18px
text-align center
position absolute
top 4px
right 20px
transition all .2s ease-in-out
&.reload
right 0px
&.ing
right 0px
&.install
right 0px
&:hover
background: #1B9EF3
color #fff
.reload-mask
position fixed
bottom 0
left 142px
right 0
padding 16px
background rgba(0, 0, 0, 0.7)
display flex
justify-content center
align-items center
backdrop-filter blur(8px)
z-index 100
box-shadow 0 -2px 10px rgba(0, 0, 0, 0.3)
animation slideUp 0.3s cubic-bezier(0.4, 0, 0.2, 1)
.reload-icon
margin-right 8px
.config-dialog
.el-dialog__header
border-bottom 1px solid rgba(255, 255, 255, 0.1)
padding-bottom 16px
.el-dialog__footer
border-top 1px solid rgba(255, 255, 255, 0.1)
padding-top 16px
@keyframes slideUp
from
transform translateY(100%)
to
transform translateY(0)
// Scrollbar styling
*::-webkit-scrollbar
width 6px
height 6px
*::-webkit-scrollbar-thumb
border-radius 10px
background rgba(255, 255, 255, 0.2)
&:hover
background rgba(255, 255, 255, 0.3)
*::-webkit-scrollbar-track
background-color transparent
position absolute
width calc(100% - 40px)
bottom -320px
text-align center
background rgba(0,0,0,0.4)
padding 10px 0
&.cut-width
width calc(100% - 48px)
</style>

View File

@@ -1,108 +1,99 @@
<template>
<div id="upload-view" class="upload-container">
<div class="container-inner">
<div class="view-title">
<el-tooltip placement="top" effect="light" :content="$T('UPLOAD_VIEW_HINT')" :persistent="false" teleported>
<span id="upload-view-title" @click="handlePicBedNameClick(picBedName, picBedConfigName)">
{{ picBedName }} - {{ picBedConfigName || 'Default' }}
</span>
</el-tooltip>
<el-button class="icon-button" type="info" size="small" circle @click="handleChangePicBed">
<el-icon><CaretBottom /></el-icon>
</el-button>
<el-button type="primary" round size="small" class="quick-upload" @click="handleImageProcess">
<el-icon><Edit /></el-icon>
{{ $T('UPLOAD_PAGE_IMAGE_PROCESS_NAME') }}
</el-button>
</div>
<div
id="upload-area"
class="upload-area"
:class="{ 'is-dragover': dragover }"
@drop.prevent="onDrop"
@dragover.prevent="dragover = true"
@dragleave.prevent="dragover = false"
>
<transition name="scale-fade">
<div v-if="showProgress" class="upload-overlay">
<el-progress
type="dashboard"
:percentage="progress"
:status="showError ? 'exception' : 'success'"
:stroke-width="8"
/>
<div class="upload-status-text">
{{ showError ? $T('UPLOAD_FAILED') : $T('UPLOADING') }}
</div>
</div>
</transition>
<div id="upload-dragger" class="upload-dragger" @click="openUplodWindow">
<el-icon class="upload-icon"><UploadFilled /></el-icon>
<div class="upload-dragger__text">
{{ $T('DRAG_FILE_TO_HERE') }}
<span class="upload-action-text">{{ $T('CLICK_TO_UPLOAD') }}</span>
</div>
<input id="file-uploader" type="file" multiple @change="onChange" />
<div id="upload-view">
<el-row :gutter="16" align="middle">
<el-col :span="24">
<div class="view-title">
<el-tooltip placement="top" effect="light" :content="$T('UPLOAD_VIEW_HINT')" :persistent="false" teleported>
<span id="upload-view-title" @click="handlePicBedNameClick(picBedName, picBedConfigName)">
{{ picBedName }} - {{ picBedConfigName || 'Default' }}
</span>
</el-tooltip>
<el-icon style="cursor: pointer; margin-left: 4px" @click="handleChangePicBed">
<CaretBottom />
</el-icon>
<el-button
type="primary"
round
size="small"
class="quick-upload"
style="margin-left: 6px"
@click="handleImageProcess"
>
{{ $T('UPLOAD_PAGE_IMAGE_PROCESS_NAME') }}
</el-button>
</div>
</div>
<div class="upload-options">
<div class="option-card link-format-card">
<div class="option-header">
<div class="option-label">
<div
id="upload-area"
:class="{ 'is-dragover': dragover }"
@drop.prevent="onDrop"
@dragover.prevent="dragover = true"
@dragleave.prevent="dragover = false"
>
<div id="upload-dragger" @click="openUplodWindow">
<el-icon>
<UploadFilled />
</el-icon>
<div class="upload-dragger__text">
{{ $T('DRAG_FILE_TO_HERE') }}
<span>{{ $T('CLICK_TO_UPLOAD') }}</span>
</div>
<input id="file-uploader" type="file" multiple @change="onChange" />
</div>
</div>
<el-progress
:percentage="progress"
:show-text="false"
class="upload-progress"
:class="{ show: showProgress }"
:status="showError ? 'exception' : undefined"
/>
<div class="paste-style">
<div class="el-col-12">
<div class="paste-style__text">
{{ $T('LINK_FORMAT') }}
</div>
<el-switch
v-model="useShortUrl"
class="short-url-switch"
active-text=""
inactive-text=""
:active-value="true"
:inactive-value="false"
@change="handleUseShortUrlChange"
/>
<span class="switch-label">{{ useShortUrl ? $T('UPLOAD_SHORT_URL') : $T('UPLOAD_NORMAL_URL') }}</span>
</div>
<div class="option-content">
<el-radio-group
v-model="pasteStyle"
size="small"
class="format-radio-group"
@change="handlePasteStyleChange"
>
<el-radio-button
v-for="(item, key) in pasteFormatList"
:key="key"
:value="key"
:title="item"
class="format-radio-btn"
>
<el-radio-group v-model="pasteStyle" size="small" @change="handlePasteStyleChange">
<el-radio-button v-for="(item, key) in pasteFormatList" :key="key" :value="key" :title="item">
{{ key }}
</el-radio-button>
</el-radio-group>
<el-radio-group v-model="useShortUrl" size="small" @change="handleUseShortUrlChange">
<el-radio-button :value="true" style="border-radius: 5px">
{{ $T('UPLOAD_SHORT_URL') }}
</el-radio-button>
<el-radio-button :value="false" style="border-radius: 5px">
{{ $T('UPLOAD_NORMAL_URL') }}
</el-radio-button>
</el-radio-group>
</div>
</div>
<div class="option-card quick-upload-card">
<div class="option-label">
{{ $T('QUICK_UPLOAD') }}
</div>
<div class="option-content quick-upload-content">
<el-button type="primary" class="quick-upload-btn" @click="uploadClipboardFiles">
<el-icon><Picture /></el-icon>
<div class="el-col-8">
<div class="paste-style__text">
{{ $T('QUICK_UPLOAD') }}
</div>
<el-button
type="primary"
round
size="small"
class="quick-upload"
style="width: 50%"
@click="uploadClipboardFiles"
>
{{ $T('CLIPBOARD_PICTURE') }}
</el-button>
<el-button type="primary" class="quick-upload-btn" @click="uploadURLFiles">
<el-icon><Link /></el-icon>
<el-button
type="primary"
round
size="small"
class="quick-upload"
style="width: 46%; margin-left: 6px"
@click="uploadURLFiles"
>
URL
</el-button>
</div>
</div>
</div>
</div>
</el-col>
</el-row>
<el-dialog
v-model="imageProcessDialogVisible"
:title="$T('UPLOAD_PAGE_IMAGE_PROCESS_DIALOG_TITLE')"
@@ -119,11 +110,10 @@
<script lang="ts" setup>
import { ipcRenderer, IpcRendererEvent } from 'electron'
import { ElMessage as $message, ElSwitch } from 'element-plus'
import { UploadFilled, CaretBottom, Picture, Link, Edit } from '@element-plus/icons-vue'
import { ElMessage as $message } from 'element-plus'
import { UploadFilled, CaretBottom } from '@element-plus/icons-vue'
import { ref, onBeforeMount, onBeforeUnmount, watch } from 'vue'
import { useRouter } from 'vue-router'
import { useDebounceFn, useThrottleFn } from '@vueuse/core'
import ImageProcessSetting from '@/components/ImageProcessSetting.vue'
import { T as $T } from '@/i18n'
@@ -142,7 +132,6 @@ import { useDragEventListeners } from '@/utils/drag'
useDragEventListeners()
const $router = useRouter()
// State variables
const imageProcessDialogVisible = ref(false)
const useShortUrl = ref(false)
const dragover = ref(false)
@@ -165,36 +154,32 @@ watch(picBedGlobal, () => {
getDefaultPicBed()
})
watch(progress, onProgressChange)
onBeforeMount(async () => {
await updatePicBedGlobal()
ipcRenderer.on('uploadProgress', handleUploadProgress)
ipcRenderer.on('syncPicBed', getDefaultPicBed)
onBeforeMount(() => {
updatePicBedGlobal()
ipcRenderer.on('uploadProgress', (_event: IpcRendererEvent, _progress: number) => {
if (_progress !== -1) {
showProgress.value = true
progress.value = _progress
} else {
progress.value = 100
showError.value = true
}
})
getUseShortUrl()
getPasteStyle()
getDefaultPicBed()
ipcRenderer.on('syncPicBed', () => {
getDefaultPicBed()
})
$bus.on(SHOW_INPUT_BOX_RESPONSE, handleInputBoxValue)
await Promise.all([getUseShortUrl(), getPasteStyle(), getDefaultPicBed()])
})
onBeforeUnmount(() => {
$bus.off(SHOW_INPUT_BOX_RESPONSE)
ipcRenderer.removeAllListeners('uploadProgress')
ipcRenderer.removeAllListeners('syncPicBed')
})
const handleUploadProgress = useThrottleFn((_event: IpcRendererEvent, _progress: number) => {
if (_progress !== -1) {
showProgress.value = true
progress.value = _progress
} else {
progress.value = 100
showError.value = true
}
}, 50)
const handleImageProcess = () => {
imageProcessDialogVisible.value = true
}
watch(progress, onProgressChange)
function onProgressChange(val: number) {
if (val === 100) {
setTimeout(() => {
@@ -226,8 +211,16 @@ async function handlePicBedNameClick(_picBedName: string, picBedConfigName: stri
})
}
const onDrop = useDebounceFn((e: DragEvent) => {
onBeforeUnmount(() => {
$bus.off(SHOW_INPUT_BOX_RESPONSE)
ipcRenderer.removeAllListeners('uploadProgress')
ipcRenderer.removeAllListeners('syncPicBed')
})
function onDrop(e: DragEvent) {
dragover.value = false
// send files first
if (e.dataTransfer?.files?.length) {
ipcSendFiles(e.dataTransfer.files)
} else if (e.dataTransfer?.items) {
@@ -243,9 +236,11 @@ const onDrop = useDebounceFn((e: DragEvent) => {
}
}
}
}, 200)
}
function handleURLDrag(items: DataTransferItemList, dataTransfer: DataTransfer) {
// text/html
// Use this data to get a more precise URL
const urlString = dataTransfer.getData(items[1].type)
const urlMatch = urlString.match(/<img.*src="(.*?)"/)
if (urlMatch) {
@@ -349,372 +344,82 @@ export default {
</script>
<style lang="stylus">
.upload-container
.view-title
display flex
color #eee
font-size 20px
text-align center
margin 10px auto
align-items center
justify-content center
#upload-view-title
&:hover
cursor pointer
color #409EFF
#upload-view
position absolute
left 142px
right 0
height 100%
padding 0
transition all 0.3s ease
overflow-y auto
overflow-x hidden
display flex
align-items center
justify-content center
.container-inner
max-width 800px
width 100%
padding 20px
max-height 90vh // Limit maximum height
display flex
flex-direction column
justify-content center // Center content vertically
margin auto // Center the container
gap 20px // Add consistent spacing between elements
.view-title
display flex
color #f2f2f2
font-size 20px
text-align center
margin-bottom 0
align-items center
justify-content center
flex-wrap wrap
padding 12px
border-radius 10px
max-width 800px
@media (max-width: 640px), (max-height: 700px)
font-size 16px
padding 10px
.icon-button
margin-left 8px
transition transform 0.2s ease
&:hover
transform scale(1.1)
.quick-upload
margin-left 12px
display flex
align-items center
gap 4px
transition all 0.3s ease
&:hover
transform translateY(-2px)
box-shadow 0 4px 12px rgba(0, 0, 0, 0.2)
@media (max-width: 640px)
margin-top 8px
margin-left 0
#upload-view-title
transition all 0.2s ease
position relative
overflow hidden
display inline-block
&:after
content ''
position absolute
left 0
bottom -2px
width 0
height 2px
background #409EFF
transition width 0.3s ease
&:hover
cursor pointer
color #409EFF
&:after
width 100%
.upload-area
flex 1
min-height 250px
max-height 550px
border 2px dashed rgba(255, 255, 255, 0.2)
border-radius 16px
text-align center
width 100%
margin 0 auto
color #eee
cursor pointer
transition all 0.3s cubic-bezier(0.25, 0.1, 0.25, 1.0)
position relative
display flex
align-items center
justify-content center
backdrop-filter blur(5px)
background linear-gradient(135deg, rgba(50, 50, 50, 0.1) 0%, rgba(30, 30, 30, 0.2) 100%)
box-shadow 0 8px 20px rgba(0, 0, 0, 0.1)
@media (max-height: 600px)
min-height 180px
max-height 250px
@media (min-height: 800px)
min-height 350px
max-height 550px
&.is-dragover,
&:hover
border 2px dashed #409EFF
background-color rgba(64, 158, 255, 0.1)
transform scale(1.01)
box-shadow 0 10px 25px rgba(0, 0, 0, 0.15)
color #fff
.upload-overlay
position absolute
top 0
left 0
right 0
bottom 0
background rgba(0, 0, 0, 0.7)
display flex
flex-direction column
justify-content center
align-items center
z-index 10
border-radius 15px
backdrop-filter blur(5px)
.upload-status-text
margin-top 16px
color #ffffff
font-size 16px
font-weight 500
.upload-dragger
height 100%
width 100%
display flex
flex-direction column
justify-content center
align-items center
.upload-icon
font-size 55px
margin-bottom 20px
color rgba(255, 255, 255, 0.8)
transition all 0.3s ease
filter drop-shadow(0 4px 6px rgba(0, 0, 0, 0.1))
@media (max-height: 600px)
font-size 40px
margin-bottom 12px
&__text
font-size 16px
color rgba(255, 255, 255, 0.9)
margin-bottom 8px
max-width 80%
.view-title
margin 10vh auto 10px
#upload-area
height 50vh
border 2px dashed #dddddd
border-radius 8px
text-align center
line-height 1.5
@media (max-height: 600px)
font-size 14px
line-height 1.3
.upload-action-text
color #409EFF
font-weight 500
margin-left 6px
position relative
display inline-block
&:after
content ''
position absolute
left 0
bottom -2px
width 100%
height 1px
background #409EFF
#file-uploader
display none
.upload-options
display flex
justify-content space-between
gap 15px
margin-bottom 0
width 100%
max-width: 800px
@media (max-width: 768px)
flex-direction column
gap 12px
.option-card
background rgba(255, 255, 255, 0.02)
border-radius 12px
padding 12px
backdrop-filter blur(5px)
box-shadow 0 5px 15px rgba(0, 0, 0, 0.1)
transition all 0.3s ease
&:hover
transform translateY(-3px)
box-shadow 0 8px 20px rgba(0, 0, 0, 0.15)
@media (max-height: 600px)
padding 10px
.option-header
display flex
width 60vw
margin 0 auto
color #dddddd
cursor pointer
transition all .2s ease-in-out
align-items center
margin-bottom 12px
flex-wrap wrap
gap 8px
.option-label
font-size 14px
color #f2f2f2
font-weight 500
border-left 3px solid #409EFF
padding-left 10px
margin-right auto
@media (max-height: 600px)
font-size 13px
.option-content
display flex
flex-wrap wrap
gap 10px
.link-format-card
flex 3
min-width 0
max-width 580px
.quick-upload-card
flex 1
min-width 200px
max-width 280px
display flex
flex-direction column
.option-label
margin-bottom 12px
.short-url-switch
margin-left auto
.switch-label
font-size 12px
color #e0e0e0
white-space nowrap
.format-radio-group
display flex
flex-wrap wrap
gap 5px
width 100%
.format-radio-btn
flex-grow 1
min-width 0
.el-radio-button__inner
padding 8px 10px
width 100%
font-size 13px
border-radius 4px !important
white-space nowrap
overflow hidden
text-overflow ellipsis
box-shadow 0 2px 4px rgba(0, 0, 0, 0.1)
.quick-upload-content
display flex
gap 10px
flex 1
@media (max-width: 768px) and (min-width: 641px)
flex-direction column
gap 8px
@media (max-width: 640px)
flex-direction row
.quick-upload-btn
flex 1
padding 8px 12px
border-radius 8px
transition all 0.3s ease
display flex
align-items center
justify-content center
gap 5px
font-size 13px
height 40px
@media (max-width: 768px) and (min-width: 641px)
height 36px
&:hover
transform translateY(-2px)
box-shadow 0 4px 12px rgba(0, 0, 0, 0.2)
// Animations
.scale-fade-enter-active,
.scale-fade-leave-active
transition all 0.4s cubic-bezier(0.4, 0, 0.2, 1)
.scale-fade-enter-from,
.scale-fade-leave-to
opacity 0
transform scale(0.9)
// Radio button styling
.el-radio-button
transition all 0.2s ease
&__inner
border-radius 8px !important
box-shadow 0 2px 4px rgba(0, 0, 0, 0.1)
margin 0 2px
&:first-child
.el-radio-button__inner
border-radius 8px 0 0 8px !important
&:last-child
.el-radio-button__inner
border-radius 0 8px 8px 0 !important
// Custom scrollbar for the container
.upload-container::-webkit-scrollbar
width 5px
.upload-container::-webkit-scrollbar-thumb
background rgba(255, 255, 255, 0.2)
border-radius 5px
&:hover
background rgba(255, 255, 255, 0.3)
.upload-container::-webkit-scrollbar-track
background transparent
// Add responsive adjustments for larger screens
@media (min-height: 900px)
.container-inner
gap 30px // Increase gap for larger screens
#upload-dragger
height 100%
item-align center
&.is-dragover,
&:hover
border 2px dashed #A4D8FA
background-color rgba(164, 216, 250, 0.3)
color #fff
i
height 80%
font-size 10vh
margin 0
span
color #409EFF
#file-uploader
display none
.upload-progress
opacity 0
transition all .2s ease-in-out
width 450px
margin 20px auto 0
&.show
opacity 1
.el-progress-bar__inner
transition all .2s ease-in-out
.paste-style
justify-content center
.upload-area
flex 1 1 auto
min-height 400px
.view-title, .upload-options
flex-shrink 0 // Prevent these elements from shrinking
text-align center
margin-top 16px
display flex
align-items flex-end
&__text
font-size 12px
color #eeeeee
margin-bottom 4px
.el-radio-button:first-child
.el-radio-button__inner
border-left none
.el-radio-button:first-child
.el-radio-button__inner
border-left none
border-radius 14px 0 0 14px
.el-radio-button:last-child
.el-radio-button__inner
border-left none
border-radius 0 14px 14px 0
.el-icon-caret-bottom
cursor pointer
</style>

View File

@@ -3,61 +3,60 @@
<div class="view-title">
{{ $T('SETTINGS') }}
</div>
<div class="config-list-container">
<el-row :gutter="20" justify="start" class="config-list">
<el-col
v-for="item in curConfigList"
:key="item._id"
class="config-item-col"
:xs="24"
:sm="curConfigList.length === 1 ? 24 : 12"
:md="curConfigList.length === 1 ? 24 : 12"
:lg="curConfigList.length === 1 ? 12 : 8"
:xl="curConfigList.length === 1 ? 12 : 6"
<el-row :gutter="15" justify="space-between" align="middle" type="flex" class="config-list">
<el-col
v-for="item in curConfigList"
:key="item._id"
class="config-item-col"
:xs="24"
:sm="curConfigList.length === 1 ? 24 : 12"
:md="curConfigList.length === 1 ? 24 : 12"
:lg="curConfigList.length === 1 ? 12 : 6"
:xl="curConfigList.length === 1 ? 12 : 3"
>
<div
:class="`config-item ${defaultConfigId === item._id ? 'selected' : ''}`"
@click="() => selectItem(item._id)"
>
<div
:class="`config-card ${defaultConfigId === item._id ? 'selected' : ''}`"
@click="() => selectItem(item._id)"
>
<div class="config-card-content">
<div class="config-name">{{ item._configName }}</div>
<div class="config-update-time">{{ formatTime(item._updatedAt) }}</div>
</div>
<div v-if="defaultConfigId === item._id" class="default-badge">
{{ $T('SELECTED_SETTING_HINT') }}
</div>
<div class="config-card-actions">
<el-button class="action-btn edit-btn" circle type="primary" @click.stop="openEditPage(item._id)">
<el-icon><Edit /></el-icon>
</el-button>
<el-button
class="action-btn delete-btn"
circle
type="danger"
:disabled="curConfigList.length <= 1"
@click.stop="() => deleteConfig(item._id)"
>
<el-icon><Delete /></el-icon>
</el-button>
</div>
<div class="config-name">
{{ item._configName }}
</div>
</el-col>
<el-col
class="config-item-col"
:xs="24"
:sm="curConfigList.length === 1 ? 24 : 12"
:md="curConfigList.length === 1 ? 24 : 12"
:lg="curConfigList.length === 1 ? 12 : 8"
:xl="curConfigList.length === 1 ? 12 : 6"
>
<div class="config-card add-card" @click="addNewConfig">
<el-icon class="add-icon"><Plus /></el-icon>
<span class="add-text">{{ $T('SETTINGS') }}</span>
<div class="config-update-time">
{{ formatTime(item._updatedAt) }}
</div>
</el-col>
</el-row>
</div>
<div class="set-default-container">
<div v-if="defaultConfigId === item._id" class="default-text">
{{ $T('SELECTED_SETTING_HINT') }}
</div>
<div class="operation-container">
<el-icon class="el-icon-edit" @click="openEditPage(item._id)">
<Edit />
</el-icon>
<el-icon
class="el-icon-delete"
:class="curConfigList.length <= 1 ? 'disabled' : ''"
@click.stop="() => deleteConfig(item._id)"
>
<Delete />
</el-icon>
</div>
</div>
</el-col>
<el-col
class="config-item-col"
:xs="24"
:sm="curConfigList.length === 1 ? 24 : 12"
:md="curConfigList.length === 1 ? 24 : 12"
:lg="curConfigList.length === 1 ? 12 : 6"
:xl="curConfigList.length === 1 ? 12 : 3"
>
<div class="config-item config-item-add" @click="addNewConfig">
<el-icon class="el-icon-plus">
<Plus />
</el-icon>
</div>
</el-col>
</el-row>
<el-row type="flex" justify="center" :span="24" class="set-default-container">
<el-button
class="set-default-btn"
type="success"
@@ -67,7 +66,7 @@
>
{{ $T('SETTINGS_SET_DEFAULT_PICBED') }}
</el-button>
</div>
</el-row>
</div>
</template>
@@ -181,15 +180,6 @@ export default {
}
</script>
<style lang="stylus">
$transitionDefault = all 0.25s cubic-bezier(0.4, 0, 0.2, 1)
$borderRadius = 8px
$cardBg = rgba(55, 60, 70, 0.55)
$hoverBg = rgba(50, 54, 62, 0.7)
$selectedBorder = #409EFF
$cardShadow = 0 3px 8px rgba(0, 0, 0, 0.25)
$cardShadowHover = 0 5px 15px rgba(0, 0, 0, 0.35)
$normalBorder = rgba(150, 150, 150, 0.25)
#config-list-view
position absolute
min-height 100%
@@ -197,160 +187,72 @@ $normalBorder = rgba(150, 150, 150, 0.25)
right 0
overflow-x hidden
overflow-y auto
padding 0 20px 90px
padding-bottom 50px
box-sizing border-box
display flex
flex-direction column
.view-title
color #eee
font-size 24px
font-weight 500
margin 20px 0
text-align center
letter-spacing 0.5px
.config-list-container
flex 1
width 100%
box-sizing border-box
.config-list
margin 0
width 100%
.config-item-col
margin-bottom 20px
.config-card
position relative
height 110px
border-radius $borderRadius
background $cardBg
backdrop-filter blur(5px)
box-shadow $cardShadow
transition $transitionDefault
cursor pointer
overflow hidden
display flex
align-items center
justify-content space-between
padding 0 15px 0 20px
border 1px solid $normalBorder
&:hover
transform translateY(-3px)
box-shadow $cardShadowHover
background $hoverBg
&.selected
border 1px solid $selectedBorder
background rgba(64, 158, 255, 0.15) // Slightly more visible selected state
.default-badge
position absolute
right 0
top 0
font-size 11px
font-weight 600
padding 4px 10px
background $selectedBorder
color #fff
z-index 5
border-bottom-left-radius $borderRadius
.config-card-content
flex-grow 1
padding 15px 0
overflow hidden
flex-wrap wrap
width: 98%
.config-item
height 85px
margin-bottom 20px
border-radius 4px
cursor pointer
box-sizing border-box
padding 8px
background rgba(130, 130, 130, .2)
border 1px solid transparent
box-shadow 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04)
position relative
.config-name
color #fff
color #eee
font-size 16px
font-weight 500
margin-bottom 10px
white-space nowrap
overflow hidden
text-overflow ellipsis
.config-update-time
color #aaa
font-size 14px
.config-card-actions
display flex
flex-direction row
gap 10px
align-items center
.action-btn
transition $transitionDefault
border none
width 32px
height 32px
margin-top 10px
.default-text
color #67C23A
font-size 12px
margin-top 5px
.operation-container
position absolute
right 5px
top 8px
left 0
font-size 18pxc
word-break break-all
display flex
align-items center
justify-content center
&.edit-btn
&:hover
transform scale(1.1)
&.delete-btn
&:hover:not(:disabled)
transform scale(1.1)
&:disabled
opacity 0.5
cursor not-allowed
.add-card
display flex
flex-direction column
align-items center
justify-content center
background rgba(50, 54, 62, 0.4)
border 1px dashed rgba(255, 255, 255, 0.4)
&:hover
border-color rgba(255, 255, 255, 0.8)
background rgba(64, 158, 255, 0.15)
.add-icon
transform rotate(90deg)
.add-icon
font-size 32px
color #eee
margin-bottom 8px
transition $transitionDefault
.add-text
color #eee
font-size 14px
color #eee
.el-icon-edit
right 20px
position absolute
top 2px
margin-right 10px
cursor pointer
.el-icon-delete
position absolute
top 2px
margin-right 10px
right 0
cursor pointer
.el-icon-edit
margin-right 10px
.disabled
cursor not-allowed
color #aaa
.config-item-add
display: flex
justify-content: center
align-items: center
color: #eee
font-size: 28px
.selected
border 1px solid #409EFF
.set-default-container
position fixed
bottom 0
left 162px
right 0
display flex
justify-content center
align-items center
padding 15px 0
height 70px
background linear-gradient(to top, rgba(30, 34, 42, 0.95) 0%, rgba(30, 34, 42, 0.8) 60%, rgba(30, 34, 42, 0) 100%)
z-index 10
position absolute
bottom 10px
width 100%
.set-default-btn
width 250px
height 40px
font-weight 500
transition $transitionDefault
margin-bottom 0
&:not(:disabled):hover
transform translateY(-2px)
box-shadow 0 4px 12px rgba(0, 0, 0, 0.3)
</style>