mirror of
https://github.com/Kuingsmile/PicList.git
synced 2026-06-29 03:21:47 +08:00
✨ Feature(custom): optimize image process setting page
This commit is contained in:
@@ -53,21 +53,31 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex justify-end gap-1.5">
|
<div class="flex justify-end gap-1.5">
|
||||||
<button v-if="currentStep > 0" class="guide-btn secondary" @click="handlePrevious">
|
<CustomButton
|
||||||
<ChevronLeftIcon :size="16" />
|
v-if="currentStep > 0"
|
||||||
{{ t('guide.previous') }}
|
type="secondary"
|
||||||
</button>
|
:icon="ChevronLeftIcon"
|
||||||
<button class="guide-btn outline" @click="handleSkip">
|
:text="t('guide.previous')"
|
||||||
{{ t('guide.skip') }}
|
class="p-2!"
|
||||||
</button>
|
@click="handlePrevious"
|
||||||
<button v-if="currentStep < steps.length - 1" class="guide-btn primary" @click="handleNext">
|
/>
|
||||||
{{ t('guide.next') }}
|
<CustomButton class="p-2!" type="secondary" :text="t('guide.skip')" @click="handleSkip" />
|
||||||
<ChevronRightIcon :size="16" />
|
<CustomButton
|
||||||
</button>
|
v-if="currentStep < steps.length - 1"
|
||||||
<button v-else class="guide-btn success" @click="handleFinish">
|
type="primary"
|
||||||
<CheckCircleIcon :size="16" />
|
class="p-2!"
|
||||||
{{ t('guide.finish') }}
|
:icon="ChevronRightIcon"
|
||||||
</button>
|
:text="t('guide.next')"
|
||||||
|
@click="handleNext"
|
||||||
|
/>
|
||||||
|
<CustomButton
|
||||||
|
v-else
|
||||||
|
type="primary"
|
||||||
|
class="p-2!"
|
||||||
|
:icon="CheckCircleIcon"
|
||||||
|
:text="t('guide.finish')"
|
||||||
|
@click="handleFinish"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -93,6 +103,8 @@ import { computed, nextTick, onMounted, ref, watch } from 'vue'
|
|||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
|
||||||
|
import CustomButton from '@/components/common/CustomButton.vue'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -363,5 +375,3 @@ onMounted(async () => {
|
|||||||
window.addEventListener('resize', updateSpotlight)
|
window.addEventListener('resize', updateSpotlight)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped src="./css/FirstTimeGuide.css"></style>
|
|
||||||
|
|||||||
@@ -33,22 +33,13 @@
|
|||||||
>
|
>
|
||||||
<!-- General Settings Tab -->
|
<!-- General Settings Tab -->
|
||||||
<div v-if="activeTab === 'general'" key="general" class="flex flex-col gap-4">
|
<div v-if="activeTab === 'general'" key="general" class="flex flex-col gap-4">
|
||||||
<div class="settings-section">
|
<SettingSection :icon="Sliders" :title="t('pages.imageProcess.general.basicImageProcessing')">
|
||||||
<div class="section-header">
|
<SettingCard p1 class="flex flex-col justify-center">
|
||||||
<div class="section-icon">
|
<CustomSwitch
|
||||||
<Sliders :size="20" />
|
|
||||||
</div>
|
|
||||||
<div class="section-title-group">
|
|
||||||
<h2>{{ $t('pages.imageProcess.general.basicImageProcessing') }}</h2>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-grid">
|
|
||||||
<div class="form-group">
|
|
||||||
<customSwitch
|
|
||||||
v-model="activeForm.compress.isRemoveExif"
|
v-model="activeForm.compress.isRemoveExif"
|
||||||
:title="$t('pages.imageProcess.general.isRemoveExif')"
|
:title="t('pages.imageProcess.general.isRemoveExif')"
|
||||||
class="custom-switch"
|
small
|
||||||
|
no-border
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<PerPicbedSetting
|
<PerPicbedSetting
|
||||||
@@ -69,12 +60,11 @@
|
|||||||
)
|
)
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</div>
|
</SettingCard>
|
||||||
|
<SettingCard class="flex flex-col justify-center">
|
||||||
<div class="form-group">
|
<CustomRange
|
||||||
<customRange
|
|
||||||
v-model.number="activeForm.compress.quality"
|
v-model.number="activeForm.compress.quality"
|
||||||
:title="$t('pages.imageProcess.general.quality')"
|
:title="t('pages.imageProcess.general.quality')"
|
||||||
:min="1"
|
:min="1"
|
||||||
:max="100"
|
:max="100"
|
||||||
:step="1"
|
:step="1"
|
||||||
@@ -97,25 +87,16 @@
|
|||||||
safeSetMapValue(compressForm, 'quality', picbedType, value, defaultCompressSetting.quality)
|
safeSetMapValue(compressForm, 'quality', picbedType, value, defaultCompressSetting.quality)
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</div>
|
</SettingCard>
|
||||||
</div>
|
</SettingSection>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="settings-section">
|
<SettingSection :icon="RefreshCw" :title="t('pages.imageProcess.general.formatConversion')">
|
||||||
<div class="section-header">
|
<SettingCard class="flex flex-col justify-center">
|
||||||
<div class="section-icon">
|
<CustomSwitch
|
||||||
<RefreshCw :size="20" />
|
|
||||||
</div>
|
|
||||||
<div class="section-title-group">
|
|
||||||
<h2>{{ $t('pages.imageProcess.general.formatConversion') }}</h2>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<customSwitch
|
|
||||||
v-model="activeForm.compress.isConvert"
|
v-model="activeForm.compress.isConvert"
|
||||||
:title="$t('pages.imageProcess.general.isConvert')"
|
:title="t('pages.imageProcess.general.isConvert')"
|
||||||
class="custom-switch"
|
no-border
|
||||||
|
small
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<PerPicbedSetting
|
<PerPicbedSetting
|
||||||
@@ -130,11 +111,12 @@
|
|||||||
safeSetMapValue(compressForm, 'isConvert', picbedType, value, defaultCompressSetting.isConvert)
|
safeSetMapValue(compressForm, 'isConvert', picbedType, value, defaultCompressSetting.isConvert)
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</div>
|
</SettingCard>
|
||||||
|
|
||||||
<div v-if="activeForm.compress.isConvert" class="form-grid">
|
<SettingCard v-if="activeForm.compress.isConvert">
|
||||||
<div class="form-group">
|
<label class="text-base font-semibold text-main">{{
|
||||||
<label class="title-text">{{ $t('pages.imageProcess.general.destinationFormat') }}</label>
|
t('pages.imageProcess.general.destinationFormat')
|
||||||
|
}}</label>
|
||||||
<select v-model="activeForm.compress.convertFormat" class="form-input">
|
<select v-model="activeForm.compress.convertFormat" class="form-input">
|
||||||
<option v-for="format in availableFormat" :key="format" :value="format">
|
<option v-for="format in availableFormat" :key="format" :value="format">
|
||||||
{{ format.toUpperCase() }}
|
{{ format.toUpperCase() }}
|
||||||
@@ -160,10 +142,12 @@
|
|||||||
)
|
)
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</div>
|
</SettingCard>
|
||||||
|
|
||||||
<div class="form-group">
|
<SettingCard v-if="activeForm.compress.isConvert">
|
||||||
<label class="title-text">{{ $t('pages.imageProcess.general.specificFormatConversion') }}</label>
|
<label class="text-base font-semibold text-main">{{
|
||||||
|
t('pages.imageProcess.general.specificFormatConversion')
|
||||||
|
}}</label>
|
||||||
<textarea
|
<textarea
|
||||||
v-model="convertStr"
|
v-model="convertStr"
|
||||||
class="form-textarea"
|
class="form-textarea"
|
||||||
@@ -190,29 +174,23 @@
|
|||||||
)
|
)
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</div>
|
</SettingCard>
|
||||||
</div>
|
</SettingSection>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Watermark Tab -->
|
<!-- Watermark Tab -->
|
||||||
<div v-else-if="activeTab === 'watermark'" key="watermark" class="flex flex-col gap-4">
|
<div v-else-if="activeTab === 'watermark'" key="watermark" class="flex flex-col gap-4">
|
||||||
<div class="settings-section">
|
<SettingSection
|
||||||
<div class="section-header">
|
:icon="Droplets"
|
||||||
<div class="section-icon watermark-icon">
|
:title="t('pages.imageProcess.watermark.title')"
|
||||||
<Droplets :size="20" />
|
:description="t('pages.imageProcess.watermark.description')"
|
||||||
</div>
|
>
|
||||||
<div class="section-title-group">
|
<SettingCard class="flex flex-col justify-center">
|
||||||
<h2>{{ $t('pages.imageProcess.watermark.title') }}</h2>
|
<CustomSwitch
|
||||||
<p>{{ $t('pages.imageProcess.watermark.description') }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<customSwitch
|
|
||||||
v-model="activeForm.watermark.isAddWatermark"
|
v-model="activeForm.watermark.isAddWatermark"
|
||||||
:title="$t('pages.imageProcess.watermark.isAdd')"
|
:title="t('pages.imageProcess.watermark.isAdd')"
|
||||||
class="custom-switch"
|
small
|
||||||
|
no-border
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<PerPicbedSetting
|
<PerPicbedSetting
|
||||||
@@ -233,24 +211,20 @@
|
|||||||
)
|
)
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</div>
|
</SettingCard>
|
||||||
|
|
||||||
<div
|
<SettingCard v-if="activeForm.watermark.isAddWatermark">
|
||||||
v-if="activeForm.watermark.isAddWatermark"
|
<label class="text-base font-semibold text-main">{{ t('pages.imageProcess.watermark.type') }}</label>
|
||||||
class="mt-4 border-t border-t-border pt-3 transition-all duration-200 ease-apple"
|
|
||||||
>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="title-text">{{ $t('pages.imageProcess.watermark.type') }}</label>
|
|
||||||
<div class="flex flex-wrap gap-4">
|
<div class="flex flex-wrap gap-4">
|
||||||
<customRadioOption
|
<CustomRadioOption
|
||||||
v-model="activeForm.watermark.watermarkType"
|
v-model="activeForm.watermark.watermarkType"
|
||||||
value="text"
|
value="text"
|
||||||
:title="$t('pages.imageProcess.watermark.text')"
|
:title="t('pages.imageProcess.watermark.text')"
|
||||||
/>
|
/>
|
||||||
<customRadioOption
|
<CustomRadioOption
|
||||||
v-model="activeForm.watermark.watermarkType"
|
v-model="activeForm.watermark.watermarkType"
|
||||||
value="image"
|
value="image"
|
||||||
:title="$t('pages.imageProcess.watermark.image')"
|
:title="t('pages.imageProcess.watermark.image')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -262,8 +236,8 @@
|
|||||||
:global-value="waterMarkForm.watermarkType"
|
:global-value="waterMarkForm.watermarkType"
|
||||||
input-type="radio"
|
input-type="radio"
|
||||||
:radio-options="[
|
:radio-options="[
|
||||||
{ value: 'text', label: $t('pages.imageProcess.watermark.text') },
|
{ value: 'text', label: t('pages.imageProcess.watermark.text') },
|
||||||
{ value: 'image', label: $t('pages.imageProcess.watermark.image') },
|
{ value: 'image', label: t('pages.imageProcess.watermark.image') },
|
||||||
]"
|
]"
|
||||||
@map-change="
|
@map-change="
|
||||||
(picbedType, value) =>
|
(picbedType, value) =>
|
||||||
@@ -276,14 +250,14 @@
|
|||||||
)
|
)
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</div>
|
</SettingCard>
|
||||||
|
|
||||||
<div class="form-grid">
|
<SettingCard v-if="activeForm.watermark.isAddWatermark" p1 class="flex flex-col justify-center">
|
||||||
<div class="form-group">
|
<CustomSwitch
|
||||||
<customSwitch
|
|
||||||
v-model="activeForm.watermark.isFullScreenWatermark"
|
v-model="activeForm.watermark.isFullScreenWatermark"
|
||||||
:title="$t('pages.imageProcess.watermark.isFullScreen')"
|
:title="t('pages.imageProcess.watermark.isFullScreen')"
|
||||||
class="custom-switch"
|
small
|
||||||
|
no-border
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<PerPicbedSetting
|
<PerPicbedSetting
|
||||||
@@ -304,12 +278,12 @@
|
|||||||
)
|
)
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</div>
|
</SettingCard>
|
||||||
|
|
||||||
<div class="form-group">
|
<SettingCard v-if="activeForm.watermark.isAddWatermark">
|
||||||
<customRange
|
<CustomRange
|
||||||
v-model.number="activeForm.watermark.watermarkDegree"
|
v-model.number="activeForm.watermark.watermarkDegree"
|
||||||
:title="$t('pages.imageProcess.watermark.degree')"
|
:title="t('pages.imageProcess.watermark.degree')"
|
||||||
:min="-360"
|
:min="-360"
|
||||||
:max="360"
|
:max="360"
|
||||||
:step="1"
|
:step="1"
|
||||||
@@ -338,12 +312,12 @@
|
|||||||
)
|
)
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</div>
|
</SettingCard>
|
||||||
|
|
||||||
<div class="form-group">
|
<SettingCard v-if="activeForm.watermark.isAddWatermark">
|
||||||
<customRange
|
<CustomRange
|
||||||
v-model.number="activeForm.watermark.watermarkScaleRatio"
|
v-model.number="activeForm.watermark.watermarkScaleRatio"
|
||||||
:title="$t('pages.imageProcess.watermark.scaleRatio')"
|
:title="t('pages.imageProcess.watermark.scaleRatio')"
|
||||||
:min="0"
|
:min="0"
|
||||||
:max="1"
|
:max="1"
|
||||||
:step="0.01"
|
:step="0.01"
|
||||||
@@ -372,17 +346,18 @@
|
|||||||
)
|
)
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</div>
|
</SettingCard>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="activeForm.watermark.watermarkType === 'text'" class="form-grid">
|
<SettingCard
|
||||||
<div class="form-group">
|
v-if="activeForm.watermark.watermarkType === 'text' && activeForm.watermark.isAddWatermark"
|
||||||
<label class="title-text">{{ $t('pages.imageProcess.watermark.inputText') }}</label>
|
class="flex flex-col justify-center"
|
||||||
|
>
|
||||||
|
<label class="text-base font-semibold text-main">{{ t('pages.imageProcess.watermark.inputText') }}</label>
|
||||||
<input
|
<input
|
||||||
v-model="activeForm.watermark.watermarkText"
|
v-model="activeForm.watermark.watermarkText"
|
||||||
type="text"
|
type="text"
|
||||||
class="form-input"
|
class="form-input"
|
||||||
:placeholder="$t('pages.imageProcess.watermark.inputTextPlaceholder')"
|
:placeholder="t('pages.imageProcess.watermark.inputTextPlaceholder')"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- Per-picbed settings for watermarkText -->
|
<!-- Per-picbed settings for watermarkText -->
|
||||||
@@ -393,7 +368,7 @@
|
|||||||
field-name="watermarkText"
|
field-name="watermarkText"
|
||||||
:global-value="waterMarkForm.watermarkText"
|
:global-value="waterMarkForm.watermarkText"
|
||||||
input-type="text"
|
input-type="text"
|
||||||
:text-placeholder="$t('pages.imageProcess.watermark.inputTextPlaceholder')"
|
:text-placeholder="t('pages.imageProcess.watermark.inputTextPlaceholder')"
|
||||||
@map-change="
|
@map-change="
|
||||||
(picbedType, value) =>
|
(picbedType, value) =>
|
||||||
safeSetMapValue(
|
safeSetMapValue(
|
||||||
@@ -405,15 +380,20 @@
|
|||||||
)
|
)
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</div>
|
</SettingCard>
|
||||||
|
|
||||||
<div class="form-group">
|
<SettingCard
|
||||||
<label class="title-text">{{ $t('pages.imageProcess.watermark.textFontPath') }}</label>
|
v-if="activeForm.watermark.watermarkType === 'text' && activeForm.watermark.isAddWatermark"
|
||||||
|
class="flex flex-col justify-center"
|
||||||
|
>
|
||||||
|
<label class="text-base font-semibold text-main">{{
|
||||||
|
t('pages.imageProcess.watermark.textFontPath')
|
||||||
|
}}</label>
|
||||||
<input
|
<input
|
||||||
v-model="activeForm.watermark.watermarkFontPath"
|
v-model="activeForm.watermark.watermarkFontPath"
|
||||||
type="text"
|
type="text"
|
||||||
class="form-input"
|
class="form-input"
|
||||||
:placeholder="$t('pages.imageProcess.watermark.textFontPathPlaceholder')"
|
:placeholder="t('pages.imageProcess.watermark.textFontPathPlaceholder')"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<PerPicbedSetting
|
<PerPicbedSetting
|
||||||
@@ -423,7 +403,7 @@
|
|||||||
field-name="watermarkFontPath"
|
field-name="watermarkFontPath"
|
||||||
:global-value="waterMarkForm.watermarkFontPath"
|
:global-value="waterMarkForm.watermarkFontPath"
|
||||||
input-type="text"
|
input-type="text"
|
||||||
:text-placeholder="$t('pages.imageProcess.watermark.textFontPathPlaceholder')"
|
:text-placeholder="t('pages.imageProcess.watermark.textFontPathPlaceholder')"
|
||||||
@map-change="
|
@map-change="
|
||||||
(picbedType, value) =>
|
(picbedType, value) =>
|
||||||
safeSetMapValue(
|
safeSetMapValue(
|
||||||
@@ -435,10 +415,13 @@
|
|||||||
)
|
)
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</div>
|
</SettingCard>
|
||||||
|
|
||||||
<div class="form-group">
|
<SettingCard
|
||||||
<label class="title-text">{{ $t('pages.imageProcess.watermark.color') }}</label>
|
v-if="activeForm.watermark.watermarkType === 'text' && activeForm.watermark.isAddWatermark"
|
||||||
|
class="flex flex-col justify-center"
|
||||||
|
>
|
||||||
|
<label class="text-base font-semibold text-main">{{ t('pages.imageProcess.watermark.color') }}</label>
|
||||||
<div class="flex flex-wrap items-center gap-2">
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
<input
|
<input
|
||||||
v-model="activeForm.watermark.watermarkColor"
|
v-model="activeForm.watermark.watermarkColor"
|
||||||
@@ -471,17 +454,19 @@
|
|||||||
)
|
)
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</div>
|
</SettingCard>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Image Watermark Settings -->
|
<!-- Image Watermark Settings -->
|
||||||
<div v-if="activeForm.watermark.watermarkType === 'image'" class="form-group">
|
<SettingCard
|
||||||
<label class="title-text">{{ $t('pages.imageProcess.watermark.imagePath') }}</label>
|
v-if="activeForm.watermark.watermarkType === 'image' && activeForm.watermark.isAddWatermark"
|
||||||
|
class="flex flex-col justify-center"
|
||||||
|
>
|
||||||
|
<label class="text-base font-semibold text-main">{{ t('pages.imageProcess.watermark.imagePath') }}</label>
|
||||||
<input
|
<input
|
||||||
v-model="activeForm.watermark.watermarkImagePath"
|
v-model="activeForm.watermark.watermarkImagePath"
|
||||||
type="text"
|
type="text"
|
||||||
class="form-input"
|
class="form-input"
|
||||||
:placeholder="$t('pages.imageProcess.watermark.imagePathPlaceholder')"
|
:placeholder="t('pages.imageProcess.watermark.imagePathPlaceholder')"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<PerPicbedSetting
|
<PerPicbedSetting
|
||||||
@@ -491,7 +476,7 @@
|
|||||||
field-name="watermarkImagePath"
|
field-name="watermarkImagePath"
|
||||||
:global-value="waterMarkForm.watermarkImagePath"
|
:global-value="waterMarkForm.watermarkImagePath"
|
||||||
input-type="text"
|
input-type="text"
|
||||||
:text-placeholder="$t('pages.imageProcess.watermark.imagePathPlaceholder')"
|
:text-placeholder="t('pages.imageProcess.watermark.imagePathPlaceholder')"
|
||||||
@map-change="
|
@map-change="
|
||||||
(picbedType, value) =>
|
(picbedType, value) =>
|
||||||
safeSetMapValue(
|
safeSetMapValue(
|
||||||
@@ -503,12 +488,15 @@
|
|||||||
)
|
)
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</div>
|
</SettingCard>
|
||||||
|
|
||||||
<div v-if="activeForm.watermark.watermarkType === 'image'" class="form-group">
|
<SettingCard
|
||||||
<customRange
|
v-if="activeForm.watermark.watermarkType === 'image' && activeForm.watermark.isAddWatermark"
|
||||||
|
class="flex flex-col justify-center"
|
||||||
|
>
|
||||||
|
<CustomRange
|
||||||
v-model.number="activeForm.watermark.watermarkImageOpacity"
|
v-model.number="activeForm.watermark.watermarkImageOpacity"
|
||||||
:title="$t('pages.imageProcess.watermark.imageOpacity')"
|
:title="t('pages.imageProcess.watermark.imageOpacity')"
|
||||||
:min="0"
|
:min="0"
|
||||||
:max="255"
|
:max="255"
|
||||||
:step="1"
|
:step="1"
|
||||||
@@ -536,10 +524,10 @@
|
|||||||
)
|
)
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</div>
|
</SettingCard>
|
||||||
|
|
||||||
<div class="form-group">
|
<SettingCard v-if="activeForm.watermark.isAddWatermark">
|
||||||
<label class="title-text">{{ $t('pages.imageProcess.watermark.position') }}</label>
|
<label class="text-base font-semibold text-main">{{ t('pages.imageProcess.watermark.position') }}</label>
|
||||||
<div class="grid max-w-[320px] grid-cols-3 gap-2.5">
|
<div class="grid max-w-[320px] grid-cols-3 gap-2.5">
|
||||||
<button
|
<button
|
||||||
v-for="[key, label] in waterMarkPositionMap"
|
v-for="[key, label] in waterMarkPositionMap"
|
||||||
@@ -577,30 +565,24 @@
|
|||||||
)
|
)
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</div>
|
</SettingCard>
|
||||||
</div>
|
</SettingSection>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Transform Tab -->
|
<!-- Transform Tab -->
|
||||||
<div v-else-if="activeTab === 'transform'" key="transform" class="flex flex-col gap-4">
|
<div v-else-if="activeTab === 'transform'" key="transform" class="flex flex-col gap-4">
|
||||||
<div class="settings-section">
|
<SettingSection
|
||||||
<div class="section-header">
|
:icon="FlipHorizontal"
|
||||||
<div class="section-icon transform-icon">
|
:title="t('pages.imageProcess.transform.title')"
|
||||||
<FlipHorizontal :size="20" />
|
:description="t('pages.imageProcess.transform.description')"
|
||||||
</div>
|
>
|
||||||
<div class="section-title-group">
|
<SettingCard>
|
||||||
<h2>{{ $t('pages.imageProcess.transform.title') }}</h2>
|
<CustomSwitch
|
||||||
<p>{{ $t('pages.imageProcess.transform.description') }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-grid">
|
|
||||||
<div class="form-group">
|
|
||||||
<customSwitch
|
|
||||||
v-model="activeForm.compress.isFlip"
|
v-model="activeForm.compress.isFlip"
|
||||||
:title="$t('pages.imageProcess.transform.isFlip')"
|
:title="t('pages.imageProcess.transform.isFlip')"
|
||||||
class="custom-switch"
|
class="custom-switch"
|
||||||
|
no-border
|
||||||
|
small
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<PerPicbedSetting
|
<PerPicbedSetting
|
||||||
@@ -615,13 +597,15 @@
|
|||||||
safeSetMapValue(compressForm, 'isFlip', picbedType, value, defaultCompressSetting.isFlip)
|
safeSetMapValue(compressForm, 'isFlip', picbedType, value, defaultCompressSetting.isFlip)
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</div>
|
</SettingCard>
|
||||||
|
|
||||||
<div class="form-group">
|
<SettingCard>
|
||||||
<customSwitch
|
<CustomSwitch
|
||||||
v-model="activeForm.compress.isFlop"
|
v-model="activeForm.compress.isFlop"
|
||||||
:title="$t('pages.imageProcess.transform.isFlop')"
|
:title="t('pages.imageProcess.transform.isFlop')"
|
||||||
class="custom-switch"
|
class="custom-switch"
|
||||||
|
small
|
||||||
|
no-border
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<PerPicbedSetting
|
<PerPicbedSetting
|
||||||
@@ -636,26 +620,21 @@
|
|||||||
safeSetMapValue(compressForm, 'isFlop', picbedType, value, defaultCompressSetting.isFlop)
|
safeSetMapValue(compressForm, 'isFlop', picbedType, value, defaultCompressSetting.isFlop)
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</div>
|
</SettingCard>
|
||||||
</div>
|
</SettingSection>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="settings-section">
|
<SettingSection
|
||||||
<div class="section-header">
|
:icon="RotateCw"
|
||||||
<div class="section-icon rotate-icon">
|
:title="t('pages.imageProcess.transform.rotationTitle')"
|
||||||
<RotateCw :size="20" />
|
:description="t('pages.imageProcess.transform.rotationDescription')"
|
||||||
</div>
|
>
|
||||||
<div class="section-title-group">
|
<SettingCard class="flex flex-col justify-center">
|
||||||
<h2>{{ $t('pages.imageProcess.transform.rotationTitle') }}</h2>
|
<CustomSwitch
|
||||||
<p>{{ $t('pages.imageProcess.transform.rotationDescription') }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<customSwitch
|
|
||||||
v-model="activeForm.compress.isRotate"
|
v-model="activeForm.compress.isRotate"
|
||||||
:title="$t('pages.imageProcess.transform.isRotate')"
|
:title="t('pages.imageProcess.transform.isRotate')"
|
||||||
class="custom-switch"
|
class="custom-switch"
|
||||||
|
no-border
|
||||||
|
small
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<PerPicbedSetting
|
<PerPicbedSetting
|
||||||
@@ -670,12 +649,12 @@
|
|||||||
safeSetMapValue(compressForm, 'isRotate', picbedType, value, defaultCompressSetting.isRotate)
|
safeSetMapValue(compressForm, 'isRotate', picbedType, value, defaultCompressSetting.isRotate)
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</div>
|
</SettingCard>
|
||||||
|
|
||||||
<div v-if="activeForm.compress.isRotate" class="form-group">
|
<SettingCard v-if="activeForm.compress.isRotate" class="flex flex-col justify-center">
|
||||||
<customRange
|
<CustomRange
|
||||||
v-model.number="activeForm.compress.rotateDegree"
|
v-model.number="activeForm.compress.rotateDegree"
|
||||||
:title="$t('pages.imageProcess.transform.rotationDegree')"
|
:title="t('pages.imageProcess.transform.rotationDegree')"
|
||||||
:min="-360"
|
:min="-360"
|
||||||
:max="360"
|
:max="360"
|
||||||
:step="1"
|
:step="1"
|
||||||
@@ -704,25 +683,21 @@
|
|||||||
)
|
)
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</div>
|
</SettingCard>
|
||||||
</div>
|
</SettingSection>
|
||||||
|
|
||||||
<div class="settings-section">
|
<SettingSection
|
||||||
<div class="section-header">
|
:icon="Maximize2"
|
||||||
<div class="section-icon resize-icon">
|
:title="t('pages.imageProcess.transform.resizeTitle')"
|
||||||
<Maximize2 :size="20" />
|
:description="t('pages.imageProcess.transform.resizeDescription')"
|
||||||
</div>
|
>
|
||||||
<div class="section-title-group">
|
<SettingCard class="flex flex-col justify-center">
|
||||||
<h2>{{ $t('pages.imageProcess.transform.resizeTitle') }}</h2>
|
<CustomSwitch
|
||||||
<p>{{ $t('pages.imageProcess.transform.resizeDescription') }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<customSwitch
|
|
||||||
v-model="activeForm.compress.isReSize"
|
v-model="activeForm.compress.isReSize"
|
||||||
:title="$t('pages.imageProcess.transform.isResize')"
|
:title="t('pages.imageProcess.transform.isResize')"
|
||||||
class="custom-switch"
|
class="custom-switch"
|
||||||
|
no-border
|
||||||
|
small
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<PerPicbedSetting
|
<PerPicbedSetting
|
||||||
@@ -737,15 +712,12 @@
|
|||||||
safeSetMapValue(compressForm, 'isReSize', picbedType, value, defaultCompressSetting.isReSize)
|
safeSetMapValue(compressForm, 'isReSize', picbedType, value, defaultCompressSetting.isReSize)
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</div>
|
</SettingCard>
|
||||||
|
|
||||||
<div
|
<SettingCard v-if="activeForm.compress.isReSize">
|
||||||
v-if="activeForm.compress.isReSize"
|
<label class="text-base font-semibold text-main">{{
|
||||||
class="mt-4 border-t border-t-border pt-3 transition-all duration-200 ease-apple"
|
t('pages.imageProcess.transform.resizeWidth')
|
||||||
>
|
}}</label>
|
||||||
<div class="form-grid">
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="title-text">{{ $t('pages.imageProcess.transform.resizeWidth') }}</label>
|
|
||||||
<input v-model.number="activeForm.compress.reSizeWidth" type="number" min="0" class="form-input" />
|
<input v-model.number="activeForm.compress.reSizeWidth" type="number" min="0" class="form-input" />
|
||||||
|
|
||||||
<PerPicbedSetting
|
<PerPicbedSetting
|
||||||
@@ -759,19 +731,15 @@
|
|||||||
:number-max="10000"
|
:number-max="10000"
|
||||||
@map-change="
|
@map-change="
|
||||||
(picbedType, value) =>
|
(picbedType, value) =>
|
||||||
safeSetMapValue(
|
safeSetMapValue(compressForm, 'reSizeWidth', picbedType, value, defaultCompressSetting.reSizeWidth)
|
||||||
compressForm,
|
|
||||||
'reSizeWidth',
|
|
||||||
picbedType,
|
|
||||||
value,
|
|
||||||
defaultCompressSetting.reSizeWidth,
|
|
||||||
)
|
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</div>
|
</SettingCard>
|
||||||
|
|
||||||
<div class="form-group">
|
<SettingCard v-if="activeForm.compress.isReSize" class="flex flex-col justify-center">
|
||||||
<label class="title-text">{{ $t('pages.imageProcess.transform.resizeHeight') }}</label>
|
<label class="text-base font-semibold text-main">{{
|
||||||
|
t('pages.imageProcess.transform.resizeHeight')
|
||||||
|
}}</label>
|
||||||
<input v-model.number="activeForm.compress.reSizeHeight" type="number" min="0" class="form-input" />
|
<input v-model.number="activeForm.compress.reSizeHeight" type="number" min="0" class="form-input" />
|
||||||
|
|
||||||
<PerPicbedSetting
|
<PerPicbedSetting
|
||||||
@@ -794,20 +762,21 @@
|
|||||||
)
|
)
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</div>
|
</SettingCard>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
<SettingCard
|
||||||
v-if="
|
v-if="
|
||||||
((activeForm.compress.reSizeHeight || 0) > 0 && (activeForm.compress.reSizeWidth || 0) === 0) ||
|
((activeForm.compress.reSizeHeight || 0) > 0 && (activeForm.compress.reSizeWidth || 0) === 0) ||
|
||||||
((activeForm.compress.reSizeWidth || 0) > 0 && (activeForm.compress.reSizeHeight || 0) === 0)
|
((activeForm.compress.reSizeWidth || 0) > 0 && (activeForm.compress.reSizeHeight || 0) === 0)
|
||||||
"
|
"
|
||||||
class="form-group"
|
class="flex flex-col justify-center"
|
||||||
>
|
>
|
||||||
<customSwitch
|
<CustomSwitch
|
||||||
v-model="activeForm.compress.skipReSizeOfSmallImg"
|
v-model="activeForm.compress.skipReSizeOfSmallImg"
|
||||||
:title="$t('pages.imageProcess.transform.skipResizeOfSmallImgHeight')"
|
:title="t('pages.imageProcess.transform.skipResizeOfSmallImgHeight')"
|
||||||
class="custom-switch"
|
class="custom-switch"
|
||||||
|
no-border
|
||||||
|
small
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<PerPicbedSetting
|
<PerPicbedSetting
|
||||||
@@ -828,26 +797,17 @@
|
|||||||
)
|
)
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</div>
|
</SettingCard>
|
||||||
</div>
|
</SettingSection>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="settings-section">
|
<SettingSection :icon="Percent" :title="t('pages.imageProcess.transform.percentageResize')">
|
||||||
<div class="section-header">
|
<SettingCard class="flex flex-col justify-center">
|
||||||
<div class="section-icon percent-icon">
|
<CustomSwitch
|
||||||
<Percent :size="20" />
|
|
||||||
</div>
|
|
||||||
<div class="section-title-group">
|
|
||||||
<h2>{{ $t('pages.imageProcess.transform.percentageResize') }}</h2>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<customSwitch
|
|
||||||
v-model="activeForm.compress.isReSizeByPercent"
|
v-model="activeForm.compress.isReSizeByPercent"
|
||||||
:title="$t('pages.imageProcess.transform.isResizeByPercent')"
|
:title="t('pages.imageProcess.transform.isResizeByPercent')"
|
||||||
:description="$t('pages.imageProcess.transform.isResizeByPercentHint')"
|
:description="t('pages.imageProcess.transform.isResizeByPercentHint')"
|
||||||
class="custom-switch"
|
no-border
|
||||||
|
small
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<PerPicbedSetting
|
<PerPicbedSetting
|
||||||
@@ -868,12 +828,12 @@
|
|||||||
)
|
)
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</div>
|
</SettingCard>
|
||||||
|
|
||||||
<div v-if="activeForm.compress.isReSizeByPercent" class="form-group">
|
<SettingCard v-if="activeForm.compress.isReSizeByPercent" class="flex flex-col justify-center">
|
||||||
<customRange
|
<CustomRange
|
||||||
v-model.number="activeForm.compress.reSizePercent"
|
v-model.number="activeForm.compress.reSizePercent"
|
||||||
:title="$t('pages.imageProcess.transform.resizePercent')"
|
:title="t('pages.imageProcess.transform.resizePercent')"
|
||||||
:min="1"
|
:min="1"
|
||||||
:max="500"
|
:max="500"
|
||||||
:step="1"
|
:step="1"
|
||||||
@@ -902,22 +862,14 @@
|
|||||||
)
|
)
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</div>
|
</SettingCard>
|
||||||
</div>
|
</SettingSection>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Skip Process Tab -->
|
<!-- Skip Process Tab -->
|
||||||
<div v-else-if="activeTab === 'skipProcess'" key="skipProcess" class="flex flex-col gap-4">
|
<div v-else-if="activeTab === 'skipProcess'" key="skipProcess" class="flex flex-col gap-4">
|
||||||
<div class="settings-section">
|
<SettingSection only-one-row :icon="FileText" :title="t('pages.imageProcess.general.skipProcessExtList')">
|
||||||
<div class="section-header">
|
<SettingCard class="flex flex-col justify-center">
|
||||||
<div class="section-icon">
|
|
||||||
<FileText :size="20" />
|
|
||||||
</div>
|
|
||||||
<div class="section-title-group">
|
|
||||||
<h2>{{ $t('pages.imageProcess.general.skipProcessExtList') }}</h2>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<textarea
|
<textarea
|
||||||
v-model="activeForm.skipProcess.skipProcessExtList"
|
v-model="activeForm.skipProcess.skipProcessExtList"
|
||||||
class="form-textarea"
|
class="form-textarea"
|
||||||
@@ -925,47 +877,54 @@
|
|||||||
:placeholder="'zip,rar,7z,tar,gz'"
|
:placeholder="'zip,rar,7z,tar,gz'"
|
||||||
/>
|
/>
|
||||||
<small class="mt-2 block rounded-sm bg-bg-secondary px-3 py-2 text-xs leading-[1.5] text-tertiary">{{
|
<small class="mt-2 block rounded-sm bg-bg-secondary px-3 py-2 text-xs leading-[1.5] text-tertiary">{{
|
||||||
$t('pages.imageProcess.general.skipProcessExtListPlaceholder')
|
t('pages.imageProcess.general.skipProcessExtListPlaceholder')
|
||||||
}}</small>
|
}}</small>
|
||||||
</div>
|
</SettingCard>
|
||||||
</div>
|
</SettingSection>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Rename Tab -->
|
<!-- Rename Tab -->
|
||||||
<div v-else-if="activeTab === 'rename'" key="rename" class="flex flex-col gap-4">
|
<div v-else-if="activeTab === 'rename'" key="rename" class="flex flex-col gap-4">
|
||||||
<div class="settings-section">
|
<SettingSection
|
||||||
<div class="form-grid">
|
:icon="Edit2Icon"
|
||||||
<div class="form-group">
|
:title="t('pages.imageProcess.rename.title')"
|
||||||
<customSwitch
|
:description="t('pages.imageProcess.rename.description')"
|
||||||
|
only-one-row
|
||||||
|
>
|
||||||
|
<SettingSection>
|
||||||
|
<SettingCard p1>
|
||||||
|
<CustomSwitch
|
||||||
v-model="autoRenameComputed"
|
v-model="autoRenameComputed"
|
||||||
:title="$t('pages.imageProcess.rename.renameTimestamp')"
|
:title="t('pages.imageProcess.rename.renameTimestamp')"
|
||||||
description="YYYYMMDDHHmmssSSS"
|
description="YYYYMMDDHHmmssSSS"
|
||||||
class="custom-switch"
|
no-border
|
||||||
|
small
|
||||||
/>
|
/>
|
||||||
</div>
|
</SettingCard>
|
||||||
|
|
||||||
<div class="form-group">
|
<SettingCard p1 class="flex flex-col justify-center">
|
||||||
<customSwitch
|
<CustomSwitch
|
||||||
v-model="manualRenameComputed"
|
v-model="manualRenameComputed"
|
||||||
:title="$t('pages.imageProcess.rename.manualRename')"
|
:title="t('pages.imageProcess.rename.manualRename')"
|
||||||
class="custom-switch"
|
no-border
|
||||||
|
small
|
||||||
/>
|
/>
|
||||||
</div>
|
</SettingCard>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
<SettingCard p1 class="flex flex-col justify-center">
|
||||||
<customSwitch
|
<CustomSwitch
|
||||||
v-model="renameSettingsComputed.rename.enable"
|
v-model="renameSettingsComputed.rename.enable"
|
||||||
:title="$t('pages.settings.upload.enableAdvancedRname')"
|
:title="t('pages.settings.upload.enableAdvancedRname')"
|
||||||
:description="$t('pages.settings.upload.enableAdvancedRnameDesc')"
|
:description="t('pages.settings.upload.enableAdvancedRnameDesc')"
|
||||||
class="custom-switch"
|
no-border
|
||||||
|
small
|
||||||
/>
|
/>
|
||||||
</div>
|
</SettingCard>
|
||||||
|
|
||||||
<div class="form-group rename-format-field">
|
<SettingCard>
|
||||||
<label class="title-text mb-4 flex items-center gap-2">
|
<label class="mb-4 flex items-center gap-2 text-base font-semibold text-main">
|
||||||
<Edit :size="14" class="text-accent" />
|
<Edit :size="14" class="text-accent" />
|
||||||
{{ $t('pages.settings.upload.advancedRnameFormat') }}
|
{{ t('pages.settings.upload.advancedRnameFormat') }}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
v-model="renameSettingsComputed.rename.format"
|
v-model="renameSettingsComputed.rename.format"
|
||||||
@@ -973,13 +932,15 @@
|
|||||||
class="form-input"
|
class="form-input"
|
||||||
placeholder="Ex. {Y}-{m}-{uuid}"
|
placeholder="Ex. {Y}-{m}-{uuid}"
|
||||||
/>
|
/>
|
||||||
</div>
|
</SettingCard>
|
||||||
|
</SettingSection>
|
||||||
<div class="form-group">
|
<SettingCard>
|
||||||
<label class="title-text">{{ $t('pages.settings.upload.availablePlaceholders') }}</label>
|
<label class="text-base font-semibold text-main">{{
|
||||||
|
t('pages.settings.upload.availablePlaceholders')
|
||||||
|
}}</label>
|
||||||
<PlaceholderTable :list="advancedRenameList" :title-list="advancedRenameTitleList" />
|
<PlaceholderTable :list="advancedRenameList" :title-list="advancedRenameTitleList" />
|
||||||
</div>
|
</SettingCard>
|
||||||
</div>
|
</SettingSection>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
@@ -991,6 +952,7 @@ import { useStorage } from '@vueuse/core'
|
|||||||
import {
|
import {
|
||||||
Droplets,
|
Droplets,
|
||||||
Edit,
|
Edit,
|
||||||
|
Edit2Icon,
|
||||||
FileText,
|
FileText,
|
||||||
FlipHorizontal,
|
FlipHorizontal,
|
||||||
Image,
|
Image,
|
||||||
@@ -1011,15 +973,18 @@ import type {
|
|||||||
import { computed, nextTick, onBeforeMount, onBeforeUnmount, onMounted, ref, toRaw, useTemplateRef, watch } from 'vue'
|
import { computed, nextTick, onBeforeMount, onBeforeUnmount, onMounted, ref, toRaw, useTemplateRef, watch } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
import customRadioOption from '@/components/common/CustomRadioOption.vue'
|
import CustomRadioOption from '@/components/common/CustomRadioOption.vue'
|
||||||
import customRange from '@/components/common/CustomRange.vue'
|
import CustomRange from '@/components/common/CustomRange.vue'
|
||||||
import customSwitch from '@/components/common/CustomSwitch.vue'
|
import CustomSwitch from '@/components/common/CustomSwitch.vue'
|
||||||
import PlaceholderTable from '@/components/common/PlaceholderTable.vue'
|
import PlaceholderTable from '@/components/common/PlaceholderTable.vue'
|
||||||
import PerPicbedSetting from '@/components/PerPicbedSetting.vue'
|
import PerPicbedSetting from '@/components/PerPicbedSetting.vue'
|
||||||
import { getRawData } from '@/utils/common'
|
import { getRawData } from '@/utils/common'
|
||||||
import { configPaths } from '@/utils/configPaths'
|
import { configPaths } from '@/utils/configPaths'
|
||||||
import { getConfig, saveConfig } from '@/utils/dataSender'
|
import { getConfig, saveConfig } from '@/utils/dataSender'
|
||||||
|
|
||||||
|
import SettingCard from './common/SettingCard.vue'
|
||||||
|
import SettingSection from './common/SettingSection.vue'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const activeTab = useStorage<string>('image-process-setting-active-tab', 'general')
|
const activeTab = useStorage<string>('image-process-setting-active-tab', 'general')
|
||||||
|
|
||||||
|
|||||||
@@ -55,20 +55,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-wrap gap-3 p-4">
|
<div class="flex flex-wrap justify-center gap-3 p-2">
|
||||||
<button
|
<CustomButton type="secondary" :text="t('common.cancel')" @click="handleInputBoxCancel" />
|
||||||
class="flex-1 rounded-sm border-none bg-danger/50 p-2.5 text-sm font-semibold text-secondary transition-colors duration-fast ease-apple hover:bg-danger/70"
|
<CustomButton
|
||||||
@click="handleInputBoxCancel"
|
type="primary"
|
||||||
>
|
|
||||||
{{ t('common.cancel') }}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="flex-1 rounded-sm border-none bg-accent p-2.5 text-sm font-semibold text-main transition-colors duration-fast ease-apple hover:bg-accent-hover disabled:cursor-not-allowed disabled:opacity-60"
|
|
||||||
:disabled="!inputBoxValue.trim()"
|
:disabled="!inputBoxValue.trim()"
|
||||||
|
:text="t('common.confirm')"
|
||||||
@click="handleInputBoxConfirm"
|
@click="handleInputBoxConfirm"
|
||||||
>
|
/>
|
||||||
{{ t('common.confirm') }}
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Transition>
|
</Transition>
|
||||||
@@ -82,6 +76,7 @@ import { XIcon } from 'lucide-vue-next'
|
|||||||
import { nextTick, onBeforeMount, onBeforeUnmount, reactive, ref, useTemplateRef } from 'vue'
|
import { nextTick, onBeforeMount, onBeforeUnmount, reactive, ref, useTemplateRef } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
|
import CustomButton from '@/components/common/CustomButton.vue'
|
||||||
import $bus from '@/utils/bus'
|
import $bus from '@/utils/bus'
|
||||||
import { SHOW_INPUT_BOX, SHOW_INPUT_BOX_RESPONSE } from '@/utils/constant'
|
import { SHOW_INPUT_BOX, SHOW_INPUT_BOX_RESPONSE } from '@/utils/constant'
|
||||||
import { getConfig } from '@/utils/dataSender'
|
import { getConfig } from '@/utils/dataSender'
|
||||||
|
|||||||
@@ -26,9 +26,11 @@
|
|||||||
class="flex flex-wrap items-center justify-between rounded-sm border border-border bg-bg p-3 transition-all duration-fast ease-apple hover:border-accent hover:bg-surface"
|
class="flex flex-wrap items-center justify-between rounded-sm border border-border bg-bg p-3 transition-all duration-fast ease-apple hover:border-accent hover:bg-surface"
|
||||||
>
|
>
|
||||||
<label class="m-0 flex-1 text-sm font-medium text-main">{{ picbed.name }}</label>
|
<label class="m-0 flex-1 text-sm font-medium text-main">{{ picbed.name }}</label>
|
||||||
|
|
||||||
<!-- Checkbox input -->
|
<!-- Checkbox input -->
|
||||||
<label v-if="inputType === 'checkbox'" class="m-0 flex cursor-pointer items-center p-0 select-none">
|
<div v-if="inputType === 'checkbox'" class="flex items-center rounded-xl">
|
||||||
|
<label
|
||||||
|
class="flex cursor-pointer items-center gap-4 rounded-lg border border-border transition-all duration-200 ease-apple hover:border-accent"
|
||||||
|
>
|
||||||
<input
|
<input
|
||||||
:checked="getMapValue(mapField, picbed.type, defaultValue)"
|
:checked="getMapValue(mapField, picbed.type, defaultValue)"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
@@ -36,10 +38,10 @@
|
|||||||
@change="e => handleMapChange(picbed.type, (e.target as HTMLInputElement).checked)"
|
@change="e => handleMapChange(picbed.type, (e.target as HTMLInputElement).checked)"
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
class="relative h-[24px] w-[44px] shrink-0 rounded-lg bg-gray-300 bg-linear-to-r transition-all duration-medium ease-standard peer-checked:bg-accent peer-checked:shadow-lg before:absolute before:top-[2px] before:left-[2px] before:h-[20px] before:w-[20px] before:rounded-full before:bg-linear-to-r before:from-white before:to-gray-100 before:shadow-md before:transition-all before:duration-medium before:ease-standard peer-checked:before:translate-x-[20px]"
|
class="relative h-[21px] w-[44px] shrink-0 rounded-full bg-gray-400/80 shadow-sm transition-all duration-medium ease-standard peer-checked:bg-accent peer-checked:shadow-[inset_0_1px_3px_rgba(0,0,0,0.1),0_2px_8px_color-mix(in_srgb,var(--color-accent),transparent_30%)] before:absolute before:top-[2px] before:left-[2px] before:h-[17px] before:w-[17px] before:rounded-full before:bg-white before:shadow-sm before:transition-all before:duration-200 before:ease-apple before:content-[''] peer-checked:before:translate-x-[24px]"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
</div>
|
||||||
<!-- Range input -->
|
<!-- Range input -->
|
||||||
<div v-else-if="inputType === 'range'" class="flex flex-wrap items-center gap-2 rounded-sm shadow-sm">
|
<div v-else-if="inputType === 'range'" class="flex flex-wrap items-center gap-2 rounded-sm shadow-sm">
|
||||||
<input
|
<input
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
class="my-3 h-[8px] w-full appearance-none rounded-sm bg-linear-to-l from-accent transition-colors duration-150 ease-apple outline-none [&::--moz-range-thumb]:shadow-sm [&::-moz-range-thumb]:h-[22px] [&::-moz-range-thumb]:w-[22px] [&::-moz-range-thumb]:cursor-pointer [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:border [&::-moz-range-thumb]:border-border [&::-moz-range-thumb]:bg-white [&::-webkit-slider-thumb]:h-[22px] [&::-webkit-slider-thumb]:w-[22px] [&::-webkit-slider-thumb]:cursor-pointer [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border [&::-webkit-slider-thumb]:border-border [&::-webkit-slider-thumb]:bg-white [&::-webkit-slider-thumb]:shadow-sm [&::-webkit-slider-thumb]:transition-all [&::-webkit-slider-thumb]:duration-200 [&::-webkit-slider-thumb]:ease-apple [&::-webkit-slider-thumb:hover]:scale-105 [&::-webkit-slider-thumb:hover]:shadow-md"
|
class="my-3 h-[8px] w-full appearance-none rounded-sm bg-linear-to-l from-accent transition-colors duration-150 ease-apple outline-none [&::--moz-range-thumb]:shadow-sm [&::-moz-range-thumb]:h-[22px] [&::-moz-range-thumb]:w-[22px] [&::-moz-range-thumb]:cursor-pointer [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:border [&::-moz-range-thumb]:border-border [&::-moz-range-thumb]:bg-white [&::-webkit-slider-thumb]:h-[22px] [&::-webkit-slider-thumb]:w-[22px] [&::-webkit-slider-thumb]:cursor-pointer [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border [&::-webkit-slider-thumb]:border-border [&::-webkit-slider-thumb]:bg-white [&::-webkit-slider-thumb]:shadow-sm [&::-webkit-slider-thumb]:transition-all [&::-webkit-slider-thumb]:duration-200 [&::-webkit-slider-thumb]:ease-apple [&::-webkit-slider-thumb:hover]:scale-105 [&::-webkit-slider-thumb:hover]:shadow-md"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
class="mt-2 inline-flex min-w-14 items-center justify-center rounded-md bg-accent px-2 py-1.5 text-sm font-semibold text-white shadow-sm"
|
class="mt-2 inline-flex max-w-16 min-w-14 items-center justify-center rounded-md bg-accent px-2 py-1.5 text-sm font-semibold text-white shadow-sm"
|
||||||
>
|
>
|
||||||
{{ showValue }}
|
{{ showValue }}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex items-center rounded-xl hover:bg-surface hover:shadow-sm">
|
<div class="flex items-center rounded-xl" :class="noHover ? '' : 'hover:bg-surface hover:shadow-sm'">
|
||||||
<label
|
<label
|
||||||
class="flex cursor-pointer items-center gap-4 rounded-lg border border-border p-4 transition-all duration-200 ease-apple hover:border-accent"
|
class="flex cursor-pointer items-center gap-4 rounded-lg border border-border transition-all duration-200 ease-apple hover:border-accent"
|
||||||
:class="noBorder ? 'border-none' : ''"
|
:class="{
|
||||||
|
'border-none': noBorder,
|
||||||
|
'p-4': !tighter,
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
<input v-model="modelValue" type="checkbox" class="peer hidden" @change.stop="emit('change', modelValue)" />
|
<input v-model="modelValue" type="checkbox" class="peer hidden" @change.stop="emit('change', modelValue)" />
|
||||||
<span
|
<span
|
||||||
class="bg-linear-180-r relative shrink-0 rounded-full bg-gray-400/80 shadow-sm transition-all duration-medium ease-standard peer-checked:bg-accent peer-checked:shadow-[inset_0_1px_3px_rgba(0,0,0,0.1),0_2px_8px_color-mix(in_srgb,var(--color-accent),transparent_30%)] before:absolute before:rounded-full before:bg-white before:shadow-sm before:transition-all before:duration-200 before:ease-apple before:content-[''] peer-checked:before:translate-x-[24px]"
|
class="relative shrink-0 rounded-full bg-gray-400/80 shadow-sm transition-all duration-medium ease-standard peer-checked:bg-accent peer-checked:shadow-[inset_0_1px_3px_rgba(0,0,0,0.1),0_2px_8px_color-mix(in_srgb,var(--color-accent),transparent_30%)] before:absolute before:rounded-full before:bg-white before:shadow-sm before:transition-all before:duration-200 before:ease-apple before:content-[''] peer-checked:before:translate-x-[24px]"
|
||||||
:class="
|
:class="
|
||||||
small
|
small
|
||||||
? 'h-[21px] w-[44px] before:top-[2px] before:left-[2px] before:h-[17px] before:w-[17px]'
|
? 'h-[21px] w-[44px] before:top-[2px] before:left-[2px] before:h-[17px] before:w-[17px]'
|
||||||
@@ -59,6 +62,8 @@ const {
|
|||||||
small = false,
|
small = false,
|
||||||
tips = '',
|
tips = '',
|
||||||
required = false,
|
required = false,
|
||||||
|
noHover = false,
|
||||||
|
tighter = false,
|
||||||
} = defineProps<{
|
} = defineProps<{
|
||||||
noBorder?: boolean
|
noBorder?: boolean
|
||||||
title?: string
|
title?: string
|
||||||
@@ -66,6 +71,8 @@ const {
|
|||||||
small?: boolean
|
small?: boolean
|
||||||
tips?: string
|
tips?: string
|
||||||
required?: boolean
|
required?: boolean
|
||||||
|
noHover?: boolean
|
||||||
|
tighter?: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
function toggleTooltip() {
|
function toggleTooltip() {
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
@import "tailwindcss" reference;
|
|
||||||
@import "../../assets/css/theme.css" reference;
|
|
||||||
@import "../../assets/css/utilities.css" reference;
|
|
||||||
|
|
||||||
.guide-btn {
|
|
||||||
@apply px-3 py-1.5 rounded-sm text-sm font-medium cursor-pointer border-none flex items-center gap-1 transition-all duration-200 ease-apple;
|
|
||||||
@apply [.primary]:bg-accent [.primary]:text-white;
|
|
||||||
@apply hover:[.primary]:bg-accent-hover hover:[.primary]:transform-[-translateY(1px)];
|
|
||||||
@apply [.secondary]:bg-bg-tertiary [.secondary]:text-main;
|
|
||||||
@apply hover:[.secondary]:bg-accent-hover;
|
|
||||||
@apply [.outline]:bg-transparent [.outline]:text-main [.outline]:border-border;
|
|
||||||
@apply hover:[.outline]:bg-accent hover:[.outline]:text-main;
|
|
||||||
@apply [.success]:bg-success [.success]:text-white;
|
|
||||||
@apply hover:[.success]:bg-success hover:[.success]:-translate-y-px;
|
|
||||||
@apply max-md:flex-1 max-md:justify-center max-md:min-w-[calc(50%-4px)]
|
|
||||||
}
|
|
||||||
@@ -2,39 +2,6 @@
|
|||||||
@import '../../assets/css/theme.css' reference;
|
@import '../../assets/css/theme.css' reference;
|
||||||
@import '../../assets/css/utilities.css' reference;
|
@import '../../assets/css/utilities.css' reference;
|
||||||
|
|
||||||
/* ==================== Settings Section ==================== */
|
|
||||||
.settings-section {
|
|
||||||
@apply rounded-xl border border-border bg-bg p-5 shadow-sm transition-all duration-200 ease-apple hover:shadow-md;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-header {
|
|
||||||
@apply mb-6 flex items-center gap-1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-icon {
|
|
||||||
@apply flex h-[30px] w-[30px] shrink-0 items-center justify-center rounded-lg bg-bg-secondary text-accent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-title-group {
|
|
||||||
@apply [&_h2]:mb-0 [&_h2]:text-lg [&_h2]:leading-0 [&_h2]:font-semibold [&_h2]:tracking-tight [&_h2]:text-main;
|
|
||||||
@apply [&_p]:mt-6 [&_p]:text-sm [&_p]:leading-0 [&_p]:text-secondary;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ==================== Form Elements ==================== */
|
|
||||||
.form-grid {
|
|
||||||
@apply grid grid-cols-[repeat(auto-fit,minmax(280px,1fr))] gap-4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-group {
|
|
||||||
@apply mb-6;
|
|
||||||
@apply last:mb-0;
|
|
||||||
@apply [&>:label:not(.custom-switch,.radio-option)]:mb-2.5
|
|
||||||
[&>:label:not(.custom-switch,.radio-option)]:block
|
|
||||||
[&>:label:not(.custom-switch,.radio-option)]:text-sm
|
|
||||||
[&>:label:not(.custom-switch,.radio-option)]:font-semibold
|
|
||||||
[&>:label:not(.custom-switch,.radio-option)]:text-main;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-input,
|
.form-input,
|
||||||
.form-textarea {
|
.form-textarea {
|
||||||
@apply box-border w-full rounded-lg border border-border bg-bg-tertiary px-4 py-3.5 text-sm text-main transition-all duration-200 ease-apple;
|
@apply box-border w-full rounded-lg border border-border bg-bg-tertiary px-4 py-3.5 text-sm text-main transition-all duration-200 ease-apple;
|
||||||
@@ -45,7 +12,3 @@
|
|||||||
.form-textarea {
|
.form-textarea {
|
||||||
@apply min-h-[90px] resize-y font-[ui-monospace,SFMono-Regular,'SF_Mono',Menlo,Consolas,'Liberation_Mono',monospace];
|
@apply min-h-[90px] resize-y font-[ui-monospace,SFMono-Regular,'SF_Mono',Menlo,Consolas,'Liberation_Mono',monospace];
|
||||||
}
|
}
|
||||||
|
|
||||||
.title-text {
|
|
||||||
@apply text-base font-semibold text-main;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -32,45 +32,40 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<span class="text-sm text-secondary">{{ t('pages.gallery.isAlwaysForceReload') }}</span>
|
<span class="text-sm text-secondary">{{ t('pages.gallery.isAlwaysForceReload') }}</span>
|
||||||
<label class="relative inline-block h-[20px] w-[44px]">
|
<CustomSwitch
|
||||||
<input
|
|
||||||
v-model="isAlwaysForceReload"
|
v-model="isAlwaysForceReload"
|
||||||
class="peer/fir h-0 w-0 opacity-0"
|
small
|
||||||
type="checkbox"
|
tighter
|
||||||
|
no-border
|
||||||
|
no-hover
|
||||||
@change="handleIsAlwaysForceReload"
|
@change="handleIsAlwaysForceReload"
|
||||||
/>
|
/>
|
||||||
<span
|
|
||||||
class="switch-slider peer-checked/fir:bg-accent peer-checked/fir:bg-none! peer-checked/fir:before:translate-x-[22px]"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<span class="text-sm text-secondary">{{ t('pages.gallery.syncDelete') }}</span>
|
<span class="text-sm text-secondary">{{ t('pages.gallery.syncDelete') }}</span>
|
||||||
<label class="relative inline-block h-[20px] w-[44px]">
|
<CustomSwitch v-model="deleteCloud" small tighter no-border no-hover @change="handleDeleteCloudFile" />
|
||||||
<input
|
|
||||||
v-model="deleteCloud"
|
|
||||||
class="peer/sec h-0 w-0 opacity-0"
|
|
||||||
type="checkbox"
|
|
||||||
@change="handleDeleteCloudFile"
|
|
||||||
/>
|
|
||||||
<span
|
|
||||||
class="switch-slider peer-checked/sec:bg-accent peer-checked/sec:bg-none! peer-checked/sec:before:translate-x-[22px]"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
<button class="head-action-button relative" :title="getViewModeLabel()" @click="toggleViewMode">
|
<CustomButton
|
||||||
<component :is="getViewModeIcon()" :size="16" />
|
type="primary"
|
||||||
<span class="mt-0.5">{{ getViewModeLabel() }}</span>
|
:text="getViewModeLabel()"
|
||||||
</button>
|
:icon="getViewModeIcon()"
|
||||||
<button class="head-action-button" @click="toggleHandleBar">
|
class="px-2!"
|
||||||
<ChevronDownIcon v-if="!handleBarActive" :size="16" />
|
@click="toggleViewMode"
|
||||||
<ChevronUpIcon v-else :size="16" />
|
/>
|
||||||
<span class="mt-0.5">{{ t('pages.gallery.hideFilters') }}</span>
|
<CustomButton
|
||||||
</button>
|
type="primary"
|
||||||
<button class="head-action-button" @click="refreshPage">
|
:text="t('pages.gallery.hideFilters')"
|
||||||
<RefreshCwIcon :size="16" />
|
:icon="handleBarActive ? ChevronUpIcon : ChevronDownIcon"
|
||||||
<span class="mt-0.5">{{ t('pages.gallery.refresh') }}</span>
|
class="px-2!"
|
||||||
</button>
|
@click="toggleHandleBar"
|
||||||
|
/>
|
||||||
|
<CustomButton
|
||||||
|
type="secondary"
|
||||||
|
:text="t('pages.gallery.refresh')"
|
||||||
|
:icon="RefreshCwIcon"
|
||||||
|
class="px-2!"
|
||||||
|
@click="refreshPage"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -90,7 +85,9 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="filter-group">
|
<div class="filter-group">
|
||||||
<label class="filter-label">{{ t('pages.gallery.dateRange') }}</label>
|
<label class="mb-0 text-sm leading-[1.4] font-semibold text-secondary">{{
|
||||||
|
t('pages.gallery.dateRange')
|
||||||
|
}}</label>
|
||||||
<div class="flex w-full flex-wrap items-center gap-2 max-md:items-start">
|
<div class="flex w-full flex-wrap items-center gap-2 max-md:items-start">
|
||||||
<input v-model="dateRangeStart" type="date" class="date-input" placeholder="Start date" />
|
<input v-model="dateRangeStart" type="date" class="date-input" placeholder="Start date" />
|
||||||
<span class="shrink-0 font-medium text-secondary">-</span>
|
<span class="shrink-0 font-medium text-secondary">-</span>
|
||||||
@@ -423,6 +420,7 @@ import { onBeforeRouteUpdate } from 'vue-router'
|
|||||||
import ALLApi from '@/apis/allApi'
|
import ALLApi from '@/apis/allApi'
|
||||||
import CustomButton from '@/components/common/CustomButton.vue'
|
import CustomButton from '@/components/common/CustomButton.vue'
|
||||||
import CustomModal from '@/components/common/CustomModal.vue'
|
import CustomModal from '@/components/common/CustomModal.vue'
|
||||||
|
import CustomSwitch from '@/components/common/CustomSwitch.vue'
|
||||||
import MultiSelect from '@/components/common/MultiSelect.vue'
|
import MultiSelect from '@/components/common/MultiSelect.vue'
|
||||||
import PlaceholderTable from '@/components/common/PlaceholderTable.vue'
|
import PlaceholderTable from '@/components/common/PlaceholderTable.vue'
|
||||||
import SingleSelect from '@/components/common/SingleSelect.vue'
|
import SingleSelect from '@/components/common/SingleSelect.vue'
|
||||||
@@ -458,7 +456,7 @@ const imgInfo = reactive({
|
|||||||
imgUrl: '',
|
imgUrl: '',
|
||||||
})
|
})
|
||||||
const choosedList: IObjT<boolean> = reactive({})
|
const choosedList: IObjT<boolean> = reactive({})
|
||||||
const gallerySliderControl = reactive({
|
const gallerySliderControl = ref({
|
||||||
visible: false,
|
visible: false,
|
||||||
index: 0,
|
index: 0,
|
||||||
})
|
})
|
||||||
@@ -808,8 +806,8 @@ function clearChoosedList() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function zoomImage(index: number) {
|
function zoomImage(index: number) {
|
||||||
gallerySliderControl.index = index
|
gallerySliderControl.value.index = index
|
||||||
gallerySliderControl.visible = true
|
gallerySliderControl.value.visible = true
|
||||||
}
|
}
|
||||||
|
|
||||||
async function copy(item: ImgInfo) {
|
async function copy(item: ImgInfo) {
|
||||||
@@ -863,18 +861,16 @@ function remove(item: ImgInfo, _: number) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleIsAlwaysForceReload(event: Event) {
|
function handleIsAlwaysForceReload(value: boolean) {
|
||||||
const ev = (event.target as HTMLInputElement).checked
|
|
||||||
isAlwaysForceReload.value = ev
|
|
||||||
saveConfig({
|
saveConfig({
|
||||||
[configPaths.settings.isAlwaysForceReload]: ev,
|
[configPaths.settings.isAlwaysForceReload]: value,
|
||||||
})
|
})
|
||||||
window.electron.sendRPC(IRPCActionType.REFRESH_SETTING_WINDOW)
|
window.electron.sendRPC(IRPCActionType.REFRESH_SETTING_WINDOW)
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleDeleteCloudFile(event: Event) {
|
function handleDeleteCloudFile(value: boolean) {
|
||||||
saveConfig({
|
saveConfig({
|
||||||
[configPaths.settings.deleteCloudFile]: (event.target as HTMLInputElement).checked,
|
[configPaths.settings.deleteCloudFile]: value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -399,7 +399,7 @@
|
|||||||
/>
|
/>
|
||||||
</SettingCard>
|
</SettingCard>
|
||||||
|
|
||||||
<SettingCard p1>
|
<SettingCard p1 class="flex flex-col justify-center">
|
||||||
<CustomSwitch
|
<CustomSwitch
|
||||||
v-model="formOfSetting.deleteLocalFile"
|
v-model="formOfSetting.deleteLocalFile"
|
||||||
small
|
small
|
||||||
@@ -429,7 +429,7 @@
|
|||||||
/>
|
/>
|
||||||
</SettingCard>
|
</SettingCard>
|
||||||
|
|
||||||
<SettingCard p1>
|
<SettingCard p1 class="flex flex-col justify-center">
|
||||||
<CustomSwitch
|
<CustomSwitch
|
||||||
v-model="formOfSetting.autoCopy"
|
v-model="formOfSetting.autoCopy"
|
||||||
small
|
small
|
||||||
|
|||||||
@@ -2,24 +2,10 @@
|
|||||||
@import "../../assets/css/theme.css" reference;
|
@import "../../assets/css/theme.css" reference;
|
||||||
@import "../../assets/css/utilities.css" reference;
|
@import "../../assets/css/utilities.css" reference;
|
||||||
|
|
||||||
.head-action-button {
|
|
||||||
@apply flex items-center border-none rounded-md px-2 py-1.5 text-sm font-medium text-white bg-accent font-[inherit] gap-2 cursor-pointer transition-all duration-fast ease-apple;
|
|
||||||
@apply hover:bg-accent-hover hover:-translate-y-px hover:shadow-md;
|
|
||||||
}
|
|
||||||
|
|
||||||
.switch-slider {
|
|
||||||
@apply absolute inset-0 cursor-pointer rounded-2xl bg-[linear-gradient(180deg,#d0d3d9_0%,#c0c4cc_100%)] shadow-sm transition-all duration-medium ease-standard;
|
|
||||||
@apply before:absolute before:bottom-[2px] before:left-[2px] before:h-[17px] before:w-[17px] before:rounded-full before:shadow-sm before:transition-all before:ease-standard before:duration-medium before:content-[""] before:bg-[linear-gradient(180deg,#ffffff_0%,#f5f5f5_100%)];
|
|
||||||
}
|
|
||||||
|
|
||||||
.filter-group {
|
.filter-group {
|
||||||
@apply flex flex-col gap-1 flex-1 min-w-[140px];
|
@apply flex flex-col gap-1 flex-1 min-w-[140px];
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter-label {
|
|
||||||
@apply mb-0 text-sm font-semibold text-secondary leading-[1.4];
|
|
||||||
}
|
|
||||||
|
|
||||||
.date-input {
|
.date-input {
|
||||||
@apply border border-border-secondary rounded-md px-2 py-1.5 min-w-[20px] h-[28px] text-xs text-main transition-all duration-fast ease-apple flex-1 leading-[1.2];
|
@apply border border-border-secondary rounded-md px-2 py-1.5 min-w-[20px] h-[28px] text-xs text-main transition-all duration-fast ease-apple flex-1 leading-[1.2];
|
||||||
@apply focus:border-accent-hover focus:outline-none focus:shadow-md;
|
@apply focus:border-accent-hover focus:outline-none focus:shadow-md;
|
||||||
@@ -54,48 +40,7 @@
|
|||||||
@apply [.delete-icon]:hover:text-error [.delete-icon]:hover:bg-error/10;
|
@apply [.delete-icon]:hover:text-error [.delete-icon]:hover:bg-error/10;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-button {
|
|
||||||
@apply absolute flex z-10 justify-center items-center border-none rounded-full w-[48px] h-[48px] text-white bg-[rgb(0_0_0_/50%)] transition-all duration-fast ease-apple cursor-pointer;
|
|
||||||
@apply not-disabled:hover:bg-accent;
|
|
||||||
@apply disabled:opacity-50 disabled:cursor-not-allowed;
|
|
||||||
@apply [.prev]:left-4 [.next]:right-4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-input {
|
.form-input {
|
||||||
@apply border border-border rounded-md p-3 w-full text-sm text-main bg-bg-secondary transition-all duration-fast ease-apple box-border;
|
@apply border border-border rounded-md p-3 w-full text-sm text-main bg-bg-secondary transition-all duration-fast ease-apple box-border;
|
||||||
@apply focus:border-accent focus:outline-none focus:shadow-md;
|
@apply focus:border-accent focus:outline-none focus:shadow-md;
|
||||||
}
|
}
|
||||||
|
|
||||||
.placeholder-grid {
|
|
||||||
@apply grid grid-cols-[repeat(auto-fit,minmax(150px,1fr))] gap-0 py-2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.placeholder-item {
|
|
||||||
@apply gap-2 flex items-center m-0 rounded-none py-2 px-4 text-sm leading-[1.4] flex-col;
|
|
||||||
@apply hover:bg-accent/30 hover:border hover:border-accent;
|
|
||||||
@apply [&_code]:mr-3.5 [&_code]:border [&_code]:border-white/20 [&_code]:rounded-md [&_code]:px-2.5 [&_code]:py-1 [&_code]:min-w-[80px] [&_code]:text-base [&_code]:font-['SF_Mono',Monaco,'Cascadia_Code','Roboto_Mono',Consolas,'Courier_New',monospace] [&_code]:font-semibold [&_code]:text-center [&_code]:text-main [&_code]:bg-bg-secondary [&_code]:shadow-sm [&_code]:tracking-[0.02em] [&_code]:shrink-0;
|
|
||||||
@apply [&_span]:text-xs [&_span]:font-medium [&_span]:text-main [&_span]:flex-1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Buttons */
|
|
||||||
.btn-primary {
|
|
||||||
@apply flex items-center border-none rounded-md px-6 py-3 text-sm font-medium font-[inherit] text-white bg-accent gap-2 cursor-pointer transition-all duration-fast ease-apple;
|
|
||||||
@apply hover:bg-accent-hover hover:-translate-y-px hover:shadow-sm;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-secondary {
|
|
||||||
@apply flex items-center border border-border rounded-md px-6 py-3 text-sm font-medium font-[inherit] text-main bg-bg-secondary gap-2 cursor-pointer transition-all duration-fast ease-apple;
|
|
||||||
@apply hover:bg-border-secondary hover:-translate-y-px hover:shadow-sm;
|
|
||||||
}
|
|
||||||
|
|
||||||
.zoom-btn {
|
|
||||||
@apply flex justify-center items-center border-none rounded-md w-[32px] h-[32px] text-sm font-semibold text-white bg-[rgb(255_255_255_/10%)] transition-all duration-fast ease-apple cursor-pointer;
|
|
||||||
@apply not-disabled:hover:bg-[rgb(255_255_255_/20%)];
|
|
||||||
@apply disabled:opacity-50 disabled:cursor-not-allowed;
|
|
||||||
@apply [.reset-btn]:py-0 [.reset-btn]:w-auto [.reset-btn]:px-3 [.reset-btn]:text-xs;
|
|
||||||
}
|
|
||||||
|
|
||||||
.category-title {
|
|
||||||
@apply m-0 border-b border-border pt-3.5 px-4 pb-2 text-sm font-semibold text-secondary bg-[linear-gradient(180deg,var(--color-background-secondary)_0%,var(--color-background-tertiary)_100%)];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user