style: 一些复杂而综合的细节修正
This commit is contained in:
@@ -38,17 +38,21 @@ import inspect
|
|||||||
import logging
|
import logging
|
||||||
from typing import Any, Dict, Optional, Set
|
from typing import Any, Dict, Optional, Set
|
||||||
|
|
||||||
from docstring_parser import DocstringStyle, ParseError, parse
|
from docstring_parser import Docstring, DocstringStyle, ParseError, parse
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from fastapi.openapi.utils import get_openapi
|
from fastapi.openapi.utils import get_openapi
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def _parse_docstring(
|
def _parse_docstring(func: Any, style: DocstringStyle = DocstringStyle.AUTO) -> Optional[Docstring]: # noqa ANN401 有意为之
|
||||||
func: Any, style: DocstringStyle = DocstringStyle.AUTO
|
"""
|
||||||
) -> Optional[Any]:
|
解析函数的 Google Style docstring,失败时返回 None。
|
||||||
"""解析函数的 Google Style docstring,失败时返回 None。"""
|
|
||||||
|
Returns:
|
||||||
|
成功解析时返回 docstring_parser.Docstring。
|
||||||
|
失败时返回 None。
|
||||||
|
"""
|
||||||
doc = inspect.getdoc(func)
|
doc = inspect.getdoc(func)
|
||||||
if not doc:
|
if not doc:
|
||||||
return None
|
return None
|
||||||
@@ -67,8 +71,13 @@ def _parse_docstring(
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _build_param_lookup(parsed_doc: Any) -> Dict[str, str]:
|
def _build_param_lookup(parsed_doc: Docstring) -> Dict[str, str]:
|
||||||
"""从解析后的 docstring 构建 {参数名: 描述} 映射。"""
|
"""
|
||||||
|
从解析后的 docstring 构建 {参数名: 描述} 映射。
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
{参数名: 描述}
|
||||||
|
"""
|
||||||
lookup: Dict[str, str] = {}
|
lookup: Dict[str, str] = {}
|
||||||
for param in getattr(parsed_doc, "params", []) or []:
|
for param in getattr(parsed_doc, "params", []) or []:
|
||||||
name = getattr(param, "arg_name", None)
|
name = getattr(param, "arg_name", None)
|
||||||
@@ -79,8 +88,13 @@ def _build_param_lookup(parsed_doc: Any) -> Dict[str, str]:
|
|||||||
return lookup
|
return lookup
|
||||||
|
|
||||||
|
|
||||||
def _build_returns_description(parsed_doc: Any) -> Optional[str]:
|
def _build_returns_description(parsed_doc: Docstring) -> Optional[str]:
|
||||||
"""从解析后的 docstring 构建返回值描述。"""
|
"""
|
||||||
|
从解析后的 docstring 构建返回值描述。
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
可能的字符串为返回值描述。
|
||||||
|
"""
|
||||||
ret = getattr(parsed_doc, "returns", None)
|
ret = getattr(parsed_doc, "returns", None)
|
||||||
if not ret:
|
if not ret:
|
||||||
return None
|
return None
|
||||||
@@ -91,8 +105,13 @@ def _build_returns_description(parsed_doc: Any) -> Optional[str]:
|
|||||||
return desc or None
|
return desc or None
|
||||||
|
|
||||||
|
|
||||||
def _build_raises_description(parsed_doc: Any) -> Optional[str]:
|
def _build_raises_description(parsed_doc: Docstring) -> Optional[str]:
|
||||||
"""从解析后的 docstring 构建异常描述(Markdown 列表)。"""
|
"""
|
||||||
|
从解析后的 docstring 构建异常描述(Markdown 列表)。
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
可能的字符串为异常描述。
|
||||||
|
"""
|
||||||
raises = getattr(parsed_doc, "raises", None)
|
raises = getattr(parsed_doc, "raises", None)
|
||||||
if not raises:
|
if not raises:
|
||||||
return None
|
return None
|
||||||
@@ -110,7 +129,12 @@ def _build_raises_description(parsed_doc: Any) -> Optional[str]:
|
|||||||
|
|
||||||
|
|
||||||
def _dedent_description(text: str) -> str:
|
def _dedent_description(text: str) -> str:
|
||||||
"""清理 docstring 描述中的多余缩进,保留段落结构。"""
|
"""
|
||||||
|
清理 docstring 描述中的多余缩进,保留段落结构。
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
docstring 字符串
|
||||||
|
"""
|
||||||
if not text:
|
if not text:
|
||||||
return ""
|
return ""
|
||||||
lines = text.splitlines()
|
lines = text.splitlines()
|
||||||
@@ -123,11 +147,12 @@ def _dedent_description(text: str) -> str:
|
|||||||
return "\n".join(cleaned).strip()
|
return "\n".join(cleaned).strip()
|
||||||
|
|
||||||
|
|
||||||
def _get_clean_operation_description(parsed_doc: Any) -> str:
|
def _get_clean_operation_description(parsed_doc: Docstring) -> str:
|
||||||
"""
|
"""
|
||||||
生成干净的 operation description。
|
生成干净的 operation description。
|
||||||
|
|
||||||
策略:保留 summary + long_description,去掉 Args/Returns/Raises 等机器块。
|
Returns:
|
||||||
|
策略:保留 summary + long_description,去掉 Args/Returns/Raises 等机器块。
|
||||||
"""
|
"""
|
||||||
parts: list[str] = []
|
parts: list[str] = []
|
||||||
short = getattr(parsed_doc, "short_description", None)
|
short = getattr(parsed_doc, "short_description", None)
|
||||||
@@ -142,10 +167,10 @@ def _get_clean_operation_description(parsed_doc: Any) -> str:
|
|||||||
|
|
||||||
|
|
||||||
def _inject_schema_properties(
|
def _inject_schema_properties(
|
||||||
schema: Dict[str, Any],
|
schema: Dict[str, Any],
|
||||||
param_lookup: Dict[str, str],
|
param_lookup: Dict[str, str],
|
||||||
visited_refs: Optional[Set[str]] = None,
|
visited_refs: Optional[Set[str]] = None,
|
||||||
openapi_schema: Optional[Dict[str, Any]] = None,
|
openapi_schema: Optional[Dict[str, Any]] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
递归注入 schema properties 的描述。
|
递归注入 schema properties 的描述。
|
||||||
@@ -174,7 +199,7 @@ def _inject_schema_properties(
|
|||||||
properties = schema.get("properties")
|
properties = schema.get("properties")
|
||||||
if isinstance(properties, dict):
|
if isinstance(properties, dict):
|
||||||
for prop_name, prop_schema in properties.items():
|
for prop_name, prop_schema in properties.items():
|
||||||
if prop_name in param_lookup:
|
if prop_name in param_lookup: # noqa SIM102
|
||||||
# 只有当 docstring 描述非空时才写入
|
# 只有当 docstring 描述非空时才写入
|
||||||
if param_lookup[prop_name]:
|
if param_lookup[prop_name]:
|
||||||
prop_schema["description"] = param_lookup[prop_name]
|
prop_schema["description"] = param_lookup[prop_name]
|
||||||
@@ -198,11 +223,11 @@ def _inject_schema_properties(
|
|||||||
|
|
||||||
|
|
||||||
def enrich_openapi_from_docstrings(
|
def enrich_openapi_from_docstrings(
|
||||||
app: FastAPI,
|
app: FastAPI,
|
||||||
*,
|
*,
|
||||||
prefer_docstring_over_field: bool = True,
|
prefer_docstring_over_field: bool = True,
|
||||||
append_raises_to_description: bool = True,
|
append_raises_to_description: bool = True,
|
||||||
docstring_style: DocstringStyle = DocstringStyle.AUTO,
|
docstring_style: DocstringStyle = DocstringStyle.AUTO,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
为 FastAPI 应用启用 docstring 驱动的 OpenAPI 增强。
|
为 FastAPI 应用启用 docstring 驱动的 OpenAPI 增强。
|
||||||
@@ -220,8 +245,8 @@ def enrich_openapi_from_docstrings(
|
|||||||
解析风格。团队统一用 Google 时可显式传入 ``DocstringStyle.GOOGLE``。
|
解析风格。团队统一用 Google 时可显式传入 ``DocstringStyle.GOOGLE``。
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# 保存原始的 openapi 函数(如果有)
|
# 可选择保存原始的 openapi 函数(如果有)
|
||||||
original_openapi = app.openapi
|
# original_openapi = app.openapi
|
||||||
|
|
||||||
def custom_openapi() -> Dict[str, Any]:
|
def custom_openapi() -> Dict[str, Any]:
|
||||||
# 如果已经有缓存,直接返回
|
# 如果已经有缓存,直接返回
|
||||||
@@ -284,9 +309,7 @@ def enrich_openapi_from_docstrings(
|
|||||||
for media_obj in content.values():
|
for media_obj in content.values():
|
||||||
schema = media_obj.get("schema", {})
|
schema = media_obj.get("schema", {})
|
||||||
if schema:
|
if schema:
|
||||||
_inject_schema_properties(
|
_inject_schema_properties(schema, param_lookup, openapi_schema=openapi_schema)
|
||||||
schema, param_lookup, openapi_schema=openapi_schema
|
|
||||||
)
|
|
||||||
|
|
||||||
# ---------- 4. 注入返回值描述 ----------
|
# ---------- 4. 注入返回值描述 ----------
|
||||||
if returns_desc:
|
if returns_desc:
|
||||||
@@ -300,9 +323,7 @@ def enrich_openapi_from_docstrings(
|
|||||||
if schema and not schema.get("description"):
|
if schema and not schema.get("description"):
|
||||||
schema["description"] = returns_desc
|
schema["description"] = returns_desc
|
||||||
# 递归注入 schema 内部字段
|
# 递归注入 schema 内部字段
|
||||||
_inject_schema_properties(
|
_inject_schema_properties(schema, param_lookup, openapi_schema=openapi_schema)
|
||||||
schema, param_lookup, openapi_schema=openapi_schema
|
|
||||||
)
|
|
||||||
|
|
||||||
# ---------- 5. 异常描述追加 ----------
|
# ---------- 5. 异常描述追加 ----------
|
||||||
if append_raises_to_description and raises_desc:
|
if append_raises_to_description and raises_desc:
|
||||||
@@ -315,61 +336,4 @@ def enrich_openapi_from_docstrings(
|
|||||||
app.openapi_schema = openapi_schema
|
app.openapi_schema = openapi_schema
|
||||||
return app.openapi_schema
|
return app.openapi_schema
|
||||||
|
|
||||||
app.openapi = custom_openapi
|
app.openapi = custom_openapi # type: ignore[method-assign]
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================
|
|
||||||
# 完整使用示例
|
|
||||||
# =============================================================================
|
|
||||||
if __name__ == "__main__":
|
|
||||||
import json
|
|
||||||
from typing import Annotated, Optional
|
|
||||||
|
|
||||||
from fastapi import FastAPI, Path, Query
|
|
||||||
from pydantic import BaseModel, Field
|
|
||||||
|
|
||||||
# ---------- 定义 DTO ----------
|
|
||||||
class EditChatDto(BaseModel):
|
|
||||||
old_message: str = Field(..., description="原始消息内容")
|
|
||||||
new_message: str = Field(..., description="修改后的消息内容")
|
|
||||||
change: str = Field(..., description="变更类型")
|
|
||||||
|
|
||||||
class ReturnDto(BaseModel):
|
|
||||||
result: str = Field(..., description="操作结果")
|
|
||||||
content: str = Field(..., description="最新聊天记录内容")
|
|
||||||
|
|
||||||
# ---------- 创建应用 ----------
|
|
||||||
app = FastAPI(title="Chatroom API", version="1.0.0")
|
|
||||||
|
|
||||||
# 启用 docstring 增强(必须在注册路由之前或之后都可以,但要在生成 schema 之前)
|
|
||||||
enrich_openapi_from_docstrings(app)
|
|
||||||
|
|
||||||
# ---------- 注册路由 ----------
|
|
||||||
@app.post("/api/chatroom/{id_}/chat/edit/", response_model=ReturnDto)
|
|
||||||
async def edit_chatroom_chat(
|
|
||||||
id_: Annotated[str, Path(description="聊天室 ID")],
|
|
||||||
body: EditChatDto,
|
|
||||||
force: Annotated[Optional[bool], Query(description="是否强制覆盖")] = None,
|
|
||||||
) -> ReturnDto:
|
|
||||||
"""编辑聊天室消息。
|
|
||||||
|
|
||||||
此端点不负责调用 AI 生成输出,而是用于修改一条已经保存在聊天记录中的消息。
|
|
||||||
前端调用后应使用返回的 content 刷新当前聊天室界面。
|
|
||||||
|
|
||||||
Args:
|
|
||||||
id_: 聊天室唯一标识符,UUID 格式。
|
|
||||||
body: 编辑请求体,包含旧消息、新消息和变更类型。
|
|
||||||
force: 是否跳过冲突检测直接覆盖。
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
ReturnDto: 操作结果,其中 result 字段表示状态,content 字段为最新聊天记录。
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
HTTPException: 404 表明未找到聊天室。
|
|
||||||
HTTPException: 400 表明聊天记录匹配失败,未更新。
|
|
||||||
"""
|
|
||||||
return ReturnDto(result="ok", content="new content")
|
|
||||||
|
|
||||||
# ---------- 输出生成的 OpenAPI schema ----------
|
|
||||||
schema = app.openapi()
|
|
||||||
print(json.dumps(schema, indent=2, ensure_ascii=False))
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ from .response_model import ReturnDto
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
admin_router = APIRouter(tags=["admin"], prefix="/admin")
|
admin_router = APIRouter(tags=["Admin"], prefix="/admin")
|
||||||
|
|
||||||
|
|
||||||
class UserLogin(BaseModel):
|
class UserLogin(BaseModel):
|
||||||
|
|||||||
Vendored
+2
@@ -15,6 +15,7 @@ declare module 'vue' {
|
|||||||
AiiModelAddModal: typeof import('./src/components/chatroom/AiiModelAddModal.vue')['default']
|
AiiModelAddModal: typeof import('./src/components/chatroom/AiiModelAddModal.vue')['default']
|
||||||
AiiProviderAddModal: typeof import('./src/components/chatroom/AiiProviderAddModal.vue')['default']
|
AiiProviderAddModal: typeof import('./src/components/chatroom/AiiProviderAddModal.vue')['default']
|
||||||
ChangeEmailModal: typeof import('./src/components/admin/ChangeEmailModal.vue')['default']
|
ChangeEmailModal: typeof import('./src/components/admin/ChangeEmailModal.vue')['default']
|
||||||
|
ChangePhoneModal: typeof import('./src/components/admin/ChangePhoneModal.vue')['default']
|
||||||
ChatControlPanel: typeof import('./src/components/chatroom/ChatControlPanel.vue')['default']
|
ChatControlPanel: typeof import('./src/components/chatroom/ChatControlPanel.vue')['default']
|
||||||
ChatMessage: typeof import('./src/components/chatroom/ChatMessage.vue')['default']
|
ChatMessage: typeof import('./src/components/chatroom/ChatMessage.vue')['default']
|
||||||
ChatPromptQuicker: typeof import('./src/components/chatroom/ChatPromptQuicker.vue')['default']
|
ChatPromptQuicker: typeof import('./src/components/chatroom/ChatPromptQuicker.vue')['default']
|
||||||
@@ -84,6 +85,7 @@ declare global {
|
|||||||
const AiiModelAddModal: typeof import('./src/components/chatroom/AiiModelAddModal.vue')['default']
|
const AiiModelAddModal: typeof import('./src/components/chatroom/AiiModelAddModal.vue')['default']
|
||||||
const AiiProviderAddModal: typeof import('./src/components/chatroom/AiiProviderAddModal.vue')['default']
|
const AiiProviderAddModal: typeof import('./src/components/chatroom/AiiProviderAddModal.vue')['default']
|
||||||
const ChangeEmailModal: typeof import('./src/components/admin/ChangeEmailModal.vue')['default']
|
const ChangeEmailModal: typeof import('./src/components/admin/ChangeEmailModal.vue')['default']
|
||||||
|
const ChangePhoneModal: typeof import('./src/components/admin/ChangePhoneModal.vue')['default']
|
||||||
const ChatControlPanel: typeof import('./src/components/chatroom/ChatControlPanel.vue')['default']
|
const ChatControlPanel: typeof import('./src/components/chatroom/ChatControlPanel.vue')['default']
|
||||||
const ChatMessage: typeof import('./src/components/chatroom/ChatMessage.vue')['default']
|
const ChatMessage: typeof import('./src/components/chatroom/ChatMessage.vue')['default']
|
||||||
const ChatPromptQuicker: typeof import('./src/components/chatroom/ChatPromptQuicker.vue')['default']
|
const ChatPromptQuicker: typeof import('./src/components/chatroom/ChatPromptQuicker.vue')['default']
|
||||||
|
|||||||
@@ -6,6 +6,10 @@ body {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
div#app {
|
div#app {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
<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',
|
||||||
@@ -62,7 +62,9 @@ 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.endsWith('/admin')) {
|
||||||
|
selectOption.value = ''
|
||||||
|
} else if (key) {
|
||||||
selectOption.value = key
|
selectOption.value = key
|
||||||
menu.value?.showOption(key)
|
menu.value?.showOption(key)
|
||||||
} else {
|
} else {
|
||||||
@@ -74,16 +76,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,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 { ref, watch } from 'vue'
|
import {ref, watch} from 'vue'
|
||||||
import SelectFileModal from '@/components/file/SelectFileModal.vue'
|
import SelectFileModal from '@/components/file/SelectFileModal.vue'
|
||||||
import { api } from '@/tools/web.js'
|
import {api} from '@/tools/web.js'
|
||||||
import type { UploadFileDto, UserDto } from '@/types/user.js'
|
import type {UploadFileDto, UserDto} from '@/types/user.js'
|
||||||
import { useHead } from '@unhead/vue'
|
import {useHead} from '@unhead/vue'
|
||||||
import ChangeEmailModal from '@/components/admin/ChangeEmailModal.vue'
|
import ChangeEmailModal from '@/components/admin/ChangeEmailModal.vue'
|
||||||
import ChangePhoneModal from '@/components/admin/ChangePhoneModal.vue'
|
import ChangePhoneModal from '@/components/admin/ChangePhoneModal.vue'
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@ watch(
|
|||||||
() => {
|
() => {
|
||||||
reInitForm()
|
reInitForm()
|
||||||
},
|
},
|
||||||
{ immediate: true },
|
{immediate: true},
|
||||||
)
|
)
|
||||||
|
|
||||||
async function save() {
|
async function save() {
|
||||||
@@ -91,14 +91,14 @@ async function save() {
|
|||||||
<div class="ui-content">
|
<div class="ui-content">
|
||||||
<n-form style="width: 450px" label-width="auto" label-placement="left" label-align="right">
|
<n-form style="width: 450px" label-width="auto" label-placement="left" label-align="right">
|
||||||
<n-form-item label="用户名">
|
<n-form-item label="用户名">
|
||||||
<n-input v-model:value="infoForm.name" />
|
<n-input v-model:value="infoForm.name"/>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item label="展示名称">
|
<n-form-item label="展示名称">
|
||||||
<n-input v-model:value="infoForm.display_name" />
|
<n-input v-model:value="infoForm.display_name"/>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item label="头像">
|
<n-form-item label="头像">
|
||||||
<n-flex>
|
<n-flex>
|
||||||
<n-avatar v-model:src="infoForm.avatar_url" :size="96" circle />
|
<n-avatar v-model:src="infoForm.avatar_url" :size="96" circle/>
|
||||||
<n-flex vertical>
|
<n-flex vertical>
|
||||||
<n-tag type="info">需在「内容-上传」中提前上传图像。</n-tag>
|
<n-tag type="info">需在「内容-上传」中提前上传图像。</n-tag>
|
||||||
<n-tag type="warning">使用方形图像以获得最佳效果。</n-tag>
|
<n-tag type="warning">使用方形图像以获得最佳效果。</n-tag>
|
||||||
@@ -108,7 +108,7 @@ async function save() {
|
|||||||
secondary
|
secondary
|
||||||
type="tertiary"
|
type="tertiary"
|
||||||
@click="infoForm.avatar_url = NOWUSER.avatar_url"
|
@click="infoForm.avatar_url = NOWUSER.avatar_url"
|
||||||
>重置
|
>重置
|
||||||
</n-button>
|
</n-button>
|
||||||
</n-flex>
|
</n-flex>
|
||||||
</n-flex>
|
</n-flex>
|
||||||
@@ -116,7 +116,7 @@ async function save() {
|
|||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item label="个人背景">
|
<n-form-item label="个人背景">
|
||||||
<n-flex>
|
<n-flex>
|
||||||
<n-avatar v-model:src="infoForm.background_url" :size="96" object-fit="cover" />
|
<n-avatar v-model:src="infoForm.background_url" :size="96" object-fit="cover"/>
|
||||||
<n-flex vertical>
|
<n-flex vertical>
|
||||||
<n-tag type="info">需在「内容-上传」中提前上传图像。</n-tag>
|
<n-tag type="info">需在「内容-上传」中提前上传图像。</n-tag>
|
||||||
<n-flex>
|
<n-flex>
|
||||||
@@ -125,7 +125,7 @@ async function save() {
|
|||||||
secondary
|
secondary
|
||||||
type="tertiary"
|
type="tertiary"
|
||||||
@click="infoForm.background_url = NOWUSER.background_url"
|
@click="infoForm.background_url = NOWUSER.background_url"
|
||||||
>重置
|
>重置
|
||||||
</n-button>
|
</n-button>
|
||||||
</n-flex>
|
</n-flex>
|
||||||
</n-flex>
|
</n-flex>
|
||||||
@@ -139,10 +139,10 @@ async function save() {
|
|||||||
/>
|
/>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item label="邮箱">
|
<n-form-item label="邮箱">
|
||||||
<n-input v-model:value="NOWUSER.email" disabled />
|
<n-input v-model:value="NOWUSER.email" disabled/>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
<n-form-item label="手机号">
|
<n-form-item label="手机号">
|
||||||
<n-input v-model:value="NOWUSER.phone" disabled />
|
<n-input v-model:value="NOWUSER.phone" disabled/>
|
||||||
</n-form-item>
|
</n-form-item>
|
||||||
</n-form>
|
</n-form>
|
||||||
<n-flex>
|
<n-flex>
|
||||||
|
|||||||
@@ -13,13 +13,10 @@
|
|||||||
// Bundler mode provides a smoother developer experience.
|
// Bundler mode provides a smoother developer experience.
|
||||||
"module": "preserve",
|
"module": "preserve",
|
||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler",
|
||||||
|
|
||||||
// Include Node.js types and avoid accidentally including other `@types/*` packages.
|
// Include Node.js types and avoid accidentally including other `@types/*` packages.
|
||||||
"types": ["node"],
|
"types": ["node"],
|
||||||
|
|
||||||
// Disable emitting output during `vue-tsc --build`, which is used for type-checking only.
|
// Disable emitting output during `vue-tsc --build`, which is used for type-checking only.
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
|
|
||||||
// `vue-tsc --build` produces a .tsbuildinfo file for incremental type-checking.
|
// `vue-tsc --build` produces a .tsbuildinfo file for incremental type-checking.
|
||||||
// Specified here to keep it out of the root directory.
|
// Specified here to keep it out of the root directory.
|
||||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo"
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo"
|
||||||
|
|||||||
+11
-16
@@ -4,15 +4,15 @@ import {defineConfig} from 'vite'
|
|||||||
import vue from '@vitejs/plugin-vue'
|
import vue from '@vitejs/plugin-vue'
|
||||||
import vueJsx from '@vitejs/plugin-vue-jsx'
|
import vueJsx from '@vitejs/plugin-vue-jsx'
|
||||||
import vueDevTools from 'vite-plugin-vue-devtools'
|
import vueDevTools from 'vite-plugin-vue-devtools'
|
||||||
import {readFileSync} from "node:fs";
|
import {readFileSync} from 'node:fs'
|
||||||
import {resolve} from "path";
|
import {resolve} from 'path'
|
||||||
import AutoImport from 'unplugin-auto-import/vite'
|
import AutoImport from 'unplugin-auto-import/vite'
|
||||||
import {NaiveUiResolver} from 'unplugin-vue-components/resolvers'
|
import {NaiveUiResolver} from 'unplugin-vue-components/resolvers'
|
||||||
import Components from 'unplugin-vue-components/vite'
|
import Components from 'unplugin-vue-components/vite'
|
||||||
import {unheadVueComposablesImports} from "@unhead/vue";
|
import {unheadVueComposablesImports} from '@unhead/vue'
|
||||||
|
|
||||||
// 从 package.json 里搞到 WebUI 版本号
|
// 从 package.json 里搞到 WebUI 版本号
|
||||||
const pkg = JSON.parse(readFileSync(resolve(__dirname, 'package.json'), 'utf-8'));
|
const pkg = JSON.parse(readFileSync(resolve(__dirname, 'package.json'), 'utf-8'))
|
||||||
|
|
||||||
// https://vite.dev/config/
|
// https://vite.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
@@ -24,22 +24,17 @@ export default defineConfig({
|
|||||||
imports: [
|
imports: [
|
||||||
'vue',
|
'vue',
|
||||||
{
|
{
|
||||||
'naive-ui': [
|
'naive-ui': ['useDialog', 'useMessage', 'useNotification', 'useLoadingBar'],
|
||||||
'useDialog',
|
|
||||||
'useMessage',
|
|
||||||
'useNotification',
|
|
||||||
'useLoadingBar'
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
unheadVueComposablesImports,
|
unheadVueComposablesImports,
|
||||||
]
|
],
|
||||||
}),
|
}),
|
||||||
Components({
|
Components({
|
||||||
resolvers: [NaiveUiResolver()]
|
resolvers: [NaiveUiResolver()],
|
||||||
})
|
}),
|
||||||
],
|
],
|
||||||
define: {
|
define: {
|
||||||
__VERSION__: JSON.stringify(pkg.version)
|
__VERSION__: JSON.stringify(pkg.version),
|
||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
rolldownOptions: {
|
rolldownOptions: {
|
||||||
@@ -47,11 +42,11 @@ export default defineConfig({
|
|||||||
index: resolve(__dirname, 'index.html'),
|
index: resolve(__dirname, 'index.html'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
outDir: "../src/nyahome/static"
|
outDir: '../src/nyahome/static',
|
||||||
},
|
},
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
'@': fileURLToPath(new URL('./src', import.meta.url)),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
|
|||||||
Reference in New Issue
Block a user