mirror of
https://github.com/Megghy/vtsuru.live.git
synced 2025-12-06 18:36:55 +08:00
fix: no voice in speech page; custom personal page redirect notworking. feat: sync sroll bar between question display page an obs component
This commit is contained in:
37
src/views/manage/DanmujiManageView.vue
Normal file
37
src/views/manage/DanmujiManageView.vue
Normal file
@@ -0,0 +1,37 @@
|
||||
<script setup lang="ts">
|
||||
import { NFlex, NInput } from 'naive-ui';
|
||||
import DanmujiOBS from '../obs/DanmujiOBS.vue';
|
||||
import { useAccount } from '@/api/account';
|
||||
import MonacoEditorComponent from '@/components/MonacoEditorComponent.vue';
|
||||
import { ref } from 'vue';
|
||||
import { CURRENT_HOST } from '@/data/constants';
|
||||
|
||||
const accountInfo = useAccount()
|
||||
const css = ref('')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NFlex wrap style="height: 100%">
|
||||
<NFlex style="flex: 1;" vertical>
|
||||
<NInput :allowInput="() => false" :value="`${CURRENT_HOST}obs/danmuji?token=${accountInfo.token}`" />
|
||||
<MonacoEditorComponent language="css" :height="500" v-model:value="css" />
|
||||
</NFlex>
|
||||
<div class="danmuji-obs" style="width: 300px; height: calc(100% - 2px); min-height: 500px;border: 1px solid #adadad;border-radius: 8px;
|
||||
overflow: hidden;">
|
||||
<DanmujiOBS :isOBS="false" style="height: 100%; width: 100%;" :customCss="css" />
|
||||
</div>
|
||||
</NFlex>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.danmuji-obs {
|
||||
--danmuji-bg: #333;
|
||||
background-color: #222;
|
||||
background-image: linear-gradient(45deg, var(--danmuji-bg) 25%, transparent 25%),
|
||||
linear-gradient(-45deg, var(--danmuji-bg) 25%, transparent 25%),
|
||||
linear-gradient(45deg, transparent 75%, var(--danmuji-bg) 75%),
|
||||
linear-gradient(-45deg, transparent 75%, var(--danmuji-bg) 75%);
|
||||
background-size: 20px 20px;
|
||||
background-position: 0 0, 0 10px, 10px -10px, -10px 0;
|
||||
}
|
||||
</style>
|
||||
@@ -1,11 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import { copyToClipboard, downloadImage } from '@/Utils'
|
||||
import { DisableFunction, EnableFunction, SaveAccountSettings, SaveSetting, useAccount } from '@/api/account'
|
||||
import { FunctionTypes, QAInfo } from '@/api/api-models'
|
||||
import { FunctionTypes, QAInfo, Setting_QuestionDisplay } from '@/api/api-models'
|
||||
import { QueryGetAPI } from '@/api/query'
|
||||
import { QUESTION_API_URL } from '@/data/constants'
|
||||
import { CURRENT_HOST, QUESTION_API_URL } from '@/data/constants'
|
||||
import router from '@/router'
|
||||
import { Heart, HeartOutline, SwapHorizontal } from '@vicons/ionicons5'
|
||||
// @ts-ignore
|
||||
import { saveAs } from 'file-saver'
|
||||
import html2canvas from 'html2canvas'
|
||||
import {
|
||||
@@ -47,6 +48,8 @@ import { useRoute } from 'vue-router'
|
||||
import QuestionItem from '@/components/QuestionItems.vue'
|
||||
import { Delete24Filled, Delete24Regular, Eye24Filled, EyeOff24Filled, Info24Filled } from '@vicons/fluent'
|
||||
import { useQuestionBox } from '@/store/useQuestionBox'
|
||||
import { useStorage } from '@vueuse/core'
|
||||
import QuestionDisplayCard from './QuestionDisplayCard.vue'
|
||||
|
||||
const accountInfo = useAccount()
|
||||
const route = useRoute()
|
||||
@@ -62,15 +65,34 @@ const replyMessage = ref()
|
||||
const addTagName = ref('')
|
||||
|
||||
const showSettingCard = ref(true)
|
||||
const showOBSModal = ref(false)
|
||||
const defaultSettings = {} as Setting_QuestionDisplay
|
||||
const setting = computed({
|
||||
get: () => {
|
||||
if (accountInfo.value && accountInfo.value.settings) {
|
||||
return accountInfo.value.settings.questionDisplay
|
||||
}
|
||||
return defaultSettings
|
||||
},
|
||||
set: (value) => {
|
||||
if (accountInfo.value) {
|
||||
accountInfo.value.settings.questionDisplay = value
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const shareCardRef = ref()
|
||||
const shareUrl = computed(() => 'https://vtsuru.live/@' + accountInfo.value?.name + '/question-box')
|
||||
const shareUrl = computed(() => `${CURRENT_HOST}@` + accountInfo.value?.name + '/question-box')
|
||||
|
||||
const ps = ref(20)
|
||||
const pn = ref(1)
|
||||
const pagedQuestions = computed(() =>
|
||||
useQB.recieveQuestionsFiltered.slice((pn.value - 1) * ps.value, pn.value * ps.value),
|
||||
)
|
||||
const savedCardSize = useStorage<{ width: number; height: number }>('Settings.QuestionDisplay.CardSize', {
|
||||
width: 400,
|
||||
height: 400,
|
||||
})
|
||||
|
||||
let isRevieveGetted = false
|
||||
let isSendGetted = false
|
||||
@@ -168,26 +190,20 @@ onMounted(() => {
|
||||
|
||||
<template>
|
||||
<NSpace align="center">
|
||||
<NAlert
|
||||
:type="accountInfo.settings.enableFunctions.includes(FunctionTypes.QuestionBox) ? 'success' : 'warning'"
|
||||
style="max-width: 200px"
|
||||
>
|
||||
<NAlert :type="accountInfo.settings.enableFunctions.includes(FunctionTypes.QuestionBox) ? 'success' : 'warning'"
|
||||
style="max-width: 200px">
|
||||
启用提问箱
|
||||
<NDivider vertical />
|
||||
<NSwitch
|
||||
:value="accountInfo?.settings.enableFunctions.includes(FunctionTypes.QuestionBox)"
|
||||
@update:value="setFunctionEnable"
|
||||
/>
|
||||
<NSwitch :value="accountInfo?.settings.enableFunctions.includes(FunctionTypes.QuestionBox)"
|
||||
@update:value="setFunctionEnable" />
|
||||
</NAlert>
|
||||
<NButton type="primary" @click="refresh"> 刷新 </NButton>
|
||||
<NButton type="primary" @click="shareModalVisiable = true" secondary> 分享 </NButton>
|
||||
<NButton
|
||||
type="primary"
|
||||
@click="$router.push({ name: 'user-questionBox', params: { id: accountInfo.name } })"
|
||||
secondary
|
||||
>
|
||||
<NButton type="primary" @click="$router.push({ name: 'user-questionBox', params: { id: accountInfo.name } })"
|
||||
secondary>
|
||||
前往提问页
|
||||
</NButton>
|
||||
<NButton @click="showOBSModal = true" type="primary" secondary> 预览OBS组件 </NButton>
|
||||
</NSpace>
|
||||
<NDivider style="margin: 10px 0 10px 0" />
|
||||
<NSpin v-if="useQB.isLoading" show />
|
||||
@@ -195,15 +211,11 @@ onMounted(() => {
|
||||
<NTabPane tab="我收到的" name="0" display-directive="show:lazy">
|
||||
<NFlex align="center">
|
||||
<NButton @click="$router.push({ name: 'question-display' })" type="primary"> 打开展示页 </NButton>
|
||||
<NSelect
|
||||
v-model:value="useQB.displayTag"
|
||||
placeholder="选择当前话题"
|
||||
filterable
|
||||
clearable
|
||||
:options="useQB.tags.map((s) => ({ label: s.name, value: s.name }))"
|
||||
style="width: 200px"
|
||||
>
|
||||
<template #header> <NText strong depth="3"> 在设置选项卡中添加或删除话题 </NText> </template>
|
||||
<NSelect v-model:value="useQB.displayTag" placeholder="选择当前话题" filterable clearable
|
||||
:options="useQB.tags.map((s) => ({ label: s.name, value: s.name }))" style="width: 200px">
|
||||
<template #header>
|
||||
<NText strong depth="3"> 在设置选项卡中添加或删除话题 </NText>
|
||||
</template>
|
||||
</NSelect>
|
||||
<NCheckbox v-model:checked="useQB.onlyFavorite"> 只显示收藏 </NCheckbox>
|
||||
<NCheckbox v-model:checked="useQB.onlyPublic"> 只显示公开 </NCheckbox>
|
||||
@@ -212,14 +224,8 @@ onMounted(() => {
|
||||
<NDivider style="margin: 10px 0 10px 0" />
|
||||
<NEmpty v-if="useQB.recieveQuestionsFiltered.length == 0" description="暂无收到的提问" />
|
||||
<div v-else>
|
||||
<NPagination
|
||||
v-model:page="pn"
|
||||
v-model:page-size="ps"
|
||||
:item-count="useQB.recieveQuestionsFiltered.length"
|
||||
show-quick-jumper
|
||||
show-size-picker
|
||||
:page-sizes="[20, 50, 100]"
|
||||
/>
|
||||
<NPagination v-model:page="pn" v-model:page-size="ps" :item-count="useQB.recieveQuestionsFiltered.length"
|
||||
show-quick-jumper show-size-picker :page-sizes="[20, 50, 100]" />
|
||||
<NDivider style="margin: 10px 0 10px 0" />
|
||||
<QuestionItem :questions="pagedQuestions">
|
||||
<template #footer="{ item }">
|
||||
@@ -230,10 +236,8 @@ onMounted(() => {
|
||||
<NButton v-else size="small" @click="useQB.read(item, false)" type="warning">重设为未读</NButton>
|
||||
<NButton size="small" @click="useQB.favorite(item, !item.isFavorite)">
|
||||
<template #icon>
|
||||
<NIcon
|
||||
:component="item.isFavorite ? Heart : HeartOutline"
|
||||
:color="item.isFavorite ? '#dd484f' : ''"
|
||||
/>
|
||||
<NIcon :component="item.isFavorite ? Heart : HeartOutline"
|
||||
:color="item.isFavorite ? '#dd484f' : ''" />
|
||||
</template>
|
||||
收藏
|
||||
</NButton>
|
||||
@@ -264,14 +268,8 @@ onMounted(() => {
|
||||
</template>
|
||||
</QuestionItem>
|
||||
<NDivider style="margin: 10px 0 10px 0" />
|
||||
<NPagination
|
||||
v-model:page="pn"
|
||||
v-model:page-size="ps"
|
||||
:item-count="useQB.recieveQuestionsFiltered.length"
|
||||
show-quick-jumper
|
||||
show-size-picker
|
||||
:page-sizes="[20, 50, 100]"
|
||||
/>
|
||||
<NPagination v-model:page="pn" v-model:page-size="ps" :item-count="useQB.recieveQuestionsFiltered.length"
|
||||
show-quick-jumper show-size-picker :page-sizes="[20, 50, 100]" />
|
||||
</div>
|
||||
</NTabPane>
|
||||
<NTabPane ref="parentRef" tab="我发送的" name="1" display-directive="show:lazy">
|
||||
@@ -320,10 +318,8 @@ onMounted(() => {
|
||||
<NTabPane tab="设置" name="2" display-directive="show:lazy">
|
||||
<NDivider> 设定 </NDivider>
|
||||
<NSpin :show="useQB.isLoading">
|
||||
<NCheckbox
|
||||
v-model:checked="accountInfo.settings.questionBox.allowUnregistedUser"
|
||||
@update:checked="saveSettings"
|
||||
>
|
||||
<NCheckbox v-model:checked="accountInfo.settings.questionBox.allowUnregistedUser"
|
||||
@update:checked="saveSettings">
|
||||
允许未注册用户进行提问
|
||||
</NCheckbox>
|
||||
<NDivider>
|
||||
@@ -395,14 +391,7 @@ onMounted(() => {
|
||||
<NModal preset="card" v-model:show="replyModalVisiable" style="max-width: 90vw; width: 500px">
|
||||
<template #header> 回复 </template>
|
||||
<NSpace vertical>
|
||||
<NInput
|
||||
placeholder="请输入回复"
|
||||
type="textarea"
|
||||
v-model:value="replyMessage"
|
||||
maxlength="1000"
|
||||
show-count
|
||||
clearable
|
||||
/>
|
||||
<NInput placeholder="请输入回复" type="textarea" v-model:value="replyMessage" maxlength="1000" show-count clearable />
|
||||
<NSpin :show="useQB.isChangingPublic">
|
||||
<NCheckbox @update:checked="(v) => useQB.setPublic(v)" :default-checked="useQB.currentQuestion?.isPublic">
|
||||
公开可见
|
||||
@@ -410,12 +399,8 @@ onMounted(() => {
|
||||
</NSpin>
|
||||
</NSpace>
|
||||
<NDivider style="margin: 10px 0 10px 0" />
|
||||
<NButton
|
||||
:loading="useQB.isRepling"
|
||||
@click="useQB.reply(useQB.currentQuestion?.id ?? -1, replyMessage)"
|
||||
type="primary"
|
||||
:secondary="useQB.currentQuestion?.answer ? true : false"
|
||||
>
|
||||
<NButton :loading="useQB.isRepling" @click="useQB.reply(useQB.currentQuestion?.id ?? -1, replyMessage)"
|
||||
type="primary" :secondary="useQB.currentQuestion?.answer ? true : false">
|
||||
{{ useQB.currentQuestion?.answer ? '修改' : '发送' }}
|
||||
</NButton>
|
||||
</NModal>
|
||||
@@ -428,15 +413,8 @@ onMounted(() => {
|
||||
</NText>
|
||||
<NDivider class="share-card divider-1" />
|
||||
<NText class="share-card site"> VTSURU.LIVE </NText>
|
||||
<QrcodeVue
|
||||
class="share-card qrcode"
|
||||
:value="shareUrl"
|
||||
level="Q"
|
||||
:size="100"
|
||||
background="#00000000"
|
||||
foreground="#ffffff"
|
||||
:margin="1"
|
||||
/>
|
||||
<QrcodeVue class="share-card qrcode" :value="shareUrl" level="Q" :size="100" background="#00000000"
|
||||
foreground="#ffffff" :margin="1" />
|
||||
</div>
|
||||
<NDivider style="margin: 10px" />
|
||||
<NInputGroup>
|
||||
@@ -449,6 +427,25 @@ onMounted(() => {
|
||||
<NButton type="primary" @click="saveQRCode"> 保存二维码 </NButton>
|
||||
</NSpace>
|
||||
</NModal>
|
||||
|
||||
<NModal preset="card" v-model:show="showOBSModal" closable style="max-width: 90vw; width: auto" title="OBS组件"
|
||||
content-style="display: flex; align-items: center; justify-content: center; flex-direction: column">
|
||||
<NAlert type="info">
|
||||
操作显示的内容请前往
|
||||
<NButton text @click="$router.push({ name: 'question-display' })"> 展示管理页 </NButton>
|
||||
</NAlert>
|
||||
<br />
|
||||
<div :style="{
|
||||
width: savedCardSize.width + 'px',
|
||||
height: savedCardSize.height + 'px',
|
||||
}">
|
||||
<QuestionDisplayCard :question="useQB.displayQuestion" :setting="setting" />
|
||||
</div>
|
||||
<NDivider />
|
||||
<NInput readonly :value="CURRENT_HOST + 'obs/question-display?token=' + accountInfo?.token" />
|
||||
<NDivider />
|
||||
<NButton type="primary" @click="$router.push({ name: 'question-display' })"> 前往展示管理页 </NButton>
|
||||
</NModal>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
@@ -463,6 +460,7 @@ onMounted(() => {
|
||||
border-radius: 10px;
|
||||
background: linear-gradient(to right, #66bea3, #9179be);
|
||||
}
|
||||
|
||||
.share-card.qrcode {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
@@ -470,6 +468,7 @@ onMounted(() => {
|
||||
border-radius: 4px;
|
||||
background: linear-gradient(to right, #3d554e, #503e74);
|
||||
}
|
||||
|
||||
.share-card.title {
|
||||
position: absolute;
|
||||
font-size: 80px;
|
||||
@@ -478,6 +477,7 @@ onMounted(() => {
|
||||
color: #e6e6e662;
|
||||
font-weight: 550;
|
||||
}
|
||||
|
||||
/* .share-card.type {
|
||||
position: absolute;
|
||||
font-size: 20px;
|
||||
@@ -494,6 +494,7 @@ onMounted(() => {
|
||||
font-weight: 550;
|
||||
color: #e6e6e662;
|
||||
}
|
||||
|
||||
.share-card.name {
|
||||
position: absolute;
|
||||
font-size: 30px;
|
||||
@@ -505,6 +506,7 @@ onMounted(() => {
|
||||
color: #e6e6e6;
|
||||
font-weight: 550;
|
||||
}
|
||||
|
||||
.share-card.site {
|
||||
position: absolute;
|
||||
font-size: 12px;
|
||||
@@ -513,6 +515,7 @@ onMounted(() => {
|
||||
color: #e6e6e6a4;
|
||||
font-weight: 550;
|
||||
}
|
||||
|
||||
.share-card.divider-1 {
|
||||
position: absolute;
|
||||
width: 400px;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, onUnmounted, watch } from 'vue'
|
||||
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
|
||||
import { QAInfo, QuestionDisplayAlign, Setting_QuestionDisplay } from '@/api/api-models'
|
||||
import { useDebounceFn, useStorage } from '@vueuse/core'
|
||||
import { useDebounceFn, useScroll, useStorage } from '@vueuse/core'
|
||||
|
||||
const props = defineProps<{
|
||||
question: QAInfo | undefined
|
||||
@@ -10,6 +10,9 @@ const props = defineProps<{
|
||||
showGreenBorder?: boolean
|
||||
css?: string
|
||||
}>()
|
||||
defineExpose({ setScroll, setScrollTop })
|
||||
const emit = defineEmits<{ scroll: [value: { clientHeight: number, scrollHeight: number, scrollTop: number }] }>()
|
||||
|
||||
let styleElement: HTMLStyleElement
|
||||
const cssDebounce = useDebounceFn(() => {
|
||||
if (styleElement) {
|
||||
@@ -19,6 +22,10 @@ const cssDebounce = useDebounceFn(() => {
|
||||
}, 1000)
|
||||
watch(() => props.css, cssDebounce)
|
||||
|
||||
const contentRef = ref()
|
||||
const { x, y, isScrolling, arrivedState, directions } = useScroll(contentRef)
|
||||
|
||||
|
||||
const align = computed(() => {
|
||||
switch (props.setting.align) {
|
||||
case QuestionDisplayAlign.Left:
|
||||
@@ -30,6 +37,28 @@ const align = computed(() => {
|
||||
}
|
||||
return 'left'
|
||||
})
|
||||
function setScrollTop(top: number) {
|
||||
contentRef.value?.scrollTo({
|
||||
top: top,
|
||||
behavior: 'smooth',
|
||||
})
|
||||
}
|
||||
function setScroll(value: { clientHeight: number, scrollHeight: number, scrollTop: number }) {
|
||||
if (contentRef.value.clientHeight == contentRef.value.scrollHeight) {
|
||||
setScrollTop(value.scrollTop)
|
||||
} else {
|
||||
const scrollRatio1 = value.scrollTop / (value.scrollHeight - value.clientHeight);
|
||||
const scrollTop = scrollRatio1 * (contentRef.value.scrollHeight - contentRef.value.clientHeight);
|
||||
setScrollTop(scrollTop)
|
||||
}
|
||||
}
|
||||
function scrollCallback(e: Event) {
|
||||
emit('scroll', {
|
||||
clientHeight: contentRef.value?.clientHeight ?? 0,
|
||||
scrollHeight: contentRef.value?.scrollHeight ?? 0,
|
||||
scrollTop: contentRef.value?.scrollTop ?? 0
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// 创建<style>元素并添加到<head>中
|
||||
@@ -37,62 +66,50 @@ onMounted(() => {
|
||||
// 可能需要对 userStyleString 做安全处理以避免XSS攻击
|
||||
styleElement.textContent = props.css ?? ''
|
||||
document.head.appendChild(styleElement)
|
||||
|
||||
contentRef.value?.addEventListener('scroll', scrollCallback)
|
||||
})
|
||||
onUnmounted(() => {
|
||||
if (styleElement && styleElement.parentNode) {
|
||||
styleElement.parentNode.removeChild(styleElement)
|
||||
}
|
||||
contentRef.value?.removeEventListener('scroll', scrollCallback)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="question-display-root"
|
||||
:style="{
|
||||
backgroundColor: '#' + setting.borderColor,
|
||||
borderColor: setting.borderColor ? '#' + setting.borderColor : undefined,
|
||||
borderWidth: setting.borderWidth ? setting.borderWidth + 'px' : undefined,
|
||||
borderTopWidth: setting.showUserName && question ? 0 : setting.borderWidth,
|
||||
}"
|
||||
:display="question ? 1 : 0"
|
||||
>
|
||||
<div class="question-display-root" :style="{
|
||||
backgroundColor: '#' + setting.borderColor,
|
||||
borderColor: setting.borderColor ? '#' + setting.borderColor : undefined,
|
||||
borderWidth: setting.borderWidth ? setting.borderWidth + 'px' : undefined,
|
||||
borderTopWidth: setting.showUserName && question ? 0 : setting.borderWidth,
|
||||
}" :display="question ? 1 : 0">
|
||||
<Transition name="scale" mode="out-in">
|
||||
<div
|
||||
v-if="setting.showUserName && question"
|
||||
class="question-display-user-name"
|
||||
:style="{
|
||||
color: '#' + setting.nameFontColor,
|
||||
fontSize: setting.nameFontSize + 'px',
|
||||
fontWeight: setting.nameFontWeight ? setting.nameFontWeight : undefined,
|
||||
fontFamily: setting.nameFont,
|
||||
}"
|
||||
>
|
||||
<div v-if="setting.showUserName && question" class="question-display-user-name" :style="{
|
||||
color: '#' + setting.nameFontColor,
|
||||
fontSize: setting.nameFontSize + 'px',
|
||||
fontWeight: setting.nameFontWeight ? setting.nameFontWeight : undefined,
|
||||
fontFamily: setting.nameFont,
|
||||
}">
|
||||
{{ question?.sender?.name ?? '匿名用户' }}
|
||||
</div>
|
||||
</Transition>
|
||||
<div
|
||||
class="question-display-content"
|
||||
:style="{
|
||||
color: '#' + setting.fontColor,
|
||||
backgroundColor: '#' + setting.backgroundColor,
|
||||
fontSize: setting.fontSize + 'px',
|
||||
fontWeight: setting.fontWeight ? setting.fontWeight : undefined,
|
||||
textAlign: align,
|
||||
fontFamily: setting.font,
|
||||
}"
|
||||
:is-empty="question ? 0 : 1"
|
||||
>
|
||||
<div class="question-display-content" @scroll="scrollCallback" ref="contentRef" :style="{
|
||||
color: '#' + setting.fontColor,
|
||||
backgroundColor: '#' + setting.backgroundColor,
|
||||
fontSize: setting.fontSize + 'px',
|
||||
fontWeight: setting.fontWeight ? setting.fontWeight : undefined,
|
||||
textAlign: align,
|
||||
fontFamily: setting.font,
|
||||
}" :is-empty="question ? 0 : 1">
|
||||
<Transition name="fade" mode="out-in">
|
||||
<template v-if="question">
|
||||
<div>
|
||||
<div class="question-display-text">
|
||||
{{ question?.question.message }}
|
||||
</div>
|
||||
<img
|
||||
class="question-display-image"
|
||||
v-if="setting.showImage && question?.question.image"
|
||||
:src="question?.question.image"
|
||||
/>
|
||||
<img class="question-display-image" v-if="setting.showImage && question?.question.image"
|
||||
:src="question?.question.image" />
|
||||
</div>
|
||||
</template>
|
||||
<div v-else class="question-display-loading loading" :style="{ color: '#' + setting.fontColor }">
|
||||
@@ -118,6 +135,7 @@ onUnmounted(() => {
|
||||
box-sizing: border-box;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.question-display-content {
|
||||
border-radius: 10px;
|
||||
display: flex;
|
||||
@@ -127,6 +145,7 @@ onUnmounted(() => {
|
||||
padding: 24px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.question-display-user-name {
|
||||
text-align: center;
|
||||
margin: 5px;
|
||||
@@ -134,9 +153,11 @@ onUnmounted(() => {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.question-display-text {
|
||||
min-height: 50px;
|
||||
}
|
||||
|
||||
.question-display-image {
|
||||
max-width: 40%;
|
||||
max-height: 40%;
|
||||
@@ -172,7 +193,7 @@ onUnmounted(() => {
|
||||
|
||||
<style scoped>
|
||||
.loading,
|
||||
.loading > div {
|
||||
.loading>div {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
@@ -187,7 +208,7 @@ onUnmounted(() => {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.loading > div {
|
||||
.loading>div {
|
||||
display: inline-block;
|
||||
float: none;
|
||||
background-color: currentColor;
|
||||
@@ -199,18 +220,18 @@ onUnmounted(() => {
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
.loading > div {
|
||||
.loading>div {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 100%;
|
||||
}
|
||||
|
||||
.loading > div:first-child {
|
||||
.loading>div:first-child {
|
||||
transform: translateX(0%);
|
||||
animation: ball-newton-cradle-left 1.5s 0s ease-out infinite;
|
||||
}
|
||||
|
||||
.loading > div:last-child {
|
||||
.loading>div:last-child {
|
||||
transform: translateX(0%);
|
||||
animation: ball-newton-cradle-right 1.5s 0s ease-out infinite;
|
||||
}
|
||||
@@ -220,7 +241,7 @@ onUnmounted(() => {
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
.loading.la-sm > div {
|
||||
.loading.la-sm>div {
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
}
|
||||
@@ -230,7 +251,7 @@ onUnmounted(() => {
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.loading.la-2x > div {
|
||||
.loading.la-2x>div {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
@@ -240,7 +261,7 @@ onUnmounted(() => {
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
.loading.la-3x > div {
|
||||
.loading.la-3x>div {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
@@ -270,11 +291,12 @@ onUnmounted(() => {
|
||||
transform: translateX(0%);
|
||||
}
|
||||
}
|
||||
.loading > div:first-child {
|
||||
|
||||
.loading>div:first-child {
|
||||
animation: ball-newton-cradle-left 1.5s 0s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.loading > div:last-child {
|
||||
.loading>div:last-child {
|
||||
animation: ball-newton-cradle-right 1.5s 0s ease-in-out infinite;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
} from '@/api/api-models'
|
||||
import { QueryGetAPI, QueryPostAPI } from '@/api/query'
|
||||
import VideoCollectInfoCard from '@/components/VideoCollectInfoCard.vue'
|
||||
import { VIDEO_COLLECT_API_URL } from '@/data/constants'
|
||||
import { CURRENT_HOST, VIDEO_COLLECT_API_URL } from '@/data/constants'
|
||||
import router from '@/router'
|
||||
import { Clock24Filled, Person24Filled } from '@vicons/fluent'
|
||||
import { useWindowSize } from '@vueuse/core'
|
||||
@@ -148,74 +148,74 @@ const gridRender = (type: 'padding' | 'reject' | 'accept') => {
|
||||
return videos.length == 0
|
||||
? h(NEmpty)
|
||||
: h(NGrid, { cols: '1 500:2 700:3 900:4 1200:5 ', xGap: '12', yGap: '12', responsive: 'self' }, () =>
|
||||
videos?.map((v) =>
|
||||
h(NGridItem, () =>
|
||||
h(
|
||||
NCard,
|
||||
{ style: 'height: 330px;', embedded: true, size: 'small' },
|
||||
{
|
||||
cover: () =>
|
||||
h('div', { style: 'position: relative;height: 150px;' }, [
|
||||
h('img', {
|
||||
src: v.video.cover.replace('http://', 'https://'),
|
||||
referrerpolicy: 'no-referrer',
|
||||
style: 'max-height: 100%; object-fit: contain;cursor: pointer',
|
||||
onClick: () => window.open('https://www.bilibili.com/video/' + v.info.bvid, '_blank'),
|
||||
}),
|
||||
h(
|
||||
NSpace,
|
||||
{
|
||||
style: { position: 'relative', bottom: '20px', background: '#00000073' },
|
||||
justify: 'space-around',
|
||||
},
|
||||
() => [
|
||||
h('span', [
|
||||
h(NIcon, { component: Clock24Filled, color: 'lightgrey' }),
|
||||
h(NText, { style: 'color: lightgrey;size:small;' }, () => formatSeconds(v.video.length)),
|
||||
]),
|
||||
h('span', [
|
||||
h(NIcon, { component: Person24Filled, color: 'lightgrey' }),
|
||||
h(NText, { style: 'color: lightgrey;size:small;' }, () => v.video.ownerName),
|
||||
]),
|
||||
],
|
||||
),
|
||||
]),
|
||||
header: () =>
|
||||
videos?.map((v) =>
|
||||
h(NGridItem, () =>
|
||||
h(
|
||||
NCard,
|
||||
{ style: 'height: 330px;', embedded: true, size: 'small' },
|
||||
{
|
||||
cover: () =>
|
||||
h('div', { style: 'position: relative;height: 150px;' }, [
|
||||
h('img', {
|
||||
src: v.video.cover.replace('http://', 'https://'),
|
||||
referrerpolicy: 'no-referrer',
|
||||
style: 'max-height: 100%; object-fit: contain;cursor: pointer',
|
||||
onClick: () => window.open('https://www.bilibili.com/video/' + v.info.bvid, '_blank'),
|
||||
}),
|
||||
h(
|
||||
NButton,
|
||||
NSpace,
|
||||
{
|
||||
style: 'width: 100%;',
|
||||
text: true,
|
||||
onClick: () => window.open('https://www.bilibili.com/video/' + v.info.bvid, '_blank'),
|
||||
style: { position: 'relative', bottom: '20px', background: '#00000073' },
|
||||
justify: 'space-around',
|
||||
},
|
||||
() =>
|
||||
h(
|
||||
NEllipsis,
|
||||
{ style: 'max-width: 100%;' },
|
||||
{
|
||||
default: () => v.video.title,
|
||||
tooltip: () => h('div', { style: 'max-width: 300px' }, v.video.title),
|
||||
},
|
||||
),
|
||||
),
|
||||
default: () =>
|
||||
h(NScrollbar, { style: 'height: 65px;' }, () =>
|
||||
h(NCard, { contentStyle: 'padding: 5px;' }, () =>
|
||||
v.info.senders.map((s) => [
|
||||
h('div', { style: 'font-size: 12px;' }, [
|
||||
h('div', `推荐人: ${s.sender ?? '未填写'} [${s.senderId ?? '未填写'}]`),
|
||||
h('div', `推荐理由: ${s.description ?? '未填写'}`),
|
||||
]),
|
||||
h(NSpace, { style: 'margin: 0;' }),
|
||||
() => [
|
||||
h('span', [
|
||||
h(NIcon, { component: Clock24Filled, color: 'lightgrey' }),
|
||||
h(NText, { style: 'color: lightgrey;size:small;' }, () => formatSeconds(v.video.length)),
|
||||
]),
|
||||
),
|
||||
h('span', [
|
||||
h(NIcon, { component: Person24Filled, color: 'lightgrey' }),
|
||||
h(NText, { style: 'color: lightgrey;size:small;' }, () => v.video.ownerName),
|
||||
]),
|
||||
],
|
||||
),
|
||||
footer: () => footer(v.info),
|
||||
},
|
||||
),
|
||||
]),
|
||||
header: () =>
|
||||
h(
|
||||
NButton,
|
||||
{
|
||||
style: 'width: 100%;',
|
||||
text: true,
|
||||
onClick: () => window.open('https://www.bilibili.com/video/' + v.info.bvid, '_blank'),
|
||||
},
|
||||
() =>
|
||||
h(
|
||||
NEllipsis,
|
||||
{ style: 'max-width: 100%;' },
|
||||
{
|
||||
default: () => v.video.title,
|
||||
tooltip: () => h('div', { style: 'max-width: 300px' }, v.video.title),
|
||||
},
|
||||
),
|
||||
),
|
||||
default: () =>
|
||||
h(NScrollbar, { style: 'height: 65px;' }, () =>
|
||||
h(NCard, { contentStyle: 'padding: 5px;' }, () =>
|
||||
v.info.senders.map((s) => [
|
||||
h('div', { style: 'font-size: 12px;' }, [
|
||||
h('div', `推荐人: ${s.sender ?? '未填写'} [${s.senderId ?? '未填写'}]`),
|
||||
h('div', `推荐理由: ${s.description ?? '未填写'}`),
|
||||
]),
|
||||
h(NSpace, { style: 'margin: 0;' }),
|
||||
]),
|
||||
),
|
||||
),
|
||||
footer: () => footer(v.info),
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
),
|
||||
)
|
||||
}
|
||||
const paddingButtonGroup = (v: VideoInfo) =>
|
||||
h(NSpace, { size: 'small', justify: 'space-around' }, () => [
|
||||
@@ -395,10 +395,8 @@ onActivated(async () => {
|
||||
<NButton type="warning" size="small" @click="closeTable">
|
||||
{{ videoDetail.table.isFinish ? '开启表' : '关闭表' }}
|
||||
</NButton>
|
||||
<NButton
|
||||
size="small"
|
||||
@click="$router.push({ name: 'video-collect-list', params: { id: videoDetail.table.id } })"
|
||||
>
|
||||
<NButton size="small"
|
||||
@click="$router.push({ name: 'video-collect-list', params: { id: videoDetail.table.id } })">
|
||||
结果表
|
||||
</NButton>
|
||||
<NPopconfirm :on-positive-click="deleteTable">
|
||||
@@ -447,14 +445,9 @@ onActivated(async () => {
|
||||
</NTabs>
|
||||
</template>
|
||||
<NModal v-model:show="shareModalVisiable" title="分享" preset="card" style="width: 600px; max-width: 90vw">
|
||||
<Qrcode
|
||||
:value="'https://vtsuru.live/video-collect/' + videoDetail.table.shortId"
|
||||
level="Q"
|
||||
:size="100"
|
||||
background="#fff"
|
||||
:margin="1"
|
||||
/>
|
||||
<NInput :value="'https://vtsuru.live/video-collect/' + videoDetail.table.shortId" />
|
||||
<Qrcode :value="`${CURRENT_HOST}video-collect/` + videoDetail.table.shortId" level="Q" :size="100" background="#fff"
|
||||
:margin="1" />
|
||||
<NInput :value="`${CURRENT_HOST}video-collect/` + videoDetail.table.shortId" />
|
||||
<NDivider />
|
||||
<NSpace justify="center">
|
||||
<NButton type="primary" @click="saveQRCode"> 保存二维码 </NButton>
|
||||
@@ -469,20 +462,12 @@ onActivated(async () => {
|
||||
<NInput v-model:value="updateModel.description" placeholder="可以是备注之类的" maxlength="300" show-count />
|
||||
</NFormItem>
|
||||
<NFormItem label="视频数量" path="maxVideoCount">
|
||||
<NInputNumber
|
||||
v-model:value="updateModel.maxVideoCount"
|
||||
placeholder="最大数量"
|
||||
type="number"
|
||||
style="max-width: 150px"
|
||||
/>
|
||||
<NInputNumber v-model:value="updateModel.maxVideoCount" placeholder="最大数量" type="number"
|
||||
style="max-width: 150px" />
|
||||
</NFormItem>
|
||||
<NFormItem label="结束时间" path="endAt">
|
||||
<NDatePicker
|
||||
v-model:value="updateModel.endAt"
|
||||
type="datetime"
|
||||
placeholder="结束征集的时间"
|
||||
:isDateDisabled="dateDisabled"
|
||||
/>
|
||||
<NDatePicker v-model:value="updateModel.endAt" type="datetime" placeholder="结束征集的时间"
|
||||
:isDateDisabled="dateDisabled" />
|
||||
<NDivider vertical />
|
||||
<NText depth="3"> 最低为一小时 </NText>
|
||||
</NFormItem>
|
||||
|
||||
Reference in New Issue
Block a user