ソースを参照

邮箱新需求

cz 1 年間 前
コミット
89217bb9a2

+ 632 - 0
connect/E-mail/businessConfig/index.vue

@@ -0,0 +1,632 @@
+<template>
+  <div class="tenant">
+    <!-- <Banner /> -->
+    <div class="content">
+      <byTable
+        :source="sourceList.data"
+        :pagination="sourceList.pagination"
+        :config="config"
+        :loading="loading"
+        highlight-current-row
+        :selectConfig="selectConfig"
+        :table-events="{
+          //element talbe事件都能传
+          select: select,
+        }"
+        :action-list="[
+          {
+            text: '邮箱域名管理',
+            action: () => openModalOne(),
+          },
+          {
+            text: '添加邮箱',
+            action: () => openModal('10'),
+          },
+        ]"
+        @get-list="getList"
+      >
+        <template #mialAddress="{ item }">
+          {{ item.mailUserPrefix }}@{{ item.domainName }}
+        </template>
+      </byTable>
+    </div>
+    <el-dialog
+      :title="titleText"
+      v-model="dialogVisible"
+      width="450"
+      v-loading="loading"
+    >
+      <byForm
+        :formConfig="formConfig"
+        :formOption="formOption"
+        v-model="formData.data"
+        :rules="rules"
+        ref="byform"
+      >
+        <template #mailAddress>
+          <el-row style="width: 100%">
+            <el-col :span="11">
+              <el-input
+                v-model="formData.data.mailUserPrefix"
+                placeholder="请输入"
+              >
+              </el-input>
+            </el-col>
+            <el-col :span="2" style="text-align: center"> @ </el-col>
+            <el-col :span="11">
+              <el-select
+                v-model="formData.data.domainId"
+                placeholder="请选择"
+                style="width: 100%"
+              >
+                <el-option
+                  v-for="item in sourceListOne.data"
+                  :label="item.domainName"
+                  :value="item.id"
+                >
+                </el-option>
+              </el-select>
+            </el-col>
+          </el-row>
+        </template>
+
+        <template #rSlot>
+          <el-row style="width: 100%" :gutter="15">
+            <el-col :span="10">
+              <el-select
+                v-model="formData.data.receiveProtocol"
+                placeholder="收件协议"
+                disabled
+              >
+              </el-select>
+            </el-col>
+            <el-col :span="10">
+              <el-input
+                v-model="formData.data.receivePort"
+                placeholder="请输入"
+              >
+              </el-input>
+            </el-col>
+          </el-row>
+        </template>
+        <template #sSlot>
+          <el-row style="width: 100%" :gutter="15">
+            <el-col :span="10">
+              <el-select
+                v-model="formData.data.sendProtocol"
+                placeholder="发件协议"
+                disabled
+              >
+              </el-select>
+            </el-col>
+            <el-col :span="10">
+              <el-input v-model="formData.data.sendPort" placeholder="请输入">
+              </el-input>
+            </el-col>
+          </el-row>
+        </template>
+      </byForm>
+      <template #footer>
+        <el-button @click="dialogVisible = false" size="large">取 消</el-button>
+        <el-button
+          type="primary"
+          @click="submitForm('byform')"
+          size="large"
+          :loading="submitLoading"
+        >
+          确 定
+        </el-button>
+      </template>
+    </el-dialog>
+
+    <el-dialog
+      title="邮箱域名管理"
+      v-model="dialogVisibleOne"
+      width="800"
+      v-loading="loading"
+    >
+      <div style="width: 100%">
+        <div style="padding: 0px 20px">
+          <el-button type="primary" @click="openModal('20')">
+            添加域名
+          </el-button>
+        </div>
+        <byTable
+          :source="sourceListOne.data"
+          :config="configOne"
+          :loading="loading"
+          highlight-current-row
+          :hidePagination="true"
+          :hideSearch="true"
+        >
+        </byTable>
+      </div>
+      <template #footer>
+        <el-button @click="dialogVisibleOne = false" size="large"
+          >取 消</el-button
+        >
+        <!-- <el-button
+          type="primary"
+          @click="submitForm('byform')"
+          size="large"
+          :loading="submitLoading"
+        >
+          确 定
+        </el-button> -->
+      </template>
+    </el-dialog>
+  </div>
+</template>
+  
+<script setup>
+/* eslint-disable vue/no-unused-components */
+import { ElMessage, ElMessageBox } from "element-plus";
+import byTable from "@/components/byTable/index";
+import byForm from "@/components/byForm/index";
+import { computed, defineComponent, ref } from "vue";
+import useUserStore from "@/store/modules/user";
+
+const loading = ref(false);
+const submitLoading = ref(false);
+const sourceList = ref({
+  data: [],
+  pagination: {
+    total: 3,
+    pageNum: 1,
+    pageSize: 10,
+  },
+});
+const sourceListOne = ref({
+  data: [],
+});
+let dialogVisible = ref(false);
+let dialogVisibleOne = ref(false);
+let modalType = ref("add");
+let submitType = ref("");
+let titleText = ref("");
+let rules = ref({
+  mailUserPrefix: [{ required: true, message: "请输入地址", trigger: "blur" }],
+  domainName: [{ required: true, message: "请输入邮箱域名", trigger: "blur" }],
+  type: [{ required: true, message: "请选择邮箱类型", trigger: "change" }],
+  mailPassword: [{ required: true, message: "请输入授权码", trigger: "blur" }],
+  receiveHost: [
+    { required: true, message: "请输入收件服务器地址", trigger: "blur" },
+  ],
+  receivePort: [{ required: true, message: "请输入收件端口", trigger: "blur" }],
+  receiveProtocol: [
+    { required: true, message: "请输入收件协议", trigger: "blur" },
+  ],
+  sendHost: [
+    { required: true, message: "请输入发件服务器地址", trigger: "blur" },
+  ],
+  sendPort: [{ required: true, message: "请输入发件端口", trigger: "blur" }],
+  sendProtocol: [
+    { required: true, message: "请输入发件协议", trigger: "blur" },
+  ],
+});
+const { proxy } = getCurrentInstance();
+const selectConfig = [];
+const config = computed(() => {
+  return [
+    {
+      attrs: {
+        type: "slot",
+        slot: "mialAddress",
+        label: "邮箱地址",
+      },
+    },
+    {
+      attrs: {
+        label: "授权码",
+        prop: "mailPassword",
+      },
+    },
+    {
+      attrs: {
+        label: "绑定用户",
+        prop: "userName",
+      },
+    },
+    {
+      attrs: {
+        label: "收件协议",
+        prop: "receiveProtocol",
+      },
+    },
+    {
+      attrs: {
+        label: "收件端口号",
+        prop: "receivePort",
+      },
+    },
+    {
+      attrs: {
+        label: "发件协议",
+        prop: "sendProtocol",
+      },
+    },
+    {
+      attrs: {
+        label: "发件端口号",
+        prop: "sendPort",
+      },
+    },
+
+    {
+      attrs: {
+        label: "操作",
+        width: "130",
+        align: "right",
+      },
+      // 渲染 el-button,一般用在最后一列。
+      renderHTML(row) {
+        return [
+          {
+            attrs: {
+              label: "修改",
+              type: "primary",
+              text: true,
+            },
+            el: "button",
+            click() {
+              getDtl(row, "10");
+            },
+          },
+          {
+            attrs: {
+              label: "删除",
+              type: "danger",
+              text: true,
+            },
+            el: "button",
+            click() {
+              // 弹窗提示是否删除
+              ElMessageBox.confirm(
+                "此操作将永久删除该数据, 是否继续?",
+                "提示",
+                {
+                  confirmButtonText: "确定",
+                  cancelButtonText: "取消",
+                  type: "warning",
+                }
+              ).then(() => {
+                // 删除
+                proxy
+                  .post("/enterpriseMailbox/delete", {
+                    id: row.id,
+                  })
+                  .then((res) => {
+                    ElMessage({
+                      message: "删除成功",
+                      type: "success",
+                    });
+                    getList();
+                  });
+              });
+            },
+          },
+        ];
+      },
+    },
+  ];
+});
+const configOne = computed(() => {
+  return [
+    {
+      attrs: {
+        label: "邮箱域名",
+        prop: "domainName",
+      },
+    },
+    {
+      attrs: {
+        label: "海外邮箱",
+        prop: "type",
+      },
+      render(type) {
+        return type == 2 ? "是" : "否";
+      },
+    },
+    {
+      attrs: {
+        label: "收件协议",
+        prop: "receiveProtocol",
+      },
+    },
+    {
+      attrs: {
+        label: "收件端口号",
+        prop: "receivePort",
+      },
+    },
+    {
+      attrs: {
+        label: "发件协议",
+        prop: "sendProtocol",
+      },
+    },
+    {
+      attrs: {
+        label: "发件端口号",
+        prop: "sendPort",
+      },
+    },
+
+    {
+      attrs: {
+        label: "操作",
+        width: "130",
+        align: "right",
+      },
+      // 渲染 el-button,一般用在最后一列。
+      renderHTML(row) {
+        return [
+          {
+            attrs: {
+              label: "修改",
+              type: "primary",
+              text: true,
+            },
+            el: "button",
+            click() {
+              getDtl(row, "20");
+            },
+          },
+          {
+            attrs: {
+              label: "删除",
+              type: "danger",
+              text: true,
+            },
+            el: "button",
+            click() {
+              // 弹窗提示是否删除
+              ElMessageBox.confirm(
+                "此操作将永久删除该数据, 是否继续?",
+                "提示",
+                {
+                  confirmButtonText: "确定",
+                  cancelButtonText: "取消",
+                  type: "warning",
+                }
+              ).then(() => {
+                // 删除
+                proxy
+                  .post("/enterpriseDomain/delete", {
+                    id: row.id,
+                  })
+                  .then((res) => {
+                    ElMessage({
+                      message: "删除成功",
+                      type: "success",
+                    });
+                    getListOne();
+                  });
+              });
+            },
+          },
+        ];
+      },
+    },
+  ];
+});
+let formData = reactive({
+  data: {},
+});
+const formOption = reactive({
+  inline: true,
+  labelWidth: 100,
+  itemWidth: 100,
+});
+const byform = ref(null);
+const treeData = ref([]);
+const formConfig = ref({});
+const configData = [
+  [
+    {
+      type: "slot",
+      slotName: "mailAddress",
+      prop: "mailUserPrefix",
+      label: "邮箱地址",
+      required: true,
+    },
+    {
+      type: "input",
+      prop: "mailPassword",
+      label: "授权码",
+      required: true,
+    },
+    {
+      type: "select",
+      prop: "userId",
+      label: "绑定用户",
+      isLoad: {
+        url: `/tenantUser/list?pageNum=1&pageSize=9999&tenantId=${
+          useUserStore().user.tenantId
+        }`,
+        labelKey: "nickName",
+        labelVal: "userId",
+        method: "get",
+        resUrl: "rows",
+      },
+    },
+  ],
+  [
+    {
+      type: "input",
+      prop: "domainName",
+      label: "邮箱域名",
+      required: true,
+    },
+    {
+      type: "radio",
+      prop: "type",
+      label: "是否海外邮箱",
+      required: true,
+      border: true,
+      data: [
+        { label: "是", value: "2" },
+        { label: "否", value: "1" },
+      ],
+    },
+
+    {
+      type: "input",
+      prop: "receiveHost",
+      label: "收件服务器地址",
+      required: true,
+    },
+    {
+      type: "select",
+      label: "收件协议",
+      prop: "receiveProtocol",
+      itemWidth: 33,
+      disabled: true,
+      style: {
+        width: "100%",
+        "margin-right": "10px",
+      },
+    },
+    {
+      type: "input",
+      prop: "receivePort",
+      label: " ",
+      itemWidth: 33.33,
+    },
+    {
+      type: "input",
+      prop: "sendHost",
+      label: "发件服务器地址",
+      required: true,
+    },
+    {
+      type: "select",
+      label: "发件协议",
+      prop: "sendProtocol",
+      itemWidth: 33,
+      disabled: true,
+      style: {
+        width: "100%",
+        "margin-right": "10px",
+      },
+    },
+    {
+      type: "input",
+      prop: "sendPort",
+      label: " ",
+      itemWidth: 33.33,
+    },
+  ],
+];
+
+const getList = async (req) => {
+  sourceList.value.pagination = { ...sourceList.value.pagination, ...req };
+  loading.value = true;
+  proxy
+    .post("/enterpriseMailbox/page", sourceList.value.pagination)
+    .then((message) => {
+      console.log(message);
+      sourceList.value.data = message.rows;
+      sourceList.value.pagination.total = message.total;
+      setTimeout(() => {
+        loading.value = false;
+      }, 200);
+    });
+};
+
+const getListOne = async () => {
+  loading.value = true;
+  proxy
+    .post("/enterpriseDomain/page", { pageNum: 1, pageSize: 9999 })
+    .then((message) => {
+      sourceListOne.value.data = message.rows;
+      setTimeout(() => {
+        loading.value = false;
+      }, 200);
+    });
+};
+
+const openModal = (type) => {
+  formData.data = {};
+  dialogVisible.value = true;
+  modalType.value = "add";
+  submitType.value = type;
+  if (type === "10") {
+    titleText.value = "添加邮箱";
+    formConfig.value = configData[0];
+  } else if (type === "20") {
+    titleText.value = "添加域名";
+    formConfig.value = configData[1];
+    formData.data = {
+      type: "1",
+      receiveProtocol: "IMAP",
+      sendProtocol: "SMTP",
+    };
+  }
+};
+
+const openModalOne = () => {
+  dialogVisibleOne.value = true;
+  modalType.value = "add";
+};
+
+const submitForm = () => {
+  byform.value.handleSubmit((valid) => {
+    submitLoading.value = true;
+    if (submitType.value === "10") {
+      proxy.post("/enterpriseMailbox/" + modalType.value, formData.data).then(
+        (res) => {
+          ElMessage({
+            message: modalType.value == "add" ? "添加成功" : "编辑成功",
+            type: "success",
+          });
+          dialogVisible.value = false;
+          submitLoading.value = false;
+          getList();
+        },
+        (err) => (submitLoading.value = false)
+      );
+    } else if (submitType.value === "20") {
+      proxy.post("/enterpriseDomain/" + modalType.value, formData.data).then(
+        (res) => {
+          ElMessage({
+            message: modalType.value == "add" ? "添加成功" : "编辑成功",
+            type: "success",
+          });
+          dialogVisible.value = false;
+          submitLoading.value = false;
+          getListOne();
+        },
+        (err) => (submitLoading.value = false)
+      );
+    }
+  });
+};
+
+const getDtl = (row, type) => {
+  modalType.value = "edit";
+  submitType.value = type;
+  if (type === "10") {
+    proxy.post("/enterpriseMailbox/detail", { id: row.id }).then((res) => {
+      formConfig.value = configData[0];
+      formData.data = res;
+    });
+  } else if (type === "20") {
+    proxy.post("/enterpriseDomain/detail", { id: row.id }).then((res) => {
+      formConfig.value = configData[1];
+      res.type = res.type + "";
+      formData.data = res;
+    });
+  }
+  dialogVisible.value = true;
+};
+
+getList();
+getListOne();
+</script>
+  
+<style lang="scss" scoped>
+.tenant {
+  padding: 20px;
+}
+</style>

