mirror of
https://github.com/ialley-workshop-open/uni-halo.git
synced 2026-09-12 16:40:40 +08:00
refactor: 优化投票相关组件与工具代码格式与样式
1. 统一uh-article-vote与uh-article-vote-item组件代码缩进与格式 2. 更新投票提示文案与文章卡片样式适配 3. 规范vote.ts工具类代码格式与分号使用 4. 调整文章卡片布局与置顶样式
This commit is contained in:
@@ -13,9 +13,13 @@
|
|||||||
contentWrapper: string
|
contentWrapper: string
|
||||||
footer: string
|
footer: string
|
||||||
authorGroup: string
|
authorGroup: string
|
||||||
|
avatar: string
|
||||||
|
nickname: string
|
||||||
|
infoCol: string
|
||||||
time: string
|
time: string
|
||||||
tagCategory: string
|
tagCategory: string
|
||||||
visits: string
|
visits: string
|
||||||
|
pinned: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 旧版全局 cardType → 新版 layout;未知值(如 only_text)由 effectiveLayout 兜底 image_top */
|
/** 旧版全局 cardType → 新版 layout;未知值(如 only_text)由 effectiveLayout 兜底 image_top */
|
||||||
@@ -34,19 +38,27 @@
|
|||||||
contentWrapper: 'w-full',
|
contentWrapper: 'w-full',
|
||||||
footer: 'flex items-center',
|
footer: 'flex items-center',
|
||||||
authorGroup: 'flex-1 items-center justify-start gap-x-1',
|
authorGroup: 'flex-1 items-center justify-start gap-x-1',
|
||||||
|
avatar: '',
|
||||||
|
nickname: '',
|
||||||
|
infoCol: 'items-center gap-x-1',
|
||||||
time: 'flex-1 text-center',
|
time: 'flex-1 text-center',
|
||||||
tagCategory: '',
|
tagCategory: '',
|
||||||
visits: 'flex-1 justify-end',
|
visits: 'flex-1 justify-end',
|
||||||
|
pinned: 'right-4 top-4',
|
||||||
},
|
},
|
||||||
image_bottom: {
|
image_bottom: {
|
||||||
container: 'flex flex-col gap-y-2',
|
container: 'flex flex-col gap-y-2',
|
||||||
cover: 'order-2',
|
cover: 'order-2',
|
||||||
contentWrapper: 'w-full',
|
contentWrapper: 'w-full',
|
||||||
footer: 'flex items-center',
|
footer: 'order-first flex items-center mb-1',
|
||||||
authorGroup: 'flex-1 items-center justify-center gap-x-1',
|
authorGroup: 'items-center gap-x-2',
|
||||||
time: 'flex-1 text-center',
|
avatar: '!h-9 !w-9 !rounded-xl',
|
||||||
|
nickname: '!text-sm',
|
||||||
|
infoCol: 'flex-col items-start leading-tight',
|
||||||
|
time: '',
|
||||||
tagCategory: '',
|
tagCategory: '',
|
||||||
visits: 'flex-1 justify-center',
|
visits: '',
|
||||||
|
pinned: 'right-3 top-3',
|
||||||
},
|
},
|
||||||
image_left: {
|
image_left: {
|
||||||
container: 'flex gap-x-3 !p-2',
|
container: 'flex gap-x-3 !p-2',
|
||||||
@@ -54,9 +66,13 @@
|
|||||||
contentWrapper: 'w-0 flex-1 justify-between',
|
contentWrapper: 'w-0 flex-1 justify-between',
|
||||||
footer: 'flex items-center justify-between',
|
footer: 'flex items-center justify-between',
|
||||||
authorGroup: 'items-center gap-x-1',
|
authorGroup: 'items-center gap-x-1',
|
||||||
|
avatar: '',
|
||||||
|
nickname: '',
|
||||||
|
infoCol: 'items-center gap-x-1',
|
||||||
time: '!hidden',
|
time: '!hidden',
|
||||||
tagCategory: '!hidden',
|
tagCategory: '!hidden',
|
||||||
visits: '',
|
visits: '',
|
||||||
|
pinned: 'left-3 top-3',
|
||||||
},
|
},
|
||||||
image_right: {
|
image_right: {
|
||||||
container: 'flex gap-x-3 !p-2',
|
container: 'flex gap-x-3 !p-2',
|
||||||
@@ -64,9 +80,13 @@
|
|||||||
contentWrapper: 'order-1 w-0 flex-1 justify-between',
|
contentWrapper: 'order-1 w-0 flex-1 justify-between',
|
||||||
footer: 'flex items-center justify-between',
|
footer: 'flex items-center justify-between',
|
||||||
authorGroup: 'items-center gap-x-1',
|
authorGroup: 'items-center gap-x-1',
|
||||||
|
avatar: '',
|
||||||
|
nickname: '',
|
||||||
|
infoCol: 'items-center gap-x-1',
|
||||||
time: '!hidden',
|
time: '!hidden',
|
||||||
tagCategory: '!hidden',
|
tagCategory: '!hidden',
|
||||||
visits: '',
|
visits: '',
|
||||||
|
pinned: 'right-3 top-3',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,6 +124,9 @@
|
|||||||
|
|
||||||
const cardLayout = computed(() => CARD_LAYOUTS[effectiveLayout.value])
|
const cardLayout = computed(() => CARD_LAYOUTS[effectiveLayout.value])
|
||||||
|
|
||||||
|
/** 社交卡片形态(封面在下):左上用户信息(头像 + 昵称/日期垂直)、右上浏览数 */
|
||||||
|
const isSocialCard = computed(() => effectiveLayout.value === 'image_bottom')
|
||||||
|
|
||||||
const publishTimeText = computed(() => {
|
const publishTimeText = computed(() => {
|
||||||
const time = props.article.spec.publishTime
|
const time = props.article.spec.publishTime
|
||||||
return time ? formatTime({ d: time, f: 'yyyy/MM/dd' }) : ''
|
return time ? formatTime({ d: time, f: 'yyyy/MM/dd' }) : ''
|
||||||
@@ -137,7 +160,8 @@
|
|||||||
<view class="uh-global-card-glass uh-shadow-xs relative overflow-hidden rounded-xl p-3"
|
<view class="uh-global-card-glass uh-shadow-xs relative overflow-hidden rounded-xl p-3"
|
||||||
:class="cardLayout.container" @click.stop="handleToArticleDetail()">
|
:class="cardLayout.container" @click.stop="handleToArticleDetail()">
|
||||||
<text v-if="article.spec.pinned"
|
<text v-if="article.spec.pinned"
|
||||||
class="text-gray-60 absolute left-2 top-2 z-1 rounded-lg bg-secondary px-2 py-1 text-xs">
|
class="box-border uh-global-card-glass border text-gray-900 absolute z-1 rounded-md bg-secondary px-1.5 py-0.5 text-xs"
|
||||||
|
:class="cardLayout.pinned">
|
||||||
置顶
|
置顶
|
||||||
</text>
|
</text>
|
||||||
<image :class="[isGrid ? 'w-full h-24 rounded-lg' : 'w-full h-36 rounded-lg', cardLayout.cover]"
|
<image :class="[isGrid ? 'w-full h-24 rounded-lg' : 'w-full h-36 rounded-lg', cardLayout.cover]"
|
||||||
@@ -152,13 +176,13 @@
|
|||||||
<view v-if="!isGrid" class="my-1 box-border flex flex-wrap gap-2" :class="cardLayout.tagCategory">
|
<view v-if="!isGrid" class="my-1 box-border flex flex-wrap gap-2" :class="cardLayout.tagCategory">
|
||||||
<template v-if="article.categories && article.categories.length !== 0">
|
<template v-if="article.categories && article.categories.length !== 0">
|
||||||
<text v-for="cate in article.categories" :key="cate.metadata.name"
|
<text v-for="cate in article.categories" :key="cate.metadata.name"
|
||||||
class="rounded-xl bg-secondary px-2 py-1 text-xs" @click.stop="handleToCategory(cate)">
|
class="box-border uh-global-card-glass border uh-shadow-xs rounded-xl bg-secondary px-2 py-0.5 text-xs" @click.stop="handleToCategory(cate)">
|
||||||
{{ cate.spec.displayName }}
|
{{ cate.spec.displayName }}
|
||||||
</text>
|
</text>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="article.tags && article.tags.length !== 0">
|
<template v-if="article.tags && article.tags.length !== 0">
|
||||||
<text v-for="tag in article.tags" :key="tag.metadata.name"
|
<text v-for="tag in article.tags" :key="tag.metadata.name"
|
||||||
class="rounded-xl bg-secondary px-2 py-1 text-xs">
|
class="box-border uh-global-card-glass border uh-shadow-xs rounded-xl bg-secondary px-2 py-0.5 text-xs">
|
||||||
# {{ tag.spec.displayName }}
|
# {{ tag.spec.displayName }}
|
||||||
</text>
|
</text>
|
||||||
</template>
|
</template>
|
||||||
@@ -166,11 +190,25 @@
|
|||||||
<view class="flex items-center text-xs text-gray-500" :class="cardLayout.footer">
|
<view class="flex items-center text-xs text-gray-500" :class="cardLayout.footer">
|
||||||
<view v-if="!isGrid" class="flex items-center" :class="cardLayout.authorGroup">
|
<view v-if="!isGrid" class="flex items-center" :class="cardLayout.authorGroup">
|
||||||
<image :src="article.owner.avatar" class="uh-global-card-glass h-5 w-5 rounded-full"
|
<image :src="article.owner.avatar" class="uh-global-card-glass h-5 w-5 rounded-full"
|
||||||
mode="aspectFill" />
|
:class="cardLayout.avatar" mode="aspectFill" />
|
||||||
<text class="truncate">{{ article.owner.displayName }}</text>
|
<template v-if="isSocialCard">
|
||||||
|
<view class="flex" :class="cardLayout.infoCol">
|
||||||
|
<text class="truncate" :class="cardLayout.nickname">{{ article.owner.displayName }}</text>
|
||||||
|
<view class="flex items-center gap-x-2">
|
||||||
|
<text class="text-gray-400" :class="cardLayout.time">{{ publishTimeText }}</text>
|
||||||
|
<view class="visits flex items-center gap-x-1 text-gray-400">
|
||||||
|
浏览
|
||||||
|
<text class="number">{{ visitCount }}</text>
|
||||||
|
次
|
||||||
</view>
|
</view>
|
||||||
<text v-if="!isGrid" class="text-gray-400" :class="cardLayout.time">{{ publishTimeText }}</text>
|
</view>
|
||||||
<view class="visits flex items-center gap-x-1" :class="cardLayout.visits">
|
</view>
|
||||||
|
</template>
|
||||||
|
<text v-else class="truncate">{{ article.owner.displayName }}</text>
|
||||||
|
</view>
|
||||||
|
<text v-if="!isGrid && !isSocialCard" class="text-gray-400"
|
||||||
|
:class="cardLayout.time">{{ publishTimeText }}</text>
|
||||||
|
<view v-if="!isSocialCard" class="visits flex items-center gap-x-1" :class="cardLayout.visits">
|
||||||
浏览
|
浏览
|
||||||
<text class="number">{{ visitCount }}</text>
|
<text class="number">{{ visitCount }}</text>
|
||||||
次
|
次
|
||||||
|
|||||||
@@ -1,8 +1,4 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
/**
|
|
||||||
* 文章详情投票单卡(源自旧项目 components/article-vote,新建复刻)
|
|
||||||
* 支持 single/multiple/pk 三种类型,已投票/已结束展示百分比,可直接提交
|
|
||||||
*/
|
|
||||||
import { computed, ref, watch } from 'vue'
|
import { computed, ref, watch } from 'vue'
|
||||||
import { getVoteDetail, submitVote } from '@/api/uni-halo'
|
import { getVoteDetail, submitVote } from '@/api/uni-halo'
|
||||||
import { calcVoteState, VOTE_TYPES, voteCacheUtil } from '@/utils/vote'
|
import { calcVoteState, VOTE_TYPES, voteCacheUtil } from '@/utils/vote'
|
||||||
@@ -19,31 +15,24 @@ const loadingText = ref('加载中,请稍等...')
|
|||||||
const isSubmit = ref(false)
|
const isSubmit = ref(false)
|
||||||
const voteData = ref<IVote | null>(null)
|
const voteData = ref<IVote | null>(null)
|
||||||
const submitForm = ref<{ voteData : string[] }>({ voteData: [] })
|
const submitForm = ref<{ voteData : string[] }>({ voteData: [] })
|
||||||
/** 选项 id → 票数(来自 VoteDetail.voteDataList 或 Vote.stats.voteDataList) */
|
|
||||||
const voteCountMap = ref<Record<string, number>>({})
|
const voteCountMap = ref<Record<string, number>>({})
|
||||||
|
|
||||||
/** 是否已投票(本地缓存) */
|
|
||||||
const isVoted = computed(() => voteCacheUtil.has(props.voteId))
|
const isVoted = computed(() => voteCacheUtil.has(props.voteId))
|
||||||
|
|
||||||
/** 投票展示状态(与旧项目一致:未开始/进行中/已结束) */
|
|
||||||
const voteState = computed(() => {
|
const voteState = computed(() => {
|
||||||
if (!voteData.value?.spec)
|
if (!voteData.value?.spec) { return null }
|
||||||
return null
|
|
||||||
return calcVoteState(voteData.value)
|
return calcVoteState(voteData.value)
|
||||||
})
|
})
|
||||||
|
|
||||||
/** 类型中文名 */
|
|
||||||
const voteTypeLabel = computed(() => {
|
const voteTypeLabel = computed(() => {
|
||||||
const type = voteData.value?.spec?.type
|
const type = voteData.value?.spec?.type
|
||||||
return type ? VOTE_TYPES[type] || type : ''
|
return type ? VOTE_TYPES[type] || type : ''
|
||||||
})
|
})
|
||||||
|
|
||||||
/** 选项票数占比(取自 voteCountMap) */
|
|
||||||
function handleCalcPercent(option : IVoteOption) : number {
|
function handleCalcPercent(option : IVoteOption) : number {
|
||||||
const total = voteData.value?.stats?.voteCount || 0
|
const total = voteData.value?.stats?.voteCount || 0
|
||||||
const count = voteCountMap.value[option.id || ''] || 0
|
const count = voteCountMap.value[option.id || ''] || 0
|
||||||
if (total === 0)
|
if (total === 0) { return 0 }
|
||||||
return 0
|
|
||||||
return Number(((count / total) * 100).toFixed(2))
|
return Number(((count / total) * 100).toFixed(2))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,14 +67,12 @@ async function handleGetData() {
|
|||||||
|
|
||||||
function handleSelectSingleOption(option : IVoteOption) {
|
function handleSelectSingleOption(option : IVoteOption) {
|
||||||
const spec = voteData.value?.spec
|
const spec = voteData.value?.spec
|
||||||
if (!spec)
|
if (!spec) { return }
|
||||||
return
|
|
||||||
if (voteState.value?.state === '未开始') {
|
if (voteState.value?.state === '未开始') {
|
||||||
showToast('投票未开始')
|
showToast('投票未开始')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (spec.hasEnded || isVoted.value)
|
if (spec.hasEnded || isVoted.value) { return }
|
||||||
return
|
|
||||||
spec.options?.forEach((item) => {
|
spec.options?.forEach((item) => {
|
||||||
item.checked = option.id === item.id
|
item.checked = option.id === item.id
|
||||||
})
|
})
|
||||||
@@ -94,14 +81,12 @@ function handleSelectSingleOption(option: IVoteOption) {
|
|||||||
|
|
||||||
function handleSelectCheckboxOption(option : IVoteOption) {
|
function handleSelectCheckboxOption(option : IVoteOption) {
|
||||||
const spec = voteData.value?.spec
|
const spec = voteData.value?.spec
|
||||||
if (!spec)
|
if (!spec) { return }
|
||||||
return
|
|
||||||
if (voteState.value?.state === '未开始') {
|
if (voteState.value?.state === '未开始') {
|
||||||
showToast('投票未开始')
|
showToast('投票未开始')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (spec.hasEnded || isVoted.value)
|
if (spec.hasEnded || isVoted.value) { return }
|
||||||
return
|
|
||||||
|
|
||||||
const checkedList = (spec.options || []).filter(x => x.checked && x.id !== option.id)
|
const checkedList = (spec.options || []).filter(x => x.checked && x.id !== option.id)
|
||||||
// maxVotes 缺失(0/undefined)时不限制多选数量,避免 0 >= 0 恒真导致无法选择
|
// maxVotes 缺失(0/undefined)时不限制多选数量,避免 0 >= 0 恒真导致无法选择
|
||||||
@@ -125,8 +110,7 @@ function handleSubmitTip(text: string) {
|
|||||||
|
|
||||||
async function handleSubmit() {
|
async function handleSubmit() {
|
||||||
const spec = voteData.value?.spec
|
const spec = voteData.value?.spec
|
||||||
if (!spec)
|
if (!spec) { return }
|
||||||
return
|
|
||||||
if (submitForm.value.voteData.length === 0) {
|
if (submitForm.value.voteData.length === 0) {
|
||||||
showToast('请先选择选项')
|
showToast('请先选择选项')
|
||||||
return
|
return
|
||||||
@@ -196,57 +180,51 @@ defineExpose({ refresh: handleGetData })
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<view class="uh-article-vote-item uh-global-card-glass box-border w-full rounded-2xl p-4">
|
<view class="uh-global-card-glass shadow-none border border-primary box-border w-full rounded-lg p-3">
|
||||||
<!-- 加载失败:点击重试 -->
|
<view v-if="loading === 'error'" class="vote-error py-6 text-center text-xs text-gray-400"
|
||||||
<view v-if="loading === 'error'" class="vote-error py-6 text-center text-[24rpx] text-gray-400" @click="handleGetData">
|
@click="handleGetData">
|
||||||
{{ loadingText }}
|
{{ loadingText }}
|
||||||
</view>
|
</view>
|
||||||
<!-- 加载中 -->
|
|
||||||
<view v-else-if="loading === 'loading'" class="loading py-6">
|
<view v-else-if="loading === 'loading'" class="loading py-6">
|
||||||
<wd-skeleton :row="3" :animated="true" />
|
<wd-skeleton :row="3" :animated="true" />
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<template v-else-if="voteData">
|
<template v-else-if="voteData">
|
||||||
<!-- 头部:序号/类型/状态 + 查看详情 -->
|
|
||||||
<view class="vote-card-head">
|
<view class="vote-card-head">
|
||||||
<view class="flex items-center justify-between">
|
<view class="flex items-center justify-between">
|
||||||
<view class="flex flex-wrap items-center gap-1">
|
<view class="flex flex-wrap items-center gap-1">
|
||||||
<text v-if="props.index !== undefined" class="rounded bg-orange-500 px-2 py-0.5 text-[22rpx] text-white">
|
<text v-if="props.index !== undefined"
|
||||||
|
class="rounded-md bg-orange-500 px-2 py-0.5 text-xs text-white">
|
||||||
{{ props.index + 1 }}
|
{{ props.index + 1 }}
|
||||||
</text>
|
</text>
|
||||||
<text v-if="voteTypeLabel" class="rounded bg-primary/15 px-2 py-0.5 text-[22rpx] text-primary">
|
<text v-if="voteTypeLabel" class="rounded-md bg-primary px-2 py-0.5 text-xs text-gray-900">
|
||||||
{{ voteTypeLabel }}
|
{{ voteTypeLabel }}
|
||||||
</text>
|
</text>
|
||||||
<text
|
<text v-if="voteState" class="rounded-md px-2 py-0.5 text-xs"
|
||||||
v-if="voteState"
|
:style="{ color: voteState.color, backgroundColor: `${voteState.color}1a` }">
|
||||||
class="rounded px-2 py-0.5 text-[22rpx]"
|
|
||||||
:style="{ color: voteState.color, backgroundColor: `${voteState.color}1a` }"
|
|
||||||
>
|
|
||||||
{{ voteState.state }}
|
{{ voteState.state }}
|
||||||
</text>
|
</text>
|
||||||
</view>
|
</view>
|
||||||
<text class="shrink-0 text-[22rpx] text-gray-400" @click="handleToVoteDetail">查看投票详情 ></text>
|
<view class="shrink-0 flex items-center justify-center text-xs text-gray-400"
|
||||||
|
@click="handleToVoteDetail"> 投票详情 <wd-icon name="right" class="mt-0.5" /> </view>
|
||||||
</view>
|
</view>
|
||||||
<view class="title mt-2 text-[30rpx] text-gray-900 font-bold">
|
<view class="title mt-2 text-sm text-gray-900 font-bold">
|
||||||
{{ voteData.spec?.title }}
|
{{ voteData.spec?.title }}
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="vote-card-body mt-3">
|
<view class="vote-card-body mt-2">
|
||||||
<view v-if="voteData.spec?.remark" class="remark mb-3 text-[24rpx] text-gray-400">
|
<view v-if="voteData.spec?.remark" class="remark mb-3 text-xs text-gray-400">
|
||||||
{{ voteData.spec.remark }}
|
{{ voteData.spec.remark }}
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 单选 -->
|
<!-- 单选 -->
|
||||||
<view v-if="voteData.spec?.type === 'single'" class="flex flex-col gap-2">
|
<view v-if="voteData.spec?.type === 'single'" class="flex flex-col gap-2">
|
||||||
<template v-if="showResult">
|
<template v-if="showResult">
|
||||||
<view
|
<view v-for="(option, optionIndex) in voteData.spec?.options || []" :key="optionIndex"
|
||||||
v-for="(option, optionIndex) in voteData.spec?.options || []"
|
|
||||||
:key="optionIndex"
|
|
||||||
class="is-voted-item relative box-border min-h-[72rpx] overflow-hidden rounded-xl text-[24rpx]"
|
class="is-voted-item relative box-border min-h-[72rpx] overflow-hidden rounded-xl text-[24rpx]"
|
||||||
:class="option.checked ? 'bg-primary/40 text-[#4d7c0f] font-bold' : 'bg-[#e5e5e5]/75'"
|
:class="option.checked ? 'bg-primary/40 text-[#4d7c0f] font-bold' : 'bg-[#e5e5e5]/75'"
|
||||||
:style="{ '--percent': `${handleCalcPercent(option)}%` }"
|
:style="{ '--percent': `${handleCalcPercent(option)}%` }">
|
||||||
>
|
|
||||||
<view class="is-voted-item-content relative z-2 box-border min-h-[72rpx] px-6 py-3">
|
<view class="is-voted-item-content relative z-2 box-border min-h-[72rpx] px-6 py-3">
|
||||||
<view class="flex items-center justify-between">
|
<view class="flex items-center justify-between">
|
||||||
<view class="flex-1 text-left">
|
<view class="flex-1 text-left">
|
||||||
@@ -260,13 +238,10 @@ defineExpose({ refresh: handleGetData })
|
|||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<view
|
<view v-for="(option, optionIndex) in voteData.spec?.options || []" :key="optionIndex"
|
||||||
v-for="(option, optionIndex) in voteData.spec?.options || []"
|
|
||||||
:key="optionIndex"
|
|
||||||
class="vote-select-option box-border rounded-xl bg-[#f6f3ee] px-5 py-4 text-[24rpx]"
|
class="vote-select-option box-border rounded-xl bg-[#f6f3ee] px-5 py-4 text-[24rpx]"
|
||||||
:class="option.checked ? 'border-2 border-primary bg-primary/15 text-primary font-bold' : ''"
|
:class="option.checked ? 'border-2 border-primary bg-primary/15 text-primary font-bold' : ''"
|
||||||
@click="handleSelectSingleOption(option)"
|
@click="handleSelectSingleOption(option)">
|
||||||
>
|
|
||||||
{{ option.title }}
|
{{ option.title }}
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
@@ -275,13 +250,10 @@ defineExpose({ refresh: handleGetData })
|
|||||||
<!-- 多选 -->
|
<!-- 多选 -->
|
||||||
<view v-else-if="voteData.spec?.type === 'multiple'" class="flex flex-col gap-2">
|
<view v-else-if="voteData.spec?.type === 'multiple'" class="flex flex-col gap-2">
|
||||||
<template v-if="showResult">
|
<template v-if="showResult">
|
||||||
<view
|
<view v-for="(option, optionIndex) in voteData.spec?.options || []" :key="optionIndex"
|
||||||
v-for="(option, optionIndex) in voteData.spec?.options || []"
|
|
||||||
:key="optionIndex"
|
|
||||||
class="is-voted-item relative box-border min-h-[72rpx] overflow-hidden rounded-xl text-[24rpx]"
|
class="is-voted-item relative box-border min-h-[72rpx] overflow-hidden rounded-xl text-[24rpx]"
|
||||||
:class="option.checked ? 'bg-primary/40 text-[#4d7c0f] font-bold' : 'bg-[#e5e5e5]/75'"
|
:class="option.checked ? 'bg-primary/40 text-[#4d7c0f] font-bold' : 'bg-[#e5e5e5]/75'"
|
||||||
:style="{ '--percent': `${handleCalcPercent(option)}%` }"
|
:style="{ '--percent': `${handleCalcPercent(option)}%` }">
|
||||||
>
|
|
||||||
<view class="is-voted-item-content relative z-2 box-border min-h-[72rpx] px-6 py-3">
|
<view class="is-voted-item-content relative z-2 box-border min-h-[72rpx] px-6 py-3">
|
||||||
<view class="flex items-center justify-between">
|
<view class="flex items-center justify-between">
|
||||||
<view class="flex-1 text-left">
|
<view class="flex-1 text-left">
|
||||||
@@ -295,13 +267,10 @@ defineExpose({ refresh: handleGetData })
|
|||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<view
|
<view v-for="(option, optionIndex) in voteData.spec?.options || []" :key="optionIndex"
|
||||||
v-for="(option, optionIndex) in voteData.spec?.options || []"
|
|
||||||
:key="optionIndex"
|
|
||||||
class="vote-select-option box-border rounded-xl bg-[#f6f3ee] px-5 py-4 text-[24rpx]"
|
class="vote-select-option box-border rounded-xl bg-[#f6f3ee] px-5 py-4 text-[24rpx]"
|
||||||
:class="option.checked ? 'border-2 border-primary bg-primary/15 text-primary font-bold' : ''"
|
:class="option.checked ? 'border-2 border-primary bg-primary/15 text-primary font-bold' : ''"
|
||||||
@click="handleSelectCheckboxOption(option)"
|
@click="handleSelectCheckboxOption(option)">
|
||||||
>
|
|
||||||
{{ option.title }}
|
{{ option.title }}
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
@@ -311,28 +280,22 @@ defineExpose({ refresh: handleGetData })
|
|||||||
<view v-else-if="voteData.spec?.type === 'pk'" class="flex flex-col gap-2">
|
<view v-else-if="voteData.spec?.type === 'pk'" class="flex flex-col gap-2">
|
||||||
<!-- PK 对抗条 -->
|
<!-- PK 对抗条 -->
|
||||||
<view class="pk-container box-border w-full flex">
|
<view class="pk-container box-border w-full flex">
|
||||||
<view
|
<view v-for="(option, optionIndex) in voteData.spec?.options || []" :key="optionIndex"
|
||||||
v-for="(option, optionIndex) in voteData.spec?.options || []"
|
class="radio-item" :class="optionIndex === 0 ? 'radio-left' : 'radio-right'"
|
||||||
:key="optionIndex"
|
:style="{ width: `${handleCalcPercent(option)}%` }">
|
||||||
class="radio-item"
|
<view class="option-item box-border w-full rounded-lg px-3 py-2"
|
||||||
:class="optionIndex === 0 ? 'radio-left' : 'radio-right'"
|
:class="optionIndex === 0 ? 'option-item-left' : 'option-item-right'">
|
||||||
:style="{ width: `${handleCalcPercent(option)}%` }"
|
|
||||||
>
|
|
||||||
<view class="option-item box-border w-full rounded-xl p-4" :class="optionIndex === 0 ? 'option-item-left' : 'option-item-right'">
|
|
||||||
{{ handleCalcPercent(option) }}%
|
{{ handleCalcPercent(option) }}%
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<!-- PK 选项列表 -->
|
<!-- PK 选项列表 -->
|
||||||
<template v-if="showResult">
|
<template v-if="showResult">
|
||||||
<view
|
<view v-for="(option, optionIndex) in voteData.spec?.options || []" :key="optionIndex"
|
||||||
v-for="(option, optionIndex) in voteData.spec?.options || []"
|
class="is-voted-item relative box-border overflow-hidden rounded-lg text-xs"
|
||||||
:key="optionIndex"
|
|
||||||
class="is-voted-item relative box-border min-h-[72rpx] overflow-hidden rounded-xl text-[24rpx]"
|
|
||||||
:class="option.checked ? 'bg-primary/40 text-[#4d7c0f] font-bold' : 'bg-[#e5e5e5]/75'"
|
:class="option.checked ? 'bg-primary/40 text-[#4d7c0f] font-bold' : 'bg-[#e5e5e5]/75'"
|
||||||
:style="{ '--percent': `${handleCalcPercent(option)}%` }"
|
:style="{ '--percent': `${handleCalcPercent(option)}%` }">
|
||||||
>
|
<view class="is-voted-item-content relative z-2 box-border px-3 py-2">
|
||||||
<view class="is-voted-item-content relative z-2 box-border min-h-[72rpx] px-6 py-3">
|
|
||||||
<view class="flex items-center justify-between">
|
<view class="flex items-center justify-between">
|
||||||
<view class="flex-1 text-left">
|
<view class="flex-1 text-left">
|
||||||
选项{{ optionIndex + 1 }}:{{ option.title }}
|
选项{{ optionIndex + 1 }}:{{ option.title }}
|
||||||
@@ -345,13 +308,10 @@ defineExpose({ refresh: handleGetData })
|
|||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<view
|
<view v-for="(option, optionIndex) in voteData.spec?.options || []" :key="optionIndex"
|
||||||
v-for="(option, optionIndex) in voteData.spec?.options || []"
|
|
||||||
:key="optionIndex"
|
|
||||||
class="vote-select-option box-border rounded-xl bg-[#f6f3ee] px-5 py-4 text-[24rpx]"
|
class="vote-select-option box-border rounded-xl bg-[#f6f3ee] px-5 py-4 text-[24rpx]"
|
||||||
:class="option.checked ? 'border-2 border-primary bg-primary/15 text-primary font-bold' : ''"
|
:class="option.checked ? 'border-2 border-primary bg-primary/15 text-primary font-bold' : ''"
|
||||||
@click="handleSelectSingleOption(option)"
|
@click="handleSelectSingleOption(option)">
|
||||||
>
|
|
||||||
选项{{ optionIndex + 1 }}:{{ option.title }}
|
选项{{ optionIndex + 1 }}:{{ option.title }}
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
@@ -359,19 +319,19 @@ defineExpose({ refresh: handleGetData })
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 底部:时间 + 参与人数/已投票 -->
|
<!-- 底部:时间 + 参与人数/已投票 -->
|
||||||
<view class="vote-card-foot mt-3 flex items-center justify-between border-t border-[#f7f7f7] pt-2">
|
<view class="mt-3 flex items-center justify-between pt-2">
|
||||||
<text v-if="voteData.spec?.timeLimit === 'permanent'" class="text-[22rpx] text-gray-400">
|
<text v-if="voteData.spec?.timeLimit === 'permanent'" class="text-xs text-gray-400">
|
||||||
结束:永久有效
|
结束:永久有效
|
||||||
</text>
|
</text>
|
||||||
<text v-else-if="voteState?.state === '未开始'" class="text-[22rpx] text-gray-400">
|
<text v-else-if="voteState?.state === '未开始'" class="text-xs text-gray-400">
|
||||||
开始:{{ formatTime(voteData.spec?.startDate) }}
|
开始:{{ formatTime(voteData.spec?.startDate) }}
|
||||||
</text>
|
</text>
|
||||||
<text v-else class="text-[22rpx] text-gray-400">
|
<text v-else class="text-xs text-gray-400">
|
||||||
结束:{{ formatTime(voteData.spec?.endDate) }}
|
结束:{{ formatTime(voteData.spec?.endDate) }}
|
||||||
</text>
|
</text>
|
||||||
<view class="flex items-center gap-2">
|
<view class="flex items-center gap-2">
|
||||||
<text class="text-[22rpx] text-gray-400">{{ voteData.stats?.voteCount || 0 }} 人已参与</text>
|
<text class="text-xs text-gray-400">{{ voteData.stats?.voteCount || 0 }} 人已参与</text>
|
||||||
<text v-if="isVoted" class="rounded bg-primary/15 px-2 py-0.5 text-[22rpx] text-primary">已投票</text>
|
<text v-if="isVoted" class="rounded bg-primary/15 px-2 py-0.5 text-xs text-primary">已投票</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
@@ -380,10 +340,12 @@ defineExpose({ refresh: handleGetData })
|
|||||||
<wd-button v-if="isVoted" disabled block>
|
<wd-button v-if="isVoted" disabled block>
|
||||||
您已参与投票
|
您已参与投票
|
||||||
</wd-button>
|
</wd-button>
|
||||||
<wd-button v-else-if="voteState?.state === '未开始'" plain block type="warning" @click="handleSubmitTip('投票未开始')">
|
<wd-button v-else-if="voteState?.state === '未开始'" plain block type="warning"
|
||||||
|
@click="handleSubmitTip('投票未开始')">
|
||||||
投票未开始
|
投票未开始
|
||||||
</wd-button>
|
</wd-button>
|
||||||
<wd-button v-else-if="voteState?.state === '已结束'" plain block type="danger" @click="handleSubmitTip('投票已结束')">
|
<wd-button v-else-if="voteState?.state === '已结束'" plain block type="danger"
|
||||||
|
@click="handleSubmitTip('投票已结束')">
|
||||||
投票已结束
|
投票已结束
|
||||||
</wd-button>
|
</wd-button>
|
||||||
<wd-button v-else-if="!voteData.spec?.canAnonymously" plain block type="danger" @click="handleSubmit()">
|
<wd-button v-else-if="!voteData.spec?.canAnonymously" plain block type="danger" @click="handleSubmit()">
|
||||||
|
|||||||
@@ -1,9 +1,4 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
/**
|
|
||||||
* 文章详情"相关投票"区块容器(源自旧项目 article-detail 的 vote-wrap 区块)
|
|
||||||
* 只接收 voteIds;插件可用性检查/展开收起/区块标题/卡片列表全部内置
|
|
||||||
* 插件未激活或没有投票数据时,整个组件不渲染
|
|
||||||
*/
|
|
||||||
import { onMounted, ref } from 'vue'
|
import { onMounted, ref } from 'vue'
|
||||||
import { NeedPluginIds } from '@/hooks/usePluginAvailable'
|
import { NeedPluginIds } from '@/hooks/usePluginAvailable'
|
||||||
|
|
||||||
@@ -11,13 +6,11 @@ const props = defineProps<{
|
|||||||
voteIds : string[]
|
voteIds : string[]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
/** 展开/收起(与旧项目 voteIsOpen 一致) */
|
|
||||||
const voteIsOpen = ref(true)
|
const voteIsOpen = ref(true)
|
||||||
|
|
||||||
/** 投票插件可用性(检查失败/未安装时整个区块不显示) */
|
|
||||||
const { available: votePluginAvailable, check: checkVotePluginAvailable } = usePluginAvailable({
|
const { available: votePluginAvailable, check: checkVotePluginAvailable } = usePluginAvailable({
|
||||||
pluginId: NeedPluginIds.PluginVote,
|
pluginId: NeedPluginIds.PluginVote,
|
||||||
tips: '检测到当前插件没有安装或者启用,无法使用投票功能哦,请联系管理员',
|
tips: '啊偶,功能正在维护中...',
|
||||||
})
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@@ -26,27 +19,26 @@ onMounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<view v-if="votePluginAvailable && voteIds.length > 0" class="box-border px-1">
|
<view v-if="votePluginAvailable && voteIds.length > 0" class="box-border px-3">
|
||||||
<view class="uh-global-card-glass uh-shadow-xs mb-3 rounded-xl p-3">
|
<view class="box-border uh-global-card-glass mb-3 rounded-xl p-3">
|
||||||
<view class="flex items-center justify-between">
|
<uh-section-title>
|
||||||
<uh-section-title>相关投票</uh-section-title>
|
相关投票
|
||||||
<text class="text-[24rpx] text-gray-400" @click="voteIsOpen = !voteIsOpen">
|
<template #right>
|
||||||
{{ voteIsOpen ? '收起' : '展开' }}
|
<text class="text-xs text-gray-400" @click="voteIsOpen = !voteIsOpen">
|
||||||
|
<wd-icon :name="voteIsOpen?'up':'down'"></wd-icon> {{ voteIsOpen ? '收起' : '展开' }}
|
||||||
</text>
|
</text>
|
||||||
</view>
|
</template>
|
||||||
|
</uh-section-title>
|
||||||
|
|
||||||
<template v-if="voteIsOpen">
|
<template v-if="voteIsOpen">
|
||||||
<view class="mt-3 flex flex-col gap-3">
|
<view class="mt-3 flex flex-col gap-3">
|
||||||
<uh-article-vote-item
|
<uh-article-vote-item v-for="(voteId, voteIdIndex) in voteIds" :key="voteId" :vote-id="voteId"
|
||||||
v-for="(voteId, voteIdIndex) in voteIds"
|
:index="voteIdIndex" />
|
||||||
:key="voteId"
|
|
||||||
:vote-id="voteId"
|
|
||||||
:index="voteIdIndex"
|
|
||||||
/>
|
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<view v-if="!voteIsOpen" class="mt-2 text-center text-[24rpx] text-gray-400" @click="voteIsOpen = !voteIsOpen">
|
<view v-if="!voteIsOpen" class="mt-3 text-center text-xs text-gray-400 py-3 bg-gray-100 rounded-xl"
|
||||||
|
@click="voteIsOpen = !voteIsOpen">
|
||||||
投票已收起,点击展开 {{ voteIds.length }} 个投票项
|
投票已收起,点击展开 {{ voteIds.length }} 个投票项
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
+44
-40
@@ -2,46 +2,46 @@
|
|||||||
* 投票工具(源自旧项目 utils/vote.js,按需命名导出)
|
* 投票工具(源自旧项目 utils/vote.js,按需命名导出)
|
||||||
* 投票状态计算、UID 缓存、投票周期判断
|
* 投票状态计算、UID 缓存、投票周期判断
|
||||||
*/
|
*/
|
||||||
import { getCache, setCache } from './storage'
|
import { getCache, setCache } from './storage';
|
||||||
|
|
||||||
/** 投票 UID 缓存 key */
|
/** 投票 UID 缓存 key */
|
||||||
const UnihaloVoteUid = 'unihalo_vote_uid'
|
const UnihaloVoteUid = 'unihalo_vote_uid';
|
||||||
|
|
||||||
export type VoteType = 'single' | 'multiple' | 'pk'
|
export type VoteType = 'single' | 'multiple' | 'pk';
|
||||||
export type VoteState = 'not-voted' | 'voting' | 'voted' | 'vote-ended'
|
export type VoteState = 'not-voted' | 'voting' | 'voted' | 'vote-ended';
|
||||||
|
|
||||||
/** 投票类型中文映射(与旧项目一致:key 为插件小写 type) */
|
/** 投票类型中文映射(与旧项目一致:key 为插件小写 type) */
|
||||||
export const VOTE_TYPES: Record<string, string> = {
|
export const VOTE_TYPES: Record<string, string> = {
|
||||||
pk: '双选PK',
|
pk: '双选PK',
|
||||||
multiple: '多选',
|
multiple: '多选',
|
||||||
single: '单选',
|
single: '单选'
|
||||||
}
|
};
|
||||||
|
|
||||||
/** 投票状态常量(内部状态机) */
|
/** 投票状态常量(内部状态机) */
|
||||||
export const VOTE_STATES: { NOT_VOTED: VoteState, VOTING: VoteState, VOTED: VoteState, VOTE_ENDED: VoteState } = {
|
export const VOTE_STATES: { NOT_VOTED: VoteState; VOTING: VoteState; VOTED: VoteState; VOTE_ENDED: VoteState } = {
|
||||||
NOT_VOTED: 'not-voted',
|
NOT_VOTED: 'not-voted',
|
||||||
VOTING: 'voting',
|
VOTING: 'voting',
|
||||||
VOTED: 'voted',
|
VOTED: 'voted',
|
||||||
VOTE_ENDED: 'vote-ended',
|
VOTE_ENDED: 'vote-ended'
|
||||||
}
|
};
|
||||||
|
|
||||||
/** 投票展示状态(与旧项目 VOTE_STATES 一致:中文 + 颜色) */
|
/** 投票展示状态(与旧项目 VOTE_STATES 一致:中文 + 颜色) */
|
||||||
export const VOTE_STATE_LABELS: Record<string, { state: string, color: string }> = {
|
export const VOTE_STATE_LABELS: Record<string, { state: string; color: string }> = {
|
||||||
未开始: { state: '未开始', color: 'orange' },
|
未开始: { state: '未开始', color: 'orange' },
|
||||||
进行中: { state: '进行中', color: 'green' },
|
进行中: { state: '进行中', color: 'green' },
|
||||||
已结束: { state: '已结束', color: 'red' },
|
已结束: { state: '已结束', color: 'red' }
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取投票 UID(不存在则生成)
|
* 获取投票 UID(不存在则生成)
|
||||||
*/
|
*/
|
||||||
export function getOrCreateVoteUid(): string {
|
export function getOrCreateVoteUid(): string {
|
||||||
let uid = getCache<string>(UnihaloVoteUid)
|
let uid = getCache<string>(UnihaloVoteUid);
|
||||||
if (!uid) {
|
if (!uid) {
|
||||||
uid = `${Date.now()}-${Math.random().toString(36).slice(2, 10)}`
|
uid = `${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
||||||
setCache(UnihaloVoteUid, uid)
|
setCache(UnihaloVoteUid, uid);
|
||||||
}
|
}
|
||||||
return uid
|
return uid;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -50,22 +50,25 @@ export function getOrCreateVoteUid(): string {
|
|||||||
* @param vote 投票对象(含 spec.timeLimit/hasEnded/startDate/endDate)
|
* @param vote 投票对象(含 spec.timeLimit/hasEnded/startDate/endDate)
|
||||||
* @returns { state: '未开始' | '进行中' | '已结束', color: 'orange' | 'green' | 'red' }
|
* @returns { state: '未开始' | '进行中' | '已结束', color: 'orange' | 'green' | 'red' }
|
||||||
*/
|
*/
|
||||||
export function calcVoteState(
|
export function calcVoteState(vote: { spec?: { timeLimit?: string; hasEnded?: boolean; startDate?: string; endDate?: string; [key: string]: unknown } }): {
|
||||||
vote: { spec?: { timeLimit?: string, hasEnded?: boolean, startDate?: string, endDate?: string, [key: string]: unknown } },
|
state: string;
|
||||||
): { state: string, color: string } {
|
color: string;
|
||||||
|
} {
|
||||||
if (vote.spec?.timeLimit !== 'custom') {
|
if (vote.spec?.timeLimit !== 'custom') {
|
||||||
return vote.spec?.hasEnded ? VOTE_STATE_LABELS['已结束'] : VOTE_STATE_LABELS['进行中']
|
return vote.spec?.hasEnded ? VOTE_STATE_LABELS['已结束'] : VOTE_STATE_LABELS['进行中'];
|
||||||
}
|
}
|
||||||
|
|
||||||
const nowTime = new Date().getTime()
|
const nowTime = new Date().getTime();
|
||||||
const startTime = vote.spec?.startDate ? new Date(vote.spec.startDate).getTime() : nowTime
|
const startTime = vote.spec?.startDate ? new Date(vote.spec.startDate).getTime() : nowTime;
|
||||||
const endTime = vote.spec?.endDate ? new Date(vote.spec.endDate).getTime() : nowTime
|
const endTime = vote.spec?.endDate ? new Date(vote.spec.endDate).getTime() : nowTime;
|
||||||
|
|
||||||
if (nowTime < startTime)
|
if (nowTime < startTime) {
|
||||||
return VOTE_STATE_LABELS['未开始']
|
return VOTE_STATE_LABELS['未开始'];
|
||||||
if (nowTime < endTime)
|
}
|
||||||
return VOTE_STATE_LABELS['进行中']
|
if (nowTime < endTime) {
|
||||||
return vote.spec?.hasEnded ? VOTE_STATE_LABELS['已结束'] : VOTE_STATE_LABELS['进行中']
|
return VOTE_STATE_LABELS['进行中'];
|
||||||
|
}
|
||||||
|
return vote.spec?.hasEnded ? VOTE_STATE_LABELS['已结束'] : VOTE_STATE_LABELS['进行中'];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -74,19 +77,20 @@ export function calcVoteState(
|
|||||||
* @param option 选项(含 count)
|
* @param option 选项(含 count)
|
||||||
*/
|
*/
|
||||||
export function calcVotePercent(vote: { stats?: { voteCount?: number } }, option: { count?: number }): number {
|
export function calcVotePercent(vote: { stats?: { voteCount?: number } }, option: { count?: number }): number {
|
||||||
const total = vote.stats?.voteCount || 0
|
const total = vote.stats?.voteCount || 0;
|
||||||
const count = option.count || 0
|
const count = option.count || 0;
|
||||||
if (total === 0)
|
if (total === 0) {
|
||||||
return 0
|
return 0;
|
||||||
return Math.round((count / total) * 100)
|
}
|
||||||
|
return Math.round((count / total) * 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 投票缓存 key 前缀 */
|
/** 投票缓存 key 前缀 */
|
||||||
const VOTE_CACHE_KEY = 'unihalo_vote_'
|
const VOTE_CACHE_KEY = 'unihalo_vote_';
|
||||||
|
|
||||||
interface IVoteCacheData {
|
interface IVoteCacheData {
|
||||||
selected: string[]
|
selected: string[];
|
||||||
data: unknown
|
data: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -95,14 +99,14 @@ interface IVoteCacheData {
|
|||||||
export const voteCacheUtil = {
|
export const voteCacheUtil = {
|
||||||
/** 是否已缓存(已投票) */
|
/** 是否已缓存(已投票) */
|
||||||
has(name: string): boolean {
|
has(name: string): boolean {
|
||||||
return !!getCache<IVoteCacheData>(VOTE_CACHE_KEY + name)
|
return !!getCache<IVoteCacheData>(VOTE_CACHE_KEY + name);
|
||||||
},
|
},
|
||||||
/** 获取缓存数据 */
|
/** 获取缓存数据 */
|
||||||
get(name: string): IVoteCacheData | null {
|
get(name: string): IVoteCacheData | null {
|
||||||
return getCache<IVoteCacheData>(VOTE_CACHE_KEY + name) || null
|
return getCache<IVoteCacheData>(VOTE_CACHE_KEY + name) || null;
|
||||||
},
|
},
|
||||||
/** 写入缓存 */
|
/** 写入缓存 */
|
||||||
set(name: string, data: IVoteCacheData): void {
|
set(name: string, data: IVoteCacheData): void {
|
||||||
setCache(VOTE_CACHE_KEY + name, data)
|
setCache(VOTE_CACHE_KEY + name, data);
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user