1. 管理员管理模块拆分
2. 图片上传自动压缩(大于850kb) 3. 省市区选择组件重构,支持搜索
This commit is contained in:
@@ -28,8 +28,8 @@ export const gridOptions: VxeGridProps<RowType> = {
|
||||
columns: [
|
||||
{ type: 'checkbox', width: 60 },
|
||||
{ field: 'id', align: 'left', title: 'ID', width: 100 },
|
||||
{ field: 'drug_name', align: 'left', title: '药品名称' },
|
||||
{ field: 'pinyin_simple', title: '拼音' },
|
||||
{ field: 'drug_name', align: 'left', title: '药品名称', width: 120 },
|
||||
{ field: 'pinyin_simple', title: '拼音', width: 120 },
|
||||
{
|
||||
field: 'zone_name',
|
||||
title: '分区',
|
||||
@@ -42,7 +42,7 @@ export const gridOptions: VxeGridProps<RowType> = {
|
||||
width: 120,
|
||||
slots: { default: 'category' },
|
||||
},
|
||||
{ field: 'supplier.name', title: '供应商', slots: { default: 'supplier' } },
|
||||
{ field: 'supplier.name', title: '供应商', slots: { default: 'supplier' },width: 130 },
|
||||
{
|
||||
field: 'image',
|
||||
align: 'left',
|
||||
@@ -57,8 +57,8 @@ export const gridOptions: VxeGridProps<RowType> = {
|
||||
slots: { default: 'instruction' },
|
||||
width: 130,
|
||||
},
|
||||
{ field: 'function', title: '主要功能' },
|
||||
{ field: 'specification', title: '规格' },
|
||||
{ field: 'function', title: '主要功能', width: 240 },
|
||||
{ field: 'specification', title: '规格', width: 130 },
|
||||
{
|
||||
field: 'is_otc',
|
||||
title: '处方药',
|
||||
@@ -66,14 +66,14 @@ export const gridOptions: VxeGridProps<RowType> = {
|
||||
slots: { default: 'is_otc' },
|
||||
},
|
||||
{ field: 'status', title: '状态', slots: { default: 'status' } },
|
||||
{ field: 'created_at', title: '发布时间' },
|
||||
{ field: 'created_at', title: '发布时间', width: 240 },
|
||||
{
|
||||
field: 'min_adjust_price',
|
||||
title: '最低调整金额',
|
||||
width: 130,
|
||||
slots: { default: 'min_adjust_price' },
|
||||
},
|
||||
{ type: 'html', title: '操作', width: 280, slots: { default: 'action' } },
|
||||
{ type: 'html', title: '操作', fixed: 'right', width: 280, slots: { default: 'action' } },
|
||||
],
|
||||
keepSource: true,
|
||||
pagerConfig: {},
|
||||
|
||||
@@ -46,8 +46,8 @@ export async function getWarehouseDrugManagementInfo(id: number) {
|
||||
/**
|
||||
* 导出仓库药品药品
|
||||
*/
|
||||
export async function exportWarehouseDrugManagementApi() {
|
||||
return requestClient.download(`${prefix}export`);
|
||||
export async function exportWarehouseDrugManagementApi(params?: { type?: number }) {
|
||||
return requestClient.download(`${prefix}export`, { params });
|
||||
}
|
||||
/**
|
||||
* 导出仓库药品药品 - 模板
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
|
||||
import { getWarehouseProductTypeOptions } from '../api';
|
||||
|
||||
// import dayjs from 'dayjs';
|
||||
|
||||
export const formOptions: VbenFormProps = {
|
||||
// 默认展开
|
||||
collapsed: false,
|
||||
schema: [
|
||||
{
|
||||
@@ -17,41 +12,11 @@ export const formOptions: VbenFormProps = {
|
||||
fieldName: 'name',
|
||||
label: '药名称',
|
||||
},
|
||||
{
|
||||
component: 'ApiSelect',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
api: getWarehouseProductTypeOptions,
|
||||
placeholder: '请选择类型',
|
||||
afterFetch: (data: { label: string; value: number }[]) => {
|
||||
if (!Array.isArray(data)) return [];
|
||||
return data.map((item) => ({
|
||||
label: item.label,
|
||||
value: item.value,
|
||||
}));
|
||||
},
|
||||
},
|
||||
defaultValue: 1,
|
||||
fieldName: 'type',
|
||||
label: '类型',
|
||||
},
|
||||
// {
|
||||
// component: 'RangePicker',
|
||||
// componentProps: {
|
||||
// format: 'YYYY-MM-DD', // 确保格式与你需要的一致
|
||||
// },
|
||||
// // defaultValue: [dayjs().startOf('month'), dayjs()],
|
||||
// fieldName: 'search_time',
|
||||
// label: '时间范围',
|
||||
// },
|
||||
],
|
||||
// 控制表单是否显示折叠按钮
|
||||
showCollapseButton: true,
|
||||
submitButtonOptions: {
|
||||
content: '查询',
|
||||
},
|
||||
// 是否在字段值改变时提交表单
|
||||
submitOnChange: true,
|
||||
// 按下回车时是否提交表单
|
||||
submitOnEnter: false,
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
|
||||
import {getWarehouseDrugManagementList} from '../api';
|
||||
import { getWarehouseDrugManagementList } from '../api';
|
||||
|
||||
interface RowType {
|
||||
id: string;
|
||||
@@ -10,70 +10,65 @@ interface RowType {
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export const gridOptions: VxeGridProps<RowType> = {
|
||||
checkboxConfig: {
|
||||
highlight: true,
|
||||
labelField: '',
|
||||
},
|
||||
columnConfig: {
|
||||
useKey: true,
|
||||
},
|
||||
rowConfig: {
|
||||
useKey: true,
|
||||
},
|
||||
columns: [
|
||||
{ type: 'checkbox', width: 60 },
|
||||
{ field: 'id', align: 'left', title: 'ID', width: 100 },
|
||||
{ field: 'drug.drug_name', align: 'left', title: '药品名称' },
|
||||
{ field: 'drug.pinyin_simple', title: '拼音' },
|
||||
{
|
||||
field: 'image',
|
||||
align: 'left',
|
||||
title: '产品图片',
|
||||
slots: { default: 'image' },
|
||||
width: 130,
|
||||
export function createGridOptions(productType: number): VxeGridProps<RowType> {
|
||||
return {
|
||||
checkboxConfig: {
|
||||
highlight: true,
|
||||
labelField: '',
|
||||
},
|
||||
{ field: 'market_price', title: '供货价' },
|
||||
{ field: 'price', title: '建议售价' },
|
||||
{ field: 'status', title: '状态', slots: { default: 'status' } },
|
||||
{ field: 'created_at', title: '创建时间' },
|
||||
{ type: 'html', title: '操作', slots: { default: 'action' } },
|
||||
],
|
||||
keepSource: true,
|
||||
pagerConfig: {},
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
// 请求后端接口方法
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getWarehouseDrugManagementList({
|
||||
page: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
columnConfig: {
|
||||
useKey: true,
|
||||
},
|
||||
rowConfig: {
|
||||
useKey: true,
|
||||
},
|
||||
columns: [
|
||||
{ type: 'checkbox', width: 60 },
|
||||
{ field: 'id', align: 'left', title: 'ID', width: 100 },
|
||||
{ field: 'drug.drug_name', align: 'left', title: '药品名称' },
|
||||
{ field: 'drug.pinyin_simple', title: '拼音' },
|
||||
{
|
||||
field: 'image',
|
||||
align: 'left',
|
||||
title: '产品图片',
|
||||
slots: { default: 'image' },
|
||||
width: 130,
|
||||
},
|
||||
{ field: 'market_price', title: '供货价' },
|
||||
{ field: 'price', title: '建议售价' },
|
||||
{ field: 'status', title: '状态', slots: { default: 'status' } },
|
||||
{ field: 'created_at', title: '创建时间' },
|
||||
{ type: 'html', title: '操作', slots: { default: 'action' } },
|
||||
],
|
||||
keepSource: true,
|
||||
pagerConfig: {},
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getWarehouseDrugManagementList({
|
||||
page: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
type: productType,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
// exportConfig: {
|
||||
// api: passApplicationApi,
|
||||
// },
|
||||
height: 'auto',
|
||||
border: false,
|
||||
toolbarConfig: {
|
||||
// 是否显示搜索表单控制按钮
|
||||
// @ts-ignore 正式环境时有完整的类型声明
|
||||
search: true,
|
||||
refresh: true, // 刷新
|
||||
print: false, // 打印
|
||||
export: false, // 导出
|
||||
// custom: true, // 自定义列
|
||||
zoom: true, // 最大化最小化
|
||||
slots: {
|
||||
buttons: 'toolbar-buttons',
|
||||
height: 'auto',
|
||||
border: false,
|
||||
toolbarConfig: {
|
||||
search: true,
|
||||
refresh: true,
|
||||
print: false,
|
||||
export: false,
|
||||
zoom: true,
|
||||
slots: {
|
||||
buttons: 'toolbar-buttons',
|
||||
},
|
||||
custom: {
|
||||
icon: 'vxe-icon-menu',
|
||||
},
|
||||
},
|
||||
custom: {
|
||||
// 自定义列-图标
|
||||
icon: 'vxe-icon-menu',
|
||||
},
|
||||
},
|
||||
showOverflow: false,
|
||||
};
|
||||
showOverflow: false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<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';
|
||||
|
||||
@@ -11,6 +11,7 @@ import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { TableAction } from '#/components/table-action';
|
||||
import { downloadByData } from '#/util/tool';
|
||||
|
||||
import { useWarehouseDrugTypeRoute } from '../shared/useWarehouseDrugTypeRoute';
|
||||
import {
|
||||
deleteWarehouseDrugManagement,
|
||||
exportWarehouseDrugManagementApi,
|
||||
@@ -19,7 +20,11 @@ import {
|
||||
import FormModalDemo from './components/modal.vue';
|
||||
import ExcelUpload from './components/ExcelUpload.vue';
|
||||
import { formOptions } from './config/search';
|
||||
import { gridOptions } from './config/table';
|
||||
import { createGridOptions } from './config/table';
|
||||
|
||||
const { productType, typeLabel } = useWarehouseDrugTypeRoute(
|
||||
'/warehouse-drug-management/type/1',
|
||||
);
|
||||
|
||||
const hasTopTableDropDownActions = ref(false);
|
||||
|
||||
@@ -36,6 +41,8 @@ const gridEvents: VxeGridListeners<any> = {
|
||||
},
|
||||
};
|
||||
|
||||
const gridOptions = computed(() => createGridOptions(productType.value));
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions,
|
||||
gridOptions,
|
||||
@@ -67,7 +74,7 @@ const showModal = (data = {}, isUpdate = false) => {
|
||||
values: data,
|
||||
update: isUpdate,
|
||||
gridApi,
|
||||
listDrugType: gridApi.formApi.latestSubmissionValues?.type,
|
||||
listDrugType: productType.value,
|
||||
});
|
||||
formModalApi.open();
|
||||
};
|
||||
@@ -88,21 +95,17 @@ const deleteApi = (row: any) => {
|
||||
* 导出
|
||||
*/
|
||||
const passApplication = () => {
|
||||
// 新标签跳转到 exportWarehouseDrugManagementApi
|
||||
exportWarehouseDrugManagementApi().then((res) => {
|
||||
// 创建新的URL表示指定的File对象或者Blob对象。
|
||||
exportWarehouseDrugManagementApi({ type: productType.value }).then((res) => {
|
||||
downloadByData(res.data, '萧康云医-仓库商品导出.xlsx');
|
||||
message.success('导出成功!');
|
||||
});
|
||||
};
|
||||
|
||||
const openExcelUploadModal = (type = 1) => {
|
||||
const uploadType = gridApi.formApi.latestSubmissionValues.type;
|
||||
|
||||
ExcelUploadModalApi.setData({
|
||||
gridApi,
|
||||
type,
|
||||
uploadType,
|
||||
uploadType: productType.value,
|
||||
passApplication,
|
||||
});
|
||||
ExcelUploadModalApi.open();
|
||||
@@ -117,10 +120,10 @@ const updateStatus = (id: number) => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height title="西(中成)药管理">
|
||||
<Page auto-content-height :title="typeLabel || '总仓库管理'">
|
||||
<ExcelUploadModal />
|
||||
<FormModal />
|
||||
<Grid>
|
||||
<Grid v-if="productType">
|
||||
<template #toolbar-buttons>
|
||||
<TableAction
|
||||
:actions="[
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import { computed, onMounted, ref, watch } from 'vue';
|
||||
import type { RouteLocationNormalizedLoaded } from 'vue-router';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
import { getWarehouseProductTypeOptions } from '../admin/api';
|
||||
|
||||
const VALID_TYPES = [1, 2, 3, 5, 6, 7];
|
||||
|
||||
function resolveProductType(route: RouteLocationNormalizedLoaded): number {
|
||||
const rawParam = route.params.type;
|
||||
const fromParam = Number(Array.isArray(rawParam) ? rawParam[0] : rawParam);
|
||||
if (VALID_TYPES.includes(fromParam)) {
|
||||
return fromParam;
|
||||
}
|
||||
|
||||
const match = route.path.match(/\/type\/(\d+)(?:\/|$)/);
|
||||
const fromPath = Number(match?.[1]);
|
||||
return VALID_TYPES.includes(fromPath) ? fromPath : 0;
|
||||
}
|
||||
|
||||
async function loadTypeLabel(productType: number, typeLabel: { value: string }) {
|
||||
try {
|
||||
const options = await getWarehouseProductTypeOptions();
|
||||
const matched = options?.find((item) => item.value === productType);
|
||||
typeLabel.value = matched?.label ?? `类型${productType}`;
|
||||
} catch {
|
||||
typeLabel.value = `类型${productType}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从路由 /.../type/:type 或静态 path /.../type/1 锁定仓库商品类型
|
||||
*/
|
||||
export function useWarehouseDrugTypeRoute(fallbackRedirect: string) {
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const typeLabel = ref('');
|
||||
|
||||
const productType = computed(() => resolveProductType(route));
|
||||
|
||||
onMounted(async () => {
|
||||
if (!productType.value) {
|
||||
await router.replace(fallbackRedirect);
|
||||
return;
|
||||
}
|
||||
await loadTypeLabel(productType.value, typeLabel);
|
||||
});
|
||||
|
||||
watch(productType, async (value) => {
|
||||
if (value) {
|
||||
await loadTypeLabel(value, typeLabel);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
productType,
|
||||
typeLabel,
|
||||
};
|
||||
}
|
||||
|
||||
export { VALID_TYPES };
|
||||
@@ -52,8 +52,8 @@ export async function updateWarehouseDrugManagementShopUpdateStatusApi(id: numbe
|
||||
/**
|
||||
* 导出仓库药品药品
|
||||
*/
|
||||
export async function exportWarehouseDrugManagementStoreApi() {
|
||||
return requestClient.download(`${prefix}export`);
|
||||
export async function exportWarehouseDrugManagementStoreApi(params?: { type?: number }) {
|
||||
return requestClient.download(`${prefix}export`, { params });
|
||||
}
|
||||
/**
|
||||
* 导出仓库药品药品 - 模板
|
||||
@@ -106,16 +106,16 @@ export async function importUpdatePriceApi(data: Record<string, any>) {
|
||||
* 同步总仓库api
|
||||
* @param data
|
||||
*/
|
||||
export async function syncDrugApi(data: Record<string, any>) {
|
||||
return requestClient.get(`${prefix}sync-drug`, data);
|
||||
export async function syncDrugApi(params?: Record<string, any>) {
|
||||
return requestClient.get(`${prefix}sync-drug`, { params });
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步总仓库价格api
|
||||
* @param data
|
||||
* @param params
|
||||
*/
|
||||
export async function syncDrugPriceApi(data: Record<string, any>) {
|
||||
return requestClient.get(`${prefix}sync-drug-price`, data);
|
||||
export async function syncDrugPriceApi(params?: Record<string, any>) {
|
||||
return requestClient.get(`${prefix}sync-drug-price`, { params });
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
|
||||
import { getWarehouseProductTypeOptions } from '../api';
|
||||
|
||||
// import dayjs from 'dayjs';
|
||||
|
||||
export const formOptions: VbenFormProps = {
|
||||
// 默认展开
|
||||
collapsed: false,
|
||||
schema: [
|
||||
{
|
||||
@@ -17,43 +12,11 @@ export const formOptions: VbenFormProps = {
|
||||
fieldName: 'name',
|
||||
label: '药名称',
|
||||
},
|
||||
{
|
||||
component: 'ApiSelect',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
api: getWarehouseProductTypeOptions,
|
||||
placeholder: '请选择类型',
|
||||
afterFetch: (data: { label: string; value: number }[]) => {
|
||||
if (!Array.isArray(data)) return [];
|
||||
return data.map((item) => ({
|
||||
label: item.label,
|
||||
value: item.value,
|
||||
}));
|
||||
},
|
||||
show: () => false,
|
||||
},
|
||||
disabled: true,
|
||||
defaultValue: 2,
|
||||
fieldName: 'type',
|
||||
label: '类型',
|
||||
},
|
||||
// {
|
||||
// component: 'RangePicker',
|
||||
// componentProps: {
|
||||
// format: 'YYYY-MM-DD', // 确保格式与你需要的一致
|
||||
// },
|
||||
// // defaultValue: [dayjs().startOf('month'), dayjs()],
|
||||
// fieldName: 'search_time',
|
||||
// label: '时间范围',
|
||||
// },
|
||||
],
|
||||
// 控制表单是否显示折叠按钮
|
||||
showCollapseButton: true,
|
||||
submitButtonOptions: {
|
||||
content: '查询',
|
||||
},
|
||||
// 是否在字段值改变时提交表单
|
||||
submitOnChange: true,
|
||||
// 按下回车时是否提交表单
|
||||
submitOnEnter: false,
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
|
||||
import {getWarehouseDrugManagementStoreList} from '../api';
|
||||
import { getWarehouseDrugManagementStoreList } from '../api';
|
||||
|
||||
interface RowType {
|
||||
id: string;
|
||||
@@ -10,75 +10,66 @@ interface RowType {
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export const gridOptions: VxeGridProps<RowType> = {
|
||||
checkboxConfig: {
|
||||
highlight: true,
|
||||
labelField: '',
|
||||
},
|
||||
columnConfig: {
|
||||
useKey: true,
|
||||
},
|
||||
rowConfig: {
|
||||
useKey: true,
|
||||
},
|
||||
columns: [
|
||||
{ type: 'checkbox', width: 60 },
|
||||
{ field: 'id', align: 'left', title: 'ID', width: 100 },
|
||||
{ field: 'drug.drug_name', align: 'left', title: '药品名称' },
|
||||
{ field: 'drug.pinyin_simple', title: '拼音' },
|
||||
{
|
||||
field: 'image',
|
||||
align: 'left',
|
||||
title: '产品图片',
|
||||
slots: { default: 'image' },
|
||||
width: 130,
|
||||
export function createGridOptions(productType: number): VxeGridProps<RowType> {
|
||||
return {
|
||||
checkboxConfig: {
|
||||
highlight: true,
|
||||
labelField: '',
|
||||
},
|
||||
// { field: 'drug.drug_store_drug.market_price', title: '供货价' },
|
||||
{ field: 'buy_price', title: '供货价' },
|
||||
{
|
||||
field: 'drug.drug_store_drug.price',
|
||||
title: '售价(不可修改)',
|
||||
columnConfig: {
|
||||
useKey: true,
|
||||
},
|
||||
// { field: 'price', title: '售价' },
|
||||
// { field: 'status', title: '状态', slots: { default: 'status' } },
|
||||
{ field: 'is_shop', title: '是否上架商城', slots: { default: 'is_shop' } },
|
||||
// { type: 'html', align: 'right', title: '操作', slots: { default: 'action' }, width: 300, },
|
||||
],
|
||||
keepSource: true,
|
||||
pagerConfig: {},
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
// 请求后端接口方法
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getWarehouseDrugManagementStoreList({
|
||||
page: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
rowConfig: {
|
||||
useKey: true,
|
||||
},
|
||||
columns: [
|
||||
{ type: 'checkbox', width: 60 },
|
||||
{ field: 'id', align: 'left', title: 'ID', width: 100 },
|
||||
{ field: 'drug.drug_name', align: 'left', title: '药品名称' },
|
||||
{ field: 'drug.pinyin_simple', title: '拼音' },
|
||||
{
|
||||
field: 'image',
|
||||
align: 'left',
|
||||
title: '产品图片',
|
||||
slots: { default: 'image' },
|
||||
width: 130,
|
||||
},
|
||||
{ field: 'buy_price', title: '供货价' },
|
||||
{
|
||||
field: 'drug.drug_store_drug.price',
|
||||
title: '售价(不可修改)',
|
||||
},
|
||||
{ field: 'is_shop', title: '是否上架商城', slots: { default: 'is_shop' } },
|
||||
],
|
||||
keepSource: true,
|
||||
pagerConfig: {},
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getWarehouseDrugManagementStoreList({
|
||||
page: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
type: productType,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
// exportConfig: {
|
||||
// api: passApplicationApi,
|
||||
// },
|
||||
height: 'auto',
|
||||
border: false,
|
||||
toolbarConfig: {
|
||||
// 是否显示搜索表单控制按钮
|
||||
// @ts-ignore 正式环境时有完整的类型声明
|
||||
search: true,
|
||||
refresh: true, // 刷新
|
||||
print: false, // 打印
|
||||
export: false, // 导出
|
||||
// custom: true, // 自定义列
|
||||
zoom: true, // 最大化最小化
|
||||
slots: {
|
||||
buttons: 'toolbar-buttons',
|
||||
height: 'auto',
|
||||
border: false,
|
||||
toolbarConfig: {
|
||||
search: true,
|
||||
refresh: true,
|
||||
print: false,
|
||||
export: false,
|
||||
zoom: true,
|
||||
slots: {
|
||||
buttons: 'toolbar-buttons',
|
||||
},
|
||||
custom: {
|
||||
icon: 'vxe-icon-menu',
|
||||
},
|
||||
},
|
||||
custom: {
|
||||
// 自定义列-图标
|
||||
icon: 'vxe-icon-menu',
|
||||
},
|
||||
},
|
||||
showOverflow: false,
|
||||
};
|
||||
showOverflow: false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<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';
|
||||
|
||||
@@ -11,6 +11,7 @@ import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { TableAction } from '#/components/table-action';
|
||||
import { downloadByData } from '#/util/tool';
|
||||
|
||||
import { useWarehouseDrugTypeRoute } from '../shared/useWarehouseDrugTypeRoute';
|
||||
import {
|
||||
checkSubscribeApi,
|
||||
deleteWarehouseDrugManagementStore,
|
||||
@@ -24,7 +25,11 @@ import {
|
||||
import ExcelUpload from './components/ExcelUpload.vue';
|
||||
import FormModalDemo from './components/modal.vue';
|
||||
import { formOptions } from './config/search';
|
||||
import { gridOptions } from './config/table';
|
||||
import { createGridOptions } from './config/table';
|
||||
|
||||
const { productType, typeLabel } = useWarehouseDrugTypeRoute(
|
||||
'/warehouse-drug-management-shop/type/2',
|
||||
);
|
||||
|
||||
const hasTopTableDropDownActions = ref(false);
|
||||
|
||||
@@ -41,6 +46,8 @@ const gridEvents: VxeGridListeners<any> = {
|
||||
},
|
||||
};
|
||||
|
||||
const gridOptions = computed(() => createGridOptions(productType.value));
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions,
|
||||
gridOptions,
|
||||
@@ -99,7 +106,7 @@ const openExcelUploadModal = (type = 1) => {
|
||||
};
|
||||
|
||||
const syncDrugs = () => {
|
||||
syncDrugApi().then((res) => {
|
||||
syncDrugApi({ type: productType.value }).then(() => {
|
||||
message.success('同步成功!');
|
||||
gridApi.query();
|
||||
});
|
||||
@@ -121,8 +128,7 @@ const updateWarehouseDrugManagementShopUpdateStatus = (id) => {
|
||||
|
||||
const syncDrugPrice = () => {
|
||||
gridApi.setLoading(true);
|
||||
syncDrugPriceApi().then((res) => {
|
||||
console.log(res, '同步结果');
|
||||
syncDrugPriceApi({ type: productType.value }).then(() => {
|
||||
message.success('同步成功!');
|
||||
gridApi.setLoading(false);
|
||||
gridApi.query();
|
||||
@@ -147,10 +153,10 @@ checkSubscribe();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height title="西(中成)药管理">
|
||||
<Page auto-content-height :title="typeLabel || '商城仓库管理'">
|
||||
<ExcelUploadModal />
|
||||
<FormModal />
|
||||
<Grid>
|
||||
<Grid v-if="productType">
|
||||
<template #toolbar-buttons>
|
||||
<TableAction
|
||||
:actions="[
|
||||
|
||||
@@ -52,8 +52,8 @@ export async function updateWarehouseDrugManagementShopUpdateStatusApi(id: numbe
|
||||
/**
|
||||
* 导出仓库药品药品
|
||||
*/
|
||||
export async function exportWarehouseDrugManagementStoreApi() {
|
||||
return requestClient.download(`${prefix}export`);
|
||||
export async function exportWarehouseDrugManagementStoreApi(params?: { type?: number }) {
|
||||
return requestClient.download(`${prefix}export`, { params });
|
||||
}
|
||||
/**
|
||||
* 导出仓库药品药品 - 模板
|
||||
@@ -106,16 +106,16 @@ export async function importUpdatePriceApi(data: Record<string, any>) {
|
||||
* 同步总仓库api
|
||||
* @param data
|
||||
*/
|
||||
export async function syncDrugApi(data: Record<string, any>) {
|
||||
return requestClient.get(`${prefix}sync-drug`, data);
|
||||
export async function syncDrugApi(params?: Record<string, any>) {
|
||||
return requestClient.get(`${prefix}sync-drug`, { params });
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步总仓库价格api
|
||||
* @param data
|
||||
* @param params
|
||||
*/
|
||||
export async function syncDrugPriceApi(data: Record<string, any>) {
|
||||
return requestClient.get(`${prefix}sync-drug-price`, data);
|
||||
export async function syncDrugPriceApi(params?: Record<string, any>) {
|
||||
return requestClient.get(`${prefix}sync-drug-price`, { params });
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
const gridApi = ref();
|
||||
const passApplication = ref();
|
||||
const importType = ref(1);
|
||||
const uploadProductType = ref(1);
|
||||
const fileList = ref<UploadFile[]>([]);
|
||||
const selectedFile = ref<File | null>(null); // 新增:存储选中的文件
|
||||
const isFileSelected = ref(false); // 新增:标记是否有文件被选中
|
||||
@@ -53,6 +54,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
|
||||
await submitApi({
|
||||
file: selectedFile.value,
|
||||
type: uploadProductType.value,
|
||||
});
|
||||
|
||||
message.success('上传成功');
|
||||
@@ -74,6 +76,7 @@ const [Modal, modalApi] = useVbenModal({
|
||||
gridApi.value = isOpen ? modalApi.getData()?.gridApi : null;
|
||||
passApplication.value = isOpen ? modalApi.getData()?.passApplication : null;
|
||||
importType.value = isOpen ? modalApi.getData()?.type : 1;
|
||||
uploadProductType.value = isOpen ? modalApi.getData()?.uploadType ?? 1 : 1;
|
||||
fileList.value = [];
|
||||
selectedFile.value = null; // 重置选中的文件
|
||||
isFileSelected.value = false; // 重置文件选择状态
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
import type { VbenFormProps } from '#/adapter/form';
|
||||
|
||||
import { getWarehouseProductTypeOptions } from '../api';
|
||||
|
||||
// import dayjs from 'dayjs';
|
||||
|
||||
export const formOptions: VbenFormProps = {
|
||||
// 默认展开
|
||||
collapsed: false,
|
||||
schema: [
|
||||
{
|
||||
@@ -17,41 +12,11 @@ export const formOptions: VbenFormProps = {
|
||||
fieldName: 'name',
|
||||
label: '药名称',
|
||||
},
|
||||
{
|
||||
component: 'ApiSelect',
|
||||
componentProps: {
|
||||
allowClear: true,
|
||||
api: getWarehouseProductTypeOptions,
|
||||
placeholder: '请选择类型',
|
||||
afterFetch: (data: { label: string; value: number }[]) => {
|
||||
if (!Array.isArray(data)) return [];
|
||||
return data.map((item) => ({
|
||||
label: item.label,
|
||||
value: item.value,
|
||||
}));
|
||||
},
|
||||
},
|
||||
defaultValue: 1,
|
||||
fieldName: 'type',
|
||||
label: '类型',
|
||||
},
|
||||
// {
|
||||
// component: 'RangePicker',
|
||||
// componentProps: {
|
||||
// format: 'YYYY-MM-DD', // 确保格式与你需要的一致
|
||||
// },
|
||||
// // defaultValue: [dayjs().startOf('month'), dayjs()],
|
||||
// fieldName: 'search_time',
|
||||
// label: '时间范围',
|
||||
// },
|
||||
],
|
||||
// 控制表单是否显示折叠按钮
|
||||
showCollapseButton: true,
|
||||
submitButtonOptions: {
|
||||
content: '查询',
|
||||
},
|
||||
// 是否在字段值改变时提交表单
|
||||
submitOnChange: true,
|
||||
// 按下回车时是否提交表单
|
||||
submitOnEnter: false,
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { VxeGridProps } from '#/adapter/vxe-table';
|
||||
|
||||
import {getWarehouseDrugManagementStoreList} from '../api';
|
||||
import { getWarehouseDrugManagementStoreList } from '../api';
|
||||
|
||||
interface RowType {
|
||||
id: string;
|
||||
@@ -10,72 +10,71 @@ interface RowType {
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export const gridOptions: VxeGridProps<RowType> = {
|
||||
checkboxConfig: {
|
||||
highlight: true,
|
||||
labelField: '',
|
||||
},
|
||||
columnConfig: {
|
||||
useKey: true,
|
||||
},
|
||||
rowConfig: {
|
||||
useKey: true,
|
||||
},
|
||||
columns: [
|
||||
{ type: 'checkbox', width: 60 },
|
||||
{ field: 'id', align: 'left', title: 'ID', width: 100 },
|
||||
{ field: 'drug.drug_name', align: 'left', title: '药品名称' },
|
||||
{ field: 'drug.pinyin_simple', title: '拼音' },
|
||||
{
|
||||
field: 'image',
|
||||
align: 'left',
|
||||
title: '产品图片',
|
||||
slots: { default: 'image' },
|
||||
width: 130,
|
||||
export function createGridOptions(productType: number): VxeGridProps<RowType> {
|
||||
return {
|
||||
checkboxConfig: {
|
||||
highlight: true,
|
||||
labelField: '',
|
||||
},
|
||||
// { field: 'drug.drug_store_drug.market_price', title: '供货价' },
|
||||
{ field: 'buy_price', title: '供货价' },
|
||||
{ field: 'drug.drug_store_drug.price', title: '建议售价' },
|
||||
{ field: 'price', title: '售价' },
|
||||
{ field: 'status', title: '状态', slots: { default: 'status' } },
|
||||
// { field: 'is_shop', title: '是否上架商城', slots: { default: 'is_shop' } },
|
||||
{ type: 'html', align: 'right', title: '操作', slots: { default: 'action' }, width: 300, },
|
||||
],
|
||||
keepSource: true,
|
||||
pagerConfig: {},
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
// 请求后端接口方法
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getWarehouseDrugManagementStoreList({
|
||||
page: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
...formValues,
|
||||
});
|
||||
columnConfig: {
|
||||
useKey: true,
|
||||
},
|
||||
rowConfig: {
|
||||
useKey: true,
|
||||
},
|
||||
columns: [
|
||||
{ type: 'checkbox', width: 60 },
|
||||
{ field: 'id', align: 'left', title: 'ID', width: 100 },
|
||||
{ field: 'drug.drug_name', align: 'left', title: '药品名称' },
|
||||
{ field: 'drug.pinyin_simple', title: '拼音' },
|
||||
{
|
||||
field: 'image',
|
||||
align: 'left',
|
||||
title: '产品图片',
|
||||
slots: { default: 'image' },
|
||||
width: 130,
|
||||
},
|
||||
{ field: 'buy_price', title: '供货价' },
|
||||
{ field: 'drug.drug_store_drug.price', title: '建议售价' },
|
||||
{ field: 'price', title: '售价' },
|
||||
{ field: 'status', title: '状态', slots: { default: 'status' } },
|
||||
{
|
||||
type: 'html',
|
||||
align: 'right',
|
||||
title: '操作',
|
||||
slots: { default: 'action' },
|
||||
width: 300,
|
||||
},
|
||||
],
|
||||
keepSource: true,
|
||||
pagerConfig: {},
|
||||
proxyConfig: {
|
||||
ajax: {
|
||||
query: async ({ page }, formValues) => {
|
||||
return await getWarehouseDrugManagementStoreList({
|
||||
page: page.currentPage,
|
||||
pageSize: page.pageSize,
|
||||
type: productType,
|
||||
...formValues,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
// exportConfig: {
|
||||
// api: passApplicationApi,
|
||||
// },
|
||||
height: 'auto',
|
||||
border: false,
|
||||
toolbarConfig: {
|
||||
// 是否显示搜索表单控制按钮
|
||||
// @ts-ignore 正式环境时有完整的类型声明
|
||||
search: true,
|
||||
refresh: true, // 刷新
|
||||
print: false, // 打印
|
||||
export: false, // 导出
|
||||
// custom: true, // 自定义列
|
||||
zoom: true, // 最大化最小化
|
||||
slots: {
|
||||
buttons: 'toolbar-buttons',
|
||||
height: 'auto',
|
||||
border: false,
|
||||
toolbarConfig: {
|
||||
search: true,
|
||||
refresh: true,
|
||||
print: false,
|
||||
export: false,
|
||||
zoom: true,
|
||||
slots: {
|
||||
buttons: 'toolbar-buttons',
|
||||
},
|
||||
custom: {
|
||||
icon: 'vxe-icon-menu',
|
||||
},
|
||||
},
|
||||
custom: {
|
||||
// 自定义列-图标
|
||||
icon: 'vxe-icon-menu',
|
||||
},
|
||||
},
|
||||
showOverflow: false,
|
||||
};
|
||||
showOverflow: false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<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';
|
||||
|
||||
@@ -11,6 +11,7 @@ import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
import { TableAction } from '#/components/table-action';
|
||||
import { downloadByData } from '#/util/tool';
|
||||
|
||||
import { useWarehouseDrugTypeRoute } from '../shared/useWarehouseDrugTypeRoute';
|
||||
import {
|
||||
checkSubscribeApi,
|
||||
deleteWarehouseDrugManagementStore,
|
||||
@@ -24,7 +25,11 @@ import {
|
||||
import ExcelUpload from './components/ExcelUpload.vue';
|
||||
import FormModalDemo from './components/modal.vue';
|
||||
import { formOptions } from './config/search';
|
||||
import { gridOptions } from './config/table';
|
||||
import { createGridOptions } from './config/table';
|
||||
|
||||
const { productType, typeLabel } = useWarehouseDrugTypeRoute(
|
||||
'/warehouse-drug-management-store/type/1',
|
||||
);
|
||||
|
||||
const hasTopTableDropDownActions = ref(false);
|
||||
|
||||
@@ -41,6 +46,8 @@ const gridEvents: VxeGridListeners<any> = {
|
||||
},
|
||||
};
|
||||
|
||||
const gridOptions = computed(() => createGridOptions(productType.value));
|
||||
|
||||
const [Grid, gridApi] = useVbenVxeGrid({
|
||||
formOptions,
|
||||
gridOptions,
|
||||
@@ -81,9 +88,7 @@ const deleteApi = (row: any) => {
|
||||
* 导出
|
||||
*/
|
||||
const passApplication = () => {
|
||||
// 新标签跳转到 exportWarehouseDrugManagementStoreApi
|
||||
exportWarehouseDrugManagementStoreApi().then((res) => {
|
||||
// 创建新的URL表示指定的File对象或者Blob对象。
|
||||
exportWarehouseDrugManagementStoreApi({ type: productType.value }).then((res) => {
|
||||
downloadByData(res.data, '萧康云医-仓库商品导出.xlsx');
|
||||
message.success('导出成功!');
|
||||
});
|
||||
@@ -93,13 +98,14 @@ const openExcelUploadModal = (type = 1) => {
|
||||
ExcelUploadModalApi.setData({
|
||||
gridApi,
|
||||
type,
|
||||
uploadType: productType.value,
|
||||
passApplication,
|
||||
});
|
||||
ExcelUploadModalApi.open();
|
||||
};
|
||||
|
||||
const syncDrugs = () => {
|
||||
syncDrugApi().then((res) => {
|
||||
syncDrugApi({ type: productType.value }).then(() => {
|
||||
message.success('同步成功!');
|
||||
gridApi.query();
|
||||
});
|
||||
@@ -121,8 +127,7 @@ const updateWarehouseDrugManagementShopUpdateStatus = (id) => {
|
||||
|
||||
const syncDrugPrice = () => {
|
||||
gridApi.setLoading(true);
|
||||
syncDrugPriceApi().then((res) => {
|
||||
console.log(res, '同步结果');
|
||||
syncDrugPriceApi({ type: productType.value }).then(() => {
|
||||
message.success('同步成功!');
|
||||
gridApi.setLoading(false);
|
||||
gridApi.query();
|
||||
@@ -147,10 +152,10 @@ checkSubscribe();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page auto-content-height title="西(中成)药管理">
|
||||
<Page auto-content-height :title="typeLabel || '门店仓库管理'">
|
||||
<ExcelUploadModal />
|
||||
<FormModal />
|
||||
<Grid>
|
||||
<Grid v-if="productType">
|
||||
<template #toolbar-buttons>
|
||||
<TableAction
|
||||
:actions="[
|
||||
@@ -158,14 +163,12 @@ checkSubscribe();
|
||||
label: '批量修改价格',
|
||||
type: 'primary',
|
||||
icon: 'ix:export-check',
|
||||
// auth: ['超级西(中成)药', 'sys:user:save'],
|
||||
onClick: openExcelUploadModal.bind(null, 2),
|
||||
},
|
||||
{
|
||||
label: '更新总仓库商品(只更新未同步的商品)',
|
||||
type: 'primary',
|
||||
icon: 'material-symbols:sync-alt',
|
||||
// auth: ['超级西(中成)药', 'sys:user:save'],
|
||||
onClick: syncDrugs,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
import FormModalDemo from './components/modal.vue';
|
||||
// 导入绑定在线复诊配置弹窗(使用store的组件)
|
||||
import BindConsultationModal from '#/views/system/store/components/BindConsultationModal.vue';
|
||||
import DrugPriceModal from '#/views/system/store/components/DrugPriceModal.vue';
|
||||
import StoreExternalFieldModal from '#/views/system/store/components/StoreExternalFieldModal.vue';
|
||||
// 导入搜索表单配置
|
||||
import { formOptions } from './config/search';
|
||||
@@ -104,6 +105,17 @@ const [StoreExternalFieldModalComponent, storeExternalFieldModalApi] = useVbenMo
|
||||
connectedComponent: StoreExternalFieldModal,
|
||||
});
|
||||
|
||||
const [DrugPriceModalComponent, drugPriceModalApi] = useVbenModal({
|
||||
connectedComponent: DrugPriceModal,
|
||||
});
|
||||
|
||||
const showDrugPriceModal = (row: any) => {
|
||||
drugPriceModalApi.setData({
|
||||
store_id: row.id,
|
||||
});
|
||||
drugPriceModalApi.open();
|
||||
};
|
||||
|
||||
const showStoreExternalFieldModal = (
|
||||
row: any,
|
||||
field: 'erp_id' | 'mes_id',
|
||||
@@ -236,6 +248,7 @@ const batchSyncDrugPrice = () => {
|
||||
<Page auto-content-height title="药店管理">
|
||||
<FormModal />
|
||||
<QrCodePreviewModal />
|
||||
<DrugPriceModalComponent />
|
||||
<BindConsultationModalComponent />
|
||||
<StoreExternalFieldModalComponent />
|
||||
<Grid>
|
||||
@@ -427,6 +440,14 @@ const batchSyncDrugPrice = () => {
|
||||
confirm: openQrCode.bind(null, row.id),
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '修改药品价格',
|
||||
type: 'link',
|
||||
icon: 'ant-design:edit-outlined',
|
||||
size: 'small',
|
||||
ifShow: isPlatformAdmin,
|
||||
onClick: showDrugPriceModal.bind(null, row),
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -117,7 +117,7 @@ export async function updateStoreSubscribeStatus(data: { id: number }) {
|
||||
/**
|
||||
* 获取诊所药品列表(按类型)
|
||||
* @param storeId 诊所ID
|
||||
* @param type 药品类型(1=中药,2=西药)
|
||||
* @param type 商品类型(1中药 2西药 3保健食品 5产品服务包 6非药品 7医疗器械)
|
||||
*/
|
||||
export async function getStoreDrugsByTypeApi(storeId: number, type: number) {
|
||||
return requestClient.get<any>(`${prefix}get-store-drugs-by-type`, {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { computed, nextTick, ref, watch } from 'vue';
|
||||
|
||||
import { useVbenModal } from '@vben/common-ui';
|
||||
|
||||
@@ -9,18 +9,18 @@ import { useVbenVxeGrid } from '#/adapter/vxe-table';
|
||||
|
||||
import {
|
||||
type DrugItem,
|
||||
getChinaMedicineGridOptions,
|
||||
getWesternMedicineGridOptions,
|
||||
handleSaveRow,
|
||||
type ProductTypeOption,
|
||||
fetchProductTypeOptions,
|
||||
getDrugPriceGridOptions,
|
||||
handleBatchSave,
|
||||
handleSaveRow,
|
||||
TCM_PLACEHOLDER_IMAGE,
|
||||
} from '../config/drug-price-table';
|
||||
import { drugPriceFormOptions } from '../config/search';
|
||||
|
||||
const activeTab = ref<string>('1'); // 1=中药, 2=西药
|
||||
const activeTab = ref<string>('');
|
||||
const storeId = ref<number>(0);
|
||||
const loading = ref(false);
|
||||
const chinaGridApi = ref<any>(null);
|
||||
const westernGridApi = ref<any>(null);
|
||||
const productTypeOptions = ref<ProductTypeOption[]>([]);
|
||||
|
||||
const [Modal, modalApi] = useVbenModal({
|
||||
fullscreenButton: false,
|
||||
@@ -30,136 +30,89 @@ const [Modal, modalApi] = useVbenModal({
|
||||
modalApi.close();
|
||||
},
|
||||
onConfirm: async () => {
|
||||
// 批量保存:保存当前激活tab的表格中所有修改的数据
|
||||
if (activeTab.value === '1') {
|
||||
await handleBatchSave(chinaGridApi.value, storeId.value);
|
||||
} else {
|
||||
await handleBatchSave(westernGridApi.value, storeId.value);
|
||||
}
|
||||
await handleBatchSave(drugGridApi, storeId.value);
|
||||
},
|
||||
onOpenChange(isOpen: boolean) {
|
||||
onOpenChange: async (isOpen: boolean) => {
|
||||
if (isOpen) {
|
||||
const data = modalApi.getData<{ store_id: number }>();
|
||||
if (data?.store_id) {
|
||||
storeId.value = data.store_id;
|
||||
// 使用proxyConfig自动加载数据,不需要手动调用
|
||||
}
|
||||
productTypeOptions.value = await fetchProductTypeOptions();
|
||||
activeTab.value = String(productTypeOptions.value[0]?.value ?? '1');
|
||||
await nextTick();
|
||||
drugGridApi?.reload?.();
|
||||
} else {
|
||||
// 重置数据
|
||||
activeTab.value = '1';
|
||||
activeTab.value = '';
|
||||
storeId.value = 0;
|
||||
productTypeOptions.value = [];
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// 单行保存回调函数
|
||||
const handleSaveChinaRow = async (row: DrugItem) => {
|
||||
await handleSaveRow(row, storeId.value, chinaGridApi.value);
|
||||
const handleSaveCurrentRow = async (row: DrugItem) => {
|
||||
await handleSaveRow(row, storeId.value, drugGridApi);
|
||||
};
|
||||
|
||||
const handleSaveWesternRow = async (row: DrugItem) => {
|
||||
await handleSaveRow(row, storeId.value, westernGridApi.value);
|
||||
};
|
||||
const activeProductType = computed(() => Number(activeTab.value) || 1);
|
||||
|
||||
// 中药表格配置 - 使用computed包装响应式数据,传入storeId
|
||||
const chinaMedicineGridOptions = computed(() =>
|
||||
getChinaMedicineGridOptions(storeId.value, handleSaveChinaRow),
|
||||
const drugPriceGridOptions = computed(() =>
|
||||
getDrugPriceGridOptions(
|
||||
() => storeId.value,
|
||||
() => activeProductType.value,
|
||||
handleSaveCurrentRow,
|
||||
),
|
||||
);
|
||||
|
||||
// 西药表格配置 - 使用computed包装响应式数据,传入storeId
|
||||
const westernMedicineGridOptions = computed(() =>
|
||||
getWesternMedicineGridOptions(storeId.value, handleSaveWesternRow),
|
||||
);
|
||||
|
||||
const [ChinaGrid, chinaGridApiInstance] = useVbenVxeGrid({
|
||||
const [DrugGrid, drugGridApi] = useVbenVxeGrid({
|
||||
formOptions: drugPriceFormOptions,
|
||||
gridOptions: chinaMedicineGridOptions,
|
||||
gridOptions: drugPriceGridOptions,
|
||||
});
|
||||
|
||||
const [WesternGrid, westernGridApiInstance] = useVbenVxeGrid({
|
||||
formOptions: drugPriceFormOptions,
|
||||
gridOptions: westernMedicineGridOptions,
|
||||
watch(activeTab, async (tab, prev) => {
|
||||
if (!storeId.value || !tab || !prev) {
|
||||
return;
|
||||
}
|
||||
await nextTick();
|
||||
drugGridApi?.reload?.();
|
||||
});
|
||||
|
||||
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);
|
||||
}
|
||||
await handleBatchSave(drugGridApi, storeId.value);
|
||||
};
|
||||
|
||||
// 监听Tab切换,刷新对应表格数据
|
||||
watch(activeTab, () => {
|
||||
// Tab切换时刷新对应表格的数据
|
||||
if (activeTab.value === '1') {
|
||||
chinaGridApi.value?.reload?.();
|
||||
} else {
|
||||
westernGridApi.value?.reload?.();
|
||||
}
|
||||
});
|
||||
const rowImageSrc = (row: DrugItem) => {
|
||||
return row.image || TCM_PLACEHOLDER_IMAGE;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Modal
|
||||
title="修改药品价格"
|
||||
class="w-[80%]"
|
||||
>
|
||||
<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>
|
||||
<TabPane
|
||||
v-for="option in productTypeOptions"
|
||||
:key="String(option.value)"
|
||||
:tab="option.label"
|
||||
/>
|
||||
</Tabs>
|
||||
<DrugGrid v-if="activeTab">
|
||||
<template #toolbar-buttons>
|
||||
<Button type="primary" @click="handleBatchSaveCurrent">
|
||||
批量保存
|
||||
</Button>
|
||||
</template>
|
||||
<template #drug-image="{ row }">
|
||||
<div class="div" style="width: 120px; height: 120px; overflow: scroll">
|
||||
<Image :src="rowImageSrc(row)" height="30" width="30" />
|
||||
</div>
|
||||
</template>
|
||||
<template #row-action="{ row }">
|
||||
<Button type="link" size="small" @click="handleSaveCurrentRow(row)">
|
||||
保存
|
||||
</Button>
|
||||
</template>
|
||||
</DrugGrid>
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -41,7 +41,8 @@ export const modalFormProps: VbenFormProps = {
|
||||
component: 'Avatar',
|
||||
fieldName: 'logo',
|
||||
label: 'LOGO',
|
||||
rules: 'required',
|
||||
defaultValue: 'https://xiaokang88.oss-cn-hangzhou.aliyuncs.com/xk_upload_20250108/1bb87d41d15fe27b500a4bfcde01bb0e.png',
|
||||
// rules: 'required',
|
||||
},
|
||||
{
|
||||
component: 'VbenInput',
|
||||
@@ -65,14 +66,14 @@ export const modalFormProps: VbenFormProps = {
|
||||
component: 'Avatar',
|
||||
fieldName: 'open_business_license',
|
||||
label: '营业执照',
|
||||
rules: 'required',
|
||||
// rules: 'required',
|
||||
formItemClass: 'col-span-6',
|
||||
},
|
||||
{
|
||||
component: 'Avatar',
|
||||
fieldName: 'business_license',
|
||||
label: '生产许可证',
|
||||
rules: 'required',
|
||||
// rules: 'required',
|
||||
formItemClass: 'col-span-6',
|
||||
},
|
||||
{
|
||||
@@ -80,7 +81,7 @@ export const modalFormProps: VbenFormProps = {
|
||||
fieldName: 'product_registration_certificate',
|
||||
label: '产品注册证',
|
||||
formItemClass: 'col-span-6',
|
||||
rules: 'required',
|
||||
// rules: 'required',
|
||||
},
|
||||
],
|
||||
showDefaultActions: false,
|
||||
|
||||
Reference in New Issue
Block a user