mirror of
https://github.com/Megghy/vtsuru.live.git
synced 2025-12-06 18:36:55 +08:00
support guard price
This commit is contained in:
@@ -85,16 +85,18 @@ export interface ForumTopicBaseModel {
|
||||
isLocked?: boolean // Assuming the default value is handled elsewhere
|
||||
isPinned?: boolean // Assuming the default value is handled elsewhere
|
||||
isHighlighted?: boolean // Assuming the default value is handled elsewhere
|
||||
isDeleted?: boolean // Assuming the default value is handled elsewhere
|
||||
}
|
||||
export interface ForumTopicModel extends ForumTopicBaseModel {
|
||||
isLocked?: boolean // Assuming the default value is handled elsewhere
|
||||
isDeleted?: boolean // Assuming the default value is handled elsewhere
|
||||
|
||||
isHidden?: boolean // Assuming the default value is handled elsewhere
|
||||
|
||||
type?: ForumTopicTypes // Assuming the default value is handled elsewhere
|
||||
extraTypeId?: number | null // Nullable int in C# is optional or null in TS
|
||||
likedBy?: number[] // Assuming the default value is handled elsewhere
|
||||
|
||||
isAdmin: boolean
|
||||
}
|
||||
export interface ForumCommentModel {
|
||||
id: number
|
||||
@@ -105,6 +107,8 @@ export interface ForumCommentModel {
|
||||
|
||||
likeCount: number
|
||||
isLiked: boolean
|
||||
|
||||
isDeleted: boolean
|
||||
}
|
||||
export interface ForumReplyModel {
|
||||
id: number
|
||||
|
||||
@@ -2,7 +2,7 @@ import { QueryGetAPI } from '@/api/query'
|
||||
import { USER_API_URL, apiFail } from '@/data/constants'
|
||||
import { ref } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { APIRoot, UserInfo } from './api-models'
|
||||
import { APIRoot, UserBasicInfo, UserInfo } from './api-models'
|
||||
|
||||
export const USERS = ref<{ [id: string]: UserInfo }>({})
|
||||
|
||||
@@ -38,6 +38,10 @@ export async function useUserWithUId(id: number) {
|
||||
}
|
||||
return USERS.value[id.toString()]
|
||||
}
|
||||
export async function getUserBasicInfo(id: string | number | undefined) {
|
||||
if (!id) return undefined
|
||||
return (await QueryGetAPI<UserBasicInfo>(`${USER_API_URL}basic/${id}`)).data
|
||||
}
|
||||
|
||||
export async function GetInfo(id: string): Promise<APIRoot<UserInfo>> {
|
||||
return QueryGetAPI<UserInfo>(`${USER_API_URL}info`, {
|
||||
|
||||
@@ -30,6 +30,7 @@ function OnClickCover() {
|
||||
params: { id: live.liveId },
|
||||
})
|
||||
}
|
||||
const guartPriceStartData = new Date(Date.UTC(2024, 2, 24, 10, 0, 0))
|
||||
|
||||
watch(
|
||||
() => live,
|
||||
@@ -119,7 +120,7 @@ watch(
|
||||
<NStatistic tabular-nums>
|
||||
<template #label>
|
||||
收益
|
||||
<NTooltip>
|
||||
<NTooltip v-if="new Date(live.startAt) < guartPriceStartData">
|
||||
<template #trigger>
|
||||
<NIcon :component="Info24Filled" />
|
||||
</template>
|
||||
|
||||
@@ -124,7 +124,7 @@ export const useForumStore = defineStore('forum', () => {
|
||||
sort,
|
||||
})
|
||||
if (data.code == 200) {
|
||||
return data.data
|
||||
return data
|
||||
} else {
|
||||
console.error('无法获取数据: ' + data.message)
|
||||
message?.error('无法获取数据: ' + data.message)
|
||||
@@ -317,7 +317,7 @@ export const useForumStore = defineStore('forum', () => {
|
||||
async function DelComment(comment: number) {
|
||||
try {
|
||||
isLoading.value = true
|
||||
const data = await QueryGetAPI(FORUM_API_URL + 'manage/delete-comment', { comment: comment })
|
||||
const data = await QueryGetAPI(FORUM_API_URL + 'delete-comment', { comment: comment })
|
||||
if (data.code == 200) {
|
||||
message?.success('删除成功')
|
||||
return true
|
||||
@@ -354,6 +354,46 @@ export const useForumStore = defineStore('forum', () => {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
async function RestoreComment(comment: number) {
|
||||
try {
|
||||
isLoading.value = true
|
||||
const data = await QueryGetAPI(FORUM_API_URL + 'manage/restore-comment', { comment: comment })
|
||||
if (data.code == 200) {
|
||||
message?.success('恢复成功')
|
||||
return true
|
||||
} else {
|
||||
message?.error('恢复失败: ' + data.message)
|
||||
console.error('恢复失败: ' + data.message)
|
||||
return false
|
||||
}
|
||||
} catch (err) {
|
||||
message?.error('恢复失败: ' + err)
|
||||
console.error('恢复失败: ' + err)
|
||||
return false
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
async function RestoreTopic(topic: number) {
|
||||
try {
|
||||
isLoading.value = true
|
||||
const data = await QueryGetAPI(FORUM_API_URL + 'manage/restore-topic', { topic })
|
||||
if (data.code == 200) {
|
||||
message?.success('恢复成功')
|
||||
return true
|
||||
} else {
|
||||
message?.error('恢复失败: ' + data.message)
|
||||
console.error('恢复失败: ' + data.message)
|
||||
return false
|
||||
}
|
||||
} catch (err) {
|
||||
message?.error('恢复失败: ' + err)
|
||||
console.error('恢复失败: ' + err)
|
||||
return false
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
async function ConfirmApply(owner: number, id: number) {
|
||||
try {
|
||||
isLoading.value = true
|
||||
@@ -393,6 +433,8 @@ export const useForumStore = defineStore('forum', () => {
|
||||
DelComment,
|
||||
DelReply,
|
||||
ConfirmApply,
|
||||
RestoreComment,
|
||||
RestoreTopic,
|
||||
isLoading,
|
||||
isLikeLoading,
|
||||
replyingComment,
|
||||
|
||||
@@ -45,6 +45,10 @@ const showAgreement = ref(false)
|
||||
const create_Name = ref('')
|
||||
const create_Description = ref('')
|
||||
|
||||
const showAddAdminModal = ref(false)
|
||||
const inputUser = ref<UserBasicInfo>({} as UserBasicInfo)
|
||||
const addAdminName = ref()
|
||||
|
||||
const paginationSetting = { defaultPageSize: 20, showSizePicker: true, pageSizes: [20, 50, 100] }
|
||||
|
||||
async function createForum() {
|
||||
@@ -154,6 +158,46 @@ const banColumns: DataTableColumns<ForumUserModel> = [
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
const adminColumns: DataTableColumns<ForumUserModel> = [
|
||||
...defaultColumns,
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
render(row) {
|
||||
return h(
|
||||
NButton,
|
||||
{
|
||||
text: true,
|
||||
type: 'success',
|
||||
onClick: () =>
|
||||
useForum.ConfirmApply(currentForum.value.owner.id, row.id).then((success) => {
|
||||
if (success) message.success('操作成功')
|
||||
currentForum.value.applying = currentForum.value.applying.filter((u) => u.id != row.id)
|
||||
}),
|
||||
},
|
||||
{ default: () => '通过申请' },
|
||||
)
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
async function addAdmin() {
|
||||
if (!currentForum.value.id) return
|
||||
try {
|
||||
const data = await QueryPostAPI<ForumModel>(FORUM_API_URL + 'manage/add-admin', {
|
||||
forum: currentForum.value.id,
|
||||
user: addAdminName.value,
|
||||
})
|
||||
if (data.code == 200) {
|
||||
message.success('操作成功')
|
||||
} else {
|
||||
message.error('操作失败: ' + data.message)
|
||||
}
|
||||
} catch (err) {
|
||||
message.error('操作失败: ' + err)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -215,11 +259,12 @@ const banColumns: DataTableColumns<ForumUserModel> = [
|
||||
</NTabPane>
|
||||
<NTabPane tab="成员" name="member">
|
||||
<NDivider> 申请 </NDivider>
|
||||
<NDataTable
|
||||
:columns="applyingColumns"
|
||||
:data="currentForum.applying"
|
||||
:pagination="paginationSetting"
|
||||
/>
|
||||
<NDataTable :columns="applyingColumns" :data="currentForum.applying" :pagination="paginationSetting" />
|
||||
<NDivider> 管理员 </NDivider>
|
||||
<NFlex>
|
||||
<NButton @click="showAddAdminModal = true" size="small" type="primary"> 添加管理员 </NButton>
|
||||
</NFlex>
|
||||
<NDataTable :columns="adminColumns" :data="currentForum.admins" :pagination="paginationSetting" />
|
||||
<template v-if="currentForum.settings.requireApply">
|
||||
<NDivider> 成员 </NDivider>
|
||||
<NDataTable
|
||||
@@ -229,11 +274,7 @@ const banColumns: DataTableColumns<ForumUserModel> = [
|
||||
/>
|
||||
</template>
|
||||
<NDivider> 封禁用户 </NDivider>
|
||||
<NDataTable
|
||||
:columns="banColumns"
|
||||
:data="currentForum.blackList"
|
||||
:pagination="paginationSetting"
|
||||
/>
|
||||
<NDataTable :columns="banColumns" :data="currentForum.blackList" :pagination="paginationSetting" />
|
||||
</NTabPane>
|
||||
</NTabs>
|
||||
</NSpin>
|
||||
@@ -246,4 +287,8 @@ const banColumns: DataTableColumns<ForumUserModel> = [
|
||||
>
|
||||
<Agreement />
|
||||
</NModal>
|
||||
<NModal v-model:show="showAddAdminModal" preset="card" title="添加管理员" style="width: 600px; max-width: 90vw">
|
||||
<NInput v-model:value="addAdminName" placeholder="请输入用户名或VTsuruId" />
|
||||
<NButton @click="addAdmin" type="primary"> 添加 </NButton>
|
||||
</NModal>
|
||||
</template>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { useAccount } from '@/api/account'
|
||||
import { ForumCommentModel, ForumTopicModel } from '@/api/models/forum'
|
||||
import { ForumCommentModel, ForumModel, ForumTopicModel } from '@/api/models/forum'
|
||||
import { VTSURU_API_URL } from '@/data/constants'
|
||||
import { useForumStore } from '@/store/useForumStore'
|
||||
import { ArrowReply16Filled } from '@vicons/fluent'
|
||||
import { Heart, HeartOutline } from '@vicons/ionicons5'
|
||||
import { NAvatar, NButton, NCard, NDivider, NFlex, NIcon, NText, NTime, NTooltip } from 'naive-ui'
|
||||
import { ArrowHookUpLeft24Filled, ArrowReply16Filled, Delete24Filled } from '@vicons/fluent'
|
||||
import { Heart, HeartOutline, SyncCircleSharp } from '@vicons/ionicons5'
|
||||
import { NAvatar, NButton, NCard, NDivider, NFlex, NIcon, NPopconfirm, NTag, NText, NTime, NTooltip } from 'naive-ui'
|
||||
import ForumReplyItem from './ForumReplyItem.vue'
|
||||
import { computed } from 'vue'
|
||||
|
||||
@@ -20,6 +20,32 @@ const accountInfo = useAccount()
|
||||
const canOprate = computed(() => {
|
||||
return !props.topic.isLocked && accountInfo.value.id > 0
|
||||
})
|
||||
|
||||
const emits = defineEmits<{
|
||||
(e: 'delete', id: number): void
|
||||
}>()
|
||||
|
||||
function delComment(id: number) {
|
||||
useForum.DelComment(id).then((success) => {
|
||||
if (success) {
|
||||
emits('delete', id)
|
||||
}
|
||||
})
|
||||
}
|
||||
function restoreComment(id: number) {
|
||||
useForum.RestoreComment(id).then((success) => {
|
||||
if (success) {
|
||||
props.item.isDeleted = false
|
||||
}
|
||||
})
|
||||
}
|
||||
function delReply(id: number) {
|
||||
useForum.DelReply(id).then((success) => {
|
||||
if (success) {
|
||||
props.item.replies = props.item.replies.filter((reply) => reply.id !== id)
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -29,7 +55,8 @@ const canOprate = computed(() => {
|
||||
:img-props="{ referrerpolicy: 'no-referrer' }"
|
||||
/>
|
||||
<NFlex vertical style="flex: 1" :size="2">
|
||||
<NFlex>
|
||||
<NFlex align="center">
|
||||
<NTag v-if="item.isDeleted" type="warning" :bordered="false"> 已删除 </NTag>
|
||||
<NText>
|
||||
{{ item.user.name }}
|
||||
</NText>
|
||||
@@ -43,7 +70,22 @@ const canOprate = computed(() => {
|
||||
</NText>
|
||||
</NFlex>
|
||||
<div class="editor-content-view" v-html="item.content"></div>
|
||||
<NDivider style="margin: 0" />
|
||||
|
||||
<NCard v-if="item.replies.length > 0" size="small" style="margin-bottom: 10px">
|
||||
<NFlex vertical>
|
||||
<ForumReplyItem
|
||||
v-for="reply in item.replies"
|
||||
:key="reply.id"
|
||||
:item="reply"
|
||||
:comment="item"
|
||||
:topic="topic"
|
||||
showReplyButton
|
||||
:reply-to="reply.replyTo ? item.replies.find((r) => r.id === reply.replyTo) : undefined"
|
||||
:reply-to-id="reply.replyTo"
|
||||
@delete="delReply"
|
||||
/>
|
||||
</NFlex>
|
||||
</NCard>
|
||||
<NFlex>
|
||||
<NTooltip>
|
||||
<template #trigger>
|
||||
@@ -71,12 +113,7 @@ const canOprate = computed(() => {
|
||||
</NTooltip>
|
||||
<NTooltip>
|
||||
<template #trigger>
|
||||
<NButton
|
||||
size="small"
|
||||
@click="useForum.SetReplyingComment(item)"
|
||||
text
|
||||
:disabled="!canOprate"
|
||||
>
|
||||
<NButton size="small" @click="useForum.SetReplyingComment(item)" text :disabled="!canOprate">
|
||||
<template #icon>
|
||||
<NIcon :component="ArrowReply16Filled" />
|
||||
</template>
|
||||
@@ -85,20 +122,42 @@ const canOprate = computed(() => {
|
||||
</template>
|
||||
回复
|
||||
</NTooltip>
|
||||
</NFlex>
|
||||
<NCard v-if="item.replies.length > 0" size="small">
|
||||
<NFlex vertical>
|
||||
<ForumReplyItem
|
||||
v-for="reply in item.replies"
|
||||
:key="reply.id"
|
||||
:item="reply"
|
||||
:comment="item"
|
||||
:topic="topic"
|
||||
showReplyButton
|
||||
:reply-to="reply.replyTo ? item.replies.find((r) => r.id === reply.replyTo) : undefined"
|
||||
/>
|
||||
<NFlex style="flex: 1" justify="end">
|
||||
<NTooltip v-if="item.user.id === accountInfo.id || topic.isAdmin">
|
||||
<template #trigger>
|
||||
<NPopconfirm @positive-click="delComment(item.id)">
|
||||
<template #trigger>
|
||||
<NButton size="small" text :disabled="!canOprate">
|
||||
<template #icon>
|
||||
<NIcon
|
||||
:component="Delete24Filled"
|
||||
:color="item.isDeleted || topic.isAdmin ? '#dd484f' : '#7f7f7f'"
|
||||
/>
|
||||
</template>
|
||||
</NButton>
|
||||
</template>
|
||||
{{ item.isDeleted ? '确定完全删除这条评论吗? 这将无法恢复' : '确定删除这条评论吗' }}
|
||||
</NPopconfirm>
|
||||
</template>
|
||||
{{ item.isDeleted || topic.isAdmin ? '完全' : '' }}删除
|
||||
</NTooltip>
|
||||
<NTooltip v-if="item.isDeleted && topic.isAdmin">
|
||||
<template #trigger>
|
||||
<NPopconfirm @positive-click="restoreComment(item.id)">
|
||||
<template #trigger>
|
||||
<NButton size="small" text :disabled="!canOprate">
|
||||
<template #icon>
|
||||
<NIcon :component="SyncCircleSharp" color="#7f7f7f" />
|
||||
</template>
|
||||
</NButton>
|
||||
</template>
|
||||
要恢复这条评论吗?
|
||||
</NPopconfirm>
|
||||
</template>
|
||||
恢复
|
||||
</NTooltip>
|
||||
</NFlex>
|
||||
</NCard>
|
||||
</NFlex>
|
||||
</NFlex>
|
||||
</NFlex>
|
||||
</template>
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { useAccount } from '@/api/account'
|
||||
import { ForumModel, ForumTopicBaseModel } from '@/api/models/forum'
|
||||
import { useForumStore } from '@/store/useForumStore'
|
||||
import { ArrowReply24Filled, Chat24Regular, MoreVertical24Filled, Star24Filled } from '@vicons/fluent'
|
||||
import { ArrowReply24Filled, Chat24Regular, Delete24Filled, MoreVertical24Filled, Star24Filled } from '@vicons/fluent'
|
||||
import { SyncCircleSharp } from '@vicons/ionicons5'
|
||||
import { NButton, NDropdown, NFlex, NIcon, NTag, NText, NTime, NTooltip, useDialog } from 'naive-ui'
|
||||
import { h } from 'vue'
|
||||
|
||||
const props = defineProps<{
|
||||
item: ForumTopicBaseModel
|
||||
@@ -11,6 +14,7 @@ const props = defineProps<{
|
||||
|
||||
const useForum = useForumStore()
|
||||
const dialog = useDialog()
|
||||
const accountInfo = useAccount()
|
||||
|
||||
function onDropdownSelect(key: string) {
|
||||
switch (key) {
|
||||
@@ -31,6 +35,21 @@ function onDropdownSelect(key: string) {
|
||||
},
|
||||
})
|
||||
break
|
||||
case 'delete':
|
||||
dialog.warning({
|
||||
title: '问问',
|
||||
content: '确定要恢复这条话题吗?',
|
||||
positiveText: '确定',
|
||||
negativeText: '再想想',
|
||||
onPositiveClick: () => {
|
||||
useForum.RestoreTopic(props.item.id).then((success) => {
|
||||
if (success) {
|
||||
props.item.isDeleted = false
|
||||
}
|
||||
})
|
||||
},
|
||||
})
|
||||
break
|
||||
case 'top':
|
||||
dialog.info({
|
||||
title: '问',
|
||||
@@ -54,7 +73,8 @@ function onDropdownSelect(key: string) {
|
||||
<template>
|
||||
<NFlex align="center">
|
||||
<NFlex align="center" :wrap="false">
|
||||
<NTag v-if="item.isPinned" size="small" round>
|
||||
<NTag v-if="item.isDeleted" size="small" round :bordered="false"> 已删除 </NTag>
|
||||
<NTag v-if="item.isPinned" size="small" round :bordered="false">
|
||||
<NIcon :component="Star24Filled" color="#dba913" />
|
||||
</NTag>
|
||||
<NTag size="small" style="color: gray">
|
||||
@@ -63,7 +83,10 @@ function onDropdownSelect(key: string) {
|
||||
</template>
|
||||
{{ item.commentCount }}
|
||||
</NTag>
|
||||
<NText style="font-size: large">
|
||||
<NText
|
||||
:style="{ fontSize: 'large', color: item.user?.id == accountInfo?.id ? '#5f877d' : '' }"
|
||||
:depth="item.isDeleted ? 3 : 1"
|
||||
>
|
||||
{{ item.title }}
|
||||
</NText>
|
||||
</NFlex>
|
||||
@@ -84,8 +107,18 @@ function onDropdownSelect(key: string) {
|
||||
<NDropdown
|
||||
v-if="forum.isAdmin"
|
||||
:options="[
|
||||
{ label: '删除', key: 'delete' },
|
||||
{ label: item.isPinned ? '取消置顶' : '置顶', key: 'top' },
|
||||
{
|
||||
label: item.isPinned ? '取消置顶' : '置顶',
|
||||
key: 'top',
|
||||
icon: () => h(NIcon, { component: Star24Filled }),
|
||||
type: 'info',
|
||||
},
|
||||
{
|
||||
label: item.isDeleted ? '恢复' : '删除',
|
||||
key: item.isDeleted ? 'restore' : 'delete',
|
||||
icon: () => h(NIcon, { component: item.isDeleted ? SyncCircleSharp : Delete24Filled }),
|
||||
type: 'error',
|
||||
},
|
||||
]"
|
||||
trigger="hover"
|
||||
@select="onDropdownSelect"
|
||||
|
||||
@@ -3,13 +3,14 @@ import { getUserAvatarUrl } from '@/Utils'
|
||||
import { useAccount } from '@/api/account'
|
||||
import { ForumCommentModel, ForumReplyModel, ForumTopicModel } from '@/api/models/forum'
|
||||
import { useForumStore } from '@/store/useForumStore'
|
||||
import { ArrowReply16Filled } from '@vicons/fluent'
|
||||
import { NAvatar, NButton, NCard, NFlex, NIcon, NText, NTime, NTooltip } from 'naive-ui'
|
||||
import { ArrowReply16Filled, Delete24Filled } from '@vicons/fluent'
|
||||
import { NAvatar, NButton, NCard, NFlex, NIcon, NPopconfirm, NText, NTime, NTooltip } from 'naive-ui'
|
||||
import { computed } from 'vue'
|
||||
|
||||
const props = defineProps<{
|
||||
item: ForumReplyModel
|
||||
replyTo?: ForumReplyModel
|
||||
replyToId?: number
|
||||
comment: ForumCommentModel
|
||||
topic: ForumTopicModel
|
||||
showReplyButton?: boolean
|
||||
@@ -21,6 +22,9 @@ const accountInfo = useAccount()
|
||||
const canOprate = computed(() => {
|
||||
return !props.topic.isLocked && accountInfo.value.id > 0
|
||||
})
|
||||
const emits = defineEmits<{
|
||||
(e: 'delete', id: number): void
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -69,6 +73,29 @@ const canOprate = computed(() => {
|
||||
</template>
|
||||
回复这条回复
|
||||
</NTooltip>
|
||||
<NPopconfirm
|
||||
v-if="(item.user.id === accountInfo.id || topic.isAdmin) && showReplyButton"
|
||||
@positive-click="emits('delete', item.id)"
|
||||
>
|
||||
<template #trigger>
|
||||
<NTooltip v-if="showReplyButton">
|
||||
<template #trigger>
|
||||
<NButton
|
||||
size="tiny"
|
||||
round
|
||||
secondary
|
||||
:disabled="!canOprate"
|
||||
>
|
||||
<template #icon>
|
||||
<NIcon :component="Delete24Filled" />
|
||||
</template>
|
||||
</NButton>
|
||||
</template>
|
||||
删除
|
||||
</NTooltip>
|
||||
</template>
|
||||
确定删除这条回复吗
|
||||
</NPopconfirm>
|
||||
</NFlex>
|
||||
</NFlex>
|
||||
</template>
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
<script setup lang="ts">
|
||||
import { getUserAvatarUrl } from '@/Utils'
|
||||
import { UserInfo } from '@/api/api-models'
|
||||
import { PaginationResponse, UserInfo } from '@/api/api-models'
|
||||
import { ForumCommentModel, ForumCommentSortTypes, ForumTopicModel } from '@/api/models/forum'
|
||||
import '@/assets/forumContentStyle.css'
|
||||
import TurnstileVerify from '@/components/TurnstileVerify.vue'
|
||||
import VEditor from '@/components/VEditor.vue'
|
||||
import { VTSURU_API_URL } from '@/data/constants'
|
||||
import { useForumStore } from '@/store/useForumStore'
|
||||
import { ArrowCircleLeft12Filled, ArrowCircleLeft12Regular, Comment24Regular, Eye24Regular } from '@vicons/fluent'
|
||||
import { Heart, HeartOutline } from '@vicons/ionicons5'
|
||||
import {
|
||||
ArrowCircleLeft12Filled,
|
||||
ArrowCircleLeft12Regular,
|
||||
Comment24Regular,
|
||||
Delete24Filled,
|
||||
Eye24Regular,
|
||||
} from '@vicons/fluent'
|
||||
import { Heart, HeartOutline, SyncCircleSharp } from '@vicons/ionicons5'
|
||||
import {
|
||||
NAvatar,
|
||||
NAvatarGroup,
|
||||
@@ -25,6 +31,9 @@ import {
|
||||
NList,
|
||||
NListItem,
|
||||
NModal,
|
||||
NPagination,
|
||||
NPopconfirm,
|
||||
NTag,
|
||||
NText,
|
||||
NTime,
|
||||
NTooltip,
|
||||
@@ -35,6 +44,7 @@ import { useRoute } from 'vue-router'
|
||||
import ForumCommentItem from './ForumCommentItem.vue'
|
||||
import ForumReplyItem from './ForumReplyItem.vue'
|
||||
import { useAccount } from '@/api/account'
|
||||
import router from '@/router'
|
||||
|
||||
type PostCommentModel = {
|
||||
content: string
|
||||
@@ -69,9 +79,10 @@ const currentCommentContent = ref<PostCommentModel>({} as PostCommentModel)
|
||||
const currentReplyContent = ref<PostReplyModel>({} as PostReplyModel)
|
||||
|
||||
const topic = ref<ForumTopicModel>({ id: -1 } as ForumTopicModel)
|
||||
const comments = ref<ForumCommentModel[]>([])
|
||||
const comments = ref<PaginationResponse<ForumCommentModel[]>>()
|
||||
const ps = ref(20)
|
||||
const pn = ref(0)
|
||||
const total = ref(0)
|
||||
const sort = ref(ForumCommentSortTypes.Time)
|
||||
|
||||
const canOprate = computed(() => {
|
||||
@@ -89,7 +100,9 @@ async function postComment() {
|
||||
.PostComment(currentCommentContent.value, token.value)
|
||||
.then(async (comment) => {
|
||||
if (comment) {
|
||||
comments.value = (await useForum.GetComments(topic.value.id, pn.value, ps.value, sort.value)) ?? []
|
||||
setTimeout(async () => {
|
||||
refreshComments()
|
||||
}, 1000)
|
||||
currentCommentContent.value = {} as PostCommentModel
|
||||
showCommentModal.value = false
|
||||
}
|
||||
@@ -110,7 +123,7 @@ async function postReply() {
|
||||
.PostReply(currentReplyContent.value, token.value)
|
||||
.then(async (comment) => {
|
||||
if (comment) {
|
||||
comments.value = (await useForum.GetComments(topic.value.id, pn.value, ps.value, sort.value)) ?? []
|
||||
refreshComments()
|
||||
currentReplyContent.value = {} as PostReplyModel
|
||||
useForum.SetReplyingComment()
|
||||
}
|
||||
@@ -119,12 +132,38 @@ async function postReply() {
|
||||
turnstile.value?.reset()
|
||||
})
|
||||
}
|
||||
async function refreshComments() {
|
||||
comments.value = await useForum.GetComments(topic.value.id, pn.value, ps.value, sort.value)
|
||||
}
|
||||
function onDeleteComment(id: number) {
|
||||
if (comments.value) {
|
||||
comments.value.data = comments.value.data.filter((c) => c.id !== id)
|
||||
}
|
||||
}
|
||||
async function delTopic(topicId: number) {
|
||||
useForum.DelTopic(topicId).then((success) => {
|
||||
if (success) {
|
||||
setTimeout(() => {
|
||||
router.push({ name: 'user-forum', params: { id: userInfo?.name } })
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
async function restoreTopic(topicId: number) {
|
||||
useForum.RestoreTopic(topicId).then((success) => {
|
||||
if (success) {
|
||||
setTimeout(() => {
|
||||
topic.value.isDeleted = false
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
if (route.params.topicId) {
|
||||
topicId.value = route.params.topicId as unknown as number
|
||||
topic.value = (await useForum.GetTopicDetail(topicId.value)) ?? ({ id: -1 } as ForumTopicModel)
|
||||
comments.value = (await useForum.GetComments(topicId.value, pn.value, ps.value, sort.value)) ?? []
|
||||
refreshComments()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -136,11 +175,14 @@ onMounted(async () => {
|
||||
<NBackTop />
|
||||
<NBadge class="back-forum-badge" style="width: 100%; left: 0" type="info" :offset="[3, 3]">
|
||||
<NCard size="small">
|
||||
<NText style="font-size: large; font-weight: bold; text-align: center; width: 100%">
|
||||
<NEllipsis style="width: 100%">
|
||||
{{ topic.title }}
|
||||
</NEllipsis>
|
||||
</NText>
|
||||
<NFlex align="center" :wrap="false">
|
||||
<NTag v-if="topic.isDeleted" type="warning" :bordered="false"> 已删除 </NTag>
|
||||
<NText style="font-size: large; font-weight: bold; text-align: center; width: 100%">
|
||||
<NEllipsis style="width: 100%">
|
||||
{{ topic.title }}
|
||||
</NEllipsis>
|
||||
</NText>
|
||||
</NFlex>
|
||||
</NCard>
|
||||
<template #value>
|
||||
<NTooltip>
|
||||
@@ -232,6 +274,41 @@ onMounted(async () => {
|
||||
</template>
|
||||
评论
|
||||
</NTooltip>
|
||||
<NFlex style="flex: 1" justify="end">
|
||||
<NTooltip v-if="topic?.user?.id === accountInfo.id || topic.isAdmin">
|
||||
<template #trigger>
|
||||
<NPopconfirm @positive-click="delTopic(topic.id)">
|
||||
<template #trigger>
|
||||
<NButton size="small" text :disabled="!canOprate">
|
||||
<template #icon>
|
||||
<NIcon
|
||||
:component="Delete24Filled"
|
||||
:color="topic.isDeleted || topic.isAdmin ? '#dd484f' : '#7f7f7f'"
|
||||
/>
|
||||
</template>
|
||||
</NButton>
|
||||
</template>
|
||||
{{ topic.isDeleted ? '确定完全删除这个话题吗? 这将无法恢复' : '确定删除这个话题吗' }}
|
||||
</NPopconfirm>
|
||||
</template>
|
||||
{{ topic.isDeleted || topic.isAdmin ? '完全' : '' }}删除
|
||||
</NTooltip>
|
||||
<NTooltip v-if="topic.isDeleted && topic.isAdmin">
|
||||
<template #trigger>
|
||||
<NPopconfirm @positive-click="restoreTopic(topic.id)">
|
||||
<template #trigger>
|
||||
<NButton size="small" text :disabled="!canOprate">
|
||||
<template #icon>
|
||||
<NIcon :component="SyncCircleSharp" color="#7f7f7f" />
|
||||
</template>
|
||||
</NButton>
|
||||
</template>
|
||||
要恢复这个话题吗?
|
||||
</NPopconfirm>
|
||||
</template>
|
||||
恢复
|
||||
</NTooltip>
|
||||
</NFlex>
|
||||
</NFlex>
|
||||
</template>
|
||||
<div class="editor-content-view" v-html="topic.content"></div>
|
||||
@@ -239,12 +316,34 @@ onMounted(async () => {
|
||||
<NDivider>
|
||||
<NButton @click="showCommentModal = true" type="primary" :disabled="!canOprate">发送评论</NButton>
|
||||
</NDivider>
|
||||
<NEmpty v-if="comments.length === 0" description="暂无评论" />
|
||||
<NFlex align="center" justify="center">
|
||||
<NPagination
|
||||
v-if="comments && (comments?.data.length ?? 0) > 0"
|
||||
v-model:page="pn"
|
||||
:item-count="comments?.data.length ?? 0"
|
||||
:page-size="ps"
|
||||
show-quick-jumper
|
||||
@update:page="refreshComments"
|
||||
/>
|
||||
</NFlex>
|
||||
<br />
|
||||
<NEmpty v-if="!comments || comments.data.length === 0" description="暂无评论" />
|
||||
<NList v-else hoverable bordered size="small">
|
||||
<NListItem v-for="item in comments" :key="item.id">
|
||||
<ForumCommentItem :item="item" :topic="topic" />
|
||||
<NListItem v-for="item in comments.data" :key="item.id">
|
||||
<ForumCommentItem :item="item" :topic="topic" @delete="onDeleteComment" />
|
||||
</NListItem>
|
||||
</NList>
|
||||
<br />
|
||||
<NFlex v-if="(comments?.data.length ?? 0) > 5" align="center" justify="center">
|
||||
<NPagination
|
||||
v-if="comments && (comments?.data.length ?? 0) > 0"
|
||||
v-model:page="pn"
|
||||
:item-count="comments?.data.length ?? 0"
|
||||
:page-size="ps"
|
||||
show-quick-jumper
|
||||
@update:page="refreshComments"
|
||||
/>
|
||||
</NFlex>
|
||||
<NDivider />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user