Browse Source

Merge branch 'master' of http://172.20.2.87:3000/herenshan112/ShutongInterconnectedChemicalCloudPlatform

# Conflicts:
#	src/components/DesignForm/app/index.vue
#	src/components/DesignForm/printHtmlRender.vue
#	src/components/DesignForm/tableListPage/index.vue
#	src/views/sysworkflow/lowcodepage/appPage/appPageForm/printSetupPage2.vue
qin_27
herenshan112 2 weeks ago
parent
commit
f5a10ef949
  1. 15
      src/api/doc/index.ts
  2. 11
      src/api/doc/space.ts
  3. 4
      src/api/doc/type.ts
  4. 96
      src/components/DesignForm/app/index.vue
  5. 8
      src/components/DesignForm/assembly/index.ts
  6. 1460
      src/components/DesignForm/formControlPropertiNew.vue
  7. 120
      src/components/DesignForm/printHtmlRender.ts
  8. 234
      src/components/DesignForm/printHtmlRender.vue
  9. 407
      src/components/DesignForm/public/form/form.vue
  10. 10
      src/components/DesignForm/public/form/formItem.vue
  11. 13
      src/components/DesignForm/tableListPage/formPageCont.vue
  12. 85
      src/components/DesignForm/tableListPage/index.vue
  13. 7
      src/main.ts
  14. 3
      src/store/modules/orgMember.ts
  15. 63
      src/views/doc/manage.vue
  16. 344
      src/views/doc/movepanel.vue
  17. 67
      src/views/doc/space.vue
  18. 443
      src/views/sysworkflow/lowcodepage/appPage/appPageForm/printHtmlDom.js
  19. 37
      src/views/sysworkflow/lowcodepage/appPage/appPageForm/printSetupPage2.vue

15
src/api/doc/index.ts

@ -188,4 +188,19 @@ export function postMatterRename(uid:string,data?: {uuid:string;name:string}){
},
data: data
});
}
/**
*
*/
export function postMatterMove(uid:string,data?: {srcUuids:string;destUuid:string}){
return request({
url: '/hxpan/api/matter/move',
method: 'post',
headers: {
'Identifier':uid,
'Content-Type': 'application/x-www-form-urlencoded'
},
data: data
});
}

11
src/api/doc/space.ts

