From db42d08c39ae6129e2b95cd24c0d57c6769282e5 Mon Sep 17 00:00:00 2001
From: huangyinfeng <1244041895@qq.com>
Date: 星期日, 29 九月 2024 15:32:51 +0800
Subject: [PATCH] 邮件右键菜单

---
 src/views/email/folder/index.vue                            |   61 +
 src/components/MyUpload/hooks/useUploadHook.ts              |   56 +
 src/views/email/Utils/folder.vue                            |   52 
 src/views/email/components/ContextMenu/CustomTimePicker.vue |   49 +
 pnpm-lock.yaml                                              |    8 
 src/components/Tinymce/src/Editor.vue                       |  109 --
 vite.config.ts                                              |    2 
 src/views/email/components/ListPage/table.vue               |  139 ++-
 src/views/email/components/ContextMenu/index.vue            |  327 +++++++++
 src/views/email/components/LeftMenu/MyMenu.vue              |  121 +++
 src/views/email/components/ListPage/drawerDetail.vue        |  112 +++
 src/views/email/label/index.vue                             |   61 +
 src/settings/projectSetting.ts                              |    2 
 src/components/MyUpload/index.vue                           |  205 +++++
 src/views/email/components/ListPage/list.vue                |   90 +
 src/views/email/components/ListPage/TooltipAndDropdown .vue |   36 
 src/views/email/components/LeftMenu/EmailMenuItem.vue       |   44 +
 src/views/email/components/LeftMenu/index.vue               |  282 +++++--
 src/router/routes/modules/email.ts                          |   23 
 /dev/null                                                   |  181 -----
 src/views/email/Edit/index.vue                              |   46 +
 src/views/email/Utils/mailboxManagement.vue                 |   47 +
 src/api/email/userList.ts                                   |   23 
 package.json                                                |    1 
 src/App.vue                                                 |    2 
 25 files changed, 1,563 insertions(+), 516 deletions(-)

diff --git a/package.json b/package.json
index 2d5ea9a..85cbe06 100644
--- a/package.json
+++ b/package.json
@@ -86,6 +86,7 @@
     "echarts": "^5.5.1",
     "exceljs": "^4.4.0",
     "html2canvas": "^1.4.1",
+    "jsencrypt": "^3.3.2",
     "lodash-es": "^4.17.21",
     "mockjs": "^1.1.0",
     "nprogress": "^0.2.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index a1f5291..fd17aeb 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -59,6 +59,9 @@
       html2canvas:
         specifier: ^1.4.1
         version: 1.4.1
+      jsencrypt:
+        specifier: ^3.3.2
+        version: 3.3.2
       lodash-es:
         specifier: ^4.17.21
         version: 4.17.21
@@ -5200,6 +5203,9 @@
     peerDependenciesMeta:
       canvas:
         optional: true
+
+  jsencrypt@3.3.2:
+    resolution: {integrity: sha512-arQR1R1ESGdAxY7ZheWr12wCaF2yF47v5qpB76TtV64H1pyGudk9Hvw8Y9tb/FiTIaaTRUyaSnm5T/Y53Ghm/A==}
 
   jsesc@2.5.2:
     resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==}
@@ -13689,6 +13695,8 @@
       - supports-color
       - utf-8-validate
 
+  jsencrypt@3.3.2: {}
+
   jsesc@2.5.2: {}
 
   json-buffer@3.0.1: {}
diff --git a/src/App.vue b/src/App.vue
index 0e74772..04eb4bd 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -26,7 +26,7 @@
     // 鍏堟鏌roxy鏄惁瀛樺湪锛屽啀杩涜鎿嶄綔
     if (proxy && proxy.$cookies) {
       proxy.$cookies.remove('JSESSIONID');
-      proxy.$cookies.set('JSESSIONID', '741E9E2AAD7578915B16287A5ECAE1DF.jvm_59_9010', '1d');
+      proxy.$cookies.set('JSESSIONID', '071339196A579D1A2D374739F29D7521.jvm_59_9010', '1d');
     } else {
       console.error('proxy瀵硅薄鏈垵濮嬪寲鎴栦笉鍖呭惈$cookies灞炴��');
     }
diff --git a/src/api/email/userList.ts b/src/api/email/userList.ts
index 2b67de7..c3aa42f 100644
--- a/src/api/email/userList.ts
+++ b/src/api/email/userList.ts
@@ -49,6 +49,7 @@
   DELETE_BLACKLIST = '/crm/mail/blacklist/deleteBlackList.do',
   GET_BLACKLIST = '/crm/mail/blacklist/getBlackList.do',
   GET_EMAIL_MODULE_BELOW = '/crm/mail/getEmailModuleBelow.do',
+  ADD_LIAS_EMAIL = '/crm/mail/account/addAliasEmail.do',
 }
 // 鑾峰彇閭欢璺敱鍒楄〃
 export const getEmailModuleApi = () => defHttp.get({ url: Api.GET_EMAIL_MODULE });
@@ -73,7 +74,8 @@
 // 淇敼閭閰嶇疆
 export const updateAccountApi = (params) => defHttp.post<{}>({ url: Api.UPDATE_ACCOUNT, params });
 // 鍒犻櫎閭閰嶇疆
-export const deleteAccountApi = (params) => defHttp.post<{}>({ url: Api.DELETE_ACCOUNT, params });
+export const deleteAccountApi = (query) =>
+  defHttp.post<{}>({ url: Api.DELETE_ACCOUNT + '?accountId=' + query });
 
 // 鑾峰彇閭鍒楄〃
 export const getAccountListApi = () => defHttp.get<{}>({ url: Api.GET_ACCOUNT_LIST });
@@ -158,7 +160,7 @@
   });
 // 璁剧疆瀹屾垚鏃堕棿
 export const updateHandleAPi = (params) =>
-  defHttp.get({
+  defHttp.post({
     url: Api.UPDATE_HANDLE,
     params,
   });
@@ -252,7 +254,6 @@
     params,
   });
 
-
 // 鏂板榛戝悕鍗�
 export const addBlackListApi = (params) =>
   defHttp.post({
@@ -281,9 +282,15 @@
     params,
   });
 
+// 鏂囦欢澶圭粨鏋�
+export const getEmailModuleBelowApi = () =>
+  defHttp.get({
+    url: Api.GET_EMAIL_MODULE_BELOW,
+  });
 
