hku-class/miniprogram/pages/vote-detail/index.js
2026-05-13 00:36:39 +08:00

108 lines
3.5 KiB
JavaScript

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 });
}
}
});