huangyinfeng
6 天以前 a9a03d64cf190188d3db04d14970fc0908b03491
部分功能
1个文件已删除
2个文件已添加
18个文件已修改
718 ■■■■ 已修改文件
src/App.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/email/userList.ts 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/ColorPicker/index.vue 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Tinymce/src/index.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layouts/page/email.vue 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/routes/modules/email.ts 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/routes/modules/preview.ts 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/email/Drafts/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/email/Edit/index.vue 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/email/Utils/folder.vue 70 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/email/Utils/label.vue 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/email/components/LeftMenu/index.vue 162 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/email/components/ListPage/TooltipAndDropdown .vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/email/components/ListPage/drawerDetail.vue 222 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/email/components/ListPage/list.vue 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/email/components/ListPage/pageHeadLeft.vue 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/email/components/ListPage/table.vue 48 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/email/components/emailDetail.vue 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/email/components/userTips/index.vue 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/email/index.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/email/outbox/index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/App.vue
@@ -26,7 +26,7 @@
    // 先检查proxy是否存在,再进行操作
    if (proxy && proxy.$cookies) {
      proxy.$cookies.remove('JSESSIONID');
      proxy.$cookies.set('JSESSIONID', '23542F948D2C450599CF5850631B432D.jvm_59_9010', '1d');
      proxy.$cookies.set('JSESSIONID', '741E9E2AAD7578915B16287A5ECAE1DF.jvm_59_9010', '1d');
    } else {
      console.error('proxy对象未初始化或不包含$cookies属性');
    }
src/api/email/userList.ts
@@ -40,7 +40,7 @@
  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',
  ADD_TAG = '/crm/mail/tag/addTag.do',
  UPDATE_TAG = '/crm/mail/tag/updateTag.do',
  DELETE_TAG = '/crm/mail/tag/deleteTag.do',
  GET_TAG = '/crm/mail/tag/getTagList.do',
@@ -48,6 +48,7 @@
  UPDATE_BLACKLIST = '/crm/mail/blacklist/updateBlackList.do',
  DELETE_BLACKLIST = '/crm/mail/blacklist/deleteBlackList.do',
  GET_BLACKLIST = '/crm/mail/blacklist/getBlackList.do',
  GET_EMAIL_MODULE_BELOW = '/crm/mail/getEmailModuleBelow.do',
}
// 获取邮件路由列表
export const getEmailModuleApi = () => defHttp.get({ url: Api.GET_EMAIL_MODULE });
@@ -279,3 +280,10 @@
    url: Api.GET_BLACKLIST,
    params,
  });
  export const getEmailModuleBelowApi = () =>
    defHttp.get({
      url: Api.GET_EMAIL_MODULE_BELOW,
    });
src/components/ColorPicker/index.vue
@@ -28,6 +28,7 @@
              button-style="solid"
              size="small"
              name="color"
              @change="groupChange"
            >
              <a-radio-button
                v-for="item in colors"
