mirror of
https://gitee.com/blackfox/geekai.git
synced 2024-12-02 12:17:42 +08:00
opt: adjust ItemList component styles
This commit is contained in:
parent
7d1d88a32f
commit
a688d3feb5
@ -145,7 +145,10 @@ func authorizeMiddleware(s *AppServer, client *redis.Client) gin.HandlerFunc {
|
|||||||
c.Request.URL.Path == "/api/mj/notify" ||
|
c.Request.URL.Path == "/api/mj/notify" ||
|
||||||
c.Request.URL.Path == "/api/chat/history" ||
|
c.Request.URL.Path == "/api/chat/history" ||
|
||||||
c.Request.URL.Path == "/api/chat/detail" ||
|
c.Request.URL.Path == "/api/chat/detail" ||
|
||||||
|
c.Request.URL.Path == "/api/role/list" ||
|
||||||
|
c.Request.URL.Path == "/api/mj/jobs" ||
|
||||||
c.Request.URL.Path == "/api/mj/proxy" ||
|
c.Request.URL.Path == "/api/mj/proxy" ||
|
||||||
|
c.Request.URL.Path == "/api/sd/jobs" ||
|
||||||
strings.HasPrefix(c.Request.URL.Path, "/api/sms/") ||
|
strings.HasPrefix(c.Request.URL.Path, "/api/sms/") ||
|
||||||
strings.HasPrefix(c.Request.URL.Path, "/api/captcha/") ||
|
strings.HasPrefix(c.Request.URL.Path, "/api/captcha/") ||
|
||||||
strings.HasPrefix(c.Request.URL.Path, "/static/") ||
|
strings.HasPrefix(c.Request.URL.Path, "/static/") ||
|
||||||
|
@ -48,13 +48,15 @@ func (h *ChatRoleHandler) List(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := utils.GetLoginUser(c, h.db)
|
userId := h.GetInt(c, "user_id", 0)
|
||||||
if err != nil {
|
if userId == 0 {
|
||||||
resp.NotAuth(c)
|
resp.NotAuth(c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
var user model.User
|
||||||
|
h.db.First(&user, userId)
|
||||||
var roleKeys []string
|
var roleKeys []string
|
||||||
err = utils.JsonDecode(user.ChatRoles, &roleKeys)
|
err := utils.JsonDecode(user.ChatRoles, &roleKeys)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.ERROR(c, "角色解析失败!")
|
resp.ERROR(c, "角色解析失败!")
|
||||||
return
|
return
|
||||||
|
@ -14,12 +14,59 @@
|
|||||||
.inner {
|
.inner {
|
||||||
display flex
|
display flex
|
||||||
color #ffffff
|
color #ffffff
|
||||||
padding 20px;
|
padding 15px;
|
||||||
|
overflow-y visible
|
||||||
|
overflow-x hidden
|
||||||
|
|
||||||
.left-menu {
|
.list-box {
|
||||||
width 160px
|
.app-item {
|
||||||
|
border 1px solid #666666
|
||||||
|
border-radius 6px
|
||||||
|
overflow hidden
|
||||||
|
transition: all 0.3s ease; /* 添加过渡效果 */
|
||||||
|
|
||||||
|
.el-image {
|
||||||
|
padding 6px
|
||||||
|
|
||||||
|
.el-image__inner {
|
||||||
|
border-radius 10px
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
display flex
|
||||||
|
padding 10px
|
||||||
|
|
||||||
|
.name {
|
||||||
|
width 100%
|
||||||
|
text-align left
|
||||||
|
font-size 16px
|
||||||
|
font-weight bold
|
||||||
|
color #47fff1
|
||||||
|
}
|
||||||
|
|
||||||
|
.opt {
|
||||||
|
position: relative;
|
||||||
|
top -5px
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hello-msg {
|
||||||
|
overflow: hidden;
|
||||||
|
white-space normal
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
height 60px
|
||||||
|
padding 10px
|
||||||
|
font-size 14px
|
||||||
|
color #999999
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
box-shadow: 0 0 10px rgba(71, 255, 241, 0.6); /* 添加阴影效果 */
|
||||||
|
transform: translateY(-10px); /* 向上移动10像素 */
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,96 +1,115 @@
|
|||||||
.task-list-box {
|
.task-list-box {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.task-list-box .running-job-list .job-item {
|
.task-list-box .running-job-list .job-item {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
background-color: #555;
|
background-color: #555;
|
||||||
}
|
}
|
||||||
|
|
||||||
.task-list-box .running-job-list .job-item .job-item-inner {
|
.task-list-box .running-job-list .job-item .job-item-inner {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.task-list-box .running-job-list .job-item .job-item-inner .progress {
|
.task-list-box .running-job-list .job-item .job-item-inner .progress {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.task-list-box .running-job-list .job-item .job-item-inner .progress span {
|
.task-list-box .running-job-list .job-item .job-item-inner .progress span {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.task-list-box .finish-job-list .job-item {
|
.task-list-box .finish-job-list .job-item {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.task-list-box .finish-job-list .job-item .opt .opt-line {
|
.task-list-box .finish-job-list .job-item .opt .opt-line {
|
||||||
margin: 6px 0;
|
margin: 6px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.task-list-box .finish-job-list .job-item .opt .opt-line ul {
|
.task-list-box .finish-job-list .job-item .opt .opt-line ul {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: row;
|
flex-flow: row;
|
||||||
}
|
}
|
||||||
|
|
||||||
.task-list-box .finish-job-list .job-item .opt .opt-line ul li {
|
.task-list-box .finish-job-list .job-item .opt .opt-line ul li {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.task-list-box .finish-job-list .job-item .opt .opt-line ul li a {
|
.task-list-box .finish-job-list .job-item .opt .opt-line ul li a {
|
||||||
padding: 3px 0;
|
padding: 3px 0;
|
||||||
width: 44px;
|
width: 44px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
display: block;
|
display: block;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
background-color: #4e5058;
|
background-color: #4e5058;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.task-list-box .finish-job-list .job-item .opt .opt-line ul li a:hover {
|
.task-list-box .finish-job-list .job-item .opt .opt-line ul li a:hover {
|
||||||
background-color: #6d6f78;
|
background-color: #6d6f78;
|
||||||
}
|
}
|
||||||
|
|
||||||
.task-list-box .finish-job-list .job-item .opt .opt-line ul .show-prompt {
|
.task-list-box .finish-job-list .job-item .opt .opt-line ul .show-prompt {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.task-list-box .el-image {
|
.task-list-box .el-image {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
max-height: 240px;
|
max-height: 240px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.task-list-box .el-image img {
|
.task-list-box .el-image img {
|
||||||
height: 240px;
|
height: 240px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.task-list-box .el-image .el-image-viewer__wrapper img {
|
.task-list-box .el-image .el-image-viewer__wrapper img {
|
||||||
width: auto;
|
width: auto;
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.task-list-box .el-image .image-slot {
|
.task-list-box .el-image .image-slot {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: column;
|
flex-flow: column;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
min-height: 200px;
|
min-height: 200px;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.task-list-box .el-image .image-slot .iconfont {
|
.task-list-box .el-image .image-slot .iconfont {
|
||||||
font-size: 50px;
|
font-size: 50px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.task-list-box .el-image.upscale {
|
.task-list-box .el-image.upscale {
|
||||||
max-height: 304px;
|
max-height: 312px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.task-list-box .el-image.upscale img {
|
.task-list-box .el-image.upscale img {
|
||||||
height: 304px;
|
height: 312px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.task-list-box .el-image.upscale .el-image-viewer__wrapper img {
|
.task-list-box .el-image.upscale .el-image-viewer__wrapper img {
|
||||||
width: auto;
|
width: auto;
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,10 @@
|
|||||||
.job-item {
|
.job-item {
|
||||||
width 100%
|
width 100%
|
||||||
height 100%
|
height 100%
|
||||||
|
border 1px solid #666666
|
||||||
|
padding 6px
|
||||||
|
overflow hidden
|
||||||
|
border-radius 6px
|
||||||
|
|
||||||
.opt {
|
.opt {
|
||||||
.opt-line {
|
.opt-line {
|
||||||
@ -54,7 +58,7 @@
|
|||||||
|
|
||||||
a {
|
a {
|
||||||
padding 3px 0
|
padding 3px 0
|
||||||
width 44px
|
width 40px
|
||||||
text-align center
|
text-align center
|
||||||
border-radius 5px
|
border-radius 5px
|
||||||
display block
|
display block
|
||||||
@ -113,10 +117,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.el-image.upscale {
|
.el-image.upscale {
|
||||||
max-height 304px
|
max-height 310px
|
||||||
|
|
||||||
img {
|
img {
|
||||||
height 304px
|
height 310px
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-image-viewer__wrapper {
|
.el-image-viewer__wrapper {
|
||||||
|
@ -5,11 +5,11 @@
|
|||||||
class="list-item"
|
class="list-item"
|
||||||
v-for="(item, index) in items"
|
v-for="(item, index) in items"
|
||||||
:key="index"
|
:key="index"
|
||||||
:style="{width:itemWidth + 'px', marginBottom: margin*2+'px'}"
|
:style="{width:itemWidth + 'px'}"
|
||||||
>
|
>
|
||||||
<div :style="{marginLeft: margin+'px', marginRight: margin+'px'}">
|
<div class="item-inner" :style="{padding: gap/2+'px'}">
|
||||||
<div class="item-wrapper">
|
<div class="item-wrapper">
|
||||||
<slot :item="item" :index="index"></slot>
|
<slot :item="item" :index="index" :width="itemWidth"></slot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -21,6 +21,7 @@
|
|||||||
// 列表组件
|
// 列表组件
|
||||||
import {onMounted, ref} from "vue";
|
import {onMounted, ref} from "vue";
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
items: {
|
items: {
|
||||||
type: Array,
|
type: Array,
|
||||||
@ -28,38 +29,25 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
gap: {
|
gap: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 10
|
default: 12
|
||||||
},
|
},
|
||||||
width: {
|
width: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 240
|
default: 240
|
||||||
},
|
|
||||||
height: {
|
|
||||||
type: Number,
|
|
||||||
default: 240
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const container = ref(null)
|
const container = ref(null)
|
||||||
const itemWidth = ref(props.width)
|
const itemWidth = ref(props.width)
|
||||||
const margin = ref(props.gap)
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
computeSize()
|
computeSize()
|
||||||
})
|
})
|
||||||
|
|
||||||
const computeSize = () => {
|
const computeSize = () => {
|
||||||
const w = container.value.offsetWidth - 10 // 减去滚动条的宽度
|
const w = container.value.offsetWidth - 8 // 减去滚动条的宽度
|
||||||
let cols = Math.floor(w / props.width)
|
let cols = Math.floor(w / props.width)
|
||||||
itemWidth.value = Math.floor(w / cols) - 1
|
itemWidth.value = Math.floor(w / cols)
|
||||||
while (itemWidth.value < props.width && cols > 1) {
|
|
||||||
cols -= 1
|
|
||||||
itemWidth.value = Math.floor(w / cols) - 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if (props.gap > 0) {
|
|
||||||
margin.value = props.gap / 2
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window.onresize = () => {
|
window.onresize = () => {
|
||||||
@ -76,15 +64,14 @@ window.onresize = () => {
|
|||||||
flex-wrap wrap
|
flex-wrap wrap
|
||||||
|
|
||||||
.list-item {
|
.list-item {
|
||||||
|
.item-inner {
|
||||||
div {
|
|
||||||
display flex
|
display flex
|
||||||
height 100%
|
|
||||||
overflow hidden
|
|
||||||
|
|
||||||
.item-wrapper {
|
.item-wrapper {
|
||||||
height 100%
|
height 100%
|
||||||
width 100%
|
width 100%
|
||||||
|
display flex
|
||||||
|
justify-content center
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,30 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="page-apps">
|
<div class="page-apps custom-scroll">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
AI 助手应用中心
|
AI 助手应用中心
|
||||||
</div>
|
</div>
|
||||||
<div class="inner custom-scroll">
|
<div class="inner" :style="{height: listBoxHeight + 'px'}">
|
||||||
<div class="app-list">
|
<ItemList :items="list" v-if="list.length > 0" gap="20" width="250">
|
||||||
<div class="list-item" v-for="item in list" :key="item.id">
|
<template #default="scope">
|
||||||
<div v-if="item.key !=='gpt'">
|
<div class="app-item" :style="{width: scope.width+'px'}">
|
||||||
<el-image :src="item.icon"/>
|
<el-image :src="scope.item.icon" fit="cover" :style="{height: scope.width+'px'}"/>
|
||||||
|
<div class="title">
|
||||||
|
<span class="name">{{ scope.item.name }}</span>
|
||||||
|
<div class="opt">
|
||||||
|
<el-button size="small"
|
||||||
|
style="--el-color-primary:#009999"
|
||||||
|
@click="addRole(scope.item)">
|
||||||
|
<el-icon>
|
||||||
|
<Plus/>
|
||||||
|
</el-icon>
|
||||||
|
<span>添加应用</span>
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hello-msg">{{ scope.item['hello_msg'] }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
</div>
|
</ItemList>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -19,18 +33,31 @@
|
|||||||
import {onMounted, ref} from "vue"
|
import {onMounted, ref} from "vue"
|
||||||
import {ElMessage} from "element-plus";
|
import {ElMessage} from "element-plus";
|
||||||
import {httpGet} from "@/utils/http";
|
import {httpGet} from "@/utils/http";
|
||||||
|
import ItemList from "@/components/ItemList.vue";
|
||||||
|
import {Plus} from "@element-plus/icons-vue";
|
||||||
|
|
||||||
|
const listBoxHeight = window.innerHeight - 97
|
||||||
const list = ref([])
|
const list = ref([])
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
httpGet("/api/role/list?all=true").then((res) => {
|
httpGet("/api/role/list?all=true").then((res) => {
|
||||||
list.value = res.data
|
const data = res.data
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
if (data[i].key === 'gpt') {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
list.value.push(data[i])
|
||||||
|
}
|
||||||
}).catch(e => {
|
}).catch(e => {
|
||||||
ElMessage.error("获取应用失败:" + e.message)
|
ElMessage.error("获取应用失败:" + e.message)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const addRole = (row) => {
|
||||||
|
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="stylus" scoped>
|
<style lang="stylus">
|
||||||
@import "@/assets/css/chat-app.styl"
|
@import "@/assets/css/chat-app.styl"
|
||||||
@import "@/assets/css/custom-scroll.styl"
|
@import "@/assets/css/custom-scroll.styl"
|
||||||
</style>
|
</style>
|
||||||
|
@ -553,7 +553,7 @@ const connect = function (chat_id, role_id) {
|
|||||||
content: _role['hello_msg'],
|
content: _role['hello_msg'],
|
||||||
orgContent: _role['hello_msg'],
|
orgContent: _role['hello_msg'],
|
||||||
})
|
})
|
||||||
ElMessage.success({message: "对话连接成功!", duration: 500})
|
ElMessage.success({message: "对话连接成功!", duration: 1000})
|
||||||
} else { // 加载聊天记录
|
} else { // 加载聊天记录
|
||||||
loadChatHistory(chat_id);
|
loadChatHistory(chat_id);
|
||||||
}
|
}
|
||||||
|
@ -286,7 +286,7 @@
|
|||||||
|
|
||||||
<h2>创作记录</h2>
|
<h2>创作记录</h2>
|
||||||
<div class="finish-job-list">
|
<div class="finish-job-list">
|
||||||
<ItemList :items="finishedJobs" v-if="finishedJobs.length > 0">
|
<ItemList :items="finishedJobs" v-if="finishedJobs.length > 0" width="240">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<div class="job-item">
|
<div class="job-item">
|
||||||
<el-image
|
<el-image
|
||||||
@ -503,7 +503,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
const clipboard = new Clipboard('.copy-prompt');
|
const clipboard = new Clipboard('.copy-prompt');
|
||||||
clipboard.on('success', () => {
|
clipboard.on('success', () => {
|
||||||
ElMessage.success({message: "复制成功!", duration: 500});
|
ElMessage.success("复制成功!");
|
||||||
})
|
})
|
||||||
|
|
||||||
clipboard.on('error', () => {
|
clipboard.on('error', () => {
|
||||||
|
@ -289,7 +289,7 @@
|
|||||||
<div class="task-list-inner" :style="{ height: listBoxHeight + 'px' }">
|
<div class="task-list-inner" :style="{ height: listBoxHeight + 'px' }">
|
||||||
<h2>任务列表</h2>
|
<h2>任务列表</h2>
|
||||||
<div class="running-job-list">
|
<div class="running-job-list">
|
||||||
<ItemList :items="runningJobs" v-if="runningJobs.length > 0">
|
<ItemList :items="runningJobs" v-if="runningJobs.length > 0" width="240">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<div class="job-item">
|
<div class="job-item">
|
||||||
<el-popover
|
<el-popover
|
||||||
@ -645,7 +645,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
const clipboard = new Clipboard('.copy-prompt');
|
const clipboard = new Clipboard('.copy-prompt');
|
||||||
clipboard.on('success', () => {
|
clipboard.on('success', () => {
|
||||||
ElMessage.success({message: "复制成功!", duration: 500});
|
ElMessage.success("复制成功!");
|
||||||
})
|
})
|
||||||
|
|
||||||
clipboard.on('error', () => {
|
clipboard.on('error', () => {
|
||||||
|
@ -281,7 +281,7 @@ getNext()
|
|||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const clipboard = new Clipboard('.copy-prompt');
|
const clipboard = new Clipboard('.copy-prompt');
|
||||||
clipboard.on('success', () => {
|
clipboard.on('success', () => {
|
||||||
ElMessage.success({message: "复制成功!", duration: 500});
|
ElMessage.success("复制成功!");
|
||||||
})
|
})
|
||||||
|
|
||||||
clipboard.on('error', () => {
|
clipboard.on('error', () => {
|
||||||
@ -293,6 +293,7 @@ const changeImgType = () => {
|
|||||||
document.getElementById('waterfall-box').scrollTo(0, 0)
|
document.getElementById('waterfall-box').scrollTo(0, 0)
|
||||||
page.value = 0
|
page.value = 0
|
||||||
list.value = []
|
list.value = []
|
||||||
|
loading.value = true
|
||||||
isOver.value = false
|
isOver.value = false
|
||||||
nextTick(() => getNext())
|
nextTick(() => getNext())
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user