74a35fac4332a8b060a92c605524ed12faf2755a..cb21a5aa46ecf7a58e3e6bee3fd7f3352ec909b5
9 天以前 huangyinfeng
邮件配置黑名单功能
cb21a5 对比 | 目录
9 天以前 huangyinfeng
邮件配置页
63d608 对比 | 目录
1个文件已删除
5个文件已添加
19个文件已修改
2416 ■■■■■ 已修改文件
src/App.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/email/userList.ts 131 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/ColorPicker/index.vue 160 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layouts/page/email.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/routes/modules/email.ts 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/email/Drafts/index.vue 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/email/Edit/index.vue 48 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/email/HandlingEmailsOnBehalfOfOthers/components/list.vue 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/email/HandlingEmailsOnBehalfOfOthers/index.vue 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/email/HandlingEmailsOnBehalfOfOthers/table.vue 443 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/email/Inbox/index.vue 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/email/UnreadEmail/index.vue 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/email/Utils/blacklist.vue 242 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/email/Utils/convention.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/email/Utils/folder.vue 200 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/email/Utils/index.vue 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/email/Utils/label.vue 260 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/email/components/LeftNav.vue 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/email/components/ListPage/drawerDetail.vue 251 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/email/components/ListPage/list.vue 35 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/email/components/ListPage/table.vue 254 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/email/components/SelectUser/index.vue 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/email/index.vue 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/email/outbox/index.vue 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
uno.config.ts 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/App.vue
@@ -26,7 +26,7 @@
    // 先检查proxy是否存在,再进行操作
    if (proxy && proxy.$cookies) {
      proxy.$cookies.remove('JSESSIONID');
      proxy.$cookies.set('JSESSIONID', 'A2EB010772E1D486E221B4DF4EA6E912.jvm_59_9010', '1d');
      proxy.$cookies.set('JSESSIONID', '23542F948D2C450599CF5850631B432D.jvm_59_9010', '1d');
    } else {
      console.error('proxy对象未初始化或不包含$cookies属性');
    }
src/api/email/userList.ts
@@ -33,7 +33,21 @@
  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 });
@@ -155,8 +169,113 @@
  });
// 快速回复
  export const setQuickReplyAPi = (params) =>
    defHttp.post({
      url: Api.SET_QUICK_REPLY,
      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,
  });
src/components/ColorPicker/index.vue
New file
@@ -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>
src/layouts/page/email.vue
@@ -2,7 +2,7 @@
  <div>
    <PageWrapper :class="`${prefixCls}`" dense contentFullHeight fixedHeight>
      <div class="default-theme" style="display: flex;height: 100%;background-color: #fff;">
        <div style="width: 16%;height: 100%;">
        <div style="width: 10%;height: 100%;">
          <LeftNav></LeftNav>
        </div>
        <div style="width: 84%;height: 100%;">
src/router/routes/modules/email.ts
@@ -30,18 +30,17 @@
          component: () => import('@/views/email/Inbox/index.vue'),
          meta: {
            title: '全部邮件',
            currentActiveMenu: '/email/index',
          },
        },
        {
          path: 'list/:id',
          name: 'EmailPage2',
          component: () => import('@/views/email/UnreadEmail/user.vue'),
          meta: {
            title: '222',
            currentActiveMenu: '/email/index',
          },
        },
        // {
        //   path: 'list/:id',
        //   name: 'EmailPage2',
        //   component: () => import('@/views/email/UnreadEmail/user.vue'),
        //   meta: {
        //     title: '222',
        //     currentActiveMenu: '/email/index',
        //   },
        // },
      ],
    },
    {
@@ -50,7 +49,6 @@
      component: () => import('@/views/email/UnreadEmail/index.vue'),
      meta: {
        title: '未读邮件',
        currentActiveMenu: '/email/index',
      },
    },
    {
@@ -80,9 +78,8 @@
      name: 'HandlingEmailsOnBehalfOfOthers',
      component: () => import('@/views/email/HandlingEmailsOnBehalfOfOthers/index.vue'),
      meta: {
        title: '代处理邮件',
        title: '待处理邮件',
        hideTab: true,
        currentActiveMenu: '/email/index',
      },
    },
