From 2af71bcf522c485ea005184c977986374a7dcc4a Mon Sep 17 00:00:00 2001
From: Sanakey <714737083@qq.com>
Date: 星期六, 28 九月 2024 09:47:10 +0800
Subject: [PATCH] Merge branch 'feng-v1-editor' of http://192.168.100.20:9090/r/onbus_crm into li-v1

---
 src/utils/dateUtil.ts                                              |   10 
 src/views/email/UnreadEmail/index.vue                              |   59 
 src/views/email/Utils/index.vue                                    |   31 
 src/views/email/HandlingEmailsOnBehalfOfOthers/index.vue           |   60 
 src/components/CButton/index.vue                                   |  249 +
 src/router/routes/index.ts                                         |    6 
 src/views/email/Utils/blacklist.vue                                |  242 +
 src/components/Tinymce/src/index.vue                               |  540 ++++
 src/views/email/Drafts/index.vue                                   |   58 
 src/views/email/Utils/quickText.vue                                |  202 +
 src/views/email/Utils/convention.vue                               |  152 +
 src/views/email/components/ListPage/TooltipAndDropdown .vue        |  304 ++
 uno.config.ts                                                      |    1 
 src/router/routes/modules/email.ts                                 |  122 
 src/components/ColorPicker/index.vue                               |  160 +
 src/views/email/components/emailDetail.vue                         |   11 
 src/views/email/components/ListPage/pageHeadLeft.vue               |  166 +
 src/views/email/Edit/index.vue                                     |  577 ++++
 src/layouts/default/email.vue                                      |   91 
 src/views/email/UnreadEmail/user.vue                               |   18 
 src/layouts/page/email.vue                                         |   86 
 src/store/modules/useCollapseStore.ts                              |   19 
 src/views/email/preview/index..vue                                 |   86 
 src/store/modules/emailRouter.ts                                   |   30 
 src/views/email/Utils/folder.vue                                   |  200 +
 types/axios.d.ts                                                   |    4 
 src/views/email/index.vue                                          |  196 +
 src/components/Tinymce/src/Editor.vue                              |   91 
 src/views/email/outbox/index.vue                                   |   61 
 src/components/Tinymce/index.ts                                    |    4 
 src/views/email/components/ListPage/table.vue                      |  476 +++
 src/utils/tool/inedx.ts                                            |    5 
 src/views/email/components/ListPage/drawerDetail.vue               |  420 +++
 src/router/constant.ts                                             |    1 
 src/utils/http/axios/index.ts                                      |   11 
 src/views/email/HandlingEmailsOnBehalfOfOthers/components/list.vue |  220 +
 src/views/email/Utils/label.vue                                    |  260 ++
 src/views/email/components/ListPage/list.vue                       |  234 +
 src/layouts/default/content/email.vue                              |   57 
 src/main.ts                                                        |    1 
 src/api/email/model/userListModel.ts                               |   34 
 /dev/null                                                          |  261 --
 src/views/email/Utils/mailboxManagement.vue                        |  689 +++++
 src/api/email/userList.ts                                          |  278 ++
 src/views/email/components/LeftNav.vue                             |  268 +
 src/router/routes/modules/preview.ts                               |   31 
 src/views/email/Inbox/index.vue                                    |   61 
 src/views/email/components/SelectUser/index.vue                    |  367 ++
 src/App.vue                                                        |   19 
 49 files changed, 7,045 insertions(+), 484 deletions(-)

diff --git a/src/App.vue b/src/App.vue
index 56b077d..401b213 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -17,20 +17,19 @@
   import { computed, ref } from 'vue';
 
   import { getCurrentInstance } from 'vue';
-console.log(getCurrentInstance,'=====-');
 
   // 鑾峰彇褰撳墠瀹炰緥
-const instance = getCurrentInstance();
-const proxy = instance?.proxy;
+  const instance = getCurrentInstance();
+  const proxy = instance?.proxy;
 
   const setCookie = () => {
-// 鍏堟鏌roxy鏄惁瀛樺湪锛屽啀杩涜鎿嶄綔
-if (proxy && proxy.$cookies) {
-  proxy.$cookies.remove('JSESSIONID');
-    proxy.$cookies.set('JSESSIONID', 'C1371DD46D2B480CC815F329B2833C99.jvm_59_9010', '1d');
-} else {
-    console.error('proxy瀵硅薄鏈垵濮嬪寲鎴栦笉鍖呭惈$cookies灞炴��');
-}
+    // 鍏堟鏌roxy鏄惁瀛樺湪锛屽啀杩涜鎿嶄綔
+    if (proxy && proxy.$cookies) {
+      proxy.$cookies.remove('JSESSIONID');
+      proxy.$cookies.set('JSESSIONID', '23542F948D2C450599CF5850631B432D.jvm_59_9010', '1d');
+    } else {
+      console.error('proxy瀵硅薄鏈垵濮嬪寲鎴栦笉鍖呭惈$cookies灞炴��');
+    }
   };
   setCookie();
   // support Multi-language
diff --git a/src/api/email/model/userListModel.ts b/src/api/email/model/userListModel.ts
index ac9cd19..8c64bf9 100644
--- a/src/api/email/model/userListModel.ts
+++ b/src/api/email/model/userListModel.ts
@@ -1,11 +1,7 @@
-
-
-import { BasicPageParams,BasicFetchResult } from '@/api/model/baseModel';
+import { BasicPageParams, BasicFetchResult } from '@/api/model/baseModel';
 export type tableParams = Partial<BasicPageParams>;
 
-
-export interface GetUserListItem
-{
+export interface GetUserListItem {
   id: string;
   name: string;
   email: string;
@@ -15,15 +11,29 @@
 }
 export interface sendingMailModel {
   sender: string;
-  receiver: string;
-  cc: string;
-  bcc: string;
+  receiver: Object;
+  cc: Object;
+  bcc: Object;
   subject: string;
   content: string;
-  fileUNID: string;
+  attachmentList: string;
   docCode: string;
-  
 }
 
+export interface addAccountParams {
+  email: string;
+  password: string;
+  aliasEmail: string;
+  biSyncFlag: boolean;
+  proxyFlag: boolean;
+  receiveProtocol: string;
+  receiveSSL: boolean;
+  receivePort: string;
+  receiveHost: string;
+  smtpSSL: boolean;
+  smtpPort: string;
+  smtpHost: string;
+  invalid: string;
+}
 
-export type GetUserListModel = BasicFetchResult<GetUserListItem>;
\ No newline at end of file
+export type GetUserListModel = BasicFetchResult<GetUserListItem>;
diff --git a/src/api/email/userList.ts b/src/api/email/userList.ts
index 98cacfe..675a1ee 100644
--- a/src/api/email/userList.ts
+++ b/src/api/email/userList.ts
@@ -1,25 +1,281 @@
-
 import { defHttp } from '@/utils/http/axios';
 import {
-  GetUserListModel,tableParams,sendingMailModel
+  GetUserListModel,
+  tableParams,
+  sendingMailModel,
+  addAccountParams,
 } from './model/userListModel';
 
 enum Api {
   GET_USER_LIST = '/crm/email/getUserList',
   SENDING_MAIL_DO = '/crm/mail/sendingMail.do',
   UPDATE_READ = '/crm/mail/updateRead.do',
-  RECEIVE = '/crm/mail/receive.do'
-
+  RECEIVE = '/crm/mail/receive.do',
+  SAVE_MAIL_DRAFTS = '/crm/mail/saveMailDrafts.do',
+  ADD_ACCOUNT = '/crm/mail/account/addAccount.do',
+  GET_ACCOUNT = '/crm/mail/account/getAccount.do',
+  UPDATE_ACCOUNT = '/crm/mail/account/updateAccount.do',
+  DELETE_ACCOUNT = '/crm/mail/account/deleteAccount.do',
+  GET_ACCOUNT_LIST = '/crm/mail/account/getAccountList.do',
+  IS_EMAIL_VALID = '/crm/mail/account/isEmailValid.do',
+  GET_EMAIL_MODULE = '/crm/mail/getEmailModule.do',
+  ADD_SIGNATURE = '/crm/mail/signature/addSignature.do',
+  UPDATE_SIGNATURE = '/crm/mail/signature/updateSignature.do',
+  GET_SIGNATURE = '/crm/mail/signature/getSignature.do',
+  ADD_QUICK_TEXT = '/crm/mail/quickText/addQuickText.do',
+  UPDATE_QUICK_TEXT = '/crm/mail/quickText/updateQuickText.do',
+  GET_QUICK_TEXT = '/crm/mail/quickText/getQuickText.do',
+  DELETE_QUICK_TEXT = '/crm/mail/quickText/deleteQuickText.do',
+  GET_MAIL_LIST = '/crm/mail/getMailList.do',
+  GET_USER_INFO = '/crm/base/userInfo.do',
+  GET_MAIL_INFO = '/crm/mail/getMailInfo.do',
+  EMAIL_LIST = '/crm/base/emailList.do',
+  UPDATE_HANDLE = '/crm/mail/updateHandle.do',
+  DELETE_EMAIL = '/crm/mail/deleteEmail.do',
+  SET_QUICK_REPLY = '/crm/mail/setQuickReply.do',
+  CONTACT_LIST = '/crm/clues/contactList.do',
+  GET_HANDLE_MAIL_LIST = '/crm/mail/getHandleMailList.do',
+  ADD_FOLDER = '/crm/mail/folder/addFolder.do',
+  UPDATE_FOLDER = '/crm/mail/folder/updateFolder.do',
+  DELETE_FOLDER = '/crm/mail/folder/deleteFolder.do',
+  GET_FOLDER = '/crm/mail/folder/getFolder.do',
+  GET_ROW_ID = '/getRowid.do',
+  ADD_TAG = '/crm/mail/blacklist/getBlackList.do',
+  UPDATE_TAG = '/crm/mail/tag/updateTag.do',
+  DELETE_TAG = '/crm/mail/tag/deleteTag.do',
+  GET_TAG = '/crm/mail/tag/getTagList.do',
+  ADD_BLACKLIST = '/crm/mail/blacklist/addBlackList.do',
+  UPDATE_BLACKLIST = '/crm/mail/blacklist/updateBlackList.do',
+  DELETE_BLACKLIST = '/crm/mail/blacklist/deleteBlackList.do',
+  GET_BLACKLIST = '/crm/mail/blacklist/getBlackList.do',
 }
+// 鑾峰彇閭欢璺敱鍒楄〃
+export const getEmailModuleApi = () => defHttp.get({ url: Api.GET_EMAIL_MODULE });
 
-export const receiveApi = (params) =>
-  defHttp.get({ url: Api.RECEIVE,params});
-// 鍙戦�侀偖浠�
+export const receiveApi = (params) => defHttp.get({ url: Api.RECEIVE, params });
 //鑾峰彇鑱旂郴浜哄垪琛�
 export const getUserListApi = (params: tableParams) =>
-  defHttp.post<GetUserListModel[]>({ url: Api.GET_USER_LIST, params});
+  defHttp.post<GetUserListModel[]>({ url: Api.GET_USER_LIST, params });
 // 鍙戦�侀偖浠�
 export const sendingMailApi = (params) =>
-  defHttp.post<sendingMailModel[]>({ url: Api.SENDING_MAIL_DO, params});
-export const updateReadApi = (params) =>
-  defHttp.post<{}>({ url: Api.UPDATE_READ , params});
\ No newline at end of file
+  defHttp.post<sendingMailModel[]>({ url: Api.SENDING_MAIL_DO, params });
+export const updateReadApi = (params) => defHttp.post<{}>({ url: Api.UPDATE_READ, params });
+// 鑽夌绠�
+export const saveMailDraftsApi = (params) =>
+  defHttp.post<{}>({ url: Api.SAVE_MAIL_DRAFTS, params });
+
+// 娣诲姞閭閰嶇疆
+export const addAccountApi = (params: addAccountParams) =>
+  defHttp.post<{}>({ url: Api.ADD_ACCOUNT, params });
+// 鑾峰彇閭閰嶇疆
+export const getAccountApi = (params) => defHttp.get<{}>({ url: Api.GET_ACCOUNT, params });
+// 淇敼閭閰嶇疆
+export const updateAccountApi = (params) => defHttp.post<{}>({ url: Api.UPDATE_ACCOUNT, params });
+// 鍒犻櫎閭閰嶇疆
+export const deleteAccountApi = (params) => defHttp.post<{}>({ url: Api.DELETE_ACCOUNT, params });
+
+// 鑾峰彇閭鍒楄〃
+export const getAccountListApi = () => defHttp.get<{}>({ url: Api.GET_ACCOUNT_LIST });
+// 妫�娴嬮偖绠�
+export const isEmailValidApi = (params) =>
+  defHttp.get({
+    url: Api.IS_EMAIL_VALID,
+    params,
+  });
+
+// 娣诲姞绛惧悕
+export const addSignatureApi = (params) =>
+  defHttp.post({
+    url: Api.ADD_SIGNATURE,
+    params,
+  });
+// 淇敼绛惧悕
+export const updateSignatureApi = (params) =>
+  defHttp.post({
+    url: Api.UPDATE_SIGNATURE,
+    params,
+  });
+
+// 鏌ヨ绛惧悕
+export const getSignatureApi = (params) =>
+  defHttp.get({
+    url: Api.GET_SIGNATURE,
+    params,
+  });
+
+// 鏌ヨ蹇�熸枃鏈�
+export const getQuickTextApi = (params) =>
+  defHttp.get({
+    url: Api.GET_QUICK_TEXT,
+    params,
+  });
+// 鏂板蹇�熸枃鏈�
+export const addQuickTextApi = (params) =>
+  defHttp.post({
+    url: Api.ADD_QUICK_TEXT,
+    params,
+  });
+// 鏇存柊蹇�熸枃鏈�
+export const updateQuickTextApi = (params) =>
+  defHttp.post({
+    url: Api.UPDATE_QUICK_TEXT,
+    params,
+  });
+// 鍒犻櫎蹇�熸枃鏈�
+export const deleteQuickTextApi = (params) =>
+  defHttp.post({
+    url: Api.DELETE_QUICK_TEXT,
+    params,
+  });
+
+// 鑾峰彇閭欢鍒楄〃
+export const getMailListApi = (params) =>
+  defHttp.get({
+    url: Api.GET_MAIL_LIST,
+    params,
+  });
+
+// 鑾峰彇浼佷笟鍚屼簨鍒楄〃
+export const getUserInfoApi = (params) =>
+  defHttp.post({
+    url: Api.GET_USER_INFO,
+    params,
+  });
+
+// 鑾峰彇閭欢璇︽儏
+export const getMailInfoApi = (params) =>
+  defHttp.get({
+    url: Api.GET_MAIL_INFO,
+    params,
+  });
+
+// 鑾峰彇妯$硦鎼滅储浜哄憳
+export const emailListAPi = (params) =>
+  defHttp.post({
+    url: Api.EMAIL_LIST,
+    params,
+  });
+// 璁剧疆瀹屾垚鏃堕棿
+export const updateHandleAPi = (params) =>
+  defHttp.get({
+    url: Api.UPDATE_HANDLE,
+    params,
+  });
+// 鍒犻櫎閭欢
+export const deleteEmailAPi = (params) =>
+  defHttp.post({
+    url: Api.DELETE_EMAIL,
+    params,
+  });
+
+// 蹇�熷洖澶�
+export const setQuickReplyAPi = (params) =>
+  defHttp.post({
+    url: Api.SET_QUICK_REPLY,
+    params,
+  });
+
+// 鑾峰彇绾跨储鑱旂郴浜哄垪琛�
+export const contactListAPi = (params) =>
+  defHttp.post({
+    url: Api.CONTACT_LIST,
+    params,
+  });
+
+// 鑾峰彇绾跨储鑱旂郴浜哄垪琛�
+export const getHandleMailListApi = (params) =>
+  defHttp.get({
+    url: Api.GET_HANDLE_MAIL_LIST,
+    params,
+  });
+
+// 鏂板鏂囦欢澶�
+export const addFolderApi = (params) =>
+  defHttp.post({
+    url: Api.ADD_FOLDER,
+    params,
+  });
+
+// 淇敼鏂囦欢澶�
+export const updateFolderApi = (params) =>
+  defHttp.post({
+    url: Api.UPDATE_FOLDER,
+    params,
+  });
+
+// 鍒犻櫎鏂囦欢澶�
+export const deleteFolderApi = (params) =>
+  defHttp.post({
+    url: Api.DELETE_FOLDER,
+    params,
+  });
+
+// 鏌ヨ鏂囦欢澶�
+export const getFolderApi = (params) =>
+  defHttp.get({
+    url: Api.GET_FOLDER,
+    params,
+  });
+
+// 鑾峰彇rowId
+export const getRowIdApi = () =>
+  defHttp.get({
+    url: Api.GET_ROW_ID,
+  });
+
+// 鏂板鏍囩
+export const addTagApi = (params) =>
+  defHttp.post({
+    url: Api.ADD_TAG,
+    params,
+  });
+
+// 淇敼鏍囩
+export const updateTagApi = (params) =>
+  defHttp.post({
+    url: Api.UPDATE_TAG,
+    params,
+  });
+
+// 鍒犻櫎鏍囩
+export const deleteTagApi = (params) =>
+  defHttp.post({
+    url: Api.DELETE_TAG,
+    params,
+  });
+
+// 鏌ヨ鏍囩
+export const getTagApi = (params) =>
+  defHttp.get({
+    url: Api.GET_TAG,
+    params,
+  });
+
+
+// 鏂板榛戝悕鍗�
+export const addBlackListApi = (params) =>
+  defHttp.post({
+    url: Api.ADD_BLACKLIST,
+    params,
+  });
+
+// 淇敼榛戝悕鍗�
+export const updateBlackListApi = (params) =>
+  defHttp.post({
+    url: Api.UPDATE_BLACKLIST,
+    params,
+  });
+
+// 鍒犻櫎榛戝悕鍗�
+export const deleteBlackListApi = (params) =>
+  defHttp.post({
+    url: Api.DELETE_BLACKLIST,
+    params,
+  });
+
+// 鏌ヨ榛戝悕鍗�
+export const getBlackListApi = (params) =>
+  defHttp.get({
+    url: Api.GET_BLACKLIST,
+    params,
+  });
diff --git a/src/components/CButton/index.vue b/src/components/CButton/index.vue
new file mode 100644
index 0000000..9c21a14
--- /dev/null
+++ b/src/components/CButton/index.vue
@@ -0,0 +1,249 @@
+<!--鎸夐挳棰滆壊缁勪欢--->
+<template>
+  <a-button :type="customType" :class="customClass" :size="customSize" :disabled="disabled">
+    <template v-if="iconType" #icon>
+      <component :is="iconType" />
+    </template>
+    <slot />
+  </a-button>
+</template>
+<script>
+  import { defineComponent, ref, watch } from 'vue';
+  export default defineComponent({
+    name: 'CButtonIndex',
+    props: {
+      type: { type: String, default: '' },
+      size: { type: String, default: '' },
+      icon: { type: String, default: '' },
+      disabled: { type: Boolean, default: false },
+      permission: { type: [String, Boolean], default: true },
+    },
+    setup(props) {
+      const customClass = ref('c-button-primary');
+      const customType = ref('');
+      const customSize = ref('middle');
+      const iconType = ref('');
+      watch(
+        () => props.type,
+        (v) => {
+          switch (v) {
+            case 'warning':
+              customClass.value = 'c-button-warning';
+              customType.value = 'default';
+              break;
+            case 'error':
+              customClass.value = 'c-button-error';
+              customType.value = 'default';
+              break;
+            case 'success':
+              customClass.value = 'c-button-success';
+              customType.value = 'default';
+              break;
+            case 'primary':
+              customClass.value = 'c-button-primary';
+              customType.value = 'primary';
+              break;
+            case 'cyan':
+              customClass.value = 'c-button-cyan';
+              customType.value = 'default';
+              break;
+            case 'black':
+              customClass.value = 'c-button-black';
+              customType.value = 'default';
+              break;
+            case 'purple':
+              customClass.value = 'c-button-purple';
+              customType.value = 'default';
+              break;
+            case 'text':
+              customClass.value = '';
+              customType.value = 'text';
+              break;
+            case 'link':
+              customClass.value = '';
+              customType.value = 'link';
+              break;
+            default:
+              customClass.value = '';
+              customType.value = 'default';
+              break;
+          }
+        },
+        { immediate: true },
+      );
+      watch(
+        () => props.size,
+        (v) => {
+          customSize.value = !v ? 'middle' : v;
+        },
+        { immediate: true },
+      );
+      watch(
+        () => props.icon,
+        (v) => {
+          iconType.value = v;
+        },
+        { immediate: true },
+      );
+      watch(
+        () => props,
+        () => {},
+        { immediate: true },
+      );
+      return {
+        customClass,
+        customType,
+        customSize,
+        iconType,
+      };
+    },
+  });
+</script>
+<style scoped>
+  .c-button-primary {
+    color: #fff;
+    background-color: #2db7f5;
+    border-color: #2db7f5;
+  }
+  .c-button-primary:hover {
+    color: #fff;
+    background-color: #3dc1fc;
+    border-color: #2db7f5;
+  }
+  .c-button-primary[disabled],
+  .c-button-primary[disabled]:hover,
+  .c-button-primary[disabled]:focus,
+  .c-button-primary[disabled]:active {
+    color: rgba(0, 0, 0, 0.25);
+    background: #f5f5f5;
+    border-color: #d9d9d9;
+    text-shadow: none;
+    box-shadow: none;
+  }
+
+  .c-button-warning {
+    color: #fff;
+    background-color: #ff9900;
+    border-color: #ff9900;
+  }
+  .c-button-warning:hover {
+    color: #fff;
+    background-color: #fcac35;
+    border-color: #ff9900;
+  }
+  .c-button-warning[disabled],
+  .c-button-warning[disabled]:hover,
+  .c-button-warning[disabled]:focus,
+  .c-button-warning[disabled]:active {
+    color: rgba(0, 0, 0, 0.25);
+    background: #f5f5f5;
+    border-color: #d9d9d9;
+    text-shadow: none;
+    box-shadow: none;
+  }
+
+  .c-button-error {
+    color: #fff;
+    background-color: #ff3300;
+    border-color: #ff3300;
+  }
+  .c-button-error:hover {
+    color: #fff;
+    background-color: #fc653f;
+    border-color: #ff3300;
+  }
+  .c-button-error[disabled],
+  .c-button-error[disabled]:hover,
+  .c-button-error[disabled]:focus,
+  .c-button-error[disabled]:active {
+    color: rgba(0, 0, 0, 0.25);
+    background: #f5f5f5;
+    border-color: #d9d9d9;
+    text-shadow: none;
+    box-shadow: none;
+  }
+
+  .c-button-success {
+    color: #fff;
+    background-color: #00cc66;
+    border-color: #00cc66;
+  }
+  .c-button-success:hover {
+    color: #fff;
+    background-color: #03e071;
+    border-color: #00cc66;
+  }
+  .c-button-success[disabled],
+  .c-button-success[disabled]:hover,
+  .c-button-success[disabled]:focus,
+  .c-button-success[disabled]:active {
+    color: rgba(0, 0, 0, 0.25);
+    background: #f5f5f5;
+    border-color: #d9d9d9;
+    text-shadow: none;
+    box-shadow: none;
+  }
+
+  .c-button-cyan {
+    color: #fff;
+    background-color: #04c1e1;
+    border-color: #04c1e1;
+  }
+  .c-button-cyan:hover {
+    color: #fff;
+    background-color: #0ad5f8;
+    border-color: #04c1e1;
+  }
+  .c-button-cyan[disabled],
+  .c-button-cyan[disabled]:hover,
+  .c-button-cyan[disabled]:focus,
+  .c-button-cyan[disabled]:active {
+    color: rgba(0, 0, 0, 0.25);
+    background: #f5f5f5;
+    border-color: #d9d9d9;
+    text-shadow: none;
+    box-shadow: none;
+  }
+
+  .c-button-black {
+    color: #fff;
+    background-color: #131313;
+    border-color: #131313;
+  }
+  .c-button-black:hover {
+    color: #fff;
+    background-color: #313131;
+    border-color: #131313;
+  }
+  .c-button-black[disabled],
+  .c-button-black[disabled]:hover,
+  .c-button-black[disabled]:focus,
+  .c-button-black[disabled]:active {
+    color: rgba(0, 0, 0, 0.25);
+    background: #f5f5f5;
+    border-color: #d9d9d9;
+    text-shadow: none;
+    box-shadow: none;
+  }
+
+  .c-button-purple {
+    color: #fff;
+    background-color: #b500fe;
+    border-color: #b500fe;
+  }
+  .c-button-purple:hover {
+    color: #fff;
+    background-color: #c951fa;
+    border-color: #b500fe;
+  }
+  .c-button-purple[disabled],
+  .c-button-purple[disabled]:hover,
+  .c-button-purple[disabled]:focus,
+  .c-button-purple[disabled]:active {
+    color: rgba(0, 0, 0, 0.25);
+    background: #f5f5f5;
+    border-color: #d9d9d9;
+    text-shadow: none;
+    box-shadow: none;
+  }
+</style>
diff --git a/src/components/ColorPicker/index.vue b/src/components/ColorPicker/index.vue
new file mode 100644
index 0000000..2bffb93
--- /dev/null
+++ b/src/components/ColorPicker/index.vue
@@ -0,0 +1,160 @@
+<template>
+  <div class="my-radio-group" v-if="type == 1">
+    <a-radio-group v-model:value="localValue" button-style="solid" size="small" name="color">
+      <a-radio-button
+        v-for="item in colors"
+        :key="item.type_id"
+        class="mr-5px"
+        :value="item.color"
+        :style="{ backgroundColor: item.color, borderColor: item.color }"
+      >
+        <span :class="localValue === item.color ? 'c-white' : 'c-white select-none op0'">鉁�</span>
+      </a-radio-button>
+    </a-radio-group>
+  </div>
+  <div v-else>
+    <a-dropdown :trigger="['click']">
+      <div @click.prevent>
+        <div style="display: flex; align-items: center">
+          <div class="bookmark" :style="{ backgroundColor: localValue }"></div>
+          <DownOutlined />
+        </div>
+      </div>
+      <template #overlay>
+        <a-menu class="my-radio-group" style="width: 154px">
+          <a-menu-item key="0">
+            <a-radio-group
+              v-model:value="localValue"
+              button-style="solid"
+              size="small"
+              name="color"
+            >
+              <a-radio-button
+                v-for="item in colors"
+                :key="item.type_id"
+                class="mr-5px"
+                :value="item.color"
+                :style="{ backgroundColor: item.color, borderColor: item.color }"
+              >
+                <span :class="localValue === item.color ? 'c-white' : 'c-white select-none op0'"
+                  >鉁�</span
+                >
+              </a-radio-button>
+            </a-radio-group>
+          </a-menu-item>
+        </a-menu>
+      </template>
+    </a-dropdown>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  import { ref, watch, defineProps, defineEmits } from 'vue';
+  import { DownOutlined } from '@ant-design/icons-vue';
+  // 瀹氫箟 props锛屾帴鏀剁埗缁勪欢浼犻�掔殑 modelValue
+  const props = defineProps({
+    modelValue: {
+      type: String,
+      default:'#000000',
+      required: true,
+    },
+    // 1鏅�氾紝2閫夋嫨鍣ㄦā寮�
+    type: {
+      type: Number,
+      default: 0,
+      required: true,
+    },
+  });
+
+  // 瀹氫箟 emits锛岀敤浜庡弻鍚戠粦瀹�
+  const emit = defineEmits(['update:modelValue','change']);
+
+  // 棰滆壊閫夐」
+  const colors = ref([
+    { type_id: 1, color: '#000000', name: '榛�' },
+    { type_id: 2, color: '#bc5959', name: '鏆楃孩' },
+    { type_id: 3, color: '#d87538', name: '姗欑孩' },
+    { type_id: 4, color: '#209890', name: '闈掔豢' }, // 琚�変腑鐨勯鑹�
+    { type_id: 5, color: '#4b679d', name: '娣辫摑' },
+    { type_id: 6, color: '#595dbf', name: '钃濈传' },
+    { type_id: 7, color: '#333333', name: '娣辩伆' },
+    { type_id: 8, color: '#e43e3e', name: '绾�' },
+    { type_id: 9, color: '#eb9955', name: '娴呮' },
+    { type_id: 10, color: '#61bc81', name: '娴呯豢' },
+    { type_id: 11, color: '#5d89e9', name: '娴呰摑' },
+    { type_id: 12, color: '#8d54bd', name: '绱�' },
+    { type_id: 13, color: '#7b8291', name: '钃濈伆' },
+    { type_id: 14, color: '#ee7b7b', name: '绮夌孩' },
+    { type_id: 15, color: '#e2ad28', name: '閲戦粍' },
+    { type_id: 16, color: '#80c463', name: '鑽夌豢' },
+    { type_id: 17, color: '#4aa8eb', name: '澶╄摑' },
+    { type_id: 18, color: '#acacac', name: '娴呯伆' },
+  ]);
+
+  // 瀹氫箟鏈湴鐘舵�侊紝浠ヤ究鐩戝惉鍜屾洿鏂�
+  const localValue = ref(props.modelValue);
+
+  // 鐩戝惉 modelValue 鐨勫彉鍖栵紝骞跺悓姝ユ洿鏂版湰鍦板��
+  watch(
+    () => props.modelValue,
+    (newValue) => {
+      localValue.value = newValue;
+    },
+  );
+
+  // 褰撴湰鍦板�煎彂鐢熷彉鍖栨椂锛屽彂鍑� update:modelValue 浜嬩欢锛岄�氱煡鐖剁粍浠舵洿鏂�
+  watch(localValue, (newValue) => {
+    emit('update:modelValue', newValue);
+    emit('change', newValue);
+  });
+</script>
+
+<style lang="less" scoped>
+  .mr-5px {
+    margin-right: 5px;
+  }
+
+  .c-white {
+    color: white;
+  }
+
+  .select-none {
+    user-select: none;
+  }
+
+  .op0 {
+    opacity: 0;
+  }
+
+  .my-radio-group {
+    :deep(.ant-radio-group) .ant-radio-button-wrapper::before {
+      width: 0;
+    }
+  }
+
+  .mr-5px {
+    margin-top: 4px;
+    border-radius: 4px;
+  }
+
+  .bookmark {
+    display: inline-block;
+    position: relative;
+    width: 14px;
+    height: 18px;
+    margin-right: 5px;
+    border-radius: 2px 2px 0 0; /* Rounded top corners */
+  }
+
+  .bookmark::after {
+    content: '';
+    position: absolute;
+    right: 0;
+    bottom: 0;
+    left: 0;
+    height: 8px;
+    background-color: #fff;
+    clip-path: polygon(50% 60%, 0% 100%, 100% 100%); /* Triangle at the bottom */
+ 
+  }
+</style>
diff --git a/src/components/Tinymce/index.ts b/src/components/Tinymce/index.ts
index dca44f1..57ce47a 100644
--- a/src/components/Tinymce/index.ts
+++ b/src/components/Tinymce/index.ts
@@ -1,4 +1,8 @@
 import { withInstall } from '@/utils';
 import tinymce from './src/Editor.vue';
+import tinymcePw from './src/index.vue';
+
 
 export const Tinymce = withInstall(tinymce);
+export const TinymcePw = withInstall(tinymcePw);
+
diff --git a/src/components/Tinymce/src/Editor.vue b/src/components/Tinymce/src/Editor.vue
index 0090041..53ed7c9 100644
--- a/src/components/Tinymce/src/Editor.vue
+++ b/src/components/Tinymce/src/Editor.vue
@@ -9,6 +9,7 @@
     <slot v-else></slot>
     <div class="p-2 tox-statusbar">
       <a-upload
+        v-if="isElse"
         v-model:file-list="fileListTemp"
         action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
         list-type="picture"
@@ -21,38 +22,40 @@
         <template #iconRender><PaperClipOutlined /></template>
         <template #itemRender="{ file, fileList, actions }">
           <a-space class="ant-upload-list-picture-card">
-            <span style="display: flex">
+            <span style="display: flex; flex-wrap: wrap">
               <PaperClipOutlined style="margin-right: 4px" />
-              <span v-if='!file.editor' :style="file.status === 'error' ? 'color: red' : ''">{{ file.name }}</span>
-              <span v-else >
+              <span v-if="!file.editor" :style="file.status === 'error' ? 'color: red' : ''">{{
+                file.name
+              }}</span>
+              <span v-else>
                 <a-input size="small" v-model:value="file.tempName"></a-input>
               </span>
             </span>
-            <span v-if='!file.editor'>
+            <span v-if="!file.editor">
               <a href="javascript:;" @click="actions.preview">棰勮</a>
               <a href="javascript:;" @click="fnRename(file, fileList)">閲嶅懡鍚�</a>
               <a href="javascript:;" @click="actions.remove">鍒犻櫎</a>
             </span>
-            <span v-else >
+            <span v-else>
               <a href="javascript:;" @click="fnSaveRename(file, fileList)">淇濆瓨</a>
               <a href="javascript:;" @click="fnOffRename(file, fileList)">鍙栨秷</a>
             </span>
           </a-space>
         </template>
       </a-upload>
-      <div style="position: absolute; left: 78px">
+      <div :class="fileListTemp.length > 0 ? 'my-upload-list' : ''">
         <div style="display: flex">
           <ImgUpload
             :fullscreen="fullscreen"
             @uploading="handleImageUploading"
             @done="handleDone"
-            v-if="showImageUpload"
+            v-if="isImg"
             v-show="editorRef"
             :title="'鍥剧墖'"
             :disabled="disabled"
             :accept="'.jpg,.jpeg,.gif,.png,.webp'"
           />
-          <a-button type="text" size="small">
+          <a-button v-if="isText" type="text" size="small">
             <SnippetsOutlined />
             蹇�熸枃鏈�</a-button
           >
@@ -197,6 +200,18 @@
     },
     fontsize: {
       type: String,
+    },
+    isElse: {
+      type: Boolean,
+      default: true,
+    },
+    isText: {
+      type: Boolean,
+      default: true,
+    },
+    isImg: {
+      type: Boolean,
+      default: true,
     },
   });
 
@@ -382,8 +397,8 @@
       emit('update:modelValue', content);
       const data = {
         content,
-        fileUNID:fileListTemp.value
-      }
+        fileUNID: fileListTemp.value,
+      };
       emit('change', data);
     });
 
@@ -418,24 +433,24 @@
 
   // 闄勪欢
   const fileListTemp = ref<UploadProps['fileList']>([
-    {
-      uid: '-1',
-      name: 'xxx.png',
-      tempName:'xxx',
-      status: 'done',
-      url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
-      thumbUrl: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
-      editor:false
-    },
-    {
-      uid: '-2',
-      name: 'yyy.png',
-      tempName:'yyy',
-      status: 'done',
-      url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
-      thumbUrl: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
-      editor:false
-    },
+    // {
+    //   uid: '-1',
+    //   name: 'xxx.png',
+    //   tempName: 'xxx',
+    //   status: 'done',
+    //   url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
+    //   thumbUrl: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
+    //   editor: false,
+    // },
+    // {
+    //   uid: '-2',
+    //   name: 'yyy.png',
+    //   tempName: 'yyy',
+    //   status: 'done',
+    //   url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
+    //   thumbUrl: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
+    //   editor: false,
+    // },
   ]);
   function fnRename(file, fileList) {
     console.log(file, fileList);
@@ -446,26 +461,25 @@
       } else {
         item.editor = false;
       }
-      return item
+      return item;
     });
   }
   function fnSaveRename(file, fileList) {
     fileListTemp.value = fileList.map((item) => {
       if (file.uid == item.uid) {
-         item.name = item.tempName;
+        item.name = item.tempName;
         item.editor = false;
       }
-      return item
+      return item;
     });
   }
-  function fnOffRename(file, fileList){
+  function fnOffRename(file, fileList) {
     fileListTemp.value = fileList.map((item) => {
       if (file.uid == item.uid) {
         item.editor = false;
       }
-      return item
+      return item;
     });
-
   }
 </script>
 <style lang="less" scope>
@@ -481,9 +495,17 @@
     }
   }
 
