feat: 金方相关

This commit is contained in:
李琦
2026-08-08 14:53:02 +08:00
parent 7697af2619
commit c4e833a1c4
11 changed files with 872 additions and 109 deletions

View File

@@ -3,6 +3,9 @@ import { requestClient } from '#/api/request';
/** 金方库管理 API */
const prefix = 'golden-formula/';
/** 金方来源字典 API */
const sourcePrefix = 'golden-formula-source/';
export async function getGoldenFormulaList(data: any) {
return requestClient.get<any>(`${prefix}list`, { params: data });
}
@@ -23,7 +26,32 @@ export async function deleteGoldenFormula(data: Record<string, any>) {
return requestClient.post<any>(`${prefix}delete`, data);
}
/** Excel 解析后的行批量导入 */
export async function importGoldenFormula(data: { rows: Record<string, any>[] }) {
/** Excel 解析后的行批量导入(带全局金方来源 source_id) */
export async function importGoldenFormula(data: {
rows: Record<string, any>[];
source_id?: number;
}) {
return requestClient.post<any>(`${prefix}import`, data);
}
/**
* 药品存在性排查(全局中药饮片 type=1 + 别名表)
* 预览页打开时调用,返回存在的药名 + 不存在的药名
*/
export async function checkGoldenFormulaDrugs(data: { drug_names: string[] }) {
return requestClient.post<{ exists: string[]; missing: string[] }>(
`${prefix}check-drugs`,
data,
);
}
/** 金方来源字典 - 下拉选项 */
export async function getGoldenFormulaSourceOption() {
return requestClient.get<any[]>(`${sourcePrefix}option`);
}
/** 金方来源字典 - 新增来源(预览页输入框新增) */
export async function createGoldenFormulaSource(data: { name: string }) {
return requestClient.post<any>(`${sourcePrefix}create`, data);
}

View File

@@ -0,0 +1,359 @@
<script lang="ts" setup>
/**
* 金方导入预览弹窗
* - 解析后的行在这里表格预览
* - 顶部全局「金方来源」选择器:下拉选已有 + 输入新增,所有行共用同一 source_id
* - 打开后批量排查药品存在性(中药饮片 type=1 + 别名表),表格加「不存在的药品」列红字标记
* - 确认导入时 emit { rows, sourceId },sourceId 为空提示选择来源
*/
import { computed, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import {
Button,
Input,
Select,
Table,
Tag,
Tooltip,
message,
} from 'ant-design-vue';
import {
checkGoldenFormulaDrugs,
createGoldenFormulaSource,
getGoldenFormulaSourceOption,
} from '../api';
const emit = defineEmits<{
(e: 'confirm', payload: { rows: any[]; sourceId: number }): void;
}>();
// 预览的原始行(Excel 解析后)
const rows = ref<any[]>([]);
// 金方来源下拉选项
const sourceOptions = ref<{ label: string; value: number }[]>([]);
// 选中的金方来源 id
const sourceId = ref<number>(0);
// 新增来源输入框文案
const newSourceName = ref('');
// 来源下拉 loading
const sourceLoading = ref(false);
// 新增来源提交中
const creatingSource = ref(false);
// 药品排查:不存在的药名集合
const missingSet = ref<Set<string>>(new Set());
// 排查中
const checking = ref(false);
const [Modal, modalApi] = useVbenModal({
fullscreenButton: false,
draggable: true,
class: 'w-[90%]',
onCancel() {
modalApi.close();
},
onConfirm: async () => {
if (sourceId.value <= 0) {
message.warning('请选择金方来源');
return;
}
if (!rows.value.length) {
message.warning('没有可导入的数据');
return;
}
emit('confirm', { rows: rows.value, sourceId: sourceId.value });
modalApi.close();
},
async onOpenChange(isOpen: boolean) {
if (!isOpen) {
// 关闭时重置状态
rows.value = [];
sourceId.value = 0;
newSourceName.value = '';
missingSet.value = new Set();
return;
}
const data = modalApi.getData<{ rows?: any[] }>() || {};
rows.value = Array.isArray(data.rows) ? data.rows : [];
await loadSourceOptions();
// 打开后批量排查药品存在性
await runDrugCheck();
},
});
/** 加载金方来源下拉 */
async function loadSourceOptions() {
sourceLoading.value = true;
try {
const list = await getGoldenFormulaSourceOption();
sourceOptions.value = (Array.isArray(list) ? list : []).map((item: any) => ({
label: String(item.name || ''),
value: Number(item.id || 0),
}));
} catch (e: any) {
sourceOptions.value = [];
message.error(e?.message || '加载金方来源失败');
} finally {
sourceLoading.value = false;
}
}
/** 新增来源:成功后刷新下拉并选中 */
async function handleCreateSource() {
const name = newSourceName.value.trim();
if (!name) {
message.warning('请输入来源名称');
return;
}
// 已存在同名则直接选中
const exist = sourceOptions.value.find(
(o) => o.label === name || o.label.includes(name),
);
if (exist) {
sourceId.value = exist.value;
newSourceName.value = '';
message.success('已存在该来源,已自动选中');
return;
}
creatingSource.value = true;
try {
const res = await createGoldenFormulaSource({ name });
const newId = Number(res?.id || 0);
if (newId > 0) {
await loadSourceOptions();
sourceId.value = newId;
newSourceName.value = '';
message.success('来源新增成功');
}
} catch (e: any) {
message.error(e?.message || '新增来源失败');
} finally {
creatingSource.value = false;
}
}
/**
* 从所有行的 drugs_json 收集药名,去重后批量排查存在性
*/
async function runDrugCheck() {
if (!rows.value.length) {
missingSet.value = new Set();
return;
}
const nameSet = new Set<string>();
for (const row of rows.value) {
const raw = row?.drugs_json;
if (!raw) continue;
try {
const arr = typeof raw === 'string' ? JSON.parse(raw) : raw;
if (Array.isArray(arr)) {
for (const item of arr) {
const n = String(item?.name || item?.drug_name || '').trim();
if (n) nameSet.add(n);
}
}
} catch {
// JSON 非法则跳过该行
}
}
const names = Array.from(nameSet);
if (!names.length) {
missingSet.value = new Set();
return;
}
checking.value = true;
try {
const res = await checkGoldenFormulaDrugs({ drug_names: names });
const missing = Array.isArray(res?.missing) ? res.missing : [];
missingSet.value = new Set(missing);
} catch (e: any) {
// 排查失败不阻塞预览
missingSet.value = new Set();
} finally {
checking.value = false;
}
}
/** 计算单行的「不存在的药品」列表(该行药名 ∩ 缺失集合) */
function rowMissingDrugs(row: any): string[] {
const raw = row?.drugs_json;
if (!raw) return [];
let arr: any[] = [];
try {
const parsed = typeof raw === 'string' ? JSON.parse(raw) : raw;
if (Array.isArray(parsed)) arr = parsed;
} catch {
return [];
}
const result: string[] = [];
for (const item of arr) {
const n = String(item?.name || item?.drug_name || '').trim();
if (n && missingSet.value.has(n)) result.push(n);
}
return result;
}
/** 汇总:不存在的药品总数(用于顶部提示) */
const missingSummary = computed(() => Array.from(missingSet.value));
/**
* 判定单行是否异常处方
* - drugs_json 解析后是对象且含 error(如 {error:无数据})
* - 或规范化后药味数为 0(空数组 / 无法解析出有效药名)
*/
function isAbnormalRow(row: any): boolean {
const raw = row?.drugs_json;
if (raw === null || raw === undefined || raw === '') return true;
let parsed: any;
try {
parsed = typeof raw === 'string' ? JSON.parse(raw) : raw;
} catch {
return true;
}
if (!parsed || typeof parsed !== 'object') return true;
// 关联对象含 error:典型 {"error":"无数据"}
if (!Array.isArray(parsed) && Object.prototype.hasOwnProperty.call(parsed, 'error')) {
return true;
}
const list = Array.isArray(parsed)
? parsed
: Array.isArray(parsed?.drugs)
? parsed.drugs
: Array.isArray(parsed?.items)
? parsed.items
: [];
// 药味数为 0
const count = list.filter(
(item: any) => String(item?.name || item?.drug_name || '').trim() !== '',
).length;
return count === 0;
}
/** 异常处方数量(顶部提示用) */
const abnormalCount = computed(
() => rows.value.filter((r) => isAbnormalRow(r)).length,
);
</script>
<template>
<Modal title="金方导入预览">
<div class="flex flex-col gap-3">
<!-- 顶部:金方来源全局选择器 + 新增 -->
<div class="flex flex-wrap items-center gap-2">
<span class="text-sm font-medium">金方来源:</span>
<Select
v-model:value="sourceId"
:options="sourceOptions"
:loading="sourceLoading"
show-search
option-filter-prop="label"
placeholder="选择已有金方来源"
style="min-width: 220px"
/>
<span class="text-xs text-muted-foreground">或新增:</span>
<Input
v-model:value="newSourceName"
placeholder="输入新来源名称"
style="width: 200px"
@press-enter="handleCreateSource"
/>
<Button
type="primary"
:loading="creatingSource"
size="small"
@click="handleCreateSource"
>
新增来源
</Button>
</div>
<!-- 异常处方提示(仅警告,不拦截) -->
<div
v-if="abnormalCount > 0"
class="rounded-lg border border-red-500/35 bg-red-500/10 px-3 py-2 text-xs"
>
<span class="font-medium">⚠ 异常处方:</span>
共
<Tag color="error" class="!mx-1">{{ abnormalCount }}</Tag>
条(药品JSON含 error 或药味数为 0),仍可导入,入库后标记为异常
</div>
<!-- 药品存在性排查提示(仅警告,不拦截) -->
<div
v-if="missingSummary.length"
class="rounded-lg border border-orange-500/35 bg-orange-500/10 px-3 py-2 text-xs"
>
<span class="font-medium">⚠ 药品排查:</span>
共发现
<Tag color="orange" class="!mx-1">{{ missingSummary.length }}</Tag>
个不存在的药品(仅提示,不拦截导入):
<span class="text-orange-700">{{ missingSummary.join('、') }}</span>
<span v-if="checking" class="ml-2 text-muted-foreground">排查中…</span>
</div>
<!-- 预览表格 -->
<Table
:columns="[
{ title: '序号', width: 60, customRender: ({ index }) => index + 1 },
{ title: '药方名字', dataIndex: 'name', width: 140 },
{ title: '条文编号', dataIndex: 'clause_number', width: 110 },
{ title: '状态', key: 'abnormal', width: 90 },
{
title: '药品JSON',
dataIndex: 'drugs_json',
ellipsis: true,
customRender: ({ text }) => String(text || ''),
},
{ title: '药材速览', dataIndex: 'herb_overview', width: 180, ellipsis: true },
{
title: '药方主治(文言文)',
dataIndex: 'indication_original',
width: 200,
ellipsis: true,
},
{
title: '现代主治(白话文)',
dataIndex: 'indication_translation',
width: 200,
ellipsis: true,
},
{ title: '拼音首拼', dataIndex: 'pinyin_initials', width: 100 },
{ title: '拼音全拼', dataIndex: 'pinyin', width: 120 },
{ title: '不存在的药品', key: 'missing', width: 180 },
]"
:data-source="rows"
:pagination="{ pageSize: 10, showSizeChanger: true }"
size="small"
:scroll="{ x: 1400 }"
:row-class-name="(record) => (isAbnormalRow(record) ? 'gf-abnormal-row' : '')"
row-key="name"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'abnormal'">
<Tag v-if="isAbnormalRow(record)" color="error" class="!m-0">异常</Tag>
<Tag v-else color="success" class="!m-0">正常</Tag>
</template>
<template v-else-if="column.key === 'missing'">
<Tooltip
v-if="rowMissingDrugs(record).length"
:title="rowMissingDrugs(record).join('、')"
>
<span class="text-red-500">
{{ rowMissingDrugs(record).join('、') }}
</span>
</Tooltip>
<Tag v-else color="success" class="!m-0">齐全</Tag>
</template>
</template>
</Table>
</div>
</Modal>
</template>
<style scoped>
/* 异常处方行:浅红底,暗色下用半透明红 */
:deep(.gf-abnormal-row) > td {
background-color: hsl(var(--destructive) / 0.08) !important;
color: hsl(var(--destructive));
}
</style>

View File

@@ -1,6 +1,7 @@
<script lang="ts" setup>
/**
* 金方新增/编辑弹窗
* 打开时拉取金方来源下拉,通过 updateSchema 注入 source_id 选项
*/
import { ref } from 'vue';
@@ -10,7 +11,11 @@ import { message } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form';
import { createGoldenFormula, updateGoldenFormula } from '../api';
import {
createGoldenFormula,
getGoldenFormulaSourceOption,
updateGoldenFormula,
} from '../api';
import { modalFormProps } from '../config/form';
const isUpdate = ref(false);
@@ -55,12 +60,28 @@ const [Modal, modalApi] = useVbenModal({
modalApi.setState({ confirmLoading: false });
}
},
onOpenChange(isOpen: boolean) {
async onOpenChange(isOpen: boolean) {
gridApi.value = isOpen ? modalApi.getData()?.gridApi : null;
if (!isOpen) {
formApi.resetForm();
return;
}
// 打开时拉取金方来源下拉选项并注入
try {
const list = await getGoldenFormulaSourceOption();
const options = (Array.isArray(list) ? list : []).map((item: any) => ({
label: String(item.name || ''),
value: Number(item.id || 0),
}));
formApi.updateSchema([
{
fieldName: 'source_id',
componentProps: { options },
},
]);
} catch {
// 拉取失败不阻塞表单
}
const { values, update } = modalApi.getData<Record<string, any>>() || {};
isUpdate.value = !!update;
if (values && update) {
@@ -76,7 +97,7 @@ const [Modal, modalApi] = useVbenModal({
});
} else {
formApi.resetForm();
formApi.setValues({ sort: 0, status: 1, drugs_json: '' });
formApi.setValues({ sort: 0, status: 1, drugs_json: '', source_id: 0 });
}
},
});

