fix: 修改要诊所供货价
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
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
CI / CI OK (push) Has been cancelled
Lock Threads / action (push) Has been cancelled
Issue Close Require / close-issues (push) Has been cancelled
Close stale issues / stale (push) Has been cancelled

This commit is contained in:
2025-12-19 15:58:31 +08:00
parent 806058eed7
commit f2d7317982
5 changed files with 588 additions and 1 deletions

View File

@@ -83,3 +83,29 @@ export async function updateStoreShippingFree(data: { id: number }) {
export async function updateStoreSubscribeStatus(data: { id: number }) {
return requestClient.post<any>(`${prefix}update-subscribe-status`, data);
}
/**
* 获取诊所药品列表(按类型)
* @param storeId 诊所ID
* @param type 药品类型1=中药2=西药)
*/
export async function getStoreDrugsByTypeApi(storeId: number, type: number) {
return requestClient.get<any>(`${prefix}get-store-drugs-by-type`, {
params: { store_id: storeId, type },
});
}
/**
* 批量更新诊所药品价格(平台管理员专用)
* @param data
*/
export async function updateStoreDrugPricesApi(data: {
store_id: number;
drugs: Array<{
id: number;
price: number;
buy_price: number;
}>;
}) {
return requestClient.post<any>(`${prefix}update-store-drug-prices`, data);
}

View File

@@ -0,0 +1,171 @@
<script lang="ts" setup>
import { computed, ref, watch } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { Button, Image, Tabs, TabPane } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import {
type DrugItem,
getChinaMedicineGridOptions,
getWesternMedicineGridOptions,
handleSaveRow,
handleBatchSave,
} from '../config/drug-price-table';
import { drugPriceFormOptions } from '../config/search';
const activeTab = ref<string>('1'); // 1=中药, 2=西药
const storeId = ref<number>(0);
const loading = ref(false);
const chinaGridApi = ref<any>(null);
const westernGridApi = ref<any>(null);
const [Modal, modalApi] = useVbenModal({
fullscreenButton: false,
draggable: true,
width: 1200,
onCancel() {
modalApi.close();
},
onConfirm: async () => {
// 批量保存保存当前激活tab的表格中所有修改的数据
if (activeTab.value === '1') {
await handleBatchSave(chinaGridApi.value, storeId.value);
} else {
await handleBatchSave(westernGridApi.value, storeId.value);
}
},
onOpenChange(isOpen: boolean) {
if (isOpen) {
const data = modalApi.getData<{ store_id: number }>();
if (data?.store_id) {
storeId.value = data.store_id;
// 使用proxyConfig自动加载数据不需要手动调用
}
} else {
// 重置数据
activeTab.value = '1';
storeId.value = 0;
}
},
});
// 单行保存回调函数
const handleSaveChinaRow = async (row: DrugItem) => {
await handleSaveRow(row, storeId.value, chinaGridApi.value);
};
const handleSaveWesternRow = async (row: DrugItem) => {
await handleSaveRow(row, storeId.value, westernGridApi.value);
};
// 中药表格配置 - 使用computed包装响应式数据传入storeId
const chinaMedicineGridOptions = computed(() =>
getChinaMedicineGridOptions(storeId.value, handleSaveChinaRow),
);
// 西药表格配置 - 使用computed包装响应式数据传入storeId
const westernMedicineGridOptions = computed(() =>
getWesternMedicineGridOptions(storeId.value, handleSaveWesternRow),
);
const [ChinaGrid, chinaGridApiInstance] = useVbenVxeGrid({
formOptions: drugPriceFormOptions,
gridOptions: chinaMedicineGridOptions,
});
const [WesternGrid, westernGridApiInstance] = useVbenVxeGrid({
formOptions: drugPriceFormOptions,
gridOptions: westernMedicineGridOptions,
});
chinaGridApi.value = chinaGridApiInstance;
westernGridApi.value = westernGridApiInstance;
// 批量保存保存当前tab的所有修改
const handleBatchSaveCurrent = async () => {
if (activeTab.value === '1') {
await handleBatchSave(chinaGridApi.value, storeId.value);
} else {
await handleBatchSave(westernGridApi.value, storeId.value);
}
};
// 监听Tab切换刷新对应表格数据
watch(activeTab, () => {
// Tab切换时刷新对应表格的数据
if (activeTab.value === '1') {
chinaGridApi.value?.reload?.();
} else {
westernGridApi.value?.reload?.();
}
});
</script>
<template>
<Modal
title="修改药品价格"
class="w-[80%]"
>
<div>
<Tabs v-model:activeKey="activeTab">
<TabPane key="1" tab="中药">
<ChinaGrid>
<template #toolbar-buttons>
<Button type="primary" @click="handleBatchSaveCurrent">
批量保存
</Button>
</template>
<template #china-action="{ row }">
<Button
type="link"
size="small"
@click="handleSaveChinaRow(row)"
>
保存
</Button>
</template>
</ChinaGrid>
</TabPane>
<TabPane key="2" tab="西药">
<WesternGrid>
<template #toolbar-buttons>
<Button type="primary" @click="handleBatchSaveCurrent">
批量保存
</Button>
</template>
<template #western-image="{ row }">
<div class="div" style="width: 120px; height: 120px; overflow: scroll">
<Image
:src="
row.image ||
'https://xiaokang88.oss-cn-hangzhou.aliyuncs.com/xk_upload_20250510/cd470ebf97e31c4048ba502ba71b66a3.jpg'
"
height="30"
width="30"
/>
</div>
</template>
<template #western-action="{ row }">
<Button
type="link"
size="small"
@click="handleSaveWesternRow(row)"
>
保存
</Button>
</template>
</WesternGrid>
</TabPane>
</Tabs>
</div>
</Modal>
</template>
<style scoped>
:deep(.ant-tabs-content-holder) {
padding-top: 16px;
}
</style>

View File

@@ -0,0 +1,336 @@
import type { VxeGridProps } from '#/adapter/vxe-table';
import { message } from 'ant-design-vue';
import {
getStoreDrugsByTypeApi,
updateStoreDrugPricesApi,
} from '../api';
export interface DrugItem {
id: number;
drug_id: number;
drug_name: string;
image?: string;
price: number;
buy_price: number;
}
/**
* 单行保存函数
* @param row 当前行数据
* @param storeId 诊所ID
* @param gridApi 表格API实例
*/
export const handleSaveRow = async (
row: DrugItem,
storeId: number,
gridApi: any,
) => {
if (!storeId) {
message.error('诊所ID不能为空');
return;
}
try {
// 获取当前行的最新数据(从表格中获取,包含编辑后的值)
const tableData = gridApi?.grid?.getTableData?.();
const fullData = tableData?.fullData || [];
const currentRow = fullData.find((item: DrugItem) => item.id === row.id);
if (!currentRow) {
message.error('未找到该行数据');
return;
}
// 构建提交数据(只提交当前行)
const drugs = [
{
id: currentRow.id,
price: parseFloat(String(currentRow.price)) || 0,
buy_price: parseFloat(String(currentRow.buy_price)) || 0,
},
];
// 验证数据
for (const drug of drugs) {
if (drug.price < 0 || drug.buy_price < 0) {
message.error('价格不能为负数');
return;
}
if (isNaN(drug.price) || isNaN(drug.buy_price)) {
message.error('价格格式不正确');
return;
}
}
await updateStoreDrugPricesApi({
store_id: storeId,
drugs,
});
message.success('保存成功');
// 刷新表格数据
gridApi?.reload?.();
} catch (error: any) {
message.error(error?.message || '保存失败');
}
};
/**
* 批量保存函数(只保存修改的数据)
* @param gridApi 表格API实例
* @param storeId 诊所ID
*/
export const handleBatchSave = async (
gridApi: any,
storeId: number,
) => {
if (!storeId) {
message.error('诊所ID不能为空');
return;
}
try {
// 使用getRecordset获取修改的记录
const recordset = gridApi?.grid?.getRecordset?.();
const updateRecords = recordset?.updateRecords || [];
// 如果getRecordset没有返回修改记录尝试获取所有数据
if (updateRecords.length === 0) {
const tableData = gridApi?.grid?.getTableData?.();
const fullData = tableData?.fullData || [];
if (fullData.length === 0) {
message.warning('没有可保存的数据');
return;
}
// 如果没有修改记录,提交所有数据
const drugs = fullData.map((row: DrugItem) => ({
id: row.id,
price: parseFloat(String(row.price)) || 0,
buy_price: parseFloat(String(row.buy_price)) || 0,
}));
// 验证数据
for (const drug of drugs) {
if (drug.price < 0 || drug.buy_price < 0) {
message.error('价格不能为负数');
return;
}
if (isNaN(drug.price) || isNaN(drug.buy_price)) {
message.error('价格格式不正确');
return;
}
}
await updateStoreDrugPricesApi({
store_id: storeId,
drugs,
});
message.success('保存成功');
gridApi?.reload?.();
return;
}
// 构建提交数据(只提交修改过的数据)
const dataToSave = updateRecords;
// 构建提交数据
const drugs = dataToSave.map((row: DrugItem) => ({
id: row.id,
price: parseFloat(String(row.price)) || 0,
buy_price: parseFloat(String(row.buy_price)) || 0,
}));
// 验证数据
for (const drug of drugs) {
if (drug.price < 0 || drug.buy_price < 0) {
message.error('价格不能为负数');
return;
}
if (isNaN(drug.price) || isNaN(drug.buy_price)) {
message.error('价格格式不正确');
return;
}
}
await updateStoreDrugPricesApi({
store_id: storeId,
drugs,
});
message.success('保存成功');
// 刷新表格数据
gridApi?.reload?.();
} catch (error: any) {
message.error(error?.message || '保存失败');
}
};
/**
* 获取中药表格配置
* @param storeId 诊所ID
* @param onSaveRow 单行保存回调函数
* @returns 表格配置对象
*/
export const getChinaMedicineGridOptions = (
storeId: number,
onSaveRow: (row: DrugItem) => Promise<void>,
): VxeGridProps<DrugItem> => ({
columns: [
{ type: 'seq', width: 60, title: '序号' },
{ field: 'drug_name', title: '药品名称' },
{
field: 'price',
title: '零售价',
editRender: { name: 'input', props: { type: 'number', step: 0.01, min: 0 } },
formatter: ({ cellValue }) => {
if (cellValue === null || cellValue === undefined || cellValue === '') {
return '0.00';
}
const num = parseFloat(String(cellValue));
return isNaN(num) ? '0.00' : num.toFixed(2);
},
},
{
field: 'buy_price',
title: '供货价',
editRender: { name: 'input', props: { type: 'number', step: 0.01, min: 0 } },
formatter: ({ cellValue }) => {
if (cellValue === null || cellValue === undefined || cellValue === '') {
return '0.00';
}
const num = parseFloat(String(cellValue));
return isNaN(num) ? '0.00' : num.toFixed(2);
},
},
{
title: '操作',
slots: { default: 'china-action' },
},
],
editConfig: {
mode: 'cell',
trigger: 'click',
showStatus: true,
},
proxyConfig: {
ajax: {
query: async ({ page }, formValues) => {
if (!storeId) {
return { items: [], total: 0 };
}
const res = await getStoreDrugsByTypeApi(storeId, 1);
let items = res || [];
// 如果有关键词搜索,进行过滤
if (formValues?.drug_name) {
const keyword = String(formValues.drug_name).toLowerCase();
items = items.filter((item: DrugItem) =>
item.drug_name?.toLowerCase().includes(keyword),
);
}
return { items, total: items.length };
},
},
},
toolbarConfig: {
search: true,
refresh: true,
zoom: true,
slots: {
buttons: 'toolbar-buttons',
},
},
height: 500,
showOverflow: true,
});
/**
* 获取西药表格配置
* @param storeId 诊所ID
* @param onSaveRow 单行保存回调函数
* @returns 表格配置对象
*/
export const getWesternMedicineGridOptions = (
storeId: number,
onSaveRow: (row: DrugItem) => Promise<void>,
): VxeGridProps<DrugItem> => ({
columns: [
{ type: 'seq', width: 60, title: '序号' },
{ field: 'drug_name', title: '药品名称' },
{
field: 'image',
title: '图片',
slots: { default: 'western-image' },
},
{
field: 'price',
title: '零售价',
editRender: { name: 'input', props: { type: 'number', step: 0.01, min: 0 } },
formatter: ({ cellValue }) => {
if (cellValue === null || cellValue === undefined || cellValue === '') {
return '0.00';
}
const num = parseFloat(String(cellValue));
return isNaN(num) ? '0.00' : num.toFixed(2);
},
},
{
field: 'buy_price',
title: '供货价',
editRender: { name: 'input', props: { type: 'number', step: 0.01, min: 0 } },
formatter: ({ cellValue }) => {
if (cellValue === null || cellValue === undefined || cellValue === '') {
return '0.00';
}
const num = parseFloat(String(cellValue));
return isNaN(num) ? '0.00' : num.toFixed(2);
},
},
{
title: '操作',
slots: { default: 'western-action' },
},
],
editConfig: {
mode: 'cell',
trigger: 'click',
showStatus: true,
},
proxyConfig: {
ajax: {
query: async ({ page }, formValues) => {
if (!storeId) {
return { items: [], total: 0 };
}
const res = await getStoreDrugsByTypeApi(storeId, 2);
let items = res || [];
// 如果有关键词搜索,进行过滤
if (formValues?.drug_name) {
const keyword = String(formValues.drug_name).toLowerCase();
items = items.filter((item: DrugItem) =>
item.drug_name?.toLowerCase().includes(keyword),
);
}
return { items, total: items.length };
},
},
},
toolbarConfig: {
search: true,
refresh: true,
zoom: true,
slots: {
buttons: 'toolbar-buttons',
},
},
height: 500,
showOverflow: true,
});

View File

@@ -57,3 +57,25 @@ export const formOptions: VbenFormProps = {
// 按下回车时是否提交表单
submitOnEnter: false,
};
// 药品价格编辑搜索表单配置
export const drugPriceFormOptions: VbenFormProps = {
collapsed: false,
schema: [
{
component: 'VbenInput',
componentProps: {
placeholder: '输入药品名称',
},
defaultValue: '',
fieldName: 'drug_name',
label: '药品名称',
},
],
showCollapseButton: true,
submitButtonOptions: {
content: '查询',
},
submitOnChange: true,
submitOnEnter: false,
};

View File

@@ -1,9 +1,10 @@
<script lang="ts" setup>
import type { VxeGridListeners } from '#/adapter/vxe-table';
import { ref } from 'vue';
import { computed, ref } from 'vue';
import { Page, useVbenModal } from '@vben/common-ui';
import { useUserStore } from '@vben/stores';
import { useClipboard } from '@vueuse/core';
import { Button, Image, message, Switch, Tag } from 'ant-design-vue';
@@ -21,9 +22,17 @@ import {
updateStoreSubscribeStatus,
} from './api';
import FormModalDemo from './components/modal.vue';
import DrugPriceModal from './components/DrugPriceModal.vue';
import { formOptions } from './config/search';
import { gridOptions } from './config/table';
const userStore = useUserStore();
// 判断是否为平台管理员user_type === 2
const isPlatformAdmin = computed(() => {
return userStore?.userInfo?.roles?.user_type === 2;
});
const hasTopTableDropDownActions = ref(false);
const gridEvents: VxeGridListeners<any> = {
@@ -63,6 +72,10 @@ const [FormModal, formModalApi] = useVbenModal({
connectedComponent: FormModalDemo,
});
const [DrugPriceModalComponent, drugPriceModalApi] = useVbenModal({
connectedComponent: DrugPriceModal,
});
const showModal = (data = {}, isUpdate = false) => {
formModalApi.setData({
// 表单值
@@ -73,6 +86,16 @@ const showModal = (data = {}, isUpdate = false) => {
formModalApi.open();
};
/**
* 打开修改药品价格模态框
*/
const showDrugPriceModal = (row: any) => {
drugPriceModalApi.setData({
store_id: row.id,
});
drugPriceModalApi.open();
};
const deleteApi = (row: any) => {
let ids = [];
if (row) {
@@ -135,6 +158,7 @@ const updateSubscribe = (id: number) => {
<Page auto-content-height title="诊所管理">
<FormModal />
<QrCodePreviewModal />
<DrugPriceModalComponent />
<Grid>
<template #toolbar-buttons>
<TableAction
@@ -288,6 +312,14 @@ const updateSubscribe = (id: number) => {
confirm: openQrCode.bind(null, row.id),
},
},
{
label: '修改药品价格',
type: 'link',
icon: 'ant-design:edit-outlined',
size: 'small',
ifShow: isPlatformAdmin,
onClick: showDrugPriceModal.bind(null, row),
},
]"
/>
</template>