diff --git a/admin/src/components/tcm-prescription/index.vue b/admin/src/components/tcm-prescription/index.vue index 06028b65..ec7ae650 100644 --- a/admin/src/components/tcm-prescription/index.vue +++ b/admin/src/components/tcm-prescription/index.vue @@ -75,12 +75,37 @@ -
+ 粘贴整张药方文本,系统按「药名 + 剂量」解析;药名须与药品库中名称完全一致才会录入,否则跳过。支持顿号/逗号/换行分隔;单行可用空格分味(如「黄芪15g + 白术10g」);「黄芪、党参各10克」等同剂量写法亦可识别。 +
+
通过:处方保持有效。驳回:将同时作废此处方(与诊间「驳回/作废处方」一致)。
@@ -1401,7 +1445,7 @@ import {
getDoctors,
prescriptionLibraryLists
} from '@/api/tcm'
-import { searchPatients as searchPatientsAPI } from '@/api/order'
+import { medicineLists } from '@/api/medicine'
import { getDictData } from '@/api/app'
import { roleAll } from '@/api/perms/role'
import { usePaging } from '@/hooks/usePaging'
@@ -1410,7 +1454,7 @@ import feedback from '@/utils/feedback'
import type { FormInstance, FormRules } from 'element-plus'
import DaterangePicker from '@/components/daterange-picker/index.vue'
import MedicineNameSelect from '@/components/medicine-name-select/index.vue'
-import { Search, Download, Printer } from '@element-plus/icons-vue'
+import { Search, Download, Printer, EditPen } from '@element-plus/icons-vue'
import { computed, onMounted, reactive, ref, watch, nextTick, defineAsyncComponent } from 'vue'
import { useRouter } from 'vue-router'
import html2canvas from 'html2canvas'
@@ -2971,6 +3015,177 @@ watch(showLibraryDialog, (open) => {
}
})
+/** 粘贴药方导入(与 components/tcm-prescription 一致) */
+const showPasteRecipeDialog = ref(false)
+const pasteRecipeText = ref('')
+const pasteRecipeImportMode = ref<'replace' | 'append'>('replace')
+const pasteRecipeImportLoading = ref(false)
+
+const PASTE_RECIPE_SKIP_SEGMENT =
+ /^(用法|用量|医嘱|忌口|水煎服|空腹|饭后|温水|内服|外用|备注|顿服|分服|ml|毫升|[×xX]\s*\d)/
+
+function normalizeRecipePasteInput(raw: string): string {
+ let s = raw.replace(/\r\n/g, '\n').replace(/\r/g, '\n')
+ s = s.replace(/[0-9]/g, (ch) => String.fromCharCode(ch.charCodeAt(0) - 0xff10 + 48))
+ return s.trim()
+}
+
+function stripRecipePasteHeaders(text: string): string {
+ return text
+ .replace(/^\s*Rp[::**\s]*/i, '')
+ .replace(/^\s*处方[::\s]*/, '')
+ .replace(/^\s*中药处方[::\s]*/, '')
+ .trimStart()
+}
+
+function stripHerbPasteNote(name: string): string {
+ return name
+ .replace(/([^)]*)$/u, '')
+ .replace(/\([^)]*\)$/u, '')
+ .trim()
+}
+
+function parseHerbPasteToken(seg: string): { name: string; dosage: number }[] | null {
+ const s = seg.trim()
+ if (!s || PASTE_RECIPE_SKIP_SEGMENT.test(s)) return null
+ if (/^\d/.test(s)) return null
+ if (/^\d+\s*剂$/.test(s)) return null
+
+ const ge = s.match(/^(.+)\s*各\s*(\d+(?:\.\d+)?)\s*(?:克|g|G)?$/i)
+ if (ge) {
+ const dose = parseFloat(ge[2])
+ if (!Number.isFinite(dose) || dose <= 0) return null
+ const names = ge[1]
+ .split(/[、,,\s]+/)
+ .map((x) => x.trim())
+ .filter(Boolean)
+ if (!names.length) return null
+ return names.map((name) => ({ name: stripHerbPasteNote(name), dosage: dose }))
+ }
+
+ const m = s.match(/^(.+?)\s*(\d+(?:\.\d+)?)\s*(?:克|g|G|钱)?$/i)
+ if (m) {
+ const name = stripHerbPasteNote(m[1].trim())
+ const dose = parseFloat(m[2])
+ if (!name || !Number.isFinite(dose) || dose <= 0) return null
+ return [{ name, dosage: dose }]
+ }
+
+ const nameOnly = stripHerbPasteNote(s)
+ if (nameOnly.length >= 2 && nameOnly.length <= 16) {
+ return [{ name: nameOnly, dosage: 6 }]
+ }
+ return null
+}
+
+function chunkRecipePasteText(text: string): string[] {
+ const t = stripRecipePasteHeaders(normalizeRecipePasteInput(text))
+ const out: string[] = []
+ for (const line of t.split(/\n+/)) {
+ const ln = line.trim()
+ if (!ln) continue
+ for (const piece of ln.split(/[、,,;;]/)) {
+ const p = piece.trim()
+ if (!p) continue
+ if (/\s/.test(p) && !parseHerbPasteToken(p)) {
+ const sub = p.split(/\s+(?=[\u4e00-\u9fff]{2,})/)
+ for (const x of sub) {
+ const c = x.trim()
+ if (c) out.push(c)
+ }
+ } else {
+ out.push(p)
+ }
+ }
+ }
+ return out
+}
+
+function parseRecipePasteToHerbs(text: string): { name: string; dosage: number }[] {
+ const herbs: { name: string; dosage: number }[] = []
+ for (const c of chunkRecipePasteText(text)) {
+ const parsed = parseHerbPasteToken(c)
+ if (!parsed) continue
+ herbs.push(...parsed)
+ }
+ return herbs
+}
+
+async function resolvePasteHerbNameFromLibrary(rawName: string): Promise