+  .my-upload-list {
+    // 杩囨浮
+    position: absolute;
+    left: 78px;
+    transition: all 0.3s;
+  }
+
   .tox-statusbar {
     display: flex;
     // position: absolute;
+    min-height: 40px;
     border-bottom-right-radius: 8px;
     border-bottom-left-radius: 8px;
     background: #f0f2f5;
@@ -496,6 +518,7 @@
 
   .ant-upload-list-picture {
     display: flex;
+    flex-wrap: wrap;
   }
 
   .ant-upload-list-picture-card {
diff --git a/src/components/Tinymce/src/index.vue b/src/components/Tinymce/src/index.vue
new file mode 100644
index 0000000..9015a80
--- /dev/null
+++ b/src/components/Tinymce/src/index.vue
@@ -0,0 +1,540 @@
+<template>
+  <div :class="prefixCls" :style="{ width: containerWidth }">
+    <textarea
+      :id="tinymceId"
+      ref="elPwRef"
+      :style="{ visibility: 'hidden' }"
+      v-if="!initOptions.inline"
+    ></textarea>
+    <slot v-else></slot>
+    <!-- <div class="p-2 tox-statusbar">
+      <a-upload
+        v-if="isElse"
+        v-model:file-list="fileListTemp"
+        action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
+        list-type="picture"
+        class="upload-list-inline"
+      >
+        <a-button type="text" size="small" style="margin: 0 auto">
+          <upload-outlined></upload-outlined>
+          闄勪欢
+        </a-button>
+        <template #iconRender><PaperClipOutlined /></template>
+        <template #itemRender="{ file, fileList, actions }">
+          <a-space class="ant-upload-list-picture-card">
+            <span style="display: flex; flex-wrap: wrap">
+              <PaperClipOutlined style="margin-right: 4px" />
+              <span v-if="!file.editor" :style="file.status === 'error' ? 'color: red' : ''">{{
+                file.name
+              }}</span>
+              <span v-else>
+                <a-input size="small" v-model:value="file.tempName"></a-input>
+              </span>
+            </span>
+            <span v-if="!file.editor">
+              <a href="javascript:;" @click="actions.preview">棰勮</a>
+              <a href="javascript:;" @click="fnRename(file, fileList)">閲嶅懡鍚�</a>
+              <a href="javascript:;" @click="actions.remove">鍒犻櫎</a>
+            </span>
+            <span v-else>
+              <a href="javascript:;" @click="fnSaveRename(file, fileList)">淇濆瓨</a>
+              <a href="javascript:;" @click="fnOffRename(file, fileList)">鍙栨秷</a>
+            </span>
+          </a-space>
+        </template>
+      </a-upload>
+      <div :class="fileListTemp.length > 0 ? 'my-upload-list' : ''">
+        <div style="display: flex">
+          <ImgUpload
+            :fullscreen="fullscreen"
+            @uploading="handleImageUploading"
+            @done="handleDone"
+            v-if="isImg"
+            v-show="editorRef"
+            :title="'鍥剧墖'"
+            :disabled="disabled"
+            :accept="'.jpg,.jpeg,.gif,.png,.webp'"
+          />
+          <a-button v-if="isText" type="text" size="small">
+            <SnippetsOutlined />
+            蹇�熸枃鏈�</a-button
+          >
+        </div>
+      </div>
+    </div> -->
+  </div>
+</template>
+
+<script lang="ts" setup>
+  import type { Editor, RawEditorSettings } from 'tinymce';
+  import { PaperClipOutlined, UploadOutlined, SnippetsOutlined } from '@ant-design/icons-vue';
+  import tinymce from 'tinymce/tinymce';
+  import 'tinymce/themes/silver';
+  import 'tinymce/icons/default/icons';
+  import 'tinymce/plugins/advlist';
+  import 'tinymce/plugins/anchor';
+  import 'tinymce/plugins/autolink';
+  import 'tinymce/plugins/autosave';
+  import 'tinymce/plugins/code';
+  import 'tinymce/plugins/codesample';
+  import 'tinymce/plugins/directionality';
+  import 'tinymce/plugins/fullscreen';
+  import 'tinymce/plugins/hr';
+  import 'tinymce/plugins/insertdatetime';
+  import 'tinymce/plugins/link';
+  import 'tinymce/plugins/lists';
+  import 'tinymce/plugins/media';
+  import 'tinymce/plugins/nonbreaking';
+  import 'tinymce/plugins/noneditable';
+  import 'tinymce/plugins/pagebreak';
+  import 'tinymce/plugins/paste';
+  import 'tinymce/plugins/preview';
+  import 'tinymce/plugins/print';
+  import 'tinymce/plugins/save';
+  import 'tinymce/plugins/searchreplace';
+  import 'tinymce/plugins/spellchecker';
+  import 'tinymce/plugins/tabfocus';
+  // import 'tinymce/plugins/table';
+  import 'tinymce/plugins/template';
+  import 'tinymce/plugins/textpattern';
+  import 'tinymce/plugins/visualblocks';
+  import 'tinymce/plugins/visualchars';
+  import 'tinymce/plugins/wordcount';
+
+  import 'tinymce/plugins/image';
+  import 'tinymce/plugins/table';
+  import 'tinymce/plugins/charmap';
+  import 'tinymce/plugins/imagetools';
+  import 'tinymce/plugins/help';
+  import 'tinymce/plugins/emoticons';
+  import 'tinymce/plugins/emoticons/js/emojis';
+  // import 'tinymce/plugins/bdmap';
+  // import 'tinymce/plugins/indent2em';
+  import 'tinymce/plugins/autoresize';
+  // import 'tinymce/plugins/formatpainter';
+  // import 'tinymce/plugins/axupimgs';
+
+  // import 'tinymce/plugins/powerpaste';
+  // import 'tinymce/plugins/casechange';
+  import 'tinymce/plugins/importcss';
+  // import 'tinymce/plugins/tinyddrive';
+  // import 'tinymce/plugins/advcode';
+  // import 'tinymce/plugins/mediaembed';
+  import 'tinymce/plugins/toc';
+  // import 'tinymce/plugins/checklist';
+  // import 'tinymce/plugins/tinycespellchecker';
+  // import 'tinymce/plugins/a11ychecker';
+  // import 'tinymce/plugins/permanentpen';
+  // import 'tinymce/plugins/pageembed';
+  // import 'tinymce/plugins/tinycomments';
+  // import 'tinymce/plugins/mentions';
+  import 'tinymce/plugins/quickbars';
+  // import 'tinymce/plugins/linkchecker';
+  // import 'tinymce/plugins/advtable';
+  // import 'tinymce/plugins/export';
+
+  import {
+    computed,
+    nextTick,
+    ref,
+    unref,
+    watch,
+    onDeactivated,
+    onBeforeUnmount,
+    PropType,
+    useAttrs,
+  } from 'vue';
+  import ImgUpload from './ImgUpload.vue';
+  import {
+    plugins as defaultPlugins,
+    toolbar as defaultToolbar,
+    toolbar_groups as defaultStyleFormats,
+  } from './tinymce';
+  import { buildShortUUID } from '@/utils/uuid';
+  import { bindHandlers } from './helper';
+  import { onMountedOrActivated } from '@vben/hooks';
+  import { useDesign } from '@/hooks/web/useDesign';
+  import { isNumber } from '@/utils/is';
+  import { useLocale } from '@/locales/useLocale';
+  import { useAppStore } from '@/store/modules/app';
+
+  defineOptions({ name: 'Tinymce', inheritAttrs: false });
+
+  const props = defineProps({
+    options: {
+      type: Object as PropType<Partial<RawEditorSettings>>,
+      default: () => ({}),
+    },
+    value: {
+      type: String,
+    },
+
+    toolbar: {
+      type: Array as PropType<string[]>,
+      default: defaultToolbar,
+    },
+    plugins: {
+      type: Array as PropType<string[]>,
+      default: defaultPlugins,
+    },
+    toolbar_groups: {
+      type: Object as PropType<{}>,
+      default: defaultStyleFormats,
+    },
+    modelValue: {
+      type: String,
+    },
+    height: {
+      type: [Number, String] as PropType<string | number>,
+      required: false,
+      default: 400,
+    },
+    width: {
+      type: [Number, String] as PropType<string | number>,
+      required: false,
+      default: 'auto',
+    },
+    showImageUpload: {
+      type: Boolean,
+      default: true,
+    },
+    fontsize: {
+      type: String,
+    },
+    isElse: {
+      type: Boolean,
+      default: true,
+    },
+    isText: {
+      type: Boolean,
+      default: true,
+    },
+    isImg: {
+      type: Boolean,
+      default: true,
+    },
+  });
+
+  const emit = defineEmits(['change', 'update:modelValue', 'inited', 'init-error']);
+
+  const attrs = useAttrs();
+  const editorRef = ref<Editor | null>(null);
+  const fullscreen = ref(false);
+  const tinymceId = ref<string>(buildShortUUID('tiny-vue-pw'));
+  const elPwRef = ref<HTMLElement | null>(null);
+
+  const { prefixCls } = useDesign('tinymce-container');
+
+  const appStore = useAppStore();
+
+  const containerWidth = computed(() => {
+    const width = props.width;
+    if (isNumber(width)) {
+      return `${width}px`;
+    }
+    return width;
+  });
+
+  const skinName = computed(() => {
+    return appStore.getDarkMode === 'light' ? 'oxide' : 'oxide-dark';
+  });
+
+  const langName = computed(() => {
+    const lang = useLocale().getLocale.value;
+    return ['zh_CN', ''].includes(lang) ? lang : 'zh_CN';
+  });
+
+  const initOptions = computed((): RawEditorSettings => {
+    const { height, options, toolbar, plugins, toolbar_groups } = props;
+
+    const publicPath = import.meta.env.VITE_PUBLIC_PATH || '/';
+    return {
+      selector: `#${unref(tinymceId)}`,
+      height,
+      min_height: 450,
+      font_formats:
+        '寰蒋闆呴粦=Microsoft YaHei,Helvetica Neue,PingFang SC,sans-serif;鑻规灉鑻规柟=PingFang SC,Microsoft YaHei,sans-serif;瀹嬩綋=simsun,serif;浠垮畫浣�=FangSong,serif;榛戜綋=SimHei,sans-serif;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;Andale Mono=andale mono,times;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;Comic Sans MS=comic sans ms,sans-serif;Courier New=courier new,courier;Georgia=georgia,palatino;Helvetica=helvetica;Impact=impact,chicago;Symbol=symbol;Tahoma=tahoma,arial,helvetica,sans-serif;Terminal=terminal,monaco;Times New Roman=times new roman,times;Trebuchet MS=trebuchet ms,geneva;Verdana=verdana,geneva;Webdings=webdings;Wingdings=wingdings,zapf dingbats',
+      fontsize_formats: '10px 11px 12px 14px 16px 18px 24px 36px 48px 48px 56px 72px',
+      image_advtab: true,
+      importcss_append: true, // 鍏佽鏍峰紡鐢熸晥
+      menubar: false,
+      branding: false,
+      elementpath: false,
+      toolbar: [],
+      // quickbars_selection_toolbar: 'bold italic | quicklink h2 h3 blockquote quickimage quicktable',
+      plugins,
+      language_url: publicPath + 'resource/tinymce/langs/' + langName.value + '.js',
+      language: langName.value,
+      default_link_target: '_blank',
+      link_title: false,
+      statusbar: false,
+      object_resizing: false,
+      auto_focus: true, //璁╃紪杈戝櫒鍔犺浇瀹屾垚鍚庤嚜鍔ㄨ幏寰楀厜鏍囩劍鐐�
+      autosave_ask_before_unload: true,
+      autosave_interval: '30s',
+      skin: skinName.value,
+      skin_url: publicPath + 'resource/tinymce/skins/ui/' + skinName.value,
+      content_css: publicPath + 'resource/tinymce/skins/ui/' + skinName.value + '/content.min.css',
+      ...options,
+      readonly: true,
+      setup: (editor: Editor) => {
+        editorRef.value = editor;
+        editor.on('init', (e) => initSetup(e));
+      },
+    };
+  });
+
+  const disabled = computed(() => {
+    const { options } = props;
+    const getdDisabled = options && Reflect.get(options, 'readonly');
+    const editor = unref(editorRef);
+    if (editor) {
+      editor.setMode(getdDisabled ? 'readonly' : 'design');
+    }
+    return getdDisabled ?? false;
+  });
+
+  watch(
+    () => attrs.disabled,
+    () => {
+      const editor = unref(editorRef);
+      if (!editor) {
+        return;
+      }
+      editor.setMode(attrs.disabled ? 'readonly' : 'design');
+    },
+  );
+
+  onMountedOrActivated(() => {
+    if (!initOptions.value.inline) {
+      tinymceId.value = buildShortUUID('tiny-vue');
+    }
+    nextTick(() => {
+      setTimeout(() => {
+        initEditor();
+      }, 30);
+    });
+  });
+
+  onBeforeUnmount(() => {
+    destory();
+  });
+
+  onDeactivated(() => {
+    destory();
+  });
+
+  function destory() {
+    if (tinymce !== null) {
+      tinymce?.remove?.(unref(initOptions).selector!);
+    }
+  }
+
+  function initEditor() {
+    const el = unref(elPwRef);
+    if (el) {
+      el.style.visibility = '';
+    }
+    tinymce
+      .init(unref(initOptions))
+      .then((editor) => {
+        emit('inited', editor);
+      })
+      .catch((err) => {
+        emit('init-error', err);
+      });
+  }
+
+  function initSetup(e) {
+    const editor = unref(editorRef);
+    if (!editor) {
+      return;
+    }
+    const value = props.modelValue || '';
+
+    editor.setContent(value);
+    bindModelHandlers(editor);
+    bindHandlers(e, attrs, unref(editorRef));
+  }
+
+  function setValue(editor: Record<string, any>, val?: string, prevVal?: string) {    
+    if (!val) {
+      editor.setContent('');
+    }
+    if (
+      editor &&
+      typeof val === 'string' &&
+      val !== prevVal &&
+      val !== editor.getContent({ format: attrs.outputFormat })
+    ) {
+      editor.setContent(val);
+    }
+
+  }
+
+  function bindModelHandlers(editor: any) {
+    const modelEvents = attrs.modelEvents ? attrs.modelEvents : null;
+    const normalizedEvents = Array.isArray(modelEvents) ? modelEvents.join(' ') : modelEvents;
+
+    watch(
+      () => props.modelValue,
+      (val, prevVal) => {
+        if(val){
+          setValue(editor, val, prevVal);
+        }
+      },
+    );
+
+    watch(
+      () => props.value,
+      (val, prevVal) => {
+        setValue(editor, val, prevVal);
+      },
+      {
+        immediate: true,
+      },
+    );
+
+    editor.on(normalizedEvents ? normalizedEvents : 'change keyup undo redo', () => {
+      const content = editor.getContent({ format: attrs.outputFormat });
+      emit('update:modelValue', content);
+      const data = {
+        content,
+        fileUNID: fileListTemp.value,
+      };
+      emit('change', data);
+    });
+
+    editor.on('FullscreenStateChanged', (e) => {
+      fullscreen.value = e.state;
+    });
+  }
+
+  function handleImageUploading(name: string) {
+    const editor = unref(editorRef);
+    if (!editor) {
+      return;
+    }
+    editor.execCommand('mceInsertContent', false, getUploadingImgName(name));
+    const content = editor?.getContent() ?? '';
+    setValue(editor, content);
+  }
+
+  function handleDone(name: string, url: string) {
+    const editor = unref(editorRef);
+    if (!editor) {
+      return;
+    }
+    const content = editor?.getContent() ?? '';
+    const val = content?.replace(getUploadingImgName(name), `<img src="${url}"/>`) ?? '';
+    setValue(editor, val);
+  }
+
+  function getUploadingImgName(name: string) {
+    return `[uploading:${name}]`;
+  }
+
+  // 闄勪欢
+  const fileListTemp = ref<UploadProps['fileList']>([
+    // {
+    //   uid: '-1',
+    //   name: 'xxx.png',
+    //   tempName: 'xxx',
+    //   status: 'done',
+    //   url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
+    //   thumbUrl: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
+    //   editor: false,
+    // },
+    // {
+    //   uid: '-2',
+    //   name: 'yyy.png',
+    //   tempName: 'yyy',
+    //   status: 'done',
+    //   url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
+    //   thumbUrl: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
+    //   editor: false,
+    // },
+  ]);
+  function fnRename(file, fileList) {
+    console.log(file, fileList);
+    fileListTemp.value = fileList.map((item) => {
+      // item.tempName = item.name.split('.').slice(0,-1)
+      if (file.uid == item.uid) {
+        item.editor = true;
+      } else {
+        item.editor = false;
+      }
+      return item;
+    });
+  }
+  function fnSaveRename(file, fileList) {
+    fileListTemp.value = fileList.map((item) => {
+      if (file.uid == item.uid) {
+        item.name = item.tempName;
+        item.editor = false;
+      }
+      return item;
+    });
+  }
+  function fnOffRename(file, fileList) {
+    fileListTemp.value = fileList.map((item) => {
+      if (file.uid == item.uid) {
+        item.editor = false;
+      }
+      return item;
+    });
+  }
+</script>
+<style lang="less" scope>
+  @prefix-cls: ~'@{namespace}-tinymce-container';
+
+  .@{prefix-cls} {
+    position: relative;
+    line-height: normal;
+
+    textarea {
+      visibility: hidden;
+      z-index: -1;
+    }
+  }
+
+  .my-upload-list {
+    // 杩囨浮
+    position: absolute;
+    left: 78px;
+    transition: all 0.3s;
+  }
+
+  .tox-statusbar {
+    display: flex;
+    // position: absolute;
+    min-height: 40px;
+    border-bottom-right-radius: 8px;
+    border-bottom-left-radius: 8px;
+    background: #f0f2f5;
+  }
+
+  .icon-text {
+    margin-right: 10px;
+    font-size: 14px;
+  }
+
+  .ant-upload-list-picture {
+    display: flex;
+    flex-wrap: wrap;
+  }
+
+  .ant-upload-list-picture-card {
+    display: flex;
+    justify-content: space-between;
+    width: 24vw;
+    margin-right: 10px;
+    padding: 5px 10px;
+    background-color: #edf3f9;
+
+    a {
+      padding: 0 5px;
+      font-size: 12px;
+    }
+  }
+</style>
diff --git a/src/layouts/default/content/email.vue b/src/layouts/default/content/email.vue
new file mode 100644
index 0000000..851be20
--- /dev/null
+++ b/src/layouts/default/content/email.vue
@@ -0,0 +1,57 @@
+<template>
+  <div
+    :class="[prefixCls, getLayoutContentMode]"
+    v-loading="getOpenPageLoading && getPageLoading"
+    ref="content"
+  >
+    <PageLayout />
+    <BackTop v-if="getUseOpenBackTop" :target="() => content" :visibilityHeight="100" />
+  </div>
+</template>
+<script lang="ts" setup>
+  import { ref } from 'vue';
+  import { BackTop } from 'ant-design-vue';
+
+  import PageLayout from '@/views/email/index.vue';
+  import { useDesign } from '@/hooks/web/useDesign';
+  import { useRootSetting } from '@/hooks/setting/useRootSetting';
+  import { useTransitionSetting } from '@/hooks/setting/useTransitionSetting';
+  import { useContentViewHeight } from './useContentViewHeight';
+
+  defineOptions({ name: 'LayoutContent' });
+
+  const { prefixCls } = useDesign('layout-content');
+  const { getOpenPageLoading } = useTransitionSetting();
+  const { getLayoutContentMode, getPageLoading, getUseOpenBackTop } = useRootSetting();
+
+  useContentViewHeight();
+
+  const content = ref();
+</script>
+<style lang="less">
+  @prefix-cls: ~'@{namespace}-layout-content';
+
+  .@{prefix-cls} {
+    display: flex;
+    position: relative;
+    flex-direction: column;
+    flex-grow: 1;
+    width: 100%;
+    height: 0;
+    min-height: 0;
+    overflow: auto;
+
+    // begin: 涓嬮潰杩欏潡浠g爜 鍦ㄦ垜鐨勯」鐩墦鍖呭悗鍦ㄦ瘮杈冨鐨勫睆骞�(2K 31 瀵�)鏈夋樉绀� bug 鏈夊伓鍙戞�� 娓呯紦瀛橀娆¤繘鍏ヤ細鍑虹幇 , 鍒锋柊灏辨病浜�, 杩欓噷涓轰粈涔堣鎸囧畾瀹藉害 ?
+    &.fixed {
+      width: 1200px;
+      margin: 0 auto;
+    }
+    // end
+
+    &-loading {
+      position: absolute;
+      z-index: @page-loading-z-index;
+      top: 200px;
+    }
+  }
+</style>
diff --git a/src/layouts/default/email.vue b/src/layouts/default/email.vue
new file mode 100644
index 0000000..1802b5f
--- /dev/null
+++ b/src/layouts/default/email.vue
@@ -0,0 +1,91 @@
+<template>
+  <Layout :class="prefixCls" v-bind="lockEvents">
+    <LayoutFeatures />
+    <LayoutHeader fixed v-if="getShowFullHeaderRef" />
+    <Layout :class="[layoutClass, `${prefixCls}-out`]">
+      <LayoutSideBar v-if="getShowSidebar || getIsMobile" />
+      <Layout :class="`${prefixCls}-main`">
+        <LayoutMultipleHeader />
+        <LayoutContent />
+        <LayoutFooter />
+      </Layout>
+    </Layout>
+  </Layout>
+</template>
+
+<script lang="ts" setup>
+  import { computed, unref } from 'vue';
+  import { Layout } from 'ant-design-vue';
+  import { createAsyncComponent } from '@/utils/factory/createAsyncComponent';
+
+  import LayoutHeader from './header/index.vue';
+  import LayoutContent from './content/email.vue';
+  import LayoutSideBar from './sider/index.vue';
+  import LayoutMultipleHeader from './header/MultipleHeader.vue';
+
+  import { useHeaderSetting } from '@/hooks/setting/useHeaderSetting';
+  import { useMenuSetting } from '@/hooks/setting/useMenuSetting';
+  import { useDesign } from '@/hooks/web/useDesign';
+  import { useLockPage } from '@/hooks/web/useLockPage';
+
+  import { useAppInject } from '@/hooks/web/useAppInject';
+
+  import { useMultipleTabSetting } from '@/hooks/setting/useMultipleTabSetting';
+
+  const LayoutFeatures = createAsyncComponent(() => import('@/layouts/default/feature/index.vue'));
+  const LayoutFooter = createAsyncComponent(() => import('@/layouts/default/footer/index.vue'));
+
+  defineOptions({ name: 'DefaultLayout' });
+
+  const { prefixCls } = useDesign('default-layout');
+  const { getIsMobile } = useAppInject();
+  const { getShowFullHeaderRef } = useHeaderSetting();
+  const { getShowSidebar, getIsMixSidebar, getShowMenu } = useMenuSetting();
+  const { getAutoCollapse } = useMultipleTabSetting();
+
+  // Create a lock screen monitor
+  const lockEvents = useLockPage();
+
+  const layoutClass = computed(() => {
+    let cls: string[] = ['ant-layout'];
+    if (unref(getIsMixSidebar) || unref(getShowMenu)) {
+      cls.push('ant-layout-has-sider');
+    }
+
+    if (!unref(getShowMenu) && unref(getAutoCollapse)) {
+      cls.push('ant-layout-auto-collapse-tabs');
+    }
+
+    return cls;
+  });
+</script>
+<style lang="less">
+  @prefix-cls: ~'@{namespace}-default-layout';
+
+  .@{prefix-cls} {
+    display: flex;
+    flex-direction: column;
+    width: 100%;
+    min-height: 100%;
+    background-color: @content-bg;
+
+    > .ant-layout {
+      min-height: 100%;
+    }
+
+    &-main {
+      width: 100%;
+      margin-left: 1px;
+    }
+  }
+
+  .@{prefix-cls}-out {
+    &.ant-layout-has-sider {
+      .@{prefix-cls} {
+        &-main {
+          margin-left: 1px;
+        }
+      }
+    }
+  }
+</style>
diff --git a/src/layouts/page/email.vue b/src/layouts/page/email.vue
new file mode 100644
index 0000000..7bf5dc7
--- /dev/null
+++ b/src/layouts/page/email.vue
@@ -0,0 +1,86 @@
+<template>
+  <div>
+    <PageWrapper :class="`${prefixCls}`" dense contentFullHeight fixedHeight>
+      <div class="default-theme" style="display: flex;height: 100%;background-color: #fff;">
+        <div style="width: 10%;height: 100%;">
+          <LeftNav></LeftNav>
+        </div>
+        <div style="width: 84%;height: 100%;">
+          <RouterView>
+            <template #default="{ Component, route }">
+              <transition
+                :name="
+                  getTransitionName({
+                    route,
+                    openCache,
+                    enableTransition: getEnableTransition,
+                    cacheTabs: getCaches,
+                    def: getBasicTransition,
+                  })
+                "
+                mode="out-in"
+                appear
+              >
+                <keep-alive v-if="openCache" :include="getCaches">
+                  <component :is="Component" :key="route.fullPath" />
+                </keep-alive>
+                <component v-else :is="Component" :key="route.fullPath" />
+              </transition>
+            </template>
+          </RouterView>
+        </div>
+      </div>
+    </PageWrapper>
+    <FrameLayout v-if="getCanEmbedIFramePage" />
+  </div>
+</template>
+
+<script lang="ts" setup>
+import {PageWrapper} from '@/components/Page';
+import { Splitpanes, Pane } from 'splitpanes';
+
+  import LeftNav from '@/views/email/components/LeftNav.vue';
+  import { computed, unref } from 'vue';
+
+  import FrameLayout from '@/layouts/iframe/index.vue';
+
+  import { useRootSetting } from '@/hooks/setting/useRootSetting';
+
+  import { useTransitionSetting } from '@/hooks/setting/useTransitionSetting';
+  import { useMultipleTabSetting } from '@/hooks/setting/useMultipleTabSetting';
+  import { getTransitionName } from './transition';
+
+  import { useMultipleTabStore } from '@/store/modules/multipleTab';
+
+  defineOptions({ name: 'PageLayout' });
+
+  const { getShowMultipleTab } = useMultipleTabSetting();
+  const tabStore = useMultipleTabStore();
+
+  const { getOpenKeepAlive, getCanEmbedIFramePage } = useRootSetting();
+
+  const { getBasicTransition, getEnableTransition } = useTransitionSetting();
+
+  const openCache = computed(() => unref(getOpenKeepAlive) && unref(getShowMultipleTab));
+
+  const getCaches = computed((): string[] => {
+    if (!unref(getOpenKeepAlive)) {
+      return [];
+    }
+    return tabStore.getCachedTabList;
+  });
+  import { useDesign } from '@/hooks/web/useDesign';
+
+  const { prefixCls } = useDesign('email');
+</script>
+<style lang="less" scoped>
+  @prefix-cls: ~'@{namespace}-email';
+  .@{prefix-cls} {
+    .splitpanes__pane {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      background-color: #fff;
+    }
+  }
+</style>
diff --git a/src/main.ts b/src/main.ts
index 29bea12..c6d9183 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -15,7 +15,6 @@
 import { router, setupRouter } from '@/router';
 import { setupRouterGuard } from '@/router/guard';
 import { setupStore } from '@/store';
-
 import VueCookies from 'vue-cookies'
 import Antd from 'ant-design-vue';
 
diff --git a/src/router/constant.ts b/src/router/constant.ts
index e57bc26..a9d7be7 100644
--- a/src/router/constant.ts
+++ b/src/router/constant.ts
@@ -10,6 +10,7 @@
  * @description: default layout
  */
 export const LAYOUT = () => import('@/layouts/default/index.vue');
+export const EMAILLAYOUT = () => import('@/layouts/default/email.vue');
 
 /**
  * @description: parent-layout
diff --git a/src/router/routes/index.ts b/src/router/routes/index.ts
index 0bb349a..3255b09 100644
--- a/src/router/routes/index.ts
+++ b/src/router/routes/index.ts
@@ -1,6 +1,7 @@
 import type { AppRouteRecordRaw, AppRouteModule } from '@/router/types';
 
 import { PAGE_NOT_FOUND_ROUTE, REDIRECT_ROUTE } from '@/router/routes/basic';
+import { useEmailRouterStoreWithout } from '@/store/modules/emailRouter';
 
 import { mainOutRoutes } from './mainOut';
 import { PageEnum } from '@/enums/pageEnum';
@@ -8,6 +9,7 @@
 
 // import.meta.glob() 鐩存帴寮曞叆鎵�鏈夌殑妯″潡 Vite 鐙湁鐨勫姛鑳�
 const modules = import.meta.glob('./modules/**/*.ts', { eager: true });
+
 const routeModuleList: AppRouteModule[] = [];
 
 // 鍔犲叆鍒拌矾鐢遍泦鍚堜腑
@@ -15,6 +17,10 @@
   const mod = (modules as Recordable)[key].default || {};
   const modList = Array.isArray(mod) ? [...mod] : [mod];
   routeModuleList.push(...modList);
+  if (mod.name === 'Email'){
+    const emailRouter = useEmailRouterStoreWithout();
+    emailRouter.setEmailRouters(mod.children)
+  }
 });
 
 export const asyncRoutes = [PAGE_NOT_FOUND_ROUTE, ...routeModuleList];
diff --git a/src/router/routes/modules/email.ts b/src/router/routes/modules/email.ts
index a7600b2..06dfdce 100644
--- a/src/router/routes/modules/email.ts
+++ b/src/router/routes/modules/email.ts
@@ -1,40 +1,128 @@
 锘縤mport type { AppRouteModule } from '@/router/types';
 
-import { LAYOUT } from '@/router/constant';
+import { EMAILLAYOUT } from '@/router/constant';
 
-const steps: AppRouteModule = {
+const email: AppRouteModule = {
   path: '/email',
   name: 'Email',
-  component: LAYOUT,
+  component: EMAILLAYOUT,
   redirect: '/email/index',
   meta: {
-    orderNo: 20,
-    hideChildrenInMenu: true,
+    orderNo: 10,
     icon: 'mdi:email',
     title: '閭欢',
+    hideTab: true,
+    hideChildrenInMenu: true,
+    // hideMenu:true
   },
   children: [
     {
-      path: 'index',
-      name: 'EmailPage',
-      component: () => import('@/views/email/index.vue'),
+      path: 'Inbox',
+      name: 'Inbox',
+      component: () => import('@/views/email/Inbox/index.vue'),
       meta: {
-        title: '閭欢',
-        icon: 'mdi:email-open',
-        hideMenu: true,
+        title: '鏀朵欢绠�',
+      },
+      children: [
+        {
+          path: 'list',
+          name: 'InboxPage1',
+          component: () => import('@/views/email/Inbox/index.vue'),
+          meta: {
+            title: '鍏ㄩ儴閭欢',
+          },
+        },
+        // {
+        //   path: 'list/:id',
+        //   name: 'EmailPage2',
+        //   component: () => import('@/views/email/UnreadEmail/user.vue'),
+        //   meta: {
+        //     title: '222',
+        //     currentActiveMenu: '/email/index',
+        //   },
+        // },
+      ],
+    },
+    {
+      path: 'index',
+      name: 'Index',
+      component: () => import('@/views/email/UnreadEmail/index.vue'),
+      meta: {
+        title: '鏈閭欢',
       },
     },
     {
-      path: 'customer',
-      name: 'EmailCustomerPage',
-      component: () => import('@/views/email/customer.vue'),
+      path: 'utils',
+      name: 'Utils',
+      component: () => import('@/views/email/Utils/index.vue'),
       meta: {
-        title: '瀹㈡埛',
-        icon: 'mdi:email-open',
+        title: '閭欢閰嶇疆',
+        hideTab: true,
         hideMenu: true,
+        currentActiveMenu: '/email/index',
       },
     },
+    {
+      path: 'edit',
+      name: 'Edit',
+      component: () => import('@/views/email/Edit/index.vue'),
+      meta: {
+        title: '缂栬緫閭欢',
+        hideTab: true,
+        hideMenu: true,
+        currentActiveMenu: '/email/index',
+      },
+    },
+    {
+      path: 'HandlingEmailsOnBehalfOfOthers',
+      name: 'HandlingEmailsOnBehalfOfOthers',
+      component: () => import('@/views/email/HandlingEmailsOnBehalfOfOthers/index.vue'),
+      meta: {
+        title: '寰呭鐞嗛偖浠�',
+        hideTab: true,
+      },
+    },
+
+    {
+      path: 'Drafts',
+      name: 'Drafts',
+      component: () => import('@/views/email/Drafts/index.vue'),
+      meta: {
+        title: '鑽夌绠�',
+        hideTab: true,
+      },
+    },
+    {
+      path: 'outbox',
+      name: 'Outbox',
+      component: () => import('@/views/email/outbox/index.vue'),
+      meta: {
+        title: '鍙戜欢绠�',
+        hideTab: true,
+        currentActiveMenu: '/email/index',
+      },
+      children: [
+        {
+          path: 'list',
+          name: 'Outbox',
+          component: () => import('@/views/email/outbox/index.vue'),
+          meta: {
+            title: '鍏ㄩ儴閭欢',
+            currentActiveMenu: '/email/index',
+          },
+        }]
+    },
+    // {
+    //   path: 'MassMailbox',
+    //   name: 'MassMailbox',
+    //   component: () => import('@/views/email/Edit/index.vue'),
+    //   meta: {
+    //     title: '缇ゅ彂绠�',
+    //     hideTab: true,
+    //     currentActiveMenu: '/email/index',
+    //   },
+    // },
   ],
 };
 
-export default steps;
+export default email;
diff --git a/src/router/routes/modules/preview.ts b/src/router/routes/modules/preview.ts
new file mode 100644
index 0000000..81b94b5
--- /dev/null
+++ b/src/router/routes/modules/preview.ts
@@ -0,0 +1,31 @@
+import type { AppRouteModule } from '@/router/types';
+
+import { LAYOUT } from '@/router/constant';
+
+
+const preview: AppRouteModule = {
+  path: '/preview',
+  name: 'preview',
+  component: LAYOUT,
+  redirect: '/preview/index',
+  meta: {
+    orderNo: 10,
+    icon: 'ion:grid-outline',
+    title: '閭欢棰勮鈥�',
+  },
+  children: [
+    {
+      path: 'index',
+      name: 'previewIndex',
+      component: () => import('@/views/email/preview/index..vue'),
+      meta: {
+        // affix: true,
+        title: '棰勮閭欢',
+        hideTab: true,
+        hideMenu: true,
+      },
+    },
+  ],
+};
+
+export default preview;
diff --git a/src/store/modules/emailRouter.ts b/src/store/modules/emailRouter.ts
new file mode 100644
index 0000000..a6132c5
--- /dev/null
+++ b/src/store/modules/emailRouter.ts
@@ -0,0 +1,30 @@
+import { defineStore } from 'pinia';
+import { store } from '@/store';
+
+// 浣犲彲浠ヤ换鎰忓懡鍚� `defineStore()` 鐨勮繑鍥炲�硷紝浣嗘渶濂戒娇鐢� store 鐨勫悕瀛楋紝
+// 鍚屾椂浠� `use` 寮�澶翠笖浠� `Store` 缁撳熬銆�
+// (姣斿 `useUserStore`锛宍useCartStore`锛宍useProductStore`)
+// 绗竴涓弬鏁版槸浣犵殑搴旂敤涓� Store 鐨勫敮涓� ID銆�
+export const useEmailRouterStore = defineStore({
+  id: 'emailRouter',
+  state: () => ({
+    children: []
+  }),
+  getters: {
+    getEmailRouters: (state) => state.children
+  },
+  actions: {
+    setEmailRouters(children: any[]) {
+      this.children = [...children];
+    }
+  }
+});
+
+export function useEmailRouterStoreWithout() {
+  try {
+    return useEmailRouterStore(store);
+  } catch (error) {
+    console.error('Failed to initialize email router store:', error);
+    throw error;
+  }
+}
\ No newline at end of file
diff --git a/src/store/modules/useCollapseStore.ts b/src/store/modules/useCollapseStore.ts
new file mode 100644
index 0000000..d47c26f
--- /dev/null
+++ b/src/store/modules/useCollapseStore.ts
@@ -0,0 +1,19 @@
+import { defineStore } from 'pinia';
+
+// 瀹氫箟 Pinia Store
+export const useCollapseStore = defineStore('collapse', {
+  state: () => ({
+    isOpen: true as boolean,  // 鎶樺彔鐘舵��
+  }),
+  actions: {
+    toggle(is) {
+      this.isOpen = is;  // 鍒囨崲鐘舵��
+    },
+    open() {
+      this.isOpen = true;  // 灞曞紑
+    },
+    close() {
+      this.isOpen = false;  // 鎶樺彔
+    },
+  },
+});
diff --git a/src/utils/dateUtil.ts b/src/utils/dateUtil.ts
index e18387d..65c693d 100644
--- a/src/utils/dateUtil.ts
+++ b/src/utils/dateUtil.ts
@@ -5,6 +5,8 @@
 
 const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss';
 const DATE_FORMAT = 'YYYY-MM-DD';
+const DATE_DAY_FORMAT = 'YYYY-MM-DD HH:mm';
+
 
 export function formatToDateTime(date?: dayjs.ConfigType, format = DATE_TIME_FORMAT): string {
   return dayjs(date).format(format);
@@ -13,5 +15,13 @@
 export function formatToDate(date?: dayjs.ConfigType, format = DATE_FORMAT): string {
   return dayjs(date).format(format);
 }
+export function formatToDateDay(date?: dayjs.ConfigType, format = DATE_DAY_FORMAT): string {
+  return dayjs(date).format(format);
+}
+export function isBeforeToDay(date?: dayjs.ConfigType): boolean {
+  //鑾峰彇浠婂ぉ鐨勬棩鏈�
+  const now = dayjs();
+  return dayjs(date).isBefore(dayjs(now));
+}
 
 export const dateUtil = dayjs;
diff --git a/src/utils/http/axios/index.ts b/src/utils/http/axios/index.ts
index cfb5051..5a9a098 100644
--- a/src/utils/http/axios/index.ts
+++ b/src/utils/http/axios/index.ts
@@ -19,6 +19,7 @@
 import { useUserStoreWithOut } from '@/store/modules/user';
 import { AxiosRetry } from '@/utils/http/axios/axiosRetry';
 import axios from 'axios';
+import DragBar from '@/layouts/default/sider/DragBar.vue';
 
 const globSetting = useGlobSetting();
 const urlPrefix = globSetting.urlPrefix;
@@ -44,17 +45,19 @@
       return res.data;
     }
     // 閿欒鐨勬椂鍊欒繑鍥�
-
     const { data } = res;
     if (!data) {
       // return '[HTTP] Request has no return value';
       throw new Error(t('sys.api.apiRequestFailed'));
     }
     //  杩欓噷 code锛宺esult锛宮essage涓� 鍚庡彴缁熶竴鐨勫瓧娈碉紝闇�瑕佸湪 types.ts鍐呬慨鏀逛负椤圭洰鑷繁鐨勬帴鍙h繑鍥炴牸寮�
-    const { code, result, message } = data;
+    const { code, result, state } = data;
+    const { msg: message } = data;
 
     // 杩欓噷閫昏緫鍙互鏍规嵁椤圭洰杩涜淇敼
-    const hasSuccess = data && Reflect.has(data, 'code') && code === ResultEnum.SUCCESS;
+    const isPass = code === ResultEnum.SUCCESS || state === ResultEnum.SUCCESS;
+    const isReflect = Reflect.has(data, 'code') || Reflect.has(data, 'state');
+    const hasSuccess = data && isReflect && isPass;
     if (hasSuccess) {
       let successMsg = message;
 
@@ -67,7 +70,7 @@
       } else if (options.successMessageMode === 'message') {
         createMessage.success(successMsg);
       }
-      return result;
+      return result || data;
     }
 
     // 鍦ㄦ澶勬牴鎹嚜宸遍」鐩殑瀹為檯鎯呭喌瀵逛笉鍚岀殑code鎵ц涓嶅悓鐨勬搷浣�
