代码分离,功能优化 (#204)

* feat: 代码分离,功能优化

1. 代码迁移到 ts.
2. 分离 axios 部分代码.
3. 增加 pinia 支持,全局状态代码迁移到相对应的 store.
4. 代码格式优化, 用 === 代替 ==.
5. 代码声明更改,用 const 代替 var 声明.
6. Header 使用 Router 导航.
7. v-for 增加 key.

* fix[fe]: 移除过时的 prop 引用

* fix[fe]: 移除过时的 prop 引用

* fix[fe]: 修复 logo 上面有横线的问题

* fix[fe]: 修复 logo 上面有横线的问题

---------

Co-authored-by: zhe28 <huangze28@foxmail.com>
This commit is contained in:
HuangZhe 2024-09-23 10:24:49 +08:00 committed by GitHub
parent dbb671df67
commit ad0167f6fd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 1640 additions and 1638 deletions

View File

@ -15,6 +15,7 @@
"element-plus": "^2.3.6",
"pinia": "^2.0.36",
"vue": "^3.3.2",
"vue-icons-plus": "^0.1.6",
"vue-router": "^4.2.0"
},
"devDependencies": {
@ -23,6 +24,7 @@
"eslint-plugin-vue": "^9.11.0",
"unplugin-auto-import": "^0.16.4",
"unplugin-vue-components": "^0.25.0",
"vite": "^4.3.5"
"vite": "^4.3.5",
"vue-tsc": "^2.1.6"
}
}

View File

@ -1,16 +1,16 @@
<script setup>
import { RouterView } from 'vue-router'
import {RouterView, useRoute} from 'vue-router'
import HomeHeader from '@/components/HomeHeader.vue'
import HomeAside from '@/components/HomeAside.vue';
import { watch, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import {ref, watch} from 'vue'
const route = useRoute()
const pageName = ref(route.name)
watch(
() => route.fullPath,
(n, o) => {
() => {
pageName.value = route.name
}
)
@ -19,13 +19,13 @@ watch(
<template>
<div id="main">
<HomeHeader />
<HomeHeader/>
<div id="content">
<div id="aside" v-if="pageName != 'login' && pageName != 'setup'">
<HomeAside />
<div id="aside" v-if="pageName !== 'login' && pageName !== 'setup'">
<HomeAside/>
</div>
<div id="body">
<RouterView />
<RouterView/>
</div>
</div>
</div>

View File

@ -1,13 +1,36 @@
<template>
<div id="main">
<el-tree :expand-on-click-node="false" :data="data" :props="defaultProps" :defaultExpandAll="true" :class="node">
<el-tree
:expand-on-click-node="false"
:data="data"
:defaultExpandAll="true"
>
<template #default="{ node, data }">
<div>
<span v-if="data.id != -1"> {{ data.label }}</span>
<el-input v-if="data.id == -1" v-model="data.label" @blur="onInputBlur(data)"></el-input>
<el-button v-if="data.id != 0" @click="del(node, data)" size="small" type="danger" circle> -
<span v-if="data.id !== -1"> {{ data.label }}</span>
<el-input
v-if="data.id === -1"
v-model="data.label"
@blur="onInputBlur(data)"
></el-input>
<el-button
v-if="data.id !== 0"
@click="del(node, data)"
size="small"
type="danger"
circle
>
-
</el-button>
<el-button
v-if="data.id !== 0"
@click="add(data)"
size="small"
type="primary"
circle
>
+
</el-button>
<el-button v-if="data.id != 0" @click="add(data)" size="small" type="primary" circle> + </el-button>
</div>
</template>
</el-tree>
@ -17,82 +40,84 @@
</template>
<script setup>
import { reactive, ref, getCurrentInstance } from 'vue'
import lang from '../i18n/i18n';
import { reactive } from "vue";
import lang from "../i18n/i18n";
import { http } from "@/utils/axios";
import { ElMessage } from "element-plus";
const data = reactive([])
const app = getCurrentInstance()
const $http = app.appContext.config.globalProperties.$http
const data = reactive([]);
$http.get('/api/group').then(res => {
data.push(...res.data)
})
http.get("/api/group").then((res) => {
data.push(...res.data);
});
const del = function (node, data) {
if (data.id != -1) {
this.$axios.post("/api/group/del", { "id": data.id }).then(res => {
if (res.errorNo != 0) {
if (data.id !== -1) {
this.$axios.post("/api/group/del", { id: data.id }).then((res) => {
if (res.errorNo !== 0) {
ElMessage({
message: res.errorMsg,
type: 'error',
})
type: "error",
});
} else {
const pc = node.parent.childNodes
const pc = node.parent.childNodes;
for (let i = 0; i < pc.length; i++) {
if (pc[i].id == node.id) {
pc.splice(i, 1)
return
if (pc[i].id === node.id) {
pc.splice(i, 1);
return;
}
}
}
})
});
} else {
const pc = node.parent.childNodes
const pc = node.parent.childNodes;
for (let i = 0; i < pc.length; i++) {
if (pc[i].id == node.id) {
pc.splice(i, 1)
return
if (pc[i].id === node.id) {
pc.splice(i, 1);
return;
}
}
}
}
};
const add = function (item) {
if (item.children == null) {
item.children = []
item.children = [];
}
item.children.push({
"children": [],
"label": "",
"id": "-1",
"parent_id": item.id
})
}
children: [],
label: "",
id: "-1",
parent_id: item.id,
});
};
const addRoot = function () {
data.push({
"children": [],
"label": "",
"id": "-1",
"parent_id": 0
})
}
children: [],
label: "",
id: "-1",
parent_id: 0,
});
};
const onInputBlur = function (item) {
if (item.label != "") {
this.$axios.post("/api/group/add", { "name": item.label, "parent_id": item.parent_id }).then(res => {
if (res.errorNo != 0) {
if (item.label !== "") {
http
.post("/api/group/add", { name: item.label, parent_id: item.parent_id })
.then((res) => {
if (res.errorNo !== 0) {
ElMessage({
message: res.errorMsg,
type: 'error',
})
type: "error",
});
} else {
this.$axios.get('/api/group').then(res => {
data.splice(0, data.length)
data.push(...res.data)
})
this.$axios.get("/api/group").then((res) => {
data.splice(0, data.length);
data.push(...res.data);
});
}
})
});
}
}
};
</script>

View File

