mailWrite.vue 16 KB

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