Sfoglia il codice sorgente

代码分离,功能优化 (#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>
HuangZhe 1 anno fa
parent
commit
ad0167f6fd

+ 3 - 1
fe/package.json

@@ -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"
   }
 }

+ 11 - 11
fe/src/App.vue

@@ -1,31 +1,31 @@
 <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
-  }
+    () => route.fullPath,
+    () => {
+      pageName.value = route.name
+    }
 )
 
 </script>
 
 <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>

+ 105 - 80
fe/src/components/GroupSettings.vue

@@ -1,98 +1,123 @@
 <template>
-    <div id="main">
-        <el-tree :expand-on-click-node="false" :data="data" :props="defaultProps" :defaultExpandAll="true" :class="node">
-            <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> -
-                    </el-button>
-                    <el-button v-if="data.id != 0" @click="add(data)" size="small" type="primary" circle> + </el-button>
-                </div>
-            </template>
-        </el-tree>
+  <div id="main">
+    <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
+          >
+            -
+          </el-button>
+          <el-button
+            v-if="data.id !== 0"
+            @click="add(data)"
+            size="small"
+            type="primary"
+            circle
+          >
+            +
+          </el-button>
+        </div>
+      </template>
+    </el-tree>
 
-        <el-button @click="addRoot">{{ lang.add_group }}</el-button>
-    </div>
+    <el-button @click="addRoot">{{ lang.add_group }}</el-button>
+  </div>
 </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) {
-                ElMessage({
-                    message: res.errorMsg,
-                    type: 'error',
-                })
-            } else {
-                const pc = node.parent.childNodes
-                for (let i = 0; i < pc.length; i++) {
-                    if (pc[i].id == node.id) {
-                        pc.splice(i, 1)
-                        return
-                    }
-                }
-            }
-        })
-    } else {
-        const pc = node.parent.childNodes
+  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",
+        });
+      } else {
+        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;
+    for (let i = 0; i < pc.length; i++) {
+      if (pc[i].id === node.id) {
+        pc.splice(i, 1);
+        return;
+      }
     }
-}
+  }
+};
 
 const add = function (item) {
-    if (item.children == null) {
-        item.children = []
-    }
-    item.children.push({
-        "children": [],
-        "label": "",
-        "id": "-1",
-        "parent_id": item.id
-    })
-}
+  if (item.children == null) {
+    item.children = [];
+  }
+  item.children.push({
+    children: [],
+    label: "",
+    id: "-1",
+    parent_id: item.id,
+  });
+};
 
 const addRoot = function () {
-    data.push({
-        "children": [],
-        "label": "",
-        "id": "-1",
-        "parent_id": 0
-    })
-}
+  data.push({
+    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) {
-                ElMessage({
-                    message: res.errorMsg,
-                    type: 'error',
-                })
-            } else {
-                this.$axios.get('/api/group').then(res => {
-                    data.splice(0, data.length)
-                    data.push(...res.data)
-                })
-            }
-        })
-    }
-}
-</script>
+  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",
+          });
+        } else {
+          this.$axios.get("/api/group").then((res) => {
+            data.splice(0, data.length);
+            data.push(...res.data);
+          });
+        }
+      });
+  }
+};
+</script>

+ 25 - 34
fe/src/components/HomeAside.vue

@@ -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;

+ 83 - 75
fe/src/components/HomeHeader.vue

@@ -1,76 +1,78 @@
 <template>
-    <div id="header_main">
-        <div id="logo">
-            <span style="padding-left: 20px;">PMail</span>
-        </div>
-        <div id="settings" @click="settings" v-if="$isLogin">
-            <el-icon style="font-size: 25px;">
-                <Setting 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 />
-                </el-tab-pane>
-
-                <el-tab-pane :label="lang.group_settings">
-                    <GroupSettings />
-                </el-tab-pane>
-
-                <el-tab-pane :label="lang.rule_setting">
-                    <RuleSettings />
-                </el-tab-pane>
-
-                <el-tab-pane v-if="$userInfos.is_admin" :label="lang.user_management">
-                    <UserManagement />
-                </el-tab-pane>
-
-                <el-tab-pane :label="lang.plugin_settings">
-                    <PluginSettings />
-                </el-tab-pane>
-
-            </el-tabs>
-        </el-drawer>
-
+  <div id="header_main">
+    <div id="logo">
+      <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">
+      <el-icon style="font-size: 25px;">
+        <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/>
+        </el-tab-pane>
+
+        <el-tab-pane :label="lang.group_settings">
+          <GroupSettings/>
+        </el-tab-pane>
+
+        <el-tab-pane :label="lang.rule_setting">
+          <RuleSettings/>
+        </el-tab-pane>
+
+        <el-tab-pane v-if="userInfos.is_admin" :label="lang.user_management">
+          <UserManagement/>
+        </el-tab-pane>
+
+        <el-tab-pane :label="lang.plugin_settings">
+          <PluginSettings/>
+        </el-tab-pane>
+
+      </el-tabs>
+    </el-drawer>
+
+  </div>
 </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
-                openSettings.value = true;
-
-            } else {
-                ElMessage({
-                    type: 'error',
-                    message: res.errorMsg,
-                })
-            }
-        })
-    }else{
+  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',
+          message: res.errorMsg,
+        })
+      }
+    })
+  } else {
+    openSettings.value = true;
+  }
 
 
 }
@@ -79,32 +81,38 @@ const settings = function () {
 
 
 <style scoped>
+
 #header_main {
-    height: 50px;
-    background-color: #000;
-    display: flex;
-    padding: 0;
+  height: 50px;
+  background-color: #000;
+  display: flex;
+  padding: 0;
 }
 
 #logo {
-    height: 3rem;
-    line-height: 3rem;
-    font-size: 2.3rem;
-    flex-grow: 1;
-    width: 200px;
-    color: #FFF;
-    text-align: left;
+  height: 3rem;
+  line-height: 3rem;
+  font-size: 2.3rem;
+  flex-grow: 1;
+  width: 200px;
+  color: #FFF;
+  text-align: left;
+}
+
+#logo h1 {
+  padding-left: 20px;
+  color: white;
 }
 
 #search {
-    height: 3rem;
-    width: 100%;
+  height: 3rem;
+  width: 100%;
 }
 
 #settings {
-    display: flex;
-    justify-content: center;
-    align-items: center;
-    padding-right: 20px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  padding-right: 20px;
 }
 </style>

+ 19 - 19
fe/src/components/PluginSettings.vue

@@ -1,35 +1,35 @@
 <template>
-    <div id="main">
-        <el-tabs>
-            <el-tab-pane v-for="(src, name) in pluginList" :label="name">
-                <iframe :src="src"></iframe>
-            </el-tab-pane>
-        </el-tabs>
-    </div>
+  <div id="main">
+    <el-tabs>
+      <el-tab-pane v-for="(src, name) in pluginList" :key="src" :label="name">
+        <iframe :src="src"></iframe>
+      </el-tab-pane>
+    </el-tabs>
+  </div>
 </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 => {
-    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";
-        }
+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";
     }
+  }
 })
 
 </script>
 
 <style scoped>
 
-iframe{
-    width: 100%;
-    border: 0;
+iframe {
+  width: 100%;
+  border: 0;
 }
 
 </style>

+ 173 - 184
fe/src/components/RuleSettings.vue

@@ -1,115 +1,105 @@
 <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="action" :label="lang.rule_do">
-            <template #default="scope">
-                {{ ActionName[scope.row.action] }}
+  <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="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>
+      <template #default="scope">
+        <div style="display: flex; align-items: center">
+          <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/>
             </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>
-            <template #default="scope">
-                <div style="display: flex; align-items: center">
-                    <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 />
-                        </template>
-                    </el-popconfirm>
-                </div>
-            </template>
-        </el-table-column>
-    </el-table>
-
-    <div>
-        <el-button @click="dialogVisible = true">{{ lang.new_rule }}</el-button>
-    </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-form-item>
-
-                <el-form-item :label="lang.rule_priority">
-                    <el-input v-model="addRuleForm.sort" type="number" oninput="value=value.replace(/[^\-\d]/g, '')" />
-                </el-form-item>
-                <el-divider />
-                <div style="width: 100%;">{{ lang.rule_desc }}</div>
-                <div style="width: 100%;">
-                    <div v-for="(rule, index) in addRuleForm.rules">
-                        <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-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-select>
-
-                        <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()" />
-                </div>
-                <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-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>
-
-                    <el-input v-if="addRuleForm.action == 2" v-model="addRuleForm.params" style="width: 250px;"
-                        placeholder="Forward Email Address" />
-
-                </el-form-item>
-
-            </el-form>
+          </el-popconfirm>
         </div>
-        <template #footer>
+      </template>
+    </el-table-column>
+  </el-table>
+
+  <div>
+    <el-button @click="dialogVisible = true">{{ lang.new_rule }}</el-button>
+  </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-form-item>
+
+        <el-form-item :label="lang.rule_priority">
+          <el-input v-model="addRuleForm.sort" type="number" oninput="value=value.replace(/[^\-\d]/g, '')"/>
+        </el-form-item>
+        <el-divider/>
+        <div style="width: 100%;">{{ lang.rule_desc }}</div>
+        <div style="width: 100%;">
+          <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-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-select>
+
+            <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()"/>
+        </div>
+        <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-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>
+
+          <el-input v-if="addRuleForm.action === 2" v-model="addRuleForm.params" style="width: 250px;"
+                    placeholder="Forward Email Address"/>
+
+        </el-form-item>
+
+      </el-form>
+    </div>
+    <template #footer>
             <span class="dialog-footer">
                 <el-button type="primary" @click="submitRule()">
                     {{ lang.submit }}
                 </el-button>
             </span>
-        </template>
-    </el-dialog>
+    </template>
+  </el-dialog>
 </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)