@@ -93,29 +90,38 @@
      meta: {
        title: '草稿箱',
        hideTab: true,
        currentActiveMenu: '/email/index',
      },
    },
    {
      path: 'ShippingBox',
      name: 'ShippingBox',
      component: () => import('@/views/email/Edit/index.vue'),
      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',
      },
    },
    // {
    //   path: 'MassMailbox',
    //   name: 'MassMailbox',
    //   component: () => import('@/views/email/Edit/index.vue'),
    //   meta: {
    //     title: '群发箱',
    //     hideTab: true,
    //     currentActiveMenu: '/email/index',
    //   },
    // },
  ],
};
src/views/email/Drafts/index.vue
@@ -1,6 +1,8 @@
<template>
  <div>
    <PageIndex :pageList="pageList" :mailType='0'></PageIndex>
    <a-spin :spinning="loading" class="p-1" style="height: 100%">
      <PageIndex :pageList="pageList" :mailType="1" :pageData="pageData"> </PageIndex>
    </a-spin>
  </div>
</template>
@@ -29,12 +31,24 @@
  });
  import { receiveApi, getMailListApi } from '@/api/email/userList';
  const pageList = ref([]);
  function getDataList() {
    getMailListApi({ mail: routerId.value, mailType: 0 }).then((res) => {
      if (res.code == 0) {
        pageList.value = res.data;
      }
    });
  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(() => {
src/views/email/Edit/index.vue
@@ -30,7 +30,7 @@
                placeholder="请选择收件人或者输入收件人邮箱"
                v-model:value="modelRef.recipients"
                :options="state.recipientsList"
                :field-names="{ label: 'email', value: 'userName' }"
                :field-names="{ label: 'email', value: 'email' }"
                :maxTagCount="4"
                @search="fetchUser"
              >
@@ -111,7 +111,7 @@
                placeholder="请选择抄送人或者输入抄送人邮箱"
                v-model:value="modelRef.ccRecipients"
                :options="state.recipientsList"
                :field-names="{ label: 'email', value: 'userName' }"
                :field-names="{ label: 'email', value: 'email' }"
                :maxTagCount="4"
              >
                <template #tagRender="{ label, closable, onClose }">
@@ -151,7 +151,7 @@
                placeholder="请选择抄送人或者输入抄送人邮箱"
                v-model:value="modelRef.bccRecipients"
                :options="state.recipientsList"
                :field-names="{ label: 'email', value: 'userName' }"
                :field-names="{ label: 'email', value: 'email' }"
                :maxTagCount="4"
              >
                <template #tagRender="{ label, closable, onClose }">
@@ -192,7 +192,7 @@
          :wrapper-col="{ span: 24 }"
          :label-col="{ span: 2 }"
        >
          <Tinymce @change="fnChangeContent"></Tinymce>
          <Tinymce v-model="modelRef.content" @change="fnChangeContent"></Tinymce>
        </a-form-item>
      </a-form>
    </a-spin>
@@ -202,7 +202,6 @@
      :selectIds="selectIds"
      :idName="idName"
      :title="selectUserTitle"
      @sudUserList="fnGetRecipientsList"
      @updateData="fnSelectUser"
    />
    <a-modal
@@ -257,7 +256,7 @@
  name: 'Edit';
  import { useDesign } from '@/hooks/web/useDesign';
  import { ref, reactive, computed } from 'vue';
  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';
@@ -293,6 +292,15 @@
      },
    ],
  });
  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);
