fix(client): 抽屉内操作不再收抽屉(原地刷新)+ 日期输入框垂直居中
- 出入库详情抽屉:通过/拒绝/撤回/提交/结清/退单/确认定价/打印 全部不收抽屉, 操作完成后原地重拉单据刷新内容(StatefulBuilder+refresh);仅 遮罩/X/ 修改·删除(离开语义)关闭——对齐拍板「点抽屉外才收回」 - 往来抽屉「编辑」、财务往来抽屉「登记收款/付款」(完成后流水原地刷新)、 商品编辑抽屉「保存」同规则;「取消」保持关闭语义 - 日期组件输入框 forceStrutHeight 锁行盒:修等宽字体 ascent/descent 不对称 导致的文字垂直偏移 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -963,23 +963,27 @@ class _StockInListScreenState extends ConsumerState<StockInListScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 点击行/眼睛 → 右侧详情抽屉(对齐原型 openDetail)。
|
// 点击行/眼睛 → 右侧详情抽屉(对齐原型 openDetail)。
|
||||||
|
// 抽屉内按钮不收抽屉(2026-07-04 拍板):操作完成后原地重拉单据刷新内容,
|
||||||
|
// 仅点遮罩/X/「修改·删除」这类离开语义才关闭。
|
||||||
Future<void> _showDetail(BuildContext context, int orderId) async {
|
Future<void> _showDetail(BuildContext context, int orderId) async {
|
||||||
await showOrderDetailDrawer(
|
await showOrderDetailDrawer(
|
||||||
context,
|
context,
|
||||||
builder: (ctx) => FutureBuilder<StockInOrder>(
|
builder: (ctx) => StatefulBuilder(
|
||||||
future: ref.read(stockInRepositoryProvider).get(orderId),
|
builder: (sctx, setSheet) => FutureBuilder<StockInOrder>(
|
||||||
builder: (c, snap) {
|
future: ref.read(stockInRepositoryProvider).get(orderId),
|
||||||
if (snap.connectionState == ConnectionState.waiting) {
|
builder: (c, snap) {
|
||||||
return const Center(child: CircularProgressIndicator());
|
if (snap.connectionState == ConnectionState.waiting) {
|
||||||
}
|
return const Center(child: CircularProgressIndicator());
|
||||||
if (snap.hasError || !snap.hasData) {
|
}
|
||||||
return Center(
|
if (snap.hasError || !snap.hasData) {
|
||||||
child: Text('暂无数据,网络不可用',
|
return Center(
|
||||||
style: TextStyle(color: context.tokens.muted)),
|
child: Text('暂无数据,网络不可用',
|
||||||
);
|
style: TextStyle(color: context.tokens.muted)),
|
||||||
}
|
);
|
||||||
return _buildDetailDrawer(snap.data!);
|
}
|
||||||
},
|
return _buildDetailDrawer(snap.data!, () => setSheet(() {}));
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -991,7 +995,7 @@ class _StockInListScreenState extends ConsumerState<StockInListScreen> {
|
|||||||
static String _fmtQty(double q) =>
|
static String _fmtQty(double q) =>
|
||||||
q == q.roundToDouble() ? q.toStringAsFixed(0) : q.toString();
|
q == q.roundToDouble() ? q.toStringAsFixed(0) : q.toString();
|
||||||
|
|
||||||
Widget _buildDetailDrawer(StockInOrder o) {
|
Widget _buildDetailDrawer(StockInOrder o, VoidCallback refresh) {
|
||||||
final t = context.tokens;
|
final t = context.tokens;
|
||||||
final rb = returnStateBadge(context, o.returnState);
|
final rb = returnStateBadge(context, o.returnState);
|
||||||
return OrderDetailDrawer(
|
return OrderDetailDrawer(
|
||||||
@@ -1031,11 +1035,12 @@ class _StockInListScreenState extends ConsumerState<StockInListScreen> {
|
|||||||
totalText: o.costTotal != null ? _money.format(o.costTotal) : '—',
|
totalText: o.costTotal != null ? _money.format(o.costTotal) : '—',
|
||||||
priceLabel: '进价(单瓶)',
|
priceLabel: '进价(单瓶)',
|
||||||
amountLabel: '总价',
|
amountLabel: '总价',
|
||||||
actionGroups: _detailActionGroups(o),
|
actionGroups: _detailActionGroups(o, refresh),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<DrawerActionGroup> _detailActionGroups(StockInOrder o) {
|
List<DrawerActionGroup> _detailActionGroups(
|
||||||
|
StockInOrder o, VoidCallback refresh) {
|
||||||
final readonly = WriteGuard.isReadonly(ref);
|
final readonly = WriteGuard.isReadonly(ref);
|
||||||
final isAdmin = ref.read(isAdminProvider);
|
final isAdmin = ref.read(isAdminProvider);
|
||||||
final canWithdraw = isAdmin ||
|
final canWithdraw = isAdmin ||
|
||||||
@@ -1051,6 +1056,7 @@ class _StockInListScreenState extends ConsumerState<StockInListScreen> {
|
|||||||
if (!readonly) {
|
if (!readonly) {
|
||||||
switch (o.status) {
|
switch (o.status) {
|
||||||
case 'draft':
|
case 'draft':
|
||||||
|
// 修改/删除是离开语义:收抽屉再走
|
||||||
audit.add(b('修改', () {
|
audit.add(b('修改', () {
|
||||||
close();
|
close();
|
||||||
context.go('/stock-in/edit/${o.id}');
|
context.go('/stock-in/edit/${o.id}');
|
||||||
@@ -1059,36 +1065,36 @@ class _StockInListScreenState extends ConsumerState<StockInListScreen> {
|
|||||||
close();
|
close();
|
||||||
_confirmDelete(context, o);
|
_confirmDelete(context, o);
|
||||||
}, v: DsBtnVariant.danger));
|
}, v: DsBtnVariant.danger));
|
||||||
audit.add(b('提交', () {
|
audit.add(b('提交', () async {
|
||||||
close();
|
await _confirmSubmit(context, o);
|
||||||
_confirmSubmit(context, o);
|
refresh();
|
||||||
}, v: DsBtnVariant.primary));
|
}, v: DsBtnVariant.primary));
|
||||||
break;
|
break;
|
||||||
case 'pending':
|
case 'pending':
|
||||||
audit.add(b('通过', () {
|
audit.add(b('通过', () async {
|
||||||
close();
|
await _confirmApprove(context, o);
|
||||||
_confirmApprove(context, o);
|
refresh();
|
||||||
}, v: DsBtnVariant.primary));
|
}, v: DsBtnVariant.primary));
|
||||||
audit.add(b('拒绝', () {
|
audit.add(b('拒绝', () async {
|
||||||
close();
|
await _confirmReject(context, o);
|
||||||
_confirmReject(context, o);
|
refresh();
|
||||||
}, v: DsBtnVariant.danger));
|
}, v: DsBtnVariant.danger));
|
||||||
if (canWithdraw) {
|
if (canWithdraw) {
|
||||||
audit.add(b('撤回', () {
|
audit.add(b('撤回', () async {
|
||||||
close();
|
await _confirmWithdraw(context, o);
|
||||||
_confirmWithdraw(context, o);
|
refresh();
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'approved':
|
case 'approved':
|
||||||
audit.add(b('结清', () {
|
audit.add(b('结清', () async {
|
||||||
close();
|
await _confirmSettle(context, o.id, 'stock_in');
|
||||||
_confirmSettle(context, o.id, 'stock_in');
|
refresh();
|
||||||
}));
|
}));
|
||||||
if (isAdmin) {
|
if (isAdmin) {
|
||||||
audit.add(b('退单', () {
|
audit.add(b('退单', () async {
|
||||||
close();
|
await _confirmReturn(context, o);
|
||||||
_confirmReturn(context, o);
|
refresh();
|
||||||
}, v: DsBtnVariant.danger));
|
}, v: DsBtnVariant.danger));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1099,9 +1105,9 @@ class _StockInListScreenState extends ConsumerState<StockInListScreen> {
|
|||||||
final pending = o.items.where((it) => it.costPrice == 0).length;
|
final pending = o.items.where((it) => it.costPrice == 0).length;
|
||||||
if (o.status == 'approved' && isAdmin && pending > 0) {
|
if (o.status == 'approved' && isAdmin && pending > 0) {
|
||||||
groups.add(DrawerActionGroup('成本 · $pending 项待定价', [
|
groups.add(DrawerActionGroup('成本 · $pending 项待定价', [
|
||||||
b('确认进价', () {
|
b('确认进价', () async {
|
||||||
close();
|
await _confirmCost(o);
|
||||||
_confirmCost(o);
|
refresh();
|
||||||
}, v: DsBtnVariant.primary),
|
}, v: DsBtnVariant.primary),
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
@@ -1109,12 +1115,8 @@ class _StockInListScreenState extends ConsumerState<StockInListScreen> {
|
|||||||
// 窄屏详情不出打印入口(移动端无打印——用户拍板)。
|
// 窄屏详情不出打印入口(移动端无打印——用户拍板)。
|
||||||
if (!context.isMobile) {
|
if (!context.isMobile) {
|
||||||
groups.add(DrawerActionGroup('打印', [
|
groups.add(DrawerActionGroup('打印', [
|
||||||
b('打印标签', () {
|
b('打印标签', () => _printLabels(o)),
|
||||||
close();
|
|
||||||
_printLabels(o);
|
|
||||||
}),
|
|
||||||
b('打印单据', () async {
|
b('打印单据', () async {
|
||||||
close();
|
|
||||||
final order = await ref.read(stockInRepositoryProvider).get(o.id);
|
final order = await ref.read(stockInRepositoryProvider).get(o.id);
|
||||||
if (mounted) await showStockInOrderPrint(context, ref, order);
|
if (mounted) await showStockInOrderPrint(context, ref, order);
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -1035,23 +1035,27 @@ class _StockOutListScreenState extends ConsumerState<StockOutListScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 点击行/眼睛 → 右侧详情抽屉(对齐原型 openDetail)。
|
// 点击行/眼睛 → 右侧详情抽屉(对齐原型 openDetail)。
|
||||||
|
// 抽屉内按钮不收抽屉(2026-07-04 拍板):操作完成后原地重拉单据刷新内容,
|
||||||
|
// 仅点遮罩/X/「修改·删除」这类离开语义才关闭。
|
||||||
Future<void> _showDetail(BuildContext context, int orderId) async {
|
Future<void> _showDetail(BuildContext context, int orderId) async {
|
||||||
await showOrderDetailDrawer(
|
await showOrderDetailDrawer(
|
||||||
context,
|
context,
|
||||||
builder: (ctx) => FutureBuilder<StockOutOrder>(
|
builder: (ctx) => StatefulBuilder(
|
||||||
future: ref.read(stockOutRepositoryProvider).get(orderId),
|
builder: (sctx, setSheet) => FutureBuilder<StockOutOrder>(
|
||||||
builder: (c, snap) {
|
future: ref.read(stockOutRepositoryProvider).get(orderId),
|
||||||
if (snap.connectionState == ConnectionState.waiting) {
|
builder: (c, snap) {
|
||||||
return const Center(child: CircularProgressIndicator());
|
if (snap.connectionState == ConnectionState.waiting) {
|
||||||
}
|
return const Center(child: CircularProgressIndicator());
|
||||||
if (snap.hasError || !snap.hasData) {
|
}
|
||||||
return Center(
|
if (snap.hasError || !snap.hasData) {
|
||||||
child: Text('暂无数据,网络不可用',
|
return Center(
|
||||||
style: TextStyle(color: context.tokens.muted)),
|
child: Text('暂无数据,网络不可用',
|
||||||
);
|
style: TextStyle(color: context.tokens.muted)),
|
||||||
}
|
);
|
||||||
return _buildDetailDrawer(snap.data!);
|
}
|
||||||
},
|
return _buildDetailDrawer(snap.data!, () => setSheet(() {}));
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1063,7 +1067,7 @@ class _StockOutListScreenState extends ConsumerState<StockOutListScreen> {
|
|||||||
static String _fmtQty(double q) =>
|
static String _fmtQty(double q) =>
|
||||||
q == q.roundToDouble() ? q.toStringAsFixed(0) : q.toString();
|
q == q.roundToDouble() ? q.toStringAsFixed(0) : q.toString();
|
||||||
|
|
||||||
Widget _buildDetailDrawer(StockOutOrder o) {
|
Widget _buildDetailDrawer(StockOutOrder o, VoidCallback refresh) {
|
||||||
final t = context.tokens;
|
final t = context.tokens;
|
||||||
final rb = returnStateBadge(context, o.returnState);
|
final rb = returnStateBadge(context, o.returnState);
|
||||||
return OrderDetailDrawer(
|
return OrderDetailDrawer(
|
||||||
@@ -1112,11 +1116,12 @@ class _StockOutListScreenState extends ConsumerState<StockOutListScreen> {
|
|||||||
ref.read(isAdminProvider) ? _money.format(o.profitTotal) : null,
|
ref.read(isAdminProvider) ? _money.format(o.profitTotal) : null,
|
||||||
priceLabel: '售价',
|
priceLabel: '售价',
|
||||||
amountLabel: '小计',
|
amountLabel: '小计',
|
||||||
actionGroups: _detailActionGroups(o),
|
actionGroups: _detailActionGroups(o, refresh),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<DrawerActionGroup> _detailActionGroups(StockOutOrder o) {
|
List<DrawerActionGroup> _detailActionGroups(
|
||||||
|
StockOutOrder o, VoidCallback refresh) {
|
||||||
final readonly = WriteGuard.isReadonly(ref);
|
final readonly = WriteGuard.isReadonly(ref);
|
||||||
final isAdmin = ref.read(isAdminProvider);
|
final isAdmin = ref.read(isAdminProvider);
|
||||||
final isMine =
|
final isMine =
|
||||||
@@ -1131,6 +1136,7 @@ class _StockOutListScreenState extends ConsumerState<StockOutListScreen> {
|
|||||||
if (!readonly) {
|
if (!readonly) {
|
||||||
switch (o.status) {
|
switch (o.status) {
|
||||||
case 'draft':
|
case 'draft':
|
||||||
|
// 修改/删除是离开语义:收抽屉再走
|
||||||
audit.add(b('修改', () {
|
audit.add(b('修改', () {
|
||||||
close();
|
close();
|
||||||
context.go('/stock-out/edit/${o.id}');
|
context.go('/stock-out/edit/${o.id}');
|
||||||
@@ -1139,36 +1145,36 @@ class _StockOutListScreenState extends ConsumerState<StockOutListScreen> {
|
|||||||
close();
|
close();
|
||||||
_confirmDelete(context, o);
|
_confirmDelete(context, o);
|
||||||
}, v: DsBtnVariant.danger));
|
}, v: DsBtnVariant.danger));
|
||||||
audit.add(b('提交', () {
|
audit.add(b('提交', () async {
|
||||||
close();
|
await _confirmSubmit(context, o);
|
||||||
_confirmSubmit(context, o);
|
refresh();
|
||||||
}, v: DsBtnVariant.primary));
|
}, v: DsBtnVariant.primary));
|
||||||
break;
|
break;
|
||||||
case 'pending':
|
case 'pending':
|
||||||
audit.add(b('通过', () {
|
audit.add(b('通过', () async {
|
||||||
close();
|
await _confirmApprove(context, o);
|
||||||
_confirmApprove(context, o);
|
refresh();
|
||||||
}, v: DsBtnVariant.primary));
|
}, v: DsBtnVariant.primary));
|
||||||
audit.add(b('拒绝', () {
|
audit.add(b('拒绝', () async {
|
||||||
close();
|
await _confirmReject(context, o);
|
||||||
_confirmReject(context, o);
|
refresh();
|
||||||
}, v: DsBtnVariant.danger));
|
}, v: DsBtnVariant.danger));
|
||||||
if (isAdmin || isMine) {
|
if (isAdmin || isMine) {
|
||||||
audit.add(b('撤回', () {
|
audit.add(b('撤回', () async {
|
||||||
close();
|
await _confirmWithdraw(context, o);
|
||||||
_confirmWithdraw(context, o);
|
refresh();
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'approved':
|
case 'approved':
|
||||||
audit.add(b('结清', () {
|
audit.add(b('结清', () async {
|
||||||
close();
|
await _confirmSettle(context, o.id, 'stock_out');
|
||||||
_confirmSettle(context, o.id, 'stock_out');
|
refresh();
|
||||||
}));
|
}));
|
||||||
if (isAdmin || isMine) {
|
if (isAdmin || isMine) {
|
||||||
audit.add(b('退单', () {
|
audit.add(b('退单', () async {
|
||||||
close();
|
await _confirmReturn(context, o);
|
||||||
_confirmReturn(context, o);
|
refresh();
|
||||||
}, v: DsBtnVariant.danger));
|
}, v: DsBtnVariant.danger));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1179,9 +1185,9 @@ class _StockOutListScreenState extends ConsumerState<StockOutListScreen> {
|
|||||||
final pending = o.items.where((it) => it.salePrice <= 0).length;
|
final pending = o.items.where((it) => it.salePrice <= 0).length;
|
||||||
if (o.status == 'approved' && pending > 0 && !readonly) {
|
if (o.status == 'approved' && pending > 0 && !readonly) {
|
||||||
groups.add(DrawerActionGroup('售价 · $pending 项待定价', [
|
groups.add(DrawerActionGroup('售价 · $pending 项待定价', [
|
||||||
b('确认售价', () {
|
b('确认售价', () async {
|
||||||
close();
|
await _confirmSale(o);
|
||||||
_confirmSale(o);
|
refresh();
|
||||||
}, v: DsBtnVariant.primary),
|
}, v: DsBtnVariant.primary),
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
@@ -1189,12 +1195,8 @@ class _StockOutListScreenState extends ConsumerState<StockOutListScreen> {
|
|||||||
// 窄屏详情不出打印入口(移动端无打印——用户拍板)。
|
// 窄屏详情不出打印入口(移动端无打印——用户拍板)。
|
||||||
if (!context.isMobile) {
|
if (!context.isMobile) {
|
||||||
groups.add(DrawerActionGroup('打印', [
|
groups.add(DrawerActionGroup('打印', [
|
||||||
b('打印标签', () {
|
b('打印标签', () => _printLabels(o)),
|
||||||
close();
|
|
||||||
_printLabels(o);
|
|
||||||
}),
|
|
||||||
b('打印单据', () async {
|
b('打印单据', () async {
|
||||||
close();
|
|
||||||
final order = await ref.read(stockOutRepositoryProvider).get(o.id);
|
final order = await ref.read(stockOutRepositoryProvider).get(o.id);
|
||||||
if (mounted) await showStockOutOrderPrint(context, ref, order);
|
if (mounted) await showStockOutOrderPrint(context, ref, order);
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -181,16 +181,22 @@ class _FinancePartnerDrawer extends ConsumerWidget {
|
|||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
DsButton('登记收款', small: true, onPressed: () {
|
// 抽屉内按钮不收抽屉(2026-07-04 拍板):登记弹窗叠在抽屉之上,
|
||||||
Navigator.of(context).pop();
|
// 保存后失效流水 provider → 抽屉内近期流水原地刷新
|
||||||
showFinanceEntryDialog(context,
|
DsButton('登记收款', small: true, onPressed: () async {
|
||||||
|
await showFinanceEntryDialog(context,
|
||||||
type: 'receipt', partnerId: row.partnerId);
|
type: 'receipt', partnerId: row.partnerId);
|
||||||
|
if (row.partnerId != null) {
|
||||||
|
ref.invalidate(_partnerFlowsProvider(row.partnerId!));
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
DsButton('登记付款', small: true, onPressed: () {
|
DsButton('登记付款', small: true, onPressed: () async {
|
||||||
Navigator.of(context).pop();
|
await showFinanceEntryDialog(context,
|
||||||
showFinanceEntryDialog(context,
|
|
||||||
type: 'payment', partnerId: row.partnerId);
|
type: 'payment', partnerId: row.partnerId);
|
||||||
|
if (row.partnerId != null) {
|
||||||
|
ref.invalidate(_partnerFlowsProvider(row.partnerId!));
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -194,10 +194,8 @@ class _PartnerDetailDrawer extends ConsumerWidget {
|
|||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
if (onEdit != null)
|
if (onEdit != null)
|
||||||
WriteGuard(
|
WriteGuard(
|
||||||
child: DsButton('编辑', small: true, onPressed: () {
|
// 抽屉内按钮不收抽屉(2026-07-04 拍板):编辑弹窗叠在抽屉之上
|
||||||
Navigator.of(context).pop();
|
child: DsButton('编辑', small: true, onPressed: onEdit),
|
||||||
onEdit!();
|
|
||||||
}),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -623,7 +623,7 @@ class _ProductEditorDrawerState extends ConsumerState<_ProductEditorDrawer> {
|
|||||||
if (_introDocId != null) 'description_doc_id': _introDocId,
|
if (_introDocId != null) 'description_doc_id': _introDocId,
|
||||||
});
|
});
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
Navigator.of(context).pop();
|
// 抽屉内按钮不收抽屉(2026-07-04 拍板):保存后停留,可继续编辑
|
||||||
showDsToast(context, '商品介绍已保存 ✓');
|
showDsToast(context, '商品介绍已保存 ✓');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
|
|||||||
@@ -427,9 +427,16 @@ class _DatePaneState extends State<_DatePane> {
|
|||||||
focusNode: _focus,
|
focusNode: _focus,
|
||||||
keyboardType: TextInputType.datetime,
|
keyboardType: TextInputType.datetime,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
textAlignVertical: TextAlignVertical.center,
|
// 强制 strut 统一行盒(等宽字体 ascent/descent 不对称会让文字
|
||||||
|
// 视觉偏离垂直中心;height:1 行高=字号,Center 精确居中)
|
||||||
|
strutStyle: const StrutStyle(
|
||||||
|
fontSize: AppDims.fsBody,
|
||||||
|
height: 1.0,
|
||||||
|
forceStrutHeight: true,
|
||||||
|
),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: AppDims.fsBody,
|
fontSize: AppDims.fsBody,
|
||||||
|
height: 1.0,
|
||||||
color: t.text,
|
color: t.text,
|
||||||
fontFamily: AppFonts.mono,
|
fontFamily: AppFonts.mono,
|
||||||
fontFamilyFallback: AppFonts.monoFallback),
|
fontFamilyFallback: AppFonts.monoFallback),
|
||||||
|
|||||||
Reference in New Issue
Block a user