@@ -119,127 +109,126 @@ const DELETE = 3
 const MOVE = 4
 
 const ActionName = {
-    1: lang.mark_read,
-    2: lang.forward,
-    3: lang.delete,
-    4: lang.move
+  1: lang.mark_read,
+  2: lang.forward,
+  3: lang.delete,
+  4: lang.move
 }
 
 const init = function () {
-    $http.post("/api/rule/get").then((res) => {
-        data.value = res.data
-    })
+  http.post("/api/rule/get").then((res) => {
+    data.value = res.data
+  })
 }
 
 init()
 
 const groupData = reactive({
-    list: []
+  list: []
 })
 
 const reflushGroupInfos = function () {
-    $http.get('/api/group/list').then(res => {
-        if (res.data != null) {
-            groupData.list = res.data
-            for (let i = 0; i < groupData.list.length; i++) {
-                groupData.list[i].id += ""
-            }
-        }
+  http.get('/api/group/list').then(res => {
+    if (res.data != null) {
+      groupData.list = res.data
+      for (let i = 0; i < groupData.list.length; i++) {
+        groupData.list[i].id += ""
+      }
+    }
 
-    })
+  })
 }
 
 reflushGroupInfos()
 
 const addRuleForm = reactive({
-    "id": 0,
-    "name": "",
-    "sort": 0,
-    "rules": [
-        {
-            "field": "",
-            "type": "",
-            "rule": ""
-        }
-    ],
-    "action": "",
-    "params": ""
+  "id": 0,
+  "name": "",
+  "sort": 0,
+  "rules": [
+    {
+      "field": "",
+      "type": "",
+      "rule": ""
+    }
+  ],
+  "action": "",
+  "params": ""
 })
 
 const delRule = function (id) {
-    $http.post("/api/rule/del", { "id": id }).then((res) => {
-        ElNotification({
-            title: res.errorNo == 0 ? lang.succ : lang.fail,
-            message: res.data,
-            type: res.errorNo == 0 ? 'success' : 'error',
-        })
-
-        init()
+  http.post("/api/rule/del", {"id": id}).then((res) => {
+    ElNotification({
+      title: res.errorNo === 0 ? lang.succ : lang.fail,
+      message: res.data,
+      type: res.errorNo === 0 ? 'success' : 'error',
     })
+
+    init()
+  })
 }
 
 const editRule = function (ruleInfo) {
-    addRuleForm.id = ruleInfo.id
-    addRuleForm.name = ruleInfo.name
-    addRuleForm.rules = ruleInfo.rules
-    addRuleForm.action = ruleInfo.action
-    addRuleForm.params = ruleInfo.params
-    addRuleForm.sort = ruleInfo.sort
-    dialogVisible.value = true
+  addRuleForm.id = ruleInfo.id
+  addRuleForm.name = ruleInfo.name
+  addRuleForm.rules = ruleInfo.rules
+  addRuleForm.action = ruleInfo.action
+  addRuleForm.params = ruleInfo.params
+  addRuleForm.sort = ruleInfo.sort
+  dialogVisible.value = true
 }
 
 const removeRuleLine = function (index) {
-    addRuleForm.rules.splice(index, 1);
+  addRuleForm.rules.splice(index, 1);
 }
 
 const addRule = function () {
-    addRuleForm.rules.push(
-        {
-            "field": "",
-            "type": "",
-            "rule": ""
-        }
-    )
+  addRuleForm.rules.push(
+      {
+        "field": "",
+        "type": "",
+        "rule": ""
+      }
+  )
 }
 
 const submitRule = function () {
-    let api = "/api/rule/add"
-    if (addRuleForm.id > 0) {
-        api = "/api/rule/update"
-    }
-
-    addRuleForm.sort = parseInt(addRuleForm.sort)
-
-    $http.post(api, addRuleForm).then((res) => {
-        if (res.errorNo != 0) {
-            ElNotification({
-                title: lang.fail,
-                message: res.data,
-                type: 'error',
-            })
-        } else {
-            init()
-            dialogVisible.value = false
-
-            addRuleForm.id = 0
-            addRuleForm.name = ""
-            addRuleForm.sort = 0
-            addRuleForm.rules = [
-                {
-                    "field": "",
-                    "type": "",
-                    "rule": ""
-                }
-            ]
-            addRuleForm.action = ""
-            addRuleForm.params = ""
+  let api = "/api/rule/add"
+  if (addRuleForm.id > 0) {
+    api = "/api/rule/update"
+  }
+
+  addRuleForm.sort = parseInt(addRuleForm.sort)
+
+  http.post(api, addRuleForm).then((res) => {
+    if (res.errorNo !== 0) {
+      ElNotification({
+        title: lang.fail,
+        message: res.data,
+        type: 'error',
+      })
+    } else {
+      init()
+      dialogVisible.value = false
+
+      addRuleForm.id = 0
+      addRuleForm.name = ""
+      addRuleForm.sort = 0
+      addRuleForm.rules = [
+        {
+          "field": "",
+          "type": "",
+          "rule": ""
         }
-    })
+      ]
+      addRuleForm.action = ""
+      addRuleForm.params = ""
+    }
+  })
 }
 
 
-
 const ruleTypeChange = function () {
-    addRuleForm.params = ''
+  addRuleForm.params = ''
 }
 </script>

+ 53 - 56
fe/src/components/SecuritySettings.vue

@@ -1,78 +1,75 @@
 <template>
-    <el-form :model="ruleForm" :rules="rules" status-icon>
-
-        <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-form-item>
-
-        <el-form-item :label="lang.enter_again" prop="new_pwd2">
-            <el-input type="password" v-model="ruleForm.new_pwd2" />
-        </el-form-item>
-
-        <el-form-item>
-            <el-button type="primary" @click="submit">
-                {{ lang.submit }}
-            </el-button>
-        </el-form-item>
-
-        <el-divider content-position="left">{{lang.logout}}</el-divider>
-        <el-form-item>
-            <el-button type="primary" @click="logout">
-                {{ lang.logout }}
-            </el-button>
-        </el-form-item>
-    </el-form>
+  <el-form :model="ruleForm" :rules="rules" status-icon>
+
+    <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-form-item>
+
+    <el-form-item :label="lang.enter_again" prop="new_pwd2">
+      <el-input type="password" v-model="ruleForm.new_pwd2"/>
+    </el-form-item>
+
+    <el-form-item>
+      <el-button type="primary" @click="submit">
+        {{ lang.submit }}
+      </el-button>
+    </el-form-item>
+
+    <el-divider content-position="left">{{ lang.logout }}</el-divider>
+    <el-form-item>
+      <el-button type="primary" @click="logout">
+        {{ lang.logout }}
+      </el-button>
+    </el-form-item>
+  </el-form>
 </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: "",
-    new_pwd2: ""
+  new_pwd: "",
+  new_pwd2: ""
 })
 
 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 => {
-        location.reload();
-    })
+const logout = function () {
+  http.post("/api/logout", {}).then(() => {
+    location.reload();
+  })
 }
 
 const submit = function () {
-    if (ruleForm.new_pwd == ""){
-        return
-    }
+  if (ruleForm.new_pwd === "") {
+    return
+  }
 
 
-    if (ruleForm.new_pwd != ruleForm.new_pwd2) {
-        ElNotification({
-            title: 'Error',
-            message: lang.err_pwd_diff,
-            type: 'error',
-        })
-        return
-    }
-    $http.post("/api/settings/modify_password", { password: ruleForm.new_pwd }).then(res => {
-        ElNotification({
-            title: res.errorNo == 0 ? lang.succ : lang.fail,
-            message: res.data,
-            type: res.errorNo == 0 ? 'success' : 'error',
-        })
+  if (ruleForm.new_pwd !== ruleForm.new_pwd2) {
+    ElNotification({
+      title: 'Error',
+      message: lang.err_pwd_diff,
+      type: 'error',
+    })
+    return
+  }
+  http.post("/api/settings/modify_password", {password: ruleForm.new_pwd}).then(res => {
+    ElNotification({
+      title: res.errorNo === 0 ? lang.succ : lang.fail,
+      message: res.data,
+      type: res.errorNo === 0 ? 'success' : 'error',
     })
+  })
 
 
 }

+ 31 - 33
fe/src/components/UserManagement.vue

@@ -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; ">
+              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
       }
     })
   }
