<template>
	<view class="western" :class="{ 'western--inspection': diagData && diagData.PHCombo }">
		<!-- 左树 右列表 双栏 -->
		<view class="ie-body">
			<!-- 左分类栏：搜索 + 递归多级分类树 -->
			<view class="ie-col ie-col--left">
				<view class="ie-tree-search">
					<input
						class="ie-tree-search__input"
						v-model="searchL"
						placeholder="搜索目录"
						confirm-type="search"
						@input="onTreeSearchInput"
						@confirm="filterTree"
					/>
					<text v-if="searchL" class="ie-tree-search__clear" @tap="clearTreeSearch">✕</text>
				</view>
				<scroll-view class="ie-scroll ie-scroll--tree" scroll-y>
					<tree-node
						v-for="(node, idx) in treeList"
						:key="node.type + '-' + (node.CBid || node.HBid || node.id) + '-' + idx"
						:node="node"
						:depth="0"
						:sibling-index="idx"
						:active-path="activePath"
						:search-key="searchL"
						@select="onTreeSelect"
					/>
					<view v-if="!treeList.length" class="ie-empty">暂无分类</view>
				</scroll-view>
			</view>

			<!-- 右数据栏：搜索 + 列表 -->
			<view class="ie-col ie-col--right">
				<view class="ie-search">
					<view class="ie-search__box">
						<input
							class="ie-search__input"
							v-model="searchR"
							placeholder="搜索名称"
							placeholder-class="ie-ph"
							confirm-type="search"
							@confirm="loadRightList"
						/>
						<text class="ie-search__btn" @tap="loadRightList">搜索</text>
					</view>
				</view>
				<view class="ie-right-head" v-if="rightNode && rightNode.visibleCheckAll">
					<text class="ie-right-head__info">
					</text>
					<!-- 检验检查可全选；治疗不显示全选；西药不显示全选 -->
					<view class="ie-right-head__all" @tap="toggleSelectAll">
						<text>全选</text>
						<view class="wa-checkbox" :class="{ 'wa-checkbox--on': isAllSelected }">
							<text v-if="isAllSelected" class="wa-checkbox__tick">✓</text>
						</view>
					</view>
				</view>
				<scroll-view class="ie-scroll" scroll-y>
				<!-- 药品项（点击整卡打开SKU弹窗） -->
				<view v-for="med in medList" :key="'med-' + med.medID" class="wa-card" @tap="toggleMed(med)">
					<view class="wa-card__main">
						<view class="wa-card__name">{{ med.medDesc || med.tradeName }}</view>
					<view class="wa-card__sub">
						<text v-if="med.medSubCatDesc" class="wa-card__tag">{{ med.medSubCatDesc }}</text>
					</view>
						<!-- <view class="wa-card__meta">
							<text class="wa-card__price">¥{{ med.medPrice || '0.00' }}</text>
							<text v-if="med.medStock !== undefined" class="wa-card__stock">库存 {{ med.medStock }}</text>
						</view> -->
					</view>
					<view class="wa-card__action" @tap.stop="toggleMed(med)">
						<view v-if="!medObjMap[med.medID]" class="wa-card__add">＋</view>
						<view v-else class="wa-stepper">
							<text class="wa-stepper__btn" @tap.stop="decMed(med)">−</text>
							<text class="wa-stepper__num">{{ medQtyMap[med.medID] || 1 }}</text>
							<text class="wa-stepper__btn" @tap.stop="incMed(med)">＋</text>
						</view>
					</view>
				</view>

				<!-- 检验检查/治疗项（item 级判定：orderTypeCode O=治疗 显示步进器，E/L=检查/检验 显示勾选框） -->
				<view
					v-for="(it, ii) in checkList"
					:key="it.medID || it.appOrderID || ii"
					class="wa-card"
					:class="isTreatItem(it) ? 'wa-card--treat' : 'wa-card--check'"
				>
					<view class="wa-card__main" @tap="onCheckTap(it)">
						<view class="wa-card__name">{{ it.medDesc || it.descripts }}</view>
						<view class="wa-card__sub">
							<text class="wa-card__tag wa-card__tag--blue">{{ checkTypeLabel(it.orderTypeCode) }}</text>
						</view>
						<view class="wa-card__meta">
							<text v-if="it.medPrice" class="wa-card__price">¥{{ it.medPrice }}</text>
						</view>
					</view>
					<!-- 治疗（O）：加减数量（数量 > 0 才显示减号和数量，否则仅显示加号） -->
					<view v-if="isTreatItem(it)" class="wa-card__action">
						<view v-if="!(treatQtyMap[it.medID || it.appOrderID] || 0)" class="wa-card__add" @tap.stop="incTreat(it)">＋</view>
						<view v-else class="wa-stepper">
							<text class="wa-stepper__btn" @tap="decTreat(it)">−</text>
							<text class="wa-stepper__num">{{ treatQtyMap[it.medID || it.appOrderID] || 0 }}</text>
							<text class="wa-stepper__btn" @tap="incTreat(it)">＋</text>
						</view>
					</view>
					<!-- 检查/检验（E/L）：勾选框 -->
					<view v-else class="wa-checkbox" :class="{ 'wa-checkbox--on': isChecked(it) }" @tap.stop="toggleCheck(it)">
						<text v-if="isChecked(it)" class="wa-checkbox__tick">✓</text>
					</view>
				</view>

				<view v-if="loadingRight" class="ie-empty">加载中…</view>
				<view v-if="!loadingRight && !medList.length && !checkList.length" class="ie-empty">请选择左侧分类</view>
				</scroll-view>
			</view>
		</view>

		<!-- 底部保存栏 -->
		<view class="ie-footer">
			<view class="ie-footer__info" @tap="openSelectedSheet">
				<text>已选 </text>
				<text class="ie-footer__num">{{ totalSelected }}</text>
				<text> 项</text>
			</view>
			<view class="ie-footer__btn" :class="{ 'ie-footer__btn--loading': saving, 'ie-footer__btn--disabled': !totalSelected }" @tap="onSaveClick">
				<view v-if="saving" class="ie-footer__loading" />
				<text>{{ saving ? '保存中…' : '保存' }}</text>
			</view>
		</view>

		<!-- 已选医嘱底部弹窗 -->
		<view v-if="showSelectedSheet" class="wa-mask wa-mask--top" @tap="closeSelectedSheet">
			<view class="wa-sheet" @tap.stop>
				<view class="wa-sheet__head">
					<text class="wa-sheet__title">已选医嘱 ({{ totalSelected }})</text>
					<text class="wa-sheet__close" @tap="closeSelectedSheet">✕</text>
				</view>
				<scroll-view class="wa-sheet__body" scroll-y>
					<view v-if="!selectedList.length" class="ie-empty">暂无已选医嘱</view>
					<view
						v-for="(it, idx) in selectedList"
						:key="it.kind + '-' + it.key + '-' + idx"
						class="sel-item"
					>
						<view class="sel-item__main">
							<view class="sel-item__name">
								<text>{{ it.title }}</text>
								<text class="sel-item__tag" :class="it.tagClass">{{ it.tag }}</text>
							</view>
							<view v-if="it.desc" class="sel-item__desc">{{ it.desc }}</view>
						</view>
						<view class="sel-item__action">
							<!-- 治疗：步进器 -->
							<view v-if="it.kind === 'treat'" class="wa-stepper">
								<text class="wa-stepper__btn" @tap.stop="decSelectedTreat(it)">−</text>
								<text class="wa-stepper__num">{{ it.qty }}</text>
								<text class="wa-stepper__btn" @tap.stop="incSelectedTreat(it)">＋</text>
							</view>
							<!-- 西药：修改 -->
							<text v-if="it.kind === 'med'" class="sel-item__edit" @tap.stop="editSelectedMed(it)">修改</text>
							<text class="sel-item__del" @tap.stop="removeSelected(it)">删除</text>
						</view>
					</view>
				</scroll-view>
				<!-- 弹窗底部：确认保存 -->
				<view class="wa-sheet__footer">
					<view
						class="wa-sheet__footer-btn"
						:class="{ 'wa-sheet__footer-btn--disabled': !totalSelected }"
						@tap="confirmSave"
					>
						<text>确认保存</text>
					</view>
				</view>
			</view>
		</view>

		<!-- 药品属性底部抽屉 -->
		<view v-if="showSheet" class="wa-mask" @tap="closeSheet">
			<view class="wa-sheet" @tap.stop>
				<view class="wa-sheet__handle"></view>
				<view class="wa-sheet__head">
					<text class="wa-sheet__title">配置西药医嘱</text>
					<text class="wa-sheet__close" @tap="closeSheet">✕</text>
				</view>

				<!-- 医嘱信息卡片：左侧药品图标，右侧 label 与内容 -->
				<view class="wa-medcard">
					<view class="wa-medcard__icon">
						<text class="wa-medcard__icon-text">{{ medIconText }}</text>
					</view>
					<view class="wa-medcard__main">
						<view class="wa-medcard__top">
							<text class="wa-medcard__label">医嘱名称</text>
							<text class="wa-medcard__hint">请补全医嘱执行信息</text>
						</view>
						<view class="wa-medcard__name">{{ sheetMed.medDesc || sheetMed.tradeName || '' }}</view>
						<view class="wa-medcard__spec">{{ sheetMed.medSpec || sheetMed.specDesc || sheetMed.spec || '规格待补全' }}</view>
						<view class="wa-medcard__info">
							<text v-if="sheetMed.medPrice !== undefined && sheetMed.medPrice !== null && sheetMed.medPrice !== ''" class="wa-medcard__price">¥{{ sheetMed.medPrice }}</text>
							<text v-if="sheetMed.medStock !== undefined && sheetMed.medStock !== null && sheetMed.medStock !== ''" class="wa-medcard__stock">库存 {{ sheetMed.medStock }}</text>
						</view>
					</view>
				</view>

				<scroll-view class="wa-sheet__body" scroll-y>
					<!-- 备注 -->
					<view class="wa-field">
						<view class="wa-field__label">备注</view>
						<input class="wa-field__input" v-model="attrForm.remark" placeholder="请输入医嘱备注" />
					</view>

					<!-- 关联医嘱 -->
					<!-- <view class="wa-field">
						<view class="wa-field__label">关联医嘱</view>
						<view class="wa-fbtn-group">
							<text
								class="wa-fbtn"
								:class="{ 'wa-fbtn--on': attrForm.relMedFlag === 'N' }"
								@tap="setRelMed('N')"
							>✓ 不关联</text>
							<text
								class="wa-fbtn wa-fbtn--info"
								@tap="setRelMed('Y')"
							>{{ attrForm.relMedFlag === 'Y' ? '✓' : '' }}{{ relMedHint }}</text>
						</view>
					</view> -->
					<!-- 数量 + 数量单位：两列并排 -->
					<view class="wa-row">
						<view class="wa-col">
							<!-- 数量（步进器） -->
							<view class="wa-field">
								<view class="wa-field__label"><text class="wa-field__required">*</text>数量</view>
								<view class="wa-fstepper">
									<text class="wa-fstepper__btn" @tap="decStepper('qty')">−</text>
									<text class="wa-fstepper__num">{{ attrForm.qty || 1 }}</text>
									<text class="wa-fstepper__btn" @tap="incStepper('qty')">＋</text>
								</view>
							</view>
						</view>
						<view class="wa-col">
							<!-- 数量单位 -->
							<attr-group
								title="数量单位"
								:required="true"
								:list="selectData5"
								field="unitDesc"
								:pick-index="picakIndex5"
								@pick="(i) => pickAttr('qtyUomID', i, 'picakIndex5')"
							/>
						</view>
					</view>
					<view class="wa-row">
						<view class="wa-col">
							<!-- 疗程（步进器） -->
							<view class="wa-field">
								<view class="wa-field__label"><text class="wa-field__required">*</text>疗程</		view>
								<view class="wa-fstepper">
									<text class="wa-fstepper__btn" @tap="decStepper('stepper')">−</text>
									<text class="wa-fstepper__num">{{ attrForm.stepper || 3 }}</text>
									<text class="wa-fstepper__btn" @tap="incStepper('stepper')">＋</text>
								</view>
							</view>
						</view>
						<view class="wa-col">
							<!-- 疗程单位 -->
							<attr-group
								title="疗程单位"
								:required="true"
								:list="courseUnitList"
								field="descripts"
								:pick-index="picakIndex9"
								@pick="(i) => pickAttr('courseUom', i, 'picakIndex9')"
							/>
						</view>
					</view>
					<!-- 剂量 + 剂量单位：两列并排 -->
					<view class="wa-row">
						<view class="wa-col">
							<!-- 剂量 -->
							<view class="wa-field">
								<view class="wa-field__label"><text class="wa-field__required">*</text>剂量</view>
								<input class="wa-field__input" v-model="attrForm.dose" type="digit" placeholder="请输入剂量" />
							</view>
						</view>
						<view class="wa-col">
							<!-- 剂量单位 -->
							<attr-group
								title="剂量单位"
								:required="true"
								:list="selectData4"
								field="doseUnitDesc"
								:pick-index="picakIndex4"
								@pick="(i) => pickAttr('doseUnit', i, 'picakIndex4')"
							/>
						</view>
					</view>
					<!-- 频次 -->
					<attr-group
						title="频次"
						:required="true"
						:list="selectData2"
						field="descripts"
						:pick-index="picakIndex2"
						@pick="(i) => pickAttr('freqCode', i, 'picakIndex2')"
					/>

					<!-- 用法 -->
					<attr-group
						title="用法"
						:required="true"
						:list="selectData3"
						field="descripts"
						:pick-index="picakIndex3"
						@pick="(i) => pickAttr('usageCode', i, 'picakIndex3')"
					/>

					<!-- 执行科室 -->
					<attr-group
						title="执行科室"
						:required="true"
						:list="selectData8"
						field="recLocDesc"
						:pick-index="picakIndex8"
						@pick="(i) => pickAttr('recLocID', i, 'picakIndex8')"
					/>

				</scroll-view>

				<!-- 抽屉底部：清空 / 取消 / 确认加入 -->
				<view class="wa-sheet__foot">
					<view class="wa-foot-btn wa-foot-btn--ghost" @tap="clearAttr">清空</view>
					<view class="wa-foot-btn wa-foot-btn--ghost" @tap="closeSheet">取消</view>
					<view class="wa-foot-btn wa-foot-btn--primary" @tap="confirmAttr">确认加入</view>
				</view>
			</view>
		</view>
	</view>
