feat: 医生端支持文章配置视频号

This commit is contained in:
huxuejian 2026-04-17 13:51:02 +08:00
parent 828f3a4510
commit ff1de948c9
4 changed files with 83 additions and 108 deletions

View File

@ -3,7 +3,7 @@ MP_IMAGE_URL=https://patient.youcan365.com
MP_CACHE_PREFIX=development
MP_WX_APP_ID=wx1d8337a40c11d66c
MP_CORP_ID=wwe3fb2faa52cf9dfb
MP_TIM_SDK_APP_ID=1600123876
MP_TIM_SDK_APP_ID=1600126296
MP_INVITE_TEAMMATE_QRCODE=https://patient.youcan365.com/invite-teammate
MP_INVITE_PATIENT_QRCODE=https://patient.youcan365.com/invite-patient
MP_PATIENT_PAGE_BASE_URL= 'https://www.youcan365.com/patientDeploy/#/'

View File

@ -335,6 +335,7 @@ const sendArticle = async (article) => {
cover: article.cover || "",
url: article.url || "",
subtitle: article.subtitle || "",
wechatChannels: article.wechatChannels,
},
{
articleId: article._id,

View File

@ -5,57 +5,31 @@
</text>
<!-- 图片消息 -->
<image
v-else-if="message.type === 'TIMImageElem'"
class="message-image"
:src="
message.payload.imageInfoArray[0].LocalURL ||
message.payload.imageInfoArray[0].url
"
mode="aspectFill"
:style="getImageStyle(message.payload.imageInfoArray[0])"
@click="
<image v-else-if="message.type === 'TIMImageElem'" class="message-image" :src="message.payload.imageInfoArray[0].LocalURL ||
message.payload.imageInfoArray[0].url
" mode="aspectFill" :style="getImageStyle(message.payload.imageInfoArray[0])" @click="
$emit(
'previewImage',
message.payload.imageInfoArray[0].LocalURL ||
message.payload.imageInfoArray[0].url
message.payload.imageInfoArray[0].url
)
"
/>
" />
<!-- 语音消息 -->
<view
v-else-if="message.type === 'TIMSoundElem'"
class="voice-message"
:class="{ 'voice-playing': isPlaying }"
:style="getVoiceStyle(message.payload.second)"
@click="$emit('playVoice', message)"
>
<view v-else-if="message.type === 'TIMSoundElem'" class="voice-message" :class="{ 'voice-playing': isPlaying }"
:style="getVoiceStyle(message.payload.second)" @click="$emit('playVoice', message)">
<view class="voice-content">
<view class="voice-icon-wrapper">
<uni-icons
type="sound"
size="20"
:color="message.flow === 'out' ? '#fff' : '#333'"
:class="{ 'icon-animate': isPlaying }"
/>
<uni-icons type="sound" size="20" :color="message.flow === 'out' ? '#fff' : '#333'"
:class="{ 'icon-animate': isPlaying }" />
<!-- 播放中的声波动画 -->
<view v-if="isPlaying" class="sound-wave">
<view
class="wave-bar"
:style="{ background: message.flow === 'out' ? '#fff' : '#0877f1' }"
style="animation-delay: 0s"
></view>
<view
class="wave-bar"
:style="{ background: message.flow === 'out' ? '#fff' : '#0877f1' }"
style="animation-delay: 0.2s"
></view>
<view
class="wave-bar"
:style="{ background: message.flow === 'out' ? '#fff' : '#0877f1' }"
style="animation-delay: 0.4s"
></view>
<view class="wave-bar" :style="{ background: message.flow === 'out' ? '#fff' : '#0877f1' }"
style="animation-delay: 0s"></view>
<view class="wave-bar" :style="{ background: message.flow === 'out' ? '#fff' : '#0877f1' }"
style="animation-delay: 0.2s"></view>
<view class="wave-bar" :style="{ background: message.flow === 'out' ? '#fff' : '#0877f1' }"
style="animation-delay: 0.4s"></view>
</view>
</view>
<text class="voice-duration">{{ message.payload.second }}"</text>
@ -65,39 +39,21 @@
<!-- 自定义消息卡片 -->
<template v-else-if="message.type === 'TIMCustomElem'">
<!-- 文章消息 -->
<view
v-if="getCustomMessageType(message) === 'article'"
class="article-card"
@click="handleArticleClick(message)"
>
<view v-if="customMessageType === 'article'" class="article-card" @click="handleArticleClick(message)">
<view class="article-content">
<view class="article-title">{{ getArticleData(message).title }}</view>
<view class="article-desc">{{ getArticleData(message).desc }}</view>
<view class="article-title">{{ articleData.title }}</view>
<view class="article-desc">{{ articleData.desc }}</view>
</view>
<image
v-if="getArticleData(message).imgUrl"
class="article-image"
:src="getArticleData(message).imgUrl"
mode="aspectFill"
/>
<image v-if="articleData.imgUrl" class="article-image" :src="articleData.imgUrl" mode="aspectFill" />
</view>
<!-- 问卷消息 -->
<view
v-else-if="getCustomMessageType(message) === 'survey'"
class="survey-card"
@click="handleSurveyClick(message)"
>
<view v-else-if="customMessageType === 'survey'" class="survey-card" @click="handleSurveyClick(message)">
<view class="survey-content">
<view class="survey-title">{{ getSurveyData(message).title }}</view>
<view class="survey-desc">{{ getSurveyData(message).desc }}</view>
<view class="survey-title">{{ surveyData.title }}</view>
<view class="survey-desc">{{ surveyData.desc }}</view>
</view>
<image
v-if="getSurveyData(message).imgUrl"
class="survey-image"
:src="getSurveyData(message).imgUrl"
mode="aspectFill"
/>
<image v-if="surveyData.imgUrl" class="survey-image" :src="surveyData.imgUrl" mode="aspectFill" />
</view>
<!-- 其他自定义消息 -->
@ -119,6 +75,7 @@
<script setup>
import { computed } from "vue";
import { getParsedCustomMessage } from "@/utils/chat-utils.js";
import { toast } from '@/utils/widget'
// import MessageCard from "./message-card/message-card.vue";
const props = defineProps({
@ -138,6 +95,35 @@ const isPlaying = computed(() => {
return props.playingVoiceId === props.message.ID;
});
const payloadData = computed(() => {
try {
return JSON.parse(props.message.payload.data);
} catch (e) {
return {}
}
})
const customMessageType = computed(() => {
return payloadData.value.type || ''
})
const articleData = computed(() => {
const article = {
title: "宣教文章",
desc: "宣教文章",
url: "",
imgUrl: "",
}
return customMessageType.value === 'article' ? payloadData.value : article;
})
const surveyData = computed(() => ({
title: payloadData.value.title || "填写问卷",
desc: payloadData.value.desc || "请填写问卷",
url: payloadData.value.url || "",
imgUrl: payloadData.value.imgUrl || "",
}))
//
const getImageStyle = (imageInfo) => {
// 使
@ -202,19 +188,6 @@ const getVoiceStyle = (duration) => {
};
};
//
const getCustomMessageType = (message) => {
try {
if (message.payload && message.payload.data) {
const data = JSON.parse(message.payload.data);
return data.type || "";
}
} catch (error) {
console.error("解析自定义消息失败:", error);
}
return "";
};
//
const getArticleData = (message) => {
try {
@ -235,48 +208,48 @@ const getArticleData = (message) => {
//
const handleArticleClick = (message) => {
if (isWechatChannels(payloadData.value)) {
uni.openChannelsActivity({
finderUserName: payloadData.value.wechatChannels.finderUserName,
feedId: payloadData.value.wechatChannels.feedId,
fail: err => {
const errMsg = err.errMsg || '';
if (/cancel/i.test(errMsg)) {
return
}
toast(`打开视频号失败:${errMsg}`)
}
})
return
}
const { articleId } = getArticleData(message);
uni.navigateTo({
url: `/pages/message/article-detail?id=${articleId}`,
});
};
//
const getSurveyData = (message) => {
try {
if (message.payload && message.payload.data) {
const data = JSON.parse(message.payload.data);
return {
title: data.title || "填写问卷",
desc: data.desc || "请填写问卷",
url: data.url || "",
imgUrl: data.imgUrl || "",
};
}
} catch (error) {
console.error("解析问卷数据失败:", error);
}
return {
title: "填写问卷",
desc: "请填写问卷",
url: "",
imgUrl: "",
};
};
//
const handleSurveyClick = (message) => {
const surveyData = getSurveyData(message);
if (surveyData.url) {
if (surveyData.value.url) {
//
console.log("打开问卷:", surveyData.url);
console.log("打开问卷:", surveyData.value.url);
// uni.navigateTo({
// url: `/pages/survey/fill?url=${encodeURIComponent(surveyData.url)}`
// url: `/pages/survey/fill?url=${encodeURIComponent(surveyData.value.url)}`
// });
}
};
function isWechatChannels(data) {
const wechatChannels = data.wechatChannels || {};
return typeof wechatChannels.finderUserName === 'string' && wechatChannels.finderUserName.trim() && typeof wechatChannels.feedId === 'string' && wechatChannels.feedId.trim();
}
</script>
<style scoped lang="scss">
@import "../chat.scss";
.channel-video {
width: 480rpx;
height: 320rpx;
}
</style>

View File

@ -179,6 +179,7 @@ export async function sendArticleMessage(article, options = {}) {
desc: article.subtitle || '点击查看详情',
url: article.url || '',
messageType: 'article',
wechatChannels: article.wechatChannels || '',
};
if (options.articleId && options.userId && options.customerId && options.corpId) {
const params = {