提交
This commit is contained in:
parent
7f737dfd8a
commit
3ad6140829
@ -7,8 +7,14 @@
|
|||||||
<view v-if="customScroll" class="page-scroll">
|
<view v-if="customScroll" class="page-scroll">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</view>
|
</view>
|
||||||
<scroll-view v-else scroll-y="true" :scroll-top="scrollTop" class="page-scroll" @scrolltolower="scrolltolower"
|
<scroll-view
|
||||||
@scroll="onScroll">
|
v-else
|
||||||
|
scroll-y="true"
|
||||||
|
:scroll-top="scrollTop"
|
||||||
|
class="page-scroll"
|
||||||
|
@scrolltolower="scrolltolower"
|
||||||
|
@scroll="onScroll"
|
||||||
|
>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
</view>
|
</view>
|
||||||
@ -16,22 +22,22 @@
|
|||||||
<slot name="footer"></slot>
|
<slot name="footer"></slot>
|
||||||
</view>
|
</view>
|
||||||
<!-- #ifdef MP-->
|
<!-- #ifdef MP-->
|
||||||
<view v-if="showSafeArea" class="safeareaBottom"></view>
|
<!-- <view v-if="showSafeArea" class="safeareaBottom"></view> -->
|
||||||
<!-- #endif -->
|
<!-- #endif -->
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, useSlots, ref } from 'vue';
|
import { computed, useSlots, ref } from "vue";
|
||||||
import useDebounce from '@/utils/useDebounce';
|
import useDebounce from "@/utils/useDebounce";
|
||||||
|
|
||||||
const emits = defineEmits(['reachBottom']);
|
const emits = defineEmits(["reachBottom"]);
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
customScroll: { type: Boolean, default: false },
|
customScroll: { type: Boolean, default: false },
|
||||||
mainClass: { type: String, default: '' },
|
mainClass: { type: String, default: "" },
|
||||||
mainStyle: { default: '' },
|
mainStyle: { default: "" },
|
||||||
pageClass: { type: String, default: '' },
|
pageClass: { type: String, default: "" },
|
||||||
pageStyle: { default: '' },
|
pageStyle: { default: "" },
|
||||||
showSafeArea: { type: Boolean, default: true }
|
showSafeArea: { type: Boolean, default: true },
|
||||||
});
|
});
|
||||||
const slots = useSlots();
|
const slots = useSlots();
|
||||||
const hasHeader = computed(() => !!slots.header);
|
const hasHeader = computed(() => !!slots.header);
|
||||||
@ -40,7 +46,7 @@ const hasFooter = computed(() => !!slots.footer);
|
|||||||
const scrollTop = ref(0);
|
const scrollTop = ref(0);
|
||||||
|
|
||||||
const scrolltolower = useDebounce(() => {
|
const scrolltolower = useDebounce(() => {
|
||||||
emits('reachBottom');
|
emits("reachBottom");
|
||||||
});
|
});
|
||||||
|
|
||||||
const onScroll = useDebounce((e) => {
|
const onScroll = useDebounce((e) => {
|
||||||
@ -52,9 +58,8 @@ function scrollToBottom() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
scrollToBottom
|
scrollToBottom,
|
||||||
})
|
});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.full-page {
|
.full-page {
|
||||||
|
|||||||
@ -65,12 +65,12 @@ const buttons = ref([
|
|||||||
icon: "/static/icon/zhuiwen.png",
|
icon: "/static/icon/zhuiwen.png",
|
||||||
loading: false,
|
loading: false,
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
id: "aiAssistant",
|
// id: "aiAssistant",
|
||||||
text: "开启AI助手",
|
// text: "开启AI助手",
|
||||||
icon: "/static/icon/kaiqiAI.png",
|
// icon: "/static/icon/kaiqiAI.png",
|
||||||
loading: false,
|
// loading: false,
|
||||||
},
|
// },
|
||||||
{
|
{
|
||||||
id: "supplementRecord",
|
id: "supplementRecord",
|
||||||
text: "补充病历",
|
text: "补充病历",
|
||||||
@ -241,7 +241,7 @@ const handleCaseTypeSelect = async (type) => {
|
|||||||
title: error.message || "生成病历失败",
|
title: error.message || "生成病历失败",
|
||||||
icon: "none",
|
icon: "none",
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("补充病历失败:", error);
|
console.error("补充病历失败:", error);
|
||||||
@ -254,13 +254,22 @@ const handleCaseTypeSelect = async (type) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 流式请求处理
|
// 流式请求处理
|
||||||
const requestWithStream = async ({ url, data, onProgress, onComplete, onError }) => {
|
const requestWithStream = async ({
|
||||||
|
url,
|
||||||
|
data,
|
||||||
|
onProgress,
|
||||||
|
onComplete,
|
||||||
|
onError,
|
||||||
|
}) => {
|
||||||
try {
|
try {
|
||||||
// 调用接口时不显示全局 loading(第二个参数为 false)
|
// 调用接口时不显示全局 loading(第二个参数为 false)
|
||||||
const result = await request({
|
const result = await request(
|
||||||
url,
|
{
|
||||||
data,
|
url,
|
||||||
}, false);
|
data,
|
||||||
|
},
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
if (result.success && result.data) {
|
if (result.success && result.data) {
|
||||||
// 模拟流式处理(如果后端返回的是完整数据)
|
// 模拟流式处理(如果后端返回的是完整数据)
|
||||||
@ -275,7 +284,7 @@ const requestWithStream = async ({ url, data, onProgress, onComplete, onError })
|
|||||||
const [key, value] = fields[i];
|
const [key, value] = fields[i];
|
||||||
|
|
||||||
// 显示所有字段,包括空值(会在组件中显示为"暂无")
|
// 显示所有字段,包括空值(会在组件中显示为"暂无")
|
||||||
await new Promise(resolve => setTimeout(resolve, delay));
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
||||||
onProgress({ key, value });
|
onProgress({ key, value });
|
||||||
progressValue += Math.floor(60 / fields.length);
|
progressValue += Math.floor(60 / fields.length);
|
||||||
progressRef.value?.updateProgress(Math.min(progressValue, 80));
|
progressRef.value?.updateProgress(Math.min(progressValue, 80));
|
||||||
|
|||||||
@ -1,5 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="message-page">
|
<view class="message-page">
|
||||||
|
<!-- 团队切换头部 -->
|
||||||
|
<view class="header-container">
|
||||||
|
<view class="team-selector" @click="showTeamPicker = true">
|
||||||
|
<text class="team-name">{{ currentTeamName }}</text>
|
||||||
|
<text class="arrow-icon">▼</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
<!-- 标签页切换 -->
|
<!-- 标签页切换 -->
|
||||||
<view class="tabs-container">
|
<view class="tabs-container">
|
||||||
<view
|
<view
|
||||||
@ -20,6 +28,27 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
<!-- 团队选择弹窗 -->
|
||||||
|
<view v-if="showTeamPicker" class="team-picker-overlay" @click="showTeamPicker = false">
|
||||||
|
<view class="team-picker-content" @click.stop>
|
||||||
|
<view class="team-picker-header">
|
||||||
|
<text class="picker-title">选择团队</text>
|
||||||
|
</view>
|
||||||
|
<scroll-view class="team-list" scroll-y>
|
||||||
|
<view
|
||||||
|
v-for="team in teamList"
|
||||||
|
:key="team._id"
|
||||||
|
class="team-item"
|
||||||
|
:class="{ active: currentTeamId === team._id }"
|
||||||
|
@click="selectTeam(team)"
|
||||||
|
>
|
||||||
|
<text class="team-item-name">{{ team.name }}</text>
|
||||||
|
<text v-if="currentTeamId === team._id" class="check-icon">✓</text>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
<!-- 消息列表 -->
|
<!-- 消息列表 -->
|
||||||
<scroll-view
|
<scroll-view
|
||||||
class="message-list"
|
class="message-list"
|
||||||
@ -101,12 +130,33 @@ import { ref, watch, computed } from "vue";
|
|||||||
import { onLoad, onShow, onHide } from "@dcloudio/uni-app";
|
import { onLoad, onShow, onHide } from "@dcloudio/uni-app";
|
||||||
import { storeToRefs } from "pinia";
|
import { storeToRefs } from "pinia";
|
||||||
import useAccountStore from "@/store/account.js";
|
import useAccountStore from "@/store/account.js";
|
||||||
|
import useTeamStore from "@/store/team.js";
|
||||||
import { globalTimChatManager } from "@/utils/tim-chat.js";
|
import { globalTimChatManager } from "@/utils/tim-chat.js";
|
||||||
import { mergeConversationWithGroupDetails } from "@/utils/conversation-merger.js";
|
import { mergeConversationWithGroupDetails } from "@/utils/conversation-merger.js";
|
||||||
|
|
||||||
// 获取登录状态
|
// 获取登录状态
|
||||||
const { account, openid, isIMInitialized } = storeToRefs(useAccountStore());
|
const { account, openid, isIMInitialized } = storeToRefs(useAccountStore());
|
||||||
const { initIMAfterLogin } = useAccountStore();
|
const { initIMAfterLogin } = useAccountStore();
|
||||||
|
|
||||||
|
// 获取团队信息
|
||||||
|
const teamStore = useTeamStore();
|
||||||
|
const { teams } = storeToRefs(teamStore);
|
||||||
|
const { getTeams } = teamStore;
|
||||||
|
|
||||||
|
// 团队相关状态
|
||||||
|
const showTeamPicker = ref(false);
|
||||||
|
const currentTeamId = ref(""); // 空字符串表示"全部会话消息"
|
||||||
|
const teamList = computed(() => {
|
||||||
|
// 在团队列表前添加"全部会话消息"选项
|
||||||
|
const allOption = { _id: "", name: "全部会话消息" };
|
||||||
|
return [allOption, ...(teams.value || [])];
|
||||||
|
});
|
||||||
|
const currentTeamName = computed(() => {
|
||||||
|
if (!currentTeamId.value) return "全部会话消息";
|
||||||
|
const team = teams.value.find((t) => t._id === currentTeamId.value);
|
||||||
|
return team ? team.name : "全部会话消息";
|
||||||
|
});
|
||||||
|
|
||||||
// 监听 IM 初始化状态
|
// 监听 IM 初始化状态
|
||||||
watch(isIMInitialized, (newValue) => {
|
watch(isIMInitialized, (newValue) => {
|
||||||
console.log("IM初始化状态变化:", newValue);
|
console.log("IM初始化状态变化:", newValue);
|
||||||
@ -126,25 +176,39 @@ const activeTab = ref("processing");
|
|||||||
|
|
||||||
// 根据 orderStatus 过滤会话列表
|
// 根据 orderStatus 过滤会话列表
|
||||||
const filteredConversationList = computed(() => {
|
const filteredConversationList = computed(() => {
|
||||||
|
let filtered = [];
|
||||||
|
|
||||||
if (activeTab.value === "processing") {
|
if (activeTab.value === "processing") {
|
||||||
// 处理中:pending(待处理) 和 processing(处理中)
|
// 处理中:pending(待处理) 和 processing(处理中)
|
||||||
const filtered = conversationList.value.filter(
|
filtered = conversationList.value.filter(
|
||||||
(conv) =>
|
(conv) =>
|
||||||
conv.orderStatus === "pending" || conv.orderStatus === "processing"
|
conv.orderStatus === "pending" || conv.orderStatus === "processing"
|
||||||
);
|
);
|
||||||
return filtered;
|
|
||||||
} else {
|
} else {
|
||||||
// 已结束:cancelled(已取消)、completed(已完成)、finished(已结束)
|
// 已结束:cancelled(已取消)、completed(已完成)、finished(已结束)
|
||||||
const filtered = conversationList.value.filter(
|
filtered = conversationList.value.filter(
|
||||||
(conv) =>
|
(conv) =>
|
||||||
conv.orderStatus === "cancelled" ||
|
conv.orderStatus === "cancelled" ||
|
||||||
conv.orderStatus === "completed" ||
|
conv.orderStatus === "completed" ||
|
||||||
conv.orderStatus === "finished"
|
conv.orderStatus === "finished"
|
||||||
);
|
);
|
||||||
return filtered;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果选择了团队,进一步过滤
|
||||||
|
if (currentTeamId.value) {
|
||||||
|
filtered = filtered.filter((conv) => conv.teamId === currentTeamId.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return filtered;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 选择团队
|
||||||
|
const selectTeam = (team) => {
|
||||||
|
currentTeamId.value = team._id;
|
||||||
|
showTeamPicker.value = false;
|
||||||
|
console.log("切换到团队:", team.name);
|
||||||
|
};
|
||||||
|
|
||||||
// 切换标签页
|
// 切换标签页
|
||||||
const switchTab = (tab) => {
|
const switchTab = (tab) => {
|
||||||
if (activeTab.value === tab) return;
|
if (activeTab.value === tab) return;
|
||||||
@ -311,13 +375,17 @@ const setupConversationListener = () => {
|
|||||||
existing.patientSex !== conversationData.patientSex ||
|
existing.patientSex !== conversationData.patientSex ||
|
||||||
existing.patientAge !== conversationData.patientAge
|
existing.patientAge !== conversationData.patientAge
|
||||||
) {
|
) {
|
||||||
Object.assign(
|
// 只更新变化的字段,保持头像和未读数稳定
|
||||||
conversationList.value[existingIndex],
|
conversationList.value[existingIndex] = {
|
||||||
conversationData
|
...conversationData,
|
||||||
);
|
// 保持原有头像,避免闪动
|
||||||
|
avatar: existing.avatar || conversationData.avatar,
|
||||||
|
// 保留较大的未读数(避免被后端数据覆盖)
|
||||||
|
unreadCount: Math.max(existing.unreadCount || 0, conversationData.unreadCount || 0)
|
||||||
|
};
|
||||||
needSort = true;
|
needSort = true;
|
||||||
console.log(
|
console.log(
|
||||||
`已更新会话: ${conversationData.name}, unreadCount: ${conversationData.unreadCount}`
|
`已更新会话: ${conversationData.name}, unreadCount: ${conversationList.value[existingIndex].unreadCount}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -350,19 +418,23 @@ const setupConversationListener = () => {
|
|||||||
if (conversationIndex !== -1) {
|
if (conversationIndex !== -1) {
|
||||||
const conversation = conversationList.value[conversationIndex];
|
const conversation = conversationList.value[conversationIndex];
|
||||||
|
|
||||||
// 检查当前页面栈,判断用户是否正在查看该会话
|
// 检查当前页面栈,判断用户是否正在查看该会话的聊天详情页
|
||||||
const pages = getCurrentPages();
|
const pages = getCurrentPages();
|
||||||
const currentPage = pages[pages.length - 1];
|
const currentPage = pages[pages.length - 1];
|
||||||
const isViewingConversation =
|
|
||||||
currentPage?.route === "pages/message/index";
|
|
||||||
|
|
||||||
// 如果用户正在查看该会话,不增加未读数
|
// 获取当前页面的 groupID 参数(如果在聊天详情页)
|
||||||
if (isViewingConversation) {
|
const currentGroupID = currentPage?.options?.groupID;
|
||||||
|
const isViewingThisConversation =
|
||||||
|
currentPage?.route === "pages/message/index" &&
|
||||||
|
currentGroupID === conversation.groupID;
|
||||||
|
|
||||||
|
// 如果用户正在查看这个具体的会话,不增加未读数
|
||||||
|
if (isViewingThisConversation) {
|
||||||
console.log("用户正在查看该会话,不增加未读数");
|
console.log("用户正在查看该会话,不增加未读数");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 只在用户不在聊天页面时才增加未读数
|
// 只在用户不在该会话的聊天页面时才增加未读数
|
||||||
conversation.unreadCount = (conversation.unreadCount || 0) + 1;
|
conversation.unreadCount = (conversation.unreadCount || 0) + 1;
|
||||||
console.log(
|
console.log(
|
||||||
"已更新会话未读数:",
|
"已更新会话未读数:",
|
||||||
@ -488,6 +560,9 @@ onLoad(() => {
|
|||||||
// 页面显示
|
// 页面显示
|
||||||
onShow(async () => {
|
onShow(async () => {
|
||||||
try {
|
try {
|
||||||
|
// 加载团队列表
|
||||||
|
await getTeams();
|
||||||
|
|
||||||
// 初始化IM
|
// 初始化IM
|
||||||
const imReady = await initIM();
|
const imReady = await initIM();
|
||||||
if (!imReady) {
|
if (!imReady) {
|
||||||
@ -531,6 +606,111 @@ onHide(() => {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.header-container {
|
||||||
|
background-color: #fff;
|
||||||
|
padding: 20rpx 32rpx;
|
||||||
|
border-bottom: 1rpx solid #f0f0f0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-selector {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-name {
|
||||||
|
font-size: 36rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.arrow-icon {
|
||||||
|
font-size: 20rpx;
|
||||||
|
color: #999;
|
||||||
|
margin-left: 12rpx;
|
||||||
|
transform: scale(0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-picker-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
z-index: 1000;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: center;
|
||||||
|
padding-top: 200rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-picker-content {
|
||||||
|
width: 600rpx;
|
||||||
|
max-height: 800rpx;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-picker-header {
|
||||||
|
padding: 32rpx;
|
||||||
|
border-bottom: 1rpx solid #f0f0f0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.picker-title {
|
||||||
|
font-size: 36rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-list {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 32rpx;
|
||||||
|
border-bottom: 1rpx solid #f0f0f0;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: #e6f7ff;
|
||||||
|
|
||||||
|
.team-item-name {
|
||||||
|
color: #1890ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-item-name {
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: #333;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-icon {
|
||||||
|
font-size: 36rpx;
|
||||||
|
color: #1890ff;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
.tabs-container {
|
.tabs-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
|
|||||||
@ -48,18 +48,14 @@ export async function mergeConversationWithGroupDetails(conversationList, option
|
|||||||
console.error('获取群组详细信息失败:', response?.message || '未知错误')
|
console.error('获取群组详细信息失败:', response?.message || '未知错误')
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
const groupDetailsMap = createGroupDetailsMap(response.data?.list || [])
|
const groupDetailsMap = createGroupDetailsMap(response.data?.list || [])
|
||||||
console.log('获取到的群组详细信息数量:', Object.keys(groupDetailsMap).size)
|
console.log('获取到的群组详细信息数量:', Object.keys(groupDetailsMap).size)
|
||||||
|
|
||||||
// 5. 合并数据并过滤
|
// 5. 合并数据并过滤
|
||||||
const mergedList = conversationList
|
const mergedList = conversationList
|
||||||
.map(conversation => mergeConversationData(conversation, groupDetailsMap))
|
.map(conversation => mergeConversationData(conversation, groupDetailsMap))
|
||||||
.filter(item => item !== null) // 过滤掉后端不存在的会话
|
.filter(item => item !== null);
|
||||||
|
|
||||||
console.log('合并后的会话列表数量:', mergedList.length)
|
console.log('合并后的会话列表数量:', mergedList.length)
|
||||||
console.log('过滤掉的会话数量:', conversationList.length - mergedList.length)
|
console.log('过滤掉的会话数量:', conversationList.length - mergedList.length)
|
||||||
|
|
||||||
// 6. 格式化并排序会话列表
|
// 6. 格式化并排序会话列表
|
||||||
const formattedList = mergedList
|
const formattedList = mergedList
|
||||||
.map((group) => ({
|
.map((group) => ({
|
||||||
@ -156,8 +152,8 @@ function mergeConversationData(conversation, groupDetailsMap) {
|
|||||||
// 更新显示名称(使用后端的患者信息)
|
// 更新显示名称(使用后端的患者信息)
|
||||||
name: formatConversationName(groupDetail),
|
name: formatConversationName(groupDetail),
|
||||||
|
|
||||||
// 更新头像
|
// 更新头像(优先使用已有头像,避免闪动)
|
||||||
avatar: groupDetail.patient?.avatar || conversation.avatar || '/static/default-avatar.png'
|
avatar: conversation.avatar || groupDetail.patient?.avatar || '/static/default-avatar.png'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user