mirror of
https://github.com/Megghy/vtsuru.live.git
synced 2025-12-07 02:46:55 +08:00
add not connect alert
This commit is contained in:
@@ -6,7 +6,9 @@
|
|||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta http-equiv="Permissions-Policy" content="interest-cohort=()">
|
<meta http-equiv="Permissions-Policy" content="interest-cohort=()">
|
||||||
<title>vtsuru.live</title>
|
<title>vtsuru.live</title>
|
||||||
<meta name="description" content="主包工具站" />
|
<meta name="description" content="为主播提供便利功能的网站" />
|
||||||
|
|
||||||
|
<script async src="https://umami.vtsuru.live/script.js" data-website-id="05567214-d234-4076-9228-e4d69e3d202f"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -47,6 +47,9 @@ function refreshCookie() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
export async function SaveAccountSettings() {
|
||||||
|
return await QueryPostAPI(ACCOUNT_API_URL + 'update-setting', ACCOUNT.value?.settings)
|
||||||
|
}
|
||||||
export function useAccount() {
|
export function useAccount() {
|
||||||
return ACCOUNT
|
return ACCOUNT
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,16 +60,38 @@ export interface Setting_QuestionBox {
|
|||||||
export interface UserSetting {
|
export interface UserSetting {
|
||||||
sendEmail: Setting_SendEmail
|
sendEmail: Setting_SendEmail
|
||||||
questionBox: Setting_QuestionBox
|
questionBox: Setting_QuestionBox
|
||||||
|
songRequest: Setting_SongRequest
|
||||||
enableFunctions: FunctionTypes[]
|
enableFunctions: FunctionTypes[]
|
||||||
|
|
||||||
indexTemplate: string | null,
|
indexTemplate: string | null,
|
||||||
songListTemplate: string | null
|
songListTemplate: string | null
|
||||||
scheduleTemplate: string | null
|
scheduleTemplate: string | null
|
||||||
}
|
}
|
||||||
|
export interface Setting_SongRequest {
|
||||||
|
orderPrefix: string
|
||||||
|
onlyAllowSongList: boolean
|
||||||
|
queueMaxSize: number
|
||||||
|
allowAllDanmaku: boolean
|
||||||
|
allowFromWeb: boolean
|
||||||
|
needWearFanMedal: boolean
|
||||||
|
needJianzhang: boolean
|
||||||
|
needTidu: boolean
|
||||||
|
needZongdu: boolean
|
||||||
|
allowSC: boolean
|
||||||
|
sCIgnoreLimit: boolean
|
||||||
|
sCMinPrice: number
|
||||||
|
fanMedalMinLevel: number
|
||||||
|
allowReorderSong: boolean
|
||||||
|
cooldownSecond: number
|
||||||
|
zongduCooldownSecond: number
|
||||||
|
tiduCooldownSecond: number
|
||||||
|
jianzhangCooldownSecond: number
|
||||||
|
}
|
||||||
export enum FunctionTypes {
|
export enum FunctionTypes {
|
||||||
SongList,
|
SongList,
|
||||||
QuestionBox,
|
QuestionBox,
|
||||||
Schedule,
|
Schedule,
|
||||||
|
SongRequest,
|
||||||
}
|
}
|
||||||
export interface SongAuthorInfo {
|
export interface SongAuthorInfo {
|
||||||
name: string
|
name: string
|
||||||
@@ -257,6 +279,7 @@ export interface UpdateLiveLotteryUsersModel {
|
|||||||
type: OpenLiveLotteryType
|
type: OpenLiveLotteryType
|
||||||
}
|
}
|
||||||
export interface SongRequestInfo {
|
export interface SongRequestInfo {
|
||||||
|
id: number
|
||||||
songName: string
|
songName: string
|
||||||
song?: SongsInfo
|
song?: SongsInfo
|
||||||
status: SongRequestStatus
|
status: SongRequestStatus
|
||||||
@@ -267,11 +290,11 @@ export interface SongRequestInfo {
|
|||||||
}
|
}
|
||||||
export interface SongRequestUserInfo {
|
export interface SongRequestUserInfo {
|
||||||
name: string
|
name: string
|
||||||
uId: number
|
uid: number
|
||||||
guardLevel: number
|
guard_level: number
|
||||||
fansMedalLevel: number
|
fans_medal_level: number
|
||||||
fansMedalName: string
|
fans_medal_name: string
|
||||||
fansMedalWearingStatus: boolean
|
fans_medal_wearing_status: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum SongRequestFrom {
|
export enum SongRequestFrom {
|
||||||
@@ -287,3 +310,23 @@ export enum SongRequestStatus {
|
|||||||
Finish,
|
Finish,
|
||||||
Cancel,
|
Cancel,
|
||||||
}
|
}
|
||||||
|
export interface EventModel {
|
||||||
|
type: EventDataTypes
|
||||||
|
name: string
|
||||||
|
avatar: string
|
||||||
|
uid: number
|
||||||
|
msg: string
|
||||||
|
time: number
|
||||||
|
num: number
|
||||||
|
price: number
|
||||||
|
guard_level: number
|
||||||
|
fans_medal_level: number
|
||||||
|
fans_medal_name: string
|
||||||
|
fans_medal_wearing_status: boolean
|
||||||
|
}
|
||||||
|
export enum EventDataTypes {
|
||||||
|
Guard,
|
||||||
|
SC,
|
||||||
|
Gift,
|
||||||
|
Message,
|
||||||
|
}
|
||||||
@@ -23,6 +23,8 @@ export const HISTORY_API_URL = `${BASE_API}history/`
|
|||||||
export const SCHEDULE_API_URL = `${BASE_API}schedule/`
|
export const SCHEDULE_API_URL = `${BASE_API}schedule/`
|
||||||
export const VIDEO_COLLECT_API_URL = `${BASE_API}video-collect/`
|
export const VIDEO_COLLECT_API_URL = `${BASE_API}video-collect/`
|
||||||
export const OPEN_LIVE_API_URL = `${BASE_API}open-live/`
|
export const OPEN_LIVE_API_URL = `${BASE_API}open-live/`
|
||||||
|
export const SONG_REQUEST_API_URL = `${BASE_API}song-request/`
|
||||||
|
export const EVENT_API_URL = `${BASE_API}event/`
|
||||||
|
|
||||||
export const ScheduleTemplateMap = {
|
export const ScheduleTemplateMap = {
|
||||||
'': { name: '默认', compoent: defineAsyncComponent(() => import('@/views/view/scheduleTemplate/DefaultScheduleTemplate.vue')) },
|
'': { name: '默认', compoent: defineAsyncComponent(() => import('@/views/view/scheduleTemplate/DefaultScheduleTemplate.vue')) },
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { QueryGetAPI } from '@/api/query'
|
import { QueryGetAPI } from '@/api/query'
|
||||||
import { useRequest } from 'vue-request'
|
import { useRequest } from 'vue-request'
|
||||||
import { NOTIFACTION_API_URL, isBackendUsable } from './constants'
|
import { NOTIFACTION_API_URL, SONG_REQUEST_API_URL, isBackendUsable } from './constants'
|
||||||
import { NotifactionInfo } from '@/api/api-models'
|
import { NotifactionInfo } from '@/api/api-models'
|
||||||
import { useAccount } from '@/api/account'
|
import { useAccount } from '@/api/account'
|
||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
@@ -10,7 +10,7 @@ const n = ref<NotifactionInfo>()
|
|||||||
let isLoading = false
|
let isLoading = false
|
||||||
function get() {
|
function get() {
|
||||||
if (isLoading) return
|
if (isLoading) return
|
||||||
QueryGetAPI<NotifactionInfo>(NOTIFACTION_API_URL + 'get')
|
QueryGetAPI<NotifactionInfo>(SONG_REQUEST_API_URL + 'get-active')
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
if (data.code == 200) {
|
if (data.code == 200) {
|
||||||
n.value = data.data
|
n.value = data.data
|
||||||
|
|||||||
@@ -29,8 +29,9 @@ import {
|
|||||||
useMessage,
|
useMessage,
|
||||||
NDivider,
|
NDivider,
|
||||||
NTag,
|
NTag,
|
||||||
|
NAlert,
|
||||||
} from 'naive-ui'
|
} from 'naive-ui'
|
||||||
import { ref, onMounted, h } from 'vue'
|
import { ref, onMounted, h, onUnmounted } from 'vue'
|
||||||
import { RouterLink, useRoute } from 'vue-router'
|
import { RouterLink, useRoute } from 'vue-router'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -89,6 +90,9 @@ onMounted(async () => {
|
|||||||
message.error('你不是从幻星平台访问此页面, 或未提供对应参数, 无法使用此功能')
|
message.error('你不是从幻星平台访问此页面, 或未提供对应参数, 无法使用此功能')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
onUnmounted(() => {
|
||||||
|
client.value?.Stop()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -108,7 +112,7 @@ onMounted(async () => {
|
|||||||
<NPageHeader :subtitle="($route.meta.title as string) ?? ''">
|
<NPageHeader :subtitle="($route.meta.title as string) ?? ''">
|
||||||
<template #extra>
|
<template #extra>
|
||||||
<NSpace align="center">
|
<NSpace align="center">
|
||||||
<NTag :type="client ? 'success' : 'warning'"> {{ client ? `已连接 | ${client.roomAuthInfo.value?.anchor_info.uname}` : '未连接' }} </NTag>
|
<NTag :type="client?.roomAuthInfo.value ? 'success' : 'warning'"> {{ client?.roomAuthInfo.value ? `已连接 | ${client.roomAuthInfo.value?.anchor_info.uname}` : '未连接' }} </NTag>
|
||||||
<NSwitch :default-value="!isDarkMode()" @update:value="(value: string & number & boolean) => themeType = value ? ThemeType.Light : ThemeType.Dark">
|
<NSwitch :default-value="!isDarkMode()" @update:value="(value: string & number & boolean) => themeType = value ? ThemeType.Light : ThemeType.Dark">
|
||||||
<template #checked>
|
<template #checked>
|
||||||
<NIcon :component="Sunny" />
|
<NIcon :component="Sunny" />
|
||||||
@@ -152,14 +156,16 @@ onMounted(async () => {
|
|||||||
</NText>
|
</NText>
|
||||||
</NSpace>
|
</NSpace>
|
||||||
</NLayoutSider>
|
</NLayoutSider>
|
||||||
<NLayoutContent yle="height: 100%" :native-scrollbar="false">
|
<NLayoutContent style="height: 100%; padding: 10px;" :native-scrollbar="false">
|
||||||
<RouterView v-if="client?.roomAuthInfo" v-slot="{ Component }">
|
<RouterView v-if="client?.roomAuthInfo.value" v-slot="{ Component }">
|
||||||
<KeepAlive>
|
<KeepAlive>
|
||||||
<component :is="Component" :room-info="client?.roomAuthInfo" :client="client" :code="authInfo.Code" />
|
<component :is="Component" :room-info="client?.roomAuthInfo" :client="client" :code="authInfo.Code" />
|
||||||
</KeepAlive>
|
</KeepAlive>
|
||||||
</RouterView>
|
</RouterView>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<NSpin show />
|
<NAlert type="info" title="正在请求弹幕客户端认证信息...">
|
||||||
|
<NSpin show />
|
||||||
|
</NAlert>
|
||||||
</template>
|
</template>
|
||||||
<NBackTop />
|
<NBackTop />
|
||||||
</NLayoutContent>
|
</NLayoutContent>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useAccount } from '@/api/account'
|
import { SaveAccountSettings, useAccount } from '@/api/account'
|
||||||
import { NButton, NCard, NCheckbox, NCheckboxGroup, NDivider, NForm, NModal, NSelect, NSpace, NSpin, NSwitch, NTabPane, NTabs, SelectOption, useMessage } from 'naive-ui'
|
import { NButton, NCard, NCheckbox, NCheckboxGroup, NDivider, NForm, NModal, NSelect, NSpace, NSpin, NSwitch, NTabPane, NTabs, SelectOption, useMessage } from 'naive-ui'
|
||||||
import { Ref, computed, h, onMounted, ref, defineAsyncComponent, onActivated } from 'vue'
|
import { Ref, computed, h, onMounted, ref, defineAsyncComponent, onActivated } from 'vue'
|
||||||
import { FunctionTypes, ScheduleWeekInfo, SongFrom, SongLanguage, SongsInfo } from '@/api/api-models'
|
import { FunctionTypes, ScheduleWeekInfo, SongFrom, SongLanguage, SongsInfo } from '@/api/api-models'
|
||||||
@@ -216,10 +216,10 @@ async function SaveComboGroupSetting(value: (string | number)[], meta: { actionT
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
async function SaveComboSetting() {
|
async function SaveComboSetting() {
|
||||||
|
isSaving.value = true
|
||||||
if (accountInfo.value) {
|
if (accountInfo.value) {
|
||||||
isSaving.value = true
|
|
||||||
//UpdateEnableFunction(meta.value as FunctionTypes, meta.actionType == 'check')
|
//UpdateEnableFunction(meta.value as FunctionTypes, meta.actionType == 'check')
|
||||||
await QueryPostAPI(ACCOUNT_API_URL + 'update-setting', accountInfo.value?.settings)
|
await SaveAccountSettings()
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
if (data.code == 200) {
|
if (data.code == 200) {
|
||||||
message.success('已保存')
|
message.success('已保存')
|
||||||
|
|||||||
@@ -1,46 +1,245 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useAccount } from '@/api/account'
|
import { SaveAccountSettings, useAccount } from '@/api/account'
|
||||||
import { SongRequestInfo } from '@/api/api-models'
|
import { EventDataTypes, EventModel, FunctionTypes, OpenLiveInfo, Setting_SongRequest, SongRequestFrom, SongRequestInfo, SongRequestStatus, SongRequestUserInfo } from '@/api/api-models'
|
||||||
|
import { QueryGetAPI, QueryPostAPI, QueryPostAPIWithParams } from '@/api/query'
|
||||||
import DanmakuClient, { AuthInfo, DanmakuInfo, RoomAuthInfo, SCInfo } from '@/data/DanmakuClient'
|
import DanmakuClient, { AuthInfo, DanmakuInfo, RoomAuthInfo, SCInfo } from '@/data/DanmakuClient'
|
||||||
import { NList, NTabPane, NTabs, useMessage } from 'naive-ui'
|
import { OPEN_LIVE_API_URL, SONG_REQUEST_API_URL } from '@/data/constants'
|
||||||
import { onMounted, onUnmounted, ref } from 'vue'
|
import { Mic24Filled, PeopleQueue24Filled } from '@vicons/fluent'
|
||||||
|
import { useStorage } from '@vueuse/core'
|
||||||
|
import { number } from 'echarts'
|
||||||
|
import { NAlert, NButton, NCard, NDivider, NIcon, NInput, NInputGroup, NList, NListItem, NSpace, NSwitch, NTabPane, NTabs, NTag, NText, NTooltip, useMessage } from 'naive-ui'
|
||||||
|
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
|
|
||||||
|
const defaultSettings = {
|
||||||
|
orderPrefix: '点歌',
|
||||||
|
onlyAllowSongList: false,
|
||||||
|
queueMaxSize: 10,
|
||||||
|
allowAllDanmaku: false,
|
||||||
|
allowFromWeb: true,
|
||||||
|
needWearFanMedal: false,
|
||||||
|
needJianzhang: false,
|
||||||
|
needTidu: false,
|
||||||
|
needZongdu: false,
|
||||||
|
allowSC: true,
|
||||||
|
sCIgnoreLimit: true,
|
||||||
|
sCMinPrice: 30,
|
||||||
|
fanMedalMinLevel: 0,
|
||||||
|
allowReorderSong: false,
|
||||||
|
cooldownSecond: 1200,
|
||||||
|
zongduCooldownSecond: 300,
|
||||||
|
tiduCooldownSecond: 600,
|
||||||
|
jianzhangCooldownSecond: 900,
|
||||||
|
} as Setting_SongRequest
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const accountInfo = useAccount()
|
const accountInfo = useAccount()
|
||||||
const message = useMessage()
|
const message = useMessage()
|
||||||
|
|
||||||
|
const settings = computed({
|
||||||
|
get: () => {
|
||||||
|
if (accountInfo.value) {
|
||||||
|
return accountInfo.value.settings.songRequest
|
||||||
|
}
|
||||||
|
return defaultSettings
|
||||||
|
},
|
||||||
|
set: (value) => {
|
||||||
|
if (accountInfo.value) {
|
||||||
|
accountInfo.value.settings.songRequest = value
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
client: DanmakuClient
|
client: DanmakuClient
|
||||||
roomInfo: RoomAuthInfo
|
roomInfo: RoomAuthInfo
|
||||||
code: string | undefined
|
code: string | undefined
|
||||||
|
isOpenLive?: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const activeSongs = ref<SongRequestInfo[]>([])
|
const localActiveSongs = useStorage('SongRequest.ActiveSongs', [] as SongRequestInfo[])
|
||||||
|
const activeSongs = ref<SongRequestInfo[]>(await getActiveSong())
|
||||||
|
|
||||||
|
const newSongName = ref('')
|
||||||
|
|
||||||
|
const defaultPrefix = useStorage('Settings.SongRequest.DefaultPrefix', '点歌')
|
||||||
|
|
||||||
|
async function getActiveSong() {
|
||||||
|
if (accountInfo.value) {
|
||||||
|
try {
|
||||||
|
const data = await QueryGetAPI<SongRequestInfo[]>(SONG_REQUEST_API_URL + 'get-active', {
|
||||||
|
id: accountInfo.value.id,
|
||||||
|
})
|
||||||
|
if (data.code == 200) {
|
||||||
|
console.log('[OPEN-LIVE-Song-Request] 已获取点歌队列')
|
||||||
|
return data.data
|
||||||
|
} else {
|
||||||
|
message.error('无法获取点歌队列: ' + data.message)
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
} else {
|
||||||
|
return localActiveSongs.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function addSong(danmaku: EventModel) {
|
||||||
|
console.log(`[OPEN-LIVE-Song-Request] 收到 [${danmaku.name}] 的点歌${danmaku.type == EventDataTypes.SC ? 'SC' : '弹幕'}: ${danmaku.msg}`)
|
||||||
|
if (accountInfo.value) {
|
||||||
|
await QueryPostAPI<SongRequestInfo>(SONG_REQUEST_API_URL + 'try-add', danmaku)
|
||||||
|
.then((data) => {
|
||||||
|
if (data.code == 200) {
|
||||||
|
message.success(`[${danmaku.name}] 添加曲目: ${data.data.songName}`)
|
||||||
|
} else {
|
||||||
|
message.error(`[${danmaku.name}] 添加曲目失败: ${data.message}`)
|
||||||
|
console.log(`[OPEN-LIVE-Song-Request] [${danmaku.name}] 添加曲目失败: ${data.message}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
const songData = {
|
||||||
|
songName: danmaku.msg.trim().substring(settings.value.orderPrefix.length),
|
||||||
|
song: undefined,
|
||||||
|
status: SongRequestStatus.Waiting,
|
||||||
|
from: danmaku.type == EventDataTypes.Message ? SongRequestFrom.Danmaku : SongRequestFrom.SC,
|
||||||
|
scPrice: danmaku.type == EventDataTypes.SC ? danmaku.price : 0,
|
||||||
|
user: {
|
||||||
|
name: danmaku.name,
|
||||||
|
uid: danmaku.uid,
|
||||||
|
fans_medal_level: danmaku.fans_medal_level,
|
||||||
|
fans_medal_name: danmaku.fans_medal_name,
|
||||||
|
fans_medal_wearing_status: danmaku.fans_medal_wearing_status,
|
||||||
|
guard_level: danmaku.guard_level,
|
||||||
|
} as SongRequestUserInfo,
|
||||||
|
createAt: Date.now(),
|
||||||
|
} as SongRequestInfo
|
||||||
|
localActiveSongs.value.push(songData)
|
||||||
|
message.success(`[${danmaku.name}] 添加曲目: ${songData.songName}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function addSongManual() {
|
||||||
|
if (!newSongName.value) {
|
||||||
|
message.error('请输入曲目名')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (accountInfo.value) {
|
||||||
|
await QueryPostAPIWithParams<SongRequestInfo>(SONG_REQUEST_API_URL + 'add', {
|
||||||
|
name: newSongName.value,
|
||||||
|
})
|
||||||
|
.then((data) => {
|
||||||
|
if (data.code == 200) {
|
||||||
|
message.success(`已手动添加曲目: ${data.data.songName}`)
|
||||||
|
} else {
|
||||||
|
message.error(`手动添加曲目失败: ${data.message}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
const songData = {
|
||||||
|
songName: newSongName.value,
|
||||||
|
song: undefined,
|
||||||
|
status: SongRequestStatus.Waiting,
|
||||||
|
from: SongRequestFrom.Manual,
|
||||||
|
scPrice: undefined,
|
||||||
|
user: undefined,
|
||||||
|
createAt: Date.now(),
|
||||||
|
} as SongRequestInfo
|
||||||
|
localActiveSongs.value.push(songData)
|
||||||
|
message.success(`已手动添加曲目: ${songData.songName}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function onGetDanmaku(danmaku: DanmakuInfo) {
|
function onGetDanmaku(danmaku: DanmakuInfo) {
|
||||||
console.log(danmaku)
|
if (checkMessage(danmaku.msg)) {
|
||||||
|
addSong({
|
||||||
|
msg: danmaku.msg,
|
||||||
|
type: EventDataTypes.Message,
|
||||||
|
time: danmaku.timestamp,
|
||||||
|
uid: danmaku.uid,
|
||||||
|
name: danmaku.uname,
|
||||||
|
avatar: danmaku.uface,
|
||||||
|
fans_medal_level: danmaku.fans_medal_level,
|
||||||
|
fans_medal_name: danmaku.fans_medal_name,
|
||||||
|
fans_medal_wearing_status: danmaku.fans_medal_wearing_status,
|
||||||
|
guard_level: danmaku.guard_level,
|
||||||
|
num: 1,
|
||||||
|
price: 0,
|
||||||
|
} as EventModel)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
function onGetSC(danmaku: SCInfo) {
|
function onGetSC(danmaku: SCInfo) {
|
||||||
console.log(danmaku)
|
if (settings.value.allowSC && checkMessage(danmaku.message)) {
|
||||||
|
addSong({
|
||||||
|
msg: danmaku.message,
|
||||||
|
type: EventDataTypes.SC,
|
||||||
|
time: danmaku.timestamp,
|
||||||
|
uid: danmaku.uid,
|
||||||
|
name: danmaku.uname,
|
||||||
|
fans_medal_level: danmaku.fans_medal_level,
|
||||||
|
fans_medal_name: danmaku.fans_medal_name,
|
||||||
|
fans_medal_wearing_status: danmaku.fans_medal_wearing_status,
|
||||||
|
guard_level: danmaku.guard_level,
|
||||||
|
avatar: danmaku.uface,
|
||||||
|
num: 1,
|
||||||
|
price: danmaku.rmb,
|
||||||
|
} as EventModel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function checkMessage(msg: string) {
|
||||||
|
return msg
|
||||||
|
.trim()
|
||||||
|
.toLowerCase()
|
||||||
|
.startsWith(accountInfo.value ? settings.value.orderPrefix : defaultPrefix.value)
|
||||||
|
}
|
||||||
|
async function onUpdateFunctionEnable() {
|
||||||
|
if (accountInfo.value) {
|
||||||
|
const oldValue = JSON.parse(JSON.stringify(accountInfo.value.settings.enableFunctions))
|
||||||
|
if (accountInfo.value?.settings.enableFunctions.includes(FunctionTypes.SongRequest)) {
|
||||||
|
accountInfo.value.settings.enableFunctions = accountInfo.value.settings.enableFunctions.filter((f) => f != FunctionTypes.SongRequest)
|
||||||
|
} else {
|
||||||
|
accountInfo.value.settings.enableFunctions.push(FunctionTypes.SongRequest)
|
||||||
|
}
|
||||||
|
accountInfo.value.settings.enableFunctions.push
|
||||||
|
await SaveAccountSettings()
|
||||||
|
.then((data) => {
|
||||||
|
if (data.code == 200) {
|
||||||
|
message.success(`已${accountInfo.value?.settings.enableFunctions.includes(FunctionTypes.SongRequest) ? '启用' : '禁用'}点歌功能`)
|
||||||
|
} else {
|
||||||
|
if (accountInfo.value) {
|
||||||
|
accountInfo.value.settings.enableFunctions = oldValue
|
||||||
|
}
|
||||||
|
message.error(`点歌功能${accountInfo.value?.settings.enableFunctions.includes(FunctionTypes.SongRequest) ? '启用' : '禁用'}失败: ${data.message}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err)
|
||||||
|
message.error(`点歌功能${accountInfo.value?.settings.enableFunctions.includes(FunctionTypes.SongRequest) ? '启用' : '禁用'}失败: ${err}`)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let timer: any
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
if (accountInfo.value) {
|
||||||
|
settings.value = accountInfo.value.settings.songRequest
|
||||||
|
}
|
||||||
props.client.on('danmaku', onGetDanmaku)
|
props.client.on('danmaku', onGetDanmaku)
|
||||||
props.client.on('sc', onGetSC)
|
props.client.on('sc', onGetSC)
|
||||||
|
timer = setInterval(() => {})
|
||||||
})
|
})
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
props.client.off('danmaku', onGetDanmaku)
|
props.client.off('danmaku', onGetDanmaku)
|
||||||
props.client.off('sc', onGetSC)
|
props.client.off('sc', onGetSC)
|
||||||
|
clearInterval(timer)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
开发中...
|
开发中...
|
||||||
<NTabs animated>
|
|
||||||
<NTabPane name="list" tab="列表">
|
|
||||||
<NList> </NList>
|
|
||||||
</NTabPane>
|
|
||||||
<NTabPane name="history" tab="历史"> </NTabPane>
|
|
||||||
</NTabs>
|
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -333,193 +333,200 @@ onUnmounted(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NLayoutContent style="height: 100vh; padding: 20px">
|
<NResult v-if="!code && !accountInfo" status="403" title="403" description="该页面只能从幻星平台访问或者注册用户使用" />
|
||||||
<NResult v-if="!code && !accountInfo" status="403" title="403" description="该页面只能从幻星平台访问或者注册用户使用" />
|
<template v-else>
|
||||||
<template v-else>
|
<NCard>
|
||||||
|
<template #header>
|
||||||
|
直播抽奖
|
||||||
|
<NDivider vertical />
|
||||||
|
<NButton text type="primary" tag="a" href="https://vtsuru.live" target="_blank"> 前往 VTsuru.live 主站 </NButton>
|
||||||
|
</template>
|
||||||
|
<NAlert v-if="!code && accountInfo && !accountInfo.isBiliVerified" type="error"> 请先绑定B站账号 </NAlert>
|
||||||
|
<NAlert v-else-if="!code && accountInfo && accountInfo.biliAuthCodeStatus != 1" type="error"> 身份码状态异常, 请重新绑定 </NAlert>
|
||||||
<NCard>
|
<NCard>
|
||||||
<template #header>
|
<NSpace align="center">
|
||||||
直播抽奖
|
<NButton type="info" @click="showModal = true" size="small"> 抽奖历史</NButton>
|
||||||
<NDivider vertical />
|
<NButton type="success" @click="showOBSModal = true" size="small"> OBS组件</NButton>
|
||||||
<NButton text type="primary" tag="a" href="https://vtsuru.live" target="_blank"> 前往 VTsuru.live 主站 </NButton>
|
</NSpace>
|
||||||
|
</NCard>
|
||||||
|
<NCard size="small" embedded title="抽奖选项">
|
||||||
|
<template #header-extra>
|
||||||
|
<NButton size="small" secondary @click="lotteryOption = defaultOption" :disabled="isStartLottery"> 恢复默认 </NButton>
|
||||||
</template>
|
</template>
|
||||||
<NAlert v-if="!code && accountInfo && !accountInfo.isBiliVerified" type="error"> 请先绑定B站账号 </NAlert>
|
<NSpace justify="center" align="center">
|
||||||
<NAlert v-else-if="!code && accountInfo && accountInfo.biliAuthCodeStatus != 1" type="error"> 身份码状态异常, 请重新绑定 </NAlert>
|
<NTag :bordered="false"> 抽奖类型 </NTag>
|
||||||
<NCard>
|
<NRadioGroup v-model:value="lotteryOption.type" :disabled="isLottering" size="small">
|
||||||
<NSpace align="center">
|
<NRadioButton value="danmaku" :disabled="isStartLottery"> 弹幕 </NRadioButton>
|
||||||
<NButton type="info" @click="showModal = true" size="small"> 抽奖历史</NButton>
|
<NRadioButton value="gift" :disabled="isStartLottery"> 礼物 </NRadioButton>
|
||||||
<NButton type="success" @click="showOBSModal = true" size="small"> OBS组件</NButton>
|
</NRadioGroup>
|
||||||
</NSpace>
|
</NSpace>
|
||||||
</NCard>
|
<NDivider style="margin: 10px 0 10px 0"></NDivider>
|
||||||
<NCard size="small" embedded title="抽奖选项">
|
<NSpace align="center">
|
||||||
<template #header-extra>
|
<NInputGroup style="max-width: 200px">
|
||||||
<NButton size="small" secondary @click="lotteryOption = defaultOption" :disabled="isStartLottery"> 恢复默认 </NButton>
|
<NInputGroupLabel> 抽选人数 </NInputGroupLabel>
|
||||||
</template>
|
<NInputNumber :disabled="isStartLottery" v-model:value="lotteryOption.resultCount" placeholder="" min="1" />
|
||||||
<NSpace justify="center" align="center">
|
</NInputGroup>
|
||||||
<NTag :bordered="false"> 抽奖类型 </NTag>
|
<NCheckbox :disabled="isStartLottery" v-model:checked="lotteryOption.needGuard"> 需要上舰 </NCheckbox>
|
||||||
<NRadioGroup v-model:value="lotteryOption.type" :disabled="isLottering" size="small">
|
<NCheckbox :disabled="isStartLottery" v-model:checked="lotteryOption.needFanMedal"> 需要粉丝牌 </NCheckbox>
|
||||||
<NRadioButton value="danmaku" :disabled="isStartLottery"> 弹幕 </NRadioButton>
|
<NCollapseTransition>
|
||||||
<NRadioButton value="gift" :disabled="isStartLottery"> 礼物 </NRadioButton>
|
<NInputGroup v-if="lotteryOption.needFanMedal" style="max-width: 200px">
|
||||||
</NRadioGroup>
|
<NInputGroupLabel> 最低粉丝牌等级 </NInputGroupLabel>
|
||||||
</NSpace>
|
<NInputNumber v-model:value="lotteryOption.fanCardLevel" min="1" max="50" :default-value="1" :disabled="isLottering || isStartLottery" />
|
||||||
<NDivider style="margin: 10px 0 10px 0"></NDivider>
|
|
||||||
<NSpace align="center">
|
|
||||||
<NInputGroup style="max-width: 200px">
|
|
||||||
<NInputGroupLabel> 抽选人数 </NInputGroupLabel>
|
|
||||||
<NInputNumber :disabled="isStartLottery" v-model:value="lotteryOption.resultCount" placeholder="" min="1" />
|
|
||||||
</NInputGroup>
|
</NInputGroup>
|
||||||
<NCheckbox :disabled="isStartLottery" v-model:checked="lotteryOption.needGuard"> 需要上舰 </NCheckbox>
|
</NCollapseTransition>
|
||||||
<NCheckbox :disabled="isStartLottery" v-model:checked="lotteryOption.needFanMedal"> 需要粉丝牌 </NCheckbox>
|
<template v-if="lotteryOption.type == 'danmaku'">
|
||||||
<NCollapseTransition>
|
<NTooltip>
|
||||||
<NInputGroup v-if="lotteryOption.needFanMedal" style="max-width: 200px">
|
<template #trigger>
|
||||||
<NInputGroupLabel> 最低粉丝牌等级 </NInputGroupLabel>
|
<NInputGroup style="max-width: 250px">
|
||||||
<NInputNumber v-model:value="lotteryOption.fanCardLevel" min="1" max="50" :default-value="1" :disabled="isLottering || isStartLottery" />
|
<NInputGroupLabel> 弹幕内容 </NInputGroupLabel>
|
||||||
</NInputGroup>
|
<NInput :disabled="isStartLottery" v-model:value="lotteryOption.danmakuKeyword" placeholder="留空则任何弹幕都可以" />
|
||||||
</NCollapseTransition>
|
</NInputGroup>
|
||||||
<template v-if="lotteryOption.type == 'danmaku'">
|
</template>
|
||||||
|
符合规则的弹幕才会被添加到抽奖队列中
|
||||||
|
</NTooltip>
|
||||||
|
<NRadioGroup v-model:value="lotteryOption.danmakuFilterType" name="判定类型" :disabled="isLottering" size="small">
|
||||||
|
<NRadioButton :disabled="isStartLottery" value="all"> 完全一致 </NRadioButton>
|
||||||
|
<NRadioButton :disabled="isStartLottery" value="contains"> 包含 </NRadioButton>
|
||||||
|
<NRadioButton :disabled="isStartLottery" value="regex"> 正则 </NRadioButton>
|
||||||
|
</NRadioGroup>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="lotteryOption.type == 'gift'">
|
||||||
|
<NInputGroup style="max-width: 250px">
|
||||||
|
<NInputGroupLabel> 最低价格 </NInputGroupLabel>
|
||||||
|
<NInputNumber :disabled="isStartLottery" v-model:value="lotteryOption.giftMinPrice" placeholder="留空则不限制" />
|
||||||
|
</NInputGroup>
|
||||||
|
<NInputGroup style="max-width: 200px">
|
||||||
|
<NInputGroupLabel> 礼物名称 </NInputGroupLabel>
|
||||||
|
<NInput :disabled="isStartLottery" v-model:value="lotteryOption.giftName" placeholder="留空则不限制" />
|
||||||
|
</NInputGroup>
|
||||||
|
</template>
|
||||||
|
</NSpace>
|
||||||
|
<NDivider style="margin: 10px 0 10px 0"></NDivider>
|
||||||
|
<NSpace justify="center" align="center">
|
||||||
|
<NTag :bordered="false"> 抽取方式 </NTag>
|
||||||
|
<NRadioGroup v-model:value="lotteryOption.lotteryType" name="抽取类型" size="small" :disabled="isLottering">
|
||||||
|
<NRadioButton value="single">
|
||||||
|
单个
|
||||||
<NTooltip>
|
<NTooltip>
|
||||||
<template #trigger>
|
<template #trigger>
|
||||||
<NInputGroup style="max-width: 250px">
|
<NIcon :component="Info24Filled" />
|
||||||
<NInputGroupLabel> 弹幕内容 </NInputGroupLabel>
|
|
||||||
<NInput :disabled="isStartLottery" v-model:value="lotteryOption.danmakuKeyword" placeholder="留空则任何弹幕都可以" />
|
|
||||||
</NInputGroup>
|
|
||||||
</template>
|
</template>
|
||||||
符合规则的弹幕才会被添加到抽奖队列中
|
一个一个减少
|
||||||
</NTooltip>
|
</NTooltip>
|
||||||
<NRadioGroup v-model:value="lotteryOption.danmakuFilterType" name="判定类型" :disabled="isLottering" size="small">
|
</NRadioButton>
|
||||||
<NRadioButton :disabled="isStartLottery" value="all"> 完全一致 </NRadioButton>
|
<NRadioButton value="half">
|
||||||
<NRadioButton :disabled="isStartLottery" value="contains"> 包含 </NRadioButton>
|
减半
|
||||||
<NRadioButton :disabled="isStartLottery" value="regex"> 正则 </NRadioButton>
|
<NTooltip>
|
||||||
</NRadioGroup>
|
<template #trigger>
|
||||||
</template>
|
<NIcon :component="Info24Filled" />
|
||||||
<template v-else-if="lotteryOption.type == 'gift'">
|
|
||||||
<NInputGroup style="max-width: 250px">
|
|
||||||
<NInputGroupLabel> 最低价格 </NInputGroupLabel>
|
|
||||||
<NInputNumber :disabled="isStartLottery" v-model:value="lotteryOption.giftMinPrice" placeholder="留空则不限制" />
|
|
||||||
</NInputGroup>
|
|
||||||
<NInputGroup style="max-width: 200px">
|
|
||||||
<NInputGroupLabel> 礼物名称 </NInputGroupLabel>
|
|
||||||
<NInput :disabled="isStartLottery" v-model:value="lotteryOption.giftName" placeholder="留空则不限制" />
|
|
||||||
</NInputGroup>
|
|
||||||
</template>
|
|
||||||
</NSpace>
|
|
||||||
<NDivider style="margin: 10px 0 10px 0"></NDivider>
|
|
||||||
<NSpace justify="center" align="center">
|
|
||||||
<NTag :bordered="false"> 抽取方式 </NTag>
|
|
||||||
<NRadioGroup v-model:value="lotteryOption.lotteryType" name="抽取类型" size="small" :disabled="isLottering">
|
|
||||||
<NRadioButton value="single">
|
|
||||||
单个
|
|
||||||
<NTooltip>
|
|
||||||
<template #trigger>
|
|
||||||
<NIcon :component="Info24Filled" />
|
|
||||||
</template>
|
|
||||||
一个一个减少
|
|
||||||
</NTooltip>
|
|
||||||
</NRadioButton>
|
|
||||||
<NRadioButton value="half">
|
|
||||||
减半
|
|
||||||
<NTooltip>
|
|
||||||
<template #trigger>
|
|
||||||
<NIcon :component="Info24Filled" />
|
|
||||||
</template>
|
|
||||||
点一次减少一半
|
|
||||||
</NTooltip>
|
|
||||||
</NRadioButton>
|
|
||||||
</NRadioGroup>
|
|
||||||
</NSpace>
|
|
||||||
</NCard>
|
|
||||||
<NCard v-if="originUsers" size="small">
|
|
||||||
<NSpace justify="center" align="center">
|
|
||||||
<NButton type="primary" @click="continueLottery" :loading="isStartLottery" :disabled="isStartLottery || isLotteried || !client"> 开始 </NButton>
|
|
||||||
<NButton type="warning" :disabled="!isStartLottery" @click="pause"> 停止 </NButton>
|
|
||||||
<NButton type="error" :disabled="isLottering || originUsers.length == 0" @click="clear"> 清空 </NButton>
|
|
||||||
</NSpace>
|
|
||||||
<NDivider style="margin: 20px 0 20px 0"> <template v-if="isStartLottery"> 进行抽取前需要先停止 </template> </NDivider>
|
|
||||||
<NSpace justify="center">
|
|
||||||
<NButton type="primary" secondary @click="startLottery" :loading="isLottering" :disabled="isStartLottery || isLotteried"> 进行抽取 </NButton>
|
|
||||||
<NButton type="info" secondary :disabled="isStartLottery || isLottering || !isLotteried" @click="reset"> 重置 </NButton>
|
|
||||||
</NSpace>
|
|
||||||
<NDivider style="margin: 10px 0 10px 0"> 共 {{ currentUsers?.length }} 人</NDivider>
|
|
||||||
<NGrid v-if="currentUsers.length > 0" cols="1 500:2 800:3 1000:4" :x-gap="12" :y-gap="8">
|
|
||||||
<NGridItem v-for="item in currentUsers" v-bind:key="item.uId">
|
|
||||||
<NCard size="small" :title="item.name" style="height: 155px" embedded>
|
|
||||||
<template #header>
|
|
||||||
<NSpace align="center" vertical :size="5">
|
|
||||||
<NAvatar round lazy borderd :size="64" :src="item.avatar + '@64w_64h'" :img-props="{ referrerpolicy: 'no-referrer' }" style="box-shadow: 0 3px 5px rgba(0, 0, 0, 0.2)" />
|
|
||||||
<NSpace v-if="item.fans_medal_wearing_status">
|
|
||||||
<NTag size="tiny" round>
|
|
||||||
<NTag size="tiny" round :bordered="false">
|
|
||||||
{{ item.fans_medal_level }}
|
|
||||||
</NTag>
|
|
||||||
<span style="color: #577fb8">
|
|
||||||
{{ item.fans_medal_name }}
|
|
||||||
</span>
|
|
||||||
</NTag>
|
|
||||||
</NSpace>
|
|
||||||
<NTag v-else size="tiny" round :bordered="false"> 无粉丝牌 </NTag>
|
|
||||||
{{ item.name }}
|
|
||||||
</NSpace>
|
|
||||||
|
|
||||||
<NButton style="position: absolute; right: 5px; top: 5px; color: #753e3e" @click="removeUser(item)" size="small" circle>
|
|
||||||
<template #icon>
|
|
||||||
<NIcon :component="Delete24Filled" />
|
|
||||||
</template>
|
|
||||||
</NButton>
|
|
||||||
</template>
|
</template>
|
||||||
</NCard>
|
点一次减少一半
|
||||||
</NGridItem>
|
</NTooltip>
|
||||||
</NGrid>
|
</NRadioButton>
|
||||||
<NEmpty v-else description="暂无用户" />
|
</NRadioGroup>
|
||||||
</NCard>
|
</NSpace>
|
||||||
</NCard>
|
</NCard>
|
||||||
</template>
|
<NCard v-if="originUsers" size="small">
|
||||||
<NModal v-model:show="showModal" preset="card" title="抽奖结果" style="max-width: 90%; width: 800px" closable>
|
<NSpace justify="center" align="center">
|
||||||
<template #header-extra>
|
<NButton type="primary" @click="continueLottery" :loading="isStartLottery" :disabled="isStartLottery || isLotteried || !client"> 开始 </NButton>
|
||||||
<NButton type="error" size="small" @click="lotteryHistory = []"> 清空 </NButton>
|
<NButton type="warning" :disabled="!isStartLottery" @click="pause"> 停止 </NButton>
|
||||||
</template>
|
<NButton type="error" :disabled="isLottering || originUsers.length == 0" @click="clear"> 清空 </NButton>
|
||||||
<NScrollbar v-if="lotteryHistory.length > 0" style="max-height: 80vh">
|
</NSpace>
|
||||||
<NList>
|
<NDivider style="margin: 20px 0 20px 0"> <template v-if="isStartLottery"> 进行抽取前需要先停止 </template> </NDivider>
|
||||||
<NListItem v-for="item in lotteryHistory" :key="item.time">
|
<NSpace justify="center">
|
||||||
<NCard size="small">
|
<NButton
|
||||||
|
type="primary"
|
||||||
|
secondary
|
||||||
|
@click="startLottery"
|
||||||
|
:loading="isLottering"
|
||||||
|
:disabled="isStartLottery || isLotteried"
|
||||||
|
data-umami-event="Open-Live Use Lottery"
|
||||||
|
:data-umami-event-uid="client.roomAuthInfo.value?.anchor_info.uid"
|
||||||
|
>
|
||||||
|
进行抽取
|
||||||
|
</NButton>
|
||||||
|
<NButton type="info" secondary :disabled="isStartLottery || isLottering || !isLotteried" @click="reset"> 重置 </NButton>
|
||||||
|
</NSpace>
|
||||||
|
<NDivider style="margin: 10px 0 10px 0"> 共 {{ currentUsers?.length }} 人</NDivider>
|
||||||
|
<NGrid v-if="currentUsers.length > 0" cols="1 500:2 800:3 1000:4" :x-gap="12" :y-gap="8">
|
||||||
|
<NGridItem v-for="item in currentUsers" v-bind:key="item.uId">
|
||||||
|
<NCard size="small" :title="item.name" style="height: 155px" embedded>
|
||||||
<template #header>
|
<template #header>
|
||||||
<NTime :time="item.time" />
|
<NSpace align="center" vertical :size="5">
|
||||||
</template>
|
<NAvatar round lazy borderd :size="64" :src="item.avatar + '@64w_64h'" :img-props="{ referrerpolicy: 'no-referrer' }" style="box-shadow: 0 3px 5px rgba(0, 0, 0, 0.2)" />
|
||||||
<template #header-extra>
|
<NSpace v-if="item.fans_medal_wearing_status">
|
||||||
<NButton type="error" size="small" @click="lotteryHistory.splice(lotteryHistory.indexOf(item), 1)"> 删除 </NButton>
|
<NTag size="tiny" round>
|
||||||
</template>
|
<NTag size="tiny" round :bordered="false">
|
||||||
<NSpace vertical>
|
{{ item.fans_medal_level }}
|
||||||
<NSpace v-for="user in item.users" :key="user.uId">
|
</NTag>
|
||||||
<NAvatar round lazy :src="user.avatar + '@64w_64h'" :img-props="{ referrerpolicy: 'no-referrer' }" />
|
<span style="color: #577fb8">
|
||||||
{{ user.name }}
|
{{ item.fans_medal_name }}
|
||||||
|
</span>
|
||||||
|
</NTag>
|
||||||
|
</NSpace>
|
||||||
|
<NTag v-else size="tiny" round :bordered="false"> 无粉丝牌 </NTag>
|
||||||
|
{{ item.name }}
|
||||||
</NSpace>
|
</NSpace>
|
||||||
</NSpace>
|
|
||||||
</NCard>
|
|
||||||
</NListItem>
|
|
||||||
</NList>
|
|
||||||
</NScrollbar>
|
|
||||||
<NEmpty v-else description="暂无记录" />
|
|
||||||
</NModal>
|
|
||||||
<NModal v-model:show="showOBSModal" preset="card" title="OBS 组件" style="max-width: 90%; width: 800px; max-height: 90vh" closable content-style="overflow: auto">
|
|
||||||
<NAlert title="这是什么? " type="info"> 将等待队列以及结果显示在OBS中 </NAlert>
|
|
||||||
<NDivider> 浏览 </NDivider>
|
|
||||||
<div style="height: 400px; width: 250px; position: relative; margin: 0 auto">
|
|
||||||
<LiveLotteryOBS :code="code" />
|
|
||||||
</div>
|
|
||||||
<br />
|
|
||||||
<NInput :value="'https://vtsuru.live/obs/live-lottery?code=' + code" />
|
|
||||||
<NDivider />
|
|
||||||
<NCollapse>
|
|
||||||
<NCollapseItem title="使用说明">
|
|
||||||
<NUl>
|
|
||||||
<NLi>在 OBS 来源中添加源, 选择 浏览器</NLi>
|
|
||||||
<NLi>在 URL 栏填入上方链接</NLi>
|
|
||||||
<NLi>根据自己的需要调整宽度和高度</NLi>
|
|
||||||
<NLi>完事</NLi>
|
|
||||||
</NUl>
|
|
||||||
</NCollapseItem>
|
|
||||||
</NCollapse>
|
|
||||||
|
|
||||||
<NDivider />
|
<NButton style="position: absolute; right: 5px; top: 5px; color: #753e3e" @click="removeUser(item)" size="small" circle>
|
||||||
</NModal>
|
<template #icon>
|
||||||
</NLayoutContent>
|
<NIcon :component="Delete24Filled" />
|
||||||
|
</template>
|
||||||
|
</NButton>
|
||||||
|
</template>
|
||||||
|
</NCard>
|
||||||
|
</NGridItem>
|
||||||
|
</NGrid>
|
||||||
|
<NEmpty v-else description="暂无用户" />
|
||||||
|
</NCard>
|
||||||
|
</NCard>
|
||||||
|
</template>
|
||||||
|
<NModal v-model:show="showModal" preset="card" title="抽奖结果" style="max-width: 90%; width: 800px" closable>
|
||||||
|
<template #header-extra>
|
||||||
|
<NButton type="error" size="small" @click="lotteryHistory = []"> 清空 </NButton>
|
||||||
|
</template>
|
||||||
|
<NScrollbar v-if="lotteryHistory.length > 0" style="max-height: 80vh">
|
||||||
|
<NList>
|
||||||
|
<NListItem v-for="item in lotteryHistory" :key="item.time">
|
||||||
|
<NCard size="small">
|
||||||
|
<template #header>
|
||||||
|
<NTime :time="item.time" />
|
||||||
|
</template>
|
||||||
|
<template #header-extra>
|
||||||
|
<NButton type="error" size="small" @click="lotteryHistory.splice(lotteryHistory.indexOf(item), 1)"> 删除 </NButton>
|
||||||
|
</template>
|
||||||
|
<NSpace vertical>
|
||||||
|
<NSpace v-for="user in item.users" :key="user.uId">
|
||||||
|
<NAvatar round lazy :src="user.avatar + '@64w_64h'" :img-props="{ referrerpolicy: 'no-referrer' }" />
|
||||||
|
{{ user.name }}
|
||||||
|
</NSpace>
|
||||||
|
</NSpace>
|
||||||
|
</NCard>
|
||||||
|
</NListItem>
|
||||||
|
</NList>
|
||||||
|
</NScrollbar>
|
||||||
|
<NEmpty v-else description="暂无记录" />
|
||||||
|
</NModal>
|
||||||
|
<NModal v-model:show="showOBSModal" preset="card" title="OBS 组件" style="max-width: 90%; width: 800px; max-height: 90vh" closable content-style="overflow: auto">
|
||||||
|
<NAlert title="这是什么? " type="info"> 将等待队列以及结果显示在OBS中 </NAlert>
|
||||||
|
<NDivider> 浏览 </NDivider>
|
||||||
|
<div style="height: 400px; width: 250px; position: relative; margin: 0 auto">
|
||||||
|
<LiveLotteryOBS :code="code" />
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<NInput :value="'https://vtsuru.live/obs/live-lottery?code=' + code" />
|
||||||
|
<NDivider />
|
||||||
|
<NCollapse>
|
||||||
|
<NCollapseItem title="使用说明">
|
||||||
|
<NUl>
|
||||||
|
<NLi>在 OBS 来源中添加源, 选择 浏览器</NLi>
|
||||||
|
<NLi>在 URL 栏填入上方链接</NLi>
|
||||||
|
<NLi>根据自己的需要调整宽度和高度</NLi>
|
||||||
|
<NLi>完事</NLi>
|
||||||
|
</NUl>
|
||||||
|
</NCollapseItem>
|
||||||
|
</NCollapse>
|
||||||
|
|
||||||
|
<NDivider />
|
||||||
|
</NModal>
|
||||||
</template>
|
</template>
|
||||||
@/data/DanmakuClient
|
|
||||||
|
|||||||
Reference in New Issue
Block a user