@ -119,6 +119,17 @@ export function doAiTraining(_url:string,data?: any){
data: data
});
}
//文档删除
export function doAiDocDels(_url:string,data?: any){
return request({
url: '/aibot'+_url,
method: 'post',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
data: data
});
}
export interface aiChatData{
inputs:object;

4
src/api/doc/type.ts

@ -15,6 +15,7 @@ export interface matterPage{
deleted?:boolean;
orderDir?:string;
name?:string;
dir?:string;// ‘true’ or ‘false’ 字符串,表示是否只查询文件夹或文件
space?:string;
orderCreateTime?:string;
}
@ -89,12 +90,13 @@ export interface respCreateShare{
/**
*
*/
export function doFileUpload(params:FormData,_url:string): AxiosPromise<matterInfo> {
export function doFileUpload(uid:string,params:FormData,_url:string): AxiosPromise<matterInfo> {
return request({
url: _url,
method: 'post',
data: params,
headers: {
'Identifier':uid,
'Content-Type': 'multipart/form-data'
}
});

96
src/components/DesignForm/app/index.vue

@ -45,6 +45,7 @@ import {
import { formatNumber } from "@/api/DesignForm/utils";
import { formHasPower } from "@/directive/permission/button";
import {printHtmlPage} from '../printHtmlRender.ts'
//
import FormPageCont from "@/components/DesignForm/tableListPage/formPageCont.vue";
@ -1186,17 +1187,17 @@ async function doGainFormPageListCont(sendData: any) {
//console.log(dataList)
rangedDatePickerInTables.forEach(element => {
let x = element.split('^^^*^^^')
console.log(x)
//console.log(x)
rangedDatePickerInTables1.push(x)
data.data.list.forEach((item: any) => {
console.log(item)
console.log(item[x[0]])
/* console.log(item)
console.log(item[x[0]]) */
let tableValues = item[x[0]]
tableValues.forEach((element1: any) => {
let start = dateStringToTimestampLocal(element1[x[1]])
let end = element1[x[1]+"_end"]
let arr = [start,end]
console.log(arr)
//console.log(arr)
element1[x[1]] = arr
});
/* let y = dateStringToTimestampLocal(item[x[0]][x[1]])
@ -1298,7 +1299,7 @@ async function doGainFormPageListCont(sendData: any) {
}
});
});
console.log(data.data)
//console.log(data.data)
return data.data;
//tableDataList.value = data.data.list
@ -1619,90 +1620,6 @@ const lookPageInfo = (val: any) => {
lookPageInfoIsShow.value = true;
};
/****************表单打印功能************************/
const printRenderMode = ref(false);
const printRenderTree: Ref<any[]> = ref([]);
const printPage = async (row: any) => {
let data:any[]=[]
let _pageConfig:PageConfig
let title:string="表单";
await getPrintTemplate({"versionid":props.versionid,"formkey":props.appKey}).then(resp=>{
title=resp.data.title
if(resp.data.formtemplatejson!=""){
data=JSON.parse(resp.data.formtemplatejson)
}else{
alert("请先创建打印模板!")
return
}
if(resp.data.pageconfigjson!=""){
_pageConfig=JSON.parse(resp.data.pageconfigjson)
if(_pageConfig.founder!=""){
_pageConfig.founder=row[_pageConfig.founder]
}
if(_pageConfig.founderTime!=""){
_pageConfig.founderTime=row[_pageConfig.founderTime]
}
if(_pageConfig.deptOrg!=""){
_pageConfig.deptOrg=row[_pageConfig.deptOrg]
}
if(_pageConfig.serialNumber!=""){
_pageConfig.serialNumber=row[_pageConfig.serialNumber]
}
}
})
let qrstr=`https://wab.hxgk.group/#/form_table/taskInfo?id=${props.formId}&key=${props.appKey}&
formid=${props.versionid}&formKey=${props.formKey}&state=2`
data.forEach(node=>{
deepLoopForm(node,row)
})
printRenderTree.value=data
printRenderMode.value = true;
ElMessageBox({
message: () => h('div',{style:{ width:'1200px',display:'flex','flex-direction':'column'}},[
h(ElButton, {
type:"primary",
style: "margin:10px 10px 5px auto;",
onClick: () => {
printElement("printContainer")
}
},'打印表单'),
h('div',{style:{ border: '1px solid black', width: 'fit-content', margin: '5px','align-self': 'center'}},[
h(printHtmlRender,{
name:title,
fieldTree:printRenderTree.value,
pageConfig:_pageConfig,
qrcode:qrstr,
})
])
]),
showConfirmButton:false,
customStyle: { '--el-messagebox-width':'1300px',padding:'10px'},
}).then(() => {
})
};
const deepLoopForm=(node:fieldTree, row: Record<string, any>)=>{
if(Array.isArray(node)){
node.forEach(item=>{deepLoopForm(item,row)})
return
}
if(node.field!=""){
let rnode:Object;
if (row.hasOwnProperty(node.field!)){//
if (node.type=="table" || node.type=="tabs"){
node.data=row[node.field!]
}else{
node.field=row[node.field!]
}
}
}
}
/**
@ 作者: 秦东
@ 时间: 2024-04-05 11:29:50
@ -2368,6 +2285,7 @@ const isObject = (obj: any) => {
placement="top-end"
>
<el-button
@click="printHtmlPage(props.formId,props.appKey,props.versionid, props.formKey,scope.row)"
type="primary"
size="small"
class="fa fa-print"

8
src/components/DesignForm/assembly/index.ts

@ -262,6 +262,10 @@ export default [
control: {
modelValue: '',
glxxsz: [],
zdtcsz: {
tbx: '',
tby: ''
},
optionsValue3Formid:'',
optionsValue3Field:'',
},
@ -312,6 +316,10 @@ export default [
modelValue: '',
appendToBody: true,
glxxsz: [],
zdtcsz: {
tbx: '',
tby: ''
},
optionsValue3Formid:'',
optionsValue3Field:'',
},

1460
src/components/DesignForm/formControlPropertiNew.vue

File diff suppressed because it is too large

120
src/components/DesignForm/printHtmlRender.ts

@ -0,0 +1,120 @@
import {getPrintTemplate} from '@/api/DesignForm/requestapi'
import {fieldTree,PageConfig} from './printHtmlRender.vue'
import printHtmlRender from './printHtmlRender.vue'
import {printWithSmartPagination} from "@/views/sysworkflow/lowcodepage/appPage/appPageForm/printHtmlDom.js"
import {gainRunTaskFlow} from "@/api/DesignForm/requestapi";
import { useOrgMemberStore } from "@/store/modules/orgMember";
const printRenderTree: Ref<any[]> = ref([]);
const orgMembers = useOrgMemberStore()
export const printHtmlPage = async (pformid:string,pappkey:string,pversionid:string,pformkey:string,row: any) => {
let data:any[]=[]
let _pageConfig:PageConfig
let title:string="表单";
let flowlist:any[]=[]
if (row.flowIsOpens==1){
let sendInfo = {
id:row.runFlowId
}
await gainRunTaskFlow(sendInfo)
.then((resp:any) =>{
//console.log("获取流程--werwerwerwer--->",resp.data)
flowlist=resp.data.flowList
})
}
let noTemplate:boolean=false
await getPrintTemplate({"versionid":pversionid,"formkey":pappkey}).then(resp=>{
title=resp.data.title
if(resp.data.formtemplatejson!=""){
data=JSON.parse(resp.data.formtemplatejson)
}else{
noTemplate=true
alert("请先创建打印模板!")
return
}
if(resp.data.pageconfigjson!=""){
_pageConfig=JSON.parse(resp.data.pageconfigjson)
if(_pageConfig.founder!=""){
_pageConfig.founder=row[_pageConfig.founder]
_pageConfig.deptOrg=orgMembers.listMap[row.createrOrg]
}
if(_pageConfig.founderTime!=""){
_pageConfig.founderTime=row[_pageConfig.founderTime]
}
_pageConfig.masters_key=row.masters_key
}
})
if(noTemplate) return;
let qrstr=`https://wab.hxgk.group/#/form_table/taskListPage?id=${pformid}&key=${pappkey}&
formid=${pversionid}&formKey=${pformkey}&qrDetailId=${row.id}`
data.forEach(node=>{
deepLoopForm(node,row)
})
printRenderTree.value=data
ElMessageBox({
message: () => h('div',{style:{ width:'1200px',display:'flex','flex-direction':'column'}},[
h(ElButton, {
type:"primary",
style: "margin:10px 10px 5px auto;",
onClick: () => {
printWithSmartPagination("printPageAll",{
paperSize: _pageConfig.pagesize,
landscape: _pageConfig.horizontal=="htal"? true : false , //默认竖向
margin: 5,
});
}
},'打印表单'),
h('div',{style:{ border: '1px solid black', width: 'fit-content', margin: '5px','align-self': 'center'}},[
h(printHtmlRender,{
name:title,
fieldTree:printRenderTree.value,
pageConfig:_pageConfig,
flowList:flowlist,
qrcode:qrstr,
})
])
]),
showConfirmButton:false,
customStyle: { '--el-messagebox-width':'1300px',padding:'10px'},
}).then(() => {
})
};
const deepLoopForm=(node:fieldTree, row: Record<string, any>)=>{
if(Array.isArray(node)){
node.forEach(item=>{deepLoopForm(item,row)})
return
}
if(node.type=="tabs"&&node.child){
node.child?.forEach(item=>{deepLoopForm(item,row)})
return
}
if(node.field!=""){
if (row.hasOwnProperty(node.field!)){// 有这个字段
if (node.type=="table"){
node.data=row[node.field!]
}else if(node.type=="select"||node.type=="checkbox"){
const val=row[node.field!]
node.field=""
for(let op of node.options!){
if(op.value==val){
node.field=op.label
break;
}
}
}else{
node.field=row[node.field!]
}
}
}
}

234
src/components/DesignForm/printHtmlRender.vue

@ -9,6 +9,7 @@ export interface fieldTree{
type:string;
checked?:number;
data?:[];
options?:[];//select, checkbox... options
child?:fieldTree[]
}
@ -23,7 +24,7 @@ export interface PageConfig{
founder:string;
founderTime:string;
deptOrg:string;
serialNumber:string;
masters_key:string;
qrcode:boolean;
}
@ -31,16 +32,26 @@ 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){
if(val==""|| val.match(/[a-z]/) ) return val;
let str=new Date(parseInt(val)).toISOString()
return str.slice(0,10)+" "+str.slice(11,16)
function parseDataPicker(val:string,vtype:string){
let str:string;
if(typeof(val)=="number"){
str=new Date(val).toLocaleString()
}else{//"";2025-12-17;sheng1chan3ri4qi1;
if(val==""|| val.match(/[a-z-:.]/) ) return val;
str=new Date(parseInt(val)).toLocaleString()
}
if(vtype.startsWith("time")){
return str.slice(11,16)
}
return str.slice(0,10)
}
const generateQrCode= ()=>{
@ -61,19 +72,28 @@ const generateQrCode= ()=>{
})
}
const getStateText=(val:number)=>{
if(val==1) return "未操作"
if(val==2) return "已同意"
if(val==3) return "已驳回"
if(val==4) return "已查看"
return "未操作"
}
</script>
<template>
<div class="page_brow" >
<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('serialNumber')">表单编号:{{ props.pageConfig.serialNumber }}</span>
<span v-if="props.pageConfig.pagebrow.includes('masters_key')">表单编号: {{ props.pageConfig.masters_key }}</span>
</div>
<div id="printContainer" :style="{width: props.pageConfig.width}" >
<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>
<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">
@ -83,7 +103,7 @@ const generateQrCode= ()=>{
{{item.name}}
</span>
<span v-if="item.type !=='datePicker'" class="content">{{item.field}}</span>
<span v-else class="content">{{parseDataPicker(item.field)}}</span>
<span v-else class="content">{{parseDataPicker(item.field,item.type)}}</span>
</div>
</template>
</div>
@ -92,29 +112,45 @@ const generateQrCode= ()=>{
<h5>{{ group.name }}</h5>
<div class="text_area">{{ group.field }}</div>
</div>
<div v-else-if="group.type=='table'" v-if="group.checked!=2" class="section_table">
<h5>{{ group.name }}</h5>
<div class="bder_table" >
<el-table class="table" :data="group.data" border >
<template v-for="child in group.child">
<el-table-column v-if="child.checked!=2" :prop="child.field" :label="child.name" />
</template>
</el-table>
</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">
<template v-if="child.type.includes('Picker')">
{{parseDataPicker(dd[child.field],child.type)}}
</template>
<template v-else>
{{dd[child.field]}}
</template>
</td>
</template>
</tr>
</tbody>
</table>
</div>
<div v-else-if="group.type=='tabs'" v-if="group.checked!=2" class="section_tabs">
<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>
<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,item.type)}}</span>
</div>
</template>
</div>
<div
v-else-if="tabs.type=='divider'" v-if="tabs.checked!=2" class="title"
@ -125,28 +161,92 @@ v-else-if="tabs.type=='divider'" v-if="tabs.checked!=2" class="title"
</div>
<div v-else-if="tabs.type=='table'" v-if="tabs.checked!=2" class="section">
<h5>{{ tabs.name }}</h5>
<div class="bder_table" >
<el-table class="table" border >
<template v-for="child in tabs.child">
<el-table-column v-if="child.checked!=2" :prop="child.field" :label="child.name" />
</template>
</el-table>
</div>
<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" >
<template v-if="child.type.includes('Picker')">
{{parseDataPicker(dd[child.field],child.type)}}
</template>
<template v-else>
{{dd[child.field]}}
</template>
</td>
</template>
</tr>
</tbody>
</table>
</div>
<div v-else v-if="tabs.checked!=2" class="tabs_cell_box">
<div v-else class="section tabs_wide_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>
<span v-else class="content">{{parseDataPicker(tabs.field??'',tabs.type)}}</span>
</div>
</div>
</div>
<div v-else v-if="group.checked!=2">{{ group }}</div>
<div v-else v-if="group.checked!=2 && group.type">{{ group }}</div>
</div>
<div v-if="props.pageConfig.qrcode" style="display: flex; justify-content: space-between; ">
<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"/>
@ -158,6 +258,7 @@ v-else-if="tabs.type=='divider'" v-if="tabs.checked!=2" class="title"
</div>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
#printContainer{
@ -182,11 +283,18 @@ div:empty {
.auto_table{
display: grid;
grid-template-columns: 1fr 1fr;
border: 1px solid rgb(226, 226, 226);
border: 1px solid rgb(200, 200, 200);
}
.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: 25px;
font-size: 22px;
font-weight: bold;
}
@ -243,7 +351,7 @@ h5{
//////////////
.tabs_item {
display: inline-flex;
min-height: 40px;
min-height: 30px;
min-width: 50%; /* 默认最小宽度 */
margin-right: -1px;/* 解决边框重合问题*/
/* 默认情况:没有 title/section 时宽度 50% */
@ -267,39 +375,57 @@ h5{
}
/* 原有样式(稍作优化) */
.cell_box, .tabs_cell_box {
.cell_box, .tabs_cell_box, .tabs_wide_box{
border: 1px solid rgb(182, 181, 181);
margin-right: -1px; /* 水平排列时用右负margin */
margin-bottom: -1px; /* 垂直排列时用下负margin */
display: inline-flex;
.box_name {
padding: 8px 2px;
text-align: center;
align-content: center;
width: 30%;
min-height: 30px;
text-wrap: wrap;
max-height: 80px;
overflow: hidden; /* 配合 ellipsis 需要 */
}
.content {
padding: 8px 2px;
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{
.tabs_cell_box, .tabs_wide_box{
width: 100%;
flex-direction: row;
}
</style>
<style>
#printContainer table{
display: block;
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;
@ -316,9 +442,15 @@ h5{
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>

407
src/components/DesignForm/public/form/form.vue

@ -679,6 +679,15 @@ provide(constControlChange, ({ key, value, data, tProp, type, attribute }: any)
let mustBeHidden: any = [];
let newModelKeyArr: string[] = [];
//
function isEmptyPlainObject(obj) {
// 0
return Object.prototype.toString.call(obj) === '[object Object]'
&& Object.keys(obj).length === 0;
}
function showOrHide(data: any) {
//console.log(data)
@ -899,6 +908,9 @@ function showOrHide(data: any) {
//showFieldskey,optionValuevalue,radioselectArr2----
const radioSelectArr2: any[] = [];
const radioSelectZdtcszConfigArr = [];
//20240815 liwenxuan start
//console.log(props.formData.list) newModelKeyArr
for (let i = 0; i < props.formData.list.length; i++) {
@ -915,8 +927,16 @@ function showOrHide(data: any) {
) {
if (!props.formData.list[i].control.multiple) {
radioSelectShowConfigArr.push(props.formData.list[i].control.glxxsz);
if(props.formData.list[i].control.zdtcsz&&!isEmptyPlainObject(props.formData.list[i].control.zdtcsz.tableData)){
props.formData.list[i].control.zdtcsz.name = props.formData.list[i].name
radioSelectZdtcszConfigArr.push(props.formData.list[i].control.zdtcsz)
}
} else {
checkboxShowConfigArr.push(props.formData.list[i].control.glxxsz);
if(props.formData.list[i].control.zdtcsz&&!isEmptyPlainObject(props.formData.list[i].control.zdtcsz.tableData)){
props.formData.list[i].control.zdtcsz.name = props.formData.list[i].name
radioSelectZdtcszConfigArr.push(props.formData.list[i].control.zdtcsz)
}
}
} else if (props.formData.list[i].type == "checkbox") {
checkboxShowConfigArr.push(props.formData.list[i].control.glxxszForCheckBox);
@ -939,6 +959,10 @@ function showOrHide(data: any) {
} else {
checkboxShowConfigArr.push(a[w].control.glxxsz);
}
if(a[w].control.zdtcsz&&!isEmptyPlainObject(a[w].control.zdtcsz.tableData)){
a[w].control.zdtcsz.name = a[w].name
radioSelectZdtcszConfigArr.push(a[w].control.zdtcsz)
}
//radioSelectShowConfigArr.push(a[w].control.glxxsz);
} else if (a[w].type == "checkbox") {
checkboxShowConfigArr.push(a[w].control.glxxszForCheckBox);
@ -960,6 +984,11 @@ function showOrHide(data: any) {
} else {
checkboxShowConfigArr.push(a.control.glxxsz);
}
if(a.control.zdtcsz&&!isEmptyPlainObject(a.control.zdtcsz.tableData)){
a.control.zdtcsz.name = a.name
radioSelectZdtcszConfigArr.push(a.control.zdtcsz)
}
//radioSelectShowConfigArr.push(a.control.glxxsz);
} else if (a.type == "checkbox") {
checkboxShowConfigArr.push(a.control.glxxszForCheckBox);
@ -984,6 +1013,10 @@ function showOrHide(data: any) {
} else {
checkboxShowConfigArr.push(a.control.glxxsz);
}
if(a.control.zdtcsz&&!isEmptyPlainObject(a.control.zdtcsz.tableData)){
a.control.zdtcsz.name = a.name
radioSelectZdtcszConfigArr.push(a.control.zdtcsz)
}
//radioSelectShowConfigArr.push(a.control.glxxsz);
} else if (a.type == "checkbox") {
checkboxShowConfigArr.push(a.control.glxxszForCheckBox);
@ -999,6 +1032,10 @@ function showOrHide(data: any) {
} else {
checkboxShowConfigArr.push(q.control.glxxsz);
}
if(q.control.zdtcsz&&!isEmptyPlainObject(q.control.zdtcsz.tableData)){
q.control.zdtcsz.name = q.name
radioSelectZdtcszConfigArr.push(q.control.zdtcsz)
}
//radioSelectShowConfigArr.push(q.control.glxxsz);
} else if (q.type == "checkbox") {
checkboxShowConfigArr.push(q.control.glxxszForCheckBox);
@ -1019,6 +1056,7 @@ function showOrHide(data: any) {
newModelKeyArr = Array.from(new Set(newModelKeyArr));
//20240815 liwenxuan end
//console.log(radioSelectShowConfigArr)
//console.log(radioSelectZdtcszConfigArr)
//
const radioSelectShowConfigArr2 = JSON.parse(JSON.stringify(radioSelectShowConfigArr));
const checkboxShowConfigArr2 = JSON.parse(JSON.stringify(checkboxShowConfigArr));
@ -1237,8 +1275,377 @@ function showOrHide(data: any) {
//props.formData.config?.hideField?.push("nin2yuan4yi4wei4wo3menda3fen1ma101939")
//console.log("--constControlChange-->",key, value, data, tProp,type,attribute)//liwenxuan
//console.log(radioSelectZdtcszConfigArr)
nextTick(()=>{
//console.log(model.value)//
radioSelectZdtcszConfigArr.forEach(element => {
let tbx=getLastColonAfterString(element.tbx)
let tby=getLastColonAfterString(element.tby)
/* console.log(tbx)
console.log(tby) */
//console.log(element.name)
let rowValue = model.value[tbx]
//console.log(rowValue)
let columnValue = model.value[tby]
//console.log(columnValue)
if(tbx!=""&&tby!=""){
let cellValue = getTableCellValue(element.tableData, rowValue, columnValue)
model.value[element.name] = cellValue*1
}else if(tbx!=""&&tby==""){
//console.log("")
let cellValue = getTableCellValue(element.tableData, rowValue, 'default')
//console.log(cellValue)
model.value[element.name] = cellValue*1
}else if(tbx==""&&tby!=""){
//console.log("")
let cellValue = getTableCellValue(element.tableData, 'any', columnValue)
//console.log(cellValue)
model.value[element.name] = cellValue*1
}
//console.log(getTableCellValueV2(element.tableData, 3, 2));
});
})
}
/**
* 获取字符串最后一个英文冒号:后的字符
* @param {string} str - 待处理的原始字符串
* @returns {string} 最后一个冒号后的子串无冒号/冒号在末尾时返回空字符串
*/
function getLastColonAfterString(str) {
// 1.
if (typeof str !== 'string') {
//console.warn('');
return '';
}
// 2.
const lastColonIndex = str.lastIndexOf(':');
// 3.
if (lastColonIndex === -1 || lastColonIndex === str.length - 1) {
return '';
}
// 4.
return str.slice(lastColonIndex + 1);
}
/**
* 获取表格单元格值兼容多种tableData结构
* 支持三种结构
* 1. 标准行列结构{列号: {行号: , ...}, ...}
* 2. 仅一行结构{default: {列号: , ...}}
* 3. 仅一列结构{列号: {default: }, ...}
*
* @param {Object} tableData - 表格数据
* @param {String|Number} rowValue - 行标识
* @param {String|Number} columnValue - 列标识
* @returns {String|undefined} - 返回对应的单元格值如果未找到则返回undefined
*/
function getTableCellValueV3(tableData, rowValue, columnValue) {
try {
//
if (!tableData || typeof tableData !== 'object' || Array.isArray(tableData)) {
return undefined;
}
// nullundefined
if (rowValue == null || columnValue == null) {
return undefined;
}
//
const rowStr = String(rowValue);
const columnStr = String(columnValue);
// 1. default
if (tableData.default && typeof tableData.default === 'object') {
// {default: {: , ...}}
// 使
return tableData.default?.[columnStr];
}
// 2.
const columnObj = tableData[columnStr];
if (columnObj && typeof columnObj === 'object') {
// 2.1
const cellValue = columnObj[rowStr];
if (cellValue !== undefined) {
return cellValue; //
}
// 2.2 default
if (columnObj.default !== undefined) {
// {: {default: }, ...}
// default
return columnObj.default;
}
}
// 3.
// 3.1 default
if (!columnObj) {
//
for (const colKey in tableData) {
if (String(colKey) === columnStr) {
const colData = tableData[colKey];
if (colData && typeof colData === 'object' && colData.default !== undefined) {
return colData.default;
}
}
}
}
// 3.2 default"default"
for (const key in tableData) {
const data = tableData[key];
if (data && typeof data === 'object') {
//
if (data[columnStr] !== undefined) {
//
return data[columnStr];
}
}
}
// 4. undefined
return undefined;
} catch (error) {
// undefined
console.error('获取表格单元格值时发生错误:', error);
return undefined;
}
}
/**
* 增强版自动检测tableData结构并获取值
* 支持动态结构检测
*/
function getTableCellValueEnhanced(tableData, rowValue, columnValue) {
try {
if (!tableData || typeof tableData !== 'object') {
return undefined;
}
if (rowValue == null || columnValue == null) {
return undefined;
}
const rowStr = String(rowValue);
const columnStr = String(columnValue);
//
const directValue = tableData?.[columnStr]?.[rowStr];
if (directValue !== undefined) {
return directValue;
}
//
const tableStructure = detectTableStructure(tableData);
switch (tableStructure) {
case 'standard':
//
break;
case 'singleRow':
//
return getFromSingleRowStructure(tableData, columnStr);
case 'singleColumn':
//
return getFromSingleColumnStructure(tableData, columnStr);
case 'mixed':
//
return getFromMixedStructure(tableData, rowStr, columnStr);
default:
// undefined
return undefined;
}
return undefined;
} catch (error) {
console.error('获取表格单元格值时发生错误:', error);
return undefined;
}
}
/**
* 检测tableData的结构
* @returns {string} - 结构类型'standard', 'singleRow', 'singleColumn', 'mixed', 'unknown'
*/
function detectTableStructure(tableData) {
if (!tableData || typeof tableData !== 'object') {
return 'unknown';
}
const keys = Object.keys(tableData);
if (keys.length === 0) {
return 'empty';
}
// default
if (keys.length === 1 && keys[0] === 'default') {
const defaultData = tableData.default;
if (defaultData && typeof defaultData === 'object') {
// default
const hasNestedObjects = Object.values(defaultData).some(
value => value && typeof value === 'object'
);
if (!hasNestedObjects) {
return 'singleRow';
}
}
}
//
let isSingleColumn = true;
for (const key of keys) {
const colData = tableData[key];
if (!colData || typeof colData !== 'object') {
isSingleColumn = false;
break;
}
// default
const colKeys = Object.keys(colData);
if (colKeys.length !== 1 || (colKeys.length === 1 && colKeys[0] !== 'default')) {
// default
if (colKeys.length > 1 || (colKeys.length === 1 && !colKeys[0].match(/^\d+$/))) {
isSingleColumn = false;
break;
}
}
}
if (isSingleColumn) {
return 'singleColumn';
}
//
let isStandard = true;
for (const key of keys) {
const colData = tableData[key];
if (!colData || typeof colData !== 'object') {
isStandard = false;
break;
}
//
const colKeys = Object.keys(colData);
if (colKeys.length === 0) {
isStandard = false;
break;
}
// default
const hasNonNumericKeys = colKeys.some(k => k !== 'default' && !k.match(/^\d+$/));
if (hasNonNumericKeys) {
isStandard = false;
break;
}
}
if (isStandard) {
return 'standard';
}
//
return 'mixed';
}
/**
* 从仅一行结构中获取值
*/
function getFromSingleRowStructure(tableData, columnStr) {
return tableData.default?.[columnStr];
}
/**
* 从仅一列结构中获取值
*/
function getFromSingleColumnStructure(tableData, columnStr) {
const colData = tableData[columnStr];
if (colData && typeof colData === 'object') {
// default
return colData.default !== undefined ? colData.default : Object.values(colData)[0];
}
return undefined;
}
/**
* 从混合结构中获取值
*/
function getFromMixedStructure(tableData, rowStr, columnStr) {
//
// 1.
const standardValue = tableData?.[columnStr]?.[rowStr];
if (standardValue !== undefined) {
return standardValue;
}
// 2. default
const columnData = tableData[columnStr];
if (columnData && typeof columnData === 'object') {
if (columnData.default !== undefined) {
return columnData.default;
}
}
// 3. default
if (tableData.default && typeof tableData.default === 'object') {
return tableData.default[columnStr];
}
// 4.
for (const key in tableData) {
const data = tableData[key];
if (data && typeof data === 'object') {
//
if (data[columnStr] !== undefined) {
return data[columnStr];
}
//
if (key === columnStr && data[rowStr] !== undefined) {
return data[rowStr];
}
}
}
return undefined;
}
/**
* 统一方法推荐使用这个
*/
function getTableCellValue(tableData, rowValue, columnValue) {
return getTableCellValueEnhanced(tableData, rowValue, columnValue);
}
function addStringIfNotExists(arr: string[], str: string) {
if (!arr.includes(str)) {
arr.push(str);

10
src/components/DesignForm/public/form/formItem.vue

@ -144,8 +144,16 @@ function onValueChange(_type: string) {
//select
function onSekectValueChange(news:number){
if(props.data.options){
let _label="";
for(const op of props.data.options){
if(op.value==news.toString()){
_label= op.label
break;
}
}
fieldChangeEnt &&
fieldChangeEnt({key:props.data.name,value:props.data.options[news-1].label, field:props.data.item.label,rowdex:props.rowIndex})
fieldChangeEnt({key:props.data.name,value:_label, field:props.data.item.label,rowdex:props.rowIndex})
}
}

13
src/components/DesignForm/tableListPage/formPageCont.vue

@ -37,6 +37,7 @@ import RunFlowStep from "@/views/taskplatform/taskmanagement/runFlowStep.vue";
import { customerFormVersionCont } from "@/api/taskapi/types";
import { stringify } from "uuid";
import { nodePoweInfo } from "@/api/taskapi/types";
import {printHtmlPage} from '../printHtmlRender.ts'
const props = defineProps({
isShow: {
@ -231,6 +232,17 @@ interface TreeNode {
children: TreeNode[];
}
function onPrintFrom(){
printHtmlPage(
gainTaskFormInfoData.structure.cfid.toString(),
formState.formData.config.groupKey,
gainTaskFormInfoData.structure.id.toString(),
gainTaskFormInfoData.structure.tablekey,
gainTaskFormInfoData.tableData
)
}
function mapIdsToLabels(treeNodes: TreeNode[], ids1: string): string[] {
/* console.log(treeNodes)
console.log(ids1) */
@ -572,6 +584,7 @@ provide('currentNodeKey', nodeKey)
<el-header height="30px">
<el-icon @click="closeDrawer"><Close /></el-icon>
</el-header>
<el-button type="primary" style="margin:-19px 0px 10px;" v-if="gainTaskFormInfoData" @click="onPrintFrom()" >打印</el-button>
<ak-form
ref="formEl"
v-loading="formLoading"

85
src/components/DesignForm/tableListPage/index.vue

@ -39,6 +39,8 @@ import { softDeletion, retractRunWorkFlow, recalSendMsg } from "@/api/taskapi/ma
import { echatsViews } from "@/api/DesignForm/types";
import { formatNumber } from "@/api/DesignForm/utils";
import { formHasPower } from "@/directive/permission/button";
import { Ref } from "vue";
import {printHtmlPage} from '../printHtmlRender.ts'
//
import FormPageCont from "@/components/DesignForm/tableListPage/formPageCont.vue";
import TableFlow from "@/views/sysworkflow/lowcodepage/pageFlow/tableFlow.vue";
@ -1348,88 +1350,6 @@ const lookPageInfo = (val: any) => {
lookPageInfoIsShow.value = true;
};
/****************表单打印功能************************/
const printRenderMode = ref(false);
const printRenderTree: Ref<any[]> = ref([]);
const printPage = async (row: any) => {
let data:any[]=[]
let _pageConfig:PageConfig
let title:string="表单";
//appkeyformkey
await getPrintTemplate({"versionid":props.versionid,"formkey":props.appKey}).then(resp=>{
title=resp.data.title
if(resp.data.formtemplatejson!=""){
data=JSON.parse(resp.data.formtemplatejson)
}
if(resp.data.pageconfigjson!=""){
_pageConfig=JSON.parse(resp.data.pageconfigjson)
if(_pageConfig.founder!=""){
_pageConfig.founder=row[_pageConfig.founder]
}
if(_pageConfig.founderTime!=""){
_pageConfig.founderTime=row[_pageConfig.founderTime]
}
if(_pageConfig.deptOrg!=""){
_pageConfig.deptOrg=row[_pageConfig.deptOrg]
}
if(_pageConfig.serialNumber!=""){
_pageConfig.serialNumber=row[_pageConfig.serialNumber]
}
}
})
let qrstr=`https://wab.hxgk.group/#/form_table/taskInfo?id=${props.formId}&key=${props.appKey}&
formid=${props.versionid}&formKey=${props.formKey}&state=2`
data.forEach(node=>{
deepLoopForm(node,row)
})
printRenderTree.value=data
printRenderMode.value = true;
ElMessageBox({
message: () => h('div',{style:{ width:'1200px',display:'flex','flex-direction':'column'}},[
h(ElButton, {
type:"primary",
style: "margin:10px 10px 5px auto;",
onClick: () => {
printElement("printContainer")
}
},'打印表单'),
h('div',{style:{ border: '1px solid black', width: 'fit-content', margin: '5px','align-self': 'center'}},[
h(printHtmlRender,{
name:title,
fieldTree:printRenderTree.value,
pageConfig:_pageConfig,
qrcode:qrstr
})
])
]),
showConfirmButton:false,
customStyle: { '--el-messagebox-width':'1300px',padding:'10px'},
}).then(() => {
})
};
const deepLoopForm=(node:fieldTree, row: Record<string, any>)=>{
if(Array.isArray(node)){
node.forEach(item=>{deepLoopForm(item,row)})
return
}
if(node.field!=""){
let rnode:Object;
if (row.hasOwnProperty(node.field!)){//
if (node.type=="table" || node.type=="tabs"){
node.data=row[node.field!]
}else{
node.field=row[node.field!]
}
}
}
}
/**
@ 作者: 秦东
@ 时间: 2024-04-05 11:29:50
@ -2109,6 +2029,7 @@ const isObject = (obj: any) => {
placement="top-end"
>
<el-button
@click="printHtmlPage(props.formId,props.appKey,props.versionid, props.formKey,scope.row)"
type="primary"
size="small"
class="fa fa-print"

7
src/main.ts

@ -31,9 +31,6 @@ import * as pinia from './store/index'
import SketchRule from 'vue3-sketch-ruler'
import 'vue3-sketch-ruler/lib/style.css'
import '@/styles/tree-select-global.css'
import {hiPrintPlugin} from "vue-plugin-hiprint"
const app = createApp(App);
@ -52,10 +49,10 @@ app.directive('focus', {
}
});
hiPrintPlugin.disAutoConnect();
//hiPrintPlugin.disAutoConnect();
app.use(router).use(i18n).use(ComComponents).use(ComWidget).use(ElementPlus, {
locale: zhCn
}).use(hiPrintPlugin).use(AKDesign).use(pinia.store).mount('#app');
}).use(AKDesign).use(pinia.store).mount('#app');

3
src/store/modules/orgMember.ts

@ -9,11 +9,12 @@ export const useOrgMemberStore = defineStore('orgMember', () => {
name: string;
child?:OrgMemberItem[];
}
const listMap = ref<Record<string, string>>({})
const dataTree = ref<OrgMemberItem>({ id: '', name: '', child: [] })
async function init() {
await request({
url: "/systemapi/app/get_org_everyone_people",//"172.20.2.87:39168",
method: "post",

63
src/views/doc/manage.vue

@ -7,7 +7,7 @@
import { getExpirTime, getFileIcon, readableSize,fileType} from "./tools"
import sharePermission from './sharePermission.vue';
import { useUserStore } from "@/store/modules/user";
import { getMatterList,postCreateDir,postDelMatter,postCreateShare,postMatterRename,
import { getMatterList,postCreateDir,postDelMatter,postCreateShare,postMatterRename,postMatterMove,
postDelMatBatch,getMySpaces,doCreateSpace,getRecyclingList} from "@/api/doc/index"
import { matterPage,matterInfo,respCreateShare,matterTree, doFileUpload,matterPermit} from "@/api/doc/type"
import { h } from 'vue'
@ -22,12 +22,13 @@ import {ElSelect,ElOption, ElText,ElInput,TableInstance,ElMessage,UploadFile,
UploadFiles,ElPagination,ElTree,TreeNode,ElDropdown,ElDropdownItem} from "element-plus";
import preview from './preview.vue';
import uploadlog from './uploadlog.vue';
import space from './space.vue';
import { useRoute } from 'vue-router'
import spacePermission from './spacePermission.vue';
import SvgIcon from "@/components/SvgIcon/index.vue";
import {doDelSpace,doAccessManage,doCreateAiagent,setAgentQueryURL,getAgentQueryURL} from "@/api/doc/space"
import Space from "./space.vue";
import Logpanel from "./logpanel.vue";
import Movepanel from "./movepanel.vue";
//TODO: add file icons done!
//TODO: click on table-item 1preview on file .....................
@ -60,7 +61,7 @@ const treeRef = ref();
const currentNode=ref<matterTree>({}) //
const officeHost=import.meta.env.VITE_OFFICE_HOST
const dynamicVNode = ref<VNode | null>(null) //permission
const route = useRoute()
const multipleTableRef = ref<TableInstance>()
const paginInfo = ref({ page: 0, total: 0 })
@ -69,7 +70,7 @@ const PRIVATESPACE = ref(true) //是空间状态的控制 2种:私有云盘和
const SpaceID= ref<{name:string,uuid:string,userUuid:string,manager:boolean,permits:matterPermit}>({}) //spaceid
const spaceEleRef = ref() //space,spaceTreeRefspaceTree
const spaceTreeData=ref<matterTree[]>([])//{name:'',uuid:'root',children:[]}
const spaceTreeRef = ref(); //space
const spaceTreeRef = ref(); //space
let spaceNodeUid="" //currentNode
const modListOrGrild=ref(true)
@ -279,6 +280,30 @@ function onMatterRename(row:matterInfo){
})
}
//
function onMoveFile(row:matterInfo){
const dstuuid=ref(row.puuid)
ElMessageBox({
customStyle: { '--el-messagebox-width':'800px',padding:'40px'},
message:() => h(Movepanel,{
style: { width:'100%' },
modelValue: dstuuid.value,
'onUpdate:modelValue': (val) => {
dstuuid.value = val
},
}),
}).then(() => {
if(dstuuid.value!=row.puuid){
postMatterMove(uid,{
srcUuids:row.uuid,
destUuid:dstuuid.value as string,
}).then(()=>onLoadMatterList())
}else{
alert("目录相同不需要移动")
}
})
}
//
function onSearchFile(name?:string){
let _page: matterPage = {
@ -596,7 +621,7 @@ async function handleSingleFile(ff:File){
fields.append("puuid",uploadFormData.value.puuid)
fields.append("file",ff)
const res = await doFileUpload(fields,'/hxpan/api/matter/upload')
const res = await doFileUpload(uid,fields,'/hxpan/api/matter/upload')
if(res.code!=200){
console.log(ff.name+"上传失败! ")
throw new Error(ff.name+"上传失败!<br> ")
@ -661,7 +686,7 @@ async function handleFolderFile(option:File){
fields.append('file', option)
fields.append("space",uploadFormData.value.space)
fields.append("puuid",puuid)
const res = await doFileUpload(fields,'/hxpan/api/matter/upload')
const res = await doFileUpload(uid,fields,'/hxpan/api/matter/upload')
if(res.code!=200){
console.log(_path+"上传失败! ")
throw new Error(_path +" 上传失败!<br> ")
@ -857,6 +882,15 @@ function onAiAgent(row:matterInfo){
})
}
//
const onSpaceShareRequest=(spaceid:string,name:string)=>{
spaceTreeRef.value.data.forEach(item=>{
if(item.uuid==spaceid){
onSpaceNodeClick(item,null,null,null)
}
})
}
//
async function onAgentQueryURL(row:matterInfo){
await getAgentQueryURL(row.uuid).then(resp=>{
@ -892,7 +926,7 @@ async function onAgentQueryURL(row:matterInfo){
//------------------------------------------------------
//http://172.20.2.87:6010/api/alien/preview/5a10aaf6-396e-4d9a-7e87-3c5c8029d4db/123.png?ir=fill_100_100
//
onMounted(() => {
onMounted(async () => {
// treeRef.value.append(
// {name:'',uuid:'root',dir:false},
// currentNode.value.uuid
@ -900,7 +934,7 @@ onMounted(() => {
currentNode.value={uuid:"root"}
//
getMySpaces(uid,{roles:Departs.value}).then((resp)=>{
await getMySpaces(uid,{roles:Departs.value}).then((resp)=>{
resp.data.forEach((item)=>{
let ismanager=false
if(item.userUuid==rawUid || item.managers.includes(rawUid)) ismanager=true;
@ -913,6 +947,14 @@ onMounted(() => {
}else{
modListOrGrild.value=true
}
const query = route.query
if(query.s_token){
const s_token=decodeURIComponent(atob(query.s_token as string)).split("|")
if(s_token.length==3){
onSpaceShareRequest(s_token[0],s_token[2])
}
}
});
const handleSelectionChange = (val:matterInfo[]) => {
@ -1081,7 +1123,8 @@ v-if="modListOrGrild"
<el-button size="small" :icon="Share" circle @click="onShareMatter(scope.row)"></el-button>
<el-button size="small" :icon="Download" circle @click="onDownload(scope.row)"></el-button>
<el-button size="small" :icon="Edit" circle @click="onlyOfficeEdit(scope.row)"></el-button>
<el-button size="small" :icon="Delete" circle @click="onDelMatter(scope.row)"></el-button>
<el-button size="small" circle @click="onMoveFile(scope.row)"></el-button>
<el-button size="small" :icon="Delete" circle @click="onDelMatter(scope.row)"></el-button>
<el-button size="small" circle @click="onMatterRename(scope.row)"></el-button>
</span>
</div>
@ -1119,6 +1162,7 @@ v-if="modListOrGrild"
<li @click="onShareMatter(row)">分享</li>
<li @click="onDownload(row)">下载</li>
<li @click="onlyOfficeEdit(row)">编辑</li>
<li @click="onMoveFile(row)">移动</li>
<li @click="onDelMatter(row)">删除</li>
<li @click="onMatterRename(row)">重命名</li>
</span>
@ -1238,6 +1282,7 @@ ref="spaceEleRef" :uid="uid" :raw-uid="rawUid" :list-or-grid="modListOrGrild"
display: flex;
flex-wrap: wrap; /* 关键属性,允许子元素自动换行 */
align-content: flex-start;
min-height: 500px;
.grid-item{
position: relative;
width: 134px;

344
src/views/doc/movepanel.vue

@ -0,0 +1,344 @@
<!--
@ 作者: han2015
@ 时间: 2025-05-12 15:39:13
@ 备注: 文档管理组件
-->
<script lang="ts" setup>
import { useUserStore } from "@/store/modules/user";
import { getMatterList,getMySpaces} from "@/api/doc/index"
import { matterPage,matterInfo,matterTree,matterPermit} from "@/api/doc/type"
import Space from "./space.vue";
import {ElSelect,ElOption, ElText,ElInput,ElTree,TreeNode,ElDropdown,ElDropdownItem} from "element-plus";
import { doAccessManage,getSpaceMatterList,doCreateSpaceDir,doDelSpaceMatter,
doAiTraining,doAiDocDels,spaceMatterRename} from "@/api/doc/space"
//
defineProps({
modelValue: String //
})
const emit = defineEmits(['update:modelValue'])
const userStore = useUserStore();
const uid=btoa("p0"+userStore.userInfoCont.userId);
const rawUid="p0"+userStore.userInfoCont.userId
const matterList = ref<matterInfo[]>([])
const treeData=ref<matterTree[]>([])//{name:'',uuid:'root',children:[]}
const currentNode=ref<matterTree>({}) //
const paginInfo = ref({ page: 0, total: 0 })
const SpaceID= ref<{name:string,uuid:string,userUuid:string,manager:boolean,permits:matterPermit}>({}) //spaceid
const spaceEleRef = ref() //space,spaceTreeRefspaceTree
const spaceTreeRef = ref(); //space
let spaceNodeUid="" //currentNode
let isNewSpaceNode=true //
const spaceCutLevelPermit=ref(0)
const Departs = computed(() => {
return `${'p0'+userStore.userInfoCont.userId},${userStore.userInfoCont.company},${userStore.userInfoCont.department},${userStore.userInfoCont.organization}`
})
enum PERMITS {
FORBID, //0
VIEW, //1
DOWNLOAD, //2
UPLOAD, //3
UPANDDOWNLOAD, //4
EDIT, //5
MANAGER, //6
}
//
function onLoadMatterList(){
emit('update:modelValue', currentNode.value.uuid)
let _page: matterPage = {
page: paginInfo.value.page,
pageSize: 50,
orderCreateTime: "DESC",
orderDir: "DESC",
dir:"true",
puuid:currentNode.value.uuid,
deleted:false
};
getMatterList(uid,_page).then((resp)=>{
//page+1 index1apiindex0
paginInfo.value={total:resp.data.totalPages,page:resp.data.page}
matterList.value=resp.data.data
})
}
//
function onNodeExpand(node: TreeNode, resolve: (data: matterTree[]) => void, reject: () => void) {
let cuuid ="root"
if ((node.data as matterTree).uuid) {
cuuid= (node.data as matterTree).uuid
currentNode.value = node.data
}
let _page: matterPage = {
page: 0,
pageSize: 50,
orderCreateTime: "DESC",
orderDir: "DESC",
dir:"true",
puuid: cuuid,
deleted: false
};
getMatterList(uid, _page).then((resp) => {
let node_data = resp.data.data.filter((item) => {
return item.dir
}).map((val) => {
const copy = structuredClone(val)
copy.dir = !copy.dir
return copy
})
resolve(node_data)
}).catch(() => reject())
emit('update:modelValue', currentNode.value.uuid)
}
//
function onNodeClick(data:matterTree,node:TreeNode,self:any,env:any){
if (data){
if (currentNode.value.uuid === data.uuid) return;
currentNode.value = data
}else{
currentNode.value={uuid:"root",name:"个人空间"}
}
onLoadMatterList()
}
//-------------------space feature---------------------
function onSpaceNodeClick(data:matterTree,node:TreeNode,self:any,env:any){
//
if(spaceNodeUid==data.uuid) return;
spaceNodeUid=data.uuid
if(data.uuid.startsWith("s0") && data.uuid!=SpaceID.value.uuid){ //
currentNode.value={uuid:"root",name:data.name}//currentNode
SpaceID.value={
name: data.name ?? "",
uuid: data.uuid ?? "",
userUuid: data.userUuid ?? "",
manager: data.manager ?? false,
permits:data.permits ?? { id: 0, MatterUuid: '', data: '{}' }
};
onSpaceMatterList()
}else{
let matter= {
uuid:data.uuid==SpaceID.value.uuid?"root":data.uuid,
puuid: data.puuid,
name:data.name,
agent:data.agent,
dir:true,
permits:data.permits,
path:data.path,
permitVal:data.permitVal
}
//
handleSpaceDoubleClick(matter)
}
}
function flushSpaceTree(uuid:string,data:matterTree[]){
if(uuid==="root") uuid=SpaceID.value.uuid
spaceTreeRef.value.updateKeyChildren(uuid,data)
}
function handleSpaceDoubleClick(row:matterInfo,ind?:number){
if(row.dir){
//
if(row.permitVal){
spaceCutLevelPermit.value=row.permitVal
}else{
if (SpaceID.value.manager) {
spaceCutLevelPermit.value=9
}else{
let _pert: Record<string, number>
_pert = JSON.parse(row.permits!.data)
let val=_pert[rawUid.replace("p0","")]
if(val){
spaceCutLevelPermit.value = val
}else{
spaceCutLevelPermit.value = PERMITS.FORBID //
}
}
}
isNewSpaceNode=true
//1
//2:
if(currentNode.value.puuid==row.uuid || row.uuid=="root") isNewSpaceNode=false
currentNode.value=row
onSpaceMatterList()
}
}
//
function onSpaceMatterList(){
emit('update:modelValue', currentNode.value.uuid)
let _page: matterPage= {
page: paginInfo.value.page,
pageSize: 50,
orderCreateTime: "DESC",
orderDir: "DESC",
puuid:currentNode.value.uuid,
dir:"true",
deleted:false,
space:SpaceID.value.uuid,
};
getSpaceMatterList(uid,_page).then((resp)=>{
if (SpaceID.value.manager) {
resp.data.data.forEach(item => {
item.permitVal=PERMITS.MANAGER
})
matterList.value=resp.data.data
}else{
matterList.value=resp.data.data.filter(item=>{ //
if(item.permits.ID==0){
item.permitVal=spaceCutLevelPermit.value
}else{
//
let _pert: Record<string, number>
_pert = JSON.parse(item.permits!.data)
let val=_pert[rawUid.replace("p0","")]
if(val){
item.permitVal = val
}else{
item.permitVal = PERMITS.FORBID //
}
}
if(item.permitVal>0){
return true
}
return false
})
}
//使
let node_data = matterList.value.filter(item=> {
return item.dir
}).map(val => {
const copy = structuredClone(toRaw(val))
copy.dir = !copy.dir
copy.manager=SpaceID.value.manager
return copy
})
if(isNewSpaceNode) {
//uuidrootspaceiduuid
//rootuuidspaceidroot
if(currentNode.value.uuid=="root") flushSpaceTree(SpaceID.value.uuid,node_data);
else flushSpaceTree(currentNode.value.uuid,node_data);
}
})
}
///////////////////////////////////////////////////////
//
onMounted(async () => {
currentNode.value={uuid:"root",name:"个人空间"}
//
await getMySpaces(uid,{roles:Departs.value}).then((resp)=>{
resp.data.forEach((item)=>{
let ismanager=false
if(item.userUuid==rawUid || item.managers.includes(rawUid)) ismanager=true;
spaceTreeRef.value.append({name:item.name,uuid:item.uuid,dir:false,userUuid:item.userUuid,manager:ismanager,permits:item.permits})
})
})
emit('update:modelValue', currentNode.value.uuid)
});
</script>
<template>
<p> <span style="font-size:20px;font-weight: bold;">请选择目标节点</span> 当前节点 > {{ currentNode.name }}</p>
<div class="menus_tree">
<div class="area_header">
<el-icon :size="20"><House /></el-icon><span class="area_name" > </span>
</div>
<el-tree
ref="treeRef"
style="max-width: 600px"
:data="treeData"
node-key="uuid"
highlight-current
lazy
:load="onNodeExpand"
:props="{label: 'name',children:'children',isLeaf:'dir'}"
:default-expanded-keys="['root']"
@node-click="onNodeClick"
>
<template #default="{ node, data }">
<div class="tree-item">
<span>{{ node.label }}</span>
</div>
</template>
</el-tree>
<div class="area_header">
<el-icon :size="20"><Collection /></el-icon> <span class="area_name" > </span>
</div>
<el-tree
ref="spaceTreeRef"
style="max-width: 600px"
node-key="uuid"
accordion
highlight-current
lazy
:props="{label: 'name',children:'children',isLeaf:'dir'}"
@node-click="onSpaceNodeClick"
>
<template #default="{ node, data }">
<div class="tree-item">
<span>{{ node.label}}</span>
</div>
</template>
</el-tree>
</div>
</template>
<style lang="scss" scoped>
.menus_tree{
display: flex;
flex-direction: column;
height:550px;
overflow-y: scroll;
.el-tree{
--el-color-primary-light-9:#6eb3f8;
--el-tree-node-hover-bg-color:#a1c7ee;
--el-tree-node-content-height:43px;
--el-tree-expand-icon-color:#4c4c4e;
}
}
.area_header{
display: flex;
margin-top: 9px;
padding: 5px 0;
background-color: white;
align-items: center;
.area_name{
align-content: center;
margin-left: 8px;
color: #686854;
font-weight: bold;
}
}
</style>
<style lang="scss">
.el-message-box__message{
width: 100%;
}
</style>

67
src/views/doc/space.vue

@ -5,29 +5,30 @@
-->
<script lang="ts" setup>
import { getFileIcon, readableSize,fileType} from "./tools"
import { useRoute } from 'vue-router'
import sharePermission from './sharePermission.vue';
import spacePermission from './spacePermission.vue';
import { matterPage,matterInfo,matterTree,doFileUpload,matterPermit} from "@/api/doc/type"
import { doAccessManage,getSpaceMatterList,doCreateSpaceDir,doDelSpaceMatter,
doAiTraining ,spaceMatterRename} from "@/api/doc/space"
doAiTraining,doAiDocDels,spaceMatterRename} from "@/api/doc/space"
import { h } from 'vue'
import {
Delete,
View,
Download,
Plus,Search,
Share,
Edit,
Setting,
Grid,List,
} from '@element-plus/icons-vue'
import {ElMessage,UploadFile,UploadFiles,ElPagination, ElProgress} from "element-plus";
import {ElMessage,UploadFile,UploadFiles,ElPagination, ElProgress,ElText} from "element-plus";
import aiagent from './agent.vue';
import uploadlog from './uploadlog.vue';
import router from "@/router";
import SvgIcon from "@/components/SvgIcon/index.vue";
import { useOrgMemberStore } from "@/store/modules/orgMember";
const orgMembers = useOrgMemberStore() //
const route = useRoute()
const siteHost=document.location.origin;
const defaultAiAgent=import.meta.env.VITE_DEFAULT_AI_AGENT
const matterList = ref<matterInfo[]>([])
const searchname=ref("") //
@ -158,6 +159,7 @@ function onDelMatter(row:matterInfo){
currentNode.value.uuid = row.puuid ?? ""
onLoadMatterList()
})
handleAiDelete(row)
})
}
}
@ -176,7 +178,7 @@ function onDownload(row:matterInfo){
}
//
function onLoadMatterList(name?:string){
function onLoadMatterList(name?:string,_puuid?:string){
percentage.value=0
onprogress.value=false
@ -195,6 +197,7 @@ function onLoadMatterList(name?:string){
pageSize: 50,
orderCreateTime: "DESC",
orderDir: "DESC",
puuid:_puuid,
deleted:false,
name:name,
space:props.spaceid,
@ -410,7 +413,7 @@ async function handleSingleFile(ff:File){
fields.append("puuid",uploadFormData.value.puuid)
fields.append("file",ff)
const res = await doFileUpload(fields,'/hxpan/api/space/upload')
const res = await doFileUpload(props.uid,fields,'/hxpan/api/space/upload')
if(res.code!=200){
console.log(ff.name+"上传失败! ")
throw new Error(ff.name+"上传失败!<br> ")
@ -476,7 +479,7 @@ async function handleFolderFile(option:File){
fields.append('file', option)
fields.append("space",uploadFormData.value.space)
fields.append("puuid",puuid)
const res = await doFileUpload(fields,'/hxpan/api/space/upload')
const res = await doFileUpload(props.uid,fields,'/hxpan/api/space/upload')
if(res.code!=200){
console.log(_path+"上传失败! ")
throw new Error(_path+"上传失败!<br> ")
@ -528,9 +531,44 @@ function handleAiUpload(info:matterInfo){
}
}
function handleAiDelete(info:matterInfo){
//
if (info.path?.startsWith(currentAgent.value.path)){
doAiDocDels(`/agents/${currentAgent.value.uuid}/deldoc`,{
"name":info.name,
"path":info.path,
}).then(resp=>{
ElMessage({
message: '已成功删除',
type: 'error',
plain: true,
})
})
}
}
//-------------------------------------------------
//-------------------edit & preive file for space---------------------
//
const onShareSpaceFile=(row:matterInfo)=>{
const str=btoa(encodeURIComponent(`${props.spaceid}|${row.puuid}|${row.name}`))
let _shareURL=`${siteHost}/#/doc/manage?s_token=${str}`
ElMessageBox({
title: '文件分享:',
customStyle: { '--el-messagebox-width':'800px',padding:'40px'},
message: () => h('div',{style:{display:'flex','flex-direction':'column','line-height':'34px', width:'600px'}},[
h(ElText,{style:{'align-self':'flex-start','font-weight':'bold'}},()=>row.name),
h(ElText,{style:{'align-self':'flex-start','overflow-wrap': 'anywhere'}},()=>_shareURL),
]),
confirmButtonText: '复制分享链接',
showCancelButton: true
}).then(()=>{
if(!navigator.clipboard) alert("clipboard 不可用")
navigator.clipboard.writeText(_shareURL)
})
}
//
function onPrivateView(row:matterInfo){
const _type=fileType(row.name!)
@ -603,7 +641,14 @@ onMounted(() => {
CutLevelPermit.value = PERMITS.FORBID //
}
}
const query = route.query
if(query.s_token){
const s_token=decodeURIComponent(atob(query.s_token as string)).split("|")
if(s_token.length==3){
onLoadMatterList(s_token[2],s_token[1])
return
}
}
onLoadMatterList()
});
@ -611,7 +656,7 @@ const handleSelectionChange = (val:matterInfo[]) => {
tabSelected.value = val
}
defineExpose({handleDoubleClick,onDelMatter,onSpaceMatterRename})
defineExpose({handleDoubleClick,onDelMatter,onSpaceMatterRename,onLoadMatterList})
//
function isOwner(){
@ -701,6 +746,7 @@ v-if="modListOrGrild"
<el-tag v-if="scope.row.agent" effect="dark" size="small" type="success" round >智能体</el-tag>
<div v-show="currentHoverRow === scope.row.name" style="display:inline; margin-left:15px">
<el-button v-if="getFileIcon(scope.row.name)!='img'" size="small" :icon="View" circle @click="onPrivateView(scope.row)"></el-button>
<el-button size="small" :icon="Share" circle @click="onShareSpaceFile(scope.row)"></el-button>
<el-button v-if="scope.row.permitVal>=PERMITS.DOWNLOAD" size="small" :icon="Download" circle @click="onDownload(scope.row)"></el-button>
<span v-if="scope.row.permitVal>=PERMITS.MANAGER" class="manager_span" >
<el-button v-if="!scope.row.dir" size="small" circle @click="handleAiUpload(scope.row)">AI</el-button>
@ -741,6 +787,7 @@ v-if="modListOrGrild"
</div>
<ul v-if="row.name!=''" v-show="currentHoverRow === row.name" class="grid-menus" @mouseleave="currentHoverRow=''">
<li v-if="getFileIcon(row.name)!='img'" @click="onPrivateView(row)">预览</li>
<li @click="onShareSpaceFile(row)">分享</li>
<li v-if="row.permitVal! >= PERMITS.DOWNLOAD" @click="onDownload(row)">下载</li>
<span v-if="row.permitVal! >= PERMITS.EDIT" >
<li @click="onlyOfficeEdit(row)">编辑</li>

443
src/views/sysworkflow/lowcodepage/appPage/appPageForm/printHtmlDom.js

@ -1,73 +1,414 @@
export function printElement(elementId) {
/**
*
* 智能分页打印解决方案 - 标准表格版支持 caption
* 新增表格分页时保留 caption 在第一页
*
*/
export function printWithSmartPagination(elementId, options = {}) {
const {
paperSize = 'A4',
pageHeight= 0,
margin = 15,
debug = false,
landscape = false,
repeatHeader = true
} = options;
const element = document.getElementById(elementId);
if (!element) {
console.error('元素未找到');
console.error('❌ 未找到元素:', elementId);
return;
}
// 克隆元素,避免影响原页面
const printContent = element.cloneNode(true);
console.log('📄 开始分析页面结构...');
const structure = analyzePageStructure(element);
const pages = paginateContent(structure, { paperSize,pageHeight, margin, landscape, repeatHeader });
console.log(`✅ 分页完成,共 ${pages.length}`);
const printContent = buildPrintPages(pages, structure, { repeatLogo: true });
printViaIframe(printContent, { paperSize, margin, debug, landscape });
}
function analyzePageStructure(container) {
const header = container.querySelector('#printHeader');
const mainContent = container.querySelector('#printContainer');
return {
hasHeader: !!header,
hasBottom: false,
header: header?.cloneNode(true),
bottom: "",
main: mainContent,
tables: Array.from(mainContent.querySelectorAll('table'))
};
}
function paginateContent(structure, options) {
const { paperSize,pageHeight, margin,landscape } = options;
let _pageH=pageHeight
//处理页高的问题
if(pageHeight==0){
if(landscape){
_pageH = {
'A5': 148,'A4': 210, 'A3': 297
}[paperSize];
}else{
_pageH = {
'A5': 210,'A4': 297, 'A3': 420
}[paperSize];
}
}
const paperHeight = _pageH * 3.78;
const marginPx = margin * 3.78;
const printableHeight = paperHeight - marginPx * 2;
// 创建隐藏iframe
const headerHeight = structure.hasHeader ? structure.header.getBoundingClientRect().height : 0;
const bottomHeight = structure.hasBottom ? structure.bottom.getBoundingClientRect().height : 0;
const availableHeight = printableHeight - headerHeight - bottomHeight-30; //50是页眉与内容的间距,
console.log(`📊 分页基础参数:`);
console.log(` 纸张高度: ${paperHeight.toFixed(0)}px, 可用高度: ${availableHeight.toFixed(0)}px`);
const pages = [];
let currentPage = [];
let currentHeight = 0;
Array.from(structure.main.children).forEach((child, index) => {
const childHeight = child.getBoundingClientRect().height;
const elementInfo = `${child.tagName}${child.className ? '.' + child.className.split(' ')[0] : ''}`;
const isTableContainer = child.querySelector('table');
const remainingHeight = availableHeight - currentHeight;
console.log(`\n🔍 [元素 ${index + 1}] ${elementInfo}`);
console.log(` 高度: ${childHeight.toFixed(1)}px, 累积: ${currentHeight.toFixed(1)}px`);
console.log(` 剩余空间: ${remainingHeight.toFixed(1)}px`);
// ==================== 智能表格分割 ====================
if (isTableContainer) {
if (currentHeight + childHeight <= availableHeight * 0.92) {
console.log(` ✅ 表格能完整放入当前页`);
currentPage.push(child.cloneNode(true));
currentHeight += childHeight;
} else {
console.log(` ⚠️ 表格无法完整放入,剩余空间: ${remainingHeight.toFixed(1)}px`);
// 对表格进行智能分页,第一页使用剩余空间
const tablePages = paginateTableSmart(child, remainingHeight, availableHeight, options);
if (tablePages.length > 0) {
// 第一页:部分表格 + 前面累积的普通元素(合并在一起)
const firstPageContent = tablePages[0];
if (firstPageContent.length > 0) {
currentPage.push(...firstPageContent);
pages.push([...currentPage]);
console.log(` ✅ 创建混合页: ${currentPage.length}个元素 (含caption和部分表格)`);
}
// 剩余的表格页(纯表格)
for (let i = 1; i < tablePages.length; i++) {
pages.push(tablePages[i]);
console.log(` 📊 添加纯表格页 ${i} (${tablePages[i].length}个元素)`);
}
}
currentPage = [];
currentHeight = 0;
}
return;
}
// ============================================================
// 普通元素处理
if (currentHeight + childHeight > availableHeight * 0.92) {
if (currentPage.length > 0) {
console.log(` 📄 推入当前页 (${currentPage.length}个元素)`);
pages.push([...currentPage]);
currentPage = [];
currentHeight = 0;
}
}
currentPage.push(child.cloneNode(true));
currentHeight += (childHeight+20); //20margin
});
// 处理剩余元素
if (currentPage.length > 0) {
console.log(`\n📄 最后推入剩余元素 (${currentPage.length}个)`);
pages.push([...currentPage]);
}
return pages;
}
/**
* 智能表格分页函数支持 caption
* @returns {Array<Array>} 分页结果每个元素是一个页面数组
*/
function paginateTableSmart(tableContainer, firstPageHeight, subsequentPageHeight, options) {
const table = tableContainer.querySelector('table');
const caption = table?.querySelector('caption'); // 获取 caption
const thead = table?.querySelector('thead');
const tbody = table?.querySelector('tbody');
if (!thead || !tbody) {
console.warn('⚠️ 表格缺少 thead 或 tbody');
return [[tableContainer.cloneNode(true)]];
}
const captionHeight = caption ? caption.getBoundingClientRect().height : 0;
const headerHeight = thead.getBoundingClientRect().height;
const rows = Array.from(tbody.querySelectorAll('tr'));
console.log(` 📊 智能分页参数:`);
console.log(` caption高度: ${captionHeight.toFixed(1)}px, 表头高度: ${headerHeight.toFixed(1)}px`);
console.log(` 第一页可用: ${firstPageHeight.toFixed(1)}px, 后续页可用: ${subsequentPageHeight.toFixed(1)}px`);
console.log(` 总行数: ${rows.length}`);
// 如果整个表格能在第一页放下
const totalTableHeight = tableContainer.getBoundingClientRect().height;
if (totalTableHeight <= firstPageHeight * 0.85) {
console.log(` ✅ 表格总高度 ${totalTableHeight.toFixed(1)}px 能放入第一页`);
// 包含 caption
return [[createTablePage(tableContainer, thead, rows, true)]];
}
const pages = [];
let currentRows = [];
let currentHeight = captionHeight + headerHeight; // 包含 caption 高度
let isFirstPage = true;
rows.forEach((row, rowIndex) => {
const rowHeight = row.getBoundingClientRect().height;
const pageHeight = isFirstPage ? firstPageHeight : subsequentPageHeight;
// 检查是否需要分页
if (currentHeight + rowHeight > pageHeight * 0.85 && currentRows.length > 0) {
// 创建当前页(只有第一页包含 caption)
pages.push([createTablePage(tableContainer, thead, currentRows, isFirstPage)]);
console.log(` 📊 分页${isFirstPage ? '(含caption)' : ''}: ${currentRows.length}行, ${currentHeight.toFixed(1)}px`);
// 重置为新页面
currentRows = [row];
currentHeight = captionHeight + headerHeight + rowHeight; // 新页面也要包含 caption 高度
isFirstPage = false;
} else {
currentRows.push(row);
currentHeight += rowHeight;
}
// 最后一行
if (rowIndex === rows.length - 1 && currentRows.length > 0) {
pages.push([createTablePage(tableContainer, thead, currentRows, isFirstPage)]);
console.log(` 📊 最后分页${isFirstPage ? '(含caption)' : ''}: ${currentRows.length}行, ${currentHeight.toFixed(1)}px`);
}
});
console.log(` 📊 表格总计分页: ${pages.length}`);
return pages;
}
/**
* 创建表格页
* @param {boolean} includeCaption - 是否包含 caption只有第一页为 true
*/
function createTablePage(originalContainer, thead, rows, includeCaption = false) {
const container = originalContainer.cloneNode(false);
const table = document.createElement('table');
const originalTable = originalContainer.querySelector('table');
table.className = originalTable.className;
table.style.cssText = originalTable.style.cssText;
// 只在第一页添加 caption
if (includeCaption) {
const caption = originalTable.querySelector('caption');
if (caption) {
table.appendChild(caption.cloneNode(true));
}
}
table.appendChild(thead.cloneNode(true));
const tbody = document.createElement('tbody');
rows.forEach(row => tbody.appendChild(row.cloneNode(true)));
table.appendChild(tbody);
container.appendChild(table);
return container;
}
function buildPrintPages(pages, structure, options) {
const container = document.createElement('div');
container.className = 'print-report-container';
pages.forEach((pageElements, index) => {
const pageDiv = document.createElement('div');
pageDiv.className = 'print-page';
const elements = Array.isArray(pageElements) ? pageElements : [pageElements];
if (options.repeatLogo && structure.hasHeader) {
const headerClone = structure.header.cloneNode(true);
headerClone.style.borderBottom = '2px solid rgb(182, 181, 181)';
headerClone.style.marginBottom = '20px';
pageDiv.appendChild(headerClone);
}
const contentDiv = document.createElement('div');
contentDiv.className = 'print-page-content';
elements.forEach(el => {
if (el?.nodeType === Node.ELEMENT_NODE) {
contentDiv.appendChild(el);
}
});
pageDiv.appendChild(contentDiv);
if (index === pages.length - 1 && structure.hasBottom) {
const bottomClone = structure.bottom.cloneNode(true);
bottomClone.style.marginTop = '30px';
bottomClone.style.borderTop = '1px solid #ccc';
bottomClone.style.paddingTop = '15px';
pageDiv.appendChild(bottomClone);
}
container.appendChild(pageDiv);
});
return container;
}
function printViaIframe(content, options) {
const { paperSize, margin, debug,landscape } = options;
const iframe = document.createElement('iframe');
iframe.style.display = 'none';
document.body.appendChild(iframe);
const iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
// 写入样式和内容
let originalStyles = '';
try {
originalStyles = collectAllStyles();
if (debug) console.log('📄 已收集原始样式,长度:', originalStyles.length);
} catch (e) {
console.warn('⚠️ 收集样式时出错:', e);
}
const orientation = landscape ? 'landscape' : 'portrait';
const printStyles = `
@page {
size: ${paperSize.toLowerCase()} ${orientation};
margin: ${margin}mm;
}
body {
margin: 0;
padding: 0;
font-family: "SimSun", "Microsoft YaHei", serif;
background: white;
}
.print-page {
page-break-after: always;
padding:${margin}mm ${margin+5}mm;
}
.print-page:last-child {
page-break-after: avoid;
}
table {
width: 100% !important;
border-collapse: collapse !important;
}
th, td {
border: 1px solid rgb(182, 181, 181) !important;
page-break-inside: avoid !important;
text-align: center;
line-height: 30px;
}
thead {
display: table-header-group !important;
}
table caption{
text-align: center;
caption-side: top;
color: black;
font-weight: bold;
margin: 5px;
}
h3{
width: 100%;
font-size: 25px;
font-weight: bold;
text-align:center;
}
h5{
font-weight: bold;
width: 100%;
margin: 5px;
text-align:center;
}
.page_brow{
padding: 0 !important;
display: flex;
justify-content: space-between;
align-items: center;
}
`;
const allStyles = `${originalStyles}\n${printStyles}`;
iframeDoc.open();
iframeDoc.write(`
<!DOCTYPE html>
<html>
<head>
<title>打印</title>
<style>
/* 打印优化样式 */
body { margin: 20px; font-size: 14px; }
@media print {
body { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
}
#printContainer table{
display: block;
border: 0px solid;
border-spacing: 1px;
border-collapse: collapse;
}
#printContainer .el-table th.el-table__cell{
background: none !important;
}
#printContainer table th{
border-right: 1px solid;
}
#printContainer table td{
border: 1px solid;
}
/* 复制原页面样式 */
${Array.from(document.styleSheets)
.map(sheet => {
try {
return Array.from(sheet.cssRules).map(rule => rule.cssText).join('\n');
} catch (e) {
return '';
}
})
.join('\n')}
</style>
<meta charset="UTF-8">
<title>检验日报打印</title>
<style>${allStyles}</style>
</head>
<body style="background:white !important;">
${printContent.outerHTML}
<body>
${content.outerHTML}
${debug ? '<div style="position: fixed; top: 10px; right: 10px; background: red; color: white; padding: 10px; z-index: 9999;">🔍 调试模式</div>' : ''}
</body>
</html>
`);
iframeDoc.close();
// 触发打印
iframe.onload = function() {
iframe.contentWindow.print();
// 延迟移除iframe
setTimeout(() => document.body.removeChild(iframe), 1000);
iframe.onload = () => {
setTimeout(() => {
if (debug) {
iframe.style.display = 'block';
iframe.style.position = 'fixed';
iframe.style.inset = '0';
iframe.style.width = '100%';
iframe.style.height = '100%';
iframe.style.zIndex = '99999';
iframe.style.background = 'white';
console.log('✅ 调试模式已开启');
} else {
iframe.contentWindow.print();
setTimeout(() => iframe.remove(), 10000);
}
}, 300);
};
}
function collectAllStyles() {
const styles = [];
Array.from(document.styleSheets).forEach(sheet => {
try {
const rules = Array.from(sheet.cssRules).map(r => r.cssText).join('\n');
if (rules) styles.push(rules);
} catch (e) {
console.warn('⚠️ 无法读取样式表:', e);
}
});
document.querySelectorAll('style').forEach(style => {
if (style.textContent) styles.push(style.textContent);
});
return styles.join('\n');
}

37
src/views/sysworkflow/lowcodepage/appPage/appPageForm/printSetupPage2.vue

@ -13,6 +13,7 @@ interface fieldTreeEx extends fieldTree{
unitName?:string;
list?:fieldTreeEx[];
columns?:fieldTreeEx[];
options?:[]
}
const printMode=ref("")
@ -27,7 +28,7 @@ const pageSize=ref('A4')
const pageDret=ref('vtal')
//pageConfig
const systemType:string[]=["founder","founderTime","deptOrg","serialNumber"]
const systemType:string[]=["founder","founderTime","deptOrg"]
const props = defineProps({
@ -104,6 +105,13 @@ const updatePageConfig=(cmd:string)=>{
}
}
const onClearTreeData=()=>{
fieldTreeData.value=[]
props.state.formData.list.forEach((item:fieldTreeEx)=>{
fieldTreeData.value.push(deepLoop(item))
})
}
//
const onSaveTreeData=()=>{
let str:string=safeStringify(htmlRenderData.value)
@ -146,6 +154,15 @@ const deepLoop=(item:fieldTreeEx)=>{
child:[],
}
break;
case "select":
case "checkbox":
data={
field:item.name?? "",
type:item.type,
name:item.item ? item.item.label : "",
options: item.options
}
break;
default:
data={
field:item.name?? "",
@ -270,14 +287,17 @@ onMounted(async ()=>{
})
/*********************为解决pageconfig字段的问题**************************/
console.log(fieldTreeData.value.length,"length????")
let newForm:boolean;
if(fieldTreeData.value.length==0){
newForm=true;
}
props.state.formData.list.forEach((item:fieldTreeEx)=>{
if(systemType.includes(item.type)) {
//type
objPageConfig.value[item.type as keyof PageConfig]=item.name??""
}
//objPageConfigfieldTreeData
if(newForm){
@ -315,13 +335,12 @@ const updateNodeData=(val:fieldTree,val2:boolean,val3:boolean)=>{
<el-tab-pane label="默认" name="html">
<div style="display: grid; grid-template-columns: 1fr 4fr;">
<div class="hiprintEpContainer" >
<el-button type="primary" @click="onSaveTreeData">保存模板</el-button>
<span>系统字段</span>
<el-checkbox-group v-model="objPageConfig.pagebrow" class="basic_fields">
<el-checkbox label="创建人" value="founder" />
<el-checkbox label="创建时间" value="founderTime" />
<el-checkbox label="所属部门" value="deptOrg" />
<el-checkbox label="表单编号" value="serialNumber" />
<el-checkbox label="表单编号" value="masters_key" />
</el-checkbox-group>
<el-checkbox v-model="objPageConfig.qrcode" style="margin-left: 22px;" label="表单二维码" />
<span>业务字段</span>
@ -342,7 +361,7 @@ const updateNodeData=(val:fieldTree,val2:boolean,val3:boolean)=>{
</template>
</el-tree>
</div>
<div style="border: 1px solid black;margin: 5px;">
<div style="border: 1px solid black;margin: 5px;background: #f1f1f1;">
<div style="display: flex;justify-content:center">
<el-radio-group v-model="pageSize" fill="#6cf" @change="(val)=>updatePageConfig(val)">
<el-radio-button label="A4" value="A4" />
@ -353,18 +372,22 @@ const updateNodeData=(val:fieldTree,val2:boolean,val3:boolean)=>{
<el-radio-button label="横向" value="htal" />
<el-radio-button label="纵向" value="vtal" />
</el-radio-group>
<el-button style="position: absolute;right: 190px;" type="primary" @click="onClearTreeData">重置</el-button>
<el-button style="position: absolute;right: 90px;" type="primary" @click="onSaveTreeData">保存模板</el-button>
</div>
<div style="margin: 5px; border: 1px dashed black;overflow-y: scroll; scrollbar-width: none;" :style="{width: objPageConfig.width,height:objPageConfig.height}">
<div style="margin: 10px auto; background: white;box-shadow:0px 0px 6px rgba(0,0,0,0.12);overflow-y: scroll; scrollbar-width: none;"
:style="{width: objPageConfig.width,height:objPageConfig.height}">
<printHtmlRender :name="props.state.formOtherData.formName" :qrcode="'test'" :page-config="objPageConfig" :field-tree="htmlRenderData"/>
</div>
</div>
</div>
</el-tab-pane>
<el-tab-pane label="定制表单" name="custom">
<!-- <el-tab-pane label="定制表单" name="custom">
<div style="width: 100%;">
ssss
</div>
</el-tab-pane>
</el-tab-pane> -->
</el-tabs>
</template>
<style lang="scss" scoped>

Loading…
Cancel
Save