From 870c650de72488e33b5abb4d63091edf09eb7c80 Mon Sep 17 00:00:00 2001 From: Kuingsmile <96409857+Kuingsmile@users.noreply.github.com> Date: Wed, 20 Aug 2025 15:16:24 +0800 Subject: [PATCH] :sparkles: Feature(custom): add support for per-picbed image process setting --- .../components/ImageProcessSetting.vue | 404 +++++++++++- src/renderer/components/PerPicbedSetting.vue | 622 ++++++++++++++++++ src/renderer/i18n/locales/en.json | 4 + src/renderer/i18n/locales/zh-CN.json | 4 + src/renderer/i18n/locales/zh-TW.json | 4 + 5 files changed, 1034 insertions(+), 4 deletions(-) create mode 100644 src/renderer/components/PerPicbedSetting.vue diff --git a/src/renderer/components/ImageProcessSetting.vue b/src/renderer/components/ImageProcessSetting.vue index 0785e72e..1461b11b 100644 --- a/src/renderer/components/ImageProcessSetting.vue +++ b/src/renderer/components/ImageProcessSetting.vue @@ -64,12 +64,36 @@ {{ $t('pages.imageProcess.general.isRemoveExif') }} + + safeSetMapValue(compressForm, 'isRemoveExif', picbedType, value, false) + " + /> {{ $t('pages.imageProcess.general.quality') }} {{ compressForm.quality }}% + + safeSetMapValue(compressForm, 'quality', picbedType, value, 100)" + /> @@ -85,6 +109,15 @@ {{ $t('pages.imageProcess.general.isConvert') }} + + safeSetMapValue(compressForm, 'isConvert', picbedType, value, false)" + /> @@ -95,6 +128,18 @@ {{ format.toUpperCase() }} + + safeSetMapValue(compressForm, 'convertFormat', picbedType, value, 'jpg') + " + /> @@ -105,6 +150,18 @@ rows="3" placeholder='{"jpg": "png", "png": "jpg"}' /> + + safeSetMapValue(compressForm, 'formatConvertObj', picbedType, value, '{}') + " + /> @@ -124,6 +181,17 @@ {{ $t('pages.imageProcess.watermark.isAdd') }} + + safeSetMapValue(waterMarkForm, 'isAddWatermark', picbedType, value, false) + " + /> @@ -141,6 +209,21 @@ {{ $t('pages.imageProcess.watermark.image') }} + + safeSetMapValue(waterMarkForm, 'watermarkType', picbedType, value, 'text') + " + /> @@ -152,6 +235,18 @@ {{ $t('pages.imageProcess.watermark.isFullScreen') }} + + + safeSetMapValue(waterMarkForm, 'isFullScreenWatermark', picbedType, value, false) + " + /> @@ -164,6 +259,21 @@ class="form-range" /> {{ waterMarkForm.watermarkDegree }}° + + safeSetMapValue(waterMarkForm, 'watermarkDegree', picbedType, value, 0) + " + /> @@ -177,10 +287,25 @@ class="form-range" /> {{ Math.round((waterMarkForm.watermarkScaleRatio || 0) * 100) }}% + + + safeSetMapValue(waterMarkForm, 'watermarkScaleRatio', picbedType, value, 0.15) + " + /> - {{ $t('pages.imageProcess.watermark.inputText') }} @@ -190,6 +315,19 @@ class="form-input" :placeholder="$t('pages.imageProcess.watermark.inputTextPlaceholder')" /> + + + safeSetMapValue(waterMarkForm, 'watermarkText', picbedType, value, '') + " + /> @@ -200,6 +338,18 @@ class="form-input" :placeholder="$t('pages.imageProcess.watermark.textFontPathPlaceholder')" /> + + safeSetMapValue(waterMarkForm, 'watermarkFontPath', picbedType, value, '') + " + /> @@ -213,6 +363,18 @@ placeholder="#CCCCCC73" /> + + + safeSetMapValue(waterMarkForm, 'watermarkColor', picbedType, value, '#CCCCCC73') + " + /> @@ -225,6 +387,18 @@ class="form-input" :placeholder="$t('pages.imageProcess.watermark.imagePathPlaceholder')" /> + + safeSetMapValue(waterMarkForm, 'watermarkImagePath', picbedType, value, '') + " + /> @@ -240,9 +414,22 @@ {{ waterMarkForm.watermarkImageOpacity || 0 }} + + safeSetMapValue(waterMarkForm, 'watermarkImageOpacity', picbedType, value, 255) + " + /> - {{ $t('pages.imageProcess.watermark.position') }} @@ -257,6 +444,25 @@ {{ label }} + + + + safeSetMapValue(waterMarkForm, 'watermarkPosition', picbedType, value, 'southeast') + " + /> @@ -277,6 +483,15 @@ {{ $t('pages.imageProcess.transform.isFlip') }} + + safeSetMapValue(compressForm, 'isFlip', picbedType, value, false)" + /> @@ -287,6 +502,15 @@ {{ $t('pages.imageProcess.transform.isFlop') }} + + safeSetMapValue(compressForm, 'isFlop', picbedType, value, false)" + /> @@ -303,12 +527,34 @@ {{ $t('pages.imageProcess.transform.isRotate') }} + + safeSetMapValue(compressForm, 'isRotate', picbedType, value, false)" + /> {{ $t('pages.imageProcess.transform.rotationDegree') }} {{ compressForm.rotateDegree }}° + + safeSetMapValue(compressForm, 'rotateDegree', picbedType, value, 0)" + /> @@ -324,6 +570,15 @@ {{ $t('pages.imageProcess.transform.isResize') }} + + safeSetMapValue(compressForm, 'isReSize', picbedType, value, false)" + /> @@ -331,11 +586,37 @@ {{ $t('pages.imageProcess.transform.resizeWidth') }} + + safeSetMapValue(compressForm, 'reSizeWidth', picbedType, value, 500) + " + /> {{ $t('pages.imageProcess.transform.resizeHeight') }} + + safeSetMapValue(compressForm, 'reSizeHeight', picbedType, value, 500) + " + /> @@ -353,6 +634,17 @@ {{ $t('pages.imageProcess.transform.skipResizeOfSmallImgHeight') }} + + safeSetMapValue(compressForm, 'skipReSizeOfSmallImg', picbedType, value, false) + " + /> @@ -369,12 +661,36 @@ {{ $t('pages.imageProcess.transform.isResizeByPercentHint') }} + + safeSetMapValue(compressForm, 'isReSizeByPercent', picbedType, value, false) + " + /> {{ $t('pages.imageProcess.transform.resizePercent') }} {{ compressForm.reSizePercent }}% + + safeSetMapValue(compressForm, 'reSizePercent', picbedType, value, 50)" + /> @@ -390,6 +706,9 @@ import { useI18n } from 'vue-i18n' import { configPaths } from '@/utils/configPaths' import { getConfig, saveConfig } from '@/utils/dataSender' +import { updatePicBedGlobal } from '@/utils/global' + +import PerPicbedSetting from './PerPicbedSetting.vue' const { t } = useI18n() const imageProcessDialogVisible = defineModel() @@ -452,33 +771,60 @@ const availableFormat = [ const waterMarkForm = reactive({ isAddWatermark: false, + isAddWatermarkMap: {}, watermarkType: 'text', + watermarkTypeMap: {}, isFullScreenWatermark: false, + isFullScreenWatermarkMap: {}, watermarkDegree: 0, + watermarkDegreeMap: {}, watermarkText: '', + watermarkTextMap: {}, watermarkFontPath: '', + watermarkFontPathMap: {}, watermarkScaleRatio: 0.15, + watermarkScaleRatioMap: {}, watermarkColor: '#CCCCCC73', + watermarkColorMap: {}, watermarkImagePath: '', + watermarkImagePathMap: {}, watermarkPosition: 'southeast', - watermarkImageOpacity: 255 + watermarkPositionMap: {}, + watermarkImageOpacity: 255, + watermarkImageOpacityMap: {} }) const compressForm = reactive({ quality: 100, + qualityMap: {}, isConvert: false, + isConvertMap: {}, convertFormat: 'jpg', + convertFormatMap: {}, isReSize: false, + isReSizeMap: {}, reSizeWidth: 500, + reSizeWidthMap: {}, reSizeHeight: 500, + reSizeHeightMap: {}, skipReSizeOfSmallImg: false, + skipReSizeOfSmallImgMap: {}, isReSizeByPercent: false, + isReSizeByPercentMap: {}, reSizePercent: 50, + reSizePercentMap: {}, isRotate: false, + isRotateMap: {}, rotateDegree: 0, + rotateDegreeMap: {}, isRemoveExif: false, + isRemoveExifMap: {}, isFlip: false, - isFlop: false + isFlipMap: {}, + isFlop: false, + isFlopMap: {}, + formatConvertObj: {}, + formatConvertObjMap: {} }) const formatConvertObj = ref('{}') @@ -486,6 +832,17 @@ const skipProcessForm = reactive({ skipProcessExtList: 'zip,rar,7z,tar,gz,tar.gz,tar.bz2,tar.xz' }) +// State for showing map settings for each field (now unused - kept for future reference) +// const showMapSettings = reactive>({}) + +// Available picbeds for map configuration (now unused - moved to component) +// const availablePicbeds = computed(() => { +// return picBedGlobal.value.map(picbed => ({ +// type: picbed.type, +// name: picbed.name +// })) +// }) + const waterMarkFormKeys = Object.keys(waterMarkForm) as (keyof typeof waterMarkForm)[] const compressFormKeys = Object.keys(compressForm) as (keyof typeof compressForm)[] const skipProcessFormKeys = Object.keys(skipProcessForm) as (keyof typeof skipProcessForm)[] @@ -502,6 +859,25 @@ function handleSaveConfig() { const formatConvertObjFilter = Object.fromEntries(formatConvertObjEntriesFilter) formatConvertObj.value = JSON.stringify(formatConvertObjFilter) compressForm.formatConvertObj = formatConvertObjFilter + + const processedFormatConvertObjMap: Record = {} + Object.entries(compressForm.formatConvertObjMap || {}).forEach(([picbedType, jsonString]) => { + try { + const parsedObj = JSON.parse(jsonString as string) + const objEntries = Object.entries(parsedObj) + const filteredEntries = objEntries.filter((item: any) => { + return imageExtList.includes(item[0]) && availableFormat.includes(item[1]) + }) + const filteredObj = Object.fromEntries(filteredEntries) + if (Object.keys(filteredObj).length > 0) { + processedFormatConvertObjMap[picbedType] = filteredObj + } + } catch (error) { + // Skip invalid JSON strings + } + }) + compressForm.formatConvertObjMap = processedFormatConvertObjMap + saveConfig(configPaths.buildIn.compress, toRaw(compressForm)) saveConfig(configPaths.buildIn.watermark, toRaw(waterMarkForm)) saveConfig(configPaths.buildIn.skipProcess, toRaw(skipProcessForm)) @@ -543,7 +919,27 @@ function closeDialog() { imageProcessDialogVisible.value = false } +// Helper function for getting map values (now unused - moved to component) +// function getMapValue(mapObj: Record | undefined, picbedType: string, defaultValue: any) { +// if (!mapObj) return defaultValue +// return mapObj[picbedType] !== undefined ? mapObj[picbedType] : defaultValue +// } + +// Safe wrapper for setMapValue that ensures map objects exist +function safeSetMapValue(form: any, fieldName: string, picbedType: string, value: any, defaultValue: any) { + const mapFieldName = `${fieldName}Map` + if (!form[mapFieldName]) { + form[mapFieldName] = {} + } + if (value === defaultValue) { + delete form[mapFieldName][picbedType] + } else { + form[mapFieldName][picbedType] = value + } +} + onBeforeMount(async () => { + await updatePicBedGlobal() await initData() }) diff --git a/src/renderer/components/PerPicbedSetting.vue b/src/renderer/components/PerPicbedSetting.vue new file mode 100644 index 00000000..14585646 --- /dev/null +++ b/src/renderer/components/PerPicbedSetting.vue @@ -0,0 +1,622 @@ + + + + + + {{ t('pages.imageProcess.perPicBed.title') }} + + + + + {{ t('pages.imageProcess.perPicBed.description') }} + + + {{ picbed.name }} + + + + handleMapChange(picbed.type, (e.target as HTMLInputElement).checked)" + /> + + + + + + handleMapChange(picbed.type, parseFloat((e.target as HTMLInputElement).value))" + /> + {{ getMapValue(mapField, picbed.type, defaultValue) }}{{ rangeSuffix }} + + + + handleMapChange(picbed.type, parseFloat((e.target as HTMLInputElement).value))" + /> + + + handleMapChange(picbed.type, (e.target as HTMLInputElement).value)" + /> + + + + handleMapChange(picbed.type, (e.target as HTMLInputElement).value)" + /> + handleMapChange(picbed.type, (e.target as HTMLInputElement).value)" + /> + + + + handleMapChange(picbed.type, (e.target as HTMLSelectElement).value)" + > + + {{ option.label }} + + + + + + + + + {{ option.label }} + + + + + + + + + + + diff --git a/src/renderer/i18n/locales/en.json b/src/renderer/i18n/locales/en.json index 163a1e5a..e1741964 100644 --- a/src/renderer/i18n/locales/en.json +++ b/src/renderer/i18n/locales/en.json @@ -128,6 +128,10 @@ "isResizeByPercent": "Enable Resize by Percentage", "isResizeByPercentHint": "Higher priority", "resizePercent": "Resize Percentage (Enter 50 for 50%)" + }, + "perPicBed": { + "title": "Per-PicBed Settings", + "description": "Configure settings for each PicBed individually" } }, "settings": { diff --git a/src/renderer/i18n/locales/zh-CN.json b/src/renderer/i18n/locales/zh-CN.json index e4010768..fc5b5d57 100644 --- a/src/renderer/i18n/locales/zh-CN.json +++ b/src/renderer/i18n/locales/zh-CN.json @@ -128,6 +128,10 @@ "isResizeByPercent": "启用按比例调整", "isResizeByPercentHint": "优先级更高", "resizePercent": "调整比例 (输入 50 表示 50%)" + }, + "perPicBed": { + "title": "图床独立设置", + "description": "为每个图床单独配置设置" } }, "settings": { diff --git a/src/renderer/i18n/locales/zh-TW.json b/src/renderer/i18n/locales/zh-TW.json index 44dce708..a2f73464 100644 --- a/src/renderer/i18n/locales/zh-TW.json +++ b/src/renderer/i18n/locales/zh-TW.json @@ -128,6 +128,10 @@ "isResizeByPercent": "啟用按比例調整", "isResizeByPercentHint": "優先級更高", "resizePercent": "調整比例 (輸入 50 表示 50%)" + }, + "perPicBed": { + "title": "圖床獨立設置", + "description": "為每個圖床單獨配置設置" } }, "settings": {