Skip to content

Commit

Permalink
feat(page): review records
Browse files Browse the repository at this point in the history
  • Loading branch information
greenhat616 committed Aug 31, 2023
1 parent 2808cd3 commit 5487006
Show file tree
Hide file tree
Showing 17 changed files with 518 additions and 28 deletions.
28 changes: 28 additions & 0 deletions components/PollMarks.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<script setup lang="ts">
const props = defineProps<{
marks: number[]
}>()
const emit = defineEmits<{
onMarkClick: [markID: number]
}>()
const marksStore = useMarksStore()
</script>

<template>
<a-tag
v-for="mark in props.marks"
:key="mark"
:color="marksStore.markColorMap[mark]?.color || 'yellow'"
@click="emit('onMarkClick', mark)"
>
{{ marksStore.markColorMap[mark]?.text || '未知' }}
</a-tag>
</template>
<style lang="scss" scoped>
.ant-tag {
@apply mx-1 mb-2;
}
</style>
3 changes: 2 additions & 1 deletion components/do/review/Card.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import xss from 'xss'
import dayjs from 'dayjs'
import type { PollMethod } from '@/enums/poll'
import type { HitokotoType } from '~/enums/hitokoto'
// Props 定义
export type CardPropsPoll = {
Expand All @@ -15,7 +16,7 @@ export type CardPropsPoll = {
export type CardPropsSentence = {
uuid: string
hitokoto: string
type: string
type: HitokotoType
fromWho?: string
from: string
creator: string
Expand Down
77 changes: 77 additions & 0 deletions components/review/records/Card.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<script lang="ts" setup>
import dayjs from 'dayjs'
import { UserRole } from '@/enums/user'
import { PollStatus } from '@/enums/poll'
import type { UserPollLogsRes } from '@/composables/api'
const userStore = useUserStore()
const props = defineProps<{
userPollLog: UserPollLogsRes['collection'][0]
}>()
const sentence = computed(() => {
return {
uuid: props.userPollLog.sentence.uuid,
hitokoto: props.userPollLog.sentence.hitokoto,
type: props.userPollLog.sentence.type,
fromWho: props.userPollLog.sentence.from_who,
from: props.userPollLog.sentence.from,
creator: props.userPollLog.sentence.creator,
createdAt: props.userPollLog.sentence.created_at
}
})
const emit = defineEmits<{
showPollDetail: [pollID: number]
}>()
</script>

<template>
<a-card class="review-record-card">
<template #title> #{{ props.userPollLog.poll_id }} </template>
<ReviewRecordsCardSentence :sentence="sentence" />
<a-divider />

<div class="polled-info-container">
<p class="tips">
操作时间:{{
dayjs(userPollLog.created_at).format('YYYY-MM-DD HH:mm:ss')
}}
</p>
<p class="poll-info">
投票类型:<b>{{ convertPollMethod(userPollLog.type) }}</b>
{{ userPollLog.point }} 票
</p>
<div
v-show="userPollLog.user_marks && userPollLog.user_marks.length > 0"
class="user-marks"
>
审核标记:<PollMarks :marks="userPollLog.user_marks" />
</div>
<p v-show="userPollLog.comment" class="comment">
投票评论:{{ userPollLog.comment }}
</p>
</div>
<div class="actions-container flex justify-end mt-5">
<a-button
v-show="
userStore.user?.role === UserRole.Admin ||
userPollLog.sentence.poll_status != PollStatus.Open
"
type="primary"
@click="emit('showPollDetail', props.userPollLog.poll_id)"
>
投票详情
</a-button>
</div>
</a-card>
</template>
<style lang="scss" scoped>
.polled-info-container {
p {
@apply mb-1 mt-0;
}
}
</style>
48 changes: 48 additions & 0 deletions components/review/records/CardSentence.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<script lang="ts" setup>
import type { CardPropsSentence } from '@/components/do/review/Card.vue'
import dayjs from 'dayjs'
import { filterXSS } from 'xss'
const props = defineProps<{
sentence: CardPropsSentence
}>()
</script>

<template>
<a-popover placement="top">
<template #content>
<p>分类:{{ convertHitokotoType(props.sentence.type) }}</p>
<p>提交者:{{ props.sentence.creator }}</p>
<p>
提交于:
{{
dayjs(+props.sentence.createdAt * 1000).format('YYYY-MM-DD HH:mm:ss')
}}
</p>
</template>
<template #title>
<span class="font-mono">{{ props.sentence.uuid }}</span>
</template>
<div class="hitokoto">
<!-- eslint-disable-next-line vue/no-v-html -->
<p class="sentence" v-html="filterXSS(props.sentence.hitokoto)"></p>
<p class="author">
—— {{ props.sentence.fromWho || '' }}『{{ props.sentence.from }}』
</p>
</div>
</a-popover>
</template>

<style lang="scss" scoped>
.hitokoto {
@apply w-full font-noto-serif;
.sentence {
@apply text-lg w-full font-600;
}
.author {
@apply text-sm w-full text-right;
}
}
</style>
103 changes: 103 additions & 0 deletions components/review/records/PollDetailModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
<script lang="ts" setup>
import dayjs from 'dayjs'
import { PollStatus } from '~/enums/poll'
const props = defineProps<{
pollId: number
open: boolean
}>()
const emit = defineEmits<{
'update:open': [value: boolean]
}>()
// fetch PollDetail
const pollID = computed(() => props.pollId)
const { data, pending, error, refresh } = usePollDetail(pollID)
watch(
() => props.open,
(val) => {
val && refresh()
}
)
</script>

<template>
<a-modal
:open="props.open"
:title="`投票详情 #${pollID}`"
@update:open="emit('update:open', $event)"
@ok="emit('update:open', false)"
>
<template v-if="pending">
<a-spin />
</template>
<template v-if="error">
<p>加载失败</p>
</template>
<template v-if="!pending && !error">
<div class="chart">
<ReviewRecordsPollPointsPieChart
:approve="data?.data.approve || 0"
:reject="data?.data.reject || 0"
:need-modify="data?.data.need_edited || 0"
/>
</div>
<div class="poll-info-container">
<p>
状态:{{ convertPollStatus(data?.data.status as PollStatus) || -1 }}
</p>
<p>
创建于:{{
dayjs(data?.data.created_at).format('YYYY-MM-DD HH:mm:ss')
}}
</p>
<p>
更新于:{{
dayjs(data?.data.updated_at).format('YYYY-MM-DD HH:mm:ss')
}}
</p>
</div>
<div
v-show="data?.data.marks && data?.data.marks.length > 0"
class="marks"
>
<span>审核标记:</span>
<PollMarks :marks="data?.data.marks || []" />
</div>
<div
v-if="data?.data.records && data?.data.records.length > 0"
class="records"
>
<p class="title">审核记录</p>
<ul>
<li v-for="record in data?.data.records" :key="record.user_id">
{{ record.user_id }}:{{ convertPollMethod(record.method) }}
{{ record.point }} 票
{{ record.comment && `,评论到:“${record.comment}”` }}
</li>
</ul>
</div>
</template>
</a-modal>
</template>
<style lang="scss" scoped>
.chart {
@apply w-full h-70 mt-5;
}
.poll-info-container {
@apply mt-5;
p {
@apply mb-0.25 mt-0;
}
}
.records {
.title {
@apply font-bold text-base;
}
}
</style>
70 changes: 70 additions & 0 deletions components/review/records/PollPointsPieChart.client.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<script lang="ts" setup>
import { use } from 'echarts/core'
import { CanvasRenderer } from 'echarts/renderers'
import { PieChart } from 'echarts/charts'
import {
TitleComponent,
TooltipComponent,
LegendComponent
} from 'echarts/components'
import VChart from 'vue-echarts'
const props = defineProps<{
approve: number
reject: number
needModify: number
}>()
use([
CanvasRenderer,
PieChart,
TitleComponent,
TooltipComponent,
LegendComponent
])
// provide(THEME_KEY, 'dark')
const option = computed(() => {
return {
title: {
text: '投票概览',
left: 'center'
},
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b} : {c} ({d}%)'
},
legend: {
orient: 'vertical',
left: 'left',
data: ['赞同', '驳回', '需要修改']
},
color: ['#91cc75', '#ee6666', '#fac858'],
series: [
{
name: '投票',
type: 'pie',
radius: '55%',
center: ['50%', '60%'],
data: [
{ value: props.approve, name: '赞同' },
{ value: props.reject, name: '驳回' },
{ value: props.needModify, name: '需要修改' }
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
}
})
</script>

<template>
<VChart class="chart" :option="option" autoresize />
</template>
8 changes: 6 additions & 2 deletions composables/api/reviewer/poll.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,13 @@ export type PollDetailReq = {
export type PollDetailRes = PollListCollectionElement

export function usePollDetail(
pollID: number,
pollID: number | Ref<number>,
req: PollDetailReq = {},
options: HTTPOption<PollDetailRes> = {}
) {
return useHTTP.get<PollDetailRes>(`/poll/${pollID}`, req, options)
return useHTTP.get<PollDetailRes>(
() => `/poll/${typeof pollID === 'number' ? pollID : pollID.value}`,
req,
options
)
}
Loading

0 comments on commit 5487006

Please sign in to comment.