-  export const getEmailModuleBelowApi = () =>
-    defHttp.get({
-      url: Api.GET_EMAIL_MODULE_BELOW,
-    });
-  
\ No newline at end of file
+//閭鍒悕
+export const addLiasEmailApi = (params) =>
+  defHttp.get({
+    url: Api.ADD_LIAS_EMAIL,
+    params,
+  });
diff --git a/src/components/MyUpload/hooks/useUploadHook.ts b/src/components/MyUpload/hooks/useUploadHook.ts
new file mode 100644
index 0000000..a6993f2
--- /dev/null
+++ b/src/components/MyUpload/hooks/useUploadHook.ts
@@ -0,0 +1,56 @@
+import { ref } from 'vue';
+import JSEncrypt from 'jsencrypt'; // 瀵煎叆 JSEncrypt 搴�
+
+export const useUploadHook = (baseUrl: string) => {
+  const computedUploadUrl = ref(baseUrl);
+
+  // RSA 鍔犲瘑鐢ㄧ殑鍏挜
+  const publicKey = `-----BEGIN PUBLIC KEY-----
+  MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkRupiYcKVGGUtDBDoR1t/1zm3ZtZgnte39iTJW6hlqjdY0UagKjpNiIv7J6XjtgfX7SgsR4AWnivqQHAICIvdPKfGZZzIs62OQ19MqrDTMoB/LvK5teNWhClv23WMUfRbP+EHgprT6hTw8U5apw1IB6i/y57NkLav792wiYBYRU4X45NoTaT+aiTSLFEflbfm94EXnhSS3vFkBmrZGy5BRNI8gmzafroslGx2Hk90CqlNdeKYxgZQ6xtvj+u33yrszWvPT6F9fsJT8aMjtvH050iYKRVct+x6Q7VRJgCI4MgvAexnTKdxW54YzvXCuO5bDiy5la7CgerWkTAq9dzXwIDAQAB
+  -----END PUBLIC KEY-----`;
+
+  // 鍥哄畾鐨勫煙鍚嶅拰ID锛堝皢鏉ュ彲浠� Vuex 涓幏鍙栵級
+  const domain = 'https://img.onbus.cn';
+  const dbid = 82; // 杩欓噷鍙互浠� Vuex 鑾峰彇
+  const formid = 0; // 杩欓噷涔熷彲浠ヤ粠 Vuex 鑾峰彇
+  const uuid = ref(undefined); // 杩欓噷涔熷彲浠ヤ粠 Vuex 鑾峰彇
+
+  // 浣跨敤 RSA 鍔犲瘑鏁版嵁
+  const encryptData = (data: Record<string, any>) => {
+    const encryptor = new JSEncrypt();
+    encryptor.setPublicKey(publicKey);
+
+    const encryptedData: Record<string, string> = {};
+    for (const key in data) {
+      if (data.hasOwnProperty(key)) {
+        const encryptedValue = encryptor.encrypt(data[key]);
+        if (encryptedValue) {
+          encryptedData[key] = encodeURIComponent(encryptedValue); // URL 缂栫爜
+        }
+      }
+    }
+    return encryptedData;
+  };
+
+  // 鑾峰彇涓婁紶鏁版嵁骞跺姞瀵�
+  const getUploadData = (uuid) => {
+    const data = {
+      dbid: dbid.toString(),
+      username: 'huang',
+      usercode: 'z9000137',
+    };
+
+    const newData = encryptData(data);
+    return { ...newData, type: 3, fieldid: '333', uuid: uuid };
+  };
+
+  // 鐢熸垚鏂囦欢 URL
+  const generateFileUrl = (response: any) => {
+    const unid = response.uuid.split(';')[0] + '@P@' + response.uuid.split(';')[1];
+    const fileExt = response.fileType;
+
+    return `${domain}/uploads/attachment/${dbid}/${formid}/${unid}.${fileExt}`;
+  };
+
+  return { computedUploadUrl, getUploadData, generateFileUrl };
+};
diff --git a/src/components/MyUpload/index.vue b/src/components/MyUpload/index.vue
new file mode 100644
index 0000000..c8a9818
--- /dev/null
+++ b/src/components/MyUpload/index.vue
@@ -0,0 +1,205 @@
+<template>
+  <div>
+    <Upload
+      v-if="type == 'png'"
+      name="file"
+      multiple
+      @change="handleUploadChange"
+      :action="computedUploadUrl"
+      :showUploadList="false"
+      :accept="accept"
+      :beforeUpload="beforeUpload"
+    >
+      <a-button type="text" size="small" v-bind="{ ...getButtonProps }">
+        <PictureOutlined /> {{ title }}
+      </a-button>
+    </Upload>
+
+    <a-upload
+      v-if="type == 'file'"
+      v-model:file-list="fileListTemp"
+      :action="computedUploadUrl"
+      list-type="picture"
+      class="upload-list-inline"
+      :before-upload="beforeUpload"
+      @change="handleFileUploadChange"
+    >
+      <a-button type="text" size="small" style="margin: 0 auto">
+        <UploadOutlined />
+        闄勪欢
+      </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="fnPreview(file)">棰勮</a>
+            <a href="javascript:;" @click="fnRename(file)">閲嶅懡鍚�</a>
+            <a href="javascript:;" @click="actions.remove">鍒犻櫎</a>
+          </span>
+          <span v-else>
+            <a href="javascript:;" @click="fnSaveRename(file)">淇濆瓨</a>
+            <a href="javascript:;" @click="fnOffRename(file)">鍙栨秷</a>
+          </span>
+        </a-space>
+      </template>
+    </a-upload>
+    <div id="previewContainer"></div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  import { computed, ref, watch } from 'vue';
+  import { PictureOutlined, UploadOutlined, PaperClipOutlined } from '@ant-design/icons-vue';
+  import { Upload } from 'ant-design-vue';
+  import { useGlobSetting } from '@/hooks/setting';
+  import { useUploadHook } from './hooks/useUploadHook';
+
+  defineOptions({ name: 'TinymceImageUpload' });
+
+  const props = defineProps({
+    fullscreen: {
+      type: Boolean,
+    },
+    disabled: {
+      type: Boolean,
+      default: false,
+    },
+    accept: {
+      type: String,
+      default: '.jpg,.jpeg,.gif,.png,.webp',
+    },
+    title: {
+      type: String,
+      default: '鍥剧墖',
+    },
+    type: {
+      type: String,
+      default: 'png',
+    },
+  });
+
+  const emit = defineEmits(['uploading', 'done', 'error', 'fileListChange']);
+  let uploading = false;
+
+  const { uploadUrl: baseUrl } = useGlobSetting();
+  const { computedUploadUrl, getUploadData, generateFileUrl } = useUploadHook(baseUrl);
+
+  const fileListTemp = ref([]); // 鐢ㄤ簬闄勪欢鐨勬枃浠跺垪琛�
+
+  const getButtonProps = computed(() => ({
+    disabled: props.disabled,
+  }));
+  const uuid = ref('');
+
+  // beforeUpload 鎷︽埅鍣紝鐢ㄤ簬鍔ㄦ�佷慨鏀� URL
+  const beforeUpload = () => {
+    const encryptedParams = getUploadData(uuid.value);
+    const queryString = new URLSearchParams(encryptedParams).toString();
+    computedUploadUrl.value = `${baseUrl}?${queryString}`;
+  };
+  // 澶勭悊涓婁紶鍙樺寲
+  const handleUploadChange = (info: Record<string, any>) => {
+    const file = info.file;
+    const status = file?.status;
+
+    if (status === 'uploading') {
+      if (!uploading) {
+        emit('uploading', file.name);
+        uploading = true;
+      }
+    } else if (status === 'done') {
+      const newUrl = generateFileUrl(file.response); // 鐢熸垚鏂囦欢 URL
+      debugger;
+      if (!uuid.value) {
+        uuid.value = file.response.uuid.split(';')[0];
+      }
+      emit('done', file.name, newUrl);
+      uploading = false;
+    } else if (status === 'error') {
+      emit('error');
+      uploading = false;
+    }
+  };
+  function handleFileUploadChange(info: Record<string, any>) {
+    const file = info.file;
+    const status = file?.status;
+    if (status === 'done') {
+      if (!uuid.value) {
+        uuid.value = file.response.uuid.split(';')[0];
+      }
+      uploading = false;
+    }
+  }
+  // 閲嶅懡鍚嶅姛鑳�
+  const fnRename = (file) => {
+    file.editor = true;
+  };
+
+  // 淇濆瓨閲嶅懡鍚�
+  const fnSaveRename = (file) => {
+    file.editor = false;
+    file.name = file.tempName;
+  };
+
+  // 鍙栨秷閲嶅懡鍚�
+  const fnOffRename = (file) => {
+    file.editor = false;
+  };
+
+  // 棰勮
+  const fnPreview = (file) => {
+    if (!file || !file.response) {
+      console.error('Invalid file or response');
+      return;
+    }
+
+    // 鐢熸垚瀹夊叏鐨勬枃浠� URL
+    const safeUrl = generateFileUrl(file.response);
+
+    // 鑾峰彇鏂囦欢绫诲瀷锛堥�氳繃鏂囦欢鎵╁睍鍚嶆垨 MIME 绫诲瀷锛�
+    const fileExt = file.response.fileType || file.name.split('.').pop().toLowerCase(); // 鑾峰彇鏂囦欢鎵╁睍鍚�
+    const isImage = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp'].includes(fileExt); // 鍒ゆ柇鏄惁涓哄浘鐗�
+
+    if (isImage) {
+      // 鐩存帴鎵撳紑鍥剧墖
+      window.open(safeUrl, '_blank');
+    } else {
+      // 闈炲浘鐗囩被鍨嬩娇鐢� Office Online Viewer 鎴栧叾浠栧伐鍏烽瑙�
+      const iframeSrc = `https://view.officeapps.live.com/op/view.aspx?src=${encodeURIComponent(safeUrl)}`;
+      window.open(iframeSrc, '_blank');
+    }
+  };
+
+  watch(
+    () => fileListTemp.value,
+    (newValue) => {
+      emit('fileListChange', fileListTemp.value);
+    },
+  );
+</script>
+
+<style lang="less" scoped>
+  @prefix-cls: ~'@{namespace}-tinymce-img-upload';
+
+  .@{prefix-cls} {
+    z-index: 20;
+    top: 4px;
+    right: 10px;
+
+    &.fullscreen {
+      position: fixed;
+      z-index: 10000;
+    }
+  }
+</style>
diff --git a/src/components/Tinymce/src/Editor.vue b/src/components/Tinymce/src/Editor.vue
index 53ed7c9..025753a 100644
--- a/src/components/Tinymce/src/Editor.vue
+++ b/src/components/Tinymce/src/Editor.vue
@@ -8,42 +8,20 @@
     ></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' : ''">
+      <ImgUpload
+        ref="fileRef"
+        :fullscreen="fullscreen"
+        :fileListTemp="fileListTemp"
+        @uploading="handleImageUploading"
+        @done="handleDone"
+        @fileListChange="fileListChange"
+        v-if="isImg"
+        v-show="editorRef"
+        :title="'闄勪欢'"
+        :type="'file'"
+        :disabled="disabled"
+      />
+      <div class="my-upload-list">
         <div style="display: flex">
           <ImgUpload
             :fullscreen="fullscreen"
@@ -52,6 +30,7 @@
             v-if="isImg"
             v-show="editorRef"
             :title="'鍥剧墖'"
+            :type="'png'"
             :disabled="disabled"
             :accept="'.jpg,.jpeg,.gif,.png,.webp'"
           />
@@ -68,6 +47,8 @@
 <script lang="ts" setup>
   import type { Editor, RawEditorSettings } from 'tinymce';
   import { PaperClipOutlined, UploadOutlined, SnippetsOutlined } from '@ant-design/icons-vue';
+  import { useGlobSetting } from '@/hooks/setting';
+
   import tinymce from 'tinymce/tinymce';
   import 'tinymce/themes/silver';
   import 'tinymce/icons/default/icons';
@@ -144,7 +125,7 @@
     PropType,
     useAttrs,
   } from 'vue';
-  import ImgUpload from './ImgUpload.vue';
+  import ImgUpload from '@/components/MyUpload/index.vue';
   import {
     plugins as defaultPlugins,
     toolbar as defaultToolbar,
@@ -157,6 +138,8 @@
   import { isNumber } from '@/utils/is';
   import { useLocale } from '@/locales/useLocale';
   import { useAppStore } from '@/store/modules/app';
+  const { uploadUrl } = useGlobSetting();
+  // console.log(uploadUrl,'uploadUrl');
 
   defineOptions({ name: 'Tinymce', inheritAttrs: false });
 
@@ -370,6 +353,7 @@
       editor.setContent(val);
     }
   }