</template>

<script>
import { $http } from '../../../../../../../../config/https';
import Dialog from '../../../../../../../../utils/dialog';
import Cache from '../../../../../../../../utils/cache';
/**
 * 西药医嘱（商城式选择器）
 * 左：12100052 医嘱树；右：12100059 西药 / 03050098 检验检查治疗
 * 参考 inspectionExamine.vue 的左右布局与选中/保存行为
 */
import attrGroup from './attrGroup.vue';
import treeNode from './treeNode.vue';

export default {
	components: { attrGroup, TreeNode: treeNode },
	props: {
		wListData: { type: Array, default: () => [] },
		editData: { type: Object, default: () => ({}) },
		diagData: { type: Object, default: () => ({}) }
	},
	data() {
		return {
			// 当前选中医嘱（03050020 入参）
			medID: '',
			ordSetID: '',
			// 03050020 返回的默认选中 ID（频次/用法/结算类型为异步接口，等待其回调回填）
			ordFreqID: '',
			ordUsageID: '',
			ordAdmReasonID: '',
			ordPriorityID: '', // 03050020 返回的默认医嘱优先级
			hospID: null,
			admID: null,
			patID: null,
			logonLocID: null,
			userID: null,
			// 搜索
			searchL: '',
			searchR: '',
			// 左树
			treeList: [],
			treeListRaw: [], // 前端搜索原始数据
			activeL1: -1,
			activeL2: -1,
			activePath: [], // 当前选中节点 id 链（递归定位）
			rightNodeLabel: '', // 当前选中节点展示名
			// 右列表
			medList: [],
			checkList: [],
			rightFinished: false,
			loadingRight: false,
			currentPage: 1,
			rightNode: null, // 当前选中节点信息 {type, id, orderTypeCode}
			isTreatmentNode: false, // 治疗节点（orderTypeCode === 'T'）
			// 药品已选数量（本地集合）
			medQtyMap: {}, // medID -> qty
			medObjMap: {}, // medID -> 已确认医嘱对象
			// 检验检查/治疗已选（key：medID，兜底 appOrderID）
			selectedMap: {}, // medID/appOrderID -> item
			treatQtyMap: {}, // 治疗 medID -> qty
			// 底部抽屉
			showSheet: false,
			// 保存中（防止重复点击）
			saving: false,
			// 已选医嘱弹窗
			showSelectedSheet: false,
			// 从已选医嘱进入配置抽屉时，关闭或确认后需要返回已选医嘱
			returnToSelectedSheet: false,
			sheetMed: {},
			attrForm: {
				remark: '',
				relMedFlag: 'N', // N=不关联，Y=关联
				dose: '',
				stepper: 3,
				qty: 1,
				ordSkinTestFlag: 'N',
				skinTestNote: '',
				doseUnit: '',
				freqCode: '',
				usageCode: '',
				courseUom: '天',
				qtyUomID: '',
				recLocID: '',
				settleType: ''
			},
			// 抽屉内「疗程单位」固定枚举（天/周/月），与 attrGroup 共用描述字段
			courseUnitList: [
				{ id: 'd', descripts: '天' },
				{ id: 'w', descripts: '周' },
				{ id: 'm', descripts: '月' }
			],
			// 关联医嘱提示文本（依据选中药品推断，可由业务接口扩展）
			relMedHint: '',
			// 下拉数据源（按钮组）
			selectData1: [], // 结算类型
			selectData2: [], // 频次
			selectData3: [], // 用法
			selectData4: [], // 剂量单位
			selectData5: [], // 数量单位
			selectData6: [], // 标本
			selectData7: [], // 皮试备注
			selectData8: [], // 执行科室
			selectData9: [], // 规格
			picakIndex1: 0,
			picakIndex2: 0,
			picakIndex3: 0,
			picakIndex4: 0,
			picakIndex5: 0,
			picakIndex7: 0,
			picakIndex8: 0,
			picakIndex9: 0,
			active: 0,
			editList: [],
			editInfo: {},
			added: false
		};
	},
	computed: {
		// 医嘱信息卡片左侧图标文字（药品名称首字）
		medIconText() {
			const name = this.sheetMed.medDesc || this.sheetMed.tradeName || '';
			return name ? name.charAt(0) : '药';
		},
		// 统一已选列表（西药 / 检查 / 治疗）
		selectedList() {
			const list = [];
			// 西药
			Object.keys(this.medObjMap).forEach((key) => {
				const it = this.medObjMap[key];
				list.push({
					key,
					kind: 'med',
					title: it.medDesc || it.tradeName || '',
					tag: '西药',
					tagClass: 'wa-card__tag--gray',
					desc: this.formatMedDesc(it),
					data: it,
					qty: it.qty || it.stepper2 || 1
				});
			});
			// 检查/治疗
			Object.keys(this.selectedMap).forEach((key) => {
				const it = this.selectedMap[key];
				if (this.isTreatItem(it)) {
					list.push({
						key,
						kind: 'treat',
						title: it.medDesc || it.descripts || '',
						tag: '治疗',
						tagClass: 'wa-card__tag--blue',
						desc: this.formatTreatDesc(it, this.treatQtyMap[key] || 1),
						data: it,
						qty: this.treatQtyMap[key] || 1
					});
				} else {
					list.push({
						key,
						kind: 'check',
						title: it.medDesc || it.descripts || '',
						tag: '检验检查',
						tagClass: 'wa-card__tag--blue',
						desc: this.formatCheckDesc(it),
						data: it
					});
				}
			});
			return list;
		},
		totalSelected() {
			return this.selectedList.length;
		},
		isAllSelected() {
			if (this.rightNode && this.rightNode.type === 'med') {
				return this.medList.length > 0 && this.medList.every((m) => this.medObjMap[m.medID]);
			}
			// 检查(E)/检验(L) 大类才可全选
			if (this.rightNode && (this.rightNode.type === 'E' || this.rightNode.type === 'L')) {
				return this.checkList.length > 0 && this.checkList.every((c) => this.selectedMap[c.medID || c.appOrderID]);
			}
			return false;
		}
	},
	watch: {
		editData: {
			handler(n) {
				if (n && n.detail) this.editDataFun(n);
			},
			immediate: false
		}
	},
	attached() {
		this.getTree();
	},
	mounted() {
		this.setUserContext();
		this.getTree();
		if (this.editData && this.editData.detail) this.editDataFun(this.editData);
	},
	
	destroyed() {
		// 组件销毁时不自动回传，避免重复提交；由用户点击「保存」统一提交
	},
	methods: {
        setUserContext() {
	    	const patientData = Cache.get('patientData');
	    	const userData = Cache.get('userData');
	    	this.setData({
	    		hospID: userData.hospID,
	    		admID: patientData.admID,
	    		patID: patientData.patID,
	    		logonLocID: userData.locID,
	    		userID: userData.userID
	    	});
        },
		/* ============ 左树 12100052（递归多级） ============ */
		getTree() {
            const userData = Cache.get('userData');
            const { hospID } = userData;
			$http.post('urlDeault', this, {
                code: '12100052',
                data: {
                    params: [
				    	{
				    		hospID: hospID,
                            searchCode: '',
				    	}
				    ]
                },
				success: (res) => {
					if (res.errorCode === '0') {
						const data = this.normalizeTree(res?.result?.data || []);
						this.setData({ treeList: data, treeListRaw: data });
						// 默认选中第一个大类（高亮 + 加载右侧），但所有节点保持收起
						if (data.length && !this.activePath.length) {
							const first = data[0];
							const path = this.findPathById(this.treeListRaw, first) || [];
							this.setData({
								activePath: path.map((p) => this.buildNodeKey(p.node, p.idx))
							});
							this.selectTreeNode(first, false);
						}
					}
				}
			});
		},
		normalizeTree(nodes) {
			let uid = 0;
			const walk = (arr) =>
				(arr || []).map((node) => {
					const _uid = ++uid;
					return {
						...node,
						_uid,
						_open: false,
						children: node.children && node.children.length ? walk(node.children) : []
					};
				});
			return walk(nodes);
		},
		// 输入框实时过滤（带命中路径自动展开）
		onTreeSearchInput() {
			this.filterTree();
		},
		clearTreeSearch() {
			this.setData({ searchL: '' });
			this.filterTree();
		},
		filterTree() {
			const kw = (this.searchL || '').trim().toLowerCase();
			if (!kw) {
				this.setData({ treeList: this.treeListRaw });
				return;
			}
			const matchNode = (node) => (node.descripts || '').toLowerCase().includes(kw);
			const filter = (nodes) =>
				(nodes || []).reduce((acc, node) => {
					const children = filter(node.children || []);
					if (matchNode(node) || children.length) {
						acc.push({ ...node, children, _open: true });
					}
					return acc;
				}, []);
			const filtered = filter(this.treeListRaw);
			// 搜索时重置选中态，避免选中一个被隐藏的节点
			this.setData({
				treeList: filtered,
				activePath: [],
				rightNodeLabel: ''
			});
		},
		// 由 treeNode 抛出：选中节点（任意层级）
		onTreeSelect(node) {
			// 右侧数据加载中：忽略再次点击，避免重复选中与重复请求
			if (this.loadingRight) return;
			const path = this.findPathById(this.treeListRaw, node) || [];
			this.setData({
				activePath: path.map((p) => this.buildNodeKey(p.node, p.idx))
			});
			this.selectTreeNode(node);
		},
		selectTreeNode(node, expand = true) {
			const hasChildren = node.children && node.children.length;
			// 父节点：切换展开/收起（expand=false 时不展开，仅选中）；叶子节点：直接选中
			let raw = this.treeListRaw;
			let willOpen = false;
			if (hasChildren && expand) {
				willOpen = !node._open;
				raw = this.findAndUpdateRaw(this.treeListRaw, node, (n) => ({ ...n, _open: willOpen }));
			}
			this.setData({
				treeListRaw: raw,
				treeList: this.searchL.trim() ? this.treeList : raw,
				rightNodeLabel: node.descripts || node.medDesc || ''
			});
			// 收起分类时不请求右侧列表数据（仅更新展开状态）
			if (hasChildren && expand && !willOpen) return;
			// 12100052：节点带 mainCode（Z=治疗，E=检查，L=检验）走 03050098；无 mainCode 走 12100059 西药
			if (node.mainCode) {
				const mainCode = this.resolveMainCode(node);
				this.loadCheckList(Object.assign({}, node, { mainCode }));
			} else {
				this.setData({ isTreatmentNode: false });
				this.loadMedList(node);
			}
		},
		// appType 子级可能无 mainCode：沿祖先路径取最近的非空 mainCode
		resolveMainCode(node) {
			if (node.mainCode) return node.mainCode;
			const path = this.findPathById(this.treeListRaw, node) || [];
			for (let i = path.length - 1; i >= 0; i--) {
				if (path[i].node.mainCode) return path[i].node.mainCode;
			}
			return '';
		},
		// 与渲染 key 一致的拼接：type + '-' + (CBid || HBid || id) + '-' + idx
		buildNodeKey(node, idx) {
			return node.type + '-' + (node.CBid || node.HBid || node.id) + '-' + idx;
		},
		// 匹配用的稳定 key（不含 idx）：12100052 的 appType 节点可能无 id，需用 CBid/HBid 兜底
		nodeKey(node) {
			return node.type + '-' + (node.CBid || node.HBid || node.id);
		},
		findPathById(nodes, node, path = []) {
			const target = this.nodeKey(node);
			let i = 0;
			for (const n of nodes || []) {
				if (this.nodeKey(n) === target) return [...path, { node: n, idx: i }];
				if (n.children && n.children.length) {
					const r = this.findPathById(n.children, node, [...path, { node: n, idx: i }]);
					if (r) return r;
				}
				i++;
			}
			return null;
		},
		findAndUpdateRaw(nodes, node, updater) {
			const target = this.nodeKey(node);
			return (nodes || []).map((n) => {
				if (this.nodeKey(n) === target) return updater(n);
				if (n.children && n.children.length)
					return { ...n, children: this.findAndUpdateRaw(n.children, node, updater) };
				return n;
			});
		},

		/* ============ 右列表 ============ */
		resetRight() {
			this.setData({ medList: [], checkList: [], currentPage: 1, rightFinished: false });
		},
		// 西药 12100059（一次查询 200 条，不分页）
		loadMedList(node) {
			this.setData({ rightNode: { type: 'med', id: node.id, medType: node.type } });
			this.resetRight();
			// orderCat
			const params = {
				hospID: this.hospID,
				logonLocID: this.logonLocID,
				userID: this.userID,
				admID: this.admID,
				searchCode: this.searchR,
               // ordType: node.type,
				drugsFlag: 'Y', // 仅检索药品类医嘱
				judgeFlag: 'Y',
				notOrdSet: 'Y',
				language: 'CH'
			};
			if (node.type == 'orderCat') {
				params.medCatID = node.id;
			} else {
				params.medSubCatID = node.id;
			}
			this.setData({ loadingRight: true }, () => {
				$http.post('urlDeault', this, {
                	code: '12100059',
                	data: {
                	    params: [params],
                	    pagination: [{ pageSize: 200, currentPage: 1 }],
                	},

					success: (res) => {
						if (res.errorCode === '0') {
							const data = res?.result?.data || [];
							this.setData({ medList: data, loadingRight: false, rightFinished: true });
						} else {
							this.setData({ loadingRight: false });
						}
					},
					fail: () => {
						this.setData({ loadingRight: false });
					}
				});
			});
			
		},
		// 检验检查/治疗 03050098（参照 inspectionExamine.getMedicalList）
		// mainCode: Z=治疗，E=检查，L=检验
		// 返回字段：appOrderID / medDesc / medID / medPrice / orderTypeCode
		loadCheckList(node) {
			this.resetRight();
			this.setData({ loadingRight: true }, () => {
					const params = {
					hospID: this.hospID,
					locID: this.logonLocID,
					admID: this.admID,
				};
				// 搜索关键字（参照 inspectionExamine.getMedicalList）
				if (this.searchR) {
					params.searchCode = this.searchR;
				}
				// 选中 node 时：HBid 有值 -> appTypeID，否则 CBid
				if (node.HBid) {
					params.appTypeID = node.HBid;
				} else {
					params.CBid = node.CBid || node.id;
				}
				$http.post('urlDeault', this, {
					code: '03050098',
					data: {
						params: [params],
					},
					success: (res) => {
						if (res.errorCode === '0') {
							const data = res.result || [];
							// 治疗分支由 mainCode === 'Z' 决定
							const isTreatment = node.mainCode === 'Z';
							this.setData({
								checkList: data,
								isTreatmentNode: isTreatment,
								rightNode: {
									// 检查(E)/检验(L) 显示全选；治疗(Z) 不显示
									visibleCheckAll: node.mainCode === 'E' || node.mainCode === 'L',
									type: node.mainCode,
									id: node.id,
									CBid: node.CBid,
									HBid: node.HBid,
									orderTypeCode: ''
								},
								loadingRight: false,
								rightFinished: true
							});
						} else {
							this.setData({ loadingRight: false });
						}
					},
					fail: () => {
						this.setData({ loadingRight: false });
					}
				});
			});
		},
		loadRightList() {
			if (!this.rightNode) return;
			// 恢复节点类型（orderCat/medSubCat），保证搜索时分类参数正确
			if (this.rightNode.type === 'med') this.loadMedList({ id: this.rightNode.id, type: this.rightNode.medType });
			else this.loadCheckList({ id: this.rightNode.id, CBid: this.rightNode.CBid, HBid: this.rightNode.HBid, mainCode: this.rightNode.type });
		},
		checkTypeLabel(code) {
			if (code === 'L') return '检验';
			if (code === 'E') return '检查';
			return '治疗';
		},

		/* ============ 药品选中/抽屉 ============ */
		openAttrSheet(med) {
			this.setData({ sheetMed: med });
			// 解锁派生数据源
			this.searchSelectFun(med);
			// 推断关联医嘱提示
			const relMedHint = this.inferRelMedHint(med);
			// 初始化 attrForm（编辑回显）
			this.setData({
				attrForm: this.buildAttrFormFromMed(med),
				relMedHint
			});
			this.syncPickIndex();
		},
		closeSheet() {
			const returnToSelectedSheet = this.returnToSelectedSheet;
			this.setData({
				showSheet: false,
				showSelectedSheet: returnToSelectedSheet,
				returnToSelectedSheet: false
			});
		},
		// 推断关联医嘱提示文本：默认携带第一个常见抗菌药提示，可由接口覆盖
		inferRelMedHint(med) {
			return '阿莫西林抗生菌 · 每日 2 次';
		},
		// 关联医嘱切换
		setRelMed(flag) {
			const attrForm = Object.assign({}, this.attrForm, { relMedFlag: flag });
			this.setData({ attrForm });
		},
		// 步进器：＋
		incStepper(key) {
			const cur = Number(this.attrForm[key]) || 0;
			const next = cur + 1;
			const attrForm = Object.assign({}, this.attrForm, { [key]: next });
			this.setData({ attrForm });
		},
		// 步进器：－（最低 1）
		decStepper(key) {
			const cur = Number(this.attrForm[key]) || 1;
			const next = Math.max(1, cur - 1);
			const attrForm = Object.assign({}, this.attrForm, { [key]: next });
			this.setData({ attrForm });
		},
		// 清空抽屉表单
		clearAttr() {
			const med = this.sheetMed || {};
			const relMedHint = this.inferRelMedHint(med);
			this.setData({
				attrForm: {
					remark: '',
					relMedFlag: 'N',
					dose: '',
					stepper: 3,
					qty: 1,
					ordSkinTestFlag: 'N',
					skinTestNote: '',
					doseUnit: '',
					freqCode: '',
					usageCode: '',
					courseUom: '天',
					qtyUomID: '',
					recLocID: '',
					settleType: ''
				},
				relMedHint
			});
			this.syncPickIndex();
		},
		/* ============ 已选医嘱弹窗 ============ */
		openSelectedSheet() {
			this.setData({ showSelectedSheet: true, showSheet: false });
		},
		closeSelectedSheet() {
			this.setData({ showSelectedSheet: false });
		},
		// 底部「保存」：无已选项目时提示禁用，有则弹出已选医嘱弹窗
		onSaveClick() {
			if (this.saving) return;
			if (!this.totalSelected) {
				uni.showToast({ title: '请先选择医嘱', icon: 'none' });
				return;
			}
			this.openSelectedSheet();
		},
		// 已选医嘱弹窗「确认保存」：关闭弹窗并执行保存
		confirmSave() {
			if (this.saving) return;
			if (!this.totalSelected) return;
			this.setData({ showSelectedSheet: false }, () => {
				this.saveAll();
			});
		},
		formatMedDesc(it) {
			const parts = [];
			// 数量 + 数量单位
			if (it.qty) {
				parts.push(it.qty + (it.qtyUomID || ''));
			} else if (it.qtyUomID) {
				parts.push(it.qtyUomID);
			}
			if (it.dose) parts.push(it.dose + (it.doseUnit || ''));
			if (it.usageCode) parts.push(it.usageCode);
			if (it.freqCode) parts.push(it.freqCode);
			return parts.join(' · ') || '';
		},
		formatTreatDesc(it, qty) {
			const parts = [];
			if (it.bodyDesc) parts.push('部位：' + it.bodyDesc);
			parts.push('已选 ' + (qty || 1) + ' 次');
			return parts.join(' · ');
		},
		formatCheckDesc(it) {
			return it.bodyDesc ? '检查部位：' + it.bodyDesc : (it.descripts || '');
		},
		removeSelected(item) {
			if (item.kind === 'med') {
				const medQtyMap = Object.assign({}, this.medQtyMap);
				const medObjMap = Object.assign({}, this.medObjMap);
				delete medQtyMap[item.key];
				delete medObjMap[item.key];
				this.setData({ medQtyMap, medObjMap });
			} else {
				const selectedMap = Object.assign({}, this.selectedMap);
				const treatQtyMap = Object.assign({}, this.treatQtyMap);
				delete selectedMap[item.key];
				delete treatQtyMap[item.key];
				this.setData({ selectedMap, treatQtyMap });
			}
		},
		incSelectedTreat(item) {
			if (item.kind !== 'treat') return;
			const key = item.key;
			const q = (this.treatQtyMap[key] || 0) + 1;
			const treatQtyMap = Object.assign({}, this.treatQtyMap, { [key]: q });
			const selectedMap = Object.assign({}, this.selectedMap);
			selectedMap[key] = item.data;
			this.setData({ treatQtyMap, selectedMap });
		},
		decSelectedTreat(item) {
			if (item.kind !== 'treat') return;
			const key = item.key;
			const q = (this.treatQtyMap[key] || 1) - 1;
			const treatQtyMap = Object.assign({}, this.treatQtyMap);
			const selectedMap = Object.assign({}, this.selectedMap);
			if (q <= 0) {
				delete treatQtyMap[key];
				delete selectedMap[key];
			} else {
				treatQtyMap[key] = q;
				selectedMap[key] = item.data;
			}
			this.setData({ treatQtyMap, selectedMap });
		},
		editSelectedMed(item) {
			if (item.kind !== 'med') return;
			// 先关闭已选弹窗，再打开药品属性抽屉
			this.setData({ showSelectedSheet: false, returnToSelectedSheet: true }, () => {
				this.openAttrSheet(item.data);
			});
		},
		syncPickIndex() {
			// 编辑既有医嘱时，优先按该医嘱已固化的 ID 回显；不能只按描述匹配，
			// 因为不同药品的单位/用法列表可能存在同名不同 ID 的情况。
			const order = this.medObjMap[this.medID] || {};
			this.setData({
				picakIndex4: this.indexByIdOrDesc(this.selectData4, 'doseUnitID', order.ordDoseUnitID, 'doseUnitDesc', this.attrForm.doseUnit),
				picakIndex2: this.indexByIdOrDesc(this.selectData2, 'id', order.ordFreqID, 'descripts', this.attrForm.freqCode),
				picakIndex3: this.indexByIdOrDesc(this.selectData3, 'id', order.ordUsageID, 'descripts', this.attrForm.usageCode),
				picakIndex5: this.indexByIdOrDesc(this.selectData5, 'unitID', order.ordQtyUomID, 'unitDesc', this.attrForm.qtyUomID),
				picakIndex7: this.indexByDesc(this.selectData7, 'descripts', this.attrForm.skinTestNote),
				picakIndex8: this.indexByIdOrDesc(this.selectData8, 'recLocID', order.ordRecLocID, 'recLocDesc', this.attrForm.recLocID),
				picakIndex1: this.indexByIdOrDesc(this.selectData1, 'id', order.ordAdmReasonID, 'descripts', this.attrForm.settleType),
				picakIndex9: this.indexByIdOrDesc(this.courseUnitList, 'id', order.ordDurationID, 'descripts', this.attrForm.courseUom)
			});
		},
		indexByIdOrDesc(list, idField, id, descField, desc) {
			if (list && list.length && id !== '' && id !== null && id !== undefined) {
				const index = list.findIndex((item) => item && String(item[idField]) === String(id));
				if (index >= 0) return index;
			}
			return this.indexByDesc(list, descField, desc);
		},
		indexByDesc(list, field, val) {
			if (!list || !list.length || !val) return -1;
			const i = list.findIndex((x) => x[field] === val);
			return i < 0 ? -1 : i;
		},
		pickAttr(key, idx, pickKey) {
			this.setData({ [pickKey]: idx });
			const map = {
				doseUnit: ['selectData4', 'doseUnitDesc'],
				freqCode: ['selectData2', 'descripts'],
				usageCode: ['selectData3', 'descripts'],
				qtyUomID: ['selectData5', 'unitDesc'],
				skinTestNote: ['selectData7', 'descripts'],
				recLocID: ['selectData8', 'recLocDesc'],
				settleType: ['selectData1', 'descripts'],
				courseUom: ['courseUnitList', 'descripts']
			};
			const conf = map[key];
			if (conf) {
				const attrForm = Object.assign({}, this.attrForm);
				attrForm[key] = this[conf[0]][idx][conf[1]];
				this.setData({ attrForm });
			}
		},
		confirmAttr() {
			const med = this.sheetMed;
			const obj = this.buildMedObj(med, this.attrForm);
			const medObjMap = Object.assign({}, this.medObjMap);
			medObjMap[med.medID] = obj;
			const qty = Number(this.attrForm.qty) || 1;
			const returnToSelectedSheet = this.returnToSelectedSheet;
			this.setData({
				medObjMap: medObjMap,
				medQtyMap: Object.assign({}, this.medQtyMap, { [med.medID]: qty }),
				showSheet: false,
				showSelectedSheet: returnToSelectedSheet,
				returnToSelectedSheet: false
			});
		},
		incMed(med) {
			const q = (this.medQtyMap[med.medID] || 1) + 1;
			const medQtyMap = Object.assign({}, this.medQtyMap, { [med.medID]: q });
			const medObjMap = Object.assign({}, this.medObjMap);
			if (medObjMap[med.medID]) {
				medObjMap[med.medID].qty = q;
				medObjMap[med.medID].stepper2 = q; // 兼容旧字段
			}
			this.setData({ medQtyMap, medObjMap });
		},
		decMed(med) {
			const q = (this.medQtyMap[med.medID] || 1) - 1;
			if (q <= 0) {
				const m = Object.assign({}, this.medQtyMap);
				delete m[med.medID];
				const o = Object.assign({}, this.medObjMap);
				delete o[med.medID];
				this.setData({ medQtyMap: m, medObjMap: o });
			} else {
				const medQtyMap = Object.assign({}, this.medQtyMap, { [med.medID]: q });
				const medObjMap = Object.assign({}, this.medObjMap);
				if (medObjMap[med.medID]) {
					medObjMap[med.medID].qty = q;
					medObjMap[med.medID].stepper2 = q; // 兼容旧字段
				}
				this.setData({ medQtyMap, medObjMap });
			}
		},
		// 治疗：仅加减数量（key 用 medID，治疗项 03050098 无 appOrderID）
		incTreat(it) {
			const key = it.medID || it.appOrderID;
			const q = (this.treatQtyMap[key] || 0) + 1;
			const treatQtyMap = Object.assign({}, this.treatQtyMap, { [key]: q });
			const selectedMap = Object.assign({}, this.selectedMap);
			selectedMap[key] = it;
			this.setData({ treatQtyMap, selectedMap });
		},
		decTreat(it) {
			const key = it.medID || it.appOrderID;
			const q = (this.treatQtyMap[key] || 0) - 1;
			const treatQtyMap = Object.assign({}, this.treatQtyMap);
			const selectedMap = Object.assign({}, this.selectedMap);
			if (q <= 0) {
				delete treatQtyMap[key];
				delete selectedMap[key];
			} else {
				treatQtyMap[key] = q;
				selectedMap[key] = it;
			}
			this.setData({ treatQtyMap, selectedMap });
		},
		// 治疗项判定：03050098 返回 orderTypeCode，O=治疗（E=检查，L=检验）
		isTreatItem(it) {
			return !!(it && (it.orderTypeCode === 'O' || it.mainCode === 'O'));
		},
		onCheckTap(it) {
			if (this.isTreatItem(it)) return;
			this.toggleCheck(it);
		},

		/* ============ 药品/检查 选中 ============ */
		toggleMed(med) {
			if (this.medObjMap[med.medID]) {
				const medQtyMap = Object.assign({}, this.medQtyMap);
				const medObjMap = Object.assign({}, this.medObjMap);
				delete medQtyMap[med.medID];
				delete medObjMap[med.medID];
				this.setData({ medQtyMap, medObjMap });
			} else {
				this.openAttrSheet(med);
			}
		},
		isChecked(it) {
			return !!this.selectedMap[it.medID || it.appOrderID];
		},
		toggleCheck(it) {
			const key = it.medID || it.appOrderID;
			const m = Object.assign({}, this.selectedMap);
			if (m[key]) delete m[key];
			else m[key] = it;
			this.setData({ selectedMap: m });
		},
		toggleSelectAll() {
			if (this.rightNode && this.rightNode.type === 'med') {
				const allOn = this.isAllSelected;
				let medObjMap = Object.assign({}, this.medObjMap);
				let medQtyMap = Object.assign({}, this.medQtyMap);
				this.medList.forEach((med) => {
					if (allOn) {
						delete medObjMap[med.medID];
						delete medQtyMap[med.medID];
					}
				});
				if (allOn) {
					this.setData({ medObjMap, medQtyMap });
				} else {
					// 全选不能沿用当前抽屉（某一个药品）的全局下拉索引；
					// 需批量取得每个药品自己的默认数据后再写入 medObjMap。
					this.addAllMedsWithDefaults();
				}
			} else if (this.rightNode && (this.rightNode.type === 'E' || this.rightNode.type === 'L')) {
				const allOn = this.isAllSelected;
				let selectedMap = Object.assign({}, this.selectedMap);
				this.checkList.forEach((it) => {
					const key = it.medID || it.appOrderID;
					if (allOn) delete selectedMap[key];
					else selectedMap[key] = it;
				});
				this.setData({ selectedMap });
			}
		},

		/* ============ 保存 ============ */
		// 格式化当前日期时间（开医嘱/医嘱开始默认当前时间）
		formatDateTime() {
			const now = new Date();
			const pad = (n) => (n < 10 ? '0' + n : '' + n);
			const date = now.getFullYear() + '-' + pad(now.getMonth() + 1) + '-' + pad(now.getDate());
			const time = pad(now.getHours()) + ':' + pad(now.getMinutes()) + ':' + pad(now.getSeconds());
			return { date, time };
		},
		// 仅组装保存数据（参考 docs/文档.md），不走接口保存
		saveAll() {
			if (this.saving) return; // 防止重复点击
			if (!this.totalSelected) {
				uni.showToast({ title: '请先选择医嘱', icon: 'none' });
				return;
			}
			this.setData({ saving: true }, () => {
				const patientData = Cache.get('patientData');
				const userData = Cache.get('userData');
				const { admID } = patientData;
				const { userID, locID, hospID, groupID } = userData;
				// 开医嘱日期时间 / 医嘱开始日期时间（默认为当前日期时间）
				const { date, time } = this.formatDateTime();

				// ============ 1. 西药（03100050） ============
				const westOrders = [];
				Object.keys(this.medObjMap).forEach((k, index) => {
					const item = this.medObjMap[k] || {};
					westOrders.push({
						ordSeqNo: item.xh || '',                          // 序号
						ordLinkSeqNo: '',                                 // 关联序号
						ordDate: date,                                    // 开医嘱日期
						ordTime: time,                                    // 开医嘱时间
						ordStartDate: date,                               // 医嘱开始日期（默认当前日期）
						ordStartTime: time,                               // 医嘱开始时间（默认当前时间）
						ordEndDate: '',                                   // 医嘱结束日期
						ordEndTime: '',                                   // 医嘱结束时间
						ordPriorityID: item.ordPriorityID || '10',            // 每个药品确认时固化的优先级
						ordAdmReasonID: item.ordAdmReasonID || '',            // 每个药品确认时固化的结算类型
						medID: item.medID || '',                          // 医嘱项id
						ordFirstDayNum: '',                               // 首日次数
						ordUsageID: item.ordUsageID || '',                   // 药品用法
						ordFreqID: item.ordFreqID || '',                     // 频次
						ordDose: item.dose || '',                         // 剂量
						ordDoseUnitID: item.ordDoseUnitID || '',            // 剂量单位
						ordQty: item.qty || item.stepper2 || '',          // 数量
						ordQtyUomID: item.ordQtyUomID || '',                // 数量单位
						ordDurQty: item.stepper || '',                    // 疗程数量
						ordDurationID: item.ordDurationID || '',           // 疗程单位
						ordInfuseSpeed: '',                               // 滴速
						ordSpeedUnitID: '',                               // 滴速单位
						ordStage: '',                                     // 医嘱阶段
						ordRecLocID: item.ordRecLocID || '',                // 接收科室
						ordOPer: '',                                      // 手术id
						ordEMFlag: '',                                    // 加急标志
						ordPriorityExtID: '',                             // 二级医嘱优先级
						ordNote: item.remark || '',                       // 备注
						orderResourceCode: '',                            // 医嘱来源代码
						ordSeqNo: index,
						ordTypeCode: 'M',                                 // 医嘱类型 M=西药
						extvalue: [                                       // 诊断信息（03100050 通过 extvalue 	传递）
							{
								code: 'Diag',
								refDescripts: (this.diagData && this.diagData.diagCode) || '',
								value: (this.diagData && this.diagData.id) || ''
							}
						]
					});
				});
				// ============ 2. 检验检查治疗：先 03050020 获取默认数据，再按 03050001 组装 ============
				const invalidWestOrder = this.validateWestOrders(westOrders);
				if (invalidWestOrder) {
					uni.showToast({ title: invalidWestOrder, icon: 'none' });
					this.setData({ saving: false });
					return;
				}
				const selectedKeys = Object.keys(this.selectedMap);
				if (selectedKeys.length === 0) {
					// 仅西药：直接提交
					this.submitMixedOrders(westOrders, [], [], [], admID, locID, userID, hospID, groupID);
					return;
				}
				const queryOrders = selectedKeys.map((k) => {
					const it = this.selectedMap[k] || {};
					return { medID: it.medID || '', ordSetID: '' };
				});
				$http.post('urlDeault', this, {
					code: '03050020',
					data: {
						params: [{
							admID: admID || '',
							orders: queryOrders
						}]
					},
					success: (res) => {
						if (res.errorCode === '0') {
							const dataList = res.data || [];
							// medID -> defaultData 映射，便于按选中顺序构造 orders
							const defaultMap = {};
							dataList.forEach((d) => {
								if (d && d.defaultData && d.defaultData.medID) {
									defaultMap[d.defaultData.medID] = d.defaultData;
								}
							});
							const { checkOrders, lisOrders, treatOrders } = this.buildCheckTreatOrders(
								selectedKeys,
								defaultMap,
								locID
							);
							this.submitMixedOrders(
								westOrders,
								checkOrders,
								lisOrders,
								treatOrders,
								admID,
								locID,
								userID,
								hospID,
								groupID
							);
						} else {
							uni.showToast({ title: res.errorMessage || '获取医嘱默认数据失败', icon: 'none' });
							this.setData({ saving: false });
						}
					},
					fail: () => {
						uni.showToast({ title: '获取医嘱默认数据失败', icon: 'none' });
						this.setData({ saving: false });
					}
				});
			});	
		},
		// 按 03050001 组装检验检查治疗订单，并按 ordTypeCode 拆分为 check/lis/treat
		// mainCode: E=检查 L=检验 O=治疗 -> ordTypeCode: C / L / T
		buildCheckTreatOrders(keys, defaultMap, locID) {
			const typeMap = { E: 'C', L: 'L', O: 'T' };
			// 开医嘱日期时间 / 医嘱开始日期时间（默认为当前日期时间）
			const { date, time } = this.formatDateTime();
			const checkOrders = [];
			const lisOrders = [];
			const treatOrders = [];
			keys.forEach((k, index) => {
				const item = this.selectedMap[k] || {};
				const mainCode = item.mainCode || item.orderTypeCode || '';
				const ordTypeCode = typeMap[mainCode] || 'T';
				const isTreat = mainCode === 'O';
				const isExamination = mainCode === 'E';
				// 03050020 返回的该医嘱默认数据
				const def = defaultMap[item.medID] || {};
				const order = {
					ordMedItemDesc: item.medDesc || item.descripts || '',
					medID: item.medID || '',
					ordPrice: def.ordPrice || 0,
					ordQty: isTreat ? (this.treatQtyMap[k] || item.qty || 1) : (def.ordQty || 1),
					ordDose: def.ordDose || 1,
					ordDate: date,                       // 开医嘱日期
					ordTime: time,                       // 开医嘱时间
					ordStartDate: date,                  // 医嘱开始日期（默认当前日期）
					ordStartTime: time,                  // 医嘱开始时间（默认当前时间）
					ordSpecID: def.ordSpecID || '',
					ordEMFlag: def.ordEMFlag || '',
					ordPriorityID: def.ordPriorityID || '10',
					ordAdmReasonID: def.ordAdmReasonID || '1',
					tabIndex: index + 1,
					ordRecLocID: def.ordRecLocID || locID || '',
					ordQtyUomID: def.ordQtyUomID || '',
					ordSeqNo: index,
					appOrderID: item.appOrderID || '',
					ordTypeCode: ordTypeCode,
					extvalue: [
						{
							code: 'Diag',
							value: (this.diagData && this.diagData.id) || '',
							refDescripts: (this.diagData && this.diagData.diagCode) || ''
						},
						{ code: 'RegDate', value: '', refDescripts: '预约执行日期' }
					]
				};
				if (isExamination) {
					// 仅检查(ordTypeCode='C')需要 bodyPart 三层结构：
					// orders.ordbodyPart=[null] -> bodyPart[].bodyPart[].bodyPart=[]
					order.ordbodyPart = [null];
				}
				if (mainCode === 'E') checkOrders.push(order);
				else if (mainCode === 'L') lisOrders.push(order);
				else treatOrders.push(order);
			});
			return { checkOrders, lisOrders, treatOrders };
		},
		// 组装统一提交参数并调用 12100054 混合医嘱保存，成功后跳转医嘱录入页
		submitMixedOrders(westOrders, checkOrders, lisOrders, treatOrders, admID, locID, userID, hospID, groupID) {
			// 参考 inspectionExamine.vue（03050001）：检查(E)按 tabIndex 分组生成 appMessage.bodyPart，
			// 每项 { bodyPart:[...], tabIndex }；检验(L)/治疗(T)不参与分组
			const bodyPartGroup = {};
			checkOrders.forEach((order) => {
				const tabIndex = order.tabIndex;
				if (!bodyPartGroup[tabIndex]) {
					bodyPartGroup[tabIndex] = [];
				}
				(order.ordbodyPart || []).forEach((part) => {
					bodyPartGroup[tabIndex].push({ bodyPart: part == null ? [] : part });
				});
			});
			const bodyPartArr = Object.keys(bodyPartGroup).map((tabIndex) => ({
				bodyPart: bodyPartGroup[tabIndex],
				tabIndex: Number(tabIndex)
			}));
			const appMessage = {
				appPurpose: '',
				HPIllness: '',
				sign: '',
				chiefComplaint: '',
				// 检查项目参与 bodyPart 分组；无检查项时传空数组
				bodyPart: bodyPartArr.length > 0 ? bodyPartArr : []
			};
			const saveData = {
				admID: admID || '',          // 就诊id
				locID: locID || '',          // 开单科室
				docID: userID || '',         // 开单医生
				userID: userID || '',        // 用户
				hospID: hospID || '',        // 登录医院id
				groupID: groupID || '',      // 登录角色id
				medType: 'WM',
				orderReSorce: 'Doctor',
				westOrders: westOrders,      // 西药 M
				checkOrders: checkOrders,    // 检查 C
				lisOrders: lisOrders,        // 检验 L
				treatOrders: treatOrders,    // 治疗 T
				appMessage: appMessage       // 检查部位分组（参考 inspectionExamine.vue 03050001 入参）
			};
			if (!westOrders.length && !checkOrders.length && !lisOrders.length && !treatOrders.length) {
				uni.showToast({ title: '请先选择医嘱', icon: 'none' });
				this.setData({ saving: false });
				return;
			}
			$http.post('urlDeault', this, {
				code: '12100054',
				data: {
					params: [saveData]
				},
				success: (res) => {
					if (res.errorCode === '0') {
						uni.showToast({ title: '保存成功', icon: 'success' });
						// 保存成功后跳转到医嘱录入页
						setTimeout(() => {
							this.setData({ saving: false });
							uni.navigateTo({
								url: '/pages/entryWrite/page/Entry/Entry?select=2&addText=添加医嘱'
							});
						}, 800);
					} else {
						const msg =
							(res.errorList && res.errorList[0] && res.errorList[0].errorMessage) ||
							res.errorMessage ||
							'保存失败';
						uni.showToast({ title: msg, icon: 'none' });
						this.setData({ saving: false });
					}
				},
				fail: () => {
					uni.showToast({ title: '保存失败，请重试', icon: 'none' });
					this.setData({ saving: false });
				}
			});
		},
		// 从下拉数据源按选中索引取 ID（找不到返回 ''）
		listId(listKey, pickKey, idField) {
			const list = this[listKey] || [];
			const idx = this[pickKey];
			if (!list.length || idx < 0 || !list[idx]) return '';
			return list[idx][idField] || '';
		},
		// 从一组下拉列表中查找指定 ID 对应的展示字段；全选批量初始化时使用，
		// 不读取当前抽屉的全局索引。
		optionDesc(list, idField, id, descField) {
			if (!list || !list.length || id === '' || id === null || id === undefined) return '';
			const option = list.find((item) => item && String(item[idField]) === String(id));
			return option ? (option[descField] || '') : '';
		},
		// 批量全选时，为每个药品以 03050020 的本药默认数据创建独立的订单对象。
		// IDs 以接口默认值为准，不能借用抽屉中上一个药品的列表和索引。
		buildMedObjFromDefault(med, defaultData, dataList) {
			const def = defaultData || {};
			const lists = dataList || {};
			const durationID = def.ordDurationID || ((lists.durationList || [])[0] && (lists.durationList || [])[0].id) || '';
			return this.buildMedObj(med, {
				remark: '',
				relMedFlag: 'N',
				dose: def.ordDose === undefined || def.ordDose === null ? '' : String(def.ordDose),
				stepper: Number(def.ordDurQty) || 1,
				qty: Number(def.ordQty) || 1,
				ordSkinTestFlag: def.ordSkinTestFlagValue === 'Y' ? 'Y' : 'N',
				skinTestNote: this.optionDesc(lists.skinTestWayList || [], 'id', def.ordSkinTestUsageID, 'descripts'),
				doseUnit: this.optionDesc(lists.doseUnitList || [], 'doseUnitID', def.ordDoseUnitID, 'doseUnitDesc'),
				freqCode: '',
				usageCode: '',
				courseUom: this.optionDesc(lists.durationList || [], 'id', durationID, 'descripts') || '天',
				qtyUomID: this.optionDesc(lists.qtyUnitList || [], 'unitID', def.ordQtyUomID, 'unitDesc'),
				recLocID: this.optionDesc(lists.recLocList || [], 'recLocID', def.ordRecLocID, 'recLocDesc'),
				settleType: ''
			}, {
				ordPriorityID: def.ordPriorityID || '10',
				ordAdmReasonID: def.ordAdmReasonID || '',
				ordUsageID: def.ordUsageID || '',
				ordFreqID: def.ordFreqID || '',
				ordDoseUnitID: def.ordDoseUnitID || '',
				ordQtyUomID: def.ordQtyUomID || '',
				ordDurationID: durationID,
				ordRecLocID: def.ordRecLocID || ''
			});
		},
		validateWestOrders(westOrders) {
			const requiredFields = [
				['medID', '药品'],
				['ordAdmReasonID', '结算类型'],
				['ordUsageID', '用法'],
				['ordFreqID', '频次'],
				['ordDose', '剂量'],
				['ordDoseUnitID', '剂量单位'],
				['ordQty', '数量'],
				['ordQtyUomID', '数量单位'],
				['ordDurQty', '疗程'],
				['ordDurationID', '疗程单位'],
				['ordRecLocID', '执行科室']
			];
			for (const order of westOrders) {
				const missing = requiredFields.find(([field]) => order[field] === '' || order[field] === null || order[field] === undefined);
				if (missing) return (order.medID || '药品') + '缺少' + missing[1] + '，请补全后保存';
			}
			return '';
		},
		addAllMedsWithDefaults() {
			const missingMeds = this.medList.filter((med) => !this.medObjMap[med.medID]);
			if (!missingMeds.length) return;
			const patientData = Cache.get('patientData') || {};
			$http.post('urlDeault', this, {
				code: '03050020',
				data: {
					params: [{
						admID: patientData.admID || '',
						orders: missingMeds.map((med) => ({ medID: med.medID || '', ordSetID: med.ordSetID || '' }))
					}]
				},
				success: (res) => {
					if (res.errorCode !== '0' || !Array.isArray(res.data)) {
						uni.showToast({ title: res.errorMessage || '获取药品默认数据失败', icon: 'none' });
						return;
					}
					const defaultsByMedID = {};
					res.data.forEach((result) => {
						const def = result && result.defaultData;
						if (def && def.medID) defaultsByMedID[def.medID] = result;
					});
					const medObjMap = Object.assign({}, this.medObjMap);
					const medQtyMap = Object.assign({}, this.medQtyMap);
					const unavailable = [];
					missingMeds.forEach((med) => {
						// 请求期间用户可能已手工配置该药，不能覆盖其结果。
						if (medObjMap[med.medID]) return;
						const result = defaultsByMedID[med.medID];
						if (!result || !result.defaultData) {
							unavailable.push(med.medDesc || med.tradeName || med.medID);
							return;
						}
						const order = this.buildMedObjFromDefault(med, result.defaultData, result.dataList || {});
						medObjMap[med.medID] = order;
						medQtyMap[med.medID] = order.qty;
					});
					this.setData({ medObjMap, medQtyMap });
					if (unavailable.length) {
						uni.showToast({ title: '部分药品未获取到默认医嘱，未加入', icon: 'none' });
					}
				},
				fail: () => uni.showToast({ title: '获取药品默认数据失败', icon: 'none' })
			});
		},
		buildCheckObj(it, qty) {
			return {
				appOrderID: it.appOrderID,
				medID: it.medID,
				medDesc: it.medDesc,
				mainCode: it.orderTypeCode,
				CBid: it.CBid,
				HBid: it.HBid,
				medPrice: it.medPrice,
				qty: qty || 1,
				ordTypeCode: it.orderTypeCode
			};
		},

		/* ============ 派生数据源（按钮组） ============ */
		// 03050020 医嘱项详情：剂量单位/数量单位/规格/皮试方式/执行科室 + 默认值
		selectDataSome() {
			const patientData = Cache.get('patientData');
			const admID = this.admID || (patientData && patientData.admID) || '';
			const { medID, ordSetID } = this;
			if (!medID || medID === '') {
				return;
			}
			$http.post('urlDeault', this, {
				code: '03050020',
				data: {
					params: [{
						admID: admID,
						orders: [{
							medID: medID,
							ordSetID: ordSetID || ''
						}]
					}]
				},
				success: (res) => {
					// 药品切换后旧请求可能晚到；不能用旧药品的下拉数据覆盖当前抽屉。
					if (this.medID !== medID) return;
					const errorData = (res.errorData && res.errorData[0]) || null;
					if (res.errorCode !== '0' || !res.data || !res.data[0]) {
						return;
					}
					if (errorData) {
                        Dialog.alert({
                            title: '提示',
                            message: errorData.errorMessage,
                            context: this
                        });
                        return false;
                    }
					this.setData({ showSheet: true });
					this.selectData1Fun();
					this.selectData2Fun();
					this.selectData3Fun();
					const dataList = res.data[0].dataList || {};
					const defaultData = res.data[0].defaultData || {};
					const doseUnitList = dataList.doseUnitList || [];
					const qtyUnitList = dataList.qtyUnitList || [];
					const specList = dataList.specList || [];
					const skinTestWayList = dataList.skinTestWayList || [];
					const recLocList = dataList.recLocList || [];
					const durationList = dataList.durationList || [];
					// 默认选中索引（无匹配时 picakIndex 兜底返回 0）
					const idx4 = this.picakIndex('doseUnitID', defaultData.ordDoseUnitID, doseUnitList);
					const idx5 = this.picakIndex('unitID', defaultData.ordQtyUomID, qtyUnitList);
					const idx7 = this.picakIndex('id', defaultData.ordSkinTestUsageID, skinTestWayList);
					const idx8 = this.picakIndex('recLocID', defaultData.ordRecLocID, recLocList);
					// 默认值回填 attrForm（描述文本）
					const attrForm = Object.assign({}, this.attrForm);
					if (defaultData.ordDose !== undefined && defaultData.ordDose !== null && defaultData.ordDose !== '') {
						attrForm.dose = String(defaultData.ordDose);
					}
					if (defaultData.ordDurQty !== undefined && defaultData.ordDurQty !== null && defaultData.ordDurQty !== '') {
						attrForm.stepper = Number(defaultData.ordDurQty) || 1;
					}
					if (defaultData.ordQty !== undefined && defaultData.ordQty !== null && defaultData.ordQty !== '') {
						attrForm.qty = Number(defaultData.ordQty) || 1;
					}
					if (doseUnitList[idx4]) {
						attrForm.doseUnit = doseUnitList[idx4].doseUnitDesc;
					}
					if (qtyUnitList[idx5]) {
						attrForm.qtyUomID = qtyUnitList[idx5].unitDesc;
					}
					if (skinTestWayList[idx7]) {
						attrForm.skinTestNote = skinTestWayList[idx7].descripts;
					}
					if (recLocList[idx8]) {
						attrForm.recLocID = recLocList[idx8].recLocDesc;
					}
					attrForm.ordSkinTestFlag = defaultData.ordSkinTestFlagValue === 'Y' ? 'Y' : 'N';
					// 疗程单位：参照 old 版前端截取，只保留第一项（天），不要周/月
					let courseUnitList = this.courseUnitList;
					let picakIndex9 = this.picakIndex9;
					if (durationList.length) {
						courseUnitList = [durationList[0]];
						picakIndex9 = 0;
						if (durationList[0] && durationList[0].descripts) {
							attrForm.courseUom = durationList[0].descripts;
						}
					}
					// 缓存默认 ID（供各数据源回调统一回填选中）
					this.ordFreqID = defaultData.ordFreqID || '';
					this.ordUsageID = defaultData.ordUsageID || '';
					this.ordAdmReasonID = defaultData.ordAdmReasonID || '';
					this.ordPriorityID = defaultData.ordPriorityID || '';
					this.ordDoseUnitID = defaultData.ordDoseUnitID || '';
					this.ordQtyUomID = defaultData.ordQtyUomID || '';
					this.ordSkinTestUsageID = defaultData.ordSkinTestUsageID || '';
					this.ordRecLocID = defaultData.ordRecLocID || '';
					const newData = {
						selectData4: doseUnitList,
						selectData5: qtyUnitList,
						selectData6: specList,
						selectData7: skinTestWayList,
						selectData8: recLocList,
						courseUnitList,
						attrForm,
						picakIndex4: idx4,
						picakIndex5: idx5,
						picakIndex7: idx7,
						picakIndex8: idx8,
						picakIndex9
					};
					// 规格回显到药品卡片
					if (defaultData.spec) {
						newData.sheetMed = Object.assign({}, this.sheetMed, { spec: defaultData.spec });
					}
					// 03050020 返回的价格/库存/库存位置回显到药品卡片（覆盖列表数据，确保弹窗展示接口实时值）
					const sheetMedPatch = {
						medPrice: defaultData.medPrice !== undefined ? defaultData.medPrice : this.sheetMed.medPrice,
						medStock: defaultData.medStock !== undefined ? defaultData.medStock : this.sheetMed.medStock,
						ordRecLocDesc: defaultData.ordRecLocDesc !== undefined ? defaultData.ordRecLocDesc : (this.sheetMed.ordRecLocDesc || this.sheetMed.recLocDesc)
					};
					newData.sheetMed = Object.assign({}, (newData.sheetMed || this.sheetMed), sheetMedPatch);
					if (this.medObjMap[this.medID]) {
						// 编辑模式：以已保存配置为准回显（防止接口默认值覆盖用户已配置内容）
						newData.attrForm = this.buildAttrFormFromMed(this.medObjMap[this.medID]);
					}
					this.setData(newData, () => {
						// 频次/用法/结算类型接口可能已先返回；此处按当前药品固化 ID 回显。
						this.syncPickIndex();
						this.backfillAsyncDefaults();
					});
				}
			});
		},
		// 属性默认值统一回填（各数据源就绪后调用，幂等；仅处理用户未选择/未回显的项）：
		// 1) 有默认 ID 且能匹配到 -> 选中默认项
		// 2) 结算类型默认 ID 未匹配 -> 按 defaultFlag === 'Y' 兜底（old 版规则）
		// 3) 无默认值 -> 默认选中第一条
		backfillAsyncDefaults() {
			const patch = {};
			let attrForm = Object.assign({}, this.attrForm);
			// [list, idField, defaultId, attrKey, pickKey, descField]
			const groups = [
				[this.selectData1, 'id', this.ordAdmReasonID, 'settleType', 'picakIndex1', 'descripts'],
				[this.selectData2, 'id', this.ordFreqID, 'freqCode', 'picakIndex2', 'descripts'],
				[this.selectData3, 'id', this.ordUsageID, 'usageCode', 'picakIndex3', 'descripts'],
				[this.selectData4, 'doseUnitID', this.ordDoseUnitID, 'doseUnit', 'picakIndex4', 'doseUnitDesc'],
				[this.selectData5, 'unitID', this.ordQtyUomID, 'qtyUomID', 'picakIndex5', 'unitDesc'],
				[this.selectData7, 'id', this.ordSkinTestUsageID, 'skinTestNote', 'picakIndex7', 'descripts'],
				[this.selectData8, 'recLocID', this.ordRecLocID, 'recLocID', 'picakIndex8', 'recLocDesc']
			];
			for (const [list, idField, defaultId, attrKey, pickKey, descField] of groups) {
				if (!list || !list.length) continue;
				if (attrForm[attrKey]) continue; // 已选择/已回显，保留
				let idx = -1;
				if (defaultId !== undefined && defaultId !== null && defaultId !== '') {
					idx = list.findIndex((it) => it && String(it[idField]) === String(defaultId));
				}
				// 结算类型：默认 ID 未匹配时按 defaultFlag === 'Y' 兜底
				if (idx < 0 && attrKey === 'settleType') {
					idx = list.findIndex((it) => it && it.defaultFlag === 'Y');
				}
				// 无默认值 -> 第一条
				if (idx < 0) idx = 0;
				patch[pickKey] = idx;
				attrForm = Object.assign({}, attrForm, { [attrKey]: list[idx][descField] });
			}
			if (Object.keys(patch).length) {
				patch.attrForm = attrForm;
				this.setData(patch);
			}
			return Object.keys(patch).length > 0;
		},
		// 在数组中按字段值查找下标（old 版 picakIndex），找不到返回 0
		picakIndex(name, id, arr) {
			if (!arr || !arr.length || id === '' || id === null || id === undefined) {
				return 0;
			}
			for (let i = 0; i < arr.length; i++) {
				if (arr[i] && arr[i][name] == id) {
					return parseInt(i, 10);
				}
			}
			return 0;
		},
		// 结算类型 03100048（old 版接口；响应含 defaultFlag === 'Y' 的默认项）
		selectData1Fun() {
			const patientData = Cache.get('patientData');
			const userData = Cache.get('userData');
			const { hospID } = userData;
			const { patID, admID } = patientData;
			const medID = this.medID;
			$http.post('urlDeault', this, {
				code: '03100048',
				data: {
					params: [{
						admID: admID || '',
						patID: patID || '',
						hospID: hospID || ''
					}]
				},
				success: (res) => {
					if (this.medID !== medID) return;
					if (res.errorCode === '0') {
						// old 版接口响应可能在 res.data，兼容两种位置
						const list = res.result || res.data || [];
						this.setData({ selectData1: list }, () => this.syncPickIndex());
						// 默认值回填：默认 ID 匹配 -> defaultFlag === 'Y' -> 第一条
						this.backfillAsyncDefaults();
					}
				}
			});
		},
		// 频次 03050143（old 版接口）
		selectData2Fun() {
			const patientData = Cache.get('patientData');
			const userData = Cache.get('userData');
			const { hospID } = userData;
			const { admID } = patientData;
			const { medID } = this;
			$http.post('urlDeault', this, {
				code: '03050143',
				data: {
					params: [{
						medID: medID || '',
						admID: admID || '',
						hospID: hospID || ''
					}]
				},
				success: (res) => {
					if (this.medID !== medID) return;
					if (res.errorCode == '0') {
						// old 版接口响应可能在 res.data（同用法 01040065），兼容两种位置
						this.setData({ selectData2: res.result || res.data || [] }, () => this.syncPickIndex());
						this.backfillAsyncDefaults();
					}
				}
			});
		},
		// 用法 01040065（old 版接口；响应在 res.data）
		selectData3Fun() {
			const userData = Cache.get('userData');
			const { hospID } = userData;
			const { medID } = this;
			$http.post('urlDeault', this, {
				code: '01040065',
				data: {
					params: [{
						medID: medID || '',
						hospID: hospID || ''
					}]
				},
				success: (res) => {
					if (this.medID !== medID) return;
					if (res.errorCode === '0') {
						this.setData({ selectData3: res.data || [] }, () => this.syncPickIndex());
						this.backfillAsyncDefaults();
					}
				}
			});
		},
		// 选中药品解锁派生数据
		searchSelectFun(med) {
			this.setData({
				medID: med.medID,
				medDesc: med.medDesc,
				ordSetID: med.ordSetID || '',
				ordTypeCode: med.ordTypeCode || 'P'
			}, () => {
				this.selectDataSome();
			});
		},
		// 依据已配置医嘱（或药品）初始化抽屉表单
		buildAttrFormFromMed(med) {
			return {
				remark: med.remark || '',
				relMedFlag: med.relMedFlag || 'N',
				dose: med.dose || '',
				stepper: med.stepper || med.stepper2 || 3,
				qty: med.qty || med.stepper2 || 1,
				ordSkinTestFlag: med.ordSkinTestFlag || 'N',
				skinTestNote: med.skinTestNote || '',
				doseUnit: med.doseUnit || '',
				freqCode: med.freqCode || '',
				usageCode: med.usageCode || '',
				courseUom: med.courseUom || '天',
				qtyUomID: med.qtyUomID || '',
				recLocID: med.recLocID || '',
				settleType: med.settleType || ''
			};
		},
		// 构建最终保存对象。所有需提交的 ID 都随药品对象固化，保存阶段不再读取页面全局索引。
		// orderIDs 仅供批量全选的接口默认值注入；普通抽屉确认时从当前抽屉的选中项读取。
		buildMedObj(med, f, orderIDs) {
			const qty = Number(f.qty) || 1;
			const ids = orderIDs || {};
			const hasID = (key) => Object.prototype.hasOwnProperty.call(ids, key);
			const selectedID = (key, listKey, pickKey, idField) => {
				if (hasID(key)) return ids[key] || '';
				return this.listId(listKey, pickKey, idField) || med[key] || '';
			};
			const selectedDesc = (key, listKey, pickKey, descField, fallback) => {
				if (hasID(key)) return fallback || '';
				const list = this[listKey] || [];
				const index = this[pickKey];
				return list[index] ? (list[index][descField] || '') : (fallback || '');
			};
			return {
				medID: med.medID,
				medDesc: med.medDesc || med.tradeName,
				remark: f.remark || '',
				relMedFlag: f.relMedFlag || 'N',
				dose: f.dose,
				doseUnit: selectedDesc('ordDoseUnitID', 'selectData4', 'picakIndex4', 'doseUnitDesc', f.doseUnit),
				freqCode: selectedDesc('ordFreqID', 'selectData2', 'picakIndex2', 'descripts', f.freqCode),
				usageCode: selectedDesc('ordUsageID', 'selectData3', 'picakIndex3', 'descripts', f.usageCode),
				stepper: Number(f.stepper) || 3,
				qty: qty,
				stepper2: qty, // 兼容原 stepper2 字段（其他模块仍按数量读取）
				courseUom: selectedDesc('ordDurationID', 'courseUnitList', 'picakIndex9', 'descripts', f.courseUom || '天'),
				qtyUomID: selectedDesc('ordQtyUomID', 'selectData5', 'picakIndex5', 'unitDesc', f.qtyUomID),
				ordSkinTestFlag: f.ordSkinTestFlag || 'N',
				skinTestNote: f.ordSkinTestFlag === 'Y' ? (this.selectData7[this.picakIndex7] ? this.selectData7[this.picakIndex7].descripts : f.skinTestNote) : '',
				recLocID: selectedDesc('ordRecLocID', 'selectData8', 'picakIndex8', 'recLocDesc', f.recLocID),
				settleType: selectedDesc('ordAdmReasonID', 'selectData1', 'picakIndex1', 'descripts', f.settleType),
				medPrice: med.medPrice,
				ordTypeCode: med.ordTypeCode || 'P',
				// 以下是保存接口真正使用的 ID。它们必须与当前 medID 绑定保存，
				// 不能在 saveAll 时再通过全局下拉索引反查。
				ordPriorityID: selectedID('ordPriorityID', '', '', '') || this.ordPriorityID || '10',
				ordAdmReasonID: selectedID('ordAdmReasonID', 'selectData1', 'picakIndex1', 'id'),
				ordUsageID: selectedID('ordUsageID', 'selectData3', 'picakIndex3', 'id'),
				ordFreqID: selectedID('ordFreqID', 'selectData2', 'picakIndex2', 'id'),
				ordDoseUnitID: selectedID('ordDoseUnitID', 'selectData4', 'picakIndex4', 'doseUnitID'),
				ordQtyUomID: selectedID('ordQtyUomID', 'selectData5', 'picakIndex5', 'unitID'),
				ordDurationID: selectedID('ordDurationID', 'courseUnitList', 'picakIndex9', 'id'),
				ordRecLocID: selectedID('ordRecLocID', 'selectData8', 'picakIndex8', 'recLocID')
			};
		},
		// 编辑回显
		editDataFun(e) {
			const d = e.detail;
			this.setData({ editList: d.editList || [], editInfo: d });
			// 预填药品已选
			let medObjMap = Object.assign({}, this.medObjMap);
			let medQtyMap = Object.assign({}, this.medQtyMap);
			let treatQtyMap = Object.assign({}, this.treatQtyMap);
			let selectedMap = Object.assign({}, this.selectedMap);
			(d.editList || []).forEach((it) => {
				// 检验检查/治疗：保存对象带 appOrderID / mainCode（buildCheckObj 产物）
				if (it.appOrderID || it.mainCode) {
					if (it.ordTypeCode === 'O' || it.mainCode === 'O') treatQtyMap[it.medID || it.appOrderID] = it.qty || 1;
					else selectedMap[it.medID || it.appOrderID] = it;
				} else if (it.medID) {
					medObjMap = Object.assign({}, medObjMap, { [it.medID]: it });
					medQtyMap = Object.assign({}, medQtyMap, { [it.medID]: it.qty || it.stepper2 || 1 });
				}
			});
			this.setData({ medObjMap, medQtyMap, treatQtyMap, selectedMap });
		},
		// 交回父组件
		addInfo(obj) {
			this.$emit('addInfo', { detail: obj || {} });
		}
	}
};
</script>
<style scoped>
.western {
	position: relative;
	display: flex;
	flex-direction: column;
	flex: 1;
	min-height: 0;
	height: 100%;
	background: #fafbfc;
	overflow: hidden;
	box-sizing: border-box;
	padding-bottom: 100rpx;
}
/* 栏内搜索框 */
.ie-search {
	display: flex;
	align-items: center;
	padding: 12rpx 16rpx;
	background: #fff;
	border-bottom: 1rpx solid #eef0f3;
}
.ie-search__box {
	flex: 1;
	display: flex;
	align-items: center;
	background: #f2f4f7;
	border-radius: 32rpx;
	padding: 0 8rpx 0 20rpx;
}
.ie-search__input {
	flex: 1;
	height: 60rpx;
	font-size: 24rpx;
	color: #1f2937;
	background: transparent;
}
.ie-search__btn {
	font-size: 24rpx;
	color: #fff;
	background: #1890ff;
	border-radius: 28rpx;
	margin-left: 12rpx;
	padding: 10rpx 24rpx;
	white-space: nowrap;
}
.ie-ph {
	color: #9ca3af;
}
/* 双栏 */
.ie-body {
	flex: 1;
	display: flex;
	overflow: hidden;
}
.ie-col {
	display: flex;
	flex-direction: column;
	min-height: 0;
	height: 100%;
}
.ie-col--left {
	width: 39%;
	background: #f5f7fa;
	border-right: 1rpx solid #eef0f3;
}
.ie-col--right {
	width: 61%;
	background: #fafbfc;
}
.ie-right-head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 16rpx 24rpx;
	background: #fff;
	border-bottom: 1rpx solid #eef0f3;
}
.ie-right-head__info {
	font-size: 26rpx;
	color: #6b7280;
}
.ie-right-head__num {
	color: #1890ff;
	font-weight: 600;
}
.ie-right-head__all {
	display: flex;
	align-items: center;
	font-size: 26rpx;
	color: #1f2937;
}
.ie-right-head__all .wa-checkbox {
	margin-left: 12rpx;
}
.ie-scroll {
	flex: 1;
	min-height: 0;
	height: 100%;
	padding: 12rpx 16rpx;
	box-sizing: border-box;
}
.ie-scroll--tree {
	padding: 8rpx 0;
}
.ie-tree-search {
	padding: 16rpx;
	background: #fff;
	border-bottom: 1rpx solid #f0f0f0;
	display: flex;
	align-items: center;
	position: relative;
}
.ie-tree-search__input {
	flex: 1;
	height: 64rpx;
	background: #f5f7fa;
	border-radius: 32rpx;
	padding: 0 24rpx;
	font-size: 26rpx;
	color: #1f2937;
}
.ie-tree-search__clear {
	position: absolute;
	right: 36rpx;
	top: 50%;
	transform: translateY(-50%);
	width: 36rpx;
	height: 36rpx;
	border-radius: 50%;
	background: #d1d5db;
	color: #fff;
	font-size: 22rpx;
	text-align: center;
	line-height: 36rpx;
}
.ie-empty {
	padding: 60rpx 0;
	text-align: center;
	font-size: 24rpx;
	color: #9ca3af;
}
/* 药品卡片 */
.wa-card {
	display: flex;
	align-items: stretch;
	padding: 20rpx;
	margin-bottom: 16rpx;
	background: #fff;
	border-radius: 16rpx;
	box-shadow: 0 2rpx 12rpx rgba(31, 41, 55, 0.04);
}
.wa-card__main {
	flex: 1;
	min-width: 0;
}
.wa-card__name {
	font-size: 28rpx;
	font-weight: 600;
	color: #1f2937;
	line-height: 1.4;
}
.wa-card__sub {
	display: flex;
	flex-wrap: wrap;
	margin-top: 8rpx;
}
.wa-card__tag {
	font-size: 20rpx;
	color: #1890ff;
	background: #eef6ff;
	border-radius: 6rpx;
	padding: 4rpx 10rpx;
	margin-right: 10rpx;
}
.wa-card__tag--gray {
	color: #6b7280;
	background: #f3f4f6;
}
.wa-card__tag--blue {
	color: #0ea5e9;
	background: #e0f2fe;
}
.wa-card__meta {
	display: flex;
	align-items: center;
	margin-top: 10rpx;
}
.wa-card__price {
	font-size: 26rpx;
	color: #ef4444;
	font-weight: 600;
}
.wa-card__stock {
	font-size: 20rpx;
	color: #9ca3af;
	margin-left: 16rpx;
}
.wa-card__action {
	display: flex;
	align-items: center;
	justify-content: center;
}
.wa-checkbox {
	width: 44rpx;
	height: 44rpx;
	border-radius: 50%;
	border: 2rpx solid #d1d5db;
	display: flex;
	align-items: center;
	justify-content: center;
	margin-left: 16rpx;
	flex-shrink: 0;
}
.wa-checkbox--on {
	background: #1890ff;
	border-color: #1890ff;
}
.wa-checkbox__tick {
	color: #fff;
	font-size: 26rpx;
}
.wa-stepper {
	display: flex;
	align-items: center;
}
.wa-stepper--inline {
	margin-top: 0;
}
.wa-stepper__btn {
	width: 48rpx;
	height: 48rpx;
	border-radius: 50%;
	background: #eef6ff;
	color: #1890ff;
	font-size: 36rpx;
	line-height: 48rpx;
	text-align: center;
}
.wa-stepper__num {
	min-width: 44rpx;
	text-align: center;
	font-size: 26rpx;
	color: #1f2937;
}
/* 检验检查/治疗卡片 */
.wa-card--check .wa-card__action {
	width: auto;
}
.wa-card--treat .wa-card__action {
	width: auto;
	justify-content: flex-end;
}
.wa-card__add {
	width: 56rpx;
	height: 56rpx;
	border-radius: 50%;
	background: #1890ff;
	color: #fff;
	font-size: 40rpx;
	line-height: 56rpx;
	text-align: center;
	font-weight: 300;
}