+ 857 - 0
connect/E-mail/mail/com/left.vue

@@ -0,0 +1,857 @@
+<template>
+  <div class="left">
+    <div class="top">
+      <el-dropdown>
+        <span class="mail">
+          {{ selectMail.mailUser }}
+          <el-icon class="el-icon--right">
+            <arrow-down />
+          </el-icon>
+        </span>
+        <template #dropdown>
+          <el-dropdown-menu>
+            <el-dropdown-item
+              v-for="item in mailList"
+              :key="item.id"
+              @click="handleClickMail(item)"
+              >{{ item.mailUser }}</el-dropdown-item
+            >
+          </el-dropdown-menu>
+        </template>
+      </el-dropdown>
+      <el-tabs v-model="activeName" class="demo-tabs" stretch>
+        <el-tab-pane label="邮箱" name="first">
+          <template #label>
+            <div>
+              <i
+                class="iconfont icon-iconm_dianzyx"
+                style="margin-right: 5px"
+              ></i>
+              <span>邮箱</span>
+            </div>
+          </template>
+        </el-tab-pane>
+        <el-tab-pane label="联系人" name="second">
+          <template #label>
+            <div>
+              <i
+                class="iconfont icon-icomm_contact"
+                style="margin-right: 5px"
+              ></i>
+              <span>联系人</span>
+            </div>
+          </template>
+        </el-tab-pane>
+        <el-tab-pane label="客户" name="third">
+          <template #label>
+            <div>
+              <i class="iconfont icon-icon_kh" style="margin-right: 5px"></i>
+              <span>客户</span>
+            </div>
+          </template>
+        </el-tab-pane>
+      </el-tabs>
+      <div>
+        <el-button
+          type="primary"
+          style="width: 100%; font-size: 12px"
+          @click="handleGoWrite()"
+          >写信</el-button
+        >
+      </div>
+    </div>
+    <div class="body">
+      <div v-if="activeName === 'first'">
+        <ul class="mail-menu">
+          <li
+            class="menu-item"
+            v-bind:class="{ 'select-menu': item.id === selectFloderId }"
+            v-for="item in selectMail.mailFolderInfoListCopy"
+            :key="item.id"
+            @click="handleOpenMenu(item, '10')"
+          >
+            <i
+              class="iconfont icon-iconm_inbox leftIcon"
+              v-if="item.sort === 1"
+            ></i>
+            <i
+              class="iconfont icon-iconm_unread leftIcon"
+              v-else-if="item.sort === 2"
+            ></i>
+            <i
+              class="iconfont icon-icomm_draftbox leftIcon"
+              v-else-if="item.sort === 3"
+            ></i>
+            <i
+              class="iconfont icon-iconm_sent leftIcon"
+              v-else-if="item.sort === 4"
+            ></i>
+            <i
+              class="iconfont icon-icomm_delete leftIcon"
+              v-else-if="item.sort === 5"
+            ></i>
+            <i
+              class="iconfont icon-iconm_ljyx leftIcon"
+              v-else-if="item.sort === 6"
+            ></i>
+            <span style="margin-left: 5px">{{ item.name }}</span>
+          </li>
+        </ul>
+        <!-- 员工邮箱 -->
+        <div class="tree" v-if="staffMailData && staffMailData.length > 0">
+          <el-tree
+            :data="staffMailData"
+            node-key="id"
+            default-expand-all
+            :expand-on-click-node="false"
+            @node-click="(data, node) => clickTreeMail(data, node)"
+          >
+            <template #default="{ node, data }">
+              <span class="tree-content">
+                <i
+                  class="iconfont icon-icomm_ygyx iconColor"
+                  v-if="data.id == '0'"
+                  style="margin-right: 5px"
+                ></i>
+                <span>{{ data.mailUser }}</span>
+              </span>
+            </template>
+          </el-tree>
+        </div>
+        <!-- 官方文件夹 -->
+        <div
+          class="tree"
+          v-if="selectMail.otherFolder && selectMail.otherFolder.length > 0"
+        >
+          <el-tree
+            :data="selectMail.otherFolder"
+            node-key="id"
+            default-expand-all
+            :expand-on-click-node="false"
+            @node-click="(data) => handleTreeNodeClick(data, 'official')"
+          >
+            <template #default="{ node, data }">
+              <span class="tree-content">
+                <i
+                  class="iconfont icon-iconm_gfwjj iconColor"
+                  v-if="data.id == '0'"
+                  style="margin-right: 5px"
+                ></i>
+                <span>{{ data.name }}</span>
+              </span>
+            </template></el-tree
+          >
+        </div>
+        <!-- 我的文件夹 -->
+        <div
+          class="tree"
+          v-if="myFolderTreeData && myFolderTreeData.length > 0"
+        >
+          <el-tree
+            :data="myFolderTreeData"
+            node-key="id"
+            default-expand-all
+            :expand-on-click-node="false"
+            @node-click="(data) => handleTreeNodeClick(data, 'folder')"
+          >
+            <template #default="{ node, data }">
+              <span class="tree-content">
+                <i
+                  class="iconfont icon-icomm_wdwjj iconColor"
+                  v-if="data.id == '0'"
+                  style="margin-right: 5px"
+                ></i>
+                <span>{{ data.label }}</span>
+                <el-popover
+                  placement="bottom-start"
+                  title=""
+                  :width="200"
+                  trigger="click"
+                >
+                  <div default style="display: flex">
+                    <el-button
+                      size="small"
+                      @click.stop="handleEditFolder(data, 'add')"
+                      >添加</el-button
+                    >
+                    <el-button
+                      size="small"
+                      v-if="data.id != '0'"
+                      @click.stop="handleEditFolder(data, 'edit')"
+                      >编辑</el-button
+                    >
+                    <el-button
+                      size="small"
+                      v-if="data.id != '0'"
+                      @click.stop="handleDelFolder(data)"
+                      >删除</el-button
+                    >
+                  </div>
+                  <template #reference>
+                    <span
+                      class="iconfont icon_more iconColor"
+                      style="padding-bottom: 5px; margin-left: auto"
+                      >...</span
+                    >
+                  </template>
+                </el-popover>
+              </span>
+            </template>
+          </el-tree>
+        </div>
+        <!-- 我的标签 -->
+        <div class="tree" v-if="tagsTreeData && tagsTreeData.length > 0">
+          <el-tree
+            :data="tagsTreeData"
+            node-key="id"
+            default-expand-all
+            :expand-on-click-node="false"
+            @node-click="(data) => handleTreeNodeClick(data, 'tag')"
+          >
+            <template #default="{ node, data }">
+              <span class="tree-content">
+                <i
+                  class="iconfont icon-icomm_label iconColor"
+                  v-if="data.id == '0'"
+                  style="margin-right: 5px"
+                ></i>
+                <span>{{ data.name }}</span>
+                <el-popover
+                  placement="bottom-start"
+                  title=""
+                  :width="150"
+                  trigger="click"
+                >
+                  <div default style="display: flex">
+                    <el-button
+                      size="small"
+                      v-if="data.id == '0'"
+                      @click.stop="handleEditTag(data, 'add')"
+                      >添加</el-button
+                    >
+                    <el-button
+                      size="small"
+                      v-if="data.id != '0'"
+                      @click.stop="handleEditTag(data, 'edit')"
+                      >编辑</el-button
+                    >
+                    <el-button
+                      size="small"
+                      v-if="data.id != '0'"
+                      @click.stop="handleDelTag(data)"
+                      >删除</el-button
+                    >
+                  </div>
+                  <template #reference>
+                    <span
+                      class="iconfont icon_more iconColor"
+                      style="padding-bottom: 5px; margin-left: auto"
+                      >...</span
+                    >
+                  </template>
+                </el-popover>
+              </span>
+            </template>
+          </el-tree>
+        </div>
+      </div>
+      <div v-if="activeName === 'second'">暂未开放</div>
+      <div v-if="activeName === 'third'">暂未开放</div>
+    </div>
+    <el-dialog
+      :title="editType === 'add' ? '添加文件夹' : '编辑文件夹'"
+      v-model="myFolderDialog"
+      width="300px"
+      destroy-on-close
+      v-loading="submitLoading"
+    >
+      <byForm
+        :formConfig="myFolderFormConfig"
+        :formOption="formOption"
+        v-model="formData.myFolderData"
+        :rules="rules"
+        ref="myFolderForm"
+      >
+      </byForm>
+      <template #footer>
+        <el-button @click="myFolderDialog = false" size="large"
+          >取 消</el-button
+        >
+        <el-button
+          type="primary"
+          @click="submitMyFolderForm()"
+          size="large"
+          :loading="submitLoading"
+        >
+          确 定
+        </el-button>
+      </template>
+    </el-dialog>
+
+    <el-dialog
+      :title="editType === 'add' ? '添加标签' : '编辑标签'"
+      v-model="tagDialog"
+      width="300px"
+      destroy-on-close
+      v-loading="submitLoading"
+    >
+      <byForm
+        :formConfig="tagFormConfig"
+        :formOption="formOption"
+        v-model="formData.tagData"
+        :rules="tagRules"
+        ref="tagForm"
+      >
+      </byForm>
+      <template #footer>
+        <el-button @click="tagDialog = false" size="large">取 消</el-button>
+        <el-button
+          type="primary"
+          @click="submitTagForm()"
+          size="large"
+          :loading="submitLoading"
+        >
+          确 定
+        </el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import useMailStore from "@/store/modules/mail";
+import useUserStore from "@/store/modules/user";
+import byForm from "@/components/byForm/index";
+import { ElMessage, ElMessageBox } from "element-plus";
+const mailStore = useMailStore();
+const { proxy } = getCurrentInstance();
+let selectMail = ref({});
+let activeName = ref("first");
+const mailMapData = {
+  inbox: ["INBOX"],
+  unread: ["UNREAD"],
+  draft: ["草稿箱", "草稿", "Drafts"],
+  sent: ["已发送", "Sent Messages"],
+  delete: ["已删除", "Deleted Messages"],
+  waste: ["垃圾邮件", "Junk"],
+};
+let selectFloderId = ref("");
+const mailList = ref([]);
+const staffMailData = ref([]);
+const formOption = reactive({
+  inline: true,
+  labelWidth: 100,
+  itemWidth: 100,
+});
+const myFolderForm = ref(null);
+const myFolderTreeData = ref([]);
+const myFolderDialog = ref(false);
+const submitLoading = ref(false);
+const editType = ref("add");
+const myFolderFormConfig = computed(() => [
+  {
+    type: "input",
+    prop: "name",
+    label: "文件夹名称",
+    disabled: false,
+    itemWidth: 100,
+  },
+]);
+const formData = reactive({
+  myFolderData: {},
+  tagData: {},
+});
+const rules = ref({
+  name: [{ required: true, message: "请输入文件夹名称", trigger: "blur" }],
+});
+const tagForm = ref(null);
+const tagsTreeData = ref([]);
+const tagDialog = ref(false);
+const tagFormConfig = computed(() => [
+  {
+    type: "input",
+    prop: "name",
+    label: "标签名称",
+    disabled: false,
+    itemWidth: 100,
+  },
+]);
+const tagRules = ref({
+  name: [{ required: true, message: "请输入标签名称", trigger: "blur" }],
+});
+const getTagsList = () => {
+  proxy
+    .post("/myTag/page", {
+      pageNum: 1,
+      pageSize: 9999,
+      id: useUserStore().user.userId,
+    })
+    .then((res) => {
+      if (res && res.rows.length > 0) {
+        tagsTreeData.value = [
+          {
+            name: "我的标签",
+            id: "0",
+            children: res.rows,
+          },
+        ];
+      }
+    });
+};
+
+const submitTagForm = () => {
+  tagForm.value.handleSubmit(() => {
+    submitLoading.value = true;
+    proxy.post("/myTag/" + editType.value, formData.tagData).then(
+      (res) => {
+        ElMessage({
+          message: `操作成功!`,
+          type: "success",
+        });
+        tagDialog.value = false;
+        submitLoading.value = false;
+        getTagsList(selectMail.value);
+      },
+      (err) => {
+        submitLoading.value = false;
+      }
+    );
+  });
+};
+
+const handleEditTag = (data, type) => {
+  editType.value = type;
+  if (type === "add") {
+    formData.tagData.name = "";
+    formData.tagData.type = selectMail.value.type;
+    formData.tagData.mailboxId = selectMail.value.id;
+  } else {
+    formData.tagData = data;
+  }
+  tagDialog.value = true;
+};
+
+const handleDelTag = (data) => {
+  ElMessageBox.confirm(`此操作将删除该标签, 是否继续?`, "提示", {
+    confirmButtonText: "确定",
+    cancelButtonText: "取消",
+    type: "warning",
+  }).then(() => {
+    // 删除
+    proxy.post("/myTag/delete", { id: data.id }).then((res) => {
+      ElMessage({
+        message: `操作成功!`,
+        type: "success",
+      });
+      getTagsList();
+    });
+  });
+};
+
+const getMyFolderTree = (data) => {
+  proxy.post("/myFolder/tree", { mailboxId: data.id }).then((res) => {
+    if (res && res.length > 0) {
+      myFolderTreeData.value = [
+        {
+          label: "我的文件夹",
+          id: "0",
+          children: res,
+        },
+      ];
+    }
+  });
+};
+
+const submitMyFolderForm = () => {
+  myFolderForm.value.handleSubmit(() => {
+    submitLoading.value = true;
+    proxy.post("/myFolder/" + editType.value, formData.myFolderData).then(
+      (res) => {
+        ElMessage({
+          message: `操作成功!`,
+          type: "success",
+        });
+        myFolderDialog.value = false;
+        submitLoading.value = false;
+        getMyFolderTree(selectMail.value);
+      },
+      (err) => {
+        submitLoading.value = false;
+      }
+    );
+  });
+};
+
+const handleEditFolder = (data, type) => {
+  formData.myFolderData = {};
+  editType.value = type;
+  if (type === "add") {
+    formData.myFolderData.parentId = data.id;
+  } else {
+    formData.myFolderData = data;
+    formData.myFolderData.name = data.label;
+  }
+  formData.myFolderData.type = selectMail.value.type;
+  formData.myFolderData.mailboxId = selectMail.value.id;
+  myFolderDialog.value = true;
+};
+
+const handleDelFolder = (data) => {
+  ElMessageBox.confirm(`此操作将删除该文件夹, 是否继续?`, "提示", {
+    confirmButtonText: "确定",
+    cancelButtonText: "取消",
+    type: "warning",
+  }).then(() => {
+    // 删除
+    proxy.post("/myFolder/delete", { id: data.id }).then((res) => {
+      ElMessage({
+        message: `操作成功!`,
+        type: "success",
+      });
+      getMyFolderTree(selectMail.value);
+    });
+  });
+};
+const getMialList = () => {
+  proxy.get("/mailService/getUserEmailList").then((res) => {
+    for (let i = 0; i < res.data.length; i++) {
+      const iele = res.data[i];
+      iele.mailFolderInfoListCopy = [];
+      iele.otherFolder = [];
+      for (let j = 0; j < iele.mailFolderInfoList.length; j++) {
+        const jele = iele.mailFolderInfoList[j];
+        if (mailMapData["inbox"].includes(jele.name)) {
+          iele.mailFolderInfoListCopy.push({
+            ...jele,
+            name: "收件箱",
+            sort: 1,
+          });
+        } else if (mailMapData["unread"].includes(jele.name)) {
+          iele.mailFolderInfoListCopy.push({
+            ...jele,
+            name: "未读邮件",
+            sort: 2,
+          });
+        } else if (mailMapData["draft"].includes(jele.name)) {
+          iele.mailFolderInfoListCopy.push({
+            ...jele,
+            name: "草稿箱",
+            sort: 3,
+          });
+        } else if (mailMapData["sent"].includes(jele.name)) {
+          iele.mailFolderInfoListCopy.push({
+            ...jele,
+            name: "已发送",
+            sort: 4,
+          });
+        } else if (mailMapData["delete"].includes(jele.name)) {
+          iele.mailFolderInfoListCopy.push({
+            ...jele,
+            name: "已删除",
+            sort: 5,
+          });
+        } else if (mailMapData["waste"].includes(jele.name)) {
+          iele.mailFolderInfoListCopy.push({
+            ...jele,
+            name: "垃圾邮件",
+            sort: 6,
+          });
+        } else {
+          iele.otherFolder.push(jele);
+        }
+      }
+      if (iele.otherFolder.length > 0) {
+        iele.otherFolder = [
+          {
+            name: "官方文件夹",
+            id: "0",
+            children: iele.otherFolder,
+          },
+        ];
+      }
+
+      iele.mailFolderInfoListCopy.sort((a, b) => a.sort - b.sort);
+    }
+    mailList.value = res.data;
+    if (mailList.value.length) {
+      // 默认赋值第一邮箱
+      selectMail.value = mailList.value[0];
+      mailStore.selectMail = mailList.value[0];
+      // 获取我的文件夹树形
+      getMyFolderTree(selectMail.value);
+      // 默认打开第一邮箱文件夹
+      if (selectMail.value.mailFolderInfoListCopy.length > 0) {
+        handleOpenMenu(selectMail.value.mailFolderInfoListCopy[0], "10");
+      }
+    }
+  });
+  proxy
+    .post("/mailInfo/getUserEmailList", { id: useUserStore().user.userId })
+    .then((res) => {
+      if (res && res.length > 0) {
+        res = res.map((x) => {
+          return {
+            ...x,
+            sort: 1,
+            children: x.mailFolderInfoList.map((y) => ({
+              ...y,
+              mailUser: y.name,
+            })),
+          };
+        });
+        for (let i = 0; i < res.length; i++) {
+          const iele = res[i];
+          iele.mailFolderInfoListCopy = [];
+          iele.otherFolder = [];
+          for (let j = 0; j < iele.mailFolderInfoList.length; j++) {
+            const jele = iele.mailFolderInfoList[j];
+            if (mailMapData["inbox"].includes(jele.name)) {
+              iele.mailFolderInfoListCopy.push({
+                ...jele,
+                name: "收件箱",
+                sort: 1,
+              });
+            } else if (mailMapData["unread"].includes(jele.name)) {
+              iele.mailFolderInfoListCopy.push({
+                ...jele,
+                name: "未读邮件",
+                sort: 2,
+              });
+            } else if (mailMapData["draft"].includes(jele.name)) {
+              iele.mailFolderInfoListCopy.push({
+                ...jele,
+                name: "草稿箱",
+                sort: 3,
+              });
+            } else if (mailMapData["sent"].includes(jele.name)) {
+              iele.mailFolderInfoListCopy.push({
+                ...jele,
+                name: "已发送",
+                sort: 4,
+              });
+            } else if (mailMapData["delete"].includes(jele.name)) {
+              iele.mailFolderInfoListCopy.push({
+                ...jele,
+                name: "已删除",
+                sort: 5,
+              });
+            } else if (mailMapData["waste"].includes(jele.name)) {
+              iele.mailFolderInfoListCopy.push({
+                ...jele,
+                name: "垃圾邮件",
+                sort: 6,
+              });
+            } else {
+              iele.otherFolder.push(jele);
+            }
+          }
+          if (iele.otherFolder.length > 0) {
+            iele.otherFolder = [
+              {
+                name: "官方文件夹",
+                id: "0",
+                children: iele.otherFolder,
+              },
+            ];
+          }
+          iele.mailFolderInfoListCopy.sort((a, b) => a.sort - b.sort);
+        }
+        staffMailData.value = [
+          {
+            mailUser: "员工邮箱",
+            id: "0",
+            children: res,
+          },
+        ];
+      }
+    });
+};
+
+const handleClickMail = (item, flag = true) => {
+  mailStore.mailMenuList = [];
+  selectMail.value = item;
+  mailStore.selectMail = item;
+  // 默认打开第一邮箱文件夹
+  if (selectMail.value.mailFolderInfoListCopy.length > 0 && flag) {
+    handleOpenMenu(selectMail.value.mailFolderInfoListCopy[0], "10");
+  }
+};
+
+const changeFloderId = (val) => {
+  selectFloderId.value = val;
+};
+
+const handleOpenMenu = (item, listPageType = "10") => {
+  // 10为官方文件夹以及六个外侧文件夹 20为我的文件夹  30为标签文件夹
+  selectFloderId.value = item.id;
+  const menu = {
+    title: item.name,
+    type: selectMail.value.type,
+    folderId: item.id,
+    id: "folder" + "," + item.id,
+    listPageType,
+  };
+  // 如没有这个菜单则push
+  const menuItem = mailStore.mailMenuList.find((x) => x.id === menu.id);
+  if (menuItem === undefined) {
+    mailStore.mailMenuList.push(menu);
+  }
+  // 更新当前选择的tab数据和tab的Id值
+  mailStore.currentMenu = menu;
+  mailStore.currentId = menu.id;
+};
+
+const handleGoWrite = (mail = "", pageType = "0") => {
+  const menu = {
+    title: "写信",
+    type: selectMail.value.type,
+    id: "write",
+    pageType,
+  };
+  if (mail) {
+    menu.reMail = mail;
+  }
+  const index = mailStore.mailMenuList.findIndex((x) => x.id === menu.id);
+  if (index >= 0) {
+    mailStore.mailMenuList[index] = menu;
+  } else {
+    mailStore.mailMenuList.push(menu);
+  }
+  mailStore.currentMenu = menu;
+  mailStore.currentId = menu.id;
+};
+
+const handleTreeNodeClick = (data, type) => {
+  if (data.id != "0") {
+    let menuData = {
+      id: data.id,
+    };
+    let listPageType = "10";
+    if (type === "official") {
+      menuData.name = data.name;
+    } else if (type === "folder") {
+      menuData.name = data.label;
+      listPageType = "20";
+    } else if (type === "tag") {
+      menuData.name = data.name;
+      listPageType = "30";
+    }
+    handleOpenMenu(menuData, listPageType);
+  }
+};
+
+const clickTreeMail = (data, node) => {
+  if (data.id !== "0" && data.sort !== 1) {
+    let mailData = node.parent.data;
+    if (mailStore.selectMail.id === mailData.id) {
+      const menuData = {
+        id: data.id,
+        name: data.name,
+      };
+      handleOpenMenu(menuData, "10");
+    } else {
+      handleClickMail(mailData, false);
+      handleOpenMenu(data, "10");
+    }
+  }
+};
+
+getMialList();
+getTagsList();
+
+defineExpose({
+  handleGoWrite,
+  changeFloderId,
+});
+</script>
+
+<style lang="scss" scoped>
+.left {
+  font-size: 12px;
+  .top {
+    padding: 10px;
+    text-align: center;
+    border-bottom: 1px solid #ddd;
+    .mail {
+      color: #333333;
+      font-weight: 700;
+      font-size: 14px;
+    }
+  }
+  .body {
+    height: calc(100vh - 260px);
+    overflow: auto;
+    // padding: 10px;
+    .mail-menu {
+      list-style: none;
+      margin-block-start: 0;
+      margin-block-end: 0;
+      padding: 0px;
+      padding: 10px 10px 0 10px;
+      .menu-item {
+        display: flex;
+        align-items: center;
+        font-weight: 700;
+        padding-left: 10px;
+        font-size: 12px;
+        width: 100%;
+        height: 40px;
+        line-height: 40px;
+        border-radius: 3px;
+        color: #333333;
+        cursor: pointer;
+        &:hover {
+          background: #ddedfe;
+        }
+        .leftIcon {
+          font-size: 16px;
+          color: #999999;
+        }
+      }
+    }
+  }
+}
+.select-menu {
+  background: #ddedfe;
+  color: #0084ff !important;
+  .leftIcon {
+    color: #0084ff !important;
+  }
+}
+.tree {
+  margin-top: 10px;
+  border-top: 1px solid #ddd;
+  padding: 10px 10px 0 10px;
+  .tree-content {
+    width: 100%;
+    display: flex;
+    // justify-content: space-between;
+    padding-right: 10px;
+    align-items: center;
+    .iconColor {
+      color: #666 !important;
+    }
+  }
+}
+</style>
+<style lang="scss">
+.body {
+  .el-tree-node__content {
+    font-weight: 700;
+    color: #333333 !important;
+    font-size: 12px !important;
+  }
+}
+.top {
+  .el-tabs__item {
+    color: #616161;
+    font-size: 12px;
+  }
+  .el-tabs__item:hover {
+    color: #409eff;
+  }
+  .el-tabs__item.is-active {
+    color: #409eff;
+  }
+}
+</style>

