= {
+ comparison: { label: '比价反馈', color: 'geekblue' },
+ profile: { label: '普通反馈', color: 'default' },
+};
+
+const sourceTag = (s: string) => {
+ const m = SOURCE_META[s] ?? { label: '普通反馈', color: 'default' };
+ return {m.label};
+};
+
function FeedbackImages({ images }: { images: string[] | null }) {
if (!images || !images.length) return 无截图;
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 }
用户 {feedback.user_id} · {formatWallTime(feedback.created_at)}
+
+ {sourceTag(feedback.source)}
+ {feedback.scene ? (
+
+ 场景:{feedback.scene}
+
+ ) : null}
+
{feedback.content || '-'}
@@ -200,7 +221,11 @@ export default function FeedbackHandleDrawer({ feedback, open, onClose, onDone }
)}
- 审核备注:{feedback.review_note || (无)}
+ 审核备注(内部):{feedback.review_note || (无)}
+
+
+ 给用户的回复(用户可见):
+ {feedback.admin_reply || (无)}
{isPending(feedback.status) && (
@@ -243,11 +268,23 @@ export default function FeedbackHandleDrawer({ feedback, open, onClose, onDone }
+
+
+
>
) : (
<>
@@ -268,6 +305,18 @@ export default function FeedbackHandleDrawer({ feedback, open, onClose, onDone }
>
+
+
+
>
)}
@@ -294,7 +343,10 @@ export default function FeedbackHandleDrawer({ feedback, open, onClose, onDone }
#{h.id} · {formatWallTime(h.created_at)}
- {statusTag(h.status)}
+
+ {sourceTag(h.source)}
+ {statusTag(h.status)}
+
)}
+ {h.admin_reply ? (
+ 回复:{h.admin_reply}
+ ) : null}
))}
diff --git a/src/app/(main)/feedbacks/page.tsx b/src/app/(main)/feedbacks/page.tsx
index 9447e92..f773e96 100644
--- a/src/app/(main)/feedbacks/page.tsx
+++ b/src/app/(main)/feedbacks/page.tsx
@@ -49,7 +49,42 @@ const statusTag = (s: string) => {
return {m.label};
};
-// 审核结果展示(采纳=金币+批注 / 未采纳=原因 / 待审核=-),主表与「该用户全部反馈」抽屉共用
+// 反馈类型:comparison=比价反馈(比价结果页入口) / profile=普通反馈(「我的」页入口)。
+// 旧数据与未识别来源按普通反馈展示。
+const SOURCE_META: Record = {
+ 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 (
+
+ {m.label}
+ {f.scene ? (
+
+ {f.scene}
+
+ ) : null}
+
+ );
+};
+
+// 用户可见的运营回复留言(采纳/未采纳都可能有)
+const replyLine = (f: Feedback) =>
+ f.admin_reply ? (
+
+ 回复:{f.admin_reply}
+
+ ) : null;
+
+// 审核结果展示(采纳=金币+批注 / 未采纳=原因 / 回复留言 / 待审核=-),主表与「该用户全部反馈」抽屉共用
const renderReview = (f: Feedback) => {
if (f.status === 'adopted') {
return (
@@ -60,14 +95,18 @@ const renderReview = (f: Feedback) => {
{f.review_note}
) : null}
+ {replyLine(f)}
);
}
if (f.status === 'rejected') {
return (
-
- 未采纳:{f.reject_reason || '-'}
-
+
+
+ 未采纳:{f.reject_reason || '-'}
+
+ {replyLine(f)}
+
);
}
return -;
@@ -95,6 +134,7 @@ const renderDeviceOs = (f: Feedback) => {
const RECORD_COLUMNS: ColumnsType = [
{ 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 = [
export default function FeedbacksPage() {
// 筛选草稿:点「查询」才应用(避免输入即刷新)
const [status, setStatus] = useState();
+ const [source, setSource] = useState();
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 || 无昵称,
},
+ {
+ 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: '未采纳' },
]}
/>
+
= {
+ coin_cash: '福利页提现',
+ invite_cash: '邀请提现',
+};
+const SOURCE_COLOR: Record = {
+ 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(undefined);
+ const [source, setSource] = useState(undefined);
const filters: Record = {};
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) => {yuan(v)},
},
+ {
+ title: '提现类型',
+ dataIndex: 'source',
+ width: 110,
+ render: (v: string) => {SOURCE_LABEL[v] || v},
+ },
{
title: '累计提现',
dataIndex: 'cumulative_success_cents',
@@ -724,6 +746,14 @@ export default function WithdrawsPage() {
}}
onSearch={(value) => setKeyword(value.trim())}
/>
+