|
|
@@ -1,6 +1,6 @@
|
|
|
import type { TransformList, TransformRecord } from '#/api';
|
|
|
|
|
|
-/** 点评专家(接口就绪后替换为真实请求) */
|
|
|
+/** 处方点评(接口就绪后替换为真实请求) */
|
|
|
export namespace PrescriptionReviewModel {
|
|
|
export interface ReviewExpert extends TransformRecord {
|
|
|
access: string;
|
|
|
@@ -12,6 +12,29 @@ export namespace PrescriptionReviewModel {
|
|
|
institutionId?: string;
|
|
|
pid?: string;
|
|
|
}
|
|
|
+
|
|
|
+ /** 点评指标来源:系统内置 / 用户新增 */
|
|
|
+ export type ReviewIndicatorSource = 'custom' | 'system';
|
|
|
+
|
|
|
+ export interface ReviewIndicatorCategory extends TransformRecord {
|
|
|
+ name: string;
|
|
|
+ /** 系统内置分类不可删除 */
|
|
|
+ source: ReviewIndicatorSource;
|
|
|
+ /** 分类下点评项数量 */
|
|
|
+ indicatorCount?: number;
|
|
|
+ }
|
|
|
+
|
|
|
+ export interface ReviewIndicator extends TransformRecord {
|
|
|
+ categoryId: string;
|
|
|
+ categoryName: string;
|
|
|
+ name: string;
|
|
|
+ source: ReviewIndicatorSource;
|
|
|
+ /** 是否关联中药 */
|
|
|
+ associatedChineseMedicine: boolean;
|
|
|
+ remark?: string;
|
|
|
+ /** 0 启用,1 禁用 */
|
|
|
+ status: 0 | 1;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
const MOCK_EXPERTS: PrescriptionReviewModel.ReviewExpert[] = [
|
|
|
@@ -166,3 +189,383 @@ export function updateReviewExpertStatusMethod(
|
|
|
expert.status = status;
|
|
|
return Promise.resolve(true);
|
|
|
}
|
|
|
+
|
|
|
+const MOCK_INDICATOR_CATEGORIES: PrescriptionReviewModel.ReviewIndicatorCategory[] =
|
|
|
+ [
|
|
|
+ { id: 'cat-1', name: '适应症', source: 'system' },
|
|
|
+ { id: 'cat-2', name: '用法用量', source: 'system' },
|
|
|
+ { id: 'cat-3', name: '配伍禁忌', source: 'system' },
|
|
|
+ { id: 'cat-4', name: '特殊人群禁忌', source: 'system' },
|
|
|
+ { id: 'cat-5', name: '其他禁忌', source: 'system' },
|
|
|
+ ];
|
|
|
+
|
|
|
+const MOCK_INDICATORS: PrescriptionReviewModel.ReviewIndicator[] = [
|
|
|
+ {
|
|
|
+ id: 'ind-1',
|
|
|
+ categoryId: 'cat-1',
|
|
|
+ categoryName: '适应症',
|
|
|
+ name: '病证禁忌',
|
|
|
+ source: 'system',
|
|
|
+ associatedChineseMedicine: false,
|
|
|
+ status: 1,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'ind-2',
|
|
|
+ categoryId: 'cat-1',
|
|
|
+ categoryName: '适应症',
|
|
|
+ name: '慎用',
|
|
|
+ source: 'system',
|
|
|
+ associatedChineseMedicine: false,
|
|
|
+ status: 1,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'ind-3',
|
|
|
+ categoryId: 'cat-1',
|
|
|
+ categoryName: '适应症',
|
|
|
+ name: '禁用',
|
|
|
+ source: 'system',
|
|
|
+ associatedChineseMedicine: false,
|
|
|
+ status: 1,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'ind-4',
|
|
|
+ categoryId: 'cat-1',
|
|
|
+ categoryName: '适应症',
|
|
|
+ name: '忌用',
|
|
|
+ source: 'system',
|
|
|
+ associatedChineseMedicine: false,
|
|
|
+ status: 1,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'ind-5',
|
|
|
+ categoryId: 'cat-2',
|
|
|
+ categoryName: '用法用量',
|
|
|
+ name: '超剂量用药',
|
|
|
+ source: 'system',
|
|
|
+ associatedChineseMedicine: false,
|
|
|
+ status: 1,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'ind-6',
|
|
|
+ categoryId: 'cat-3',
|
|
|
+ categoryName: '配伍禁忌',
|
|
|
+ name: '十八反十九畏',
|
|
|
+ source: 'system',
|
|
|
+ associatedChineseMedicine: true,
|
|
|
+ status: 0,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'ind-7',
|
|
|
+ categoryId: 'cat-1',
|
|
|
+ categoryName: '适应症',
|
|
|
+ name: '点评项1',
|
|
|
+ source: 'custom',
|
|
|
+ associatedChineseMedicine: true,
|
|
|
+ createUser: '陆长林',
|
|
|
+ createTime: '2023-09-23 15:29:38',
|
|
|
+ status: 0,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: 'ind-8',
|
|
|
+ categoryId: 'cat-1',
|
|
|
+ categoryName: '适应症',
|
|
|
+ name: '点评项2',
|
|
|
+ source: 'custom',
|
|
|
+ associatedChineseMedicine: true,
|
|
|
+ createUser: '陆长林',
|
|
|
+ createTime: '2023-09-23 15:29:38',
|
|
|
+ status: 0,
|
|
|
+ },
|
|
|
+];
|
|
|
+
|
|
|
+function getCategoryIndicatorCount(categoryId: string) {
|
|
|
+ return MOCK_INDICATORS.filter((item) => item.categoryId === categoryId).length;
|
|
|
+}
|
|
|
+
|
|
|
+function withCategoryIndicatorCount(
|
|
|
+ categories: PrescriptionReviewModel.ReviewIndicatorCategory[],
|
|
|
+) {
|
|
|
+ return categories.map((item) => ({
|
|
|
+ ...item,
|
|
|
+ indicatorCount: getCategoryIndicatorCount(item.id),
|
|
|
+ }));
|
|
|
+}
|
|
|
+
|
|
|
+function getNextCustomCategoryName() {
|
|
|
+ const prefix = '新加分类';
|
|
|
+ let index = 1;
|
|
|
+ while (
|
|
|
+ MOCK_INDICATOR_CATEGORIES.some((item) => item.name === `${prefix}${index}`)
|
|
|
+ ) {
|
|
|
+ index += 1;
|
|
|
+ }
|
|
|
+ return `${prefix}${index}`;
|
|
|
+}
|
|
|
+
|
|
|
+function syncIndicatorCategoryName(
|
|
|
+ indicator: PrescriptionReviewModel.ReviewIndicator,
|
|
|
+) {
|
|
|
+ const category = MOCK_INDICATOR_CATEGORIES.find(
|
|
|
+ (item) => item.id === indicator.categoryId,
|
|
|
+ );
|
|
|
+ indicator.categoryName = category?.name ?? indicator.categoryName;
|
|
|
+}
|
|
|
+
|
|
|
+function filterIndicators(
|
|
|
+ items: PrescriptionReviewModel.ReviewIndicator[],
|
|
|
+ query?: Partial<
|
|
|
+ PrescriptionReviewModel.ReviewIndicator & { categoryId?: string }
|
|
|
+ >,
|
|
|
+) {
|
|
|
+ let result = [...items];
|
|
|
+ if (query?.categoryId) {
|
|
|
+ result = result.filter((item) => item.categoryId === query.categoryId);
|
|
|
+ }
|
|
|
+ if (query?.name?.trim()) {
|
|
|
+ const keyword = query.name.trim();
|
|
|
+ result = result.filter((item) => item.name.includes(keyword));
|
|
|
+ }
|
|
|
+ return result;
|
|
|
+}
|
|
|
+
|
|
|
+/** 点评指标分类列表(当前为本地 mock) */
|
|
|
+export function listReviewIndicatorCategoriesMethod(): Promise<
|
|
|
+ PrescriptionReviewModel.ReviewIndicatorCategory[]
|
|
|
+> {
|
|
|
+ return Promise.resolve(withCategoryIndicatorCount([...MOCK_INDICATOR_CATEGORIES]));
|
|
|
+}
|
|
|
+
|
|
|
+/** 新增点评指标分类(当前为本地 mock) */
|
|
|
+export function createReviewIndicatorCategoryMethod(name?: string) {
|
|
|
+ const trimmed = (name ?? getNextCustomCategoryName()).trim();
|
|
|
+ if (!trimmed) {
|
|
|
+ return Promise.reject(new Error('分类名称不能为空'));
|
|
|
+ }
|
|
|
+ if (MOCK_INDICATOR_CATEGORIES.some((item) => item.name === trimmed)) {
|
|
|
+ return Promise.reject(new Error('分类名称已存在'));
|
|
|
+ }
|
|
|
+ const category: PrescriptionReviewModel.ReviewIndicatorCategory = {
|
|
|
+ id: `cat-${Date.now()}`,
|
|
|
+ name: trimmed,
|
|
|
+ source: 'custom',
|
|
|
+ indicatorCount: 0,
|
|
|
+ };
|
|
|
+ MOCK_INDICATOR_CATEGORIES.push(category);
|
|
|
+ return Promise.resolve(category);
|
|
|
+}
|
|
|
+
|
|
|
+/** 更新点评指标分类名称(当前为本地 mock,系统内置不可改) */
|
|
|
+export function updateReviewIndicatorCategoryMethod(
|
|
|
+ categoryId: string,
|
|
|
+ { name }: { name: string },
|
|
|
+) {
|
|
|
+ const category = MOCK_INDICATOR_CATEGORIES.find((item) => item.id === categoryId);
|
|
|
+ if (!category) {
|
|
|
+ return Promise.reject(new Error('分类不存在'));
|
|
|
+ }
|
|
|
+ if (category.source === 'system') {
|
|
|
+ return Promise.reject(new Error('系统内置分类不可修改'));
|
|
|
+ }
|
|
|
+ const trimmed = name.trim();
|
|
|
+ if (!trimmed) {
|
|
|
+ return Promise.reject(new Error('分类名称不能为空'));
|
|
|
+ }
|
|
|
+ if (
|
|
|
+ MOCK_INDICATOR_CATEGORIES.some(
|
|
|
+ (item) => item.id !== categoryId && item.name === trimmed,
|
|
|
+ )
|
|
|
+ ) {
|
|
|
+ return Promise.reject(new Error('分类名称已存在'));
|
|
|
+ }
|
|
|
+ category.name = trimmed;
|
|
|
+ MOCK_INDICATORS.forEach((item) => {
|
|
|
+ if (item.categoryId === categoryId) {
|
|
|
+ item.categoryName = trimmed;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return Promise.resolve({
|
|
|
+ ...category,
|
|
|
+ indicatorCount: getCategoryIndicatorCount(categoryId),
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+/** 点评指标分类排序(当前为本地 mock) */
|
|
|
+export function sortReviewIndicatorCategoriesMethod(orderedIds: string[]) {
|
|
|
+ if (orderedIds.length !== MOCK_INDICATOR_CATEGORIES.length) {
|
|
|
+ return Promise.reject(new Error('分类排序数据不完整'));
|
|
|
+ }
|
|
|
+ const categoryMap = new Map(
|
|
|
+ MOCK_INDICATOR_CATEGORIES.map((item) => [item.id, item]),
|
|
|
+ );
|
|
|
+ const sorted = orderedIds.map((id) => categoryMap.get(id)).filter(Boolean) as
|
|
|
+ PrescriptionReviewModel.ReviewIndicatorCategory[];
|
|
|
+ if (sorted.length !== MOCK_INDICATOR_CATEGORIES.length) {
|
|
|
+ return Promise.reject(new Error('分类排序数据无效'));
|
|
|
+ }
|
|
|
+ MOCK_INDICATOR_CATEGORIES.splice(0, MOCK_INDICATOR_CATEGORIES.length, ...sorted);
|
|
|
+ return Promise.resolve(withCategoryIndicatorCount([...MOCK_INDICATOR_CATEGORIES]));
|
|
|
+}
|
|
|
+
|
|
|
+/** 删除点评指标分类(当前为本地 mock,系统内置不可删) */
|
|
|
+export function deleteReviewIndicatorCategoryMethod(categoryId: string) {
|
|
|
+ const index = MOCK_INDICATOR_CATEGORIES.findIndex(
|
|
|
+ (item) => item.id === categoryId,
|
|
|
+ );
|
|
|
+ if (index === -1) {
|
|
|
+ return Promise.reject(new Error('分类不存在'));
|
|
|
+ }
|
|
|
+ const category = MOCK_INDICATOR_CATEGORIES[index];
|
|
|
+ if (!category) {
|
|
|
+ return Promise.reject(new Error('分类不存在'));
|
|
|
+ }
|
|
|
+ if (category.source === 'system') {
|
|
|
+ return Promise.reject(new Error('系统内置分类不可删除'));
|
|
|
+ }
|
|
|
+ const inUse = MOCK_INDICATORS.some((item) => item.categoryId === categoryId);
|
|
|
+ if (inUse) {
|
|
|
+ return Promise.reject(new Error('该分类下存在点评项,无法删除'));
|
|
|
+ }
|
|
|
+ MOCK_INDICATOR_CATEGORIES.splice(index, 1);
|
|
|
+ return Promise.resolve(true);
|
|
|
+}
|
|
|
+
|
|
|
+/** 按分类获取点评指标列表(当前为本地 mock) */
|
|
|
+export function listReviewIndicatorsByCategoryMethod(categoryId: string) {
|
|
|
+ const category = MOCK_INDICATOR_CATEGORIES.find((item) => item.id === categoryId);
|
|
|
+ if (!category) {
|
|
|
+ return Promise.reject(new Error('分类不存在'));
|
|
|
+ }
|
|
|
+ return Promise.resolve(
|
|
|
+ MOCK_INDICATORS.filter((item) => item.categoryId === categoryId),
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+/** 点评指标排序(当前为本地 mock,按分类内顺序) */
|
|
|
+export function sortReviewIndicatorsMethod(
|
|
|
+ categoryId: string,
|
|
|
+ orderedIds: string[],
|
|
|
+) {
|
|
|
+ const categoryIndicators = MOCK_INDICATORS.filter(
|
|
|
+ (item) => item.categoryId === categoryId,
|
|
|
+ );
|
|
|
+ if (orderedIds.length !== categoryIndicators.length) {
|
|
|
+ return Promise.reject(new Error('点评项排序数据不完整'));
|
|
|
+ }
|
|
|
+ const indicatorMap = new Map(categoryIndicators.map((item) => [item.id, item]));
|
|
|
+ const sorted = orderedIds.map((id) => indicatorMap.get(id)).filter(Boolean) as
|
|
|
+ PrescriptionReviewModel.ReviewIndicator[];
|
|
|
+ if (sorted.length !== categoryIndicators.length) {
|
|
|
+ return Promise.reject(new Error('点评项排序数据无效'));
|
|
|
+ }
|
|
|
+ const others = MOCK_INDICATORS.filter((item) => item.categoryId !== categoryId);
|
|
|
+ MOCK_INDICATORS.splice(0, MOCK_INDICATORS.length, ...others, ...sorted);
|
|
|
+ return Promise.resolve(sorted);
|
|
|
+}
|
|
|
+
|
|
|
+/** 点评指标列表(当前为本地 mock,后期对接后端分页接口) */
|
|
|
+export function listReviewIndicatorsMethod(
|
|
|
+ page = 1,
|
|
|
+ size = 10,
|
|
|
+ query?: Partial<
|
|
|
+ PrescriptionReviewModel.ReviewIndicator & { categoryId?: string }
|
|
|
+ >,
|
|
|
+): Promise<TransformList<PrescriptionReviewModel.ReviewIndicator>> {
|
|
|
+ const filtered = filterIndicators(MOCK_INDICATORS, query);
|
|
|
+ const start = (page - 1) * size;
|
|
|
+ const items = filtered.slice(start, start + size);
|
|
|
+ return Promise.resolve({
|
|
|
+ items,
|
|
|
+ total: filtered.length,
|
|
|
+ data: { page, size, total: filtered.length },
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+/** 新增点评指标(当前为本地 mock) */
|
|
|
+export function createReviewIndicatorMethod(
|
|
|
+ data: Pick<
|
|
|
+ PrescriptionReviewModel.ReviewIndicator,
|
|
|
+ 'categoryId' | 'name' | 'associatedChineseMedicine' | 'remark' | 'status'
|
|
|
+ >,
|
|
|
+) {
|
|
|
+ const category = MOCK_INDICATOR_CATEGORIES.find(
|
|
|
+ (item) => item.id === data.categoryId,
|
|
|
+ );
|
|
|
+ if (!category) {
|
|
|
+ return Promise.reject(new Error('点评项分类不存在'));
|
|
|
+ }
|
|
|
+ const indicator: PrescriptionReviewModel.ReviewIndicator = {
|
|
|
+ id: `ind-${Date.now()}`,
|
|
|
+ categoryId: data.categoryId,
|
|
|
+ categoryName: category.name,
|
|
|
+ name: data.name.trim(),
|
|
|
+ source: 'custom',
|
|
|
+ associatedChineseMedicine: data.associatedChineseMedicine,
|
|
|
+ remark: data.remark,
|
|
|
+ status: data.status ?? 0,
|
|
|
+ createUser: '陆长林',
|
|
|
+ createTime: new Date().toISOString().slice(0, 19).replace('T', ' '),
|
|
|
+ };
|
|
|
+ MOCK_INDICATORS.push(indicator);
|
|
|
+ return Promise.resolve(indicator);
|
|
|
+}
|
|
|
+
|
|
|
+/** 编辑点评指标(当前为本地 mock,系统内置仅可改部分字段) */
|
|
|
+export function updateReviewIndicatorMethod(
|
|
|
+ indicatorId: string,
|
|
|
+ data: Partial<
|
|
|
+ Pick<
|
|
|
+ PrescriptionReviewModel.ReviewIndicator,
|
|
|
+ | 'categoryId'
|
|
|
+ | 'name'
|
|
|
+ | 'associatedChineseMedicine'
|
|
|
+ | 'remark'
|
|
|
+ | 'status'
|
|
|
+ >
|
|
|
+ >,
|
|
|
+) {
|
|
|
+ const indicator = MOCK_INDICATORS.find((item) => item.id === indicatorId);
|
|
|
+ if (!indicator) {
|
|
|
+ return Promise.reject(new Error('点评项不存在'));
|
|
|
+ }
|
|
|
+ if (data.categoryId) {
|
|
|
+ indicator.categoryId = data.categoryId;
|
|
|
+ syncIndicatorCategoryName(indicator);
|
|
|
+ }
|
|
|
+ if (data.name !== undefined) indicator.name = data.name.trim();
|
|
|
+ if (data.associatedChineseMedicine !== undefined) {
|
|
|
+ indicator.associatedChineseMedicine = data.associatedChineseMedicine;
|
|
|
+ }
|
|
|
+ if (data.remark !== undefined) indicator.remark = data.remark;
|
|
|
+ if (data.status !== undefined) indicator.status = data.status;
|
|
|
+ return Promise.resolve(indicator);
|
|
|
+}
|
|
|
+
|
|
|
+/** 删除点评指标(当前为本地 mock,系统内置不可删) */
|
|
|
+export function deleteReviewIndicatorMethod(indicatorId: string) {
|
|
|
+ const index = MOCK_INDICATORS.findIndex((item) => item.id === indicatorId);
|
|
|
+ if (index === -1) {
|
|
|
+ return Promise.reject(new Error('点评项不存在'));
|
|
|
+ }
|
|
|
+ const indicator = MOCK_INDICATORS[index];
|
|
|
+ if (!indicator) {
|
|
|
+ return Promise.reject(new Error('点评项不存在'));
|
|
|
+ }
|
|
|
+ if (indicator.source === 'system') {
|
|
|
+ return Promise.reject(new Error('系统内置点评项不可删除'));
|
|
|
+ }
|
|
|
+ MOCK_INDICATORS.splice(index, 1);
|
|
|
+ return Promise.resolve(true);
|
|
|
+}
|
|
|
+
|
|
|
+/** 点评指标状态更改(当前为本地 mock,后期对接后端接口) */
|
|
|
+export function updateReviewIndicatorStatusMethod(
|
|
|
+ indicatorId: string,
|
|
|
+ { status }: { status: 0 | 1 },
|
|
|
+) {
|
|
|
+ const indicator = MOCK_INDICATORS.find((item) => item.id === indicatorId);
|
|
|
+ if (!indicator) {
|
|
|
+ return Promise.reject(new Error('点评项不存在'));
|
|
|
+ }
|
|
|
+ indicator.status = status;
|
|
|
+ return Promise.resolve(true);
|
|
|
+}
|