-
-
 }
 
 

+ 49 - 59
fe/src/i18n/i18n.js

@@ -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;

+ 2 - 111
fe/src/main.js

@@ -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')
-

+ 1 - 1
fe/src/router/index.js

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

+ 1 - 1
fe/src/stores/group.js

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

+ 15 - 0
fe/src/stores/useGlobalStatusStore.js

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

+ 103 - 0
fe/src/utils/axios.js

@@ -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};

+ 235 - 243
fe/src/views/EditerView.vue

@@ -1,247 +1,243 @@
 <template>
-    <div id="main">
-        <el-form label-width="100px" :rules="rules" ref="ruleFormRef" :model="ruleForm" status-icon>
-
-            <el-form-item :label="lang.sender" prop="sender">
-                <el-popover trigger="click" :width="600">
-                    <template #reference>
-                        <div
-                            style="border: 1px solid #dcdfe6; border-radius:3px;height: 30px; line-height: 30px; padding: 0 5px 0 5px;">
-                            <span style="font-size: 16px; font-weight: bolder;">{{ ruleForm.nickName }}</span>
-                            <span> &lt;{{ ruleForm.sender }}@{{ ruleForm.pickDomain }}&gt;</span>
-                        </div>
-                    </template>
-                    <template #default>
-                        <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" />
-                                    <div>@</div>
-                                    <el-select v-model="ruleForm.pickDomain">
-                                        <el-option :value="item" v-for="item in ruleForm.domains">{{ 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>
-                            </div>
-
-                        </div>
-                    </template>
-                </el-popover>
-
-            </el-form-item>
-
-
-            <el-form-item :label="lang.to" prop="receivers">
-                <el-select v-model="ruleForm.receivers" style="width: 100%;" multiple filterable allow-create
-                    :reserve-keyword="false" :placeholder="lang.to_desc"></el-select>
-            </el-form-item>
-
-
-            <el-form-item :label="lang.cc" prop="cc">
-                <el-select v-model="ruleForm.cc" style="width: 100%;" multiple filterable allow-create
-                    :reserve-keyword="false" :placeholder="lang.cc_desc"></el-select>
-            </el-form-item>
-
-
-            <el-form-item :label="lang.title" prop="subject">
-                <el-input v-model="ruleForm.subject" :placeholder="lang.title"></el-input>
-            </el-form-item>
-
-
-            <div id="editor">
-                <div style="border: 1px solid #ccc">
-                    <Toolbar style="border-bottom: 1px solid #ccc" :editor="editorRef" :defaultConfig="toolbarConfig"
-                        :mode="mode" />
-                    <Editor style="height: 300px;" v-model="valueHtml" :defaultConfig="editorConfig" :mode="mode"
-                        @onCreated="handleCreated" />
-                </div>
+  <div id="main">
+    <el-form label-width="100px" :rules="rules" ref="ruleFormRef" :model="ruleForm" status-icon>
+
+      <el-form-item :label="lang.sender" prop="sender">
+        <el-popover trigger="click" :width="600">
+          <template #reference>
+            <div
+                style="border: 1px solid #dcdfe6; border-radius:3px;height: 30px; line-height: 30px; padding: 0 5px 0 5px;">
+              <span style="font-size: 16px; font-weight: bolder;">{{ ruleForm.nickName }}</span>
+              <span> &lt;{{ ruleForm.sender }}@{{ ruleForm.pickDomain }}&gt;</span>
             </div>
+          </template>
+          <template #default>
+            <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"/>
+                  <div>@</div>
+                  <el-select v-model="ruleForm.pickDomain">
+                    <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>
+              </div>
 
-            <div id="fileList">
-                <ol>
-                    <li v-for="(item, index) in fileList">{{ item.name }} <el-icon @click="delFile(index)">
-                            <Close />
-                        </el-icon> </li>
-                </ol>
             </div>
+          </template>
+        </el-popover>
 
-            <div id="sendButton">
-                <el-button type="primary" @click="send(ruleFormRef)">{{ lang.send }}</el-button>
-                <!-- <el-button>定时发送</el-button> -->
+      </el-form-item>
 
-                <div style="margin-left: 15px">
-                    <el-button @click="upload">{{ lang.add_att }}</el-button>
-                    <input v-show="false" ref="fileRef" type="file" @change="fileChange">
-                </div>
-            </div>
+
+      <el-form-item :label="lang.to" prop="receivers">
+        <el-select v-model="ruleForm.receivers" style="width: 100%;" multiple filterable allow-create
+                   :reserve-keyword="false" :placeholder="lang.to_desc"></el-select>
+      </el-form-item>
+
+
+      <el-form-item :label="lang.cc" prop="cc">
+        <el-select v-model="ruleForm.cc" style="width: 100%;" multiple filterable allow-create
+                   :reserve-keyword="false" :placeholder="lang.cc_desc"></el-select>
+      </el-form-item>
+
+
+      <el-form-item :label="lang.title" prop="subject">
+        <el-input v-model="ruleForm.subject" :placeholder="lang.title"></el-input>
+      </el-form-item>
 
 
+      <div id="editor">
+        <div style="border: 1px solid #ccc">
+          <Toolbar style="border-bottom: 1px solid #ccc" :editor="editorRef" :defaultConfig="toolbarConfig"
+                   :mode="mode"/>
+          <Editor style="height: 300px;" v-model="valueHtml" :defaultConfig="editorConfig" :mode="mode"
+                  @onCreated="handleCreated"/>
+        </div>
+      </div>
 
-        </el-form>
+      <div id="fileList">
+        <ol>
+          <li v-for="(item, index) in fileList" :key="item">{{ item.name }}
+            <el-icon @click="delFile(index)">
+              <Close/>
+            </el-icon>
+          </li>
+        </ol>
+      </div>
 
-    </div>
+      <div id="sendButton">
+        <el-button type="primary" @click="send(ruleFormRef)">{{ lang.send }}</el-button>
+        <!-- <el-button>定时发送</el-button> -->
+
+        <div style="margin-left: 15px">
+          <el-button @click="upload">{{ lang.add_att }}</el-button>
+          <input v-show="false" ref="fileRef" type="file" @change="fileChange">
+        </div>
+      </div>
+
+    </el-form>
+
+  </div>
 </template>
 
 <style scoped>
 #main {
-    text-align: left;
-    padding-right: 20px;
+  text-align: left;
+  padding-right: 20px;
 }
 
 #editor {
-    padding-left: 25px;
+  padding-left: 25px;
 }
 
 #sendButton {
-    padding-left: 25px;
-    padding-top: 5px;
-    display: flex;
+  padding-left: 25px;
+  padding-top: 5px;
+  display: flex;
 }
 </style>
 
 
 <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'
+import {useGlobalStatusStore} from "@/stores/useGlobalStatusStore";
+
+const router = useRouter();
+
 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
