1. 特色方功能迭代

This commit is contained in:
李琦
2026-07-13 12:52:36 +08:00
parent 6da8ae063a
commit 086d464145
17 changed files with 530 additions and 59 deletions

View File

@@ -1,5 +1,20 @@
import { formatMoney } from './format.js'
/** 展示用优先顶层合并医嘱含系统规则文案fallback content 纯医嘱 */
export function formatDoctorOrder(item) {
const pres = item && item.prescription ? item.prescription : item
if (!pres) return '-'
const raw = pres.doctor_order != null && pres.doctor_order !== ''
? pres.doctor_order
: (pres.content && pres.content.doctor_order) || ''
if (Array.isArray(raw)) {
const list = raw.map(s => String(s).trim()).filter(Boolean)
return list.length ? list.join('') : '-'
}
const parts = String(raw).split('|').map(s => s.trim()).filter(Boolean)
return parts.length ? parts.join('') : (raw ? String(raw) : '-')
}
const DEFAULT_DRUG_IMAGE =
'https://xkyp-web2.obs.cn-south-1.myhuaweicloud.com/buy/public/drug_no_mage.png'
@@ -271,7 +286,7 @@ export function mapOrderPrescription(info) {
return {
prescriptionNo: content.prescription_no || '-',
diagnose: content.clinical_diagnose || '-',
doctorOrder: content.doctor_order || '-',
doctorOrder: formatDoctorOrder(pres),
doctorName: doctor.name || '-',
departName: depart.name || '-',
titleName: title.name || '-',
@@ -370,7 +385,7 @@ export function mapPrescriptionDetailView(item) {
patientAge: patient.age != null ? String(patient.age) : '-',
category: content.category || '-',
diagnose: content.clinical_diagnose || '-',
doctorOrder: content.doctor_order || '-',
doctorOrder: formatDoctorOrder(item),
doctorName: doctor.name || '-',
departName: depart.name || '-',
titleName: title.name || '-',

View File

@@ -1,5 +1,20 @@
import { formatMoney } from './format.js'
/** 展示用优先顶层合并医嘱含系统规则文案fallback content 纯医嘱 */
export function formatDoctorOrder(item) {
const pres = item && item.prescription ? item.prescription : item
if (!pres) return '-'
const raw = pres.doctor_order != null && pres.doctor_order !== ''
? pres.doctor_order
: (pres.content && pres.content.doctor_order) || ''
if (Array.isArray(raw)) {
const list = raw.map(s => String(s).trim()).filter(Boolean)
return list.length ? list.join('') : '-'
}
const parts = String(raw).split('|').map(s => s.trim()).filter(Boolean)
return parts.length ? parts.join('') : (raw ? String(raw) : '-')
}
const DEFAULT_DRUG_IMAGE =
'https://xiaokang88.oss-cn-hangzhou.aliyuncs.com/xk_upload_20250510/cd470ebf97e31c4048ba502ba71b66a3.jpg'
@@ -271,7 +286,7 @@ export function mapOrderPrescription(info) {
return {
prescriptionNo: content.prescription_no || '-',
diagnose: content.clinical_diagnose || '-',
doctorOrder: content.doctor_order || '-',
doctorOrder: formatDoctorOrder(pres),
doctorName: doctor.name || '-',
departName: depart.name || '-',
titleName: title.name || '-',
@@ -370,7 +385,7 @@ export function mapPrescriptionDetailView(item) {
patientAge: patient.age != null ? String(patient.age) : '-',
category: content.category || '-',
diagnose: content.clinical_diagnose || '-',
doctorOrder: content.doctor_order || '-',
doctorOrder: formatDoctorOrder(item),
doctorName: doctor.name || '-',
departName: depart.name || '-',
titleName: title.name || '-',

View File

@@ -106,7 +106,8 @@
<transfer-mode-modal v-model="showTransferModeModal" @select="onTransferModeSelect" />
<transfer-patient-drawer
ref="photoTransferDrawer"
v-model="showPhotoTransferDrawer"
:value="showPhotoTransferDrawer"
@input="onPhotoTransferDrawerInput"
:photo-mode="true"
:submitting="photoSubmitting"
@confirm="handlePhotoTransferConfirm"
@@ -212,24 +213,49 @@ export default {
/** 选择传方方式 */
onTransferModeSelect(mode) {
if (mode === 'photo') {
this.showPhotoTransferDrawer = true
// 先重置再打开,避免跨分包组件 input 事件未解包时父级状态卡在 true
this.showPhotoTransferDrawer = false
this.$nextTick(() => {
this.showPhotoTransferDrawer = true
})
return
}
uni.navigateTo({
url: '/subPackages/sub_workbench/prescription_v2/index?salesperson_transfer=1&initial_category=1',
})
},
/** 解包跨分包原生组件 emit 的参数(微信会包在 detail.__args__ 里) */
unwrapComponentEventPayload(payload) {
if (payload?.detail?.__args__) return payload.detail.__args__[0]
if (Array.isArray(payload?.__args__)) return payload.__args__[0]
return payload
},
/** 拍照传方抽屉显隐同步(替代 v-model避免原生事件包装导致状态不同步 */
onPhotoTransferDrawerInput(val) {
this.showPhotoTransferDrawer = !!this.unwrapComponentEventPayload(val)
},
/** 构造拍照传方提交参数(显式字段,避免事件对象被误展开) */
buildPhotoTransferPayload(patientData) {
const data = this.unwrapComponentEventPayload(patientData) || {}
return {
patient_name: data.patient_name,
patient_mobile: data.patient_mobile,
patient_address: data.patient_address,
prescription_images: data.prescription_images || [],
remark: data.remark || '',
transfer_mode: 2,
drugs: [],
clinical_diagnose: '',
doctor_order: '',
}
},
/** 拍照传方:抽屉确认后直接提交 */
async handlePhotoTransferConfirm(patientData) {
this.photoSubmitting = true
try {
const wrap = await createClinicSalespersonTransferPrescription({
...patientData,
transfer_mode: 2,
drugs: [],
clinical_diagnose: '',
doctor_order: '',
})
const wrap = await createClinicSalespersonTransferPrescription(
this.buildPhotoTransferPayload(patientData)
)
if (wrap && wrap.ok) {
this.showPhotoTransferDrawer = false
if (this.$refs.photoTransferDrawer && this.$refs.photoTransferDrawer.resetForm) {

View File

@@ -329,6 +329,7 @@ import ReceptionActionBar from '@/subPackages/sub_online_reception/components/Re
import RefuseReceptionModal from '@/subPackages/sub_online_reception/components/RefuseReceptionModal.vue';
import { withWxKey, ensureWxKey } from '@/utils/wxListKey.js';
import { prepareImagePath } from '@/common/js/image-compress.js';
import { checkText, checkImage, handleSecurityError, SCENE_CHAT } from '@/common/js/content-security.js';
const PENDING_RX_FROM_CHAT = 'xk_pending_rx_from_chat';
@@ -1007,9 +1008,16 @@ export default {
toggleVoiceInput() {
this.voiceInputActive = !this.voiceInputActive;
},
sendTextMessage() {
async sendTextMessage() {
if (!this.newMessage.trim() || !this.roomId || this.inputLocked) return;
this.sendMessage({ type: 'text', content: this.newMessage });
const content = this.newMessage;
try {
await checkText(content, SCENE_CHAT);
} catch (e) {
handleSecurityError(e);
return;
}
this.sendMessage({ type: 'text', content });
this.newMessage = '';
this.showToolPanel = false;
},
@@ -1101,6 +1109,12 @@ export default {
size: fileSize,
});
if (!prepared) return;
try {
await checkImage(prepared.path, SCENE_CHAT);
} catch (e) {
handleSecurityError(e);
return;
}
this.uploadMedia(prepared.path, 'image');
},
});

View File

@@ -208,6 +208,7 @@
toUploaded
} from "@/api/all.js";
import { chooseAvatarImage } from '@/common/js/select-avatar-image.js';
import { checkProfileTexts, handleSecurityError, SCENE_PROFILE } from '@/common/js/content-security.js';
export default {
data() {
return {
@@ -231,6 +232,8 @@
title_id: "",
depart_id: "",
work_avator: "",
// 内容安全 baseline详情加载完成后快照
_securityBaseline: {},
}
},
computed: {
@@ -352,6 +355,10 @@
if (res.errcode == 0) {
// console.log(res.data, 'wd');
this.docInfo = res.data;
this._securityBaseline = {
good_at: (res.data && res.data.good_at) || '',
intro: (res.data && res.data.intro) || '',
};
this.$nextTick(() => {
this.loading = false;
});
@@ -368,6 +375,10 @@
if (res.errcode == 0) {
// console.log(res, 'wd');
this.doctorInfo = res.data;
this._securityBaseline = {
good_at: (res.data.DoctorInfo && res.data.DoctorInfo.good_at) || '',
intro: (res.data.DoctorInfo && res.data.DoctorInfo.intro) || '',
};
this.$nextTick(() => {
this.loading = false;
});
@@ -425,7 +436,16 @@
});
},
// 保存编辑
edit() {
async edit() {
try {
await checkProfileTexts({
good_at: this.doctorInfo.DoctorInfo.good_at,
intro: this.doctorInfo.DoctorInfo.intro,
}, this._securityBaseline, SCENE_PROFILE);
} catch (e) {
handleSecurityError(e);
return;
}
infoEdit({
title_id: this.title_id,
yard_id: this.yard_id,

View File

@@ -1,5 +1,6 @@
import { prepareImagePath } from '@/common/js/image-compress.js'
import { uploadToOss } from '@/common/js/oss-upload.js'
import { checkImage } from '@/common/js/content-security.js'
import { requestGalleryPick } from '@/subPackages/sub_salesperson/common/imageGalleryBridge.js'
function basenameFromPath(filePath) {
@@ -52,6 +53,7 @@ async function uploadChosenImages(res, count = 1) {
size: fileMeta.size,
})
if (!prepared) continue
await checkImage(prepared.path, 1)
const fileSize = fileMeta.size || 0
const fileName = fileMeta.name || basenameFromPath(path)
const result = await uploadToOss({ filePath: prepared.path, fileSize, fileName })

View File

@@ -12,6 +12,8 @@
<view class="row"><text class="k">手机号</text><text class="v">{{ detail.phone || '-' }}</text></view>
<view class="row"><text class="k">录入诊所</text><text class="v">{{ detail.clinic_input_count || 0 }} </text></view>
<view class="row"><text class="k">录入药店</text><text class="v">{{ detail.pharmacy_input_count || 0 }} </text></view>
<view class="row"><text class="k">正式诊所</text><text class="v">{{ detail.clinic_store_count || 0 }} </text></view>
<view class="row"><text class="k">正式药店</text><text class="v">{{ detail.pharmacy_store_count || 0 }} </text></view>
</view>
<view v-else class="loading">暂无数据</view>
</drawer-page-container>

View File

@@ -105,7 +105,6 @@ export default {
},
data() {
return {
show: false,
form: {
patient_name: '',
patient_mobile: '',
@@ -134,20 +133,16 @@ export default {
},
};
},
watch: {
value(newVal) {
this.show = newVal;
},
show(newVal) {
if (!newVal) {
this.$emit('input', false);
}
computed: {
/** 与父级 v-model 双向绑定,避免 data+watch 双状态不同步 */
show: {
get() { return this.value; },
set(v) { this.$emit('input', v); },
},
},
methods: {
handleClose() {
this.show = false;
this.$emit('input', false);
},
chooseImage() {
uni.chooseImage({

View File

@@ -2081,7 +2081,10 @@ export default {
} else {
this.diagnoses = [];
}
this.medicalAdvice = detail.doctor_order ?? content.doctor_order ?? '';
const fallbackOrder = Array.isArray(detail.doctor_order)
? detail.doctor_order.join('|')
: (detail.doctor_order ?? '');
this.medicalAdvice = content.doctor_order ?? fallbackOrder ?? '';
this.saveToLocalStorage();
return true;
},

View File

@@ -90,6 +90,7 @@
import selfIntroduction from './components/self-introduction.vue';
import openServe from './components/open-serve.vue';
import practiceInfo from './components/practice-info.vue';
import { checkProfileTexts, handleSecurityError, SCENE_PROFILE } from '@/common/js/content-security.js';
export default {
components: {
basicInformation,
@@ -148,6 +149,8 @@
identity: uni.getStorageSync('role') || 1,
role: uni.getStorageSync('role') || 1,
identitys: "",
// 内容安全 baseline表单初始值快照
_securityBaseline: {},
};
},
created() {
@@ -204,6 +207,11 @@
// positioned: { name: userInfo.titles.name },
id_card: userInfo.idcard
})
this._securityBaseline = {
name: userInfo.name || '',
beGoodAt: userInfo.good_at || '',
about: userInfo.intro || '',
}
uni.setStorageSync('d_name', userInfo.depart)
}
// 门店
@@ -474,6 +482,22 @@
// 完善基本信息
async stepOne() {
console.log(this.info, 'this.infossssssssssssssss');
try {
if (this.role == 1) {
await checkProfileTexts({
beGoodAt: this.info[0]['beGoodAt'],
about: this.info[0]['about'],
name: this.info[0]['name'],
}, this._securityBaseline, SCENE_PROFILE);
} else if (this.role == 2) {
await checkProfileTexts({
name: this.info[0]['name'],
}, this._securityBaseline, SCENE_PROFILE);
}
} catch (e) {
handleSecurityError(e);
return;
}
const data = {
name: this.info[0]['name'],
avatar: this.info[0]['avatar'],