1. 管理员管理模块拆分

2. 图片上传自动压缩(大于850kb)
3. 省市区选择组件重构,支持搜索
This commit is contained in:
李琦
2026-05-30 13:52:59 +08:00
parent 402da17e3d
commit 75d1b4db42
20 changed files with 475 additions and 620 deletions

View File

@@ -2,6 +2,8 @@ import type { VxeGridProps } from '#/adapter/vxe-table';
import { message } from 'ant-design-vue';
import { getWarehouseProductTypeOptions } from '#/views/business/warehouse-drug-management/admin/api';
import {
getStoreDrugsByTypeApi,
updateStoreDrugPricesApi,
@@ -16,11 +18,21 @@ export interface DrugItem {
buy_price: number;
}
export interface ProductTypeOption {
label: string;
value: number;
}
const TCM_PLACEHOLDER_IMAGE =
'https://xiaokang88.oss-cn-hangzhou.aliyuncs.com/xk_upload_20250510/cd470ebf97e31c4048ba502ba71b66a3.jpg';
export async function fetchProductTypeOptions(): Promise<ProductTypeOption[]> {
const data = await getWarehouseProductTypeOptions();
return Array.isArray(data) ? data : [];
}
/**
* 单行保存函数
* @param row 当前行数据
* @param storeId 诊所ID
* @param gridApi 表格API实例
*/
export const handleSaveRow = async (
row: DrugItem,
@@ -28,12 +40,11 @@ export const handleSaveRow = async (
gridApi: any,
) => {
if (!storeId) {
message.error('诊所ID不能为空');
message.error('门店ID不能为空');
return;
}
try {
// 获取当前行的最新数据(从表格中获取,包含编辑后的值)
const tableData = gridApi?.grid?.getTableData?.();
const fullData = tableData?.fullData || [];
const currentRow = fullData.find((item: DrugItem) => item.id === row.id);
@@ -43,7 +54,6 @@ export const handleSaveRow = async (
return;
}
// 构建提交数据(只提交当前行)
const drugs = [
{
id: currentRow.id,
@@ -52,7 +62,6 @@ export const handleSaveRow = async (
},
];
// 验证数据
for (const drug of drugs) {
if (drug.price < 0 || drug.buy_price < 0) {
message.error('价格不能为负数');
@@ -70,7 +79,6 @@ export const handleSaveRow = async (
});
message.success('保存成功');
// 刷新表格数据
gridApi?.reload?.();
} catch (error: any) {
message.error(error?.message || '保存失败');
@@ -79,24 +87,17 @@ export const handleSaveRow = async (
/**
* 批量保存函数(只保存修改的数据)
* @param gridApi 表格API实例
* @param storeId 诊所ID
*/
export const handleBatchSave = async (
gridApi: any,
storeId: number,
) => {
export const handleBatchSave = async (gridApi: any, storeId: number) => {
if (!storeId) {
message.error('诊所ID不能为空');
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 || [];
@@ -106,14 +107,12 @@ export const handleBatchSave = async (
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('价格不能为负数');
@@ -135,17 +134,12 @@ export const handleBatchSave = async (
return;
}
// 构建提交数据(只提交修改过的数据)
const dataToSave = updateRecords;
// 构建提交数据
const drugs = dataToSave.map((row: DrugItem) => ({
const drugs = updateRecords.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('价格不能为负数');
@@ -163,7 +157,6 @@ export const handleBatchSave = async (
});
message.success('保存成功');
// 刷新表格数据
gridApi?.reload?.();
} catch (error: any) {
message.error(error?.message || '保存失败');
@@ -171,103 +164,29 @@ export const handleBatchSave = async (
};
/**
* 获取中药表格配置
* @param storeId 诊所ID
* @param onSaveRow 单行保存回调函数
* @returns 表格配置对象
* 按 ProductTypeEnum 获取改价表格配置
*/
export const getChinaMedicineGridOptions = (
storeId: number,
export const getDrugPriceGridOptions = (
getStoreId: () => number,
getProductType: () => number,
onSaveRow: (row: DrugItem) => Promise<void>,
): VxeGridProps<DrugItem> => ({
columns: [
): VxeGridProps<DrugItem> => {
const showImage = getProductType() !== 1;
const columns: 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: '药品名称' },
{
if (showImage) {
columns.push({
field: 'image',
title: '图片',
slots: { default: 'western-image' },
},
slots: { default: 'drug-image' },
});
}
columns.push(
{
field: 'price',
title: '零售价',
@@ -294,43 +213,50 @@ export const getWesternMedicineGridOptions = (
},
{
title: '操作',
slots: { default: 'western-action' },
slots: { default: 'row-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 {
columns,
editConfig: {
mode: 'cell',
trigger: 'click',
showStatus: true,
},
proxyConfig: {
ajax: {
query: async (_ctx, formValues) => {
const storeId = getStoreId();
const productType = getProductType();
if (!storeId) {
return { items: [], total: 0 };
}
const res = await getStoreDrugsByTypeApi(storeId, productType);
let items = res || [];
return { items, total: items.length };
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',
toolbarConfig: {
search: true,
refresh: true,
zoom: true,
slots: {
buttons: 'toolbar-buttons',
},
},
},
height: 500,
showOverflow: true,
});
height: 500,
showOverflow: true,
};
};
export { TCM_PLACEHOLDER_IMAGE };