diff --git a/src/utils/tool/inedx.ts b/src/utils/tool/inedx.ts
new file mode 100644
index 0000000..12f284f
--- /dev/null
+++ b/src/utils/tool/inedx.ts
@@ -0,0 +1,5 @@
+const queryString = window.location.search; // 鑾峰彇 ? 鍚庨潰鐨勫瓧绗︿覆
+const params = new URLSearchParams(queryString);
+
+const emailParam = params.toString(); // 鑾峰彇鏁翠釜鏌ヨ閮ㄥ垎鐨勫瓧绗︿覆
+console.log(emailParam); // 杈撳嚭 1244041895@qq.com=test@example.com
diff --git a/src/views/email/Drafts/index.vue b/src/views/email/Drafts/index.vue
new file mode 100644
index 0000000..b56bbee
--- /dev/null
+++ b/src/views/email/Drafts/index.vue
@@ -0,0 +1,58 @@
+<template>
+  <div>
+    <a-spin :spinning="loading" class="p-1" style="height: 100%">
+      <PageIndex :pageList="pageList" :mailType="1" :pageData="pageData"> </PageIndex>
+    </a-spin>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  name: 'Drafts';
+  import { ref, onMounted, computed, provide } from 'vue';
+  import PageIndex from '@/views/email/components/ListPage/list.vue';
+  import { useRoute } from 'vue-router';
+
+  const route = useRoute();
+  // 鑾峰彇褰撳墠瀹屾暣 URL
+  const routerId = computed(() => {
+    try {
+      const url = window.location.href;
+      if (!url) {
+        throw new Error('Invalid URL');
+      }
+      const basePart = url.split('?')[1];
+      console.log(basePart, 'basePart');
+      return basePart;
+    } catch (error) {
+      console.error('Error processing URL:', error);
+      // 杩斿洖榛樿鍊兼垨绌哄瓧绗︿覆
+      return '';
+    }
+  });
+  import { receiveApi, getMailListApi } from '@/api/email/userList';
+  const pageList = ref([]);
+  const loading = ref(false);
+  const pageData = ref({
+    page: 1,
+    limit: 20,
+    total: 0,
+  });
+  function getDataList(page: 1) {
+    getMailListApi({ mail: routerId.value, mailType: 0, page })
+      .then((res) => {
+        loading.value = false;
+        if (res.code == 0) {
+          pageList.value = res.data.list;
+          pageData.value.total = res.data.total;
+        }
+      })
+      .catch(() => {
+        loading.value = false;
+      });
+  }
+  provide('getDataList', getDataList);
+  onMounted(() => {
+    getDataList();
+  });
+</script>
+<style scoped lang="less"></style>
diff --git a/src/views/email/Edit/index.vue b/src/views/email/Edit/index.vue
new file mode 100644
index 0000000..c855cd3
--- /dev/null
+++ b/src/views/email/Edit/index.vue
@@ -0,0 +1,577 @@
+<template>
+  <div class="p-3">
+    <a-spin :spinning="loading" class="p-1">
+      <a-form ref="formRef" :label-col="{ span: 2 }" :wrapper-col="{ span: 24 }" :model="modelRef">
+        <a-form-item :wrapper-col="{ span: 8 }">
+          <div style="display: flex; justify-content: space-evenly">
+            <a-button type="primary" shape="round" @click="fnHandleSubmit(modelRef)">鍙戦��</a-button>
+            <a-button shape="round" @click="fnSaveMailDrafts">瀛樿崏绋�</a-button>
+            <a-button shape="round" @click="fnPreview">棰勮</a-button>
+            <a-button shape="round">鎻愪氦瀹℃壒</a-button>
+            <a-button shape="round">鍙栨秷</a-button></div
+          >
+        </a-form-item>
+        <a-form-item label="鍙戜欢浜�" v-bind="validateInfos.sender" :wrapper-col="{ span: 12 }">
+          <a-select
+            v-model:value="modelRef.sender"
+            placeholder="閫夋嫨鍙戜欢浜�"
+            show-search
+            :field-names="{ label: 'email', value: 'email' }"
+            :options="state.senderList"
+            :filter-option="fnFilterOption"
+          >
+          </a-select>
+        </a-form-item>
+        <a-form-item label="鏀朵欢浜�" v-bind="validateInfos.recipients" :wrapper-col="{ span: 18 }">
+          <a-row>
+            <a-col class="gutter-row" :span="16">
+              <a-select
+                mode="tags"
+                placeholder="璇烽�夋嫨鏀朵欢浜烘垨鑰呰緭鍏ユ敹浠朵汉閭"
+                v-model:value="modelRef.recipients"
+                :options="state.recipientsList"
+                :field-names="{ label: 'email', value: 'email' }"
+                :maxTagCount="4"
+                @search="fetchUser"
+              >
+                <template #tagRender="{ label, closable, onClose, option }">
+                  <a-tag
+                    :closable="closable"
+                    style="margin-right: 3px"
+                    @close="onClose"
+                    :color="validateEmail(label) ? 'default' : 'red'"
+                  >
+                    {{ label }}&nbsp;&nbsp;
+                  </a-tag>
+                </template>
+              </a-select>
+            </a-col>
+            <a-col
+              class="gutter-row"
+              :span="2"
+              style="display: flex; align-items: center; justify-content: center"
+            >
+              <plus-circle-outlined
+                style="color: rgb(0 0 0 / 45%)"
+                @click="fnHandleSelect('recipients')"
+              />
+            </a-col>
+            <a-col class="gutter-row" :span="1">
+              <a-form-item-rest>
+                <a-button
+                  shape="round"
+                  type="link"
+                  block
+                  size="small"
+                  @click="ccCheckboxChange($event)"
+                >
+                  {{ !isSecretDeliveryPerson ? '鎶勯��' : '鍙栨秷鎶勯��' }}
+                </a-button>
+              </a-form-item-rest>
+            </a-col>
+            <a-col class="gutter-row" :span="1">
+              <a-form-item-rest>
+                <a-button
+                  shape="round"
+                  type="link"
+                  block
+                  size="small"
+                  @click="bccCheckboxChange($event)"
+                >
+                  {{ !isCRecipient ? '瀵嗛��' : '鍙栨秷瀵嗛��' }}
+                </a-button>
+              </a-form-item-rest>
+            </a-col>
+          </a-row>
+        </a-form-item>
+        <span style="position: relative; top: -18px; left: 9%" v-if="modelRef.recipients.length">
+          {{ userParticulars && `${userParticulars.userName}<${userParticulars.email}` }}>
+          <span v-if="!userParticulars.title" style="margin-right: 10px; margin-left: 5px">
+            鏆傛湭鏌ヨ鍒拌瀹㈡埛鐨勫綋鍦版椂闂�
+            <a @click="fnHandleTimeZone(userParticulars, 'add')">琛ュ厖鏃跺尯</a>
+          </span>
+          <span v-else>
+            {{ userParticulars.title }}
+            <a @click="fnHandleTimeZone(userParticulars, 'update')">鏃跺尯涓嶅锛�</a>
+          </span>
+
+          <a v-if="modelRef.recipients.length > 2">鏌ョ湅鍏ㄩ儴</a>
+        </span>
+
+        <a-form-item
+          label="鎶勯�佷汉"
+          v-bind="validateInfos.ccRecipients"
+          v-if="isSecretDeliveryPerson"
+          :wrapper-col="{ span: 18 }"
+        >
+          <a-row>
+            <a-col class="gutter-row" :span="16">
+              <a-select
+                mode="tags"
+                placeholder="璇烽�夋嫨鎶勯�佷汉鎴栬�呰緭鍏ユ妱閫佷汉閭"
+                v-model:value="modelRef.ccRecipients"
+                :options="state.recipientsList"
+                :field-names="{ label: 'email', value: 'email' }"
+                :maxTagCount="4"
+              >
+                <template #tagRender="{ label, closable, onClose }">
+                  <a-tag
+                    :closable="closable"
+                    style="margin-right: 3px"
+                    @close="onClose"
+                    :color="validateEmail(label) ? 'default' : 'red'"
+                  >
+                    {{ label }}&nbsp;&nbsp;
+                  </a-tag>
+                </template>
+              </a-select>
+            </a-col>
+            <a-col
+              class="gutter-row"
+              :span="2"
+              style="display: flex; align-items: center; justify-content: center"
+            >
+              <plus-circle-outlined
+                style="color: rgb(0 0 0 / 45%)"
+                @click="fnHandleSelect('ccRecipients')"
+              />
+            </a-col>
+          </a-row>
+        </a-form-item>
+        <a-form-item
+          label="瀵嗛�佷汉"
+          v-bind="validateInfos.bccRecipients"
+          v-if="isCRecipient"
+          :wrapper-col="{ span: 18 }"
+        >
+          <a-row>
+            <a-col class="gutter-row" :span="16">
+              <a-select
+                mode="tags"
+                placeholder="璇烽�夋嫨鎶勯�佷汉鎴栬�呰緭鍏ユ妱閫佷汉閭"
+                v-model:value="modelRef.bccRecipients"
+                :options="state.recipientsList"
+                :field-names="{ label: 'email', value: 'email' }"
+                :maxTagCount="4"
+              >
+                <template #tagRender="{ label, closable, onClose }">
+                  <a-tag
+                    :closable="closable"
+                    style="margin-right: 3px"
+                    @close="onClose"
+                    :color="validateEmail(label) ? 'default' : 'red'"
+                  >
+                    {{ label }}&nbsp;&nbsp;
+                  </a-tag>
+                </template>
+              </a-select>
+            </a-col>
+            <a-col
+              class="gutter-row"
+              :span="2"
+              style="display: flex; align-items: center; justify-content: center"
+            >
+              <plus-circle-outlined
+                style="color: rgb(0 0 0 / 45%)"
+                @click="fnHandleSelect('bccRecipients')"
+              />
+            </a-col>
+          </a-row>
+        </a-form-item>
+        <div
+          style="position: relative; top: -20px; left: 9%; color: #909090; font-size: 12px"
+          v-if="modelRef.recipients.length"
+        >
+          {{ `${userLength}/100 鏀朵欢浜恒�佹妱閫佷汉鍜屽瘑閫佺殑鑱旂郴浜烘�绘暟涓嶅彲瓒呰繃100` }}
+        </div>
+        <a-form-item label="涓婚" :wrapper-col="{ span: 12 }" :label-col="{ span: 2 }">
+          <a-input placeholder="璇疯緭鍏ラ偖浠朵富棰�" v-model:value="modelRef.subject" />
+        </a-form-item>
+        <a-form-item
+          v-bind="validateInfos.content"
+          :wrapper-col="{ span: 24 }"
+          :label-col="{ span: 2 }"
+        >
+          <Tinymce v-model="modelRef.content" @change="fnChangeContent"></Tinymce>
+        </a-form-item>
+      </a-form>
+    </a-spin>
+    <SelectUser
+      ref="selectUserRef"
+      v-model="openSelectUser"
+      :selectIds="selectIds"
+      :idName="idName"
+      :title="selectUserTitle"
+      @updateData="fnSelectUser"
+    />
+    <a-modal
+      v-model:open="openTimeZone"
+      :title="`${titleTimeZone}鏃跺尯`"
+      :confirm-loading="confirmLoading"
+      @ok="fnHandleTimeZoneOk"
+    >
+      <a-form
+        ref="formTimeZoneRef"
+        :label-col="{ span: 6 }"
+        :wrapper-col="{ span: 24 }"
+        :model="modelRef"
+      >
+        <a-form-item label="鍥藉鍦板尯锛�" :wrapper-col="{ span: 12 }">
+          <a-select
+            v-model:value="modelRef.sender"
+            placeholder="閫夋嫨鍥藉鍦板尯"
+            show-search
+            :field-names="{ label: 'name', value: 'id' }"
+            :options="state.data"
+          >
+          </a-select>
+        </a-form-item>
+        <a-form-item label="鏃跺尯锛�" :wrapper-col="{ span: 12 }">
+          <a-select
+            v-model:value="modelRef.sender"
+            placeholder="閫夋嫨鏃跺尯"
+            show-search
+            :field-names="{ label: 'name', value: 'id' }"
+            :options="state.data"
+          >
+          </a-select>
+        </a-form-item>
+        <a-form-item :label-col="{ span: 6 }">
+          <a-row>
+            <a-col span="6"> </a-col>
+            <a-col span="18">
+              <div style="color: #909090"
+                >褰撳湴瀹炴椂鏃堕棿鏍规嵁鏃跺尯淇℃伅鏄剧ず<br />
+                鍥藉鍦板尯/鏃跺尯淇℃伅鍦ㄥ鎴疯祫鏂�-鐗瑰緛淇℃伅鏌ョ湅</div
+              >
+            </a-col>
+          </a-row>
+        </a-form-item>
+      </a-form>
+    </a-modal>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  name: 'Edit';
+  import { useDesign } from '@/hooks/web/useDesign';
+
+  import { ref, reactive, computed, onMounted } from 'vue';
+  import { useMessage } from '@/hooks/web/useMessage';
+  import { Tinymce } from '@/components/Tinymce';
+  import { PlusCircleOutlined } from '@ant-design/icons-vue';
+  import SelectUser from '../components/SelectUser/index.vue';
+  import { Form } from 'ant-design-vue';
+  const modelRef = reactive({
+    sender: '',
+    recipients: [],
+    ccRecipients: [],
+    bccRecipients: [],
+    attachmentList: [],
+    subject: '',
+    content: '',
+  });
+  const loading = ref(false);
+  const rulesRef = reactive({
+    sender: [
+      {
+        required: true,
+        message: '鍙戜欢浜轰笉鑳戒负绌�',
+      },
+    ],
+    recipients: [
+      {
+        required: true,
+        message: '鏀朵欢浜轰笉鑳戒负绌�',
+      },
+    ],
+    content: [
+      {
+        required: true,
+        message: '鍐呭涓嶈兘涓虹┖',
+      },
+    ],
+  });
+  const TYPE = computed(() => {
+    return router.currentRoute.value.query.type || 'send';
+  });
+  onMounted(() => {
+    fnGetRecipientsList();
+    if (TYPE.value === 'reply') {
+      fuGetReplyEmailData();
+    }
+  });
+  const useForm = Form.useForm;
+  const { validate, validateInfos } = useForm(modelRef, rulesRef);
+
+  const formRef = ref();
+
+  let isSecretDeliveryPerson = ref(false);
+
+  let isCRecipient = ref(false);
+  const handleCheckboxChange = (ref, e) => {
+    ref.value = !ref.value;
+  };
+
+  const ccCheckboxChange = (e) => {
+    handleCheckboxChange(isSecretDeliveryPerson, e);
+  };
+  const bccCheckboxChange = (e) => {
+    handleCheckboxChange(isCRecipient, e);
+  };
+
+  const { createMessage } = useMessage();
+  import {
+    getAccountListApi,
+    sendingMailApi,
+    saveMailDraftsApi,
+    emailListAPi,
+    getMailInfoApi
+  } from '@/api/email/userList';
+  // 瀹氫箟鐘舵�佺鐞嗗璞�
+  const state = reactive({
+    data: [],
+    fetching: false,
+    error: null,
+    senderList: [],
+    recipientsList: [],
+  });
+
+  // 鑾峰彇鐢ㄦ埛鍒楄〃鐨勫嚱鏁�
+  const fnGetUserList = async (params) => {
+    try {
+      state.fetching = true;
+      const res = await getAccountListApi();
+      console.log(res, 'res');
+
+      if (res && res.data && Array.isArray(res.data)) {
+        state.senderList = res.data;
+      } else {
+        console.error('Invalid response format:', res);
+      }
+    } catch (error) {
+      console.error('Failed to fetch user list:', error);
+    } finally {
+      state.fetching = false;
+    }
+  };
+
+  // 鍒濆鍖栫敤鎴锋暟鎹�
+  const fetchData = async () => {
+    await fnGetUserList({ page: 1, pageSize: 30 });
+  };
+  console.log(state.data, 'state.data');
+  // 璋冪敤鍒濆鍖栧嚱鏁�
+  fetchData();
+
+  const fnFilterOption = (input: string, option: any) => {
+    return option.email.toLowerCase().indexOf(input.toLowerCase()) >= 0;
+  };
+
+  const openSelectUser = ref(false);
+  const selectUserTitle = ref('閫夋嫨鏀朵欢浜�');
+  const selectIds = ref([]);
+  const idName = ref('');
+  const fnHandleSelect = (e) => {
+    if (e === 'recipients') {
+      selectUserTitle.value = '閫夋嫨鏀朵欢浜�';
+      idName.value = e;
+    }
+    if (e === 'ccRecipients') {
+      selectUserTitle.value = '閫夋嫨鎶勯�佷汉';
+      idName.value = e;
+    }
+    if (e === 'bccRecipients') {
+      selectUserTitle.value = '閫夋嫨瀵嗛�佷汉';
+      idName.value = e;
+    }
+    selectIds.value = modelRef[e];
+    openSelectUser.value = true;
+  };
+
+  function fnChangeContent(e) {
+    modelRef.content = e.content;
+    modelRef.attachmentList = e.attachmentList;
+  }
+
+  function fnBuildingCommitData() {
+    return {
+      sender: modelRef.sender,
+      receiver: modelRef.recipients,
+      cc: modelRef.ccRecipients,
+      bcc: modelRef.bccRecipients,
+      subject: modelRef.subject,
+      content: modelRef.content,
+      attachmentList: '',
+      docCode: docCode.value,
+    };
+  }
+  const docCode = ref('');
+  import { Modal } from 'ant-design-vue';
+  function fnHandleSubmit(values: any) {
+    validate()
+      .then((res) => {
+        if (!modelRef.subject) {
+          Modal.confirm({
+            title: '鎻愮ず',
+            content: '閭欢涓婚涓虹┖锛岀‘瀹氳鍙戦�佸悧锛�',
+            onOk() {
+              pushSendingMail();
+            },
+            onCancel() {},
+          });
+        } else {
+          pushSendingMail();
+        }
+      })
+      .catch((error) => {
+        createMessage.error('琛ㄥ崟楠岃瘉澶辫触');
+      });
+  }
+  import { useRouter } from 'vue-router';
+  const router = useRouter();
+  function pushSendingMail() {
+    const data = fnBuildingCommitData();
+    loading.value = true;
+    sendingMailApi(data)
+      .then((res) => {
+        loading.value = false;
+        if (res.code === 0) {
+          createMessage.success(res.msg);
+          router.push('/email/list');
+        }
+      })
+      .catch((error) => {
+        loading.value = false;
+      });
+  }
+  function fnPreview() {
+    router.push({ path: '/preview/index', query: { docCode: docCode.value } });
+  }
+
+  function fnSaveMailDrafts() {
+    const data = fnBuildingCommitData();
+    loading.value = true;
+
+    saveMailDraftsApi(data)
+      .then((res) => {
+        loading.value = false;
+        if (res.code == 0) {
+          docCode.value = res.data.docCode;
+          createMessage.success('淇濆瓨鎴愬姛');
+        }
+      })
+      .catch((error) => {
+        loading.value = false;
+      });
+  }
+
+  const fnSelectUser = (e) => {
+    modelRef[e.idName] = e.selectedRowKeys;
+  };
+
+  const userLength = computed(() => {
+    return (
+      modelRef.recipients.length + modelRef.ccRecipients.length + modelRef.bccRecipients.length
+    );
+  });
+
+  interface Recipient {
+    value: string;
+    email: string;
+    title: string;
+  }
+
+  const userParticulars = computed<Recipient | null>(() => {
+    const recipientId = modelRef.recipients?.[0];
+    if (!recipientId) {
+      console.error('Recipient ID is not defined.');
+      return { value: '', email: '', title: '' };
+    }
+    // 浣跨敤 find 鏂规硶鏌ユ壘鍖归厤椤�
+    const foundItem = state.recipientsList.find((item) => item.email === recipientId);
+
+    // 杩斿洖鎵惧埌鐨勯」锛屽鏋滄湭鎵惧埌鍒欒繑鍥� null
+    return foundItem || { value: '', email: '', title: '' };
+  });
+  const fnHandleTimeZone = (e, type) => {
+    console.log('fnHandleTimeZone');
+    if (type == 'add') {
+      titleTimeZone.value = '娣诲姞';
+      typeTimeZone.value = 1;
+    } else {
+      titleTimeZone.value = '淇敼';
+      typeTimeZone.value = 2;
+    }
+    openTimeZone.value = true;
+  };
+
+  const openTimeZone = ref<boolean>(false);
+  const confirmLoading = ref<boolean>(false);
+  const titleTimeZone = ref<string>('娣诲姞');
+  const typeTimeZone = ref<number>(1);
+
+  const fnHandleTimeZoneOk = () => {
+    confirmLoading.value = true;
+    setTimeout(() => {
+      openTimeZone.value = false;
+      confirmLoading.value = false;
+    }, 2000);
+  };
+
+  function fnGetRecipientsList() {
+    emailListAPi({ key: '' }).then((body) => {
+      state.recipientsList = body.data;
+    });
+  }
+  // 閭鏍¢獙姝e垯琛ㄨ揪寮�
+  const validateEmail = (email) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
+  import { debounce } from 'lodash-es';
+  let lastFetchId = 0;
+
+  const fetchUser = debounce((value) => {
+    lastFetchId += 1;
+    const fetchId = lastFetchId;
+    state.data = [];
+    state.fetching = true;
+    emailListAPi({ key: value }).then((body) => {
+      if (fetchId !== lastFetchId) {
+        // for fetch callback order
+        return;
+      }
+
+      state.recipientsList = body.data;
+      state.fetching = false;
+    });
+  }, 300);
+
+  // 鍥炲
+  function fuGetReplyEmailData() {
+    getMailInfoApi({ docCode: router.currentRoute.value.query.docCode })
+      .then((res) => {
+        console.log(ref.data,'---3022');
+        modelRef.sender = res.data.receiver[0]
+        modelRef.recipients = [res.data.sender]
+        modelRef.subject = 'Re锛�'+ res.data.subject
+        modelRef.content = setContent(res.data)
+        // tableRowData.value = res.data;
+      })
+      .catch(() => {});
+    console.log('----------------4');
+  }
+  const setContent = (row) => {
+  const text =  `<div style=\"font-size: 12px; font-family: Arial Narrow,serif; padding: 2px 0 2px 0;\">------------------&nbsp;Original&nbsp;------------------</div>\n<div style=\"font-size: 12px; background: #efefef; padding: 8px;\">\n<div><strong>From:&nbsp;</strong>&nbsp;${row.sender} &lt;<a style=\"color: #1e7bf9; text-decoration: none;\" href=\"mailto:${row.sender}\" target=\"_blank\" rel=\"noopener noreferrer\">${row.sender}</a>&gt;</div>\n<div><strong>Send time:&nbsp;</strong>&nbsp;${row.createTime}</div>\n<div><strong>To:&nbsp;</strong>&nbsp;${row.userName} &lt;<a style=\"color: #1e7bf9; text-decoration: none;\" href=\"mailto:${row.receiver}\" target=\"_blank\" rel=\"noopener noreferrer\">${row.receiver}</a>&gt;</div>\n<div><strong>Subject:&nbsp;</strong> ${row.subject}</div>\n</div>`
+  return text + row.content
+  };
+</script>
+<style lang="less" scoped>
+  @prefix-cls: ~'@{namespace}-email';
+  .@{prefix-cls} {
+    .splitpanes__pane {
+      display: flex;
+      align-items: stretch;
+      justify-content: center;
+      // background-color: var(--component-background-color);
+    }
+  }
+</style>
diff --git a/src/views/email/HandlingEmailsOnBehalfOfOthers/components/list.vue b/src/views/email/HandlingEmailsOnBehalfOfOthers/components/list.vue
new file mode 100644
index 0000000..bf9bd5e
--- /dev/null
+++ b/src/views/email/HandlingEmailsOnBehalfOfOthers/components/list.vue
@@ -0,0 +1,220 @@
+<template>
+  <PageWrapper>
+    <div style="height: calc(100vh - 84px)">
+      <div class="head">
+        <div class="left">
+          <div class="left-box p-3">
+            <!-- 澶氶�� -->
+            <a-checkbox
+              class="icon"
+              style="margin-right: 10px"
+              v-model:checked="state.checkAll"
+              :indeterminate="state.indeterminate"
+              @change="fnCheckedChange"
+            ></a-checkbox>
+            <!--鏇存柊  -->
+            <SyncOutlined class="icon" v-show="!checked" />
+            <pageHeadLeft
+              :checked="checked"
+              :selectAllRow="selectAllRow"
+              :parentTableList="newList"
+            ></pageHeadLeft>
+          </div>
+        </div>
+
+        <div class="right p-3"
+          >鍏�<span style="padding: 0 5px">20</span>灏�
+          <a-pagination
+            v-model:current="pageCurrent"
+            v-model:page-size='page.limit'
+            simple
+            :total="page.total"
+            style="margin-left: 10px"
+            @change="handlePageChange"
+          />
+          <FilterOutlined style="margin-left: 10px" />
+          <a-popover placement="left" trigger="click">
+            <template #content>
+              <div>
+                <span>寰�鏉ラ偖浠惰仛鍚�</span>
+                <a-switch style="margin-left: 50px" v-model:checked="checked3"> </a-switch>
+              </div>
+              <a-divider style="margin: 10px" />
+              <div>
+                <span>鍒楄〃灞曠ず鍐呭</span>
+              </div>
+              <div class="p-2">
+                <a-checkbox v-model:checked="checked">閭欢鎽樿</a-checkbox>
+              </div>
+              <div class="p-2">
+                <a-checkbox v-model:checked="checked">闄勪欢</a-checkbox>
+              </div>
+              <div style="text-align: center">
+                <a-button @click="$router.push('/email/utils')">鏇村閭璁剧疆</a-button>
+              </div>
+            </template>
+            <SettingOutlined style="margin-left: 10px" />
+          </a-popover>
+          <a-switch style="margin-left: 10px" v-model:checked="checked3">
+            <template #checkedChildren><PushpinOutlined style="color: #0a6aff" /></template>
+            <template #unCheckedChildren><PushpinOutlined /></template>
+          </a-switch>
+        </div>
+      </div>
+      <div v-if="checked" class="left-bt p-3">
+        宸查�夋嫨姝ら〉闈笂鎵�鏈� 20 灏侀偖浠� 锛� 閫夋嫨鍏ㄩ儴 335 灏侀偖浠�
+      </div>
+      <div class="p-4" style="height: 90%; overflow: hidden">
+        <a-tabs v-model:activeKey="activeKey">
+          <a-tab-pane
+            v-for="item in tabsList"
+            :key="item.key"
+            :tab="`${item.label}${item.num ? '(' + item.num + ')' : ''}`"
+            style="height: 200px"
+          >
+            <Table
+              ref="tableRef"
+              :page="pageCurrent"
+              :pageList="newList"
+              @selectAll="fnSelectAll"
+              @updateSelectAll="updateSelectAll"
+            />
+          </a-tab-pane>
+        </a-tabs>
+      </div>
+    </div>
+  </PageWrapper>
+</template>
+
+<script lang="ts" setup>
+  name: 'ListPage';
+  import {
+    SyncOutlined,
+    SettingOutlined,
+    FilterOutlined,
+    PushpinOutlined,
+  } from '@ant-design/icons-vue';
+  import pageHeadLeft from '@/views/email/components/ListPage/pageHeadLeft.vue';
+  import { PageWrapper } from '@/components/Page';
+
+  import { ref, watch, defineProps, defineEmits, computed, reactive, onMounted,inject } from 'vue';
+
+  // 瀹氫箟灞炴��
+  interface Props {
+    pageList: [];
+    pageData?:any;
+  }
+  const props = defineProps<Props>();
+  const newList = ref([]);
+  const selectAllRow = ref([]);
+  watch(
+    () => props.pageList,
+    (newValue) => {
+      newList.value = newValue;
+    },
+  );
+
+  const checked = computed(() => selectAllRow.value.length > 0);
+  const pageCurrent = ref(1);
+  const tableRef = ref();
+  const state = reactive({
+    indeterminate: false,
+    checkAll: false,
+  });
+  function fnCheckedChange(e) {
+    Object.assign(state, {
+      indeterminate: false,
+    });
+    tableRef.value[0].fnSelectAll(e.target.checked);
+    checked.value = e.target.checked;
+  }
+  function updateSelectAll(data) {
+    selectAllRow.value = data.records;
+    if (!data.isAll) {
+      state.indeterminate = true;
+      state.checkAll = false;
+      if (data.records.length === 0) {
+        state.indeterminate = false;
+      }
+    } else {
+      state.indeterminate = false;
+      state.checkAll = true;
+    }
+  }
+  const tabsList = computed(() => {
+    return [
+      {
+        key: '1',
+        label: '闇�瑕佸鐞�',
+        num: 20,
+      },
+      {
+        key: '2',
+        label: '灏氭湭鍒版湡',
+        num: 0,
+      },
+      {
+        key: '3',
+        label: '宸插畬鎴�',
+        num: 0,
+      },
+    ];
+  })
+  const activeKey = ref('1');
+  const checked3 = ref(false);
+  import Table from '@/views/email/components/ListPage/table.vue';
+  onMounted(() => {
+    console.log('tableRef:', tableRef.value[0]);
+  });
+  function fnSelectAll() {
+    console.log('44444444444');
+  }
+
+  const page = computed(() => props.pageData);
+  const getDataList = inject('getDataList');
+  function handlePageChange(page, pageSize){
+    getDataList(page)
+    
+  }
+</script>
+<style scoped lang="less">
+  .head {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    width: 100%;
+    height: 60px;
+    border-bottom: 1px solid rgb(5 5 5 / 6%);
+
+    /* 澧炲姞閫夋嫨鍣ㄧ壒寮傛�� */
+    & .left {
+      width: 20%;
+
+      & .left-box {
+        display: flex;
+        align-items: center;
+        justify-content: space-flex-start;
+        width: 100%;
+        height: 100%;;
+
+        & .icon {
+          margin-right: 15px;
+          font-size: 16px;
+        }
+      }
+    }
+
+    & .right {
+      display: flex;
+      align-items: center;
+    }
+  }
+
+  .left-bt {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    padding-left: 27px;
+    background: #fffbe6;
+  }
+</style>
diff --git a/src/views/email/HandlingEmailsOnBehalfOfOthers/index.vue b/src/views/email/HandlingEmailsOnBehalfOfOthers/index.vue
new file mode 100644
index 0000000..cc217ac
--- /dev/null
+++ b/src/views/email/HandlingEmailsOnBehalfOfOthers/index.vue
@@ -0,0 +1,60 @@
+<template>
+  <div>
+    <a-spin :spinning="loading" class="p-1" style="height: 100%">
+      <PageIndex :pageList="pageList" :mailType="1" :pageData="pageData"> </PageIndex>
+    </a-spin>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  name: 'Inbox';
+  import { ref, onMounted, computed, provide } from 'vue';
+  import PageIndex from './components/list.vue';
+  import { useRoute } from 'vue-router';
+
+  const route = useRoute();
+  // 鑾峰彇褰撳墠瀹屾暣 URL
+  const routerId = computed(() => {
+    try {
+      const url = window.location.href;
+      if (!url) {
+        throw new Error('Invalid URL');
+      }
+      const basePart = url.split('?')[1];
+      console.log(basePart, 'basePart');
+      return basePart;
+    } catch (error) {
+      console.error('Error processing URL:', error);
+      // 杩斿洖榛樿鍊兼垨绌哄瓧绗︿覆
+      return '';
+    }
+  });
+  import { getHandleMailListApi } from '@/api/email/userList';
+  const pageList = ref([]);
+  const loading = ref(false);
+  const pageData = ref({
+    page: 1,
+    limit: 20,
+    total: 0,
+  });
+  function getDataList(page: 1) {
+    loading.value = true;
+
+    getHandleMailListApi({ page })
+      .then((res) => {
+        loading.value = false;
+        if (res.code == 0) {
+          pageList.value = res.data.list;
+          pageData.value.total = res.data.total;
+        }
+      })
+      .catch(() => {
+        loading.value = false;
+      });
+  }
+  provide('getDataList', getDataList);
+  onMounted(() => {
+    getDataList();
+  });
+</script>
+<style scoped lang="less"></style>
diff --git a/src/views/email/Inbox/index.vue b/src/views/email/Inbox/index.vue
new file mode 100644
index 0000000..b03eeb8
--- /dev/null
+++ b/src/views/email/Inbox/index.vue
@@ -0,0 +1,61 @@
+<template>
+  <div>
+    <a-spin :spinning="loading" class="p-1" style="height: 100%;">
+      <PageIndex :pageList="pageList" :mailType="1" :pageData="pageData" > </PageIndex>
+    </a-spin>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  name: 'Inbox';
+  import { ref, onMounted, computed, provide } from 'vue';
+  import PageIndex from '@/views/email/components/ListPage/list.vue';
+  import { useRoute } from 'vue-router';
+
+  const route = useRoute();
+  // 鑾峰彇褰撳墠瀹屾暣 URL
+  const routerId = computed(() => {
+    try {
+      const url = window.location.href;
+      if (!url) {
+        throw new Error('Invalid URL');
+      }
+      const basePart = url.split('?')[1];
+      console.log(basePart, 'basePart');
+      return basePart;
+    } catch (error) {
+      console.error('Error processing URL:', error);
+      // 杩斿洖榛樿鍊兼垨绌哄瓧绗︿覆
+      return '';
+    }
+  });
+  import { getMailListApi } from '@/api/email/userList';
+  const pageList = ref([]);
+  const loading = ref(false);
+  const pageData = ref({
+    page: 1,
+    limit: 20,
+    total: 0,
+  });
+  function getDataList(page:1) {
+    loading.value = true;
+    getMailListApi({ mail: routerId.value, mailType: 1,page })
+      .then((res) => {
+        loading.value = false;
+        if (res.code == 0) {
+          pageList.value = res.data.list;
+          pageData.value.total = res.data.total;
+        }
+      })
+      .catch(() => {
+        loading.value = false;
+      });
+  }
+
+  
+  provide('getDataList', getDataList);
+  onMounted(() => {
+    getDataList();
+  });
+</script>
+<style scoped lang="less"></style>
diff --git a/src/views/email/UnreadEmail/index.vue b/src/views/email/UnreadEmail/index.vue
new file mode 100644
index 0000000..68f9ad3
--- /dev/null
+++ b/src/views/email/UnreadEmail/index.vue
@@ -0,0 +1,59 @@
+<template>
+  <div>
+    <a-spin :spinning="loading" class="p-1" style="height: 100%;">
+    <PageIndex :pageList="pageList" :mailType='1' :pageData='pageData' ></PageIndex>
+    </a-spin>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  name: 'Inbox';
+  import { ref, onMounted, computed, provide } from 'vue';
+  import PageIndex from '@/views/email/components/ListPage/list.vue';
+  import { useRoute } from 'vue-router';
+
+  const route = useRoute();
+  // 鑾峰彇褰撳墠瀹屾暣 URL
+  const routerId = computed(() => {
+    try {
+      const url = window.location.href;
+      if (!url) {
+        throw new Error('Invalid URL');
+      }
+      const basePart = url.split('?')[1];
+      console.log(basePart, 'basePart');
+      return basePart;
+    } catch (error) {
+      console.error('Error processing URL:', error);
+      // 杩斿洖榛樿鍊兼垨绌哄瓧绗︿覆
+      return '';
+    }
+  });
+  import { getMailListApi } from '@/api/email/userList';
+import { limit } from 'packages/hooks/src/useRequest/utils/limit';
+  const pageList = ref([]);
+  const pageData = ref({
+    page: 1,
+    limit: 20,
+    total: 0,
+  })
+  const loading = ref(false);
+
+  function getDataList() {
+    getMailListApi({ mail: routerId.value,mailType:1,isNoRead:true ,limit:pageData.value.limit}).then((res) => {
+      loading.value = false;
+
+      if (res.code == 0) {
+        pageList.value = res.data.list;
+        pageData.value.total = res.data.total;
+      }
+    }).catch(() => {
+      loading.value = false;
+    });
+  }
+  provide('getDataList',getDataList);
+  onMounted(() => {
+    getDataList();
+  });
+</script>
+<style scoped lang="less"></style>
diff --git a/src/views/email/UnreadEmail/user.vue b/src/views/email/UnreadEmail/user.vue
new file mode 100644
index 0000000..584af91
--- /dev/null
+++ b/src/views/email/UnreadEmail/user.vue
@@ -0,0 +1,18 @@
+<template>
+  <PageWrapper dense contentFullHeight fixedHeight>
+<div>
+sdfdsfds
+</div>
+<div class='table-list'>
+sdffsdfsdffdsdsfdsf
+</div>
+
+    
+  </PageWrapper>
+</template>
+<script lang="ts" setup>
+name: 'UnreadEmail'
+import {PageWrapper} from '@/components/Page';
+  import { ref, watch } from 'vue';
+
+</script>
diff --git a/src/views/email/Utils/blacklist.vue b/src/views/email/Utils/blacklist.vue
new file mode 100644
index 0000000..cd379f0
--- /dev/null
+++ b/src/views/email/Utils/blacklist.vue
@@ -0,0 +1,242 @@
+<template>
+  <div class="p-2">
+    <div>
+      <vxe-toolbar>
+        <template #buttons>
+          <div style="display: flex; align-items: flex-end">
+            <span style="font-size: 1.25rem; font-weight: 600">閭鍦板潃榛戝悕鍗�</span
+            ><span style="margin-left: 5px; padding-bottom: 4px; font-size: 12px"
+              >榛戝悕鍗曚腑閭瀵瑰簲鐨勯偖浠跺皢鑷姩鏀跺彇鍒板瀮鍦鹃偖浠讹紙涓嶄細鏀跺埌鏂伴偖浠舵彁閱掞級</span
+            >
+          </div>
+        </template>
+        <template #tools>
+          <a-button type="primary" @click="showModal('mail')">娣诲姞閭鍦板潃</a-button>
+        </template>
+      </vxe-toolbar>
+      <a-input-search
+        v-model:value="searchMail"
+        placeholder="璇疯緭鍏ュ叧閿瓧鎼滅储"
+        style="width: 300px; margin-bottom: 20px"
+        @search="onSearch($event, 'mail')"
+      />
+      <a-table :columns="columns" :data-source="demo.mail" :scroll="{ y: 220 }" size="small">
+        <template #bodyCell="{ column, record }">
+          <template v-if="column.field == 'operate'">
+            <a class="ant-dropdown-link" @click="fnDelete(record)">鍒犻櫎</a>
+          </template>
+          <template v-else-if="column.field == 'mail'">
+            {{ record.blackContent }}
+          </template>
+        </template>
+      </a-table>
+    </div>
+    <div>
+      <vxe-toolbar>
+        <template #buttons>
+          <div style="display: flex; align-items: flex-end">
+            <span style="font-size: 1.25rem; font-weight: 600">鍩熷悕榛戝悕鍗�</span
+            ><span style="margin-left: 5px; padding-bottom: 4px; font-size: 12px"
+              >榛戝悕鍗曚腑鍩熷悕瀵瑰簲鐨勯偖浠跺皢鑷姩鏀跺彇鍒板瀮鍦鹃偖浠讹紙涓嶄細鏀跺埌鏂伴偖浠舵彁閱掞級</span
+            >
+          </div>
+        </template>
+        <template #tools>
+          <a-button type="primary" @click="showModal('domainName')">娣诲姞鍩熷悕</a-button>
+        </template>
+      </vxe-toolbar>
+      <a-input-search
+        v-model:value="searchDomainName"
+        placeholder="璇疯緭鍏ュ叧閿瓧鎼滅储"
+        style="width: 300px; margin-bottom: 20px"
+        @search="onSearch($event, 'domainName')"
+      />
+      <a-table
+        :columns="columnsDomainName"
+        :data-source="demo.domain"
+        :scroll="{ y: 220 }"
+        size="small"
+      >
+        <template #bodyCell="{ column, record }">
+          <template v-if="column.field == 'operate'">
+            <a class="ant-dropdown-link" @click="fnDelete(record)">鍒犻櫎</a>
+          </template>
+          <template v-else-if="column.field == 'domainName'">
+            {{ record.blackContent }}
+          </template>
+        </template>
+      </a-table>
+    </div>
+
+    <a-modal :width="600" v-model:open="open" :title="`鏂板缓${formTypeName}榛戝悕鍗昤" @ok="handleOk">
+      <a-form ref="formRef" :model="form" style="margin-top: 20px">
+        <a-form-item
+          v-if="formType === 'mail'"
+          label="閭鍦板潃"
+          name="blackContent"
+          :rules="[{ required: true, type: 'email', message: '璇疯緭鍏ラ偖绠卞湴鍧�', trigger: 'blur' }]"
+        >
+          <a-input v-model:value="form.blackContent" placeholder="璇疯緭鍏ラ偖绠卞湴鍧�" />
+        </a-form-item>
+        <a-form-item
+          v-else
+          label="鍩熷悕鍦板潃"
+          name="blackContent"
+          :rules="[{ required: true, validator: validatorDomainName }]"
+        >
+          <a-input v-model:value="form.blackContent" placeholder="璇疯緭鍏ュ煙鍚嶅湴鍧�" />
+        </a-form-item>
+      </a-form>
+    </a-modal>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  import { ref, onMounted, onUnmounted, reactive } from 'vue';
+  import { getBlackListApi, addBlackListApi, deleteBlackListApi } from '@/api/email/userList';
+  onUnmounted(() => {});
+
+  function fnGetList() {
+    getBlackListApi({}).then((res) => {
+      console.log(res);
+      demo.mail = res.data.mail;
+      demo.domain = res.data.domain;
+    });
+  }
+
+  import { useMessage } from '@/hooks/web/useMessage';
+  const { createMessage } = useMessage();
+
+  const open = ref(false);
+  const formRef = ref();
+  const columns = [
+    {
+      title: '閭鍦板潃',
+      field: 'mail',
+      minWidth: 200,
+    },
+    {
+      title: '鎿嶄綔',
+      field: 'operate',
+      width: 200,
+    },
+  ];
+  const columnsDomainName = [
+    {
+      title: '鍩熷悕鍦板潃',
+      field: 'domainName',
+      minWidth: 200,
+    },
+    {
+      title: '鎿嶄綔',
+      field: 'operate',
+      width: 200,
+    },
+  ];
+  function validatorDomainName(rule, value, callback) {
+    if (!value) {
+      callback(new Error('璇疯緭鍏ュ煙鍚嶅湴鍧�'));
+    } else {
+      if (!/[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+\.?/.test(value)) {
+        callback(new Error('鍩熷悕鍦板潃鏍煎紡涓嶆纭�!'));
+      } else {
+        callback();
+      }
+    }
+  }
+  const form = reactive({
+    blackContent: '',
+  });
+  const formTypeName = ref('閭鍦板潃');
+  const formType = ref('mail');
+  const demo = reactive({
+    mail: [],
+    domain: [],
+  });
+
+  const searchMail = ref('');
+  const searchDomainName = ref('');
+  function showModal(blackType) {
+    open.value = true;
+    if (blackType === 'mail') {
+      formTypeName.value = '閭鍦板潃';
+      formType.value = 'mail';
+    } else {
+      formTypeName.value = '鍩熷悕鍦板潃';
+      formType.value = 'domainName';
+    }
+  }
+  function handleOk() {
+    formRef.value.validate().then(() => {
+      addBlackListApi({
+        blackContent: form.blackContent,
+        blackType: formType.value === 'mail' ? false : true,
+      }).then((res) => {
+        createMessage.success('娣诲姞鎴愬姛');
+        open.value = false;
+        fnGetList();
+      });
+    });
+  }
+  function fnDelete(row) {
+    deleteBlackListApi({
+      blackId: row.blackId,
+    }).then((res) => {
+      createMessage.success('鍒犻櫎鎴愬姛');
+      fnGetList();
+    });
+  }
+function onSearch(e, type) {
+    console.log(e, type,'---3333');
+    
+    const data = {
+      search: e,
+      type: type === 'mail' ? 0 : 1,
+    };
+    if (type === 'mail') {
+      getBlackListApi(data).then((res) => {
+        demo.mail = res.data.mail;
+      });
+    } else {
+      getBlackListApi(data).then((res) => {
+        demo.domain = res.data.domain;
+      });
+    }
+  }
+
+  onMounted(() => {
+    fnGetList();
+  });
+</script>
+
+<style scoped>
+  .sortable-row-demo .drag-btn {
+    font-size: 12px;
+    cursor: move;
+  }
+
+  .sortable-row-demo .vxe-body--row.sortable-ghost,
+  .sortable-row-demo .vxe-body--row.sortable-chosen {
+    background-color: #dfecfb;
+  }
+
+  .bookmark {
+    display: inline-block;
+    position: relative;
+    width: 14px;
+    height: 18px;
+    margin-right: 5px;
+    border-radius: 2px 2px 0 0; /* Rounded top corners */
+  }
+
+  .bookmark::after {
+    content: '';
+    position: absolute;
+    right: 0;
+    bottom: 0;
+    left: 0;
+    height: 8px;
+    background-color: #fff;
+    clip-path: polygon(50% 60%, 0% 100%, 100% 100%); /* Triangle at the bottom */
+  }
+</style>
diff --git a/src/views/email/Utils/convention.vue b/src/views/email/Utils/convention.vue
new file mode 100644
index 0000000..172ea88
--- /dev/null
+++ b/src/views/email/Utils/convention.vue
@@ -0,0 +1,152 @@
+<template>
+  <div class="p-2" style="margin-left: 20px">
+    <h2 class="title" style="font-size: 20px">甯歌</h2>
+
+    <a-form style="width: 60%" :labelCol="{ span: 2 }">
+      <div>
+        <h2 class="title">璐﹀彿</h2>
+        <a-form-item label="榛樿閭">
+          <a-select class='w-200'>
+            <a-select-option v-for="(item, index) in emailList" :key="index" :value="item.email">
+              {{ item.email }}
+            </a-select-option>
+          </a-select>
+          <span style="font-size: 12px">鍦ㄧ粦瀹氬涓偖绠辩殑鎯呭喌涓嬶紝鍙戜俊鏃堕粯璁ら�夋嫨璇ラ偖绠便��</span>
+        </a-form-item>
+      </div>
+
+      <div>
+        <h2 class="title">绛惧悕</h2>
+        <a-form-item label="涓�х鍚�">
+          <div style="display: flex">
+            <a-select @change="fnSignatureChange" v-model:value="signature">
+              <a-select-option
+                v-for="(item, index) in signatureList"
+                :key="index"
+                :value="item.signId"
+              >
+                {{ item.signName }}
+              </a-select-option>
+            </a-select>
+            <PlusCircleOutlined style="margin-left: 20px" @click="showModal('add')" />
+          </div>
+        </a-form-item>
+        <a-form-item :labelCol="{ span: 2 }" label="鍐呭">
+          <div style="height: 200px; overflow: hidden; border-bottom: 1px solid #d9d9d9">
+            <TinymcePw v-model="signContent"></TinymcePw>
+          </div>
+          <div style="margin-top: 20px; text-align: right">
+            <a-button type="primary" size="small" @click="showModal('update')">缂栬緫</a-button>
+            <a-button size="small" style="margin-left: 20px">鍒犻櫎</a-button>
+          </div>
+        </a-form-item>
+      </div>
+    </a-form>
+    <a-modal :width="700" v-model:open="open" :title="`${title}涓�х鍚峘" @ok="handleOk">
+      <a-form ref="formRef" :model="form" style="margin-top: 20px">
+        <a-form-item
+          label="鍚嶇О"
+          name="signName"
+          :rules="[{ required: true, message: '璇疯緭鍏ュ悕绉�', trigger: 'blur' }]"
+        >
+          <a-input v-model:value="form.signName" placeholder="璇疯緭鍏ュ悕绉�" />
+        </a-form-item>
+        <a-form-item label="鍐呭">
+          <Tinymce v-model="form.signContent" :isElse="false" :isText="false"></Tinymce>
+        </a-form-item>
+      </a-form>
+    </a-modal>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  name: 'convention';
+  import { nextTick, onMounted, ref } from 'vue';
+  import { getAccountListApi } from '@/api/email/userList';
+  import { PlusCircleOutlined } from '@ant-design/icons-vue';
+  import { TinymcePw, Tinymce } from '@/components/Tinymce';
+  import { addSignatureApi, getSignatureApi, updateSignatureApi } from '@/api/email/userList';
+
+  interface EmailItem {
+    email: string;
+  }
+  const emailList = ref<EmailItem[]>([]);
+  const signatureList = ref<any[]>([]);
+  function getEmailSelect() {
+    getAccountListApi().then((res) => {
+      emailList.value = res.data;
+      console.log(res);
+    });
+
+    getSignatureApi({}).then((res) => {
+      signatureList.value = res.data;
+    });
+  }
+  onMounted(() => {
+    getEmailSelect();
+  });
+  const open = ref(false);
+  const signType = ref('add');
+  const form = ref<Record<string, any>>({});
+  const title = ref('鏂板缓');
+  const showModal = (type) => {
+    signType.value = type;
+    open.value = true;
+    if (type == 'add') {
+      form.value = {
+        signName: '',
+        signContent: '',
+      };
+    } else {
+      title.value = '缂栬緫';
+      nextTick(() => {
+        formRef.value.resetFields();
+        getSign(signature.value);
+      });
+    }
+  };
+  function getSign(id) {
+    const matchedItem = signatureList.value.find((item) => item.signId === id);
+    if (matchedItem) {
+      form.value = Object.assign(form.value, matchedItem);
+    }
+  }
+  import { useMessage } from '@/hooks/web/useMessage';
+  const { createMessage } = useMessage();
+  const formRef = ref();
+  const signContent = ref();
+  function fnSignatureChange(e) {
+    const matchedItem = signatureList.value.find((item) => item.signId === e);
+    if (matchedItem) {
+      signContent.value = matchedItem.signContent;
+    } else {
+      signContent.value = '';
+    }
+  }
+  const signature = ref();
+  const handleOk = (e: MouseEvent) => {
+    formRef.value
+      .validate()
+      .then(() => {
+        const data = form.value;
+        const api = signType.value == 'update' ? updateSignatureApi : addSignatureApi;
+        // const api = addSignatureApi;
+        api(data).then((res) => {
+          if (res.code === 0) {
+            createMessage.success(res.msg);
+            open.value = false;
+          }
+        });
+      })
+      .catch((e) => {
+        console.log(e);
+      });
+  };
+</script>
+<style scoped lang="less">
+  .title {
+    margin-bottom: 20px;
+    font-size: 16px;
+    font-weight: 600;
+  }
+</style>
diff --git a/src/views/email/Utils/folder.vue b/src/views/email/Utils/folder.vue
new file mode 100644
index 0000000..65e1ce5
--- /dev/null
+++ b/src/views/email/Utils/folder.vue
@@ -0,0 +1,200 @@
+<template>
+  <div class="p-2">
+    <vxe-toolbar>
+      <template #buttons>
+        <div style="display: flex; align-items: flex-end">
+          <span style="font-size: 1.25rem; font-weight: 600">鏂囦欢澶�</span
+          ><span style="margin-left: 5px; padding-bottom: 4px; font-size: 12px"
+            >绠$悊鎮ㄧ殑涓汉鏂囦欢澶�</span
+          >
+        </div>
+      </template>
+      <template #tools>
+        <a-button type="primary" @click="insertEvent">鏂板缓鏂囦欢澶�</a-button>
+      </template>
+    </vxe-toolbar>
+
+    <vxe-table
+      ref="xTable"
+      style="margin: 10px 0"
+      :data="demo.tableData"
+      @mounted="onMounted"
+      :row-config="{ keyField: 'id' }"
+      :column-config="{ resizable: true }"
+      :export-config="{}"
+      :tree-config="{ transform: true,rowField: 'rowId', parentField: 'parentRowId' }"
+      :edit-config="{ trigger: 'manual', mode: 'row' }"
+      height="600"
+    >
+      <vxe-column width="40">
+        <template #default>
+          <span class="drag-btn">
+            <HolderOutlined />
+          </span>
+        </template>
+      </vxe-column>
+      <vxe-column field="folderName" title="鏂囦欢澶瑰悕绉�" minWidth="250" tree-node :edit-render="{}">
+        <template #edit="{ row }">
+          <vxe-input
+            :ref="el => inputRefs[row.id] = el"
+            v-model="row.folderName"
+            type="text"
+            style="width: 300px"
+            @blur="fnInputHandle(row)"
+          ></vxe-input>
+        </template>
+      </vxe-column>
+      <vxe-column field="age" title="鎿嶄綔" width="250">
+        <template #default="{ row }">
+          <a style="margin-right: 10px" @click="insertRow(row)">娣诲姞瀛愭枃浠跺す</a>
+          <a style="margin-right: 10px" @click="editRowEvent(row)">缂栬緫</a>
+          <a style="margin-right: 10px" @click="fnDelete(row)">鍒犻櫎</a>
+        </template>
+      </vxe-column>
+    </vxe-table>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  import { ref, computed, onMounted, nextTick, onUnmounted, reactive } from 'vue';
+  import {
+    addFolderApi,
+    deleteFolderApi,
+    updateFolderApi,
+    getFolderApi,
+  } from '@/api/email/userList';
+
+  // 鎺掑簭
+  import { HolderOutlined } from '@ant-design/icons-vue';
+  import Sortable from 'sortablejs';
+  let sortable: any;
+  const demo = reactive({
+    showHelpTip: false,
+    tableData: [],
+  });
+  const xTable = ref();
+  const rowDrop = () => {
+    const $table = xTable.value;
+    sortable = Sortable.create($table.$el.querySelector('.body--wrapper>.vxe-table--body tbody'), {
+      handle: '.drag-btn',
+      onEnd: (sortableEvent) => {
+        const newIndex = sortableEvent.newIndex as number;
+        const oldIndex = sortableEvent.oldIndex as number;
+        const currRow: Record<string, any> = demo.tableData.splice(oldIndex, 1)[0];
+        // demo.tableData.splice(newIndex, 0, currRow);
+        updateFolderApi({
+          textId: currRow.textId,
+          textName: currRow.textName,
+          content: currRow.content,
+          sortId: newIndex,
+        })
+          .then(() => {
+            fnGetList();
+          })
+          .catch(() => {});
+      },
+    });
+  };
+
+  let initTime: any;
+  nextTick(() => {
+    // 鍔犺浇瀹屾垚涔嬪悗鍦ㄧ粦瀹氭嫋鍔ㄤ簨浠�
+    initTime = setTimeout(() => {
+      rowDrop();
+    }, 500);
+  });
+
+  onUnmounted(() => {
+    clearTimeout(initTime);
+    if (sortable) {
+      sortable.destroy();
+    }
+  });
+
+  function fnGetList() {
+    getFolderApi({}).then((res) => {
+      console.log(res);
+      demo.tableData = res.data;
+    });
+  }
+
+  const inputRefs = ref<{ [key: number]: HTMLElement | null }>({});
+  function insertEvent() {
+    const $table = xTable.value;
+    const rid = Date.now();
+    const record = {
+      folderName: `鏂版暟鎹�${rid}`,
+      id: rid,
+    };
+    $table.insert(record).then(({ row }) => $table.setEditRow(row));
+    setTimeout(() => {
+      inputRefs.value[rid].focus();
+    }, 300);
+  }
+  import { useMessage } from '@/hooks/web/useMessage';
+
+  const { createMessage } = useMessage();
+  function fnInputHandle(row) {
+    console.log(row, '----333');
+    const data = {
+      folderName: row.folderName,
+      parentRowId: row.rowId,
+    };
+    addFolderApi(data).then((res) => {
+      if (res.code == 0) {
+        createMessage.success('娣诲姞鎴愬姛');
+        fnGetList();
+      } else {
+        createMessage.error(res.msg);
+      }
+    });
+  }
+  async function insertRow(row) {
+    const $table = xTable.value;
+    const rid = Date.now();
+    const record = {
+      folderName: `鏂版暟鎹�${rid}`,
+      id: rid,
+      parentRowId: row.rowId, // 闇�瑕佹寚瀹氱埗鑺傜偣锛岃嚜鍔ㄦ彃鍏ヨ鑺傜偣涓�
+    };
+    const { row: newRow } = await $table.insert(record);
+    console.log(row,'99999993');
+    
+    await $table.setTreeExpand(row, true); // 灏嗙埗鑺傜偣灞曞紑
+    await $table.setEditRow(newRow); // 鎻掑叆瀛愯妭鐐�
+  }
+  function fnDelete(row) {
+    deleteFolderApi({ folderId: row.folderId })
+      .then((res) => {
+        if (res.code == 0) {
+          fnGetList();
+          createMessage.success(res.msg);
+        }
+      })
+      .catch((err) => {
+        // createMessage.error(err);
+      });
+  }
+
+  function editRowEvent(row) {
+    const $table = xTable.value;
+    console.log(row,'---30494');
+    
+    $table.setEditRow(row);
+  }
+  onMounted(() => {
+    fnGetList();
+  });
+</script>
+
+<style scoped>
+  .sortable-row-demo .drag-btn {
+    font-size: 12px;
+    cursor: move;
+  }
+
+  .sortable-row-demo .vxe-body--row.sortable-ghost,
+  .sortable-row-demo .vxe-body--row.sortable-chosen {
+    background-color: #dfecfb;
+  }
+</style>
diff --git a/src/views/email/Utils/index.vue b/src/views/email/Utils/index.vue
new file mode 100644
index 0000000..ed65b65
--- /dev/null
+++ b/src/views/email/Utils/index.vue
@@ -0,0 +1,31 @@
+<template>
+  <div class="p-2">
+    <a-tabs v-model:activeKey="activeKey">
+      <a-tab-pane key="1" tab="甯歌">
+        <Convention v-if="activeKey === '1'"></Convention>
+      </a-tab-pane>
+      <a-tab-pane key="2" tab="閭绠$悊" force-render>
+        <MailboxManagement v-if="activeKey === '2'"></MailboxManagement>
+      </a-tab-pane>
+      <a-tab-pane key="3" tab="蹇�熸枃鏈�"
+        ><QuickText v-if="activeKey === '3'"></QuickText
+      ></a-tab-pane>
+      <a-tab-pane key="4" tab="鏂囦欢澶�"><Folder v-if="activeKey === '4'" /></a-tab-pane>
+      <a-tab-pane key="5" tab="鏍囩"><Label v-if="activeKey==='5'" /></a-tab-pane>
+      <a-tab-pane key="6" tab="榛戝悕鍗�"><Blacklist v-if="activeKey === '6'" /></a-tab-pane>
+    </a-tabs>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  name: 'Utils';
+  import { ref } from 'vue';
+  const activeKey = ref('1');
+  import MailboxManagement from './mailboxManagement.vue';
+  import Convention from './convention.vue';
+  import QuickText from './quickText.vue';
+  import Folder from './folder.vue';
+import Label from './label.vue';
+  import Blacklist from './blacklist.vue'
+</script>
+<style scoped lang="less"></style>
diff --git a/src/views/email/Utils/label.vue b/src/views/email/Utils/label.vue
new file mode 100644
index 0000000..e4039ed
--- /dev/null
+++ b/src/views/email/Utils/label.vue
@@ -0,0 +1,260 @@
+<template>
+  <div class="p-2">
+    <vxe-toolbar>
+      <template #buttons>
+        <div style="display: flex; align-items: flex-end">
+          <span style="font-size: 1.25rem; font-weight: 600">鎴戠殑鏍囩</span
+          ><span style="margin-left: 5px; padding-bottom: 4px; font-size: 12px"
+            >绠$悊鎮ㄧ殑鎵�鏈変釜浜烘爣绛�</span
+          >
+        </div>
+      </template>
+      <template #tools>
+        <a-button type="primary" @click="showModal('add')">鏂板缓涓汉鏍囩</a-button>
+      </template>
+    </vxe-toolbar>
+
+    <vxe-table
+      ref="xTable"
+      style="margin: 10px 0"
+      :data="demo.tableData"
+      @mounted="onMounted"
+      height="600"
+    >
+      <vxe-column width="40">
+        <template #default>
+          <span class="drag-btn">
+            <HolderOutlined />
+          </span>
+        </template>
+      </vxe-column>
+      <vxe-column field="TagName" title="鏂囦欢澶瑰悕绉�" minWidth="250">
+        <template #default="{ row }">
+          <span class="my-d-f">
+            <div
+              v-if="row.systemFlag"
+              class="bookmark"
+              :style="{ backgroundColor: row.tagColor }"
+            ></div>
+            <div v-else>
+              <ColorPicker
+                v-model="row.tagColor"
+                :type="2"
+                @change="fnRowColorChange($event, row)"
+              ></ColorPicker>
+            </div>
+            <a-tag class="ml-5" :color="row.tagColor">{{ row.tagName }}</a-tag>
+          </span>
+        </template>
+      </vxe-column>
+      <vxe-column field="age" title="鎿嶄綔" width="150">
+        <template #default="{ row }">
+          <a style="margin-right: 10px" @click="showModal('edit', row)">缂栬緫</a>
+          <a style="margin-right: 10px" @click="fnDeleteRow(row)">鍒犻櫎</a>
+        </template>
+      </vxe-column>
+    </vxe-table>
+    <a-modal :width="300" v-model:open="open" :title="`${title}涓汉鏍囩`" @ok="handleOk">
+      <a-form ref="formRef" :model="form" style="margin-top: 20px">
+        <a-form-item label="鏍囩棰滆壊" name="tagColor">
+          <ColorPicker v-model="form.tagColor" :type="2"></ColorPicker>
+        </a-form-item>
+        <a-form-item
+          label="鏍囩鍚嶇О"
+          name="tagName"
+          :rules="[{ required: true, message: '璇疯緭鍏ユ爣绛惧悕绉�', trigger: 'blur' }]"
+        >
+          <a-input v-model:value="form.tagName" placeholder="璇疯緭鍏ユ爣绛惧悕绉�" />
+        </a-form-item>
+      </a-form>
+    </a-modal>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  import { ref, computed, onMounted, nextTick, onUnmounted, reactive } from 'vue';
+  import ColorPicker from '@/components/ColorPicker/index.vue';
+  import { addTagApi, deleteTagApi, updateTagApi, getTagApi } from '@/api/email/userList';
+
+  // 鎺掑簭
+  import { HolderOutlined } from '@ant-design/icons-vue';
+  import Sortable from 'sortablejs';
+  let sortable: any;
+  const demo = reactive({
+    showHelpTip: false,
+    tableData: [],
+  });
+  const xTable = ref();
+  const rowDrop = () => {
+    const $table = xTable.value;
+    sortable = Sortable.create($table.$el.querySelector('.body--wrapper>.vxe-table--body tbody'), {
+      handle: '.drag-btn',
+      onEnd: (sortableEvent) => {
+        const newIndex = sortableEvent.newIndex as number;
+        const oldIndex = sortableEvent.oldIndex as number;
+        const currRow: Record<string, any> = demo.tableData.splice(oldIndex, 1)[0];
+        // demo.tableData.splice(newIndex, 0, currRow);
+        updateTagApi({
+          tagColor: currRow.tagColor,
+          tagName: currRow.tagName,
+          sortId: newIndex,
+          tagId: currRow.tagId,
+        })
+          .then(() => {
+            fnGetList();
+          })
+          .catch(() => {});
+      },
+    });
+  };
+
+  let initTime: any;
+  nextTick(() => {
+    // 鍔犺浇瀹屾垚涔嬪悗鍦ㄧ粦瀹氭嫋鍔ㄤ簨浠�
+    initTime = setTimeout(() => {
+      rowDrop();
+    }, 500);
+  });
+
+  onUnmounted(() => {
+    clearTimeout(initTime);
+    if (sortable) {
+      sortable.destroy();
+    }
+  });
+
+  function fnGetList() {
+    getTagApi({}).then((res) => {
+      console.log(res);
+      demo.tableData = res.data;
+    });
+  }
+
+  import { useMessage } from '@/hooks/web/useMessage';
+
+  const { createMessage } = useMessage();
+
+  const open = ref(false);
+  const formRef = ref();
+
+  interface formType {
+    tagColor: string;
+    tagName: string;
+    tagType: number;
+    systemFlag: boolean;
+    tagId?: number;
+  }
+
+  const defaultForm: formType = {
+    tagColor: '#000000',
+    tagName: '',
+    tagType: 1,
+    systemFlag: false,
+  };
+
+  const form = ref<formType>({ ...defaultForm });
+
+  const title = ref('鏂板缓');
+  const signType = ref('add');
+
+  const showModal = (type: string, row) => {
+    signType.value = type;
+    open.value = true;
+
+    if (type == 'add') {
+      form.value = { ...defaultForm };
+    } else {
+      title.value = '缂栬緫';
+      nextTick(() => {
+        formRef.value.resetFields();
+        form.value = {
+          tagColor: row.tagColor,
+          tagName: row.tagName,
+          tagType: row.tagType,
+          systemFlag: row.systemFlag,
+          tagId: row.tagId,
+        };
+      });
+    }
+  };
+
+  function fnDeleteRow(row) {
+    deleteTagApi({ tagId: row.tagId }).then((res) => {
+      if (res.code === 0) {
+        createMessage.success(res.msg);
+        fnGetList();
+      }
+    });
+  }
+
+  function handleOk() {
+    nextTick(() => {
+      formRef.value
+        .validate()
+        .then(() => {
+          const api = signType.value == 'add' ? addTagApi : updateTagApi;
+          api(form.value).then((res) => {
+            if (res.code === 0) {
+              createMessage.success(res.msg);
+              fnGetList();
+              open.value = false;
+            }
+          });
+        })
+        .catch(() => {});
+    });
+  }
+
+  function fnRowColorChange(color, row) {
+    console.log(color, row);
+    const data = {
+      tagColor: row.tagColor,
+      tagName: row.tagName,
+      tagType: row.tagType,
+      systemFlag: row.systemFlag,
+      tagId: row.tagId,
+    };
+    updateTagApi(data).then((res) => {
+      if (res.code === 0) {
+        createMessage.success(res.msg);
+        fnGetList();
+      }
+    });
+  }
+
+  onMounted(() => {
+    fnGetList();
+  });
+</script>
+
+<style scoped>
+  .sortable-row-demo .drag-btn {
+    font-size: 12px;
+    cursor: move;
+  }
+
+  .sortable-row-demo .vxe-body--row.sortable-ghost,
+  .sortable-row-demo .vxe-body--row.sortable-chosen {
+    background-color: #dfecfb;
+  }
+
+  .bookmark {
+    display: inline-block;
+    position: relative;
+    width: 14px;
+    height: 18px;
+    margin-right: 5px;
+    border-radius: 2px 2px 0 0; /* Rounded top corners */
+  }
+
+  .bookmark::after {
+    content: '';
+    position: absolute;
+    right: 0;
+    bottom: 0;
+    left: 0;
+    height: 8px;
+    background-color: #fff;
+    clip-path: polygon(50% 60%, 0% 100%, 100% 100%); /* Triangle at the bottom */
+  }
+</style>
diff --git a/src/views/email/Utils/mailboxManagement.vue b/src/views/email/Utils/mailboxManagement.vue
new file mode 100644
index 0000000..2809d78
--- /dev/null
+++ b/src/views/email/Utils/mailboxManagement.vue
@@ -0,0 +1,689 @@
+<template>
+  <div class="p-2">
+    <vxe-toolbar>
+      <template #buttons>
+        <span style="font-size: 1.25rem; font-weight: 600">涓汉閭缁戝畾</span>
+      </template>
+      <template #tools>
+        <a-button style="margin-right: 20px" @click="fnCheckAll">妫�鏌ュ叏閮ㄩ偖绠�</a-button>
+        <a-button type="primary" @click="openAccount('add', '')">鏂板缓閭</a-button>
+      </template>
+    </vxe-toolbar>
+    <vxe-table
+      ref="xTable"
+      id="key"
+      style="margin: 10px 0"
+      :data="demo.tableData"
+      min-height="40px"
+      row-key
+      keep-source
+      :filter-config="{ showIcon: false }"
+      :row-config="{ isHover: true }"
+      :column-config="{ resizable: true }"
+    >
+      <vxe-column width="60">
+        <template #default>
+          <span class="drag-btn">
+            <HolderOutlined />
+          </span>
+        </template>
+      </vxe-column>
+      <vxe-column show-overflow field="email" title="閭璐﹀彿" min-width="250">
+        <template #default="{ row }">
+          <!-- <HolderOutlined /> -->
+          <span style="margin-left: 10px; color: #3081fe; font-weight: 500">{{ row.email }}</span>
+        </template>
+      </vxe-column>
+      <vxe-column show-overflow field="companyName" title="鏄剧ず鍚嶇О" min-width="250">
+        <template #default="{ row }">
+          <span style="color: #999">{{ row.companyName }}</span>
+        </template>
+      </vxe-column>
+      <vxe-column show-overflow field="status" title="閭鐘舵��" min-width="250">
+        <template #default="{ row }">
+          <div v-if="!isCheckAll">
+            <a-tag color="orange" v-if="row.status === '姝e父'">姝e父</a-tag>
+            <a-tag color="red" v-else>寮傚父</a-tag>
+          </div>
+          <div v-else>
+            <a-spin :indicator="indicator" />
+          </div>
+        </template>
+      </vxe-column>
+      <vxe-column show-overflow field="action" title="鎿嶄綔" min-width="250">
+        <template #default="{ row }">
+          <a style="margin-right: 10px" @click="fnOpenAlias(row)">娣诲姞鍒悕閭</a>
+          <a style="margin-right: 10px" @click="openIsEmailValid(row)">妫�娴�</a>
+          <a style="margin-right: 10px" @click="openAccount('update', row.email)">淇敼</a>
+          <a style="margin-right: 10px" @click="openDelete(row)">瑙g粦</a>
+        </template>
+      </vxe-column>
+    </vxe-table>
+    <a-modal
+      :bodyStyle="{ maxHeight: '70vh', overflow: 'auto' }"
+      v-model:open="open"
+      :title="`${title}閭`"
+      :confirmLoading="loading"
+      @ok="fnHandleOk"
+    >
+      <div class="p-2">
+        <a-form
+          style="margin-top: 20px"
+          layout="vertical"
+          :model="formData"
+          v-bind="{ span: 8 }"
+          ref="formRef"
+          :rules="rules"
+        >
+          <a-form-item v-if="isShow" label="鏀跺彂浠舵湇鍔″櫒楠岃瘉">
+            <a-select
+              ref="select"
+              v-model:value="isCustom"
+              style="width: 120px"
+              @change="fnHandleChange"
+            >
+              <a-select-option key="onCustom" value="onCustom"> 涓庨偖绠辩浉鍚� </a-select-option>
+              <a-select-option key="custom" value="custom"> 鑷畾涔� </a-select-option>
+            </a-select>
+          </a-form-item>
+          <a-form-item name="email">
+            <template v-slot:label>
+              閭璐﹀彿
+              <a-tooltip placement="right">
+                <template #title>
+                  <span>宸ヤ綔閭鏄偍澶勭悊鍏徃浜嬪姟鏃舵墍浣跨敤鐨勫姙鍏偖绠� 寤鸿鎮ㄤ笓鐢ㄤ簬鍔炲叕鐩殑</span>
+                </template>
+                <ExclamationCircleOutlined style="margin-left: 5px" />
+              </a-tooltip>
+            </template>
+            <a-input :disabled="typeAccount === 2" v-model:value="formData.email" />
+          </a-form-item>
+          <a-form-item v-if="isCustom == 'custom'" name="password" label="閭瀵嗙爜">
+            <a-input-password
+              type="password"
+              v-model:value="formData.password"
+              placeholder="杈撳叆閭瀵嗙爜鎴栬�呮巿鏉冪爜"
+            />
+          </a-form-item>
+          <a-form-item v-if="isShow" name="receiveProtocol" label="鍗忚绫诲瀷">
+            <a-radio-group v-model:value="formData.receiveProtocol" name="radioGroup">
+              <a-radio value="imap">IMAP</a-radio>
+              <a-radio value="pop3">POP3(涓嶆帹鑽�,鏃犳硶鍚屾鍙戜欢)</a-radio>
+              <!-- <a-radio value="exchange">Exchange</a-radio> -->
+            </a-radio-group>
+            <div style="color: red; font-size: 12px"
+              >鐢变簬exchange鍗忚鏀寔闂锛岀綉鏄撻偖绠辨帹鑽愪娇鐢↖MAP鍗忚銆�
+              濡傞渶璋冩暣鍗忚绫诲瀷锛岃鑱旂郴灏忔弧瀹㈡湇</div
+            >
+          </a-form-item>
+          <!-- <a-form-item v-if="formData.receiveProtocol === 'exchange'" :name="['user', 'email']" label="Exchange鏈嶅姟鍣�" :rules="[{ type: 'email' }]">
+          <a-row style="display: flex; align-items: center">
+            <a-col :span="18">
+              <a-input v-model:value="formData.email" placeholder="Exchange鏈嶅姟鍣�"
+            /></a-col>
+            <a-col :span="3">
+              <a-checkbox v-model:value="formData.email">SSL</a-checkbox>
+            </a-col>
+          </a-row>
+        </a-form-item>
+        <a-form-item v-if="formData.receiveProtocol === 'exchange'" :name="['user', 'email']" label="鍩�" :rules="[{ type: 'email' }]">
+          <a-row style="display: flex; align-items: center">
+            <a-col :span="18">
+              <a-input v-model:value="formData.email" placeholder="Exchange鏈嶅姟鍣�"
+            /></a-col>
+            <a-col :span="3">
+              <a-checkbox v-model:value="formData.email">SSL</a-checkbox>
+            </a-col>
+          </a-row>
+        </a-form-item> -->
+          <a-form-item v-if="isShow" name="receiveHost" label="鏀堕偖浠舵湇鍔″櫒">
+            <a-row style="display: flex; align-items: center">
+              <a-col :span="12">
+                <a-input v-model:value="formData.receiveHost" placeholder="鏀堕偖浠舵湇鍔″櫒" />
+              </a-col>
+              <a-col :span="1" style="margin-right: 10px; margin-left: 5px">:</a-col>
+              <a-col :span="6" style="margin-right: 10px">
+                <a-form-item-rest
+                  ><a-input width="50px" v-model:value="formData.receivePort" placeholder="绔彛"
+                /></a-form-item-rest>
+              </a-col>
+              <a-col :span="3">
+                <a-form-item-rest>
+                  <a-checkbox v-model:checked="formData.receiveSSL"
+                    >SSL</a-checkbox
+                  ></a-form-item-rest
+                >
+              </a-col>
+            </a-row>
+          </a-form-item>
+          <a-form-item
+            style="margin-top: 22px"
+            v-if="isCustom == 'custom'"
+            name="receiveEmail"
+            label="鏀朵欢璐﹀彿"
+          >
+            <a-input v-model:value="formData.receiveEmail" />
+          </a-form-item>
+          <a-form-item v-if="isCustom == 'custom'" name="receivePassword" label="鏀朵欢瀵嗙爜">
+            <a-input v-model:value="formData.receivePassword" />
+          </a-form-item>
+          <a-form-item v-if="isShow" name="smtpHost" label="鍙戦偖浠舵湇鍔″櫒">
+            <a-row style="display: flex; align-items: center">
+              <a-col :span="12">
+                <a-input v-model:value="formData.smtpHost" placeholder="鍙戦偖浠舵湇鍔″櫒" />
+              </a-col>
+              <a-col :span="1" style="margin-right: 10px; margin-left: 5px">:</a-col>
+              <a-col :span="6" style="margin-right: 10px">
+                <a-form-item-rest
+                  ><a-input width="50px" v-model:value="formData.smtpPort" placeholder="绔彛"
+                /></a-form-item-rest>
+              </a-col>
+              <a-col :span="3">
+                <a-form-item-rest
+                  ><a-checkbox v-model:checked="formData.smtpSSL">SSL</a-checkbox></a-form-item-rest
+                >
+              </a-col>
+            </a-row>
+          </a-form-item>
+          <a-form-item
+            style="margin-top: 22px"
+            v-if="isCustom == 'custom'"
+            name="smtpEmail"
+            label="鍙戜欢璐﹀彿"
+          >
+            <a-input v-model:value="formData.smtpEmail" />
+          </a-form-item>
+          <a-form-item v-if="isCustom == 'custom'" name="smtpPassword" label="鍙戜欢瀵嗙爜">
+            <a-input v-model:value="formData.smtpPassword" />
+          </a-form-item>
+          <a-form-item v-if="isShow" name="proxyFlag">
+            <template v-slot:label>
+              <a-tooltip placement="right">
+                <template #title>
+                  <span>寮�鍚悗缃戠粶鎻愰��</span>
+                </template>
+                <QuestionCircleOutlined style="margin-right: 5px" />
+              </a-tooltip>
+              鑷畾涔変唬鐞�
+            </template>
+            <a-radio-group v-model:value="formData.proxyFlag" name="radioGroup">
+              <a-radio :value="true">寮�鍚�</a-radio>
+              <a-radio :value="false">鍏抽棴</a-radio>
+            </a-radio-group>
+          </a-form-item>
+          <a-form-item v-if="isShow" name="biSyncFlag">
+            <template v-slot:label>
+              <a-tooltip placement="right">
+                <template #title>
+                  <span
+                    >寮�鍚悗锛屽浜庢柊缁戝畾鐨勯偖绠憋紝鍏ㄩ噺鍚屾鏂囦欢澶瑰強鏂囦欢澶瑰唴鐨勯偖浠躲�傚浜庡凡缁忕粦瀹氱殑閭锛屽叏閲忓悓姝ユ枃浠跺す鍙婃枃浠跺す鏂版敹鍙栫殑閭欢锛屽巻鍙查偖浠朵笉绉诲姩</span
+                  >
+                </template>
+                <QuestionCircleOutlined style="margin-right: 5px" />
+              </a-tooltip>
+              鍚屾鏂囦欢澶�
+            </template>
+            <a-radio-group v-model:value="formData.biSyncFlag" name="radioGroup">
+              <a-radio :value="true">寮�鍚�</a-radio>
+              <a-radio :value="false">鍏抽棴</a-radio>
+            </a-radio-group>
+          </a-form-item>
+        </a-form>
+        <a @click="fnIsShow" v-if="!isShow"> 鎵嬪姩閰嶇疆</a>
+        <a @click="fnIsShow" v-else> 鏀惰捣鎵嬪姩閰嶇疆</a>
+
+        <a-divider style="margin-top: 50px" />
+        <div style="font-size: 18px">甯姪鏂囨。</div>
+        <div
+          ><a href="https://www.yuque.com/help.xiaoman/qwwqei/vkr8p7" target="_blank" rel="noopener"
+            >1銆佹煡鐪嬬粦瀹氶偖绠卞け璐ョ殑甯歌鍘熷洜鍙婅В鍐虫柟妗�</a
+          ></div
+        >
+        <div
+          ><a href="https://www.yuque.com/help.xiaoman/qwwqei/sl9xuk" target="_blank" rel="noopener"
+            >2銆佷簡瑙e父瑙佸嚑绫婚偖绠辩殑鍏蜂綋缁戝畾鏂规硶</a
+          ></div
+        ></div
+      >
+    </a-modal>
+
+    <a-modal
+      v-model:open="openDrawerDetail"
+      :destroyOnClose="true"
+      title="纭瑙g粦"
+      :loading="loading"
+      @cancel="fnHandleDetailCancel"
+    >
+      <div style="padding: 20px 20px 20px 0; color: #000; font-size: 18px">
+        瑙g粦<span style="padding: 0 5px"> {{ deleteEmail }} </span>鍚�
+      </div>
+      <ul>
+        <li v-for="(warning, index) in removalWarnings" :key="index">
+          <span class="bullet">鈥�</span> {{ warning }}
+        </li>
+      </ul>
+      <div style="margin-top: 20px">
+        鍥犵粦瀹氬紓甯搁渶璋冩暣锛屽彲鍦ㄩ偖绠便�屼慨鏀广�嶅鏇存敼 濡備慨鏀规椂閬囧埌鍥伴毦锛屽彲鑱旂郴瀹㈡湇
+      </div>
+      <template #footer>
+        <a-button @click="fnHandleDetailCancel">鍙栨秷</a-button>
+
+        <a-button
+          type="primary"
+          danger
+          @click="fnHandleDetailOk"
+          :disabled="countdown !== 0"
+          :loading="loading"
+          >瑙g粦<span v-show="countdown > 0"> ({{ countdown }}) </span></a-button
+        >
+      </template>
+    </a-modal>
+    <a-modal
+      v-model:open="openDrawerIsEmailValid"
+      :destroyOnClose="true"
+      title="閭妫�娴�"
+      v-model:value="isEmailValid"
+    >
+      <div style="margin-top: 20px; margin-right: 10%" class="p-4">
+        <a-form labelAlign="right" :labelCol="{ span: 8 }">
+          <a-form-item label="閭">
+            {{ isEmailValid.email }}
+          </a-form-item>
+          <a-form-item label="閭瀵嗙爜">
+            <div class="form-item">
+              <div class="left" :class="checkStatus ? 'isColor' : 'isRed'"> ********</div>
+              <div class="right">
+                <a-spin v-if="!isCheck" :indicator="indicator" />
+                <CheckCircleOutlined v-else-if="checkStatus" class="isColor" />
+                <CloseCircleOutlined v-else class="isRed" />
+              </div>
+            </div>
+          </a-form-item>
+          <a-form-item label="鍗忚绫诲瀷">
+            {{ isEmailValid.email }}
+          </a-form-item>
+          <a-form-item label="鏀堕偖浠舵湇鍔″櫒">
+            <div class="form-item">
+              <div class="left" :class="checkStatus ? 'isColor' : 'isRed'">
+                {{ isEmailValid.receiveHost }}
+              </div>
+              <div class="right"
+                ><a-spin v-if="!isCheck" :indicator="indicator" /><CheckCircleOutlined
+                  v-else-if="checkStatus"
+                  class="isColor"
+                />
+                <CloseCircleOutlined v-else class="isRed" />
+              </div> </div
+          ></a-form-item>
+          <a-form-item label="鍙戦偖浠舵湇鍔″櫒">
+            <div class="form-item">
+              <div class="left" :class="checkStatus ? 'isColor' : 'isRed'">
+                {{ isEmailValid.smtpHost }}
+              </div>
+              <div class="right"
+                ><a-spin v-if="!isCheck" :indicator="indicator" />
+                <CheckCircleOutlined v-else-if="checkStatus" class="isColor" />
+                <CloseCircleOutlined v-else class="isRed" />
+              </div> </div
+          ></a-form-item>
+          <a-form-item label="鑷畾涔変唬鐞�">
+            <span style="margin-right: 5px"> {{ isEmailValid.proxyFlag ? '寮�鍚�' : '鍏抽棴' }} </span>
+            <a-tooltip placement="right">
+              <template #title>
+                <span>寮�鍚悗缃戠粶鎻愰��</span>
+              </template>
+              <QuestionCircleOutlined style="margin-right: 5px" />
+            </a-tooltip>
+          </a-form-item>
+          <a-form-item label="鍚屾鏂囦欢澶�">
+            <span style="margin-right: 5px"> {{ isEmailValid.biSyncFlag ? '寮�鍚�' : '鍏抽棴' }} </span>
+            <a-tooltip placement="right">
+              <template #title>
+                <span
+                  >寮�鍚悗锛屽浜庢柊缁戝畾鐨勯偖绠憋紝鍏ㄩ噺鍚屾鏂囦欢澶瑰強鏂囦欢澶瑰唴鐨勯偖浠躲��<br />瀵逛簬宸茬粡缁戝畾鐨勯偖绠憋紝鍏ㄩ噺鍚屾鏂囦欢澶瑰強鏂囦欢澶规柊鏀跺彇鐨勯偖浠讹紝鍘嗗彶閭欢涓嶇Щ鍔�</span
+                >
+              </template>
+              <QuestionCircleOutlined style="margin-right: 5px" />
+            </a-tooltip>
+          </a-form-item>
+        </a-form>
+      </div>
+      <template #footer>
+        <div style="text-align: center">
+          <a-button type="primary" @click="openIsEmailValid(isEmailValid)">閲嶆柊妫�娴�</a-button>
+        </div>
+      </template>
+    </a-modal>
+    <a-modal
+      v-model:open="openAlias"
+      :title="`${titleAlias}娣诲姞鍒悕閭`"
+      :confirmLoading="loading"
+      @ok="fnHandleAliasOk"
+    >
+      <a-form
+        ref="aliasRef"
+        style="margin-top: 20px"
+        layout="vertical"
+        :model="aliasFormData"
+        v-bind="{ span: 8 }"
+      >
+        <a-form-item
+          style="margin-top: 22px"
+          name="aliasEmailName"
+          label="鍒悕閭"
+          :rules="[{ required: true, message: '璇疯緭鍏ュ埆鍚嶉偖绠�', trigger: 'blur' }]"
+        >
+          <a-input v-model:value="aliasFormData.aliasEmailName" />
+        </a-form-item>
+      </a-form>
+    </a-modal>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  name: 'mailboxManagement';
+  import {
+    HolderOutlined,
+    ExclamationCircleOutlined,
+    QuestionCircleOutlined,
+    CheckCircleOutlined,
+    CloseCircleOutlined,
+  } from '@ant-design/icons-vue';
+  import { ref, reactive, onUnmounted } from 'vue';
+  import {
+    addAccountApi,
+    getAccountApi,
+    updateAccountApi,
+    deleteAccountApi,
+    getAccountListApi,
+    isEmailValidApi,
+  } from '@/api/email/userList';
+  const loading = ref(false);
+  import Sortable from 'sortablejs';
+  let sortable: any;
+  const demo = reactive({
+    showHelpTip: false,
+    tableData: [],
+  });
+  const xTable = ref();
+  const rowDrop = () => {
+    const $table = xTable.value;
+    sortable = Sortable.create($table.$el.querySelector('.body--wrapper>.vxe-table--body tbody'), {
+      handle: '.drag-btn',
+      onEnd: (sortableEvent) => {
+        const newIndex = sortableEvent.newIndex as number;
+        const oldIndex = sortableEvent.oldIndex as number;
+        const currRow = demo.tableData.splice(oldIndex, 1)[0];
+        demo.tableData.splice(newIndex, 0, currRow);
+      },
+    });
+  };
+
+  let initTime: any;
+  nextTick(() => {
+    // 鍔犺浇瀹屾垚涔嬪悗鍦ㄧ粦瀹氭嫋鍔ㄤ簨浠�
+    initTime = setTimeout(() => {
+      rowDrop();
+    }, 500);
+  });
+
+  onUnmounted(() => {
+    clearTimeout(initTime);
+    if (sortable) {
+      sortable.destroy();
+    }
+  });
+
+  function fnMailList() {
+    getAccountListApi()
+      .then((res) => {
+        if (res.code == 0) {
+          demo.tableData = res.data;
+        }
+        console.log(res);
+      })
+      .catch((err) => {
+        console.log(err);
+        demo.tableData = [];
+      });
+  }
+  fnMailList();
+  const defaultFormData = {
+    email: '',
+    password: '',
+    aliasEmail: '',
+    biSyncFlag: false,
+    proxyFlag: true,
+    receiveProtocol: 'imap',
+    receiveSSL: false,
+    receivePort: '',
+    receiveHost: '',
+    smtpSSL: false,
+    smtpPort: '',
+    smtpHost: '',
+    invalid: '',
+    mailType: 0,
+  };
+  const formData = ref<Record<string, any>>(defaultFormData);
+  const isCustom = ref('onCustom');
+  function fnHandleChange(e) {
+    console.log(e, 'iririririr');
+
+    isCustom.value = e;
+  }
+  const checkReceivePort = async (value) => {
+    console.log(formData.value.receivePort, '=-3--3', value);
+    if (value === '') {
+      return Promise.reject('璇疯緭鍏ユ敹浠舵湇鍔″櫒');
+    }
+    if (!formData.value.receivePort) {
+      return Promise.reject('璇疯緭鍏ユ敹浠舵湇鍔″櫒銆佺鍙�');
+    }
+    return Promise.resolve();
+  };
+  const checkSmtpHost = async (value) => {
+    if (value === '') {
+      return Promise.reject('璇疯緭鍏ユ敹浠舵湇鍔″櫒');
+    }
+    if (!formData.value.smtpHost) {
+      return Promise.reject('璇疯緭鍏ユ敹浠舵湇鍔″櫒銆佺鍙�');
+    }
+    return Promise.resolve();
+  };
+  const rules = ref({
+    email: [
+      { required: true, message: '璇疯緭鍏ラ偖绠卞湴鍧�', trigger: 'blur' },
+      { type: 'email', message: '璇疯緭鍏ユ纭殑閭鍦板潃', trigger: ['blur', 'change'] },
+    ],
+    password: [{ required: true, message: '璇疯緭鍏ュ瘑鐮�', trigger: 'blur' }],
+    receiveHost: [{ required: true, validator: checkReceivePort }],
+    smtpHost: [{ required: true, validator: checkSmtpHost }],
+    smtpPassword: [{ required: true, message: '璇疯緭鍏ュ彂浠跺瘑鐮�', trigger: 'blur' }],
+    smtpEmail: [{ required: true, message: '璇疯緭鍏ュ彂浠惰处鍙�', trigger: 'blur' }],
+    receiveEmail: [{ required: true, message: '璇疯緭鍏ユ敹浠跺瘑鐮�', trigger: 'blur' }],
+    receivePassword: [{ required: true, message: '璇疯緭鍏ユ敹浠惰处鍙�', trigger: 'blur' }],
+  });
+  import { useMessage } from '@/hooks/web/useMessage';
+  const { createMessage } = useMessage();
+  const formRef = ref();
+
+  const open = ref(false);
+  const fnHandleOk = () => {
+    formRef.value.validate().then(() => {
+      const data = formData.value;
+      if (isShow.value == true) {
+        data.mailType = isCustom.value === 'onCustom' ? 1 : 2;
+      }
+      const api = typeAccount.value === 2 ? updateAccountApi : addAccountApi;
+      loading.value = true;
+      api(data)
+        .then((res) => {
+          if (res.code === 0) {
+            createMessage.success(res.msg);
+          }
+          loading.value = false;
+          fnMailList();
+        })
+        .catch((err) => {
+          loading.value = false;
+        });
+      open.value = false;
+    });
+  };
+
+  const isShow = ref(false);
+  function fnIsShow() {
+    isShow.value = !isShow.value;
+  }
+  const title = ref('娣诲姞');
+  const typeAccount = ref(1);
+  function openAccount(type, email) {
+    formData.value = { ...defaultFormData };
+    try {
+      if (type == 'add') {
+        title.value = '娣诲姞';
+        // formData.value = {};
+        isShow.value = false;
+        typeAccount.value = 1;
+      } else {
+        title.value = '淇敼';
+        typeAccount.value = 2;
+        isShow.value = true;
+        getAccountApi({ mail: email }).then((res) => {
+          formData.value = res.data;
+        });
+      }
+      open.value = true;
+    } catch (error) {
+      open.value = false;
+    }
+  }
+
+  // 鍒犻櫎
+
+  const removalWarnings = [
+    '姝ら偖绠变笅鐨勫巻鍙查偖浠朵笉鍙煡鐪�',
+    '鎮ㄤ笉鑳戒娇鐢ㄦ閭鏀跺彂浠�',
+    '鍚屾鍒犻櫎鑷畾涔夋枃浠跺す銆佹爣绛俱�佹敹鍙戜欢瑙勫垯鍜岀粦瀹氱殑鍒悕閭銆傞噸鏂扮粦瀹氬悗涔熶笉鑳芥仮澶�',
+    '鐩稿叧閭欢绾跨储浠呭彲鏌ョ湅涓嶅彲鎿嶄綔',
+  ];
+  const deleteEmail = ref();
+  const openDrawerDetail = ref<boolean>(false);
+  const accountId = ref();
+
+  const fnHandleDetailCancel = () => {
+    countdown.value = 10;
+    openDrawerDetail.value = false;
+    clearInterval(intervalId);
+  };
+  function openDelete(row) {
+    openDrawerDetail.value = true;
+    deleteEmail.value = row.email;
+    accountId.value = row.accountId;
+    countdown.value = 10;
+    clearInterval(intervalId);
+    startCountdown();
+  }
+  function fnHandleDetailOk() {
+    openDrawerDetail.value = false;
+    deleteAccountApi({ accountId: accountId.value })
+      .then((res) => {
+        if (res.code === 0) {
+          createMessage.success(res.msg);
+          fnMailList();
+        }
+      })
+      .catch((err) => {
+        // createMessage.error(err.msg);
+      });
+  }
+  const countdown = ref(10);
+  let intervalId: any;
+
+  const startCountdown = () => {
+    if (countdown.value > 0) {
+      intervalId = setInterval(() => {
+        countdown.value--;
+        if (countdown.value === 0) {
+          clearInterval(intervalId);
+        }
+      }, 1000);
+    }
+  };
+
+  import { LoadingOutlined } from '@ant-design/icons-vue';
+  import { h, nextTick } from 'vue';
+  // 閭妫�娴�
+  const isCheck = ref(false);
+  const checkStatus = ref(false);
+
+  const openDrawerIsEmailValid = ref(false);
+  function openIsEmailValid(row) {
+    openDrawerIsEmailValid.value = true;
+    isEmailValid.value = row;
+    const email = { email: row.email };
+    isCheck.value = false;
+    isEmailValidApi(email).then((res) => {
+      if (res.code == 0) {
+        isCheck.value = true;
+        checkStatus.value = res.data.status;
+      }
+    });
+  }
+  const indicator = h(LoadingOutlined, {
+    style: {
+      fontSize: '24px',
+    },
+    spin: true,
+  });
+
+  const isEmailValid = ref<Record<string, any>>({});
+
+  // 鍒悕澶勭悊
+  const aliasFormData = ref<Record<string, any>>({});
+  const openAlias = ref(false);
+  const titleAlias = ref('');
+  function fnOpenAlias(row) {
+    openAlias.value = true;
+    titleAlias.value = row.email;
+  }
+  const aliasRef = ref();
+  function fnHandleAliasOk() {
+    nextTick(() => {
+      aliasRef.value.validate().then(() => {
+        openAlias.value = false;
+      });
+    });
+  }
+
+  // 鍏ㄩ儴妫�娴�
+  const isCheckAll = ref(false);
+  function fnCheckAll() {
+    isCheckAll.value = true;
+    setTimeout(() => {
+      isCheckAll.value = false;
+    }, 3000);
+  }
+</script>
+<style scoped lang="less">
+  .bullet {
+    display: inline-block;
+    margin-right: 5px;
+    color: #000;
+    font-size: 16px;
+  }
+
+  .form-item {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+  }
+
+  .isColor {
+    color: #18a561;
+  }
+
+  .isRed {
+    color: red;
+  }
+</style>
diff --git a/src/views/email/Utils/quickText.vue b/src/views/email/Utils/quickText.vue
new file mode 100644
index 0000000..a99f052
--- /dev/null
+++ b/src/views/email/Utils/quickText.vue
@@ -0,0 +1,202 @@
+<template>
+  <div class="p-2">
+    <vxe-toolbar>
+      <template #buttons>
+        <div style="display: flex; align-items: flex-end">
+          <span style="font-size: 1.25rem; font-weight: 600">蹇�熸枃鏈�</span
+          ><span style="margin-left: 5px; padding-bottom: 4px; font-size: 12px"
+            >浣滀负妯℃澘浣跨敤锛屽湪鍐欎俊鏃跺揩閫熷垱寤哄唴瀹�</span
+          >
+        </div>
+      </template>
+      <template #tools>
+        <a-button type="primary" @click="showDrawer('add', '')">鏂板缓鏂囨湰</a-button>
+      </template>
+    </vxe-toolbar>
+
+    <vxe-table ref="xTable" style="margin: 10px 0" :data="demo.tableData" @mounted="onMounted">
+      <vxe-column width="60">
+        <template #default>
+          <span class="drag-btn">
+            <HolderOutlined />
+          </span>
+        </template>
+      </vxe-column>
+      <vxe-column field="userName" title="鏍囬" width="250"></vxe-column>
+      <vxe-column field="content" title="鍐呭" min-width="250"></vxe-column>
+      <vxe-column field="age" title="鎿嶄綔" width="150">
+        <template #default="{ row }">
+          <a style="margin-right: 10px" @click="showDrawer('update', row)">缂栬緫</a>
+          <a style="margin-right: 10px" @click="fnDelete(row)">鍒犻櫎</a>
+        </template>
+      </vxe-column>
+    </vxe-table>
+    <a-drawer :title="`${title}鏂囨湰`" placement="right" :open="open" @close="onClose" width="600">
+      <a-form ref="formRef" :model="form" style="margin-top: 20px">
+        <a-form-item
+          label="鏍囬"
+          name="textName"
+          :rules="[{ required: true, message: '璇疯緭鍏ュ悕绉�', trigger: 'blur' }]"
+        >
+          <a-input v-model:value="form.textName" placeholder="璇疯緭鍏ュ悕绉�" />
+        </a-form-item>
+        <a-form-item
+          label="鍐呭"
+          name="content"
+          :rules="[{ required: true, message: '璇疯緭鍏ュ唴瀹�', trigger: 'blur' }]"
+        >
+          <Tinymce v-model="form.content" :isElse="false" :isText="false" :isImg="false"></Tinymce>
+        </a-form-item>
+      </a-form>
+      <template #footer>
+        <div style="margin-top: 20px; text-align: center">
+          <div style="margin-bottom: 20px"
+            >鍐欓偖浠舵椂锛岃緭鍏�
+            <span style="color: red">&</span>鏍囬鍏抽敭璇嶏紝鑱旀兂鍑哄揩閫熸枃鏈紝蹇嵎鎻掑叆</div
+          >
+          <a-button style="margin-right: 8px" @click="onClose">鍙栨秷</a-button>
+          <a-button type="primary" @click="onOk">淇濆瓨</a-button>
+        </div>
+      </template>
+    </a-drawer>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  import { ref, computed, onMounted, nextTick, onUnmounted, reactive } from 'vue';
+  import { Tinymce } from '@/components/Tinymce';
+  import {
+    getQuickTextApi,
+    addQuickTextApi,
+    updateQuickTextApi,
+    deleteQuickTextApi,
+  } from '@/api/email/userList';
+  
+  // 鎺掑簭
+  import { HolderOutlined } from '@ant-design/icons-vue';
+  import Sortable from 'sortablejs';
+  let sortable: any;
+  const demo = reactive({
+    showHelpTip: false,
+    tableData: [],
+  });
+  const xTable = ref();
+  const rowDrop = () => {
+    const $table = xTable.value;
+    sortable = Sortable.create($table.$el.querySelector('.body--wrapper>.vxe-table--body tbody'), {
+      handle: '.drag-btn',
+      onEnd: (sortableEvent) => {
+        const newIndex = sortableEvent.newIndex as number;
+        const oldIndex = sortableEvent.oldIndex as number;
+        const currRow:Record<string, any> = demo.tableData.splice(oldIndex, 1)[0];
+        // demo.tableData.splice(newIndex, 0, currRow);
+        updateQuickTextApi({
+          textId: currRow.textId,
+          textName: currRow.textName,
+          content: currRow.content,
+          sortId: newIndex,
+        }).then(() => {
+          fnGetList();
+        }).catch(()=>{});
+      },
+    });
+  };
+
+  let initTime: any;
+  nextTick(() => {
+    // 鍔犺浇瀹屾垚涔嬪悗鍦ㄧ粦瀹氭嫋鍔ㄤ簨浠�
+    initTime = setTimeout(() => {
+      rowDrop();
+    }, 500);
+  });
+
+  onUnmounted(() => {
+    clearTimeout(initTime);
+    if (sortable) {
+      sortable.destroy();
+    }
+  });
+
+  const form = ref<Record<string, any>>({});
+  const title = ref('鏂板缓');
+  const open = ref<boolean>(false);
+  const handleType = ref('add');
+  const formRef = ref();
+  const showDrawer = (type, row) => {
+    handleType.value = type;
+    open.value = true;
+    if (type == 'add') {
+      form.value = {
+        textName: '',
+        content: '',
+      };
+    } else {
+      title.value = '缂栬緫';
+      nextTick(() => {
+        formRef.value.resetFields();
+        form.value = Object.assign(form.value, row);
+      });
+    }
+  };
+  const api = computed(() => {
+    return handleType.value == 'add' ? addQuickTextApi : updateQuickTextApi;
+  });
+
+  function fnDelete(row) {
+    deleteQuickTextApi({ textId: row.textId }).then((res) => {
+      if (res.code == 0) {
+        fnGetList();
+      }
+    });
+  }
+  function fnGetList() {
+    getQuickTextApi({}).then((res) => {
+      console.log(res);
+      demo.tableData = res.data;
+    });
+  }
+  const onClose = () => {
+    open.value = false;
+  };
+  import { useMessage } from '@/hooks/web/useMessage';
+  const { createMessage } = useMessage();
+  const onOk = () => {
+    formRef.value.validate().then(() => {
+      const data: Record<string, any> = {
+        textName: form.value.textName,
+        content: form.value.content,
+      };
+      if (handleType.value != 'add') {
+        data.textId = form.value.textId;
+      }
+      api
+        .value(data)
+        .then((res) => {
+          if (res.code === 0) {
+            createMessage.success(res.msg);
+          }
+          fnGetList();
+          onClose();
+        })
+        .catch((e) => {
+          createMessage.warning(e);
+        });
+    });
+  };
+
+  onMounted(() => {
+    fnGetList();
+  });
+</script>
+
+<style scoped>
+  .sortable-row-demo .drag-btn {
+    font-size: 12px;
+    cursor: move;
+  }
+
+  .sortable-row-demo .vxe-body--row.sortable-ghost,
+  .sortable-row-demo .vxe-body--row.sortable-chosen {
+    background-color: #dfecfb;
+  }
+</style>
diff --git a/src/views/email/components/Editor.vue b/src/views/email/components/Editor.vue
deleted file mode 100644
index 9e7933d..0000000
--- a/src/views/email/components/Editor.vue
+++ /dev/null
@@ -1,426 +0,0 @@
-<template>
-  <div>
-    <div class="p-1">
-      <a-form ref="formRef" :label-col="{ span: 2 }" :wrapper-col="{ span: 24 }" :model="modelRef">
-        <a-form-item :wrapper-col="{ span: 10 }">
-          <div style="display: flex; justify-content: space-evenly">
-            <a-button type="primary" shape="round" @click="fnHandleSubmit(modelRef)">鍙戦��</a-button>
-            <a-button shape="round">瀛樿崏绋�</a-button>
-            <a-button shape="round">棰勮</a-button>
-            <a-button shape="round">鎻愪氦瀹℃壒</a-button>
-            <a-button shape="round">鍙栨秷</a-button></div
-          >
-        </a-form-item>
-        <a-form-item label="鍙戜欢浜�" v-bind="validateInfos.sender" :wrapper-col="{ span: 12 }">
-          <a-select
-            v-model:value="modelRef.sender"
-            placeholder="閫夋嫨鍙戜欢浜�"
-            show-search
-            :field-names="{ label: 'name', value: 'id' }"
-            :options="state.data"
-            :filter-option="fnFilterOption"
-          >
-          </a-select>
-        </a-form-item>
-        <a-form-item label="鏀朵欢浜�" v-bind="validateInfos.recipients" :wrapper-col="{ span: 18 }">
-          <a-row>
-            <a-col class="gutter-row" :span="16">
-              <a-select
-                mode="multiple"
-                show-search
-                placeholder="璇烽�夋嫨鏀朵欢浜烘垨鑰呰緭鍏ユ敹浠朵汉閭"
-                v-model:value="modelRef.recipients"
-                :options="state.data"
-                :field-names="{ label: 'name', value: 'id' }"
-                :maxTagCount="4"
-                :filter-option="fnFilterOption"
-              />
-            </a-col>
-            <a-col
-              class="gutter-row"
-              :span="2"
-              style="display: flex; align-items: center; justify-content: center"
-            >
-              <plus-circle-outlined
-                style="color: rgb(0 0 0 / 45%)"
-                @click="fnHandleSelect('recipients')"
-              />
-            </a-col>
-            <a-col class="gutter-row" :span="1">
-              <a-form-item-rest>
-                <a-button
-                  shape="round"
-                  type="link"
-                  block
-                  size="small"
-                  @click="ccCheckboxChange($event)"
-                >
-                  {{ !isSecretDeliveryPerson ? '鎶勯��' : '鍙栨秷鎶勯��' }}
-                </a-button>
-              </a-form-item-rest>
-            </a-col>
-            <a-col class="gutter-row" :span="1">
-              <a-form-item-rest>
-                <a-button
-                  shape="round"
-                  type="link"
-                  block
-                  size="small"
-                  @click="bccCheckboxChange($event)"
-                >
-                  {{ !isCRecipient ? '瀵嗛��' : '鍙栨秷瀵嗛��' }}
-                </a-button>
-              </a-form-item-rest>
-            </a-col>
-          </a-row>
-        </a-form-item>
-        <span style="position: relative; top: -18px; left: 9%" v-if="modelRef.recipients.length">
-          {{ userParticulars && `${userParticulars.name}<${userParticulars.email}` }}>
-          <span v-if="!userParticulars.title" style="margin-right: 10px; margin-left: 5px">
-            鏆傛湭鏌ヨ鍒拌瀹㈡埛鐨勫綋鍦版椂闂�
-            <a @click="fnHandleTimeZone(userParticulars, 'add')">琛ュ厖鏃跺尯</a>
-          </span>
-          <span v-else>
-            {{ userParticulars.title }}
-            <a @click="fnHandleTimeZone(userParticulars, 'update')">鏃跺尯涓嶅锛�</a>
-          </span>
-
-          <a v-if="modelRef.recipients.length > 2">鏌ョ湅鍏ㄩ儴</a>
-        </span>
-
-        <a-form-item
-          label="鎶勯�佷汉"
-          v-bind="validateInfos.ccRecipients"
-          v-if="isSecretDeliveryPerson"
-          :wrapper-col="{ span: 18 }"
-        >
-          <a-row>
-            <a-col class="gutter-row" :span="16">
-              <a-select
-                mode="multiple"
-                show-search
-                placeholder="璇烽�夋嫨鎶勯�佷汉鎴栬�呰緭鍏ユ妱閫佷汉閭"
-                v-model:value="modelRef.ccRecipients"
-                :options="state.data"
-                :field-names="{ label: 'name', value: 'id' }"
-                :maxTagCount="4"
-                :filter-option="fnFilterOption"
-              />
-            </a-col>
-            <a-col
-              class="gutter-row"
-              :span="2"
-              style="display: flex; align-items: center; justify-content: center"
-            >
-              <plus-circle-outlined
-                style="color: rgb(0 0 0 / 45%)"
-                @click="fnHandleSelect('ccRecipients')"
-              />
-            </a-col>
-          </a-row>
-        </a-form-item>
-        <a-form-item
-          label="瀵嗛�佷汉"
-          v-bind="validateInfos.bccRecipients"
-          v-if="isCRecipient"
-          :wrapper-col="{ span: 18 }"
-        >
-          <a-row>
-            <a-col class="gutter-row" :span="16">
-              <a-select
-                mode="multiple"
-                show-search
-                placeholder="璇烽�夋嫨鎶勯�佷汉鎴栬�呰緭鍏ユ妱閫佷汉閭"
-                v-model:value="modelRef.bccRecipients"
-                :options="state.data"
-                :field-names="{ label: 'name', value: 'id' }"
-                :maxTagCount="4"
-                :filter-option="fnFilterOption"
-              />
-            </a-col>
-            <a-col
-              class="gutter-row"
-              :span="2"
-              style="display: flex; align-items: center; justify-content: center"
-            >
-              <plus-circle-outlined
-                style="color: rgb(0 0 0 / 45%)"
-                @click="fnHandleSelect('bccRecipients')"
-              />
-            </a-col>
-          </a-row>
-        </a-form-item>
-        <div
-          style="position: relative; top: -20px; left: 9%; color: #909090; font-size: 12px"
-          v-if="modelRef.recipients.length"
-        >
-          {{ `${userLength}/100 鏀朵欢浜恒�佹妱閫佷汉鍜屽瘑閫佺殑鑱旂郴浜烘�绘暟涓嶅彲瓒呰繃100` }}
-        </div>
-        <a-form-item
-          label="涓婚"
-          v-bind="validateInfos.subject"
-          :wrapper-col="{ span: 12 }"
-          :label-col="{ span: 2 }"
-        >
-          <a-input placeholder="璇疯緭鍏ラ偖浠朵富棰�" v-model:value="modelRef.subject" />
-        </a-form-item>
-        <a-form-item
-          v-bind="validateInfos.content"
-          :wrapper-col="{ span: 24 }"
-          :label-col="{ span: 2 }"
-        >
-          <Tinymce @change = "fnChangeContent"></Tinymce>
-        </a-form-item>
-      </a-form>
-    </div>
-    <SelectUser
-      ref="selectUserRef"
-      v-model="openSelectUser"
-      :selectIds="selectIds"
-      :idName="idName"
-      :title="selectUserTitle"
-      @updateData="fnSelectUser"
-    />
-    <a-modal
-      v-model:open="openTimeZone"
-      :title="`${titleTimeZone}鏃跺尯`"
-      :confirm-loading="confirmLoading"
-      @ok="fnHandleTimeZoneOk"
-    >
-      <a-form
-        ref="formTimeZoneRef"
-        :label-col="{ span: 6 }"
-        :wrapper-col="{ span: 24 }"
-        :model="modelRef"
-      >
-        <a-form-item label="鍥藉鍦板尯锛�" :wrapper-col="{ span: 12 }">
-          <a-select
-            v-model:value="modelRef.sender"
-            placeholder="閫夋嫨鍥藉鍦板尯"
-            show-search
-            :field-names="{ label: 'name', value: 'id' }"
-            :options="state.data"
-          >
-          </a-select>
-        </a-form-item>
-        <a-form-item label="鏃跺尯锛�" :wrapper-col="{ span: 12 }">
-          <a-select
-            v-model:value="modelRef.sender"
-            placeholder="閫夋嫨鏃跺尯"
-            show-search
-            :field-names="{ label: 'name', value: 'id' }"
-            :options="state.data"
-          >
-          </a-select>
-        </a-form-item>
-        <a-form-item :label-col="{ span: 6 }">
-          <a-row>
-            <a-col span="6"> </a-col>
-            <a-col span="18">
-              <div style="color: #909090"
-                >褰撳湴瀹炴椂鏃堕棿鏍规嵁鏃跺尯淇℃伅鏄剧ず<br />
-                鍥藉鍦板尯/鏃跺尯淇℃伅鍦ㄥ鎴疯祫鏂�-鐗瑰緛淇℃伅鏌ョ湅</div
-              >
-            </a-col>
-          </a-row>
-        </a-form-item>
-      </a-form>
-    </a-modal>
-  </div>
-</template>
-
-<script lang="ts" setup>
-  import { ref, reactive, computed } from 'vue';
-  import { useMessage } from '@/hooks/web/useMessage';
-  import { Tinymce } from '@/components/Tinymce';
-  import { PlusCircleOutlined } from '@ant-design/icons-vue';
-  import SelectUser from './SelectUser.vue';
-  import { Form } from 'ant-design-vue';
-  const modelRef = reactive({
-    sender: '',
-    recipients: [],
-    ccRecipients: [],
-    bccRecipients: [],
-    fileUNID:[],
-    subject: '',
-    content: '',
-  });
-  const rulesRef = reactive({
-    sender: [
-      {
-        required: true,
-        message: '鍙戜欢浜轰笉鑳戒负绌�',
-      },
-    ],
-    recipients: [
-      {
-        required: true,
-        message: '鏀朵欢浜轰笉鑳戒负绌�',
-      },
-    ],
-    subject: [
-      {
-        required: true,
-        message: '涓婚涓嶈兘涓虹┖',
-      },
-    ],
-    content: [
-      {
-        required: true,
-        message: '鍐呭涓嶈兘涓虹┖',
-      },
-    ],
-  });
-  const useForm = Form.useForm;
-  const { validate, validateInfos } = useForm(modelRef, rulesRef);
-
-  const formRef = ref();
-
-  let isSecretDeliveryPerson = ref(false);
-
-  let isCRecipient = ref(false);
-  const handleCheckboxChange = (ref, e) => {
-    ref.value = !ref.value;
-  };
-
-  const ccCheckboxChange = (e) => {
-    handleCheckboxChange(isSecretDeliveryPerson, e);
-  };
-  const bccCheckboxChange = (e) => {
-    handleCheckboxChange(isCRecipient, e);
-  };
-
-  const { createMessage } = useMessage();
-  import { getUserListApi,sendingMailApi,receiveApi } from '@/api/email/userList';
-  // 瀹氫箟鐘舵�佺鐞嗗璞�
-  const state = reactive({
-    data: [],
-    fetching: false,
-    error: null,
-  });
-
-  // 鑾峰彇鐢ㄦ埛鍒楄〃鐨勫嚱鏁�
-  const fnGetUserList = async (params) => {
-    try {
-      state.fetching = true;
-      const res = await getUserListApi(params);
-      const data = await receiveApi({mail:'574600396@qq.com'})
-      console.log(data,'--------');
-      
-
-      if (res && res.items && Array.isArray(res.items)) {
-        state.data = res.items;
-      } else {
-        console.error('Invalid response format:', res);
-      }
-    } catch (error) {
-      console.error('Failed to fetch user list:', error);
-    } finally {
-      state.fetching = false;
-    }
-  };
-
-  // 鍒濆鍖栫敤鎴锋暟鎹�
-  const fetchData = async () => {
-    await fnGetUserList({ page: 1, pageSize: 30 });
-  };
-  console.log(state.data, 'state.data');
-  // 璋冪敤鍒濆鍖栧嚱鏁�
-  fetchData();
-
-  const fnFilterOption = (input: string, option: any) => {
-    return option.name.toLowerCase().indexOf(input.toLowerCase()) >= 0;
-  };
-
-  const openSelectUser = ref(false);
-  const selectUserTitle = ref('閫夋嫨鏀朵欢浜�');
-  const selectIds = ref([]);
-  const idName = ref('');
-  const fnHandleSelect = (e) => {
-    if (e === 'recipients') {
-      selectUserTitle.value = '閫夋嫨鏀朵欢浜�';
-      idName.value = e;
-    }
-    if (e === 'ccRecipients') {
-      selectUserTitle.value = '閫夋嫨鎶勯�佷汉';
-      idName.value = e;
-    }
-    if (e === 'bccRecipients') {
-      selectUserTitle.value = '閫夋嫨瀵嗛�佷汉';
-      idName.value = e;
-    }
-    selectIds.value = modelRef[e];
-    openSelectUser.value = true;
-  };
-
-  function fnChangeContent(e) {
-    modelRef.content = e.content;
-    modelRef.fileUNID = e.fileUNID
-  }
-  function fnHandleSubmit(values: any) {
-    validate()
-      .then((res) => {
-
-        sendingMailApi(modelRef).then((res=>{
-
-        }))
-        createMessage.success('鍙戦�佹垚鍔�');
-      })
-      .catch((error) => {
-        createMessage.error('琛ㄥ崟楠岃瘉澶辫触');
-      });
-  }
-  const fnSelectUser = (e) => {
-    modelRef[e.idName] = e.selectedRowKeys;
-  };
-
-  const userLength = computed(() => {
-    return (
-      modelRef.recipients.length + modelRef.ccRecipients.length + modelRef.bccRecipients.length
-    );
-  });
-
-  interface Recipient {
-    value: string;
-    email: string;
-    title: string;
-  }
-
-  const userParticulars = computed<Recipient | null>(() => {
-    const recipientId = modelRef.recipients?.[0];
-    if (!recipientId) {
-      console.error('Recipient ID is not defined.');
-      return { value: '', email: '', title: '' };
-    }
-    // 浣跨敤 find 鏂规硶鏌ユ壘鍖归厤椤�
-    const foundItem = state.data.find((item) => item.id === recipientId);
-
-    // 杩斿洖鎵惧埌鐨勯」锛屽鏋滄湭鎵惧埌鍒欒繑鍥� null
-    return foundItem || { value: '', email: '', title: '' };
-  });
-  const fnHandleTimeZone = (e, type) => {
-    console.log('fnHandleTimeZone');
-    if (type == 'add') {
-      titleTimeZone.value = '娣诲姞';
-      typeTimeZone.value = 1;
-    } else {
-      titleTimeZone.value = '淇敼';
-      typeTimeZone.value = 2;
-    }
-    openTimeZone.value = true;
-  };
-
-  const openTimeZone = ref<boolean>(false);
-  const confirmLoading = ref<boolean>(false);
-  const titleTimeZone = ref<string>('娣诲姞');
-  const typeTimeZone = ref<number>(1);
-
-  const fnHandleTimeZoneOk = () => {
-    confirmLoading.value = true;
-    setTimeout(() => {
-      openTimeZone.value = false;
-      confirmLoading.value = false;
-    }, 2000);
-  };
-</script>
-<style scoped lang="less"></style>
diff --git a/src/views/email/components/LeftNav.vue b/src/views/email/components/LeftNav.vue
index c9f3ff4..ebb5161 100644
--- a/src/views/email/components/LeftNav.vue
+++ b/src/views/email/components/LeftNav.vue
@@ -1,107 +1,181 @@
 锘�<template>
-  <ScrollContainer class="mt-4">
-    <a-menu
-      id="email-left-nav"
-      v-model:openKeys="openKeys"
-      v-model:selectedKeys="selectedKeys"
-      mode="inline"
-      :items="items"
-      @click="handleClick"
-    ></a-menu>
-  </ScrollContainer>
+  <PageWrapper dense contentFullHeight fixedHeight>
+    <div>
+      <div style="height: 15vh; padding: 20px 40px; text-align: center">
+        <span style="display: flex; justify-content: space-around">
+          <a-button shape="circle" size="large">
+            <MailOutlined />
+          </a-button>
+          <a-button shape="circle" size="large">
+            <UserOutlined />
+          </a-button>
+        </span>
+        <a-button
+          style="width: 100%; margin-top: 10px; padding: 0 30px"
+          type="primary"
+          size="large"
+          shape="round"
+          @click="$router.push('/email/edit')"
+        >
+          鍐欎俊
+        </a-button>
+      </div>
 
-
+      <div class="menu-container">
+        <a-menu
+          id="email-left-nav"
+          v-model:open-keys="openKeys"
+          v-model:selected-keys="selectedKeys"
+          mode="inline"
+          :popupClassName="popupClassName"
+        >
+          <template v-for="item in items" :key="item.key">
+            <a-sub-menu v-if="item.children" :key="item.key">
+              <template #title>
+                <div class="my-display">
+                  <span>{{ item.title }}</span>
+                  <!-- <span class="my-left" v-if="item.total > 0">{{ item.total }}</span> -->
+                </div>
+              </template>
+              <a-menu-item
+                style="display: flex; justify-content: space-between; padding-left: 28px"
+                v-for="(child, index) in item.children"
+                :key="index"
+                @click="handleClick(child)"
+              >
+                <div class="my-display">
+                  <span>{{ child.title }}</span>
+                  <span v-if="item.total > 0"> {{ child.total }}</span>
+                </div>
+              </a-menu-item>
+            </a-sub-menu>
+            <a-menu-item v-else :key="item.key" @click="handleClick(item)">
+              <div class="my-display">
+                <span>{{ item.title }}</span>
+                <span class="my-left" v-if="item.total > 0">{{ item.total }}</span>
+              </div>
+            </a-menu-item>
+          </template>
+        </a-menu>
+      </div>
+    </div>
+  </PageWrapper>
 </template>
+
 <script lang="ts" setup>
-import {  ref,   } from 'vue';
-import type { MenuProps } from 'ant-design-vue';
-import  { Menu } from 'ant-design-vue';
-import { ScrollContainer } from '@/components/Container';
+  import { ref, onMounted } from 'vue';
+  import { PageWrapper } from '@/components/Page';
+  import { MailOutlined, UserOutlined } from '@ant-design/icons-vue';
+  import { getEmailModuleApi } from '@/api/email/userList';
+  import { useRouter } from 'vue-router';
 
-const selectedKeys = ref<string[]>(['1']);
-const openKeys = ref<string[]>(['sub1']);
+  const selectedKeys = ref<string[]>(['Index']);
+  const openKeys = ref<string[]>(['Inbox']);
+  const items = ref([]); // 瀹氫箟 items 绫诲瀷
 
-const AMenu = Menu;
+  const fnGetEmailModule = async () => {
+    try {
+      const res = await getEmailModuleApi();
+      items.value = convertRoutesToMenuItems(res.data);
+    } catch (error) {
+      console.error('鑾峰彇閭妯″潡澶辫触:', error); // 澶勭悊閿欒
+    }
+  };
 
-const items = ref([
-  {
-    key: '1',
-    label: 'Navigation One',
-    title: 'Navigation One',
-  },
-  {
-    key: '2',
-    label: 'Navigation Two',
-    title: 'Navigation Two',
-  },
-  {
-    key: 'sub1',
-    label: 'Navigation Three',
-    title: 'Navigation Three',
-    children: [
-      {
-        key: '3',
-        label: 'Option 3',
-        title: 'Option 3',
-      },
-      {
-        key: '4',
-        label: 'Option 4',
-        title: 'Option 4',
-      },
-      {
-        key: 'sub1-2',
-        label: 'Submenu',
-        title: 'Submenu',
-        children: [
-          {
-            key: '5',
-            label: 'Option 5',
-            title: 'Option 5',
-          },
-          {
-            key: '6',
-            label: 'Option 6',
-            title: 'Option 6',
-          },
-        ],
-      },
-    ],
-  },
-  {
-    key: 'sub2',
-    label: 'Navigation Four',
-    title: 'Navigation Four',
-    children: [
-      {
-        key: '7',
-        label: 'Option 7',
-        title: 'Option 7',
-      },
-      {
-        key: '8',
-        label: 'Option 8',
-        title: 'Option 8',
-      },
-      {
-        key: '9',
-        label: 'Option 9',
-        title: 'Option 9',
-      },
-      {
-        key: '10',
-        label: 'Option 10',
-        title: 'Option 10',
-      },
-    ],
-  },
-]);
+  const convertRoutesToMenuItems = (routes: any[]) => {
+    return routes
+      .map((route) => {
+        if (route.children && route.children.length > 0) {
+          return {
+            key: route.key,
+            title: route.mailName,
+            total: route.total,
+            children: convertRoutesToMenuItems(route.children),
+          };
+        }
+        if (!route.hideMenu) {
+          return {
+            key: route.key,
+            title: route.mailName,
+            total: route.total,
+          };
+        }
+      })
+      .filter(Boolean); // 杩囨护鎺� undefined
+  };
 
-const handleClick: MenuProps['onClick'] = e => {
-  console.log('click', e);
-};
+  // 鐢熷懡鍛ㄦ湡閽╁瓙锛屽姞杞借彍鍗曟暟鎹�
+  onMounted(() => {
+    fnGetEmailModule();
+  });
+  const routesConfig = {
+    InboxPage1: '/email/index',
+    receiver: '/email/Inbox/list',
+    sender: '/email/outbox',
+    IndexPage1:'/email/outbox'
+  };
+  // 鐐瑰嚮浜嬩欢澶勭悊
+  const router = useRouter();
+  const handleClick = (e: any) => {
+    console.log(e, '------4');
+    let matched = false;
+    const route = router.getRoutes() || [];
+    route.forEach((item) => {
+      if (item.name === e.key) {
+        router.push(item.path);
+        matched = true;
+        return; // 璺冲嚭褰撳墠寰幆
+      }
 
-// watch(openKeys, val => {
-//   console.log('openKeys', val);
-// });
+      if (!matched) {
+        switch (e.key) {
+          case 'InboxPage1':
+            router.push(routesConfig[e.key]);
+            matched = true;
+            return; // 璺冲嚭褰撳墠寰幆
+          case 'receiver':
+            router.push(`${routesConfig[e.key]}?${e.title}`);
+            matched = true;
+            return; // 璺冲嚭褰撳墠寰幆
+          case 'sender':
+            router.push(`${routesConfig[e.key]}?${e.title}`);
+            matched = true;
+            return; // 璺冲嚭褰撳墠寰幆
+          case 'IndexPage1':
+            router.push(`${routesConfig[e.key]}`);
+            matched = true;
+            return; // 璺冲嚭褰撳墠寰幆
+          default:
+            // 澶勭悊榛樿鎯呭喌锛屼緥濡傝褰曟棩蹇楁垨鎶涘嚭璀﹀憡
+            console.warn(`Unknown key: ${e.key}`);
+        }
+      }
+    });
+  };
+  const popupClassName = {
+    display: 'flex',
+    'align-items': 'center',
+    'justify-content': 'space-between',
+  };
 </script>
+
+<style lang="less" scoped>
+  :deep(.ant-menu-inline) {
+    border-inline-end: 0 solid rgb(5 5 5 / 6%) !important;
+  }
+
+  .menu-container {
+    height: 70vh;
+    overflow: auto;
+  }
+
+  .my-display {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+  }
+
+  .my-left {
+    margin-left: 5px;
+  }
+</style>
diff --git a/src/views/email/components/ListPage/TooltipAndDropdown .vue b/src/views/email/components/ListPage/TooltipAndDropdown .vue
new file mode 100644
index 0000000..19b6c55
--- /dev/null
+++ b/src/views/email/components/ListPage/TooltipAndDropdown .vue
@@ -0,0 +1,304 @@
+<!-- TooltipAndDropdown.vue -->
+<template>
+  <div style="display: flex; justify-content: space-around">
+    <!-- Tooltip -->
+
+    <!-- Dropdown -->
+    <a-dropdown v-model:open="dropdownOpen" :trigger="['click']" placement="bottomLeft">
+      <a-tooltip v-if="showTooltip" v-model:open="tooltipOpen" placement="bottomLeft">
+        <template #title>
+          <div class="p-1" style="width: 210px">
+            <span>{{ tooltipTitle }}</span>
+            <div class="p-2" style="font-size: 18px; font-weight: 600">{{ formattedTime }}</div>
+            <div class="p-2" style="text-align: center">
+              <CButton size="small" shape="round" type="success" @click="onUpdate">淇敼</CButton>
+              <a-button
+                style="margin-left: 10px"
+                size="small"
+                shape="round"
+                type="primary"
+                @click="onComplete"
+                >瀹屾垚</a-button
+              >
+            </div>
+          </div>
+        </template>
+        <FieldTimeOutlined v-if="showTooltip" class="color-handle" />
+      </a-tooltip>
+      <FieldTimeOutlined v-else @click.stop="toggleDropdown" />
+      <template #overlay>
+        <a-card title="閫夋嫨绋嶅悗澶勭悊鏃堕棿锛�" style="width: 250px" size="small">
+          <div
+            class="date p-1"
+            v-for="item in dateList"
+            :key="item.key"
+            @click="fnSelectDate(item)"
+          >
+            <div class="date-left">{{ item.name }}</div>
+            <div class="date-right">
+              <span v-if="item.key !== 'today'">{{ item.dayOfWeek }}</span>
+              <span style="margin-left: 5px">{{ item.time }}</span>
+            </div>
+          </div>
+          <a-divider style="margin: 5px 0" />
+          <div class="date p-1">
+            <a-popover
+              trigger="click"
+              title="鑷畾涔夋椂闂�"
+              v-model:open="customTimeDropdownOpen"
+              @confirm="onSubmitCustomTime"
+            >
+              <template #content>
+                <a-form :model="form" ref="formRef">
+                  <a-form-item
+                    label="鏃ユ湡"
+                    name="date"
+                    :rules="[{ required: true, message: '璇烽�夋嫨鏃ユ湡' }]"
+                  >
+                    <a-date-picker
+                      format="YYYY/MM/DD"
+                      :disabledDate="disabledDate"
+                      v-model:value="form.date"
+                    />
+                  </a-form-item>
+                  <a-form-item
+                    label="鏃堕棿"
+                    name="time"
+                    :rules="[{ required: true, message: '璇烽�夋嫨鏃堕棿' }]"
+                  >
+                    <a-time-picker v-model:value="form.time" />
+                  </a-form-item>
+                  <a-form-item>
+                    <a-button @click="cancelCustomTime">鍙栨秷</a-button>
+                    <a-button style="margin-left: 10px" type="primary" @click="submitCustomTime"
+                      >纭畾</a-button
+                    >
+                  </a-form-item>
+                </a-form>
+              </template>
+              <div class="date-left" @click="toggleCustomTime">鑷畾涔夋椂闂�</div>
+            </a-popover>
+          </div>
+        </a-card>
+      </template>
+    </a-dropdown>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  import { ref, reactive, defineProps, defineEmits, computed, inject, nextTick } from 'vue';
+  import { FieldTimeOutlined, PushpinOutlined } from '@ant-design/icons-vue';
+  import dayjs from 'dayjs';
+  import CButton from '@/components/CButton/index.vue';
+  const getDataList = inject('getDataList');
+
+  function processDateList() {
+    const dateList = [
+      { name: '浠婂ぉ绋嶆櫄', key: 'today' },
+      { name: '鏄庡ぉ', key: 'tomorrow' },
+      { name: '鏈懆绋嶆櫄', key: 'thisWeek' },
+      { name: '鏈懆鏈�', key: 'thisWeekend' },
+      { name: '涓嬪懆', key: 'nextWeek' },
+    ];
+
+    const now = new Date();
+
+    const dateArray = dateList.map((item) => {
+      let date = new Date(); // 鍒濆鍖栧綋鍓嶆棩鏈�
+      let dayOfWeekString = ''; // 鍒濆鍖栨槦鏈熷嚑
+      let timeString = ''; // 鍒濆鍖栨椂闂�
+
+      switch (item.key) {
+        case 'today':
+          // 浠婂ぉ绋嶆櫄鏄粖澶╃殑 16:00
+          date.setHours(16, 0, 0, 0);
+          break;
+        case 'tomorrow':
+          // 鏄庡ぉ 08:00
+          date.setDate(now.getDate() + 1);
+          date.setHours(8, 0, 0, 0);
+          break;
+        case 'thisWeek':
+          // 鏈懆绋嶆櫄锛氬懆浜� 08:00
+          const dayOfWeek = now.getDay();
+          const daysUntilFriday = (5 - dayOfWeek + 7) % 7; // 璁$畻鍒板懆浜旂殑澶╂暟
+          date.setDate(now.getDate() + daysUntilFriday);
+          date.setHours(8, 0, 0, 0);
+          break;
+        case 'thisWeekend':
+          // 鏈懆鏈細鍛ㄦ棩 08:00
+          const daysUntilSunday = (7 - now.getDay()) % 7; // 璁$畻鍒板懆鏃ョ殑澶╂暟
+          date.setDate(now.getDate() + daysUntilSunday);
+          date.setHours(8, 0, 0, 0);
+          break;
+        case 'nextWeek':
+          // 涓嬪懆涓� 08:00
+          const daysUntilNextMonday = ((1 - now.getDay() + 7) % 7) + 7; // 璁$畻鍒颁笅鍛ㄤ竴鐨勫ぉ鏁�
+          date.setDate(now.getDate() + daysUntilNextMonday);
+          date.setHours(8, 0, 0, 0);
+          break;
+        default:
+          break;
+      }
+
+      // 鑾峰彇鏄熸湡鍑犵殑瀛楃涓茶〃绀�
+      dayOfWeekString = date.toLocaleDateString('zh-CN', { weekday: 'long' });
+
+      // 鑾峰彇浠呮椂闂撮儴鍒�
+      timeString = date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' });
+
+      // 杩斿洖鏃ユ湡銆佹槦鏈熷嚑鍜屾椂闂翠俊鎭�
+      return {
+        name: item.name,
+        key: item.key,
+        date: date,
+        dayOfWeek: dayOfWeekString,
+        time: timeString, // 淇濆瓨浠呮椂闂撮儴鍒�
+      };
+    });
+
+    return dateArray;
+  }
+  const dateList = processDateList();
+  const props = defineProps({
+    tooltipTitle: String, // Tooltip 鏍囬
+    initialDropdownOpen: Boolean, // Dropdown 鍒濆鎵撳紑鐘舵��
+    showTooltip: Boolean, // 鏄惁灞曠ず Tooltip
+    initialTooltipOpen: Boolean, // Tooltip 鍒濆鎵撳紑鐘舵��
+    row: Object, // 褰撳墠琛屽璞�
+    docCodeS: Array,
+  });
+console.log(props,'-59585855');
+
+  const emit = defineEmits(['updateHandleTime', 'completeAction', 'customTimeSubmit', 'tagRow']);
+
+  import { useMessage } from '@/hooks/web/useMessage';
+  import { formatToDateDay } from '@/utils/dateUtil';
+  import { updateHandleAPi } from '@/api/email/userList';
+
+  const { createMessage } = useMessage();
+  function fnSelectDate(item) {
+    dropdownOpen.value = false;
+    const date = formatToDateDay(new Date(item.date));
+    const data = {
+      handleTime: date,
+      docCode: props.docCodeS,
+    };
+    pushUpdateHandle(data);
+  }
+  function pushUpdateHandle(data) {
+    updateHandleAPi(data)
+      .then((res) => {
+        if (res.code == 0) {
+          createMessage.success(res.msg);
+          getDataList({});
+        }
+      })
+      .catch((err) => {});
+  }
+
+  // Dropdown 鍜� Tooltip 鐘舵�佺鐞�
+  const dropdownOpen = ref(props.initialDropdownOpen);
+  const tooltipOpen = ref(props.initialTooltipOpen);
+  const customTimeDropdownOpen = ref(false);
+
+  // 琛ㄥ崟鏁版嵁
+  const form = reactive({
+    date: '',
+    time: '',
+  });
+  const onUpdate = () => {
+    dropdownOpen.value = true;
+    tooltipOpen.value = false;
+  };
+  // 浜嬩欢澶勭悊
+  const toggleDropdown = () => {
+    dropdownOpen.value = !dropdownOpen.value;
+  };
+
+  const toggleTooltip = () => {
+    tooltipOpen.value = !tooltipOpen.value;
+  };
+
+  const toggleCustomTime = () => {
+    customTimeDropdownOpen.value = !customTimeDropdownOpen.value;
+    dropdownOpen.value = false;
+  };
+
+  const onComplete = () => {
+    const data = {
+      handleTime: '',
+      docCode: props.docCodeS,
+    };
+    pushUpdateHandle(data);
+  };
+  const formRef = ref();
+  const submitCustomTime = () => {
+      formRef.value.validate().then((valid) => {
+        if (valid) {
+          customTimeDropdownOpen.value = false;
+          const date = form.date ? dayjs(form.date).format('YYYY-MM-DD') : '';
+          const time = form.time ? dayjs(form.time).format('HH:mm') : '';
+          const data = {
+            handleTime: date + ' ' + time,
+            docCode: props.docCodeS,
+          };
+          pushUpdateHandle(data);
+        } else {
+          return false;
+        }
+      });
+  };
+
+  const cancelCustomTime = () => {
+    customTimeDropdownOpen.value = false;
+    dropdownOpen.value = true;
+  };
+
+  // 澶勭悊鏃堕棿鏍煎紡鍖�
+  const formattedTime = computed(() =>
+    props.row.handleTime ? dayjs(props.row.handleTime).format('YYYY-MM-DD HH:mm') : '',
+  );
+
+  // 鏃ユ湡绂佺敤澶勭悊
+  const disabledDate = (currentDate) => {
+    return currentDate && currentDate < dayjs().startOf('day');
+  };
+
+  function TooltipAndDropdown() {}
+
+  function onSubmitCustomTime() {
+    customTimeDropdownOpen.value = true;
+  }
+</script>
+
+<style scoped lang="less">
+  .display-flex {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+  }
+
+  .date {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+  }
+
+  .date-left {
+    flex-grow: 1;
+  }
+
+  .color-handle {
+    cursor: pointer;
+  }
+
+  .date:hover {
+    transition: all 0.2s;
+    background-color: #0000000a;
+  }
+
+  .color-handle {
+    color: #0a6aff;
+  }
+</style>
diff --git a/src/views/email/components/ListPage/drawerDetail.vue b/src/views/email/components/ListPage/drawerDetail.vue
new file mode 100644
index 0000000..f3443c4
--- /dev/null
+++ b/src/views/email/components/ListPage/drawerDetail.vue
@@ -0,0 +1,420 @@
+<template>
+  <a-drawer
+    v-model:open="drawerOpen"
+    :placement="placement"
+    width="1200"
+    :body-style="{ paddingBottom: '80px' }"
+    :footer-style="{ textAlign: 'right' }"
+    @after-open-change="afterOpenChange"
+    @close="drawerClose"
+  >
+    <template #title>
+      <div class="ctb">
+        <pageHeadLeft
+          :checked="true"
+          :selectAllRow="[{ docCode }]"
+          @nextNum="drawerClose"
+        ></pageHeadLeft>
+      <div class="ct-top">
+        <div class="title" style="margin-bottom: 20px">
+          <div class="left">
+            <span style="margin-right: 20px; font-size: 24px; font-weight: 700">
+              <a-tooltip placement="bottom">
+                <template #title>
+                  <span>{{ tableRowData.subject }}</span>
+                </template>
+                {{ truncateString(tableRowData.subject, 23) }}
+              </a-tooltip>
+            </span>
+            <span style="margin-right: 10px; font-size: 16px">
+              <PushpinOutlined />
+            </span>
+          </div>
+          <div class="right">
+            <div class="tate">{{ formatToDateDay(tableRowData.receiveTime) }}</div>
+            <div>
+              <a-dropdown-button>
+                <span>
+                  <a-tooltip placement="bottom">
+                    <template #title>
+                      <span>鍥炲</span>
+                    </template>
+                    <LeftOutlined @click="replyEmail(tableRowData)" />
+                  </a-tooltip>
+                </span>
+                <a-divider type="vertical" />
+                <span>
+                  <a-tooltip placement="bottom">
+                    <template #title>
+                      <span>蹇�熷洖澶�</span>
+                    </template>
+                    <DoubleLeftOutlined />
+                  </a-tooltip>
+                </span>
+                <template #overlay>
+                  <a-menu>
+                    <a-menu-item key="1">
+                      <UserOutlined />
+                      1st menu item
+                    </a-menu-item>
+                    <a-menu-item key="2">
+                      <UserOutlined />
+                      2nd menu item
+                    </a-menu-item>
+                    <a-menu-item key="3">
+                      <UserOutlined />
+                      3rd item
+                    </a-menu-item>
+                  </a-menu>
+                </template>
+              </a-dropdown-button>
+            </div>
+          </div>
+        </div>
+      </div></div>
+    </template>
+    <template #extra>
+      <div style="font-size: 16px">
+        鍏�<span class="m-1">{{ props.allList.length }}</span
+        >灏�
+        <LeftOutlined style="padding: 0 20px" @click="fnPrev" />
+        <RightOutlined @click="fnNext" />
+      </div>
+    </template>
+    <template #footer>
+      <div style="display: flex">
+        <a-textarea autoSize size="large" v-model:value="content" placeholder="蹇�熷洖澶�">
+          <template #prefix>
+            <RollbackOutlined style="color: #999" />
+          </template>
+        </a-textarea>
+        <a-button size="large" type="primary" style="margin-left: 10px" @click="fnQuickReply"
+          >鍙戦��
+        </a-button>
+      </div>
+    </template>
+    <div>
+      <div class="flex-between">
+        <div class="ct-left p-2" :class="isOpen ? 'isOpen' : 'onOpen'">
+          <div class="user p-1">
+            <div style="display: flex; align-items: center">
+              <a-avatar size="small" style="margin-right: 8px" src="#" />
+              {{ tableRowData.sender }}
+              <span>{{ `<${tableRowData.sender}>` }}</span>
+              <span style="margin: 0 10px">鍙戦��</span>
+              <a-popover placement="bottom">
+                <template #content>
+                  <div
+                    class="p-2"
+                    style="
+                      display: flex;
+                      align-items: center;
+                      border-bottom: 1px solid rgb(5 5 5 / 6%);
+                    "
+                  >
+                    <a-avatar size="small" style="margin-right: 8px" src="#" />
+                    <span style="color: #000; font-weight: 700">
+                      {{ `${tableRowData.receiver}` }}</span
+                    >
+                    <CopyOutlined />
+                  </div>
+                  <div class="display-flex p-2">
+                    <a-button type="link" size="small">寰�鏉ラ偖浠�</a-button>
+                  </div>
+                </template>
+                <a-avatar size="small" style="margin-right: 8px" src="#" />
+                {{ `${tableRowData.receiver}` }}<span>{{ `<${tableRowData.receiver}>` }}</span>
+              </a-popover>
+            </div>
+            <div
+              type="info"
+              class="p-2"
+              style="margin-top: 10px; background-color: #e4f1ff; font-size: 14px"
+            >
+              <span>{{ `<${tableRowData.sender}>` }}</span>
+              <span>鏆傛湭鏌ヨ鍒拌瀹㈡埛鐨勫綋鍦版椂闂�</span>
+              <!-- <span>2024-06-08 22:22</span> -->
+            </div>
+            <div class="ct" v-if="tableRowData.content">
+              <TinymcePw ref="TinymcePwRef" v-model="tableRowData.content" />
+            </div>
+          </div>
+        </div>
+        <div v-show="isOpen" class="ct-right p-2">sssss</div>
+      </div>
+      <div @click="fuToggleContent" class="toggle-btn" :class="isOpen ? 'onIconOpen' : 'iconOpen'">
+        <LeftOutlined v-if="!isOpen" />
+        <RightOutlined v-else />
+      </div>
+    </div>
+  </a-drawer>
+</template>
+
+<script lang="ts" setup>
+  name: 'drawerDetail';
+
+  import { ref, watch, defineProps, defineEmits, computed } from 'vue';
+  import {
+    LeftOutlined,
+    RightOutlined,
+    DoubleLeftOutlined,
+    PushpinOutlined,
+    CopyOutlined,
+    UserOutlined,
+    RollbackOutlined,
+  } from '@ant-design/icons-vue';
+  import pageHeadLeft from './pageHeadLeft.vue';
+  import { TinymcePw } from '@/components/Tinymce';
+  import { getMailInfoApi, setQuickReplyAPi } from '@/api/email/userList';
+  import { useCollapseStore } from '@/store/modules/useCollapseStore';
+  import { nextTick } from 'vue';
+  import { formatToDateDay } from '@/utils/dateUtil';
+  import { useLoading } from '@/components/Loading';
+
+  // const [open, close, setTip] = useLoading();
+  // 瀹氫箟灞炴��
+  interface Props {
+    modelValue: boolean;
+    title?: string;
+    placement?: 'left' | 'right' | 'top' | 'bottom';
+    idName?: string;
+    selectIds?: number[];
+    mailId?: string;
+    selectAllRow: Array<any>;
+    allList;
+  }
+
+  const props = defineProps<Props>();
+
+  const tableRowData = ref<Record<string, any>>({});
+  const emit = defineEmits(['update:modelValue', 'updateData']);
+  const drawerOpen = ref(props.modelValue);
+  const TinymcePwRef = ref();
+  const docCode = ref(props.mailId);
+  watch(
+    () => props.mailId,
+    (newVal) => {
+      docCode.value = newVal;
+    },
+  );
+  // 鐩戝惉灞炴�у彉鍖�
+  watch(
+    () => props.modelValue,
+    (newValue) => {
+      drawerOpen.value = newValue;
+      nextTick(() => {
+        console.log(newValue, '---------4', TinymcePwRef.value);
+      });
+      if (newValue) {
+        fnGetMailInfo(props.mailId);
+      }
+    },
+  );
+
+  function fnGetMailInfo(id) {
+    getMailInfoApi({ docCode: id })
+      .then((res) => {
+        docCode.value = id;
+        tableRowData.value = res.data;
+      })
+      .catch(() => {});
+  }
+  // 鏇存柊澶栭儴灞炴��
+  watch(drawerOpen, (newValue) => {
+    emit('update:modelValue', newValue);
+  });
+
+  // 鏂规硶
+  const collapseStore = useCollapseStore();
+  const afterOpenChange = (bool: boolean) => {
+    if (bool) {
+      fnGetUserList({ page: 1, pageSize: 30 });
+    }
+    collapseStore.toggle(false);
+  };
+  const drawerClose = (e) => {
+    drawerOpen.value = false;
+  };
+
+  function truncateString(str, maxLength) {
+    return str.length > maxLength ? str.substring(0, maxLength) + '...' : str;
+  }
+
+  const fnGetUserList = (params) => {
+    // 璋冪敤鎺ュ彛閫昏緫
+  };
+
+  // 璁$畻灞炴��
+  const placement = computed(() => props.placement || 'right');
+
+  // 鎶藉眽寮�鍏�
+  const fnSaveOpenChange = () => {
+    // 鎶藉眽淇濆瓨閫昏緫
+  };
+
+  const content = ref('');
+  const isOpen = ref(false);
+
+  function fuToggleContent() {
+    isOpen.value = !isOpen.value;
+  }
+
+  function fnPrev() {
+    const id = getPrevId(props.allList, docCode.value);
+    fnGetMailInfo(id);
+  }
+  function fnNext() {
+    const id = getNextId(props.allList, docCode.value);
+    fnGetMailInfo(id);
+  }
+  function getNextId(list, id) {
+    const index = list.findIndex((item) => item.docCode === id);
+    if (index < list.length - 1) {
+      return list[index + 1].docCode;
+    } else {
+      return list[0].docCode;
+    }
+  }
+  function getPrevId(list, id) {
+    const index = list.findIndex((item) => item.docCode === id);
+    if (index > 0) {
+      return list[index - 1].docCode;
+    } else {
+      return list[list.length - 1].docCode;
+    }
+  }
+
+  import { useMessage } from '@/hooks/web/useMessage';
+
+  const { createMessage } = useMessage();
+  function fnQuickReply() {
+    if (!content.value) {
+      createMessage.warning('璇疯緭鍏ュ洖澶嶅唴瀹�');
+      return;
+    }
+    const data = {
+      docCode: docCode.value,
+      content: content.value,
+    };
+    setQuickReplyAPi(data).then((res) => {
+      if (res.code == 0) {
+        createMessage.success(res.msg);
+
+        fnGetMailInfo(props.mailId);
+      }
+    });
+  }
+  import { useRouter } from 'vue-router';
+  const router = useRouter();
+  function replyEmail(row) {
+    router.push({ path: '/email/edit', query: { docCode: row.docCode, type: 'reply' } });
+  }
+</script>
+
+<style scoped lang="less">
+  .table-content {
+    display: flex;
+    justify-content: space-between;
+    border-top: 1px solid #f0f0f0;
+
+    .left {
+      width: 80%;
+      height: 100%;
+      border-right: 1px solid #f0f0f0;
+    }
+
+    .right {
+      height: 100%;
+    }
+  }
+
+  ::v-deep(.ant-table) {
+    min-height: 355px !important;
+  }
+
+  .title {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+
+    & .left {
+      display: flex;
+      align-items: center;
+      width: 50%;
+    }
+
+    & .right {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      width: 25%;
+
+      & .tate {
+        color: #999;
+        font-size: 14px;
+      }
+    }
+  }
+
+  .ct {
+    margin: 20px 0;
+  }
+
+  .flex-between {
+    display: flex;
+    padding-top: 4%;
+  }
+
+  .ct-left {
+    padding-right: 20px;
+  }
+
+  .ct-right {
+    border-left: 1px solid #f0f0f0;
+  }
+
+  .toggle-btn {
+    display: flex;
+    position: absolute;
+    z-index: 99;
+    top: 50%;
+    width: 20px;
+    height: 54px;
+    padding-left: 5px;
+    transform: translateY(-50%);
+    border: 1px solid #f0f0f0;
+    border-right: none;
+    border-radius: 10px 0 0 10px;
+    background: #fafafa;
+  }
+
+  .onOpen {
+    width: 100%;
+  }
+
+  .isOpen {
+    width: 69%;
+  }
+
+  .iconOpen {
+    right: 0%;
+  }
+
+  .onIconOpen {
+    right: 32%;
+  }
+
+  .ctb {
+    position: relative;
+  }
+
+  .ctb .ct-top {
+    position: absolute;
+    z-index: 99;
+    top: 41px;
+    left: -28px;
+    width: 116%;
+    padding: 10px;
+    background: #fff;
+  }
+</style>
diff --git a/src/views/email/components/ListPage/list.vue b/src/views/email/components/ListPage/list.vue
new file mode 100644
index 0000000..b760c3e
--- /dev/null
+++ b/src/views/email/components/ListPage/list.vue
@@ -0,0 +1,234 @@
+<template>
+  <PageWrapper>
+    <div style="height: calc(100vh - 84px)">
+      <div class="my-head">
+        <div class="left">
+          <div class="left-box p-3">
+            <!-- 澶氶�� -->
+            <a-checkbox
+              class="icon"
+              style="margin-right: 10px"
+              v-model:checked="state.checkAll"
+              :indeterminate="state.indeterminate"
+              @change="fnCheckedChange"
+            ></a-checkbox>
+            <!--鏇存柊  -->
+            <SyncOutlined class="icon" v-show="!checked" />
+            <pageHeadLeft
+              :checked="checked"
+              :selectAllRow="selectAllRow"
+              :parentTableList="newList"
+            ></pageHeadLeft>
+          </div>
+        </div>
+
+        <div class="right p-3"
+          >鍏�<span style="padding: 0 5px">{{page.total}}</span>灏�
+          <a-pagination
+            v-model:current="pageCurrent"
+             v-model:page-size='page.limit'
+            simple
+            :total="page.total"
+            style="margin-left: 10px"
+            @change="handlePageChange"
+          />
+          <FilterOutlined style="margin-left: 10px" />
+          <a-popover placement="left" trigger="click">
+            <template #content>
+              <div>
+                <span>寰�鏉ラ偖浠惰仛鍚�</span>
+                <a-switch style="margin-left: 50px" v-model:checked="checked3"> </a-switch>
+              </div>
+              <a-divider style="margin: 10px" />
+              <div>
+                <span>鍒楄〃灞曠ず鍐呭</span>
+              </div>
+              <div class="p-2">
+                <a-checkbox v-model:checked="checked">閭欢鎽樿</a-checkbox>
+              </div>
+              <div class="p-2">
+                <a-checkbox v-model:checked="checked">闄勪欢</a-checkbox>
+              </div>
+              <div style="text-align: center">
+                <a-button @click="$router.push('/email/utils')">鏇村閭璁剧疆</a-button>
+              </div>
+            </template>
+            <SettingOutlined style="margin-left: 10px" />
+          </a-popover>
+          <a-switch style="margin-left: 10px" v-model:checked="checked3">
+            <template #checkedChildren><PushpinOutlined style="color: #0a6aff" /></template>
+            <template #unCheckedChildren><PushpinOutlined /></template>
+          </a-switch>
+        </div>
+      </div>
+      <div v-if="checked" class="left-bt p-3">
+        宸查�夋嫨姝ら〉闈笂鎵�鏈� 20 灏侀偖浠� 锛� 閫夋嫨鍏ㄩ儴 335 灏侀偖浠�
+      </div>
+      <div class="p-4" style="height: 90%; overflow: hidden">
+        <a-tabs v-model:activeKey="activeKey">
+          <a-tab-pane
+            v-for="item in tabsList"
+            :key="item.key"
+            :tab="`${item.label}${item.num ? '(' + item.num + ')' : ''}`"
+            style="height: 200px"
+          >
+            <Table
+              ref="tableRef"
+              :page="pageCurrent"
+              :pageList="newList"
+              @selectAll="fnSelectAll"
+              @updateSelectAll="updateSelectAll"
+            />
+          </a-tab-pane>
+        </a-tabs>
+      </div>
+    </div>
+  </PageWrapper>
+</template>
+
+<script lang="ts" setup>
+  name: 'ListPage';
+  import {
+    SyncOutlined,
+    SettingOutlined,
+    FilterOutlined,
+    PushpinOutlined,
+  } from '@ant-design/icons-vue';
+  import pageHeadLeft from './pageHeadLeft.vue';
+  import { PageWrapper } from '@/components/Page';
+
+  import { ref, watch, defineProps, defineEmits, computed, reactive, onMounted,inject } from 'vue';
+
+  // 瀹氫箟灞炴��
+  interface Props {
+    pageList?: [];
+    pageData?:any;
+  }
+  const props = defineProps<Props>();
+  const newList = ref([]);
+  const selectAllRow = ref([]);
+  watch(
+    () => props.pageList,
+    (newValue) => {
+      newList.value = newValue;
+    },
+  );
+
+const page = computed(() => props.pageData);
+  const checked = computed(() => selectAllRow.value.length > 0);
+  const pageCurrent = ref(1);
+  const tableRef = ref();
+  const state = reactive({
+    indeterminate: false,
+    checkAll: false,
+  });
+  function fnCheckedChange(e) {
+    Object.assign(state, {
+      indeterminate: false,
+    });
+    tableRef.value[0].fnSelectAll(e.target.checked);
+    checked.value = e.target.checked;
+  }
+  function updateSelectAll(data) {
+    selectAllRow.value = data.records;
+    if (!data.isAll) {
+      state.indeterminate = true;
+      state.checkAll = false;
+      if (data.records.length === 0) {
+        state.indeterminate = false;
+      }
+    } else {
+      state.indeterminate = false;
+      state.checkAll = true;
+    }
+  }
+  const tabsList = computed(() => {
+    return [
+      {
+        key: '1',
+        label: '鍏ㄩ儴',
+        num: 0,
+      },
+      {
+        key: '2',
+        label: '瀹㈡埛',
+        num: 0,
+      },
+      {
+        key: '3',
+        label: '鍚屼簨',
+        num: 0,
+      },
+      {
+        key: '4',
+        label: '閫氳褰�',
+        num: 0,
+      },
+      {
+        key: '5',
+        label: '鍏朵粬',
+        num: 0,
+      },
+    ];
+  });
+  const activeKey = ref('1');
+  const checked3 = ref(false);
+  import Table from './table.vue';
+  onMounted(() => {
+    console.log('tableRef:', tableRef.value[0]);
+  });
+  function fnSelectAll() {
+    console.log('44444444444');
+  }
+  const emit = defineEmits(['pageChange']);
+  defineExpose({
+    fnSelectAll,
+  });
+
+  const getDataList = inject('getDataList');
+  function handlePageChange(page, pageSize){
+    getDataList(page)
+    
+  }
+</script>
+<style scoped lang="less">
+  .my-head {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    width: 100%;
+    height: 60px;
+    border-bottom: 1px solid rgb(5 5 5 / 6%);
+
+    /* 澧炲姞閫夋嫨鍣ㄧ壒寮傛�� */
+    & .left {
+      width: 20%;
+
+      & .left-box {
+        display: flex;
+        align-items: center;
+        justify-content: space-flex-start;
+        width: 100%;
+        height: 100%;;
+
+        & .icon {
+          margin-right: 15px;
+          font-size: 16px;
+        }
+      }
+    }
+
+    & .right {
+      display: flex;
+      align-items: center;
+    }
+  }
+
+  .left-bt {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    padding-left: 27px;
+    background: #fffbe6;
+  }
+</style>
diff --git a/src/views/email/components/ListPage/pageHeadLeft.vue b/src/views/email/components/ListPage/pageHeadLeft.vue
new file mode 100644
index 0000000..0280483
--- /dev/null
+++ b/src/views/email/components/ListPage/pageHeadLeft.vue
@@ -0,0 +1,166 @@
+<template>
+  <div class="left-box">
+    <!-- 鍒嗗彂 -->
+    <a-tooltip placement="bottom">
+      <template #title>
+        <span>鍒嗗彂</span>
+      </template>
+      <ExportOutlined class="icon" />
+    </a-tooltip>
+    <!-- 寰呭鐞� -->
+    <TooltipAndDropdown
+      class="icon"
+      v-show="checked"
+      :tooltipTitle="'寰呭鐞嗛偖浠�'"
+      :initialDropdownOpen="false"
+      :initialTooltipOpen="false"
+      :showTooltip="false"
+      :docCodeS="getSelectAllBocCode"
+    />
+    <!-- 鍒犻櫎 -->
+    <a-tooltip placement="bottom">
+      <template #title>
+        <span>鍒犻櫎</span>
+      </template>
+      <DeleteOutlined v-show="checked" class="icon" @click="fuDeleteEmail" />
+    </a-tooltip>
+
+    <!-- 鏀惰棌 -->
+    <a-tooltip placement="bottom">
+      <template #title>
+        <span>鏍囧織</span>
+      </template>
+      <TagOutlined class="icon" v-show="checked" />
+    </a-tooltip>
+
+    <!-- 鏂囦欢澶� -->
+    <a-tooltip placement="bottom">
+      <template #title>
+        <span>鏂囦欢澶�</span>
+      </template>
+      <FolderOutlined class="icon" v-show="checked" />
+    </a-tooltip>
+    <!-- 鏇村 -->
+    <a-tooltip placement="bottom">
+      <template #title>
+        <span>鏇村</span>
+      </template>
+      <a-dropdown :arrow="{ pointAtCenter: true }" placement="bottom" :trigger="['click']">
+        <template #overlay>
+          <a-menu>
+            <a-menu-item key="2" @click="fnSelectAllRead(true)"> 鏍囦负宸茶</a-menu-item>
+            <a-menu-item key="3" @click="fnSelectAllRead(false)"> 鏍囦负鏈</a-menu-item>
+            <a-menu-item key="4"> 璁句负缃《</a-menu-item>
+            <a-divider style="margin: 2px; padding: 2px" />
+            <a-menu-item key="5"> 鏍囪涓哄瀮鍦鹃偖浠�</a-menu-item>
+          </a-menu>
+        </template>
+        <MoreOutlined v-show="checked" class="icon" />
+      </a-dropdown>
+      <a-dropdown :arrow="{ pointAtCenter: true }" placement="bottom" :trigger="['click']">
+        <template #overlay>
+          <a-menu>
+            <a-menu-item key="1" @click="fnAllRead">鍏ㄩ儴鏍囪涓哄凡璇�</a-menu-item>
+            <a-divider style="margin: 2px; padding: 2px" />
+            <p style="color: #999; font-size: 12px">鍕鹃�夐偖浠跺嵆鍙煡鐪嬫洿澶氭搷浣�</p>
+          </a-menu>
+        </template>
+        <MoreOutlined v-show="!checked" class="icon" />
+      </a-dropdown>
+    </a-tooltip>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  import TooltipAndDropdown from './TooltipAndDropdown .vue';
+  import {
+    ExportOutlined,
+    ClockCircleOutlined,
+    DeleteOutlined,
+    TagOutlined,
+    FolderOutlined,
+    MoreOutlined,
+  } from '@ant-design/icons-vue';
+  import { ref, defineProps, defineEmits, computed, reactive, inject } from 'vue';
+  interface Props {
+    checked: boolean;
+    handleId?: number;
+    selectAllRow?: Array<any>;
+    parentTableList?: Array<any>;
+  }
+
+  const props = defineProps<Props>();
+
+  const checked = computed(() => props.checked);
+  import { updateReadApi, deleteEmailAPi } from '@/api/email/userList';
+  import { useMessage } from '@/hooks/web/useMessage';
+
+  const { createMessage } = useMessage();
+  const getDataList = inject('getDataList');
+  function fnSelectAllRead(is) {
+    const data = {
+      status: is,
+      list: getReadId(),
+    };
+    pushUpdateReadApi(data);
+  }
+  const getSelectAllBocCode = computed(() => {
+    let data = props.selectAllRow.map((item) => item.docCode);
+    console.log(data, '00003');
+
+    return data;
+  });
+  function pushUpdateReadApi(data) {
+    updateReadApi(data).then((res) => {
+      if (res.code == 0) {
+        //
+        getDataList({});
+      }
+    });
+  }
+  function fnAllRead() {
+    fnGetTableList();
+  }
+
+  function fnGetTableList() {
+    const data = {
+      status: true,
+      list: getReadId(),
+    };
+    pushUpdateReadApi(data);
+  }
+  function getReadId() {
+    const ids = [];
+    props.parentTableList.forEach((item: Record<string, any>) => {
+      ids.push(item.docCode);
+    });
+    return ids;
+  }
+  const emit = defineEmits(['nextNum']);
+  function fuDeleteEmail() {
+    deleteEmailAPi(getSelectAllBocCode.value).then((res) => {
+      if (res.code == 0) {
+        createMessage.success(res.msg);
+        // 鍖哄垎鎿嶄綔
+        getDataList({});
+        if (!props.parentTableList) {
+          emit('nextNum');
+        }
+      }
+    });
+  }
+</script>
+
+<style lang="less" scoped>
+  .left-box {
+    display: flex;
+    align-items: center;
+    justify-content: space-flex-start;
+    width: 100%;
+
+    & .icon {
+      margin-right: 15px;
+      font-size: 16px;
+    }
+  }
+</style>
diff --git a/src/views/email/components/ListPage/table.vue b/src/views/email/components/ListPage/table.vue
new file mode 100644
index 0000000..cf832e1
--- /dev/null
+++ b/src/views/email/components/ListPage/table.vue
@@ -0,0 +1,476 @@
+<template>
+  <div style="overflow: auto">
+    <div v-if="groupedEmails.length != 0">
+      <div v-for="(item, index) in groupedEmails" :key="index">
+        <div class="span-title">{{ `${item.name}(${item.data.length})` }}</div>
+        <vxe-table
+          ref="vxeTableRef"
+          style="margin: 10px 0"
+          :showHeader="false"
+          :data="item.data"
+          size="small"
+          min-height="40px"
+          :row-config="{ isCurrent: true, isHover: true }"
+          :menu-config="tableMenu"
+          @menu-click="contextMenuClickEvent"
+          @cell-click="cellClickEvent"
+          @checkbox-change="selectChangeEvent"
+        >
+          <vxe-column type="checkbox" width="30"></vxe-column>
+          <vxe-column field="sender" title="鍙戜欢浜�" data-index="sender" min-width="300px">
+            <template #default="{ row }">
+              <div style="display: flex; align-items: center">
+                <div
+                  v-if="row.mailType != 0"
+                  class="dot"
+                  :class="row.readFlag ? 'dot-color' : ''"
+                  @click.stop="fnRowUpdateRead(row)"
+                ></div>
+                <a-tooltip placement="bottom">
+                  <template #title>
+                    <span>闄岀敓浜�</span>
+                  </template>
+                  <a-avatar size="small" style="margin-right: 8px" :src="row.avatar" />
+                </a-tooltip>
+
+                <a-popover placement="bottom">
+                  <template #content>
+                    <div
+                      class="p-2"
+                      style="
+                        display: flex;
+                        align-items: center;
+                        border-bottom: 1px solid rgb(5 5 5 / 6%);
+                      "
+                    >
+                      <a-avatar size="small" style="margin-right: 8px" :src="row.avatar" />
+                      <span style="color: #000; font-weight: 700">{{ row.sender }}</span>
+                      <CopyOutlined />
+                    </div>
+                    <div class="display-flex p-2">
+                      <a-button type="link" size="small">鏂板缓瀹㈡埛</a-button>
+                      <a-dropdown>
+                        <a style="margin-right: 5px" class="ant-dropdown-link" @click.prevent>
+                          <DownOutlined />
+                        </a>
+                        <template #overlay>
+                          <a-menu>
+                            <a-menu-item>
+                              <a href="javascript:;">娣诲姞鍒板凡鏈夊鎴�</a>
+                            </a-menu-item>
+                          </a-menu>
+                        </template>
+                      </a-dropdown>
+                      <a-button type="link" size="small">娣诲姞涓虹嚎绱�</a-button>
+
+                      <a-dropdown style="margin-right: 5px">
+                        <a style="margin-right: 5px" class="ant-dropdown-link" @click.prevent>
+                          <DownOutlined />
+                        </a>
+                        <template #overlay>
+                          <a-menu>
+                            <a-menu-item>
+                              <a href="javascript:;">娣诲姞鍒伴�氳褰�</a>
+                            </a-menu-item>
+                          </a-menu>
+                        </template>
+                      </a-dropdown>
+                      <a-button type="link" size="small">寰�鏉ラ偖浠�</a-button></div
+                    >
+                  </template>
+                  <div class="title-dot" :class="row.readFlag ? 'title-dot-color' : ''">
+                    <span style="font-weight: 700">{{ row.senderName }}</span
+                    ><span style="padding: 0 8px">|</span>
+                    <span style="font-weight: 500">{{ row.sender }}</span>
+                  </div>
+                </a-popover>
+              </div>
+            </template>
+          </vxe-column>
+          <vxe-column
+            show-overflow
+            field="subject"
+            title="琛ㄩ"
+            data-index="subject"
+            min-width="250"
+          >
+            <template #default="{ row }">
+              <span
+                class="title-dot"
+                :class="row.readFlag ? 'title-dot-color' : ''"
+                style="font-weight: 500"
+                >{{ row.subject || '锛堟棤涓婚锛�' }}</span
+              >
+              -
+              <span style="color: #999">{{ row.subject }}</span>
+            </template>
+          </vxe-column>
+          <vxe-column field="action" title="Action" width="190">
+            <template #default="{ row, rowIndex }">
+              <span style="display: flex; justify-content: space-around">
+                <span>{{
+                  row.mailType !== 0
+                    ? formatToDateDay(row.receiveTime)
+                    : formatToDateDay(row.createTime)
+                }}</span>
+
+                <TooltipAndDropdown
+                  :tooltipTitle="'寰呭鐞嗛偖浠�'"
+                  :initialDropdownOpen="false"
+                  :initialTooltipOpen="false"
+                  :showTooltip="!!row.handleTime"
+                  :row="row"
+                  :docCodeS="[row.docCode]"
+                />
+                <span style="margin-left: 5px"><PushpinOutlined @click.stop="fnTagging" /></span>
+              </span>
+            </template>
+          </vxe-column>
+        </vxe-table> 
+        </div
+    ></div>
+
+    <div v-else style="height: 70vh; display: flex; align-items: center; justify-content: center">
+      <a-empty />
+    </div>
+    <DrawerDetail
+      ref="drawerDetailRef"
+      v-model="openDrawerDetail"
+      :mailId="rowMailId"
+      :selectAllRow="selectRow"
+      :allList="dataSource"
+    />
+    <a-dropdown :trigger="['click']" placement="bottomLeft" ref="dropdownRefs"> </a-dropdown>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  name: 'ListPageTable';
+  import {
+    FieldTimeOutlined,
+    PushpinOutlined,
+    CopyOutlined,
+    DownOutlined,
+  } from '@ant-design/icons-vue';
+
+  import { ref, watch, defineProps, defineEmits, computed, defineExpose, inject } from 'vue';
+
+  // 瀹氫箟灞炴��
+  interface Props {
+    pageList: [];
+  }
+  const props = defineProps<Props>();
+
+  const groupedEmails = ref<GroupedDataItem[]>([]);
+
+  const dataSource = ref([]);
+  watch(
+    () => props.pageList,
+    (newValue) => {
+      dataSource.value = newValue || [];
+      groupedEmails.value = groupEmailsByDate(newValue || []);
+    },
+  );
+  import dayjs from 'dayjs';
+  import isSameOrAfter from 'dayjs/plugin/isSameOrAfter';
+  import isSameOrBefore from 'dayjs/plugin/isSameOrBefore';
+  import isoWeek from 'dayjs/plugin/isoWeek';
+  dayjs.extend(isSameOrAfter);
+  dayjs.extend(isSameOrBefore);
+  dayjs.extend(isoWeek);
+
+  interface EmailItem {
+    id: number;
+    subject: string;
+  }
+
+  // 纭繚 groupedData 鐨勭粨鏋勬纭笖 data 鏄� EmailItem 绫诲瀷鐨勬暟缁�
+  interface GroupedDataItem {
+    key: string;
+    data: EmailItem[];
+    name: string;
+  }
+
+  function groupEmailsByDate(dataSource) {
+    const today = dayjs();
+    const yesterday = dayjs().subtract(1, 'day');
+    const startOfWeek = dayjs().startOf('week');
+    const startOfMonth = dayjs().startOf('month');
+    const startOfLastMonth = dayjs().subtract(1, 'month').startOf('month');
+    const endOfLastMonth = dayjs().subtract(1, 'month').endOf('month');
+
+    const groupedData: GroupedDataItem[] = [
+      {
+        data: [],
+        name: '浠婂ぉ',
+        key: 'today',
+      },
+      {
+        data: [],
+        name: '鏄ㄥぉ',
+        key: 'yesterday',
+      },
+      {
+        data: [],
+        name: '鏈懆',
+        key: 'thisWeek',
+      },
+      {
+        data: [],
+        name: '杩欎釜鏈�',
+        key: 'thisMonth',
+      },
+      {
+        data: [],
+        name: '涓婁釜鏈�',
+        key: 'lastMonth',
+      },
+      {
+        data: [],
+        name: '鏇存棭',
+        key: 'earlier',
+      },
+    ];
+
+    dataSource.forEach((item: any) => {
+      try {
+        const emailDate = dayjs(item.createTime);
+        if (emailDate.isSame(today, 'day')) {
+          groupedData[0].data.push(item);
+        } else if (emailDate.isSame(yesterday, 'day')) {
+          groupedData[1].data.push(item);
+        } else if (emailDate.isSameOrAfter(startOfWeek) && emailDate.isBefore(today, 'day')) {
+          groupedData[2].data.push(item);
+        } else if (emailDate.isSameOrAfter(startOfMonth) && emailDate.isBefore(today, 'day')) {
+          groupedData[3].data.push(item);
+        } else if (
+          emailDate.isSameOrAfter(startOfLastMonth) &&
+          emailDate.isSameOrBefore(endOfLastMonth)
+        ) {
+          groupedData[4].data.push(item);
+        } else {
+          groupedData[5].data.push(item);
+        }
+      } catch (error) {
+        console.error(`Error processing item date: ${item.date}`, error);
+      }
+    });
+    // 灏嗙粨鏋滄寜涓枃鏄犲皠杩涜杩斿洖
+    const result = <{ data: any; name: string; key: string }[]>[];
+    groupedData.forEach((group: { data: any; name: string; key: string }) => {
+      if (group.data.length > 0) {
+        result.push(group);
+      }
+    });
+
+    return result;
+  }
+
+  // 鍙抽敭鑿滃崟
+  const tableMenu = {
+    className: 'my-menus',
+    body: {
+      options: [
+        [
+          {
+            code: 'reply',
+            name: '鍥炲',
+          },
+          { code: 'replyAll', name: '鍥炲鍏ㄩ儴' },
+          { code: 'replyWithAttachment', name: '甯﹂檮浠跺洖澶�' },
+          { code: 'replyAllWithAttachment', name: '甯﹂檮浠跺洖澶嶅叏閮�' },
+          { code: 'forward', name: '杞彂' },
+          { code: 'forwardAsAttachment', name: '浣滀负闄勪欢杞彂' },
+          { code: 'distribute', name: '鍒嗗彂' },
+          { code: 'setRemark', name: '璁剧疆澶囨敞' },
+        ],
+        [
+          { code: 'toHandle', name: '寰呭鐞�' },
+          { code: 'markAsUnread', name: '鏍囦负鏈' },
+          { code: 'labelAs', name: '鏍囨敞涓�' },
+        ],
+        [
+          { code: 'newRule', name: '鏂板缓鏀跺彂浠惰鍒�' },
+          { code: 'moveTo', name: '绉诲姩鍒�' },
+        ],
+        [
+          { code: 'exportEmail', name: '瀵煎嚭閭欢' },
+          { code: 'createFollowUp', name: '寤轰负瀹㈡埛璺熻繘' },
+          { code: 'createSchedule', name: '鏂板缓鏃ョ▼' },
+        ],
+        [
+          { code: 'markAsSpam', name: '鏍囦负鍨冨溇閭欢' },
+          { code: 'delete', name: '鍒犻櫎' },
+        ],
+      ],
+    },
+  };
+
+  function contextMenuClickEvent({ menu, row, column }) {
+    switch (menu.code) {
+      case 'copy':
+        if (row && column) {
+        }
+        break;
+      default:
+    }
+  }
+  const vxeTableRef = ref();
+  function fnSelectAll(is) {
+    vxeTableRef.value.forEach((row) => {
+      row.setAllCheckboxRow(is);
+    });
+    selectChangeEvent();
+  }
+
+  function selectChangeEvent() {
+    const isAll = getCheckboxRecords().length === dataSource.value.length;
+    const data = {
+      isAll,
+      records: getCheckboxRecords(),
+    };
+    emit('updateSelectAll', data);
+  }
+  function getCheckboxRecords() {
+    const list = new Set();
+
+    vxeTableRef.value.forEach((row) => {
+      const records = row.getCheckboxRecords(); // 鍋囪璇ユ柟娉曡繑鍥炰竴涓暟缁�
+      if (Array.isArray(records)) {
+        // 纭繚 records 鏄暟缁�
+        records.forEach((record) => list.add(record)); // 灏嗚褰曟坊鍔犲埌 Set 涓�
+      }
+    });
+
+    return Array.from(list); // 灏� Set 杞崲鍥炴暟缁�
+  }
+  // 鎿嶄綔row
+  function fnProcessingTime(row) {
+    console.log(row);
+  }
+  function fnTagging(row) {
+    console.log(row);
+  }
+  import DrawerDetail from './drawerDetail.vue';
+  // 璇︽儏鍐呭
+  const openDrawerDetail = ref(false);
+  const rowMailId = ref('');
+  const selectRow = ref([]);
+  const cellClickEvent = (event) => {
+    selectRow.value = [];
+    rowMailId.value = event.row.docCode;
+    selectRow.value.push({ docCode: event.row.docCode });
+    openDrawerDetail.value = true;
+  };
+
+  // 鏇存柊绁栫埗缁勪欢鏁版嵁
+  const getDataList = inject('getDataList');
+  console.log(getDataList, '0000004');
+
+  import { updateReadApi, updateHandleAPi } from '@/api/email/userList';
+  // 鏍囧織鏈/缁忚
+  function fnRowUpdateRead(row) {
+    const data = {
+      status: !row.readFlag,
+      list: [row.docCode],
+    };
+    pushReadApi(data);
+  }
+  function pushReadApi(params) {
+    updateReadApi(params).then((res) => {
+      if (res.code == 0) {
+        //
+        getDataList({});
+      }
+    });
+  }
+  const emit = defineEmits(['selectAll', 'updateSelectAll']);
+  defineExpose({
+    fnSelectAll,
+  });
+
+  import TooltipAndDropdown from './TooltipAndDropdown .vue';
+  import { formatToDateDay } from '@/utils/dateUtil';
+</script>
+<style scoped lang="less">
+  .display-flex {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+  }
+
+  .head {
+    display: flex;
+    justify-content: space-between;
+    width: 100%;
+    border-bottom: 1px solid rgb(5 5 5 / 6%);
+
+    /* 澧炲姞閫夋嫨鍣ㄧ壒寮傛�� */
+    & .left {
+      width: 20%;
+
+      & .left-box {
+        display: flex;
+        align-items: center;
+        justify-content: space-flex-start;
+        width: 100%;
+
+        & .icon {
+          margin-right: 15px;
+          font-size: 16px;
+        }
+      }
+    }
+
+    & .right {
+      display: flex;
+      align-items: center;
+    }
+  }
+
+  .left-bt {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    padding-left: 27px;
+    background: #fffbe6;
+  }
+
+  .span-title {
+    width: 100%;
+    padding: 5px;
+    color: #000;
+    font-weight: 700;
+    text-align: left;
+  }
+
+  .table {
+    height: 80vh;
+  }
+
+  .my-menus {
+    background-color: #f8f8f9;
+  }
+  // 鍦嗙偣
+  .dot {
+    display: inline-block;
+    width: 8px;
+    height: 8px;
+    margin-right: 10px;
+    border-radius: 50%;
+    background-color: #0a6aff;
+  }
+
+  .dot-color {
+    background-color: #d9d9d9;
+    color: #d9d9d9;
+  }
+
+  .title-dot {
+    color: #0a6aff;
+  }
+
+  .title-dot-color {
+    color: #999;
+  }
+</style>
diff --git a/src/views/email/components/SelectUser.vue b/src/views/email/components/SelectUser.vue
deleted file mode 100644
index 7d9dffb..0000000
--- a/src/views/email/components/SelectUser.vue
+++ /dev/null
@@ -1,261 +0,0 @@
-<!-- Props:
-
-modelValue: 鎺у埗鎶藉眽鐨勬墦寮�涓庡叧闂��
-title: 鎶藉眽鏍囬锛岄粯璁や负Basic Drawer銆�
-placement: 鎶藉眽浣嶇疆锛岄粯璁や负right銆�
-Emits:
-
-update:modelValue: 褰撳唴閮ㄧ姸鎬佹敼鍙樻椂锛屾洿鏂板閮ㄧ粦瀹氱殑modelValue銆�
-璁$畻灞炴��:
-
-title 鍜� placement 鐢ㄤ簬鑾峰彇灞炴�у�兼垨榛樿鍊笺��
-鍐呴儴鐘舵��:
-
-drawerOpen 鐢ㄤ簬鎺у埗鎶藉眽鐨勭姸鎬併��
-鏂规硶:
-
-showDrawer 鐢ㄤ簬鏄剧ず鎶藉眽銆� -->
-
-<template>
-  <a-drawer
-    v-model:open="drawerOpen"
-    :title="title"
-    :placement="placement"
-    :width="1200"
-    :body-style="{ paddingBottom: '80px' }"
-    :footer-style="{ textAlign: 'right' }"
-    @after-open-change="afterOpenChange"
-    @close="afterOpenChange"
-  >
-    <a-form :model="form" :rules="rules" layout="vertical">
-      <a-row :gutter="16">
-        <a-col :span="12">
-          <a-form-item label="鍏抽敭瀛�">
-            <a-input v-model:value="form.keyword" placeholder="缂栧彿/鑱旂郴浜哄悕绉�" />
-          </a-form-item>
-        </a-col>
-      </a-row>
-    </a-form>
-    <div class="table-content">
-      <div class="left">
-        <a-card :bordered="false" :title="`鑱旂郴浜猴紙${selectContactNum}锛塦" style="height: 500px">
-          <template #extra>
-            <a-checkbox v-model:checked="selectAllCurrentPage" @change="fnSelectAllCurrentPage"
-              >鍏ㄩ�夊綋鍓嶉〉</a-checkbox
-            >
-            <a-checkbox v-model:checked="selectEmail" @change="fnSelectEmail"
-              >閫夋嫨鍓�30涓偖绠�</a-checkbox
-            ></template
-          >
-          <a-table
-            :showHeader="false"
-            :data-source="dataSource"
-            :row-selection="{ selectedRowKeys: state.selectedRowKeys, onChange: onSelectChange }"
-            :scroll="{ y: 300 }"
-            size="small"
-            :pagination="{ pageSize: 100 }"
-            rowKey="id"
-          >
-            <a-table-column key="name" title="name" data-index="name">
-              <template #default="{ record }">
-                <span>{{ record.name }}</span
-                ><a>{{ record.address }}</a>
-              </template>
-            </a-table-column>
-          </a-table>
-        </a-card>
-      </div>
-      <div class="right">
-        <a-card :bordered="false" :title="`宸查�夎仈绯讳汉锛�${contactNum}/100锛塦" style="height: 500px">
-          <template #extra><a @click="fnClearSelect">娓呯┖閫夐」</a></template>
-          <a-table
-            :showHeader="false"
-            :data-source="tempDataSource"
-            :pagination="false"
-            :scroll="{ y: 355 }"
-            size="small"
-          >
-            <a-table-column key="name" title="name" data-index="name">
-              <template #default="{ record }">
-                <span>{{ record.name }}</span
-                ><a>{{ record.address }}</a>
-              </template>
-            </a-table-column>
-          </a-table>
-        </a-card>
-      </div>
-    </div>
-    <template #extra>
-      <a-space>
-        <a-button @click="afterOpenChange">鍙栨秷</a-button>
-        <a-button type="primary" @click="fnSaveOpenChange">淇濆瓨</a-button>
-      </a-space>
-    </template>
-  </a-drawer>
-</template>
-
-<script lang="ts" setup>
-  import { ref, watch, defineProps, defineEmits, computed, reactive } from 'vue';
-  // 瀹氫箟缁勪欢鍚嶇О
-  defineOptions({ name: 'SelectUser' });
-
-  // 瀹氫箟灞炴��
-  interface Props {
-    modelValue: boolean;
-    title?: string;
-    placement?: 'left' | 'right' | 'top' | 'bottom';
-    idName?: string;
-    selectIds?: number[];
-  }
-  const props = defineProps<Props>();
-
-  // 瀹氫箟浜嬩欢
-  const emit = defineEmits(['update:modelValue', 'updateData']);
-
-  // 鍐呴儴鐘舵��
-  const drawerOpen = ref(props.modelValue);
-
-  // 鐩戝惉灞炴�у彉鍖�
-  watch(
-    () => props.modelValue,
-    (newValue) => {
-      drawerOpen.value = newValue;
-      if (props.selectIds && props.selectIds.length > 0) {
-        state.selectedRowKeys = props.selectIds;
-        updateTempDataSource(props.selectIds);
-      } else {
-        state.selectedRowKeys = [];
-        updateTempDataSource([]);
-      }
-    },
-  );
-
-  // 鏇存柊澶栭儴灞炴��
-  watch(drawerOpen, (newValue) => {
-    emit('update:modelValue', newValue);
-  });
-  import { getUserListApi } from '@/api/email/userList';
-
-  // 鏂规硶
-  const afterOpenChange = (bool: boolean) => {
-    if (bool) {
-      fnGetUserList({ page: 1, pageSize: 30 });
-    }
-  };
-  const fnGetUserList = (params) =>
-    getUserListApi(params).then((res) => {
-      if (res && res.items && Array.isArray(res.items)) {
-        dataSource.value = res.items;
-        selectContactNum.value = res.total;
-      } else {
-        console.error('Invalid response format:', res);
-      }
-    });
-  // 璁$畻灞炴��
-  const title = computed(() => props.title || 'Basic Drawer');
-  const placement = computed(() => props.placement || 'right');
-  const idName = computed(() => props.idName || 'recipients');
-
-  // // 鏄剧ず鎶藉眽鐨勬柟娉�
-  // const showDrawer = () => {
-  //   drawerOpen.value = true;
-  // };
-
-  const form = ref({
-    keyword: '',
-  });
-  const rules = ref({
-    keyword: [{ required: true, message: '鍏抽敭瀛椾笉鑳戒负绌�', trigger: 'blur' }],
-  });
-  const selectAllCurrentPage = ref(false);
-  const selectEmail = ref(false);
-  const fnSelectAllCurrentPage = (e) => {
-    const temp = dataSource.value;
-    if (selectAllCurrentPage.value) {
-      state.selectedRowKeys = temp.map((item) => item.id);
-    } else {
-      state.selectedRowKeys = state.selectedRowKeys.filter(
-        (item) => !temp.some((t) => t.id === item),
-      );
-    }
-    updateTempDataSource(state.selectedRowKeys);
-  };
-
-  const fnSelectEmail = (e) => {
-    const temp = dataSource.value.slice(0, 30);
-    if (selectEmail.value) {
-      state.selectedRowKeys = temp.map((item) => item.id);
-    } else {
-      state.selectedRowKeys = state.selectedRowKeys.filter(
-        (item) => !temp.some((t) => t.id === item),
-      );
-    }
-    updateTempDataSource(state.selectedRowKeys);
-  };
-  const updateTempDataSource = (selectedRowKeys: Key[]) => {
-    tempDataSource.value = dataSource.value.filter((item) => selectedRowKeys.includes(item.id));
-    contactNum.value = selectedRowKeys.length;
-  };
-
-  const contactNum = ref(0);
-  const selectContactNum = ref(0);
-
-  const dataSource = ref([]);
-
-  type Key = string | number;
-  const state = reactive<{
-    selectedRowKeys: Key[];
-    loading: boolean;
-  }>({
-    selectedRowKeys: [], // Check here to configure the default column
-    loading: false,
-  });
-  const tempDataSource = ref([]);
-
-  const fnClearSelect = () => {
-    state.selectedRowKeys = [];
-    tempDataSource.value = [];
-    contactNum.value = 0;
-  };
-  const onSelectChange = (selectedRowKeys: Key[]) => {
-    console.log('selectedRowKeys changed: ', selectedRowKeys);
-    state.selectedRowKeys = selectedRowKeys;
-    tempDataSource.value = dataSource.value.filter((item) => selectedRowKeys.includes(item.id));
-    contactNum.value = selectedRowKeys.length;
-  };
-  const fnSaveOpenChange = () => {
-    console.log('selectedRowKeys changed: ', state.selectedRowKeys);
-    drawerOpen.value = false;
-    emit('update:modelValue', false);
-    const data = {
-      selectedRowKeys: state.selectedRowKeys,
-      idName: idName.value,
-    };
-    emit('updateData', data);
-  };
-</script>
-
-<style scoped lang="less">
-  /* 鏍峰紡鍙互鍦ㄨ繖閲屽畾涔� */
-
-  .table-content {
-    display: flex;
-    justify-content: space-between;
-    border-top: 1px solid #f0f0f0;
-
-    .left {
-      width: 50%;
-      height: 100%;
-      border-right: 1px solid #f0f0f0;
-    }
-
-    .right {
-      width: 50%;
-      height: 100%;
-    }
-  }
-
-  ::v-deep(.ant-table) {
-    min-height: 355px !important;
-  }
-</style>
diff --git a/src/views/email/components/SelectUser/index.vue b/src/views/email/components/SelectUser/index.vue
new file mode 100644
index 0000000..d8fe0a7
--- /dev/null
+++ b/src/views/email/components/SelectUser/index.vue
@@ -0,0 +1,367 @@
+<!-- Props:
+
+modelValue: 鎺у埗鎶藉眽鐨勬墦寮�涓庡叧闂��
+title: 鎶藉眽鏍囬锛岄粯璁や负Basic Drawer銆�
+placement: 鎶藉眽浣嶇疆锛岄粯璁や负right銆�
+Emits:
+
+update:modelValue: 褰撳唴閮ㄧ姸鎬佹敼鍙樻椂锛屾洿鏂板閮ㄧ粦瀹氱殑modelValue銆�
+璁$畻灞炴��:
+
+title 鍜� placement 鐢ㄤ簬鑾峰彇灞炴�у�兼垨榛樿鍊笺��
+鍐呴儴鐘舵��:
+
+drawerOpen 鐢ㄤ簬鎺у埗鎶藉眽鐨勭姸鎬併��
+鏂规硶:
+
+showDrawer 鐢ㄤ簬鏄剧ず鎶藉眽銆� -->
+
+<template>
+  <a-drawer
+    v-model:open="drawerOpen"
+    :title="title"
+    :placement="placement"
+    :width="1200"
+    :body-style="{ paddingBottom: '80px' }"
+    :footer-style="{ textAlign: 'right' }"
+    @after-open-change="afterOpenChange"
+    @close="afterOpenChange"
+  >
+    <a-tabs v-model:activeKey="activeKey" @change="handleTabChange">
+      <a-tab-pane :key="item.key" :tab="item.title" v-for="item in tabsList">
+        <a-form :model="form" :rules="rules" layout="vertical">
+          <a-row :gutter="16">
+            <a-col :span="12">
+              <a-form-item label="鍏抽敭瀛�">
+                <a-input v-model:value="form.keyword" placeholder="缂栧彿/鑱旂郴浜哄悕绉�" />
+              </a-form-item>
+            </a-col>
+          </a-row>
+        </a-form>
+        <div class="table-content">
+          <div class="left">
+            <a-card
+              :bordered="false"
+              :title="`鑱旂郴浜猴紙${selectContactNum}锛塦"
+              style="height: 500px"
+            >
+              <template #extra>
+                <a-checkbox v-model:checked="selectAllCurrentPage" @change="fnSelectAllCurrentPage"
+                  >鍏ㄩ�夊綋鍓嶉〉</a-checkbox
+                >
+                <a-checkbox v-model:checked="selectEmail" @change="fnSelectEmail"
+                  >閫夋嫨鍓�30涓偖绠�</a-checkbox
+                ></template
+              >
+              <a-table
+                :showHeader="false"
+                :data-source="dataSource"
+                :row-selection="{
+                  selectedRowKeys: state.selectedRowKeys,
+                  onChange: onSelectChange,
+                }"
+                :scroll="{ y: 300 }"
+                size="small"
+                rowKey="userCode"
+              >
+                <!-- :pagination="{ pageSize: 100 }" -->
+                <a-table-column key="userName" title="name" data-index="name">
+                  <template #default="{ record }">
+                    <span>{{ record.userName }}</span
+                    ><span>{{ `<${record.email}>` }}</span>
+                    <span style="margin-left: 5px">{{ record.ccName }}</span>
+                  </template>
+                </a-table-column>
+              </a-table>
+            </a-card>
+          </div>
+          <div class="right">
+            <a-card
+              :bordered="false"
+              :title="`宸查�夎仈绯讳汉锛�${contactNum}/100锛塦"
+              style="height: 500px"
+            >
+              <template #extra><a @click="fnClearSelect">娓呯┖閫夐」</a></template>
+              <a-table
+                :showHeader="false"
+                :data-source="tempDataSource"
+                :pagination="false"
+                :scroll="{ y: 355 }"
+                size="small"
+              >
+                <a-table-column key="userName" title="name" data-index="name">
+                  <template #default="{ record }">
+                    <span>{{ record.userName }}</span
+                    ><span>{{ `<${record.email}>` }}</span>
+                    <!-- <span style="margin-left: 5px">{{ record.ccName }}</span> -->
+                  </template>
+                </a-table-column>
+              </a-table>
+            </a-card>
+          </div>
+        </div>
+      </a-tab-pane>
+    </a-tabs>
+
+    <template #extra>
+      <a-space>
+        <a-button @click="afterOpenChange">鍙栨秷</a-button>
+        <a-button type="primary" @click="fnSaveOpenChange">淇濆瓨</a-button>
+      </a-space>
+    </template>
+  </a-drawer>
+</template>
+
+<script lang="ts" setup>
+  import { ref, watch, defineProps, defineEmits, computed, reactive } from 'vue';
+  import { getUserInfoApi, contactListAPi, emailListAPi } from '@/api/email/userList';
+  // 瀹氫箟缁勪欢鍚嶇О
+  defineOptions({ name: 'SelectUser' });
+
+  // 瀹氫箟灞炴��
+  interface Props {
+    modelValue: boolean;
+    title?: string;
+    placement?: 'left' | 'right' | 'top' | 'bottom';
+    idName?: string;
+    selectIds?: number[];
+  }
+  const props = defineProps<Props>();
+
+  const activeKey = ref('3');
+  const tabsList = ref([
+    {
+      key: '1',
+      title: '瀹㈡埛鑱旂郴浜�',
+    },
+    {
+      key: '2',
+      title: '绾跨储鑱旂郴浜�',
+    },
+    {
+      key: '3',
+      title: '鍟嗘満鑱旂郴浜�',
+    },
+    {
+      key: '4',
+      title: '浼佷笟鍚屼簨',
+    },
+    {
+      key: '5',
+      title: '涓汉閫氳褰�',
+    },
+    {
+      key: '6',
+      title: '浼佷笟閫氳褰�',
+    },
+  ]);
+
+  // 瀹氫箟浜嬩欢
+  const emit = defineEmits(['update:modelValue', 'updateData', 'sudUserList']);
+  const gutUserApiList = ref([
+    { key: '3', api: emailListAPi },
+    { key: '2', api: contactListAPi },
+  ]);
+
+  const gutUserApi = ref<Record<string, any>>(emailListAPi);
+  function handleTabChange(key: string) {
+    gutUserApi.value = gutUserApiList.value.find((item) => item.key === key)?.api || emailListAPi;
+    fnGetUserList();
+  }
+
+  function fnGetUserList() {
+    gutUserApi.value({ key: '' }).then((res) => {
+      if (res.state === 0) {
+        dataSource.value = res.data;
+        if (res.data.data) {
+          dataSource.value = res.data.data;
+        }
+        selectContactNum.value = dataSource.value.length;
+        // emit('sudUserList', dataSource.value);
+      }
+    });
+  }
+  // 鍐呴儴鐘舵��
+  const drawerOpen = ref(props.modelValue);
+
+  // 鐩戝惉灞炴�у彉鍖�
+  watch(
+    () => props.modelValue,
+    (newValue) => {
+      drawerOpen.value = newValue;
+      if (props.selectIds && props.selectIds.length > 0) {
+        state.selectedRowKeys = props.selectIds;
+        updateTempDataSource(props.selectIds);
+      } else {
+        state.selectedRowKeys = [];
+        updateTempDataSource([]);
+      }
+    },
+  );
+
+  // 鏇存柊澶栭儴灞炴��
+  watch(drawerOpen, (newValue) => {
+    emit('update:modelValue', newValue);
+  });
+  // 鏂规硶
+  const afterOpenChange = (bool: boolean) => {
+    if (bool) {
+      fnGetUserList();
+    }
+  };
+  interface User {
+    ccCode: string;
+    ccName: string;
+    userCode: string;
+    userName: string;
+    email: string;
+  }
+  // const fnGetUserList = (params) =>
+  //   getUserInfoApi(params).then((res) => {
+  //     if (res && res.data && Array.isArray(res.data)) {
+  //       dataSource.value = flattenAndDeduplicateData(res.data) || ([] as User[]);
+  //       selectContactNum.value = dataSource.value.length;
+  //       emit('sudUserList', dataSource.value);
+  //     } else {
+  //       console.error('Invalid response format:', res);
+  //     }
+  //   });
+
+  function flattenAndDeduplicateData(data) {
+    const result: Record<string, any>[] = [];
+    const userCodeSet = new Set(); // 鐢ㄤ簬璁板綍宸茬粡鍑虹幇杩囩殑 userCode
+
+    data.forEach((department) => {
+      const { ccCode, ccName, subList } = department;
+
+      if (subList && subList.length > 0) {
+        subList.forEach((user) => {
+          // 濡傛灉 userCode 涓嶅湪 Set 涓紝鎵嶆坊鍔犲埌缁撴灉涓�
+          if (!userCodeSet.has(user.userCode)) {
+            result.push({
+              ccCode: user.ccCode || ccCode, // 浣跨敤瀛愰」鎴栫埗椤圭殑 ccCode
+              ccName: user.ccName || ccName, // 浣跨敤瀛愰」鎴栫埗椤圭殑 ccName
+              userCode: user.userCode,
+              userName: user.userName,
+              id: user.userCode,
+              email: user.email || '', // 纭繚 email 涓嶄负 null
+            });
+            // 灏� userCode 鍔犲叆 Set锛岄伩鍏嶉噸澶�
+            userCodeSet.add(user.userCode);
+          }
+        });
+      }
+    });
+
+    return result;
+  }
+  // 璁$畻灞炴��
+  const title = computed(() => props.title || 'Basic Drawer');
+  const placement = computed(() => props.placement || 'right');
+  const idName = computed(() => props.idName || 'recipients');
+
+  // // 鏄剧ず鎶藉眽鐨勬柟娉�
+  // const showDrawer = () => {
+  //   drawerOpen.value = true;
+  // };
+
+  const form = ref({
+    keyword: '',
+  });
+  const rules = ref({
+    keyword: [{ required: true, message: '鍏抽敭瀛椾笉鑳戒负绌�', trigger: 'blur' }],
+  });
+  const selectAllCurrentPage = ref(false);
+  const selectEmail = ref(false);
+  const fnSelectAllCurrentPage = (e) => {
+    const temp = dataSource.value;
+    if (selectAllCurrentPage.value) {
+      state.selectedRowKeys = temp.map((item) => item.userCode);
+    } else {
+      state.selectedRowKeys = state.selectedRowKeys.filter(
+        (item) => !temp.some((t) => t.userCode === item),
+      );
+    }
+    updateTempDataSource(state.selectedRowKeys);
+  };
+
+  const fnSelectEmail = (e) => {
+    const temp = dataSource.value.slice(0, 30);
+    if (selectEmail.value) {
+      state.selectedRowKeys = temp.map((item) => item.userCode);
+    } else {
+      state.selectedRowKeys = state.selectedRowKeys.filter(
+        (item) => !temp.some((t) => t.userCode === item),
+      );
+    }
+    updateTempDataSource(state.selectedRowKeys);
+  };
+  const updateTempDataSource = (selectedRowKeys: Key[]) => {
+    tempDataSource.value = dataSource.value.filter((item) => selectedRowKeys.includes(item.userCode));
+    contactNum.value = selectedRowKeys.length;
+  };
+
+  const contactNum = ref(0);
+  const selectContactNum = ref(0);
+
+  const dataSource = ref<Record<string, any>>([]);
+
+  type Key = string | number;
+  const state = reactive<{
+    selectedRowKeys: Key[];
+    loading: boolean;
+  }>({
+    selectedRowKeys: [], // Check here to configure the default column
+    loading: false,
+  });
+  const tempDataSource = ref([]);
+
+  const fnClearSelect = () => {
+    state.selectedRowKeys = [];
+    tempDataSource.value = [];
+    contactNum.value = 0;
+  };
+  const onSelectChange = (selectedRowKeys: Key[]) => {
+    
+    state.selectedRowKeys = dataSource.value.filter((item) => selectedRowKeys.includes(item.userCode));;
+    console.log('selectedRowKeys changed: ',  state.selectedRowKeys);
+
+    tempDataSource.value = dataSource.value.filter((item) => selectedRowKeys.includes(item.userCode));
+    contactNum.value = selectedRowKeys.length;
+  };
+  const fnSaveOpenChange = () => {
+    console.log('selectedRowKeys changed: ', state.selectedRowKeys);
+    drawerOpen.value = false;
+    emit('update:modelValue', false);
+    const data = {
+      selectedRowKeys: state.selectedRowKeys,
+      idName: idName.value,
+    };
+    emit('updateData', data);
+  };
+</script>
+
+<style scoped lang="less">
+  /* 鏍峰紡鍙互鍦ㄨ繖閲屽畾涔� */
+
+  .table-content {
+    display: flex;
+    justify-content: space-between;
+    border-top: 1px solid #f0f0f0;
+
+    .left {
+      width: 50%;
+      height: 100%;
+      border-right: 1px solid #f0f0f0;
+    }
+
+    .right {
+      width: 50%;
+      height: 100%;
+    }
+  }
+
+  ::v-deep(.ant-table) {
+    min-height: 355px !important;
+  }
+</style>
diff --git a/src/views/email/components/emailDetail.vue b/src/views/email/components/emailDetail.vue
new file mode 100644
index 0000000..2b41ec9
--- /dev/null
+++ b/src/views/email/components/emailDetail.vue
@@ -0,0 +1,11 @@
+<template>
+  <PageWrapper dense contentFullHeight fixedHeight>
+    888
+  </PageWrapper>
+</template>
+
+<script lang="ts" setup>
+import { PageWrapper } from '@/components/Page';
+</script>
+
+<style lang="less" scoped></style>
diff --git a/src/views/email/index.vue b/src/views/email/index.vue
index 2fd0db9..a3186f6 100644
--- a/src/views/email/index.vue
+++ b/src/views/email/index.vue
@@ -1,45 +1,167 @@
-锘�<template>
-  <PageWrapper :class="`${prefixCls}`" dense contentFullHeight fixedHeight>
-
-    <Splitpanes class="default-theme" :push-other-panes="false" style="height: 100%">
-      <Pane min-size="12" size="12">
-        <LeftNav></LeftNav>
-      </Pane>
-      <Pane min-size="50" size="88" >
-        <ScrollContainer class="p-4">
-          <Editor></Editor>
-        </ScrollContainer>
-      </Pane>
-      <!-- <Pane min-size="5" size="15">鍙�</Pane> -->
-    </Splitpanes>
-  </PageWrapper>
+锘�
+<template>
+  <div>
+      <a-layout class="default-theme" style="display: flex; height: 100%; background-color: #fff">
+        <a-layout-sider width="250"  style="border-inline-end: 1px solid rgb(5 5 5 / 6%)" :style="siderStyle"  v-show="collapseStore.isOpen">
+          <LeftNav></LeftNav>
+        </a-layout-sider>
+        <a-layout>
+        <a-layout-content :style="contentStyle">
+          <RouterView>
+            <template #default="{ Component, route }">
+              <transition
+                :name="
+                  getTransitionName({
+                    route,
+                    openCache,
+                    enableTransition: getEnableTransition,
+                    cacheTabs: getCaches,
+                    def: getBasicTransition,
+                  })
+                "
+                mode="out-in"
+                appear
+              >
+                <keep-alive v-if="openCache" :include="getCaches">
+                  <component :is="Component" :key="route.fullPath" />
+                </keep-alive>
+                <component v-else :is="Component" :key="route.fullPath" />
+              </transition>
+            </template>
+          </RouterView>
+        </a-layout-content>
+      </a-layout>
+      </a-layout>
+    <div @click="fuToggleContent(!collapseStore.isOpen)" class="toggle-btn" :class="collapseStore.isOpen ? 'iconOpen' : 'onIconOpen'">
+      <LeftOutlined v-if="collapseStore.isOpen" />
+      <RightOutlined v-else />
+    </div>
+    <FrameLayout v-if="getCanEmbedIFramePage" />
+  </div>
 </template>
 
 <script lang="ts" setup>
