update
This commit is contained in:
+1
-1
@@ -1 +1 @@
|
|||||||
VITE_API_BASE_URL_PROD=https://share.lanol.cn
|
VITE_API_BASE_URL_PROD=
|
||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
import { createRouter, createWebHistory } from 'vue-router'
|
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(import.meta.env.BASE_URL),
|
history: createWebHashHistory(import.meta.env.BASE_URL),
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
|
|||||||
+1
-1
@@ -51,7 +51,7 @@ api.interceptors.response.use(
|
|||||||
case 401:
|
case 401:
|
||||||
console.error('未授权,请重新登录')
|
console.error('未授权,请重新登录')
|
||||||
localStorage.clear()
|
localStorage.clear()
|
||||||
window.location.href = '/login'
|
window.location.href = '/#/login'
|
||||||
break
|
break
|
||||||
case 403:
|
case 403:
|
||||||
// 禁止访问
|
// 禁止访问
|
||||||
|
|||||||
@@ -45,10 +45,11 @@
|
|||||||
id="code"
|
id="code"
|
||||||
v-model="code"
|
v-model="code"
|
||||||
type="text"
|
type="text"
|
||||||
class="w-full px-4 py-3 rounded-lg text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-indigo-500 transition duration-300 pr-10"
|
class="w-full px-4 py-3 rounded-lg placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-indigo-500 transition duration-300 pr-10"
|
||||||
:class="[
|
:class="[
|
||||||
isDarkMode ? 'bg-gray-700 bg-opacity-50' : 'bg-gray-100',
|
isDarkMode ? 'bg-gray-700 bg-opacity-50' : 'bg-gray-100',
|
||||||
{ 'ring-2 ring-red-500': error }
|
{ 'ring-2 ring-red-500': error },
|
||||||
|
isDarkMode ? 'text-gray-300' : 'text-gray-800'
|
||||||
]"
|
]"
|
||||||
placeholder="请输入5位取件码"
|
placeholder="请输入5位取件码"
|
||||||
required
|
required
|
||||||
@@ -592,19 +593,26 @@ const showContentPreview = () => {
|
|||||||
|
|
||||||
/* 添加 Markdown 样式 */
|
/* 添加 Markdown 样式 */
|
||||||
:deep(.prose) {
|
:deep(.prose) {
|
||||||
@apply text-left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
:deep(.prose h1, .prose h2, .prose h3, .prose h4, .prose h5, .prose h6) {
|
:deep(.prose h1),
|
||||||
@apply text-indigo-600 dark:text-indigo-400;
|
:deep(.prose h2),
|
||||||
|
:deep(.prose h3),
|
||||||
|
:deep(.prose h4),
|
||||||
|
:deep(.prose h5),
|
||||||
|
:deep(.prose h6) {
|
||||||
|
color: rgb(79, 70, 229); /* text-indigo-600 */
|
||||||
}
|
}
|
||||||
:deep(.prose a) {
|
|
||||||
@apply text-blue-600 dark:text-blue-400;
|
@media (prefers-color-scheme: dark) {
|
||||||
|
:deep(.prose h1),
|
||||||
|
:deep(.prose h2),
|
||||||
|
:deep(.prose h3),
|
||||||
|
:deep(.prose h4),
|
||||||
|
:deep(.prose h5),
|
||||||
|
:deep(.prose h6) {
|
||||||
|
color: rgb(129, 140, 248); /* text-indigo-400 */
|
||||||
}
|
}
|
||||||
:deep(.prose code) {
|
|
||||||
@apply bg-gray-100 dark:bg-gray-700 rounded px-1;
|
|
||||||
}
|
|
||||||
:deep(.prose pre) {
|
|
||||||
@apply bg-gray-100 dark:bg-gray-700 rounded p-4 overflow-x-auto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 添加新的宽度类 */
|
/* 添加新的宽度类 */
|
||||||
|
|||||||
@@ -424,7 +424,7 @@ const selectedFile = ref<File | null>(null)
|
|||||||
const textContent = ref('')
|
const textContent = ref('')
|
||||||
const fileInput = ref<HTMLInputElement | null>(null)
|
const fileInput = ref<HTMLInputElement | null>(null)
|
||||||
const expirationMethod = ref('day')
|
const expirationMethod = ref('day')
|
||||||
const expirationValue = ref('')
|
const expirationValue = ref('1')
|
||||||
const uploadProgress = ref(0)
|
const uploadProgress = ref(0)
|
||||||
const showDrawer = ref(false)
|
const showDrawer = ref(false)
|
||||||
const selectedRecord = ref<any>(null)
|
const selectedRecord = ref<any>(null)
|
||||||
|
|||||||
+136
-120
@@ -11,7 +11,7 @@
|
|||||||
<div class="relative flex-1">
|
<div class="relative flex-1">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
v-model="fileSearchQuery"
|
v-model="params.keyword"
|
||||||
:class="[
|
:class="[
|
||||||
isDarkMode
|
isDarkMode
|
||||||
? 'bg-gray-700 border-gray-600 text-white placeholder-gray-400'
|
? 'bg-gray-700 border-gray-600 text-white placeholder-gray-400'
|
||||||
@@ -25,53 +25,20 @@
|
|||||||
:class="[isDarkMode ? 'text-gray-400' : 'text-gray-500']"
|
:class="[isDarkMode ? 'text-gray-400' : 'text-gray-500']"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<select
|
|
||||||
v-model="fileTypeFilter"
|
|
||||||
:class="[
|
|
||||||
isDarkMode
|
|
||||||
? 'bg-gray-700 border-gray-600 text-white'
|
|
||||||
: 'bg-white border-gray-300 text-gray-900'
|
|
||||||
]"
|
|
||||||
class="border rounded-lg px-4 py-2 focus:ring-2 focus:ring-indigo-500 focus:border-transparent"
|
|
||||||
>
|
|
||||||
<option value="">所有类型</option>
|
|
||||||
<option value="PDF">PDF</option>
|
|
||||||
<option value="Image">图片</option>
|
|
||||||
<option value="Video">视频</option>
|
|
||||||
<option value="Document">文档</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 上传按钮 -->
|
<!-- 搜索按钮 -->
|
||||||
<div class="flex gap-4">
|
<div class="flex gap-4">
|
||||||
<button
|
<button
|
||||||
@click="$refs.fileInput.click()"
|
@click="handleSearch"
|
||||||
class="flex items-center px-4 py-2 rounded-lg bg-indigo-600 text-white hover:bg-indigo-700 transition-colors duration-200"
|
class="flex items-center px-4 py-2 rounded-lg bg-indigo-600 text-white hover:bg-indigo-700 transition-colors duration-200"
|
||||||
>
|
>
|
||||||
<UploadIcon class="w-5 h-5 mr-2" />
|
<SearchIcon class="w-5 h-5 mr-2" />
|
||||||
上传文件
|
搜索
|
||||||
</button>
|
</button>
|
||||||
<input ref="fileInput" type="file" multiple class="hidden" @change="handleFileUpload" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 拖拽上传区域 -->
|
|
||||||
<div
|
|
||||||
v-if="showDropZone"
|
|
||||||
class="mb-6 border-2 border-dashed rounded-lg p-8 text-center"
|
|
||||||
:class="[isDarkMode ? 'border-gray-600 bg-gray-800/50' : 'border-gray-300 bg-gray-50']"
|
|
||||||
@drop.prevent="handleFileDrop"
|
|
||||||
@dragover.prevent="showDropZone = true"
|
|
||||||
@dragleave.prevent="showDropZone = false"
|
|
||||||
>
|
|
||||||
<UploadCloudIcon
|
|
||||||
class="mx-auto w-12 h-12 mb-4"
|
|
||||||
:class="[isDarkMode ? 'text-gray-400' : 'text-gray-500']"
|
|
||||||
/>
|
|
||||||
<p :class="[isDarkMode ? 'text-gray-300' : 'text-gray-700']">拖拽文件到此处上传</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- File List -->
|
<!-- File List -->
|
||||||
<div
|
<div
|
||||||
class="rounded-lg shadow-md overflow-hidden transition-colors duration-300"
|
class="rounded-lg shadow-md overflow-hidden transition-colors duration-300"
|
||||||
@@ -106,7 +73,14 @@
|
|||||||
: 'bg-white divide-y divide-gray-200'
|
: 'bg-white divide-y divide-gray-200'
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
<tr v-for="file in files" :key="file.id">
|
<tr v-for="file in tableData" :key="file.id">
|
||||||
|
<td class="px-6 py-4 whitespace-nowrap">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<span class="font-medium" :class="[isDarkMode ? 'text-white' : 'text-gray-900']">
|
||||||
|
{{ file.code }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
<td class="px-6 py-4 whitespace-nowrap">
|
<td class="px-6 py-4 whitespace-nowrap">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<FileIcon
|
<FileIcon
|
||||||
@@ -114,28 +88,29 @@
|
|||||||
:class="[isDarkMode ? 'text-indigo-400' : 'text-indigo-500']"
|
:class="[isDarkMode ? 'text-indigo-400' : 'text-indigo-500']"
|
||||||
/>
|
/>
|
||||||
<span class="font-medium" :class="[isDarkMode ? 'text-white' : 'text-gray-900']">
|
<span class="font-medium" :class="[isDarkMode ? 'text-white' : 'text-gray-900']">
|
||||||
{{ file.name }}
|
{{ file.prefix }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-4 whitespace-nowrap">
|
<td class="px-6 py-4 whitespace-nowrap">
|
||||||
<span :class="[isDarkMode ? 'text-gray-400' : 'text-gray-500']">
|
<span :class="[isDarkMode ? 'text-gray-400' : 'text-gray-500']">
|
||||||
{{ file.size }}
|
{{ Math.round((file.size / 1024 / 1024) * 100) / 100 }}MB
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-4 whitespace-nowrap">
|
<td class="px-6 py-4 whitespace-nowrap">
|
||||||
<span :class="[isDarkMode ? 'text-gray-400' : 'text-gray-500']">
|
<span :class="[isDarkMode ? 'text-gray-400' : 'text-gray-500']">
|
||||||
{{ file.type }}
|
{{ file.text }}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-4 whitespace-nowrap">
|
<td class="px-6 py-4 whitespace-nowrap">
|
||||||
<span :class="[isDarkMode ? 'text-gray-400' : 'text-gray-500']">
|
<span :class="[isDarkMode ? 'text-gray-400' : 'text-gray-500']">
|
||||||
{{ file.lastModified }}
|
{{ file.expired_at ? formatTimestamp(file.expired_at) : '永久' }}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
|
||||||
<button
|
<!-- <button
|
||||||
@click="downloadFile(file)"
|
v-if="file.file_path"
|
||||||
|
@click="downloadFile(file.id)"
|
||||||
class="mr-3 transition-colors duration-200"
|
class="mr-3 transition-colors duration-200"
|
||||||
:class="[
|
:class="[
|
||||||
isDarkMode
|
isDarkMode
|
||||||
@@ -144,9 +119,9 @@
|
|||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
下载
|
下载
|
||||||
</button>
|
</button> -->
|
||||||
<button
|
<button
|
||||||
@click="deleteFile(file)"
|
@click="deleteFile(file.id)"
|
||||||
class="transition-colors duration-200"
|
class="transition-colors duration-200"
|
||||||
:class="[
|
:class="[
|
||||||
isDarkMode
|
isDarkMode
|
||||||
@@ -244,75 +219,128 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { inject, ref, computed } from 'vue'
|
import { inject, ref, computed } from 'vue'
|
||||||
|
import api from '@/utils/api'
|
||||||
|
import { FileIcon, SearchIcon } from 'lucide-vue-next'
|
||||||
|
import { useAlertStore } from '@/stores/alertStore'
|
||||||
|
const alertStore = useAlertStore()
|
||||||
|
function formatTimestamp(timestamp: string): string {
|
||||||
|
const date = new Date(timestamp)
|
||||||
|
const year = date.getFullYear()
|
||||||
|
const month = (date.getMonth() + 1).toString().padStart(2, '0')
|
||||||
|
const day = date.getDate().toString().padStart(2, '0')
|
||||||
|
const hours = date.getHours().toString().padStart(2, '0')
|
||||||
|
const minutes = date.getMinutes().toString().padStart(2, '0')
|
||||||
|
const seconds = date.getSeconds().toString().padStart(2, '0')
|
||||||
|
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
|
||||||
|
}
|
||||||
|
|
||||||
const isDarkMode = inject('isDarkMode')
|
const isDarkMode = inject('isDarkMode')
|
||||||
|
const tableData: any = ref([])
|
||||||
|
|
||||||
// 新增的响应式变量
|
// 修改文件表头
|
||||||
const fileSearchQuery = ref('')
|
const fileTableHeaders = ['取件码', '名称', '大小', '描述', '过期时间', '操作']
|
||||||
const fileTypeFilter = ref('')
|
|
||||||
const showDropZone = ref(false)
|
|
||||||
|
|
||||||
const handleFileUpload = (event: Event) => {
|
|
||||||
const files = (event.target as HTMLInputElement).files
|
|
||||||
if (files && files.length > 0) {
|
|
||||||
console.log('Uploading files:', files)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface File {
|
|
||||||
id: number
|
|
||||||
name: string
|
|
||||||
size: string
|
|
||||||
type: string
|
|
||||||
lastModified: string
|
|
||||||
}
|
|
||||||
|
|
||||||
const fileTableHeaders = ['名称', '大小', '类型', '最后修改', '操作']
|
|
||||||
|
|
||||||
const files: File[] = [
|
|
||||||
{ id: 1, name: 'document.pdf', size: '2.5 MB', type: 'PDF', lastModified: '2024-01-15' },
|
|
||||||
{ id: 2, name: 'image.jpg', size: '1.8 MB', type: 'Image', lastModified: '2024-01-14' },
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
name: 'spreadsheet.xlsx',
|
|
||||||
size: '3.2 MB',
|
|
||||||
type: 'Spreadsheet',
|
|
||||||
lastModified: '2024-01-13'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 4,
|
|
||||||
name: 'presentation.pptx',
|
|
||||||
size: '5.1 MB',
|
|
||||||
type: 'Presentation',
|
|
||||||
lastModified: '2024-01-12'
|
|
||||||
},
|
|
||||||
{ id: 5, name: 'video.mp4', size: '15.7 MB', type: 'Video', lastModified: '2024-01-11' }
|
|
||||||
]
|
|
||||||
|
|
||||||
const handleFileDrop = (event: any) => {
|
|
||||||
const files = (event.dataTransfer as DataTransfer).files
|
|
||||||
if (files && files.length > 0) {
|
|
||||||
console.log('Dropped files:', files)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 分页参数
|
// 分页参数
|
||||||
const params = ref({
|
const params = ref({
|
||||||
page: 1,
|
page: 1,
|
||||||
size: 10,
|
size: 10,
|
||||||
total: 0
|
total: 0,
|
||||||
|
keyword: ''
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 下载文件处理
|
||||||
|
const downloadFile = async (id: number) => {
|
||||||
|
try {
|
||||||
|
const response = await api({
|
||||||
|
url: '/admin/file/download',
|
||||||
|
method: 'get',
|
||||||
|
params: { id },
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
|
||||||
|
const contentDisposition = response.headers['content-disposition']
|
||||||
|
let filename = 'file'
|
||||||
|
const filenameMatch = contentDisposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/)
|
||||||
|
if (filenameMatch != null && filenameMatch[1]) {
|
||||||
|
filename = filenameMatch[1].replace(/['"]/g, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
if (window.showSaveFilePicker) {
|
||||||
|
await saveFileByWebApi(response.data, filename)
|
||||||
|
} else {
|
||||||
|
await saveFileByElementA(response.data, filename)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('下载失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除文件处理
|
||||||
|
const deleteFile = async (id: number) => {
|
||||||
|
try {
|
||||||
|
await api({
|
||||||
|
url: '/admin/file/delete',
|
||||||
|
method: 'delete',
|
||||||
|
data: { id }
|
||||||
|
})
|
||||||
|
await loadFiles()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('删除失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文件保存辅助函数
|
||||||
|
async function saveFileByElementA(fileBlob: Blob, filename: string) {
|
||||||
|
const downloadUrl = window.URL.createObjectURL(fileBlob)
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = downloadUrl
|
||||||
|
link.download = filename
|
||||||
|
document.body.appendChild(link)
|
||||||
|
link.click()
|
||||||
|
window.URL.revokeObjectURL(downloadUrl)
|
||||||
|
document.body.removeChild(link)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveFileByWebApi(fileBlob: Blob, filename: string) {
|
||||||
|
// @ts-ignore
|
||||||
|
const newHandle = await window.showSaveFilePicker({
|
||||||
|
suggestedName: filename
|
||||||
|
})
|
||||||
|
const writableStream = await newHandle.createWritable()
|
||||||
|
await writableStream.write(fileBlob)
|
||||||
|
await writableStream.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载文件列表
|
||||||
|
const loadFiles = async () => {
|
||||||
|
try {
|
||||||
|
const res: any = await api({
|
||||||
|
url: '/admin/file/list',
|
||||||
|
method: 'get',
|
||||||
|
params: params.value
|
||||||
|
})
|
||||||
|
tableData.value = res.detail.data
|
||||||
|
params.value.total = res.detail.total
|
||||||
|
alertStore.showAlert('加载成功', 'success')
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载文件列表失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 页码改变处理函数
|
||||||
|
const handlePageChange = async (page: any) => {
|
||||||
|
if (page < 1 || page > totalPages.value) return
|
||||||
|
params.value.page = page
|
||||||
|
await loadFiles()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始加载
|
||||||
|
loadFiles()
|
||||||
|
|
||||||
// 计算总页数
|
// 计算总页数
|
||||||
const totalPages = computed(() => Math.ceil(params.value.total / params.value.size))
|
const totalPages = computed(() => Math.ceil(params.value.total / params.value.size))
|
||||||
|
|
||||||
const downloadFile = (file: File) => {
|
|
||||||
console.log('Downloading file:', file.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
const deleteFile = (file: File) => {
|
|
||||||
console.log('Deleting file:', file.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 计算要显示的页码
|
// 计算要显示的页码
|
||||||
const displayedPages = computed(() => {
|
const displayedPages = computed(() => {
|
||||||
const current = params.value.page
|
const current = params.value.page
|
||||||
@@ -349,21 +377,9 @@ const displayedPages = computed(() => {
|
|||||||
return pages
|
return pages
|
||||||
})
|
})
|
||||||
|
|
||||||
// 页码改变处理函数
|
// 添加搜索处理函数
|
||||||
const handlePageChange = async (page: any) => {
|
const handleSearch = async () => {
|
||||||
if (page < 1 || page > totalPages.value) return
|
params.value.page = 1 // 重置页码到第一页
|
||||||
params.value.page = page
|
await loadFiles()
|
||||||
await loadFiles() // 重新加载文件列表
|
|
||||||
}
|
|
||||||
|
|
||||||
// 加载文件列表
|
|
||||||
const loadFiles = async () => {
|
|
||||||
try {
|
|
||||||
params.value.total = 85
|
|
||||||
// 更新文件列表数据...
|
|
||||||
} catch (error) {
|
|
||||||
console.error('加载文件列表失败:', error)
|
|
||||||
// 处理错误...
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { inject, ref } from 'vue'
|
import { inject, ref } from 'vue'
|
||||||
import api from '@/utils/api'
|
import api from '@/utils/api'
|
||||||
|
import { useAlertStore } from '@/stores/alertStore'
|
||||||
|
|
||||||
const isDarkMode = inject('isDarkMode')
|
const isDarkMode = inject('isDarkMode')
|
||||||
interface ConfigState {
|
interface ConfigState {
|
||||||
@@ -124,6 +125,7 @@ const refreshData = () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
const alertStore = useAlertStore()
|
||||||
// 转换文件大小为字节
|
// 转换文件大小为字节
|
||||||
const convertToBytes = (size: number, unit: string): number => {
|
const convertToBytes = (size: number, unit: string): number => {
|
||||||
const units = {
|
const units = {
|
||||||
@@ -151,9 +153,9 @@ const submitSave = () => {
|
|||||||
data: formData
|
data: formData
|
||||||
}).then((res: any) => {
|
}).then((res: any) => {
|
||||||
if (res.code == 200) {
|
if (res.code == 200) {
|
||||||
alert('保存成功')
|
alertStore.showAlert('保存成功', 'success')
|
||||||
} else {
|
} else {
|
||||||
alert(res.message)
|
alertStore.showAlert(res.message, 'error')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user