新增预警页面

This commit is contained in:
2025-12-14 21:54:26 +08:00
parent 0b40608761
commit f3da7da439
33 changed files with 1666 additions and 780 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

@@ -30,6 +30,9 @@ export interface BizMailAttachments extends BasicModel<BizMailAttachments> {
export const bizMailAttachmentsList = (params?: BizMailAttachments | any) =>
defHttp.get<BizMailAttachments>({ url: adminPath + '/biz/mailAttachments/list', params });
export const bizMailAttachmentsListAll = (params?: BizMailAttachments | any) =>
defHttp.get<BizMailAttachments[]>({ url: adminPath + '/biz/mailAttachments/listAll', params });
export const bizMailAttachmentsListData = (params?: BizMailAttachments | any) =>
defHttp.post<Page<BizMailAttachments>>({ url: adminPath + '/biz/mailAttachments/listData', params });
@@ -53,3 +56,4 @@ export const bizMailAttachmentsImportData = (
export const bizMailAttachmentsDelete = (params?: BizMailAttachments | any) =>
defHttp.get<BizMailAttachments>({ url: adminPath + '/biz/mailAttachments/delete', params });

View File

@@ -58,3 +58,7 @@ export const bizMailSentImportData = (
export const bizMailSentDelete = (params?: BizMailSent | any) =>
defHttp.get<BizMailSent>({ url: adminPath + '/biz/mailSent/delete', params });
export const bizMailSent = (params?: BizMailSent | any) =>
defHttp.get<BizMailSent>({ url: adminPath + '/biz/mailSent/sent', params });

View File

@@ -236,6 +236,7 @@
confirm: handleDelete.bind(this, record),
},
auth: 'biz:mailAccount:edit',
ifShow: record.ustatus == '0'
},
{
icon: 'ant-design:undo-outlined',

View File

@@ -1,180 +0,0 @@
import { useI18n } from '@jeesite/core/hooks/web/useI18n';
import { BasicColumn, BasicTableProps, FormProps } from '@jeesite/core/components/Table';
import { bizMailAccountListData } from '@jeesite/biz/api/biz/mailAccount';
const { t } = useI18n('biz.mailAccount');
const modalProps = {
title: t('邮件信息选择'),
};
const searchForm: FormProps<BizMailAccount> = {
baseColProps: { md: 8, lg: 6 },
labelWidth: 90,
schemas: [
{
label: t('记录时间起'),
field: 'createTime_gte',
component: 'DatePicker',
componentProps: {
format: 'YYYY-MM-DD HH:mm',
showTime: { format: 'HH:mm' },
},
},
{
label: t('记录时间止'),
field: 'createTime_lte',
component: 'DatePicker',
componentProps: {
format: 'YYYY-MM-DD HH:mm',
showTime: { format: 'HH:mm' },
},
},
{
label: t('服务地址'),
field: 'host',
component: 'Input',
},
{
label: t('用户名称'),
field: 'username',
component: 'Input',
},
{
label: t('发件人地址'),
field: 'fromAddress',
component: 'Input',
},
{
label: t('是否启用SSL'),
field: 'sslEnable',
component: 'Select',
componentProps: {
dictType: 'is_open',
allowClear: true,
},
},
{
label: t('状态'),
field: 'status',
component: 'Select',
componentProps: {
dictType: 'ustatus',
allowClear: true,
},
},
],
};
const tableColumns: BasicColumn<BizMailAccount>[] = [
{
title: t('记录时间'),
dataIndex: 'createTime',
key: 'a.create_time',
sorter: true,
width: 230,
align: 'left',
slot: 'firstColumn',
},
{
title: t('服务地址'),
dataIndex: 'host',
key: 'a.host',
sorter: true,
width: 130,
align: 'left',
},
{
title: t('SMTP端口'),
dataIndex: 'smtpPort',
key: 'a.smtp_port',
sorter: true,
width: 130,
align: 'center',
},
{
title: t('IMAP端口'),
dataIndex: 'imapPort',
key: 'a.imap_port',
sorter: true,
width: 130,
align: 'center',
},
{
title: t('用户名称'),
dataIndex: 'username',
key: 'a.username',
sorter: true,
width: 130,
align: 'left',
},
{
title: t('用户密码'),
dataIndex: 'password',
key: 'a.password',
sorter: true,
width: 130,
align: 'left',
},
{
title: t('发件人地址'),
dataIndex: 'fromAddress',
key: 'a.from_address',
sorter: true,
width: 130,
align: 'left',
},
{
title: t('是否启用SSL'),
dataIndex: 'sslEnable',
key: 'a.ssl_enable',
sorter: true,
width: 130,
align: 'center',
dictType: 'is_open',
},
{
title: t('状态'),
dataIndex: 'status',
key: 'a.status',
sorter: true,
width: 130,
align: 'center',
dictType: 'ustatus',
},
{
title: t('备注'),
dataIndex: 'remark',
key: 'a.remark',
sorter: true,
width: 130,
align: 'left',
},
{
title: t('更新时间'),
dataIndex: 'updateTime',
key: 'a.update_time',
sorter: true,
width: 130,
align: 'center',
},
];
const tableProps: BasicTableProps = {
api: bizMailAccountListData,
beforeFetch: (params) => {
params['isAll'] = true;
return params;
},
columns: tableColumns,
formConfig: searchForm,
rowKey: 'id',
};
export default {
modalProps,
tableProps,
itemCode: 'id',
itemName: 'id',
isShowCode: false,
};

View File

@@ -68,31 +68,11 @@
showTime: { format: 'HH:mm' },
},
},
{
label: t('收件标识'),
field: 'mailId',
component: 'Input',
},
{
label: t('附件名称'),
field: 'fileName',
component: 'Input',
},
{
label: t('文件扩展名'),
field: 'fileExt',
component: 'Input',
},
{
label: t('是否压缩'),
field: 'isCompressed',
component: 'Input',
},
{
label: t('是否加密'),
field: 'isEncrypted',
component: 'Input',
},
],
};
@@ -104,15 +84,7 @@
sorter: true,
width: 180,
align: 'left',
slot: 'firstColumn',
},
{
title: t('收件标识'),
dataIndex: 'mailId',
key: 'a.mail_id',
sorter: true,
width: 130,
align: 'center',
fixed: 'left'
},
{
title: t('附件名称'),
@@ -146,14 +118,6 @@
width: 130,
align: 'left',
},
{
title: t('存储路径'),
dataIndex: 'storagePath',
key: 'a.storage_path',
sorter: true,
width: 130,
align: 'left',
},
{
title: t('文件MD5'),
dataIndex: 'fileMd5',
@@ -177,6 +141,7 @@
sorter: true,
width: 130,
align: 'left',
dictType: 'is_compressed',
},
{
title: t('是否加密'),
@@ -185,11 +150,13 @@
sorter: true,
width: 130,
align: 'left',
dictType: 'is_encrypted',
},
];
const actionColumn: BasicColumn<BizMailAttachments> = {
width: 160,
align: 'center',
actions: (record: BizMailAttachments) => [
{
icon: 'i-ant-design:delete-outlined',

View File

@@ -15,7 +15,13 @@
<Icon icon="i-ant-design:download-outlined" /> {{ t('导出') }}
</a-button>
</template>
<template #slotBizKey="{ record }">
<a @click="openViewModal(true, record)" :title="record.subject">
{{ record.subject }}
</a>
</template>
</BasicTable>
<ViewModal @register="registerViewModal" @success="handleSuccess" />
<FormImport @register="registerImportModal" @success="handleSuccess" />
</div>
</template>
@@ -34,6 +40,7 @@
import { useModal } from '@jeesite/core/components/Modal';
import { FormProps } from '@jeesite/core/components/Form';
import FormImport from './formImport.vue';
import ViewModal from './view.vue';
const { t } = useI18n('biz.mailReceived');
const { showMessage } = useMessage();
@@ -71,7 +78,11 @@
{
label: t('账号名称'),
field: 'accountId',
component: 'Input',
fieldLabel: 'accountName',
component: 'ListSelect',
componentProps: {
selectType: 'bizMailAccountSelect',
},
},
{
label: t('发件人名称'),
@@ -86,12 +97,20 @@
{
label: t('是否有附件'),
field: 'hasAttachment',
component: 'Input',
component: 'Select',
componentProps: {
dictType: 'has_attachment',
allowClear: true,
},
},
{
label: t('状态'),
field: 'ustatus',
component: 'Input',
component: 'Select',
componentProps: {
dictType: 'send_status',
allowClear: true,
},
},
],
};
@@ -107,9 +126,9 @@
fixed: 'left',
},
{
title: t('账户标识'),
dataIndex: 'accountId',
key: 'a.account_id',
title: t('服务名称'),
dataIndex: 'accountName',
key: 'b.account_name',
sorter: true,
width: 130,
align: 'left',
@@ -122,21 +141,14 @@
width: 130,
align: 'left',
},
{
title: t('发件人名称'),
dataIndex: 'fromName',
key: 'a.from_name',
sorter: true,
width: 130,
align: 'left',
},
{
title: t('邮件主题'),
dataIndex: 'subject',
key: 'a.subject',
sorter: true,
width: 130,
width: 425,
align: 'left',
slot: 'slotBizKey',
},
{
title: t('接收时间'),
@@ -153,6 +165,7 @@
sorter: true,
width: 130,
align: 'left',
dictType: 'has_attachment',
},
];
@@ -197,6 +210,8 @@
function handleForm(record: Recordable) {
openDrawer(true, record);
}
const [registerViewModal, { openModal: openViewModal }] = useModal();
async function handleExport() {
loading.value = true;

View File

@@ -0,0 +1,614 @@
<template>
<BasicModal
v-bind="$attrs"
@register="register"
title="邮件详情"
:showOkBtn="false"
:showCancelBtn="false"
width="70%"
defaultFullscreen="true"
@cancel="handleCancel"
>
<div class="detail-container">
<div class="detail-title">{{ ReceivedList?.subject || '无标题' }}</div>
<div class="detail-info">
<span class="info-item">接收时间{{ ReceivedList?.createTime || '-' }}</span>
<span class="info-item">发件邮箱{{ ReceivedList?.fromAddress }}|{{ ReceivedList?.fromName }}</span>
</div>
<div class="detail-content">
<div class="content-header">
<span class="content-title">邮件内容</span>
<span class="content-tip" v-if="!ReceivedList?.mailContent">暂无邮件内容</span>
</div>
<div class="db-content" v-html="ReceivedList?.mailContent || ''"></div>
</div>
<!-- 附件列表区域 -->
<div class="attachments-section" v-if="MailAttachments.length > 0">
<div class="attachments-title">
附件列表 ({{ MailAttachments.length }})
</div>
<div class="attachments-vertical-container">
<div class="attachments-grid">
<div class="attachment-card" v-for="(item, index) in MailAttachments" :key="index">
<div class="attachment-icon-wrapper">
<img
class="attachment-icon"
:src="getAttachmentIconUrl(item)"
:alt="item.fileName + '图标'"
@error="handleIconLoadError"
/>
</div>
<div class="attachment-info-wrapper">
<div class="file-info-row">
<span class="attachment-name">{{ item.fileName }}</span>
<span class="attachment-size">({{ formatFileSize(item.fileSize) }})</span>
</div>
</div>
<div class="attachment-action-wrapper">
<button class="download-btn" @click="downloadAttachment(item)">
下载
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</BasicModal>
</template>
<script lang="ts">
import { defineComponent, ref, PropType } from 'vue';
import { useMessage } from '@jeesite/core/hooks/web/useMessage';
import { BasicModal, useModalInner } from '@jeesite/core/components/Modal';
import { BizMailReceived } from '@jeesite/biz/api/biz/mailReceived';
import { useGlobSetting } from '@jeesite/core/hooks/setting';
import { downloadByUrl } from '@jeesite/core/utils/file/download';
import { BizMailAttachments, bizMailAttachmentsListAll } from '@jeesite/biz/api/biz/mailAttachments';
import defaultIcon from '@jeesite/assets/images/file.png';
import docxIcon from '@jeesite/assets/images/docx.png';
import gzIcon from '@jeesite/assets/images/gz.png';
import pdfIcon from '@jeesite/assets/images/pdf.png';
import pptxIcon from '@jeesite/assets/images/pptx.png';
import pyIcon from '@jeesite/assets/images/py.png';
import sqlIcon from '@jeesite/assets/images/sql.png';
import wpsIcon from '@jeesite/assets/images/wps.png';
import xlsxIcon from '@jeesite/assets/images/xlsx.png';
import zipIcon from '@jeesite/assets/images/zip.png';
// 定义自定义图标配置类型
interface CustomIconConfig {
default?: string;
types?: Record<string, string>;
exts?: Record<string, string>;
}
export default defineComponent({
components: { BasicModal },
emits: ['modalClose'],
props: {
customIcons: {
type: Object as PropType<CustomIconConfig>,
default: () => ({
default: 'default-file.png',
types: {
docx: 'docx.png',
gz: 'gz.png',
pdf: 'pdf.png',
pptx: 'pptx.png',
py: 'py.png',
sql: 'sql.png',
wps: 'wps.png',
xlsx: 'xlsx.png',
zip: 'zip.png',
other: 'default-file.png'
},
exts: {}
})
},
iconBasePath: {
type: String,
default: '@jeesite/assets/images/'
}
},
setup(props, { emit }) {
const ReceivedList = ref<BizMailReceived>();
const MailAttachments = ref<BizMailAttachments[]>([]);
const { createMessage } = useMessage();
const iconMap = {
'default-file.png': defaultIcon,
'docx.png': docxIcon,
'gz.png': gzIcon,
'pdf.png': pdfIcon,
'pptx.png': pptxIcon,
'py.png': pyIcon,
'sql.png': sqlIcon,
'wps.png': wpsIcon,
'xlsx.png': xlsxIcon,
'zip.png': zipIcon
};
const [register, { closeModal }] = useModalInner(async (data: any) => {
if (!data) return;
if (data.messageId) {
await getAttachments({ messageId: data.messageId });
}
ReceivedList.value = data;
});
// 获取附件列表
const getAttachments = async (params: { messageId: any }) => {
try {
const result = await bizMailAttachmentsListAll(params);
MailAttachments.value = result;
} catch (error) {
console.error('获取附件信息失败:', error);
MailAttachments.value = [];
}
};
async function downloadAttachment(attachment: BizMailAttachments) {
const { ctxAdminPath } = useGlobSetting();
await downloadByUrl({
url: ctxAdminPath + '/biz/mailAttachments/downloadFile',
params: {
id : attachment.id
},
});
}
// 格式化文件大小
const formatFileSize = (size: number) => {
if (!size) return '0 B';
const units = ['B', 'KB', 'MB', 'GB'];
let unitIndex = 0;
let fileSize = size;
while (fileSize >= 1024 && unitIndex < units.length - 1) {
fileSize /= 1024;
unitIndex++;
}
return `${fileSize.toFixed(1)} ${units[unitIndex]}`;
};
// 第一步:根据文件扩展名获取图标文件名
const getAttachmentIconName = (file: BizMailAttachments) => {
const { customIcons } = props;
const ext = file.fileExt?.toLowerCase() || ''; // 统一转小写,避免大小写问题
let iconName = '';
// 1. 优先按扩展名配置匹配
if (customIcons.exts?.[ext]) {
iconName = customIcons.exts[ext];
}
// 2. 按文件类型映射匹配
else if (customIcons.types) {
// 扩展名 → 图标类型的映射(覆盖所有常见类型)
const typeMap: Record<string, string> = {
'doc': 'docx.png',
'docx': 'docx.png',
'gz': 'gz.png',
'pdf': 'pdf.png',
'ppt': 'pptx.png',
'pptx': 'pptx.png',
'py': 'py.png',
'sql': 'sql.png',
'wps': 'wps.png',
'xls': 'xlsx.png',
'xlsx': 'xlsx.png',
'zip': 'zip.png',
'rar': 'zip.png',
'7z': 'zip.png',
'tar': 'zip.png',
'jpg': 'default-file.png',
'png': 'default-file.png',
'txt': 'default-file.png',
'json': 'default-file.png'
};
// 匹配不到则用other类型
iconName = typeMap[ext] || customIcons.types['other'] || 'default-file.png';
}
// 3. 兜底用默认图标
iconName = iconName || customIcons.default || 'default-file.png';
return iconName;
};
// 第二步根据图标文件名获取打包后的真实URL核心
const getAttachmentIconUrl = (file: BizMailAttachments) => {
const iconName = getAttachmentIconName(file);
// 从映射表获取,兜底用默认图标
return iconMap[iconName] || iconMap['default-file.png'];
};
// 处理图标加载失败
const handleIconLoadError = (e: Event) => {
const target = e.target as HTMLImageElement;
// 避免无限循环
if (target.src === iconMap['default-file.png']) return;
target.src = iconMap['default-file.png'];
};
const handleCancel = () => {
emit('modalClose');
closeModal();
};
return {
register,
closeModal,
handleCancel,
ReceivedList,
MailAttachments,
downloadAttachment,
formatFileSize,
getAttachmentIconUrl,
handleIconLoadError
};
},
});
</script>
<style scoped>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.detail-container {
background-color: #f0f8ff;
padding: 20px;
border-radius: 8px;
font-size: 14px;
color: #333;
width: 100%;
height: 100%;
min-height: 0;
display: flex;
flex-direction: column;
}
.detail-title {
font-size: 18px;
font-weight: 600;
text-align: center;
margin-bottom: 16px;
color: #1f2937;
padding-bottom: 12px;
border-bottom: 1px solid #d1e7ff;
flex-shrink: 0;
}
.detail-info {
display: flex;
justify-content: space-between;
margin-bottom: 16px;
color: #4b5563;
font-size: 13px;
gap: 15px;
flex-wrap: wrap;
flex-shrink: 0;
}
.info-item {
background-color: #ffffff;
padding: 6px 12px;
border-radius: 6px;
border: 1px solid #d1e7ff;
box-shadow: 0 1px 2px rgba(0,0,0,0.03);
white-space: nowrap;
}
.detail-content {
font-size: 13px;
line-height: 1.6;
color: #374151;
margin-bottom: 20px;
padding: 0;
background-color: #ffffff;
border-radius: 8px;
border: 1px solid #d1e7ff;
flex: 1;
min-height: 100px;
overflow: hidden;
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
display: flex;
flex-direction: column;
}
.content-header {
padding: 12px 14px;
border-bottom: 1px solid #f0f0f0;
display: flex;
justify-content: space-between;
align-items: center;
flex-shrink: 0;
}
.content-title {
font-size: 14px;
font-weight: 600;
color: #1f2937;
display: flex;
align-items: center;
}
.content-title::before {
content: '';
display: inline-block;
width: 4px;
height: 14px;
background-color: #4096ff;
border-radius: 2px;
margin-right: 6px;
}
.content-tip {
font-size: 12px;
color: #9ca3af;
font-style: italic;
}
.db-content {
padding: 14px;
margin: 0;
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
white-space: pre-wrap;
word-break: break-all;
color: #333;
flex: 1;
overflow-y: auto;
min-height: 80px;
}
.attachments-section {
margin-top: auto;
width: 100%;
overflow: hidden;
flex-shrink: 0;
}
.attachments-title {
font-size: 14px;
font-weight: 600;
color: #1f2937;
margin-bottom: 12px;
display: flex;
align-items: center;
gap: 6px;
}
.attachments-vertical-container {
width: 100%;
--card-height: 70px;
--card-gap: 8px;
--container-padding: 4px;
max-height: calc(3 * var(--card-height) + 2 * var(--card-gap) + 2 * var(--container-padding));
overflow-y: auto;
overflow-x: hidden;
padding: 0 var(--container-padding) var(--container-padding) var(--container-padding);
box-sizing: border-box;
scrollbar-width: thin;
scrollbar-color: #d1e7ff #f8fbff;
}
.attachments-vertical-container::-webkit-scrollbar {
width: 6px;
}
.attachments-vertical-container::-webkit-scrollbar-track {
background: #f8fbff;
border-radius: 3px;
}
.attachments-vertical-container::-webkit-scrollbar-thumb {
background: #d1e7ff;
border-radius: 3px;
}
.attachments-vertical-container::-webkit-scrollbar-thumb:hover {
background: #4096ff;
}
.attachments-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(min(100%/4, max(150px, 100%/6)), 1fr));
gap: var(--card-gap);
padding-bottom: var(--card-gap);
width: 100%;
box-sizing: border-box;
}
.attachment-card {
width: 100%;
height: var(--card-height);
background: #ffffff;
border: 1px solid #d1e7ff;
border-radius: 6px;
padding: 8px 10px;
display: flex;
align-items: center;
gap: 8px;
box-shadow: 0 1px 2px rgba(0,0,0,0.03);
transition: all 0.2s ease;
box-sizing: border-box;
overflow: hidden;
}
.attachment-card:hover {
box-shadow: 0 3px 8px rgba(64, 150, 255, 0.1);
border-color: #4096ff;
transform: translateY(-1px);
}
.attachment-icon-wrapper {
width: 32px;
height: 32px;
background-color: #f8fbff;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
padding: 2px;
}
.attachment-icon {
width: 100%;
height: 100%;
object-fit: contain;
display: block;
border-radius: 2px;
opacity: 0;
transition: opacity 0.2s ease;
}
.attachment-icon[src] {
opacity: 1;
}
.attachment-info-wrapper {
flex: 1;
min-width: 0;
display: flex;
align-items: center;
width: calc(100% - 100px);
}
.file-info-row {
display: flex;
align-items: center;
gap: 4px;
width: 100%;
flex-wrap: nowrap;
}
.attachment-name {
font-size: 12px;
color: #1f2937;
font-weight: 500;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
flex: 1;
}
.attachment-size {
font-size: 10px;
color: #6b7280;
white-space: nowrap;
flex-shrink: 0;
min-width: 40px;
}
.attachment-action-wrapper {
flex-shrink: 0;
width: 60px;
display: flex;
justify-content: flex-end;
}
.download-btn {
padding: 3px 8px;
border: 1px solid #1890ff;
border-radius: 4px;
background-color: #ffffff;
color: #1890ff;
cursor: pointer;
font-size: 11px;
transition: all 0.2s ease;
white-space: nowrap;
width: 50px;
text-align: center;
}
.download-btn:hover {
background-color: #e6f7ff;
border-color: #40a9ff;
color: #40a9ff;
}
.download-btn:active {
background-color: #1890ff;
color: white;
border-color: #1890ff;
}
.db-content::-webkit-scrollbar {
width: 6px;
height: 6px;
}
.db-content::-webkit-scrollbar-track {
background: #f8fbff;
border-radius: 3px;
}
.db-content::-webkit-scrollbar-thumb {
background: #d1e7ff;
border-radius: 3px;
}
.db-content::-webkit-scrollbar-thumb:hover {
background: #4096ff;
}
@media (max-width: 1200px) {
.attachments-grid {
grid-template-columns: repeat(auto-fill, minmax(min(100%/4, max(120px, 100%/5)), 1fr));
}
}
@media (max-width: 992px) {
.attachments-grid {
grid-template-columns: repeat(auto-fill, minmax(min(100%/3, max(100px, 100%/4)), 1fr));
}
}
@media (max-width: 768px) {
.attachments-grid {
grid-template-columns: repeat(auto-fill, minmax(min(100%/2, max(150px, 100%/3)), 1fr));
}
.attachment-card {
padding: 6px 8px;
}
}
@media (max-width: 576px) {
.attachments-grid {
grid-template-columns: 1fr;
}
.attachment-info-wrapper {
width: calc(100% - 80px);
}
.detail-container {
padding: 15px;
}
.detail-title {
font-size: 16px;
}
.content-header {
padding: 10px 12px;
}
.content-title {
font-size: 13px;
}
}
:deep(.ant-modal-content) {
height: 100%;
display: flex;
flex-direction: column;
}
:deep(.ant-modal-body) {
height: 100%;
padding: 0;
overflow: hidden;
}
</style>

