💥 feat(模块): 添加了个很棒的功能
This commit is contained in:
parent
9c4cbcec92
commit
230cbb8434
56
src/pages/examine/record/components/printStyle.css
Normal file
56
src/pages/examine/record/components/printStyle.css
Normal file
@ -0,0 +1,56 @@
|
||||
/* 打印样式 */
|
||||
@media print {
|
||||
|
||||
/* 隐藏不需要打印的元素 */
|
||||
.ant-pro-card-header,
|
||||
.ant-pro-table-search,
|
||||
.ant-pro-table-list-toolbar,
|
||||
.ant-pagination,
|
||||
.ant-btn,
|
||||
.ant-pro-table-list-toolbar-container {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* 调整表格样式 */
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
border: 1px solid #000;
|
||||
padding: 8px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/* 页面设置 */
|
||||
@page {
|
||||
margin: 1cm;
|
||||
}
|
||||
|
||||
/* 确保内容不会被截断 */
|
||||
.print-wrapper {
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
/* 处理分页 */
|
||||
.page-break {
|
||||
page-break-after: always;
|
||||
}
|
||||
|
||||
/* 表格标题样式 */
|
||||
.print-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
/* 页眉页脚样式 */
|
||||
.print-header,
|
||||
.print-footer {
|
||||
margin: 20px 0;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
}
|
||||
@ -1,12 +1,14 @@
|
||||
import { ConnectState } from "@/models/global";
|
||||
import { ActionType, ProTable } from "@ant-design/pro-components";
|
||||
import ProForm, { FormInstance, ProFormList, ProFormText } from "@ant-design/pro-form";
|
||||
import { Col, Row, Image, Button } from "antd";
|
||||
import { Col, Row, Image, Button, Input, Modal } from "antd";
|
||||
import moment from "moment";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { connect } from "umi";
|
||||
import { handleGetExamineTypeTreeList } from "../../index/service";
|
||||
import session from "@/utils/session";
|
||||
import { TextAreaRef } from "antd/lib/input/TextArea";
|
||||
import './printStyle.css';
|
||||
|
||||
|
||||
type DetailProps = {
|
||||
@ -19,8 +21,43 @@ const RecordDetail = ({ parentRow, show }: DetailProps) => {
|
||||
|
||||
const actionRef = useRef<ActionType>();
|
||||
const tableFormRef = useRef<FormInstance>();
|
||||
// 表格数据
|
||||
const [proTableData, setTableData] = useState<any>([])
|
||||
|
||||
const [formRes, setFormRes] = useState<any>()
|
||||
// 打印相关状态
|
||||
const [printModalVisible, setPrintModalVisible] = useState<boolean>(false)
|
||||
const [headerContent, setHeaderContent] = useState<string>('')
|
||||
const [footerContent, setFooterContent] = useState<string>('')
|
||||
const headerRef = useRef<TextAreaRef>(null)
|
||||
const footerRef = useRef<TextAreaRef>(null)
|
||||
|
||||
const columns: any = [
|
||||
{
|
||||
title: "考核分类",
|
||||
dataIndex: ""
|
||||
},
|
||||
{
|
||||
title: "考核子类",
|
||||
dataIndex: "title",
|
||||
render: (_, record) => {
|
||||
return record?.question.title ? record?.question.title : ""
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "考核结果",
|
||||
dataIndex: "title",
|
||||
render: (_, record) => {
|
||||
let str: string = ''
|
||||
if (record.choiceResponse && record?.choiceResponse.length > 0) {
|
||||
record.choiceResponse.forEach((item: any, index: number) => {
|
||||
str += `${index > 0 ? ',' : ''}答案${index + 1}:${item}`
|
||||
})
|
||||
}
|
||||
return str || ""
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
useEffect(() => {
|
||||
if (show) {
|
||||
@ -40,6 +77,302 @@ const RecordDetail = ({ parentRow, show }: DetailProps) => {
|
||||
}
|
||||
}, [show])
|
||||
|
||||
// 递归 只有当没有子集的时候 才会把问题的数组 到question 数组中
|
||||
const handeGetTableData = async (resList: any, options: any) => {
|
||||
// resList 需要判断是否有子集的数组
|
||||
if (resList && resList.length > 0) {
|
||||
resList.forEach((item: any) => {
|
||||
if (item.children && item.children.length > 0) {
|
||||
handeGetTableData(item.children, options)
|
||||
} else {
|
||||
if (options && options.length > 0) {
|
||||
options.forEach((optionItem: any) => {
|
||||
if (optionItem.question.categoryId === item.id) {
|
||||
if (item.question && item.question.length > 0) {
|
||||
item.question.push(optionItem)
|
||||
} else {
|
||||
item.question = [optionItem]
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
return resList
|
||||
}
|
||||
|
||||
// 把需要打印的数组拿到一层中来
|
||||
const handleGetTableRes = (list: any) => {
|
||||
let res: any = [];
|
||||
if (list && list.length > 0) {
|
||||
list.forEach((item: any) => {
|
||||
if (item.question && item.question.length > 0) {
|
||||
// 如果当前项有question字段且不为空,将其添加到结果数组
|
||||
item.question.forEach((questionItem: any) => {
|
||||
res.push(questionItem);
|
||||
});
|
||||
}
|
||||
if (item.children && item.children.length > 0) {
|
||||
// 如果有子项,递归处理子项并合并结果
|
||||
const childResults = handleGetTableRes(item.children);
|
||||
if (childResults && childResults.length > 0) {
|
||||
res = [...res, ...childResults];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// 处理表格数据,为合并单元格做准备
|
||||
const processTableDataForMerge = (data: any[]) => {
|
||||
if (!data || data.length === 0) return [];
|
||||
|
||||
// 深拷贝数据,避免修改原始数据
|
||||
const processedData = JSON.parse(JSON.stringify(data));
|
||||
|
||||
// 添加分类信息
|
||||
processedData.forEach((item: any) => {
|
||||
// 从categoryId获取分类名称
|
||||
if (item.question && item.question.categoryId) {
|
||||
// 这里需要根据实际情况获取分类名称
|
||||
// 可以从缓存或其他地方获取
|
||||
const categoryInfo = getCategoryInfo(item.question.categoryId);
|
||||
item.categoryName = categoryInfo?.name || '';
|
||||
item.parentCategoryName = categoryInfo?.parentName || '';
|
||||
}
|
||||
});
|
||||
|
||||
// 计算合并信息
|
||||
let currentCategory = '';
|
||||
let currentSubCategory = '';
|
||||
let categorySpan = 0;
|
||||
let subCategorySpan = 0;
|
||||
|
||||
processedData.forEach((item: any, index: number) => {
|
||||
// 处理考核分类合并
|
||||
if (item.parentCategoryName === currentCategory) {
|
||||
categorySpan++;
|
||||
item._categoryMerged = true; // 标记为已合并
|
||||
} else {
|
||||
// 更新前一个分类的合并信息
|
||||
if (categorySpan > 0 && index > 0) {
|
||||
processedData[index - categorySpan]._categoryRowSpan = categorySpan + 1;
|
||||
}
|
||||
currentCategory = item.parentCategoryName;
|
||||
categorySpan = 0;
|
||||
item._categoryRowSpan = 1; // 默认占一行
|
||||
}
|
||||
|
||||
// 处理考核子类合并
|
||||
if (item.categoryName === currentSubCategory) {
|
||||
subCategorySpan++;
|
||||
item._subCategoryMerged = true; // 标记为已合并
|
||||
} else {
|
||||
// 更新前一个子类的合并信息
|
||||
if (subCategorySpan > 0 && index > 0) {
|
||||
processedData[index - subCategorySpan]._subCategoryRowSpan = subCategorySpan + 1;
|
||||
}
|
||||
currentSubCategory = item.categoryName;
|
||||
subCategorySpan = 0;
|
||||
item._subCategoryRowSpan = 1; // 默认占一行
|
||||
}
|
||||
});
|
||||
|
||||
// 处理最后一组数据的合并信息
|
||||
if (categorySpan > 0 && processedData.length > 0) {
|
||||
const targetIndex = processedData.length - categorySpan - 1;
|
||||
if (targetIndex >= 0 && targetIndex < processedData.length && processedData[targetIndex]) {
|
||||
processedData[targetIndex]._categoryRowSpan = categorySpan + 1;
|
||||
}
|
||||
}
|
||||
if (subCategorySpan > 0 && processedData.length > 0) {
|
||||
const targetIndex = processedData.length - subCategorySpan - 1;
|
||||
if (targetIndex >= 0 && targetIndex < processedData.length && processedData[targetIndex]) {
|
||||
processedData[targetIndex]._subCategoryRowSpan = subCategorySpan + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return processedData;
|
||||
};
|
||||
|
||||
// 获取分类信息的辅助函数
|
||||
const getCategoryInfo = (categoryId: number) => {
|
||||
// 这里需要根据实际情况实现
|
||||
// 可以从缓存或其他地方获取分类信息
|
||||
// 示例实现
|
||||
const categoriesTypeObj = session.get('categoriesTypeObj') || {};
|
||||
const categoriesIdObj = session.get('categoriesIdObj') || {};
|
||||
|
||||
let parentName = '';
|
||||
let name = '';
|
||||
|
||||
// 查找父分类
|
||||
for (let key in categoriesIdObj) {
|
||||
const list = categoriesIdObj[key];
|
||||
if (list && list.length > 0 && list.indexOf(Number(categoryId)) !== -1) {
|
||||
parentName = categoriesTypeObj[key] || '';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 获取当前分类名称
|
||||
name = categoriesTypeObj[categoryId] || '';
|
||||
|
||||
return { name, parentName };
|
||||
};
|
||||
|
||||
// 打印表格方法
|
||||
const handlePrintTable = () => {
|
||||
// 创建打印内容
|
||||
const printWindow = window.open('', '_blank');
|
||||
if (!printWindow) {
|
||||
Modal.error({ title: '打印失败', content: '无法创建打印窗口,请检查浏览器设置。' });
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取表格数据
|
||||
const tableData = proTableData || [];
|
||||
|
||||
// 构建HTML内容
|
||||
let printContent = `
|
||||
<html>
|
||||
<head>
|
||||
<title>考核记录打印</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; margin: 20px; }
|
||||
.print-header, .print-footer { margin: 20px 0; white-space: pre-wrap; }
|
||||
table { width: 100%; border-collapse: collapse; margin: 20px 0; }
|
||||
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
|
||||
th { background-color: #f2f2f2; }
|
||||
.info-section { margin-bottom: 20px; }
|
||||
.info-item { margin-bottom: 10px; }
|
||||
.info-label { font-weight: bold; display: inline-block; width: 100px; }
|
||||
.image-container { display: flex; flex-wrap: wrap; margin-top: 10px; }
|
||||
.image-item { margin-right: 10px; margin-bottom: 10px; }
|
||||
@media print {
|
||||
button { display: none; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
`;
|
||||
|
||||
// 添加自定义页眉
|
||||
if (headerContent) {
|
||||
printContent += `<div class="print-header">${headerContent}</div>`;
|
||||
}
|
||||
|
||||
// 添加基本信息
|
||||
printContent += `
|
||||
<div class="info-section">
|
||||
<div class="info-item"><span class="info-label">服务区名称:</span> ${formRes?.serverPartName || ''}</div>
|
||||
<div class="info-item"><span class="info-label">站点名称:</span> ${formRes?.placeName || ''}</div>
|
||||
<div class="info-item"><span class="info-label">巡查内容:</span> ${formRes?.uploadResult || ''}</div>
|
||||
<div class="info-item"><span class="info-label">巡查人员:</span> ${formRes?.userName || ''}</div>
|
||||
<div class="info-item"><span class="info-label">巡查时间:</span> ${formRes?.submittedAt || ''}</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// 添加图片信息
|
||||
if (formRes?.imgsList && formRes.imgsList.length > 0) {
|
||||
printContent += `
|
||||
<div class="info-section">
|
||||
<div class="info-label">附件列表:</div>
|
||||
<div class="image-container">
|
||||
`;
|
||||
|
||||
formRes.imgsList.forEach((item: string) => {
|
||||
printContent += `<div class="image-item"><img src="${item}" style="width: 150px; height: 150px;" /></div>`;
|
||||
});
|
||||
|
||||
printContent += `
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// 添加表格
|
||||
printContent += `
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>考核分类</th>
|
||||
<th>考核子类</th>
|
||||
<th>考核结果</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
`;
|
||||
|
||||
// 添加表格数据行
|
||||
tableData.forEach((record: any) => {
|
||||
printContent += '<tr>';
|
||||
|
||||
// 考核分类列 - 处理合并单元格
|
||||
if (record._categoryMerged) {
|
||||
// 如果已合并,不显示单元格
|
||||
} else {
|
||||
printContent += `<td ${record._categoryRowSpan && record._categoryRowSpan > 1 ? `rowspan="${record._categoryRowSpan}"` : ''}>${record.parentCategoryName || ''}</td>`;
|
||||
}
|
||||
|
||||
// 考核子类列 - 处理合并单元格
|
||||
if (record._subCategoryMerged) {
|
||||
// 如果已合并,不显示单元格
|
||||
} else {
|
||||
printContent += `<td ${record._subCategoryRowSpan && record._subCategoryRowSpan > 1 ? `rowspan="${record._subCategoryRowSpan}"` : ''}>${record.question?.title || ''}</td>`;
|
||||
}
|
||||
|
||||
// 考核结果列
|
||||
let resultStr = '';
|
||||
if (record.choiceResponse && record.choiceResponse.length > 0) {
|
||||
record.choiceResponse.forEach((item: any, index: number) => {
|
||||
resultStr += `${index > 0 ? ',' : ''}答案${index + 1}:${item}`;
|
||||
});
|
||||
}
|
||||
printContent += `<td>${resultStr || ''}</td>`;
|
||||
|
||||
printContent += '</tr>';
|
||||
});
|
||||
|
||||
printContent += `
|
||||
</tbody>
|
||||
</table>
|
||||
`;
|
||||
|
||||
// 添加自定义页脚
|
||||
if (footerContent) {
|
||||
printContent += `<div class="print-footer">${footerContent}</div>`;
|
||||
}
|
||||
|
||||
// 添加打印按钮
|
||||
printContent += `
|
||||
<div style="text-align: center; margin-top: 20px;">
|
||||
<button onclick="window.print();" style="padding: 8px 16px;">打印</button>
|
||||
<button onclick="window.close();" style="padding: 8px 16px; margin-left: 10px;">关闭</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
printContent += `
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
// 写入打印窗口并打印
|
||||
printWindow.document.open();
|
||||
printWindow.document.write(printContent);
|
||||
printWindow.document.close();
|
||||
|
||||
// 等待图片加载完成后打印
|
||||
setTimeout(() => {
|
||||
printWindow.focus();
|
||||
// 自动打印可以取消注释下面的代码
|
||||
// printWindow.print();
|
||||
}, 500);
|
||||
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<ProForm
|
||||
@ -117,9 +450,11 @@ const RecordDetail = ({ parentRow, show }: DetailProps) => {
|
||||
actionRef={actionRef}
|
||||
formRef={tableFormRef}
|
||||
search={false}
|
||||
columns={columns}
|
||||
toolbar={{
|
||||
actions: [
|
||||
<Button type="primary" onClick={(e) => {
|
||||
setPrintModalVisible(true)
|
||||
}}>
|
||||
打印
|
||||
</Button>
|
||||
@ -143,6 +478,19 @@ const RecordDetail = ({ parentRow, show }: DetailProps) => {
|
||||
})
|
||||
}
|
||||
|
||||
let tableData = await handeGetTableData(typeData, res)
|
||||
console.log('tableData', tableData);
|
||||
// 拿到整个数据之后 递归取出tableData中有 question 数据的那一层数据变为一个数组
|
||||
let tableRes = await handleGetTableRes(tableData)
|
||||
console.log('tableRes', tableRes);
|
||||
|
||||
// 处理表格数据,为合并单元格做准备
|
||||
let processedData = processTableDataForMerge(tableRes);
|
||||
setTableData(processedData)
|
||||
if (processedData && processedData.length > 0) {
|
||||
return { data: processedData, success: true }
|
||||
}
|
||||
return []
|
||||
|
||||
|
||||
// let categoriesObj: any = {} // 子父级关系的缓存对象
|
||||
@ -198,11 +546,42 @@ const RecordDetail = ({ parentRow, show }: DetailProps) => {
|
||||
}}
|
||||
/>
|
||||
|
||||
<Modal
|
||||
title="打印设置"
|
||||
open={printModalVisible}
|
||||
onOk={() => {
|
||||
handlePrintTable();
|
||||
setPrintModalVisible(false);
|
||||
}}
|
||||
onCancel={() => setPrintModalVisible(false)}
|
||||
width={600}
|
||||
>
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<div style={{ marginBottom: 8 }}>页眉内容(可选):</div>
|
||||
<Input.TextArea
|
||||
ref={headerRef}
|
||||
rows={4}
|
||||
value={headerContent}
|
||||
onChange={(e) => setHeaderContent(e.target.value)}
|
||||
placeholder="请输入要在打印页面顶部显示的内容"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div style={{ marginBottom: 8 }}>页脚内容(可选):</div>
|
||||
<Input.TextArea
|
||||
ref={footerRef}
|
||||
rows={4}
|
||||
value={footerContent}
|
||||
onChange={(e) => setFooterContent(e.target.value)}
|
||||
placeholder="请输入要在打印页面底部显示的内容"
|
||||
/>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export default connect(({ user }: ConnectState) => ({
|
||||
currentUser: user.data
|
||||
}))(RecordDetail);
|
||||
|
||||
@ -32,7 +32,7 @@ export async function retrieveUserAuthority(params: any) {
|
||||
|
||||
//获取菜单数据
|
||||
export async function retrieveMenuData(params: any) {
|
||||
const data = await request.get('/menus', params)
|
||||
// const data = await request.get('/menus', params)
|
||||
// return data
|
||||
return [
|
||||
{
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user