修改登录页面
This commit is contained in:
12
README.md
12
README.md
@@ -18,6 +18,8 @@
|
|||||||
|
|
||||||
[**👉在线演示**](https://skymail.ink)
|
[**👉在线演示**](https://skymail.ink)
|
||||||
|
|
||||||
|
[**👉小白保姆教程-界面部署**](https://doc.skymail.ink)
|
||||||
|
|
||||||
|  |  |
|
|  |  |
|
||||||
|---------------------|---------------------|
|
|---------------------|---------------------|
|
||||||
|  |  |
|
|  |  |
|
||||||
@@ -76,8 +78,6 @@
|
|||||||
|
|
||||||
## 使用教程
|
## 使用教程
|
||||||
|
|
||||||
[**👉小白保姆教程-界面部署**](https://doc.skymail.ink)
|
|
||||||
|
|
||||||
### 环境要求
|
### 环境要求
|
||||||
|
|
||||||
|
|
||||||
@@ -140,7 +140,7 @@ jwt_secret = "" #登录身份令牌的密钥,随便填一串字符串
|
|||||||
npm run deploy
|
npm run deploy
|
||||||
```
|
```
|
||||||
|
|
||||||
然后进入域名管理->电子邮件->路由规则->Catch-all 地址. 这里选择发送到 worker, 然后选择创建的worker
|
然后在Cloudflare->域名管理->电子邮件->路由规则->Catch-all 地址. 这里选择发送到 worker, 然后选择创建的worker
|
||||||
|
|
||||||
浏览器输入 https://<你的项目域名>/api/init/<你的jwt_secret> 初始化或更新 d1和kv数据库
|
浏览器输入 https://<你的项目域名>/api/init/<你的jwt_secret> 初始化或更新 d1和kv数据库
|
||||||
|
|
||||||
@@ -148,9 +148,9 @@ npm run deploy
|
|||||||
|
|
||||||
**邮件发送**
|
**邮件发送**
|
||||||
|
|
||||||
在resend官网注册后验证你的域名并创建ApiKey,回到项目网站设置页面添加 resend token
|
在resend注册后验证你的域名并创建ApiKey, 回到项目网站设置页面添加 resend token
|
||||||
|
|
||||||
Webhooks 添加回调地址https://<你的项目域名>/api/webhooks
|
在resend Webhooks 添加回调地址https://<你的项目域名>/api/webhooks
|
||||||
|
|
||||||
勾选✅ (email.bounced email.complained email.delivered email.delivery_delayed)
|
勾选✅ (email.bounced email.complained email.delivered email.delivery_delayed)
|
||||||
|
|
||||||
@@ -165,7 +165,7 @@ npm run dev
|
|||||||
```
|
```
|
||||||
浏览器输入http://127.0.0.1:8787/api/init/<你的jwt_secret>初始化d1和kv数据库
|
浏览器输入http://127.0.0.1:8787/api/init/<你的jwt_secret>初始化d1和kv数据库
|
||||||
|
|
||||||
本地运行r2域名可设置为http://127.0.0.1:8787/api/file
|
本地运行项目页面设置r2域名可设置为http://127.0.0.1:8787/api/file
|
||||||
|
|
||||||
|
|
||||||
## 目录结构
|
## 目录结构
|
||||||
|
|||||||
5
mail-vue/package-lock.json
generated
5
mail-vue/package-lock.json
generated
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "video-admin-vue",
|
"name": "mail-vue",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "video-admin-vue",
|
"name": "mail-vue",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@iconify/vue": "^4.3.0",
|
"@iconify/vue": "^4.3.0",
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
"date-time-format-timezone": "^1.0.22",
|
"date-time-format-timezone": "^1.0.22",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"element-plus": "^2.9.5",
|
"element-plus": "^2.9.5",
|
||||||
|
"lodash-es": "^4.17.21",
|
||||||
"path": "^0.12.7",
|
"path": "^0.12.7",
|
||||||
"pinia": "^3.0.2",
|
"pinia": "^3.0.2",
|
||||||
"pinia-plugin-persistedstate": "^4.2.0",
|
"pinia-plugin-persistedstate": "^4.2.0",
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
"date-time-format-timezone": "^1.0.22",
|
"date-time-format-timezone": "^1.0.22",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"element-plus": "^2.9.5",
|
"element-plus": "^2.9.5",
|
||||||
|
"lodash-es": "^4.17.21",
|
||||||
"path": "^0.12.7",
|
"path": "^0.12.7",
|
||||||
"pinia": "^3.0.2",
|
"pinia": "^3.0.2",
|
||||||
"pinia-plugin-persistedstate": "^4.2.0",
|
"pinia-plugin-persistedstate": "^4.2.0",
|
||||||
|
|||||||
@@ -610,7 +610,7 @@ function loadData() {
|
|||||||
@media (max-width: 1199px) {
|
@media (max-width: 1199px) {
|
||||||
padding-right: 15px;
|
padding-right: 15px;
|
||||||
}
|
}
|
||||||
@media (max-width: 991px) {
|
@media (max-width: 1024px) {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
}
|
}
|
||||||
@@ -630,7 +630,7 @@ function loadData() {
|
|||||||
display: grid;
|
display: grid;
|
||||||
gap: 5px;
|
gap: 5px;
|
||||||
grid-template-columns: auto 1fr;
|
grid-template-columns: auto 1fr;
|
||||||
@media (min-width: 992px) {
|
@media (min-width: 1024px) {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
> span:last-child {
|
> span:last-child {
|
||||||
display: none;
|
display: none;
|
||||||
@@ -719,7 +719,7 @@ function loadData() {
|
|||||||
width: 40px;
|
width: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 991px) {
|
@media (max-width: 1024px) {
|
||||||
.pc-star {
|
.pc-star {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -334,7 +334,7 @@ function submit() {
|
|||||||
.item {
|
.item {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 12px;
|
padding: 12px 10px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
|
|||||||
@@ -52,11 +52,6 @@
|
|||||||
</el-menu>
|
</el-menu>
|
||||||
</div>
|
</div>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
<div class="github">
|
|
||||||
<a href="https://github.com/LaziestRen/cloud-mail">
|
|
||||||
<Icon icon="codicon:github-inverted" width="28" height="28" />
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
@@ -75,7 +70,7 @@ const route = useRoute();
|
|||||||
.title {
|
.title {
|
||||||
margin: 15px 10px;
|
margin: 15px 10px;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
border-radius: 8px;
|
border-radius: 6px;
|
||||||
display: flex;
|
display: flex;
|
||||||
position: relative;
|
position: relative;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
@@ -110,7 +105,7 @@ const route = useRoute();
|
|||||||
|
|
||||||
.el-menu-item {
|
.el-menu-item {
|
||||||
margin: 5px 10px !important;
|
margin: 5px 10px !important;
|
||||||
border-radius: 8px;
|
border-radius: 6px;
|
||||||
height: 36px;
|
height: 36px;
|
||||||
padding: 10px !important;
|
padding: 10px !important;
|
||||||
}
|
}
|
||||||
@@ -148,7 +143,7 @@ const route = useRoute();
|
|||||||
border-right: 0;
|
border-right: 0;
|
||||||
width: 250px;
|
width: 250px;
|
||||||
@media (max-width: 1199px) {
|
@media (max-width: 1199px) {
|
||||||
width: 230px;
|
width: 250px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
{{ userStore.user.email }}
|
{{ userStore.user.email }}
|
||||||
</div>
|
</div>
|
||||||
<div class="detail-user-type">
|
<div class="detail-user-type">
|
||||||
<el-tag type="warning">{{userStore.user.role.name}}</el-tag>
|
<el-tag >{{userStore.user.role.name}}</el-tag>
|
||||||
</div>
|
</div>
|
||||||
<div class="action-info">
|
<div class="action-info">
|
||||||
<div>
|
<div>
|
||||||
@@ -38,14 +38,14 @@
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<div>
|
||||||
<span v-if="sendCount !== null" style="margin-right: 5px">{{ sendCount }}</span>
|
<span v-if="sendCount" style="margin-right: 5px" >{{ sendCount }}</span>
|
||||||
<el-tag v-if="!hasPerm('email:send')" type="warning">{{sendType}}</el-tag>
|
<el-tag v-if="!hasPerm('email:send')" >{{sendType}}</el-tag>
|
||||||
<el-tag v-else type="success">{{sendType}}</el-tag>
|
<el-tag v-else >{{sendType}}</el-tag>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span v-if="accountCount && hasPerm('account:add')" style="margin-right: 5px">{{ accountCount }}个</span>
|
<span v-if="accountCount && hasPerm('account:add')" style="margin-right: 5px">{{ accountCount }}个</span>
|
||||||
<el-tag v-if="!accountCount && hasPerm('account:add')" type="success">无限制</el-tag>
|
<el-tag v-if="!accountCount && hasPerm('account:add')" >无限制</el-tag>
|
||||||
<el-tag v-if="!hasPerm('account:add')" type="warning">无权限</el-tag>
|
<el-tag v-if="!hasPerm('account:add')" >无权限</el-tag>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -84,6 +84,11 @@ const sendType = computed(() => {
|
|||||||
if (!hasPerm('email:send')) {
|
if (!hasPerm('email:send')) {
|
||||||
return '无权限'
|
return '无权限'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (userStore.user.role.sendCount === 0) {
|
||||||
|
return '无限制'
|
||||||
|
}
|
||||||
|
|
||||||
if (userStore.user.role.sendType === 'day') {
|
if (userStore.user.role.sendType === 'day') {
|
||||||
return '每天'
|
return '每天'
|
||||||
}
|
}
|
||||||
@@ -99,7 +104,7 @@ const sendCount = computed(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!userStore.user.role.sendCount) {
|
if (!userStore.user.role.sendCount) {
|
||||||
return '无限制'
|
return null
|
||||||
}
|
}
|
||||||
return userStore.user.sendCount + '/' + userStore.user.role.sendCount
|
return userStore.user.sendCount + '/' + userStore.user.role.sendCount
|
||||||
})
|
})
|
||||||
@@ -143,7 +148,7 @@ function formatName(email) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
:deep(.el-popper.is-pure) {
|
:deep(.el-popper.is-pure) {
|
||||||
border-radius: 8px;
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-details {
|
.user-details {
|
||||||
@@ -196,20 +201,21 @@ function formatName(email) {
|
|||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
.el-button {
|
.el-button {
|
||||||
height: 26px;
|
border-radius: 8px;
|
||||||
|
height: 28px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.details-avatar {
|
.details-avatar {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
height: 45px;
|
height: 40px;
|
||||||
width: 45px;
|
width: 40px;
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
border-radius: 8px;
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,10 +31,10 @@ import writer from '@/layout/write/index.vue'
|
|||||||
|
|
||||||
const uiStore = useUiStore();
|
const uiStore = useUiStore();
|
||||||
const writerRef = ref({})
|
const writerRef = ref({})
|
||||||
const isMobile = ref(window.innerWidth < 992)
|
const isMobile = ref(window.innerWidth < 1024)
|
||||||
const handleResize = () => {
|
const handleResize = () => {
|
||||||
isMobile.value = window.innerWidth < 992
|
isMobile.value = window.innerWidth < 1024
|
||||||
uiStore.asideShow = window.innerWidth >= 991;
|
uiStore.asideShow = window.innerWidth >= 1024;
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@@ -66,7 +66,7 @@ onBeforeUnmount(() => {
|
|||||||
transform: translateX(0);
|
transform: translateX(0);
|
||||||
transition: all 100ms ease;
|
transition: all 100ms ease;
|
||||||
z-index: 101;
|
z-index: 101;
|
||||||
@media (max-width: 991px) {
|
@media (max-width: 1024px) {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ const handleResize = () => {
|
|||||||
@media (max-width: 767px) {
|
@media (max-width: 767px) {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
width: 229px;
|
width: 250px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,8 +87,8 @@ const handleResize = () => {
|
|||||||
position: fixed;
|
position: fixed;
|
||||||
transform: translateX(-100%);
|
transform: translateX(-100%);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
@media (max-width: 991px) {
|
@media (max-width: 1024px) {
|
||||||
width: 229px;
|
width: 250px;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -99,7 +99,7 @@ const handleResize = () => {
|
|||||||
grid-template-columns: 250px 1fr;
|
grid-template-columns: 250px 1fr;
|
||||||
height: calc(100% - 60px);
|
height: calc(100% - 60px);
|
||||||
@media (max-width: 1200px) {
|
@media (max-width: 1200px) {
|
||||||
grid-template-columns: 229px 1fr;
|
grid-template-columns: 250px 1fr;
|
||||||
}
|
}
|
||||||
@media (max-width: 767px) {
|
@media (max-width: 767px) {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
|
|||||||
@@ -285,13 +285,13 @@ function close() {
|
|||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@media (max-width: 991px) {
|
@media (max-width: 1024px) {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 992px) {
|
@media (min-width: 1024px) {
|
||||||
height: min(800px, calc(100vh - 60px));
|
height: min(800px, calc(100vh - 60px));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ router.afterEach((to) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (window.innerWidth < 992) {
|
if (window.innerWidth < 1024) {
|
||||||
uiStore.asideShow = false
|
uiStore.asideShow = false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ export const useSettingStore = defineStore('setting', {
|
|||||||
state: () => ({
|
state: () => ({
|
||||||
domainList: [],
|
domainList: [],
|
||||||
settings: {
|
settings: {
|
||||||
r2Domain: ''
|
r2Domain: '',
|
||||||
|
loginOpacity: 1.00,
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
actions: {
|
actions: {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { defineStore } from 'pinia'
|
|||||||
|
|
||||||
export const useUiStore = defineStore('ui', {
|
export const useUiStore = defineStore('ui', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
asideShow: window.innerWidth > 991,
|
asideShow: window.innerWidth > 1024,
|
||||||
accountShow: false,
|
accountShow: false,
|
||||||
backgroundLoading: true,
|
backgroundLoading: true,
|
||||||
writerRef: null,
|
writerRef: null,
|
||||||
|
|||||||
@@ -65,20 +65,20 @@ button, input, select, textarea {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tox .tox-dialog--width-lg {
|
.tox .tox-dialog--width-lg {
|
||||||
@media (min-width: 992px) {
|
@media (min-width: 1024px) {
|
||||||
height: 850px !important;
|
height: 850px !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tox .tox-dialog__body-content {
|
.tox .tox-dialog__body-content {
|
||||||
max-height: min(850px, calc(100vh - 110px)) !important;
|
max-height: min(850px, calc(100vh - 110px)) !important;
|
||||||
@media (min-width: 992px) {
|
@media (min-width: 1024px) {
|
||||||
max-height: min(850px, calc(100vh - 110px));
|
max-height: min(850px, calc(100vh - 110px));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tox .tox-dialog__body-content {
|
.tox .tox-dialog__body-content {
|
||||||
@media (min-width: 992px) {
|
@media (min-width: 1024px) {
|
||||||
max-height: min(850px, calc(100vh - 110px)) !important;
|
max-height: min(850px, calc(100vh - 110px)) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="login-box" :style="background">
|
<div id="login-box">
|
||||||
<div id="background-wrap" v-if="!settingStore.settings.background">
|
<div id="background-wrap" v-if="!settingStore.settings.background">
|
||||||
<div class="x1 cloud"></div>
|
<div class="x1 cloud"></div>
|
||||||
<div class="x2 cloud"></div>
|
<div class="x2 cloud"></div>
|
||||||
@@ -7,17 +7,14 @@
|
|||||||
<div class="x4 cloud"></div>
|
<div class="x4 cloud"></div>
|
||||||
<div class="x5 cloud"></div>
|
<div class="x5 cloud"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-else :style="background"></div>
|
||||||
<div class="form-wrapper">
|
<div class="form-wrapper">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<span class="form-title">{{ settingStore.settings.title }}</span>
|
<span class="form-title">{{settingStore.settings.title}}</span>
|
||||||
<div class="custom-style" v-if="settingStore.settings.register === 0">
|
<span class="form-desc" v-if="show === 'login'">请输入你的账号信息以开始使用邮箱系统</span>
|
||||||
<el-segmented v-model="show" :options="options"/>
|
<span class="form-desc" v-else>请输入你的账号密码以开始注册邮箱系统</span>
|
||||||
</div>
|
|
||||||
<div v-if="show === 'login'">
|
<div v-if="show === 'login'">
|
||||||
<el-input v-model="form.email" type="text" placeholder="邮箱" autocomplete="off">
|
<el-input class="email-input" v-model="form.email" type="text" placeholder="邮箱" autocomplete="off">
|
||||||
<template #prefix>
|
|
||||||
<Icon icon="weui:email-outlined" width="22" height="22"/>
|
|
||||||
</template>
|
|
||||||
<template #append>
|
<template #append>
|
||||||
<div @click.stop="openSelect">
|
<div @click.stop="openSelect">
|
||||||
<el-select
|
<el-select
|
||||||
@@ -41,19 +38,13 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
<el-input v-model="form.password" placeholder="密码" type="password" autocomplete="off">
|
<el-input v-model="form.password" placeholder="密码" type="password" autocomplete="off">
|
||||||
<template #prefix>
|
|
||||||
<Icon icon="carbon:password" width="22" height="22"/>
|
|
||||||
</template>
|
|
||||||
</el-input>
|
</el-input>
|
||||||
<el-button class="btn" type="primary" @click="submit" :loading="loginLoading"
|
<el-button class="btn" type="primary" @click="submit" :loading="loginLoading"
|
||||||
>登录
|
>登录
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<el-input v-model="registerForm.email" type="text" placeholder="邮箱" autocomplete="off">
|
<el-input class="email-input" v-model="registerForm.email" type="text" placeholder="邮箱" autocomplete="off">
|
||||||
<template #prefix>
|
|
||||||
<Icon icon="weui:email-outlined" width="22" height="22"/>
|
|
||||||
</template>
|
|
||||||
<template #append>
|
<template #append>
|
||||||
<div @click.stop="openSelect">
|
<div @click.stop="openSelect">
|
||||||
<el-select
|
<el-select
|
||||||
@@ -76,34 +67,21 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-input>
|
</el-input>
|
||||||
<el-input v-model="registerForm.password" placeholder="密码" type="password" autocomplete="off">
|
<el-input v-model="registerForm.password" placeholder="密码" type="password" autocomplete="off" />
|
||||||
<template #prefix>
|
<el-input v-model="registerForm.confirmPassword" placeholder="确认密码" type="password" autocomplete="off" />
|
||||||
<Icon icon="carbon:password" width="22" height="22"/>
|
<div v-show="verifyShow"
|
||||||
</template>
|
class="register-turnstile"
|
||||||
</el-input>
|
:data-sitekey="settingStore.settings.siteKey"
|
||||||
<el-input v-model="registerForm.confirmPassword" placeholder="确认密码" type="password" autocomplete="off">
|
data-callback="onTurnstileSuccess"
|
||||||
<template #prefix>
|
></div>
|
||||||
<Icon icon="carbon:password" width="22" height="22"/>
|
|
||||||
</template>
|
|
||||||
</el-input>
|
|
||||||
<el-button class="btn" type="primary" @click="submitRegister" :loading="registerLoading"
|
<el-button class="btn" type="primary" @click="submitRegister" :loading="registerLoading"
|
||||||
>注册
|
>注册
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="switch" @click="show = 'register'" v-if="show === 'login'">还有没有账号? <span>创建账号</span></div>
|
||||||
|
<div class="switch" @click="show = 'login'" v-else>已有账号? <span>去登录</span></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<el-dialog
|
|
||||||
v-model="verifyShow"
|
|
||||||
title="验证你是不是人"
|
|
||||||
width="332"
|
|
||||||
align-center
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="register-turnstile"
|
|
||||||
:data-sitekey="settingStore.settings.siteKey"
|
|
||||||
data-callback="onTurnstileSuccess"
|
|
||||||
></div>
|
|
||||||
</el-dialog>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -132,7 +110,6 @@ const form = reactive({
|
|||||||
password: '',
|
password: '',
|
||||||
|
|
||||||
});
|
});
|
||||||
const options = [{label: '登录', value: 'login'}, {label: '注册', value: 'register'}];
|
|
||||||
const mySelect = ref()
|
const mySelect = ref()
|
||||||
const suffix = ref('')
|
const suffix = ref('')
|
||||||
const registerForm = reactive({
|
const registerForm = reactive({
|
||||||
@@ -147,6 +124,19 @@ const verifyShow = ref(false)
|
|||||||
let verifyToken = ''
|
let verifyToken = ''
|
||||||
let turnstileId = ''
|
let turnstileId = ''
|
||||||
|
|
||||||
|
|
||||||
|
window.onTurnstileSuccess = (token) => {
|
||||||
|
verifyToken = token;
|
||||||
|
setTimeout(() => {
|
||||||
|
verifyShow.value = false
|
||||||
|
}, 2000)
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const loginOpacity = computed(() => {
|
||||||
|
return `rgba(255, 255, 255, ${settingStore.settings.loginOpacity})`
|
||||||
|
})
|
||||||
|
|
||||||
const background = computed(() => {
|
const background = computed(() => {
|
||||||
|
|
||||||
return settingStore.settings.background ? {
|
return settingStore.settings.background ? {
|
||||||
@@ -157,12 +147,6 @@ const background = computed(() => {
|
|||||||
} : ''
|
} : ''
|
||||||
})
|
})
|
||||||
|
|
||||||
window.onTurnstileSuccess = (token) => {
|
|
||||||
verifyToken = token;
|
|
||||||
setTimeout(() => {
|
|
||||||
verifyShow.value = false
|
|
||||||
}, 1500)
|
|
||||||
};
|
|
||||||
|
|
||||||
const openSelect = () => {
|
const openSelect = () => {
|
||||||
mySelect.value.toggleMenu()
|
mySelect.value.toggleMenu()
|
||||||
@@ -302,53 +286,103 @@ function submitRegister() {
|
|||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.el-select-dropdown__item {
|
||||||
|
padding: 0 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-autofill-pwd {
|
||||||
|
.el-input__inner {
|
||||||
|
-webkit-text-security: disc !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
.form-wrapper {
|
.form-wrapper {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
right: 0;
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
z-index: 110;
|
z-index: 10;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
max-width: 380px;
|
background: v-bind(loginOpacity);
|
||||||
background: #FFFFFF;
|
padding-left: 40px;
|
||||||
padding: 20px;
|
padding-right: 40px;
|
||||||
margin: 0 20px;
|
|
||||||
border-radius: 8px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
width: 450px;
|
||||||
|
height: 100%;
|
||||||
border: 1px solid #e4e7ed;
|
border: 1px solid #e4e7ed;
|
||||||
box-shadow: var(--el-box-shadow-light);
|
box-shadow: var(--el-box-shadow-light);
|
||||||
.switch {
|
@media (max-width: 1024px) {
|
||||||
padding-top: 10px;
|
padding: 20px 18px;
|
||||||
font-size: 14px;
|
border-radius: 6px;
|
||||||
|
width: 384px;
|
||||||
|
margin-left: 18px;
|
||||||
|
}
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
padding: 20px 18px;
|
||||||
|
border-radius: 6px;
|
||||||
|
height: fit-content;
|
||||||
|
width: 100%;
|
||||||
|
margin-right: 18px;
|
||||||
|
margin-left: 18px;
|
||||||
|
}
|
||||||
|
.btn {
|
||||||
|
height: 36px;
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn {
|
.form-desc {
|
||||||
width: 100%;
|
margin-top: 5px;
|
||||||
|
margin-bottom: 18px;
|
||||||
|
color: #71717a;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-title {
|
.form-title {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 18px;
|
font-size: 22px !important;
|
||||||
margin-bottom: 20px;
|
}
|
||||||
|
|
||||||
|
.switch {
|
||||||
|
margin-top: 20px;
|
||||||
|
text-align: center;
|
||||||
|
span {
|
||||||
|
color: #006be6;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-input__wrapper) {
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.email-input :deep(.el-input__wrapper){
|
||||||
|
border-radius: 6px 0 0 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-input {
|
.el-input {
|
||||||
|
height: 38px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-bottom: 15px;
|
margin-bottom: 18px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:deep(.el-select-dropdown__item) {
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.setting-icon {
|
.setting-icon {
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -358,7 +392,13 @@ function submitRegister() {
|
|||||||
:deep(.el-input-group__append) {
|
:deep(.el-input-group__append) {
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
padding-left: 8px !important;
|
padding-left: 8px !important;
|
||||||
|
padding-right: 4px !important;
|
||||||
background: #FFFFFF;
|
background: #FFFFFF;
|
||||||
|
border-radius: 0 8px 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.register-turnstile {
|
||||||
|
margin-bottom: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.select {
|
.select {
|
||||||
@@ -378,6 +418,8 @@ function submitRegister() {
|
|||||||
width: 180px;
|
width: 180px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#login-box {
|
#login-box {
|
||||||
background: linear-gradient(to bottom, #2980b9, #6dd5fa, #fff);
|
background: linear-gradient(to bottom, #2980b9, #6dd5fa, #fff);
|
||||||
color: #333;
|
color: #333;
|
||||||
@@ -386,6 +428,8 @@ function submitRegister() {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -242,6 +242,7 @@ function openRoleSet(role) {
|
|||||||
roleFormShow.value = true
|
roleFormShow.value = true
|
||||||
form.sort = role.sort
|
form.sort = role.sort
|
||||||
form.name = role.name
|
form.name = role.name
|
||||||
|
form.description = role.description
|
||||||
form.sendType = role.sendType
|
form.sendType = role.sendType
|
||||||
form.sendCount = role.sendCount
|
form.sendCount = role.sendCount
|
||||||
form.accountCount = role.accountCount
|
form.accountCount = role.accountCount
|
||||||
|
|||||||
@@ -85,6 +85,12 @@
|
|||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="title-item"><span>登录透明</span></div>
|
||||||
|
<div>
|
||||||
|
<el-input-number size="small" v-model="loginOpacity" @change="opacityChange" :precision="2" :step="0.01" :max="1" :min="0" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="setting-item personalized">
|
<div class="setting-item personalized">
|
||||||
<div><span>登录背景</span></div>
|
<div><span>登录背景</span></div>
|
||||||
<div>
|
<div>
|
||||||
@@ -211,16 +217,21 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="concerning-item">
|
<div class="concerning-item">
|
||||||
<span>交流:</span>
|
<span>交流:</span>
|
||||||
<el-button @click="jumpTelegram">
|
<el-button @click="jump('https://t.me/cloud_mail_tg')">
|
||||||
telegram
|
telegram
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<Icon icon="logos:telegram" width="30" height="30"/>
|
<Icon icon="logos:telegram" width="30" height="30"/>
|
||||||
</template>
|
</template>
|
||||||
</el-button>
|
</el-button>
|
||||||
|
<el-button @click="jump('https://github.com/LaziestRen/cloud-mail')">
|
||||||
|
github
|
||||||
|
<template #icon>
|
||||||
|
<Icon icon="codicon:github-inverted" width="22" height="22" />
|
||||||
|
</template>
|
||||||
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@@ -296,6 +307,7 @@ import {Icon} from "@iconify/vue";
|
|||||||
import {compressImage} from "@/utils/file-utils.js";
|
import {compressImage} from "@/utils/file-utils.js";
|
||||||
import {cvtR2Url} from "@/utils/convert.js";
|
import {cvtR2Url} from "@/utils/convert.js";
|
||||||
import {storeToRefs} from "pinia";
|
import {storeToRefs} from "pinia";
|
||||||
|
import { debounce } from 'lodash-es'
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'sys-setting'
|
name: 'sys-setting'
|
||||||
@@ -315,7 +327,8 @@ const {settings: setting} = storeToRefs(settingStore);
|
|||||||
const editTitle = ref('')
|
const editTitle = ref('')
|
||||||
const settingLoading = ref(false)
|
const settingLoading = ref(false)
|
||||||
const r2DomainInput = ref('')
|
const r2DomainInput = ref('')
|
||||||
let backup = {}
|
const loginOpacity = ref(0)
|
||||||
|
let backup = '{}'
|
||||||
const resendTokenForm = reactive({
|
const resendTokenForm = reactive({
|
||||||
domain: '',
|
domain: '',
|
||||||
token: '',
|
token: '',
|
||||||
@@ -336,6 +349,18 @@ const options = [
|
|||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
resendTokenForm.domain = settingStore.domainList[0];
|
resendTokenForm.domain = settingStore.domainList[0];
|
||||||
|
loginOpacity.value = settingStore.settings.loginOpacity
|
||||||
|
})
|
||||||
|
|
||||||
|
function doOpacityChange() {
|
||||||
|
const form = {...setting.value}
|
||||||
|
form.loginOpacity = loginOpacity.value
|
||||||
|
editSetting(form,true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const opacityChange = debounce(doOpacityChange, 1000, {
|
||||||
|
leading: false,
|
||||||
|
trailing: true
|
||||||
})
|
})
|
||||||
|
|
||||||
function physicsDeleteAllData() {
|
function physicsDeleteAllData() {
|
||||||
@@ -468,14 +493,15 @@ function saveTitle() {
|
|||||||
editSetting({title: editTitle.value})
|
editSetting({title: editTitle.value})
|
||||||
}
|
}
|
||||||
|
|
||||||
function jumpTelegram() {
|
function jump(href) {
|
||||||
const doc = document.createElement('a')
|
const doc = document.createElement('a')
|
||||||
doc.href = 'https://t.me/cloud_mail_tg'
|
doc.href = href
|
||||||
doc.target = '_blank'
|
doc.target = '_blank'
|
||||||
doc.click()
|
doc.click()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function editSetting(settingForm, refreshStatus = true) {
|
function editSetting(settingForm, refreshStatus = true) {
|
||||||
if (settingLoading.value) return
|
if (settingLoading.value) return
|
||||||
settingLoading.value = true
|
settingLoading.value = true
|
||||||
@@ -497,6 +523,7 @@ function editSetting(settingForm, refreshStatus = true) {
|
|||||||
resendTokenFormShow.value = false
|
resendTokenFormShow.value = false
|
||||||
turnstileShow.value = false
|
turnstileShow.value = false
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
|
loginOpacity.value = setting.value.loginOpacity
|
||||||
setting.value = {...setting.value, ...JSON.parse(backup)}
|
setting.value = {...setting.value, ...JSON.parse(backup)}
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
settingLoading.value = false
|
settingLoading.value = false
|
||||||
@@ -535,8 +562,8 @@ function editSetting(settingForm, refreshStatus = true) {
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
border: 1px solid #e4e7ed;
|
border: 1px solid #e4e7ed;
|
||||||
@media (max-width: 500px) {
|
@media (max-width: 500px) {
|
||||||
width: 140px;
|
width: 150px;
|
||||||
height: 79px;
|
height: 83px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -702,7 +729,7 @@ function editSetting(settingForm, refreshStatus = true) {
|
|||||||
display: grid;
|
display: grid;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
grid-template-columns: 1fr auto;
|
grid-template-columns: 1fr auto;
|
||||||
|
align-items: center;
|
||||||
span {
|
span {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
|||||||
@@ -728,7 +728,7 @@ adjustWidth()
|
|||||||
|
|
||||||
function adjustWidth() {
|
function adjustWidth() {
|
||||||
const width = window.innerWidth
|
const width = window.innerWidth
|
||||||
statusShow.value = width > 1060
|
statusShow.value = width > 1090
|
||||||
createTimeShow.value = width > 1200
|
createTimeShow.value = width > 1200
|
||||||
accountNumShow.value = width > 650
|
accountNumShow.value = width > 650
|
||||||
sendNumShow.value = width > 685
|
sendNumShow.value = width > 685
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
4
mail-worker/dist/index.html
vendored
4
mail-worker/dist/index.html
vendored
@@ -6,8 +6,8 @@
|
|||||||
<title></title>
|
<title></title>
|
||||||
<link rel="icon" href="/assets/favicon-C5dAZutX.svg" type="image/svg+xml">
|
<link rel="icon" href="/assets/favicon-C5dAZutX.svg" type="image/svg+xml">
|
||||||
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
|
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
|
||||||
<script type="module" crossorigin src="/assets/index-BMdBWW7j.js"></script>
|
<script type="module" crossorigin src="/assets/index-DQ5PlDSG.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-Blmh8UBo.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-6pbK1Sk3.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="loading-first">
|
<div id="loading-first">
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export const setting = sqliteTable('setting', {
|
|||||||
secretKey: text('secret_key'),
|
secretKey: text('secret_key'),
|
||||||
siteKey: text('site_key'),
|
siteKey: text('site_key'),
|
||||||
background: text('background'),
|
background: text('background'),
|
||||||
|
loginOpacity: integer('login_opacity').default(0.9),
|
||||||
resendTokens: text('resend_tokens').default("{}").notNull(),
|
resendTokens: text('resend_tokens').default("{}").notNull(),
|
||||||
});
|
});
|
||||||
export default setting
|
export default setting
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ const initService = {
|
|||||||
`ALTER TABLE setting ADD COLUMN site_key TEXT;`,
|
`ALTER TABLE setting ADD COLUMN site_key TEXT;`,
|
||||||
`ALTER TABLE setting ADD COLUMN secret_key TEXT;`,
|
`ALTER TABLE setting ADD COLUMN secret_key TEXT;`,
|
||||||
`ALTER TABLE setting ADD COLUMN background TEXT;`,
|
`ALTER TABLE setting ADD COLUMN background TEXT;`,
|
||||||
|
`ALTER TABLE setting ADD COLUMN login_opacity INTEGER NOT NULL DEFAULT 0.88;`,
|
||||||
|
|
||||||
`ALTER TABLE user ADD COLUMN create_ip TEXT;`,
|
`ALTER TABLE user ADD COLUMN create_ip TEXT;`,
|
||||||
`ALTER TABLE user ADD COLUMN active_ip TEXT;`,
|
`ALTER TABLE user ADD COLUMN active_ip TEXT;`,
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ const loginService = {
|
|||||||
|
|
||||||
const userId = await userService.insert(c, { email, password: hash, salt, type: roleRow.roleId });
|
const userId = await userService.insert(c, { email, password: hash, salt, type: roleRow.roleId });
|
||||||
|
|
||||||
await userService.updateUserInfo(c, userId);
|
await userService.updateUserInfo(c, userId, true);
|
||||||
|
|
||||||
await accountService.insert(c, { userId: userId, email });
|
await accountService.insert(c, { userId: userId, email });
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user