From 4fc7a8f6f2386a850897f58619f27587687b0683 Mon Sep 17 00:00:00 2001
From: wangdongbo <949818794@qq.com>
Date: Thu, 29 Jan 2026 18:03:40 +0800
Subject: [PATCH 01/15] no message
---
pages.json | 6 +
pages/case/medical-case-form.vue | 486 ++++++++++++++++++
pages/message/chat.scss | 17 +-
.../components/ai-assistant-buttons.vue | 356 +++++++++++++
pages/message/components/chat-input.vue | 33 +-
.../components/medical-case-progress.vue | 197 +++++++
.../components/medical-case-type-selector.vue | 134 +++++
pages/message/index.vue | 29 +-
static/icon/buchong.png | Bin 0 -> 1095 bytes
static/icon/kaiqiAI.png | Bin 0 -> 1790 bytes
static/icon/zhuiwen.png | Bin 0 -> 1008 bytes
utils/api.js | 4 +-
12 files changed, 1251 insertions(+), 11 deletions(-)
create mode 100644 pages/case/medical-case-form.vue
create mode 100644 pages/message/components/ai-assistant-buttons.vue
create mode 100644 pages/message/components/medical-case-progress.vue
create mode 100644 pages/message/components/medical-case-type-selector.vue
create mode 100644 static/icon/buchong.png
create mode 100644 static/icon/kaiqiAI.png
create mode 100644 static/icon/zhuiwen.png
diff --git a/pages.json b/pages.json
index 203a112..f9151e3 100644
--- a/pages.json
+++ b/pages.json
@@ -115,6 +115,12 @@
"navigationBarTitleText": "病历详情"
}
},
+ {
+ "path": "pages/case/medical-case-form",
+ "style": {
+ "navigationBarTitleText": "添加病历"
+ }
+ },
{
"path": "pages/case/service-record-detail",
"style": {
diff --git a/pages/case/medical-case-form.vue b/pages/case/medical-case-form.vue
new file mode 100644
index 0000000..c650169
--- /dev/null
+++ b/pages/case/medical-case-form.vue
@@ -0,0 +1,486 @@
+
+
+
+
+
+
+ 就诊机构
+
+
+
+
+ 就诊日期
+
+
+ {{ formData.visitTime || '暂无' }}
+
+
+
+
+
+ 门诊诊断
+
+
+
+
+ 治疗方案
+
+
+
+
+
+
+
+ 就诊机构
+
+
+
+
+ 入院日期
+
+
+ {{ formData.inhosDate || '暂无' }}
+
+
+
+
+
+ 住院主诊断
+
+
+
+
+ 手术名称
+
+
+
+
+ 手术日期
+
+
+ {{ formData.operationDate || '暂无' }}
+
+
+
+
+
+ 治疗方案
+
+
+
+
+
+
+
+ 就诊机构
+
+
+
+
+ 体检日期
+
+
+ {{ formData.inspectTime || '暂无' }}
+
+
+
+
+
+ 体检小结
+
+
+
+
+ 阳性发现及处理意见
+
+
+
+
+
+
+
+ 主诉
+
+
+
+
+ 现病史
+
+
+
+
+ 既往史
+
+
+
+
+
+
+ 1、门诊、住院病历记录生成,生成后支持医生在线编辑,并保存至档案,或者重新生成;
+
+
+ 2、若未来集到有效信息则以模板字段中默认项写无内容生成,医生可以直接在存字段上进行编辑。
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/message/chat.scss b/pages/message/chat.scss
index d35fe0c..a5f27fb 100644
--- a/pages/message/chat.scss
+++ b/pages/message/chat.scss
@@ -348,23 +348,30 @@ $primary-color: #0877F1;
.text-input,
.voice-input-btn {
flex: 1;
- padding: 0 46rpx;
+ padding: 16rpx 46rpx;
background-color: #f3f5fa;
border-radius: 20rpx;
margin: 0 16rpx;
font-size: 28rpx;
- height: 80rpx;
+ min-height: 80rpx;
+ max-height: 200rpx;
border: none;
outline: none;
box-sizing: border-box;
- display: flex;
- align-items: center;
- line-height: 96rpx;
+ line-height: 1.5;
color: #333;
}
+.voice-input-btn {
+ height: 80rpx;
+ display: flex;
+ align-items: center;
+ padding: 0 46rpx;
+}
+
.voice-input-btn {
text-align: center;
+ line-height: 80rpx;
}
.more-panel {
diff --git a/pages/message/components/ai-assistant-buttons.vue b/pages/message/components/ai-assistant-buttons.vue
new file mode 100644
index 0000000..d097a4c
--- /dev/null
+++ b/pages/message/components/ai-assistant-buttons.vue
@@ -0,0 +1,356 @@
+
+
+
+
+ {{
+ button.loading && button.loadingText ? button.loadingText : button.text
+ }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/message/components/chat-input.vue b/pages/message/components/chat-input.vue
index 41f1bcb..b6727d1 100644
--- a/pages/message/components/chat-input.vue
+++ b/pages/message/components/chat-input.vue
@@ -6,8 +6,9 @@
-
+
@@ -25,7 +26,6 @@
{{ btn.text }}
-
@@ -95,6 +95,11 @@ const cloudCustomData = computed(() => {
return arr.filter(Boolean).join("|");
});
+// 流式输入文本
+const appendStreamText = (char) => {
+ inputText.value += char;
+};
+
// 录音相关扩展状态(特效 + 取消逻辑)
const recordingDuration = ref(0);
let recordingTimer = null;
@@ -165,9 +170,22 @@ const sendTextMessageFromPhrase = async (content) => {
});
};
+// 设置输入框文本(覆盖原内容)
+const setInputText = (text) => {
+ inputText.value = text;
+};
+
+// 清空输入框
+const clearInputText = () => {
+ inputText.value = '';
+};
+
// 暴露方法给父组件调用
defineExpose({
- sendTextMessageFromPhrase
+ sendTextMessageFromPhrase,
+ appendStreamText,
+ setInputText,
+ clearInputText
});
// 发送图片消息
@@ -432,6 +450,13 @@ function handleInputFocus() {
});
}
+function handleInput(e) {
+ // textarea 输入时触发,可以在这里处理额外逻辑
+ nextTick().then(() => {
+ emit("scrollToBottom");
+ });
+}
+
onMounted(() => {
// 初始化录音管理器
initRecorderManager();
diff --git a/pages/message/components/medical-case-progress.vue b/pages/message/components/medical-case-progress.vue
new file mode 100644
index 0000000..5bd762e
--- /dev/null
+++ b/pages/message/components/medical-case-progress.vue
@@ -0,0 +1,197 @@
+
+
+
+
+ ✕
+
+
+
+ {{ progressTitle }}
+
+
+
+
+
+ {{ progress }}%
+
+
+
+ 检测到以下{{ caseTypeName }}信息:
+
+
+ ✓
+ {{ item }}
+
+
+
+
+
+ 正在生成结构化{{ caseTypeName }}...
+
+
+
+
+
+
+
+
+
diff --git a/pages/message/components/medical-case-type-selector.vue b/pages/message/components/medical-case-type-selector.vue
new file mode 100644
index 0000000..9b696a2
--- /dev/null
+++ b/pages/message/components/medical-case-type-selector.vue
@@ -0,0 +1,134 @@
+
+
+
+
+
+
+
+ {{ type.name }}
+
+
+
+
+
+
+
+
+
diff --git a/pages/message/index.vue b/pages/message/index.vue
index 131a069..210933e 100644
--- a/pages/message/index.vue
+++ b/pages/message/index.vue
@@ -125,6 +125,16 @@
@cancel="handleRejectReasonCancel"
/>
+
+
+
{
const sendCommonPhrase = (content) => {
if (chatInputRef.value) {
- chatInputRef.value.sendTextMessageFromPhrase(content);
+ // 覆盖输入框内容,而不是直接发送
+ chatInputRef.value.setInputText(content);
}
};
+
+// 处理流式文本输入
+const handleStreamText = (char) => {
+ if (chatInputRef.value) {
+ chatInputRef.value.appendStreamText(char);
+ }
+};
+
+// 处理清空输入框
+const handleClearInput = () => {
+ if (chatInputRef.value) {
+ chatInputRef.value.clearInputText();
+ }
+};
+
// 暴露方法给常用语页面调用
defineExpose({
sendCommonPhrase,
diff --git a/static/icon/buchong.png b/static/icon/buchong.png
new file mode 100644
index 0000000000000000000000000000000000000000..7dd053b3d4d1e493a8234279f603d21f1f77ecfb
GIT binary patch
literal 1095
zcmV-N1i1T&P)`n
zRZ$dP>*Cu=M}Z)vqlQWYgBnN)LeQidHt09_52Qa}<7N$-G?4~D6cR|3LLfb$>8%J+
zqL9L?bB$ry++7?6s
z2P+swX0%YXc+nUJN!UwfF9RkoqE@4!6-mAV#I_Ra4Awdiv0}J_wsW%@$k|pag&SFB
zr;}KulN5wi1O&7PjG5C
z7%HY9H+eW6$#Al<8`G=Tj{)@O4{aZe$1jP*P&bGe0Pt>#zGWq4sGVc#YXBh4K#Lud
zuX5RZ_`Ccs0AV<_J3sl+s1sC&JR^yvuu%%^I28}1d#CbU5s!ZxWTos5T?7&{{!2i{?{s8?78+DSRsNDbn
N002ovPDHLkV1mRtos;%Nl~2=2}J`%#SQZ?tcPMN7|ZE
zlNjdLE(+&_Y8<{~
zr01*gXwUJDU0iRH&Bw>0x%UiUmjE7v(i7N+Qjn4U#epmeKz+6;2^9<(#fp{$PJfl+
zW7-`u!U$SpC&bd;9n)@r1kNbGu7^jrI2e&f^O>sxP@m8us6nFcf=CfwkVC01Tc%qb
z?Px`f3_(;Uj|#z6i%=NB9J8S(wpQ(LOA?5d+03UvFd2Cw1{$3<(XE`Up)H`G+1hlZ
zSi7TfUPpKeTC3tYkx;vSCm16sTFb)ob5%gzVtfFDp<7(pVcCcQ9PPM3K~yf)bRS`A
z={OLz;tEAZLMXzpQ8vrW75KQ)b7Eg4>rJ{v5~JMPidrKt_WDu{3888+}Y*lEXPc)nN0-dh|_q|{l@IrQOr1H+ag1fdOn)5@(;xSGMQ+&QK
z445?WWf-%@_N3b1ewtv$Vs1hf%AYIO0@tqs{<;V>t)s;ofS*rUm;0gP0}VE?!J^OT
zfDOV(Nt;;MP?qk{7k!bBjfFB`RX4lJ7pSR^7fiw9=dnF^i3<`SN)h
zF^fe<4QVP8Mx0jKPo7y>SS9WIsu2A7V}d(20&BMb`CRNa10VzxA2aD|(l9}`A$KuqavjLq9oFQ6WFAYq~OZn;8B#G*R6H)G)=O!b?
zgR!iq12<|^5s)`0Yw=TJp`#68kLsJM2nVX3{PpHZ&>Tgmt>VRIZ_3kd<8%=_&wb
z;}Bb}1V*_@ceNMzKPS&lURtu&Vsqv>-Zi=aLHCzrSxH)U
zFVd1#0omQw$BnK9qJ8MNva_PINwsMv2BDi3%Rzx3{gRy=j}^0VwHc+`c`8;}1P!?|
zD^~?z1iWf!PlipZ9%GoJPu+?>FV()ywrULrs_3pSv7?qg<7*YLt4gHgwTdiQ3VKd=xaWFpI>OQ~1&pMVg-Qxt8kzFw>*90I^;L=*{OQrNhQXo67ykgyA=oSWU
zU|Mg|&DMMOM0P21mkb~RveN{W_P_ldsHAART=TO{NBNb0{$BwY5IQXb(fQnGs+v+$
gEYCU!3s~m(2cF4~{lV!(mjD0&07*qoM6N<$g0;v&CjbBd
literal 0
HcmV?d00001
diff --git a/static/icon/zhuiwen.png b/static/icon/zhuiwen.png
new file mode 100644
index 0000000000000000000000000000000000000000..d95bbd8e6ca350eb89ca4563057814e5700d9cc6
GIT binary patch
literal 1008
zcmVyJP0C)u6fF&N3ldEz-
zobIx^yQ-&l++`LcC+#pyPyc;ief4$s0{k!kdcp1vb?6dodVm{9Fq?ZQ^w#PmpU8id
zdx3Z|QGAF>Zs)r9@cQHntPfUka2A2Kn}H4VupC!I1BV%#@zSFD-|(UU3?49&2=DOi
zBAEx%GjZ7JnFM&TqyZsSz=s}}R1h+=%>*R3s8N&Yt@0dHh`{0TUVu{W5q!*f07Xp+cp*E1
z0)ho#@|>#1XrZAfVg7`xEe{8hbBJVyeE{HdDzKRVB+v{-(STX<*pyriAMap4e9wKL
ztbhd;P*6p{D1c9Cmq*?-*nM*4O51>f6-*LOL$YcSaE+dZNknqctUwowsm*qUmfob<
zsL2{5lj4o>0+`pRN!o>k_i%PWT4;ls3$;yL%#+;|bU)q3!}`3VyI5$wvrXNCJpncy
zYSalmP@Mu2dE7#w`mK2Z2(BzDm5dRQniK%t1(^4?Iy){
zM)4r3j8_qHlMsNMZ_xb>N+jhpnanJfPdsPjcKHqfu!d|R5HApWmTMH%meNC|B2YgA
zTs1G;1KEs|c!11gm6UWF>BS;|oi}t1PE*8Wwpu;^x^*Y=Kt5Ka>|jDNF~akh^i8wQjTSrD|6x+T;FO`;PXA~UEPj*
zeTgmG5dvL%wi_>mXb*>OJlVqfFd=Z^<|NoEpJA_wz{qVfyGh}??OLPB`Tpv74IjfF
zlo7XKQef0zvgS+D4)~5({l13pU|e}pJ{;FoOtr3){~dfhfij(N;^h7EC#;;Tr{4nO
z3bu+3esxB!=f`h{VpJpJM`LnMUmQQe#U^kmdvMOewDy{ByGcurI#Bu^UtyQEiTCC*
z)Kp<*_oQhQZpg(Fh$OJjByI%~
e{43&pz55r0KOOH
Date: Thu, 29 Jan 2026 18:44:34 +0800
Subject: [PATCH 02/15] IM
---
.../components/ai-assistant-buttons.vue | 151 +++++++++-----
.../components/medical-case-progress.vue | 195 +++++++++++++++++-
2 files changed, 289 insertions(+), 57 deletions(-)
diff --git a/pages/message/components/ai-assistant-buttons.vue b/pages/message/components/ai-assistant-buttons.vue
index d097a4c..00cf432 100644
--- a/pages/message/components/ai-assistant-buttons.vue
+++ b/pages/message/components/ai-assistant-buttons.vue
@@ -20,7 +20,11 @@
/>
-
+
@@ -211,12 +215,10 @@ const handleCaseTypeSelect = async (type) => {
try {
// 打开进度弹窗
progressRef.value?.open(type.id);
+ progressRef.value?.updateProgress(10);
- // 模拟进度更新
- progressRef.value?.updateProgress(20);
-
- // 调用补充病历接口
- const result = await request({
+ // 调用补充病历接口(流式处理)
+ await requestWithStream({
url: "/getYoucanData/im",
data: {
type: "supplementMedicalCase",
@@ -225,50 +227,22 @@ const handleCaseTypeSelect = async (type) => {
corpId: props.corpId,
caseType: type.id,
},
- });
-
- progressRef.value?.updateProgress(60);
-
- if (result.success && result.data) {
- const { detectedInfo, formData } = result.data;
-
- // 显示检测到的信息
- if (detectedInfo && detectedInfo.length > 0) {
- detectedInfo.forEach((info) => {
- progressRef.value?.addDetectedInfo(info);
+ onProgress: (data) => {
+ // 处理流式数据
+ handleStreamData(data, type.id);
+ },
+ onComplete: (finalData) => {
+ // 完成后跳转
+ handleComplete(finalData, type.id);
+ },
+ onError: (error) => {
+ progressRef.value?.close();
+ uni.showToast({
+ title: error.message || "生成病历失败",
+ icon: "none",
});
}
-
- progressRef.value?.updateProgress(80);
- progressRef.value?.setGenerating(true);
-
- // 模拟生成结构化病历
- setTimeout(() => {
- progressRef.value?.updateProgress(100);
-
- // 延迟后关闭进度弹窗并跳转
- setTimeout(() => {
- progressRef.value?.close();
-
- // 跳转到病历填写页面
- uni.navigateTo({
- url: `/pages/case/medical-case-form?caseType=${
- type.id
- }&customerId=${
- props.customerId || props.patientAccountId
- }&groupId=${props.groupId}&formData=${encodeURIComponent(
- JSON.stringify(formData || {})
- )}`,
- });
- }, 500);
- }, 1000);
- } else {
- progressRef.value?.close();
- uni.showToast({
- title: result.message || "生成病历失败",
- icon: "none",
- });
- }
+ });
} catch (error) {
console.error("补充病历失败:", error);
progressRef.value?.close();
@@ -279,6 +253,87 @@ const handleCaseTypeSelect = async (type) => {
}
};
+// 流式请求处理
+const requestWithStream = async ({ url, data, onProgress, onComplete, onError }) => {
+ try {
+ // 调用接口时不显示全局 loading(第二个参数为 false)
+ const result = await request({
+ url,
+ data,
+ }, false);
+
+ if (result.success && result.data) {
+ // 模拟流式处理(如果后端返回的是完整数据)
+ const extractedData = result.data.extractedData || {};
+
+ // 逐个字段动态显示(包括空值字段)
+ let progressValue = 20;
+ const fields = Object.entries(extractedData);
+ const delay = 300; // 每个字段显示间隔
+
+ for (let i = 0; i < fields.length; i++) {
+ const [key, value] = fields[i];
+
+ // 显示所有字段,包括空值(会在组件中显示为"暂无")
+ await new Promise(resolve => setTimeout(resolve, delay));
+ onProgress({ key, value });
+ progressValue += Math.floor(60 / fields.length);
+ progressRef.value?.updateProgress(Math.min(progressValue, 80));
+ }
+
+ // 完成
+ onComplete(result.data);
+ } else {
+ onError(new Error(result.message || "请求失败"));
+ }
+ } catch (error) {
+ onError(error);
+ }
+};
+
+// 处理流式数据
+const handleStreamData = (data, caseType) => {
+ const { key, value } = data;
+
+ // 添加检测到的信息
+ progressRef.value?.addDetectedInfo(key, value);
+};
+
+// 处理完成
+const handleComplete = (finalData, caseType) => {
+ progressRef.value?.updateProgress(90);
+ progressRef.value?.setGenerating(true);
+
+ // 延迟后完成
+ setTimeout(() => {
+ progressRef.value?.updateProgress(100);
+ progressRef.value?.setGenerating(false);
+
+ // 延迟后显示操作按钮(不自动跳转)
+ setTimeout(() => {
+ progressRef.value?.setCompleted(true, finalData);
+ }, 500);
+ }, 800);
+};
+
+// 处理从进度弹窗点击重新生成
+const handleRegenerateFromProgress = (data) => {
+ const type = { id: data.caseType };
+ handleCaseTypeSelect(type);
+};
+
+// 处理从进度弹窗点击下一步
+const handleNextFromProgress = (data) => {
+ // 跳转到病历填写页面
+ uni.navigateTo({
+ url: `/pages/case/medical-case-form?caseType=${data.caseType}&customerId=${
+ props.customerId || props.patientAccountId
+ }&groupId=${props.groupId}&formData=${encodeURIComponent(
+ JSON.stringify(data.data?.extractedData || {})
+ )}`,
+ });
+};
+
// 监听重新生成事件
onMounted(() => {
uni.$on("regenerateMedicalCase", handleRegenerateMedicalCase);
diff --git a/pages/message/components/medical-case-progress.vue b/pages/message/components/medical-case-progress.vue
index 5bd762e..c1fb044 100644
--- a/pages/message/components/medical-case-progress.vue
+++ b/pages/message/components/medical-case-progress.vue
@@ -15,18 +15,38 @@
{{ progress }}%
-
+
检测到以下{{ caseTypeName }}信息:
-
+
✓
- {{ item }}
+
+ {{ item.label }}:{{ item.value }}
+
- 正在生成结构化{{ caseTypeName }}...
+ 正在生成结构化{{ caseTypeName }}
+
+
+
+
+
+ 重新生成
+
+
+ 下一步
+
@@ -36,11 +56,15 @@
@@ -98,6 +181,8 @@ defineExpose({
border-radius: 24rpx;
padding: 48rpx 40rpx;
position: relative;
+ max-height: 80vh;
+ overflow-y: auto;
.close-btn {
position: absolute;
@@ -108,6 +193,7 @@ defineExpose({
display: flex;
align-items: center;
justify-content: center;
+ z-index: 10;
.close-icon {
font-size: 40rpx;
@@ -140,7 +226,7 @@ defineExpose({
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #1890ff 0%, #40a9ff 100%);
- transition: width 0.3s ease;
+ transition: width 0.5s ease;
}
}
@@ -164,23 +250,39 @@ defineExpose({
}
.info-list {
+ max-height: 400rpx;
+ overflow-y: auto;
+
.info-item {
display: flex;
align-items: flex-start;
gap: 12rpx;
margin-bottom: 12rpx;
+ opacity: 0;
+ transform: translateX(-20rpx);
+
+ &.fade-in {
+ animation: fadeInSlide 0.4s ease forwards;
+ }
.check-icon {
font-size: 28rpx;
color: #52c41a;
font-weight: bold;
+ margin-top: 2rpx;
}
.info-text {
flex: 1;
font-size: 26rpx;
color: #333333;
- line-height: 1.5;
+ line-height: 1.6;
+ word-break: break-all;
+
+ // 暂无数据的样式
+ &.empty-value {
+ color: #999999;
+ }
}
}
}
@@ -191,7 +293,82 @@ defineExpose({
color: #1890ff;
text-align: center;
padding: 16rpx 0;
+
+ .dot-animation::after {
+ content: '...';
+ animation: dots 1.5s steps(4, end) infinite;
+ }
+ }
+
+ .action-buttons {
+ display: flex;
+ gap: 16rpx;
+ margin-top: 32rpx;
+
+ .action-button {
+ flex: 1;
+ height: 80rpx;
+ border-radius: 40rpx;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: all 0.3s ease;
+
+ &.primary {
+ background: linear-gradient(90deg, #1890ff 0%, #40a9ff 100%);
+
+ .button-text {
+ color: #ffffff;
+ }
+
+ &:active {
+ opacity: 0.8;
+ transform: scale(0.98);
+ }
+ }
+
+ &.secondary {
+ background-color: #ffffff;
+ border: 2rpx solid #d9d9d9;
+
+ .button-text {
+ color: #666666;
+ }
+
+ &:active {
+ background-color: #f5f5f5;
+ transform: scale(0.98);
+ }
+ }
+
+ .button-text {
+ font-size: 30rpx;
+ font-weight: 500;
+ }
+ }
}
}
}
+
+@keyframes fadeInSlide {
+ to {
+ opacity: 1;
+ transform: translateX(0);
+ }
+}
+
+@keyframes dots {
+ 0%, 20% {
+ content: '';
+ }
+ 40% {
+ content: '.';
+ }
+ 60% {
+ content: '..';
+ }
+ 80%, 100% {
+ content: '...';
+ }
+}
From ca5ad25854600c3b2392596374ab6f2ea5d750c4 Mon Sep 17 00:00:00 2001
From: huxuejian
Date: Fri, 30 Jan 2026 10:26:11 +0800
Subject: [PATCH 03/15] Update team.js
---
store/team.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/store/team.js b/store/team.js
index c1d1a82..e3c39b9 100644
--- a/store/team.js
+++ b/store/team.js
@@ -21,7 +21,7 @@ export default defineStore("teamStore", () => {
async function getTeams() {
const corpId = account.value?.corpId;
- const mateId = doctorInfo.value?.corpId;
+ const mateId = doctorInfo.value?.userid;
if (!corpId || !mateId) return;
const res = await api('getJoinedTeams', { corpId, mateId });
teams.value = res && Array.isArray(res.data) ? res.data : [];
From 3ad6140829597a45948df45eb911c5593d77f049 Mon Sep 17 00:00:00 2001
From: wangdongbo <949818794@qq.com>
Date: Fri, 30 Jan 2026 10:28:34 +0800
Subject: [PATCH 04/15] =?UTF-8?q?=E6=8F=90=E4=BA=A4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
components/full-page.vue | 35 +--
.../components/ai-assistant-buttons.vue | 49 ++--
pages/message/message.vue | 210 ++++++++++++++++--
utils/conversation-merger.js | 10 +-
4 files changed, 247 insertions(+), 57 deletions(-)
diff --git a/components/full-page.vue b/components/full-page.vue
index 45eefc8..4453441 100644
--- a/components/full-page.vue
+++ b/components/full-page.vue
@@ -7,8 +7,14 @@
-
+
@@ -16,22 +22,22 @@
-
+
diff --git a/pages/message/index.vue b/pages/message/index.vue
index 210933e..5357584 100644
--- a/pages/message/index.vue
+++ b/pages/message/index.vue
@@ -1,5 +1,18 @@
+
+
+
+
+ {{ patientInfo.name }}
+ {{ patientInfo.sex }} · {{ patientInfo.age }}岁
+
+
+ 查看档案
+
+
+
+
{
+const updateNavigationTitle = (title = "群聊") => {
uni.setNavigationBarTitle({
- title: "群聊",
+ title: title,
});
};
@@ -222,6 +239,14 @@ const isEvaluationPopupOpen = ref(false);
// 订单状态
const orderStatus = ref("");
+// 患者信息
+const patientInfo = ref({
+ name: "",
+ sex: "",
+ age: "",
+ mobile: "",
+});
+
// 计算弹框显示状态 - 只有 pending 状态才显示接受问诊组件
const showConsultAccept = computed(() => orderStatus.value === "pending");
@@ -281,8 +306,25 @@ const fetchGroupOrderStatus = async () => {
if (result.success && result.data) {
orderStatus.value = result.data.orderStatus || "";
+
+ // 更新导航栏标题为团队名称
+ const teamName = result.data.team?.name || "群聊";
+ updateNavigationTitle(teamName);
+
+ // 更新患者信息
+ if (result.data.patient) {
+ patientInfo.value = {
+ name: result.data.patient.name || "",
+ sex: result.data.patient.sex || "",
+ age: result.data.patient.age || "",
+ mobile: result.data.patient.mobile || "",
+ };
+ }
+
console.log("获取群组订单状态:", {
orderStatus: orderStatus.value,
+ teamName: teamName,
+ patientInfo: patientInfo.value,
groupId: groupId.value,
});
} else {
@@ -401,7 +443,11 @@ const initTIMCallbacks = async () => {
});
// 立即标记会话为已读,确保未读数为0
- if (timChatManager.tim && timChatManager.isLoggedIn && chatInfo.value.conversationID) {
+ if (
+ timChatManager.tim &&
+ timChatManager.isLoggedIn &&
+ chatInfo.value.conversationID
+ ) {
timChatManager.tim
.setMessageRead({
conversationID: chatInfo.value.conversationID,
@@ -409,9 +455,9 @@ const initTIMCallbacks = async () => {
.then(() => {
console.log("✓ 收到新消息后已标记为已读");
// 触发会话列表更新,确保未读数为0
- timChatManager.triggerCallback('onConversationListUpdated', {
+ timChatManager.triggerCallback("onConversationListUpdated", {
conversationID: chatInfo.value.conversationID,
- unreadCount: 0
+ unreadCount: 0,
});
})
.catch((error) => {
@@ -570,9 +616,9 @@ const loadMessageList = async () => {
.then(() => {
console.log("✓ 会话已标记为已读:", chatInfo.value.conversationID);
// 触发会话列表更新回调,通知消息列表页面清空未读数
- timChatManager.triggerCallback('onConversationListUpdated', {
+ timChatManager.triggerCallback("onConversationListUpdated", {
conversationID: chatInfo.value.conversationID,
- unreadCount: 0
+ unreadCount: 0,
});
})
.catch((error) => {
@@ -851,6 +897,15 @@ const handleRejectReasonConfirm = async (reason) => {
const handleRejectReasonCancel = () => {
showRejectReasonModal.value = false;
};
+
+// 处理查看患者详情
+const handleViewPatientDetail = () => {
+ // TODO: 跳转到患者详情页面
+ uni.showToast({
+ title: "患者详情功能开发中",
+ icon: "none",
+ });
+};
// 处理结束问诊
const handleEndConsult = async () => {
try {
diff --git a/pages/message/message.vue b/pages/message/message.vue
index 8908c6e..872161d 100644
--- a/pages/message/message.vue
+++ b/pages/message/message.vue
@@ -1,53 +1,11 @@
-
-
-
-
-
-
- 处理中
-
-
-
- 已结束
-
-
-
-
-
-
-
-
-
-
- {{ team.name }}
- ✓
-
-
-
-
+
+
{
- // 在团队列表前添加"全部会话消息"选项
- const allOption = { _id: "", name: "全部会话消息" };
- return [allOption, ...(teams.value || [])];
-});
-const currentTeamName = computed(() => {
- if (!currentTeamId.value) return "全部会话消息";
- const team = teams.value.find((t) => t._id === currentTeamId.value);
- return team ? team.name : "全部会话消息";
-});
// 监听 IM 初始化状态
watch(isIMInitialized, (newValue) => {
@@ -177,7 +128,7 @@ const activeTab = ref("processing");
// 根据 orderStatus 过滤会话列表
const filteredConversationList = computed(() => {
let filtered = [];
-
+
if (activeTab.value === "processing") {
// 处理中:pending(待处理) 和 processing(处理中)
filtered = conversationList.value.filter(
@@ -198,23 +149,21 @@ const filteredConversationList = computed(() => {
if (currentTeamId.value) {
filtered = filtered.filter((conv) => conv.teamId === currentTeamId.value);
}
-
return filtered;
});
-// 选择团队
-const selectTeam = (team) => {
- currentTeamId.value = team._id;
- showTeamPicker.value = false;
- console.log("切换到团队:", team.name);
+// 处理团队切换
+const handleTeamChange = (teamId) => {
+ currentTeamId.value = teamId;
+ console.log("切换到团队ID:", teamId);
};
-// 切换标签页
-const switchTab = (tab) => {
- if (activeTab.value === tab) return;
- activeTab.value = tab;
- console.log("切换到标签页:", tab);
-};
+// 邀请患者 - 使用 withInfo 包装,确保信息完善后才能使用
+const handleAddPatient = withInfo(() => {
+ uni.navigateTo({
+ url: "/pages/work/team/invite/invite-patient",
+ });
+});
// 初始化IM
const initIM = async () => {
@@ -381,7 +330,10 @@ const setupConversationListener = () => {
// 保持原有头像,避免闪动
avatar: existing.avatar || conversationData.avatar,
// 保留较大的未读数(避免被后端数据覆盖)
- unreadCount: Math.max(existing.unreadCount || 0, conversationData.unreadCount || 0)
+ unreadCount: Math.max(
+ existing.unreadCount || 0,
+ conversationData.unreadCount || 0
+ ),
};
needSort = true;
console.log(
@@ -421,11 +373,11 @@ const setupConversationListener = () => {
// 检查当前页面栈,判断用户是否正在查看该会话的聊天详情页
const pages = getCurrentPages();
const currentPage = pages[pages.length - 1];
-
+
// 获取当前页面的 groupID 参数(如果在聊天详情页)
const currentGroupID = currentPage?.options?.groupID;
- const isViewingThisConversation =
- currentPage?.route === "pages/message/index" &&
+ const isViewingThisConversation =
+ currentPage?.route === "pages/message/index" &&
currentGroupID === conversation.groupID;
// 如果用户正在查看这个具体的会话,不增加未读数
@@ -562,7 +514,7 @@ onShow(async () => {
try {
// 加载团队列表
await getTeams();
-
+
// 初始化IM
const imReady = await initIM();
if (!imReady) {
@@ -606,151 +558,6 @@ onHide(() => {
flex-direction: column;
}
-.header-container {
- background-color: #fff;
- padding: 20rpx 32rpx;
- border-bottom: 1rpx solid #f0f0f0;
- flex-shrink: 0;
-}
-
-.team-selector {
- display: flex;
- align-items: center;
- cursor: pointer;
-
- &:active {
- opacity: 0.7;
- }
-}
-
-.team-name {
- font-size: 36rpx;
- font-weight: 500;
- color: #333;
- flex: 1;
-}
-
-.arrow-icon {
- font-size: 20rpx;
- color: #999;
- margin-left: 12rpx;
- transform: scale(0.8);
-}
-
-.team-picker-overlay {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background-color: rgba(0, 0, 0, 0.5);
- z-index: 1000;
- display: flex;
- align-items: flex-start;
- justify-content: center;
- padding-top: 200rpx;
-}
-
-.team-picker-content {
- width: 600rpx;
- max-height: 800rpx;
- background-color: #fff;
- border-radius: 16rpx;
- overflow: hidden;
- display: flex;
- flex-direction: column;
-}
-
-.team-picker-header {
- padding: 32rpx;
- border-bottom: 1rpx solid #f0f0f0;
- text-align: center;
-}
-
-.picker-title {
- font-size: 36rpx;
- font-weight: 500;
- color: #333;
-}
-
-.team-list {
- flex: 1;
- overflow-y: auto;
-}
-
-.team-item {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 32rpx;
- border-bottom: 1rpx solid #f0f0f0;
- cursor: pointer;
-
- &:active {
- background-color: #f5f5f5;
- }
-
- &.active {
- background-color: #e6f7ff;
-
- .team-item-name {
- color: #1890ff;
- }
- }
-}
-
-.team-item-name {
- font-size: 32rpx;
- color: #333;
- flex: 1;
-}
-
-.check-icon {
- font-size: 36rpx;
- color: #1890ff;
- font-weight: bold;
-}
-
-.tabs-container {
- display: flex;
- background-color: #fff;
- border-bottom: 1rpx solid #f0f0f0;
- flex-shrink: 0;
-}
-
-.tab-item {
- flex: 1;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- padding: 28rpx 0;
- position: relative;
- cursor: pointer;
-
- &.active {
- .tab-text {
- color: #1890ff;
- font-weight: 500;
- }
- }
-}
-
-.tab-text {
- font-size: 32rpx;
- color: #666;
- transition: color 0.3s;
-}
-
-.tab-indicator {
- position: absolute;
- bottom: 0;
- width: 60rpx;
- height: 6rpx;
- background-color: #1890ff;
- border-radius: 3rpx;
-}
-
.message-list {
width: 100%;
flex: 1;
@@ -784,7 +591,7 @@ onHide(() => {
.message-item {
display: flex;
align-items: center;
- padding: 10rpx 32rpx;
+ padding: 24rpx 32rpx;
background-color: #fff;
border-bottom: 1rpx solid #f0f0f0;
@@ -796,6 +603,7 @@ onHide(() => {
.avatar-container {
position: relative;
margin-right: 24rpx;
+ flex-shrink: 0;
}
.avatar {
@@ -839,14 +647,28 @@ onHide(() => {
margin-bottom: 8rpx;
}
+.name-info {
+ display: flex;
+ align-items: center;
+ flex: 1;
+ min-width: 0;
+}
+
.name {
- font-size: 32rpx;
+ font-size: 30rpx;
font-weight: 500;
color: #333;
- flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
+ flex-shrink: 1;
+}
+
+.patient-info {
+ font-size: 26rpx;
+ padding-left: 12rpx;
+ color: #999;
+ flex-shrink: 0;
}
.time {
@@ -862,7 +684,7 @@ onHide(() => {
}
.preview-text {
- font-size: 28rpx;
+ font-size: 26rpx;
color: #999;
overflow: hidden;
text-overflow: ellipsis;
@@ -878,10 +700,4 @@ onHide(() => {
font-size: 24rpx;
color: #999;
}
-
-.patient-info {
- font-size: 28rpx;
- padding-left: 10rpx;
- color: #999;
-}
diff --git a/static/zhuanhua.svg b/static/zhuanhua.svg
new file mode 100644
index 0000000..061cbf5
--- /dev/null
+++ b/static/zhuanhua.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/utils/conversation-merger.js b/utils/conversation-merger.js
index f5c59a2..ddeb727 100644
--- a/utils/conversation-merger.js
+++ b/utils/conversation-merger.js
@@ -60,19 +60,19 @@ export async function mergeConversationWithGroupDetails(conversationList, option
const formattedList = mergedList
.map((group) => ({
conversationID: group.conversationID || `GROUP${group.groupID}`,
- groupID: group.groupID,
- name: group.patientName
- ? `${group.patientName}的问诊`
- : group.name || "问诊群聊",
avatar: group.avatar || "/static/default-avatar.png",
lastMessage: group.lastMessage || "暂无消息",
lastMessageTime: group.lastMessageTime || Date.now(),
+ groupID: group.groupID,
unreadCount: group.unreadCount || 0,
doctorId: group.doctorId,
patientName: group.patientName,
patientSex: group.patientSex,
patientAge: group.patientAge,
orderStatus: group.orderStatus,
+ teamId: group.teamId,
+ teamName: group.teamName,
+ teamMemberList: group.teamMemberList,
}))
.sort((a, b) => b.lastMessageTime - a.lastMessageTime)
@@ -123,9 +123,6 @@ function mergeConversationData(conversation, groupDetailsMap) {
return {
// 保留原有的会话信息
...conversation,
-
- // 合并后端的群组信息
- _id: groupDetail._id,
corpId: groupDetail.corpId,
teamId: groupDetail.teamId,
customerId: groupDetail.customerId,
@@ -144,7 +141,7 @@ function mergeConversationData(conversation, groupDetailsMap) {
teamName: groupDetail.team?.name,
teamMemberList: groupDetail.team?.memberList,
teamDescription: groupDetail.team?.description,
-
+ teamId: groupDetail.teamId,
// 时间信息
createdAt: groupDetail.createdAt,
updatedAt: groupDetail.updatedAt,
From ceccd59b65f8e5d889d5c656ca8e2da61d11184f Mon Sep 17 00:00:00 2001
From: huxuejian
Date: Fri, 30 Jan 2026 14:14:13 +0800
Subject: [PATCH 06/15] =?UTF-8?q?fix:=E4=BF=AE=E6=94=B9appId?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.env.development | 2 +-
.env.localhost | 2 +-
store/account.js | 1 +
3 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/.env.development b/.env.development
index c00ccb1..40eeb46 100644
--- a/.env.development
+++ b/.env.development
@@ -1,7 +1,7 @@
MP_API_BASE_URL=http://localhost:8080
MP_IMAGE_URL=https://patient.youcan365.com
MP_CACHE_PREFIX=development
-MP_WX_APP_ID=wx93af55767423938e
+MP_WX_APP_ID=wx1d8337a40c11d66c
MP_CORP_ID=wwe3fb2faa52cf9dfb
MP_TIM_SDK_APP_ID=1600123876
MP_INVITE_TEAMMATE_QRCODE=https://patient.youcan365.com/invite-teammate
diff --git a/.env.localhost b/.env.localhost
index c00ccb1..40eeb46 100644
--- a/.env.localhost
+++ b/.env.localhost
@@ -1,7 +1,7 @@
MP_API_BASE_URL=http://localhost:8080
MP_IMAGE_URL=https://patient.youcan365.com
MP_CACHE_PREFIX=development
-MP_WX_APP_ID=wx93af55767423938e
+MP_WX_APP_ID=wx1d8337a40c11d66c
MP_CORP_ID=wwe3fb2faa52cf9dfb
MP_TIM_SDK_APP_ID=1600123876
MP_INVITE_TEAMMATE_QRCODE=https://patient.youcan365.com/invite-teammate
diff --git a/store/account.js b/store/account.js
index ce571c3..ab3e387 100644
--- a/store/account.js
+++ b/store/account.js
@@ -48,6 +48,7 @@ export default defineStore("accountStore", () => {
});
if (code) {
const res = await api('wxAppLogin', {
+ appId:appid,
phoneCode,
code,
corpId,
From 01575ef51add7f0d5e953738de1d5c1ba6693c71 Mon Sep 17 00:00:00 2001
From: wangdongbo <949818794@qq.com>
Date: Fri, 30 Jan 2026 14:25:09 +0800
Subject: [PATCH 07/15] no message
---
.env.development | 2 +-
pages/message/article-list.vue | 17 +++++++++++++++--
pages/message/components/chat-input.vue | 3 ++-
pages/message/index.vue | 8 ++++++++
4 files changed, 26 insertions(+), 4 deletions(-)
diff --git a/.env.development b/.env.development
index c00ccb1..40eeb46 100644
--- a/.env.development
+++ b/.env.development
@@ -1,7 +1,7 @@
MP_API_BASE_URL=http://localhost:8080
MP_IMAGE_URL=https://patient.youcan365.com
MP_CACHE_PREFIX=development
-MP_WX_APP_ID=wx93af55767423938e
+MP_WX_APP_ID=wx1d8337a40c11d66c
MP_CORP_ID=wwe3fb2faa52cf9dfb
MP_TIM_SDK_APP_ID=1600123876
MP_INVITE_TEAMMATE_QRCODE=https://patient.youcan365.com/invite-teammate
diff --git a/pages/message/article-list.vue b/pages/message/article-list.vue
index 97e66ac..023d7d1 100644
--- a/pages/message/article-list.vue
+++ b/pages/message/article-list.vue
@@ -120,7 +120,7 @@ const corpId = env.MP_CORP_ID;
// 从页面参数获取群组信息
const pageParams = ref({
groupId: "",
- userId: "",
+ patientId: "",
corpId: "",
});
@@ -325,6 +325,19 @@ const sendArticle = async (article) => {
});
if (result.success) {
+ // 记录文章发送记录
+ try {
+ await api("addArticleSendRecord", {
+ articleId: article._id,
+ doctorId: doctorInfo.userId,
+ customerId: pageParams.value.patientId,
+ corpId: corpId,
+ });
+ } catch (recordError) {
+ console.error("记录文章发送失败:", recordError);
+ // 记录失败不影响发送流程
+ }
+
uni.navigateBack();
} else {
throw new Error(result.message || "发送失败");
@@ -351,7 +364,7 @@ onLoad((options) => {
pageParams.value.groupId = options.groupId;
}
if (options.userId) {
- pageParams.value.userId = options.userId;
+ pageParams.value.patientId = options.patientId;
}
if (options.corpId) {
pageParams.value.corpId = options.corpId;
diff --git a/pages/message/components/chat-input.vue b/pages/message/components/chat-input.vue
index b6727d1..2ef7f6c 100644
--- a/pages/message/components/chat-input.vue
+++ b/pages/message/components/chat-input.vue
@@ -75,6 +75,7 @@ const props = defineProps({
formatTime: { type: Function, required: true },
groupId: { type: String, default: '' },
userId: { type: String, default: '' },
+ patientId: { type: String, default: '' },
corpId: { type: String, default: '' },
});
@@ -385,7 +386,7 @@ const goToCommonPhrases = () => {
// 跳转到宣教文章页面
const goToArticleList = () => {
uni.navigateTo({
- url: `/pages/message/article-list?groupId=${props.groupId}&userId=${props.userId}&corpId=${props.corpId}`
+ url: `/pages/message/article-list?groupId=${props.groupId}&patientId=${props.patientId}&corpId=${props.corpId}`
});
};
diff --git a/pages/message/index.vue b/pages/message/index.vue
index 5357584..a022062 100644
--- a/pages/message/index.vue
+++ b/pages/message/index.vue
@@ -164,6 +164,7 @@
: ''
"
:userId="openid"
+ :patientId="patientId"
:corpId="corpId"
@scrollToBottom="() => scrollToBottom(true)"
@messageSent="() => scrollToBottom(true)"
@@ -247,6 +248,9 @@ const patientInfo = ref({
mobile: "",
});
+// 患者ID
+const patientId = ref("");
+
// 计算弹框显示状态 - 只有 pending 状态才显示接受问诊组件
const showConsultAccept = computed(() => orderStatus.value === "pending");
@@ -320,6 +324,10 @@ const fetchGroupOrderStatus = async () => {
mobile: result.data.patient.mobile || "",
};
}
+ // 更新患者ID
+ if (result.data.patientId) {
+ patientId.value = result.data.patientId.toString();
+ }
console.log("获取群组订单状态:", {
orderStatus: orderStatus.value,
From e26785bf4be1ba7ba3e98fc5ddd361c5b28d667c Mon Sep 17 00:00:00 2001
From: wangdongbo <949818794@qq.com>
Date: Fri, 30 Jan 2026 14:47:52 +0800
Subject: [PATCH 08/15] no message
---
pages/message/article-list.vue | 8 +++-----
utils/api.js | 2 +-
2 files changed, 4 insertions(+), 6 deletions(-)
diff --git a/pages/message/article-list.vue b/pages/message/article-list.vue
index 023d7d1..a20b2cb 100644
--- a/pages/message/article-list.vue
+++ b/pages/message/article-list.vue
@@ -323,19 +323,17 @@ const sendArticle = async (article) => {
imgUrl: article.cover || "",
desc: "点击查看详情",
});
-
if (result.success) {
// 记录文章发送记录
try {
- await api("addArticleSendRecord", {
+ await api("addCustomerArticleSendRecord", {
articleId: article._id,
- doctorId: doctorInfo.userId,
+ doctorId: doctorInfo.userid,
customerId: pageParams.value.patientId,
corpId: corpId,
});
} catch (recordError) {
console.error("记录文章发送失败:", recordError);
- // 记录失败不影响发送流程
}
uni.navigateBack();
@@ -363,7 +361,7 @@ onLoad((options) => {
if (options.groupId) {
pageParams.value.groupId = options.groupId;
}
- if (options.userId) {
+ if (options.patientId) {
pageParams.value.patientId = options.patientId;
}
if (options.corpId) {
diff --git a/utils/api.js b/utils/api.js
index c6779ef..36f4707 100644
--- a/utils/api.js
+++ b/utils/api.js
@@ -47,7 +47,7 @@ const urlsConfig = {
getArticleCateList: 'getArticleCateList',
getArticleList: 'getArticleList',
getArticle: 'getArticle',
- addArticleSendRecord: 'addArticleSendRecord'
+ addCustomerArticleSendRecord: 'addCustomerArticleSendRecord'
},
survery: {
From 18675de48b70bff237dc86ce5890b38b1c4733f7 Mon Sep 17 00:00:00 2001
From: wangdongbo <949818794@qq.com>
Date: Fri, 30 Jan 2026 16:41:12 +0800
Subject: [PATCH 09/15] no message
---
manifest.json | 2 +-
pages/login/login.vue | 8 ++++----
pages/message/article-list.vue | 4 ++--
pages/message/chat.scss | 14 ++++++++++----
pages/message/index.vue | 22 +++-------------------
pages/message/message.vue | 16 +++++-----------
store/account.js | 4 ++--
utils/api.js | 8 ++++----
utils/conversation-merger.js | 2 +-
9 files changed, 32 insertions(+), 48 deletions(-)
diff --git a/manifest.json b/manifest.json
index dbbc303..36d7ad9 100644
--- a/manifest.json
+++ b/manifest.json
@@ -50,7 +50,7 @@
"quickapp" : {},
/* 小程序特有相关 */
"mp-weixin" : {
- "appid" : "wx93af55767423938e",
+ "appid" : "wx1d8337a40c11d66c",
"setting" : {
"urlCheck" : false
},
diff --git a/pages/login/login.vue b/pages/login/login.vue
index d339408..6cd4ed2 100644
--- a/pages/login/login.vue
+++ b/pages/login/login.vue
@@ -5,12 +5,12 @@
生命全周期健康管理伙伴
-
-
+
+
\ No newline at end of file
diff --git a/pages/work/work.vue b/pages/work/work.vue
index ac125ef..ee85a44 100644
--- a/pages/work/work.vue
+++ b/pages/work/work.vue
@@ -46,23 +46,29 @@
待办列表
- 个人
- 团队
+
+ 个人
+
+
+ 团队
+
共
- 23
+ {{ total }}
条
+ :class="filterData.eventStatus == i.value ? 'text-primary' : 'text-dark'" @click="changeStatus(i.value)">
{{ i.label }}
-
+
@@ -70,28 +76,35 @@
-
+
- 计划执行: 2025-10-22
+ 计划执行: {{ i.planDate }}
- 患者: 李珊珊
+ 患者: {{ i.customerName }}
- 患者满意度调查
-
- 待处理
+ {{ i.eventTypeLabel }}
+
+ {{ i.eventStatusLabel }}
- 对于门诊就诊患者的满意度做统计,以便优化…
-
-
- 发送内容:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX…
+ {{ i.sendContent }}
+
+
+
+ 发送内容:{{ file.file.name }}
+
发送
- 张敏西(张敏希服务团队)
- 创建:2026-01-08 张敏西
+
+ {{ i.executorUserName }}({{ i.executeTeamName }})
+
+
+ 创建:{{ i.createTime }} {{ i.creatorUserName }}
+
@@ -103,19 +116,28 @@
+
+
diff --git a/store/team.js b/store/team.js
index e3c39b9..1ba0653 100644
--- a/store/team.js
+++ b/store/team.js
@@ -1,4 +1,4 @@
-import { ref } from "vue";
+import { computed, ref } from "vue";
import { defineStore, storeToRefs } from "pinia";
import api from '@/utils/api';
import { toast } from '@/utils/widget';
@@ -9,6 +9,13 @@ export default defineStore("teamStore", () => {
const { account, doctorInfo } = storeToRefs(useAccountStore());
const teams = ref([]);
+ const chargeTeams = computed(() => {
+ const userid = doctorInfo.value?.userid;
+ return teams.value.filter(team => {
+ const memberLeaderList = Array.isArray(team.memberLeaderList) ? team.memberLeaderList : [];
+ return memberLeaderList.includes(userid);
+ });
+ })
async function getTeam(teamId) {
if (!teamId || !account.value?.corpId) return;
const res = await api('getTeamData', { teamId, corpId: account.value.corpId });
@@ -27,5 +34,5 @@ export default defineStore("teamStore", () => {
teams.value = res && Array.isArray(res.data) ? res.data : [];
}
- return { teams, getTeam, getTeams }
+ return { teams, chargeTeams, getTeam, getTeams }
})
\ No newline at end of file
diff --git a/utils/api.js b/utils/api.js
index c6779ef..fa218dd 100644
--- a/utils/api.js
+++ b/utils/api.js
@@ -121,6 +121,7 @@ const urlsConfig = {
// 客户流转记录
customerTransferRecord: 'customerTransferRecord',
// sendConsultRejectedMessage: "sendConsultRejectedMessage"
+ getTeamTodos: 'getTeamTodos'
}
}
From dcc84cf449d65d672fb400ebe67f2483dd82a2b2 Mon Sep 17 00:00:00 2001
From: wangdongbo <949818794@qq.com>
Date: Mon, 2 Feb 2026 08:53:26 +0800
Subject: [PATCH 13/15] =?UTF-8?q?fix:=20=E6=8F=90=E4=BA=A4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
components/share-actions.vue | 89 ++
pages/message/message.vue | 1393 ++++++++++-----------
pages/work/team/invite/invite-patient.vue | 83 +-
utils/share-usage-example.md | 234 ++++
utils/share.js | 169 +++
utils/widget.js | 43 +
6 files changed, 1311 insertions(+), 700 deletions(-)
create mode 100644 components/share-actions.vue
create mode 100644 utils/share-usage-example.md
create mode 100644 utils/share.js
diff --git a/components/share-actions.vue b/components/share-actions.vue
new file mode 100644
index 0000000..2306b1a
--- /dev/null
+++ b/components/share-actions.vue
@@ -0,0 +1,89 @@
+
+
+
+ {{ saveText }}
+
+
+ {{ shareText }}
+
+
+
+
+
+
+
diff --git a/pages/message/message.vue b/pages/message/message.vue
index ae2c985..654726d 100644
--- a/pages/message/message.vue
+++ b/pages/message/message.vue
@@ -1,697 +1,696 @@
-
-
-
-
-
-
-
-
-
-
-
-
- {{
- conversation.unreadCount > 99 ? "99+" : conversation.unreadCount
- }}
-
-
-
-
-
-
- {{
- conversation.lastMessage || "暂无消息"
- }}
-
-
-
-
-
-
-
- {{
- activeTab === "processing" ? "暂无处理中的会话" : "暂无已结束的会话"
- }}
-
-
-
-
- {{
- loadingMore ? "加载中..." : "上拉加载更多"
- }}
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+ {{
+ conversation.unreadCount > 99 ? "99+" : conversation.unreadCount
+ }}
+
+
+
+
+
+
+ {{
+ conversation.lastMessage || "暂无消息"
+ }}
+
+
+
+
+
+
+
+ {{
+ activeTab === "processing" ? "暂无处理中的会话" : "暂无已结束的会话"
+ }}
+
+
+
+
+ {{
+ loadingMore ? "加载中..." : "上拉加载更多"
+ }}
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/work/team/invite/invite-patient.vue b/pages/work/team/invite/invite-patient.vue
index 9016ff1..4221424 100644
--- a/pages/work/team/invite/invite-patient.vue
+++ b/pages/work/team/invite/invite-patient.vue
@@ -42,8 +42,8 @@
进入团队首页,即可发起线上咨询、建档授权等服务
- 保存图片
- 分享微信
+ 保存图片
+ 分享微信
@@ -56,7 +56,7 @@ import { onLoad } from "@dcloudio/uni-app";
import useAccountStore from "@/store/account.js";
import useGuard from '@/hooks/useGuard';
import api from "@/utils/api.js";
-import { toast } from "@/utils/widget";
+import { toast, saveImageToPhotosAlbum, shareToWeChat } from "@/utils/widget";
import emptyData from "@/components/empty-data.vue";
import renamePopup from "./rename-popup.vue";
@@ -69,6 +69,7 @@ const current = ref(0);
const list = ref([]);
const visible = ref(false);
const teamId = ref('')
+const qrcodes = ref(null);
const indicator = computed(() => ({
prev: current.value > 0,
next: current.value < list.value.length - 1
@@ -113,6 +114,65 @@ async function change(name) {
}
}
+// 保存二维码图片
+async function saveImage() {
+ if (!team.value || !team.value.qrcode) {
+ toast('暂无二维码');
+ return;
+ }
+
+ try {
+ const qrcodeComponent = qrcodes.value[current.value];
+ if (!qrcodeComponent) {
+ toast('二维码未加载完成');
+ return;
+ }
+
+ // 获取二维码临时文件路径
+ const tempFilePath = qrcodeComponent.toTempFilePath();
+ if (tempFilePath) {
+ await saveImageToPhotosAlbum(tempFilePath);
+ } else {
+ toast('获取二维码失败');
+ }
+ } catch (err) {
+ console.error('保存图片失败:', err);
+ toast('保存失败');
+ }
+}
+
+// 分享配置
+function onShareAppMessage() {
+ if (!team.value) {
+ return shareToWeChat({
+ title: '邀请患者加入团队',
+ path: '/pages/work/team/invite/invite-patient'
+ });
+ }
+
+ return shareToWeChat({
+ title: `邀请您加入${team.value.name}`,
+ path: `/pages/work/team/invite/invite-patient?teamId=${team.value.teamId}`,
+ imageUrl: team.value.qrcode || ''
+ });
+}
+
+// 分享到朋友圈
+function onShareTimeline() {
+ if (!team.value) {
+ return {
+ title: '邀请患者加入团队',
+ path: '/pages/work/team/invite/invite-patient'
+ };
+ }
+
+ return {
+ title: `邀请您加入${team.value.name}`,
+ query: `teamId=${team.value.teamId}`,
+ imageUrl: team.value.qrcode || ''
+ };
+}
+
onLoad(opts => {
teamId.value = opts.teamId || '';
})
@@ -121,6 +181,12 @@ useShow(() => {
getTeams()
})
+// 导出分享方法供页面使用
+defineExpose({
+ onShareAppMessage,
+ onShareTimeline
+})
+
\ No newline at end of file
diff --git a/utils/share-usage-example.md b/utils/share-usage-example.md
new file mode 100644
index 0000000..b4b8606
--- /dev/null
+++ b/utils/share-usage-example.md
@@ -0,0 +1,234 @@
+# 微信小程序分享功能使用指南
+
+## 功能说明
+
+提供了完整的微信小程序分享功能,包括:
+- 分享给好友
+- 分享到朋友圈
+- 保存图片到相册
+
+## 使用方法
+
+### 1. 基础分享(在页面中)
+
+```vue
+
+
+ 分享给好友
+
+
+
+
+```
+
+### 2. 使用分享组件
+
+```vue
+
+
+
+
+
+
+
+```
+
+### 3. 保存二维码图片
+
+```vue
+
+
+
+ 保存二维码
+
+
+
+
+```
+
+### 4. 动态分享内容
+
+```vue
+
+```
+
+## 配置说明
+
+### 1. 启用分享到朋友圈
+
+在 `pages.json` 中配置页面:
+
+```json
+{
+ "path": "pages/index/index",
+ "style": {
+ "navigationBarTitleText": "首页",
+ "enableShareTimeline": true
+ }
+}
+```
+
+### 2. 全局分享配置
+
+在 `App.vue` 中配置全局分享:
+
+```vue
+
+```
+
+## API 说明
+
+### createShareMessage(options)
+
+创建分享给好友的配置
+
+**参数:**
+- `title` (string): 分享标题
+- `path` (string): 分享路径
+- `imageUrl` (string): 分享图片URL
+
+**返回:** 分享配置对象
+
+### createShareTimeline(options)
+
+创建分享到朋友圈的配置
+
+**参数:**
+- `title` (string): 分享标题
+- `query` (string): 分享路径参数
+- `imageUrl` (string): 分享图片URL
+
+**返回:** 分享配置对象
+
+### saveImageToAlbum(filePath)
+
+保存图片到相册
+
+**参数:**
+- `filePath` (string): 图片路径(本地临时路径或网络路径)
+
+**返回:** Promise
+
+## 注意事项
+
+1. 分享图片建议尺寸:5:4,推荐 500x400px
+2. 分享路径必须是已注册的页面路径
+3. 保存图片需要用户授权相册权限
+4. 分享到朋友圈需要在页面配置中启用
+5. 网络图片会自动下载后保存到相册
diff --git a/utils/share.js b/utils/share.js
new file mode 100644
index 0000000..2119bc0
--- /dev/null
+++ b/utils/share.js
@@ -0,0 +1,169 @@
+/**
+ * 微信小程序分享工具
+ */
+
+import { toast } from './widget'
+
+/**
+ * 创建分享到好友的配置
+ * @param {Object} options 分享配置
+ * @param {string} options.title 分享标题
+ * @param {string} options.path 分享路径
+ * @param {string} options.imageUrl 分享图片URL
+ * @returns {Object} 分享配置对象
+ */
+export function createShareMessage(options = {}) {
+ const { title = '', path = '', imageUrl = '' } = options
+
+ return {
+ title,
+ path,
+ imageUrl,
+ success: () => {
+ toast('分享成功')
+ },
+ fail: (err) => {
+ console.error('分享失败:', err)
+ toast('分享失败')
+ }
+ }
+}
+
+/**
+ * 创建分享到朋友圈的配置
+ * @param {Object} options 分享配置
+ * @param {string} options.title 分享标题
+ * @param {string} options.query 分享路径参数
+ * @param {string} options.imageUrl 分享图片URL
+ * @returns {Object} 分享配置对象
+ */
+export function createShareTimeline(options = {}) {
+ const { title = '', query = '', imageUrl = '' } = options
+
+ return {
+ title,
+ query,
+ imageUrl
+ }
+}
+
+/**
+ * 在页面中启用分享功能
+ * 使用方法:在页面的 setup 中调用
+ *
+ * @example
+ * import { enableShare } from '@/utils/share'
+ *
+ * // 在 setup 中
+ * enableShare({
+ * message: {
+ * title: '分享标题',
+ * path: '/pages/index/index',
+ * imageUrl: 'https://example.com/image.jpg'
+ * },
+ * timeline: {
+ * title: '朋友圈标题',
+ * query: 'id=123',
+ * imageUrl: 'https://example.com/image.jpg'
+ * }
+ * })
+ */
+export function enableShare(config = {}) {
+ const { message, timeline } = config
+
+ // 分享给好友
+ if (message) {
+ uni.$on('onShareAppMessage', () => {
+ return createShareMessage(message)
+ })
+ }
+
+ // 分享到朋友圈
+ if (timeline) {
+ uni.$on('onShareTimeline', () => {
+ return createShareTimeline(timeline)
+ })
+ }
+}
+
+/**
+ * 保存图片到相册
+ * @param {string} filePath 图片路径(本地临时路径或网络路径)
+ */
+export async function saveImageToAlbum(filePath) {
+ try {
+ // 如果是网络图片,先下载
+ let localPath = filePath
+ if (filePath.startsWith('http')) {
+ const res = await uni.downloadFile({ url: filePath })
+ if (res[0]) {
+ throw new Error('下载图片失败')
+ }
+ localPath = res[1].tempFilePath
+ }
+
+ // 检查授权
+ const authRes = await uni.getSetting()
+ if (!authRes[1].authSetting['scope.writePhotosAlbum']) {
+ // 请求授权
+ try {
+ await uni.authorize({ scope: 'scope.writePhotosAlbum' })
+ } catch (err) {
+ // 用户拒绝授权,引导去设置
+ const [modalErr, modalRes] = await uni.showModal({
+ title: '提示',
+ content: '需要您授权保存相册',
+ confirmText: '去设置',
+ cancelText: '取消'
+ })
+
+ if (modalRes && modalRes.confirm) {
+ await uni.openSetting()
+ }
+ return false
+ }
+ }
+
+ // 保存图片
+ const [saveErr] = await uni.saveImageToPhotosAlbum({ filePath: localPath })
+ if (saveErr) {
+ throw saveErr
+ }
+
+ await toast('保存成功')
+ return true
+ } catch (err) {
+ console.error('保存图片失败:', err)
+ await toast('保存失败')
+ return false
+ }
+}
+
+/**
+ * 生成带参数的小程序码
+ * 需要后端接口支持
+ * @param {Object} options
+ * @param {string} options.scene 场景值
+ * @param {string} options.page 页面路径
+ * @returns {Promise} 返回小程序码图片URL
+ */
+export async function generateMiniCode(options = {}) {
+ // 这里需要调用后端接口生成小程序码
+ // 示例代码,需要根据实际后端接口调整
+ try {
+ const res = await uni.request({
+ url: '/api/wechat/generateMiniCode',
+ method: 'POST',
+ data: options
+ })
+
+ if (res[0] || !res[1].data.success) {
+ throw new Error('生成小程序码失败')
+ }
+
+ return res[1].data.data.url
+ } catch (err) {
+ console.error('生成小程序码失败:', err)
+ throw err
+ }
+}
diff --git a/utils/widget.js b/utils/widget.js
index 495028e..8c88f8a 100644
--- a/utils/widget.js
+++ b/utils/widget.js
@@ -50,4 +50,47 @@ export async function confirm(content, opt = {}) {
}
})
})
+}
+
+// 保存图片到相册
+export async function saveImageToPhotosAlbum(filePath) {
+ try {
+ // 检查授权
+ const authRes = await uni.getSetting()
+ if (!authRes[1].authSetting['scope.writePhotosAlbum']) {
+ // 请求授权
+ try {
+ await uni.authorize({ scope: 'scope.writePhotosAlbum' })
+ } catch (err) {
+ await confirm('需要您授权保存相册', { title: '提示', showCancel: false })
+ await uni.openSetting()
+ return
+ }
+ }
+
+ // 保存图片
+ await uni.saveImageToPhotosAlbum({ filePath })
+ await toast('保存成功')
+ } catch (err) {
+ console.error('保存图片失败:', err)
+ await toast('保存失败')
+ }
+}
+
+// 分享到微信
+export function shareToWeChat(options = {}) {
+ const { title = '', path = '', imageUrl = '' } = options
+
+ return {
+ title,
+ path,
+ imageUrl,
+ success: () => {
+ toast('分享成功')
+ },
+ fail: (err) => {
+ console.error('分享失败:', err)
+ toast('分享失败')
+ }
+ }
}
\ No newline at end of file
From 3ccdc954c26d539c0042b16544fc3dc9e0505ab5 Mon Sep 17 00:00:00 2001
From: wangdongbo <949818794@qq.com>
Date: Mon, 2 Feb 2026 13:27:48 +0800
Subject: [PATCH 14/15] no message
---
pages/case/medical-case-form.vue | 577 +++++++++---------
.../components/ai-assistant-buttons.vue | 13 +-
.../components/medical-case-progress.vue | 152 ++---
.../components/medical-case-type-selector.vue | 44 +-
pages/message/index.vue | 3 +-
5 files changed, 394 insertions(+), 395 deletions(-)
diff --git a/pages/case/medical-case-form.vue b/pages/case/medical-case-form.vue
index c650169..532e89b 100644
--- a/pages/case/medical-case-form.vue
+++ b/pages/case/medical-case-form.vue
@@ -1,214 +1,48 @@
-
-
-
- 就诊机构
-
-
-
-
- 就诊日期
-
-
- {{ formData.visitTime || '暂无' }}
-
-
-
-
-
- 门诊诊断
-
-
-
-
- 治疗方案
-
-
-
-
-
-
-
- 就诊机构
-
-
-
-
- 入院日期
-
-
- {{ formData.inhosDate || '暂无' }}
-
-
-
-
-
- 住院主诊断
-
-
-
-
- 手术名称
-
-
-
-
- 手术日期
-
-
- {{ formData.operationDate || '暂无' }}
-
-
-
-
-
- 治疗方案
-
-
-
-
-
-
-
- 就诊机构
-
-
-
-
- 体检日期
-
-
- {{ formData.inspectTime || '暂无' }}
-
-
-
-
-
- 体检小结
-
-
-
-
- 阳性发现及处理意见
-
-
-
-
-
-
-
- 主诉
-
-
-
-
- 现病史
-
-
-
-
- 既往史
-
-
-
-
-
-
- 1、门诊、住院病历记录生成,生成后支持医生在线编辑,并保存至档案,或者重新生成;
-
-
- 2、若未来集到有效信息则以模板字段中默认项写无内容生成,医生可以直接在存字段上进行编辑。
-
+
+
+ {{ field.label }}
+
+
+
+
+ {{ formData[field.key] || "暂无" }}
+
+
+
+
+
+
+
+
-
+
@@ -363,26 +349,26 @@ const getRequiredFields = () => {
min-height: 100vh;
background-color: #f5f5f5;
padding-bottom: 120rpx;
-
+
.form-container {
background-color: #ffffff;
padding: 32rpx;
-
+
.form-item {
margin-bottom: 32rpx;
-
+
&.required .item-label::before {
- content: '*';
+ content: "*";
color: #ff4d4f;
margin-right: 8rpx;
}
-
+
.item-label {
font-size: 28rpx;
color: #333333;
margin-bottom: 16rpx;
}
-
+
.item-input,
.picker-value {
width: 100%;
@@ -392,64 +378,65 @@ const getRequiredFields = () => {
border-radius: 8rpx;
font-size: 28rpx;
color: #333333;
-
+
&[disabled] {
color: #999999;
}
}
-
+
.picker-value {
display: flex;
align-items: center;
color: #999999;
}
-
+
.item-textarea {
width: 100%;
- min-height: 160rpx;
+ min-height: 100rpx;
padding: 20rpx 24rpx;
background-color: #f8f9fa;
border-radius: 8rpx;
font-size: 28rpx;
color: #333333;
-
+ height: 100px;
&[disabled] {
color: #999999;
}
}
}
-
+
.tips-box {
margin-top: 32rpx;
padding: 24rpx;
background-color: #fffbe6;
border-radius: 8rpx;
-
+
.tips-text {
display: block;
font-size: 24rpx;
color: #666666;
line-height: 1.6;
margin-bottom: 8rpx;
-
+
&:last-child {
margin-bottom: 0;
}
}
}
}
-
+
.footer-buttons {
position: fixed;
bottom: 0;
left: 0;
right: 0;
+ z-index: 100;
display: flex;
gap: 24rpx;
padding: 24rpx 32rpx;
background-color: #ffffff;
box-shadow: 0 -4rpx 16rpx rgba(0, 0, 0, 0.08);
-
+
.btn-regenerate,
.btn-save {
flex: 1;
@@ -458,25 +445,25 @@ const getRequiredFields = () => {
align-items: center;
justify-content: center;
border-radius: 44rpx;
-
+
.btn-text {
font-size: 32rpx;
font-weight: 500;
}
}
-
+
.btn-regenerate {
background-color: #ffffff;
border: 2rpx solid #1890ff;
-
+
.btn-text {
color: #1890ff;
}
}
-
+
.btn-save {
background-color: #1890ff;
-
+
.btn-text {
color: #ffffff;
}
diff --git a/pages/message/components/ai-assistant-buttons.vue b/pages/message/components/ai-assistant-buttons.vue
index 3ac4854..9c8f2a1 100644
--- a/pages/message/components/ai-assistant-buttons.vue
+++ b/pages/message/components/ai-assistant-buttons.vue
@@ -44,6 +44,10 @@ const props = defineProps({
type: String,
default: "",
},
+ patientId: {
+ type: String,
+ default: "",
+ },
corpId: {
type: String,
default: "",
@@ -333,12 +337,15 @@ const handleRegenerateFromProgress = (data) => {
// 处理从进度弹窗点击下一步
const handleNextFromProgress = (data) => {
+ // 根据病历类型动态构建表单数据
+ const extractedData = data.data?.extractedData || {};
+
// 跳转到病历填写页面
uni.navigateTo({
- url: `/pages/case/medical-case-form?caseType=${data.caseType}&customerId=${
- props.customerId || props.patientAccountId
+ url: `/pages/case/medical-case-form?caseType=${data.caseType}&patientId=${
+ props.patientId
}&groupId=${props.groupId}&formData=${encodeURIComponent(
- JSON.stringify(data.data?.extractedData || {})
+ JSON.stringify(extractedData)
)}`,
});
};
diff --git a/pages/message/components/medical-case-progress.vue b/pages/message/components/medical-case-progress.vue
index c1fb044..c2803ab 100644
--- a/pages/message/components/medical-case-progress.vue
+++ b/pages/message/components/medical-case-progress.vue
@@ -4,29 +4,32 @@
✕
-
+
{{ progressTitle }}
-
+
-
+
{{ progress }}%
-
-
+
+
检测到以下{{ caseTypeName }}信息:
-
✓
-
{{ item.label }}:{{ item.value }}
@@ -34,11 +37,11 @@
-
+
正在生成结构化{{ caseTypeName }}
-
+
@@ -54,48 +57,47 @@
@@ -183,7 +185,7 @@ defineExpose({
position: relative;
max-height: 80vh;
overflow-y: auto;
-
+
.close-btn {
position: absolute;
top: 20rpx;
@@ -194,13 +196,13 @@ defineExpose({
align-items: center;
justify-content: center;
z-index: 10;
-
+
.close-icon {
font-size: 40rpx;
color: #999999;
}
}
-
+
.progress-content {
.progress-title {
font-size: 32rpx;
@@ -209,27 +211,27 @@ defineExpose({
margin-bottom: 32rpx;
text-align: center;
}
-
+
.progress-bar-wrapper {
display: flex;
align-items: center;
gap: 16rpx;
margin-bottom: 32rpx;
-
+
.progress-bar {
flex: 1;
height: 16rpx;
background-color: #e5e5e5;
border-radius: 8rpx;
overflow: hidden;
-
+
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #1890ff 0%, #40a9ff 100%);
transition: width 0.5s ease;
}
}
-
+
.progress-text {
font-size: 28rpx;
color: #1890ff;
@@ -238,21 +240,21 @@ defineExpose({
text-align: right;
}
}
-
+
.detected-info {
margin-bottom: 24rpx;
-
+
.detected-title {
font-size: 28rpx;
color: #666666;
display: block;
margin-bottom: 16rpx;
}
-
+
.info-list {
max-height: 400rpx;
overflow-y: auto;
-
+
.info-item {
display: flex;
align-items: flex-start;
@@ -260,25 +262,25 @@ defineExpose({
margin-bottom: 12rpx;
opacity: 0;
transform: translateX(-20rpx);
-
+
&.fade-in {
animation: fadeInSlide 0.4s ease forwards;
}
-
+
.check-icon {
font-size: 28rpx;
color: #52c41a;
font-weight: bold;
margin-top: 2rpx;
}
-
+
.info-text {
flex: 1;
font-size: 26rpx;
color: #333333;
line-height: 1.6;
word-break: break-all;
-
+
// 暂无数据的样式
&.empty-value {
color: #999999;
@@ -287,24 +289,24 @@ defineExpose({
}
}
}
-
+
.generating-text {
font-size: 28rpx;
color: #1890ff;
text-align: center;
padding: 16rpx 0;
-
+
.dot-animation::after {
- content: '...';
+ content: "...";
animation: dots 1.5s steps(4, end) infinite;
}
}
-
+
.action-buttons {
display: flex;
gap: 16rpx;
margin-top: 32rpx;
-
+
.action-button {
flex: 1;
height: 80rpx;
@@ -313,34 +315,34 @@ defineExpose({
align-items: center;
justify-content: center;
transition: all 0.3s ease;
-
+
&.primary {
background: linear-gradient(90deg, #1890ff 0%, #40a9ff 100%);
-
+
.button-text {
color: #ffffff;
}
-
+
&:active {
opacity: 0.8;
transform: scale(0.98);
}
}
-
+
&.secondary {
background-color: #ffffff;
border: 2rpx solid #d9d9d9;
-
+
.button-text {
color: #666666;
}
-
+
&:active {
background-color: #f5f5f5;
transform: scale(0.98);
}
}
-
+
.button-text {
font-size: 30rpx;
font-weight: 500;
@@ -358,17 +360,19 @@ defineExpose({
}
@keyframes dots {
- 0%, 20% {
- content: '';
+ 0%,
+ 20% {
+ content: "";
}
40% {
- content: '.';
+ content: ".";
}
60% {
- content: '..';
+ content: "..";
}
- 80%, 100% {
- content: '...';
+ 80%,
+ 100% {
+ content: "...";
}
}
diff --git a/pages/message/components/medical-case-type-selector.vue b/pages/message/components/medical-case-type-selector.vue
index 9b696a2..6c5cab6 100644
--- a/pages/message/components/medical-case-type-selector.vue
+++ b/pages/message/components/medical-case-type-selector.vue
@@ -7,7 +7,7 @@
✕
-
+
@@ -73,38 +73,38 @@ defineExpose({
background-color: #ffffff;
border-radius: 24rpx;
padding: 40rpx;
-
+
.selector-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 40rpx;
-
+
.header-title {
font-size: 36rpx;
font-weight: 600;
color: #333333;
}
-
+
.close-btn {
width: 48rpx;
height: 48rpx;
display: flex;
align-items: center;
justify-content: center;
-
+
.close-icon {
font-size: 40rpx;
color: #999999;
}
}
}
-
+
.case-type-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 24rpx;
-
+
.case-type-item {
display: flex;
flex-direction: column;
@@ -115,13 +115,13 @@ defineExpose({
border: 2rpx solid #e5e5e5;
border-radius: 16rpx;
transition: all 0.3s ease;
-
+
&:active {
background-color: #e8f4ff;
border-color: #1890ff;
transform: scale(0.98);
}
-
+
.type-name {
font-size: 30rpx;
color: #333333;
diff --git a/pages/message/index.vue b/pages/message/index.vue
index b8d2aa8..f817983 100644
--- a/pages/message/index.vue
+++ b/pages/message/index.vue
@@ -149,6 +149,7 @@
"
:groupId="groupId"
:patientAccountId="chatInfo.userID || ''"
+ :patientId="patientId"
:corpId="corpId"
@streamText="handleStreamText"
@clearInput="handleClearInput"
@@ -1048,4 +1049,4 @@ uni.$on("sendSurvey", async (data) => {
+
\ No newline at end of file
From 65c9a3efe54e61678974622de79dec758f5ffbff Mon Sep 17 00:00:00 2001
From: wangdongbo <949818794@qq.com>
Date: Mon, 2 Feb 2026 14:12:46 +0800
Subject: [PATCH 15/15] =?UTF-8?q?=E5=A4=B4=E5=83=8F=E8=B0=83=E6=95=B4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pages/message/hooks/use-group-chat.js | 84 +++++++++--
pages/message/index.vue | 13 +-
pages/work/profile.vue | 200 +++++++++++++++++---------
pages/work/work.vue | 2 +-
static/default-patient-avatar.png | Bin 0 -> 3694 bytes
store/team.js | 15 +-
utils/api.js | 3 +-
utils/conversation-merger.js | 4 +-
utils/tim-chat.js | 11 +-
9 files changed, 227 insertions(+), 105 deletions(-)
create mode 100644 static/default-patient-avatar.png
diff --git a/pages/message/hooks/use-group-chat.js b/pages/message/hooks/use-group-chat.js
index e550907..73d7a8e 100644
--- a/pages/message/hooks/use-group-chat.js
+++ b/pages/message/hooks/use-group-chat.js
@@ -1,5 +1,7 @@
import { ref, computed } from 'vue'
import { onShow, onUnload } from '@dcloudio/uni-app'
+import api from '@/utils/api.js'
+import useTeamStore from '@/store/team.js'
/**
* 简单的群聊hook
@@ -8,6 +10,9 @@ import { onShow, onUnload } from '@dcloudio/uni-app'
export default function useGroupChat(groupID) {
const groupInfo = ref({})
const members = ref([])
+ const teamMemberIds = ref([]) // 存储团队成员的userId列表
+ const patientId = ref('') // 存储患者ID
+ const teamStore = useTeamStore()
// 群聊成员映射
const chatMember = computed(() => {
@@ -15,30 +20,79 @@ export default function useGroupChat(groupID) {
members.value.forEach(member => {
res[member.id] = {
name: member.name,
- avatar: member.avatar || '/static/default-avatar.png'
+ avatar: member.avatar,
+ isTeamMember: member.isTeamMember // 标记是否为团队成员
}
})
return res
})
- // 获取群聊信息
+ // 判断某个userId是否为团队成员
+ const isTeamMember = (userId) => {
+ return teamMemberIds.value.includes(userId)
+ }
+
+ // 获取用户头像(根据是否为团队成员返回不同的默认头像)
+ const getUserAvatar = (userId) => {
+ const member = chatMember.value[userId]
+ if (!member) {
+ // 如果找不到成员信息,根据是否为团队成员返回默认头像
+ return isTeamMember(userId) ? '/static/home/avatar.svg' : '/static/default-patient-avatar.png'
+ }
+
+ // 如果有头像且不为空字符串,返回头像
+ if (member.avatar && member.avatar.trim() !== '') {
+ return member.avatar
+ }
+
+ // 否则根据是否为团队成员返回默认头像
+ return member.isTeamMember ? '/static/home/avatar.svg' : '/static/default-patient-avatar.png'
+ }
+
+ // 获取群聊信息和成员头像
async function getGroupInfo() {
const gid = typeof groupID === 'string' ? groupID : groupID.value
if (!gid) return
try {
- // 这里可以调用API获取群聊信息
- // const res = await getGroupDetail(gid)
- // if (res && res.success) {
- // groupInfo.value = res.data
- // members.value = res.data.members || []
- // }
+ // 1. 获取群聊基本信息
+ const groupResult = await api('getGroupListByGroupId', { groupId: gid })
- // 暂时使用本地数据
- groupInfo.value = {
- groupID: gid,
- name: '群聊',
- status: 'active'
+ if (groupResult && groupResult.success && groupResult.data) {
+ groupInfo.value = {
+ groupID: gid,
+ name: groupResult.data.team?.name || '群聊',
+ status: groupResult.data.orderStatus || 'active',
+ teamId: groupResult.data.teamId
+ }
+
+ // 2. 如果有teamId,获取团队成员头像
+ if (groupResult.data.teamId) {
+ const avatarMap = await teamStore.getTeamMemberAvatars(groupResult.data.teamId)
+
+ // 3. 存储团队成员ID列表
+ teamMemberIds.value = Object.keys(avatarMap)
+
+ // 4. 构建团队成员列表
+ members.value = teamMemberIds.value.map(userId => ({
+ id: userId,
+ name: userId, // 这里可以从其他地方获取真实姓名
+ avatar: avatarMap[userId] || '',
+ isTeamMember: true
+ }))
+
+ // 5. 添加患者信息(使用默认患者头像)
+ if (groupResult.data.patient) {
+ const pid = groupResult.data.patientId?.toString() || ''
+ patientId.value = pid
+ members.value.push({
+ id: pid,
+ name: groupResult.data.patient.name || '患者',
+ avatar: '', // 患者不设置头像,使用默认
+ isTeamMember: false
+ })
+ }
+ }
}
} catch (error) {
console.error('获取群聊信息失败:', error)
@@ -57,6 +111,8 @@ export default function useGroupChat(groupID) {
groupInfo,
members,
chatMember,
- getGroupInfo
+ getGroupInfo,
+ isTeamMember,
+ getUserAvatar
}
}
diff --git a/pages/message/index.vue b/pages/message/index.vue
index f817983..a2d1c8f 100644
--- a/pages/message/index.vue
+++ b/pages/message/index.vue
@@ -65,23 +65,16 @@
-
-
-
@@ -222,7 +215,7 @@ const { initIMAfterLogin } = useAccountStore();
const chatInputRef = ref(null);
const groupId = ref("");
-const { chatMember, getGroupInfo } = useGroupChat(groupId);
+const { chatMember, getGroupInfo, getUserAvatar } = useGroupChat(groupId);
// 动态设置导航栏标题
const updateNavigationTitle = (title = "群聊") => {
diff --git a/pages/work/profile.vue b/pages/work/profile.vue
index 06bd33a..3124fa1 100644
--- a/pages/work/profile.vue
+++ b/pages/work/profile.vue
@@ -2,23 +2,59 @@
-
+
-
-
-
+
+
+
-
-
+
+
-
-
+
+
{{ jobStr }}
@@ -39,12 +75,24 @@
-
+
-
+
@@ -58,46 +106,56 @@ import api from "@/utils/api.js";
import { upload } from "@/utils/http.js";
import { toast } from "@/utils/widget";
-import buttonFooter from '@/components/button-footer.vue';
+import buttonFooter from "@/components/button-footer.vue";
import commonCell from "@/components/form-template/common-cell.vue";
import FormInput from "@/components/form-template/form-cell/form-input.vue";
import FormSelect from "@/components/form-template/form-cell/form-select.vue";
import FormTextarea from "@/components/form-template/form-cell/form-textarea.vue";
-import fullPage from '@/components/full-page.vue';
+import fullPage from "@/components/full-page.vue";
const { account, doctorInfo } = storeToRefs(useAccountStore());
const { useLoad, useShow } = useGuard();
const { getDoctorInfo } = useAccountStore();
-const job = { assistant: '医生助理', doctor: '医生' };
+const job = { assistant: "医生助理", doctor: "医生" };
const form = ref({});
-const inviteTeamId = ref('')
-const type = ref('');
+const inviteTeamId = ref("");
+const type = ref("");
-const formData = computed(() => ({ ...(doctorInfo.value || {}), ...form.value, mobile: account.value?.mobile }));
-const cancelText = computed(() => doctorInfo.value ? '取消' : '暂不填写');
-const confirmText = computed(() => type.value === 'cert' ? '下一步' : '保存');
+const formData = computed(() => ({
+ ...(doctorInfo.value || {}),
+ ...form.value,
+ mobile: account.value?.mobile,
+}));
+const cancelText = computed(() => (doctorInfo.value ? "取消" : "暂不填写"));
+const confirmText = computed(() => (type.value === "cert" ? "下一步" : "保存"));
const jobStr = computed(() => {
- const jobs = formData.value && Array.isArray(formData.value.job) ? formData.value.job.filter(i => i === 'assistant' || i === 'doctor') : [];
- return jobs[0] && job[jobs[0]] ? job[jobs[0]] : '';
-})
+ const jobs =
+ formData.value && Array.isArray(formData.value.job)
+ ? formData.value.job.filter((i) => i === "assistant" || i === "doctor")
+ : [];
+ return jobs[0] && job[jobs[0]] ? job[jobs[0]] : "";
+});
const rule = computed(() => {
- if (doctorInfo.value && ['verified', 'verifying'].includes(doctorInfo.value.verifyStatus)) {
+ if (
+ doctorInfo.value &&
+ ["verified", "verifying"].includes(doctorInfo.value.verifyStatus)
+ ) {
return {
- anotherName: { name: '姓名 (不可修改)', required: false, disabled: true },
- job: { name: '岗位 (不可修改)', disabled: true },
- title: { name: '职称 (不可修改)', disabled: true },
- dept: { name: '科室 (不可修改)', disabled: true },
- }
+ anotherName: { name: "姓名 (不可修改)", required: false, disabled: true },
+ job: { name: "岗位 (不可修改)", disabled: true },
+ title: { name: "职称 (不可修改)", disabled: true },
+ dept: { name: "科室 (不可修改)", disabled: true },
+ };
}
return {
- anotherName: { name: '姓名', required: true, disabled: false },
- job: { name: '岗位', disabled: false },
- title: { name: '职称', disabled: false },
- dept: { name: '科室', disabled: false },
- }
-})
+ anotherName: { name: "姓名", required: true, disabled: false },
+ job: { name: "岗位", disabled: false },
+ title: { name: "职称", disabled: false },
+ dept: { name: "科室", disabled: false },
+ };
+});
// 选项数据
const genderOptions = [
@@ -139,55 +197,60 @@ function chooseAvatar() {
if (url) {
form.value.avatar = url;
} else {
- toast('上传失败')
+ toast("上传失败");
}
- }
- })
+ },
+ });
}
function onChange({ title, value }) {
- form.value[title] = value
+ form.value[title] = value;
}
function selectJob() {
if (rule.value.job.disabled) return;
uni.showActionSheet({
- itemList: ['医生', '医生助理', '无'],
+ itemList: ["医生", "医生助理", "无"],
success: ({ tapIndex }) => {
- const job = ['doctor', 'assistant',][tapIndex];
+ const job = ["doctor", "assistant"][tapIndex];
form.value.job = job ? [job] : [];
- }
- })
+ },
+ });
}
function toCert() {
- if (jobStr.value === '医生') {
+ if (jobStr.value === "医生") {
uni.navigateTo({
- url: '/pages/work/verify/doctor'
- })
- } else if (jobStr.value === '医生助理') {
+ url: "/pages/work/verify/doctor",
+ });
+ } else if (jobStr.value === "医生助理") {
uni.navigateTo({
- url: '/pages/work/verify/assistant'
- })
+ url: "/pages/work/verify/assistant",
+ });
} else {
- toast('请选择岗位信息')
+ toast("请选择岗位信息");
}
}
async function save() {
- if (typeof formData.value.anotherName !== 'string' || !formData.value.anotherName.trim()) {
- return toast('请输入姓名')
+ if (
+ typeof formData.value.anotherName !== "string" ||
+ !formData.value.anotherName.trim()
+ ) {
+ return toast("请输入姓名");
}
- if (type.value === 'cert' && !jobStr.value) {
- return toast('请选择岗位信息')
+ if (type.value === "cert" && !jobStr.value) {
+ return toast("请选择岗位信息");
}
- const apiName = doctorInfo.value ? 'updateCorpMemberFromWxapp' : 'addCorpMemberFromWxapp';
+ const apiName = doctorInfo.value
+ ? "updateCorpMemberFromWxapp"
+ : "addCorpMemberFromWxapp";
const data = {
...form.value,
weChatOpenId: account.value.openid,
mobile: account.value.mobile,
corpId: account.value.corpId,
- }
+ };
if (doctorInfo.value) {
data.id = doctorInfo.value._id;
}
@@ -196,30 +259,29 @@ async function save() {
}
const res = await api(apiName, data);
if (res && res.success) {
- await getDoctorInfo()
+ await getDoctorInfo();
form.value = {};
- if (type.value === 'cert') {
- toCert()
+ if (type.value === "cert") {
+ toCert();
} else {
- await toast('保存成功');
- back()
+ await toast("保存成功");
+ back();
}
} else {
- await toast(res?.message || '保存失败');
+ await toast(res?.message || "保存失败");
}
}
-useLoad(opts => {
+useLoad((opts) => {
type.value = opts?.type;
- if (type.value === 'joinTeam' && opts.teamId) {
- inviteTeamId.value = opts.teamId
+ if (type.value === "joinTeam" && opts.teamId) {
+ inviteTeamId.value = opts.teamId;
}
-})
-
-useShow(() => {
- getDoctorInfo()
});
+useShow(() => {
+ getDoctorInfo();
+});