/* 底部保存栏：固定在底部（配合 .western 纵向 flex，.ie-body flex:1 滚动） */
.ie-footer {
	flex-shrink: 0;
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 16rpx 28rpx;
	background: #fff;
	border-top: 1rpx solid #eef0f3;
	position: fixed;
	bottom: 0;
	left: 0;
	width: 100%;
}
.ie-footer__info {
	font-size: 26rpx;
	color: #6b7280;
}
.ie-footer__num {
	color: #1890ff;
	font-weight: 600;
	font-size: 30rpx;
}
.ie-footer__btn {
	padding: 16rpx 56rpx;
	background: #1890ff;
	color: #fff;
	font-size: 28rpx;
	border-radius: 36rpx;
	display: flex;
	align-items: center;
	justify-content: center;
}
.ie-footer__btn--loading {
	background: #a5c9f5;
}
.ie-footer__btn--disabled {
	background: #c0c4cc;
}
.ie-footer__loading {
	width: 28rpx;
	height: 28rpx;
	margin-right: 12rpx;
	border: 3rpx solid rgba(255, 255, 255, 0.4);
	border-top-color: #fff;
	border-radius: 50%;
	animation: ieSpin 0.8s linear infinite;
}
@keyframes ieSpin {
	from { transform: rotate(0deg); }
	to { transform: rotate(360deg); }
}
/* 底部抽屉 */
.wa-mask {
	position: fixed;
	left: 0;
	right: 0;
	top: 0;
	bottom: 0;
	background: rgba(0, 0, 0, 0.45);
	z-index: 100;
	display: flex;
	align-items: flex-end;
}
.wa-sheet {
	width: 100%;
	max-height: 88vh;
	background: #fff;
	border-radius: 22rpx 22rpx 0 0;
	display: flex;
	flex-direction: column;
	animation: waUp 0.25s ease;
}
@keyframes waUp {
	from {
		transform: translateY(100%);
	}
	to {
		transform: translateY(0);
	}
}
.wa-sheet__handle {
	width: 60rpx;
	height: 6rpx;
	border-radius: 4rpx;
	background: #e5e7eb;
	margin: 12rpx auto 0;
}
.wa-sheet__head {
	display: flex;
	align-items: center;
	justify-content: space-between;
	padding: 16rpx 24rpx;
	border-bottom: 1rpx solid #f3f4f6;
}
.wa-sheet__title {
	font-size: 30rpx;
	font-weight: 600;
	color: #1f2937;
}
.wa-sheet__close {
	font-size: 32rpx;
	color: #9ca3af;
}
.wa-sheet__body {
	flex: 1;
	overflow: hidden;
	padding: 4rpx 24rpx 14rpx;
	overflow: auto;
}
.wa-sheet__footer {
	flex-shrink: 0;
	padding: 16rpx 28rpx calc(16rpx + env(safe-area-inset-bottom));
	border-top: 1rpx solid #f3f4f6;
}
.wa-sheet__footer-btn {
	padding: 20rpx 0;
	background: #1890ff;
	color: #fff;
	font-size: 30rpx;
	font-weight: 600;
	border-radius: 40rpx;
	display: flex;
	align-items: center;
	justify-content: center;
}
.wa-sheet__footer-btn--disabled {
	background: #c0c4cc;
}
.wa-medcard {
	display: flex;
	align-items: center;
	background: #f0f7ff;
	border-radius: 16rpx;
	padding: 16rpx 20rpx;
	margin: 12rpx 24rpx 2rpx;
}
.wa-medcard__icon {
	flex-shrink: 0;
	width: 76rpx;
	height: 76rpx;
	border-radius: 50%;
	background: linear-gradient(135deg, #60a5fa, #1890ff);
	display: flex;
	align-items: center;
	justify-content: center;
	margin-right: 16rpx;
}
.wa-medcard__icon-text {
	color: #fff;
	font-size: 30rpx;
	font-weight: 600;
	line-height: 1;
}
.wa-medcard__main {
	flex: 1;
	min-width: 0;
}
.wa-medcard__top {
	display: flex;
	justify-content: space-between;
	align-items: center;
	margin-bottom: 4rpx;
}
.wa-medcard__label {
	font-size: 22rpx;
	color: #6b7280;
}
.wa-medcard__hint {
	font-size: 22rpx;
	color: #1890ff;
}
.wa-medcard__name {
	font-size: 28rpx;
	font-weight: 600;
	color: #111827;
	line-height: 1.4;
}
.wa-medcard__spec {
	font-size: 22rpx;
	color: #6b7280;
	margin-top: 4rpx;
}
.wa-medcard__info {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: 16rpx;
	margin-top: 10rpx;
}
.wa-medcard__price {
	font-size: 24rpx;
	font-weight: 600;
	color: #ff4d4f;
}
.wa-medcard__stock {
	font-size: 22rpx;
	color: #1890ff;
	background: #e6f4ff;
	border-radius: 8rpx;
	padding: 2rpx 12rpx;
}
.wa-medcard__loc {
	font-size: 22rpx;
	color: #52c41a;
	background: #f6ffed;
	border-radius: 8rpx;
	padding: 2rpx 12rpx;
}
.wa-row {
	display: flex;
	align-items: flex-start;
}
.wa-col {
	flex: 1;
	min-width: 0;
	padding-right: 12rpx;
}
.wa-col + .wa-col {
	padding-right: 0;
	padding-left: 12rpx;
}
.wa-field {
	padding: 12rpx 0;
	border-bottom: 1rpx solid #f3f4f6;
}
.wa-field__label {
	font-size: 23rpx;
	color: #6b7280;
	margin-bottom: 8rpx;
}
.wa-field__required {
	color: #ef4444;
	font-size: 24rpx;
	margin-right: 4rpx;
	line-height: 1;
}
.wa-field__input {
	height: 56rpx;
	padding: 0 16rpx;
	background: #f5f7fa;
	border-radius: 10rpx;
	font-size: 24rpx;
	color: #1f2937;
}
.wa-fbtn-group {
	display: flex;
	flex-wrap: wrap;
}
.wa-fbtn {
	padding: 10rpx 24rpx;
	margin: 0 16rpx 16rpx 0;
	background: #f3f4f6;
	color: #374151;
	font-size: 24rpx;
	border-radius: 12rpx;
	line-height: 1.2;
	max-width: 100%;
}
.wa-fbtn--on {
	background: #e6f4ff;
	color: #1890ff;
	border: 1rpx solid #1890ff;
}
.wa-fbtn--info {
	background: #f3f4f6;
	color: #9ca3af;
}
.wa-fstepper {
	display: flex;
	align-items: center;
	height: 56rpx;
}
.wa-fstepper__btn {
	width: 56rpx;
	height: 56rpx;
	line-height: 52rpx;
	text-align: center;
	background: #f5f7fa;
	color: #1890ff;
	font-size: 32rpx;
	border-radius: 10rpx;
}
.wa-fstepper__num {
	flex: 1;
	min-width: 0;
	text-align: center;
	font-size: 26rpx;
	color: #1f2937;
	line-height: 56rpx;
}
.wa-sheet__foot {
	display: flex;
	gap: 16rpx;
	padding: 14rpx 24rpx calc(14rpx + env(safe-area-inset-bottom));
	border-top: 1rpx solid #f3f4f6;
	background: #fff;
}
.wa-foot-btn {
	flex: 1;
	height: 72rpx;
	line-height: 72rpx;
	text-align: center;
	font-size: 26rpx;
	border-radius: 14rpx;
}
.wa-foot-btn--ghost {
	background: #f5f7fa;
	color: #4b5563;
}
.wa-foot-btn--primary {
	background: #1890ff;
	color: #fff;
	font-weight: 500;
}
.wa-attr {
	padding: 16rpx 0;
}
.wa-attr__label {
	font-size: 24rpx;
	color: #6b7280;
	margin-bottom: 12rpx;
}
.wa-attr__input {
	height: 64rpx;
	padding: 0 20rpx;
	background: #f5f7fa;
	border-radius: 12rpx;
	font-size: 26rpx;
	color: #1f2937;
}

/* 已选医嘱弹窗 */
.wa-mask--top {
	z-index: 1010;
}
.wa-sheet__close {
	font-size: 36rpx;
	color: #9ca3af;
	padding: 0 12rpx;
}
.ie-empty {
	padding: 60rpx 0;
	text-align: center;
	font-size: 26rpx;
	color: #9ca3af;
}
.sel-item {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	padding: 24rpx 0;
	border-bottom: 1rpx solid #f3f4f6;
}
.sel-item:last-child {
	border-bottom: none;
}
.sel-item__main {
	flex: 1;
	min-width: 0;
	margin-right: 20rpx;
}
.sel-item__name {
	align-items: center;
	font-size: 30rpx;
	font-weight: 600;
	color: #1f2937;
}
.sel-item__tag {
	font-size: 20rpx;
	font-weight: normal;
	padding: 2rpx 8rpx;
	border-radius: 8rpx;
	margin-left: 10rpx;
	white-space: nowrap;
}
.sel-item__tag--blue {
	background: #e6f4ff;
	color: #1890ff;
}
.sel-item__tag--gray {
	background: #f3f4f6;
	color: #6b7280;
}
.sel-item__desc {
	margin-top: 8rpx;
	font-size: 24rpx;
	color: #6b7280;
	line-height: 1.5;
	word-break: break-all;
}
.sel-item__action {
	display: flex;
	align-items: center;
	gap: 16rpx;
	flex-shrink: 0;
}
.wa-stepper {
	display: flex;
	align-items: center;
	border: 1rpx solid #d1d5db;
	border-radius: 28rpx;
	padding: 0 8rpx;
}
.wa-stepper__btn {
	width: 48rpx;
	height: 48rpx;
	line-height: 48rpx;
	text-align: center;
	font-size: 28rpx;
	color: #1890ff;
}
.wa-stepper__num {
	min-width: 44rpx;
	text-align: center;
	font-size: 26rpx;
	color: #1f2937;
}
.sel-item__edit,
.sel-item__del {
	font-size: 26rpx;
	padding: 0 4rpx;
}
.sel-item__edit {
	color: #1890ff;
}
.sel-item__del {
	color: #ff4d4f;
}
</style>
