修复了一些问题

This commit is contained in:
李琦
2026-05-15 16:28:11 +08:00
parent cd369ddf09
commit 6294a3db0c
9 changed files with 572 additions and 417 deletions

View File

@@ -1,300 +1,386 @@
<template>
<view class="follow-up-drug-drawer">
<view class="trigger" @click="openPopup">
<view class="trigger-main">
<text v-if="!displaySummary" class="placeholder">请选择药品</text>
<text v-else class="summary">{{ displaySummary }}</text>
</view>
<u-icon name="arrow-right" color="#94A3B8" size="28"></u-icon>
</view>
<u-popup
v-model="popupVisible"
mode="bottom"
border-radius="24"
height="66%"
:safe-area-inset-bottom="true"
:closeable="true"
@close="onPopupClose"
>
<view class="drawer-inner">
<view class="drawer-title">选择相关西药</view>
<view v-if="!drugList.length" class="drawer-empty">暂无可选西药</view>
<scroll-view v-else scroll-y class="drawer-scroll">
<view
v-for="d in drugList"
:key="d.id"
class="drawer-row"
>
<view class="drug-check" :class="{ on: getQty(d.id) > 0 }" @click="toggleRow(d)">
<text v-if="getQty(d.id) > 0" class="check-mark">✓</text>
</view>
<image class="drug-thumb" :src="drugImage(d)" mode="aspectFill" @click="toggleRow(d)" />
<view class="drug-meta" @click="toggleRow(d)">
<text class="dn">{{ d.drug_name }}</text>
<text class="ds" v-if="d.specification">{{ d.specification }}</text>
</view>
<view class="drawer-row-actions">
<u-number-box
:value="getQty(d.id)"
:min="0"
:max="99"
:size="22"
:index="d.id"
@input="onQtyInput(d, $event)"
/>
</view>
</view>
</scroll-view>
<view class="drawer-footer safe-area-inset-bottom">
<view class="btn-text" @click="clearDraft">清空</view>
<view class="btn-primary" @click="confirm">确定</view>
</view>
</view>
</u-popup>
</view>
</template>
<script>
export default {
name: 'FollowUpDrugDrawer',
props: {
drugList: {
type: Array,
default: () => [],
},
value: {
type: Array,
default: () => [],
},
},
data() {
return {
popupVisible: false,
draftQty: {},
};
},
computed: {
displaySummary() {
const v = this.value || [];
if (!v.length) return '';
return v.map((i) => `${i.drug_name} * ${i.quantity}`).join(',');
},
},
methods: {
drugImage(d) {
const u = d && d.image;
return u && String(u).trim() ? u : '/static/mine/avatar_1.png';
},
getQty(id) {
const q = this.draftQty[id];
return q != null && q > 0 ? Number(q) : 0;
},
initDraftFromValue() {
const next = {};
for (const it of this.value || []) {
const id = it.id;
const q = Number(it.quantity);
if (id != null && q > 0) {
next[id] = q;
}
}
this.draftQty = next;
},
openPopup() {
this.initDraftFromValue();
this.popupVisible = true;
},
onPopupClose() {
this.popupVisible = false;
},
toggleRow(d) {
const id = d.id;
if (this.getQty(id) > 0) {
this.$delete(this.draftQty, id);
} else {
this.$set(this.draftQty, id, 1);
}
},
onQtyInput(d, val) {
const n = Number(val);
if (!n || n < 1) {
this.$delete(this.draftQty, d.id);
} else {
this.$set(this.draftQty, d.id, Math.min(99, n));
}
},
clearDraft() {
this.draftQty = {};
},
confirm() {
const out = [];
for (const d of this.drugList) {
const q = this.getQty(d.id);
if (q > 0) {
out.push({
id: d.id,
drug_name: d.drug_name || '',
quantity: q,
});
}
}
this.$emit('input', out);
this.$emit('confirm', out);
this.popupVisible = false;
},
},
};
</script>
<style lang="scss" scoped>
.follow-up-drug-drawer {
width: 100%;
}
.trigger {
display: flex;
align-items: center;
justify-content: space-between;
padding: 24rpx 20rpx;
margin-top: 12rpx;
background: #f8fafc;
border-radius: 12rpx;
border: 1rpx solid #e2e8f0;
}
.trigger-main {
flex: 1;
min-width: 0;
margin-right: 16rpx;
}
.placeholder {
font-size: 28rpx;
color: #94a3b8;
}
.summary {
font-size: 28rpx;
color: #0f172a;
line-height: 1.5;
}
.drawer-inner {
display: flex;
flex-direction: column;
height: 100%;
box-sizing: border-box;
padding: 24rpx 24rpx 0;
}
.drawer-title {
font-size: 32rpx;
font-weight: 600;
color: #0f172a;
text-align: center;
padding-bottom: 20rpx;
}
.drawer-empty {
flex: 1;
padding: 48rpx 0;
text-align: center;
color: #94a3b8;
font-size: 28rpx;
}
.drawer-scroll {
flex: 1;
max-height: 55vh;
min-height: 200rpx;
}
.drawer-row {
display: flex;
align-items: center;
padding: 20rpx 0;
border-bottom: 1rpx solid #f1f5f9;
}
.drug-check {
width: 40rpx;
height: 40rpx;
border: 2rpx solid #cbd5e1;
border-radius: 8rpx;
margin-right: 16rpx;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
&.on {
background: #4175ee;
border-color: #4175ee;
}
.check-mark {
color: #fff;
font-size: 24rpx;
line-height: 1;
}
}
.drug-thumb {
width: 96rpx;
height: 96rpx;
border-radius: 12rpx;
margin-right: 16rpx;
flex-shrink: 0;
background: #f1f5f9;
}
.drug-meta {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
.dn {
font-size: 28rpx;
color: #0f172a;
}
.ds {
font-size: 24rpx;
color: #64748b;
margin-top: 6rpx;
}
}
.drawer-row-actions {
flex-shrink: 0;
margin-left: 12rpx;
}
.drawer-footer {
display: flex;
align-items: center;
justify-content: space-between;
padding: 24rpx 0 32rpx;
gap: 24rpx;
}
.btn-text {
flex: 1;
text-align: center;
font-size: 30rpx;
color: #64748b;
padding: 22rpx 0;
}
.btn-primary {
flex: 2;
text-align: center;
font-size: 30rpx;
color: #fff;
background: #4175ee;
border-radius: 12rpx;
padding: 22rpx 0;
}
</style>
<template>
<view class="follow-up-drug-drawer">
<view class="trigger" @click="openPopup">
<view class="trigger-main">
<text v-if="!displaySummary" class="placeholder">请选择药品</text>
<text v-else class="summary">{{ displaySummary }}</text>
</view>
<u-icon name="arrow-right" color="#94A3B8" size="28"></u-icon>
</view>
<u-popup
v-model="popupVisible"
mode="bottom"
border-radius="24"
height="66%"
:safe-area-inset-bottom="true"
:closeable="true"
@close="onPopupClose"
>
<view class="drawer-inner">
<view class="drawer-title">选择相关西药</view>
<view v-if="storeId" class="drawer-search">
<u-search
v-model="searchKeyword"
placeholder="请输入药品名称或拼音首拼"
bg-color="#F1F5F9"
border-color="#F1F5F9"
:show-action="false"
:clearabled="true"
shape="round"
height="66"
@clear="onSearchClear"
/>
</view>
<view v-if="searchLoading" class="drawer-empty">加载中...</view>
<view v-else-if="!remoteDrugList.length" class="drawer-empty">{{ emptyText }}</view>
<scroll-view v-else scroll-y class="drawer-scroll">
<view
v-for="d in remoteDrugList"
:key="d.id"
class="drawer-row"
>
<view class="drug-check" :class="{ on: getQty(d.id) > 0 }" @click="toggleRow(d)">
<text v-if="getQty(d.id) > 0" class="check-mark">✓</text>
</view>
<image class="drug-thumb" :src="drugImage(d)" mode="aspectFill" @click="toggleRow(d)" />
<view class="drug-meta" @click="toggleRow(d)">
<text class="dn">{{ d.drug_name }}</text>
<text class="ds" v-if="d.specification">{{ d.specification }}</text>
</view>
<view class="drawer-row-actions">
<u-number-box
:value="getQty(d.id)"
:min="0"
:max="99"
:size="22"
:index="d.id"
@input="onQtyInput(d, $event)"
/>
</view>
</view>
</scroll-view>
<view class="drawer-footer safe-area-inset-bottom">
<view class="btn-text" @click="clearDraft">清空</view>
<view class="btn-primary" @click="confirm">确定</view>
</view>
</view>
</u-popup>
</view>
</template>
<script>
import { getWesternDrugsForFollowUpApi } from '@/request/api/order';
export default {
name: 'FollowUpDrugDrawer',
props: {
storeId: {
type: [String, Number],
default: '',
},
value: {
type: Array,
default: () => [],
},
},
data() {
return {
popupVisible: false,
draftQty: {},
searchKeyword: '',
remoteDrugList: [],
searchLoading: false,
drugMetaCache: {},
searchTimer: null,
};
},
computed: {
displaySummary() {
const v = this.value || [];
if (!v.length) return '';
return v.map((i) => `${i.drug_name} * ${i.quantity}`).join(',');
},
emptyText() {
return (this.searchKeyword || '').trim() ? '未找到相关药品' : '暂无可选西药';
},
},
watch: {
searchKeyword(val) {
if (!this.popupVisible) return;
if (this.searchTimer) {
clearTimeout(this.searchTimer);
}
this.searchTimer = setTimeout(() => {
this.fetchRemoteDrugs(val);
}, 300);
},
},
beforeDestroy() {
if (this.searchTimer) {
clearTimeout(this.searchTimer);
}
},
methods: {
drugImage(d) {
const u = d && d.image;
return u && String(u).trim() ? u : '/static/mine/avatar_1.png';
},
getQty(id) {
const q = this.draftQty[id];
return q != null && q > 0 ? Number(q) : 0;
},
cacheDrugMeta(d) {
if (d && d.id) {
this.$set(this.drugMetaCache, d.id, d);
}
},
initDraftFromValue() {
const next = {};
for (const it of this.value || []) {
const id = it.id;
const q = Number(it.quantity);
if (id != null && q > 0) {
next[id] = q;
this.cacheDrugMeta(it);
}
}
this.draftQty = next;
},
async fetchRemoteDrugs(keyword = '') {
if (!this.storeId) {
this.remoteDrugList = [];
return;
}
this.searchLoading = true;
try {
const params = { store_id: this.storeId };
const kw = (keyword || '').trim();
if (kw) params.keyword = kw;
const res = await getWesternDrugsForFollowUpApi(params);
const list = Array.isArray(res.data?.result) ? res.data.result : [];
this.remoteDrugList = list;
list.forEach((d) => this.cacheDrugMeta(d));
} catch (e) {
console.warn('fetchRemoteDrugs', e);
this.remoteDrugList = [];
} finally {
this.searchLoading = false;
}
},
openPopup() {
if (this.searchTimer) {
clearTimeout(this.searchTimer);
this.searchTimer = null;
}
this.searchKeyword = '';
this.initDraftFromValue();
this.popupVisible = true;
this.fetchRemoteDrugs('');
},
onPopupClose() {
this.popupVisible = false;
},
onSearchClear() {
this.searchKeyword = '';
this.fetchRemoteDrugs('');
},
toggleRow(d) {
const id = d.id;
this.cacheDrugMeta(d);
if (this.getQty(id) > 0) {
this.$delete(this.draftQty, id);
} else {

View File

@@ -10,6 +10,9 @@
<text class="type-label">{{ registerTypeLabel }}</text>
</view>
</view>
<text v-if="showDelegateInfo" class="delegate-info">
委托诊所:{{ delegateStoreName }} 委托医生:{{ delegateDoctorName }}
</text>
<text class="sub-title">请选择患者信息,以便医生给出更准确的诊疗建议,患者信息仅医生可见</text>
</view>
@@ -64,11 +67,9 @@
</view>
<view class="drug-container">
<view v-if="followUpDrugList.length === 0" class="empty-drugs">暂无可选西药</view>
<FollowUpDrugDrawer
v-else
v-model="selectedFollowUpDrugs"
:drug-list="followUpDrugList"
:store-id="resolveRegisterStoreId()"
/>
</view>
</view>
@@ -158,10 +159,13 @@
import {
userList,
userEidtPatient,
registe
registe,
storeInfo,
doctorDetail,
} from '@/request/api/api.js'
import { getClinicOnlineConsultationInfoApi } from '@/request/api/product';
import {checkDev} from "@/utils/utils";
import {createRegisterInfoApi, getWesternDrugsForFollowUpApi, genOrderApi, saveRegisterInfoApi} from "@/request/api/order";
import {createRegisterInfoApi, genOrderApi, saveRegisterInfoApi} from "@/request/api/order";
import request from "@/request/api/request";
export default {
data() {
@@ -217,8 +221,9 @@ export default {
rInfo: [], // 健康信息
registerType: "0", // 新增:挂号类型(0线下/1线上)
delegateStoreId: '', // 委托诊所ID(购药时使用)
delegateStoreName: '',
delegateDoctorName: '',
chiefComplaint: '',
followUpDrugList: [],
selectedFollowUpDrugs: [],
}
},
@@ -247,6 +252,7 @@ export default {
// 获取委托诊所ID(如果有)
this.delegateStoreId = e.delegate_store_id || '';
this.loadDelegateDisplayInfo();
console.log(e)
},
computed: {
@@ -281,7 +287,11 @@ export default {
showFollowUpForm() {
const t = this.registerType;
return (t === '2' || t === '3') && !this.hasPrefilledFollowUpInfo;
}
},
showDelegateInfo() {
const isFollowUp = this.registerType === '2' || this.registerType === '3';
return isFollowUp && !!(this.delegateStoreName || this.delegateDoctorName);
},
},
methods: {
checkDev,
@@ -290,16 +300,33 @@ export default {
? this.delegateStoreId
: (uni.getStorageSync('store_id') || '11001');
},
async loadFollowUpWesternDrugs() {
if (!this.showFollowUpForm) return;
const storeId = this.resolveRegisterStoreId();
async loadDelegateDisplayInfo() {
if (this.registerType !== '2' && this.registerType !== '3') return;
const currentStoreId = uni.getStorageSync('store_id') || '11001';
try {
const res = await getWesternDrugsForFollowUpApi({ store_id: storeId });
const list = res.data?.result;
this.followUpDrugList = Array.isArray(list) ? list : [];
if (this.registerType === '2') {
const res = await getClinicOnlineConsultationInfoApi({ store_id: currentStoreId });
const result = res.data?.result;
if (res.data?.code === 0 && result) {
this.delegateStoreName = result.delegate_store_name || result.store_name || '';
this.delegateDoctorName = result.doctor_name || '';
}
} else {
const storeId = this.delegateStoreId || this.resolveRegisterStoreId();
const [storeRes, doctorRes] = await Promise.all([
storeInfo({ method: 'post', data: { store_id: storeId } }),
doctorDetail({ method: 'post', data: { doctor_id: this.Did, store_id: currentStoreId } }),
]);
if (storeRes.data?.errcode == 0) {
this.delegateStoreName = storeRes.data.data?.store?.name || '';
}
const doctorData = doctorRes.data?.data || doctorRes.data?.result;
if (doctorRes.data?.errcode == 0 || doctorRes.data?.code === 0) {
this.delegateDoctorName = doctorData?.name || '';
}
}
} catch (e) {
console.error('加载复诊西药列表失败', e);
this.followUpDrugList = [];
console.warn('loadDelegateDisplayInfo', e);
}
},
followUpDrugIdsForApi() {
@@ -652,11 +679,9 @@ export default {
},
mounted() {
this.getInfolist()
this.loadFollowUpWesternDrugs()
},
onShow() {
this.getInfolist()
this.loadFollowUpWesternDrugs()
}
}
</script>
@@ -710,6 +735,15 @@ export default {
color: #64748B;
line-height: 1.5;
}
.delegate-info {
display: block;
font-size: 26rpx;
font-weight: 400;
color: #4175EE;
line-height: 1.5;
margin-bottom: 8rpx;
}
}
/* 原有就诊人卡片区 */

View File

@@ -59,15 +59,27 @@
<u-radio name="0" active-color="#2B85E4">否</u-radio>
</u-radio-group>
</u-form-item>
<u-form-item label="症状" prop="illnessInfo" label-width="200">
<view class="illness-section">
<view class="illness-label">症状</view>
<view class="illness-tags">
<u-tag
v-for="(item, index) in illnessOptions"
:key="index"
:text="item.label"
mode="dark"
:bg-color="selectedIllnessIndexes.includes(index) ? '#2B85E4' : 'rgba(43,133,228,0.08)'"
:color="selectedIllnessIndexes.includes(index) ? '#FFFFFF' : '#475569'"
@tap="toggleIllnessTag(index)"
/>
</view>
<u-input
v-model="prescriptionFormData.illnessInfo"
type="select"
placeholder="请选择症状"
@click="showIllnessPicker = true"
disabled
v-if="showIllnessOtherInput"
v-model="illnessOtherText"
placeholder="请填写具体症状"
:border="true"
@input="syncIllnessInfo"
/>
</u-form-item>
</view>
</u-form>
</block>
@@ -97,16 +109,6 @@
</view>
</view>
<u-picker
v-model="showIllnessPicker"
mode="selector"
:range="illnessOptions"
range-key="label"
@confirm="onIllnessConfirm"
:mask-close-able="true"
confirm-color="#2B85E4"
></u-picker>
<u-toast ref="uToast" />
</view>
</template>
@@ -151,7 +153,8 @@ export default {
illnessInfo: '',
},
illnessOptions: [],
showIllnessPicker: false,
selectedIllnessIndexes: [],
illnessOtherText: '',
delegateStoreId: '',
doctorIdFromQuery: '',
submitted: false,
@@ -253,6 +256,11 @@ export default {
}
return parseFloat(this.productDetail.price || 0);
},
showIllnessOtherInput() {
return this.selectedIllnessIndexes.some(
(idx) => this.illnessOptions[idx] && this.illnessOptions[idx].value === '__other__',
);
},
},
onLoad(options) {
if (options.mode === 'clinic_post_register') {
@@ -511,19 +519,39 @@ export default {
if (res.data.code === 0) {
const data = res.data.result;
Object.assign(this.productDetail, data);
if (data.indications_array && data.indications_array.length > 0) {
this.illnessOptions = data.indications_array.map((item) => ({
value: item.label || item.value,
label: item.label || item.value,
}));
}
const options = (data.indications_array || []).map((item) => ({
value: item.label || item.value,
label: item.label || item.value,
}));
options.push({ value: '__other__', label: '其他' });
this.illnessOptions = options;
}
});
},
onIllnessConfirm(e) {
const index = e[0];
this.prescriptionFormData.illnessInfo = this.illnessOptions[index].value;
this.showIllnessPicker = false;
toggleIllnessTag(index) {
const pos = this.selectedIllnessIndexes.indexOf(index);
if (pos === -1) {
this.selectedIllnessIndexes.push(index);
} else {
this.selectedIllnessIndexes.splice(pos, 1);
if (this.illnessOptions[index] && this.illnessOptions[index].value === '__other__') {
this.illnessOtherText = '';
}
}
this.syncIllnessInfo();
},
syncIllnessInfo() {
const parts = this.selectedIllnessIndexes
.map((idx) => {
const opt = this.illnessOptions[idx];
if (!opt) return '';
if (opt.value === '__other__') {
return (this.illnessOtherText || '').trim();
}
return opt.label || opt.value;
})
.filter(Boolean);
this.prescriptionFormData.illnessInfo = parts.join('、');
},
async getDefaultDoctorId() {
try {
@@ -657,10 +685,23 @@ export default {
});
},
async submitPharmacy() {
if (!this.prescriptionFormData.hasVisited || !this.prescriptionFormData.hasUsedDrug || !this.prescriptionFormData.illnessInfo) {
if (!this.prescriptionFormData.hasVisited || !this.prescriptionFormData.hasUsedDrug) {
uni.showToast({ title: '请填写完整信息', icon: 'none' });
return;
}
if (!this.selectedIllnessIndexes.length) {
uni.showToast({ title: '请选择症状', icon: 'none' });
return;
}
if (this.showIllnessOtherInput && !(this.illnessOtherText || '').trim()) {
uni.showToast({ title: '请填写具体症状', icon: 'none' });
return;
}
this.syncIllnessInfo();
if (!this.prescriptionFormData.illnessInfo) {
uni.showToast({ title: '请选择症状', icon: 'none' });
return;
}
if (this.submitted) return;
const formData = { ...this.prescriptionFormData };
const quantity = this.prescriptionQuantity;
@@ -792,6 +833,21 @@ export default {
color: #333;
}
}
.illness-section {
padding: 20rpx 0;
.illness-label {
font-size: 28rpx;
color: #333;
margin-bottom: 16rpx;
}
.illness-tags {
display: flex;
flex-wrap: wrap;
::v-deep .u-tag {
margin: 0 20rpx 16rpx 0;
}
}
}
.popup-actions {
display: flex;
gap: 30rpx;