TREA
一.简介
中文官网:https://docs.trae.cn/
TRAE是字节跳动旗下的一款AI编程工具,定位为“超级AI开发工程师”
TRAE不仅仅是一个简单的代码补全工具,它通过提供两种核心工作模式,深度融入了开发流程:

IDE模式:在你熟悉的编码流程中,通过智能代码补全(Cue功能)等方式,提供高采纳率的编码建议,让你在保留完全控制感的同时提升效率。

SOLO模式:这是一种高度自动化的开发方式。AI能够理解你的目标,自主规划任务、调度工具,独立完成从构思到可执行产出的端到端开发任务。在此模式下,你的角色从“写代码的人”转变为“指挥AI的

二. 技能

  • 定义:被视为提供给智能体的一套 “专业能力说明书”(类似用户手册或操作指南)。在执行任务时,智能体可以按需加载相应的技能,从而增强其对任务的理解与执行能力。

  • 特点

  • 结构化

    一个技能对应一个 SKILL.md 文件,文件中以结构化的方式描述完成某一类任务所需的信息,例如:任务目标与适用场景;关键约束与注意事项;推荐流程或操作步骤;可选的脚本、模板或示例。

  • 动态按需加载

    智能体不会在任务开始时一次性读取所有技能的完整内容。在执行任务前,智能体会先扫描所有技能的简要描述,仅当判断当前任务与某个技能高度相关时,才会加载该技能的详细内容。这种按需加载机制可以有效减少上下文中的 Token 消耗、避免无关信息干扰智能体的决策。

  1. 使用场景
  • 保证输出结果的一致性与规范性
    需要智能体在不同时间、不同任务中,始终按照既定标准输出结果。例如,将统一设计规范、执行团队标准、保持品牌一致性或确保代码符合项目约定等要求封装为技能,从而将隐性的个人或团队标准转化为显式、可复用的专业能力,最终使输出结果更加稳定、可控。
  • 自动化重复性工作流
    需要频繁执行相同或高度相似的多步骤任务。例如,对于测试流程、代码规范检查、常规数据分析等难以避免的日常工作,可以将既有的 SOP 封装为技能。一旦相关任务被触发,智能体即可自动按照定义好的流程执行,从而减少重复的指令输入,提升效率。
  • 总结与共享专业能力
    总结个人经验或团队规范,并在更大范围内复用。例如,将技能在社区、交流群等公共平台进行分享,从而在不同的智能体、项目、团队间复用相同的技能。
  1. 技能类型
  • 全局技能 – 跨项目全局生效的技能,~/.trae-cn/skills
  • 项目技能 – 仅在当前项目生效的技能, 相关文件位于 .trae/skills/ 目录。
  1. 创建技能
    5.1 通过对话,由 AI 自动创建技能
