mailWrite.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640
  1. <template>
  2. <div v-loading="loading" style="padding: 10px">
  3. <!-- <div style="margin-bottom: 10px; padding-left: 65px">
  4. <el-button type="primary" @click="handleSend">发 送</el-button>
  5. </div> -->
  6. <div style="width: 100%">
  7. <el-form
  8. ref="submit"
  9. :model="formData.data"
  10. :rules="rules"
  11. label-width="65px"
  12. labelPosition="right"
  13. >
  14. <el-form-item>
  15. <el-button type="primary" @click="handleSend()"> 发 送 </el-button>
  16. </el-form-item>
  17. <el-form-item label="收件人" prop="to">
  18. <div style="width: 100%">
  19. <div style="display: flex; width: 100%">
  20. <!-- <el-autocomplete
  21. v-model="formData.data.to"
  22. clearable
  23. placeholder="请输入"
  24. style="width: 100%"
  25. @select="handlePerson"
  26. :fetch-suggestions="querySearchPerson"
  27. @keyup.enter.native="handleAdd(10)"
  28. >
  29. </el-autocomplete> -->
  30. <el-input
  31. v-model="formData.data.to"
  32. placeholder="请输入"
  33. @keyup.enter.native="handleAdd(10)"
  34. />
  35. <!-- <el-button
  36. type="primary"
  37. @click="handleAdd(10)"
  38. style="margin-left: 10px"
  39. :disabled="!formData.data.to"
  40. >添加</el-button
  41. > -->
  42. </div>
  43. <div style="margin-top: 15px" v-if="to && to.length > 0">
  44. <el-tag
  45. style="margin-right: 10px"
  46. class="ml-2"
  47. type="info"
  48. v-for="(item, index) in to"
  49. :key="index"
  50. closable
  51. @close="handleClose(index, 10)"
  52. >{{ item.address }}</el-tag
  53. >
  54. </div>
  55. </div>
  56. </el-form-item>
  57. <el-form-item label="" prop="">
  58. <div style="display: flex">
  59. <span
  60. style="color: #666666; cursor: pointer"
  61. @click="showcc = !showcc"
  62. >抄送</span
  63. >
  64. <span style="color: ##dddddd; margin: 0 8px">|</span>
  65. <span
  66. style="color: #666666; cursor: pointer"
  67. @click="showbcc = !showbcc"
  68. >密送</span
  69. >
  70. </div>
  71. </el-form-item>
  72. <el-form-item label="抄送人" prop="cc" v-if="showcc">
  73. <div style="width: 100%">
  74. <div style="display: flex; width: 100%">
  75. <!-- <el-autocomplete
  76. v-model="formData.data.cc"
  77. clearable
  78. class="inline-input w-50"
  79. placeholder="请输入"
  80. @select="handlePerson"
  81. :fetch-suggestions="querySearchPerson"
  82. @keyup.enter.native="handleAdd(20)"
  83. >
  84. </el-autocomplete> -->
  85. <el-input
  86. v-model="formData.data.cc"
  87. placeholder="请输入"
  88. @keyup.enter.native="handleAdd(20)"
  89. />
  90. <!-- <el-button
  91. type="primary"
  92. @click="handleAdd(20)"
  93. style="margin-left: 10px"
  94. :disabled="!formData.data.cc"
  95. >添加</el-button
  96. > -->
  97. </div>
  98. <div style="margin-top: 15px" v-if="cc && cc.length > 0">
  99. <el-tag
  100. style="margin-right: 10px"
  101. class="ml-2"
  102. type="info"
  103. v-for="(item, index) in cc"
  104. :key="index"
  105. closable
  106. @close="handleClose(index, 20)"
  107. >{{ item.address }}</el-tag
  108. >
  109. </div>
  110. </div>
  111. </el-form-item>
  112. <el-form-item label="密送人" prop="bcc" v-if="showbcc">
  113. <div style="width: 100%">
  114. <div style="display: flex; width: 100%">
  115. <!-- <el-autocomplete
  116. v-model="formData.data.bcc"
  117. clearable
  118. placeholder="请输入"
  119. @select="handlePerson"
  120. :fetch-suggestions="querySearchPerson"
  121. @keyup.enter.native="handleAdd(30)"
  122. >
  123. </el-autocomplete> -->
  124. <el-input
  125. v-model="formData.data.bcc"
  126. placeholder="请输入"
  127. @keyup.enter.native="handleAdd(30)"
  128. />
  129. <!-- <el-button
  130. type="primary"
  131. @click="handleAdd(30)"
  132. style="margin-left: 10px"
  133. :disabled="!formData.data.bcc"
  134. >添加</el-button
  135. > -->
  136. </div>
  137. <div style="margin-top: 15px" v-if="bcc && bcc.length > 0">
  138. <el-tag
  139. style="margin-right: 10px"
  140. class="ml-2"
  141. type="info"
  142. v-for="(item, index) in bcc"
  143. :key="index"
  144. closable
  145. @close="handleClose(index, 30)"
  146. >{{ item.address }}</el-tag
  147. >
  148. </div>
  149. </div>
  150. </el-form-item>
  151. <el-form-item label="主题" prop="subject">
  152. <el-input v-model="formData.data.subject" placeholder="请输入" />
  153. </el-form-item>
  154. <el-form-item label="附件">
  155. <div style="width: 100%">
  156. <el-upload
  157. v-model:fileList="fileList"
  158. class="upload-demo"
  159. action="https://winfaster.obs.cn-south-1.myhuaweicloud.com"
  160. :data="uploadData"
  161. drag
  162. multiple
  163. :show-file-list="false"
  164. :before-upload="handleBeforeUpload"
  165. >
  166. <div>
  167. <img
  168. src="@/assets/images/icon_attachment.svg"
  169. class="att-img"
  170. fit="scale-down"
  171. />
  172. <span style="padding-left: 8px"
  173. >将文件拖到此处,或<span style="color: #409eff"
  174. >点击上传</span
  175. ></span
  176. >
  177. </div>
  178. </el-upload>
  179. <div class="att-box" v-if="fileListCopy && fileListCopy.length > 0">
  180. <div v-for="(itemFile, index) in fileListCopy" :key="index">
  181. <div class="att-item">
  182. <img
  183. src="@/assets/images/icon_dz.svg"
  184. style="cursor: pointer"
  185. fit="scale-down"
  186. />
  187. <div class="att-name">
  188. {{ itemFile.fileName }}
  189. </div>
  190. <img
  191. src="@/assets/images/icon_delete.svg"
  192. class="att-img"
  193. fit="scale-down"
  194. />
  195. </div>
  196. </div>
  197. </div>
  198. </div>
  199. </el-form-item>
  200. <el-form-item label="正文" prop="content">
  201. <div style="width: 100%">
  202. <Editor
  203. :value="formData.data.content"
  204. @updateValue="updateContent"
  205. ref="contentEditor"
  206. />
  207. </div>
  208. </el-form-item>
  209. <el-form-item label="发件人" prop="replyTo">
  210. <el-input
  211. v-model="formData.data.replyTo"
  212. placeholder="请输入"
  213. style="width: 50%"
  214. />
  215. </el-form-item>
  216. <el-form-item>
  217. <el-button type="primary" @click="handleSend()"> 发 送 </el-button>
  218. </el-form-item>
  219. </el-form>
  220. </div>
  221. </div>
  222. </template>
  223. <script setup>
  224. import byForm from "@/components/byForm/index";
  225. import { ElMessage, ElMessageBox } from "element-plus";
  226. import Editor from "@/components/Editor/index.vue";
  227. import { validEmail } from "@/utils/validate.js";
  228. import useMailStore from "@/store/modules/mail";
  229. import { nextTick } from "vue";
  230. const mailStore = useMailStore();
  231. const { proxy } = getCurrentInstance();
  232. const loading = ref(false);
  233. let uploadData = ref({});
  234. const fileList = ref([]);
  235. const fileListCopy = ref([]);
  236. const showcc = ref(false);
  237. const showbcc = ref(false);
  238. const formOption = reactive({
  239. inline: true,
  240. labelWidth: 65,
  241. itemWidth: 100,
  242. labelPosition: "right",
  243. });
  244. const formConfig = computed(() => {
  245. return [
  246. {
  247. type: "slot",
  248. slotName: "to",
  249. label: "收件人",
  250. prop: "to",
  251. },
  252. {
  253. type: "slot",
  254. slotName: "bbcc",
  255. },
  256. {
  257. type: "slot",
  258. slotName: "cc",
  259. label: "抄送人",
  260. prop: "cc",
  261. },
  262. {
  263. type: "slot",
  264. slotName: "bcc",
  265. label: "密送人",
  266. prop: "bcc",
  267. },
  268. {
  269. type: "input",
  270. prop: "subject",
  271. label: "主题",
  272. required: true,
  273. itemWidth: 50.1,
  274. itemType: "text",
  275. },
  276. {
  277. type: "slot",
  278. slotName: "fileSlot",
  279. label: "附件",
  280. },
  281. {
  282. type: "slot",
  283. slotName: "contentSlot",
  284. label: "正文",
  285. },
  286. {
  287. type: "input",
  288. prop: "replyTo",
  289. label: "发件人",
  290. required: true,
  291. itemWidth: 50,
  292. itemType: "text",
  293. },
  294. ];
  295. });
  296. const rules = ref({
  297. subject: [{ required: true, message: "请输入主题", trigger: "blur" }],
  298. replyTo: [{ required: true, message: "请输入发件人", trigger: "blur" }],
  299. });
  300. const formData = reactive({
  301. data: {
  302. content: "",
  303. },
  304. });
  305. const to = ref([]);
  306. const cc = ref([]);
  307. const bcc = ref([]);
  308. const replyTo = ref([]);
  309. const submit = ref(null);
  310. const handleReset = () => {
  311. formData.data = {
  312. to: "",
  313. cc: "",
  314. bcc: "",
  315. content: "",
  316. subject: "",
  317. replyTo: "",
  318. };
  319. to.value = [];
  320. cc.value = [];
  321. bcc.value = [];
  322. fileList.value = [];
  323. fileListCopy.value = [];
  324. contentEditor.value.changeHtml("");
  325. };
  326. const handleSend = () => {
  327. submit.value.validate((valid) => {
  328. if (valid) {
  329. const data = { ...formData.data };
  330. if (!to.value.length > 0) {
  331. return ElMessage({
  332. message: "请添加收件人",
  333. type: "info",
  334. });
  335. }
  336. if (!validEmail(formData.data.replyTo)) {
  337. return ElMessage({
  338. message: "发件人邮箱格式不正确",
  339. type: "info",
  340. });
  341. }
  342. if (data.content) {
  343. loading.value = true;
  344. let replyTo = [
  345. {
  346. address: formData.data.replyTo,
  347. personal: null,
  348. },
  349. ];
  350. const submitData = {
  351. type: mailStore.selectMail.type,
  352. mailboxId: mailStore.selectMail.id,
  353. subject: data.subject,
  354. content: data.content,
  355. to: to.value,
  356. cc: cc.value,
  357. bcc: bcc.value,
  358. replyTo: replyTo,
  359. fileList: fileListCopy.value.map((x) => ({
  360. fileName: x.fileName,
  361. fileUrl: x.fileUrl,
  362. })),
  363. };
  364. proxy.post("/mailService/sendMail", submitData).then((res) => {
  365. ElMessage({
  366. message: "发送成功",
  367. type: "success",
  368. });
  369. handleReset();
  370. loading.value = false;
  371. });
  372. } else {
  373. return ElMessage({
  374. message: "请输入正文",
  375. type: "info",
  376. });
  377. }
  378. }
  379. });
  380. };
  381. const updateContent = (val) => {
  382. formData.data.content = val;
  383. };
  384. const handleBeforeUpload = async (file) => {
  385. const res = await proxy.post("/fileInfo/getSing", { fileName: file.name });
  386. uploadData.value = res.uploadBody;
  387. fileListCopy.value.push({
  388. id: res.id,
  389. fileName: res.fileName,
  390. path: res.fileUrl,
  391. fileUrl: res.fileUrl,
  392. uid: file.uid,
  393. });
  394. };
  395. const handleClose = (index, val) => {
  396. switch (val) {
  397. case 10:
  398. to.value.splice(index, 1);
  399. break;
  400. case 20:
  401. cc.value.splice(index, 1);
  402. break;
  403. case 30:
  404. bcc.value.splice(index, 1);
  405. break;
  406. }
  407. };
  408. const handleAdd = (val) => {
  409. switch (val) {
  410. case 10: {
  411. if (!validEmail(formData.data.to)) {
  412. return ElMessage({
  413. message: "收件人邮箱格式不正确",
  414. type: "info",
  415. });
  416. }
  417. to.value.push({
  418. address: formData.data.to,
  419. personal: null,
  420. });
  421. formData.data.to = "";
  422. break;
  423. }
  424. case 20: {
  425. if (!validEmail(formData.data.cc)) {
  426. return ElMessage({
  427. message: "抄送人邮箱格式不正确",
  428. type: "info",
  429. });
  430. }
  431. cc.value.push({
  432. address: formData.data.cc,
  433. personal: null,
  434. });
  435. formData.data.cc = "";
  436. break;
  437. }
  438. case 30: {
  439. if (!validEmail(formData.data.bcc)) {
  440. return ElMessage({
  441. message: "密送人邮箱格式不正确",
  442. type: "info",
  443. });
  444. }
  445. bcc.value.push({
  446. address: formData.data.bcc,
  447. personal: null,
  448. });
  449. formData.data.bcc = "";
  450. break;
  451. }
  452. }
  453. };
  454. const handlePerson = () => {};
  455. const createFilter = (queryString) => {
  456. return (restaurant) => {
  457. return (
  458. restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0
  459. );
  460. };
  461. };
  462. const distributionCenterData = ref([]);
  463. const querySearchPerson = (queryString, callback) => {
  464. const results = queryString
  465. ? distributionCenterData.value.filter(createFilter(queryString))
  466. : distributionCenterData.value;
  467. callback(results);
  468. };
  469. const contentEditor = ref(null);
  470. const handleReplyInit = (allData, pageType) => {
  471. let data = allData.details;
  472. // 回复功能:则发件人是回复的收件人
  473. if (pageType === "10") {
  474. if (data.messageAddressList && data.messageAddressList.length > 0) {
  475. to.value = data.messageAddressList
  476. .filter((x) => x.type === 4)
  477. .map((x) => ({
  478. address: x.email,
  479. personal: x.personalName,
  480. }));
  481. }
  482. formData.data.subject = "Re: " + data.subject;
  483. } else if (pageType === "20") {
  484. to.value = [];
  485. formData.data.subject = "Fw: " + data.subject;
  486. } else if (pageType === "30") {
  487. // 全部回复
  488. if (data.messageAddressList && data.messageAddressList.length > 0) {
  489. to.value = data.messageAddressList
  490. .filter((x) => x.type !== 1)
  491. .map((x) => ({
  492. address: x.email,
  493. personal: x.personalName,
  494. }));
  495. }
  496. formData.data.subject = "Re: " + data.subject;
  497. } else if (pageType === "40") {
  498. if (data.messageAddressList && data.messageAddressList.length > 0) {
  499. to.value = data.messageAddressList
  500. .filter((x) => x.type === 1)
  501. .map((x) => ({
  502. address: x.email,
  503. personal: x.personalName,
  504. }));
  505. const cc = data.messageAddressList
  506. .filter((x) => x.type === 2)
  507. .map((x) => ({
  508. address: x.email,
  509. personal: x.personalName,
  510. }));
  511. if (cc.length > 0) {
  512. showcc.value = true;
  513. cc.value = cc;
  514. }
  515. const bcc = data.messageAddressList
  516. .filter((x) => x.type === 2)
  517. .map((x) => ({
  518. address: x.email,
  519. personal: x.personalName,
  520. }));
  521. if (bcc.length > 0) {
  522. showbcc.value = true;
  523. bcc.value = bcc;
  524. }
  525. }
  526. formData.data.subject = data.subject;
  527. } else if (pageType === "50") {
  528. handleReset();
  529. to.value = [
  530. {
  531. address: mailStore.currentMenu.reMail,
  532. personal: null,
  533. },
  534. ];
  535. }
  536. if (pageType !== "50") {
  537. contentEditor.value.changeHtml(
  538. `<p><br></p><p><br></p>${data.content}`,
  539. true
  540. );
  541. nextTick(() => {
  542. contentEditor.value.getFocus();
  543. });
  544. formData.data.replyTo = mailStore.selectMail.mailUser;
  545. }
  546. };
  547. // pageType 10为回复 20为转发 30为全部回复 40为再次编辑 0为写信 50为只回填收件人
  548. const init = () => {
  549. if (mailStore.currentMenu.pageType === "0") {
  550. handleReset();
  551. } else if (mailStore.currentMenu.pageType === "10") {
  552. handleReplyInit(mailStore.currentMenu, "10");
  553. } else if (mailStore.currentMenu.pageType === "20") {
  554. handleReplyInit(mailStore.currentMenu, "20");
  555. } else if (mailStore.currentMenu.pageType === "30") {
  556. handleReplyInit(mailStore.currentMenu, "30");
  557. } else if (mailStore.currentMenu.pageType === "40") {
  558. handleReplyInit(mailStore.currentMenu, "40");
  559. } else if (mailStore.currentMenu.pageType === "50") {
  560. handleReplyInit(mailStore.currentMenu, "50");
  561. }
  562. };
  563. watch(
  564. () => mailStore.currentMenu.pageType,
  565. (val) => {
  566. if (val === "0") {
  567. handleReset();
  568. } else if (val === "10") {
  569. handleReplyInit(mailStore.currentMenu, "10");
  570. } else if (val === "20") {
  571. handleReplyInit(mailStore.currentMenu, "20");
  572. } else if (val === "30") {
  573. handleReplyInit(mailStore.currentMenu, "30");
  574. } else if (val === "40") {
  575. handleReplyInit(mailStore.currentMenu, "40");
  576. } else if (val === "50") {
  577. handleReplyInit(mailStore.currentMenu, "50");
  578. }
  579. }
  580. );
  581. onMounted(() => {
  582. // if (mailStore.currentMenu.reMail) {
  583. // to.value.push({
  584. // address: mailStore.currentMenu.reMail,
  585. // personal: null,
  586. // });
  587. // }
  588. });
  589. defineExpose({
  590. initFn: init,
  591. });
  592. </script>
  593. <style lang="scss" scoped>
  594. .att-img {
  595. height: 18px;
  596. width: 18px;
  597. transform: translateY(4px);
  598. }
  599. .att-box {
  600. padding-bottom: 8px;
  601. display: flex;
  602. flex-wrap: wrap;
  603. .att-item {
  604. width: 200px;
  605. background-color: #eeeeee;
  606. height: 28px;
  607. line-height: 26px;
  608. margin-right: 16px;
  609. padding-left: 8px;
  610. display: flex;
  611. margin-top: 10px;
  612. // margin-bottom: 8px;
  613. }
  614. .att-name {
  615. padding-left: 8px;
  616. width: 150px;
  617. white-space: nowrap;
  618. overflow: hidden;
  619. text-overflow: ellipsis;
  620. cursor: pointer;
  621. }
  622. }
  623. :deep(.el-upload-dragger) {
  624. width: 250px;
  625. height: 40px;
  626. line-height: 40px;
  627. padding: 0 8px;
  628. }
  629. // .el-form-item--default {
  630. // margin-bottom: 0px;
  631. // }
  632. </style>