Auto commit: 2026-03-24 13:04

This commit is contained in:
xiongxiao
2026-03-24 13:04:47 +08:00
committed by cnb
parent c8955e684f
commit d6ba26fd39
4 changed files with 418 additions and 27 deletions

View File

@@ -30,23 +30,20 @@ pages/page-app/
└── store/ # 模块状态管理Zustand
```
### hooks/ 文件夹说明
### api 请求
每个模块的 `hooks/` 文件用于封装与该模块相关的 React Query hooks
`modules/*-api.ts` 文件用于封装 API 请求函数,使用 `@kevisual/query` 进行底层请求处理。
- **use-api-query.ts**: 使用 `@tanstack/react-query``useQuery` 封装 API 调用
- 定义 `queryKeys` 常量用于缓存标识
- 封装 `useQuery` hooks 用于数据获取GET 请求)
- 封装 `useMutation` hooks 用于数据修改POST/PUT/DELETE 请求)
- 支持预取prefetch和无限滚动infinite query
- **index.ts**: 导出模块所有 hooks便于统一导入使用
参考示例
```ts
// src/modules/mark-api.ts
import { queryApi as markApi } from '@/modules/mark-api.ts';
const res = await markApi.mark.list({ page: 1, pageSize: 10 });
```
### 状态和数据获取
- **@tanstack/react-query** 用于数据获取、缓存和状态管理
- 在模块的 `hooks/` 文件夹中封装 API 调用
- QueryClient 实例位于 `src/modules/query.ts`
-`src/routes/__root.tsx` 中通过 `QueryClientProvider` 提供
- **Zustand** 用于全局状态管理
- **@kevisual/query** 用于底层 API 请求封装
- **React Hook Form** 用于表单管理
@@ -54,7 +51,6 @@ pages/page-app/
## 核心依赖
- **@base-ui/react**: Headless UI 基础组件
- **@tanstack/react-query**: 数据获取、缓存和状态管理(配合 hooks/ 使用)
- **@tanstack/react-router**: 基于 TanStack Router 插件的文件路由
- **class-variance-authority**: 基于变体的样式系统
- **clsx + tailwind-merge**: 通过 `cn()` 提供 className 工具函数

View File

@@ -1,5 +1,5 @@
{
"name": "vite-react",
"name": "@kevisual/app",
"private": true,
"version": "0.0.1",
"type": "module",
@@ -9,6 +9,7 @@
"build": "vite build",
"preview": "vite preview",
"ui": "bunx shadcn@latest add ",
"template": "ev deploy . -k vite-react-template -v 0.0.1 -y y -u",
"pub": "envision deploy ./dist -k vite-react -v 0.0.1 -y y -u"
},
"files": [
@@ -20,9 +21,9 @@
"@base-ui/react": "^1.3.0",
"@kevisual/api": "^0.0.65",
"@kevisual/context": "^0.0.8",
"@kevisual/router": "0.1.6",
"@tanstack/react-query": "^5.91.3",
"@tanstack/react-router": "^1.168.1",
"@kevisual/router": "0.2.2",
"@tanstack/react-query": "^5.95.2",
"@tanstack/react-router": "^1.168.3",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
@@ -30,12 +31,12 @@
"dayjs": "^1.11.20",
"es-toolkit": "^1.45.1",
"fuse.js": "^7.1.0",
"lucide-react": "^0.577.0",
"lucide-react": "^1.0.1",
"nanoid": "^5.1.7",
"next-themes": "^0.4.6",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-hook-form": "^7.71.2",
"react-hook-form": "^7.72.0",
"sonner": "^2.0.7",
"zustand": "^5.0.12"
},
@@ -49,18 +50,19 @@
"@kevisual/types": "^0.0.12",
"@kevisual/vite-html-plugin": "^0.0.1",
"@tailwindcss/vite": "^4.2.2",
"@tanstack/react-router-devtools": "^1.166.10",
"@tanstack/router-plugin": "^1.167.2",
"@tanstack/react-router-devtools": "^1.166.11",
"@tanstack/router-plugin": "^1.167.4",
"@types/node": "^25.5.0",
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^6.0.1",
"dotenv": "^17.3.1",
"re-resizable": "^6.11.2",
"tailwind-merge": "^3.5.0",
"tailwindcss": "^4.2.2",
"tw-animate-css": "^1.4.0",
"typescript": "^5.9.3",
"vite": "v8.0.1",
"typescript": "^6.0.2",
"vite": "v8.0.2",
"vite-plugin-pwa": "^1.2.0"
}
}

View File

@@ -32,6 +32,7 @@ export interface SidebarProps {
children?: React.ReactNode
logo?: React.ReactNode
title?: React.ReactNode
footer?: React.ReactNode
defaultCollapsed?: boolean
defaultWidth?: number
minWidth?: number
@@ -44,6 +45,7 @@ export function Sidebar({
children,
logo,
title,
footer,
defaultCollapsed = false,
defaultWidth = 208,
minWidth = 120,
@@ -80,7 +82,7 @@ export function Sidebar({
if (item.isDeveloping) {
setDevelopingDialog({ open: true, title: item.title })
} else if (item.external && item.path.startsWith('http')) {
} else if (item.external) {
window.open(item.path, '_blank')
} else if (item.path.startsWith('/')) {
navigate({ to: item.path })
@@ -172,7 +174,6 @@ export function Sidebar({
</li>
)
}
return (
<>
<div className={cn('flex h-full', className)}>
@@ -194,7 +195,7 @@ export function Sidebar({
>
<aside
className={cn(
'border-r bg-white flex-shrink-0 flex flex-col'
'h-full border-r bg-white flex-shrink-0 flex flex-col'
)}
style={{ width: sidebarWidth }}
>
@@ -223,11 +224,18 @@ export function Sidebar({
{items.map((item) => renderNavItem(item))}
</ul>
</nav>
{/* Footer */}
{footer && (
<div className="border-t flex-shrink-0">
{footer}
</div>
)}
</aside>
</Resizable>
) : (
// 收起状态
<aside className="w-14 border-r bg-white flex-shrink-0 flex flex-col">
<aside className="h-full w-14 border-r bg-white flex-shrink-0 flex flex-col">
{/* Logo 区域 */}
<div className="h-12 flex items-center justify-center border-b px-2">
<div className="group relative flex-shrink-0">
@@ -248,6 +256,13 @@ export function Sidebar({
{items.map((item) => renderNavItem(item))}
</ul>
</nav>
{/* Footer */}
{footer && (
<div className="border-t flex-shrink-0">
{footer}
</div>
)}
</aside>
)}

378
src/modules/mark-api.ts Normal file
View File

@@ -0,0 +1,378 @@
import { createQueryApi } from '@kevisual/query/api';
const api = {
"mark": {
/**
* 获取mark列表
*
* @param data - Request parameters
* @param data.page - {number} 页码
* @param data.pageSize - {number} 每页数量
* @param data.search - {string} 搜索关键词
* @param data.markType - {string} mark类型,simple,wallnote,md,draw等
* @param data.sort - {"DESC" | "ASC"} 排序字段
*/
"list": {
"path": "mark",
"key": "list",
"description": "获取mark列表",
"metadata": {
"args": {
"page": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"description": "页码",
"type": "number",
"optional": true
},
"pageSize": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"description": "每页数量",
"type": "number",
"optional": true
},
"search": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"description": "搜索关键词",
"type": "string",
"optional": true
},
"markType": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"description": "mark类型,simple,wallnote,md,draw等",
"type": "string",
"optional": true
},
"sort": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"default": "DESC",
"description": "排序字段",
"type": "string",
"enum": [
"DESC",
"ASC"
],
"optional": true
}
},
"url": "/api/router",
"source": "query-proxy-api"
}
},
/**
* 获取mark版本信息
*
* @param data - Request parameters
* @param data.id - {string} mark id
*/
"getVersion": {
"path": "mark",
"key": "getVersion",
"description": "获取mark版本信息",
"metadata": {
"args": {
"id": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string",
"description": "mark id"
}
},
"url": "/api/router",
"source": "query-proxy-api"
}
},
/**
* 获取mark详情
*
* @param data - Request parameters
* @param data.id - {string} mark id
*/
"get": {
"path": "mark",
"key": "get",
"description": "获取mark详情",
"metadata": {
"args": {
"id": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string",
"description": "mark id"
}
},
"url": "/api/router",
"source": "query-proxy-api"
}
},
/**
* 更新mark内容
*
* @param data - Request parameters
* @param data.data - {object}
*/
"update": {
"path": "mark",
"key": "update",
"description": "更新mark内容",
"metadata": {
"args": {
"data": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "mark id"
},
"title": {
"default": "",
"type": "string"
},
"tags": {
"default": []
},
"link": {
"default": "",
"type": "string"
},
"summary": {
"default": "",
"type": "string"
},
"description": {
"default": "",
"type": "string"
}
},
"required": [
"id",
"title",
"tags",
"link",
"summary",
"description"
],
"additionalProperties": false
}
},
"url": "/api/router",
"source": "query-proxy-api"
}
},
/**
* 更新mark节点支持更新和删除操作
*
* @param data - Request parameters
* @param data.id - {string} mark id
* @param data.operate - {"update" | "delete"} 节点操作类型update或delete
* @param data.data - {object} 要更新的节点数据
*/
"updateNode": {
"path": "mark",
"key": "updateNode",
"description": "更新mark节点支持更新和删除操作",
"metadata": {
"args": {
"id": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string",
"description": "mark id"
},
"operate": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"default": "update",
"description": "节点操作类型update或delete",
"type": "string",
"enum": [
"update",
"delete"
],
"optional": true
},
"data": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "节点id"
},
"node": {
"description": "要更新的节点数据"
}
},
"required": [
"id",
"node"
],
"additionalProperties": false,
"description": "要更新的节点数据"
}
},
"url": "/api/router",
"source": "query-proxy-api"
}
},
/**
* 批量更新mark节点支持更新和删除操作
*
* @param data - Request parameters
* @param data.id - {string} mark id
* @param data.nodeOperateList - {array} 要更新的节点列表
*/
"updateNodes": {
"path": "mark",
"key": "updateNodes",
"description": "批量更新mark节点支持更新和删除操作",
"metadata": {
"args": {
"id": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string",
"description": "mark id"
},
"nodeOperateList": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "array",
"items": {
"type": "object",
"properties": {
"operate": {
"default": "update",
"description": "节点操作类型update或delete",
"type": "string",
"enum": [
"update",
"delete"
]
},
"node": {
"description": "要更新的节点数据"
}
},
"required": [
"operate",
"node"
],
"additionalProperties": false
},
"description": "要更新的节点列表"
}
},
"url": "/api/router",
"source": "query-proxy-api"
}
},
/**
* @param data - Request parameters
* @param data.id - {string} mark id
*/
"delete": {
"path": "mark",
"key": "delete",
"metadata": {
"args": {
"id": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "string",
"description": "mark id"
}
},
"url": "/api/router",
"source": "query-proxy-api"
}
},
/**
* 创建一个新的mark.
*
* @param data - Request parameters
* @param data.title - {string} 标题
* @param data.tags - {unknown} 标签
* @param data.link - {string} 链接
* @param data.summary - {string} 摘要
* @param data.description - {string} 描述
* @param data.markType - {string} mark类型
* @param data.config - {unknown} 配置
* @param data.data - {unknown} 数据
*/
"create": {
"path": "mark",
"key": "create",
"description": "创建一个新的mark.",
"metadata": {
"args": {
"title": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"default": "",
"description": "标题",
"type": "string",
"optional": true
},
"tags": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"default": [],
"description": "标签",
"optional": true
},
"link": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"default": "",
"description": "链接",
"type": "string",
"optional": true
},
"summary": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"default": "",
"description": "摘要",
"type": "string",
"optional": true
},
"description": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"default": "",
"description": "描述",
"type": "string",
"optional": true
},
"markType": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"default": "md",
"description": "mark类型",
"type": "string",
"optional": true
},
"config": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"default": {},
"description": "配置",
"optional": true
},
"data": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"default": {},
"description": "数据",
"optional": true
}
},
"url": "/api/router",
"source": "query-proxy-api"
}
},
/**
* 获取mark菜单
*/
"getMenu": {
"path": "mark",
"key": "getMenu",
"description": "获取mark菜单",
"metadata": {
"url": "/api/router",
"source": "query-proxy-api"
}
}
}
} as const;
const queryApi = createQueryApi({ api });
export { queryApi };
// 使用例子,方法为对应的方法data 为对应的 args 的参数的定义数据
// queryApi['user-app'].delete(data)