+ 433 - 0
connect/E-mail/mail/com/mailDetail.vue

@@ -0,0 +1,433 @@
+<template>
+  <div v-loading="loading" class="box">
+    <div style="margin-bottom: 10px">
+      <el-button
+        :disabled="currentMailIndex === 0"
+        @click="handleChangeEail('10')"
+        >上一封</el-button
+      >
+      <el-button
+        :disabled="currentMailIndex === allLength - 1"
+        @click="handleChangeEail('20')"
+        >下一封</el-button
+      >
+      <el-button type="primary" @click="handleReply('10')">回复</el-button>
+      <el-button type="primary" @click="handleReply('30')">全部回复</el-button>
+      <el-button type="warning" @click="handleReply('20')">转发</el-button>
+      <el-button @click="handleReply('40')">再次编辑</el-button>
+      <el-button @click="handleMove">移动</el-button>
+      <el-button @click="handleRemove">删除</el-button>
+    </div>
+    <div class="top">
+      <div class="top-row">
+        <div class="label">发 件 人:</div>
+        <div class="value">
+          <span
+            v-for="item in replyTo"
+            :key="item.id"
+            style="margin-right: 10px"
+          >
+            {{ item.email || item.personalName }}</span
+          >
+        </div>
+      </div>
+      <div class="top-row">
+        <div class="label">收 件 人:</div>
+        <div class="value">
+          <span v-for="item in to" :key="item.id" style="margin-right: 10px">{{
+            item.email || item.personalName
+          }}</span>
+        </div>
+      </div>
+      <div class="top-row" v-if="cc && cc.length > 0">
+        <div class="label">抄 送 人:</div>
+        <div class="value">
+          <span v-for="item in cc" :key="item.id" style="margin-right: 10px">
+            {{ item.email || item.personalName }}</span
+          >
+        </div>
+      </div>
+      <div class="top-row" v-if="bcc && bcc.length > 0">
+        <div class="label">密 送 人:</div>
+        <div class="value">
+          <span v-for="item in bcc" :key="item.id" style="margin-right: 10px">
+            {{ item.email || item.personalName }}</span
+          >
+        </div>
+      </div>
+      <div class="top-row">
+        <div class="label">时 间:</div>
+        <div class="value">
+          {{ time }}
+        </div>
+      </div>
+      <div class="top-row" v-if="fileList && fileList.length > 0">
+        <div class="label">附 件:</div>
+        <div class="value">
+          <div
+            v-for="(item, index) in fileList"
+            style="margin-right: 20px; display: flex; align-items: center"
+            :key="index"
+          >
+            <span style="cursor: pointer" @click="handleClickFile(item)">{{
+              item.name
+            }}</span>
+            <span
+              style="margin-left: 8px; cursor: pointer"
+              @click="handleClickDownload(item)"
+              ><el-icon color="#0084FF"><Download /></el-icon
+            ></span>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <div class="body">
+      <iframe
+        frameborder="0"
+        allowTransparency="true"
+        style="
+          width: 99% !important;
+          overflow-x: scroll;
+          padding-top: 10px;
+          height: 500px;
+        "
+        :srcdoc="showBodyText(detailsData.data.content)"
+        :ref="'iframeText_' + mailStore.currentId"
+        :id="'iframeText_' + mailStore.currentId"
+      >
+      </iframe>
+    </div>
+
+    <el-dialog
+      title="移动邮件"
+      v-model="myFolderDialog"
+      width="300px"
+      destroy-on-close
+      v-loading="submitLoading"
+    >
+      <byForm
+        :formConfig="myFolderFormConfig"
+        :formOption="formOption"
+        v-model="formData.myFolderData"
+        :rules="rules"
+        ref="myFolderForm"
+      >
+      </byForm>
+      <template #footer>
+        <el-button @click="myFolderDialog = false" size="large"
+          >取 消</el-button
+        >
+        <el-button
+          type="primary"
+          @click="submitMyFolderForm()"
+          size="large"
+          :loading="submitLoading"
+        >
+          确 定
+        </el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import useMailStore from "@/store/modules/mail";
+import byForm from "@/components/byForm/index";
+import { ElMessage, ElMessageBox } from "element-plus";
+const { proxy } = getCurrentInstance();
+const mailStore = useMailStore();
+let loading = ref(false);
+const detailsData = reactive({
+  data: {},
+});
+const currentMailIndex = computed(() => mailStore.currentMailIndex);
+const allLength = computed(() => mailStore.mailDataList.length);
+const to = ref([]);
+const cc = ref([]);
+const bcc = ref([]);
+const replyTo = ref([]);
+const fileList = ref([]);
+const requestData = ref({});
+const time = ref("");
+// 移动文件夹
+const formOption = reactive({
+  inline: true,
+  labelWidth: 100,
+  itemWidth: 100,
+});
+const myFolderForm = ref(null);
+const myFolderTreeData = ref([]);
+const myFolderDialog = ref(false);
+const submitLoading = ref(false);
+const editType = ref("add");
+const myFolderFormConfig = computed(() => [
+  {
+    type: "treeSelect",
+    prop: "myFolderId",
+    label: "文件夹",
+    disabled: false,
+    itemWidth: 100,
+    data: myFolderTreeData.value,
+    style: {
+      width: "100%",
+    },
+  },
+]);
+const formData = reactive({
+  myFolderData: {},
+});
+const rules = ref({
+  myFolderId: [{ required: true, message: "请选择文件夹", trigger: "change" }],
+});
+
+const getMyFolderTree = (flag) => {
+  if (flag) {
+    setTimeout(() => {
+      proxy
+        .post("/myFolder/tree", { mailboxId: mailStore.selectMail.id })
+        .then((res) => {
+          myFolderTreeData.value = res.map((x) => ({ ...x, value: x.id }));
+        });
+    }, 1000);
+  } else {
+    proxy
+      .post("/myFolder/tree", { mailboxId: mailStore.selectMail.id })
+      .then((res) => {
+        myFolderTreeData.value = res.map((x) => ({ ...x, value: x.id }));
+      });
+  }
+};
+
+const handleMove = () => {
+  formData.myFolderData.messageId = mailStore.currentMenu.messageId;
+  myFolderDialog.value = true;
+};
+
+const handleRemove = () => {
+  ElMessageBox.confirm(`此操作将删除该邮件, 是否继续?`, "提示", {
+    confirmButtonText: "确定",
+    cancelButtonText: "取消",
+    type: "warning",
+  }).then(() => {
+    // 删除
+    proxy
+      .post("/mailService/deleteMail", {
+        id: mailStore.currentMenu.messageId,
+        type: mailStore.currentMenu.type,
+      })
+      .then((res) => {
+        ElMessage({
+          message: `操作成功!`,
+          type: "success",
+        });
+      });
+  });
+};
+
+const submitMyFolderForm = () => {
+  myFolderForm.value.handleSubmit(() => {
+    submitLoading.value = true;
+    proxy.post("/myFolderMessage/add", formData.myFolderData).then(
+      (res) => {
+        ElMessage({
+          message: `操作成功!`,
+          type: "success",
+        });
+        myFolderDialog.value = false;
+        submitLoading.value = false;
+        getMyFolderTree(false);
+      },
+      (err) => {
+        submitLoading.value = false;
+      }
+    );
+  });
+};
+
+const getMailTag = () => {
+  // proxy
+  //   .post("/myTagMessage/getListByMessageId", {
+  //     id: mailStore.currentMenu.messageId,
+  //   })
+  //   .then((res) => {
+  //     console.log(res, "ada");
+  //   });
+};
+
+const getDetails = (messageId) => {
+  loading.value = true;
+  proxy
+    .post("/mailService/getMessageDetail", {
+      type: mailStore.currentMenu.type,
+      messageId,
+    })
+    .then((res) => {
+      if (mailStore.currentMenu.time) {
+        time.value = mailStore.currentMenu.time;
+      }
+      detailsData.data = res;
+      to.value = res.messageAddressList.filter((x) => x.type === 1);
+      cc.value = res.messageAddressList.filter((x) => x.type === 2);
+      bcc.value = res.messageAddressList.filter((x) => x.type === 3);
+      replyTo.value = res.messageAddressList.filter((x) => x.type === 4);
+      fileList.value = res.messageAttachmentList;
+      loading.value = false;
+    });
+};
+
+const showBodyText = (text) => {
+  if (text) {
+    let val = JSON.parse(JSON.stringify(text));
+    val = val.replace("body {", "tbody {");
+    val = val.replace(
+      "td, p, li, th {",
+      "tbody td, tbody p, tbody li, tbody th {"
+    );
+    val = val.replace(
+      /<p>/g,
+      '<p style="margin-block-start: 0; margin-block-end: 0;">'
+    );
+    return val;
+  } else {
+    return text;
+  }
+};
+
+const init = () => {
+  //实时更换索引
+  if (mailStore.currentMenu.messageId) {
+    mailStore.currentMailIndex = mailStore.mailDataList.findIndex(
+      (x) => x.id === mailStore.currentMenu.messageId
+    );
+  }
+  getDetails(mailStore.currentMenu.messageId);
+};
+
+onMounted(() => {
+  // init();
+});
+
+const handleReply = (pageType) => {
+  // pageType 10为回复  20为转发 30为全部回复 40为再次编辑  0为写信
+  let title = "";
+  if (pageType === "10") {
+    title = "回复";
+  } else if (pageType === "20") {
+    title = "转发";
+  } else if (pageType === "30") {
+    title = "全部回复";
+  } else if (pageType === "40") {
+    title = "再次编辑";
+  }
+  const menu = {
+    title: title,
+    type: mailStore.currentMenu.type,
+    id: "write",
+    pageType: pageType,
+    details: {
+      ...detailsData.data,
+      ...mailStore.currentMenu.row,
+    },
+  };
+  const index = mailStore.mailMenuList.findIndex((x) => x.id === menu.id);
+  if (index >= 0) {
+    mailStore.mailMenuList[index] = menu;
+  } else {
+    mailStore.mailMenuList.push(menu);
+  }
+  mailStore.currentMenu = menu;
+  mailStore.currentId = menu.id;
+};
+
+const handleChangeEail = (type) => {
+  if (type === "10") {
+    mailStore.currentMailIndex = currentMailIndex.value - 1;
+  } else if (type === "20") {
+    mailStore.currentMailIndex = currentMailIndex.value + 1;
+  }
+  // 拿到更变索引之后的行数据
+  const row = mailStore.mailDataList[mailStore.currentMailIndex];
+  // 查当前菜单的数据
+  const arr = mailStore.currentMenu.id.split(",");
+  if (arr.length > 1) {
+    const index = mailStore.mailMenuList.findIndex(
+      (x) => x.messageId === arr[1]
+    );
+    const menu = {
+      title: row.subject.slice(0, 4) + "...",
+      type: mailStore.selectMail.type,
+      messageId: row.id,
+      id: "detail" + "," + row.id,
+      time: row.sendDate,
+      row: { ...row },
+    };
+    const menuItem = mailStore.mailMenuList.find((x) => x.id === menu.id);
+    if (menuItem === undefined) {
+      mailStore.mailMenuList[index] = menu;
+    }
+    mailStore.currentMenu = menu;
+    mailStore.currentId = menu.id;
+  }
+};
+
+const handleClickFile = (file) => {
+  const path = file.path ? file.path : file.url ? file.url : "";
+  if (path) {
+    window.open(path, "_blank");
+  }
+};
+
+const handleClickDownload = (file) => {
+  let xhr = new XMLHttpRequest();
+  //域名是华为云的
+  xhr.open("GET", `${file.url}`, true);
+  xhr.responseType = "blob";
+  xhr.send();
+  xhr.onreadystatechange = function () {
+    if (xhr.readyState === 4 && xhr.status === 200) {
+      let url = window.URL.createObjectURL(xhr.response);
+      const a = document.createElement("a");
+      a.href = url;
+      a.download = file.name; // 下载后文件名
+      a.style.display = "none";
+      document.body.appendChild(a);
+      a.click(); // 点击下载
+      window.URL.revokeObjectURL(a.href);
+      document.body.removeChild(a); // 下载完成移除元素
+    }
+  };
+};
+getMyFolderTree(true);
+getMailTag();
+defineExpose({
+  initFn: init,
+});
+</script>
+
+<style lang="scss" scoped>
+.box {
+  padding: 0 10px;
+  font-size: 12px;
+  .top {
+    padding: 10px;
+    background: #f1f1f1;
+    .top-row {
+      display: flex;
+      margin-bottom: 10px;
+      .label {
+        min-width: 60px;
+        color: #999999;
+        text-align: right;
+      }
+      .value {
+        flex: 1;
+        color: #333333;
+        font-weight: 700;
+        margin-right: 10px;
+        display: flex;
+      }
+    }
+  }
+}
+</style>