@ -1,58 +1,49 @@
<template>
<div id="main">
<input id="search" :placeholder="lang.search">
<el-tree :data="data" :props="defaultProps" :defaultExpandAll="true" @node-click="handleNodeClick" :class="node" />
<input id="search" :placeholder="lang.search" />
<el-tree
:data="data"
:defaultExpandAll="true"
@node-click="handleNodeClick"
/>
</div>
</template>
<script setup>
import { useRouter } from 'vue-router'
import { reactive, ref } from 'vue'
import useGroupStore from '../stores/group'
import lang from '../i18n/i18n';
import { getCurrentInstance } from 'vue'
const app = getCurrentInstance()
const $http = app.appContext.config.globalProperties.$http
const groupStore = useGroupStore()
const router = useRouter()
const data = ref([])
$http.get('/api/group').then(res => {
data.value = res.data
})
import { useRouter } from "vue-router";
import { ref } from "vue";
import useGroupStore from "../stores/group";
import lang from "../i18n/i18n";
import { http } from "@/utils/axios";
const groupStore = useGroupStore();
const router = useRouter();
const data = ref([]);
http.get("/api/group").then((res) => {
if (res.data) data.value = res.data;
});
const handleNodeClick = function (data) {
if (data.tag != null) {
groupStore.name = data.label
groupStore.tag = data.tag
groupStore.name = data.label;
groupStore.tag = data.tag;
router.push({
name: "list",
})
});
}
}
};
</script>
<style scoped>
#main {
width: 243px;
background-color: #F1F1F1;
background-color: #f1f1f1;
height: 100%;
}
#search {
background-color: #D6E7F7;
background-color: #d6e7f7;
width: 100%;
height: 40px;
padding-left: 10px;
@ -62,10 +53,10 @@ const handleNodeClick = function (data) {
}
.el-tree {
background-color: #F1F1F1;
background-color: #f1f1f1;
}
.add_group{
.add_group {
font-size: 14px;
text-align: left;
padding-left: 15px;

View File

@ -1,33 +1,35 @@
<template>
<div id="header_main">
<div id="logo">
<span style="padding-left: 20px;">PMail</span>
<router-link to="/" style="text-underline: none">
<el-text :line-clamp="1" size="large"><h1>Pmail</h1></el-text>
</router-link>
</div>
<div id="settings" @click="settings" v-if="$isLogin">
<div id="settings" @click="settings" v-if="isLogin">
<el-icon style="font-size: 25px;">
<Setting style="color:#FFFFFF" />
<TbSettings style="color:#FFFFFF"/>
</el-icon>
</div>
<el-drawer v-model="openSettings" size="80%" :title="lang.settings">
<el-tabs tab-position="left">
<el-tab-pane :label="lang.security">
<SecuritySettings />
<SecuritySettings/>
</el-tab-pane>
<el-tab-pane :label="lang.group_settings">
<GroupSettings />
<GroupSettings/>
</el-tab-pane>
<el-tab-pane :label="lang.rule_setting">
<RuleSettings />
<RuleSettings/>
</el-tab-pane>
<el-tab-pane v-if="$userInfos.is_admin" :label="lang.user_management">
<UserManagement />
<el-tab-pane v-if="userInfos.is_admin" :label="lang.user_management">
<UserManagement/>
</el-tab-pane>
<el-tab-pane :label="lang.plugin_settings">
<PluginSettings />
<PluginSettings/>
</el-tab-pane>
</el-tabs>
@ -37,29 +39,30 @@
</template>
<script setup>
import { Setting } from '@element-plus/icons-vue';
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
import {TbSettings} from "vue-icons-plus/tb";
import {ref} from 'vue'
import {ElMessage} from 'element-plus'
import SecuritySettings from '@/components/SecuritySettings.vue'
import lang from '../i18n/i18n';
import GroupSettings from './GroupSettings.vue';
import RuleSettings from './RuleSettings.vue';
import UserManagement from './UserManagement.vue';
import { getCurrentInstance } from 'vue'
import PluginSettings from './PluginSettings.vue';
const app = getCurrentInstance()
const $http = app.appContext.config.globalProperties.$http
const $isLogin = app.appContext.config.globalProperties.$isLogin
const $userInfos = app.appContext.config.globalProperties.$userInfos
import {http} from "@/utils/axios";
import {useGlobalStatusStore} from "@/stores/useGlobalStatusStore";
const globalStatus = useGlobalStatusStore();
const isLogin = globalStatus.isLogin;
const userInfos = globalStatus.userInfos;
const openSettings = ref(false)
const settings = function () {
if (Object.keys($userInfos.value).length == 0) {
$http.post("/api/user/info", {}).then(res => {
if (res.errorNo == 0) {
$userInfos.value = res.data
if (Object.keys(userInfos).length === 0) {
http.post("/api/user/info", {}).then(res => {
if (res.errorNo === 0) {
userInfos.value = res.data
openSettings.value = true;
} else {
ElMessage({
type: 'error',
@ -67,18 +70,18 @@ const settings = function () {
})
}
})
}else{
} else {
openSettings.value = true;
}
}
</script>
<style scoped>
#header_main {
height: 50px;
background-color: #000;
@ -96,6 +99,11 @@ const settings = function () {
text-align: left;
}
#logo h1 {
padding-left: 20px;
color: white;
}
#search {
height: 3rem;
width: 100%;

View File

@ -1,7 +1,7 @@
<template>
<div id="main">
<el-tabs>
<el-tab-pane v-for="(src, name) in pluginList" :label="name">
<el-tab-pane v-for="(src, name) in pluginList" :key="src" :label="name">
<iframe :src="src"></iframe>
</el-tab-pane>
</el-tabs>
@ -9,16 +9,16 @@
</template>
<script setup>
import { reactive, ref, getCurrentInstance } from 'vue'
const app = getCurrentInstance()
const $http = app.appContext.config.globalProperties.$http
import {reactive} from 'vue'
import {http} from "@/utils/axios";
const pluginList = reactive({})
$http.get('/api/plugin/list').then(res => {
http.get('/api/plugin/list').then(res => {
if (res.data != null && res.data.length > 0) {
for (let i = 0; i < res.data.length; i++) {
let name = res.data[i];
pluginList[name] = "/api/plugin/settings/"+ name +"/index.html";
pluginList[name] = "/api/plugin/settings/" + name + "/index.html";
}
}
})
@ -27,7 +27,7 @@ $http.get('/api/plugin/list').then(res => {
<style scoped>
iframe{
iframe {
width: 100%;
border: 0;
}

View File

@ -1,22 +1,22 @@
<template>
<el-table :data="data" :show-header="true">
<el-table-column prop="id" label="id" />
<el-table-column prop="name" :label="lang.rule_name" />
<el-table-column prop="id" label="id"/>
<el-table-column prop="name" :label="lang.rule_name"/>
<el-table-column prop="action" :label="lang.rule_do">
<template #default="scope">
{{ ActionName[scope.row.action] }}
</template>
</el-table-column>
<el-table-column prop="params" :label="lang.rule_params" />
<el-table-column prop="sort" :label="lang.rule_priority" />
<el-table-column prop="params" :label="lang.rule_params"/>
<el-table-column prop="sort" :label="lang.rule_priority"/>
<el-table-column>
<template #default="scope">
<div style="display: flex; align-items: center">
<el-button size="small" type="primary" :icon="Edit" circle @click="editRule(scope.row)" />
<el-button size="small" type="primary" :icon="Edit" circle @click="editRule(scope.row)"/>
<el-popconfirm confirm-button-text="Yes" cancel-button-text="No, Thanks" :icon="InfoFilled"
@confirm="delRule(scope.row.id)" icon-color="#626AEF" :title="lang.del_rule_confirm">
<template #reference>
<el-button size="small" type="danger" :icon="Delete" circle />
<el-button size="small" type="danger" :icon="Delete" circle/>
</template>
</el-popconfirm>
</div>
@ -29,58 +29,56 @@
</div>
<el-dialog v-model="dialogVisible" :title="lang.new_rule" width="60%">
<div style="text-align: left; padding-left: 20px;">
<el-form v-model="addRuleForm" :inline="true" label-position="top">
<el-form-item style="width: 400px;" :label="lang.rule_name">
<el-input v-model="addRuleForm.name" />
<el-input v-model="addRuleForm.name"/>
</el-form-item>
<el-form-item :label="lang.rule_priority">
<el-input v-model="addRuleForm.sort" type="number" oninput="value=value.replace(/[^\-\d]/g, '')" />
<el-input v-model="addRuleForm.sort" type="number" oninput="value=value.replace(/[^\-\d]/g, '')"/>
</el-form-item>
<el-divider />
<el-divider/>
<div style="width: 100%;">{{ lang.rule_desc }}</div>
<div style="width: 100%;">
<div v-for="(rule, index) in addRuleForm.rules">
<div v-for="(rule, index) in addRuleForm.rules" :key="index">
<el-select v-model="rule.field" placeholder="Select">
<el-option key="From" :label="lang.from" value="From" />
<el-option key="Subject" :label="lang.subject" value="Subject" />
<el-option key="To" :label="lang.to" value="To" />
<el-option key="Cc" :label="lang.cc" value="Cc" />
<el-option key="Content" :label="lang.content" value="Content" />
<el-option key="From" :label="lang.from" value="From"/>
<el-option key="Subject" :label="lang.subject" value="Subject"/>
<el-option key="To" :label="lang.to" value="To"/>
<el-option key="Cc" :label="lang.cc" value="Cc"/>
<el-option key="Content" :label="lang.content" value="Content"/>
</el-select>
<el-select v-model="rule.type" placeholder="Select">
<el-option key="equal" :label="lang.equal" value="equal" />
<el-option key="contains" :label="lang.contains" value="contains" />
<el-option key="regex" :label="lang.regex" value="regex" />
<el-option key="equal" :label="lang.equal" value="equal"/>
<el-option key="contains" :label="lang.contains" value="contains"/>
<el-option key="regex" :label="lang.regex" value="regex"/>
</el-select>
<el-input v-model="rule.rule" style="width: 350px;" />
<el-button size="small" type="danger" :icon="Delete" @click="removeRuleLine(index)" circle />
<el-input v-model="rule.rule" style="width: 350px;"/>
<el-button size="small" type="danger" :icon="Delete" @click="removeRuleLine(index)" circle/>
</div>
</div>
<div style="padding-top: 7px;">
<el-button size="small" type="primary" :icon="Plus" circle @click="addRule()" />
<el-button size="small" type="primary" :icon="Plus" circle @click="addRule()"/>
</div>
<el-divider />
<el-divider/>
<div style="width: 100%;">{{ lang.rule_do }}</div>
<el-form-item>
<el-select v-model="addRuleForm.action" placeholder="Select" @change="ruleTypeChange()">
<el-option key="mark_read" :label="lang.mark_read" :value="READ" />
<el-option key="move" :label="lang.move" :value="MOVE" />
<el-option key="delete" :label="lang.delete" :value="DELETE" />
<el-option key="forward" :label="lang.forward" :value="FORWARD" />
<el-option key="mark_read" :label="lang.mark_read" :value="READ"/>
<el-option key="move" :label="lang.move" :value="MOVE"/>
<el-option key="delete" :label="lang.delete" :value="DELETE"/>
<el-option key="forward" :label="lang.forward" :value="FORWARD"/>
</el-select>
<el-select v-if="addRuleForm.action == 4" v-model="addRuleForm.params" @click="reflushGroupInfos">
<el-option v-for="gp in groupData.list" :key="gp.id" :label="gp.name" :value="gp.id" />
<el-select v-if="addRuleForm.action === 4" v-model="addRuleForm.params" @click="reflushGroupInfos">
<el-option v-for="gp in groupData.list" :key="gp.id" :label="gp.name" :value="gp.id"/>
</el-select>
<el-input v-if="addRuleForm.action == 2" v-model="addRuleForm.params" style="width: 250px;"
placeholder="Forward Email Address" />
<el-input v-if="addRuleForm.action === 2" v-model="addRuleForm.params" style="width: 250px;"
placeholder="Forward Email Address"/>
</el-form-item>
@ -97,19 +95,11 @@
</template>
<script setup>
import { ref, reactive } from 'vue';
import {reactive, ref} from 'vue';
import lang from '../i18n/i18n';
import {
Plus,
Delete,
Edit,
InfoFilled
} from '@element-plus/icons-vue'
import { getCurrentInstance } from 'vue'
const app = getCurrentInstance()
const $http = app.appContext.config.globalProperties.$http
import {Delete, Edit, InfoFilled, Plus} from '@element-plus/icons-vue'
import {http} from "@/utils/axios";
import {ElNotification} from "element-plus";
const data = ref([])
const dialogVisible = ref(false)
@ -126,7 +116,7 @@ const ActionName = {
}
const init = function () {
$http.post("/api/rule/get").then((res) => {
http.post("/api/rule/get").then((res) => {
data.value = res.data
})
}
@ -138,7 +128,7 @@ const groupData = reactive({
})
const reflushGroupInfos = function () {
$http.get('/api/group/list').then(res => {
http.get('/api/group/list').then(res => {
if (res.data != null) {
groupData.list = res.data
for (let i = 0; i < groupData.list.length; i++) {
@ -167,11 +157,11 @@ const addRuleForm = reactive({
})
const delRule = function (id) {
$http.post("/api/rule/del", { "id": id }).then((res) => {
http.post("/api/rule/del", {"id": id}).then((res) => {
ElNotification({
title: res.errorNo == 0 ? lang.succ : lang.fail,
title: res.errorNo === 0 ? lang.succ : lang.fail,
message: res.data,
type: res.errorNo == 0 ? 'success' : 'error',
type: res.errorNo === 0 ? 'success' : 'error',
})
init()
@ -210,8 +200,8 @@ const submitRule = function () {
addRuleForm.sort = parseInt(addRuleForm.sort)
$http.post(api, addRuleForm).then((res) => {
if (res.errorNo != 0) {
http.post(api, addRuleForm).then((res) => {
if (res.errorNo !== 0) {
ElNotification({
title: lang.fail,
message: res.data,
@ -238,7 +228,6 @@ const submitRule = function () {
}
const ruleTypeChange = function () {
addRuleForm.params = ''
}

View File

@ -1,14 +1,14 @@
<template>
<el-form :model="ruleForm" :rules="rules" status-icon>
<el-divider content-position="left">{{lang.modify_pwd}}</el-divider>
<el-divider content-position="left">{{ lang.modify_pwd }}</el-divider>
<el-form-item :label="lang.modify_pwd" prop="new_pwd">
<el-input type="password" v-model="ruleForm.new_pwd" />
<el-input type="password" v-model="ruleForm.new_pwd"/>
</el-form-item>
<el-form-item :label="lang.enter_again" prop="new_pwd2">
<el-input type="password" v-model="ruleForm.new_pwd2" />
<el-input type="password" v-model="ruleForm.new_pwd2"/>
</el-form-item>
<el-form-item>
@ -17,7 +17,7 @@
</el-button>
</el-form-item>
<el-divider content-position="left">{{lang.logout}}</el-divider>
<el-divider content-position="left">{{ lang.logout }}</el-divider>
<el-form-item>
<el-button type="primary" @click="logout">
{{ lang.logout }}
@ -27,13 +27,10 @@
</template>
<script setup>
import { reactive, ref } from 'vue'
import { ElNotification } from 'element-plus'
import {reactive} from 'vue'
import {ElNotification} from 'element-plus'
import lang from '../i18n/i18n';
import { getCurrentInstance } from 'vue'
const app = getCurrentInstance()
const $http = app.appContext.config.globalProperties.$http
import {http} from "@/utils/axios";
const ruleForm = reactive({
new_pwd: "",
@ -41,24 +38,24 @@ const ruleForm = reactive({
})
const rules = reactive({
new_pwd: [{ required: true, message: lang.err_required_pwd, trigger: 'blur' },],
new_pwd2: [{ required: true, message: lang.err_required_pwd, trigger: 'blur' },],
new_pwd: [{required: true, message: lang.err_required_pwd, trigger: 'blur'},],
new_pwd2: [{required: true, message: lang.err_required_pwd, trigger: 'blur'},],
})
const logout = function(){
$http.post("/api/logout", { }).then(res => {
const logout = function () {
http.post("/api/logout", {}).then(() => {
location.reload();
})
}
const submit = function () {
if (ruleForm.new_pwd == ""){
if (ruleForm.new_pwd === "") {
return
}
if (ruleForm.new_pwd != ruleForm.new_pwd2) {
if (ruleForm.new_pwd !== ruleForm.new_pwd2) {
ElNotification({
title: 'Error',
message: lang.err_pwd_diff,
@ -66,11 +63,11 @@ const submit = function () {
})
return
}
$http.post("/api/settings/modify_password", { password: ruleForm.new_pwd }).then(res => {
http.post("/api/settings/modify_password", {password: ruleForm.new_pwd}).then(res => {
ElNotification({
title: res.errorNo == 0 ? lang.succ : lang.fail,
title: res.errorNo === 0 ? lang.succ : lang.fail,
message: res.data,
type: res.errorNo == 0 ? 'success' : 'error',
type: res.errorNo === 0 ? 'success' : 'error',
})
})

View File

@ -1,12 +1,12 @@
<template>
<div id="main">
<el-table :data="userList" style="width: 100%">
<el-table-column label="ID" prop="ID" />
<el-table-column :label="lang.account" prop="Account" />
<el-table-column :label="lang.user_name" prop="Name" />
<el-table-column label="ID" prop="ID"/>
<el-table-column :label="lang.account" prop="Account"/>
<el-table-column :label="lang.user_name" prop="Name"/>
<el-table-column :label="lang.disabled" prop="Disabled">
<template #default="scope">
<span>{{ scope.row.Disabled == 1 ? lang.disabled : lang.enabled }}</span>
<span>{{ scope.row.Disabled === 1 ? lang.disabled : lang.enabled }}</span>
</template>
</el-table-column>
<el-table-column align="right">
@ -24,30 +24,29 @@
</el-table>
<div id="paginationBox">
<el-pagination v-model:current-page="currentPage" small background layout="prev, pager, next"
:page-count="totalPage" class="mt-4" @current-change="reflushList" />
:page-count="totalPage" class="mt-4" @current-change="reflushList"/>
</div>
<el-dialog v-model="userInfoDialog" :title="title" width="500">
<el-form>
<el-form-item label-width="100px" :label="lang.account">
<el-input :disabled="editModel == 'edit'" v-model="editUserInfo.account" autocomplete="off" />
<el-input :disabled="editModel === 'edit'" v-model="editUserInfo.account" autocomplete="off"/>
</el-form-item>
<el-form-item label-width="100px" :label="lang.user_name">
<el-input v-model="editUserInfo.name" autocomplete="off" />
<el-input v-model="editUserInfo.name" autocomplete="off"/>
</el-form-item>
<el-form-item label-width="100px" :label="lang.password">
<el-input :placeholder="lang.resetPwd" v-model="editUserInfo.password" autocomplete="off" />
<el-input :placeholder="lang.resetPwd" v-model="editUserInfo.password" autocomplete="off"/>
</el-form-item>
<div style="display: flex;">
<div
style="display: inline-flex;justify-content: flex-end;align-items: flex-start;flex: 0 0 auto;font-size: var(--el-form-label-font-size); height: 32px;line-height: 32px;padding: 0 12px 0 60px;box-sizing: border-box; ">
<el-switch v-model="editUserInfo.disabled" class="ml-2" :active-text="lang.disabled"
:inactive-text="lang.enabled" />
:inactive-text="lang.enabled"/>
</div>
@ -70,11 +69,12 @@
<script setup>
import { reactive, ref, getCurrentInstance } from 'vue'
import {reactive, ref} from 'vue'
import lang from '../i18n/i18n';
import {http} from "@/utils/axios";
import {ElNotification} from "element-plus";
const userList = reactive([])
const app = getCurrentInstance()
const $http = app.appContext.config.globalProperties.$http
const currentPage = ref(1)
const totalPage = ref(1)
const userInfoDialog = ref(false)
@ -88,7 +88,7 @@ const editUserInfo = reactive({
const title = ref(lang.editUser)
const reflushList = function () {
$http.post('/api/user/list', { "current_page": currentPage.value, "page_size": 10 }).then(res => {
http.post('/api/user/list', {"current_page": currentPage.value, "page_size": 10}).then(res => {
userList.length = 0
totalPage.value = res.data.total_page
@ -100,14 +100,14 @@ const reflushList = function () {
const handleEdit = function (idx, row) {
editUserInfo.account = row.Account
editUserInfo.name = row.Name
editUserInfo.disabled = row.Disabled == 1
editUserInfo.disabled = row.Disabled === 1
editUserInfo.password = ""
editModel.value = "edit"
title.value = lang.editUser
userInfoDialog.value = true
}
const createUser = function(){
const createUser = function () {
editUserInfo.account = ""
editUserInfo.name = ""
editUserInfo.disabled = false
@ -119,50 +119,48 @@ const createUser = function(){
const submit = function () {
if (editModel.value == 'edit') {
if (editModel.value === 'edit') {
let newData = {
"account": editUserInfo.account,
"username": editUserInfo.name,
"disabled": editUserInfo.disabled ? 1 : 0
}
if (editUserInfo.password != "") {
if (editUserInfo.password !== "") {
newData["password"] = editUserInfo.password
}
$http.post('/api/user/edit', newData).then(res => {
http.post('/api/user/edit', newData).then(res => {
ElNotification({
title: res.errorNo == 0 ? lang.succ : lang.fail,
message: res.errorNo == 0 ? "" : res.data,
type: res.errorNo == 0 ? 'success' : 'error',
title: res.errorNo === 0 ? lang.succ : lang.fail,
message: res.errorNo === 0 ? "" : res.data,
type: res.errorNo === 0 ? 'success' : 'error',
})
if (res.errorNo == 0) {
if (res.errorNo === 0) {
reflushList()
userInfoDialog.value = false
}
})
}else{
} else {
let newData = {
"account": editUserInfo.account,
"username": editUserInfo.name,
"disabled": editUserInfo.disabled ? 1 : 0,
"password":editUserInfo.password
"password": editUserInfo.password
}
$http.post('/api/user/create', newData).then(res => {
http.post('/api/user/create', newData).then(res => {
ElNotification({
title: res.errorNo == 0 ? lang.succ : lang.fail,
message: res.errorNo == 0 ? "" : res.data,
type: res.errorNo == 0 ? 'success' : 'error',
title: res.errorNo === 0 ? lang.succ : lang.fail,
message: res.errorNo === 0 ? "" : res.data,
type: res.errorNo === 0 ? 'success' : 'error',
})
if (res.errorNo == 0) {
if (res.errorNo === 0) {
reflushList()
userInfoDialog.value = false
}
})
}
}

View File

@ -1,4 +1,4 @@
var lang = {
let lang = {
"logout": "Logout",
"resetPwd": "Reset the account password",
"disabled": "Disabled",
@ -71,11 +71,11 @@ var lang = {
"ssl_auto": "Automatically configure SSL certificates (recommended)",
"wait_desc": "Please Wait.",
"dns_challenge_wait": "DNS propagation and cache refreshes take a long time, and a wait of 10-30 minutes is possible here.",
"ssl_challenge_type":"Challenge Type",
"ssl_auto_http":"Http Request",
"ssl_auto_dns":"DNS Records",
"ssl_challenge_type": "Challenge Type",
"ssl_auto_http": "Http Request",
"ssl_auto_dns": "DNS Records",
"challenge_typ_desc": "If PMail uses port 80 directly, it is recommended that you use the HTTP challenge method.",
"oomain_service_provider":"Domain Name Service Provider",
"oomain_service_provider": "Domain Name Service Provider",
"ssl_manuallyf": "Manually configure an SSL certificate",
"ssl_key_path": "ssl key file path",
"ssl_crt_path": "ssl crt file path",
@ -86,32 +86,31 @@ var lang = {
"del_btn": "Delete",
"move_btn": "Move",
"read_btn": "Readed",
"dangerous":"The content of this email is not secure!",
"rule_setting":"Rules",
"new_rule":"New mail receiving rules",
"rule_name":"Rule Name",
"rule_priority":"Rule Priority(Larger values are executed first)",
"rule_desc":"When a new message arrives that meets all these conditions:",
"rule_do":"Do the following:",
"from":"From Email Address",
"subject":"Email Subject",
"content":"Email Content",
"equal":"Equal",
"regex":"Regex Match",
"contains":"Contains",
"mark_read":"Mark Read",
"delete":"Delete",
"forward":"Forward",
"move":"Move to group",
"del_rule_confirm":"Are you sure to delete this?",
"rule_params":"Executed params",
"dangerous": "The content of this email is not secure!",
"rule_setting": "Rules",
"new_rule": "New mail receiving rules",
"rule_name": "Rule Name",
"rule_priority": "Rule Priority(Larger values are executed first)",
"rule_desc": "When a new message arrives that meets all these conditions:",
"rule_do": "Do the following:",
"from": "From Email Address",
"subject": "Email Subject",
"content": "Email Content",
"equal": "Equal",
"regex": "Regex Match",
"contains": "Contains",
"mark_read": "Mark Read",
"delete": "Delete",
"forward": "Forward",
"move": "Move to group",
"del_rule_confirm": "Are you sure to delete this?",
"rule_params": "Executed params",
"autoSSLWarn": "PMail is not currently running on port 80. If you want PMail to manage SSL certificates automatically, please forward the /.well-known/* route to PMail. See https://github.com/Jinnrry/PMail/issues/94 for details.",
"err_db_dsn_empty": "Database path cannot be empty!",
};
var zhCN = {
const zhCN = {
"logout": "注销",
"resetPwd": "重置账号密码",
"disabled": "禁用",
@ -182,10 +181,10 @@ var zhCN = {
"web_domain": "Web域名地址",
"dns_desc": "请将以下信息添加到DNS记录中",
"ssl_auto": "自动配置SSL证书(推荐)",
"oomain_service_provider":"域名服务商",
"ssl_auto_http":"HTTP请求",
"ssl_auto_dns":"DNS记录",
"ssl_challenge_type":"验证方式",
"oomain_service_provider": "域名服务商",
"ssl_auto_http": "HTTP请求",
"ssl_auto_dns": "DNS记录",
"ssl_challenge_type": "验证方式",
"ssl_manuallyf": "手动配置SSL证书",
"challenge_typ_desc": "如果PMail直接使用80端口建议使用HTTP验证方式。",
"wait_desc": "请稍等",
@ -199,38 +198,29 @@ var zhCN = {
"del_btn": "删除",
"move_btn": "移动",
"read_btn": "已读",
"dangerous":"该邮件内容不安全!",
"rule_setting":"规则",
"new_rule":"新建收信规则",
"rule_name":"规则名称",
"rule_priority":"优先级(值越大越先执行)",
"rule_desc":"以下规则全部满足时:",
"rule_do":"执行操作:",
"from":"发件人地址",
"subject":"邮件主题",
"content":"邮件内容",
"equal":"等于",
"regex":"正则匹配",
"contains":"包含",
"mark_read":"标记已读",
"delete":"删除",
"forward":"转发",
"move":"移动分组",
"del_rule_confirm":"确定要删除吗?",
"rule_params":"执行参数",
"dangerous": "该邮件内容不安全!",
"rule_setting": "规则",
"new_rule": "新建收信规则",
"rule_name": "规则名称",
"rule_priority": "优先级(值越大越先执行)",
"rule_desc": "以下规则全部满足时:",
"rule_do": "执行操作:",
"from": "发件人地址",
"subject": "邮件主题",
"content": "邮件内容",
"equal": "等于",
"regex": "正则匹配",
"contains": "包含",
"mark_read": "标记已读",
"delete": "删除",
"forward": "转发",
"move": "移动分组",
"del_rule_confirm": "确定要删除吗?",
"rule_params": "执行参数",
"autoSSLWarn": "PMail当前未使用80端口启动如果想要PMail自动管理SSL证书请将/.well-known/*路由转发到PMail。 详见https://github.com/Jinnrry/PMail/issues/94",
"err_db_dsn_empty": "数据库路径不能为空!",
}
switch (navigator.language) {
case "zh":
lang = zhCN
break
case "zh-CN":
lang = zhCN
break
default:
break
}
if (navigator.language === "zh-CN" || navigator.language === "zh") lang = zhCN
export default lang;

View File

@ -3,119 +3,10 @@ import 'element-plus/dist/index.css'
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import {ref} from 'vue'
import App from './App.vue'
import router from './router'
import {router} from './router'
const app = createApp(App)
app.config.globalProperties.$isLogin = ref(true)
app.config.globalProperties.$userInfos = ref({})
app.use(createPinia())
app.use(router)
import axios from 'axios'
import lang from './i18n/i18n';
//创建axios的一个实例
var $http = axios.create({
baseURL: import.meta.env.VITE_APP_URL, //接口统一域名
timeout: 60000, //设置超时
headers: {
'Content-Type': 'application/json;charset=UTF-8;',
'Lang': lang.lang
}
})
//请求拦截器
$http.interceptors.request.use((config) => {
//若请求方式为post则将data参数转为JSON字符串
if (config.method === 'POST') {
config.data = JSON.stringify(config.data);
}
return config;
}, (error) =>
// 对请求错误做些什么
Promise.reject(error));
//响应拦截器
$http.interceptors.response.use((response) => {
//响应成功
if (response.data.errorNo == 403) {
app.config.globalProperties.$isLogin.value = false
router.replace({
path: '/login',
query: {
redirect: router.currentRoute.fullPath
}
})
}
//响应成功
if (response.data.errorNo == 402) {
router.replace({
path: '/setup',
query: {
redirect: router.currentRoute.fullPath
}
})
}
return response.data;
}, (error) => {
//响应错误
if (error.response && error.response.status) {
const status = error.response.status
let message = ""
switch (status) {
case 400:
message = '请求错误';
break;
case 401:
message = '请求错误';
break;
case 403:
router.replace({
path: '/login',
query: {
redirect: router.currentRoute.fullPath
}
})
break;
case 404:
message = '请求地址出错';
break;
case 408:
message = '请求超时';
break;
case 500:
message = '服务器内部错误!';
break;
case 501:
message = '服务未实现!';
break;
case 502:
message = '网关错误!';
break;
case 503:
message = '服务不可用!';
break;
case 504:
message = '网关超时!';
break;
case 505:
message = 'HTTP版本不受支持';
break;
default:
message = '请求失败'
}
return Promise.reject(error);
}
return Promise.reject(error);
});
// 注册到全局
app.config.globalProperties.$http = $http
app.use(createPinia())
app.mount('#app')

View File

@ -46,4 +46,4 @@ const router = createRouter({
export default router
export {router};

View File

@ -1,4 +1,4 @@
import { ref, computed } from 'vue'
import { ref } from 'vue'
import { defineStore } from 'pinia'
import lang from '../i18n/i18n';

View File

@ -0,0 +1,15 @@
import {defineStore} from "pinia";
const useGlobalStatusStore = defineStore('useGlobalStatusStore', {
state() {
return {
isLogin: true,
userInfos:{}
}
},
getters: {},
actions: {}
})
export {useGlobalStatusStore};

103
fe/src/utils/axios.js Normal file
View File

@ -0,0 +1,103 @@
import axios from 'axios'
import lang from '../i18n/i18n';
import {useGlobalStatusStore} from "@/stores/useGlobalStatusStore";
import {router} from "@/router";
//创建axios的一个实例
const http = axios.create({
baseURL: import.meta.env.VITE_APP_URL, //接口统一域名
timeout: 60000, //设置超时
headers: {
'Content-Type': 'application/json;charset=UTF-8;',
'Lang': lang.lang
}
});
//请求拦截器
http.interceptors.request.use((config) => {
//若请求方式为post则将data参数转为JSON字符串
if (config.method === 'POST') {
config.data = JSON.stringify(config.data);
}
return config;
}, (error) =>
// 对请求错误做些什么
Promise.reject(error));
//响应拦截器
http.interceptors.response.use(async (response) => {
const globalStatus = useGlobalStatusStore();
//响应成功
if (response.data.errorNo === 403) {
globalStatus.isLogin = false
await router.replace({
path: '/login',
query: {
redirect: router.currentRoute.fullPath
}
});
}
//响应成功
if (response.data.errorNo === 402) {
await router.replace({
path: '/setup',
query: {
redirect: router.currentRoute.fullPath
}
});
}
return response.data;
}, async (error) => {
//响应错误
if (error.response && error.response.status) {
let message = ""
switch (error.response.status) {
case 400:
message = '请求错误';
break;
case 401:
message = '请求错误';
break;
case 403:
await router.replace({
path: '/login',
query: {
redirect: router.currentRoute.fullPath
}
});
break;
case 404:
message = '请求地址出错';
break;
case 408:
message = '请求超时';
break;
case 500:
message = '服务器内部错误!';
break;
case 501:
message = '服务未实现!';
break;
case 502:
message = '网关错误!';
break;
case 503:
message = '服务不可用!';
break;
case 504:
message = '网关超时!';
break;
case 505:
message = 'HTTP版本不受支持';
break;
default:
// eslint-disable-next-line no-unused-vars
message = '请求失败';
}
return Promise.reject(error);
}
return Promise.reject(error);
});
export {http};

View File

@ -15,18 +15,18 @@
<div style="display: flex; flex-direction:column;">
<div style=" margin-bottom: 10px;">
<el-form-item :label="lang.sender" prop="sender">
<el-input style="max-width: 200px" :disabled="!$userInfos.is_admin"
v-model="ruleForm.sender" :placeholder="lang.sender_desc" />
<el-input style="max-width: 200px" :disabled="!userInfos.is_admin"
v-model="ruleForm.sender" :placeholder="lang.sender_desc"/>
<div>@</div>
<el-select v-model="ruleForm.pickDomain">
<el-option :value="item" v-for="item in ruleForm.domains">{{ item }}</el-option>
<el-option :value="item" v-for="item in ruleForm.domains" :key="item">{{ item }}</el-option>
</el-select>
</el-form-item>
</div>
<div>
<el-form-item :label="lang.nick_name" >
<el-input style="max-width: 300px" v-model="ruleForm.nickName" />
<el-form-item :label="lang.nick_name">
<el-input style="max-width: 300px" v-model="ruleForm.nickName"/>
</el-form-item>
</div>
@ -57,17 +57,19 @@
<div id="editor">
<div style="border: 1px solid #ccc">
<Toolbar style="border-bottom: 1px solid #ccc" :editor="editorRef" :defaultConfig="toolbarConfig"
:mode="mode" />
:mode="mode"/>
<Editor style="height: 300px;" v-model="valueHtml" :defaultConfig="editorConfig" :mode="mode"
@onCreated="handleCreated" />
@onCreated="handleCreated"/>
</div>
</div>
<div id="fileList">
<ol>
<li v-for="(item, index) in fileList">{{ item.name }} <el-icon @click="delFile(index)">
<Close />
</el-icon> </li>
<li v-for="(item, index) in fileList" :key="item">{{ item.name }}
<el-icon @click="delFile(index)">
<Close/>
</el-icon>
</li>
</ol>
</div>
@ -81,8 +83,6 @@
</div>
</div>
</el-form>
</div>
@ -108,30 +108,31 @@
<script setup>
import '@wangeditor/editor/dist/css/style.css' // css
import { ElMessage } from 'element-plus'
import { onBeforeUnmount, ref, shallowRef, reactive, onMounted } from 'vue'
import { Close } from '@element-plus/icons-vue';
import {ElMessage} from 'element-plus'
import {onBeforeUnmount, reactive, ref, shallowRef} from 'vue'
import {Close} from '@element-plus/icons-vue';
import lang from '../i18n/i18n';
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import { i18nChangeLanguage } from '@wangeditor/editor'
import { useRouter } from 'vue-router';
const router = useRouter();
import {Editor, Toolbar} from '@wangeditor/editor-for-vue'
import {i18nChangeLanguage} from '@wangeditor/editor'
import {useRouter} from 'vue-router';
import {http} from "@/utils/axios";
import useGroupStore from '../stores/group'
const groupStore = useGroupStore()
import { getCurrentInstance } from 'vue'
const app = getCurrentInstance()
const $http = app.appContext.config.globalProperties.$http
const $isLogin = app.appContext.config.globalProperties.$isLogin
const $userInfos = app.appContext.config.globalProperties.$userInfos
import {useGlobalStatusStore} from "@/stores/useGlobalStatusStore";
if (lang.lang == "zhCn") {
const router = useRouter();
const groupStore = useGroupStore()
const globalStatus = useGlobalStatusStore();
const userInfos = globalStatus.userInfos
if (lang.lang === "zhCn") {
i18nChangeLanguage('zh-CN')
} else {
i18nChangeLanguage('en')
}
// HTML
const valueHtml = ref('<p>hello</p>')
@ -147,7 +148,6 @@ editorConfig.MENU_CONF['uploadImage'] = {
}
const mode = ref()
const fileRef = ref();
const pickFile = ref();
const ruleFormRef = ref()
const ruleForm = reactive({
nickName: '',
@ -162,10 +162,10 @@ const fileList = reactive([]);
const init = function () {
if (Object.keys($userInfos.value).length == 0) {
$http.post("/api/user/info", {}).then(res => {
if (res.errorNo == 0) {
$userInfos.value = res.data
if (Object.keys(userInfos.value).length === 0) {
http.post("/api/user/info", {}).then(res => {
if (res.errorNo === 0) {
userInfos.value = res.data
ruleForm.sender = res.data.account
ruleForm.domains = res.data.domains
ruleForm.pickDomain = res.data.domains[0]
@ -178,10 +178,10 @@ const init = function () {
}
})
} else {
ruleForm.sender = $userInfos.value.account
ruleForm.domains = $userInfos.value.domains
ruleForm.pickDomain = $userInfos.value.domains[0]
ruleForm.nickName = $userInfos.value.name
ruleForm.sender = userInfos.value.account
ruleForm.domains = userInfos.value.domains
ruleForm.pickDomain = userInfos.value.domains[0]
ruleForm.nickName = userInfos.value.name
}
}
@ -199,12 +199,8 @@ const validateSender = function (rule, value, callback) {
}
const checkEmail = function (str) {
var re = /.+@.+\..+/
if (re.test(str)) {
return true
} else {
return false
}
const re = /.+@.+\..+/;
return re.test(str);
}
const validateReceivers = function (rule, value, callback) {
@ -222,7 +218,7 @@ const validateCc = function (rule, value, callback) {
for (let index = 0; index < ruleForm.cc.length; index++) {
let element = ruleForm.cc[index];
if (!checkEmail(element)) {
callback(new Error(err_email_format))
callback(new Error(lang.err_email_format))
return
}
}
@ -231,16 +227,16 @@ const validateCc = function (rule, value, callback) {
const rules = reactive({
sender: [
{ validator: validateSender, trigger: 'change' }
{validator: validateSender, trigger: 'change'}
],
receivers: [
{ validator: validateReceivers, trigger: 'change' }
{validator: validateReceivers, trigger: 'change'}
],
cc: [
{ validator: validateCc, trigger: 'change' }
{validator: validateCc, trigger: 'change'}
],
subject: [
{ required: true, message: lang.err_title_must, trigger: 'change' },
{required: true, message: lang.err_title_must, trigger: 'change'},
],
})
@ -282,8 +278,8 @@ const send = function (formEl) {
let text = editorRef.value.getText()
$http.post("/api/email/send", {
from: { name: ruleForm.nickName, email: ruleForm.sender + "@" + ruleForm.pickDomain },
http.post("/api/email/send", {
from: {name: ruleForm.nickName, email: ruleForm.sender + "@" + ruleForm.pickDomain},
to: objectTos,
cc: objectCcs,
subject: ruleForm.subject,
@ -311,13 +307,9 @@ const send = function (formEl) {
})
}
const upload = function () {
fileRef.value.dispatchEvent(new MouseEvent('click'))
}

View File

@ -1,24 +1,26 @@
<template>
<div id="main">
<div id="title">{{ detailData.subject }}</div>
<el-divider />
<el-divider/>
<div>
<div>{{ lang.to }}
<el-tooltip v-for="to in tos" class="box-item" effect="dark" :content="to.EmailAddress" placement="top">
<el-tag size="small" type="info">{{to.Name != '' ? to.Name : to.EmailAddress }}</el-tag>
<el-tooltip v-for="to in tos" :key.prop="to" class="box-item" effect="dark" :content="to.EmailAddress" placement="top">
<el-tag size="small" type="info">{{ to.Name !== '' ? to.Name : to.EmailAddress }}</el-tag>
</el-tooltip>
</div>
<div v-if="showCC">{{ lang.cc }}
<el-tooltip v-for="item in ccs" class="box-item" effect="dark" :content="item.EmailAddress" placement="top">
<el-tag size="small" type="info">{{item.Name != '' ? item.Name : item.EmailAddress }}</el-tag>
<el-tooltip v-for="item in ccs" :key="item" class="box-item" effect="dark" :content="item.EmailAddress" placement="top">
<el-tag size="small" type="info">{{ item.Name !== '' ? item.Name : item.EmailAddress }}</el-tag>
</el-tooltip>
</div>
<div>{{ lang.sender }}
<el-tooltip class="box-item" effect="dark" :content="detailData.from_address" placement="top">
<el-tag size="small" type="info">{{detailData.from_name != '' ? detailData.from_name : detailData.from_address }}</el-tag>
<el-tag size="small" type="info">
{{ detailData.from_name !== '' ? detailData.from_name : detailData.from_address }}
</el-tag>
</el-tooltip>
</div>
@ -26,8 +28,8 @@
{{ detailData.send_date }}
</div>
</div>
<el-divider />
<div class="content" id="text" v-if="detailData.html == ''">
<el-divider/>
<div class="content" id="text" v-if="detailData.html === ''">
{{ detailData.text }}
</div>
@ -36,12 +38,14 @@
</div>
<div v-if="detailData.attachments.length > 0" style="">
<el-divider />
<el-divider/>
{{ lang.attachment }}
<a class="att" v-for="item in detailData.attachments"
:href="'/attachments/download/' + detailData.id + '/' + item.Index"> <el-icon>
<Document />
</el-icon> {{ item.Filename }} </a>
<a class="att" v-for="item in detailData.attachments" :key="item"
:href="'/attachments/download/' + detailData.id + '/' + item.Index">
<el-icon>
<Document/>
</el-icon>
{{ item.Filename }} </a>
</div>
@ -50,38 +54,34 @@
<script setup>
import { reactive, ref } from 'vue'
import { useRoute } from 'vue-router'
import { Document } from '@element-plus/icons-vue';
import {ref} from 'vue'
import {useRoute} from 'vue-router'
import {Document} from '@element-plus/icons-vue';
import lang from '../i18n/i18n';
import { getCurrentInstance } from 'vue'
const app = getCurrentInstance()
const $http = app.appContext.config.globalProperties.$http
import {http} from "@/utils/axios";
const route = useRoute()
const detailData = ref({
attachments:[]
attachments: []
})
const tos = ref()
const ccs = ref()
const showCC = ref(false)
$http.post("/api/email/detail", { id: parseInt(route.params.id) }).then(res => {
http.post("/api/email/detail", {id: parseInt(route.params.id)}).then(res => {
detailData.value = res.data
if (res.data.to != "" && res.data.to != null) {
if (res.data.to !== "" && res.data.to != null) {
tos.value = JSON.parse(res.data.to)
}
if (res.data.cc != "" && res.data.cc != null) {
if (res.data.cc !== "" && res.data.cc != null) {
ccs.value = JSON.parse(res.data.cc)
}
if (ccs.value != null && ccs.value != undefined){
if (ccs.value != null) {
showCC.value = ccs.value.length > 0
}else{
} else {
showCC.value = false
}
})
@ -100,18 +100,19 @@ $http.post("/api/email/detail", { id: parseInt(route.params.id) }).then(res => {
text-align: left;
}
#userItem {}
#userItem {
}
.content {
/* background-color: aliceblue; */
}
a,a:link,a:visited,a:hover,a:active{
a, a:link, a:visited, a:hover, a:active {
text-decoration: none;
color:inherit;
color: inherit;
}
.att{
display:block;
.att {
display: block;
}
</style>

View File

@ -12,20 +12,24 @@
<el-dropdown style="margin-left: 12px;">
<el-button size="small">
{{ lang.move_btn }}
<el-icon class="el-icon--right"><arrow-down /></el-icon>
<el-icon class="el-icon--right">
<EpArrowDownBold />
</el-icon>
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="move(group.id)" v-for="group in groupList">{{ group.name
}}</el-dropdown-item>
<el-dropdown-item @click="move(group.id)" v-for="group in groupList" :key="group.id">{{
group.name
}}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
<div id="table">
<el-table ref="taskTableDataRef" @selection-change="selectionLineChange" :data="data" :show-header="true"
<el-table ref="taskTableDataRef" :data="data" :show-header="true"
:border="false" @row-click="rowClick" :row-style="rowStyle">
<el-table-column type="selection" width="30" />
<el-table-column type="selection" width="30"/>
<el-table-column prop="is_read" label="" width="50">
<template #default="scope">
<div>
@ -38,7 +42,7 @@
</el-tooltip>
</span>
<span style="font-weight: 900;color: #FF0000;" v-if="scope.row.error != ''">
<span style="font-weight: 900;color: #FF0000;" v-if="scope.row.error !== ''">
<el-tooltip effect="dark" :content="scope.row.error" placement="top-start">
!
</el-tooltip>
@ -50,15 +54,18 @@
<el-table-column prop="title" :label="lang.sender" width="150">
<template #default="scope">
<el-tooltip class="box-item" effect="dark" :content="scope.row.sender.EmailAddress" placement="top">
<el-tag size="small" type="info">{{scope.row.sender.Name != '' ? scope.row.sender.Name : scope.row.sender.EmailAddress }}</el-tag>
<el-tag size="small" type="info">
{{ scope.row.sender.Name !== '' ? scope.row.sender.Name : scope.row.sender.EmailAddress }}
</el-tag>
</el-tooltip>
</template>
</el-table-column>
<el-table-column prop="title" :label="lang.to" width="150">
<template #default="scope">
<el-tooltip v-for="toInfo in scope.row.to" class="box-item" effect="dark" :content="toInfo.EmailAddress" placement="top">
<el-tag size="small" type="info">{{toInfo.Name != '' ? toInfo.Name : toInfo.EmailAddress }}</el-tag>
<el-tooltip v-for="toInfo in scope.row.to" :key="toInfo" class="box-item" effect="dark" :content="toInfo.EmailAddress"
placement="top">
<el-tag size="small" type="info">{{ toInfo.Name !== '' ? toInfo.Name : toInfo.EmailAddress }}</el-tag>
</el-tooltip>
</template>
</el-table-column>
@ -81,70 +88,59 @@
</el-table>
</div>
<div id="pagination">
<el-pagination background layout="prev, pager, next" :page-count="totalPage" @current-change="pageChange" />
<el-pagination background layout="prev, pager, next" :page-count="totalPage" @current-change="pageChange"/>
</div>
</div>
</template>
<script setup>
import { ArrowDown } from '@element-plus/icons-vue'
import { RouterLink } from 'vue-router'
import { reactive, ref, watch } from 'vue'
import {EpArrowDownBold} from "vue-icons-plus/ep";
import {RouterLink, useRouter} from 'vue-router'
import {ref, watch} from 'vue'
import useGroupStore from '../stores/group'
import lang from '../i18n/i18n';
import { useRouter } from 'vue-router';
import { getCurrentInstance } from 'vue'
const app = getCurrentInstance()
const $http = app.appContext.config.globalProperties.$http
import {http} from "@/utils/axios";
import {ElMessage, ElMessageBox} from "element-plus";
const router = useRouter();
const groupStore = useGroupStore()
const groupList = ref([])
const taskTableDataRef = ref(null)
let tag = groupStore.tag;
if (tag == "") {
if (tag === "") {
tag = '{"type":0,"status":-1}'
}
watch(groupStore, async (newV, oldV) => {
watch(groupStore, async (newV) => {
tag = newV.tag;
if (tag == "") {
if (tag === "") {
tag = '{"type":0,"status":-1}'
}
data.value = []
$http.post("/api/email/list", { tag: tag, page_size: 10 }).then(res => {
http.post("/api/email/list", {tag: tag, page_size: 10}).then(res => {
data.value = res.data.list
totalPage.value = res.data.total_page
})
})
const data = ref([])
const totalPage = ref(0)
const updateList = function () {
$http.post("/api/email/list", { tag: tag, page_size: 10 }).then(res => {
http.post("/api/email/list", {tag: tag, page_size: 10}).then(res => {
data.value = res.data.list
totalPage.value = res.data.total_page
})
}
const updateGroupList = function () {
$http.post("/api/group/list").then(res => {
http.post("/api/group/list").then(res => {
groupList.value = res.data
})
}
@ -152,7 +148,7 @@ const updateGroupList = function () {
updateList()
updateGroupList()
const rowClick = function (row, column, event) {
const rowClick = function (row) {
router.push("/detail/" + row.id)
}
@ -163,8 +159,8 @@ const markRead = function () {
ids.push(element.id)
});
$http.post("/api/email/read", { "ids": ids }).then(res => {
if (res.errorNo == 0) {
http.post("/api/email/read", {"ids": ids}).then(res => {
if (res.errorNo === 0) {
updateList()
} else {
ElMessage({
@ -193,8 +189,8 @@ const move = function (group_id) {
}
)
.then(() => {
$http.post("/api/email/move", { "group_id": group_id, "ids": ids }).then(res => {
if (res.errorNo == 0) {
http.post("/api/email/move", {"group_id": group_id, "ids": ids}).then(res => {
if (res.errorNo === 0) {
updateList()
ElMessage({
type: 'success',
@ -209,12 +205,10 @@ const move = function (group_id) {
})
})
}
const del = function () {
let rows = taskTableDataRef.value?.getSelectionRows()
let ids = []
@ -235,8 +229,8 @@ const del = function () {
}
)
.then(() => {
$http.post("/api/email/del", { "ids": ids ,"forcedDel":groupTag.status == 3 }).then(res => {
if (res.errorNo == 0) {
http.post("/api/email/del", {"ids": ids, "forcedDel": groupTag.status === 3}).then(res => {
if (res.errorNo === 0) {
updateList()
ElMessage({
type: 'success',
@ -251,17 +245,16 @@ const del = function () {
})
})
}
const rowStyle = function ({ row, rowIndwx }) {
return { 'cursor': 'pointer' }
const rowStyle = function () {
return {'cursor': 'pointer'}
}
const pageChange = function (p) {
$http.post("/api/email/list", { tag: tag, page_size: 10, current_page: p }).then(res => {
http.post("/api/email/list", {tag: tag, page_size: 10, current_page: p}).then(res => {
data.value = res.data.list
})
}

View File

@ -1,12 +1,12 @@
<template>
<div id="main">
<div id="form">
<el-form :model="form" label-width="120px" @keyup.enter.native="onSubmit">
<el-form :model="form" label-width="120px" @keyup.enter="onSubmit">
<el-form-item :label="lang.account">
<el-input v-model="form.account" placeholder="User Name" />
<el-input v-model="form.account" placeholder="User Name"/>
</el-form-item>
<el-form-item :label="lang.password">
<el-input v-model="form.password" placeholder="Password" type="password" />
<el-input v-model="form.password" placeholder="Password" type="password"/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">{{ lang.login }}</el-button>
@ -19,15 +19,17 @@
<script setup>
import { reactive } from 'vue'
import { ElMessage } from 'element-plus'
import router from "@/router"; //
import {reactive} from 'vue'
import {ElMessage} from 'element-plus'
import {router} from "@/router"; //
import lang from '../i18n/i18n';
import { getCurrentInstance } from 'vue'
const app = getCurrentInstance()
const $http = app.appContext.config.globalProperties.$http
const $isLogin = app.appContext.config.globalProperties.$isLogin
const $userInfos = app.appContext.config.globalProperties.$userInfos
import {http} from "@/utils/axios";
import {useGlobalStatusStore} from "@/stores/useGlobalStatusStore";
const globalStatus = useGlobalStatusStore();
// eslint-disable-next-line no-unused-vars
let isLogin = globalStatus.isLogin
const userInfos = globalStatus.userInfos
const form = reactive({
account: '',
@ -35,12 +37,12 @@ const form = reactive({
})
const onSubmit = () => {
$http.post("/api/login", form).then(res => {
if (res.errorNo != 0) {
http.post("/api/login", form).then(res => {
if (res.errorNo !== 0) {
ElMessage.error(res.errorMsg)
} else {
$isLogin.value = true
$userInfos.value = res.data
isLogin = true
userInfos.value = res.data
router.replace({
path: '/',
query: {

View File

@ -1,26 +1,23 @@
<template>
<div id="main">
<el-steps :active="active" align-center finish-status="success" id="status">
<el-step :title="lang.welcome" />
<el-step :title="lang.setDatabase" />
<el-step :title="lang.setAdminPassword" />
<el-step :title="lang.SetDomail" />
<el-step :title="lang.setDNS" />
<el-step :title="lang.setSSL" />
<el-step :title="lang.welcome"/>
<el-step :title="lang.setDatabase"/>
<el-step :title="lang.setAdminPassword"/>
<el-step :title="lang.SetDomail"/>
<el-step :title="lang.setDNS"/>
<el-step :title="lang.setSSL"/>
</el-steps>
<div v-if="active == 0" class="ctn">
<div v-if="active === 0" class="ctn">
<div class="desc">
<h2>{{ lang.tks_pmail }}</h2>
<div style="margin-top: 10px;">{{ lang.guid_desc }}</div>
</div>
</div>
<div v-if="active == 1" class="ctn">
<div v-if="active === 1" class="ctn">
<div class="desc">
<h2>{{ lang.select_db }}</h2>
<div style="margin-top: 10px;">{{ lang.db_desc }}</div>
@ -30,23 +27,23 @@
<el-form-item :label="lang.type">
<el-select :placeholder="lang.db_select_ph" v-model="dbSettings.type"
@change="dbSettings.dsn = ''">
<el-option label="MySQL" value="mysql" />
<el-option label="SQLite3" value="sqlite" />
<el-option label="PostgreSQL" value="postgres" />
<el-option label="MySQL" value="mysql"/>
<el-option label="SQLite3" value="sqlite"/>
<el-option label="PostgreSQL" value="postgres"/>
</el-select>
</el-form-item>
<el-form-item :label="lang.mysql_dsn" v-if="dbSettings.type == 'mysql'">
<el-form-item :label="lang.mysql_dsn" v-if="dbSettings.type === 'mysql'">
<el-input :rows="2" type="textarea" v-model="dbSettings.dsn"
placeholder="root:12345@tcp(127.0.0.1:3306)/pmail?parseTime=True&loc=Local"></el-input>
</el-form-item>
<el-form-item :label="lang.pg_dsn" v-if="dbSettings.type == 'postgres'">
<el-form-item :label="lang.pg_dsn" v-if="dbSettings.type === 'postgres'">
<el-input :rows="2" type="textarea" v-model="dbSettings.dsn"
placeholder="postgres://postgres:12345@127.0.0.1:5432/pmail?sslmode=disable"></el-input>
</el-form-item>
<el-form-item :label="lang.sqlite_db_path" v-if="dbSettings.type == 'sqlite'">
<el-form-item :label="lang.sqlite_db_path" v-if="dbSettings.type === 'sqlite'">
<el-input v-model="dbSettings.dsn" placeholder="./config/pmail.db"></el-input>
</el-form-item>
</el-form>
@ -54,7 +51,7 @@
</div>
<div v-if="active == 2" class="ctn">
<div v-if="active === 2" class="ctn">
<div class="desc">
<h2>{{ lang.setAdminPassword }}</h2>
<!-- <div style="margin-top: 10px;">{{ lang.domain_desc }}</div> -->
@ -81,7 +78,7 @@
</div>
<div v-if="active == 3" class="ctn">
<div v-if="active === 3" class="ctn">
<div class="desc">
<h2>{{ lang.SetDomail }}</h2>
<!-- <div style="margin-top: 10px;">{{ lang.domain_desc }}</div> -->
@ -100,10 +97,14 @@
</el-form-item>
<el-form-item :label="lang.multi_domain_setting">
<span>{{ lang.multi_domain_setting_desc }} <el-button @click="addDomain" size="small"
type="success" :icon="Plus" circle></el-button></span>
<el-input :placeholder="'domain' + i + '.com'" v-for="(item, i) in domainSettings.multi_domain"
v-model="domainSettings.multi_domain[i]"></el-input>
<span>{{ lang.multi_domain_setting_desc }}
<el-button @click="addDomain" size="small"
type="success" :icon="Plus"
circle>
</el-button>
</span>
<el-input :placeholder="'domain' + i + '.com'" v-for="(item, i) in domainSettings.multi_domain "
v-model="domainSettings.multi_domain[i]" :key="item"></el-input>
</el-form-item>
@ -111,45 +112,46 @@
</div>
</div>
<div v-if="active == 4" class="ctn_s">
<div v-if="active === 4" class="ctn_s">
<div class="desc">
<h2>{{ lang.setDNS }}</h2>
<div style="margin-top: 10px;">{{ lang.dns_desc }}</div>
</div>
<div class="form" width="600px" v-for="(info,domain) in dnsInfos">
<div class="form" width="600px" v-for="(info,domain) in dnsInfos" :key="info">
<h3>{{ domain }}</h3>
<el-table :data="info" border style="width: 100%">
<el-table-column prop="host" label="HOSTNAME" width="110px" >
<el-table-column prop="host" label="HOSTNAME" width="110px">
<template #default="scope">
<div style="display: flex; align-items: center">
<el-tooltip :content="lang.dns_root_desc" placement="top" v-if="scope.row.host == '' || scope.row.host == '@' ">
<el-tooltip :content="lang.dns_root_desc" placement="top"
v-if="scope.row.host === '' || scope.row.host === '@' ">
{{ scope.row.host }}
</el-tooltip>
<span v-else>{{ scope.row.host }}</span>
</div>
</template>
</el-table-column>
<el-table-column prop="type" label="TYPE" width="110px" />
<el-table-column prop="type" label="TYPE" width="110px"/>
<el-table-column prop="value" label="VALUE">
<template #default="scope">
<div style="display: flex; align-items: center">
<el-tooltip :content="scope.row.tips" placement="top" v-if="scope.row.tips != ''">
<el-tooltip :content="scope.row.tips" placement="top" v-if="scope.row.tips !== ''">
{{ scope.row.value }}
</el-tooltip>
<span v-else>{{ scope.row.value }}</span>
</div>
</template>
</el-table-column>
<el-table-column prop="ttl" label="TTL" width="110px" />
<el-table-column prop="ttl" label="TTL" width="110px"/>
</el-table>
</div>
</div>
<el-alert :closable="false" title="Warning!" type="error" center
v-if="active == 5 && sslSettings.type == 0 && port != 80" :description="lang.autoSSLWarn" />
v-if="active === 5 && sslSettings.type === 0 && port !== 80" :description="lang.autoSSLWarn"/>
<div v-if="active == 5" class="ctn">
<div v-if="active === 5" class="ctn">
<div class="desc">
<h2>{{ lang.setSSL }}</h2>
<div style="margin-top: 10px;">{{ lang.setSSL }}</div>
@ -158,16 +160,16 @@
<el-form label-width="120px">
<el-form-item :label="lang.type">
<el-select :placeholder="lang.ssl_auto" v-model="sslSettings.type" :disabled="dnsChecking">
<el-option :label="lang.ssl_auto" value="0" />
<el-option :label="lang.ssl_manuallyf" value="1" />
<el-option :label="lang.ssl_auto" value="0"/>
<el-option :label="lang.ssl_manuallyf" value="1"/>
</el-select>
</el-form-item>
<el-form-item :label="lang.ssl_challenge_type" v-if="sslSettings.type == '0'">
<el-form-item :label="lang.ssl_challenge_type" v-if="sslSettings.type === '0'">
<el-select :placeholder="lang.ssl_auto_http" v-model="sslSettings.challenge"
:disabled="dnsChecking">
<el-option :label="lang.ssl_auto_http" value="http" />
<el-option :label="lang.ssl_auto_dns" value="dns" />
<el-option :label="lang.ssl_auto_http" value="http"/>
<el-option :label="lang.ssl_auto_dns" value="dns"/>
</el-select>
<el-tooltip class="box-item" effect="dark" :content="lang.challenge_typ_desc"
@ -177,14 +179,11 @@
</el-form-item>
<el-form-item :label="lang.ssl_key_path" v-if="sslSettings.type == '1'">
<el-form-item :label="lang.ssl_key_path" v-if="sslSettings.type === '1'">
<el-input placeholder="./config/ssl/private.key" v-model="sslSettings.key_path"></el-input>
</el-form-item>
<el-form-item :label="lang.ssl_crt_path" v-if="sslSettings.type == '1'">
<el-form-item :label="lang.ssl_crt_path" v-if="sslSettings.type === '1'">
<el-input placeholder="./config/ssl/public.crt" v-model="sslSettings.crt_path"></el-input>
</el-form-item>
</el-form>
@ -196,13 +195,13 @@
<div v-if="dnsChecking">
<label>{{ lang.dns_desc }}</label>
<el-table :data="sslSettings.paramsList" border v-loading="sslSettings.paramsList.length == 0">
<el-table-column prop="host" label="HOSTNAME" width="110px" />
<el-table-column prop="type" label="TYPE" width="110px" />
<el-table :data="sslSettings.paramsList" border v-loading="sslSettings.paramsList.length === 0">
<el-table-column prop="host" label="HOSTNAME" width="110px"/>
<el-table-column prop="type" label="TYPE" width="110px"/>
<el-table-column prop="value" label="VALUE">
<template #default="scope">
<div style="display: flex; align-items: center">
<el-tooltip :content="scope.row.tips" placement="top" v-if="scope.row.tips != ''">
<el-tooltip :content="scope.row.tips" placement="top" v-if="scope.row.tips !== ''">
{{ scope.row.value }}
</el-tooltip>
<span v-else>{{ scope.row.value }}</span>
@ -210,7 +209,7 @@
</template>
</el-table-column>
<el-table-column prop="ttl" label="TTL" width="110px" />
<el-table-column prop="ttl" label="TTL" width="110px"/>
</el-table>
</div>
@ -218,26 +217,22 @@
<el-button :element-loading-text="waitDesc" v-loading.fullscreen.lock="fullscreenLoading" id="next"
style="margin-top: 12px" @click="next">{{
lang.next }}</el-button>
lang.next
}}
</el-button>
</div>
</template>
<script setup>
import { reactive, ref } from 'vue'
import { ElMessage } from 'element-plus'
import {reactive, ref} from 'vue'
import {ElMessage} from 'element-plus'
import lang from '../i18n/i18n';
import axios from 'axios'
import { getCurrentInstance } from 'vue'
import {
Plus
} from '@element-plus/icons-vue'
const app = getCurrentInstance()
const $http = app.appContext.config.globalProperties.$http
const waitDesc = ref(lang.wait_desc)
import {Plus} from '@element-plus/icons-vue'
import {http} from "@/utils/axios";
const waitDesc = ref(lang.wait_desc);
const adminSettings = reactive({
"account": "admin",
@ -287,11 +282,16 @@ const setPassword = () => {
return;
}
if (adminSettings.password != adminSettings.password2) {
if (adminSettings.password !== adminSettings.password2) {
ElMessage.error(lang.err_pwd_diff)
} else {
$http.post("/api/setup", { "action": "set", "step": "password", "account": adminSettings.account, "password": adminSettings.password }).then((res) => {
if (res.errorNo != 0) {
http.post("/api/setup", {
"action": "set",
"step": "password",
"account": adminSettings.account,
"password": adminSettings.password
}).then((res) => {
if (res.errorNo !== 0) {
ElMessage.error(res.errorMsg)
} else {
active.value++;
@ -302,11 +302,11 @@ const setPassword = () => {
}
const getPassword = () => {
$http.post("/api/setup", { "action": "get", "step": "password" }).then((res) => {
if (res.errorNo != 0) {
http.post("/api/setup", {"action": "get", "step": "password"}).then((res) => {
if (res.errorNo !== 0) {
ElMessage.error(res.errorMsg)
} else {
adminSettings.hadSeted = res.data != ""
adminSettings.hadSeted = res.data !== ""
if (adminSettings.hadSeted) {
adminSettings.account = res.data
adminSettings.password = "*******"
@ -319,8 +319,8 @@ const getPassword = () => {
const getDbConfig = () => {
$http.post("/api/setup", { "action": "get", "step": "database" }).then((res) => {
if (res.errorNo != 0) {
http.post("/api/setup", {"action": "get", "step": "database"}).then((res) => {
if (res.errorNo !== 0) {
ElMessage.error(res.errorMsg)
} else {
dbSettings.type = res.data.db_type;
@ -330,8 +330,8 @@ const getDbConfig = () => {
}
const getDomainConfig = () => {
$http.post("/api/setup", { "action": "get", "step": "domain" }).then((res) => {
if (res.errorNo != 0) {
http.post("/api/setup", {"action": "get", "step": "domain"}).then((res) => {
if (res.errorNo !== 0) {
ElMessage.error(res.errorMsg)
} else {
domainSettings.web_domain = res.data.web_domain;
@ -349,8 +349,13 @@ const setDbConfig = () => {
message: lang.err_db_dsn_empty,
type: "error",
});
$http.post("/api/setup", { "action": "set", "step": "database", "db_type": dbSettings.type, "db_dsn": dbSettings.dsn }).then((res) => {
if (res.errorNo != 0) {
http.post("/api/setup", {
"action": "set",
"step": "database",
"db_type": dbSettings.type,
"db_dsn": dbSettings.dsn
}).then((res) => {
if (res.errorNo !== 0) {
ElMessage.error(res.errorMsg)
} else {
active.value++;
@ -360,8 +365,8 @@ const setDbConfig = () => {
}
const getDNSConfig = () => {
$http.post("/api/setup", { "action": "get", "step": "dns" }).then((res) => {
if (res.errorNo != 0) {
http.post("/api/setup", {"action": "get", "step": "dns"}).then((res) => {
if (res.errorNo !== 0) {
ElMessage.error(res.errorMsg)
} else {
dnsInfos.value = res.data
@ -371,12 +376,12 @@ const getDNSConfig = () => {
const getSSLConfig = () => {
$http.post("/api/setup", { "action": "get", "step": "ssl" }).then((res) => {
if (res.errorNo != 0) {
http.post("/api/setup", {"action": "get", "step": "ssl"}).then((res) => {
if (res.errorNo !== 0) {
ElMessage.error(res.errorMsg)
} else {
sslSettings.type = res.data.type
if (sslSettings.type == "2") {
if (sslSettings.type === "2") {
sslSettings.type = "0"
sslSettings.challenge = "dns"
}
@ -392,24 +397,23 @@ const setSSLConfig = () => {
fullscreenLoading.value = true;
let sslType = sslSettings.type;
if (sslType == "0" && sslSettings.challenge == "dns") {
if (sslType === "0" && sslSettings.challenge === "dns") {
sslType = "2"
}
$http.post("/api/setup", {
http.post("/api/setup", {
"action": "set",
"step": "ssl",
"ssl_type": sslType,
"key_path": sslSettings.key_path,
"crt_path": sslSettings.crt_path
}).then((res) => {
if (res.errorNo != 0) {
if (res.errorNo !== 0) {
fullscreenLoading.value = false;
ElMessage.error(res.errorMsg)
} else {
if (sslType == 2) {
if (sslType === 2) {
fullscreenLoading.value = false;
dnsChecking.value = true;
getSSLDNSParams();
@ -422,18 +426,18 @@ const setSSLConfig = () => {
const checkStatus = () => {
axios.post("/api/ping", {}).then((res) => {
if (res.data.errorNo != 0) {
if (res.data.errorNo !== 0) {
setTimeout(function () {
checkStatus()
}, 1000);
} else {
if(sslSettings.type == 1){
if (sslSettings.type === 1) {
window.location.href = "http://" + domainSettings.web_domain;
}else{
} else {
window.location.href = "https://" + domainSettings.web_domain;
}
}
}).catch((error) => {
}).catch(() => {
setTimeout(function () {
checkStatus()
}, 1000);
@ -442,14 +446,14 @@ const checkStatus = () => {
const setDomainConfig = () => {
$http.post("/api/setup", {
http.post("/api/setup", {
"action": "set",
"step": "domain",
"web_domain": domainSettings.web_domain,
"smtp_domain": domainSettings.smtp_domain,
"multi_domain": domainSettings.multi_domain.join(",")
}).then((res) => {
if (res.errorNo != 0) {
if (res.errorNo !== 0) {
ElMessage.error(res.errorMsg)
} else {
active.value++;
@ -459,8 +463,8 @@ const setDomainConfig = () => {
}
const getSSLDNSParams = () => {
$http.post("/api/setup", { "action": "getParams", "step": "ssl" }).then((res) => {
if (res.errorNo != 0) {
http.post("/api/setup", {"action": "getParams", "step": "ssl"}).then((res) => {
if (res.errorNo !== 0) {
ElMessage.error(res.errorMsg)
} else {
sslSettings.paramsList = res.data
@ -468,14 +472,13 @@ const getSSLDNSParams = () => {
}
})
if (sslSettings.paramsList.length == 0) {
if (sslSettings.paramsList.length === 0) {
setTimeout(function () {
getSSLDNSParams()
}, 1000);
}
}
@ -526,7 +529,8 @@ const next = () => {
padding-right: 20px;
}
#status {}
#status {
}
.ctn {
display: flex;
@ -539,5 +543,6 @@ const next = () => {
}
#next {}
#next {
}
</style>