+  const fileRef = ref();
 
   function bindModelHandlers(editor: any) {
     const modelEvents = attrs.modelEvents ? attrs.modelEvents : null;
@@ -432,54 +416,9 @@
   }
 
   // 闄勪欢
-  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;
-    });
+  const fileListTemp = ref([]);
+  function fileListChange(data) {
+    fileListTemp.value = data;
   }
 </script>
 <style lang="less" scope>
diff --git a/src/components/Tinymce/src/ImgUpload.vue b/src/components/Tinymce/src/ImgUpload.vue
deleted file mode 100644
index 61aa1e2..0000000
--- a/src/components/Tinymce/src/ImgUpload.vue
+++ /dev/null
@@ -1,94 +0,0 @@
-<template>
-  <div :class="[prefixCls, { fullscreen }]">
-    <Upload
-      name="file"
-      multiple
-      @change="handleChange"
-      :action="uploadUrl"
-      :showUploadList="false"
-      :accept="accept"
-    >
-      <a-button type="text" size='small' v-bind="{ ...getButtonProps }">
-        <PictureOutlined />  {{ title }}
-      </a-button>
-    </Upload>
-  </div>
-</template>
-<script lang="ts" setup>
-  import { computed } from 'vue';
-  import {PictureOutlined } from '@ant-design/icons-vue';
-  import { Upload } from 'ant-design-vue';
-  import { useDesign } from '@/hooks/web/useDesign';
-  import { useGlobSetting } from '@/hooks/setting';
-  import { useI18n } from '@/hooks/web/useI18n';
-
-  defineOptions({ name: 'TinymceImageUpload' });
-
-  const props = defineProps({
-    fullscreen: {
-      type: Boolean,
-    },
-    disabled: {
-      type: Boolean,
-      default: false,
-    },
-    accept:{
-      type: String,
-      default: '.jpg,.jpeg,.gif,.png,.webp'
-    },
-    title:{
-      type: String,
-      default: '鍥剧墖'
-    }
-  });
-
-  const emit = defineEmits(['uploading', 'done', 'error']);
-
-  let uploading = false;
-
-  const { uploadUrl } = useGlobSetting();
-  const { t } = useI18n();
-  const { prefixCls } = useDesign('tinymce-img-upload');
-
-  const getButtonProps = computed(() => {
-    const { disabled } = props;
-    return {
-      disabled,
-    };
-  });
-
-  function handleChange(info: Record<string, any>) {
-    const file = info.file;
-    const status = file?.status;
-    const url = file?.response?.url;
-    const name = file?.name;
-
-    if (status === 'uploading') {
-      if (!uploading) {
-        emit('uploading', name);
-        uploading = true;
-      }
-    } else if (status === 'done') {
-      emit('done', name, url);
-      uploading = false;
-    } else if (status === 'error') {
-      emit('error');
-      uploading = false;
-    }
-  }
-</script>
-<style lang="less" scoped>
-  @prefix-cls: ~'@{namespace}-tinymce-img-upload';
-
-  .@{prefix-cls} {
-    // position: absolute;
-    z-index: 20;
-    top: 4px;
-    right: 10px;
-
-    &.fullscreen {
-      position: fixed;
-      z-index: 10000;
-    }
-  }
-</style>
diff --git a/src/router/routes/modules/email.ts b/src/router/routes/modules/email.ts
index a362a3a..7da5c22 100644
--- a/src/router/routes/modules/email.ts
+++ b/src/router/routes/modules/email.ts
@@ -108,7 +108,8 @@
           meta: {
             title: '鍏ㄩ儴鍙戜欢',
           },
-        }]
+        },
+      ],
     },
     // {
     //   path: 'MassMailbox',
@@ -120,6 +121,26 @@
     //     currentActiveMenu: '/email/index',
     //   },
     // },
+    {
+      path: 'folder',
+      name: 'Folder',
+      component: () => import('@/views/email/folder/index.vue'),
+      meta: {
+        title: '鏂囦欢澶�',
+        hideTab: true,
+        currentActiveMenu: '/email/index',
+      },
+    },
+    {
+      path: 'label',
+      name: 'Label',
+      component: () => import('@/views/email/label/index.vue'),
+      meta: {
+        title: '鏍囩',
+        hideTab: true,
+        currentActiveMenu: '/email/index',
+      },
+    },
   ],
 };
 
diff --git a/src/settings/projectSetting.ts b/src/settings/projectSetting.ts
index 12b128f..bb30f0c 100644
--- a/src/settings/projectSetting.ts
+++ b/src/settings/projectSetting.ts
@@ -235,7 +235,7 @@
 
   // Use error-handler-plugin
   // 鏄惁浣跨敤鍏ㄥ眬閿欒鎹曡幏
-  useErrorHandle: true,
+  useErrorHandle: false,
 
   // Whether to open back to top
   // 鏄惁寮�鍚洖鍒伴《閮�
diff --git a/src/views/email/Edit/index.vue b/src/views/email/Edit/index.vue
index ae47131..f683557 100644
--- a/src/views/email/Edit/index.vue
+++ b/src/views/email/Edit/index.vue
@@ -393,9 +393,47 @@
   };
 
   function fnChangeContent(e) {
-    modelRef.content = e.content;
-    modelRef.attachmentList = e.attachmentList;
+  if (!isValidEvent(e)) {
+    console.error('Invalid event:', e);
+    return;
   }
+
+  console.log('Event:', e, '----------------');
+  
+  modelRef.content = e.content;
+  modelRef.attachmentList = mergeArrayWithPrefix(fnBuildAttachmentList(e.fileUNID));
+  
+  console.log('Updated modelRef:', modelRef);
+}
+
+function isValidEvent(e) {
+  return e && typeof e === 'object' && typeof e.content === 'string';
+}
+
+function fnBuildAttachmentList(data) {
+  if (!Array.isArray(data)) {
+    console.error('Invalid argument: data must be an array');
+    return [];
+  }
+
+  return data.reduce((acc, item) => {
+    const uuid = item?.response?.uuid;
+    if (uuid) {
+      acc.push(uuid);
+    } else {
+      console.warn('Invalid item:', item);
+    }
+    return acc;
+  }, []);
+}
+
+function mergeArrayWithPrefix(arr) {
+  if (!arr || arr.length === 0) return '';
+  const [prefix] = arr[0].split(';');
+  const suffixes = arr.map(item => item.split(';')[1]).filter(Boolean).join(';');
+  
+  return `${prefix};${suffixes}`;
+}
 
   function fnBuildingCommitData() {
     return {
@@ -405,7 +443,7 @@
       bcc: modelRef.bccRecipients,
       subject: modelRef.subject,
       content: modelRef.content,
-      attachmentList: '',
+      attachmentList: modelRef.attachmentList,
       docCode: docCode.value,
     };
   }
@@ -441,7 +479,7 @@
         loading.value = false;
         if (res.code === 0) {
           createMessage.success(res.msg);
-          router.push('/email/list');
+          router.push('/email/index');
         }
       })
       .catch((error) => {
diff --git a/src/views/email/Utils/folder.vue b/src/views/email/Utils/folder.vue
index 09769c5..c373435 100644
--- a/src/views/email/Utils/folder.vue
+++ b/src/views/email/Utils/folder.vue
@@ -114,31 +114,32 @@
 
   function fnGetList() {
     getFolderApi({}).then((res) => {
-      console.log(res);
       demo.tableData = convertToTableData(res.data);
-      console.log(demo.tableData, '3333333333333');
     });
   }
   function convertToTableData(data, parentId = null) {
-    let tableData = [];
+    try {
+      let tableData = [];
 
-    data.forEach((item) => {
-      let tableItem = {
-        folderId: item.folderId,
-        parentRowId: parentId,
-        rowId: item.rowId,
-        folderName: item.folderName,
-        treeControl: item.treeControl,
-      };
+      data.forEach((item) => {
+        let tableItem = {
+          folderId: item.folderId,
+          parentRowId: parentId,
+          rowId: item.rowId,
+          folderName: item.folderName,
+          treeControl: item.treeControl,
+        };
 
-      if (item.list && item.list.length > 0) {
-        let children = convertToTableData(item.list, item.rowId);
-        tableData = tableData.concat(children);
-      }
+        if (item.list && item.list.length > 0) {
+          let children = convertToTableData(item.list, item.rowId);
+          tableData = tableData.concat(children);
+        }
 
-      tableData.push(tableItem);
-    });
-    return tableData;
+        tableData.push(tableItem);
+      });
+      return tableData;
+     
+    } catch (error) { return [];}
   }
 
   const inputRefs = ref<{ [key: number]: HTMLElement | null }>({});
@@ -146,7 +147,7 @@
     const $table = xTable.value;
     const rid = Date.now();
     const record = {
-      folderName: `鏂版暟鎹�${rid}`,
+      folderName: `鏂版枃浠跺す`,
       id: rid,
     };
     $table.insert(record).then(({ row }) => $table.setEditRow(row));
@@ -158,9 +159,9 @@
 
   const { createMessage } = useMessage();
   function fnInputHandle(row) {
-    console.log(row, '----333');
     if (row.folderName == '') {
-      editRowEvent(row)
+      editRowEvent(row);
+      fnGetList();
       return createMessage.error('璇疯緭鍏ユ枃浠跺す鍚嶇О');
     }
     const data =
@@ -184,6 +185,7 @@
         fnGetList();
       } else {
         createMessage.error(res.msg);
+        fnGetList();
       }
     });
   }
