This commit is contained in:
wyuu 2025-12-30 17:38:01 +08:00
parent 36826154e0
commit 92b55c83dc
6 changed files with 942 additions and 0 deletions

View File

@ -0,0 +1,43 @@
// @ts-ignore
import request from '@/utils/request'
// 保存数据
export function saveOpt(data?: Object) {
return request({
url: '/qc/saveOpt',
method: 'post',
data
})
}
// 查询项目的质控规则
export function queryRules(data?: Object) {
return request({
url: '/qc/queryRules',
method: 'get',
params: data
})
}
// 保存质控项目数据
export function saveQCItem(data?: Object) {
return request({
url: '/qc/saveQCItem',
method: 'post',
data
})
}
// 普通标本和质控标本对应关系
export function queryDataFromSample(data?: Object) {
return request({
url: '/qc/queryDataFromSample',
method: 'get',
params: data
})
}
// 查询质控品数据
export function queryQcSample(data?: Object) {
return request({
url: '/qc/queryQcSample',
method: 'get',
params: data
})
}

View File

@ -18,6 +18,14 @@ export function getdef(query?: Object) {
params: query, params: query,
}); });
} }
// 查询某个参数的值
export function queryValue(query?: Object) {
return request({
url: '/param/queryValue',
method: 'get',
params: query,
});
}
// 修改参数列表 // 修改参数列表
export function updateParam(query?: Object) { export function updateParam(query?: Object) {
return request({ return request({

View File

@ -0,0 +1,288 @@
/**
* @file index.vue 参数配置
* @author: w
* @since: 2025-12-29
*/
<template>
<div class="app-container">
<div class="table-toolbar">
<el-button type="primary" plain icon="Check" @click="handleSave">保存</el-button>
</div>
<div class="tips">说明: 本软件支持每天最多做6个质控品,质控品号固定为1-6,如果仪器上的质控标本号不是1-6,请在下面设定它们的对应关系,这样系统在采集仪器数据时,质控标本号据时能够自动转换!</div>
<div class="bb_box">
<el-row>
<el-col :span="14">
<div>
<span class="text"> 每日质控标本个数: </span>
<el-radio-group v-model="zkphValue" @change="zkphChange">
<el-radio value="1">1</el-radio>
<el-radio value="2">2</el-radio>
<el-radio value="3">3</el-radio>
<el-radio value="4">4</el-radio>
<el-radio value="5">5</el-radio>
<el-radio value="6">6</el-radio>
</el-radio-group>
</div>
<div>
<span class="text"> 模式: </span>
<el-radio-group v-model="radio2" @change="radioChange">
<el-radio value="1">SD模式(WestGuard)</el-radio>
<el-radio value="2">CV模式(WestGuard)</el-radio>
<el-radio value="3">Gurbbs+Tn界值法</el-radio>
</el-radio-group>
</div>
<div class="check-box">
<el-checkbox v-model="check1" label="质控图显示其他非画点图" value="1" />
<el-checkbox v-model="check2" label="质控图是否画虚线" value="1" />
<el-checkbox v-model="check3" label="质控图累计SD/CV值通过批号计算" value="1" />
</div>
<div class="bbh-box">
<span class="text">每日参考对照标本号: </span>
<el-input v-model="input" style="width:200px" />
</div>
</el-col>
<el-col :span="2" class="text_btn">
<el-button type="primary" @click="addItem">新增</el-button>
<el-button type="danger" @click="delItem">删除</el-button>
</el-col>
<el-col :span="8">
<CustomTable ref="tableRef" :data="tableData" :columns="columns" :config="tableConfig"
@row-click="rowYbhClick">
<template #ybh="{ row }">
<el-input v-model="row.ybh" class="full-width-input" />
</template>
<template #zkph="{ row }">
<el-input v-model="row.zkph" class="full-width-input" />
</template>
<template #cs="{ row }">
<el-input v-model="row.cs" class="full-width-input" />
</template>
</CustomTable>
</el-col>
</el-row>
</div>
<el-row :gutter="10" class="table-row">
<el-col :span="5" class="table-col">
<div class="tips">指标设定</div>
<CustomTable ref="leftTableRef" :data="leftTableData" :columns="leftColumns" @row-click="rowClick"
:config="{ border: true, highlightCurrentRow: true, height: '100%' }">
<template #xmdh="{ row }">
{{ row.xmdh }} {{ row.xmmc }}
</template>
<template #qcuselog="{ row }">
<el-checkbox :model-value="row.qcuselog == 1" readonly></el-checkbox>
</template>
<template #qcusepos="{ row }">
<el-checkbox :model-value="row.qcusepos == 1" readonly></el-checkbox>
</template>
</CustomTable>
</el-col>
<el-col :span="19" class="table-col">
<el-button type="primary">新增</el-button>
<el-button type="danger">删除</el-button>
<el-button>取消改动</el-button>
<CustomTable ref="rightTableRef" :data="rightTableData" :columns="rightTolumns" :enableColumnDrag="true"
@column-drag-end="columnDragEnd" :config="{ border: true, highlightCurrentRow: true, height: '100%' }">
</CustomTable>
</el-col>
</el-row>
</div>
</template>
<script setup lang="ts">
import { queryOldxminfo } from '@/api/liswork/xtwh/xmReqItemVsApi'
import { queryDataFromSample, queryQcSample, } from '@/api/liswork/qualityControl'
import { ElMessage } from 'element-plus'
const yq = defineModel<string>()
const zkphValue = ref('1')
const radio2 = ref('1')
const check1 = ref(false)
const check2 = ref(false)
const check3 = ref(false)
const input = ref('')
const selectedRow = ref()
interface ybhItem {
ybh: string
zkph: string
cs: string
}
const tableData = ref<ybhItem[]>([])
const columns = [
{ label: '普通标准号', prop: 'ybh', visible: true, align: 'center', slot: 'ybh' },
{ label: '对应质控品号', prop: 'zkph', visible: true, align: 'center', slot: 'zkph' },
{ label: '次数', prop: 'cs', visible: true, align: 'center', slot: 'cs' },
]
const tableConfig = {
border: true,
highlightCurrentRow: true,
height: '200px'
}
const tableRef = ref()
const ybhInfo = ref()
const rowYbhClick = (row: ybhItem) => {
ybhInfo.value = row
}
const addItem = () => {
tableData.value.push({
ybh: '',
zkph: '',
cs: ''
})
nextTick(() => {
tableRef.value.setScrollTop(tableData.value.length)
})
}
const delItem = () => {
const index = tableData.value.indexOf(ybhInfo.value)
tableData.value.splice(index, 1)
}
const leftTableData = ref([])
const leftColumns = [
{ label: '项目代号', prop: 'xmdh', visible: true, slot: 'xmdh', },
{ label: '值取对数', prop: 'qcuselog', visible: true, align: 'center', slot: 'qcuselog', width: 60 },
{ label: '半定量值', prop: 'qcusepos', visible: true, align: 'center', slot: 'qcusepos', width: 60 },
]
const rightTableData = ref([])
const rightTolumns = ref([
{ label: '启用日期', prop: 'ksrq', visible: true, align: 'center' },
{ label: '质控品', prop: 'zkph', visible: true, align: 'center' },
{ label: '批号', prop: 'zkpph', visible: true, align: 'center' },
{ label: '浓度', prop: 'ld', visible: true, align: 'center' },
{ label: '靶值', prop: 'zkbz', visible: true, align: 'center' },
{ label: '有效期至', prop: 'endrq', visible: true, align: 'center' },
{ label: 'CV(%)', prop: 'cv', visible: true, align: 'center' },
{ label: '目标CV(%)', prop: 'qccv', visible: true, align: 'center' },
{ label: '试剂', prop: 'sjph', visible: true, align: 'center' },
{ label: '质控品厂商', prop: 'qcsrc', visible: true, align: 'center' },
{ label: '波长', prop: 'bc', visible: true, align: 'center' },
{ label: '方法', prop: 'ff', visible: true, align: 'center' },
{ label: '校准品厂商', prop: 'standsrc', visible: true, align: 'center' },
])
const columnDragEnd = (data: any) => {
rightTolumns.value = data.columns
}
const zkphChange = (val: string) => {
rowClick(selectedRow.value)
}
const radioChange = (val: string) => {
if (val == "1") {
rightTolumns.value[6].label = 'Sd'
rightTolumns.value[6].visible = true
}
if (val == "2") {
rightTolumns.value[6].label = 'CV(%)'
rightTolumns.value[6].visible = true
}
if (val == "3") {
rightTolumns.value[6].visible = false
}
}
watch(zkphValue, (val) => {
if (val) {
radioChange(zkphValue.value)
}
}, { immediate: true })
const rowClick = (row: any) => {
selectedRow.value = row
const data = {
xmdh: row.xmdh,
yq: yq.value,
zkph: zkphValue.value,
}
queryQcSample(data).then((res: any) => {
rightTableData.value = res.data
})
}
const handleSave = () => {
}
const getParamsInfo = () => {
queryDataFromSample({ yq: yq.value }).then((res: any) => {
tableData.value = res.data
})
}
const getXmList = () => {
queryOldxminfo({ yq: yq.value, }).then((res: any) => {
leftTableData.value = res.data
rowClick(leftTableData.value[0])
})
}
watch(yq, (val) => {
if (val) {
getParamsInfo()
getXmList()
}
}, { immediate: true })
</script>
<style scoped lang="scss">
.tips {
font-family: SourceHanSansCN, SourceHanSansCN;
font-size: 16px;
color: #409eff;
font-weight: 600;
height: 32px;
}
.bb_box {
border-bottom: 1px solid #ccc;
padding-bottom: 10px;
margin-bottom: 10px;
.text {
font-family: SourceHanSansCN, SourceHanSansCN;
font-size: 14px;
color: #409eff;
display: inline-block;
width: 140px;
text-align: right;
margin-right: 10px;
}
.text_btn {
display: flex;
flex-direction: column;
align-items: center;
.el-button {
margin-left: 0;
margin-bottom: 10px;
}
}
}
.check-box {
display: flex;
align-items: center;
margin-bottom: 5px;
}
.bbh-box {
padding-top: 10px;
}
.table-row {
height: calc(100% - 385px);
.table-col {
height: 100%;
}
}
</style>

View File

@ -0,0 +1,162 @@
/**
* @file index.vue 规则设定
* @author: w
* @since: 2025-12-29
*/
<template>
<div class="app-container">
<div class="table-toolbar">
<el-button type="primary" plain icon="Check" @click="handleSave">保存</el-button>
</div>
<div class="tips">提示:左边的列表已经列出所有检验项目,请在质控项目前面打勾,右边是当前项目的质控规则</div>
<el-row :gutter="20" class="table-row">
<el-col :span="12" class="table-col">
<CustomTable ref="tableLeftRef" :data="tableLeftData" :columns="leftColumns" :config="tableConfig"
@row-click="rowClick">
<template #xmdh="{ row }">
{{ row.xmdh }} {{ row.xmmc }}
</template>
<template #zkxm="{ row }">
<el-checkbox v-model="row.zkxm" true-value="Y" false-value="N"></el-checkbox>
</template>
<template #qcuselog="{ row }">
<el-checkbox v-model="row.qcuselog" true-value="1" false-value="0"></el-checkbox>
</template>
<template #qcusepos="{ row }">
<el-checkbox v-model="row.qcusepos" true-value="1" false-value="0"></el-checkbox>
</template>
<template #qcvs="{ row }">
<el-checkbox v-model="row.qcvs" true-value="Y" false-value="N"></el-checkbox>
</template>
<template #qccv="{ row }">
<el-input v-model="row.qccv" class="full-width-input" />
</template>
</CustomTable>
</el-col>
<el-col :span="12" class="table-col">
<CustomTable ref="tableRef" :data="rightTableData" :columns="rightColumns" :config="tableConfig"
@selection-change="handleSelectionChange" />
</el-col>
</el-row>
</div>
</template>
<script setup lang="ts">
import { saveQCItem, queryRules } from '@/api/liswork/qualityControl'
// import { getDictData, dictData } from '@/hooks'
import { getComDicts } from '@/api/liswork/dict/ComDict'
import { queryOldxminfo } from '@/api/liswork/xtwh/xmReqItemVsApi'
import { ElMessage } from 'element-plus'
const yq = defineModel<string>()
const tableLeftData = ref<Array<any>>([])
const leftColumns = [
{ label: '项目代号', prop: 'xmdh', visible: true, width: 200, slot: 'xmdh' },
{ label: '质控', prop: 'zkxm', visible: true, align: 'center', slot: 'zkxm' }, //Y/N
{ label: '值取对数', prop: 'qcuselog', visible: true, align: 'center', slot: 'qcuselog' },
{ label: '半定量值', prop: 'qcusepos', visible: true, align: 'center', slot: 'qcusepos' },
{ label: '室间质评项目', prop: 'qcvs', visible: true, align: 'center', slot: 'qcvs' },
{ label: '规定CV值(%)', prop: 'qccv', visible: true, align: 'center', slot: 'qccv', showOverflowTooltip: false },
];
const rightTableData = ref<Array<any>>([])
const tableRef = ref()
const rightColumns = [
{ label: '有效', type: 'selection', visible: true, width: 50, align: 'center' },
{ label: '规则', prop: 'zddh', visible: true, width: 100, align: 'center' },
{ label: '规则说明', prop: 'zdmc', visible: true, align: 'center' },
];
const tableConfig = {
border: true,
height: '100%',
highlightCurrentRow: true,
}
const zddhStr = ref<string>('')
const currentIndex = ref<number>(0)
const handleSelectionChange = (selection: any) => {
zddhStr.value = selection.map((item: any) => { return item.zddh }).join(',')
tableLeftData.value[currentIndex.value].qcRules = {
rules: zddhStr.value,
xmdh: tableLeftData.value[currentIndex.value].xmdh,
yq: yq.value
}
}
const handleSave = () => {
const arr = tableLeftData.value.map((item: any) => {
return {
qcRules: item.qcRules || null,
xmInfoNew: item
}
})
saveQCItem(arr).then((res: any) => {
if (res.code == 0) {
ElMessage.success('保存成功')
}
})
}
const getRuleList = () => {
queryOldxminfo({ yq: yq.value }).then((res: any) => {
tableLeftData.value = res.data || []
getSelectRule(tableLeftData.value[0])
})
getComDicts({ zdlb: 'ZKGZ' }).then((res: any) => {
rightTableData.value = res.data || []
})
}
const rowClick = (row: any) => {
getSelectRule(row)
currentIndex.value = tableLeftData.value.findIndex((item: any) => item.xmdh === row.xmdh)
}
const getSelectRule = (row: any) => {
queryRules({ yq: yq.value, xmdh: row.xmdh }).then((res: any) => {
let arr = []
const info = tableLeftData.value[currentIndex.value]?.qcRules || {}
if (info?.rules) {
arr = info.rules.split(',')
} else {
if (res.data && res.data.rules) {
arr = res.data.rules.split(',')
}
}
tableRef.value.clearSelection()
arr.forEach((item: any) => {
const index = rightTableData.value.findIndex((val: any) => val.zddh === item)
if (index > -1) {
tableRef.value.toggleRowSelection(rightTableData.value[index], true)
}
})
})
}
watch(yq, (val) => {
if (val) {
getRuleList()
}
}, { immediate: true })
</script>
<style scoped lang="scss">
.table-row {
height: calc(100% - 135px);
margin-top: 10px;
.table-col {
height: 100%;
}
}
.tips {
font-family: SourceHanSansCN, SourceHanSansCN;
font-size: 16px;
color: #409eff;
font-weight: 600;
}
</style>

View File

@ -0,0 +1,93 @@
/**
* @file index.vue 规则设定
* @author: w
* @since: 2025-12-29
*/
<template>
<div class="app-container">
<div class="table-toolbar">
<el-button type="primary" plain icon="Check" @click="handleSave">保存</el-button>
</div>
<div class="tips">说明:1_2S作为一个警告规则,如果选中这个规则,则表示所有其他失控规则必须先满足说明:25,则视为在控。</div>
<div class="table-row" style="margin-top: 10px;">
<CustomTable ref="tableRef" :data="tableData" :columns="columns" :config="tableConfig"
@selection-change="handleSelectionChange" />
</div>
</div>
</template>
<script setup lang="ts">
import { saveOpt } from '@/api/liswork/qualityControl'
// import { getDictData, dictData } from '@/hooks'
import { getComDicts } from '@/api/liswork/dict/ComDict'
import { queryValue } from '@/api/liswork/xtwh/ComOpt'
import { ElMessage } from 'element-plus'
const yq = defineModel<string>()
const tableData = ref<Array<any>>([])
const tableRef = ref()
const columns = [
{ label: '有效', type: 'selection', visible: true, width: 50, align: 'center' },
{ label: '规则', prop: 'zddh', visible: true, width: 200, align: 'center' },
{ label: '规则说明', prop: 'zdmc', visible: true, align: 'center' },
];
const tableConfig = {
border: true,
height: '100%',
highlightCurrentRow: true,
}
const zddhStr = ref<string>('')
const handleSelectionChange = (selection: any) => {
zddhStr.value = selection.map((item: any) => { return item.zddh }).join(',')
}
const handleSave = () => {
if (!zddhStr.value) return ElMessage.warning('请至少选择一项规则')
saveOpt([{ yq: yq.value, xxqz: zddhStr.value, xxdh: 'QCRULES' }]).then((res: any) => {
if (res.code == 0) {
ElMessage.success('保存成功')
}
})
}
const getRuleList = () => {
getComDicts({ zdlb: 'ZKGZ' }).then((res: any) => {
tableData.value = res.data || []
queryValue({ yq: yq.value, xxdh: 'QCRULES' }).then((res: any) => {
tableRef.value.clearSelection()
if (res.data) {
const arr = res.data.split(',')
arr.forEach((item: any) => {
const index = tableData.value.findIndex((val: any) => val.zddh === item)
if (index > -1) {
tableRef.value.toggleRowSelection(tableData.value[index], true)
}
})
}
})
})
}
watch(yq, (val) => {
if (val) {
getRuleList()
}
}, { immediate: true })
</script>
<style scoped lang="scss">
.table-row {
height: calc(100% - 135px);
}
.tips {
font-family: SourceHanSansCN, SourceHanSansCN;
font-size: 16px;
color: #409eff;
font-weight: 600;
}
</style>

View File

@ -0,0 +1,348 @@
/**
* @file index.vue 质控
* @author: w
* @since: 2025-12-29
*/
<template>
<div class="app-container">
<el-row :gutter="20" class="main-row">
<!-- 左侧树区域 -->
<el-col :span="4" :xs="24" class="tree-col">
<el-input v-model="deptName" placeholder="请输入仪器名称" clearable prefix-icon="Search" class="tree-search-input" />
<el-radio-group v-model="radiotype" @change="radioChange">
<el-radio value="1" size="large">仪器组</el-radio>
<el-radio value="2" size="large">所有仪器</el-radio>
</el-radio-group>
<div class="tree-container">
<el-tree :data="deptOptions" :props="treeProps" expand-on-click-node :filter-node-method="filterNode"
ref="deptTreeRef" node-key="id" highlight-current :expanded-keys="expandedKeys"
@node-click="handleNodeClick">
<!-- 自定义树节点模板:新增newflag徽章 -->
<template #default="{ node, data }">
<span class="tree-node-content">
{{ node.label }}
<!-- 新节点徽章:newflag=1时显示黄色"新"标 -->
<el-badge v-if="data.newflag === '1'" value="new" class="new-node-badge" />
</span>
</template>
</el-tree>
</div>
</el-col>
<!-- 右侧列表区域 -->
<el-col :span="20" :xs="24" class="table-col">
<el-row :gutter="10" class="yq-title-row">
<h1 class="yq-title">仪器: {{ yqmc }}</h1>
</el-row>
<el-tabs v-model="activeName" type="card" class="tabs_box">
<el-tab-pane v-for="tab in tabList" :key="tab.key" :label="tab.label" :name="tab.key" />
</el-tabs>
<!-- 动态组件渲染 -->
<component :is="currentComponent" :key="activeName" v-model="queryParams.yq" @updateTree="getDeptTree" />
</el-col>
</el-row>
</div>
</template>
<script setup name="ComOpt" lang="ts">
import { ref, watch, nextTick, computed } from 'vue'
import { ElMessage, ElTree, TreeNode, ElMessageBox, ElBadge } from 'element-plus'
import { paramList, paramTree, getdef, updateParam } from '@/api/liswork/xtwh/ComOpt'
import { getFirstLetter } from '@/utils/pinyin'
import { formatDict, getDictData, dictData } from '@/hooks';
import SetRule from './components/setRule/index.vue';
import SetProject from './components/setProject/index.vue';
import SetParams from './components/setParams/index.vue';
// ========== TypeScript 类型定义 ==========
interface InstrTreeNode {
id: string
label: string
lx: string
newflag: string // 新增newflag字段
children?: InstrTreeNode[]
}
interface ParamTreeResponse {
code: string
msg: string
data: InstrTreeNode[]
url: null | string
method: null | string
params: null | any
problemId: null | string
}
interface QueryParams {
yq?: string | undefined
xxdh?: string | undefined
xxqz?: string | undefined
groupId?: string | undefined
}
const radiotype = ref<string>("1")
// ========== 响应式变量 ==========
// 树相关
const deptTreeRef = ref<InstanceType<typeof ElTree>>()
const deptOptions = ref<InstrTreeNode[]>([])
const deptName = ref<string>("")
const yqmc = ref<string>("")
const expandedKeys = ref<string[]>([])
const isFiltering = ref<boolean>(false)
const treeProps = ref({
label: 'label',
children: 'children',
isLeaf: (data: Record<string, any>) => !data.children || data.children.length === 0
})
// 查询参数
const queryParams = ref<QueryParams>({
yq: undefined,
xxdh: undefined,
xxqz: undefined,
groupId: undefined
})
const tabList = [
{ key: 'SetRule', label: '质控规则设定', component: SetRule },
{ key: 'SetProject', label: '质控项目设定', component: SetProject },
{ key: 'SetParams', label: '质控品参数设定', component: SetParams }
]
const activeName = ref('SetParams')
// 计算当前要渲染的组件
const currentComponent = computed(() => {
return tabList.find(tab => tab.key === activeName.value)?.component || 'SetRule'
})
const treeData = ref<InstrTreeNode[]>([])
const radioChange = (val: string) => {
if (val == "1") {
deptOptions.value = treeData.value
} else {
const result = [];
// 遍历外层数组的每个分组
for (let i = 0; i < treeData.value.length; i++) {
const group = treeData.value[i];
if (group.children && Array.isArray(group.children)) {
for (let j = 0; j < group.children.length; j++) {
result.push(group.children[j]);
}
}
}
deptOptions.value = result
}
}
// ========== 树操作核心方法 ==========
const forceExpandSingleNode = (nodeId: string): void => {
expandedKeys.value = []
nextTick(() => {
expandedKeys.value = [nodeId]
// deptTreeRef.value?.expand(nodeId, false)
})
}
const handleNodeClick = (data: InstrTreeNode): void => {
if (isFiltering.value) {
queryParams.value.groupId = data.id === "-1" ? undefined : data.id
return
}
if (data.children && data.children.length > 0) {
const isExpanded = expandedKeys.value.includes(data.id)
expandedKeys.value = isExpanded ? [] : [data.id]
!isExpanded && forceExpandSingleNode(data.id)
} else {
queryParams.value.yq = data.id
yqmc.value = data.label
}
}
// ========== 检索过滤逻辑 ==========
const filterNode = (value: string, data: any, _node: TreeNode): boolean => {
if (!value) return true
const keyword = value.trim().toUpperCase()
const nodeLabel = data.label?.toLowerCase() || ''
const nodePinyinFirst = data.label ? getFirstLetter(data.label).toUpperCase() : ''
const currentMatch = nodeLabel.includes(keyword.toLowerCase()) || nodePinyinFirst.includes(keyword)
const childrenMatch = (data.children || []).some((child: any) => filterNode(value, child, _node))
return currentMatch || childrenMatch
}
const debounceFilter = (() => {
let timer: number | undefined
return (val: string): void => {
clearTimeout(timer)
timer = setTimeout(() => {
isFiltering.value = true
deptTreeRef.value?.filter(val)
if (val) {
expandedKeys.value = []
const expandMatched = (nodes: InstrTreeNode[]) => {
nodes.forEach(node => {
if (filterNode(val, node, {} as TreeNode) && node.children?.length) {
expandedKeys.value.push(node.id)
}
node.children && expandMatched(node.children)
})
}
expandMatched(deptOptions.value)
} else {
expandedKeys.value = []
}
isFiltering.value = false
}, 300)
}
})()
watch(deptName, debounceFilter, { immediate: false })
// ========== 业务逻辑 ==========
const getDeptTree = async (): Promise<void> => {
try {
const response = await paramTree() as ParamTreeResponse
if (response.code === "0" && response.data) {
treeData.value = response.data
radioChange('1')
if (!expandedKeys.value.length) {
// 默认展开第一个一级节点的子级
nextTick(() => {
if (deptOptions.value.length > 0) {
const firstLevelNode = deptOptions.value[0]
if (firstLevelNode && firstLevelNode.children && firstLevelNode.children.length > 0) {
expandedKeys.value = [firstLevelNode.id]
const firstChildNode = firstLevelNode.children[0]
queryParams.value.yq = firstChildNode.id
yqmc.value = firstChildNode.label
deptTreeRef.value?.setCurrentKey(firstChildNode.id)
}
}
})
}
} else {
ElMessage.warning(response.msg || "获取树数据失败")
}
} catch (error) {
ElMessage.error(`获取树数据异常:${(error as Error).message}`)
}
}
onMounted(() => {
})
// ========== 初始化 ==========
getDeptTree()
</script>
<style lang="scss" scoped>
.app-container {
width: 100%;
height: 90vh;
display: flex;
flex-direction: column;
overflow: hidden;
padding: 8px;
box-sizing: border-box;
}
.main-row {
display: flex;
flex: 1;
height: 100%;
overflow: hidden;
}
.tree-col {
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
.tree-container {
flex: 1;
height: 100%;
overflow: auto;
background: #fff;
border: 1px solid #e6e6e6;
border-radius: 4px;
padding: 8px;
// 核心:树选中节点样式
:deep(.el-tree) {
--el-tree-node-transition-duration: 0.2s;
.el-tree-node__children {
transition: all var(--el-tree-node-transition-duration) ease-in-out;
}
.el-tree-node__expand-icon.is-leaf {
display: none;
}
// 选中节点的背景色(蓝色)+ 文字白色
.el-tree-node.is-current>.el-tree-node__content {
background-color: #409eff !important;
color: #ffffff !important;
}
// 选中节点hover样式(加深蓝色)
.el-tree-node.is-current>.el-tree-node__content:hover {
background-color: #1e88e5 !important;
}
// 树节点内容容器样式
.tree-node-content {
display: inline-flex;
align-items: center;
gap: 4px; // 徽章和文字间距
height: 100%;
}
// 新节点徽章样式
.new-node-badge {
--el-badge-font-size: 10px !important;
--el-badge-height: 16px !important;
--el-badge-padding: 0 4px !important;
--el-badge-warning-color: #FFEC24 !important; // 黄色徽章
--el-badge-warning-bg-color: #fff7e6 !important; // 浅黄色背景
--el-badge-border-radius: 2px !important; // 圆角优化
// 徽章文字样式
.el-badge__content {
font-size: 10px !important;
line-height: 16px !important;
padding: 0 4px !important;
height: 16px !important;
}
}
}
}
}
.table-col {
height: 100%;
// 仪器标题行样式
.yq-title-row {
margin-bottom: 8px;
.yq-title {
font-family: SourceHanSansCN, SourceHanSansCN;
font-size: 16px; // 缩小h1字体(默认24px→16px)
color: #409eff; // 蓝色(Element Plus主题色)
font-weight: 600;
margin: 0; // 清除默认margin
line-height: 1.2;
}
}
}
</style>