mirror of
https://github.com/Megghy/vtsuru.live.git
synced 2025-12-07 02:46:55 +08:00
1012
This commit is contained in:
@@ -103,6 +103,7 @@ export interface LotteryUserInfo {
|
|||||||
location?: string
|
location?: string
|
||||||
isVIP?: boolean
|
isVIP?: boolean
|
||||||
card?: LotteryUserCardInfo
|
card?: LotteryUserCardInfo
|
||||||
|
visiable: boolean
|
||||||
}
|
}
|
||||||
export interface LotteryUserCardInfo {
|
export interface LotteryUserCardInfo {
|
||||||
name: string
|
name: string
|
||||||
|
|||||||
@@ -5,16 +5,14 @@ import { NotifactionInfo } from '@/api/api-models'
|
|||||||
import { useAccount } from '@/api/account'
|
import { useAccount } from '@/api/account'
|
||||||
|
|
||||||
const account = useAccount()
|
const account = useAccount()
|
||||||
const { data, loading, run } = useRequest(
|
const { data, run } = useRequest(get, {
|
||||||
() => {
|
|
||||||
return QueryGetAPI<NotifactionInfo>(NOTIFACTION_API_URL + 'get')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
errorRetryCount: 5,
|
errorRetryCount: 5,
|
||||||
pollingInterval: 5000,
|
pollingInterval: 5000,
|
||||||
pollingWhenHidden: false,
|
pollingWhenHidden: false,
|
||||||
|
})
|
||||||
|
function get() {
|
||||||
|
return QueryGetAPI<NotifactionInfo>(NOTIFACTION_API_URL + 'get')
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
export const notifactions = () => data
|
export const notifactions = () => data
|
||||||
export const GetNotifactions = () => {
|
export const GetNotifactions = () => {
|
||||||
|
|||||||
@@ -41,6 +41,20 @@ const menuOptions = [
|
|||||||
key: 'manage-questionBox',
|
key: 'manage-questionBox',
|
||||||
icon: renderIcon(BookOutline),
|
icon: renderIcon(BookOutline),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: () =>
|
||||||
|
h(
|
||||||
|
RouterLink,
|
||||||
|
{
|
||||||
|
to: {
|
||||||
|
name: 'manage-lottery',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ default: () => '动态抽奖' }
|
||||||
|
),
|
||||||
|
key: 'manage-lottery',
|
||||||
|
icon: renderIcon(BookOutline),
|
||||||
|
},
|
||||||
]
|
]
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,33 @@
|
|||||||
import { LotteryUserInfo } from '@/api/api-models'
|
import { LotteryUserInfo } from '@/api/api-models'
|
||||||
import { QueryGetAPI } from '@/api/query'
|
import { QueryGetAPI } from '@/api/query'
|
||||||
import { LOTTERY_API_URL, TURNSTILE_KEY } from '@/data/constants'
|
import { LOTTERY_API_URL, TURNSTILE_KEY } from '@/data/constants'
|
||||||
import { NButton, NTabPane, NTabs, useMessage } from 'naive-ui'
|
import { useLocalStorage } from '@vueuse/core'
|
||||||
import { ref } from 'vue'
|
import { randomInt } from 'crypto'
|
||||||
|
import { List } from 'linqts'
|
||||||
|
import {
|
||||||
|
NAvatar,
|
||||||
|
NButton,
|
||||||
|
NCard,
|
||||||
|
NCheckbox,
|
||||||
|
NCollapseTransition,
|
||||||
|
NCountdown,
|
||||||
|
NDivider,
|
||||||
|
NGrid,
|
||||||
|
NGridItem,
|
||||||
|
NInput,
|
||||||
|
NInputGroup,
|
||||||
|
NInputGroupLabel,
|
||||||
|
NInputNumber,
|
||||||
|
NRadioButton,
|
||||||
|
NRadioGroup,
|
||||||
|
NSpace,
|
||||||
|
NTabPane,
|
||||||
|
NTabs,
|
||||||
|
NTag,
|
||||||
|
useMessage,
|
||||||
|
} from 'naive-ui'
|
||||||
|
import { breadcrumbLight } from 'naive-ui/es/breadcrumb/styles'
|
||||||
|
import { computed, ref } from 'vue'
|
||||||
import VueTurnstile from 'vue-turnstile'
|
import VueTurnstile from 'vue-turnstile'
|
||||||
|
|
||||||
interface TempLotteryResponseModel {
|
interface TempLotteryResponseModel {
|
||||||
@@ -11,23 +36,91 @@ interface TempLotteryResponseModel {
|
|||||||
createTime: number
|
createTime: number
|
||||||
total: number
|
total: number
|
||||||
}
|
}
|
||||||
|
interface LotteryOption {
|
||||||
|
resultCount: number
|
||||||
|
lotteryType: 'single' | 'half'
|
||||||
|
needVIP: boolean
|
||||||
|
}
|
||||||
|
|
||||||
const message = useMessage()
|
const message = useMessage()
|
||||||
const token = ref()
|
const token = ref()
|
||||||
const turnstile = ref()
|
const turnstile = ref()
|
||||||
|
const defaultOption = {
|
||||||
|
resultCount: 1,
|
||||||
|
lotteryType: 'single',
|
||||||
|
needVIP: false,
|
||||||
|
} as LotteryOption
|
||||||
|
const lotteryOption = useLocalStorage('Settings.LotteryOption', defaultOption)
|
||||||
|
|
||||||
|
const isLoading = ref(false)
|
||||||
|
const isLottering = ref(false)
|
||||||
|
|
||||||
|
const inputDynamic = ref<string>()
|
||||||
|
const inputDynamicId = computed(() => {
|
||||||
|
try {
|
||||||
|
var id = BigInt(inputDynamic.value ?? '')
|
||||||
|
return id
|
||||||
|
} catch {
|
||||||
|
try {
|
||||||
|
const url = new URL(inputDynamic.value ?? '')
|
||||||
|
if (url.host.endsWith('bilibili.com')) {
|
||||||
|
const sp = url.pathname.split('/')
|
||||||
|
const id = BigInt(sp.length > 1 ? sp[sp.length - 1] : sp[0])
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
const isCommentCountDown = ref(true)
|
||||||
|
const originCommentUsers = ref<TempLotteryResponseModel>()
|
||||||
|
const originForwardUsers = ref<TempLotteryResponseModel>()
|
||||||
|
const currentType = ref<'comment' | 'forward'>('comment')
|
||||||
|
|
||||||
const commentUsers = ref<TempLotteryResponseModel>()
|
const commentUsers = ref<TempLotteryResponseModel>()
|
||||||
|
const forwardUsers = ref<TempLotteryResponseModel>()
|
||||||
|
|
||||||
|
const currentUsers = computed(() => {
|
||||||
|
switch (currentType.value) {
|
||||||
|
case 'comment': {
|
||||||
|
return commentUsers.value
|
||||||
|
}
|
||||||
|
case 'forward': {
|
||||||
|
return forwardUsers.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
})
|
||||||
|
|
||||||
|
async function onGet() {
|
||||||
|
switch (currentType.value) {
|
||||||
|
case 'comment': {
|
||||||
|
await getCommentsUsers()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'forward': {
|
||||||
|
await getForwardUsers()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
currentUsers.value?.users.forEach((u) => (u.visiable = true))
|
||||||
|
}
|
||||||
async function getCommentsUsers() {
|
async function getCommentsUsers() {
|
||||||
|
isLoading.value = true
|
||||||
await QueryGetAPI<TempLotteryResponseModel>(
|
await QueryGetAPI<TempLotteryResponseModel>(
|
||||||
LOTTERY_API_URL + 'comments',
|
LOTTERY_API_URL + 'comments',
|
||||||
{
|
{
|
||||||
id: 803541974225256452n,
|
id: inputDynamicId.value,
|
||||||
},
|
},
|
||||||
[['Turnstile', token.value]]
|
[['Turnstile', token.value]]
|
||||||
)
|
)
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
if (data.code == 200) {
|
if (data.code == 200) {
|
||||||
|
originCommentUsers.value = data.data
|
||||||
commentUsers.value = data.data
|
commentUsers.value = data.data
|
||||||
|
isCommentCountDown.value = false
|
||||||
} else {
|
} else {
|
||||||
message.error('获取用户失败: ' + data.message)
|
message.error('获取用户失败: ' + data.message)
|
||||||
}
|
}
|
||||||
@@ -38,12 +131,135 @@ async function getCommentsUsers() {
|
|||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
turnstile.value?.reset()
|
turnstile.value?.reset()
|
||||||
|
isLoading.value = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
async function getForwardUsers() {
|
||||||
|
isLoading.value = true
|
||||||
|
await QueryGetAPI<TempLotteryResponseModel>(
|
||||||
|
LOTTERY_API_URL + 'forward',
|
||||||
|
{
|
||||||
|
id: inputDynamicId.value,
|
||||||
|
},
|
||||||
|
[['Turnstile', token.value]]
|
||||||
|
)
|
||||||
|
.then((data) => {
|
||||||
|
if (data.code == 200) {
|
||||||
|
originForwardUsers.value = data.data
|
||||||
|
forwardUsers.value = data.data
|
||||||
|
isCommentCountDown.value = false
|
||||||
|
} else {
|
||||||
|
message.error('获取用户失败: ' + data.message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err)
|
||||||
|
message.error('获取失败')
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
turnstile.value?.reset()
|
||||||
|
isLoading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function getRandomInt(max: number) {
|
||||||
|
return Math.floor(Math.random() * max)
|
||||||
|
}
|
||||||
|
function startLottery() {
|
||||||
|
if (!isLottering.value && currentUsers.value) {
|
||||||
|
isLottering.value = true
|
||||||
|
try {
|
||||||
|
const data = currentUsers.value
|
||||||
|
const users = data.users.filter((u) => {
|
||||||
|
if (lotteryOption.value.needVIP) {
|
||||||
|
return u.isVIP == true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
switch (lotteryOption.value.lotteryType) {
|
||||||
|
case 'single': {
|
||||||
|
console.log('开始抽取单个用户')
|
||||||
|
const removed = [] as number[]
|
||||||
|
removeSingleUser()
|
||||||
|
function removeSingleUser() {
|
||||||
|
if (data.users.length > lotteryOption.value.resultCount) {
|
||||||
|
console.log(`[${data.users.length}] 移除` + data.users.splice(getRandomInt(data.users.length), 1)[0].name)
|
||||||
|
setTimeout(() => {
|
||||||
|
removeSingleUser()
|
||||||
|
}, 500)
|
||||||
|
} else {
|
||||||
|
isLottering.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
message.error('发生错误')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<NTabs>
|
<NCard size="medium" embedded title="抽奖">
|
||||||
<NTabPane name="dynamic" tab="动态抽奖"> <NButton @click="getCommentsUsers" :loading="!token"> 11 </NButton> </NTabPane>
|
<NInput v-model:value="inputDynamic" placeholder="动态链接或直接输入动态Id" :status="inputDynamicId ? 'success' : 'warning'" />
|
||||||
</NTabs>
|
<NDivider style="margin: 10px 0 10px 0" />
|
||||||
|
<NCard size="small" embedded title="选项">
|
||||||
|
<template #header-extra>
|
||||||
|
<NButton size="small" secondary @click="lotteryOption = defaultOption"> 恢复默认 </NButton>
|
||||||
|
</template>
|
||||||
|
<NSpace justify="center">
|
||||||
|
<NRadioGroup v-model:value="currentType" name="用户类型">
|
||||||
|
<NRadioButton value="comment"> 评论 </NRadioButton>
|
||||||
|
<NRadioButton value="forward"> 转发 </NRadioButton>
|
||||||
|
</NRadioGroup>
|
||||||
|
</NSpace>
|
||||||
|
<NSpace align="center">
|
||||||
|
<NInputGroup style="max-width: 200px">
|
||||||
|
<NInputGroupLabel> 抽选人数 </NInputGroupLabel>
|
||||||
|
<NInputNumber v-model:value="lotteryOption.resultCount" placeholder="" min="1" />
|
||||||
|
</NInputGroup>
|
||||||
|
<NCheckbox> 是否大会员 </NCheckbox>
|
||||||
|
<NRadioGroup v-model:value="lotteryOption.lotteryType" name="抽取类型">
|
||||||
|
<NRadioButton value="single"> 单个 </NRadioButton>
|
||||||
|
<NRadioButton value="half"> 减半 </NRadioButton>
|
||||||
|
</NRadioGroup>
|
||||||
|
</NSpace>
|
||||||
|
</NCard>
|
||||||
|
<NDivider style="margin: 10px 0 10px 0" />
|
||||||
|
<NSpace justify="center" align="center">
|
||||||
|
<NButton :disabled="!inputDynamicId || !isCommentCountDown || !token || isLottering" :loading="!token || isLoading" @click="onGet" type="primary"> 加载用户 </NButton>
|
||||||
|
<NCountdown v-if="!isCommentCountDown" :duration="(currentUsers?.createTime ?? -1) + 60000 - Date.now()" @finish="isCommentCountDown = true" />
|
||||||
|
</NSpace>
|
||||||
|
<NDivider style="margin: 10px 0 10px 0" />
|
||||||
|
<template v-if="currentUsers">
|
||||||
|
<NSpace justify="space-between">
|
||||||
|
<span> 共 {{ currentUsers.users.length }} 人 </span>
|
||||||
|
</NSpace>
|
||||||
|
<NSpace justify="center">
|
||||||
|
<NButton type="primary" @click="startLottery" secondary :loading="isLottering"> 开始抽取 </NButton>
|
||||||
|
</NSpace>
|
||||||
|
<NDivider style="margin: 10px 0 10px 0" />
|
||||||
|
<NGrid cols="1 400:2 700:3 800:4" :x-gap="12" :y-gap="8">
|
||||||
|
<NGridItem v-for="item in currentUsers.users" v-bind:key="item.uId" :span="item.visiable ? 1 : 0">
|
||||||
|
<NCard size="small" :title="item.name">
|
||||||
|
<template #header>
|
||||||
|
<NSpace align="center">
|
||||||
|
<NAvatar round lazy :src="item.avatar + '@64w_64h'" :img-props="{ referrerpolicy: 'no-referrer' }" />
|
||||||
|
<NTag v-if="item.level" size="tiny">
|
||||||
|
{{ item.level }}
|
||||||
|
</NTag>
|
||||||
|
{{ item.name }}
|
||||||
|
|
||||||
|
<NTag v-if="item.card" size="tiny">
|
||||||
|
{{ item.card.level }}
|
||||||
|
{{ item.card.name }}
|
||||||
|
</NTag>
|
||||||
|
</NSpace>
|
||||||
|
</template>
|
||||||
|
</NCard>
|
||||||
|
</NGridItem>
|
||||||
|
</NGrid>
|
||||||
|
</template>
|
||||||
|
</NCard>
|
||||||
<VueTurnstile ref="turnstile" :site-key="TURNSTILE_KEY" v-model="token" theme="auto" style="text-align: center" />
|
<VueTurnstile ref="turnstile" :site-key="TURNSTILE_KEY" v-model="token" theme="auto" style="text-align: center" />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { QAInfo } from '@/api/api-models'
|
import { QAInfo } from '@/api/api-models'
|
||||||
import { QueryGetAPI, QueryPostAPI } from '@/api/query'
|
import { QueryGetAPI, QueryPostAPI } from '@/api/query'
|
||||||
import { BASE_API, QUESTION_API_URL } from '@/data/constants'
|
import { ACCOUNT_API_URL, BASE_API, QUESTION_API_URL } from '@/data/constants'
|
||||||
import { Heart, HeartOutline } from '@vicons/ionicons5'
|
import { Heart, HeartOutline } from '@vicons/ionicons5'
|
||||||
import { NButton, NCard, NDivider, NIcon, NImage, NInput, NList, NListItem, NModal, NSpace, NSwitch, NTabPane, NTabs, NTag, NText, NTime, NTooltip, useMessage } from 'naive-ui'
|
import { NButton, NCard, NDivider, NIcon, NImage, NInput, NList, NListItem, NModal, NSpace, NSwitch, NTabPane, NTabs, NTag, NText, NTime, NTooltip, useMessage } from 'naive-ui'
|
||||||
import { computed, onMounted, ref } from 'vue'
|
import { computed, onMounted, ref } from 'vue'
|
||||||
@@ -120,6 +120,34 @@ async function favorite(question: QAInfo, fav: boolean) {
|
|||||||
message.error('修改失败')
|
message.error('修改失败')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
async function blacklist(question: QAInfo) {
|
||||||
|
await QueryGetAPI(ACCOUNT_API_URL + 'black-list/add', {
|
||||||
|
id: question.sender.id,
|
||||||
|
})
|
||||||
|
.then(async (data) => {
|
||||||
|
if (data.code == 200) {
|
||||||
|
await QueryGetAPI(QUESTION_API_URL + 'del', {
|
||||||
|
id: question.id,
|
||||||
|
})
|
||||||
|
.then((data) => {
|
||||||
|
if (data.code == 200) {
|
||||||
|
message.success('已拉黑 ' + question.sender.name)
|
||||||
|
} else {
|
||||||
|
message.error('修改失败: ' + data.message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
message.error('拉黑失败: ' + data.message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err)
|
||||||
|
message.error('拉黑失败')
|
||||||
|
})
|
||||||
|
}
|
||||||
let isRevieveGetted = false
|
let isRevieveGetted = false
|
||||||
let isSendGetted = false
|
let isSendGetted = false
|
||||||
async function onTabChange(value: string) {
|
async function onTabChange(value: string) {
|
||||||
|
|||||||
Reference in New Issue
Block a user