头像调整

This commit is contained in:
wangdongbo 2026-02-02 14:12:46 +08:00
parent 3ccdc954c2
commit 65c9a3efe5
9 changed files with 227 additions and 105 deletions

View File

@ -1,5 +1,7 @@
import { ref, computed } from 'vue' import { ref, computed } from 'vue'
import { onShow, onUnload } from '@dcloudio/uni-app' import { onShow, onUnload } from '@dcloudio/uni-app'
import api from '@/utils/api.js'
import useTeamStore from '@/store/team.js'
/** /**
* 简单的群聊hook * 简单的群聊hook
@ -8,6 +10,9 @@ import { onShow, onUnload } from '@dcloudio/uni-app'
export default function useGroupChat(groupID) { export default function useGroupChat(groupID) {
const groupInfo = ref({}) const groupInfo = ref({})
const members = ref([]) const members = ref([])
const teamMemberIds = ref([]) // 存储团队成员的userId列表
const patientId = ref('') // 存储患者ID
const teamStore = useTeamStore()
// 群聊成员映射 // 群聊成员映射
const chatMember = computed(() => { const chatMember = computed(() => {
@ -15,30 +20,79 @@ export default function useGroupChat(groupID) {
members.value.forEach(member => { members.value.forEach(member => {
res[member.id] = { res[member.id] = {
name: member.name, name: member.name,
avatar: member.avatar || '/static/default-avatar.png' avatar: member.avatar,
isTeamMember: member.isTeamMember // 标记是否为团队成员
} }
}) })
return res 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() { async function getGroupInfo() {
const gid = typeof groupID === 'string' ? groupID : groupID.value const gid = typeof groupID === 'string' ? groupID : groupID.value
if (!gid) return if (!gid) return
try { try {
// 这里可以调用API获取群聊信息 // 1. 获取群聊基本信息
// const res = await getGroupDetail(gid) const groupResult = await api('getGroupListByGroupId', { groupId: gid })
// if (res && res.success) {
// groupInfo.value = res.data
// members.value = res.data.members || []
// }
// 暂时使用本地数据 if (groupResult && groupResult.success && groupResult.data) {
groupInfo.value = { groupInfo.value = {
groupID: gid, groupID: gid,
name: '群聊', name: groupResult.data.team?.name || '群聊',
status: 'active' 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) { } catch (error) {
console.error('获取群聊信息失败:', error) console.error('获取群聊信息失败:', error)
@ -57,6 +111,8 @@ export default function useGroupChat(groupID) {
groupInfo, groupInfo,
members, members,
chatMember, chatMember,
getGroupInfo getGroupInfo,
isTeamMember,
getUserAvatar
} }
} }

View File

@ -65,23 +65,16 @@
<!-- 消息内容 --> <!-- 消息内容 -->
<view v-else class="message-content"> <view v-else class="message-content">
<!-- 医生头像左侧 -->
<image <image
v-if="message.flow === 'in'" v-if="message.flow === 'in'"
class="doctor-msg-avatar" class="doctor-msg-avatar"
:src=" :src="getUserAvatar(message.from)"
chatMember[message.from]?.avatar || '/static/default-avatar.png'
"
mode="aspectFill" mode="aspectFill"
/> />
<!-- 患者头像右侧 -->
<image <image
v-if="message.flow === 'out'" v-if="message.flow === 'out'"
class="user-msg-avatar" class="user-msg-avatar"
:src=" :src="getUserAvatar(message.from)"
chatMember[message.from]?.avatar || '/static/home/avatar.svg'
"
mode="aspectFill" mode="aspectFill"
/> />
@ -222,7 +215,7 @@ const { initIMAfterLogin } = useAccountStore();
const chatInputRef = ref(null); const chatInputRef = ref(null);
const groupId = ref(""); const groupId = ref("");
const { chatMember, getGroupInfo } = useGroupChat(groupId); const { chatMember, getGroupInfo, getUserAvatar } = useGroupChat(groupId);
// //
const updateNavigationTitle = (title = "群聊") => { const updateNavigationTitle = (title = "群聊") => {

View File

@ -2,23 +2,59 @@
<full-page> <full-page>
<view class="p-15"> <view class="p-15">
<view class="bg-white px-10 mb-10 rounded"> <view class="bg-white px-10 mb-10 rounded">
<form-input :form="formData" :required="rule.anotherName.required" wordLimit="10" title="anotherName" <form-input
:name="rule.anotherName.name" @change="onChange($event)" /> :form="formData"
:required="rule.anotherName.required"
wordLimit="10"
title="anotherName"
:name="rule.anotherName.name"
@change="onChange($event)"
/>
<common-cell title="avatar" name="头像"> <common-cell title="avatar" name="头像">
<view class="flex-grow flex items-center justify-end" @click="chooseAvatar()"> <view
<image v-if="formData.avatar" class="avatar mr-5 rounded-full" :src="formData.avatar" /> class="flex-grow flex items-center justify-end"
<image v-else class="avatar mr-5 rounded-full" src="/static/default-avatar.png" /> @click="chooseAvatar()"
>
<image
v-if="formData.avatar"
class="avatar mr-5 rounded-full"
:src="formData.avatar"
/>
<image
v-else
class="avatar mr-5 rounded-full"
src="/static/home/avatar.svg"
/>
<uni-icons color="#999" type="right" size="16" /> <uni-icons color="#999" type="right" size="16" />
</view> </view>
</common-cell> </common-cell>
<form-select :form="formData" name="性别" title="gender" :range="genderOptions" @change="onChange($event)" /> <form-select
<form-input :form="formData" disableChange wordLimit="11" title="mobile" name="手机号 (不可修改)" /> :form="formData"
name="性别"
title="gender"
:range="genderOptions"
@change="onChange($event)"
/>
<form-input
:form="formData"
disableChange
wordLimit="11"
title="mobile"
name="手机号 (不可修改)"
/>
</view> </view>
<view class="bg-white px-10 mb-10 rounded"> <view class="bg-white px-10 mb-10 rounded">
<!-- 填写认证资料的时候岗位必填 --> <!-- 填写认证资料的时候岗位必填 -->
<common-cell :required="type === 'cert'" title="job" :name="rule.job.name"> <common-cell
<view class="flex-grow flex items-center justify-end" @click="selectJob()"> :required="type === 'cert'"
title="job"
:name="rule.job.name"
>
<view
class="flex-grow flex items-center justify-end"
@click="selectJob()"
>
<view v-if="jobStr" class="text-base text-base">{{ jobStr }}</view> <view v-if="jobStr" class="text-base text-base">{{ jobStr }}</view>
<!-- <view class="mr-5 rounded-full" style="width: 64rpx;height: 64rpx;background: red;"></view> --> <!-- <view class="mr-5 rounded-full" style="width: 64rpx;height: 64rpx;background: red;"></view> -->
<uni-icons color="#999" type="right" size="16" /> <uni-icons color="#999" type="right" size="16" />
@ -39,12 +75,24 @@
</view> </view>
<view class="bg-white rounded"> <view class="bg-white rounded">
<form-textarea autoHeight :border="false" :form="formData" title="intro" name="个人介绍" :wordLimit="300" <form-textarea
@change="onChange($event)" /> autoHeight
:border="false"
:form="formData"
title="intro"
name="个人介绍"
:wordLimit="300"
@change="onChange($event)"
/>
</view> </view>
</view> </view>
<template #footer> <template #footer>
<button-footer :cancelText="cancelText" :confirmText="confirmText" @confirm="save()" @cancel="back()" /> <button-footer
:cancelText="cancelText"
:confirmText="confirmText"
@confirm="save()"
@cancel="back()"
/>
</template> </template>
</full-page> </full-page>
</template> </template>
@ -58,46 +106,56 @@ import api from "@/utils/api.js";
import { upload } from "@/utils/http.js"; import { upload } from "@/utils/http.js";
import { toast } from "@/utils/widget"; 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 commonCell from "@/components/form-template/common-cell.vue";
import FormInput from "@/components/form-template/form-cell/form-input.vue"; import FormInput from "@/components/form-template/form-cell/form-input.vue";
import FormSelect from "@/components/form-template/form-cell/form-select.vue"; import FormSelect from "@/components/form-template/form-cell/form-select.vue";
import FormTextarea from "@/components/form-template/form-cell/form-textarea.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 { account, doctorInfo } = storeToRefs(useAccountStore());
const { useLoad, useShow } = useGuard(); const { useLoad, useShow } = useGuard();
const { getDoctorInfo } = useAccountStore(); const { getDoctorInfo } = useAccountStore();
const job = { assistant: '医生助理', doctor: '医生' }; const job = { assistant: "医生助理", doctor: "医生" };
const form = ref({}); const form = ref({});
const inviteTeamId = ref('') const inviteTeamId = ref("");
const type = ref(''); const type = ref("");
const formData = computed(() => ({ ...(doctorInfo.value || {}), ...form.value, mobile: account.value?.mobile })); const formData = computed(() => ({
const cancelText = computed(() => doctorInfo.value ? '取消' : '暂不填写'); ...(doctorInfo.value || {}),
const confirmText = computed(() => type.value === 'cert' ? '下一步' : '保存'); ...form.value,
mobile: account.value?.mobile,
}));
const cancelText = computed(() => (doctorInfo.value ? "取消" : "暂不填写"));
const confirmText = computed(() => (type.value === "cert" ? "下一步" : "保存"));
const jobStr = computed(() => { const jobStr = computed(() => {
const jobs = formData.value && Array.isArray(formData.value.job) ? formData.value.job.filter(i => i === 'assistant' || i === 'doctor') : []; const jobs =
return jobs[0] && job[jobs[0]] ? job[jobs[0]] : ''; 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(() => { const rule = computed(() => {
if (doctorInfo.value && ['verified', 'verifying'].includes(doctorInfo.value.verifyStatus)) { if (
doctorInfo.value &&
["verified", "verifying"].includes(doctorInfo.value.verifyStatus)
) {
return { return {
anotherName: { name: '姓名 (不可修改)', required: false, disabled: true }, anotherName: { name: "姓名 (不可修改)", required: false, disabled: true },
job: { name: '岗位 (不可修改)', disabled: true }, job: { name: "岗位 (不可修改)", disabled: true },
title: { name: '职称 (不可修改)', disabled: true }, title: { name: "职称 (不可修改)", disabled: true },
dept: { name: '科室 (不可修改)', disabled: true }, dept: { name: "科室 (不可修改)", disabled: true },
} };
} }
return { return {
anotherName: { name: '姓名', required: true, disabled: false }, anotherName: { name: "姓名", required: true, disabled: false },
job: { name: '岗位', disabled: false }, job: { name: "岗位", disabled: false },
title: { name: '职称', disabled: false }, title: { name: "职称", disabled: false },
dept: { name: '科室', disabled: false }, dept: { name: "科室", disabled: false },
} };
}) });
// //
const genderOptions = [ const genderOptions = [
@ -139,55 +197,60 @@ function chooseAvatar() {
if (url) { if (url) {
form.value.avatar = url; form.value.avatar = url;
} else { } else {
toast('上传失败') toast("上传失败");
} }
} },
}) });
} }
function onChange({ title, value }) { function onChange({ title, value }) {
form.value[title] = value form.value[title] = value;
} }
function selectJob() { function selectJob() {
if (rule.value.job.disabled) return; if (rule.value.job.disabled) return;
uni.showActionSheet({ uni.showActionSheet({
itemList: ['医生', '医生助理', '无'], itemList: ["医生", "医生助理", "无"],
success: ({ tapIndex }) => { success: ({ tapIndex }) => {
const job = ['doctor', 'assistant',][tapIndex]; const job = ["doctor", "assistant"][tapIndex];
form.value.job = job ? [job] : []; form.value.job = job ? [job] : [];
} },
}) });
} }
function toCert() { function toCert() {
if (jobStr.value === '医生') { if (jobStr.value === "医生") {
uni.navigateTo({ uni.navigateTo({
url: '/pages/work/verify/doctor' url: "/pages/work/verify/doctor",
}) });
} else if (jobStr.value === '医生助理') { } else if (jobStr.value === "医生助理") {
uni.navigateTo({ uni.navigateTo({
url: '/pages/work/verify/assistant' url: "/pages/work/verify/assistant",
}) });
} else { } else {
toast('请选择岗位信息') toast("请选择岗位信息");
} }
} }
async function save() { async function save() {
if (typeof formData.value.anotherName !== 'string' || !formData.value.anotherName.trim()) { if (
return toast('请输入姓名') typeof formData.value.anotherName !== "string" ||
!formData.value.anotherName.trim()
) {
return toast("请输入姓名");
} }
if (type.value === 'cert' && !jobStr.value) { if (type.value === "cert" && !jobStr.value) {
return toast('请选择岗位信息') return toast("请选择岗位信息");
} }
const apiName = doctorInfo.value ? 'updateCorpMemberFromWxapp' : 'addCorpMemberFromWxapp'; const apiName = doctorInfo.value
? "updateCorpMemberFromWxapp"
: "addCorpMemberFromWxapp";
const data = { const data = {
...form.value, ...form.value,
weChatOpenId: account.value.openid, weChatOpenId: account.value.openid,
mobile: account.value.mobile, mobile: account.value.mobile,
corpId: account.value.corpId, corpId: account.value.corpId,
} };
if (doctorInfo.value) { if (doctorInfo.value) {
data.id = doctorInfo.value._id; data.id = doctorInfo.value._id;
} }
@ -196,30 +259,29 @@ async function save() {
} }
const res = await api(apiName, data); const res = await api(apiName, data);
if (res && res.success) { if (res && res.success) {
await getDoctorInfo() await getDoctorInfo();
form.value = {}; form.value = {};
if (type.value === 'cert') { if (type.value === "cert") {
toCert() toCert();
} else { } else {
await toast('保存成功'); await toast("保存成功");
back() back();
} }
} else { } else {
await toast(res?.message || '保存失败'); await toast(res?.message || "保存失败");
} }
} }
useLoad(opts => { useLoad((opts) => {
type.value = opts?.type; type.value = opts?.type;
if (type.value === 'joinTeam' && opts.teamId) { if (type.value === "joinTeam" && opts.teamId) {
inviteTeamId.value = opts.teamId inviteTeamId.value = opts.teamId;
} }
})
useShow(() => {
getDoctorInfo()
}); });
useShow(() => {
getDoctorInfo();
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -7,7 +7,7 @@
<view class="relative user-avatar mr-10" @click="editProfile()"> <view class="relative user-avatar mr-10" @click="editProfile()">
<image v-if="doctorInfo && doctorInfo.avatar" class="avatar-img rounded-full overflow-hidden" <image v-if="doctorInfo && doctorInfo.avatar" class="avatar-img rounded-full overflow-hidden"
:src="doctorInfo.avatar" mode="aspectFill" /> :src="doctorInfo.avatar" mode="aspectFill" />
<image v-else class="avatar-img rounded-full overflow-hidden" src="/static/default-avatar.png" <image v-else class="avatar-img rounded-full overflow-hidden" src="/static/home/avatar.svg"
mode="aspectFill" /> mode="aspectFill" />
<view v-if="doctorInfo" class="edit-sub flex items-center justify-center rounded-full bg-primary"> <view v-if="doctorInfo" class="edit-sub flex items-center justify-center rounded-full bg-primary">
<image class="edit-icon" src="/static/work/pen.svg" mode="aspectFill" /> <image class="edit-icon" src="/static/work/pen.svg" mode="aspectFill" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -34,5 +34,18 @@ export default defineStore("teamStore", () => {
teams.value = res && Array.isArray(res.data) ? res.data : []; teams.value = res && Array.isArray(res.data) ? res.data : [];
} }
return { teams, chargeTeams, getTeam, getTeams } // 获取团队成员头像映射
async function getTeamMemberAvatars(teamId) {
if (!teamId || !account.value?.corpId) return {};
const res = await api('getTeamMemberAvatars', {
teamId,
corpId: account.value.corpId
});
if (res && res.success && res.data) {
return res.data; // 返回 { userId: avatar } 的映射对象
}
return {};
}
return { teams, chargeTeams, getTeam, getTeams, getTeamMemberAvatars }
}) })

View File

@ -25,7 +25,8 @@ const urlsConfig = {
createOwnTeam: 'createOwnTeam', createOwnTeam: 'createOwnTeam',
removeTeammate: "removeTeammate", removeTeammate: "removeTeammate",
toggleTeamLeaderRole: "toggleTeamLeaderRole", toggleTeamLeaderRole: "toggleTeamLeaderRole",
joinTheInvitedTeam: 'joinTheInvitedTeam' joinTheInvitedTeam: 'joinTheInvitedTeam',
getTeamMemberAvatars: 'getTeamMemberAvatars'
}, },
knowledgeBase: { knowledgeBase: {

View File

@ -60,7 +60,7 @@ export async function mergeConversationWithGroupDetails(conversationList, option
const formattedList = mergedList const formattedList = mergedList
.map((group) => ({ .map((group) => ({
conversationID: group.conversationID || `GROUP${group.groupID}`, conversationID: group.conversationID || `GROUP${group.groupID}`,
avatar: group.avatar || "/static/default-avatar.png", avatar: group.avatar || "/static/default-patient-avatar.png",
lastMessage: group.lastMessage || "暂无消息", lastMessage: group.lastMessage || "暂无消息",
lastMessageTime: group.lastMessageTime || Date.now(), lastMessageTime: group.lastMessageTime || Date.now(),
groupID: group.groupID, groupID: group.groupID,
@ -150,7 +150,7 @@ function mergeConversationData(conversation, groupDetailsMap) {
name: formatConversationName(groupDetail), name: formatConversationName(groupDetail),
// 更新头像(优先使用已有头像,避免闪动) // 更新头像(优先使用已有头像,避免闪动)
avatar: conversation.avatar || groupDetail.patient?.avatar || '/static/default-avatar.png' avatar: conversation.avatar || groupDetail.patient?.avatar
} }
} }

View File

@ -1086,7 +1086,7 @@ class TimChatManager {
const groupInfo = { const groupInfo = {
groupID: groupID, groupID: groupID,
name: group?.name || '问诊群聊', name: group?.name || '问诊群聊',
avatar: '/static/home/avatar.svg', // avatar: '/static/home/avatar.svg',
memberCount: group?.memberCount || 0 memberCount: group?.memberCount || 0
} }
@ -1172,7 +1172,7 @@ class TimChatManager {
name: conversation.groupProfile?.name || '问诊群聊', name: conversation.groupProfile?.name || '问诊群聊',
doctorId: '', doctorId: '',
patientName: '', patientName: '',
avatar: '/static/home/avatar.svg', // avatar: '/static/home/avatar.svg',
lastMessage: '获取失败', lastMessage: '获取失败',
lastMessageTime: Date.now(), lastMessageTime: Date.now(),
unreadCount: conversation.unreadCount || 0, unreadCount: conversation.unreadCount || 0,
@ -1573,7 +1573,6 @@ class TimChatManager {
} else if (dbMsg.createdAt) { } else if (dbMsg.createdAt) {
lastTime = new Date(dbMsg.createdAt).getTime() lastTime = new Date(dbMsg.createdAt).getTime()
} }
// 构建基础消息对象 // 构建基础消息对象
const message = { const message = {
ID: dbMsg.MsgSeq || dbMsg._id || `db_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, ID: dbMsg.MsgSeq || dbMsg._id || `db_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
@ -2569,7 +2568,6 @@ class TimChatManager {
conversationID, conversationID,
groupID, groupID,
name: patientName ? `${patientName}的问诊` : groupName || '问诊群聊', name: patientName ? `${patientName}的问诊` : groupName || '问诊群聊',
avatar: '/static/default-avatar.png',
lastMessage, lastMessage,
lastMessageTime, lastMessageTime,
unreadCount: conversation.unreadCount || 0, unreadCount: conversation.unreadCount || 0,
@ -2582,7 +2580,6 @@ class TimChatManager {
conversationID: conversation.conversationID, conversationID: conversation.conversationID,
groupID: conversation.conversationID?.replace('GROUP', '') || '', groupID: conversation.conversationID?.replace('GROUP', '') || '',
name: '问诊群聊', name: '问诊群聊',
avatar: '/static/default-avatar.png',
lastMessage: '暂无消息', lastMessage: '暂无消息',
lastMessageTime: Date.now(), lastMessageTime: Date.now(),
unreadCount: 0, unreadCount: 0,