@@ -191,15 +193,16 @@
     const $table = xTable.value;
     const rid = Date.now();
     const record = {
-      folderName: `鏂版暟鎹�${rid}`,
+      folderName: `鏂板瓙鏂囦欢澶筦,
       id: rid,
       parentRowId: row.rowId, // 闇�瑕佹寚瀹氱埗鑺傜偣锛岃嚜鍔ㄦ彃鍏ヨ鑺傜偣涓�
     };
-    console.log(record, '99999993');
     const { row: newRow } = await $table.insert(record);
-
     await $table.setTreeExpand(row, true); // 灏嗙埗鑺傜偣灞曞紑
     await $table.setEditRow(newRow); // 鎻掑叆瀛愯妭鐐�
+    setTimeout(() => {
+      inputRefs.value[rid].focus();
+    }, 300);
   }
   function fnDelete(row) {
     deleteFolderApi({ folderId: row.folderId })
@@ -216,7 +219,6 @@
 
   function editRowEvent(row) {
     const $table = xTable.value;
-    console.log(row, '---30494');
     row.opType = 'edit';
     $table.setEditRow(row);
   }
diff --git a/src/views/email/Utils/mailboxManagement.vue b/src/views/email/Utils/mailboxManagement.vue
index 2809d78..7a68cfb 100644
--- a/src/views/email/Utils/mailboxManagement.vue
+++ b/src/views/email/Utils/mailboxManagement.vue
@@ -20,6 +20,7 @@
       :filter-config="{ showIcon: false }"
       :row-config="{ isHover: true }"
       :column-config="{ resizable: true }"
+      :edit-config="{ trigger: 'click', mode: 'cell' }"
     >
       <vxe-column width="60">
         <template #default>
@@ -34,15 +35,29 @@
           <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">
+      <vxe-column
+        show-overflow
+        field="aliasEmail"
+        title="鏄剧ず鍚嶇О"
+        min-width="250"
+        :edit-render="{}"
+      >
         <template #default="{ row }">
-          <span style="color: #999">{{ row.companyName }}</span>
+          <span style="color: #999">{{ row.aliasEmail }}</span>
+        </template>
+        <template #edit="{ row }">
+          <vxe-input
+            ref="inputRef"
+            v-model="row.aliasEmail"
+            type="text"
+            @blur="fnInputHandle(row)"
+          ></vxe-input>
         </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="success" v-if="row.mailStatus === '姝e父'">姝e父</a-tag>
             <a-tag color="red" v-else>寮傚父</a-tag>
           </div>
           <div v-else>
@@ -98,7 +113,8 @@
             </template>
             <a-input :disabled="typeAccount === 2" v-model:value="formData.email" />
           </a-form-item>
-          <a-form-item v-if="isCustom == 'custom'" name="password" label="閭瀵嗙爜">
+          <!-- v-if="isCustom == 'custom'"   -->
+          <a-form-item name="password" label="閭瀵嗙爜">
             <a-input-password
               type="password"
               v-model:value="formData.password"
@@ -397,6 +413,7 @@
     deleteAccountApi,
     getAccountListApi,
     isEmailValidApi,
+    addLiasEmailApi,
   } from '@/api/email/userList';
   const loading = ref(false);
   import Sortable from 'sortablejs';
@@ -454,11 +471,11 @@
     aliasEmail: '',
     biSyncFlag: false,
     proxyFlag: true,
-    receiveProtocol: 'imap',
+    receiveProtocol: 'imaps',
     receiveSSL: false,
     receivePort: '',
     receiveHost: '',
-    smtpSSL: false,
+    smtpSSL: true,
     smtpPort: '',
     smtpHost: '',
     invalid: '',
@@ -510,7 +527,12 @@
   const open = ref(false);
   const fnHandleOk = () => {
     formRef.value.validate().then(() => {
-      const data = formData.value;
+      const data = !isShow
+        ? formData.value
+        : {
+            email: formData.value.email,
+            password: formData.value.password,
+          };
       if (isShow.value == true) {
         data.mailType = isCustom.value === 'onCustom' ? 1 : 2;
       }
@@ -523,11 +545,12 @@
           }
           loading.value = false;
           fnMailList();
+          open.value = false;
         })
         .catch((err) => {
           loading.value = false;
+          open.value = false;
         });
-      open.value = false;
     });
   };
 
@@ -586,7 +609,7 @@
   }
   function fnHandleDetailOk() {
     openDrawerDetail.value = false;
-    deleteAccountApi({ accountId: accountId.value })
+    deleteAccountApi(accountId.value )
       .then((res) => {
         if (res.code === 0) {
           createMessage.success(res.msg);
@@ -626,7 +649,7 @@
     isEmailValidApi(email).then((res) => {
       if (res.code == 0) {
         isCheck.value = true;
-        checkStatus.value = res.data.status;
+        checkStatus.value = res.data.code == 0 ? true : false;
       }
     });
   }
@@ -664,6 +687,10 @@
       isCheckAll.value = false;
     }, 3000);
   }
+
+  function fnInputHandle(row) {
+    addLiasEmailApi({ aliasEmail: row.aliasEmail, accountId: row.accountId }).then((res) => {});
+  }
 </script>
 <style scoped lang="less">
   .bullet {
diff --git a/src/views/email/components/ContextMenu/CustomTimePicker.vue b/src/views/email/components/ContextMenu/CustomTimePicker.vue
new file mode 100644
index 0000000..515012d
--- /dev/null
+++ b/src/views/email/components/ContextMenu/CustomTimePicker.vue
@@ -0,0 +1,49 @@
+<template>
+  <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="$emit('cancel')">鍙栨秷</a-button>
+      <a-button
+        style="margin-left: 10px"
+        type="primary"
+        @click="$emit('submit')"
+      >纭畾</a-button>
+    </a-form-item>
+  </a-form>
+</template>
+
+<script setup>
+import { defineProps, defineEmits, reactive } from 'vue';
+
+const props = defineProps();
+const emit = defineEmits();
+const form = reactive({
+  date: '',
+  time: '',
+});
+
+const disabledDate = (currentDate) => {
+  return currentDate && currentDate < dayjs().startOf('day');
+};
+</script>
+
+<style scoped>
+/* 娣诲姞鑷畾涔夋牱寮� */
+</style>
diff --git a/src/views/email/components/ContextMenu/index.vue b/src/views/email/components/ContextMenu/index.vue
new file mode 100644
index 0000000..54ad3db
--- /dev/null
+++ b/src/views/email/components/ContextMenu/index.vue
@@ -0,0 +1,327 @@
+<template>
+  <div :style="style" class="custom-dropdown">
+    <!-- <template #overlay> -->
+    <a-menu>
+      <a-menu-item key="reply" @click="handleMenuAction('reply')">鍥炲</a-menu-item>
+      <a-menu-item key="replyAll" @click="handleMenuAction('replyAll')">鍥炲鍏ㄩ儴</a-menu-item>
+      <a-menu-item key="replyWithAttachment" @click="handleMenuAction('replyWithAttachment')">
+        甯﹂檮浠跺洖澶�
+      </a-menu-item>
+      <a-menu-item key="replyAllWithAttachment" @click="handleMenuAction('replyAllWithAttachment')">
+        甯﹂檮浠跺洖澶嶅叏閮�
+      </a-menu-item>
+      <a-menu-item key="forward" @click="handleMenuAction('forward')">杞彂</a-menu-item>
+      <a-menu-item key="forwardAsAttachment" @click="handleMenuAction('forwardAsAttachment')">
+        浣滀负闄勪欢杞彂
+      </a-menu-item>
+      <a-menu-item key="reEdit" @click="handleMenuAction('reEdit')">鍒嗗彂</a-menu-item>
+      <a-menu-item key="addNote" @click="handleMenuAction('addNote')">璁剧疆澶囨敞</a-menu-item>
+      <a-divider style="margin: 5px" />
+
+      <!-- <a-menu-item key="toProcess" @click="handleMenuAction('toProcess')">
+         <div class="my-display">
+          <a-dropdown>
+            <span>寰呭鐞� <FieldTimeOutlined /></span>
+            <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="trigger"
+                    title="鑷畾涔夋椂闂�"
+                    v-model:open="customTimeDropdownOpen"
+                    @confirm="onSubmitCustomTime"
+                  >
+                    <template #content>
+                      <CustomTimePicker
+                        :form="form"
+                        @cancel="cancelCustomTime"
+                        @submit="submitCustomTime"
+                      />
+                    </template>
+
+                    <div class="date-left" @click="toggleCustomTime">鑷畾涔夋椂闂�</div>
+                  </a-popover>
+                </div>
+              </a-card>
+            </template>
+          </a-dropdown>
+        </div> 
+      </a-menu-item> -->
+      <a-menu-item key="markAs" @click="handleMenuAction('markAs')">{{
+        `鏍囪${row.readFlag ? '鏈�' : '宸�'}璇籤
+      }}</a-menu-item>
+      <a-menu-item key="markAs" @click="handleMenuAction('markAs')">鏍囪涓�</a-menu-item>
+      <a-divider style="margin: 5px" />
+      <a-menu-item key="createRule" @click="handleMenuAction('createRule')"
+        >鏂板缓鏀跺彂浠惰鍒�</a-menu-item
+      >
+      <a-menu-item key="moveTo" @click="handleMenuAction('moveTo')">绉诲姩鍒�</a-menu-item>
+      <a-divider style="margin: 5px" />
+      <a-menu-item key="exportEmail" @click="handleMenuAction('exportEmail')">瀵煎嚭閭欢</a-menu-item>
+      <a-menu-item key="trackCustomer" @click="handleMenuAction('trackCustomer')"
+        >寤轰负瀹㈡埛璺熻繘</a-menu-item
+      >
+      <a-menu-item key="createSchedule" @click="handleMenuAction('createSchedule')"
+        >鏂板缓鏃ョ▼</a-menu-item
+      >
+      <a-divider style="margin: 5px" />
+
+      <a-menu-item key="delete" @click="handleMenuAction('delete')">鏍囦负鍨冨溇閭欢</a-menu-item>
+      <a-menu-item key="delete" @click="handleMenuAction('delete')">鍒犻櫎</a-menu-item>
+    </a-menu>
+    <!-- </template> -->
+  </div>
+</template>
+
+<script setup>
+  import { defineProps, defineEmits, computed, ref, reactive, inject } from 'vue';
+  import CustomTimePicker from './CustomTimePicker.vue';
+  const props = defineProps({
+    style: Object,
+    selectedCell: Object,
+  });
+
+  const row = computed(() => props.selectedCell.row);
+  const emit = defineEmits(['close-menu']);
+  import { useRouter } from 'vue-router';
+  const router = useRouter();
+  import { updateReadApi, updateHandleAPi, deleteEmailAPi } from '@/api/email/userList';
+  const getDataList = inject('getDataList');
+  const handleMenuAction = (action) => {
+    const res = props.selectedCell.row;
+    // 鏍规嵁涓嶅悓鐨刟ction鎵ц瀵瑰簲鐨勬搷浣�
+    switch (action) {
+      case 'reply':
+        router.push({ path: '/email/edit', query: { docCode: res.docCode, type: 'reply' } });
+        break;
+      case 'replyAll':
+        router.push({ path: '/email/edit', query: { docCode: res.docCode, type: 'reply' } });
+        break;
+      case 'replyWithAttachment':
+        router.push({ path: '/email/edit', query: { docCode: res.docCode, type: 'reply' } });
+        break;
+      case 'replyAllWithAttachment':
+        router.push({ path: '/email/edit', query: { docCode: res.docCode, type: 'reply' } });
+        break;
+      case 'forward':
+        console.log('Forward action triggered');
+        break;
+      case 'addNote':
+        console.log('Add Note action triggered');
+        break;
+      case 'forwardAsAttachment':
+        console.log('Forward as Attachment action triggered');
+        break;
+      case 'reEdit':
+        console.log('Re-edit action triggered');
+        break;
+      case 'toProcess':
+        console.log('Add to Process action triggered');
+        break;
+      case 'markAs':
+        const data = {
+          status: !res.readFlag,
+          list: [res.docCode],
+        };
+        updateReadApi(data).then((res) => {
+          if (res.code == 0) {
+            getDataList({});
+          }
+        });
+        console.log('Mark as action triggered');
+        break;
+      case 'createRule':
+        console.log('Create rule action triggered');
+        break;
+      case 'moveTo':
+        console.log('Move to action triggered');
+        break;
+      case 'exportEmail':
+        console.log('Export Email action triggered');
+        break;
+      case 'trackCustomer':
+        console.log('Track Customer action triggered');
+        break;
+      case 'createSchedule':
+        console.log('Create Schedule action triggered');
+        break;
+      case 'delete':
+        deleteEmailAPi([res.docCode]).then((res) => {
+          if (res.code == 0) {
+            getDataList({});
+          }
+        });
+        break;
+      default:
+        console.warn('Unknown action:', action);
+    }
+
+    // 鎿嶄綔瀹屾垚鍚庡叧闂彍鍗�
+    emit('close-menu');
+  };
+
+  // 寰呭鐞�
+  import { FieldTimeOutlined, PushpinOutlined } from '@ant-design/icons-vue';
+  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 customTimeDropdownOpen = ref(false);
+  function onSubmitCustomTime() {
+    customTimeDropdownOpen.value = true;
+  }
+
+  const disabledDate = (currentDate) => {
+    return currentDate && currentDate < dayjs().startOf('day');
+  };
+  const cancelCustomTime = () => {
+    customTimeDropdownOpen.value = false;
+    dropdownOpen.value = true;
+  };
+  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 toggleCustomTime = () => {
+    customTimeDropdownOpen.value = !customTimeDropdownOpen.value;
+    dropdownOpen.value = false;
+  };
+  // 琛ㄥ崟鏁版嵁
+  const form = reactive({
+    date: '',
+    time: '',
+  });
+
+  import { useMessage } from '@/hooks/web/useMessage';
+  import { formatToDateDay } from '@/utils/dateUtil';
+
+  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) => {});
+  }
+</script>
+
+<style scoped>
+  .custom-dropdown {
+    position: absolute;
+    z-index: 9900;
+    width: 250px;
+    padding: 10px;
+    border: 1px solid #0505050f;
+    background-color: #fff;
+  }
+
+  .custom-dropdown :deep(.ant-menu-vertical) {
+    border-inline-end: 0;
+  }
+
+  .custom-dropdown :deep(.ant-menu-item) {
+    height: 32px;
+    line-height: 32px;
+  }
+</style>
diff --git a/src/views/email/components/LeftMenu/EmailMenuItem.vue b/src/views/email/components/LeftMenu/EmailMenuItem.vue
new file mode 100644
index 0000000..11eaa7d
--- /dev/null
+++ b/src/views/email/components/LeftMenu/EmailMenuItem.vue
@@ -0,0 +1,44 @@
+<template>
+  <div>
+    <a-sub-menu v-if="item.children.length > 0" :key="item.key">
+      <template #title>
+        <div class="my-display">
+          <span>{{ item.title }}</span>
+        </div>
+      </template>
+      <a-menu-item
+        v-for="child in item.children"
+        :key="child.key"
+        @click="$emit('click', child)"
+        style="display: flex; justify-content: space-between; padding-left: 28px"
+      >
+        <div class="my-display">
+          <span>{{ child.title }}</span>
+          <span v-if="child.total > 0">{{ child.total }}</span>
+        </div>
+      </a-menu-item>
+    </a-sub-menu>
+    <a-menu-item v-else :key="item.key" @click.stop="$emit('click', item)">
+      <div class="my-display">
+        <span>{{ item.title }}</span>
+        <span v-if="item.total > 0" class="my-left">{{ item.total }}</span>
+      </div>
+    </a-menu-item>
+  </div>
+</template>
+
+<script setup>
+  defineProps(['item']);
+</script>
+
+<style scoped>
+  .my-display {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+  }
+
+  .my-left {
+    margin-left: 5px;
+  }
+</style>
diff --git a/src/views/email/components/LeftMenu/MyMenu.vue b/src/views/email/components/LeftMenu/MyMenu.vue
new file mode 100644
index 0000000..930a8f2
--- /dev/null
+++ b/src/views/email/components/LeftMenu/MyMenu.vue
@@ -0,0 +1,121 @@
+<template>
+  <div>
+    <a-sub-menu :key="item.key" :open="isOpen">
+      <template #title>
+        <div>
+          <div v-if="!isSearch" class="my-display">
+            <span>{{ item.title }}</span>
+            <div class="handle-icon">
+              <SearchOutlined v-if="item.key == 'moduleBelowA'" @click.stop="handleSearch(item)" />
+              <MoreOutlined />
+            </div>
+          </div>
+          <a-select
+            v-if="isSearch"
+            ref="inputRef"
+            v-model:value="value"
+            style="width: 100%"
+            show-search
+            placeholder="璇疯緭鍏ユ枃浠跺す鍏抽敭瀛楁悳绱�"
+            :options="options"
+            :field-names="{ label: 'title', value: 'key' }"
+            @change="handleChange"
+            @blur="blurSearch"
+          ></a-select>
+        </div>
+      </template>
+      <template v-for="child in item.children" :key="child.key">
+        <template v-if="!child.children || child.children.length === 0">
+          <a-menu-item :key="child.key" @click="$emit('click', child)">
+            <span>{{ child.title }}</span>
+            <span v-if="child.total > 0" class="my-left">{{ child.total }}</span>
+          </a-menu-item>
+        </template>
+        <template v-else>
+          <!-- 閫掑綊璋冪敤鑷韩 -->
+          <MyMenu :item="child" @click="$emit('click', child)" />
+        </template>
+      </template>
+    </a-sub-menu>
+  </div>
+</template>
+
+<script lang="ts" setup>
+  import { defineProps, defineEmits, ref, computed } from 'vue';
+  import { SearchOutlined, MoreOutlined } from '@ant-design/icons-vue';
+
+  interface MenuItem {
+    title: string;
+    key: string;
+    total: number;
+    children?: MenuItem[];
+  }
+
+  const props = defineProps<{ item: MenuItem }>();
+  const emit = defineEmits();
+  const options = ref([]);
+  const isSearch = ref(false);
+  const inputRef = ref();
+  const value = ref();
+
+  // 璁$畻灞炴�э紝鍒ゆ柇褰撳墠鑿滃崟椤规槸鍚﹀簲灞曞紑
+  const isOpen = computed(() => {
+    return true; // 榛樿涓哄睍寮�鐘舵��
+  });
+
+  function handleSearch(item: MenuItem) {
+    console.log('handleSearch', item);
+    options.value = flattenMenuItems(item.children);
+    console.log('options', options.value);
+    isSearch.value = true;
+    setTimeout(() => {
+      inputRef.value.focus();
+    }, 100);
+    emit('handleSearch', item);
+  }
+
+  function blurSearch() {
+    isSearch.value = false;
+  }
+
+  const handleChange = (value: string) => {
+    console.log(`selected ${value}`, isSearch.value);
+    isSearch.value = false;
+    emit('updateSelectedKeys', value);
+  };
+
+  const flattenMenuItems = (items: MenuItem[]): MenuItem[] => {
+    let result: MenuItem[] = [];
+    const recursiveFlatten = (items: MenuItem[]) => {
+      for (const item of items) {
+        result.push({ ...item, children: undefined }); // 涓嶄繚鐣� children
+        if (item.children && item.children.length > 0) {
+          recursiveFlatten(item.children);
+        }
+      }
+    };
+    recursiveFlatten(items);
+    return result;
+  };
+</script>
+
+<style scoped>
+  .my-display {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+  }
+
+  .my-left {
+    margin-left: 5px;
+  }
+
+  .handle-icon {
+    display: none;
+    font-size: 16px;
+  }
+
+  .my-display:hover .handle-icon {
+    display: block;
+  }
+</style>
diff --git a/src/views/email/components/LeftMenu/index.vue b/src/views/email/components/LeftMenu/index.vue
index ad5e369..24483b3 100644
--- a/src/views/email/components/LeftMenu/index.vue
+++ b/src/views/email/components/LeftMenu/index.vue
@@ -27,22 +27,34 @@
           v-model:open-keys="openKeys"
           v-model:selected-keys="selectedKeys"
           mode="inline"
