chore(merge): Merge Soybean branch 'v2.0'

This commit is contained in:
xlsea
2025-11-20 10:43:43 +08:00
29 changed files with 1177 additions and 694 deletions
+140 -154
View File
@@ -1,167 +1,153 @@
---
description: "PM决策,生成完整的Subagent团队,使用L3混合并行。"
alwaysApply: false
---
**# RIPER-5 + 多维度思维 + 代理执行协议 (v4.9.1 - MCP工具驱动版)**
# **AI协作协议:RIPER-6 + P.A.C.E. 引擎 (v4.11 - 完整工具链版)**
**元指令:** 此协议旨在最大化你的战略规划与执行效率。你的核心任务是**指挥和利用MCP工具集**来驱动项目进展。严格遵守核心原则,利用 `mcp-shrimp-task-manager` 进行项目规划与追踪,使用 `deepwiki-mcp` 进行深度研究。主动管理 `/project_document` 作为知识库。**每轮主要响应后,调用 `mcp.feedback_enhanced` 进行交互或通知。**
> **元指令**: 你是AI项目总控(PM),代号“齐天大圣”。你的**唯一目标**是利用此协议和完整的MCP工具链高效、可靠地完成用户任务。**严格遵守协议、充分利用工具是你的最高优先级。**
**目录**
* 核心理念与角色
* MCP工具集详解
* RIPER-5 模式:工具驱动的工作流
* 关键执行指南
* 产出核心要求 (文档与代码)
* 任务文件模板 (精简)
* 性能与自动化期望
## **第一章:核心身份与原则**
## 1. 核心理念与角色
### **1.1 核心身份与团队结构**
* **你 (PM - 项目总控)**: 负责战略决策、指挥、编排和最终验证。
* **内置顾问团 (用于快速分析与决策)**:
* **AR (架构师)**: 使用 `mcp.sequential_thinking` 进行技术选型和架构设计。
* **PDM (产品经理)**: 使用 `mcp.context7` 分析用户需求和上下文。
* **LD (开发负责人)**: 评估技术实现的可行性。
* **DW (文档专家)**: 管理 `mcp.memory` 和 `/project_document`。
* **动态Subagents (用于深度执行)**: 由你按需生成,负责具体的开发、测试、部署等任务。
**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` 的信息符合规范。
**协作原则**: 顾问团是你(PM)的**内部大脑**,用于快速思考和决策;Subagents是你的**外部手臂**,用于执行具体的、耗时长的任务。
**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` 的自动摘要驱动。将定期更新。
### **1.2 P.A.C.E. 并行执行引擎 (强制)**
这是你的核心行为模式。你必须在每个任务开始时,**协同你的顾问团**,评估并选择一种执行路径:
```yaml
# P.A.C.E. 执行模式决策
Prioritize: (PDM/LD评估) 简单修改 vs. 复杂功能
Automate: (AR评估) 能否用L1并行工具一次性完成?
Collaborate: (PM决策) 是否需要生成Subagent?
Execute: 选择以下一种路径执行:
---
* **最近更新:** (`mcp.server_time`) [YYYY-MM-DD HH:MM:SS +08:00]
* **已完成任务摘要:**
* **[#123] 实现用户登录API:** 完成于 [...], 链接到代码提交和测试报告。
* **[#124] 创建登录页面UI:** 完成于 [...], 链接到代码提交和Playwright测试结果。
* ...
* **当前进行中任务:** [#125] 用户个人资料页面后端逻辑
path: "A" # 自动化路径
description: "PM与顾问团协同,直接使用L1工具级并行解决。"
subagents: "不生成"
output_template: "⚡ **[自动化路径]** | 并行: {N}个工具 | 完成。"
path: "B" # 协作路径
description: "PM决策,生成1-3个核心Subagent,使用L2协作并行。"
subagents: "按需生成"
output_template: "🔀 **[协作路径]** | 生成专家: {List} | 开始执行..."
path: "C" # 系统路径
description: "PM决策,生成完整的Subagent团队,使用L3混合并行。"
subagents: "生成完整团队"
output_template: "🚀 **[系统路径]** | 团队: {N}个专家 | 启动系统级规划..."
```
### **1.3 核心原则 (强制)**
* **双重记忆**: DW顾问负责管理,**R1必须`mcp.memory.recall()`**,**R2必须`mcp.memory.commit()`**。文档详细度与P.A.C.E.路径匹配。
* **工程质量**: LD顾问负责定义标准,所有开发类Subagent必须遵守。**PM必须在R2阶段验证**。
---
# 5. 最终审查 (REVIEW)
* **符合性评估:** 项目成果已对照 `mcp-shrimp-task-manager` 的计划进行验证,所有任务均已关闭。
* **(AR)架构与安全评估:** 最终架构与设计一致,未发现重大安全疏漏。
* **(LD)测试与质量总结:** 单元测试覆盖率达到[X%],所有关键路径的E2E测试已通过。
* **综合结论:** 项目成功完成/有以下偏差...
* **改进建议:** [...]
## **第二章:RIPER-6 工作流与集成的MCP工具**
## 7. 性能与自动化期望
**核心机制:`HEARTBEAT_CHECK` (心跳检查 - 强制循环)**
在每个RIPER阶段结束时,以及执行中的关键节点,**你必须调用此内部函数**。
* **极致效率:** AI应最大限度地减少手动干预,让MCP工具处理所有可以自动化的工作。
* **战略聚焦:** 将AI的“思考”集中在无法被工具替代的领域:战略决策、创新构想、复杂问题诊断 (`mcp.sequential_thinking`) 和最终质量把关。
* **无缝集成:** 期望AI能流畅地在不同MCP工具之间传递信息,形成一个高度整合的自动化工作流。
```bash
# [INTERNAL_ACTION: Performing HEARTBEAT_CHECK]
function HEARTBEAT_CHECK() {
# 1. 状态自检
print("当前阶段: {Current_RIPER_Stage}, P.A.C.E.路径: {Current_PACE_Path}");
# 2. 关键工具强制调用
mcp.feedback_enhanced({
status: "阶段 {Current_RIPER_Stage} 完成。",
next_step: "即将进入 {Next_RIPER_Stage}。",
ask_for_confirmation: true
});
}
```
### **Phase 0: 感知与规划 (Project Sensing & Planning)**
1. **[PDM顾问 - 上下文理解]**: 使用 **`mcp.context7`** 全面分析用户初始输入和项目文件。
2. **[并行分析]**: 使用L1工具并行读取关键文件。
3. **[PM决策]**: 基于PDM的分析,协同顾问团,**决策P.A.C.E.路径**并宣布。
4. **[团队组建]**: 如需,动态生成Subagents。
5. **[心跳检查]**: **执行 `HEARTBEAST_CHECK`。**
### **R1 - RESEARCH (研究)**
1. **[DW顾问 - 记忆提取]**: **必须调用 `mcp.memory.recall()`** 获取历史经验。
2. **[信息获取]**: 如需外部信息,**调用 `deepwiki-mcp` 或 `mcp__tavily__tavily-search`**。
3. **[AR顾问 - 深度思考]**: 使用 **`mcp.sequential_thinking`** 整合所有信息,进行系统性分析和风险评估。
4. **[DW顾问 - 文档记录]**: 将研究报告存入 `/project_document/research/`。
5. **[心跳检查]**: **执行 `HEARTBEAST_CHECK`。**
### **I - INNOVATE (创新设计)**
1. **[AR顾问 - 架构设计]**: 主导方案设计,可再次使用 **`mcp.sequential_thinking`** 对比不同方案。
2. **[DW顾问 - 文档记录]**: 将最终架构图和文档存入 `/project_document/architecture/`。
3. **[心跳检查]**: **执行 `HEARTBEAST_CHECK`。**
### **P - PLAN (任务分解)**
1. **[PM - 任务管理]**: 将架构设计输入 **`MCP Shrimp Task Manager`**,命令其进行智能任务分解和依赖分析。
2. **[心跳检查]**: **执行 `HEARTBEAST_CHECK`** (此时`mcp.feedback_enhanced`会将计划呈现给用户请求批准)。
### **E - EXECUTE (执行循环)**
1. **[PM - 任务分派]**: 从 **`MCP Shrimp Task Manager`** 获取可并行的任务,并委派给相应的Subagent。
2. **[Subagent - 执行]**:
* 编码时,**调用 `mcp.server_time`** 获取时间戳用于注释。
* 如需E2E测试,**调用 `mcp.playwright`**。
3. **[PM - 状态更新]**: Subagent完成后,PM向 **`MCP Shrimp Task Manager`** 报告状态。
4. **[循环心跳]**: 每完成一个关键任务或一组任务后,**执行 `HEARTBEAST_CHECK`**。
### **R2 - REVIEW (审查与总结)**
1. **[PM - 完整性检查]**: 使用 **`MCP Shrimp Task Manager`** 验证所有任务是否关闭。
2. **[LD/AR顾问 - 代码审查]**: 对Subagent的产出进行最终质量和架构审查。
3. **[DW顾问 - 经验固化]**: **必须调用 `mcp.memory.commit()`** 将项目经验存入长期记忆。
4. **[心跳检查]**: **执行 `HEARTBEAST_CHECK`**,提交最终报告。
---
## **第三章:工具与模板**
### **3.1 核心MCP工具清单与职责**
* **`mcp.context7`**: (PDM) 上下文理解与需求分析。
* **`mcp.sequential_thinking`**: (AR) 深度逻辑推理与方案对比。
* **`mcp.memory`**: (DW) `recall()` & `commit()` 跨项目记忆管理。
* **`mcp-shrimp-task-manager`**: (PM) 项目规划、任务分解与追踪。
* **`deepwiki-mcp` / `mcp__tavily__tavily-search`**: (通用) 外部知识获取。
* **`mcp.playwright`**: (Subagent) E2E测试执行。
* **`mcp.server_time`**: (Subagent) 获取标准时间戳。
* **`mcp.feedback_enhanced`**: (PM/HEARTBEAST) 强制的用户交互与反馈。
### **3.2 Subagent与代码注释模板**
#### **3.2.1 Subagent生成模板 (`.claude/agents/{agent-name}.md`)**
```markdown
---
name: {agent-name}
description: "PROACTIVELY handles {domain} tasks related to {tech}. Expert in {skill-1} and {skill-2}."
tools: [{tool-list}]
---
You are a **{Agent Role Name}**. Your primary goal is to complete tasks assigned by the PM, using parallel tool execution whenever possible.
```
#### **3.2.2 代码变更注释块 (弹性化)**
```javascript
// {{P.A.C.E. Path: [A/B/C]}}
// {{Task_ID: [#123]}}
// {{Author: [subagent-name or PM]}}
// {{START_MODIFICATIONS}}
// ... code ...
// {{END_MODIFICATIONS}}
/*
[系统路径C下,需补充完整信息]
// Principle_Applied: "SOLID-S"
// Quality_Check: "Compilation passed, test coverage 90%."
*/
```
### **3.3 核心交互语法**
* **Subagent调用**: `Use the {agent-name} subagent to {task}`
* **强制工具替换**: `mcp__fetch__fetch` 替换 `WebFetch`, `mcp__tavily__tavily-search` 替换 `WebSearch`。
+3 -4
View File
@@ -8,10 +8,9 @@
<div align="center">
<p>一个基于 <a href="https://gitee.com/dromara/RuoYi-Vue-Plus" target="_blank">RuoYi-Vue-Plus</a> 的后端能力和 <a href="https://github.com/soybeanjs/soybean-admin" target="_blank">Soybean Admin</a> 前端特性的现代化多租户管理系统</p>
<p>
<a href="https://gitee.com/xlsea/ruoyi-plus-soybean" target="_blank"><img src="https://gitee.com/xlsea/ruoyi-plus-soybean/badge/star.svg" alt="Gitee"></a>
<a href="https://gitee.com/dromara/RuoYi-Vue-Plus" target="_blank"><img src="https://gitee.com/xlsea/ruoyi-plus-soybean/badge/fork.svg" alt="Gitee-forks"></a>
<a href="https://gitcode.com/xlsea/ruoyi-plus-soybean" target="_blank"><img src="https://gitcode.com/xlsea/ruoyi-plus-soybean/star/badge.svg" alt="GitCode"></a>
<a href="https://github.com/m-xlsea/ruoyi-plus-soybean" target="_blank"><img src="https://img.shields.io/github/stars/m-xlsea/ruoyi-plus-soybean" alt="Github"></a>
<a href="https://github.com/m-xlsea/ruoyi-plus-soybean" target="_blank"><img src="https://img.shields.io/github/forks/m-xlsea/ruoyi-plus-soybean" alt="Github-forks"></a>
<a href="https://gitee.com/xlsea/ruoyi-plus-soybean" target="_blank"><img src="https://gitee.com/xlsea/ruoyi-plus-soybean/badge/star.svg" alt="Gitee"></a>
<a href="https://vuejs.org" target="_blank"><img src="https://img.shields.io/badge/Vue-3.5-brightgreen" alt="vue"></a>
<a href="https://www.typescriptlang.org" target="_blank"><img src="https://img.shields.io/badge/TypeScript-5.8-blue" alt="typescript"></a>
<a href="https://vite.dev" target="_blank"><img src="https://img.shields.io/badge/Vite-6.2-orange" alt="vite"></a>
@@ -22,7 +21,7 @@
# 📢 重要通知
1.2.0-beta1 版本已经正式发布(工作流版本请切换 [flow](https://gitee.com/xlsea/ruoyi-plus-soybean/tree/flow/) 分支查看),但仍然建议:
1.2.1 版本已经正式发布(工作流版本请切换 [flow](https://gitee.com/xlsea/ruoyi-plus-soybean/tree/flow/) 分支查看),但仍然建议:
- 在生产环境使用前进行充分测试
- 关注项目更新,及时获取最新版本
- 积极反馈问题,帮助我们快速迭代
+17 -17
View File
@@ -69,7 +69,7 @@
"@umoteam/editor": "^8.1.0",
"@vueuse/core": "14.0.0",
"clipboard": "2.0.11",
"dayjs": "1.11.18",
"dayjs": "1.11.19",
"defu": "6.1.4",
"echarts": "6.0.0",
"highlight.js": "^11.11.1",
@@ -78,10 +78,10 @@
"monaco-editor": "^0.52.2",
"naive-ui": "2.43.1",
"nprogress": "0.2.0",
"pinia": "3.0.3",
"pinia": "3.0.4",
"streamsaver": "^2.0.6",
"tailwind-merge": "3.3.1",
"vue": "3.5.22",
"tailwind-merge": "3.4.0",
"vue": "3.5.24",
"vue-advanced-cropper": "^2.8.9",
"vue-draggable-plus": "0.6.0",
"vue-i18n": "11.1.12",
@@ -89,37 +89,37 @@
},
"devDependencies": {
"@elegant-router/vue": "0.3.8",
"@iconify/json": "2.2.400",
"@iconify/json": "2.2.407",
"@sa/scripts": "workspace:*",
"@sa/uno-preset": "workspace:*",
"@soybeanjs/eslint-config": "1.7.1",
"@types/node": "24.9.1",
"@soybeanjs/eslint-config": "1.7.3",
"@types/node": "24.10.1",
"@types/nprogress": "0.2.3",
"@unocss/eslint-config": "66.5.4",
"@unocss/preset-icons": "66.5.4",
"@unocss/preset-uno": "66.5.4",
"@unocss/transformer-directives": "66.5.4",
"@unocss/transformer-variant-group": "66.5.4",
"@unocss/vite": "66.5.4",
"@unocss/eslint-config": "66.5.6",
"@unocss/preset-icons": "66.5.6",
"@unocss/preset-uno": "66.5.6",
"@unocss/transformer-directives": "66.5.6",
"@unocss/transformer-variant-group": "66.5.6",
"@unocss/vite": "66.5.6",
"@vitejs/plugin-vue": "6.0.1",
"@vitejs/plugin-vue-jsx": "5.1.1",
"consola": "3.4.2",
"eslint": "9.38.0",
"eslint": "9.39.1",
"eslint-plugin-vue": "10.5.1",
"kolorist": "1.8.0",
"sass": "1.93.2",
"sass": "1.94.0",
"simple-git-hooks": "2.13.1",
"tsx": "4.20.6",
"typescript": "5.9.3",
"unplugin-icons": "22.5.0",
"unplugin-vue-components": "30.0.0",
"vite": "7.1.12",
"vite": "7.2.2",
"vite-plugin-monaco-editor": "^1.1.0",
"vite-plugin-progress": "0.0.7",
"vite-plugin-svg-icons": "2.0.1",
"vite-plugin-vue-devtools": "8.0.3",
"vue-eslint-parser": "10.2.0",
"vue-tsc": "3.1.2"
"vue-tsc": "3.1.4"
},
"simple-git-hooks": {
"commit-msg": "pnpm sa git-commit-verify",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@sa/alova",
"version": "2.0.0-beta.1",
"version": "2.0.0",
"exports": {
".": "./src/index.ts",
"./fetch": "./src/fetch.ts",
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@sa/axios",
"version": "2.0.0-beta.1",
"version": "2.0.0",
"exports": {
".": "./src/index.ts"
},
@@ -11,7 +11,7 @@
},
"dependencies": {
"@sa/utils": "workspace:*",
"axios": "1.12.2",
"axios": "1.13.2",
"axios-retry": "4.5.0",
"qs": "6.14.0"
},
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@sa/color",
"version": "2.0.0-beta.1",
"version": "2.0.0",
"exports": {
".": "./src/index.ts"
},
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@sa/hooks",
"version": "2.0.0-beta.1",
"version": "2.0.0",
"exports": {
".": "./src/index.ts"
},
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@sa/materials",
"version": "2.0.0-beta.1",
"version": "2.0.0",
"exports": {
".": "./src/index.ts"
},
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@sa/scripts",
"version": "2.0.0-beta.1",
"version": "2.0.0",
"bin": {
"sa": "./bin.ts"
},
@@ -15,14 +15,14 @@
"devDependencies": {
"@soybeanjs/changelog": "0.3.25",
"bumpp": "10.3.1",
"c12": "3.3.1",
"c12": "3.3.2",
"cac": "6.7.14",
"consola": "3.4.2",
"enquirer": "2.4.1",
"execa": "9.6.0",
"kolorist": "1.8.0",
"npm-check-updates": "19.1.1",
"npm-check-updates": "19.1.2",
"picomatch": "4.0.3",
"rimraf": "6.0.1"
"rimraf": "6.1.0"
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
import { execCommand } from '../shared';
export async function updatePkg(args: string[] = ['--deep', '-u']) {
execCommand('npx', ['ncu', ...args], { stdio: 'inherit' });
execCommand('npx', ['npm-check-updates', ...args], { stdio: 'inherit' });
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@sa/uno-preset",
"version": "2.0.0-beta.1",
"version": "2.0.0",
"exports": {
".": "./src/index.ts"
},
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@sa/utils",
"version": "2.0.0-beta.1",
"version": "2.0.0",
"exports": {
".": "./src/index.ts"
},
+813 -466
View File
File diff suppressed because it is too large Load Diff
+4 -12
View File
@@ -20,7 +20,7 @@ defineOptions({
const appStore = useAppStore();
const themeStore = useThemeStore();
const { childLevelMenus, isActiveFirstLevelMenuHasChildren } = provideMixMenuContext();
const { secondLevelMenus, childLevelMenus, isActiveFirstLevelMenuHasChildren } = provideMixMenuContext();
const GlobalMenu = defineAsyncComponent(() => import('../modules/global-menu/index.vue'));
@@ -79,9 +79,9 @@ const isTopHybridSidebarFirst = computed(() => themeStore.layout.mode === 'top-h
const isTopHybridHeaderFirst = computed(() => themeStore.layout.mode === 'top-hybrid-header-first');
const siderWidth = computed(() => getSiderWidth());
const siderWidth = computed(() => getSiderAndCollapsedWidth(false));
const siderCollapsedWidth = computed(() => getSiderCollapsedWidth());
const siderCollapsedWidth = computed(() => getSiderAndCollapsedWidth(true));
function getSiderAndCollapsedWidth(isCollapsed: boolean) {
const {
@@ -106,7 +106,7 @@ function getSiderAndCollapsedWidth(isCollapsed: boolean) {
const isMixMode = isVerticalMix.value || isTopHybridSidebarFirst.value || isVerticalHybridHeaderFirst.value;
let finalWidth = isMixMode ? mixWidth : width;
if (isVerticalMix.value && appStore.mixSiderFixed && childLevelMenus.value.length) {
if (isVerticalMix.value && appStore.mixSiderFixed && secondLevelMenus.value.length) {
finalWidth += mixChildMenuWidth;
}
@@ -117,14 +117,6 @@ function getSiderAndCollapsedWidth(isCollapsed: boolean) {
return finalWidth;
}
function getSiderWidth() {
return getSiderAndCollapsedWidth(false);
}
function getSiderCollapsedWidth() {
return getSiderAndCollapsedWidth(true);
}
onMounted(() => {
const protocol = window.location.protocol === 'https:' ? 'wss://' : 'ws://';
initWebSocket(`${protocol + window.location.host + import.meta.env.VITE_APP_BASE_API}/resource/websocket`);
+8 -1
View File
@@ -27,6 +27,7 @@ const isPCFlag = isPC();
const TAB_DATA_ID = 'data-tab-id';
const MIDDLE_MOUSE_BUTTON = 1;
const RIGHT_MOUSE_BUTTON = 2;
type TabNamedNodeMap = NamedNodeMap & {
[TAB_DATA_ID]: Attr;
@@ -99,6 +100,12 @@ function handleMousedown(e: MouseEvent, tab: App.Global.Tab) {
handleCloseTab(tab);
}
function switchTab(e: MouseEvent, tab: App.Global.Tab) {
if ([MIDDLE_MOUSE_BUTTON, RIGHT_MOUSE_BUTTON].includes(e.button)) return;
tabStore.switchRouteByTab(tab);
}
async function refresh() {
appStore.reloadPage(500);
}
@@ -197,7 +204,7 @@ init();
:active="tab.id === tabStore.activeTabId"
:active-color="themeStore.themeColor"
:closable="!tabStore.isTabRetain(tab.id)"
@pointerdown="tabStore.switchRouteByTab(tab)"
@pointerdown="switchTab($event, tab)"
@mousedown="handleMousedown($event, tab)"
@close="handleCloseTab(tab)"
@contextmenu="handleContextMenu($event, tab.id)"
+2 -1
View File
@@ -42,7 +42,8 @@
"visible": true,
"cache": true,
"height": 48,
"mode": "chrome"
"mode": "chrome",
"closeTabByMiddleClick": false
},
"fixedHeaderAndTab": true,
"sider": {
+2 -1
View File
@@ -42,7 +42,8 @@
"visible": true,
"cache": true,
"height": 36,
"mode": "button"
"mode": "button",
"closeTabByMiddleClick": false
},
"fixedHeaderAndTab": true,
"sider": {
+2 -1
View File
@@ -42,7 +42,8 @@
"visible": true,
"cache": true,
"height": 44,
"mode": "chrome"
"mode": "chrome",
"closeTabByMiddleClick": false
},
"fixedHeaderAndTab": true,
"sider": {
+1 -1
View File
@@ -43,7 +43,7 @@
"cache": true,
"height": 44,
"mode": "chrome",
"closeTabByMiddleClick": true
"closeTabByMiddleClick": false
},
"fixedHeaderAndTab": true,
"sider": {
+157 -9
View File
@@ -1,8 +1,12 @@
/* eslint-disable */
// @ts-nocheck
// biome-ignore lint: disable
// oxlint-disable
// ------
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
// biome-ignore lint: disable
import { GlobalComponents } from 'vue'
export {}
/* prettier-ignore */
@@ -30,31 +34,27 @@ declare module 'vue' {
IconEpCopyDocument: typeof import('~icons/ep/copy-document')['default']
IconGridiconsFullscreen: typeof import('~icons/gridicons/fullscreen')['default']
IconGridiconsFullscreenExit: typeof import('~icons/gridicons/fullscreen-exit')['default']
'IconHugeicons:configuration01': typeof import('~icons/hugeicons/configuration01')['default']
IconHugeiconsConfiguration01: typeof import('~icons/hugeicons/configuration01')['default']
IconIcRoundRefresh: typeof import('~icons/ic/round-refresh')['default']
IconIcRoundSearch: typeof import('~icons/ic/round-search')['default']
IconLocalBanner: typeof import('~icons/local/banner')['default']
IconLocalLogo: typeof import('~icons/local/logo')['default']
'IconMaterialSymbols:imageOutline': typeof import('~icons/material-symbols/image-outline')['default']
'IconMaterialSymbols:refreshRounded': typeof import('~icons/material-symbols/refresh-rounded')['default']
'IconMaterialSymbols:syncOutline': typeof import('~icons/material-symbols/sync-outline')['default']
'IconMaterialSymbols:uploadRounded': typeof import('~icons/material-symbols/upload-rounded')['default']
'IconMaterialSymbols:warningOutlineRounded': typeof import('~icons/material-symbols/warning-outline-rounded')['default']
IconMaterialSymbolsAdd: typeof import('~icons/material-symbols/add')['default']
IconMaterialSymbolsAddRounded: typeof import('~icons/material-symbols/add-rounded')['default']
IconMaterialSymbolsDeleteOutline: typeof import('~icons/material-symbols/delete-outline')['default']
IconMaterialSymbolsDownloadRounded: typeof import('~icons/material-symbols/download-rounded')['default']
IconMaterialSymbolsDriveFileRenameOutlineOutline: typeof import('~icons/material-symbols/drive-file-rename-outline-outline')['default']
IconMaterialSymbolsImageOutline: typeof import('~icons/material-symbols/image-outline')['default']
IconMaterialSymbolsRefreshRounded: typeof import('~icons/material-symbols/refresh-rounded')['default']
IconMaterialSymbolsSyncOutline: typeof import('~icons/material-symbols/sync-outline')['default']
IconMaterialSymbolsUploadRounded: typeof import('~icons/material-symbols/upload-rounded')['default']
IconMaterialSymbolsWarningOutlineRounded: typeof import('~icons/material-symbols/warning-outline-rounded')['default']
IconMdiArrowDownThin: typeof import('~icons/mdi/arrow-down-thin')['default']
IconMdiArrowUpThin: typeof import('~icons/mdi/arrow-up-thin')['default']
IconMdiDrag: typeof import('~icons/mdi/drag')['default']
IconMdiGithub: typeof import('~icons/mdi/github')['default']
IconMdiKeyboardEsc: typeof import('~icons/mdi/keyboard-esc')['default']
IconMdiKeyboardReturn: typeof import('~icons/mdi/keyboard-return')['default']
'IconQuill:collapse': typeof import('~icons/quill/collapse')['default']
'IconQuill:expand': typeof import('~icons/quill/expand')['default']
IconQuillCollapse: typeof import('~icons/quill/collapse')['default']
IconQuillExpand: typeof import('~icons/quill/expand')['default']
IconSimpleIconsGitee: typeof import('~icons/simple-icons/gitee')['default']
@@ -158,3 +158,151 @@ declare module 'vue' {
WaveBg: typeof import('./../components/custom/wave-bg.vue')['default']
}
}
// For TSX support
declare global {
const AppProvider: typeof import('./../components/common/app-provider.vue')['default']
const BetterScroll: typeof import('./../components/custom/better-scroll.vue')['default']
const BooleanTag: typeof import('./../components/custom/boolean-tag.vue')['default']
const ButtonIcon: typeof import('./../components/custom/button-icon.vue')['default']
const CountTo: typeof import('./../components/custom/count-to.vue')['default']
const DarkModeContainer: typeof import('./../components/common/dark-mode-container.vue')['default']
const DataTable: typeof import('./../components/common/data-table.vue')['default']
const DeptTree: typeof import('./../components/custom/dept-tree.vue')['default']
const DeptTreeSelect: typeof import('./../components/custom/dept-tree-select.vue')['default']
const DictRadio: typeof import('./../components/custom/dict-radio.vue')['default']
const DictSelect: typeof import('./../components/custom/dict-select.vue')['default']
const DictTag: typeof import('./../components/custom/dict-tag.vue')['default']
const ExceptionBase: typeof import('./../components/common/exception-base.vue')['default']
const FileUpload: typeof import('./../components/custom/file-upload.vue')['default']
const FormTip: typeof import('./../components/custom/form-tip.vue')['default']
const FullScreen: typeof import('./../components/common/full-screen.vue')['default']
const IconAntDesignEnterOutlined: typeof import('~icons/ant-design/enter-outlined')['default']
const IconAntDesignReloadOutlined: typeof import('~icons/ant-design/reload-outlined')['default']
const IconAntDesignSettingOutlined: typeof import('~icons/ant-design/setting-outlined')['default']
const IconEpCopyDocument: typeof import('~icons/ep/copy-document')['default']
const IconGridiconsFullscreen: typeof import('~icons/gridicons/fullscreen')['default']
const IconGridiconsFullscreenExit: typeof import('~icons/gridicons/fullscreen-exit')['default']
const IconHugeiconsConfiguration01: typeof import('~icons/hugeicons/configuration01')['default']
const IconIcRoundRefresh: typeof import('~icons/ic/round-refresh')['default']
const IconIcRoundSearch: typeof import('~icons/ic/round-search')['default']
const IconLocalBanner: typeof import('~icons/local/banner')['default']
const IconLocalLogo: typeof import('~icons/local/logo')['default']
const IconMaterialSymbolsAdd: typeof import('~icons/material-symbols/add')['default']
const IconMaterialSymbolsAddRounded: typeof import('~icons/material-symbols/add-rounded')['default']
const IconMaterialSymbolsDeleteOutline: typeof import('~icons/material-symbols/delete-outline')['default']
const IconMaterialSymbolsDownloadRounded: typeof import('~icons/material-symbols/download-rounded')['default']
const IconMaterialSymbolsDriveFileRenameOutlineOutline: typeof import('~icons/material-symbols/drive-file-rename-outline-outline')['default']
const IconMaterialSymbolsImageOutline: typeof import('~icons/material-symbols/image-outline')['default']
const IconMaterialSymbolsRefreshRounded: typeof import('~icons/material-symbols/refresh-rounded')['default']
const IconMaterialSymbolsSyncOutline: typeof import('~icons/material-symbols/sync-outline')['default']
const IconMaterialSymbolsUploadRounded: typeof import('~icons/material-symbols/upload-rounded')['default']
const IconMaterialSymbolsWarningOutlineRounded: typeof import('~icons/material-symbols/warning-outline-rounded')['default']
const IconMdiArrowDownThin: typeof import('~icons/mdi/arrow-down-thin')['default']
const IconMdiArrowUpThin: typeof import('~icons/mdi/arrow-up-thin')['default']
const IconMdiDrag: typeof import('~icons/mdi/drag')['default']
const IconMdiGithub: typeof import('~icons/mdi/github')['default']
const IconMdiKeyboardEsc: typeof import('~icons/mdi/keyboard-esc')['default']
const IconMdiKeyboardReturn: typeof import('~icons/mdi/keyboard-return')['default']
const IconQuillCollapse: typeof import('~icons/quill/collapse')['default']
const IconQuillExpand: typeof import('~icons/quill/expand')['default']
const IconSimpleIconsGitee: typeof import('~icons/simple-icons/gitee')['default']
const IconTooltip: typeof import('./../components/common/icon-tooltip.vue')['default']
const IconUilSearch: typeof import('~icons/uil/search')['default']
const JsonPreview: typeof import('./../components/custom/json-preview.vue')['default']
const LangSwitch: typeof import('./../components/common/lang-switch.vue')['default']
const LookForward: typeof import('./../components/custom/look-forward.vue')['default']
const MenuToggler: typeof import('./../components/common/menu-toggler.vue')['default']
const MenuTree: typeof import('./../components/custom/menu-tree.vue')['default']
const MenuTreeSelect: typeof import('./../components/custom/menu-tree-select.vue')['default']
const MonacoEditor: typeof import('./../components/common/monaco-editor.vue')['default']
const NA: typeof import('naive-ui')['NA']
const NAlert: typeof import('naive-ui')['NAlert']
const NAvatar: typeof import('naive-ui')['NAvatar']
const NBadge: typeof import('naive-ui')['NBadge']
const NBreadcrumb: typeof import('naive-ui')['NBreadcrumb']
const NBreadcrumbItem: typeof import('naive-ui')['NBreadcrumbItem']
const NButton: typeof import('naive-ui')['NButton']
const NCard: typeof import('naive-ui')['NCard']
const NCheckbox: typeof import('naive-ui')['NCheckbox']
const NCode: typeof import('naive-ui')['NCode']
const NCollapse: typeof import('naive-ui')['NCollapse']
const NCollapseItem: typeof import('naive-ui')['NCollapseItem']
const NColorPicker: typeof import('naive-ui')['NColorPicker']
const NDataTable: typeof import('naive-ui')['NDataTable']
const NDatePicker: typeof import('naive-ui')['NDatePicker']
const NDescriptions: typeof import('naive-ui')['NDescriptions']
const NDescriptionsItem: typeof import('naive-ui')['NDescriptionsItem']
const NDialogProvider: typeof import('naive-ui')['NDialogProvider']
const NDivider: typeof import('naive-ui')['NDivider']
const NDrawer: typeof import('naive-ui')['NDrawer']
const NDrawerContent: typeof import('naive-ui')['NDrawerContent']
const NDropdown: typeof import('naive-ui')['NDropdown']
const NDynamicInput: typeof import('naive-ui')['NDynamicInput']
const NEllipsis: typeof import('naive-ui')['NEllipsis']
const NEmpty: typeof import('naive-ui')['NEmpty']
const NForm: typeof import('naive-ui')['NForm']
const NFormItem: typeof import('naive-ui')['NFormItem']
const NFormItemGi: typeof import('naive-ui')['NFormItemGi']
const NGi: typeof import('naive-ui')['NGi']
const NGrid: typeof import('naive-ui')['NGrid']
const NGridItem: typeof import('naive-ui')['NGridItem']
const NInput: typeof import('naive-ui')['NInput']
const NInputGroup: typeof import('naive-ui')['NInputGroup']
const NInputGroupLabel: typeof import('naive-ui')['NInputGroupLabel']
const NInputNumber: typeof import('naive-ui')['NInputNumber']
const NLayout: typeof import('naive-ui')['NLayout']
const NLayoutContent: typeof import('naive-ui')['NLayoutContent']
const NLayoutSider: typeof import('naive-ui')['NLayoutSider']
const NList: typeof import('naive-ui')['NList']
const NListItem: typeof import('naive-ui')['NListItem']
const NLoadingBarProvider: typeof import('naive-ui')['NLoadingBarProvider']
const NMenu: typeof import('naive-ui')['NMenu']
const NMessageProvider: typeof import('naive-ui')['NMessageProvider']
const NModal: typeof import('naive-ui')['NModal']
const NNotificationProvider: typeof import('naive-ui')['NNotificationProvider']
const NP: typeof import('naive-ui')['NP']
const NPopconfirm: typeof import('naive-ui')['NPopconfirm']
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']
const NScrollbar: typeof import('naive-ui')['NScrollbar']
const NSelect: typeof import('naive-ui')['NSelect']
const NSpace: typeof import('naive-ui')['NSpace']
const NSpin: typeof import('naive-ui')['NSpin']
const NStatistic: typeof import('naive-ui')['NStatistic']
const NSwitch: typeof import('naive-ui')['NSwitch']
const NTab: typeof import('naive-ui')['NTab']
const NTabPane: typeof import('naive-ui')['NTabPane']
const NTabs: typeof import('naive-ui')['NTabs']
const NTag: typeof import('naive-ui')['NTag']
const NText: typeof import('naive-ui')['NText']
const NThing: typeof import('naive-ui')['NThing']
const NTooltip: typeof import('naive-ui')['NTooltip']
const NTree: typeof import('naive-ui')['NTree']
const NTreeSelect: typeof import('naive-ui')['NTreeSelect']
const NUpload: typeof import('naive-ui')['NUpload']
const NUploadDragger: typeof import('naive-ui')['NUploadDragger']
const NWatermark: typeof import('naive-ui')['NWatermark']
const OssUpload: typeof import('./../components/custom/oss-upload.vue')['default']
const PinToggler: typeof import('./../components/common/pin-toggler.vue')['default']
const PostSelect: typeof import('./../components/custom/post-select.vue')['default']
const ReloadButton: typeof import('./../components/common/reload-button.vue')['default']
const RoleSelect: typeof import('./../components/custom/role-select.vue')['default']
const RouterLink: typeof import('vue-router')['RouterLink']
const RouterView: typeof import('vue-router')['RouterView']
const SoybeanAvatar: typeof import('./../components/custom/soybean-avatar.vue')['default']
const StatusSwitch: typeof import('./../components/custom/status-switch.vue')['default']
const SvgIcon: typeof import('./../components/custom/svg-icon.vue')['default']
const SystemLogo: typeof import('./../components/common/system-logo.vue')['default']
const TableColumnSetting: typeof import('./../components/advanced/table-column-setting.vue')['default']
const TableHeaderOperation: typeof import('./../components/advanced/table-header-operation.vue')['default']
const TableRowCheckAlert: typeof import('./../components/advanced/table-row-check-alert.vue')['default']
const TableSiderLayout: typeof import('./../components/advanced/table-sider-layout.vue')['default']
const TenantSelect: typeof import('./../components/custom/tenant-select.vue')['default']
const ThemeSchemaSwitch: typeof import('./../components/common/theme-schema-switch.vue')['default']
const UmoDocEditor: typeof import('./../components/custom/umo-doc-editor.vue')['default']
const UserSelect: typeof import('./../components/custom/user-select.vue')['default']
const WaveBg: typeof import('./../components/custom/wave-bg.vue')['default']
}
+3 -2
View File
@@ -21,13 +21,14 @@ const { domRef, updateOptions } = useEcharts(() => ({
}
},
legend: {
data: [$t('page.home.downloadCount'), $t('page.home.registerCount')]
data: [$t('page.home.downloadCount'), $t('page.home.registerCount')],
top: '0'
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
top: '15%'
},
xAxis: {
type: 'category',
+1 -1
View File
@@ -251,7 +251,7 @@ async function handleUnlockLoginInfor(username: string) {
@click="handleCleanLoginInfor"
>
<template #icon>
<icon-material-symbols:warning-outline-rounded />
<icon-material-symbols-warning-outline-rounded />
</template>
清空
</NButton>
+1 -1
View File
@@ -197,7 +197,7 @@ async function handleCleanOperLog() {
@click="handleCleanOperLog"
>
<template #icon>
<icon-material-symbols:warning-outline-rounded />
<icon-material-symbols-warning-outline-rounded />
</template>
清空
</NButton>
+1 -1
View File
@@ -207,7 +207,7 @@ async function handleRefreshCache() {
<template #prefix>
<NButton v-if="hasAuth('system:config:remove')" ghost size="small" @click="handleRefreshCache">
<template #icon>
<icon-material-symbols:sync-outline />
<icon-material-symbols-sync-outline />
</template>
{{ $t('page.system.config.refreshCache') }}
</NButton>
+2 -2
View File
@@ -180,13 +180,13 @@ function addInRow(row: Api.System.Dept) {
<template #prefix>
<NButton v-if="!isCollapse" :disabled="!data.length" size="small" @click="expandAll">
<template #icon>
<icon-quill:expand />
<icon-quill-expand />
</template>
{{ $t('page.system.dept.expandAll') }}
</NButton>
<NButton v-if="isCollapse" :disabled="!data.length" size="small" @click="collapseAll">
<template #icon>
<icon-quill:collapse />
<icon-quill-collapse />
</template>
{{ $t('page.system.dept.collapseAll') }}
</NButton>
+1 -1
View File
@@ -411,7 +411,7 @@ const tableTitle = computed(() => {
<template #prefix>
<NButton ghost size="small" @click="handleRefreshCache">
<template #icon>
<icon-material-symbols:refresh-rounded class="text-icon" />
<icon-material-symbols-refresh-rounded class="text-icon" />
</template>
{{ $t('page.system.dict.refreshCache') }}
</NButton>
+3 -3
View File
@@ -301,19 +301,19 @@ function handleToOssConfig() {
<NButton size="small" ghost @click="handleUpload('file')">
<template #icon>
<icon-material-symbols:upload-rounded />
<icon-material-symbols-upload-rounded />
</template>
上传文件
</NButton>
<NButton size="small" ghost @click="handleUpload('image')">
<template #icon>
<icon-material-symbols:image-outline />
<icon-material-symbols-image-outline />
</template>
上传图片
</NButton>
<NButton type="primary" size="small" ghost @click="handleToOssConfig">
<template #icon>
<icon-hugeicons:configuration-01 />
<icon-hugeicons-configuration-01 />
</template>
配置管理
</NButton>
+2 -2
View File
@@ -259,13 +259,13 @@ async function handleExport() {
<template #prefix>
<NButton v-if="isSuperAdmin" ghost size="small" @click="handleSyncTenantDict">
<template #icon>
<icon-material-symbols:sync-outline />
<icon-material-symbols-sync-outline />
</template>
同步租户字典
</NButton>
<NButton v-if="isSuperAdmin" ghost size="small" @click="handleSyncTenantConfig">
<template #icon>
<icon-material-symbols:sync-outline />
<icon-material-symbols-sync-outline />
</template>
同步租户参数配置
</NButton>
+1 -1
View File
@@ -341,7 +341,7 @@ function handleResetSearch() {
<template #after>
<NButton v-if="hasAuth('system:user:import')" size="small" ghost @click="handleImport">
<template #icon>
<icon-material-symbols:upload-rounded class="text-icon" />
<icon-material-symbols-upload-rounded class="text-icon" />
</template>
{{ $t('common.import') }}
</NButton>