+ 304 - 0
connect/E-mail/mail/com/mailList.vue

@@ -0,0 +1,304 @@
+<template>
+  <div>
+    <div style="padding-left: 20px">
+      <el-icon @click="handleRefresh" style="cursor: pointer">
+        <Refresh />
+      </el-icon>
+    </div>
+    <div style="padding: 10px 15px; width: 100%">
+      <el-table
+        :data="tableData.data"
+        style="width: 100%"
+        :height="tableHeight"
+        v-loading="loading"
+      >
+        <el-table-column label="状态" width="70">
+          <template #default="{ row, $index }">
+            <div style="cursor: pointer" @click="handleRowClick(row, $index)">
+              <span v-if="row.flags && !row.flags.includes('6')">
+                <img
+                  src="@/assets/images/mail/message.png"
+                  alt=""
+                  title="未读"
+                  class="messageImg"
+                />
+              </span>
+              <span
+                v-if="
+                  row.flags !== undefined &&
+                  row.flags !== null &&
+                  row.flags === ''
+                "
+              >
+                <img
+                  src="@/assets/images/mail/message.png"
+                  alt=""
+                  title="未读"
+                  class="messageImg"
+                />
+              </span>
+              <span
+                v-if="
+                  row.flags &&
+                  row.flags.includes('6') &&
+                  row.flags.includes('1')
+                "
+              >
+                <img
+                  src="@/assets/images/mail/replied.png"
+                  alt=""
+                  title="已回复"
+                  class="messageImg"
+                />
+              </span>
+              <span v-if="row.flags && row.flags.includes('6')">
+                <img
+                  src="@/assets/images/mail/message-open.png"
+                  alt=""
+                  title="已读"
+                  class="messageImg"
+                />
+              </span>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column prop="fromPersonalName" label="发件人" width="150">
+          <template #default="{ row, $index }">
+            <div style="cursor: pointer" @click="handleRowClick(row, $index)">
+              {{ row.fromPersonalName }}
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column prop="fromEmail" label="发件人地址" width="210">
+          <template #default="{ row, $index }">
+            <div style="cursor: pointer" @click="handleRowClick(row, $index)">
+              {{ row.fromEmail }}
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column prop="subject" label="主题">
+          <template #default="{ row, $index }">
+            <div style="cursor: pointer" @click="handleRowClick(row, $index)">
+              {{ row.subject }}
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column prop="sendDate" label="时间" width="160">
+          <template #default="{ row, $index }">
+            <div style="cursor: pointer" @click="handleRowClick(row, $index)">
+              {{ row.sendDate }}
+            </div>
+          </template>
+        </el-table-column>
+      </el-table>
+      <el-pagination
+        style="margin-top: 15px"
+        v-model:current-page="tableData.pagination.pageNum"
+        v-model:page-size="tableData.pagination.pageSize"
+        :page-sizes="[15, 30, 50, 100]"
+        layout="sizes, prev, pager, next"
+        :total="tableData.pagination.total"
+        prev-text="上一页"
+        next-text="下一页"
+        @current-change="handleCurrentChange"
+        @size-change="handleSizeChange"
+      />
+    </div>
+    <!-- <byTable
+      :source="tableData.data"
+      :pagination="tableData.pagination"
+      :config="config"
+      :loading="loading"
+      :hideSearch="true"
+      highlight-current-row
+      :action-list="[]"
+      :table-events="{
+        //element talbe事件都能传
+        'row-click': handleRowClick,
+      }"
+      @get-list="getList"
+    >
+      <template #icon="{ item }">
+        <div>
+          <span v-if="!item.flags.includes('6')">
+            <img
+              src="@/assets/images/mail/message.png"
+              alt=""
+              title="未读"
+              class="messageImg"
+            />
+          </span>
+          <span v-if="item.flags.includes('6') && item.flags.includes('1')">
+            <img
+              src="@/assets/images/mail/replied.png"
+              alt=""
+              title="已回复"
+              class="messageImg"
+            />
+          </span>
+          <span v-if="item.flags.includes('6')">
+            <img
+              src="@/assets/images/mail/message-open.png"
+              alt=""
+              title="已读"
+              class="messageImg"
+            />
+          </span>
+        </div>
+      </template>
+    </byTable> -->
+  </div>
+</template>
+
+<script setup>
+import byTable from "@/components/byTable/index";
+import useMailStore from "@/store/modules/mail";
+const { proxy } = getCurrentInstance();
+const mailStore = useMailStore();
+let loading = ref(false);
+const tableHeight = ref(0);
+tableHeight.value = window.innerHeight - 270;
+const tableData = reactive({
+  data: [],
+  pagination: {
+    pageNum: 1,
+    pageSize: 15,
+    total: 0,
+  },
+});
+const config = computed(() => {
+  return [
+    {
+      attrs: {
+        label: "状态",
+        slot: "icon",
+        width: 70,
+      },
+    },
+    {
+      attrs: {
+        label: "发件人",
+        prop: "fromPersonalName",
+      },
+    },
+    {
+      attrs: {
+        label: "发件人地址",
+        prop: "fromEmail",
+      },
+    },
+    {
+      attrs: {
+        label: "主题",
+        prop: "subject",
+      },
+    },
+    {
+      attrs: {
+        label: "时间",
+        prop: "sendDate",
+      },
+    },
+  ];
+});
+
+const getList = () => {
+  if (mailStore.currentMenu.listPageType === "10") {
+    loading.value = true;
+    proxy
+      .post("/mailService/getMessagePage", {
+        ...mailStore.currentMenu,
+        ...tableData.pagination,
+      })
+      .then((res) => {
+        tableData.data = res.rows;
+        mailStore.mailDataList = res.rows;
+        tableData.pagination.total = res.total;
+        loading.value = false;
+      });
+  } else if (mailStore.currentMenu.listPageType === "20") {
+    loading.value = true;
+    proxy
+      .post("/myFolderMessage/page", {
+        myFolderId: mailStore.currentMenu.folderId,
+        type: mailStore.selectMail.type,
+        ...tableData.pagination,
+      })
+      .then((res) => {
+        tableData.data = res.rows;
+        mailStore.mailDataList = res.rows;
+        tableData.pagination.total = res.total;
+        loading.value = false;
+      });
+  } else if (mailStore.currentMenu.listPageType === "30") {
+    loading.value = true;
+    proxy
+      .post("/myTagMessage/page", {
+        myTagId: mailStore.currentMenu.folderId,
+        type: mailStore.selectMail.type,
+        ...tableData.pagination,
+      })
+      .then((res) => {
+        tableData.data = res.rows;
+        mailStore.mailDataList = res.rows;
+        tableData.pagination.total = res.total;
+        loading.value = false;
+      });
+  }
+};
+
+const handleRefresh = () => {
+  tableData.pagination = {
+    pageNum: 1,
+    pageSize: 15,
+  };
+  getList();
+};
+
+const handleRowClick = (row, index) => {
+  mailStore.currentMailIndex = index;
+  const menu = {
+    title: row.subject.slice(0, 4) + "...",
+    type: mailStore.selectMail.type,
+    messageId: row.id,
+    id: "detail" + "," + row.id,
+    time: row.sendDate,
+    row: { ...row },
+  };
+  const menuItem = mailStore.mailMenuList.find((x) => x.id === menu.id);
+  if (menuItem === undefined) {
+    mailStore.mailMenuList.push(menu);
+  }
+  mailStore.currentMenu = menu;
+  mailStore.currentId = menu.id;
+};
+
+const init = () => {
+  getList();
+};
+
+const handleCurrentChange = (val) => {
+  tableData.pagination.pageNum = val;
+  getList();
+};
+
+const handleSizeChange = (val) => {
+  tableData.pagination.pageSize = val;
+  getList();
+};
+
+defineExpose({
+  initFn: init,
+});
+</script>
+
+<style lang="scss" scoped>
+.messageImg {
+  width: 18px;
+  height: 18px;
+  transform: translateY(5px);
+}
+.el-pagination {
+  padding-left: 30vw;
+}
+</style>

