const { get, post } = require("../../utils/api"); const { showError } = require("../../utils/page-helpers"); function formatDateTime(value) { if (!value) return ""; return String(value).replace("T", " ").slice(0, 16); } function normalizeVote(item, selectedIds) { const optionIds = selectedIds || item.my_option_ids || []; const totalVotes = (item.options || []).reduce((sum, option) => sum + Number(option.vote_count || 0), 0); return { ...item, status_text: item.status === "open" ? "进行中" : "已关闭", vote_type_text: item.vote_type === "multiple" ? `多选,最多 ${item.max_choices || 1} 项` : "单选", deadline_text: item.deadline ? formatDateTime(item.deadline) : "", can_submit: item.status === "open" && !item.has_voted, voted_action_text: item.has_voted ? "已参与" : "请选择", submit_text: item.has_voted ? "你已参与" : "投票已关闭", selectedIds: optionIds, options: (item.options || []).map((option) => { const voteCount = Number(option.vote_count || 0); const percent = totalVotes > 0 ? Math.round((voteCount / totalVotes) * 100) : 0; return { ...option, checked: optionIds.includes(option.id), selected_class: optionIds.includes(option.id) ? "selected" : "", check_text: optionIds.includes(option.id) ? "✓" : "", percent, percent_style: `width: ${percent}%`, voter_names_text: Array.isArray(option.voter_names) ? option.voter_names.join("、") : "" }; }) }; } Page({ data: { id: null, item: null, loading: false, submitting: false }, onLoad(options) { wx.setNavigationBarTitle({ title: "投票详情" }); this.setData({ id: options.id || null }); this.load(options.id); }, async onPullDownRefresh() { await this.load(this.data.id); wx.stopPullDownRefresh(); }, async load(id) { if (!id) return; this.setData({ loading: true }); try { const item = await get(`/api/votes/${id}`); this.setData({ item: normalizeVote(item) }); } catch (error) { showError(error, "加载投票失败"); } finally { this.setData({ loading: false }); } }, toggleOption(event) { const optionId = Number(event.currentTarget.dataset.id); const item = this.data.item; if (!item || item.status !== "open" || item.has_voted) return; let selectedIds = item.selectedIds || []; if (item.vote_type === "single") { selectedIds = [optionId]; } else if (selectedIds.includes(optionId)) { selectedIds = selectedIds.filter((id) => id !== optionId); } else { if (selectedIds.length >= Number(item.max_choices || 1)) { wx.showToast({ title: `最多选择 ${item.max_choices} 项`, icon: "none" }); return; } selectedIds = [...selectedIds, optionId]; } this.setData({ item: normalizeVote(item, selectedIds) }); }, async submit() { const item = this.data.item; if (!item || item.has_voted || item.status !== "open") return; const selectedIds = item.selectedIds || []; if (!selectedIds.length) { wx.showToast({ title: "请选择投票选项", icon: "none" }); return; } this.setData({ submitting: true }); try { await post(`/api/votes/${item.id}/submit`, { option_ids: selectedIds }); wx.showToast({ title: "已投票", icon: "success" }); await this.load(item.id); } catch (error) { showError(error, "投票失败"); } finally { this.setData({ submitting: false }); } } });