版本更新
This commit is contained in:
@@ -0,0 +1,359 @@
|
||||
<template>
|
||||
<div
|
||||
class="pf_card"
|
||||
:style="{
|
||||
outlineWidth:active?'2px':'0',
|
||||
outlineColor:active?getBedBackColor(data.triageLevel):'#9aa6b5',
|
||||
backgroundColor:data.bedOperationalStatus==='U'?'#EDFFFD':'',
|
||||
borderColor: getBedBackColor(data.triageLevel)
|
||||
}"
|
||||
@click="clickAct"
|
||||
>
|
||||
<div v-if="data.bedOperationalStatus==='U'">
|
||||
<img :src="emptyBed" class="pf_card_emptyBed_img">
|
||||
<div class="pf_card_emptyBed_text">{{ data.bedName }}</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div v-if="data.isDischarge" class="pf_card_discharge">
|
||||
<span style="margin-left: 6px">离</span>
|
||||
</div>
|
||||
<div class="pf_card_card">
|
||||
<CardSign :color="getBedBackColor(data.triageLevel)" :title="data.bedName" :tail="getDisplay(data.triageLevel)" />
|
||||
</div>
|
||||
<div class="pf_card_nameSexAndAge">
|
||||
<span class="pf_card_name">{{ data.patientName }}</span>
|
||||
<span class="pf_card_sexAndAge">{{ data.gender.display }}/{{ data.age }}</span>
|
||||
</div>
|
||||
<div class="pf_card_rescueTime">
|
||||
<span style="margin-right: 16px">入室时间</span>
|
||||
{{ moment(data.checkInWardTime).format('YYYY-MM-DD HH:mm') }}
|
||||
</div>
|
||||
<div class="pf_card_noCode">{{ data.hisId }}</div>
|
||||
<div class="pf_card_rescueTimeText">{{ rescueTimeText() }}</div>
|
||||
<div v-if="data.diag!==''" class="pf_card_diagnosis">
|
||||
<div class="card-rectangle-text">{{ data.diag }}</div>
|
||||
<span style="margin-left: 4px">(诊断)</span>
|
||||
</div>
|
||||
<div v-if="isNewSign()" class="card-rectangle">新</div>
|
||||
<div v-if="is72HourSign()" class="card-rectangle2">超72H</div>
|
||||
<hr class="pf_card_line">
|
||||
<div class="pf_card_nursingMeasuresString">{{ getStringByCode(data.nursingMeasures, nursingMeasures) }}</div>
|
||||
<div class="pf_card_specialArrangementString">{{ getStringByCode(data.specialArrangement, specialArrangementList) }}</div>
|
||||
<div v-if="false" class="pf_card_btn" @click="moreClick">更多</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
// Ⅰ-1、Ⅱ-2、Ⅲ-3、Ⅳ-4、Ⅴ-5、Ⅵ-6、Ⅶ-7、Ⅷ-8、Ⅸ-9
|
||||
import emptyBed from '@/assets/png/emptyBed.png'
|
||||
|
||||
export default {
|
||||
name: 'PfPatientCard',
|
||||
inject: ['PfPatientCards'],
|
||||
props: {
|
||||
data: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
bedConfig: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
colors: [],
|
||||
emptyBed,
|
||||
bedStations: [],
|
||||
nursingMeasures: [],
|
||||
specialArrangementList: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
active() {
|
||||
return this.PfPatientCards.activePatient.bedId === this.data.bedId
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.colors = this.config.echartsColor.color
|
||||
this.nursingMeasures = this.bedConfig.nursingMeasures
|
||||
this.specialArrangementList = this.bedConfig.specialArrangementList
|
||||
this.bedStations = this.config.bedStation.options
|
||||
},
|
||||
mounted() {
|
||||
const setIntervalId = setInterval(() => {
|
||||
this.$forceUpdate()
|
||||
}, 60000)
|
||||
this.$once('hook:beforeDestroy', () => {
|
||||
clearInterval(setIntervalId)
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
clickAct() {
|
||||
if (!this.data.isBedNull) {
|
||||
this.PfPatientCards.activePatient = this.$props.data
|
||||
}
|
||||
this.$emit('click', this.$props.data)
|
||||
},
|
||||
moreClick() {
|
||||
this.$emit('moreClick', this.$props.data)
|
||||
},
|
||||
|
||||
getStringByCode(codeList, itemList) {
|
||||
const arr = itemList.filter((item) => {
|
||||
return codeList.includes(item.code)
|
||||
})
|
||||
|
||||
const rearr = arr.map((item) => {
|
||||
return item.display
|
||||
})
|
||||
return rearr.join('、')
|
||||
},
|
||||
isNewSign() {
|
||||
const ytime = this.data.checkInWardTime
|
||||
const hour = this.moment().diff(ytime, 'hours')
|
||||
return hour < 24
|
||||
},
|
||||
is72HourSign() {
|
||||
const ytime = this.data.checkInWardTime
|
||||
const hour = this.moment().diff(ytime, 'hours')
|
||||
return hour > 72
|
||||
},
|
||||
rescueTimeText() {
|
||||
const ytime = this.data.checkInWardTime
|
||||
const days = this.moment().diff(ytime, 'days')
|
||||
const hour = this.moment().diff(ytime, 'hours')
|
||||
const minutes = this.moment().diff(ytime, 'minutes')
|
||||
if (hour >= 24) {
|
||||
return days + '天' + hour % 24 + '时' + minutes % 60 + '分'
|
||||
} else
|
||||
if (hour < 24) {
|
||||
return hour + '时' + minutes % 60 + '分'
|
||||
} else
|
||||
if (minutes < 60) {
|
||||
return minutes + '分'
|
||||
}
|
||||
},
|
||||
getDisplay(triageLevel) {
|
||||
return triageLevel?.display ?? ''
|
||||
},
|
||||
getBedBackColor(triageLevel) {
|
||||
const Level = triageLevel?.display ?? '0级'
|
||||
let backColor = '#808080'
|
||||
switch (Level) {
|
||||
case '一级':
|
||||
backColor = this.colors[0]
|
||||
break
|
||||
case '二级':
|
||||
backColor = this.colors[1]
|
||||
break
|
||||
case '三级':
|
||||
backColor = this.colors[2]
|
||||
break
|
||||
case '四级':
|
||||
backColor = this.colors[3]
|
||||
break
|
||||
default:
|
||||
backColor = '#C4C4C4'
|
||||
}
|
||||
return backColor
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.pf_card {
|
||||
outline-style: solid;
|
||||
position: relative;
|
||||
border-radius: 5px;
|
||||
background-color: #FFFFFF;
|
||||
box-shadow: 1px 1px 4px #888888;
|
||||
border: 1px solid #e2dfdf;
|
||||
width: 265px;
|
||||
height: 194px;
|
||||
user-select: none;
|
||||
font-family: Microsoft YaHei;
|
||||
|
||||
&:hover {
|
||||
box-shadow: 4px 4px 16px #888888;
|
||||
}
|
||||
|
||||
.pf_card_card {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 7px;
|
||||
}
|
||||
|
||||
.pf_card_nameSexAndAge {
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
left: 74px;
|
||||
width: 192px;
|
||||
line-height: 18px;
|
||||
|
||||
.pf_card_name {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.pf_card_sexAndAge {
|
||||
position: absolute;
|
||||
font-size: 12px;
|
||||
color: #9AA6B5;
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.pf_card_rescueTime {
|
||||
position: absolute;
|
||||
left: 74px;
|
||||
top: 45px;
|
||||
font-size: 13px;
|
||||
color: #4C5E75;
|
||||
}
|
||||
|
||||
.pf_card_noCode {
|
||||
position: absolute;
|
||||
left: 74px;
|
||||
top: 68px;
|
||||
font-size: 13px;
|
||||
color: #4C5E75;
|
||||
}
|
||||
|
||||
.pf_card_rescueTimeText {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 68px;
|
||||
font-size: 13px;
|
||||
color: #4C5E75;
|
||||
}
|
||||
|
||||
.pf_card_diagnosis {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
left: 8px;
|
||||
top: 100px;
|
||||
font-size: 14px;
|
||||
color: #111111;
|
||||
|
||||
.card-rectangle-text {
|
||||
width: 100px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.card-rectangle {
|
||||
position: absolute;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
left: 178px;
|
||||
top: 100px;
|
||||
font-size: 14px;
|
||||
color: #FFFFFF;
|
||||
background-color: #FF3939;
|
||||
border-radius: 2px;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
.card-rectangle2 {
|
||||
position: absolute;
|
||||
width: 54px;
|
||||
height: 20px;
|
||||
right: 8px;
|
||||
top: 100px;
|
||||
font-size: 14px;
|
||||
color: #FFFFFF;
|
||||
background-color: #FDC13F;
|
||||
border-radius: 2px;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.pf_card_line {
|
||||
position: absolute;
|
||||
left: 8px;
|
||||
top: 127px;
|
||||
border: none;
|
||||
width: 250px;
|
||||
height: 1px;
|
||||
background-color: #DCDFE6;
|
||||
}
|
||||
|
||||
.pf_card_nursingMeasuresString {
|
||||
position: absolute;
|
||||
width: 240px;
|
||||
left: 8px;
|
||||
top: 140px;
|
||||
color: #0EB396;
|
||||
font-size: 12px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.pf_card_specialArrangementString{
|
||||
position: absolute;
|
||||
width: 200px;
|
||||
left: 8px;
|
||||
top: 164px;
|
||||
color: #FF9F3B;
|
||||
font-size: 12px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.pf_card_btn{
|
||||
position: absolute;
|
||||
width: 46px;
|
||||
height: 20px;
|
||||
right: 8px;
|
||||
top: 164px;
|
||||
color: #0EB396;
|
||||
text-align: center;
|
||||
line-height: 20px;
|
||||
font-size: 12px;
|
||||
border-radius: 4px;
|
||||
background-color:#9CF6E6;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.pf_card_discharge {
|
||||
position: absolute;
|
||||
width: 24px;
|
||||
height: 20px;
|
||||
top: 10px;
|
||||
left: 242px;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
line-height: 20px;
|
||||
color: white;
|
||||
border-top-left-radius: 50%;
|
||||
border-bottom-left-radius: 50%;
|
||||
background-color: #0EB396;
|
||||
}
|
||||
|
||||
.pf_card_emptyBed_img {
|
||||
position: absolute;
|
||||
left: 72px;
|
||||
top: 16px
|
||||
}
|
||||
|
||||
.pf_card_emptyBed_text {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #0EB396;
|
||||
text-align: center;
|
||||
top: 152px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,6 @@
|
||||
import mycomp from './index.vue'
|
||||
/* istanbul ignore next */
|
||||
mycomp.install = function(Vue) {
|
||||
Vue.component(mycomp.name, mycomp)
|
||||
}
|
||||
export default mycomp
|
||||
66
openhis-ui-vue3/src/components/Auto/PfPatientCardB/index.vue
Normal file
66
openhis-ui-vue3/src/components/Auto/PfPatientCardB/index.vue
Normal file
@@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<div class="pf-card-group">
|
||||
<PfPatientCard
|
||||
v-for="item in cardList"
|
||||
:key="item.bedId"
|
||||
:data="item"
|
||||
:bed-config="bedConfig"
|
||||
@click="clickAct"
|
||||
@moreClick="moreClickAct"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import PfPatientCard from './PfPatientCard'
|
||||
export default {
|
||||
name: 'PfPatientCardB',
|
||||
components: { PfPatientCard },
|
||||
provide() {
|
||||
return {
|
||||
PfPatientCards: this
|
||||
}
|
||||
},
|
||||
props: {
|
||||
cardList: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
}
|
||||
},
|
||||
bedConfig: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activePatient: { noCode: '' }
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.cardList.length > 0) {
|
||||
this.$nextTick(() => {
|
||||
this.activePatient.noCode = this.cardList[0].noCode
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clickAct(data) {
|
||||
this.$emit('itemClick', data)
|
||||
},
|
||||
moreClickAct(data) {
|
||||
this.$emit('itemMoreClick', data)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.pf-card-group {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, 264px);
|
||||
margin: 12px;
|
||||
grid-gap: 12px;
|
||||
}
|
||||
</style>
|
||||
76
openhis-ui-vue3/src/components/Auto/printBills/bedCard.vue
Normal file
76
openhis-ui-vue3/src/components/Auto/printBills/bedCard.vue
Normal file
@@ -0,0 +1,76 @@
|
||||
<template>
|
||||
<div class="printCard">
|
||||
<div ref="refQr" style="float: left; margin: 30px 15px">
|
||||
<img :src="emptyBed" style="height: 120px" class="pf_card_emptyBed_img">
|
||||
</div>
|
||||
<div class="printView_content" style=" margin: 30px 0">
|
||||
<div>
|
||||
<span>床号:</span>
|
||||
<span>{{ printData.bedName }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>姓名:</span>
|
||||
<span>{{ printData.patientName }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>患者编号:</span>
|
||||
<span>{{ printData.hisId }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>分诊科室:</span>
|
||||
<span>{{ printData.dept }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>分诊等级:</span>
|
||||
<span>{{ printData.triageLevel }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import emptyBed from '@/assets/png/emptyBed.png'
|
||||
export default {
|
||||
name: 'TriageTicket',
|
||||
props: {
|
||||
printData: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {
|
||||
bedName: '',
|
||||
patientName: '',
|
||||
dept: '',
|
||||
triageLevel: '',
|
||||
triageTime: '',
|
||||
hisId: '11111'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
emptyBed
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
updated() {
|
||||
},
|
||||
methods: {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.printCard {
|
||||
border: #8d8d8d 1px solid;
|
||||
height: 200px !important;
|
||||
width: 320px;
|
||||
display: grid;
|
||||
grid-template-columns: 150px 200px;
|
||||
}
|
||||
|
||||
.printView_content{
|
||||
display: grid;
|
||||
padding-top: 10px;
|
||||
grid-template-rows: repeat(5,25px);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,120 @@
|
||||
<template>
|
||||
<div class="recordBill">
|
||||
<div id="div1" class="printView_header">
|
||||
<div style="text-align: center; height: 40px">
|
||||
护理交接班
|
||||
</div>
|
||||
<div>
|
||||
<span style="display: inline-block; width: 200px">日期:{{ printData.date.substring(0, 10) }}</span>
|
||||
<span style="display: inline-block; width: 180px">发起人:{{ printData.initiator.name }}</span>
|
||||
<span style="display: inline-block; width: 140px">接收人:{{ printData.heir.name }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span
|
||||
v-for="item in printData.cols"
|
||||
:key="item.propertyType"
|
||||
style="display: inline-block; width: 90px"
|
||||
v-text="item.display + ':' + printData[item.propertyType]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div id="div2" class="printView_content">
|
||||
<table border="1" cellSpacing="0" width="98%" cellPadding="1" style=" border-collapse:collapse; font-size: 14px" bordercolor="#333333">
|
||||
<thead>
|
||||
<TR>
|
||||
<TD colspan="1">
|
||||
<DIV style="width: 40px" align="center">类别</DIV>
|
||||
</TD>
|
||||
<TD colspan="1">
|
||||
<DIV style="width: 50px" align="center">床号</DIV>
|
||||
</TD>
|
||||
<TD colspan="1">
|
||||
<DIV style="width: 60px" align="center">姓名</DIV>
|
||||
</TD>
|
||||
<TD colspan="1">
|
||||
<DIV style="width: 90px" align="center">主诉</DIV>
|
||||
</TD>
|
||||
<TD colspan="1">
|
||||
<DIV style="width: 90px" align="center">既往史</DIV>
|
||||
</TD>
|
||||
<TD colspan="1">
|
||||
<DIV style="width: 90px" align="center">诊断</DIV>
|
||||
</TD>
|
||||
<TD colspan="1">
|
||||
<DIV style="width: 155px" align="center">交接信息</DIV>
|
||||
</TD>
|
||||
</TR>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="item in printData.shiftRecordItems" :key="item.id">
|
||||
<td v-html="item.typeDisplay" />
|
||||
<td v-html="item.bedName" />
|
||||
<td v-html="item.patientName" />
|
||||
<td v-html="item.mainSuit" />
|
||||
<td v-html="item.previousHistory" />
|
||||
<td v-html="item.diagnosis" />
|
||||
<td v-html="item.content" />
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { getLodop } from '../../../plugins/print/LodopFuncs'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
printData: {
|
||||
patientInfo: {},
|
||||
recordData: {},
|
||||
cols: []
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
methods: {
|
||||
printTest() {
|
||||
const LODOP = getLodop()
|
||||
LODOP.PRINT_INIT('')
|
||||
LODOP.ADD_PRINT_TABLE(100, 40, 750, 900, document.getElementById('div2').innerHTML)
|
||||
LODOP.SET_PRINT_STYLEA(0, 'Horient', 3)
|
||||
LODOP.ADD_PRINT_HTM(20, 40, '100%', 100, document.getElementById('div1').innerHTML)
|
||||
LODOP.SET_PRINT_STYLEA(0, 'ItemType', 1)
|
||||
LODOP.SET_PRINT_STYLEA(0, 'LinkedItem', 1)
|
||||
// LODOP.SET_PRINT_PAGESIZE(2, '', '', ''); // 设置横向打印
|
||||
LODOP.ADD_PRINT_HTM(1080, 500, 300, 100, '总页数:<span><span tdata="pageNO">第##页</span>/ <span tdata="pageCount">共##页</span></span>')
|
||||
LODOP.SET_PRINT_STYLEA(0, 'ItemType', 1)
|
||||
LODOP.SET_PRINT_STYLEA(0, 'Horient', 1)
|
||||
// LODOP.PREVIEW(); // 打印预览
|
||||
LODOP.PRINT() // 直接打印
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.recordBill {
|
||||
border: #8d8d8d 1px solid;
|
||||
display: grid;
|
||||
grid-template-rows: 90px 1fr;
|
||||
height: 200px !important;
|
||||
width: 740px;
|
||||
|
||||
/deep/ .el-table .cell {
|
||||
font-size: 10px !important;
|
||||
}
|
||||
.printView_header {
|
||||
grid-template-rows: 40px 30px 30px;
|
||||
height: 200px !important;
|
||||
h4{
|
||||
text-align: center;
|
||||
margin: 15px;
|
||||
}
|
||||
}
|
||||
.printView_content{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
149
openhis-ui-vue3/src/components/Auto/printBills/exeOrderSheet.vue
Normal file
149
openhis-ui-vue3/src/components/Auto/printBills/exeOrderSheet.vue
Normal file
@@ -0,0 +1,149 @@
|
||||
<template>
|
||||
<div class="recordBill">
|
||||
<div :id="'exeSheetTitle' + printData.id" class="printView_header">
|
||||
<div style="text-align: center; height: 60px">
|
||||
呼和浩特市第一医院医嘱执行单
|
||||
</div>
|
||||
<div>
|
||||
<span style="display: inline-block; width: 100px">床号:{{ printData.patientInfo.encounterLocationName }}</span>
|
||||
<span style="display: inline-block; width: 180px">姓名:{{ printData.patientInfo.name }}</span>
|
||||
<span style="display: inline-block; width: 140px">年龄:{{ printData.patientInfo.patientAge }}</span>
|
||||
<span style="display: inline-block; width: 280px">诊断:{{ printData.patientInfo.diag }}</span>
|
||||
</div>
|
||||
<!-- <div>
|
||||
<span style="display: inline-block; width: 200px">档案号:{{printData.patientInfo.hisNo}}</span>
|
||||
<span style="display: inline-block; width: 260px">入室时间:{{printData.patientInfo.checkInTime}}</span>
|
||||
<span style="display: inline-block; width: 140px">性别:{{!printData.patientInfo.gender? '':printData.patientInfo.gender.display}}</span>
|
||||
</div>-->
|
||||
</div>
|
||||
<div :id="'exeSheet' + printData.id" class="printView_content">
|
||||
<table border="1" cellSpacing="0" width="97%" cellPadding="1" style=" border-collapse:collapse; font-size: 13px" bordercolor="#333333">
|
||||
<thead>
|
||||
<TR>
|
||||
<TD rowspan="1">
|
||||
<DIV style="width: 65px;text-align: center">医嘱日期</DIV>
|
||||
</TD>
|
||||
|
||||
<TD colspan="1">
|
||||
<DIV style="width: 120px" align="center">医嘱</DIV>
|
||||
</TD>
|
||||
<TD colspan="1">
|
||||
<DIV style="width: 10px" align="center" />
|
||||
</TD>
|
||||
<TD colspan="1">
|
||||
<DIV style="width: 70px" align="center">嘱托</DIV>
|
||||
</TD>
|
||||
<TD rowspan="1">
|
||||
<DIV style="width: 60px" align="center">用量</DIV>
|
||||
</TD>
|
||||
<TD colspan="1">
|
||||
<DIV style="width: 40px" align="center">用法</DIV>
|
||||
</TD>
|
||||
<TD colspan="1">
|
||||
<DIV style="width: 40px" align="center">频次</DIV>
|
||||
</TD>
|
||||
<TD rowspan="1">
|
||||
<DIV style="width: 65px" align="center">开立医生</DIV>
|
||||
</TD>
|
||||
<TD rowspan="1">
|
||||
<DIV style="width: 65px" align="center">执行时间</DIV>
|
||||
</TD>
|
||||
<TD colspan="1">
|
||||
<DIV style="width: 65px" align="center">执行护士</DIV>
|
||||
</TD>
|
||||
<TD colspan="1">
|
||||
<DIV style="width: 55px" align="center">终止时间</DIV>
|
||||
</TD>
|
||||
<TD colspan="1">
|
||||
<DIV style="width: 55px" align="center">终止人</DIV>
|
||||
</TD>
|
||||
</TR>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="item in printData.recordData" :key="item.id">
|
||||
<td v-html="item.moTime" />
|
||||
<td v-html="item.orderName" />
|
||||
<td v-html="item.flag" />
|
||||
<td v-html="item.remark" />
|
||||
<td v-html="item.doseOnce<=0?'':(item.doseOnce+ item.doseUnit)" />
|
||||
<td v-html="item.usageName" />
|
||||
<td v-html="item.frequency" />
|
||||
<td :id="item.id">
|
||||
<span v-if="(item.docSignImage === ''||item.docSignImage === null)">{{ item.moDocName }}</span>
|
||||
<img v-if="(item.docSignImage !== ''&&item.docSignImage !== null)" :src="'data:image/png;base64,'+ item.docSignImage" style="height: 100%; width: 100%;object-fit: cover;">
|
||||
</td>
|
||||
<td v-html="item.occurrence" />
|
||||
<td :id="item.id">
|
||||
<span v-if="(item.perNurserSignImage === ''||item.perNurserSignImage === null)">{{ item.performName }}</span>
|
||||
<img v-if="(item.perNurserSignImage !== ''&&item.perNurserSignImage !== null)" :src="'data:image/png;base64,'+ item.perNurserSignImage" style="height: 100%; width: 100%;object-fit: cover;">
|
||||
</td>
|
||||
<td />
|
||||
<td />
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { getLodop } from '../../../plugins/print/LodopFuncs'
|
||||
export default {
|
||||
props: {
|
||||
printData: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
methods: {
|
||||
printTest() {
|
||||
const LODOP = getLodop()
|
||||
LODOP.PRINT_INIT('')
|
||||
LODOP.ADD_PRINT_TABLE(120, 35, 750, 900, document.getElementById('exeSheet' + this.printData.id).innerHTML)
|
||||
LODOP.SET_PRINT_STYLEA(0, 'Horient', 3)
|
||||
LODOP.ADD_PRINT_HTM(20, 40, '100%', 100, document.getElementById('exeSheetTitle' + this.printData.id).innerHTML)
|
||||
LODOP.SET_PRINT_STYLEA(0, 'ItemType', 1)
|
||||
LODOP.SET_PRINT_STYLEA(0, 'LinkedItem', 1)
|
||||
// LODOP.SET_PRINT_PAGESIZE(2, '', '', ''); // 设置横向打印
|
||||
LODOP.ADD_PRINT_HTM(1080, 500, 300, 100, '总页数:<span><span tdata="pageNO">第##页</span>/ <span tdata="pageCount">共##页</span></span>')
|
||||
LODOP.SET_PRINT_STYLEA(0, 'ItemType', 1)
|
||||
LODOP.SET_PRINT_STYLEA(0, 'Horient', 1)
|
||||
LODOP.SET_SHOW_MODE('LANDSCAPE_DEFROTATED', 1)// 横向时的正向显示
|
||||
LODOP.PREVIEW() // 打印预览
|
||||
// LODOP.PRINT(); // 直接打印
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.recordBill {
|
||||
display: grid;
|
||||
grid-template-rows: 90px 1fr;
|
||||
height: 500px !important;
|
||||
width: 680px;
|
||||
|
||||
/deep/ .el-table .cell {
|
||||
font-size: 10px !important;
|
||||
}
|
||||
.printView_header {
|
||||
grid-template-rows: 40px 30px 30px;
|
||||
height: 200px !important;
|
||||
h4{
|
||||
text-align: center;
|
||||
margin: 15px;
|
||||
}
|
||||
}
|
||||
.printView_content{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
155
openhis-ui-vue3/src/components/Auto/printBills/injectLabel.vue
Normal file
155
openhis-ui-vue3/src/components/Auto/printBills/injectLabel.vue
Normal file
@@ -0,0 +1,155 @@
|
||||
<template>
|
||||
<div ref="print">
|
||||
<div class="printInjectCard">
|
||||
<div :id="printData.id + 'div1'">
|
||||
<div style="display:block; height: 60px; width: 280px; float:left;">
|
||||
<span style="font-weight: bolder; font-size: 16px; line-height: 36px; margin-left: 160px;">急诊输液贴</span>
|
||||
<div>
|
||||
<span style="margin-left: 8px;">{{ printData.patient.hisNo }}</span>
|
||||
<span style="margin-left: 8px;">{{ printData.patient.name }}</span>
|
||||
<span style="margin-left: 8px;">{{ printData.patient.sexName }}</span>
|
||||
<span style="margin-left: 8px;">{{ printData.patient.patientAge }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: block; width: 120px; height: 60px; float:left; ">
|
||||
<div :id="getId(printData.id)" style="float: left; margin: 5px;" />
|
||||
<span style="float: left; margin: 5px">{{ printData.priority }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div :id="printData.id + 'div2'">
|
||||
<table border="1" cellSpacing="0" width="390px" cellPadding="1" style="margin-left: 8px; border-collapse:collapse; table-layout: fixed; font-size: 14px" bordercolor="#333333">
|
||||
<thead>
|
||||
<TR>
|
||||
<Th style="width: 160px" v-html="'药品名称'" />
|
||||
<Th style="width: 75px" v-html="'用量'" />
|
||||
<Th style="width: 10px" v-html="''" />
|
||||
<Th style="width: 50px" v-html="'频次'" />
|
||||
<Th style="width: 75px" v-html="'用法'" />
|
||||
</TR>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="item in printData.orderDetail" :key="item.id">
|
||||
<td v-html="item.orderName" />
|
||||
<td v-html="item.doseOnce + item.doseUnit" />
|
||||
<td v-html="item.flag" />
|
||||
<td v-html="item.frequency" />
|
||||
<td v-html="item.usageName" />
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div :id="printData.id + 'div3'">
|
||||
<span>日期:</span>
|
||||
<span>{{ moment().format('YYYY-MM-DD HH:mm') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
// import QRCode from 'qrcodejs2'
|
||||
import { getLodop } from '../../../plugins/print/LodopFuncs'
|
||||
export default {
|
||||
name: 'VuePrintNb',
|
||||
props: {
|
||||
printData: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
lastId: '',
|
||||
qrCode: ''
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// console.log('mounted方法');
|
||||
// this.initBarCode();
|
||||
},
|
||||
methods: {
|
||||
// initBarCode() {
|
||||
// this.$nextTick(() => {
|
||||
// if (this.lastId !== this.printData.patient.hisId) {
|
||||
// new QRCode(this.getId(this.printData.id), {
|
||||
// text: this.printData.patient.hisId,
|
||||
// width: 50,
|
||||
// height: 50,
|
||||
// colorDark: '#000000',
|
||||
// colorLight: '#ffffff',
|
||||
// correctLevel: QRCode.CorrectLevel.H
|
||||
// });
|
||||
// }
|
||||
// this.lastId = this.printData.patient.hisId;
|
||||
// });
|
||||
// },
|
||||
//
|
||||
// getId(id) {
|
||||
// return 'qrcode' + id;
|
||||
// },
|
||||
print(printerName) {
|
||||
const LODOP = getLodop()
|
||||
const printer = this.getPrinter(LODOP, printerName)
|
||||
if (printer === null) {
|
||||
this.openMesBox('6', '没有找到打印机【' + printerName + '】')
|
||||
return
|
||||
}
|
||||
console.log(this.printData, 'printData')
|
||||
this.qrCode = this.printData.orderDetail[0].comboNo + this.printData.orderDetail[0].executionSeq
|
||||
LODOP.PRINT_INIT()
|
||||
LODOP.SET_PRINTER_INDEX(printer)// 指定打印机
|
||||
this.setPrint(LODOP)
|
||||
LODOP.SET_PRINT_PAGESIZE(0, 1070, 800, '')
|
||||
LODOP.PREVIEW() // 打印预览
|
||||
// LODOP.PRINT(); // 直接打印
|
||||
},
|
||||
setPrint(LODOP) {
|
||||
LODOP.ADD_PRINT_HTM(0, 0, '100%', '100%', document.getElementById(this.printData.id + 'div1').innerHTML)
|
||||
LODOP.ADD_PRINT_HTM(82, 0, '100%', '100%', document.getElementById(this.printData.id + 'div2').innerHTML)
|
||||
LODOP.ADD_PRINT_HTM(265, 10, '100%', '100%', document.getElementById(this.printData.id + 'div3').innerHTML)
|
||||
LODOP.SET_PRINT_STYLEA(0, 'ItemType', 1)
|
||||
// 设置二维码 qrcode ,条码128B等
|
||||
// LODOP.ADD_PRINT_BARCODE(Top,Left,Width,Height,BarCodeType,BarCodeValue);
|
||||
LODOP.ADD_PRINT_BARCODE(0, 300, 75, 75, 'qrcode', this.qrCode)// 设置条码位置、宽高、字体、值
|
||||
LODOP.SET_PRINT_STYLEA(0, 'FontSize', 18)// 设置上面这个条码下方的文字字体大小
|
||||
// LODOP.SET_PRINT_STYLEA(0,"Color","#FF0000");//设置当前条码以及条码下方字体的颜色
|
||||
LODOP.SET_PRINT_STYLEA(0, 'Angle', 180)// 设置旋转角度
|
||||
LODOP.SET_PRINT_STYLEA(0, 'ShowBarText', 0)// 设置是否显示下方的文字
|
||||
LODOP.SET_PRINT_STYLEA(0, 'AlignJustify', 2)// 设置条码下方的文字相对于条码本身居中
|
||||
// LODOP.SET_PRINT_STYLEA(0,"AlignJustify",1);//设置条码下方的文字相对于条码本身居左
|
||||
// LODOP.SET_PRINT_STYLEA(0,"AlignJustify",3);//设置条码下方的文字相对于条码本身居右
|
||||
// LODOP.SET_PRINT_STYLEA(0,"GroundColor","#0080FF");//设置条码的背景色
|
||||
},
|
||||
// 获取打印机
|
||||
getPrinter(LODOP, name) {
|
||||
const listCount = LODOP.GET_PRINTER_COUNT() // 当前打印设备数量
|
||||
for (let i = 0; i < listCount; i++) {
|
||||
if (LODOP.GET_PRINTER_NAME(i) === name) {
|
||||
return name
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="less">
|
||||
.printInjectCard{
|
||||
display: grid;
|
||||
width: 400px;
|
||||
grid-template-rows: 60px 205px 30px;
|
||||
border: solid #555 1px;
|
||||
background-color: #FFFFFF;
|
||||
|
||||
td{
|
||||
padding-left: 3px;
|
||||
}
|
||||
}
|
||||
@page{
|
||||
size: auto;
|
||||
margin: 32px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
<template>
|
||||
<div class="recordBill">
|
||||
<div id="div1" class="printView_header">
|
||||
<div style="text-align: center; font-size: 20px; height: 40px">
|
||||
呼和浩特市第一医院输液执行单
|
||||
</div>
|
||||
<div>
|
||||
<span>座位:{{ printData.patientInfo.encounterLocationName }}</span>
|
||||
<span style="margin-left: 18px">姓名:{{ printData.patientInfo.name }}</span>
|
||||
<span style="margin-left: 18px">性别:{{ printData.patientInfo.sexName }}</span>
|
||||
<span style="margin-left: 18px">年龄:{{ printData.patientInfo.patientAge }}</span>
|
||||
<span style="margin-left: 18px">卡号:{{ printData.patientInfo.hisNo }}</span>
|
||||
<span style="margin-left: 18px">科室:{{ printData.patientInfo.deptName }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="div2" class="printView_content">
|
||||
<table border="1" cellSpacing="0" cellPadding="1" style=" border-collapse:collapse; font-size: 14px" bordercolor="#333333">
|
||||
<thead>
|
||||
<TR style="height: 30px">
|
||||
<TD rowspan="1">
|
||||
<DIV style="width: 35px" align="center">时间</DIV>
|
||||
</TD>
|
||||
<TD colspan="1">
|
||||
<DIV style="width: 280px" align="center">药品名称</DIV>
|
||||
</TD>
|
||||
<TD colspan="1">
|
||||
<DIV style="width: 10px" align="center" />
|
||||
</TD>
|
||||
<TD rowspan="1">
|
||||
<DIV style="width: 55px" align="center">剂量</DIV>
|
||||
</TD>
|
||||
<TD colspan="1">
|
||||
<DIV style="width: 30px" align="center">频次</DIV>
|
||||
</TD>
|
||||
<TD colspan="1">
|
||||
<DIV style="width: 55px" align="center">用法</DIV>
|
||||
</TD>
|
||||
<TD rowspan="1">
|
||||
<DIV style="width: 70px" align="center">执行时间</DIV>
|
||||
</TD>
|
||||
<TD rowspan="1">
|
||||
<DIV style="width: 55px" align="center">执行人</DIV>
|
||||
</TD>
|
||||
</TR>
|
||||
</thead>
|
||||
<tbody style=" border-collapse:collapse;">
|
||||
<tr v-for="item in printData.recordData" :key="item.id">
|
||||
<td v-html="item.moTime.substring(0,16)" />
|
||||
<td v-html="item.orderName" />
|
||||
<td v-html="item.flag" />
|
||||
<td v-html="item.doseOnce + item.doseUnit" />
|
||||
<td v-html="item.freqName" />
|
||||
<td v-html="item.usageName" />
|
||||
<td />
|
||||
<td />
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { getLodop } from '../../../plugins/print/LodopFuncs'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
printData: {
|
||||
patientInfo: {},
|
||||
recordData: {}
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
methods: {
|
||||
printTest() {
|
||||
const LODOP = getLodop()
|
||||
LODOP.PRINT_INIT('')
|
||||
LODOP.ADD_PRINT_TABLE(100, 35, 700, 900, document.getElementById('div2').innerHTML)
|
||||
LODOP.ADD_PRINT_HTM(20, 40, '100%', 100, document.getElementById('div1').innerHTML)
|
||||
LODOP.SET_PRINT_PAGESIZE(0, '148mm', '210mm', '') // 设置横向打印
|
||||
// LODOP.SET_SHOW_MODE('LANDSCAPE_DEFROTATED', 1);// 横向时的正向显示
|
||||
LODOP.PREVIEW() // 打印预览
|
||||
// LODOP.PRINT(); // 直接打印
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.recordBill {
|
||||
border: #8d8d8d 1px solid;
|
||||
display: grid;
|
||||
grid-template-rows: 90px 1fr;
|
||||
height: 200px !important;
|
||||
width: 680px;
|
||||
|
||||
/deep/ .el-table .cell {
|
||||
font-size: 10px !important;
|
||||
}
|
||||
.printView_header {
|
||||
grid-template-rows: 40px 30px 30px;
|
||||
height: 200px !important;
|
||||
h4{
|
||||
text-align: center;
|
||||
margin: 15px;
|
||||
}
|
||||
}
|
||||
.printView_content{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<div>
|
||||
<div ref="print">
|
||||
<div v-for="item in printData" :key="item.id">
|
||||
<div class="myccs2">
|
||||
<injectLabel :ref="item.id" :print-data="item" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Print from 'vue-print-nb'
|
||||
import injectLabel from './injectLabel'
|
||||
export default {
|
||||
components: { injectLabel },
|
||||
props: {
|
||||
printData: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
}
|
||||
}
|
||||
},
|
||||
Print,
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 打印
|
||||
fprint(preview, printer) {
|
||||
this.$nextTick(() => {
|
||||
if (preview) {
|
||||
this.$print(this.$refs.print)
|
||||
} else {
|
||||
this.printData.forEach(data => {
|
||||
this['$refs'][data.id][0].print(printer)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="less">
|
||||
.myccs{
|
||||
background-color: forestgreen;
|
||||
height: 100px;
|
||||
width: 200px;
|
||||
padding: 1px;
|
||||
border: 1px solid red;
|
||||
color: #0EB396;
|
||||
}
|
||||
@page{
|
||||
size: auto;
|
||||
margin: 32px;
|
||||
}
|
||||
@media print {
|
||||
.myccs2{
|
||||
page-break-before: always;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<div>
|
||||
<div ref="print">
|
||||
<div v-for="item in printData" :key="item.id">
|
||||
<div class="myccs2">
|
||||
<orderSheet v-if="!item.type" :ref="item.id" :print-data="item" />
|
||||
<exeOrderSheet v-if="item.type" :ref="item.id" :print-data="item" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Print from 'vue-print-nb'
|
||||
import orderSheet from './orderSheet'
|
||||
import exeOrderSheet from './exeOrderSheet'
|
||||
export default {
|
||||
name: 'VuePrintNb',
|
||||
components: { orderSheet, exeOrderSheet },
|
||||
props: {
|
||||
printData: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
}
|
||||
}
|
||||
},
|
||||
Print,
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 打印
|
||||
fprint(preview, printer) {
|
||||
console.log(this.printData, 'printData')
|
||||
this.$nextTick(() => {
|
||||
if (preview) {
|
||||
this.$print(this.$refs.print)
|
||||
} else {
|
||||
this.printData.forEach(data => {
|
||||
this['$refs'][data.id][0].printTest()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="less">
|
||||
.myccs{
|
||||
background-color: forestgreen;
|
||||
height: 100px;
|
||||
width: 200px;
|
||||
padding: 1px;
|
||||
border: 1px solid red;
|
||||
color: #0EB396;
|
||||
}
|
||||
@page{
|
||||
size: auto;
|
||||
margin: 20px;
|
||||
}
|
||||
@media print {
|
||||
.myccs2{
|
||||
page-break-before: always;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
<template>
|
||||
<Graphics v-if="graphicsDataDone" :value="resInfo" print @done="printPage" />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Graphics from '../../../views/inpatientNurse/tprChart/index';
|
||||
import data from '../../../action/nurseStation/temperatureSheet/datas';
|
||||
|
||||
const printData = ref({});
|
||||
const resInfo = ref({});
|
||||
const graphicsDataDone = ref(false);
|
||||
const printPromiseReslove = ref(null);
|
||||
const dateClosed = ref({
|
||||
stopTime: true, // 控制结束日期
|
||||
stopNumber: true, // 控制住院天数
|
||||
});
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
function removeIframe(id) {
|
||||
const child = window.parent.document.getElementById(id);
|
||||
if (child) {
|
||||
child.parentElement.removeChild(child);
|
||||
}
|
||||
}
|
||||
function setTime(num) {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(resolve, num);
|
||||
});
|
||||
}
|
||||
// export default {
|
||||
// components: {
|
||||
// Graphics,
|
||||
// },
|
||||
// data() {
|
||||
// return {
|
||||
// printData: {},
|
||||
// resInfo: {},
|
||||
// graphicsDataDone: false,
|
||||
// // 当前页面是否完成打印的reslove函数
|
||||
// printPromiseReslove: null,
|
||||
// dateClosed: {
|
||||
// stopTime: true, // 控制结束日期
|
||||
// stopNumber: true, // 控制住院天数
|
||||
// },
|
||||
// };
|
||||
// },
|
||||
// mounted() {
|
||||
// const printData = window.localStorage.getItem('printItemData');
|
||||
// this.printData = JSON.parse(printData);
|
||||
// this.addPrintEvent();
|
||||
// this.runTask();
|
||||
// },
|
||||
// methods: {
|
||||
function addPrintEvent() {
|
||||
window.addEventListener('afterprint', () => {
|
||||
if (printPromiseReslove.value) {
|
||||
printPromiseReslove.value();
|
||||
}
|
||||
});
|
||||
}
|
||||
async function runTask() {
|
||||
// const weeks = this.printData.weekList;
|
||||
const weeks = [];
|
||||
for (let index = 0; index < weeks.length; index++) {
|
||||
const week = weeks[index];
|
||||
await this.getData(week);
|
||||
}
|
||||
try {
|
||||
removeIframe(this.$route.query.id);
|
||||
} catch (error) {
|
||||
// window.location.href = './compTemperature';
|
||||
var child = window.parent.document.getElementById('my_dataviz');
|
||||
child.parentElement.appendChild(child);
|
||||
}
|
||||
}
|
||||
function printPage() {
|
||||
window.print();
|
||||
}
|
||||
// 获取每周数据
|
||||
async function getData(curWeekInfo) {
|
||||
this.resInfo = data;
|
||||
if (this.graphicsDataDone) {
|
||||
this.graphicsDataDone = false;
|
||||
}
|
||||
await setTime(10);
|
||||
this.graphicsDataDone = true;
|
||||
return new Promise((resolve) => {
|
||||
this.printPromiseReslove = resolve;
|
||||
});
|
||||
}
|
||||
// },
|
||||
// };
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@page {
|
||||
margin-top: 30;
|
||||
margin-bottom: 0;
|
||||
background-color: #1890ff;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,77 @@
|
||||
<template>
|
||||
<div class="printTicket">
|
||||
<p>xx人民医院</p>
|
||||
<div>
|
||||
<span>姓名:</span>
|
||||
<span>{{ printData.patientName }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>患者编号:</span>
|
||||
<span>{{ printData.hisId }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>分诊科室:</span>
|
||||
<span>{{ printData.dept }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>分诊等级:</span>
|
||||
<span>{{ printData.triageLevel }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>分诊时间:</span>
|
||||
<span>{{ printData.triageTime }}</span>
|
||||
</div>
|
||||
<img ref="refQr" style="position: absolute; top: 10px; left: 100px">
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import JsBarcode from 'jsbarcode'
|
||||
export default {
|
||||
name: 'TriageTicket',
|
||||
props: {
|
||||
printData: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {
|
||||
patientName: '',
|
||||
dept: '',
|
||||
triageLevel: '',
|
||||
triageTime: '',
|
||||
hisId: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
updated() {
|
||||
JsBarcode(this.$refs.refQr,
|
||||
this.printData.hisId,
|
||||
{
|
||||
format: 'CODE128',
|
||||
lineColor: '#000',
|
||||
background: '#fff',
|
||||
displayValue: false,
|
||||
height: 30,
|
||||
margin: 2
|
||||
})
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
methods: {
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.printTicket{
|
||||
display: block;
|
||||
width: 300px;
|
||||
height: 200px;
|
||||
border: 1px solid #A3A3A3;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<div>
|
||||
<div ref="print">
|
||||
<div class="myccs2">
|
||||
<triageTicketNew ref="printTriage" :print-data="printData" />
|
||||
</div>
|
||||
<!-- <div v-for="item in printData" :key="item.id">-->
|
||||
<!-- <div class="myccs2">-->
|
||||
<!-- <triageTicketNew :ref="item.id" :print-data="printData"/>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Print from 'vue-print-nb'
|
||||
import triageTicketNew from './triageTicketNew'
|
||||
export default {
|
||||
components: { triageTicketNew },
|
||||
props: {
|
||||
printData: {
|
||||
// type: Array,
|
||||
default() {
|
||||
return {
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Print,
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 打印
|
||||
fprint(preview, printer) {
|
||||
this.$nextTick(() => {
|
||||
if (preview) {
|
||||
this.$print(this.$refs.print)
|
||||
} else {
|
||||
// this.$refs.printTriage.initBarCode();
|
||||
this.$refs.printTriage.printTriage(printer)
|
||||
// this.printData.forEach(data => {
|
||||
// this['$refs'][data.id][0].print(printer);
|
||||
// });
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.myccs{
|
||||
background-color: forestgreen;
|
||||
height: 100px;
|
||||
width: 200px;
|
||||
padding: 1px;
|
||||
border: 1px solid red;
|
||||
color: #0EB396;
|
||||
}
|
||||
@page{
|
||||
size: auto;
|
||||
margin: 32px;
|
||||
}
|
||||
@media print {
|
||||
.myccs2{
|
||||
page-break-before: always;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,192 @@
|
||||
<template>
|
||||
<div ref="print">
|
||||
<div class="printInjectCard">
|
||||
<div :id="'div1'">
|
||||
<div style="width: 300px; text-align: center">
|
||||
<span style="font-weight: bolder; font-size: 18px; line-height: 36px">{{ printData.greenText }}</span>
|
||||
<span style="font-weight: bolder; font-size: 18px; line-height: 36px">分诊单</span>
|
||||
</div>
|
||||
<div style="position: absolute; top: 135px; text-align: center; width: 300px">{{ printData.hisId }}</div>
|
||||
<div style="position: absolute; top: 155px; text-align: center; width: 300px">
|
||||
{{ printData.triageLevel }}({{ printData.dept }})
|
||||
</div>
|
||||
<div
|
||||
style="
|
||||
position: absolute;
|
||||
top: 180px;
|
||||
left: 15px;
|
||||
display: block;
|
||||
width: 300px;
|
||||
height: 10px;
|
||||
border-bottom: 1px solid #5a5a5a;
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<div :id="'div2'">
|
||||
<div style="width: 320px; margin-left: 25px">
|
||||
<div style="font-size: 15px">
|
||||
<span>姓名:</span>
|
||||
<span>{{ printData.patientName }}</span>
|
||||
<span style="margin-left: 15px">性别:</span>
|
||||
<span>{{ printData.sex }}</span>
|
||||
<span style="margin-left: 15px">年龄:</span>
|
||||
<span>{{ printData.age }}</span>
|
||||
</div>
|
||||
<div
|
||||
style="
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
left: 15px;
|
||||
display: block;
|
||||
width: 300px;
|
||||
height: 10px;
|
||||
border-bottom: 1px solid #5a5a5a;
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div :id="'div3'">
|
||||
<div style="margin-left: 15px; vertical-align: center; line-height: 18px">
|
||||
<div>
|
||||
<div style="display: inline-block; font-size: 15px">
|
||||
<span>Temp:</span>
|
||||
<span>{{ printData.observation ? printData.observation.temperature : '' }}℃</span>
|
||||
</div>
|
||||
<div style="display: inline-block; position: absolute; left: 180px; font-size: 15px">
|
||||
<span>P:</span>
|
||||
<span>{{ printData.observation ? printData.observation.sphygmus : '' }}次/分</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: block">
|
||||
<div style="display: inline-block; font-size: 15px">
|
||||
<span>R:</span>
|
||||
<span>{{ printData.observation ? printData.observation.breath : '' }}次/分</span>
|
||||
</div>
|
||||
<div style="display: inline-block; position: absolute; left: 180px; font-size: 15px">
|
||||
<span>BP:</span>
|
||||
<span>{{ getBloodPressure(printData.observation) }}mmHg</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: block; font-size: 15px">
|
||||
<span>SPO2:</span>
|
||||
<span>{{ printData.observation ? printData.observation.bloodOxygen : '' }}%</span>
|
||||
</div>
|
||||
<div
|
||||
style="
|
||||
position: absolute;
|
||||
top: 48px;
|
||||
left: 5px;
|
||||
display: block;
|
||||
width: 300px;
|
||||
height: 10px;
|
||||
border-bottom: 1px solid #5a5a5a;
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div :id="'div4'">
|
||||
<div style="margin-left: 15px; font-size: 15px">
|
||||
<div>
|
||||
<span>分诊时间:</span>
|
||||
<span>{{ printData.triageTime }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>联系电话:</span>
|
||||
<span>{{ printData.tel }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
style="
|
||||
position: absolute;
|
||||
top: 35px;
|
||||
left: 5px;
|
||||
display: block;
|
||||
width: 300px;
|
||||
height: 10px;
|
||||
border-bottom: 1px solid #5a5a5a;
|
||||
"
|
||||
/>
|
||||
<div style="margin-left: 15px">
|
||||
<div style="font-size: 14px; margin-top: 15px; font-weight: bolder">请仔细核对个人信息后进行挂号</div>
|
||||
<div style="margin-top: 5px; font-size: 14px">为了您家人和其他患者的健康</div>
|
||||
<div style="font-size: 14px">请您保持就诊秩序保持诊区安静</div>
|
||||
<div style="font-size: 14px">祝您早日康复</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { getLodop } from '../../../plugins/print/LodopFuncs'
|
||||
export default {
|
||||
name: 'VuePrintNb',
|
||||
props: {
|
||||
printData: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
lastId: ''
|
||||
}
|
||||
},
|
||||
mounted() {},
|
||||
|
||||
methods: {
|
||||
// 获取血压值
|
||||
getBloodPressure(pressure) {
|
||||
if (!pressure) return ''
|
||||
else if (pressure.bloodPressureShrinkOne === null) return ''
|
||||
return pressure.bloodPressureShrinkOne + '/' + pressure.bloodPressureDiastoleOne
|
||||
},
|
||||
printTriage(printerName) {
|
||||
console.log(this.printData, 'printData')
|
||||
const LODOP = getLodop()
|
||||
const printer = this.getPrinter(LODOP, printerName)
|
||||
if (printer === null) {
|
||||
this.openMesBox('6', '没有找到打印机【' + printerName + '】')
|
||||
return
|
||||
}
|
||||
LODOP.PRINT_INIT()
|
||||
LODOP.SET_PRINTER_INDEX(printer) // 指定打印机
|
||||
this.setPrint(LODOP)
|
||||
LODOP.SET_PRINT_PAGESIZE(0, '100mm', '140mm', '')
|
||||
// LODOP.PREVIEW(); // 打印预览
|
||||
LODOP.PRINT() // 直接打印
|
||||
},
|
||||
setPrint(LODOP) {
|
||||
LODOP.ADD_PRINT_HTM(0, 0, '100%', '100%', document.getElementById('div1').innerHTML)
|
||||
LODOP.ADD_PRINT_BARCODE(40, 100, 100, 100, 'qrcode', this.printData.hisId) // 设置条码位置、宽高、字体、值
|
||||
LODOP.ADD_PRINT_HTM(200, 0, '100%', '100%', document.getElementById('div2').innerHTML)
|
||||
LODOP.ADD_PRINT_HTM(230, 10, '100%', '100%', document.getElementById('div3').innerHTML)
|
||||
LODOP.ADD_PRINT_HTM(295, 10, '100%', '100%', document.getElementById('div4').innerHTML)
|
||||
},
|
||||
// 获取打印机
|
||||
getPrinter(LODOP, name) {
|
||||
const listCount = LODOP.GET_PRINTER_COUNT() // 当前打印设备数量
|
||||
for (let i = 0; i < listCount; i++) {
|
||||
if (LODOP.GET_PRINTER_NAME(i) === name) {
|
||||
return name
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="less">
|
||||
.printInjectCard {
|
||||
display: grid;
|
||||
width: 400px;
|
||||
grid-template-rows: 60px 205px 30px;
|
||||
border: solid #555 1px;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
@page {
|
||||
size: auto;
|
||||
margin: 32px;
|
||||
}
|
||||
</style>
|
||||
115
openhis-ui-vue3/src/components/Auto/printBills/wristBill.vue
Normal file
115
openhis-ui-vue3/src/components/Auto/printBills/wristBill.vue
Normal file
@@ -0,0 +1,115 @@
|
||||
<template>
|
||||
<div class="printWrist">
|
||||
<div id="div1" class="printView_content">
|
||||
<div style="margin: 1px;font-size: 12px">
|
||||
<span>姓名: </span>
|
||||
<span>{{ printData.patientName }}</span>
|
||||
<span style="position: absolute; left: 110px">病历号:</span>
|
||||
<span style="position: absolute; left: 155px">{{ printData.hisId }}</span>
|
||||
<span style="position: absolute; left: 250px">入院时间:</span>
|
||||
<span style="position: absolute; left: 305px">{{ printData.checkInWardTime? printData.checkInWardTime.substr(0,16):'' }}</span>
|
||||
</div>
|
||||
<div style="margin: 1px;font-size: 12px">
|
||||
<span>性别: </span>
|
||||
<span>{{ printData.gender? printData.gender.display:'' }}</span>
|
||||
<span style="position: absolute; left: 110px">科室:</span>
|
||||
<span style="position: absolute; left: 140px">{{ printData.dept }}</span>
|
||||
</div>
|
||||
<div style="margin-top: 2px;font-size: 12px">
|
||||
<span>床号: </span>
|
||||
<span>{{ printData.bedName }}</span>
|
||||
<span style="position: absolute; left: 110px">分级:</span>
|
||||
<span style="position: absolute; left: 140px">{{ printData.triageLevel }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="qrcode" ref="refQr" style="padding-top: 1px" />
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import QRCode from 'qrcodejs2'
|
||||
import { getLodop } from '../../../plugins/print/LodopFuncs'
|
||||
export default {
|
||||
name: 'WristPrint',
|
||||
data() {
|
||||
return {
|
||||
printData: {
|
||||
patientName: '',
|
||||
deptName: '',
|
||||
triageLevel: '',
|
||||
triageTime: '',
|
||||
hisId: '11111',
|
||||
lastId: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
updated() {
|
||||
this.initBarCode()
|
||||
},
|
||||
methods: {
|
||||
initBarCode() {
|
||||
this.$nextTick(() => {
|
||||
if (this.lastId !== this.printData.hisId) {
|
||||
new QRCode('qrcode', {
|
||||
text: this.printData.hisId,
|
||||
width: 40,
|
||||
height: 40,
|
||||
colorDark: '#000000',
|
||||
colorLight: '#ffffff',
|
||||
correctLevel: QRCode.CorrectLevel.H
|
||||
})
|
||||
}
|
||||
this.lastId = this.printData.hisId
|
||||
})
|
||||
},
|
||||
clear() {
|
||||
document.getElementById('qrcode').innerHTML = ''
|
||||
},
|
||||
execPrint(preview, printerName) {
|
||||
const LODOP = getLodop()
|
||||
const printer = this.getPrinter(LODOP, printerName)
|
||||
if (printer === null) {
|
||||
this.openMesBox('6', '没有找到打印机【' + printerName + '】')
|
||||
return
|
||||
}
|
||||
LODOP.PRINT_INIT('')
|
||||
LODOP.ADD_PRINT_HTM(30, 100, '100%', 100, document.getElementById('div1').innerHTML)
|
||||
LODOP.ADD_PRINT_HTM(30, 20, '100%', 40, document.getElementById('qrcode').innerHTML)
|
||||
LODOP.SET_PRINT_PAGESIZE(2, '25mm', '270mm', '') // 设置横向打印
|
||||
if (preview) {
|
||||
LODOP.PREVIEW() // 打印预览
|
||||
return
|
||||
}
|
||||
// LODOP.PREVIEW(); // 打印预览
|
||||
LODOP.PRINT() // 直接打印
|
||||
},
|
||||
// 获取打印机
|
||||
getPrinter(LODOP, name) {
|
||||
const listCount = LODOP.GET_PRINTER_COUNT() // 当前打印设备数量
|
||||
for (let i = 0; i < listCount; i++) {
|
||||
if (LODOP.GET_PRINTER_NAME(i) === name) {
|
||||
return name
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.printWrist {
|
||||
display: grid;
|
||||
width: 460px;
|
||||
grid-template-columns: 330px 40px;
|
||||
height: 45px;
|
||||
|
||||
.printView_content{
|
||||
display: grid;
|
||||
padding-top: 1px;
|
||||
padding-left: 60px;
|
||||
grid-template-rows: repeat(3,14px);
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
66
openhis-ui-vue3/src/components/Breadcrumb/index.vue
Normal file
66
openhis-ui-vue3/src/components/Breadcrumb/index.vue
Normal file
@@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<el-breadcrumb class="app-breadcrumb" separator="/">
|
||||
<transition-group name="breadcrumb">
|
||||
<el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path">
|
||||
<span v-if="item.redirect === 'noRedirect' || index == levelList.length - 1" class="no-redirect">{{ item.meta.title }}</span>
|
||||
<a v-else @click.prevent="handleLink(item)">{{ item.meta.title }}</a>
|
||||
</el-breadcrumb-item>
|
||||
</transition-group>
|
||||
</el-breadcrumb>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const levelList = ref([])
|
||||
|
||||
function getBreadcrumb() {
|
||||
// only show routes with meta.title
|
||||
let matched = route.matched.filter(item => item.meta && item.meta.title);
|
||||
const first = matched[0]
|
||||
// 判断是否为首页
|
||||
if (!isDashboard(first)) {
|
||||
matched = [{ path: '/index', meta: { title: '首页' } }].concat(matched)
|
||||
}
|
||||
|
||||
levelList.value = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)
|
||||
}
|
||||
function isDashboard(route) {
|
||||
const name = route && route.name
|
||||
if (!name) {
|
||||
return false
|
||||
}
|
||||
return name.trim() === 'Index'
|
||||
}
|
||||
function handleLink(item) {
|
||||
const { redirect, path } = item
|
||||
if (redirect) {
|
||||
router.push(redirect)
|
||||
return
|
||||
}
|
||||
router.push(path)
|
||||
}
|
||||
|
||||
watchEffect(() => {
|
||||
// if you go to the redirect page, do not update the breadcrumbs
|
||||
if (route.path.startsWith('/redirect/')) {
|
||||
return
|
||||
}
|
||||
getBreadcrumb()
|
||||
})
|
||||
getBreadcrumb();
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.app-breadcrumb.el-breadcrumb {
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
line-height: 50px;
|
||||
margin-left: 8px;
|
||||
|
||||
.no-redirect {
|
||||
color: #97a8be;
|
||||
cursor: text;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
174
openhis-ui-vue3/src/components/Crontab/day.vue
Normal file
174
openhis-ui-vue3/src/components/Crontab/day.vue
Normal file
@@ -0,0 +1,174 @@
|
||||
<template>
|
||||
<el-form>
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="1">
|
||||
日,允许的通配符[, - * ? / L W]
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="2">
|
||||
不指定
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="3">
|
||||
周期从
|
||||
<el-input-number v-model='cycle01' :min="1" :max="30" /> -
|
||||
<el-input-number v-model='cycle02' :min="cycle01 + 1" :max="31" /> 日
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="4">
|
||||
从
|
||||
<el-input-number v-model='average01' :min="1" :max="30" /> 号开始,每
|
||||
<el-input-number v-model='average02' :min="1" :max="31 - average01" /> 日执行一次
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="5">
|
||||
每月
|
||||
<el-input-number v-model='workday' :min="1" :max="31" /> 号最近的那个工作日
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="6">
|
||||
本月最后一天
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="7">
|
||||
指定
|
||||
<el-select clearable v-model="checkboxList" placeholder="可多选" multiple :multiple-limit="10">
|
||||
<el-option v-for="item in 31" :key="item" :label="item" :value="item" />
|
||||
</el-select>
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
<script setup>
|
||||
const emit = defineEmits(['update'])
|
||||
const props = defineProps({
|
||||
cron: {
|
||||
type: Object,
|
||||
default: {
|
||||
second: "*",
|
||||
min: "*",
|
||||
hour: "*",
|
||||
day: "*",
|
||||
month: "*",
|
||||
week: "?",
|
||||
year: "",
|
||||
}
|
||||
},
|
||||
check: {
|
||||
type: Function,
|
||||
default: () => {
|
||||
}
|
||||
}
|
||||
})
|
||||
const radioValue = ref(1)
|
||||
const cycle01 = ref(1)
|
||||
const cycle02 = ref(2)
|
||||
const average01 = ref(1)
|
||||
const average02 = ref(1)
|
||||
const workday = ref(1)
|
||||
const checkboxList = ref([])
|
||||
const checkCopy = ref([1])
|
||||
const cycleTotal = computed(() => {
|
||||
cycle01.value = props.check(cycle01.value, 1, 30)
|
||||
cycle02.value = props.check(cycle02.value, cycle01.value + 1, 31)
|
||||
return cycle01.value + '-' + cycle02.value
|
||||
})
|
||||
const averageTotal = computed(() => {
|
||||
average01.value = props.check(average01.value, 1, 30)
|
||||
average02.value = props.check(average02.value, 1, 31 - average01.value)
|
||||
return average01.value + '/' + average02.value
|
||||
})
|
||||
const workdayTotal = computed(() => {
|
||||
workday.value = props.check(workday.value, 1, 31)
|
||||
return workday.value + 'W'
|
||||
})
|
||||
const checkboxString = computed(() => {
|
||||
return checkboxList.value.join(',')
|
||||
})
|
||||
watch(() => props.cron.day, value => changeRadioValue(value))
|
||||
watch([radioValue, cycleTotal, averageTotal, workdayTotal, checkboxString], () => onRadioChange())
|
||||
function changeRadioValue(value) {
|
||||
if (value === "*") {
|
||||
radioValue.value = 1
|
||||
} else if (value === "?") {
|
||||
radioValue.value = 2
|
||||
} else if (value.indexOf("-") > -1) {
|
||||
const indexArr = value.split('-')
|
||||
cycle01.value = Number(indexArr[0])
|
||||
cycle02.value = Number(indexArr[1])
|
||||
radioValue.value = 3
|
||||
} else if (value.indexOf("/") > -1) {
|
||||
const indexArr = value.split('/')
|
||||
average01.value = Number(indexArr[0])
|
||||
average02.value = Number(indexArr[1])
|
||||
radioValue.value = 4
|
||||
} else if (value.indexOf("W") > -1) {
|
||||
const indexArr = value.split("W")
|
||||
workday.value = Number(indexArr[0])
|
||||
radioValue.value = 5
|
||||
} else if (value === "L") {
|
||||
radioValue.value = 6
|
||||
} else {
|
||||
checkboxList.value = [...new Set(value.split(',').map(item => Number(item)))]
|
||||
radioValue.value = 7
|
||||
}
|
||||
}
|
||||
// 单选按钮值变化时
|
||||
function onRadioChange() {
|
||||
if (radioValue.value === 2 && props.cron.week === '?') {
|
||||
emit('update', 'week', '*', 'day')
|
||||
}
|
||||
if (radioValue.value !== 2 && props.cron.week !== '?') {
|
||||
emit('update', 'week', '?', 'day')
|
||||
}
|
||||
switch (radioValue.value) {
|
||||
case 1:
|
||||
emit('update', 'day', '*', 'day')
|
||||
break
|
||||
case 2:
|
||||
emit('update', 'day', '?', 'day')
|
||||
break
|
||||
case 3:
|
||||
emit('update', 'day', cycleTotal.value, 'day')
|
||||
break
|
||||
case 4:
|
||||
emit('update', 'day', averageTotal.value, 'day')
|
||||
break
|
||||
case 5:
|
||||
emit('update', 'day', workdayTotal.value, 'day')
|
||||
break
|
||||
case 6:
|
||||
emit('update', 'day', 'L', 'day')
|
||||
break
|
||||
case 7:
|
||||
if (checkboxList.value.length === 0) {
|
||||
checkboxList.value.push(checkCopy.value[0])
|
||||
} else {
|
||||
checkCopy.value = checkboxList.value
|
||||
}
|
||||
emit('update', 'day', checkboxString.value, 'day')
|
||||
break
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-input-number--small, .el-select, .el-select--small {
|
||||
margin: 0 0.2rem;
|
||||
}
|
||||
.el-select, .el-select--small {
|
||||
width: 18.8rem;
|
||||
}
|
||||
</style>
|
||||
127
openhis-ui-vue3/src/components/Crontab/hour.vue
Normal file
127
openhis-ui-vue3/src/components/Crontab/hour.vue
Normal file
@@ -0,0 +1,127 @@
|
||||
<template>
|
||||
<el-form>
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="1">
|
||||
小时,允许的通配符[, - * /]
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="2">
|
||||
周期从
|
||||
<el-input-number v-model='cycle01' :min="0" :max="22" /> -
|
||||
<el-input-number v-model='cycle02' :min="cycle01 + 1" :max="23" /> 时
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="3">
|
||||
从
|
||||
<el-input-number v-model='average01' :min="0" :max="22" /> 时开始,每
|
||||
<el-input-number v-model='average02' :min="1" :max="23 - average01" /> 小时执行一次
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="4">
|
||||
指定
|
||||
<el-select clearable v-model="checkboxList" placeholder="可多选" multiple :multiple-limit="10">
|
||||
<el-option v-for="item in 24" :key="item" :label="item - 1" :value="item - 1" />
|
||||
</el-select>
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const emit = defineEmits(['update'])
|
||||
const props = defineProps({
|
||||
cron: {
|
||||
type: Object,
|
||||
default: {
|
||||
second: "*",
|
||||
min: "*",
|
||||
hour: "*",
|
||||
day: "*",
|
||||
month: "*",
|
||||
week: "?",
|
||||
year: "",
|
||||
}
|
||||
},
|
||||
check: {
|
||||
type: Function,
|
||||
default: () => {
|
||||
}
|
||||
}
|
||||
})
|
||||
const radioValue = ref(1)
|
||||
const cycle01 = ref(0)
|
||||
const cycle02 = ref(1)
|
||||
const average01 = ref(0)
|
||||
const average02 = ref(1)
|
||||
const checkboxList = ref([])
|
||||
const checkCopy = ref([0])
|
||||
const cycleTotal = computed(() => {
|
||||
cycle01.value = props.check(cycle01.value, 0, 22)
|
||||
cycle02.value = props.check(cycle02.value, cycle01.value + 1, 23)
|
||||
return cycle01.value + '-' + cycle02.value
|
||||
})
|
||||
const averageTotal = computed(() => {
|
||||
average01.value = props.check(average01.value, 0, 22)
|
||||
average02.value = props.check(average02.value, 1, 23 - average01.value)
|
||||
return average01.value + '/' + average02.value
|
||||
})
|
||||
const checkboxString = computed(() => {
|
||||
return checkboxList.value.join(',')
|
||||
})
|
||||
watch(() => props.cron.hour, value => changeRadioValue(value))
|
||||
watch([radioValue, cycleTotal, averageTotal, checkboxString], () => onRadioChange())
|
||||
function changeRadioValue(value) {
|
||||
if (value === '*') {
|
||||
radioValue.value = 1
|
||||
} else if (value.indexOf('-') > -1) {
|
||||
const indexArr = value.split('-')
|
||||
cycle01.value = Number(indexArr[0])
|
||||
cycle02.value = Number(indexArr[1])
|
||||
radioValue.value = 2
|
||||
} else if (value.indexOf('/') > -1) {
|
||||
const indexArr = value.split('/')
|
||||
average01.value = Number(indexArr[0])
|
||||
average02.value = Number(indexArr[1])
|
||||
radioValue.value = 3
|
||||
} else {
|
||||
checkboxList.value = [...new Set(value.split(',').map(item => Number(item)))]
|
||||
radioValue.value = 4
|
||||
}
|
||||
}
|
||||
function onRadioChange() {
|
||||
switch (radioValue.value) {
|
||||
case 1:
|
||||
emit('update', 'hour', '*', 'hour')
|
||||
break
|
||||
case 2:
|
||||
emit('update', 'hour', cycleTotal.value, 'hour')
|
||||
break
|
||||
case 3:
|
||||
emit('update', 'hour', averageTotal.value, 'hour')
|
||||
break
|
||||
case 4:
|
||||
if (checkboxList.value.length === 0) {
|
||||
checkboxList.value.push(checkCopy.value[0])
|
||||
} else {
|
||||
checkCopy.value = checkboxList.value
|
||||
}
|
||||
emit('update', 'hour', checkboxString.value, 'hour')
|
||||
break
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-input-number--small, .el-select, .el-select--small {
|
||||
margin: 0 0.2rem;
|
||||
}
|
||||
.el-select, .el-select--small {
|
||||
width: 18.8rem;
|
||||
}
|
||||
</style>
|
||||
310
openhis-ui-vue3/src/components/Crontab/index.vue
Normal file
310
openhis-ui-vue3/src/components/Crontab/index.vue
Normal file
@@ -0,0 +1,310 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-tabs type="border-card">
|
||||
<el-tab-pane label="秒" v-if="shouldHide('second')">
|
||||
<CrontabSecond
|
||||
@update="updateCrontabValue"
|
||||
:check="checkNumber"
|
||||
:cron="crontabValueObj"
|
||||
ref="cronsecond"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="分钟" v-if="shouldHide('min')">
|
||||
<CrontabMin
|
||||
@update="updateCrontabValue"
|
||||
:check="checkNumber"
|
||||
:cron="crontabValueObj"
|
||||
ref="cronmin"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="小时" v-if="shouldHide('hour')">
|
||||
<CrontabHour
|
||||
@update="updateCrontabValue"
|
||||
:check="checkNumber"
|
||||
:cron="crontabValueObj"
|
||||
ref="cronhour"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="日" v-if="shouldHide('day')">
|
||||
<CrontabDay
|
||||
@update="updateCrontabValue"
|
||||
:check="checkNumber"
|
||||
:cron="crontabValueObj"
|
||||
ref="cronday"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="月" v-if="shouldHide('month')">
|
||||
<CrontabMonth
|
||||
@update="updateCrontabValue"
|
||||
:check="checkNumber"
|
||||
:cron="crontabValueObj"
|
||||
ref="cronmonth"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="周" v-if="shouldHide('week')">
|
||||
<CrontabWeek
|
||||
@update="updateCrontabValue"
|
||||
:check="checkNumber"
|
||||
:cron="crontabValueObj"
|
||||
ref="cronweek"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="年" v-if="shouldHide('year')">
|
||||
<CrontabYear
|
||||
@update="updateCrontabValue"
|
||||
:check="checkNumber"
|
||||
:cron="crontabValueObj"
|
||||
ref="cronyear"
|
||||
/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<div class="popup-main">
|
||||
<div class="popup-result">
|
||||
<p class="title">时间表达式</p>
|
||||
<table>
|
||||
<thead>
|
||||
<th v-for="item of tabTitles" :key="item">{{item}}</th>
|
||||
<th>Cron 表达式</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
<td>
|
||||
<span v-if="crontabValueObj.second.length < 10">{{crontabValueObj.second}}</span>
|
||||
<el-tooltip v-else :content="crontabValueObj.second" placement="top"><span>{{crontabValueObj.second}}</span></el-tooltip>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="crontabValueObj.min.length < 10">{{crontabValueObj.min}}</span>
|
||||
<el-tooltip v-else :content="crontabValueObj.min" placement="top"><span>{{crontabValueObj.min}}</span></el-tooltip>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="crontabValueObj.hour.length < 10">{{crontabValueObj.hour}}</span>
|
||||
<el-tooltip v-else :content="crontabValueObj.hour" placement="top"><span>{{crontabValueObj.hour}}</span></el-tooltip>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="crontabValueObj.day.length < 10">{{crontabValueObj.day}}</span>
|
||||
<el-tooltip v-else :content="crontabValueObj.day" placement="top"><span>{{crontabValueObj.day}}</span></el-tooltip>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="crontabValueObj.month.length < 10">{{crontabValueObj.month}}</span>
|
||||
<el-tooltip v-else :content="crontabValueObj.month" placement="top"><span>{{crontabValueObj.month}}</span></el-tooltip>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="crontabValueObj.week.length < 10">{{crontabValueObj.week}}</span>
|
||||
<el-tooltip v-else :content="crontabValueObj.week" placement="top"><span>{{crontabValueObj.week}}</span></el-tooltip>
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="crontabValueObj.year.length < 10">{{crontabValueObj.year}}</span>
|
||||
<el-tooltip v-else :content="crontabValueObj.year" placement="top"><span>{{crontabValueObj.year}}</span></el-tooltip>
|
||||
</td>
|
||||
<td class="result">
|
||||
<span v-if="crontabValueString.length < 90">{{crontabValueString}}</span>
|
||||
<el-tooltip v-else :content="crontabValueString" placement="top"><span>{{crontabValueString}}</span></el-tooltip>
|
||||
</td>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<CrontabResult :ex="crontabValueString"></CrontabResult>
|
||||
|
||||
<div class="pop_btn">
|
||||
<el-button type="primary" @click="submitFill">确定</el-button>
|
||||
<el-button type="warning" @click="clearCron">重置</el-button>
|
||||
<el-button @click="hidePopup">取消</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import CrontabSecond from "./second.vue"
|
||||
import CrontabMin from "./min.vue"
|
||||
import CrontabHour from "./hour.vue"
|
||||
import CrontabDay from "./day.vue"
|
||||
import CrontabMonth from "./month.vue"
|
||||
import CrontabWeek from "./week.vue"
|
||||
import CrontabYear from "./year.vue"
|
||||
import CrontabResult from "./result.vue"
|
||||
const { proxy } = getCurrentInstance()
|
||||
const emit = defineEmits(['hide', 'fill'])
|
||||
const props = defineProps({
|
||||
hideComponent: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
expression: {
|
||||
type: String,
|
||||
default: ""
|
||||
}
|
||||
})
|
||||
const tabTitles = ref(["秒", "分钟", "小时", "日", "月", "周", "年"])
|
||||
const tabActive = ref(0)
|
||||
const hideComponent = ref([])
|
||||
const expression = ref('')
|
||||
const crontabValueObj = ref({
|
||||
second: "*",
|
||||
min: "*",
|
||||
hour: "*",
|
||||
day: "*",
|
||||
month: "*",
|
||||
week: "?",
|
||||
year: "",
|
||||
})
|
||||
const crontabValueString = computed(() => {
|
||||
const obj = crontabValueObj.value
|
||||
return obj.second
|
||||
+ " "
|
||||
+ obj.min
|
||||
+ " "
|
||||
+ obj.hour
|
||||
+ " "
|
||||
+ obj.day
|
||||
+ " "
|
||||
+ obj.month
|
||||
+ " "
|
||||
+ obj.week
|
||||
+ (obj.year === "" ? "" : " " + obj.year)
|
||||
})
|
||||
watch(expression, () => resolveExp())
|
||||
function shouldHide(key) {
|
||||
return !(hideComponent.value && hideComponent.value.includes(key))
|
||||
}
|
||||
function resolveExp() {
|
||||
// 反解析 表达式
|
||||
if (expression.value) {
|
||||
const arr = expression.value.split(/\s+/)
|
||||
if (arr.length >= 6) {
|
||||
//6 位以上是合法表达式
|
||||
let obj = {
|
||||
second: arr[0],
|
||||
min: arr[1],
|
||||
hour: arr[2],
|
||||
day: arr[3],
|
||||
month: arr[4],
|
||||
week: arr[5],
|
||||
year: arr[6] ? arr[6] : ""
|
||||
}
|
||||
crontabValueObj.value = {
|
||||
...obj,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 没有传入的表达式 则还原
|
||||
clearCron()
|
||||
}
|
||||
}
|
||||
// tab切换值
|
||||
function tabCheck(index) {
|
||||
tabActive.value = index
|
||||
}
|
||||
// 由子组件触发,更改表达式组成的字段值
|
||||
function updateCrontabValue(name, value, from) {
|
||||
crontabValueObj.value[name] = value
|
||||
}
|
||||
// 表单选项的子组件校验数字格式(通过-props传递)
|
||||
function checkNumber(value, minLimit, maxLimit) {
|
||||
// 检查必须为整数
|
||||
value = Math.floor(value)
|
||||
if (value < minLimit) {
|
||||
value = minLimit
|
||||
} else if (value > maxLimit) {
|
||||
value = maxLimit
|
||||
}
|
||||
return value
|
||||
}
|
||||
// 隐藏弹窗
|
||||
function hidePopup() {
|
||||
emit("hide")
|
||||
}
|
||||
// 填充表达式
|
||||
function submitFill() {
|
||||
emit("fill", crontabValueString.value)
|
||||
hidePopup()
|
||||
}
|
||||
function clearCron() {
|
||||
// 还原选择项
|
||||
crontabValueObj.value = {
|
||||
second: "*",
|
||||
min: "*",
|
||||
hour: "*",
|
||||
day: "*",
|
||||
month: "*",
|
||||
week: "?",
|
||||
year: "",
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
expression.value = props.expression
|
||||
hideComponent.value = props.hideComponent
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.pop_btn {
|
||||
text-align: center;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.popup-main {
|
||||
position: relative;
|
||||
margin: 10px auto;
|
||||
background: #fff;
|
||||
border-radius: 5px;
|
||||
font-size: 12px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.popup-title {
|
||||
overflow: hidden;
|
||||
line-height: 34px;
|
||||
padding-top: 6px;
|
||||
background: #f2f2f2;
|
||||
}
|
||||
.popup-result {
|
||||
box-sizing: border-box;
|
||||
line-height: 24px;
|
||||
margin: 25px auto;
|
||||
padding: 15px 10px 10px;
|
||||
border: 1px solid #ccc;
|
||||
position: relative;
|
||||
}
|
||||
.popup-result .title {
|
||||
position: absolute;
|
||||
top: -28px;
|
||||
left: 50%;
|
||||
width: 140px;
|
||||
font-size: 14px;
|
||||
margin-left: -70px;
|
||||
text-align: center;
|
||||
line-height: 30px;
|
||||
background: #fff;
|
||||
}
|
||||
.popup-result table {
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.popup-result table td:not(.result) {
|
||||
width: 3.5rem;
|
||||
min-width: 3.5rem;
|
||||
max-width: 3.5rem;
|
||||
}
|
||||
.popup-result table span {
|
||||
display: block;
|
||||
width: 100%;
|
||||
font-family: arial;
|
||||
line-height: 30px;
|
||||
height: 30px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
border: 1px solid #e8e8e8;
|
||||
}
|
||||
.popup-result-scroll {
|
||||
font-size: 12px;
|
||||
line-height: 24px;
|
||||
height: 10em;
|
||||
overflow-y: auto;
|
||||
}
|
||||
</style>
|
||||
126
openhis-ui-vue3/src/components/Crontab/min.vue
Normal file
126
openhis-ui-vue3/src/components/Crontab/min.vue
Normal file
@@ -0,0 +1,126 @@
|
||||
<template>
|
||||
<el-form>
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="1">
|
||||
分钟,允许的通配符[, - * /]
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="2">
|
||||
周期从
|
||||
<el-input-number v-model='cycle01' :min="0" :max="58" /> -
|
||||
<el-input-number v-model='cycle02' :min="cycle01 + 1" :max="59" /> 分钟
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="3">
|
||||
从
|
||||
<el-input-number v-model='average01' :min="0" :max="58" /> 分钟开始, 每
|
||||
<el-input-number v-model='average02' :min="1" :max="59 - average01" /> 分钟执行一次
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="4">
|
||||
指定
|
||||
<el-select clearable v-model="checkboxList" placeholder="可多选" multiple :multiple-limit="10">
|
||||
<el-option v-for="item in 60" :key="item" :label="item - 1" :value="item - 1" />
|
||||
</el-select>
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
<script setup>
|
||||
const emit = defineEmits(['update'])
|
||||
const props = defineProps({
|
||||
cron: {
|
||||
type: Object,
|
||||
default: {
|
||||
second: "*",
|
||||
min: "*",
|
||||
hour: "*",
|
||||
day: "*",
|
||||
month: "*",
|
||||
week: "?",
|
||||
year: "",
|
||||
}
|
||||
},
|
||||
check: {
|
||||
type: Function,
|
||||
default: () => {
|
||||
}
|
||||
}
|
||||
})
|
||||
const radioValue = ref(1)
|
||||
const cycle01 = ref(0)
|
||||
const cycle02 = ref(1)
|
||||
const average01 = ref(0)
|
||||
const average02 = ref(1)
|
||||
const checkboxList = ref([])
|
||||
const checkCopy = ref([0])
|
||||
const cycleTotal = computed(() => {
|
||||
cycle01.value = props.check(cycle01.value, 0, 58)
|
||||
cycle02.value = props.check(cycle02.value, cycle01.value + 1, 59)
|
||||
return cycle01.value + '-' + cycle02.value
|
||||
})
|
||||
const averageTotal = computed(() => {
|
||||
average01.value = props.check(average01.value, 0, 58)
|
||||
average02.value = props.check(average02.value, 1, 59 - average01.value)
|
||||
return average01.value + '/' + average02.value
|
||||
})
|
||||
const checkboxString = computed(() => {
|
||||
return checkboxList.value.join(',')
|
||||
})
|
||||
watch(() => props.cron.min, value => changeRadioValue(value))
|
||||
watch([radioValue, cycleTotal, averageTotal, checkboxString], () => onRadioChange())
|
||||
function changeRadioValue(value) {
|
||||
if (value === '*') {
|
||||
radioValue.value = 1
|
||||
} else if (value.indexOf('-') > -1) {
|
||||
const indexArr = value.split('-')
|
||||
cycle01.value = Number(indexArr[0])
|
||||
cycle02.value = Number(indexArr[1])
|
||||
radioValue.value = 2
|
||||
} else if (value.indexOf('/') > -1) {
|
||||
const indexArr = value.split('/')
|
||||
average01.value = Number(indexArr[0])
|
||||
average02.value = Number(indexArr[1])
|
||||
radioValue.value = 3
|
||||
} else {
|
||||
checkboxList.value = [...new Set(value.split(',').map(item => Number(item)))]
|
||||
radioValue.value = 4
|
||||
}
|
||||
}
|
||||
function onRadioChange() {
|
||||
switch (radioValue.value) {
|
||||
case 1:
|
||||
emit('update', 'min', '*', 'min')
|
||||
break
|
||||
case 2:
|
||||
emit('update', 'min', cycleTotal.value, 'min')
|
||||
break
|
||||
case 3:
|
||||
emit('update', 'min', averageTotal.value, 'min')
|
||||
break
|
||||
case 4:
|
||||
if (checkboxList.value.length === 0) {
|
||||
checkboxList.value.push(checkCopy.value[0])
|
||||
} else {
|
||||
checkCopy.value = checkboxList.value
|
||||
}
|
||||
emit('update', 'min', checkboxString.value, 'min')
|
||||
break
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-input-number--small, .el-select, .el-select--small {
|
||||
margin: 0 0.2rem;
|
||||
}
|
||||
.el-select, .el-select--small {
|
||||
width: 19.8rem;
|
||||
}
|
||||
</style>
|
||||
141
openhis-ui-vue3/src/components/Crontab/month.vue
Normal file
141
openhis-ui-vue3/src/components/Crontab/month.vue
Normal file
@@ -0,0 +1,141 @@
|
||||
<template>
|
||||
<el-form>
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="1">
|
||||
月,允许的通配符[, - * /]
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="2">
|
||||
周期从
|
||||
<el-input-number v-model='cycle01' :min="1" :max="11" /> -
|
||||
<el-input-number v-model='cycle02' :min="cycle01 + 1" :max="12" /> 月
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="3">
|
||||
从
|
||||
<el-input-number v-model='average01' :min="1" :max="11" /> 月开始,每
|
||||
<el-input-number v-model='average02' :min="1" :max="12 - average01" /> 月月执行一次
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="4">
|
||||
指定
|
||||
<el-select clearable v-model="checkboxList" placeholder="可多选" multiple :multiple-limit="8">
|
||||
<el-option v-for="item in monthList" :key="item.key" :label="item.value" :value="item.key" />
|
||||
</el-select>
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const emit = defineEmits(['update'])
|
||||
const props = defineProps({
|
||||
cron: {
|
||||
type: Object,
|
||||
default: {
|
||||
second: "*",
|
||||
min: "*",
|
||||
hour: "*",
|
||||
day: "*",
|
||||
month: "*",
|
||||
week: "?",
|
||||
year: "",
|
||||
}
|
||||
},
|
||||
check: {
|
||||
type: Function,
|
||||
default: () => {
|
||||
}
|
||||
}
|
||||
})
|
||||
const radioValue = ref(1)
|
||||
const cycle01 = ref(1)
|
||||
const cycle02 = ref(2)
|
||||
const average01 = ref(1)
|
||||
const average02 = ref(1)
|
||||
const checkboxList = ref([])
|
||||
const checkCopy = ref([1])
|
||||
const monthList = ref([
|
||||
{key: 1, value: '一月'},
|
||||
{key: 2, value: '二月'},
|
||||
{key: 3, value: '三月'},
|
||||
{key: 4, value: '四月'},
|
||||
{key: 5, value: '五月'},
|
||||
{key: 6, value: '六月'},
|
||||
{key: 7, value: '七月'},
|
||||
{key: 8, value: '八月'},
|
||||
{key: 9, value: '九月'},
|
||||
{key: 10, value: '十月'},
|
||||
{key: 11, value: '十一月'},
|
||||
{key: 12, value: '十二月'}
|
||||
])
|
||||
const cycleTotal = computed(() => {
|
||||
cycle01.value = props.check(cycle01.value, 1, 11)
|
||||
cycle02.value = props.check(cycle02.value, cycle01.value + 1, 12)
|
||||
return cycle01.value + '-' + cycle02.value
|
||||
})
|
||||
const averageTotal = computed(() => {
|
||||
average01.value = props.check(average01.value, 1, 11)
|
||||
average02.value = props.check(average02.value, 1, 12 - average01.value)
|
||||
return average01.value + '/' + average02.value
|
||||
})
|
||||
const checkboxString = computed(() => {
|
||||
return checkboxList.value.join(',')
|
||||
})
|
||||
watch(() => props.cron.month, value => changeRadioValue(value))
|
||||
watch([radioValue, cycleTotal, averageTotal, checkboxString], () => onRadioChange())
|
||||
function changeRadioValue(value) {
|
||||
if (value === '*') {
|
||||
radioValue.value = 1
|
||||
} else if (value.indexOf('-') > -1) {
|
||||
const indexArr = value.split('-')
|
||||
cycle01.value = Number(indexArr[0])
|
||||
cycle02.value = Number(indexArr[1])
|
||||
radioValue.value = 2
|
||||
} else if (value.indexOf('/') > -1) {
|
||||
const indexArr = value.split('/')
|
||||
average01.value = Number(indexArr[0])
|
||||
average02.value = Number(indexArr[1])
|
||||
radioValue.value = 3
|
||||
} else {
|
||||
checkboxList.value = [...new Set(value.split(',').map(item => Number(item)))]
|
||||
radioValue.value = 4
|
||||
}
|
||||
}
|
||||
function onRadioChange() {
|
||||
switch (radioValue.value) {
|
||||
case 1:
|
||||
emit('update', 'month', '*', 'month')
|
||||
break
|
||||
case 2:
|
||||
emit('update', 'month', cycleTotal.value, 'month')
|
||||
break
|
||||
case 3:
|
||||
emit('update', 'month', averageTotal.value, 'month')
|
||||
break
|
||||
case 4:
|
||||
if (checkboxList.value.length === 0) {
|
||||
checkboxList.value.push(checkCopy.value[0])
|
||||
} else {
|
||||
checkCopy.value = checkboxList.value
|
||||
}
|
||||
emit('update', 'month', checkboxString.value, 'month')
|
||||
break
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-input-number--small, .el-select, .el-select--small {
|
||||
margin: 0 0.2rem;
|
||||
}
|
||||
.el-select, .el-select--small {
|
||||
width: 18.8rem;
|
||||
}
|
||||
</style>
|
||||
540
openhis-ui-vue3/src/components/Crontab/result.vue
Normal file
540
openhis-ui-vue3/src/components/Crontab/result.vue
Normal file
@@ -0,0 +1,540 @@
|
||||
<template>
|
||||
<div class="popup-result">
|
||||
<p class="title">最近5次运行时间</p>
|
||||
<ul class="popup-result-scroll">
|
||||
<template v-if='isShow'>
|
||||
<li v-for='item in resultList' :key="item">{{item}}</li>
|
||||
</template>
|
||||
<li v-else>计算结果中...</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
ex: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
const dayRule = ref('')
|
||||
const dayRuleSup = ref('')
|
||||
const dateArr = ref([])
|
||||
const resultList = ref([])
|
||||
const isShow = ref(false)
|
||||
watch(() => props.ex, () => expressionChange())
|
||||
// 表达式值变化时,开始去计算结果
|
||||
function expressionChange() {
|
||||
// 计算开始-隐藏结果
|
||||
isShow.value = false;
|
||||
// 获取规则数组[0秒、1分、2时、3日、4月、5星期、6年]
|
||||
let ruleArr = props.ex.split(' ');
|
||||
// 用于记录进入循环的次数
|
||||
let nums = 0;
|
||||
// 用于暂时存符号时间规则结果的数组
|
||||
let resultArr = [];
|
||||
// 获取当前时间精确至[年、月、日、时、分、秒]
|
||||
let nTime = new Date();
|
||||
let nYear = nTime.getFullYear();
|
||||
let nMonth = nTime.getMonth() + 1;
|
||||
let nDay = nTime.getDate();
|
||||
let nHour = nTime.getHours();
|
||||
let nMin = nTime.getMinutes();
|
||||
let nSecond = nTime.getSeconds();
|
||||
// 根据规则获取到近100年可能年数组、月数组等等
|
||||
getSecondArr(ruleArr[0]);
|
||||
getMinArr(ruleArr[1]);
|
||||
getHourArr(ruleArr[2]);
|
||||
getDayArr(ruleArr[3]);
|
||||
getMonthArr(ruleArr[4]);
|
||||
getWeekArr(ruleArr[5]);
|
||||
getYearArr(ruleArr[6], nYear);
|
||||
// 将获取到的数组赋值-方便使用
|
||||
let sDate = dateArr.value[0];
|
||||
let mDate = dateArr.value[1];
|
||||
let hDate = dateArr.value[2];
|
||||
let DDate = dateArr.value[3];
|
||||
let MDate = dateArr.value[4];
|
||||
let YDate = dateArr.value[5];
|
||||
// 获取当前时间在数组中的索引
|
||||
let sIdx = getIndex(sDate, nSecond);
|
||||
let mIdx = getIndex(mDate, nMin);
|
||||
let hIdx = getIndex(hDate, nHour);
|
||||
let DIdx = getIndex(DDate, nDay);
|
||||
let MIdx = getIndex(MDate, nMonth);
|
||||
let YIdx = getIndex(YDate, nYear);
|
||||
// 重置月日时分秒的函数(后面用的比较多)
|
||||
const resetSecond = function () {
|
||||
sIdx = 0;
|
||||
nSecond = sDate[sIdx]
|
||||
}
|
||||
const resetMin = function () {
|
||||
mIdx = 0;
|
||||
nMin = mDate[mIdx]
|
||||
resetSecond();
|
||||
}
|
||||
const resetHour = function () {
|
||||
hIdx = 0;
|
||||
nHour = hDate[hIdx]
|
||||
resetMin();
|
||||
}
|
||||
const resetDay = function () {
|
||||
DIdx = 0;
|
||||
nDay = DDate[DIdx]
|
||||
resetHour();
|
||||
}
|
||||
const resetMonth = function () {
|
||||
MIdx = 0;
|
||||
nMonth = MDate[MIdx]
|
||||
resetDay();
|
||||
}
|
||||
// 如果当前年份不为数组中当前值
|
||||
if (nYear !== YDate[YIdx]) {
|
||||
resetMonth();
|
||||
}
|
||||
// 如果当前月份不为数组中当前值
|
||||
if (nMonth !== MDate[MIdx]) {
|
||||
resetDay();
|
||||
}
|
||||
// 如果当前“日”不为数组中当前值
|
||||
if (nDay !== DDate[DIdx]) {
|
||||
resetHour();
|
||||
}
|
||||
// 如果当前“时”不为数组中当前值
|
||||
if (nHour !== hDate[hIdx]) {
|
||||
resetMin();
|
||||
}
|
||||
// 如果当前“分”不为数组中当前值
|
||||
if (nMin !== mDate[mIdx]) {
|
||||
resetSecond();
|
||||
}
|
||||
// 循环年份数组
|
||||
goYear: for (let Yi = YIdx; Yi < YDate.length; Yi++) {
|
||||
let YY = YDate[Yi];
|
||||
// 如果到达最大值时
|
||||
if (nMonth > MDate[MDate.length - 1]) {
|
||||
resetMonth();
|
||||
continue;
|
||||
}
|
||||
// 循环月份数组
|
||||
goMonth: for (let Mi = MIdx; Mi < MDate.length; Mi++) {
|
||||
// 赋值、方便后面运算
|
||||
let MM = MDate[Mi];
|
||||
MM = MM < 10 ? '0' + MM : MM;
|
||||
// 如果到达最大值时
|
||||
if (nDay > DDate[DDate.length - 1]) {
|
||||
resetDay();
|
||||
if (Mi === MDate.length - 1) {
|
||||
resetMonth();
|
||||
continue goYear;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// 循环日期数组
|
||||
goDay: for (let Di = DIdx; Di < DDate.length; Di++) {
|
||||
// 赋值、方便后面运算
|
||||
let DD = DDate[Di];
|
||||
let thisDD = DD < 10 ? '0' + DD : DD;
|
||||
// 如果到达最大值时
|
||||
if (nHour > hDate[hDate.length - 1]) {
|
||||
resetHour();
|
||||
if (Di === DDate.length - 1) {
|
||||
resetDay();
|
||||
if (Mi === MDate.length - 1) {
|
||||
resetMonth();
|
||||
continue goYear;
|
||||
}
|
||||
continue goMonth;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// 判断日期的合法性,不合法的话也是跳出当前循环
|
||||
if (checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true && dayRule.value !== 'workDay' && dayRule.value !== 'lastWeek' && dayRule.value !== 'lastDay') {
|
||||
resetDay();
|
||||
continue goMonth;
|
||||
}
|
||||
// 如果日期规则中有值时
|
||||
if (dayRule.value === 'lastDay') {
|
||||
// 如果不是合法日期则需要将前将日期调到合法日期即月末最后一天
|
||||
if (checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
|
||||
while (DD > 0 && checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
|
||||
DD--;
|
||||
thisDD = DD < 10 ? '0' + DD : DD;
|
||||
}
|
||||
}
|
||||
} else if (dayRule.value === 'workDay') {
|
||||
// 校验并调整如果是2月30号这种日期传进来时需调整至正常月底
|
||||
if (checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
|
||||
while (DD > 0 && checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
|
||||
DD--;
|
||||
thisDD = DD < 10 ? '0' + DD : DD;
|
||||
}
|
||||
}
|
||||
// 获取达到条件的日期是星期X
|
||||
let thisWeek = formatDate(new Date(YY + '-' + MM + '-' + thisDD + ' 00:00:00'), 'week');
|
||||
// 当星期日时
|
||||
if (thisWeek === 1) {
|
||||
// 先找下一个日,并判断是否为月底
|
||||
DD++;
|
||||
thisDD = DD < 10 ? '0' + DD : DD;
|
||||
// 判断下一日已经不是合法日期
|
||||
if (checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
|
||||
DD -= 3;
|
||||
}
|
||||
} else if (thisWeek === 7) {
|
||||
// 当星期6时只需判断不是1号就可进行操作
|
||||
if (dayRuleSup.value !== 1) {
|
||||
DD--;
|
||||
} else {
|
||||
DD += 2;
|
||||
}
|
||||
}
|
||||
} else if (dayRule.value === 'weekDay') {
|
||||
// 如果指定了是星期几
|
||||
// 获取当前日期是属于星期几
|
||||
let thisWeek = formatDate(new Date(YY + '-' + MM + '-' + DD + ' 00:00:00'), 'week');
|
||||
// 校验当前星期是否在星期池(dayRuleSup)中
|
||||
if (dayRuleSup.value.indexOf(thisWeek) < 0) {
|
||||
// 如果到达最大值时
|
||||
if (Di === DDate.length - 1) {
|
||||
resetDay();
|
||||
if (Mi === MDate.length - 1) {
|
||||
resetMonth();
|
||||
continue goYear;
|
||||
}
|
||||
continue goMonth;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
} else if (dayRule.value === 'assWeek') {
|
||||
// 如果指定了是第几周的星期几
|
||||
// 获取每月1号是属于星期几
|
||||
let thisWeek = formatDate(new Date(YY + '-' + MM + '-' + DD + ' 00:00:00'), 'week');
|
||||
if (dayRuleSup.value[1] >= thisWeek) {
|
||||
DD = (dayRuleSup.value[0] - 1) * 7 + dayRuleSup.value[1] - thisWeek + 1;
|
||||
} else {
|
||||
DD = dayRuleSup.value[0] * 7 + dayRuleSup.value[1] - thisWeek + 1;
|
||||
}
|
||||
} else if (dayRule.value === 'lastWeek') {
|
||||
// 如果指定了每月最后一个星期几
|
||||
// 校验并调整如果是2月30号这种日期传进来时需调整至正常月底
|
||||
if (checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
|
||||
while (DD > 0 && checkDate(YY + '-' + MM + '-' + thisDD + ' 00:00:00') !== true) {
|
||||
DD--;
|
||||
thisDD = DD < 10 ? '0' + DD : DD;
|
||||
}
|
||||
}
|
||||
// 获取月末最后一天是星期几
|
||||
let thisWeek = formatDate(new Date(YY + '-' + MM + '-' + thisDD + ' 00:00:00'), 'week');
|
||||
// 找到要求中最近的那个星期几
|
||||
if (dayRuleSup.value < thisWeek) {
|
||||
DD -= thisWeek - dayRuleSup.value;
|
||||
} else if (dayRuleSup.value > thisWeek) {
|
||||
DD -= 7 - (dayRuleSup.value - thisWeek)
|
||||
}
|
||||
}
|
||||
// 判断时间值是否小于10置换成“05”这种格式
|
||||
DD = DD < 10 ? '0' + DD : DD;
|
||||
// 循环“时”数组
|
||||
goHour: for (let hi = hIdx; hi < hDate.length; hi++) {
|
||||
let hh = hDate[hi] < 10 ? '0' + hDate[hi] : hDate[hi]
|
||||
// 如果到达最大值时
|
||||
if (nMin > mDate[mDate.length - 1]) {
|
||||
resetMin();
|
||||
if (hi === hDate.length - 1) {
|
||||
resetHour();
|
||||
if (Di === DDate.length - 1) {
|
||||
resetDay();
|
||||
if (Mi === MDate.length - 1) {
|
||||
resetMonth();
|
||||
continue goYear;
|
||||
}
|
||||
continue goMonth;
|
||||
}
|
||||
continue goDay;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// 循环"分"数组
|
||||
goMin: for (let mi = mIdx; mi < mDate.length; mi++) {
|
||||
let mm = mDate[mi] < 10 ? '0' + mDate[mi] : mDate[mi];
|
||||
// 如果到达最大值时
|
||||
if (nSecond > sDate[sDate.length - 1]) {
|
||||
resetSecond();
|
||||
if (mi === mDate.length - 1) {
|
||||
resetMin();
|
||||
if (hi === hDate.length - 1) {
|
||||
resetHour();
|
||||
if (Di === DDate.length - 1) {
|
||||
resetDay();
|
||||
if (Mi === MDate.length - 1) {
|
||||
resetMonth();
|
||||
continue goYear;
|
||||
}
|
||||
continue goMonth;
|
||||
}
|
||||
continue goDay;
|
||||
}
|
||||
continue goHour;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// 循环"秒"数组
|
||||
goSecond: for (let si = sIdx; si <= sDate.length - 1; si++) {
|
||||
let ss = sDate[si] < 10 ? '0' + sDate[si] : sDate[si];
|
||||
// 添加当前时间(时间合法性在日期循环时已经判断)
|
||||
if (MM !== '00' && DD !== '00') {
|
||||
resultArr.push(YY + '-' + MM + '-' + DD + ' ' + hh + ':' + mm + ':' + ss)
|
||||
nums++;
|
||||
}
|
||||
// 如果条数满了就退出循环
|
||||
if (nums === 5) break goYear;
|
||||
// 如果到达最大值时
|
||||
if (si === sDate.length - 1) {
|
||||
resetSecond();
|
||||
if (mi === mDate.length - 1) {
|
||||
resetMin();
|
||||
if (hi === hDate.length - 1) {
|
||||
resetHour();
|
||||
if (Di === DDate.length - 1) {
|
||||
resetDay();
|
||||
if (Mi === MDate.length - 1) {
|
||||
resetMonth();
|
||||
continue goYear;
|
||||
}
|
||||
continue goMonth;
|
||||
}
|
||||
continue goDay;
|
||||
}
|
||||
continue goHour;
|
||||
}
|
||||
continue goMin;
|
||||
}
|
||||
} //goSecond
|
||||
} //goMin
|
||||
}//goHour
|
||||
}//goDay
|
||||
}//goMonth
|
||||
}
|
||||
// 判断100年内的结果条数
|
||||
if (resultArr.length === 0) {
|
||||
resultList.value = ['没有达到条件的结果!'];
|
||||
} else {
|
||||
resultList.value = resultArr;
|
||||
if (resultArr.length !== 5) {
|
||||
resultList.value.push('最近100年内只有上面' + resultArr.length + '条结果!')
|
||||
}
|
||||
}
|
||||
// 计算完成-显示结果
|
||||
isShow.value = true;
|
||||
}
|
||||
// 用于计算某位数字在数组中的索引
|
||||
function getIndex(arr, value) {
|
||||
if (value <= arr[0] || value > arr[arr.length - 1]) {
|
||||
return 0;
|
||||
} else {
|
||||
for (let i = 0; i < arr.length - 1; i++) {
|
||||
if (value > arr[i] && value <= arr[i + 1]) {
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 获取"年"数组
|
||||
function getYearArr(rule, year) {
|
||||
dateArr.value[5] = getOrderArr(year, year + 100);
|
||||
if (rule !== undefined) {
|
||||
if (rule.indexOf('-') >= 0) {
|
||||
dateArr.value[5] = getCycleArr(rule, year + 100, false)
|
||||
} else if (rule.indexOf('/') >= 0) {
|
||||
dateArr.value[5] = getAverageArr(rule, year + 100)
|
||||
} else if (rule !== '*') {
|
||||
dateArr.value[5] = getAssignArr(rule)
|
||||
}
|
||||
}
|
||||
}
|
||||
// 获取"月"数组
|
||||
function getMonthArr(rule) {
|
||||
dateArr.value[4] = getOrderArr(1, 12);
|
||||
if (rule.indexOf('-') >= 0) {
|
||||
dateArr.value[4] = getCycleArr(rule, 12, false)
|
||||
} else if (rule.indexOf('/') >= 0) {
|
||||
dateArr.value[4] = getAverageArr(rule, 12)
|
||||
} else if (rule !== '*') {
|
||||
dateArr.value[4] = getAssignArr(rule)
|
||||
}
|
||||
}
|
||||
// 获取"日"数组-主要为日期规则
|
||||
function getWeekArr(rule) {
|
||||
// 只有当日期规则的两个值均为“”时则表达日期是有选项的
|
||||
if (dayRule.value === '' && dayRuleSup.value === '') {
|
||||
if (rule.indexOf('-') >= 0) {
|
||||
dayRule.value = 'weekDay';
|
||||
dayRuleSup.value = getCycleArr(rule, 7, false)
|
||||
} else if (rule.indexOf('#') >= 0) {
|
||||
dayRule.value = 'assWeek';
|
||||
let matchRule = rule.match(/[0-9]{1}/g);
|
||||
dayRuleSup.value = [Number(matchRule[1]), Number(matchRule[0])];
|
||||
dateArr.value[3] = [1];
|
||||
if (dayRuleSup.value[1] === 7) {
|
||||
dayRuleSup.value[1] = 0;
|
||||
}
|
||||
} else if (rule.indexOf('L') >= 0) {
|
||||
dayRule.value = 'lastWeek';
|
||||
dayRuleSup.value = Number(rule.match(/[0-9]{1,2}/g)[0]);
|
||||
dateArr.value[3] = [31];
|
||||
if (dayRuleSup.value === 7) {
|
||||
dayRuleSup.value = 0;
|
||||
}
|
||||
} else if (rule !== '*' && rule !== '?') {
|
||||
dayRule.value = 'weekDay';
|
||||
dayRuleSup.value = getAssignArr(rule)
|
||||
}
|
||||
}
|
||||
}
|
||||
// 获取"日"数组-少量为日期规则
|
||||
function getDayArr(rule) {
|
||||
dateArr.value[3] = getOrderArr(1, 31);
|
||||
dayRule.value = '';
|
||||
dayRuleSup.value = '';
|
||||
if (rule.indexOf('-') >= 0) {
|
||||
dateArr.value[3] = getCycleArr(rule, 31, false)
|
||||
dayRuleSup.value = 'null';
|
||||
} else if (rule.indexOf('/') >= 0) {
|
||||
dateArr.value[3] = getAverageArr(rule, 31)
|
||||
dayRuleSup.value = 'null';
|
||||
} else if (rule.indexOf('W') >= 0) {
|
||||
dayRule.value = 'workDay';
|
||||
dayRuleSup.value = Number(rule.match(/[0-9]{1,2}/g)[0]);
|
||||
dateArr.value[3] = [dayRuleSup.value];
|
||||
} else if (rule.indexOf('L') >= 0) {
|
||||
dayRule.value = 'lastDay';
|
||||
dayRuleSup.value = 'null';
|
||||
dateArr.value[3] = [31];
|
||||
} else if (rule !== '*' && rule !== '?') {
|
||||
dateArr.value[3] = getAssignArr(rule)
|
||||
dayRuleSup.value = 'null';
|
||||
} else if (rule === '*') {
|
||||
dayRuleSup.value = 'null';
|
||||
}
|
||||
}
|
||||
// 获取"时"数组
|
||||
function getHourArr(rule) {
|
||||
dateArr.value[2] = getOrderArr(0, 23);
|
||||
if (rule.indexOf('-') >= 0) {
|
||||
dateArr.value[2] = getCycleArr(rule, 24, true)
|
||||
} else if (rule.indexOf('/') >= 0) {
|
||||
dateArr.value[2] = getAverageArr(rule, 23)
|
||||
} else if (rule !== '*') {
|
||||
dateArr.value[2] = getAssignArr(rule)
|
||||
}
|
||||
}
|
||||
// 获取"分"数组
|
||||
function getMinArr(rule) {
|
||||
dateArr.value[1] = getOrderArr(0, 59);
|
||||
if (rule.indexOf('-') >= 0) {
|
||||
dateArr.value[1] = getCycleArr(rule, 60, true)
|
||||
} else if (rule.indexOf('/') >= 0) {
|
||||
dateArr.value[1] = getAverageArr(rule, 59)
|
||||
} else if (rule !== '*') {
|
||||
dateArr.value[1] = getAssignArr(rule)
|
||||
}
|
||||
}
|
||||
// 获取"秒"数组
|
||||
function getSecondArr(rule) {
|
||||
dateArr.value[0] = getOrderArr(0, 59);
|
||||
if (rule.indexOf('-') >= 0) {
|
||||
dateArr.value[0] = getCycleArr(rule, 60, true)
|
||||
} else if (rule.indexOf('/') >= 0) {
|
||||
dateArr.value[0] = getAverageArr(rule, 59)
|
||||
} else if (rule !== '*') {
|
||||
dateArr.value[0] = getAssignArr(rule)
|
||||
}
|
||||
}
|
||||
// 根据传进来的min-max返回一个顺序的数组
|
||||
function getOrderArr(min, max) {
|
||||
let arr = [];
|
||||
for (let i = min; i <= max; i++) {
|
||||
arr.push(i);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
// 根据规则中指定的零散值返回一个数组
|
||||
function getAssignArr(rule) {
|
||||
let arr = [];
|
||||
let assiginArr = rule.split(',');
|
||||
for (let i = 0; i < assiginArr.length; i++) {
|
||||
arr[i] = Number(assiginArr[i])
|
||||
}
|
||||
arr.sort(compare)
|
||||
return arr;
|
||||
}
|
||||
// 根据一定算术规则计算返回一个数组
|
||||
function getAverageArr(rule, limit) {
|
||||
let arr = [];
|
||||
let agArr = rule.split('/');
|
||||
let min = Number(agArr[0]);
|
||||
let step = Number(agArr[1]);
|
||||
while (min <= limit) {
|
||||
arr.push(min);
|
||||
min += step;
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
// 根据规则返回一个具有周期性的数组
|
||||
function getCycleArr(rule, limit, status) {
|
||||
// status--表示是否从0开始(则从1开始)
|
||||
let arr = [];
|
||||
let cycleArr = rule.split('-');
|
||||
let min = Number(cycleArr[0]);
|
||||
let max = Number(cycleArr[1]);
|
||||
if (min > max) {
|
||||
max += limit;
|
||||
}
|
||||
for (let i = min; i <= max; i++) {
|
||||
let add = 0;
|
||||
if (status === false && i % limit === 0) {
|
||||
add = limit;
|
||||
}
|
||||
arr.push(Math.round(i % limit + add))
|
||||
}
|
||||
arr.sort(compare)
|
||||
return arr;
|
||||
}
|
||||
// 比较数字大小(用于Array.sort)
|
||||
function compare(value1, value2) {
|
||||
if (value2 - value1 > 0) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
// 格式化日期格式如:2017-9-19 18:04:33
|
||||
function formatDate(value, type) {
|
||||
// 计算日期相关值
|
||||
let time = typeof value == 'number' ? new Date(value) : value;
|
||||
let Y = time.getFullYear();
|
||||
let M = time.getMonth() + 1;
|
||||
let D = time.getDate();
|
||||
let h = time.getHours();
|
||||
let m = time.getMinutes();
|
||||
let s = time.getSeconds();
|
||||
let week = time.getDay();
|
||||
// 如果传递了type的话
|
||||
if (type === undefined) {
|
||||
return Y + '-' + (M < 10 ? '0' + M : M) + '-' + (D < 10 ? '0' + D : D) + ' ' + (h < 10 ? '0' + h : h) + ':' + (m < 10 ? '0' + m : m) + ':' + (s < 10 ? '0' + s : s);
|
||||
} else if (type === 'week') {
|
||||
// 在quartz中 1为星期日
|
||||
return week + 1;
|
||||
}
|
||||
}
|
||||
// 检查日期是否存在
|
||||
function checkDate(value) {
|
||||
let time = new Date(value);
|
||||
let format = formatDate(time)
|
||||
return value === format;
|
||||
}
|
||||
onMounted(() => {
|
||||
expressionChange()
|
||||
})
|
||||
</script>
|
||||
128
openhis-ui-vue3/src/components/Crontab/second.vue
Normal file
128
openhis-ui-vue3/src/components/Crontab/second.vue
Normal file
@@ -0,0 +1,128 @@
|
||||
<template>
|
||||
<el-form>
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="1">
|
||||
秒,允许的通配符[, - * /]
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="2">
|
||||
周期从
|
||||
<el-input-number v-model='cycle01' :min="0" :max="58" /> -
|
||||
<el-input-number v-model='cycle02' :min="cycle01 + 1" :max="59" /> 秒
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="3">
|
||||
从
|
||||
<el-input-number v-model='average01' :min="0" :max="58" /> 秒开始,每
|
||||
<el-input-number v-model='average02' :min="1" :max="59 - average01" /> 秒执行一次
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="4">
|
||||
指定
|
||||
<el-select clearable v-model="checkboxList" placeholder="可多选" multiple :multiple-limit="10">
|
||||
<el-option v-for="item in 60" :key="item" :label="item - 1" :value="item - 1" />
|
||||
</el-select>
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const emit = defineEmits(['update'])
|
||||
const props = defineProps({
|
||||
cron: {
|
||||
type: Object,
|
||||
default: {
|
||||
second: "*",
|
||||
min: "*",
|
||||
hour: "*",
|
||||
day: "*",
|
||||
month: "*",
|
||||
week: "?",
|
||||
year: "",
|
||||
}
|
||||
},
|
||||
check: {
|
||||
type: Function,
|
||||
default: () => {
|
||||
}
|
||||
}
|
||||
})
|
||||
const radioValue = ref(1)
|
||||
const cycle01 = ref(0)
|
||||
const cycle02 = ref(1)
|
||||
const average01 = ref(0)
|
||||
const average02 = ref(1)
|
||||
const checkboxList = ref([])
|
||||
const checkCopy = ref([0])
|
||||
const cycleTotal = computed(() => {
|
||||
cycle01.value = props.check(cycle01.value, 0, 58)
|
||||
cycle02.value = props.check(cycle02.value, cycle01.value + 1, 59)
|
||||
return cycle01.value + '-' + cycle02.value
|
||||
})
|
||||
const averageTotal = computed(() => {
|
||||
average01.value = props.check(average01.value, 0, 58)
|
||||
average02.value = props.check(average02.value, 1, 59 - average01.value)
|
||||
return average01.value + '/' + average02.value
|
||||
})
|
||||
const checkboxString = computed(() => {
|
||||
return checkboxList.value.join(',')
|
||||
})
|
||||
watch(() => props.cron.second, value => changeRadioValue(value))
|
||||
watch([radioValue, cycleTotal, averageTotal, checkboxString], () => onRadioChange())
|
||||
function changeRadioValue(value) {
|
||||
if (value === '*') {
|
||||
radioValue.value = 1
|
||||
} else if (value.indexOf('-') > -1) {
|
||||
const indexArr = value.split('-')
|
||||
cycle01.value = Number(indexArr[0])
|
||||
cycle02.value = Number(indexArr[1])
|
||||
radioValue.value = 2
|
||||
} else if (value.indexOf('/') > -1) {
|
||||
const indexArr = value.split('/')
|
||||
average01.value = Number(indexArr[0])
|
||||
average02.value = Number(indexArr[1])
|
||||
radioValue.value = 3
|
||||
} else {
|
||||
checkboxList.value = [...new Set(value.split(',').map(item => Number(item)))]
|
||||
radioValue.value = 4
|
||||
}
|
||||
}
|
||||
// 单选按钮值变化时
|
||||
function onRadioChange() {
|
||||
switch (radioValue.value) {
|
||||
case 1:
|
||||
emit('update', 'second', '*', 'second')
|
||||
break
|
||||
case 2:
|
||||
emit('update', 'second', cycleTotal.value, 'second')
|
||||
break
|
||||
case 3:
|
||||
emit('update', 'second', averageTotal.value, 'second')
|
||||
break
|
||||
case 4:
|
||||
if (checkboxList.value.length === 0) {
|
||||
checkboxList.value.push(checkCopy.value[0])
|
||||
} else {
|
||||
checkCopy.value = checkboxList.value
|
||||
}
|
||||
emit('update', 'second', checkboxString.value, 'second')
|
||||
break
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-input-number--small, .el-select, .el-select--small {
|
||||
margin: 0 0.2rem;
|
||||
}
|
||||
.el-select, .el-select--small {
|
||||
width: 18.8rem;
|
||||
}
|
||||
</style>
|
||||
197
openhis-ui-vue3/src/components/Crontab/week.vue
Normal file
197
openhis-ui-vue3/src/components/Crontab/week.vue
Normal file
@@ -0,0 +1,197 @@
|
||||
<template>
|
||||
<el-form>
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="1">
|
||||
周,允许的通配符[, - * ? / L #]
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="2">
|
||||
不指定
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="3">
|
||||
周期从
|
||||
<el-select clearable v-model="cycle01">
|
||||
<el-option
|
||||
v-for="(item,index) of weekList"
|
||||
:key="index"
|
||||
:label="item.value"
|
||||
:value="item.key"
|
||||
:disabled="item.key === 7"
|
||||
>{{item.value}}</el-option>
|
||||
</el-select>
|
||||
-
|
||||
<el-select clearable v-model="cycle02">
|
||||
<el-option
|
||||
v-for="(item,index) of weekList"
|
||||
:key="index"
|
||||
:label="item.value"
|
||||
:value="item.key"
|
||||
:disabled="item.key <= cycle01"
|
||||
>{{item.value}}</el-option>
|
||||
</el-select>
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="4">
|
||||
第
|
||||
<el-input-number v-model='average01' :min="1" :max="4" /> 周的
|
||||
<el-select clearable v-model="average02">
|
||||
<el-option v-for="item in weekList" :key="item.key" :label="item.value" :value="item.key" />
|
||||
</el-select>
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="5">
|
||||
本月最后一个
|
||||
<el-select clearable v-model="weekday">
|
||||
<el-option v-for="item in weekList" :key="item.key" :label="item.value" :value="item.key" />
|
||||
</el-select>
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio v-model='radioValue' :label="6">
|
||||
指定
|
||||
<el-select class="multiselect" clearable v-model="checkboxList" placeholder="可多选" multiple :multiple-limit="6">
|
||||
<el-option v-for="item in weekList" :key="item.key" :label="item.value" :value="item.key" />
|
||||
</el-select>
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const emit = defineEmits(['update'])
|
||||
const props = defineProps({
|
||||
cron: {
|
||||
type: Object,
|
||||
default: {
|
||||
second: "*",
|
||||
min: "*",
|
||||
hour: "*",
|
||||
day: "*",
|
||||
month: "*",
|
||||
week: "?",
|
||||
year: ""
|
||||
}
|
||||
},
|
||||
check: {
|
||||
type: Function,
|
||||
default: () => {
|
||||
}
|
||||
}
|
||||
})
|
||||
const radioValue = ref(2)
|
||||
const cycle01 = ref(2)
|
||||
const cycle02 = ref(3)
|
||||
const average01 = ref(1)
|
||||
const average02 = ref(2)
|
||||
const weekday = ref(2)
|
||||
const checkboxList = ref([])
|
||||
const checkCopy = ref([2])
|
||||
const weekList = ref([
|
||||
{key: 1, value: '星期日'},
|
||||
{key: 2, value: '星期一'},
|
||||
{key: 3, value: '星期二'},
|
||||
{key: 4, value: '星期三'},
|
||||
{key: 5, value: '星期四'},
|
||||
{key: 6, value: '星期五'},
|
||||
{key: 7, value: '星期六'}
|
||||
])
|
||||
const cycleTotal = computed(() => {
|
||||
cycle01.value = props.check(cycle01.value, 1, 6)
|
||||
cycle02.value = props.check(cycle02.value, cycle01.value + 1, 7)
|
||||
return cycle01.value + '-' + cycle02.value
|
||||
})
|
||||
const averageTotal = computed(() => {
|
||||
average01.value = props.check(average01.value, 1, 4)
|
||||
average02.value = props.check(average02.value, 1, 7)
|
||||
return average02.value + '#' + average01.value
|
||||
})
|
||||
const weekdayTotal = computed(() => {
|
||||
weekday.value = props.check(weekday.value, 1, 7)
|
||||
return weekday.value + 'L'
|
||||
})
|
||||
const checkboxString = computed(() => {
|
||||
return checkboxList.value.join(',')
|
||||
})
|
||||
watch(() => props.cron.week, value => changeRadioValue(value))
|
||||
watch([radioValue, cycleTotal, averageTotal, weekdayTotal, checkboxString], () => onRadioChange())
|
||||
function changeRadioValue(value) {
|
||||
if (value === "*") {
|
||||
radioValue.value = 1
|
||||
} else if (value === "?") {
|
||||
radioValue.value = 2
|
||||
} else if (value.indexOf("-") > -1) {
|
||||
const indexArr = value.split('-')
|
||||
cycle01.value = Number(indexArr[0])
|
||||
cycle02.value = Number(indexArr[1])
|
||||
radioValue.value = 3
|
||||
} else if (value.indexOf("#") > -1) {
|
||||
const indexArr = value.split('#')
|
||||
average01.value = Number(indexArr[1])
|
||||
average02.value = Number(indexArr[0])
|
||||
radioValue.value = 4
|
||||
} else if (value.indexOf("L") > -1) {
|
||||
const indexArr = value.split("L")
|
||||
weekday.value = Number(indexArr[0])
|
||||
radioValue.value = 5
|
||||
} else {
|
||||
checkboxList.value = [...new Set(value.split(',').map(item => Number(item)))]
|
||||
radioValue.value = 6
|
||||
}
|
||||
}
|
||||
function onRadioChange() {
|
||||
if (radioValue.value === 2 && props.cron.day === '?') {
|
||||
emit('update', 'day', '*', 'week')
|
||||
}
|
||||
if (radioValue.value !== 2 && props.cron.day !== '?') {
|
||||
emit('update', 'day', '?', 'week')
|
||||
}
|
||||
switch (radioValue.value) {
|
||||
case 1:
|
||||
emit('update', 'week', '*', 'week')
|
||||
break
|
||||
case 2:
|
||||
emit('update', 'week', '?', 'week')
|
||||
break
|
||||
case 3:
|
||||
emit('update', 'week', cycleTotal.value, 'week')
|
||||
break
|
||||
case 4:
|
||||
emit('update', 'week', averageTotal.value, 'week')
|
||||
break
|
||||
case 5:
|
||||
emit('update', 'week', weekdayTotal.value, 'week')
|
||||
break
|
||||
case 6:
|
||||
if (checkboxList.value.length === 0) {
|
||||
checkboxList.value.push(checkCopy.value[0])
|
||||
} else {
|
||||
checkCopy.value = checkboxList.value
|
||||
}
|
||||
emit('update', 'week', checkboxString.value, 'week')
|
||||
break
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-input-number--small, .el-select, .el-select--small {
|
||||
margin: 0 0.5rem;
|
||||
}
|
||||
.el-select, .el-select--small {
|
||||
width: 8rem;
|
||||
}
|
||||
.el-select.multiselect, .el-select--small.multiselect {
|
||||
width: 17.8rem;
|
||||
}
|
||||
</style>
|
||||
149
openhis-ui-vue3/src/components/Crontab/year.vue
Normal file
149
openhis-ui-vue3/src/components/Crontab/year.vue
Normal file
@@ -0,0 +1,149 @@
|
||||
<template>
|
||||
<el-form>
|
||||
<el-form-item>
|
||||
<el-radio :label="1" v-model='radioValue'>
|
||||
不填,允许的通配符[, - * /]
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio :label="2" v-model='radioValue'>
|
||||
每年
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio :label="3" v-model='radioValue'>
|
||||
周期从
|
||||
<el-input-number v-model='cycle01' :min='fullYear' :max="2098"/> -
|
||||
<el-input-number v-model='cycle02' :min="cycle01 ? cycle01 + 1 : fullYear + 1" :max="2099"/>
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio :label="4" v-model='radioValue'>
|
||||
从
|
||||
<el-input-number v-model='average01' :min='fullYear' :max="2098"/> 年开始,每
|
||||
<el-input-number v-model='average02' :min="1" :max="2099 - average01 || fullYear"/> 年执行一次
|
||||
</el-radio>
|
||||
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-radio :label="5" v-model='radioValue'>
|
||||
指定
|
||||
<el-select clearable v-model="checkboxList" placeholder="可多选" multiple :multiple-limit="8">
|
||||
<el-option v-for="item in 9" :key="item" :value="item - 1 + fullYear" :label="item -1 + fullYear" />
|
||||
</el-select>
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const emit = defineEmits(['update'])
|
||||
const props = defineProps({
|
||||
cron: {
|
||||
type: Object,
|
||||
default: {
|
||||
second: "*",
|
||||
min: "*",
|
||||
hour: "*",
|
||||
day: "*",
|
||||
month: "*",
|
||||
week: "?",
|
||||
year: ""
|
||||
}
|
||||
},
|
||||
check: {
|
||||
type: Function,
|
||||
default: () => {
|
||||
}
|
||||
}
|
||||
})
|
||||
const fullYear = ref(0)
|
||||
const maxFullYear = ref(0)
|
||||
const radioValue = ref(1)
|
||||
const cycle01 = ref(0)
|
||||
const cycle02 = ref(0)
|
||||
const average01 = ref(0)
|
||||
const average02 = ref(1)
|
||||
const checkboxList = ref([])
|
||||
const checkCopy = ref([])
|
||||
const cycleTotal = computed(() => {
|
||||
cycle01.value = props.check(cycle01.value, fullYear.value, maxFullYear.value - 1)
|
||||
cycle02.value = props.check(cycle02.value, cycle01.value + 1, maxFullYear.value)
|
||||
return cycle01.value + '-' + cycle02.value
|
||||
})
|
||||
const averageTotal = computed(() => {
|
||||
average01.value = props.check(average01.value, fullYear.value, maxFullYear.value - 1)
|
||||
average02.value = props.check(average02.value, 1, 10)
|
||||
return average01.value + '/' + average02.value
|
||||
})
|
||||
const checkboxString = computed(() => {
|
||||
return checkboxList.value.join(',')
|
||||
})
|
||||
watch(() => props.cron.year, value => changeRadioValue(value))
|
||||
watch([radioValue, cycleTotal, averageTotal, checkboxString], () => onRadioChange())
|
||||
function changeRadioValue(value) {
|
||||
if (value === '') {
|
||||
radioValue.value = 1
|
||||
} else if (value === "*") {
|
||||
radioValue.value = 2
|
||||
} else if (value.indexOf("-") > -1) {
|
||||
const indexArr = value.split('-')
|
||||
cycle01.value = Number(indexArr[0])
|
||||
cycle02.value = Number(indexArr[1])
|
||||
radioValue.value = 3
|
||||
} else if (value.indexOf("/") > -1) {
|
||||
const indexArr = value.split('/')
|
||||
average01.value = Number(indexArr[1])
|
||||
average02.value = Number(indexArr[0])
|
||||
radioValue.value = 4
|
||||
} else {
|
||||
checkboxList.value = [...new Set(value.split(',').map(item => Number(item)))]
|
||||
radioValue.value = 5
|
||||
}
|
||||
}
|
||||
function onRadioChange() {
|
||||
switch (radioValue.value) {
|
||||
case 1:
|
||||
emit('update', 'year', '', 'year')
|
||||
break
|
||||
case 2:
|
||||
emit('update', 'year', '*', 'year')
|
||||
break
|
||||
case 3:
|
||||
emit('update', 'year', cycleTotal.value, 'year')
|
||||
break
|
||||
case 4:
|
||||
emit('update', 'year', averageTotal.value, 'year')
|
||||
break
|
||||
case 5:
|
||||
if (checkboxList.value.length === 0) {
|
||||
checkboxList.value.push(checkCopy.value[0])
|
||||
} else {
|
||||
checkCopy.value = checkboxList.value
|
||||
}
|
||||
emit('update', 'year', checkboxString.value, 'year')
|
||||
break
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
fullYear.value = Number(new Date().getFullYear())
|
||||
maxFullYear.value = fullYear.value + 10
|
||||
cycle01.value = fullYear.value
|
||||
cycle02.value = cycle01.value + 1
|
||||
average01.value = fullYear.value
|
||||
checkCopy.value = [fullYear.value]
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-input-number--small, .el-select, .el-select--small {
|
||||
margin: 0 0.2rem;
|
||||
}
|
||||
.el-select, .el-select--small {
|
||||
width: 18.8rem;
|
||||
}
|
||||
</style>
|
||||
82
openhis-ui-vue3/src/components/DictTag/index.vue
Normal file
82
openhis-ui-vue3/src/components/DictTag/index.vue
Normal file
@@ -0,0 +1,82 @@
|
||||
<template>
|
||||
<div>
|
||||
<template v-for="(item, index) in options">
|
||||
<template v-if="values.includes(item.value)">
|
||||
<span
|
||||
v-if="(item.elTagType == 'default' || item.elTagType == '') && (item.elTagClass == '' || item.elTagClass == null)"
|
||||
:key="item.value"
|
||||
:index="index"
|
||||
:class="item.elTagClass"
|
||||
>{{ item.label + " " }}</span>
|
||||
<el-tag
|
||||
v-else
|
||||
:disable-transitions="true"
|
||||
:key="item.value + ''"
|
||||
:index="index"
|
||||
:type="item.elTagType === 'primary' ? '' : item.elTagType"
|
||||
:class="item.elTagClass"
|
||||
>{{ item.label + " " }}</el-tag>
|
||||
</template>
|
||||
</template>
|
||||
<template v-if="unmatch && showValue">
|
||||
{{ unmatchArray | handleArray }}
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// 记录未匹配的项
|
||||
const unmatchArray = ref([]);
|
||||
|
||||
const props = defineProps({
|
||||
// 数据
|
||||
options: {
|
||||
type: Array,
|
||||
default: null,
|
||||
},
|
||||
// 当前的值
|
||||
value: [Number, String, Array],
|
||||
// 当未找到匹配的数据时,显示value
|
||||
showValue: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
separator: {
|
||||
type: String,
|
||||
default: ",",
|
||||
}
|
||||
});
|
||||
|
||||
const values = computed(() => {
|
||||
if (props.value === null || typeof props.value === 'undefined' || props.value === '') return [];
|
||||
return Array.isArray(props.value) ? props.value.map(item => '' + item) : String(props.value).split(props.separator);
|
||||
});
|
||||
|
||||
const unmatch = computed(() => {
|
||||
unmatchArray.value = [];
|
||||
// 没有value不显示
|
||||
if (props.value === null || typeof props.value === 'undefined' || props.value === '' || props.options.length === 0) return false
|
||||
// 传入值为数组
|
||||
let unmatch = false // 添加一个标志来判断是否有未匹配项
|
||||
values.value.forEach(item => {
|
||||
if (!props.options.some(v => v.value === item)) {
|
||||
unmatchArray.value.push(item)
|
||||
unmatch = true // 如果有未匹配项,将标志设置为true
|
||||
}
|
||||
})
|
||||
return unmatch // 返回标志的值
|
||||
});
|
||||
|
||||
function handleArray(array) {
|
||||
if (array.length === 0) return "";
|
||||
return array.reduce((pre, cur) => {
|
||||
return pre + " " + cur;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.el-tag + .el-tag {
|
||||
margin-left: 10px;
|
||||
}
|
||||
</style>
|
||||
251
openhis-ui-vue3/src/components/Editor/index.vue
Normal file
251
openhis-ui-vue3/src/components/Editor/index.vue
Normal file
@@ -0,0 +1,251 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-upload
|
||||
:action="uploadUrl"
|
||||
:before-upload="handleBeforeUpload"
|
||||
:on-success="handleUploadSuccess"
|
||||
:on-error="handleUploadError"
|
||||
name="file"
|
||||
:show-file-list="false"
|
||||
:headers="headers"
|
||||
class="editor-img-uploader"
|
||||
v-if="type == 'url'"
|
||||
>
|
||||
<i ref="uploadRef" class="editor-img-uploader"></i>
|
||||
</el-upload>
|
||||
</div>
|
||||
<div class="editor">
|
||||
<quill-editor
|
||||
ref="quillEditorRef"
|
||||
v-model:content="content"
|
||||
contentType="html"
|
||||
@textChange="(e) => $emit('update:modelValue', content)"
|
||||
:options="options"
|
||||
:style="styles"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { QuillEditor } from "@vueup/vue-quill";
|
||||
import "@vueup/vue-quill/dist/vue-quill.snow.css";
|
||||
import { getToken } from "@/utils/auth";
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const quillEditorRef = ref();
|
||||
const uploadUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload"); // 上传的图片服务器地址
|
||||
const headers = ref({
|
||||
Authorization: "Bearer " + getToken()
|
||||
});
|
||||
|
||||
const props = defineProps({
|
||||
/* 编辑器的内容 */
|
||||
modelValue: {
|
||||
type: String,
|
||||
},
|
||||
/* 高度 */
|
||||
height: {
|
||||
type: Number,
|
||||
default: null,
|
||||
},
|
||||
/* 最小高度 */
|
||||
minHeight: {
|
||||
type: Number,
|
||||
default: null,
|
||||
},
|
||||
/* 只读 */
|
||||
readOnly: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
/* 上传文件大小限制(MB) */
|
||||
fileSize: {
|
||||
type: Number,
|
||||
default: 5,
|
||||
},
|
||||
/* 类型(base64格式、url格式) */
|
||||
type: {
|
||||
type: String,
|
||||
default: "url",
|
||||
}
|
||||
});
|
||||
|
||||
const options = ref({
|
||||
theme: "snow",
|
||||
bounds: document.body,
|
||||
debug: "warn",
|
||||
modules: {
|
||||
// 工具栏配置
|
||||
toolbar: [
|
||||
["bold", "italic", "underline", "strike"], // 加粗 斜体 下划线 删除线
|
||||
["blockquote", "code-block"], // 引用 代码块
|
||||
[{ list: "ordered" }, { list: "bullet" }], // 有序、无序列表
|
||||
[{ indent: "-1" }, { indent: "+1" }], // 缩进
|
||||
[{ size: ["small", false, "large", "huge"] }], // 字体大小
|
||||
[{ header: [1, 2, 3, 4, 5, 6, false] }], // 标题
|
||||
[{ color: [] }, { background: [] }], // 字体颜色、字体背景颜色
|
||||
[{ align: [] }], // 对齐方式
|
||||
["clean"], // 清除文本格式
|
||||
["link", "image", "video"] // 链接、图片、视频
|
||||
],
|
||||
},
|
||||
placeholder: "请输入内容",
|
||||
readOnly: props.readOnly
|
||||
});
|
||||
|
||||
const styles = computed(() => {
|
||||
let style = {};
|
||||
if (props.minHeight) {
|
||||
style.minHeight = `${props.minHeight}px`;
|
||||
}
|
||||
if (props.height) {
|
||||
style.height = `${props.height}px`;
|
||||
}
|
||||
return style;
|
||||
});
|
||||
|
||||
const content = ref("");
|
||||
watch(() => props.modelValue, (v) => {
|
||||
if (v !== content.value) {
|
||||
content.value = v === undefined ? "<p></p>" : v;
|
||||
}
|
||||
}, { immediate: true });
|
||||
|
||||
// 如果设置了上传地址则自定义图片上传事件
|
||||
onMounted(() => {
|
||||
if (props.type == 'url') {
|
||||
let quill = quillEditorRef.value.getQuill();
|
||||
let toolbar = quill.getModule("toolbar");
|
||||
toolbar.addHandler("image", (value) => {
|
||||
if (value) {
|
||||
proxy.$refs.uploadRef.click();
|
||||
} else {
|
||||
quill.format("image", false);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 上传前校检格式和大小
|
||||
function handleBeforeUpload(file) {
|
||||
const type = ["image/jpeg", "image/jpg", "image/png", "image/svg"];
|
||||
const isJPG = type.includes(file.type);
|
||||
//检验文件格式
|
||||
if (!isJPG) {
|
||||
proxy.$modal.msgError(`图片格式错误!`);
|
||||
return false;
|
||||
}
|
||||
// 校检文件大小
|
||||
if (props.fileSize) {
|
||||
const isLt = file.size / 1024 / 1024 < props.fileSize;
|
||||
if (!isLt) {
|
||||
proxy.$modal.msgError(`上传文件大小不能超过 ${props.fileSize} MB!`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 上传成功处理
|
||||
function handleUploadSuccess(res, file) {
|
||||
// 如果上传成功
|
||||
if (res.code == 200) {
|
||||
// 获取富文本实例
|
||||
let quill = toRaw(quillEditorRef.value).getQuill();
|
||||
// 获取光标位置
|
||||
let length = quill.selection.savedRange.index;
|
||||
// 插入图片,res.url为服务器返回的图片链接地址
|
||||
quill.insertEmbed(length, "image", import.meta.env.VITE_APP_BASE_API + res.fileName);
|
||||
// 调整光标到最后
|
||||
quill.setSelection(length + 1);
|
||||
} else {
|
||||
proxy.$modal.msgError("图片插入失败");
|
||||
}
|
||||
}
|
||||
|
||||
// 上传失败处理
|
||||
function handleUploadError() {
|
||||
proxy.$modal.msgError("图片插入失败");
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.editor-img-uploader {
|
||||
display: none;
|
||||
}
|
||||
.editor, .ql-toolbar {
|
||||
white-space: pre-wrap !important;
|
||||
line-height: normal !important;
|
||||
}
|
||||
.quill-img {
|
||||
display: none;
|
||||
}
|
||||
.ql-snow .ql-tooltip[data-mode="link"]::before {
|
||||
content: "请输入链接地址:";
|
||||
}
|
||||
.ql-snow .ql-tooltip.ql-editing a.ql-action::after {
|
||||
border-right: 0px;
|
||||
content: "保存";
|
||||
padding-right: 0px;
|
||||
}
|
||||
.ql-snow .ql-tooltip[data-mode="video"]::before {
|
||||
content: "请输入视频地址:";
|
||||
}
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-label::before,
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item::before {
|
||||
content: "14px";
|
||||
}
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="small"]::before,
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="small"]::before {
|
||||
content: "10px";
|
||||
}
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="large"]::before,
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="large"]::before {
|
||||
content: "18px";
|
||||
}
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="huge"]::before,
|
||||
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="huge"]::before {
|
||||
content: "32px";
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item::before {
|
||||
content: "文本";
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
|
||||
content: "标题1";
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
|
||||
content: "标题2";
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
|
||||
content: "标题3";
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
|
||||
content: "标题4";
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
|
||||
content: "标题5";
|
||||
}
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before,
|
||||
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
|
||||
content: "标题6";
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-label::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item::before {
|
||||
content: "标准字体";
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="serif"]::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="serif"]::before {
|
||||
content: "衬线字体";
|
||||
}
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="monospace"]::before,
|
||||
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="monospace"]::before {
|
||||
content: "等宽字体";
|
||||
}
|
||||
</style>
|
||||
207
openhis-ui-vue3/src/components/FileUpload/index.vue
Normal file
207
openhis-ui-vue3/src/components/FileUpload/index.vue
Normal file
@@ -0,0 +1,207 @@
|
||||
<template>
|
||||
<div class="upload-file">
|
||||
<el-upload
|
||||
multiple
|
||||
:action="uploadFileUrl"
|
||||
:before-upload="handleBeforeUpload"
|
||||
:file-list="fileList"
|
||||
:limit="limit"
|
||||
:on-error="handleUploadError"
|
||||
:on-exceed="handleExceed"
|
||||
:on-success="handleUploadSuccess"
|
||||
:show-file-list="false"
|
||||
:headers="headers"
|
||||
class="upload-file-uploader"
|
||||
ref="fileUpload"
|
||||
>
|
||||
<!-- 上传按钮 -->
|
||||
<el-button type="primary">选取文件</el-button>
|
||||
</el-upload>
|
||||
<!-- 上传提示 -->
|
||||
<div class="el-upload__tip" v-if="showTip">
|
||||
请上传
|
||||
<template v-if="fileSize"> 大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b> </template>
|
||||
<template v-if="fileType"> 格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b> </template>
|
||||
的文件
|
||||
</div>
|
||||
<!-- 文件列表 -->
|
||||
<transition-group class="upload-file-list el-upload-list el-upload-list--text" name="el-fade-in-linear" tag="ul">
|
||||
<li :key="file.uid" class="el-upload-list__item ele-upload-list__item-content" v-for="(file, index) in fileList">
|
||||
<el-link :href="`${baseUrl}${file.url}`" :underline="false" target="_blank">
|
||||
<span class="el-icon-document"> {{ getFileName(file.name) }} </span>
|
||||
</el-link>
|
||||
<div class="ele-upload-list__item-content-action">
|
||||
<el-link :underline="false" @click="handleDelete(index)" type="danger">删除</el-link>
|
||||
</div>
|
||||
</li>
|
||||
</transition-group>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { getToken } from "@/utils/auth";
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: [String, Object, Array],
|
||||
// 数量限制
|
||||
limit: {
|
||||
type: Number,
|
||||
default: 5,
|
||||
},
|
||||
// 大小限制(MB)
|
||||
fileSize: {
|
||||
type: Number,
|
||||
default: 5,
|
||||
},
|
||||
// 文件类型, 例如['png', 'jpg', 'jpeg']
|
||||
fileType: {
|
||||
type: Array,
|
||||
default: () => ["doc", "xls", "ppt", "txt", "pdf"],
|
||||
},
|
||||
// 是否显示提示
|
||||
isShowTip: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
});
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
const emit = defineEmits();
|
||||
const number = ref(0);
|
||||
const uploadList = ref([]);
|
||||
const baseUrl = import.meta.env.VITE_APP_BASE_API;
|
||||
const uploadFileUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload"); // 上传文件服务器地址
|
||||
const headers = ref({ Authorization: "Bearer " + getToken() });
|
||||
const fileList = ref([]);
|
||||
const showTip = computed(
|
||||
() => props.isShowTip && (props.fileType || props.fileSize)
|
||||
);
|
||||
|
||||
watch(() => props.modelValue, val => {
|
||||
if (val) {
|
||||
let temp = 1;
|
||||
// 首先将值转为数组
|
||||
const list = Array.isArray(val) ? val : props.modelValue.split(',');
|
||||
// 然后将数组转为对象数组
|
||||
fileList.value = list.map(item => {
|
||||
if (typeof item === "string") {
|
||||
item = { name: item, url: item };
|
||||
}
|
||||
item.uid = item.uid || new Date().getTime() + temp++;
|
||||
return item;
|
||||
});
|
||||
} else {
|
||||
fileList.value = [];
|
||||
return [];
|
||||
}
|
||||
},{ deep: true, immediate: true });
|
||||
|
||||
// 上传前校检格式和大小
|
||||
function handleBeforeUpload(file) {
|
||||
// 校检文件类型
|
||||
if (props.fileType.length) {
|
||||
const fileName = file.name.split('.');
|
||||
const fileExt = fileName[fileName.length - 1];
|
||||
const isTypeOk = props.fileType.indexOf(fileExt) >= 0;
|
||||
if (!isTypeOk) {
|
||||
proxy.$modal.msgError(`文件格式不正确, 请上传${props.fileType.join("/")}格式文件!`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// 校检文件大小
|
||||
if (props.fileSize) {
|
||||
const isLt = file.size / 1024 / 1024 < props.fileSize;
|
||||
if (!isLt) {
|
||||
proxy.$modal.msgError(`上传文件大小不能超过 ${props.fileSize} MB!`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
proxy.$modal.loading("正在上传文件,请稍候...");
|
||||
number.value++;
|
||||
return true;
|
||||
}
|
||||
|
||||
// 文件个数超出
|
||||
function handleExceed() {
|
||||
proxy.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`);
|
||||
}
|
||||
|
||||
// 上传失败
|
||||
function handleUploadError(err) {
|
||||
proxy.$modal.msgError("上传文件失败");
|
||||
}
|
||||
|
||||
// 上传成功回调
|
||||
function handleUploadSuccess(res, file) {
|
||||
if (res.code === 200) {
|
||||
uploadList.value.push({ name: res.fileName, url: res.fileName });
|
||||
uploadedSuccessfully();
|
||||
} else {
|
||||
number.value--;
|
||||
proxy.$modal.closeLoading();
|
||||
proxy.$modal.msgError(res.msg);
|
||||
proxy.$refs.fileUpload.handleRemove(file);
|
||||
uploadedSuccessfully();
|
||||
}
|
||||
}
|
||||
|
||||
// 删除文件
|
||||
function handleDelete(index) {
|
||||
fileList.value.splice(index, 1);
|
||||
emit("update:modelValue", listToString(fileList.value));
|
||||
}
|
||||
|
||||
// 上传结束处理
|
||||
function uploadedSuccessfully() {
|
||||
if (number.value > 0 && uploadList.value.length === number.value) {
|
||||
fileList.value = fileList.value.filter(f => f.url !== undefined).concat(uploadList.value);
|
||||
uploadList.value = [];
|
||||
number.value = 0;
|
||||
emit("update:modelValue", listToString(fileList.value));
|
||||
proxy.$modal.closeLoading();
|
||||
}
|
||||
}
|
||||
|
||||
// 获取文件名称
|
||||
function getFileName(name) {
|
||||
// 如果是url那么取最后的名字 如果不是直接返回
|
||||
if (name.lastIndexOf("/") > -1) {
|
||||
return name.slice(name.lastIndexOf("/") + 1);
|
||||
} else {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
// 对象转成指定字符串分隔
|
||||
function listToString(list, separator) {
|
||||
let strs = "";
|
||||
separator = separator || ",";
|
||||
for (let i in list) {
|
||||
if (list[i].url) {
|
||||
strs += list[i].url + separator;
|
||||
}
|
||||
}
|
||||
return strs != '' ? strs.substr(0, strs.length - 1) : '';
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.upload-file-uploader {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.upload-file-list .el-upload-list__item {
|
||||
border: 1px solid #e4e7ed;
|
||||
line-height: 2;
|
||||
margin-bottom: 10px;
|
||||
position: relative;
|
||||
}
|
||||
.upload-file-list .ele-upload-list__item-content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
color: inherit;
|
||||
}
|
||||
.ele-upload-list__item-content-action .el-link {
|
||||
margin-right: 10px;
|
||||
}
|
||||
</style>
|
||||
41
openhis-ui-vue3/src/components/Hamburger/index.vue
Normal file
41
openhis-ui-vue3/src/components/Hamburger/index.vue
Normal file
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<div style="padding: 0 15px;" @click="toggleClick">
|
||||
<svg
|
||||
:class="{'is-active':isActive}"
|
||||
class="hamburger"
|
||||
viewBox="0 0 1024 1024"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="64"
|
||||
height="64"
|
||||
>
|
||||
<path d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z" />
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
defineProps({
|
||||
isActive: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits()
|
||||
const toggleClick = () => {
|
||||
emit('toggleClick');
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.hamburger {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.hamburger.is-active {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
</style>
|
||||
198
openhis-ui-vue3/src/components/HeaderSearch/index.vue
Normal file
198
openhis-ui-vue3/src/components/HeaderSearch/index.vue
Normal file
@@ -0,0 +1,198 @@
|
||||
<template>
|
||||
<div :class="{ show: show }" class="header-search">
|
||||
<svg-icon class-name="search-icon" icon-class="search" @click.stop="click" v-if="!show" />
|
||||
<el-select
|
||||
v-else
|
||||
ref="headerSearchSelectRef"
|
||||
v-model="search"
|
||||
:remote-method="querySearch"
|
||||
filterable
|
||||
default-first-option
|
||||
remote
|
||||
placeholder="搜索菜单"
|
||||
class="header-search-select"
|
||||
@change="change"
|
||||
>
|
||||
<el-option
|
||||
v-for="option in options"
|
||||
:key="option.item.path"
|
||||
:value="option.item"
|
||||
:label="option.item.title.join(' > ')"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Fuse from 'fuse.js';
|
||||
import { getNormalPath } from '@/utils/openhis';
|
||||
import { isHttp } from '@/utils/validate';
|
||||
import usePermissionStore from '@/store/modules/permission';
|
||||
|
||||
const search = ref('');
|
||||
const options = ref([]);
|
||||
const searchPool = ref([]);
|
||||
const show = ref(false);
|
||||
const fuse = ref(undefined);
|
||||
const headerSearchSelectRef = ref(null);
|
||||
const router = useRouter();
|
||||
const routes = computed(() => usePermissionStore().routes);
|
||||
|
||||
function click() {
|
||||
show.value = !show.value;
|
||||
if (show.value) {
|
||||
headerSearchSelectRef.value && headerSearchSelectRef.value.focus();
|
||||
}
|
||||
}
|
||||
function close() {
|
||||
headerSearchSelectRef.value && headerSearchSelectRef.value.blur();
|
||||
options.value = [];
|
||||
show.value = false;
|
||||
}
|
||||
function change(val) {
|
||||
const path = val.path;
|
||||
const query = val.query;
|
||||
if (isHttp(path)) {
|
||||
// http(s):// 路径新窗口打开
|
||||
const pindex = path.indexOf('http');
|
||||
window.open(path.substr(pindex, path.length), '_blank');
|
||||
} else {
|
||||
if (query) {
|
||||
router.push({ path: path, query: JSON.parse(query) });
|
||||
} else {
|
||||
router.push(path);
|
||||
}
|
||||
}
|
||||
|
||||
search.value = '';
|
||||
options.value = [];
|
||||
nextTick(() => {
|
||||
show.value = false;
|
||||
});
|
||||
}
|
||||
function initFuse(list) {
|
||||
fuse.value = new Fuse(list, {
|
||||
shouldSort: true,
|
||||
threshold: 0.4,
|
||||
location: 0,
|
||||
distance: 100,
|
||||
minMatchCharLength: 1,
|
||||
keys: [
|
||||
{
|
||||
name: 'title',
|
||||
weight: 0.7,
|
||||
},
|
||||
{
|
||||
name: 'path',
|
||||
weight: 0.3,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
// Filter out the routes that can be displayed in the sidebar
|
||||
// And generate the internationalized title
|
||||
function generateRoutes(routes, basePath = '', prefixTitle = []) {
|
||||
let res = [];
|
||||
|
||||
for (const r of routes) {
|
||||
// skip hidden router
|
||||
if (r.hidden) {
|
||||
continue;
|
||||
}
|
||||
const p = r.path.length > 0 && r.path[0] === '/' ? r.path : '/' + r.path;
|
||||
const data = {
|
||||
path: !isHttp(r.path) ? getNormalPath(basePath + p) : r.path,
|
||||
title: [...prefixTitle],
|
||||
};
|
||||
|
||||
if (r.meta && r.meta.title) {
|
||||
data.title = [...data.title, r.meta.title];
|
||||
|
||||
if (r.redirect !== 'noRedirect') {
|
||||
// only push the routes with title
|
||||
// special case: need to exclude parent router without redirect
|
||||
res.push(data);
|
||||
}
|
||||
}
|
||||
if (r.query) {
|
||||
data.query = r.query;
|
||||
}
|
||||
|
||||
// recursive child routes
|
||||
if (r.children) {
|
||||
const tempRoutes = generateRoutes(r.children, data.path, data.title);
|
||||
if (tempRoutes.length >= 1) {
|
||||
res = [...res, ...tempRoutes];
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
function querySearch(query) {
|
||||
if (query !== '') {
|
||||
options.value = fuse.value.search(query);
|
||||
} else {
|
||||
options.value = [];
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
searchPool.value = generateRoutes(routes.value);
|
||||
});
|
||||
|
||||
watchEffect(() => {
|
||||
searchPool.value = generateRoutes(routes.value);
|
||||
});
|
||||
|
||||
watch(show, (value) => {
|
||||
if (value) {
|
||||
document.body.addEventListener('click', close);
|
||||
} else {
|
||||
document.body.removeEventListener('click', close);
|
||||
}
|
||||
});
|
||||
|
||||
watch(searchPool, (list) => {
|
||||
initFuse(list);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.header-search {
|
||||
font-size: 0 !important;
|
||||
|
||||
.search-icon {
|
||||
cursor: pointer;
|
||||
font-size: 18px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.header-search-select {
|
||||
font-size: 18px;
|
||||
transition: width 0.2s;
|
||||
width: 0;
|
||||
overflow: hidden;
|
||||
background: transparent;
|
||||
border-radius: 0;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
|
||||
:deep(.el-input__inner) {
|
||||
border-radius: 0;
|
||||
border: 0;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
box-shadow: none !important;
|
||||
border-bottom: 1px solid #d9d9d9;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
&.show {
|
||||
.header-search-select {
|
||||
width: 180px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
111
openhis-ui-vue3/src/components/IconSelect/index.vue
Normal file
111
openhis-ui-vue3/src/components/IconSelect/index.vue
Normal file
@@ -0,0 +1,111 @@
|
||||
<template>
|
||||
<div class="icon-body">
|
||||
<el-input
|
||||
v-model="iconName"
|
||||
class="icon-search"
|
||||
clearable
|
||||
placeholder="请输入图标名称"
|
||||
@clear="filterIcons"
|
||||
@input="filterIcons"
|
||||
>
|
||||
<template #suffix><i class="el-icon-search el-input__icon" /></template>
|
||||
</el-input>
|
||||
<div class="icon-list">
|
||||
<div class="list-container">
|
||||
<div v-for="(item, index) in iconList" class="icon-item-wrapper" :key="index" @click="selectedIcon(item)">
|
||||
<div :class="['icon-item', { active: activeIcon === item }]">
|
||||
<svg-icon :icon-class="item" class-name="icon" style="height: 25px;width: 16px;"/>
|
||||
<span>{{ item }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import icons from './requireIcons'
|
||||
|
||||
const props = defineProps({
|
||||
activeIcon: {
|
||||
type: String
|
||||
}
|
||||
});
|
||||
|
||||
const iconName = ref('');
|
||||
const iconList = ref(icons);
|
||||
const emit = defineEmits(['selected']);
|
||||
|
||||
function filterIcons() {
|
||||
iconList.value = icons
|
||||
if (iconName.value) {
|
||||
iconList.value = icons.filter(item => item.indexOf(iconName.value) !== -1)
|
||||
}
|
||||
}
|
||||
|
||||
function selectedIcon(name) {
|
||||
emit('selected', name)
|
||||
document.body.click()
|
||||
}
|
||||
|
||||
function reset() {
|
||||
iconName.value = ''
|
||||
iconList.value = icons
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
reset
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.icon-body {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
.icon-search {
|
||||
position: relative;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.icon-list {
|
||||
height: 200px;
|
||||
overflow: auto;
|
||||
.list-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
.icon-item-wrapper {
|
||||
width: calc(100% / 3);
|
||||
height: 25px;
|
||||
line-height: 25px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
.icon-item {
|
||||
display: flex;
|
||||
max-width: 100%;
|
||||
height: 100%;
|
||||
padding: 0 5px;
|
||||
&:hover {
|
||||
background: #ececec;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.icon {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
span {
|
||||
display: inline-block;
|
||||
vertical-align: -0.15em;
|
||||
fill: currentColor;
|
||||
padding-left: 2px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
.icon-item.active {
|
||||
background: #ececec;
|
||||
border-radius: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,8 @@
|
||||
let icons = []
|
||||
const modules = import.meta.glob('./../../assets/icons/svg/*.svg');
|
||||
for (const path in modules) {
|
||||
const p = path.split('assets/icons/svg/')[1].split('.svg')[0];
|
||||
icons.push(p);
|
||||
}
|
||||
|
||||
export default icons
|
||||
92
openhis-ui-vue3/src/components/ImagePreview/index.vue
Normal file
92
openhis-ui-vue3/src/components/ImagePreview/index.vue
Normal file
@@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<el-image
|
||||
:src="`${realSrc}`"
|
||||
fit="cover"
|
||||
:style="`width:${realWidth};height:${realHeight};`"
|
||||
:preview-src-list="realSrcList"
|
||||
preview-teleported
|
||||
>
|
||||
<template #error>
|
||||
<div class="image-slot">
|
||||
<el-icon><picture-filled /></el-icon>
|
||||
</div>
|
||||
</template>
|
||||
</el-image>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { isExternal } from "@/utils/validate";
|
||||
|
||||
const props = defineProps({
|
||||
src: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
width: {
|
||||
type: [Number, String],
|
||||
default: ""
|
||||
},
|
||||
height: {
|
||||
type: [Number, String],
|
||||
default: ""
|
||||
}
|
||||
});
|
||||
|
||||
const realSrc = computed(() => {
|
||||
if (!props.src) {
|
||||
return;
|
||||
}
|
||||
let real_src = props.src.split(",")[0];
|
||||
if (isExternal(real_src)) {
|
||||
return real_src;
|
||||
}
|
||||
return import.meta.env.VITE_APP_BASE_API + real_src;
|
||||
});
|
||||
|
||||
const realSrcList = computed(() => {
|
||||
if (!props.src) {
|
||||
return;
|
||||
}
|
||||
let real_src_list = props.src.split(",");
|
||||
let srcList = [];
|
||||
real_src_list.forEach(item => {
|
||||
if (isExternal(item)) {
|
||||
return srcList.push(item);
|
||||
}
|
||||
return srcList.push(import.meta.env.VITE_APP_BASE_API + item);
|
||||
});
|
||||
return srcList;
|
||||
});
|
||||
|
||||
const realWidth = computed(() =>
|
||||
typeof props.width == "string" ? props.width : `${props.width}px`
|
||||
);
|
||||
|
||||
const realHeight = computed(() =>
|
||||
typeof props.height == "string" ? props.height : `${props.height}px`
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-image {
|
||||
border-radius: 5px;
|
||||
background-color: #ebeef5;
|
||||
box-shadow: 0 0 5px 1px #ccc;
|
||||
:deep(.el-image__inner) {
|
||||
transition: all 0.3s;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
}
|
||||
:deep(.image-slot) {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
color: #909399;
|
||||
font-size: 30px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
213
openhis-ui-vue3/src/components/ImageUpload/index.vue
Normal file
213
openhis-ui-vue3/src/components/ImageUpload/index.vue
Normal file
@@ -0,0 +1,213 @@
|
||||
<template>
|
||||
<div class="component-upload-image">
|
||||
<el-upload
|
||||
multiple
|
||||
:action="uploadImgUrl"
|
||||
list-type="picture-card"
|
||||
:on-success="handleUploadSuccess"
|
||||
:before-upload="handleBeforeUpload"
|
||||
:limit="limit"
|
||||
:on-error="handleUploadError"
|
||||
:on-exceed="handleExceed"
|
||||
ref="imageUpload"
|
||||
:before-remove="handleDelete"
|
||||
:show-file-list="true"
|
||||
:headers="headers"
|
||||
:file-list="fileList"
|
||||
:on-preview="handlePictureCardPreview"
|
||||
:class="{ hide: fileList.length >= limit }"
|
||||
>
|
||||
<el-icon class="avatar-uploader-icon"><plus /></el-icon>
|
||||
</el-upload>
|
||||
<!-- 上传提示 -->
|
||||
<div class="el-upload__tip" v-if="showTip">
|
||||
请上传
|
||||
<template v-if="fileSize">
|
||||
大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
|
||||
</template>
|
||||
<template v-if="fileType">
|
||||
格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b>
|
||||
</template>
|
||||
的文件
|
||||
</div>
|
||||
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
title="预览"
|
||||
width="800px"
|
||||
append-to-body
|
||||
>
|
||||
<img
|
||||
:src="dialogImageUrl"
|
||||
style="display: block; max-width: 100%; margin: 0 auto"
|
||||
/>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { getToken } from "@/utils/auth";
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: [String, Object, Array],
|
||||
// 图片数量限制
|
||||
limit: {
|
||||
type: Number,
|
||||
default: 5,
|
||||
},
|
||||
// 大小限制(MB)
|
||||
fileSize: {
|
||||
type: Number,
|
||||
default: 5,
|
||||
},
|
||||
// 文件类型, 例如['png', 'jpg', 'jpeg']
|
||||
fileType: {
|
||||
type: Array,
|
||||
default: () => ["png", "jpg", "jpeg"],
|
||||
},
|
||||
// 是否显示提示
|
||||
isShowTip: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
});
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
const emit = defineEmits();
|
||||
const number = ref(0);
|
||||
const uploadList = ref([]);
|
||||
const dialogImageUrl = ref("");
|
||||
const dialogVisible = ref(false);
|
||||
const baseUrl = import.meta.env.VITE_APP_BASE_API;
|
||||
const uploadImgUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload"); // 上传的图片服务器地址
|
||||
const headers = ref({ Authorization: "Bearer " + getToken() });
|
||||
const fileList = ref([]);
|
||||
const showTip = computed(
|
||||
() => props.isShowTip && (props.fileType || props.fileSize)
|
||||
);
|
||||
|
||||
watch(() => props.modelValue, val => {
|
||||
if (val) {
|
||||
// 首先将值转为数组
|
||||
const list = Array.isArray(val) ? val : props.modelValue.split(",");
|
||||
// 然后将数组转为对象数组
|
||||
fileList.value = list.map(item => {
|
||||
if (typeof item === "string") {
|
||||
if (item.indexOf(baseUrl) === -1) {
|
||||
item = { name: baseUrl + item, url: baseUrl + item };
|
||||
} else {
|
||||
item = { name: item, url: item };
|
||||
}
|
||||
}
|
||||
return item;
|
||||
});
|
||||
} else {
|
||||
fileList.value = [];
|
||||
return [];
|
||||
}
|
||||
},{ deep: true, immediate: true });
|
||||
|
||||
// 上传前loading加载
|
||||
function handleBeforeUpload(file) {
|
||||
let isImg = false;
|
||||
if (props.fileType.length) {
|
||||
let fileExtension = "";
|
||||
if (file.name.lastIndexOf(".") > -1) {
|
||||
fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1);
|
||||
}
|
||||
isImg = props.fileType.some(type => {
|
||||
if (file.type.indexOf(type) > -1) return true;
|
||||
if (fileExtension && fileExtension.indexOf(type) > -1) return true;
|
||||
return false;
|
||||
});
|
||||
} else {
|
||||
isImg = file.type.indexOf("image") > -1;
|
||||
}
|
||||
if (!isImg) {
|
||||
proxy.$modal.msgError(
|
||||
`文件格式不正确, 请上传${props.fileType.join("/")}图片格式文件!`
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (props.fileSize) {
|
||||
const isLt = file.size / 1024 / 1024 < props.fileSize;
|
||||
if (!isLt) {
|
||||
proxy.$modal.msgError(`上传头像图片大小不能超过 ${props.fileSize} MB!`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
proxy.$modal.loading("正在上传图片,请稍候...");
|
||||
number.value++;
|
||||
}
|
||||
|
||||
// 文件个数超出
|
||||
function handleExceed() {
|
||||
proxy.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`);
|
||||
}
|
||||
|
||||
// 上传成功回调
|
||||
function handleUploadSuccess(res, file) {
|
||||
if (res.code === 200) {
|
||||
uploadList.value.push({ name: res.fileName, url: res.fileName });
|
||||
uploadedSuccessfully();
|
||||
} else {
|
||||
number.value--;
|
||||
proxy.$modal.closeLoading();
|
||||
proxy.$modal.msgError(res.msg);
|
||||
proxy.$refs.imageUpload.handleRemove(file);
|
||||
uploadedSuccessfully();
|
||||
}
|
||||
}
|
||||
|
||||
// 删除图片
|
||||
function handleDelete(file) {
|
||||
const findex = fileList.value.map(f => f.name).indexOf(file.name);
|
||||
if (findex > -1 && uploadList.value.length === number.value) {
|
||||
fileList.value.splice(findex, 1);
|
||||
emit("update:modelValue", listToString(fileList.value));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 上传结束处理
|
||||
function uploadedSuccessfully() {
|
||||
if (number.value > 0 && uploadList.value.length === number.value) {
|
||||
fileList.value = fileList.value.filter(f => f.url !== undefined).concat(uploadList.value);
|
||||
uploadList.value = [];
|
||||
number.value = 0;
|
||||
emit("update:modelValue", listToString(fileList.value));
|
||||
proxy.$modal.closeLoading();
|
||||
}
|
||||
}
|
||||
|
||||
// 上传失败
|
||||
function handleUploadError() {
|
||||
proxy.$modal.msgError("上传图片失败");
|
||||
proxy.$modal.closeLoading();
|
||||
}
|
||||
|
||||
// 预览
|
||||
function handlePictureCardPreview(file) {
|
||||
dialogImageUrl.value = file.url;
|
||||
dialogVisible.value = true;
|
||||
}
|
||||
|
||||
// 对象转成指定字符串分隔
|
||||
function listToString(list, separator) {
|
||||
let strs = "";
|
||||
separator = separator || ",";
|
||||
for (let i in list) {
|
||||
if (undefined !== list[i].url && list[i].url.indexOf("blob:") !== 0) {
|
||||
strs += list[i].url.replace(baseUrl, "") + separator;
|
||||
}
|
||||
}
|
||||
return strs != "" ? strs.substr(0, strs.length - 1) : "";
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
// .el-upload--picture-card 控制加号部分
|
||||
:deep(.hide .el-upload--picture-card) {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
13
openhis-ui-vue3/src/components/OpenHis/Doc/index.vue
Normal file
13
openhis-ui-vue3/src/components/OpenHis/Doc/index.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
<svg-icon icon-class="question" @click="goto" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// const url = ref('http://doc.ruoyi.vip/ruoyi-vue');
|
||||
|
||||
function goto() {
|
||||
// window.open(url.value)
|
||||
}
|
||||
</script>
|
||||
231
openhis-ui-vue3/src/components/OpenHis/TraceNoDialog/index.vue
Normal file
231
openhis-ui-vue3/src/components/OpenHis/TraceNoDialog/index.vue
Normal file
@@ -0,0 +1,231 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
title="药品追溯码"
|
||||
v-model="props.openDialog"
|
||||
width="842"
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
:draggable="true"
|
||||
@close="cancel"
|
||||
@opened="
|
||||
() => {
|
||||
console.log(123);
|
||||
traceNoTempRef.focus();
|
||||
}
|
||||
"
|
||||
>
|
||||
<div>
|
||||
<div style="font-size: 16px">
|
||||
<div style="margin-bottom: 15px">药品名称: {{ props.ypName }}</div>
|
||||
扫描追溯码
|
||||
<el-input
|
||||
ref="traceNoTempRef"
|
||||
type="textarea"
|
||||
:rows="1"
|
||||
v-model="traceNoTemp"
|
||||
style="width: 180px; margin-right: 20px"
|
||||
@input="throttledGetList(traceNoTemp)"
|
||||
/>
|
||||
输入追溯码
|
||||
<el-input
|
||||
v-model="traceNoInput"
|
||||
style="width: 180px; margin-right: 20px"
|
||||
@keyup.enter="handelTraceNo(traceNoInput)"
|
||||
/>
|
||||
<el-button type="primary" plain icon="CircleClose" @click="handleReturn"> 撤回 </el-button>
|
||||
<el-button type="danger" plain icon="CircleClose" @click="handleClear"> 清除 </el-button>
|
||||
</div>
|
||||
<!-- <el-input
|
||||
ref="inputRef"
|
||||
v-model="traceNo"
|
||||
type="textarea"
|
||||
:rows="15"
|
||||
disabled
|
||||
@input="throttledGetList"
|
||||
style="width: 100%; margin-top: 10px; margin-right: 20px"
|
||||
/> -->
|
||||
<div
|
||||
style="
|
||||
background: #f5f7fa;
|
||||
margin-top: 10px;
|
||||
padding: 10px;
|
||||
width: 780px;
|
||||
height: 350px;
|
||||
border: solid 1px #ebeef5;
|
||||
overflow-y: auto;
|
||||
"
|
||||
>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 8px">
|
||||
<div
|
||||
v-for="(item, index) in traceNoList"
|
||||
:key="item + index"
|
||||
style="
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
background: white;
|
||||
padding: 4px 8px 4px 12px;
|
||||
border-radius: 12px;
|
||||
"
|
||||
>
|
||||
<span style="margin-right: 6px">
|
||||
[{{ index + 1 }}]
|
||||
<template v-if="index < 9"> </template>
|
||||
<template v-else></template>
|
||||
{{ item }}
|
||||
</span>
|
||||
<div
|
||||
@click="removeTraceNo(index)"
|
||||
style="
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: #f56c6c;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
"
|
||||
>
|
||||
<el-icon size="10" color="white">
|
||||
<Close />
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
追溯码:{{ existenceTraceNoList[rowData.itemId] }}
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button type="primary" @click="submit">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { watch } from 'vue';
|
||||
import { searchTraceNo } from '@/api/public';
|
||||
import { debounce } from 'lodash-es';
|
||||
|
||||
const props = defineProps({
|
||||
openDialog: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
ypName: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
rowData: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
});
|
||||
const openTraceNo = ref(false);
|
||||
const traceNo = ref('');
|
||||
const traceNoList = ref([]);
|
||||
const existenceTraceNoList = ref([]);
|
||||
const traceNoTemp = ref('');
|
||||
const traceNoInput = ref('');
|
||||
const traceNoTempRef = ref();
|
||||
const emit = defineEmits(['submit', 'cancel']);
|
||||
|
||||
watch(
|
||||
() => props.rowData,
|
||||
async (newRowData) => {
|
||||
if (newRowData && Object.keys(newRowData).length > 0) {
|
||||
const { itemType, itemId, locationId, lotNumber } = newRowData;
|
||||
|
||||
try {
|
||||
const response = await searchTraceNo({
|
||||
itemType,
|
||||
itemId,
|
||||
locationId,
|
||||
lotNumber,
|
||||
});
|
||||
|
||||
// 处理返回数据,例如更新 traceNoList
|
||||
existenceTraceNoList.value = response.data || [];
|
||||
} catch (error) {
|
||||
console.error('获取追溯码失败:', error);
|
||||
}
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
|
||||
// 撤回最后一条追溯码
|
||||
function handleReturn() {
|
||||
if (traceNoList.value.length !== 0) {
|
||||
// 1. 从数组中移除最后一项
|
||||
traceNoList.value.pop();
|
||||
// 2. 重新构建显示文本
|
||||
traceNo.value = traceNoList.value
|
||||
.map((item, index) => {
|
||||
return index < 9 ? `[${index + 1}] ${item}` : `[${index + 1}] ${item}`;
|
||||
})
|
||||
.join(', ');
|
||||
if (traceNoList.value != 0) {
|
||||
traceNo.value += '\n';
|
||||
}
|
||||
}
|
||||
traceNoTempRef.value.focus();
|
||||
}
|
||||
|
||||
function removeTraceNo(index) {
|
||||
traceNoList.value.splice(index, 1);
|
||||
traceNoTempRef.value.focus();
|
||||
}
|
||||
|
||||
function formatValue(index) {
|
||||
if (index >= 10) {
|
||||
return ` `;
|
||||
} else {
|
||||
return ` `;
|
||||
}
|
||||
}
|
||||
|
||||
function handleClear() {
|
||||
traceNo.value = '';
|
||||
traceNoList.value = [];
|
||||
}
|
||||
const throttledGetList = debounce(handelTraceNo, 500);
|
||||
function handelTraceNo(value) {
|
||||
let list = value.trim().split('\n');
|
||||
list.forEach((item) => {
|
||||
traceNoList.value.push(item);
|
||||
});
|
||||
traceNo.value = traceNoList.value
|
||||
.map((item, index) => {
|
||||
return index < 9 ? `[${index + 1}] ${item}` : `[${index + 1}] ${item}`;
|
||||
})
|
||||
.join(', ');
|
||||
if (traceNoList.value != 0) {
|
||||
traceNo.value += '\n';
|
||||
}
|
||||
traceNoTemp.value = '';
|
||||
traceNoInput.value = '';
|
||||
// let saveValue = value.substring(inputValue.length + 5, value.length);
|
||||
// inputValue = value;
|
||||
// console.log(value);
|
||||
// console.log(saveValue);
|
||||
// traceNoList.value.push(saveValue);
|
||||
// traceNo.value = value + '[' + (traceNoList.value.length + 1) + ']' + ' ';
|
||||
}
|
||||
function cancel() {
|
||||
emit('cancel');
|
||||
traceNoList.value = [];
|
||||
traceNo.value = '';
|
||||
}
|
||||
|
||||
function submit() {
|
||||
emit('submit', traceNoList.value.join(','));
|
||||
openTraceNo.value = false;
|
||||
traceNoList.value = [];
|
||||
traceNo.value = '';
|
||||
}
|
||||
</script>
|
||||
84
openhis-ui-vue3/src/components/OpenHis/popoverList/index.vue
Normal file
84
openhis-ui-vue3/src/components/OpenHis/popoverList/index.vue
Normal file
@@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<el-popover
|
||||
:popper-style="{ padding: '0' }"
|
||||
:placement="placement"
|
||||
:visible="isPopoverVisible"
|
||||
trigger="manual"
|
||||
:width="width"
|
||||
>
|
||||
<slot name="popover-content" :row="row" :index="index">
|
||||
<div>列表内容</div>
|
||||
</slot>
|
||||
<template #reference>
|
||||
<el-input
|
||||
v-model="inputValue"
|
||||
:placeholder="placeholder"
|
||||
@input="handleInput"
|
||||
@focus="handleFocus"
|
||||
@blur="handleBlur"
|
||||
/>
|
||||
</template>
|
||||
</el-popover>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
row: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
index: {
|
||||
type: Number,
|
||||
default: -1,
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
placement: {
|
||||
type: String,
|
||||
default: 'bottom-start',
|
||||
},
|
||||
width: {
|
||||
type: Number,
|
||||
default: 800,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:modelValue', 'selsect-advice-base', 'focus', 'blur']);
|
||||
|
||||
const isPopoverVisible = ref(false);
|
||||
const inputValue = ref(props.modelValue);
|
||||
|
||||
// 同步外部modelValue变化
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newVal) => {
|
||||
inputValue.value = newVal;
|
||||
}
|
||||
);
|
||||
|
||||
const handleInput = (value) => {
|
||||
emit('search', value);
|
||||
};
|
||||
|
||||
const handleFocus = () => {
|
||||
isPopoverVisible.value = true;
|
||||
emit('focus', props.row, props.index);
|
||||
};
|
||||
|
||||
const handleBlur = () => {
|
||||
// 延迟隐藏以允许选择操作完成
|
||||
isPopoverVisible.value = false;
|
||||
emit('blur', props.row);
|
||||
};
|
||||
|
||||
const handleSelectAdvice = (payload) => {
|
||||
isPopoverVisible.value = false;
|
||||
emit('selsect-advice-base', payload);
|
||||
};
|
||||
</script>
|
||||
105
openhis-ui-vue3/src/components/Pagination/index.vue
Normal file
105
openhis-ui-vue3/src/components/Pagination/index.vue
Normal file
@@ -0,0 +1,105 @@
|
||||
<template>
|
||||
<div :class="{ 'hidden': hidden }" class="pagination-container">
|
||||
<el-pagination
|
||||
:background="background"
|
||||
v-model:current-page="currentPage"
|
||||
v-model:page-size="pageSize"
|
||||
:layout="layout"
|
||||
:page-sizes="pageSizes"
|
||||
:pager-count="pagerCount"
|
||||
:total="total"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { scrollTo } from '@/utils/scroll-to'
|
||||
|
||||
const props = defineProps({
|
||||
total: {
|
||||
required: true,
|
||||
type: Number
|
||||
},
|
||||
page: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
limit: {
|
||||
type: Number,
|
||||
default: 20
|
||||
},
|
||||
pageSizes: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [10, 20, 30, 50]
|
||||
}
|
||||
},
|
||||
// 移动端页码按钮的数量端默认值5
|
||||
pagerCount: {
|
||||
type: Number,
|
||||
default: document.body.clientWidth < 992 ? 5 : 7
|
||||
},
|
||||
layout: {
|
||||
type: String,
|
||||
default: 'total, sizes, prev, pager, next, jumper'
|
||||
},
|
||||
background: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
autoScroll: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
hidden: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits();
|
||||
const currentPage = computed({
|
||||
get() {
|
||||
return props.page
|
||||
},
|
||||
set(val) {
|
||||
emit('update:page', val)
|
||||
}
|
||||
})
|
||||
const pageSize = computed({
|
||||
get() {
|
||||
return props.limit
|
||||
},
|
||||
set(val){
|
||||
emit('update:limit', val)
|
||||
}
|
||||
})
|
||||
function handleSizeChange(val) {
|
||||
if (currentPage.value * val > props.total) {
|
||||
currentPage.value = 1
|
||||
}
|
||||
emit('pagination', { page: currentPage.value, limit: val })
|
||||
if (props.autoScroll) {
|
||||
scrollTo(0, 800)
|
||||
}
|
||||
}
|
||||
function handleCurrentChange(val) {
|
||||
emit('pagination', { page: val, limit: pageSize.value })
|
||||
if (props.autoScroll) {
|
||||
scrollTo(0, 800)
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.pagination-container {
|
||||
background: #fff;
|
||||
padding: 32px 16px;
|
||||
}
|
||||
.pagination-container.hidden {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
3
openhis-ui-vue3/src/components/ParentView/index.vue
Normal file
3
openhis-ui-vue3/src/components/ParentView/index.vue
Normal file
@@ -0,0 +1,3 @@
|
||||
<template >
|
||||
<router-view />
|
||||
</template>
|
||||
134
openhis-ui-vue3/src/components/RightToolbar/index.vue
Normal file
134
openhis-ui-vue3/src/components/RightToolbar/index.vue
Normal file
@@ -0,0 +1,134 @@
|
||||
<template>
|
||||
<div class="top-right-btn" :style="style">
|
||||
<el-row>
|
||||
<el-tooltip class="item" effect="dark" :content="showSearch ? '隐藏搜索' : '显示搜索'" placement="top" v-if="search">
|
||||
<el-button circle icon="Search" @click="toggleSearch()" />
|
||||
</el-tooltip>
|
||||
<el-tooltip class="item" effect="dark" content="刷新" placement="top">
|
||||
<el-button circle icon="Refresh" @click="refresh()" />
|
||||
</el-tooltip>
|
||||
<el-tooltip class="item" effect="dark" content="显隐列" placement="top" v-if="columns">
|
||||
<el-button circle icon="Menu" @click="showColumn()" v-if="showColumnsType == 'transfer'"/>
|
||||
<el-dropdown trigger="click" :hide-on-click="false" style="padding-left: 12px" v-if="showColumnsType == 'checkbox'">
|
||||
<el-button circle icon="Menu" />
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<template v-for="item in columns" :key="item.key">
|
||||
<el-dropdown-item>
|
||||
<el-checkbox :checked="item.visible" @change="checkboxChange($event, item.label)" :label="item.label" />
|
||||
</el-dropdown-item>
|
||||
</template>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</el-tooltip>
|
||||
</el-row>
|
||||
<el-dialog :title="title" v-model="open" append-to-body>
|
||||
<el-transfer
|
||||
:titles="['显示', '隐藏']"
|
||||
v-model="value"
|
||||
:data="columns"
|
||||
@change="dataChange"
|
||||
></el-transfer>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
/* 是否显示检索条件 */
|
||||
showSearch: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
/* 显隐列信息 */
|
||||
columns: {
|
||||
type: Array,
|
||||
},
|
||||
/* 是否显示检索图标 */
|
||||
search: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
/* 显隐列类型(transfer穿梭框、checkbox复选框) */
|
||||
showColumnsType: {
|
||||
type: String,
|
||||
default: "checkbox",
|
||||
},
|
||||
/* 右外边距 */
|
||||
gutter: {
|
||||
type: Number,
|
||||
default: 10,
|
||||
},
|
||||
})
|
||||
|
||||
const emits = defineEmits(['update:showSearch', 'queryTable']);
|
||||
|
||||
// 显隐数据
|
||||
const value = ref([]);
|
||||
// 弹出层标题
|
||||
const title = ref("显示/隐藏");
|
||||
// 是否显示弹出层
|
||||
const open = ref(false);
|
||||
|
||||
const style = computed(() => {
|
||||
const ret = {};
|
||||
if (props.gutter) {
|
||||
ret.marginRight = `${props.gutter / 2}px`;
|
||||
}
|
||||
return ret;
|
||||
});
|
||||
|
||||
// 搜索
|
||||
function toggleSearch() {
|
||||
emits("update:showSearch", !props.showSearch);
|
||||
}
|
||||
|
||||
// 刷新
|
||||
function refresh() {
|
||||
emits("queryTable");
|
||||
}
|
||||
|
||||
// 右侧列表元素变化
|
||||
function dataChange(data) {
|
||||
for (let item in props.columns) {
|
||||
const key = props.columns[item].key;
|
||||
props.columns[item].visible = !data.includes(key);
|
||||
}
|
||||
}
|
||||
|
||||
// 打开显隐列dialog
|
||||
function showColumn() {
|
||||
open.value = true;
|
||||
}
|
||||
|
||||
if (props.showColumnsType == 'transfer') {
|
||||
// 显隐列初始默认隐藏列
|
||||
for (let item in props.columns) {
|
||||
if (props.columns[item].visible === false) {
|
||||
value.value.push(parseInt(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 勾选
|
||||
function checkboxChange(event, label) {
|
||||
props.columns.filter(item => item.label == label)[0].visible = event;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
:deep(.el-transfer__button) {
|
||||
border-radius: 50%;
|
||||
display: block;
|
||||
margin-left: 0px;
|
||||
}
|
||||
:deep(.el-transfer__button:first-child) {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
:deep(.el-dropdown-menu__item) {
|
||||
line-height: 30px;
|
||||
padding: 0 17px;
|
||||
}
|
||||
</style>
|
||||
22
openhis-ui-vue3/src/components/Screenfull/index.vue
Normal file
22
openhis-ui-vue3/src/components/Screenfull/index.vue
Normal file
@@ -0,0 +1,22 @@
|
||||
<template>
|
||||
<div>
|
||||
<svg-icon :icon-class="isFullscreen ? 'exit-fullscreen' : 'fullscreen'" @click="toggle" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useFullscreen } from '@vueuse/core'
|
||||
|
||||
const { isFullscreen, enter, exit, toggle } = useFullscreen();
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.screenfull-svg {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
fill: #5a5e66;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
vertical-align: 10px;
|
||||
}
|
||||
</style>
|
||||
45
openhis-ui-vue3/src/components/SizeSelect/index.vue
Normal file
45
openhis-ui-vue3/src/components/SizeSelect/index.vue
Normal file
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-dropdown trigger="click" @command="handleSetSize">
|
||||
<div class="size-icon--style">
|
||||
<svg-icon class-name="size-icon" icon-class="size" />
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item v-for="item of sizeOptions" :key="item.value" :disabled="size === item.value" :command="item.value">
|
||||
{{ item.label }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import useAppStore from "@/store/modules/app";
|
||||
|
||||
const appStore = useAppStore();
|
||||
const size = computed(() => appStore.size);
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const { proxy } = getCurrentInstance();
|
||||
const sizeOptions = ref([
|
||||
{ label: "较大", value: "large" },
|
||||
{ label: "默认", value: "default" },
|
||||
{ label: "稍小", value: "small" },
|
||||
]);
|
||||
|
||||
function handleSetSize(size) {
|
||||
proxy.$modal.loading("正在设置布局大小,请稍候...");
|
||||
appStore.setSize(size);
|
||||
setTimeout("window.location.reload()", 1000);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.size-icon--style {
|
||||
font-size: 18px;
|
||||
line-height: 50px;
|
||||
padding-right: 7px;
|
||||
}
|
||||
</style>
|
||||
53
openhis-ui-vue3/src/components/SvgIcon/index.vue
Normal file
53
openhis-ui-vue3/src/components/SvgIcon/index.vue
Normal file
@@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<svg :class="svgClass" aria-hidden="true">
|
||||
<use :xlink:href="iconName" :fill="color" />
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default defineComponent({
|
||||
props: {
|
||||
iconClass: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
className: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
return {
|
||||
iconName: computed(() => `#icon-${props.iconClass}`),
|
||||
svgClass: computed(() => {
|
||||
if (props.className) {
|
||||
return `svg-icon ${props.className}`
|
||||
}
|
||||
return 'svg-icon'
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scope lang="scss">
|
||||
.sub-el-icon,
|
||||
.nav-icon {
|
||||
display: inline-block;
|
||||
font-size: 15px;
|
||||
margin-right: 12px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.svg-icon {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
position: relative;
|
||||
fill: currentColor;
|
||||
vertical-align: -2px;
|
||||
}
|
||||
</style>
|
||||
10
openhis-ui-vue3/src/components/SvgIcon/svgicon.js
Normal file
10
openhis-ui-vue3/src/components/SvgIcon/svgicon.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import * as components from '@element-plus/icons-vue'
|
||||
|
||||
export default {
|
||||
install: (app) => {
|
||||
for (const key in components) {
|
||||
const componentConfig = components[key];
|
||||
app.component(componentConfig.name, componentConfig);
|
||||
}
|
||||
},
|
||||
};
|
||||
214
openhis-ui-vue3/src/components/TopNav/index.vue
Normal file
214
openhis-ui-vue3/src/components/TopNav/index.vue
Normal file
@@ -0,0 +1,214 @@
|
||||
<template>
|
||||
<el-menu
|
||||
:default-active="activeMenu"
|
||||
mode="horizontal"
|
||||
@select="handleSelect"
|
||||
:ellipsis="false"
|
||||
>
|
||||
<template v-for="(item, index) in topMenus">
|
||||
<el-menu-item :style="{'--theme': theme}" :index="item.path" :key="index" v-if="index < visibleNumber">
|
||||
<svg-icon
|
||||
v-if="item.meta && item.meta.icon && item.meta.icon !== '#'"
|
||||
:icon-class="item.meta.icon"/>
|
||||
{{ item.meta.title }}
|
||||
</el-menu-item>
|
||||
</template>
|
||||
|
||||
<!-- 顶部菜单超出数量折叠 -->
|
||||
<el-sub-menu :style="{'--theme': theme}" index="more" v-if="topMenus.length > visibleNumber">
|
||||
<template #title>更多菜单</template>
|
||||
<template v-for="(item, index) in topMenus">
|
||||
<el-menu-item
|
||||
:index="item.path"
|
||||
:key="index"
|
||||
v-if="index >= visibleNumber">
|
||||
<svg-icon
|
||||
v-if="item.meta && item.meta.icon && item.meta.icon !== '#'"
|
||||
:icon-class="item.meta.icon"/>
|
||||
{{ item.meta.title }}
|
||||
</el-menu-item>
|
||||
</template>
|
||||
</el-sub-menu>
|
||||
</el-menu>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { constantRoutes } from "@/router"
|
||||
import { isHttp } from '@/utils/validate'
|
||||
import useAppStore from '@/store/modules/app'
|
||||
import useSettingsStore from '@/store/modules/settings'
|
||||
import usePermissionStore from '@/store/modules/permission'
|
||||
|
||||
// 顶部栏初始数
|
||||
const visibleNumber = ref(null);
|
||||
// 当前激活菜单的 index
|
||||
const currentIndex = ref(null);
|
||||
// 隐藏侧边栏路由
|
||||
const hideList = ['/index', '/user/profile'];
|
||||
|
||||
const appStore = useAppStore()
|
||||
const settingsStore = useSettingsStore()
|
||||
const permissionStore = usePermissionStore()
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
// 主题颜色
|
||||
const theme = computed(() => settingsStore.theme);
|
||||
// 所有的路由信息
|
||||
const routers = computed(() => permissionStore.topbarRouters);
|
||||
|
||||
// 顶部显示菜单
|
||||
const topMenus = computed(() => {
|
||||
let topMenus = [];
|
||||
routers.value.map((menu) => {
|
||||
if (menu.hidden !== true) {
|
||||
// 兼容顶部栏一级菜单内部跳转
|
||||
if (menu.path === "/") {
|
||||
topMenus.push(menu.children[0]);
|
||||
} else {
|
||||
topMenus.push(menu);
|
||||
}
|
||||
}
|
||||
})
|
||||
return topMenus;
|
||||
})
|
||||
|
||||
// 设置子路由
|
||||
const childrenMenus = computed(() => {
|
||||
let childrenMenus = [];
|
||||
routers.value.map((router) => {
|
||||
for (let item in router.children) {
|
||||
if (router.children[item].parentPath === undefined) {
|
||||
if(router.path === "/") {
|
||||
router.children[item].path = "/" + router.children[item].path;
|
||||
} else {
|
||||
if(!isHttp(router.children[item].path)) {
|
||||
router.children[item].path = router.path + "/" + router.children[item].path;
|
||||
}
|
||||
}
|
||||
router.children[item].parentPath = router.path;
|
||||
}
|
||||
childrenMenus.push(router.children[item]);
|
||||
}
|
||||
})
|
||||
return constantRoutes.concat(childrenMenus);
|
||||
})
|
||||
|
||||
// 默认激活的菜单
|
||||
const activeMenu = computed(() => {
|
||||
const path = route.path;
|
||||
let activePath = path;
|
||||
if (path !== undefined && path.lastIndexOf("/") > 0 && hideList.indexOf(path) === -1) {
|
||||
const tmpPath = path.substring(1, path.length);
|
||||
activePath = "/" + tmpPath.substring(0, tmpPath.indexOf("/"));
|
||||
if (!route.meta.link) {
|
||||
appStore.toggleSideBarHide(false);
|
||||
}
|
||||
} else if(!route.children) {
|
||||
activePath = path;
|
||||
appStore.toggleSideBarHide(true);
|
||||
}
|
||||
activeRoutes(activePath);
|
||||
return activePath;
|
||||
})
|
||||
|
||||
function setVisibleNumber() {
|
||||
const width = document.body.getBoundingClientRect().width / 3;
|
||||
visibleNumber.value = parseInt(width / 85);
|
||||
}
|
||||
|
||||
function handleSelect(key, keyPath) {
|
||||
currentIndex.value = key;
|
||||
const route = routers.value.find(item => item.path === key);
|
||||
if (isHttp(key)) {
|
||||
// http(s):// 路径新窗口打开
|
||||
window.open(key, "_blank");
|
||||
} else if (!route || !route.children) {
|
||||
// 没有子路由路径内部打开
|
||||
const routeMenu = childrenMenus.value.find(item => item.path === key);
|
||||
if (routeMenu && routeMenu.query) {
|
||||
let query = JSON.parse(routeMenu.query);
|
||||
router.push({ path: key, query: query });
|
||||
} else {
|
||||
router.push({ path: key });
|
||||
}
|
||||
appStore.toggleSideBarHide(true);
|
||||
} else {
|
||||
// 显示左侧联动菜单
|
||||
activeRoutes(key);
|
||||
appStore.toggleSideBarHide(false);
|
||||
}
|
||||
}
|
||||
|
||||
function activeRoutes(key) {
|
||||
let routes = [];
|
||||
if (childrenMenus.value && childrenMenus.value.length > 0) {
|
||||
childrenMenus.value.map((item) => {
|
||||
if (key == item.parentPath || (key == "index" && "" == item.path)) {
|
||||
routes.push(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
if(routes.length > 0) {
|
||||
permissionStore.setSidebarRouters(routes);
|
||||
} else {
|
||||
appStore.toggleSideBarHide(true);
|
||||
}
|
||||
return routes;
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('resize', setVisibleNumber)
|
||||
})
|
||||
onBeforeUnmount(() => {
|
||||
window.removeEventListener('resize', setVisibleNumber)
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
setVisibleNumber()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.topmenu-container.el-menu--horizontal > .el-menu-item {
|
||||
float: left;
|
||||
height: 50px !important;
|
||||
line-height: 50px !important;
|
||||
color: #999093 !important;
|
||||
padding: 0 5px !important;
|
||||
margin: 0 10px !important;
|
||||
}
|
||||
|
||||
.topmenu-container.el-menu--horizontal > .el-menu-item.is-active, .el-menu--horizontal > .el-sub-menu.is-active .el-submenu__title {
|
||||
border-bottom: 2px solid #{'var(--theme)'} !important;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
/* sub-menu item */
|
||||
.topmenu-container.el-menu--horizontal > .el-sub-menu .el-sub-menu__title {
|
||||
float: left;
|
||||
height: 50px !important;
|
||||
line-height: 50px !important;
|
||||
color: #999093 !important;
|
||||
padding: 0 5px !important;
|
||||
margin: 0 10px !important;
|
||||
}
|
||||
|
||||
/* 背景色隐藏 */
|
||||
.topmenu-container.el-menu--horizontal>.el-menu-item:not(.is-disabled):focus, .topmenu-container.el-menu--horizontal>.el-menu-item:not(.is-disabled):hover, .topmenu-container.el-menu--horizontal>.el-submenu .el-submenu__title:hover {
|
||||
background-color: #ffffff !important;
|
||||
}
|
||||
|
||||
/* 图标右间距 */
|
||||
.topmenu-container .svg-icon {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
/* topmenu more arrow */
|
||||
.topmenu-container .el-sub-menu .el-sub-menu__icon-arrow {
|
||||
position: static;
|
||||
vertical-align: middle;
|
||||
margin-left: 8px;
|
||||
margin-top: 0px;
|
||||
}
|
||||
</style>
|
||||
156
openhis-ui-vue3/src/components/TreeSelect/index.vue
Normal file
156
openhis-ui-vue3/src/components/TreeSelect/index.vue
Normal file
@@ -0,0 +1,156 @@
|
||||
<template>
|
||||
<div class="el-tree-select">
|
||||
<el-select
|
||||
style="width: 100%"
|
||||
v-model="valueId"
|
||||
ref="treeSelect"
|
||||
:filterable="true"
|
||||
:clearable="true"
|
||||
@clear="clearHandle"
|
||||
:filter-method="selectFilterData"
|
||||
:placeholder="placeholder"
|
||||
>
|
||||
<el-option :value="valueId" :label="valueTitle">
|
||||
<el-tree
|
||||
id="tree-option"
|
||||
ref="selectTree"
|
||||
:accordion="accordion"
|
||||
:data="options"
|
||||
:props="objMap"
|
||||
:node-key="objMap.value"
|
||||
:expand-on-click-node="false"
|
||||
:default-expanded-keys="defaultExpandedKey"
|
||||
:filter-node-method="filterNode"
|
||||
@node-click="handleNodeClick"
|
||||
></el-tree>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
|
||||
const { proxy } = getCurrentInstance();
|
||||
|
||||
const props = defineProps({
|
||||
/* 配置项 */
|
||||
objMap: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
value: 'id', // ID字段名
|
||||
label: 'label', // 显示名称
|
||||
children: 'children' // 子级字段名
|
||||
}
|
||||
}
|
||||
},
|
||||
/* 自动收起 */
|
||||
accordion: {
|
||||
type: Boolean,
|
||||
default: () => {
|
||||
return false
|
||||
}
|
||||
},
|
||||
/**当前双向数据绑定的值 */
|
||||
value: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
},
|
||||
/**当前的数据 */
|
||||
options: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
/**输入框内部的文字 */
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:value']);
|
||||
|
||||
const valueId = computed({
|
||||
get: () => props.value,
|
||||
set: (val) => {
|
||||
emit('update:value', val)
|
||||
}
|
||||
});
|
||||
const valueTitle = ref('');
|
||||
const defaultExpandedKey = ref([]);
|
||||
|
||||
function initHandle() {
|
||||
nextTick(() => {
|
||||
const selectedValue = valueId.value;
|
||||
if(selectedValue !== null && typeof (selectedValue) !== 'undefined') {
|
||||
const node = proxy.$refs.selectTree.getNode(selectedValue)
|
||||
if (node) {
|
||||
valueTitle.value = node.data[props.objMap.label]
|
||||
proxy.$refs.selectTree.setCurrentKey(selectedValue) // 设置默认选中
|
||||
defaultExpandedKey.value = [selectedValue] // 设置默认展开
|
||||
}
|
||||
} else {
|
||||
clearHandle()
|
||||
}
|
||||
})
|
||||
}
|
||||
function handleNodeClick(node) {
|
||||
valueTitle.value = node[props.objMap.label]
|
||||
valueId.value = node[props.objMap.value];
|
||||
defaultExpandedKey.value = [];
|
||||
proxy.$refs.treeSelect.blur()
|
||||
selectFilterData('')
|
||||
}
|
||||
function selectFilterData(val) {
|
||||
proxy.$refs.selectTree.filter(val)
|
||||
}
|
||||
function filterNode(value, data) {
|
||||
if (!value) return true
|
||||
return data[props.objMap['label']].indexOf(value) !== -1
|
||||
}
|
||||
function clearHandle() {
|
||||
valueTitle.value = ''
|
||||
valueId.value = ''
|
||||
defaultExpandedKey.value = [];
|
||||
clearSelected()
|
||||
}
|
||||
function clearSelected() {
|
||||
const allNode = document.querySelectorAll('#tree-option .el-tree-node')
|
||||
allNode.forEach((element) => element.classList.remove('is-current'))
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initHandle()
|
||||
})
|
||||
|
||||
watch(valueId, () => {
|
||||
initHandle();
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
@import "@/assets/styles/variables.module.scss";
|
||||
.el-scrollbar .el-scrollbar__view .el-select-dropdown__item {
|
||||
padding: 0;
|
||||
background-color: #fff;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.el-select-dropdown__item.selected {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
ul li .el-tree .el-tree-node__content {
|
||||
height: auto;
|
||||
padding: 0 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:deep(.el-tree-node__content:hover),
|
||||
:deep(.el-tree-node__content:active),
|
||||
:deep(.is-current > div:first-child),
|
||||
:deep(.el-tree-node__content:focus) {
|
||||
background-color: mix(#fff, $--color-primary, 90%);
|
||||
color: $--color-primary;
|
||||
}
|
||||
</style>
|
||||
31
openhis-ui-vue3/src/components/iFrame/index.vue
Normal file
31
openhis-ui-vue3/src/components/iFrame/index.vue
Normal file
@@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<div v-loading="loading" :style="'height:' + height">
|
||||
<iframe
|
||||
:src="url"
|
||||
frameborder="no"
|
||||
style="width: 100%; height: 100%"
|
||||
scrolling="auto" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
src: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const height = ref(document.documentElement.clientHeight - 94.5 + "px;")
|
||||
const loading = ref(true)
|
||||
const url = computed(() => props.src)
|
||||
|
||||
onMounted(() => {
|
||||
setTimeout(() => {
|
||||
loading.value = false;
|
||||
}, 300);
|
||||
window.onresize = function temp() {
|
||||
height.value = document.documentElement.clientHeight - 94.5 + "px;";
|
||||
};
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,79 @@
|
||||
<!--
|
||||
* @Author: sjjh
|
||||
* @Date: 2025-04-09 17:53:29
|
||||
* @Description:
|
||||
-->
|
||||
<template>
|
||||
<div class="ball-tag" v-show="tagId" :style="{ backgroundColor: _tag?.color }">
|
||||
{{ _tag?.name }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
const props = defineProps({
|
||||
tagId: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
})
|
||||
const tagList = [
|
||||
{
|
||||
id: '危',
|
||||
name: '危',
|
||||
color: '#ba7bc8',
|
||||
},
|
||||
{
|
||||
id: '径',
|
||||
name: '径',
|
||||
color: '#57bfe0',
|
||||
},
|
||||
{
|
||||
id: '术',
|
||||
name: '术',
|
||||
color: '#51ccc2',
|
||||
},
|
||||
{
|
||||
id: '预警',
|
||||
name: '¥',
|
||||
color: '#ee9038',
|
||||
},
|
||||
{
|
||||
id: '欠',
|
||||
name: '¥',
|
||||
color: '#E95657',
|
||||
},
|
||||
{
|
||||
id: '敏',
|
||||
name: '敏',
|
||||
color: '#5585e3',
|
||||
},
|
||||
{
|
||||
id: '重',
|
||||
name: '重',
|
||||
color: '#E95657',
|
||||
},
|
||||
]
|
||||
|
||||
const _tag = computed(() => {
|
||||
if (!props.tagId) {
|
||||
return tagList[0]
|
||||
} else {
|
||||
return tagList.find((item) => item.id == props.tagId)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.ball-tag {
|
||||
display: inline-flex;
|
||||
flex-shrink: 0; // 防止子元素缩小
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
font-size: 13px;
|
||||
border-radius: 20px;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,397 @@
|
||||
<!--
|
||||
* @Author: sjjh
|
||||
* @Date: 2025-04-07 20:42:45
|
||||
* @Description:住院患者信息,给医生用,带折叠
|
||||
-->
|
||||
<template>
|
||||
<div class="inPatientBarDoctorFold-container">
|
||||
<div class="basic_info">
|
||||
<div class="patient-header white-bg">
|
||||
<div class="select_wrapper_div">
|
||||
<b class="bedNumber" style="margin-left: 12px">{{ patientInfo?.bedName }}</b>
|
||||
<label class="content-text-color" style="margin-left: 12px; color: #a15209">
|
||||
{{ patientInfo?.patientName }}
|
||||
<span class="sex-age"> {{ patientInfo?.sexName }}/{{ patientInfo?.age }} </span>
|
||||
</label>
|
||||
|
||||
<div style="display: flex; margin-left: 8px">
|
||||
<!-- 状态展示// TODO 后端给状态,前段 -->
|
||||
<ball-tag
|
||||
style="margin-right: 4px"
|
||||
v-for="item in patientInfo?.list"
|
||||
:key="item"
|
||||
:tagId="item"
|
||||
></ball-tag>
|
||||
</div>
|
||||
<div
|
||||
class="gray-border"
|
||||
v-show="patientInfo?.feeTypeName && patientInfo?.feeTypeName !== ''"
|
||||
>
|
||||
{{ patientInfo?.feeTypeName }}
|
||||
</div>
|
||||
<label style="margin-left: 24px">
|
||||
<span class="label-text-color">住院:</span>
|
||||
<span class="content-text-color">{{ patientInfo?.inHospitalDays + '天' }}</span>
|
||||
</label>
|
||||
<label style="margin-left: 24px">
|
||||
<span class="label-text-color">入科:</span>
|
||||
<span class="content-text-color">{{ patientInfo?.inDeptDate }}</span>
|
||||
</label>
|
||||
<label style="margin-left: 24px">
|
||||
<span class="label-text-color">入院时间:</span>
|
||||
<span class="content-text-color">{{ patientInfo?.inHospitalTime }}</span>
|
||||
</label>
|
||||
<label style="margin-left: 24px">
|
||||
<span class="label-text-color">住院号:{{ patientInfo?.busNo }}</span>
|
||||
</label>
|
||||
<svg-icon icon-class="hipCopy" height="20px" width="20px" class="copy-svg" />
|
||||
<label style="margin-left: 30px">
|
||||
<span class="label-text-color">诊断:</span>
|
||||
<span class="content-text-color">{{ patientInfo?.regDiagnosisName }}</span>
|
||||
</label>
|
||||
<!-- <div style="margin-left: auto">
|
||||
<el-icon v-if="expand" @click="toggleExpand"><ArrowUpBold /></el-icon>
|
||||
<el-icon v-else @click="toggleExpand"><ArrowDownBold /></el-icon>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="expand" class="expand_more">
|
||||
<div style="background-color: #ffffff">
|
||||
<div style="margin-top: -10px">
|
||||
<label style="font-size: 14px">
|
||||
<span class="primary-text">过敏:</span>
|
||||
<span class="primary-text">{{ patientInfo?.allergies || '无过敏史' }}</span>
|
||||
</label>
|
||||
<label style="font-size: 14px; margin-left: 32px" v-show="patientInfo?.insuplcAdmdvsName">
|
||||
<span class="primary-text">医保统筹区:</span>
|
||||
<span class="primary-text">{{ patientInfo?.insuplcAdmdvsName }}</span>
|
||||
</label>
|
||||
|
||||
<label style="font-size: 14px; margin-left: 32px" v-show="patientInfo?.ciType">
|
||||
<span class="primary-text">商保信息:</span>
|
||||
<span class="primary-text">{{ patientInfo?.ciType }}</span>
|
||||
</label>
|
||||
<div style="display: flex; flex-wrap: nowrap; margin-top: 8px; white-space: nowrap">
|
||||
<div
|
||||
class="blue-bg"
|
||||
style="background-color: #f1faff; flex-shrink: 0; min-width: fit-content"
|
||||
>
|
||||
<span class="content-text-color">
|
||||
{{
|
||||
patientInfo?.height && patientInfo?.weight
|
||||
? `${patientInfo?.height}cm/${patientInfo?.weight}kg`
|
||||
: '身高/体重'
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="blue-bg"
|
||||
style="
|
||||
margin-left: 24px;
|
||||
background-color: #f1faff;
|
||||
flex-shrink: 0;
|
||||
min-width: fit-content;
|
||||
"
|
||||
v-show="patientInfo?.postoperativeDays"
|
||||
>
|
||||
<span class="content-text-color">术后{{ patientInfo?.postoperativeDays }}天</span>
|
||||
</div>
|
||||
<div
|
||||
class="blue-bg"
|
||||
style="
|
||||
margin-left: 16px;
|
||||
background-color: #f1faff;
|
||||
flex-shrink: 0;
|
||||
min-width: fit-content;
|
||||
"
|
||||
v-show="patientInfo?.poorTypeName"
|
||||
>
|
||||
<span class="label-text-color">贫困类型:</span>
|
||||
<span class="content-text-color" style="margin-left: 4px">{{
|
||||
patientInfo?.poorTypeName
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="blue-bg"
|
||||
style="
|
||||
margin-left: 16px;
|
||||
background-color: #f1faff;
|
||||
flex-shrink: 0;
|
||||
min-width: fit-content;
|
||||
"
|
||||
v-show="patientInfo?.pathwayName"
|
||||
>
|
||||
<span class="label-text-color">路径情况:</span>
|
||||
<span class="content-text-color" style="margin-left: 4px">{{
|
||||
patientInfo?.pathwayName
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="background-color: #ffffff">
|
||||
<div style="margin-top: -10px">
|
||||
<div class="patient-board">
|
||||
<div class="item-center">
|
||||
<div class="line-block">
|
||||
<div class="line-block-top">
|
||||
<span class="label-text-color">科室:</span>
|
||||
<span class="content-text-color">{{ patientInfo?.admissionDeptName }}</span>
|
||||
</div>
|
||||
<div class="line-block-bottom">
|
||||
<span class="label-text-color">病区:</span>
|
||||
<span class="content-text-color">{{ patientInfo?.deptNurseName }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="line-block">
|
||||
<div class="line-block-top">
|
||||
<span class="label-text-color">主治医生:</span>
|
||||
<span class="content-text-color">{{ patientInfo?.masterDoctorName }}</span>
|
||||
</div>
|
||||
<div class="line-block-bottom">
|
||||
<span class="label-text-color">责任护士:</span>
|
||||
<span class="content-text-color">{{ patientInfo?.masterNurseName }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="line-blockMoney">
|
||||
<div class="line-blockMoney-top">
|
||||
<span class="label-text-color">费用</span>
|
||||
</div>
|
||||
<div class="line-blockMoney-bottom">
|
||||
<b class="money-content size-15">{{
|
||||
patientInfo?.totalAmount ? patientInfo?.totalAmount : 0
|
||||
}}</b>
|
||||
</div>
|
||||
</div>
|
||||
<div class="line-blockMoney">
|
||||
<div class="line-blockMoney-top">
|
||||
<span class="label-text-color">预交金</span>
|
||||
</div>
|
||||
<div class="line-blockMoney-bottom">
|
||||
<b class="money-content size-15">{{
|
||||
patientInfo?.prepayAmount ? patientInfo?.prepayAmount : 0
|
||||
}}</b>
|
||||
</div>
|
||||
</div>
|
||||
<div class="line-blockMoney">
|
||||
<div class="line-blockMoney-top">
|
||||
<span class="label-text-color">余额</span>
|
||||
</div>
|
||||
<div class="line-blockMoney-bottom">
|
||||
<b class="money-content size-15">{{
|
||||
patientInfo?.balance ? patientInfo?.balance : 0
|
||||
}}</b>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hip-dividers></hip-dividers>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { computed, onMounted, ref, watch } from 'vue';
|
||||
import BallTag from './components/BallTag.vue';
|
||||
import { patientInfo } from '@/views/inpatientDoctor/home/store/patient.js';
|
||||
// import { ElMessage } from 'element-plus'
|
||||
|
||||
const expand = ref(false);
|
||||
|
||||
// 定义一个布尔变量来控制是否显示 hip-dividers
|
||||
const showDividers = ref(true);
|
||||
|
||||
// 示例方法:切换显示状态
|
||||
const toggleDividers = () => {
|
||||
showDividers.value = !showDividers.value;
|
||||
};
|
||||
const iconClass = ref('hipBarDown');
|
||||
|
||||
// 切换展开状态的方法
|
||||
function toggleExpand() {
|
||||
expand.value = !expand.value;
|
||||
iconClass.value = expand.value ? 'hipBarUp' : 'hipBarDown';
|
||||
toggleDividers();
|
||||
}
|
||||
|
||||
const fetchPatientInfoById = async (patientId) => {
|
||||
// 查询患者信息
|
||||
console.log(patientId);
|
||||
};
|
||||
|
||||
const props = defineProps({
|
||||
visitCode: '',
|
||||
});
|
||||
watch(
|
||||
() => props.visitCode,
|
||||
(val) => {
|
||||
if (val !== null && val !== '') {
|
||||
fetchPatientInfoById(val);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
defineOptions({
|
||||
name: 'NurserDoctorPatientBarminimal',
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.inPatientBarDoctorFold-container {
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
.basic_info {
|
||||
height: 43px;
|
||||
padding: 0 8px;
|
||||
}
|
||||
/* expand_more */
|
||||
.expand_more {
|
||||
width: 100%;
|
||||
height: 56px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 0 8px;
|
||||
}
|
||||
.patient-header {
|
||||
width: 100%;
|
||||
padding: 6px 0;
|
||||
font-size: 13px;
|
||||
|
||||
.sex-age {
|
||||
margin-left: 20px;
|
||||
color: var(--hip-color-text-description);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.gray-border {
|
||||
display: inline-flex;
|
||||
color: var(--hip-color-text-description);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: 1px solid var(--hip-color-text-description);
|
||||
border-radius: 20px;
|
||||
padding: 0px 8px;
|
||||
}
|
||||
|
||||
.copy-svg {
|
||||
fill: var(--hip-color-primary);
|
||||
cursor: pointer;
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.size-15 {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.bedNumber {
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.primary-text {
|
||||
color: var(--hip-color-primary);
|
||||
}
|
||||
|
||||
.flex-between {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.item-center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.flex-row {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.patient-board {
|
||||
margin-left: 20px;
|
||||
|
||||
.line-block {
|
||||
position: relative;
|
||||
min-width: 73px;
|
||||
padding-left: 15px;
|
||||
padding-right: 10px;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 50%;
|
||||
top: 25%;
|
||||
left: 0;
|
||||
background-color: #e1e1e1;
|
||||
}
|
||||
|
||||
&-top {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.money-content {
|
||||
color: #ff8616;
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.line-blockMoney {
|
||||
position: relative;
|
||||
min-width: 73px;
|
||||
padding-left: 15px;
|
||||
padding-right: 10px;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 50%;
|
||||
top: 25%;
|
||||
left: 0;
|
||||
background-color: #e1e1e1;
|
||||
}
|
||||
|
||||
&-top {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.money-content {
|
||||
color: #ff8616;
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.color-content) {
|
||||
background-color: rgba(252, 252, 252, 0.8);
|
||||
}
|
||||
}
|
||||
|
||||
.white-bg {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.blue-bg {
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: #f1faff;
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
border-radius: 4px; /*圆角*/
|
||||
}
|
||||
|
||||
.label-text-color {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.content-text-color {
|
||||
font-size: 14px;
|
||||
color: #666666;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user