Files
his/openhis-ui-vue3/src/views/maintainSystem/Inspection/index.vue
2026-01-16 16:32:36 +08:00

2741 lines
85 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="inspection-container">
<!-- 左侧导航 -->
<div class="side-nav">
<div
v-for="(navItem, index) in navItems"
:key="index"
class="nav-item"
:class="{ active: activeNav === index }"
@click="activeNav = index"
>
{{ navItem }}
</div>
</div>
<!-- 右侧主内容 -->
<div class="main-content">
<!-- 检验类型页面 -->
<template v-if="activeNav === 0">
<div class="header-actions">
<button class="add-new-btn" @click="addNewRow">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<line x1="12" y1="5" x2="12" y2="19"></line>
<line x1="5" y1="12" x2="19" y2="12"></line>
</svg>
新增
</button>
</div>
<div class="table-container" @click="handleTableClick">
<table class="data-table data-table-type">
<thead>
<tr>
<th style="width: 50px;"></th>
<th style="width: 130px;">*大类编码</th>
<th style="width: 200px;">*大类项目名称</th>
<th style="width: 160px;">*执行科室</th>
<th style="width: 120px;">序号</th>
<th style="width: 160px;">备注</th>
<th style="width: 200px; text-align: left; padding-left: 60px;">操作</th>
</tr>
</thead>
<tbody>
<tr
v-for="(row, index) in tableData"
:key="row.id"
:class="{ 'editing': editingRowId === row.id }"
>
<td>{{ index + 1 }}</td>
<td>
<template v-if="editingRowId === row.id">
<input v-model="row.code" type="text" :style="inputStyle">
</template>
<template v-else>
{{ row.code }}
</template>
</td>
<td>
<template v-if="editingRowId === row.id">
<input v-model="row.name" type="text" :style="inputStyle">
</template>
<template v-else>
{{ row.name }}
</template>
</td>
<td>
<template v-if="editingRowId === row.id">
<el-tree-select
v-model="row.department"
:data="departments"
:props="{
value: 'name',
label: 'name',
children: 'children',
}"
value-key="name"
placeholder="请选择科室"
check-strictly
:expand-on-click-node="false"
clearable
style="width: 100%;"
/>
</template>
<template v-else>
{{ row.department }}
</template>
</td>
<td>
<template v-if="editingRowId === row.id">
<input v-model="row.sortOrder" type="text" :style="inputStyle">
</template>
<template v-else>
{{ row.sortOrder }}
</template>
</td>
<td>
<template v-if="editingRowId === row.id">
<input v-model="row.remark" type="text" :style="inputStyle">
</template>
<template v-else>
{{ row.remark }}
</template>
</td>
<td class="action-cell">
<div
class="action-btn confirm-btn"
@click="handleConfirm(row)"
>
<svg v-if="editingRowId === row.id" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="20 6 9 17 4 12"></polyline>
</svg>
<span v-else></span>
</div>
<div
v-if="editingRowId !== row.id"
class="action-btn edit-btn"
@click="handleEdit(row)"
>
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path>
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path>
</svg>
</div>
<div
v-if="editingRowId !== row.id"
class="action-btn add-btn"
@click="handleAdd(row, index)"
>
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<line x1="12" y1="5" x2="12" y2="19"></line>
<line x1="5" y1="12" x2="19" y2="12"></line>
</svg>
</div>
<div
class="action-btn delete-btn"
@click="handleDelete(row.id)"
>
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="3 6 5 6 21 6"></polyline>
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
<line x1="10" y1="11" x2="10" y2="17"></line>
<line x1="14" y1="11" x2="14" y2="17"></line>
</svg>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- 页码区域 -->
<div class="pagination">
<div class="page-btn">上一页</div>
<div class="page-btn active">1</div>
<div class="page-btn">2</div>
<div class="page-btn">3</div>
<div class="page-btn">下一页</div>
</div>
</template>
<!-- 检验项目页面 -->
<template v-else-if="activeNav === 1">
<div class="page-header">
<h2>检验项目</h2>
</div>
<div class="filter-section">
<div class="filter-item">
<label>检验类型</label>
<select v-model="testTypeFilter" class="filter-select">
<option value="">选择检验类型</option>
<option v-for="type in testTypes" :key="type.value" :value="type.value">
{{ type.label }}
</option>
</select>
</div>
<div class="filter-item">
<label>名称</label>
<input v-model="nameFilter" type="text" class="filter-input" placeholder="名称/编码">
</div>
<div class="filter-item">
<label>费用套餐</label>
<select v-model="packageFilter" class="filter-select">
<option value="">选择费用套餐</option>
<option v-for="pkg in feePackages" :key="pkg.value" :value="pkg.value">
{{ pkg.label }}
</option>
</select>
</div>
<div class="filter-actions">
<button class="btn btn-primary add-btn" @click="addNewItem">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<line x1="12" y1="5" x2="12" y2="19"></line>
<line x1="5" y1="12" x2="19" y2="12"></line>
</svg>
新增
</button>
<button class="btn btn-secondary reset-btn" @click="resetFilters">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="3 6 5 6 21 6"></polyline>
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
<line x1="10" y1="11" x2="10" y2="17"></line>
<line x1="14" y1="11" x2="14" y2="17"></line>
</svg>
重置
</button>
<button class="btn btn-primary search-btn" @click="filterItems">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="11" cy="11" r="8"></circle>
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
</svg>
查询
</button>
<button class="btn btn-primary export-btn" @click="exportTable">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
<polyline points="7 10 12 15 17 10"></polyline>
<line x1="12" y1="15" x2="12" y2="3"></line>
</svg>
导出表格
</button>
</div>
</div>
<div class="table-container">
<table class="data-table">
<thead>
<tr>
<th></th>
<th>小类编码</th>
<th>小类项目名称</th>
<th>检验类型</th>
<th>费用套餐</th>
<th>样本类型</th>
<th>金额</th>
<th>序号</th>
<th>服务范围</th>
<th>下级医技类型</th>
<th>备注</th>
<th style="padding-left: 40px;">操作</th>
</tr>
</thead>
<tbody>
<tr
v-for="(item, index) in filteredInspectionItems"
:key="item.id"
:class="{ 'editing': editingRowId === item.id }"
>
<td>{{ index + 1 }}</td>
<td>
<template v-if="editingRowId === item.id">
<input v-model="item.code" type="text" :style="inputStyle">
</template>
<template v-else>
{{ item.code }}
</template>
</td>
<td>
<template v-if="editingRowId === item.id">
<input v-model="item.name" type="text" :style="inputStyle">
</template>
<template v-else>
{{ item.name }}
</template>
</td>
<td>
<template v-if="editingRowId === item.id">
<select v-model="item.testType" :style="inputStyle">
<option value="">选择检验类型</option>
<option v-for="type in testTypes" :key="type.value" :value="type.value">
{{ type.label }}
</option>
</select>
</template>
<template v-else>
{{ item.testType }}
</template>
</td>
<td>
<template v-if="editingRowId === item.id">
<select v-model="item.package" :style="inputStyle" @change="updateAmountFromPackage(item)">
<option value="">选择费用套餐</option>
<option v-for="pkg in feePackages" :key="pkg.value" :value="pkg.value">
{{ pkg.label }}
</option>
</select>
</template>
<template v-else>
{{ item.package }}
</template>
</td>
<td>
<template v-if="editingRowId === item.id">
<select v-model="item.sampleType" :style="inputStyle">
<option value="">选择样本类型</option>
<option v-for="type in sampleTypes" :key="type.value" :value="type.value">
{{ type.label }}
</option>
</select>
</template>
<template v-else>
{{ item.sampleType }}
</template>
</td>
<td>{{ item.amount }}</td>
<td>
<template v-if="editingRowId === item.id">
<input v-model="item.sortOrder" type="number" :style="inputStyle">
</template>
<template v-else>
{{ item.sortOrder || 999999 }}
</template>
</td>
<td>
<template v-if="editingRowId === item.id">
<select v-model="item.serviceRange" :style="inputStyle">
<option v-for="range in serviceRanges" :key="range.value" :value="range.value">
{{ range.label }}
</option>
</select>
</template>
<template v-else>
{{ item.serviceRange || '全部' }}
</template>
</td>
<td>
<template v-if="editingRowId === item.id">
<input v-model="item.sub医技Type" type="text" :style="inputStyle">
</template>
<template v-else>
{{ item.sub医技Type || '-' }}
</template>
</td>
<td>
<template v-if="editingRowId === item.id">
<input v-model="item.remark" type="text" :style="inputStyle">
</template>
<template v-else>
{{ item.remark || '-' }}
</template>
</td>
<td class="action-cell">
<template v-if="editingRowId === item.id">
<div class="action-btn save-btn" @click="saveItem(item)" title="保存">
<span style="font-size: 12px;"></span>
</div>
<div class="action-btn cancel-btn" @click="cancelEdit(item)" title="取消">
<span style="font-size: 12px;"></span>
</div>
</template>
<template v-else-if="!editingRowId">
<div class="action-btn edit-btn" @click="editItem(item)">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path>
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path>
</svg>
</div>
</template>
<div
class="action-btn delete-btn"
@click="deleteItem(item.id)"
v-if="!editingRowId || editingRowId === item.id"
>
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="3 6 5 6 21 6"></polyline>
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
<line x1="10" y1="11" x2="10" y2="17"></line>
<line x1="14" y1="11" x2="14" y2="17"></line>
</svg>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- 页码区域 -->
<div class="pagination">
<div class="page-btn">上一页</div>
<div class="page-btn active">1</div>
<div class="page-btn">2</div>
<div class="page-btn">3</div>
<div class="page-btn">下一页</div>
</div>
</template>
<!-- 套餐设置页面 -->
<template v-else-if="activeNav === 2">
<!-- 顶部操作栏 -->
<div class="top-bar">
<div class="action-group">
<button class="btn btn-icon" @click="refreshPage">
<i></i> 刷新
</button>
<button class="btn btn-secondary" @click="resetForm">
<i>🔄</i> 重置
</button>
<button class="btn btn-success" @click="handlePackageManagement">套餐管理</button>
</div>
<button class="btn btn-lg" @click="handleSave">保存</button>
</div>
<!-- 表单区域 -->
<div class="form-section">
<div class="section-title">基本信息</div>
<div class="form-grid">
<div class="form-item">
<span class="form-label">套餐类别</span>
<select class="form-control form-select">
<option value="1" selected>检验套餐</option>
</select>
</div>
<div class="form-item">
<span class="form-label">套餐级别</span>
<el-select
v-model="packageLevel"
filterable
allow-create
default-first-option
placeholder="请选择或输入套餐级别"
style="width: 100%;"
@change="handlePackageLevelChange"
>
<el-option
v-for="item in packageLevelOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</div>
<div class="form-item" id="departmentContainer" v-show="packageLevel === '科室套餐'">
<span class="form-label">科室</span>
<el-tree-select
v-model="department"
placeholder="请选择科室"
:data="departments"
:props="{
value: 'name',
label: 'name',
children: 'children',
}"
value-key="name"
check-strictly
:expand-on-click-node="false"
clearable
style="width: 100%;"
@change="handlePackageDepartmentChange"
/>
</div>
<div class="form-item" id="userContainer" v-show="packageLevel === '个人套餐'">
<span class="form-label">用户</span>
<input type="text" class="form-control" :value="userStore.nickName" readonly>
</div>
<div class="form-item">
<span class="form-label"><span style="color:red"></span>套餐名称</span>
<input type="text" class="form-control" v-model="packageName" placeholder="请输入套餐名称">
<div class="error-message" id="packageNameError" style="color: #ff4d4f; font-size: 12px; margin-top: 4px; display: none;">套餐名称不能为空</div>
</div>
<div class="form-item">
<span class="form-label">卫生机构</span>
<input type="text" class="form-control" :value="userStore.orgName || '测试机构'" readonly>
</div>
<div class="form-item">
<span class="form-label">套餐金额</span>
<input type="text" class="form-control" style="width: 120px;" :value="packageAmount.toFixed(2)" readonly>
</div>
<div class="form-item">
<span class="form-label">折扣 %</span>
<input type="text" class="form-control" v-model="discount" @input="calculateAmounts">
</div>
<div class="form-item">
<span class="form-label">制单人</span>
<input type="text" class="form-control" readonly :value="userStore.nickName">
</div>
<div class="form-item">
<span class="form-label">备注</span>
<input type="text" class="form-control" v-model="remarks" placeholder="请输入备注">
</div>
<div class="form-item">
<span class="form-label">是否停用</span>
<el-radio-group v-model="isDisabled" size="medium">
<el-radio :label="false">启用</el-radio>
<el-radio :label="true">停用</el-radio>
</el-radio-group>
</div>
<div class="form-item">
<span class="form-label">显示套餐名</span>
<el-radio-group v-model="showPackageName" size="medium">
<el-radio :label="true"></el-radio>
<el-radio :label="false"></el-radio>
</el-radio-group>
</div>
<div class="form-item">
<span class="form-label">生成服务费</span>
<el-radio-group v-model="generateServiceFee" size="medium">
<el-radio :label="true"></el-radio>
<el-radio :label="false"></el-radio>
</el-radio-group>
</div>
<div class="form-item">
<span class="form-label">套餐价格</span>
<el-radio-group v-model="enablePackagePrice" size="medium">
<el-radio :label="true">启用</el-radio>
<el-radio :label="false">不启用</el-radio>
</el-radio-group>
</div>
<div class="form-item">
<span class="form-label">服务费</span>
<input type="text" class="form-control" :value="serviceFee.toFixed(2)" readonly>
</div>
<div class="form-item">
<span class="form-label">lis分组</span>
<select class="form-control form-select" v-model="selectedLisGroup">
<option value="">请选择lis分组</option>
<option v-for="group in lisGroupList" :key="group.id" :value="group.id">
{{ group.groupName || group.lisGroupName }}
</option>
</select>
</div>
<div class="form-item">
<span class="form-label">血量</span>
<input type="text" class="form-control" v-model="bloodVolume">
</div>
</div>
</div>
<!-- 检验套餐明细表格区域 -->
<div class="table-container" style="width: 100%; margin-top: 20px;">
<div class="table-header" style="display: flex; justify-content: space-between; align-items: center; padding: 10px;">
<div class="table-title" style="font-size: 16px; font-weight: bold;">检验套餐明细</div>
<button class="action-btn" title="添加项目" @click="addPackageItem" style="width: 32px; height: 32px; font-size: 20px; display: flex; align-items: center; justify-content: center;">+</button>
</div>
<table class="data-table">
<thead>
<tr>
<th style="width: 40px;">行号</th>
<th style="width: 250px;">项目名称/规格</th>
<th style="width: 100px;">剂量</th>
<th style="width: 100px;">途径</th>
<th style="width: 100px;">频次</th>
<th style="width: 80px;">天数</th>
<th style="width: 80px;">数量</th>
<th style="width: 80px;">单位</th>
<th style="width: 100px;">单价</th>
<th style="width: 100px;">金额</th>
<th style="width: 100px;">服务费</th>
<th style="width: 100px;">总金额</th>
<th style="width: 150px;">产地</th>
<th style="width: 80px;">操作</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, index) in packageItems" :key="index" :class="{ 'editing': editingRowId === index }">
<td>{{ index + 1 }}</td>
<td>
<template v-if="editingRowId === index">
<input
type="text"
v-model="item.name"
placeholder="请输入或选择项目名称"
@focus="handleItemFocus(index)"
:style="inputStyle"
/>
</template>
<template v-else>
{{ item.name }}
</template>
</td>
<td>
<template v-if="editingRowId === index">
<input type="text" v-model="item.dosage" placeholder="剂量" :style="inputStyle">
</template>
<template v-else>
{{ item.dosage || '-' }}
</template>
</td>
<td>
<template v-if="editingRowId === index">
<select v-model="item.route" style="width: 100%;" :style="inputStyle">
<option value="">请选择</option>
<option value="项/人">/</option>
<option value="次/人">/</option>
<!-- 可根据实际需求添加更多选项 -->
</select>
</template>
<template v-else>
{{ item.route || '-' }}
</template>
</td>
<td>
<template v-if="editingRowId === index">
<select v-model="item.frequency" style="width: 100%;" :style="inputStyle">
<option value="">请选择</option>
<option value="一次">一次</option>
<option value="每日">每日</option>
<option value="每周">每周</option>
<!-- 可根据实际需求添加更多选项 -->
</select>
</template>
<template v-else>
{{ item.frequency || '-' }}
</template>
</td>
<td>
<template v-if="editingRowId === index">
<input type="number" v-model.number="item.days" placeholder="天数" style="width: 100%;" :style="inputStyle">
</template>
<template v-else>
{{ item.days || '-' }}
</template>
</td>
<td>
<template v-if="editingRowId === index">
<input type="number" v-model.number="item.quantity" placeholder="数量" style="width: 100%;" @input="updateItemAmount(item)" :style="inputStyle" min="0" step="1">
</template>
<template v-else>
{{ item.quantity || '-' }}
</template>
</td>
<td>{{ item.unit || '-' }}</td>
<td>
<template v-if="editingRowId === index">
<input type="number" v-model.number="item.unitPrice" placeholder="单价" style="width: 100%;" @input="updateItemAmount(item)" :style="inputStyle" min="0" step="0.01">
</template>
<template v-else>
{{ item.unitPrice.toFixed(2) }}
</template>
</td>
<td>{{ item.amount.toFixed(2) }}</td>
<td>
<template v-if="editingRowId === index">
<input type="number" v-model.number="item.serviceFee" placeholder="服务费" style="width: 100%;" step="0.01" @input="updateItemTotalAmount(item)" :style="inputStyle" min="0">
</template>
<template v-else>
{{ item.serviceFee.toFixed(2) }}
</template>
</td>
<td>{{ item.totalAmount.toFixed(2) }}</td>
<td>
<template v-if="editingRowId === index">
<input type="text" v-model="item.origin" placeholder="产地" style="width: 100%;" :style="inputStyle">
</template>
<template v-else>
{{ item.origin || '-' }}
</template>
</td>
<td class="action-cell">
<div class="table-actions" style="display: flex; gap: 5px;">
<template v-if="editingRowId === index">
<!-- 编辑模式下的保存和取消按钮 -->
<div class="action-btn confirm-btn" title="保存" @click="handleEditItem(index)">
<span style="font-size: 12px;"></span>
</div>
<div class="action-btn cancel-btn" title="取消" @click="cancelEditItem(index)">
<span style="font-size: 12px;"></span>
</div>
</template>
<template v-else>
<!-- 非编辑模式下的编辑和删除按钮 -->
<div class="action-btn edit-btn" title="编辑" @click="handleEditItem(index)">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path>
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path>
</svg>
</div>
<div class="action-btn delete-btn" title="删除" @click="deletePackageItem(index)">
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="3 6 5 6 21 6"></polyline>
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
<line x1="10" y1="11" x2="10" y2="17"></line>
<line x1="14" y1="11" x2="14" y2="17"></line>
</svg>
</div>
</template>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</template>
</div>
</div>
</template>
<script setup>
import useUserStore from '@/store/modules/user';
import {computed, onMounted, ref, watch} from 'vue';
import {ElLoading, ElMessage, ElMessageBox} from 'element-plus';
import {useRoute, useRouter} from 'vue-router';
import {
addInspectionType,
delInspectionType,
listInspectionType,
updateInspectionType
} from '@/api/system/inspectionType';
import {
getDiagnosisTreatmentList,
addDiagnosisTreatment,
editDiagnosisTreatment,
stopDiseaseTreatment
} from '@/views/catalog/diagnosistreatment/components/diagnosistreatment';
import {listLisGroup} from '@/api/system/checkType';
import {
addInspectionPackage,
getInspectionPackage,
listInspectionPackageDetails,
saveInspectionPackageDetails
} from '@/api/system/inspectionPackage';
import {getLocationTree} from '@/views/charge/outpatientregistration/components/outpatientregistration';
// 获取当前登录用户信息
const userStore = useUserStore();
// 创建路由实例
const router = useRouter();
const route = useRoute();
// 存储LIS分组数据
const lisGroupList = ref([]);
// 选中的LIS分组
const selectedLisGroup = ref('');
// 获取就诊科室数据 - 与门诊挂号页面保持一致,在组件初始化时直接调用
getLocationInfo();
onMounted(() => {
// 其他初始化逻辑
});
// 获取LIS分组数据
const getLisGroupList = async () => {
try {
const response = await listLisGroup();
if (response.code === 200) {
console.log('完整LIS分组响应:', response); // 记录完整响应
// 适配可能的不同响应格式
let items = [];
// 检查响应数据
if (response.data) {
// 格式1: {data: {rows: [], total: number}} - 标准分页格式
if (response.data.rows && Array.isArray(response.data.rows)) {
items = response.data.rows;
console.log('LIS分组分页数据:', items);
}
// 格式2: {data: []} - 简单数组格式
else if (Array.isArray(response.data)) {
items = response.data;
console.log('LIS分组数组数据:', items);
}
// 格式3: {data: {data: []}} - 双重嵌套格式
else if (response.data.data && Array.isArray(response.data.data)) {
items = response.data.data;
console.log('LIS分组双重嵌套数据:', items);
}
// 格式4: {data: {data: {rows: []}}} - 双重嵌套分页格式
else if (response.data.data && response.data.data.rows && Array.isArray(response.data.data.rows)) {
items = response.data.data.rows;
console.log('LIS分组双重嵌套分页数据:', items);
}
// 其他格式
else {
console.log('无法识别的数据格式:', response.data);
}
}
lisGroupList.value = items;
} else {
ElMessage.error('获取LIS分组数据失败');
}
} catch (error) {
console.error('获取LIS分组数据失败:', error);
ElMessage.error('获取LIS分组数据失败');
}
};
// 导航数据
const navItems = ref(['检验类型', '检验项目', '套餐设置']);
const activeNav = ref(0);
// 检验类型数据
const tableData = ref([]);
// 获取检验类型列表 - 从后端API获取
const getInspectionTypeList = () => {
listInspectionType().then(data => {
// 确保数据结构与前端使用的一致处理后端返回的AjaxResult格式
// 后端返回的数据格式: {code: 200, msg: "查询成功", data: [检验类型列表]}
const inspectionTypeList = data.data || [];
// 后端实体字段名本身就是 sortOrder这里不再从不存在的 item.order 做映射
const formattedData = inspectionTypeList.map(item => ({
...item,
sortOrder: item.sortOrder
}));
// 过滤掉已逻辑删除的记录validFlag为0
tableData.value = formattedData.filter(item => item.validFlag === 1);
}).catch(error => {
console.error('获取检验类型列表失败:', error);
});
};
const editingRowId = ref(null);
const inputStyle = { width: '100%', height: '28px', padding: '0 4px', border: '1px solid #d9d9d9', borderRadius: '2px' };
const departments = ref([]);
/** 查询就诊科室 - 与门诊挂号页面保持一致 */
function getLocationInfo() {
console.log('调用getLocationTree API...');
getLocationTree().then((response) => {
console.log('getLocationTree API完整返回:', response);
console.log('getLocationTree API数据结构:', JSON.stringify(response.data, null, 2));
// 检查数据结构并转换为适合el-tree-select的格式
if (Array.isArray(response.data)) {
// 直接使用数组数据
departments.value = response.data;
} else if (response.data && response.data.records) {
// 处理分页格式数据
departments.value = response.data.records;
} else if (response.data && response.data.rows) {
// 处理另一种分页格式数据
departments.value = response.data.rows;
} else {
console.error('API返回数据格式不符合预期:', response.data);
departments.value = [];
}
console.log('最终科室数据:', JSON.stringify(departments.value, null, 2));
}).catch((error) => {
console.error('获取科室数据失败:', error);
departments.value = [];
});
}
// 处理科室选择变化
function handleDepartmentChange(selectedNode, item) {
console.log('选择的科室节点:', selectedNode);
// 如果selectedNode是对象只取name属性
if (typeof selectedNode === 'object' && selectedNode !== null) {
item.department = selectedNode.name;
} else {
// 否则直接使用(可能是字符串)
item.department = selectedNode;
}
}
// 处理套餐级别选择变化
function handlePackageLevelChange(value) {
console.log('选择的套餐级别:', value);
if (value !== '科室套餐') {
department.value = '';
}
}
// 处理套餐科室选择变化
function handlePackageDepartmentChange(selectedNode) {
console.log('选择的套餐科室节点:', selectedNode);
// 如果selectedNode是对象只取name属性
if (typeof selectedNode === 'object' && selectedNode !== null) {
department.value = selectedNode.name;
} else {
// 否则直接使用(可能是字符串)
department.value = selectedNode;
}
}
// 费用套餐数据
const feePackages = ref([
{ value: '肝功能12项', label: '肝功能12项', amount: 120.00 },
{ value: '血常规', label: '血常规', amount: 35.00 },
{ value: '尿常规', label: '尿常规', amount: 25.00 },
{ value: '血脂五项', label: '血脂五项', amount: 80.00 },
{ value: '肾功能三项', label: '肾功能三项', amount: 50.00 },
{ value: '乙肝套餐', label: '乙肝套餐', amount: 75.00 },
{ value: '糖尿病套餐', label: '糖尿病套餐', amount: 65.00 },
{ value: '肾功能套餐', label: '肾功能套餐', amount: 45.00 },
{ value: '肿瘤筛查套餐', label: '肿瘤筛查套餐', amount: 200.00 },
{ value: '心脑血管套餐', label: '心脑血管套餐', amount: 180.00 },
{ value: '甲状腺功能套餐', label: '甲状腺功能套餐', amount: 150.00 },
{ value: '微量元素套餐', label: '微量元素套餐', amount: 90.00 },
{ value: '电解质套餐', label: '电解质套餐', amount: 40.00 },
{ value: '风湿免疫套餐', label: '风湿免疫套餐', amount: 120.00 },
{ value: '性激素套餐', label: '性激素套餐', amount: 160.00 }
]);
// 样本类型数据
const sampleTypes = ref([
{ value: '血液', label: '血液' },
{ value: '尿液', label: '尿液' },
{ value: '粪便', label: '粪便' },
{ value: '脑脊液', label: '脑脊液' },
{ value: '胸水', label: '胸水' },
{ value: '腹水', label: '腹水' },
{ value: '分泌物', label: '分泌物' },
{ value: '组织', label: '组织' },
{ value: '其他', label: '其他' }
]);
// 服务范围数据
const serviceRanges = ref([
{ value: '全部', label: '全部' },
{ value: '门诊', label: '门诊' },
{ value: '住院', label: '住院' },
{ value: '急诊', label: '急诊' },
{ value: '体检', label: '体检' }
]);
// 检验类型数据
const testTypes = ref([
{ value: '生化', label: '生化' },
{ value: '常规检验', label: '常规检验' },
{ value: '免疫学检验', label: '免疫学检验' },
{ value: '微生物检验', label: '微生物检验' },
{ value: '分子生物学检验', label: '分子生物学检验' },
{ value: '血液学检验', label: '血液学检验' },
{ value: '尿液检验', label: '尿液检验' },
{ value: '粪便检验', label: '粪便检验' },
{ value: '脑脊液检验', label: '脑脊液检验' },
{ value: '其他检验', label: '其他检验' }
]);
// 检验项目数据 - 从后端API获取
const inspectionItems = ref([]);
// 从后端API获取检验项目数据
const loadObservationItems = async () => {
try {
const response = await getDiagnosisTreatmentList({
pageNo: 1,
pageSize: 100,
categoryCode: '检验'
});
if (response.code === 200) {
let data = [];
if (response.data && response.data.records) {
data = response.data.records;
} else if (response.data && Array.isArray(response.data)) {
data = response.data;
}
inspectionItems.value = data
// 过滤掉已停用的项目状态为3
.filter(item => item.statusEnum !== 3)
.map(item => ({
id: item.id,
code: item.busNo || '',
name: item.name || '',
testType: '',
package: '',
sampleType: item.specimenCode_dictText || '',
amount: parseFloat(item.retailPrice || 0),
sortOrder: item.sortOrder || null,
serviceRange: item.serviceRange || '全部',
sub医技Type: '',
remark: item.descriptionText || '',
status: true
}));
}
} catch (error) {
console.error('获取检验项目数据失败:', error);
}
};
// 过滤条件
const testTypeFilter = ref('');
const nameFilter = ref('');
const packageFilter = ref('');
// 过滤后的检验项目数据
const filteredInspectionItems = computed(() => {
return inspectionItems.value.filter(item => {
// 按检验类型过滤
if (testTypeFilter.value && item.testType !== testTypeFilter.value) {
return false;
}
// 按名称或编码过滤
if (nameFilter.value && !(item.name.includes(nameFilter.value) || item.code.includes(nameFilter.value))) {
return false;
}
// 按费用套餐过滤
if (packageFilter.value && item.package !== packageFilter.value) {
return false;
}
return true;
});
});
// 执行过滤
const filterItems = () => {
// 过滤逻辑已经在computed属性中实现这里可以添加额外的逻辑
};
// 重置过滤条件
const resetFilters = () => {
testTypeFilter.value = '';
nameFilter.value = '';
packageFilter.value = '';
};
// 套餐相关数据
const packageCategory = ref('检验套餐');
const packageLevel = ref('');
const packageLevelOptions = ref([
{ value: '全院套餐', label: '全院套餐' },
{ value: '科室套餐', label: '科室套餐' },
{ value: '个人套餐', label: '个人套餐' }
]);
const packageName = ref('');
const department = ref('');
const discount = ref('');
const isDisabled = ref(false);
const showPackageName = ref(true);
const generateServiceFee = ref(true);
const enablePackagePrice = ref(true);
const packageAmount = ref(0.00);
const serviceFee = ref(0.00);
const bloodVolume = ref('');
const remarks = ref('');
// 检验套餐明细项目 - 从后端API获取
const packageItems = ref([]);
// 从后端API获取检验项目数据并转换为套餐明细格式
const loadPackageItemsFromAPI = () => {
queryDiagnosisItems('', (results) => {
// 将API返回的检验项目转换为套餐明细格式
const formattedItems = results.map(item => ({
name: item.name,
dosage: '项/人',
route: '项/人',
frequency: '',
days: '',
quantity: 1,
unit: item.unit || '项',
unitPrice: parseFloat(item.retailPrice || 0.00),
amount: parseFloat(item.retailPrice || 0.00),
serviceFee: 0.00,
totalAmount: parseFloat(item.retailPrice || 0.00),
origin: ''
}));
// 只保留前几个项目作为示例
packageItems.value = formattedItems.slice(0, 10);
console.log('加载的套餐项目:', packageItems.value);
});
};
// 查询诊疗目录中的检验项目
const queryDiagnosisItems = (queryString, cb) => {
// 调用诊疗目录API查询检验类别的项目
const params = {
searchKey: queryString || '',
pageNo: 1,
pageSize: 100 // 增加分页大小,显示更多项目
};
getDiagnosisTreatmentList(params).then(response => {
console.log('诊疗目录API完整返回:', response);
// 处理不同的数据结构
let data;
if (response.data && response.data.records) {
data = response.data.records;
} else if (response.data && Array.isArray(response.data)) {
data = response.data;
} else if (response.data && response.data.rows) {
data = response.data.rows;
} else {
console.error('API返回数据格式不符合预期:', response.data);
return cb([]);
}
// 添加调试信息,查看返回的数据结构
console.log('诊疗目录返回数据:', data);
// 过滤出目录类别为检验的项目
// 支持多种可能的字段名
const inspectionItems = data.filter(item => {
return item.categoryCode_dictText === '检验' ||
item.categoryName === '检验' ||
item.category === '检验';
});
// 处理每个检验项目,确保有正确的字段映射
const results = inspectionItems.map(item => {
// 确保每个项目都有必要的字段
return {
value: item.name || item.itemName || item.drugName || '',
label: `${item.name || item.itemName || item.drugName || ''} - ${item.unit || item.usageUnit || '项'} - ¥${item.retailPrice || item.price || item.unitPrice || 0.00}`,
name: item.name || item.itemName || item.drugName || '',
unit: item.unit || item.usageUnit || '',
retailPrice: item.retailPrice || item.price || item.unitPrice || 0.00,
...item
};
});
console.log('处理后的检验项目:', results);
cb(results);
}).catch(error => {
console.error('查询诊疗目录失败:', error);
ElMessage.error('查询诊疗目录失败,请稍后重试');
cb([]);
});
};
// 处理项目名称输入框获得焦点事件
const handleItemFocus = (index) => {
// 可以在这里添加项目选择逻辑,或者留空让用户直接输入
};
// 添加新的套餐项目
let addingItem = false;
const addPackageItem = () => {
if (addingItem) return; // 防止重复调用
addingItem = true;
const newItem = {
name: '',
dosage: '',
route: '',
frequency: '',
days: '',
quantity: 1,
unit: '',
unitPrice: 0.00,
amount: 0.00,
serviceFee: 0.00,
totalAmount: 0.00,
origin: ''
};
packageItems.value.push(newItem);
// 延迟重置标志位,确保不会影响其他操作
setTimeout(() => {
addingItem = false;
}, 100);
};
// 删除套餐项目
const deletePackageItem = (index) => {
ElMessageBox.confirm('确定要删除该项目吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
packageItems.value.splice(index, 1);
calculateAmounts();
ElMessage.success('删除成功');
}).catch(() => {
// 取消删除
});
};
// 处理选择诊疗项目
const handleItemSelect = (item, selectedItem) => {
console.log('选择的诊疗项目:', selectedItem);
// 将选中的项目名称、使用单位、单价赋值到检验套餐明细行对应的字段中
item.name = selectedItem.name || selectedItem.value; // 使用name或value作为项目名称
item.unit = selectedItem.unit || selectedItem.usageUnit || ''; // 使用unit或usageUnit作为使用单位
item.unitPrice = parseFloat(selectedItem.retailPrice || selectedItem.price || 0.00); // 使用retailPrice或price作为单价
item.quantity = 1; // 默认数量为1
item.serviceFee = calculateItemServiceFee(item); // 计算该项目的服务费
updateItemAmount(item);
ElMessage.success(`已选择检验项目: ${item.name}`);
};
// 更新项目金额(考虑折扣)
const updateItemAmount = (item) => {
// 计算项目原价金额
const originalAmount = (item.quantity || 1) * (item.unitPrice || 0.00);
// 应用折扣到项目金额
let discountedAmount = originalAmount;
if (discount.value && !isNaN(parseFloat(discount.value))) {
const discountRate = parseFloat(discount.value) / 100;
discountedAmount = originalAmount * (1 - discountRate);
}
// 更新项目金额
item.amount = parseFloat(discountedAmount.toFixed(2));
// 基于折扣后的金额计算服务费
item.serviceFee = calculateItemServiceFee(item);
// 更新项目总金额
updateItemTotalAmount(item);
// 重新计算套餐金额和服务费
calculateAmounts();
};
// 更新项目总金额
const updateItemTotalAmount = (item) => {
item.totalAmount = (item.amount || 0.00) + (item.serviceFee || 0.00);
};
// 处理编辑项目
const handleEditItem = (index) => {
if (editingRowId.value !== null && editingRowId.value !== index) {
ElMessage.warning('请先保存或取消当前正在编辑的行');
return;
}
if (editingRowId.value === index) {
// 保存编辑 - 验证并计算金额
const item = packageItems.value[index];
if (!item.name || item.name.trim() === '') {
ElMessage.error('请输入项目名称');
return;
}
if (!item.unit || item.unit.trim() === '') {
ElMessage.error('请输入单位');
return;
}
if (item.quantity <= 0) {
ElMessage.error('数量必须大于0');
return;
}
if (item.unitPrice <= 0) {
ElMessage.error('单价必须大于0');
return;
}
// 重新计算金额
updateItemAmount(item);
editingRowId.value = null;
ElMessage.success('保存成功');
} else {
// 进入编辑模式
editingRowId.value = index;
}
};
// 取消编辑项目
const cancelEditItem = (index) => {
editingRowId.value = null;
ElMessage.info('已取消编辑');
};
// 计算单个项目的服务费(基于折扣后的金额)
const calculateItemServiceFee = (item) => {
if (!generateServiceFee.value) return 0;
// 服务费是项目折扣后金额的10%
return parseFloat((item.amount * 0.1).toFixed(2));
};
// 重新分配所有项目的服务费(基于折扣后的金额)
const redistributeServiceFee = () => {
if (!generateServiceFee.value) {
// 如果不生成服务费将所有项目的服务费设为0
packageItems.value.forEach(item => {
item.serviceFee = 0;
updateItemTotalAmount(item);
});
return;
}
// 重新计算每个项目的服务费
packageItems.value.forEach(item => {
item.serviceFee = calculateItemServiceFee(item);
updateItemTotalAmount(item);
});
};
// 计算套餐金额和服务费
const calculateAmounts = () => {
// 更新每个项目的折扣金额
packageItems.value.forEach(item => {
// 计算项目原价金额
const originalAmount = (item.quantity || 1) * (item.unitPrice || 0.00);
// 应用折扣到项目金额
let discountedAmount = originalAmount;
if (discount.value && !isNaN(parseFloat(discount.value))) {
const discountRate = parseFloat(discount.value) / 100;
discountedAmount = originalAmount * (1 - discountRate);
}
// 更新项目金额
item.amount = parseFloat(discountedAmount.toFixed(2));
});
// 重新分配所有项目的服务费
redistributeServiceFee();
// 计算套餐总金额(基于项目的折扣后金额)
const totalAmount = packageItems.value.reduce((sum, item) => sum + (item.amount || 0), 0);
// 更新套餐金额
packageAmount.value = parseFloat(totalAmount.toFixed(2));
// 计算套餐总服务费
if (generateServiceFee.value) {
serviceFee.value = parseFloat(packageItems.value.reduce((sum, item) => sum + (item.serviceFee || 0), 0).toFixed(2));
} else {
serviceFee.value = 0;
}
};
// 选择套餐项目
const handleSelectPackage = (item) => {
console.log('选择的检验项目:', item);
// 直接更新套餐金额
if (item.retailPrice) {
// 清空现有套餐项目列表
packageItems.value = [];
// 创建新的套餐项目
const newPackageItem = {
name: item.name,
dosage: '项/人',
route: '项/人',
frequency: '',
days: '',
quantity: 1,
unit: item.unit || '项',
unitPrice: parseFloat(item.retailPrice),
amount: parseFloat(item.retailPrice),
serviceFee: 0.00, // 初始化为0
totalAmount: parseFloat(item.retailPrice), // 初始总金额
origin: ''
};
// 添加到套餐项目列表
packageItems.value.push(newPackageItem);
// 调用updateItemAmount计算折扣后的金额和服务费
updateItemAmount(newPackageItem);
// 更新套餐名称
packageName.value = item.name;
} else {
console.error('选择的项目没有价格信息:', item);
ElMessage.warning('选择的项目没有价格信息');
}
ElMessage.success(`已选择检验项目: ${item.name}`);
};
// 检验类型相关方法
// 处理表格点击事件,用于自动删除空的编辑行
const handleTableClick = (event) => {
// 如果点击的是表格内部的元素,不处理
if (event.target.closest('input, select, button, .action-btn')) {
return;
}
// 如果当前有正在编辑的行,检查是否为空
if (editingRowId.value) {
const editingRow = tableData.value.find(row => row.id === editingRowId.value);
if (editingRow && (!editingRow.code || editingRow.code.trim() === '') &&
(!editingRow.name || editingRow.name.trim() === '') &&
(!editingRow.department || editingRow.department.trim() === '')) {
// 删除空的编辑行
const index = tableData.value.findIndex(row => row.id === editingRowId.value);
if (index !== -1) {
tableData.value.splice(index, 1);
editingRowId.value = null;
ElMessage.info('已自动删除空行');
}
}
}
};
const addNewRow = () => {
if (editingRowId.value) {
ElMessage.warning('请先保存或取消当前正在编辑的行');
return;
}
const newRow = { id: Date.now(), code: '', name: '', department: departments.value[0], sortOrder: tableData.value.length + 1, remark: '' };
tableData.value.push(newRow);
editingRowId.value = newRow.id;
};
const handleEdit = (row) => {
if (editingRowId.value && editingRowId.value !== row.id) {
ElMessage.warning('请先保存或取消当前正在编辑的行');
return;
}
editingRowId.value = row.id;
};
const handleConfirm = (row) => {
// 准备提交给后端的数据保留sortOrder字段名后端会自动映射到数据库的order字段
const submitData = {
...row,
// 确保sortOrder字段存在且为数字类型
sortOrder: row.sortOrder ? Number(row.sortOrder) : 0
};
console.log('原始row数据:', row);
console.log('提交前的submitData:', submitData);
// 验证必填字段,如果为空则删除该行
if (!submitData.code || submitData.code.trim() === '') {
// 删除空的编辑行
const index = tableData.value.findIndex(r => r.id === row.id);
if (index !== -1) {
tableData.value.splice(index, 1);
editingRowId.value = null;
ElMessage.info('已删除空行');
}
return;
}
if (!submitData.name || submitData.name.trim() === '') {
// 删除空的编辑行
const index = tableData.value.findIndex(r => r.id === row.id);
if (index !== -1) {
tableData.value.splice(index, 1);
editingRowId.value = null;
ElMessage.info('已删除空行');
}
return;
}
if (!submitData.department || submitData.department.trim() === '') {
// 删除空的编辑行
const index = tableData.value.findIndex(r => r.id === row.id);
if (index !== -1) {
tableData.value.splice(index, 1);
editingRowId.value = null;
ElMessage.info('已删除空行');
}
return;
}
// 检查是否是已知的重复编码
if (submitData.code.trim() === '21') {
alert('检验类型编码21已存在请使用其他编码');
return;
}
// 输出调试信息
console.log('原始code值:', submitData.code, '长度:', submitData.code.length);
// 去除code字段的前后空格确保唯一性验证准确
submitData.code = submitData.code.trim();
console.log('去除空格后的code值:', submitData.code, '长度:', submitData.code.length);
console.log('准备提交的数据:', submitData);
// 区分新增和更新操作
if (row.id.toString().length > 10) { // 新增的临时ID
// 新增数据时移除临时ID让后端自动生成主键
const { id, ...newData } = submitData;
console.log('删除ID后的newData:', newData);
addInspectionType(newData).then(response => {
console.log('新增成功响应:', response);
getInspectionTypeList();
}).catch(error => {
console.error('新增检验类型失败:', error);
if (error.response && error.response.data) {
alert('新增失败: ' + error.response.data.msg);
} else {
alert('新增失败,请重试');
}
});
} else { // 更新操作
// 更新数据时保留ID
console.log('更新时的submitData:', submitData);
updateInspectionType(submitData).then(response => {
console.log('更新成功响应:', response);
getInspectionTypeList();
}).catch(error => {
console.error('更新检验类型失败:', error);
if (error.response && error.response.data) {
alert('更新失败: ' + error.response.data.msg);
} else {
alert('更新失败,请重试');
}
});
}
editingRowId.value = null;
};
const handleAdd = (row, index) => {
if (editingRowId.value) {
ElMessage.warning('请先保存或取消当前正在编辑的行');
return;
}
const newRow = { id: Date.now(), code: '', name: '', department: row.department, sortOrder: row.sortOrder + 1, remark: '' };
tableData.value.splice(index + 1, 0, newRow);
editingRowId.value = newRow.id;
};
const handleDelete = (id) => {
ElMessageBox.confirm('确定要删除该检验类型吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
console.log('删除按钮被点击原始ID:', id, '类型:', typeof id);
// 确保ID是数字类型
const numericId = Number(id);
console.log('转换后的ID:', numericId, '类型:', typeof numericId);
// 判断是否为临时ID临时ID是通过Date.now()生成的值很大通常大于2000000000000
const isTemporaryId = numericId > 2e12;
console.log('是否为临时ID:', isTemporaryId, '判断阈值:', 2e12);
if (!isTemporaryId) { // 真实数据库ID
console.log('调用删除APIID:', numericId, 'API路径:', `/system/inspection-type/${numericId}`);
delInspectionType(numericId).then(response => {
console.log('删除成功,响应:', response);
ElMessage.success('删除成功');
getInspectionTypeList();
}).catch(error => {
console.error('删除检验类型失败:', error);
ElMessage.error('删除失败: ' + (error.response?.data?.msg || '未知错误'));
if (error.response) {
console.error('响应状态:', error.response.status);
console.error('响应数据:', error.response.data);
} else if (error.request) {
console.error('请求发送但未收到响应:', error.request);
} else {
console.error('请求配置错误:', error.message);
}
});
} else {
// 删除临时新增的行
console.log('删除临时行ID:', numericId);
tableData.value = tableData.value.filter(row => Number(row.id) !== numericId);
ElMessage.success('删除成功');
}
}).catch(() => {
// 取消删除
});
};
// 检验项目相关方法
const addNewItem = () => {
if (editingRowId.value) {
ElMessage.warning('请先保存或取消当前正在编辑的行');
return;
}
const newItem = {
id: Date.now(),
code: '',
name: '',
testType: '',
package: '',
sampleType: '',
amount: 0.00,
sortOrder: inspectionItems.value.length + 1,
serviceRange: '全部',
sub医技Type: '',
remark: '',
status: true
};
inspectionItems.value.push(newItem);
editingRowId.value = newItem.id;
};
const editItem = (item) => {
if (editingRowId.value === item.id) {
// 如果当前行已经在编辑模式,点击编辑按钮则保存
saveItem(item);
} else {
// 否则进入编辑模式
editingRowId.value = item.id;
}
};
const updateAmountFromPackage = (item) => {
if (item.package) {
const selectedPackage = feePackages.value.find(pkg => pkg.value === item.package);
if (selectedPackage) {
item.amount = selectedPackage.amount;
}
} else {
item.amount = 0.00;
}
};
const saveItem = async (item) => {
// 验证必填字段
if (!item.code || item.code.trim() === '') {
ElMessage.error('小类编码不能为空');
return;
}
// 验证小类编码格式4位数字
const codeRegex = /^\d{4}$/;
if (!codeRegex.test(item.code.trim())) {
ElMessage.error('小类编码必须为4位数字');
return;
}
if (!item.name || item.name.trim() === '') {
ElMessage.error('小类项目名称不能为空');
return;
}
if (!item.testType) {
ElMessage.error('检验类型不能为空');
return;
}
if (!item.sampleType) {
ElMessage.error('样本类型不能为空');
return;
}
// 验证小类编码唯一性
const isDuplicate = inspectionItems.value.some(i =>
i.id !== item.id && i.code.trim() === item.code.trim()
);
if (isDuplicate) {
ElMessage.error('小类编码已存在');
return;
}
// 从费用套餐获取金额
updateAmountFromPackage(item);
try {
// 准备提交给后端的数据
const submitData = {
busNo: item.code.trim(),
name: item.name.trim(),
categoryCode: '检验',
specimenCode: item.sampleType,
retailPrice: item.amount,
descriptionText: item.remark,
typeEnum: 1,
statusEnum: 2,
sortOrder: item.sortOrder ? parseInt(item.sortOrder) : null,
serviceRange: item.serviceRange || '全部'
};
// 判断是新增还是更新
if (typeof item.id === 'number') { // 临时ID数字类型新增
const response = await addDiagnosisTreatment(submitData);
if (response.code === 200) {
ElMessage.success('添加成功');
await loadObservationItems();
} else {
ElMessage.error(response.msg || '添加失败');
}
} else { // 真实ID字符串类型更新
submitData.id = item.id;
const response = await editDiagnosisTreatment(submitData);
if (response.code === 200) {
ElMessage.success('更新成功');
await loadObservationItems();
} else {
ElMessage.error(response.msg || '更新失败');
}
}
editingRowId.value = null;
} catch (error) {
console.error('保存检验项目失败:', error);
ElMessage.error('保存失败,请稍后重试');
}
};
const deleteItem = async (id) => {
ElMessageBox.confirm('确定要删除该检验项目吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async () => {
try {
const response = await stopDiseaseTreatment([id]);
if (response.code === 200) {
ElMessage.success('删除成功');
await loadObservationItems();
} else {
ElMessage.error(response.msg || '删除失败');
}
} catch (error) {
console.error('删除检验项目失败:', error);
ElMessage.error('删除失败,请稍后重试');
}
}).catch(() => {
});
};
const cancelEdit = (item) => {
// 如果是新添加的行,则直接删除
if (item.id.toString().length > 10) { // 临时ID使用Date.now()生成
const index = inspectionItems.value.findIndex(i => i.id === item.id);
if (index !== -1) {
inspectionItems.value.splice(index, 1);
}
}
editingRowId.value = null;
};
// 导出表格数据
const exportTable = () => {
// 将表格数据转换为CSV格式
const headers = ['行号', '小类编码', '小类项目名称', '检验类型', '费用套餐', '样本类型', '金额', '序号', '服务范围', '下级医技类型', '备注'];
const csvContent = [
headers.join(','),
...filteredInspectionItems.value.map((item, index) => [
index + 1,
`"${item.code}"`,
`"${item.name}"`,
`"${item.testType}"`,
`"${item.package}"`,
`"${item.sampleType}"`,
item.amount,
item.sortOrder || 999999,
`"${item.serviceRange || '全部'}"`,
`"${item.sub医技Type || '-'}"`,
`"${item.remark || '-'}"`
].join(','))
].join('\n');
// 创建Blob对象
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
// 创建下载链接
const link = document.createElement('a');
const url = URL.createObjectURL(blob);
link.setAttribute('href', url);
link.setAttribute('download', `检验项目导出_${new Date().toISOString().split('T')[0]}.csv`);
link.style.visibility = 'hidden';
// 添加到DOM并触发下载
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
ElMessage.success('导出成功');
};
// 套餐相关方法
const handleSave = () => {
// 验证必填字段
if (!packageLevel.value) {
ElMessage.error('请选择套餐级别');
return;
}
if (!packageName.value || packageName.value.trim() === '') {
ElMessage.error('请输入套餐名称');
return;
}
if (packageItems.value.length === 0) {
ElMessage.error('请至少添加一个检验项目');
return;
}
// 验证套餐明细数据
for (let i = 0; i < packageItems.value.length; i++) {
const item = packageItems.value[i];
if (!item.name || item.name.trim() === '') {
ElMessage.error(`${i + 1}行:请输入项目名称`);
return;
}
if (!item.unit || item.unit.trim() === '') {
ElMessage.error(`${i + 1}行:请输入单位`);
return;
}
if (item.quantity <= 0) {
ElMessage.error(`${i + 1}数量必须大于0`);
return;
}
if (item.unitPrice <= 0) {
ElMessage.error(`${i + 1}单价必须大于0`);
return;
}
}
// 准备基本信息数据
const basicInfo = {
packageCategory: packageCategory.value,
packageLevel: packageLevel.value,
packageName: packageName.value.trim(),
department: department.value,
discount: discount.value || 0,
isDisabled: isDisabled.value,
showPackageName: showPackageName.value,
generateServiceFee: generateServiceFee.value,
enablePackagePrice: enablePackagePrice.value,
packageAmount: packageAmount.value,
serviceFee: serviceFee.value,
lisGroup: selectedLisGroup.value, // 从下拉框获取
bloodVolume: bloodVolume.value,
remarks: remarks.value,
orgName: userStore.orgName || '测试机构', // 卫生机构
createBy: userStore.nickName, // 制单人
createTime: new Date().toISOString(),
updateTime: new Date().toISOString()
};
// 根据套餐级别设置用户信息
if (packageLevel.value === '科室套餐' && department.value) {
// 科室套餐:设置科室信息
basicInfo.departmentId = department.value;
} else if (packageLevel.value === '个人套餐') {
// 个人套餐设置用户ID套餐所有者
basicInfo.userId = userStore.userId || userStore.nickName;
}
// 全院套餐:不需要额外设置用户信息
// 准备明细数据
const detailData = packageItems.value.map(item => ({
packageName: packageName.value.trim(),
itemName: item.name,
dosage: item.dosage,
route: item.route,
frequency: item.frequency,
days: item.days,
quantity: item.quantity,
unit: item.unit,
unitPrice: item.unitPrice,
amount: item.amount,
serviceFee: item.serviceFee,
totalAmount: item.totalAmount,
origin: item.origin,
createTime: new Date().toISOString(),
updateTime: new Date().toISOString()
}));
console.log('准备保存的基本信息:', basicInfo);
console.log('准备保存的明细数据:', detailData);
// 调用保存API暂时使用模拟保存后续对接真实API
savePackageData(basicInfo, detailData);
};
// 保存套餐数据到数据库
const savePackageData = async (basicInfo, detailData) => {
// 显示保存进度
const loading = ElLoading.service({
lock: true,
text: '正在保存...',
background: 'rgba(0, 0, 0, 0.7)',
});
try {
// 1. 先保存基本信息
console.log('准备发送的基本信息数据:', basicInfo);
const basicResponse = await addInspectionPackage(basicInfo);
console.log('后端返回的完整响应:', basicResponse);
console.log('响应类型:', typeof basicResponse);
console.log('响应数据结构:', JSON.stringify(basicResponse, null, 2));
// 检查响应码
if (basicResponse.code !== 200) {
loading.close();
throw new Error(basicResponse.msg || '保存基本信息失败');
}
// 检查响应数据结构 - 兼容多种可能的响应格式
let packageId = null;
if (basicResponse.data) {
// 标准格式:{code: 200, data: {packageId: xxx}}
packageId = basicResponse.data.packageId || basicResponse.data.id;
} else if (basicResponse.packageId) {
// 如果data不存在尝试直接从响应根级别获取
packageId = basicResponse.packageId;
} else if (basicResponse.id) {
// 如果data不存在尝试直接从响应根级别获取id
packageId = basicResponse.id;
}
// 验证套餐ID是否存在
if (!packageId) {
loading.close();
console.error('无法从响应中获取套餐ID完整响应:', basicResponse);
throw new Error('保存成功但未返回套餐ID。请检查后端接口是否正确返回了packageId或id字段');
}
console.log('成功获取到套餐ID:', packageId);
// 2. 分别保存每个明细数据到明细表
for (let i = 0; i < detailData.length; i++) {
const detailItem = {
...detailData[i],
packageId: packageId
};
console.log(`保存第 ${i + 1} 个明细项:`, detailItem);
const detailResponse = await saveInspectionPackageDetails(detailItem);
if (detailResponse.code !== 200) {
loading.close();
throw new Error(`保存第 ${i + 1} 个明细项失败: ${detailResponse.msg || '未知错误'}`);
}
console.log(`${i + 1} 个明细项保存成功:`, detailResponse.data);
}
console.log('所有明细项保存完成');
// 关闭加载提示
loading.close();
ElMessage.success('保存成功');
// 保存成功后重置表单
doResetForm();
} catch (error) {
// 确保在错误时也关闭loading
loading.close();
console.error('保存失败:', error);
// 处理不同类型的错误
let errorMessage = '保存失败,请重试';
if (error.response) {
// 服务器返回错误状态码
const status = error.response.status;
const data = error.response.data;
if (status === 400) {
errorMessage = data.msg || '请求参数错误';
} else if (status === 401) {
errorMessage = '未授权,请重新登录';
} else if (status === 403) {
errorMessage = '没有权限执行此操作';
} else if (status === 500) {
errorMessage = '服务器内部错误';
} else {
errorMessage = data.msg || `请求失败 (${status})`;
}
} else if (error.request) {
// 网络错误
errorMessage = '网络连接失败,请检查网络设置';
} else {
// 其他错误包括我们throw的Error
errorMessage = error.message || '保存失败,请重试';
}
ElMessage.error(errorMessage);
}
};
// 重置表单(带确认对话框)
const resetForm = () => {
ElMessageBox.confirm('确定要重置表单吗?所有未保存的数据将丢失。', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
doResetForm();
ElMessage.success('表单已重置');
}).catch(() => {
// 用户取消重置
});
};
// 执行表单重置(不带确认对话框,用于保存成功后自动重置)
const doResetForm = () => {
// 重置基本信息
packageLevel.value = '';
packageName.value = '';
department.value = '';
discount.value = '';
isDisabled.value = false;
showPackageName.value = true;
generateServiceFee.value = true;
enablePackagePrice.value = true;
packageAmount.value = 0.00;
serviceFee.value = 0.00;
selectedLisGroup.value = '';
bloodVolume.value = '';
remarks.value = '';
// 清空明细数据
packageItems.value = [];
// 重新计算金额
calculateAmounts();
};
// 加载检验套餐数据(用于编辑现有套餐)
const loadInspectionPackage = async (packageId) => {
try {
const loading = ElLoading.service({
lock: true,
text: '正在加载套餐数据...',
background: 'rgba(0, 0, 0, 0.7)',
});
// 获取基本信息
const basicResponse = await getInspectionPackage(packageId);
if (basicResponse.code !== 200) {
throw new Error(basicResponse.msg || '加载基本信息失败');
}
// 获取明细数据
const detailResponse = await listInspectionPackageDetails(packageId);
if (detailResponse.code !== 200) {
throw new Error(detailResponse.msg || '加载明细数据失败');
}
const basicData = basicResponse.data;
const detailData = detailResponse.data || [];
// 填充基本信息
packageLevel.value = basicData.packageLevel;
packageName.value = basicData.packageName;
department.value = basicData.department;
discount.value = basicData.discount || '';
isDisabled.value = basicData.isDisabled || false;
showPackageName.value = basicData.showPackageName !== false;
generateServiceFee.value = basicData.generateServiceFee !== false;
enablePackagePrice.value = basicData.enablePackagePrice !== false;
packageAmount.value = basicData.packageAmount || 0.00;
serviceFee.value = basicData.serviceFee || 0.00;
selectedLisGroup.value = basicData.lisGroup || '';
bloodVolume.value = basicData.bloodVolume || '';
remarks.value = basicData.remarks || '';
// 填充明细数据
packageItems.value = detailData.map(item => ({
name: item.itemName || item.name,
dosage: item.dosage || '',
route: item.route || '',
frequency: item.frequency || '',
days: item.days || '',
quantity: item.quantity || 1,
unit: item.unit || '',
unitPrice: parseFloat(item.unitPrice || 0),
amount: parseFloat(item.amount || 0),
serviceFee: parseFloat(item.serviceFee || 0),
totalAmount: parseFloat(item.totalAmount || 0),
origin: item.origin || ''
}));
loading.close();
ElMessage.success('套餐数据加载成功');
} catch (error) {
console.error('加载套餐数据失败:', error);
ElMessage.error(error.message || '加载套餐数据失败');
}
};
const handlePackageManagement = () => {
// 跳转到套餐管理页面
router.push({
path: '/maintainSystem/Inspection/PackageManagement'
});
};
const refreshPage = () => {
getInspectionTypeList();
// 刷新时也重新加载套餐项目
loadPackageItemsFromAPI();
};
// 页面加载时获取数据
onMounted(() => {
getInspectionTypeList();
getLisGroupList();
// 加载检验项目数据
loadObservationItems();
// 加载检验套餐明细项目
loadPackageItemsFromAPI();
// 检查URL参数如果有tab参数则切换到对应导航项
const query = router.currentRoute.value.query;
if (query.tab === '0' || query.tab === '1' || query.tab === '2') {
activeNav.value = parseInt(query.tab);
}
// 初始化计算套餐金额和服务费
calculateAmounts();
});
// 监听生成服务费选项变更
watch(generateServiceFee, (newVal) => {
calculateAmounts();
});
// 监听套餐项目变化
watch(packageItems, (newVal) => {
calculateAmounts();
}, { deep: true });
</script>
<style>
/* 基础样式重置 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "PingFang SC", "Microsoft YaHei", sans-serif;
}
/* 整体布局 */
.inspection-container {
display: flex;
width: 100%;
height: 100vh;
background-color: #f5f7fa;
}
/* 左侧导航 */
.side-nav {
width: 200px;
background-color: #fff;
box-shadow: 1px 0 5px rgba(0, 0, 0, 0.05);
padding: 20px 0;
}
.nav-item {
height: 40px;
display: flex;
align-items: center;
padding: 0 20px;
cursor: pointer;
transition: all 0.2s;
color: #333;
}
.nav-item:hover {
background-color: #f0f7ff;
color: #1890ff;
}
.nav-item.active {
background-color: #1890ff;
color: #fff;
}
/* 右侧主内容 */
.main-content {
flex: 1;
padding: 20px;
overflow-y: auto;
overflow-x: hidden;
max-width: 100%;
}
/* 页面标题 */
.page-header {
margin-bottom: 20px;
}
.page-header h2 {
font-size: 18px;
font-weight: 600;
color: #333;
}
/* 头部操作按钮 */
.header-actions {
display: flex;
justify-content: flex-start;
margin-bottom: 20px;
}
.add-new-btn {
height: 32px;
padding: 0 16px;
background-color: #1890ff;
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
display: flex;
align-items: center;
gap: 4px;
transition: all 0.2s;
}
.add-new-btn:hover {
background-color: #40a9ff;
}
/* 过滤区域 */
.filter-section {
background-color: #fff;
padding: 16px;
border-radius: 4px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04);
display: flex;
gap: 16px;
align-items: center;
margin-bottom: 16px;
}
.filter-item {
display: flex;
align-items: center;
gap: 8px;
}
.filter-item label {
font-size: 14px;
color: #333;
white-space: nowrap;
font-weight: 500;
}
.filter-select {
width: 120px;
height: 32px;
border: 1px solid #d9d9d9;
border-radius: 4px;
padding: 0 8px;
font-size: 14px;
background-color: #fff;
cursor: pointer;
transition: all 0.2s;
}
.filter-select:focus {
outline: none;
border-color: #1890ff;
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}
.filter-input {
width: 180px;
height: 32px;
border: 1px solid #d9d9d9;
border-radius: 4px;
padding: 0 8px;
font-size: 14px;
transition: all 0.2s;
}
.filter-input:focus {
outline: none;
border-color: #1890ff;
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}
.filter-actions {
display: flex;
gap: 8px;
margin-left: auto;
}
/* 按钮样式 */
.btn {
height: 32px;
padding: 0 16px;
border: 1px solid #d9d9d9;
border-radius: 4px;
cursor: pointer;
display: flex;
align-items: center;
gap: 4px;
font-size: 14px;
transition: all 0.2s;
}
.btn-primary {
background-color: #1890ff;
color: #fff;
border-color: #1890ff;
}
.btn-primary:hover {
background-color: #40a9ff;
border-color: #40a9ff;
}
.btn-secondary {
background-color: #f5f5f5;
color: #333;
}
.btn-secondary:hover {
background-color: #e8e8e8;
}
/* 表格容器 */
.table-container {
background-color: #fff;
border-radius: 4px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.03);
overflow: hidden;
margin-bottom: 20px;
}
/* 数据表格 */
.data-table {
width: 100%;
border-collapse: collapse;
}
.data-table th,
.data-table td {
height: 36px;
padding: 0 12px;
text-align: left;
border-bottom: 1px solid #f0f0f0;
}
.data-table th {
background-color: #fafafa;
font-weight: 600;
color: #333;
white-space: nowrap;
}
.data-table tr:hover {
background-color: #fafafa;
}
.data-table tr.editing {
background-color: #f0f7ff;
box-shadow: 0 0 0 1px #1890ff;
}
/* 操作列 */
.action-cell {
display: flex;
justify-content: center;
align-items: center;
gap: 8px;
position: relative;
z-index: 10;
height: 100%;
}
.data-table-type .action-cell {
justify-content: flex-start;
padding-left: 24px;
}
/* 套餐明细表格的操作列垂直居中 */
.data-table .action-cell {
vertical-align: middle;
}
.action-btn {
width: 28px;
height: 28px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border: none;
transition: all 0.2s ease;
font-size: 16px;
font-weight: bold;
position: relative;
z-index: 10;
background-color: #66b1ff;
color: white;
flex-shrink: 0;
}
.confirm-btn {
background-color: #1890FF;
color: white;
}
.edit-btn {
background-color: #FFC107;
color: white;
font-size: 14px;
}
.add-btn {
background-color: #1890FF;
color: white;
}
.delete-btn {
background-color: #FF4D4F;
color: white;
font-size: 14px;
z-index: 20;
pointer-events: auto;
}
.cancel-btn {
background-color: #FFA500;
color: white;
font-size: 14px;
}
/* 分页 */
.pagination {
display: flex;
justify-content: center;
align-items: center;
gap: 8px;
margin-top: 20px;
}
.page-btn {
padding: 6px 12px;
border: 1px solid #d9d9d9;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s;
}
.page-btn:hover {
border-color: #1890ff;
color: #1890ff;
}
.page-btn.active {
background-color: #1890ff;
color: #fff;
border-color: #1890ff;
}
/* 套餐设置样式 */
.top-bar {
height: 48px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 16px;
border-bottom: 1px solid #e8e8e8;
background: #fff;
margin-bottom: 16px;
gap: 16px;
}
.action-group {
display: flex;
gap: 12px;
}
.btn {
height: 32px;
padding: 0 16px;
border-radius: 4px;
font-weight: 500;
font-size: 14px;
display: inline-flex;
align-items: center;
justify-content: center;
cursor: pointer;
border: none;
transition: all 0.2s;
}
.btn-icon {
padding: 0 12px;
}
.btn-icon i {
margin-right: 4px;
}
.btn-primary {
background-color: #1890ff;
color: #fff;
}
.btn-success {
background-color: #00b27a;
color: #fff;
}
.btn-lg {
height: 36px;
padding: 0 24px;
font-size: 14px;
background-color: #0fb26d;
color: white;
transition: all 0.2s;
}
.btn-lg:hover {
background-color: #0d9d5f;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.btn-lg:active {
transform: translateY(1px);
}
/* 表单区域 */
.form-section {
background: #fff;
border-radius: 4px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.03);
padding: 16px;
margin-bottom: 16px;
max-width: 100%;
overflow-x: hidden;
}
.section-title {
font-weight: 700;
font-size: 14px;
margin-bottom: 16px;
padding-bottom: 8px;
border-bottom: 1px solid #f0f0f0;
}
.form-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 16px;
}
.form-item {
display: flex;
align-items: center;
min-width: 0;
max-width: 100%;
}
.form-label {
width: 80px;
text-align: right;
padding-right: 12px;
color: #666;
flex-shrink: 0;
}
/* 响应式设计 */
@media (max-width: 992px) {
/* 隐藏左侧导航 */
.side-nav {
display: none;
}
/* 主内容区域设置为100%宽度 */
.main-content {
margin-left: 0;
width: 100%;
padding: 10px;
}
/* 过滤区域垂直排列 */
.filter-section {
flex-direction: column;
align-items: flex-start;
gap: 12px;
}
.filter-item {
width: 100%;
justify-content: flex-start;
}
/* 过滤操作按钮换行 */
.filter-actions {
width: 100%;
flex-wrap: wrap;
justify-content: flex-start;
margin-left: 0;
}
/* 调整按钮样式 */
.filter-actions .btn {
margin-right: 8px;
margin-bottom: 8px;
}
/* 调整表单网格布局 */
.form-grid {
grid-template-columns: repeat(2, 1fr);
}
/* 操作按钮换行显示 */
.filter-actions {
width: 100%;
flex-wrap: wrap;
justify-content: flex-start;
}
/* 表格容器在小屏幕上添加横向滚动 */
.table-container {
overflow-x: auto;
}
/* 数据表格在小屏幕上保持最小宽度 */
.data-table {
min-width: 1000px;
}
/* 调整表单标签宽度 */
.form-label {
width: 60px;
}
/* 调整输入框和选择框样式 */
.filter-input,
.filter-select {
width: calc(100% - 80px);
}
/* 表单网格调整为2列 */
.form-grid {
grid-template-columns: repeat(2, 1fr);
}
}
.form-control {
flex: 1;
height: 32px;
border: 1px solid #d9d9d9;
border-radius: 4px;
padding: 0 8px;
font-size: 14px;
max-width: 100%;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.form-control:focus {
outline: none;
border-color: #1890ff;
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}
.form-select {
width: 100%;
background: white;
appearance: none;
}
.radio-group {
display: flex;
gap: 1px;
}
.radio-item {
display: flex;
align-items: center;
gap: 4px;
}
/* 表格头部 */
.table-header {
display: flex;
justify-content: space-between;
margin-bottom: 16px;
padding: 16px;
}
.table-title {
font-weight: 700;
font-size: 14px;
}
/* 表格内操作按钮 */
.table-actions {
display: flex;
gap: 8px;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
flex-wrap: nowrap;
}
/* 表格内输入框和选择框样式 */
.data-table input,
.data-table select {
width: 100%;
height: 28px;
padding: 0 8px;
border: 1px solid #d9d9d9;
border-radius: 4px;
font-size: 12px;
box-sizing: border-box;
}
.data-table input:focus,
.data-table select:focus {
outline: none;
border-color: #40a9ff;
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}
/* 编辑模式行样式 */
.data-table tr.editing {
background-color: #f0f7ff;
box-shadow: 0 0 0 1px #1890ff;
}
/* 响应式设计 */
@media (max-width: 1200px) {
.form-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 992px) {
.inspection-container {
flex-direction: column;
}
.side-nav {
display: none;
}
.main-content {
margin-left: 0;
width: 100%;
padding: 10px;
}
.filter-section {
flex-direction: column;
gap: 12px;
}
.filter-item {
width: 100%;
}
.filter-actions {
width: 100%;
justify-content: flex-start;
flex-wrap: wrap;
gap: 8px;
}
.form-grid {
grid-template-columns: 1fr;
}
.table-container {
overflow-x: auto;
}
.data-table {
min-width: 1000px;
}
}
</style>