Add pagination to admin management tabs (cases/categories/sources/orgs)
This commit is contained in:
+202
-11
@@ -26,9 +26,11 @@
|
||||
<el-button size="default" :type="activeTab === 'tasklogs' ? 'primary' : ''" @click="switchTab('tasklogs')">任务日志</el-button>
|
||||
<el-button size="default" :type="activeTab === 'llmconfigs' ? 'primary' : ''" @click="switchTab('llmconfigs')">LLM配置</el-button>
|
||||
<el-button size="default" :type="activeTab === 'systemconfigs' ? 'primary' : ''" @click="switchTab('systemconfigs')">系统配置</el-button>
|
||||
<el-button size="default" :type="activeTab === 'users' ? 'primary' : ''" @click="switchTab('users')">用户管理</el-button>
|
||||
<el-button size="default" :type="activeTab === 'categories' ? 'primary' : ''" @click="switchTab('categories')">采集类别</el-button>
|
||||
<el-button size="default" :type="activeTab === 'sources' ? 'primary' : ''" @click="switchTab('sources')">信息源</el-button>
|
||||
<el-button size="default" :type="activeTab === 'orgs' ? 'primary' : ''" @click="switchTab('orgs')">组织管理</el-button>
|
||||
<el-button size="default" :type="activeTab === 'logs' ? 'primary' : ''" @click="switchTab('logs')">运行日志</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -48,7 +50,7 @@
|
||||
<div class="stat-summary">
|
||||
<div class="stat-item"><span class="num">{{ cases.length }}</span><span class="label">总案例</span></div>
|
||||
</div>
|
||||
<el-table :data="cases" border stripe class="data-table" style="width:100%">
|
||||
<el-table :data="paginatedCases" border stripe class="data-table" style="width:100%">
|
||||
<el-table-column prop="id" label="ID" width="70"></el-table-column>
|
||||
<el-table-column prop="title" label="标题" min-width="120" :show-overflow-tooltip="true"></el-table-column>
|
||||
<el-table-column prop="field" label="领域" width="100"></el-table-column>
|
||||
@@ -61,9 +63,12 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div v-if="cases.length > casePageSize" style="display:flex;justify-content:center;margin:12px 0;">
|
||||
<el-pagination background layout="prev, pager, next" :total="cases.length" :page-size="casePageSize" :current-page="casePage" @current-change="casePage = $event"></el-pagination>
|
||||
</div>
|
||||
</template>
|
||||
<div class="card-list-mobile">
|
||||
<div v-for="item in cases" :key="item.id" class="card-item">
|
||||
<div v-for="item in paginatedCases" :key="item.id" class="card-item">
|
||||
<div class="card-row"><span class="card-label">标题</span><span class="card-value">{{ item.title }}</span></div>
|
||||
<div class="card-row"><span class="card-label">领域</span><span class="card-value">{{ item.field }}</span></div>
|
||||
<div class="card-row"><span class="card-label">来源</span><span class="card-value">{{ item.source }}</span></div>
|
||||
@@ -198,6 +203,71 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="activeTab === 'users'">
|
||||
<div class="toolbar">
|
||||
<el-button type="primary" size="small" @click="addUser">+ 新建用户</el-button>
|
||||
<el-button size="small" @click="fetchUsers">刷新</el-button>
|
||||
<span style="font-size:13px;color:#909399;margin-left:8px;">共 {{ users.length }} 个</span>
|
||||
</div>
|
||||
<div v-if="usersLoading" class="card-loading">加载中...</div>
|
||||
<template v-else-if="users.length === 0">
|
||||
<div class="empty-state">
|
||||
<el-icon style="font-size:48px;color:#c0c4cc;"><IconUser /></el-icon>
|
||||
<div class="empty-text">暂无用户</div>
|
||||
<el-button type="primary" size="small" @click="addUser">新增第一个用户</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-table :data="paginatedUsers" border stripe class="data-table" style="width:100%">
|
||||
<el-table-column prop="id" label="ID" width="70"></el-table-column>
|
||||
<el-table-column prop="username" label="用户名"></el-table-column>
|
||||
<el-table-column prop="role" label="角色" width="100">
|
||||
<template #default="scope"><el-tag :type="scope.row.role === 'admin' ? 'danger' : 'info'">{{ scope.row.role === 'admin' ? '管理员' : '编辑' }}</el-tag></template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="org_id" label="组织" width="100"></el-table-column>
|
||||
<el-table-column prop="created_at" label="创建时间"><template #default="scope">{{ formatDate(scope.row.created_at) }}</template></el-table-column>
|
||||
<el-table-column label="操作" width="120">
|
||||
<template #default="scope">
|
||||
<el-button size="small" type="danger" @click="deleteUser(scope.row.id)" :disabled="scope.row.role === 'admin'">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div v-if="users.length > userPageSize" style="display:flex;justify-content:center;margin:12px 0;">
|
||||
<el-pagination background layout="prev, pager, next" :total="users.length" :page-size="userPageSize" :current-page="userPage" @current-change="userPage = $event"></el-pagination>
|
||||
</div>
|
||||
<div class="card-list-mobile">
|
||||
<div v-for="u in users" :key="u.id" class="card-item">
|
||||
<div class="card-row"><span class="card-label">用户名</span><span class="card-value">{{ u.username }} <el-tag size="small" :type="u.role === 'admin' ? 'danger' : 'info'">{{ u.role === 'admin' ? '管理员' : '编辑' }}</el-tag></span></div>
|
||||
<div class="card-row"><span class="card-label">组织</span><span class="card-value">{{ u.org_id || '-' }}</span></div>
|
||||
<div class="card-row"><span class="card-label">创建时间</span><span class="card-value">{{ formatDate(u.created_at) }}</span></div>
|
||||
<div class="card-actions">
|
||||
<el-button size="small" type="danger" plain @click="deleteUser(u.id)" :disabled="u.role === 'admin'">删除</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-dialog v-model="userDialogVisible" title="新建用户" width="400px" :close-on-click-modal="false">
|
||||
<el-form :model="userForm" label-width="80px">
|
||||
<el-form-item label="用户名" required>
|
||||
<el-input v-model="userForm.username" placeholder="2-20个字符" maxlength="20" clearable></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="密码" required>
|
||||
<el-input v-model="userForm.password" type="password" placeholder="至少6位" show-password></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="角色">
|
||||
<el-select v-model="userForm.role" style="width:100%">
|
||||
<el-option label="编辑" value="editor"></el-option>
|
||||
<el-option label="管理员" value="admin"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="userDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitUser" :loading="userSubmitting">创建</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
<div v-if="activeTab === 'categories'">
|
||||
<div class="toolbar">
|
||||
<el-button type="primary" size="small" @click="showCategoryDialog()">新增类别</el-button>
|
||||
@@ -212,7 +282,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-table :data="categories" border stripe class="data-table" style="width:100%">
|
||||
<el-table :data="paginatedCategories" border stripe class="data-table" style="width:100%">
|
||||
<el-table-column prop="id" label="ID" width="60"></el-table-column>
|
||||
<el-table-column prop="name" label="名称" min-width="120"></el-table-column>
|
||||
<el-table-column prop="search_query" label="搜索词" min-width="200"></el-table-column>
|
||||
@@ -230,9 +300,12 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div v-if="categories.length > categoryPageSize" style="display:flex;justify-content:center;margin:12px 0;">
|
||||
<el-pagination background layout="prev, pager, next" :total="categories.length" :page-size="categoryPageSize" :current-page="categoryPage" @current-change="categoryPage = $event"></el-pagination>
|
||||
</div>
|
||||
</template>
|
||||
<div class="card-list-mobile" v-if="categories.length > 0">
|
||||
<div v-for="item in categories" :key="item.id" class="card-item">
|
||||
<div v-for="item in paginatedCategories" :key="item.id" class="card-item">
|
||||
<div class="card-row"><span class="card-label">名称</span><span class="card-value">{{ item.name }}</span></div>
|
||||
<div class="card-row"><span class="card-label">搜索词</span><span class="card-value">{{ item.search_query }}</span></div>
|
||||
<div class="card-row"><span class="card-label">源数</span><span class="card-value">{{ item.source_count }}</span></div>
|
||||
@@ -259,7 +332,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-table :data="orgs" border stripe class="data-table" style="width:100%">
|
||||
<el-table :data="paginatedOrgs" border stripe class="data-table" style="width:100%">
|
||||
<el-table-column prop="org_id" label="组织ID" min-width="120"></el-table-column>
|
||||
<el-table-column prop="name" label="名称" min-width="150"></el-table-column>
|
||||
<el-table-column prop="description" label="描述" min-width="200" :show-overflow-tooltip="true"></el-table-column>
|
||||
@@ -277,9 +350,12 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div v-if="orgs.length > orgPageSize" style="display:flex;justify-content:center;margin:12px 0;">
|
||||
<el-pagination background layout="prev, pager, next" :total="orgs.length" :page-size="orgPageSize" :current-page="orgPage" @current-change="orgPage = $event"></el-pagination>
|
||||
</div>
|
||||
</template>
|
||||
<div class="card-list-mobile" v-if="orgs.length > 0">
|
||||
<div v-for="item in orgs" :key="item.org_id" class="card-item">
|
||||
<div v-for="item in paginatedOrgs" :key="item.org_id" class="card-item">
|
||||
<div class="card-row"><span class="card-label">ID</span><span class="card-value">{{ item.org_id }}</span></div>
|
||||
<div class="card-row"><span class="card-label">名称</span><span class="card-value">{{ item.name }}</span></div>
|
||||
<div class="card-row"><span class="card-label">用户</span><span class="card-value">{{ item.user_count }}</span></div>
|
||||
@@ -292,6 +368,32 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="activeTab === 'logs'">
|
||||
<div class="toolbar">
|
||||
<el-select v-model="logType" placeholder="日志类型" style="width:200px;">
|
||||
<el-option label="创作日志" value="creator"></el-option>
|
||||
<el-option label="审查日志" value="optimizer"></el-option>
|
||||
<el-option label="研究日志" value="research"></el-option>
|
||||
<el-option label="大纲日志" value="outline"></el-option>
|
||||
<el-option label="写作日志" value="writer"></el-option>
|
||||
<el-option label="发布日志" value="publisher"></el-option>
|
||||
<el-option label="收集日志" value="collector"></el-option>
|
||||
<el-option label="通知日志" value="notifier"></el-option>
|
||||
<el-option label="趋势日志" value="trends"></el-option>
|
||||
</el-select>
|
||||
<el-date-picker v-model="logDate" type="date" placeholder="选择日期" format="YYYY-MM-DD" value-format="YYYY-MM-DD" style="width:200px;"></el-date-picker>
|
||||
<el-button type="primary" @click="fetchLogs" :loading="logsLoading">加载日志</el-button>
|
||||
</div>
|
||||
<div v-if="logsLoading" class="card-loading">加载中...</div>
|
||||
<div v-else-if="!logContent" class="empty-state">
|
||||
<el-icon style="font-size:48px;color:#c0c4cc;"><IconDocument /></el-icon>
|
||||
<div class="empty-text">选择日志类型和日期后点击加载</div>
|
||||
</div>
|
||||
<div v-else class="log-container" style="max-height:600px;overflow-y:auto;background:#f9fafb;border:1px solid #e5e7eb;border-radius:8px;">
|
||||
<pre style="margin:0;padding:16px;white-space:pre-wrap;word-wrap:break-word;font-size:13px;line-height:1.6;color:#303133;">{{ logContent }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="activeTab === 'sources'">
|
||||
<div class="toolbar">
|
||||
<el-button type="primary" size="small" @click="showSourceDialog()">新增信息源</el-button>
|
||||
@@ -307,7 +409,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-table :data="sources" border stripe class="data-table" style="width:100%">
|
||||
<el-table :data="paginatedSources" border stripe class="data-table" style="width:100%">
|
||||
<el-table-column prop="id" label="ID" width="50"></el-table-column>
|
||||
<el-table-column prop="name" label="名称" min-width="130"></el-table-column>
|
||||
<el-table-column prop="source_type" label="类型" width="90">
|
||||
@@ -328,9 +430,12 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div v-if="sources.length > sourcePageSize" style="display:flex;justify-content:center;margin:12px 0;">
|
||||
<el-pagination background layout="prev, pager, next" :total="sources.length" :page-size="sourcePageSize" :current-page="sourcePage" @current-change="sourcePage = $event"></el-pagination>
|
||||
</div>
|
||||
</template>
|
||||
<div class="card-list-mobile" v-if="sources.length > 0">
|
||||
<div v-for="item in sources" :key="item.id" class="card-item">
|
||||
<div v-for="item in paginatedSources" :key="item.id" class="card-item">
|
||||
<div class="card-row"><span class="card-label">名称</span><span class="card-value">{{ item.name }}</span></div>
|
||||
<div class="card-row"><span class="card-label">类型</span><span class="card-value">{{ item.source_type }}</span></div>
|
||||
<div class="card-row"><span class="card-label">查询</span><span class="card-value">{{ item.query || item.url }}</span></div>
|
||||
@@ -483,7 +588,7 @@
|
||||
<script src="icon-components.js"></script>
|
||||
<script src="element-plus.full.js"></script>
|
||||
<script>
|
||||
const { createApp, ref, reactive, onMounted } = Vue;
|
||||
const { createApp, ref, reactive, computed, onMounted } = Vue;
|
||||
const { ElMessage, ElMessageBox } = ElementPlus;
|
||||
|
||||
const app = createApp({
|
||||
@@ -513,7 +618,7 @@
|
||||
})
|
||||
.catch(() => { localStorage.removeItem('authToken'); window.location.href = '/login.html'; });
|
||||
|
||||
const redirectToPage = (page) => { window.location.href = '/' + page; };
|
||||
const redirectToPage = (page) => { window.location.href = page.startsWith('/') ? page : '/' + page; };
|
||||
|
||||
const cases = ref([]);
|
||||
const casesLoading = ref(false);
|
||||
@@ -543,6 +648,9 @@
|
||||
try { await ElMessageBox.confirm('确定删除该案例吗?', '提示', { type: 'warning' }); await api.delete(`/api/admin/cases/${id}`); ElMessage.success('删除成功'); await loadCases(); }
|
||||
catch (e) { if (e !== 'cancel') ElMessage.error('删除失败: ' + e.message); }
|
||||
};
|
||||
const casePage = ref(1);
|
||||
const casePageSize = 20;
|
||||
const paginatedCases = computed(() => { const s = (casePage.value - 1) * casePageSize; return cases.value.slice(s, s + casePageSize); });
|
||||
|
||||
const taskLogs = ref([]);
|
||||
const taskLogsLoading = ref(false);
|
||||
@@ -642,6 +750,72 @@
|
||||
try { await ElMessageBox.confirm('确定删除该组织吗?关联的用户和选题不会被删除。', '提示', { type: 'warning' }); await api.delete(`/api/admin/orgs/${orgId}`); ElMessage.success('删除成功'); await loadOrgs(); }
|
||||
catch (e) { if (e !== 'cancel') ElMessage.error('删除失败: ' + e.message); }
|
||||
};
|
||||
const orgPage = ref(1);
|
||||
const orgPageSize = 20;
|
||||
const paginatedOrgs = computed(() => { const s = (orgPage.value - 1) * orgPageSize; return orgs.value.slice(s, s + orgPageSize); });
|
||||
|
||||
const users = ref([]);
|
||||
const usersLoading = ref(false);
|
||||
const userDialogVisible = ref(false);
|
||||
const userSubmitting = ref(false);
|
||||
const userForm = reactive({ username: '', password: '', role: 'editor' });
|
||||
const userPage = ref(1);
|
||||
const userPageSize = 20;
|
||||
const paginatedUsers = computed(() => {
|
||||
const start = (userPage.value - 1) * userPageSize;
|
||||
return users.value.slice(start, start + userPageSize);
|
||||
});
|
||||
const fetchUsers = async () => {
|
||||
usersLoading.value = true;
|
||||
try { users.value = await api.get('/api/admin/users'); } catch (e) { ElMessage.error('加载用户失败: ' + e.message); }
|
||||
finally { usersLoading.value = false; }
|
||||
};
|
||||
const addUser = () => { userForm.username = ''; userForm.password = ''; userForm.role = 'editor'; userDialogVisible.value = true; };
|
||||
const submitUser = async () => {
|
||||
if (!userForm.username || userForm.username.length < 2) { ElMessage.warning('用户名至少2个字符'); return; }
|
||||
if (!userForm.password || userForm.password.length < 6) { ElMessage.warning('密码至少6位'); return; }
|
||||
userSubmitting.value = true;
|
||||
try {
|
||||
const u = await api.post('/api/admin/users', { ...userForm });
|
||||
users.value.push(u);
|
||||
ElMessage.success('创建成功');
|
||||
userDialogVisible.value = false;
|
||||
} catch (e) { ElMessage.error('创建失败: ' + e.message); }
|
||||
finally { userSubmitting.value = false; }
|
||||
};
|
||||
const deleteUser = async (id) => {
|
||||
if (id === 'admin') { ElMessage.warning('不能删除管理员用户'); return; }
|
||||
try {
|
||||
await ElMessageBox.confirm('确定删除该用户?', '提示', { type: 'warning' });
|
||||
await api.delete(`/api/admin/users/${encodeURIComponent(id)}`);
|
||||
users.value = users.value.filter(u => u.id !== id);
|
||||
ElMessage.success('删除成功');
|
||||
} catch (e) { if (e !== 'cancel') ElMessage.error('删除失败: ' + e.message); }
|
||||
};
|
||||
|
||||
const logType = ref('creator');
|
||||
const logDate = ref(new Date().toISOString().slice(0, 10));
|
||||
const logContent = ref('');
|
||||
const logsLoading = ref(false);
|
||||
const fetchLogs = async () => {
|
||||
logsLoading.value = true;
|
||||
try {
|
||||
const resp = await fetch(`/api/system/logs/${logDate.value}?log_type=${logType.value}`, {
|
||||
headers: { 'Authorization': `Bearer ${localStorage.getItem('authToken')}` }
|
||||
});
|
||||
if (resp.ok) {
|
||||
const data = await resp.json();
|
||||
const lines = data.content || [];
|
||||
logContent.value = lines.join('\n');
|
||||
} else if (resp.status === 404) {
|
||||
logContent.value = '该日暂无日志';
|
||||
} else {
|
||||
logContent.value = `请求失败: ${resp.status}`;
|
||||
}
|
||||
} catch (e) {
|
||||
logContent.value = '加载失败: ' + e.message;
|
||||
} finally { logsLoading.value = false; }
|
||||
};
|
||||
|
||||
const loadSystemConfigs = async () => {
|
||||
systemConfigsLoading.value = true;
|
||||
@@ -686,6 +860,9 @@
|
||||
try { await ElMessageBox.confirm('确定删除该类别及其关联的信息源吗?', '提示', { type: 'warning' }); await api.delete(`/api/admin/collector/categories/${id}`); ElMessage.success('删除成功'); await loadCategories(); }
|
||||
catch (e) { if (e !== 'cancel') ElMessage.error('删除失败: ' + e.message); }
|
||||
};
|
||||
const categoryPage = ref(1);
|
||||
const categoryPageSize = 20;
|
||||
const paginatedCategories = computed(() => { const s = (categoryPage.value - 1) * categoryPageSize; return categories.value.slice(s, s + categoryPageSize); });
|
||||
|
||||
const loadSources = async () => {
|
||||
sourcesLoading.value = true;
|
||||
@@ -708,12 +885,17 @@
|
||||
try { await ElMessageBox.confirm('确定删除该信息源吗?', '提示', { type: 'warning' }); await api.delete(`/api/admin/collector/sources/${id}`); ElMessage.success('删除成功'); await loadSources(); }
|
||||
catch (e) { if (e !== 'cancel') ElMessage.error('删除失败: ' + e.message); }
|
||||
};
|
||||
const sourcePage = ref(1);
|
||||
const sourcePageSize = 20;
|
||||
const paginatedSources = computed(() => { const s = (sourcePage.value - 1) * sourcePageSize; return sources.value.slice(s, s + sourcePageSize); });
|
||||
|
||||
const formatDate = (dateStr) => { if (!dateStr) return '-'; return new Date(dateStr.replace(' ', 'T')).toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }); };
|
||||
const logout = () => { localStorage.removeItem('authToken'); localStorage.removeItem('userRole'); localStorage.removeItem('currentUser'); window.location.href = '/login.html'; };
|
||||
|
||||
const tabLoaders = {
|
||||
cases: loadCases, tasklogs: loadTaskLogs,
|
||||
llmconfigs: loadLLMConfigs, systemconfigs: loadSystemConfigs,
|
||||
users: fetchUsers,
|
||||
categories: loadCategories, sources: loadSources,
|
||||
orgs: loadOrgs,
|
||||
};
|
||||
@@ -730,18 +912,27 @@
|
||||
onMounted(() => {
|
||||
loadedTabs.add('cases');
|
||||
loadCases();
|
||||
const hash = window.location.hash.replace('#tab=', '');
|
||||
if (hash && tabLoaders[hash]) { switchTab(hash); }
|
||||
});
|
||||
|
||||
return {
|
||||
activeTab, switchTab,
|
||||
cases, casesLoading, caseDialogVisible, caseForm, caseDialogTitle, showCaseDialog, saveCase, deleteCase,
|
||||
casePage, casePageSize, paginatedCases,
|
||||
taskLogs, taskLogsLoading, loadTaskLogs,
|
||||
llmConfigs, llmConfigsLoading, llmConfigDialogVisible, llmConfigForm, llmConfigDialogTitle, showLLMConfigDialog, saveLLMConfig, deleteLLMConfig,
|
||||
systemConfigs, systemConfigsLoading, systemConfigDialogVisible, systemConfigForm, systemConfigDialogTitle, showSystemConfigDialog, saveSystemConfig, deleteSystemConfig,
|
||||
categories, categoriesLoading, categoryDialogVisible, categoryForm, categoryDialogTitle, showCategoryDialog, saveCategory, deleteCategory,
|
||||
categoryPage, categoryPageSize, paginatedCategories,
|
||||
sources, sourcesLoading, sourceDialogVisible, sourceForm, sourceDialogTitle, showSourceDialog, saveSource, deleteSource,
|
||||
sourcePage, sourcePageSize, paginatedSources,
|
||||
orgs, orgsLoading, orgDialogVisible, orgForm, orgDialogTitle, showOrgDialog, saveOrg, deleteOrg,
|
||||
logout, currentUser, isAdmin, redirectToPage
|
||||
orgPage, orgPageSize, paginatedOrgs,
|
||||
logout, currentUser, isAdmin, redirectToPage,
|
||||
users, usersLoading, userDialogVisible, userSubmitting, userForm, userPage, userPageSize, paginatedUsers,
|
||||
fetchUsers, addUser, submitUser, deleteUser,
|
||||
logType, logDate, logContent, logsLoading, fetchLogs
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user