diff --git a/mail-vue/src/i18n/en.js b/mail-vue/src/i18n/en.js index 5f13f76..50c95da 100644 --- a/mail-vue/src/i18n/en.js +++ b/mail-vue/src/i18n/en.js @@ -143,6 +143,7 @@ const en = { websiteTitle: 'Title', loginBoxOpacity: 'Login Box Opacity', loginBackground: 'Background', + backgroundDarken: 'Background Dim', emailSetting: 'Email', receiveEmail: 'Receive Email', autoRefresh: 'Auto Refresh', diff --git a/mail-vue/src/i18n/zh.js b/mail-vue/src/i18n/zh.js index b5d09c3..c518468 100644 --- a/mail-vue/src/i18n/zh.js +++ b/mail-vue/src/i18n/zh.js @@ -143,6 +143,7 @@ const zh = { websiteTitle: '网站标题', loginBoxOpacity: '登录透明', loginBackground: '登录背景', + backgroundDarken: '背景压暗', emailSetting: '邮件设置', receiveEmail: '邮件接收', autoRefresh: '自动刷新', diff --git a/mail-vue/src/store/setting.js b/mail-vue/src/store/setting.js index b24414f..3a90dc2 100644 --- a/mail-vue/src/store/setting.js +++ b/mail-vue/src/store/setting.js @@ -6,6 +6,7 @@ export const useSettingStore = defineStore('setting', { settings: { r2Domain: '', loginOpacity: 1.00, + loginDarkenFactor: 0, }, lang: '', }), diff --git a/mail-vue/src/views/login/index.vue b/mail-vue/src/views/login/index.vue index b65be49..f7dada5 100644 --- a/mail-vue/src/views/login/index.vue +++ b/mail-vue/src/views/login/index.vue @@ -237,16 +237,27 @@ const loginOpacity = computed(() => { return uiStore.dark ? `rgba(0, 0, 0, ${opacity})` : `rgba(255, 255, 255, ${opacity})` }) +const loginDarkenFactor = computed(() => { + const factor = Number(settingStore.settings.loginDarkenFactor ?? 0) + if (Number.isNaN(factor)) return 0 + return Math.min(1, Math.max(0, factor)) +}) + const hideLoginDomain = computed(() => settingStore.settings.loginDomain === 1) const background = computed(() => { - - return settingStore.settings.background ? { - 'background-image': `url(${cvtR2Url(settingStore.settings.background)})`, - 'background-repeat': 'no-repeat', - 'background-size': 'cover', - 'background-position': 'center' - } : '' + const bg = settingStore.settings.background + if (!bg) return '' + const bgUrl = cvtR2Url(bg) + return { + 'background-image': ` + linear-gradient(rgba(0, 0, 0, ${loginDarkenFactor.value}), rgba(0, 0, 0, ${loginDarkenFactor.value})), + url(${bgUrl}) + `, + 'background-repeat': 'no-repeat, no-repeat', + 'background-size': 'cover, cover', + 'background-position': 'center, center' + } }) const openSelect = () => { diff --git a/mail-vue/src/views/sys-setting/index.vue b/mail-vue/src/views/sys-setting/index.vue index 072c406..02867ed 100644 --- a/mail-vue/src/views/sys-setting/index.vue +++ b/mail-vue/src/views/sys-setting/index.vue @@ -120,6 +120,13 @@ +
+
{{ $t('backgroundDarken') }}
+
+ +
+
@@ -855,6 +862,7 @@ const settingLoading = ref(false) const clearS3Loading = ref(false) const r2DomainInput = ref('') const loginOpacity = ref(0) +const loginDarkenFactor = ref(0) const minEmailPrefix = ref(0) const emailPrefixFilter = ref([]) const backgroundUrl = ref('') @@ -946,6 +954,7 @@ function getSettings() { settingStore.domainList = settingData.domainList; resendTokenForm.domain = setting.value.domainList[0] loginOpacity.value = setting.value.loginOpacity + loginDarkenFactor.value = normalizeFactor(setting.value.loginDarkenFactor) minEmailPrefix.value = setting.value.minEmailPrefix firstLoading.value = false backgroundUrl.value = setting.value.background?.startsWith('http') ? setting.value.background : '' @@ -1231,6 +1240,19 @@ function doOpacityChange() { editSetting(form, true) } +function normalizeFactor(value) { + const factor = Number(value ?? 0) + if (Number.isNaN(factor)) return 0 + return Math.min(1, Math.max(0, factor)) +} + +function doDarkenChange() { + if (!settingReady.value) return + const form = {} + form.loginDarkenFactor = normalizeFactor(loginDarkenFactor.value) + editSetting(form, true) +} + function resetEmailPrefix() { minEmailPrefix.value = setting.value.minEmailPrefix emailPrefixFilter.value = setting.value.emailPrefixFilter @@ -1262,6 +1284,11 @@ const opacityChange = debounce(doOpacityChange, 1000, { trailing: true }) +const darkenChange = debounce(doDarkenChange, 1000, { + leading: false, + trailing: true +}) + function saveBlackList() { let form = { @@ -1494,6 +1521,7 @@ function editSetting(settingForm, refreshStatus = true) { aiCodeFilterShow.value = false }).catch((e) => { loginOpacity.value = setting.value.loginOpacity + loginDarkenFactor.value = normalizeFactor(setting.value.loginDarkenFactor) setting.value = {...setting.value, ...JSON.parse(backup)} }).finally(() => { settingLoading.value = false diff --git a/mail-worker/src/entity/setting.js b/mail-worker/src/entity/setting.js index b50519f..9738ef9 100644 --- a/mail-worker/src/entity/setting.js +++ b/mail-worker/src/entity/setting.js @@ -24,6 +24,7 @@ export const setting = sqliteTable('setting', { ruleEmail: text('rule_email').default('').notNull(), ruleType: integer('rule_type').default(0).notNull(), loginOpacity: integer('login_opacity').default(0.88), + loginDarkenFactor: integer('login_darken_factor').default(0), resendTokens: text('resend_tokens').default("{}").notNull(), noticeTitle: text('notice_title').default('').notNull(), noticeContent: text('notice_content').default('').notNull(), diff --git a/mail-worker/src/init/init.js b/mail-worker/src/init/init.js index 249d344..efa7b73 100644 --- a/mail-worker/src/init/init.js +++ b/mail-worker/src/init/init.js @@ -29,10 +29,19 @@ const dbInit = { await this.v2_8DB(c); await this.v2_9DB(c); await this.v3_0DB(c); + await this.v3_1DB(c); await settingService.refresh(c); return c.text('success'); }, + async v3_1DB(c) { + try { + await c.env.db.prepare(`ALTER TABLE setting ADD COLUMN login_darken_factor INTEGER NOT NULL DEFAULT 0;`).run(); + } catch (e) { + console.warn(`跳过字段:${e.message}`); + } + }, + async v3_0DB(c) { try { await c.env.db.batch([ diff --git a/mail-worker/src/service/setting-service.js b/mail-worker/src/service/setting-service.js index 7c63b22..9356b5f 100644 --- a/mail-worker/src/service/setting-service.js +++ b/mail-worker/src/service/setting-service.js @@ -138,6 +138,11 @@ const settingService = { params.aiCodeFilter = params.aiCodeFilter + ''; } + if (params.loginDarkenFactor !== undefined) { + const factor = Number(params.loginDarkenFactor); + params.loginDarkenFactor = Number.isNaN(factor) ? 0 : Math.min(1, Math.max(0, factor)); + } + params.resendTokens = JSON.stringify(resendTokens); await orm(c).update(setting).set({ ...params }).returning().get(); await this.refresh(c); @@ -214,6 +219,7 @@ const settingService = { siteKey: settingRow.siteKey, background: settingRow.background, loginOpacity: settingRow.loginOpacity, + loginDarkenFactor: settingRow.loginDarkenFactor, domainList: settingRow.loginDomain === 1 && !token ? [] : settingRow.domainList, regKey: settingRow.regKey, regVerifyOpen: settingRow.regVerifyOpen,