mailDetail.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664
  1. <template>
  2. <div style="display:flex" ref="outerDom" v-loading="loading">
  3. <div style="overflow:auto" :style="{width:boxWidth+'px',height:boxHeight+'px'}">
  4. <div class="box">
  5. <div style="
  6. margin-bottom: 10px;
  7. display: flex;
  8. align-items: center;
  9. flex-wrap: wrap;
  10. ">
  11. <el-button :disabled="currentMailIndex === 0" @click="handleChangeEail('10')">上一封</el-button>
  12. <el-button :disabled="currentMailIndex === allLength - 1" @click="handleChangeEail('20')">下一封</el-button>
  13. <el-button type="primary" @click="handleReply('10')">回复</el-button>
  14. <el-button type="primary" @click="handleReply('30')">全部回复</el-button>
  15. <el-button type="warning" @click="handleReply('20')">转发</el-button>
  16. <el-button @click="handleReply('40')">再次编辑</el-button>
  17. <el-button @click="handleMove">移动</el-button>
  18. <el-button @click="handleRemove">删除</el-button>
  19. <div style="
  20. background-color: #eff6ff;
  21. padding: 6px;
  22. margin-left: 10px;
  23. cursor: pointer;
  24. border-radius: 2px 2px 2px 2px;
  25. " @click="handleAddTag">
  26. <span class="iconfont icon-icon_biaoqian2" style="color: #0084ff; font-size: 20px"></span>
  27. </div>
  28. <!-- <el-tag
  29. style="margin-left: 8px; cursor: pointer"
  30. type="success"
  31. @click="handleAddTag"
  32. >
  33. +
  34. </el-tag> -->
  35. </div>
  36. <div class="top">
  37. <div class="top-row">
  38. <div class="subject">
  39. {{ subject }}
  40. </div>
  41. </div>
  42. <div class="top-row" v-if="mailTagList && mailTagList.length > 0">
  43. <el-tag style="margin-left: 8px" type="success" closable v-for="(tag, index) in mailTagList" :key="index" @close="tagClose(tag)">
  44. {{ tag.name }}
  45. </el-tag>
  46. </div>
  47. <div class="top-row">
  48. <div class="label">发 件 人:</div>
  49. <div class="value">
  50. <span v-for="item in replyTo" :key="item.id" style="margin-right: 10px">
  51. {{ item.email || item.personalName }}</span>
  52. </div>
  53. </div>
  54. <div class="top-row">
  55. <div class="label">收 件 人:</div>
  56. <div class="value">
  57. <span v-for="item in to" :key="item.id" style="margin-right: 10px">{{
  58. item.email || item.personalName
  59. }}</span>
  60. </div>
  61. </div>
  62. <div class="top-row" v-if="cc && cc.length > 0">
  63. <div class="label">抄 送 人:</div>
  64. <div class="value">
  65. <span v-for="item in cc" :key="item.id" style="margin-right: 10px">
  66. {{ item.email || item.personalName }}</span>
  67. </div>
  68. </div>
  69. <div class="top-row" v-if="bcc && bcc.length > 0">
  70. <div class="label">密 送 人:</div>
  71. <div class="value">
  72. <span v-for="item in bcc" :key="item.id" style="margin-right: 10px">
  73. {{ item.email || item.personalName }}</span>
  74. </div>
  75. </div>
  76. <div class="top-row">
  77. <div class="label">时 间:</div>
  78. <div class="value">
  79. {{ time }}
  80. </div>
  81. </div>
  82. <div class="top-row" v-if="fileList && fileList.length > 0">
  83. <div class="label">附 件:</div>
  84. <div class="value">
  85. <div v-for="(item, index) in fileList" style="margin-right: 20px; display: flex; align-items: center" :key="index">
  86. <span style="cursor: pointer" @click="handleClickFile(item)">{{
  87. item.name
  88. }}</span>
  89. <span style="margin-left: 8px; cursor: pointer" @click="handleClickDownload(item)"><el-icon color="#0084FF">
  90. <Download />
  91. </el-icon></span>
  92. </div>
  93. </div>
  94. </div>
  95. </div>
  96. <div class="body">
  97. <iframe frameborder="0" allowTransparency="true" style="
  98. width: 99% !important;
  99. overflow-x: scroll;
  100. padding-top: 10px;
  101. " :srcdoc="showBodyText(detailsData.data.content)" :ref="'iframeText_' + mailStore.currentId" :id="'iframeText_' + mailStore.currentId">
  102. </iframe>
  103. </div>
  104. </div>
  105. </div>
  106. <div style="width:210px;margin-left:10px;overflow:auto" :style="{height:boxHeight+'px'}" v-if="isShowCommunication">
  107. <el-tabs v-model="activeName" class="demo-tabs" type="card" @tab-click="handleClick">
  108. <el-tab-pane label="往来附件" name="first"></el-tab-pane>
  109. <el-tab-pane label="往来邮件" name="second"></el-tab-pane>
  110. </el-tabs>
  111. <div class="communication">
  112. <div class="list" v-show="activeName=='first'">
  113. <div class="item" v-for="(item,index) in communicationAtt" :key="item.id" @click="handleRowClick(item)">
  114. <div>
  115. 发件时间: {{item.sendDate}}
  116. </div>
  117. <div style="margin:5px 0">
  118. 主题: {{item.subject}}
  119. </div>
  120. <div>
  121. 附件: aa.png
  122. </div>
  123. </div>
  124. </div>
  125. <div class="list" v-show="activeName=='second'">
  126. <div class="item" v-for="(item,index) in communicationMail" :key="item.id" @click="handleRowClick(item)">
  127. <div>
  128. 发件时间: {{item.sendDate}}
  129. </div>
  130. <div style="margin-top:5px">
  131. 主题: {{item.subject}}
  132. </div>
  133. </div>
  134. </div>
  135. </div>
  136. </div>
  137. <el-dialog title="移动邮件" v-model="myFolderDialog" width="300px" destroy-on-close v-loading="submitLoading">
  138. <byForm :formConfig="myFolderFormConfig" :formOption="formOption" v-model="formData.myFolderData" :rules="rules" ref="myFolderForm">
  139. </byForm>
  140. <template #footer>
  141. <el-button @click="myFolderDialog = false" size="large">取 消</el-button>
  142. <el-button type="primary" @click="submitMyFolderForm()" size="large" :loading="submitLoading">
  143. 确 定
  144. </el-button>
  145. </template>
  146. </el-dialog>
  147. <el-dialog title="添加标签" v-model="tagDialog" width="300px" destroy-on-close v-loading="submitLoading">
  148. <byForm :formConfig="tagFormConfig" :formOption="formOption" v-model="formData.tagData" :rules="rules" ref="tagForm">
  149. </byForm>
  150. <template #footer>
  151. <el-button @click="tagDialog = false" size="large">取 消</el-button>
  152. <el-button type="primary" @click="submitTagForm()" size="large" :loading="submitLoading">
  153. 确 定
  154. </el-button>
  155. </template>
  156. </el-dialog>
  157. </div>
  158. </template>
  159. <script setup>
  160. import useMailStore from "@/store/modules/mail";
  161. import useUserStore from "@/store/modules/user";
  162. import byForm from "@/components/byForm/index";
  163. import { ElMessage, ElMessageBox } from "element-plus";
  164. import { nextTick } from "vue";
  165. const { proxy } = getCurrentInstance();
  166. const outerDom = ref(null);
  167. const boxHeight = ref(0);
  168. const boxWidth = ref(700);
  169. const getTableHeight = () => {
  170. boxHeight.value = window.innerHeight - 180;
  171. };
  172. getTableHeight();
  173. window.addEventListener("resize", () => {
  174. getTableHeight();
  175. });
  176. const isShowCommunication = ref(true);
  177. const mailStore = useMailStore();
  178. let loading = ref(false);
  179. const detailsData = reactive({
  180. data: {},
  181. });
  182. const currentMailIndex = computed(() => mailStore.currentMailIndex);
  183. const allLength = computed(() => mailStore.mailDataList.length);
  184. const to = ref([]);
  185. const cc = ref([]);
  186. const bcc = ref([]);
  187. const replyTo = ref([]);
  188. const fileList = ref([]);
  189. const requestData = ref({});
  190. const time = ref("");
  191. const subject = ref("");
  192. // 移动文件夹
  193. const formOption = reactive({
  194. inline: true,
  195. labelWidth: 100,
  196. itemWidth: 100,
  197. });
  198. const myFolderForm = ref(null);
  199. const myFolderTreeData = ref([]);
  200. const myFolderDialog = ref(false);
  201. const submitLoading = ref(false);
  202. const editType = ref("add");
  203. const myFolderFormConfig = computed(() => [
  204. {
  205. type: "treeSelect",
  206. prop: "myFolderId",
  207. label: "文件夹",
  208. disabled: false,
  209. itemWidth: 100,
  210. data: myFolderTreeData.value,
  211. style: {
  212. width: "100%",
  213. },
  214. },
  215. ]);
  216. const formData = reactive({
  217. myFolderData: {},
  218. tagData: {},
  219. });
  220. const rules = ref({
  221. myFolderId: [{ required: true, message: "请选择文件夹", trigger: "change" }],
  222. myTagId: [{ required: true, message: "请选择标签", trigger: "change" }],
  223. });
  224. const allTagList = ref([]);
  225. const mailTagList = ref([]);
  226. const tagForm = ref(null);
  227. const tagDialog = ref(false);
  228. const tagFormConfig = computed(() => [
  229. {
  230. type: "select",
  231. prop: "myTagId",
  232. label: "标签名称",
  233. disabled: false,
  234. itemWidth: 100,
  235. data: allTagList.value,
  236. style: {
  237. width: "100%",
  238. },
  239. },
  240. ]);
  241. const getMyFolderTree = (flag) => {
  242. if (flag) {
  243. setTimeout(() => {
  244. proxy
  245. .post("/myFolder/tree", { mailboxId: mailStore.selectMail.id })
  246. .then((res) => {
  247. myFolderTreeData.value = res.map((x) => ({ ...x, value: x.id }));
  248. });
  249. }, 1500);
  250. } else {
  251. proxy
  252. .post("/myFolder/tree", { mailboxId: mailStore.selectMail.id })
  253. .then((res) => {
  254. myFolderTreeData.value = res.map((x) => ({ ...x, value: x.id }));
  255. });
  256. }
  257. };
  258. const handleMove = () => {
  259. formData.myFolderData.messageId = mailStore.currentMenu.messageId;
  260. myFolderDialog.value = true;
  261. };
  262. const handleRemove = () => {
  263. ElMessageBox.confirm(`此操作将删除该邮件, 是否继续?`, "提示", {
  264. confirmButtonText: "确定",
  265. cancelButtonText: "取消",
  266. type: "warning",
  267. }).then(() => {
  268. // 删除
  269. proxy
  270. .post("/mailService/deleteMail", {
  271. id: mailStore.currentMenu.messageId,
  272. type: mailStore.currentMenu.type,
  273. })
  274. .then((res) => {
  275. ElMessage({
  276. message: `操作成功!`,
  277. type: "success",
  278. });
  279. });
  280. });
  281. };
  282. const submitMyFolderForm = () => {
  283. myFolderForm.value.handleSubmit(() => {
  284. submitLoading.value = true;
  285. proxy.post("/myFolderMessage/add", formData.myFolderData).then(
  286. (res) => {
  287. ElMessage({
  288. message: `操作成功!`,
  289. type: "success",
  290. });
  291. myFolderDialog.value = false;
  292. submitLoading.value = false;
  293. getMyFolderTree(false);
  294. },
  295. (err) => {
  296. submitLoading.value = false;
  297. }
  298. );
  299. });
  300. };
  301. const getAllTagData = () => {
  302. proxy
  303. .post("/myTag/page", {
  304. pageNum: 1,
  305. pageSize: 9999,
  306. id: useUserStore().user.userId,
  307. })
  308. .then((res) => {
  309. allTagList.value = res.rows;
  310. });
  311. };
  312. const getMailTag = () => {
  313. if (mailStore.currentMenu.messageId) {
  314. proxy
  315. .post("/myTag/getListByMessageId", {
  316. id: mailStore.currentMenu.messageId,
  317. })
  318. .then((res) => {
  319. mailTagList.value = res;
  320. });
  321. }
  322. };
  323. const tagClose = (tag) => {
  324. proxy
  325. .post("/myTagMessage/deleteTag", {
  326. myTagId: tag.id,
  327. messageId: mailStore.currentMenu.messageId,
  328. })
  329. .then(() => {
  330. ElMessage({
  331. message: "操作成功",
  332. type: "success",
  333. });
  334. getMailTag();
  335. });
  336. };
  337. const handleAddTag = () => {
  338. formData.tagData.messageId = mailStore.currentMenu.messageId;
  339. formData.tagData.myTagId = "";
  340. tagDialog.value = true;
  341. };
  342. const submitTagForm = () => {
  343. tagForm.value.handleSubmit(() => {
  344. submitLoading.value = true;
  345. proxy.post("/myTagMessage/add", formData.tagData).then(
  346. (res) => {
  347. ElMessage({
  348. message: `操作成功!`,
  349. type: "success",
  350. });
  351. tagDialog.value = false;
  352. submitLoading.value = false;
  353. getMailTag();
  354. },
  355. (err) => {
  356. submitLoading.value = false;
  357. }
  358. );
  359. });
  360. };
  361. const getDetails = (messageId) => {
  362. loading.value = true;
  363. proxy
  364. .post("/mailService/getMessageDetail", {
  365. type: mailStore.currentMenu.type,
  366. messageId,
  367. })
  368. .then((res) => {
  369. if (mailStore.currentMenu.time) {
  370. time.value = mailStore.currentMenu.time;
  371. }
  372. if (mailStore.currentMenu.subject) {
  373. subject.value = mailStore.currentMenu.subject;
  374. }
  375. if (mailStore.currentMenu.fromEmail) {
  376. // 如果发件人是自己的话就不显示往来邮件
  377. let flag =
  378. mailStore.currentMenu.fromEmail == mailStore.selectMail.mailUser;
  379. if (flag) {
  380. isShowCommunication.value = false;
  381. nextTick(() => {
  382. boxWidth.value = outerDom.value.offsetWidth;
  383. });
  384. } else {
  385. getCommunicationData();
  386. nextTick(() => {
  387. boxWidth.value = outerDom.value.offsetWidth - 220;
  388. });
  389. }
  390. }
  391. detailsData.data = res;
  392. to.value = res.messageAddressList.filter((x) => x.type === 1);
  393. cc.value = res.messageAddressList.filter((x) => x.type === 2);
  394. bcc.value = res.messageAddressList.filter((x) => x.type === 3);
  395. replyTo.value = res.messageAddressList.filter((x) => x.type === 4);
  396. fileList.value = res.messageAttachmentList;
  397. loading.value = false;
  398. });
  399. };
  400. const showBodyText = (text) => {
  401. if (text) {
  402. let val = JSON.parse(JSON.stringify(text));
  403. val = val.replace("body {", "tbody {");
  404. val = val.replace(
  405. "td, p, li, th {",
  406. "tbody td, tbody p, tbody li, tbody th {"
  407. );
  408. val = val.replace(
  409. /<p>/g,
  410. '<p style="margin-block-start: 0; margin-block-end: 0;">'
  411. );
  412. return val;
  413. } else {
  414. return text;
  415. }
  416. };
  417. const handleReply = (pageType) => {
  418. // pageType 10为回复 20为转发 30为全部回复 40为再次编辑 0为写信
  419. let title = "";
  420. if (pageType === "10") {
  421. title = "回复";
  422. } else if (pageType === "20") {
  423. title = "转发";
  424. } else if (pageType === "30") {
  425. title = "全部回复";
  426. } else if (pageType === "40") {
  427. title = "再次编辑";
  428. }
  429. const menu = {
  430. title: title,
  431. type: mailStore.currentMenu.type,
  432. id: "write",
  433. pageType: pageType,
  434. details: {
  435. ...detailsData.data,
  436. ...mailStore.currentMenu.row,
  437. },
  438. };
  439. const index = mailStore.mailMenuList.findIndex((x) => x.id === menu.id);
  440. if (index >= 0) {
  441. mailStore.mailMenuList[index] = menu;
  442. } else {
  443. mailStore.mailMenuList.push(menu);
  444. }
  445. mailStore.currentMenu = menu;
  446. mailStore.currentId = menu.id;
  447. };
  448. const handleChangeEail = (type) => {
  449. if (type === "10") {
  450. mailStore.currentMailIndex = currentMailIndex.value - 1;
  451. } else if (type === "20") {
  452. mailStore.currentMailIndex = currentMailIndex.value + 1;
  453. }
  454. // 拿到更变索引之后的行数据
  455. const row = mailStore.mailDataList[mailStore.currentMailIndex];
  456. // 查当前菜单的数据
  457. const arr = mailStore.currentMenu.id.split(",");
  458. if (arr.length > 1) {
  459. const index = mailStore.mailMenuList.findIndex(
  460. (x) => x.messageId === arr[1]
  461. );
  462. const menu = {
  463. title: row.subject.slice(0, 4) + "...",
  464. type: mailStore.selectMail.type,
  465. messageId: row.id,
  466. id: "detail" + "," + row.id,
  467. time: row.sendDate,
  468. row: { ...row },
  469. };
  470. const menuItem = mailStore.mailMenuList.find((x) => x.id === menu.id);
  471. if (menuItem === undefined) {
  472. mailStore.mailMenuList[index] = menu;
  473. }
  474. mailStore.currentMenu = menu;
  475. mailStore.currentId = menu.id;
  476. }
  477. };
  478. const handleClickFile = (file) => {
  479. const path = file.path ? file.path : file.url ? file.url : "";
  480. if (path) {
  481. window.open(path, "_blank");
  482. }
  483. };
  484. const handleClickDownload = (file) => {
  485. let xhr = new XMLHttpRequest();
  486. //域名是华为云的
  487. xhr.open("GET", `${file.url}`, true);
  488. xhr.responseType = "blob";
  489. xhr.send();
  490. xhr.onreadystatechange = function () {
  491. if (xhr.readyState === 4 && xhr.status === 200) {
  492. let url = window.URL.createObjectURL(xhr.response);
  493. const a = document.createElement("a");
  494. a.href = url;
  495. a.download = file.name; // 下载后文件名
  496. a.style.display = "none";
  497. document.body.appendChild(a);
  498. a.click(); // 点击下载
  499. window.URL.revokeObjectURL(a.href);
  500. document.body.removeChild(a); // 下载完成移除元素
  501. }
  502. };
  503. };
  504. onMounted(() => {
  505. // getMyFolderTree(true);
  506. // getAllTagData();
  507. // iframe动态高度
  508. const iframe = proxy.$refs["iframeText_" + mailStore.currentId];
  509. iframe.addEventListener("load", () => {
  510. iframe.style.height =
  511. iframe.contentWindow.document.body.scrollHeight + 30 + "px";
  512. });
  513. });
  514. const communicationMail = ref([]);
  515. const communicationAtt = ref([]);
  516. const getCommunicationData = () => {
  517. let data = {
  518. folderId: "",
  519. type: mailStore.selectMail.type,
  520. mailboxId: mailStore.selectMail.id,
  521. dealingsEmail: mailStore.currentMenu.fromEmail,
  522. };
  523. proxy.post("/mailService/getMessagePage", data).then((res) => {
  524. communicationMail.value = res.rows.filter((x) => !x.isAttachments);
  525. communicationAtt.value = res.rows.filter((x) => x.isAttachments == 1);
  526. });
  527. };
  528. const handleRowClick = (row) => {
  529. const menu = {
  530. title: row.subject.slice(0, 4) + "...",
  531. type: mailStore.selectMail.type,
  532. messageId: row.id,
  533. id: "detail" + "," + row.id,
  534. time: row.sendDate,
  535. subject: row.subject,
  536. fromEmail: row.fromEmail,
  537. row: { ...row },
  538. };
  539. const menuItem = mailStore.mailMenuList.find((x) => x.id === menu.id);
  540. if (menuItem === undefined) {
  541. mailStore.mailMenuList.push(menu);
  542. }
  543. mailStore.currentMenu = menu;
  544. mailStore.currentId = menu.id;
  545. };
  546. const init = () => {
  547. //实时更换索引
  548. if (mailStore.currentMenu.messageId) {
  549. mailStore.currentMailIndex = mailStore.mailDataList.findIndex(
  550. (x) => x.id === mailStore.currentMenu.messageId
  551. );
  552. }
  553. getDetails(mailStore.currentMenu.messageId);
  554. getMailTag();
  555. getMyFolderTree(true);
  556. getAllTagData();
  557. };
  558. const activeName = ref("first");
  559. defineExpose({
  560. initFn: init,
  561. });
  562. </script>
  563. <style lang="scss" scoped>
  564. .box {
  565. padding: 0 10px;
  566. font-size: 12px;
  567. .top {
  568. padding: 10px;
  569. background: #eeeeee;
  570. .top-row {
  571. display: flex;
  572. margin-bottom: 10px;
  573. .subject {
  574. font-size: 14px;
  575. font-weight: bold;
  576. }
  577. .label {
  578. min-width: 60px;
  579. color: #999999;
  580. text-align: right;
  581. }
  582. .value {
  583. flex: 1;
  584. color: #333333;
  585. font-weight: 700;
  586. margin-right: 10px;
  587. display: flex;
  588. flex-wrap: wrap;
  589. }
  590. }
  591. }
  592. }
  593. .el-tag {
  594. background: #ffffff;
  595. color: #0084ff;
  596. }
  597. .el-tag.el-tag--success {
  598. color: #0084ff;
  599. }
  600. :deep(.el-tabs__item) {
  601. font-size: 12px;
  602. line-height: 30px;
  603. height: 30px;
  604. font-weight: normal;
  605. }
  606. :deep(.el-tabs__header) {
  607. margin: 8px 0;
  608. height: 32px;
  609. }
  610. </style>
  611. <style lang="scss">
  612. .top-row {
  613. .el-tag .el-tag__close {
  614. color: #0084ff !important;
  615. }
  616. .el-tag .el-tag__close:hover {
  617. color: #fff !important;
  618. background: #0084ff !important ;
  619. }
  620. }
  621. .communication {
  622. font-size: 12px;
  623. padding: 0 10px;
  624. .list {
  625. display: flex;
  626. flex-direction: column;
  627. .item {
  628. border-bottom: 1px solid #eee;
  629. margin-bottom: 10px;
  630. cursor: pointer;
  631. padding-bottom: 10px;
  632. }
  633. }
  634. }
  635. </style>