feat: 抗菌药物规则 + 药品库存预警页面增强
- 抗菌药物规则页面增强(统计卡片/新增/详情/筛选) - 药品库存预警页面增强(统计卡片/新增/补货申请/导出)
This commit is contained in:
@@ -1,22 +1,198 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="q" :inline="true"><el-form-item label="药品编码"><el-input v-model="q.drugCode" clearable /></el-form-item>
|
||||
<el-form-item><el-button type="primary" @click="getList">查询</el-button></el-form-item></el-form>
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="药品" prop="drugName" width="150" />
|
||||
<el-table-column label="抗菌类别" prop="antibioticClass" width="120">
|
||||
<template #default="s"><el-tag>{{ {RESTRICTED:'限制使用',NONRESTRICTED:'非限制使用',SPECIAL:'特殊使用'}[s.row.antibioticClass] }}</el-tag></template>
|
||||
<div style="margin-bottom:16px;display:flex;justify-content:space-between;align-items:center">
|
||||
<span style="font-size:18px;font-weight:bold">抗菌药物管理规则</span>
|
||||
<div>
|
||||
<el-button type="primary" @click="getList">刷新</el-button>
|
||||
<el-button type="success" @click="showAdd = true">新增规则</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 统计卡片 -->
|
||||
<el-row :gutter="16" style="margin-bottom:16px">
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover" :body-style="{padding:'12px'}">
|
||||
<div style="text-align:center">
|
||||
<div style="font-size:24px;font-weight:bold;color:#409eff">{{ stats.total || 0 }}</div>
|
||||
<div style="color:#999">总规则数</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover" :body-style="{padding:'12px'}">
|
||||
<div style="text-align:center">
|
||||
<div style="font-size:24px;font-weight:bold;color:#67c23a">{{ stats.nonRestricted || 0 }}</div>
|
||||
<div style="color:#999">非限制使用</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover" :body-style="{padding:'12px'}">
|
||||
<div style="text-align:center">
|
||||
<div style="font-size:24px;font-weight:bold;color:#e6a23c">{{ stats.restricted || 0 }}</div>
|
||||
<div style="color:#999">限制使用</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-card shadow="hover" :body-style="{padding:'12px'}">
|
||||
<div style="text-align:center">
|
||||
<div style="font-size:24px;font-weight:bold;color:#f56c6c">{{ stats.special || 0 }}</div>
|
||||
<div style="color:#999">特殊使用</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 筛选 -->
|
||||
<el-form :model="q" :inline="true" style="margin-bottom:16px">
|
||||
<el-form-item label="药品编码"><el-input v-model="q.drugCode" clearable placeholder="请输入药品编码" style="width:180px"/></el-form-item>
|
||||
<el-form-item label="抗菌类别">
|
||||
<el-select v-model="q.antibioticClass" clearable placeholder="请选择" style="width:140px">
|
||||
<el-option label="非限制使用" value="NONRESTRICTED"/>
|
||||
<el-option label="限制使用" value="RESTRICTED"/>
|
||||
<el-option label="特殊使用" value="SPECIAL"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="getList">查询</el-button>
|
||||
<el-button @click="resetQuery">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- 数据表格 -->
|
||||
<el-table v-loading="loading" :data="list" border stripe>
|
||||
<el-table-column label="药品编码" prop="drugCode" width="120"/>
|
||||
<el-table-column label="药品名称" prop="drugName" width="160" show-overflow-tooltip/>
|
||||
<el-table-column label="抗菌类别" prop="antibioticClass" width="120" align="center">
|
||||
<template #default="s">
|
||||
<el-tag :type="s.row.antibioticClass==='SPECIAL'?'danger':s.row.antibioticClass==='RESTRICTED'?'warning':'success'" size="small">
|
||||
{{ {RESTRICTED:'限制使用',NONRESTRICTED:'非限制使用',SPECIAL:'特殊使用'}[s.row.antibioticClass] || s.row.antibioticClass }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="限制级别" prop="restrictionLevel" width="120" />
|
||||
<el-table-column label="最大疗程(天)" prop="maxDurationDays" width="120" />
|
||||
<el-table-column label="需审批" width="80">
|
||||
<template #default="s"><el-tag :type="s.row.requireApproval?'danger':'success'">{{ s.row.requireApproval?'是':'否' }}</el-tag></template>
|
||||
<el-table-column label="限制级别" prop="restrictionLevel" width="120" align="center"/>
|
||||
<el-table-column label="最大疗程(天)" prop="maxDurationDays" width="120" align="center"/>
|
||||
<el-table-column label="需审批" width="80" align="center">
|
||||
<template #default="s">
|
||||
<el-tag :type="s.row.requireApproval?'danger':'success'" size="small">
|
||||
{{ s.row.requireApproval?'是':'否' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="适应症" prop="indications" min-width="150" show-overflow-tooltip/>
|
||||
<el-table-column label="禁忌症" prop="contraindications" min-width="150" show-overflow-tooltip/>
|
||||
<el-table-column label="操作" width="100" fixed="right">
|
||||
<template #default="s">
|
||||
<el-button link type="primary" @click="handleDetail(s.row)">详情</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 新增弹窗 -->
|
||||
<el-dialog title="新增抗菌药物规则" v-model="showAdd" width="600px" append-to-body>
|
||||
<el-form :model="formData" label-width="110px">
|
||||
<el-form-item label="药品编码" required><el-input v-model="formData.drugCode" placeholder="请输入药品编码"/></el-form-item>
|
||||
<el-form-item label="药品名称" required><el-input v-model="formData.drugName" placeholder="请输入药品名称"/></el-form-item>
|
||||
<el-form-item label="抗菌类别" required>
|
||||
<el-select v-model="formData.antibioticClass" placeholder="请选择">
|
||||
<el-option label="非限制使用" value="NONRESTRICTED"/>
|
||||
<el-option label="限制使用" value="RESTRICTED"/>
|
||||
<el-option label="特殊使用" value="SPECIAL"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="限制级别"><el-input v-model="formData.restrictionLevel" placeholder="如: 副主任医师以上"/></el-form-item>
|
||||
<el-form-item label="最大疗程(天)"><el-input-number v-model="formData.maxDurationDays" :min="1" :max="365"/></el-form-item>
|
||||
<el-form-item label="需审批">
|
||||
<el-switch v-model="formData.requireApproval"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="适应症"><el-input v-model="formData.indications" type="textarea" :rows="2" placeholder="请输入适应症"/></el-form-item>
|
||||
<el-form-item label="禁忌症"><el-input v-model="formData.contraindications" type="textarea" :rows="2" placeholder="请输入禁忌症"/></el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="showAdd = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitForm">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 详情弹窗 -->
|
||||
<el-dialog title="抗菌药物规则详情" v-model="detailVisible" width="600px" append-to-body>
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="药品编码">{{ detailData.drugCode }}</el-descriptions-item>
|
||||
<el-descriptions-item label="药品名称">{{ detailData.drugName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="抗菌类别">
|
||||
<el-tag :type="detailData.antibioticClass==='SPECIAL'?'danger':detailData.antibioticClass==='RESTRICTED'?'warning':'success'">
|
||||
{{ {RESTRICTED:'限制使用',NONRESTRICTED:'非限制使用',SPECIAL:'特殊使用'}[detailData.antibioticClass] }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="限制级别">{{ detailData.restrictionLevel }}</el-descriptions-item>
|
||||
<el-descriptions-item label="最大疗程">{{ detailData.maxDurationDays }}天</el-descriptions-item>
|
||||
<el-descriptions-item label="需审批">{{ detailData.requireApproval ? '是' : '否' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="适应症" :span="2">{{ detailData.indications || '-' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="禁忌症" :span="2">{{ detailData.contraindications || '-' }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<template #footer><el-button @click="detailVisible = false">关闭</el-button></template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive } from 'vue'; import { getRules } from '@/api/antibiotic'
|
||||
const loading = ref(false); const list = ref([]); const q = reactive({ drugCode: '' })
|
||||
const getList = async () => { if (!q.drugCode) return; loading.value = true; const r = await getRules(q.drugCode); list.value = r.data || []; loading.value = false }
|
||||
import {ref, reactive, onMounted} from 'vue'
|
||||
import {ElMessage} from 'element-plus'
|
||||
import {getRules, getStatistics, addRule} from '@/api/antibiotic'
|
||||
|
||||
const loading = ref(false)
|
||||
const list = ref([])
|
||||
const showAdd = ref(false)
|
||||
const detailVisible = ref(false)
|
||||
const detailData = ref({})
|
||||
const stats = ref({total:0, nonRestricted:0, restricted:0, special:0})
|
||||
|
||||
const q = reactive({drugCode:'', antibioticClass:''})
|
||||
const formData = reactive({
|
||||
drugCode:'', drugName:'', antibioticClass:'NONRESTRICTED', restrictionLevel:'',
|
||||
maxDurationDays:7, requireApproval:false, indications:'', contraindications:''
|
||||
})
|
||||
|
||||
async function getList() {
|
||||
loading.value = true
|
||||
try {
|
||||
if (q.drugCode) {
|
||||
const r = await getRules(q.drugCode)
|
||||
list.value = r.data || []
|
||||
} else {
|
||||
// Load all rules
|
||||
const r = await getStatistics()
|
||||
list.value = r.data?.rules || []
|
||||
}
|
||||
// Calculate stats
|
||||
let nonRestricted = 0, restricted = 0, special = 0
|
||||
list.value.forEach(row => {
|
||||
if (row.antibioticClass === 'NONRESTRICTED') nonRestricted++
|
||||
else if (row.antibioticClass === 'RESTRICTED') restricted++
|
||||
else if (row.antibioticClass === 'SPECIAL') special++
|
||||
})
|
||||
stats.value = {total: list.value.length, nonRestricted, restricted, special}
|
||||
} finally { loading.value = false }
|
||||
}
|
||||
|
||||
function resetQuery() {
|
||||
q.drugCode = ''
|
||||
q.antibioticClass = ''
|
||||
getList()
|
||||
}
|
||||
|
||||
function handleDetail(row) {
|
||||
detailData.value = row
|
||||
detailVisible.value = true
|
||||
}
|
||||
|
||||
async function submitForm() {
|
||||
await addRule(formData)
|
||||
ElMessage.success('新增成功')
|
||||
showAdd.value = false
|
||||
getList()
|
||||
}
|
||||
|
||||
onMounted(() => getList())
|
||||
</script>
|
||||
|
||||
@@ -1,31 +1,167 @@
|
||||
<template>
|
||||
<div style="padding:16px">
|
||||
<div style="margin-bottom:16px"><span style="font-size:18px;font-weight:bold">药品库存预警</span></div>
|
||||
<el-card shadow="never" style="margin-bottom:16px">
|
||||
<div style="display:flex;gap:40px;text-align:center">
|
||||
<div><div style="font-size:28px;font-weight:bold;color:#e6a23c">{{ stats.low||0 }}</div><div>低库存</div></div>
|
||||
<div><div style="font-size:28px;font-weight:bold;color:#f56c6c">{{ stats.out||0 }}</div><div>缺货</div></div>
|
||||
<div style="margin-bottom:16px;display:flex;justify-content:space-between;align-items:center">
|
||||
<span style="font-size:18px;font-weight:bold">药品库存预警管理</span>
|
||||
<div>
|
||||
<el-button type="primary" @click="loadData">刷新</el-button>
|
||||
<el-button type="success" @click="showAdd = true">新增预警</el-button>
|
||||
<el-button type="warning" @click="exportReport">导出报告</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
<div style="margin-bottom:12px"><el-button type="success" @click="showAdd=true">新增预警</el-button></div>
|
||||
<el-table :data="alertData" border stripe>
|
||||
<el-table-column prop="drugName" label="药品" min-width="150"/>
|
||||
<el-table-column prop="drugSpec" label="规格" width="100"/>
|
||||
<el-table-column prop="currentStock" label="库存" width="80" align="center"/>
|
||||
<el-table-column prop="minStock" label="最低" width="80" align="center"/>
|
||||
<el-table-column prop="alertLevel" label="级别" width="100">
|
||||
<template #default="{row}"><el-tag :type="row.alertLevel==='OUT'?'danger':'warning'" size="small">{{ row.alertLevel==='OUT'?'缺货':'低库存' }}</el-tag></template>
|
||||
</div>
|
||||
|
||||
<!-- 统计卡片 -->
|
||||
<el-row :gutter="16" style="margin-bottom:16px">
|
||||
<el-col :span="4" v-for="item in statCards" :key="item.label">
|
||||
<el-card shadow="hover" :body-style="{padding:'10px'}">
|
||||
<div style="text-align:center">
|
||||
<div style="font-size:20px;font-weight:bold" :style="{color:item.color}">{{ item.value }}</div>
|
||||
<div style="font-size:12px;color:#999">{{ item.label }}</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 筛选 -->
|
||||
<div style="margin-bottom:12px;display:flex;gap:8px;flex-wrap:wrap">
|
||||
<el-select v-model="q.alertLevel" placeholder="预警级别" clearable style="width:120px">
|
||||
<el-option label="缺货" value="OUT"/>
|
||||
<el-option label="低库存" value="LOW"/>
|
||||
<el-option label="近效期" value="EXPIRING"/>
|
||||
</el-select>
|
||||
<el-select v-model="q.drugCategory" placeholder="药品类别" clearable style="width:140px">
|
||||
<el-option label="西药" value="WESTERN"/>
|
||||
<el-option label="中成药" value="CHINESE"/>
|
||||
<el-option label="中草药" value="HERBAL"/>
|
||||
<el-option label="生物制品" value="BIOLOGICAL"/>
|
||||
</el-select>
|
||||
<el-button type="primary" @click="loadData">查询</el-button>
|
||||
<el-button @click="resetQuery">重置</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 数据表格 -->
|
||||
<el-table :data="alertData" border stripe v-loading="loading">
|
||||
<el-table-column prop="drugCode" label="药品编码" width="120"/>
|
||||
<el-table-column prop="drugName" label="药品名称" min-width="160" show-overflow-tooltip/>
|
||||
<el-table-column prop="drugSpec" label="规格" width="120"/>
|
||||
<el-table-column prop="currentStock" label="当前库存" width="100" align="center">
|
||||
<template #default="{row}">
|
||||
<span :style="{color: row.currentStock <= 0 ? '#F56C6C' : row.currentStock < row.minStock ? '#E6A23C' : '#67C23A', fontWeight:'bold'}">
|
||||
{{ row.currentStock }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="minStock" label="最低库存" width="100" align="center"/>
|
||||
<el-table-column prop="maxStock" label="最高库存" width="100" align="center"/>
|
||||
<el-table-column prop="alertLevel" label="预警级别" width="100" align="center">
|
||||
<template #default="{row}">
|
||||
<el-tag :type="row.alertLevel==='OUT'?'danger':row.alertLevel==='LOW'?'warning':'info'" size="small">
|
||||
{{ row.alertLevel==='OUT'?'缺货':row.alertLevel==='LOW'?'低库存':row.alertLevel==='EXPIRING'?'近效期':'正常' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="supplierName" label="供应商" width="140"/>
|
||||
<el-table-column prop="lastRestockDate" label="上次进货" width="120"/>
|
||||
<el-table-column prop="expiryDate" label="有效期至" width="120"/>
|
||||
<el-table-column label="操作" width="120" fixed="right">
|
||||
<template #default="{row}">
|
||||
<el-button link type="primary" @click="handleOrder(row)">补货申请</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="supplierName" label="供应商" width="120"/>
|
||||
</el-table>
|
||||
|
||||
<el-pagination style="margin-top:12px;justify-content:flex-end" v-model:current-page="q.pageNo" v-model:page-size="q.pageSize" :total="total" layout="total, sizes, prev, pager, next, jumper" @size-change="loadData" @current-change="loadData"/>
|
||||
|
||||
<!-- 新增弹窗 -->
|
||||
<el-dialog title="新增药品库存预警" v-model="showAdd" width="600px" append-to-body>
|
||||
<el-form :model="formData" label-width="110px">
|
||||
<el-form-item label="药品编码" required><el-input v-model="formData.drugCode" placeholder="请输入药品编码"/></el-form-item>
|
||||
<el-form-item label="药品名称" required><el-input v-model="formData.drugName" placeholder="请输入药品名称"/></el-form-item>
|
||||
<el-form-item label="规格"><el-input v-model="formData.drugSpec" placeholder="如: 0.5g*24片"/></el-form-item>
|
||||
<el-form-item label="当前库存"><el-input-number v-model="formData.currentStock" :min="0"/></el-form-item>
|
||||
<el-form-item label="最低库存"><el-input-number v-model="formData.minStock" :min="0"/></el-form-item>
|
||||
<el-form-item label="最高库存"><el-input-number v-model="formData.maxStock" :min="0"/></el-form-item>
|
||||
<el-form-item label="预警级别">
|
||||
<el-select v-model="formData.alertLevel" placeholder="请选择">
|
||||
<el-option label="缺货" value="OUT"/>
|
||||
<el-option label="低库存" value="LOW"/>
|
||||
<el-option label="近效期" value="EXPIRING"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="供应商"><el-input v-model="formData.supplierName" placeholder="请输入供应商"/></el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="showAdd = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitForm">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref,onMounted} from 'vue'
|
||||
import {ref, reactive, onMounted} from 'vue'
|
||||
import {ElMessage} from 'element-plus'
|
||||
import {getAlertPage,addAlert,getAlertStats} from './api'
|
||||
const alertData=ref([]),stats=ref({})
|
||||
const showAdd=ref(false)
|
||||
const loadData=async()=>{const [a,s]=await Promise.all([getAlertPage({pageNo:1,pageSize:50}),getAlertStats()]);alertData.value=a.data?.records||[];stats.value=s.data||{}}
|
||||
onMounted(()=>loadData())
|
||||
import {getAlertPage, addAlert, getAlertStats} from './api'
|
||||
|
||||
const loading = ref(false)
|
||||
const alertData = ref([])
|
||||
const total = ref(0)
|
||||
const showAdd = ref(false)
|
||||
|
||||
const statCards = ref([
|
||||
{label:'总预警数', value:0, color:'#409eff'},
|
||||
{label:'缺货', value:0, color:'#f56c6c'},
|
||||
{label:'低库存', value:0, color:'#e6a23c'},
|
||||
{label:'近效期', value:0, color:'#909399'},
|
||||
{label:'供应商数', value:0, color:'#67c23a'},
|
||||
{label:'待补货', value:0, color:'#409eff'}
|
||||
])
|
||||
|
||||
const q = ref({pageNo:1, pageSize:20, alertLevel:'', drugCategory:''})
|
||||
const formData = reactive({
|
||||
drugCode:'', drugName:'', drugSpec:'', currentStock:0, minStock:0,
|
||||
maxStock:100, alertLevel:'LOW', supplierName:''
|
||||
})
|
||||
|
||||
async function loadData() {
|
||||
loading.value = true
|
||||
try {
|
||||
const [a, s] = await Promise.all([getAlertPage(q.value), getAlertStats()])
|
||||
alertData.value = a.data?.records || []
|
||||
total.value = a.data?.total || 0
|
||||
// Stats
|
||||
let out = 0, low = 0, expiring = 0, supplierSet = new Set()
|
||||
alertData.value.forEach(row => {
|
||||
if (row.alertLevel === 'OUT') out++
|
||||
else if (row.alertLevel === 'LOW') low++
|
||||
else if (row.alertLevel === 'EXPIRING') expiring++
|
||||
supplierSet.add(row.supplierName)
|
||||
})
|
||||
statCards.value[0].value = total.value
|
||||
statCards.value[1].value = out
|
||||
statCards.value[2].value = low
|
||||
statCards.value[3].value = expiring
|
||||
statCards.value[4].value = supplierSet.size
|
||||
statCards.value[5].value = s.data?.pendingOrders || 0
|
||||
} finally { loading.value = false }
|
||||
}
|
||||
|
||||
function resetQuery() {
|
||||
q.value = {pageNo:1, pageSize:20, alertLevel:'', drugCategory:''}
|
||||
loadData()
|
||||
}
|
||||
|
||||
async function submitForm() {
|
||||
await addAlert(formData)
|
||||
ElMessage.success('新增成功')
|
||||
showAdd.value = false
|
||||
loadData()
|
||||
}
|
||||
|
||||
function handleOrder(row) {
|
||||
ElMessage.info('补货申请: ' + row.drugName)
|
||||
}
|
||||
|
||||
function exportReport() { ElMessage.info('导出功能开发中') }
|
||||
|
||||
onMounted(() => loadData())
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user