帮我在 ./trae/skills 目录下创建一个新的技能
技能的名字叫 create-router-component
这个技能可以帮我做以下事情:
- 在指定文件夹下的view 文件夹下面新建一个文件夹,以需要创建的组件名命名
- 在生成的文件夹下面,新建index.vue 文件
- index文件的内容是标准的vue文件,里面有data,method,created
- 生成的index.vue 中引入src/components/custom/HisignTableModel组件,并使用
- 给HisignTableModel组件传一下值:传title为组件名;reduceHeight:230;act:”“;sys: "", config, tableConfig,tableColumns,actionBtns
- 在组件的data中添加config, tableConfig,tableColumns,actionBtns;
- 如果描述中操作按钮有添加,则在config.button 中加入{title: '新增', type: "primary", icon:'iconfont icon-add', callback: () => {this.add()},在method中添加add方法
- 如果描述中操作按钮有导出按钮,则在config.button 中加入{title: "导出数据", type: "default", icon: "iconfont icon-file-upload fs20 tran-X-2", export:true, act: ""},然后再引入src/components/custom/HisignFileExport组件,并使用
- 如果描述中操作按钮有导入按钮,则在config.button 中加入{title: "导入数据", type: 'primary', isCustomIcon: true, icon: 'iconfont icon-add', callback: () => {this.importFile()}}},在method中添加importFile方法, src/components/custom/HisignFileImport组件,并使用,给HisignFileImport组件绑定v-model="importVb",:config="importCfg",在data中添加importVb: false, importCfg
- 如果描述中操作有批量删除按钮,则在config.button 中加入{title: "批量删除", type: 'default', icon: 'iconfont icon-delete fs20 tran-X-2', callback: () => {this.batchDel()}},在method中添加batchDel 方法, 在data中添加checkedListData: []
-  如果描述中列表操作栏有删除按钮,则引入src/components/custom/HisignPromptDialog组件,并使用    <HisignPromptDialog v-model="delVb" :promptText="promptText" @on-sure="sureDel" />,在data中添加delVb:false,promptText: ["是否确定删除?", "是否确定删除?"],在actionBtns中添加{type: 'error', icon: 'iconfont icon-delete fs14', iconStyle: {transform: 'scale(1.2)'}, txt: '删除', cb: () => {this.del()}},在method中添加sureDel,del方法
-如果描述中添加查询条件,则在config.simpleForm 中添加{property: "", type: "text", span: 3, placeholder: ""},property动态生成英文字段,placeholder为添加的查询条件名称;有多个查询条件则在simpleForm 添加多个;如果有添加事件查询条件,则在config.simpleForm 中添加{property: [], type: "dayrange", span: 3, placeholder: ""},property的第一个元素以start开头,第二个元素以end开头
-根据描述中列表表头 在tableColumns动态添加{title: "", key: "", minWidth: 140}, tableColumns中第一个元素是{type: 'index', title: "序号", width: 65},最后一个元素是{title: "操作", key: "action", width: 200, slot: "action", className: "table-btns", fixed: 'right'}
-  如果描述中列表操作栏有编辑按钮,在actionBtns中添加{type: 'primary', icon: 'iconfont icon-qianbi fs14', iconStyle: {transform: 'scale(1.2)'}, txt: '修改', cb: () => {this.edit()}},在method中添加edit方法
- 在指定文件夹下的router文件中添加modules文件夹,
- 在modules文件夹新增 路由文件, 以需要创建的组件名命名,并在指定文件夹下的router/index.js中引入该文件

5.2 在./trae/skills文件夹下
index.js

const fs = require('fs');
const path = require('path');

