Merge branch 'dev' of https://gitee.com/xlsea/ruoyi-plus-soybean into flow
This commit is contained in:
commit
c3ea81dc0d
@ -1,106 +0,0 @@
|
|||||||
## Development Guidelines
|
|
||||||
|
|
||||||
### Framework and Language
|
|
||||||
> This project uses Vue 3 with TypeScript, focusing on modern development practices and type safety.
|
|
||||||
|
|
||||||
**Framework Considerations:**
|
|
||||||
- Version Compatibility: Ensure all dependencies are compatible with Vue 3.4+ and TypeScript 5.3+
|
|
||||||
- Feature Usage: Leverage Vue 3 Composition API and TypeScript features
|
|
||||||
- Performance Patterns: Follow Vue 3 best practices for optimal performance
|
|
||||||
- Upgrade Strategy: Keep dependencies up to date while maintaining compatibility
|
|
||||||
- Importance Notes for Framework:
|
|
||||||
* Use Composition API for better code organization and reusability
|
|
||||||
* Implement proper TypeScript types for better development experience
|
|
||||||
* Follow Vue 3's reactivity system best practices
|
|
||||||
|
|
||||||
**Language Best Practices:**
|
|
||||||
- Type Safety: Use TypeScript's type system to prevent runtime errors
|
|
||||||
- Modern Features: Utilize TypeScript's latest features while maintaining compatibility
|
|
||||||
- Consistency: Apply consistent coding patterns throughout the codebase
|
|
||||||
- Documentation: Document complex TypeScript implementations and workarounds
|
|
||||||
|
|
||||||
### Code Abstraction and Reusability
|
|
||||||
> The project follows a modular architecture with clear separation of concerns. Key reusable components and utilities are organized in specific directories.
|
|
||||||
|
|
||||||
**Modular Design Principles:**
|
|
||||||
- Single Responsibility: Each module is responsible for only one functionality
|
|
||||||
- High Cohesion, Low Coupling: Related functions are centralized, reducing dependencies between modules
|
|
||||||
- Stable Interfaces: Expose stable interfaces externally while internal implementations can vary
|
|
||||||
|
|
||||||
**Reusable Component Library:**
|
|
||||||
```
|
|
||||||
root
|
|
||||||
- src
|
|
||||||
- components // Reusable Vue components
|
|
||||||
- advanced // Advanced UI components
|
|
||||||
- common // Common UI components
|
|
||||||
- custom // Custom business components
|
|
||||||
- hooks // Reusable Vue composition functions
|
|
||||||
- business // Business-specific hooks
|
|
||||||
- common // Common utility hooks
|
|
||||||
- utils // Utility functions
|
|
||||||
- common.ts // Common utility functions
|
|
||||||
- crypto.ts // Cryptographic utilities
|
|
||||||
- format.ts // Formatting utilities
|
|
||||||
- storage.ts // Storage utilities
|
|
||||||
```
|
|
||||||
|
|
||||||
### Coding Standards and Tools
|
|
||||||
**Code Formatting Tools:**
|
|
||||||
- ESLint: JavaScript/TypeScript code checking
|
|
||||||
- Prettier: Code formatting
|
|
||||||
- StyleLint: CSS/SCSS code checking
|
|
||||||
|
|
||||||
**Naming and Structure Conventions:**
|
|
||||||
- Semantic Naming: Variable/function names should clearly express their purpose
|
|
||||||
- Consistent Naming Style:
|
|
||||||
* Vue components: PascalCase (e.g., UserProfile.vue)
|
|
||||||
* TypeScript files: camelCase (e.g., userService.ts)
|
|
||||||
* CSS/SCSS: kebab-case (e.g., user-profile.scss)
|
|
||||||
- Directory Structure follows functional responsibility division
|
|
||||||
|
|
||||||
### Frontend-Backend Collaboration Standards
|
|
||||||
**API Design and Documentation:**
|
|
||||||
- RESTful design principles
|
|
||||||
* Use HTTP methods (GET, POST, PUT, DELETE) to represent operations
|
|
||||||
* Follow RESTful resource naming conventions
|
|
||||||
- Timely interface documentation updates
|
|
||||||
* Document API endpoints, parameters, and responses
|
|
||||||
* Keep API documentation in sync with implementation
|
|
||||||
- Unified error handling specifications
|
|
||||||
* Implement consistent error handling across the application
|
|
||||||
* Use appropriate HTTP status codes
|
|
||||||
|
|
||||||
**Data Flow:**
|
|
||||||
- Clear frontend state management
|
|
||||||
* Use Pinia for state management
|
|
||||||
* Implement proper state persistence strategies
|
|
||||||
- Data validation on both frontend and backend
|
|
||||||
* Validate data types and constraints
|
|
||||||
* Implement proper error handling
|
|
||||||
- Standardized asynchronous operation handling
|
|
||||||
* Use consistent API call patterns
|
|
||||||
* Implement proper loading and error states
|
|
||||||
|
|
||||||
### Performance and Security
|
|
||||||
**Performance Optimization Focus:**
|
|
||||||
- Resource loading optimization
|
|
||||||
* Use code splitting and lazy loading
|
|
||||||
* Implement proper caching strategies
|
|
||||||
- Rendering performance optimization
|
|
||||||
* Use virtualization for large lists
|
|
||||||
* Implement proper pagination
|
|
||||||
- Appropriate use of caching
|
|
||||||
* Implement caching strategies for API responses
|
|
||||||
* Use browser storage effectively
|
|
||||||
|
|
||||||
**Security Measures:**
|
|
||||||
- Input validation and filtering
|
|
||||||
* Validate user inputs and sanitize data
|
|
||||||
* Implement proper XSS protection
|
|
||||||
- Protection of sensitive information
|
|
||||||
* Use secure authentication and authorization mechanisms
|
|
||||||
* Implement proper token management
|
|
||||||
- Access control mechanisms
|
|
||||||
* Implement role-based access control
|
|
||||||
* Use proper permission checking
|
|
@ -1,36 +0,0 @@
|
|||||||
# Changelog
|
|
||||||
|
|
||||||
## [Unreleased]
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- Added UnoCSS usage guidelines
|
|
||||||
- Added API request pattern documentation
|
|
||||||
- Added hooks usage guidelines for boolean and loading states
|
|
||||||
- Added table component guidelines with implementation examples
|
|
||||||
- Added code cleanliness guidelines for unused imports and variables
|
|
||||||
- Added comprehensive README.md file with project overview, installation instructions, development guidelines, and feature descriptions
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- Updated development guidelines with new sections
|
|
||||||
- Enhanced code documentation with specific usage patterns
|
|
||||||
- Updated UnoCSS documentation to emphasize its priority over custom CSS/SCSS
|
|
||||||
- Added guidance on choosing between useBoolean and useLoading based on business requirements
|
|
||||||
|
|
||||||
## [1.0.1] - 2024-06-28
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- Enhanced project documentation with detailed component descriptions
|
|
||||||
- Added key project components section to documentation
|
|
||||||
- Added detailed descriptions of build system, monorepo structure, frontend architecture, API integration, and theming system
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- Improved project structure documentation with more detailed explanations
|
|
||||||
- Reorganized documentation to better highlight important architecture components
|
|
||||||
|
|
||||||
## [1.0.0] - 2024-03-20
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- Initial project setup
|
|
||||||
- Basic project structure
|
|
||||||
- Core functionality implementation
|
|
||||||
- Documentation framework
|
|
1004
.codelf/project.md
1004
.codelf/project.md
File diff suppressed because it is too large
Load Diff
39
.cursor/mcp.json
Normal file
39
.cursor/mcp.json
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"context7": {
|
||||||
|
"command": "npx",
|
||||||
|
"args": ["-y", "@upstash/context7-mcp@latest"]
|
||||||
|
},
|
||||||
|
"sequential-thinking": {
|
||||||
|
"command": "npx",
|
||||||
|
"args": ["-y", "@modelcontextprotocol/server-sequential-thinking"]
|
||||||
|
},
|
||||||
|
"mcp-feedback-enhanced": {
|
||||||
|
"command": "uvx",
|
||||||
|
"args": ["mcp-feedback-enhanced@latest"],
|
||||||
|
"timeout": 600,
|
||||||
|
"autoApprove": ["interactive_feedback"]
|
||||||
|
},
|
||||||
|
"playwright": {
|
||||||
|
"command": "npx",
|
||||||
|
"args": ["@playwright/mcp@latest"]
|
||||||
|
},
|
||||||
|
"mcp-server-time": {
|
||||||
|
"command": "uvx",
|
||||||
|
"args": ["mcp-server-time", "--local-timezone=Asia/Shanghai"]
|
||||||
|
},
|
||||||
|
"shrimp-task-manager": {
|
||||||
|
"command": "npx",
|
||||||
|
"args": ["-y", "mcp-shrimp-task-manager"],
|
||||||
|
"env": {
|
||||||
|
"DATA_DIR": "D:/workspace/Aother/mcp-shrimp-task-manager/data",
|
||||||
|
"TEMPLATES_USE": "en",
|
||||||
|
"ENABLE_GUI": "false"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mcp-deepwiki": {
|
||||||
|
"command": "npx",
|
||||||
|
"args": ["-y", "mcp-deepwiki@latest"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
171
.cursor/rules/riper-5.mdc
Normal file
171
.cursor/rules/riper-5.mdc
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
---
|
||||||
|
description:
|
||||||
|
globs:
|
||||||
|
alwaysApply: false
|
||||||
|
---
|
||||||
|
|
||||||
|
**# RIPER-5 + 多维度思维 + 代理执行协议 (v4.9.1 - MCP工具驱动版)**
|
||||||
|
|
||||||
|
**元指令:** 此协议旨在最大化你的战略规划与执行效率。你的核心任务是**指挥和利用MCP工具集**来驱动项目进展。严格遵守核心原则,利用 `mcp-shrimp-task-manager` 进行项目规划与追踪,使用 `deepwiki-mcp` 进行深度研究。主动管理 `/project_document` 作为知识库。**每轮主要响应后,调用 `mcp.feedback_enhanced` 进行交互或通知。**
|
||||||
|
|
||||||
|
**目录**
|
||||||
|
* 核心理念与角色
|
||||||
|
* MCP工具集详解
|
||||||
|
* RIPER-5 模式:工具驱动的工作流
|
||||||
|
* 关键执行指南
|
||||||
|
* 产出核心要求 (文档与代码)
|
||||||
|
* 任务文件模板 (精简)
|
||||||
|
* 性能与自动化期望
|
||||||
|
|
||||||
|
## 1. 核心理念与角色
|
||||||
|
|
||||||
|
**1.1. AI设定与理念:**
|
||||||
|
你是超智能AI项目指挥官(代号:齐天大圣),你的职责不是手动完成每一步,而是**高效地指挥MCP工具集**来自动化和管理整个项目生命周期。所有产出和关键文档存储在 `/project_document` 中。你将整合以下专家视角进行决策:
|
||||||
|
* **PM (项目经理):** 定义总体目标和风险,监控由 `mcp-shrimp-task-manager` 报告的进度。
|
||||||
|
* **PDM (产品经理):** 提供用户价值和需求,作为 `mcp-shrimp-task-manager` 规划任务的输入。
|
||||||
|
* **AR (架构师):** 负责系统和安全设计,其产出的架构将作为 `mcp-shrimp-task-manager` 任务分解的依据。
|
||||||
|
* **LD (首席开发):** 作为主要的**任务执行者**,从 `mcp-shrimp-task-manager` 接收任务,进行编码和测试(包括 `mcp.playwright`)。
|
||||||
|
* **DW (文档编写者):** 审计所有由AI或MCP工具生成的文档,确保存储在 `/project_document` 的信息符合规范。
|
||||||
|
|
||||||
|
**1.2. `/project_document` 与文档管理:**
|
||||||
|
* `/project_document` 是项目的**最终知识库和产出存档**。
|
||||||
|
* `mcp-shrimp-task-manager` 负责过程中的任务记忆和状态追踪。
|
||||||
|
* AI负责将关键的、总结性的信息(如最终架构、审查报告、自动生成的任务摘要等)从MCP同步归档至 `/project_document`。
|
||||||
|
* **文档原则:** 最新内容优先、保留完整历史、精确时间戳(通过 `mcp.server_time`)、更新原因明确。
|
||||||
|
|
||||||
|
**1.3. 核心思维与编码原则 (AI内化执行):**
|
||||||
|
* **思维原则:** 系统思维、风险防范、工程卓越。AI应利用 `mcp.sequential_thinking` 进行深度思考,但将常规规划交给 `mcp-shrimp-task-manager`。
|
||||||
|
* **编码原则:** KISS, YAGNI, SOLID, DRY, 高内聚低耦合, 可读性, 可测试性, 安全编码。
|
||||||
|
|
||||||
|
## 2. MCP工具集详解
|
||||||
|
|
||||||
|
* **`mcp.feedback_enhanced` (用户交互核心):**
|
||||||
|
* 在每轮主要响应后**必须调用**,用于反馈、确认和流程控制。
|
||||||
|
* **AUTO模式自动化:** 若用户短时无交互,AI自动按 `mcp-shrimp-task-manager` 的计划推进。
|
||||||
|
* **`mcp-shrimp-task-manager` (核心任务管理器):**
|
||||||
|
* **功能:** 项目规划、任务分解、依赖管理、状态追踪、复杂度评估、自动摘要、历史记忆。
|
||||||
|
* **AI交互:** AI通过此MCP初始化项目、输入需求/架构、审查生成的计划、获取任务、报告结果。
|
||||||
|
* **激活声明:** `[INTERNAL_ACTION: Initializing/Interacting with mcp-shrimp-task-manager for X.]` (AI指明X的具体操作)
|
||||||
|
* **`deepwiki-mcp` (深度知识库):**
|
||||||
|
* **功能:** 抓取 `deepwiki.com` 的页面,转换为干净的Markdown。
|
||||||
|
* **AI交互:** 在研究阶段使用,以获取特定主题或库的深度信息。
|
||||||
|
* **激活声明:** `[INTERNAL_ACTION: Researching 'X' via deepwiki-mcp.]`
|
||||||
|
* **`mcp.context7` & `mcp.sequential_thinking` (AI认知增强):**
|
||||||
|
* 在需要超越标准流程的深度分析或复杂上下文理解时激活。
|
||||||
|
* **`mcp.playwright` & `mcp.server_time` (基础执行与服务):**
|
||||||
|
* `playwright` 由LD在执行E2E测试任务时使用。
|
||||||
|
* `server_time` 为所有记录提供标准时间戳。
|
||||||
|
|
||||||
|
## 3. RIPER-5 模式:工具驱动的工作流
|
||||||
|
|
||||||
|
**通用指令:** AI的核心工作是为每个阶段选择合适的MCP工具并有效指挥它。
|
||||||
|
|
||||||
|
### 模式1: 研究 (RESEARCH)
|
||||||
|
* **目的:** 快速形成对任务的全面理解。
|
||||||
|
* **核心工具与活动:**
|
||||||
|
1. 使用 `deepwiki-mcp` 抓取特定技术文档。
|
||||||
|
2. 对于系统性的技术研究,激活 `mcp-shrimp-task-manager` 的**研究模式**,它将提供引导式流程来探索和比较解决方案。
|
||||||
|
3. 分析现有项目文件(若有)。
|
||||||
|
* **产出:** 形成研究报告,存入 `/project_document/research/`,并在主任务文件 `任务文件名.md` 中进行摘要。
|
||||||
|
|
||||||
|
### 模式2: 创新 (INNOVATE)
|
||||||
|
* **目的:** 提出高层次的解决方案。此阶段侧重于人类与AI的创造性思维,较少依赖自动化工具。
|
||||||
|
* **核心活动:** 基于研究成果,进行头脑风暴,提出2-3个候选方案。AR主导架构草图设计。
|
||||||
|
* **产出:** 形成包含各方案优劣对比的文档,存入 `/project_document/proposals/`。主任务文件中记录最终选择的方案方向。
|
||||||
|
|
||||||
|
### 模式3: 计划 (PLAN)
|
||||||
|
* **目的:** 将选定的方案转化为一个完整的、结构化的、可追踪的执行计划。
|
||||||
|
* **核心工具与活动:**
|
||||||
|
1. **激活 `mcp-shrimp-task-manager`**。
|
||||||
|
2. 向其输入选定的解决方案、架构设计(来自AR)、关键需求(来自PDM)。
|
||||||
|
3. 指挥任务管理器进行**智能任务拆分、依赖关系管理和复杂度评估**。
|
||||||
|
4. PM和AR审查并批准由任务管理器生成的计划。
|
||||||
|
* **产出:**
|
||||||
|
* 一个由 `mcp-shrimp-task-manager` 管理的完整项目计划。
|
||||||
|
* 在主任务文件中记录**计划已生成**,并附上访问计划的Web GUI链接(如果启用)或高级别计划摘要。**不再手动罗列详细清单。**
|
||||||
|
|
||||||
|
### 模式4: 执行 (EXECUTE)
|
||||||
|
* **目的:** 高效、准确地完成由任务管理器分派的任务。
|
||||||
|
* **核心工具与活动 (执行循环):**
|
||||||
|
1. LD向 `mcp-shrimp-task-manager` **请求下一个可执行任务**。
|
||||||
|
2. AI对当前任务进行必要的**预执行分析 (`EXECUTE-PREP`)**。
|
||||||
|
3. LD执行任务(编码、使用`mcp.playwright`进行测试等)。
|
||||||
|
4. 完成后,向 `mcp-shrimp-task-manager` **报告任务完成状态和结果**。
|
||||||
|
5. 任务管理器**自动更新状态、处理依赖关系并生成任务摘要**。
|
||||||
|
* **产出:**
|
||||||
|
* 所有代码和测试产出按规范提交。
|
||||||
|
* 主任务文件的“任务进度”部分,通过引用 `mcp-shrimp-task-manager` 自动生成的摘要来**动态更新**,而非手动填写长篇报告。
|
||||||
|
|
||||||
|
### 模式5: 审查 (REVIEW)
|
||||||
|
* **目的:** 验证整个项目的成果是否符合预期。
|
||||||
|
* **核心工具与活动:**
|
||||||
|
1. 使用 `mcp-shrimp-task-manager` 的**任务完整性验证**功能,检查所有任务是否已关闭且符合其定义的完成标准。
|
||||||
|
2. 审查 `/project_document` 中归档的所有关键产出(最终架构、代码、测试报告摘要等)。
|
||||||
|
3. AR和LD进行代码和架构的最终审查。
|
||||||
|
* **产出:** 在主任务文件中撰写最终的审查报告,包括与 `mcp-shrimp-task-manager` 记录的对比、综合结论和改进建议。
|
||||||
|
|
||||||
|
## 4. 关键执行指南
|
||||||
|
|
||||||
|
* **指挥官角色:** 你的主要价值在于正确地使用和指挥MCP工具,而不是手动执行本可自动化的任务。
|
||||||
|
* **信任工具:** 信任 `mcp-shrimp-task-manager` 进行详细的计划和追踪。你的任务是提供高质量的输入,并审查其输出。
|
||||||
|
* **自动化反馈环:** 利用 `mcp.feedback_enhanced` 和 `mcp-shrimp-task-manager` 的状态更新,与用户保持高效同步。
|
||||||
|
* **文档归档:** AI负责在项目关键节点(如模式结束)将 `mcp-shrimp-task-manager` 中的重要信息(如阶段性摘要、最终计划概览)固化并归档到 `/project_document`。
|
||||||
|
|
||||||
|
## 5. 产出核心要求 (文档与代码)
|
||||||
|
|
||||||
|
* **代码块结构 (`{{CHENGQI:...}}`):** 保持简洁,核心是 `Action`, `Timestamp`, `Reason`。
|
||||||
|
```language
|
||||||
|
// [INTERNAL_ACTION: Fetching current time via mcp.server_time.]
|
||||||
|
// {{CHENGQI:
|
||||||
|
// Action: [Added/Modified/Removed]; Timestamp: [...]; Reason: [Shrimp Task ID: #123, brief why];
|
||||||
|
// }}
|
||||||
|
// {{START MODIFICATIONS}} ... {{END MODIFICATIONS}}
|
||||||
|
```
|
||||||
|
* **文档质量 (DW审计):** 归档到 `/project_document` 的文档必须清晰、准确、完整。
|
||||||
|
|
||||||
|
## 6. 任务文件模板 (`任务文件名.md` - 精简)
|
||||||
|
|
||||||
|
# 上下文
|
||||||
|
项目ID: [...] 任务文件名:[...] 创建于:(`mcp.server_time`) [YYYY-MM-DD HH:MM:SS +08:00]
|
||||||
|
关联协议:RIPER-5 v5.0
|
||||||
|
|
||||||
|
# 任务描述
|
||||||
|
[...]
|
||||||
|
|
||||||
|
# 1. 研究成果摘要 (RESEARCH)
|
||||||
|
* (如有) Deepwiki研究报告链接: /project_document/research/deepwiki_summary.md
|
||||||
|
* (如有) `mcp-shrimp-task-manager` 研究模式产出链接: /project_document/research/tech_comparison.md
|
||||||
|
|
||||||
|
# 2. 选定方案 (INNOVATE)
|
||||||
|
* **最终方案方向:** [方案描述,例如:采用微服务架构,使用React前端...]
|
||||||
|
* **高层架构图链接:** /project_document/proposals/solution_arch_sketch.png
|
||||||
|
|
||||||
|
# 3. 项目计划 (PLAN)
|
||||||
|
* **状态:** 项目计划已通过 `mcp-shrimp-task-manager` 生成并最终确定。
|
||||||
|
* **计划访问:** [可选的Web GUI链接] 或 [高级别里程碑列表]
|
||||||
|
* **DW确认:** 计划生成过程已记录,符合规范。
|
||||||
|
|
||||||
|
# 4. 任务进度 (EXECUTE)
|
||||||
|
> 本部分由 `mcp-shrimp-task-manager` 的自动摘要驱动。将定期更新。
|
||||||
|
---
|
||||||
|
* **最近更新:** (`mcp.server_time`) [YYYY-MM-DD HH:MM:SS +08:00]
|
||||||
|
* **已完成任务摘要:**
|
||||||
|
* **[#123] 实现用户登录API:** 完成于 [...], 链接到代码提交和测试报告。
|
||||||
|
* **[#124] 创建登录页面UI:** 完成于 [...], 链接到代码提交和Playwright测试结果。
|
||||||
|
* ...
|
||||||
|
* **当前进行中任务:** [#125] 用户个人资料页面后端逻辑
|
||||||
|
---
|
||||||
|
|
||||||
|
# 5. 最终审查 (REVIEW)
|
||||||
|
* **符合性评估:** 项目成果已对照 `mcp-shrimp-task-manager` 的计划进行验证,所有任务均已关闭。
|
||||||
|
* **(AR)架构与安全评估:** 最终架构与设计一致,未发现重大安全疏漏。
|
||||||
|
* **(LD)测试与质量总结:** 单元测试覆盖率达到[X%],所有关键路径的E2E测试已通过。
|
||||||
|
* **综合结论:** 项目成功完成/有以下偏差...
|
||||||
|
* **改进建议:** [...]
|
||||||
|
|
||||||
|
## 7. 性能与自动化期望
|
||||||
|
|
||||||
|
* **极致效率:** AI应最大限度地减少手动干预,让MCP工具处理所有可以自动化的工作。
|
||||||
|
* **战略聚焦:** 将AI的“思考”集中在无法被工具替代的领域:战略决策、创新构想、复杂问题诊断 (`mcp.sequential_thinking`) 和最终质量把关。
|
||||||
|
|
||||||
|
* **无缝集成:** 期望AI能流畅地在不同MCP工具之间传递信息,形成一个高度整合的自动化工作流。
|
@ -192,7 +192,7 @@ public class VelocityUtils {
|
|||||||
} else if (template.contains("api.d.ts.vm")) {
|
} else if (template.contains("api.d.ts.vm")) {
|
||||||
fileName = StringUtils.format("{}/typings/api/{}.api.d.ts", soybeanPath, moduleName);
|
fileName = StringUtils.format("{}/typings/api/{}.api.d.ts", soybeanPath, moduleName);
|
||||||
} else if (template.contains("api.ts.vm")) {
|
} else if (template.contains("api.ts.vm")) {
|
||||||
fileName = StringUtils.format("{}/api/{}/{}.ts", soybeanPath, moduleName, StrUtil.toSymbolCase(businessName, '-'));
|
fileName = StringUtils.format("{}/service/api/{}/{}.ts", soybeanPath, moduleName, StrUtil.toSymbolCase(businessName, '-'));
|
||||||
} else if (template.contains("search.vue.vm")) {
|
} else if (template.contains("search.vue.vm")) {
|
||||||
fileName = StringUtils.format("{}/views/{}/{}/modules/{}-search.vue", soybeanPath, moduleName, StrUtil.toSymbolCase(businessName, '-'), StrUtil.toSymbolCase(businessName, '-'));
|
fileName = StringUtils.format("{}/views/{}/{}/modules/{}-search.vue", soybeanPath, moduleName, StrUtil.toSymbolCase(businessName, '-'), StrUtil.toSymbolCase(businessName, '-'));
|
||||||
} else if (template.contains("operate-drawer.vue.vm")) {
|
} else if (template.contains("operate-drawer.vue.vm")) {
|
||||||
|
2
docs/template/index-tree.vue.vm
vendored
2
docs/template/index-tree.vue.vm
vendored
@ -2,7 +2,7 @@
|
|||||||
import { NDivider } from 'naive-ui';
|
import { NDivider } from 'naive-ui';
|
||||||
import { jsonClone } from '@sa/utils';
|
import { jsonClone } from '@sa/utils';
|
||||||
import { type TableDataWithIndex } from '@sa/hooks';
|
import { type TableDataWithIndex } from '@sa/hooks';
|
||||||
import { fetchBatchDelete${BusinessName}, fetchGet${BusinessName}List } from '@/service/api/${moduleName}/${businessName}';
|
import { fetchBatchDelete${BusinessName}, fetchGet${BusinessName}List } from '@/service/api/${moduleName}/${business__name}';
|
||||||
import { useAppStore } from '@/store/modules/app';
|
import { useAppStore } from '@/store/modules/app';
|
||||||
import { useAuth } from '@/hooks/business/auth';
|
import { useAuth } from '@/hooks/business/auth';
|
||||||
import { useTreeTable, useTreeTableOperate } from '@/hooks/common/tree-table';
|
import { useTreeTable, useTreeTableOperate } from '@/hooks/common/tree-table';
|
||||||
|
2
docs/template/index.vue.vm
vendored
2
docs/template/index.vue.vm
vendored
@ -1,6 +1,6 @@
|
|||||||
<script setup lang="tsx">
|
<script setup lang="tsx">
|
||||||
import { NDivider } from 'naive-ui';
|
import { NDivider } from 'naive-ui';
|
||||||
import { fetchBatchDelete${BusinessName}, fetchGet${BusinessName}List } from '@/service/api/${moduleName}/${businessName}';
|
import { fetchBatchDelete${BusinessName}, fetchGet${BusinessName}List } from '@/service/api/${moduleName}/${business__name}';
|
||||||
import { useAppStore } from '@/store/modules/app';
|
import { useAppStore } from '@/store/modules/app';
|
||||||
import { useAuth } from '@/hooks/business/auth';
|
import { useAuth } from '@/hooks/business/auth';
|
||||||
import { useDownload } from '@/hooks/business/download';
|
import { useDownload } from '@/hooks/business/download';
|
||||||
|
2
docs/template/modules/operate-drawer.vue.vm
vendored
2
docs/template/modules/operate-drawer.vue.vm
vendored
@ -52,7 +52,7 @@ function createDefaultModel(): Model {
|
|||||||
return {
|
return {
|
||||||
#foreach($column in $columns)
|
#foreach($column in $columns)
|
||||||
#if($column.insert)
|
#if($column.insert)
|
||||||
${column.javaField}:#if($column.javaType == 'String' && $!column.dictType && $column.dictType == '') ''#else undefined#end#if($foreach.hasNext),#end
|
${column.javaField}:#if($column.javaType == 'String' || ($!column.dictType && $column.dictType != '')) ''#else undefined#end#if($foreach.hasNext),#end
|
||||||
#end
|
#end
|
||||||
#end
|
#end
|
||||||
};
|
};
|
||||||
|
20
package.json
20
package.json
@ -27,8 +27,8 @@
|
|||||||
"UnoCSS"
|
"UnoCSS"
|
||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.20.0",
|
"node": ">=20.19.0",
|
||||||
"pnpm": ">=8.7.0"
|
"pnpm": ">=10.5.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "vite build --mode prod",
|
"build": "vite build --mode prod",
|
||||||
@ -80,18 +80,18 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@elegant-router/vue": "0.3.8",
|
"@elegant-router/vue": "0.3.8",
|
||||||
"@iconify/json": "2.2.352",
|
"@iconify/json": "2.2.353",
|
||||||
"@sa/scripts": "workspace:*",
|
"@sa/scripts": "workspace:*",
|
||||||
"@sa/uno-preset": "workspace:*",
|
"@sa/uno-preset": "workspace:*",
|
||||||
"@soybeanjs/eslint-config": "1.6.1",
|
"@soybeanjs/eslint-config": "1.7.0",
|
||||||
"@types/node": "24.0.4",
|
"@types/node": "24.0.4",
|
||||||
"@types/nprogress": "0.2.3",
|
"@types/nprogress": "0.2.3",
|
||||||
"@unocss/eslint-config": "66.2.3",
|
"@unocss/eslint-config": "66.3.2",
|
||||||
"@unocss/preset-icons": "66.2.3",
|
"@unocss/preset-icons": "66.3.2",
|
||||||
"@unocss/preset-uno": "66.2.3",
|
"@unocss/preset-uno": "66.3.2",
|
||||||
"@unocss/transformer-directives": "66.2.3",
|
"@unocss/transformer-directives": "66.3.2",
|
||||||
"@unocss/transformer-variant-group": "66.2.3",
|
"@unocss/transformer-variant-group": "66.3.2",
|
||||||
"@unocss/vite": "66.2.3",
|
"@unocss/vite": "66.3.2",
|
||||||
"@vitejs/plugin-vue": "6.0.0",
|
"@vitejs/plugin-vue": "6.0.0",
|
||||||
"@vitejs/plugin-vue-jsx": "5.0.0",
|
"@vitejs/plugin-vue-jsx": "5.0.0",
|
||||||
"consola": "3.4.2",
|
"consola": "3.4.2",
|
||||||
|
1251
pnpm-lock.yaml
1251
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
@ -20,6 +20,17 @@ export function fetchForceLogout(tokenId: string) {
|
|||||||
method: 'delete'
|
method: 'delete'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* 强退当前在线设备
|
||||||
|
*
|
||||||
|
* @param tokenId - 令牌ID
|
||||||
|
*/
|
||||||
|
export function fetchKickOutCurrentDevice(tokenId: string) {
|
||||||
|
return request<boolean>({
|
||||||
|
url: `/monitor/online/myself/${tokenId}`,
|
||||||
|
method: 'delete'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/** 获取在线设备列表 */
|
/** 获取在线设备列表 */
|
||||||
export function fetchGetOnlineDeviceList(params?: Api.Monitor.OnlineUserSearchParams) {
|
export function fetchGetOnlineDeviceList(params?: Api.Monitor.OnlineUserSearchParams) {
|
||||||
|
10
src/typings/api/system.api.d.ts
vendored
10
src/typings/api/system.api.d.ts
vendored
@ -373,7 +373,15 @@ declare namespace Api {
|
|||||||
type DictDataOperateParams = CommonType.RecordNullable<
|
type DictDataOperateParams = CommonType.RecordNullable<
|
||||||
Pick<
|
Pick<
|
||||||
Api.System.DictData,
|
Api.System.DictData,
|
||||||
'dictCode' | 'dictSort' | 'dictLabel' | 'dictValue' | 'dictType' | 'cssClass' | 'listClass' | 'remark'
|
| 'dictCode'
|
||||||
|
| 'dictSort'
|
||||||
|
| 'dictLabel'
|
||||||
|
| 'dictValue'
|
||||||
|
| 'dictType'
|
||||||
|
| 'cssClass'
|
||||||
|
| 'listClass'
|
||||||
|
| 'isDefault'
|
||||||
|
| 'remark'
|
||||||
>
|
>
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ const activeModule = computed(() => moduleMap[props.module || 'pwd-login']);
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="relative min-h-screen w-full flex flex-wrap">
|
<div class="relative min-h-screen w-full flex flex-wrap">
|
||||||
<div class="hidden h-screen w-50% bg-primary-100 lg:block dark:bg-primary-800">
|
<div class="hidden min-h-screen w-50% bg-primary-100 lg:block dark:bg-primary-800">
|
||||||
<div class="size-full flex-center">
|
<div class="size-full flex-center">
|
||||||
<img class="w-60% sm:w-80%" :src="loginBackground" />
|
<img class="w-60% sm:w-80%" :src="loginBackground" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
<script setup lang="tsx">
|
<script setup lang="tsx">
|
||||||
import { NTime } from 'naive-ui';
|
import { NTime } from 'naive-ui';
|
||||||
import { useLoading } from '@sa/hooks';
|
import { useLoading } from '@sa/hooks';
|
||||||
import { fetchForceLogout, fetchGetOnlineDeviceList } from '@/service/api/monitor';
|
import { fetchGetOnlineDeviceList, fetchKickOutCurrentDevice } from '@/service/api/monitor';
|
||||||
import { useAppStore } from '@/store/modules/app';
|
import { useAppStore } from '@/store/modules/app';
|
||||||
import { useTable } from '@/hooks/common/table';
|
import { useTable } from '@/hooks/common/table';
|
||||||
|
import { useDict } from '@/hooks/business/dict';
|
||||||
import { getBrowserIcon, getOsIcon } from '@/utils/icon-tag-format';
|
import { getBrowserIcon, getOsIcon } from '@/utils/icon-tag-format';
|
||||||
import { $t } from '@/locales';
|
import { $t } from '@/locales';
|
||||||
|
import DictTag from '@/components/custom/dict-tag.vue';
|
||||||
import ButtonIcon from '@/components/custom/button-icon.vue';
|
import ButtonIcon from '@/components/custom/button-icon.vue';
|
||||||
import SvgIcon from '@/components/custom/svg-icon.vue';
|
import SvgIcon from '@/components/custom/svg-icon.vue';
|
||||||
|
|
||||||
@ -13,13 +15,23 @@ defineOptions({
|
|||||||
name: 'OnlineTable'
|
name: 'OnlineTable'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useDict('sys_device_type');
|
||||||
|
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
const { loading: btnLoading, startLoading: startBtnLoading, endLoading: endBtnLoading } = useLoading(false);
|
const { loading: btnLoading, startLoading: startBtnLoading, endLoading: endBtnLoading } = useLoading(false);
|
||||||
|
|
||||||
const { columns, data, loading, getData } = useTable({
|
const { columns, data, loading, getData } = useTable({
|
||||||
apiFn: fetchGetOnlineDeviceList,
|
apiFn: fetchGetOnlineDeviceList,
|
||||||
columns: () => [
|
columns: () => [
|
||||||
{ title: '用户名', key: 'userName', align: 'center', minWidth: 120 },
|
{
|
||||||
|
title: '设备类型',
|
||||||
|
key: 'deviceType',
|
||||||
|
align: 'center',
|
||||||
|
minWidth: 120,
|
||||||
|
render: row => {
|
||||||
|
return <DictTag size="small" value={row.deviceType} dict-code="sys_device_type" />;
|
||||||
|
}
|
||||||
|
},
|
||||||
{ title: 'IP地址', key: 'ipaddr', align: 'center', minWidth: 120 },
|
{ title: 'IP地址', key: 'ipaddr', align: 'center', minWidth: 120 },
|
||||||
{ title: '登录地点', key: 'loginLocation', align: 'center', minWidth: 120 },
|
{ title: '登录地点', key: 'loginLocation', align: 'center', minWidth: 120 },
|
||||||
{
|
{
|
||||||
@ -86,7 +98,7 @@ const { columns, data, loading, getData } = useTable({
|
|||||||
/** 强制下线 */
|
/** 强制下线 */
|
||||||
async function forceLogout(tokenId: string) {
|
async function forceLogout(tokenId: string) {
|
||||||
startBtnLoading();
|
startBtnLoading();
|
||||||
const { error } = await fetchForceLogout(tokenId);
|
const { error } = await fetchKickOutCurrentDevice(tokenId);
|
||||||
if (!error) {
|
if (!error) {
|
||||||
window.$message?.success('强制下线成功');
|
window.$message?.success('强制下线成功');
|
||||||
await getData();
|
await getData();
|
||||||
|
@ -3,12 +3,13 @@ import { computed, reactive, watch } from 'vue';
|
|||||||
import { NTag } from 'naive-ui';
|
import { NTag } from 'naive-ui';
|
||||||
import { fetchCreateDictData, fetchUpdateDictData } from '@/service/api/system/dict-data';
|
import { fetchCreateDictData, fetchUpdateDictData } from '@/service/api/system/dict-data';
|
||||||
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
|
import { useFormRules, useNaiveForm } from '@/hooks/common/form';
|
||||||
|
import { useDict } from '@/hooks/business/dict';
|
||||||
import { $t } from '@/locales';
|
import { $t } from '@/locales';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: 'DictDataOperateDrawer'
|
name: 'DictDataOperateDrawer'
|
||||||
});
|
});
|
||||||
|
useDict('sys_yes_no');
|
||||||
interface Props {
|
interface Props {
|
||||||
/** the type of operation */
|
/** the type of operation */
|
||||||
operateType: NaiveUI.TableOperateType;
|
operateType: NaiveUI.TableOperateType;
|
||||||
@ -63,7 +64,8 @@ function createDefaultModel(): Model {
|
|||||||
dictType: props.dictType,
|
dictType: props.dictType,
|
||||||
cssClass: '',
|
cssClass: '',
|
||||||
listClass: null,
|
listClass: null,
|
||||||
remark: ''
|
remark: '',
|
||||||
|
isDefault: 'N'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,7 +97,7 @@ async function handleSubmit() {
|
|||||||
|
|
||||||
// request
|
// request
|
||||||
if (props.operateType === 'add') {
|
if (props.operateType === 'add') {
|
||||||
const { dictSort, dictLabel, dictValue, dictType, cssClass, listClass, remark } = model;
|
const { dictSort, dictLabel, dictValue, dictType, cssClass, listClass, isDefault, remark } = model;
|
||||||
const { error } = await fetchCreateDictData({
|
const { error } = await fetchCreateDictData({
|
||||||
dictSort,
|
dictSort,
|
||||||
dictLabel,
|
dictLabel,
|
||||||
@ -103,13 +105,14 @@ async function handleSubmit() {
|
|||||||
dictType,
|
dictType,
|
||||||
cssClass,
|
cssClass,
|
||||||
listClass,
|
listClass,
|
||||||
|
isDefault,
|
||||||
remark
|
remark
|
||||||
});
|
});
|
||||||
if (error) return;
|
if (error) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.operateType === 'edit') {
|
if (props.operateType === 'edit') {
|
||||||
const { dictCode, dictSort, dictLabel, dictValue, dictType, cssClass, listClass, remark } = model;
|
const { dictCode, dictSort, dictLabel, dictValue, dictType, cssClass, listClass, isDefault, remark } = model;
|
||||||
const { error } = await fetchUpdateDictData({
|
const { error } = await fetchUpdateDictData({
|
||||||
dictCode,
|
dictCode,
|
||||||
dictSort,
|
dictSort,
|
||||||
@ -118,6 +121,7 @@ async function handleSubmit() {
|
|||||||
dictType,
|
dictType,
|
||||||
cssClass,
|
cssClass,
|
||||||
listClass,
|
listClass,
|
||||||
|
isDefault,
|
||||||
remark
|
remark
|
||||||
});
|
});
|
||||||
if (error) return;
|
if (error) return;
|
||||||
@ -179,6 +183,9 @@ function renderTagLabel(option: { label: string; value: string }) {
|
|||||||
<NFormItem :label="$t('page.system.dict.data.dictSort')" path="dictSort">
|
<NFormItem :label="$t('page.system.dict.data.dictSort')" path="dictSort">
|
||||||
<NInputNumber v-model:value="model.dictSort" :placeholder="$t('page.system.dict.form.dictSort.required')" />
|
<NInputNumber v-model:value="model.dictSort" :placeholder="$t('page.system.dict.form.dictSort.required')" />
|
||||||
</NFormItem>
|
</NFormItem>
|
||||||
|
<NFormItem :label="$t('page.system.dict.data.isDefault')" path="isDefault">
|
||||||
|
<DictRadio v-model:value="model.isDefault" dict-code="sys_yes_no" />
|
||||||
|
</NFormItem>
|
||||||
<NFormItem :label="$t('page.system.dict.data.remark')" path="remark">
|
<NFormItem :label="$t('page.system.dict.data.remark')" path="remark">
|
||||||
<NInput
|
<NInput
|
||||||
v-model:value="model.remark"
|
v-model:value="model.remark"
|
||||||
|
@ -56,7 +56,7 @@ function createDefaultModel(): Model {
|
|||||||
nickName: '',
|
nickName: '',
|
||||||
email: '',
|
email: '',
|
||||||
phonenumber: '',
|
phonenumber: '',
|
||||||
sex: '',
|
sex: '0',
|
||||||
password: '',
|
password: '',
|
||||||
status: '0',
|
status: '0',
|
||||||
roleIds: [],
|
roleIds: [],
|
||||||
|
Loading…
Reference in New Issue
Block a user