huangyinfeng
4 天以前 db42d08c39ae6129e2b95cd24c0d57c6769282e5
提交 | 用户 | age
00fe0e 1 <template>
ccfd07 2   <div class="p-3">
H 3     <a-spin :spinning="loading" class="p-1">
4       <a-form ref="formRef" :label-col="{ span: 2 }" :wrapper-col="{ span: 24 }" :model="modelRef">
5         <a-form-item :wrapper-col="{ span: 8 }">
6           <div style="display: flex; justify-content: space-evenly">
7             <a-button type="primary" shape="round" @click="fnHandleSubmit(modelRef)">发送</a-button>
8             <a-button shape="round" @click="fnSaveMailDrafts">存草稿</a-button>
9             <a-button shape="round" @click="fnPreview">预览</a-button>
10             <a-button shape="round">提交审批</a-button>
11             <a-button shape="round">取消</a-button></div
12           >
13         </a-form-item>
14         <a-form-item label="发件人" v-bind="validateInfos.sender" :wrapper-col="{ span: 12 }">
15           <a-select
16             v-model:value="modelRef.sender"
17             placeholder="选择发件人"
18             show-search
19             :field-names="{ label: 'email', value: 'email' }"
20             :options="state.senderList"
21             :filter-option="fnFilterOption"
22           >
23           </a-select>
24         </a-form-item>
25         <a-form-item label="收件人" v-bind="validateInfos.recipients" :wrapper-col="{ span: 18 }">
26           <a-row>
27             <a-col class="gutter-row" :span="16">
28               <a-select
29                 mode="tags"
30                 placeholder="请选择收件人或者输入收件人邮箱"
31                 v-model:value="modelRef.recipients"
32                 :options="state.recipientsList"
63d608 33                 :field-names="{ label: 'email', value: 'email' }"
ccfd07 34                 :maxTagCount="4"
H 35                 @search="fetchUser"
00fe0e 36               >
ccfd07 37                 <template #tagRender="{ label, closable, onClose, option }">
H 38                   <a-tag
39                     :closable="closable"
40                     style="margin-right: 3px"
41                     @close="onClose"
42                     :color="validateEmail(label) ? 'default' : 'red'"
00fe0e 43                   >
ccfd07 44                     {{ label }}&nbsp;&nbsp;
H 45                   </a-tag>
46                 </template>
47               </a-select>
48             </a-col>
49             <a-col
50               class="gutter-row"
51               :span="2"
52               style="display: flex; align-items: center; justify-content: center"
53             >
54               <plus-circle-outlined
55                 style="color: rgb(0 0 0 / 45%)"
56                 @click="fnHandleSelect('recipients')"
57               />
58             </a-col>
59             <a-col class="gutter-row" :span="1">
60               <a-form-item-rest>
61                 <a-button
62                   shape="round"
63                   type="link"
64                   block
65                   size="small"
66                   @click="ccCheckboxChange($event)"
67                 >
68                   {{ !isSecretDeliveryPerson ? '抄送' : '取消抄送' }}
69                 </a-button>
70               </a-form-item-rest>
71             </a-col>
72             <a-col class="gutter-row" :span="1">
73               <a-form-item-rest>
74                 <a-button
75                   shape="round"
76                   type="link"
77                   block
78                   size="small"
79                   @click="bccCheckboxChange($event)"
80                 >
81                   {{ !isCRecipient ? '密送' : '取消密送' }}
82                 </a-button>
83               </a-form-item-rest>
84             </a-col>
85           </a-row>
86         </a-form-item>
87         <span style="position: relative; top: -18px; left: 9%" v-if="modelRef.recipients.length">
88           {{ userParticulars && `${userParticulars.userName}<${userParticulars.email}` }}>
89           <span v-if="!userParticulars.title" style="margin-right: 10px; margin-left: 5px">
90             暂未查询到该客户的当地时间
91             <a @click="fnHandleTimeZone(userParticulars, 'add')">补充时区</a>
92           </span>
93           <span v-else>
94             {{ userParticulars.title }}
95             <a @click="fnHandleTimeZone(userParticulars, 'update')">时区不对?</a>
00fe0e 96           </span>
H 97
ccfd07 98           <a v-if="modelRef.recipients.length > 2">查看全部</a>
H 99         </span>
00fe0e 100
ccfd07 101         <a-form-item
H 102           label="抄送人"
103           v-bind="validateInfos.ccRecipients"
104           v-if="isSecretDeliveryPerson"
105           :wrapper-col="{ span: 18 }"
106         >
107           <a-row>
108             <a-col class="gutter-row" :span="16">
109               <a-select
110                 mode="tags"
111                 placeholder="请选择抄送人或者输入抄送人邮箱"
74a35f 112                 v-model:value="modelRef.ccRecipients"
ccfd07 113                 :options="state.recipientsList"
63d608 114                 :field-names="{ label: 'email', value: 'email' }"
ccfd07 115                 :maxTagCount="4"
H 116               >
117                 <template #tagRender="{ label, closable, onClose }">
118                   <a-tag
119                     :closable="closable"
120                     style="margin-right: 3px"
121                     @close="onClose"
122                     :color="validateEmail(label) ? 'default' : 'red'"
123                   >
124                     {{ label }}&nbsp;&nbsp;
125                   </a-tag>
126                 </template>
127               </a-select>
128             </a-col>
129             <a-col
130               class="gutter-row"
131               :span="2"
132               style="display: flex; align-items: center; justify-content: center"
133             >
134               <plus-circle-outlined
135                 style="color: rgb(0 0 0 / 45%)"
136                 @click="fnHandleSelect('ccRecipients')"
137               />
138             </a-col>
139           </a-row>
140         </a-form-item>
141         <a-form-item
142           label="密送人"
143           v-bind="validateInfos.bccRecipients"
144           v-if="isCRecipient"
145           :wrapper-col="{ span: 18 }"
146         >
147           <a-row>
148             <a-col class="gutter-row" :span="16">
149               <a-select
150                 mode="tags"
151                 placeholder="请选择抄送人或者输入抄送人邮箱"
152                 v-model:value="modelRef.bccRecipients"
153                 :options="state.recipientsList"
63d608 154                 :field-names="{ label: 'email', value: 'email' }"
ccfd07 155                 :maxTagCount="4"
H 156               >
157                 <template #tagRender="{ label, closable, onClose }">
158                   <a-tag
159                     :closable="closable"
160                     style="margin-right: 3px"
161                     @close="onClose"
162                     :color="validateEmail(label) ? 'default' : 'red'"
163                   >
164                     {{ label }}&nbsp;&nbsp;
165                   </a-tag>
166                 </template>
167               </a-select>
168             </a-col>
169             <a-col
170               class="gutter-row"
171               :span="2"
172               style="display: flex; align-items: center; justify-content: center"
173             >
174               <plus-circle-outlined
175                 style="color: rgb(0 0 0 / 45%)"
176                 @click="fnHandleSelect('bccRecipients')"
177               />
178             </a-col>
179           </a-row>
180         </a-form-item>
181         <div
182           style="position: relative; top: -20px; left: 9%; color: #909090; font-size: 12px"
183           v-if="modelRef.recipients.length"
184         >
185           {{ `${userLength}/100 收件人、抄送人和密送的联系人总数不可超过100` }}
186         </div>
187         <a-form-item label="主题" :wrapper-col="{ span: 12 }" :label-col="{ span: 2 }">
188           <a-input placeholder="请输入邮件主题" v-model:value="modelRef.subject" />
189         </a-form-item>
190         <a-form-item
191           v-bind="validateInfos.content"
192           :wrapper-col="{ span: 24 }"
193           :label-col="{ span: 2 }"
194         >
63d608 195           <Tinymce v-model="modelRef.content" @change="fnChangeContent"></Tinymce>
ccfd07 196         </a-form-item>
H 197       </a-form>
198     </a-spin>
199     <SelectUser
200       ref="selectUserRef"
201       v-model="openSelectUser"
202       :selectIds="selectIds"
203       :idName="idName"
204       :title="selectUserTitle"
205       @updateData="fnSelectUser"
206     />
207     <a-modal
208       v-model:open="openTimeZone"
209       :title="`${titleTimeZone}时区`"
210       :confirm-loading="confirmLoading"
211       @ok="fnHandleTimeZoneOk"
212     >
213       <a-form
214         ref="formTimeZoneRef"
215         :label-col="{ span: 6 }"
216         :wrapper-col="{ span: 24 }"
217         :model="modelRef"
218       >
219         <a-form-item label="国家地区:" :wrapper-col="{ span: 12 }">
220           <a-select
221             v-model:value="modelRef.sender"
222             placeholder="选择国家地区"
223             show-search
224             :field-names="{ label: 'name', value: 'id' }"
225             :options="state.data"
226           >
227           </a-select>
228         </a-form-item>
229         <a-form-item label="时区:" :wrapper-col="{ span: 12 }">
230           <a-select
231             v-model:value="modelRef.sender"
232             placeholder="选择时区"
233             show-search
234             :field-names="{ label: 'name', value: 'id' }"
235             :options="state.data"
236           >
237           </a-select>
238         </a-form-item>
239         <a-form-item :label-col="{ span: 6 }">
240           <a-row>
241             <a-col span="6"> </a-col>
242             <a-col span="18">
243               <div style="color: #909090"
244                 >当地实时时间根据时区信息显示<br />
245                 国家地区/时区信息在客户资料-特征信息查看</div
246               >
247             </a-col>
248           </a-row>
249         </a-form-item>
250       </a-form>
251     </a-modal>
252   </div>
00fe0e 253 </template>
H 254
255 <script lang="ts" setup>
ccfd07 256   name: 'Edit';
H 257   import { useDesign } from '@/hooks/web/useDesign';
00fe0e 258
63d608 259   import { ref, reactive, computed, onMounted } from 'vue';
00fe0e 260   import { useMessage } from '@/hooks/web/useMessage';
H 261   import { Tinymce } from '@/components/Tinymce';
262   import { PlusCircleOutlined } from '@ant-design/icons-vue';
12f730 263   import SelectUser from '../components/SelectUser/index.vue';
00fe0e 264   import { Form } from 'ant-design-vue';
H 265   const modelRef = reactive({
266     sender: '',
267     recipients: [],
268     ccRecipients: [],
269     bccRecipients: [],
ccfd07 270     attachmentList: [],
00fe0e 271     subject: '',
H 272     content: '',
273   });
ccfd07 274   const loading = ref(false);
00fe0e 275   const rulesRef = reactive({
H 276     sender: [
277       {
278         required: true,
279         message: '发件人不能为空',
280       },
281     ],
282     recipients: [
283       {
284         required: true,
285         message: '收件人不能为空',
286       },
287     ],
288     content: [
289       {
290         required: true,
291         message: '内容不能为空',
292       },
293     ],
294   });
63d608 295   const TYPE = computed(() => {
H 296     return router.currentRoute.value.query.type || 'send';
297   });
298   onMounted(() => {
299     fnGetRecipientsList();
300     if (TYPE.value === 'reply') {
301       fuGetReplyEmailData();
302     }
a9a03d 303     if (TYPE.value === 'edit') {
H 304       fuGetEditEmailData();
305     }
63d608 306   });
00fe0e 307   const useForm = Form.useForm;
H 308   const { validate, validateInfos } = useForm(modelRef, rulesRef);
309
310   const formRef = ref();
311
312   let isSecretDeliveryPerson = ref(false);
313
314   let isCRecipient = ref(false);
315   const handleCheckboxChange = (ref, e) => {
316     ref.value = !ref.value;
317   };
318
319   const ccCheckboxChange = (e) => {
320     handleCheckboxChange(isSecretDeliveryPerson, e);
321   };
322   const bccCheckboxChange = (e) => {
323     handleCheckboxChange(isCRecipient, e);
324   };
325
326   const { createMessage } = useMessage();
ccfd07 327   import {
H 328     getAccountListApi,
329     sendingMailApi,
330     saveMailDraftsApi,
331     emailListAPi,
a9a03d 332     getMailInfoApi,
ccfd07 333   } from '@/api/email/userList';
00fe0e 334   // 定义状态管理对象
H 335   const state = reactive({
336     data: [],
337     fetching: false,
338     error: null,
ccfd07 339     senderList: [],
H 340     recipientsList: [],
00fe0e 341   });
H 342
343   // 获取用户列表的函数
344   const fnGetUserList = async (params) => {
345     try {
346       state.fetching = true;
ccfd07 347       const res = await getAccountListApi();
H 348       console.log(res, 'res');
00fe0e 349
ccfd07 350       if (res && res.data && Array.isArray(res.data)) {
H 351         state.senderList = res.data;
00fe0e 352       } else {
H 353         console.error('Invalid response format:', res);
354       }
355     } catch (error) {
356       console.error('Failed to fetch user list:', error);
357     } finally {
358       state.fetching = false;
359     }
360   };
361
362   // 初始化用户数据
363   const fetchData = async () => {
364     await fnGetUserList({ page: 1, pageSize: 30 });
365   };
366   console.log(state.data, 'state.data');
367   // 调用初始化函数
368   fetchData();
369
370   const fnFilterOption = (input: string, option: any) => {
ccfd07 371     return option.email.toLowerCase().indexOf(input.toLowerCase()) >= 0;
00fe0e 372   };
H 373
374   const openSelectUser = ref(false);
375   const selectUserTitle = ref('选择收件人');
376   const selectIds = ref([]);
377   const idName = ref('');
378   const fnHandleSelect = (e) => {
379     if (e === 'recipients') {
380       selectUserTitle.value = '选择收件人';
381       idName.value = e;
382     }
383     if (e === 'ccRecipients') {
384       selectUserTitle.value = '选择抄送人';
385       idName.value = e;
386     }
387     if (e === 'bccRecipients') {
388       selectUserTitle.value = '选择密送人';
389       idName.value = e;
390     }
391     selectIds.value = modelRef[e];
392     openSelectUser.value = true;
393   };
394
395   function fnChangeContent(e) {
db42d0 396   if (!isValidEvent(e)) {
H 397     console.error('Invalid event:', e);
398     return;
00fe0e 399   }
db42d0 400
H 401   console.log('Event:', e, '----------------');
402   
403   modelRef.content = e.content;
404   modelRef.attachmentList = mergeArrayWithPrefix(fnBuildAttachmentList(e.fileUNID));
405   
406   console.log('Updated modelRef:', modelRef);
407 }
408
409 function isValidEvent(e) {
410   return e && typeof e === 'object' && typeof e.content === 'string';
411 }
412
413 function fnBuildAttachmentList(data) {
414   if (!Array.isArray(data)) {
415     console.error('Invalid argument: data must be an array');
416     return [];
417   }
418
419   return data.reduce((acc, item) => {
420     const uuid = item?.response?.uuid;
421     if (uuid) {
422       acc.push(uuid);
423     } else {
424       console.warn('Invalid item:', item);
425     }
426     return acc;
427   }, []);
428 }
429
430 function mergeArrayWithPrefix(arr) {
431   if (!arr || arr.length === 0) return '';
432   const [prefix] = arr[0].split(';');
433   const suffixes = arr.map(item => item.split(';')[1]).filter(Boolean).join(';');
434   
435   return `${prefix};${suffixes}`;
436 }
00fe0e 437
H 438   function fnBuildingCommitData() {
439     return {
440       sender: modelRef.sender,
441       receiver: modelRef.recipients,
442       cc: modelRef.ccRecipients,
443       bcc: modelRef.bccRecipients,
444       subject: modelRef.subject,
445       content: modelRef.content,
db42d0 446       attachmentList: modelRef.attachmentList,
00fe0e 447       docCode: docCode.value,
H 448     };
449   }
450   const docCode = ref('');
451   import { Modal } from 'ant-design-vue';
452   function fnHandleSubmit(values: any) {
453     validate()
454       .then((res) => {
455         if (!modelRef.subject) {
456           Modal.confirm({
457             title: '提示',
458             content: '邮件主题为空,确定要发送吗?',
459             onOk() {
460               pushSendingMail();
461             },
462             onCancel() {},
463           });
464         } else {
465           pushSendingMail();
466         }
467       })
468       .catch((error) => {
469         createMessage.error('表单验证失败');
470       });
471   }
ccfd07 472   import { useRouter } from 'vue-router';
H 473   const router = useRouter();
00fe0e 474   function pushSendingMail() {
H 475     const data = fnBuildingCommitData();
ccfd07 476     loading.value = true;
H 477     sendingMailApi(data)
478       .then((res) => {
74a35f 479         loading.value = false;
ccfd07 480         if (res.code === 0) {
H 481           createMessage.success(res.msg);
db42d0 482           router.push('/email/index');
ccfd07 483         }
H 484       })
485       .catch((error) => {
486         loading.value = false;
487       });
00fe0e 488   }
74a35f 489   function fnPreview() {
H 490     router.push({ path: '/preview/index', query: { docCode: docCode.value } });
491   }
00fe0e 492
H 493   function fnSaveMailDrafts() {
494     const data = fnBuildingCommitData();
ccfd07 495     loading.value = true;
H 496
497     saveMailDraftsApi(data)
498       .then((res) => {
499         loading.value = false;
74a35f 500         if (res.code == 0) {
ccfd07 501           docCode.value = res.data.docCode;
H 502           createMessage.success('保存成功');
503         }
504       })
505       .catch((error) => {
506         loading.value = false;
507       });
00fe0e 508   }
H 509
510   const fnSelectUser = (e) => {
511     modelRef[e.idName] = e.selectedRowKeys;
512   };
513
514   const userLength = computed(() => {
515     return (
516       modelRef.recipients.length + modelRef.ccRecipients.length + modelRef.bccRecipients.length
517     );
518   });
519
520   interface Recipient {
521     value: string;
522     email: string;
523     title: string;
524   }
525
526   const userParticulars = computed<Recipient | null>(() => {
527     const recipientId = modelRef.recipients?.[0];
528     if (!recipientId) {
529       console.error('Recipient ID is not defined.');
530       return { value: '', email: '', title: '' };
531     }
532     // 使用 find 方法查找匹配项
ccfd07 533     const foundItem = state.recipientsList.find((item) => item.email === recipientId);
00fe0e 534
H 535     // 返回找到的项,如果未找到则返回 null
536     return foundItem || { value: '', email: '', title: '' };
537   });
538   const fnHandleTimeZone = (e, type) => {
539     console.log('fnHandleTimeZone');
540     if (type == 'add') {
541       titleTimeZone.value = '添加';
542       typeTimeZone.value = 1;
543     } else {
544       titleTimeZone.value = '修改';
545       typeTimeZone.value = 2;
546     }
547     openTimeZone.value = true;
548   };
549
550   const openTimeZone = ref<boolean>(false);
551   const confirmLoading = ref<boolean>(false);
552   const titleTimeZone = ref<string>('添加');
553   const typeTimeZone = ref<number>(1);
554
555   const fnHandleTimeZoneOk = () => {
556     confirmLoading.value = true;
557     setTimeout(() => {
558       openTimeZone.value = false;
559       confirmLoading.value = false;
560     }, 2000);
561   };
ccfd07 562
63d608 563   function fnGetRecipientsList() {
H 564     emailListAPi({ key: '' }).then((body) => {
565       state.recipientsList = body.data;
566     });
ccfd07 567   }
H 568   // 邮箱校验正则表达式
569   const validateEmail = (email) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
570   import { debounce } from 'lodash-es';
571   let lastFetchId = 0;
572
573   const fetchUser = debounce((value) => {
574     lastFetchId += 1;
575     const fetchId = lastFetchId;
576     state.data = [];
577     state.fetching = true;
578     emailListAPi({ key: value }).then((body) => {
579       if (fetchId !== lastFetchId) {
580         // for fetch callback order
581         return;
582       }
583
584       state.recipientsList = body.data;
585       state.fetching = false;
586     });
587   }, 300);
a9a03d 588   // 编辑
H 589   function fuGetEditEmailData() {
590     getMailInfoApi({ docCode: router.currentRoute.value.query.docCode })
591       .then((res) => {
592         modelRef.sender = res.data.sender;
593         modelRef.recipients = res.data.receiver;
594         modelRef.subject = res.data.subject;
595         modelRef.content = res.data.content;
596       })
597       .catch(() => {});
598   }
63d608 599
H 600   // 回复
601   function fuGetReplyEmailData() {
602     getMailInfoApi({ docCode: router.currentRoute.value.query.docCode })
603       .then((res) => {
a9a03d 604         console.log(ref.data, '---3022');
H 605         modelRef.sender = res.data.receiver[0];
606         modelRef.recipients = [res.data.sender];
607         modelRef.subject = 'Re:' + res.data.subject;
608         modelRef.content = setContent(res.data);
63d608 609         // tableRowData.value = res.data;
H 610       })
611       .catch(() => {});
612     console.log('----------------4');
613   }
614   const setContent = (row) => {
a9a03d 615     const text = `<div style=\"font-size: 12px; font-family: Arial Narrow,serif; padding: 2px 0 2px 0;\">------------------&nbsp;Original&nbsp;------------------</div>\n<div style=\"font-size: 12px; background: #efefef; padding: 8px;\">\n<div><strong>From:&nbsp;</strong>&nbsp;${row.sender} &lt;<a style=\"color: #1e7bf9; text-decoration: none;\" href=\"mailto:${row.sender}\" target=\"_blank\" rel=\"noopener noreferrer\">${row.sender}</a>&gt;</div>\n<div><strong>Send time:&nbsp;</strong>&nbsp;${row.createTime}</div>\n<div><strong>To:&nbsp;</strong>&nbsp;${row.userName} &lt;<a style=\"color: #1e7bf9; text-decoration: none;\" href=\"mailto:${row.receiver}\" target=\"_blank\" rel=\"noopener noreferrer\">${row.receiver}</a>&gt;</div>\n<div><strong>Subject:&nbsp;</strong> ${row.subject}</div>\n</div>`;
H 616     return text + row.content;
63d608 617   };
00fe0e 618 </script>
H 619 <style lang="less" scoped>
620   @prefix-cls: ~'@{namespace}-email';
621   .@{prefix-cls} {
622     .splitpanes__pane {
623       display: flex;
624       align-items: stretch;
625       justify-content: center;
626       // background-color: var(--component-background-color);
627     }
628   }
629 </style>