+ 640 - 0
connect/E-mail/mail/com/mailWrite.vue

@@ -0,0 +1,640 @@
+<template>
+  <div v-loading="loading" style="padding: 10px">
+    <!-- <div style="margin-bottom: 10px; padding-left: 65px">
+      <el-button type="primary" @click="handleSend">发 送</el-button>
+    </div> -->
+    <div style="width: 100%">
+      <el-form
+        ref="submit"
+        :model="formData.data"
+        :rules="rules"
+        label-width="65px"
+        labelPosition="right"
+      >
+        <el-form-item>
+          <el-button type="primary" @click="handleSend()"> 发 送 </el-button>
+        </el-form-item>
+        <el-form-item label="收件人" prop="to">
+          <div style="width: 100%">
+            <div style="display: flex; width: 100%">
+              <!-- <el-autocomplete
+                v-model="formData.data.to"
+                clearable
+                placeholder="请输入"
+                style="width: 100%"
+                @select="handlePerson"
+                :fetch-suggestions="querySearchPerson"
+                @keyup.enter.native="handleAdd(10)"
+              >
+              </el-autocomplete> -->
+              <el-input
+                v-model="formData.data.to"
+                placeholder="请输入"
+                @keyup.enter.native="handleAdd(10)"
+              />
+              <!-- <el-button
+                type="primary"
+                @click="handleAdd(10)"
+                style="margin-left: 10px"
+                :disabled="!formData.data.to"
+                >添加</el-button
+              > -->
+            </div>
+            <div style="margin-top: 15px" v-if="to && to.length > 0">
+              <el-tag
+                style="margin-right: 10px"
+                class="ml-2"
+                type="info"
+                v-for="(item, index) in to"
+                :key="index"
+                closable
+                @close="handleClose(index, 10)"
+                >{{ item.address }}</el-tag
+              >
+            </div>
+          </div>
+        </el-form-item>
+        <el-form-item label="" prop="">
+          <div style="display: flex">
+            <span
+              style="color: #666666; cursor: pointer"
+              @click="showcc = !showcc"
+              >抄送</span
+            >
+            <span style="color: ##dddddd; margin: 0 8px">|</span>
+            <span
+              style="color: #666666; cursor: pointer"
+              @click="showbcc = !showbcc"
+              >密送</span
+            >
+          </div>
+        </el-form-item>
+        <el-form-item label="抄送人" prop="cc" v-if="showcc">
+          <div style="width: 100%">
+            <div style="display: flex; width: 100%">
+              <!-- <el-autocomplete
+                v-model="formData.data.cc"
+                clearable
+                class="inline-input w-50"
+                placeholder="请输入"
+                @select="handlePerson"
+                :fetch-suggestions="querySearchPerson"
+                @keyup.enter.native="handleAdd(20)"
+              >
+              </el-autocomplete> -->
+              <el-input
+                v-model="formData.data.cc"
+                placeholder="请输入"
+                @keyup.enter.native="handleAdd(20)"
+              />
+              <!-- <el-button
+                type="primary"
+                @click="handleAdd(20)"
+                style="margin-left: 10px"
+                :disabled="!formData.data.cc"
+                >添加</el-button
+              > -->
+            </div>
+            <div style="margin-top: 15px" v-if="cc && cc.length > 0">
+              <el-tag
+                style="margin-right: 10px"
+                class="ml-2"
+                type="info"
+                v-for="(item, index) in cc"
+                :key="index"
+                closable
+                @close="handleClose(index, 20)"
+                >{{ item.address }}</el-tag
+              >
+            </div>
+          </div>
+        </el-form-item>
+        <el-form-item label="密送人" prop="bcc" v-if="showbcc">
+          <div style="width: 100%">
+            <div style="display: flex; width: 100%">
+              <!-- <el-autocomplete
+                v-model="formData.data.bcc"
+                clearable
+                placeholder="请输入"
+                @select="handlePerson"
+                :fetch-suggestions="querySearchPerson"
+                @keyup.enter.native="handleAdd(30)"
+              >
+              </el-autocomplete> -->
+              <el-input
+                v-model="formData.data.bcc"
+                placeholder="请输入"
+                @keyup.enter.native="handleAdd(30)"
+              />
+              <!-- <el-button
+                type="primary"
+                @click="handleAdd(30)"
+                style="margin-left: 10px"
+                :disabled="!formData.data.bcc"
+                >添加</el-button
+              > -->
+            </div>
+            <div style="margin-top: 15px" v-if="bcc && bcc.length > 0">
+              <el-tag
+                style="margin-right: 10px"
+                class="ml-2"
+                type="info"
+                v-for="(item, index) in bcc"
+                :key="index"
+                closable
+                @close="handleClose(index, 30)"
+                >{{ item.address }}</el-tag
+              >
+            </div>
+          </div>
+        </el-form-item>
+        <el-form-item label="主题" prop="subject">
+          <el-input v-model="formData.data.subject" placeholder="请输入" />
+        </el-form-item>
+        <el-form-item label="附件">
+          <div style="width: 100%">
+            <el-upload
+              v-model:fileList="fileList"
+              class="upload-demo"
+              action="https://winfaster.obs.cn-south-1.myhuaweicloud.com"
+              :data="uploadData"
+              drag
+              multiple
+              :show-file-list="false"
+              :before-upload="handleBeforeUpload"
+            >
+              <div>
+                <img
+                  src="@/assets/images/icon_attachment.svg"
+                  class="att-img"
+                  fit="scale-down"
+                />
+                <span style="padding-left: 8px"
+                  >将文件拖到此处,或<span style="color: #409eff"
+                    >点击上传</span
+                  ></span
+                >
+              </div>
+            </el-upload>
+            <div class="att-box" v-if="fileListCopy && fileListCopy.length > 0">
+              <div v-for="(itemFile, index) in fileListCopy" :key="index">
+                <div class="att-item">
+                  <img
+                    src="@/assets/images/icon_dz.svg"
+                    style="cursor: pointer"
+                    fit="scale-down"
+                  />
+                  <div class="att-name">
+                    {{ itemFile.fileName }}
+                  </div>
+                  <img
+                    src="@/assets/images/icon_delete.svg"
+                    class="att-img"
+                    fit="scale-down"
+                  />
+                </div>
+              </div>
+            </div>
+          </div>
+        </el-form-item>
+        <el-form-item label="正文" prop="content">
+          <div style="width: 100%">
+            <Editor
+              :value="formData.data.content"
+              @updateValue="updateContent"
+              ref="contentEditor"
+            />
+          </div>
+        </el-form-item>
+        <el-form-item label="发件人" prop="replyTo">
+          <el-input
+            v-model="formData.data.replyTo"
+            placeholder="请输入"
+            style="width: 50%"
+          />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="handleSend()"> 发 送 </el-button>
+        </el-form-item>
+      </el-form>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import byForm from "@/components/byForm/index";
+import { ElMessage, ElMessageBox } from "element-plus";
+import Editor from "@/components/Editor/index.vue";
+import { validEmail } from "@/utils/validate.js";
+import useMailStore from "@/store/modules/mail";
+import { nextTick } from "vue";
+const mailStore = useMailStore();
+const { proxy } = getCurrentInstance();
+const loading = ref(false);
+let uploadData = ref({});
+const fileList = ref([]);
+const fileListCopy = ref([]);
+const showcc = ref(false);
+const showbcc = ref(false);
+const formOption = reactive({
+  inline: true,
+  labelWidth: 65,
+  itemWidth: 100,
+  labelPosition: "right",
+});
+const formConfig = computed(() => {
+  return [
+    {
+      type: "slot",
+      slotName: "to",
+      label: "收件人",
+      prop: "to",
+    },
+    {
+      type: "slot",
+      slotName: "bbcc",
+    },
+    {
+      type: "slot",
+      slotName: "cc",
+      label: "抄送人",
+      prop: "cc",
+    },
+    {
+      type: "slot",
+      slotName: "bcc",
+      label: "密送人",
+      prop: "bcc",
+    },
+    {
+      type: "input",
+      prop: "subject",
+      label: "主题",
+      required: true,
+      itemWidth: 50.1,
+      itemType: "text",
+    },
+    {
+      type: "slot",
+      slotName: "fileSlot",
+      label: "附件",
+    },
+    {
+      type: "slot",
+      slotName: "contentSlot",
+      label: "正文",
+    },
+    {
+      type: "input",
+      prop: "replyTo",
+      label: "发件人",
+      required: true,
+      itemWidth: 50,
+      itemType: "text",
+    },
+  ];
+});
+const rules = ref({
+  subject: [{ required: true, message: "请输入主题", trigger: "blur" }],
+  replyTo: [{ required: true, message: "请输入发件人", trigger: "blur" }],
+});
+const formData = reactive({
+  data: {
+    content: "",
+  },
+});
+const to = ref([]);
+const cc = ref([]);
+const bcc = ref([]);
+const replyTo = ref([]);
+const submit = ref(null);
+const handleReset = () => {
+  formData.data = {
+    to: "",
+    cc: "",
+    bcc: "",
+    content: "",
+    subject: "",
+    replyTo: "",
+  };
+  to.value = [];
+  cc.value = [];
+  bcc.value = [];
+  fileList.value = [];
+  fileListCopy.value = [];
+  contentEditor.value.changeHtml("");
+};
+const handleSend = () => {
+  submit.value.validate((valid) => {
+    if (valid) {
+      const data = { ...formData.data };
+      if (!to.value.length > 0) {
+        return ElMessage({
+          message: "请添加收件人",
+          type: "info",
+        });
+      }
+      if (!validEmail(formData.data.replyTo)) {
+        return ElMessage({
+          message: "发件人邮箱格式不正确",
+          type: "info",
+        });
+      }
+      if (data.content) {
+        loading.value = true;
+        let replyTo = [
+          {
+            address: formData.data.replyTo,
+            personal: null,
+          },
+        ];
+        const submitData = {
+          type: mailStore.selectMail.type,
+          mailboxId: mailStore.selectMail.id,
+          subject: data.subject,
+          content: data.content,
+          to: to.value,
+          cc: cc.value,
+          bcc: bcc.value,
+          replyTo: replyTo,
+          fileList: fileListCopy.value.map((x) => ({
+            fileName: x.fileName,
+            fileUrl: x.fileUrl,
+          })),
+        };
+        proxy.post("/mailService/sendMail", submitData).then((res) => {
+          ElMessage({
+            message: "发送成功",
+            type: "success",
+          });
+          handleReset();
+          loading.value = false;
+        });
+      } else {
+        return ElMessage({
+          message: "请输入正文",
+          type: "info",
+        });
+      }
+    }
+  });
+};
+const updateContent = (val) => {
+  formData.data.content = val;
+};
+const handleBeforeUpload = async (file) => {
+  const res = await proxy.post("/fileInfo/getSing", { fileName: file.name });
+  uploadData.value = res.uploadBody;
+  fileListCopy.value.push({
+    id: res.id,
+    fileName: res.fileName,
+    path: res.fileUrl,
+    fileUrl: res.fileUrl,
+    uid: file.uid,
+  });
+};
+const handleClose = (index, val) => {
+  switch (val) {
+    case 10:
+      to.value.splice(index, 1);
+      break;
+    case 20:
+      cc.value.splice(index, 1);
+      break;
+    case 30:
+      bcc.value.splice(index, 1);
+      break;
+  }
+};
+
+const handleAdd = (val) => {
+  switch (val) {
+    case 10: {
+      if (!validEmail(formData.data.to)) {
+        return ElMessage({
+          message: "收件人邮箱格式不正确",
+          type: "info",
+        });
+      }
+      to.value.push({
+        address: formData.data.to,
+        personal: null,
+      });
+      formData.data.to = "";
+      break;
+    }
+    case 20: {
+      if (!validEmail(formData.data.cc)) {
+        return ElMessage({
+          message: "抄送人邮箱格式不正确",
+          type: "info",
+        });
+      }
+      cc.value.push({
+        address: formData.data.cc,
+        personal: null,
+      });
+      formData.data.cc = "";
+      break;
+    }
+    case 30: {
+      if (!validEmail(formData.data.bcc)) {
+        return ElMessage({
+          message: "密送人邮箱格式不正确",
+          type: "info",
+        });
+      }
+      bcc.value.push({
+        address: formData.data.bcc,
+        personal: null,
+      });
+      formData.data.bcc = "";
+      break;
+    }
+  }
+};
+
+const handlePerson = () => {};
+const createFilter = (queryString) => {
+  return (restaurant) => {
+    return (
+      restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0
+    );
+  };
+};
+
+const distributionCenterData = ref([]);
+const querySearchPerson = (queryString, callback) => {
+  const results = queryString
+    ? distributionCenterData.value.filter(createFilter(queryString))
+    : distributionCenterData.value;
+  callback(results);
+};
+
+const contentEditor = ref(null);
+const handleReplyInit = (allData, pageType) => {
+  let data = allData.details;
+  // 回复功能:则发件人是回复的收件人
+  if (pageType === "10") {
+    if (data.messageAddressList && data.messageAddressList.length > 0) {
+      to.value = data.messageAddressList
+        .filter((x) => x.type === 4)
+        .map((x) => ({
+          address: x.email,
+          personal: x.personalName,
+        }));
+    }
+    formData.data.subject = "Re: " + data.subject;
+  } else if (pageType === "20") {
+    to.value = [];
+    formData.data.subject = "Fw: " + data.subject;
+  } else if (pageType === "30") {
+    // 全部回复
+    if (data.messageAddressList && data.messageAddressList.length > 0) {
+      to.value = data.messageAddressList
+        .filter((x) => x.type !== 1)
+        .map((x) => ({
+          address: x.email,
+          personal: x.personalName,
+        }));
+    }
+    formData.data.subject = "Re: " + data.subject;
+  } else if (pageType === "40") {
+    if (data.messageAddressList && data.messageAddressList.length > 0) {
+      to.value = data.messageAddressList
+        .filter((x) => x.type === 1)
+        .map((x) => ({
+          address: x.email,
+          personal: x.personalName,
+        }));
+      const cc = data.messageAddressList
+        .filter((x) => x.type === 2)
+        .map((x) => ({
+          address: x.email,
+          personal: x.personalName,
+        }));
+      if (cc.length > 0) {
+        showcc.value = true;
+        cc.value = cc;
+      }
+      const bcc = data.messageAddressList
+        .filter((x) => x.type === 2)
+        .map((x) => ({
+          address: x.email,
+          personal: x.personalName,
+        }));
+      if (bcc.length > 0) {
+        showbcc.value = true;
+        bcc.value = bcc;
+      }
+    }
+    formData.data.subject = data.subject;
+  } else if (pageType === "50") {
+    handleReset();
+    to.value = [
+      {
+        address: mailStore.currentMenu.reMail,
+        personal: null,
+      },
+    ];
+  }
+  if (pageType !== "50") {
+    contentEditor.value.changeHtml(
+      `<p><br></p><p><br></p>${data.content}`,
+      true
+    );
+    nextTick(() => {
+      contentEditor.value.getFocus();
+    });
+    formData.data.replyTo = mailStore.selectMail.mailUser;
+  }
+};
+// pageType 10为回复  20为转发 30为全部回复 40为再次编辑  0为写信 50为只回填收件人
+const init = () => {
+  if (mailStore.currentMenu.pageType === "0") {
+    handleReset();
+  } else if (mailStore.currentMenu.pageType === "10") {
+    handleReplyInit(mailStore.currentMenu, "10");
+  } else if (mailStore.currentMenu.pageType === "20") {
+    handleReplyInit(mailStore.currentMenu, "20");
+  } else if (mailStore.currentMenu.pageType === "30") {
+    handleReplyInit(mailStore.currentMenu, "30");
+  } else if (mailStore.currentMenu.pageType === "40") {
+    handleReplyInit(mailStore.currentMenu, "40");
+  } else if (mailStore.currentMenu.pageType === "50") {
+    handleReplyInit(mailStore.currentMenu, "50");
+  }
+};
+watch(
+  () => mailStore.currentMenu.pageType,
+  (val) => {
+    if (val === "0") {
+      handleReset();
+    } else if (val === "10") {
+      handleReplyInit(mailStore.currentMenu, "10");
+    } else if (val === "20") {
+      handleReplyInit(mailStore.currentMenu, "20");
+    } else if (val === "30") {
+      handleReplyInit(mailStore.currentMenu, "30");
+    } else if (val === "40") {
+      handleReplyInit(mailStore.currentMenu, "40");
+    } else if (val === "50") {
+      handleReplyInit(mailStore.currentMenu, "50");
+    }
+  }
+);
+
+onMounted(() => {
+  // if (mailStore.currentMenu.reMail) {
+  //   to.value.push({
+  //     address: mailStore.currentMenu.reMail,
+  //     personal: null,
+  //   });
+  // }
+});
+
+defineExpose({
+  initFn: init,
+});
+</script>
+
+<style lang="scss" scoped>
+.att-img {
+  height: 18px;
+  width: 18px;
+  transform: translateY(4px);
+}
+.att-box {
+  padding-bottom: 8px;
+  display: flex;
+  flex-wrap: wrap;
+  .att-item {
+    width: 200px;
+    background-color: #eeeeee;
+    height: 28px;
+    line-height: 26px;
+    margin-right: 16px;
+    padding-left: 8px;
+    display: flex;
+    margin-top: 10px;
+    // margin-bottom: 8px;
+  }
+  .att-name {
+    padding-left: 8px;
+    width: 150px;
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    cursor: pointer;
+  }
+}
+:deep(.el-upload-dragger) {
+  width: 250px;
+  height: 40px;
+  line-height: 40px;
+  padding: 0 8px;
+}
+// .el-form-item--default {
+//   margin-bottom: 0px;
+// }
+</style>