View File

@@ -54,18 +54,10 @@
{
label: t('账户名称'),
field: 'accountId',
component: 'Input',
fieldLabel: 'accountName',
component: 'ListSelect',
componentProps: {
maxlength: 52,
},
required: true,
},
{
label: t('发件人地址'),
field: 'fromAddress',
component: 'Input',
componentProps: {
maxlength: 255,
selectType: 'bizMailAccountSelect',
},
required: true,
},
@@ -74,7 +66,14 @@
field: 'toAddresses',
component: 'Input',
required: true,
colProps: { md: 24, lg: 24 },
},
{
label: t('抄送人地址'),
field: 'ccAddresses',
component: 'Input',
colProps: { md: 24, lg: 24 },
},
{
label: t('邮件内容'),
field: 'content',

View File

@@ -38,7 +38,7 @@
import { Icon } from '@jeesite/core/components/Icon';
import { BasicTable, BasicColumn, useTable } from '@jeesite/core/components/Table';
import { BizMailSent, bizMailSentList } from '@jeesite/biz/api/biz/mailSent';
import { bizMailSentDelete, bizMailSentListData } from '@jeesite/biz/api/biz/mailSent';
import { bizMailSentDelete, bizMailSent, bizMailSentListData } from '@jeesite/biz/api/biz/mailSent';
import { useModal } from '@jeesite/core/components/Modal';
import { FormProps } from '@jeesite/core/components/Form';
import InputForm from './form.vue';
@@ -80,7 +80,11 @@
{
label: t('账号名称'),
field: 'accountId',
component: 'Input',
fieldLabel: 'accountName',
component: 'ListSelect',
componentProps: {
selectType: 'bizMailAccountSelect',
},
},
{
label: t('发件地址'),
@@ -97,7 +101,7 @@
field: 'sendStatus',
component: 'Select',
componentProps: {
dictType: '',
dictType: 'send_status',
allowClear: true,
},
},
@@ -106,7 +110,7 @@
field: 'hasAttachment',
component: 'Select',
componentProps: {
dictType: '',
dictType: 'has_attachment',
allowClear: true,
},
},
@@ -121,7 +125,7 @@
sorter: true,
width: 180,
align: 'left',
slot: 'firstColumn',
fixed: 'left',
},
{
title: t('发件人地址'),
@@ -162,7 +166,7 @@
sorter: true,
width: 130,
align: 'left',
dictType: '',
dictType: 'send_status',
},
{
title: t('是否有附件'),
@@ -171,7 +175,7 @@
sorter: true,
width: 130,
align: 'left',
dictType: '',
dictType: 'has_attachment',
},
];
@@ -184,7 +188,7 @@
title: t('编辑'),
onClick: handleForm.bind(this, { id: record.id }),
auth: 'biz:mailSent:edit',
},
},
{
icon: 'i-ant-design:delete-outlined',
color: 'error',
@@ -195,6 +199,15 @@
},
auth: 'biz:mailSent:edit',
},
{
icon: 'simple-line-icons:action-redo',
color: 'success',
title: t('发送'),
popConfirm: {
title: t('是否确认发送邮件信息?'),
confirm: handleSent.bind(this, record),
},
},
],
};
@@ -245,6 +258,13 @@
showMessage(res.message);
await handleSuccess(record);
}
async function handleSent(record: Recordable) {
const params = { id: record.id };
const res = await bizMailSent(params);
showMessage(res.message);
await handleSuccess(record);
}
async function handleSuccess(record: Recordable) {
await reload({ record });

View File

@@ -1,196 +0,0 @@
import { useI18n } from '@jeesite/core/hooks/web/useI18n';
import { BasicColumn, BasicTableProps, FormProps } from '@jeesite/core/components/Table';
import { bizMailSentListData } from '@jeesite/biz/api/biz/mailSent';
const { t } = useI18n('biz.mailSent');
const modalProps = {
title: t('发件选择'),
};
const searchForm: FormProps<BizMailSent> = {
baseColProps: { md: 8, lg: 6 },
labelWidth: 90,
schemas: [
{
label: t('记录时间起'),
field: 'createTime_gte',
component: 'DatePicker',
componentProps: {
format: 'YYYY-MM-DD HH:mm',
showTime: { format: 'HH:mm' },
},
},
{
label: t('记录时间止'),
field: 'createTime_lte',
component: 'DatePicker',
componentProps: {
format: 'YYYY-MM-DD HH:mm',
showTime: { format: 'HH:mm' },
},
},
{
label: t('邮件账户标识'),
field: 'accountId',
component: 'Input',
},
{
label: t('发件人地址'),
field: 'fromAddress',
component: 'Input',
},
{
label: t('邮件主题'),
field: 'subject',
component: 'Input',
},
{
label: t('发送状态'),
field: 'sendStatus',
component: 'Select',
componentProps: {
dictType: '',
allowClear: true,
},
},
{
label: t('是否有附件'),
field: 'hasAttachment',
component: 'Select',
componentProps: {
dictType: '',
allowClear: true,
},
},
],
};
const tableColumns: BasicColumn<BizMailSent>[] = [
{
title: t('记录时间'),
dataIndex: 'createTime',
key: 'a.create_time',
sorter: true,
width: 230,
align: 'left',
slot: 'firstColumn',
},
{
title: t('件服务器消息标识'),
dataIndex: 'messageId',
key: 'a.message_id',
sorter: true,
width: 130,
align: 'left',
},
{
title: t('邮件账户标识'),
dataIndex: 'accountId',
key: 'a.account_id',
sorter: true,
width: 130,
align: 'left',
},
{
title: t('发件人地址'),
dataIndex: 'fromAddress',
key: 'a.from_address',
sorter: true,
width: 130,
align: 'left',
},
{
title: t('收件人地址'),
dataIndex: 'toAddresses',
key: 'a.to_addresses',
sorter: true,
width: 130,
align: 'left',
},
{
title: t('抄送人地址'),
dataIndex: 'ccAddresses',
key: 'a.cc_addresses',
sorter: true,
width: 130,
align: 'left',
},
{
title: t('邮件主题'),
dataIndex: 'subject',
key: 'a.subject',
sorter: true,
width: 130,
align: 'left',
},
{
title: t('邮件内容'),
dataIndex: 'content',
key: 'a.content',
sorter: true,
width: 130,
align: 'left',
},
{
title: t('发送时间'),
dataIndex: 'sendTime',
key: 'a.send_time',
sorter: true,
width: 130,
align: 'center',
},
{
title: t('发送状态'),
dataIndex: 'sendStatus',
key: 'a.send_status',
sorter: true,
width: 130,
align: 'left',
dictType: '',
},
{
title: t('错误信息'),
dataIndex: 'errorMsg',
key: 'a.error_msg',
sorter: true,
width: 130,
align: 'left',
},
{
title: t('是否有附件'),
dataIndex: 'hasAttachment',
key: 'a.has_attachment',
sorter: true,
width: 130,
align: 'left',
dictType: '',
},
{
title: t('更新时间'),
dataIndex: 'updateTime',
key: 'a.update_time',
sorter: true,
width: 130,
align: 'center',
},
];
const tableProps: BasicTableProps = {
api: bizMailSentListData,
beforeFetch: (params) => {
params['isAll'] = true;
return params;
},
columns: tableColumns,
formConfig: searchForm,
rowKey: 'id',
};
export default {
modalProps,
tableProps,
itemCode: 'id',
itemName: 'id',
isShowCode: false,
};

View File

@@ -0,0 +1,181 @@
import { useI18n } from '@jeesite/core/hooks/web/useI18n';
import { BasicColumn, BasicTableProps, FormProps } from '@jeesite/core/components/Table';
import { BizMailAccount, bizMailAccountListData } from '@jeesite/biz/api/biz/mailAccount';
const { t } = useI18n('biz.mailAccount');
const modalProps = {
title: t('邮件账号选择'),
};
const searchForm: FormProps<BizMailAccount> = {
baseColProps: { md: 8, lg: 6 },
labelWidth: 90,
schemas: [
{
label: t('记录时间起'),
field: 'createTime_gte',
component: 'DatePicker',
componentProps: {
format: 'YYYY-MM-DD HH:mm',
showTime: { format: 'HH:mm' },
},
},
{
label: t('记录时间止'),
field: 'createTime_lte',
component: 'DatePicker',
componentProps: {
format: 'YYYY-MM-DD HH:mm',
showTime: { format: 'HH:mm' },
},
},
{
label: t('SMTP服务'),
field: 'host',
component: 'Input',
},
{
label: t('用户名称'),
field: 'username',
component: 'Input',
},
{
label: t('发件地址'),
field: 'fromAddress',
component: 'Input',
},
{
label: t('启用SSL'),
field: 'sslEnable',
component: 'Select',
componentProps: {
dictType: 'is_open',
allowClear: true,
},
},
{
label: t('状态'),
field: 'ustatus',
component: 'Select',
componentProps: {
dictType: 'ustatus',
allowClear: true,
},
},
],
};
const tableColumns: BasicColumn<BizMailAccount>[] = [
{
title: t('记录时间'),
dataIndex: 'createTime',
key: 'a.create_time',
sorter: true,
width: 180,
align: 'left',
fixed: 'left',
},
{
title: t('配置名称'),
dataIndex: 'accountName',
key: 'a.account_name',
sorter: true,
width: 130,
align: 'left',
slot: 'slotBizKey',
},
{
title: t('SMTP服务'),
dataIndex: 'host',
key: 'a.host',
sorter: true,
width: 130,
align: 'left',
},
{
title: t('SMTP端口'),
dataIndex: 'smtpPort',
key: 'a.smtp_port',
sorter: true,
width: 130,
align: 'center',
},
{
title: t('IMAP服务'),
dataIndex: 'imapHost',
key: 'a.imap_host',
sorter: true,
width: 130,
align: 'left',
},
{
title: t('IMAP端口'),
dataIndex: 'imapPort',
key: 'a.imap_port',
sorter: true,
width: 130,
align: 'center',
},
{
title: t('用户名称'),
dataIndex: 'username',
key: 'a.username',
sorter: true,
width: 130,
align: 'left',
},
{
title: t('发件地址'),
dataIndex: 'fromAddress',
key: 'a.from_address',
sorter: true,
width: 130,
align: 'left',
},
{
title: t('启用SSL'),
dataIndex: 'sslEnable',
key: 'a.ssl_enable',
sorter: true,
width: 130,
align: 'center',
dictType: 'is_open',
},
{
title: t('状态'),
dataIndex: 'ustatus',
key: 'a.ustatus',
sorter: true,
width: 130,
align: 'center',
dictType: 'ustatus',
},
{
title: t('备注'),
dataIndex: 'remark',
key: 'a.remark',
sorter: true,
width: 130,
align: 'left',
},
];
const tableProps: BasicTableProps = {
api: bizMailAccountListData,
beforeFetch: (params) => {
params['isAll'] = true;
return params;
},
columns: tableColumns,
formConfig: searchForm,
rowKey: 'id',
};
export default {
modalProps,
tableProps,
itemCode: 'id',
itemName: 'accountName',
isShowCode: true,
};