@@ -55,7 +56,7 @@
  const props = defineProps({
    modelValue: {
      type: String,
      default:'#000000',
      default: '#000000',
      required: true,
    },
    // 1普通,2选择器模式
@@ -67,7 +68,7 @@
  });
  // 定义 emits,用于双向绑定
  const emit = defineEmits(['update:modelValue','change']);
  const emit = defineEmits(['update:modelValue', 'change']);
  // 颜色选项
  const colors = ref([
@@ -105,8 +106,13 @@
  // 当本地值发生变化时,发出 update:modelValue 事件,通知父组件更新
  watch(localValue, (newValue) => {
    emit('update:modelValue', newValue);
    emit('change', newValue);
    // emit('change', newValue);
  });
  function groupChange() {
    console.log(localValue.value, '------------2');
    emit('change', localValue.value);
  }
</script>
<style lang="less" scoped>
@@ -155,6 +161,5 @@
    height: 8px;
    background-color: #fff;
    clip-path: polygon(50% 60%, 0% 100%, 100% 100%); /* Triangle at the bottom */
  }
</style>
src/components/Tinymce/src/index.vue
@@ -104,7 +104,7 @@
  import 'tinymce/plugins/image';
  import 'tinymce/plugins/table';
  import 'tinymce/plugins/charmap';
  import 'tinymce/plugins/imagetools';
  // import 'tinymce/plugins/imagetools';
  import 'tinymce/plugins/help';
  import 'tinymce/plugins/emoticons';
  import 'tinymce/plugins/emoticons/js/emojis';
@@ -120,7 +120,7 @@
  // import 'tinymce/plugins/tinyddrive';
  // import 'tinymce/plugins/advcode';
  // import 'tinymce/plugins/mediaembed';
  import 'tinymce/plugins/toc';
  // import 'tinymce/plugins/toc';
  // import 'tinymce/plugins/checklist';
  // import 'tinymce/plugins/tinycespellchecker';
  // import 'tinymce/plugins/a11ychecker';
src/layouts/page/email.vue
@@ -1,11 +1,11 @@
<template>
  <div>
    <PageWrapper :class="`${prefixCls}`" dense contentFullHeight fixedHeight>
      <div class="default-theme" style="display: flex;height: 100%;background-color: #fff;">
        <div style="width: 10%;height: 100%;">
      <div class="default-theme" style="display: flex; height: 100%; background-color: #fff">
        <div style="width: 10%; height: 100%">
          <LeftNav></LeftNav>
        </div>
        <div style="width: 84%;height: 100%;">
        <div style="width: 84%; height: 100%">
          <RouterView>
            <template #default="{ Component, route }">
              <transition
@@ -36,10 +36,10 @@
</template>
<script lang="ts" setup>
import {PageWrapper} from '@/components/Page';
import { Splitpanes, Pane } from 'splitpanes';
  import { PageWrapper } from '@/components/Page';
  import { Splitpanes, Pane } from 'splitpanes';
  import LeftNav from '@/views/email/components/LeftNav.vue';
  import LeftNav from '@/views/email/components/LeftMenu/index.vue';
  import { computed, unref } from 'vue';
  import FrameLayout from '@/layouts/iframe/index.vue';
src/router/routes/modules/email.ts
@@ -99,16 +99,14 @@
      meta: {
        title: '发件箱',
        hideTab: true,
        currentActiveMenu: '/email/index',
      },
      children: [
        {
          path: 'list',
          name: 'Outbox',
          name: 'IndexPage1',
          component: () => import('@/views/email/outbox/index.vue'),
          meta: {
            title: '全部邮件',
            currentActiveMenu: '/email/index',
            title: '全部发件',
          },
        }]
    },
src/router/routes/modules/preview.ts
@@ -11,7 +11,9 @@
  meta: {
    orderNo: 10,
    icon: 'ion:grid-outline',
    title: '邮件预览‘',
    title: '邮件预览',
    hideTab: true,
    hideMenu: true,
  },
  children: [
    {
src/views/email/Drafts/index.vue
@@ -1,7 +1,7 @@
<template>
  <div>
    <a-spin :spinning="loading" class="p-1" style="height: 100%">
      <PageIndex :pageList="pageList" :mailType="1" :pageData="pageData"> </PageIndex>
      <PageIndex :pageList="pageList" :mailType="1" :pageData="pageData" :isDrafts='true'> </PageIndex>
    </a-spin>
  </div>
</template>
src/views/email/Edit/index.vue
@@ -300,6 +300,9 @@
    if (TYPE.value === 'reply') {
      fuGetReplyEmailData();
    }
    if (TYPE.value === 'edit') {
      fuGetEditEmailData();
    }
  });
  const useForm = Form.useForm;
  const { validate, validateInfos } = useForm(modelRef, rulesRef);
