Compare commits
No commits in common. "1332b12b03cd5fc392e83601cafb0d6e9797a338" and "25966f97f4b7dd1b988a79b64c4fc5609be0b1cb" have entirely different histories.
1332b12b03
...
25966f97f4
12
pages.json
12
pages.json
@ -19,18 +19,6 @@
|
|||||||
"navigationBarTitleText": "常用语"
|
"navigationBarTitleText": "常用语"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "pages/message/article-list",
|
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "宣教文章"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "pages/message/survey-list",
|
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "问卷列表"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "pages/work/work",
|
"path": "pages/work/work",
|
||||||
"style": {
|
"style": {
|
||||||
|
|||||||
95
pages/message/README.md
Normal file
95
pages/message/README.md
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
# 常用语功能说明
|
||||||
|
|
||||||
|
## 功能概述
|
||||||
|
在聊天页面添加了常用语功能,医生可以快速选择并发送预设的常用语内容。
|
||||||
|
|
||||||
|
## 功能特性
|
||||||
|
|
||||||
|
### 1. 常用语列表页面 (`common-phrases.vue`)
|
||||||
|
- 支持分类管理(文字随访、语音随访、常用回复等)
|
||||||
|
- 支持添加、编辑、删除常用语
|
||||||
|
- 支持添加自定义分类
|
||||||
|
- 点击常用语直接发送到聊天
|
||||||
|
|
||||||
|
### 2. 聊天输入框集成
|
||||||
|
- 在聊天输入框的"更多"面板中添加"常用语"入口
|
||||||
|
- 点击后跳转到常用语列表页面
|
||||||
|
- 选择常用语后自动返回并发送
|
||||||
|
|
||||||
|
### 3. 数据持久化
|
||||||
|
- 常用语数据存储在MongoDB的`common-words`集合中
|
||||||
|
- 支持机构级别和个人级别的常用语
|
||||||
|
- 与PC端管理后台数据互通
|
||||||
|
|
||||||
|
## 文件结构
|
||||||
|
|
||||||
|
```
|
||||||
|
ykt-wx-app/pages/message/
|
||||||
|
├── common-phrases.vue # 常用语列表页面
|
||||||
|
├── components/
|
||||||
|
│ └── chat-input.vue # 聊天输入框组件(已更新)
|
||||||
|
├── index.vue # 聊天主页面(已更新)
|
||||||
|
└── README.md # 本文档
|
||||||
|
|
||||||
|
ykt-wx-app/utils/
|
||||||
|
└── api.js # API配置(已添加常用语接口)
|
||||||
|
|
||||||
|
ytk-customer-service/knowledgeBase/common-words/
|
||||||
|
└── index.js # 后端常用语接口(已添加小程序接口)
|
||||||
|
```
|
||||||
|
|
||||||
|
## API接口
|
||||||
|
|
||||||
|
### 1. 获取常用语列表
|
||||||
|
```javascript
|
||||||
|
api('getCommonPhrases', { corpId })
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 保存常用语
|
||||||
|
```javascript
|
||||||
|
api('saveCommonPhrase', {
|
||||||
|
id, // 可选,更新时传入
|
||||||
|
corpId,
|
||||||
|
userId,
|
||||||
|
categoryId,
|
||||||
|
content
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 删除常用语
|
||||||
|
```javascript
|
||||||
|
api('deleteCommonPhrase', {
|
||||||
|
id,
|
||||||
|
corpId
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 获取分类列表
|
||||||
|
```javascript
|
||||||
|
api('getCommonPhraseCategories', { corpId })
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. 保存分类
|
||||||
|
```javascript
|
||||||
|
api('saveCommonPhraseCategory', {
|
||||||
|
corpId,
|
||||||
|
userId,
|
||||||
|
name
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用流程
|
||||||
|
|
||||||
|
1. 用户在聊天页面点击输入框右侧的"+"按钮
|
||||||
|
2. 在弹出的功能面板中点击"常用语"
|
||||||
|
3. 跳转到常用语列表页面
|
||||||
|
4. 可以切换分类查看不同类型的常用语
|
||||||
|
5. 点击任意常用语,自动返回聊天页面并发送该内容
|
||||||
|
6. 在编辑模式下可以添加、编辑、删除常用语
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. 常用语功能需要用户已登录并有有效的corpId
|
||||||
|
2. 如果后端API调用失败,会使用本地模拟数据
|
||||||
|
3. 常用语内容最多500字
|
||||||
|
4. 分类名称最多6个字
|
||||||
@ -1,503 +0,0 @@
|
|||||||
<template>
|
|
||||||
<view class="article-page">
|
|
||||||
<view class="header">
|
|
||||||
<view class="search-bar">
|
|
||||||
<uni-icons type="search" size="18" color="#999" />
|
|
||||||
<input
|
|
||||||
class="search-input"
|
|
||||||
v-model="searchTitle"
|
|
||||||
placeholder="输入内容名称搜索"
|
|
||||||
@input="handleSearch"
|
|
||||||
/>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="content">
|
|
||||||
<view class="category-sidebar">
|
|
||||||
<scroll-view scroll-y class="category-scroll">
|
|
||||||
<view
|
|
||||||
v-for="cate in categoryList"
|
|
||||||
:key="cate._id || 'all'"
|
|
||||||
class="category-item"
|
|
||||||
:class="{ active: currentCateId === (cate._id || 'all') }"
|
|
||||||
@click="selectCategory(cate)"
|
|
||||||
>
|
|
||||||
{{ cate.name }}
|
|
||||||
</view>
|
|
||||||
</scroll-view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="article-list">
|
|
||||||
<scroll-view
|
|
||||||
scroll-y
|
|
||||||
class="article-scroll"
|
|
||||||
@scrolltolower="loadMore"
|
|
||||||
lower-threshold="50"
|
|
||||||
>
|
|
||||||
<view
|
|
||||||
v-if="loading && articleList.length === 0"
|
|
||||||
class="loading-container"
|
|
||||||
>
|
|
||||||
<uni-icons type="spinner-cycle" size="30" color="#999" />
|
|
||||||
<text class="loading-text">加载中...</text>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view v-else-if="articleList.length === 0" class="empty-container">
|
|
||||||
<empty-data title="暂无文章" />
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view v-else>
|
|
||||||
<view
|
|
||||||
v-for="article in articleList"
|
|
||||||
:key="article._id"
|
|
||||||
class="article-item"
|
|
||||||
>
|
|
||||||
<view class="article-content" @click="previewArticle(article)">
|
|
||||||
<text class="article-title">{{ article.title }}</text>
|
|
||||||
<text class="article-date">创建时间:{{ article.date }}</text>
|
|
||||||
</view>
|
|
||||||
<view class="article-action">
|
|
||||||
<button
|
|
||||||
class="send-btn"
|
|
||||||
size="mini"
|
|
||||||
type="primary"
|
|
||||||
@click="sendArticle(article)"
|
|
||||||
>
|
|
||||||
发送
|
|
||||||
</button>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view v-if="loading && articleList.length > 0" class="loading-more">
|
|
||||||
<uni-icons type="spinner-cycle" size="20" color="#999" />
|
|
||||||
<text>加载中...</text>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view
|
|
||||||
v-if="!loading && articleList.length >= total"
|
|
||||||
class="no-more"
|
|
||||||
>
|
|
||||||
没有更多了
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</scroll-view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 文章预览弹窗 -->
|
|
||||||
<uni-popup ref="previewPopup" type="bottom" :safe-area="false">
|
|
||||||
<view class="preview-container">
|
|
||||||
<view class="preview-header">
|
|
||||||
<text class="preview-title">{{ previewArticleData.title }}</text>
|
|
||||||
<view class="preview-close" @click="closePreview">
|
|
||||||
<uni-icons type="closeempty" size="24" color="#333" />
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<scroll-view scroll-y class="preview-content">
|
|
||||||
<rich-text :nodes="previewArticleData.content"></rich-text>
|
|
||||||
</scroll-view>
|
|
||||||
<view class="preview-footer">
|
|
||||||
<button class="preview-close-btn" @click="closePreview">关闭</button>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</uni-popup>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref, onMounted } from "vue";
|
|
||||||
import { getArticleCateList, getArticleList, getArticle } from "@/utils/api.js";
|
|
||||||
import useAccountStore from "@/store/account.js";
|
|
||||||
import EmptyData from "@/components/empty-data.vue";
|
|
||||||
|
|
||||||
const env = __VITE_ENV__;
|
|
||||||
const accountStore = useAccountStore();
|
|
||||||
const corpId = env.MP_CORP_ID;
|
|
||||||
const userId = ref("");
|
|
||||||
|
|
||||||
// 搜索关键词
|
|
||||||
const searchTitle = ref("");
|
|
||||||
let searchTimer = null;
|
|
||||||
|
|
||||||
// 分类列表
|
|
||||||
const categoryList = ref([{ _id: "", name: "全部" }]);
|
|
||||||
const currentCateId = ref("");
|
|
||||||
|
|
||||||
// 文章列表
|
|
||||||
const articleList = ref([]);
|
|
||||||
const loading = ref(false);
|
|
||||||
const page = ref(1);
|
|
||||||
const pageSize = 30;
|
|
||||||
const total = ref(0);
|
|
||||||
|
|
||||||
// 预览文章数据
|
|
||||||
const previewArticleData = ref({
|
|
||||||
title: "",
|
|
||||||
content: "",
|
|
||||||
});
|
|
||||||
const previewPopup = ref(null);
|
|
||||||
|
|
||||||
// 获取分类列表
|
|
||||||
const getCategoryList = async () => {
|
|
||||||
try {
|
|
||||||
const res = await getArticleCateList({ corpId: corpId });
|
|
||||||
if (res.success && res.list) {
|
|
||||||
const cates = res.list || [];
|
|
||||||
categoryList.value = [{ _id: "", name: "全部" }, ...cates];
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("获取分类列表失败:", error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 选择分类
|
|
||||||
const selectCategory = (cate) => {
|
|
||||||
currentCateId.value = cate._id || "";
|
|
||||||
page.value = 1;
|
|
||||||
articleList.value = [];
|
|
||||||
loadArticleList();
|
|
||||||
};
|
|
||||||
|
|
||||||
// 搜索处理
|
|
||||||
const handleSearch = () => {
|
|
||||||
if (searchTimer) {
|
|
||||||
clearTimeout(searchTimer);
|
|
||||||
}
|
|
||||||
searchTimer = setTimeout(() => {
|
|
||||||
page.value = 1;
|
|
||||||
articleList.value = [];
|
|
||||||
loadArticleList();
|
|
||||||
}, 500);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 加载文章列表
|
|
||||||
const loadArticleList = async () => {
|
|
||||||
if (loading.value) return;
|
|
||||||
|
|
||||||
loading.value = true;
|
|
||||||
try {
|
|
||||||
const params = {
|
|
||||||
corpId: corpId,
|
|
||||||
page: page.value,
|
|
||||||
pageSize: pageSize,
|
|
||||||
enable: true,
|
|
||||||
title: searchTitle.value,
|
|
||||||
};
|
|
||||||
|
|
||||||
// 如果选择了分类,添加分类ID
|
|
||||||
if (currentCateId.value) {
|
|
||||||
params.cateIds = [currentCateId.value];
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await getArticleList(params);
|
|
||||||
if (res.success && res.list) {
|
|
||||||
const { list = [], total: count = 0 } = res;
|
|
||||||
const formattedList = list.map((item) => {
|
|
||||||
// 格式化日期
|
|
||||||
let date = "";
|
|
||||||
if (item.createTime) {
|
|
||||||
const d = new Date(item.createTime);
|
|
||||||
const year = d.getFullYear();
|
|
||||||
const month = String(d.getMonth() + 1).padStart(2, "0");
|
|
||||||
const day = String(d.getDate()).padStart(2, "0");
|
|
||||||
date = `${year}-${month}-${day}`;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
...item,
|
|
||||||
date,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
if (page.value === 1) {
|
|
||||||
articleList.value = formattedList;
|
|
||||||
} else {
|
|
||||||
articleList.value = [...articleList.value, ...formattedList];
|
|
||||||
}
|
|
||||||
total.value = count;
|
|
||||||
} else {
|
|
||||||
uni.showToast({
|
|
||||||
title: res.message || "获取文章列表失败",
|
|
||||||
icon: "none",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("加载文章列表失败:", error);
|
|
||||||
uni.showToast({
|
|
||||||
title: "加载失败,请重试",
|
|
||||||
icon: "none",
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
loading.value = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 加载更多
|
|
||||||
const loadMore = () => {
|
|
||||||
if (loading.value || articleList.value.length >= total.value) return;
|
|
||||||
page.value += 1;
|
|
||||||
loadArticleList();
|
|
||||||
};
|
|
||||||
|
|
||||||
// 预览文章
|
|
||||||
const previewArticle = async (article) => {
|
|
||||||
try {
|
|
||||||
uni.showLoading({ title: "加载中..." });
|
|
||||||
const res = await getArticle({ id: article._id, corpId: corpId });
|
|
||||||
uni.hideLoading();
|
|
||||||
|
|
||||||
if (res.success && res.data ) {
|
|
||||||
previewArticleData.value = {
|
|
||||||
title: res.data.title || article.title,
|
|
||||||
content: res.data.content || "",
|
|
||||||
};
|
|
||||||
previewPopup.value?.open();
|
|
||||||
} else {
|
|
||||||
uni.showToast({
|
|
||||||
title: res.message || "预览文章失败",
|
|
||||||
icon: "none",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
uni.hideLoading();
|
|
||||||
console.error("预览文章失败:", error);
|
|
||||||
uni.showToast({
|
|
||||||
title: "预览失败,请重试",
|
|
||||||
icon: "none",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 关闭预览
|
|
||||||
const closePreview = () => {
|
|
||||||
previewPopup.value?.close();
|
|
||||||
};
|
|
||||||
|
|
||||||
// 发送文章
|
|
||||||
const sendArticle = (article) => {
|
|
||||||
// 获取当前页面栈
|
|
||||||
const pages = getCurrentPages();
|
|
||||||
const prevPage = pages[pages.length - 2];
|
|
||||||
|
|
||||||
if (prevPage) {
|
|
||||||
// 获取医生信息
|
|
||||||
const doctorInfo = accountStore.doctorInfo;
|
|
||||||
userId.value = doctorInfo?.userid || accountStore.openid;
|
|
||||||
|
|
||||||
// 通过事件总线传递数据
|
|
||||||
uni.$emit("sendArticle", {
|
|
||||||
article: article,
|
|
||||||
corpId: corpId,
|
|
||||||
userId: userId.value,
|
|
||||||
});
|
|
||||||
|
|
||||||
uni.showToast({
|
|
||||||
title: "已选择文章",
|
|
||||||
icon: "success",
|
|
||||||
});
|
|
||||||
|
|
||||||
// 延迟返回,让用户看到提示
|
|
||||||
setTimeout(() => {
|
|
||||||
uni.navigateBack();
|
|
||||||
}, 500);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 返回
|
|
||||||
const goBack = () => {
|
|
||||||
uni.navigateBack();
|
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
getCategoryList();
|
|
||||||
loadArticleList();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.article-page {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
height: 100vh;
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header {
|
|
||||||
background-color: #fff;
|
|
||||||
padding: 20rpx;
|
|
||||||
border-bottom: 1px solid #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-bar {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
border-radius: 8rpx;
|
|
||||||
padding: 16rpx 24rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-input {
|
|
||||||
flex: 1;
|
|
||||||
margin-left: 16rpx;
|
|
||||||
font-size: 28rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.category-sidebar {
|
|
||||||
width: 200rpx;
|
|
||||||
background-color: #f8f8f8;
|
|
||||||
border-right: 1px solid #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
.category-scroll {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.category-item {
|
|
||||||
padding: 32rpx 24rpx;
|
|
||||||
font-size: 28rpx;
|
|
||||||
color: #333;
|
|
||||||
text-align: center;
|
|
||||||
border-bottom: 1px solid #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
.category-item.active {
|
|
||||||
background-color: #fff;
|
|
||||||
color: #1890ff;
|
|
||||||
font-weight: bold;
|
|
||||||
border-left: 4rpx solid #1890ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article-list {
|
|
||||||
flex: 1;
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article-scroll {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading-container,
|
|
||||||
.empty-container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 100rpx 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading-text {
|
|
||||||
margin-top: 20rpx;
|
|
||||||
font-size: 28rpx;
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 24rpx 30rpx;
|
|
||||||
border-bottom: 1px solid #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article-content {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
margin-right: 20rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article-title {
|
|
||||||
font-size: 28rpx;
|
|
||||||
color: #333;
|
|
||||||
line-height: 1.5;
|
|
||||||
word-break: break-all;
|
|
||||||
margin-bottom: 12rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article-date {
|
|
||||||
font-size: 24rpx;
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article-action {
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.send-btn {
|
|
||||||
padding: 8rpx 32rpx;
|
|
||||||
font-size: 26rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading-more,
|
|
||||||
.no-more {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 30rpx 0;
|
|
||||||
font-size: 24rpx;
|
|
||||||
color: #999;
|
|
||||||
gap: 10rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
|
||||||
background-color: #fff;
|
|
||||||
padding: 20rpx;
|
|
||||||
border-top: 1px solid #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cancel-btn {
|
|
||||||
width: 100%;
|
|
||||||
background-color: #fff;
|
|
||||||
color: #333;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 预览弹窗样式 */
|
|
||||||
.preview-container {
|
|
||||||
background-color: #fff;
|
|
||||||
border-radius: 20rpx 20rpx 0 0;
|
|
||||||
height: 80vh;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 30rpx;
|
|
||||||
border-bottom: 1px solid #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-title {
|
|
||||||
flex: 1;
|
|
||||||
font-size: 32rpx;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-close {
|
|
||||||
padding: 10rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-content {
|
|
||||||
flex: 1;
|
|
||||||
padding: 30rpx;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-footer {
|
|
||||||
padding: 20rpx;
|
|
||||||
border-top: 1px solid #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-close-btn {
|
|
||||||
width: 100%;
|
|
||||||
background-color: #1890ff;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1249,111 +1249,3 @@ $primary-color: #0877F1;
|
|||||||
0%, 100% { transform: translateY(0); }
|
0%, 100% { transform: translateY(0); }
|
||||||
50% { transform: translateY(-10rpx); }
|
50% { transform: translateY(-10rpx); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 文章卡片样式 */
|
|
||||||
.article-card {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
background-color: #fff;
|
|
||||||
border-radius: 12rpx;
|
|
||||||
padding: 24rpx;
|
|
||||||
max-width: 500rpx;
|
|
||||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08);
|
|
||||||
}
|
|
||||||
|
|
||||||
.article-content {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
margin-right: 20rpx;
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article-title {
|
|
||||||
font-size: 28rpx;
|
|
||||||
color: #333;
|
|
||||||
font-weight: 500;
|
|
||||||
line-height: 1.4;
|
|
||||||
margin-bottom: 8rpx;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
display: -webkit-box;
|
|
||||||
-webkit-line-clamp: 2;
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article-desc {
|
|
||||||
font-size: 24rpx;
|
|
||||||
color: #999;
|
|
||||||
line-height: 1.3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article-image {
|
|
||||||
width: 120rpx;
|
|
||||||
height: 120rpx;
|
|
||||||
border-radius: 8rpx;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 文章卡片在不同消息流中的样式 */
|
|
||||||
.message-right .article-card {
|
|
||||||
background-color: #e8f4ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message-left .article-card {
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 问卷卡片样式 */
|
|
||||||
.survey-card {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
background-color: #fff;
|
|
||||||
border-radius: 12rpx;
|
|
||||||
padding: 24rpx;
|
|
||||||
max-width: 500rpx;
|
|
||||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08);
|
|
||||||
}
|
|
||||||
|
|
||||||
.survey-content {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
margin-right: 20rpx;
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.survey-title {
|
|
||||||
font-size: 28rpx;
|
|
||||||
color: #333;
|
|
||||||
font-weight: 500;
|
|
||||||
line-height: 1.4;
|
|
||||||
margin-bottom: 8rpx;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
display: -webkit-box;
|
|
||||||
-webkit-line-clamp: 2;
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
}
|
|
||||||
|
|
||||||
.survey-desc {
|
|
||||||
font-size: 24rpx;
|
|
||||||
color: #999;
|
|
||||||
line-height: 1.3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.survey-image {
|
|
||||||
width: 120rpx;
|
|
||||||
height: 120rpx;
|
|
||||||
border-radius: 8rpx;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 问卷卡片在不同消息流中的样式 */
|
|
||||||
.message-right .survey-card {
|
|
||||||
background-color: #e8f4ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.message-left .survey-card {
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
|
||||||
|
|||||||
@ -5,11 +5,11 @@
|
|||||||
<view class="category-sidebar">
|
<view class="category-sidebar">
|
||||||
<scroll-view class="category-list" scroll-y>
|
<scroll-view class="category-list" scroll-y>
|
||||||
<view
|
<view
|
||||||
v-for="category in allCategories"
|
v-for="category in categories"
|
||||||
:key="category.id"
|
:key="category.id"
|
||||||
class="category-item"
|
class="category-item"
|
||||||
:class="{ active: currentCategory === category.id }"
|
:class="{ active: currentCategory === category.id }"
|
||||||
@click="handleCategoryClick(category)"
|
@click="switchCategory(category.id)"
|
||||||
@longpress="handleCategoryLongPress(category)"
|
@longpress="handleCategoryLongPress(category)"
|
||||||
>
|
>
|
||||||
<view class="category-content">
|
<view class="category-content">
|
||||||
@ -23,16 +23,26 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
|
||||||
|
|
||||||
<!-- 新增分类按钮(固定在底部) -->
|
<view class="add-category-item" @click="showAddCategoryDialog">
|
||||||
<view class="add-category-footer" @click="showAddCategoryDialog">
|
<text class="plus-icon">+</text>
|
||||||
<text class="add-category-text">新增分类</text>
|
</view>
|
||||||
</view>
|
</scroll-view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 右侧常用语列表 -->
|
<!-- 右侧常用语列表 -->
|
||||||
<view class="phrases-container">
|
<view class="phrases-container">
|
||||||
|
<!-- 顶部操作栏 -->
|
||||||
|
<view class="action-bar">
|
||||||
|
<view class="add-phrase-btn" @click="showAddPhraseDialog">
|
||||||
|
<text class="add-icon">+</text>
|
||||||
|
<text class="add-text">添加快捷回复</text>
|
||||||
|
</view>
|
||||||
|
<view class="edit-btn" @click="toggleEditMode">
|
||||||
|
<text class="edit-text">{{ isEditMode ? "完成" : "编辑" }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
<scroll-view class="phrases-list" scroll-y>
|
<scroll-view class="phrases-list" scroll-y>
|
||||||
<view
|
<view
|
||||||
v-for="phrase in currentPhrases"
|
v-for="phrase in currentPhrases"
|
||||||
@ -56,20 +66,9 @@
|
|||||||
|
|
||||||
<view v-if="currentPhrases.length === 0" class="empty-state">
|
<view v-if="currentPhrases.length === 0" class="empty-state">
|
||||||
<text class="empty-text">暂无常用语</text>
|
<text class="empty-text">暂无常用语</text>
|
||||||
<text class="empty-hint">点击下方按钮添加常用语</text>
|
<text class="empty-hint">点击上方按钮添加常用语</text>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
|
||||||
<!-- 底部操作栏(页脚) -->
|
|
||||||
<view class="footer-action-bar">
|
|
||||||
<view class="add-phrase-btn" @click="showAddPhraseDialog">
|
|
||||||
<text class="add-icon">+</text>
|
|
||||||
<text class="add-text">添加快捷回复</text>
|
|
||||||
</view>
|
|
||||||
<view class="edit-btn" @click="toggleEditMode">
|
|
||||||
<text class="edit-text">{{ isEditMode ? "完成" : "编辑" }}</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@ -109,9 +108,7 @@
|
|||||||
>
|
>
|
||||||
<view class="popup-content category-popup" @click.stop>
|
<view class="popup-content category-popup" @click.stop>
|
||||||
<view class="popup-header">
|
<view class="popup-header">
|
||||||
<text class="popup-title">{{
|
<text class="popup-title">新建分类</text>
|
||||||
editingCategory ? "编辑分类" : "新建分类"
|
|
||||||
}}</text>
|
|
||||||
<view class="popup-close" @click="closeCategoryPopup">
|
<view class="popup-close" @click="closeCategoryPopup">
|
||||||
<text class="close-icon">×</text>
|
<text class="close-icon">×</text>
|
||||||
</view>
|
</view>
|
||||||
@ -120,12 +117,11 @@
|
|||||||
v-model="categoryForm.name"
|
v-model="categoryForm.name"
|
||||||
class="category-input"
|
class="category-input"
|
||||||
placeholder="请输入分类名,最多6个字"
|
placeholder="请输入分类名,最多6个字"
|
||||||
|
maxlength="6"
|
||||||
/>
|
/>
|
||||||
<view class="popup-actions">
|
<view class="popup-actions">
|
||||||
<button class="cancel-btn" @click="closeCategoryPopup">取消</button>
|
<button class="cancel-btn" @click="closeCategoryPopup">取消</button>
|
||||||
<button class="confirm-btn" @click="saveCategory">
|
<button class="confirm-btn" @click="saveCategory">确认添加</button>
|
||||||
{{ editingCategory ? "保存" : "确认添加" }}
|
|
||||||
</button>
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@ -134,12 +130,7 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, onMounted } from "vue";
|
import { ref, computed, onMounted } from "vue";
|
||||||
import { storeToRefs } from "pinia";
|
|
||||||
import api from "@/utils/api";
|
import api from "@/utils/api";
|
||||||
import useAccountStore from "@/store/account";
|
|
||||||
|
|
||||||
// 获取 store
|
|
||||||
const { doctorInfo } = storeToRefs(useAccountStore());
|
|
||||||
|
|
||||||
// 获取系统状态栏高度
|
// 获取系统状态栏高度
|
||||||
const statusBarHeight = ref(0);
|
const statusBarHeight = ref(0);
|
||||||
@ -147,64 +138,21 @@ const statusBarHeight = ref(0);
|
|||||||
// 编辑模式
|
// 编辑模式
|
||||||
const isEditMode = ref(false);
|
const isEditMode = ref(false);
|
||||||
|
|
||||||
// 默认分类(虚拟分类,不存在于数据库)
|
// 分类数据
|
||||||
const DEFAULT_CATEGORY = {
|
const categories = ref([
|
||||||
id: "__default__",
|
{ id: "visit", name: "文字随访", deletable: false },
|
||||||
name: "默认",
|
{ id: "voice", name: "语音随访", deletable: false },
|
||||||
sort: -1,
|
{ id: "common", name: "常用回复", deletable: false },
|
||||||
deletable: false,
|
]);
|
||||||
type: "personal",
|
|
||||||
};
|
|
||||||
|
|
||||||
// 分类数据(初始为空,从后台加载)
|
const currentCategory = ref("common");
|
||||||
const categories = ref([]);
|
|
||||||
|
|
||||||
// 机构分类数据
|
|
||||||
const corpCategories = ref([]);
|
|
||||||
|
|
||||||
// 所有分类(包含默认分类 + 个人分类 + 机构分类)
|
|
||||||
const allCategories = computed(() => {
|
|
||||||
// 个人分类
|
|
||||||
const personalCats = [DEFAULT_CATEGORY, ...categories.value];
|
|
||||||
|
|
||||||
// 机构分类(标记为只读)
|
|
||||||
const corpCats = corpCategories.value.map(cat => ({
|
|
||||||
...cat,
|
|
||||||
type: "corp",
|
|
||||||
deletable: false,
|
|
||||||
isCorpCategory: true, // 标记为机构分类
|
|
||||||
}));
|
|
||||||
|
|
||||||
return [...personalCats, ...corpCats];
|
|
||||||
});
|
|
||||||
|
|
||||||
const currentCategory = ref(DEFAULT_CATEGORY.id);
|
|
||||||
|
|
||||||
// 常用语数据
|
// 常用语数据
|
||||||
const phrases = ref([]);
|
const phrases = ref([]);
|
||||||
|
|
||||||
// 机构常用语数据
|
|
||||||
const corpPhrases = ref([]);
|
|
||||||
|
|
||||||
// 当前分类的常用语
|
// 当前分类的常用语
|
||||||
const currentPhrases = computed(() => {
|
const currentPhrases = computed(() => {
|
||||||
// 获取当前分类信息
|
return phrases.value.filter((p) => p.categoryId === currentCategory.value);
|
||||||
const currentCat = allCategories.value.find(c => c.id === currentCategory.value);
|
|
||||||
|
|
||||||
if (currentCategory.value === DEFAULT_CATEGORY.id) {
|
|
||||||
// 默认分类显示没有 categoryId 或 categoryId 为空的个人常用语
|
|
||||||
return phrases.value.filter((p) => !p.categoryId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果是机构分类,返回机构常用语
|
|
||||||
if (currentCat?.isCorpCategory) {
|
|
||||||
return corpPhrases.value.filter((p) => p.categoryId === currentCategory.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 个人分类,返回个人常用语
|
|
||||||
const filtered = phrases.value.filter((p) => p.categoryId === currentCategory.value);
|
|
||||||
console.log("当前分类:", currentCategory.value);
|
|
||||||
return filtered;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 弹窗显示状态
|
// 弹窗显示状态
|
||||||
@ -221,7 +169,6 @@ const categoryForm = ref({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const editingPhrase = ref(null);
|
const editingPhrase = ref(null);
|
||||||
const editingCategory = ref(null);
|
|
||||||
|
|
||||||
// 返回上一页
|
// 返回上一页
|
||||||
const goBack = () => {
|
const goBack = () => {
|
||||||
@ -238,24 +185,6 @@ const switchCategory = (categoryId) => {
|
|||||||
currentCategory.value = categoryId;
|
currentCategory.value = categoryId;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 处理分类点击(编辑模式下编辑分类名称)
|
|
||||||
const handleCategoryClick = (category) => {
|
|
||||||
if (isEditMode.value && category.deletable) {
|
|
||||||
// 编辑模式下,点击可编辑的分类,弹出编辑框
|
|
||||||
editCategory(category);
|
|
||||||
} else {
|
|
||||||
// 非编辑模式,切换分类
|
|
||||||
switchCategory(category.id);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 编辑分类
|
|
||||||
const editCategory = (category) => {
|
|
||||||
editingCategory.value = category;
|
|
||||||
categoryForm.value.name = category.name;
|
|
||||||
showCategoryPopup.value = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 处理常用语点击
|
// 处理常用语点击
|
||||||
const handlePhraseClick = (phrase) => {
|
const handlePhraseClick = (phrase) => {
|
||||||
if (isEditMode.value) {
|
if (isEditMode.value) {
|
||||||
@ -291,79 +220,63 @@ const editPhrase = (phrase) => {
|
|||||||
const savePhrase = async () => {
|
const savePhrase = async () => {
|
||||||
if (!phraseForm.value.content.trim()) {
|
if (!phraseForm.value.content.trim()) {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: "请输入内容",
|
title: '请输入内容',
|
||||||
icon: "none",
|
icon: 'none'
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!doctorInfo.value) {
|
const corpId = uni.getStorageSync('corpId');
|
||||||
|
const userId = uni.getStorageSync('userId');
|
||||||
|
|
||||||
|
if (!corpId || !userId) {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: "请先登录",
|
title: '请先登录',
|
||||||
icon: "none",
|
icon: 'none'
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const corpId = doctorInfo.value.corpId;
|
const result = await api('saveCommonPhrase', {
|
||||||
const userId = doctorInfo.value.userid;
|
|
||||||
|
|
||||||
// 如果是默认分类,categoryId 设置为 null
|
|
||||||
let categoryId = editingPhrase.value?.categoryId || currentCategory.value;
|
|
||||||
if (categoryId === DEFAULT_CATEGORY.id) {
|
|
||||||
categoryId = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await api("savePersonalPhrase", {
|
|
||||||
id: editingPhrase.value?.id,
|
id: editingPhrase.value?.id,
|
||||||
categoryId: categoryId,
|
categoryId: editingPhrase.value?.categoryId || currentCategory.value,
|
||||||
content: phraseForm.value.content,
|
content: phraseForm.value.content,
|
||||||
corpId,
|
corpId,
|
||||||
userId,
|
userId
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("保存常用语返回结果:", result);
|
if (result.code === 200) {
|
||||||
|
|
||||||
if (result.success) {
|
|
||||||
if (editingPhrase.value) {
|
if (editingPhrase.value) {
|
||||||
// 更新
|
// 更新
|
||||||
const index = phrases.value.findIndex(
|
const index = phrases.value.findIndex(p => p.id === editingPhrase.value.id);
|
||||||
(p) => p.id === editingPhrase.value.id
|
|
||||||
);
|
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
phrases.value[index].content = phraseForm.value.content;
|
phrases.value[index].content = phraseForm.value.content;
|
||||||
// 如果是默认分类,categoryId 设置为 null
|
|
||||||
const newCategoryId = editingPhrase.value.categoryId || currentCategory.value;
|
|
||||||
phrases.value[index].categoryId = newCategoryId === DEFAULT_CATEGORY.id ? null : newCategoryId;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 新增
|
// 新增
|
||||||
console.log("新增常用语,返回数据:", result.data);
|
|
||||||
console.log("当前分类ID:", currentCategory.value);
|
|
||||||
if (result.data) {
|
if (result.data) {
|
||||||
phrases.value.push(result.data);
|
phrases.value.push(result.data);
|
||||||
console.log("添加后的 phrases:", phrases.value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: editingPhrase.value ? "保存成功" : "添加成功",
|
title: editingPhrase.value ? '保存成功' : '添加成功',
|
||||||
icon: "success",
|
icon: 'success'
|
||||||
});
|
});
|
||||||
|
|
||||||
closePopup();
|
closePopup();
|
||||||
} else {
|
} else {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: result.message || "操作失败",
|
title: result.message || '操作失败',
|
||||||
icon: "none",
|
icon: 'none'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("保存常用语失败:", error);
|
console.error('保存常用语失败:', error);
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: "操作失败",
|
title: '操作失败',
|
||||||
icon: "none",
|
icon: 'none'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -376,31 +289,20 @@ const deletePhrase = (phrase) => {
|
|||||||
success: async (res) => {
|
success: async (res) => {
|
||||||
if (res.confirm) {
|
if (res.confirm) {
|
||||||
try {
|
try {
|
||||||
const corpId = doctorInfo.value.corpId;
|
await api("deleteCommonPhrase", {
|
||||||
const userId = doctorInfo.value.userid;
|
|
||||||
|
|
||||||
const result = await api("deletePersonalPhrase", {
|
|
||||||
id: phrase.id,
|
id: phrase.id,
|
||||||
corpId,
|
corpId: uni.getStorageSync("corpId"),
|
||||||
userId,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result.success) {
|
const index = phrases.value.findIndex((p) => p.id === phrase.id);
|
||||||
const index = phrases.value.findIndex((p) => p.id === phrase.id);
|
if (index !== -1) {
|
||||||
if (index !== -1) {
|
phrases.value.splice(index, 1);
|
||||||
phrases.value.splice(index, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
uni.showToast({
|
|
||||||
title: "删除成功",
|
|
||||||
icon: "success",
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
uni.showToast({
|
|
||||||
title: result.message || "删除失败",
|
|
||||||
icon: "none",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uni.showToast({
|
||||||
|
title: "删除成功",
|
||||||
|
icon: "success",
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("删除常用语失败:", error);
|
console.error("删除常用语失败:", error);
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
@ -415,13 +317,12 @@ const deletePhrase = (phrase) => {
|
|||||||
|
|
||||||
// 显示添加分类弹窗
|
// 显示添加分类弹窗
|
||||||
const showAddCategoryDialog = () => {
|
const showAddCategoryDialog = () => {
|
||||||
editingCategory.value = null;
|
|
||||||
categoryForm.value.name = "";
|
categoryForm.value.name = "";
|
||||||
showCategoryPopup.value = true;
|
showCategoryPopup.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 保存分类
|
// 保存分类
|
||||||
const saveCategory = async () => {
|
const saveCategory = () => {
|
||||||
if (!categoryForm.value.name.trim()) {
|
if (!categoryForm.value.name.trim()) {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: "请输入分类名",
|
title: "请输入分类名",
|
||||||
@ -430,66 +331,18 @@ const saveCategory = async () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (categoryForm.value.name.length > 6) {
|
categories.value.push({
|
||||||
uni.showToast({
|
id: `category_${Date.now()}`,
|
||||||
title: "输入内容超过6个字",
|
name: categoryForm.value.name,
|
||||||
icon: "none",
|
deletable: true, // 用户添加的分类可删除
|
||||||
});
|
});
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
uni.showToast({
|
||||||
const corpId = doctorInfo.value.corpId;
|
title: "添加成功",
|
||||||
const userId = doctorInfo.value.userid;
|
icon: "success",
|
||||||
|
});
|
||||||
|
|
||||||
const result = await api("savePersonalPhraseCategory", {
|
closeCategoryPopup();
|
||||||
id: editingCategory.value?.id, // 如果是编辑,传入 id
|
|
||||||
name: categoryForm.value.name,
|
|
||||||
corpId,
|
|
||||||
userId,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (result.success && result.data) {
|
|
||||||
if (editingCategory.value) {
|
|
||||||
// 更新分类
|
|
||||||
const index = categories.value.findIndex(
|
|
||||||
(c) => c.id === editingCategory.value.id
|
|
||||||
);
|
|
||||||
if (index !== -1) {
|
|
||||||
categories.value[index].name = result.data.name;
|
|
||||||
}
|
|
||||||
uni.showToast({
|
|
||||||
title: "修改成功",
|
|
||||||
icon: "success",
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// 新增分类
|
|
||||||
categories.value.push({
|
|
||||||
id: result.data.id,
|
|
||||||
name: result.data.name,
|
|
||||||
sort: result.data.sort,
|
|
||||||
deletable: result.data.deletable,
|
|
||||||
});
|
|
||||||
uni.showToast({
|
|
||||||
title: "添加成功",
|
|
||||||
icon: "success",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
closeCategoryPopup();
|
|
||||||
} else {
|
|
||||||
uni.showToast({
|
|
||||||
title: result.message || "操作失败",
|
|
||||||
icon: "none",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("保存分类失败:", error);
|
|
||||||
uni.showToast({
|
|
||||||
title: "操作失败",
|
|
||||||
icon: "none",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 长按分类
|
// 长按分类
|
||||||
@ -508,54 +361,33 @@ const handleCategoryLongPress = (category) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 删除分类
|
// 删除分类
|
||||||
const deleteCategory = async (category) => {
|
const deleteCategory = (category) => {
|
||||||
if (!category.deletable) {
|
if (!category.deletable) {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: "该分类不可删除",
|
title: "默认分类不可删除",
|
||||||
icon: "none",
|
icon: "none",
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
// 删除分类下的所有常用语
|
||||||
const corpId = doctorInfo.value.corpId;
|
phrases.value = phrases.value.filter((p) => p.categoryId !== category.id);
|
||||||
const userId = doctorInfo.value.userid;
|
|
||||||
|
|
||||||
const result = await api("deletePersonalPhraseCategory", {
|
// 删除分类
|
||||||
id: category.id,
|
const index = categories.value.findIndex((c) => c.id === category.id);
|
||||||
corpId,
|
if (index !== -1) {
|
||||||
userId,
|
categories.value.splice(index, 1);
|
||||||
});
|
|
||||||
|
|
||||||
if (result.success) {
|
|
||||||
// 删除分类
|
|
||||||
const index = categories.value.findIndex((c) => c.id === category.id);
|
|
||||||
if (index !== -1) {
|
|
||||||
categories.value.splice(index, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果删除的是当前分类,切换到默认分类
|
|
||||||
if (currentCategory.value === category.id) {
|
|
||||||
currentCategory.value = DEFAULT_CATEGORY.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
uni.showToast({
|
|
||||||
title: "删除成功",
|
|
||||||
icon: "success",
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
uni.showToast({
|
|
||||||
title: result.message || "删除失败",
|
|
||||||
icon: "none",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("删除分类失败:", error);
|
|
||||||
uni.showToast({
|
|
||||||
title: "删除失败",
|
|
||||||
icon: "none",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果删除的是当前分类,切换到第一个分类
|
||||||
|
if (currentCategory.value === category.id && categories.value.length > 0) {
|
||||||
|
currentCategory.value = categories.value[0].id;
|
||||||
|
}
|
||||||
|
|
||||||
|
uni.showToast({
|
||||||
|
title: "删除成功",
|
||||||
|
icon: "success",
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 关闭弹窗
|
// 关闭弹窗
|
||||||
@ -570,46 +402,104 @@ const closeCategoryPopup = () => {
|
|||||||
// 加载常用语数据
|
// 加载常用语数据
|
||||||
const loadPhrases = async () => {
|
const loadPhrases = async () => {
|
||||||
try {
|
try {
|
||||||
const corpId = doctorInfo.value.corpId;
|
const corpId = uni.getStorageSync('corpId');
|
||||||
const userId = doctorInfo.value.userid;
|
const userId = uni.getStorageSync('userId');
|
||||||
|
|
||||||
const result = await api("getPersonalPhrases", {
|
if (!corpId) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '请先登录',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await api('getCommonPhrases', {
|
||||||
corpId,
|
corpId,
|
||||||
userId,
|
userId
|
||||||
});
|
});
|
||||||
|
|
||||||
if (result.success && result.data) {
|
if (result.code === 200) {
|
||||||
// 更新常用语列表
|
// 更新常用语列表
|
||||||
if (Array.isArray(result.data.phrases)) {
|
if (Array.isArray(result.data)) {
|
||||||
phrases.value = result.data.phrases;
|
phrases.value = result.data;
|
||||||
}
|
}
|
||||||
// 更新分类列表
|
|
||||||
if (Array.isArray(result.data.categories)) {
|
|
||||||
categories.value = result.data.categories;
|
|
||||||
|
|
||||||
// 如果当前分类不存在,保持默认分类
|
// 更新分类列表(合并默认分类和后台分类)
|
||||||
if (currentCategory.value && currentCategory.value !== DEFAULT_CATEGORY.id) {
|
if (Array.isArray(result.categories)) {
|
||||||
const categoryExists = categories.value.find((c) => c.id === currentCategory.value);
|
const backendCategories = result.categories.map(cat => ({
|
||||||
if (!categoryExists) {
|
id: cat.id,
|
||||||
currentCategory.value = DEFAULT_CATEGORY.id;
|
name: cat.name,
|
||||||
|
deletable: true // 后台分类都可以删除
|
||||||
|
}));
|
||||||
|
|
||||||
|
// 如果后台有分类,使用后台分类,否则使用默认分类
|
||||||
|
if (backendCategories.length > 0) {
|
||||||
|
categories.value = backendCategories;
|
||||||
|
// 设置当前分类为第一个
|
||||||
|
if (!currentCategory.value || !categories.value.find(c => c.id === currentCategory.value)) {
|
||||||
|
currentCategory.value = categories.value[0].id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
uni.showToast({
|
|
||||||
title: result.message || "加载失败",
|
|
||||||
icon: "none",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("加载常用语失败:", error);
|
console.error('加载常用语失败:', error);
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: "加载失败",
|
title: '加载失败',
|
||||||
icon: "none",
|
icon: 'none'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 模拟数据
|
||||||
|
const getMockPhrases = () => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
categoryId: "common",
|
||||||
|
content:
|
||||||
|
"想要买草药自己熬还是配方颗粒直接开水冲着喝?线上购买全国包邮,一二线城市一般1-2天送到,其他城市可能慢一些。",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
categoryId: "common",
|
||||||
|
content: "以前得过什么病吗?做过什么检查吗?有检查结果吗?详细说一下。",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
categoryId: "common",
|
||||||
|
content: "把我之前给您开的处方,拍个照上传给我看一下,拍招清楚一点。",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
categoryId: "common",
|
||||||
|
content: "服药期间要慎炸身体,少吃油炸等辛辣食物,按时作息,精神放松。",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
categoryId: "common",
|
||||||
|
content:
|
||||||
|
"煎药最好用砂锅,避免用铁锅,煎药前用冷水浸泡30分钟,水量超过药材2-5厘米,一般中药煎2次,第1次煮沸后再煎20分钟就可以倒出来了,再次加水煮沸后再煎30分钟后倒出,将2次倒出的药混合在一起,就可以按照医嘱用药了。",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 6,
|
||||||
|
categoryId: "common",
|
||||||
|
content:
|
||||||
|
"如果在线上买药,点击整体治疗方案,选择首选处方,划价取药,填写地址后去付款即可。",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 7,
|
||||||
|
categoryId: "voice",
|
||||||
|
content: "想要买草药自己熬还是配方颗粒直接开水冲着喝?",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 8,
|
||||||
|
categoryId: "visit",
|
||||||
|
content: "您好,我是您的健康管理师,请问有什么可以帮助您的吗?",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 获取系统信息
|
// 获取系统信息
|
||||||
const systemInfo = uni.getSystemInfoSync();
|
const systemInfo = uni.getSystemInfoSync();
|
||||||
@ -710,29 +600,34 @@ $primary-gradient-end: #0877f1;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 底部操作栏(页脚)
|
// 标题栏
|
||||||
.footer-action-bar {
|
.action-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
gap: 16rpx;
|
gap: 16rpx;
|
||||||
padding: 24rpx;
|
margin: 24rpx 24rpx 0;
|
||||||
background-color: #fff;
|
.edit-btn {
|
||||||
border-top: 1px solid #f0f0f0;
|
padding: 16rpx 32rpx;
|
||||||
box-shadow: 0 -2rpx 12rpx rgba(0, 0, 0, 0.04);
|
.edit-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: $primary-color;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.add-phrase-btn {
|
.add-phrase-btn {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 20rpx 24rpx;
|
padding: 16rpx 24rpx;
|
||||||
background: linear-gradient(
|
background: linear-gradient(
|
||||||
135deg,
|
135deg,
|
||||||
$primary-gradient-end 0%,
|
$primary-gradient-end 0%,
|
||||||
$primary-gradient-start 100%
|
$primary-gradient-start 100%
|
||||||
);
|
);
|
||||||
border-radius: 30rpx;
|
border-radius: 40rpx;
|
||||||
box-shadow: 0 4rpx 16rpx rgba(8, 119, 241, 0.2);
|
box-shadow: 0 4rpx 16rpx rgba(8, 119, 241, 0.2);
|
||||||
|
|
||||||
.add-icon {
|
.add-icon {
|
||||||
@ -748,19 +643,6 @@ $primary-gradient-end: #0877f1;
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.edit-btn {
|
|
||||||
padding: 20rpx 32rpx;
|
|
||||||
// background-color: $primary-light;
|
|
||||||
border-radius: 20rpx;
|
|
||||||
// box-shadow: 0 2rpx 8rpx rgba(8, 119, 241, 0.15);
|
|
||||||
|
|
||||||
.edit-text {
|
|
||||||
font-size: 28rpx;
|
|
||||||
color: $primary-color;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.content-wrapper {
|
.content-wrapper {
|
||||||
@ -771,21 +653,17 @@ $primary-gradient-end: #0877f1;
|
|||||||
|
|
||||||
// 左侧分类栏
|
// 左侧分类栏
|
||||||
.category-sidebar {
|
.category-sidebar {
|
||||||
width: 210rpx;
|
width: 180rpx;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border-right: 1px solid #f0f0f0;
|
border-right: 1px solid #f0f0f0;
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.category-list {
|
.category-list {
|
||||||
flex: 1;
|
height: 100%;
|
||||||
height: 0; // 配合 flex: 1 实现固定高度
|
|
||||||
overflow-y: auto; // 支持垂直滚动
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.category-item {
|
.category-item {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding: 20rpx 16rpx;
|
padding: 40rpx 16rpx;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
transition: all 0.3s;
|
transition: all 0.3s;
|
||||||
@ -812,10 +690,10 @@ $primary-gradient-end: #0877f1;
|
|||||||
|
|
||||||
.delete-badge {
|
.delete-badge {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: -20rpx;
|
top: -12rpx;
|
||||||
right: 2rpx;
|
right: 8rpx;
|
||||||
width: 28rpx;
|
width: 32rpx;
|
||||||
height: 28rpx;
|
height: 32rpx;
|
||||||
background-color: #ff3b30;
|
background-color: #ff3b30;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -824,7 +702,7 @@ $primary-gradient-end: #0877f1;
|
|||||||
box-shadow: 0 2rpx 8rpx rgba(255, 59, 48, 0.3);
|
box-shadow: 0 2rpx 8rpx rgba(255, 59, 48, 0.3);
|
||||||
|
|
||||||
.delete-icon {
|
.delete-icon {
|
||||||
font-size: 24rpx;
|
font-size: 28rpx;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
@ -832,25 +710,27 @@ $primary-gradient-end: #0877f1;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增分类按钮(固定在底部)
|
.add-category-item {
|
||||||
.add-category-footer {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 40rpx 16rpx;
|
padding: 40rpx 16rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: $primary-color;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border-top: 1px solid #f0f0f0;
|
border-top: 1px solid #f0f0f0;
|
||||||
box-shadow: 0 -2rpx 8rpx rgba(0, 0, 0, 0.04);
|
|
||||||
margin-bottom: 18rpx;
|
|
||||||
|
|
||||||
.add-category-text {
|
.plus-icon {
|
||||||
font-size: 26rpx;
|
width: 48rpx;
|
||||||
|
height: 48rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 40rpx;
|
||||||
|
font-weight: 300;
|
||||||
color: $primary-color;
|
color: $primary-color;
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
background-color: $primary-light;
|
background-color: $primary-light;
|
||||||
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -865,7 +745,7 @@ $primary-gradient-end: #0877f1;
|
|||||||
|
|
||||||
.phrases-list {
|
.phrases-list {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 10rpx;
|
padding: 24rpx;
|
||||||
height: 0; // 配合 flex: 1 实现固定高度
|
height: 0; // 配合 flex: 1 实现固定高度
|
||||||
overflow-y: auto; // 支持垂直滚动
|
overflow-y: auto; // 支持垂直滚动
|
||||||
|
|
||||||
@ -1014,7 +894,7 @@ $primary-gradient-end: #0877f1;
|
|||||||
|
|
||||||
.category-input {
|
.category-input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0 20rpx; // 只设置左右 padding,去掉上下 padding
|
padding: 20rpx;
|
||||||
font-size: 28rpx;
|
font-size: 28rpx;
|
||||||
color: #333;
|
color: #333;
|
||||||
background-color: #f8f9fa;
|
background-color: #f8f9fa;
|
||||||
@ -1023,7 +903,6 @@ $primary-gradient-end: #0877f1;
|
|||||||
margin-bottom: 32rpx;
|
margin-bottom: 32rpx;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
height: 100rpx;
|
height: 100rpx;
|
||||||
line-height: 100rpx; // 行高等于高度,实现垂直居中
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.popup-actions {
|
.popup-actions {
|
||||||
|
|||||||
@ -361,20 +361,6 @@ const goToCommonPhrases = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 跳转到宣教文章页面
|
|
||||||
const goToArticleList = () => {
|
|
||||||
uni.navigateTo({
|
|
||||||
url: '/pages/message/article-list'
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// 跳转到问卷列表页面
|
|
||||||
const goToSurveyList = () => {
|
|
||||||
uni.navigateTo({
|
|
||||||
url: '/pages/message/survey-list'
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const morePanelButtons = [
|
const morePanelButtons = [
|
||||||
{ text: "照片", icon: "/static/icon/zhaopian.png", action: showImagePicker },
|
{ text: "照片", icon: "/static/icon/zhaopian.png", action: showImagePicker },
|
||||||
{
|
{
|
||||||
@ -390,12 +376,12 @@ const morePanelButtons = [
|
|||||||
{
|
{
|
||||||
text: "宣教",
|
text: "宣教",
|
||||||
icon: "/static/icon/xuanjiaowenzhang.png",
|
icon: "/static/icon/xuanjiaowenzhang.png",
|
||||||
action: goToArticleList,
|
action: showImagePicker,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: "问卷",
|
text: "问卷",
|
||||||
icon: "/static/icon/wenjuan.png",
|
icon: "/static/icon/zhaopian.png",
|
||||||
action: goToSurveyList,
|
action: showImagePicker,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: "结束问诊",
|
text: "结束问诊",
|
||||||
|
|||||||
@ -1,88 +0,0 @@
|
|||||||
<template>
|
|
||||||
<view class="consult-accept-container">
|
|
||||||
<view class="accept-card">
|
|
||||||
<view class="accept-content">
|
|
||||||
<text class="accept-text">患者已发起咨询申请,请及时接诊</text>
|
|
||||||
</view>
|
|
||||||
<view class="accept-actions">
|
|
||||||
<button class="btn-cancel" @click="handleReject">暂不接受</button>
|
|
||||||
<button class="btn-confirm" @click="handleAccept">接受咨询</button>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { defineEmits } from 'vue';
|
|
||||||
|
|
||||||
const emit = defineEmits(['accept', 'reject']);
|
|
||||||
|
|
||||||
const handleAccept = () => {
|
|
||||||
emit('accept');
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleReject = () => {
|
|
||||||
emit('reject');
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.consult-accept-container {
|
|
||||||
width: 100%;
|
|
||||||
padding: 20rpx 32rpx;
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.accept-card {
|
|
||||||
background-color: #fff;
|
|
||||||
border-radius: 16rpx;
|
|
||||||
padding: 32rpx;
|
|
||||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
|
|
||||||
}
|
|
||||||
|
|
||||||
.accept-content {
|
|
||||||
margin-bottom: 32rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.accept-text {
|
|
||||||
font-size: 28rpx;
|
|
||||||
color: #333;
|
|
||||||
line-height: 1.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.accept-actions {
|
|
||||||
display: flex;
|
|
||||||
gap: 24rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-cancel,
|
|
||||||
.btn-confirm {
|
|
||||||
flex: 1;
|
|
||||||
height: 80rpx;
|
|
||||||
border-radius: 8rpx;
|
|
||||||
font-size: 28rpx;
|
|
||||||
border: none;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-cancel {
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-cancel::after {
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-confirm {
|
|
||||||
background-color: #1677ff;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-confirm::after {
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -63,46 +63,8 @@
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 自定义消息卡片 -->
|
<!-- 自定义消息卡片 -->
|
||||||
<template v-else-if="message.type === 'TIMCustomElem'">
|
<!-- <template v-else-if="message.type === 'TIMCustomElem'">
|
||||||
<!-- 文章消息 -->
|
|
||||||
<view
|
<view
|
||||||
v-if="getCustomMessageType(message) === '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>
|
|
||||||
<image
|
|
||||||
v-if="getArticleData(message).imgUrl"
|
|
||||||
class="article-image"
|
|
||||||
:src="getArticleData(message).imgUrl"
|
|
||||||
mode="aspectFill"
|
|
||||||
/>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 问卷消息 -->
|
|
||||||
<view
|
|
||||||
v-else-if="getCustomMessageType(message) === '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>
|
|
||||||
<image
|
|
||||||
v-if="getSurveyData(message).imgUrl"
|
|
||||||
class="survey-image"
|
|
||||||
:src="getSurveyData(message).imgUrl"
|
|
||||||
mode="aspectFill"
|
|
||||||
/>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 其他自定义消息 -->
|
|
||||||
<!-- <view
|
|
||||||
v-else
|
|
||||||
class="card-avatar-row"
|
class="card-avatar-row"
|
||||||
@click="() => console.log('点击头像', message)"
|
@click="() => console.log('点击头像', message)"
|
||||||
>
|
>
|
||||||
@ -112,8 +74,8 @@
|
|||||||
:flow="message.flow"
|
:flow="message.flow"
|
||||||
@viewDetail="$emit('viewDetail', $event)"
|
@viewDetail="$emit('viewDetail', $event)"
|
||||||
/>
|
/>
|
||||||
</view> -->
|
</view>
|
||||||
</template>
|
</template> -->
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
@ -201,90 +163,6 @@ const getVoiceStyle = (duration) => {
|
|||||||
width: width + "rpx",
|
width: width + "rpx",
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取自定义消息类型
|
|
||||||
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 {
|
|
||||||
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 handleArticleClick = (message) => {
|
|
||||||
const articleData = getArticleData(message);
|
|
||||||
if (articleData.url) {
|
|
||||||
// 跳转到文章详情页或打开外部链接
|
|
||||||
// 这里可以根据实际需求处理
|
|
||||||
console.log('打开文章:', articleData.url);
|
|
||||||
// uni.navigateTo({
|
|
||||||
// url: `/pages/article/detail?url=${encodeURIComponent(articleData.url)}`
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 获取问卷数据
|
|
||||||
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) {
|
|
||||||
// 跳转到问卷填写页面或打开外部链接
|
|
||||||
console.log('打开问卷:', surveyData.url);
|
|
||||||
// uni.navigateTo({
|
|
||||||
// url: `/pages/survey/fill?url=${encodeURIComponent(surveyData.url)}`
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|||||||
@ -1,297 +0,0 @@
|
|||||||
<template>
|
|
||||||
<view v-if="visible" class="modal-overlay" @click="handleCancel">
|
|
||||||
<view class="modal-container" @click.stop>
|
|
||||||
<view class="modal-header">
|
|
||||||
<text class="modal-title">选择暂不接受咨询原因,供患者知晓</text>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="modal-content">
|
|
||||||
<!-- 预设原因选项 -->
|
|
||||||
<view class="reason-options">
|
|
||||||
<view
|
|
||||||
v-for="(option, index) in reasonOptions"
|
|
||||||
:key="index"
|
|
||||||
class="reason-option"
|
|
||||||
:class="{ active: selectedReason === option }"
|
|
||||||
@click="selectReason(option)"
|
|
||||||
>
|
|
||||||
<text class="option-text">{{ option }}</text>
|
|
||||||
<view class="option-icon" :class="{ checked: selectedReason === option }">
|
|
||||||
<text v-if="selectedReason === option" class="check-mark">✓</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 自定义输入选项 -->
|
|
||||||
<view
|
|
||||||
class="reason-option custom-option"
|
|
||||||
:class="{ active: isCustomInput }"
|
|
||||||
@click="selectCustomInput"
|
|
||||||
>
|
|
||||||
<text class="option-text">填写拒诊理由</text>
|
|
||||||
<view class="option-icon arrow">
|
|
||||||
<text class="arrow-icon">›</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 自定义输入框 -->
|
|
||||||
<view v-if="isCustomInput" class="custom-input-container">
|
|
||||||
<textarea
|
|
||||||
class="custom-textarea"
|
|
||||||
v-model="customReason"
|
|
||||||
placeholder="请输入理由,供患者知晓"
|
|
||||||
maxlength="200"
|
|
||||||
:auto-height="true"
|
|
||||||
/>
|
|
||||||
<text class="char-count">{{ customReason.length }}/200</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="modal-footer">
|
|
||||||
<button class="btn-cancel" @click="handleCancel">取消</button>
|
|
||||||
<button class="btn-confirm" @click="handleConfirm">确定</button>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref, computed } from 'vue';
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
visible: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const emit = defineEmits(['confirm', 'cancel']);
|
|
||||||
|
|
||||||
// 预设的拒绝原因选项
|
|
||||||
const reasonOptions = ref([
|
|
||||||
'临时有紧急事务',
|
|
||||||
'患者病情复杂,需线下就诊',
|
|
||||||
]);
|
|
||||||
|
|
||||||
const selectedReason = ref('');
|
|
||||||
const isCustomInput = ref(false);
|
|
||||||
const customReason = ref('');
|
|
||||||
|
|
||||||
// 选择预设原因
|
|
||||||
const selectReason = (option) => {
|
|
||||||
selectedReason.value = option;
|
|
||||||
isCustomInput.value = false;
|
|
||||||
customReason.value = '';
|
|
||||||
};
|
|
||||||
|
|
||||||
// 选择自定义输入
|
|
||||||
const selectCustomInput = () => {
|
|
||||||
selectedReason.value = '';
|
|
||||||
isCustomInput.value = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 取消
|
|
||||||
const handleCancel = () => {
|
|
||||||
// 重置状态
|
|
||||||
selectedReason.value = '';
|
|
||||||
isCustomInput.value = false;
|
|
||||||
customReason.value = '';
|
|
||||||
emit('cancel');
|
|
||||||
};
|
|
||||||
|
|
||||||
// 确定
|
|
||||||
const handleConfirm = () => {
|
|
||||||
let reason = '';
|
|
||||||
|
|
||||||
if (isCustomInput.value) {
|
|
||||||
reason = customReason.value.trim();
|
|
||||||
if (!reason) {
|
|
||||||
uni.showToast({
|
|
||||||
title: '请输入拒绝原因',
|
|
||||||
icon: 'none',
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
reason = selectedReason.value;
|
|
||||||
if (!reason) {
|
|
||||||
uni.showToast({
|
|
||||||
title: '请选择拒绝原因',
|
|
||||||
icon: 'none',
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
emit('confirm', reason);
|
|
||||||
|
|
||||||
// 重置状态
|
|
||||||
selectedReason.value = '';
|
|
||||||
isCustomInput.value = false;
|
|
||||||
customReason.value = '';
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.modal-overlay {
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
background-color: rgba(0, 0, 0, 0.5);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
z-index: 9999;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-container {
|
|
||||||
width: 600rpx;
|
|
||||||
background-color: #fff;
|
|
||||||
border-radius: 16rpx;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-header {
|
|
||||||
padding: 32rpx 32rpx 24rpx;
|
|
||||||
border-bottom: 1rpx solid #f0f0f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-title {
|
|
||||||
font-size: 32rpx;
|
|
||||||
font-weight: 500;
|
|
||||||
color: #333;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-content {
|
|
||||||
padding: 24rpx 32rpx;
|
|
||||||
max-height: 600rpx;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.reason-options {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 16rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.reason-option {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 24rpx;
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
border-radius: 8rpx;
|
|
||||||
border: 2rpx solid transparent;
|
|
||||||
transition: all 0.3s;
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
background-color: #e6f4ff;
|
|
||||||
border-color: #1677ff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.option-text {
|
|
||||||
font-size: 28rpx;
|
|
||||||
color: #333;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.option-icon {
|
|
||||||
width: 40rpx;
|
|
||||||
height: 40rpx;
|
|
||||||
border-radius: 50%;
|
|
||||||
border: 2rpx solid #d9d9d9;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
transition: all 0.3s;
|
|
||||||
|
|
||||||
&.checked {
|
|
||||||
background-color: #1677ff;
|
|
||||||
border-color: #1677ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.arrow {
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.check-mark {
|
|
||||||
font-size: 24rpx;
|
|
||||||
color: #fff;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.arrow-icon {
|
|
||||||
font-size: 40rpx;
|
|
||||||
color: #999;
|
|
||||||
font-weight: 300;
|
|
||||||
}
|
|
||||||
|
|
||||||
.custom-input-container {
|
|
||||||
margin-top: 16rpx;
|
|
||||||
padding: 16rpx;
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
border-radius: 8rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.custom-textarea {
|
|
||||||
width: 100%;
|
|
||||||
min-height: 120rpx;
|
|
||||||
font-size: 28rpx;
|
|
||||||
color: #333;
|
|
||||||
line-height: 1.6;
|
|
||||||
background-color: transparent;
|
|
||||||
border: none;
|
|
||||||
outline: none;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.char-count {
|
|
||||||
display: block;
|
|
||||||
text-align: right;
|
|
||||||
font-size: 24rpx;
|
|
||||||
color: #999;
|
|
||||||
margin-top: 8rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-footer {
|
|
||||||
display: flex;
|
|
||||||
gap: 24rpx;
|
|
||||||
padding: 24rpx 32rpx 32rpx;
|
|
||||||
border-top: 1rpx solid #f0f0f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-cancel,
|
|
||||||
.btn-confirm {
|
|
||||||
flex: 1;
|
|
||||||
height: 80rpx;
|
|
||||||
border-radius: 8rpx;
|
|
||||||
font-size: 28rpx;
|
|
||||||
border: none;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-cancel {
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-cancel::after {
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-confirm {
|
|
||||||
background-color: #1677ff;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-confirm::after {
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -1,9 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<!-- <view v-if="notifyText" class="notify-bar">
|
<view v-if="notifyText" class="notify-bar">
|
||||||
<view class="notify-line"></view>
|
<view class="notify-line"></view>
|
||||||
<view class="notify-text">{{ notifyText }}</view>
|
<view class="notify-text">{{ notifyText }}</view>
|
||||||
<view class="notify-line"></view>
|
<view class="notify-line"></view>
|
||||||
</view> -->
|
</view>
|
||||||
<view class="system-message">
|
<view class="system-message">
|
||||||
<view class="system-text">{{ text }}</view>
|
<view class="system-text">{{ text }}</view>
|
||||||
</view>
|
</view>
|
||||||
@ -21,111 +21,17 @@ const props = defineProps({
|
|||||||
|
|
||||||
const payload = computed(() => props.message?.payload || {});
|
const payload = computed(() => props.message?.payload || {});
|
||||||
|
|
||||||
// 解析系统消息内容
|
|
||||||
const systemMessageData = computed(() => {
|
|
||||||
try {
|
|
||||||
// 尝试从 payload.data 解析系统消息
|
|
||||||
if (payload.value.data) {
|
|
||||||
const data = typeof payload.value.data === 'string'
|
|
||||||
? JSON.parse(payload.value.data)
|
|
||||||
: payload.value.data;
|
|
||||||
|
|
||||||
if (data.type === 'system_message') {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error('解析系统消息失败:', e);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
// 解析扩展信息
|
|
||||||
const extension = computed(() => {
|
const extension = computed(() => {
|
||||||
try {
|
try {
|
||||||
if (payload.value.extension) {
|
return JSON.parse(payload.value.extension);
|
||||||
return typeof payload.value.extension === 'string'
|
|
||||||
? JSON.parse(payload.value.extension)
|
|
||||||
: payload.value.extension;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('解析扩展信息失败:', e);
|
return {};
|
||||||
}
|
}
|
||||||
return {};
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 显示的文本内容
|
const text = computed(() => extension.value.patient || payload.value.data || '')
|
||||||
const text = computed(() => {
|
|
||||||
// 优先从系统消息数据中获取文本
|
|
||||||
if (systemMessageData.value?.text) {
|
|
||||||
return systemMessageData.value.text;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 根据消息类型返回默认文本
|
const notifyText = computed(() => extension.value.notifyText || '')
|
||||||
if (systemMessageData.value?.messageType) {
|
|
||||||
const messageType = systemMessageData.value.messageType;
|
|
||||||
switch (messageType) {
|
|
||||||
case 'consult_pending':
|
|
||||||
return '患者已发起咨询申请,请及时接诊';
|
|
||||||
case 'consult_accepted':
|
|
||||||
return '医生已接诊';
|
|
||||||
case 'consult_rejected':
|
|
||||||
return '医生暂时无法接诊';
|
|
||||||
case 'consult_ended':
|
|
||||||
return '问诊已结束';
|
|
||||||
case 'consult_timeout':
|
|
||||||
return '问诊已超时';
|
|
||||||
default:
|
|
||||||
return systemMessageData.value.content || '[系统消息]';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 兼容旧格式:从 extension 中获取
|
|
||||||
if (extension.value.patient) {
|
|
||||||
return extension.value.patient;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 兼容旧格式:直接从 payload.data 获取
|
|
||||||
if (payload.value.data && typeof payload.value.data === 'string') {
|
|
||||||
// 如果 data 不是 JSON 格式,直接显示
|
|
||||||
try {
|
|
||||||
JSON.parse(payload.value.data);
|
|
||||||
} catch {
|
|
||||||
return payload.value.data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return '[系统消息]';
|
|
||||||
});
|
|
||||||
|
|
||||||
// 通知文本(红色提示)
|
|
||||||
const notifyText = computed(() => {
|
|
||||||
// 从扩展信息中获取通知文本
|
|
||||||
if (extension.value.notifyText) {
|
|
||||||
return extension.value.notifyText;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 根据系统消息类型显示不同的通知
|
|
||||||
if (systemMessageData.value) {
|
|
||||||
const messageType = systemMessageData.value.messageType;
|
|
||||||
switch (messageType) {
|
|
||||||
case 'consult_pending':
|
|
||||||
return '待接诊';
|
|
||||||
case 'consult_rejected':
|
|
||||||
return '已拒绝';
|
|
||||||
case 'consult_timeout':
|
|
||||||
return '已超时';
|
|
||||||
case 'consult_accepted':
|
|
||||||
return '已接诊';
|
|
||||||
case 'consult_ended':
|
|
||||||
return '已结束';
|
|
||||||
default:
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return '';
|
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -54,9 +54,7 @@
|
|||||||
<image
|
<image
|
||||||
v-if="message.flow === 'in'"
|
v-if="message.flow === 'in'"
|
||||||
class="doctor-msg-avatar"
|
class="doctor-msg-avatar"
|
||||||
:src="
|
:src="chatMember[message.from]?.avatar || '/static/default-avatar.png'"
|
||||||
chatMember[message.from]?.avatar || '/static/default-avatar.png'
|
|
||||||
"
|
|
||||||
mode="aspectFill"
|
mode="aspectFill"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@ -64,9 +62,7 @@
|
|||||||
<image
|
<image
|
||||||
v-if="message.flow === 'out'"
|
v-if="message.flow === 'out'"
|
||||||
class="user-msg-avatar"
|
class="user-msg-avatar"
|
||||||
:src="
|
:src="chatMember[message.from]?.avatar || '/static/home/avatar.svg'"
|
||||||
chatMember[message.from]?.avatar || '/static/home/avatar.svg'
|
|
||||||
"
|
|
||||||
mode="aspectFill"
|
mode="aspectFill"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@ -112,23 +108,9 @@
|
|||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
|
||||||
<!-- 接受问诊组件 -->
|
|
||||||
<ConsultAccept
|
|
||||||
v-if="showConsultAccept"
|
|
||||||
@accept="handleAcceptConsult"
|
|
||||||
@reject="handleRejectConsult"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- 拒绝原因对话框 -->
|
|
||||||
<RejectReasonModal
|
|
||||||
:visible="showRejectReasonModal"
|
|
||||||
@confirm="handleRejectReasonConfirm"
|
|
||||||
@cancel="handleRejectReasonCancel"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- 聊天输入组件 -->
|
<!-- 聊天输入组件 -->
|
||||||
<ChatInput
|
<ChatInput
|
||||||
v-if="!isEvaluationPopupOpen && !showConsultAccept"
|
v-if="!isEvaluationPopupOpen"
|
||||||
ref="chatInputRef"
|
ref="chatInputRef"
|
||||||
:timChatManager="timChatManager"
|
:timChatManager="timChatManager"
|
||||||
:formatTime="formatTime"
|
:formatTime="formatTime"
|
||||||
@ -161,13 +143,10 @@ import {
|
|||||||
handleViewDetail,
|
handleViewDetail,
|
||||||
checkIMConnectionStatus,
|
checkIMConnectionStatus,
|
||||||
} from "@/utils/chat-utils.js";
|
} from "@/utils/chat-utils.js";
|
||||||
import { sendConsultRejectedMessage } from "@/utils/api.js";
|
|
||||||
import useGroupChat from "./hooks/use-group-chat";
|
import useGroupChat from "./hooks/use-group-chat";
|
||||||
import MessageTypes from "./components/message-types.vue";
|
import MessageTypes from "./components/message-types.vue";
|
||||||
import ChatInput from "./components/chat-input.vue";
|
import ChatInput from "./components/chat-input.vue";
|
||||||
import SystemMessage from "./components/system-message.vue";
|
import SystemMessage from "./components/system-message.vue";
|
||||||
import ConsultAccept from "./components/consult-accept.vue";
|
|
||||||
import RejectReasonModal from "./components/reject-reason-modal.vue";
|
|
||||||
|
|
||||||
const timChatManager = globalTimChatManager;
|
const timChatManager = globalTimChatManager;
|
||||||
|
|
||||||
@ -179,12 +158,15 @@ 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
|
||||||
|
} = useGroupChat(groupId);
|
||||||
|
|
||||||
// 动态设置导航栏标题
|
// 动态设置导航栏标题
|
||||||
const updateNavigationTitle = () => {
|
const updateNavigationTitle = () => {
|
||||||
uni.setNavigationBarTitle({
|
uni.setNavigationBarTitle({
|
||||||
title: "群聊",
|
title: "群聊"
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -198,12 +180,6 @@ const chatInfo = ref({
|
|||||||
// 评价弹窗状态
|
// 评价弹窗状态
|
||||||
const isEvaluationPopupOpen = ref(false);
|
const isEvaluationPopupOpen = ref(false);
|
||||||
|
|
||||||
// 接受问诊状态
|
|
||||||
const showConsultAccept = ref(false);
|
|
||||||
|
|
||||||
// 拒绝原因对话框状态
|
|
||||||
const showRejectReasonModal = ref(false);
|
|
||||||
|
|
||||||
// 消息列表相关状态
|
// 消息列表相关状态
|
||||||
const messageList = ref([]);
|
const messageList = ref([]);
|
||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
@ -216,73 +192,10 @@ const lastFirstMessageId = ref("");
|
|||||||
|
|
||||||
// 判断是否为系统消息
|
// 判断是否为系统消息
|
||||||
function isSystemMessage(message) {
|
function isSystemMessage(message) {
|
||||||
if (message.type !== "TIMCustomElem") {
|
const description = message.payload?.description;
|
||||||
return false;
|
return (
|
||||||
}
|
message.type === "TIMCustomElem" && description === "SYSTEM_NOTIFICATION"
|
||||||
|
);
|
||||||
try {
|
|
||||||
// 检查 payload.data 是否包含系统消息标记
|
|
||||||
if (message.payload?.data) {
|
|
||||||
const data = typeof message.payload.data === 'string'
|
|
||||||
? JSON.parse(message.payload.data)
|
|
||||||
: message.payload.data;
|
|
||||||
|
|
||||||
// 检查是否为系统消息类型
|
|
||||||
if (data.type === 'system_message') {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查 description 是否为系统消息标记
|
|
||||||
if (message.payload?.description === '系统消息标记') {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 兼容旧的系统消息格式
|
|
||||||
if (message.payload?.description === 'SYSTEM_NOTIFICATION') {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('判断系统消息失败:', error);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查是否有待接诊的系统消息
|
|
||||||
function checkConsultPendingStatus() {
|
|
||||||
// 查找最新的系统消息
|
|
||||||
for (let i = messageList.value.length - 1; i >= 0; i--) {
|
|
||||||
const message = messageList.value[i];
|
|
||||||
|
|
||||||
if (message.type === "TIMCustomElem" && message.payload?.data) {
|
|
||||||
try {
|
|
||||||
const data = typeof message.payload.data === 'string'
|
|
||||||
? JSON.parse(message.payload.data)
|
|
||||||
: message.payload.data;
|
|
||||||
|
|
||||||
// 如果是 consult_pending 类型的系统消息,显示接受组件
|
|
||||||
if (data.type === 'system_message' && data.messageType === 'consult_pending') {
|
|
||||||
showConsultAccept.value = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果是其他系统消息类型(如已接诊、已结束等),隐藏接受组件
|
|
||||||
if (data.type === 'system_message' &&
|
|
||||||
(data.messageType === 'consult_accepted' ||
|
|
||||||
data.messageType === 'consult_ended' ||
|
|
||||||
data.messageType === 'consult_rejected')) {
|
|
||||||
showConsultAccept.value = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('解析系统消息失败:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果没有找到相关系统消息,隐藏接受组件
|
|
||||||
showConsultAccept.value = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取消息气泡样式类
|
// 获取消息气泡样式类
|
||||||
@ -291,6 +204,7 @@ function getBubbleClass(message) {
|
|||||||
if (message.type === "TIMImageElem") {
|
if (message.type === "TIMImageElem") {
|
||||||
return "image-bubble";
|
return "image-bubble";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.type === "TIMCustomElem") {
|
if (message.type === "TIMCustomElem") {
|
||||||
return message.flow === "out" ? "user-bubble" : "doctor-bubble-blue";
|
return message.flow === "out" ? "user-bubble" : "doctor-bubble-blue";
|
||||||
}
|
}
|
||||||
@ -374,10 +288,6 @@ const initTIMCallbacks = async () => {
|
|||||||
if (!existingMessage) {
|
if (!existingMessage) {
|
||||||
messageList.value.push(message);
|
messageList.value.push(message);
|
||||||
console.log("✓ 添加消息到列表,当前消息数量:", messageList.value.length);
|
console.log("✓ 添加消息到列表,当前消息数量:", messageList.value.length);
|
||||||
|
|
||||||
// 检查是否有待接诊的系统消息
|
|
||||||
checkConsultPendingStatus();
|
|
||||||
|
|
||||||
// 立即滚动到底部,不使用延迟
|
// 立即滚动到底部,不使用延迟
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
scrollToBottom(true);
|
scrollToBottom(true);
|
||||||
@ -436,9 +346,6 @@ const initTIMCallbacks = async () => {
|
|||||||
isCompleted.value = data.isCompleted || false;
|
isCompleted.value = data.isCompleted || false;
|
||||||
isLoadingMore.value = false;
|
isLoadingMore.value = false;
|
||||||
|
|
||||||
// 检查是否有待接诊的系统消息
|
|
||||||
checkConsultPendingStatus();
|
|
||||||
|
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (data.isRefresh) {
|
if (data.isRefresh) {
|
||||||
console.log("后台刷新完成,保持当前滚动位置");
|
console.log("后台刷新完成,保持当前滚动位置");
|
||||||
@ -581,13 +488,13 @@ const scrollToBottom = (immediate = false) => {
|
|||||||
|
|
||||||
if (immediate) {
|
if (immediate) {
|
||||||
// 立即滚动:先清空再设置,触发滚动
|
// 立即滚动:先清空再设置,触发滚动
|
||||||
scrollIntoView.value = "";
|
scrollIntoView.value = '';
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
scrollIntoView.value = targetId;
|
scrollIntoView.value = targetId;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// 正常滚动,使用短延迟确保DOM更新
|
// 正常滚动,使用短延迟确保DOM更新
|
||||||
scrollIntoView.value = "";
|
scrollIntoView.value = '';
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
scrollIntoView.value = targetId;
|
scrollIntoView.value = targetId;
|
||||||
}, 50);
|
}, 50);
|
||||||
@ -696,110 +603,9 @@ const sendCommonPhrase = (content) => {
|
|||||||
|
|
||||||
// 暴露方法给常用语页面调用
|
// 暴露方法给常用语页面调用
|
||||||
defineExpose({
|
defineExpose({
|
||||||
sendCommonPhrase,
|
sendCommonPhrase
|
||||||
});
|
});
|
||||||
|
|
||||||
// 处理接受问诊
|
|
||||||
const handleAcceptConsult = async () => {
|
|
||||||
try {
|
|
||||||
uni.showLoading({
|
|
||||||
title: '处理中...',
|
|
||||||
});
|
|
||||||
|
|
||||||
// 发送接受问诊的系统消息
|
|
||||||
const customMessage = {
|
|
||||||
data: JSON.stringify({
|
|
||||||
type: 'system_message',
|
|
||||||
messageType: 'consult_accepted',
|
|
||||||
content: '医生已接诊',
|
|
||||||
timestamp: Date.now(),
|
|
||||||
}),
|
|
||||||
description: '系统消息标记',
|
|
||||||
extension: '',
|
|
||||||
};
|
|
||||||
|
|
||||||
const message = timChatManager.tim.createCustomMessage({
|
|
||||||
to: chatInfo.value.conversationID.replace('GROUP', ''),
|
|
||||||
conversationType: timChatManager.TIM.TYPES.CONV_GROUP,
|
|
||||||
payload: customMessage,
|
|
||||||
});
|
|
||||||
|
|
||||||
const sendResult = await timChatManager.tim.sendMessage(message);
|
|
||||||
|
|
||||||
if (sendResult.code === 0) {
|
|
||||||
showConsultAccept.value = false;
|
|
||||||
uni.hideLoading();
|
|
||||||
uni.showToast({
|
|
||||||
title: '已接受问诊',
|
|
||||||
icon: 'success',
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
throw new Error(sendResult.message || '发送失败');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('接受问诊失败:', error);
|
|
||||||
uni.hideLoading();
|
|
||||||
uni.showToast({
|
|
||||||
title: error.message || '操作失败',
|
|
||||||
icon: 'none',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 处理拒绝问诊
|
|
||||||
const handleRejectConsult = () => {
|
|
||||||
// 显示拒绝原因选择对话框
|
|
||||||
showRejectReasonModal.value = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 处理拒绝原因确认
|
|
||||||
const handleRejectReasonConfirm = async (reason) => {
|
|
||||||
try {
|
|
||||||
showRejectReasonModal.value = false;
|
|
||||||
|
|
||||||
uni.showLoading({
|
|
||||||
title: '处理中...',
|
|
||||||
});
|
|
||||||
|
|
||||||
// 获取医生信息
|
|
||||||
const memberName = account.value?.name || '医生';
|
|
||||||
|
|
||||||
// 获取群组ID
|
|
||||||
const groupId = chatInfo.value.conversationID.replace('GROUP', '');
|
|
||||||
|
|
||||||
// 调用后端接口发送拒绝消息
|
|
||||||
const result = await sendConsultRejectedMessage({
|
|
||||||
groupId,
|
|
||||||
memberName,
|
|
||||||
reason,
|
|
||||||
});
|
|
||||||
|
|
||||||
uni.hideLoading();
|
|
||||||
|
|
||||||
if (result.success) {
|
|
||||||
showConsultAccept.value = false;
|
|
||||||
uni.showToast({
|
|
||||||
title: '已拒绝问诊',
|
|
||||||
icon: 'success',
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
throw new Error(result.message || '发送失败');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('拒绝问诊失败:', error);
|
|
||||||
uni.hideLoading();
|
|
||||||
uni.showToast({
|
|
||||||
title: error.message || '操作失败',
|
|
||||||
icon: 'none',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 处理拒绝原因取消
|
|
||||||
const handleRejectReasonCancel = () => {
|
|
||||||
showRejectReasonModal.value = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 页面卸载
|
// 页面卸载
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
clearMessageCache();
|
clearMessageCache();
|
||||||
@ -809,172 +615,6 @@ onUnmounted(() => {
|
|||||||
timChatManager.setCallback("onMessageReceived", null);
|
timChatManager.setCallback("onMessageReceived", null);
|
||||||
timChatManager.setCallback("onMessageListLoaded", null);
|
timChatManager.setCallback("onMessageListLoaded", null);
|
||||||
timChatManager.setCallback("onError", null);
|
timChatManager.setCallback("onError", null);
|
||||||
|
|
||||||
// 移除文章发送监听
|
|
||||||
uni.$off("sendArticle");
|
|
||||||
// 移除问卷发送监听
|
|
||||||
uni.$off("sendSurvey");
|
|
||||||
});
|
|
||||||
|
|
||||||
// 监听文章发送事件
|
|
||||||
uni.$on("sendArticle", async (data) => {
|
|
||||||
const { article, corpId, userId } = data;
|
|
||||||
|
|
||||||
if (!article || !article._id) {
|
|
||||||
uni.showToast({
|
|
||||||
title: "文章信息不完整",
|
|
||||||
icon: "none",
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 获取环境变量
|
|
||||||
const env = __VITE_ENV__;
|
|
||||||
const baseUrl = env.VITE_PATIENT_PAGE_BASE_URL || "";
|
|
||||||
|
|
||||||
// 构建文章链接
|
|
||||||
const articleUrl = `${baseUrl}pages/article/index?id=${article._id}&corpId=${corpId}`;
|
|
||||||
|
|
||||||
// 创建自定义消息
|
|
||||||
const customMessage = {
|
|
||||||
data: JSON.stringify({
|
|
||||||
type: "article",
|
|
||||||
title: article.title || "宣教文章",
|
|
||||||
desc: "宣教文章",
|
|
||||||
url: articleUrl,
|
|
||||||
imgUrl:
|
|
||||||
"https://796f-youcan-clouddev-1-8ewcqf31dbb2b5-1317294507.tcb.qcloud.la/other/19-%E9%97%AE%E5%8D%B7.png?sign=55a4cd77c418b2c548b65792a2cf6bce&t=1701328694",
|
|
||||||
}),
|
|
||||||
description: "ARTICLE",
|
|
||||||
extension: "",
|
|
||||||
};
|
|
||||||
|
|
||||||
// 发送自定义消息
|
|
||||||
const message = timChatManager.tim.createCustomMessage({
|
|
||||||
to: chatInfo.value.conversationID.replace("GROUP", ""),
|
|
||||||
conversationType: timChatManager.TIM.TYPES.CONV_GROUP,
|
|
||||||
payload: customMessage,
|
|
||||||
});
|
|
||||||
|
|
||||||
const sendResult = await timChatManager.tim.sendMessage(message);
|
|
||||||
|
|
||||||
if (sendResult.code === 0) {
|
|
||||||
uni.showToast({
|
|
||||||
title: "发送成功",
|
|
||||||
icon: "success",
|
|
||||||
});
|
|
||||||
|
|
||||||
// 记录发送记录(可选)
|
|
||||||
// await addArticleSendRecord({
|
|
||||||
// corpId,
|
|
||||||
// userId,
|
|
||||||
// articleId: article._id,
|
|
||||||
// customerId: chatInfo.value.userID
|
|
||||||
// });
|
|
||||||
} else {
|
|
||||||
throw new Error(sendResult.message || "发送失败");
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("发送文章失败:", error);
|
|
||||||
uni.showToast({
|
|
||||||
title: error.message || "发送失败",
|
|
||||||
icon: "none",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 监听问卷发送事件
|
|
||||||
uni.$on("sendSurvey", async (data) => {
|
|
||||||
const { survey, corpId, userId, sendSurveyId } = data;
|
|
||||||
|
|
||||||
if (!survey || !survey._id) {
|
|
||||||
uni.showToast({
|
|
||||||
title: "问卷信息不完整",
|
|
||||||
icon: "none",
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 获取环境变量
|
|
||||||
const env = __VITE_ENV__;
|
|
||||||
const baseUrl = env.VITE_PATIENT_PAGE_BASE_URL || "";
|
|
||||||
const surveyUrl = env.VITE_SURVEY_URL || "";
|
|
||||||
|
|
||||||
// 获取客户信息
|
|
||||||
const customerId = chatInfo.value.userID || "";
|
|
||||||
const customerName = chatInfo.value.customerName || "";
|
|
||||||
|
|
||||||
// 创建问卷记录
|
|
||||||
const { createSurveyRecord } = await import("@/utils/api.js");
|
|
||||||
const recordRes = await createSurveyRecord({
|
|
||||||
corpId,
|
|
||||||
userId,
|
|
||||||
surveryId: survey._id,
|
|
||||||
memberId: customerId,
|
|
||||||
customer: customerName,
|
|
||||||
sendSurveyId,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!recordRes.success) {
|
|
||||||
throw new Error(recordRes.message || "创建问卷记录失败");
|
|
||||||
}
|
|
||||||
|
|
||||||
const answerId = recordRes.data?.id || "";
|
|
||||||
|
|
||||||
// 构建问卷链接
|
|
||||||
let surveyLink = "";
|
|
||||||
if (survey.createBy === "system") {
|
|
||||||
// 系统问卷
|
|
||||||
surveyLink = `${surveyUrl}?corpId=${corpId}&surveyId=${survey.surveyId}&memberId=${customerId}&sendSurveyId=${sendSurveyId}&userId=${userId}`;
|
|
||||||
} else {
|
|
||||||
// 自定义问卷
|
|
||||||
surveyLink = `${baseUrl}pages/survery/fill?corpId=${corpId}&surveryId=${
|
|
||||||
survey._id
|
|
||||||
}&memberId=${customerId}&answerId=${answerId}&name=${encodeURIComponent(
|
|
||||||
customerName
|
|
||||||
)}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建自定义消息
|
|
||||||
const customMessage = {
|
|
||||||
data: JSON.stringify({
|
|
||||||
type: "survey",
|
|
||||||
title: survey.name || "填写问卷",
|
|
||||||
desc: "请填写问卷",
|
|
||||||
url: surveyLink,
|
|
||||||
imgUrl:
|
|
||||||
"https://796f-youcan-clouddev-1-8ewcqf31dbb2b5-1317294507.tcb.qcloud.la/other/19-%E9%97%AE%E5%8D%B7.png?sign=55a4cd77c418b2c548b65792a2cf6bce&t=1701328694",
|
|
||||||
}),
|
|
||||||
description: "SURVEY",
|
|
||||||
extension: "",
|
|
||||||
};
|
|
||||||
|
|
||||||
// 发送自定义消息
|
|
||||||
const message = timChatManager.tim.createCustomMessage({
|
|
||||||
to: chatInfo.value.conversationID.replace("GROUP", ""),
|
|
||||||
conversationType: timChatManager.TIM.TYPES.CONV_GROUP,
|
|
||||||
payload: customMessage,
|
|
||||||
});
|
|
||||||
|
|
||||||
const sendResult = await timChatManager.tim.sendMessage(message);
|
|
||||||
|
|
||||||
if (sendResult.code === 0) {
|
|
||||||
uni.showToast({
|
|
||||||
title: "发送成功",
|
|
||||||
icon: "success",
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
throw new Error(sendResult.message || "发送失败");
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("发送问卷失败:", error);
|
|
||||||
uni.showToast({
|
|
||||||
title: error.message || "发送失败",
|
|
||||||
icon: "none",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -1,423 +0,0 @@
|
|||||||
<template>
|
|
||||||
<view class="survey-page">
|
|
||||||
<view class="header">
|
|
||||||
<view class="search-bar">
|
|
||||||
<uni-icons type="search" size="18" color="#999" />
|
|
||||||
<input class="search-input" v-model="searchName" placeholder="输入问卷名称搜索" @input="handleSearch" />
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="content">
|
|
||||||
<view class="category-sidebar">
|
|
||||||
<scroll-view scroll-y class="category-scroll">
|
|
||||||
<view
|
|
||||||
v-for="cate in categoryList"
|
|
||||||
:key="cate._id || 'all'"
|
|
||||||
class="category-item"
|
|
||||||
:class="{ active: currentCateId === (cate._id || 'all') }"
|
|
||||||
@click="selectCategory(cate)"
|
|
||||||
>
|
|
||||||
{{ cate.name }}
|
|
||||||
</view>
|
|
||||||
</scroll-view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="survey-list">
|
|
||||||
<scroll-view
|
|
||||||
scroll-y
|
|
||||||
class="survey-scroll"
|
|
||||||
@scrolltolower="loadMore"
|
|
||||||
lower-threshold="50"
|
|
||||||
>
|
|
||||||
<view v-if="loading && surveyList.length === 0" class="loading-container">
|
|
||||||
<uni-icons type="spinner-cycle" size="30" color="#999" />
|
|
||||||
<text class="loading-text">加载中...</text>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view v-else-if="surveyList.length === 0" class="empty-container">
|
|
||||||
<empty-data :title="emptyText || '暂无问卷'" />
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view v-else>
|
|
||||||
<view
|
|
||||||
v-for="survey in surveyList"
|
|
||||||
:key="survey._id"
|
|
||||||
class="survey-item"
|
|
||||||
>
|
|
||||||
<view class="survey-content" @click="previewSurvey(survey)">
|
|
||||||
<text class="survey-title">{{ survey.name }}</text>
|
|
||||||
<text class="survey-desc">{{ survey.description || '暂无问卷说明' }}</text>
|
|
||||||
</view>
|
|
||||||
<view class="survey-action">
|
|
||||||
<button class="send-btn" size="mini" type="primary" @click="sendSurvey(survey)">
|
|
||||||
发送
|
|
||||||
</button>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view v-if="loading && surveyList.length > 0" class="loading-more">
|
|
||||||
<uni-icons type="spinner-cycle" size="20" color="#999" />
|
|
||||||
<text>加载中...</text>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view v-if="!loading && surveyList.length >= total" class="no-more">
|
|
||||||
没有更多了
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</scroll-view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<view class="footer">
|
|
||||||
<button class="cancel-btn" @click="goBack">取消</button>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { ref, onMounted } from 'vue';
|
|
||||||
import { getSurveyCateList, getSurveyList, createSurveyRecord } from '@/utils/api.js';
|
|
||||||
import useAccountStore from '@/store/account.js';
|
|
||||||
import EmptyData from '@/components/empty-data.vue';
|
|
||||||
|
|
||||||
const env = __VITE_ENV__;
|
|
||||||
const accountStore = useAccountStore();
|
|
||||||
const corpId = env.MP_CORP_ID;
|
|
||||||
const userId = ref('');
|
|
||||||
|
|
||||||
// 搜索关键词
|
|
||||||
const searchName = ref('');
|
|
||||||
let searchTimer = null;
|
|
||||||
|
|
||||||
// 分类列表
|
|
||||||
const categoryList = ref([{ _id: '', name: '全部' }]);
|
|
||||||
const currentCateId = ref('');
|
|
||||||
|
|
||||||
// 问卷列表
|
|
||||||
const surveyList = ref([]);
|
|
||||||
const loading = ref(false);
|
|
||||||
const page = ref(1);
|
|
||||||
const pageSize = 30;
|
|
||||||
const total = ref(0);
|
|
||||||
const emptyText = ref('');
|
|
||||||
|
|
||||||
// 获取分类列表
|
|
||||||
const getCategoryList = async () => {
|
|
||||||
try {
|
|
||||||
const res = await getSurveyCateList({ corpId: corpId });
|
|
||||||
if (res.success && res.data) {
|
|
||||||
const cates = res.data.list || [];
|
|
||||||
categoryList.value = [
|
|
||||||
{ _id: '', name: '全部' },
|
|
||||||
...cates
|
|
||||||
];
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('获取分类列表失败:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 选择分类
|
|
||||||
const selectCategory = (cate) => {
|
|
||||||
currentCateId.value = cate._id || '';
|
|
||||||
page.value = 1;
|
|
||||||
surveyList.value = [];
|
|
||||||
loadSurveyList();
|
|
||||||
};
|
|
||||||
|
|
||||||
// 搜索处理
|
|
||||||
const handleSearch = () => {
|
|
||||||
if (searchTimer) {
|
|
||||||
clearTimeout(searchTimer);
|
|
||||||
}
|
|
||||||
searchTimer = setTimeout(() => {
|
|
||||||
page.value = 1;
|
|
||||||
surveyList.value = [];
|
|
||||||
loadSurveyList();
|
|
||||||
}, 500);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 加载问卷列表
|
|
||||||
const loadSurveyList = async () => {
|
|
||||||
if (loading.value) return;
|
|
||||||
|
|
||||||
loading.value = true;
|
|
||||||
try {
|
|
||||||
const params = {
|
|
||||||
corpId: corpId,
|
|
||||||
page: page.value,
|
|
||||||
pageSize: pageSize,
|
|
||||||
name: searchName.value.trim(),
|
|
||||||
status: 'enable',
|
|
||||||
showCount: false
|
|
||||||
};
|
|
||||||
|
|
||||||
// 如果选择了分类,添加分类ID
|
|
||||||
if (currentCateId.value) {
|
|
||||||
params.cateIds = [currentCateId.value];
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await getSurveyList(params);
|
|
||||||
if (res.success && res.data) {
|
|
||||||
const { list = [], total: count = 0 } = res.data;
|
|
||||||
|
|
||||||
if (page.value === 1) {
|
|
||||||
surveyList.value = list;
|
|
||||||
} else {
|
|
||||||
surveyList.value = [...surveyList.value, ...list];
|
|
||||||
}
|
|
||||||
total.value = count;
|
|
||||||
emptyText.value = '暂无问卷信息';
|
|
||||||
} else {
|
|
||||||
emptyText.value = res.message || '加载失败';
|
|
||||||
uni.showToast({
|
|
||||||
title: res.message || '获取问卷列表失败',
|
|
||||||
icon: 'none'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('加载问卷列表失败:', error);
|
|
||||||
emptyText.value = '加载失败';
|
|
||||||
uni.showToast({
|
|
||||||
title: '加载失败,请重试',
|
|
||||||
icon: 'none'
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
loading.value = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 加载更多
|
|
||||||
const loadMore = () => {
|
|
||||||
if (loading.value || surveyList.value.length >= total.value) return;
|
|
||||||
page.value += 1;
|
|
||||||
loadSurveyList();
|
|
||||||
};
|
|
||||||
|
|
||||||
// 预览问卷
|
|
||||||
const previewSurvey = (survey) => {
|
|
||||||
// 可以跳转到问卷预览页面或显示问卷详情
|
|
||||||
uni.showToast({
|
|
||||||
title: '预览功能开发中',
|
|
||||||
icon: 'none'
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// 生成随机字符串
|
|
||||||
const generateRandomString = (length) => {
|
|
||||||
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
||||||
let result = '';
|
|
||||||
for (let i = 0; i < length; i++) {
|
|
||||||
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 发送问卷
|
|
||||||
const sendSurvey = async (survey) => {
|
|
||||||
if (loading.value) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
loading.value = true;
|
|
||||||
|
|
||||||
// 获取医生信息
|
|
||||||
const doctorInfo = accountStore.doctorInfo;
|
|
||||||
userId.value = doctorInfo?.userid || accountStore.openid;
|
|
||||||
|
|
||||||
// 生成发送ID
|
|
||||||
const sendSurveyId = generateRandomString(10);
|
|
||||||
|
|
||||||
// 获取当前聊天的客户信息(从上一页传递)
|
|
||||||
const pages = getCurrentPages();
|
|
||||||
const prevPage = pages[pages.length - 2];
|
|
||||||
|
|
||||||
// 这里需要从聊天页面获取客户信息
|
|
||||||
// 暂时使用事件传递方式
|
|
||||||
uni.$emit('sendSurvey', {
|
|
||||||
survey: survey,
|
|
||||||
corpId: corpId,
|
|
||||||
userId: userId.value,
|
|
||||||
sendSurveyId: sendSurveyId
|
|
||||||
});
|
|
||||||
|
|
||||||
uni.showToast({
|
|
||||||
title: '已选择问卷',
|
|
||||||
icon: 'success'
|
|
||||||
});
|
|
||||||
|
|
||||||
// 延迟返回
|
|
||||||
setTimeout(() => {
|
|
||||||
uni.navigateBack();
|
|
||||||
}, 500);
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('发送问卷失败:', error);
|
|
||||||
uni.showToast({
|
|
||||||
title: error.message || '发送失败',
|
|
||||||
icon: 'none'
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
loading.value = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 返回
|
|
||||||
const goBack = () => {
|
|
||||||
uni.navigateBack();
|
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
getCategoryList();
|
|
||||||
loadSurveyList();
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="scss">
|
|
||||||
.survey-page {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
height: 100vh;
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header {
|
|
||||||
background-color: #fff;
|
|
||||||
padding: 20rpx;
|
|
||||||
border-bottom: 1px solid #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-bar {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
background-color: #f5f5f5;
|
|
||||||
border-radius: 8rpx;
|
|
||||||
padding: 16rpx 24rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-input {
|
|
||||||
flex: 1;
|
|
||||||
margin-left: 16rpx;
|
|
||||||
font-size: 28rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.content {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.category-sidebar {
|
|
||||||
width: 200rpx;
|
|
||||||
background-color: #f8f8f8;
|
|
||||||
border-right: 1px solid #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
.category-scroll {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.category-item {
|
|
||||||
padding: 32rpx 24rpx;
|
|
||||||
font-size: 28rpx;
|
|
||||||
color: #333;
|
|
||||||
text-align: center;
|
|
||||||
border-bottom: 1px solid #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
.category-item.active {
|
|
||||||
background-color: #fff;
|
|
||||||
color: #1890ff;
|
|
||||||
font-weight: bold;
|
|
||||||
border-left: 4rpx solid #1890ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.survey-list {
|
|
||||||
flex: 1;
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.survey-scroll {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading-container,
|
|
||||||
.empty-container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 100rpx 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading-text {
|
|
||||||
margin-top: 20rpx;
|
|
||||||
font-size: 28rpx;
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
|
|
||||||
.survey-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 24rpx 30rpx;
|
|
||||||
border-bottom: 1px solid #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
.survey-content {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
margin-right: 20rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.survey-title {
|
|
||||||
font-size: 28rpx;
|
|
||||||
color: #333;
|
|
||||||
font-weight: 500;
|
|
||||||
line-height: 1.5;
|
|
||||||
margin-bottom: 12rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.survey-desc {
|
|
||||||
font-size: 24rpx;
|
|
||||||
color: #999;
|
|
||||||
line-height: 1.4;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
display: -webkit-box;
|
|
||||||
-webkit-line-clamp: 2;
|
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
}
|
|
||||||
|
|
||||||
.survey-action {
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.send-btn {
|
|
||||||
padding: 8rpx 32rpx;
|
|
||||||
font-size: 26rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading-more,
|
|
||||||
.no-more {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 30rpx 0;
|
|
||||||
font-size: 24rpx;
|
|
||||||
color: #999;
|
|
||||||
gap: 10rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
|
||||||
background-color: #fff;
|
|
||||||
padding: 20rpx;
|
|
||||||
border-top: 1px solid #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cancel-btn {
|
|
||||||
width: 100%;
|
|
||||||
background-color: #fff;
|
|
||||||
color: #333;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
70
utils/api.js
70
utils/api.js
@ -24,26 +24,7 @@ const urlsConfig = {
|
|||||||
saveCommonPhrase: 'saveCommonPhrase',
|
saveCommonPhrase: 'saveCommonPhrase',
|
||||||
deleteCommonPhrase: 'deleteCommonPhrase',
|
deleteCommonPhrase: 'deleteCommonPhrase',
|
||||||
getCommonPhraseCategories: 'getCommonPhraseCategories',
|
getCommonPhraseCategories: 'getCommonPhraseCategories',
|
||||||
saveCommonPhraseCategory: 'saveCommonPhraseCategory',
|
saveCommonPhraseCategory: 'saveCommonPhraseCategory'
|
||||||
// 个人常用语接口
|
|
||||||
getPersonalPhrases: 'getPersonalPhrases',
|
|
||||||
savePersonalPhrase: 'savePersonalPhrase',
|
|
||||||
deletePersonalPhrase: 'deletePersonalPhrase',
|
|
||||||
getPersonalPhraseCategories: 'getPersonalPhraseCategories',
|
|
||||||
savePersonalPhraseCategory: 'savePersonalPhraseCategory',
|
|
||||||
deletePersonalPhraseCategory: 'deletePersonalPhraseCategory',
|
|
||||||
// 宣教文章接口
|
|
||||||
getArticleCateList: 'getArticleCateList',
|
|
||||||
getArticleList: 'getArticleList',
|
|
||||||
getArticle: 'getArticle',
|
|
||||||
addArticleSendRecord: 'addArticleSendRecord'
|
|
||||||
},
|
|
||||||
|
|
||||||
survery: {
|
|
||||||
getSurveyCateList: 'getSurveryCateList',
|
|
||||||
getSurveyList: 'getList',
|
|
||||||
createSurveyRecord: 'createRecord',
|
|
||||||
getSurveyDetail: 'getDetail'
|
|
||||||
},
|
},
|
||||||
member: {
|
member: {
|
||||||
addCustomer: 'add',
|
addCustomer: 'add',
|
||||||
@ -60,8 +41,7 @@ const urlsConfig = {
|
|||||||
im: {
|
im: {
|
||||||
getUserSig: 'getUserSig',
|
getUserSig: 'getUserSig',
|
||||||
sendSystemMessage: "sendSystemMessage",
|
sendSystemMessage: "sendSystemMessage",
|
||||||
getChatRecordsByGroupId: "getChatRecordsByGroupId",
|
getChatRecordsByGroupId: "getChatRecordsByGroupId"
|
||||||
sendConsultRejectedMessage: "sendConsultRejectedMessage"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -95,49 +75,3 @@ export default async function api(urlId, data) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 宣教文章相关 API
|
|
||||||
export async function getArticleCateList(data) {
|
|
||||||
return api('getArticleCateList', data);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getArticleList(data) {
|
|
||||||
return api('getArticleList', data);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getArticle(data) {
|
|
||||||
return api('getArticle', data);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function addArticleSendRecord(data) {
|
|
||||||
return api('addArticleSendRecord', data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 问卷相关 API
|
|
||||||
export async function getSurveyCateList(data) {
|
|
||||||
return api('getSurveyCateList', data);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getSurveyList(data) {
|
|
||||||
return api('getSurveyList', data);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function createSurveyRecord(data) {
|
|
||||||
return api('createSurveyRecord', data);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getSurveyDetail(data) {
|
|
||||||
return api('getSurveyDetail', data);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// IM 系统消息相关 API
|
|
||||||
export async function sendConsultRejectedMessage(data) {
|
|
||||||
return request({
|
|
||||||
url: '/getYoucanData/im',
|
|
||||||
data: {
|
|
||||||
type: 'sendConsultRejectedMessage',
|
|
||||||
...data
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|||||||
@ -363,6 +363,7 @@ class TimChatManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取用户信息并登录
|
||||||
async getUserInfoAndLogin(userID) {
|
async getUserInfoAndLogin(userID) {
|
||||||
try {
|
try {
|
||||||
if (userID) {
|
if (userID) {
|
||||||
@ -375,6 +376,7 @@ class TimChatManager {
|
|||||||
}
|
}
|
||||||
this.currentUserID = userInfo.userID
|
this.currentUserID = userInfo.userID
|
||||||
}
|
}
|
||||||
|
|
||||||
this.currentUserSig = await this.getUserSig(this.currentUserID)
|
this.currentUserSig = await this.getUserSig(this.currentUserID)
|
||||||
await this.loginTIM()
|
await this.loginTIM()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -709,37 +711,26 @@ class TimChatManager {
|
|||||||
// 获取消息所属的会话ID
|
// 获取消息所属的会话ID
|
||||||
const messageConversationID = convertedMessage.conversationID
|
const messageConversationID = convertedMessage.conversationID
|
||||||
|
|
||||||
// 检查是否为系统消息
|
|
||||||
const isSystemMsg = this.isSystemMessage(convertedMessage)
|
|
||||||
|
|
||||||
console.log('收到新消息:', {
|
console.log('收到新消息:', {
|
||||||
messageID: convertedMessage.ID,
|
messageID: convertedMessage.ID,
|
||||||
messageConversationID: messageConversationID,
|
messageConversationID: messageConversationID,
|
||||||
currentConversationID: this.currentConversationID,
|
currentConversationID: this.currentConversationID,
|
||||||
messageType: convertedMessage.type,
|
messageType: convertedMessage.type,
|
||||||
from: convertedMessage.from,
|
from: convertedMessage.from
|
||||||
isSystemMessage: isSystemMsg
|
|
||||||
})
|
})
|
||||||
|
// 判断是否为当前会话的消息(必须有currentConversationID且匹配才显示)
|
||||||
// 判断是否为当前会话的消息
|
const isCurrentConversation = this.currentConversationID &&
|
||||||
// 系统消息:只要会话ID匹配就显示(不要求必须有currentConversationID)
|
messageConversationID === this.currentConversationID
|
||||||
// 普通消息:必须有currentConversationID且匹配才显示
|
|
||||||
const isCurrentConversation = isSystemMsg
|
|
||||||
? messageConversationID === this.currentConversationID
|
|
||||||
: (this.currentConversationID && messageConversationID === this.currentConversationID)
|
|
||||||
|
|
||||||
console.log('消息会话匹配检查:', {
|
console.log('消息会话匹配检查:', {
|
||||||
isCurrentConversation,
|
isCurrentConversation,
|
||||||
isSystemMessage: isSystemMsg,
|
|
||||||
hasCurrentConversationID: !!this.currentConversationID,
|
hasCurrentConversationID: !!this.currentConversationID,
|
||||||
conversationIDMatch: messageConversationID === this.currentConversationID
|
conversationIDMatch: messageConversationID === this.currentConversationID
|
||||||
})
|
})
|
||||||
|
|
||||||
if (isCurrentConversation) {
|
if (isCurrentConversation) {
|
||||||
// 当前会话的消息,触发回调
|
// 当前会话的消息,触发回调
|
||||||
console.log('✓ 消息属于当前会话,触发显示')
|
console.log('✓ 消息属于当前会话,触发显示')
|
||||||
this.triggerCallback('onMessageReceived', convertedMessage)
|
this.triggerCallback('onMessageReceived', convertedMessage)
|
||||||
// 处理已读状态(系统消息也标记为已读)
|
// 处理已读状态
|
||||||
if (this.currentConversationID) {
|
if (this.currentConversationID) {
|
||||||
this.markConversationAsRead(this.currentConversationID)
|
this.markConversationAsRead(this.currentConversationID)
|
||||||
}
|
}
|
||||||
@ -1102,52 +1093,7 @@ class TimChatManager {
|
|||||||
} else if (lastMessage.type === 'TIMSoundElem') {
|
} else if (lastMessage.type === 'TIMSoundElem') {
|
||||||
lastMessageText = '[语音]'
|
lastMessageText = '[语音]'
|
||||||
} else if (lastMessage.type === 'TIMCustomElem') {
|
} else if (lastMessage.type === 'TIMCustomElem') {
|
||||||
// 解析自定义消息
|
lastMessageText = lastMessage.payload.data || '[自定义消息]'
|
||||||
try {
|
|
||||||
const customData = JSON.parse(lastMessage.payload.data)
|
|
||||||
const messageType = customData.messageType
|
|
||||||
// 根据消息类型返回不同的预览文本
|
|
||||||
switch (messageType) {
|
|
||||||
case 'system_message':
|
|
||||||
lastMessageText = '[系统消息]'
|
|
||||||
break
|
|
||||||
case 'symptom':
|
|
||||||
lastMessageText = '[病情描述]'
|
|
||||||
break
|
|
||||||
case 'prescription':
|
|
||||||
lastMessageText = '[处方单]'
|
|
||||||
break
|
|
||||||
case 'refill':
|
|
||||||
lastMessageText = '[续方申请]'
|
|
||||||
break
|
|
||||||
case 'survey':
|
|
||||||
lastMessageText = '[问卷调查]'
|
|
||||||
break
|
|
||||||
case 'article':
|
|
||||||
lastMessageText = '[文章]'
|
|
||||||
break
|
|
||||||
case "consult_pending":
|
|
||||||
lastMessageText = '患者向团队发起咨询,请在1小时内接诊,超时将自动关闭会话'
|
|
||||||
break
|
|
||||||
case "consult_rejected":
|
|
||||||
lastMessageText = '患者向团队发起咨询,由于有紧急事务要处理暂时无法接受咨询.本次会话丿关闭'
|
|
||||||
break
|
|
||||||
case "consult_timeout":
|
|
||||||
lastMessageText = '患者向团队发起咨询,团队成员均未接受咨询,本次会话已自动关闭'
|
|
||||||
break
|
|
||||||
case "consult_accepted":
|
|
||||||
lastMessageText = '已接诊,会话已开始'
|
|
||||||
break
|
|
||||||
case "consult_ended":
|
|
||||||
lastMessageText = '已结束当前会话'
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
lastMessageText = '[自定义消息]'
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('解析自定义消息失败:', error)
|
|
||||||
lastMessageText = '[自定义消息]'
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
lastMessageText = '[未知消息类型]'
|
lastMessageText = '[未知消息类型]'
|
||||||
}
|
}
|
||||||
@ -2416,41 +2362,6 @@ class TimChatManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 工具方法
|
// 工具方法
|
||||||
// 判断是否为系统消息
|
|
||||||
isSystemMessage(message) {
|
|
||||||
if (message.type !== 'TIMCustomElem') {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查 payload.data 是否包含系统消息标记
|
|
||||||
try {
|
|
||||||
if (message.payload && message.payload.data) {
|
|
||||||
const data = typeof message.payload.data === 'string'
|
|
||||||
? JSON.parse(message.payload.data)
|
|
||||||
: message.payload.data
|
|
||||||
|
|
||||||
// 检查是否为系统消息类型
|
|
||||||
if (data.type === 'system_message') {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查 description 是否为系统消息标记
|
|
||||||
if (message.payload && message.payload.description === '系统消息标记') {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 兼容旧的系统消息格式
|
|
||||||
if (message.payload && message.payload.description === 'SYSTEM_NOTIFICATION') {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('判断系统消息失败:', error)
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
filterMessage(message) {
|
filterMessage(message) {
|
||||||
if (message.type === 'TIMCustomElem' && message.payload && message.payload.data) {
|
if (message.type === 'TIMCustomElem' && message.payload && message.payload.data) {
|
||||||
if (message.payload.data === 'group_create' || message.payload.data === 'purchased') {
|
if (message.payload.data === 'group_create' || message.payload.data === 'purchased') {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user