mirror of
https://github.com/Megghy/vtsuru.live.git
synced 2025-12-07 02:46:55 +08:00
particularly complete forum function, add point order export and user delete
This commit is contained in:
@@ -87,7 +87,7 @@ watch(
|
||||
</span>
|
||||
<span v-else>
|
||||
已直播:
|
||||
{{ ((Date.now() - (live.stopAt ?? 0)) / (3600 * 1000)).toFixed(1) }}
|
||||
{{ ((Date.now() - (live.startAt ?? 0)) / (3600 * 1000)).toFixed(1) }}
|
||||
时
|
||||
</span>
|
||||
</NPopover>
|
||||
|
||||
27
src/components/TurnstileVerify.vue
Normal file
27
src/components/TurnstileVerify.vue
Normal file
@@ -0,0 +1,27 @@
|
||||
<script setup lang="ts">
|
||||
import { TURNSTILE_KEY } from '@/data/constants'
|
||||
import { onUnmounted, ref } from 'vue'
|
||||
import { onMounted } from 'vue'
|
||||
|
||||
import VueTurnstile from 'vue-turnstile'
|
||||
const turnstile = ref()
|
||||
|
||||
const token = defineModel<string>('token', {
|
||||
default: '',
|
||||
})
|
||||
onUnmounted(() => {
|
||||
turnstile.value?.remove()
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
reset,
|
||||
})
|
||||
|
||||
function reset() {
|
||||
turnstile.value?.reset()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<VueTurnstile ref="turnstile" :site-key="TURNSTILE_KEY" v-model="token" theme="auto" style="text-align: center" />
|
||||
</template>
|
||||
119
src/components/VEditor.vue
Normal file
119
src/components/VEditor.vue
Normal file
@@ -0,0 +1,119 @@
|
||||
<script setup lang="ts">
|
||||
import { isDarkMode } from '@/Utils'
|
||||
import { APIRoot } from '@/api/api-models'
|
||||
import { GetHeaders } from '@/api/query'
|
||||
import '@/assets/editorDarkMode.css'
|
||||
import { BASE_URL, VTSURU_API_URL } from '@/data/constants'
|
||||
import { DomEditor, IEditorConfig, IToolbarConfig } from '@wangeditor/editor'
|
||||
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
|
||||
import '@wangeditor/editor/dist/css/style.css' // 引入 css
|
||||
import { NotificationReactive, useMessage } from 'naive-ui'
|
||||
import { onBeforeUnmount, ref, shallowRef, onMounted } from 'vue'
|
||||
|
||||
type InsertFnType = (url: string, alt: string, href: string) => void
|
||||
|
||||
const props = defineProps({
|
||||
defaultValue: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'default',
|
||||
},
|
||||
maxLength: {
|
||||
type: Number,
|
||||
default: 10000,
|
||||
},
|
||||
})
|
||||
const message = useMessage()
|
||||
|
||||
const editorRef = shallowRef()
|
||||
const toolbar = DomEditor.getToolbar(editorRef.value)
|
||||
const toolbarConfig: Partial<IToolbarConfig> = {
|
||||
excludeKeys: ['group-video', 'group-lineHeight', 'insertImage', 'fullScreen'],
|
||||
}
|
||||
const uploadProgressRef = ref<NotificationReactive>()
|
||||
const editorConfig: Partial<IEditorConfig> = {
|
||||
placeholder: '请输入内容...',
|
||||
maxLength: props.maxLength,
|
||||
|
||||
MENU_CONF: {
|
||||
uploadImage: {
|
||||
maxFileSize: 10 * 1024 * 1024,
|
||||
maxNumberOfFiles: 10,
|
||||
async customUpload(file: File, insertFn: InsertFnType) {
|
||||
const formData = new FormData() //创建一个FormData实例。
|
||||
message.info('图片上传中')
|
||||
formData.append('file', file)
|
||||
const resp = await fetch(VTSURU_API_URL + 'image/upload', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
headers: GetHeaders(),
|
||||
})
|
||||
if (resp.ok) {
|
||||
const data = (await resp.json()) as APIRoot<string>
|
||||
if (data.code == 200) {
|
||||
insertFn(data.data, '', '')
|
||||
} else {
|
||||
message.error('图片上传失败: ' + data.message)
|
||||
}
|
||||
} else {
|
||||
message.error('图片上传失败: ' + resp.statusText)
|
||||
}
|
||||
},
|
||||
onProgress(progress: number) {
|
||||
console.log(progress)
|
||||
},
|
||||
onSuccess(file: File, res: any) {
|
||||
console.log(`${file.name} 上传成功`, res)
|
||||
message.success('图片上传成功')
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const value = defineModel<string>('value')
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
const editor = editorRef.value
|
||||
if (editor == null) return
|
||||
editor.destroy()
|
||||
})
|
||||
onMounted(() => {
|
||||
//editorRef.value?.setHtml(props.defaultValue)
|
||||
})
|
||||
function handleCreated(editor: unknown) {
|
||||
editorRef.value = editor // 记录 editor 实例,重要!
|
||||
}
|
||||
function getText() {
|
||||
return editorRef.value?.getText()
|
||||
}
|
||||
function getHtml() {
|
||||
return editorRef.value?.getText()
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
getText,
|
||||
getHtml,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="{ 'dark-theme': isDarkMode }" style="border: 1px solid #ccc">
|
||||
<Toolbar
|
||||
ref="toolbarRef"
|
||||
style="border-bottom: 1px solid #ccc"
|
||||
:editor="editorRef"
|
||||
:defaultConfig="toolbarConfig"
|
||||
:mode="mode"
|
||||
/>
|
||||
<Editor
|
||||
style="height: 500px; overflow-y: hidden"
|
||||
v-model="value"
|
||||
:defaultConfig="editorConfig"
|
||||
:mode="mode"
|
||||
@onCreated="handleCreated"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -119,7 +119,7 @@ const historyColumn: DataTableColumns<ResponsePointHisrotyModel> = [
|
||||
h(
|
||||
NTag,
|
||||
{ type: 'warning', size: 'tiny', style: { margin: '0' }, bordered: false },
|
||||
() => (row.extra?.danmaku.num ?? 1) + '个',
|
||||
() => (row.count ?? 1) + '个',
|
||||
),
|
||||
])
|
||||
case EventDataTypes.SC:
|
||||
|
||||
@@ -98,6 +98,17 @@ const orderColumn: DataTableColumns<ResponsePointOrder2UserModel | ResponsePoint
|
||||
title: '订单号',
|
||||
key: 'id',
|
||||
},
|
||||
{
|
||||
title: '礼物名',
|
||||
key: 'giftName',
|
||||
render: (row: ResponsePointOrder2UserModel | ResponsePointOrder2OwnerModel) => {
|
||||
return row.instanceOf == 'user' ? row.goods.name : props.goods?.find((g) => g.id == row.goodsId)?.name
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '数量',
|
||||
key: 'count',
|
||||
},
|
||||
{
|
||||
title: '时间',
|
||||
key: 'time',
|
||||
@@ -118,9 +129,12 @@ const orderColumn: DataTableColumns<ResponsePointOrder2UserModel | ResponsePoint
|
||||
{
|
||||
title: '订单状态',
|
||||
key: 'status',
|
||||
filter: (filterOptionValue: unknown, row: ResponsePointOrder2UserModel | ResponsePointOrder2OwnerModel) => {
|
||||
return row.status == filterOptionValue
|
||||
},
|
||||
filter:
|
||||
props.type == 'owner'
|
||||
? undefined
|
||||
: (filterOptionValue: unknown, row: ResponsePointOrder2UserModel | ResponsePointOrder2OwnerModel) => {
|
||||
return row.status == filterOptionValue
|
||||
},
|
||||
filterOptions: [
|
||||
{
|
||||
label: '等待发货',
|
||||
@@ -151,9 +165,12 @@ const orderColumn: DataTableColumns<ResponsePointOrder2UserModel | ResponsePoint
|
||||
{
|
||||
title: '订单类型',
|
||||
key: 'type',
|
||||
filter: (filterOptionValue: unknown, row: ResponsePointOrder2UserModel | ResponsePointOrder2OwnerModel) => {
|
||||
return row.type == filterOptionValue
|
||||
},
|
||||
filter:
|
||||
props.type == 'owner'
|
||||
? undefined
|
||||
: (filterOptionValue: unknown, row: ResponsePointOrder2UserModel | ResponsePointOrder2OwnerModel) => {
|
||||
return row.type == filterOptionValue
|
||||
},
|
||||
filterOptions: [
|
||||
{
|
||||
label: '实体礼物',
|
||||
@@ -360,7 +377,10 @@ onMounted(() => {
|
||||
></iframe>
|
||||
</template>
|
||||
</template>
|
||||
<template v-else-if="orderDetail.instanceOf == 'owner'">
|
||||
<template v-else-if="orderDetail.instanceOf == 'owner'"
|
||||
><NFlex justify="center">
|
||||
<PointGoodsItem style="max-width: 300px" :goods="currentGoods" />
|
||||
</NFlex>
|
||||
<NDivider> 设置订单状态 </NDivider>
|
||||
<NFlex justify="center" style="width: 100%">
|
||||
<NSteps
|
||||
|
||||
Reference in New Issue
Block a user