mailDetail.vue 15 KB

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