-          :popupClassName="popupClassName"
         >
           <template v-for="item in menuItems" :key="item.key">
-            <render-menu-item :item="item" @click="handleClick" />
+            <EmailMenuItem :item="item" @click="handleClick" />
           </template>
         </a-menu>
         <a-divider />
         <a-menu
           id="email-left-nav2"
-          v-model:open-keys="openKeys"
-          v-model:selected-keys="selectedKeys"
+          v-model:open-keys="openKeys2"
+          v-model:selected-keys="selectedKeys2"
           mode="inline"
-          :popupClassName="popupClassName"
         >
           <template v-for="item in menuItems2" :key="item.key">
-            <render-menu-item :item="item" @click="handleClick" />
+            <template v-if="!item.children">
+              <a-menu-item :key="item.key" @click="$emit('click', item)">
+                <div class="my-display">
+                  <span>{{ item.title }}</span>
+                  <span v-if="item.total > 0" class="my-left">{{ item.total }}</span>
+                </div>
+              </a-menu-item>
+            </template>
+            <template v-else>
+              <MyMenu
+                :item="item"
+                @click.stop="handleClickMyMenu(item)"
+                @updateSelectedKeys="updateSelectedKeys"
+              />
+            </template>
           </template>
         </a-menu>
       </div>
@@ -51,112 +63,178 @@
 </template>
 
 <script lang="ts" setup>
