style(webui): Oxc format 已有代码

This commit is contained in:
2026-05-24 16:50:44 +08:00
parent ab396b01f2
commit 3117af670b
31 changed files with 559 additions and 468 deletions
+9 -9
View File
@@ -1,14 +1,14 @@
<script setup lang="ts"> <script setup lang="ts">
import PageHeader from '@/components/PageHeader.vue' import PageHeader from '@/components/PageHeader.vue'
import {dateZhCN, zhCN} from 'naive-ui' import { dateZhCN, zhCN } from 'naive-ui'
import {useNowUser} from '@/stores/now-user.ts' import { useNowUser } from '@/stores/now-user.ts'
import {onMounted} from 'vue' import { onMounted } from 'vue'
import {useHead} from "@unhead/vue"; import { useHead } from '@unhead/vue'
const NOWUSER = useNowUser() const NOWUSER = useNowUser()
useHead({ useHead({
titleTemplate: "%s | NayHome" titleTemplate: '%s | NayHome',
}) })
onMounted(async () => { onMounted(async () => {
@@ -18,9 +18,9 @@ onMounted(async () => {
try { try {
await NOWUSER.loadUserInfo(Number(user_id), access_token) await NOWUSER.loadUserInfo(Number(user_id), access_token)
} catch { } catch {
localStorage.removeItem("user-id") localStorage.removeItem('user-id')
localStorage.removeItem('access-token') localStorage.removeItem('access-token')
console.log("已移除 localstorage 中存储的验证信息。") console.log('已移除 localstorage 中存储的验证信息。')
} }
} }
}) })
@@ -29,7 +29,7 @@ onMounted(async () => {
<template> <template>
<n-config-provider id="aapp" :date-locale="dateZhCN" :locale="zhCN"> <n-config-provider id="aapp" :date-locale="dateZhCN" :locale="zhCN">
<div class="header-container"> <div class="header-container">
<page-header/> <page-header />
</div> </div>
<div class="content-container"> <div class="content-container">
<n-message-provider> <n-message-provider>
@@ -37,7 +37,7 @@ onMounted(async () => {
</n-message-provider> </n-message-provider>
</div> </div>
<div class="footer-container">🌸 Nya Home ~</div> <div class="footer-container">🌸 Nya Home ~</div>
<n-global-style/> <n-global-style />
</n-config-provider> </n-config-provider>
</template> </template>
+2 -6
View File
@@ -1,13 +1,9 @@
<script setup lang="ts"> <script setup lang="ts"></script>
</script>
<template> <template>
<div class="in-dev"> <div class="in-dev">
<n-text class="in-dev-title">功能开发中</n-text> <n-text class="in-dev-title">功能开发中</n-text>
<n-text class="in-dev-content"> <n-text class="in-dev-content"> 已经被画在饼上辽请耐心等待喵 </n-text>
已经被画在饼上辽请耐心等待喵
</n-text>
</div> </div>
</template> </template>
+22 -15
View File
@@ -1,23 +1,23 @@
<script setup lang="ts"> <script setup lang="ts">
import {ref} from 'vue' import { ref } from 'vue'
import {api} from '@/tools/web.ts' import { api } from '@/tools/web.ts'
import type {ReturnDto} from '@/types/response.ts' import type { ReturnDto } from '@/types/response.ts'
import {useMessage} from 'naive-ui' import { useMessage } from 'naive-ui'
import VerifyCodeModal from '@/components/admin/VerifyCodeModal.vue' import VerifyCodeModal from '@/components/admin/VerifyCodeModal.vue'
import {useNowUser} from "@/stores/now-user.ts"; import { useNowUser } from '@/stores/now-user.ts'
const MESSAGE = useMessage() const MESSAGE = useMessage()
const NOWUSER = useNowUser() const NOWUSER = useNowUser()
const showModal = defineModel('showModal', {required: true}) const showModal = defineModel('showModal', { required: true })
const showVerifyCodeModal = ref(false) const showVerifyCodeModal = ref(false)
const newEmail = ref("") const newEmail = ref('')
const verifyCode = ref("") const verifyCode = ref('')
function sendEmail() { function sendEmail() {
api api
.post('/admin/me/email-verify/send/', JSON.stringify({to: newEmail.value})) .post('/admin/me/email-verify/send/', JSON.stringify({ to: newEmail.value }))
.then((res) => res.data as ReturnDto) .then((res) => res.data as ReturnDto)
.then((res) => { .then((res) => {
if (res.success) { if (res.success) {
@@ -33,10 +33,14 @@ function sendEmail() {
} }
function verifyEmail() { function verifyEmail() {
api.post('/admin/me/email-verify/', JSON.stringify({ api
.post(
'/admin/me/email-verify/',
JSON.stringify({
to: newEmail.value, to: newEmail.value,
verify_code: String(verifyCode.value).split(",").join(""), verify_code: String(verifyCode.value).split(',').join(''),
})) }),
)
.then((res) => res.data as ReturnDto) .then((res) => res.data as ReturnDto)
.then((res) => { .then((res) => {
if (res.success) { if (res.success) {
@@ -59,7 +63,7 @@ function verifyEmail() {
<n-form label-placement="left"> <n-form label-placement="left">
<n-p>你需要使用新的邮件地址接收一个验证码来完成修改</n-p> <n-p>你需要使用新的邮件地址接收一个验证码来完成修改</n-p>
<n-form-item path="to" label="新的邮件地址"> <n-form-item path="to" label="新的邮件地址">
<n-input v-model:value="newEmail"/> <n-input v-model:value="newEmail" />
</n-form-item> </n-form-item>
<n-flex> <n-flex>
<n-button type="warning" @click="sendEmail()">获取验证码</n-button> <n-button type="warning" @click="sendEmail()">获取验证码</n-button>
@@ -69,8 +73,11 @@ function verifyEmail() {
</n-form> </n-form>
</n-modal> </n-modal>
<verify-code-modal v-model:show-modal="showVerifyCodeModal" v-model:verify-code="verifyCode" <verify-code-modal
:verify="verifyEmail"/> v-model:show-modal="showVerifyCodeModal"
v-model:verify-code="verifyCode"
:verify="verifyEmail"
/>
</template> </template>
<style scoped></style> <style scoped></style>
+5 -7
View File
@@ -1,21 +1,19 @@
<script setup lang="ts"> <script setup lang="ts">
defineProps<{ defineProps<{
title: string; title: string
}>() }>()
</script> </script>
<template> <template>
<n-card :title> <n-card :title>
<template #header-extra> <template #header-extra>
<slot name="extra"/> <slot name="extra" />
</template> </template>
<slot name="default"/> <slot name="default" />
<template #action> <template #action>
<slot name="action"/> <slot name="action" />
</template> </template>
</n-card> </n-card>
</template> </template>
<style scoped> <style scoped></style>
</style>
+14 -14
View File
@@ -1,10 +1,10 @@
<script setup lang="ts"> <script setup lang="ts">
import {ref} from 'vue' import { ref } from 'vue'
import {api} from '@/tools/web.js' import { api } from '@/tools/web.js'
import type {ReturnDto} from '@/types/response.js' import type { ReturnDto } from '@/types/response.js'
import {useMessage} from 'naive-ui' import { useMessage } from 'naive-ui'
import {AxiosError} from 'axios' import { AxiosError } from 'axios'
import {useNowUser} from '@/stores/now-user.js' import { useNowUser } from '@/stores/now-user.js'
const MESSAGE = useMessage() const MESSAGE = useMessage()
const NOWUSER = useNowUser() const NOWUSER = useNowUser()
@@ -55,9 +55,9 @@ function login() {
<template> <template>
<div> <div>
<div class="user-action nyahome-card" v-if="NOWUSER.isLogin" style="position: relative"> <div class="user-action nyahome-card" v-if="NOWUSER.isLogin" style="position: relative">
<img :src="NOWUSER.background_url" alt="User Background" class="user-action-background"> <img :src="NOWUSER.background_url" alt="User Background" class="user-action-background" />
<div class="card-content" style="margin-top: auto; margin-bottom: 20px;"> <div class="card-content" style="margin-top: auto; margin-bottom: 20px">
<n-avatar :size="96" circle :src="NOWUSER.avatar_url"/> <n-avatar :size="96" circle :src="NOWUSER.avatar_url" />
<n-h2 style="margin: 0"> <n-h2 style="margin: 0">
{{ NOWUSER.display_name ? NOWUSER.display_name : NOWUSER.name }} {{ NOWUSER.display_name ? NOWUSER.display_name : NOWUSER.name }}
</n-h2> </n-h2>
@@ -82,13 +82,13 @@ function login() {
<n-radio-button value="register">注册</n-radio-button> <n-radio-button value="register">注册</n-radio-button>
</n-radio-group> </n-radio-group>
<div class="card-content" v-if="page === 'login'"> <div class="card-content" v-if="page === 'login'">
<n-avatar :size="96" circle/> <n-avatar :size="96" circle />
<n-radio-group v-model:value="loginMethod"> <n-radio-group v-model:value="loginMethod">
<n-radio-button value="name">用户名</n-radio-button> <n-radio-button value="name">用户名</n-radio-button>
<n-radio-button value="email">邮箱</n-radio-button> <n-radio-button value="email">邮箱</n-radio-button>
<n-radio-button value="phone">手机</n-radio-button> <n-radio-button value="phone">手机</n-radio-button>
</n-radio-group> </n-radio-group>
<n-input v-model:value="loginForm.username" class="card-input" placeholder=""/> <n-input v-model:value="loginForm.username" class="card-input" placeholder="" />
<n-input <n-input
v-model:value="loginForm.password" v-model:value="loginForm.password"
class="card-input" class="card-input"
@@ -103,9 +103,9 @@ function login() {
</div> </div>
<div class="card-content" v-else> <div class="card-content" v-else>
<n-avatar :size="96" circle/> <n-avatar :size="96" circle />
<n-input class="card-input" placeholder="用户名"/> <n-input class="card-input" placeholder="用户名" />
<n-input class="card-input" placeholder="密码" type="password" show-password-toggle/> <n-input class="card-input" placeholder="密码" type="password" show-password-toggle />
<n-flex class="card-input"> <n-flex class="card-input">
<n-button type="primary" class="card-button">注册</n-button> <n-button type="primary" class="card-button">注册</n-button>
</n-flex> </n-flex>
@@ -1,15 +1,15 @@
<script setup lang="ts"> <script setup lang="ts">
import {ref} from "vue"; import { ref } from 'vue'
import {api} from "@/tools/web.ts"; import { api } from '@/tools/web.ts'
import {useNowUser} from "@/stores/now-user.ts"; import { useNowUser } from '@/stores/now-user.ts'
import {useMessage} from "naive-ui"; import { useMessage } from 'naive-ui'
import {useRouter} from "vue-router"; import { useRouter } from 'vue-router'
const ROUTER = useRouter(); const ROUTER = useRouter()
const MESSAGE = useMessage() const MESSAGE = useMessage()
const NOWUSER = useNowUser() const NOWUSER = useNowUser()
const showModal = defineModel("showModal", {required: true}) const showModal = defineModel('showModal', { required: true })
const changeForm = ref({ const changeForm = ref({
old_password: '', old_password: '',
@@ -17,40 +17,41 @@ const changeForm = ref({
}) })
function change() { function change() {
api.post("/admin/me/password/", JSON.stringify(changeForm.value)) api
.post('/admin/me/password/', JSON.stringify(changeForm.value))
.then(() => { .then(() => {
MESSAGE.success("密码修改成功,请重新登录。") MESSAGE.success('密码修改成功,请重新登录。')
NOWUSER.isLogin = false NOWUSER.isLogin = false
localStorage.removeItem("user-id") localStorage.removeItem('user-id')
localStorage.removeItem("access-token") localStorage.removeItem('access-token')
ROUTER.push("/") ROUTER.push('/')
}) })
.catch((err) => { .catch((err) => {
MESSAGE.error(`密码修改失败:${err}`) MESSAGE.error(`密码修改失败:${err}`)
MESSAGE.warning("如果您忘记了原密码,请选择「忘记密码」。") MESSAGE.warning('如果您忘记了原密码,请选择「忘记密码」。')
}) })
} }
</script> </script>
<template> <template>
<n-modal style="width: 600px;" v-model:show="showModal" title="修改密码" preset="card"> <n-modal style="width: 600px" v-model:show="showModal" title="修改密码" preset="card">
<n-form label-align="right" label-placement="left" label-width="auto" :model="changeForm"> <n-form label-align="right" label-placement="left" label-width="auto" :model="changeForm">
<n-form-item label="原密码" path="old_password"> <n-form-item label="原密码" path="old_password">
<n-input v-model:value="changeForm.old_password"/> <n-input v-model:value="changeForm.old_password" />
</n-form-item> </n-form-item>
<n-form-item label="新密码" path="new_password"> <n-form-item label="新密码" path="new_password">
<n-input v-model:value="changeForm.new_password" type="password" show-password-toggle/> <n-input v-model:value="changeForm.new_password" type="password" show-password-toggle />
</n-form-item> </n-form-item>
<n-form-item label="确认修改"> <n-form-item label="确认修改">
<n-flex> <n-flex>
<n-button type="error" @click="change()">确认修改</n-button> <n-button type="error" @click="change()">确认修改</n-button>
<n-tag type="warning" size="large">修改密码会注销所有已登录状态您将需要重新登录</n-tag> <n-tag type="warning" size="large"
>修改密码会注销所有已登录状态您将需要重新登录</n-tag
>
</n-flex> </n-flex>
</n-form-item> </n-form-item>
</n-form> </n-form>
</n-modal> </n-modal>
</template> </template>
<style scoped> <style scoped></style>
</style>
@@ -1,14 +1,14 @@
<script setup lang="ts"> <script setup lang="ts">
import {computed, onMounted, ref, watch} from 'vue' import { computed, onMounted, ref, watch } from 'vue'
import AiiProviderAddModal from '@/components/chatroom/AiiProviderAddModal.vue' import AiiProviderAddModal from '@/components/chatroom/AiiProviderAddModal.vue'
import {api} from '@/tools/web.js' import { api } from '@/tools/web.js'
import type {ReturnDto} from '@/types/response.js' import type { ReturnDto } from '@/types/response.js'
import {type SelectOption, useMessage} from 'naive-ui' import { type SelectOption, useMessage } from 'naive-ui'
import type {AiiProviderPublicWithoutKey} from '@/types/aii.js' import type { AiiProviderPublicWithoutKey } from '@/types/aii.js'
const MESSAGE = useMessage() const MESSAGE = useMessage()
const showModal = defineModel<boolean>('showModal', {required: true}) const showModal = defineModel<boolean>('showModal', { required: true })
const showAddProviderModal = ref(false) const showAddProviderModal = ref(false)
const selectProvider = ref<number | null>(null) const selectProvider = ref<number | null>(null)
@@ -120,21 +120,19 @@ function onConfirm() {
<n-form :model="addModelForm" label-placement="left" label-width="auto" label-align="right"> <n-form :model="addModelForm" label-placement="left" label-width="auto" label-align="right">
<n-form-item label="模型提供商" path="aii_provider_id"> <n-form-item label="模型提供商" path="aii_provider_id">
<n-flex style="width: 100%" justify="right" align="center"> <n-flex style="width: 100%" justify="right" align="center">
<n-select v-model:value="selectProvider" :options="providerOptions"/> <n-select v-model:value="selectProvider" :options="providerOptions" />
<n-tag round type="info">修改已添加的提供商请前往管理中心</n-tag> <n-tag round type="info">修改已添加的提供商请前往管理中心</n-tag>
<n-button secondary type="success" size="small" round @click="loadProviders()" <n-button secondary type="success" size="small" round @click="loadProviders()"
>刷新 >刷新
</n-button </n-button>
>
<n-button secondary type="warning" size="small" round @click="showAddProviderModal = true" <n-button secondary type="warning" size="small" round @click="showAddProviderModal = true"
>添加 >添加
</n-button </n-button>
>
</n-flex> </n-flex>
</n-form-item> </n-form-item>
<n-form-item label="模型名称" path="model_name"> <n-form-item label="模型名称" path="model_name">
<n-flex style="width: 100%" justify="right" align="center"> <n-flex style="width: 100%" justify="right" align="center">
<n-input v-model:value="addModelForm.model_name"/> <n-input v-model:value="addModelForm.model_name" />
<n-flex style="overflow: auto"> <n-flex style="overflow: auto">
<n-button <n-button
secondary secondary
@@ -145,13 +143,11 @@ function onConfirm() {
v-bind:key="m" v-bind:key="m"
@click="addModelForm.model_name = m" @click="addModelForm.model_name = m"
>{{ m }} >{{ m }}
</n-button </n-button>
>
</n-flex> </n-flex>
<n-button secondary type="success" size="small" round @click="onGetRemoteModels()" <n-button secondary type="success" size="small" round @click="onGetRemoteModels()"
>获取模型列表 >获取模型列表
</n-button </n-button>
>
</n-flex> </n-flex>
</n-form-item> </n-form-item>
<n-form-item label="最大上下文" path="max_context_length"> <n-form-item label="最大上下文" path="max_context_length">
@@ -166,7 +162,7 @@ function onConfirm() {
</n-flex> </n-flex>
</n-form-item> </n-form-item>
</n-form> </n-form>
<aii-provider-add-modal v-model:show-modal="showAddProviderModal"/> <aii-provider-add-modal v-model:show-modal="showAddProviderModal" />
</n-modal> </n-modal>
</template> </template>
@@ -1,12 +1,12 @@
<script setup lang="ts"> <script setup lang="ts">
import {ref} from 'vue' import { ref } from 'vue'
import {api} from '@/tools/web.js' import { api } from '@/tools/web.js'
import type {ReturnDto} from '@/types/response.js' import type { ReturnDto } from '@/types/response.js'
import {useMessage} from 'naive-ui' import { useMessage } from 'naive-ui'
const MESSAGE = useMessage() const MESSAGE = useMessage()
const showModal = defineModel('showModal', {required: true}) const showModal = defineModel('showModal', { required: true })
const addProviderForm = ref({ const addProviderForm = ref({
id: 0, id: 0,
@@ -53,13 +53,13 @@ function onConfirm() {
<n-modal v-model:show="showModal" preset="card" title="添加模型提供商"> <n-modal v-model:show="showModal" preset="card" title="添加模型提供商">
<n-form :model="addProviderForm" label-placement="left" label-width="auto" label-align="right"> <n-form :model="addProviderForm" label-placement="left" label-width="auto" label-align="right">
<n-form-item label="名称" path="name"> <n-form-item label="名称" path="name">
<n-input v-model:value="addProviderForm.name"/> <n-input v-model:value="addProviderForm.name" />
</n-form-item> </n-form-item>
<n-form-item label="Base URL" path="base_url"> <n-form-item label="Base URL" path="base_url">
<n-input v-model:value="addProviderForm.base_url"/> <n-input v-model:value="addProviderForm.base_url" />
</n-form-item> </n-form-item>
<n-form-item label="API Key" path="api_key"> <n-form-item label="API Key" path="api_key">
<n-input v-model:value="addProviderForm.api_key"/> <n-input v-model:value="addProviderForm.api_key" />
</n-form-item> </n-form-item>
<n-form-item label="添加完成"> <n-form-item label="添加完成">
<n-flex> <n-flex>
@@ -1,19 +1,19 @@
<script setup lang="ts"> <script setup lang="ts">
import {computed, onMounted, ref} from 'vue' import { computed, onMounted, ref } from 'vue'
import {api} from '@/tools/web.js' import { api } from '@/tools/web.js'
import type {ReturnDto} from '@/types/response.js' import type { ReturnDto } from '@/types/response.js'
import {type SelectOption, useMessage} from 'naive-ui' import { type SelectOption, useMessage } from 'naive-ui'
import AiiModelAddModal from '@/components/chatroom/AiiModelAddModal.vue' import AiiModelAddModal from '@/components/chatroom/AiiModelAddModal.vue'
import type {AiiModelPublic} from '@/types/aii.js' import type { AiiModelPublic } from '@/types/aii.js'
import ChatPromptQuicker from '@/components/chatroom/ChatPromptQuicker.vue' import ChatPromptQuicker from '@/components/chatroom/ChatPromptQuicker.vue'
import ScriptDrawer from '@/components/chatroom/ScriptDrawer.vue' import ScriptDrawer from '@/components/chatroom/ScriptDrawer.vue'
const MESSAGE = useMessage() const MESSAGE = useMessage()
const selectedModel = defineModel<number | null>('selectModel', {required: true}) const selectedModel = defineModel<number | null>('selectModel', { required: true })
const quickerPrompt = defineModel<string>('quickerPrompt', {required: true}) const quickerPrompt = defineModel<string>('quickerPrompt', { required: true })
const {script} = defineProps<{ const { script } = defineProps<{
script: string script: string
}>() }>()
@@ -68,11 +68,11 @@ onMounted(() => {
</n-button-group> </n-button-group>
</n-flex> </n-flex>
</template> </template>
<n-select v-model:value="selectedModel" :options="modelOptions"/> <n-select v-model:value="selectedModel" :options="modelOptions" />
<aii-model-add-modal v-model:show-modal="showModal"/> <aii-model-add-modal v-model:show-modal="showModal" />
</n-card> </n-card>
<chat-prompt-quicker v-model:prompt-prefix="quickerPrompt"/> <chat-prompt-quicker v-model:prompt-prefix="quickerPrompt" />
<n-card title="剧本"> <n-card title="剧本">
<template #header-extra>故事设定 · 世界书</template> <template #header-extra>故事设定 · 世界书</template>
@@ -82,7 +82,7 @@ onMounted(() => {
故事设定 · 世界书 故事设定 · 世界书
</n-button> </n-button>
</n-flex> </n-flex>
<script-drawer :script v-model:show-drawer="showScriptDrawer"/> <script-drawer :script v-model:show-drawer="showScriptDrawer" />
</n-card> </n-card>
<n-card title="设置"> <n-card title="设置">
@@ -1,8 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import {md} from '@/tools/md.js' import { md } from '@/tools/md.js'
import {onMounted, ref, useTemplateRef} from 'vue' import { onMounted, ref, useTemplateRef } from 'vue'
const {role, msg} = defineProps<{ const { role, msg } = defineProps<{
role: 'aii' | 'user' role: 'aii' | 'user'
msg: string msg: string
onMessageEdit: (oldMessage: string, newMessage: string, change: 'aii' | 'user') => void onMessageEdit: (oldMessage: string, newMessage: string, change: 'aii' | 'user') => void
@@ -37,7 +37,7 @@ onMounted(() => {
<template> <template>
<div :class="[`${role}-message`, 'message']" ref="self"> <div :class="[`${role}-message`, 'message']" ref="self">
<p v-html="md.render(msg)"/> <p v-html="md.render(msg)" />
<n-button class="modify-button" secondary type="info" circle @click="showModal = true"> <n-button class="modify-button" secondary type="info" circle @click="showModal = true">
</n-button> </n-button>
@@ -70,7 +70,7 @@ onMounted(() => {
> >
<n-h3 prefix="bar" v-if="showEditor">编辑中</n-h3> <n-h3 prefix="bar" v-if="showEditor">编辑中</n-h3>
<n-input v-if="showEditor" type="textarea" :rows="10" v-model:value="editorMessage"></n-input> <n-input v-if="showEditor" type="textarea" :rows="10" v-model:value="editorMessage"></n-input>
<n-code v-else :code="msg" word-wrap/> <n-code v-else :code="msg" word-wrap />
<!--suppress VueUnrecognizedSlot --> <!--suppress VueUnrecognizedSlot -->
<template #footer> <template #footer>
<n-flex align="center" style="padding-top: 10px"> <n-flex align="center" style="padding-top: 10px">
+7 -7
View File
@@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import {createChatTableMessages} from '@/components/chatroom/chat-table-messages.js' import { createChatTableMessages } from '@/components/chatroom/chat-table-messages.js'
import {md} from '@/tools/md.js' import { md } from '@/tools/md.js'
defineProps<{ defineProps<{
content: string | null content: string | null
@@ -15,8 +15,8 @@ defineProps<{
onMessageDelete: (message: string, change: 'aii' | 'user') => void onMessageDelete: (message: string, change: 'aii' | 'user') => void
}>() }>()
const message = defineModel<string>('message', {required: true}) const message = defineModel<string>('message', { required: true })
const mode = defineModel<'continue' | 'expand'>('mode', {required: true}) const mode = defineModel<'continue' | 'expand'>('mode', { required: true })
</script> </script>
<template> <template>
@@ -26,15 +26,15 @@ const mode = defineModel<'continue' | 'expand'>('mode', {required: true})
v-if="content !== null" v-if="content !== null"
:is="createChatTableMessages(content, onMessageEdit, onMessageDelete)" :is="createChatTableMessages(content, onMessageEdit, onMessageDelete)"
/> />
<div v-if="aiiMessage !== null" class="user-message message" v-html="md.render(message)"/> <div v-if="aiiMessage !== null" class="user-message message" v-html="md.render(message)" />
<div v-if="aiiMessage !== null" class="aii-message-streaming message"> <div v-if="aiiMessage !== null" class="aii-message-streaming message">
<div class="thinking">{{ aiiThinking }}</div> <div class="thinking">{{ aiiThinking }}</div>
<div v-html="md.render(aiiMessage)"/> <div v-html="md.render(aiiMessage)" />
</div> </div>
</div> </div>
<div v-if="aiiMessage === null" class="editor"> <div v-if="aiiMessage === null" class="editor">
<n-input v-model:value="message" type="textarea" :resizable="false"/> <n-input v-model:value="message" type="textarea" :resizable="false" />
<n-flex justify="right" align="center"> <n-flex justify="right" align="center">
<n-button type="tertiary" size="small" circle>!</n-button> <n-button type="tertiary" size="small" circle>!</n-button>
<n-switch <n-switch
+17 -4
View File
@@ -10,11 +10,24 @@ defineProps<{
<template> <template>
<div class="chatroom-card"> <div class="chatroom-card">
<n-image v-if="infoMode" class="image" object-fit="cover" preview-disabled :src="feature_image" <n-image
v-if="infoMode"
class="image"
object-fit="cover"
preview-disabled
:src="feature_image"
width="140" width="140"
height="100"/> height="100"
<n-image v-else class="image" object-fit="cover" preview-disabled :src="feature_image" />
width="84" height="60"/> <n-image
v-else
class="image"
object-fit="cover"
preview-disabled
:src="feature_image"
width="84"
height="60"
/>
<div class="card-body"> <div class="card-body">
<n-text class="name">{{ name }}</n-text> <n-text class="name">{{ name }}</n-text>
<n-ellipsis :line-clamp="2" style="max-width: 100%" class="description"> <n-ellipsis :line-clamp="2" style="max-width: 100%" class="description">
@@ -1,16 +1,16 @@
<script setup lang="ts"> <script setup lang="ts">
import {computed, ref, watch} from 'vue' import { computed, ref, watch } from 'vue'
import type {ChatroomPublic} from '@/types/chatroom.js' import type { ChatroomPublic } from '@/types/chatroom.js'
import {api} from '@/tools/web.js' import { api } from '@/tools/web.js'
import type {ReturnDto} from '@/types/response.js' import type { ReturnDto } from '@/types/response.js'
import {useMessage} from 'naive-ui' import { useMessage } from 'naive-ui'
import UploadFileModal from "@/components/file/UploadFileModal.vue"; import UploadFileModal from '@/components/file/UploadFileModal.vue'
import SelectFileModal from "@/components/file/SelectFileModal.vue"; import SelectFileModal from '@/components/file/SelectFileModal.vue'
import type {UploadFileDto} from "@/types/user.js"; import type { UploadFileDto } from '@/types/user.js'
const MESSAGE = useMessage() const MESSAGE = useMessage()
const showModal = defineModel<boolean>('showModal', {required: true}) const showModal = defineModel<boolean>('showModal', { required: true })
const showSelectModal = ref(false) const showSelectModal = ref(false)
const showUploadModal = ref(false) const showUploadModal = ref(false)
@@ -34,7 +34,7 @@ watch(image_url, () => {
}) })
async function loadFiles() { async function loadFiles() {
return await api.get("/file/").then(res => files.value = res.data as UploadFileDto[]) return await api.get('/file/').then((res) => (files.value = res.data as UploadFileDto[]))
} }
function onSubmit() { function onSubmit() {
@@ -56,8 +56,13 @@ function onSubmit() {
</script> </script>
<template> <template>
<n-modal v-model:show="showModal" preset="card" title="创建聊天室" content-scrollable <n-modal
style="width: 800px;"> v-model:show="showModal"
preset="card"
title="创建聊天室"
content-scrollable
style="width: 800px"
>
<n-form <n-form
:model="createChatroomForm" :model="createChatroomForm"
label-placement="left" label-placement="left"
@@ -65,17 +70,19 @@ function onSubmit() {
label-width="auto" label-width="auto"
> >
<n-form-item path="name" label="名称"> <n-form-item path="name" label="名称">
<n-input v-model:value="createChatroomForm.name"/> <n-input v-model:value="createChatroomForm.name" />
</n-form-item> </n-form-item>
<n-form-item path="description" label="简介"> <n-form-item path="description" label="简介">
<n-input type="textarea" v-model:value="createChatroomForm.description"/> <n-input type="textarea" v-model:value="createChatroomForm.description" />
</n-form-item> </n-form-item>
<n-form-item path="feature_image" label="特色图像"> <n-form-item path="feature_image" label="特色图像">
<n-flex style="width: 100%;" :wrap="false"> <n-flex style="width: 100%" :wrap="false">
<n-input v-model:value="createChatroomForm.feature_image" <n-input
placeholder="留空以使用默认图像"/> v-model:value="createChatroomForm.feature_image"
<n-button secondary type="info" @click="showSelectModal = true;">选择</n-button> placeholder="留空以使用默认图像"
<n-button secondary type="warning" @click="showUploadModal = true;">上传</n-button> />
<n-button secondary type="info" @click="showSelectModal = true">选择</n-button>
<n-button secondary type="warning" @click="showUploadModal = true">上传</n-button>
</n-flex> </n-flex>
</n-form-item> </n-form-item>
<n-form-item label="确认?"> <n-form-item label="确认?">
@@ -83,9 +90,14 @@ function onSubmit() {
</n-form-item> </n-form-item>
</n-form> </n-form>
<select-file-modal :max="1" :extensions="['png', 'jpeg', 'jpg']" :load-files="loadFiles" <select-file-modal
v-model:show-modal="showSelectModal" v-model:select-files="selectFiles"/> :max="1"
<upload-file-modal v-model:show-modal="showUploadModal" :after-leave="loadFiles"/> :extensions="['png', 'jpeg', 'jpg']"
:load-files="loadFiles"
v-model:show-modal="showSelectModal"
v-model:select-files="selectFiles"
/>
<upload-file-modal v-model:show-modal="showUploadModal" :after-leave="loadFiles" />
</n-modal> </n-modal>
</template> </template>
+11 -12
View File
@@ -1,20 +1,20 @@
<script setup lang="ts"> <script setup lang="ts">
import {api} from '@/tools/web.js' import { api } from '@/tools/web.js'
import {ref, watch} from 'vue' import { ref, watch } from 'vue'
import {useRoute} from 'vue-router' import { useRoute } from 'vue-router'
import type {ChatScript} from '@/types/chatroom.js' import type { ChatScript } from '@/types/chatroom.js'
import type {ReturnDto} from '@/types/response.js' import type { ReturnDto } from '@/types/response.js'
import {useMessage} from 'naive-ui' import { useMessage } from 'naive-ui'
import XamlModal from '@/components/XamlModal.vue' import XamlModal from '@/components/XamlModal.vue'
const ROUTE = useRoute() const ROUTE = useRoute()
const MESSAGE = useMessage() const MESSAGE = useMessage()
const showDrawer = defineModel('showDrawer', {required: true}) const showDrawer = defineModel('showDrawer', { required: true })
const showXamlModal = ref(false) const showXamlModal = ref(false)
const {script} = defineProps<{ const { script } = defineProps<{
script: string script: string
}>() }>()
@@ -48,10 +48,9 @@ watch(
() => { () => {
try { try {
scriptForm.value = JSON.parse(script) as ChatScript scriptForm.value = JSON.parse(script) as ChatScript
} catch { } catch {}
}
}, },
{immediate: true}, { immediate: true },
) )
</script> </script>
@@ -127,7 +126,7 @@ watch(
</template> </template>
</n-drawer-content> </n-drawer-content>
<xaml-modal v-model:show-modal="showXamlModal"/> <xaml-modal v-model:show-modal="showXamlModal" />
</n-drawer> </n-drawer>
</template> </template>
+9 -8
View File
@@ -1,27 +1,28 @@
<script setup lang="ts"> <script setup lang="ts">
import type {UploadFileDto} from "@/types/user.js"; import type { UploadFileDto } from '@/types/user.js'
import {useNowUser} from "@/stores/now-user.js"; import { useNowUser } from '@/stores/now-user.js'
import {computed} from "vue"; import { computed } from 'vue'
const NOWUSER = useNowUser() const NOWUSER = useNowUser()
const {file} = defineProps<{ const { file } = defineProps<{
file: UploadFileDto file: UploadFileDto
}>() }>()
const is_you = computed(() => NOWUSER.id === file.uploader_id) const is_you = computed(() => NOWUSER.id === file.uploader_id)
const showModal = defineModel("showModal", {required: true}) const showModal = defineModel('showModal', { required: true })
</script> </script>
<template> <template>
<n-modal v-model:show="showModal" preset="card" style="width: 1000px;" title="文件信息"> <n-modal v-model:show="showModal" preset="card" style="width: 1000px" title="文件信息">
<div class="card-content"> <div class="card-content">
<n-image :width="500" :height="500" object-fit="contain" :src="file.download_url"/> <n-image :width="500" :height="500" object-fit="contain" :src="file.download_url" />
<div class="side"> <div class="side">
<n-h3>{{ file.original_name }}</n-h3> <n-h3>{{ file.original_name }}</n-h3>
<n-p>保存文件名{{ file.safe_name }}</n-p> <n-p>保存文件名{{ file.safe_name }}</n-p>
<n-p>上传用户ID{{ file.uploader_id }} <n-p
>上传用户ID{{ file.uploader_id }}
<n-tag v-if="is_you" type="primary"></n-tag> <n-tag v-if="is_you" type="primary"></n-tag>
</n-p> </n-p>
+12 -10
View File
@@ -1,9 +1,9 @@
<script setup lang="ts"> <script setup lang="ts">
import type {UploadFileDto} from "@/types/user.js"; import type { UploadFileDto } from '@/types/user.js'
import {computed, onMounted, ref, useTemplateRef} from "vue"; import { computed, onMounted, ref, useTemplateRef } from 'vue'
import FileModal from "@/components/file/FileModal.vue"; import FileModal from '@/components/file/FileModal.vue'
const {file, size, enableSelect, onSelect, onRemove} = defineProps<{ const { file, size, enableSelect, onSelect, onRemove } = defineProps<{
file: UploadFileDto file: UploadFileDto
size: number size: number
enableSelect?: boolean enableSelect?: boolean
@@ -11,12 +11,12 @@ const {file, size, enableSelect, onSelect, onRemove} = defineProps<{
onRemove?: (file: UploadFileDto) => boolean onRemove?: (file: UploadFileDto) => boolean
}>() }>()
const th = useTemplateRef("th") const th = useTemplateRef('th')
const showModal = ref(false) const showModal = ref(false)
const selected = ref(false) const selected = ref(false)
const ALLOWED_EXTENSIONS = ["jpg", "jpeg", "png"] const ALLOWED_EXTENSIONS = ['jpg', 'jpeg', 'png']
onMounted(() => { onMounted(() => {
if (ALLOWED_EXTENSIONS.includes(file.safe_name.split('.').at(-1)!.toLowerCase())) { if (ALLOWED_EXTENSIONS.includes(file.safe_name.split('.').at(-1)!.toLowerCase())) {
@@ -32,13 +32,13 @@ function onClick() {
if (selected.value && onRemove) { if (selected.value && onRemove) {
if (onRemove(file)) { if (onRemove(file)) {
selected.value = false selected.value = false
th.value?.classList.remove("selected") th.value?.classList.remove('selected')
console.log(`选中文件:${file.original_name}`) console.log(`选中文件:${file.original_name}`)
} }
} else if (!selected.value && onSelect) { } else if (!selected.value && onSelect) {
if (onSelect(file)) { if (onSelect(file)) {
selected.value = true selected.value = true
th.value?.classList.add("selected") th.value?.classList.add('selected')
console.log(`取消文件:${file.original_name}`) console.log(`取消文件:${file.original_name}`)
} }
} }
@@ -50,7 +50,7 @@ const size_px = computed(() => `${size}px`)
<template> <template>
<div class="file-thumbnail" ref="th" @click="onClick"></div> <div class="file-thumbnail" ref="th" @click="onClick"></div>
<file-modal :file v-model:show-modal="showModal"/> <file-modal :file v-model:show-modal="showModal" />
</template> </template>
<style scoped> <style scoped>
@@ -64,7 +64,9 @@ div.file-thumbnail {
background-repeat: no-repeat; background-repeat: no-repeat;
background-size: cover; background-size: cover;
transition: border-color 0.3s, box-shadow 0.3s; transition:
border-color 0.3s,
box-shadow 0.3s;
} }
div.selected { div.selected {
+27 -16
View File
@@ -1,29 +1,29 @@
<script setup lang="ts"> <script setup lang="ts">
import {selectFilesCom} from "@/components/file/upload-files.js"; import { selectFilesCom } from '@/components/file/upload-files.js'
import {computed, ref, watch} from "vue"; import { computed, ref, watch } from 'vue'
import type {UploadFileDto} from "@/types/user.js"; import type { UploadFileDto } from '@/types/user.js'
import {useMessage} from "naive-ui"; import { useMessage } from 'naive-ui'
const MESSAGE = useMessage() const MESSAGE = useMessage()
const {max, extensions, loadFiles} = defineProps<{ const { max, extensions, loadFiles } = defineProps<{
max: number max: number
extensions: string[] extensions: string[]
loadFiles: () => Promise<UploadFileDto[]> loadFiles: () => Promise<UploadFileDto[]>
}>() }>()
const showModal = defineModel("showModal", {required: true}) const showModal = defineModel('showModal', { required: true })
const files = ref<UploadFileDto[]>([]) const files = ref<UploadFileDto[]>([])
const tempFiles = ref<UploadFileDto[]>([]) const tempFiles = ref<UploadFileDto[]>([])
const selectFiles = defineModel<UploadFileDto[]>("selectFiles", {required: true}) const selectFiles = defineModel<UploadFileDto[]>('selectFiles', { required: true })
function selectFile(file: UploadFileDto) { function selectFile(file: UploadFileDto) {
if (tempFiles.value.length < max) { if (tempFiles.value.length < max) {
tempFiles.value.push(file) tempFiles.value.push(file)
return true return true
} else { } else {
MESSAGE.warning("可选择文件数量达到上限……") MESSAGE.warning('可选择文件数量达到上限……')
return false return false
} }
} }
@@ -41,27 +41,38 @@ watch(showModal, async () => {
files.value = await loadFiles() files.value = await loadFiles()
}) })
const tip_1 = computed(() => max > 1 ? `请选择至少 ${max} 个文件。` : "请选择一个文件。") const tip_1 = computed(() => (max > 1 ? `请选择至少 ${max} 个文件。` : '请选择一个文件。'))
const tip_2 = computed(() => `允许的文件类型:${extensions.join('、')}`) const tip_2 = computed(() => `允许的文件类型:${extensions.join('、')}`)
</script> </script>
<template> <template>
<n-modal preset="card" style="max-width: 600px; max-height: 600px;" title="选择文件" <n-modal
preset="card"
style="max-width: 600px; max-height: 600px"
title="选择文件"
content-scrollable content-scrollable
v-model:show="showModal"> v-model:show="showModal"
>
<n-flex vertical> <n-flex vertical>
<n-alert type="info"> <n-alert type="info">
{{ tip_1 }} {{ tip_1 }}
{{ tip_2 }} {{ tip_2 }}
</n-alert> </n-alert>
<component :is="selectFilesCom(files, selectFile, removeFile)"/> <component :is="selectFilesCom(files, selectFile, removeFile)" />
<n-button type="primary" secondary @click="selectFiles = tempFiles; showModal = false;"> <n-button
type="primary"
secondary
@click="
() => {
selectFiles = tempFiles
showModal = false
}
"
>
确认选择 确认选择
</n-button> </n-button>
</n-flex> </n-flex>
</n-modal> </n-modal>
</template> </template>
<style scoped> <style scoped></style>
</style>
+34 -23
View File
@@ -1,37 +1,37 @@
<script setup lang="ts"> <script setup lang="ts">
import {type UploadCustomRequestOptions, type UploadFileInfo} from "naive-ui"; import { type UploadCustomRequestOptions, type UploadFileInfo } from 'naive-ui'
import {api} from "@/tools/web.js"; import { api } from '@/tools/web.js'
import type {UploadFileDto} from "@/types/user.js"; import type { UploadFileDto } from '@/types/user.js'
import {shallowRef, useTemplateRef} from "vue"; import { shallowRef, useTemplateRef } from 'vue'
defineProps<{ defineProps<{
afterLeave?: () => void; afterLeave?: () => void
}>() }>()
const showModal = defineModel("showModal", {required: true}) const showModal = defineModel('showModal', { required: true })
const upload = useTemplateRef("upload") const upload = useTemplateRef('upload')
const fileList = shallowRef<UploadFileInfo[]>([]) const fileList = shallowRef<UploadFileInfo[]>([])
async function handle_upload({file, onFinish, onError, onProgress}: UploadCustomRequestOptions) { async function handle_upload({ file, onFinish, onError, onProgress }: UploadCustomRequestOptions) {
const formData = new FormData(); const formData = new FormData()
console.log(file.file) console.log(file.file)
formData.append("file", file.file!) formData.append('file', file.file!)
console.log(formData) console.log(formData)
try { try {
const data = await api.post("/file/upload/", formData, { const data = await api
.post('/file/upload/', formData, {
headers: { headers: {
'Content-Type': undefined // 取消全局默认的 application/json 很重要!!!!!!!! 'Content-Type': undefined, // 取消全局默认的 application/json 很重要!!!!!!!!
}, },
onUploadProgress: (progressEvent) => { onUploadProgress: (progressEvent) => {
const percent = Math.ceil( const percent = Math.ceil((progressEvent.loaded / progressEvent.total!) * 100)
(progressEvent.loaded / progressEvent.total!) * 100 onProgress({ percent }) // 更新进度条
) },
onProgress({percent}) // 更新进度条 })
} .then((res) => res.data as UploadFileDto)
}).then((res) => res.data as UploadFileDto)
file.url = data.download_url file.url = data.download_url
onFinish() onFinish()
@@ -47,12 +47,23 @@ function onUpload() {
</script> </script>
<template> <template>
<n-modal style="width: 600px;" preset="card" v-model:show="showModal" <n-modal
title="上传文件" content-scrollable style="width: 600px"
@after-leave="afterLeave"> preset="card"
v-model:show="showModal"
title="上传文件"
content-scrollable
@after-leave="afterLeave"
>
<n-flex vertical> <n-flex vertical>
<n-upload multiple ref="upload" :default-upload="false" list-type="image" <n-upload
:custom-request="handle_upload" v-model:file-list="fileList"> multiple
ref="upload"
:default-upload="false"
list-type="image"
:custom-request="handle_upload"
v-model:file-list="fileList"
>
<n-upload-dragger> <n-upload-dragger>
<n-p>拖拽文件到此区域可以快速上传</n-p> <n-p>拖拽文件到此区域可以快速上传</n-p>
</n-upload-dragger> </n-upload-dragger>
+22 -11
View File
@@ -1,30 +1,41 @@
import type {UploadFileDto} from "@/types/user.ts"; import type { UploadFileDto } from '@/types/user.ts'
import FileThumbnail from "@/components/file/FileThumbnail.vue"; import FileThumbnail from '@/components/file/FileThumbnail.vue'
import {NEmpty, NFlex} from "naive-ui"; import { NEmpty, NFlex } from 'naive-ui'
export function uploadFilesCom(files: UploadFileDto[]) { export function uploadFilesCom(files: UploadFileDto[]) {
if (files.length === 0) { if (files.length === 0) {
return <NEmpty description="你还没有上传任何文件。" size="large"/> return <NEmpty description="你还没有上传任何文件。" size="large" />
} }
return <NFlex> return (
<NFlex>
{files.map((file: UploadFileDto) => { {files.map((file: UploadFileDto) => {
return <FileThumbnail size={120} file={file}></FileThumbnail>; return <FileThumbnail size={120} file={file}></FileThumbnail>
})} })}
</NFlex> </NFlex>
)
} }
export function selectFilesCom( export function selectFilesCom(
files: UploadFileDto[], files: UploadFileDto[],
onSelect: (file: UploadFileDto) => boolean, onSelect: (file: UploadFileDto) => boolean,
onRemove: (file: UploadFileDto) => boolean onRemove: (file: UploadFileDto) => boolean,
) { ) {
if (files.length === 0) { if (files.length === 0) {
return <NEmpty description="你还没有上传任何文件。" size="large"/> return <NEmpty description="你还没有上传任何文件。" size="large" />
} }
return <NFlex> return (
<NFlex>
{files.map((file: UploadFileDto) => { {files.map((file: UploadFileDto) => {
return <FileThumbnail size={82} file={file} enableSelect onSelect={onSelect} return (
onRemove={onRemove}></FileThumbnail>; <FileThumbnail
size={82}
file={file}
enableSelect
onSelect={onSelect}
onRemove={onRemove}
></FileThumbnail>
)
})} })}
</NFlex> </NFlex>
)
} }
+3 -3
View File
@@ -1,6 +1,6 @@
import {createApp} from 'vue' import { createApp } from 'vue'
import {createPinia} from 'pinia' import { createPinia } from 'pinia'
import {createHead} from "@unhead/vue/client"; import { createHead } from '@unhead/vue/client'
import '@/assets/main.scss' import '@/assets/main.scss'
import '@/assets/beautiful.scss' import '@/assets/beautiful.scss'
+36 -36
View File
@@ -1,59 +1,59 @@
<script setup lang="ts"> <script setup lang="ts">
import UserAction from "@/components/admin/UserAction.vue"; import UserAction from '@/components/admin/UserAction.vue'
import type {MenuOption} from "naive-ui"; import type { MenuOption } from 'naive-ui'
import {computed, onMounted, ref, useTemplateRef} from "vue"; import { computed, onMounted, ref, useTemplateRef } from 'vue'
import {useRouter} from "vue-router"; import { useRouter } from 'vue-router'
import {useNowUser} from "@/stores/now-user.js"; import { useNowUser } from '@/stores/now-user.js'
import {useHead} from "@unhead/vue"; import { useHead } from '@unhead/vue'
useHead({ useHead({
titleTemplate: "%s | 管理面板 | NayHome" titleTemplate: '%s | 管理面板 | NayHome',
}) })
const ROUTER = useRouter() const ROUTER = useRouter()
const NOWUSER = useNowUser() const NOWUSER = useNowUser()
const menu = useTemplateRef("menu") const menu = useTemplateRef('menu')
const selectOption = ref("") const selectOption = ref('')
const options = computed<MenuOption[]>(() => [ const options = computed<MenuOption[]>(() => [
{ {
label: "总览", label: '总览',
key: "", key: '',
}, },
{ {
label: "用户", label: '用户',
key: "user-basic", key: 'user-basic',
children: [ children: [
{ {
label: "资料", label: '资料',
key: "user-info" key: 'user-info',
}, },
{ {
label: "安全", label: '安全',
key: "user-security" key: 'user-security',
} },
] ],
}, },
{ {
label: "内容", label: '内容',
key: "user-creation", key: 'user-creation',
children: [ children: [
{ {
label: "上传", label: '上传',
key: "user-upload" key: 'user-upload',
}, },
{ {
label: "剧本", label: '剧本',
key: "user-script" key: 'user-script',
} },
] ],
}, },
{ {
label: "NyaHome 管理后台", label: 'NyaHome 管理后台',
key: "nyahome", key: 'nyahome',
show: NOWUSER.is_admin, show: NOWUSER.is_admin,
} },
]) ])
function handleMenuClick(key: string) { function handleMenuClick(key: string) {
@@ -61,12 +61,12 @@ function handleMenuClick(key: string) {
} }
onMounted(() => { onMounted(() => {
const key = ROUTER.currentRoute.value.fullPath.replace("/admin/", "") const key = ROUTER.currentRoute.value.fullPath.replace('/admin/', '')
if (key) { if (key) {
selectOption.value = key selectOption.value = key
menu.value?.showOption(key) menu.value?.showOption(key)
} else { } else {
selectOption.value = "" selectOption.value = ''
} }
}) })
</script> </script>
@@ -74,16 +74,16 @@ onMounted(() => {
<template> <template>
<div id="user-page"> <div id="user-page">
<div id="user-page-sidebar"> <div id="user-page-sidebar">
<user-action/> <user-action />
<div class="nyahome-card"> <div class="nyahome-card">
<n-menu ref="menu" v-model:value="selectOption" :options @update:value="handleMenuClick"/> <n-menu ref="menu" v-model:value="selectOption" :options @update:value="handleMenuClick" />
</div> </div>
</div> </div>
<router-view v-slot="{Component}"> <router-view v-slot="{ Component }">
<div id="user-page-content"> <div id="user-page-content">
<keep-alive> <keep-alive>
<component :is="Component"/> <component :is="Component" />
</keep-alive> </keep-alive>
</div> </div>
</router-view> </router-view>
+16 -3
View File
@@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import {useRoute} from 'vue-router' import {useRoute} from 'vue-router'
import {onMounted, ref, useTemplateRef, watch} from 'vue' import {onMounted, reactive, ref, useTemplateRef, watch} from 'vue'
import {api} from '@/tools/web.ts' import {api} from '@/tools/web.ts'
import type {ReturnDto} from '@/types/response.ts' import type {ReturnDto} from '@/types/response.ts'
import type {Chatroom} from '@/types/chatroom.ts' import type {Chatroom} from '@/types/chatroom.ts'
@@ -11,6 +11,14 @@ import ChatControlPanel from '@/components/chatroom/ChatControlPanel.vue'
import {fetchEventSource} from '@microsoft/fetch-event-source' import {fetchEventSource} from '@microsoft/fetch-event-source'
import type {AiiTokenInfo} from '@/types/aii.ts' import type {AiiTokenInfo} from '@/types/aii.ts'
import {SEE_YOU_TOMORROW} from '@/types/syt.ts' import {SEE_YOU_TOMORROW} from '@/types/syt.ts'
import {useHead} from '@unhead/vue'
const pageHead = reactive({
title: '正在加载聊天室...',
titleTemplate: '%s | 聊天室 | NyaHome'
})
useHead(pageHead)
const ROUTE = useRoute() const ROUTE = useRoute()
const MESSAGE = useMessage() const MESSAGE = useMessage()
@@ -45,6 +53,7 @@ function load() {
} }
}) })
.then((cr) => { .then((cr) => {
pageHead.title = cr.name
crName.value = cr.name crName.value = cr.name
crDescription.value = cr.description crDescription.value = cr.description
crFeatureImage.value = cr.feature_image crFeatureImage.value = cr.feature_image
@@ -235,8 +244,12 @@ function enableSidebar() {
<template> <template>
<div class="page-container"> <div class="page-container">
<div class="main-column"> <div class="main-column">
<chatroom-card :id="Number(ROUTE.params.id)" :name="crName" :description="crDescription" <chatroom-card
:feature_image="crFeatureImage"/> :id="Number(ROUTE.params.id)"
:name="crName"
:description="crDescription"
:feature_image="crFeatureImage"
/>
<chat-table <chat-table
:content="crContent" :content="crContent"
:aii-thinking :aii-thinking
+12 -3
View File
@@ -5,7 +5,12 @@ import {api} from '@/tools/web.ts'
import type {ChatroomPublic} from '@/types/chatroom.ts' import type {ChatroomPublic} from '@/types/chatroom.ts'
import type {ReturnDto} from '@/types/response.ts' import type {ReturnDto} from '@/types/response.ts'
import ChatroomCreatorModal from '@/components/chatroom/ChatroomCreatorModal.vue' import ChatroomCreatorModal from '@/components/chatroom/ChatroomCreatorModal.vue'
import {useNowUser} from "@/stores/now-user.ts"; import {useNowUser} from '@/stores/now-user.ts'
import {useHead} from "@unhead/vue";
useHead({
title: "聊天室列表",
})
const NOWUSER = useNowUser() const NOWUSER = useNowUser()
@@ -23,9 +28,13 @@ function load() {
}) })
} }
watch(() => NOWUSER.isLogin, () => { watch(
() => NOWUSER.isLogin,
() => {
load() load()
}, {immediate: true}) },
{immediate: true},
)
</script> </script>
<template> <template>
+17 -3
View File
@@ -1,19 +1,33 @@
<script setup lang="ts"> <script setup lang="ts">
import UserAction from '@/components/admin/UserAction.vue' import UserAction from '@/components/admin/UserAction.vue'
import { useHead } from '@unhead/vue'
useHead({
title: '首页',
})
</script> </script>
<template> <template>
<n-flex vertical style="padding: 6px 20px"> <div class="welcome-page">
<n-flex> <n-flex>
<n-card class="welcome-card" title="Welcome to Welcome!"></n-card> <n-card class="welcome-card" title="Welcome to Welcome!"></n-card>
<div class="user-action-card"> <div class="user-action-card">
<user-action/> <user-action />
</div> </div>
</n-flex> </n-flex>
</n-flex> </div>
</template> </template>
<style scoped> <style scoped>
div.welcome-page {
min-width: min(1200px, 90%);
width: min(1200px, 90%);
min-height: 0;
padding: 6px 20px;
display: flex;
flex-direction: column;
}
.welcome-card { .welcome-card {
flex: 1; flex: 1;
} }
+57 -57
View File
@@ -1,73 +1,71 @@
<script setup lang="ts"> <script setup lang="ts">
import ConfigCard from "@/components/admin/ConfigCard.vue"; import ConfigCard from '@/components/admin/ConfigCard.vue'
import {useHead} from "@unhead/vue"; import { useHead } from '@unhead/vue'
import {ref} from "vue"; import { ref } from 'vue'
import {api} from "@/tools/web.ts"; import { api } from '@/tools/web.ts'
import InDev from "@/components/InDev.vue"; import InDev from '@/components/InDev.vue'
import {useMessage} from "naive-ui"; import { useMessage } from 'naive-ui'
import type {ReturnDto} from "@/types/response.ts"; import type { ReturnDto } from '@/types/response.ts'
interface SiteConfig { interface SiteConfig {
site_name: string; site_name: string
site_url: string; site_url: string
backend_url: string; backend_url: string
jwt_secret_key: string; jwt_secret_key: string
smtp_enable: boolean; smtp_enable: boolean
smtp_sender: string; smtp_sender: string
smtp_hostname: string; smtp_hostname: string
smtp_port: number; smtp_port: number
smtp_username: string; smtp_username: string
smtp_password: string; smtp_password: string
smtp_use_tls: boolean; smtp_use_tls: boolean
} }
const MESSAGE = useMessage() const MESSAGE = useMessage()
useHead({ useHead({
title: "NyaHome 管理后台" title: 'NyaHome 管理后台',
}) })
const siteConfig = ref<SiteConfig | null>(null); const siteConfig = ref<SiteConfig | null>(null)
function getConfig() { function getConfig() {
api.get("/admin/site_config/") api.get('/admin/site_config/').then((res) => {
.then((res) => {
siteConfig.value = res.data as SiteConfig siteConfig.value = res.data as SiteConfig
MESSAGE.success("成功获取设置~") MESSAGE.success('成功获取设置~')
}) })
} }
function saveConfig() { function saveConfig() {
api.post("/admin/site_config/", JSON.stringify(siteConfig.value)) api.post('/admin/site_config/', JSON.stringify(siteConfig.value)).then((res) => {
.then((res) => {
siteConfig.value = res.data as SiteConfig siteConfig.value = res.data as SiteConfig
MESSAGE.success("保存并刷新设置成功~") MESSAGE.success('保存并刷新设置成功~')
}) })
} }
const testMailTo = ref("25565@qq.com") const testMailTo = ref('25565@qq.com')
function sendTestMail() { function sendTestMail() {
api.post("/admin/email-test/", JSON.stringify({to: testMailTo.value})) api
.then(res => res.data as ReturnDto) .post('/admin/email-test/', JSON.stringify({ to: testMailTo.value }))
.then(data => data.success) .then((res) => res.data as ReturnDto)
.then(success => { .then((data) => data.success)
.then((success) => {
if (success) { if (success) {
MESSAGE.success("邮件发送成功,请稍等片刻,然后检查收件箱~") MESSAGE.success('邮件发送成功,请稍等片刻,然后检查收件箱~')
} else { } else {
MESSAGE.error("后端表示邮件发送失败,请检查日志输出。") MESSAGE.error('后端表示邮件发送失败,请检查日志输出。')
} }
}) })
} }
</script> </script>
<template> <template>
<n-card> <n-card>
<template #header> <template #header>
<n-h3 prefix="bar" style="margin: 0;">NyaHome 管理后台</n-h3> <n-h3 prefix="bar" style="margin: 0">NyaHome 管理后台</n-h3>
</template> </template>
<template #header-extra> <template #header-extra>
<n-flex> <n-flex>
@@ -80,19 +78,19 @@ function sendTestMail() {
<n-tabs type="card" v-if="siteConfig !== null"> <n-tabs type="card" v-if="siteConfig !== null">
<n-tab-pane name="user" tab="用户" display-directive="show"> <n-tab-pane name="user" tab="用户" display-directive="show">
<config-card title="全部用户"> <config-card title="全部用户">
<in-dev/> <in-dev />
</config-card> </config-card>
</n-tab-pane> </n-tab-pane>
<n-tab-pane name="chatroom" tab="聊天室" display-directive="show"> <n-tab-pane name="chatroom" tab="聊天室" display-directive="show">
<config-card title="全部聊天室"> <config-card title="全部聊天室">
<in-dev/> <in-dev />
</config-card> </config-card>
</n-tab-pane> </n-tab-pane>
<n-tab-pane name="script" tab="剧本" display-directive="show"> <n-tab-pane name="script" tab="剧本" display-directive="show">
<config-card title="全部剧本"> <config-card title="全部剧本">
<in-dev/> <in-dev />
</config-card> </config-card>
</n-tab-pane> </n-tab-pane>
@@ -101,29 +99,30 @@ function sendTestMail() {
<config-card title="基本信息"> <config-card title="基本信息">
<n-form> <n-form>
<n-form-item label="站点名称"> <n-form-item label="站点名称">
<n-input v-model:value="siteConfig.site_name"/> <n-input v-model:value="siteConfig.site_name" />
</n-form-item> </n-form-item>
<n-form-item label="站点地址"> <n-form-item label="站点地址">
<n-input v-model:value="siteConfig.site_url"/> <n-input v-model:value="siteConfig.site_url" />
</n-form-item> </n-form-item>
<n-alert type="info" class="in-form-alert"> <n-alert type="info" class="in-form-alert">
如果您需要将 NyaHome 的前后端分开部署则需要在此设置后端地址您需要自行处理跨域问题 如果您需要将 NyaHome
的前后端分开部署则需要在此设置后端地址您需要自行处理跨域问题
</n-alert> </n-alert>
<n-form-item label="FastAPI 后端地址"> <n-form-item label="FastAPI 后端地址">
<n-input v-model:value="siteConfig.backend_url"/> <n-input v-model:value="siteConfig.backend_url" />
</n-form-item> </n-form-item>
</n-form> </n-form>
</config-card> </config-card>
<config-card title="搜索引擎设置与 SEO"> <config-card title="搜索引擎设置与 SEO">
<in-dev/> <in-dev />
</config-card> </config-card>
</n-flex> </n-flex>
</n-tab-pane> </n-tab-pane>
<n-tab-pane name="permission" tab="权限设置" display-directive="show"> <n-tab-pane name="permission" tab="权限设置" display-directive="show">
<config-card title="用户权限"> <config-card title="用户权限">
<in-dev/> <in-dev />
</config-card> </config-card>
</n-tab-pane> </n-tab-pane>
@@ -132,11 +131,11 @@ function sendTestMail() {
<config-card title="JWT"> <config-card title="JWT">
<n-form> <n-form>
<n-alert type="info" class="in-form-alert"> <n-alert type="info" class="in-form-alert">
JWTJson Web Token签名需要一个密钥你可以手动提供一个或者自行生成一个<br/> JWTJson Web Token签名需要一个密钥你可以手动提供一个或者自行生成一个<br />
修改此密钥会导致所有用户的登录状态丢失你也会请一次性设置一个足够安全的 修改此密钥会导致所有用户的登录状态丢失你也会请一次性设置一个足够安全的
</n-alert> </n-alert>
<n-form-item label="JWT 密钥"> <n-form-item label="JWT 密钥">
<n-input v-model:value="siteConfig.jwt_secret_key"/> <n-input v-model:value="siteConfig.jwt_secret_key" />
</n-form-item> </n-form-item>
</n-form> </n-form>
</config-card> </config-card>
@@ -148,37 +147,38 @@ function sendTestMail() {
<config-card title="邮件 SMTP"> <config-card title="邮件 SMTP">
<n-form> <n-form>
<n-alert type="info" class="in-form-alert"> <n-alert type="info" class="in-form-alert">
NayHome 无法自己发送邮件需要配置 SMTP 服务<br/> NayHome 无法自己发送邮件需要配置 SMTP 服务<br />
或者你也可以关闭邮件功能当然芒果还是建议你配置一下的 或者你也可以关闭邮件功能当然芒果还是建议你配置一下的
</n-alert> </n-alert>
<n-form-item label="启用邮件功能(SMTP"> <n-form-item label="启用邮件功能(SMTP">
<n-switch v-model:value="siteConfig.smtp_enable"/> <n-switch v-model:value="siteConfig.smtp_enable" />
</n-form-item> </n-form-item>
<n-form-item label="发件人邮件地址"> <n-form-item label="发件人邮件地址">
<n-input v-model:value="siteConfig.smtp_sender"/> <n-input v-model:value="siteConfig.smtp_sender" />
</n-form-item> </n-form-item>
<n-form-item label="SMTP 主机名"> <n-form-item label="SMTP 主机名">
<n-input v-model:value="siteConfig.smtp_hostname"/> <n-input v-model:value="siteConfig.smtp_hostname" />
</n-form-item> </n-form-item>
<n-form-item label="SMTP 端口"> <n-form-item label="SMTP 端口">
<n-input-number v-model:value="siteConfig.smtp_port"/> <n-input-number v-model:value="siteConfig.smtp_port" />
</n-form-item> </n-form-item>
<n-form-item label="SMTP 用户名"> <n-form-item label="SMTP 用户名">
<n-input v-model:value="siteConfig.smtp_username"/> <n-input v-model:value="siteConfig.smtp_username" />
</n-form-item> </n-form-item>
<n-form-item label="SMTP 密码(一般应当是一个独立的应用程序密码)"> <n-form-item label="SMTP 密码(一般应当是一个独立的应用程序密码)">
<n-input v-model:value="siteConfig.smtp_password"/> <n-input v-model:value="siteConfig.smtp_password" />
</n-form-item> </n-form-item>
<n-form-item label="使用 TLS/SSL 加密"> <n-form-item label="使用 TLS/SSL 加密">
<n-switch v-model:value="siteConfig.smtp_use_tls"/> <n-switch v-model:value="siteConfig.smtp_use_tls" />
</n-form-item> </n-form-item>
</n-form> </n-form>
<template #action> <template #action>
<n-flex vertical> <n-flex vertical>
<n-text>你可以在这里测试 NayHome 的邮件系统能否使用上述 SMTP <n-text
>你可以在这里测试 NayHome 的邮件系统能否使用上述 SMTP
设置工作这会发送一封测试邮件 设置工作这会发送一封测试邮件
</n-text> </n-text>
<n-input v-model:value="testMailTo"/> <n-input v-model:value="testMailTo" />
<n-button secondary type="warning" @click="sendTestMail()">发送测试邮件</n-button> <n-button secondary type="warning" @click="sendTestMail()">发送测试邮件</n-button>
</n-flex> </n-flex>
</template> </template>
@@ -187,7 +187,7 @@ function sendTestMail() {
</n-tab-pane> </n-tab-pane>
</n-tabs> </n-tabs>
<n-empty size="large" v-else description="请尝试手动获取设置..."/> <n-empty size="large" v-else description="请尝试手动获取设置..." />
</template> </template>
<style scoped> <style scoped>
+4 -4
View File
@@ -1,10 +1,10 @@
<script setup lang="ts"> <script setup lang="ts">
import {useNowUser} from "@/stores/now-user.js"; import { useNowUser } from '@/stores/now-user.js'
import {computed} from "vue"; import { computed } from 'vue'
import {useHead} from "@unhead/vue"; import { useHead } from '@unhead/vue'
useHead({ useHead({
title: "总览" title: '总览',
}) })
const NOWUSER = useNowUser() const NOWUSER = useNowUser()
+5 -5
View File
@@ -1,19 +1,19 @@
<script setup lang="ts"> <script setup lang="ts">
import InDev from "@/components/InDev.vue"; import InDev from '@/components/InDev.vue'
import {useHead} from "@unhead/vue"; import { useHead } from '@unhead/vue'
useHead({ useHead({
title: "剧本" title: '剧本',
}) })
</script> </script>
<template> <template>
<n-card> <n-card>
<template #header> <template #header>
<n-h3 prefix="bar" style="margin: 0;">个人剧本库</n-h3> <n-h3 prefix="bar" style="margin: 0">个人剧本库</n-h3>
</template> </template>
<in-dev/> <in-dev />
</n-card> </n-card>
</template> </template>
+38 -43
View File
@@ -1,14 +1,14 @@
<script setup lang="ts"> <script setup lang="ts">
import {useNowUser} from "@/stores/now-user.js"; import { useNowUser } from '@/stores/now-user.js'
import UserPasswordModal from "@/components/admin/UserPasswordModal.vue"; import UserPasswordModal from '@/components/admin/UserPasswordModal.vue'
import {h, ref} from "vue"; import { h, ref } from 'vue'
import {api} from "@/tools/web.ts"; import { api } from '@/tools/web.ts'
import {type DataTableColumn, NTag, NText} from "naive-ui"; import { type DataTableColumn, NTag, NText } from 'naive-ui'
import InDev from "@/components/InDev.vue"; import InDev from '@/components/InDev.vue'
import {useHead} from "@unhead/vue"; import { useHead } from '@unhead/vue'
useHead({ useHead({
title: "用户安全" title: '用户安全',
}) })
const NOWUSER = useNowUser() const NOWUSER = useNowUser()
@@ -16,8 +16,8 @@ const NOWUSER = useNowUser()
const showPasswordModal = ref(false) const showPasswordModal = ref(false)
interface SecureChange { interface SecureChange {
created_at: number; created_at: number
type: "login" | "change_password" | "change_email" | "change_phone" type: 'login' | 'change_password' | 'change_email' | 'change_phone'
old: string | null old: string | null
new: string | null new: string | null
} }
@@ -26,56 +26,53 @@ const secureChanges = ref<SecureChange[]>([])
const columns = [ const columns = [
{ {
title: "记录时间", title: '记录时间',
key: "created_at", key: 'created_at',
render(row) { render(row) {
const date = new Date(row.created_at * 1000) const date = new Date(row.created_at * 1000)
return h( return h(NText, {}, { default: () => date.toLocaleString() })
NText, },
{},
{default: () => date.toLocaleString()}
)
}
}, },
{ {
title: "类型", title: '类型',
key: "type", key: 'type',
render: (row) => { render: (row) => {
return h( return h(
NTag, NTag,
{ {
type: "info" type: 'info',
}, },
{default: () => row.type} { default: () => row.type },
) )
} },
}, },
{ {
title: "事件之前", title: '事件之前',
key: "old", key: 'old',
}, },
{ {
title: "事件之后", title: '事件之后',
key: "new", key: 'new',
} },
] as DataTableColumn<SecureChange>[] ] as DataTableColumn<SecureChange>[]
function loadSecureChanges() { function loadSecureChanges() {
api.get("/admin/me/secure_changes/") api
.then(res => secureChanges.value = res.data as SecureChange[]) .get('/admin/me/secure_changes/')
.then((res) => (secureChanges.value = res.data as SecureChange[]))
} }
</script> </script>
<template> <template>
<n-card> <n-card>
<template #header> <template #header>
<n-h3 prefix="bar" style="margin: 0;">密码</n-h3> <n-h3 prefix="bar" style="margin: 0">密码</n-h3>
</template> </template>
<n-flex vertical> <n-flex vertical>
<n-alert type="warning" v-if="NOWUSER.id === 1"> <n-alert type="warning" v-if="NOWUSER.id === 1">
您正在使用 NyaHome 初始化时创建的管理员账号此账号的默认密码为 admin 您正在使用 NyaHome 初始化时创建的管理员账号此账号的默认密码为 admin
<strong>您应该及时修改默认密码</strong><br/> <strong>您应该及时修改默认密码</strong><br />
如果您已修改密码请忽略 如果您已修改密码请忽略
</n-alert> </n-alert>
<n-flex> <n-flex>
@@ -84,27 +81,27 @@ function loadSecureChanges() {
</n-flex> </n-flex>
</n-flex> </n-flex>
<user-password-modal v-model:show-modal="showPasswordModal"/> <user-password-modal v-model:show-modal="showPasswordModal" />
</n-card> </n-card>
<n-card> <n-card>
<template #header> <template #header>
<n-h3 prefix="bar" style="margin: 0;">其他登录方式</n-h3> <n-h3 prefix="bar" style="margin: 0">其他登录方式</n-h3>
</template> </template>
<n-flex vertical> <n-flex vertical>
<n-alert type="info"> <n-alert type="info">
在这里连接第三方账户之后可以使用它们进行登录<br/> 在这里连接第三方账户之后可以使用它们进行登录<br />
<strong>必须先在这里连接后才能使用第三方账户进行登录</strong> <strong>必须先在这里连接后才能使用第三方账户进行登录</strong>
</n-alert> </n-alert>
<in-dev/> <in-dev />
</n-flex> </n-flex>
</n-card> </n-card>
<n-card> <n-card>
<template #header> <template #header>
<n-h3 prefix="bar" style="margin: 0;">两步验证</n-h3> <n-h3 prefix="bar" style="margin: 0">两步验证</n-h3>
</template> </template>
<n-flex vertical> <n-flex vertical>
@@ -112,21 +109,19 @@ function loadSecureChanges() {
启用两步验证可以更好地保护您的账户这会强制此账号在登录时进行额外验证 启用两步验证可以更好地保护您的账户这会强制此账号在登录时进行额外验证
</n-alert> </n-alert>
<in-dev/> <in-dev />
</n-flex> </n-flex>
</n-card> </n-card>
<n-card> <n-card>
<template #header> <template #header>
<n-h3 prefix="bar" style="margin: 0;">安全事件记录</n-h3> <n-h3 prefix="bar" style="margin: 0">安全事件记录</n-h3>
</template> </template>
<n-flex vertical> <n-flex vertical>
<n-data-table :columns :data="secureChanges"/> <n-data-table :columns :data="secureChanges" />
<n-button secondary type="warning" @click="loadSecureChanges()">查询更新</n-button> <n-button secondary type="warning" @click="loadSecureChanges()">查询更新</n-button>
</n-flex> </n-flex>
</n-card> </n-card>
</template> </template>
<style scoped> <style scoped></style>
</style>
+30 -26
View File
@@ -1,62 +1,66 @@
<script setup lang="ts"> <script setup lang="ts">
import {ref, watch} from "vue"; import { ref, watch } from 'vue'
import UploadFileModal from "@/components/file/UploadFileModal.vue"; import UploadFileModal from '@/components/file/UploadFileModal.vue'
import {api} from "@/tools/web.js"; import { api } from '@/tools/web.js'
import type {UploadFileDto} from "@/types/user.js"; import type { UploadFileDto } from '@/types/user.js'
import {useNowUser} from "@/stores/now-user.js"; import { useNowUser } from '@/stores/now-user.js'
import {uploadFilesCom} from "@/components/file/upload-files.js"; import { uploadFilesCom } from '@/components/file/upload-files.js'
import {useHead} from "@unhead/vue"; import { useHead } from '@unhead/vue'
useHead({ useHead({
title: "上传" title: '上传',
}) })
const NOWUSER = useNowUser(); const NOWUSER = useNowUser()
const showUploadModal = ref(false) const showUploadModal = ref(false)
const files = ref<UploadFileDto[]>([]) const files = ref<UploadFileDto[]>([])
function load() { function load() {
api.get("/file/") api.get('/file/').then((res) => {
.then(res => {
files.value = res.data as UploadFileDto[] files.value = res.data as UploadFileDto[]
}) })
} }
watch(() => NOWUSER.isLogin, () => { watch(
() => NOWUSER.isLogin,
() => {
load() load()
}, {immediate: true}) },
{ immediate: true },
)
</script> </script>
<template> <template>
<n-card> <n-card>
<template #header> <template #header>
<n-h3 prefix="bar" style="margin: 0;">上传文件</n-h3> <n-h3 prefix="bar" style="margin: 0">上传文件</n-h3>
</template> </template>
<n-flex vertical> <n-flex vertical>
<n-alert type="info"> <n-alert type="info"> 接受的文件类型 </n-alert>
接受的文件类型
</n-alert>
<n-button @click="showUploadModal = true">打开上传向导</n-button> <n-button @click="showUploadModal = true">打开上传向导</n-button>
<upload-file-modal v-model:show-modal="showUploadModal" :after-leave="() => {load()}"/> <upload-file-modal
v-model:show-modal="showUploadModal"
:after-leave="
() => {
load()
}
"
/>
</n-flex> </n-flex>
</n-card> </n-card>
<n-card> <n-card>
<template #header> <template #header>
<n-h3 prefix="bar" style="margin: 0;">个人文件库</n-h3> <n-h3 prefix="bar" style="margin: 0">个人文件库</n-h3>
</template>
<template #header-extra>
您已经上传的文件都在这里,可以选择性地删除以及重新下载。
</template> </template>
<template #header-extra> 您已经上传的文件都在这里,可以选择性地删除以及重新下载。 </template>
<component :is="uploadFilesCom(files)"/> <component :is="uploadFilesCom(files)" />
</n-card> </n-card>
</template> </template>
<style scoped> <style scoped></style>
</style>
+23 -23
View File
@@ -1,14 +1,14 @@
import {createRouter, createWebHashHistory} from 'vue-router' import { createRouter, createWebHashHistory } from 'vue-router'
import ChatroomPage from '@/pages/ChatroomPage.vue' import ChatroomPage from '@/pages/ChatroomPage.vue'
import WelcomePage from '@/pages/WelcomePage.vue' import WelcomePage from '@/pages/WelcomePage.vue'
import Chatroom1Page from "@/pages/Chatroom1Page.vue"; import Chatroom1Page from '@/pages/Chatroom1Page.vue'
import AdminPage from "@/pages/AdminPage.vue"; import AdminPage from '@/pages/AdminPage.vue'
import AdminOverview from "@/pages/admin/AdminOverview.vue"; import AdminOverview from '@/pages/admin/AdminOverview.vue'
import AdminUserInfo from "@/pages/admin/AdminUserInfo.vue"; import AdminUserInfo from '@/pages/admin/AdminUserInfo.vue'
import AdminUserSecurity from "@/pages/admin/AdminUserSecurity.vue"; import AdminUserSecurity from '@/pages/admin/AdminUserSecurity.vue'
import AdminUserUpload from "@/pages/admin/AdminUserUpload.vue"; import AdminUserUpload from '@/pages/admin/AdminUserUpload.vue'
import AdminNyahome from "@/pages/admin/AdminNyahome.vue"; import AdminNyahome from '@/pages/admin/AdminNyahome.vue'
import AdminUserScript from "@/pages/admin/AdminUserScript.vue"; import AdminUserScript from '@/pages/admin/AdminUserScript.vue'
const router = createRouter({ const router = createRouter({
history: createWebHashHistory(import.meta.env.BASE_URL), history: createWebHashHistory(import.meta.env.BASE_URL),
@@ -34,36 +34,36 @@ const router = createRouter({
component: AdminPage, component: AdminPage,
children: [ children: [
{ {
name: "admin-overview", name: 'admin-overview',
path: "", path: '',
component: AdminOverview, component: AdminOverview,
}, },
{ {
name: "admin-user-info", name: 'admin-user-info',
path: "user-info", path: 'user-info',
component: AdminUserInfo, component: AdminUserInfo,
}, },
{ {
name: "admin-user-security", name: 'admin-user-security',
path: "user-security", path: 'user-security',
component: AdminUserSecurity, component: AdminUserSecurity,
}, },
{ {
name: "admin-user-upload", name: 'admin-user-upload',
path: "user-upload", path: 'user-upload',
component: AdminUserUpload, component: AdminUserUpload,
}, },
{ {
name: "admin-user-script", name: 'admin-user-script',
path: "user-script", path: 'user-script',
component: AdminUserScript, component: AdminUserScript,
}, },
{ {
name: "admin-nyahome", name: 'admin-nyahome',
path: "nyahome", path: 'nyahome',
component: AdminNyahome, component: AdminNyahome,
} },
] ],
}, },
], ],
}) })
+5 -7
View File
@@ -1,7 +1,7 @@
import {defineStore} from 'pinia' import { defineStore } from 'pinia'
import {ref} from 'vue' import { ref } from 'vue'
import {api, setApiToken} from '@/tools/web.ts' import { api, setApiToken } from '@/tools/web.ts'
import type {UserDto} from '@/types/user.ts' import type { UserDto } from '@/types/user.ts'
export const useNowUser = defineStore('now-user', () => { export const useNowUser = defineStore('now-user', () => {
const isLogin = ref(false) const isLogin = ref(false)
@@ -22,9 +22,7 @@ export const useNowUser = defineStore('now-user', () => {
let user: UserDto let user: UserDto
try { try {
user = await api user = await api.get('/admin/me/').then((res) => res.data as UserDto)
.get('/admin/me/')
.then((res) => res.data as UserDto)
} catch (err) { } catch (err) {
console.error(`请求用户信息时失败:${err}`) console.error(`请求用户信息时失败:${err}`)
throw err throw err