@@ -318,6 +326,7 @@
    sendingMailApi,
    saveMailDraftsApi,
    emailListAPi,
    getMailInfoApi
  } from '@/api/email/userList';
  // 定义状态管理对象
  const state = reactive({
@@ -510,14 +519,14 @@
    }, 2000);
  };
  function fnGetRecipientsList(data) {
    state.recipientsList = data;
  function fnGetRecipientsList() {
    emailListAPi({ key: '' }).then((body) => {
      state.recipientsList = body.data;
    });
  }
  // 邮箱校验正则表达式
  const validateEmail = (email) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
  import { debounce } from 'lodash-es';
import DragBar from '@/layouts/default/sider/DragBar.vue';
  let lastFetchId = 0;
  const fetchUser = debounce((value) => {
@@ -535,6 +544,25 @@
      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';
src/views/email/HandlingEmailsOnBehalfOfOthers/components/list.vue
@@ -14,9 +14,11 @@
            ></a-checkbox>
            <!--更新  -->
            <SyncOutlined class="icon" v-show="!checked" />
            <pageHeadLeft :checked="checked" :selectAllRow="selectAllRow"
            :parentTableList='newList'
           ></pageHeadLeft>
            <pageHeadLeft
              :checked="checked"
              :selectAllRow="selectAllRow"
              :parentTableList="newList"
            ></pageHeadLeft>
          </div>
        </div>
@@ -24,9 +26,11 @@
          >共<span style="padding: 0 5px">20</span>封
          <a-pagination
            v-model:current="pageCurrent"
            v-model:page-size='page.limit'
            simple
            :total="50"
            :total="page.total"
            style="margin-left: 10px"
            @change="handlePageChange"
          />
          <FilterOutlined style="margin-left: 10px" />
          <a-popover placement="left" trigger="click">
@@ -93,11 +97,12 @@
  import pageHeadLeft from '@/views/email/components/ListPage/pageHeadLeft.vue';
  import { PageWrapper } from '@/components/Page';
  import { ref, watch, defineProps, defineEmits, computed, reactive, onMounted } from 'vue';
  import { ref, watch, defineProps, defineEmits, computed, reactive, onMounted,inject } from 'vue';
  // 定义属性
  interface Props {
    pageList: [];
    pageData?:any;
  }
  const props = defineProps<Props>();
  const newList = ref([]);
@@ -109,7 +114,7 @@
    },
  );
  const checked = ref(false);
  const checked = computed(() => selectAllRow.value.length > 0);
  const pageCurrent = ref(1);
  const tableRef = ref();
  const state = reactive({
@@ -154,10 +159,10 @@
        num: 0,
      },
    ];
  });
  })
  const activeKey = ref('1');
  const checked3 = ref(false);
  import Table from '../table.vue';
  import Table from '@/views/email/components/ListPage/table.vue';
  onMounted(() => {
    console.log('tableRef:', tableRef.value[0]);
  });
@@ -165,12 +170,20 @@
    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%);
    /* 增加选择器特异性 */
@@ -182,6 +195,7 @@
        align-items: center;
        justify-content: space-flex-start;
        width: 100%;
        height: 100%;;
        & .icon {
          margin-right: 15px;
src/views/email/HandlingEmailsOnBehalfOfOthers/index.vue
@@ -1,6 +1,8 @@
<template>
  <div>
    <PageIndex :pageList="pageList"></PageIndex>
    <a-spin :spinning="loading" class="p-1" style="height: 100%">
      <PageIndex :pageList="pageList" :mailType="1" :pageData="pageData"> </PageIndex>
    </a-spin>
  </div>
</template>
@@ -27,14 +29,28 @@
      return '';
    }
  });
  import { getMailListApi } from '@/api/email/userList';
  import { getHandleMailListApi } from '@/api/email/userList';
  const pageList = ref([]);
  function getDataList() {
    getMailListApi({}).then((res) => {
      if (res.code == 0) {
        pageList.value = res.data;
      }
    });
  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(() => {
src/views/email/HandlingEmailsOnBehalfOfOthers/table.vue
File was deleted
src/views/email/Inbox/index.vue
@@ -1,7 +1,7 @@
<template>
  <div>
    <a-spin :spinning="loading" class="p-1">
      <PageIndex :pageList="pageList" :mailType="1"></PageIndex>
    <a-spin :spinning="loading" class="p-1" style="height: 100%;">
      <PageIndex :pageList="pageList" :mailType="1" :pageData="pageData" > </PageIndex>
    </a-spin>
  </div>
</template>
@@ -32,20 +32,27 @@
  import { getMailListApi } from '@/api/email/userList';
  const pageList = ref([]);
  const loading = ref(false);
  function getDataList() {
  const pageData = ref({
    page: 1,
    limit: 20,
    total: 0,
  });
  function getDataList(page:1) {
    loading.value = true;
    getMailListApi({ mail: routerId.value, mailType: 1 })
    getMailListApi({ mail: routerId.value, mailType: 1,page })
      .then((res) => {
        loading.value = false;
        if (res.code == 0) {
          pageList.value = res.data;
          pageList.value = res.data.list;
          pageData.value.total = res.data.total;
        }
      })
      .catch(() => {
        loading.value = false;
      });
  }
  provide('getDataList', getDataList);
  onMounted(() => {
    getDataList();
src/views/email/UnreadEmail/index.vue
@@ -1,6 +1,8 @@
<template>
  <div>
    <PageIndex :pageList="pageList" :mailType='1'></PageIndex>
    <a-spin :spinning="loading" class="p-1" style="height: 100%;">
    <PageIndex :pageList="pageList" :mailType='1' :pageData='pageData' ></PageIndex>
    </a-spin>
  </div>
</template>
@@ -28,12 +30,25 @@
    }
  });
  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 }).then((res) => {
    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;
        pageList.value = res.data.list;
        pageData.value.total = res.data.total;
      }
    }).catch(() => {
      loading.value = false;
    });
  }
  provide('getDataList',getDataList);