module.exports = {
  name: 'create-router-component',
  description: '创建带有路由的Vue组件',
  parameters: [
    {
      name: 'componentName',
      type: 'string',
      required: true,
      description: '组件名称'
    },
    {
      name: 'targetDir',
      type: 'string',
      required: true,
      description: '目标目录(相对于项目根目录)'
    },
    {
      name: 'description',
      type: 'string',
      required: false,
      description: '组件描述,包含操作按钮、查询条件、列表表头和操作栏等信息'
    }
  ],
  async execute(params, context) {
    const { componentName, targetDir, description = '' } = params;
    const projectRoot = context.workspaceRoot;
    
    // 构建组件路径
    const viewDir = path.join(projectRoot, targetDir, 'view');
    const componentDir = path.join(viewDir, componentName);
    const indexVuePath = path.join(componentDir, 'index.vue');
    
    // 确保目标目录存在
    if (!fs.existsSync(viewDir)) {
      fs.mkdirSync(viewDir, { recursive: true });
    }
    
    // 确保组件目录存在
    if (!fs.existsSync(componentDir)) {
      fs.mkdirSync(componentDir, { recursive: true });
    }
    
    // 解析描述,提取操作按钮、查询条件、列表表头和操作栏信息
    const hasAddButton = description.includes('添加');
    const hasExportButton = description.includes('导出');
    const hasImportButton = description.includes('导入');
    const hasBatchDeleteButton = description.includes('批量删除');
    const hasDeleteButton = description.includes('删除按钮');
    const hasEditButton = description.includes('编辑按钮');
    const hasQueryConditions = description.includes('查询条件');
    const hasDateRange = description.includes('时间查询条件');
    const hasSort = description.includes('排序');
    const hasSortColumn = description.includes('列表表头有排序');

    
    // 提取查询条件名称
    const queryConditions = [];
    
    // 提取列表表头
    const tableHeaders = [];

    // 生成导入语句
    let importStatements = 'import HisignTableModel from \'@/components/custom/HisignTableModel\';';
    if (hasImportButton) {
      importStatements += '\nimport HisignFileImport from \'@/components/custom/HisignFileImport\';';
    }
    if (hasDeleteButton) {
      importStatements += '\nimport HisignPromptDialog from \'@/components/custom/HisignPromptDialog\';';
    }
    
    // 生成组件注册
    let components = 'HisignTableModel';
    if (hasImportButton) {
      components += ', HisignFileImport';
    }
    if (hasDeleteButton) {
      components += ', HisignPromptDialog';
    }
    
    // 生成配置对象
    let config = '{' + 
        '\n    button: [';
    // 添加按钮
    if (hasAddButton) {
      config += '\n        {title: \'新增\', type: "primary", icon:\'iconfont icon-add\', callback: () => {this.add()}},';
    }
    if (hasExportButton) {
      config += '\n        {title: "导出数据", type: "default", icon: "iconfont icon-file-upload fs20 tran-X-2", export:true, act: ""},';
    }
    if (hasImportButton) {
      config += '\n        {title: "导入数据", type: \'primary\', isCustomIcon: true, icon: \'iconfont icon-add\', callback: () => {this.importFile()}},';
    }
    if (hasBatchDeleteButton) {
      config += '\n        {title: "批量删除", type: \'default\', icon: \'iconfont icon-delete fs20 tran-X-2\', callback: () => {this.batchDel()}},';
    }
    
    // 添加查询条件
    if (hasQueryConditions) {
      config += '\n    ],\n    simpleForm: [';
      queryConditions.forEach((condition, index) => {
        const property = condition.toLowerCase().replace(/\s+/g, '');
        if (hasDateRange) {
          config += '\n        {property: ["start"' + property + '"Date", "end"' + property + '"Date"], type: "dayrange", span: 3, placeholder: "' + condition + '"},';
        } else {
          config += '\n        {property: "' + property + '", type: "text", span: 3, placeholder: "' + condition + '"},';
        }
      });
    }
    
    config += '\n    ]\n  }';
    
    // 生成操作按钮
    let actionBtns = '[';
    if (hasEditButton) {
      actionBtns += '\n    {type: \'primary\', icon: \'iconfont icon-qianbi fs14\', iconStyle: {transform: \'scale(1.2)\'}, txt: \'修改\', cb: () => {this.edit()}},';
    }
    if (hasDeleteButton) {
      actionBtns += '\n    {type: \'error\', icon: \'iconfont icon-delete fs14\', iconStyle: {transform: \'scale(1.2)\'}, txt: \'删除\', cb: () => {this.del()}},';
    }
    actionBtns += '\n  ]';
    
    // 生成表格列
    let tableColumns = '[' +
        '\n    {type: \'index\', title: "序号", width: 65},';
    
    // 添加排序列
    if (hasSort || hasSortColumn) {
      tableColumns += '\n    {title: "排序", width: 80, render: (h, params) => {\n          return h("div", {class: "drag-handle"}, [\n            h("Icon", {props: {type: \'md-menu\', size: 20, color: \'#515a6e\'}})\n          ])\n        }},';
    }
    
    tableHeaders.forEach(header => {
      tableColumns += '\n    {title: "' + header.title + '", key: "' + header.key + '", minWidth: 140},';
    });
    tableColumns += '\n    {title: "操作", key: "action", width: 200, slot: "action", className: "table-btns", fixed: \'right\'}\n  ]';
    
    // 生成 tableConfig
    let tableConfig = '{\n    pageSelection: true';
    if (hasSort || hasSortColumn) {
      tableConfig += ',\n    dragSort: true,\n    rowKey: "id"';
    }
    tableConfig += '\n  }';
    
    // 生成 data 部分
    let data = '{' +
      '\n    config: ' + config + ','+
      '\n    tableConfig: ' + tableConfig + ','+ 
      '\n    tableColumns: ' + tableColumns + ',' +
      '\n    actionBtns: ' + actionBtns + ','
    
    if (hasImportButton) {
      data += '\n    importVb: false,\n    importCfg: {\n      uploadAct: "",\n      systemId: "",\n      tplAct: "",\n      importAct: "",\n      tip: "导入过程中如发现必填项未填写,则这个无法导入。",\n      dialogWidth: 1000,\n      columns: [\n        {title: "序号", type: "index", width: 65},\n      ],\n      completed: () => {\n        this.doSearch();\n      },\n    },';
    }
    if (hasBatchDeleteButton) {
      data += '\n    checkedListData: [],\n    delIds: [] ,';
    }
    if (hasDeleteButton) {
      data += '\n    delVb: false,\n    promptText: ["是否确定删除?", "是否确定删除?"] ,';
    }
    
    data += '\n    // 其他数据\n  }'
    
    // 生成方法部分
    let methods = '{' +
      '\n    doSearch() {\n      // 查询\n    },';
    
    if (hasAddButton) {
      methods += '\n    add() {\n      // 新增逻辑\n    },';
    }
    if (hasImportButton) {
      methods += '\n    importFile() {\n      this.importVb = true;\n    },';
    }
    if (hasBatchDeleteButton) {
      methods += '\n    batchDel() {\n      if (this.checkedListData.length == 0) {\n        this.$Message.error({content: "请勾选任务", backgroud: true});\n        return;\n      }\n      this.delVb = true;\n      this.delIds = this.checkedListData.map(v => v.id);\n    },';
    }
    if (hasDeleteButton) {
      methods += '\n    del() {\n      this.delVb = true;\n    },\n    sureDel() {\n      // 删除确认逻辑\n      this.delVb = false;\n    },';
    }
    if (hasEditButton) {
      methods += '\n    edit() {\n      // 编辑逻辑\n    },';
    }
    
    methods += '\n    // 其他方法\n  }';

    
    // 生成模板部分
    let template = '<template>\n  <div>\n    <HisignTableModel \n      ref="tableModel"\n      :title="\'' + componentName + '\'"\n      reduceHeight="230"\n      :act="\'\'"\n      :sys="\'\'"\n      :config="config"\n      :tableConfig="tableConfig"\n      :tableColumns="tableColumns"\n      :actionBtns="actionBtns"\n    />';
    
    if (hasImportButton) {
      template += '\n    <HisignFileImport v-model="importVb" :config="importCfg" />';
    }
    if (hasDeleteButton) {
      template += '\n    <HisignPromptDialog v-model="delVb" :promptText="promptText" @on-sure="sureDel" />';
    }
    
    template += '\n  </div>\n</template>';
    
    // 生成完整的 Vue 文件内容
    const vueContent = template + '\n\n<script>\n' + importStatements + '\nexport default {\n  name: \'' + componentName + '\',\n  components: {\n    ' + components + '\n  },\n  data() {\n    return ' + data + ';\n  },\n  methods: ' + methods + ',\n  created() {\n    // 创建时执行\n  }\n};\n</script>\n\n<style scoped>\n/* 样式 */\n</style>';
    
    // 写入 index.vue 文件
    fs.writeFileSync(indexVuePath, vueContent);
    
    // 构建路由路径
    const routerDir = path.join(projectRoot, targetDir, 'router');
    const modulesDir = path.join(routerDir, 'modules');
    const routeFilePath = path.join(modulesDir, `${componentName}.js`);
    const routerIndexPath = path.join(routerDir, 'index.js');
    
    // 确保 modules 目录存在
    if (!fs.existsSync(modulesDir)) {
      fs.mkdirSync(modulesDir, { recursive: true });
    }
    
    // 生成路由模块文件
    const routeFileContent = `export default [\n  {\n    path: '/${componentName}',\n    name: '${componentName}',\n    component: () => import('../../view/${componentName}/index.vue')\n  }\n];`;
    
    // 写入路由模块文件
    fs.writeFileSync(routeFilePath, routeFileContent);
    
    // 检查路由 index.js 文件是否存在
    if (fs.existsSync(routerIndexPath)) {
      // 读取现有路由文件内容
      let routerContent = fs.readFileSync(routerIndexPath, 'utf8');
      
      // 检查是否已经导入了该模块
      const importStatement = `import ${componentName}Routes from './modules/${componentName}';`;
      
      // 如果没有导入,则添加导入语句
      if (!routerContent.includes(importStatement)) {
        // 找到导入区域的位置
        const importEndIndex = routerContent.lastIndexOf('import');
        if (importEndIndex !== -1) {
          // 找到该导入语句的结束位置
          const lineEndIndex = routerContent.indexOf(';', importEndIndex) + 1;
          if (lineEndIndex !== -1) {
            // 在导入语句后添加新的导入
            routerContent = routerContent.substring(0, lineEndIndex) + '\n' + importStatement + routerContent.substring(lineEndIndex);
          }
        } else {
          // 如果没有任何导入语句,则在文件开头添加
          routerContent = importStatement + '\n' + routerContent;
        }
      }
      
      // 找到 routes 数组的位置并插入新路由
      if (routerContent.includes('routes: [')) {
        // 找到 routes 数组的结束位置
        const routesStartIndex = routerContent.indexOf('routes: [');
        const routesEndIndex = findMatchingBracket(routerContent, routesStartIndex + 8);
        
        if (routesEndIndex !== -1) {
          // 检查是否已经包含该路由
          if (!routerContent.includes(`${componentName}Routes`)) {
            // 检查是否需要添加逗号
            const beforeEnd = routerContent.substring(routesStartIndex + 8, routesEndIndex).trim();
            const comma = beforeEnd ? ', ' : '';
            
            // 插入新路由
            const newRouterContent = routerContent.substring(0, routesEndIndex) + 
              comma + `${componentName}Routes` + 
              routerContent.substring(routesEndIndex);
            
            // 写入更新后的路由文件
            fs.writeFileSync(routerIndexPath, newRouterContent);
          }
        }
      }
    }
    
    return {
      success: true,
      message: `组件 ${componentName} 创建成功,路由已添加`
    };
  }
};