-
-if (lang.lang == "zhCn") {
-    i18nChangeLanguage('zh-CN')
+
+const globalStatus = useGlobalStatusStore();
+const userInfos = globalStatus.userInfos
+
+if (lang.lang === "zhCn") {
+  i18nChangeLanguage('zh-CN')
 } else {
-    i18nChangeLanguage('en')
+  i18nChangeLanguage('en')
 }
 
 
-
 // 内容 HTML
 const valueHtml = ref('<p>hello</p>')
 
 const toolbarConfig = {}
 const editorConfig = {
-    MENU_CONF: {},
-    placeholder: ''
+  MENU_CONF: {},
+  placeholder: ''
 }
 
 
 editorConfig.MENU_CONF['uploadImage'] = {
-    base64LimitSize: 100 * 1024 * 1024 * 1024,  // 100G以下的文件都base64传
+  base64LimitSize: 100 * 1024 * 1024 * 1024,  // 100G以下的文件都base64传
 }
 const mode = ref()
 const fileRef = ref();
-const pickFile = ref();
 const ruleFormRef = ref()
 const ruleForm = reactive({
-    nickName: '',
-    sender: '',
-    receivers: '',
-    cc: '',
-    subject: '',
-    domains: [],
-    pickDomain: ""
+  nickName: '',
+  sender: '',
+  receivers: '',
+  cc: '',
+  subject: '',
+  domains: [],
+  pickDomain: ""
 })
 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
-                ruleForm.sender = res.data.account
-                ruleForm.domains = res.data.domains
-                ruleForm.pickDomain = res.data.domains[0]
-                ruleForm.nickName = res.data.name
-            } else {
-                ElMessage({
-                    type: 'error',
-                    message: res.errorMsg,
-                })
-            }
+  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]
+        ruleForm.nickName = res.data.name
+      } else {
+        ElMessage({
+          type: 'error',
+          message: res.errorMsg,
         })
-    } else {
-        ruleForm.sender = $userInfos.value.account
-        ruleForm.domains = $userInfos.value.domains
-        ruleForm.pickDomain = $userInfos.value.domains[0]
-        ruleForm.nickName = $userInfos.value.name
-    }
+      }
+    })
+  } else {
+    ruleForm.sender = userInfos.value.account
+    ruleForm.domains = userInfos.value.domains
+    ruleForm.pickDomain = userInfos.value.domains[0]
+    ruleForm.nickName = userInfos.value.name
+  }
 
 }
 init()
 
 
 const validateSender = function (rule, value, callback) {
-    if (typeof ruleForm.sender === "undefined" || ruleForm.sender === null || ruleForm.sender.trim() === "") {
-        callback(new Error(lang.err_sender_must))
-    } else if (ruleForm.sender.includes("@")) {
-        callback(new Error(lang.only_prefix))
-    } else {
-        callback()
-    }
+  if (typeof ruleForm.sender === "undefined" || ruleForm.sender === null || ruleForm.sender.trim() === "") {
+    callback(new Error(lang.err_sender_must))
+  } else if (ruleForm.sender.includes("@")) {
+    callback(new Error(lang.only_prefix))
+  } else {
+    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) {
-    for (let index = 0; index < ruleForm.receivers.length; index++) {
-        let element = ruleForm.receivers[index];
-        if (!checkEmail(element)) {
-            callback(new Error(lang.err_email_format))
-            return
-        }
+  for (let index = 0; index < ruleForm.receivers.length; index++) {
+    let element = ruleForm.receivers[index];
+    if (!checkEmail(element)) {
+      callback(new Error(lang.err_email_format))
+      return
     }
-    callback()
+  }
+  callback()
 }
 
 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))
-            return
-        }
+  for (let index = 0; index < ruleForm.cc.length; index++) {
+    let element = ruleForm.cc[index];
+    if (!checkEmail(element)) {
+      callback(new Error(lang.err_email_format))
+      return
     }
-    callback()
+  }
+  callback()
 }
 
 const rules = reactive({
-    sender: [
-        { validator: validateSender, trigger: 'change' }
-    ],
-    receivers: [
-        { validator: validateReceivers, trigger: 'change' }
-    ],
-    cc: [
-        { validator: validateCc, trigger: 'change' }
-    ],
-    subject: [
-        { required: true, message: lang.err_title_must, trigger: 'change' },
-    ],
+  sender: [
+    {validator: validateSender, trigger: 'change'}
+  ],
+  receivers: [
+    {validator: validateReceivers, trigger: 'change'}
+  ],
+  cc: [
+    {validator: validateCc, trigger: 'change'}
+  ],
+  subject: [
+    {required: true, message: lang.err_title_must, trigger: 'change'},
+  ],
 })
 
 