View File

@@ -26,10 +26,32 @@ export const modalFormProps: VbenFormProps = {
},
{
component: 'VbenInput',
fieldName: 'source',
fieldName: 'clause_number',
label: '条文编号',
help: '典籍条文编号,如「第12条」,可空',
componentProps: { placeholder: '如:第12条' },
defaultValue: '',
},
{
component: 'VbenSelect',
fieldName: 'source_id',
label: '金方来源',
help: '选择来源字典;如无合适项可先在「金方来源管理」新增',
componentProps: {
showSearch: true,
optionFilterProp: 'label',
options: [],
allowClear: true,
placeholder: '选择金方来源',
},
defaultValue: 0,
},
{
component: 'VbenInput',
fieldName: 'source',
label: '来源(旧文本)',
formItemClass: 'col-span-6',
componentProps: { placeholder: '如:伤寒论' },
componentProps: { placeholder: '如:伤寒论(旧字段,优先用上面的来源选择)' },
defaultValue: '',
},
{

View File

@@ -21,6 +21,7 @@ import {
importGoldenFormula,
} from './api';
import FormModal from './components/modal.vue';
import ImportPreviewModal from './components/ImportPreviewModal.vue';
import { STATUS_OPTIONS } from './config/constants';
import { exportGoldenFormulaTemplate } from './utils/exportGoldenFormulaExcel';
import { parseGoldenFormulaExcelBuffer } from './utils/parseGoldenFormulaExcel';
@@ -51,6 +52,18 @@ const [Grid, gridApi] = useVbenVxeGrid({
label: '状态',
componentProps: { allowClear: true, options: STATUS_OPTIONS },
},
{
component: 'VbenSelect',
fieldName: 'is_abnormal',
label: '异常标记',
componentProps: {
allowClear: true,
options: [
{ label: '正常', value: 0 },
{ label: '异常', value: 1 },
],
},
},
],
},
gridOptions: {
@@ -58,10 +71,27 @@ const [Grid, gridApi] = useVbenVxeGrid({
columns: [
{ type: 'checkbox', width: 60 },
{ field: 'id', title: 'ID', width: 80 },
{ field: 'name', title: '药方名字', minWidth: 140 },
{ field: 'source', title: '来源', width: 120 },
{
field: 'name',
title: '药方名字',
minWidth: 140,
slots: { default: 'name' },
},
{ field: 'clause_number', title: '条文编号', width: 110 },
{
field: 'source_name',
title: '来源',
width: 120,
slots: { default: 'source_name' },
},
{ field: 'herb_overview', title: '药材速览', minWidth: 180 },
{ field: 'drug_count', title: '药味数', width: 80 },
{
field: 'is_abnormal',
title: '异常',
width: 90,
slots: { default: 'is_abnormal' },
},
{ field: 'pinyin_initials', title: '首拼', width: 100 },
{ field: 'status', title: '状态', width: 90, slots: { default: 'status' } },
{ field: 'sort', title: '排序', width: 80 },
@@ -104,6 +134,11 @@ const [FormModalComp, formModalApi] = useVbenModal({
connectedComponent: FormModal,
});
/** 导入预览弹窗:解析后预览 + 选金方来源 + 确认导入 */
const [ImportPreviewModalComp, importPreviewApi] = useVbenModal({
connectedComponent: ImportPreviewModal,
});
const showModal = (data: any = {}, isUpdate = false) => {
formModalApi.setData({
values: data,
@@ -144,7 +179,27 @@ async function handleImportFile(file: File) {
);
return false;
}
const res = await importGoldenFormula({ rows: parsed.items });
// 打开预览弹窗:用户选金方来源 + 确认后再提交
importPreviewApi.setData({ rows: parsed.items });
importPreviewApi.open();
} catch (e: any) {
message.error(e?.message || '解析失败');
} finally {
importing.value = false;
}
return false;
}
/**
* 预览弹窗确认后:带 source_id 提交导入,弹结果框
*/
async function handleConfirmImport(payload: { rows: any[]; sourceId: number }) {
importing.value = true;
try {
const res = await importGoldenFormula({
rows: payload.rows,
source_id: payload.sourceId,
});
const ok = Number(res?.success || 0);
const fail = Number(res?.fail || 0);
const errors = Array.isArray(res?.errors) ? res.errors : [];
@@ -161,12 +216,12 @@ async function handleImportFile(file: File) {
} finally {
importing.value = false;
}
return false;
}
</script>
<template>
<Page auto-content-height title="金方管理">
<FormModalComp />
<ImportPreviewModalComp @confirm="handleConfirmImport" />
<Grid>
<template #toolbar-buttons>
<TableAction
@@ -203,6 +258,19 @@ async function handleImportFile(file: File) {
<Button :loading="importing" type="default">导入 Excel</Button>
</Upload>
</template>
<!-- 异常处方:药方名字整列标红 -->
<template #name="{ row }">
<span :class="{ 'gf-name-abnormal': row.is_abnormal === 1 }">
{{ row.name }}
</span>
</template>
<template #source_name="{ row }">
{{ row.source_name || row.source || '—' }}
</template>
<template #is_abnormal="{ row }">
<Tag v-if="row.is_abnormal === 1" color="error">异常</Tag>
<Tag v-else color="success">正常</Tag>
</template>
<template #status="{ row }">
<Tag :color="row.status === 1 ? 'success' : 'default'">
{{ row.status === 1 ? '启用' : '禁用' }}
@@ -229,3 +297,10 @@ async function handleImportFile(file: File) {
</Grid>
</Page>
</template>
<style scoped>
/* 异常处方药方名字:用主题 destructive 色,暗色下可读 */
.gf-name-abnormal {
color: hsl(var(--destructive));
font-weight: 600;
}
</style>

View File

@@ -8,17 +8,16 @@ const THIN_BORDER: Partial<ExcelJS.Borders> = {
};
const HEADERS = [
'序号',
'药方名字',
'条文编号',
'药品JSON',
'药材速览',
'原方',
'药方主治(原文)',
'药方主治(译文)',
'简介',
'封面',
'药方拼音首拼',
'药方拼音全拼',
'金方来源',
'药方主治(文言文)',
'现代主治(白话文)',
'拼音首拼',
'拼音全拼',
] as const;
/**
@@ -28,17 +27,16 @@ export async function exportGoldenFormulaTemplate(): Promise<ArrayBuffer> {
const workbook = new ExcelJS.Workbook();
const sheet = workbook.addWorksheet('金方导入');
sheet.columns = [
{ width: 14 },
{ width: 52 },
{ width: 24 },
{ width: 48 },
{ width: 24 },
{ width: 24 },
{ width: 20 },
{ width: 20 },
{ width: 12 },
{ width: 16 },
{ width: 12 },
{ width: 8 }, // 序号
{ width: 14 }, // 药方名字
{ width: 14 }, // 条文编号
{ width: 52 }, // 药品JSON
{ width: 24 }, // 药材速览
{ width: 48 }, // 原方
{ width: 28 }, // 药方主治(文言文)
{ width: 28 }, // 现代主治(白话文)
{ width: 12 }, // 拼音首拼
{ width: 16 }, // 拼音全拼
];
const headerRow = sheet.addRow([...HEADERS]);
headerRow.height = 28;
@@ -58,17 +56,16 @@ export async function exportGoldenFormulaTemplate(): Promise<ArrayBuffer> {
const sampleOriginal =
'桂枝三两,去皮 芍药三两 甘草二两,炙 生姜三两,切 大枣十二枚,擘。右五味,㕮咀三味,以水七升,微火煮取三升,去滓,适寒温,服一升。服已须臾,啜热稀粥一升余,以助药力。禁生冷、粘滑、肉面、五辛、酒酪、臭恶等物。';
const sample = sheet.addRow([
'桂枝汤',
sampleDrugs,
'桂枝、芍药、甘草、生姜、大枣',
sampleOriginal,
'太阳中风,阳浮而阴弱',
'外感风寒表虚证',
'解肌发表,调和营卫',
'',
'gzt',
'guizhitang',
'伤寒论',
1, // 序号
'桂枝汤', // 药方名字
'第12条', // 条文编号
sampleDrugs, // 药品JSON
'桂枝、芍药、甘草、生姜、大枣', // 药材速览
sampleOriginal, // 原方
'太阳中风,阳浮而阴弱', // 药方主治(文言文)
'外感风寒表虚证', // 现代主治(白话文)
'gzt', // 拼音首拼
'guizhitang', // 拼音全拼
]);
sample.height = 80;
sample.eachCell((cell) => {
@@ -90,7 +87,9 @@ export async function exportGoldenFormulaTemplate(): Promise<ArrayBuffer> {
cell.border = THIN_BORDER;
});
const tips: [string, string][] = [
['序号', '仅作行号,不导入;可空'],
['药方名字', '必填'],
['条文编号', '典籍条文编号,如「第12条」,可空'],
[
'药品JSON',
'系统字段(导入用):name/dose(克数数字)/unit(固定g)/usage(先煎、后下等,可空)。展示扩展(仅展示):ancient_dose(如三两、十二枚)、prep(去皮、炙、切、擘)。勿把古代单位或炮制写进 dose/unit/usage。',
@@ -100,13 +99,10 @@ export async function exportGoldenFormulaTemplate(): Promise<ArrayBuffer> {
'原方',
'典籍药味+煮法+服法+禁忌全文,如「桂枝三两,去皮…服一升…禁生冷…」',
],
['药方主治(原文)', '主治典籍原文'],
['药方主治(译文)', '主治白话译文'],
['简介', '方义/说明'],
['封面', '封面图完整 URL,可空'],
['药方拼音首拼', '可空,系统按药方名自动生成'],
['药方拼音全拼', '可空,系统按药方名自动生成'],
['金方来源', '如伤寒论、金匮要略'],
['药方主治(文言文)', '主治典籍原文'],
['现代主治(白话文)', '主治白话译文'],
['拼音首拼', '可空,系统按药方名自动生成'],
['拼音全拼', '可空,系统按药方名自动生成'],
];
tips.forEach(([k, v]) => {
const row = tip.addRow([k, v]);

View File

@@ -4,16 +4,14 @@ const MAX_ROWS = 2000;
export type ParsedGoldenFormulaRow = {
name: string;
clause_number: string;
drugs_json: string;
herb_overview: string;
original_formula: string;
indication_original: string;
indication_translation: string;
intro: string;
cover: string;
pinyin_initials: string;
pinyin: string;
source: string;
status: number;
sort: number;
};
@@ -51,16 +49,31 @@ function normalizeHeader(value: string): string {
}
const NAME_ALIASES = ['药方名字', '药方名称', '方名', 'name'];
const CLAUSE_ALIASES = ['条文编号', '条文', '条款', 'clause_number'];
const DRUGS_ALIASES = ['药品JSON', '药品json', '药品', 'drugs_json', 'drugs'];
const HERB_ALIASES = ['药材速览', '速览', 'herb_overview'];
const ORIGINAL_ALIASES = ['原方', '原方全文', 'original_formula', '方文'];
const IND_ORI_ALIASES = ['药方主治(原文)', '药方主治原文', '主治原文', 'indication_original'];
const IND_TR_ALIASES = ['药方主治(译文)', '药方主治译文', '主治译文', 'indication_translation'];
const INTRO_ALIASES = ['简介', 'intro'];
const COVER_ALIASES = ['封面', '封面图', 'cover'];
const PY_ABBR_ALIASES = ['药方拼音首拼', '拼音首拼', '首拼', 'pinyin_initials'];
const PY_FULL_ALIASES = ['药方拼音全拼', '拼音全拼', '全拼', 'pinyin'];
const SOURCE_ALIASES = ['金方来源', '来源', 'source'];
const IND_ORI_ALIASES = [
'药方主治(文言文)',
'药方主治(文言文)',
'药方主治原文',
'主治文言文',
'文言文',
'indication_original',
];
const IND_TR_ALIASES = [
'现代主治(白话文)',
'现代主治(白话文)',
'现代主治',
'白话文',
'药方主治(译文)',
'药方主治(译文)',
'药方主治译文',
'主治译文',
'indication_translation',
];
const PY_ABBR_ALIASES = ['拼音首拼', '药方拼音首拼', '首拼', 'pinyin_initials'];
const PY_FULL_ALIASES = ['拼音全拼', '药方拼音全拼', '全拼', 'pinyin'];
const STATUS_ALIASES = ['状态', 'status'];
const SORT_ALIASES = ['排序', 'sort'];
@@ -129,16 +142,14 @@ export async function parseGoldenFormulaExcelBuffer(
if (!nameCol) {
throw new Error('模板表头缺少「药方名字」列,请重新下载模板');
}
const clauseCol = pickColumn(headerMap, CLAUSE_ALIASES);
const drugsCol = pickColumn(headerMap, DRUGS_ALIASES);
const herbCol = pickColumn(headerMap, HERB_ALIASES);
const originalCol = pickColumn(headerMap, ORIGINAL_ALIASES);
const indOriCol = pickColumn(headerMap, IND_ORI_ALIASES);
const indTrCol = pickColumn(headerMap, IND_TR_ALIASES);
const introCol = pickColumn(headerMap, INTRO_ALIASES);
const coverCol = pickColumn(headerMap, COVER_ALIASES);
const pyAbbrCol = pickColumn(headerMap, PY_ABBR_ALIASES);
const pyFullCol = pickColumn(headerMap, PY_FULL_ALIASES);
const sourceCol = pickColumn(headerMap, SOURCE_ALIASES);
const statusCol = pickColumn(headerMap, STATUS_ALIASES);
const sortCol = pickColumn(headerMap, SORT_ALIASES);
const items: ParsedGoldenFormulaRow[] = [];
@@ -160,16 +171,14 @@ export async function parseGoldenFormulaExcelBuffer(
const statusRaw = getCellInt(row, statusCol, 1);
items.push({
name,
clause_number: getCellText(row, clauseCol),
drugs_json: getCellText(row, drugsCol),
herb_overview: getCellText(row, herbCol),
original_formula: getCellText(row, originalCol),
indication_original: getCellText(row, indOriCol),
indication_translation: getCellText(row, indTrCol),
intro: getCellText(row, introCol),
cover: getCellText(row, coverCol),
pinyin_initials: getCellText(row, pyAbbrCol),
pinyin: getCellText(row, pyFullCol),
source: getCellText(row, sourceCol),
status: statusRaw === 0 ? 0 : 1,
sort: getCellInt(row, sortCol, 0),
});