mirror of
https://gitee.com/devlive-community/datacap.git
synced 2024-12-02 20:17:45 +08:00
[Core] Add role
This commit is contained in:
parent
4f0956f8fa
commit
82c7595365
@ -0,0 +1,49 @@
|
||||
package io.edurt.datacap.server.controller.admin;
|
||||
|
||||
import io.edurt.datacap.server.body.FilterBody;
|
||||
import io.edurt.datacap.server.common.Response;
|
||||
import io.edurt.datacap.server.entity.PageEntity;
|
||||
import io.edurt.datacap.server.entity.RoleEntity;
|
||||
import io.edurt.datacap.server.repository.RoleRepository;
|
||||
import io.edurt.datacap.server.service.RoleService;
|
||||
import io.edurt.datacap.server.validation.ValidationGroup;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping(value = "/api/v1/admin/role")
|
||||
public class RoleController
|
||||
{
|
||||
private final RoleRepository roleRepository;
|
||||
private final RoleService roleService;
|
||||
|
||||
public RoleController(RoleRepository roleRepository, RoleService roleService)
|
||||
{
|
||||
this.roleRepository = roleRepository;
|
||||
this.roleService = roleService;
|
||||
}
|
||||
|
||||
@PostMapping(value = "list")
|
||||
public Response<PageEntity<RoleEntity>> getAllByFilter(@RequestBody FilterBody filter)
|
||||
{
|
||||
return this.roleService.getAll(roleRepository, filter);
|
||||
}
|
||||
|
||||
@RequestMapping(method = {RequestMethod.POST, RequestMethod.PUT})
|
||||
public Response<RoleEntity> save(@RequestBody @Validated(ValidationGroup.Crud.Create.class) RoleEntity configure)
|
||||
{
|
||||
return this.roleService.saveOrUpdate(roleRepository, configure);
|
||||
}
|
||||
|
||||
@GetMapping(value = "{id}")
|
||||
public Response<RoleEntity> getInfo(@PathVariable(value = "id") Long id)
|
||||
{
|
||||
return this.roleService.getById(roleRepository, id);
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package io.edurt.datacap.server.entity;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
@ -12,6 +13,7 @@ import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Transient;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
|
||||
@ -37,5 +39,22 @@ public class RoleEntity
|
||||
private String description;
|
||||
|
||||
@Column(name = "create_time", columnDefinition = "datetime(5) default CURRENT_TIMESTAMP()")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Timestamp createTime;
|
||||
|
||||
@Transient
|
||||
private boolean isDefault;
|
||||
|
||||
@Transient
|
||||
private String code;
|
||||
|
||||
public boolean isDefault()
|
||||
{
|
||||
return this.getCode().equals("ROLE_ADMIN") || this.getCode().equals("ROLE_USER") ? true : false;
|
||||
}
|
||||
|
||||
public String getCode()
|
||||
{
|
||||
return String.format("ROLE_%s", this.name.toUpperCase());
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,29 @@
|
||||
package io.edurt.datacap.server.service;
|
||||
|
||||
import io.edurt.datacap.server.adapter.PageRequestAdapter;
|
||||
import io.edurt.datacap.server.body.FilterBody;
|
||||
import io.edurt.datacap.server.common.Response;
|
||||
import io.edurt.datacap.server.entity.PageEntity;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
||||
|
||||
public interface BaseService<T>
|
||||
{
|
||||
default Response<PageEntity<T>> getAll(PagingAndSortingRepository repository, FilterBody filter)
|
||||
{
|
||||
Pageable pageable = PageRequestAdapter.of(filter);
|
||||
return Response.success(PageEntity.build(repository.findAll(pageable)));
|
||||
}
|
||||
|
||||
default Response<T> getById(PagingAndSortingRepository repository, Long id)
|
||||
{
|
||||
return Response.success(repository.findById(id));
|
||||
}
|
||||
|
||||
default Response<T> saveOrUpdate(PagingAndSortingRepository repository, T configure)
|
||||
{
|
||||
return Response.success(repository.save(configure));
|
||||
}
|
||||
|
||||
Response<Long> delete(Long id);
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package io.edurt.datacap.server.service;
|
||||
|
||||
import io.edurt.datacap.server.entity.RoleEntity;
|
||||
|
||||
public interface RoleService
|
||||
extends BaseService<RoleEntity>
|
||||
{
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package io.edurt.datacap.server.service.impl;
|
||||
|
||||
import io.edurt.datacap.server.common.Response;
|
||||
import io.edurt.datacap.server.repository.RoleRepository;
|
||||
import io.edurt.datacap.server.service.RoleService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class RoleServiceImpl
|
||||
implements RoleService
|
||||
{
|
||||
private final RoleRepository roleRepository;
|
||||
|
||||
public RoleServiceImpl(RoleRepository roleRepository)
|
||||
{
|
||||
this.roleRepository = roleRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response<Long> delete(Long id)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
@ -77,5 +77,8 @@ export default {
|
||||
ip: 'IP',
|
||||
ua: 'User Agent',
|
||||
loginTime: 'Login Time',
|
||||
ssl: 'SSL'
|
||||
ssl: 'SSL',
|
||||
id: 'ID',
|
||||
authority: 'Authority',
|
||||
code: 'Code'
|
||||
}
|
||||
|
@ -77,5 +77,8 @@ export default {
|
||||
ip: 'IP',
|
||||
ua: '用户代理',
|
||||
loginTime: '登录时间',
|
||||
ssl: 'SSL'
|
||||
ssl: 'SSL',
|
||||
id: 'ID',
|
||||
authority: '权限',
|
||||
code: '代码',
|
||||
}
|
||||
|
67
core/datacap-web/console-fe/src/model/Pagination.ts
Normal file
67
core/datacap-web/console-fe/src/model/Pagination.ts
Normal file
@ -0,0 +1,67 @@
|
||||
export class Pagination
|
||||
{
|
||||
current = 1;
|
||||
total = 0;
|
||||
size = 10;
|
||||
|
||||
constructor(builder: PaginationBuilder)
|
||||
{
|
||||
this.current = builder.current;
|
||||
this.total = builder.total;
|
||||
this.size = builder.size;
|
||||
}
|
||||
}
|
||||
|
||||
export class PaginationBuilder
|
||||
{
|
||||
private _current: number;
|
||||
private _total: number;
|
||||
private _size: number;
|
||||
|
||||
setCurrent(value: number)
|
||||
{
|
||||
this._current = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
setTotal(value: number)
|
||||
{
|
||||
this._total = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
setSize(value: number)
|
||||
{
|
||||
this._size = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
get current(): number
|
||||
{
|
||||
return this._current;
|
||||
}
|
||||
|
||||
get total(): number
|
||||
{
|
||||
return this._total;
|
||||
}
|
||||
|
||||
get size(): number
|
||||
{
|
||||
return this._size;
|
||||
}
|
||||
|
||||
build()
|
||||
{
|
||||
return new Pagination(this);
|
||||
}
|
||||
|
||||
static newInstance(): Pagination
|
||||
{
|
||||
return new PaginationBuilder()
|
||||
.setCurrent(1)
|
||||
.setTotal(0)
|
||||
.setSize(10)
|
||||
.build();
|
||||
}
|
||||
}
|
@ -135,6 +135,13 @@ const routes: Array<RouteRecordRaw> = [
|
||||
component: () => import("../views/pages/admin/monitor/processor/ProcessorAdmin.vue")
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: "role",
|
||||
meta: {
|
||||
roles: ['Admin']
|
||||
},
|
||||
component: () => import("@/views/admin/role/RoleHome.vue")
|
||||
}
|
||||
]
|
||||
},
|
||||
|
62
core/datacap-web/console-fe/src/services/BaseService.ts
Normal file
62
core/datacap-web/console-fe/src/services/BaseService.ts
Normal file
@ -0,0 +1,62 @@
|
||||
import {Filter} from "@/model/Filter";
|
||||
import {ResponseModel} from "@/model/ResponseModel";
|
||||
import {HttpCommon} from "@/common/HttpCommon";
|
||||
|
||||
export abstract class BaseService<T>
|
||||
{
|
||||
private readonly baseUrl: string;
|
||||
|
||||
protected constructor(baseUrl: string)
|
||||
{
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the corresponding data according to the filter
|
||||
* <p>
|
||||
* If the filter is empty, all data will be returned.
|
||||
* Supported filters:
|
||||
* <ul>
|
||||
* <li>paging</li>
|
||||
* <li>order</li>
|
||||
* </ul>
|
||||
* </p>
|
||||
* @param {Filter} filter
|
||||
* @param filter
|
||||
*/
|
||||
getAll<T>(filter: Filter): Promise<ResponseModel>
|
||||
{
|
||||
return new HttpCommon().post(`${this.baseUrl}/list`, filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the corresponding data according to the id
|
||||
* @param id
|
||||
*/
|
||||
getById<T>(id: number): Promise<ResponseModel>
|
||||
{
|
||||
return new HttpCommon().get(`${this.baseUrl}/${id}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Report an error or update data, if id > 0 in the data, it is updated, otherwise it is saved
|
||||
* @param configure
|
||||
*/
|
||||
saveOrUpdate<T>(configure: T): Promise<ResponseModel>
|
||||
{
|
||||
if (configure['id'] > 0) {
|
||||
return new HttpCommon().put(this.baseUrl, configure);
|
||||
}
|
||||
else {
|
||||
return new HttpCommon().post(this.baseUrl, configure);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the corresponding data according to the name
|
||||
* @param name
|
||||
*/
|
||||
abstract getByName<T>(name: string): Promise<ResponseModel>;
|
||||
|
||||
abstract deleteById(id: number): Promise<ResponseModel>;
|
||||
}
|
58
core/datacap-web/console-fe/src/services/admin/RoleModel.ts
Normal file
58
core/datacap-web/console-fe/src/services/admin/RoleModel.ts
Normal file
@ -0,0 +1,58 @@
|
||||
export class Role
|
||||
{
|
||||
id = 0;
|
||||
name: string;
|
||||
description: string;
|
||||
|
||||
constructor(builder: RoleBuilder)
|
||||
{
|
||||
this.id = builder.id;
|
||||
this.name = builder.name;
|
||||
this.description = builder.description;
|
||||
}
|
||||
}
|
||||
|
||||
export class RoleBuilder
|
||||
{
|
||||
private _id: number;
|
||||
private _name: string;
|
||||
private _description: string;
|
||||
|
||||
setId(value: number)
|
||||
{
|
||||
this._id = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
setName(value: string)
|
||||
{
|
||||
this._name = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
setDescription(value: string)
|
||||
{
|
||||
this._description = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
get id(): number
|
||||
{
|
||||
return this._id;
|
||||
}
|
||||
|
||||
get name(): string
|
||||
{
|
||||
return this._name;
|
||||
}
|
||||
|
||||
get description(): string
|
||||
{
|
||||
return this._description;
|
||||
}
|
||||
|
||||
build()
|
||||
{
|
||||
return new Role(this);
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
import {ResponseModel} from '@/model/ResponseModel'
|
||||
import {BaseService} from "@/services/BaseService";
|
||||
import {Role} from "@/services/admin/RoleModel";
|
||||
|
||||
const baseUrl = '/api/v1/admin/role'
|
||||
|
||||
class RoleService
|
||||
extends BaseService<Role>
|
||||
{
|
||||
|
||||
constructor()
|
||||
{
|
||||
super(baseUrl);
|
||||
}
|
||||
|
||||
deleteById(id: number): Promise<ResponseModel>
|
||||
{
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
getByName<T>(name: string): Promise<ResponseModel>
|
||||
{
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
export default new RoleService()
|
135
core/datacap-web/console-fe/src/views/admin/role/RoleDetails.vue
Normal file
135
core/datacap-web/console-fe/src/views/admin/role/RoleDetails.vue
Normal file
@ -0,0 +1,135 @@
|
||||
<template>
|
||||
<div>
|
||||
<Drawer :title="title" :width="720" :closable="false" v-model="visible" :maskClosable="false" :z-index="9">
|
||||
<Form :model="formState" ref="formValidate" :rules="ruleValidate">
|
||||
<FormItem v-if="formState.id > 0" :label="$t('common.id')" :label-width="80" prop="id">
|
||||
<Input v-model="formState.id" :disabled="formState.id > 0"/>
|
||||
</FormItem>
|
||||
<FormItem :label="$t('common.name')" :label-width="80" prop="name">
|
||||
<Input v-model="formState.name"/>
|
||||
</FormItem>
|
||||
<FormItem :label="$t('common.description')" :label-width="80" prop="description">
|
||||
<Input type="textarea" :rows="10" v-model="formState.description"/>
|
||||
</FormItem>
|
||||
</Form>
|
||||
<div class="datacap-drawer-footer">
|
||||
<Button :disabled="saving" style="margin-right: 8px" @click="handlerCancel()">{{ $t('common.cancel') }}</Button>
|
||||
<Button type="primary" :loading="saving" @click="handlerSave()">{{ $t('common.submit') }}</Button>
|
||||
</div>
|
||||
<Spin size="large" fix :show="loading"></Spin>
|
||||
</Drawer>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import {defineComponent, reactive} from 'vue';
|
||||
import {Role, RoleBuilder} from "@/services/admin/RoleModel";
|
||||
import {useI18n} from "vue-i18n";
|
||||
import RoleService from "@/services/admin/RoleService";
|
||||
|
||||
export default defineComponent({
|
||||
name: 'RoleDetails',
|
||||
props: {
|
||||
isVisible: {
|
||||
type: Boolean,
|
||||
default: () => false
|
||||
},
|
||||
id: {
|
||||
type: Number,
|
||||
default: () => 0
|
||||
}
|
||||
},
|
||||
data()
|
||||
{
|
||||
return {
|
||||
loading: false,
|
||||
saving: false,
|
||||
formState: null as Role,
|
||||
title: null,
|
||||
ruleValidate: {
|
||||
name: [
|
||||
{required: true}
|
||||
],
|
||||
description: [
|
||||
{required: true}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
created()
|
||||
{
|
||||
const role: Role = new RoleBuilder().build();
|
||||
this.formState = reactive<Role>(role);
|
||||
this.handlerInitialize();
|
||||
},
|
||||
methods: {
|
||||
handlerInitialize()
|
||||
{
|
||||
const i18n = useI18n();
|
||||
this.title = i18n.t('common.create');
|
||||
if (this.id > 0) {
|
||||
this.title = i18n.t('common.modify');
|
||||
this.loading = true;
|
||||
RoleService.getById(this.id)
|
||||
.then(response => {
|
||||
if (response.status) {
|
||||
this.formState = reactive<Role>(response.data);
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
this.title += i18n.t('common.authority');
|
||||
},
|
||||
handlerSave()
|
||||
{
|
||||
this.$refs['formValidate'].validate((valid) => {
|
||||
if (valid) {
|
||||
this.saving = true;
|
||||
RoleService.saveOrUpdate(this.formState)
|
||||
.then(response => {
|
||||
if (response.status) {
|
||||
this.$Message.success("successful");
|
||||
this.handlerCancel();
|
||||
}
|
||||
else {
|
||||
this.$Message.error(response.message);
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.saving = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
handlerCancel()
|
||||
{
|
||||
this.visible = false;
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
visible: {
|
||||
get(): boolean
|
||||
{
|
||||
return this.isVisible
|
||||
},
|
||||
set(value: boolean)
|
||||
{
|
||||
this.$emit('close', value)
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style scoped>
|
||||
.datacap-drawer-footer {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
border-top: 1px solid #e8e8e8;
|
||||
padding: 10px 16px;
|
||||
text-align: right;
|
||||
background: #fff;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,39 @@
|
||||
const createHeaders = (i18n: any) => {
|
||||
return [
|
||||
{
|
||||
title: i18n.t('common.id'),
|
||||
key: 'id',
|
||||
ellipsis: true,
|
||||
tooltip: true
|
||||
},
|
||||
{
|
||||
title: i18n.t('common.name'),
|
||||
key: 'name',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: i18n.t('common.code'),
|
||||
key: 'code',
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
title: i18n.t('common.description'),
|
||||
key: 'description',
|
||||
},
|
||||
{
|
||||
title: i18n.t('common.createTime'),
|
||||
key: 'createTime',
|
||||
},
|
||||
{
|
||||
title: i18n.t('common.action'),
|
||||
slot: 'action',
|
||||
key: 'action',
|
||||
width: 150,
|
||||
align: 'center'
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
export {
|
||||
createHeaders
|
||||
}
|
112
core/datacap-web/console-fe/src/views/admin/role/RoleHome.vue
Normal file
112
core/datacap-web/console-fe/src/views/admin/role/RoleHome.vue
Normal file
@ -0,0 +1,112 @@
|
||||
<template>
|
||||
<div>
|
||||
<Card style="width:100%" :title="$t('common.authority')">
|
||||
<template #extra>
|
||||
<Tooltip>
|
||||
<template #content>{{ $t('common.create') }}</template>
|
||||
<Button type="primary" shape="circle" icon="md-add" size="small" @click="handlerVisibleDetail(null, true)"/>
|
||||
</Tooltip>
|
||||
</template>
|
||||
<Table :loading="loading" :columns="headers" :data="finalData?.content">
|
||||
<template #action="{ row }">
|
||||
<Space>
|
||||
<Tooltip :content="$t('common.modify')" transfer>
|
||||
<Button shape="circle" :disabled="row.default" type="primary" size="small" icon="md-create" @click="handlerVisibleDetail(row.id, true)"/>
|
||||
</Tooltip>
|
||||
</Space>
|
||||
</template>
|
||||
</Table>
|
||||
<p v-if="!loading" style="margin-top: 10px;">
|
||||
<Page v-model="pagination.current" :total="pagination.total" :page-size="pagination.size" show-sizer show-elevator show-total
|
||||
@on-page-size-change="handlerSizeChange" @on-change="handlerIndexChange"/>
|
||||
</p>
|
||||
</Card>
|
||||
<RoleDetails v-if="visibleDetail" :isVisible="visibleDetail" :id="applyId" @close="handlerVisibleDetail(null, $event)"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {defineComponent} from 'vue'
|
||||
import {useI18n} from 'vue-i18n';
|
||||
import {createHeaders} from "@/views/admin/role/RoleGenerate";
|
||||
import RoleService from "@/services/admin/RoleService";
|
||||
import {Filter} from "@/model/Filter";
|
||||
import {ResponsePage} from "@/model/ResponsePage";
|
||||
import RoleDetails from "@/views/admin/role/RoleDetails.vue";
|
||||
import {Pagination, PaginationBuilder} from "@/model/Pagination";
|
||||
|
||||
const filter: Filter = new Filter();
|
||||
const pagination: Pagination = PaginationBuilder.newInstance();
|
||||
export default defineComponent({
|
||||
name: 'AdminRoleHome',
|
||||
components: {RoleDetails},
|
||||
setup()
|
||||
{
|
||||
const i18n = useI18n();
|
||||
const headers = createHeaders(i18n);
|
||||
return {
|
||||
headers,
|
||||
filter,
|
||||
pagination
|
||||
}
|
||||
},
|
||||
data()
|
||||
{
|
||||
return {
|
||||
loading: false,
|
||||
visibleDetail: false,
|
||||
applyId: null,
|
||||
finalData: null as ResponsePage
|
||||
}
|
||||
},
|
||||
created()
|
||||
{
|
||||
this.handlerInitialize(this.filter);
|
||||
},
|
||||
methods: {
|
||||
handlerInitialize(filter: Filter)
|
||||
{
|
||||
this.loading = true;
|
||||
RoleService.getAll(filter)
|
||||
.then((response) => {
|
||||
if (response.status) {
|
||||
this.finalData = response.data;
|
||||
this.pagination.total = response.data.total;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
handlerVisibleDetail(value: number, isOpened: boolean)
|
||||
{
|
||||
if (isOpened) {
|
||||
this.applyId = value;
|
||||
this.visibleDetail = true;
|
||||
}
|
||||
else {
|
||||
this.applyId = null;
|
||||
this.visibleDetail = false;
|
||||
}
|
||||
},
|
||||
handlerSizeChange(size: number)
|
||||
{
|
||||
this.pagination.size = size;
|
||||
this.handlerTableChange(this.pagination);
|
||||
},
|
||||
handlerIndexChange(index: number)
|
||||
{
|
||||
this.pagination.current = index;
|
||||
this.handlerTableChange(this.pagination);
|
||||
},
|
||||
handlerTableChange(pagination: any)
|
||||
{
|
||||
this.pagination.current = pagination.current;
|
||||
this.pagination.size = pagination.size;
|
||||
this.filter.page = pagination.current;
|
||||
this.filter.size = pagination.size;
|
||||
this.handlerInitialize(this.filter);
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
Loading…
Reference in New Issue
Block a user