-import {PageWrapper} from '@/components/Page';
-import {onMounted} from 'vue';
-import {useDesign} from "@/hooks/web/useDesign";
-import {Splitpanes, Pane} from 'splitpanes';
-import 'splitpanes/dist/splitpanes.css';
-import LeftNav from './components/LeftNav.vue';
-import Editor from './components/Editor.vue';
-import ScrollContainer from "@/components/Container/src/ScrollContainer.vue";
+  import { PageWrapper } from '@/components/Page';
+  import { LeftOutlined, RightOutlined } from '@ant-design/icons-vue';
+  import LeftNav from '@/views/email/components/LeftNav.vue';
+  import { computed, unref, ref } from 'vue';
 
-onMounted(() => {
-  Logger.log('Hello, Logger!1');
-});
-const {prefixCls} = useDesign('email');
+  import FrameLayout from '@/layouts/iframe/index.vue';
 
-</script>
+  import { useRootSetting } from '@/hooks/setting/useRootSetting';
 
-<style lang="less" scoped>
-@prefix-cls: ~'@{namespace}-email';
-.@{prefix-cls} {
-  .splitpanes__pane {
-    display: flex;
-    justify-content: center;
-    align-items: center;
-    background-color: var(--component-background-color)
+  import { useTransitionSetting } from '@/hooks/setting/useTransitionSetting';
+  import { useMultipleTabSetting } from '@/hooks/setting/useMultipleTabSetting';
+
+  import { useMultipleTabStore } from '@/store/modules/multipleTab';
+
+  defineOptions({ name: 'PageLayout' });
+  import type { FunctionalComponent } from 'vue';
+  import type { RouteLocation } from 'vue-router';
+
+  export interface DefaultContext {
+    Component: FunctionalComponent & { type: Recordable };
+    route: RouteLocation;
   }
-}
+  const siderStyle = {
+    lineHeight: '120px',
+    backgroundColor:'#fff',
+  };
+  const contentStyle = {
+  lineHeight: '120px',
+  backgroundColor:'#fff',
+
+};
+  function getTransitionName({
+    route,
+    openCache,
+    cacheTabs,
+    enableTransition,
+    def,
+  }: Pick<DefaultContext, 'route'> & {
+    enableTransition: boolean;
+    openCache: boolean;
+    def: string;
+    cacheTabs: string[];
+  }): string | undefined {
+    if (!enableTransition) {
+      return undefined;
+    }
+
+    const isInCache = cacheTabs.includes(route.name as string);
+    const transitionName = 'fade-slide';
+    let name: string | undefined = transitionName;
+
+    if (openCache) {
+      name = isInCache && route.meta.loaded ? transitionName : undefined;
+    }
+    return name || (route.meta.transitionName as string) || def;
+  }
+
+  const { getShowMultipleTab } = useMultipleTabSetting();
+  const tabStore = useMultipleTabStore();
+
+  const { getOpenKeepAlive, getCanEmbedIFramePage } = useRootSetting();
+
+  const { getBasicTransition, getEnableTransition } = useTransitionSetting();
+
+  const openCache = computed(() => unref(getOpenKeepAlive) && unref(getShowMultipleTab));
+
+  const getCaches = computed((): string[] => {
+    if (!unref(getOpenKeepAlive)) {
+      return [];
+    }
+    return tabStore.getCachedTabList;
+  });
+  import { useDesign } from '@/hooks/web/useDesign';
+
+  const { prefixCls } = useDesign('email');
+
+
+  import { useCollapseStore } from '@/store/modules/useCollapseStore';
+  const collapseStore = useCollapseStore();
+  function fuToggleContent(is) {
+    collapseStore.toggle(is);
+  }
+</script>
+<style lang="less" scoped>
+  @prefix-cls: ~'@{namespace}-email';
+  .@{prefix-cls} {
+    .splitpanes__pane {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      background-color: #fff;
+    }
+  }
+
+
+  .toggle-btn {
+    display: flex;
+    position: absolute;
+    z-index: 99;
+    top: 45%;
+    width: 20px; /* 鍘熷瀹藉害 */
+    height: 54px;
+    transform: translateY(-50%);
+    border-left: none;
+    border-radius: 0 10px 10px 0;
+    background: #dfe1e5;
+  }
+
+  .onOpen {
+    width: 86vw;
+  }
+
+  .isOpen {
+    width: 100vw;
+  }
+
+  .iconOpen {
+    left: 250px;
+  }
+
+  .onIconOpen {
+    left: 0%;
+  }
 </style>
diff --git a/src/views/email/outbox/index.vue b/src/views/email/outbox/index.vue
new file mode 100644
index 0000000..e5386d2
--- /dev/null
+++ b/src/views/email/outbox/index.vue
@@ -0,0 +1,61 @@
+<template>
+  <div>
+    <a-spin :spinning="loading" class="p-1" style="height: 100%;">
+      <PageIndex :pageList="pageList" :mailType="1" :pageData="pageData" > </PageIndex>
+    </a-spin>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  name: 'outbox';
+  import { ref, onMounted, computed, provide } from 'vue';
+  import PageIndex from '@/views/email/components/ListPage/list.vue';
+  import { useRoute } from 'vue-router';
+
+  const route = useRoute();
+  // 鑾峰彇褰撳墠瀹屾暣 URL
+  const routerId = computed(() => {
+    try {
+      const url = window.location.href;
+      if (!url) {
+        throw new Error('Invalid URL');
+      }
+      const basePart = url.split('?')[1];
+      console.log(basePart, 'basePart');
+      return basePart;
+    } catch (error) {
+      console.error('Error processing URL:', error);
+      // 杩斿洖榛樿鍊兼垨绌哄瓧绗︿覆
+      return '';
+    }
+  });
+  import { getMailListApi } from '@/api/email/userList';
+  const pageList = ref([]);
+  const loading = ref(false);
+  const pageData = ref({
+    page: 1,
+    limit: 20,
+    total: 0,
+  });
+  function getDataList(page:1) {
+    loading.value = true;
+    getMailListApi({ mail: routerId.value, mailType: 2,page })
+      .then((res) => {
+        loading.value = false;
+        if (res.code == 0) {
+          pageList.value = res.data.list;
+          pageData.value.total = res.data.total;
+        }
+      })
+      .catch(() => {
+        loading.value = false;
+      });
+  }
+
+  
+  provide('getDataList', getDataList);
+  onMounted(() => {
+    getDataList();
+  });
+</script>
+<style scoped lang="less"></style>
diff --git a/src/views/email/preview/index..vue b/src/views/email/preview/index..vue
new file mode 100644
index 0000000..6913562
--- /dev/null
+++ b/src/views/email/preview/index..vue
@@ -0,0 +1,86 @@
+<template>
+  <a-card :title="data.subject">
+    <div style="display: flex; align-items: center" class="p-4">
+      <div class="left">
+        <a-avatar :size="{ xs: 24, sm: 32, md: 40, lg: 64, xl: 80, xxl: 100 }">
+          <template #icon>
+            <AntDesignOutlined />
+          </template>
+        </a-avatar>
+      </div>
+      <div class="right">
+        <div class="top">
+          <div class="top-left">
+            <div>
+              <span class="top-left-title">鍙戜欢浜猴細</span>
+              <span class="top-right-title">{{ data.sender }}</span>
+            </div>
+            <div>
+              <span class="top-left-title">鏀朵欢浜猴細</span>
+              <span class="top-right-title">{{ `${data.receiver}` }}</span>
+            </div>
+            <div>
+              <span class="top-left-title">鏃堕棿锛�</span>
+              <span class="top-right-title">{{ data.createTime }}</span>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+    <TinymcePw ref="TinymcePwRef" v-model="data.content"></TinymcePw>
+  </a-card>
+</template>
+
+<script lang="ts" setup>
+    name: 'previewIndex';
+    import { AntDesignOutlined } from '@ant-design/icons-vue';
+
+    import { ref, onMounted, computed, } from 'vue';
+    import { TinymcePw } from '@/components/Tinymce';
+    import { useRouter } from 'vue-router';
+    import { getMailInfoApi } from '@/api/email/userList';
+  import { nextTick } from 'vue';
+
+    const { currentRoute } = useRouter();
+    const docCode = computed(() => {
+      return currentRoute.value.query.docCode;
+    });
+    const data = ref<Record<string, any>>({});
+    const TinymcePwRef = ref()
+function fnGetData() {
+  getMailInfoApi({ docCode: docCode.value })
+    .then((res) => {
+      console.log(res, 'res');
+      nextTick(() => {
+        console.log(data.value, 'data');
+        data.value = res.data;
+        if (!TinymcePwRef || TinymcePwRef.value === undefined) {
+          setTimeout(() => {
+            Object.assign(data.value, data.value.content);
+          }, 1000);
+        }
+      });
+    })
+    .catch((error) => {
+      console.error('鑾峰彇鏁版嵁澶辫触:', error);
+      // 鍙互鍦ㄨ繖閲屾坊鍔犲叾浠栭敊璇鐞嗛�昏緫锛屼緥濡傞�氱煡鐢ㄦ埛
+    });
+}
+    onMounted(() => {
+      fnGetData();
+    });
+</script>
+
+<style scoped lang="less">
+  .right {
+    margin-left: 30px;
+  }
+
+  .top-left-title {
+    color: #999;
+  }
+
+  .top-right-title {
+    color: #000;
+  }
+</style>
diff --git a/types/axios.d.ts b/types/axios.d.ts
index 384ec33..9adcee6 100644
--- a/types/axios.d.ts
+++ b/types/axios.d.ts
@@ -41,6 +41,10 @@
   type: 'success' | 'error' | 'warning';
   message: string;
   result: T;
+  msg: string;
+  data: T;
+  total: number;
+  state: number;
 }
 
 // multipart/form-data: upload file
diff --git a/uno.config.ts b/uno.config.ts
index 5e3512f..a947a87 100644
--- a/uno.config.ts
+++ b/uno.config.ts
@@ -1,5 +1,6 @@
 import { defineConfig, presetTypography, presetUno } from 'unocss';
 
 export default defineConfig({
+  rules: [['my-d-f', { display: 'flex', 'align-items': 'center' }]],
   presets: [presetUno(), presetTypography()],
 });

--
Gitblit v1.8.0