no message
This commit is contained in:
parent
dae3b4d125
commit
8963a60391
@ -418,6 +418,25 @@ $primary-color: #0877F1;
|
|||||||
background: #2456c7;
|
background: #2456c7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.disabled-btn {
|
||||||
|
background: #ccc;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
border: none;
|
||||||
|
border-radius: 40rpx;
|
||||||
|
height: 56rpx;
|
||||||
|
min-width: 112rpx;
|
||||||
|
padding: 0 32rpx;
|
||||||
|
box-shadow: 0 2rpx 8rpx rgba(200, 200, 200, 0.08);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
.input-area {
|
.input-area {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
margin: 0 8rpx;
|
margin: 0 8rpx;
|
||||||
|
|||||||
@ -57,10 +57,12 @@ const props = defineProps({
|
|||||||
default: "",
|
default: "",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const emit = defineEmits(["streamText", "clearInput"]);
|
const emit = defineEmits(["streamText", "clearInput", "generatingStateChange"]);
|
||||||
|
|
||||||
const typeSelectorRef = ref(null);
|
const typeSelectorRef = ref(null);
|
||||||
const progressRef = ref(null);
|
const progressRef = ref(null);
|
||||||
|
const isGenerating = ref(false);
|
||||||
|
|
||||||
const buttons = ref([
|
const buttons = ref([
|
||||||
{
|
{
|
||||||
id: "followUp",
|
id: "followUp",
|
||||||
@ -183,6 +185,8 @@ const streamTextToInput = (text) => {
|
|||||||
|
|
||||||
// 先清空输入框
|
// 先清空输入框
|
||||||
emit("clearInput");
|
emit("clearInput");
|
||||||
|
isGenerating.value = true;
|
||||||
|
emit("generatingStateChange", true);
|
||||||
|
|
||||||
let currentIndex = 0;
|
let currentIndex = 0;
|
||||||
const speed = 50; // 每个字符的延迟时间(毫秒)
|
const speed = 50; // 每个字符的延迟时间(毫秒)
|
||||||
@ -196,6 +200,8 @@ const streamTextToInput = (text) => {
|
|||||||
currentIndex++;
|
currentIndex++;
|
||||||
} else {
|
} else {
|
||||||
clearInterval(streamInterval);
|
clearInterval(streamInterval);
|
||||||
|
isGenerating.value = false;
|
||||||
|
emit("generatingStateChange", false);
|
||||||
}
|
}
|
||||||
}, speed);
|
}, speed);
|
||||||
}, 100);
|
}, 100);
|
||||||
@ -363,6 +369,11 @@ const handleRegenerateMedicalCase = (data) => {
|
|||||||
const type = { id: data.caseType };
|
const type = { id: data.caseType };
|
||||||
handleCaseTypeSelect(type);
|
handleCaseTypeSelect(type);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 暴露生成状态给父组件
|
||||||
|
defineExpose({
|
||||||
|
isGenerating,
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@ -8,18 +8,21 @@
|
|||||||
<view class="input-area">
|
<view class="input-area">
|
||||||
<textarea v-if="!showVoiceInput" class="text-input" v-model="inputText" placeholder="我来说两句..."
|
<textarea v-if="!showVoiceInput" class="text-input" v-model="inputText" placeholder="我来说两句..."
|
||||||
@confirm="sendTextMessage" @focus="handleInputFocus" @input="handleInput"
|
@confirm="sendTextMessage" @focus="handleInputFocus" @input="handleInput"
|
||||||
:auto-height="true" :show-confirm-bar="false" :adjust-position="true" :cursor-spacing="60"
|
:auto-height="true" :show-confirm-bar="false" :adjust-position="true" :cursor-spacing="30"
|
||||||
/>
|
/>
|
||||||
<input v-else class="voice-input-btn" :class="{ recording: isRecording }" @touchstart="startRecord"
|
<input v-else class="voice-input-btn" :class="{ recording: isRecording }" @touchstart="startRecord"
|
||||||
@touchmove="onRecordTouchMove" @touchend="stopRecord" @touchcancel="cancelRecord" :placeholder="isRecording ? '松开发送' : '按住说话'" disabled>
|
@touchmove="onRecordTouchMove" @touchend="stopRecord" @touchcancel="cancelRecord" :placeholder="isRecording ? '松开发送' : '按住说话'" disabled>
|
||||||
</input>
|
</input>
|
||||||
</view>
|
</view>
|
||||||
<button v-if="inputText.trim()" class="send-btn" @click="sendTextMessage">
|
<button v-if="inputText.trim() && !props.isGenerating" class="send-btn" @click="sendTextMessage">
|
||||||
发送
|
发送
|
||||||
</button>
|
</button>
|
||||||
<view v-else class="plus-btn" @click="toggleMorePanel()">
|
<view v-else-if="!inputText.trim() && !props.isGenerating" class="plus-btn" @click="toggleMorePanel()">
|
||||||
<uni-icons type="plusempty" size="28" color="#666" />
|
<uni-icons type="plusempty" size="28" color="#666" />
|
||||||
</view>
|
</view>
|
||||||
|
<view v-else class="send-btn disabled-btn">
|
||||||
|
<text>生成中...</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="more-panel" v-if="showMorePanel">
|
<view class="more-panel" v-if="showMorePanel">
|
||||||
<view v-for="btn in morePanelButtons" :key="btn.text" class="more-btn" @click="btn.action">
|
<view v-for="btn in morePanelButtons" :key="btn.text" class="more-btn" @click="btn.action">
|
||||||
@ -80,6 +83,7 @@ const props = defineProps({
|
|||||||
patientId: { type: String, default: "" },
|
patientId: { type: String, default: "" },
|
||||||
corpId: { type: String, default: "" },
|
corpId: { type: String, default: "" },
|
||||||
orderStatus: { type: String, default: "" },
|
orderStatus: { type: String, default: "" },
|
||||||
|
isGenerating: { type: Boolean, default: false },
|
||||||
});
|
});
|
||||||
|
|
||||||
// Emits
|
// Emits
|
||||||
|
|||||||
@ -140,12 +140,14 @@
|
|||||||
!showConsultAccept &&
|
!showConsultAccept &&
|
||||||
orderStatus === 'processing'
|
orderStatus === 'processing'
|
||||||
"
|
"
|
||||||
|
ref="aiAssistantRef"
|
||||||
:groupId="groupId"
|
:groupId="groupId"
|
||||||
:patientAccountId="chatInfo.userID || ''"
|
:patientAccountId="chatInfo.userID || ''"
|
||||||
:patientId="patientId"
|
:patientId="patientId"
|
||||||
:corpId="corpId"
|
:corpId="corpId"
|
||||||
@streamText="handleStreamText"
|
@streamText="handleStreamText"
|
||||||
@clearInput="handleClearInput"
|
@clearInput="handleClearInput"
|
||||||
|
@generatingStateChange="handleGeneratingStateChange"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- 聊天输入组件 -->
|
<!-- 聊天输入组件 -->
|
||||||
@ -165,6 +167,7 @@
|
|||||||
:corpId="corpId"
|
:corpId="corpId"
|
||||||
:patientInfo="patientInfo"
|
:patientInfo="patientInfo"
|
||||||
:orderStatus="orderStatus"
|
:orderStatus="orderStatus"
|
||||||
|
:isGenerating="isGenerating"
|
||||||
@scrollToBottom="() => scrollToBottom(true)"
|
@scrollToBottom="() => scrollToBottom(true)"
|
||||||
@messageSent="() => scrollToBottom(true)"
|
@messageSent="() => scrollToBottom(true)"
|
||||||
@endConsult="handleEndConsult"
|
@endConsult="handleEndConsult"
|
||||||
@ -217,6 +220,8 @@ const { initIMAfterLogin } = useAccountStore();
|
|||||||
|
|
||||||
// 聊天输入组件引用
|
// 聊天输入组件引用
|
||||||
const chatInputRef = ref(null);
|
const chatInputRef = ref(null);
|
||||||
|
const aiAssistantRef = ref(null);
|
||||||
|
const isGenerating = ref(false);
|
||||||
|
|
||||||
const groupId = ref("");
|
const groupId = ref("");
|
||||||
const { chatMember, getGroupInfo, getUserAvatar } = useGroupChat(groupId);
|
const { chatMember, getGroupInfo, getUserAvatar } = useGroupChat(groupId);
|
||||||
@ -316,7 +321,11 @@ const fetchGroupOrderStatus = async () => {
|
|||||||
const teamName = result.data.team?.name || "群聊";
|
const teamName = result.data.team?.name || "群聊";
|
||||||
updateNavigationTitle(teamName);
|
updateNavigationTitle(teamName);
|
||||||
|
|
||||||
teamId.value = result.data.teamId || result.data.team?.teamId || result.data.team?._id || "";
|
teamId.value =
|
||||||
|
result.data.teamId ||
|
||||||
|
result.data.team?.teamId ||
|
||||||
|
result.data.team?._id ||
|
||||||
|
"";
|
||||||
|
|
||||||
// 更新患者信息
|
// 更新患者信息
|
||||||
if (result.data.patient) {
|
if (result.data.patient) {
|
||||||
@ -765,7 +774,7 @@ onShow(() => {
|
|||||||
startIMMonitoring(30000);
|
startIMMonitoring(30000);
|
||||||
|
|
||||||
// 监听回访任务发送事件
|
// 监听回访任务发送事件
|
||||||
uni.$on('send-followup-message', handleSendFollowUpMessage);
|
uni.$on("send-followup-message", handleSendFollowUpMessage);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 处理发送回访任务消息
|
// 处理发送回访任务消息
|
||||||
@ -783,10 +792,10 @@ const handleSendFollowUpMessage = async (data) => {
|
|||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('发送回访任务消息失败:', error);
|
console.error("发送回访任务消息失败:", error);
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '发送失败,请重试',
|
title: "发送失败,请重试",
|
||||||
icon: 'none',
|
icon: "none",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -820,6 +829,11 @@ const handleClearInput = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 处理生成状态变化
|
||||||
|
const handleGeneratingStateChange = (generating) => {
|
||||||
|
isGenerating.value = generating;
|
||||||
|
};
|
||||||
|
|
||||||
// 暴露方法给常用语页面调用
|
// 暴露方法给常用语页面调用
|
||||||
defineExpose({
|
defineExpose({
|
||||||
sendCommonPhrase,
|
sendCommonPhrase,
|
||||||
@ -1016,7 +1030,7 @@ onUnmounted(() => {
|
|||||||
timChatManager.setCallback("onError", null);
|
timChatManager.setCallback("onError", null);
|
||||||
|
|
||||||
// 移除回访任务发送事件监听
|
// 移除回访任务发送事件监听
|
||||||
uni.$off('send-followup-message', handleSendFollowUpMessage);
|
uni.$off("send-followup-message", handleSendFollowUpMessage);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -57,8 +57,6 @@ export async function sendImageMessage(imageUrl, imageName = '图片') {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 直接调用 tim-chat 的 sendImageMessage 方法
|
|
||||||
// tim-chat.js 中的 getImageUrl 方法可以处理 URL 字符串
|
|
||||||
const result = await globalTimChatManager.sendImageMessage(imageUrl);
|
const result = await globalTimChatManager.sendImageMessage(imageUrl);
|
||||||
|
|
||||||
if (result?.success) {
|
if (result?.success) {
|
||||||
|
|||||||
@ -1,234 +0,0 @@
|
|||||||
# 微信小程序分享功能使用指南
|
|
||||||
|
|
||||||
## 功能说明
|
|
||||||
|
|
||||||
提供了完整的微信小程序分享功能,包括:
|
|
||||||
- 分享给好友
|
|
||||||
- 分享到朋友圈
|
|
||||||
- 保存图片到相册
|
|
||||||
|
|
||||||
## 使用方法
|
|
||||||
|
|
||||||
### 1. 基础分享(在页面中)
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<view>
|
|
||||||
<button open-type="share">分享给好友</button>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { createShareMessage, createShareTimeline } from '@/utils/share'
|
|
||||||
|
|
||||||
// 分享给好友
|
|
||||||
function onShareAppMessage() {
|
|
||||||
return createShareMessage({
|
|
||||||
title: '分享标题',
|
|
||||||
path: '/pages/index/index?id=123',
|
|
||||||
imageUrl: 'https://example.com/share.jpg'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 分享到朋友圈(需要在 app.json 中配置)
|
|
||||||
function onShareTimeline() {
|
|
||||||
return createShareTimeline({
|
|
||||||
title: '朋友圈标题',
|
|
||||||
query: 'id=123',
|
|
||||||
imageUrl: 'https://example.com/share.jpg'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 导出分享方法
|
|
||||||
defineExpose({
|
|
||||||
onShareAppMessage,
|
|
||||||
onShareTimeline
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 使用分享组件
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<view>
|
|
||||||
<share-actions
|
|
||||||
@save="handleSave"
|
|
||||||
:show-save="true"
|
|
||||||
:show-share="true"
|
|
||||||
save-text="保存图片"
|
|
||||||
share-text="分享微信"
|
|
||||||
/>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { saveImageToAlbum, createShareMessage } from '@/utils/share'
|
|
||||||
import shareActions from '@/components/share-actions.vue'
|
|
||||||
|
|
||||||
// 保存图片
|
|
||||||
async function handleSave() {
|
|
||||||
const imagePath = 'https://example.com/image.jpg'
|
|
||||||
await saveImageToAlbum(imagePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 分享配置
|
|
||||||
function onShareAppMessage() {
|
|
||||||
return createShareMessage({
|
|
||||||
title: '分享标题',
|
|
||||||
path: '/pages/index/index'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
defineExpose({
|
|
||||||
onShareAppMessage
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. 保存二维码图片
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<template>
|
|
||||||
<view>
|
|
||||||
<uqrcode ref="qrcode" canvasId="qrcode" :value="qrcodeUrl" />
|
|
||||||
<button @click="saveQrcode">保存二维码</button>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref } from 'vue'
|
|
||||||
import { saveImageToAlbum } from '@/utils/share'
|
|
||||||
import { toast } from '@/utils/widget'
|
|
||||||
|
|
||||||
const qrcode = ref(null)
|
|
||||||
const qrcodeUrl = ref('https://example.com')
|
|
||||||
|
|
||||||
async function saveQrcode() {
|
|
||||||
try {
|
|
||||||
if (!qrcode.value) {
|
|
||||||
toast('二维码未加载完成')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取二维码临时文件路径
|
|
||||||
const tempFilePath = qrcode.value.toTempFilePath()
|
|
||||||
if (tempFilePath) {
|
|
||||||
await saveImageToAlbum(tempFilePath)
|
|
||||||
} else {
|
|
||||||
toast('获取二维码失败')
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error('保存失败:', err)
|
|
||||||
toast('保存失败')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. 动态分享内容
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<script setup>
|
|
||||||
import { ref, computed } from 'vue'
|
|
||||||
import { createShareMessage } from '@/utils/share'
|
|
||||||
|
|
||||||
const currentItem = ref({
|
|
||||||
id: '123',
|
|
||||||
title: '商品标题',
|
|
||||||
image: 'https://example.com/product.jpg'
|
|
||||||
})
|
|
||||||
|
|
||||||
// 动态生成分享配置
|
|
||||||
function onShareAppMessage() {
|
|
||||||
return createShareMessage({
|
|
||||||
title: currentItem.value.title,
|
|
||||||
path: `/pages/detail/detail?id=${currentItem.value.id}`,
|
|
||||||
imageUrl: currentItem.value.image
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
defineExpose({
|
|
||||||
onShareAppMessage
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
## 配置说明
|
|
||||||
|
|
||||||
### 1. 启用分享到朋友圈
|
|
||||||
|
|
||||||
在 `pages.json` 中配置页面:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"path": "pages/index/index",
|
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "首页",
|
|
||||||
"enableShareTimeline": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 全局分享配置
|
|
||||||
|
|
||||||
在 `App.vue` 中配置全局分享:
|
|
||||||
|
|
||||||
```vue
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
onShareAppMessage() {
|
|
||||||
return {
|
|
||||||
title: '默认分享标题',
|
|
||||||
path: '/pages/index/index'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onShareTimeline() {
|
|
||||||
return {
|
|
||||||
title: '默认朋友圈标题'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
## API 说明
|
|
||||||
|
|
||||||
### createShareMessage(options)
|
|
||||||
|
|
||||||
创建分享给好友的配置
|
|
||||||
|
|
||||||
**参数:**
|
|
||||||
- `title` (string): 分享标题
|
|
||||||
- `path` (string): 分享路径
|
|
||||||
- `imageUrl` (string): 分享图片URL
|
|
||||||
|
|
||||||
**返回:** 分享配置对象
|
|
||||||
|
|
||||||
### createShareTimeline(options)
|
|
||||||
|
|
||||||
创建分享到朋友圈的配置
|
|
||||||
|
|
||||||
**参数:**
|
|
||||||
- `title` (string): 分享标题
|
|
||||||
- `query` (string): 分享路径参数
|
|
||||||
- `imageUrl` (string): 分享图片URL
|
|
||||||
|
|
||||||
**返回:** 分享配置对象
|
|
||||||
|
|
||||||
### saveImageToAlbum(filePath)
|
|
||||||
|
|
||||||
保存图片到相册
|
|
||||||
|
|
||||||
**参数:**
|
|
||||||
- `filePath` (string): 图片路径(本地临时路径或网络路径)
|
|
||||||
|
|
||||||
**返回:** Promise<boolean>
|
|
||||||
|
|
||||||
## 注意事项
|
|
||||||
|
|
||||||
1. 分享图片建议尺寸:5:4,推荐 500x400px
|
|
||||||
2. 分享路径必须是已注册的页面路径
|
|
||||||
3. 保存图片需要用户授权相册权限
|
|
||||||
4. 分享到朋友圈需要在页面配置中启用
|
|
||||||
5. 网络图片会自动下载后保存到相册
|
|
||||||
Loading…
x
Reference in New Issue
Block a user