// 辅助函数:查找匹配的括号
function findMatchingBracket(str, startIndex) {
  let count = 1;
  for (let i = startIndex; i < str.length; i++) {
    if (str[i] === '[') {
      count++;
    } else if (str[i] === ']') {
      count--;
      if (count === 0) {
        return i;
      }
    }
  }
  return -1;
}

package.json

{
  "name": "create-router-component",
  "version": "1.0.0",
  "description": "创建带有路由的Vue组件",
  "main": "index.js",
  "trae": {
    "type": "skill",
    "displayName": "创建路由组件",
    "description": "在指定文件夹下创建带有路由的Vue组件",
    "parameters": [
      {
        "name": "componentName",
        "type": "string",
        "required": true,
        "description": "组件名称"
      },
      {
        "name": "targetDir",
        "type": "string",
        "required": true,
        "description": "目标目录(相对于项目根目录)"
      },
      {
        "name": "description",
        "type": "string",
        "required": false,
        "description": "组件描述,包含操作按钮、查询条件、列表表头和操作栏等信息"
      }
    ]
  },
  "keywords": [
    "trae",
    "skill",
    "vue",
    "router",
    "component"
  ],
  "author": "",
  "license": "MIT"
}
  1. 使用

    在src\projects\xzsp-vue生成 库存管理 路由组件,有导出,导入,添加操作按钮,有耗材名称,品牌,编号查询条件,列表表头有排序,保管部门,耗材名称,品牌,型号,性状,数量,单位,列表操作栏有编辑按钮

  2. 视图
    在这里插入图片描述

Logo

小龙虾开发者社区是 CSDN 旗下专注 OpenClaw 生态的官方阵地,聚焦技能开发、插件实践与部署教程,为开发者提供可直接落地的方案、工具与交流平台,助力高效构建与落地 AI 应用

更多推荐