src/views/email/Utils/blacklist.vue
New file
@@ -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>
src/views/email/Utils/convention.vue
@@ -2,11 +2,11 @@
  <div class="p-2" style="margin-left: 20px">
    <h2 class="title" style="font-size: 20px">常规</h2>
    <a-form style="width: 60%" :labelCol="{ span: 6 }">
    <a-form style="width: 60%" :labelCol="{ span: 2 }">
      <div>
        <h2 class="title">账号</h2>
        <a-form-item label="默认邮箱">
          <a-select>
          <a-select class='w-200'>
            <a-select-option v-for="(item, index) in emailList" :key="index" :value="item.email">
              {{ item.email }}
            </a-select-option>
@@ -31,7 +31,7 @@
            <PlusCircleOutlined style="margin-left: 20px" @click="showModal('add')" />
          </div>
        </a-form-item>
        <a-form-item :labelCol="{ span: 6 }" label="内容">
        <a-form-item :labelCol="{ span: 2 }" label="内容">
          <div style="height: 200px; overflow: hidden; border-bottom: 1px solid #d9d9d9">
            <TinymcePw v-model="signContent"></TinymcePw>
          </div>
src/views/email/Utils/folder.vue
New file
@@ -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>
src/views/email/Utils/index.vue
@@ -10,9 +10,9 @@
      <a-tab-pane key="3" tab="快速文本"
        ><QuickText v-if="activeKey === '3'"></QuickText
      ></a-tab-pane>
      <a-tab-pane key="4" tab="文件夹">Content of Tab Pane 3</a-tab-pane>
      <a-tab-pane key="5" tab="标签">Content of Tab Pane 3</a-tab-pane>
      <a-tab-pane key="6" tab="黑名单">Content of Tab Pane 3</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>
@@ -24,5 +24,8 @@
  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>
src/views/email/Utils/label.vue
New file
@@ -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>
src/views/email/components/LeftNav.vue
@@ -1,6 +1,6 @@
<template>
  <PageWrapper dense contentFullHeight fixedHeight>
    <div style="height: 100vh; border-inline-end: 1px solid rgb(5 5 5 / 6%)">
    <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">
@@ -33,8 +33,7 @@
            <a-sub-menu v-if="item.children" :key="item.key">
              <template #title>
                <div class="my-display">
                  <span>{{ item.title }}</span
                  >
                  <span>{{ item.title }}</span>
                  <!-- <span class="my-left" v-if="item.total > 0">{{ item.total }}</span> -->
                </div>
              </template>
@@ -52,8 +51,7 @@
            </a-sub-menu>
            <a-menu-item v-else :key="item.key" @click="handleClick(item)">
              <div class="my-display">
                <span>{{ item.title }}</span
                >
                <span>{{ item.title }}</span>
                <span class="my-left" v-if="item.total > 0">{{ item.total }}</span>
              </div>
            </a-menu-item>
@@ -113,6 +111,8 @@
  const routesConfig = {
    InboxPage1: '/email/index',
    receiver: '/email/Inbox/list',
    sender: '/email/outbox',
    IndexPage1:'/email/outbox'
  };
  // 点击事件处理
  const router = useRouter();