-import { ref, onMounted, computed } from 'vue';
-import { PageWrapper } from '@/components/Page';
-import { MailOutlined, UserOutlined } from '@ant-design/icons-vue';
-import { getEmailModuleApi, getEmailModuleBelowApi } from '@/api/email/userList';
-import { useRouter } from 'vue-router';
+  import { ref, onMounted } from 'vue';
+  import { PageWrapper } from '@/components/Page';
+  import { DingdingOutlined, MailOutlined, UserOutlined } from '@ant-design/icons-vue';
+  import { getEmailModuleApi, getEmailModuleBelowApi } from '@/api/email/userList';
+  import { useRouter } from 'vue-router';
+  import MyMenu from './MyMenu.vue';
+  import EmailMenuItem from './EmailMenuItem.vue';
 
-interface MenuItem {
-  key: string;
-  title: string;
-  total?: number;
-  children?: MenuItem[];
-}
+  const selectedKeys = ref<string[]>(['Index']);
+  const openKeys = ref<string[]>(['Inbox']);
+  const openKeys2 = ref(['0049LM']);
+  const selectedKeys2 = ref([]);
 
-const selectedKeys = ref<string[]>(['Index']);
-const openKeys = ref<string[]>(['Inbox']);
-const items = ref<MenuItem[]>([]);
-const items2 = ref<MenuItem[]>([]);
+  const menuItems = ref([]);
+  const menuItems2 = ref([]);
 
-const fetchEmailModules = async () => {
-  try {
-    const [res, res2] = await Promise.all([getEmailModuleApi(), getEmailModuleBelowApi()]);
-    items.value = convertRoutesToMenuItems(res.data);
-    items2.value = convertRoutesToMenuItems2(res2.data);
-  } catch (error) {
-    console.error('鑾峰彇閭妯″潡澶辫触:', error);
+  const fetchEmailModules = async () => {
+    try {
+      const [res, res2] = await Promise.all([getEmailModuleApi(), getEmailModuleBelowApi()]);
+      menuItems.value = convertRoutesToMenuItems(res.data);
+      menuItems2.value = convertRoutesToMenuItems2(res2.data);
+      console.log('menuItems:', menuItems2.value);
+    } catch (error) {
+      console.error('鑾峰彇閭妯″潡澶辫触:', error);
+    }
+  };
+
+  const convertRoutesToMenuItems = (routes: any[]) => {
+    return routes
+      .map((route) => ({
+        key: route.key,
+        title: route.mailName,
+        total: route.total,
+        children: route.children ? convertRoutesToMenuItems(route.children) : undefined,
+      }))
+      .filter(Boolean);
+  };
+
+  const convertRoutesToMenuItems2 = (routes: any[], path) => {
+    return routes
+      .map((route) => ({
+        key: route.key,
+        title: route.name,
+        total: route.number,
+        path: path ? path : route.key,
+        children: route.list
+          ? convertRoutesToMenuItems2(route.list, path ? path : route.key)
+          : undefined,
+      }))
+      .filter(Boolean);
+  };
+
+  onMounted(fetchEmailModules);
+
+  const routesConfig = {
+    InboxPage1: '/email/index',
+    receiver: '/email/Inbox/list',
+    sender: '/email/outbox/list',
+    IndexPage1: '/email/outbox',
+    moduleBelowA: '/email/index',
+    moduleBelowB: '/email/index',
+    moduleBelowC: '/email/index',
+  };
+
+  const router = useRouter();
+  // const handleClick = (item: any) => {
+  //   selectedKeys2.value = [];
+  //   debugger
+  //   const routePath =
+  //     routesConfig[item.key] || router.getRoutes().find((r) => r.name === item.key)?.path;
+  //   if (routePath) {
+
+  //     router.push(routePath);
+  //   } else {
+  //     console.warn(`Unknown key: ${item.key}`);
+  //   }
+  // };
+  const handleClick = (e: any) => {
+    selectedKeys2.value = [];
+
+    let matched = false;
+    const route = router.getRoutes() || [];
+    route.forEach((item) => {
+      if (item.name === e.key) {
+        router.push(item.path);
+        matched = true;
+        return; // 璺冲嚭褰撳墠寰幆
+      }
+
+      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 updateSelectedKeys = (keys) => {
+    openKeys2.value = findParentKeys(menuItems2.value[0], keys);
+    selectedKeys2.value = [keys];
+  };
+
+  function findParentKeys(data, targetKey, path = []) {
+    path.push(data.key);
+    if (data.key === targetKey) {
+      return path;
+    }
+
+    for (const item of data.children || []) {
+      const result = findParentKeys(item, targetKey, path.slice());
+      if (result) {
+        return result;
+      }
+    }
+    return null;
   }
-};
-
-const convertRoutesToMenuItems = (routes: any[]): MenuItem[] => {
-  return routes
-    .map(route => ({
-      key: route.key,
-      title: route.mailName,
-      total: route.total,
-      children: route.children ? convertRoutesToMenuItems(route.children) : undefined,
-    }))
-    .filter(Boolean) as MenuItem[];
-};
-
-const convertRoutesToMenuItems2 = (routes: any[]): MenuItem[] => {
-  return routes
-    .map(route => ({
-      key: route.key,
-      title: route.name,
-      total: route.number,
-      children: route.list ? convertRoutesToMenuItems2(route.list) : undefined,
-    }))
-    .filter(Boolean) as MenuItem[];
-};
-
-onMounted(fetchEmailModules);
-
-const routesConfig = {
-  InboxPage1: '/email/index',
-  receiver: '/email/Inbox/list',
-  sender: '/email/outbox/list',
-  IndexPage1: '/email/outbox',
-};
-
-const router = useRouter();
-const handleClick = (item: MenuItem) => {
-  const routePath = routesConfig[item.key] || router.getRoutes().find(r => r.name === item.key)?.path;
-  if (routePath) {
-    router.push(routePath);
-  } else {
-    console.warn(`Unknown key: ${item.key}`);
-  }
-};
-
-const popupClassName = {
-  display: 'flex',
-  'align-items': 'center',
-  'justify-content': 'space-between',
-};
+  const handleClickMyMenu = (item: any) => {
+    selectedKeys.value = [];
+    // console.log('handleClickMyMenu:', item);
+    router.push({ path: '/email/folder' });
+    console.log('updateSelectedKeys111111112333:', openKeys2.value);
+  };
 </script>
 
 <style lang="less" scoped>
-.header-container {
-  height: 15vh;
-  padding: 20px 40px;
-  text-align: center;
-}
+  .header-container {
+    height: 15vh;
+    padding: 20px 40px;
+    text-align: center;
+  }
 
-.button-group {
-  display: flex;
-  justify-content: space-around;
-}
+  .button-group {
+    display: flex;
+    justify-content: space-around;
+  }
 
-.write-button {
-  width: 100%;
-  margin-top: 10px;
-  padding: 0 30px;
-}
+  .write-button {
+    width: 100%;
+    margin-top: 10px;
+    padding: 0 30px;
+  }
 
-.menu-container {
-  height: 70vh;
-  overflow: auto;
-}
+  .menu-container {
+    height: 75vh;
+    overflow: auto;
+  }
 
-.my-display {
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-}
+  .my-display {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+  }
 
-.my-left {
-  margin-left: 5px;
-}
+  .my-left {
+    margin-left: 5px;
+  }
 </style>
diff --git a/src/views/email/components/LeftNav.vue b/src/views/email/components/LeftNav.vue
deleted file mode 100644
index ebb5161..0000000
--- a/src/views/email/components/LeftNav.vue
+++ /dev/null
@@ -1,181 +0,0 @@
-锘�<template>
-  <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, 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[]>(['Index']);
-  const openKeys = ref<string[]>(['Inbox']);
-  const items = ref([]); // 瀹氫箟 items 绫诲瀷
-
-  const fnGetEmailModule = async () => {
-    try {
-      const res = await getEmailModuleApi();
-      items.value = convertRoutesToMenuItems(res.data);
-    } catch (error) {
-      console.error('鑾峰彇閭妯″潡澶辫触:', error); // 澶勭悊閿欒
-    }
-  };
-
-  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
-  };
-
-  // 鐢熷懡鍛ㄦ湡閽╁瓙锛屽姞杞借彍鍗曟暟鎹�
-  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; // 璺冲嚭褰撳墠寰幆
-      }
-
-      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
index d179450..059b322 100644
--- a/src/views/email/components/ListPage/TooltipAndDropdown .vue
+++ b/src/views/email/components/ListPage/TooltipAndDropdown .vue
@@ -43,7 +43,7 @@
           <a-divider style="margin: 5px 0" />
           <div class="date p-1">
             <a-popover
-              trigger="click"
+              :trigger="trigger"
               title="鑷畾涔夋椂闂�"
               v-model:open="customTimeDropdownOpen"
               @confirm="onSubmitCustomTime"
@@ -167,6 +167,10 @@
     initialTooltipOpen: Boolean, // Tooltip 鍒濆鎵撳紑鐘舵��
     row: Object, // 褰撳墠琛屽璞�
     docCodeS: Array,
+    trigger: {
+      type: String,
+      default: 'click',
+    },
   });
 
   const emit = defineEmits(['updateHandleTime', 'completeAction', 'customTimeSubmit', 'tagRow']);
