huangyinfeng
4 天以前 db42d08c39ae6129e2b95cd24c0d57c6769282e5
提交 | 用户 | age
00fe0e 1 <template>
H 2   <a-drawer
3     v-model:open="drawerOpen"
4     :placement="placement"
a9a03d 5     width="68%"
00fe0e 6     :body-style="{ paddingBottom: '80px' }"
H 7     :footer-style="{ textAlign: 'right' }"
8     @after-open-change="afterOpenChange"
74a35f 9     @close="drawerClose"
00fe0e 10   >
H 11     <template #title>
a9a03d 12       <div>
cb21a5 13         <pageHeadLeft
H 14           :checked="true"
15           :selectAllRow="[{ docCode }]"
16           @nextNum="drawerClose"
17         ></pageHeadLeft>
a9a03d 18       </div>
63d608 19     </template>
H 20     <template #extra>
21       <div style="font-size: 16px">
22         共<span class="m-1">{{ props.allList.length }}</span
23         >封
24         <LeftOutlined style="padding: 0 20px" @click="fnPrev" />
25         <RightOutlined @click="fnNext" />
26       </div>
27     </template>
28     <template #footer>
29       <div style="display: flex">
30         <a-textarea autoSize size="large" v-model:value="content" placeholder="快速回复">
31           <template #prefix>
32             <RollbackOutlined style="color: #999" />
33           </template>
34         </a-textarea>
35         <a-button size="large" type="primary" style="margin-left: 10px" @click="fnQuickReply"
36           >发送
37         </a-button>
38       </div>
39     </template>
a9a03d 40     <div class="ct-top" :class="isOpen ? 'isOpenTop' : 'onOpenTop'">
H 41       <div class="title">
42         <div class="left">
43           <span style="margin-right: 20px; font-size: 24px; font-weight: 700">
44             <a-tooltip placement="bottom">
45               <template #title>
46                 <span>{{ tableRowData.subject }}</span>
47               </template>
48               <div :style="{ maxWidth: isOpen ? '300px' : '600px' }" class="text-ellipsis">{{
49                 tableRowData.subject
50               }}</div>
51             </a-tooltip>
52           </span>
53           <span style="margin-right: 10px; font-size: 16px">
54             <PushpinOutlined />
55           </span>
db42d0 56           <a-tag
H 57             v-if="tableRowData.attachmentPath?.length > 0"
58             style="margin-right: 10px; font-size: 12px"
59           >
60             <PaperClipOutlined style="margin-right: 4px; color: #ffac00" />{{
61               tableRowData.attachmentPath?.length
62             }}
63           </a-tag>
a9a03d 64         </div>
H 65         <div class="right">
db42d0 66           <div class="tate">{{
H 67             formatToDateDay(tableRowData.receiveTime || tableRowData.updateTime)
68           }}</div>
a9a03d 69           <div>
H 70             <a-dropdown-button>
71               <div v-if="!isDrafts">
72                 <span>
73                   <a-tooltip placement="bottom">
74                     <template #title>
75                       <span>回复</span>
76                     </template>
77                     <LeftOutlined @click="replyEmail(tableRowData, 'reply')" />
78                   </a-tooltip>
79                 </span>
80                 <a-divider type="vertical" />
81                 <span>
82                   <a-tooltip placement="bottom">
83                     <template #title>
84                       <span>快速回复</span>
85                     </template>
86                     <DoubleLeftOutlined />
87                   </a-tooltip>
88                 </span>
89               </div>
90               <div v-else>
91                 <span @click="replyEmail(tableRowData, 'edit')">再次编辑</span>
92               </div>
93
94               <template #overlay>
95                 <a-menu>
96                   <a-menu-item key="1">
97                     <UserOutlined />
98                     1st menu item
99                   </a-menu-item>
100                   <a-menu-item key="2">
101                     <UserOutlined />
102                     2nd menu item
103                   </a-menu-item>
104                   <a-menu-item key="3">
105                     <UserOutlined />
106                     3rd item
107                   </a-menu-item>
108                 </a-menu>
109               </template>
110             </a-dropdown-button>
111           </div>
112         </div>
113       </div>
114     </div>
115
63d608 116     <div>
cb21a5 117       <div class="flex-between">
H 118         <div class="ct-left p-2" :class="isOpen ? 'isOpen' : 'onOpen'">
a9a03d 119           <div class="user p-1 f-z-14" style="margin-top: 40px">
cb21a5 120             <div style="display: flex; align-items: center">
ccfd07 121               <a-avatar size="small" style="margin-right: 8px" src="#" />
a9a03d 122               <div>
H 123                 <span>{{ tableRowData.senderName }} {{ `<${tableRowData.sender}>` }}</span>
124               </div>
cb21a5 125               <span style="margin: 0 10px">发送</span>
H 126               <a-popover placement="bottom">
127                 <template #content>
128                   <div
129                     class="p-2"
130                     style="
131                       display: flex;
132                       align-items: center;
133                       border-bottom: 1px solid rgb(5 5 5 / 6%);
134                     "
135                   >
136                     <a-avatar size="small" style="margin-right: 8px" src="#" />
137                     <span style="color: #000; font-weight: 700">
138                       {{ `${tableRowData.receiver}` }}</span
139                     >
140                     <CopyOutlined />
141                   </div>
142                   <div class="display-flex p-2">
143                     <a-button type="link" size="small">往来邮件</a-button>
144                   </div>
145                 </template>
146                 <a-avatar size="small" style="margin-right: 8px" src="#" />
147                 {{ `${tableRowData.receiver}` }}<span>{{ `<${tableRowData.receiver}>` }}</span>
148               </a-popover>
149             </div>
a9a03d 150             <div type="info" class="p-2 f-z-14" style="margin-top: 10px; background-color: #e4f1ff">
cb21a5 151               <span>{{ `<${tableRowData.sender}>` }}</span>
H 152               <span>暂未查询到该客户的当地时间</span>
153               <!-- <span>2024-06-08 22:22</span> -->
154             </div>
db42d0 155
H 156             <div v-if="tableRowData.attachmentPath?.length>0" class="p-2 f-z-14" style="margin-top: 10px">
157               <div style="display: flex; align-items: center">
158                 <span style="margin-right: 10px">{{
159                   `附件(${tableRowData.attachmentPath?.length})`
160                 }}</span>
161                 <a type="link" @click="fnAllDownload">全部下载</a>
162               </div>
163               <div class="my-d-f" style="width: 100%; margin-top: 10px">
164                 <div
165                   class="file-item my-d-f"
166                   v-for="item in tableRowData.attachmentPath"
167                   :key="item"
168                 >
169                   <div class="icon"><FileExcelOutlined /></div>
170                   <div class="name" @click="fnPreview(item)">{{ item.name }}</div>
171                   <div class="size"> {{ item.size + 'k' }} </div>
172                   <div class="download" @click="fnDownload(item)"><CloudDownloadOutlined /></div>
173                   <!-- <div class="delete">删除</div> -->
174                 </div>
175               </div>
176             </div>
a9a03d 177             <div class="ct">
H 178               <div v-if="tableRowData.content">
179                 <TinymcePw ref="TinymcePwRef" v-model="tableRowData.content" />
180               </div>
cb21a5 181             </div>
ccfd07 182           </div>
H 183         </div>
a9a03d 184         <div v-show="isOpen" class="ct-right">
H 185           <div style="position: relative">
186             <div style="position: absolute; top: 0; left: 10px">
187               <UserTips></UserTips>
188             </div>
189           </div>
190         </div>
ccfd07 191       </div>
cb21a5 192       <div @click="fuToggleContent" class="toggle-btn" :class="isOpen ? 'onIconOpen' : 'iconOpen'">
H 193         <LeftOutlined v-if="!isOpen" />
194         <RightOutlined v-else />
195       </div>
00fe0e 196     </div>
H 197   </a-drawer>
198 </template>
199
ccfd07 200 <script lang="ts" setup>
74a35f 201   name: 'drawerDetail';
ccfd07 202
74a35f 203   import { ref, watch, defineProps, defineEmits, computed } from 'vue';
H 204   import {
205     LeftOutlined,
206     RightOutlined,
207     DoubleLeftOutlined,
208     PushpinOutlined,
209     CopyOutlined,
210     UserOutlined,
211     RollbackOutlined,
db42d0 212     FileExcelOutlined,
H 213     CloudDownloadOutlined,
214     PaperClipOutlined,
74a35f 215   } from '@ant-design/icons-vue';
H 216   import pageHeadLeft from './pageHeadLeft.vue';
a9a03d 217   import UserTips from '@/views/email/components/userTips/index.vue';
74a35f 218   import { TinymcePw } from '@/components/Tinymce';
H 219   import { getMailInfoApi, setQuickReplyAPi } from '@/api/email/userList';
220   import { useCollapseStore } from '@/store/modules/useCollapseStore';
221   import { nextTick } from 'vue';
222   import { formatToDateDay } from '@/utils/dateUtil';
223   import { useLoading } from '@/components/Loading';
00fe0e 224
74a35f 225   // const [open, close, setTip] = useLoading();
H 226   // 定义属性
227   interface Props {
228     modelValue: boolean;
229     title?: string;
230     placement?: 'left' | 'right' | 'top' | 'bottom';
231     idName?: string;
232     selectIds?: number[];
233     mailId?: string;
234     selectAllRow: Array<any>;
235     allList;
a9a03d 236     isDrafts?: boolean;
74a35f 237   }
ccfd07 238
74a35f 239   const props = defineProps<Props>();
ccfd07 240
74a35f 241   const tableRowData = ref<Record<string, any>>({});
H 242   const emit = defineEmits(['update:modelValue', 'updateData']);
243   const drawerOpen = ref(props.modelValue);
244   const TinymcePwRef = ref();
245   const docCode = ref(props.mailId);
246   watch(
247     () => props.mailId,
248     (newVal) => {
249       docCode.value = newVal;
250     },
251   );
252   // 监听属性变化
253   watch(
254     () => props.modelValue,
255     (newValue) => {
256       nextTick(() => {
a9a03d 257         drawerOpen.value = newValue;
H 258         setTimeout(() => {
259           console.log(newValue, '---------4', TinymcePwRef.value);
260           tableRowData.value.content = tableRowData.value.content;
261         }, 500);
74a35f 262       });
H 263       if (newValue) {
264         fnGetMailInfo(props.mailId);
265       }
266     },
267   );
268
269   function fnGetMailInfo(id) {
270     getMailInfoApi({ docCode: id })
271       .then((res) => {
a9a03d 272         nextTick(() => {
H 273           docCode.value = id;
274           tableRowData.value = res.data;
275         });
74a35f 276       })
H 277       .catch(() => {});
278   }
279   // 更新外部属性
280   watch(drawerOpen, (newValue) => {
281     emit('update:modelValue', newValue);
282   });
283
284   // 方法
285   const collapseStore = useCollapseStore();
286   const afterOpenChange = (bool: boolean) => {
287     if (bool) {
288       fnGetUserList({ page: 1, pageSize: 30 });
289     }
290     collapseStore.toggle(false);
291   };
292   const drawerClose = (e) => {
293     drawerOpen.value = false;
294   };
295
296   const fnGetUserList = (params) => {
297     // 调用接口逻辑
298   };
299
300   // 计算属性
301   const placement = computed(() => props.placement || 'right');
302
303   const content = ref('');
304   const isOpen = ref(false);
305
306   function fuToggleContent() {
307     isOpen.value = !isOpen.value;
308   }
309
310   function fnPrev() {
311     const id = getPrevId(props.allList, docCode.value);
312     fnGetMailInfo(id);
313   }
314   function fnNext() {
315     const id = getNextId(props.allList, docCode.value);
316     fnGetMailInfo(id);
317   }
318   function getNextId(list, id) {
319     const index = list.findIndex((item) => item.docCode === id);
320     if (index < list.length - 1) {
321       return list[index + 1].docCode;
322     } else {
323       return list[0].docCode;
324     }
325   }
326   function getPrevId(list, id) {
327     const index = list.findIndex((item) => item.docCode === id);
328     if (index > 0) {
329       return list[index - 1].docCode;
330     } else {
331       return list[list.length - 1].docCode;
332     }
333   }
334
335   import { useMessage } from '@/hooks/web/useMessage';
336
337   const { createMessage } = useMessage();
338   function fnQuickReply() {
339     if (!content.value) {
340       createMessage.warning('请输入回复内容');
341       return;
342     }
343     const data = {
344       docCode: docCode.value,
345       content: content.value,
346     };
347     setQuickReplyAPi(data).then((res) => {
348       if (res.code == 0) {
349         createMessage.success(res.msg);
350
351         fnGetMailInfo(props.mailId);
352       }
ccfd07 353     });
00fe0e 354   }
cb21a5 355   import { useRouter } from 'vue-router';
63d608 356   const router = useRouter();
a9a03d 357   function replyEmail(row, type) {
H 358     router.push({ path: '/email/edit', query: { docCode: row.docCode, type: type } });
63d608 359   }
db42d0 360
H 361   const fnPreview = (item) => {
362     if (!item) {
363       console.error('Invalid file or response');
364       return;
365     }
366
367     // // 获取文件类型(通过文件扩展名或 MIME 类型)
368     const fileExt = item.fileType; // 获取文件扩展名
369     const isImage = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp'].includes(fileExt); // 判断是否为图片
370
371     if (isImage) {
372       // 直接打开图片
373       window.open(item.url, '_blank');
374     } else {
375       const iframeSrc = `https://view.officeapps.live.com/op/view.aspx?src=${item.url}`;
376       window.open(iframeSrc, '_blank');
377     }
378   };
379
380   const fnDownload = (item) => {
381     const link = document.createElement('a');
382     link.href = item.url;
383     link.download = item.name; // 提取文件名
384     document.body.appendChild(link); // 将链接添加到 DOM
385     link.click(); // 模拟点击下载
386     document.body.removeChild(link); // 下载后移除链接
387   };
388
389   const fnAllDownload = () => {
390     const urls = tableRowData.value.attachmentPath.map((item) => item.url);
391     const url = urls.join(',');
392     fnDownload(url);
393   };
00fe0e 394 </script>
H 395
396 <style scoped lang="less">
74a35f 397   .table-content {
00fe0e 398     display: flex;
74a35f 399     justify-content: space-between;
H 400     border-top: 1px solid #f0f0f0;
401
402     .left {
403       width: 80%;
404       height: 100%;
405       border-right: 1px solid #f0f0f0;
406     }
407
408     .right {
409       height: 100%;
410     }
00fe0e 411   }
H 412
74a35f 413   ::v-deep(.ant-table) {
H 414     min-height: 355px !important;
415   }
416
417   .title {
00fe0e 418     display: flex;
H 419     align-items: center;
420     justify-content: space-between;
421
74a35f 422     & .left {
H 423       display: flex;
424       align-items: center;
425       width: 50%;
426     }
427
428     & .right {
429       display: flex;
a9a03d 430       flex: 1;
74a35f 431       align-items: center;
a9a03d 432       justify-content: flex-end;
74a35f 433
H 434       & .tate {
a9a03d 435         margin-right: 10px;
74a35f 436       }
00fe0e 437     }
H 438   }
439
74a35f 440   .ct {
H 441     margin: 20px 0;
442   }
00fe0e 443
74a35f 444   .flex-between {
H 445     display: flex;
446   }
00fe0e 447
74a35f 448   .ct-left {
H 449     padding-right: 20px;
450   }
00fe0e 451
74a35f 452   .ct-right {
a9a03d 453     flex: 1;
74a35f 454   }
00fe0e 455
74a35f 456   .toggle-btn {
H 457     display: flex;
458     position: absolute;
459     z-index: 99;
460     top: 50%;
461     width: 20px;
462     height: 54px;
463     padding-left: 5px;
464     transform: translateY(-50%);
465     border: 1px solid #f0f0f0;
466     border-right: none;
467     border-radius: 10px 0 0 10px;
468     background: #fafafa;
469   }
00fe0e 470
74a35f 471   .onOpen {
H 472     width: 100%;
473   }
00fe0e 474
74a35f 475   .isOpen {
a9a03d 476     width: calc(100% - 420px);
H 477   }
478
479   .onOpenTop {
480     width: 98%;
481   }
482
483   .isOpenTop {
484     width: calc(98% - 460px);
74a35f 485   }
00fe0e 486
74a35f 487   .iconOpen {
H 488     right: 0%;
489   }
00fe0e 490
74a35f 491   .onIconOpen {
a9a03d 492     right: 425px;
74a35f 493   }
cb21a5 494
H 495   .ctb {
496     position: relative;
497   }
498
a9a03d 499   .ct-top {
cb21a5 500     position: absolute;
H 501     z-index: 99;
a9a03d 502     top: 57px;
H 503     left: 10px;
cb21a5 504     padding: 10px;
H 505     background: #fff;
506   }
a9a03d 507
H 508   .text-ellipsis {
509     overflow: hidden;
510     text-overflow: ellipsis;
511     white-space: nowrap;
512   }
513
514   .f-z-14 {
515     font-size: 12px;
516   }
db42d0 517
H 518   .file-item {
519     width: 240px;
520     margin-right: 10px;
521     padding: 10px;
522     border-radius: 4px;
523     background-color: #f0f0f0;
524
525     & .icon {
526       width: 20px;
527       margin-right: 5px;
528     }
529
530     & .name {
531       flex: 1;
532       margin-right: 5px;
533       overflow: hidden;
534       text-overflow: ellipsis;
535       white-space: nowrap;
536     }
537
538     & .download {
539       display: none;
540       width: 20px;
541     }
542   }
543
544   .file-item:hover .name {
545     transition: 0.3s;
546     color: #0960bd;
547   }
548
549   .file-item:hover .size {
550     display: none;
551     transition: 0.3s;
552   }
553
554   .file-item:hover .download {
555     display: block;
556     transition: 0.3s;
557   }
00fe0e 558 </style>