体检结果汇总界面修改

This commit is contained in:
张佳炜 2025-03-03 16:42:39 +08:00
parent a5ffa9d636
commit 9c6305a0d2
14 changed files with 2689 additions and 35 deletions

View File

@ -3,6 +3,8 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="/favicon.ico" />
<!-- 必须引入,否则打印不正常或重叠 -->
<link rel="stylesheet" type="text/css" media="print" href="/print-lock.css">
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta <meta

View File

@ -73,6 +73,7 @@
"vue-cropper": "^1.1.4", "vue-cropper": "^1.1.4",
"vue-dompurify-html": "^4.1.4", "vue-dompurify-html": "^4.1.4",
"vue-i18n": "9.10.2", "vue-i18n": "9.10.2",
"vue-plugin-hiprint": "0.0.58-fix",
"vue-router": "4.4.5", "vue-router": "4.4.5",
"vue-types": "^5.1.1", "vue-types": "^5.1.1",
"vue3-signature": "^0.2.4", "vue3-signature": "^0.2.4",

View File

@ -27,6 +27,11 @@ export const getPatientItems = (medicalSn: String) => {
return request.get({ url: '/checkup/result/getPatientItems?medicalSn=' + medicalSn }) return request.get({ url: '/checkup/result/getPatientItems?medicalSn=' + medicalSn })
} }
// 根据条码获取患者检查项
export const groupNameItemsOfMedicalSn = (medicalSn: String) => {
return request.get({ url: '/checkup/result/groupNameItemsOfMedicalSn?medicalSn=' + medicalSn })
}
// 更新患者汇总结果分析 // 更新患者汇总结果分析
export const updatePatient = (data: SummaryResultVO) => { export const updatePatient = (data: SummaryResultVO) => {
return request.put({ url: '/checkup/result/updatePatient', data }) return request.put({ url: '/checkup/result/updatePatient', data })

View File

@ -6,8 +6,8 @@
<div class="filter-options"> <div class="filter-options">
<div class="header-title">人员列表</div> <div class="header-title">人员列表</div>
<el-radio-group v-model="auditStatus"> <el-radio-group v-model="auditStatus">
<el-radio value="0" size="large" >审核</el-radio> <el-radio size="large" :label="2">审核</el-radio>
<el-radio value="2" size="large" >审核</el-radio> <el-radio size="large" :label="0">审核</el-radio>
</el-radio-group> </el-radio-group>
</div> </div>
<div class="search-box"> <div class="search-box">
@ -53,19 +53,19 @@
<div class="report-content" > <div class="report-content" >
<ContentWrap > <ContentWrap >
<el-form class="-mb-15px" :inline="true"> <el-form class="-mb-15px" :inline="true">
<el-form-item label="条码" prop="medicalSn"> <!-- <el-form-item label="条码" prop="medicalSn">
<el-input v-model="medicalSn" placeholder="请输入条码" clearable class="!w-240px" @blur="handleBlur"/> <el-input v-model="medicalSn" placeholder="请输入条码" clearable class="!w-240px" @blur="handleBlur"/>
</el-form-item> </el-form-item>-->
<el-form-item> <el-form-item>
<el-button @click="pass" type="primary">通过</el-button> <el-button @click="pass" type="primary" :disabled="!(formData2.auditStatus!=0)">通过</el-button>
<el-button @click="overrule" type="primary">驳回</el-button> <el-button @click="overrule" type="primary" :disabled="!(formData2.auditStatus!=0)">驳回</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
</ContentWrap> </ContentWrap>
<ContentWrap > <ContentWrap >
<el-form class="-mb-15px" :inline="true"> <el-form class="-mb-15px" :inline="true">
<el-form-item label="总检意见"> <el-form-item label="总检意见">
<el-input v-model="inspectionOpinion" placeholder="请输入内容" type="textarea" style="width: 400px"/> <el-input v-model="inspectionOpinion" placeholder="请输入内容" type="textarea" style="width: 400px" :disabled="!(formData2.auditStatus!=0)"/>
</el-form-item> </el-form-item>
</el-form> </el-form>
</ContentWrap> </ContentWrap>
@ -73,8 +73,8 @@
<ContentWrap > <ContentWrap >
<el-table :data="list" @row-click="rowChange" :show-overflow-tooltip="true" :scroll-x="true"> <el-table :data="list" @row-click="rowChange" :show-overflow-tooltip="true" :scroll-x="true">
<el-table-column label="用户编号" align="center" key="id" prop="id" v-if="false"/> <el-table-column label="用户编号" align="center" key="id" prop="id" v-if="false"/>
<el-table-column label="检查项目" align="center" prop="itemName" min-width="150"/> <el-table-column label="模块名称" align="center" prop="groupname" min-width="150"/>
<el-table-column label="阳性" align="center" prop="itemResult" min-width="320" /> <el-table-column label="科室小结" align="center" prop="analyse" min-width="320" />
<el-table-column label="检查医生" align="center" prop="inspectdoctor" min-width="120" /> <el-table-column label="检查医生" align="center" prop="inspectdoctor" min-width="120" />
<el-table-column label="检查时间" align="center" prop="inspecttime" :formatter="dateFormatter2" min-width="120" /> <el-table-column label="检查时间" align="center" prop="inspecttime" :formatter="dateFormatter2" min-width="120" />
</el-table> </el-table>
@ -98,8 +98,8 @@
<ContentWrap class="h-1/1"> <ContentWrap class="h-1/1">
<ol :style="{ listStyleType: showNumbers ? 'decimal' : 'none' }" style="margin-left: 15px"> <ol :style="{ listStyleType: showNumbers ? 'decimal' : 'none' }" style="margin-left: 15px">
<li v-for="(item, index) in list3" :key="index" @click="handleClick(item)"> <li v-for="(item, index) in list3" :key="index" @click="handleClick(item)">
<span style="display: block"> {{ analyseFlag ? '【' : '' }} {{ item.itemResult }} {{ analyseFlag ? '】' : '' }}</span> <span style="display: block"> {{ analyseFlag ? '【' : '' }} {{ item.groupname }} {{ analyseFlag ? '】' : '' }}</span>
<span style="display: block" v-if="analyseFlag"> <el-input v-model="item.analyse" placeholder="请输入" type="textarea"/> </span> <span style="display: block" v-if="analyseFlag"> <el-input v-model="item.analyse" placeholder="请输入" type="textarea" :disabled="!(formData2.auditStatus!=0)"/> </span>
</li> </li>
</ol> </ol>
</ContentWrap> </ContentWrap>
@ -113,6 +113,8 @@ import {ElMessage} from "element-plus";
import * as SummaryApi from '@/api/summary' import * as SummaryApi from '@/api/summary'
import {dateFormatter, dateFormatter2} from "@/utils/formatTime"; import {dateFormatter, dateFormatter2} from "@/utils/formatTime";
import {PatientApi} from "@/api/inspect/inspectpatient"; import {PatientApi} from "@/api/inspect/inspectpatient";
import {ref} from "vue";
import {groupNameItemsOfMedicalSn} from "@/api/summary";
const { t } = useI18n() // const { t } = useI18n() //
@ -125,6 +127,18 @@ const list3 = ref([]) // 列表的数
const showNumbers = ref(false) const showNumbers = ref(false)
const analyseFlag = ref(false) const analyseFlag = ref(false)
const formData2 = ref({
id: undefined,
medicalSn: undefined,
pname: undefined,
gender: undefined,
birthday: undefined,
medicalDateTime: undefined,
auditStatus: undefined,
})
const checkAll = () => { const checkAll = () => {
showNumbers.value = false; // showNumbers.value = false; //
/*const descriptions = ref(list.value.map(item => item.description)); /*const descriptions = ref(list.value.map(item => item.description));
@ -169,19 +183,32 @@ const handleClick = (item) =>{
const medicalSn = ref() const medicalSn = ref()
const inspectionOpinion = ref() const inspectionOpinion = ref()
const passFlag = ref(false)
const overruleFlag = ref(false)
const pass = async () =>{ const pass = async () =>{
if(medicalSn.value == null || medicalSn.value == ''){ /*if(medicalSn.value == null || medicalSn.value == ''){
message.error(t('请输入条码!')) message.error(t('请输入条码!'))
return return
}*/
if (passFlag.value){
message.error(t('已发起审核流程!'))
return
} }
inspectionOpinion.value = '同意汇总分析意见' inspectionOpinion.value = '同意汇总分析意见'
await SummaryApi.updateAudit(medicalSn.value,0,inspectionOpinion.value) await SummaryApi.updateAudit(medicalSn.value,0,inspectionOpinion.value)
message.success(t('审核成功')) message.success(t('审核成功'))
passFlag.value = true
overruleFlag.value = true
await fetchPatientsByDate()
} }
const overrule = async () =>{ const overrule = async () =>{
if(medicalSn.value == null || medicalSn.value == ''){ /* if(medicalSn.value == null || medicalSn.value == ''){
message.error(t('请输入条码!')) message.error(t('请输入条码!'))
return return
}*/
if (overruleFlag.value){
message.error(t('已发起审核流程!'))
return
} }
if(inspectionOpinion.value == '' || inspectionOpinion.value ==null){ if(inspectionOpinion.value == '' || inspectionOpinion.value ==null){
ElMessage({ ElMessage({
@ -191,12 +218,15 @@ const overrule = async () =>{
}else { }else {
await SummaryApi.updateAudit(medicalSn.value,1,inspectionOpinion.value) await SummaryApi.updateAudit(medicalSn.value,1,inspectionOpinion.value)
message.success(t('驳回成功')) message.success(t('驳回成功'))
overruleFlag.value = true
passFlag.value = true
await fetchPatientsByDate()
} }
} }
const handleBlur = async () =>{ const handleBlur = async () =>{
const patientInfo = await SummaryApi.getPatient(medicalSn.value) const patientInfo = await SummaryApi.getPatient(medicalSn.value)
const patientItemsInfo = await SummaryApi.getPatientItems(medicalSn.value) const patientItemsInfo = await SummaryApi.groupNameItemsOfMedicalSn(medicalSn.value)
inspectionOpinion.value = patientInfo.inspectionOpinion inspectionOpinion.value = patientInfo.inspectionOpinion
list.value = patientItemsInfo list.value = patientItemsInfo
analyseFlag.value = true; analyseFlag.value = true;
@ -219,7 +249,8 @@ const handlePatientSelect = async (patient) => {
medicalSn.value = patient.medicalSn medicalSn.value = patient.medicalSn
selectedPatient.value = patient selectedPatient.value = patient
const patientInfo = await SummaryApi.getPatient(patient.medicalSn) const patientInfo = await SummaryApi.getPatient(patient.medicalSn)
const patientItemsInfo = await SummaryApi.getPatientItems(patient.medicalSn) const patientItemsInfo = await SummaryApi.groupNameItemsOfMedicalSn(patient.medicalSn)
formData2.value = patientInfo
inspectionOpinion.value = patientInfo.inspectionOpinion inspectionOpinion.value = patientInfo.inspectionOpinion
list.value = patientItemsInfo list.value = patientItemsInfo
analyseFlag.value = true; analyseFlag.value = true;
@ -228,7 +259,7 @@ const handlePatientSelect = async (patient) => {
const pageNo = ref(1) const pageNo = ref(1)
const pageSize = ref(20) const pageSize = ref(20)
const total = ref(0) const total = ref(0)
const auditStatus = ref(0) const auditStatus = ref(2)
// //
const handleCurrentChange = (val) => { const handleCurrentChange = (val) => {
pageNo.value = val pageNo.value = val
@ -239,7 +270,7 @@ onMounted(() => {
// //
patients.value = [] patients.value = []
selectedPatient.value = null selectedPatient.value = null
auditStatus.value = null auditStatus.value = 2
// //
fetchPatientsByDate() fetchPatientsByDate()
}) })

View File

@ -6,8 +6,8 @@
<div class="filter-options"> <div class="filter-options">
<div class="header-title">人员列表</div> <div class="header-title">人员列表</div>
<el-radio-group v-model="inspectionStatus"> <el-radio-group v-model="inspectionStatus">
<el-radio value="0" size="large" >总检</el-radio> <el-radio size="large" :label="1">总检</el-radio>
<el-radio value="1" size="large" >总检</el-radio> <el-radio size="large" :label="0">总检</el-radio>
</el-radio-group> </el-radio-group>
</div> </div>
<div class="search-box"> <div class="search-box">
@ -53,12 +53,14 @@
<div class="report-content" > <div class="report-content" >
<ContentWrap :istable="true"> <ContentWrap :istable="true">
<el-form class="-mb-15px" :inline="true"> <el-form class="-mb-15px" :inline="true">
<el-form-item label="条码" prop="medicalSn"> <!-- <el-form-item label="条码" prop="medicalSn">
<el-input v-model="medicalSn" placeholder="请输入条码" clearable class="!w-240px" @blur="handleBlur"/> <el-input v-model="medicalSn" placeholder="请输入条码" clearable class="!w-240px" @blur="handleBlur"/>
</el-form-item> </el-form-item>-->
<el-form-item> <el-form-item>
<el-button @click="save" type="primary">汇总保存</el-button> <el-button @click="save" type="primary" :disabled="!(formData2.auditStatus!=0)">汇总保存</el-button>
<el-button @click="submit" type="primary">提交</el-button> <el-button @click="submit" type="primary" :disabled="!(formData2.auditStatus!=0)">提交</el-button>
<!-- <el-button @click="go" type="primary">打印</el-button>
<el-button @click="goto" type="primary">打印导检单</el-button>-->
</el-form-item> </el-form-item>
</el-form> </el-form>
</ContentWrap> </ContentWrap>
@ -106,8 +108,8 @@
<ContentWrap class="h-1/1"> <ContentWrap class="h-1/1">
<el-table :data="list" @row-click="rowChange" :show-overflow-tooltip="true" :scroll-x="true"> <el-table :data="list" @row-click="rowChange" :show-overflow-tooltip="true" :scroll-x="true">
<el-table-column label="用户编号" align="center" key="id" prop="id" v-if="false"/> <el-table-column label="用户编号" align="center" key="id" prop="id" v-if="false"/>
<el-table-column label="检查项目" align="center" prop="itemName" min-width="150"/> <el-table-column label="模块名称" align="center" prop="groupname" min-width="150"/>
<el-table-column label="阳性" align="center" prop="itemResult" min-width="320" /> <el-table-column label="科室小结" align="center" prop="analyse" min-width="320" />
</el-table> </el-table>
</ContentWrap> </ContentWrap>
</el-col> </el-col>
@ -130,8 +132,10 @@
<ContentWrap class="h-1/1"> <ContentWrap class="h-1/1">
<ol :style="{ listStyleType: showNumbers ? 'decimal' : 'none' }" style="margin-left: 15px"> <ol :style="{ listStyleType: showNumbers ? 'decimal' : 'none' }" style="margin-left: 15px">
<li v-for="(item, index) in list3" :key="index" @click="handleClick(item)"> <li v-for="(item, index) in list3" :key="index" @click="handleClick(item)">
<span style="display: block"> {{ analyseFlag ? '【' : '' }} {{ item.itemResult }} {{ analyseFlag ? '】' : '' }}</span> <span style="display: block"> {{ analyseFlag ? '【' : '' }} {{ item.groupname }} {{ analyseFlag ? '】' : '' }}</span>
<span style="display: block" v-if="analyseFlag"> <el-input v-model="item.analyse" placeholder="请输入" type="textarea"/> </span> <span style="display: block" v-if="analyseFlag">
<el-input v-model="item.analyse" placeholder="请输入" type="textarea" :disabled="!editFlag"/>
</span>
</li> </li>
</ol> </ol>
</ContentWrap> </ContentWrap>
@ -148,6 +152,50 @@ import {DICT_TYPE, getIntDictOptions} from "@/utils/dict";
import {onMounted, ref} from "vue"; import {onMounted, ref} from "vue";
import {PatientApi} from "@/api/inspect/inspectpatient"; import {PatientApi} from "@/api/inspect/inspectpatient";
import { useRouter } from 'vue-router'
import {newHiprintPrintTemplate} from "@/views/summary/utils/template-helper";
import printData from "@/views/summary/print/printData";
import {usePaper} from "@/views/summary/hooks/use-paper";
import {useZoom} from "@/views/summary/hooks/use-zoom";
import template from "../print/template";
import {groupNameItemsOfMedicalSn} from "@/api/summary";
const router = useRouter()
const go = () => {
router.push({
name: 'PrintInfo',
params: {
medicalSn: medicalSn.value // :id
}
})
}
const templateRef = ref(template);
let hiprintTemplate;
const TEMPLATE_KEY = getCurrentInstance().type.name; // key
const { paperTypes, curPaperType, paperPopVisible, paperWidth, paperHeight, showPaperPop, setPaper, setPaperOther } = usePaper(TEMPLATE_KEY);
const { scaleValue, changeScale } = useZoom(TEMPLATE_KEY);
const goto = () =>{
hiprintTemplate = newHiprintPrintTemplate(TEMPLATE_KEY, {
template: templateRef.value, // json(object)
settingContainer: "#PrintElementOptionSetting", //
});
// :
let options = { leftOffset: -1, topOffset: -1 };
//
let ext = {
callback: () => {
console.log("浏览器打印窗口已打开");
},
styleHandler: () => {
//
return "<style>.hiprint-printElement-text{color:red !important;}</style>";
},
};
//
hiprintTemplate.print(printData, options, ext);
}
const { t } = useI18n() // const { t } = useI18n() //
const message = useMessage() // const message = useMessage() //
@ -156,6 +204,7 @@ const list3 = ref([]) // 列表的数
const showNumbers = ref(false) const showNumbers = ref(false)
const analyseFlag = ref(false) const analyseFlag = ref(false)
const editFlag = ref(true)
const checkAll = () => { const checkAll = () => {
showNumbers.value = false; // showNumbers.value = false; //
@ -209,7 +258,7 @@ const formData2 = ref({
const handleBlur = async () =>{ const handleBlur = async () =>{
const patientInfo = await SummaryApi.getPatient(medicalSn.value) const patientInfo = await SummaryApi.getPatient(medicalSn.value)
const patientItemsInfo = await SummaryApi.getPatientItems(medicalSn.value) const patientItemsInfo = await SummaryApi.groupNameItemsOfMedicalSn(medicalSn.value)
formData2.value = patientInfo formData2.value = patientInfo
//list.value = patientItemsInfo //list.value = patientItemsInfo
const isCheck = await SummaryApi.isExistUncheck(medicalSn.value); const isCheck = await SummaryApi.isExistUncheck(medicalSn.value);
@ -222,6 +271,7 @@ const handleBlur = async () =>{
}) })
}else { }else {
list.value = patientItemsInfo list.value = patientItemsInfo
} }
} }
@ -230,7 +280,10 @@ const save = async () =>{
message.error(t('请选择患者或者输入条码!')) message.error(t('请选择患者或者输入条码!'))
return return
} }
if (formData2.value.auditStatus == 2){
message.error(t('已提交,不能进行修改汇总!'))
return
}
const isCheck = await SummaryApi.isExistUncheck(medicalSn.value); const isCheck = await SummaryApi.isExistUncheck(medicalSn.value);
if (isCheck){ if (isCheck){
await ElMessageBox({ await ElMessageBox({
@ -245,21 +298,28 @@ const save = async () =>{
}else { }else {
await SummaryApi.updateItemsAnalyse(list3.value) await SummaryApi.updateItemsAnalyse(list3.value)
message.success(t('保存成功')) message.success(t('保存成功'))
await fetchPatientsByDate()
} }
} }
} }
const submitFlag = ref(false)
const submit = async () =>{ const submit = async () =>{
if (submitFlag.value){
message.error(t('已提交,无须再次提交!'))
return
}
if(formData2.value.medicalSn == null || formData2.value.medicalSn == ''){ if(formData2.value.medicalSn == null || formData2.value.medicalSn == ''){
message.error(t('请选择患者或者输入条码!')) message.error(t('请选择患者'))
return return
} }
if(formData2.value.auditStatus == 0){ if(formData2.value.auditStatus == 0){
message.error(t('已审核通过,无须再次提交!')) message.error(t('已审核通过,无须再次提交!'))
}else if (formData2.value.auditStatus == 2){
message.error(t('已提交,无须再次提交!'))
}else { }else {
await SummaryApi.updateAudit(medicalSn.value,2,'') await SummaryApi.updateAudit(medicalSn.value,2,'')
message.success(t('提交成功')) message.success(t('提交成功'))
submitFlag.value = true
} }
} }
@ -281,9 +341,14 @@ const handlePatientSelect = async (patient) => {
medicalSn.value = patient.medicalSn medicalSn.value = patient.medicalSn
selectedPatient.value = patient selectedPatient.value = patient
const patientInfo = await SummaryApi.getPatient(patient.medicalSn) const patientInfo = await SummaryApi.getPatient(patient.medicalSn)
const patientItemsInfo = await SummaryApi.getPatientItems(patient.medicalSn) const patientItemsInfo = await SummaryApi.groupNameItemsOfMedicalSn(patient.medicalSn)
formData2.value = patientInfo formData2.value = patientInfo
//list.value = patientItemsInfo //list.value = patientItemsInfo
if(patientInfo.auditStatus == 0 || patientInfo.auditStatus == 2){
editFlag.value = false
}else {
editFlag.value = true
}
const isCheck = await SummaryApi.isExistUncheck(patient.medicalSn); const isCheck = await SummaryApi.isExistUncheck(patient.medicalSn);
if (isCheck){ if (isCheck){
await ElMessageBox({ await ElMessageBox({
@ -295,12 +360,17 @@ const handlePatientSelect = async (patient) => {
}else { }else {
list.value = patientItemsInfo list.value = patientItemsInfo
} }
if(patientInfo.auditStatus == 0){
analyseFlag.value = true;
showNumbers.value = true; //
list3.value = patientItemsInfo
}
} }
const pageNo = ref(1) const pageNo = ref(1)
const pageSize = ref(20) const pageSize = ref(20)
const total = ref(0) const total = ref(0)
const inspectionStatus = ref() const inspectionStatus = ref(1)
// //
const handleCurrentChange = (val) => { const handleCurrentChange = (val) => {
pageNo.value = val pageNo.value = val
@ -311,7 +381,7 @@ onMounted(() => {
// //
patients.value = [] patients.value = []
selectedPatient.value = null selectedPatient.value = null
inspectionStatus.value = null inspectionStatus.value = 1
// //
fetchPatientsByDate() fetchPatientsByDate()

View File

@ -0,0 +1,108 @@
/*
* @Description:
* @Author: CcSimple
* @Github: https://github.com/CcSimple
* @Date: 2023-02-09 13:32:39
* @LastEditors: CcSimple
* @LastEditTime: 2023-02-09 23:38:03
*/
import { reactive, computed, toRefs } from "vue";
import { getHiprintPrintTemplate } from "../utils/template-helper";
/**
* vue3 组合式函数
* 把一些逻辑抽离出来方便复用
* 返回 使用方 可用的方法和数据
*/
export function usePaper(key) {
// 数据
const state = reactive({
curPaper: {
type: "A4",
width: 210,
height: 296.6,
},
paperTypes: {
A3: {
width: 420,
height: 296.6,
},
A4: {
width: 210,
height: 296.6,
},
A5: {
width: 210,
height: 147.6,
},
B3: {
width: 500,
height: 352.6,
},
B4: {
width: 250,
height: 352.6,
},
B5: {
width: 250,
height: 175.6,
},
},
// 自定义纸张
paperPopVisible: false,
paperWidth: "220",
paperHeight: "80",
});
// 计算属性
const curPaperType = computed(() => {
let type = "other";
let types = state.paperTypes;
for (const key in types) {
let item = types[key];
let { width, height } = state.curPaper;
if (item.width === width && item.height === height) {
type = key;
}
}
return type;
});
const tp = () => {
return getHiprintPrintTemplate(key);
};
// 方法
const showPaperPop = () => {
state.paperPopVisible = true;
};
const hidePaperPop = () => {
state.paperPopVisible = false;
};
const setPaper = (type, value) => {
try {
if (Object.keys(state.paperTypes).includes(type)) {
state.curPaper = { type: type, width: value.width, height: value.height };
tp().setPaper(value.width, value.height);
} else {
state.curPaper = { type: "other", width: value.width, height: value.height };
tp().setPaper(value.width, value.height);
}
} catch (error) {
alert(`操作失败: ${error}`);
}
};
const setPaperOther = () => {
let value = {};
value.width = state.paperWidth;
value.height = state.paperHeight;
state.paperPopVisible = false;
setPaper("other", value);
};
// 暴露给使用方
return {
...toRefs(state),
curPaperType,
showPaperPop,
hidePaperPop,
setPaper,
setPaperOther,
};
}

View File

@ -0,0 +1,49 @@
/*
* @Description:
* @Author: CcSimple
* @Github: https://github.com/CcSimple
* @Date: 2023-02-09 13:32:39
* @LastEditors: CcSimple
* @LastEditTime: 2023-02-10 16:00:54
*/
import { reactive, toRefs } from "vue";
import { getHiprintPrintTemplate } from "../utils/template-helper";
/**
* vue3 组合式函数
* 把一些逻辑抽离出来方便复用
* 返回 使用方 可用的方法和数据
*/
export function useZoom(key) {
// 数据
const state = reactive({
scaleValue: 1,
scaleMax: 5,
scaleMin: 0.5,
});
// 获取 template
const tp = () => {
return getHiprintPrintTemplate(key);
};
// 方法
const changeScale = (big) => {
let scaleValue = state.scaleValue;
if (big) {
scaleValue += 0.1;
if (scaleValue > state.scaleMax) scaleValue = 5;
} else {
scaleValue -= 0.1;
if (scaleValue < state.scaleMin) scaleValue = 0.5;
}
if (tp()) {
// scaleValue: 放大缩小值, false: 不保存(不传也一样), 如果传 true, 打印时也会放大
tp().zoom(scaleValue);
state.scaleValue = scaleValue;
}
};
// 暴露给使用方
return {
...toRefs(state),
changeScale,
};
}

View File

@ -0,0 +1,398 @@
<template>
<div class="flex-col">
<div class="flex-row justify-center" style="margin-bottom: 10px">
<!-- 纸张大小 A3A4 -->
<div class="paper">
<template v-for="(value, type) in paperTypes" :key="type">
<button :class="curPaperType === type ? 'api' : 'info'" @click="setPaper(type, value)">
{{ type }}
</button>
</template>
<!-- 自定义纸张 -->
<button :class="'other' == curPaperType ? 'api' : 'info'" class="auto" @click="showPaperPop">自定义纸张</button>
<div class="popover">
<div class="popover-content flex-col" v-show="paperPopVisible">
<div style="font-size: 16px; font-weight: bold">设置纸张宽高(mm)</div>
<div class="flex-row mt-10">
<input class="input" :value="paperWidth" type="number" placeholder="宽(mm)" />
<span class="ml-10 mr-10">x</span>
<input class="input" :value="paperHeight" type="number" placeholder="高(mm)" />
</div>
<button class="primary circle-10" style="margin-top: 6px" @click.stop="setPaperOther">确定</button>
</div>
</div>
</div>
<!-- 缩放 -->
<div class="flex-row align-center ml-10">
<button class="info circle-10" @click="changeScale(false)"><i class="iconfont sv-zoom-out"></i></button>
<div style="margin: 0 4px; width: 40px">{{ (scaleValue * 100).toFixed(0) }}%</div>
<button class="info circle-10" @click="changeScale(true)"><i class="iconfont sv-zoom-in" ></i></button>
</div>
<button class="api circle-10 ml-10" @click.stop="rotatePaper">
<i class="iconfont sv-rotate" ></i>
旋转纸张(宽高互换)
</button>
<button class="api circle-10 ml-10" @click.stop="clearPaper">
<i class="iconfont sv-clear" ></i>
清空纸张
</button>
<button class="api circle-10 ml-10" @click.stop="exportJson">
<i class="iconfont sv-export" ></i>
导出模板 json
</button>
<button class="api circle-10 ml-10" @click.stop="exportJsonTid">
导出模板 json tid
<i class="iconfont sv-export" ></i>
</button>
<button class="secondary circle-10 ml-10" @click.stop="print">
<i class="iconfont sv-printer" ></i>
浏览器打印
</button>
<button class="warning circle-10 ml-10" @click.stop="print2">
直接打印(需要连接客户端)
<i class="iconfont sv-printer" ></i>
</button>
</div>
<div class="flex-row" style="height: 87vh">
<div class="flex-2 left flex-col">
<div class="title">provider1 默认样式</div>
<!-- provider1 的容器; 加上 class "rect-printElement-types" 使用默认样式 -->
<!-- 当然可以 重写 或者 自定义样式 -->
<div id="provider-container1" class="container rect-printElement-types"></div>
<div class="title">provider2 自定义样式</div>
<!-- provider2 的容器; -->
<!-- 这里自定义显示样式 custom-style-types -->
<div id="provider-container2" class="container custom-style-types"></div>
<div class=""></div>
</div>
<div class="flex-5 center">
<!-- 设计器的 容器 -->
<div id="hiprint-printTemplate"></div>
</div>
<div class="flex-2 right">
<!-- 元素参数的 容器 -->
<div id="PrintElementOptionSetting"></div>
</div>
</div>
</div>
</template>
<script setup>
import { onMounted, ref, getCurrentInstance } from "vue";
import { hiprint } from "vue-plugin-hiprint";
import { provider1 } from "./provider1";
import { provider2 } from "./provider2";
import template from "./template";
import printData from "./printData";
// hooks
import { usePaper } from "../hooks/use-paper";
import { useZoom } from "../hooks/use-zoom";
//
import { newHiprintPrintTemplate } from "../utils/template-helper";
import {useRoute} from "vue-router";
const TEMPLATE_KEY = getCurrentInstance().type.name; // key
const { paperTypes, curPaperType, paperPopVisible, paperWidth, paperHeight, showPaperPop, setPaper, setPaperOther } = usePaper(TEMPLATE_KEY);
const { scaleValue, changeScale } = useZoom(TEMPLATE_KEY);
defineOptions({ name: 'PrintInfo' })
// provider
let options = {
config: {
tid: "providerModule1.config",
title: "参数provider示例",
type: "text",
options: {
testData: "单据表头",
height: 30,
fontSize: 16,
},
},
};
// provider
hiprint.init({
providers: [provider1(options), provider2(options)],
});
/**
* 这里必须要在 onMounted 中去构建 左侧可拖拽元素 或者 设计器
* 因为都是把元素挂载到对应容器中, 必须要先找到该容器
*/
onMounted(() => {
buildLeftElement();
buildDesigner();
});
/**
* 构建左侧可拖拽元素
* 注意: 可拖拽元素必须在 hiprint.init() 之后调用
* 调用之前 可以先 console.log($("#hiprint-printTemplate")) 看看是否有该 dom
*/
const buildLeftElement = () => {
// ----- providerModule1 -----
// eslint-disable-next-line no-undef
$("#provider-container1").empty(); // ,
// eslint-disable-next-line no-undef
hiprint.PrintElementTypeManager.build($("#provider-container1"), "providerModule1");
// ----- providerModule2 -----
// eslint-disable-next-line no-undef
$("#provider-container2").empty(); // ,
// eslint-disable-next-line no-undef
hiprint.PrintElementTypeManager.build($("#provider-container2"), "providerModule2");
};
/**
* 构建设计器
* 注意: 必须要在 onMounted 中去构建
* 因为都是把元素挂载到对应容器中, 必须要先找到该容器
*/
let hiprintTemplate;
// ref json
const templateRef = ref(template);
console.log("templateRef 数据格式:");
console.log("templateRef", templateRef);
console.log("templateRef.value", templateRef.value);
const buildDesigner = () => {
// eslint-disable-next-line no-undef
$("#hiprint-printTemplate").empty(); // ,
// : json(object)
// 使 vue refjson, 使 .value (使 object.key )
hiprintTemplate = newHiprintPrintTemplate(TEMPLATE_KEY, {
template: templateRef.value, // json(object)
settingContainer: "#PrintElementOptionSetting", //
});
//
hiprintTemplate.design("#hiprint-printTemplate");
console.log(hiprintTemplate);
};
/**
* 浏览器打印
*/
const print = () => {
// :
let options = { leftOffset: -1, topOffset: -1 };
//
let ext = {
callback: () => {
console.log("浏览器打印窗口已打开");
},
styleHandler: () => {
//
return "<style>.hiprint-printElement-text{color:red !important;}</style>";
},
};
//
hiprintTemplate.print(printData, options, ext);
};
/**
* 直接打印: 借助客户端,静默打印(无弹窗直接打印)
* 注意: 需要先连接客户端
*/
const print2 = () => {
if (hiprint.hiwebSocket.opened) {
hiprintTemplate.print2(printData);
} else {
alert("请先连接客户端(刷新网页), 然后再点击「直接打印」");
}
};
// ----------------- api -----------------
/**
* 旋转纸张
*/
const rotatePaper = () => {
hiprintTemplate.rotatePaper();
};
/**
* 清空所有元素
*/
const clearPaper = () => {
hiprintTemplate.clear();
};
/**
* 导出模板 json
* 必须确保 hiprintTemplate 已成功创建
*/
const exportJson = () => {
let json = hiprintTemplate.getJson();
console.log(json);
alert("导出成功! 请查看控制台输出");
};
/**
* 导出模板 json tid
* 仅导出 options, 不导出 printElementType
* 必须确保 hiprintTemplate 已成功创建
*/
const exportJsonTid = () => {
let json = hiprintTemplate.getJsonTid();
console.log(json);
alert("导出成功! 请查看控制台输出");
};
const route = useRoute();
/*queryParams = route.query;*/
//console.log('route.query',route.query)
console.log('123:',route.query.districtCode)
</script>
<style>
/* 重写默认的一个样式 */
.rect-printElement-types .hiprint-printElement-type > li > ul > li > a {
color: red !important;
}
/* 自定义 provider 构建样式 */
.custom-style-types .hiprint-printElement-type {
display: block;
}
.custom-style-types .hiprint-printElement-type {
padding: 0 0 0 0;
list-style: none;
}
.custom-style-types .hiprint-printElement-type > li > .title {
display: block;
padding: 4px 0px;
color: rgb(0, 58, 230);
clear: both;
}
.custom-style-types .hiprint-printElement-type > li > ul {
padding: 0 0 0 0;
display: block;
list-style: none;
}
.custom-style-types .hiprint-printElement-type > li > ul > li {
display: block;
width: 50%;
float: left;
max-width: 100px;
}
.custom-style-types .hiprint-printElement-type > li > ul > li > a {
padding: 12px 6px;
color: #1296db;
text-decoration: none;
background: #fff;
border: 1px solid #ddd;
margin-right: 5px;
width: 95%;
max-width: 100px;
display: inline-block;
text-align: center;
margin-bottom: 7px;
box-sizing: border-box;
border: 1px solid rgba(0, 0, 0, 0.2);
border-radius: 4px;
box-shadow: 0 1px 0 0 rgba(0, 0, 0, 0.15);
}
</style>
<style scoped>
/* api按钮 */
.api {
background: #00acc1;
}
.auto {
width: auto !important;
}
/* 纸张 */
.paper {
margin-right: 10px;
}
.paper button:not(class*="auto") {
width: 60px !important;
}
/* 多个 button 间距 */
.paper button + button {
margin-left: -1px;
}
.paper button:first-child:last-child {
border-radius: 4px;
}
/* 两边的 btn 圆角 */
.paper button:first-child:not(:last-child) {
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
}
.paper button:last-child:not(:first-child) {
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
}
.popover {
position: absolute;
margin-top: 10px;
z-index: 10;
}
.popover .popover-content {
background: white;
border-radius: 4px;
padding: 10px 14px;
box-shadow: 2px 2px 2px 4px rgb(128 0 128 / 20%);
}
.popover .input {
height: 24px;
padding: 2px 4px;
}
.popover .input:hover {
border-color: rgb(245, 155, 241);
border-radius: 4px;
}
/* 区域 */
.left {
background: white;
border-radius: 4px;
border: 1px solid #d9d9d9;
box-shadow: 2px 2px 2px 0px rgb(128 0 128 / 20%);
overflow: auto;
}
.left .container {
height: 50%;
overflow: auto;
padding: 0 10%;
background: rgb(245, 155, 241);
}
.left .container[id*="provider-container2"] {
margin-bottom: 10px;
background: rgb(209, 120, 239);
}
.center {
margin: 0 10px;
background: white;
border-radius: 4px;
border: 1px solid #d9d9d9;
padding: 20px;
box-shadow: 2px 2px 2px 0px rgb(128 0 128 / 20%);
overflow: auto;
}
.right {
background: white;
border-radius: 4px;
border: 1px solid #d9d9d9;
padding: 10px 0;
box-shadow: 2px 2px 2px 0px rgb(128 0 128 / 20%);
overflow: auto;
}
/* 左侧拖拽元素样式 */
.title {
font-size: 16px;
font-weight: 500;
margin: 4px 0 4px 10px;
}
.item {
display: flex;
flex-direction: column;
align-items: center;
background: white;
padding: 4px 10px;
margin: 10px 8px 4px 8px;
width: 38%;
min-height: 60px;
border-radius: 4px;
box-shadow: 2px 2px 2px 2px rgba(171, 171, 171, 0.2);
}
.item .iconfont {
font-size: 1.5rem;
}
.item span {
font-size: 14px;
}
</style>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,158 @@
/*
* @Description:
* @Author: CcSimple
* @Github: https://github.com/CcSimple
* @Date: 2023-02-09 10:40:26
* @LastEditors: CcSimple
* @LastEditTime: 2023-02-09 11:37:21
*/
import { hiprint } from "vue-plugin-hiprint";
export const provider1 = function (options) {
console.log(options);
var addElementTypes = function (context) {
context.removePrintElementTypes("providerModule1");
context.addPrintElementTypes("providerModule1", [
new hiprint.PrintElementTypeGroup("常规", [
options.config,
{
tid: "providerModule1.header",
title: "单据表头",
data: "单据表头",
type: "text",
options: {
testData: "单据表头",
height: 17,
fontSize: 16.5,
fontWeight: "700",
textAlign: "center",
hideTitle: true,
},
},
{
tid: "providerModule1.type",
title: "单据类型",
data: "单据类型",
type: "text",
options: {
testData: "单据类型",
height: 16,
fontSize: 15,
fontWeight: "700",
textAlign: "center",
hideTitle: true,
},
},
{
tid: "providerModule1.order",
title: "订单编号",
data: "XS888888888",
type: "text",
options: {
field: "order",
testData: "XS888888888",
height: 16,
fontSize: 6.75,
fontWeight: "700",
textAlign: "left",
textContentVerticalAlign: "middle",
},
},
{
tid: "providerModule1.date",
title: "业务日期",
data: "2020-01-01",
type: "text",
options: {
field: "date",
testData: "2020-01-01",
height: 16,
fontSize: 6.75,
fontWeight: "700",
textAlign: "left",
textContentVerticalAlign: "middle",
},
},
{
tid: "providerModule1.barcode",
title: "条形码",
data: "XS888888888",
type: "text",
options: {
field: "barcode",
testData: "XS888888888",
height: 32,
fontSize: 12,
lineHeight: 18,
textAlign: "left",
textType: "barcode",
},
},
{
tid: "providerModule1.qrcode",
title: "二维码",
data: "XS888888888",
type: "text",
options: {
field: "qrcode",
testData: "XS888888888",
height: 32,
fontSize: 12,
lineHeight: 18,
textType: "qrcode",
},
},
{
tid: "providerModule1.platform",
title: "平台名称",
data: "平台名称",
type: "text",
options: {
field: "platform",
testData: "平台名称",
height: 17,
fontSize: 16.5,
fontWeight: "700",
textAlign: "center",
hideTitle: true,
},
},
{ tid: "providerModule1.image", title: "Logo", data: "", type: "image" },
]),
new hiprint.PrintElementTypeGroup("客户", [
{
tid: "providerModule1.khname",
title: "客户名称",
data: "高级客户",
type: "text",
options: {
field: "name",
testData: "高级客户",
height: 16,
fontSize: 6.75,
fontWeight: "700",
textAlign: "left",
textContentVerticalAlign: "middle",
},
},
{
tid: "providerModule1.tel",
title: "客户电话",
data: "18888888888",
type: "text",
options: {
field: "tel",
testData: "18888888888",
height: 16,
fontSize: 6.75,
fontWeight: "700",
textAlign: "left",
textContentVerticalAlign: "middle",
},
},
]),
]);
};
return {
addElementTypes: addElementTypes,
};
};

View File

@ -0,0 +1,90 @@
/*
* @Description:
* @Author: CcSimple
* @Github: https://github.com/CcSimple
* @Date: 2023-02-09 10:40:26
* @LastEditors: CcSimple
* @LastEditTime: 2023-02-09 10:50:02
*/
import { hiprint } from "vue-plugin-hiprint";
export const provider2 = function (options) {
console.log(options);
var addElementTypes = function (context) {
context.removePrintElementTypes("providerModule2");
context.addPrintElementTypes("providerModule2", [
new hiprint.PrintElementTypeGroup("表格/其他", [
{
tid: "providerModule2.table",
title: "订单数据",
type: "table",
options: {
field: "table",
fields: [
{ text: "名称", field: "NAME" },
{ text: "数量", field: "SL" },
{ text: "规格", field: "GG" },
{ text: "条码", field: "TM" },
{ text: "单价", field: "DJ" },
{ text: "金额", field: "JE" },
{ text: "备注", field: "DETAIL" },
],
},
columns: [
[
{ title: "名称", align: "center", field: "NAME", width: 100 },
{ title: "数量", align: "center", field: "SL", width: 100 },
{ title: "条码", align: "center", field: "TM", width: 100 },
{ title: "规格", align: "center", field: "GG", width: 100 },
{ title: "单价", align: "center", field: "DJ", width: 100 },
{ title: "金额", align: "center", field: "JE", width: 100 },
{ title: "备注", align: "center", field: "DETAIL", width: 100 },
],
],
footerFormatter: function (options, rows, data, currentPageGridRowsData) {
console.log(currentPageGridRowsData);
if (data && data["totalCap"]) {
return `<td style="padding:0 10px" colspan="100">${"应收金额大写: " + data["totalCap"]}</td>`;
}
return '<td style="padding:0 10px" colspan="100">应收金额大写: </td>';
},
},
{ tid: "providerModule2.customText", title: "文本", customText: "自定义文本", custom: true, type: "text" },
{
tid: "providerModule2.longText",
title: "长文本",
type: "longText",
options: {
field: "test.longText",
width: 200,
testData: "长文本分页/不分页测试",
},
},
]),
new hiprint.PrintElementTypeGroup("辅助", [
{
tid: "providerModule2.hline",
title: "横线",
type: "hline",
},
{
tid: "providerModule2.vline",
title: "竖线",
type: "vline",
},
{
tid: "providerModule2.rect",
title: "矩形",
type: "rect",
},
{
tid: "providerModule2.oval",
title: "椭圆",
type: "oval",
},
]),
]);
};
return {
addElementTypes: addElementTypes,
};
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,89 @@
import { createApp } from "vue";
/*
* @Description: 封装的一个 Modal, 使用 Api 形式调用
* @Author: CcSimple
* @Github: https://github.com/CcSimple
* @Date: 2023-02-09 16:12:54
* @LastEditors: CcSimple
* @LastEditTime: 2023-02-09 23:25:36
*/
const Modal = {
props: {
title: {
type: String,
required: true,
},
bodyStyle: {
type: String,
default: "",
},
headerStyle: {
type: String,
default: "",
},
footerStyle: {
type: String,
default: "padding:10px",
},
},
render(ctx) {
const { $props, $emit } = ctx;
const cancel = (e) => {
e.stopPropagation();
$emit("cancel");
};
const confirm = (e) => {
e.stopPropagation();
$emit("confirm");
};
const stopPropagation = (e) => {
e.stopPropagation();
};
return (
<div class="modal">
{/* <div class="mask" onClick={cancel} /> */}
<div class="wrap" onClick={cancel}>
<div class="box" onClick={stopPropagation} style={$props.bodyStyle}>
<div class="modal-box__header" style={$props.headerStyle}>
{$props.title}
</div>
<div class="modal-box__footer" style={$props.footerStyle}>
<button class="info" onClick={cancel}>
取消
</button>
<button class="primary" onClick={confirm}>
确定
</button>
</div>
</div>
</div>
</div>
);
},
};
export function showModal(options) {
const div = document.createElement("div");
document.body.appendChild(div);
const { title, bodyStyle, headerStyle, footerStyle } = options;
const { onCancel, onConfirm } = options;
const hide = (modal) => {
modal.unmount();
div.remove();
};
const modal = createApp(Modal, {
title: title,
bodyStyle: bodyStyle,
headerStyle: headerStyle,
footerStyle: footerStyle,
onCancel() {
hide(modal);
onCancel && onCancel();
},
onConfirm() {
onConfirm && onConfirm(() => hide(modal));
},
});
modal.mount(div);
}

View File

@ -0,0 +1,21 @@
/*
* @Description:
* @Author: CcSimple
* @Github: https://github.com/CcSimple
* @Date: 2023-02-09 23:26:18
* @LastEditors: CcSimple
* @LastEditTime: 2023-02-09 23:35:20
*/
import { hiprint } from "vue-plugin-hiprint";
const templateMap = {};
export function newHiprintPrintTemplate(key, options) {
let template = new hiprint.PrintTemplate(options);
templateMap[key] = template;
return template;
}
export function getHiprintPrintTemplate(key) {
return templateMap[key];
}