9.19自定义消息,聊天相关页面
This commit is contained in:
parent
b884a8f6fc
commit
ae08b222af
9
App.vue
9
App.vue
@ -331,6 +331,10 @@ export default {
|
|||||||
|
|
||||||
/** nim sdk 登录 */
|
/** nim sdk 登录 */
|
||||||
nim.V2NIMLoginService.login(opts.account, opts.token).then(async () => {
|
nim.V2NIMLoginService.login(opts.account, opts.token).then(async () => {
|
||||||
|
console.log("登录成功");
|
||||||
|
nim.V2NIMLoginService.on('onKickedOffline', (res) => {
|
||||||
|
console.log("被踢下线", res);
|
||||||
|
});
|
||||||
// #ifdef APP-PLUS
|
// #ifdef APP-PLUS
|
||||||
/** 初始化音视频通话插件*/
|
/** 初始化音视频通话插件*/
|
||||||
nimCallKit.initConfig(
|
nimCallKit.initConfig(
|
||||||
@ -345,6 +349,7 @@ export default {
|
|||||||
if (ret.code != 200) {
|
if (ret.code != 200) {
|
||||||
// callkit init失败
|
// callkit init失败
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
nimCallKit.login(
|
nimCallKit.login(
|
||||||
{
|
{
|
||||||
account: opts.account,
|
account: opts.account,
|
||||||
@ -355,6 +360,7 @@ export default {
|
|||||||
// 登录失败
|
// 登录失败
|
||||||
} else {
|
} else {
|
||||||
// 登录成功
|
// 登录成功
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -396,7 +402,8 @@ export default {
|
|||||||
console.log("音视频通话插件退出失败", error);
|
console.log("音视频通话插件退出失败", error);
|
||||||
}
|
}
|
||||||
// 退出登录
|
// 退出登录
|
||||||
uni.$UIKitNIM.V2NIMLoginService.logout().then(() => {
|
uni.$UIKitNIM.V2NIMLoginService.logout().then((res) => {
|
||||||
|
console.log("退出登录", res);
|
||||||
uni.$UIKitStore.destroy();
|
uni.$UIKitStore.destroy();
|
||||||
customReLaunch({
|
customReLaunch({
|
||||||
url: "/pages/Login/index",
|
url: "/pages/Login/index",
|
||||||
|
|||||||
18
api/api.js
18
api/api.js
@ -493,9 +493,21 @@ const api = {
|
|||||||
meetingV2Video(data){
|
meetingV2Video(data){
|
||||||
return request('/expertAPI/meetingV2Video', data, 'post', false);
|
return request('/expertAPI/meetingV2Video', data, 'post', false);
|
||||||
},
|
},
|
||||||
|
listGroupSendMsg(data){
|
||||||
|
return request('/expertAPI/listGroupSendMsg', data, 'post', false);
|
||||||
|
},
|
||||||
|
addGroupSendMsg4YunXin(data){
|
||||||
|
return request('/expertAPI/addGroupSendMsg4YunXin', data, 'post', false);
|
||||||
|
},
|
||||||
|
quickReplyList(data){
|
||||||
|
return request('/expertAPI/QuickReplyList', data, 'post', false);
|
||||||
|
},
|
||||||
|
addQuickReply(data){
|
||||||
|
return request('/expertAPI/addQuickReply', data, 'post', false);
|
||||||
|
},
|
||||||
|
deleteQuickReply(data){
|
||||||
|
return request('/expertAPI/deleteQuickReply', data, 'post', false);
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
60
pages.json
60
pages.json
@ -1047,6 +1047,66 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "groupSend/groupSend",
|
||||||
|
"style": {
|
||||||
|
"navigationStyle": "custom",
|
||||||
|
"navigationBarTitleText": "uni-app分页",
|
||||||
|
"app": {
|
||||||
|
"bounce": "none"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "patientGroup/patientGroup",
|
||||||
|
"style": {
|
||||||
|
"navigationStyle": "custom",
|
||||||
|
"navigationBarTitleText": "uni-app分页",
|
||||||
|
"app": {
|
||||||
|
"bounce": "none"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "quickReply/quickReply",
|
||||||
|
"style": {
|
||||||
|
"navigationStyle": "custom",
|
||||||
|
"navigationBarTitleText": "uni-app分页",
|
||||||
|
"app": {
|
||||||
|
"bounce": "none"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "article/article",
|
||||||
|
"style": {
|
||||||
|
"navigationStyle": "custom",
|
||||||
|
"navigationBarTitleText": "uni-app分页",
|
||||||
|
"app": {
|
||||||
|
"bounce": "none"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "searchArticle/searchArticle",
|
||||||
|
"style": {
|
||||||
|
"navigationStyle": "custom",
|
||||||
|
"navigationBarTitleText": "uni-app分页",
|
||||||
|
"app": {
|
||||||
|
"bounce": "none"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "video/video",
|
||||||
|
"style": {
|
||||||
|
"navigationStyle": "custom",
|
||||||
|
"navigationBarTitleText": "uni-app分页",
|
||||||
|
"app": {
|
||||||
|
"bounce": "none"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "groupMessage/groupMessage",
|
"path": "groupMessage/groupMessage",
|
||||||
"style": {
|
"style": {
|
||||||
|
|||||||
@ -304,7 +304,7 @@
|
|||||||
url="/pages_app/patientGroup/patientGroup"
|
url="/pages_app/patientGroup/patientGroup"
|
||||||
break;
|
break;
|
||||||
case 'groupMessage':
|
case 'groupMessage':
|
||||||
url="/pages_app/groupMessage/groupMessage"
|
url="/pages_chat/groupMessage/groupMessage"
|
||||||
break;
|
break;
|
||||||
case 'qrcode':
|
case 'qrcode':
|
||||||
url="/pages_app/myCode/myCode"
|
url="/pages_app/myCode/myCode"
|
||||||
|
|||||||
@ -88,7 +88,7 @@
|
|||||||
const updateSelectedDetail = () => {
|
const updateSelectedDetail = () => {
|
||||||
selectedDetail.value = selectedIds.value.map(id => {
|
selectedDetail.value = selectedIds.value.map(id => {
|
||||||
const p = patientList.value.find(x => x.uuid === id)
|
const p = patientList.value.find(x => x.uuid === id)
|
||||||
return { uuid: id, realName: p?.realName || '', photo: p?.photo || '' }
|
return { uuid: id, realName: p?.realName || '', photo: p?.photo || '',nickName: p?.nickName || '' }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,7 +103,7 @@
|
|||||||
// 如果未选中,则选中
|
// 如果未选中,则选中
|
||||||
selectedIds.value.push(id)
|
selectedIds.value.push(id)
|
||||||
const p = patientList.value.find(x => x.uuid === id)
|
const p = patientList.value.find(x => x.uuid === id)
|
||||||
selectedDetail.value.push({ uuid: id, realName: p?.realName || '', photo: p?.photo || '' })
|
selectedDetail.value.push({ uuid: id, realName: p?.realName || '', photo: p?.photo || '',nickName: p?.nickName || '' })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,7 +116,9 @@
|
|||||||
const pages = getCurrentPages()
|
const pages = getCurrentPages()
|
||||||
const curr = pages[pages.length - 1]
|
const curr = pages[pages.length - 1]
|
||||||
const ec = curr?.getOpenerEventChannel?.()
|
const ec = curr?.getOpenerEventChannel?.()
|
||||||
ec?.emit && ec.emit('onPatientsSelected', payload)
|
ec?.emit && ec.emit('onPatientsSelected', payload);
|
||||||
|
console.log(selectedDetail.value)
|
||||||
|
uni.$emit('selectedChatPatientsSingle', { patients: selectedDetail.value });
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
// 兜底:使用本地存储
|
// 兜底:使用本地存储
|
||||||
try { uni.setStorageSync('patientsSelectedPayload', payload) } catch (e) {}
|
try { uni.setStorageSync('patientsSelectedPayload', payload) } catch (e) {}
|
||||||
|
|||||||
@ -88,7 +88,7 @@
|
|||||||
const updateSelectedDetail = () => {
|
const updateSelectedDetail = () => {
|
||||||
selectedDetail.value = selectedIds.value.map(id => {
|
selectedDetail.value = selectedIds.value.map(id => {
|
||||||
const p = patientList.value.find(x => x.uuid === id)
|
const p = patientList.value.find(x => x.uuid === id)
|
||||||
return { uuid: id, realName: p?.realName || '', photo: p?.photo || '' }
|
return { uuid: id, realName: p?.realName || '', photo: p?.photo || '',nickname: p?.nickname || '' }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,7 +105,7 @@
|
|||||||
selectedIds.value.push(id)
|
selectedIds.value.push(id)
|
||||||
const p = patientList.value.find(x => x.uuid === id)
|
const p = patientList.value.find(x => x.uuid === id)
|
||||||
selectedDetail.value.push({ uuid: id, realName: p?.realName || '', photo: p?.photo || '' })
|
selectedDetail.value.push({ uuid: id, realName: p?.realName || '', photo: p?.photo || '' })
|
||||||
let payload = { uuid: id, realName: p?.realName || '', photo: p?.photo || '' }
|
let payload = { uuid: id, realName: p?.realName || '', photo: p?.photo || '',nickname: p?.nickname || '' }
|
||||||
const pages = getCurrentPages()
|
const pages = getCurrentPages()
|
||||||
const curr = pages[pages.length - 1]
|
const curr = pages[pages.length - 1]
|
||||||
const ec = curr?.getOpenerEventChannel?.()
|
const ec = curr?.getOpenerEventChannel?.()
|
||||||
|
|||||||
668
pages_chat/article/article.vue
Normal file
668
pages_chat/article/article.vue
Normal file
@ -0,0 +1,668 @@
|
|||||||
|
<template>
|
||||||
|
<navBar title="患教学堂"></navBar>
|
||||||
|
<view class="page">
|
||||||
|
<view class="top">
|
||||||
|
<!-- 筛选栏 -->
|
||||||
|
<view class="filter-bar">
|
||||||
|
<view class="search-box" @click="goToSearch">
|
||||||
|
<uni-icons type="search" size="16" color="#999"></uni-icons>
|
||||||
|
<text class="search-text">搜索</text>
|
||||||
|
</view>
|
||||||
|
<view class="divider"></view>
|
||||||
|
<view class="filter-item active" @click="toggleSort">
|
||||||
|
<text>{{type==1?'最新':'最热'}}</text>
|
||||||
|
<up-image v-if="type==1" :src="upImg" width="20rpx" height="26rpx" ></up-image>
|
||||||
|
<up-image v-else :src="downImg" width="20rpx" height="26rpx" ></up-image>
|
||||||
|
</view>
|
||||||
|
<!-- <view class="divider"></view>
|
||||||
|
<view class="filter-item" :class="{ active: isFilterActive }" @click="showFilterPopup">
|
||||||
|
<text>筛选</text>
|
||||||
|
<up-image :src="isFilterActive ? filterOn : filter" width="30rpx" height="30rpx" ></up-image>
|
||||||
|
</view> -->
|
||||||
|
</view>
|
||||||
|
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 筛选弹窗 -->
|
||||||
|
<view class="filter-popup" v-if="showFilter" @click="hideFilterPopup">
|
||||||
|
<view class="filter-content" @click.stop>
|
||||||
|
<!-- 筛选标签网格 -->
|
||||||
|
<view class="filter-tags">
|
||||||
|
<view
|
||||||
|
class="tag-item"
|
||||||
|
v-for="(tag, index) in filterTags"
|
||||||
|
:key="index"
|
||||||
|
:class="{ active: tag.selected }"
|
||||||
|
@click="toggleTag(index)"
|
||||||
|
>
|
||||||
|
{{ tag.DES }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 底部按钮 -->
|
||||||
|
<view class="filter-buttons">
|
||||||
|
<view class="btn-reset" @click="resetFilter">重置</view>
|
||||||
|
<view class="btn-confirm" @click="confirmFilter">确定</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 文章列表 -->
|
||||||
|
<scroll-view
|
||||||
|
class="article-list"
|
||||||
|
scroll-y
|
||||||
|
refresher-enabled="true"
|
||||||
|
:refresher-triggered="refreshing"
|
||||||
|
@refresherrefresh="onRefresh"
|
||||||
|
@scrolltolower="onLoadMore"
|
||||||
|
:lower-threshold="100"
|
||||||
|
>
|
||||||
|
<view class="article-item" v-for="(article, index) in articleList" :key="article.uuid || index" @click="goToDetail(article)">
|
||||||
|
<image class="article-image" :src="article.image" mode="aspectFill"></image>
|
||||||
|
<view class="article-content">
|
||||||
|
<view class="article-title">{{ article.title }}</view>
|
||||||
|
<view class="article-meta">
|
||||||
|
<view class="date-tag" v-if="article.isToday">今日</view>
|
||||||
|
<view class="date" v-else>{{ article.date }}</view>
|
||||||
|
<view class="stats">
|
||||||
|
<view class="stat-item">
|
||||||
|
<uni-icons type="eye" size="12" color="#999"></uni-icons>
|
||||||
|
<text>{{ article.views }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="stat-item">
|
||||||
|
<uni-icons type="hand-up" size="13" color="#999"></uni-icons>
|
||||||
|
<text>{{ article.likes }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 加载更多状态 -->
|
||||||
|
<view class="load-more" v-if="loading">
|
||||||
|
<uni-icons type="spinner-cycle" size="20" color="#999"></uni-icons>
|
||||||
|
<text class="load-text">加载中...</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 没有更多数据 -->
|
||||||
|
<view class="no-more" v-if="!hasMore && articleList.length > 0">
|
||||||
|
<text class="no-more-text">没有更多数据了</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 空状态 -->
|
||||||
|
<view class="empty-state" v-if="articleList.length === 0 && !loading">
|
||||||
|
<uni-icons type="article" size="80" color="#cccccc"></uni-icons>
|
||||||
|
<text class="empty-text">暂无文章</text>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
|
||||||
|
<!-- 底部导航栏 -->
|
||||||
|
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { onShow } from "@dcloudio/uni-app";
|
||||||
|
import upImg from "@/static/cb_up.png"
|
||||||
|
import downImg from "@/static/cb_up.png"
|
||||||
|
import filter from "@/static/cb_screen_no.png"
|
||||||
|
import filterOn from "@/static/cb_screen_yes.png"
|
||||||
|
import api from "@/api/api.js"
|
||||||
|
import docUrl from '@/utils/docUrl.js';
|
||||||
|
import navTo from '@/utils/navTo.js';
|
||||||
|
import navBar from '@/components/navBar/navBar.vue';
|
||||||
|
|
||||||
|
const type=ref(1)
|
||||||
|
|
||||||
|
// 分页相关状态
|
||||||
|
const currentPage = ref(1);
|
||||||
|
const pageSize = ref(10);
|
||||||
|
const hasMore = ref(true);
|
||||||
|
const loading = ref(false);
|
||||||
|
const refreshing = ref(false);
|
||||||
|
// 响应式数据
|
||||||
|
const activeTab = ref(0);
|
||||||
|
const showFilter = ref(false);
|
||||||
|
const isFilterActive = ref(false);
|
||||||
|
const toggleSort = () => {
|
||||||
|
type.value = type.value === 1 ? 2 : 1;
|
||||||
|
// 重置分页状态
|
||||||
|
currentPage.value = 1;
|
||||||
|
hasMore.value = true;
|
||||||
|
polularScienceArticleListByKeywordsNew(true);
|
||||||
|
};
|
||||||
|
const loadGuideTags = async () => {
|
||||||
|
try {
|
||||||
|
const res = await api.guideTag({
|
||||||
|
type:4
|
||||||
|
});
|
||||||
|
if(res && res.code === 200 && res.data) {
|
||||||
|
// 将API返回的标签数据转换为筛选标签格式
|
||||||
|
filterTags.value = res.data.map(tag => ({
|
||||||
|
...tag,
|
||||||
|
selected: false
|
||||||
|
}));
|
||||||
|
console.log('指南标签加载成功:', filterTags.value);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('加载指南标签失败:', e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const polularScienceArticleListByKeywordsNew = async (isRefresh = false) => {
|
||||||
|
if (loading.value) return;
|
||||||
|
|
||||||
|
loading.value = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const page = isRefresh ? 1 : currentPage.value;
|
||||||
|
const res = await api.polularScienceArticleListByKeywordsNew({
|
||||||
|
keywords: keywords.value,
|
||||||
|
page: page,
|
||||||
|
type: type.value
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res && res.code === 200 && res.data) {
|
||||||
|
const newData = res.data.list || [];
|
||||||
|
|
||||||
|
if (isRefresh) {
|
||||||
|
// 下拉刷新:替换数据
|
||||||
|
articleList.value = newData.map(item => formatArticleData(item));
|
||||||
|
currentPage.value = 1;
|
||||||
|
} else {
|
||||||
|
// 上拉加载:追加数据
|
||||||
|
const formattedData = newData.map(item => formatArticleData(item));
|
||||||
|
articleList.value = [...articleList.value, ...formattedData];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断是否还有更多数据
|
||||||
|
hasMore.value = newData.length >= pageSize.value;
|
||||||
|
|
||||||
|
if (!isRefresh) {
|
||||||
|
currentPage.value++;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('文章数据加载成功:', articleList.value);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取文章列表失败:', error);
|
||||||
|
uni.showToast({
|
||||||
|
title: '获取数据失败',
|
||||||
|
icon: 'error',
|
||||||
|
duration: 2000
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
refreshing.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 格式化文章数据
|
||||||
|
const formatArticleData = (item) => {
|
||||||
|
// 安全检查,确保item存在且有所需属性
|
||||||
|
if (!item) return null;
|
||||||
|
|
||||||
|
const submitDate = item.submitDate ? new Date(item.submitDate) : new Date();
|
||||||
|
const today = new Date();
|
||||||
|
const isToday = submitDate.toDateString() === today.toDateString();
|
||||||
|
|
||||||
|
return {
|
||||||
|
uuid: item.uuid || '',
|
||||||
|
title: item.topic || '无标题',
|
||||||
|
summary: item.summary || '',
|
||||||
|
image: item.imgPath ? docUrl + item.imgPath : '/static/liver_knowledge.png',
|
||||||
|
date: isToday ? '今日' : formatDate(submitDate),
|
||||||
|
isToday: isToday,
|
||||||
|
views: item.readnum || 0,
|
||||||
|
likes: item.agreenum || 0,
|
||||||
|
tags: item.tags || '',
|
||||||
|
path: item.path || '',
|
||||||
|
submitDate: item.submitDate || ''
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// 格式化日期
|
||||||
|
const formatDate = (date) => {
|
||||||
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||||
|
const day = String(date.getDate()).padStart(2, '0');
|
||||||
|
return `${month}-${day}`;
|
||||||
|
};
|
||||||
|
// 筛选标签数据
|
||||||
|
const filterTags = ref([]);
|
||||||
|
|
||||||
|
// 文章列表数据
|
||||||
|
const articleList = ref([]);
|
||||||
|
|
||||||
|
// 方法
|
||||||
|
const switchTab = (index) => {
|
||||||
|
|
||||||
|
if(index==1){
|
||||||
|
navTo({
|
||||||
|
url: '/pages_app/patientVideo/patientVideo'
|
||||||
|
})
|
||||||
|
}else if(index==2){
|
||||||
|
let url=encodeURIComponent('https://wx.igandan.com/wxPatient/index.htm#/problem?link=share&fromtype=doctor')
|
||||||
|
navTo({
|
||||||
|
url: '/pages_app/webview/webview?url='+url
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const goToDetail = (article) => {
|
||||||
|
console.log('查看文章详情:', article);
|
||||||
|
// 跳转到详情页
|
||||||
|
uni.$emit('articelItem', article);
|
||||||
|
uni.navigateBack();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 跳转到搜索页面
|
||||||
|
const goToSearch = () => {
|
||||||
|
navTo({
|
||||||
|
url: '/pages_chat/searchArticle/searchArticle'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 下拉刷新
|
||||||
|
const onRefresh = async () => {
|
||||||
|
refreshing.value = true;
|
||||||
|
await polularScienceArticleListByKeywordsNew(true);
|
||||||
|
uni.showToast({
|
||||||
|
title: '刷新成功',
|
||||||
|
icon: 'success',
|
||||||
|
duration: 1500
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 上拉加载更多
|
||||||
|
const onLoadMore = async () => {
|
||||||
|
if (!hasMore.value || loading.value) return;
|
||||||
|
await polularScienceArticleListByKeywordsNew(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const goToPage = (page) => {
|
||||||
|
console.log('跳转到页面:', page);
|
||||||
|
// 根据页面名称进行跳转
|
||||||
|
if (page === 'index') {
|
||||||
|
uni.switchTab({
|
||||||
|
url: '/pages/index/index'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 筛选相关方法
|
||||||
|
const showFilterPopup = () => {
|
||||||
|
showFilter.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const hideFilterPopup = () => {
|
||||||
|
showFilter.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleTag = (index) => {
|
||||||
|
filterTags.value[index].selected = !filterTags.value[index].selected;
|
||||||
|
};
|
||||||
|
|
||||||
|
const resetFilter = () => {
|
||||||
|
filterTags.value.forEach(tag => {
|
||||||
|
tag.selected = false;
|
||||||
|
});
|
||||||
|
isFilterActive.value = false;
|
||||||
|
};
|
||||||
|
const keywords=ref('');
|
||||||
|
const confirmFilter = () => {
|
||||||
|
// 检查是否有选中的标签
|
||||||
|
const hasSelected = filterTags.value.some(tag => tag.selected);
|
||||||
|
isFilterActive.value = hasSelected;
|
||||||
|
|
||||||
|
// 执行筛选逻辑
|
||||||
|
if (hasSelected) {
|
||||||
|
const selectedTags = filterTags.value.filter(tag => tag.selected).map(tag => tag.DES);
|
||||||
|
console.log('选中的标签:', selectedTags);
|
||||||
|
keywords.value = selectedTags.join(',');
|
||||||
|
} else {
|
||||||
|
keywords.value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置分页状态
|
||||||
|
currentPage.value = 1;
|
||||||
|
hasMore.value = true;
|
||||||
|
|
||||||
|
showFilter.value = false;
|
||||||
|
polularScienceArticleListByKeywordsNew(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
onShow(() => {
|
||||||
|
polularScienceArticleListByKeywordsNew(true);
|
||||||
|
loadGuideTags();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
// 颜色变量
|
||||||
|
$primary-color: #ff6b6b;
|
||||||
|
$theme-color: #8B2316;
|
||||||
|
$white: #fff;
|
||||||
|
$gray-bg: #f5f5f5;
|
||||||
|
$gray-light: #eee;
|
||||||
|
$gray-medium: #999;
|
||||||
|
$gray-dark: #666;
|
||||||
|
$text-color: #333;
|
||||||
|
|
||||||
|
// 尺寸变量
|
||||||
|
$border-radius: 8px;
|
||||||
|
$border-radius-small: 6px;
|
||||||
|
$padding: 15px;
|
||||||
|
$padding-small: 10px;
|
||||||
|
|
||||||
|
.page {
|
||||||
|
background-color: $gray-bg;
|
||||||
|
height: calc(100vh - 140rpx);
|
||||||
|
display: flex;
|
||||||
|
overflow-y: hidden;
|
||||||
|
flex-direction: column;
|
||||||
|
// 为固定导航栏留出空间
|
||||||
|
}
|
||||||
|
.top{
|
||||||
|
width:100%;
|
||||||
|
position: fixed;
|
||||||
|
top:140rpx;
|
||||||
|
}
|
||||||
|
// 选项卡
|
||||||
|
.tabs {
|
||||||
|
background-color: $white;
|
||||||
|
display: flex;
|
||||||
|
border-bottom: 1px solid $gray-light;
|
||||||
|
|
||||||
|
.tab-item {
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
padding: $padding 0;
|
||||||
|
font-size: 16px;
|
||||||
|
color: $gray-dark;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
color: $theme-color;
|
||||||
|
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 30px;
|
||||||
|
height: 2px;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 筛选栏
|
||||||
|
.filter-bar {
|
||||||
|
background-color: $white;
|
||||||
|
padding: $padding-small $padding;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border-bottom: 1px solid $gray-light;
|
||||||
|
|
||||||
|
.search-box {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
|
||||||
|
.search-text {
|
||||||
|
font-size: 14px;
|
||||||
|
color: $gray-medium;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
width: 1px;
|
||||||
|
height: 16px;
|
||||||
|
background-color: $gray-medium;
|
||||||
|
margin: 0 $padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-item {
|
||||||
|
flex:1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 3px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #999;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.filter-item.active{
|
||||||
|
color: $theme-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 文章列表
|
||||||
|
.article-list {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
top:240rpx;
|
||||||
|
padding-bottom: 0rpx;
|
||||||
|
.article-item {
|
||||||
|
background-color: $white;
|
||||||
|
margin-bottom: $padding-small;
|
||||||
|
border-radius: $border-radius;
|
||||||
|
padding: $padding;
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
.article-image {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
border-radius: $border-radius-small;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-content {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.article-title {
|
||||||
|
font-size: 16px;
|
||||||
|
color: $text-color;
|
||||||
|
line-height: 1.4;
|
||||||
|
margin-bottom: $padding-small;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-meta {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.date-tag {
|
||||||
|
background-color: $primary-color;
|
||||||
|
color: $white;
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date {
|
||||||
|
font-size: 12px;
|
||||||
|
color: $gray-medium;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats {
|
||||||
|
display: flex;
|
||||||
|
gap: $padding;
|
||||||
|
|
||||||
|
.stat-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 3px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: $gray-medium;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载状态样式
|
||||||
|
.load-more {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 30rpx;
|
||||||
|
color: #999;
|
||||||
|
|
||||||
|
.load-text {
|
||||||
|
margin-left: 10rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-more {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 30rpx;
|
||||||
|
|
||||||
|
.no-more-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 100rpx 30rpx;
|
||||||
|
|
||||||
|
.empty-text {
|
||||||
|
margin-top: 20rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 底部导航栏
|
||||||
|
.bottom-nav {
|
||||||
|
background-color: $white;
|
||||||
|
display: flex;
|
||||||
|
border-top: 1px solid $gray-light;
|
||||||
|
padding: 8px 0;
|
||||||
|
|
||||||
|
.nav-item {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 2px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: $gray-medium;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
color: $primary-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
text {
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 筛选弹窗样式
|
||||||
|
.filter-popup {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
// background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
z-index: 9999;
|
||||||
|
|
||||||
|
.filter-content {
|
||||||
|
margin-top:329rpx;
|
||||||
|
|
||||||
|
background-color: $white;
|
||||||
|
// border-radius: 20rpx 20rpx 0 0;
|
||||||
|
padding: 20rpx 30rpx 60rpx;
|
||||||
|
width: 100%;
|
||||||
|
max-height: 80vh;
|
||||||
|
|
||||||
|
.filter-tags {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 20rpx;
|
||||||
|
margin-bottom: 60rpx;
|
||||||
|
max-height: 65vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
.tag-item {
|
||||||
|
background-color: #f8f8f8;
|
||||||
|
color: $gray-dark;
|
||||||
|
padding: 8rpx 24rpx;
|
||||||
|
border-radius: 30rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
border: 2rpx solid #efefef;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: #fff;
|
||||||
|
color: $theme-color;
|
||||||
|
border-color: $theme-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 20rpx;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 30rpx;
|
||||||
|
left: 30rpx;
|
||||||
|
right: 30rpx;
|
||||||
|
|
||||||
|
.btn-reset,
|
||||||
|
.btn-confirm {
|
||||||
|
flex: 1;
|
||||||
|
height: 70rpx;
|
||||||
|
border-radius: 14rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-reset {
|
||||||
|
background-color: $white;
|
||||||
|
color: $theme-color;
|
||||||
|
border: 2rpx solid $theme-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-confirm {
|
||||||
|
border: 2rpx solid $theme-color;
|
||||||
|
background-color: $theme-color;
|
||||||
|
color: $white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
234
pages_chat/chat/message/message-custom.vue
Normal file
234
pages_chat/chat/message/message-custom.vue
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
<template>
|
||||||
|
<div class="msg-custom" :style="{ fontSize: (fontSize || 16) + 'px' }">
|
||||||
|
<view
|
||||||
|
class="customCell cloumn"
|
||||||
|
v-if="JSON.parse(msg.attachment.raw).gdxz_ext_data == '[肝胆商城]'"
|
||||||
|
@click="goToDetail(JSON.parse(msg.attachment.raw).gdxz_url)"
|
||||||
|
>
|
||||||
|
<view class="title">{{ JSON.parse(msg.attachment.raw).gdxz_title }}</view>
|
||||||
|
<view class="row">
|
||||||
|
<view class="left"> 肝胆相照®肝胆病在线公共服务平台 </view>
|
||||||
|
<view class="right">
|
||||||
|
<image
|
||||||
|
src="http://doc.igandan.com/app/html/img/2016/20160714132557.png"
|
||||||
|
mode="widthFix"
|
||||||
|
></image>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="customCell cloumn"
|
||||||
|
v-else-if="JSON.parse(msg.attachment.raw).gdxz_ext_data == '[公益咨询]'"
|
||||||
|
@click="goDetail(JSON.parse(msg.attachment.raw).gdxz_id)"
|
||||||
|
>
|
||||||
|
<view class="title">{{ JSON.parse(msg.attachment.raw).gdxz_title }}</view>
|
||||||
|
<view class="row">
|
||||||
|
<view class="left"> 点击查看问题详情 </view>
|
||||||
|
<view class="right">
|
||||||
|
<image
|
||||||
|
src="http://doc.igandan.com/app/html/img/2016/20160714132557.png"
|
||||||
|
mode="widthFix"
|
||||||
|
></image>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="customCell cloumn"
|
||||||
|
v-else-if="JSON.parse(msg.attachment.raw).gdxz_ext_data == '[门诊公告]'"
|
||||||
|
@click="goToDetail(JSON.parse(msg.attachment.raw).gdxz_url)"
|
||||||
|
>
|
||||||
|
|
||||||
|
<view class="title">{{ JSON.parse(msg.attachment.raw).gdxz_title }}</view>
|
||||||
|
<view class="row">
|
||||||
|
<view class="left"> 肝胆相照®肝胆病在线公共服务平台 </view>
|
||||||
|
<view class="right">
|
||||||
|
<image
|
||||||
|
src="http://doc.igandan.com/app/html/img/2016/20160714132557.png"
|
||||||
|
mode="widthFix"
|
||||||
|
></image>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="customCell cloumn"
|
||||||
|
v-else-if="
|
||||||
|
JSON.parse(msg.attachment.raw).gdxz_ext_data == '[图文科普]' ||
|
||||||
|
JSON.parse(msg.attachment.raw).gdxz_ext_data == '[视频科普]'
|
||||||
|
"
|
||||||
|
@click="goToDetail(JSON.parse(msg.attachment.raw).gdxz_url)"
|
||||||
|
>
|
||||||
|
<view class="title">{{ JSON.parse(msg.attachment.raw).gdxz_title }}</view>
|
||||||
|
<view class="row">
|
||||||
|
<view class="left">
|
||||||
|
{{ JSON.parse(msg.attachment.raw).gdxz_content }}
|
||||||
|
</view>
|
||||||
|
<view class="right">
|
||||||
|
<image
|
||||||
|
src="http://doc.igandan.com/app/html/img/2016/20160714132557.png"
|
||||||
|
mode="widthFix"
|
||||||
|
></image>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view
|
||||||
|
class="customCell cloumn"
|
||||||
|
v-else-if="JSON.parse(msg.attachment.raw).gdxz_ext_data == '[互联网医院]'"
|
||||||
|
@click="goToMiniProgram"
|
||||||
|
>
|
||||||
|
|
||||||
|
<view class="title">肝胆相照互联网医院</view>
|
||||||
|
<view class="row">
|
||||||
|
<view class="left">
|
||||||
|
{{ JSON.parse(msg.attachment.raw).gdxz_content }}
|
||||||
|
</view>
|
||||||
|
<view class="right">
|
||||||
|
<image
|
||||||
|
src="http://doc.igandan.com/app/html/img/2016/20160714132557.png"
|
||||||
|
mode="widthFix"
|
||||||
|
></image>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
/**文本消息组件 */
|
||||||
|
import Icon from "@/components/Icon.vue";
|
||||||
|
// @ts-ignore
|
||||||
|
import UniLink from "@/components/uni-components/uni-link/components/uni-link/uni-link.vue";
|
||||||
|
import { parseText } from "@/utils/im/parseText";
|
||||||
|
import { EMOJI_ICON_MAP_CONFIG } from "@/utils/im/emoji";
|
||||||
|
import { V2NIMMessageForUI } from "@xkit-yx/im-store-v2/dist/types/types";
|
||||||
|
import { t } from "@/utils/im/i18n";
|
||||||
|
import { isHarmonyOs } from "@/utils/im/index";
|
||||||
|
import navTo from "@/utils/navTo";
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
msg: V2NIMMessageForUI;
|
||||||
|
fontSize?: number;
|
||||||
|
}>(),
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
const goToDetail = (url: string) => {
|
||||||
|
navTo({
|
||||||
|
url: `/pages_app/webview/webview?url=${encodeURIComponent(url)}`,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const goDetail = (id: string) => {
|
||||||
|
navTo({
|
||||||
|
url: `/pages_app/consultDetail/consultDetail?uuid=${id}`,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
// 跳转到微信小程序
|
||||||
|
const goToMiniProgram = () => {
|
||||||
|
// 检查是否在App端
|
||||||
|
// #ifdef APP-PLUS
|
||||||
|
// App端跳转小程序的逻辑
|
||||||
|
plus.share.getServices((res) => {
|
||||||
|
let sweixin = null;
|
||||||
|
for (let i in res) {
|
||||||
|
if (res[i].id == "weixin") {
|
||||||
|
sweixin = res[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//唤醒微信小程序
|
||||||
|
if (sweixin) {
|
||||||
|
uni.hideLoading();
|
||||||
|
sweixin.launchMiniProgram({
|
||||||
|
id: "wxc83296720404aa7b", // 小程序的原始ID,微信公众平台设置里有
|
||||||
|
type: 0, //小程序版本 0-正式版; 1-测试版; 2-体验版。
|
||||||
|
path: "Pages/index/index", //小程序的页面,用传的参数在小程序接值判断跳转指定页面
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
// 微信小程序端跳转其他小程序
|
||||||
|
uni.navigateToMiniProgram({
|
||||||
|
appId: "wxc83296720404aa7b",
|
||||||
|
path: "Pages/index/index",
|
||||||
|
extraData: {
|
||||||
|
// 可以传递额外数据给小程序
|
||||||
|
},
|
||||||
|
envVersion: "release",
|
||||||
|
success: (res) => {
|
||||||
|
console.log("跳转小程序成功", res);
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
console.log("跳转小程序失败", err);
|
||||||
|
uni.showToast({
|
||||||
|
title: "跳转失败",
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// #endif
|
||||||
|
};
|
||||||
|
/**解析文本 */
|
||||||
|
const textArr = parseText(props.msg?.text || "", props.msg?.serverExtension);
|
||||||
|
|
||||||
|
/**unilink 不支持鸿蒙 故提示在浏览器打开链接 */
|
||||||
|
const openInBrowser = (url: string) => {
|
||||||
|
uni.setClipboardData({
|
||||||
|
data: url,
|
||||||
|
showToast: false,
|
||||||
|
success: () => {
|
||||||
|
uni.showToast({
|
||||||
|
title: t("openUrlText"),
|
||||||
|
icon: "none",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.msg-custom {
|
||||||
|
color: #333;
|
||||||
|
display: flex;
|
||||||
|
text-align: left;
|
||||||
|
overflow-y: auto;
|
||||||
|
word-break: break-all;
|
||||||
|
word-wrap: break-word;
|
||||||
|
white-space: break-spaces;
|
||||||
|
}
|
||||||
|
.customCell {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.customCell.cloumn {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.customCell .title {
|
||||||
|
font-size: 30rpx;
|
||||||
|
color: #333;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2; /* 注意:这不是标准的CSS属性 */
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
||||||
|
.customCell .row {
|
||||||
|
margin-top: 30rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.left {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #999;
|
||||||
|
margin-right: 20rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2; /* 注意:这不是标准的CSS属性 */
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
||||||
|
.right image {
|
||||||
|
width: 100rpx;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
@ -78,9 +78,9 @@
|
|||||||
<div class="msg-input-button">
|
<div class="msg-input-button">
|
||||||
<Icon @tap="handleSendMoreVisible" type="send-more" :size="20" />
|
<Icon @tap="handleSendMoreVisible" type="send-more" :size="20" />
|
||||||
</div>
|
</div>
|
||||||
<div class="msg-input-button">
|
<!-- <div class="msg-input-button">
|
||||||
<Icon @tap="handleSetting" type="icon-shezhi" :size="20" />
|
<Icon @tap="handleSetting" type="icon-shezhi" :size="20" />
|
||||||
</div>
|
</div> -->
|
||||||
</div>
|
</div>
|
||||||
<div v-if="inputVisible" class="msg-input">
|
<div v-if="inputVisible" class="msg-input">
|
||||||
<!-- 当从表情面板切换到文字输入时,直接唤起键盘,会导致input框滚动消失,故此处需要用EmojiInput兼容下,保证先隐藏表情面板,再弹出键盘 -->
|
<!-- 当从表情面板切换到文字输入时,直接唤起键盘,会导致input框滚动消失,故此处需要用EmojiInput兼容下,保证先隐藏表情面板,再弹出键盘 -->
|
||||||
@ -157,8 +157,54 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="icon-text">{{ t('albumText') }}</div>
|
<div class="icon-text">{{ t('albumText') }}</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 音频呼叫 -->
|
<div class="send-more-panel-item-wrapper">
|
||||||
<div
|
<div
|
||||||
|
class="send-more-panel-item"
|
||||||
|
@tap="(event: any) => handleCustom('reply', event)"
|
||||||
|
>
|
||||||
|
<image :src="quickImg" mode="widthFix"></image>
|
||||||
|
</div>
|
||||||
|
<div class="icon-text">快捷回复</div>
|
||||||
|
</div>
|
||||||
|
<div class="send-more-panel-item-wrapper">
|
||||||
|
<div
|
||||||
|
class="send-more-panel-item"
|
||||||
|
@tap="(event: any) => handleCustom('hj', event)"
|
||||||
|
>
|
||||||
|
<image :src="quickImg" mode="widthFix"></image>
|
||||||
|
</div>
|
||||||
|
<div class="icon-text">患教</div>
|
||||||
|
</div>
|
||||||
|
<div class="send-more-panel-item-wrapper">
|
||||||
|
<div
|
||||||
|
class="send-more-panel-item"
|
||||||
|
@tap="(event: any) => handleCustom('outpatient', event)"
|
||||||
|
>
|
||||||
|
<image :src="chuzhenImg" mode="widthFix"></image>
|
||||||
|
</div>
|
||||||
|
<div class="icon-text">出/停诊公告</div>
|
||||||
|
</div>
|
||||||
|
<div class="send-more-panel-item-wrapper">
|
||||||
|
<div
|
||||||
|
class="send-more-panel-item"
|
||||||
|
@tap="(event: any) => handleCustom('mall', event)"
|
||||||
|
>
|
||||||
|
<image :src="mallImg" mode="widthFix"></image>
|
||||||
|
</div>
|
||||||
|
<div class="icon-text">商城</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- <div class="send-more-panel-item-wrapper">
|
||||||
|
<div
|
||||||
|
class="send-more-panel-item"
|
||||||
|
@tap="(event: any) => handleCustom('hospital', event)"
|
||||||
|
>
|
||||||
|
<image :src="hospitalImg" mode="widthFix"></image>
|
||||||
|
</div>
|
||||||
|
<div class="icon-text">互联网医院</div>
|
||||||
|
</div> -->
|
||||||
|
<!-- 音频呼叫 -->
|
||||||
|
<!-- <div
|
||||||
class="send-more-panel-item-wrapper"
|
class="send-more-panel-item-wrapper"
|
||||||
v-if="
|
v-if="
|
||||||
isAndroidOrIosApp &&
|
isAndroidOrIosApp &&
|
||||||
@ -170,10 +216,10 @@
|
|||||||
<Icon type="icon-audio-call" :size="30"></Icon>
|
<Icon type="icon-audio-call" :size="30"></Icon>
|
||||||
</div>
|
</div>
|
||||||
<div class="icon-text">{{ t('voiceCallText') }}</div>
|
<div class="icon-text">{{ t('voiceCallText') }}</div>
|
||||||
</div>
|
</div> -->
|
||||||
|
|
||||||
<!-- 视频呼叫 -->
|
<!-- 视频呼叫 -->
|
||||||
<div
|
<!-- <div
|
||||||
class="send-more-panel-item-wrapper"
|
class="send-more-panel-item-wrapper"
|
||||||
v-if="
|
v-if="
|
||||||
isAndroidOrIosApp &&
|
isAndroidOrIosApp &&
|
||||||
@ -185,8 +231,8 @@
|
|||||||
<Icon type="icon-video-call" :size="30"></Icon>
|
<Icon type="icon-video-call" :size="30"></Icon>
|
||||||
</div>
|
</div>
|
||||||
<div class="icon-text">{{ t('videoCallText') }}</div>
|
<div class="icon-text">{{ t('videoCallText') }}</div>
|
||||||
</div>
|
</div> -->
|
||||||
<div
|
<!-- <div
|
||||||
v-if="isWeb"
|
v-if="isWeb"
|
||||||
class="send-more-panel-item-wrapper"
|
class="send-more-panel-item-wrapper"
|
||||||
@tap="handleSendFileMsg"
|
@tap="handleSendFileMsg"
|
||||||
@ -195,7 +241,7 @@
|
|||||||
<Icon type="icon-file" :size="30"></Icon>
|
<Icon type="icon-file" :size="30"></Icon>
|
||||||
</div>
|
</div>
|
||||||
<div class="icon-text">{{ t('fileText') }}</div>
|
<div class="icon-text">{{ t('fileText') }}</div>
|
||||||
</div>
|
</div> -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- @消息相关 popup -->
|
<!-- @消息相关 popup -->
|
||||||
@ -208,12 +254,38 @@
|
|||||||
>
|
>
|
||||||
<MentionMemberList :team-id="to"></MentionMemberList>
|
<MentionMemberList :team-id="to"></MentionMemberList>
|
||||||
</UniPopup>
|
</UniPopup>
|
||||||
|
<!-- 复制自 groupSend 的遮罩与弹窗 -->
|
||||||
|
<view v-if="showModal" class="mask" @tap="closeModal"></view>
|
||||||
|
<view v-if="showModal" class="center-modal">
|
||||||
|
<view class="modal-title">温馨提示</view>
|
||||||
|
<view class="modal-divider"></view>
|
||||||
|
<view class="modal-item" @tap="onSelectSince('article')">
|
||||||
|
<text>图文科普</text>
|
||||||
|
</view>
|
||||||
|
<view class="modal-divider"></view>
|
||||||
|
<view class="modal-item" @tap="onSelectSince('video')">
|
||||||
|
<text>视频科普</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<!-- 商城弹窗 -->
|
||||||
|
<view v-if="showMallModal" class="mask" @tap="closeMallModal"></view>
|
||||||
|
<view v-if="showMallModal" class="center-modal">
|
||||||
|
<view class="modal-title">温馨提示</view>
|
||||||
|
<view class="modal-divider"></view>
|
||||||
|
<view class="modal-item" @tap="sendMallMsg">
|
||||||
|
<text>纽娃复合营养素固体饮料</text>
|
||||||
|
</view>
|
||||||
|
<view class="modal-divider"></view>
|
||||||
|
<view class="modal-item" >
|
||||||
|
<text>更多商品正在准备中</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
/** 消息页面输入框组件 */
|
/** 消息页面输入框组件 */
|
||||||
|
import docUrl from '@/utils/docUrl'
|
||||||
import Face from './face.vue'
|
import Face from './face.vue'
|
||||||
import VoicePanel from './voice-panel.vue'
|
import VoicePanel from './voice-panel.vue'
|
||||||
import Icon from '@/components/Icon.vue'
|
import Icon from '@/components/Icon.vue'
|
||||||
@ -242,6 +314,11 @@ import Appellation from '@/components/Appellation.vue'
|
|||||||
import { AT_ALL_ACCOUNT } from '@/utils/im/constants'
|
import { AT_ALL_ACCOUNT } from '@/utils/im/constants'
|
||||||
import { replaceEmoji } from '@/utils/im/index'
|
import { replaceEmoji } from '@/utils/im/index'
|
||||||
import { autorun } from 'mobx'
|
import { autorun } from 'mobx'
|
||||||
|
import quickImg from '@/static/quck_message.png'
|
||||||
|
import chuzhenImg from '@/static/outpatient_true.png'
|
||||||
|
import mallImg from '@/static/ytx_chattingfooter_shopping.png'
|
||||||
|
import hospitalImg from '@/static/ytx_chatting_hospital.png'
|
||||||
|
import {onShow,onUnload} from '@dcloudio/uni-app'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
V2NIMTeam,
|
V2NIMTeam,
|
||||||
@ -257,7 +334,30 @@ import {
|
|||||||
import { V2NIMConst } from '@/utils/im/nim'
|
import { V2NIMConst } from '@/utils/im/nim'
|
||||||
import { V2NIMMessage } from 'nim-web-sdk-ng/dist/esm/nim/src/V2NIMMessageService'
|
import { V2NIMMessage } from 'nim-web-sdk-ng/dist/esm/nim/src/V2NIMMessageService'
|
||||||
export type MentionedMember = { accountId: string; appellation: string }
|
export type MentionedMember = { accountId: string; appellation: string }
|
||||||
|
import navTo from '@/utils/navTo'
|
||||||
|
import api from '@/api/api.js'
|
||||||
|
let userInfo=uni.getStorageSync('userInfo');
|
||||||
|
let expert_uuid=userInfo.uuid;
|
||||||
|
let expert_name=userInfo.realName;
|
||||||
|
let patient_uuid='';
|
||||||
|
const articleInfo=ref({});
|
||||||
|
const videoInfo=ref({});
|
||||||
|
const patientListByGBK = async () => {
|
||||||
|
|
||||||
|
const res = await api.patientListByGBK();
|
||||||
|
if(res.code == 1){
|
||||||
|
//patientList.value = res.data;
|
||||||
|
for (let i = 0; i < res.data.length; i++) {
|
||||||
|
if (res.data[i].uuid.toLowerCase() === props.to) {
|
||||||
|
patient_uuid = res.data[i].uuid;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
conversationType: V2NIMConst.V2NIMConversationType
|
conversationType: V2NIMConst.V2NIMConversationType
|
||||||
@ -278,6 +378,10 @@ const conversationId =
|
|||||||
/** 输入框内容 */
|
/** 输入框内容 */
|
||||||
const inputText = ref('')
|
const inputText = ref('')
|
||||||
const extVisible = ref(false)
|
const extVisible = ref(false)
|
||||||
|
/** 群发选择弹窗 */
|
||||||
|
const showModal = ref(false)
|
||||||
|
/** 商城弹窗 */
|
||||||
|
const showMallModal = ref(false)
|
||||||
/** 音频面板flag */
|
/** 音频面板flag */
|
||||||
const audioPanelVisible = ref(false)
|
const audioPanelVisible = ref(false)
|
||||||
/** 发送更多面板flag */
|
/** 发送更多面板flag */
|
||||||
@ -314,6 +418,39 @@ const handleCall = (type: number) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
onShow(() => {
|
||||||
|
uni.$on('articelItem', (article) => {
|
||||||
|
console.log('article', article);
|
||||||
|
articleInfo.value = article;
|
||||||
|
senCustomMsg('article');
|
||||||
|
//handleSendTextMsg('article', article);
|
||||||
|
});
|
||||||
|
uni.$on('videoItem', (video) => {
|
||||||
|
videoInfo.value = video;
|
||||||
|
senCustomMsg('video');
|
||||||
|
//handleSendTextMsg('video', video);
|
||||||
|
});
|
||||||
|
uni.$on('quickReply', (reply) => {
|
||||||
|
inputText.value= reply;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
onUnload(() => {
|
||||||
|
uni.$off('articelItem');
|
||||||
|
uni.$off('videoItem');
|
||||||
|
uni.$off('quickReply');
|
||||||
|
});
|
||||||
|
/** 商城弹窗 */
|
||||||
|
let rawStr="{\"gdxz_content\":\"我已入驻肝胆相照互联网医院,复诊购药一站式服务,快来看看吧\",\"gdxz_ext_data\":\"[互联网医院]\",\"gdxz_id\":\"1681174885629431808\",\"gdxz_img\":\"https://img.applets.igandanyiyuan.com/applet/admin/avatar/2023071813261420200708181049.png\",\"gdxz_title\":\"肝胆相照互联网医院\",\"gdxz_type\":\"[互联网医院]\",\"gdxz_url\":\"\"}";
|
||||||
|
const sendMallMsg = () => {
|
||||||
|
showMallModal.value = false;
|
||||||
|
handleSendTextMsg('mall');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 商城弹窗 */
|
||||||
|
const closeMallModal = () => {
|
||||||
|
showMallModal.value = false
|
||||||
|
}
|
||||||
|
|
||||||
/** 用于解决表情面板和键盘冲突,导致输入框滚动消失问题 */
|
/** 用于解决表情面板和键盘冲突,导致输入框滚动消失问题 */
|
||||||
const showEmojiInput = ref(false)
|
const showEmojiInput = ref(false)
|
||||||
@ -326,6 +463,7 @@ const replyMsg = ref<V2NIMMessageForUI>()
|
|||||||
/** @ 消息相关 */
|
/** @ 消息相关 */
|
||||||
const ctx = getCurrentInstance()
|
const ctx = getCurrentInstance()
|
||||||
const popupRef = ref(null)
|
const popupRef = ref(null)
|
||||||
|
|
||||||
/** @ 成员列表 */
|
/** @ 成员列表 */
|
||||||
const selectedAtMembers = ref<MentionedMember[]>([])
|
const selectedAtMembers = ref<MentionedMember[]>([])
|
||||||
|
|
||||||
@ -381,6 +519,26 @@ const onPopupChange = (e: any) => {
|
|||||||
uni.$emit(events.HANDLE_MOVE_THROUGH, e.value)
|
uni.$emit(events.HANDLE_MOVE_THROUGH, e.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 打开/关闭群发选择弹窗(与 groupSend 保持一致) */
|
||||||
|
const openModal = () => {
|
||||||
|
showModal.value = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
const closeModal = () => {
|
||||||
|
showModal.value = false
|
||||||
|
}
|
||||||
|
const toggleModal = () => {
|
||||||
|
showModal.value = !showModal.value
|
||||||
|
}
|
||||||
|
const onSelectSince = (type: string) => {
|
||||||
|
showModal.value = false
|
||||||
|
if (type === 'article') {
|
||||||
|
navTo({ url: '/pages_chat/article/article' })
|
||||||
|
} else if (type === 'video') {
|
||||||
|
navTo({ url: '/pages_chat/video/video' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** 点击@ 群成员 */
|
/** 点击@ 群成员 */
|
||||||
const handleMentionItemClick = (member: MentionedMember) => {
|
const handleMentionItemClick = (member: MentionedMember) => {
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
@ -455,19 +613,21 @@ const handleInput = (event: any) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 发送文本消息 */
|
/** 发送文本消息 */
|
||||||
const handleSendTextMsg = () => {
|
const handleSendTextMsg = (type:string) => {
|
||||||
if (inputText.value.trim() === '') return
|
if (inputText.value.trim() === '') return
|
||||||
const ext = onAtMembersExtHandler()
|
const ext = onAtMembersExtHandler()
|
||||||
let text = replaceEmoji(inputText.value)
|
let mallText='纽娃复合营养素固体饮料主要成分是:蜂花粉、乳清蛋白粉、灰树花粉、低聚木糖、蚕蛹氨基酸、麦芽粉、薏苡仁粉、烟酸、磷脂,以及其他调味品、辅助原料。科学配比制成,含有丰富的蛋白质、氨基酸、维生素、微量元素及其他营养元素,点击链接了解详情。'
|
||||||
|
let text = type==='mall'?mallText:replaceEmoji(inputText.value)
|
||||||
const textMsg = uni.$UIKitNIM.V2NIMMessageCreator.createTextMessage(text)
|
const textMsg = uni.$UIKitNIM.V2NIMMessageCreator.createTextMessage(text)
|
||||||
|
let serverExtension={"gdxz_nickName":"测试","gdxz_sessionType":"general"};
|
||||||
uni.$UIKitStore.msgStore
|
uni.$UIKitStore.msgStore
|
||||||
.sendMessageActive({
|
.sendMessageActive({
|
||||||
msg: textMsg as unknown as V2NIMMessage,
|
msg: textMsg as unknown as V2NIMMessage,
|
||||||
conversationId,
|
conversationId,
|
||||||
serverExtension:selectedAtMembers.value.length && (ext as any),
|
serverExtension:selectedAtMembers.value.length && (ext as any),
|
||||||
sendBefore: () => {
|
sendBefore: () => {
|
||||||
scrollBottom()
|
scrollBottom();
|
||||||
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
@ -483,7 +643,10 @@ const handleSendTextMsg = () => {
|
|||||||
inputText.value = ''
|
inputText.value = ''
|
||||||
isReplyMsg.value = false
|
isReplyMsg.value = false
|
||||||
replyMsg.value = undefined
|
replyMsg.value = undefined
|
||||||
selectedAtMembers.value = []
|
selectedAtMembers.value = [];
|
||||||
|
if(type=='mall'){
|
||||||
|
senCustomMsg('mall');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 发送文件消息 */
|
/** 发送文件消息 */
|
||||||
@ -629,7 +792,75 @@ const handleSendImageMsg = () => {
|
|||||||
complete: handleNoPermission,
|
complete: handleNoPermission,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
const senCustomMsg = (type:string) => {
|
||||||
|
let rawStr="";
|
||||||
|
let text="";
|
||||||
|
if(type == 'hospital'){
|
||||||
|
text="[互联网医院]";
|
||||||
|
rawStr=`{\"gdxz_content\":\"我已入驻肝胆相照互联网医院,复诊购药一站式服务,快来看看吧\",\"gdxz_ext_data\":\"[互联网医院]\",\"gdxz_id\":\"1681174885629431808\",\"gdxz_img\":\"https://img.applets.igandanyiyuan.com/applet/admin/avatar/2023071813261420200708181049.png\",\"gdxz_title\":\"肝胆相照互联网医院\",\"gdxz_type\":\"[互联网医院]\",\"gdxz_url\":\"\"}`;
|
||||||
|
}else if(type == 'mall'){
|
||||||
|
text="[肝胆商城]";
|
||||||
|
rawStr=`{\"gdxz_content\":\"肝胆相照®肝胆病在线公共服务平台\",\"gdxz_ext_data\":\"[肝胆商城]\",\"gdxz_id\":\"\",\"gdxz_img\":\"\",\"gdxz_title\":\"纽娃复合营养素固体饮料\",\"gdxz_type\":\"[肝胆商城]\",\"gdxz_url\":\"https://wx.igandan.com/shop_notify/setInfo?patient_uuid=${patient_uuid}&expert_uuid=${expert_uuid}\"}`;
|
||||||
|
}else if(type == 'outpatient'){
|
||||||
|
text="[门诊公告]";
|
||||||
|
rawStr=`{\"gdxz_content\":\"门诊详情\",\"gdxz_ext_data\":\"[门诊公告]\",\"gdxz_id\":\"\",\"gdxz_img\":\"\",\"gdxz_title\":\"${expert_name}医生门诊详情\",\"gdxz_type\":\"[门诊公告]\",\"gdxz_url\":\"https://dev-wx.igandan.com/wxPatient/index.htm#/outPatient?link=share&expertUuid=${expert_uuid}\"}`
|
||||||
|
}else if(type == 'article'){
|
||||||
|
text="[图文科普]";
|
||||||
|
let content='"'+articleInfo.value.summary+'"';
|
||||||
|
let id='"'+articleInfo.value.uuid+'"';
|
||||||
|
let title='"'+articleInfo.value.title+'"';
|
||||||
|
let path='"'+docUrl+articleInfo.value.path+'"';
|
||||||
|
rawStr=`{\"gdxz_content\":${content},\"gdxz_ext_data\":\"[图文科普]\",\"gdxz_id\":${id},\"gdxz_img\":\"http://doc.igandan.org/app/book/pdf/2019/20190613152617.png\",\"gdxz_title\":${title},\"gdxz_type\":\"[图文科普]\",\"gdxz_url\":${path}}`
|
||||||
|
}else if(type == 'video'){
|
||||||
|
let content='"'+videoInfo.value.note+'"';
|
||||||
|
let id='"'+videoInfo.value.uuid+'"';
|
||||||
|
let title='"'+videoInfo.value.name+'"';
|
||||||
|
let path='"'+docUrl+videoInfo.value.path+'"';
|
||||||
|
text="[视频科普]";
|
||||||
|
rawStr=`{\"gdxz_content\":${content},\"gdxz_ext_data\":\"[视频科普]\",\"gdxz_id\":${id},\"gdxz_img\":\"http://doc.igandan.org/app/book/pdf/2019/20190613152617.png\",\"gdxz_title\":${title},\"gdxz_type\":\"[视频科普]\",\"gdxz_url\":${path}}`
|
||||||
|
}
|
||||||
|
const customMsg = uni.$UIKitNIM.V2NIMMessageCreator.createCustomMessage(text,rawStr)
|
||||||
|
uni.$UIKitStore.msgStore
|
||||||
|
.sendMessageActive({
|
||||||
|
msg: customMsg as unknown as V2NIMMessage,
|
||||||
|
conversationId,
|
||||||
|
progress: () => true,
|
||||||
|
sendBefore: () => {
|
||||||
|
scrollBottom()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
scrollBottom();
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
scrollBottom()
|
||||||
|
uni.showToast({
|
||||||
|
icon: 'error',
|
||||||
|
title: '发送失败',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCustom = (type: string, event: any) => {
|
||||||
|
if (isTeamMute.value) return
|
||||||
|
if(type == 'reply'){
|
||||||
|
navTo({
|
||||||
|
url: '/pages_chat/quickReply/quickReply',
|
||||||
|
})
|
||||||
|
}else if(type == 'hj'){
|
||||||
|
showModal.value = true
|
||||||
|
}else if(type == 'mall'){
|
||||||
|
showMallModal.value = true
|
||||||
|
patientListByGBK();
|
||||||
|
}else if(type == 'outpatient'){
|
||||||
|
senCustomMsg('outpatient');
|
||||||
|
// navTo({
|
||||||
|
// url: '/pages_chat/outpatient/outpatient',
|
||||||
|
// })
|
||||||
|
}else if(type == 'hospital'){
|
||||||
|
senCustomMsg('hospital');
|
||||||
|
}
|
||||||
|
}
|
||||||
/** 发送视频消息(使用相机或者从相册选择) */
|
/** 发送视频消息(使用相机或者从相册选择) */
|
||||||
const handleSendVideoMsg = (type: string, event: any) => {
|
const handleSendVideoMsg = (type: string, event: any) => {
|
||||||
if (isTeamMute.value) return
|
if (isTeamMute.value) return
|
||||||
@ -1073,12 +1304,18 @@ onUnmounted(() => {
|
|||||||
.send-more-panel-item {
|
.send-more-panel-item {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
width: 60px;
|
width: 100rpx;
|
||||||
height: 60px;
|
height: 100rpx;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
overflow: hidden;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin: 0 15px;
|
margin: 0 15px;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
image{
|
||||||
|
width: 100rpx;
|
||||||
|
height: 100rpx;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.icon-text {
|
.icon-text {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
@ -1178,4 +1415,47 @@ onUnmounted(() => {
|
|||||||
height: 60px;
|
height: 60px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 复制自 groupSend 的遮罩与弹窗样式 */
|
||||||
|
.mask {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.6);
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center-modal {
|
||||||
|
position: fixed;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: 650rpx;
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
z-index: 1001;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 10rpx 40rpx rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-title {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 34rpx;
|
||||||
|
color: #d32f2f;
|
||||||
|
padding: 28rpx 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-item {
|
||||||
|
padding: 36rpx 28rpx;
|
||||||
|
font-size: 30rpx;
|
||||||
|
color: #333333;
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-divider {
|
||||||
|
height: 2rpx;
|
||||||
|
background: #eeeeee;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<!-- {{ props.msg }} -->
|
||||||
<div
|
<div
|
||||||
:class="`msg-item-wrapper ${
|
:class="`msg-item-wrapper ${
|
||||||
props.msg.pinState &&
|
props.msg.pinState &&
|
||||||
@ -351,6 +352,42 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- <MessageIsRead v-if="props.msg?.isSelf" :msg="props.msg"></MessageIsRead> -->
|
<!-- <MessageIsRead v-if="props.msg?.isSelf" :msg="props.msg"></MessageIsRead> -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 自定义消息-->
|
||||||
|
<div
|
||||||
|
class="msg-common"
|
||||||
|
v-else-if="
|
||||||
|
props.msg.messageType ===
|
||||||
|
V2NIMConst.V2NIMMessageType.V2NIM_MESSAGE_TYPE_CUSTOM
|
||||||
|
"
|
||||||
|
:style="{ flexDirection: !props.msg.isSelf ? 'row' : 'row-reverse' }"
|
||||||
|
>
|
||||||
|
<Avatar
|
||||||
|
:account="props.msg.senderId"
|
||||||
|
:teamId="
|
||||||
|
conversationType ===
|
||||||
|
V2NIMConst.V2NIMConversationType.V2NIM_CONVERSATION_TYPE_TEAM
|
||||||
|
? to
|
||||||
|
: ''
|
||||||
|
"
|
||||||
|
:goto-user-card="true"
|
||||||
|
/>
|
||||||
|
<div class="msg-content">
|
||||||
|
<div class="msg-name" v-if="!props.msg.isSelf">
|
||||||
|
{{ appellation }}
|
||||||
|
</div>
|
||||||
|
<MessageBubble
|
||||||
|
:msg="props.msg"
|
||||||
|
:tooltip-visible="true"
|
||||||
|
:bg-visible="true"
|
||||||
|
>
|
||||||
|
<ReplyMessage v-if="!!replyMsg" :replyMsg="replyMsg"></ReplyMessage>
|
||||||
|
<!-- <MessageText :msg="props.msg"></MessageText> -->
|
||||||
|
<MessageCustom :msg="props.msg"></MessageCustom>
|
||||||
|
</MessageBubble>
|
||||||
|
</div>
|
||||||
|
<MessageIsRead v-if="props.msg?.isSelf" :msg="props.msg"></MessageIsRead>
|
||||||
|
</div>
|
||||||
<!-- 通知消息-->
|
<!-- 通知消息-->
|
||||||
<MessageNotification
|
<MessageNotification
|
||||||
v-else-if="
|
v-else-if="
|
||||||
@ -435,6 +472,7 @@ import MessageNotification from './message-notification.vue'
|
|||||||
import MessageG2 from './message-g2.vue'
|
import MessageG2 from './message-g2.vue'
|
||||||
import { customNavigateTo } from '@/utils/im/customNavigate'
|
import { customNavigateTo } from '@/utils/im/customNavigate'
|
||||||
import MessageIsRead from './message-read.vue'
|
import MessageIsRead from './message-read.vue'
|
||||||
|
import MessageCustom from './message-custom.vue'
|
||||||
import Icon from '@/components/Icon.vue'
|
import Icon from '@/components/Icon.vue'
|
||||||
import Appellation from '@/components/Appellation.vue'
|
import Appellation from '@/components/Appellation.vue'
|
||||||
import { events, MSG_ID_FLAG } from '@/utils/im/constants'
|
import { events, MSG_ID_FLAG } from '@/utils/im/constants'
|
||||||
|
|||||||
@ -1,67 +1,207 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="page">
|
<view class="page">
|
||||||
<!-- 顶部导航 -->
|
<!-- 顶部导航 -->
|
||||||
<view class="nav">
|
<navBar title="群发消息" />
|
||||||
<view class="back" @click="onBack">
|
|
||||||
<uni-icons type="left" color="#8B2316" size="24"></uni-icons>
|
|
||||||
</view>
|
|
||||||
<view class="title">群发消息</view>
|
|
||||||
<view class="right"></view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 时间展示 -->
|
|
||||||
<view class="time-row">{{ currentTime }}</view>
|
|
||||||
|
|
||||||
|
<!-- 下拉刷新和上拉加载容器 -->
|
||||||
|
<scroll-view
|
||||||
|
class="scroll-container"
|
||||||
|
scroll-y
|
||||||
|
refresher-enabled
|
||||||
|
:refresher-triggered="refreshing"
|
||||||
|
@refresherrefresh="onRefresh"
|
||||||
|
@scrolltolower="onLoadMore"
|
||||||
|
:lower-threshold="100"
|
||||||
|
>
|
||||||
<!-- 卡片区域 -->
|
<!-- 卡片区域 -->
|
||||||
|
<view class="card-cell" v-if="list.length > 0" v-for="(item, index) in list" :key="item.uuid">
|
||||||
|
<view class="time-row">{{ item.msg_send_date }}</view>
|
||||||
<view class="card" >
|
<view class="card" >
|
||||||
|
|
||||||
|
<view class="card-body">
|
||||||
|
|
||||||
<view class="card-header">
|
<view class="card-header">
|
||||||
<text class="label">{{ patientCount }}位患者:</text>
|
<text class="label">{{ item.realname.split(',').length }}位患者:</text>
|
||||||
<view class="close" @click="onClear">
|
<view class="close" @click="onClear">
|
||||||
<text>×</text>
|
<text>×</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="card-body">
|
<view class="line" >
|
||||||
<view class="line">{{ patientName }}</view>
|
{{ item.realname }}
|
||||||
<view class="line phone">{{ patientPhone }}</view>
|
</view>
|
||||||
|
<view class="line phone" >
|
||||||
|
{{ item.msg_content }}
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="card-footer">
|
<view class="card-footer">
|
||||||
<view class="btn-outline" @click="onResend">再发一条</view>
|
<view class="btn-outline" @click="onResend">再发一条</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 空状态 -->
|
||||||
|
<view class="card" v-else>
|
||||||
|
<view class="card-header">
|
||||||
|
<text class="label">群发消息记录</text>
|
||||||
|
</view>
|
||||||
|
<view class="card-body">
|
||||||
|
<view class="line">暂无群发消息记录</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 加载状态 -->
|
||||||
|
<view class="loading-state" v-if="loading">
|
||||||
|
<text>加载中...</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 没有更多数据 -->
|
||||||
|
<view class="no-more" v-if="!hasMore && list.length > 0">
|
||||||
|
<text>没有更多数据了</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
<!-- 占位滚动区域 -->
|
<!-- 占位滚动区域 -->
|
||||||
<view class="spacer" />
|
<view class="spacer" />
|
||||||
|
</scroll-view>
|
||||||
|
|
||||||
<!-- 底部按钮 -->
|
<!-- 底部按钮 -->
|
||||||
<view class="bottom-bar">
|
<view class="bottom-bar">
|
||||||
<view class="btn-primary" @click="onSendGroup">群发消息</view>
|
<view class="btn-primary" @click="openModal">群发消息</view>
|
||||||
|
</view>
|
||||||
|
<!-- 弹窗与遮罩 -->
|
||||||
|
<view v-if="showModal" class="mask" @click="closeModal"></view>
|
||||||
|
<view v-if="showModal" class="center-modal">
|
||||||
|
<view class="modal-title">温馨提示</view>
|
||||||
|
<view class="modal-divider"></view>
|
||||||
|
<view class="modal-item" @click="onSelect('single')">
|
||||||
|
<text>单独选择</text>
|
||||||
|
</view>
|
||||||
|
<view class="modal-divider"></view>
|
||||||
|
<view class="modal-item" @click="onSelect('group')">
|
||||||
|
<text>分组选择</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted } from 'vue'
|
||||||
|
import navBar from '@/components/navBar/navBar.vue'
|
||||||
|
import api from '@/api/api.js'
|
||||||
|
import { onShow } from '@dcloudio/uni-app'
|
||||||
const currentTime = ref('')
|
const currentTime = ref('')
|
||||||
const patientCount = ref(1)
|
const patientCount = ref(1)
|
||||||
const patientName = ref('测试')
|
const patientName = ref('测试')
|
||||||
const patientPhone = ref('155555555')
|
const patientPhone = ref('155555555')
|
||||||
|
const showModal=ref(false);
|
||||||
|
const page=ref(1);
|
||||||
|
const list=ref([]);
|
||||||
|
const refreshing=ref(false);
|
||||||
|
const loading=ref(false);
|
||||||
|
const hasMore=ref(true);
|
||||||
const pad = (n) => (n < 10 ? `0${n}` : `${n}`)
|
const pad = (n) => (n < 10 ? `0${n}` : `${n}`)
|
||||||
const updateTime = () => {
|
|
||||||
const d = new Date()
|
// 格式化时间显示
|
||||||
currentTime.value = `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`
|
const formatTime = (dateStr) => {
|
||||||
|
if (!dateStr) return ''
|
||||||
|
const date = new Date(dateStr)
|
||||||
|
const now = new Date()
|
||||||
|
const diff = now - date
|
||||||
|
|
||||||
|
// 如果是今天
|
||||||
|
if (diff < 24 * 60 * 60 * 1000 && date.getDate() === now.getDate()) {
|
||||||
|
return `${pad(date.getHours())}:${pad(date.getMinutes())}`
|
||||||
|
}
|
||||||
|
// 如果是昨天
|
||||||
|
else if (diff < 48 * 60 * 60 * 1000 && date.getDate() === now.getDate() - 1) {
|
||||||
|
return `昨天 ${pad(date.getHours())}:${pad(date.getMinutes())}`
|
||||||
|
}
|
||||||
|
// 其他情况显示完整日期
|
||||||
|
else {
|
||||||
|
return `${date.getMonth() + 1}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
// 加载数据
|
||||||
updateTime()
|
const listGroupSendMsg=async(isRefresh = false)=>{
|
||||||
// 每秒刷新一次时间(如不需要可删除)
|
if(loading.value) return;
|
||||||
setInterval(updateTime, 1000)
|
|
||||||
})
|
|
||||||
|
|
||||||
|
loading.value = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res=await api.listGroupSendMsg({
|
||||||
|
page: page.value,
|
||||||
|
});
|
||||||
|
|
||||||
|
if(res.code==200){
|
||||||
|
const newList = res.data.list || [];
|
||||||
|
|
||||||
|
if(isRefresh) {
|
||||||
|
// 下拉刷新,替换数据
|
||||||
|
list.value = newList;
|
||||||
|
} else {
|
||||||
|
// 上拉加载,追加数据
|
||||||
|
list.value = [...list.value, ...newList];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断是否还有更多数据
|
||||||
|
hasMore.value = newList.length >= 10; // 假设每页10条数据
|
||||||
|
|
||||||
|
// 如果不是刷新,页码+1
|
||||||
|
if(!isRefresh) {
|
||||||
|
page.value++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载数据失败:', error);
|
||||||
|
uni.showToast({ title: '加载失败', icon: 'none' });
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 下拉刷新
|
||||||
|
const onRefresh = async () => {
|
||||||
|
refreshing.value = true;
|
||||||
|
page.value = 1;
|
||||||
|
hasMore.value = true;
|
||||||
|
|
||||||
|
await listGroupSendMsg(true);
|
||||||
|
|
||||||
|
refreshing.value = false;
|
||||||
|
uni.showToast({ title: '刷新成功', icon: 'success' });
|
||||||
|
};
|
||||||
|
|
||||||
|
// 上拉加载更多
|
||||||
|
const onLoadMore = async () => {
|
||||||
|
if(!hasMore.value || loading.value) return;
|
||||||
|
|
||||||
|
await listGroupSendMsg(false);
|
||||||
|
};
|
||||||
|
onMounted(() => {
|
||||||
|
|
||||||
|
})
|
||||||
|
onShow(() => {
|
||||||
|
listGroupSendMsg()
|
||||||
|
})
|
||||||
const onBack = () => {
|
const onBack = () => {
|
||||||
uni.navigateBack()
|
uni.navigateBack()
|
||||||
}
|
}
|
||||||
|
const openModal = () => {
|
||||||
|
showModal.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeModal = () => {
|
||||||
|
showModal.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSelect = (type) => {
|
||||||
|
showModal.value = false;
|
||||||
|
if (type === 'single') {
|
||||||
|
uni.showToast({ title: '单独选择', icon: 'none' });
|
||||||
|
} else if (type === 'group') {
|
||||||
|
uni.showToast({ title: '分组选择', icon: 'none' });
|
||||||
|
}
|
||||||
|
};
|
||||||
const onClear = () => {
|
const onClear = () => {
|
||||||
// 清空选择的患者(占位)
|
// 清空选择的患者(占位)
|
||||||
patientCount.value = 0
|
patientCount.value = 0
|
||||||
@ -82,7 +222,48 @@ $brand: #8B2316;
|
|||||||
$brand-deep: #8B2316;
|
$brand-deep: #8B2316;
|
||||||
$primary: #00cbc0;
|
$primary: #00cbc0;
|
||||||
$red: #D32F2F;
|
$red: #D32F2F;
|
||||||
|
/* 遮罩与弹窗 */
|
||||||
|
.mask {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0,0,0,0.6);
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center-modal {
|
||||||
|
position: fixed;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: 650rpx;
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
z-index: 11;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 10rpx 40rpx rgba(0,0,0,0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-title {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 34rpx;
|
||||||
|
color: #8B2316;
|
||||||
|
padding: 28rpx 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-item {
|
||||||
|
padding: 36rpx 28rpx;
|
||||||
|
font-size: 30rpx;
|
||||||
|
color: #333333;
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-divider {
|
||||||
|
height: 2rpx;
|
||||||
|
background: #eeeeee;
|
||||||
|
}
|
||||||
.page {
|
.page {
|
||||||
background: $page-bg;
|
background: $page-bg;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
@ -101,16 +282,45 @@ $red: #D32F2F;
|
|||||||
.right { width: 80rpx; }
|
.right { width: 80rpx; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 滚动容器样式
|
||||||
|
.scroll-container {
|
||||||
|
position: fixed;
|
||||||
|
top: 140rpx;
|
||||||
|
|
||||||
|
width:100%;
|
||||||
|
bottom: 200rpx;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
.time-row {
|
.time-row {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 32rpx;
|
font-size: 32rpx;
|
||||||
padding: 32rpx 0;
|
padding: 32rpx 0;
|
||||||
color: #333;
|
color: #333;
|
||||||
background: #fff;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 加载状态样式
|
||||||
|
.loading-state {
|
||||||
|
text-align: center;
|
||||||
|
padding: 40rpx 0;
|
||||||
|
color: #999;
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-more {
|
||||||
|
text-align: center;
|
||||||
|
padding: 40rpx 0;
|
||||||
|
color: #ccc;
|
||||||
|
font-size: 24rpx;
|
||||||
|
}
|
||||||
|
.card-cell{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin: 0 30rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
.card {
|
.card {
|
||||||
margin: 24rpx;
|
margin: 24rpx 0;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-radius: 16rpx;
|
border-radius: 16rpx;
|
||||||
box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.06);
|
box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.06);
|
||||||
@ -122,8 +332,8 @@ $red: #D32F2F;
|
|||||||
.label { font-size: 32rpx; color: #333; }
|
.label { font-size: 32rpx; color: #333; }
|
||||||
.close {
|
.close {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: -15rpx;
|
||||||
top: 0;
|
top: -18rpx;
|
||||||
width: 100rpx;
|
width: 100rpx;
|
||||||
height: 100rpx;
|
height: 100rpx;
|
||||||
border-bottom-left-radius: 100rpx;
|
border-bottom-left-radius: 100rpx;
|
||||||
|
|||||||
347
pages_chat/groupSend/groupSend.vue
Normal file
347
pages_chat/groupSend/groupSend.vue
Normal file
@ -0,0 +1,347 @@
|
|||||||
|
<template>
|
||||||
|
<view class="page">
|
||||||
|
<!-- 顶部导航 -->
|
||||||
|
<uni-nav-bar
|
||||||
|
left-icon="left"
|
||||||
|
title="群发消息"
|
||||||
|
@clickLeft="goBack"
|
||||||
|
fixed
|
||||||
|
color="#8B2316"
|
||||||
|
height="140rpx"
|
||||||
|
:border="false"
|
||||||
|
backgroundColor="#eee"
|
||||||
|
>
|
||||||
|
<template #right>
|
||||||
|
<view class="nav-right">
|
||||||
|
<uni-icons type="plus" size="24" color="#8B2316" @click="toggleModal"></uni-icons>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
</uni-nav-bar>
|
||||||
|
|
||||||
|
<!-- 警告提示区域 -->
|
||||||
|
<view class="warning-section">
|
||||||
|
<view class="warning-icon">🔔</view>
|
||||||
|
<text class="warning-text"
|
||||||
|
>若群发消息选择患者过多(建议少于200人), 部分患者可能将延迟收到消息</text
|
||||||
|
>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 消息预览卡片 -->
|
||||||
|
<view class="message-preview-card" v-if="selectedPatients.length > 0">
|
||||||
|
<view class="card-header">
|
||||||
|
<text class="label"
|
||||||
|
>消息将发送给{{ selectedPatients.length }}位患者:</text
|
||||||
|
>
|
||||||
|
<view class="close" @click="onClear">
|
||||||
|
<text>×</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="card-body">
|
||||||
|
<view class="patient-list">
|
||||||
|
<text
|
||||||
|
class="patient-name"
|
||||||
|
v-for="(patient, index) in selectedPatients"
|
||||||
|
:key="patient.uuid"
|
||||||
|
>
|
||||||
|
{{ patient.nickname || patient.realName
|
||||||
|
}}{{ index < selectedPatients.length - 1 ? "、" : "" }}
|
||||||
|
</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 空状态区域 -->
|
||||||
|
<view class="empty-area" v-else>
|
||||||
|
<view class="empty-content">
|
||||||
|
<text class="empty-text">请选择要发送消息的患者</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 底部按钮 -->
|
||||||
|
<view class="bottom-bar">
|
||||||
|
<view style="height: 'auto'">
|
||||||
|
<!-- <MessageInput
|
||||||
|
:reply-msgs-map="replyMsgsMap"
|
||||||
|
:conversation-type="conversationType"
|
||||||
|
:to="to"
|
||||||
|
/> -->
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 弹窗与遮罩 -->
|
||||||
|
<view v-if="showModal" class="mask" @click="closeModal"></view>
|
||||||
|
<view v-if="showModal" class="center-modal">
|
||||||
|
<view class="modal-title">温馨提示</view>
|
||||||
|
<view class="modal-divider"></view>
|
||||||
|
<view class="modal-item" @click="onSelect('single')">
|
||||||
|
<text>单独选择</text>
|
||||||
|
</view>
|
||||||
|
<view class="modal-divider"></view>
|
||||||
|
<view class="modal-item" @click="onSelect('group')">
|
||||||
|
<text>分组选择</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted } from "vue";
|
||||||
|
import navBar from "@/components/navBar/navBar.vue";
|
||||||
|
import api from "@/api/api.js";
|
||||||
|
import { onShow,onLoad,onUnload } from "@dcloudio/uni-app";
|
||||||
|
import navTo from "@/utils/navTo.js";
|
||||||
|
const showModal = ref(false);
|
||||||
|
const selectedPatients = ref([]);
|
||||||
|
const replyMsgsMap = ref({});
|
||||||
|
const conversationType = ref("1");
|
||||||
|
const msg_content = ref("");
|
||||||
|
const msg_type = ref("1");
|
||||||
|
const to = ref("");
|
||||||
|
const toggleModal = () => {
|
||||||
|
showModal.value = !showModal.value;
|
||||||
|
}
|
||||||
|
const goBack = () => {
|
||||||
|
uni.navigateBack({
|
||||||
|
delta: 1,
|
||||||
|
fail: (err) => {
|
||||||
|
navTo({
|
||||||
|
url: "/pages/index/index"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const addGroupSendMsg4YunXin=async()=>{
|
||||||
|
const res=await api.addGroupSendMsg4YunXin({
|
||||||
|
patient_user_uuid: selectedPatients.value.map(item => item.uuid).join(','),
|
||||||
|
msg_content: msg_content.value,
|
||||||
|
msg_type: msg_type.value,
|
||||||
|
});
|
||||||
|
if(res.code==200){
|
||||||
|
uni.showToast({ title: "发送成功", icon: "none" });
|
||||||
|
navTo({
|
||||||
|
url: "/pages_chat/groupMessage/groupMessage"
|
||||||
|
})
|
||||||
|
}else{
|
||||||
|
uni.showToast({ title: res.msg, icon: "none" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
// 初始化数据
|
||||||
|
});
|
||||||
|
|
||||||
|
onShow(() => {
|
||||||
|
try {
|
||||||
|
uni.$on('selectedChatPatients',(data)=>{
|
||||||
|
console.log(data)
|
||||||
|
selectedPatients.value = data.patients;
|
||||||
|
});
|
||||||
|
uni.$on('selectedChatPatientsSingle',(data)=>{
|
||||||
|
console.log(data)
|
||||||
|
selectedPatients.value = data.patients;
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error('接收已选患者失败', e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
onUnload(() => {
|
||||||
|
uni.$off('selectedChatPatients');
|
||||||
|
uni.$off('selectedChatPatientsSingle');
|
||||||
|
});
|
||||||
|
const openModal = () => {
|
||||||
|
showModal.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeModal = () => {
|
||||||
|
showModal.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSelect = (type) => {
|
||||||
|
showModal.value = false;
|
||||||
|
if (type === "single") {
|
||||||
|
navTo({
|
||||||
|
url: "/pages_app/selectPatient/selectPatient"
|
||||||
|
})
|
||||||
|
// 这里可以跳转到患者选择页面
|
||||||
|
} else if (type === "group") {
|
||||||
|
navTo({
|
||||||
|
url: "/pages_chat/patientGroup/patientGroup"
|
||||||
|
})
|
||||||
|
// 这里可以跳转到分组选择页面
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onClear = () => {
|
||||||
|
selectedPatients.value = [];
|
||||||
|
uni.showToast({ title: "已清空选择", icon: "success" });
|
||||||
|
try { uni.removeStorageSync('GROUP_SEND_SELECTED_PATIENTS'); } catch (e) {}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
$page-bg: #f5f5f5;
|
||||||
|
$brand: #8b2316;
|
||||||
|
$brand-deep: #8b2316;
|
||||||
|
$primary: #00cbc0;
|
||||||
|
$red: #d32f2f;
|
||||||
|
|
||||||
|
.page {
|
||||||
|
background: $page-bg;
|
||||||
|
min-height: 100vh;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 警告提示区域
|
||||||
|
.warning-section {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
padding: 24rpx 30rpx;
|
||||||
|
background: #fff8e1;
|
||||||
|
border-left: 6rpx solid #ff9800;
|
||||||
|
margin: 20rpx 30rpx;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
|
||||||
|
.warning-icon {
|
||||||
|
font-size: 28rpx;
|
||||||
|
margin-right: 16rpx;
|
||||||
|
margin-top: 4rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning-text {
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #e65100;
|
||||||
|
line-height: 1.5;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 消息预览卡片
|
||||||
|
.message-preview-card {
|
||||||
|
margin: 30rpx;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
|
||||||
|
overflow: hidden;
|
||||||
|
border-top: 6rpx solid $red;
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
position: relative;
|
||||||
|
padding: 28rpx 28rpx 12rpx 28rpx;
|
||||||
|
|
||||||
|
.label {
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close {
|
||||||
|
position: absolute;
|
||||||
|
right: -15rpx;
|
||||||
|
top: -18rpx;
|
||||||
|
width: 100rpx;
|
||||||
|
height: 100rpx;
|
||||||
|
border-bottom-left-radius: 100rpx;
|
||||||
|
background: $red;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
text {
|
||||||
|
color: #fff;
|
||||||
|
font-size: 40rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-body {
|
||||||
|
padding: 20rpx 28rpx 28rpx 28rpx;
|
||||||
|
|
||||||
|
.patient-list {
|
||||||
|
.patient-name {
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: #333;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 空状态区域
|
||||||
|
.empty-area {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 100rpx 30rpx;
|
||||||
|
|
||||||
|
.empty-content {
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
.empty-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 底部按钮
|
||||||
|
.bottom-bar {
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: $primary;
|
||||||
|
height: 100rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
color: #fff;
|
||||||
|
font-size: 34rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 遮罩与弹窗
|
||||||
|
.mask {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.6);
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center-modal {
|
||||||
|
position: fixed;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: 650rpx;
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
z-index: 11;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 10rpx 40rpx rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-title {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 34rpx;
|
||||||
|
color: $red;
|
||||||
|
padding: 28rpx 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-item {
|
||||||
|
padding: 36rpx 28rpx;
|
||||||
|
font-size: 30rpx;
|
||||||
|
color: #333333;
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-divider {
|
||||||
|
height: 2rpx;
|
||||||
|
background: #eeeeee;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
714
pages_chat/patientGroup/patientGroup.vue
Normal file
714
pages_chat/patientGroup/patientGroup.vue
Normal file
@ -0,0 +1,714 @@
|
|||||||
|
<template>
|
||||||
|
<view class="patient-group-page">
|
||||||
|
|
||||||
|
<!-- 头部 -->
|
||||||
|
<uni-nav-bar
|
||||||
|
left-icon="left"
|
||||||
|
title="患者分组"
|
||||||
|
@clickLeft="goBack"
|
||||||
|
fixed
|
||||||
|
color="#8B2316"
|
||||||
|
height="140rpx"
|
||||||
|
:border="false"
|
||||||
|
backgroundColor="#eeeeee"
|
||||||
|
>
|
||||||
|
<template #right>
|
||||||
|
<view class="save-text" @click="confirmGroup">确定({{ totalSelectedCount }})</view>
|
||||||
|
</template>
|
||||||
|
</uni-nav-bar>
|
||||||
|
<!-- 筛选排序栏 -->
|
||||||
|
<view class="filter-sort-bar">
|
||||||
|
<view class="sort-section" @click="toggleGroupSort">
|
||||||
|
<text class="sort-label">{{ groupSortTitle }}</text>
|
||||||
|
<view class="imgbox">
|
||||||
|
<up-image :src="upImg" width="26rpx" height="26rpx" ></up-image>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="divider"></view>
|
||||||
|
<view class="current-sort" @click="toggleInnerSort">
|
||||||
|
<text class="sort-text">{{ innerSortTitle }}</text>
|
||||||
|
<view class="imgbox">
|
||||||
|
<up-image :src="upImg" width="26rpx" height="26rpx" ></up-image>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 分组排序弹窗 -->
|
||||||
|
<view v-if="showGroupSort" class="popup-panel">
|
||||||
|
<view class="popup-item" :class="{ active: group_sort==0 }" @click.stop="chooseGroupSort('letter')">
|
||||||
|
<text class="item-text">按首字母</text>
|
||||||
|
<uni-icons v-if="selectedGroupSort==='letter'" type="checkmarkempty" color="#8B2316" size="22"></uni-icons>
|
||||||
|
</view>
|
||||||
|
<view class="popup-divider"></view>
|
||||||
|
<view class="popup-item" :class="{ active:group_sort==1}" @click.stop="chooseGroupSort('count')">
|
||||||
|
<text class="item-text">分组人数</text>
|
||||||
|
<uni-icons v-if="selectedGroupSort==='count'" type="checkmarkempty" color="#8B2316" size="22"></uni-icons>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view v-if="showGroupSort" class="popup-mask" @click="closeGroupSort"></view>
|
||||||
|
|
||||||
|
<!-- 组内排序弹窗 -->
|
||||||
|
<view v-if="showInnerSort" class="popup-panel">
|
||||||
|
<view class="popup-item" :class="{ active:list_sort==0 }" @click.stop="chooseInnerSort('letter')">
|
||||||
|
<text class="item-text">按首字母</text>
|
||||||
|
<uni-icons v-if="selectedInnerSort==='letter'" type="checkmarkempty" color="#8B2316" size="22"></uni-icons>
|
||||||
|
</view>
|
||||||
|
<view class="popup-divider"></view>
|
||||||
|
<view class="popup-item" :class="{ active:list_sort==1 }" @click.stop="chooseInnerSort('count')">
|
||||||
|
<text class="item-text">随访时间</text>
|
||||||
|
<uni-icons v-if="selectedInnerSort==='count'" type="checkmarkempty" color="#8B2316" size="22"></uni-icons>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view v-if="showInnerSort" class="popup-mask" @click="closeInnerSort"></view>
|
||||||
|
|
||||||
|
<!-- 患者列表 -->
|
||||||
|
<scroll-view class="patient-list-section" scroll-y="true" :style="{ height: scrollViewHeight }">
|
||||||
|
<!-- 分组循环渲染 -->
|
||||||
|
<view class="groupcell" v-for="(group, gi) in groups" :key="group.uuid || gi">
|
||||||
|
<view class="section-title" @click="toggleGroup(gi)">
|
||||||
|
<view class="left">
|
||||||
|
<view class="imgbox" :class="{ 'active':openGroups[gi] }">
|
||||||
|
<up-image v-if="!openGroups[gi]" :src="groupRightImg" width="19rpx" height="32rpx" ></up-image>
|
||||||
|
<up-image v-else :src="groupDownImg" width="32rpx" height="19rpx" ></up-image>
|
||||||
|
</view>
|
||||||
|
<view class="title">{{ group.name || '未命名分组' }} | {{ group.patientNum || (group.patientList ? group.patientList.length : 0) }}</view>
|
||||||
|
</view>
|
||||||
|
<view class="right-checkbox" @click.stop="toggleGroupSelection(gi)" v-if="group.patientList.length > 0">
|
||||||
|
<view class="checkbox" :class="{ 'checked': isGroupSelected(gi) }">
|
||||||
|
<uni-icons v-if="isGroupSelected(gi)" type="checkmarkempty" color="#8B2316" size="16"></uni-icons>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="patient-list" v-if="openGroups[gi]">
|
||||||
|
<view class="patient-item" v-for="(patient, index) in (group.patientList || [])" :key="patient.uuid || index">
|
||||||
|
|
||||||
|
<view class="patient-avatar">
|
||||||
|
<up-image :src="docUrl + patient.photo" width="80rpx" height="80rpx" mode="aspectFill"></up-image>
|
||||||
|
</view>
|
||||||
|
<view class="patient-checkbox" @click.stop="togglePatientSelection(gi, patient, index)">
|
||||||
|
<view class="checkbox" :class="{ 'checked': isPatientSelected(gi, patient, index) }">
|
||||||
|
<uni-icons v-if="isPatientSelected(gi, patient, index)" type="checkmarkempty" color="#8B2316" size="16"></uni-icons>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
<!-- 底部操作栏 -->
|
||||||
|
<view class="bottom-bar">
|
||||||
|
<view class="clear-btn" @click="clearSelection">清空选择</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed, onMounted } from 'vue';
|
||||||
|
import { onShow,onLoad } from "@dcloudio/uni-app";
|
||||||
|
import upImg from "@/static/triangle_green_theme.png"
|
||||||
|
import downImg from "@/static/triangle_normal.png"
|
||||||
|
import groupRightImg from "@/static/groupright_big.png"
|
||||||
|
import groupDownImg from "@/static/groupup_big.png"
|
||||||
|
import api from '@/api/api.js';
|
||||||
|
import docUrl from '@/utils/docUrl'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
import navTo from '@/utils/navTo.js'
|
||||||
|
const list_sort = ref(0);
|
||||||
|
const group_sort = ref(0);
|
||||||
|
|
||||||
|
// 分组数据与展开状态
|
||||||
|
const groups = ref([]);
|
||||||
|
const openGroups = ref({});
|
||||||
|
// 选中的分组状态
|
||||||
|
const selectedGroups = ref({});
|
||||||
|
// 选中的患者状态:以分组索引为key,值为Set或索引Map
|
||||||
|
const selectedPatients = ref({});
|
||||||
|
|
||||||
|
const formatYMD = (val) => {
|
||||||
|
if (!val) return '';
|
||||||
|
const d = dayjs(val);
|
||||||
|
return d.isValid() ? d.format('YYYY-MM-DD') : '';
|
||||||
|
};
|
||||||
|
|
||||||
|
// 排序栏标题
|
||||||
|
const groupSortTitle = computed(() => group_sort.value === 0 ? '按首字母' : '分组人数');
|
||||||
|
const innerSortTitle = computed(() => list_sort.value === 0 ? '按首字母' : '随访时间');
|
||||||
|
|
||||||
|
// 计算选中的分组数量
|
||||||
|
const selectedGroupCount = computed(() => {
|
||||||
|
return Object.values(selectedGroups.value).filter(Boolean).length;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 计算选中的患者数量(去重)
|
||||||
|
const getPatientUniqueId = (p) => {
|
||||||
|
return (p && (p.uuid || p.patientUuid || p.id || p.userId)) || '';
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectedPatientCount = computed(() => {
|
||||||
|
const idSet = new Set();
|
||||||
|
groups.value.forEach((group, gi) => {
|
||||||
|
const map = selectedPatients.value[gi] || {};
|
||||||
|
(group.patientList || []).forEach((patient, pi) => {
|
||||||
|
if (map[pi]) {
|
||||||
|
const uid = getPatientUniqueId(patient);
|
||||||
|
if (uid) idSet.add(uid);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return idSet.size;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 合计数量:只计算去重后的患者数量
|
||||||
|
const totalSelectedCount = computed(() => selectedPatientCount.value);
|
||||||
|
|
||||||
|
onShow(() => {
|
||||||
|
fetchGroupList();
|
||||||
|
});
|
||||||
|
|
||||||
|
const fetchGroupList = async () => {
|
||||||
|
const res = await api.groupList({
|
||||||
|
list_sort:list_sort.value,
|
||||||
|
page:1,
|
||||||
|
pageSize:10,
|
||||||
|
group_sort:group_sort.value
|
||||||
|
});
|
||||||
|
if(res.code === 200){
|
||||||
|
groups.value = Array.isArray(res.data) ? res.data : [];
|
||||||
|
// 初始化展开状态:默认收起
|
||||||
|
openGroups.value = {};
|
||||||
|
groups.value.forEach((group, idx) => {
|
||||||
|
openGroups.value[idx] = false;
|
||||||
|
// 初始化患者选择Map,避免未定义
|
||||||
|
if (!selectedPatients.value[idx]) selectedPatients.value[idx] = {};
|
||||||
|
// 若组选中但患者为空,则保持组选中;否则按患者是否全选同步组选中状态
|
||||||
|
updateGroupSelectionByPatients(idx);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleGroup = (idx) => {
|
||||||
|
openGroups.value[idx] = !openGroups.value[idx];
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleGroupSelection = (idx) => {
|
||||||
|
const group = groups.value[idx] || {};
|
||||||
|
const patients = group.patientList || [];
|
||||||
|
const willSelect = !isGroupSelected(idx);
|
||||||
|
// 设置组选中状态
|
||||||
|
selectedGroups.value[idx] = willSelect;
|
||||||
|
// 同步该组下的患者选中状态
|
||||||
|
if (!selectedPatients.value[idx]) selectedPatients.value[idx] = {};
|
||||||
|
patients.forEach((_, pi) => {
|
||||||
|
selectedPatients.value[idx][pi] = willSelect;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const isPatientSelected = (gi, patient, index) => {
|
||||||
|
const groupSelectedMap = selectedPatients.value[gi] || {};
|
||||||
|
return !!groupSelectedMap[index];
|
||||||
|
};
|
||||||
|
|
||||||
|
const togglePatientSelection = (gi, patient, index) => {
|
||||||
|
if (!selectedPatients.value[gi]) selectedPatients.value[gi] = {};
|
||||||
|
selectedPatients.value[gi][index] = !selectedPatients.value[gi][index];
|
||||||
|
// 若组内所有患者都被勾选,则自动勾选组;否则取消组勾选
|
||||||
|
updateGroupSelectionByPatients(gi);
|
||||||
|
};
|
||||||
|
|
||||||
|
const isGroupSelected = (gi) => {
|
||||||
|
// 优先:若显式设置了组选中状态,且为true,直接认为选中
|
||||||
|
if (selectedGroups.value[gi]) return true;
|
||||||
|
// 否则根据患者是否全选判断
|
||||||
|
const group = groups.value[gi] || {};
|
||||||
|
const patients = group.patientList || [];
|
||||||
|
if (patients.length === 0) return !!selectedGroups.value[gi];
|
||||||
|
const map = selectedPatients.value[gi] || {};
|
||||||
|
return patients.every((_, pi) => !!map[pi]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateGroupSelectionByPatients = (gi) => {
|
||||||
|
const group = groups.value[gi] || {};
|
||||||
|
const patients = group.patientList || [];
|
||||||
|
const map = selectedPatients.value[gi] || {};
|
||||||
|
if (patients.length === 0) return;
|
||||||
|
selectedGroups.value[gi] = patients.every((_, pi) => !!map[pi]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const editGroup = (group) => {
|
||||||
|
navTo({
|
||||||
|
url: `/pages_app/groupEdit/groupEdit?uuid=${group.uuid}`
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
// 分组排序弹窗状态
|
||||||
|
const showGroupSort = ref(false);
|
||||||
|
const selectedGroupSort = ref('letter'); // letter | count
|
||||||
|
|
||||||
|
// 组内排序弹窗状态
|
||||||
|
const showInnerSort = ref(false);
|
||||||
|
const selectedInnerSort = ref('letter');
|
||||||
|
|
||||||
|
// 待分组展开/收起
|
||||||
|
const showPending = ref(false);
|
||||||
|
|
||||||
|
const toggleGroupSort = () => {
|
||||||
|
showGroupSort.value = !showGroupSort.value;
|
||||||
|
if (showGroupSort.value) showInnerSort.value = false;
|
||||||
|
};
|
||||||
|
const closeGroupSort = () => {
|
||||||
|
showGroupSort.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const chooseGroupSort = (type) => {
|
||||||
|
selectedGroupSort.value = type;
|
||||||
|
// 修改接口字段:按首字母=0,分组人数=1
|
||||||
|
group_sort.value = type === 'letter' ? 0 : 1;
|
||||||
|
closeGroupSort();
|
||||||
|
fetchGroupList();
|
||||||
|
};
|
||||||
|
const confirmGroup = () => {
|
||||||
|
// 收集已选患者,并做去重
|
||||||
|
const idSet = new Set();
|
||||||
|
const dedupPatients = [];
|
||||||
|
groups.value.forEach((group, gi) => {
|
||||||
|
const map = selectedPatients.value[gi] || {};
|
||||||
|
(group.patientList || []).forEach((patient, pi) => {
|
||||||
|
if (map[pi]) {
|
||||||
|
const uid = getPatientUniqueId(patient);
|
||||||
|
if (uid && !idSet.has(uid)) {
|
||||||
|
idSet.add(uid);
|
||||||
|
dedupPatients.push(patient);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (dedupPatients.length === 0) {
|
||||||
|
uni.showToast({ title: '请至少选择1个患者', icon: 'none' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages_chat/groupSend/groupSend',
|
||||||
|
success: (res) => {
|
||||||
|
try {
|
||||||
|
uni.$emit('selectedChatPatients', { patients: dedupPatients });
|
||||||
|
} catch (emitErr) {
|
||||||
|
console.error('传递已选患者失败', emitErr);
|
||||||
|
uni.showToast({ title: '传递失败,请重试', icon: 'none' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error('跳转失败', e);
|
||||||
|
uni.showToast({ title: '跳转失败,请重试', icon: 'none' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const toggleInnerSort = () => {
|
||||||
|
showInnerSort.value = !showInnerSort.value;
|
||||||
|
if (showInnerSort.value) showGroupSort.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeInnerSort = () => {
|
||||||
|
showInnerSort.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const chooseInnerSort = (type) => {
|
||||||
|
selectedInnerSort.value = type;
|
||||||
|
// 组内排序:按首字母=0,随访时间=1
|
||||||
|
list_sort.value = type === 'letter' ? 0 : 1;
|
||||||
|
closeInnerSort();
|
||||||
|
fetchGroupList();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 方法
|
||||||
|
const goBack = () => {
|
||||||
|
uni.navigateBack();
|
||||||
|
};
|
||||||
|
|
||||||
|
const createNew = () => {
|
||||||
|
uni.showToast({
|
||||||
|
title: '跳转到新建分组页面',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
// 这里可以跳转到新建分组页面
|
||||||
|
};
|
||||||
|
|
||||||
|
// 清空选择
|
||||||
|
const clearSelection = () => {
|
||||||
|
selectedGroups.value = {};
|
||||||
|
selectedPatients.value = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.patient-group-page {
|
||||||
|
min-height: 100vh;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 弹窗样式 */
|
||||||
|
.popup-mask {
|
||||||
|
position: fixed;
|
||||||
|
top: 220rpx; /* 位于筛选栏下方 */
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0,0,0,0.3);
|
||||||
|
z-index: 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-panel {
|
||||||
|
position: fixed;
|
||||||
|
top: 220rpx; /* 紧贴筛选栏 */
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background: #fff;
|
||||||
|
z-index: 10;
|
||||||
|
box-shadow: 0 6rpx 20rpx rgba(0,0,0,0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 28rpx 30rpx;
|
||||||
|
font-size: 30rpx;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-divider {
|
||||||
|
height: 2rpx;
|
||||||
|
background: #eaeaea;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-item.active .item-text {
|
||||||
|
color: #8B2316;
|
||||||
|
}
|
||||||
|
.save-text{
|
||||||
|
margin-top: -20rpx;
|
||||||
|
color:#8B2316;
|
||||||
|
font-size: 30rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
}
|
||||||
|
.status-bar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10rpx 30rpx;
|
||||||
|
background-color: #ffffff;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #333;
|
||||||
|
|
||||||
|
.status-left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 20rpx;
|
||||||
|
|
||||||
|
.time {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-icons {
|
||||||
|
display: flex;
|
||||||
|
gap: 10rpx;
|
||||||
|
|
||||||
|
.app-icon {
|
||||||
|
width: 24rpx;
|
||||||
|
height: 24rpx;
|
||||||
|
border-radius: 4rpx;
|
||||||
|
|
||||||
|
&.weibo {
|
||||||
|
background-color: #ff8200;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.taobao {
|
||||||
|
background-color: #ff6a00;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.xiaohongshu {
|
||||||
|
background-color: #ff2442;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 15rpx;
|
||||||
|
|
||||||
|
.network {
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.signal-icon {
|
||||||
|
width: 32rpx;
|
||||||
|
height: 20rpx;
|
||||||
|
background-color: #333;
|
||||||
|
border-radius: 2rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.battery-text {
|
||||||
|
font-size: 20rpx;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.battery {
|
||||||
|
width: 40rpx;
|
||||||
|
height: 20rpx;
|
||||||
|
border: 2rpx solid #333;
|
||||||
|
border-radius: 4rpx;
|
||||||
|
background-color: #4caf50;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
right: -6rpx;
|
||||||
|
top: 6rpx;
|
||||||
|
width: 4rpx;
|
||||||
|
height: 8rpx;
|
||||||
|
background-color: #333;
|
||||||
|
border-radius: 0 2rpx 2rpx 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 20rpx 30rpx;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-bottom: 1rpx solid #f0f0f0;
|
||||||
|
|
||||||
|
.back-btn {
|
||||||
|
padding: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 36rpx;
|
||||||
|
font-weight: normal;
|
||||||
|
color: #ff0000;
|
||||||
|
margin-right: 60rpx; // 为了保持标题居中
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-btn {
|
||||||
|
padding: 10rpx;
|
||||||
|
|
||||||
|
.new-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #ff0000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-sort-bar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content:center;
|
||||||
|
padding: 20rpx 30rpx;
|
||||||
|
background-color: #ffff;
|
||||||
|
position: fixed;
|
||||||
|
top: 140rpx;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 9;
|
||||||
|
border-bottom: 1rpx solid #f0f0f0;
|
||||||
|
.imgbox{
|
||||||
|
width: 32rpx;
|
||||||
|
margin-top: -10rpx;
|
||||||
|
}
|
||||||
|
.sort-section {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10rpx;
|
||||||
|
|
||||||
|
.sort-label {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sort-icon.down {
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-left: 8rpx solid transparent;
|
||||||
|
border-right: 8rpx solid transparent;
|
||||||
|
border-top: 12rpx solid #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
width: 2rpx;
|
||||||
|
height: 40rpx;
|
||||||
|
background-color: #e0e0e0;
|
||||||
|
margin: 0 80rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.current-sort {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10rpx;
|
||||||
|
|
||||||
|
.sort-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sort-icon.up {
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-left: 8rpx solid transparent;
|
||||||
|
border-right: 8rpx solid transparent;
|
||||||
|
border-bottom: 12rpx solid #999;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
border-bottom-color: #ff0000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.patient-list-section {
|
||||||
|
top: 240rpx;
|
||||||
|
width:100%;
|
||||||
|
bottom:120rpx; // 预留底部操作栏高度
|
||||||
|
position: fixed;
|
||||||
|
background-color: #ffffff;
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
padding: 30rpx 30rpx 20rpx;
|
||||||
|
font-size: 32rpx;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-weight: normal;
|
||||||
|
display: flex;
|
||||||
|
color: #333;
|
||||||
|
border-bottom: 1rpx solid #f0f0f0;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
.left{
|
||||||
|
display:flex;
|
||||||
|
align-items:center;
|
||||||
|
}
|
||||||
|
.imgbox{
|
||||||
|
&.active{
|
||||||
|
margin-top: -20rpx;
|
||||||
|
}
|
||||||
|
margin-right: 8rpx;
|
||||||
|
}
|
||||||
|
.right-checkbox{
|
||||||
|
display:flex;
|
||||||
|
align-items:center;
|
||||||
|
.checkbox{
|
||||||
|
width: 40rpx;
|
||||||
|
height: 40rpx;
|
||||||
|
border: 2rpx solid #ddd;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: #fff;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&.checked{
|
||||||
|
border-color: #8B2316;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.patient-list {
|
||||||
|
// 滚动视图样式
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.patient-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 20rpx;
|
||||||
|
padding: 30rpx 40rpx;
|
||||||
|
border-bottom: 1rpx solid #f0f0f0;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.patient-checkbox{
|
||||||
|
margin-right: 8rpx;
|
||||||
|
.checkbox{
|
||||||
|
width: 40rpx;
|
||||||
|
height: 40rpx;
|
||||||
|
border: 2rpx solid #ddd;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: #fff;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
&.checked{ border-color: #8B2316; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.patient-avatar {
|
||||||
|
width: 80rpx;
|
||||||
|
height: 80rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.patient-info {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.patient-name {
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: #333;
|
||||||
|
margin-top: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.follow-up-time {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-bar{
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 0 -6rpx 20rpx rgba(0,0,0,0.06);
|
||||||
|
padding: 20rpx 30rpx env(safe-area-inset-bottom);
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
.align-right{justify-content:flex-end;}
|
||||||
|
|
||||||
|
.clear-btn{
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
height: 80rpx;
|
||||||
|
line-height: 80rpx;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
border: 2rpx solid #8B2316;
|
||||||
|
color: #8B2316;
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
279
pages_chat/quickReply/quickReply.vue
Normal file
279
pages_chat/quickReply/quickReply.vue
Normal file
@ -0,0 +1,279 @@
|
|||||||
|
<template>
|
||||||
|
<view class="quick-reply-page">
|
||||||
|
<uni-nav-bar
|
||||||
|
left-icon="left"
|
||||||
|
@clickLeft="goBack"
|
||||||
|
title="快捷回复"
|
||||||
|
fixed
|
||||||
|
color="#8B2316"
|
||||||
|
height="140rpx"
|
||||||
|
:border="false"
|
||||||
|
backgroundColor="#f5f5f5"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 顶部添加快捷回复入口 -->
|
||||||
|
<view class="add-entry" @click="onAdd">
|
||||||
|
<view class="row">
|
||||||
|
<view class="add-icon">
|
||||||
|
<uni-icons type="plusempty" color="#8B2316" size="22" />
|
||||||
|
</view>
|
||||||
|
<text class="add-text">添加快捷回复</text>
|
||||||
|
</view>
|
||||||
|
<view class="bar">
|
||||||
|
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- 列表 -->
|
||||||
|
<scroll-view class="list" scroll-y>
|
||||||
|
<view
|
||||||
|
class="item"
|
||||||
|
v-for="(text, index) in replies"
|
||||||
|
:key="index"
|
||||||
|
@click="onSelect(text.replystr)"
|
||||||
|
@longpress="onLongPress(text.uuid)"
|
||||||
|
>
|
||||||
|
<text class="item-text">{{text.replystr }}</text>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
|
||||||
|
<!-- 输入内容弹窗 -->
|
||||||
|
<UniPopup
|
||||||
|
ref="popupRef"
|
||||||
|
type="center"
|
||||||
|
background-color="#ffffff"
|
||||||
|
mask-background-color="rgba(0,0,0,0.4)"
|
||||||
|
>
|
||||||
|
<view class="input-popup">
|
||||||
|
<view class="popup-title">新增快捷回复</view>
|
||||||
|
<textarea
|
||||||
|
v-model="inputContent"
|
||||||
|
class="popup-textarea"
|
||||||
|
:maxlength="-1"
|
||||||
|
placeholder="请输入回复内容"
|
||||||
|
auto-height
|
||||||
|
/>
|
||||||
|
<view class="popup-actions">
|
||||||
|
<view class="btn cancel" @click="onCancel">取消</view>
|
||||||
|
<view class="btn confirm" @click="onConfirm">确定</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</UniPopup>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted, getCurrentInstance } from 'vue'
|
||||||
|
// @ts-ignore
|
||||||
|
const delUuid=ref('');
|
||||||
|
import UniPopup from '@/components/uni-components/uni-popup/components/uni-popup/uni-popup.vue'
|
||||||
|
import api from '@/api/api'
|
||||||
|
// 示例数据(后续可替换为接口数据或本地存储)
|
||||||
|
const replies = ref([])
|
||||||
|
|
||||||
|
const goBack = () => {
|
||||||
|
uni.navigateBack({
|
||||||
|
fail() {
|
||||||
|
uni.redirectTo({ url: '/pages/index/index' })
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const popupRef = ref(null)
|
||||||
|
const inputContent = ref('')
|
||||||
|
|
||||||
|
const onAdd = () => {
|
||||||
|
// @ts-ignore
|
||||||
|
popupRef.value && popupRef.value.open('center')
|
||||||
|
}
|
||||||
|
|
||||||
|
const onSelect = (text) => {
|
||||||
|
uni.$emit('quickReply', text);
|
||||||
|
uni.navigateBack()
|
||||||
|
}
|
||||||
|
const getQuickReplyList = async () => {
|
||||||
|
const res = await api.quickReplyList({})
|
||||||
|
if(res.code==200){
|
||||||
|
replies.value = res.data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const addQuickReply = async () => {
|
||||||
|
const res = await api.addQuickReply({
|
||||||
|
replystr:inputContent.value
|
||||||
|
})
|
||||||
|
if(res.code==200){
|
||||||
|
uni.showToast({title: '添加成功', icon: 'none'})
|
||||||
|
getQuickReplyList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const deleteQuickReply = async () => {
|
||||||
|
const res = await api.deleteQuickReply({
|
||||||
|
uuid:delUuid.value
|
||||||
|
})
|
||||||
|
if(res.code==200){
|
||||||
|
uni.showToast({title: '删除成功', icon: 'none'})
|
||||||
|
getQuickReplyList()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// 长按删除
|
||||||
|
const onLongPress = (index) => {
|
||||||
|
delUuid.value = index;
|
||||||
|
uni.showActionSheet({
|
||||||
|
itemList: ['删除该条快捷回复'],
|
||||||
|
success: (res) => {
|
||||||
|
if (res.tapIndex === 0) {
|
||||||
|
uni.showModal({
|
||||||
|
title: '确认删除',
|
||||||
|
content: '删除后不可恢复,是否删除?',
|
||||||
|
confirmColor: '#8B2316',
|
||||||
|
success: (modalRes) => {
|
||||||
|
if (modalRes.confirm) {
|
||||||
|
deleteQuickReply()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const onCancel = () => {
|
||||||
|
// @ts-ignore
|
||||||
|
popupRef.value && popupRef.value.close()
|
||||||
|
inputContent.value = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const onConfirm = () => {
|
||||||
|
const text = inputContent.value.trim()
|
||||||
|
if (!text) {
|
||||||
|
uni.showToast({ title: '请输入内容', icon: 'none' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
replies.value = [text, ...replies.value]
|
||||||
|
uni.setClipboardData({ data: text, showToast: false })
|
||||||
|
uni.showToast({ title: '已添加并复制', icon: 'none' })
|
||||||
|
onCancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.bar{
|
||||||
|
width:100%;
|
||||||
|
height:20rpx;
|
||||||
|
background-color: #e3e3e3;
|
||||||
|
}
|
||||||
|
.quick-reply-page {
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 顶部添加入口 */
|
||||||
|
.add-entry {
|
||||||
|
|
||||||
|
|
||||||
|
position: fixed;
|
||||||
|
top: 140rpx; /* 与导航栏高度一致 */
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
.row{
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 28rpx 30rpx;
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-icon {
|
||||||
|
width: 56rpx;
|
||||||
|
height: 56rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 2rpx solid #8b2316;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-right: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-text {
|
||||||
|
font-size: 30rpx;
|
||||||
|
color: #8b2316;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 列表区域 */
|
||||||
|
.list {
|
||||||
|
position: fixed;
|
||||||
|
top: 272rpx; /* 导航栏 + 添加入口高度(约) */
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item {
|
||||||
|
padding: 28rpx 30rpx;
|
||||||
|
border-bottom: 1rpx solid #eeeeee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-text {
|
||||||
|
font-size: 30rpx;
|
||||||
|
color: #333333;
|
||||||
|
line-height: 1.6;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 弹窗样式 */
|
||||||
|
.input-popup{
|
||||||
|
width: 640rpx;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 30rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.popup-title{
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: #333333;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
}
|
||||||
|
.popup-textarea{
|
||||||
|
width: 100%;
|
||||||
|
min-height: 160rpx;
|
||||||
|
background: #f7f7f7;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
padding: 20rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333333;
|
||||||
|
}
|
||||||
|
.popup-actions{
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-top: 24rpx;
|
||||||
|
}
|
||||||
|
.btn{
|
||||||
|
flex: 1;
|
||||||
|
height: 72rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 30rpx;
|
||||||
|
}
|
||||||
|
.btn.cancel{
|
||||||
|
background-color: #ffffff;
|
||||||
|
border: 2rpx solid #8b2316;
|
||||||
|
color: #8b2316;
|
||||||
|
margin-right: 20rpx;
|
||||||
|
}
|
||||||
|
.btn.confirm{
|
||||||
|
background-color: #8b2316;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
762
pages_chat/searchArticle/searchArticle.vue
Normal file
762
pages_chat/searchArticle/searchArticle.vue
Normal file
@ -0,0 +1,762 @@
|
|||||||
|
<template>
|
||||||
|
<view class="search-header">
|
||||||
|
<view class="back-icon" @click="goBack">
|
||||||
|
<uni-icons type="left" size="30" color="#8B2316"></uni-icons>
|
||||||
|
</view>
|
||||||
|
<view class="search-input-wrapper">
|
||||||
|
<view class="search-icon">
|
||||||
|
<uni-icons type="search" size="20" color="#999"></uni-icons>
|
||||||
|
</view>
|
||||||
|
<input
|
||||||
|
class="search-input"
|
||||||
|
placeholder="请输入搜索内容"
|
||||||
|
v-model="keywords"
|
||||||
|
@input="onSearchInput"
|
||||||
|
@confirm="onSearchConfirm"
|
||||||
|
confirm-type="search"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</view>
|
||||||
|
<view class="cancel-btn" @click="goSearch">
|
||||||
|
<text class="cancel-text">搜索</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="page">
|
||||||
|
<!-- <view class="top">
|
||||||
|
|
||||||
|
<view class="filter-bar">
|
||||||
|
<view class="search-box" @click="goToSearch">
|
||||||
|
<uni-icons type="search" size="16" color="#999"></uni-icons>
|
||||||
|
<text class="search-text">搜索</text>
|
||||||
|
</view>
|
||||||
|
<view class="divider"></view>
|
||||||
|
<view class="filter-item active" @click="toggleSort">
|
||||||
|
<text>{{type==1?'最新':'最热'}}</text>
|
||||||
|
<up-image v-if="type==1" :src="upImg" width="20rpx" height="26rpx" ></up-image>
|
||||||
|
<up-image v-else :src="downImg" width="20rpx" height="26rpx" ></up-image>
|
||||||
|
</view>
|
||||||
|
<view class="divider"></view>
|
||||||
|
<view class="filter-item" :class="{ active: isFilterActive }" @click="showFilterPopup">
|
||||||
|
<text>筛选</text>
|
||||||
|
<up-image :src="isFilterActive ? filterOn : filter" width="30rpx" height="30rpx" ></up-image>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
</view> -->
|
||||||
|
|
||||||
|
<!-- 筛选弹窗 -->
|
||||||
|
<view class="filter-popup" v-if="showFilter" @click="hideFilterPopup">
|
||||||
|
<view class="filter-content" @click.stop>
|
||||||
|
<!-- 筛选标签网格 -->
|
||||||
|
<view class="filter-tags">
|
||||||
|
<view
|
||||||
|
class="tag-item"
|
||||||
|
v-for="(tag, index) in filterTags"
|
||||||
|
:key="index"
|
||||||
|
:class="{ active: tag.selected }"
|
||||||
|
@click="toggleTag(index)"
|
||||||
|
>
|
||||||
|
{{ tag.DES }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 底部按钮 -->
|
||||||
|
<view class="filter-buttons">
|
||||||
|
<view class="btn-reset" @click="resetFilter">重置</view>
|
||||||
|
<view class="btn-confirm" @click="confirmFilter">确定</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 文章列表 -->
|
||||||
|
<scroll-view
|
||||||
|
class="article-list"
|
||||||
|
scroll-y
|
||||||
|
refresher-enabled="true"
|
||||||
|
:refresher-triggered="refreshing"
|
||||||
|
@refresherrefresh="onRefresh"
|
||||||
|
@scrolltolower="onLoadMore"
|
||||||
|
:lower-threshold="100"
|
||||||
|
>
|
||||||
|
<view class="article-item" v-for="(article, index) in articleList" :key="article.uuid || index" @click="goToDetail(article)">
|
||||||
|
<image class="article-image" :src="article.image" mode="aspectFill"></image>
|
||||||
|
<view class="article-content">
|
||||||
|
<view class="article-title">{{ article.title }}</view>
|
||||||
|
<view class="article-meta">
|
||||||
|
<view class="date-tag" v-if="article.isToday">今日</view>
|
||||||
|
<view class="date" v-else>{{ article.date }}</view>
|
||||||
|
<view class="stats">
|
||||||
|
<view class="stat-item">
|
||||||
|
<uni-icons type="eye" size="12" color="#999"></uni-icons>
|
||||||
|
<text>{{ article.views }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="stat-item">
|
||||||
|
<uni-icons type="hand-up" size="13" color="#999"></uni-icons>
|
||||||
|
<text>{{ article.likes }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 加载更多状态 -->
|
||||||
|
<view class="load-more" v-if="loading">
|
||||||
|
<uni-icons type="spinner-cycle" size="20" color="#999"></uni-icons>
|
||||||
|
<text class="load-text">加载中...</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 没有更多数据 -->
|
||||||
|
<view class="no-more" v-if="!hasMore && articleList.length > 0">
|
||||||
|
<text class="no-more-text">没有更多数据了</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 空状态 -->
|
||||||
|
<view class="empty-state" v-if="articleList.length === 0 && !loading">
|
||||||
|
<uni-icons type="article" size="80" color="#cccccc"></uni-icons>
|
||||||
|
<text class="empty-text">暂无文章</text>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
|
||||||
|
<!-- 底部导航栏 -->
|
||||||
|
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { onShow } from "@dcloudio/uni-app";
|
||||||
|
import upImg from "@/static/cb_up.png"
|
||||||
|
import downImg from "@/static/cb_up.png"
|
||||||
|
import filter from "@/static/cb_screen_no.png"
|
||||||
|
import filterOn from "@/static/cb_screen_yes.png"
|
||||||
|
import api from "@/api/api.js"
|
||||||
|
import docUrl from '@/utils/docUrl.js';
|
||||||
|
import navTo from '@/utils/navTo.js';
|
||||||
|
import navBar from '@/components/navBar/navBar.vue';
|
||||||
|
|
||||||
|
const type=ref(1)
|
||||||
|
|
||||||
|
// 分页相关状态
|
||||||
|
const currentPage = ref(1);
|
||||||
|
const pageSize = ref(10);
|
||||||
|
const hasMore = ref(true);
|
||||||
|
const loading = ref(false);
|
||||||
|
const refreshing = ref(false);
|
||||||
|
// 响应式数据
|
||||||
|
const activeTab = ref(0);
|
||||||
|
const showFilter = ref(false);
|
||||||
|
const isFilterActive = ref(false);
|
||||||
|
const toggleSort = () => {
|
||||||
|
type.value = type.value === 1 ? 2 : 1;
|
||||||
|
// 重置分页状态
|
||||||
|
currentPage.value = 1;
|
||||||
|
hasMore.value = true;
|
||||||
|
polularScienceArticleListByKeywordsNew(true);
|
||||||
|
};
|
||||||
|
const goSearch = () => {
|
||||||
|
if(!keywords.value){
|
||||||
|
uni.showToast({
|
||||||
|
title: '请输入搜索内容',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
polularScienceArticleListByKeywordsNew(true);
|
||||||
|
};
|
||||||
|
const loadGuideTags = async () => {
|
||||||
|
try {
|
||||||
|
const res = await api.guideTag({
|
||||||
|
type:4
|
||||||
|
});
|
||||||
|
if(res && res.code === 200 && res.data) {
|
||||||
|
// 将API返回的标签数据转换为筛选标签格式
|
||||||
|
filterTags.value = res.data.map(tag => ({
|
||||||
|
...tag,
|
||||||
|
selected: false
|
||||||
|
}));
|
||||||
|
console.log('指南标签加载成功:', filterTags.value);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('加载指南标签失败:', e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const polularScienceArticleListByKeywordsNew = async (isRefresh = false) => {
|
||||||
|
if (loading.value) return;
|
||||||
|
|
||||||
|
loading.value = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const page = isRefresh ? 1 : currentPage.value;
|
||||||
|
const res = await api.polularScienceArticleListByKeywordsNew({
|
||||||
|
keywords: keywords.value,
|
||||||
|
page: page,
|
||||||
|
type: type.value
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res && res.code === 200 && res.data) {
|
||||||
|
const newData = res.data.list || [];
|
||||||
|
|
||||||
|
if (isRefresh) {
|
||||||
|
// 下拉刷新:替换数据
|
||||||
|
articleList.value = newData.map(item => formatArticleData(item));
|
||||||
|
currentPage.value = 1;
|
||||||
|
} else {
|
||||||
|
// 上拉加载:追加数据
|
||||||
|
const formattedData = newData.map(item => formatArticleData(item));
|
||||||
|
articleList.value = [...articleList.value, ...formattedData];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断是否还有更多数据
|
||||||
|
hasMore.value = newData.length >= pageSize.value;
|
||||||
|
|
||||||
|
if (!isRefresh) {
|
||||||
|
currentPage.value++;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('文章数据加载成功:', articleList.value);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取文章列表失败:', error);
|
||||||
|
uni.showToast({
|
||||||
|
title: '获取数据失败',
|
||||||
|
icon: 'error',
|
||||||
|
duration: 2000
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
refreshing.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 格式化文章数据
|
||||||
|
const formatArticleData = (item) => {
|
||||||
|
// 安全检查,确保item存在且有所需属性
|
||||||
|
if (!item) return null;
|
||||||
|
|
||||||
|
const submitDate = item.submitDate ? new Date(item.submitDate) : new Date();
|
||||||
|
const today = new Date();
|
||||||
|
const isToday = submitDate.toDateString() === today.toDateString();
|
||||||
|
|
||||||
|
return {
|
||||||
|
uuid: item.uuid || '',
|
||||||
|
title: item.topic || '无标题',
|
||||||
|
summary: item.summary || '',
|
||||||
|
image: item.imgPath ? docUrl + item.imgPath : '/static/liver_knowledge.png',
|
||||||
|
date: isToday ? '今日' : formatDate(submitDate),
|
||||||
|
isToday: isToday,
|
||||||
|
views: item.readnum || 0,
|
||||||
|
likes: item.agreenum || 0,
|
||||||
|
tags: item.tags || '',
|
||||||
|
path: item.path || '',
|
||||||
|
submitDate: item.submitDate || ''
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// 格式化日期
|
||||||
|
const formatDate = (date) => {
|
||||||
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||||
|
const day = String(date.getDate()).padStart(2, '0');
|
||||||
|
return `${month}-${day}`;
|
||||||
|
};
|
||||||
|
// 筛选标签数据
|
||||||
|
const filterTags = ref([]);
|
||||||
|
|
||||||
|
// 文章列表数据
|
||||||
|
const articleList = ref([]);
|
||||||
|
|
||||||
|
// 方法
|
||||||
|
const switchTab = (index) => {
|
||||||
|
|
||||||
|
if(index==1){
|
||||||
|
navTo({
|
||||||
|
url: '/pages_app/patientVideo/patientVideo'
|
||||||
|
})
|
||||||
|
}else if(index==2){
|
||||||
|
let url=encodeURIComponent('https://wx.igandan.com/wxPatient/index.htm#/problem?link=share&fromtype=doctor')
|
||||||
|
navTo({
|
||||||
|
url: '/pages_app/webview/webview?url='+url
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const goToDetail = (article) => {
|
||||||
|
console.log('查看文章详情:', article);
|
||||||
|
// 跳转到详情页
|
||||||
|
uni.$emit('articelItem', article);
|
||||||
|
uni.navigateBack();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 跳转到搜索页面
|
||||||
|
const goToSearch = () => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages_app/search/search'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 下拉刷新
|
||||||
|
const onRefresh = async () => {
|
||||||
|
refreshing.value = true;
|
||||||
|
await polularScienceArticleListByKeywordsNew(true);
|
||||||
|
uni.showToast({
|
||||||
|
title: '刷新成功',
|
||||||
|
icon: 'success',
|
||||||
|
duration: 1500
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 上拉加载更多
|
||||||
|
const onLoadMore = async () => {
|
||||||
|
if (!hasMore.value || loading.value) return;
|
||||||
|
await polularScienceArticleListByKeywordsNew(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const goToPage = (page) => {
|
||||||
|
console.log('跳转到页面:', page);
|
||||||
|
// 根据页面名称进行跳转
|
||||||
|
if (page === 'index') {
|
||||||
|
uni.switchTab({
|
||||||
|
url: '/pages/index/index'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 筛选相关方法
|
||||||
|
const showFilterPopup = () => {
|
||||||
|
showFilter.value = true;
|
||||||
|
};
|
||||||
|
const goBack = () => {
|
||||||
|
uni.navigateBack({
|
||||||
|
delta:1,
|
||||||
|
fail(){
|
||||||
|
uni.redirectTo({
|
||||||
|
url:'/pages/index/index'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const hideFilterPopup = () => {
|
||||||
|
showFilter.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleTag = (index) => {
|
||||||
|
filterTags.value[index].selected = !filterTags.value[index].selected;
|
||||||
|
};
|
||||||
|
|
||||||
|
const resetFilter = () => {
|
||||||
|
filterTags.value.forEach(tag => {
|
||||||
|
tag.selected = false;
|
||||||
|
});
|
||||||
|
isFilterActive.value = false;
|
||||||
|
};
|
||||||
|
const keywords=ref('');
|
||||||
|
const confirmFilter = () => {
|
||||||
|
// 检查是否有选中的标签
|
||||||
|
const hasSelected = filterTags.value.some(tag => tag.selected);
|
||||||
|
isFilterActive.value = hasSelected;
|
||||||
|
|
||||||
|
// 执行筛选逻辑
|
||||||
|
if (hasSelected) {
|
||||||
|
const selectedTags = filterTags.value.filter(tag => tag.selected).map(tag => tag.DES);
|
||||||
|
console.log('选中的标签:', selectedTags);
|
||||||
|
keywords.value = selectedTags.join(',');
|
||||||
|
} else {
|
||||||
|
keywords.value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置分页状态
|
||||||
|
currentPage.value = 1;
|
||||||
|
hasMore.value = true;
|
||||||
|
|
||||||
|
showFilter.value = false;
|
||||||
|
polularScienceArticleListByKeywordsNew(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
onShow(() => {
|
||||||
|
polularScienceArticleListByKeywordsNew(true);
|
||||||
|
loadGuideTags();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
// 颜色变量
|
||||||
|
$primary-color: #ff6b6b;
|
||||||
|
$theme-color: #8B2316;
|
||||||
|
$white: #fff;
|
||||||
|
$gray-bg: #f5f5f5;
|
||||||
|
$gray-light: #eee;
|
||||||
|
$gray-medium: #999;
|
||||||
|
$gray-dark: #666;
|
||||||
|
$text-color: #333;
|
||||||
|
|
||||||
|
// 尺寸变量
|
||||||
|
$border-radius: 8px;
|
||||||
|
$border-radius-small: 6px;
|
||||||
|
$padding: 15px;
|
||||||
|
$padding-small: 10px;
|
||||||
|
|
||||||
|
.page {
|
||||||
|
background-color: $gray-bg;
|
||||||
|
height: calc(100vh - 140rpx);
|
||||||
|
display: flex;
|
||||||
|
overflow-y: hidden;
|
||||||
|
flex-direction: column;
|
||||||
|
// 为固定导航栏留出空间
|
||||||
|
}
|
||||||
|
.top{
|
||||||
|
width:100%;
|
||||||
|
position: fixed;
|
||||||
|
top:140rpx;
|
||||||
|
}
|
||||||
|
// 选项卡
|
||||||
|
.tabs {
|
||||||
|
background-color: $white;
|
||||||
|
display: flex;
|
||||||
|
border-bottom: 1px solid $gray-light;
|
||||||
|
|
||||||
|
.tab-item {
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
padding: $padding 0;
|
||||||
|
font-size: 16px;
|
||||||
|
color: $gray-dark;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
color: $theme-color;
|
||||||
|
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: 30px;
|
||||||
|
height: 2px;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 头部搜索栏
|
||||||
|
.search-header {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 100;
|
||||||
|
background-color: #eeeeee;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
height:140rpx;
|
||||||
|
align-items: center;
|
||||||
|
padding:0 30rpx;
|
||||||
|
gap: 20rpx;
|
||||||
|
|
||||||
|
|
||||||
|
.search-input-wrapper {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
padding: 0 20rpx;
|
||||||
|
height:70rpx;
|
||||||
|
|
||||||
|
.search-icon {
|
||||||
|
margin-right: 15rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input {
|
||||||
|
flex: 1;
|
||||||
|
height: 80rpx;
|
||||||
|
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clear-icon {
|
||||||
|
margin-left: 15rpx;
|
||||||
|
padding: 10rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cancel-btn {
|
||||||
|
padding: 10rpx 20rpx;
|
||||||
|
background:#8B2316;
|
||||||
|
border-radius: 10rpx;
|
||||||
|
|
||||||
|
.cancel-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 筛选栏
|
||||||
|
.filter-bar {
|
||||||
|
background-color: $white;
|
||||||
|
padding: $padding-small $padding;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border-bottom: 1px solid $gray-light;
|
||||||
|
|
||||||
|
.search-box {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
|
||||||
|
.search-text {
|
||||||
|
font-size: 14px;
|
||||||
|
color: $gray-medium;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
width: 1px;
|
||||||
|
height: 16px;
|
||||||
|
background-color: $gray-medium;
|
||||||
|
margin: 0 $padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-item {
|
||||||
|
flex:1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 3px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #999;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.filter-item.active{
|
||||||
|
color: $theme-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 文章列表
|
||||||
|
.article-list {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
top:140rpx;
|
||||||
|
padding-bottom: 0rpx;
|
||||||
|
.article-item {
|
||||||
|
background-color: $white;
|
||||||
|
margin-bottom: $padding-small;
|
||||||
|
border-radius: $border-radius;
|
||||||
|
padding: $padding;
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
.article-image {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
border-radius: $border-radius-small;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-content {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.article-title {
|
||||||
|
font-size: 16px;
|
||||||
|
color: $text-color;
|
||||||
|
line-height: 1.4;
|
||||||
|
margin-bottom: $padding-small;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
.article-meta {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.date-tag {
|
||||||
|
background-color: $primary-color;
|
||||||
|
color: $white;
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date {
|
||||||
|
font-size: 12px;
|
||||||
|
color: $gray-medium;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats {
|
||||||
|
display: flex;
|
||||||
|
gap: $padding;
|
||||||
|
|
||||||
|
.stat-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 3px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: $gray-medium;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载状态样式
|
||||||
|
.load-more {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 30rpx;
|
||||||
|
color: #999;
|
||||||
|
|
||||||
|
.load-text {
|
||||||
|
margin-left: 10rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-more {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 30rpx;
|
||||||
|
|
||||||
|
.no-more-text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 100rpx 30rpx;
|
||||||
|
|
||||||
|
.empty-text {
|
||||||
|
margin-top: 20rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 底部导航栏
|
||||||
|
.bottom-nav {
|
||||||
|
background-color: $white;
|
||||||
|
display: flex;
|
||||||
|
border-top: 1px solid $gray-light;
|
||||||
|
padding: 8px 0;
|
||||||
|
|
||||||
|
.nav-item {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 2px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: $gray-medium;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
color: $primary-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
text {
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 筛选弹窗样式
|
||||||
|
.filter-popup {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
// background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
z-index: 9999;
|
||||||
|
|
||||||
|
.filter-content {
|
||||||
|
margin-top:329rpx;
|
||||||
|
|
||||||
|
background-color: $white;
|
||||||
|
// border-radius: 20rpx 20rpx 0 0;
|
||||||
|
padding: 20rpx 30rpx 60rpx;
|
||||||
|
width: 100%;
|
||||||
|
max-height: 80vh;
|
||||||
|
|
||||||
|
.filter-tags {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 20rpx;
|
||||||
|
margin-bottom: 60rpx;
|
||||||
|
max-height: 65vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
.tag-item {
|
||||||
|
background-color: #f8f8f8;
|
||||||
|
color: $gray-dark;
|
||||||
|
padding: 8rpx 24rpx;
|
||||||
|
border-radius: 30rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
border: 2rpx solid #efefef;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: #fff;
|
||||||
|
color: $theme-color;
|
||||||
|
border-color: $theme-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 20rpx;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 30rpx;
|
||||||
|
left: 30rpx;
|
||||||
|
right: 30rpx;
|
||||||
|
|
||||||
|
.btn-reset,
|
||||||
|
.btn-confirm {
|
||||||
|
flex: 1;
|
||||||
|
height: 70rpx;
|
||||||
|
border-radius: 14rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-reset {
|
||||||
|
background-color: $white;
|
||||||
|
color: $theme-color;
|
||||||
|
border: 2rpx solid $theme-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-confirm {
|
||||||
|
border: 2rpx solid $theme-color;
|
||||||
|
background-color: $theme-color;
|
||||||
|
color: $white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
860
pages_chat/video/video.vue
Normal file
860
pages_chat/video/video.vue
Normal file
@ -0,0 +1,860 @@
|
|||||||
|
<template>
|
||||||
|
<uni-nav-bar left-icon="left"
|
||||||
|
@clickLeft="goBack" title="患教视频" fixed color="#8B2316" height="140rpx" :border="false" backgroundColor="#eeeeee"></uni-nav-bar>
|
||||||
|
<view class="video-page">
|
||||||
|
<!-- Header -->
|
||||||
|
<!-- Fixed Filter Tabs -->
|
||||||
|
<view class="filter-tabs-container">
|
||||||
|
<view class="filter-tabs">
|
||||||
|
<view
|
||||||
|
class="tab-item"
|
||||||
|
@click="showAllVideoPopup=!showAllVideoPopup"
|
||||||
|
>
|
||||||
|
<up-image :src="isAllActive?allOnImg:allImg" width="30rpx" height="30rpx" ></up-image>
|
||||||
|
<text class="tab-text" :class="{active:isAllActive}">{{typeName}}</text>
|
||||||
|
<up-image :src="isAllActive?selectOnImg:selectImg" width="30rpx" height="30rpx" ></up-image>
|
||||||
|
</view>
|
||||||
|
<view class="divider"></view>
|
||||||
|
<view class="right">
|
||||||
|
<view
|
||||||
|
class="tab-item"
|
||||||
|
@click="toggleSort"
|
||||||
|
>
|
||||||
|
<text class="tab-text active">{{sort==2?'最新':'最热'}}</text>
|
||||||
|
<view class="newbox">
|
||||||
|
<up-image :src="sort==2?upImg:downImg" width="20rpx" height="26rpx" ></up-image>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<!-- <view
|
||||||
|
class="tab-item"
|
||||||
|
@click="showFilterPopup"
|
||||||
|
>
|
||||||
|
<text class="tab-text " :class="{active:isFilterActive}">筛选</text>
|
||||||
|
<view class="filterbox">
|
||||||
|
<up-image :src="isFilterActive ? filterOn : filter" width="30rpx" height="30rpx" ></up-image>
|
||||||
|
</view>
|
||||||
|
</view> -->
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- Main Content -->
|
||||||
|
<scroll-view
|
||||||
|
class="scroll-view"
|
||||||
|
scroll-y="true"
|
||||||
|
refresher-enabled="true"
|
||||||
|
:refresher-triggered="refreshing"
|
||||||
|
@refresherrefresh="onRefresh"
|
||||||
|
@scrolltolower="onLoadMore"
|
||||||
|
lower-threshold="100"
|
||||||
|
>
|
||||||
|
|
||||||
|
<!-- Video List -->
|
||||||
|
<view class="video-list">
|
||||||
|
<view
|
||||||
|
class="video-item"
|
||||||
|
v-for="(video, index) in videoList"
|
||||||
|
:key="video.id || video.uuid"
|
||||||
|
@click="playVideo(video)"
|
||||||
|
>
|
||||||
|
<view class="video-thumbnail">
|
||||||
|
<image :src="docUrl + video.imgpath" mode="aspectFill"></image>
|
||||||
|
</view>
|
||||||
|
<view class="video-info">
|
||||||
|
<view class="video-title">{{video.title || video.name}}</view>
|
||||||
|
<view class="video-meta">
|
||||||
|
<text class="author">{{video.public_name}}</text>
|
||||||
|
<view class="stats">
|
||||||
|
<uni-icons type="eye" size="14" color="#999"></uni-icons>
|
||||||
|
<text class="view-count">{{video.readnum}}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- Loading More -->
|
||||||
|
<uni-load-more
|
||||||
|
v-if="videoList.length > 0"
|
||||||
|
:status="loadMoreStatus"
|
||||||
|
:content-text="{
|
||||||
|
contentdown: '上拉加载更多',
|
||||||
|
contentrefresh: '正在加载...',
|
||||||
|
contentnomore: '没有更多数据了'
|
||||||
|
}"
|
||||||
|
></uni-load-more>
|
||||||
|
|
||||||
|
<!-- Empty State -->
|
||||||
|
<view class="empty-state" v-if="videoList.length === 0 && !loading">
|
||||||
|
<empty></empty>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
<!-- 筛选弹窗 -->
|
||||||
|
<view class="filter-popup" v-if="showFilter" @click="hideFilterPopup">
|
||||||
|
<view class="filter-content" @click.stop>
|
||||||
|
<!-- 筛选标签网格 -->
|
||||||
|
<view class="filter-tags">
|
||||||
|
<view
|
||||||
|
class="tag-item"
|
||||||
|
v-for="(tag, index) in filterTags"
|
||||||
|
:key="index"
|
||||||
|
:class="{ active: tag.selected }"
|
||||||
|
@click="toggleTag(index)"
|
||||||
|
>
|
||||||
|
{{ tag.NAME }}
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 底部按钮 -->
|
||||||
|
<view class="filter-buttons">
|
||||||
|
<view class="btn-reset" @click="resetFilter">重置</view>
|
||||||
|
<view class="btn-confirm" @click="confirmFilter">确定</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- 全部视频弹窗 -->
|
||||||
|
<view class="all-video-popup" v-if="showAllVideoPopup" @click="closeAllVideoPopup">
|
||||||
|
<view class="popup-content" @click.stop>
|
||||||
|
<view class="popup-body">
|
||||||
|
<view
|
||||||
|
class="category-item"
|
||||||
|
:class="{ active: typeUuid === item.uuid }"
|
||||||
|
v-for="item in videoTypeList"
|
||||||
|
:key="item.uuid"
|
||||||
|
@click="selectCategory(item.uuid)"
|
||||||
|
>
|
||||||
|
{{ item.name }}
|
||||||
|
</view>
|
||||||
|
<view v-if="videoTypeList.length === 0" class="empty-content">
|
||||||
|
<text>暂无数据</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted, computed,reactive } from 'vue';
|
||||||
|
import { onShow } from "@dcloudio/uni-app";
|
||||||
|
import api from '@/api/api.js';
|
||||||
|
import allImg from "@/static/video_all.png"
|
||||||
|
import allOnImg from "@/static/video_select.png"
|
||||||
|
import selectImg from "@/static/all_video.png"
|
||||||
|
import selectOnImg from "@/static/select_video.png"
|
||||||
|
import filter from "@/static/cb_screen_no.png"
|
||||||
|
import filterOn from "@/static/cb_screen_yes.png"
|
||||||
|
import upImg from "@/static/cb_up.png"
|
||||||
|
import downImg from "@/static/cb_down.png"
|
||||||
|
import docUrl from '@/utils/docUrl';
|
||||||
|
import navTo from '@/utils/navTo';
|
||||||
|
import empty from '@/components/empty/empty.vue';
|
||||||
|
const isAllActive=ref(false)
|
||||||
|
// 响应式数据
|
||||||
|
const videoList = ref([]);
|
||||||
|
const activeTab = ref(1); // 默认选中"最新"
|
||||||
|
const loading = ref(false);
|
||||||
|
const refreshing = ref(false);
|
||||||
|
const loadMoreStatus = ref('more'); // more, loading, noMore
|
||||||
|
const currentPage = ref(1);
|
||||||
|
const pageSize = ref(10);
|
||||||
|
const hasMoreData = ref(true);
|
||||||
|
const filteredContent=ref([])
|
||||||
|
const sort =ref(2);
|
||||||
|
const keywords=ref('');
|
||||||
|
const typeUuid=ref('0e5fa3d76b8047528fdd3c452b77e9dd');
|
||||||
|
const typeName=ref('乙肝')
|
||||||
|
const isFilterActive=ref(false)
|
||||||
|
const showFilter = ref(false);
|
||||||
|
const yearList=ref([]);
|
||||||
|
const selectYearContent=reactive({})
|
||||||
|
// 筛选弹窗相关数据
|
||||||
|
const filterTags = ref([]);
|
||||||
|
|
||||||
|
// 全部视频弹窗相关数据
|
||||||
|
const showAllVideoPopup = ref(false);
|
||||||
|
const selectedCategory = ref('全部');
|
||||||
|
const selectCategory=(uuid)=>{
|
||||||
|
console.log(222)
|
||||||
|
console.log(uuid)
|
||||||
|
typeUuid.value=uuid;
|
||||||
|
typeName.value=videoTypeList.value.find(item=>item.uuid==uuid).name;
|
||||||
|
isAllActive.value=true;
|
||||||
|
closeAllVideoPopup();
|
||||||
|
currentPage.value = 1;
|
||||||
|
hasMoreData.value = true;
|
||||||
|
loadVideoData();
|
||||||
|
}
|
||||||
|
|
||||||
|
const videoTypeList=ref([]);
|
||||||
|
const patientVideoNew=async ()=>{
|
||||||
|
const res=await api.patientVideoNew();
|
||||||
|
if(res.code==200){
|
||||||
|
videoTypeList.value=res.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 页面加载
|
||||||
|
onMounted(() => {
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
onShow(() => {
|
||||||
|
// 调用真实API获取数据
|
||||||
|
currentPage.value = 1;
|
||||||
|
hasMoreData.value = true;
|
||||||
|
loadGuideTags();
|
||||||
|
loadVideoData();
|
||||||
|
patientVideoNew();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 加载轮播数据
|
||||||
|
|
||||||
|
// 加载视频标签
|
||||||
|
const loadGuideTags = async () => {
|
||||||
|
try {
|
||||||
|
const res = await api.guideTag({
|
||||||
|
type:5
|
||||||
|
});
|
||||||
|
console.log('指南标签API响应:', res);
|
||||||
|
if(res && res.code === 200 && res.data) {
|
||||||
|
// 将API返回的标签数据转换为筛选标签格式
|
||||||
|
filterTags.value = res.data.map(tag => ({
|
||||||
|
...tag,
|
||||||
|
selected: false
|
||||||
|
}));
|
||||||
|
console.log('指南标签加载成功:', filterTags.value);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('加载指南标签失败:', e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// 加载视频数据
|
||||||
|
const loadVideoData = async (isRefresh = false) => {
|
||||||
|
if (loading.value && !isRefresh) return;
|
||||||
|
|
||||||
|
loading.value = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await api.patientVideoByKeyWordsNew({
|
||||||
|
page: currentPage.value,
|
||||||
|
pageSize: pageSize.value,
|
||||||
|
keywords: keywords.value,
|
||||||
|
sort:sort.value,
|
||||||
|
typeUuid:typeUuid.value,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('视频列表API原始响应:', response);
|
||||||
|
|
||||||
|
// 检查响应结构
|
||||||
|
let videoData = {};
|
||||||
|
if (response && response.data) {
|
||||||
|
if (response.data.code === 200) {
|
||||||
|
videoData = response.data.data || {};
|
||||||
|
} else if (response.code === 200) {
|
||||||
|
videoData = response.data || {};
|
||||||
|
}
|
||||||
|
} else if (response && response.code === 200) {
|
||||||
|
videoData = response.data || {};
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('解析后的视频数据:', videoData);
|
||||||
|
|
||||||
|
if (videoData && videoData.list) {
|
||||||
|
const { list, totalPage,pageNumber } = videoData;
|
||||||
|
console.log('视频列表数据:', list);
|
||||||
|
|
||||||
|
if (isRefresh) {
|
||||||
|
videoList.value = list || [];
|
||||||
|
currentPage.value = 1;
|
||||||
|
} else {
|
||||||
|
videoList.value = [...videoList.value, ...(list || [])];
|
||||||
|
}
|
||||||
|
|
||||||
|
hasMoreData.value = totalPage>pageNumber;
|
||||||
|
loadMoreStatus.value = hasMoreData.value ? 'more' : 'noMore';
|
||||||
|
} else {
|
||||||
|
throw new Error(response?.message || '获取数据失败');
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载视频失败:', error);
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
refreshing.value = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 下拉刷新
|
||||||
|
const onRefresh = () => {
|
||||||
|
refreshing.value = true;
|
||||||
|
currentPage.value = 1;
|
||||||
|
hasMoreData.value = true;
|
||||||
|
loadVideoData(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 上拉加载更多
|
||||||
|
const onLoadMore = () => {
|
||||||
|
if (!hasMoreData.value || loading.value) return;
|
||||||
|
|
||||||
|
loadMoreStatus.value = 'loading';
|
||||||
|
currentPage.value++;
|
||||||
|
loadVideoData();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 切换标签
|
||||||
|
const switchTab = (tabIndex) => {
|
||||||
|
activeTab.value = tabIndex;
|
||||||
|
// 可以根据标签加载不同的数据
|
||||||
|
currentPage.value = 1;
|
||||||
|
hasMoreData.value = true;
|
||||||
|
loadVideoData();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 播放视频
|
||||||
|
const playVideo = (video) => {
|
||||||
|
uni.$emit('videoItem', video);
|
||||||
|
uni.navigateBack();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 搜索
|
||||||
|
const goSearch = () => {
|
||||||
|
uni.navigateTo({
|
||||||
|
url: '/pages_app/search/search'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 跳转到患教视频列表
|
||||||
|
|
||||||
|
// 搜索视频
|
||||||
|
const searchVideos = async (keywords) => {
|
||||||
|
if (!keywords || keywords.trim() === '') {
|
||||||
|
loadVideoData();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log('搜索关键词:', keywords.trim());
|
||||||
|
const response = await api.patientVideoByKeyWordsNew({
|
||||||
|
keywords: keywords.trim(),
|
||||||
|
page: 1,
|
||||||
|
pageSize: pageSize.value
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('搜索API原始响应:', response);
|
||||||
|
|
||||||
|
// 检查响应结构
|
||||||
|
let searchData = {};
|
||||||
|
if (response && response.data) {
|
||||||
|
if (response.data.code === 200) {
|
||||||
|
searchData = response.data.data || {};
|
||||||
|
} else if (response.code === 200) {
|
||||||
|
searchData = response.data || {};
|
||||||
|
}
|
||||||
|
} else if (response && response.code === 200) {
|
||||||
|
searchData = response.data || {};
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('解析后的搜索数据:', searchData);
|
||||||
|
|
||||||
|
if (searchData && searchData.list) {
|
||||||
|
const { list, hasMore } = searchData;
|
||||||
|
console.log('搜索结果:', list);
|
||||||
|
videoList.value = list || [];
|
||||||
|
hasMoreData.value = hasMore !== false;
|
||||||
|
loadMoreStatus.value = hasMoreData.value ? 'more' : 'noMore';
|
||||||
|
currentPage.value = 1;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('搜索视频失败:', error);
|
||||||
|
uni.showToast({
|
||||||
|
title: '搜索失败,请重试',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 全部视频弹窗相关方法
|
||||||
|
const openAllVideoPopup = () => {
|
||||||
|
showAllVideoPopup.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeAllVideoPopup = () => {
|
||||||
|
showAllVideoPopup.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 筛选弹窗相关方法
|
||||||
|
const showFilterPopup = () => {
|
||||||
|
showFilter.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const hideFilterPopup = () => {
|
||||||
|
showFilter.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleTag = (index) => {
|
||||||
|
filterTags.value[index].selected = !filterTags.value[index].selected;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const resetFilter = () => {
|
||||||
|
// 清空所有已选标签
|
||||||
|
filterTags.value.forEach(tag => tag.selected = false);
|
||||||
|
// 关闭筛选激活态
|
||||||
|
|
||||||
|
// 清空关键字
|
||||||
|
keywords.value = '';
|
||||||
|
// 刷新列表数据(可按需保留或移除)
|
||||||
|
currentPage.value = 1;
|
||||||
|
hasMoreData.value = true;
|
||||||
|
loadVideoData(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const confirmFilter = () => {
|
||||||
|
const selectedTags = filterTags.value.filter(tag => tag.selected);
|
||||||
|
console.log('选中的筛选标签:', selectedTags);
|
||||||
|
for (var i = 0; i < selectedTags.length; i++) {
|
||||||
|
if(keywords.value){
|
||||||
|
keywords.value+=","+selectedTags[i].NAME
|
||||||
|
}else{
|
||||||
|
keywords.value=selectedTags[i].NAME
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isFilterActive.value=true;
|
||||||
|
hideFilterPopup();
|
||||||
|
// 根据选中的标签重新加载数据
|
||||||
|
currentPage.value = 1;
|
||||||
|
hasMoreData.value = true;
|
||||||
|
loadVideoData(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 返回
|
||||||
|
const goBack = () => {
|
||||||
|
uni.navigateBack({
|
||||||
|
fail() {
|
||||||
|
uni.redirectTo({
|
||||||
|
url: '/pages/index/index'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const toggleSort = () => {
|
||||||
|
sort.value = sort.value === 1 ? 2 : 1; // 1=最新, 2=最热(与后端约定)
|
||||||
|
currentPage.value = 1;
|
||||||
|
hasMoreData.value = true;
|
||||||
|
loadVideoData();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
$primary-color: #ff6b6b;
|
||||||
|
$theme-color: #8B2316;
|
||||||
|
$white: #fff;
|
||||||
|
$gray-bg: #f5f5f5;
|
||||||
|
$gray-light: #eee;
|
||||||
|
$gray-medium: #999;
|
||||||
|
$gray-dark: #666;
|
||||||
|
$text-color: #333;
|
||||||
|
|
||||||
|
// 尺寸变量
|
||||||
|
$border-radius: 8px;
|
||||||
|
$border-radius-small: 6px;
|
||||||
|
$padding: 15px;
|
||||||
|
$padding-small: 10px;
|
||||||
|
.video-page {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
overflow: hidden;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
.divider {
|
||||||
|
width: 1px;
|
||||||
|
height: 16px;
|
||||||
|
background-color: $gray-medium;
|
||||||
|
margin: 0 $padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Header Styles */
|
||||||
|
.header {
|
||||||
|
background-color: #fff;
|
||||||
|
height: 88rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0 30rpx;
|
||||||
|
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.1);
|
||||||
|
position: relative;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-left, .header-right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-title {
|
||||||
|
font-size: 36rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #8B4513;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scroll View */
|
||||||
|
.scroll-view {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0rpx;
|
||||||
|
height: calc(100vh - 140rpx); /* 固定高度,减去导航栏高度 */
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
top: 172rpx; /* 为导航栏留出空间 */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Filter Tabs */
|
||||||
|
.filter-tabs {
|
||||||
|
background-color: #fff;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content:center;
|
||||||
|
padding: 30rpx 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex:1;
|
||||||
|
padding: 10rpx 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item:last-child{
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
.right{
|
||||||
|
flex:1;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item.active .tab-text {
|
||||||
|
color: #E74C3C;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-text {
|
||||||
|
margin: 0 8rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
.tab-item:first-child .tab-text{
|
||||||
|
max-width:155rpx;
|
||||||
|
display: inline-block;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.tab-item .tab-text.active {
|
||||||
|
color: #8B2316;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fixed Filter Tabs Container */
|
||||||
|
.filter-tabs-container {
|
||||||
|
position: fixed;
|
||||||
|
top: 140rpx; /* uni-nav-bar height */
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 40;
|
||||||
|
background-color: #fff;
|
||||||
|
border-bottom: 1rpx solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.doctor-avatar {
|
||||||
|
width: 80rpx;
|
||||||
|
height: 80rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-right: 20rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.doctor-avatar image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Video List */
|
||||||
|
.video-list {
|
||||||
|
padding: 0 20rpx;
|
||||||
|
margin-top: 100rpx; /* 为固定的Filter Tabs留出空间 */
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 16rpx; /* 卡片间距 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-item {
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
margin-bottom: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.1);
|
||||||
|
width: calc(50% - 8rpx); /* 两列布局,减半gap */
|
||||||
|
min-width: 300rpx; /* 确保最小宽度 */
|
||||||
|
flex-shrink: 0; /* 防止收缩 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-thumbnail {
|
||||||
|
position: relative;
|
||||||
|
height: 200rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-thumbnail image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.video-duration {
|
||||||
|
margin-top: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-duration text {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-info {
|
||||||
|
padding: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-title {
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 400;
|
||||||
|
color: #333;
|
||||||
|
line-height: 1.4;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
line-clamp: 2;
|
||||||
|
overflow: hidden;
|
||||||
|
height: 2.8em; /* 固定2行高度:1.4 * 2 = 2.8em */
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-meta {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.author {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-count {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #999;
|
||||||
|
margin-left: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Empty State */
|
||||||
|
.empty-state {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 100rpx 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state image {
|
||||||
|
width: 200rpx;
|
||||||
|
height: 200rpx;
|
||||||
|
margin-bottom: 30rpx;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state text {
|
||||||
|
color: #999;
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
.newbox{
|
||||||
|
margin-top: -12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 全部视频弹窗样式 */
|
||||||
|
.all-video-popup {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 1000;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-content {
|
||||||
|
width: 100%;
|
||||||
|
background-color: #fff;
|
||||||
|
height:calc(100vh - 272rpx);
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-header {
|
||||||
|
padding: 30rpx;
|
||||||
|
border-bottom: 1rpx solid #f0f0f0;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-title {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-body {
|
||||||
|
height:100%;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-item {
|
||||||
|
padding: 30rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #333;
|
||||||
|
border-bottom: 1rpx solid #f0f0f0;
|
||||||
|
background-color: #fff;
|
||||||
|
transition: background-color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-item:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-item.active {
|
||||||
|
background-color: #f8f8f8;
|
||||||
|
color: #8B2316;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-item:active {
|
||||||
|
background-color: #e8f4fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-content {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 200rpx;
|
||||||
|
color: #999;
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
// 筛选弹窗样式
|
||||||
|
.filter-popup {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
// background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
z-index: 9999;
|
||||||
|
|
||||||
|
.filter-content {
|
||||||
|
margin-top:272rpx;
|
||||||
|
overflow-y:scroll;
|
||||||
|
background-color: $white;
|
||||||
|
// border-radius: 20rpx 20rpx 0 0;
|
||||||
|
padding: 20rpx 30rpx 60rpx;
|
||||||
|
width: 100%;
|
||||||
|
height: calc(100vh - 272rpx);
|
||||||
|
|
||||||
|
.filter-tags {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 20rpx;
|
||||||
|
margin-bottom: 60rpx;
|
||||||
|
max-height: 65vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
.tag-item {
|
||||||
|
background-color: #f8f8f8;
|
||||||
|
color: $gray-dark;
|
||||||
|
padding: 8rpx 0; /* 去掉左右padding,仅保留上下 */
|
||||||
|
border-radius: 30rpx;
|
||||||
|
font-size: 26rpx;
|
||||||
|
border: 2rpx solid #efefef;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
width: 152rpx; /* 固定宽度 */
|
||||||
|
text-align: center; /* 文本居中 */
|
||||||
|
white-space: nowrap; /* 单行显示 */
|
||||||
|
overflow: hidden; /* 隐藏溢出 */
|
||||||
|
text-overflow: ellipsis; /* 超出显示省略号 */
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: #fff;
|
||||||
|
color: $theme-color;
|
||||||
|
border-color: $theme-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-buttons {
|
||||||
|
display: flex;
|
||||||
|
gap: 20rpx;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 30rpx;
|
||||||
|
left: 30rpx;
|
||||||
|
right: 30rpx;
|
||||||
|
background-color: #fff; /* 背景色为白色 */
|
||||||
|
|
||||||
|
.btn-reset,
|
||||||
|
.btn-confirm {
|
||||||
|
flex: 1;
|
||||||
|
height: 70rpx;
|
||||||
|
border-radius: 14rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-reset {
|
||||||
|
background-color: $white;
|
||||||
|
color: $theme-color;
|
||||||
|
border: 2rpx solid $theme-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-confirm {
|
||||||
|
border: 2rpx solid $theme-color;
|
||||||
|
background-color: $theme-color;
|
||||||
|
color: $white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
BIN
static/im_icon_camera_on.png
Normal file
BIN
static/im_icon_camera_on.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.7 KiB |
BIN
static/im_icon_images_on.png
Normal file
BIN
static/im_icon_images_on.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.3 KiB |
BIN
static/outpatient_true.png
Normal file
BIN
static/outpatient_true.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.5 KiB |
BIN
static/quck_message.png
Normal file
BIN
static/quck_message.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.4 KiB |
BIN
static/ytx_chatting_hospital.png
Normal file
BIN
static/ytx_chatting_hospital.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
BIN
static/ytx_chattingfooter_shopping.png
Normal file
BIN
static/ytx_chattingfooter_shopping.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.5 KiB |
Loading…
x
Reference in New Issue
Block a user