| | |
| | | // 先检查proxy是否存在,再进行操作 |
| | | if (proxy && proxy.$cookies) { |
| | | proxy.$cookies.remove('JSESSIONID'); |
| | | proxy.$cookies.set('JSESSIONID', 'A2EB010772E1D486E221B4DF4EA6E912.jvm_59_9010', '1d'); |
| | | proxy.$cookies.set('JSESSIONID', '23542F948D2C450599CF5850631B432D.jvm_59_9010', '1d'); |
| | | } else { |
| | | console.error('proxy对象未初始化或不包含$cookies属性'); |
| | | } |
| | |
| | | UPDATE_HANDLE = '/crm/mail/updateHandle.do', |
| | | DELETE_EMAIL = '/crm/mail/deleteEmail.do', |
| | | SET_QUICK_REPLY = '/crm/mail/setQuickReply.do', |
| | | |
| | | CONTACT_LIST = '/crm/clues/contactList.do', |
| | | GET_HANDLE_MAIL_LIST = '/crm/mail/getHandleMailList.do', |
| | | ADD_FOLDER = '/crm/mail/folder/addFolder.do', |
| | | UPDATE_FOLDER = '/crm/mail/folder/updateFolder.do', |
| | | DELETE_FOLDER = '/crm/mail/folder/deleteFolder.do', |
| | | GET_FOLDER = '/crm/mail/folder/getFolder.do', |
| | | GET_ROW_ID = '/getRowid.do', |
| | | ADD_TAG = '/crm/mail/blacklist/getBlackList.do', |
| | | UPDATE_TAG = '/crm/mail/tag/updateTag.do', |
| | | DELETE_TAG = '/crm/mail/tag/deleteTag.do', |
| | | GET_TAG = '/crm/mail/tag/getTagList.do', |
| | | ADD_BLACKLIST = '/crm/mail/blacklist/addBlackList.do', |
| | | UPDATE_BLACKLIST = '/crm/mail/blacklist/updateBlackList.do', |
| | | DELETE_BLACKLIST = '/crm/mail/blacklist/deleteBlackList.do', |
| | | GET_BLACKLIST = '/crm/mail/blacklist/getBlackList.do', |
| | | } |
| | | // 获取邮件路由列表 |
| | | export const getEmailModuleApi = () => defHttp.get({ url: Api.GET_EMAIL_MODULE }); |
| | |
| | | }); |
| | | |
| | | // 快速回复 |
| | | export const setQuickReplyAPi = (params) => |
| | | defHttp.post({ |
| | | url: Api.SET_QUICK_REPLY, |
| | | params, |
| | | }); |
| | | export const setQuickReplyAPi = (params) => |
| | | defHttp.post({ |
| | | url: Api.SET_QUICK_REPLY, |
| | | params, |
| | | }); |
| | | |
| | | // 获取线索联系人列表 |
| | | export const contactListAPi = (params) => |
| | | defHttp.post({ |
| | | url: Api.CONTACT_LIST, |
| | | params, |
| | | }); |
| | | |
| | | // 获取线索联系人列表 |
| | | export const getHandleMailListApi = (params) => |
| | | defHttp.get({ |
| | | url: Api.GET_HANDLE_MAIL_LIST, |
| | | params, |
| | | }); |
| | | |
| | | // 新增文件夹 |
| | | export const addFolderApi = (params) => |
| | | defHttp.post({ |
| | | url: Api.ADD_FOLDER, |
| | | params, |
| | | }); |
| | | |
| | | // 修改文件夹 |
| | | export const updateFolderApi = (params) => |
| | | defHttp.post({ |
| | | url: Api.UPDATE_FOLDER, |
| | | params, |
| | | }); |
| | | |
| | | // 删除文件夹 |
| | | export const deleteFolderApi = (params) => |
| | | defHttp.post({ |
| | | url: Api.DELETE_FOLDER, |
| | | params, |
| | | }); |
| | | |
| | | // 查询文件夹 |
| | | export const getFolderApi = (params) => |
| | | defHttp.get({ |
| | | url: Api.GET_FOLDER, |
| | | params, |
| | | }); |
| | | |
| | | // 获取rowId |
| | | export const getRowIdApi = () => |
| | | defHttp.get({ |
| | | url: Api.GET_ROW_ID, |
| | | }); |
| | | |
| | | // 新增标签 |
| | | export const addTagApi = (params) => |
| | | defHttp.post({ |
| | | url: Api.ADD_TAG, |
| | | params, |
| | | }); |
| | | |
| | | // 修改标签 |
| | | export const updateTagApi = (params) => |
| | | defHttp.post({ |
| | | url: Api.UPDATE_TAG, |
| | | params, |
| | | }); |
| | | |
| | | // 删除标签 |
| | | export const deleteTagApi = (params) => |
| | | defHttp.post({ |
| | | url: Api.DELETE_TAG, |
| | | params, |
| | | }); |
| | | |
| | | // 查询标签 |
| | | export const getTagApi = (params) => |
| | | defHttp.get({ |
| | | url: Api.GET_TAG, |
| | | params, |
| | | }); |
| | | |
| | | |
| | | // 新增黑名单 |
| | | export const addBlackListApi = (params) => |
| | | defHttp.post({ |
| | | url: Api.ADD_BLACKLIST, |
| | | params, |
| | | }); |
| | | |
| | | // 修改黑名单 |
| | | export const updateBlackListApi = (params) => |
| | | defHttp.post({ |
| | | url: Api.UPDATE_BLACKLIST, |
| | | params, |
| | | }); |
| | | |
| | | // 删除黑名单 |
| | | export const deleteBlackListApi = (params) => |
| | | defHttp.post({ |
| | | url: Api.DELETE_BLACKLIST, |
| | | params, |
| | | }); |
| | | |
| | | // 查询黑名单 |
| | | export const getBlackListApi = (params) => |
| | | defHttp.get({ |
| | | url: Api.GET_BLACKLIST, |
| | | params, |
| | | }); |
New file |
| | |
| | | <template> |
| | | <div class="my-radio-group" v-if="type == 1"> |
| | | <a-radio-group v-model:value="localValue" button-style="solid" size="small" name="color"> |
| | | <a-radio-button |
| | | v-for="item in colors" |
| | | :key="item.type_id" |
| | | class="mr-5px" |
| | | :value="item.color" |
| | | :style="{ backgroundColor: item.color, borderColor: item.color }" |
| | | > |
| | | <span :class="localValue === item.color ? 'c-white' : 'c-white select-none op0'">✓</span> |
| | | </a-radio-button> |
| | | </a-radio-group> |
| | | </div> |
| | | <div v-else> |
| | | <a-dropdown :trigger="['click']"> |
| | | <div @click.prevent> |
| | | <div style="display: flex; align-items: center"> |
| | | <div class="bookmark" :style="{ backgroundColor: localValue }"></div> |
| | | <DownOutlined /> |
| | | </div> |
| | | </div> |
| | | <template #overlay> |
| | | <a-menu class="my-radio-group" style="width: 154px"> |
| | | <a-menu-item key="0"> |
| | | <a-radio-group |
| | | v-model:value="localValue" |
| | | button-style="solid" |
| | | size="small" |
| | | name="color" |
| | | > |
| | | <a-radio-button |
| | | v-for="item in colors" |
| | | :key="item.type_id" |
| | | class="mr-5px" |
| | | :value="item.color" |
| | | :style="{ backgroundColor: item.color, borderColor: item.color }" |
| | | > |
| | | <span :class="localValue === item.color ? 'c-white' : 'c-white select-none op0'" |
| | | >✓</span |
| | | > |
| | | </a-radio-button> |
| | | </a-radio-group> |
| | | </a-menu-item> |
| | | </a-menu> |
| | | </template> |
| | | </a-dropdown> |
| | | </div> |
| | | </template> |
| | | |
| | | <script lang="ts" setup> |
| | | import { ref, watch, defineProps, defineEmits } from 'vue'; |
| | | import { DownOutlined } from '@ant-design/icons-vue'; |
| | | // 定义 props,接收父组件传递的 modelValue |
| | | const props = defineProps({ |
| | | modelValue: { |
| | | type: String, |
| | | default:'#000000', |
| | | required: true, |
| | | }, |
| | | // 1普通,2选择器模式 |
| | | type: { |
| | | type: Number, |
| | | default: 0, |
| | | required: true, |
| | | }, |
| | | }); |
| | | |
| | | // 定义 emits,用于双向绑定 |
| | | const emit = defineEmits(['update:modelValue','change']); |
| | | |
| | | // 颜色选项 |
| | | const colors = ref([ |
| | | { type_id: 1, color: '#000000', name: '黑' }, |
| | | { type_id: 2, color: '#bc5959', name: '暗红' }, |
| | | { type_id: 3, color: '#d87538', name: '橙红' }, |
| | | { type_id: 4, color: '#209890', name: '青绿' }, // 被选中的颜色 |
| | | { type_id: 5, color: '#4b679d', name: '深蓝' }, |
| | | { type_id: 6, color: '#595dbf', name: '蓝紫' }, |
| | | { type_id: 7, color: '#333333', name: '深灰' }, |
| | | { type_id: 8, color: '#e43e3e', name: '红' }, |
| | | { type_id: 9, color: '#eb9955', name: '浅橙' }, |
| | | { type_id: 10, color: '#61bc81', name: '浅绿' }, |
| | | { type_id: 11, color: '#5d89e9', name: '浅蓝' }, |
| | | { type_id: 12, color: '#8d54bd', name: '紫' }, |
| | | { type_id: 13, color: '#7b8291', name: '蓝灰' }, |
| | | { type_id: 14, color: '#ee7b7b', name: '粉红' }, |
| | | { type_id: 15, color: '#e2ad28', name: '金黄' }, |
| | | { type_id: 16, color: '#80c463', name: '草绿' }, |
| | | { type_id: 17, color: '#4aa8eb', name: '天蓝' }, |
| | | { type_id: 18, color: '#acacac', name: '浅灰' }, |
| | | ]); |
| | | |
| | | // 定义本地状态,以便监听和更新 |
| | | const localValue = ref(props.modelValue); |
| | | |
| | | // 监听 modelValue 的变化,并同步更新本地值 |
| | | watch( |
| | | () => props.modelValue, |
| | | (newValue) => { |
| | | localValue.value = newValue; |
| | | }, |
| | | ); |
| | | |
| | | // 当本地值发生变化时,发出 update:modelValue 事件,通知父组件更新 |
| | | watch(localValue, (newValue) => { |
| | | emit('update:modelValue', newValue); |
| | | emit('change', newValue); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | | .mr-5px { |
| | | margin-right: 5px; |
| | | } |
| | | |
| | | .c-white { |
| | | color: white; |
| | | } |
| | | |
| | | .select-none { |
| | | user-select: none; |
| | | } |
| | | |
| | | .op0 { |
| | | opacity: 0; |
| | | } |
| | | |
| | | .my-radio-group { |
| | | :deep(.ant-radio-group) .ant-radio-button-wrapper::before { |
| | | width: 0; |
| | | } |
| | | } |
| | | |
| | | .mr-5px { |
| | | margin-top: 4px; |
| | | border-radius: 4px; |
| | | } |
| | | |
| | | .bookmark { |
| | | display: inline-block; |
| | | position: relative; |
| | | width: 14px; |
| | | height: 18px; |
| | | margin-right: 5px; |
| | | border-radius: 2px 2px 0 0; /* Rounded top corners */ |
| | | } |
| | | |
| | | .bookmark::after { |
| | | content: ''; |
| | | position: absolute; |
| | | right: 0; |
| | | bottom: 0; |
| | | left: 0; |
| | | height: 8px; |
| | | background-color: #fff; |
| | | clip-path: polygon(50% 60%, 0% 100%, 100% 100%); /* Triangle at the bottom */ |
| | | |
| | | } |
| | | </style> |
| | |
| | | <div> |
| | | <PageWrapper :class="`${prefixCls}`" dense contentFullHeight fixedHeight> |
| | | <div class="default-theme" style="display: flex;height: 100%;background-color: #fff;"> |
| | | <div style="width: 16%;height: 100%;"> |
| | | <div style="width: 10%;height: 100%;"> |
| | | <LeftNav></LeftNav> |
| | | </div> |
| | | <div style="width: 84%;height: 100%;"> |
| | |
| | | component: () => import('@/views/email/Inbox/index.vue'), |
| | | meta: { |
| | | title: '全部邮件', |
| | | currentActiveMenu: '/email/index', |
| | | }, |
| | | }, |
| | | { |
| | | path: 'list/:id', |
| | | name: 'EmailPage2', |
| | | component: () => import('@/views/email/UnreadEmail/user.vue'), |
| | | meta: { |
| | | title: '222', |
| | | currentActiveMenu: '/email/index', |
| | | }, |
| | | }, |
| | | // { |
| | | // path: 'list/:id', |
| | | // name: 'EmailPage2', |
| | | // component: () => import('@/views/email/UnreadEmail/user.vue'), |
| | | // meta: { |
| | | // title: '222', |
| | | // currentActiveMenu: '/email/index', |
| | | // }, |
| | | // }, |
| | | ], |
| | | }, |
| | | { |
| | |
| | | component: () => import('@/views/email/UnreadEmail/index.vue'), |
| | | meta: { |
| | | title: '未读邮件', |
| | | currentActiveMenu: '/email/index', |
| | | }, |
| | | }, |
| | | { |
| | |
| | | name: 'HandlingEmailsOnBehalfOfOthers', |
| | | component: () => import('@/views/email/HandlingEmailsOnBehalfOfOthers/index.vue'), |
| | | meta: { |
| | | title: '代处理邮件', |
| | | title: '待处理邮件', |
| | | hideTab: true, |
| | | currentActiveMenu: '/email/index', |
| | | }, |
| | | }, |
| | | |
| | |
| | | meta: { |
| | | title: '草稿箱', |
| | | hideTab: true, |
| | | currentActiveMenu: '/email/index', |
| | | }, |
| | | }, |
| | | { |
| | | path: 'ShippingBox', |
| | | name: 'ShippingBox', |
| | | component: () => import('@/views/email/Edit/index.vue'), |
| | | path: 'outbox', |
| | | name: 'Outbox', |
| | | component: () => import('@/views/email/outbox/index.vue'), |
| | | meta: { |
| | | title: '发件箱', |
| | | hideTab: true, |
| | | currentActiveMenu: '/email/index', |
| | | }, |
| | | children: [ |
| | | { |
| | | path: 'list', |
| | | name: 'Outbox', |
| | | component: () => import('@/views/email/outbox/index.vue'), |
| | | meta: { |
| | | title: '全部邮件', |
| | | currentActiveMenu: '/email/index', |
| | | }, |
| | | }] |
| | | }, |
| | | { |
| | | path: 'MassMailbox', |
| | | name: 'MassMailbox', |
| | | component: () => import('@/views/email/Edit/index.vue'), |
| | | meta: { |
| | | title: '群发箱', |
| | | hideTab: true, |
| | | currentActiveMenu: '/email/index', |
| | | }, |
| | | }, |
| | | // { |
| | | // path: 'MassMailbox', |
| | | // name: 'MassMailbox', |
| | | // component: () => import('@/views/email/Edit/index.vue'), |
| | | // meta: { |
| | | // title: '群发箱', |
| | | // hideTab: true, |
| | | // currentActiveMenu: '/email/index', |
| | | // }, |
| | | // }, |
| | | ], |
| | | }; |
| | | |
| | |
| | | <template> |
| | | <div> |
| | | <PageIndex :pageList="pageList" :mailType='0'></PageIndex> |
| | | <a-spin :spinning="loading" class="p-1" style="height: 100%"> |
| | | <PageIndex :pageList="pageList" :mailType="1" :pageData="pageData"> </PageIndex> |
| | | </a-spin> |
| | | </div> |
| | | </template> |
| | | |
| | |
| | | }); |
| | | import { receiveApi, getMailListApi } from '@/api/email/userList'; |
| | | const pageList = ref([]); |
| | | function getDataList() { |
| | | getMailListApi({ mail: routerId.value, mailType: 0 }).then((res) => { |
| | | if (res.code == 0) { |
| | | pageList.value = res.data; |
| | | } |
| | | }); |
| | | const loading = ref(false); |
| | | const pageData = ref({ |
| | | page: 1, |
| | | limit: 20, |
| | | total: 0, |
| | | }); |
| | | function getDataList(page: 1) { |
| | | getMailListApi({ mail: routerId.value, mailType: 0, page }) |
| | | .then((res) => { |
| | | loading.value = false; |
| | | if (res.code == 0) { |
| | | pageList.value = res.data.list; |
| | | pageData.value.total = res.data.total; |
| | | } |
| | | }) |
| | | .catch(() => { |
| | | loading.value = false; |
| | | }); |
| | | } |
| | | provide('getDataList', getDataList); |
| | | onMounted(() => { |
| | |
| | | placeholder="请选择收件人或者输入收件人邮箱" |
| | | v-model:value="modelRef.recipients" |
| | | :options="state.recipientsList" |
| | | :field-names="{ label: 'email', value: 'userName' }" |
| | | :field-names="{ label: 'email', value: 'email' }" |
| | | :maxTagCount="4" |
| | | @search="fetchUser" |
| | | > |
| | |
| | | placeholder="请选择抄送人或者输入抄送人邮箱" |
| | | v-model:value="modelRef.ccRecipients" |
| | | :options="state.recipientsList" |
| | | :field-names="{ label: 'email', value: 'userName' }" |
| | | :field-names="{ label: 'email', value: 'email' }" |
| | | :maxTagCount="4" |
| | | > |
| | | <template #tagRender="{ label, closable, onClose }"> |
| | |
| | | placeholder="请选择抄送人或者输入抄送人邮箱" |
| | | v-model:value="modelRef.bccRecipients" |
| | | :options="state.recipientsList" |
| | | :field-names="{ label: 'email', value: 'userName' }" |
| | | :field-names="{ label: 'email', value: 'email' }" |
| | | :maxTagCount="4" |
| | | > |
| | | <template #tagRender="{ label, closable, onClose }"> |
| | |
| | | :wrapper-col="{ span: 24 }" |
| | | :label-col="{ span: 2 }" |
| | | > |
| | | <Tinymce @change="fnChangeContent"></Tinymce> |
| | | <Tinymce v-model="modelRef.content" @change="fnChangeContent"></Tinymce> |
| | | </a-form-item> |
| | | </a-form> |
| | | </a-spin> |
| | |
| | | :selectIds="selectIds" |
| | | :idName="idName" |
| | | :title="selectUserTitle" |
| | | @sudUserList="fnGetRecipientsList" |
| | | @updateData="fnSelectUser" |
| | | /> |
| | | <a-modal |
| | |
| | | name: 'Edit'; |
| | | import { useDesign } from '@/hooks/web/useDesign'; |
| | | |
| | | import { ref, reactive, computed } from 'vue'; |
| | | import { ref, reactive, computed, onMounted } from 'vue'; |
| | | import { useMessage } from '@/hooks/web/useMessage'; |
| | | import { Tinymce } from '@/components/Tinymce'; |
| | | import { PlusCircleOutlined } from '@ant-design/icons-vue'; |
| | |
| | | }, |
| | | ], |
| | | }); |
| | | const TYPE = computed(() => { |
| | | return router.currentRoute.value.query.type || 'send'; |
| | | }); |
| | | onMounted(() => { |
| | | fnGetRecipientsList(); |
| | | if (TYPE.value === 'reply') { |
| | | fuGetReplyEmailData(); |
| | | } |
| | | }); |
| | | const useForm = Form.useForm; |
| | | const { validate, validateInfos } = useForm(modelRef, rulesRef); |
| | | |
| | |
| | | sendingMailApi, |
| | | saveMailDraftsApi, |
| | | emailListAPi, |
| | | getMailInfoApi |
| | | } from '@/api/email/userList'; |
| | | // 定义状态管理对象 |
| | | const state = reactive({ |
| | |
| | | }, 2000); |
| | | }; |
| | | |
| | | function fnGetRecipientsList(data) { |
| | | state.recipientsList = data; |
| | | function fnGetRecipientsList() { |
| | | emailListAPi({ key: '' }).then((body) => { |
| | | state.recipientsList = body.data; |
| | | }); |
| | | } |
| | | |
| | | // 邮箱校验正则表达式 |
| | | const validateEmail = (email) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); |
| | | import { debounce } from 'lodash-es'; |
| | | import DragBar from '@/layouts/default/sider/DragBar.vue'; |
| | | let lastFetchId = 0; |
| | | |
| | | const fetchUser = debounce((value) => { |
| | |
| | | state.fetching = false; |
| | | }); |
| | | }, 300); |
| | | |
| | | // 回复 |
| | | function fuGetReplyEmailData() { |
| | | getMailInfoApi({ docCode: router.currentRoute.value.query.docCode }) |
| | | .then((res) => { |
| | | console.log(ref.data,'---3022'); |
| | | modelRef.sender = res.data.receiver[0] |
| | | modelRef.recipients = [res.data.sender] |
| | | modelRef.subject = 'Re:'+ res.data.subject |
| | | modelRef.content = setContent(res.data) |
| | | // tableRowData.value = res.data; |
| | | }) |
| | | .catch(() => {}); |
| | | console.log('----------------4'); |
| | | } |
| | | const setContent = (row) => { |
| | | const text = `<div style=\"font-size: 12px; font-family: Arial Narrow,serif; padding: 2px 0 2px 0;\">------------------ Original ------------------</div>\n<div style=\"font-size: 12px; background: #efefef; padding: 8px;\">\n<div><strong>From: </strong> ${row.sender} <<a style=\"color: #1e7bf9; text-decoration: none;\" href=\"mailto:${row.sender}\" target=\"_blank\" rel=\"noopener noreferrer\">${row.sender}</a>></div>\n<div><strong>Send time: </strong> ${row.createTime}</div>\n<div><strong>To: </strong> ${row.userName} <<a style=\"color: #1e7bf9; text-decoration: none;\" href=\"mailto:${row.receiver}\" target=\"_blank\" rel=\"noopener noreferrer\">${row.receiver}</a>></div>\n<div><strong>Subject: </strong> ${row.subject}</div>\n</div>` |
| | | return text + row.content |
| | | }; |
| | | </script> |
| | | <style lang="less" scoped> |
| | | @prefix-cls: ~'@{namespace}-email'; |
| | |
| | | ></a-checkbox> |
| | | <!--更新 --> |
| | | <SyncOutlined class="icon" v-show="!checked" /> |
| | | <pageHeadLeft :checked="checked" :selectAllRow="selectAllRow" |
| | | :parentTableList='newList' |
| | | ></pageHeadLeft> |
| | | <pageHeadLeft |
| | | :checked="checked" |
| | | :selectAllRow="selectAllRow" |
| | | :parentTableList="newList" |
| | | ></pageHeadLeft> |
| | | </div> |
| | | </div> |
| | | |
| | |
| | | >共<span style="padding: 0 5px">20</span>封 |
| | | <a-pagination |
| | | v-model:current="pageCurrent" |
| | | v-model:page-size='page.limit' |
| | | simple |
| | | :total="50" |
| | | :total="page.total" |
| | | style="margin-left: 10px" |
| | | @change="handlePageChange" |
| | | /> |
| | | <FilterOutlined style="margin-left: 10px" /> |
| | | <a-popover placement="left" trigger="click"> |
| | |
| | | import pageHeadLeft from '@/views/email/components/ListPage/pageHeadLeft.vue'; |
| | | import { PageWrapper } from '@/components/Page'; |
| | | |
| | | import { ref, watch, defineProps, defineEmits, computed, reactive, onMounted } from 'vue'; |
| | | import { ref, watch, defineProps, defineEmits, computed, reactive, onMounted,inject } from 'vue'; |
| | | |
| | | // 定义属性 |
| | | interface Props { |
| | | pageList: []; |
| | | pageData?:any; |
| | | } |
| | | const props = defineProps<Props>(); |
| | | const newList = ref([]); |
| | |
| | | }, |
| | | ); |
| | | |
| | | const checked = ref(false); |
| | | const checked = computed(() => selectAllRow.value.length > 0); |
| | | const pageCurrent = ref(1); |
| | | const tableRef = ref(); |
| | | const state = reactive({ |
| | |
| | | num: 0, |
| | | }, |
| | | ]; |
| | | }); |
| | | }) |
| | | const activeKey = ref('1'); |
| | | const checked3 = ref(false); |
| | | import Table from '../table.vue'; |
| | | import Table from '@/views/email/components/ListPage/table.vue'; |
| | | onMounted(() => { |
| | | console.log('tableRef:', tableRef.value[0]); |
| | | }); |
| | |
| | | console.log('44444444444'); |
| | | } |
| | | |
| | | const page = computed(() => props.pageData); |
| | | const getDataList = inject('getDataList'); |
| | | function handlePageChange(page, pageSize){ |
| | | getDataList(page) |
| | | |
| | | } |
| | | </script> |
| | | <style scoped lang="less"> |
| | | .head { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | width: 100%; |
| | | height: 60px; |
| | | border-bottom: 1px solid rgb(5 5 5 / 6%); |
| | | |
| | | /* 增加选择器特异性 */ |
| | |
| | | align-items: center; |
| | | justify-content: space-flex-start; |
| | | width: 100%; |
| | | height: 100%;; |
| | | |
| | | & .icon { |
| | | margin-right: 15px; |
| | |
| | | <template> |
| | | <div> |
| | | <PageIndex :pageList="pageList"></PageIndex> |
| | | <a-spin :spinning="loading" class="p-1" style="height: 100%"> |
| | | <PageIndex :pageList="pageList" :mailType="1" :pageData="pageData"> </PageIndex> |
| | | </a-spin> |
| | | </div> |
| | | </template> |
| | | |
| | |
| | | return ''; |
| | | } |
| | | }); |
| | | import { getMailListApi } from '@/api/email/userList'; |
| | | import { getHandleMailListApi } from '@/api/email/userList'; |
| | | const pageList = ref([]); |
| | | function getDataList() { |
| | | getMailListApi({}).then((res) => { |
| | | if (res.code == 0) { |
| | | pageList.value = res.data; |
| | | } |
| | | }); |
| | | const loading = ref(false); |
| | | const pageData = ref({ |
| | | page: 1, |
| | | limit: 20, |
| | | total: 0, |
| | | }); |
| | | function getDataList(page: 1) { |
| | | loading.value = true; |
| | | |
| | | getHandleMailListApi({ page }) |
| | | .then((res) => { |
| | | loading.value = false; |
| | | if (res.code == 0) { |
| | | pageList.value = res.data.list; |
| | | pageData.value.total = res.data.total; |
| | | } |
| | | }) |
| | | .catch(() => { |
| | | loading.value = false; |
| | | }); |
| | | } |
| | | provide('getDataList', getDataList); |
| | | onMounted(() => { |
| | |
| | | <template> |
| | | <div> |
| | | <a-spin :spinning="loading" class="p-1"> |
| | | <PageIndex :pageList="pageList" :mailType="1"></PageIndex> |
| | | <a-spin :spinning="loading" class="p-1" style="height: 100%;"> |
| | | <PageIndex :pageList="pageList" :mailType="1" :pageData="pageData" > </PageIndex> |
| | | </a-spin> |
| | | </div> |
| | | </template> |
| | |
| | | import { getMailListApi } from '@/api/email/userList'; |
| | | const pageList = ref([]); |
| | | const loading = ref(false); |
| | | function getDataList() { |
| | | const pageData = ref({ |
| | | page: 1, |
| | | limit: 20, |
| | | total: 0, |
| | | }); |
| | | function getDataList(page:1) { |
| | | loading.value = true; |
| | | getMailListApi({ mail: routerId.value, mailType: 1 }) |
| | | getMailListApi({ mail: routerId.value, mailType: 1,page }) |
| | | .then((res) => { |
| | | loading.value = false; |
| | | |
| | | if (res.code == 0) { |
| | | pageList.value = res.data; |
| | | pageList.value = res.data.list; |
| | | pageData.value.total = res.data.total; |
| | | } |
| | | }) |
| | | .catch(() => { |
| | | loading.value = false; |
| | | }); |
| | | } |
| | | |
| | | |
| | | provide('getDataList', getDataList); |
| | | onMounted(() => { |
| | | getDataList(); |
| | |
| | | <template> |
| | | <div> |
| | | <PageIndex :pageList="pageList" :mailType='1'></PageIndex> |
| | | <a-spin :spinning="loading" class="p-1" style="height: 100%;"> |
| | | <PageIndex :pageList="pageList" :mailType='1' :pageData='pageData' ></PageIndex> |
| | | </a-spin> |
| | | </div> |
| | | </template> |
| | | |
| | |
| | | } |
| | | }); |
| | | import { getMailListApi } from '@/api/email/userList'; |
| | | import { limit } from 'packages/hooks/src/useRequest/utils/limit'; |
| | | const pageList = ref([]); |
| | | const pageData = ref({ |
| | | page: 1, |
| | | limit: 20, |
| | | total: 0, |
| | | }) |
| | | const loading = ref(false); |
| | | |
| | | function getDataList() { |
| | | getMailListApi({ mail: routerId.value,mailType:1,isNoRead:true }).then((res) => { |
| | | getMailListApi({ mail: routerId.value,mailType:1,isNoRead:true ,limit:pageData.value.limit}).then((res) => { |
| | | loading.value = false; |
| | | |
| | | if (res.code == 0) { |
| | | pageList.value = res.data; |
| | | pageList.value = res.data.list; |
| | | pageData.value.total = res.data.total; |
| | | } |
| | | }).catch(() => { |
| | | loading.value = false; |
| | | }); |
| | | } |
| | | provide('getDataList',getDataList); |
New file |
| | |
| | | <template> |
| | | <div class="p-2"> |
| | | <div> |
| | | <vxe-toolbar> |
| | | <template #buttons> |
| | | <div style="display: flex; align-items: flex-end"> |
| | | <span style="font-size: 1.25rem; font-weight: 600">邮箱地址黑名单</span |
| | | ><span style="margin-left: 5px; padding-bottom: 4px; font-size: 12px" |
| | | >黑名单中邮箱对应的邮件将自动收取到垃圾邮件(不会收到新邮件提醒)</span |
| | | > |
| | | </div> |
| | | </template> |
| | | <template #tools> |
| | | <a-button type="primary" @click="showModal('mail')">添加邮箱地址</a-button> |
| | | </template> |
| | | </vxe-toolbar> |
| | | <a-input-search |
| | | v-model:value="searchMail" |
| | | placeholder="请输入关键字搜索" |
| | | style="width: 300px; margin-bottom: 20px" |
| | | @search="onSearch($event, 'mail')" |
| | | /> |
| | | <a-table :columns="columns" :data-source="demo.mail" :scroll="{ y: 220 }" size="small"> |
| | | <template #bodyCell="{ column, record }"> |
| | | <template v-if="column.field == 'operate'"> |
| | | <a class="ant-dropdown-link" @click="fnDelete(record)">删除</a> |
| | | </template> |
| | | <template v-else-if="column.field == 'mail'"> |
| | | {{ record.blackContent }} |
| | | </template> |
| | | </template> |
| | | </a-table> |
| | | </div> |
| | | <div> |
| | | <vxe-toolbar> |
| | | <template #buttons> |
| | | <div style="display: flex; align-items: flex-end"> |
| | | <span style="font-size: 1.25rem; font-weight: 600">域名黑名单</span |
| | | ><span style="margin-left: 5px; padding-bottom: 4px; font-size: 12px" |
| | | >黑名单中域名对应的邮件将自动收取到垃圾邮件(不会收到新邮件提醒)</span |
| | | > |
| | | </div> |
| | | </template> |
| | | <template #tools> |
| | | <a-button type="primary" @click="showModal('domainName')">添加域名</a-button> |
| | | </template> |
| | | </vxe-toolbar> |
| | | <a-input-search |
| | | v-model:value="searchDomainName" |
| | | placeholder="请输入关键字搜索" |
| | | style="width: 300px; margin-bottom: 20px" |
| | | @search="onSearch($event, 'domainName')" |
| | | /> |
| | | <a-table |
| | | :columns="columnsDomainName" |
| | | :data-source="demo.domain" |
| | | :scroll="{ y: 220 }" |
| | | size="small" |
| | | > |
| | | <template #bodyCell="{ column, record }"> |
| | | <template v-if="column.field == 'operate'"> |
| | | <a class="ant-dropdown-link" @click="fnDelete(record)">删除</a> |
| | | </template> |
| | | <template v-else-if="column.field == 'domainName'"> |
| | | {{ record.blackContent }} |
| | | </template> |
| | | </template> |
| | | </a-table> |
| | | </div> |
| | | |
| | | <a-modal :width="600" v-model:open="open" :title="`新建${formTypeName}黑名单`" @ok="handleOk"> |
| | | <a-form ref="formRef" :model="form" style="margin-top: 20px"> |
| | | <a-form-item |
| | | v-if="formType === 'mail'" |
| | | label="邮箱地址" |
| | | name="blackContent" |
| | | :rules="[{ required: true, type: 'email', message: '请输入邮箱地址', trigger: 'blur' }]" |
| | | > |
| | | <a-input v-model:value="form.blackContent" placeholder="请输入邮箱地址" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | v-else |
| | | label="域名地址" |
| | | name="blackContent" |
| | | :rules="[{ required: true, validator: validatorDomainName }]" |
| | | > |
| | | <a-input v-model:value="form.blackContent" placeholder="请输入域名地址" /> |
| | | </a-form-item> |
| | | </a-form> |
| | | </a-modal> |
| | | </div> |
| | | </template> |
| | | |
| | | <script lang="ts" setup> |
| | | import { ref, onMounted, onUnmounted, reactive } from 'vue'; |
| | | import { getBlackListApi, addBlackListApi, deleteBlackListApi } from '@/api/email/userList'; |
| | | onUnmounted(() => {}); |
| | | |
| | | function fnGetList() { |
| | | getBlackListApi({}).then((res) => { |
| | | console.log(res); |
| | | demo.mail = res.data.mail; |
| | | demo.domain = res.data.domain; |
| | | }); |
| | | } |
| | | |
| | | import { useMessage } from '@/hooks/web/useMessage'; |
| | | const { createMessage } = useMessage(); |
| | | |
| | | const open = ref(false); |
| | | const formRef = ref(); |
| | | const columns = [ |
| | | { |
| | | title: '邮箱地址', |
| | | field: 'mail', |
| | | minWidth: 200, |
| | | }, |
| | | { |
| | | title: '操作', |
| | | field: 'operate', |
| | | width: 200, |
| | | }, |
| | | ]; |
| | | const columnsDomainName = [ |
| | | { |
| | | title: '域名地址', |
| | | field: 'domainName', |
| | | minWidth: 200, |
| | | }, |
| | | { |
| | | title: '操作', |
| | | field: 'operate', |
| | | width: 200, |
| | | }, |
| | | ]; |
| | | function validatorDomainName(rule, value, callback) { |
| | | if (!value) { |
| | | callback(new Error('请输入域名地址')); |
| | | } else { |
| | | if (!/[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+\.?/.test(value)) { |
| | | callback(new Error('域名地址格式不正确!')); |
| | | } else { |
| | | callback(); |
| | | } |
| | | } |
| | | } |
| | | const form = reactive({ |
| | | blackContent: '', |
| | | }); |
| | | const formTypeName = ref('邮箱地址'); |
| | | const formType = ref('mail'); |
| | | const demo = reactive({ |
| | | mail: [], |
| | | domain: [], |
| | | }); |
| | | |
| | | const searchMail = ref(''); |
| | | const searchDomainName = ref(''); |
| | | function showModal(blackType) { |
| | | open.value = true; |
| | | if (blackType === 'mail') { |
| | | formTypeName.value = '邮箱地址'; |
| | | formType.value = 'mail'; |
| | | } else { |
| | | formTypeName.value = '域名地址'; |
| | | formType.value = 'domainName'; |
| | | } |
| | | } |
| | | function handleOk() { |
| | | formRef.value.validate().then(() => { |
| | | addBlackListApi({ |
| | | blackContent: form.blackContent, |
| | | blackType: formType.value === 'mail' ? false : true, |
| | | }).then((res) => { |
| | | createMessage.success('添加成功'); |
| | | open.value = false; |
| | | fnGetList(); |
| | | }); |
| | | }); |
| | | } |
| | | function fnDelete(row) { |
| | | deleteBlackListApi({ |
| | | blackId: row.blackId, |
| | | }).then((res) => { |
| | | createMessage.success('删除成功'); |
| | | fnGetList(); |
| | | }); |
| | | } |
| | | function onSearch(e, type) { |
| | | console.log(e, type,'---3333'); |
| | | |
| | | const data = { |
| | | search: e, |
| | | type: type === 'mail' ? 0 : 1, |
| | | }; |
| | | if (type === 'mail') { |
| | | getBlackListApi(data).then((res) => { |
| | | demo.mail = res.data.mail; |
| | | }); |
| | | } else { |
| | | getBlackListApi(data).then((res) => { |
| | | demo.domain = res.data.domain; |
| | | }); |
| | | } |
| | | } |
| | | |
| | | onMounted(() => { |
| | | fnGetList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .sortable-row-demo .drag-btn { |
| | | font-size: 12px; |
| | | cursor: move; |
| | | } |
| | | |
| | | .sortable-row-demo .vxe-body--row.sortable-ghost, |
| | | .sortable-row-demo .vxe-body--row.sortable-chosen { |
| | | background-color: #dfecfb; |
| | | } |
| | | |
| | | .bookmark { |
| | | display: inline-block; |
| | | position: relative; |
| | | width: 14px; |
| | | height: 18px; |
| | | margin-right: 5px; |
| | | border-radius: 2px 2px 0 0; /* Rounded top corners */ |
| | | } |
| | | |
| | | .bookmark::after { |
| | | content: ''; |
| | | position: absolute; |
| | | right: 0; |
| | | bottom: 0; |
| | | left: 0; |
| | | height: 8px; |
| | | background-color: #fff; |
| | | clip-path: polygon(50% 60%, 0% 100%, 100% 100%); /* Triangle at the bottom */ |
| | | } |
| | | </style> |
| | |
| | | <div class="p-2" style="margin-left: 20px"> |
| | | <h2 class="title" style="font-size: 20px">常规</h2> |
| | | |
| | | <a-form style="width: 60%" :labelCol="{ span: 6 }"> |
| | | <a-form style="width: 60%" :labelCol="{ span: 2 }"> |
| | | <div> |
| | | <h2 class="title">账号</h2> |
| | | <a-form-item label="默认邮箱"> |
| | | <a-select> |
| | | <a-select class='w-200'> |
| | | <a-select-option v-for="(item, index) in emailList" :key="index" :value="item.email"> |
| | | {{ item.email }} |
| | | </a-select-option> |
| | |
| | | <PlusCircleOutlined style="margin-left: 20px" @click="showModal('add')" /> |
| | | </div> |
| | | </a-form-item> |
| | | <a-form-item :labelCol="{ span: 6 }" label="内容"> |
| | | <a-form-item :labelCol="{ span: 2 }" label="内容"> |
| | | <div style="height: 200px; overflow: hidden; border-bottom: 1px solid #d9d9d9"> |
| | | <TinymcePw v-model="signContent"></TinymcePw> |
| | | </div> |
New file |
| | |
| | | <template> |
| | | <div class="p-2"> |
| | | <vxe-toolbar> |
| | | <template #buttons> |
| | | <div style="display: flex; align-items: flex-end"> |
| | | <span style="font-size: 1.25rem; font-weight: 600">文件夹</span |
| | | ><span style="margin-left: 5px; padding-bottom: 4px; font-size: 12px" |
| | | >管理您的个人文件夹</span |
| | | > |
| | | </div> |
| | | </template> |
| | | <template #tools> |
| | | <a-button type="primary" @click="insertEvent">新建文件夹</a-button> |
| | | </template> |
| | | </vxe-toolbar> |
| | | |
| | | <vxe-table |
| | | ref="xTable" |
| | | style="margin: 10px 0" |
| | | :data="demo.tableData" |
| | | @mounted="onMounted" |
| | | :row-config="{ keyField: 'id' }" |
| | | :column-config="{ resizable: true }" |
| | | :export-config="{}" |
| | | :tree-config="{ transform: true,rowField: 'rowId', parentField: 'parentRowId' }" |
| | | :edit-config="{ trigger: 'manual', mode: 'row' }" |
| | | height="600" |
| | | > |
| | | <vxe-column width="40"> |
| | | <template #default> |
| | | <span class="drag-btn"> |
| | | <HolderOutlined /> |
| | | </span> |
| | | </template> |
| | | </vxe-column> |
| | | <vxe-column field="folderName" title="文件夹名称" minWidth="250" tree-node :edit-render="{}"> |
| | | <template #edit="{ row }"> |
| | | <vxe-input |
| | | :ref="el => inputRefs[row.id] = el" |
| | | v-model="row.folderName" |
| | | type="text" |
| | | style="width: 300px" |
| | | @blur="fnInputHandle(row)" |
| | | ></vxe-input> |
| | | </template> |
| | | </vxe-column> |
| | | <vxe-column field="age" title="操作" width="250"> |
| | | <template #default="{ row }"> |
| | | <a style="margin-right: 10px" @click="insertRow(row)">添加子文件夹</a> |
| | | <a style="margin-right: 10px" @click="editRowEvent(row)">编辑</a> |
| | | <a style="margin-right: 10px" @click="fnDelete(row)">删除</a> |
| | | </template> |
| | | </vxe-column> |
| | | </vxe-table> |
| | | </div> |
| | | </template> |
| | | |
| | | <script lang="ts" setup> |
| | | import { ref, computed, onMounted, nextTick, onUnmounted, reactive } from 'vue'; |
| | | import { |
| | | addFolderApi, |
| | | deleteFolderApi, |
| | | updateFolderApi, |
| | | getFolderApi, |
| | | } from '@/api/email/userList'; |
| | | |
| | | // 排序 |
| | | import { HolderOutlined } from '@ant-design/icons-vue'; |
| | | import Sortable from 'sortablejs'; |
| | | let sortable: any; |
| | | const demo = reactive({ |
| | | showHelpTip: false, |
| | | tableData: [], |
| | | }); |
| | | const xTable = ref(); |
| | | const rowDrop = () => { |
| | | const $table = xTable.value; |
| | | sortable = Sortable.create($table.$el.querySelector('.body--wrapper>.vxe-table--body tbody'), { |
| | | handle: '.drag-btn', |
| | | onEnd: (sortableEvent) => { |
| | | const newIndex = sortableEvent.newIndex as number; |
| | | const oldIndex = sortableEvent.oldIndex as number; |
| | | const currRow: Record<string, any> = demo.tableData.splice(oldIndex, 1)[0]; |
| | | // demo.tableData.splice(newIndex, 0, currRow); |
| | | updateFolderApi({ |
| | | textId: currRow.textId, |
| | | textName: currRow.textName, |
| | | content: currRow.content, |
| | | sortId: newIndex, |
| | | }) |
| | | .then(() => { |
| | | fnGetList(); |
| | | }) |
| | | .catch(() => {}); |
| | | }, |
| | | }); |
| | | }; |
| | | |
| | | let initTime: any; |
| | | nextTick(() => { |
| | | // 加载完成之后在绑定拖动事件 |
| | | initTime = setTimeout(() => { |
| | | rowDrop(); |
| | | }, 500); |
| | | }); |
| | | |
| | | onUnmounted(() => { |
| | | clearTimeout(initTime); |
| | | if (sortable) { |
| | | sortable.destroy(); |
| | | } |
| | | }); |
| | | |
| | | function fnGetList() { |
| | | getFolderApi({}).then((res) => { |
| | | console.log(res); |
| | | demo.tableData = res.data; |
| | | }); |
| | | } |
| | | |
| | | const inputRefs = ref<{ [key: number]: HTMLElement | null }>({}); |
| | | function insertEvent() { |
| | | const $table = xTable.value; |
| | | const rid = Date.now(); |
| | | const record = { |
| | | folderName: `新数据${rid}`, |
| | | id: rid, |
| | | }; |
| | | $table.insert(record).then(({ row }) => $table.setEditRow(row)); |
| | | setTimeout(() => { |
| | | inputRefs.value[rid].focus(); |
| | | }, 300); |
| | | } |
| | | import { useMessage } from '@/hooks/web/useMessage'; |
| | | |
| | | const { createMessage } = useMessage(); |
| | | function fnInputHandle(row) { |
| | | console.log(row, '----333'); |
| | | const data = { |
| | | folderName: row.folderName, |
| | | parentRowId: row.rowId, |
| | | }; |
| | | addFolderApi(data).then((res) => { |
| | | if (res.code == 0) { |
| | | createMessage.success('添加成功'); |
| | | fnGetList(); |
| | | } else { |
| | | createMessage.error(res.msg); |
| | | } |
| | | }); |
| | | } |
| | | async function insertRow(row) { |
| | | const $table = xTable.value; |
| | | const rid = Date.now(); |
| | | const record = { |
| | | folderName: `新数据${rid}`, |
| | | id: rid, |
| | | parentRowId: row.rowId, // 需要指定父节点,自动插入该节点中 |
| | | }; |
| | | const { row: newRow } = await $table.insert(record); |
| | | console.log(row,'99999993'); |
| | | |
| | | await $table.setTreeExpand(row, true); // 将父节点展开 |
| | | await $table.setEditRow(newRow); // 插入子节点 |
| | | } |
| | | function fnDelete(row) { |
| | | deleteFolderApi({ folderId: row.folderId }) |
| | | .then((res) => { |
| | | if (res.code == 0) { |
| | | fnGetList(); |
| | | createMessage.success(res.msg); |
| | | } |
| | | }) |
| | | .catch((err) => { |
| | | // createMessage.error(err); |
| | | }); |
| | | } |
| | | |
| | | function editRowEvent(row) { |
| | | const $table = xTable.value; |
| | | console.log(row,'---30494'); |
| | | |
| | | $table.setEditRow(row); |
| | | } |
| | | onMounted(() => { |
| | | fnGetList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .sortable-row-demo .drag-btn { |
| | | font-size: 12px; |
| | | cursor: move; |
| | | } |
| | | |
| | | .sortable-row-demo .vxe-body--row.sortable-ghost, |
| | | .sortable-row-demo .vxe-body--row.sortable-chosen { |
| | | background-color: #dfecfb; |
| | | } |
| | | </style> |
| | |
| | | <a-tab-pane key="3" tab="快速文本" |
| | | ><QuickText v-if="activeKey === '3'"></QuickText |
| | | ></a-tab-pane> |
| | | <a-tab-pane key="4" tab="文件夹">Content of Tab Pane 3</a-tab-pane> |
| | | <a-tab-pane key="5" tab="标签">Content of Tab Pane 3</a-tab-pane> |
| | | <a-tab-pane key="6" tab="黑名单">Content of Tab Pane 3</a-tab-pane> |
| | | <a-tab-pane key="4" tab="文件夹"><Folder v-if="activeKey === '4'" /></a-tab-pane> |
| | | <a-tab-pane key="5" tab="标签"><Label v-if="activeKey==='5'" /></a-tab-pane> |
| | | <a-tab-pane key="6" tab="黑名单"><Blacklist v-if="activeKey === '6'" /></a-tab-pane> |
| | | </a-tabs> |
| | | </div> |
| | | </template> |
| | |
| | | import MailboxManagement from './mailboxManagement.vue'; |
| | | import Convention from './convention.vue'; |
| | | import QuickText from './quickText.vue'; |
| | | import Folder from './folder.vue'; |
| | | import Label from './label.vue'; |
| | | import Blacklist from './blacklist.vue' |
| | | </script> |
| | | <style scoped lang="less"></style> |
New file |
| | |
| | | <template> |
| | | <div class="p-2"> |
| | | <vxe-toolbar> |
| | | <template #buttons> |
| | | <div style="display: flex; align-items: flex-end"> |
| | | <span style="font-size: 1.25rem; font-weight: 600">我的标签</span |
| | | ><span style="margin-left: 5px; padding-bottom: 4px; font-size: 12px" |
| | | >管理您的所有个人标签</span |
| | | > |
| | | </div> |
| | | </template> |
| | | <template #tools> |
| | | <a-button type="primary" @click="showModal('add')">新建个人标签</a-button> |
| | | </template> |
| | | </vxe-toolbar> |
| | | |
| | | <vxe-table |
| | | ref="xTable" |
| | | style="margin: 10px 0" |
| | | :data="demo.tableData" |
| | | @mounted="onMounted" |
| | | height="600" |
| | | > |
| | | <vxe-column width="40"> |
| | | <template #default> |
| | | <span class="drag-btn"> |
| | | <HolderOutlined /> |
| | | </span> |
| | | </template> |
| | | </vxe-column> |
| | | <vxe-column field="TagName" title="文件夹名称" minWidth="250"> |
| | | <template #default="{ row }"> |
| | | <span class="my-d-f"> |
| | | <div |
| | | v-if="row.systemFlag" |
| | | class="bookmark" |
| | | :style="{ backgroundColor: row.tagColor }" |
| | | ></div> |
| | | <div v-else> |
| | | <ColorPicker |
| | | v-model="row.tagColor" |
| | | :type="2" |
| | | @change="fnRowColorChange($event, row)" |
| | | ></ColorPicker> |
| | | </div> |
| | | <a-tag class="ml-5" :color="row.tagColor">{{ row.tagName }}</a-tag> |
| | | </span> |
| | | </template> |
| | | </vxe-column> |
| | | <vxe-column field="age" title="操作" width="150"> |
| | | <template #default="{ row }"> |
| | | <a style="margin-right: 10px" @click="showModal('edit', row)">编辑</a> |
| | | <a style="margin-right: 10px" @click="fnDeleteRow(row)">删除</a> |
| | | </template> |
| | | </vxe-column> |
| | | </vxe-table> |
| | | <a-modal :width="300" v-model:open="open" :title="`${title}个人标签`" @ok="handleOk"> |
| | | <a-form ref="formRef" :model="form" style="margin-top: 20px"> |
| | | <a-form-item label="标签颜色" name="tagColor"> |
| | | <ColorPicker v-model="form.tagColor" :type="2"></ColorPicker> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="标签名称" |
| | | name="tagName" |
| | | :rules="[{ required: true, message: '请输入标签名称', trigger: 'blur' }]" |
| | | > |
| | | <a-input v-model:value="form.tagName" placeholder="请输入标签名称" /> |
| | | </a-form-item> |
| | | </a-form> |
| | | </a-modal> |
| | | </div> |
| | | </template> |
| | | |
| | | <script lang="ts" setup> |
| | | import { ref, computed, onMounted, nextTick, onUnmounted, reactive } from 'vue'; |
| | | import ColorPicker from '@/components/ColorPicker/index.vue'; |
| | | import { addTagApi, deleteTagApi, updateTagApi, getTagApi } from '@/api/email/userList'; |
| | | |
| | | // 排序 |
| | | import { HolderOutlined } from '@ant-design/icons-vue'; |
| | | import Sortable from 'sortablejs'; |
| | | let sortable: any; |
| | | const demo = reactive({ |
| | | showHelpTip: false, |
| | | tableData: [], |
| | | }); |
| | | const xTable = ref(); |
| | | const rowDrop = () => { |
| | | const $table = xTable.value; |
| | | sortable = Sortable.create($table.$el.querySelector('.body--wrapper>.vxe-table--body tbody'), { |
| | | handle: '.drag-btn', |
| | | onEnd: (sortableEvent) => { |
| | | const newIndex = sortableEvent.newIndex as number; |
| | | const oldIndex = sortableEvent.oldIndex as number; |
| | | const currRow: Record<string, any> = demo.tableData.splice(oldIndex, 1)[0]; |
| | | // demo.tableData.splice(newIndex, 0, currRow); |
| | | updateTagApi({ |
| | | tagColor: currRow.tagColor, |
| | | tagName: currRow.tagName, |
| | | sortId: newIndex, |
| | | tagId: currRow.tagId, |
| | | }) |
| | | .then(() => { |
| | | fnGetList(); |
| | | }) |
| | | .catch(() => {}); |
| | | }, |
| | | }); |
| | | }; |
| | | |
| | | let initTime: any; |
| | | nextTick(() => { |
| | | // 加载完成之后在绑定拖动事件 |
| | | initTime = setTimeout(() => { |
| | | rowDrop(); |
| | | }, 500); |
| | | }); |
| | | |
| | | onUnmounted(() => { |
| | | clearTimeout(initTime); |
| | | if (sortable) { |
| | | sortable.destroy(); |
| | | } |
| | | }); |
| | | |
| | | function fnGetList() { |
| | | getTagApi({}).then((res) => { |
| | | console.log(res); |
| | | demo.tableData = res.data; |
| | | }); |
| | | } |
| | | |
| | | import { useMessage } from '@/hooks/web/useMessage'; |
| | | |
| | | const { createMessage } = useMessage(); |
| | | |
| | | const open = ref(false); |
| | | const formRef = ref(); |
| | | |
| | | interface formType { |
| | | tagColor: string; |
| | | tagName: string; |
| | | tagType: number; |
| | | systemFlag: boolean; |
| | | tagId?: number; |
| | | } |
| | | |
| | | const defaultForm: formType = { |
| | | tagColor: '#000000', |
| | | tagName: '', |
| | | tagType: 1, |
| | | systemFlag: false, |
| | | }; |
| | | |
| | | const form = ref<formType>({ ...defaultForm }); |
| | | |
| | | const title = ref('新建'); |
| | | const signType = ref('add'); |
| | | |
| | | const showModal = (type: string, row) => { |
| | | signType.value = type; |
| | | open.value = true; |
| | | |
| | | if (type == 'add') { |
| | | form.value = { ...defaultForm }; |
| | | } else { |
| | | title.value = '编辑'; |
| | | nextTick(() => { |
| | | formRef.value.resetFields(); |
| | | form.value = { |
| | | tagColor: row.tagColor, |
| | | tagName: row.tagName, |
| | | tagType: row.tagType, |
| | | systemFlag: row.systemFlag, |
| | | tagId: row.tagId, |
| | | }; |
| | | }); |
| | | } |
| | | }; |
| | | |
| | | function fnDeleteRow(row) { |
| | | deleteTagApi({ tagId: row.tagId }).then((res) => { |
| | | if (res.code === 0) { |
| | | createMessage.success(res.msg); |
| | | fnGetList(); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | function handleOk() { |
| | | nextTick(() => { |
| | | formRef.value |
| | | .validate() |
| | | .then(() => { |
| | | const api = signType.value == 'add' ? addTagApi : updateTagApi; |
| | | api(form.value).then((res) => { |
| | | if (res.code === 0) { |
| | | createMessage.success(res.msg); |
| | | fnGetList(); |
| | | open.value = false; |
| | | } |
| | | }); |
| | | }) |
| | | .catch(() => {}); |
| | | }); |
| | | } |
| | | |
| | | function fnRowColorChange(color, row) { |
| | | console.log(color, row); |
| | | const data = { |
| | | tagColor: row.tagColor, |
| | | tagName: row.tagName, |
| | | tagType: row.tagType, |
| | | systemFlag: row.systemFlag, |
| | | tagId: row.tagId, |
| | | }; |
| | | updateTagApi(data).then((res) => { |
| | | if (res.code === 0) { |
| | | createMessage.success(res.msg); |
| | | fnGetList(); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | onMounted(() => { |
| | | fnGetList(); |
| | | }); |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .sortable-row-demo .drag-btn { |
| | | font-size: 12px; |
| | | cursor: move; |
| | | } |
| | | |
| | | .sortable-row-demo .vxe-body--row.sortable-ghost, |
| | | .sortable-row-demo .vxe-body--row.sortable-chosen { |
| | | background-color: #dfecfb; |
| | | } |
| | | |
| | | .bookmark { |
| | | display: inline-block; |
| | | position: relative; |
| | | width: 14px; |
| | | height: 18px; |
| | | margin-right: 5px; |
| | | border-radius: 2px 2px 0 0; /* Rounded top corners */ |
| | | } |
| | | |
| | | .bookmark::after { |
| | | content: ''; |
| | | position: absolute; |
| | | right: 0; |
| | | bottom: 0; |
| | | left: 0; |
| | | height: 8px; |
| | | background-color: #fff; |
| | | clip-path: polygon(50% 60%, 0% 100%, 100% 100%); /* Triangle at the bottom */ |
| | | } |
| | | </style> |
| | |
| | | <template> |
| | | <PageWrapper dense contentFullHeight fixedHeight> |
| | | <div style="height: 100vh; border-inline-end: 1px solid rgb(5 5 5 / 6%)"> |
| | | <div> |
| | | <div style="height: 15vh; padding: 20px 40px; text-align: center"> |
| | | <span style="display: flex; justify-content: space-around"> |
| | | <a-button shape="circle" size="large"> |
| | |
| | | <a-sub-menu v-if="item.children" :key="item.key"> |
| | | <template #title> |
| | | <div class="my-display"> |
| | | <span>{{ item.title }}</span |
| | | > |
| | | <span>{{ item.title }}</span> |
| | | <!-- <span class="my-left" v-if="item.total > 0">{{ item.total }}</span> --> |
| | | </div> |
| | | </template> |
| | |
| | | </a-sub-menu> |
| | | <a-menu-item v-else :key="item.key" @click="handleClick(item)"> |
| | | <div class="my-display"> |
| | | <span>{{ item.title }}</span |
| | | > |
| | | <span>{{ item.title }}</span> |
| | | <span class="my-left" v-if="item.total > 0">{{ item.total }}</span> |
| | | </div> |
| | | </a-menu-item> |
| | |
| | | const routesConfig = { |
| | | InboxPage1: '/email/index', |
| | | receiver: '/email/Inbox/list', |
| | | sender: '/email/outbox', |
| | | IndexPage1:'/email/outbox' |
| | | }; |
| | | // 点击事件处理 |
| | | const router = useRouter(); |
| | |
| | | router.push(`${routesConfig[e.key]}?${e.title}`); |
| | | matched = true; |
| | | return; // 跳出当前循环 |
| | | case 'sender': |
| | | router.push(`${routesConfig[e.key]}?${e.title}`); |
| | | matched = true; |
| | | return; // 跳出当前循环 |
| | | case 'IndexPage1': |
| | | router.push(`${routesConfig[e.key]}`); |
| | | matched = true; |
| | | return; // 跳出当前循环 |
| | | default: |
| | | // 处理默认情况,例如记录日志或抛出警告 |
| | | console.warn(`Unknown key: ${e.key}`); |
| | |
| | | @close="drawerClose" |
| | | > |
| | | <template #title> |
| | | <pageHeadLeft |
| | | :checked="true" |
| | | :selectAllRow="[{ docCode }]" |
| | | @nextNum="drawerClose" |
| | | ></pageHeadLeft> |
| | | <div class="ctb"> |
| | | <pageHeadLeft |
| | | :checked="true" |
| | | :selectAllRow="[{ docCode }]" |
| | | @nextNum="drawerClose" |
| | | ></pageHeadLeft> |
| | | <div class="ct-top"> |
| | | <div class="title" style="margin-bottom: 20px"> |
| | | <div class="left"> |
| | | <span style="margin-right: 20px; font-size: 24px; font-weight: 700"> |
| | | <a-tooltip placement="bottom"> |
| | | <template #title> |
| | | <span>{{ tableRowData.subject }}</span> |
| | | </template> |
| | | {{ truncateString(tableRowData.subject, 23) }} |
| | | </a-tooltip> |
| | | </span> |
| | | <span style="margin-right: 10px; font-size: 16px"> |
| | | <PushpinOutlined /> |
| | | </span> |
| | | </div> |
| | | <div class="right"> |
| | | <div class="tate">{{ formatToDateDay(tableRowData.receiveTime) }}</div> |
| | | <div> |
| | | <a-dropdown-button> |
| | | <span> |
| | | <a-tooltip placement="bottom"> |
| | | <template #title> |
| | | <span>回复</span> |
| | | </template> |
| | | <LeftOutlined @click="replyEmail(tableRowData)" /> |
| | | </a-tooltip> |
| | | </span> |
| | | <a-divider type="vertical" /> |
| | | <span> |
| | | <a-tooltip placement="bottom"> |
| | | <template #title> |
| | | <span>快速回复</span> |
| | | </template> |
| | | <DoubleLeftOutlined /> |
| | | </a-tooltip> |
| | | </span> |
| | | <template #overlay> |
| | | <a-menu> |
| | | <a-menu-item key="1"> |
| | | <UserOutlined /> |
| | | 1st menu item |
| | | </a-menu-item> |
| | | <a-menu-item key="2"> |
| | | <UserOutlined /> |
| | | 2nd menu item |
| | | </a-menu-item> |
| | | <a-menu-item key="3"> |
| | | <UserOutlined /> |
| | | 3rd item |
| | | </a-menu-item> |
| | | </a-menu> |
| | | </template> |
| | | </a-dropdown-button> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div></div> |
| | | </template> |
| | | <template #extra> |
| | | <div style="font-size: 16px"> |
| | |
| | | </a-button> |
| | | </div> |
| | | </template> |
| | | <div |
| | | style=" |
| | | position: fixed; |
| | | z-index: 99; |
| | | top: 6.8%; |
| | | width: 72%; |
| | | padding-top: 24px; |
| | | background: #fff; |
| | | " |
| | | > |
| | | <div class="title" style="margin-bottom: 20px"> |
| | | <div class="left"> |
| | | <span style="margin-right: 20px; font-size: 24px; font-weight: 700"> |
| | | <a-tooltip placement="bottom"> |
| | | <template #title> |
| | | <span>{{ tableRowData.subject }}</span> |
| | | </template> |
| | | {{ truncateString(tableRowData.subject, 23) }} |
| | | </a-tooltip> |
| | | </span> |
| | | <span style="margin-right: 10px; font-size: 16px"> |
| | | <PushpinOutlined /> |
| | | </span> |
| | | </div> |
| | | <div class="right"> |
| | | <div class="tate">{{ formatToDateDay(tableRowData.receiveTime) }}</div> |
| | | <div> |
| | | <a-dropdown-button> |
| | | <span> |
| | | <a-tooltip placement="bottom"> |
| | | <template #title> |
| | | <span>回复</span> |
| | | </template> |
| | | <LeftOutlined /> |
| | | </a-tooltip> |
| | | </span> |
| | | <a-divider type="vertical" /> |
| | | <span> |
| | | <a-tooltip placement="bottom"> |
| | | <template #title> |
| | | <span>快速回复</span> |
| | | </template> |
| | | <DoubleLeftOutlined /> |
| | | </a-tooltip> |
| | | </span> |
| | | <template #overlay> |
| | | <a-menu> |
| | | <a-menu-item key="1"> |
| | | <UserOutlined /> |
| | | 1st menu item |
| | | </a-menu-item> |
| | | <a-menu-item key="2"> |
| | | <UserOutlined /> |
| | | 2nd menu item |
| | | </a-menu-item> |
| | | <a-menu-item key="3"> |
| | | <UserOutlined /> |
| | | 3rd item |
| | | </a-menu-item> |
| | | </a-menu> |
| | | </template> |
| | | </a-dropdown-button> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="flex-between"> |
| | | <div class="ct-left p-2" :class="isOpen ? 'isOpen' : 'onOpen'"> |
| | | <div class="user p-1"> |
| | | <div style="display: flex; align-items: center"> |
| | | <a-avatar size="small" style="margin-right: 8px" src="#" /> |
| | | {{ tableRowData.sender }} |
| | | <span>{{ `<${tableRowData.sender}>` }}</span> |
| | | <span style="margin: 0 10px">发送</span> |
| | | <a-popover placement="bottom"> |
| | | <template #content> |
| | | <div |
| | | class="p-2" |
| | | style=" |
| | | display: flex; |
| | | align-items: center; |
| | | border-bottom: 1px solid rgb(5 5 5 / 6%); |
| | | " |
| | | > |
| | | <a-avatar size="small" style="margin-right: 8px" src="#" /> |
| | | <span style="color: #000; font-weight: 700"> |
| | | {{ `${tableRowData.receiver}` }}</span |
| | | > |
| | | <CopyOutlined /> |
| | | </div> |
| | | <div class="display-flex p-2"> |
| | | <a-button type="link" size="small">往来邮件</a-button> |
| | | </div> |
| | | </template> |
| | | <div> |
| | | <div class="flex-between"> |
| | | <div class="ct-left p-2" :class="isOpen ? 'isOpen' : 'onOpen'"> |
| | | <div class="user p-1"> |
| | | <div style="display: flex; align-items: center"> |
| | | <a-avatar size="small" style="margin-right: 8px" src="#" /> |
| | | {{ `${tableRowData.receiver}` }}<span>{{ `<${tableRowData.receiver}>` }}</span> |
| | | </a-popover> |
| | | </div> |
| | | <div |
| | | type="info" |
| | | class="p-2" |
| | | style="margin-top: 10px; background-color: #e4f1ff; font-size: 14px" |
| | | > |
| | | <span>{{ `<${tableRowData.sender}>` }}</span> |
| | | <span>暂未查询到该客户的当地时间</span> |
| | | <!-- <span>2024-06-08 22:22</span> --> |
| | | </div> |
| | | <div class="ct" v-if="tableRowData.content"> |
| | | <TinymcePw ref="TinymcePwRef" v-model="tableRowData.content" /> |
| | | {{ tableRowData.sender }} |
| | | <span>{{ `<${tableRowData.sender}>` }}</span> |
| | | <span style="margin: 0 10px">发送</span> |
| | | <a-popover placement="bottom"> |
| | | <template #content> |
| | | <div |
| | | class="p-2" |
| | | style=" |
| | | display: flex; |
| | | align-items: center; |
| | | border-bottom: 1px solid rgb(5 5 5 / 6%); |
| | | " |
| | | > |
| | | <a-avatar size="small" style="margin-right: 8px" src="#" /> |
| | | <span style="color: #000; font-weight: 700"> |
| | | {{ `${tableRowData.receiver}` }}</span |
| | | > |
| | | <CopyOutlined /> |
| | | </div> |
| | | <div class="display-flex p-2"> |
| | | <a-button type="link" size="small">往来邮件</a-button> |
| | | </div> |
| | | </template> |
| | | <a-avatar size="small" style="margin-right: 8px" src="#" /> |
| | | {{ `${tableRowData.receiver}` }}<span>{{ `<${tableRowData.receiver}>` }}</span> |
| | | </a-popover> |
| | | </div> |
| | | <div |
| | | type="info" |
| | | class="p-2" |
| | | style="margin-top: 10px; background-color: #e4f1ff; font-size: 14px" |
| | | > |
| | | <span>{{ `<${tableRowData.sender}>` }}</span> |
| | | <span>暂未查询到该客户的当地时间</span> |
| | | <!-- <span>2024-06-08 22:22</span> --> |
| | | </div> |
| | | <div class="ct" v-if="tableRowData.content"> |
| | | <TinymcePw ref="TinymcePwRef" v-model="tableRowData.content" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div v-show="isOpen" class="ct-right p-2">sssss</div> |
| | | </div> |
| | | <div v-show="isOpen" class="ct-right p-2">sssss</div> |
| | | </div> |
| | | <div @click="fuToggleContent" class="toggle-btn" :class="isOpen ? 'onIconOpen' : 'iconOpen'"> |
| | | <LeftOutlined v-if="!isOpen" /> |
| | | <RightOutlined v-else /> |
| | | <div @click="fuToggleContent" class="toggle-btn" :class="isOpen ? 'onIconOpen' : 'iconOpen'"> |
| | | <LeftOutlined v-if="!isOpen" /> |
| | | <RightOutlined v-else /> |
| | | </div> |
| | | </div> |
| | | </a-drawer> |
| | | </template> |
| | |
| | | } |
| | | }); |
| | | } |
| | | import { useRouter } from 'vue-router'; |
| | | const router = useRouter(); |
| | | function replyEmail(row) { |
| | | router.push({ path: '/email/edit', query: { docCode: row.docCode, type: 'reply' } }); |
| | | } |
| | | </script> |
| | | |
| | | <style scoped lang="less"> |
| | |
| | | |
| | | .flex-between { |
| | | display: flex; |
| | | margin-top: 4%; |
| | | padding-top: 4%; |
| | | } |
| | | |
| | | .ct-left { |
| | |
| | | .onIconOpen { |
| | | right: 32%; |
| | | } |
| | | |
| | | .ctb { |
| | | position: relative; |
| | | } |
| | | |
| | | .ctb .ct-top { |
| | | position: absolute; |
| | | z-index: 99; |
| | | top: 41px; |
| | | left: -28px; |
| | | width: 116%; |
| | | padding: 10px; |
| | | background: #fff; |
| | | } |
| | | </style> |
| | |
| | | <template> |
| | | <PageWrapper> |
| | | <div style="height: calc(100vh - 84px)"> |
| | | <div class="head"> |
| | | <div class="my-head"> |
| | | <div class="left"> |
| | | <div class="left-box p-3"> |
| | | <!-- 多选 --> |
| | |
| | | </div> |
| | | |
| | | <div class="right p-3" |
| | | >共<span style="padding: 0 5px">20</span>封 |
| | | >共<span style="padding: 0 5px">{{page.total}}</span>封 |
| | | <a-pagination |
| | | v-model:current="pageCurrent" |
| | | v-model:page-size='page.limit' |
| | | simple |
| | | :total="50" |
| | | :total="page.total" |
| | | style="margin-left: 10px" |
| | | @change="handlePageChange" |
| | | /> |
| | | <FilterOutlined style="margin-left: 10px" /> |
| | | <a-popover placement="left" trigger="click"> |
| | |
| | | import pageHeadLeft from './pageHeadLeft.vue'; |
| | | import { PageWrapper } from '@/components/Page'; |
| | | |
| | | import { ref, watch, defineProps, defineEmits, computed, reactive, onMounted } from 'vue'; |
| | | import { ref, watch, defineProps, defineEmits, computed, reactive, onMounted,inject } from 'vue'; |
| | | |
| | | // 定义属性 |
| | | interface Props { |
| | | pageList: []; |
| | | pageList?: []; |
| | | pageData?:any; |
| | | } |
| | | const props = defineProps<Props>(); |
| | | const newList = ref([]); |
| | |
| | | }, |
| | | ); |
| | | |
| | | const page = computed(() => props.pageData); |
| | | const checked = computed(() => selectAllRow.value.length > 0); |
| | | const pageCurrent = ref(1); |
| | | const tableRef = ref(); |
| | |
| | | { |
| | | key: '1', |
| | | label: '全部', |
| | | num: 60, |
| | | num: 0, |
| | | }, |
| | | { |
| | | key: '2', |
| | | label: '客户', |
| | | num: 20, |
| | | num: 0, |
| | | }, |
| | | { |
| | | key: '3', |
| | |
| | | { |
| | | key: '5', |
| | | label: '其他', |
| | | num: 30, |
| | | num: 0, |
| | | }, |
| | | ]; |
| | | }); |
| | |
| | | function fnSelectAll() { |
| | | console.log('44444444444'); |
| | | } |
| | | const emit = defineEmits(['pageChange']); |
| | | defineExpose({ |
| | | fnSelectAll, |
| | | }); |
| | | |
| | | const getDataList = inject('getDataList'); |
| | | function handlePageChange(page, pageSize){ |
| | | getDataList(page) |
| | | |
| | | } |
| | | </script> |
| | | <style scoped lang="less"> |
| | | .head { |
| | | .my-head { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | width: 100%; |
| | | height: 60px; |
| | | border-bottom: 1px solid rgb(5 5 5 / 6%); |
| | | |
| | | /* 增加选择器特异性 */ |
| | |
| | | align-items: center; |
| | | justify-content: space-flex-start; |
| | | width: 100%; |
| | | height: 100%;; |
| | | |
| | | & .icon { |
| | | margin-right: 15px; |
| | |
| | | <template> |
| | | <div style="height: 70vh; overflow: auto"> |
| | | <div v-for="(item, index) in groupedEmails" :key="index"> |
| | | <span class="span-title">{{ `${item.name}(${item.data.length})` }}</span> |
| | | <vxe-table |
| | | ref="vxeTableRef" |
| | | style="margin: 10px 0" |
| | | :showHeader="false" |
| | | :data="item.data" |
| | | size="small" |
| | | min-height="40px" |
| | | :row-config="{ isCurrent: true, isHover: true }" |
| | | :menu-config="tableMenu" |
| | | @menu-click="contextMenuClickEvent" |
| | | @cell-click="cellClickEvent" |
| | | @checkbox-change="selectChangeEvent" |
| | | > |
| | | <vxe-column type="checkbox" width="30"></vxe-column> |
| | | <vxe-column field="sender" title="发件人" data-index="sender" min-width="300px"> |
| | | <template #default="{ row }"> |
| | | <div style="display: flex; align-items: center"> |
| | | <div |
| | | v-if="row.mailType != 0" |
| | | class="dot" |
| | | :class="row.readFlag ? 'dot-color' : ''" |
| | | @click.stop="fnRowUpdateRead(row)" |
| | | ></div> |
| | | <a-tooltip placement="bottom"> |
| | | <template #title> |
| | | <span>陌生人</span> |
| | | </template> |
| | | <a-avatar size="small" style="margin-right: 8px" :src="row.avatar" /> |
| | | </a-tooltip> |
| | | <div style="overflow: auto"> |
| | | <div v-if="groupedEmails.length != 0"> |
| | | <div v-for="(item, index) in groupedEmails" :key="index"> |
| | | <div class="span-title">{{ `${item.name}(${item.data.length})` }}</div> |
| | | <vxe-table |
| | | ref="vxeTableRef" |
| | | style="margin: 10px 0" |
| | | :showHeader="false" |
| | | :data="item.data" |
| | | size="small" |
| | | min-height="40px" |
| | | :row-config="{ isCurrent: true, isHover: true }" |
| | | :menu-config="tableMenu" |
| | | @menu-click="contextMenuClickEvent" |
| | | @cell-click="cellClickEvent" |
| | | @checkbox-change="selectChangeEvent" |
| | | > |
| | | <vxe-column type="checkbox" width="30"></vxe-column> |
| | | <vxe-column field="sender" title="发件人" data-index="sender" min-width="300px"> |
| | | <template #default="{ row }"> |
| | | <div style="display: flex; align-items: center"> |
| | | <div |
| | | v-if="row.mailType != 0" |
| | | class="dot" |
| | | :class="row.readFlag ? 'dot-color' : ''" |
| | | @click.stop="fnRowUpdateRead(row)" |
| | | ></div> |
| | | <a-tooltip placement="bottom"> |
| | | <template #title> |
| | | <span>陌生人</span> |
| | | </template> |
| | | <a-avatar size="small" style="margin-right: 8px" :src="row.avatar" /> |
| | | </a-tooltip> |
| | | |
| | | <a-popover placement="bottom"> |
| | | <template #content> |
| | | <div |
| | | class="p-2" |
| | | style=" |
| | | display: flex; |
| | | align-items: center; |
| | | border-bottom: 1px solid rgb(5 5 5 / 6%); |
| | | " |
| | | > |
| | | <a-avatar size="small" style="margin-right: 8px" :src="row.avatar" /> |
| | | <span style="color: #000; font-weight: 700">{{ row.sender }}</span> |
| | | <CopyOutlined /> |
| | | <a-popover placement="bottom"> |
| | | <template #content> |
| | | <div |
| | | class="p-2" |
| | | style=" |
| | | display: flex; |
| | | align-items: center; |
| | | border-bottom: 1px solid rgb(5 5 5 / 6%); |
| | | " |
| | | > |
| | | <a-avatar size="small" style="margin-right: 8px" :src="row.avatar" /> |
| | | <span style="color: #000; font-weight: 700">{{ row.sender }}</span> |
| | | <CopyOutlined /> |
| | | </div> |
| | | <div class="display-flex p-2"> |
| | | <a-button type="link" size="small">新建客户</a-button> |
| | | <a-dropdown> |
| | | <a style="margin-right: 5px" class="ant-dropdown-link" @click.prevent> |
| | | <DownOutlined /> |
| | | </a> |
| | | <template #overlay> |
| | | <a-menu> |
| | | <a-menu-item> |
| | | <a href="javascript:;">添加到已有客户</a> |
| | | </a-menu-item> |
| | | </a-menu> |
| | | </template> |
| | | </a-dropdown> |
| | | <a-button type="link" size="small">添加为线索</a-button> |
| | | |
| | | <a-dropdown style="margin-right: 5px"> |
| | | <a style="margin-right: 5px" class="ant-dropdown-link" @click.prevent> |
| | | <DownOutlined /> |
| | | </a> |
| | | <template #overlay> |
| | | <a-menu> |
| | | <a-menu-item> |
| | | <a href="javascript:;">添加到通讯录</a> |
| | | </a-menu-item> |
| | | </a-menu> |
| | | </template> |
| | | </a-dropdown> |
| | | <a-button type="link" size="small">往来邮件</a-button></div |
| | | > |
| | | </template> |
| | | <div class="title-dot" :class="row.readFlag ? 'title-dot-color' : ''"> |
| | | <span style="font-weight: 700">{{ row.senderName }}</span |
| | | ><span style="padding: 0 8px">|</span> |
| | | <span style="font-weight: 500">{{ row.sender }}</span> |
| | | </div> |
| | | <div class="display-flex p-2"> |
| | | <a-button type="link" size="small">新建客户</a-button> |
| | | <a-dropdown> |
| | | <a style="margin-right: 5px" class="ant-dropdown-link" @click.prevent> |
| | | <DownOutlined /> |
| | | </a> |
| | | <template #overlay> |
| | | <a-menu> |
| | | <a-menu-item> |
| | | <a href="javascript:;">添加到已有客户</a> |
| | | </a-menu-item> |
| | | </a-menu> |
| | | </template> |
| | | </a-dropdown> |
| | | <a-button type="link" size="small">添加为线索</a-button> |
| | | </a-popover> |
| | | </div> |
| | | </template> |
| | | </vxe-column> |
| | | <vxe-column |
| | | show-overflow |
| | | field="subject" |
| | | title="表题" |
| | | data-index="subject" |
| | | min-width="250" |
| | | > |
| | | <template #default="{ row }"> |
| | | <span |
| | | class="title-dot" |
| | | :class="row.readFlag ? 'title-dot-color' : ''" |
| | | style="font-weight: 500" |
| | | >{{ row.subject || '(无主题)' }}</span |
| | | > |
| | | - |
| | | <span style="color: #999">{{ row.subject }}</span> |
| | | </template> |
| | | </vxe-column> |
| | | <vxe-column field="action" title="Action" width="190"> |
| | | <template #default="{ row, rowIndex }"> |
| | | <span style="display: flex; justify-content: space-around"> |
| | | <span>{{ |
| | | row.mailType !== 0 |
| | | ? formatToDateDay(row.receiveTime) |
| | | : formatToDateDay(row.createTime) |
| | | }}</span> |
| | | |
| | | <a-dropdown style="margin-right: 5px"> |
| | | <a style="margin-right: 5px" class="ant-dropdown-link" @click.prevent> |
| | | <DownOutlined /> |
| | | </a> |
| | | <template #overlay> |
| | | <a-menu> |
| | | <a-menu-item> |
| | | <a href="javascript:;">添加到通讯录</a> |
| | | </a-menu-item> |
| | | </a-menu> |
| | | </template> |
| | | </a-dropdown> |
| | | <a-button type="link" size="small">往来邮件</a-button></div |
| | | > |
| | | </template> |
| | | <div class="title-dot" :class="row.readFlag ? 'title-dot-color' : ''"> |
| | | <span style="font-weight: 700">{{ row.senderName }}</span |
| | | ><span style="padding: 0 8px">|</span> |
| | | <span style="font-weight: 500">{{ row.sender }}</span> |
| | | </div> |
| | | </a-popover> |
| | | </div> |
| | | </template> |
| | | </vxe-column> |
| | | <vxe-column show-overflow field="subject" title="表题" data-index="subject" min-width="250"> |
| | | <template #default="{ row }"> |
| | | <span |
| | | class="title-dot" |
| | | :class="row.readFlag ? 'title-dot-color' : ''" |
| | | style="font-weight: 500" |
| | | >{{ row.subject || '(无主题)' }}</span |
| | | > |
| | | - |
| | | <span style="color: #999">{{ row.subject }}</span> |
| | | </template> |
| | | </vxe-column> |
| | | <vxe-column field="action" title="Action" width="190"> |
| | | <template #default="{ row, rowIndex }"> |
| | | <span style="display: flex; justify-content: space-around"> |
| | | <span>{{ |
| | | row.mailType !== 0 |
| | | ? formatToDateDay(row.receiveTime) |
| | | : formatToDateDay(row.createTime) |
| | | }}</span> |
| | | <TooltipAndDropdown |
| | | :tooltipTitle="'待处理邮件'" |
| | | :initialDropdownOpen="false" |
| | | :initialTooltipOpen="false" |
| | | :showTooltip="!!row.handleTime" |
| | | :row="row" |
| | | :docCodeS="[row.docCode]" |
| | | /> |
| | | <span style="margin-left: 5px"><PushpinOutlined @click.stop="fnTagging" /></span> |
| | | </span> |
| | | </template> |
| | | </vxe-column> |
| | | </vxe-table> |
| | | </div |
| | | ></div> |
| | | |
| | | <TooltipAndDropdown |
| | | :tooltipTitle="'待处理邮件'" |
| | | :initialDropdownOpen="false" |
| | | :initialTooltipOpen="false" |
| | | :showTooltip="!!row.handleTime" |
| | | :row="row" |
| | | :docCodeS="[row.docCode]" |
| | | /> |
| | | <span style="margin-left: 5px"><PushpinOutlined @click.stop="fnTagging" /></span> |
| | | </span> |
| | | </template> |
| | | </vxe-column> |
| | | </vxe-table> |
| | | <div v-else style="height: 70vh; display: flex; align-items: center; justify-content: center"> |
| | | <a-empty /> |
| | | </div> |
| | | <DrawerDetail |
| | | ref="drawerDetailRef" |
| | |
| | | watch( |
| | | () => props.pageList, |
| | | (newValue) => { |
| | | dataSource.value = newValue; |
| | | groupedEmails.value = groupEmailsByDate(newValue); |
| | | dataSource.value = newValue || []; |
| | | groupedEmails.value = groupEmailsByDate(newValue || []); |
| | | }, |
| | | ); |
| | | import dayjs from 'dayjs'; |
| | |
| | | } |
| | | |
| | | .span-title { |
| | | padding: 20px; |
| | | width: 100%; |
| | | padding: 5px; |
| | | color: #000; |
| | | font-weight: 700; |
| | | text-align: left; |
| | | } |
| | | |
| | | .table { |
| | |
| | | @after-open-change="afterOpenChange" |
| | | @close="afterOpenChange" |
| | | > |
| | | <a-tabs v-model:activeKey="activeKey"> |
| | | <a-tabs v-model:activeKey="activeKey" @change="handleTabChange"> |
| | | <a-tab-pane :key="item.key" :tab="item.title" v-for="item in tabsList"> |
| | | <a-form :model="form" :rules="rules" layout="vertical"> |
| | | <a-row :gutter="16"> |
| | |
| | | }" |
| | | :scroll="{ y: 300 }" |
| | | size="small" |
| | | rowKey="email" |
| | | > |
| | | rowKey="userCode" |
| | | > |
| | | <!-- :pagination="{ pageSize: 100 }" --> |
| | | <a-table-column key="userName" title="name" data-index="name"> |
| | | <template #default="{ record }"> |
| | |
| | | |
| | | <script lang="ts" setup> |
| | | import { ref, watch, defineProps, defineEmits, computed, reactive } from 'vue'; |
| | | import { getUserInfoApi } from '@/api/email/userList'; |
| | | import { getUserInfoApi, contactListAPi, emailListAPi } from '@/api/email/userList'; |
| | | // 定义组件名称 |
| | | defineOptions({ name: 'SelectUser' }); |
| | | |
| | |
| | | ]); |
| | | |
| | | // 定义事件 |
| | | const emit = defineEmits(['update:modelValue', 'updateData','sudUserList']); |
| | | const emit = defineEmits(['update:modelValue', 'updateData', 'sudUserList']); |
| | | const gutUserApiList = ref([ |
| | | { key: '3', api: emailListAPi }, |
| | | { key: '2', api: contactListAPi }, |
| | | ]); |
| | | |
| | | const gutUserApi = ref<Record<string, any>>(emailListAPi); |
| | | function handleTabChange(key: string) { |
| | | gutUserApi.value = gutUserApiList.value.find((item) => item.key === key)?.api || emailListAPi; |
| | | fnGetUserList(); |
| | | } |
| | | |
| | | function fnGetUserList() { |
| | | gutUserApi.value({ key: '' }).then((res) => { |
| | | if (res.state === 0) { |
| | | dataSource.value = res.data; |
| | | if (res.data.data) { |
| | | dataSource.value = res.data.data; |
| | | } |
| | | selectContactNum.value = dataSource.value.length; |
| | | // emit('sudUserList', dataSource.value); |
| | | } |
| | | }); |
| | | } |
| | | // 内部状态 |
| | | const drawerOpen = ref(props.modelValue); |
| | | |
| | |
| | | // 方法 |
| | | const afterOpenChange = (bool: boolean) => { |
| | | if (bool) { |
| | | fnGetUserList({}); |
| | | fnGetUserList(); |
| | | } |
| | | }; |
| | | interface User { |
| | |
| | | userName: string; |
| | | email: string; |
| | | } |
| | | const fnGetUserList = (params) => |
| | | getUserInfoApi(params).then((res) => { |
| | | if (res && res.data && Array.isArray(res.data)) { |
| | | dataSource.value = flattenAndDeduplicateData(res.data) || ([] as User[]); |
| | | selectContactNum.value = dataSource.value.length; |
| | | emit('sudUserList', dataSource.value) |
| | | } else { |
| | | console.error('Invalid response format:', res); |
| | | } |
| | | }); |
| | | // const fnGetUserList = (params) => |
| | | // getUserInfoApi(params).then((res) => { |
| | | // if (res && res.data && Array.isArray(res.data)) { |
| | | // dataSource.value = flattenAndDeduplicateData(res.data) || ([] as User[]); |
| | | // selectContactNum.value = dataSource.value.length; |
| | | // emit('sudUserList', dataSource.value); |
| | | // } else { |
| | | // console.error('Invalid response format:', res); |
| | | // } |
| | | // }); |
| | | |
| | | function flattenAndDeduplicateData(data) { |
| | | const result: Record<string, any>[] = []; |
| | |
| | | const fnSelectAllCurrentPage = (e) => { |
| | | const temp = dataSource.value; |
| | | if (selectAllCurrentPage.value) { |
| | | state.selectedRowKeys = temp.map((item) => item.id); |
| | | state.selectedRowKeys = temp.map((item) => item.userCode); |
| | | } else { |
| | | state.selectedRowKeys = state.selectedRowKeys.filter( |
| | | (item) => !temp.some((t) => t.id === item), |
| | | (item) => !temp.some((t) => t.userCode === item), |
| | | ); |
| | | } |
| | | updateTempDataSource(state.selectedRowKeys); |
| | |
| | | const fnSelectEmail = (e) => { |
| | | const temp = dataSource.value.slice(0, 30); |
| | | if (selectEmail.value) { |
| | | state.selectedRowKeys = temp.map((item) => item.id); |
| | | state.selectedRowKeys = temp.map((item) => item.userCode); |
| | | } else { |
| | | state.selectedRowKeys = state.selectedRowKeys.filter( |
| | | (item) => !temp.some((t) => t.id === item), |
| | | (item) => !temp.some((t) => t.userCode === item), |
| | | ); |
| | | } |
| | | updateTempDataSource(state.selectedRowKeys); |
| | | }; |
| | | const updateTempDataSource = (selectedRowKeys: Key[]) => { |
| | | tempDataSource.value = dataSource.value.filter((item) => selectedRowKeys.includes(item.id)); |
| | | tempDataSource.value = dataSource.value.filter((item) => selectedRowKeys.includes(item.userCode)); |
| | | contactNum.value = selectedRowKeys.length; |
| | | }; |
| | | |
| | |
| | | contactNum.value = 0; |
| | | }; |
| | | const onSelectChange = (selectedRowKeys: Key[]) => { |
| | | state.selectedRowKeys = selectedRowKeys; |
| | | |
| | | tempDataSource.value = dataSource.value.filter((item) => selectedRowKeys.includes(item.id)); |
| | | state.selectedRowKeys = dataSource.value.filter((item) => selectedRowKeys.includes(item.userCode));; |
| | | console.log('selectedRowKeys changed: ', state.selectedRowKeys); |
| | | |
| | | tempDataSource.value = dataSource.value.filter((item) => selectedRowKeys.includes(item.userCode)); |
| | | contactNum.value = selectedRowKeys.length; |
| | | |
| | | }; |
| | | const fnSaveOpenChange = () => { |
| | | console.log('selectedRowKeys changed: ', state.selectedRowKeys); |
| | |
| | | |
| | | <template> |
| | | <div> |
| | | <div class="default-theme" style="display: flex; height: 100%; background-color: #fff"> |
| | | <div v-show="collapseStore.isOpen" class="default-theme-left"> |
| | | <a-layout class="default-theme" style="display: flex; height: 100%; background-color: #fff"> |
| | | <a-layout-sider width="250" style="border-inline-end: 1px solid rgb(5 5 5 / 6%)" :style="siderStyle" v-show="collapseStore.isOpen"> |
| | | <LeftNav></LeftNav> |
| | | </div> |
| | | <div style="height: 100%" :class="collapseStore.isOpen ? 'onOpen' : 'isOpen'"> |
| | | </a-layout-sider> |
| | | <a-layout> |
| | | <a-layout-content :style="contentStyle"> |
| | | <RouterView> |
| | | <template #default="{ Component, route }"> |
| | | <transition |
| | |
| | | </transition> |
| | | </template> |
| | | </RouterView> |
| | | </div> |
| | | </div> |
| | | </a-layout-content> |
| | | </a-layout> |
| | | </a-layout> |
| | | <div @click="fuToggleContent(!collapseStore.isOpen)" class="toggle-btn" :class="collapseStore.isOpen ? 'iconOpen' : 'onIconOpen'"> |
| | | <LeftOutlined v-if="collapseStore.isOpen" /> |
| | | <RightOutlined v-else /> |
| | |
| | | Component: FunctionalComponent & { type: Recordable }; |
| | | route: RouteLocation; |
| | | } |
| | | const siderStyle = { |
| | | lineHeight: '120px', |
| | | backgroundColor:'#fff', |
| | | }; |
| | | const contentStyle = { |
| | | lineHeight: '120px', |
| | | backgroundColor:'#fff', |
| | | |
| | | }; |
| | | function getTransitionName({ |
| | | route, |
| | | openCache, |
| | |
| | | } |
| | | } |
| | | |
| | | .default-theme-left { |
| | | width: 20%; |
| | | height: 100%; |
| | | } |
| | | |
| | | .toggle-btn { |
| | | display: flex; |
| | |
| | | } |
| | | |
| | | .onOpen { |
| | | width: 80%; |
| | | width: 86vw; |
| | | } |
| | | |
| | | .isOpen { |
| | | width: 100%; |
| | | width: 100vw; |
| | | } |
| | | |
| | | .iconOpen { |
| | | left: 20%; |
| | | left: 250px; |
| | | } |
| | | |
| | | .onIconOpen { |
New file |
| | |
| | | <template> |
| | | <div> |
| | | <a-spin :spinning="loading" class="p-1" style="height: 100%;"> |
| | | <PageIndex :pageList="pageList" :mailType="1" :pageData="pageData" > </PageIndex> |
| | | </a-spin> |
| | | </div> |
| | | </template> |
| | | |
| | | <script lang="ts" setup> |
| | | name: 'outbox'; |
| | | import { ref, onMounted, computed, provide } from 'vue'; |
| | | import PageIndex from '@/views/email/components/ListPage/list.vue'; |
| | | import { useRoute } from 'vue-router'; |
| | | |
| | | const route = useRoute(); |
| | | // 获取当前完整 URL |
| | | const routerId = computed(() => { |
| | | try { |
| | | const url = window.location.href; |
| | | if (!url) { |
| | | throw new Error('Invalid URL'); |
| | | } |
| | | const basePart = url.split('?')[1]; |
| | | console.log(basePart, 'basePart'); |
| | | return basePart; |
| | | } catch (error) { |
| | | console.error('Error processing URL:', error); |
| | | // 返回默认值或空字符串 |
| | | return ''; |
| | | } |
| | | }); |
| | | import { getMailListApi } from '@/api/email/userList'; |
| | | const pageList = ref([]); |
| | | const loading = ref(false); |
| | | const pageData = ref({ |
| | | page: 1, |
| | | limit: 20, |
| | | total: 0, |
| | | }); |
| | | function getDataList(page:1) { |
| | | loading.value = true; |
| | | getMailListApi({ mail: routerId.value, mailType: 2,page }) |
| | | .then((res) => { |
| | | loading.value = false; |
| | | if (res.code == 0) { |
| | | pageList.value = res.data.list; |
| | | pageData.value.total = res.data.total; |
| | | } |
| | | }) |
| | | .catch(() => { |
| | | loading.value = false; |
| | | }); |
| | | } |
| | | |
| | | |
| | | provide('getDataList', getDataList); |
| | | onMounted(() => { |
| | | getDataList(); |
| | | }); |
| | | </script> |
| | | <style scoped lang="less"></style> |
| | |
| | | import { defineConfig, presetTypography, presetUno } from 'unocss'; |
| | | |
| | | export default defineConfig({ |
| | | rules: [['my-d-f', { display: 'flex', 'align-items': 'center' }]], |
| | | presets: [presetUno(), presetTypography()], |
| | | }); |