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:
2024-11-23 18:46:37 +08:00
parent 14267bab3a
commit 47ade4a965
33 changed files with 838 additions and 1119 deletions

View 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>

View File

@@ -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;

View File

@@ -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>

View File

@@ -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>