mirror of
https://github.com/cool-team-official/cool-admin-vue.git
synced 2024-11-01 14:10:27 +08:00
优化
This commit is contained in:
parent
5066bd0691
commit
3a55028265
@ -9,7 +9,7 @@
|
|||||||
"lint:eslint": "eslint \"./src/**/*.{vue,ts,tsx}\" --fix"
|
"lint:eslint": "eslint \"./src/**/*.{vue,ts,tsx}\" --fix"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cool-vue/crud": "^7.1.12",
|
"@cool-vue/crud": "^7.1.13",
|
||||||
"@element-plus/icons-vue": "^2.1.0",
|
"@element-plus/icons-vue": "^2.1.0",
|
||||||
"@vueuse/core": "^10.4.0",
|
"@vueuse/core": "^10.4.0",
|
||||||
"@wangeditor/editor": "^5.1.23",
|
"@wangeditor/editor": "^5.1.23",
|
||||||
|
2
packages/crud/index.d.ts
vendored
2
packages/crud/index.d.ts
vendored
@ -650,8 +650,10 @@ declare namespace ClAdvSearch {
|
|||||||
|
|
||||||
declare namespace ClSearch {
|
declare namespace ClSearch {
|
||||||
interface Config<T = any> {
|
interface Config<T = any> {
|
||||||
|
inline?: boolean;
|
||||||
items?: ClForm.Item[];
|
items?: ClForm.Item[];
|
||||||
data?: T;
|
data?: T;
|
||||||
|
props?: ElementPlus.FormProps;
|
||||||
resetBtn?: boolean;
|
resetBtn?: boolean;
|
||||||
onLoad?(data: T): void;
|
onLoad?(data: T): void;
|
||||||
onSearch?(data: T, options: { next: ClCrud.Service["api"]["page"] }): void;
|
onSearch?(data: T, options: { next: ClCrud.Service["api"]["page"] }): void;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@cool-vue/crud",
|
"name": "@cool-vue/crud",
|
||||||
"version": "7.1.12",
|
"version": "7.1.13",
|
||||||
"private": false,
|
"private": false,
|
||||||
"main": "./dist/index.umd.min.js",
|
"main": "./dist/index.umd.min.js",
|
||||||
"typings": "types/index.d.ts",
|
"typings": "types/index.d.ts",
|
||||||
|
@ -451,11 +451,17 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let span = e.span || style.form.span;
|
||||||
|
|
||||||
|
if (browser.isMini) {
|
||||||
|
span = 24;
|
||||||
|
}
|
||||||
|
|
||||||
// 是否行内
|
// 是否行内
|
||||||
const Item = props.inline ? (
|
const Item = props.inline ? (
|
||||||
FormItem
|
FormItem
|
||||||
) : (
|
) : (
|
||||||
<el-col span={e.span || style.form.span} {...e.col} v-show={inGroup && !e._hidden}>
|
<el-col span={span} {...e.col} v-show={inGroup && !e._hidden}>
|
||||||
{FormItem}
|
{FormItem}
|
||||||
</el-col>
|
</el-col>
|
||||||
);
|
);
|
||||||
@ -468,6 +474,12 @@ export default defineComponent({
|
|||||||
// 表单项列表
|
// 表单项列表
|
||||||
const children = config.items.map(renderFormItem);
|
const children = config.items.map(renderFormItem);
|
||||||
|
|
||||||
|
// 表单标签位置
|
||||||
|
const labelPosition =
|
||||||
|
browser.isMini && !props.inline
|
||||||
|
? "top"
|
||||||
|
: config.props.labelPosition || style.form.labelPosition;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="cl-form__container" ref={setRefs("form")}>
|
<div class="cl-form__container" ref={setRefs("form")}>
|
||||||
{h(
|
{h(
|
||||||
@ -486,29 +498,23 @@ export default defineComponent({
|
|||||||
/>,
|
/>,
|
||||||
{
|
{
|
||||||
...config.props,
|
...config.props,
|
||||||
labelPosition:
|
labelPosition
|
||||||
browser.isMini && !props.inline
|
|
||||||
? "top"
|
|
||||||
: config.props.labelPosition || style.form.labelPosition
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
default: () => {
|
default: () => {
|
||||||
|
const items = [
|
||||||
|
slots.prepend && slots.prepend({ scope: form }),
|
||||||
|
children,
|
||||||
|
slots.append && slots.append({ scope: form })
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="cl-form__items">
|
<div class="cl-form__items" v-loading={loading.value}>
|
||||||
{/* 前 */}
|
|
||||||
{slots.prepend && slots.prepend({ scope: form })}
|
|
||||||
|
|
||||||
{/* 项 */}
|
|
||||||
{props.inline ? (
|
{props.inline ? (
|
||||||
children
|
items
|
||||||
) : (
|
) : (
|
||||||
<el-row gutter={10} v-loading={loading.value}>
|
<el-row gutter={10}>{items}</el-row>
|
||||||
{children}
|
|
||||||
</el-row>
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 后 */}
|
|
||||||
{slots.append && slots.append({ scope: form })}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,15 @@ export default defineComponent({
|
|||||||
name: "cl-search",
|
name: "cl-search",
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
|
inline: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
},
|
||||||
|
|
||||||
// 表单值
|
// 表单值
|
||||||
data: {
|
data: {
|
||||||
type: Object,
|
type: Object,
|
||||||
@ -114,6 +123,7 @@ export default defineComponent({
|
|||||||
op: {
|
op: {
|
||||||
hidden: true
|
hidden: true
|
||||||
},
|
},
|
||||||
|
props: config.props,
|
||||||
items: config.items,
|
items: config.items,
|
||||||
form: config.data,
|
form: config.data,
|
||||||
on: {
|
on: {
|
||||||
@ -129,12 +139,12 @@ export default defineComponent({
|
|||||||
isEmpty(config.items) || (
|
isEmpty(config.items) || (
|
||||||
<div class="cl-search">
|
<div class="cl-search">
|
||||||
{h(
|
{h(
|
||||||
<cl-form ref={Form} inner inline />,
|
<cl-form ref={Form} inner inline={config.inline} />,
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
append() {
|
append() {
|
||||||
return (
|
return (
|
||||||
<el-form-item>
|
<el-form-item label=" " class="cl-search__btns">
|
||||||
{/* 搜索按钮 */}
|
{/* 搜索按钮 */}
|
||||||
<el-button
|
<el-button
|
||||||
type="primary"
|
type="primary"
|
||||||
|
@ -150,32 +150,26 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.el-select {
|
.el-select {
|
||||||
min-width: 100px;
|
min-width: 120px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.cl-search {
|
.cl-search {
|
||||||
margin-bottom: 0px !important;
|
margin-bottom: 0px !important;
|
||||||
|
|
||||||
.el-form--inline {
|
.el-form:not(.el-form--label-top) {
|
||||||
.el-form-item {
|
.cl-search__btns {
|
||||||
margin: 0 10px 10px 0;
|
.el-form-item__label {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.el-date-editor {
|
.el-form:not(.el-form--inline) {
|
||||||
box-sizing: border-box;
|
.cl-search__btns {
|
||||||
|
|
||||||
.el-range-input {
|
|
||||||
&:nth-child(2) {
|
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
margin-right: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.cl-adv-btn {
|
.cl-adv-btn {
|
||||||
@ -254,39 +248,6 @@
|
|||||||
min-height: 0;
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-form-item {
|
|
||||||
.el-input-number {
|
|
||||||
&__decrease,
|
|
||||||
&__increase {
|
|
||||||
border: 0;
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__label {
|
|
||||||
.el-tooltip {
|
|
||||||
i {
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__content {
|
|
||||||
min-width: 0px;
|
|
||||||
|
|
||||||
& > div {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.no-label {
|
|
||||||
& > .el-form-item__label {
|
|
||||||
padding: 0;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&-item {
|
&-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
@ -329,10 +290,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-form-item {
|
|
||||||
margin-bottom: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-table__header tr {
|
.el-table__header tr {
|
||||||
line-height: normal;
|
line-height: normal;
|
||||||
}
|
}
|
||||||
@ -346,6 +303,78 @@
|
|||||||
.cl-crud {
|
.cl-crud {
|
||||||
line-height: normal;
|
line-height: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.el-form-item {
|
||||||
|
margin-bottom: 18px;
|
||||||
|
|
||||||
|
.el-input-number {
|
||||||
|
&__decrease,
|
||||||
|
&__increase {
|
||||||
|
border: 0;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__label {
|
||||||
|
.el-tooltip {
|
||||||
|
i {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
min-width: 0px;
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(.el-form--label-top) {
|
||||||
|
&.no-label {
|
||||||
|
& > .el-form-item__label {
|
||||||
|
padding: 0;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-form--label-top {
|
||||||
|
.el-form-item__label {
|
||||||
|
min-height: 22px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-form--inline {
|
||||||
|
.cl-form__items {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-form-item {
|
||||||
|
margin: 0 10px 10px 0;
|
||||||
|
|
||||||
|
.el-date-editor {
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
.el-range-input {
|
||||||
|
&:nth-child(2) {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-select {
|
||||||
|
width: 173px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.cl-form-tabs {
|
.cl-form-tabs {
|
||||||
|
@ -5,7 +5,7 @@ declare const _default: import("vue").DefineComponent<{
|
|||||||
inner: BooleanConstructor;
|
inner: BooleanConstructor;
|
||||||
inline: BooleanConstructor;
|
inline: BooleanConstructor;
|
||||||
}>>, {
|
}>>, {
|
||||||
inner: boolean;
|
|
||||||
inline: boolean;
|
inline: boolean;
|
||||||
|
inner: boolean;
|
||||||
}, {}>;
|
}, {}>;
|
||||||
export default _default;
|
export default _default;
|
||||||
|
18
packages/crud/types/components/search/index.d.ts
vendored
18
packages/crud/types/components/search/index.d.ts
vendored
@ -1,6 +1,14 @@
|
|||||||
/// <reference types="../index" />
|
/// <reference types="../index" />
|
||||||
import { PropType } from "vue";
|
import { PropType } from "vue";
|
||||||
declare const _default: import("vue").DefineComponent<{
|
declare const _default: import("vue").DefineComponent<{
|
||||||
|
inline: {
|
||||||
|
type: BooleanConstructor;
|
||||||
|
default: boolean;
|
||||||
|
};
|
||||||
|
props: {
|
||||||
|
type: ObjectConstructor;
|
||||||
|
default: () => void;
|
||||||
|
};
|
||||||
data: {
|
data: {
|
||||||
type: ObjectConstructor;
|
type: ObjectConstructor;
|
||||||
default: () => {};
|
default: () => {};
|
||||||
@ -16,6 +24,14 @@ declare const _default: import("vue").DefineComponent<{
|
|||||||
onLoad: FunctionConstructor;
|
onLoad: FunctionConstructor;
|
||||||
onSearch: FunctionConstructor;
|
onSearch: FunctionConstructor;
|
||||||
}, () => any, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, "reset"[], "reset", import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
}, () => any, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, "reset"[], "reset", import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||||
|
inline: {
|
||||||
|
type: BooleanConstructor;
|
||||||
|
default: boolean;
|
||||||
|
};
|
||||||
|
props: {
|
||||||
|
type: ObjectConstructor;
|
||||||
|
default: () => void;
|
||||||
|
};
|
||||||
data: {
|
data: {
|
||||||
type: ObjectConstructor;
|
type: ObjectConstructor;
|
||||||
default: () => {};
|
default: () => {};
|
||||||
@ -34,6 +50,8 @@ declare const _default: import("vue").DefineComponent<{
|
|||||||
onReset?: ((...args: any[]) => any) | undefined;
|
onReset?: ((...args: any[]) => any) | undefined;
|
||||||
}, {
|
}, {
|
||||||
items: ClForm.Item<any>[];
|
items: ClForm.Item<any>[];
|
||||||
|
props: Record<string, any>;
|
||||||
|
inline: boolean;
|
||||||
data: Record<string, any>;
|
data: Record<string, any>;
|
||||||
resetBtn: boolean;
|
resetBtn: boolean;
|
||||||
}, {}>;
|
}, {}>;
|
||||||
|
152
src/modules/demo/views/crud/components/search/layout.vue
Normal file
152
src/modules/demo/views/crud/components/search/layout.vue
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
<template>
|
||||||
|
<div class="scope">
|
||||||
|
<div class="h">
|
||||||
|
<el-tag size="small" effect="dark">layout</el-tag>
|
||||||
|
<span>布局</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="c">
|
||||||
|
<el-button @click="open">预览</el-button>
|
||||||
|
<demo-code :files="['search/layout.vue']" />
|
||||||
|
|
||||||
|
<!-- 自定义表格组件 -->
|
||||||
|
<cl-dialog v-model="visible" title="布局" width="80%">
|
||||||
|
<cl-crud ref="Crud">
|
||||||
|
<!--【很重要】搜索组件 -->
|
||||||
|
<cl-search ref="Search" :reset-btn="true" />
|
||||||
|
|
||||||
|
<cl-row>
|
||||||
|
<cl-table ref="Table" />
|
||||||
|
</cl-row>
|
||||||
|
|
||||||
|
<cl-row>
|
||||||
|
<cl-flex1 />
|
||||||
|
<cl-pagination />
|
||||||
|
</cl-row>
|
||||||
|
</cl-crud>
|
||||||
|
</cl-dialog>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="f">
|
||||||
|
<span class="date">2024-01-01</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useCrud, useSearch, useTable } from "@cool-vue/crud";
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { useDict } from "/$/dict";
|
||||||
|
|
||||||
|
const { dict } = useDict();
|
||||||
|
|
||||||
|
// cl-crud 配置
|
||||||
|
const Crud = useCrud(
|
||||||
|
{
|
||||||
|
service: "test"
|
||||||
|
},
|
||||||
|
(app) => {
|
||||||
|
app.refresh();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// cl-table 配置
|
||||||
|
const Table = useTable({
|
||||||
|
autoHeight: false,
|
||||||
|
contextMenu: ["refresh"],
|
||||||
|
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
label: "姓名",
|
||||||
|
prop: "name",
|
||||||
|
minWidth: 140
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "手机号",
|
||||||
|
prop: "phone",
|
||||||
|
minWidth: 140
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "工作",
|
||||||
|
prop: "occupation",
|
||||||
|
dict: dict.get("occupation"),
|
||||||
|
minWidth: 140
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "创建时间",
|
||||||
|
prop: "createTime",
|
||||||
|
minWidth: 160,
|
||||||
|
sortable: "desc"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
// cl-search 配置
|
||||||
|
//【很重要】该组件基于 cl-form 故很多示例都可复用
|
||||||
|
const Search = useSearch({
|
||||||
|
// 取消行内表单模式
|
||||||
|
inline: false,
|
||||||
|
|
||||||
|
// 表单参数
|
||||||
|
props: {
|
||||||
|
labelPosition: "top"
|
||||||
|
},
|
||||||
|
|
||||||
|
// 配置如 cl-form 一样
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
label: "姓名",
|
||||||
|
prop: "name",
|
||||||
|
span: 6,
|
||||||
|
component: {
|
||||||
|
name: "el-input",
|
||||||
|
props: {
|
||||||
|
clearable: true,
|
||||||
|
|
||||||
|
// 值改变的时候刷新列表
|
||||||
|
onChange(val: string) {
|
||||||
|
refresh({
|
||||||
|
name: val,
|
||||||
|
page: 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "手机号",
|
||||||
|
prop: "phone",
|
||||||
|
span: 6,
|
||||||
|
component: {
|
||||||
|
name: "el-input",
|
||||||
|
props: {
|
||||||
|
clearable: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "工作",
|
||||||
|
prop: "occupation",
|
||||||
|
span: 6,
|
||||||
|
component: {
|
||||||
|
name: "cl-select",
|
||||||
|
props: {
|
||||||
|
tree: true,
|
||||||
|
checkStrictly: true,
|
||||||
|
options: dict.get("occupation")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
function refresh(params?: any) {
|
||||||
|
Crud.value?.refresh(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
const visible = ref(false);
|
||||||
|
|
||||||
|
function open() {
|
||||||
|
visible.value = true;
|
||||||
|
}
|
||||||
|
</script>
|
@ -71,6 +71,7 @@ import UpsertHook from "./components/upsert/hook/index.vue";
|
|||||||
|
|
||||||
import SearchBase from "./components/search/base.vue";
|
import SearchBase from "./components/search/base.vue";
|
||||||
import SearchCustom from "./components/search/custom.vue";
|
import SearchCustom from "./components/search/custom.vue";
|
||||||
|
import SearchLayout from "./components/search/layout.vue";
|
||||||
|
|
||||||
import AdvSearchBase from "./components/adv-search/base.vue";
|
import AdvSearchBase from "./components/adv-search/base.vue";
|
||||||
import AdvSearchCustom from "./components/adv-search/custom.vue";
|
import AdvSearchCustom from "./components/adv-search/custom.vue";
|
||||||
@ -165,7 +166,7 @@ const list = [
|
|||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
label: "基础",
|
label: "基础",
|
||||||
children: [SearchBase, SearchCustom]
|
children: [SearchBase, SearchCustom, SearchLayout]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -282,10 +282,10 @@
|
|||||||
"@babel/helper-validator-identifier" "^7.22.20"
|
"@babel/helper-validator-identifier" "^7.22.20"
|
||||||
to-fast-properties "^2.0.0"
|
to-fast-properties "^2.0.0"
|
||||||
|
|
||||||
"@cool-vue/crud@^7.1.12":
|
"@cool-vue/crud@^7.1.13":
|
||||||
version "7.1.12"
|
version "7.1.13"
|
||||||
resolved "https://registry.yarnpkg.com/@cool-vue/crud/-/crud-7.1.12.tgz#e1b703da12f3f4519ffdde73538d1d7a642fdb56"
|
resolved "https://registry.npmjs.org/@cool-vue/crud/-/crud-7.1.13.tgz#8c150d0f0ccbe466628a1190ac19219054e93cca"
|
||||||
integrity sha512-a5+9uBCQWUnibtpZU+q+gQDyZCAvjx2/bC1aFDM6XQ3+3C/4A0jxeZessBlXh3DaleYbLu8Kp2dBty8mVuy//g==
|
integrity sha512-e5Mt5HjYpr89sF+dMqudGHrcSP8KVdzNrn8hHEqgcpc1J7oAxyMtS8Wq2XjJY4XkxSZP7xeBEC6yqNgSQOYKyw==
|
||||||
dependencies:
|
dependencies:
|
||||||
array.prototype.flat "^1.2.4"
|
array.prototype.flat "^1.2.4"
|
||||||
core-js "^3.21.1"
|
core-js "^3.21.1"
|
||||||
|
Loading…
Reference in New Issue
Block a user