diff --git a/.kilo/README.md b/.kilo/README.md new file mode 100644 index 0000000..abb9d91 --- /dev/null +++ b/.kilo/README.md @@ -0,0 +1,133 @@ +# Kilo Configuration for Vue Admin Project + +This directory contains the Kilo AI assistant configuration for this Vue 3 + Arco Design admin project. + +## Directory Structure + +``` +.kilo/ +├── agent/ # Agent mode rules +│ ├── rules-code.md # Code writing rules +│ ├── rules-architect.md # Architecture rules +│ ├── rules-debug.md # Debugging rules +│ └── rules-ask.md # Q&A rules +├── command/ # Slash commands +│ ├── new-page.md # Create new page module +│ ├── new-api.md # Create new API module +│ ├── new-component.md # Create new component +│ ├── lint-fix.md # Fix lint issues +│ └── type-check.md # Check TypeScript types +├── skill/ # Skill definitions +│ └── vue-admin-dev.md # Vue admin development skill +├── templates/ # Code templates +│ ├── api-module.ts # API module template +│ ├── columns.ts # Table columns template +│ ├── search-form.ts # Search form template +│ ├── index.vue # Main page template +│ ├── FormDialog.vue # Form dialog template +│ └── Detail.vue # Detail view template +├── logs/ # Log files +└── node_modules/ # Dependencies +``` + +## Configuration Files + +### kilo.json + +Main configuration file with: + +- Agent settings (default mode, allowed modes) +- Command directory path +- Rules directory path +- Skills directory path +- Hooks (beforeFileWrite, afterTaskComplete) +- Context settings (maxTokens, include/exclude patterns) +- Logging configuration + +### AGENTS.md + +Project-level guidance file in root directory with: + +- Build commands +- Architecture notes +- Module structure patterns +- Code style requirements +- Import order +- Debug tips + +## Usage + +### Commands + +``` +/new-page # Create new page module +/new-api [base-path] # Create new API module +/new-component [type] # Create new component +/lint-fix [path] # Fix lint issues +/type-check # Check TypeScript types +``` + +### Skills + +Load the vue-admin-dev skill for comprehensive development guidance: + +``` +/skill vue-admin-dev +``` + +## Key Rules + +### Storage + +Always use `SafeStorage` with `AppStorageKey` enum - never use localStorage directly. + +### API Choice + +- Use `request.ts` for workspace-aware APIs (most ops APIs) +- Use `interceptor.ts` for Bearer token APIs expecting `code: 20000` + +### API Response Format + +Standard response from `request.ts`: `{code: 0, details: {data: [...], total: number}}` + +- Success code is `0`, NOT `200` or `20000` +- Access data via `response.details.data` +- Access total via `response.details.total` + +### Comments + +Add JSDoc comments for interfaces and functions: + +```ts +/** 服务器类型 */ +export interface ServerItem { ... } + +/** 获取服务器列表(分页) */ +export const fetchServerList = (params?: ServerListParams) => { ... } +``` + +### useRequest Hook + +Does NOT work in async functions. Use `.bind(null, params)` pattern in setup scope. + +### Dynamic Routes + +Routes loaded from server via permission guard. Don't modify `isMenuLoading`/`isMenuLoaded` flags. + +### Code Style + +- No semicolons +- Single quotes +- Print width: 140 characters +- JSDoc comments for types and functions + +## Templates + +Templates use placeholder syntax: + +- `{{Module}}` - PascalCase module name (e.g., `UrlDevice`) +- `{{module}}` - lowercase module name (e.g., `url-device`) +- `{{ModuleTitle}}` - Chinese title (e.g., `URL监控设备`) +- `{{ModuleName}}` - Vue component name (e.g., `UrlDeviceManagement`) + +Replace these placeholders when generating new modules. diff --git a/.kilo/agent/rules-architect.md b/.kilo/agent/rules-architect.md new file mode 100644 index 0000000..8ef4090 --- /dev/null +++ b/.kilo/agent/rules-architect.md @@ -0,0 +1,68 @@ +# Architect Mode Rules (Non-Obvious Only) + +## Architecture Overview + +- Vue 3 SPA with dynamic route loading from server +- Pinia stores: `app`, `user`, `tab-bar` (see [`src/store/`](src/store/)) +- Two-layer route guard: user login check + permission check + +## Dynamic Route System + +- Server menu fetched in [`app store`](src/store/modules/app/index.ts) via `fetchServerMenuConfig()` +- Routes registered dynamically in [`permission.ts`](src/router/guard/permission.ts) +- Uses `isMenuLoading`/`isMenuLoaded` flags to prevent duplicate loads + +## API Architecture + +- Two axios instances for different auth patterns: + - [`request.ts`](src/api/request.ts): Workspace header + custom token format + - [`interceptor.ts`](src/api/interceptor.ts): Bearer token + code 20000 validation +- Choose based on backend API requirements + +## API Response Format + +- Standard response structure: `{code: 0, details: {data: [...], total: number}}` +- Success code is `0`, NOT `200` or `20000` +- `request.ts` returns `response.data` directly +- Access list data via `response.details.data` +- Access total count via `response.details.total` + +## State Management + +- [`SafeStorage`](src/utils/safeStorage.ts) required for all localStorage access +- Supports TTL expiry and type-safe keys via enum + +## Component Patterns + +- Global components registered in [`components/index.ts`](src/components/index.ts) +- ECharts modules manually imported for bundle size optimization +- Use `SearchTable` composite component for list pages + +## Directory Structure + +``` +src/ + api/ # API layer (ops/ for business, module/ for auth) + components/ # Global reusable components + hooks/ # Composition functions + router/ # Route config and guards + store/ # Pinia stores + utils/ # Utility functions + views/ops/ # Main business modules + pages/ + dc/ # Data center management + monitor/ # Monitoring modules + netarch/ # Network architecture + report/ # Reports + system-settings/ # System config +``` + +## Module Creation Checklist + +1. Create API file in `src/api/ops/` +2. Create page directory in `src/views/ops/pages/` +3. Create `config/columns.ts` for table config +4. Create `config/search-form.ts` for search config +5. Create main `index.vue` with SearchTable +6. Create `components/FormDialog.vue` for CRUD +7. Create `components/Detail.vue` for detail view diff --git a/.kilo/agent/rules-ask.md b/.kilo/agent/rules-ask.md new file mode 100644 index 0000000..973ac66 --- /dev/null +++ b/.kilo/agent/rules-ask.md @@ -0,0 +1,47 @@ +# Ask Mode Rules (Non-Obvious Only) + +## Project Structure + +- Vue 3 + Arco Design admin template with Pinia state management +- Vite config files located in `config/` directory (not root) + +## Key Directories + +- `src/api/` - API layer with two axios instances +- `src/views/ops/` - Main business modules (kb, netarch, asset, etc.) +- `src/router/guard/` - Route guards including dynamic menu loading +- `src/store/modules/app/` - App store with server menu fetching + +## Documentation References + +- Arco Design Vue: https://arco.design/vue +- Vue Flow (for topology): https://vueflow.dev/ + +## API Patterns + +- Use [`request.ts`](src/api/request.ts) for workspace-aware requests +- Use [`interceptor.ts`](src/api/interceptor.ts) for standard Bearer token auth + +## API Response Format + +- Standard response: `{code: 0, details: {...}}` +- Success code is `0`, NOT `200` or `20000` +- Access data via `response.details.data` + +## Component Libraries + +- Arco Design Vue components: ``, ``, ``, etc. +- Global components: `SearchTable`, `SearchForm`, `DataTable`, `Chart` + +## Code Style + +- No semicolons +- Single quotes +- Print width: 140 characters +- Path alias: `@/` → `src/` + +## Build Commands + +- `pnpm dev` - Start dev server +- `pnpm build` - Production build +- `pnpm lint` - Run ESLint + Prettier diff --git a/.kilo/agent/rules-code.md b/.kilo/agent/rules-code.md new file mode 100644 index 0000000..7221e17 --- /dev/null +++ b/.kilo/agent/rules-code.md @@ -0,0 +1,85 @@ +# Code Mode Rules (Non-Obvious Only) + +## API Layer + +- Two axios instances exist: [`request.ts`](src/api/request.ts) (custom with workspace header) and [`interceptor.ts`](src/api/interceptor.ts) (global with Bearer token). Choose based on whether you need workspace support. +- [`request.ts`](src/api/request.ts) returns `response.data` directly (no nesting) + +## Storage + +- Always use [`SafeStorage`](src/utils/safeStorage.ts) with `AppStorageKey` enum - never use localStorage directly. Supports TTL via third parameter. + +## useRequest Hook + +- [`useRequest()`](src/hooks/request.ts) invokes API immediately - does NOT work in async functions. Pass params via `.bind(null, params)` pattern. + +## Dynamic Routes + +- Routes loaded from server in [`permission.ts`](src/router/guard/permission.ts) using `isMenuLoading`/`isMenuLoaded` flags. Don't modify these flags manually. + +## Vue Component Structure + +- Use ` + + + + +```` + +## Critical Rules + +### Storage + +```ts +// CORRECT +SafeStorage.set(AppStorageKey.TOKEN, token, 3600000) +SafeStorage.get(AppStorageKey.USER_INFO) + +// WRONG +localStorage.setItem('token', token) +``` + +### API Choice + +```ts +// Workspace-aware APIs (most ops APIs) +import { request } from '@/api/request' +request.post('/DC-Control/v1/servers', data) +// Response: {code: 0, details: {data: [...], total: number}} + +// Standard Bearer token APIs +import axios from 'axios' // Uses interceptor.ts defaults +// Response: {code: 20000, data: {...}} +``` + +### Comments (JSDoc Style) + +```ts +/** 服务器类型 */ +export interface ServerItem { + id: number + name: string +} + +/** 获取服务器列表(分页) */ +export const fetchServerList = (params?: ServerListParams) => { + return request.get('/DC-Control/v1/servers', { params }) +} +``` + +### API Choice + +```ts +// Workspace-aware APIs (most ops APIs) +import { request } from '@/api/request' +request.post('/DC-Control/v1/servers', data) + +// Standard Bearer token APIs +import axios from 'axios' // Uses interceptor.ts defaults +``` + +### useRequest Hook + +```ts +// CORRECT - Use in setup scope +const { loading, response } = useRequest(fetchList.bind(null, params)) + +// WRONG - Use in async function +async function loadData() { + const { loading, response } = useRequest(fetchList()) // Doesn't work! +} +``` + +### Dynamic Routes + +```ts +// Routes auto-loaded by permission guard +// Don't manually add routes except in menu config +// Don't modify isMenuLoading/isMenuLoaded flags +``` + +## Quick Reference + +### Arco Design Components + +- Table: `` with columns, pagination, slots +- Form: `` with `` and validation +- Dialog: `` with v-model:visible +- Drawer: `` for side panels +- Message: `Message.success/error/info/warning()` +- Modal: `Modal.confirm/info/error()` + +### Common Slots + +- `#toolbar-left` - Left toolbar buttons +- `#toolbar-right` - Right toolbar buttons +- `#actions` - Row actions column +- `#enabled` - Status toggle column +- `#{fieldName}` - Custom field rendering + +### Import Order + +1. Vue: `ref, reactive, computed, watch, onMounted` +2. Arco: `Message, Modal` + Icons +3. Components: `SearchTable, FormDialog` +4. Configs: `searchFormConfig, columns` +5. APIs: `fetchList, createItem, updateItem` +6. Types: `Item, ListParams, CreateData` + +## Workflow + +### Creating New Module + +1. Run `/new-api {module}` to create API +2. Run `/new-page {module} {category}` to create page +3. Adjust columns and search-form configs +4. Customize FormDialog fields +5. Add Detail view content +6. Test with `pnpm dev` + +### Adding Feature to Existing Module + +1. Check existing patterns in similar modules +2. Add API function if needed +3. Update columns/search-form config +4. Add component or slot if needed +5. Run `pnpm lint` after changes + +## Debugging + +- Console logs prefixed with `[Permission Guard]` for route issues +- Network tab for API debugging +- Vue DevTools for state inspection +- `pnpm lint` for code issues diff --git a/.kilo/templates/Detail.vue b/.kilo/templates/Detail.vue new file mode 100644 index 0000000..fe590ab --- /dev/null +++ b/.kilo/templates/Detail.vue @@ -0,0 +1,77 @@ + + + + + diff --git a/.kilo/templates/FormDialog.vue b/.kilo/templates/FormDialog.vue new file mode 100644 index 0000000..527d82f --- /dev/null +++ b/.kilo/templates/FormDialog.vue @@ -0,0 +1,107 @@ + + + + + diff --git a/.kilo/templates/api-module.ts b/.kilo/templates/api-module.ts new file mode 100644 index 0000000..e80f62c --- /dev/null +++ b/.kilo/templates/api-module.ts @@ -0,0 +1,66 @@ +import { request } from '@/api/request' + +/** {{Module}}类型 */ +export interface {{Module}}Item { + id: number + created_at: string + updated_at: string + name: string + description?: string + enabled: boolean +} + +/** {{Module}}列表响应 */ +export interface {{Module}}ListResponse { + total: number + page: number + page_size: number + data: {{Module}}Item[] +} + +/** {{Module}}列表请求参数 */ +export interface {{Module}}ListParams { + page?: number + size?: number + keyword?: string + enabled?: boolean +} + +/** 创建{{Module}}请求参数 */ +export interface {{Module}}CreateData { + name: string + description?: string + enabled?: boolean +} + +/** 更新{{Module}}请求参数 */ +export interface {{Module}}UpdateData { + name?: string + description?: string + enabled?: boolean +} + +/** 获取{{Module}}列表(分页) */ +export const fetch{{Module}}List = (params?: {{Module}}ListParams) => { + return request.get<{{Module}}ListResponse>('/DC-Control/v1/{{module}}s', { params }) +} + +/** 获取{{Module}}详情 */ +export const fetch{{Module}}Detail = (id: number) => { + return request.get<{{Module}}Item>(`/DC-Control/v1/{{module}}s/${id}`) +} + +/** 创建{{Module}} */ +export const create{{Module}} = (data: {{Module}}CreateData) => { + return request.post<{ message: string; id: number }>('/DC-Control/v1/{{module}}s', data) +} + +/** 更新{{Module}} */ +export const update{{Module}} = (id: number, data: {{Module}}UpdateData) => { + return request.put<{ message: string }>(`/DC-Control/v1/{{module}}s/${id}`, data) +} + +/** 删除{{Module}} */ +export const delete{{Module}} = (id: number) => { + return request.delete<{ message: string }>(`/DC-Control/v1/{{module}}s/${id}`) +} \ No newline at end of file diff --git a/.kilo/templates/columns.ts b/.kilo/templates/columns.ts new file mode 100644 index 0000000..c5499e5 --- /dev/null +++ b/.kilo/templates/columns.ts @@ -0,0 +1,39 @@ +export const columns = [ + { + dataIndex: 'id', + title: 'ID', + width: 80, + slotName: 'id', + }, + { + dataIndex: 'name', + title: '名称', + width: 150, + }, + { + dataIndex: 'description', + title: '描述', + width: 200, + ellipsis: true, + tooltip: true, + }, + { + dataIndex: 'enabled', + title: '启用状态', + width: 100, + slotName: 'enabled', + }, + { + dataIndex: 'created_at', + title: '创建时间', + width: 180, + slotName: 'created_at', + }, + { + dataIndex: 'actions', + title: '操作', + width: 180, + fixed: 'right' as const, + slotName: 'actions', + }, +] diff --git a/.kilo/templates/index.vue b/.kilo/templates/index.vue new file mode 100644 index 0000000..7393dd6 --- /dev/null +++ b/.kilo/templates/index.vue @@ -0,0 +1,235 @@ + + + + + + + diff --git a/.kilo/templates/search-form.ts b/.kilo/templates/search-form.ts new file mode 100644 index 0000000..1c794db --- /dev/null +++ b/.kilo/templates/search-form.ts @@ -0,0 +1,22 @@ +import type { FormItem } from '@/components/search-form/types' + +export const searchFormConfig: FormItem[] = [ + { + field: 'keyword', + label: '关键词', + type: 'input', + placeholder: '请输入名称', + span: 6, + }, + { + field: 'enabled', + label: '启用状态', + type: 'select', + placeholder: '请选择启用状态', + options: [ + { label: '已启用', value: true }, + { label: '已禁用', value: false }, + ], + span: 6, + }, +] diff --git a/AGENTS.md b/AGENTS.md index 0abef67..5ba390b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -3,34 +3,187 @@ This file provides guidance to agents when working with code in this repository. ## Build Commands + - `pnpm dev` - Start dev server (config: `config/vite.config.dev.ts`) - `pnpm build` - Production build (config: `config/vite.config.prod.ts`) - `pnpm lint` - Run ESLint + Prettier +- `pnpm lint:eslint --fix` - Fix ESLint issues +- `pnpm lint:prettier --write` - Fix Prettier formatting - No test framework configured ## Critical Architecture Notes ### Vite Config Location + Config files are in `config/` directory, NOT root. All vite commands reference `./config/vite.config.*.ts`. ### Two Axios Instances -- [`src/api/request.ts`](src/api/request.ts) - Custom instance with workspace header support and `needWorkspace` param + +- [`src/api/request.ts`](src/api/request.ts) - Custom instance with workspace header support and `needWorkspace` param. Returns `response.data` directly. - [`src/api/interceptor.ts`](src/api/interceptor.ts) - Global instance with Bearer token and code 20000 validation +- Use `request.ts` for most ops APIs that need workspace header +- Use `interceptor.ts` pattern for APIs expecting `code: 20000` response format + +### API Response Format + +Standard response from `request.ts`: `{code: 0, details: {data: [...], total: number}}` + +- Success code is `0`, NOT `200` or `20000` +- Access list data via `response.details.data` +- Access total count via `response.details.total` + +```ts +const response = await fetchList(params) +if (response && response.code === 0 && response.details) { + tableData.value = response.details.data || [] + pagination.total = response.details.total || 0 +} +``` ### Storage Pattern + Use [`SafeStorage`](src/utils/safeStorage.ts) instead of localStorage directly. Supports TTL expiry and type-safe keys via `AppStorageKey` enum. +```ts +// Set with optional TTL (milliseconds) +SafeStorage.set(AppStorageKey.TOKEN, token, 3600000) +// Get +SafeStorage.get(AppStorageKey.USER_INFO) +// Clear all app storage +SafeStorage.clearAppStorage() +``` + ### Dynamic Route Loading + Routes are loaded from server via [`fetchServerMenuConfig()`](src/store/modules/app/index.ts) with permission guard in [`permission.ts`](src/router/guard/permission.ts). Uses flags `isMenuLoading`/`isMenuLoaded` to prevent duplicate loads. +- Do NOT manually modify `isMenuLoading`/`isMenuLoaded` flags +- Routes are registered dynamically via `router.addRoute()` +- Menu data comes from `userPmn` API and transformed via `buildTree` + `transformMenuToRoutes` + ### useRequest Hook Limitation + [`useRequest()`](src/hooks/request.ts) does NOT work in async functions - it immediately invokes the API. Use `.bind(null, params)` to pass arguments. +```ts +// CORRECT - Use in setup scope with bind +const { loading, response } = useRequest(fetchList.bind(null, { page: 1 })) +// WRONG - Use in async function +async function loadData() { + const { loading, response } = useRequest(fetchList()) // Won't work! +} +``` + +## Standard Module Structure + +### Page Module Pattern + +``` +src/views/ops/pages/{category}/{module}/ +├── index.vue # Main page component (SearchTable) +├── components/ +│ ├── FormDialog.vue # Create/Edit form dialog +│ ├── Detail.vue # Detail drawer/view +│ └── QuickConfigDialog.vue # Optional quick config dialog +└── config/ + ├── columns.ts # Table columns configuration + └── search-form.ts # Search form configuration +``` + +### API Module Pattern + +``` +src/api/ops/{module}.ts +├── {Module}Item # Single item interface +├── {Module}ListResponse # List response interface +├── {Module}ListParams # Query parameters interface +├── {Module}CreateData # Create payload interface +├── {Module}UpdateData # Update payload interface +├── fetch{Module}List() # GET list with pagination +├── fetch{Module}Detail() # GET single item +├── create{Module}() # POST create +├── update{Module}() # PUT update +├── patch{Module}() # PATCH partial update +└── delete{Module}() # DELETE remove +``` + +### Component Template Pattern + +```vue + + + + + + + +``` + ## Code Style + - No semicolons (Prettier enforced) - Single quotes, trailing commas (es5) - Print width: 140 characters - Path alias: `@/` → `src/` +- **Add JSDoc comments** for interfaces, types, and functions + + ```ts + /** 服务器类型 */ + export interface ServerItem { ... } + + /** 获取服务器列表(分页) */ + export const fetchServerList = (params?: ServerListParams) => { ... } + ``` ## Vue/i18n Aliases Required -Vite config includes aliases for `vue-i18n/dist/vue-i18n.cjs.js` and `vue/dist/vue.esm-bundler.js` - don't remove these. \ No newline at end of file + +Vite config includes aliases for `vue-i18n/dist/vue-i18n.cjs.js` and `vue/dist/vue.esm-bundler.js` - don't remove these. + +## Key Components + +- [`SearchTable`](src/components/search-table/index.vue) - Composite search + table component +- [`SearchForm`](src/components/search-form/index.vue) - Search form with dynamic fields +- [`DataTable`](src/components/data-table/index.vue) - Enhanced table with toolbar +- [`Chart`](src/components/chart/index.vue) - ECharts wrapper component + +## Import Order + +1. Vue core (ref, reactive, computed, watch, onMounted) +2. Third-party (Message, Modal, icons from Arco Design) +3. Global components (SearchTable, DataTable) +4. Local components (FormDialog, Detail) +5. Config files (columns, searchForm) +6. API functions (fetchList, createItem) +7. Type interfaces (Item, ListParams) + +## API Response Handling + +- `request.ts` returns `response.data` directly (no `.data.data` nesting) +- Success code: `0` (NOT `200` or `20000`) +- Access data: `response.details.data` +- Access total: `response.details.total` +- `interceptor.ts` expects `code: 20000` for success +- Token expiry codes: 50008, 50012, 50014 trigger logout (interceptor.ts) +- Status 401 or error "Token has expired" redirects to `/auth/login` (request.ts) + +## Debug Tips + +- Console logs prefixed with `[Permission Guard]` for route loading issues +- Check `isMenuLoading`/`isMenuLoaded` flags if routes not appearing +- Network tab for API debugging +- Vue DevTools for component state inspection +- Run `pnpm lint` after changes to catch issues diff --git a/kilo.json b/kilo.json new file mode 100644 index 0000000..d619db5 --- /dev/null +++ b/kilo.json @@ -0,0 +1,28 @@ +{ + "agents": { + "default": "code", + "allowed": ["code", "architect", "ask", "debug"] + }, + "commands": { + "dir": ".kilo/command" + }, + "rules": { + "dir": ".kilo/agent" + }, + "skills": { + "dir": ".kilo/skill" + }, + "hooks": { + "beforeFileWrite": "pnpm lint --fix", + "afterTaskComplete": "pnpm lint" + }, + "context": { + "maxTokens": 8000, + "includePatterns": ["src/**/*.ts", "src/**/*.vue", "src/**/*.tsx"], + "excludePatterns": ["node_modules", "dist", ".git"] + }, + "logging": { + "level": "info", + "file": ".kilo/logs/kilo.log" + } +}