核心升级: - 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端点正常
510 lines
14 KiB
Vue
Executable File
510 lines
14 KiB
Vue
Executable File
<template>
|
|
<div class="app-container">
|
|
<el-form
|
|
ref="queryForm"
|
|
:model="queryParams"
|
|
label-width="120px"
|
|
:inline="true"
|
|
>
|
|
<el-form-item
|
|
label="药品名称/编码/拼音/五笔码"
|
|
prop="searchKey"
|
|
label-width="200px"
|
|
>
|
|
<el-input
|
|
v-model="queryParams.searchKey"
|
|
placeholder="请输入"
|
|
clearable
|
|
style="width: 350px"
|
|
@keyup.enter="handleQuery"
|
|
/>
|
|
</el-form-item>
|
|
<el-form-item
|
|
label="期初备份单据号"
|
|
prop="startBusNo"
|
|
>
|
|
<el-select
|
|
v-model="queryParams.startBusNo"
|
|
filterable
|
|
clearable
|
|
placeholder="请选择"
|
|
@change="handleQuery"
|
|
>
|
|
<el-option
|
|
v-for="(item, index) in backUpOptions"
|
|
:key="index"
|
|
:label="item"
|
|
:value="item"
|
|
/>
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item
|
|
label="期末备份单据号"
|
|
prop="endBusNo"
|
|
>
|
|
<el-select
|
|
v-model="queryParams.endBusNo"
|
|
filterable
|
|
clearable
|
|
placeholder="请选择"
|
|
@change="handleQuery"
|
|
>
|
|
<el-option
|
|
v-for="(item, index) in backUpOptions"
|
|
:key="index"
|
|
:label="item"
|
|
:value="item"
|
|
/>
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item
|
|
label="库房"
|
|
prop="locationId"
|
|
>
|
|
<el-select
|
|
v-model="queryParams.locationId"
|
|
placeholder="请选择库房"
|
|
filterable
|
|
clearable
|
|
@change="handleQuery"
|
|
>
|
|
<el-option
|
|
v-for="item in locationOptions"
|
|
:key="item.id"
|
|
:label="item.name"
|
|
:value="item.id"
|
|
/>
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item
|
|
label="是否发生过业务"
|
|
prop="isBus"
|
|
>
|
|
<el-select
|
|
v-model="queryParams.isBus"
|
|
filterable
|
|
clearable
|
|
@change="handleQuery"
|
|
>
|
|
<el-option
|
|
v-for="item in [
|
|
{ id: 1, name: '已发生业务' },
|
|
{ id: 0, name: '未发生业务' },
|
|
]"
|
|
:key="item.id"
|
|
:label="item.name"
|
|
:value="item.id"
|
|
/>
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item
|
|
label="是否账平"
|
|
prop="isBalance"
|
|
>
|
|
<el-select
|
|
v-model="queryParams.isBalance"
|
|
filterable
|
|
clearable
|
|
@change="handleQuery"
|
|
>
|
|
<el-option
|
|
v-for="item in [
|
|
{ id: 1, name: '已平账' },
|
|
{ id: 0, name: '未平账' },
|
|
]"
|
|
:key="item.id"
|
|
:label="item.name"
|
|
:value="item.id"
|
|
/>
|
|
</el-select>
|
|
</el-form-item>
|
|
<el-form-item>
|
|
<el-button
|
|
type="primary"
|
|
@click="handleQuery"
|
|
>
|
|
查询
|
|
</el-button>
|
|
<el-button @click="resetQuery">
|
|
重置
|
|
</el-button>
|
|
</el-form-item>
|
|
</el-form>
|
|
<el-table
|
|
v-loading="loading"
|
|
:data="tableData"
|
|
border
|
|
stripe
|
|
element-loading-text="数据加载中..."
|
|
:header-cell-style="{ backgroundColor: '#f5f7fa', color: '#303133' }"
|
|
empty-text="暂无数据"
|
|
>
|
|
<el-table-column
|
|
type="expand"
|
|
width="48"
|
|
>
|
|
<template #default="{ row }">
|
|
<el-row
|
|
:gutter="10"
|
|
style="width: 100%"
|
|
>
|
|
<el-form
|
|
label-width="100px"
|
|
:inline="true"
|
|
>
|
|
<el-form-item
|
|
label="发药单据数"
|
|
:class="computedClass(row)"
|
|
>
|
|
{{ row.dispenseOrderCount }}
|
|
</el-form-item>
|
|
<el-form-item
|
|
label="退药单据数"
|
|
:class="computedClass(row)"
|
|
>
|
|
{{ row.returnOrderCount }}
|
|
</el-form-item>
|
|
<el-form-item
|
|
label="采购单据数"
|
|
:class="computedClass(row)"
|
|
>
|
|
{{ row.purchaseOrderCount }}
|
|
</el-form-item>
|
|
<el-form-item
|
|
label="退库单据数"
|
|
:class="computedClass(row)"
|
|
>
|
|
{{ row.purchaseReturnOrderCount }}
|
|
</el-form-item>
|
|
<el-form-item
|
|
label="调拨进单据数"
|
|
:class="computedClass(row)"
|
|
>
|
|
{{ row.transferInOrderCount }}
|
|
</el-form-item>
|
|
<el-form-item
|
|
label="调拨出单据数"
|
|
:class="computedClass(row)"
|
|
>
|
|
{{ row.transferOutOrderCount }}
|
|
</el-form-item>
|
|
<el-form-item
|
|
label="报损单据数"
|
|
:class="computedClass(row)"
|
|
>
|
|
{{ row.lossOrderCount }}
|
|
</el-form-item>
|
|
<el-form-item
|
|
label="盘点单据数"
|
|
:class="computedClass(row)"
|
|
>
|
|
{{ row.stocktakeOrderCount }}
|
|
</el-form-item>
|
|
<el-form-item
|
|
label="领用单据数"
|
|
:class="computedClass(row)"
|
|
>
|
|
{{ row.issueOrderCount }}
|
|
</el-form-item>
|
|
<el-form-item
|
|
label="退领单据数"
|
|
:class="computedClass(row)"
|
|
>
|
|
{{ row.returnIssueOrderCount }}
|
|
</el-form-item>
|
|
</el-form>
|
|
</el-row>
|
|
<el-row
|
|
:gutter="40"
|
|
style="width: 100%"
|
|
>
|
|
<el-col
|
|
:span="16"
|
|
class="formula-wrapper"
|
|
>
|
|
<div class="formula">
|
|
<template
|
|
v-for="segment in balanceSegments"
|
|
:key="segment.label"
|
|
>
|
|
<span
|
|
v-if="segment.operator"
|
|
class="formula-operator"
|
|
>
|
|
{{ segment.operator }}
|
|
</span>
|
|
<span class="formula-label">{{ segment.label }}</span>
|
|
<span>(</span>
|
|
<span :class="computedClass(row)">
|
|
{{ formatInventoryUnit(row[segment.field], row) }}
|
|
</span>
|
|
<span>)</span>
|
|
</template>
|
|
<span class="formula-operator">=</span>
|
|
<span class="formula-label">推算期末数</span>
|
|
<span>(</span>
|
|
<span :class="computedClass(row)">{{ computedEndNum(row) }}</span>
|
|
<span>)</span>
|
|
</div>
|
|
</el-col>
|
|
<el-col
|
|
:span="4"
|
|
class="formula-wrapper"
|
|
>
|
|
<span>期末数量</span>
|
|
<span>(</span>
|
|
<span :class="computedClass(row)">
|
|
{{ formatInventoryUnit(row.endCount, row) }}
|
|
</span>
|
|
<span>)</span>
|
|
<span class="formula-operator">-</span>
|
|
<span>推测期末数量</span>
|
|
<span>(</span>
|
|
<span :class="computedClass(row)">{{ computedEndNum(row) }}</span>
|
|
<span>)</span>
|
|
<span class="formula-operator">=</span>
|
|
<span>盈亏数量</span>
|
|
<span>(</span>
|
|
<span :class="computedClass(row)">
|
|
{{ formatInventoryUnit(row.profitLossDiff, row) }}</span>
|
|
<span>)</span>
|
|
</el-col>
|
|
</el-row>
|
|
</template>
|
|
</el-table-column>
|
|
<el-table-column
|
|
prop="itemName"
|
|
label="药品名称"
|
|
align="center"
|
|
width="200"
|
|
/>
|
|
<el-table-column
|
|
prop="itemNo"
|
|
label="项目编号"
|
|
align="center"
|
|
/>
|
|
<el-table-column
|
|
prop="unitCode_dictText"
|
|
label="单位"
|
|
align="center"
|
|
/>
|
|
<el-table-column
|
|
prop="minUnitCode_dictText"
|
|
label="最小单位"
|
|
align="center"
|
|
/>
|
|
<el-table-column
|
|
prop="totalVolume"
|
|
label="规格"
|
|
align="center"
|
|
/>
|
|
<el-table-column
|
|
prop="categoryCode_dictText"
|
|
label="药品分类"
|
|
align="center"
|
|
/>
|
|
<el-table-column
|
|
prop="manufacturerText"
|
|
label="生产厂家"
|
|
align="center"
|
|
width="300"
|
|
/>
|
|
<el-table-column
|
|
prop="partPercent"
|
|
label="拆零比"
|
|
align="center"
|
|
/>
|
|
<el-table-column
|
|
prop="isBalance"
|
|
label="是否账平"
|
|
align="center"
|
|
width="100"
|
|
>
|
|
<template #default="scope">
|
|
<el-tag
|
|
v-if="scope.row.isBalance == 1"
|
|
type="success"
|
|
>
|
|
已平账
|
|
</el-tag>
|
|
<el-tag
|
|
v-else-if="scope.row.isBalance == 0"
|
|
type="danger"
|
|
>
|
|
未平账
|
|
</el-tag>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
<pagination
|
|
v-show="total > 0"
|
|
v-model:page="queryParams.pageNo"
|
|
v-model:limit="queryParams.pageSize"
|
|
:total="total"
|
|
@pagination="getList"
|
|
/>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup name="storeReconciliation">
|
|
import {onMounted, reactive, ref, watch} from 'vue';
|
|
import {ElMessage} from 'element-plus';
|
|
import {getBackupList, getPharmacyCabinetList, getStoreReconciliationList,} from './components/api.js';
|
|
import {formatInventory} from '@/utils/his.js';
|
|
|
|
const loading = ref(false);
|
|
const total = ref(0);
|
|
const queryParams = reactive({
|
|
pageNo: 1,
|
|
pageSize: 10,
|
|
searchKey: '',
|
|
startBusNo: '',
|
|
endBusNo: '',
|
|
locationId: '',
|
|
isBus: 1,
|
|
isBalance: 0,
|
|
});
|
|
// 获取库存单位
|
|
function formatInventoryUnit(num, row) {
|
|
return formatInventory(num, row.partPercent, row.unitCode_dictText, row.minUnitCode_dictText);
|
|
}
|
|
// 推算期末数
|
|
function computedEndNum(row) {
|
|
if (!row) return '';
|
|
|
|
const {
|
|
startCount = 0,
|
|
dispenseCount = 0,
|
|
returnCount = 0,
|
|
purchaseCount = 0,
|
|
purchaseReturnCount = 0,
|
|
transferInCount = 0,
|
|
transferOutCount = 0,
|
|
lossCount = 0,
|
|
stocktakeCount = 0,
|
|
issueCount = 0,
|
|
returnIssueCount = 0,
|
|
} = row;
|
|
|
|
const endCount =
|
|
Number(startCount || 0) -
|
|
Number(dispenseCount || 0) +
|
|
Number(returnCount || 0) +
|
|
Number(purchaseCount || 0) -
|
|
Number(purchaseReturnCount || 0) +
|
|
Number(transferInCount || 0) -
|
|
Number(transferOutCount || 0) -
|
|
Number(lossCount || 0) +
|
|
Number(stocktakeCount || 0) -
|
|
Number(issueCount || 0) +
|
|
Number(returnIssueCount || 0);
|
|
|
|
return formatInventoryUnit(endCount, row);
|
|
}
|
|
function computedClass(row) {
|
|
if (row.isBalance == 1) {
|
|
return 'label-green';
|
|
}
|
|
if (row.isBalance == 0) {
|
|
return 'label-red';
|
|
}
|
|
return '';
|
|
}
|
|
// function
|
|
const handleQuery = () => {
|
|
queryParams.pageNo = 1;
|
|
getList();
|
|
};
|
|
|
|
const resetQuery = () => {
|
|
queryParams.locationId = '';
|
|
queryParams.startBusNo = '';
|
|
queryParams.endBusNo = '';
|
|
queryParams.isBus = 1;
|
|
queryParams.isBalance = 0;
|
|
handleQuery();
|
|
};
|
|
|
|
const tableData = ref([]);
|
|
const getList = async () => {
|
|
const response = await getStoreReconciliationList(queryParams);
|
|
tableData.value = response?.data?.records || [];
|
|
total.value = response?.data?.total || 0;
|
|
};
|
|
const locationOptions = ref([]);
|
|
const getLocationList = async () => {
|
|
try {
|
|
const response = await getPharmacyCabinetList();
|
|
locationOptions.value = response?.data || [];
|
|
queryParams.locationId = response?.data[0].id;
|
|
} catch (error) {
|
|
ElMessage.error('获取库房列表失败,请稍后重试');
|
|
}
|
|
};
|
|
const backUpOptions = ref([]);
|
|
const getBackupOptionsList = async () => {
|
|
try {
|
|
const response = await getBackupList();
|
|
backUpOptions.value = response?.data || [];
|
|
queryParams.startBusNo = response?.data[backUpOptions.value.length - 1];
|
|
} catch (error) {
|
|
ElMessage.error('获取备份单号失败,请稍后重试');
|
|
}
|
|
};
|
|
const balanceSegments = [
|
|
{ label: '期初数', field: 'startCount', operator: null },
|
|
{ label: '发药数', field: 'dispenseCount', operator: '-' },
|
|
{ label: '退药数', field: 'returnCount', operator: '+' },
|
|
{ label: '入库数', field: 'purchaseCount', operator: '+' },
|
|
{ label: '退库数', field: 'purchaseReturnCount', operator: '-' },
|
|
{ label: '调拨进数', field: 'transferInCount', operator: '+' },
|
|
{ label: '调拨出数', field: 'transferOutCount', operator: '-' },
|
|
{ label: '报损数', field: 'lossCount', operator: '-' },
|
|
{ label: '盘点数', field: 'stocktakeCount', operator: '+' },
|
|
{ label: '领用数', field: 'issueCount', operator: '-' },
|
|
{ label: '领用退数', field: 'returnIssueCount', operator: '+' },
|
|
];
|
|
watch(
|
|
[() => queryParams.locationId, () => queryParams.startBusNo],
|
|
([newLocationId, newStartBusNo]) => {
|
|
if (newLocationId && newStartBusNo) {
|
|
getList();
|
|
}
|
|
}
|
|
);
|
|
onMounted(() => {
|
|
getBackupOptionsList();
|
|
getLocationList();
|
|
});
|
|
</script>
|
|
<style lang="scss" scoped>
|
|
.label-red {
|
|
color: #f56c6c;
|
|
}
|
|
.label-green {
|
|
color: #67c23a;
|
|
}
|
|
.formula-wrapper {
|
|
margin-top: 8px;
|
|
margin-left: 12px;
|
|
}
|
|
.formula-wrapper span {
|
|
font-size: 14px;
|
|
font-weight: bold;
|
|
}
|
|
.formula {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: center;
|
|
gap: 4px;
|
|
font-size: 14px;
|
|
line-height: 28px;
|
|
}
|
|
.formula-label {
|
|
font-weight: 500;
|
|
}
|
|
.formula-operator {
|
|
padding: 0 2px;
|
|
color: #909399;
|
|
}
|
|
</style> |