@@ -183,6 +187,7 @@
       handleTime: date,
       docCode: props.docCodeS,
     };
+
     pushUpdateHandle(data);
   }
   function pushUpdateHandle(data) {
@@ -226,27 +231,26 @@
 
   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;
-        }
-      });
+    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 = () => {
diff --git a/src/views/email/components/ListPage/drawerDetail.vue b/src/views/email/components/ListPage/drawerDetail.vue
index 862ff7c..e4a020f 100644
--- a/src/views/email/components/ListPage/drawerDetail.vue
+++ b/src/views/email/components/ListPage/drawerDetail.vue
@@ -53,9 +53,19 @@
           <span style="margin-right: 10px; font-size: 16px">
             <PushpinOutlined />
           </span>
+          <a-tag
+            v-if="tableRowData.attachmentPath?.length > 0"
+            style="margin-right: 10px; font-size: 12px"
+          >
+            <PaperClipOutlined style="margin-right: 4px; color: #ffac00" />{{
+              tableRowData.attachmentPath?.length
+            }}
+          </a-tag>
         </div>
         <div class="right">
-          <div class="tate">{{ formatToDateDay(tableRowData.receiveTime || tableRowData.updateTime) }}</div>
+          <div class="tate">{{
+            formatToDateDay(tableRowData.receiveTime || tableRowData.updateTime)
+          }}</div>
           <div>
             <a-dropdown-button>
               <div v-if="!isDrafts">
@@ -142,6 +152,28 @@
               <span>鏆傛湭鏌ヨ鍒拌瀹㈡埛鐨勫綋鍦版椂闂�</span>
               <!-- <span>2024-06-08 22:22</span> -->
             </div>
+
+            <div v-if="tableRowData.attachmentPath?.length>0" class="p-2 f-z-14" style="margin-top: 10px">
+              <div style="display: flex; align-items: center">
+                <span style="margin-right: 10px">{{
+                  `闄勪欢(${tableRowData.attachmentPath?.length})`
+                }}</span>
+                <a type="link" @click="fnAllDownload">鍏ㄩ儴涓嬭浇</a>
+              </div>
+              <div class="my-d-f" style="width: 100%; margin-top: 10px">
+                <div
+                  class="file-item my-d-f"
+                  v-for="item in tableRowData.attachmentPath"
+                  :key="item"
+                >
+                  <div class="icon"><FileExcelOutlined /></div>
+                  <div class="name" @click="fnPreview(item)">{{ item.name }}</div>
+                  <div class="size"> {{ item.size + 'k' }} </div>
+                  <div class="download" @click="fnDownload(item)"><CloudDownloadOutlined /></div>
+                  <!-- <div class="delete">鍒犻櫎</div> -->
+                </div>
+              </div>
+            </div>
             <div class="ct">
               <div v-if="tableRowData.content">
                 <TinymcePw ref="TinymcePwRef" v-model="tableRowData.content" />
@@ -177,6 +209,9 @@
     CopyOutlined,
     UserOutlined,
     RollbackOutlined,
+    FileExcelOutlined,
+    CloudDownloadOutlined,
+    PaperClipOutlined,
   } from '@ant-design/icons-vue';
   import pageHeadLeft from './pageHeadLeft.vue';
   import UserTips from '@/views/email/components/userTips/index.vue';
@@ -322,6 +357,40 @@
   function replyEmail(row, type) {
     router.push({ path: '/email/edit', query: { docCode: row.docCode, type: type } });
   }
+
+  const fnPreview = (item) => {
+    if (!item) {
+      console.error('Invalid file or response');
+      return;
+    }
+
+    // // 鑾峰彇鏂囦欢绫诲瀷锛堥�氳繃鏂囦欢鎵╁睍鍚嶆垨 MIME 绫诲瀷锛�
+    const fileExt = item.fileType; // 鑾峰彇鏂囦欢鎵╁睍鍚�
+    const isImage = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp'].includes(fileExt); // 鍒ゆ柇鏄惁涓哄浘鐗�
+
+    if (isImage) {
+      // 鐩存帴鎵撳紑鍥剧墖
+      window.open(item.url, '_blank');
+    } else {
+      const iframeSrc = `https://view.officeapps.live.com/op/view.aspx?src=${item.url}`;
+      window.open(iframeSrc, '_blank');
+    }
+  };
+
+  const fnDownload = (item) => {
+    const link = document.createElement('a');
+    link.href = item.url;
+    link.download = item.name; // 鎻愬彇鏂囦欢鍚�
+    document.body.appendChild(link); // 灏嗛摼鎺ユ坊鍔犲埌 DOM
+    link.click(); // 妯℃嫙鐐瑰嚮涓嬭浇
+    document.body.removeChild(link); // 涓嬭浇鍚庣Щ闄ら摼鎺�
+  };
+
+  const fnAllDownload = () => {
+    const urls = tableRowData.value.attachmentPath.map((item) => item.url);
+    const url = urls.join(',');
+    fnDownload(url);
+  };
 </script>
 
 <style scoped lang="less">
@@ -445,4 +514,45 @@
   .f-z-14 {
     font-size: 12px;
   }
+
+  .file-item {
+    width: 240px;
+    margin-right: 10px;
+    padding: 10px;
+    border-radius: 4px;
+    background-color: #f0f0f0;
+
+    & .icon {
+      width: 20px;
+      margin-right: 5px;
+    }
+
+    & .name {
+      flex: 1;
+      margin-right: 5px;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+    }
+
+    & .download {
+      display: none;
+      width: 20px;
+    }
+  }
+
+  .file-item:hover .name {
+    transition: 0.3s;
+    color: #0960bd;
+  }
+
+  .file-item:hover .size {
+    display: none;
+    transition: 0.3s;
+  }
+
+  .file-item:hover .download {
+    display: block;
+    transition: 0.3s;
+  }
 </style>
diff --git a/src/views/email/components/ListPage/list.vue b/src/views/email/components/ListPage/list.vue
index 3ac5a6e..ccaa7fb 100644
--- a/src/views/email/components/ListPage/list.vue
+++ b/src/views/email/components/ListPage/list.vue
@@ -23,10 +23,11 @@
         </div>
 
         <div class="right p-3"
-          >鍏�<span style="padding: 0 5px">{{page.total}}</span>灏�
+          >鍏�<span style="padding: 0 5px">{{ page.total }}</span
+          >灏�
           <a-pagination
             v-model:current="pageCurrent"
-             v-model:page-size='page.limit'
+            v-model:page-size="page.limit"
             simple
             :total="page.total"
             style="margin-left: 10px"
@@ -61,27 +62,39 @@
           </a-switch>
         </div>
       </div>
-      <div v-if="checked" style="height: 30px;" class="left-bt p-3">
+      <div v-if="checked" style="height: 30px" 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"
-              :isDrafts="isDrafts"
-              @selectAll="fnSelectAll"
-              @updateSelectAll="updateSelectAll"
-            />
-          </a-tab-pane>
-        </a-tabs>
+      <div class="p-4" style="height: 90%; overflow: auto">
+        <div v-if="isTabs">
+          <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"
+                :isDrafts="isDrafts"
+                @selectAll="fnSelectAll"
+                @updateSelectAll="updateSelectAll"
+              />
+            </a-tab-pane>
+          </a-tabs>
+        </div>
+        <div v-else>
+          <Table
+            ref="tableRef"
+            :page="pageCurrent"
+            :pageList="newList"
+            :isDrafts="isDrafts"
+            @selectAll="fnSelectAll"
+            @updateSelectAll="updateSelectAll"
+          />
+        </div>
       </div>
     </div>
   </PageWrapper>