+ 122 - 0
connect/E-mail/mail/com/main.vue

@@ -0,0 +1,122 @@
+<template>
+  <div class="container">
+    <el-tabs
+      v-model="activeMenu"
+      closable
+      :stretch="false"
+      type="card"
+      @tab-remove="handleTabRemove"
+      @tab-change="handleTabChange"
+    >
+      <el-tab-pane
+        v-for="(item, index) in menuList"
+        :key="index"
+        :label="item.title"
+        :name="item.id"
+      >
+        <div class="main">
+          <mailList
+            v-show="item.id.includes('folder')"
+            :ref="'folder' + index"
+          ></mailList>
+          <mailDetail
+            v-show="item.id.includes('detail')"
+            :ref="'detail' + index"
+          ></mailDetail>
+          <mailWrite
+            v-show="item.id.includes('write')"
+            :ref="'write' + index"
+          ></mailWrite>
+        </div>
+      </el-tab-pane>
+    </el-tabs>
+  </div>
+</template>
+
+<script setup>
+import mailList from "./mailList.vue";
+import mailDetail from "./mailDetail.vue";
+import mailWrite from "./mailWrite.vue";
+
+import useMailStore from "@/store/modules/mail";
+import { ElMessage, ElMessageBox } from "element-plus";
+import { nextTick, watch } from "vue";
+
+const mailStore = useMailStore();
+const { proxy } = getCurrentInstance();
+const menuList = ref([]);
+let activeMenu = ref("");
+// 订阅监听
+mailStore.$subscribe((mutations, state) => {
+  activeMenu.value = state.currentId;
+  menuList.value = state.mailMenuList;
+});
+
+const handleTabRemove = (id) => {
+  const clickIndex = menuList.value.findIndex((x) => x.id === id);
+  let currentMenu = {};
+  if (menuList.value.length > 1) {
+    // 如果删除的是当前菜单
+    if (activeMenu.value === id) {
+      if (menuList.value.length > clickIndex + 1) {
+        currentMenu = menuList.value[clickIndex + 1];
+      } else {
+        currentMenu = menuList.value[clickIndex - 1];
+      }
+      // 更新store值
+      mailStore.currentMenu = currentMenu;
+      mailStore.currentId = currentMenu.id;
+    }
+    menuList.value.splice(clickIndex, 1);
+  } else {
+    return ElMessage({
+      message: "最后一个标签页啦!",
+      type: "info",
+    });
+  }
+};
+
+const checkType = (menu, type) => {
+  // if (type == "write") return;
+  const index = menuList.value.findIndex((x) => x.id === activeMenu.value);
+  const dom = type + index.toString();
+  nextTick(() => {
+    if (type == "write") {
+      proxy.$refs[dom][0].initFn();
+    } else {
+      proxy.$refs[dom][0].initFn();
+    }
+  });
+};
+
+const handleTabChange = (val) => {
+  const currentMenu = menuList.value.find((x) => x.id === val);
+  mailStore.$patch({
+    currentId: val,
+    currentMenu: currentMenu,
+  });
+  // 判断是文件夹还是邮件详情
+  let type = "";
+  if (currentMenu.id.includes("folder")) {
+    type = "folder";
+    // 如果更换的是文件夹菜单则更新左侧样式
+    proxy.$emit("changeFloderId", currentMenu.folderId);
+  } else if (currentMenu.id.includes("detail")) {
+    type = "detail";
+  } else {
+    type = "write";
+  }
+  checkType(currentMenu, type);
+};
+
+onMounted(() => {});
+</script>
+
+<style lang="scss" scoped>
+.container {
+  .main {
+    height: calc(100vh - 180px);
+    overflow: auto;
+  }
+}
+</style>

+ 58 - 0
connect/E-mail/mail/com/right.vue

@@ -0,0 +1,58 @@
+<template>
+  <div style="height: calc(100vh - 50px - 50px - 10px - 30px)">
+    <!-- <el-radio-group v-model="headRadio" size="mini">
+      <el-radio-button label="1">合同制作</el-radio-button>
+      <el-radio-button label="2">单证</el-radio-button>
+      <el-radio-button label="3">采购合同</el-radio-button>
+      <el-radio-button label="4">网盘资料</el-radio-button>
+    </el-radio-group> -->
+    <el-tabs v-model="headRadio" style="font-size: 13px" stretch>
+      <el-tab-pane label="合同制作" name="1"> </el-tab-pane>
+      <el-tab-pane label="单证" name="2"> </el-tab-pane>
+      <el-tab-pane label="采购合同" name="3"> </el-tab-pane>
+      <el-tab-pane label="网盘资料" name="4"> </el-tab-pane>
+    </el-tabs>
+    <div v-show="headRadio === '1'">
+      <Contract></Contract>
+    </div>
+    <div v-show="headRadio === '2'">
+      <Documents></Documents>
+    </div>
+    <div v-show="headRadio === '3'">
+      <Purchase></Purchase>
+    </div>
+    <div v-show="headRadio === '4'">
+      <Netdisk></Netdisk>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import Contract from "./right/contract/index.vue";
+import Documents from "./right/documents/index.vue";
+import Purchase from "./right/purchase/index.vue";
+import Netdisk from "./right/netdisk/index.vue";
+
+const headRadio = ref("");
+onMounted(() => {
+  headRadio.value = "1";
+});
+</script>
+<style lang="scss" scoped>
+* {
+  font-size: 12px;
+}
+// :deep(.el-radio-button__inner) {
+//   font-size: 12px;
+//   padding: 6px 10px;
+// }
+:deep(.el-tabs__item) {
+  font-size: 12px;
+  line-height: 30px;
+  height: 30px;
+  font-weight: normal;
+}
+:deep(.el-tabs__header) {
+  margin: 10px 0;
+}
+</style>

+ 36 - 0
connect/E-mail/mail/com/right/contract/index.vue

@@ -0,0 +1,36 @@
+<template>
+  <div>
+    <!-- <el-tabs v-model="contractName" style="font-size: 12px">
+      <el-tab-pane label="报价单" name="1">
+        <Quotation> </Quotation>
+      </el-tab-pane>
+      <el-tab-pane label="PI" name="2"> <Pi></Pi> </el-tab-pane>
+    </el-tabs> -->
+    <el-radio-group
+      v-model="contractName"
+      size="mini"
+      style="margin: 5px 0px 15px"
+    >
+      <el-radio-button label="1">报价单</el-radio-button>
+      <el-radio-button label="2">PI</el-radio-button>
+    </el-radio-group>
+    <div>
+      <Quotation v-show="contractName === '1'"> </Quotation>
+      <Pi v-show="contractName === '2'"></Pi>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import Quotation from "./quotation";
+import Pi from "./pi";
+const contractName = ref("");
+contractName.value = "1";
+</script>
+
+<style lang="scss" scoped>
+:deep(.el-radio-button__inner) {
+  font-size: 13px;
+  padding: 6px 10px;
+}
+</style>

+ 165 - 0
connect/E-mail/mail/com/right/contract/pi.vue

@@ -0,0 +1,165 @@
+<template>
+  <div>
+    <el-button type="primary" size="mini" class="btn" @click="handleMakeNew"
+      >制作合同</el-button
+    >
+    <div style="margin: 10px 0; display: flex; align-items: center">
+      <el-input
+        class="input_content"
+        placeholder="请输入内容"
+        v-model="sourceList.pagination.keyword"
+        style="width: 50%"
+        clearable
+        size="small"
+      ></el-input>
+      <el-select
+        v-model="sourceList.pagination.status"
+        clearable
+        filterable
+        style="width: 40%; margin-left: 10px"
+        size="small"
+      >
+        <el-option
+          v-for="(item, index) in statusData"
+          :key="index"
+          :value="item.value"
+          :label="item.label"
+        ></el-option>
+      </el-select>
+      <div style="text-align: center; width: 10%">
+        <el-icon style="cursor: pointer" @click="getData"><Search /></el-icon>
+      </div>
+    </div>
+    <el-table
+      :data="sourceList.data"
+      style="width: 100%; margin-top: 10px"
+      v-loading="loading"
+      :height="tableHeight"
+    >
+      <el-table-column prop="code" label="单号" width="115" fixed="left" />
+      <el-table-column prop="createTime" label="日期" width="140" />
+      <el-table-column prop="buyContactName" label="客户" min-width="150" />
+      <el-table-column prop="amount" label="报价" width="100">
+        <template #default="{ row }">
+          <div>{{ row.currency }} {{ moneyFormat(row.amount, 2) }}</div>
+        </template>
+      </el-table-column>
+      <el-table-column
+        prop="status"
+        label="状态"
+        width="80"
+        :formatter="getStatus"
+      />
+      <!-- <el-table-column label="操作" width="60" fixed="right" align="center">
+        <template #default="{ row }">
+          <el-button type="primary" text
+            ><i class="iconfont icon-iconm_wofqd"></i
+          ></el-button>
+        </template>
+      </el-table-column> -->
+    </el-table>
+    <el-pagination
+      style="margin-top: 10px"
+      v-model:current-page="sourceList.pagination.pageNum"
+      :page-size="10"
+      layout="total, prev, pager, next"
+      :total="sourceList.pagination.total"
+      prev-text="上一页"
+      next-text="下一页"
+      @current-change="handleCurrentChange"
+    />
+  </div>
+</template>
+  
+<script setup>
+const tableHeight = window.innerHeight - 280;
+const loading = ref(false);
+const sourceList = ref({
+  data: [],
+  pagination: {
+    total: 300,
+    pageNum: 1,
+    pageSize: 10,
+  },
+});
+const statusData = ref([
+  {
+    label: "草稿",
+    value: 0,
+  },
+  {
+    label: "审批中",
+    value: 10,
+  },
+  {
+    label: "驳回",
+    value: 20,
+  },
+  {
+    label: "通过",
+    value: 30,
+  },
+  {
+    label: "终止",
+    value: 99,
+  },
+]);
+const { proxy } = getCurrentInstance();
+const getStatus = (row) => {
+  const current = statusData.value.find((x) => x.value == row.status);
+  if (current) return current.label;
+};
+
+const getData = () => {
+  loading.value = true;
+  proxy.post("/contract/page", sourceList.value.pagination).then((res) => {
+    sourceList.value.data = res.rows;
+    sourceList.value.pagination.total = res.total;
+    setTimeout(() => {
+      loading.value = false;
+    }, 200);
+  });
+};
+
+const handleCurrentChange = (val) => {
+  sourceList.value.pagination.pageNum = val;
+  getData();
+};
+
+getData();
+onMounted(() => {});
+const handleMakeNew = () => {
+  proxy.$router.replace({
+    path: "/platform_manage/process/processApproval",
+    query: {
+      flowKey: "contract_flow",
+      flowName: "销售合同审批流程",
+      random: proxy.random(),
+    },
+  });
+};
+</script>
+  
+<style lang="scss" scoped>
+* {
+  font-size: 12px;
+}
+.el-button {
+  padding: 0px;
+}
+.btn {
+  width: 100%;
+  border-radius: 10px;
+  padding: 6px 10px;
+  height: 24px;
+}
+.el-pagination {
+  padding-left: 30px;
+}
+:deep(.el-pagination button, .el-pager li) {
+  font-size: 12px;
+}
+:deep(.el-table .el-table__cell) {
+  padding: 2px 0px;
+}
+</style>

+ 166 - 0
connect/E-mail/mail/com/right/contract/quotation.vue

