优化:模拟数据显示优化
This commit is contained in:
parent
748544c883
commit
9d92b4cbfe
7
.browserslistrc
Normal file
7
.browserslistrc
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# 兼容国产手机、微信内置浏览器、旧版 Android WebView
|
||||||
|
>= 0.5%
|
||||||
|
last 2 versions
|
||||||
|
not dead
|
||||||
|
Chrome >= 64
|
||||||
|
Android >= 5
|
||||||
|
iOS >= 11
|
||||||
1
.env
1
.env
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
# VITE_USE_MOCK_DATA=false # 全部关闭 mock
|
# VITE_USE_MOCK_DATA=false # 全部关闭 mock
|
||||||
# SSH 部署(npm run deploy),可选覆盖
|
# SSH 部署(npm run deploy),可选覆盖
|
||||||
|
VITE_USE_MOCK_WALLET=false # 关闭钱包 mock
|
||||||
# DEPLOY_HOST=38.246.250.238
|
# DEPLOY_HOST=38.246.250.238
|
||||||
# DEPLOY_USER=root
|
# DEPLOY_USER=root
|
||||||
# DEPLOY_PATH=/opt/1panel/www/sites/pm.xtrader.vip/index
|
# DEPLOY_PATH=/opt/1panel/www/sites/pm.xtrader.vip/index
|
||||||
|
|||||||
@ -8,10 +8,10 @@
|
|||||||
|
|
||||||
## 核心能力
|
## 核心能力
|
||||||
|
|
||||||
- **分类导航**:三层级分类选择(一级 Tab、二级图标、三级 Tab)
|
- **分类导航**:三层级分类选择(一级 Tab、二级文字标签、三级 Tab)
|
||||||
- **事件列表**:卡片式展示,支持下拉刷新、触底加载
|
- **事件列表**:卡片式展示,支持下拉刷新、触底加载
|
||||||
- **搜索**:可按关键词搜索事件
|
- **搜索**:可按关键词搜索事件
|
||||||
- **分类筛选**:选中分类后,自动提取所有层级节点的 `tagIds` 进行事件筛选
|
- **分类筛选**:选中分类后,自动提取所有层级节点的 `tagIds` 进行事件筛选;切换语言时重新请求分类接口并刷新列表
|
||||||
- **Footer**:使用 `<Footer />` 组件,包含品牌、链接、语言选择、免责声明
|
- **Footer**:使用 `<Footer />` 组件,包含品牌、链接、语言选择、免责声明
|
||||||
|
|
||||||
## 数据流
|
## 数据流
|
||||||
|
|||||||
72
package-lock.json
generated
72
package-lock.json
generated
@ -31,6 +31,7 @@
|
|||||||
"@vue/eslint-config-typescript": "^14.6.0",
|
"@vue/eslint-config-typescript": "^14.6.0",
|
||||||
"@vue/test-utils": "^2.4.6",
|
"@vue/test-utils": "^2.4.6",
|
||||||
"@vue/tsconfig": "^0.8.1",
|
"@vue/tsconfig": "^0.8.1",
|
||||||
|
"autoprefixer": "^10.4.27",
|
||||||
"eslint": "^9.39.2",
|
"eslint": "^9.39.2",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
"eslint-plugin-oxlint": "~1.42.0",
|
"eslint-plugin-oxlint": "~1.42.0",
|
||||||
@ -40,6 +41,7 @@
|
|||||||
"jsdom": "^27.4.0",
|
"jsdom": "^27.4.0",
|
||||||
"npm-run-all2": "^8.0.4",
|
"npm-run-all2": "^8.0.4",
|
||||||
"oxlint": "~1.42.0",
|
"oxlint": "~1.42.0",
|
||||||
|
"postcss": "^8.5.8",
|
||||||
"prettier": "3.8.1",
|
"prettier": "3.8.1",
|
||||||
"typescript": "~5.9.3",
|
"typescript": "~5.9.3",
|
||||||
"vite": "^7.3.1",
|
"vite": "^7.3.1",
|
||||||
@ -3307,6 +3309,43 @@
|
|||||||
"url": "https://github.com/sponsors/sxzz"
|
"url": "https://github.com/sponsors/sxzz"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/autoprefixer": {
|
||||||
|
"version": "10.4.27",
|
||||||
|
"resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.27.tgz",
|
||||||
|
"integrity": "sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/postcss/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "tidelift",
|
||||||
|
"url": "https://tidelift.com/funding/github/npm/autoprefixer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/ai"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"browserslist": "^4.28.1",
|
||||||
|
"caniuse-lite": "^1.0.30001774",
|
||||||
|
"fraction.js": "^5.3.4",
|
||||||
|
"picocolors": "^1.1.1",
|
||||||
|
"postcss-value-parser": "^4.2.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"autoprefixer": "bin/autoprefixer"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^10 || ^12 || >=14"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"postcss": "^8.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/available-typed-arrays": {
|
"node_modules/available-typed-arrays": {
|
||||||
"version": "1.0.7",
|
"version": "1.0.7",
|
||||||
"resolved": "https://registry.npmmirror.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
|
"resolved": "https://registry.npmmirror.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
|
||||||
@ -3715,9 +3754,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/caniuse-lite": {
|
"node_modules/caniuse-lite": {
|
||||||
"version": "1.0.30001767",
|
"version": "1.0.30001780",
|
||||||
"resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001767.tgz",
|
"resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001780.tgz",
|
||||||
"integrity": "sha512-34+zUAMhSH+r+9eKmYG+k2Rpt8XttfE4yXAjoZvkAPs15xcYQhyBYdalJ65BzivAvGRMViEjy6oKr/S91loekQ==",
|
"integrity": "sha512-llngX0E7nQci5BPJDqoZSbuZ5Bcs9F5db7EtgfwBerX9XGtkkiO4NwfDDIRzHTTwcYC8vC7bmeUEPGrKlR/TkQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -5042,6 +5081,20 @@
|
|||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fraction.js": {
|
||||||
|
"version": "5.3.4",
|
||||||
|
"resolved": "https://registry.npmmirror.com/fraction.js/-/fraction.js-5.3.4.tgz",
|
||||||
|
"integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": "*"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/rawify"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/fsevents": {
|
"node_modules/fsevents": {
|
||||||
"version": "2.3.2",
|
"version": "2.3.2",
|
||||||
"resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.2.tgz",
|
"resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.2.tgz",
|
||||||
@ -6867,9 +6920,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.5.6",
|
"version": "8.5.8",
|
||||||
"resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz",
|
"resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.8.tgz",
|
||||||
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
|
"integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@ -6908,6 +6961,13 @@
|
|||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/postcss-value-parser": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/prelude-ls": {
|
"node_modules/prelude-ls": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
"resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz",
|
||||||
|
|||||||
@ -41,6 +41,7 @@
|
|||||||
"@vue/eslint-config-typescript": "^14.6.0",
|
"@vue/eslint-config-typescript": "^14.6.0",
|
||||||
"@vue/test-utils": "^2.4.6",
|
"@vue/test-utils": "^2.4.6",
|
||||||
"@vue/tsconfig": "^0.8.1",
|
"@vue/tsconfig": "^0.8.1",
|
||||||
|
"autoprefixer": "^10.4.27",
|
||||||
"eslint": "^9.39.2",
|
"eslint": "^9.39.2",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
"eslint-config-prettier": "^10.1.8",
|
||||||
"eslint-plugin-oxlint": "~1.42.0",
|
"eslint-plugin-oxlint": "~1.42.0",
|
||||||
@ -50,6 +51,7 @@
|
|||||||
"jsdom": "^27.4.0",
|
"jsdom": "^27.4.0",
|
||||||
"npm-run-all2": "^8.0.4",
|
"npm-run-all2": "^8.0.4",
|
||||||
"oxlint": "~1.42.0",
|
"oxlint": "~1.42.0",
|
||||||
|
"postcss": "^8.5.8",
|
||||||
"prettier": "3.8.1",
|
"prettier": "3.8.1",
|
||||||
"typescript": "~5.9.3",
|
"typescript": "~5.9.3",
|
||||||
"vite": "^7.3.1",
|
"vite": "^7.3.1",
|
||||||
|
|||||||
5
postcss.config.js
Normal file
5
postcss.config.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export default {
|
||||||
|
plugins: {
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
}
|
||||||
@ -39,8 +39,9 @@ export function toLwcData(points: [number, number][]): { time: UTCTimestamp; val
|
|||||||
for (const [t, v] of sorted) {
|
for (const [t, v] of sorted) {
|
||||||
const utcSec = t >= 1e12 ? t / 1000 : t
|
const utcSec = t >= 1e12 ? t / 1000 : t
|
||||||
const time = timeToLocal(utcSec)
|
const time = timeToLocal(utcSec)
|
||||||
if (result.length > 0 && result[result.length - 1].time === time) {
|
const last = result[result.length - 1]
|
||||||
result[result.length - 1].value = v
|
if (last && last.time === time) {
|
||||||
|
last.value = v
|
||||||
} else {
|
} else {
|
||||||
result.push({ time, value: v })
|
result.push({ time, value: v })
|
||||||
}
|
}
|
||||||
|
|||||||
@ -116,13 +116,6 @@
|
|||||||
:class="{ 'home-category-icon-item--active': layerActiveValues[1] === item.id }"
|
:class="{ 'home-category-icon-item--active': layerActiveValues[1] === item.id }"
|
||||||
@click="onCategorySelect(1, item.id)"
|
@click="onCategorySelect(1, item.id)"
|
||||||
>
|
>
|
||||||
<v-icon
|
|
||||||
size="24"
|
|
||||||
class="home-category-icon"
|
|
||||||
:color="resolveCategoryIconColor(item) ?? 'grey'"
|
|
||||||
>
|
|
||||||
{{ item.icon || DEFAULT_CATEGORY_ICON }}
|
|
||||||
</v-icon>
|
|
||||||
<span class="home-category-icon-label">{{ item.label }}</span>
|
<span class="home-category-icon-label">{{ item.label }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -243,11 +236,9 @@ import {
|
|||||||
type EventCardItem,
|
type EventCardItem,
|
||||||
} from '../api/event'
|
} from '../api/event'
|
||||||
import {
|
import {
|
||||||
DEFAULT_CATEGORY_ICON,
|
|
||||||
enrichWithIcons,
|
enrichWithIcons,
|
||||||
getPmTagMain,
|
getPmTagMain,
|
||||||
MOCK_CATEGORY_TREE,
|
MOCK_CATEGORY_TREE,
|
||||||
resolveCategoryIconColor,
|
|
||||||
type CategoryTreeNode,
|
type CategoryTreeNode,
|
||||||
} from '../api/category'
|
} from '../api/category'
|
||||||
import { USE_MOCK_CATEGORY } from '../config/mock'
|
import { USE_MOCK_CATEGORY } from '../config/mock'
|
||||||
@ -480,13 +471,49 @@ function onCardOpenTrade(
|
|||||||
const toastStore = useToastStore()
|
const toastStore = useToastStore()
|
||||||
const localeStore = useLocaleStore()
|
const localeStore = useLocaleStore()
|
||||||
|
|
||||||
// 监听语言切换,语言变化时重新加载事件列表
|
/** 加载分类树(接口或 mock),完成后初始化选中并触发事件列表加载 */
|
||||||
|
async function loadCategory() {
|
||||||
|
function loadEventListAfterCategoryReady() {
|
||||||
|
const cached = getEventListCache()
|
||||||
|
if (cached && cached.list.length > 0) {
|
||||||
|
eventList.value = cached.list
|
||||||
|
eventPage.value = cached.page
|
||||||
|
eventTotal.value = cached.total
|
||||||
|
eventPageSize.value = cached.pageSize
|
||||||
|
} else {
|
||||||
|
loadEvents(1, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (USE_MOCK_CATEGORY) {
|
||||||
|
categoryTree.value = enrichWithIcons(MOCK_CATEGORY_TREE)
|
||||||
|
initCategorySelection()
|
||||||
|
loadEventListAfterCategoryReady()
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
const res = await getPmTagMain()
|
||||||
|
if (res.code === 0 || res.code === 200) {
|
||||||
|
const data = res.data
|
||||||
|
categoryTree.value =
|
||||||
|
Array.isArray(data) && data.length > 0 ? data : enrichWithIcons(MOCK_CATEGORY_TREE)
|
||||||
|
} else {
|
||||||
|
categoryTree.value = enrichWithIcons(MOCK_CATEGORY_TREE)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
categoryTree.value = enrichWithIcons(MOCK_CATEGORY_TREE)
|
||||||
|
}
|
||||||
|
initCategorySelection()
|
||||||
|
loadEventListAfterCategoryReady()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听语言切换,语言变化时重新请求分类并加载事件列表
|
||||||
watch(
|
watch(
|
||||||
() => localeStore.currentLocale,
|
() => localeStore.currentLocale,
|
||||||
() => {
|
() => {
|
||||||
clearEventListCache()
|
clearEventListCache()
|
||||||
eventPage.value = 1
|
eventPage.value = 1
|
||||||
loadEvents(1, false, activeSearchKeyword.value)
|
loadCategory()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -605,41 +632,7 @@ function checkScrollLoad() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
/** 分类树就绪后加载列表(确保 activeTagIds 已计算,与下拉刷新参数一致) */
|
loadCategory()
|
||||||
function loadEventListAfterCategoryReady() {
|
|
||||||
const cached = getEventListCache()
|
|
||||||
if (cached && cached.list.length > 0) {
|
|
||||||
eventList.value = cached.list
|
|
||||||
eventPage.value = cached.page
|
|
||||||
eventTotal.value = cached.total
|
|
||||||
eventPageSize.value = cached.pageSize
|
|
||||||
} else {
|
|
||||||
loadEvents(1, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (USE_MOCK_CATEGORY) {
|
|
||||||
categoryTree.value = enrichWithIcons(MOCK_CATEGORY_TREE)
|
|
||||||
initCategorySelection()
|
|
||||||
loadEventListAfterCategoryReady()
|
|
||||||
} else {
|
|
||||||
getPmTagMain()
|
|
||||||
.then((res) => {
|
|
||||||
if (res.code === 0 || res.code === 200) {
|
|
||||||
const data = res.data
|
|
||||||
categoryTree.value = Array.isArray(data) && data.length > 0 ? data : enrichWithIcons(MOCK_CATEGORY_TREE)
|
|
||||||
} else {
|
|
||||||
categoryTree.value = enrichWithIcons(MOCK_CATEGORY_TREE)
|
|
||||||
}
|
|
||||||
initCategorySelection()
|
|
||||||
loadEventListAfterCategoryReady()
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
categoryTree.value = enrichWithIcons(MOCK_CATEGORY_TREE)
|
|
||||||
initCategorySelection()
|
|
||||||
loadEventListAfterCategoryReady()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
const sentinel = sentinelRef.value
|
const sentinel = sentinelRef.value
|
||||||
if (sentinel) {
|
if (sentinel) {
|
||||||
@ -996,15 +989,15 @@ onActivated(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.home-category-layer--icon {
|
.home-category-layer--icon {
|
||||||
padding: 12px 16px;
|
padding: 6px 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.home-category-icon-row {
|
.home-category-icon-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
gap: 8px;
|
gap: 6px;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
padding-bottom: 4px;
|
padding-bottom: 2px;
|
||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1015,13 +1008,12 @@ onActivated(() => {
|
|||||||
.home-category-icon-item {
|
.home-category-icon-item {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 4px;
|
justify-content: center;
|
||||||
min-width: 56px;
|
min-width: 44px;
|
||||||
padding: 8px 12px;
|
padding: 4px 10px;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 8px;
|
border-radius: 6px;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: #64748b;
|
color: #64748b;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
|||||||
@ -3,6 +3,16 @@ import { defineConfig } from 'vite'
|
|||||||
import vue from '@vitejs/plugin-vue'
|
import vue from '@vitejs/plugin-vue'
|
||||||
import { nodePolyfills } from 'vite-plugin-node-polyfills'
|
import { nodePolyfills } from 'vite-plugin-node-polyfills'
|
||||||
|
|
||||||
|
/** 移除 crossorigin 属性,避免部分手机浏览器加载资源异常 */
|
||||||
|
function removeCrossorigin() {
|
||||||
|
return {
|
||||||
|
name: 'remove-crossorigin',
|
||||||
|
transformIndexHtml(html: string) {
|
||||||
|
return html.replace(/\s*crossorigin\s*/g, ' ')
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
@ -11,17 +21,19 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
vue(),
|
vue(),
|
||||||
// 2. 将此插件添加到插件数组中
|
|
||||||
nodePolyfills({
|
nodePolyfills({
|
||||||
// 为了彻底解决SIWE等库的问题,建议包含以下选项
|
|
||||||
protocolImports: true,
|
protocolImports: true,
|
||||||
}),
|
}),
|
||||||
|
removeCrossorigin(),
|
||||||
],
|
],
|
||||||
// 3. 可选但推荐:显式定义 process.env 以避免其他潜在错误
|
|
||||||
define: {
|
define: {
|
||||||
'process.env': {},
|
'process.env': {},
|
||||||
},
|
},
|
||||||
|
build: {
|
||||||
|
target: 'es2020',
|
||||||
|
cssTarget: 'chrome64',
|
||||||
|
},
|
||||||
server: {
|
server: {
|
||||||
host: true, // 监听 0.0.0.0,允许局域网内其他设备访问
|
host: true,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user