Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e5e6a64605 | |||
| 281cfdb0e4 |
@@ -49,6 +49,17 @@ const statusTag = (s: string) => {
|
||||
return <Tag color={m.color}>{m.label}</Tag>;
|
||||
};
|
||||
|
||||
// 反馈类型:comparison=比价反馈 / profile(及旧数据)=普通反馈
|
||||
const SOURCE_META: Record<string, { label: string; color: string }> = {
|
||||
comparison: { label: '比价反馈', color: 'geekblue' },
|
||||
profile: { label: '普通反馈', color: 'default' },
|
||||
};
|
||||
|
||||
const sourceTag = (s: string) => {
|
||||
const m = SOURCE_META[s] ?? { label: '普通反馈', color: 'default' };
|
||||
return <Tag color={m.color}>{m.label}</Tag>;
|
||||
};
|
||||
|
||||
function FeedbackImages({ images }: { images: string[] | null }) {
|
||||
if (!images || !images.length) return <Text type="secondary">无截图</Text>;
|
||||
return (
|
||||
@@ -113,12 +124,14 @@ export default function FeedbackHandleDrawer({ feedback, open, onClose, onDone }
|
||||
await api.post(`/admin/api/feedbacks/${feedback.id}/approve`, {
|
||||
reward_coins: v.reward_coins,
|
||||
note: v.note?.trim() || null,
|
||||
reply: v.reply?.trim() || null,
|
||||
});
|
||||
message.success(`已采纳,发放 ${v.reward_coins} 金币`);
|
||||
} else {
|
||||
await api.post(`/admin/api/feedbacks/${feedback.id}/reject`, {
|
||||
reason: v.reason.trim(),
|
||||
note: v.note?.trim() || null,
|
||||
reply: v.reply?.trim() || null,
|
||||
});
|
||||
message.success('已拒绝');
|
||||
}
|
||||
@@ -155,6 +168,14 @@ export default function FeedbackHandleDrawer({ feedback, open, onClose, onDone }
|
||||
<Text type="secondary">
|
||||
用户 {feedback.user_id} · {formatWallTime(feedback.created_at)}
|
||||
</Text>
|
||||
<div style={{ marginTop: 6 }}>
|
||||
{sourceTag(feedback.source)}
|
||||
{feedback.scene ? (
|
||||
<Text type="secondary" style={{ marginLeft: 6 }}>
|
||||
场景:{feedback.scene}
|
||||
</Text>
|
||||
) : null}
|
||||
</div>
|
||||
<Paragraph style={{ whiteSpace: 'pre-wrap', marginTop: 8, marginBottom: 8 }}>
|
||||
{feedback.content || '-'}
|
||||
</Paragraph>
|
||||
@@ -200,7 +221,11 @@ export default function FeedbackHandleDrawer({ feedback, open, onClose, onDone }
|
||||
</div>
|
||||
)}
|
||||
<div style={{ marginTop: 6 }}>
|
||||
审核备注:{feedback.review_note || <Text type="secondary">(无)</Text>}
|
||||
审核备注(内部):{feedback.review_note || <Text type="secondary">(无)</Text>}
|
||||
</div>
|
||||
<div style={{ marginTop: 6 }}>
|
||||
给用户的回复(用户可见):
|
||||
{feedback.admin_reply || <Text type="secondary">(无)</Text>}
|
||||
</div>
|
||||
{isPending(feedback.status) && (
|
||||
<div style={{ marginTop: 6 }}>
|
||||
@@ -243,11 +268,23 @@ export default function FeedbackHandleDrawer({ feedback, open, onClose, onDone }
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="note"
|
||||
label="采纳要点 / 审核备注(选填)"
|
||||
label="采纳要点 / 审核备注(选填,内部)"
|
||||
rules={[{ max: 256, message: '最多 256 字' }]}
|
||||
>
|
||||
<Input.TextArea rows={3} maxLength={256} showCount placeholder="如:建议已采纳,将在下个版本上线" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="reply"
|
||||
label="给用户的回复(选填,用户端可见)"
|
||||
rules={[{ max: 256, message: '最多 256 字' }]}
|
||||
>
|
||||
<Input.TextArea
|
||||
rows={2}
|
||||
maxLength={256}
|
||||
showCount
|
||||
placeholder="将展示在用户的「我的反馈」里,如:感谢反馈,已收到~"
|
||||
/>
|
||||
</Form.Item>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
@@ -268,6 +305,18 @@ export default function FeedbackHandleDrawer({ feedback, open, onClose, onDone }
|
||||
>
|
||||
<Input.TextArea rows={2} maxLength={256} showCount placeholder="运营内部备注" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="reply"
|
||||
label="给用户的回复(选填,用户端可见)"
|
||||
rules={[{ max: 256, message: '最多 256 字' }]}
|
||||
>
|
||||
<Input.TextArea
|
||||
rows={2}
|
||||
maxLength={256}
|
||||
showCount
|
||||
placeholder="除未采纳原因外,想额外对用户说的话(选填)"
|
||||
/>
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
</Form>
|
||||
@@ -294,7 +343,10 @@ export default function FeedbackHandleDrawer({ feedback, open, onClose, onDone }
|
||||
<Text type="secondary">
|
||||
#{h.id} · {formatWallTime(h.created_at)}
|
||||
</Text>
|
||||
{statusTag(h.status)}
|
||||
<Space size={4}>
|
||||
{sourceTag(h.source)}
|
||||
{statusTag(h.status)}
|
||||
</Space>
|
||||
</div>
|
||||
<Paragraph
|
||||
style={{ whiteSpace: 'pre-wrap', margin: '6px 0 0' }}
|
||||
@@ -313,6 +365,9 @@ export default function FeedbackHandleDrawer({ feedback, open, onClose, onDone }
|
||||
未采纳:{h.reject_reason || '-'}
|
||||
</Text>
|
||||
)}
|
||||
{h.admin_reply ? (
|
||||
<div style={{ fontSize: 12, marginTop: 2 }}>回复:{h.admin_reply}</div>
|
||||
) : null}
|
||||
</div>
|
||||
))}
|
||||
</Space>
|
||||
|
||||
@@ -49,7 +49,42 @@ const statusTag = (s: string) => {
|
||||
return <Tag color={m.color}>{m.label}</Tag>;
|
||||
};
|
||||
|
||||
// 审核结果展示(采纳=金币+批注 / 未采纳=原因 / 待审核=-),主表与「该用户全部反馈」抽屉共用
|
||||
// 反馈类型:comparison=比价反馈(比价结果页入口) / profile=普通反馈(「我的」页入口)。
|
||||
// 旧数据与未识别来源按普通反馈展示。
|
||||
const SOURCE_META: Record<string, { label: string; color: string }> = {
|
||||
comparison: { label: '比价反馈', color: 'geekblue' },
|
||||
profile: { label: '普通反馈', color: 'default' },
|
||||
};
|
||||
|
||||
const SOURCE_OPTIONS = [
|
||||
{ value: 'comparison', label: '比价反馈' },
|
||||
{ value: 'profile', label: '普通反馈' },
|
||||
];
|
||||
|
||||
// 反馈类型标签 +(比价反馈时)问题场景副标题
|
||||
const renderSource = (f: Feedback) => {
|
||||
const m = SOURCE_META[f.source] ?? { label: '普通反馈', color: 'default' };
|
||||
return (
|
||||
<Space direction="vertical" size={0}>
|
||||
<Tag color={m.color}>{m.label}</Tag>
|
||||
{f.scene ? (
|
||||
<Text type="secondary" style={{ fontSize: 12 }}>
|
||||
{f.scene}
|
||||
</Text>
|
||||
) : null}
|
||||
</Space>
|
||||
);
|
||||
};
|
||||
|
||||
// 用户可见的运营回复留言(采纳/未采纳都可能有)
|
||||
const replyLine = (f: Feedback) =>
|
||||
f.admin_reply ? (
|
||||
<Text style={{ fontSize: 12 }} ellipsis>
|
||||
回复:{f.admin_reply}
|
||||
</Text>
|
||||
) : null;
|
||||
|
||||
// 审核结果展示(采纳=金币+批注 / 未采纳=原因 / 回复留言 / 待审核=-),主表与「该用户全部反馈」抽屉共用
|
||||
const renderReview = (f: Feedback) => {
|
||||
if (f.status === 'adopted') {
|
||||
return (
|
||||
@@ -60,14 +95,18 @@ const renderReview = (f: Feedback) => {
|
||||
{f.review_note}
|
||||
</Text>
|
||||
) : null}
|
||||
{replyLine(f)}
|
||||
</Space>
|
||||
);
|
||||
}
|
||||
if (f.status === 'rejected') {
|
||||
return (
|
||||
<Text type="secondary" style={{ fontSize: 12 }} ellipsis>
|
||||
未采纳:{f.reject_reason || '-'}
|
||||
</Text>
|
||||
<Space direction="vertical" size={0}>
|
||||
<Text type="secondary" style={{ fontSize: 12 }} ellipsis>
|
||||
未采纳:{f.reject_reason || '-'}
|
||||
</Text>
|
||||
{replyLine(f)}
|
||||
</Space>
|
||||
);
|
||||
}
|
||||
return <Text type="secondary">-</Text>;
|
||||
@@ -95,6 +134,7 @@ const renderDeviceOs = (f: Feedback) => {
|
||||
const RECORD_COLUMNS: ColumnsType<Feedback> = [
|
||||
{ title: '提交时间', dataIndex: 'created_at', width: 150, render: (v: string) => formatWallTime(v) },
|
||||
{ title: '状态', dataIndex: 'status', width: 80, render: statusTag },
|
||||
{ title: '反馈类型', key: 'source', width: 100, render: (_: unknown, f: Feedback) => renderSource(f) },
|
||||
{
|
||||
title: '内容',
|
||||
dataIndex: 'content',
|
||||
@@ -116,6 +156,7 @@ const RECORD_COLUMNS: ColumnsType<Feedback> = [
|
||||
export default function FeedbacksPage() {
|
||||
// 筛选草稿:点「查询」才应用(避免输入即刷新)
|
||||
const [status, setStatus] = useState<string | undefined>();
|
||||
const [source, setSource] = useState<string | undefined>();
|
||||
const [userId, setUserId] = useState('');
|
||||
const [content, setContent] = useState('');
|
||||
const [createdRange, setCreatedRange] = useState<[Dayjs, Dayjs] | null>(null);
|
||||
@@ -158,6 +199,7 @@ export default function FeedbacksPage() {
|
||||
const search = () =>
|
||||
setApplied({
|
||||
status,
|
||||
source,
|
||||
user_id: userId.trim() ? Number(userId.trim()) : undefined,
|
||||
content: content.trim() || undefined,
|
||||
created_from: createdRange?.[0] ? createdRange[0].startOf('day').toISOString() : undefined,
|
||||
@@ -166,6 +208,7 @@ export default function FeedbacksPage() {
|
||||
|
||||
const resetFilters = () => {
|
||||
setStatus(undefined);
|
||||
setSource(undefined);
|
||||
setUserId('');
|
||||
setContent('');
|
||||
setCreatedRange(null);
|
||||
@@ -216,6 +259,12 @@ export default function FeedbacksPage() {
|
||||
width: 120,
|
||||
render: (v: string | null) => v || <Text type="secondary">无昵称</Text>,
|
||||
},
|
||||
{
|
||||
title: '反馈类型',
|
||||
key: 'source',
|
||||
width: 110,
|
||||
render: (_: unknown, f: Feedback) => renderSource(f),
|
||||
},
|
||||
{
|
||||
title: '提交版本号',
|
||||
dataIndex: 'app_version',
|
||||
@@ -325,6 +374,14 @@ export default function FeedbacksPage() {
|
||||
{ value: 'rejected', label: '未采纳' },
|
||||
]}
|
||||
/>
|
||||
<Select
|
||||
placeholder="反馈类型"
|
||||
value={source}
|
||||
onChange={setSource}
|
||||
allowClear
|
||||
style={{ width: 130 }}
|
||||
options={SOURCE_OPTIONS}
|
||||
/>
|
||||
<Input
|
||||
placeholder="用户ID"
|
||||
value={userId}
|
||||
@@ -367,7 +424,7 @@ export default function FeedbacksPage() {
|
||||
onChange: onPageChange,
|
||||
}}
|
||||
onChange={onTableChange}
|
||||
scroll={{ x: 1610 }}
|
||||
scroll={{ x: 1720 }}
|
||||
/>
|
||||
|
||||
<FeedbackHandleDrawer
|
||||
|
||||
@@ -23,6 +23,7 @@ import { canDo } from '@/lib/auth';
|
||||
import { formatUtcTime, formatWallTime, yuan } from '@/lib/format';
|
||||
import { useCursorList } from '@/lib/useCursorList';
|
||||
import { AdjustCashModal, AdjustCoinModal } from '@/components/AdjustBalanceModals';
|
||||
import { DeleteUserModal } from '@/components/DeleteUserModal';
|
||||
import type { CashTxn, CoinTxn, UserOverview, WithdrawOrder } from '@/lib/types';
|
||||
|
||||
export default function UserDetailPage() {
|
||||
@@ -33,6 +34,7 @@ export default function UserDetailPage() {
|
||||
const [err, setErr] = useState<string | null>(null);
|
||||
const [coinOpen, setCoinOpen] = useState(false);
|
||||
const [cashOpen, setCashOpen] = useState(false);
|
||||
const [delOpen, setDelOpen] = useState(false);
|
||||
|
||||
const loadData = useCallback(() => {
|
||||
setErr(null);
|
||||
@@ -52,6 +54,7 @@ export default function UserDetailPage() {
|
||||
|
||||
const canCoins = canDo(['finance']);
|
||||
const canStatus = canDo(['operator']);
|
||||
const canDelete = canDo(['super_admin']); // 注销账号最高危,仅 super_admin(与后端 require_role 对齐)
|
||||
|
||||
const toggleStatus = () => {
|
||||
if (!data) return;
|
||||
@@ -154,6 +157,11 @@ export default function UserDetailPage() {
|
||||
{data.user.status === 'active' ? '封禁' : '解封'}
|
||||
</Button>
|
||||
)}
|
||||
{canDelete && data.user.status !== 'deleted' && (
|
||||
<Button danger onClick={() => setDelOpen(true)}>
|
||||
注销账号
|
||||
</Button>
|
||||
)}
|
||||
</Space>
|
||||
</Space>
|
||||
<Card style={{ marginBottom: 16 }}>
|
||||
@@ -272,6 +280,12 @@ export default function UserDetailPage() {
|
||||
cash.reload();
|
||||
}}
|
||||
/>
|
||||
<DeleteUserModal
|
||||
user={data.user}
|
||||
open={delOpen}
|
||||
onClose={() => setDelOpen(false)}
|
||||
onDone={loadData}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import { canDo } from '@/lib/auth';
|
||||
import { formatUtcTime } from '@/lib/format';
|
||||
import { usePagedList } from '@/lib/usePagedList';
|
||||
import { AdjustCashModal, AdjustCoinModal } from '@/components/AdjustBalanceModals';
|
||||
import { DeleteUserModal } from '@/components/DeleteUserModal';
|
||||
import type { UserListItem } from '@/lib/types';
|
||||
|
||||
const { RangePicker } = DatePicker;
|
||||
@@ -43,9 +44,11 @@ export default function UsersPage() {
|
||||
|
||||
const canCoins = canDo(['finance']);
|
||||
const canStatus = canDo(['operator']);
|
||||
const canDelete = canDo(['super_admin']); // 注销账号最高危,仅 super_admin(与后端 require_role 对齐)
|
||||
|
||||
const [coinUser, setCoinUser] = useState<UserListItem | null>(null);
|
||||
const [cashUser, setCashUser] = useState<UserListItem | null>(null);
|
||||
const [delUser, setDelUser] = useState<UserListItem | null>(null);
|
||||
const [selectedRowKeys, setSelectedRowKeys] = useState<Key[]>([]);
|
||||
|
||||
// 筛选/排序变化后清空已选(避免选中项跨筛选错位)
|
||||
@@ -246,7 +249,7 @@ export default function UsersPage() {
|
||||
title: '操作',
|
||||
key: 'op',
|
||||
fixed: 'right',
|
||||
width: 300,
|
||||
width: 340,
|
||||
render: (_: unknown, u: UserListItem) => (
|
||||
<Space wrap={false} onClick={(e) => e.stopPropagation()}>
|
||||
<a onClick={() => router.push(`/users/${u.id}`)}>详情</a>
|
||||
@@ -269,6 +272,11 @@ export default function UsersPage() {
|
||||
) : (
|
||||
<a onClick={() => setForceOnboarding(u, true)}>开启新手引导</a>
|
||||
))}
|
||||
{canDelete && u.status !== 'deleted' && (
|
||||
<a style={{ color: '#cf1322' }} onClick={() => setDelUser(u)}>
|
||||
注销
|
||||
</a>
|
||||
)}
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
@@ -377,12 +385,18 @@ export default function UsersPage() {
|
||||
showTotal: (t) => `共 ${t} 个用户`,
|
||||
onChange: onPageChange,
|
||||
}}
|
||||
scroll={{ x: 1160 }}
|
||||
scroll={{ x: 1200 }}
|
||||
onChange={onTableChange}
|
||||
/>
|
||||
|
||||
<AdjustCoinModal user={coinUser} open={!!coinUser} onClose={() => setCoinUser(null)} />
|
||||
<AdjustCashModal user={cashUser} open={!!cashUser} onClose={() => setCashUser(null)} />
|
||||
<DeleteUserModal
|
||||
user={delUser}
|
||||
open={!!delUser}
|
||||
onClose={() => setDelUser(null)}
|
||||
onDone={reload}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -104,6 +104,19 @@ const SORT_OPTIONS = [
|
||||
{ value: 'created_at', label: '申请时间' },
|
||||
{ value: 'amount_cents', label: '提现金额' },
|
||||
];
|
||||
// 提现类型:coin_cash=福利页提现(金币兑换现金) / invite_cash=邀请提现(邀请奖励金)
|
||||
const SOURCE_LABEL: Record<string, string> = {
|
||||
coin_cash: '福利页提现',
|
||||
invite_cash: '邀请提现',
|
||||
};
|
||||
const SOURCE_COLOR: Record<string, string> = {
|
||||
coin_cash: 'blue',
|
||||
invite_cash: 'purple',
|
||||
};
|
||||
const SOURCE_OPTIONS = [
|
||||
{ value: 'coin_cash', label: '福利页提现' },
|
||||
{ value: 'invite_cash', label: '邀请提现' },
|
||||
];
|
||||
const DATE_FIELD_OPTIONS = [
|
||||
{ value: 'created_at', label: '申请时间' },
|
||||
{ value: 'updated_at', label: '更新时间' },
|
||||
@@ -184,9 +197,11 @@ export default function WithdrawsPage() {
|
||||
const [sortBy, setSortBy] = useState<'created_at' | 'amount_cents'>('created_at');
|
||||
const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('desc');
|
||||
const [quickFilter, setQuickFilter] = useState<string | undefined>(undefined);
|
||||
const [source, setSource] = useState<string | undefined>(undefined);
|
||||
|
||||
const filters: Record<string, unknown> = {};
|
||||
if (activeStatus !== 'all') filters.status = activeStatus;
|
||||
if (source) filters.source = source;
|
||||
if (keyword.trim()) filters.keyword = keyword.trim();
|
||||
if (dateRange?.[0]) filters.date_from = dateRange[0].startOf('day').toISOString();
|
||||
if (dateRange?.[1]) filters.date_to = dateRange[1].endOf('day').toISOString();
|
||||
@@ -211,6 +226,7 @@ export default function WithdrawsPage() {
|
||||
setSortBy('created_at');
|
||||
setSortOrder('desc');
|
||||
setQuickFilter(undefined);
|
||||
setSource(undefined);
|
||||
};
|
||||
|
||||
const applyQuickFilter = (value?: string) => {
|
||||
@@ -481,6 +497,12 @@ export default function WithdrawsPage() {
|
||||
width: 120,
|
||||
render: (v: number) => <Text strong>{yuan(v)}</Text>,
|
||||
},
|
||||
{
|
||||
title: '提现类型',
|
||||
dataIndex: 'source',
|
||||
width: 110,
|
||||
render: (v: string) => <Tag color={SOURCE_COLOR[v] || 'default'}>{SOURCE_LABEL[v] || v}</Tag>,
|
||||
},
|
||||
{
|
||||
title: '累计提现',
|
||||
dataIndex: 'cumulative_success_cents',
|
||||
@@ -724,6 +746,14 @@ export default function WithdrawsPage() {
|
||||
}}
|
||||
onSearch={(value) => setKeyword(value.trim())}
|
||||
/>
|
||||
<Select
|
||||
value={source}
|
||||
allowClear
|
||||
placeholder="提现类型"
|
||||
style={{ width: 140 }}
|
||||
options={SOURCE_OPTIONS}
|
||||
onChange={(value?: string) => setSource(value)}
|
||||
/>
|
||||
<Select
|
||||
value={quickFilter}
|
||||
allowClear
|
||||
@@ -849,7 +879,7 @@ export default function WithdrawsPage() {
|
||||
showTotal: (t) => `共 ${t} 条`,
|
||||
onChange: onPageChange,
|
||||
}}
|
||||
scroll={{ x: 1350 }}
|
||||
scroll={{ x: 1460 }}
|
||||
onRow={(record) => ({
|
||||
onClick: () => openDetail(record),
|
||||
style: { cursor: 'pointer' },
|
||||
@@ -881,6 +911,11 @@ export default function WithdrawsPage() {
|
||||
<Descriptions.Item label="金额">
|
||||
<Text strong>{yuan(detail.order.amount_cents)}</Text>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="提现类型">
|
||||
<Tag color={SOURCE_COLOR[detail.order.source] || 'default'}>
|
||||
{SOURCE_LABEL[detail.order.source] || detail.order.source}
|
||||
</Tag>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="提现实名">
|
||||
{detail.order.user_name || '-'}
|
||||
</Descriptions.Item>
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
'use client';
|
||||
|
||||
// 注销/删除账号弹窗(受控)。列表页与用户详情页共用,避免两处各写一套(同 AdjustBalanceModals)。
|
||||
// 复用 C 端自助注销同一套后端清理:物理删除个人内容/行为/PII 表 + 清零余额 + 匿名化 user 行,
|
||||
// 保留资金流水/邀请关系/广告幂等+对账表。仅 super_admin 可见入口;后端亦 require_role("super_admin") 兜底。
|
||||
// 后端资金前置闸(有未提现现金/邀请金 / 在审提现单 → 409)带明确 detail,errMsg 原样透出。
|
||||
import { App, Form, Input, Modal } from 'antd';
|
||||
import { api, errMsg } from '@/lib/api';
|
||||
|
||||
export type DeleteTargetUser = { id: number; phone: string };
|
||||
|
||||
interface Props {
|
||||
user: DeleteTargetUser | null;
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
onDone?: () => void; // 删除成功后回调(刷新详情/列表)
|
||||
}
|
||||
|
||||
export function DeleteUserModal({ user, open, onClose, onDone }: Props) {
|
||||
const { message } = App.useApp();
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const submit = async () => {
|
||||
if (!user) return;
|
||||
const v = await form.validateFields(); // 原因必填,缺失即红字阻断
|
||||
try {
|
||||
// axios 的 DELETE 带 body 要放在 config.data 里
|
||||
await api.delete(`/admin/api/users/${user.id}`, { data: { reason: v.reason } });
|
||||
message.success('账号已注销');
|
||||
onClose();
|
||||
onDone?.();
|
||||
} catch (e) {
|
||||
message.error(errMsg(e));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={`注销账号 - ${user?.phone ?? ''}`}
|
||||
open={open}
|
||||
onOk={submit}
|
||||
onCancel={onClose}
|
||||
okText="确认注销"
|
||||
okButtonProps={{ danger: true }}
|
||||
afterOpenChange={(o) => {
|
||||
if (o) form.resetFields();
|
||||
}}
|
||||
destroyOnHidden
|
||||
>
|
||||
<p style={{ color: '#cf1322', marginBottom: 12 }}>
|
||||
将永久删除该账号的个人数据(比价/省钱/签到/领券/埋点/设备/邀请指纹等)并清零余额、匿名化手机号,
|
||||
<strong>不可恢复</strong>。资金流水/邀请关系/广告对账记录按合规保留。
|
||||
</p>
|
||||
<Form form={form} layout="vertical">
|
||||
<Form.Item name="reason" label="注销原因(入审计)" rules={[{ required: true, message: '请填写注销原因' }]}>
|
||||
<Input.TextArea rows={2} />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
+8
-1
@@ -117,6 +117,8 @@ export interface WithdrawOrder {
|
||||
user_id: number;
|
||||
out_bill_no: string;
|
||||
amount_cents: number;
|
||||
// 提现类型:coin_cash(福利页提现,金币兑换现金) / invite_cash(邀请提现,邀请奖励金)
|
||||
source: string;
|
||||
user_name: string | null;
|
||||
status: string; // reviewing / pending / success / failed / rejected
|
||||
wechat_state: string | null;
|
||||
@@ -239,12 +241,17 @@ export interface Feedback {
|
||||
user_id: number;
|
||||
content: string;
|
||||
contact: string;
|
||||
// 反馈来源入口:profile(「我的」页=普通反馈) / comparison(比价结果页=比价反馈)
|
||||
source: string;
|
||||
// 比价反馈的问题场景(找错商品/优惠不对…);普通反馈为 null
|
||||
scene: string | null;
|
||||
images: string[] | null;
|
||||
// pending(审核中) / adopted(已采纳) / rejected(未采纳);历史数据可能为 new
|
||||
status: string;
|
||||
reject_reason: string | null; // 未采纳原因(用户端可见)
|
||||
reward_coins: number | null; // 采纳后发放金币
|
||||
review_note: string | null; // 审核批注(采纳要点 / 内部备注)
|
||||
review_note: string | null; // 审核批注(采纳要点 / 内部备注,用户不可见)
|
||||
admin_reply: string | null; // 运营给用户的回复留言(用户端可见)
|
||||
reviewed_by_admin_id: number | null;
|
||||
reviewed_at: string | null;
|
||||
created_at: string;
|
||||
|
||||
Reference in New Issue
Block a user