@@ -0,0 +1,166 @@
+<template>
+  <div>
+    <el-button type="primary" size="mini" class="btn" @click="handleMakeNew"
+      >制作报价单</el-button
+    >
+    <div style="margin: 10px 0; display: flex; align-items: center">
+      <el-input
+        class="input_content"
+        placeholder="请输入内容"
+        v-model="sourceList.pagination.keyword"
+        style="width: 50%"
+        clearable
+        size="small"
+      ></el-input>
+      <el-select
+        v-model="sourceList.pagination.status"
+        clearable
+        filterable
+        style="width: 40%; margin-left: 10px"
+        size="small"
+      >
+        <el-option
+          v-for="item in statusData"
+          :key="item.value"
+          :label="item.label"
+          :value="item.value"
+        />
+      </el-select>
+      <div style="text-align: center; width: 10%">
+        <el-icon style="cursor: pointer" @click="getData"><Search /></el-icon>
+      </div>
+    </div>
+    <el-table
+      :data="sourceList.data"
+      style="width: 100%; margin-top: 10px"
+      v-loading="loading"
+      :height="tableHeight"
+    >
+      <el-table-column prop="code" label="单号" width="115" fixed="left" />
+      <el-table-column prop="createTime" label="日期" width="140" />
+      <el-table-column prop="buyContactName" label="客户" min-width="150" />
+      <el-table-column prop="amount" label="报价" width="100">
+        <template #default="{ row }">
+          <div>{{ row.currency }} {{ moneyFormat(row.amount, 2) }}</div>
+        </template>
+      </el-table-column>
+      <el-table-column
+        prop="status"
+        label="状态"
+        width="80"
+        :formatter="getStatus"
+      />
+      <!-- <el-table-column label="操作" width="60" fixed="right" align="center">
+        <template #default="{ row }">
+          <el-button type="primary" text
+            ><i class="iconfont icon-iconm_wofqd"></i
+          ></el-button>
+        </template>
+      </el-table-column> -->
+    </el-table>
+    <el-pagination
+      style="margin-top: 10px"
+      v-model:current-page="sourceList.pagination.pageNum"
+      :page-size="10"
+      layout="total, prev, pager, next"
+      :total="sourceList.pagination.total"
+      prev-text="上一页"
+      next-text="下一页"
+      @current-change="handleCurrentChange"
+    />
+  </div>
+</template>
+  
+<script setup>
+const tableHeight = window.innerHeight - 280;
+const loading = ref(false);
+const sourceList = ref({
+  data: [],
+  pagination: {
+    total: 300,
+    pageNum: 1,
+    pageSize: 10,
+    keyword: "",
+  },
+});
+const statusData = ref([
+  {
+    label: "草稿",
+    value: 0,
+  },
+  {
+    label: "审批中",
+    value: 10,
+  },
+  {
+    label: "驳回",
+    value: 20,
+  },
+  {
+    label: "通过",
+    value: 30,
+  },
+  {
+    label: "终止",
+    value: 99,
+  },
+]);
+const { proxy } = getCurrentInstance();
+const getStatus = (row) => {
+  const current = statusData.value.find((x) => x.value == row.status);
+  if (current) return current.label;
+};
+
+const getData = () => {
+  loading.value = true;
+  proxy.post("/saleQuotation/page", sourceList.value.pagination).then((res) => {
+    sourceList.value.data = res.rows;
+    sourceList.value.pagination.total = res.total;
+    setTimeout(() => {
+      loading.value = false;
+    }, 200);
+  });
+};
+
+const handleCurrentChange = (val) => {
+  sourceList.value.pagination.pageNum = val;
+  getData();
+};
+
+getData();
+onMounted(() => {});
+const handleMakeNew = () => {
+  proxy.$router.replace({
+    path: "/platform_manage/process/processApproval",
+    query: {
+      flowKey: "sale_quotation_flow",
+      flowName: "报价审批流程",
+      random: proxy.random(),
+    },
+  });
+};
+</script>
+  
+<style lang="scss" scoped>
+* {
+  font-size: 12px;
+}
+.el-button {
+  padding: 0px;
+}
+.btn {
+  width: 100%;
+  border-radius: 10px;
+  padding: 6px 10px;
+  height: 24px;
+}
+.el-pagination {
+  padding-left: 30px;
+}
+:deep(.el-pagination button, .el-pager li) {
+  font-size: 12px;
+}
+:deep(.el-table .el-table__cell) {
+  padding: 2px 0px;
+}
+</style>

+ 109 - 0
connect/E-mail/mail/com/right/documents/index.vue

@@ -0,0 +1,109 @@
+<template>
+  <div>
+    <div style="margin: 10px 0; display: flex; align-items: center">
+      <el-input
+        class="input_content"
+        placeholder="请输入内容"
+        v-model="sourceList.pagination.keyword"
+        style="width: 90%"
+        clearable
+        size="small"
+      ></el-input>
+      <div style="text-align: center; width: 10%">
+        <el-icon style="cursor: pointer" @click="getData"><Search /></el-icon>
+      </div>
+    </div>
+    <el-table
+      :data="sourceList.data"
+      style="width: 100%; margin-top: 10px"
+      v-loading="loading"
+      :height="tableHeight"
+    >
+      <el-table-column
+        prop="buyCorporationName"
+        label="客户"
+        min-width="120"
+        fixed="left"
+      />
+      <el-table-column prop="code" label="主合同号" width="100" />
+      <el-table-column prop="amount" label="货运" min-width="150">
+        <template #default="{ row }">
+          <div style="color: #409eff" v-if="row.acceptCode">
+            {{ row.acceptCarriage }} ({{ row.acceptCode }})
+          </div>
+        </template>
+      </el-table-column>
+      <!-- <el-table-column label="操作" width="60" fixed="right" align="center">
+        <template #default="{ row }">
+          <el-button type="primary" text
+            ><i class="iconfont icon-iconm_wofqd"></i
+          ></el-button>
+        </template>
+      </el-table-column> -->
+    </el-table>
+    <el-pagination
+      style="margin-top: 10px"
+      v-model:current-page="sourceList.pagination.pageNum"
+      :page-size="10"
+      layout="total, prev, pager, next"
+      :total="sourceList.pagination.total"
+      prev-text="上一页"
+      next-text="下一页"
+      @current-change="handleCurrentChange"
+    />
+  </div>
+</template>
+  
+<script setup>
+const tableHeight = window.innerHeight - 209;
+const loading = ref(false);
+const sourceList = ref({
+  data: [],
+  pagination: {
+    total: 300,
+    pageNum: 1,
+    pageSize: 10,
+  },
+});
+const { proxy } = getCurrentInstance();
+const getData = () => {
+  loading.value = true;
+  proxy.post("/documents/page", sourceList.value.pagination).then((res) => {
+    sourceList.value.data = res.rows;
+    sourceList.value.pagination.total = res.total;
+    setTimeout(() => {
+      loading.value = false;
+    }, 200);
+  });
+};
+const handleCurrentChange = (val) => {
+  sourceList.value.pagination.pageNum = val;
+  getData();
+};
+getData();
+onMounted(() => {});
+</script>
+  
+<style lang="scss" scoped>
+* {
+  font-size: 12px;
+}
+.el-button {
+  padding: 0px;
+}
+.btn {
+  width: 100%;
+  border-radius: 10px;
+  padding: 6px 10px;
+  height: 24px;
+}
+.el-pagination {
+  padding-left: 30px;
+}
+:deep(.el-pagination button, .el-pager li) {
+  font-size: 12px;
+}
+:deep(.el-table .el-table__cell) {
+  padding: 2px 0px;
+}
+</style>

+ 194 - 0
connect/E-mail/mail/com/right/netdisk/enterprise.vue

@@ -0,0 +1,194 @@
+<template>
+  <div>
+    <div style="margin-bottom: 10px; display: flex; align-items: center">
+      <el-input
+        class="input_content"
+        placeholder="请输入内容"
+        v-model="sourceList.pagination.keyword"
+        style="width: 90%"
+        clearable
+        size="small"
+      ></el-input>
+      <div style="text-align: center; width: 10%">
+        <el-icon style="cursor: pointer" @click="getList"><Search /></el-icon>
+      </div>
+    </div>
+    <el-row>
+      <el-col :span="6" style="border-right: 1px solid #d7d7d7">
+        <div class="mulu">
+          <el-tree
+            :data="treeListData"
+            :props="{ label: 'name', children: 'netdiskList' }"
+            ref="tree"
+            node-key="id"
+            @node-click="treeChange"
+            default-expand-all
+            :expand-on-click-node="false"
+          ></el-tree>
+        </div>
+      </el-col>
+      <el-col :span="18" style="padding: 0 5px">
+        <el-table
+          :data="sourceList.data"
+          style="width: 100%"
+          v-loading="loading"
+          :height="tableHeight"
+        >
+          <el-table-column prop="name" label="文件名" width="115" />
+          <el-table-column prop="createTime" label="上传时间" width="140" />
+          <!-- <el-table-column
+            label="操作"
+            width="60"
+            fixed="right"
+            align="center"
+            v-if="isShowBtn"
+          >
+            <template #default="{ row }">
+              <el-button type="primary" text @click="handleClickRow(row)"
+                ><i class="iconfont icon-iconm_wofqd"></i
+              ></el-button>
+            </template>
+          </el-table-column> -->
+        </el-table>
+
+        <el-pagination
+          style="margin-top: 10px"
+          v-model:current-page="sourceList.pagination.pageNum"
+          :page-size="10"
+          layout="total, prev, pager, next"
+          :total="sourceList.pagination.total"
+          prev-text="上一页"
+          next-text="下一页"
+          @current-change="handleCurrentChange"
+        />
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script setup>
+import useMailStore from "@/store/modules/mail";
+const mailStore = useMailStore();
+const isShowBtn = ref(false);
+mailStore.$subscribe((mutations, state) => {
+  if (state.currentId.includes("write")) {
+    isShowBtn.value = true;
+  } else {
+    isShowBtn.value = false;
+  }
+});
+const loading = ref(false);
+const sourceList = ref({
+  data: [],
+  pagination: {
+    total: 300,
+    pageNum: 1,
+    pageSize: 10,
+    keyword: "",
+  },
+});
+const treeListData = ref([]);
+const tableHeight = window.innerHeight - 250;
+const statusData = ref([
+  {
+    label: "草稿",
+    value: 0,
+  },
+  {
+    label: "审批中",
+    value: 10,
+  },
+  {
+    label: "驳回",
+    value: 20,
+  },
+  {
+    label: "通过",
+    value: 30,
+  },
+  {
+    label: "终止",
+    value: 99,
+  },
+]);
+const { proxy } = getCurrentInstance();
+const getList = (req) => {
+  loading.value = true;
+  proxy.post("/netdisk/page", sourceList.value.pagination).then((res) => {
+    sourceList.value.data = res.rows;
+    sourceList.value.pagination.total = res.total;
+    setTimeout(() => {
+      loading.value = false;
+    }, 200);
+  });
+};
+
+const handleCurrentChange = (val) => {
+  sourceList.value.pagination.pageNum = val;
+  getList();
+};
+
+const treeChange = (e) => {
+  sourceList.value.pagination.parentFolderId = e.id;
+  sourceList.value.pagination.pageNum = 1;
+  getList();
+};
+
+const getTreeList = () => {
+  proxy.get("/netdisk/tree", {}).then((res) => {
+    treeListData.value = res.data;
+    console.log(treeListData.value, "ada");
+  });
+};
+getTreeList();
+getList();
+
+const handleClickRow = (row) => {
+  console.log(row, "ada");
+};
+</script>
+
+<style lang="scss" scoped>
+* {
+  font-size: 12px;
+}
+.el-button {
+  padding: 0px;
+}
+.btn {
+  width: 100%;
+  border-radius: 10px;
+  padding: 6px 10px;
+  height: 24px;
+}
+.el-pagination {
+  padding-left: 30px;
+}
+:deep(.el-pagination button, .el-pager li) {
+  font-size: 12px;
+}
+:deep(.el-table .el-table__cell) {
+  padding: 2px 0px;
+}
+</style>
+<style lang="scss">
+.mulu {
+  width: 100%;
+  padding: 0 5px;
+  height: calc(100vh - 50px - 50px - 10px - 30px - 28px - 46px - 35px - 24px);
+  overflow-y: auto;
+  overflow-x: auto;
+  .el-tree {
+    .el-tree-node.is-expanded > .el-tree-node__children {
+      overflow: visible;
+    }
+    .el-tree-node > .el-tree-node__children {
+      overflow: visible;
+    }
+
+    .el-tree-node__content {
+      height: 26px !important;
+    }
+  }
+}
+</style>

+ 39 - 0
connect/E-mail/mail/com/right/netdisk/index.vue

@@ -0,0 +1,39 @@
+<template>
+  <div>
+    <!-- <el-tabs v-model="activeName" style="font-size: 12px">
+      <el-tab-pane label="企业网盘" name="1">
+        <Enterprise> </Enterprise>
+      </el-tab-pane>
+      <el-tab-pane label="个人网盘" name="2">
+        暂未开放
+      </el-tab-pane>
+    </el-tabs> -->
+
+    <el-radio-group
+      v-model="activeName"
+      size="mini"
+      style="margin: 5px 0px 15px"
+    >
+      <el-radio-button label="1">企业网盘</el-radio-button>
+      <el-radio-button label="2">个人网盘</el-radio-button>
+    </el-radio-group>
+    <div>
+      <Enterprise v-show="activeName === '1'"> </Enterprise>
+      <div v-show="activeName === '2'">暂未开放</div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import Enterprise from "./enterprise";
+// import Pi from "./pi";
+const activeName = ref("");
+activeName.value = "1";
+</script>
+
+<style lang="scss" scoped>
+:deep(.el-radio-button__inner) {
+  font-size: 13px;
+  padding: 6px 10px;
+}
+</style>

+ 150 - 0
connect/E-mail/mail/com/right/purchase/index.vue

