style(webui): 在 Oxfmt 中配置导入排序功能,并对全 webui 目录进行格式化

This commit is contained in:
2026-05-28 21:03:11 +08:00
parent d62a9d9304
commit 1f1ac5f87a
37 changed files with 427 additions and 185 deletions
+2 -1
View File
@@ -2,5 +2,6 @@
"$schema": "./node_modules/oxfmt/configuration_schema.json",
"semi": false,
"singleQuote": true,
"bracketSpacing": true
"bracketSpacing": true,
"sortImports": true
}
+10
View File
@@ -21,11 +21,13 @@ declare module 'vue' {
ChatPromptQuicker: typeof import('./src/components/chatroom/ChatPromptQuicker.vue')['default']
ChatroomCard: typeof import('./src/components/chatroom/ChatroomCard.vue')['default']
ChatroomCreatorModal: typeof import('./src/components/chatroom/ChatroomCreatorModal.vue')['default']
ChatroomEditorModal: typeof import('./src/components/chatroom/ChatroomEditorModal.vue')['default']
ChatTable: typeof import('./src/components/chatroom/ChatTable.vue')['default']
ConfigCard: typeof import('./src/components/admin/ConfigCard.vue')['default']
FileModal: typeof import('./src/components/file/FileModal.vue')['default']
FileThumbnail: typeof import('./src/components/file/FileThumbnail.vue')['default']
InDev: typeof import('./src/components/InDev.vue')['default']
InputFile: typeof import('./src/components/file/InputFile.vue')['default']
NAlert: typeof import('naive-ui')['NAlert']
NAvatar: typeof import('naive-ui')['NAvatar']
NButton: typeof import('naive-ui')['NButton']
@@ -47,6 +49,7 @@ declare module 'vue' {
NGridItem: typeof import('naive-ui')['NGridItem']
NH2: typeof import('naive-ui')['NH2']
NH3: typeof import('naive-ui')['NH3']
NH4: typeof import('naive-ui')['NH4']
NImage: typeof import('naive-ui')['NImage']
NInput: typeof import('naive-ui')['NInput']
NInputNumber: typeof import('naive-ui')['NInputNumber']
@@ -56,6 +59,7 @@ declare module 'vue' {
NModal: typeof import('naive-ui')['NModal']
NModalProvider: typeof import('naive-ui')['NModalProvider']
NP: typeof import('naive-ui')['NP']
NPopover: typeof import('naive-ui')['NPopover']
NRadio: typeof import('naive-ui')['NRadio']
NRadioButton: typeof import('naive-ui')['NRadioButton']
NRadioGroup: typeof import('naive-ui')['NRadioGroup']
@@ -65,6 +69,7 @@ declare module 'vue' {
NTabs: typeof import('naive-ui')['NTabs']
NTag: typeof import('naive-ui')['NTag']
NText: typeof import('naive-ui')['NText']
NTooltip: typeof import('naive-ui')['NTooltip']
NUpload: typeof import('naive-ui')['NUpload']
NUploadDragger: typeof import('naive-ui')['NUploadDragger']
PageHeader: typeof import('./src/components/PageHeader.vue')['default']
@@ -92,11 +97,13 @@ declare global {
const ChatPromptQuicker: typeof import('./src/components/chatroom/ChatPromptQuicker.vue')['default']
const ChatroomCard: typeof import('./src/components/chatroom/ChatroomCard.vue')['default']
const ChatroomCreatorModal: typeof import('./src/components/chatroom/ChatroomCreatorModal.vue')['default']
const ChatroomEditorModal: typeof import('./src/components/chatroom/ChatroomEditorModal.vue')['default']
const ChatTable: typeof import('./src/components/chatroom/ChatTable.vue')['default']
const ConfigCard: typeof import('./src/components/admin/ConfigCard.vue')['default']
const FileModal: typeof import('./src/components/file/FileModal.vue')['default']
const FileThumbnail: typeof import('./src/components/file/FileThumbnail.vue')['default']
const InDev: typeof import('./src/components/InDev.vue')['default']
const InputFile: typeof import('./src/components/file/InputFile.vue')['default']
const NAlert: typeof import('naive-ui')['NAlert']
const NAvatar: typeof import('naive-ui')['NAvatar']
const NButton: typeof import('naive-ui')['NButton']
@@ -118,6 +125,7 @@ declare global {
const NGridItem: typeof import('naive-ui')['NGridItem']
const NH2: typeof import('naive-ui')['NH2']
const NH3: typeof import('naive-ui')['NH3']
const NH4: typeof import('naive-ui')['NH4']
const NImage: typeof import('naive-ui')['NImage']
const NInput: typeof import('naive-ui')['NInput']
const NInputNumber: typeof import('naive-ui')['NInputNumber']
@@ -127,6 +135,7 @@ declare global {
const NModal: typeof import('naive-ui')['NModal']
const NModalProvider: typeof import('naive-ui')['NModalProvider']
const NP: typeof import('naive-ui')['NP']
const NPopover: typeof import('naive-ui')['NPopover']
const NRadio: typeof import('naive-ui')['NRadio']
const NRadioButton: typeof import('naive-ui')['NRadioButton']
const NRadioGroup: typeof import('naive-ui')['NRadioGroup']
@@ -136,6 +145,7 @@ declare global {
const NTabs: typeof import('naive-ui')['NTabs']
const NTag: typeof import('naive-ui')['NTag']
const NText: typeof import('naive-ui')['NText']
const NTooltip: typeof import('naive-ui')['NTooltip']
const NUpload: typeof import('naive-ui')['NUpload']
const NUploadDragger: typeof import('naive-ui')['NUploadDragger']
const PageHeader: typeof import('./src/components/PageHeader.vue')['default']
+5 -4
View File
@@ -1,9 +1,10 @@
<script setup lang="ts">
import PageHeader from '@/components/PageHeader.vue'
import { dateZhCN, zhCN } from 'naive-ui'
import { useNowUser } from '@/stores/now-user.ts'
import { onMounted } from 'vue'
import { useHead } from '@unhead/vue'
import { dateZhCN, zhCN } from 'naive-ui'
import { onMounted } from 'vue'
import PageHeader from '@/components/PageHeader.vue'
import { useNowUser } from '@/stores/now-user.ts'
const NOWUSER = useNowUser()
+34 -14
View File
@@ -1,6 +1,9 @@
div.message {
position: relative;
padding: 4px 12px;
padding: 0 12px;
border-width: 0 0 0 4px;
border-style: solid;
border-radius: 4px;
font-size: 1rem;
height: max-content;
@@ -18,35 +21,52 @@ div.message {
}
div.user-message {
border: 2px solid #e1ff20;
border-radius: 4px;
background: #fffbb1;
border-color: #e1ff20;
background: #fffbdb;
}
div.aii-message {
border: 2px solid #20ff54;
border-radius: 4px;
background: #b1ffd0;
border-color: #20ff54;
background: #e6fff1;
}
div.aii-message-streaming {
border: 2px solid #20d2ff;
border-radius: 4px;
background: #b1f8ff;
border-color: #20d2ff;
background: #dbfaff;
div.thinking {
border: 1px solid #e9ff20;
border: solid #5f20ff;
border-width: 0 0 0 2px;
border-radius: 4px;
padding: 4px 8px;
padding: 8px 12px;
margin: 6px 3px;
background: rgb(34 197 94 / 0.2);
background: rgb(205 127 255 / 0.2);
min-height: 60px;
}
}
// 折叠消息
div.collapse {
overflow: hidden;
min-height: 80px;
min-height: 160px;
&::after {
content: '● ● ●';
text-align: center;
display: block;
position: absolute;
font-size: 18px;
width: 80px;
height: 32px;
bottom: 10px;
left: 50%;
transform: translateX(-50%);
background: rgb(255 255 255 / 0.6);
border: 1px solid rgb(255 255 255);
border-radius: 4px;
}
}
div.xaml-block {
+1
View File
@@ -1,5 +1,6 @@
<script setup lang="ts">
import { ref } from 'vue'
import { createErrorBlock, createXamlBlock, type Xaml } from '@/components/xaml-block.tsx'
const showModal = defineModel('showModal', { required: true })
@@ -1,10 +1,11 @@
<script setup lang="ts">
import { ref } from 'vue'
import { api } from '@/tools/web.ts'
import type { ReturnDto } from '@/types/response.ts'
import { useMessage } from 'naive-ui'
import { ref } from 'vue'
import VerifyCodeModal from '@/components/admin/VerifyCodeModal.vue'
import { useNowUser } from '@/stores/now-user.ts'
import { api } from '@/tools/web.ts'
import type { ReturnDto } from '@/types/response.ts'
const MESSAGE = useMessage()
const NOWUSER = useNowUser()
+26 -8
View File
@@ -1,10 +1,11 @@
<script setup lang="ts">
import { AxiosError } from 'axios'
import { useMessage } from 'naive-ui'
import { ref } from 'vue'
import { useNowUser } from '@/stores/now-user.js'
import { api } from '@/tools/web.js'
import type { ReturnDto } from '@/types/response.js'
import { useMessage } from 'naive-ui'
import { AxiosError } from 'axios'
import { useNowUser } from '@/stores/now-user.js'
const MESSAGE = useMessage()
const NOWUSER = useNowUser()
@@ -20,7 +21,7 @@ const loginForm = ref({
function login() {
api
.post(`/admin/login/${loginMethod.value}`, {
.post(`/admin/login/${loginMethod.value}/`, {
username: loginForm.value.username,
password: loginForm.value.password,
})
@@ -50,11 +51,18 @@ function login() {
MESSAGE.error(`登录失败:${err_msg}`)
})
}
function logout() {
NOWUSER.is_login = false
localStorage.removeItem('user-id')
localStorage.removeItem('access-token')
MESSAGE.success('已注销登录状态……')
}
</script>
<template>
<div>
<div class="user-action nyahome-card" v-if="NOWUSER.isLogin" style="position: relative">
<div class="user-action nyahome-card" v-if="NOWUSER.is_login" style="position: relative">
<img :src="NOWUSER.background_url" alt="User Background" class="user-action-background" />
<div class="card-content" style="margin-top: auto; margin-bottom: 20px">
<n-avatar :size="96" circle :src="NOWUSER.avatar_url" />
@@ -69,9 +77,19 @@ function login() {
<router-link class="card-button" :to="`/user/${NOWUSER.id}`">
<n-button type="info" style="width: 100%" secondary>主页</n-button>
</router-link>
<router-link class="card-button" to="#">
<n-button type="error" style="width: 100%" secondary>注销</n-button>
</router-link>
<n-popover trigger="click">
<template #trigger>
<n-button class="card-button" type="error" style="width: 100%; padding: 0" secondary>
注销
</n-button>
</template>
<n-flex vertical>
<n-alert type="info">
确认注销登录吗注销后你可以重新登录或者切换至其他账号
</n-alert>
<n-button type="warning" secondary @click="logout()">确认注销</n-button>
</n-flex>
</n-popover>
</n-flex>
</div>
</div>
@@ -1,10 +1,11 @@
<script setup lang="ts">
import { ref } from 'vue'
import { api } from '@/tools/web.ts'
import { useNowUser } from '@/stores/now-user.ts'
import { useMessage } from 'naive-ui'
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import { useNowUser } from '@/stores/now-user.ts'
import { api } from '@/tools/web.ts'
const ROUTER = useRouter()
const MESSAGE = useMessage()
const NOWUSER = useNowUser()
@@ -21,7 +22,7 @@ function change() {
.post('/admin/me/password/', JSON.stringify(changeForm.value))
.then(() => {
MESSAGE.success('密码修改成功,请重新登录。')
NOWUSER.isLogin = false
NOWUSER.is_login = false
localStorage.removeItem('user-id')
localStorage.removeItem('access-token')
ROUTER.push('/')
@@ -46,8 +47,8 @@ function change() {
<n-flex>
<n-button type="error" @click="change()">确认修改</n-button>
<n-tag type="warning" size="large"
>修改密码会注销所有已登录状态您将需要重新登录</n-tag
>
>修改密码会注销所有已登录状态您将需要重新登录
</n-tag>
</n-flex>
</n-form-item>
</n-form>
@@ -1,10 +1,11 @@
<script setup lang="ts">
import { type SelectOption, useMessage } from 'naive-ui'
import { computed, onMounted, ref, watch } from 'vue'
import AiiProviderAddModal from '@/components/chatroom/AiiProviderAddModal.vue'
import { api } from '@/tools/web.js'
import type { ReturnDto } from '@/types/response.js'
import { type SelectOption, useMessage } from 'naive-ui'
import type { AiiProviderPublicWithoutKey } from '@/types/aii.js'
import type { ReturnDto } from '@/types/response.js'
const MESSAGE = useMessage()
@@ -1,8 +1,9 @@
<script setup lang="ts">
import { useMessage } from 'naive-ui'
import { ref } from 'vue'
import { api } from '@/tools/web.js'
import type { ReturnDto } from '@/types/response.js'
import { useMessage } from 'naive-ui'
const MESSAGE = useMessage()
@@ -1,23 +1,29 @@
<script setup lang="ts">
import { computed, onMounted, ref } from 'vue'
import { api } from '@/tools/web.js'
import type { ReturnDto } from '@/types/response.js'
import { type SelectOption, useMessage } from 'naive-ui'
import AiiModelAddModal from '@/components/chatroom/AiiModelAddModal.vue'
import type { AiiModelPublic } from '@/types/aii.js'
import ChatPromptQuicker from '@/components/chatroom/ChatPromptQuicker.vue'
import ScriptDrawer from '@/components/chatroom/ScriptDrawer.vue'
import { computed, onMounted, ref } from 'vue'
import AiiModelAddModal from '@/components/chatroom/AiiModelAddModal.vue'
import ChatPromptQuicker from '@/components/chatroom/ChatPromptQuicker.vue'
import ChatroomEditorModal from '@/components/chatroom/ChatroomEditorModal.vue'
import ScriptDrawer from '@/components/chatroom/ScriptDrawer.vue'
import { useNowUser } from '@/stores/now-user.ts'
import { api } from '@/tools/web.js'
import type { AiiModelPublic } from '@/types/aii.js'
import type { Chatroom, ChatroomPublic } from '@/types/chatroom.ts'
import type { ReturnDto } from '@/types/response.js'
const NOWUSER = useNowUser()
const MESSAGE = useMessage()
const selectedModel = defineModel<number | null>('selectModel', { required: true })
const quickerPrompt = defineModel<string>('quickerPrompt', { required: true })
const { script } = defineProps<{
script: string
const { chatroom, loadPage } = defineProps<{
chatroom: Chatroom
loadPage: () => void
}>()
const showModal = ref(false)
const showAddModelModal = ref(false)
const models = ref<AiiModelPublic[]>([])
const showScriptDrawer = ref(false)
@@ -32,7 +38,7 @@ const modelOptions = computed(() => {
return options
})
function load() {
function loadModels() {
api
.get('/aii/model')
.then((res) => res.data as ReturnDto)
@@ -49,8 +55,42 @@ function load() {
}
onMounted(() => {
load()
loadModels()
if (chatroom.default_model_id) {
selectedModel.value = chatroom.default_model_id
} else {
MESSAGE.info(
'此聊天室还未设置默认模型。你需要选择一个模型然后开始聊天,或者现在就保存一个默认模型嘛?',
)
}
})
const showChatroomInfoModal = ref(false)
const chatroomInfo = ref<ChatroomPublic>({
id: chatroom.id,
name: chatroom.name,
description: chatroom.description,
feature_image: chatroom.feature_image,
default_model_id: chatroom.default_model_id,
script_template_id: chatroom.script_template_id,
script_template_version: chatroom.script_template_version,
})
function saveDefaultModel() {
if (selectedModel.value) {
chatroomInfo.value.default_model_id = selectedModel.value
api
.post(`/chatroom/${chatroom.id}/`, JSON.stringify(chatroomInfo.value))
.then(() => {
MESSAGE.success('默认模型设置成功~')
})
.catch((err) => {
MESSAGE.error(`设置默认模型失败:${err}`)
})
} else {
MESSAGE.warning('请先选择一个模型哦~')
}
}
</script>
<template>
@@ -58,18 +98,37 @@ onMounted(() => {
<n-card title="模型">
<template #header-extra>
<n-flex>
<n-button secondary type="info" size="small" round @click="load()">刷新</n-button>
<n-button secondary type="warning" size="small" round @click="showModal = true">
<n-button secondary type="info" size="small" round @click="loadModels()">刷新</n-button>
<n-button
v-if="NOWUSER.is_admin"
secondary
type="warning"
size="small"
round
@click="showAddModelModal = true"
>
添加
</n-button>
<n-button-group>
<n-button secondary type="primary" size="small" round>保存</n-button>
<n-button secondary type="tertiary" size="small" round>?</n-button>
<n-button secondary type="primary" size="small" round @click="saveDefaultModel()">
保存
</n-button>
<n-popover>
<template #trigger>
<n-button secondary type="tertiary" size="small" round>?</n-button>
</template>
<n-h4>有哪些模型</n-h4>
<n-p>NyaHome 管理员可以添加模型然后所有用户都可以使用这些模型</n-p>
<n-p>NyaHome 不提供模型调用</n-p>
<n-h4>默认模型切换模型</n-h4>
<n-p>可以针对聊天室保存一个默认模型</n-p>
<n-p>你也可以在这里切换其他的模型但在保存之前默认模型不会修改</n-p>
</n-popover>
</n-button-group>
</n-flex>
</template>
<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="showAddModelModal" />
</n-card>
<chat-prompt-quicker v-model:prompt-prefix="quickerPrompt" />
@@ -82,15 +141,22 @@ onMounted(() => {
故事设定 · 世界书
</n-button>
</n-flex>
<script-drawer :script v-model:show-drawer="showScriptDrawer" />
<script-drawer :script="chatroom.script" v-model:show-drawer="showScriptDrawer" />
</n-card>
<n-card title="设置">
<template #header-extra>也许你不需要修改这里</template>
<n-flex vertical>
<n-button secondary type="primary">聊天室信息</n-button>
<n-button secondary type="primary" @click="showChatroomInfoModal = true">
聊天室信息
</n-button>
<n-button secondary type="info">系统设置</n-button>
</n-flex>
<chatroom-editor-modal
:chatroom="chatroomInfo"
v-model:show-modal="showChatroomInfoModal"
:reload="loadPage"
/>
</n-card>
</n-flex>
</template>
@@ -1,7 +1,8 @@
<script setup lang="ts">
import { md } from '@/tools/md.js'
import { onMounted, ref, useTemplateRef } from 'vue'
import { md } from '@/tools/md.js'
const { role, msg } = defineProps<{
role: 'aii' | 'user'
msg: string
@@ -1,5 +1,8 @@
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { useMessage } from 'naive-ui'
import { onMounted, ref, watch } from 'vue'
const MESSAGE = useMessage()
const promptPrefix = defineModel<string>('promptPrefix', { required: true })
@@ -8,12 +11,31 @@ const quickerForm = ref({
style: '第三人称全知视角,禁止打破第四面墙。',
})
function save() {
promptPrefix.value = `<要求><输出字数>${quickerForm.value.length}</输出字数><风格约束>${quickerForm.value.style}</风格约束></要求>`
function saveToBrowser() {
localStorage.setItem('prompt-quicker', JSON.stringify(quickerForm.value))
MESSAGE.success('已保存快速提示词至浏览器。')
}
function loadFromBrowser() {
const temp = localStorage.getItem('prompt-quicker')
if (temp) {
quickerForm.value = JSON.parse(temp)
MESSAGE.success('从浏览器中读取到保存的快速提示词。')
} else {
MESSAGE.info('未找到保存的快速提示词。')
}
}
// 将本组件中进行的对快速提示词的修改自动同步到父级
watch(
() => quickerForm.value,
(newValue) => {
promptPrefix.value = `<要求><输出字数>${newValue.length}</输出字数><风格约束>${newValue.style}</风格约束></要求>`
},
)
onMounted(() => {
save()
loadFromBrowser()
})
</script>
@@ -21,8 +43,17 @@ onMounted(() => {
<n-card title="快速调整">
<template #header-extra>
<n-button-group>
<n-button secondary type="primary" size="small" round @click="save()">保存</n-button>
<n-button secondary type="tertiary" size="small" round>?</n-button>
<n-button secondary type="primary" size="small" round @click="saveToBrowser()">
保存
</n-button>
<n-popover>
<template #trigger>
<n-button secondary type="tertiary" size="small" round>?</n-button>
</template>
<n-h4>这是什么</n-h4>
<n-p>快速预设一些可能需要随时修改的提示词它们会被结构化地拼接在用户消息中</n-p>
<n-p>您的修改无需保存即可随请求发送保存可以将提示词存储在浏览器中</n-p>
</n-popover>
</n-button-group>
</template>
<n-form :model="quickerForm">
@@ -1,42 +1,25 @@
<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import type { ChatroomPublic } from '@/types/chatroom.js'
import { api } from '@/tools/web.js'
import type { ReturnDto } from '@/types/response.js'
import { useMessage } from 'naive-ui'
import UploadFileModal from '@/components/file/UploadFileModal.vue'
import { ref } from 'vue'
import InputFile from '@/components/file/InputFile.vue'
import SelectFileModal from '@/components/file/SelectFileModal.vue'
import type { UploadFileDto } from '@/types/user.js'
import UploadFileModal from '@/components/file/UploadFileModal.vue'
import { api } from '@/tools/web.js'
import type { ChatroomPublic } from '@/types/chatroom.js'
import type { ReturnDto } from '@/types/response.js'
const MESSAGE = useMessage()
const showModal = defineModel<boolean>('showModal', { required: true })
const showSelectModal = ref(false)
const showUploadModal = ref(false)
const files = ref<UploadFileDto[]>([])
const selectFiles = ref<UploadFileDto[]>([])
const image_url = computed(() => selectFiles.value.at(0)?.download_url)
const createChatroomForm = ref<ChatroomPublic>({
id: 0,
name: '',
description: '',
feature_image: '',
script_template_id: 0,
script_template_version: '',
})
watch(image_url, () => {
if (image_url.value) {
createChatroomForm.value.feature_image = image_url.value
}
})
async function loadFiles() {
return await api.get('/file/').then((res) => (files.value = res.data as UploadFileDto[]))
}
function onSubmit() {
api
.post('/chatroom/', JSON.stringify(createChatroomForm.value))
@@ -76,14 +59,7 @@ function onSubmit() {
<n-input type="textarea" v-model:value="createChatroomForm.description" />
</n-form-item>
<n-form-item path="feature_image" label="特色图像">
<n-flex style="width: 100%" :wrap="false">
<n-input
v-model:value="createChatroomForm.feature_image"
placeholder="留空以使用默认图像"
/>
<n-button secondary type="info" @click="showSelectModal = true">选择</n-button>
<n-button secondary type="warning" @click="showUploadModal = true">上传</n-button>
</n-flex>
<input-file v-model:value="createChatroomForm.feature_image" />
</n-form-item>
<n-form-item label="确认?">
<n-button secondary type="primary" @click="onSubmit()">确认</n-button>
@@ -0,0 +1,56 @@
<script setup lang="ts">
import { useMessage } from 'naive-ui'
import InputFile from '@/components/file/InputFile.vue'
import { api } from '@/tools/web.ts'
import type { ChatroomPublic } from '@/types/chatroom.ts'
const MESSAGE = useMessage()
const showModal = defineModel('showModal', { required: true })
const { chatroom, reload } = defineProps<{
chatroom: ChatroomPublic
reload: () => void
}>()
function submit() {
api
.post(`/chatroom/${chatroom.id}/`, JSON.stringify(chatroom))
.then(() => {
MESSAGE.success('成功修改聊天室基本信息~')
showModal.value = false
reload() // 由调用方重新获取聊天室数据
})
.catch((err) => {
MESSAGE.error(`修改聊天室基本信息失败:${err}`)
})
}
</script>
<template>
<n-modal
v-model:show="showModal"
title="修改聊天室信息"
style="width: 800px; max-height: 600px"
content-scrollable
preset="card"
>
<n-form label-placement="left" label-align="right" label-width="auto" :model="chatroom">
<n-form-item label="聊天室名称" path="name">
<n-input v-model:value="chatroom.name" />
</n-form-item>
<n-form-item label="聊天室描述" path="description">
<n-input v-model:value="chatroom.description" type="textarea" :rows="3" />
</n-form-item>
<n-form-item label="特色图片" path="feature_image">
<input-file v-model:value="chatroom.feature_image" />
</n-form-item>
<n-form-item label="保存">
<n-button secondary type="primary" @click="submit()">保存</n-button>
</n-form-item>
</n-form>
</n-modal>
</template>
<style scoped></style>
@@ -1,11 +1,12 @@
<script setup lang="ts">
import { api } from '@/tools/web.js'
import { useMessage } from 'naive-ui'
import { ref, watch } from 'vue'
import { useRoute } from 'vue-router'
import XamlModal from '@/components/XamlModal.vue'
import { api } from '@/tools/web.js'
import type { ChatScript } from '@/types/chatroom.js'
import type { ReturnDto } from '@/types/response.js'
import { useMessage } from 'naive-ui'
import XamlModal from '@/components/XamlModal.vue'
const ROUTE = useRoute()
const MESSAGE = useMessage()
+3 -2
View File
@@ -1,8 +1,9 @@
<script setup lang="ts">
import type { UploadFileDto } from '@/types/user.js'
import { useNowUser } from '@/stores/now-user.js'
import { computed } from 'vue'
import { useNowUser } from '@/stores/now-user.js'
import type { UploadFileDto } from '@/types/user.js'
const NOWUSER = useNowUser()
const { file } = defineProps<{
+2 -1
View File
@@ -1,7 +1,8 @@
<script setup lang="ts">
import type { UploadFileDto } from '@/types/user.js'
import { computed, onMounted, ref, useTemplateRef } from 'vue'
import FileModal from '@/components/file/FileModal.vue'
import type { UploadFileDto } from '@/types/user.js'
const { file, size, enableSelect, onSelect, onRemove } = defineProps<{
file: UploadFileDto
+44
View File
@@ -0,0 +1,44 @@
<script setup lang="ts">
import { ref, watch } from 'vue'
import SelectFileModal from '@/components/file/SelectFileModal.vue'
import UploadFileModal from '@/components/file/UploadFileModal.vue'
import { api } from '@/tools/web.ts'
import type { UploadFileDto } from '@/types/user.ts'
const value = defineModel('value', { required: true })
const showSelectModal = ref(false)
const showUploadModal = ref(false)
const selectFiles = ref<UploadFileDto[]>([])
async function loadFiles() {
return await api.get('/file/').then((res) => res.data as UploadFileDto[])
}
watch(
() => selectFiles.value,
() => {
value.value = selectFiles.value[0]?.download_url
},
)
</script>
<template>
<n-flex style="width: 100%" :wrap="false">
<n-input v-model:value="value" placeholder="留空以使用默认图像" />
<n-button secondary type="info" @click="showSelectModal = true">选择</n-button>
<n-button secondary type="warning" @click="showUploadModal = true">上传</n-button>
</n-flex>
<select-file-modal
:max="1"
: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" />
</template>
<style scoped></style>
@@ -1,8 +1,9 @@
<script setup lang="ts">
import { selectFilesCom } from '@/components/file/upload-files.js'
import { computed, ref, watch } from 'vue'
import type { UploadFileDto } from '@/types/user.js'
import { useMessage } from 'naive-ui'
import { computed, ref, watch } from 'vue'
import { selectFilesCom } from '@/components/file/upload-files.js'
import type { UploadFileDto } from '@/types/user.js'
const MESSAGE = useMessage()
@@ -1,8 +1,9 @@
<script setup lang="ts">
import { type UploadCustomRequestOptions, type UploadFileInfo } from 'naive-ui'
import { shallowRef, useTemplateRef } from 'vue'
import { api } from '@/tools/web.js'
import type { UploadFileDto } from '@/types/user.js'
import { shallowRef, useTemplateRef } from 'vue'
defineProps<{
afterLeave?: () => void
+3 -2
View File
@@ -1,7 +1,8 @@
import type { UploadFileDto } from '@/types/user.ts'
import FileThumbnail from '@/components/file/FileThumbnail.vue'
import { NEmpty, NFlex } from 'naive-ui'
import FileThumbnail from '@/components/file/FileThumbnail.vue'
import type { UploadFileDto } from '@/types/user.ts'
export function uploadFilesCom(files: UploadFileDto[]) {
if (files.length === 0) {
return <NEmpty description="你还没有上传任何文件。" size="large" />
+2 -3
View File
@@ -1,11 +1,10 @@
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import { createHead } from '@unhead/vue/client'
import { createPinia } from 'pinia'
import { createApp } from 'vue'
import '@/assets/main.scss'
import '@/assets/beautiful.scss'
import '@/assets/chat.scss'
import App from './App.vue'
import router from './router'
+3 -2
View File
@@ -1,10 +1,11 @@
<script setup lang="ts">
import UserAction from '@/components/admin/UserAction.vue'
import { useHead } from '@unhead/vue'
import type { MenuOption } from 'naive-ui'
import { computed, onMounted, ref, useTemplateRef } from 'vue'
import { useRouter } from 'vue-router'
import UserAction from '@/components/admin/UserAction.vue'
import { useNowUser } from '@/stores/now-user.js'
import { useHead } from '@unhead/vue'
useHead({
titleTemplate: '%s | 管理面板 | NayHome',
+22 -28
View File
@@ -1,17 +1,18 @@
<script setup lang="ts">
import { useRoute } from 'vue-router'
import { onMounted, reactive, ref, useTemplateRef, watch } from 'vue'
import { api } from '@/tools/web.ts'
import type { ReturnDto } from '@/types/response.ts'
import type { Chatroom } from '@/types/chatroom.ts'
import { fetchEventSource } from '@microsoft/fetch-event-source'
import { useHead } from '@unhead/vue'
import { useMessage } from 'naive-ui'
import { onMounted, reactive, ref, useTemplateRef, watch } from 'vue'
import { useRoute } from 'vue-router'
import ChatControlPanel from '@/components/chatroom/ChatControlPanel.vue'
import ChatroomCard from '@/components/chatroom/ChatroomCard.vue'
import ChatTable from '@/components/chatroom/ChatTable.vue'
import ChatControlPanel from '@/components/chatroom/ChatControlPanel.vue'
import { fetchEventSource } from '@microsoft/fetch-event-source'
import { api } from '@/tools/web.ts'
import type { AiiTokenInfo } from '@/types/aii.ts'
import type { Chatroom } from '@/types/chatroom.ts'
import type { ReturnDto } from '@/types/response.ts'
import { SEE_YOU_TOMORROW } from '@/types/syt.ts'
import { useHead } from '@unhead/vue'
const pageHead = reactive({
title: '正在加载聊天室...',
@@ -23,11 +24,7 @@ useHead(pageHead)
const ROUTE = useRoute()
const MESSAGE = useMessage()
const crName = ref('')
const crDescription = ref('')
const crFeatureImage = ref('')
const crContent = ref('')
const crScript = ref('')
const chatroom = ref<Chatroom | null>(null)
const selectedModel = ref<number | null>(null)
const quickerPrompt = ref('')
@@ -54,11 +51,7 @@ function load() {
})
.then((cr) => {
pageHead.title = cr.name
crName.value = cr.name
crDescription.value = cr.description
crFeatureImage.value = cr.feature_image
crContent.value = cr.content
crScript.value = cr.script
chatroom.value = cr
})
.catch((e) => {
MESSAGE.error(`访问聊天室失败:${e}`)
@@ -155,7 +148,7 @@ function accept() {
}
})
.then((result) => {
crContent.value = result.content
chatroom.value!.content = result.content
})
.catch((err) => {
MESSAGE.error(`保存失败:${err}`)
@@ -190,8 +183,8 @@ function messageEdit(oldMessage: string, newMessage: string, change: 'aii' | 'us
}
})
.then((result) => {
crContent.value = result.content
MESSAGE.success('聊天记录已删除,页面已更新~')
chatroom.value!.content = result.content
MESSAGE.success('聊天记录已更新,页面已更新~')
})
.catch((err) => {
MESSAGE.error(`修改聊天消息失败:${err}`)
@@ -211,7 +204,7 @@ function messageDelete(message: string, change: 'aii' | 'user') {
}
})
.then((result) => {
crContent.value = result.content
chatroom.value!.content = result.content
MESSAGE.success('聊天记录已删除,页面已更新~')
})
.catch((err) => {
@@ -243,16 +236,16 @@ function enableSidebar() {
</script>
<template>
<div class="page-container">
<div class="page-container" v-if="chatroom !== null">
<div class="main-column">
<chatroom-card
:id="Number(ROUTE.params.id)"
:name="crName"
:description="crDescription"
:feature_image="crFeatureImage"
:name="chatroom.name"
:description="chatroom.description"
:feature_image="chatroom.feature_image"
/>
<chat-table
:content="crContent"
:content="chatroom.content"
:aii-thinking
:aii-message
:aii-token-info
@@ -269,7 +262,8 @@ function enableSidebar() {
</div>
<div class="sidebar-column" ref="sidebar">
<chat-control-panel
:script="crScript"
:chatroom="chatroom"
:load-page="load"
v-model:quicker-prompt="quickerPrompt"
v-model:select-model="selectedModel"
/>
+6 -5
View File
@@ -1,12 +1,13 @@
<script setup lang="ts">
import ChatroomCard from '@/components/chatroom/ChatroomCard.vue'
import { useHead } from '@unhead/vue'
import { ref, watch } from 'vue'
import ChatroomCard from '@/components/chatroom/ChatroomCard.vue'
import ChatroomCreatorModal from '@/components/chatroom/ChatroomCreatorModal.vue'
import { useNowUser } from '@/stores/now-user.ts'
import { api } from '@/tools/web.ts'
import type { ChatroomPublic } from '@/types/chatroom.ts'
import type { ReturnDto } from '@/types/response.ts'
import ChatroomCreatorModal from '@/components/chatroom/ChatroomCreatorModal.vue'
import { useNowUser } from '@/stores/now-user.ts'
import { useHead } from '@unhead/vue'
useHead({
title: '聊天室列表',
@@ -29,7 +30,7 @@ function load() {
}
watch(
() => NOWUSER.isLogin,
() => NOWUSER.is_login,
() => {
load()
},
+2 -1
View File
@@ -1,7 +1,8 @@
<script setup lang="ts">
import InDev from '@/components/InDev.vue'
import { useHead } from '@unhead/vue'
import InDev from '@/components/InDev.vue'
useHead({
title: '剧本市场',
})
+2 -1
View File
@@ -1,7 +1,8 @@
<script setup lang="ts">
import UserAction from '@/components/admin/UserAction.vue'
import { useHead } from '@unhead/vue'
import UserAction from '@/components/admin/UserAction.vue'
useHead({
title: '首页',
})
+5 -4
View File
@@ -1,10 +1,11 @@
<script setup lang="ts">
import ConfigCard from '@/components/admin/ConfigCard.vue'
import { useHead } from '@unhead/vue'
import { ref } from 'vue'
import { api } from '@/tools/web.ts'
import InDev from '@/components/InDev.vue'
import { useMessage } from 'naive-ui'
import { ref } from 'vue'
import ConfigCard from '@/components/admin/ConfigCard.vue'
import InDev from '@/components/InDev.vue'
import { api } from '@/tools/web.ts'
import type { ReturnDto } from '@/types/response.ts'
interface SiteConfig {
+3 -2
View File
@@ -1,7 +1,8 @@
<script setup lang="ts">
import { useNowUser } from '@/stores/now-user.js'
import { computed } from 'vue'
import { useHead } from '@unhead/vue'
import { computed } from 'vue'
import { useNowUser } from '@/stores/now-user.js'
useHead({
title: '总览',
+7 -6
View File
@@ -1,12 +1,13 @@
<script setup lang="ts">
import { useNowUser } from '@/stores/now-user.js'
import { ref, watch } from 'vue'
import SelectFileModal from '@/components/file/SelectFileModal.vue'
import { api } from '@/tools/web.js'
import type { UploadFileDto, UserDto } from '@/types/user.js'
import { useHead } from '@unhead/vue'
import { ref, watch } from 'vue'
import ChangeEmailModal from '@/components/admin/ChangeEmailModal.vue'
import ChangePhoneModal from '@/components/admin/ChangePhoneModal.vue'
import SelectFileModal from '@/components/file/SelectFileModal.vue'
import { useNowUser } from '@/stores/now-user.js'
import { api } from '@/tools/web.js'
import type { UploadFileDto, UserDto } from '@/types/user.js'
useHead({
title: '用户资料',
@@ -65,7 +66,7 @@ watch(
)
watch(
() => NOWUSER.isLogin,
() => NOWUSER.is_login,
() => {
reInitForm()
},
+2 -1
View File
@@ -1,7 +1,8 @@
<script setup lang="ts">
import InDev from '@/components/InDev.vue'
import { useHead } from '@unhead/vue'
import InDev from '@/components/InDev.vue'
useHead({
title: '剧本',
})
+7 -6
View File
@@ -1,11 +1,12 @@
<script setup lang="ts">
import { useNowUser } from '@/stores/now-user.js'
import UserPasswordModal from '@/components/admin/UserPasswordModal.vue'
import { h, ref } from 'vue'
import { api } from '@/tools/web.ts'
import { type DataTableColumn, NTag, NText } from 'naive-ui'
import InDev from '@/components/InDev.vue'
import { useHead } from '@unhead/vue'
import { type DataTableColumn, NTag, NText } from 'naive-ui'
import { h, ref } from 'vue'
import UserPasswordModal from '@/components/admin/UserPasswordModal.vue'
import InDev from '@/components/InDev.vue'
import { useNowUser } from '@/stores/now-user.js'
import { api } from '@/tools/web.ts'
useHead({
title: '用户安全',
+7 -6
View File
@@ -1,11 +1,12 @@
<script setup lang="ts">
import { useHead } from '@unhead/vue'
import { ref, watch } from 'vue'
import { uploadFilesCom } from '@/components/file/upload-files.js'
import UploadFileModal from '@/components/file/UploadFileModal.vue'
import { useNowUser } from '@/stores/now-user.js'
import { api } from '@/tools/web.js'
import type { UploadFileDto } from '@/types/user.js'
import { useNowUser } from '@/stores/now-user.js'
import { uploadFilesCom } from '@/components/file/upload-files.js'
import { useHead } from '@unhead/vue'
useHead({
title: '上传',
@@ -24,7 +25,7 @@ function load() {
}
watch(
() => NOWUSER.isLogin,
() => NOWUSER.is_login,
() => {
load()
},
@@ -39,7 +40,7 @@ watch(
</template>
<n-flex vertical>
<n-alert type="info"> 接受的文件类型 </n-alert>
<n-alert type="info"> 接受的文件类型</n-alert>
<n-button @click="showUploadModal = true">打开上传向导</n-button>
<upload-file-modal
@@ -57,7 +58,7 @@ watch(
<template #header>
<n-h3 prefix="bar" style="margin: 0">个人文件库</n-h3>
</template>
<template #header-extra> 您已经上传的文件都在这里,可以选择性地删除以及重新下载。 </template>
<template #header-extra> 您已经上传的文件都在这里,可以选择性地删除以及重新下载。</template>
<component :is="uploadFilesCom(files)" />
</n-card>
+7 -6
View File
@@ -1,15 +1,16 @@
import { createRouter, createWebHashHistory } from 'vue-router'
import ChatroomPage from '@/pages/ChatroomPage.vue'
import WelcomePage from '@/pages/WelcomePage.vue'
import Chatroom1Page from '@/pages/Chatroom1Page.vue'
import AdminPage from '@/pages/AdminPage.vue'
import AdminNyahome from '@/pages/admin/AdminNyahome.vue'
import AdminOverview from '@/pages/admin/AdminOverview.vue'
import AdminUserInfo from '@/pages/admin/AdminUserInfo.vue'
import AdminUserScript from '@/pages/admin/AdminUserScript.vue'
import AdminUserSecurity from '@/pages/admin/AdminUserSecurity.vue'
import AdminUserUpload from '@/pages/admin/AdminUserUpload.vue'
import AdminNyahome from '@/pages/admin/AdminNyahome.vue'
import AdminUserScript from '@/pages/admin/AdminUserScript.vue'
import AdminPage from '@/pages/AdminPage.vue'
import Chatroom1Page from '@/pages/Chatroom1Page.vue'
import ChatroomPage from '@/pages/ChatroomPage.vue'
import Marketplace from '@/pages/Marketplace.vue'
import WelcomePage from '@/pages/WelcomePage.vue'
const router = createRouter({
history: createWebHashHistory(import.meta.env.BASE_URL),
+4 -3
View File
@@ -1,10 +1,11 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
import { api, setApiToken } from '@/tools/web.ts'
import type { UserDto } from '@/types/user.ts'
export const useNowUser = defineStore('now-user', () => {
const isLogin = ref(false)
const is_login = ref(false)
const id = ref(0)
const name = ref('')
@@ -41,11 +42,11 @@ export const useNowUser = defineStore('now-user', () => {
description.value = user.description
is_admin.value = user.is_admin
isLogin.value = true
is_login.value = true
}
return {
isLogin,
is_login,
id,
name,
display_name,
+4 -2
View File
@@ -4,8 +4,10 @@ export interface ChatroomPublic {
description: string
feature_image: string
script_template_id: number
script_template_version: string
script_template_id?: number
script_template_version?: string
default_model_id?: number
}
export interface Chatroom extends ChatroomPublic {