style(webui): Oxc format 已有代码
This commit is contained in:
+9
-9
@@ -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>
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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>
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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
@@ -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'
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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">
|
||||||
JWT(Json Web Token)签名需要一个密钥,你可以手动提供一个,或者自行生成一个。<br/>
|
JWT(Json 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>
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
|
||||||
|
|||||||
@@ -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
@@ -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,
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user