Files
his/backup/vxetable-migration-20260602/medicationmanagement/statisticalManagement/index.vue
华佗 1d21661a78 feat: Spring Boot 3.5.14 全量升级 + 组件升级
核心升级:
- Spring Boot 2.7.18 → 3.5.14
- MyBatis Plus 3.5.5 → 3.5.16 (spring-boot3-starter)
- Springdoc 1.8.0 → 2.8.6 (OpenAPI 3)
- Flowable 6.8.0 → 7.1.0
- Druid 1.2.x → 1.2.28 (boot3-starter)
- kotlin-reflect 1.9.10 → 1.9.25

迁移适配:
- javax → jakarta 命名空间 (620+ 文件)
- Swagger 注解迁移到 OpenAPI 3 (@Tag/@Schema/@Operation/@Parameter)
- Spring Security 6.2 适配 (antMatchers→requestMatchers, EnableMethodSecurity)
- Druid 包名迁移 (boot→boot3)
- Redis 配置路径迁移 (spring.redis→spring.data.redis)
- Flyway 适配 (flyway-database-postgresql)
- Flowable 7.x 适配 (MULE_TASK_IMAGE 移除)

修复:
- spring-boot-maven-plugin 2.5.15→3.5.14 (SPI服务发现失效)
- mybatis-plus-boot-starter 3.5.5→3.5.16 (kotlin-reflect+fastjson2冲突)
- Flowable database-schema-update 启用自动建表

验证: 23/23 测试通过, 1374 API端点正常
2026-06-04 22:39:49 +08:00

