mirror of
https://github.com/fofolee/uTools-quickcommand.git
synced 2025-06-08 14:34:13 +08:00
完善编排执行命令功能
This commit is contained in:
parent
e082304c56
commit
e94118881a
@ -2,6 +2,7 @@ const quickcomposer = {
|
|||||||
textProcessor: require("./quickcomposer/textProcessor"),
|
textProcessor: require("./quickcomposer/textProcessor"),
|
||||||
simulate: require("./quickcomposer/simulate"),
|
simulate: require("./quickcomposer/simulate"),
|
||||||
file: require("./quickcomposer/file"),
|
file: require("./quickcomposer/file"),
|
||||||
|
system: require("./quickcomposer/system"),
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = quickcomposer;
|
module.exports = quickcomposer;
|
||||||
|
48
plugin/lib/quickcomposer/system/exec.js
Normal file
48
plugin/lib/quickcomposer/system/exec.js
Normal file
@ -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;
|
5
plugin/lib/quickcomposer/system/index.js
Normal file
5
plugin/lib/quickcomposer/system/index.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
const exec = require("./exec");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
exec,
|
||||||
|
};
|
@ -247,13 +247,16 @@ export default defineComponent({
|
|||||||
background: rgba(255, 255, 255, 0.08);
|
background: rgba(255, 255, 255, 0.08);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* checkbox大小及字体 */
|
/* checkbox/toggle大小及字体 */
|
||||||
.command-composer :deep(.q-checkbox__label) {
|
.command-composer :deep(.q-checkbox__label),
|
||||||
|
.command-composer :deep(.q-toggle__label) {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.command-composer :deep(.q-checkbox__inner) {
|
.command-composer :deep(.q-checkbox__inner),
|
||||||
font-size: 24px;
|
.command-composer :deep(.q-toggle__inner) {
|
||||||
|
font-size: 28px;
|
||||||
|
margin: 4px 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 暗黑模式下的标签栏背景颜色 */
|
/* 暗黑模式下的标签栏背景颜色 */
|
||||||
|
@ -564,7 +564,7 @@ export default defineComponent({
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return `quickcomposer.file.operation(${stringifyObject(params)})`;
|
return `${this.modelValue.value}(${stringifyObject(params)})`;
|
||||||
},
|
},
|
||||||
updateArgvs(key, value) {
|
updateArgvs(key, value) {
|
||||||
const argvs = { ...this.argvs };
|
const argvs = { ...this.argvs };
|
||||||
|
@ -472,7 +472,7 @@ export default defineComponent({
|
|||||||
? `, ${stringifyObject(restConfig)}`
|
? `, ${stringifyObject(restConfig)}`
|
||||||
: "";
|
: "";
|
||||||
|
|
||||||
return `axios.${method.toLowerCase()}(${stringifyWithType(url)}${
|
return `${this.modelValue.value}.${method.toLowerCase()}(${stringifyWithType(url)}${
|
||||||
this.hasRequestData ? `, ${stringifyObject(data)}` : ""
|
this.hasRequestData ? `, ${stringifyObject(data)}` : ""
|
||||||
}${configStr})`;
|
}${configStr})`;
|
||||||
},
|
},
|
||||||
|
@ -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) {
|
parseCodeToArgvs(code) {
|
||||||
|
303
src/components/composer/system/SystemCommandEditor.vue
Normal file
303
src/components/composer/system/SystemCommandEditor.vue
Normal file
@ -0,0 +1,303 @@
|
|||||||
|
<template>
|
||||||
|
<div class="row q-col-gutter-sm">
|
||||||
|
<!-- 命令输入和选项按钮 -->
|
||||||
|
<div class="col-12 command-input-wrapper">
|
||||||
|
<div class="row items-center q-gutter-sm">
|
||||||
|
<div class="col">
|
||||||
|
<VariableInput
|
||||||
|
:model-value="argvs.command"
|
||||||
|
@update:model-value="(val) => updateArgvs('command', val)"
|
||||||
|
label="命令"
|
||||||
|
icon="terminal"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<q-btn
|
||||||
|
flat
|
||||||
|
round
|
||||||
|
dense
|
||||||
|
icon="settings"
|
||||||
|
:color="showOptions ? 'primary' : 'grey-7'"
|
||||||
|
@click="showOptions = !showOptions"
|
||||||
|
>
|
||||||
|
<q-tooltip>选项</q-tooltip>
|
||||||
|
</q-btn>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 执行选项 -->
|
||||||
|
<div class="col-12">
|
||||||
|
<q-slide-transition>
|
||||||
|
<div v-show="showOptions" class="row q-col-gutter-sm">
|
||||||
|
<!-- 工作目录 -->
|
||||||
|
<div class="col-12 col-sm-6">
|
||||||
|
<VariableInput
|
||||||
|
:model-value="argvs.options.cwd"
|
||||||
|
@update:model-value="(val) => updateArgvs('options.cwd', val)"
|
||||||
|
label="工作目录"
|
||||||
|
icon="folder"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Shell路径 -->
|
||||||
|
<div class="col-12 col-sm-6">
|
||||||
|
<VariableInput
|
||||||
|
:model-value="argvs.options.shell"
|
||||||
|
@update:model-value="(val) => updateArgvs('options.shell', val)"
|
||||||
|
label="Shell路径"
|
||||||
|
icon="terminal"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 超时和缓冲区 -->
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="row q-col-gutter-sm">
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<NumberInput
|
||||||
|
:model-value="argvs.options.timeout"
|
||||||
|
@update:model-value="
|
||||||
|
(val) => updateArgvs('options.timeout', val)
|
||||||
|
"
|
||||||
|
label="超时时间(ms)"
|
||||||
|
icon="timer"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<NumberInput
|
||||||
|
:model-value="argvs.options.maxBuffer"
|
||||||
|
@update:model-value="
|
||||||
|
(val) => updateArgvs('options.maxBuffer', val)
|
||||||
|
"
|
||||||
|
label="最大缓冲区(bytes)"
|
||||||
|
icon="memory"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 编码处理 -->
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="row q-col-gutter-sm items-center">
|
||||||
|
<div class="col-6 row justify-between">
|
||||||
|
<q-toggle
|
||||||
|
:model-value="argvs.options.windowsHide"
|
||||||
|
@update:model-value="
|
||||||
|
(val) => updateArgvs('options.windowsHide', val)
|
||||||
|
"
|
||||||
|
label="隐藏命令窗口"
|
||||||
|
/>
|
||||||
|
<q-toggle
|
||||||
|
:model-value="argvs.options.autoEncoding"
|
||||||
|
@update:model-value="
|
||||||
|
(val) => updateArgvs('options.autoEncoding', val)
|
||||||
|
"
|
||||||
|
label="自动处理编码"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col-6" v-if="!argvs.options.autoEncoding">
|
||||||
|
<q-select
|
||||||
|
:model-value="argvs.options.encoding"
|
||||||
|
@update:model-value="
|
||||||
|
(val) => updateArgvs('options.encoding', val)
|
||||||
|
"
|
||||||
|
:options="encodingOptions"
|
||||||
|
label="输出编码"
|
||||||
|
dense
|
||||||
|
filled
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
>
|
||||||
|
<template v-slot:prepend>
|
||||||
|
<q-icon name="code" />
|
||||||
|
</template>
|
||||||
|
</q-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 环境变量 -->
|
||||||
|
<div class="col-12">
|
||||||
|
<BorderLabel label="环境变量">
|
||||||
|
<DictEditor
|
||||||
|
:model-value="argvs.options.env"
|
||||||
|
@update:model-value="(val) => updateArgvs('options.env', val)"
|
||||||
|
/>
|
||||||
|
</BorderLabel>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-slide-transition>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
import {
|
||||||
|
parseFunction,
|
||||||
|
stringifyObject,
|
||||||
|
stringifyWithType,
|
||||||
|
} from "js/composer/formatString";
|
||||||
|
import VariableInput from "components/composer/ui/VariableInput.vue";
|
||||||
|
import NumberInput from "components/composer/ui/NumberInput.vue";
|
||||||
|
import DictEditor from "components/composer/ui/DictEditor.vue";
|
||||||
|
import BorderLabel from "components/composer/ui/BorderLabel.vue";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "SystemCommandEditor",
|
||||||
|
components: {
|
||||||
|
VariableInput,
|
||||||
|
NumberInput,
|
||||||
|
DictEditor,
|
||||||
|
BorderLabel,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
modelValue: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: ["update:modelValue"],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showOptions: false,
|
||||||
|
encodingOptions: [
|
||||||
|
{ label: "Buffer", value: "buffer" },
|
||||||
|
{ label: "UTF-8", value: "utf8" },
|
||||||
|
{ label: "GBK", value: "gbk" },
|
||||||
|
{ label: "GB2312", value: "gb2312" },
|
||||||
|
{ label: "ASCII", value: "ascii" },
|
||||||
|
{ label: "Base64", value: "base64" },
|
||||||
|
],
|
||||||
|
defaultArgvs: {
|
||||||
|
command: {
|
||||||
|
value: "",
|
||||||
|
isString: true,
|
||||||
|
__varInputVal__: true,
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
cwd: {
|
||||||
|
value: "",
|
||||||
|
isString: true,
|
||||||
|
__varInputVal__: true,
|
||||||
|
},
|
||||||
|
env: {},
|
||||||
|
autoEncoding: true,
|
||||||
|
encoding: "buffer",
|
||||||
|
timeout: 0,
|
||||||
|
maxBuffer: 1024 * 1024, // 1MB
|
||||||
|
shell: {
|
||||||
|
value: "",
|
||||||
|
isString: true,
|
||||||
|
__varInputVal__: true,
|
||||||
|
},
|
||||||
|
windowsHide: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
argvs: {
|
||||||
|
get() {
|
||||||
|
return (
|
||||||
|
this.modelValue.argvs ||
|
||||||
|
this.parseCodeToArgvs(this.modelValue.code) ||
|
||||||
|
this.defaultArgvs
|
||||||
|
);
|
||||||
|
},
|
||||||
|
set(value) {
|
||||||
|
this.$emit("update:modelValue", {
|
||||||
|
...this.modelValue,
|
||||||
|
code: this.generateCode(value),
|
||||||
|
argvs: value,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
parseCodeToArgvs(code) {
|
||||||
|
if (!code) return null;
|
||||||
|
|
||||||
|
// 定义需要使用variable格式的路径
|
||||||
|
const variableFormatPaths = [
|
||||||
|
"arg0", // 命令字符串
|
||||||
|
"arg1.cwd", // 工作目录
|
||||||
|
"arg1.env.**", // 环境变量
|
||||||
|
];
|
||||||
|
|
||||||
|
// 解析代码
|
||||||
|
const result = parseFunction(code, { variableFormatPaths });
|
||||||
|
if (!result) return this.defaultArgvs;
|
||||||
|
|
||||||
|
// 返回解析结果
|
||||||
|
const [command, options = {}] = result.args;
|
||||||
|
return {
|
||||||
|
command: command || this.defaultArgvs.command,
|
||||||
|
options: {
|
||||||
|
...this.defaultArgvs.options,
|
||||||
|
...options,
|
||||||
|
cwd: options.cwd || this.defaultArgvs.options.cwd,
|
||||||
|
env: options.env || this.defaultArgvs.options.env,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
generateCode(argvs) {
|
||||||
|
const args = [];
|
||||||
|
|
||||||
|
// 添加命令
|
||||||
|
args.push(stringifyWithType(argvs.command));
|
||||||
|
|
||||||
|
// 添加选项
|
||||||
|
const options = { ...argvs.options };
|
||||||
|
// 移除默认值
|
||||||
|
Object.keys(options).forEach((key) => {
|
||||||
|
if (
|
||||||
|
JSON.stringify(options[key]) ===
|
||||||
|
JSON.stringify(this.defaultArgvs.options[key])
|
||||||
|
) {
|
||||||
|
delete options[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (Object.keys(options).length > 0) {
|
||||||
|
args.push(stringifyObject(options));
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${this.modelValue.value}(${args.join(", ")})`;
|
||||||
|
},
|
||||||
|
updateArgvs(key, value) {
|
||||||
|
if (key.includes(".")) {
|
||||||
|
// 处理嵌套属性,如 'options.encoding'
|
||||||
|
const [parent, child] = key.split(".");
|
||||||
|
this.argvs = {
|
||||||
|
...this.argvs,
|
||||||
|
[parent]: {
|
||||||
|
...this.argvs[parent],
|
||||||
|
[child]: value,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// 处理顶层属性
|
||||||
|
this.argvs = { ...this.argvs, [key]: value };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
if (!this.modelValue.argvs && !this.modelValue.code) {
|
||||||
|
this.$emit("update:modelValue", {
|
||||||
|
...this.modelValue,
|
||||||
|
code: this.generateCode(this.defaultArgvs),
|
||||||
|
argvs: this.defaultArgvs,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.q-expansion-item :deep(.q-expansion-item__container) {
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.command-input-wrapper {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
</style>
|
@ -8,6 +8,7 @@
|
|||||||
align="left"
|
align="left"
|
||||||
narrow-indicator
|
narrow-indicator
|
||||||
inline-label
|
inline-label
|
||||||
|
active-class="ubrowser-tabs-active"
|
||||||
>
|
>
|
||||||
<q-tab name="1" icon="settings" label="基础参数" size="sm" />
|
<q-tab name="1" icon="settings" label="基础参数" size="sm" />
|
||||||
<q-tab name="2" icon="touch_app" label="浏览器操作" size="sm" />
|
<q-tab name="2" icon="touch_app" label="浏览器操作" size="sm" />
|
||||||
@ -133,6 +134,10 @@ export default defineComponent({
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ubrowser-tabs-active {
|
||||||
|
color: var(--q-primary);
|
||||||
|
}
|
||||||
|
|
||||||
.ubrowser-tabs {
|
.ubrowser-tabs {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
@ -143,7 +148,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
.panel-content {
|
.panel-content {
|
||||||
padding: 16px;
|
padding: 8px;
|
||||||
min-height: 200px;
|
min-height: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,7 +284,7 @@ export default defineComponent({
|
|||||||
.map((key) => (!isMac && key === "command" ? "meta" : key));
|
.map((key) => (!isMac && key === "command" ? "meta" : key));
|
||||||
|
|
||||||
const args = [argvs.mainKey, ...activeModifiers];
|
const args = [argvs.mainKey, ...activeModifiers];
|
||||||
return `keyTap("${args.join('","')}")`;
|
return `${this.modelValue.value}("${args.join('","')}")`;
|
||||||
},
|
},
|
||||||
updateValue(argv) {
|
updateValue(argv) {
|
||||||
const newArgvs = {
|
const newArgvs = {
|
||||||
|
@ -49,7 +49,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
icon: {
|
icon: {
|
||||||
type: String,
|
type: String,
|
||||||
default: "pin",
|
default: "",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
emits: ["update:modelValue"],
|
emits: ["update:modelValue"],
|
||||||
|
@ -54,3 +54,8 @@ export const AsymmetricCryptoEditor = defineAsyncComponent(() =>
|
|||||||
export const FileOperationEditor = defineAsyncComponent(() =>
|
export const FileOperationEditor = defineAsyncComponent(() =>
|
||||||
import("components/composer/file/FileOperationEditor.vue")
|
import("components/composer/file/FileOperationEditor.vue")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// System Components
|
||||||
|
export const SystemCommandEditor = defineAsyncComponent(() =>
|
||||||
|
import("components/composer/system/SystemCommandEditor.vue")
|
||||||
|
);
|
||||||
|
@ -4,7 +4,7 @@ export const simulateCommands = {
|
|||||||
defaultOpened: false,
|
defaultOpened: false,
|
||||||
commands: [
|
commands: [
|
||||||
{
|
{
|
||||||
value: "keyTap",
|
value: "utools.simulateKeyboardTap",
|
||||||
label: "模拟按键",
|
label: "模拟按键",
|
||||||
config: [],
|
config: [],
|
||||||
component: "KeyEditor",
|
component: "KeyEditor",
|
||||||
|
@ -3,18 +3,6 @@ export const systemCommands = {
|
|||||||
icon: "computer",
|
icon: "computer",
|
||||||
defaultOpened: false,
|
defaultOpened: false,
|
||||||
commands: [
|
commands: [
|
||||||
{
|
|
||||||
value: "system",
|
|
||||||
label: "执行系统命令",
|
|
||||||
config: [
|
|
||||||
{
|
|
||||||
key: "command",
|
|
||||||
label: "要执行的命令行",
|
|
||||||
type: "varInput",
|
|
||||||
icon: "terminal",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
value: "copyTo",
|
value: "copyTo",
|
||||||
label: "将内容写入剪贴板",
|
label: "将内容写入剪贴板",
|
||||||
@ -33,5 +21,13 @@ export const systemCommands = {
|
|||||||
config: [],
|
config: [],
|
||||||
allowEmptyArgv: true,
|
allowEmptyArgv: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
value: "quickcomposer.system.exec",
|
||||||
|
label: "执行系统命令",
|
||||||
|
desc: "执行系统命令并返回输出结果",
|
||||||
|
config: [],
|
||||||
|
component: "SystemCommandEditor",
|
||||||
|
icon: "terminal",
|
||||||
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
@ -249,44 +249,124 @@ const customComponentGuide = {
|
|||||||
},
|
},
|
||||||
tips: "formatJsonVariables 主要用于处理对象中的变量,避免对简单参数使用,以免产生不必要的引号",
|
tips: "formatJsonVariables 主要用于处理对象中的变量,避免对简单参数使用,以免产生不必要的引号",
|
||||||
},
|
},
|
||||||
asyncCommand: "后端使用异步函数时,命令配置需要设置isAsync: true",
|
commonComponents: {
|
||||||
componentStructure: "参考现有组件的实现方式,保持一致的代码风格",
|
description: "常用组件的使用说明",
|
||||||
errorHandling: "前后端都需要适当的错误处理和提示",
|
components: {
|
||||||
typeChecking: "确保所有参数都有适当的类型检查",
|
VariableInput: {
|
||||||
codeGeneration: {
|
description: "变量输入组件",
|
||||||
description: "代码生成的关键点",
|
usage: "用于输入可能包含变量的字符串",
|
||||||
points: [
|
props: [
|
||||||
"1. 使用 generateCode 方法生成标准格式的代码",
|
"model-value - 输入值,需要包含 value、isString、__varInputVal__ 属性",
|
||||||
"2. 确保生成的代码可以被正确解析回参数",
|
"label - 输入框标签",
|
||||||
"3. 处理特殊字符和引号",
|
"icon - 输入框图标",
|
||||||
"4. 考虑不同类型参数的处理方式",
|
],
|
||||||
|
},
|
||||||
|
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: {
|
commonPitfalls: {
|
||||||
description: "代码解析的关键点",
|
description: "自定义组件开发中的常见错误和最佳实践",
|
||||||
points: [
|
componentRegistration: {
|
||||||
"1. 使用 parseCodeToArgvs 方法解析代码为参数",
|
description: "组件注册相关注意事项",
|
||||||
"2. 处理参数中的特殊字符和引号",
|
tips: [
|
||||||
"3. 正确识别参数类型",
|
"组件名使用 PascalCase 命名,如 SystemCommandEditor",
|
||||||
"4. 处理解析失败的情况",
|
"在 cardComponents.js 中注册时使用字符串形式,如 component: 'SystemCommandEditor'",
|
||||||
],
|
"不要在配置文件中导入组件",
|
||||||
},
|
"不要在配置文件中定义 defaultArgvs,这应该只在组件内部定义",
|
||||||
componentLifecycle: {
|
],
|
||||||
description: "组件生命周期管理",
|
},
|
||||||
points: [
|
codeGeneration: {
|
||||||
"1. mounted 中初始化默认值",
|
description: "代码生成相关注意事项",
|
||||||
"2. 清理事件监听器",
|
tips: [
|
||||||
"3. 处理组件销毁",
|
"使用 modelValue.value 获取函数名,避免硬编码",
|
||||||
],
|
"在 generateCode 中正确处理默认值的移除",
|
||||||
},
|
"正确定义 variableFormatPaths 以处理变量格式",
|
||||||
codeStyle: {
|
],
|
||||||
description: "代码风格规范",
|
},
|
||||||
points: [
|
dataManagement: {
|
||||||
"1. 保持代码结构清晰",
|
description: "数据管理相关注意事项",
|
||||||
"2. 使用有意义的变量名",
|
tips: [
|
||||||
"3. 添加必要的注释",
|
"使用 updateArgvs(key, value) 方法显式更新数据",
|
||||||
"4. 遵循 Vue 组件命名规范",
|
"在模板中使用 :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: {
|
examples: {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user