@@ -137,6 +137,14 @@
            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}`);
src/views/email/components/ListPage/drawerDetail.vue
@@ -9,11 +9,69 @@
    @close="drawerClose"
  >
    <template #title>
      <pageHeadLeft
        :checked="true"
        :selectAllRow="[{ docCode }]"
        @nextNum="drawerClose"
      ></pageHeadLeft>
      <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">
@@ -35,123 +93,59 @@
        </a-button>
      </div>
    </template>
    <div
      style="
        position: fixed;
        z-index: 99;
        top: 6.8%;
        width: 72%;
        padding-top: 24px;
        background: #fff;
      "
    >
      <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 />
                </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 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>
    <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.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" />
              {{ 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 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 @click="fuToggleContent" class="toggle-btn" :class="isOpen ? 'onIconOpen' : 'iconOpen'">
        <LeftOutlined v-if="!isOpen" />
        <RightOutlined v-else />
      </div>
    </div>
  </a-drawer>
</template>
@@ -310,6 +304,11 @@
      }
    });
  }
  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">
@@ -363,7 +362,7 @@
  .flex-between {
    display: flex;
    margin-top: 4%;
    padding-top: 4%;
  }
  .ct-left {
@@ -404,4 +403,18 @@
  .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>
src/views/email/components/ListPage/list.vue
@@ -1,7 +1,7 @@
<template>
  <PageWrapper>
    <div style="height: calc(100vh - 84px)">
      <div class="head">
      <div class="my-head">
        <div class="left">
          <div class="left-box p-3">
            <!-- 多选 -->
@@ -23,12 +23,14 @@
        </div>
        <div class="right p-3"
          >共<span style="padding: 0 5px">20</span>封
          >共<span style="padding: 0 5px">{{page.total}}</span>封
          <a-pagination
            v-model:current="pageCurrent"
             v-model:page-size='page.limit'
            simple
            :total="50"
            :total="page.total"
            style="margin-left: 10px"
            @change="handlePageChange"
          />
          <FilterOutlined style="margin-left: 10px" />
          <a-popover placement="left" trigger="click">
@@ -95,11 +97,12 @@
  import pageHeadLeft from './pageHeadLeft.vue';
  import { PageWrapper } from '@/components/Page';
  import { ref, watch, defineProps, defineEmits, computed, reactive, onMounted } from 'vue';
  import { ref, watch, defineProps, defineEmits, computed, reactive, onMounted,inject } from 'vue';
  // 定义属性
  interface Props {
    pageList: [];
    pageList?: [];
    pageData?:any;
  }
  const props = defineProps<Props>();
  const newList = ref([]);
@@ -111,6 +114,7 @@
    },
  );
const page = computed(() => props.pageData);
  const checked = computed(() => selectAllRow.value.length > 0);
  const pageCurrent = ref(1);
  const tableRef = ref();
@@ -143,12 +147,12 @@
      {
        key: '1',
        label: '全部',
        num: 60,
        num: 0,
      },
      {
        key: '2',
        label: '客户',
        num: 20,
        num: 0,
      },
      {
        key: '3',
@@ -163,7 +167,7 @@
      {
        key: '5',
        label: '其他',
        num: 30,
        num: 0,
      },
    ];
  });
@@ -176,12 +180,24 @@
  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">
  .head {
  .my-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    height: 60px;
    border-bottom: 1px solid rgb(5 5 5 / 6%);
    /* 增加选择器特异性 */
@@ -193,6 +209,7 @@
        align-items: center;
        justify-content: space-flex-start;
        width: 100%;
        height: 100%;;
        & .icon {
          margin-right: 15px;
src/views/email/components/ListPage/table.vue
@@ -1,125 +1,137 @@
<template>
  <div style="height: 70vh; overflow: auto">
    <div v-for="(item, index) in groupedEmails" :key="index">
      <span class="span-title">{{ `${item.name}(${item.data.length})` }}</span>
      <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>
  <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 />
                <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>
                  <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-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>
                    <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>
              <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 v-else style="height: 70vh; display: flex; align-items: center; justify-content: center">
      <a-empty />
    </div>
    <DrawerDetail
      ref="drawerDetailRef"
@@ -155,8 +167,8 @@
  watch(
    () => props.pageList,
    (newValue) => {
      dataSource.value = newValue;
      groupedEmails.value = groupEmailsByDate(newValue);
      dataSource.value = newValue || [];
      groupedEmails.value = groupEmailsByDate(newValue || []);
    },
  );
  import dayjs from 'dayjs';
@@ -425,9 +437,11 @@
  }
  .span-title {
    padding: 20px;
    width: 100%;
    padding: 5px;
    color: #000;
    font-weight: 700;
    text-align: left;
  }
  .table {
src/views/email/components/SelectUser/index.vue
@@ -27,7 +27,7 @@
    @after-open-change="afterOpenChange"
    @close="afterOpenChange"
  >
    <a-tabs v-model:activeKey="activeKey">
    <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">
@@ -62,8 +62,8 @@
                }"
                :scroll="{ y: 300 }"
                size="small"
                rowKey="email"
                >
                rowKey="userCode"
              >
                <!-- :pagination="{ pageSize: 100 }" -->
                <a-table-column key="userName" title="name" data-index="name">
                  <template #default="{ record }">
@@ -114,7 +114,7 @@
<script lang="ts" setup>
  import { ref, watch, defineProps, defineEmits, computed, reactive } from 'vue';
  import { getUserInfoApi } from '@/api/email/userList';
  import { getUserInfoApi, contactListAPi, emailListAPi } from '@/api/email/userList';
  // 定义组件名称
  defineOptions({ name: 'SelectUser' });
@@ -157,8 +157,30 @@
  ]);
  // 定义事件
  const emit = defineEmits(['update:modelValue', 'updateData','sudUserList']);
  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);
@@ -184,7 +206,7 @@
  // 方法
  const afterOpenChange = (bool: boolean) => {
    if (bool) {
      fnGetUserList({});
      fnGetUserList();
    }
  };
  interface User {
@@ -194,16 +216,16 @@
    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);
      }
    });
  // 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>[] = [];
@@ -254,10 +276,10 @@
  const fnSelectAllCurrentPage = (e) => {
    const temp = dataSource.value;
    if (selectAllCurrentPage.value) {
      state.selectedRowKeys = temp.map((item) => item.id);
      state.selectedRowKeys = temp.map((item) => item.userCode);
    } else {
      state.selectedRowKeys = state.selectedRowKeys.filter(
        (item) => !temp.some((t) => t.id === item),
        (item) => !temp.some((t) => t.userCode === item),
      );
    }
    updateTempDataSource(state.selectedRowKeys);
@@ -266,16 +288,16 @@
  const fnSelectEmail = (e) => {
    const temp = dataSource.value.slice(0, 30);
    if (selectEmail.value) {
      state.selectedRowKeys = temp.map((item) => item.id);
      state.selectedRowKeys = temp.map((item) => item.userCode);
    } else {
      state.selectedRowKeys = state.selectedRowKeys.filter(
        (item) => !temp.some((t) => t.id === item),
        (item) => !temp.some((t) => t.userCode === item),
      );
    }
    updateTempDataSource(state.selectedRowKeys);
  };
  const updateTempDataSource = (selectedRowKeys: Key[]) => {
    tempDataSource.value = dataSource.value.filter((item) => selectedRowKeys.includes(item.id));
    tempDataSource.value = dataSource.value.filter((item) => selectedRowKeys.includes(item.userCode));
    contactNum.value = selectedRowKeys.length;
  };
@@ -300,11 +322,12 @@
    contactNum.value = 0;
  };
  const onSelectChange = (selectedRowKeys: Key[]) => {
    state.selectedRowKeys = selectedRowKeys;
    
    tempDataSource.value = dataSource.value.filter((item) => selectedRowKeys.includes(item.id));
    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);
src/views/email/index.vue
@@ -1,11 +1,12 @@

<template>
  <div>
      <div class="default-theme" style="display: flex; height: 100%; background-color: #fff">
        <div v-show="collapseStore.isOpen" class="default-theme-left">
      <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>
        </div>
        <div style="height: 100%" :class="collapseStore.isOpen ? 'onOpen' : 'isOpen'">
        </a-layout-sider>
        <a-layout>
        <a-layout-content :style="contentStyle">
          <RouterView>
            <template #default="{ Component, route }">
              <transition
@@ -28,8 +29,9 @@
              </transition>
            </template>
          </RouterView>
        </div>
      </div>
        </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 />
@@ -61,7 +63,15 @@
    Component: FunctionalComponent & { type: Recordable };
    route: RouteLocation;
  }
  const siderStyle = {
    lineHeight: '120px',
    backgroundColor:'#fff',
  };
  const contentStyle = {
  lineHeight: '120px',
  backgroundColor:'#fff',
};
  function getTransitionName({
    route,
    openCache,
@@ -125,10 +135,6 @@
    }
  }
  .default-theme-left {
    width: 20%;
    height: 100%;
  }
  .toggle-btn {
    display: flex;
@@ -144,15 +150,15 @@
  }
  .onOpen {
    width: 80%;
    width: 86vw;
  }
  .isOpen {
    width: 100%;
    width: 100vw;
  }
  .iconOpen {
    left: 20%;
    left: 250px;
  }
  .onIconOpen {
src/views/email/outbox/index.vue
New file
@@ -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>
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()],
});