@@ -326,7 +329,7 @@
    sendingMailApi,
    saveMailDraftsApi,
    emailListAPi,
    getMailInfoApi
    getMailInfoApi,
  } from '@/api/email/userList';
  // 定义状态管理对象
  const state = reactive({
@@ -544,24 +547,35 @@
      state.fetching = false;
    });
  }, 300);
  // 编辑
  function fuGetEditEmailData() {
    getMailInfoApi({ docCode: router.currentRoute.value.query.docCode })
      .then((res) => {
        modelRef.sender = res.data.sender;
        modelRef.recipients = res.data.receiver;
        modelRef.subject = res.data.subject;
        modelRef.content = res.data.content;
      })
      .catch(() => {});
  }
  // 回复
  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)
        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
    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>
src/views/email/Utils/folder.vue
@@ -22,7 +22,7 @@
      :row-config="{ keyField: 'id' }"
      :column-config="{ resizable: true }"
      :export-config="{}"
      :tree-config="{ transform: true,rowField: 'rowId', parentField: 'parentRowId' }"
      :tree-config="{ transform: true, rowField: 'rowId', parentField: 'parentRowId' }"
      :edit-config="{ trigger: 'manual', mode: 'row' }"
      height="600"
    >
@@ -36,7 +36,7 @@
      <vxe-column field="folderName" title="文件夹名称" minWidth="250" tree-node :edit-render="{}">
        <template #edit="{ row }">
          <vxe-input
            :ref="el => inputRefs[row.id] = el"
            :ref="(el) => (inputRefs[row.id] = el)"
            v-model="row.folderName"
            type="text"
            style="width: 300px"
@@ -83,9 +83,10 @@
        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,
          folderId: currRow.folderId,
          folderName: currRow.folderName,
          treeControl: currRow.treeControl,
          parentRowId: currRow.parentRowId,
          sortId: newIndex,
        })
          .then(() => {
@@ -114,8 +115,30 @@
  function fnGetList() {
    getFolderApi({}).then((res) => {
      console.log(res);
      demo.tableData = res.data;
      demo.tableData = convertToTableData(res.data);
      console.log(demo.tableData, '3333333333333');
    });
  }
  function convertToTableData(data, parentId = null) {
    let tableData = [];
    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);
      }
      tableData.push(tableItem);
    });
    return tableData;
  }
  const inputRefs = ref<{ [key: number]: HTMLElement | null }>({});