@@ -98,7 +111,7 @@
   import pageHeadLeft from './pageHeadLeft.vue';
   import { PageWrapper } from '@/components/Page';
 
-  import { ref, watch, defineProps, defineEmits, computed, reactive, onMounted,inject } from 'vue';
+  import { ref, watch, defineProps, defineEmits, computed, reactive, onMounted, inject } from 'vue';
 
   // 瀹氫箟灞炴��
   interface Props {
@@ -106,8 +119,30 @@
     pageData?: any;
     mailType?: number;
     isDrafts?: boolean;
+    isTabs?: boolean;
   }
-  const props = defineProps<Props>();
+  const props = defineProps({
+    isTabs: {
+      type: Boolean,
+      default: true,
+    },
+    pageList: {
+      type: Array,
+      default: () => [],
+    },
+    pageData: {
+      type: Object,
+      default: () => {},
+    },
+    mailType: {
+      type: Number,
+      default: 0,
+    },
+    isDrafts: {
+      type: Boolean,
+      default: false,
+    },
+  });
   const newList = ref([]);
   const selectAllRow = ref([]);
   watch(
@@ -117,7 +152,7 @@
     },
   );
 
-const page = computed(() => props.pageData);
+  const page = computed(() => props.pageData);
   const checked = computed(() => selectAllRow.value.length > 0);
   const pageCurrent = ref(1);
   const tableRef = ref();
@@ -189,9 +224,8 @@
   });
 
   const getDataList = inject('getDataList');
-  function handlePageChange(page, pageSize){
-    getDataList(page)
-    
+  function handlePageChange(page, pageSize) {
+    getDataList(page);
   }
 </script>
 <style scoped lang="less">
@@ -212,7 +246,7 @@
         align-items: center;
         justify-content: space-flex-start;
         width: 100%;
-        height: 100%;;
+        height: 100%;
 
         & .icon {
           margin-right: 15px;
diff --git a/src/views/email/components/ListPage/table.vue b/src/views/email/components/ListPage/table.vue
index 9e107d4..141caa4 100644
--- a/src/views/email/components/ListPage/table.vue
+++ b/src/views/email/components/ListPage/table.vue
@@ -11,10 +11,10 @@
           size="small"
           min-height="40px"
           :row-config="{ isCurrent: true, isHover: true }"
-          :menu-config="tableMenu"
-          @menu-click="contextMenuClickEvent"
+          :menu-config="{ enabled: true }"
           @cell-click="cellClickEvent"
           @checkbox-change="selectChangeEvent"
+          @cell-menu="onCellContextMenu"
         >
           <vxe-column type="checkbox" width="30"></vxe-column>
           <vxe-column field="sender" title="鍙戜欢浜�" data-index="sender" min-width="300px">
@@ -78,7 +78,7 @@
                       <a-button type="link" size="small">寰�鏉ラ偖浠�</a-button></div
                     >
                   </template>
-                  <div class="title-dot" :class="fnIsItHighlighted(row) ? 'title-dot-color' : ''">
+                  <div :class="fnIsItHighlighted(row) ? '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>
@@ -87,6 +87,26 @@
               </div>
             </template>
           </vxe-column>
+          <vxe-column
+            show-overflow
+            field="icon"
+            title="琛ㄩ"
+            data-index="icon"
+            width="100"
+            align="right"
+          >
+            <template #default="{ row }">
+              <a-tooltip placement="bottom">
+                <template #title>
+                  <span>鏈夐檮浠�</span>
+                </template>
+                <span v-show="row.attachmentList?.length > 0">
+                  <PaperClipOutlined />
+                </span>
+              </a-tooltip>
+            </template>
+          </vxe-column>
+
           <vxe-column
             show-overflow
             field="subject"
@@ -110,7 +130,7 @@
               <span style="display: flex; justify-content: space-around">
                 <span>{{
                   row.mailType !== 0
-                    ? formatToDateDay(row.receiveTime)
+                    ? formatToDateDay(row.receiveTime || row.createTime)
                     : formatToDateDay(row.createTime)
                 }}</span>
 
@@ -126,7 +146,13 @@
               </span>
             </template>
           </vxe-column>
-        </vxe-table> </div
+        </vxe-table>
+        <ContextMenu
+          v-if="showMenu"
+          :style="menuStyle"
+          :selected-cell="selectedCell"
+          @close-menu="showMenu = false"
+        /> </div
     ></div>
 
     <div v-else style="display: flex; align-items: center; justify-content: center; height: 70vh">
@@ -151,6 +177,7 @@
     PushpinOutlined,
     CopyOutlined,
     DownOutlined,
+    PaperClipOutlined,
   } from '@ant-design/icons-vue';
 
   import { ref, watch, defineProps, defineEmits, computed, defineExpose, inject } from 'vue';
@@ -268,57 +295,63 @@
   }
 
   // 鍙抽敭鑿滃崟
-  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: '鍒犻櫎' },
-        ],
-      ],
-    },
+  import ContextMenu from '@/views/email/components/ContextMenu/index.vue';
+  const showMenu = ref(false);
+  const menuStyle = ref({});
+  const selectedCell = ref(null);
+  const onCellContextMenu = ({
+    type,
+    row,
+    rowIndex,
+    $rowIndex,
+    column,
+    columnIndex,
+    $columnIndex,
+    $event,
+  }) => {
+    $event.preventDefault(); // 闃绘榛樿鐨勬祻瑙堝櫒鍙抽敭鑿滃崟?
+    selectedCell.value = { row, column, columnIndex }; // 淇濆瓨褰撳墠閫変腑鐨勫崟鍏冩牸鏁版嵁
+
+    // 璁$畻鑿滃崟鍒濆浣嶇疆
+    let menuX = $event.clientX;
+    let menuY = $event.clientY;
+
+    // 鑾峰彇鑿滃崟鐨勫搴﹀拰楂樺害
+    const menuWidth = 200; // 鍋囪鑿滃崟瀹藉害涓�200px锛屽彲浠ユ牴鎹疄闄呮儏鍐佃皟鏁�
+    const menuHeight = 800; // 鍋囪鑿滃崟楂樺害涓�150px锛屽彲浠ユ牴鎹疄闄呮儏鍐佃皟鏁�
+    // 鑾峰彇绐楀彛瀹藉害鍜岄珮搴�
+    const windowWidth = window.innerWidth;
+    const windowHeight = window.innerHeight;
+
+    // 妫�鏌ヨ彍鍗曟槸鍚﹁秴鍑虹獥鍙g殑鍙宠竟鐣岋紝濡傛灉鏄紝鍒欏悜宸﹁皟鏁�
+    if (menuX + menuWidth > windowWidth) {
+      menuX = windowWidth - menuWidth;
+    }
+
+    // 妫�鏌ヨ彍鍗曟槸鍚﹁秴鍑虹獥鍙g殑涓嬭竟鐣岋紝濡傛灉鏄紝鍒欏悜涓婅皟鏁�
+    if (menuY + menuHeight > windowHeight) {
+      menuY = windowHeight - menuHeight;
+    }
+    menuStyle.value = {
+      position: 'fixed',
+      top: `${menuY}px`,
+      left: `${menuX}px`,
+    };
+    showMenu.value = true;
   };
 
-  function contextMenuClickEvent({ menu, row, column }) {
-    switch (menu.code) {
-      case 'copy':
-        if (row && column) {
-        }
-        break;
-      default:
-    }
-  }
+  // 鐩戝惉鍏ㄥ眬鐐瑰嚮浜嬩欢锛岀偣鍑婚〉闈㈠叾浠栧湴鏂瑰叧闂彍鍗�
+  document.addEventListener('click', () => {
+    showMenu.value = false;
+  });
+
   const vxeTableRef = ref();
   function fnIsItHighlighted(row) {
-    return row.readFlag && props.isDrafts;
+    if (props.isDrafts) {
+      return row.readFlag && props.isDrafts;
+    } else {
+      return row.readFlag;
+    }
   }
   function fnSelectAll(is) {
     try {
@@ -462,9 +495,11 @@
 
   .span-title {
     width: 100%;
+    height: 30px;
     padding: 5px;
     color: #000;
     font-weight: 700;
+    line-height: 30px;
     text-align: left;
   }
 
diff --git a/src/views/email/folder/index.vue b/src/views/email/folder/index.vue
new file mode 100644
index 0000000..af20b1c
--- /dev/null
+++ b/src/views/email/folder/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" :isTabs="false"> </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/label/index.vue b/src/views/email/label/index.vue
new file mode 100644
index 0000000..af20b1c
--- /dev/null
+++ b/src/views/email/label/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" :isTabs="false"> </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/vite.config.ts b/vite.config.ts
index fe823e1..8ba3d87 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -31,7 +31,7 @@
           // secure: false
         },
         '/upload': {
-          target: 'http://localhost:3300/upload',
+          target: 'http://yingchen.onbus.cn:9010/attachment/uploadAttachmentV2.do',
           changeOrigin: true,
           ws: true,
           rewrite: (path) => path.replace(new RegExp(`^/upload`), ''),

--
Gitblit v1.8.0