2268 lines
75 KiB
Vue
2268 lines
75 KiB
Vue
<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">
|
||
<!-- 调试按钮 -->
|
||
<div style="margin-bottom: 10px;">
|
||
<el-button @click="debugDepartments">调试科室数据</el-button>
|
||
</div>
|
||
<table class="data-table">
|
||
<thead>
|
||
<tr>
|
||
<th>行</th>
|
||
<th>*大类编码</th>
|
||
<th>*大类项目名称</th>
|
||
<th>*执行科室</th>
|
||
<th>序号</th>
|
||
<th>备注</th>
|
||
<th>操作</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>操作</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)">
|
||
<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="20 6 9 17 4 12"></polyline>
|
||
</svg>
|
||
保存
|
||
</div>
|
||
<div class="action-btn cancel-btn" @click="cancelEdit(item)">
|
||
<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="18" y1="6" x2="6" y2="18"></line>
|
||
<line x1="6" y1="6" x2="18" y2="18"></line>
|
||
</svg>
|
||
取消
|
||
</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-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>
|
||
<select class="form-control form-select" v-model="packageCategory" disabled>
|
||
<option value="检验套餐">检验套餐</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-item">
|
||
<span class="form-label">套餐级别</span>
|
||
<select class="form-control form-select" id="packageLevel" v-model="packageLevel">
|
||
<option value="">请选择套餐级别</option>
|
||
<option value="全院套餐">全院套餐</option>
|
||
<option value="科室套餐">科室套餐</option>
|
||
<option value="个人套餐">个人套餐</option>
|
||
</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>
|
||
<select class="form-control form-select">
|
||
<option value="">请选择用户</option>
|
||
<option value="1">张三</option>
|
||
<option value="2">李四</option>
|
||
<option value="3">王五</option>
|
||
<option value="4">赵六</option>
|
||
</select>
|
||
</div>
|
||
<div class="form-item">
|
||
<span class="form-label"><span style="color:red">*</span>套餐名称</span>
|
||
<el-autocomplete
|
||
ref="autocompleteRef"
|
||
v-model="packageName"
|
||
:fetch-suggestions="queryDiagnosisItems"
|
||
placeholder="输入套餐名称或首字母搜索"
|
||
class="form-control"
|
||
@select="handleSelectPackage"
|
||
@focus="handleFocus"
|
||
clearable
|
||
trigger-on-focus
|
||
:min-length="0"
|
||
>
|
||
<template #suffix>
|
||
<i class="el-icon-search"></i>
|
||
</template>
|
||
</el-autocomplete>
|
||
<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>
|
||
<div class="radio-group">
|
||
<label class="radio-item">
|
||
<input type="radio" v-model="isDisabled" :value="false" checked> 启用
|
||
</label>
|
||
<label class="radio-item">
|
||
<input type="radio" v-model="isDisabled" :value="true"> 停用
|
||
</label>
|
||
</div>
|
||
</div>
|
||
<div class="form-item">
|
||
<span class="form-label">显示套餐名</span>
|
||
<div class="radio-group">
|
||
<label class="radio-item">
|
||
<input type="radio" v-model="showPackageName" :value="true" checked> 是
|
||
</label>
|
||
<label class="radio-item">
|
||
<input type="radio" v-model="showPackageName" :value="false"> 否
|
||
</label>
|
||
</div>
|
||
</div>
|
||
<div class="form-item">
|
||
<span class="form-label">生成服务费</span>
|
||
<div class="radio-group">
|
||
<label class="radio-item">
|
||
<input type="radio" v-model="generateServiceFee" :value="true" checked> 是
|
||
</label>
|
||
<label class="radio-item">
|
||
<input type="radio" v-model="generateServiceFee" :value="false"> 否
|
||
</label>
|
||
</div>
|
||
</div>
|
||
<div class="form-item">
|
||
<span class="form-label">套餐价格</span>
|
||
<div class="radio-group">
|
||
<label class="radio-item">
|
||
<input type="radio" v-model="enablePackagePrice" :value="true" checked> 启用
|
||
</label>
|
||
<label class="radio-item">
|
||
<input type="radio" v-model="enablePackagePrice" :value="false"> 不启用
|
||
</label>
|
||
</div>
|
||
</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>
|
||
<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">
|
||
<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">
|
||
</template>
|
||
<template v-else>
|
||
{{ item.quantity || '-' }}
|
||
</template>
|
||
</td>
|
||
<td>{{ item.unit || '-' }}</td>
|
||
<td>{{ item.unitPrice.toFixed(2) }}</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">
|
||
</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;">
|
||
<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>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import useUserStore from '@/store/modules/user';
|
||
import { ref, reactive, onMounted, watch, computed, nextTick, getCurrentInstance } from 'vue';
|
||
import { ElMessage, ElAutocomplete, ElMessageBox } from 'element-plus';
|
||
import * as echarts from 'echarts';
|
||
import { useRouter, useRoute } from 'vue-router';
|
||
import { formatDate } from '@/utils/index';
|
||
import request from '@/utils/request';
|
||
import { listInspectionType, getInspectionType, addInspectionType, updateInspectionType, delInspectionType } from '@/api/system/inspectionType';
|
||
import { listLisGroup } from '@/api/system/checkType';
|
||
import { getDiagnosisTreatmentList } from '@/views/catalog/diagnosistreatment/components/diagnosistreatment';
|
||
import { getLocationTree } from '@/views/charge/outpatientregistration/components/outpatientregistration';
|
||
|
||
// 获取当前登录用户信息
|
||
const userStore = useUserStore();
|
||
|
||
// 创建路由实例
|
||
const router = useRouter();
|
||
const route = useRoute();
|
||
|
||
// 存储LIS分组数据
|
||
const lisGroupList = 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 handlePackageDepartmentChange(selectedNode) {
|
||
console.log('选择的套餐科室节点:', selectedNode);
|
||
// 如果selectedNode是对象,只取name属性
|
||
if (typeof selectedNode === 'object' && selectedNode !== null) {
|
||
department.value = selectedNode.name;
|
||
} else {
|
||
// 否则直接使用(可能是字符串)
|
||
department.value = selectedNode;
|
||
}
|
||
}
|
||
|
||
// 调试科室数据
|
||
function debugDepartments() {
|
||
console.log('当前科室数据:', departments.value);
|
||
console.log('第一个检验项目科室:', inspectionItems.value[0]?.department);
|
||
// 尝试模拟选择一个科室
|
||
if (departments.value && departments.value.length > 0) {
|
||
const firstDept = departments.value[0];
|
||
console.log('尝试选择科室:', firstDept.name);
|
||
inspectionItems.value[0].department = firstDept.name;
|
||
}
|
||
}
|
||
|
||
// 费用套餐数据
|
||
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: '其他检验' }
|
||
]);
|
||
|
||
// 检验项目数据
|
||
const inspectionItems = ref([
|
||
{ id: 1, code: '0101', name: '血常规五分类', testType: '生化', package: '肝功能12项', sampleType: '血液', amount: 36.00, sortOrder: 1, serviceRange: '全部', sub医技Type: '', remark: '', status: true },
|
||
{ id: 2, code: '0102', name: '肝功能12项', testType: '生化', package: '肝功能12项', sampleType: '血液', amount: 120.00, sortOrder: 2, serviceRange: '全部', sub医技Type: '', remark: '', status: true },
|
||
{ id: 3, code: '0201', name: '尿常规', testType: '常规检验', package: '', sampleType: '尿液', amount: 25.00, sortOrder: 3, serviceRange: '全部', sub医技Type: '', remark: '', status: true },
|
||
{ id: 4, code: '0202', name: '便常规+潜血', testType: '常规检验', package: '', sampleType: '粪便', amount: 30.00, sortOrder: 4, serviceRange: '门诊', sub医技Type: '', remark: '', status: true },
|
||
{ id: 5, code: '0301', name: '乙肝五项', testType: '免疫学检验', package: '乙肝套餐', sampleType: '血液', amount: 75.00, sortOrder: 5, serviceRange: '全部', sub医技Type: '', remark: '', status: true },
|
||
{ id: 6, code: '0302', name: '丙肝抗体', testType: '免疫学检验', package: '', sampleType: '血液', amount: 45.00, sortOrder: 6, serviceRange: '住院', sub医技Type: '', remark: '', status: true },
|
||
{ id: 7, code: '0401', name: '血糖', testType: '生化', package: '糖尿病套餐', sampleType: '血液', amount: 15.00, sortOrder: 7, serviceRange: '全部', sub医技Type: '', remark: '', status: true },
|
||
{ id: 8, code: '0402', name: '糖化血红蛋白', testType: '生化', package: '糖尿病套餐', sampleType: '血液', amount: 50.00, sortOrder: 8, serviceRange: '全部', sub医技Type: '', remark: '', status: true },
|
||
{ id: 9, code: '0501', name: '肌酐', testType: '生化', package: '肾功能套餐', sampleType: '血液', amount: 25.00, sortOrder: 9, serviceRange: '住院', sub医技Type: '', remark: '', status: true },
|
||
{ id: 10, code: '0502', name: '尿素氮', testType: '生化', package: '肾功能套餐', sampleType: '血液', amount: 20.00, sortOrder: 10, serviceRange: '住院', sub医技Type: '', remark: '', status: true },
|
||
{ id: 11, code: '0601', name: '白带常规', testType: '常规检验', package: '', sampleType: '分泌物', amount: 30.00, sortOrder: 11, serviceRange: '门诊', sub医技Type: '', remark: '', status: true },
|
||
{ id: 12, code: '0602', name: '前列腺液常规', testType: '常规检验', package: '', sampleType: '分泌物', amount: 35.00, sortOrder: 12, serviceRange: '门诊', sub医技Type: '', remark: '', status: true },
|
||
{ id: 13, code: '0701', name: '脑脊液常规', testType: '常规检验', package: '', sampleType: '脑脊液', amount: 60.00, sortOrder: 13, serviceRange: '住院', sub医技Type: '', remark: '', status: true },
|
||
{ id: 14, code: '0801', name: '肿瘤标志物CA125', testType: '免疫学检验', package: '肿瘤筛查套餐', sampleType: '血液', amount: 120.00, sortOrder: 14, serviceRange: '体检', sub医技Type: '', remark: '', status: true },
|
||
{ id: 15, code: '0802', name: '肿瘤标志物AFP', testType: '免疫学检验', package: '肿瘤筛查套餐', sampleType: '血液', amount: 80.00, sortOrder: 15, serviceRange: '体检', sub医技Type: '', remark: '', status: true }
|
||
]);
|
||
|
||
// 过滤条件
|
||
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 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('');
|
||
const autocompleteRef = ref();
|
||
const packageItems = ref([
|
||
{ name: '血常规五分类', dosage: '项/人', route: '项/人', frequency: '', days: '', quantity: 1, unit: '项', unitPrice: 25.00, amount: 25.00, serviceFee: 0.00, totalAmount: 25.00, origin: '' },
|
||
{ name: '总IgE测定', dosage: '项/人', route: '项/人', frequency: '', days: '', quantity: 1, unit: '项', unitPrice: 30.00, amount: 30.00, serviceFee: 0.00, totalAmount: 30.00, origin: '' },
|
||
{ name: '肝功能12项', dosage: '项/人', route: '项/人', frequency: '', days: '', quantity: 1, unit: '项', unitPrice: 120.00, amount: 120.00, serviceFee: 0.00, totalAmount: 120.00, origin: '' },
|
||
{ name: '肾功能三项', dosage: '项/人', route: '项/人', frequency: '', days: '', quantity: 1, unit: '项', unitPrice: 50.00, amount: 50.00, serviceFee: 0.00, totalAmount: 50.00, origin: '' },
|
||
{ name: '血糖', dosage: '项/人', route: '项/人', frequency: '', days: '', quantity: 1, unit: '项', unitPrice: 15.00, amount: 15.00, serviceFee: 0.00, totalAmount: 15.00, origin: '' },
|
||
{ name: '糖化血红蛋白', dosage: '项/人', route: '项/人', frequency: '', days: '', quantity: 1, unit: '项', unitPrice: 50.00, amount: 50.00, serviceFee: 0.00, totalAmount: 50.00, origin: '' },
|
||
{ name: '血脂五项', dosage: '项/人', route: '项/人', frequency: '', days: '', quantity: 1, unit: '项', unitPrice: 80.00, amount: 80.00, serviceFee: 0.00, totalAmount: 80.00, origin: '' }
|
||
]);
|
||
|
||
// 查询诊疗目录中的检验项目
|
||
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) => {
|
||
// 可以在这里添加项目选择逻辑,或者留空让用户直接输入
|
||
};
|
||
|
||
// 处理输入框获得焦点事件,手动触发下拉框显示
|
||
const handleFocus = () => {
|
||
// 延迟执行,确保输入框已经获得焦点
|
||
setTimeout(() => {
|
||
// 手动触发查询,获取所有检验项目
|
||
if (autocompleteRef.value) {
|
||
// 模拟空输入查询
|
||
queryDiagnosisItems('', (results) => {
|
||
// 设置建议列表
|
||
autocompleteRef.value.suggestions = results;
|
||
// 显示下拉框
|
||
autocompleteRef.value.showPopper = true;
|
||
});
|
||
}
|
||
}, 100);
|
||
};
|
||
|
||
// 添加新的套餐项目
|
||
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) {
|
||
// 保存编辑
|
||
editingRowId.value = null;
|
||
ElMessage.success('保存成功');
|
||
} else {
|
||
// 进入编辑模式
|
||
editingRowId.value = index;
|
||
}
|
||
};
|
||
|
||
// 计算单个项目的服务费(基于折扣后的金额)
|
||
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 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() === '') {
|
||
alert('检验类型编码不能为空');
|
||
return;
|
||
}
|
||
|
||
if (!submitData.name || submitData.name.trim() === '') {
|
||
alert('检验类型名称不能为空');
|
||
return;
|
||
}
|
||
|
||
if (!submitData.department || submitData.department.trim() === '') {
|
||
alert('执行科室不能为空');
|
||
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 + 0.5, 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('调用删除API,ID:', 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 = (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);
|
||
|
||
// 保存成功
|
||
editingRowId.value = null;
|
||
ElMessage.success('保存成功');
|
||
};
|
||
|
||
const deleteItem = (id) => {
|
||
ElMessageBox.confirm('确定要删除该检验项目吗?', '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
}).then(() => {
|
||
const index = inspectionItems.value.findIndex(item => item.id === id);
|
||
if (index !== -1) {
|
||
inspectionItems.value.splice(index, 1);
|
||
ElMessage.success('删除成功');
|
||
}
|
||
}).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) {
|
||
alert('请选择套餐级别');
|
||
return;
|
||
}
|
||
|
||
// 这里可以添加其他表单验证和保存逻辑
|
||
console.log('保存表单数据');
|
||
};
|
||
|
||
const handlePackageManagement = () => {
|
||
// 跳转到套餐管理页面
|
||
router.push({
|
||
path: '/maintainSystem/Inspection/PackageManagement'
|
||
});
|
||
};
|
||
|
||
const refreshPage = () => {
|
||
getInspectionTypeList();
|
||
};
|
||
|
||
// 页面加载时获取数据
|
||
onMounted(() => {
|
||
getInspectionTypeList();
|
||
getLisGroupList();
|
||
// 检查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;
|
||
}
|
||
|
||
/* 页面标题 */
|
||
.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;
|
||
gap: 8px;
|
||
position: relative;
|
||
z-index: 10;
|
||
}
|
||
|
||
.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;
|
||
}
|
||
|
||
.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;
|
||
}
|
||
|
||
/* 分页 */
|
||
.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;
|
||
}
|
||
|
||
.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;
|
||
}
|
||
|
||
.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;
|
||
}
|
||
|
||
.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;
|
||
}
|
||
|
||
/* 表格内输入框和选择框样式 */
|
||
.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> |