feat(posts): 类型帖截止时间、抽奖奖品表与悬赏发帖体验
统一投票/悬赏/抽奖截止与状态展示,抽奖支持多奖品行;发帖页悬赏积分对齐表格输入,并在余额不足时于底部就地提示。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -254,6 +254,7 @@ export interface QuestionState {
|
||||
accepted_comment_id: number;
|
||||
accepted_floor: number;
|
||||
accepted_answer?: AcceptedAnswer | null;
|
||||
solved_at?: string;
|
||||
can_accept: boolean;
|
||||
can_solve: boolean;
|
||||
can_reopen: boolean;
|
||||
@@ -279,6 +280,8 @@ export interface PollState {
|
||||
multi: boolean;
|
||||
closed: boolean;
|
||||
anonymous: boolean;
|
||||
ends_at?: string;
|
||||
closed_at?: string;
|
||||
total_votes: number;
|
||||
voters: number;
|
||||
my_options: number[];
|
||||
@@ -298,7 +301,11 @@ export interface BountyState {
|
||||
ends_at?: string;
|
||||
settled: boolean;
|
||||
can_accept: boolean;
|
||||
can_refund: boolean;
|
||||
}
|
||||
|
||||
export interface LotteryPrize {
|
||||
name: string;
|
||||
count: number;
|
||||
}
|
||||
|
||||
export interface LotteryEntrant {
|
||||
@@ -309,17 +316,21 @@ export interface LotteryEntrant {
|
||||
/** 中奖楼层评论 ID(该用户最早主评,每人仅一条) */
|
||||
comment_id?: number;
|
||||
floor?: number;
|
||||
prize_name?: string;
|
||||
}
|
||||
|
||||
export interface LotteryState {
|
||||
slots: number;
|
||||
prizes: LotteryPrize[];
|
||||
drawn: boolean;
|
||||
closed: boolean;
|
||||
ends_at?: string;
|
||||
entry_count: number;
|
||||
eligible?: boolean;
|
||||
/** 新号入池冷静期小时数(与发帖冷静期同源,0=关闭) */
|
||||
cooldown_hours?: number;
|
||||
can_draw: boolean;
|
||||
can_close: boolean;
|
||||
can_edit_prizes?: boolean;
|
||||
winners: LotteryEntrant[];
|
||||
}
|
||||
|
||||
@@ -938,14 +949,6 @@ export async function apiAcceptBounty(postId: string, commentId: number) {
|
||||
return res.json();
|
||||
}
|
||||
|
||||
export async function apiRefundBounty(postId: string) {
|
||||
const res = await fetchWithRefresh(`/api/posts/${postId}/bounty/refund`, {
|
||||
method: "POST",
|
||||
headers: clientHeaders(),
|
||||
});
|
||||
return res.json();
|
||||
}
|
||||
|
||||
export async function apiAcceptQuestion(postId: string, commentId: number) {
|
||||
const res = await fetchWithRefresh(`/api/posts/${postId}/question/accept`, {
|
||||
method: "POST",
|
||||
@@ -971,14 +974,6 @@ export async function apiReopenQuestion(postId: string) {
|
||||
return res.json();
|
||||
}
|
||||
|
||||
export async function apiCloseLottery(postId: string) {
|
||||
const res = await fetchWithRefresh(`/api/posts/${postId}/lottery/close`, {
|
||||
method: "POST",
|
||||
headers: clientHeaders(),
|
||||
});
|
||||
return res.json();
|
||||
}
|
||||
|
||||
export async function apiDrawLottery(postId: string) {
|
||||
const res = await fetchWithRefresh(`/api/posts/${postId}/lottery/draw`, {
|
||||
method: "POST",
|
||||
|
||||
@@ -5,7 +5,7 @@ export const POST_TYPES = [
|
||||
{ value: "question", label: "问答", hint: "提问求解" },
|
||||
{ value: "poll", label: "投票", hint: "发起投票,读者可参与" },
|
||||
{ value: "bounty", label: "悬赏", hint: "发布时托管积分,采纳后发放" },
|
||||
{ value: "lottery", label: "抽奖", hint: "回帖即参与,截止自动开奖" },
|
||||
{ value: "lottery", label: "抽奖", hint: "奖品类型+数量入池,评论后锁定奖品" },
|
||||
] as const;
|
||||
|
||||
export type PostTypeValue = (typeof POST_TYPES)[number]["value"];
|
||||
@@ -21,6 +21,7 @@ export type ContentAccessValue = (typeof CONTENT_ACCESS)[number]["value"];
|
||||
|
||||
export const BOUNTY_EXPIRE_DAYS = [3, 7, 14, 30] as const;
|
||||
export const LOTTERY_EXPIRE_DAYS = [1, 3, 7, 14] as const;
|
||||
export const POLL_EXPIRE_DAYS = [1, 3, 7, 14] as const;
|
||||
|
||||
export function normalizePostType(t?: string): PostTypeValue {
|
||||
if (!t) return "discussion";
|
||||
@@ -74,12 +75,13 @@ export function typeStatusClass(postType?: string, typeStatus?: string): string
|
||||
break;
|
||||
case "bounty":
|
||||
if (s === "solved") kind = "solved";
|
||||
else if (s === "expired") kind = "expired";
|
||||
// 已过期:与投票「已结束」同色(灰胶囊,标识已到期)
|
||||
else if (s === "expired") kind = "closed";
|
||||
else kind = "open";
|
||||
break;
|
||||
case "lottery":
|
||||
if (s === "drawn") kind = "drawn";
|
||||
else if (s === "closed") kind = "closed";
|
||||
// 已开奖 / 已截止:与投票「已结束」同色(灰胶囊)
|
||||
if (s === "drawn" || s === "closed") kind = "closed";
|
||||
else kind = "open";
|
||||
break;
|
||||
default:
|
||||
|
||||
Reference in New Issue
Block a user