diff --git a/README-en.md b/README-en.md index fb05086..4511e84 100644 --- a/README-en.md +++ b/README-en.md @@ -161,9 +161,9 @@ After the update, run `https://your-project-domain/api/init/your-jwt-secret` to ## Support -**Buy me a cup of milk tea** - - + + +

**Special Sponsors** diff --git a/README.md b/README.md index 244984d..1b30c7a 100644 --- a/README.md +++ b/README.md @@ -182,9 +182,10 @@ jwt_secret = "" #登录身份令牌的密钥,随便填一串字符串 ## 赞助 -**请我喝一杯奶茶** - + + +

**特别赞助商** diff --git a/doc/images/support.png b/doc/images/support.png new file mode 100644 index 0000000..c84921b Binary files /dev/null and b/doc/images/support.png differ diff --git a/mail-vue/package-lock.json b/mail-vue/package-lock.json index a91f751..bd978cd 100644 --- a/mail-vue/package-lock.json +++ b/mail-vue/package-lock.json @@ -22,7 +22,6 @@ "pinia": "^3.0.2", "pinia-plugin-persistedstate": "^4.2.0", "vue": "^3.5.13", - "vue-cropper": "^1.1.4", "vue-i18n": "^11.1.10", "vue-router": "^4.5.0" }, @@ -3842,12 +3841,6 @@ } } }, - "node_modules/vue-cropper": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/vue-cropper/-/vue-cropper-1.1.4.tgz", - "integrity": "sha512-5m98vBsCEI9rbS4JxELxXidtAui3qNyTHLHg67Qbn7g8cg+E6LcnC+hh3SM/p94x6mFh6KRxT1ttnta+wCYqWA==", - "license": "ISC" - }, "node_modules/vue-i18n": { "version": "11.1.10", "resolved": "https://registry.npmmirror.com/vue-i18n/-/vue-i18n-11.1.10.tgz", diff --git a/mail-vue/package.json b/mail-vue/package.json index c093b0b..fbdd047 100644 --- a/mail-vue/package.json +++ b/mail-vue/package.json @@ -24,7 +24,6 @@ "pinia": "^3.0.2", "pinia-plugin-persistedstate": "^4.2.0", "vue": "^3.5.13", - "vue-cropper": "^1.1.4", "vue-i18n": "^11.1.10", "vue-router": "^4.5.0" }, diff --git a/mail-vue/src/App.vue b/mail-vue/src/App.vue index 377b175..9b6eaf2 100644 --- a/mail-vue/src/App.vue +++ b/mail-vue/src/App.vue @@ -7,7 +7,7 @@ import { useI18n } from "vue-i18n"; import {useSettingStore} from "@/store/setting.js"; const settingStore = useSettingStore() -import zhCn from 'element-plus/es/locale/lang/zh-cn'; +import zhCn from 'element-plus/es/locale/lang/zh-cn';; const { locale } = useI18n() locale.value = settingStore.lang \ No newline at end of file diff --git a/mail-vue/src/i18n/en.js b/mail-vue/src/i18n/en.js index ca373b6..9fb4fd0 100644 --- a/mail-vue/src/i18n/en.js +++ b/mail-vue/src/i18n/en.js @@ -96,7 +96,6 @@ const en = { expand: 'Expand', collapse: 'Collapse', daily: 'Daily', - emailBlock: '*Intercept all incoming emails to @example.com', searchRegKeyDesc: 'Enter invite code to search', remainingUses: 'Remaining uses', exhausted: 'Exhausted', @@ -168,11 +167,11 @@ const en = { addOsDomain: 'Add domain', domainDesc: 'Domain', addTurnstileSecret: 'Add turnstile secret', - backgroundCropping: 'Background cropping', + backgroundTitle: 'Change background', tgBotDesc: 'Forward received emails to a Telegram bot', tgBotToken: 'Bot token', toBotTokenDesc: 'Multiple user chat_ids, separated by commas', - otherEmailDec: 'emails can be forwarded to external email, but must be verified via cloudflare.', + otherEmailDesc: 'emails can be forwarded to external email, but must be verified via cloudflare.', otherEmailInputDesc: 'Separate multiple email addresses with commas.', forwardingRulesDesc: 'Rule-based forwarding only forwards emails received by the specified address.', ruleEmailsInputDesc: 'Separate multiple email addresses with commas.', @@ -246,7 +245,17 @@ const en = { totalUserAccount: '{msg}', sendBanned: 'Banned', wrote: 'wrote', - support: 'Support' + support: 'Support', + supportDesc: 'Buy me tea', + featDesc: 'Feature Description', + emailInterception: 'Email Interception', + emailInterceptionDesc: '*Intercept emails by blocking entire domain using @example.com to prevent users from receiving emails from certain websites.', + availableDomains: 'Available domains', + availableDomainsDesc: 'Restrict users to email domains specified. Domains not on the approved list will be blocked from registration, adding email addresses, and sending/receiving emails. If left blank, all domains will be allowed by default.', + backgroundUrlDesc: 'Image URL', + localUpload: ' Local upload', + imageLink: 'Image URL', + backgroundWarning: 'Image file size affects website load speed.' } export default en diff --git a/mail-vue/src/i18n/zh.js b/mail-vue/src/i18n/zh.js index 2e6ffb8..c1eab35 100644 --- a/mail-vue/src/i18n/zh.js +++ b/mail-vue/src/i18n/zh.js @@ -96,7 +96,6 @@ const zh = { expand: '展开', collapse: '收起', daily: '每天', - emailBlock: '输入邮箱拦截收件, 拦截所有前缀 *@example.com', searchRegKeyDesc: '输入注册码搜索', remainingUses: '剩余次数', exhausted: '已用尽', @@ -165,14 +164,14 @@ const zh = { community: '交流', changeTitle: '修改标题', addResendTokenDesc: '输入内容添加,不填则删除', - addOsDomain: '添加访对象存储问域名', + addOsDomain: '添加访问域名', domainDesc: '域名', addTurnstileSecret: '添加 Turnstile 密钥', - backgroundCropping: '背景截图', + backgroundTitle: '设置背景', tgBotDesc: '可以将接收的邮件转发到Tg机器人', tgBotToken: '机器人 token', toBotTokenDesc: '用户 chat_id 多个用,分开', - otherEmailDec: '可以将邮件转到其他服务商邮箱,需要在cloudflare验证邮箱', + otherEmailDesc: '可以将邮件转到其他服务商邮箱,需要在cloudflare验证邮箱', otherEmailInputDesc: '多个邮箱用, 分开', forwardingRulesDesc: '规则转发只会转发设置邮箱所接收的邮件', ruleEmailsInputDesc: '多个邮箱用, 分开', @@ -246,6 +245,16 @@ const zh = { totalUserAccount: '{msg} 个', sendBanned: '已禁用', wrote: '来信', - support: '赞助' + support: '赞助', + supportDesc: '请我喝杯奶茶', + featDesc: '功能说明', + emailInterception: '邮件拦截', + emailInterceptionDesc: '拦截邮件, 要拦截整个域名输入 *@example.com, 可用于禁止用户接收某些网站的邮件', + availableDomains: '可用域名', + availableDomainsDesc: '限制用户只能使用指定的域名邮箱,不在配置名单内的域名会被禁止使用注册添加邮箱,接收发送邮件等功能,留空默认允许可用所有域名', + backgroundUrlDesc: '在线图片链接', + localUpload: '本地上传', + imageLink: '图片链接', + backgroundWarning: '图片文件大小会影响网站加载速度', } export default zh \ No newline at end of file diff --git a/mail-vue/src/layout/aside/index.vue b/mail-vue/src/layout/aside/index.vue index 8a3dbbd..57c92c8 100644 --- a/mail-vue/src/layout/aside/index.vue +++ b/mail-vue/src/layout/aside/index.vue @@ -61,8 +61,8 @@ - - {{$t('SystemSettings')}} + + {{$t('SystemSettings')}} diff --git a/mail-vue/src/layout/index.vue b/mail-vue/src/layout/index.vue index 4b6af20..8ec30b5 100644 --- a/mail-vue/src/layout/index.vue +++ b/mail-vue/src/layout/index.vue @@ -38,7 +38,6 @@ const handleResize = () => { } onMounted(() => { - uiStore.writerRef = writerRef window.addEventListener('resize', handleResize) diff --git a/mail-vue/src/main.js b/mail-vue/src/main.js index 8da163c..29bfd35 100644 --- a/mail-vue/src/main.js +++ b/mail-vue/src/main.js @@ -2,8 +2,6 @@ import {createApp} from 'vue'; import App from './App.vue'; import router from './router'; import './style.css'; -import VueCropper from 'vue-cropper'; -import 'vue-cropper/dist/index.css' import { init } from '@/init/init.js'; import { createPinia } from 'pinia'; import piniaPersistedState from 'pinia-plugin-persistedstate'; @@ -12,7 +10,7 @@ const pinia = createPinia().use(piniaPersistedState) import i18n from "@/i18n/index.js"; const app = createApp(App).use(pinia) await init() -app.use(router).use(VueCropper).use(i18n).directive('perm',perm) +app.use(router).use(i18n).directive('perm',perm) app.config.devtools = true; app.mount('#app'); diff --git a/mail-vue/src/utils/convert.js b/mail-vue/src/utils/convert.js index 2289672..26522db 100644 --- a/mail-vue/src/utils/convert.js +++ b/mail-vue/src/utils/convert.js @@ -1,5 +1,24 @@ import {useSettingStore} from "@/store/setting.js"; export function cvtR2Url(key) { - const settingStore = useSettingStore(); - return settingStore.settings.r2Domain + '/' + key + + if (!key) { + return + 'https://' + '' + } + + if (key.startsWith('https://')) { + return key + } + + const { settings } = useSettingStore(); + + let domain = settings.r2Domain + + if (!domain.startsWith('http')) { + return 'https://' + domain + '/' + key + } + + if (domain.endsWith("/")) { + domain = domain.slice(0, -1); + } + return domain + '/' + key } \ No newline at end of file diff --git a/mail-vue/src/utils/day.js b/mail-vue/src/utils/day.js index 01a57ae..f7ab2f0 100644 --- a/mail-vue/src/utils/day.js +++ b/mail-vue/src/utils/day.js @@ -40,11 +40,11 @@ export function fromNow(date) { if (diffSeconds < 60) return `Just now`; if (diffMinutes < 60) return `${diffMinutes} min ago`; if (diffHours < 2) return `${diffHours} hour${diffHours > 1 ? 's' : ''} ago`; - return d.format('HH:mm'); + return d.format('hh:mm A'); } if (now.subtract(1, 'day').isSame(d, 'day')) { - return d.format('hh:mm A'); + return d.format('MMM D'); } return d.year() === now.year() diff --git a/mail-vue/src/utils/file-utils.js b/mail-vue/src/utils/file-utils.js index 8763fc1..6611efa 100644 --- a/mail-vue/src/utils/file-utils.js +++ b/mail-vue/src/utils/file-utils.js @@ -14,13 +14,18 @@ export function formatBytes(bytes) { return `${size} ${units[i]}`; } -export function fileToBase64(file) { +export function fileToBase64(file, type = false) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.readAsDataURL(file); reader.onload = () => { - const base64 = reader.result.split(',')[1]; - resolve(base64); + if (type) { + const base64 = reader.result; + resolve(base64); + } else { + const base64 = reader.result.split(',')[1]; + resolve(base64); + } }; reader.onerror = reject; }); @@ -50,4 +55,4 @@ export function compressImage(file, config = {}) { }, }); }); -} +} \ No newline at end of file diff --git a/mail-vue/src/views/role/index.vue b/mail-vue/src/views/role/index.vue index 576f5ab..5e2608a 100644 --- a/mail-vue/src/views/role/index.vue +++ b/mail-vue/src/views/role/index.vue @@ -47,15 +47,52 @@ - + +
- + + + +
@@ -108,6 +145,7 @@ import {roleAdd, roleDelete, rolePermTree, roleRoleList, roleSet, roleSetDef} fr import loading from '@/components/loading/index.vue'; import {useRoleStore} from "@/store/role.js"; import {useUserStore} from "@/store/user.js"; +import {useSettingStore} from "@/store/setting.js"; import {isEmail} from "@/utils/verify-utils.js"; import {useI18n} from "vue-i18n"; @@ -115,6 +153,7 @@ defineOptions({ name: 'role' }) +const { domainList } = useSettingStore(); const { t, locale } = useI18n(); const userStore = useUserStore(); const roleStore = useRoleStore(); @@ -144,8 +183,11 @@ const form = reactive({ accountCount: 0, sort: 0, isDefault: 0, + availDomain: [] }) +let domainOptions = [] + const expand = ref(false) let chooseRole = {} @@ -156,6 +198,18 @@ rolePermTree().then(tree => { treeList.push(...tree) }) +domainOptions = domainList.map(domain => ({label: domain,value: domain})) + + +function availDomainChange() { + const index = form.availDomain.findIndex(domain => { + return !domainOptions.map(option => option.value).includes(domain) + }) + if (index > -1) { + form.availDomain.splice(index,1) + } +} + function banEmailAddTag(val) { const emails = Array.from(new Set( val.split(/[,,]/).map(item => item.trim()).filter(item => item) @@ -270,6 +324,7 @@ function resetForm() { form.accountCount = 0 form.banEmail = [] form.banEmailType = 0 + form.availDomain = [] tree.value.setCheckedKeys([]) } @@ -285,6 +340,7 @@ function openRoleSet(role) { form.sendCount = role.sendCount form.accountCount = role.accountCount form.banEmail = role.banEmail + form.availDomain = role.availDomain nextTick(() => { tree.value.setCheckedKeys(role.permIds) }) @@ -394,7 +450,7 @@ window.onresize = () => { .warning { position: relative; left: 5px; - top: 5px; + top: 2px; color: gray; cursor: pointer; } diff --git a/mail-vue/src/views/setting/index.vue b/mail-vue/src/views/setting/index.vue index 160a0d6..705bee3 100644 --- a/mail-vue/src/views/setting/index.vue +++ b/mail-vue/src/views/setting/index.vue @@ -73,9 +73,8 @@ import {accountSetName} from "@/request/account.js"; import {useAccountStore} from "@/store/account.js"; import {useI18n} from "vue-i18n"; import {useSettingStore} from "@/store/setting.js"; -import dayjs from "dayjs"; -const { t, locale } = useI18n() +const { t } = useI18n() const settingStore = useSettingStore() const accountStore = useAccountStore() const userStore = useUserStore(); diff --git a/mail-vue/src/views/sys-setting/index.vue b/mail-vue/src/views/sys-setting/index.vue index 017f61e..f4b14e3 100644 --- a/mail-vue/src/views/sys-setting/index.vue +++ b/mail-vue/src/views/sys-setting/index.vue @@ -101,13 +101,13 @@ fit="cover" >
- + @@ -286,10 +286,10 @@
{{$t('support')}} : - - Afdian + + {{t('supportDesc')}}
@@ -299,7 +299,7 @@
- +
{{$t('save')}} @@ -319,7 +319,7 @@ {{$t('save')}}
- +
{{$t('save')}} @@ -334,24 +334,37 @@
-
- -
+ + +
+ + {{$t('localUpload')}} + + + {{$t('imageLink')}} + {{$t('save')}}
@@ -387,7 +400,7 @@