@@ -0,0 +1,150 @@
+<template>
+  <div>
+    <div style="margin: 10px 0; display: flex; align-items: center">
+      <el-input
+        class="input_content"
+        placeholder="请输入内容"
+        v-model="sourceList.pagination.keyword"
+        style="width: 50%"
+        clearable
+        size="small"
+      ></el-input>
+      <el-select
+        v-model="sourceList.pagination.purchaseStatus"
+        clearable
+        filterable
+        style="width: 40%; margin-left: 10px"
+        size="small"
+      >
+        <el-option
+          v-for="(item, index) in statusData"
+          :key="index"
+          :value="item.value"
+          :label="item.label"
+        ></el-option>
+      </el-select>
+      <div style="text-align: center; width: 10%">
+        <el-icon style="cursor: pointer" @click="getData"><Search /></el-icon>
+      </div>
+    </div>
+    <el-table
+      :data="sourceList.data"
+      style="width: 100%; margin-top: 10px"
+      v-loading="loading"
+      :height="tableHeight"
+    >
+      <el-table-column prop="code" label="单号" width="100" fixed="left" />
+      <el-table-column prop="createTime" label="日期" width="150" />
+      <el-table-column prop="supplyName" label="供应商" min-width="150" />
+      <el-table-column prop="amount" label="采购总价" width="100">
+        <template #default="{ row }">
+          <div>{{ moneyFormat(row.amount, 2) }}</div>
+        </template>
+      </el-table-column>
+      <el-table-column
+        prop="purchaseStatus"
+        label="状态"
+        width="100"
+        :formatter="getStatus"
+      />
+      <!-- <el-table-column label="操作" width="60" fixed="right" align="center">
+        <template #default="{ row }">
+          <el-button type="primary" text
+            ><i class="iconfont icon-iconm_wofqd"></i
+          ></el-button>
+        </template>
+      </el-table-column> -->
+    </el-table>
+
+    <el-pagination
+      style="margin-top: 10px"
+      v-model:current-page="sourceList.pagination.pageNum"
+      :page-size="10"
+      layout="total, prev, pager, next"
+      :total="sourceList.pagination.total"
+      prev-text="上一页"
+      next-text="下一页"
+      @current-change="handleCurrentChange"
+    />
+  </div>
+</template>
+  
+<script setup>
+const tableHeight = window.innerHeight - 209;
+const loading = ref(false);
+const sourceList = ref({
+  data: [],
+  pagination: {
+    total: 300,
+    pageNum: 1,
+    pageSize: 10,
+  },
+});
+const statusData = ref([
+  {
+    label: "审批中",
+    value: "10",
+  },
+  {
+    label: "驳回",
+    value: "20",
+  },
+  {
+    label: "已采购",
+    value: "30",
+  },
+  {
+    label: "终止",
+    value: "99",
+  },
+  {
+    label: "已作废",
+    value: "88",
+  },
+]);
+const { proxy } = getCurrentInstance();
+const getStatus = (row) => {
+  const current = statusData.value.find((x) => x.value == row.purchaseStatus);
+  if (current) return current.label;
+};
+const getData = () => {
+  loading.value = true;
+  proxy.post("/purchase/page", sourceList.value.pagination).then((res) => {
+    sourceList.value.data = res.rows;
+    sourceList.value.pagination.total = res.total;
+    setTimeout(() => {
+      loading.value = false;
+    }, 200);
+  });
+};
+const handleCurrentChange = (val) => {
+  sourceList.value.pagination.pageNum = val;
+  getData();
+};
+getData();
+onMounted(() => {});
+</script>
+  
+<style lang="scss" scoped>
+* {
+  font-size: 12px;
+}
+.el-button {
+  padding: 0px;
+}
+.btn {
+  width: 100%;
+  border-radius: 10px;
+  padding: 6px 10px;
+  height: 24px;
+}
+.el-pagination {
+  padding-left: 30px;
+}
+:deep(.el-pagination button, .el-pager li) {
+  font-size: 12px;
+}
+:deep(.el-table .el-table__cell) {
+  padding: 2px 0px;
+}
+</style>

+ 133 - 0
connect/E-mail/mail/index.vue

@@ -0,0 +1,133 @@
+<template>
+  <div>
+    <div class="box">
+      <div class="left">
+        <mailLeft ref="leftDom"></mailLeft>
+      </div>
+      <div class="right">
+        <mailMain @changeFloderId="handleChangeFloderId"></mailMain>
+      </div>
+    </div>
+    <div>
+      <!-- 右边弹窗 -->
+      <div :class="mailCon">
+        <div class="changeBox" @click="handleClose()">
+          <span
+            v-if="showRight"
+            class="iconfont icon-icomx_rightt1"
+            style="color: #fff; font-size: 20px"
+          ></span>
+          <span
+            v-if="!showRight"
+            class="iconfont icon-iconm_leftt1"
+            style="color: #fff; font-size: 20px"
+          ></span>
+        </div>
+        <mailRight v-show="showRight" />
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import mailLeft from "./com/left.vue";
+import mailMain from "./com/main.vue";
+import mailRight from "./com/right.vue";
+const mailCon = ref("mail-right-con");
+const showRight = ref(true);
+const handleClose = () => {
+  if (showRight.value) {
+    mailCon.value = "mail-right-con move-to-close";
+  } else {
+    mailCon.value = "mail-right-con";
+  }
+  showRight.value = !showRight.value;
+};
+const route = useRoute();
+const leftDom = ref(null);
+
+const handleChangeFloderId = (val) => {
+  leftDom.value.changeFloderId(val);
+};
+
+onMounted(() => {
+  if (route.query.mail) {
+    leftDom.value.handleGoWrite(route.query.mail);
+  }
+});
+</script>
+
+<style lang="scss" scoped>
+.box {
+  width: calc(100vw - 0px);
+  height: calc(100vh - 50px - 50px);
+  padding: 10px;
+  display: flex;
+  position: relative;
+
+  .left,
+  .right {
+    background: #fff;
+  }
+  .left {
+    width: 300px;
+    margin-right: 10px;
+    box-sizing: border-box;
+  }
+  .right {
+    flex: 1;
+  }
+  .mail-right {
+    position: absolute;
+    right: 0px;
+  }
+}
+
+.mail-right-con {
+  .changeBox {
+    width: 40px;
+    height: 40px;
+    position: absolute;
+    text-align: center;
+    line-height: 40px;
+    left: -40px;
+    top: 5px;
+    background: #ff9315;
+    border-radius: 10px 0px 0px 10px;
+    cursor: pointer;
+  }
+  // border-radius: 5px;
+  border-top-left-radius: 5px;
+  border-bottom-left-radius: 5px;
+  z-index: 1000;
+  position: fixed;
+  margin-top: 50px;
+  top: 0;
+  width: 400px;
+  transition: all cubic-bezier(0.05, 0.91, 0.22, 1.03) 0.5s;
+  padding: 10px 15px 0px 15px;
+  height: calc(100vh - 50px - 10px);
+  background: white;
+  left: 100%;
+  margin-left: -398px;
+  box-shadow: var(--devui-shadow-fullscreen-overlay, 0 10px 20px 0)
+    var(--devui-shadow, rgba(0, 0, 0, 0.08));
+  i {
+    cursor: pointer;
+    transition: all cubic-bezier(0, 0.88, 0, 1.29) 1s;
+    font-size: 15px;
+    position: relative;
+    &:hover {
+      color: #169bd5;
+      transform: rotate(360deg);
+    }
+  }
+}
+.move-to-close {
+  transform: translateX(400px);
+  .mail-right {
+    transition: all linear 0.3s;
+    padding-left: 20px;
+  }
+}
+</style>

+ 368 - 0
connect/E-mail/personalConfig/index.vue

@@ -0,0 +1,368 @@
+<template>
+  <div class="tenant">
+    <!-- <Banner /> -->
+    <div class="content">
+      <byTable
+        :source="sourceList.data"
+        :pagination="sourceList.pagination"
+        :config="config"
+        :loading="loading"
+        highlight-current-row
+        :selectConfig="selectConfig"
+        :table-events="{
+          //element talbe事件都能传
+          select: select,
+        }"
+        :action-list="[
+          {
+            text: '添加邮箱',
+            action: () => openModal('add'),
+          },
+        ]"
+        @get-list="getList"
+      >
+        <template #slotName="{ item }">
+          {{ item.createTime }}
+        </template>
+      </byTable>
+    </div>
+    <el-dialog
+      :title="modalType == 'add' ? '添加邮箱' : '编辑邮箱'"
+      v-model="dialogVisible"
+      width="400"
+      v-loading="loading"
+    >
+      <byForm
+        :formConfig="formConfig"
+        :formOption="formOption"
+        v-model="formData.data"
+        :rules="rules"
+        ref="byform"
+      >
+        <template #rSlot>
+          <el-row style="width: 100%" :gutter="15">
+            <el-col :span="10">
+              <el-select
+                v-model="formData.data.receiveProtocol"
+                placeholder="收件协议"
+                disabled
+              >
+              </el-select>
+            </el-col>
+            <el-col :span="10">
+              <el-input
+                v-model="formData.data.receivePort"
+                placeholder="请输入"
+              >
+              </el-input>
+            </el-col>
+          </el-row>
+        </template>
+        <template #sSlot>
+          <el-row style="width: 100%" :gutter="15">
+            <el-col :span="10">
+              <el-select
+                v-model="formData.data.sendProtocol"
+                placeholder="发件协议"
+                disabled
+              >
+              </el-select>
+            </el-col>
+            <el-col :span="10">
+              <el-input v-model="formData.data.sendPort" placeholder="请输入">
+              </el-input>
+            </el-col>
+          </el-row>
+        </template>
+      </byForm>
+      <template #footer>
+        <el-button @click="dialogVisible = false" size="large">取 消</el-button>
+        <el-button
+          type="primary"
+          @click="submitForm('byform')"
+          size="large"
+          :loading="submitLoading"
+        >
+          确 定
+        </el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+  
+<script setup>
+/* eslint-disable vue/no-unused-components */
+import { ElMessage, ElMessageBox } from "element-plus";
+import byTable from "@/components/byTable/index";
+import byForm from "@/components/byForm/index";
+import { computed, defineComponent, ref } from "vue";
+import { validEmail } from "@/utils/validate.js";
+
+const loading = ref(false);
+const submitLoading = ref(false);
+const sourceList = ref({
+  data: [],
+  pagination: {
+    total: 3,
+    pageNum: 1,
+    pageSize: 10,
+  },
+});
+let dialogVisible = ref(false);
+let modalType = ref("add");
+let rules = ref({
+  mailUser: [{ required: true, message: "请输入邮箱地址", trigger: "blur" }],
+  type: [{ required: true, message: "请选择邮箱类型", trigger: "change" }],
+  mailPassword: [{ required: true, message: "请输入授权码", trigger: "blur" }],
+  receiveHost: [
+    { required: true, message: "请输入收件服务器地址", trigger: "blur" },
+  ],
+  receivePort: [{ required: true, message: "请输入收件端口", trigger: "blur" }],
+  receiveProtocol: [
+    { required: true, message: "请输入收件协议", trigger: "blur" },
+  ],
+  sendHost: [
+    { required: true, message: "请输入发件服务器地址", trigger: "blur" },
+  ],
+  sendPort: [{ required: true, message: "请输入发件端口", trigger: "blur" }],
+  sendProtocol: [
+    { required: true, message: "请输入发件协议", trigger: "blur" },
+  ],
+});
+const { proxy } = getCurrentInstance();
+const selectConfig = [];
+const config = computed(() => {
+  return [
+    {
+      attrs: {
+        label: "邮箱地址",
+        prop: "mailUser",
+      },
+    },
+    {
+      attrs: {
+        label: "海外邮箱",
+        prop: "type",
+      },
+      render(type) {
+        return type == 2 ? "是" : "否";
+      },
+    },
+    {
+      attrs: {
+        label: "授权码",
+        prop: "mailPassword",
+      },
+    },
+    {
+      attrs: {
+        label: "收件协议",
+        prop: "receiveProtocol",
+      },
+    },
+    {
+      attrs: {
+        label: "收件端口号",
+        prop: "receivePort",
+      },
+    },
+    {
+      attrs: {
+        label: "发件协议",
+        prop: "sendProtocol",
+      },
+    },
+    {
+      attrs: {
+        label: "发件端口号",
+        prop: "sendPort",
+      },
+    },
+
+    {
+      attrs: {
+        label: "操作",
+        width: "200",
+        align: "right",
+      },
+      // 渲染 el-button,一般用在最后一列。
+      renderHTML(row) {
+        return [
+          {
+            attrs: {
+              label: "修改",
+              type: "primary",
+              text: true,
+            },
+            el: "button",
+            click() {
+              getDtl(row);
+            },
+          },
+          {
+            attrs: {
+              label: "删除",
+              type: "danger",
+              text: true,
+            },
+            el: "button",
+            click() {
+              // 弹窗提示是否删除
+              ElMessageBox.confirm(
+                "此操作将永久删除该数据, 是否继续?",
+                "提示",
+                {
+                  confirmButtonText: "确定",
+                  cancelButtonText: "取消",
+                  type: "warning",
+                }
+              ).then(() => {
+                // 删除
+                proxy
+                  .post("/personalMailbox/delete", {
+                    id: row.id,
+                  })
+                  .then((res) => {
+                    ElMessage({
+                      message: "删除成功",
+                      type: "success",
+                    });
+                    getList();
+                  });
+              });
+            },
+          },
+        ];
+      },
+    },
+  ];
+});
+
+let formData = reactive({
+  data: {},
+});
+const formOption = reactive({
+  inline: true,
+  labelWidth: 100,
+  itemWidth: 100,
+  rules: [],
+});
+const byform = ref(null);
+const formConfig = computed(() => {
+  return [
+    {
+      type: "input",
+      prop: "mailUser",
+      label: "邮箱地址",
+      required: true,
+    },
+    {
+      type: "radio",
+      prop: "type",
+      label: "是否海外邮箱",
+      required: true,
+      border: true,
+      data: [
+        { label: "是", value: "2" },
+        { label: "否", value: "1" },
+      ],
+    },
+    {
+      type: "input",
+      prop: "mailPassword",
+      label: "授权码",
+      required: true,
+    },
+    {
+      type: "input",
+      prop: "receiveHost",
+      label: "收件服务器地址",
+      required: true,
+    },
+    {
+      type: "slot",
+      slotName: "rSlot",
+      prop: "receivePort",
+      label: "收件协议",
+      required: true,
+    },
+    {
+      type: "input",
+      prop: "sendHost",
+      label: "发件服务器地址",
+      required: true,
+    },
+    {
+      type: "slot",
+      slotName: "sSlot",
+      prop: "sendPort",
+      label: "发件协议",
+      required: true,
+    },
+  ];
+});
+const getList = async (req) => {
+  sourceList.value.pagination = { ...sourceList.value.pagination, ...req };
+  loading.value = true;
+  proxy
+    .post("/personalMailbox/page", sourceList.value.pagination)
+    .then((message) => {
+      console.log(message);
+      sourceList.value.data = message.rows;
+      sourceList.value.pagination.total = message.total;
+      setTimeout(() => {
+        loading.value = false;
+      }, 200);
+    });
+};
+const openModal = () => {
+  modalType.value = "add";
+  formData.data = {};
+  formData.data.type = "1";
+  formData.data.receiveProtocol = "IMAP";
+  formData.data.sendProtocol = "SMTP";
+  dialogVisible.value = true;
+};
+
+const submitForm = () => {
+  byform.value.handleSubmit((valid) => {
+    if (validEmail(formData.data.mailUser)) {
+      submitLoading.value = true;
+      proxy.post("/personalMailbox/" + modalType.value, formData.data).then(
+        (res) => {
+          ElMessage({
+            message: modalType.value == "add" ? "添加成功" : "编辑成功",
+            type: "success",
+          });
+          dialogVisible.value = false;
+          submitLoading.value = false;
+          getList();
+        },
+        (err) => (submitLoading.value = false)
+      );
+    } else {
+      ElMessage({
+        message: "邮箱格式不正确!",
+        type: "info",
+      });
+    }
+  });
+};
+
+const getDtl = (row) => {
+  modalType.value = "edit";
+  proxy.post("/personalMailbox/detail", { id: row.id }).then((res) => {
+    res.type = res.type + "";
+    formData.data = res;
+    dialogVisible.value = true;
+  });
+};
+
+getList();
+</script>
+  
+<style lang="scss" scoped>
+.tenant {
+  padding: 20px;
+}
+</style>