add switch

This commit is contained in:
2024-02-21 12:04:41 +08:00
parent 76d1838768
commit aa7fbd47a0
65 changed files with 2220 additions and 806 deletions

View File

@@ -2,7 +2,19 @@
import { GetSelfAccount, useAccount } from '@/api/account'
import { QueryGetAPI } from '@/api/query'
import { BILI_API_URL } from '@/data/constants'
import { NAlert, NButton, NCard, NCountdown, NInput, NInputGroup, NInputNumber, NSpace, NSpin, NText, useMessage } from 'naive-ui'
import {
NAlert,
NButton,
NCard,
NCountdown,
NInput,
NInputGroup,
NInputNumber,
NSpace,
NSpin,
NText,
useMessage,
} from 'naive-ui'
import { onMounted, ref } from 'vue'
const message = useMessage()
@@ -102,7 +114,9 @@ onMounted(async () => {
<NInput :allow-input="() => false" v-model:value="accountInfo.biliVerifyCode" />
<NButton @click="copyCode"> 复制认证码 </NButton>
</NInputGroup>
<NButton v-if="roomId" type="primary" tag="a" :href="'https://live.bilibili.com/' + roomId" target="_blank"> 前往直播间 </NButton>
<NButton v-if="roomId" type="primary" tag="a" :href="'https://live.bilibili.com/' + roomId" target="_blank">
前往直播间
</NButton>
</NSpace>
</template>
<template v-else>

View File

@@ -30,6 +30,11 @@ onUnmounted(() => {
<NAlert v-if="accountInfo?.isBiliVerified != true" type="info"> 尚未进行Bilibili认证 </NAlert>
<NSpin v-else-if="isClientLoading" show />
<KeepAlive v-else>
<component :is="component" :client="client" :room-info="client.roomAuthInfo?.value" :code="accountInfo?.biliAuthCode" />
<component
:is="component"
:client="client"
:room-info="client.roomAuthInfo?.value"
:code="accountInfo?.biliAuthCode"
/>
</KeepAlive>
</template>

View File

@@ -34,7 +34,7 @@ import {
NUl,
useMessage,
} from 'naive-ui'
import { computed, onMounted, ref } from 'vue'
import { computed, ref } from 'vue'
enum EventType {
Guard,
@@ -57,7 +57,10 @@ const rangeShortcuts = {
上个月: () => {
const cur = new Date()
const lastMonth = new Date(cur.getFullYear(), cur.getMonth() - 1)
return [new Date(cur.getFullYear(), cur.getMonth() - 1, 1).getTime(), new Date(cur.getFullYear(), cur.getMonth(), 1).getTime()] as const
return [
new Date(cur.getFullYear(), cur.getMonth() - 1, 1).getTime(),
new Date(cur.getFullYear(), cur.getMonth(), 1).getTime(),
] as const
},
本月: () => {
const cur = new Date()
@@ -143,8 +146,9 @@ function exportData() {
}
saveAs(
new Blob([text], { type: 'text/plain;charset=utf-8' }),
`${format(Date.now(), 'yyyy-MM-dd HH:mm:ss')}_${format(selectedDate.value[0], 'yyyy-MM-dd HH:mm:ss')}_${format(selectedDate.value[1], 'yyyy-MM-dd HH:mm:ss')}}_${accountInfo.value
?.id}_${accountInfo.value?.name}_${selectedType.value}.${exportType.value}`,
`${format(Date.now(), 'yyyy-MM-dd HH:mm:ss')}_${format(selectedDate.value[0], 'yyyy-MM-dd HH:mm:ss')}_${format(selectedDate.value[1], 'yyyy-MM-dd HH:mm:ss')}}_${
accountInfo.value?.id
}_${accountInfo.value?.name}_${selectedType.value}.${exportType.value}`,
)
}
function objectsToCSV(arr: any[]) {
@@ -163,12 +167,19 @@ function objectsToCSV(arr: any[]) {
<template>
<NSpace vertical>
<NAlert v-if="!accountInfo?.isBiliVerified" type="warning"> 使用此功能前你需要先<NButton type="info" text @click="$router.push({ name: 'manage-biliVerify' })">认证Bilibili账号</NButton> </NAlert>
<NAlert v-if="!accountInfo?.isBiliVerified" type="warning">
使用此功能前你需要先<NButton type="info" text @click="$router.push({ name: 'manage-biliVerify' })"
>认证Bilibili账号</NButton
>
</NAlert>
<NAlert type="info">
当前本站正在测试为粉丝数大于 1000 或至少拥有一位舰长的主播直接从服务端记录并储存弹幕数据, 不过并不清楚B站的风控策略, 此功能不一定会长期启用
当前本站正在测试为粉丝数大于 1000 或至少拥有一位舰长的主播直接从服务端记录并储存弹幕数据,
不过并不清楚B站的风控策略, 此功能不一定会长期启用
<br />
在我们被限制连接之前满足以上条件的主播无需部署
<NButton tag="a" href="https://www.yuque.com/megghy/dez70g/vfvcyv3024xvaa1p" target="_blank" type="primary" text> VtsuruEventFetcher </NButton>
<NButton tag="a" href="https://www.yuque.com/megghy/dez70g/vfvcyv3024xvaa1p" target="_blank" type="primary" text>
VtsuruEventFetcher
</NButton>
即可使用相关功能 (如记录上舰和SC, 直播场记录等) 😊
</NAlert>
<EventFetcherStatusCard />
@@ -177,7 +188,14 @@ function objectsToCSV(arr: any[]) {
<NCard size="small" style="witdh: 100%">
<template v-if="accountInfo?.isBiliVerified">
<NSpace justify="center" align="center">
<NDatePicker v-model:value="selectedDate" @update:value="onDateChange" type="datetimerange" :shortcuts="rangeShortcuts" start-placeholder="开始时间" end-placeholder="结束时间" />
<NDatePicker
v-model:value="selectedDate"
@update:value="onDateChange"
type="datetimerange"
:shortcuts="rangeShortcuts"
start-placeholder="开始时间"
end-placeholder="结束时间"
/>
<NRadioGroup v-model:value="selectedType">
<NRadioButton :value="EventType.Guard">舰长</NRadioButton>
<NRadioButton :value="EventType.SC">Superchat</NRadioButton>
@@ -209,15 +227,32 @@ function objectsToCSV(arr: any[]) {
<div v-if="displayMode == 'grid'">
<NGrid cols="1 500:2 800:3 1000:4" :x-gap="12" :y-gap="8">
<NGridItem v-for="item in selectedEvents" v-bind:key="item.time">
<NCard size="small" :style="`height: ${selectedType == EventType.Guard ? '175px' : '220'}px`" embedded hoverable>
<NCard
size="small"
:style="`height: ${selectedType == EventType.Guard ? '175px' : '220'}px`"
embedded
hoverable
>
<NSpace align="center" vertical :size="5">
<NAvatar round lazy borderd :size="64" :src="AVATAR_URL + item.uid" :img-props="{ referrerpolicy: 'no-referrer' }" style="box-shadow: 0 3px 5px rgba(0, 0, 0, 0.2)" />
<NAvatar
round
lazy
borderd
:size="64"
:src="AVATAR_URL + item.uid"
:img-props="{ referrerpolicy: 'no-referrer' }"
style="box-shadow: 0 3px 5px rgba(0, 0, 0, 0.2)"
/>
<NSpace>
<NTag size="tiny" v-if="selectedType == EventType.Guard" :bordered="false"> {{ item.msg }} </NTag>
<NTag
size="tiny"
round
:color="{ color: selectedType == EventType.Guard ? GetGuardColor(item.price) : GetSCColor(item.price), textColor: 'white', borderColor: isDarkMode() ? 'white' : '#00000000' }"
:color="{
color: selectedType == EventType.Guard ? GetGuardColor(item.price) : GetSCColor(item.price),
textColor: 'white',
borderColor: isDarkMode() ? 'white' : '#00000000',
}"
>
{{ item.price }}
</NTag>
@@ -252,7 +287,15 @@ function objectsToCSV(arr: any[]) {
<td><NTime :time="item.time" /></td>
<td v-if="selectedType == EventType.Guard">{{ item.msg }}</td>
<td>
<NTag :color="{ color: selectedType == EventType.Guard ? GetGuardColor(item.price) : GetSCColor(item.price), textColor: 'white', borderColor: 'white' }"> {{ item.price }} </NTag>
<NTag
:color="{
color: selectedType == EventType.Guard ? GetGuardColor(item.price) : GetSCColor(item.price),
textColor: 'white',
borderColor: 'white',
}"
>
{{ item.price }}
</NTag>
</td>
<td v-if="selectedType == EventType.SC">
<NEllipsis style="max-width: 300px">{{ item.msg }}</NEllipsis>
@@ -265,8 +308,13 @@ function objectsToCSV(arr: any[]) {
</template>
<template v-else>
<NCollapse :default-expanded-names="['1']">
<NCollapseItem title="这是什么?" name="1"> 可以查看曾经收到的Superchat以及上舰记录, 并导出为 CSV 之类的表格 </NCollapseItem>
<NCollapseItem title="可以直接用吗"> 遗憾的是并不能, 使用这个功能需要你拥有一个可以7*24小时运行 Docker 容器或者 Node.js 脚本的环境, 并且可以访问互联网 </NCollapseItem>
<NCollapseItem title="这是什么?" name="1">
可以查看曾经收到的Superchat以及上舰记录, 并导出为 CSV 之类的表格
</NCollapseItem>
<NCollapseItem title="可以直接用吗">
遗憾的是并不能, 使用这个功能需要你拥有一个可以7*24小时运行 Docker 容器或者 Node.js 脚本的环境,
并且可以访问互联网
</NCollapseItem>
<NCollapseItem title="有没有什么要求?">
关于环境的话理论上能够运行 Docker 或者 Node.js 的环境都能可以
<br /><br />
@@ -278,13 +326,18 @@ function objectsToCSV(arr: any[]) {
<NLi>拥有掌握以上技能的 stf 或者朋友</NLi>
</NUl>
<NH3>
<NText strong> 即使你对相关知识一窍不通也不用担心, 跟着后面的傻瓜教程中的 Koyeb 也可以完成部署. 理论上这玩意里头的免费套餐就够用了, 当然如果你想要更稳一点上个付费套餐也不影响 </NText>
<NText strong>
即使你对相关知识一窍不通也不用担心, 跟着后面的傻瓜教程中的 Koyeb 也可以完成部署.
理论上这玩意里头的免费套餐就够用了, 当然如果你想要更稳一点上个付费套餐也不影响
</NText>
</NH3>
</NCollapseItem>
</NCollapse>
<NDivider style="margin-bottom: 10px" />
<NSpace justify="center">
<NButton tag="a" href="https://www.yuque.com/megghy/dez70g/vfvcyv3024xvaa1p" target="_blank" type="primary"> 部署指南 </NButton>
<NButton tag="a" href="https://www.yuque.com/megghy/dez70g/vfvcyv3024xvaa1p" target="_blank" type="primary">
部署指南
</NButton>
</NSpace>
</template>
</NCard>

View File

@@ -15,7 +15,7 @@ import {
} from 'echarts/components'
import { use } from 'echarts/core'
import { CanvasRenderer } from 'echarts/renderers'
import { NAlert, NButton, NCard, NDivider, NIcon, NSpace, NSpin, NText, NTime, NTooltip, useMessage } from 'naive-ui'
import { NAlert, NButton, NCard, NDivider, NIcon, NSpace, NSpin, NTime, NTooltip, useMessage } from 'naive-ui'
import { onMounted, ref } from 'vue'
import VChart from 'vue-echarts'
@@ -97,8 +97,8 @@ function isSameDaySimple(time1: number, time2: number) {
const statisticStartDate = new Date(2023, 10, 4)
const statisticStartDateTime = statisticStartDate.getTime()
function getOptions() {
let fansIncreacement = [] as { time: Date; count: number }[]
let completeTimeSeries: {
const fansIncreacement = [] as { time: Date; count: number }[]
const completeTimeSeries: {
time: Date
count: number
change: boolean
@@ -147,7 +147,7 @@ function getOptions() {
completeTimeSeries.forEach((entry, index, array) => {
if (index === 0 || !isSameDay(entry.time, array[index - 1].time)) {
if (index > 0) {
let dailyIncrement = entry.count - previousDayCount
const dailyIncrement = entry.count - previousDayCount
fansIncreacement.push({
time: startOfHour(array[index - 1].time),
count: dailyIncrement,
@@ -156,7 +156,7 @@ function getOptions() {
}
previousDayCount = entry.count
} else if (index === array.length - 1) {
let dailyIncrement = entry.count - previousDayCount
const dailyIncrement = entry.count - previousDayCount
fansIncreacement.push({
time: startOfHour(entry.time),
count: dailyIncrement,
@@ -168,19 +168,19 @@ function getOptions() {
let lastDayGuards = 0
let lastDay = 0
let guardsIncreacement = [] as { time: number; count: number; timeString: string }[]
let guards = [] as { time: number; count: number; timeString: string }[]
const guardsIncreacement = [] as { time: number; count: number; timeString: string }[]
const guards = [] as { time: number; count: number; timeString: string }[]
// 生成完整的天序列
let currentGuardTime = startTime
let lastDayGuardCount = 0
let completeGuardTimeSeries: {
const completeGuardTimeSeries: {
time: Date
count: number
}[] = []
while (currentGuardTime <= endTime) {
let found = guardHistory.value?.find((f) => isSameDay(currentGuardTime, f.time))
let count = found ? found.count : lastDayGuardCount
const found = guardHistory.value?.find((f) => isSameDay(currentGuardTime, f.time))
const count = found ? found.count : lastDayGuardCount
lastDayGuardCount = count
completeGuardTimeSeries.push({
@@ -208,8 +208,8 @@ function getOptions() {
lastDayGuards = g.count
}
})
let upstatViewIncreace: { time: number; value: number }[] = []
let upstatLikeIncreace: { time: number; value: number }[] = []
const upstatViewIncreace: { time: number; value: number }[] = []
const upstatLikeIncreace: { time: number; value: number }[] = []
if (upstatHistory.value && upstatHistory.value.length > 0) {
let lastUpstatView = upstatHistory.value[0].stats.views
let lastUpstatLike = upstatHistory.value[0].stats.likes
@@ -247,7 +247,7 @@ function getOptions() {
},
},
formatter: (param: any) => {
let name = param[0].name + '<br>'
const name = param[0].name + '<br>'
let str = ''
for (var i = 0; i < param.length; i++) {
const status =

View File

@@ -6,7 +6,7 @@ import { onMounted, onUnmounted } from 'vue'
import OpenLottery from '../open_live/OpenLottery.vue'
const accountInfo = useAccount()
let client = new DanmakuClient(null)
const client = new DanmakuClient(null)
onMounted(() => {
client.Start()

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
import { copyToClipboard, downloadImage } from '@/Utils'
import { SaveAccountSettings, useAccount } from '@/api/account'
import { QAInfo } from '@/api/api-models'
import { DisableFunction, EnableFunction, SaveAccountSettings, useAccount } from '@/api/account'
import { FunctionTypes, QAInfo } from '@/api/api-models'
import { QueryGetAPI } from '@/api/query'
import { QUESTION_API_URL } from '@/data/constants'
import router from '@/router'
@@ -10,6 +10,7 @@ import { saveAs } from 'file-saver'
import html2canvas from 'html2canvas'
import {
NAffix,
NAlert,
NButton,
NCard,
NCheckbox,
@@ -123,6 +124,20 @@ async function saveSettings() {
const parentRef = ref<HTMLElement | null>(null)
async function setFunctionEnable(enable: boolean) {
let success = false
if (enable) {
success = await EnableFunction(FunctionTypes.QuestionBox)
} else {
success = await DisableFunction(FunctionTypes.QuestionBox)
}
if (success) {
message.success('已' + (enable ? '启用' : '禁用'))
} else {
message.error('无法' + (enable ? '启用' : '禁用'))
}
}
onMounted(() => {
if (selectedTabItem.value == '0') {
useQB.GetRecieveQAInfo()
@@ -137,7 +152,15 @@ onMounted(() => {
</script>
<template>
<NSpace>
<NSpace align="center">
<NAlert type="info" style="max-width: 200px">
启用提问箱
<NDivider vertical />
<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>
</NSpace>
@@ -145,9 +168,7 @@ onMounted(() => {
<NSpin v-if="useQB.isLoading" show />
<NTabs v-else animated @update:value="onTabChange" v-model:value="selectedTabItem">
<NTabPane tab="我收到的" name="0">
<NButton @click="$router.push({ name: 'question-display' })" type="primary">
打开展示页
</NButton>
<NButton @click="$router.push({ name: 'question-display' })" type="primary"> 打开展示页 </NButton>
<NDivider vertical />
<NCheckbox v-model:checked="useQB.onlyFavorite"> 只显示收藏 </NCheckbox>
<NCheckbox v-model:checked="useQB.onlyPublic"> 只显示公开 </NCheckbox>

View File

@@ -1,11 +1,27 @@
<script setup lang="ts">
import { useAccount } from '@/api/account'
import { ScheduleWeekInfo } from '@/api/api-models'
import { DisableFunction, EnableFunction, useAccount } from '@/api/account'
import { FunctionTypes, ScheduleWeekInfo } from '@/api/api-models'
import { QueryGetAPI, QueryPostAPI } from '@/api/query'
import ScheduleList from '@/components/ScheduleList.vue'
import { SCHEDULE_API_URL } from '@/data/constants'
import { addWeeks, endOfWeek, endOfYear, format, isBefore, startOfWeek, startOfYear } from 'date-fns'
import { NAlert, NBadge, NButton, NColorPicker, NDivider, NInput, NInputGroup, NInputGroupLabel, NModal, NSelect, NSpace, NSpin, NTimePicker, useMessage } from 'naive-ui'
import {
NAlert,
NBadge,
NButton,
NColorPicker,
NDivider,
NInput,
NInputGroup,
NInputGroupLabel,
NModal,
NSelect,
NSpace,
NSpin,
NSwitch,
NTimePicker,
useMessage,
} from 'naive-ui'
import { SelectMixedOption, SelectOption } from 'naive-ui/es/select/src/interface'
import { VNode, computed, h, onMounted, ref } from 'vue'
@@ -40,10 +56,11 @@ const yearOptions = [
},
] as SelectMixedOption[]
const weekOptions = computed(() => {
let weeks = [] as SelectMixedOption[]
const weeks = [] as SelectMixedOption[]
const all = getAllWeeks(selectedScheduleYear.value)
all.forEach((week) => {
const isExist = (schedules.value?.findIndex((s) => s.year == selectedScheduleYear.value && s.week == week[0] + 1) ?? -1) > -1
const isExist =
(schedules.value?.findIndex((s) => s.year == selectedScheduleYear.value && s.week == week[0] + 1) ?? -1) > -1
weeks.push({
label: `${isExist ? '(已安排)' : ''}${week[0] + 1}周 (${week[1]})`,
value: week[0] + 1,
@@ -53,7 +70,7 @@ const weekOptions = computed(() => {
return weeks
})
const dayOptions = computed(() => {
let days = [] as SelectMixedOption[]
const days = [] as SelectMixedOption[]
for (let i = 0; i < 7; i++) {
try {
days.push({
@@ -67,7 +84,7 @@ const dayOptions = computed(() => {
return days
})
const existTagOptions = computed(() => {
let colors = [] as SelectMixedOption[]
const colors = [] as SelectMixedOption[]
schedules.value?.forEach((s) => {
s.days.forEach((d) => {
if (d.tag && !colors.find((c) => c.value == d.tagColor && c.label == d.tag)) {
@@ -85,7 +102,7 @@ function getAllWeeks(year: number) {
const endDate = endOfYear(new Date(year, 11, 31))
let date = startOfWeek(startDate, { weekStartsOn: 1 })
let weeks: [number, string][] = []
const weeks: [number, string][] = []
let index = 0
@@ -175,7 +192,9 @@ async function onUpdateSchedule() {
.then((data) => {
if (data.code == 200) {
message.success('成功')
const s = schedules.value?.find((s) => s.year == selectedScheduleYear.value && s.week == selectedScheduleWeek.value)
const s = schedules.value?.find(
(s) => s.year == selectedScheduleYear.value && s.week == selectedScheduleWeek.value,
)
if (s) {
s.days[selectedDay.value] = updateScheduleModel.value.days[selectedDay.value]
} else {
@@ -218,7 +237,23 @@ function onSelectChange(value: string | null, option: SelectMixedOption) {
}
}
const renderOption = ({ node, option }: { node: VNode; option: SelectOption }) =>
h(NSpace, { align: 'center', size: 3, style: 'margin-left: 5px' }, () => [option.value ? h(NBadge, { dot: true, color: option.value?.toString() }) : null, node])
h(NSpace, { align: 'center', size: 3, style: 'margin-left: 5px' }, () => [
option.value ? h(NBadge, { dot: true, color: option.value?.toString() }) : null,
node,
])
async function setFunctionEnable(enable: boolean) {
let success = false
if (enable) {
success = await EnableFunction(FunctionTypes.Schedule)
} else {
success = await DisableFunction(FunctionTypes.Schedule)
}
if (success) {
message.success('已' + (enable ? '启用' : '禁用'))
} else {
message.error('无法' + (enable ? '启用' : '禁用'))
}
}
onMounted(() => {
get()
@@ -226,9 +261,19 @@ onMounted(() => {
</script>
<template>
<NSpace>
<NSpace align="center">
<NAlert type="info" style="max-width: 200px">
启用日程表
<NDivider vertical />
<NSwitch
:value="accountInfo?.settings.enableFunctions.includes(FunctionTypes.Schedule)"
@update:value="setFunctionEnable"
/>
</NAlert>
<NButton @click="showAddModal = true" type="primary"> 添加周程 </NButton>
<NButton @click="$router.push({ name: 'manage-index', query: { tab: 'template', template: 'schedule' } })"> 修改模板 </NButton>
<NButton @click="$router.push({ name: 'manage-index', query: { tab: 'template', template: 'schedule' } })">
修改模板
</NButton>
</NSpace>
<NDivider />
<NModal v-model:show="showAddModal" style="width: 600px; max-width: 90vw" preset="card" title="添加周程">
@@ -260,7 +305,13 @@ onMounted(() => {
<NSpace>
<NInputGroup>
<NInputGroupLabel type="primary"> 标签 </NInputGroupLabel>
<NInput v-model:value="updateScheduleModel.days[selectedDay].tag" placeholder="标签 | 留空视为无安排" style="max-width: 300px" maxlength="10" show-count />
<NInput
v-model:value="updateScheduleModel.days[selectedDay].tag"
placeholder="标签 | 留空视为无安排"
style="max-width: 300px"
maxlength="10"
show-count
/>
</NInputGroup>
<NSelect
v-model:value="selectedExistTag"
@@ -275,9 +326,19 @@ onMounted(() => {
</NSpace>
<NInputGroup>
<NInputGroupLabel> 内容 </NInputGroupLabel>
<NInput v-model:value="updateScheduleModel.days[selectedDay].title" placeholder="内容" style="max-width: 200px" maxlength="30" show-count />
<NInput
v-model:value="updateScheduleModel.days[selectedDay].title"
placeholder="内容"
style="max-width: 200px"
maxlength="30"
show-count
/>
</NInputGroup>
<NTimePicker default-formatted-value="20:00" v-model:formatted-value="updateScheduleModel.days[selectedDay].time" format="HH:mm" />
<NTimePicker
default-formatted-value="20:00"
v-model:formatted-value="updateScheduleModel.days[selectedDay].time"
format="HH:mm"
/>
<NColorPicker
v-model:value="updateScheduleModel.days[selectedDay].tagColor"
:swatches="['#FFFFFF', '#18A058', '#2080F0', '#F0A020', 'rgba(208, 48, 80, 1)']"
@@ -290,5 +351,12 @@ onMounted(() => {
</template>
</NModal>
<NSpin v-if="isLoading" show />
<ScheduleList v-else :schedules="schedules ?? []" @on-update="onOpenUpdateModal" @on-delete="onDeleteSchedule" @on-copy="onOpenCopyModal" is-self />
<ScheduleList
v-else
:schedules="schedules ?? []"
@on-update="onOpenUpdateModal"
@on-delete="onDeleteSchedule"
@on-copy="onOpenCopyModal"
is-self
/>
</template>

View File

@@ -1,10 +1,35 @@
<script setup lang="ts">
import { DelBiliBlackList, SaveAccountSettings, SaveEnableFunctions, downloadConfigDirect, useAccount } from '@/api/account'
import {
DelBiliBlackList,
SaveAccountSettings,
SaveEnableFunctions,
downloadConfigDirect,
useAccount,
} from '@/api/account'
import { FunctionTypes, ScheduleWeekInfo, SongFrom, SongLanguage, SongRequestOption, SongsInfo } from '@/api/api-models'
import DynamicForm from '@/components/DynamicForm.vue'
import { TemplateConfig } from '@/data/VTsuruTypes'
import { FETCH_API, IndexTemplateMap, ScheduleTemplateMap, SongListTemplateMap } from '@/data/constants'
import { NAlert, NButton, NCard, NCheckbox, NCheckboxGroup, NDivider, NEmpty, NList, NListItem, NModal, NSelect, NSpace, NSpin, NTabPane, NTabs, NText, SelectOption, useMessage } from 'naive-ui'
import {
NAlert,
NButton,
NCard,
NCheckbox,
NCheckboxGroup,
NDivider,
NEmpty,
NList,
NListItem,
NModal,
NSelect,
NSpace,
NSpin,
NTabPane,
NTabs,
NText,
SelectOption,
useMessage,
} from 'naive-ui'
import { computed, h, nextTick, onActivated, onMounted, ref } from 'vue'
import { useRoute } from 'vue-router'
@@ -188,7 +213,9 @@ const selectedTab = ref(route.query.tab?.toString() ?? 'general')
const dynamicConfigRef = ref()
const selectedTemplateData = computed(() => templates.value[selectedOption.value])
const selectedComponent = computed(() => selectedTemplateData.value?.TemplateMap[selectedTemplateData.value.Selected].compoent)
const selectedComponent = computed(
() => selectedTemplateData.value?.TemplateMap[selectedTemplateData.value.Selected].compoent,
)
const selectedTemplateConfig = computed(() => {
if (dynamicConfigRef.value?.Config) {
return dynamicConfigRef.value?.Config as TemplateConfig<any>
@@ -201,7 +228,7 @@ const settingModalVisiable = ref(false)
async function RequestBiliUserData() {
await fetch(FETCH_API + `https://account.bilibili.com/api/member/getCardByMid?mid=10021741`).then(async (respone) => {
let data = await respone.json()
const data = await respone.json()
if (data.code == 0) {
biliUserInfo.value = data.card
} else {
@@ -209,7 +236,10 @@ async function RequestBiliUserData() {
}
})
}
async function SaveComboGroupSetting(value: (string | number)[], meta: { actionType: 'check' | 'uncheck'; value: string | number }) {
async function SaveComboGroupSetting(
value: (string | number)[],
meta: { actionType: 'check' | 'uncheck'; value: string | number },
) {
if (accountInfo.value) {
isSaving.value = true
//UpdateEnableFunction(meta.value as FunctionTypes, meta.actionType == 'check')
@@ -220,12 +250,14 @@ async function SaveComboGroupSetting(value: (string | number)[], meta: { actionT
} else {
message.error('修改失败')
if (accountInfo.value) {
accountInfo.value.settings.enableFunctions = accountInfo.value.settings.enableFunctions.filter((f) => f != (meta.value as FunctionTypes))
accountInfo.value.settings.enableFunctions = accountInfo.value.settings.enableFunctions.filter(
(f) => f != (meta.value as FunctionTypes),
)
}
}
})
.catch((err) => {
message.error('修改失败')
message.error('修改失败: ' + err)
})
.finally(() => {
isSaving.value = false
@@ -245,7 +277,7 @@ async function SaveComboSetting() {
}
})
.catch((err) => {
message.error('修改失败')
message.error('修改失败: ' + err)
})
.finally(() => {
isSaving.value = false
@@ -300,7 +332,10 @@ async function getTemplateConfig() {
}
}
const buttonGroup = computed(() => {
return h(NSpace, () => [h(NButton, { type: 'primary', onClick: () => SaveTemplateSetting() }, () => '设为展示模板'), h(NButton, { type: 'info', onClick: onOpenTemplateSettings }, () => '模板设置')])
return h(NSpace, () => [
h(NButton, { type: 'primary', onClick: () => SaveTemplateSetting() }, () => '设为展示模板'),
h(NButton, { type: 'info', onClick: onOpenTemplateSettings }, () => '模板设置'),
])
})
function unblockUser(id: number) {
@@ -348,12 +383,24 @@ onMounted(async () => {
</NCheckboxGroup>
<NDivider> 通知 </NDivider>
<NSpace>
<NCheckbox v-model:checked="accountInfo.settings.sendEmail.recieveQA" @update:checked="SaveComboSetting"> 收到新提问时发送邮件 </NCheckbox>
<NCheckbox v-model:checked="accountInfo.settings.sendEmail.recieveQAReply" @update:checked="SaveComboSetting"> 提问收到回复时发送邮件 </NCheckbox>
<NCheckbox v-model:checked="accountInfo.settings.sendEmail.recieveQA" @update:checked="SaveComboSetting">
收到新提问时发送邮件
</NCheckbox>
<NCheckbox
v-model:checked="accountInfo.settings.sendEmail.recieveQAReply"
@update:checked="SaveComboSetting"
>
提问收到回复时发送邮件
</NCheckbox>
</NSpace>
<NDivider> 提问箱 </NDivider>
<NSpace>
<NCheckbox v-model:checked="accountInfo.settings.questionBox.allowUnregistedUser" @update:checked="SaveComboSetting"> 允许未注册用户提问 </NCheckbox>
<NCheckbox
v-model:checked="accountInfo.settings.questionBox.allowUnregistedUser"
@update:checked="SaveComboSetting"
>
允许未注册用户提问
</NCheckbox>
</NSpace>
</NTabPane>
<NTabPane tab="黑名单" name="blacklist">
@@ -373,22 +420,32 @@ onMounted(async () => {
<NEmpty v-else />
</NTabPane>
<NTabPane tab="模板" name="template">
<NAlert type="success">
如果有合适的设计稿或者想法可以给我说然后做成模板捏
</NAlert>
<br/>
<NAlert type="success"> 如果有合适的设计稿或者想法可以给我说然后做成模板捏 </NAlert>
<br />
<NSpace vertical>
<NSpace align="center"> 页面 <NSelect :options="templateOptions" v-model:value="selectedOption" style="width: 150px" /> </NSpace>
<NSpace align="center">
页面 <NSelect :options="templateOptions" v-model:value="selectedOption" style="width: 150px" />
</NSpace>
<NDivider style="margin: 5px 0 5px 0" title-placement="left"> 模板 </NDivider>
<div>
<NSpace>
<NSelect style="width: 150px" :options="selectedTemplateData.Options" v-model:value="selectedTemplateData.Selected" />
<NSelect
style="width: 150px"
:options="selectedTemplateData.Options"
v-model:value="selectedTemplateData.Selected"
/>
<component :is="buttonGroup" />
</NSpace>
<NDivider />
<Transition name="fade" mode="out-in">
<div v-if="selectedComponent" :key="selectedTemplateData.Selected">
<component ref="dynamicConfigRef" :is="selectedComponent" :user-info="accountInfo" :bili-info="biliUserInfo" :current-data="selectedTemplateData.Data" />
<component
ref="dynamicConfigRef"
:is="selectedComponent"
:user-info="accountInfo"
:bili-info="biliUserInfo"
:current-data="selectedTemplateData.Data"
/>
</div>
</Transition>
</div>
@@ -397,9 +454,20 @@ onMounted(async () => {
</NTabs>
</NSpin>
</NCard>
<NModal preset="card" v-model:show="settingModalVisiable" closable style="width: 600px; max-width: 90vw" title="模板设置">
<NModal
preset="card"
v-model:show="settingModalVisiable"
closable
style="width: 600px; max-width: 90vw"
title="模板设置"
>
只是测试, 没用
<NSpin v-if="!selectedTemplateData.Config" show />
<DynamicForm v-else :key="selectedTemplateData.Selected" :configData="selectedTemplateData.Config" :config="selectedTemplateConfig" />
<DynamicForm
v-else
:key="selectedTemplateData.Selected"
:configData="selectedTemplateData.Config"
:config="selectedTemplateConfig"
/>
</NModal>
</template>

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
import { objectsToCSV } from '@/Utils'
import { useAccount } from '@/api/account'
import { SongFrom, SongLanguage, SongRequestOption, SongsInfo } from '@/api/api-models'
import { DisableFunction, EnableFunction, useAccount } from '@/api/account'
import { FunctionTypes, SongFrom, SongLanguage, SongRequestOption, SongsInfo } from '@/api/api-models'
import { QueryGetAPI, QueryPostAPI } from '@/api/query'
import SongList from '@/components/SongList.vue'
import { FETCH_API, SONG_API_URL } from '@/data/constants'
@@ -31,6 +31,7 @@ import {
NSelect,
NSpace,
NSpin,
NSwitch,
NTabPane,
NTable,
NTabs,
@@ -545,6 +546,19 @@ function beforeUpload(data: { file: UploadFileInfo; fileList: UploadFileInfo[] }
message.error('只能选择xlsx和xls和csv')
return false
}
async function setFunctionEnable(enable: boolean) {
let success = false
if (enable) {
success = await EnableFunction(FunctionTypes.SongList)
} else {
success = await DisableFunction(FunctionTypes.SongList)
}
if (success) {
message.success('已' + (enable ? '启用' : '禁用'))
} else {
message.error('无法' + (enable ? '启用' : '禁用'))
}
}
onMounted(async () => {
await getSongs()
@@ -552,7 +566,15 @@ onMounted(async () => {
</script>
<template>
<NSpace>
<NSpace align="center">
<NAlert type="info" style="max-width: 200px">
启用歌单
<NDivider vertical />
<NSwitch
:value="accountInfo?.settings.enableFunctions.includes(FunctionTypes.SongList)"
@update:value="setFunctionEnable"
/>
</NAlert>
<NButton @click="showModal = true" type="primary"> 添加歌曲 </NButton>
<NButton @click="exportData" type="primary" secondary> 导出为 CSV </NButton>
<NButton
@@ -833,9 +855,9 @@ onMounted(async () => {
>
<NUploadDragger>
<div style="margin-bottom: 12px">
<n-icon size="48" :depth="3">
<NIcon size="48" :depth="3">
<ArchiveOutline />
</n-icon>
</NIcon>
</div>
<NText style="font-size: 16px"> 点击或者拖动文件到该区域来上传 </NText>
<NP depth="3" style="margin: 8px 0 0 0"> 仅限 Excel 文件(.xlsx和.xls) 以及 csv 文件 </NP>

View File

@@ -6,7 +6,7 @@ import { onMounted, onUnmounted } from 'vue'
import MusicRequest from '../open_live/MusicRequest.vue'
const accountInfo = useAccount()
let client = new DanmakuClient(null)
const client = new DanmakuClient(null)
onMounted(() => {
client.Start()

View File

@@ -1,6 +1,13 @@
<script setup lang="ts">
import { downloadImage } from '@/Utils'
import { VideoCollectCreateModel, VideoCollectDetail, VideoCollectTable, VideoCollectVideo, VideoInfo, VideoStatus } from '@/api/api-models'
import {
VideoCollectCreateModel,
VideoCollectDetail,
VideoCollectTable,
VideoCollectVideo,
VideoInfo,
VideoStatus,
} from '@/api/api-models'
import { QueryGetAPI, QueryPostAPI } from '@/api/query'
import VideoCollectInfoCard from '@/components/VideoCollectInfoCard.vue'
import { VIDEO_COLLECT_API_URL } from '@/data/constants'
@@ -140,63 +147,114 @@ 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: () =>
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;' }),
: 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),
]),
],
),
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' }, () => [
h(NButton, { type: 'success', loading: isLoading.value, onClick: () => setStatus(VideoStatus.Accepted, v) }, () => '通过'),
h(NButton, { type: 'error', loading: isLoading.value, onClick: () => setStatus(VideoStatus.Rejected, v) }, () => '拒绝'),
h(
NButton,
{ type: 'success', loading: isLoading.value, onClick: () => setStatus(VideoStatus.Accepted, v) },
() => '通过',
),
h(
NButton,
{ type: 'error', loading: isLoading.value, onClick: () => setStatus(VideoStatus.Rejected, v) },
() => '拒绝',
),
])
const acceptButtonGroup = (v: VideoInfo) =>
h(NSpace, { size: 'small', justify: 'space-around' }, () => [
h(NButton, { type: 'info', loading: isLoading.value, onClick: () => setStatus(VideoStatus.Pending, v) }, () => '重设为未审核'),
h(NButton, { type: 'error', loading: isLoading.value, onClick: () => setStatus(VideoStatus.Rejected, v) }, () => '拒绝'),
h(
NButton,
{ type: 'info', loading: isLoading.value, onClick: () => setStatus(VideoStatus.Pending, v) },
() => '重设为未审核',
),
h(
NButton,
{ type: 'error', loading: isLoading.value, onClick: () => setStatus(VideoStatus.Rejected, v) },
() => '拒绝',
),
])
const rejectButtonGroup = (v: VideoInfo) =>
h(NSpace, { size: 'small', justify: 'space-around' }, () => [
h(NButton, { type: 'success', loading: isLoading.value, onClick: () => setStatus(VideoStatus.Accepted, v) }, () => '通过'),
h(NButton, { type: 'info', loading: isLoading.value, onClick: () => setStatus(VideoStatus.Pending, v) }, () => '重设为未审核'),
h(
NButton,
{ type: 'success', loading: isLoading.value, onClick: () => setStatus(VideoStatus.Accepted, v) },
() => '通过',
),
h(
NButton,
{ type: 'info', loading: isLoading.value, onClick: () => setStatus(VideoStatus.Pending, v) },
() => '重设为未审核',
),
])
function setStatus(status: VideoStatus, video: VideoInfo) {
isLoading.value = true
@@ -295,7 +353,10 @@ function closeTable() {
})
}
function saveQRCode() {
downloadImage(`https://api.qrserver.com/v1/create-qr-code/?data=${'https://vtsuru.live/video-collect/' + videoDetail.value.table.shortId}`, `vtsuru-视频征集二维码-${videoDetail.value.table.name}.png`)
downloadImage(
`https://api.qrserver.com/v1/create-qr-code/?data=${'https://vtsuru.live/video-collect/' + videoDetail.value.table.shortId}`,
`vtsuru-视频征集二维码-${videoDetail.value.table.name}.png`,
)
}
</script>
<template>
@@ -306,8 +367,12 @@ function saveQRCode() {
<template v-if="width <= 1000">
<NButton type="success" size="small" @click="shareModalVisiable = true"> 分享 </NButton>
<NButton type="info" size="small" @click="editModalVisiable = true"> 更新 </NButton>
<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>
<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>
<NPopconfirm :on-positive-click="deleteTable">
<template #trigger>
<NButton type="error" size="small"> 删除 </NButton>
@@ -321,8 +386,15 @@ function saveQRCode() {
<NSpace>
<NButton type="success" size="small" @click="shareModalVisiable = true"> 分享 </NButton>
<NButton type="info" size="small" @click="editModalVisiable = true"> 更新 </NButton>
<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>
<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>
<NPopconfirm :on-positive-click="deleteTable">
<template #trigger>
<NButton type="error" size="small"> 删除 </NButton>
@@ -369,9 +441,15 @@ function saveQRCode() {
</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" />
<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" />
<NDivider/>
<NDivider />
<NSpace justify="center">
<NButton type="primary" @click="saveQRCode"> 保存二维码 </NButton>
</NSpace>
@@ -385,10 +463,20 @@ function saveQRCode() {
<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>

View File

@@ -4,7 +4,24 @@ import { VideoCollectTable } 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 { FormRules, NButton, NDatePicker, NDivider, NEmpty, NForm, NFormItem, NInput, NInputNumber, NList, NListItem, NModal, NSpace, NSpin, NText, useMessage } from 'naive-ui'
import {
FormRules,
NButton,
NDatePicker,
NDivider,
NEmpty,
NForm,
NFormItem,
NInput,
NInputNumber,
NList,
NListItem,
NModal,
NSpace,
NSpin,
NText,
useMessage,
} from 'naive-ui'
import { ref } from 'vue'
const accountInfo = useAccount()
@@ -128,13 +145,28 @@ function createTable() {
<NInput v-model:value="createVideoModel.name" placeholder="征集表的标题" maxlength="30" show-count />
</NFormItem>
<NFormItem label="描述" path="description">
<NInput v-model:value="createVideoModel.description" placeholder="可以是备注之类的" maxlength="300" show-count />
<NInput
v-model:value="createVideoModel.description"
placeholder="可以是备注之类的"
maxlength="300"
show-count
/>
</NFormItem>
<NFormItem label="视频数量" path="maxVideoCount">
<NInputNumber v-model:value="createVideoModel.maxVideoCount" placeholder="最大数量" type="number" style="max-width: 150px" />
<NInputNumber
v-model:value="createVideoModel.maxVideoCount"
placeholder="最大数量"
type="number"
style="max-width: 150px"
/>
</NFormItem>
<NFormItem label="结束时间" path="endAt">
<NDatePicker v-model:value="createVideoModel.endAt" type="datetime" placeholder="结束征集的时间" :isDateDisabled="dateDisabled" />
<NDatePicker
v-model:value="createVideoModel.endAt"
type="datetime"
placeholder="结束征集的时间"
:isDateDisabled="dateDisabled"
/>
<NDivider vertical />
<NText depth="3"> 最低为一小时 </NText>
</NFormItem>

View File

@@ -1,62 +1,50 @@
<script setup lang="ts">
import { getBase64, getImageUploadModel } from '@/Utils'
import { getImageUploadModel } from '@/Utils'
import { DisableFunction, EnableFunction, useAccount } from '@/api/account'
import {
ResponsePointGoodModel,
FunctionTypes,
PointGoodsModel,
GoodsTypes,
GoodsStatus,
TagInfo,
} from '@/api/api-models'
import { FunctionTypes, GoodsStatus, GoodsTypes, PointGoodsModel, ResponsePointGoodModel } from '@/api/api-models'
import { QueryGetAPI, QueryPostAPI } from '@/api/query'
import EventFetcherStatusCard from '@/components/EventFetcherStatusCard.vue'
import PointGoodsItem from '@/components/manage/PointGoodsItem.vue'
import { FILE_BASE_URL, POINT_API_URL } from '@/data/constants'
import { Info24Filled, MoreVertical16Filled } from '@vicons/fluent'
import { List } from 'linqts'
import { useAuthStore } from '@/store/useAuthStore'
import { Info24Filled } from '@vicons/fluent'
import { useRouteHash } from '@vueuse/router'
import {
FormItemRule,
NAlert,
NButton,
NCheckbox,
NDivider,
NEmpty,
NFlex,
NForm,
NFormItem,
NGrid,
NGridItem,
NIcon,
NImage,
NInput,
NInputNumber,
NModal,
NPopconfirm,
NRadioButton,
NRadioGroup,
NScrollbar,
NSelect,
NSwitch,
NTabPane,
NTabs,
NText,
useMessage,
NFlex,
NInputNumber,
NRadioGroup,
NRadio,
NTooltip,
NIcon,
NCheckbox,
NRadioButton,
NUpload,
UploadFileInfo,
FormItemRule,
NScrollbar,
FormValidationError,
NSelect,
NGrid,
NGridItem,
NDropdown,
NImage,
useDialog,
NPopconfirm,
NEmpty,
useMessage,
} from 'naive-ui'
import { computed, onMounted, ref } from 'vue'
import PointOrderManage from './PointOrderManage.vue'
import PointUserManage from './PointUserManage.vue'
import { cloneFnJSON } from '@vueuse/core'
import { useAuthStore } from '@/store/useAuthStore'
import PointSettings from './PointSettings.vue'
import { useRouteHash } from '@vueuse/router'
import EventFetcherStatusCard from '@/components/EventFetcherStatusCard.vue'
import PointUserManage from './PointUserManage.vue'
const message = useMessage()
const accountInfo = useAccount()
@@ -240,7 +228,7 @@ async function updateGoods(e: MouseEvent) {
}
function OnFileListChange(files: UploadFileInfo[]) {
if (files.length == 1) {
var file = files[0]
const file = files[0]
if ((file.file?.size ?? 0) > 10 * 1024 * 1024) {
message.error('文件大小不能超过10MB')
currentGoodsModel.value.fileList = []

View File

@@ -3,8 +3,8 @@ import { ResponsePointGoodModel, ResponsePointOrder2OwnerModel } from '@/api/api
import { QueryGetAPI } from '@/api/query'
import PointOrderCard from '@/components/manage/PointOrderCard.vue'
import { POINT_API_URL } from '@/data/constants'
import { NButton, NCard, NEmpty, NList, NListItem, useMessage } from 'naive-ui'
import { h, onMounted, ref } from 'vue'
import { NEmpty, useMessage } from 'naive-ui'
import { onMounted, ref } from 'vue'
const props = defineProps<{
goods: ResponsePointGoodModel[]

View File

@@ -188,13 +188,20 @@ async function updateGift() {
</template>
<NCard>
<NFlex vertical>
<NButton @click="showAddGiftModal = true" type="primary" :disabled="!canEdit" style="max-width: 200px"> 添加礼物 </NButton>
<NButton @click="showAddGiftModal = true" type="primary" :disabled="!canEdit" style="max-width: 200px">
添加礼物
</NButton>
<NList bordered>
<NListItem v-for="item in Object.entries(setting.giftPercentMap)" :key="item[0]">
<NFlex align="center">
<NTag :bordered="false" size="small" type="success"> {{ item[0] }} </NTag>
<NInputGroup style="width: 200px" :disabled="!canEdit">
<NInputNumber :value="setting.giftPercentMap[item[0]]" @update:value="(v) => (setting.giftPercentMap[item[0]] = v ?? 0)" :disabled="!canEdit" min="0" />
<NInputNumber
:value="setting.giftPercentMap[item[0]]"
@update:value="(v) => (setting.giftPercentMap[item[0]] = v ?? 0)"
:disabled="!canEdit"
min="0"
/>
<NButton @click="updateSettings" type="info" :disabled="!canEdit">确定</NButton>
</NInputGroup>
<NPopconfirm @positive-click="deleteGift(item[0])">

View File

@@ -9,12 +9,9 @@ import { QueryGetAPI } from '@/api/query'
import PointHistoryCard from '@/components/manage/PointHistoryCard.vue'
import PointOrderCard from '@/components/manage/PointOrderCard.vue'
import { POINT_API_URL } from '@/data/constants'
import { useAuthStore } from '@/store/useAuthStore'
import {
DataTableColumns,
NButton,
NCard,
NDataTable,
NDescriptions,
NDescriptionsItem,
NDivider,
@@ -22,8 +19,6 @@ import {
NFlex,
NInput,
NInputNumber,
NList,
NListItem,
NModal,
NSpin,
NTag,
@@ -32,7 +27,7 @@ import {
NTooltip,
useMessage,
} from 'naive-ui'
import { h, onMounted, ref } from 'vue'
import { onMounted, ref } from 'vue'
const props = defineProps<{
user: ResponsePointUserModel

View File

@@ -2,6 +2,7 @@
import { ResponsePointGoodModel, ResponsePointUserModel } from '@/api/api-models'
import { QueryGetAPI } from '@/api/query'
import { POINT_API_URL } from '@/data/constants'
import { useStorage } from '@vueuse/core'
import {
DataTableColumns,
NButton,
@@ -11,8 +12,6 @@ import {
NDivider,
NEmpty,
NFlex,
NList,
NListItem,
NModal,
NPopconfirm,
NScrollbar,
@@ -24,7 +23,6 @@ import {
} from 'naive-ui'
import { computed, h, onMounted, ref } from 'vue'
import PointUserDetailCard from './PointUserDetailCard.vue'
import { useStorage } from '@vueuse/core'
const props = defineProps<{
goods: ResponsePointGoodModel[]