@@ -249,97 +245,93 @@ const rules = reactive({
 const editorRef = shallowRef()
 // 组件销毁时,也及时销毁编辑器
 onBeforeUnmount(() => {
-    const editor = editorRef.value
-    if (editor == null) return
-    editor.destroy()
+  const editor = editorRef.value
+  if (editor == null) return
+  editor.destroy()
 })
 
 const handleCreated = (editor) => {
-    editorRef.value = editor // 记录 editor 实例,重要!
+  editorRef.value = editor // 记录 editor 实例,重要!
 }
 
 const send = function (formEl) {
-    if (!formEl) return
-    formEl.validate((valid) => {
-        if (valid) {
-            let objectTos = []
-            for (let index = 0; index < ruleForm.receivers.length; index++) {
-                let element = ruleForm.receivers[index];
-                objectTos.push({
-                    name: "",
-                    email: element
-                })
-            }
-
-            let objectCcs = []
-            for (let index = 0; index < ruleForm.cc.length; index++) {
-                let element = ruleForm.cc[index];
-                objectCcs.push({
-                    name: "",
-                    email: element
-                })
-            }
-
-            let text = editorRef.value.getText()
-
-            $http.post("/api/email/send", {
-                from: { name: ruleForm.nickName, email: ruleForm.sender + "@" + ruleForm.pickDomain },
-                to: objectTos,
-                cc: objectCcs,
-                subject: ruleForm.subject,
-                text: text,
-                html: valueHtml.value,
-                attrs: fileList
-            }).then(res => {
-                if (res.errorNo === 0) {
-                    ElMessage({
-                        message: lang.succ_send,
-                        type: 'success',
-                    })
-                    groupStore.name = lang.outbox
-                    groupStore.tag = '{"type":1,"status":-1}'
-                    router.replace({
-                        name: 'list',
-                    })
-                } else {
-                    ElMessage.error(res.data)
-                }
-            })
+  if (!formEl) return
+  formEl.validate((valid) => {
+    if (valid) {
+      let objectTos = []
+      for (let index = 0; index < ruleForm.receivers.length; index++) {
+        let element = ruleForm.receivers[index];
+        objectTos.push({
+          name: "",
+          email: element
+        })
+      }
+
+      let objectCcs = []
+      for (let index = 0; index < ruleForm.cc.length; index++) {
+        let element = ruleForm.cc[index];
+        objectCcs.push({
+          name: "",
+          email: element
+        })
+      }
+
+      let text = editorRef.value.getText()
+
+      http.post("/api/email/send", {
+        from: {name: ruleForm.nickName, email: ruleForm.sender + "@" + ruleForm.pickDomain},
+        to: objectTos,
+        cc: objectCcs,
+        subject: ruleForm.subject,
+        text: text,
+        html: valueHtml.value,
+        attrs: fileList
+      }).then(res => {
+        if (res.errorNo === 0) {
+          ElMessage({
+            message: lang.succ_send,
+            type: 'success',
+          })
+          groupStore.name = lang.outbox
+          groupStore.tag = '{"type":1,"status":-1}'
+          router.replace({
+            name: 'list',
+          })
         } else {
-            return false
+          ElMessage.error(res.data)
         }
-    })
-
-
+      })
+    } else {
+      return false
+    }
+  })
 
 
 }
 
 
-
-
 const upload = function () {
-    fileRef.value.dispatchEvent(new MouseEvent('click'))
+  fileRef.value.dispatchEvent(new MouseEvent('click'))
 }
 
 const fileChange = function (e) {
-    let files = e.target.files || e.dataTransfer.files;
-    if (!files.length)
-        return;
-    for (let i = 0; i < files.length; i++) {
-        const reader = new FileReader();
-        reader.onload = function fileReadCompleted() {
-            fileList.push({
-                name: files[i].name,
-                data: this.result
-            })
-        };
-        reader.readAsDataURL(files[i]);
-
-    }
+  let files = e.target.files || e.dataTransfer.files;
+  if (!files.length)
+    return;
+  for (let i = 0; i < files.length; i++) {
+    const reader = new FileReader();
+    reader.onload = function fileReadCompleted() {
+      fileList.push({
+        name: files[i].name,
+        data: this.result
+      })
+    };
+    reader.readAsDataURL(files[i]);
+
+  }
 }
 
 const delFile = function (index) {
-    fileList.splice(index, 1);
+  fileList.splice(index, 1);
 }
 </script>

+ 82 - 81
fe/src/views/EmailDetailView.vue

@@ -1,117 +1,118 @@
 <template>
-    <div id="main">
-        <div id="title">{{ detailData.subject }}</div>
-        <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>
-            </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>
-            </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-tooltip>
-            </div>
-
-            <div>{{ lang.date }}:
-                {{ detailData.send_date }}
-            </div>
-        </div>
-        <el-divider />
-        <div class="content" id="text" v-if="detailData.html == ''">
-            {{ detailData.text }}
-        </div>
-
-        <div class="content" id="html" v-else v-html="detailData.html">
-
-        </div>
-
-        <div v-if="detailData.attachments.length > 0" style="">
-            <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>
-        </div>
+  <div id="main">
+    <div id="title">{{ detailData.subject }}</div>
+    <el-divider/>
+
+    <div>
+      <div>{{ lang.to }}:
+        <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" :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-tooltip>
+      </div>
+
+      <div>{{ lang.date }}:
+        {{ detailData.send_date }}
+      </div>
+    </div>
+    <el-divider/>
+    <div class="content" id="text" v-if="detailData.html === ''">
+      {{ detailData.text }}
+    </div>
 
+    <div class="content" id="html" v-else v-html="detailData.html">
 
     </div>
+
+    <div v-if="detailData.attachments.length > 0" style="">
+      <el-divider/>
+      {{ lang.attachment }}:
+      <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>
+
+
+  </div>
 </template>
 
 <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 => {
-    detailData.value = res.data
-    if (res.data.to != "" && res.data.to != null) {
-        tos.value = JSON.parse(res.data.to)
-    }
-    if (res.data.cc != "" && res.data.cc != null) {
-        ccs.value = JSON.parse(res.data.cc)
-
-    }
-
-    if (ccs.value != null && ccs.value != undefined){
-         showCC.value = ccs.value.length > 0
-    }else{
-        showCC.value = false
-    }
+http.post("/api/email/detail", {id: parseInt(route.params.id)}).then(res => {
+  detailData.value = res.data
+  if (res.data.to !== "" && res.data.to != null) {
+    tos.value = JSON.parse(res.data.to)
+  }
+  if (res.data.cc !== "" && res.data.cc != null) {
+    ccs.value = JSON.parse(res.data.cc)
+
+  }
+
+  if (ccs.value != null) {
+    showCC.value = ccs.value.length > 0
+  } else {
+    showCC.value = false
+  }
 })
 </script>
 
 <style scoped>
 #main {
-    display: flex;
-    padding-left: 20px;
-    padding-right: 80px;
-    text-align: left;
+  display: flex;
+  padding-left: 20px;
+  padding-right: 80px;
+  text-align: left;
 }
 
 #title {
-    font-size: 40px;
-    text-align: left;
+  font-size: 40px;
+  text-align: left;
 }
 
-#userItem {}
+#userItem {
+}
 
 .content {
-    /* background-color: aliceblue; */
+  /* background-color: aliceblue; */
 }
 
-a,a:link,a:visited,a:hover,a:active{
-    text-decoration: none;
-    color:inherit;
+a, a:link, a:visited, a:hover, a:active {
+  text-decoration: none;
+  color: inherit;
 }
 
-.att{
-    display:block;
+.att {
+  display: block;
 }
 </style>

+ 206 - 213
fe/src/views/ListView.vue

@@ -1,269 +1,262 @@
 <template>
-    <div style="height: 100%">
-        <div id="operation">
-            <div id="action">
-                <RouterLink to="/editer">+{{ lang.compose }}</RouterLink>
-            </div>
-        </div>
-        <div id="title">{{ groupStore.name }}</div>
-        <div id="action">
-            <el-button @click="del" size="small">{{ lang.del_btn }}</el-button>
-            <el-button @click="markRead" size="small">{{ lang.read_btn }}</el-button>
-            <el-dropdown style="margin-left: 12px;">
-                <el-button size="small">
-                    {{ lang.move_btn }}
-                    <el-icon class="el-icon--right"><arrow-down /></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-menu>
-                </template>
-            </el-dropdown>
-        </div>
-        <div id="table">
-            <el-table ref="taskTableDataRef" @selection-change="selectionLineChange" :data="data" :show-header="true"
+  <div style="height: 100%">
+    <div id="operation">
+      <div id="action">
+        <RouterLink to="/editer">+{{ lang.compose }}</RouterLink>
+      </div>
+    </div>
+    <div id="title">{{ groupStore.name }}</div>
+    <div id="action">
+      <el-button @click="del" size="small">{{ lang.del_btn }}</el-button>
+      <el-button @click="markRead" size="small">{{ lang.read_btn }}</el-button>
+      <el-dropdown style="margin-left: 12px;">
+        <el-button size="small">
+          {{ lang.move_btn }}
+          <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" :key="group.id">{{
+                group.name
+              }}
+            </el-dropdown-item>
+          </el-dropdown-menu>
+        </template>
+      </el-dropdown>
+    </div>
+    <div id="table">
+      <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 prop="is_read" label="" width="50">
-                    <template #default="scope">
-                        <div>
+        <el-table-column type="selection" width="30"/>
+        <el-table-column prop="is_read" label="" width="50">
+          <template #default="scope">
+            <div>
                             <span v-if="!scope.row.is_read">
                                 {{ lang.new }}
                             </span>
-                            <span style="font-weight: 900;color: #FF0000;" v-if="scope.row.dangerous">
+              <span style="font-weight: 900;color: #FF0000;" v-if="scope.row.dangerous">
                                 <el-tooltip effect="dark" :content="lang.dangerous" placement="top-start">
                                     !
                                 </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>
 
                             </span>
-                        </div>
-                    </template>
-                </el-table-column>
-                <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-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>
-                    </template>
-                </el-table-column>
-
-                <el-table-column prop="desc" :label="lang.title">
-                    <template #default="scope">
-                        <div v-if="scope.row.is_read">{{ scope.row.title }}</div>
-                        <div v-else style="font-weight:bolder;">{{ scope.row.title }}</div>
-
-                        <div style="font-size: 12px;height: 24px;">{{ scope.row.desc }}</div>
-
-                    </template>
-                </el-table-column>
-                <el-table-column prop="datetime" :label="lang.date" width="180">
-                    <template #default="scope">
-                        <span v-if="scope.row.is_read">{{ scope.row.datetime }}</span>
-                        <span v-else style="font-weight:bolder;">{{ scope.row.datetime }}</span>
-                    </template>
-                </el-table-column>
-            </el-table>
-        </div>
-        <div id="pagination">
-            <el-pagination background layout="prev, pager, next" :page-count="totalPage" @current-change="pageChange" />
-        </div>
+            </div>
+          </template>
+        </el-table-column>
+        <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-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" :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>
+
+        <el-table-column prop="desc" :label="lang.title">
+          <template #default="scope">
+            <div v-if="scope.row.is_read">{{ scope.row.title }}</div>
+            <div v-else style="font-weight:bolder;">{{ scope.row.title }}</div>
+
+            <div style="font-size: 12px;height: 24px;">{{ scope.row.desc }}</div>
+
+          </template>
+        </el-table-column>
+        <el-table-column prop="datetime" :label="lang.date" width="180">
+          <template #default="scope">
+            <span v-if="scope.row.is_read">{{ scope.row.datetime }}</span>
+            <span v-else style="font-weight:bolder;">{{ scope.row.datetime }}</span>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+    <div id="pagination">
+      <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 == "") {
-    tag = '{"type":0,"status":-1}'
+if (tag === "") {
+  tag = '{"type":0,"status":-1}'
 }
 
 
-watch(groupStore, async (newV, oldV) => {
-    tag = newV.tag;
-    if (tag == "") {
-        tag = '{"type":0,"status":-1}'
-    }
-    data.value = []
-    $http.post("/api/email/list", { tag: tag, page_size: 10 }).then(res => {
-        data.value = res.data.list
-        totalPage.value = res.data.total_page
-    })
+watch(groupStore, async (newV) => {
+  tag = newV.tag;
+  if (tag === "") {
+    tag = '{"type":0,"status":-1}'
+  }
+  data.value = []
+  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 => {
-        data.value = res.data.list
-        totalPage.value = res.data.total_page
-    })
+  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 => {
-        groupList.value = res.data
-    })
+  http.post("/api/group/list").then(res => {
+    groupList.value = res.data
+  })
 }
 
 updateList()
 updateGroupList()
 
-const rowClick = function (row, column, event) {
-    router.push("/detail/" + row.id)
+const rowClick = function (row) {
+  router.push("/detail/" + row.id)
 }
 
 const markRead = function () {
-    let rows = taskTableDataRef.value?.getSelectionRows()
-    let ids = []
-    rows.forEach(element => {
-        ids.push(element.id)
-    });
-
-    $http.post("/api/email/read", { "ids": ids }).then(res => {
-        if (res.errorNo == 0) {
-            updateList()
-        } else {
-            ElMessage({
-                type: 'error',
-                message: res.errorMsg,
-            })
-        }
-    })
+  let rows = taskTableDataRef.value?.getSelectionRows()
+  let ids = []
+  rows.forEach(element => {
+    ids.push(element.id)
+  });
+
+  http.post("/api/email/read", {"ids": ids}).then(res => {
+    if (res.errorNo === 0) {
+      updateList()
+    } else {
+      ElMessage({
+        type: 'error',
+        message: res.errorMsg,
+      })
+    }
+  })
 }
 
 
 const move = function (group_id) {
-    let rows = taskTableDataRef.value?.getSelectionRows()
-    let ids = []
-    rows.forEach(element => {
-        ids.push(element.id)
-    });
-
-    ElMessageBox.confirm(
-        lang.move_email_confirm,
-        'Warning',
-        {
-            confirmButtonText: 'OK',
-            cancelButtonText: 'Cancel',
-            type: 'warning',
-        }
-    )
-        .then(() => {
-            $http.post("/api/email/move", { "group_id": group_id, "ids": ids }).then(res => {
-                if (res.errorNo == 0) {
-                    updateList()
-                    ElMessage({
-                        type: 'success',
-                        message: 'Move completed',
-                    })
-                } else {
-                    ElMessage({
-                        type: 'error',
-                        message: res.errorMsg,
-                    })
-                }
+  let rows = taskTableDataRef.value?.getSelectionRows()
+  let ids = []
+  rows.forEach(element => {
+    ids.push(element.id)
+  });
+
+  ElMessageBox.confirm(
+      lang.move_email_confirm,
+      'Warning',
+      {
+        confirmButtonText: 'OK',
+        cancelButtonText: 'Cancel',
+        type: 'warning',
+      }
+  )
+      .then(() => {
+        http.post("/api/email/move", {"group_id": group_id, "ids": ids}).then(res => {
+          if (res.errorNo === 0) {
+            updateList()
+            ElMessage({
+              type: 'success',
+              message: 'Move completed',
             })
+          } else {
+            ElMessage({
+              type: 'error',
+              message: res.errorMsg,
+            })
+          }
+        })
 
 
-
-        })
+      })
 }
 
 
-
 const del = function () {
-    let rows = taskTableDataRef.value?.getSelectionRows()
-    let ids = []
-    rows.forEach(element => {
-        ids.push(element.id)
-    });
-
-    let groupTag = JSON.parse(tag)
-
-
-    ElMessageBox.confirm(
-        lang.del_email_confirm,
-        'Warning',
-        {
-            confirmButtonText: 'OK',
-            cancelButtonText: 'Cancel',
-            type: 'warning',
-        }
-    )
-        .then(() => {
-            $http.post("/api/email/del", { "ids": ids ,"forcedDel":groupTag.status == 3 }).then(res => {
-                if (res.errorNo == 0) {
-                    updateList()
-                    ElMessage({
-                        type: 'success',
-                        message: 'Delete completed',
-                    })
-                } else {
-                    ElMessage({
-                        type: 'error',
-                        message: res.errorMsg,
-                    })
-                }
+  let rows = taskTableDataRef.value?.getSelectionRows()
+  let ids = []
+  rows.forEach(element => {
+    ids.push(element.id)
+  });
+
+  let groupTag = JSON.parse(tag)
+
+
+  ElMessageBox.confirm(
+      lang.del_email_confirm,
+      'Warning',
+      {
+        confirmButtonText: 'OK',
+        cancelButtonText: 'Cancel',
+        type: 'warning',
+      }
+  )
+      .then(() => {
+        http.post("/api/email/del", {"ids": ids, "forcedDel": groupTag.status === 3}).then(res => {
+          if (res.errorNo === 0) {
+            updateList()
+            ElMessage({
+              type: 'success',
+              message: 'Delete completed',
             })
+          } else {
+            ElMessage({
+              type: 'error',
+              message: res.errorMsg,
+            })
+          }
+        })
 
 
-
-        })
+      })
 }
 
 
-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 => {
-        data.value = res.data.list
-    })
+  http.post("/api/email/list", {tag: tag, page_size: 10, current_page: p}).then(res => {
+    data.value = res.data.list
+  })
 }
 
 </script>
@@ -271,41 +264,41 @@ const pageChange = function (p) {
 
 <style scoped>
 #action {
-    display: flex;
-    flex-direction: row;
+  display: flex;
+  flex-direction: row;
 }
 
 
 #action a,
 a:visited {
-    color: #000000;
-    text-decoration: none;
+  color: #000000;
+  text-decoration: none;
 }
 
 #operation {
-    display: flex;
-    height: 40px;
-    background-color: rgb(236, 244, 251);
+  display: flex;
+  height: 40px;
+  background-color: rgb(236, 244, 251);
 }
 
 #title {
-    margin-top: 10px;
-    font-size: 23px;
-    text-align: left;
-    padding-left: 20px;
+  margin-top: 10px;
+  font-size: 23px;
+  text-align: left;
+  padding-left: 20px;
 }
 
 #table {
-    text-align: left;
-    width: 100%;
-    padding-left: 20px;
+  text-align: left;
+  width: 100%;
+  padding-left: 20px;
 }
 
 #pagination {
-    padding-top: 30px;
-    display: flex;
-    justify-content: center;
-    /* 水平居中 */
-    width: 100%;
+  padding-top: 30px;
+  display: flex;
+  justify-content: center;
+  /* 水平居中 */
+  width: 100%;
 }
 </style>

+ 47 - 45
fe/src/views/LoginView.vue

@@ -1,54 +1,56 @@
 <template>
-    <div id="main">
-        <div id="form">
-            <el-form :model="form" label-width="120px" @keyup.enter.native="onSubmit">
-                <el-form-item :label="lang.account">
-                    <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-form-item>
-                <el-form-item>
-                    <el-button type="primary" @click="onSubmit">{{ lang.login }}</el-button>
-                </el-form-item>
-            </el-form>
+  <div id="main">
+    <div id="form">
+      <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-form-item>
+        <el-form-item :label="lang.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>
+        </el-form-item>
+      </el-form>
 
-        </div>
     </div>
+  </div>
 </template>
 
 <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: '',
-    password: '',
+  account: '',
+  password: '',
 })
 
 const onSubmit = () => {
-    $http.post("/api/login", form).then(res => {
-        if (res.errorNo != 0) {
-            ElMessage.error(res.errorMsg)
-        } else {
-            $isLogin.value = true
-            $userInfos.value = res.data
-            router.replace({
-                path: '/',
-                query: {
-                    redirect: router.currentRoute.fullPath
-                }
-            })
+  http.post("/api/login", form).then(res => {
+    if (res.errorNo !== 0) {
+      ElMessage.error(res.errorMsg)
+    } else {
+      isLogin = true
+      userInfos.value = res.data
+      router.replace({
+        path: '/',
+        query: {
+          redirect: router.currentRoute.fullPath
         }
-    })
+      })
+    }
+  })
 
 }
 </script>
@@ -56,13 +58,13 @@ const onSubmit = () => {
 
 <style scoped>
 #main {
-    width: 100%;
-    height: 100%;
-    background-color: #f1f1f1;
-    display: flex;
-    justify-content: center;
-    /* 水平居中 */
-    align-items: center;
-    /* 垂直居中 */
+  width: 100%;
+  height: 100%;
+  background-color: #f1f1f1;
+  display: flex;
+  justify-content: center;
+  /* 水平居中 */
+  align-items: center;
+  /* 垂直居中 */
 }
 </style>

+ 432 - 427
fe/src/views/SetupView.vue

@@ -1,269 +1,264 @@
 <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-steps>
-
-
-        <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 class="desc">
-                <h2>{{ lang.select_db }}</h2>
-                <div style="margin-top: 10px;">{{ lang.db_desc }}</div>
-            </div>
-            <div class="form" style="width: 400px;">
-                <el-form label-width="120px">
-                    <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-select>
-                    </el-form-item>
-
-                    <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-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-input v-model="dbSettings.dsn" placeholder="./config/pmail.db"></el-input>
-                    </el-form-item>
-                </el-form>
-            </div>
-        </div>
-
-
-        <div v-if="active == 2" class="ctn">
-            <div class="desc">
-                <h2>{{ lang.setAdminPassword }}</h2>
-                <!-- <div style="margin-top: 10px;">{{ lang.domain_desc }}</div> -->
-            </div>
-            <div class="form" style="width: 400px;">
-                <el-form label-width="120px">
-
-                    <el-form-item :label="lang.admin_account">
-                        <el-input v-bind:disabled="adminSettings.hadSeted" placeholder="admin"
-                            v-model="adminSettings.account"></el-input>
-                    </el-form-item>
-
-                    <el-form-item :label="lang.password">
-                        <el-input type="password" v-bind:disabled="adminSettings.hadSeted" placeholder=""
-                            v-model="adminSettings.password"></el-input>
-                    </el-form-item>
-
-                    <el-form-item :label="lang.enter_again">
-                        <el-input type="password" v-bind:disabled="adminSettings.hadSeted" placeholder=""
-                            v-model="adminSettings.password2"></el-input>
-                    </el-form-item>
-                </el-form>
-            </div>
-        </div>
-
-
-        <div v-if="active == 3" class="ctn">
-            <div class="desc">
-                <h2>{{ lang.SetDomail }}</h2>
-                <!-- <div style="margin-top: 10px;">{{ lang.domain_desc }}</div> -->
-            </div>
-            <div class="form" style="width: 400px;">
-                <el-form label-width="120px">
-
-                    <el-form-item :label="lang.smtp_domain">
-                        <el-input placeholder="domaim.com" v-model="domainSettings.smtp_domain">
-                            <template #prepend>smtp.</template>
-                        </el-input>
-                    </el-form-item>
-
-                    <el-form-item :label="lang.web_domain">
-                        <el-input placeholder="pmail.domain.com" v-model="domainSettings.web_domain"></el-input>
-                    </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>
-                    </el-form-item>
-
-
-                </el-form>
-            </div>
-        </div>
-
-        <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">
-                <h3>{{ domain }}</h3>
-                <el-table :data="info" border style="width: 100%">
-                    <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 == '@' ">
-                                    {{ 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="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 != ''">
-                                    {{ 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>
-            </div>
-        </div>
-
-        <el-alert :closable="false" title="Warning!" type="error" center
-            v-if="active == 5 && sslSettings.type == 0 && port != 80" :description="lang.autoSSLWarn" />
-
-        <div v-if="active == 5" class="ctn">
-            <div class="desc">
-                <h2>{{ lang.setSSL }}</h2>
-                <div style="margin-top: 10px;">{{ lang.setSSL }}</div>
-            </div>
-            <div class="form" width="600px">
-                <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-select>
-                    </el-form-item>
-
-                    <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-select>
+  <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-steps>
+
+
+    <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>
 
-                        <el-tooltip class="box-item" effect="dark" :content="lang.challenge_typ_desc"
-                            placement="top-start">
-                            <span style="margin-left: 6px; font-size:18px; font-weight: bolder;">?</span>
-                        </el-tooltip>
-                    </el-form-item>
+    <div v-if="active === 1" class="ctn">
+      <div class="desc">
+        <h2>{{ lang.select_db }}</h2>
+        <div style="margin-top: 10px;">{{ lang.db_desc }}</div>
+      </div>
+      <div class="form" style="width: 400px;">
+        <el-form label-width="120px">
+          <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-select>
+          </el-form-item>
+
+          <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-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-input v-model="dbSettings.dsn" placeholder="./config/pmail.db"></el-input>
+          </el-form-item>
+        </el-form>
+      </div>
+    </div>
 
 
+    <div v-if="active === 2" class="ctn">
+      <div class="desc">
+        <h2>{{ lang.setAdminPassword }}</h2>
+        <!-- <div style="margin-top: 10px;">{{ lang.domain_desc }}</div> -->
+      </div>
+      <div class="form" style="width: 400px;">
+        <el-form label-width="120px">
+
+          <el-form-item :label="lang.admin_account">
+            <el-input v-bind:disabled="adminSettings.hadSeted" placeholder="admin"
+                      v-model="adminSettings.account"></el-input>
+          </el-form-item>
+
+          <el-form-item :label="lang.password">
+            <el-input type="password" v-bind:disabled="adminSettings.hadSeted" placeholder=""
+                      v-model="adminSettings.password"></el-input>
+          </el-form-item>
+
+          <el-form-item :label="lang.enter_again">
+            <el-input type="password" v-bind:disabled="adminSettings.hadSeted" placeholder=""
+                      v-model="adminSettings.password2"></el-input>
+          </el-form-item>
+        </el-form>
+      </div>
+    </div>
 
 
+    <div v-if="active === 3" class="ctn">
+      <div class="desc">
+        <h2>{{ lang.SetDomail }}</h2>
+        <!-- <div style="margin-top: 10px;">{{ lang.domain_desc }}</div> -->
+      </div>
+      <div class="form" style="width: 400px;">
+        <el-form label-width="120px">
+
+          <el-form-item :label="lang.smtp_domain">
+            <el-input placeholder="domaim.com" v-model="domainSettings.smtp_domain">
+              <template #prepend>smtp.</template>
+            </el-input>
+          </el-form-item>
+
+          <el-form-item :label="lang.web_domain">
+            <el-input placeholder="pmail.domain.com" v-model="domainSettings.web_domain"></el-input>
+          </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]" :key="item"></el-input>
+          </el-form-item>
+
+
+        </el-form>
+      </div>
+    </div>
 
-                    <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>
+    <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" :key="info">
+        <h3>{{ domain }}</h3>
+        <el-table :data="info" border style="width: 100%">
+          <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 === '@' ">
+                  {{ 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="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 !== ''">
+                  {{ 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>
+      </div>
+    </div>
 
-                    <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>
+    <el-alert :closable="false" title="Warning!" type="error" center
+              v-if="active === 5 && sslSettings.type === 0 && port !== 80" :description="lang.autoSSLWarn"/>
+
+    <div v-if="active === 5" class="ctn">
+      <div class="desc">
+        <h2>{{ lang.setSSL }}</h2>
+        <div style="margin-top: 10px;">{{ lang.setSSL }}</div>
+      </div>
+      <div class="form" width="600px">
+        <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-select>
+          </el-form-item>
+
+          <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-select>
+
+            <el-tooltip class="box-item" effect="dark" :content="lang.challenge_typ_desc"
+                        placement="top-start">
+              <span style="margin-left: 6px; font-size:18px; font-weight: bolder;">?</span>
+            </el-tooltip>
+          </el-form-item>
+
+
+          <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-input placeholder="./config/ssl/public.crt" v-model="sslSettings.crt_path"></el-input>
+          </el-form-item>
+        </el-form>
+
+
+      </div>
 
+    </div>
 
+    <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-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 !== ''">
+                {{ scope.row.value }}
+              </el-tooltip>
+              <span v-else>{{ scope.row.value }}</span>
             </div>
+          </template>
 
-        </div>
-
-        <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-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 != ''">
-                                {{ 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>
+        </el-table-column>
+        <el-table-column prop="ttl" label="TTL" width="110px"/>
+      </el-table>
 
-        </div>
+    </div>
 
 
-        <el-button :element-loading-text="waitDesc" v-loading.fullscreen.lock="fullscreenLoading" id="next"
-            style="margin-top: 12px" @click="next">{{
-            lang.next }}</el-button>
+    <el-button :element-loading-text="waitDesc" v-loading.fullscreen.lock="fullscreenLoading" id="next"
+               style="margin-top: 12px" @click="next">{{
+        lang.next
+      }}
+    </el-button>
 
-    </div>
+  </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",
-    "password": "",
-    "password2": "",
-    "hadSeted": false
+  "account": "admin",
+  "password": "",
+  "password2": "",
+  "hadSeted": false
 })
 
 const dbSettings = reactive({
-    "type": "sqlite",
-    "dsn": "./config/pmail.db",
-    "lable": ""
+  "type": "sqlite",
+  "dsn": "./config/pmail.db",
+  "lable": ""
 })
 
 const domainSettings = reactive({
-    "web_domain": "",
-    "smtp_domain": "",
-    "multi_domain": []
+  "web_domain": "",
+  "smtp_domain": "",
+  "multi_domain": []
 })
 
 const sslSettings = reactive({
-    "type": "0",
-    "challenge": "http",
-    "key_path": "./config/ssl/private.key",
-    "crt_path": "./config/ssl/public.crt",
-    "paramsList": [],
+  "type": "0",
+  "challenge": "http",
+  "key_path": "./config/ssl/private.key",
+  "crt_path": "./config/ssl/public.crt",
+  "paramsList": [],
 })
 
 
@@ -277,236 +272,244 @@ const port = ref(80)
 
 
 const addDomain = () => {
-    domainSettings.multi_domain.push([])
+  domainSettings.multi_domain.push([])
 }
 
 const setPassword = () => {
-    if (adminSettings.hadSeted) {
+  if (adminSettings.hadSeted) {
+    active.value++;
+    getDomainConfig();
+    return;
+  }
+
+  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) {
+        ElMessage.error(res.errorMsg)
+      } else {
         active.value++;
         getDomainConfig();
-        return;
-    }
-
-    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) {
-                ElMessage.error(res.errorMsg)
-            } else {
-                active.value++;
-                getDomainConfig();
-            }
-        })
-    }
+      }
+    })
+  }
 }
 
 const getPassword = () => {
-    $http.post("/api/setup", { "action": "get", "step": "password" }).then((res) => {
-        if (res.errorNo != 0) {
-            ElMessage.error(res.errorMsg)
-        } else {
-            adminSettings.hadSeted = res.data != ""
-            if (adminSettings.hadSeted) {
-                adminSettings.account = res.data
-                adminSettings.password = "*******"
-                adminSettings.password2 = "*******"
-            }
-
-        }
-    })
+  http.post("/api/setup", {"action": "get", "step": "password"}).then((res) => {
+    if (res.errorNo !== 0) {
+      ElMessage.error(res.errorMsg)
+    } else {
+      adminSettings.hadSeted = res.data !== ""
+      if (adminSettings.hadSeted) {
+        adminSettings.account = res.data
+        adminSettings.password = "*******"
+        adminSettings.password2 = "*******"
+      }
+
+    }
+  })
 }
 
 
 const getDbConfig = () => {
-    $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;
-            dbSettings.dsn = res.data.db_dsn;
-        }
-    })
+  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;
+      dbSettings.dsn = res.data.db_dsn;
+    }
+  })
 }
 
 const getDomainConfig = () => {
-    $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;
-            domainSettings.smtp_domain = res.data.smtp_domain;
-            domainSettings.multi_domain = res.data.domains;
-        }
-    })
+  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;
+      domainSettings.smtp_domain = res.data.smtp_domain;
+      domainSettings.multi_domain = res.data.domains;
+    }
+  })
 }
 
 const setDbConfig = () => {
-    // 切换数据库类型为sqlite时,数据库路径为空,则使用默认路径
-    if (dbSettings.type === "sqlite" && !dbSettings.dsn) dbSettings.dsn = "./config/pmail.db";
-    else if (!dbSettings.dsn) ElMessage({
-        title: "Error",
-        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) {
-            ElMessage.error(res.errorMsg)
-        } else {
-            active.value++;
-            getPassword();
-        }
-    })
+  // 切换数据库类型为sqlite时,数据库路径为空,则使用默认路径
+  if (dbSettings.type === "sqlite" && !dbSettings.dsn) dbSettings.dsn = "./config/pmail.db";
+  else if (!dbSettings.dsn) ElMessage({
+    title: "Error",
+    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) {
+      ElMessage.error(res.errorMsg)
+    } else {
+      active.value++;
+      getPassword();
+    }
+  })
 }
 
 const getDNSConfig = () => {
-    $http.post("/api/setup", { "action": "get", "step": "dns" }).then((res) => {
-        if (res.errorNo != 0) {
-            ElMessage.error(res.errorMsg)
-        } else {
-            dnsInfos.value = res.data
-        }
-    })
+  http.post("/api/setup", {"action": "get", "step": "dns"}).then((res) => {
+    if (res.errorNo !== 0) {
+      ElMessage.error(res.errorMsg)
+    } else {
+      dnsInfos.value = res.data
+    }
+  })
 }
 
 
 const getSSLConfig = () => {
-    $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") {
-                sslSettings.type = "0"
-                sslSettings.challenge = "dns"
-            }
-
-
-            port.value = res.data.port
-        }
-    })
-}
+  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") {
+        sslSettings.type = "0"
+        sslSettings.challenge = "dns"
+      }
 
 
-const setSSLConfig = () => {
-    fullscreenLoading.value = true;
-
-    let sslType = sslSettings.type;
-    if (sslType == "0" && sslSettings.challenge == "dns") {
-        sslType = "2"
+      port.value = res.data.port
     }
+  })
+}
 
 
-
-    $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) {
-            fullscreenLoading.value = false;
-            ElMessage.error(res.errorMsg)
-        } else {
-            if (sslType == 2) {
-                fullscreenLoading.value = false;
-                dnsChecking.value = true;
-                getSSLDNSParams();
-            }
-            checkStatus();
-        }
-    })
+const setSSLConfig = () => {
+  fullscreenLoading.value = true;
+
+  let sslType = sslSettings.type;
+  if (sslType === "0" && sslSettings.challenge === "dns") {
+    sslType = "2"
+  }
+
+
+  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) {
+      fullscreenLoading.value = false;
+      ElMessage.error(res.errorMsg)
+    } else {
+      if (sslType === 2) {
+        fullscreenLoading.value = false;
+        dnsChecking.value = true;
+        getSSLDNSParams();
+      }
+      checkStatus();
+    }
+  })
 }
 
 
 const checkStatus = () => {
-    axios.post("/api/ping", {}).then((res) => {
-        if (res.data.errorNo != 0) {
-            setTimeout(function () {
-                checkStatus()
-            }, 1000);
-        } else {
-            if(sslSettings.type == 1){
-                window.location.href = "http://" + domainSettings.web_domain;
-            }else{
-                window.location.href = "https://" + domainSettings.web_domain;
-            }
-        }
-    }).catch((error) => {
-        setTimeout(function () {
-            checkStatus()
-        }, 1000);
-    })
+  axios.post("/api/ping", {}).then((res) => {
+    if (res.data.errorNo !== 0) {
+      setTimeout(function () {
+        checkStatus()
+      }, 1000);
+    } else {
+      if (sslSettings.type === 1) {
+        window.location.href = "http://" + domainSettings.web_domain;
+      } else {
+        window.location.href = "https://" + domainSettings.web_domain;
+      }
+    }
+  }).catch(() => {
+    setTimeout(function () {
+      checkStatus()
+    }, 1000);
+  })
 }
 
 
 const setDomainConfig = () => {
-    $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) {
-            ElMessage.error(res.errorMsg)
-        } else {
-            active.value++;
-            getDNSConfig();
-        }
-    })
+  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) {
+      ElMessage.error(res.errorMsg)
+    } else {
+      active.value++;
+      getDNSConfig();
+    }
+  })
 }
 
 const getSSLDNSParams = () => {
-    $http.post("/api/setup", { "action": "getParams", "step": "ssl" }).then((res) => {
-        if (res.errorNo != 0) {
-            ElMessage.error(res.errorMsg)
-        } else {
-            sslSettings.paramsList = res.data
-            console.log(sslSettings.paramsList)
-        }
-    })
-
-    if (sslSettings.paramsList.length == 0) {
-        setTimeout(function () {
-            getSSLDNSParams()
-        }, 1000);
+  http.post("/api/setup", {"action": "getParams", "step": "ssl"}).then((res) => {
+    if (res.errorNo !== 0) {
+      ElMessage.error(res.errorMsg)
+    } else {
+      sslSettings.paramsList = res.data
+      console.log(sslSettings.paramsList)
     }
+  })
 
+  if (sslSettings.paramsList.length === 0) {
+    setTimeout(function () {
+      getSSLDNSParams()
+    }, 1000);
+  }
 
 
 }
 
 
 const next = () => {
-    switch (active.value) {
-        case 0:
-            active.value++
-            getDbConfig();
-            break
-        case 1:
-            setDbConfig();
-            break;
-        case 2:
-            setPassword();
-            break;
-        case 3:
-            setDomainConfig();
-            break;
-        case 4:
-            getSSLConfig();
-            active.value++
-            break
-        case 5:
-            if (dnsChecking.value) {
-                fullscreenLoading.value = true;
-                waitDesc.value = lang.dns_challenge_wait;
-            } else {
-                setSSLConfig();
-            }
-            break
-    }
+  switch (active.value) {
+    case 0:
+      active.value++
+      getDbConfig();
+      break
+    case 1:
+      setDbConfig();
+      break;
+    case 2:
+      setPassword();
+      break;
+    case 3:
+      setDomainConfig();
+      break;
+    case 4:
+      getSSLConfig();
+      active.value++
+      break
+    case 5:
+      if (dnsChecking.value) {
+        fullscreenLoading.value = true;
+        waitDesc.value = lang.dns_challenge_wait;
+      } else {
+        setSSLConfig();
+      }
+      break
+  }
 
 }
 </script>
@@ -514,30 +517,32 @@ const next = () => {
 
 <style scoped>
 #main {
-    width: 100%;
-    height: 100%;
-    background-color: #f1f1f1;
-    display: flex;
-    flex-direction: column;
-    justify-content: space-between;
+  width: 100%;
+  height: 100%;
+  background-color: #f1f1f1;
+  display: flex;
+  flex-direction: column;
+  justify-content: space-between;
 }
 
 .desc {
-    padding-right: 20px;
+  padding-right: 20px;
 }
 
-#status {}
+#status {
+}
 
 .ctn {
-    display: flex;
-    justify-content: center;
+  display: flex;
+  justify-content: center;
 }
 
 .ctn_s {
-    display: flex;
-    flex-direction: column;
+  display: flex;
+  flex-direction: column;
 
 }
 
-#next {}
+#next {
+}
 </style>