You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
437 lines
12 KiB
437 lines
12 KiB
<script lang="ts" setup>
|
|
import QRCode from 'qrcode';
|
|
import logourl from "@/assets/logo_text.png";
|
|
import { useUserStore } from '@/store/modules/user';
|
|
|
|
export interface fieldTree{
|
|
field?:string;
|
|
name?:string;
|
|
type:string;
|
|
checked?:number;
|
|
data?:[];
|
|
child?:fieldTree[]
|
|
}
|
|
|
|
export interface PageConfig{
|
|
width:string;
|
|
height:string;
|
|
horizontal:string;//横向
|
|
pagesize:string;
|
|
|
|
//页脚页眉
|
|
pagebrow:string[];
|
|
founder:string;
|
|
founderTime:string;
|
|
deptOrg:string;
|
|
masters_key:string;
|
|
qrcode:boolean;
|
|
}
|
|
|
|
const props = withDefaults(defineProps<{
|
|
name:string,
|
|
pageConfig:PageConfig,
|
|
fieldTree:fieldTree[],
|
|
flowList:any[],
|
|
qrcode?:string,
|
|
}>(),{})
|
|
|
|
const qrdata=ref("")
|
|
const userName=useUserStore().nickname;
|
|
|
|
function parseDataPicker(val:string){
|
|
let str:string;
|
|
if(typeof(val)=="number"){
|
|
str=new Date(val).toISOString()
|
|
}else{
|
|
if(val==""|| val.match(/[a-z]/) ) return val;
|
|
str=new Date(parseInt(val)).toISOString()
|
|
}
|
|
|
|
return str.slice(0,10)+" "+str.slice(11,16)
|
|
}
|
|
|
|
const generateQrCode= ()=>{
|
|
var opts = {
|
|
errorCorrectionLevel: 'M',
|
|
type: 'image/png',
|
|
quality: 0.3,
|
|
margin: 1,
|
|
color: {
|
|
dark:"#000",
|
|
light:"#fff"
|
|
}
|
|
}
|
|
|
|
QRCode.toDataURL(props.qrcode, opts, function (err, url) {
|
|
if (err) throw err
|
|
qrdata.value=url
|
|
})
|
|
}
|
|
|
|
const getStateText=(val:number)=>{
|
|
if(val==1) return "未操作"
|
|
if(val==2) return "已同意"
|
|
if(val==3) return "已驳回"
|
|
if(val==4) return "已查看"
|
|
return "未操作"
|
|
}
|
|
|
|
</script>
|
|
|
|
<template>
|
|
<div id="printPageAll" :style="{width: props.pageConfig.width}">
|
|
<div id="printHeader" class="page_brow">
|
|
<img height="50px" :src="logourl" />
|
|
<span v-if="props.pageConfig.pagebrow.includes('masters_key')">表单编号: {{ props.pageConfig.masters_key }}</span>
|
|
</div>
|
|
<div id="printContainer">
|
|
<h3>{{props.name}}</h3>
|
|
<div style="display: flex;justify-content: space-between; margin-bottom: 10px;">
|
|
<span v-if="props.pageConfig.pagebrow.includes('founder')">创建人: {{ props.pageConfig.founder }}</span>
|
|
<span v-if="props.pageConfig.pagebrow.includes('founderTime')">创建时间: {{ props.pageConfig.founderTime }}</span>
|
|
<span v-if="props.pageConfig.pagebrow.includes('deptOrg')">所属部门: {{ props.pageConfig.deptOrg }}</span>
|
|
</div>
|
|
<div v-for="group in props.fieldTree">
|
|
<div v-if="Array.isArray(group) && group.length>0" class="auto_table">
|
|
<template v-for="item in group" >
|
|
<div v-if="item.checked!=2" class="cell_box">
|
|
<span class="box_name">
|
|
{{item.name}}
|
|
</span>
|
|
<span v-if="item.type !=='datePicker'" class="content">{{item.field}}</span>
|
|
<span v-else class="content">{{parseDataPicker(item.field)}}</span>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
<div v-else-if="group.type=='divider'" class="title" v-if="group.checked!=2">{{ group.name }}</div>
|
|
<div v-else-if="group.type=='textarea'" class="section" v-if="group.checked!=2">
|
|
<h5>{{ group.name }}</h5>
|
|
<div class="text_area">{{ group.field }}</div>
|
|
</div>
|
|
<div v-else-if="group.type=='table'" class="section_table" v-if="group.checked!=2">
|
|
<table class="bder_table">
|
|
<caption >{{ group.name }}</caption>
|
|
<thead>
|
|
<tr>
|
|
<template v-for="child in group.child">
|
|
<th v-if="child.checked!=2" >{{ child.name }}</th>
|
|
</template>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr v-for="(dd, index) in group.data" :key="dd">
|
|
<template v-for="child in group.child">
|
|
<td v-if="child.checked!=2" >{{ dd[child.field]}}</td>
|
|
</template>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div v-else-if="group.type=='tabs'" class="section_tabs" v-if="group.checked!=2">
|
|
<h5>{{ group.name }}</h5>
|
|
<div v-for="tabs in group.child" class="tabs_item">
|
|
<div v-if="Array.isArray(tabs)" class="auto_table">
|
|
<template v-for="item in tabs" >
|
|
<div v-if="item.checked!=2" class="tabs_cell_box" style="width: auto;">
|
|
<span class="box_name">
|
|
{{item.name}}
|
|
</span>
|
|
<span v-if="item.type !=='datePicker'" class="content">{{item.field}}</span>
|
|
<span v-else class="content">{{parseDataPicker(item.field)}}</span>
|
|
</div>
|
|
</template>
|
|
</div>
|
|
<div v-else-if="tabs.type=='divider'" class="title" v-if="tabs.checked!=2"
|
|
style="font-size: 12px; margin-top: 20px;">{{ tabs.name }}</div>
|
|
<div v-else-if="tabs.type=='textarea'" class="section" v-if="tabs.checked!=2">
|
|
<h5>{{ tabs.name }}</h5>
|
|
<div class="text_area">{{ tabs.field }}</div>
|
|
</div>
|
|
<div v-else-if="tabs.type=='table'" class="section" v-if="tabs.checked!=2">
|
|
<h5>{{ tabs.name }}</h5>
|
|
<table class="bder_table">
|
|
<thead>
|
|
<tr>
|
|
<template v-for="child in tabs.child">
|
|
<th v-if="child.checked!=2" >{{ child.name }}</th>
|
|
</template>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr v-for="(dd, index) in tabs.data" :key="dd">
|
|
<template v-for="child in tabs.child">
|
|
<td v-if="child.checked!=2" >{{ dd[child.field]}}</td>
|
|
</template>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div v-else class="tabs_cell_box" v-if="tabs.checked!=2">
|
|
<span class="box_name">
|
|
{{tabs.name}}
|
|
</span>
|
|
<span v-if="tabs.type !=='datePicker'" class="content">{{tabs.field}}</span>
|
|
<span v-else class="content">{{parseDataPicker(tabs.field??'')}}</span>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
<div v-else v-if="group.checked!=2">{{ group }}</div>
|
|
</div>
|
|
|
|
|
|
<div v-if="props.flowList && props.flowList.length>0" class="section_table">
|
|
<table class="bder_table">
|
|
<caption >审批流程</caption>
|
|
<thead>
|
|
<tr>
|
|
<th >审批节点</th>
|
|
<th >处理人</th>
|
|
<th >操作记录</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr v-for="(item, index) in props.flowList" :key="dd">
|
|
<template v-if="item.step>1">
|
|
<td >
|
|
<div style="line-height: 22px;">{{ item.nodeName}}
|
|
<span v-if="item.examinemode==1">(依次审批)</span>
|
|
<span v-if="item.examinemode==2">(会签)</span>
|
|
<span v-if="item.examinemode==3">(或签)</span>
|
|
<br><span v-if="item.status==2">已同意</span>
|
|
<span v-else-if="item.status==3">已驳回</span>
|
|
<span v-else>未处理</span>
|
|
</div>
|
|
</td>
|
|
|
|
<td >
|
|
<template v-for="oper in item.operator" >
|
|
<span v-if="oper.log" class="operate_log" >
|
|
<template v-for="log in oper.log">
|
|
{{ oper.name }}--{{ oper.departmentname }}--{{ oper.postname }}
|
|
</template>
|
|
</span>
|
|
</template>
|
|
</td>
|
|
<td >
|
|
<template v-for="oper in item.operator" >
|
|
<span v-if="oper.log" class="operate_log" >
|
|
<template v-for="log in oper.log">
|
|
({{ getStateText(log.state) }}) {{ log.cause }} {{ log.time.slice(0,16) }}
|
|
</template>
|
|
</span>
|
|
</template>
|
|
</td>
|
|
</template>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div v-if="props.pageConfig.qrcode" style="display: flex; justify-content: space-between;margin-top: 20px; ">
|
|
{{ generateQrCode() }}
|
|
<div style="display: inherit; align-items: center;">
|
|
<img id="qrcode" width="100px" :src="qrdata"/>
|
|
<div style="text-align: left;padding-left: 5px;">使用手机企业微信<br>扫一扫</div>
|
|
</div>
|
|
<div style="display: flex;flex-direction: column;text-align: left;justify-content: center;">
|
|
<p>打印日期:{{ new Date().toLocaleString() }}</p>
|
|
<p>打印人: {{userName}}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<style lang="scss" scoped>
|
|
#printContainer{
|
|
padding: 0px 57px 30px 57px;
|
|
text-align: center;
|
|
display: flex;
|
|
flex-direction: column;
|
|
overflow-y: scroll;
|
|
scrollbar-width: none;
|
|
}
|
|
//没有元素时,隐藏
|
|
div:empty {
|
|
display: none;
|
|
}
|
|
.page_brow{
|
|
padding: 24px 57px 0 57px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
}
|
|
|
|
.auto_table{
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
border: 1px solid rgb(226, 226, 226);
|
|
}
|
|
.operate_log{
|
|
display: block;
|
|
border-bottom: 1px solid rgb(182, 181, 181);
|
|
}
|
|
td > .operate_log:last-child{
|
|
border-bottom: none;
|
|
}
|
|
h3{
|
|
width: 100%;
|
|
font-size: 22px;
|
|
font-weight: bold;
|
|
}
|
|
|
|
h5{
|
|
font-weight: bold;
|
|
width: 100%;
|
|
margin: 5px;
|
|
}
|
|
|
|
.title{
|
|
margin-left:10px;
|
|
text-align: left;
|
|
font-size: 15px;
|
|
font-weight: bold;
|
|
}
|
|
.el-descriptions {
|
|
margin-top: 20px;
|
|
}
|
|
.cell-item {
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
.margin-top {
|
|
margin: 10px;
|
|
}
|
|
|
|
.section_title{
|
|
width:100%
|
|
}
|
|
|
|
.normal_cell,.normal_cell_field{
|
|
width: 23%;
|
|
height: 20px;
|
|
}
|
|
|
|
|
|
.section{
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.section_tabs{
|
|
margin: 10px 0;
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.text_area{
|
|
border: 1px solid rgb(182, 181, 181);
|
|
border-radius: 3px;
|
|
min-height: 40px;
|
|
margin: 5px 0px 10px 0px;
|
|
}
|
|
//////////////自动伸缩
|
|
.tabs_item {
|
|
display: inline-flex;
|
|
min-height: 30px;
|
|
min-width: 50%; /* 默认最小宽度 */
|
|
margin-right: -1px;/* 解决边框重合问题*/
|
|
/* 默认情况:没有 title/section 时宽度 50% */
|
|
width: 50%;
|
|
.section{
|
|
width: 100%;
|
|
}
|
|
}
|
|
|
|
/* 当直接子元素包含 title 或 section 时,宽度变为 100% */
|
|
.tabs_item:has(> .title),
|
|
.tabs_item:has(> .section) {
|
|
width: 100%;
|
|
}
|
|
|
|
.bder_table{
|
|
border: 1px solid rgb(182, 181, 181);
|
|
margin:5px 0px 10px 0px;
|
|
width: 100% !important; /* 确保表格不超出父容器 */
|
|
overflow-x: hidden;
|
|
}
|
|
|
|
/* 原有样式(稍作优化) */
|
|
.cell_box, .tabs_cell_box {
|
|
border: 1px solid rgb(182, 181, 181);
|
|
margin-right: -1px; /* 水平排列时用右负margin */
|
|
margin-bottom: -1px; /* 垂直排列时用下负margin */
|
|
display: inline-flex;
|
|
|
|
.box_name {
|
|
text-align: center;
|
|
align-content: center;
|
|
width: 30%;
|
|
min-height: 30px;
|
|
text-wrap: wrap;
|
|
max-height: 80px;
|
|
overflow: hidden; /* 配合 ellipsis 需要 */
|
|
}
|
|
|
|
.content {
|
|
text-align: center;
|
|
align-content: center;
|
|
width: 70%;
|
|
min-height: 30px;
|
|
text-wrap: wrap;
|
|
word-break: break-all;
|
|
border-left: 1px solid rgb(182, 181, 181);
|
|
}
|
|
}
|
|
.tabs_cell_box{
|
|
width: 100%;
|
|
}
|
|
|
|
|
|
</style>
|
|
|
|
<style>
|
|
#printContainer table{
|
|
border: 0px solid;
|
|
border-spacing: 1px;
|
|
border-collapse: collapse;
|
|
overflow-y: visible !important;
|
|
}
|
|
#printContainer thead{
|
|
border: 1px solid rgb(182, 181, 181);
|
|
}
|
|
#printContainer table caption{
|
|
text-align: center;
|
|
caption-side: top;
|
|
color: black;
|
|
font-weight: bold;
|
|
width: 100%;
|
|
margin: 5px;
|
|
}
|
|
#printContainer .el-table th.el-table__cell{
|
|
background: none !important;
|
|
word-wrap: break-word;
|
|
}
|
|
|
|
#printContainer .el-table .cell{
|
|
padding: 0 5px;
|
|
text-align: center;
|
|
max-width: max-content;
|
|
}
|
|
|
|
#printContainer table th{
|
|
font-weight: normal;
|
|
border-right: 1px solid rgb(182, 181, 181);
|
|
text-align: center;
|
|
line-height: 28px;
|
|
}
|
|
#printContainer table td{
|
|
border: 1px solid rgb(182, 181, 181);
|
|
line-height: 28px;
|
|
}
|
|
|
|
.el-scrollbar__view {
|
|
display: block !important;
|
|
width: 100% !important;
|
|
}
|
|
</style>
|
|
|