feat:移动端表格宽度动态计算
This commit is contained in:
@@ -27,12 +27,16 @@ import MobileSettingModal from './MobileSettingModal';
|
|||||||
import { ExitIcon, SettingsIcon, StarIcon } from './Icons';
|
import { ExitIcon, SettingsIcon, StarIcon } from './Icons';
|
||||||
|
|
||||||
const MOBILE_NON_FROZEN_COLUMN_IDS = [
|
const MOBILE_NON_FROZEN_COLUMN_IDS = [
|
||||||
|
'latestNav',
|
||||||
|
'estimateNav',
|
||||||
'yesterdayChangePercent',
|
'yesterdayChangePercent',
|
||||||
'estimateChangePercent',
|
'estimateChangePercent',
|
||||||
'todayProfit',
|
'todayProfit',
|
||||||
'holdingProfit',
|
'holdingProfit',
|
||||||
];
|
];
|
||||||
const MOBILE_COLUMN_HEADERS = {
|
const MOBILE_COLUMN_HEADERS = {
|
||||||
|
latestNav: '最新净值',
|
||||||
|
estimateNav: '估算净值',
|
||||||
yesterdayChangePercent: '昨日涨跌幅',
|
yesterdayChangePercent: '昨日涨跌幅',
|
||||||
estimateChangePercent: '估值涨跌幅',
|
estimateChangePercent: '估值涨跌幅',
|
||||||
todayProfit: '当日收益',
|
todayProfit: '当日收益',
|
||||||
@@ -214,6 +218,48 @@ export default function MobileFundTable({
|
|||||||
return allVisible;
|
return allVisible;
|
||||||
});
|
});
|
||||||
const [settingModalOpen, setSettingModalOpen] = useState(false);
|
const [settingModalOpen, setSettingModalOpen] = useState(false);
|
||||||
|
const tableContainerRef = useRef(null);
|
||||||
|
const [tableContainerWidth, setTableContainerWidth] = useState(0);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const el = tableContainerRef.current;
|
||||||
|
if (!el) return;
|
||||||
|
const updateWidth = () => setTableContainerWidth(el.clientWidth || 0);
|
||||||
|
updateWidth();
|
||||||
|
const ro = new ResizeObserver(updateWidth);
|
||||||
|
ro.observe(el);
|
||||||
|
return () => ro.disconnect();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const NAME_CELL_WIDTH = 140;
|
||||||
|
const GAP = 12;
|
||||||
|
const LAST_COLUMN_EXTRA = 12;
|
||||||
|
const FALLBACK_WIDTHS = {
|
||||||
|
fundName: 140,
|
||||||
|
latestNav: 64,
|
||||||
|
estimateNav: 64,
|
||||||
|
yesterdayChangePercent: 72,
|
||||||
|
estimateChangePercent: 80,
|
||||||
|
todayProfit: 80,
|
||||||
|
holdingProfit: 80,
|
||||||
|
};
|
||||||
|
|
||||||
|
const columnWidthMap = useMemo(() => {
|
||||||
|
const visibleNonNameIds = mobileColumnOrder.filter((id) => mobileColumnVisibility[id] !== false);
|
||||||
|
const nonNameCount = visibleNonNameIds.length;
|
||||||
|
if (tableContainerWidth > 0 && nonNameCount > 0) {
|
||||||
|
const gapTotal = nonNameCount >= 3 ? 3 * GAP : (nonNameCount) * GAP;
|
||||||
|
const remaining = tableContainerWidth - NAME_CELL_WIDTH - gapTotal - LAST_COLUMN_EXTRA;
|
||||||
|
const divisor = nonNameCount >= 3 ? 3 : nonNameCount;
|
||||||
|
const otherColumnWidth = Math.max(48, Math.floor(remaining / divisor));
|
||||||
|
const map = { fundName: NAME_CELL_WIDTH };
|
||||||
|
MOBILE_NON_FROZEN_COLUMN_IDS.forEach((id) => {
|
||||||
|
map[id] = otherColumnWidth;
|
||||||
|
});
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
return { ...FALLBACK_WIDTHS };
|
||||||
|
}, [tableContainerWidth, mobileColumnOrder, mobileColumnVisibility]);
|
||||||
|
|
||||||
const handleResetMobileColumnOrder = () => {
|
const handleResetMobileColumnOrder = () => {
|
||||||
const defaultOrder = [...MOBILE_NON_FROZEN_COLUMN_IDS];
|
const defaultOrder = [...MOBILE_NON_FROZEN_COLUMN_IDS];
|
||||||
@@ -356,7 +402,31 @@ export default function MobileFundTable({
|
|||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
cell: (info) => <MobileFundNameCell info={info} />,
|
cell: (info) => <MobileFundNameCell info={info} />,
|
||||||
meta: { align: 'left', cellClassName: 'name-cell', width: 140 },
|
meta: { align: 'left', cellClassName: 'name-cell', width: columnWidthMap.fundName },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'latestNav',
|
||||||
|
header: '最新净值',
|
||||||
|
cell: (info) => (
|
||||||
|
<span style={{ display: 'block', width: '100%', fontWeight: 700 }}>
|
||||||
|
<FitText maxFontSize={14} minFontSize={10}>
|
||||||
|
{info.getValue() ?? '—'}
|
||||||
|
</FitText>
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
meta: { align: 'right', cellClassName: 'value-cell', width: columnWidthMap.latestNav },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'estimateNav',
|
||||||
|
header: '估算净值',
|
||||||
|
cell: (info) => (
|
||||||
|
<span style={{ display: 'block', width: '100%', fontWeight: 700 }}>
|
||||||
|
<FitText maxFontSize={14} minFontSize={10}>
|
||||||
|
{info.getValue() ?? '—'}
|
||||||
|
</FitText>
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
meta: { align: 'right', cellClassName: 'value-cell', width: columnWidthMap.estimateNav },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'yesterdayChangePercent',
|
accessorKey: 'yesterdayChangePercent',
|
||||||
@@ -375,7 +445,7 @@ export default function MobileFundTable({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
meta: { align: 'right', cellClassName: 'change-cell', width: 72 },
|
meta: { align: 'right', cellClassName: 'change-cell', width: columnWidthMap.yesterdayChangePercent },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'estimateChangePercent',
|
accessorKey: 'estimateChangePercent',
|
||||||
@@ -395,7 +465,7 @@ export default function MobileFundTable({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
meta: { align: 'right', cellClassName: 'est-change-cell', width: 80 },
|
meta: { align: 'right', cellClassName: 'est-change-cell', width: columnWidthMap.estimateChangePercent },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'todayProfit',
|
accessorKey: 'todayProfit',
|
||||||
@@ -413,7 +483,7 @@ export default function MobileFundTable({
|
|||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
meta: { align: 'right', cellClassName: 'profit-cell', width: 80 },
|
meta: { align: 'right', cellClassName: 'profit-cell', width: columnWidthMap.todayProfit },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'holdingProfit',
|
accessorKey: 'holdingProfit',
|
||||||
@@ -441,10 +511,10 @@ export default function MobileFundTable({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
meta: { align: 'right', cellClassName: 'holding-cell', width: 80 },
|
meta: { align: 'right', cellClassName: 'holding-cell', width: columnWidthMap.holdingProfit },
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[currentTab, favorites, refreshing]
|
[currentTab, favorites, refreshing, columnWidthMap]
|
||||||
);
|
);
|
||||||
|
|
||||||
const table = useReactTable({
|
const table = useReactTable({
|
||||||
@@ -482,7 +552,6 @@ export default function MobileFundTable({
|
|||||||
|
|
||||||
const headerGroup = table.getHeaderGroups()[0];
|
const headerGroup = table.getHeaderGroups()[0];
|
||||||
|
|
||||||
const LAST_COLUMN_EXTRA = 12;
|
|
||||||
const mobileGridLayout = (() => {
|
const mobileGridLayout = (() => {
|
||||||
if (!headerGroup?.headers?.length) return { gridTemplateColumns: '', minWidth: undefined };
|
if (!headerGroup?.headers?.length) return { gridTemplateColumns: '', minWidth: undefined };
|
||||||
const gap = 12;
|
const gap = 12;
|
||||||
@@ -501,12 +570,12 @@ export default function MobileFundTable({
|
|||||||
|
|
||||||
const getAlignClass = (columnId) => {
|
const getAlignClass = (columnId) => {
|
||||||
if (columnId === 'fundName') return '';
|
if (columnId === 'fundName') return '';
|
||||||
if (['yesterdayChangePercent', 'estimateChangePercent', 'todayProfit', 'holdingProfit'].includes(columnId)) return 'text-right';
|
if (['latestNav', 'estimateNav', 'yesterdayChangePercent', 'estimateChangePercent', 'todayProfit', 'holdingProfit'].includes(columnId)) return 'text-right';
|
||||||
return 'text-right';
|
return 'text-right';
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mobile-fund-table">
|
<div className="mobile-fund-table" ref={tableContainerRef}>
|
||||||
<div
|
<div
|
||||||
className="mobile-fund-table-scroll"
|
className="mobile-fund-table-scroll"
|
||||||
style={mobileGridLayout.minWidth != null ? { minWidth: mobileGridLayout.minWidth } : undefined}
|
style={mobileGridLayout.minWidth != null ? { minWidth: mobileGridLayout.minWidth } : undefined}
|
||||||
|
|||||||
@@ -23,11 +23,13 @@ import {
|
|||||||
} from '@dnd-kit/sortable';
|
} from '@dnd-kit/sortable';
|
||||||
import { CSS } from '@dnd-kit/utilities';
|
import { CSS } from '@dnd-kit/utilities';
|
||||||
import ConfirmModal from './ConfirmModal';
|
import ConfirmModal from './ConfirmModal';
|
||||||
|
import FitText from './FitText';
|
||||||
import PcTableSettingModal from './PcTableSettingModal';
|
import PcTableSettingModal from './PcTableSettingModal';
|
||||||
import { DragIcon, ExitIcon, SettingsIcon, StarIcon, TrashIcon } from './Icons';
|
import { DragIcon, ExitIcon, SettingsIcon, StarIcon, TrashIcon } from './Icons';
|
||||||
|
|
||||||
const NON_FROZEN_COLUMN_IDS = [
|
const NON_FROZEN_COLUMN_IDS = [
|
||||||
'navOrEstimate',
|
'latestNav',
|
||||||
|
'estimateNav',
|
||||||
'yesterdayChangePercent',
|
'yesterdayChangePercent',
|
||||||
'estimateChangePercent',
|
'estimateChangePercent',
|
||||||
'holdingAmount',
|
'holdingAmount',
|
||||||
@@ -35,7 +37,8 @@ const NON_FROZEN_COLUMN_IDS = [
|
|||||||
'holdingProfit',
|
'holdingProfit',
|
||||||
];
|
];
|
||||||
const COLUMN_HEADERS = {
|
const COLUMN_HEADERS = {
|
||||||
navOrEstimate: '净值/估值',
|
latestNav: '最新净值',
|
||||||
|
estimateNav: '估算净值',
|
||||||
yesterdayChangePercent: '昨日涨跌幅',
|
yesterdayChangePercent: '昨日涨跌幅',
|
||||||
estimateChangePercent: '估值涨跌幅',
|
estimateChangePercent: '估值涨跌幅',
|
||||||
holdingAmount: '持仓金额',
|
holdingAmount: '持仓金额',
|
||||||
@@ -98,7 +101,8 @@ function SortableRow({ row, children, isTableDragging, disabled }) {
|
|||||||
* {
|
* {
|
||||||
* fundName: string; // 基金名称
|
* fundName: string; // 基金名称
|
||||||
* code?: string; // 基金代码(可选,只用于展示在名称下方)
|
* code?: string; // 基金代码(可选,只用于展示在名称下方)
|
||||||
* navOrEstimate: string|number; // 净值/估值
|
* latestNav: string|number; // 最新净值
|
||||||
|
* estimateNav: string|number; // 估算净值
|
||||||
* yesterdayChangePercent: string|number; // 昨日涨跌幅
|
* yesterdayChangePercent: string|number; // 昨日涨跌幅
|
||||||
* estimateChangePercent: string|number; // 估值涨跌幅
|
* estimateChangePercent: string|number; // 估值涨跌幅
|
||||||
* holdingAmount: string|number; // 持仓金额
|
* holdingAmount: string|number; // 持仓金额
|
||||||
@@ -396,12 +400,29 @@ export default function PcFundTable({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'navOrEstimate',
|
accessorKey: 'latestNav',
|
||||||
header: '净值/估值',
|
header: '最新净值',
|
||||||
size: 100,
|
size: 100,
|
||||||
minSize: 80,
|
minSize: 80,
|
||||||
cell: (info) => (
|
cell: (info) => (
|
||||||
<span style={{ fontWeight: 700 }}>{info.getValue() ?? '—'}</span>
|
<FitText style={{ fontWeight: 700 }} maxFontSize={14} minFontSize={10}>
|
||||||
|
{info.getValue() ?? '—'}
|
||||||
|
</FitText>
|
||||||
|
),
|
||||||
|
meta: {
|
||||||
|
align: 'right',
|
||||||
|
cellClassName: 'value-cell',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'estimateNav',
|
||||||
|
header: '估算净值',
|
||||||
|
size: 100,
|
||||||
|
minSize: 80,
|
||||||
|
cell: (info) => (
|
||||||
|
<FitText style={{ fontWeight: 700 }} maxFontSize={14} minFontSize={10}>
|
||||||
|
{info.getValue() ?? '—'}
|
||||||
|
</FitText>
|
||||||
),
|
),
|
||||||
meta: {
|
meta: {
|
||||||
align: 'right',
|
align: 'right',
|
||||||
@@ -420,9 +441,9 @@ export default function PcFundTable({
|
|||||||
const cls = value > 0 ? 'up' : value < 0 ? 'down' : '';
|
const cls = value > 0 ? 'up' : value < 0 ? 'down' : '';
|
||||||
return (
|
return (
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: 0 }}>
|
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: 0 }}>
|
||||||
<span className={cls} style={{ fontWeight: 700 }}>
|
<FitText className={cls} style={{ fontWeight: 700 }} maxFontSize={14} minFontSize={10} as="div">
|
||||||
{info.getValue() ?? '—'}
|
{info.getValue() ?? '—'}
|
||||||
</span>
|
</FitText>
|
||||||
<span className="muted" style={{ fontSize: '11px' }}>
|
<span className="muted" style={{ fontSize: '11px' }}>
|
||||||
{date}
|
{date}
|
||||||
</span>
|
</span>
|
||||||
@@ -447,9 +468,9 @@ export default function PcFundTable({
|
|||||||
const cls = isMuted ? 'muted' : value > 0 ? 'up' : value < 0 ? 'down' : '';
|
const cls = isMuted ? 'muted' : value > 0 ? 'up' : value < 0 ? 'down' : '';
|
||||||
return (
|
return (
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: 0 }}>
|
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: 0 }}>
|
||||||
<span className={cls} style={{ fontWeight: 700 }}>
|
<FitText className={cls} style={{ fontWeight: 700 }} maxFontSize={14} minFontSize={10} as="div">
|
||||||
{info.getValue() ?? '—'}
|
{info.getValue() ?? '—'}
|
||||||
</span>
|
</FitText>
|
||||||
<span className="muted" style={{ fontSize: '11px' }}>
|
<span className="muted" style={{ fontSize: '11px' }}>
|
||||||
{time}
|
{time}
|
||||||
</span>
|
</span>
|
||||||
@@ -494,13 +515,17 @@ export default function PcFundTable({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
title="点击设置持仓"
|
title="点击设置持仓"
|
||||||
style={{ display: 'inline-flex', alignItems: 'center', cursor: 'pointer' }}
|
style={{ display: 'flex', alignItems: 'center', cursor: 'pointer', width: '100%', minWidth: 0 }}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation?.();
|
e.stopPropagation?.();
|
||||||
onHoldingAmountClickRef.current?.(original, { hasHolding: true });
|
onHoldingAmountClickRef.current?.(original, { hasHolding: true });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span style={{ fontWeight: 700, marginRight: 6 }}>{info.getValue() ?? '—'}</span>
|
<div style={{ flex: '1 1 0', minWidth: 0 }}>
|
||||||
|
<FitText style={{ fontWeight: 700 }} maxFontSize={14} minFontSize={10}>
|
||||||
|
{info.getValue() ?? '—'}
|
||||||
|
</FitText>
|
||||||
|
</div>
|
||||||
<button
|
<button
|
||||||
className="icon-button no-hover"
|
className="icon-button no-hover"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
@@ -508,7 +533,7 @@ export default function PcFundTable({
|
|||||||
onHoldingAmountClickRef.current?.(original, { hasHolding: true });
|
onHoldingAmountClickRef.current?.(original, { hasHolding: true });
|
||||||
}}
|
}}
|
||||||
title="编辑持仓"
|
title="编辑持仓"
|
||||||
style={{ border: 'none', width: '28px', height: '28px', marginLeft: -6, backgroundColor: 'transparent' }}
|
style={{ border: 'none', width: '28px', height: '28px', marginLeft: 4, flexShrink: 0, backgroundColor: 'transparent' }}
|
||||||
>
|
>
|
||||||
<SettingsIcon width="14" height="14" />
|
<SettingsIcon width="14" height="14" />
|
||||||
</button>
|
</button>
|
||||||
@@ -531,9 +556,9 @@ export default function PcFundTable({
|
|||||||
const hasProfit = value != null;
|
const hasProfit = value != null;
|
||||||
const cls = hasProfit ? (value > 0 ? 'up' : value < 0 ? 'down' : '') : 'muted';
|
const cls = hasProfit ? (value > 0 ? 'up' : value < 0 ? 'down' : '') : 'muted';
|
||||||
return (
|
return (
|
||||||
<span className={cls} style={{ fontWeight: 700 }}>
|
<FitText className={cls} style={{ fontWeight: 700 }} maxFontSize={14} minFontSize={10}>
|
||||||
{hasProfit ? (info.getValue() ?? '') : ''}
|
{hasProfit ? (info.getValue() ?? '') : ''}
|
||||||
</span>
|
</FitText>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
meta: {
|
meta: {
|
||||||
@@ -554,16 +579,16 @@ export default function PcFundTable({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
title="点击切换金额/百分比"
|
title="点击切换金额/百分比"
|
||||||
style={{ cursor: hasTotal ? 'pointer' : 'default' }}
|
style={{ cursor: hasTotal ? 'pointer' : 'default', width: '100%' }}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
if (!hasTotal) return;
|
if (!hasTotal) return;
|
||||||
e.stopPropagation?.();
|
e.stopPropagation?.();
|
||||||
onHoldingProfitClickRef.current?.(original);
|
onHoldingProfitClickRef.current?.(original);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span className={cls} style={{ fontWeight: 700 }}>
|
<FitText className={cls} style={{ fontWeight: 700 }} maxFontSize={14} minFontSize={10}>
|
||||||
{hasTotal ? (info.getValue() ?? '') : ''}
|
{hasTotal ? (info.getValue() ?? '') : ''}
|
||||||
</span>
|
</FitText>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -836,6 +861,8 @@ export default function PcFundTable({
|
|||||||
const columnId = cell.column.id || cell.column.columnDef?.accessorKey;
|
const columnId = cell.column.id || cell.column.columnDef?.accessorKey;
|
||||||
const isNameColumn = columnId === 'fundName';
|
const isNameColumn = columnId === 'fundName';
|
||||||
const rightAlignedColumns = new Set([
|
const rightAlignedColumns = new Set([
|
||||||
|
'latestNav',
|
||||||
|
'estimateNav',
|
||||||
'yesterdayChangePercent',
|
'yesterdayChangePercent',
|
||||||
'estimateChangePercent',
|
'estimateChangePercent',
|
||||||
'holdingAmount',
|
'holdingAmount',
|
||||||
|
|||||||
17
app/page.jsx
17
app/page.jsx
@@ -728,14 +728,12 @@ export default function HomePage() {
|
|||||||
() =>
|
() =>
|
||||||
displayFunds.map((f) => {
|
displayFunds.map((f) => {
|
||||||
const hasTodayData = f.jzrq === todayStr;
|
const hasTodayData = f.jzrq === todayStr;
|
||||||
const shouldHideChange = isTradingDay && !hasTodayData;
|
const latestNav = f.dwjz != null && f.dwjz !== '' ? (typeof f.dwjz === 'number' ? Number(f.dwjz).toFixed(4) : String(f.dwjz)) : '—';
|
||||||
const navOrEstimate = !shouldHideChange
|
const estimateNav = f.noValuation
|
||||||
? (f.dwjz ?? '—')
|
? '—'
|
||||||
: (f.noValuation
|
: (f.estPricedCoverage > 0.05
|
||||||
? (f.dwjz ?? '—')
|
? (f.estGsz != null ? Number(f.estGsz).toFixed(4) : '—')
|
||||||
: (f.estPricedCoverage > 0.05
|
: (f.gsz != null ? (typeof f.gsz === 'number' ? Number(f.gsz).toFixed(4) : String(f.gsz)) : '—'));
|
||||||
? (f.estGsz != null ? Number(f.estGsz).toFixed(4) : '—')
|
|
||||||
: (f.gsz ?? '—')));
|
|
||||||
|
|
||||||
const yesterdayChangePercent =
|
const yesterdayChangePercent =
|
||||||
f.zzl != null && f.zzl !== ''
|
f.zzl != null && f.zzl !== ''
|
||||||
@@ -794,7 +792,8 @@ export default function HomePage() {
|
|||||||
code: f.code,
|
code: f.code,
|
||||||
fundName: f.name,
|
fundName: f.name,
|
||||||
isUpdated: f.jzrq === todayStr,
|
isUpdated: f.jzrq === todayStr,
|
||||||
navOrEstimate,
|
latestNav,
|
||||||
|
estimateNav,
|
||||||
yesterdayChangePercent,
|
yesterdayChangePercent,
|
||||||
yesterdayChangeValue,
|
yesterdayChangeValue,
|
||||||
yesterdayDate,
|
yesterdayDate,
|
||||||
|
|||||||
Reference in New Issue
Block a user