no message
This commit is contained in:
parent
dae3b4d125
commit
8963a60391
@ -418,6 +418,25 @@ $primary-color: #0877F1;
|
||||
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 {
|
||||
flex: 1;
|
||||
margin: 0 8rpx;
|
||||
|
||||
@ -57,10 +57,12 @@ const props = defineProps({
|
||||
default: "",
|
||||
},
|
||||
});
|
||||
const emit = defineEmits(["streamText", "clearInput"]);
|
||||
const emit = defineEmits(["streamText", "clearInput", "generatingStateChange"]);
|
||||
|
||||
const typeSelectorRef = ref(null);
|
||||
const progressRef = ref(null);
|
||||
const isGenerating = ref(false);
|
||||
|
||||
const buttons = ref([
|
||||
{
|
||||
id: "followUp",
|
||||
@ -183,6 +185,8 @@ const streamTextToInput = (text) => {
|
||||
|
||||
// 先清空输入框
|
||||
emit("clearInput");
|
||||
isGenerating.value = true;
|
||||
emit("generatingStateChange", true);
|
||||
|
||||
let currentIndex = 0;
|
||||
const speed = 50; // 每个字符的延迟时间(毫秒)
|
||||
@ -196,6 +200,8 @@ const streamTextToInput = (text) => {
|
||||
currentIndex++;
|
||||
} else {
|
||||
clearInterval(streamInterval);
|
||||
isGenerating.value = false;
|
||||
emit("generatingStateChange", false);
|
||||
}
|
||||
}, speed);
|
||||
}, 100);
|
||||
@ -363,6 +369,11 @@ const handleRegenerateMedicalCase = (data) => {
|
||||
const type = { id: data.caseType };
|
||||
handleCaseTypeSelect(type);
|
||||
};
|
||||
|
||||
// 暴露生成状态给父组件
|
||||
defineExpose({
|
||||
isGenerating,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@ -8,18 +8,21 @@
|
||||
<view class="input-area">
|
||||
<textarea v-if="!showVoiceInput" class="text-input" v-model="inputText" placeholder="我来说两句..."
|
||||
@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"
|
||||
@touchmove="onRecordTouchMove" @touchend="stopRecord" @touchcancel="cancelRecord" :placeholder="isRecording ? '松开发送' : '按住说话'" disabled>
|
||||
</input>
|
||||
</view>
|
||||
<button v-if="inputText.trim()" class="send-btn" @click="sendTextMessage">
|
||||
<button v-if="inputText.trim() && !props.isGenerating" class="send-btn" @click="sendTextMessage">
|
||||
发送
|
||||
</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" />
|
||||
</view>
|
||||
<view v-else class="send-btn disabled-btn">
|
||||
<text>生成中...</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="more-panel" v-if="showMorePanel">
|
||||
<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: "" },
|
||||
corpId: { type: String, default: "" },
|
||||
orderStatus: { type: String, default: "" },
|
||||
isGenerating: { type: Boolean, default: false },
|
||||
});
|
||||
|
||||
// Emits
|
||||
|
||||
@ -140,12 +140,14 @@
|
||||
!showConsultAccept &&
|
||||
orderStatus === 'processing'
|
||||
"
|
||||
ref="aiAssistantRef"
|
||||
:groupId="groupId"
|
||||
:patientAccountId="chatInfo.userID || ''"
|
||||
:patientId="patientId"
|
||||
:corpId="corpId"
|
||||
@streamText="handleStreamText"
|
||||
@clearInput="handleClearInput"
|
||||
@generatingStateChange="handleGeneratingStateChange"
|
||||
/>
|
||||
|
||||
<!-- 聊天输入组件 -->
|
||||
@ -165,6 +167,7 @@
|
||||
:corpId="corpId"
|
||||
:patientInfo="patientInfo"
|
||||
:orderStatus="orderStatus"
|
||||
:isGenerating="isGenerating"
|
||||
@scrollToBottom="() => scrollToBottom(true)"
|
||||
@messageSent="() => scrollToBottom(true)"
|
||||
@endConsult="handleEndConsult"
|
||||
@ -217,6 +220,8 @@ const { initIMAfterLogin } = useAccountStore();
|
||||
|
||||
// 聊天输入组件引用
|
||||
const chatInputRef = ref(null);
|
||||
const aiAssistantRef = ref(null);
|
||||
const isGenerating = ref(false);
|
||||
|
||||
const groupId = ref("");
|
||||
const { chatMember, getGroupInfo, getUserAvatar } = useGroupChat(groupId);
|
||||
@ -316,7 +321,11 @@ const fetchGroupOrderStatus = async () => {
|
||||
const teamName = result.data.team?.name || "群聊";
|
||||
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) {
|
||||
@ -765,7 +774,7 @@ onShow(() => {
|
||||
startIMMonitoring(30000);
|
||||
|
||||
// 监听回访任务发送事件
|
||||
uni.$on('send-followup-message', handleSendFollowUpMessage);
|
||||
uni.$on("send-followup-message", handleSendFollowUpMessage);
|
||||
});
|
||||
|
||||
// 处理发送回访任务消息
|
||||
@ -774,7 +783,7 @@ const handleSendFollowUpMessage = async (data) => {
|
||||
if (chatInputRef.value) {
|
||||
// 将回访计划内容设置到输入框
|
||||
chatInputRef.value.setInputText(data.content);
|
||||
|
||||
|
||||
// 延迟后自动发送
|
||||
setTimeout(() => {
|
||||
if (chatInputRef.value) {
|
||||
@ -783,10 +792,10 @@ const handleSendFollowUpMessage = async (data) => {
|
||||
}, 100);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('发送回访任务消息失败:', error);
|
||||
console.error("发送回访任务消息失败:", error);
|
||||
uni.showToast({
|
||||
title: '发送失败,请重试',
|
||||
icon: 'none',
|
||||
title: "发送失败,请重试",
|
||||
icon: "none",
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -820,6 +829,11 @@ const handleClearInput = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// 处理生成状态变化
|
||||
const handleGeneratingStateChange = (generating) => {
|
||||
isGenerating.value = generating;
|
||||
};
|
||||
|
||||
// 暴露方法给常用语页面调用
|
||||
defineExpose({
|
||||
sendCommonPhrase,
|
||||
@ -1016,7 +1030,7 @@ onUnmounted(() => {
|
||||
timChatManager.setCallback("onError", null);
|
||||
|
||||
// 移除回访任务发送事件监听
|
||||
uni.$off('send-followup-message', handleSendFollowUpMessage);
|
||||
uni.$off("send-followup-message", handleSendFollowUpMessage);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@ -57,8 +57,6 @@ export async function sendImageMessage(imageUrl, imageName = '图片') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 直接调用 tim-chat 的 sendImageMessage 方法
|
||||
// tim-chat.js 中的 getImageUrl 方法可以处理 URL 字符串
|
||||
const result = await globalTimChatManager.sendImageMessage(imageUrl);
|
||||
|
||||
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