876 lines
24 KiB
Vue
Executable File
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="app-container">
<el-form
ref="queryRef"
:model="queryParams"
:inline="true"
label-width="90px"
>
<el-form-item
label="药品名称"
prop="searchKey"
>
<el-input
v-model="queryParams.searchKey"
placeholder="药品编码/药品名称/名称拼音"
clearable
style="width: 260px"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item
label="剩余过期天数"
prop="remainingDays"
label-width="110px"
>
<el-input
v-model="queryParams.remainingDays"
placeholder="查询 ≤ X 天的记录"
clearable
style="width: 200px"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item
label="药品类型"
prop="medCategoryCodes"
>
<el-select
v-model="queryParams.medCategoryCodes"
placeholder="请选择药品类别"
clearable
multiple
filterable
style="width: 260px"
:collapse-tags="true"
:max-collapse-tags="2"
@change="handleChangeMedCategoryCode"
>
<el-option
v-for="dict in medCategoryCodeOptions"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item
label="耗材类型"
prop="devCategoryCodes"
>
<el-select
v-model="queryParams.devCategoryCodes"
placeholder="请选择耗材类别"
clearable
multiple
filterable
style="width: 260px"
:collapse-tags="true"
:max-collapse-tags="2"
@change="handleChangeDeviceCategoryCode"
>
<el-option
v-for="dict in deviceCategoryCodeOptions"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item
label="停供状态"
prop="inventoryStatusEnum"
>
<el-select
v-model="queryParams.inventoryStatusEnum"
placeholder="请选择停供状态"
clearable
style="width: 150px"
@change="handleQuery"
>
<el-option
label="未停供"
:value="2"
/>
<el-option
label="已停供"
:value="3"
/>
</el-select>
</el-form-item>
<el-form-item
label="医保等级"
prop="chrgitmLv"
>
<el-select
v-model="queryParams.chrgitmLv"
placeholder="请选择医保等级"
clearable
style="width: 150px"
@change="handleQuery"
>
<el-option
v-for="chrgitmLv_enumText in chrgitmLv_enumTextOptions"
:key="chrgitmLv_enumText.value"
:label="chrgitmLv_enumText.info"
:value="chrgitmLv_enumText.value"
/>
</el-select>
</el-form-item>
<el-form-item
label="库存范围"
prop="zeroFlag"
>
<el-select
v-model="queryParams.zeroFlag"
placeholder="请选择库存范围"
clearable
style="width: 150px"
@change="handleQuery"
>
<el-option
v-for="supplyStatus in inventory_range"
:key="supplyStatus.value"
:label="supplyStatus.label"
:value="supplyStatus.value"
/>
</el-select>
</el-form-item>
<el-form-item
label="仓库药房"
prop="locationId"
>
<el-select
v-model="locationId"
placeholder="请选择仓库药房"
multiple
clearable
style="width: 260px"
@change="handleQuery"
>
<el-option
v-for="supplyStatus in locationIdList"
:key="supplyStatus.value"
:label="supplyStatus.name"
:value="supplyStatus.id"
/>
</el-select>
</el-form-item>
<el-form-item
label="供应商"
prop="supplierId"
>
<el-select
v-model="supplierId"
placeholder="请选择供应商"
multiple
clearable
style="width: 260px"
@change="handleQuery"
>
<el-option
v-for="supplyStatus in supplierList"
:key="supplyStatus.value"
:label="supplyStatus.name"
:value="supplyStatus.id"
/>
</el-select>
</el-form-item>
<el-form-item
label="备份日期"
prop="backUpDate"
>
<el-date-picker
v-model="queryParams.backUpDate"
type="date"
placeholder="请选择备份日期"
clearable
value-format="YYYY-MM-DD"
style="width: 150px"
/>
</el-form-item>
</el-form>
<el-row
:gutter="10"
class="mb8"
>
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="Search"
@click="handleQuery"
>
查询
</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="CircleClose"
@click="handleClear"
>
重置
</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="Download"
@click="handleExport"
>
导出Excel
</el-button>
</el-col>
</el-row>
<el-table
v-loading="loading"
:data="purchaseinventoryList"
max-height="600"
@selection-change="handleSelectionChange"
>
<el-table-column
label="序号"
type="index"
width="50"
align="center"
/>
<el-table-column
key="busNo"
label="项目编码"
align="center"
prop="busNo"
width="130"
:show-overflow-tooltip="true"
/>
<el-table-column
key="itemName"
label="项目名称"
align="center"
prop="itemName"
width="160"
:show-overflow-tooltip="true"
/>
<el-table-column
key="medCategoryCode_dictText"
label="项目类别"
align="center"
prop="medCategoryCode_dictText"
width="100"
:show-overflow-tooltip="true"
>
<template #default="scope">
<span>
{{
scope.row.itemTable == 'adm_device_definition'
? scope.row.devCategoryCode_dictText
: scope.row.medCategoryCode_dictText
}}
</span>
</template>
</el-table-column>
<el-table-column
key="locationName"
label="仓库"
align="center"
prop="locationName"
:show-overflow-tooltip="true"
/>
<el-table-column
key="totalVolume"
label="规格"
align="center"
prop="totalVolume"
width="150"
:show-overflow-tooltip="true"
/>
<el-table-column
key="manufacturerText"
label="厂家/产地"
align="center"
prop="manufacturerText"
width="180px"
:show-overflow-tooltip="true"
/>
<el-table-column
key="lotNumber"
label="生产批号"
align="center"
prop="lotNumber"
width="120px"
:show-overflow-tooltip="true"
/>
<el-table-column
key="quantityUnit"
label="库存(包装单位)"
align="right"
header-align="center"
prop="quantityUnit"
width="130px"
:show-overflow-tooltip="true"
>
<template #default="scope">
<span>{{ formatQuantity(scope.row) }}</span>
</template>
</el-table-column>
<el-table-column
key="quantity"
label="库存(最小单位)"
align="right"
header-align="center"
prop="quantity"
width="130px"
:show-overflow-tooltip="true"
>
<template #default="scope">
<span>{{ scope.row.quantity + ' ' + scope.row.minUnitCode_dictText }}</span>
</template>
</el-table-column>
<el-table-column
key="partPercent"
label="拆零比"
align="center"
prop="partPercent"
:show-overflow-tooltip="true"
/>
<el-table-column
key="purchasePrice"
label="采购价格"
align="right"
header-align="center"
prop="purchasePrice"
width="100"
:show-overflow-tooltip="true"
>
<template #default="scope">
<span>{{ Number(scope.row.purchasePrice || 0).toFixed(3) + ' 元' }}</span>
</template>
</el-table-column>
<el-table-column
key="salePrice"
label="零售价"
align="right"
header-align="center"
prop="salePrice"
width="100"
:show-overflow-tooltip="true"
>
<template #default="scope">
<span>{{ Number(scope.row.salePrice || 0).toFixed(3) + ' 元' }}</span>
</template>
</el-table-column>
<el-table-column
key="totalPurchasePrice"
label="进价总额"
align="right"
header-align="center"
prop="totalPurchasePrice"
width="100"
:show-overflow-tooltip="true"
>
<template #default="scope">
<span>{{ Number(scope.row.totalPurchasePrice || 0).toFixed(3) + ' 元' }}</span>
</template>
</el-table-column>
<el-table-column
key="totalSalePrice"
label="零售价总额"
align="right"
header-align="center"
prop="totalSalePrice"
width="100"
:show-overflow-tooltip="true"
>
<template #default="scope">
<span>{{ Number(scope.row.totalSalePrice || 0).toFixed(3) + ' 元' }}</span>
</template>
</el-table-column>
<el-table-column
key="doseFormCode_dictText"
label="剂型"
align="center"
prop="doseFormCode_dictText"
:show-overflow-tooltip="true"
>
<template #default="scope">
<span>{{ scope.row.doseFormCode_dictText || '-' }}</span>
</template>
</el-table-column>
<el-table-column
key="approvalNumber"
label="国药准字"
align="center"
prop="approvalNumber"
width="165"
:show-overflow-tooltip="true"
>
<template #default="scope">
<span>{{ scope.row.approvalNumber || '-' }}</span>
</template>
</el-table-column>
<el-table-column
key="supplierName"
label="供应商"
align="center"
prop="supplierName"
width="170"
:show-overflow-tooltip="true"
/>
<el-table-column
key="chrgitmLv_enumText"
label="医保等级"
align="center"
prop="chrgitmLv_enumText"
/>
<el-table-column
key="productionDate"
label="生产日期"
align="center"
prop="productionDate"
width="120"
:show-overflow-tooltip="true"
>
<template #default="scope">
<span>{{ formatDateStr(scope.row.productionDate, 'YYYY-MM-DD') }}</span>
</template>
</el-table-column>
<el-table-column
key="expirationDate"
label="有效期至"
align="center"
prop="expirationDate"
width="120"
:show-overflow-tooltip="true"
>
<template #default="scope">
<span>{{ formatDateStr(scope.row.expirationDate, 'YYYY-MM-DD') }}</span>
</template>
</el-table-column>
<el-table-column
key="remainingDays"
label="剩余过期天数"
align="center"
prop="remainingDays"
width="110px"
:show-overflow-tooltip="true"
/>
<el-table-column
key="locationStoreName"
label="货位"
align="center"
prop="locationStoreName"
:show-overflow-tooltip="true"
/>
<el-table-column
key="inventoryStatusEnum_enumText"
label="停供状态"
align="center"
prop="inventoryStatusEnum_enumText"
:show-overflow-tooltip="true"
/>
<el-table-column
label="操作"
align="center"
width="180"
fixed="right"
class-name="small-padding fixed-width"
>
<!-- 停供/停用3取消停供/启用2 -->
<template #default="scope">
<el-button
v-if="scope.row.inventoryStatusEnum == 2"
link
type="primary"
icon="Edit"
@click="handlestopcancelSupply(scope.row)"
>
停供
</el-button>
<el-button
v-if="scope.row.inventoryStatusEnum == 3"
link
type="primary"
icon="Edit"
@click="handlestopcancelSupply(scope.row)"
>
取消停供
</el-button>
<el-button
link
type="primary"
icon="Edit"
@click="handleOpenPage(scope.row)"
>
追溯码
</el-button>
</template>
</el-table-column>
</el-table>
<div style="float: right; margin-top: 10px; margin-right: 10px">
<span class="descriptions-item-label"> 进价合计金额{{ salePriceTotal.toFixed(4) }} </span>
<span class="descriptions-item-label"> 零售价合计金额{{ priceTotal.toFixed(4) }} </span>
</div>
<pagination
v-show="total > 0"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
:total="total"
:style="'margin-top: 35px;'"
@pagination="paginationList"
/>
<el-dialog
v-model="visible"
title="药品追溯码"
width="1400"
append-to-body
destroy-on-close
:draggable="true"
>
<traceabilityCodeManagement :record-info="recordInfo" />
</el-dialog>
</div>
</template>
<script setup name="StatisticalManagement">
import {
cancelSupply,
getInit,
getPharmacyCabinetList,
getproductReturnBackUpPage,
getproductReturnPage,
productBackupPageTotal,
productPageTotal,
stopSupply,
} from './statisticalManagent';
import traceabilityCodeManagement from './traceabilityCodeManagement.vue';
import {formatDateStr} from '@/utils/index';
import {getSupplierList} from '@/api/public';
import {useStore} from '@/store/store';
import {computed, watch} from 'vue';
const store = useStore();
const { proxy } = getCurrentInstance();
const { item_category_code, inventory_range, device_category_code, med_category_code } =
proxy.useDict(
'item_category_code',
'inventory_range',
'med_category_code',
'device_category_code'
);
const medCategoryCodeOptions = computed(() => {
return [{ value: 'all', label: '全部' }, ...med_category_code.value];
});
const deviceCategoryCodeOptions = computed(() => {
return [{ value: 'all', label: '全部' }, ...device_category_code.value];
});
const purchaseinventoryList = ref([]);
const visible = ref(false);
const recordInfo = ref({});
const route = useRoute();
const loading = ref(true);
const ids = ref([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const salePriceTotal = ref(0);
const priceTotal = ref(0);
const chrgitmLv_enumTextOptions = ref([]);
const locationIdList = ref([]);
const supplierList = ref([]);
const locationId = ref([]);
const supplierId = ref([]);
const data = reactive({
form: {},
queryParams: {
pageNo: 1,
pageSize: 10,
searchKey: undefined,
remainingDays: undefined,
categoryCode: [],
chrgitmLv: undefined,
zeroFlag: undefined,
locationId: undefined,
medCategoryCodes: [],
devCategoryCodes: [],
backUpDate: undefined,
inventoryStatusEnum: undefined,
},
rules: {},
});
const { queryParams, form, rules } = toRefs(data);
watch(
() => store.remainingDays,
(newValue) => {
if (newValue) {
queryParams.value.remainingDays = newValue;
handleQuery(); // 触发查询
store.clearRemainingDays(); // 清除值
}
},
{ immediate: true } // 修正拼写错误
);
watch(
() => queryParams.value.backUpDate,
(newValue, oldValue) => {
// 只在值真正变化时触发,避免初始化时触发
if (newValue !== oldValue) {
if (newValue) {
getBackUpList();
} else if (oldValue !== undefined) {
// 只有当之前有值,现在清空时才调用 getList
getList();
}
}
}
);
const paginationList = computed(() => {
if (queryParams.value.backUpDate) {
return getBackUpList();
}
return getList();
});
function handleChangeMedCategoryCode(value) {
// 如果选择的value等于"全部"all则全部选中
if (value && value.includes('all')) {
// 获取所有选项的值(排除"全部"本身,因为"全部"只是触发全选的选项)
const allValues = medCategoryCodeOptions.value
.filter((item) => item.value !== 'all')
.map((item) => item.value);
// 选中所有实际选项(不包含"全部"
queryParams.value.medCategoryCodes = allValues;
} else {
// 如果选择了其他选项,保持当前选择
queryParams.value.medCategoryCodes = value || [];
}
}
function handleChangeDeviceCategoryCode(value) {
// 如果选择的value等于"全部"all则全部选中
if (value && value.includes('all')) {
// 获取所有选项的值(排除"全部"本身,因为"全部"只是触发全选的选项)
const allValues = deviceCategoryCodeOptions.value
.filter((item) => item.value !== 'all')
.map((item) => item.value);
// 选中所有实际选项(不包含"全部"
queryParams.value.devCategoryCodes = allValues;
} else {
// 如果选择了其他选项,保持当前选择
queryParams.value.devCategoryCodes = value || [];
}
}
function getPharmacyCabinetLists() {
getPharmacyCabinetList().then((response) => {
locationIdList.value = response.data;
});
getSupplierList().then((response) => {
supplierList.value = response.data;
});
getInit().then((response) => {
chrgitmLv_enumTextOptions.value = response.data.chrgitmLvOptions;
});
}
/** 查询备份日期库存明细列表 */
function getBackUpList() {
loading.value = true;
salePriceTotal.value = 0;
priceTotal.value = 0;
const params = {
...queryParams.value,
locationIds: locationId.value.join(','),
supplierIds: supplierId.value.join(','),
medCategoryCodes:
queryParams.value.medCategoryCodes?.length > 0
? queryParams.value.medCategoryCodes.join(',')
: undefined,
devCategoryCodes:
queryParams.value.devCategoryCodes?.length > 0
? queryParams.value.devCategoryCodes.join(',')
: undefined,
backUpDateSTime: queryParams.value.backUpDate + ' 00:00:00' || undefined,
backUpDateETime: queryParams.value.backUpDate + ' 23:59:59' || undefined,
backUpDate: undefined,
};
getproductReturnBackUpPage(params).then((res) => {
loading.value = false;
purchaseinventoryList.value = res.data.records;
if (purchaseinventoryList.value && purchaseinventoryList.value.length > 0) {
purchaseinventoryList.value.map((k, index) => {
k.inventoryStatusEnum_enumText =
k.inventoryStatusEnum == 2 ? '未停供' : k.inventoryStatusEnum == 3 ? '已停供' : '';
k.quantityUnit = k.quantity / k.partPercent;
});
}
total.value = res.data.total;
});
productBackupPageTotal(params).then((res) => {
salePriceTotal.value = res.data.purchasePriceStatistics;
priceTotal.value = res.data.salePriceStatistics;
});
}
/** 查询调拨管理项目列表 */
function getList() {
loading.value = true;
salePriceTotal.value = 0;
priceTotal.value = 0;
const params = {
...queryParams.value,
locationIds: locationId.value.join(','),
supplierIds: supplierId.value.join(','),
medCategoryCodes:
queryParams.value.medCategoryCodes?.length > 0
? queryParams.value.medCategoryCodes.join(',')
: undefined,
devCategoryCodes:
queryParams.value.devCategoryCodes?.length > 0
? queryParams.value.devCategoryCodes.join(',')
: undefined,
};
getproductReturnPage(params).then((res) => {
loading.value = false;
purchaseinventoryList.value = res.data.records;
if (purchaseinventoryList.value && purchaseinventoryList.value.length > 0) {
purchaseinventoryList.value.map((k, index) => {
k.inventoryStatusEnum_enumText =
k.inventoryStatusEnum == 2 ? '未停供' : k.inventoryStatusEnum == 3 ? '已停供' : '';
k.quantityUnit = k.quantity / k.partPercent;
});
}
total.value = res.data.total;
});
productPageTotal(params).then((res) => {
// 使用 Number() 强制转换,解决字符串问题
// 使用 || 0 解决 null/undefined 问题
const purchasePrice = res.data?.purchasePriceStatistics;
const salePrice = res.data?.salePriceStatistics;
salePriceTotal.value = Number(purchasePrice || 0);
priceTotal.value = Number(salePrice || 0);
});
}
/** 搜索按钮操作 */
function handleQuery() {
// queryParams.value.occurrenceTimeSTime =
// occurrenceTime.value && occurrenceTime.value.length == 2
// ? occurrenceTime.value[0] + " 00:00:00"
// : "";
// queryParams.value.occurrenceTimeETime =
// occurrenceTime.value && occurrenceTime.value.length == 2
// ? occurrenceTime.value[1] + " 23:59:59"
// : "";
queryParams.value.pageNo = 1;
if (queryParams.value.backUpDate) {
getBackUpList();
} else {
getList();
}
}
function formatQuantity(row) {
if (row.remainder > 0) {
return (
row.number + ' ' + row.unitCode_dictText + row.remainder + ' ' + row.minUnitCode_dictText
);
} else {
return row.number + ' ' + row.unitCode_dictText;
}
}
// 导出
function handleExport() {
const exportParams = {
...queryParams.value,
locationIds: locationId.value.join(','),
supplierIds: supplierId.value.join(','),
medCategoryCodes:
queryParams.value.medCategoryCodes?.length > 0
? queryParams.value.medCategoryCodes.join(',')
: undefined,
devCategoryCodes:
queryParams.value.devCategoryCodes?.length > 0
? queryParams.value.devCategoryCodes.join(',')
: undefined,
};
proxy.downloadGet(
'/inventory-manage/product/excel-out',
exportParams,
`库存明细记录_${proxy.formatDateStr(new Date(), 'YYYY-MM-DD')}.xlsx`
);
}
/** 清空条件按钮操作 */
function handleClear() {
// 清空查询条件
proxy.resetForm('queryRef');
// 清空不在 queryParams 中的字段
locationId.value = [];
supplierId.value = [];
getList();
}
function handleOpenPage(row) {
recordInfo.value = row;
visible.value = true;
}
/** 选择条数 */
function handleSelectionChange(selection) {
ids.value = selection.map((item) => item.id);
single.value = selection.length != 1;
multiple.value = !selection.length;
}
/** 备份 */
function openAddaddTransferProductDialog() {
// getTransferProductTypeList();
// nextTick(() => {
// proxy.$refs["transferProductRef"].show();
// });
// const partItem = {partFlg: 'add',rowData: []}
// item: JSON.stringify(partItem)
// ,query:{item: JSON.stringify(partItem)}
// router.push({ path: '/medicationmanagement/transferManagement/transferManagent'})
}
// 取消停供 停供
function handlestopcancelSupply(row) {
if (row.inventoryStatusEnum == 2) {
stopSupply(row.inventoryId).then((response) => {
proxy.$modal.msgSuccess('停供成功');
open.value = false;
getList();
});
}
if (row.inventoryStatusEnum == 3) {
cancelSupply(row.inventoryId).then((response) => {
proxy.$modal.msgSuccess('取消停供成功');
open.value = false;
getList();
});
}
}
// 停供
function handlestopSupply(row) {}
// getTransferProductTypeList();
getList();
getPharmacyCabinetLists();
</script>
<style scoped>
.custom-tree-node {
display: flex;
align-items: center;
}
</style>