1. 修复了部分开方的错误
Some checks failed
CI / Test (ubuntu-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Lint (ubuntu-latest) (push) Has been cancelled
CI / Lint (windows-latest) (push) Has been cancelled
CI / Check (ubuntu-latest) (push) Has been cancelled
CI / Check (windows-latest) (push) Has been cancelled
CI / CI OK (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Website on push / Deploy Push Playground Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Docs Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Antd Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Element Ftp (push) Has been cancelled
Deploy Website on push / Deploy Push Naive Ftp (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
Close stale issues / stale (push) Has been cancelled

2. 新增药品的ERP抓取数量倍数
This commit is contained in:
李琦
2026-07-14 08:24:00 +08:00
parent c6fa14ee90
commit f671ea7d41
8 changed files with 231 additions and 17 deletions

View File

@@ -53,6 +53,7 @@ import CommonPrescriptionModal from '#/views/doctor/doctor-reception/components/
// 信息提示模态框
import InfoModal from '#/components/modal/InfoModal.vue';
// 药品搜索选择组件
import { DrugSearchSelect } from '#/components/drug-search-select';
import { formatPriceDisplay } from '#/utils/formatPrice';
import ChineseMedicineConfig from '#/views/doctor/doctor-reception/components/ChineseMedicineConfig.vue';
@@ -90,6 +91,11 @@ const allowInsuranceCategory = computed(
() => Number(prescriptionStore.registerStoreInfo?.allow_insurance_category ?? 0) === 1,
);
/** 仅中药/西药支持常用方模板(保健食品等不提供常用方) */
const canUseCommonPrescription = computed(() =>
[1, 2].includes(prescriptionStore.activeCategory),
);
watch(allowInsuranceCategory, (allow) => {
if (!allow) {
prescriptionStore.category = 1;
@@ -422,6 +428,10 @@ const filterOption = (input: string, option: any) => {
* @description 打开常用方弹窗,选择后自动填充药品到处方中
*/
function openCommonPrescriptionModal() {
if (!canUseCommonPrescription.value) {
message.warning('当前分类不支持选择常用方');
return;
}
CommonPrescriptionModalApi.setData({
type: prescriptionStore.activeCategory,
onSelect: handleSelectCommonPrescription,
@@ -522,7 +532,7 @@ async function handleSelectCommonPrescription(data: any, type: number) {
}
// 保存到本地存储
prescriptionStore.saveToLocalStorage();
prescriptionStore.syncToLocalStorage();
message.success('已选择常用方,药品已填充到处方中');
}
@@ -533,6 +543,10 @@ async function handleSelectCommonPrescription(data: any, type: number) {
* @description 检查是否有药品可保存,然后打开弹窗
*/
const openSaveCommonPrescriptionModal = () => {
if (!canUseCommonPrescription.value) {
message.warning('当前分类不支持保存为常用方');
return;
}
// 检查是否有药品
if (prescriptionStore.currentDrugs.length === 0) {
message.warning('请先添加药品后再保存为常用方');
@@ -576,7 +590,7 @@ function handleSimpleProductSelect(drug: any) {
prescriptionStore.currentDrugs.push(newProduct);
// 更新本地存储
prescriptionStore.saveToLocalStorage();
prescriptionStore.syncToLocalStorage();
// 提示成功
message.success(`已将${newProduct.drug_name}添加到清单中!`);
@@ -600,6 +614,11 @@ const saveAsCommonPrescription = async () => {
return;
}
if (!canUseCommonPrescription.value) {
message.warning('当前分类不支持保存为常用方');
return;
}
isSavingCommonPrescription.value = true;
try {
@@ -875,6 +894,7 @@ const cancelSaveCommonPrescription = () => {
<div class="flex gap-2">
<!-- 选择常用方按钮 -->
<Button
v-if="canUseCommonPrescription"
type="default"
@click="openCommonPrescriptionModal"
>
@@ -882,6 +902,7 @@ const cancelSaveCommonPrescription = () => {
</Button>
<!-- 保存为常用方按钮 -->
<Button
v-if="canUseCommonPrescription"
type="default"
@click="openSaveCommonPrescriptionModal"
>

View File

@@ -80,3 +80,11 @@ export async function updateZoneCategory(data: Record<string, any>) {
export async function updateMinAdjustPrice(data: { id: number; min_adjust_price: number | null }) {
return requestClient.post<any>(`${prefix}update-min-adjust-price`, data);
}
/**
* 更新 ERP 抓取数量倍数
* @param data
*/
export async function updateErpQtyFactor(data: { id: number; erp_qty_factor: number }) {
return requestClient.post<any>(`${prefix}update-erp-qty-factor`, data);
}

View File

@@ -0,0 +1,123 @@
<script setup lang="ts">
import { ref } from 'vue';
import { Form, InputNumber, message } from 'ant-design-vue';
import { useVbenModal } from '@vben/common-ui';
import { updateErpQtyFactor } from '../api';
defineOptions({
name: 'ErpQtyFactorModal',
});
const formRef = ref();
const loading = ref(false);
const formData = ref<{
id?: number;
drugName?: string;
currentFactor?: number;
}>({});
const erpQtyFactor = ref<number>(1);
const [ModalComponent, modalApi] = useVbenModal({
class: 'w-[500px]',
onCancel() {
modalApi.close();
},
onConfirm: async () => {
if (!formRef.value) {
return;
}
try {
await formRef.value.validate();
} catch (error) {
return;
}
if (!formData.value.id || erpQtyFactor.value === null) {
message.error('参数不完整');
return;
}
loading.value = true;
try {
await updateErpQtyFactor({
id: formData.value.id,
erp_qty_factor: erpQtyFactor.value,
});
message.success('设置成功');
modalApi.close();
const gridApi = modalApi.getData()?.gridApi;
if (gridApi) {
gridApi.query();
}
} catch (error: any) {
console.error('设置失败:', error);
message.error(error?.message || '设置失败,请重试');
} finally {
loading.value = false;
}
},
onOpenChange(isOpen: boolean) {
if (isOpen) {
const data = modalApi.getData<typeof formData.value & { gridApi?: any }>();
if (data) {
const current = data.currentFactor ?? data.erp_qty_factor;
formData.value = {
id: data.id,
drugName: data.drugName || data.drug_name,
currentFactor: current != null && current !== '' ? Number(current) : 1,
};
erpQtyFactor.value = formData.value.currentFactor ?? 1;
}
} else {
formData.value = {};
erpQtyFactor.value = 1;
}
},
});
</script>
<template>
<ModalComponent
title="设置ERP抓取数量倍数"
:loading="loading"
>
<div v-if="formData.drugName" class="erp-qty-factor-modal">
<div class="mb-4">
<p><strong>药品名称</strong>{{ formData.drugName }}</p>
<p class="mt-2">
<strong>当前倍数</strong>{{ Number(formData.currentFactor ?? 1).toFixed(2) }}
</p>
</div>
<Form ref="formRef" :model="{ erpQtyFactor }" layout="vertical">
<Form.Item
label="ERP抓取数量倍数"
name="erpQtyFactor"
:rules="[
{ required: true, message: '请输入ERP抓取数量倍数' },
{ type: 'number', min: 0.01, message: '倍数必须大于0' },
]"
>
<InputNumber
v-model:value="erpQtyFactor"
:min="0.01"
:precision="2"
:step="1"
style="width: 100%"
placeholder="请输入ERP抓取数量倍数默认1"
/>
</Form.Item>
<p class="text-gray-500 text-sm mt-2">
提示ERP拉取订单时数量 = 订单数量 × 该倍数
</p>
</Form>
</div>
</ModalComponent>
</template>
<style scoped>
.erp-qty-factor-modal {
padding: 8px 0;
}
</style>

View File

@@ -154,6 +154,22 @@ export const modalFormProps: VbenFormProps = {
label: '规格',
rules: 'required',
},
{
component: 'InputNumber',
componentProps: {
placeholder: '请输入ERP抓取数量倍数',
min: 0.01,
precision: 2,
step: 1,
class: 'w-full',
},
defaultValue: 1,
fieldName: 'erp_qty_factor',
formItemClass: 'col-span-6',
label: 'ERP抓取数量倍数',
help: 'ERP拉取订单时数量=订单数量×该倍数默认1',
rules: 'required',
},
{
component: 'VbenSelect',
componentProps: {

View File

@@ -73,6 +73,7 @@ export const gridOptions: VxeGridProps<RowType> = {
width: 130,
slots: { default: 'min_adjust_price' },
},
{ field: 'erp_qty_factor', title: 'ERP抓取数量倍数', width: 150, slots: { default: 'erp_qty_factor' } },
{ type: 'html', title: '操作', fixed: 'right', width: 280, slots: { default: 'action' } },
],
keepSource: true,

View File

@@ -16,6 +16,7 @@ import FormModalDemo from './components/modal.vue';
import ExcelUpload from './components/ExcelUpload.vue';
import CategoryModal from './components/CategoryModal.vue';
import MinPriceModal from './components/MinPriceModal.vue';
import ErpQtyFactorModal from './components/ErpQtyFactorModal.vue';
import { formOptions } from './config/search';
import { gridOptions } from './config/table';
@@ -56,6 +57,10 @@ const [MinPriceModalComp, minPriceModalApi] = useVbenModal({
connectedComponent: MinPriceModal,
});
const [ErpQtyFactorModalComp, erpQtyFactorModalApi] = useVbenModal({
connectedComponent: ErpQtyFactorModal,
});
const openCategoryModal = (row: any) => {
categoryModalApi.setData({
row,
@@ -74,6 +79,16 @@ const openMinPriceModal = (row: any) => {
minPriceModalApi.open();
};
const openErpQtyFactorModal = (row: any) => {
erpQtyFactorModalApi.setData({
id: row.id,
drugName: row.drug_name,
erp_qty_factor: row.erp_qty_factor ?? 1,
gridApi,
});
erpQtyFactorModalApi.open();
};
const showModal = (data = {}, isUpdate = false) => {
formModalApi.setData({
// 表单值
@@ -122,6 +137,7 @@ const openExcelUploadModal = () => {
<FormModal />
<CategoryModalComp />
<MinPriceModalComp />
<ErpQtyFactorModalComp />
<Grid>
<template #toolbar-buttons>
<TableAction
@@ -211,6 +227,14 @@ const openExcelUploadModal = () => {
<span v-else style="color: #999">未设置</span>
</a>
</template>
<template #erp_qty_factor="{ row }">
<a
class="text-primary cursor-pointer hover:underline"
@click="openErpQtyFactorModal(row)"
>
{{ Number(row.erp_qty_factor ?? 1).toFixed(2) }}
</a>
</template>
<template #action="{ row }">
<TableAction
:actions="[

View File

@@ -584,7 +584,7 @@ function updateProductNumber(id, number) {
<!-- 西药列表视图 (参考EditModal) -->
<Col :span="24" v-if="type === 2">
<div v-if="drugList.length === 0" class="text-center py-8 text-gray-400 bg-gray-50 rounded-lg border border-dashed border-gray-200">
<div v-if="drugList.length === 0" class="text-center py-8 text-gray-400 dark:text-gray-500 bg-gray-50 dark:bg-gray-800/50 rounded-lg border border-dashed border-gray-200 dark:border-gray-700">
暂无药品请搜索添加
</div>
<div v-else class="flex flex-col gap-2">
@@ -609,7 +609,7 @@ function updateProductNumber(id, number) {
/>
<div
v-else
class="w-12 h-12 bg-gray-100 rounded flex items-center justify-center text-gray-400 text-xs"
class="w-12 h-12 bg-gray-100 dark:bg-gray-700 rounded flex items-center justify-center text-gray-400 dark:text-gray-500 text-xs"
>
无图
</div>
@@ -622,14 +622,14 @@ function updateProductNumber(id, number) {
<!-- 2. 信息区 -->
<div class="flex-1 min-w-0">
<div class="flex items-center gap-2 mb-1">
<span class="font-bold text-gray-800 truncate" :title="item.drug?.drug_name">{{ item.drug?.drug_name }}</span>
<span class="font-bold text-gray-800 dark:text-gray-100 truncate" :title="item.drug?.drug_name">{{ item.drug?.drug_name }}</span>
<Tag v-if="item.select_number > 0" color="blue" class="m-0 text-xs scale-90">已选</Tag>
</div>
<div class="text-xs text-gray-500 truncate mb-1" :title="item.drug?.function">
<div class="text-xs text-gray-500 dark:text-gray-400 truncate mb-1" :title="item.drug?.function">
{{ item.drug?.pinyin_simple }}
</div>
<div class="text-red-500 font-medium text-sm">
¥{{ item.price }} <span class="text-gray-400 text-xs font-normal">/ {{ item.drug?.unit?.name || '单位' }}</span>
¥{{ item.price }} <span class="text-gray-400 dark:text-gray-500 text-xs font-normal">/ {{ item.drug?.unit?.name || '单位' }}</span>
</div>
</div>
@@ -673,8 +673,8 @@ function updateProductNumber(id, number) {
</div>
<!-- 4. 单次剂量 -->
<div class="flex items-center gap-1 bg-gray-50 p-1 rounded border border-gray-100">
<span class="text-xs text-gray-400 px-1">每次</span>
<div class="flex items-center gap-1 bg-gray-50 dark:bg-gray-800 p-1 rounded border border-gray-100 dark:border-gray-700">
<span class="text-xs text-gray-400 dark:text-gray-500 px-1">每次</span>
<InputNumber
v-model:value="item.drug.number"
:min="1"
@@ -698,7 +698,7 @@ function updateProductNumber(id, number) {
</div>
<!-- 5. 数量操作区 -->
<div class="flex items-center gap-2 pl-2 border-l border-gray-100">
<div class="flex items-center gap-2 pl-2 border-l border-gray-100 dark:border-gray-700">
<template v-if="item.select_number > 0">
<Button size="small" shape="circle" @click="propProducts(item)">
<template #icon><MinusOutlined class="text-xs" /></template>
@@ -728,12 +728,12 @@ function updateProductNumber(id, number) {
<div class="flex flex-col gap-2">
<div class="flex justify-between items-start">
<div>
<div class="font-bold text-base text-gray-800">{{ item.drug?.drug_name }}</div>
<div class="text-xs text-gray-400 mt-1">{{ item.drug?.pinyin_simple }}</div>
<div class="font-bold text-base text-gray-800 dark:text-gray-100">{{ item.drug?.drug_name }}</div>
<div class="text-xs text-gray-400 dark:text-gray-500 mt-1">{{ item.drug?.pinyin_simple }}</div>
</div>
<div class="text-right">
<div class="text-red-500">¥{{ item.price }}</div>
<div class="text-xs text-gray-400">/g</div>
<div class="text-xs text-gray-400 dark:text-gray-500">/g</div>
</div>
</div>
@@ -759,7 +759,7 @@ function updateProductNumber(id, number) {
<!-- 数量控制 -->
<div class="flex items-center justify-between mt-1">
<!-- 数量输入 (dose) -->
<div class="flex items-center border rounded px-1 flex-1 mr-2 bg-gray-50">
<div class="flex items-center border rounded px-1 flex-1 mr-2 bg-gray-50 dark:bg-gray-800 dark:border-gray-700">
<InputNumber
v-model:value="item.drug.number"
:controls="false"
@@ -767,7 +767,7 @@ function updateProductNumber(id, number) {
class="flex-1 !bg-transparent !border-0 shadow-none text-center"
@change="updateProductNumber(item.id, item.drug.number)"
/>
<span class="text-gray-400 text-xs px-1">g</span>
<span class="text-gray-400 dark:text-gray-500 text-xs px-1">g</span>
</div>
<!-- 加减按钮 (Action) -->
@@ -817,6 +817,22 @@ function updateProductNumber(id, number) {
}
}
.dark .drug-row-card {
border-color: #374151;
&.is-selected {
border-color: #3b82f6;
background-color: rgba(59, 130, 246, 0.12);
}
&:hover {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}
}
.dark .chinese-drug-grid-card.is-selected {
border-color: #10b981;
background-color: rgba(16, 185, 129, 0.12);
}
.text-center-input {
text-align: center;
:deep(input) {

View File

@@ -1380,7 +1380,7 @@ const openWesternModal = () => {
// 使用简单产品模态框
SimpleProductModalApi.setData({
values: activeCategory.value,
activePatient_id: activePatient.value?.id,
activePatient_id: getRegisterId(), // 与主页面 storage key 保持一致,使用挂号 ID
getCurrentDrugs,
storagePrefix: '', // 接诊页面不加前缀
});
@@ -1389,7 +1389,7 @@ const openWesternModal = () => {
// 使用原有的西药/中药模态框
WesternDrugModalApi.setData({
values: activeCategory.value,
activePatient_id: activePatient.value?.id,
activePatient_id: getRegisterId(), // 与主页面 storage key 保持一致,使用挂号 ID
getCurrentDrugs,
storagePrefix: '', // 接诊页面不加前缀
});
@@ -3710,6 +3710,11 @@ watch(
padding: 12px;
font-weight: bold;
text-align: center;
background-color: #fafafa;
}
.dark .table-header {
background-color: #262626;
}
.table-row {