From e94118881a9d945f0467e97907540de212f5d5a2 Mon Sep 17 00:00:00 2001 From: fofolee Date: Sun, 5 Jan 2025 11:04:20 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E7=BC=96=E6=8E=92=E6=89=A7?= =?UTF-8?q?=E8=A1=8C=E5=91=BD=E4=BB=A4=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugin/lib/quickcomposer.js | 1 + plugin/lib/quickcomposer/system/exec.js | 48 +++ plugin/lib/quickcomposer/system/index.js | 5 + src/components/composer/CommandComposer.vue | 11 +- .../composer/file/FileOperationEditor.vue | 2 +- .../composer/http/AxiosConfigEditor.vue | 2 +- .../composer/simulate/ImageSearchEditor.vue | 2 +- .../composer/system/SystemCommandEditor.vue | 303 ++++++++++++++++++ .../composer/ubrowser/UBrowserEditor.vue | 7 +- src/components/composer/ui/KeyEditor.vue | 2 +- src/components/composer/ui/NumberInput.vue | 2 +- src/js/composer/cardComponents.js | 5 + src/js/composer/commands/simulateCommands.js | 2 +- src/js/composer/commands/systemCommands.js | 20 +- src/js/composer/customComponentGuide.js | 152 ++++++--- 15 files changed, 505 insertions(+), 59 deletions(-) create mode 100644 plugin/lib/quickcomposer/system/exec.js create mode 100644 plugin/lib/quickcomposer/system/index.js create mode 100644 src/components/composer/system/SystemCommandEditor.vue diff --git a/plugin/lib/quickcomposer.js b/plugin/lib/quickcomposer.js index 9748dd6..4fb8c88 100644 --- a/plugin/lib/quickcomposer.js +++ b/plugin/lib/quickcomposer.js @@ -2,6 +2,7 @@ const quickcomposer = { textProcessor: require("./quickcomposer/textProcessor"), simulate: require("./quickcomposer/simulate"), file: require("./quickcomposer/file"), + system: require("./quickcomposer/system"), }; module.exports = quickcomposer; diff --git a/plugin/lib/quickcomposer/system/exec.js b/plugin/lib/quickcomposer/system/exec.js new file mode 100644 index 0000000..399c94f --- /dev/null +++ b/plugin/lib/quickcomposer/system/exec.js @@ -0,0 +1,48 @@ +const { execSync } = require("child_process"); +const iconv = require("iconv-lite"); +const os = require("os"); + +function getSystemEncoding() { + // Windows 默认使用 GBK/GB2312,其他系统默认 UTF-8 + return os.platform() === "win32" ? "gbk" : "utf8"; +} + +function exec(command, options = {}) { + try { + const { + autoEncoding = true, + encoding = "buffer", + windowsHide = true, + ...execOptions + } = options; + + // 执行命令,总是使用 buffer 获取原始输出 + const output = execSync(command, { + ...execOptions, + encoding: "buffer", + windowsHide, + }); + + // 如果设置了自动编码,根据系统编码解码 + if (autoEncoding) { + return iconv.decode(output, getSystemEncoding()); + } + + // 如果手动设置了编码且不是 buffer,则按指定编码解码 + if (encoding !== "buffer") { + return iconv.decode(output, encoding); + } + + // 返回原始 buffer + return output; + } catch (error) { + // 如果是执行错误,尝试解码错误信息 + if (error.stderr) { + const stderr = iconv.decode(error.stderr, getSystemEncoding()); + error.message = stderr; + } + throw error; + } +} + +module.exports = exec; diff --git a/plugin/lib/quickcomposer/system/index.js b/plugin/lib/quickcomposer/system/index.js new file mode 100644 index 0000000..5043d03 --- /dev/null +++ b/plugin/lib/quickcomposer/system/index.js @@ -0,0 +1,5 @@ +const exec = require("./exec"); + +module.exports = { + exec, +}; diff --git a/src/components/composer/CommandComposer.vue b/src/components/composer/CommandComposer.vue index d3f1fa3..b73e041 100644 --- a/src/components/composer/CommandComposer.vue +++ b/src/components/composer/CommandComposer.vue @@ -247,13 +247,16 @@ export default defineComponent({ background: rgba(255, 255, 255, 0.08); } -/* checkbox大小及字体 */ -.command-composer :deep(.q-checkbox__label) { +/* checkbox/toggle大小及字体 */ +.command-composer :deep(.q-checkbox__label), +.command-composer :deep(.q-toggle__label) { font-size: 12px; } -.command-composer :deep(.q-checkbox__inner) { - font-size: 24px; +.command-composer :deep(.q-checkbox__inner), +.command-composer :deep(.q-toggle__inner) { + font-size: 28px; + margin: 4px 0px; } /* 暗黑模式下的标签栏背景颜色 */ diff --git a/src/components/composer/file/FileOperationEditor.vue b/src/components/composer/file/FileOperationEditor.vue index 61ccc93..f9459a8 100644 --- a/src/components/composer/file/FileOperationEditor.vue +++ b/src/components/composer/file/FileOperationEditor.vue @@ -564,7 +564,7 @@ export default defineComponent({ break; } - return `quickcomposer.file.operation(${stringifyObject(params)})`; + return `${this.modelValue.value}(${stringifyObject(params)})`; }, updateArgvs(key, value) { const argvs = { ...this.argvs }; diff --git a/src/components/composer/http/AxiosConfigEditor.vue b/src/components/composer/http/AxiosConfigEditor.vue index 922f027..4e4c26c 100644 --- a/src/components/composer/http/AxiosConfigEditor.vue +++ b/src/components/composer/http/AxiosConfigEditor.vue @@ -472,7 +472,7 @@ export default defineComponent({ ? `, ${stringifyObject(restConfig)}` : ""; - return `axios.${method.toLowerCase()}(${stringifyWithType(url)}${ + return `${this.modelValue.value}.${method.toLowerCase()}(${stringifyWithType(url)}${ this.hasRequestData ? `, ${stringifyObject(data)}` : "" }${configStr})`; }, diff --git a/src/components/composer/simulate/ImageSearchEditor.vue b/src/components/composer/simulate/ImageSearchEditor.vue index 1d43f27..ea870c5 100644 --- a/src/components/composer/simulate/ImageSearchEditor.vue +++ b/src/components/composer/simulate/ImageSearchEditor.vue @@ -212,7 +212,7 @@ export default defineComponent({ }; // 生成代码 - return `quickcomposer.simulate.findImage("data:image/png;base64,${config.imageData}", { threshold: ${config.threshold}, mouseAction: "${config.mouseAction}" })`; + return `${this.modelValue.value}("data:image/png;base64,${config.imageData}", { threshold: ${config.threshold}, mouseAction: "${config.mouseAction}" })`; }, parseCodeToArgvs(code) { diff --git a/src/components/composer/system/SystemCommandEditor.vue b/src/components/composer/system/SystemCommandEditor.vue new file mode 100644 index 0000000..e480219 --- /dev/null +++ b/src/components/composer/system/SystemCommandEditor.vue @@ -0,0 +1,303 @@ + + + + + diff --git a/src/components/composer/ubrowser/UBrowserEditor.vue b/src/components/composer/ubrowser/UBrowserEditor.vue index 944b6dc..3776f43 100644 --- a/src/components/composer/ubrowser/UBrowserEditor.vue +++ b/src/components/composer/ubrowser/UBrowserEditor.vue @@ -8,6 +8,7 @@ align="left" narrow-indicator inline-label + active-class="ubrowser-tabs-active" > @@ -133,6 +134,10 @@ export default defineComponent({ width: 100%; } +.ubrowser-tabs-active { + color: var(--q-primary); +} + .ubrowser-tabs { flex-shrink: 0; } @@ -143,7 +148,7 @@ export default defineComponent({ } .panel-content { - padding: 16px; + padding: 8px; min-height: 200px; } diff --git a/src/components/composer/ui/KeyEditor.vue b/src/components/composer/ui/KeyEditor.vue index e86c8d4..73cba38 100644 --- a/src/components/composer/ui/KeyEditor.vue +++ b/src/components/composer/ui/KeyEditor.vue @@ -284,7 +284,7 @@ export default defineComponent({ .map((key) => (!isMac && key === "command" ? "meta" : key)); const args = [argvs.mainKey, ...activeModifiers]; - return `keyTap("${args.join('","')}")`; + return `${this.modelValue.value}("${args.join('","')}")`; }, updateValue(argv) { const newArgvs = { diff --git a/src/components/composer/ui/NumberInput.vue b/src/components/composer/ui/NumberInput.vue index 671ce7f..96ef632 100644 --- a/src/components/composer/ui/NumberInput.vue +++ b/src/components/composer/ui/NumberInput.vue @@ -49,7 +49,7 @@ export default defineComponent({ }, icon: { type: String, - default: "pin", + default: "", }, }, emits: ["update:modelValue"], diff --git a/src/js/composer/cardComponents.js b/src/js/composer/cardComponents.js index f4047f5..690bea6 100644 --- a/src/js/composer/cardComponents.js +++ b/src/js/composer/cardComponents.js @@ -54,3 +54,8 @@ export const AsymmetricCryptoEditor = defineAsyncComponent(() => export const FileOperationEditor = defineAsyncComponent(() => import("components/composer/file/FileOperationEditor.vue") ); + +// System Components +export const SystemCommandEditor = defineAsyncComponent(() => + import("components/composer/system/SystemCommandEditor.vue") +); diff --git a/src/js/composer/commands/simulateCommands.js b/src/js/composer/commands/simulateCommands.js index 60fe8eb..89287f4 100644 --- a/src/js/composer/commands/simulateCommands.js +++ b/src/js/composer/commands/simulateCommands.js @@ -4,7 +4,7 @@ export const simulateCommands = { defaultOpened: false, commands: [ { - value: "keyTap", + value: "utools.simulateKeyboardTap", label: "模拟按键", config: [], component: "KeyEditor", diff --git a/src/js/composer/commands/systemCommands.js b/src/js/composer/commands/systemCommands.js index 1f5353a..0be1f62 100644 --- a/src/js/composer/commands/systemCommands.js +++ b/src/js/composer/commands/systemCommands.js @@ -3,18 +3,6 @@ export const systemCommands = { icon: "computer", defaultOpened: false, commands: [ - { - value: "system", - label: "执行系统命令", - config: [ - { - key: "command", - label: "要执行的命令行", - type: "varInput", - icon: "terminal", - }, - ], - }, { value: "copyTo", label: "将内容写入剪贴板", @@ -33,5 +21,13 @@ export const systemCommands = { config: [], allowEmptyArgv: true, }, + { + value: "quickcomposer.system.exec", + label: "执行系统命令", + desc: "执行系统命令并返回输出结果", + config: [], + component: "SystemCommandEditor", + icon: "terminal", + }, ], }; diff --git a/src/js/composer/customComponentGuide.js b/src/js/composer/customComponentGuide.js index cf857ba..0faa6d1 100644 --- a/src/js/composer/customComponentGuide.js +++ b/src/js/composer/customComponentGuide.js @@ -249,44 +249,124 @@ const customComponentGuide = { }, tips: "formatJsonVariables 主要用于处理对象中的变量,避免对简单参数使用,以免产生不必要的引号", }, - asyncCommand: "后端使用异步函数时,命令配置需要设置isAsync: true", - componentStructure: "参考现有组件的实现方式,保持一致的代码风格", - errorHandling: "前后端都需要适当的错误处理和提示", - typeChecking: "确保所有参数都有适当的类型检查", - codeGeneration: { - description: "代码生成的关键点", - points: [ - "1. 使用 generateCode 方法生成标准格式的代码", - "2. 确保生成的代码可以被正确解析回参数", - "3. 处理特殊字符和引号", - "4. 考虑不同类型参数的处理方式", + commonComponents: { + description: "常用组件的使用说明", + components: { + VariableInput: { + description: "变量输入组件", + usage: "用于输入可能包含变量的字符串", + props: [ + "model-value - 输入值,需要包含 value、isString、__varInputVal__ 属性", + "label - 输入框标签", + "icon - 输入框图标", + ], + }, + DictEditor: { + description: "键值对编辑组件", + usage: "用于编辑对象类型的配置,如 headers、env 等", + features: [ + "自带添加/删除按钮", + "支持键值对编辑", + "值部分默认使用 VariableInput", + "支持下拉选项(通过 options.items 配置)", + ], + notes: [ + "初始值应该是一个对象,如 {}", + "支持通过 options 属性配置下拉选项", + ], + }, + NumberInput: { + description: "数字输入组件", + usage: "用于输入数字类型的配置", + props: [ + "model-value - 数字值", + "label - 输入框标签", + "icon - 输入框图标", + ], + }, + }, + }, + dataFlow: { + description: "数据流转说明", + patterns: { + modelValue: { + description: "组件数据结构", + structure: { + value: "函数名,来自命令配置", + code: "生成的代码字符串", + argvs: "解析后的参数对象", + }, + }, + updateFlow: { + description: "数据更新流程", + steps: [ + "1. 用户输入触发 update:model-value 事件", + "2. 组件内部更新 argvs", + "3. argvs 更新触发 generateCode", + "4. 发送完整的 modelValue 给父组件", + ], + }, + }, + }, + bestPractices: { + description: "最佳实践", + tips: [ + "使用现有组件而不是重新开发", + "参考 axios 等成熟组件的实现", + "保持数据流的清晰和一致", + "合理使用计算属性和方法", + "避免直接修改 props", + "正确处理默认值", ], }, - codeParsing: { - description: "代码解析的关键点", - points: [ - "1. 使用 parseCodeToArgvs 方法解析代码为参数", - "2. 处理参数中的特殊字符和引号", - "3. 正确识别参数类型", - "4. 处理解析失败的情况", - ], - }, - componentLifecycle: { - description: "组件生命周期管理", - points: [ - "1. mounted 中初始化默认值", - "2. 清理事件监听器", - "3. 处理组件销毁", - ], - }, - codeStyle: { - description: "代码风格规范", - points: [ - "1. 保持代码结构清晰", - "2. 使用有意义的变量名", - "3. 添加必要的注释", - "4. 遵循 Vue 组件命名规范", - ], + commonPitfalls: { + description: "自定义组件开发中的常见错误和最佳实践", + componentRegistration: { + description: "组件注册相关注意事项", + tips: [ + "组件名使用 PascalCase 命名,如 SystemCommandEditor", + "在 cardComponents.js 中注册时使用字符串形式,如 component: 'SystemCommandEditor'", + "不要在配置文件中导入组件", + "不要在配置文件中定义 defaultArgvs,这应该只在组件内部定义", + ], + }, + codeGeneration: { + description: "代码生成相关注意事项", + tips: [ + "使用 modelValue.value 获取函数名,避免硬编码", + "在 generateCode 中正确处理默认值的移除", + "正确定义 variableFormatPaths 以处理变量格式", + ], + }, + dataManagement: { + description: "数据管理相关注意事项", + tips: [ + "使用 updateArgvs(key, value) 方法显式更新数据", + "在模板中使用 :model-value 和 @update:model-value 而不是 v-model", + "在 emit 时不需要提交 value 字段,因为这在 command 配置里已定义", + "使用 getter/setter 形式的 argvs 计算属性", + "在 mounted 和 updateArgvs 中保留原始字段 ...this.modelValue", + "处理嵌套对象更新时需要正确处理路径,如 'options.encoding' 需要分别更新 options 对象", + "更新嵌套对象时要保持对象的响应性,使用解构运算符复制对象", + ], + }, + performance: { + description: "性能优化相关注意事项", + tips: [ + "避免使用过多的 watch 属性,容易造成性能问题", + "使用 computed 代替复杂的 watch", + "减少不必要的数据更新和重渲染", + "保持简单的 getter/setter 模式,避免复杂的嵌套对象更新", + ], + }, + componentStructure: { + description: "组件结构相关注意事项", + tips: [ + "使用 data() 而不是 setup 来定义组件数据", + "在配置文件中的 value 应该包含完整路径,如 quickcomposer.system.exec", + "组件内部保持简单清晰的结构,避免过度复杂的逻辑", + ], + }, }, }, examples: {