Files
his/openhis-ui-vue3/src/views/doctorstation/components/examination/examinationApplication.vue
2026-04-03 16:47:03 +08:00

933 lines
30 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="exam-app-container">
<!-- ====== 顶部卡片申请单列表 ====== -->
<div class="top-section">
<div class="section-header">
<span class="section-title">检查项目 ({{ applicationList.length }})</span>
<div class="header-actions">
<el-button type="primary" @click="handleAdd" icon="Plus">新增</el-button>
<el-button type="success" @click="handleSave" icon="Finished">保存</el-button>
</div>
</div>
<el-table
v-loading="loading"
:data="applicationList"
:max-height="200"
highlight-current-row
@row-click="handleRowClick"
border
size="small"
:header-cell-style="{ background: '#f5f5f5', color: '#303133', fontWeight: '600' }"
>
<el-table-column type="selection" width="40" align="center" />
<el-table-column label="申请ID" prop="id" width="80" align="center" />
<el-table-column label="申请单号" prop="applyNo" min-width="140" align="center" />
<el-table-column label="申检部位" prop="inspectionArea" min-width="100" align="center" />
<el-table-column label="申请医生" prop="applyDocCode" min-width="90" align="center" />
<el-table-column label="急" prop="isUrgent" width="50" align="center">
<template #default="{ row }">
<el-checkbox v-model="row.isUrgent" :true-label="1" :false-label="0" disabled />
</template>
</el-table-column>
<el-table-column label="收费" prop="isCharged" width="50" align="center">
<template #default="{ row }">
<el-checkbox v-model="row.isCharged" :true-label="1" :false-label="0" disabled />
</template>
</el-table-column>
<el-table-column label="退费" prop="isRefunded" width="50" align="center">
<template #default="{ row }">
<el-checkbox v-model="row.isRefunded" :true-label="1" :false-label="0" disabled />
</template>
</el-table-column>
<el-table-column label="执行" prop="isExecuted" width="50" align="center">
<template #default="{ row }">
<el-checkbox v-model="row.isExecuted" :true-label="1" :false-label="0" disabled />
</template>
</el-table-column>
<el-table-column label="金额" prop="totalAmount" width="90" align="right">
<template #default="{ row }">
{{ (row.totalAmount || 0).toFixed(2) }}
</template>
</el-table-column>
<el-table-column label="操作" width="80" align="center" fixed="right">
<template #default="{ row }">
<el-button link @click.stop="handlePrint(row)" title="打印">
<el-icon><Printer /></el-icon>
</el-button>
<el-button link type="danger" @click.stop="handleDelete(row)" title="删除">
<el-icon><Delete /></el-icon>
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- ====== 底部主区左表单 + 右分类 ====== -->
<div class="bottom-section">
<!-- 表单区 -->
<div class="form-panel">
<el-tabs v-model="activeDetailTab" class="form-tabs">
<!-- TAB1检查申请单 -->
<el-tab-pane label="检查申请单" name="applyForm">
<el-form ref="formRef" :model="form" :rules="rules" size="small" class="apply-form">
<el-row :gutter="12">
<el-col :span="8">
<el-form-item label="申请单号" prop="applyNo">
<el-input v-model="form.applyNo" readonly />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="姓名" prop="patientName">
<el-input v-model="form.patientName" readonly />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="就诊卡号" prop="medicalrecordNumber">
<el-input v-model="form.medicalrecordNumber" readonly />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="12">
<el-col :span="8">
<el-form-item label="费用性质" prop="natureofCost">
<el-select v-model="form.natureofCost" style="width:100%">
<el-option label="自费医疗" value="自费医疗" />
<el-option label="医保报销" value="医保报销" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="申请日期" prop="applyTime">
<el-date-picker v-model="form.applyTime" type="date" style="width:100%"
format="YYYY-MM-DD" value-format="YYYY-MM-DD HH:mm:ss" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="申请科室" prop="applyDeptCode">
<el-input v-model="form.applyDeptCode" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="12">
<el-col :span="8">
<el-form-item label="申请医生" prop="applyDocCode">
<el-input v-model="form.applyDocCode" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="执行科室" prop="performDeptCode">
<el-input v-model="form.performDeptCode" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="12">
<el-col :span="24">
<el-form-item label="诊断描述" prop="clinicDesc">
<el-input v-model="form.clinicDesc" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="12">
<el-col :span="8">
<el-form-item label="禁忌症" prop="contraindication">
<el-input v-model="form.contraindication" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="临床诊断" prop="clinicalDiag">
<el-input v-model="form.clinicalDiag" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="病史摘要" prop="medicalHistorySummary">
<el-input v-model="form.medicalHistorySummary" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="12">
<el-col :span="24">
<el-form-item label="检查目的" prop="purposeDesc">
<el-input v-model="form.purposeDesc" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="12">
<el-col :span="8">
<el-form-item label="体格检查">
<el-input v-model="form.purposeofInspection" placeholder="T(摄氏度) P次/分 R次/分 BF" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="申检部位">
<el-input v-model="form.inspectionArea" readonly />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="检查方法">
<el-select v-model="form.inspectionMethod" placeholder="请选择" clearable filterable style="width: 100%;">
<el-option
v-for="method in availableMethods"
:key="method.id"
:label="method.name"
:value="method.name"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="12">
<el-col :span="8">
<el-form-item label="备注" prop="applyRemark">
<el-input v-model="form.applyRemark" />
</el-form-item>
</el-col>
<el-col :span="16">
<el-form-item label="状态">
<el-checkbox v-model="form.isUrgent" :true-label="1" :false-label="0"></el-checkbox>
<el-checkbox v-model="form.isCharged" :true-label="1" :false-label="0" disabled>收费</el-checkbox>
<el-checkbox v-model="form.isRefunded" :true-label="1" :false-label="0" disabled>退费</el-checkbox>
<el-checkbox v-model="form.isExecuted" :true-label="1" :false-label="0" disabled>执行</el-checkbox>
</el-form-item>
</el-col>
</el-row>
</el-form>
</el-tab-pane>
<!-- TAB2检查明细 -->
<el-tab-pane label="检查明细" name="applyDetail">
<el-table
ref="detailTableRef"
:data="selectedItems"
border
size="small"
style="width:100%"
:max-height="350"
:header-cell-style="{ background: '#f5f5f5', color: '#303133' }"
>
<el-table-column label="行" type="index" width="45" align="center" />
<el-table-column label="检查项目" prop="name" min-width="120" />
<el-table-column label="部位" prop="applyPart" min-width="90">
<template #default="scope">
<el-input v-model="scope.row.applyPart" size="small" />
</template>
</el-table-column>
<el-table-column label="单位" prop="unit" width="55" align="center" />
<el-table-column label="总量" prop="quantity" width="70" align="center">
<template #default="scope">
<el-input-number v-model="scope.row.quantity" :min="1" size="small" :controls="false" style="width:100%" />
</template>
</el-table-column>
<el-table-column label="单价" prop="price" width="75" align="right" />
<el-table-column label="金额" width="80" align="right">
<template #default="scope">
{{ ((scope.row.price || 0) * (scope.row.quantity || 1)).toFixed(2) }}
</template>
</el-table-column>
<el-table-column label="类型" prop="checkType" width="70" align="center" />
<el-table-column label="国码" prop="nationalCode" width="70" align="center" />
<el-table-column label="自费" width="50" align="center">
<template #default>
<el-checkbox disabled />
</template>
</el-table-column>
</el-table>
<div class="total-row">
合计<span class="total-amount">{{ totalAmountCalc }}</span>
</div>
</el-tab-pane>
</el-tabs>
</div>
<!-- 检查项目分类面板 -->
<div class="category-panel">
<div class="panel-top">
<!-- 左侧分类搜索 + 折叠树 -->
<div class="category-left">
<div class="panel-label">检查项目分类</div>
<el-input
v-model="dictSearchKey"
placeholder="搜索检查项目(支持拼音首字母)"
prefix-icon="Search"
clearable
size="small"
class="search-input"
/>
<!-- 分类折叠列表 -->
<div class="collapse-scroll" v-loading="dictLoading">
<div v-if="filteredCategoryList.length === 0" class="empty-hint">
{{ dictLoading ? '' : '暂无检查项目请在"检查项目设置"中配置' }}
</div>
<el-collapse v-else v-model="activeNames">
<el-collapse-item
v-for="cat in filteredCategoryList"
:key="cat.typeId"
:name="cat.typeId"
>
<template #title>
<span class="cat-title">{{ cat.categoryName }}</span>
</template>
<div
v-for="item in cat.items"
:key="item.id"
class="item-row"
>
<el-checkbox
v-model="item.checked"
@change="(val) => handleItemSelect(val, item, cat)"
class="item-checkbox"
>
{{ item.name }}
</el-checkbox>
<span class="item-price">¥{{ item.price }}</span>
</div>
</el-collapse-item>
</el-collapse>
</div>
</div>
<!-- 右侧已选择 tags -->
<div class="selected-panel">
<div class="panel-label">已选择:</div>
<div class="selected-tags">
<div v-if="selectedItems.length === 0" class="empty-selected"></div>
<el-tag
v-else
v-for="(item, idx) in selectedItems"
:key="idx"
closable
size="small"
@close="handleRemoveItem(idx, item)"
class="selected-tag"
>
{{ item.name }} ¥{{ item.price }}
</el-tag>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, reactive, computed, watch, onMounted, nextTick } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { Printer, Delete } from '@element-plus/icons-vue';
import useUserStore from '@/store/modules/user';
import request from '@/utils/request';
import { listCheckMethod } from '@/api/system/checkType';
const props = defineProps({
patientInfo: { type: Object, default: () => ({}) },
activeTab: { type: String, default: '' }
});
// 保存成功后通知父组件刷新医嘱列表
const emit = defineEmits(['saved']);
const userStore = useUserStore();
const loading = ref(false);
const dictLoading = ref(false);
const activeDetailTab = ref('applyForm');
const applicationList = ref([]);
const selectedItems = ref([]);
const detailTableRef = ref(null);
const formRef = ref(null);
// ====== 表单数据 ======
const form = reactive({
applyNo: '',
patientName: '',
patientId: '',
visitNo: '',
applyDeptCode: '',
performDeptCode: '',
applyDocCode: '',
applyTime: '',
medicalrecordNumber: '',
natureofCost: '自费医疗',
clinicDesc: '',
contraindication: '',
medicalHistorySummary: '',
purposeofInspection: '',
inspectionArea: '',
inspectionMethod: '',
applyRemark: '',
clinicalDiag: '',
purposeDesc: '',
isUrgent: 0,
pregnancyState: 0,
allergyDesc: '',
applyStatus: 0,
isCharged: 0,
isRefunded: 0,
isExecuted: 0,
examTypeCode: '' // 检查类型编码,必填字段,保存时从已选项目自动推导
});
const rules = {
natureofCost: [{ required: true, message: '请选择费用性质', trigger: 'change' }],
applyDeptCode: [{ required: true, message: '请输入申请科室', trigger: 'blur' }],
applyDocCode: [{ required: true, message: '请输入申请医生', trigger: 'blur' }],
performDeptCode: [{ required: true, message: '请输入执行科室', trigger: 'blur' }],
clinicDesc: [{ required: true, message: '请输入诊断描述', trigger: 'blur' }],
clinicalDiag: [{ required: true, message: '请输入临床诊断', trigger: 'blur' }],
medicalHistorySummary: [{ required: true, message: '请输入病史摘要', trigger: 'blur' }],
purposeDesc: [{ required: true, message: '请输入检查目的', trigger: 'blur' }]
};
// ====== 检查项目分类 ======
const categoryList = ref([]); // 原始分类+项目数据
const dictSearchKey = ref('');
const activeNames = ref([]); // 当前展开的折叠项
const allMethods = ref([]);
// 加载所有检查方法
async function loadAllMethods() {
try {
const res = await listCheckMethod(); // 使用已导入的或者直接利用 request 请求
let methods = [];
if (res && res.data) {
if (Array.isArray(res.data)) {
methods = res.data;
} else if (res.data.records) {
methods = res.data.records;
} else if (res.data.data && Array.isArray(res.data.data)) {
methods = res.data.data;
}
} else if (Array.isArray(res)) {
methods = res;
} else if (res && res.rows) {
methods = res.rows;
}
allMethods.value = methods;
} catch (err) {
console.error('加载检查方法失败', err);
}
}
onMounted(async () => {
await loadAllMethods();
await loadCategoryList();
});
// 动态可用的检查方法(根据已选部位所属的检查类型进行过滤)
const normalizeTypeValue = value => String(value ?? '').trim().toLowerCase();
const availableMethods = computed(() => {
// 获取当前已选部位的检查类型(可取第一个选中的部位的 checkType
const currentType = form.examTypeCode || (selectedItems.value.length > 0 ? selectedItems.value[0].checkType : '');
const normalizedCurrentType = normalizeTypeValue(currentType);
if (normalizedCurrentType) {
// 兼容脏数据method 的类型可能落在 checkType/type/typeCode/code/typeName/categoryName 中
const filtered = allMethods.value.filter(m => {
const typeCandidates = [
m.checkType,
m.type,
m.typeCode,
m.code,
m.typeName,
m.categoryName
].map(normalizeTypeValue).filter(Boolean);
return typeCandidates.includes(normalizedCurrentType);
});
return filtered.length > 0 ? filtered : allMethods.value;
}
return allMethods.value;
});
// 当可选方法列表改变时,如果当前选中的方法不在新列表中,则清空
watch(availableMethods, (newMethods) => {
if (form.inspectionMethod && !newMethods.find(m => m.name === form.inspectionMethod)) {
form.inspectionMethod = '';
}
});
/**
* 加载检查类型(分类)和检查项目(部位/项目),按类型分组展示
*/
async function loadCategoryList() {
dictLoading.value = true;
try {
// 1. 加载检查类型(分类名称),只取父级
const typeRes = await request({
url: '/system/check-type/list',
method: 'get',
params: { pageNo: 1, pageSize: 500 } // 取全量分类数据
});
let types = [];
if (typeRes && typeRes.data) {
if (Array.isArray(typeRes.data)) {
types = typeRes.data;
} else if (typeRes.data.records) {
types = typeRes.data.records;
} else if (typeRes.data.data && Array.isArray(typeRes.data.data)) {
types = typeRes.data.data;
}
} else if (Array.isArray(typeRes)) {
types = typeRes;
} else if (typeRes && typeRes.rows) {
types = typeRes.rows;
}
// 2. 加载检查项目(检查部位项目)
const partRes = await request({ url: '/check/part/list', method: 'get' });
let parts = [];
if (partRes && partRes.data) {
if (Array.isArray(partRes.data)) {
parts = partRes.data;
} else if (partRes.data.records) {
parts = partRes.data.records;
} else if (partRes.data.data && Array.isArray(partRes.data.data)) {
parts = partRes.data.data;
}
} else if (Array.isArray(partRes)) {
parts = partRes;
} else if (partRes && partRes.rows) {
parts = partRes.rows;
}
// 3. 按 checkType 归类
const dict = [];
for (const t of types) {
dict.push({
typeId: t.id,
typeCode: t.code, // 保存 code 用于后备匹配
orgType: t.type, // 保存 type 用于后备匹配
typeName: t.name, // 保存 name
categoryName: t.name,
items: []
});
}
const unclassified = [];
for (const p of parts) {
const mapped = {
id: p.id,
name: p.name,
price: p.price || 0,
serviceFee: p.serviceFee || 0,
unit: '次',
checkType: p.checkType || '',
nationalCode: p.nationalCode || '',
checked: false
};
// 增强匹配逻辑:部位的 checkType (如 'ECG', 'CT') 优先去匹配大类的 orgType
// 如果大类的 type 字段脏了(比如填了中文),则尝试匹配 code甚至是分类名称
const target = dict.find(d =>
d.orgType === p.checkType ||
d.typeCode === p.checkType ||
d.typeName === p.checkType
);
if (target) target.items.push(mapped);
else unclassified.push(mapped);
}
if (unclassified.length > 0) {
dict.push({ typeId: 'uncls', typeCode: '', categoryName: '其他', items: unclassified });
}
categoryList.value = dict.filter(d => d.items.length > 0);
// 默认展开第一个
if (categoryList.value.length > 0) {
activeNames.value = [categoryList.value[0].typeId];
}
} catch (err) {
console.error('加载检查项目分类失败', err);
} finally {
dictLoading.value = false;
}
}
/** 关键词过滤后的分类列表 */
const filteredCategoryList = computed(() => {
if (!dictSearchKey.value) return categoryList.value;
const key = dictSearchKey.value.toLowerCase();
return categoryList.value.map(cat => ({
...cat,
items: cat.items.filter(item => (item.name || '').toLowerCase().includes(key))
})).filter(cat => cat.items.length > 0);
});
// ====== 合计 ======
const totalAmountCalc = computed(() => {
const total = selectedItems.value.reduce((sum, item) => {
return sum + (item.price * (item.quantity || 1));
}, 0);
return total.toFixed(2);
});
// 监听已选项:自动更新申检部位
watch(selectedItems, () => {
form.inspectionArea = selectedItems.value.map(i => i.name).join('+');
form.isCharged = selectedItems.value.length > 0 ? 1 : 0;
}, { deep: true });
// 监听患者变化
watch(() => props.patientInfo, (newVal) => {
if (newVal?.encounterId) {
initPatientForm(newVal);
getList();
}
}, { immediate: true, deep: true });
watch(() => props.activeTab, (val) => {
if (val === 'examination') getList();
});
function initPatientForm(patient) {
form.patientName = patient.patientName || '';
form.medicalrecordNumber = patient.busNo || patient.visitNo || '';
form.patientId = patient.patientId || '';
form.visitNo = patient.visitNo || '';
form.applyDeptCode = userStore.orgName || patient.organizationName || '';
form.applyDocCode = userStore.nickName || '';
}
// ====== 申请单 CRUD ======
function getList() {
loading.value = true;
request({
url: '/exam/apply/list',
method: 'get',
params: { visitNo: props.patientInfo?.visitNo || '' }
}).then(res => {
applicationList.value = res.rows || res.data || [];
}).catch(err => console.error('获取申请单列表失败', err))
.finally(() => { loading.value = false; });
}
function handleAdd() {
formRef.value?.resetFields();
Object.assign(form, {
applyNo: '', patientId: props.patientInfo?.patientId || '',
visitNo: props.patientInfo?.visitNo || '',
applyDeptCode: userStore.orgName || '',
performDeptCode: '',
applyDocCode: userStore.nickName || '',
applyTime: new Date().toISOString().split('T')[0] + ' 12:00:00',
medicalrecordNumber: props.patientInfo?.busNo || '',
natureofCost: '自费医疗',
clinicDesc: '', contraindication: '', medicalHistorySummary: '',
purposeofInspection: '', inspectionArea: '', inspectionMethod: '',
applyRemark: '', clinicalDiag: '', purposeDesc: '',
isUrgent: 0, pregnancyState: 0, allergyDesc: '',
applyStatus: 0, isCharged: 0, isRefunded: 0, isExecuted: 0
});
selectedItems.value = [];
resetCategoryChecked();
activeDetailTab.value = 'applyForm';
}
function handleSave() {
formRef.value.validate(valid => {
if (!valid) return;
if (selectedItems.value.length === 0) {
ElMessage.warning('请至少选择一个检查明细项目');
return;
}
// 从已选项目推导检查类型编码(取第一个项目的 checkType如 CT / ECG / GI
const firstCheckType = selectedItems.value[0]?.checkType || 'unknown';
if (!form.examTypeCode) form.examTypeCode = firstCheckType;
const payload = {
...form,
encounterId: props.patientInfo?.encounterId || null,
patientIdNum: props.patientInfo?.patientId || null,
items: selectedItems.value.map((item, index) => ({
itemCode: String(item.id),
itemName: item.name,
bodyPartCode: item.checkType || 'unknown',
itemFee: item.price,
performDeptCode: form.performDeptCode || '',
itemStatus: 0,
itemSeq: index + 1
}))
};
request({
url: '/exam/apply',
method: payload.applyNo ? 'put' : 'post',
data: payload
}).then(res => {
ElMessage.success('保存成功');
getList();
if (res.data) form.applyNo = res.data;
// 通知父组件刷新医嘱列表
emit('saved');
});
});
}
function handleRowClick(row) {
Object.assign(form, row);
selectedItems.value = [];
activeDetailTab.value = 'applyForm';
request({ url: `/exam/apply/${row.applyNo}`, method: 'get' }).then(res => {
const d = res.data || res;
if (d.data) Object.assign(form, d.data);
if (d.items && Array.isArray(d.items)) {
selectedItems.value = d.items.map(m => ({
id: m.itemCode, name: m.itemName,
price: m.itemFee || 0, quantity: 1,
serviceFee: 0, unit: '次',
applyPart: m.itemName,
checkType: m.bodyPartCode || '',
nationalCode: '', checked: true
}));
syncCategoryChecked();
}
});
}
function handlePrint(row) { ElMessage.info('打印申请单:' + row.applyNo); }
function handleDelete(row) {
ElMessageBox.confirm('确认删除该检查申请单吗?', '警告', { type: 'warning' }).then(() => {
request({ url: `/exam/apply/${row.applyNo}`, method: 'delete' }).then(() => {
ElMessage.success('删除成功');
getList();
if (form.applyNo === row.applyNo) handleAdd();
});
});
}
// ====== 勾选逻辑 ======
function handleItemSelect(checked, item, cat) {
if (checked) {
selectedItems.value.push({
id: item.id, name: item.name,
price: item.price, quantity: 1,
serviceFee: item.serviceFee || 0,
unit: item.unit || '次',
applyPart: item.name,
checkType: cat.typeCode || '',
nationalCode: item.nationalCode || '',
checked: true
});
} else {
const idx = selectedItems.value.findIndex(s => s.id === item.id);
if (idx > -1) selectedItems.value.splice(idx, 1);
}
// 有选项时切换到明细tab
if (selectedItems.value.length > 0) {
activeDetailTab.value = 'applyDetail';
nextTick(() => detailTableRef.value?.doLayout());
}
}
function handleRemoveItem(idx, item) {
selectedItems.value.splice(idx, 1);
// 取消对应 category 中的 checkbox
for (const cat of categoryList.value) {
const found = cat.items.find(x => x.id === item.id);
if (found) { found.checked = false; break; }
}
}
function resetCategoryChecked() {
for (const cat of categoryList.value)
for (const item of cat.items) item.checked = false;
}
function syncCategoryChecked() {
resetCategoryChecked();
const ids = new Set(selectedItems.value.map(s => s.id));
for (const cat of categoryList.value)
for (const item of cat.items)
if (ids.has(item.id)) item.checked = true;
}
defineExpose({ getList });
</script>
<style scoped>
.exam-app-container {
display: flex;
flex-direction: column;
gap: 10px;
padding: 8px;
height: 100%;
background: #f0f2f5;
}
/* 顶部申请单列表 */
.top-section {
background: #fff;
border-radius: 4px;
padding: 10px 12px;
box-shadow: 0 1px 3px rgba(0,0,0,0.06);
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.section-title {
font-size: 14px;
font-weight: 600;
color: #303133;
}
.header-actions {
display: flex;
gap: 8px;
}
/* 底部区域:左表单 + 右分类 */
.bottom-section {
display: flex;
gap: 10px;
flex: 1;
min-height: 0;
}
/* 左:表单面板 */
.form-panel {
flex: 1;
background: #fff;
border-radius: 4px;
padding: 10px 12px;
box-shadow: 0 1px 3px rgba(0,0,0,0.06);
overflow-y: auto;
min-width: 0;
}
.form-tabs :deep(.el-tabs__header) {
margin-bottom: 10px;
}
.apply-form :deep(.el-form-item) {
margin-bottom: 10px;
}
.apply-form :deep(.el-form-item__label) {
font-size: 12px;
}
.total-row {
margin-top: 8px;
text-align: right;
font-size: 13px;
color: #606266;
}
.total-amount {
font-size: 15px;
font-weight: bold;
color: #f56c6c;
margin-left: 4px;
}
/* 右:分类面板 */
.category-panel {
width: 380px;
flex-shrink: 0;
background: #fff;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0,0,0,0.06);
padding: 10px 12px;
display: flex;
flex-direction: column;
overflow: hidden;
}
.panel-top {
display: flex;
gap: 12px;
flex: 1;
min-height: 0;
}
.category-left {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
}
.panel-label {
font-size: 13px;
font-weight: 600;
color: #303133;
margin-bottom: 6px;
}
.search-input {
margin-bottom: 8px;
}
.collapse-scroll {
flex: 1;
overflow-y: auto;
}
.empty-hint {
color: #909399;
font-size: 12px;
text-align: center;
padding: 20px 0;
}
/* 检查项目分类折叠 */
.cat-title {
font-size: 13px;
font-weight: 500;
color: #303133;
}
.item-row {
display: flex;
align-items: center;
justify-content: space-between;
padding: 3px 4px;
}
.item-row:hover {
background: #f5f7fa;
}
.item-checkbox {
flex: 1;
overflow: hidden;
}
.item-checkbox :deep(.el-checkbox__label) {
font-size: 12px;
color: #303133;
}
.item-price {
font-size: 12px;
color: #1890FF;
flex-shrink: 0;
margin-left: 6px;
}
/* 已选择 tags */
.selected-panel {
width: 120px;
flex-shrink: 0;
display: flex;
flex-direction: column;
}
.selected-tags {
flex: 1;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 4px;
}
.selected-tag {
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.empty-selected {
color: #c0c4cc;
font-size: 12px;
}
/* 折叠组件细节 */
:deep(.el-collapse) {
border: none;
}
:deep(.el-collapse-item__header) {
font-size: 13px;
padding: 6px 0;
height: auto;
line-height: 1.5;
}
:deep(.el-collapse-item__content) {
padding-bottom: 4px;
}
:deep(.el-collapse-item__wrap) {
border: none;
}
</style>