mirror of
https://github.com/Megghy/vtsuru.live.git
synced 2025-12-07 02:46:55 +08:00
add feedback page
This commit is contained in:
@@ -437,3 +437,24 @@ export interface DanmakuModel {
|
|||||||
isEmoji: boolean
|
isEmoji: boolean
|
||||||
num: number
|
num: number
|
||||||
}
|
}
|
||||||
|
export interface ResponseFeedbackModel {
|
||||||
|
message: string
|
||||||
|
type: FeedbackType
|
||||||
|
status: FeedbackStatus
|
||||||
|
replyMessage?: string
|
||||||
|
userName?: string
|
||||||
|
createAt: number
|
||||||
|
}
|
||||||
|
export enum FeedbackType {
|
||||||
|
Opinion,
|
||||||
|
Bug,
|
||||||
|
FunctionRequest,
|
||||||
|
Other,
|
||||||
|
}
|
||||||
|
export enum FeedbackStatus {
|
||||||
|
Padding,
|
||||||
|
Progressing,
|
||||||
|
Finish,
|
||||||
|
Todo,
|
||||||
|
Reject,
|
||||||
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ export const SONG_REQUEST_API_URL = `${BASE_API}song-request/`
|
|||||||
export const QUEUE_API_URL = `${BASE_API}queue/`
|
export const QUEUE_API_URL = `${BASE_API}queue/`
|
||||||
export const EVENT_API_URL = `${BASE_API}event/`
|
export const EVENT_API_URL = `${BASE_API}event/`
|
||||||
export const LIVE_API_URL = `${BASE_API}live/`
|
export const LIVE_API_URL = `${BASE_API}live/`
|
||||||
|
export const FEEDBACK_API_URL = `${BASE_API}feedback/`
|
||||||
|
|
||||||
export const ScheduleTemplateMap = {
|
export const ScheduleTemplateMap = {
|
||||||
'': { name: '默认', compoent: defineAsyncComponent(() => import('@/views/view/scheduleTemplate/DefaultScheduleTemplate.vue')) },
|
'': { name: '默认', compoent: defineAsyncComponent(() => import('@/views/view/scheduleTemplate/DefaultScheduleTemplate.vue')) },
|
||||||
|
|||||||
@@ -228,6 +228,14 @@ const routes: Array<RouteRecordRaw> = [
|
|||||||
keepAlive: true,
|
keepAlive: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'feedback',
|
||||||
|
name: 'manage-feedback',
|
||||||
|
component: () => import('@/views/FeedbackManage.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '反馈',
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ import { NButton, NCard, NDivider, NLayoutContent, NSpace, NText, NTimeline, NTi
|
|||||||
>
|
>
|
||||||
873260337
|
873260337
|
||||||
</NButton>
|
</NButton>
|
||||||
|
或者
|
||||||
|
<NButton text type="info" @click="$router.push({ name: 'manage-feedback' })"> 反馈页面 </NButton>
|
||||||
<NDivider vertical />
|
<NDivider vertical />
|
||||||
邮箱:
|
邮箱:
|
||||||
<NButton tag="a" type="info" href="mailto:admin@vtsuru.live" text> admin@vtsuru.live </NButton>
|
<NButton tag="a" type="info" href="mailto:admin@vtsuru.live" text> admin@vtsuru.live </NButton>
|
||||||
|
|||||||
151
src/views/FeedbackManage.vue
Normal file
151
src/views/FeedbackManage.vue
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { useAccount } from '@/api/account'
|
||||||
|
import { FeedbackStatus, FeedbackType, ResponseFeedbackModel } from '@/api/api-models'
|
||||||
|
import { QueryGetAPI, QueryPostAPI } from '@/api/query'
|
||||||
|
import { FEEDBACK_API_URL } from '@/data/constants'
|
||||||
|
import { List } from 'linqts'
|
||||||
|
import { NButton, NCard, NCheckbox, NDivider, NEllipsis, NEmpty, NForm, NGrid, NGridItem, NInput, NModal, NRadioButton, NRadioGroup, NSpace, NSpin, NTag, NText, NTooltip, useMessage } from 'naive-ui'
|
||||||
|
import { computed, ref } from 'vue'
|
||||||
|
|
||||||
|
interface FeedbackModel {
|
||||||
|
message: string
|
||||||
|
type: FeedbackType
|
||||||
|
isAnonymous: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const accountInfo = useAccount()
|
||||||
|
const message = useMessage()
|
||||||
|
|
||||||
|
const feedbacks = ref<ResponseFeedbackModel[]>(await get())
|
||||||
|
const orderType = ref<'time' | 'status'>('status')
|
||||||
|
const order = {
|
||||||
|
[FeedbackStatus.Progressing]: 1,
|
||||||
|
[FeedbackStatus.Padding]: 2,
|
||||||
|
[FeedbackStatus.Todo]: 3,
|
||||||
|
[FeedbackStatus.Finish]: 4,
|
||||||
|
[FeedbackStatus.Reject]: 5,
|
||||||
|
}
|
||||||
|
const selectedFeedback = computed(() => {
|
||||||
|
return feedbacks.value.sort((a, b) => {
|
||||||
|
if (orderType.value == 'time') {
|
||||||
|
return b.createAt - a.createAt
|
||||||
|
} else {
|
||||||
|
return (order[a.status] ?? 0) - (order[b.status] ?? 0)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
const showAddModal = ref(false)
|
||||||
|
const newFeedback = ref<FeedbackModel>({
|
||||||
|
message: '',
|
||||||
|
type: 0,
|
||||||
|
isAnonymous: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
async function get() {
|
||||||
|
try {
|
||||||
|
const data = await QueryGetAPI<ResponseFeedbackModel[]>(FEEDBACK_API_URL + 'get')
|
||||||
|
if (data.code == 200) {
|
||||||
|
return new List(data.data).OrderByDescending((s) => s.createAt).ToArray()
|
||||||
|
} else {
|
||||||
|
message.error('无法获取数据: ' + data.message)
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
message.error('无法获取数据')
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
async function add() {
|
||||||
|
if (!newFeedback.value.message) {
|
||||||
|
message.error('反馈内容不能为空')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
await QueryPostAPI<ResponseFeedbackModel>(FEEDBACK_API_URL + 'add', newFeedback.value)
|
||||||
|
.then((data) => {
|
||||||
|
if (data.code == 200) {
|
||||||
|
message.success('发送成功, 感谢你的反馈!')
|
||||||
|
feedbacks.value.push(data.data)
|
||||||
|
|
||||||
|
showAddModal.value = false
|
||||||
|
} else {
|
||||||
|
message.error(data.message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err)
|
||||||
|
message.error('发送失败')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<NSpace>
|
||||||
|
<NButton @click="showAddModal = true" type="info">添加反馈</NButton>
|
||||||
|
</NSpace>
|
||||||
|
<NDivider>
|
||||||
|
<NTooltip>
|
||||||
|
<template #trigger>
|
||||||
|
<NRadioGroup v-model:value="orderType" size="small">
|
||||||
|
<NRadioButton value="status">状态</NRadioButton>
|
||||||
|
<NRadioButton value="time">时间</NRadioButton>
|
||||||
|
</NRadioGroup>
|
||||||
|
</template>
|
||||||
|
排序方式
|
||||||
|
</NTooltip>
|
||||||
|
</NDivider>
|
||||||
|
<NEmpty v-if="feedbacks.length == 0" description="暂无反馈" />
|
||||||
|
<NSpace v-else>
|
||||||
|
<NCard v-for="item in selectedFeedback" v-bind:key="item.createAt" size="small" embedded style="min-width: 300px">
|
||||||
|
<template #header>
|
||||||
|
<NTag v-if="item.status == FeedbackStatus.Padding" :bordered="false"> 等待中 </NTag>
|
||||||
|
<NTag v-else-if="item.status == FeedbackStatus.Progressing" type="success">
|
||||||
|
<template #icon>
|
||||||
|
<NSpin :size="12" />
|
||||||
|
</template>
|
||||||
|
处理中
|
||||||
|
</NTag>
|
||||||
|
<NTag v-else-if="item.status == FeedbackStatus.Finish" :bordered="false" type="primary"> 已完成 </NTag>
|
||||||
|
<NTag v-else-if="item.status == FeedbackStatus.Todo" :bordered="false" type="info"> 计划中 </NTag>
|
||||||
|
<NTag v-else-if="item.status == FeedbackStatus.Reject" :bordered="false" type="warning"> 搁置 </NTag>
|
||||||
|
<NDivider vertical />
|
||||||
|
<NTag v-if="!item.userName"> 匿名 </NTag>
|
||||||
|
<template v-else>
|
||||||
|
<NEllipsis>
|
||||||
|
{{ item.userName }}
|
||||||
|
</NEllipsis>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<template #header-extra>
|
||||||
|
<NTag v-if="item.type == FeedbackType.Opinion" :bordered="false" size="small" type="info" :color="{ color: '#5f877d', textColor: 'white' }"> 建议 </NTag>
|
||||||
|
<NTag v-else-if="item.type == FeedbackType.Bug" :bordered="false" size="small" type="info" :color="{ color: '#875f5f', textColor: 'white' }"> Bug </NTag>
|
||||||
|
<NTag v-else-if="item.type == FeedbackType.FunctionRequest" :bordered="false" size="small" type="info" :color="{ color: '#5f6887', textColor: 'white' }"> 功能 </NTag>
|
||||||
|
<NTag v-else-if="item.type == FeedbackType.Other" :bordered="false" size="small" type="info" :color="{ color: '#595557', textColor: 'white' }"> 其他 </NTag>
|
||||||
|
</template>
|
||||||
|
{{ item.message }}
|
||||||
|
<template v-if="item.replyMessage" #footer>
|
||||||
|
<NDivider style="margin: 0px 0 10px 0" />
|
||||||
|
<NSpace align="center">
|
||||||
|
<div :style="`border-radius: 4px; background-color: #75c37f; width: 10px; height: 15px`"></div>
|
||||||
|
<NText>
|
||||||
|
{{ item.replyMessage }}
|
||||||
|
</NText>
|
||||||
|
</NSpace>
|
||||||
|
</template>
|
||||||
|
</NCard>
|
||||||
|
</NSpace>
|
||||||
|
<NModal v-model:show="showAddModal" preset="card" title="添加反馈" style="width: 600px; max-width: 90vw">
|
||||||
|
<NSpace vertical>
|
||||||
|
<NInput v-model:value="newFeedback.message" type="textarea" placeholder="请输入反馈内容" clearable show-count maxlength="1000" />
|
||||||
|
<NRadioGroup v-model:value="newFeedback.type" name="radiogroup">
|
||||||
|
<NRadioButton :value="FeedbackType.Opinion">建议 / 意见</NRadioButton>
|
||||||
|
<NRadioButton :value="FeedbackType.Bug">Bug</NRadioButton>
|
||||||
|
<NRadioButton :value="FeedbackType.FunctionRequest">功能需求</NRadioButton>
|
||||||
|
<NRadioButton :value="FeedbackType.Other">其他</NRadioButton>
|
||||||
|
</NRadioGroup>
|
||||||
|
<NCheckbox v-model:checked="newFeedback.isAnonymous">匿名</NCheckbox>
|
||||||
|
<NDivider style="margin: 10px 0 10px 0" />
|
||||||
|
<NButton @click="add" type="primary">发送</NButton>
|
||||||
|
</NSpace>
|
||||||
|
</NModal>
|
||||||
|
</template>
|
||||||
@@ -22,7 +22,7 @@ import {
|
|||||||
} from 'naive-ui'
|
} from 'naive-ui'
|
||||||
import { h, onMounted, ref } from 'vue'
|
import { h, onMounted, ref } from 'vue'
|
||||||
import { BrowsersOutline, Chatbox, Moon, MusicalNote, Sunny, AnalyticsSharp } from '@vicons/ionicons5'
|
import { BrowsersOutline, Chatbox, Moon, MusicalNote, Sunny, AnalyticsSharp } from '@vicons/ionicons5'
|
||||||
import { CalendarClock24Filled, Chat24Filled, Info24Filled, Live24Filled, Lottery24Filled, PeopleQueue24Filled, VehicleShip24Filled, VideoAdd20Filled } from '@vicons/fluent'
|
import { CalendarClock24Filled, Chat24Filled, Info24Filled, Live24Filled, Lottery24Filled, PeopleQueue24Filled, PersonFeedback24Filled, VehicleShip24Filled, VideoAdd20Filled } from '@vicons/fluent'
|
||||||
import { isLoadingAccount, useAccount } from '@/api/account'
|
import { isLoadingAccount, useAccount } from '@/api/account'
|
||||||
import RegisterAndLogin from '@/components/RegisterAndLogin.vue'
|
import RegisterAndLogin from '@/components/RegisterAndLogin.vue'
|
||||||
import { RouterLink, useRoute } from 'vue-router'
|
import { RouterLink, useRoute } from 'vue-router'
|
||||||
@@ -342,6 +342,16 @@ onMounted(() => {
|
|||||||
</template>
|
</template>
|
||||||
<template v-if="width >= 180"> 面板 </template>
|
<template v-if="width >= 180"> 面板 </template>
|
||||||
</NButton>
|
</NButton>
|
||||||
|
<NTooltip v-if="width >= 180">
|
||||||
|
<template #trigger>
|
||||||
|
<NButton @click="$router.push({ name: 'manage-feedback' })">
|
||||||
|
<template #icon>
|
||||||
|
<NIcon :component="PersonFeedback24Filled" />
|
||||||
|
</template>
|
||||||
|
</NButton>
|
||||||
|
</template>
|
||||||
|
反馈
|
||||||
|
</NTooltip>
|
||||||
</NSpace>
|
</NSpace>
|
||||||
<NMenu
|
<NMenu
|
||||||
style="margin-top: 12px"
|
style="margin-top: 12px"
|
||||||
@@ -351,10 +361,13 @@ onMounted(() => {
|
|||||||
:collapsed-icon-size="22"
|
:collapsed-icon-size="22"
|
||||||
:options="menuOptions"
|
:options="menuOptions"
|
||||||
/>
|
/>
|
||||||
<NSpace justify="center">
|
<NSpace v-if="width > 150" justify="center" align="center" vertical>
|
||||||
<NText depth="3" v-if="width > 150">
|
<NText depth="3">
|
||||||
有更多功能建议请
|
有更多功能建议请
|
||||||
<NButton text type="info" @click="$router.push({ name: 'about' })"> 反馈 </NButton>
|
<NButton text type="info" @click="$router.push({ name: 'manage-feedback' })"> 反馈 </NButton>
|
||||||
|
</NText>
|
||||||
|
<NText depth="3">
|
||||||
|
<NButton text type="info" @click="$router.push({ name: 'about' })"> 关于本站 </NButton>
|
||||||
</NText>
|
</NText>
|
||||||
</NSpace>
|
</NSpace>
|
||||||
</NLayoutSider>
|
</NLayoutSider>
|
||||||
|
|||||||
@@ -309,13 +309,19 @@ onMounted(() => {
|
|||||||
</template>
|
</template>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<NSpace>
|
<NSpace>
|
||||||
|
<NButton v-if="!item.isReaded" size="small" @click="read(item, true)" type="success"> 设为已读 </NButton>
|
||||||
<NButton size="small" @click="favorite(item, !item.isFavorite)">
|
<NButton size="small" @click="favorite(item, !item.isFavorite)">
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<NIcon :component="item.isFavorite ? Heart : HeartOutline" :color="item.isFavorite ? '#dd484f' : ''" />
|
<NIcon :component="item.isFavorite ? Heart : HeartOutline" :color="item.isFavorite ? '#dd484f' : ''" />
|
||||||
</template>
|
</template>
|
||||||
收藏
|
收藏
|
||||||
</NButton>
|
</NButton>
|
||||||
<NButton size="small"> 举报 </NButton>
|
<NTooltip>
|
||||||
|
<template #trigger>
|
||||||
|
<NButton size="small"> 举报 </NButton>
|
||||||
|
</template>
|
||||||
|
暂时还没写
|
||||||
|
</NTooltip>
|
||||||
<NButton size="small" @click="blacklist(item)"> 拉黑 </NButton>
|
<NButton size="small" @click="blacklist(item)"> 拉黑 </NButton>
|
||||||
</NSpace>
|
</NSpace>
|
||||||
</template>
|
</template>
|
||||||
@@ -360,9 +366,7 @@ onMounted(() => {
|
|||||||
<NCard :bordered="false" size="small">
|
<NCard :bordered="false" size="small">
|
||||||
<template #header>
|
<template #header>
|
||||||
<NSpace align="center">
|
<NSpace align="center">
|
||||||
<NText depth="3">
|
<NText depth="3"> 回复 </NText>
|
||||||
回复
|
|
||||||
</NText>
|
|
||||||
</NSpace>
|
</NSpace>
|
||||||
</template>
|
</template>
|
||||||
{{ item.answer.message }}
|
{{ item.answer.message }}
|
||||||
|
|||||||
Reference in New Issue
Block a user