@@ -136,13 +159,28 @@
  const { createMessage } = useMessage();
  function fnInputHandle(row) {
    console.log(row, '----333');
    const data = {
      folderName: row.folderName,
      parentRowId: row.rowId,
    };
    addFolderApi(data).then((res) => {
    if (row.folderName == '') {
      editRowEvent(row)
      return createMessage.error('请输入文件夹名称');
    }
    const data =
      row.opType == 'edit'
        ? {
            folderId: row.folderId,
            folderName: row.folderName,
            treeControl: row.treeControl,
            parentRowId: row.parentRowId,
            sortId: row.sortId,
          }
        : {
            folderName: row.folderName,
            parentRowId: row.parentRowId,
          };
    const api = row.opType == 'edit' ? updateFolderApi : addFolderApi;
    const title = row.opType == 'edit' ? '编辑' : '添加';
    api(data).then((res) => {
      if (res.code == 0) {
        createMessage.success('添加成功');
        createMessage.success(`${title}成功`);
        fnGetList();
      } else {
        createMessage.error(res.msg);
@@ -157,9 +195,9 @@
      id: rid,
      parentRowId: row.rowId, // 需要指定父节点,自动插入该节点中
    };
    console.log(record, '99999993');
    const { row: newRow } = await $table.insert(record);
    console.log(row,'99999993');
    await $table.setTreeExpand(row, true); // 将父节点展开
    await $table.setEditRow(newRow); // 插入子节点
  }
@@ -178,8 +216,8 @@
  function editRowEvent(row) {
    const $table = xTable.value;
    console.log(row,'---30494');
    console.log(row, '---30494');
    row.opType = 'edit';
    $table.setEditRow(row);
  }
  onMounted(() => {
src/views/email/Utils/label.vue
@@ -10,7 +10,7 @@
        </div>
      </template>
      <template #tools>
        <a-button type="primary" @click="showModal('add')">新建个人标签</a-button>
        <a-button type="primary" @click="showModal('add','')">新建个人标签</a-button>
      </template>
    </vxe-toolbar>
@@ -206,9 +206,8 @@
  }
  function fnRowColorChange(color, row) {
    console.log(color, row);
    const data = {
      tagColor: row.tagColor,
      tagColor: color,
      tagName: row.tagName,
      tagType: row.tagType,
      systemFlag: row.systemFlag,
src/views/email/components/LeftMenu/index.vue
New file
@@ -0,0 +1,162 @@
<template>
  <PageWrapper dense contentFullHeight fixedHeight>
    <div>
      <div class="header-container">
        <span class="button-group">
          <a-button shape="circle" size="large">
            <MailOutlined />
          </a-button>
          <a-button shape="circle" size="large">
            <UserOutlined />
          </a-button>
        </span>
        <a-button
          class="write-button"
          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 menuItems" :key="item.key">
            <render-menu-item :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"
          mode="inline"
          :popupClassName="popupClassName"
        >
          <template v-for="item in menuItems2" :key="item.key">
            <render-menu-item :item="item" @click="handleClick" />
          </template>
        </a-menu>
      </div>
    </div>
  </PageWrapper>
</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';
interface MenuItem {
  key: string;
  title: string;
  total?: number;
  children?: MenuItem[];
}
const selectedKeys = ref<string[]>(['Index']);
const openKeys = ref<string[]>(['Inbox']);
const items = ref<MenuItem[]>([]);
const items2 = ref<MenuItem[]>([]);
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 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',
};
</script>
<style lang="less" scoped>
.header-container {
  height: 15vh;
  padding: 20px 40px;
  text-align: center;
}
.button-group {
  display: flex;
  justify-content: space-around;
}
.write-button {
  width: 100%;
  margin-top: 10px;
  padding: 0 30px;
}
.menu-container {
  height: 70vh;
  overflow: auto;
}
.my-display {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.my-left {
  margin-left: 5px;
}
</style>
src/views/email/components/ListPage/TooltipAndDropdown .vue
@@ -168,7 +168,6 @@
    row: Object, // 当前行对象
    docCodeS: Array,
  });
console.log(props,'-59585855');
  const emit = defineEmits(['updateHandleTime', 'completeAction', 'customTimeSubmit', 'tagRow']);
src/views/email/components/ListPage/drawerDetail.vue
@@ -2,76 +2,20 @@
  <a-drawer
    v-model:open="drawerOpen"
    :placement="placement"
    width="1200"
    width="68%"
    :body-style="{ paddingBottom: '80px' }"
    :footer-style="{ textAlign: 'right' }"
    @after-open-change="afterOpenChange"
    @close="drawerClose"
  >
    <template #title>
      <div class="ctb">
      <div>
        <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>
      </div>
    </template>
    <template #extra>
      <div style="font-size: 16px">
@@ -93,14 +37,81 @@
        </a-button>
      </div>
    </template>
    <div class="ct-top" :class="isOpen ? 'isOpenTop' : 'onOpenTop'">
      <div class="title">
        <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>
              <div :style="{ maxWidth: isOpen ? '300px' : '600px' }" class="text-ellipsis">{{
                tableRowData.subject
              }}</div>
            </a-tooltip>
          </span>
          <span style="margin-right: 10px; font-size: 16px">
            <PushpinOutlined />
          </span>
        </div>
        <div class="right">
          <div class="tate">{{ formatToDateDay(tableRowData.receiveTime || tableRowData.updateTime) }}</div>
          <div>
            <a-dropdown-button>
              <div v-if="!isDrafts">
                <span>
                  <a-tooltip placement="bottom">
                    <template #title>
                      <span>回复</span>
                    </template>
                    <LeftOutlined @click="replyEmail(tableRowData, 'reply')" />
                  </a-tooltip>
                </span>
                <a-divider type="vertical" />
                <span>
                  <a-tooltip placement="bottom">
                    <template #title>
                      <span>快速回复</span>
                    </template>
                    <DoubleLeftOutlined />
                  </a-tooltip>
                </span>
              </div>
              <div v-else>
                <span @click="replyEmail(tableRowData, 'edit')">再次编辑</span>
              </div>
              <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>
      <div class="flex-between">
        <div class="ct-left p-2" :class="isOpen ? 'isOpen' : 'onOpen'">
          <div class="user p-1">
          <div class="user p-1 f-z-14" style="margin-top: 40px">
            <div style="display: flex; align-items: center">
              <a-avatar size="small" style="margin-right: 8px" src="#" />
              {{ tableRowData.sender }}
              <span>{{ `<${tableRowData.sender}>` }}</span>
              <div>
                <span>{{ tableRowData.senderName }} {{ `<${tableRowData.sender}>` }}</span>
              </div>
              <span style="margin: 0 10px">发送</span>
              <a-popover placement="bottom">
                <template #content>
@@ -126,21 +137,25 @@
                {{ `${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"
            >
            <div type="info" class="p-2 f-z-14" style="margin-top: 10px; background-color: #e4f1ff">
              <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 class="ct">
              <div v-if="tableRowData.content">
                <TinymcePw ref="TinymcePwRef" v-model="tableRowData.content" />
              </div>
            </div>
          </div>
        </div>
        <div v-show="isOpen" class="ct-right p-2">sssss</div>
        <div v-show="isOpen" class="ct-right">
          <div style="position: relative">
            <div style="position: absolute; top: 0; left: 10px">
              <UserTips></UserTips>
            </div>
          </div>
        </div>
      </div>
      <div @click="fuToggleContent" class="toggle-btn" :class="isOpen ? 'onIconOpen' : 'iconOpen'">
        <LeftOutlined v-if="!isOpen" />
@@ -164,6 +179,7 @@
    RollbackOutlined,
  } from '@ant-design/icons-vue';
  import pageHeadLeft from './pageHeadLeft.vue';
  import UserTips from '@/views/email/components/userTips/index.vue';
  import { TinymcePw } from '@/components/Tinymce';
  import { getMailInfoApi, setQuickReplyAPi } from '@/api/email/userList';
  import { useCollapseStore } from '@/store/modules/useCollapseStore';
@@ -182,6 +198,7 @@
    mailId?: string;
    selectAllRow: Array<any>;
    allList;
    isDrafts?: boolean;
  }
  const props = defineProps<Props>();
@@ -201,9 +218,12 @@
  watch(
    () => props.modelValue,
    (newValue) => {
      drawerOpen.value = newValue;
      nextTick(() => {
        console.log(newValue, '---------4', TinymcePwRef.value);
        drawerOpen.value = newValue;
        setTimeout(() => {
          console.log(newValue, '---------4', TinymcePwRef.value);
          tableRowData.value.content = tableRowData.value.content;
        }, 500);
      });
      if (newValue) {
        fnGetMailInfo(props.mailId);
@@ -214,8 +234,10 @@
  function fnGetMailInfo(id) {
    getMailInfoApi({ docCode: id })
      .then((res) => {
        docCode.value = id;
        tableRowData.value = res.data;
        nextTick(() => {
          docCode.value = id;
          tableRowData.value = res.data;
        });
      })
      .catch(() => {});
  }
@@ -236,21 +258,12 @@
    drawerOpen.value = false;
  };
  function truncateString(str, maxLength) {
    return str.length > maxLength ? str.substring(0, maxLength) + '...' : str;
  }
  const fnGetUserList = (params) => {
    // 调用接口逻辑
  };
  // 计算属性
  const placement = computed(() => props.placement || 'right');
  // 抽屉开关
  const fnSaveOpenChange = () => {
    // 抽屉保存逻辑
  };
  const content = ref('');
  const isOpen = ref(false);
@@ -306,8 +319,8 @@
  }
  import { useRouter } from 'vue-router';
  const router = useRouter();
  function replyEmail(row) {
    router.push({ path: '/email/edit', query: { docCode: row.docCode, type: 'reply' } });
  function replyEmail(row, type) {
    router.push({ path: '/email/edit', query: { docCode: row.docCode, type: type } });
  }
</script>
@@ -345,13 +358,12 @@
    & .right {
      display: flex;
      flex: 1;
      align-items: center;
      justify-content: space-between;
      width: 25%;
      justify-content: flex-end;
      & .tate {
        color: #999;
        font-size: 14px;
        margin-right: 10px;
      }
    }
  }
@@ -362,7 +374,6 @@
  .flex-between {
    display: flex;
    padding-top: 4%;
  }
  .ct-left {
@@ -370,7 +381,7 @@
  }
  .ct-right {
    border-left: 1px solid #f0f0f0;
    flex: 1;
  }
  .toggle-btn {
@@ -393,7 +404,15 @@
  }
  .isOpen {
    width: 69%;
    width: calc(100% - 420px);
  }
  .onOpenTop {
    width: 98%;
  }
  .isOpenTop {
    width: calc(98% - 460px);
  }
  .iconOpen {
@@ -401,20 +420,29 @@
  }
  .onIconOpen {
    right: 32%;
    right: 425px;
  }
  .ctb {
    position: relative;
  }
  .ctb .ct-top {
  .ct-top {
    position: absolute;
    z-index: 99;
    top: 41px;
    left: -28px;
    width: 116%;
    top: 57px;
    left: 10px;
    padding: 10px;
    background: #fff;
  }
  .text-ellipsis {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .f-z-14 {
    font-size: 12px;
  }
</style>
src/views/email/components/ListPage/list.vue
@@ -61,7 +61,7 @@
          </a-switch>
        </div>
      </div>
      <div v-if="checked" 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">
@@ -76,6 +76,7 @@
              ref="tableRef"
              :page="pageCurrent"
              :pageList="newList"
              :isDrafts="isDrafts"
              @selectAll="fnSelectAll"
              @updateSelectAll="updateSelectAll"
            />
@@ -102,7 +103,9 @@
  // 定义属性
  interface Props {
    pageList?: [];
    pageData?:any;
    pageData?: any;
    mailType?: number;
    isDrafts?: boolean;
  }
  const props = defineProps<Props>();
  const newList = ref([]);
src/views/email/components/ListPage/pageHeadLeft.vue
@@ -47,25 +47,25 @@
      </template>
      <a-dropdown :arrow="{ pointAtCenter: true }" placement="bottom" :trigger="['click']">
        <template #overlay>
          <a-menu>
          <a-menu v-if="checked">
            <a-menu-item key="2" @click="fnSelectAllRead(true)"> 标为已读</a-menu-item>
            <a-menu-item key="3" @click="fnSelectAllRead(false)"> 标为未读</a-menu-item>
            <a-menu-item key="4"> 设为置顶</a-menu-item>
            <a-divider style="margin: 2px; padding: 2px" />
            <a-menu-item key="5"> 标记为垃圾邮件</a-menu-item>
          </a-menu>
        </template>
        <MoreOutlined v-show="checked" class="icon" />
          <!-- </template>
        <MoreOutlined v-if="checked" class="icon" />
      </a-dropdown>
      <a-dropdown :arrow="{ pointAtCenter: true }" placement="bottom" :trigger="['click']">
        <template #overlay>
          <a-menu>
        <template #overlay> -->
          <a-menu v-else>
            <a-menu-item key="1" @click="fnAllRead">全部标记为已读</a-menu-item>
            <a-divider style="margin: 2px; padding: 2px" />
            <p style="color: #999; font-size: 12px">勾选邮件即可查看更多操作</p>
          </a-menu>
        </template>
        <MoreOutlined v-show="!checked" class="icon" />
        <MoreOutlined class="icon" />
      </a-dropdown>
    </a-tooltip>
  </div>
@@ -105,10 +105,17 @@
    pushUpdateReadApi(data);
  }
  const getSelectAllBocCode = computed(() => {
    let data = props.selectAllRow.map((item) => item.docCode);
    console.log(data, '00003');
    try {
      if (props.parentTableList?.length == 0) {
        return [];
      }
      let data = props.selectAllRow.map((item) => item.docCode);
      console.log(data, '00003');
    return data;
      return data;
    } catch (error) {
      return [];
    }
  });
  function pushUpdateReadApi(data) {
    updateReadApi(data).then((res) => {
@@ -130,11 +137,18 @@
    pushUpdateReadApi(data);
  }
  function getReadId() {
    const ids = [];
    props.parentTableList.forEach((item: Record<string, any>) => {
      ids.push(item.docCode);
    });
    return ids;
    try {
      const ids = [];
      if (props.parentTableList?.length == 0) {
        return [];
      }
      props.parentTableList.forEach((item: Record<string, any>) => {
        ids.push(item.docCode);
      });
      return ids;
    } catch (error) {
      return [];
    }
  }
  const emit = defineEmits(['nextNum']);
  function fuDeleteEmail() {
src/views/email/components/ListPage/table.vue
@@ -23,7 +23,7 @@
                <div
                  v-if="row.mailType != 0"
                  class="dot"
                  :class="row.readFlag ? 'dot-color' : ''"
                  :class="fnIsItHighlighted(row) ? 'dot-color' : ''"
                  @click.stop="fnRowUpdateRead(row)"
                ></div>
                <a-tooltip placement="bottom">
@@ -45,7 +45,7 @@
                    >
                      <a-avatar size="small" style="margin-right: 8px" :src="row.avatar" />
                      <span style="color: #000; font-weight: 700">{{ row.sender }}</span>
                      <CopyOutlined />
                      <CopyOutlined @click="copyText(row.sender)" />
                    </div>
                    <div class="display-flex p-2">
                      <a-button type="link" size="small">新建客户</a-button>
@@ -78,7 +78,7 @@
                      <a-button type="link" size="small">往来邮件</a-button></div
                    >
                  </template>
                  <div class="title-dot" :class="row.readFlag ? 'title-dot-color' : ''">
                  <div class="title-dot" :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>
@@ -97,7 +97,7 @@
            <template #default="{ row }">
              <span
                class="title-dot"
                :class="row.readFlag ? 'title-dot-color' : ''"
                :class="fnIsItHighlighted(row) ? 'title-dot-color' : ''"
                style="font-weight: 500"
                >{{ row.subject || '(无主题)' }}</span
              >
@@ -126,11 +126,10 @@
              </span>
            </template>
          </vxe-column>
        </vxe-table>
        </div
        </vxe-table> </div
    ></div>
    <div v-else style="height: 70vh; display: flex; align-items: center; justify-content: center">
    <div v-else style="display: flex; align-items: center; justify-content: center; height: 70vh">
      <a-empty />
    </div>
    <DrawerDetail
@@ -139,6 +138,7 @@
      :mailId="rowMailId"
      :selectAllRow="selectRow"
      :allList="dataSource"
      :isDrafts="isDrafts"
    />
    <a-dropdown :trigger="['click']" placement="bottomLeft" ref="dropdownRefs"> </a-dropdown>
  </div>
@@ -158,6 +158,7 @@
  // 定义属性
  interface Props {
    pageList: [];
    isDrafts?: boolean;
  }
  const props = defineProps<Props>();
@@ -316,11 +317,21 @@
    }
  }
  const vxeTableRef = ref();
  function fnIsItHighlighted(row) {
    return row.readFlag && props.isDrafts;
  }
  function fnSelectAll(is) {
    vxeTableRef.value.forEach((row) => {
      row.setAllCheckboxRow(is);
    });
    selectChangeEvent();
    try {
      if (!vxeTableRef.value) {
        return;
      }
      vxeTableRef.value.forEach((row) => {
        row.setAllCheckboxRow(is);
      });
      selectChangeEvent();
    } catch (error) {
      console.log(error);
    }
  }
  function selectChangeEvent() {
@@ -360,12 +371,12 @@
    selectRow.value = [];
    rowMailId.value = event.row.docCode;
    selectRow.value.push({ docCode: event.row.docCode });
    fnRowUpdateRead(event.row);
    openDrawerDetail.value = true;
  };
  // 更新祖父组件数据
  const getDataList = inject('getDataList');
  console.log(getDataList, '0000004');
  import { updateReadApi, updateHandleAPi } from '@/api/email/userList';
  // 标志未读/经读
@@ -384,6 +395,19 @@
      }
    });
  }
  import { useMessage } from '@/hooks/web/useMessage';
  const { createMessage } = useMessage();
  const copyText = async (value) => {
    try {
      await navigator.clipboard.writeText(value);
      setTimeout(() => {
        createMessage.success('复制成功');
      }, 500);
    } catch (err) {
      console.error('复制失败: ', err);
    }
  };
  const emit = defineEmits(['selectAll', 'updateSelectAll']);
  defineExpose({
    fnSelectAll,
src/views/email/components/emailDetail.vue
File was deleted
src/views/email/components/userTips/index.vue
New file
@@ -0,0 +1,61 @@
<template>
  <a-layout class="p-2" style="width: 420px; background-color: #fff">
    <div :style="headerStyle">
      <div class="my-d-f">
        <a-avatar style="margin-right: 8px" src="#" />
        <div>
          <div style="color: #000; font-weight: 700">XXX</div>
          <div style="color: #000"> 124404189@qq.com </div>
        </div>
      </div>
    </div>
    <a-layout-content>
      <a-tabs v-model:activeKey="activeKey" class="m-t-4">
        <a-tab-pane key="1" tab="客户画像">
          <div class="tabs-bk p-2" v-if="activeKey === '1'">sssssssssssssss</div>
        </a-tab-pane>
        <a-tab-pane key="2" tab="建档建议" force-render>
          <div class="tabs-bk p-2" v-if="activeKey === '2'"></div>
        </a-tab-pane>
        <a-tab-pane key="3" tab="谈单指南"
          ><div class="tabs-bk p-2" v-if="activeKey === '3'"></div
        ></a-tab-pane>
        <a-tab-pane key="4" tab="文档记录"
          ><div class="tabs-bk p-2" v-if="activeKey === '4'"
        /></a-tab-pane>
        <a-tab-pane key="5" tab="物流查价"
          ><div class="tabs-bk p-2" v-if="activeKey === '5'"
        /></a-tab-pane>
        <a-tab-pane key="6" tab="往来邮件"
          ><div class="tabs-bk p-2" v-if="activeKey === '6'"
        /></a-tab-pane>
        <a-tab-pane key="6" tab="Tips"
          ><div class="tabs-bk p-2" v-if="activeKey === '6'"
        /></a-tab-pane>
      </a-tabs>
    </a-layout-content>
  </a-layout>
</template>
<script lang="ts" setup>
  name: 'Utils';
  import { ref } from 'vue';
  const headerStyle = {
    color: '#fff',
    height: 104,
    paddingInline: 50,
    backgroundColor: '#fff',
  };
  const activeKey = ref('1');
</script>
<style scoped lang="less">
  .tabs-bk {
    height: 60vh;
    background-color: #fafafa;
  }
  .vxe-tabs-header--bar {
    border: 0 solid transparent;
  }
</style>
src/views/email/index.vue
@@ -42,8 +42,8 @@
<script lang="ts" setup>
  import { PageWrapper } from '@/components/Page';
  import { LeftOutlined, RightOutlined } from '@ant-design/icons-vue';
  import LeftNav from '@/views/email/components/LeftNav.vue';
import { LeftOutlined, RightOutlined } from '@ant-design/icons-vue';
  import LeftNav from '@/views/email/components/LeftMenu/index.vue';
  import { computed, unref, ref } from 'vue';
  import FrameLayout from '@/layouts/iframe/index.vue';
src/views/email/outbox/index.vue
@@ -7,7 +7,7 @@
</template>
<script lang="ts" setup>
  name: 'outbox';
  name: 'IndexPage1';
  import { ref, onMounted, computed, provide } from 'vue';
  import PageIndex from '@/views/email/components/ListPage/list.vue';
  import { useRoute } from 'vue-router';