refactor(ui): 优化调试模式实现并改进界面样式
- 将调试模式状态管理移至 infoStore 实现全局统一控制 - 移除各组件中分散的调试模式状态管理代码 - 调整侧边栏和头部组件的边框和背景颜色 - 优化按钮和交互元素的悬停效果 - 更新文档中的 changelog 记录待办事项
This commit is contained in:
parent
95b084ec69
commit
d6dfa2b5e3
@ -9,6 +9,8 @@
|
|||||||
- [x] 将现在的 graphview.vue 文件中 GraphContainer相关的代码分离到一个单独的组件中,然后 actions 和 footer 都是作为 slot top/bottom 提供的
|
- [x] 将现在的 graphview.vue 文件中 GraphContainer相关的代码分离到一个单独的组件中,然后 actions 和 footer 都是作为 slot top/bottom 提供的
|
||||||
- [x] 然后应用到 web/src/components/ToolCallingResult/KnowledgeGraphResult.vue 中,替换现有的 GraphContainer。
|
- [x] 然后应用到 web/src/components/ToolCallingResult/KnowledgeGraphResult.vue 中,替换现有的 GraphContainer。
|
||||||
- [ ] 知识图谱的上传和可视化,支持属性,标签的展示
|
- [ ] 知识图谱的上传和可视化,支持属性,标签的展示
|
||||||
|
- [ ] 集成智能体评估,首先使用命令行来实现,然后考虑放在 UI 里面展示
|
||||||
|
- [ ] 开发与生产环境隔离
|
||||||
|
|
||||||
🐛**BUGs**
|
🐛**BUGs**
|
||||||
- [x] LlightRAG 知识库中,点击边,没有显示,但是在全屏的时候却又能够显示出来。
|
- [x] LlightRAG 知识库中,点击边,没有显示,但是在全屏的时候却又能够显示出来。
|
||||||
|
|||||||
@ -100,7 +100,6 @@
|
|||||||
:message="message"
|
:message="message"
|
||||||
:key="msgIndex"
|
:key="msgIndex"
|
||||||
:is-processing="isProcessing && conv.status === 'streaming' && msgIndex === conv.messages.length - 1"
|
:is-processing="isProcessing && conv.status === 'streaming' && msgIndex === conv.messages.length - 1"
|
||||||
:debug-mode="state.debug_mode"
|
|
||||||
:show-refs="showMsgRefs(message)"
|
:show-refs="showMsgRefs(message)"
|
||||||
@retry="retryMessage(message)"
|
@retry="retryMessage(message)"
|
||||||
>
|
>
|
||||||
@ -207,7 +206,6 @@ const threadMessages = ref({});
|
|||||||
|
|
||||||
const uiState = reactive({
|
const uiState = reactive({
|
||||||
...props.state,
|
...props.state,
|
||||||
debug_mode: computed(() => props.state.debug_mode ?? false),
|
|
||||||
isSidebarOpen: localStorage.getItem('chat_sidebar_open', 'true') === 'true',
|
isSidebarOpen: localStorage.getItem('chat_sidebar_open', 'true') === 'true',
|
||||||
isInitialRender: true,
|
isInitialRender: true,
|
||||||
showRenameButton: false,
|
showRenameButton: false,
|
||||||
@ -1004,8 +1002,8 @@ watch(conversations, () => {
|
|||||||
|
|
||||||
.example-chip {
|
.example-chip {
|
||||||
padding: 6px 12px;
|
padding: 6px 12px;
|
||||||
background: var(--gray-100);
|
background: var(--gray-25);
|
||||||
border: 1px solid var(--gray-200);
|
// border: 1px solid var(--gray-100);
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
@ -1017,11 +1015,10 @@ watch(conversations, () => {
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: var(--main-50);
|
// background: var(--main-25);
|
||||||
border-color: var(--main-200);
|
border-color: var(--main-200);
|
||||||
color: var(--main-700);
|
color: var(--main-700);
|
||||||
transform: translateY(-1px);
|
box-shadow: 0 0px 4px rgba(0, 0, 0, 0.03);
|
||||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&:active {
|
&:active {
|
||||||
|
|||||||
@ -21,11 +21,6 @@
|
|||||||
<div class="agent-info" v-if="selectedAgent">
|
<div class="agent-info" v-if="selectedAgent">
|
||||||
<div class="agent-basic-info">
|
<div class="agent-basic-info">
|
||||||
<p class="agent-description">{{ selectedAgent.description }}</p>
|
<p class="agent-description">{{ selectedAgent.description }}</p>
|
||||||
<!-- <div class="debug-toggle" v-if="selectedAgent.name">
|
|
||||||
<span class="debug-text">调试信息</span>
|
|
||||||
<a-switch v-model:checked="debugMode" size="small" />
|
|
||||||
</div>
|
|
||||||
<pre v-if="debugMode && selectedAgent.name" class="debug-info">{{ selectedAgent }}</pre> -->
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a-divider />
|
<a-divider />
|
||||||
@ -334,7 +329,6 @@ const {
|
|||||||
// console.log(availableTools.value)
|
// console.log(availableTools.value)
|
||||||
|
|
||||||
// 本地状态
|
// 本地状态
|
||||||
const debugMode = ref(false);
|
|
||||||
const toolsModalOpen = ref(false);
|
const toolsModalOpen = ref(false);
|
||||||
const selectedTools = ref([]);
|
const selectedTools = ref([]);
|
||||||
const toolsSearchText = ref('');
|
const toolsSearchText = ref('');
|
||||||
@ -362,10 +356,6 @@ const closeSidebar = () => {
|
|||||||
emit('close');
|
emit('close');
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleDebugMode = () => {
|
|
||||||
debugMode.value = !debugMode.value;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getConfigLabel = (key, value) => {
|
const getConfigLabel = (key, value) => {
|
||||||
// console.log(configurableItems)
|
// console.log(configurableItems)
|
||||||
if (value.description && value.name !== key) {
|
if (value.description && value.name !== key) {
|
||||||
@ -648,30 +638,6 @@ watch(() => props.isOpen, (newVal) => {
|
|||||||
color: var(--gray-700);
|
color: var(--gray-700);
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.debug-toggle {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: 8px 0;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
.debug-text {
|
|
||||||
font-size: 13px;
|
|
||||||
color: var(--gray-600);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.debug-info {
|
|
||||||
margin-top: 12px;
|
|
||||||
padding: 12px;
|
|
||||||
background: var(--gray-50);
|
|
||||||
border-radius: 6px;
|
|
||||||
font-size: 12px;
|
|
||||||
color: var(--gray-700);
|
|
||||||
max-height: 200px;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -71,7 +71,7 @@
|
|||||||
<!-- 错误消息 -->
|
<!-- 错误消息 -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="debugMode" class="status-info">{{ message }}</div>
|
<div v-if="infoStore.debugMode" class="status-info">{{ message }}</div>
|
||||||
|
|
||||||
<!-- 自定义内容 -->
|
<!-- 自定义内容 -->
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
@ -85,6 +85,7 @@ import RefsComponent from '@/components/RefsComponent.vue'
|
|||||||
import { Loader, CircleCheckBig } from 'lucide-vue-next';
|
import { Loader, CircleCheckBig } from 'lucide-vue-next';
|
||||||
import { ToolResultRenderer } from '@/components/ToolCallingResult'
|
import { ToolResultRenderer } from '@/components/ToolCallingResult'
|
||||||
import { useAgentStore } from '@/stores/agent'
|
import { useAgentStore } from '@/stores/agent'
|
||||||
|
import { useInfoStore } from '@/stores/info'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
|
|
||||||
|
|
||||||
@ -112,12 +113,13 @@ const props = defineProps({
|
|||||||
type: [Array, Boolean],
|
type: [Array, Boolean],
|
||||||
default: () => false
|
default: () => false
|
||||||
},
|
},
|
||||||
debugMode: {
|
// 是否为最新消息
|
||||||
|
isLatestMessage: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
// 是否为最新消息
|
// 是否显示调试信息 (已废弃,使用 infoStore.debugMode)
|
||||||
isLatestMessage: {
|
debugMode: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
}
|
}
|
||||||
@ -133,6 +135,7 @@ const expandedToolCalls = ref(new Set()); // 展开的工具调用集合
|
|||||||
|
|
||||||
// 引入智能体 store
|
// 引入智能体 store
|
||||||
const agentStore = useAgentStore();
|
const agentStore = useAgentStore();
|
||||||
|
const infoStore = useInfoStore();
|
||||||
const { availableTools } = storeToRefs(agentStore);
|
const { availableTools } = storeToRefs(agentStore);
|
||||||
|
|
||||||
// 工具相关方法
|
// 工具相关方法
|
||||||
|
|||||||
@ -244,7 +244,7 @@ const openAgentModal = () => {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: 0 16px;
|
padding: 0 16px;
|
||||||
border-bottom: 1px solid var(--gray-200);
|
border-bottom: 1px solid var(--gray-50);
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
|
||||||
.header-title {
|
.header-title {
|
||||||
@ -261,6 +261,7 @@ const openAgentModal = () => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
// color: var(--gray-600);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -360,10 +361,10 @@ const openAgentModal = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--gray-50);
|
background-color: var(--gray-25);
|
||||||
|
|
||||||
.actions-mask {
|
.actions-mask {
|
||||||
background: linear-gradient(to right, transparent, var(--gray-100) 20px);
|
background: linear-gradient(to right, transparent, var(--gray-25) 20px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.actions-mask, .conversation-actions {
|
.actions-mask, .conversation-actions {
|
||||||
@ -372,14 +373,14 @@ const openAgentModal = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
background-color: var(--gray-100);
|
background-color: var(--gray-50);
|
||||||
|
|
||||||
.conversation-title {
|
.conversation-title {
|
||||||
color: var(--main-600);
|
color: var(--main-600);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
.actions-mask {
|
.actions-mask {
|
||||||
background: linear-gradient(to right, transparent, var(--gray-100) 20px);
|
background: linear-gradient(to right, transparent, var(--gray-50) 20px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -415,20 +416,25 @@ const openAgentModal = () => {
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
color: var(--gray-500);
|
|
||||||
// padding: 0.5rem;
|
// padding: 0.5rem;
|
||||||
transition: background-color 0.3s;
|
transition: background-color 0.3s;
|
||||||
&:hover {
|
|
||||||
color: var(--main-color);
|
svg {
|
||||||
|
stroke: var(--gray-600);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover svg {
|
||||||
|
stroke: var(--main-color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 智能体选择器样式
|
// 智能体选择器样式
|
||||||
.agent-selector {
|
.agent-selector {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 14px;
|
font-size: 15px;
|
||||||
color: var(--gray-900);
|
color: var(--gray-900);
|
||||||
transition: color 0.2s ease;
|
transition: color 0.2s ease;
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: var(--main-500);
|
color: var(--main-500);
|
||||||
|
|||||||
@ -22,6 +22,10 @@
|
|||||||
<template #icon><RobotOutlined /></template>
|
<template #icon><RobotOutlined /></template>
|
||||||
智能体配置
|
智能体配置
|
||||||
</a-button>
|
</a-button>
|
||||||
|
<a-button @click="toggleDebugMode" :type="infoStore.debugMode ? 'primary' : 'default'">
|
||||||
|
<template #icon><BugOutlined /></template>
|
||||||
|
Debug 模式: {{ infoStore.debugMode ? '开启' : '关闭' }}
|
||||||
|
</a-button>
|
||||||
<a-button @click="toggleFullscreen">
|
<a-button @click="toggleFullscreen">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<FullscreenOutlined v-if="!state.isFullscreen" />
|
<FullscreenOutlined v-if="!state.isFullscreen" />
|
||||||
@ -99,6 +103,7 @@ import { useConfigStore } from '@/stores/config';
|
|||||||
import { useUserStore } from '@/stores/user';
|
import { useUserStore } from '@/stores/user';
|
||||||
import { useDatabaseStore } from '@/stores/database';
|
import { useDatabaseStore } from '@/stores/database';
|
||||||
import { useAgentStore } from '@/stores/agent';
|
import { useAgentStore } from '@/stores/agent';
|
||||||
|
import { useInfoStore } from '@/stores/info';
|
||||||
import { useThrottleFn } from '@vueuse/core';
|
import { useThrottleFn } from '@vueuse/core';
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
import {
|
import {
|
||||||
@ -112,7 +117,8 @@ import {
|
|||||||
PlusCircleOutlined,
|
PlusCircleOutlined,
|
||||||
UserOutlined,
|
UserOutlined,
|
||||||
DatabaseOutlined,
|
DatabaseOutlined,
|
||||||
RobotOutlined
|
RobotOutlined,
|
||||||
|
BugOutlined
|
||||||
} from '@ant-design/icons-vue';
|
} from '@ant-design/icons-vue';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { configApi } from '@/apis/system_api';
|
import { configApi } from '@/apis/system_api';
|
||||||
@ -122,6 +128,7 @@ const configStore = useConfigStore()
|
|||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const databaseStore = useDatabaseStore();
|
const databaseStore = useDatabaseStore();
|
||||||
const agentStore = useAgentStore();
|
const agentStore = useAgentStore();
|
||||||
|
const infoStore = useInfoStore();
|
||||||
const config = configStore.config;
|
const config = configStore.config;
|
||||||
|
|
||||||
|
|
||||||
@ -366,6 +373,12 @@ const printDatabaseInfo = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 切换Debug模式
|
||||||
|
const toggleDebugMode = () => {
|
||||||
|
if (!checkAdminPermission()) return;
|
||||||
|
infoStore.toggleDebugMode();
|
||||||
|
};
|
||||||
|
|
||||||
// 打印智能体配置
|
// 打印智能体配置
|
||||||
const printAgentConfig = async () => {
|
const printAgentConfig = async () => {
|
||||||
if (!checkAdminPermission()) return;
|
if (!checkAdminPermission()) return;
|
||||||
|
|||||||
@ -245,7 +245,7 @@ div.header, #app-router-view {
|
|||||||
background-color: var(--main-10);
|
background-color: var(--main-10);
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: var(--header-width);
|
width: var(--header-width);
|
||||||
border-right: 1px solid var(--gray-300);
|
border-right: 1px solid var(--gray-100);
|
||||||
|
|
||||||
.nav {
|
.nav {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@ -7,6 +7,7 @@ export const useInfoStore = defineStore('info', () => {
|
|||||||
const infoConfig = ref({})
|
const infoConfig = ref({})
|
||||||
const isLoading = ref(false)
|
const isLoading = ref(false)
|
||||||
const isLoaded = ref(false)
|
const isLoaded = ref(false)
|
||||||
|
const debugMode = ref(false)
|
||||||
|
|
||||||
// 计算属性 - 组织信息
|
// 计算属性 - 组织信息
|
||||||
const organization = computed(() => infoConfig.value.organization || {
|
const organization = computed(() => infoConfig.value.organization || {
|
||||||
@ -41,6 +42,14 @@ export const useInfoStore = defineStore('info', () => {
|
|||||||
isLoaded.value = true
|
isLoaded.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setDebugMode(enabled) {
|
||||||
|
debugMode.value = enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleDebugMode() {
|
||||||
|
debugMode.value = !debugMode.value
|
||||||
|
}
|
||||||
|
|
||||||
async function loadInfoConfig(force = false) {
|
async function loadInfoConfig(force = false) {
|
||||||
// 如果已经加载过且不强制刷新,则不重新加载
|
// 如果已经加载过且不强制刷新,则不重新加载
|
||||||
if (isLoaded.value && !force) {
|
if (isLoaded.value && !force) {
|
||||||
@ -93,6 +102,7 @@ export const useInfoStore = defineStore('info', () => {
|
|||||||
infoConfig,
|
infoConfig,
|
||||||
isLoading,
|
isLoading,
|
||||||
isLoaded,
|
isLoaded,
|
||||||
|
debugMode,
|
||||||
|
|
||||||
// 计算属性
|
// 计算属性
|
||||||
organization,
|
organization,
|
||||||
@ -102,6 +112,8 @@ export const useInfoStore = defineStore('info', () => {
|
|||||||
|
|
||||||
// 方法
|
// 方法
|
||||||
setInfoConfig,
|
setInfoConfig,
|
||||||
|
setDebugMode,
|
||||||
|
toggleDebugMode,
|
||||||
loadInfoConfig,
|
loadInfoConfig,
|
||||||
reloadInfoConfig
|
reloadInfoConfig
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,19 +2,19 @@
|
|||||||
<div class="agent-view">
|
<div class="agent-view">
|
||||||
<div class="agent-view-header">
|
<div class="agent-view-header">
|
||||||
<div class="header-left">
|
<div class="header-left">
|
||||||
<div class="header-item">
|
|
||||||
<span class="brandname">{{ infoStore.branding?.title }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="header-item">
|
<div class="header-item">
|
||||||
<a-button class="header-button" @click="openAgentModal">
|
<span class="brandname">{{ infoStore.branding?.title }}</span>
|
||||||
<Bot size="18" stroke-width="1.75" />
|
|
||||||
选择:{{ selectedAgent?.name || '选择智能体' }}
|
|
||||||
</a-button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="header-center">
|
<div class="header-center">
|
||||||
</div>
|
</div>
|
||||||
<div class="header-right">
|
<div class="header-right">
|
||||||
|
<div class="header-item">
|
||||||
|
<a-button class="header-button" @click="openAgentModal">
|
||||||
|
<Bot size="18" stroke-width="1.75" />
|
||||||
|
切换智能体
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
<div class="header-item">
|
<div class="header-item">
|
||||||
<a-button class="header-button" @click="toggleConf" :icon="h(SettingOutlined)"> 配置 </a-button>
|
<a-button class="header-button" @click="toggleConf" :icon="h(SettingOutlined)"> 配置 </a-button>
|
||||||
</div>
|
</div>
|
||||||
@ -118,7 +118,6 @@ const {
|
|||||||
configurableItems,
|
configurableItems,
|
||||||
} = storeToRefs(agentStore);
|
} = storeToRefs(agentStore);
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
debug_mode: false,
|
|
||||||
agentModalOpen: false,
|
agentModalOpen: false,
|
||||||
isConfigSidebarOpen: false,
|
isConfigSidebarOpen: false,
|
||||||
isEmptyConfig: computed(() =>
|
isEmptyConfig: computed(() =>
|
||||||
@ -217,11 +216,6 @@ const goToAgentPage = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleDebugMode = () => {
|
|
||||||
state.debug_mode = !state.debug_mode;
|
|
||||||
console.log("debug_mode", state.debug_mode);
|
|
||||||
};
|
|
||||||
|
|
||||||
const toggleConf = () => {
|
const toggleConf = () => {
|
||||||
state.isConfigSidebarOpen = !state.isConfigSidebarOpen
|
state.isConfigSidebarOpen = !state.isConfigSidebarOpen
|
||||||
}
|
}
|
||||||
@ -240,7 +234,7 @@ const toggleConf = () => {
|
|||||||
.agent-view-header {
|
.agent-view-header {
|
||||||
height: var(--agent-view-header-height);
|
height: var(--agent-view-header-height);
|
||||||
background-color: var(--bg-sider);
|
background-color: var(--bg-sider);
|
||||||
border-bottom: 1px solid var(--gray-200);
|
border-bottom: 1px solid var(--gray-100);
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -260,7 +254,7 @@ const toggleConf = () => {
|
|||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
|
||||||
span.brandname {
|
span.brandname {
|
||||||
font-size: 17px;
|
font-size: 18px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
margin-right: 16px;
|
margin-right: 16px;
|
||||||
@ -271,6 +265,7 @@ const toggleConf = () => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
|
border-color: var(--gray-100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user