mirror of
https://github.com/fofolee/uTools-quickcommand.git
synced 2025-06-28 03:42:51 +08:00
各种调整
This commit is contained in:
parent
cdfb2b502f
commit
e8d12b95d4
8
.gitattributes
vendored
Normal file
8
.gitattributes
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
# 自动识别文本文件并进行换行符标准化
|
||||
* text=auto
|
||||
|
||||
# 指定特定文件类型的换行符处理
|
||||
*.txt text
|
||||
*.js text
|
||||
*.css text
|
||||
*.html text
|
2626
plugin/package-lock.json
generated
2626
plugin/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,10 +1,10 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"axios": "^0.24.0",
|
||||
"iconv-lite": "^0.6.3",
|
||||
"lodash": "^4.17.21",
|
||||
"ses": "^0.15.15",
|
||||
"sharp": "^0.33.5",
|
||||
"tree-kill": "^1.2.2"
|
||||
}
|
||||
"dependencies": {
|
||||
"axios": "^0.24.0",
|
||||
"iconv-lite": "^0.6.3",
|
||||
"jimp": "^0.22.12",
|
||||
"lodash": "^4.17.21",
|
||||
"ses": "^0.15.15",
|
||||
"tree-kill": "^1.2.2"
|
||||
}
|
||||
}
|
||||
|
@ -12,8 +12,7 @@ const url = require("url");
|
||||
const kill = require("tree-kill");
|
||||
const crypto = require("crypto");
|
||||
require("ses");
|
||||
const sharp = require("sharp");
|
||||
|
||||
const Jimp = require("jimp");
|
||||
const md5 = (input) => {
|
||||
return crypto.createHash("md5").update(input, "utf8").digest("hex");
|
||||
};
|
||||
@ -789,17 +788,19 @@ window.quickcommandHttpServer = () => {
|
||||
};
|
||||
};
|
||||
|
||||
// 处理背景图片
|
||||
window.imageProcessor = async (imagePath) => {
|
||||
try {
|
||||
// 读取图片
|
||||
let image = sharp(imagePath);
|
||||
let metadata = await image.metadata();
|
||||
const image = await Jimp.read(imagePath);
|
||||
|
||||
// 设置固定目标尺寸
|
||||
// 获取原始尺寸
|
||||
const originalWidth = image.getWidth();
|
||||
const originalHeight = image.getHeight();
|
||||
const ratio = originalWidth / originalHeight;
|
||||
|
||||
// 设置目标尺寸
|
||||
let targetWidth = 1280;
|
||||
let targetHeight = 720;
|
||||
const ratio = metadata.width / metadata.height;
|
||||
|
||||
if (ratio > 16 / 9) {
|
||||
targetHeight = Math.min(720, Math.round(targetWidth / ratio));
|
||||
@ -808,15 +809,14 @@ window.imageProcessor = async (imagePath) => {
|
||||
}
|
||||
|
||||
// 调整大小并压缩
|
||||
let processedBuffer = await image
|
||||
.resize(targetWidth, targetHeight, {
|
||||
fit: "contain",
|
||||
background: { r: 0, g: 0, b: 0, alpha: 0 },
|
||||
})
|
||||
.jpeg({ quality: 80, progressive: true })
|
||||
.toBuffer();
|
||||
await image
|
||||
.resize(targetWidth, targetHeight, Jimp.RESIZE_BICUBIC)
|
||||
.quality(80);
|
||||
|
||||
return `data:image/jpeg;base64,${processedBuffer.toString("base64")}`;
|
||||
// 转换为 base64
|
||||
const base64 = await image.getBase64Async(Jimp.MIME_JPEG);
|
||||
|
||||
return base64;
|
||||
} catch (error) {
|
||||
console.error("处理图片失败:", error);
|
||||
return null;
|
||||
|
@ -111,7 +111,6 @@ export default defineComponent({
|
||||
argv: "",
|
||||
argvType: "string",
|
||||
saveOutput: false,
|
||||
useOutput: null,
|
||||
outputVariable: null,
|
||||
cmd: action.value || action.cmd,
|
||||
value: action.value || action.cmd,
|
||||
@ -122,8 +121,6 @@ export default defineComponent({
|
||||
|
||||
this.commandFlow.forEach((cmd) => {
|
||||
let line = "";
|
||||
// TODO: 切换到变量后还是string类型
|
||||
console.log("Generating code for command:", cmd);
|
||||
|
||||
if (cmd.outputVariable) {
|
||||
line += `let ${cmd.outputVariable} = `;
|
||||
@ -131,14 +128,8 @@ export default defineComponent({
|
||||
|
||||
if (cmd.value === "ubrowser") {
|
||||
line += cmd.argv;
|
||||
} else if (cmd.useOutput !== null) {
|
||||
const outputVar = this.commandFlow[cmd.useOutput].outputVariable;
|
||||
line += `${cmd.value}(${outputVar})`;
|
||||
} else {
|
||||
const needQuotes =
|
||||
cmd.argvType === "string" && cmd.argvType !== "variable";
|
||||
const argv = needQuotes ? `"${cmd.argv}"` : cmd.argv;
|
||||
line += `${cmd.value}(${argv})`;
|
||||
line += `${cmd.value}(${cmd.argv})`;
|
||||
}
|
||||
|
||||
code.push(line);
|
||||
|
@ -89,9 +89,9 @@
|
||||
<VariableInput
|
||||
v-model="argvLocal"
|
||||
:label="placeholder"
|
||||
:command="command"
|
||||
class="col"
|
||||
ref="variableInput"
|
||||
@update:type="handleArgvTypeUpdate"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
@ -106,6 +106,7 @@ import { defineComponent, inject } from "vue";
|
||||
import KeyEditor from "./KeyEditor.vue";
|
||||
import UBrowserEditor from "./ubrowser/UBrowserEditor.vue";
|
||||
import VariableInput from "./VariableInput.vue";
|
||||
import { validateVariableName } from "js/common/variableValidator";
|
||||
|
||||
export default defineComponent({
|
||||
name: "ComposerCard",
|
||||
@ -137,13 +138,7 @@ export default defineComponent({
|
||||
showKeyRecorder: false,
|
||||
};
|
||||
},
|
||||
emits: [
|
||||
"remove",
|
||||
"toggle-output",
|
||||
"update:argv",
|
||||
"update:use-output",
|
||||
"update:command",
|
||||
],
|
||||
emits: ["remove", "toggle-output", "update:argv", "update:command"],
|
||||
computed: {
|
||||
saveOutputLocal: {
|
||||
get() {
|
||||
@ -158,31 +153,26 @@ export default defineComponent({
|
||||
return this.command.argv;
|
||||
},
|
||||
set(value) {
|
||||
this.$emit("update:argv", value);
|
||||
},
|
||||
},
|
||||
useOutputLocal: {
|
||||
get() {
|
||||
return this.command.useOutput;
|
||||
},
|
||||
set(value) {
|
||||
this.$emit("update:use-output", value);
|
||||
const updatedCommand = {
|
||||
...this.command,
|
||||
argv: value,
|
||||
};
|
||||
this.$emit("update:command", updatedCommand);
|
||||
},
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const addVariable = inject("addVariable");
|
||||
const removeVariable = inject("removeVariable");
|
||||
const variables = inject("composerVariables", []);
|
||||
|
||||
return {
|
||||
addVariable,
|
||||
removeVariable,
|
||||
variables,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
handleClearOutput() {
|
||||
this.$emit("update:use-output", null);
|
||||
},
|
||||
handleKeyRecord(keys) {
|
||||
this.showKeyRecorder = false;
|
||||
// 从keyTap("a","control")格式中提取参数
|
||||
@ -200,6 +190,19 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
handleOutputVariableUpdate(value) {
|
||||
// 检查变量名是否合法
|
||||
const validation = validateVariableName(value);
|
||||
if (!validation.isValid) {
|
||||
quickcommand.showMessageBox(validation.error, "warning");
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查变量名是否重复
|
||||
if (this.variables.some((v) => v.name === value)) {
|
||||
quickcommand.showMessageBox(`变量名 "${value}" 已经存在`, "warning");
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建命令的副本并更新
|
||||
const updatedCommand = {
|
||||
...this.command,
|
||||
@ -210,14 +213,6 @@ export default defineComponent({
|
||||
// 处理变量管理
|
||||
this.handleOutputVariableChange(value);
|
||||
},
|
||||
handleArgvTypeUpdate(type) {
|
||||
console.log("Type updated in card:", type);
|
||||
const updatedCommand = {
|
||||
...this.command,
|
||||
argvType: type,
|
||||
};
|
||||
this.$emit("update:command", updatedCommand);
|
||||
},
|
||||
handleToggleOutput() {
|
||||
// 创建命令的副本
|
||||
const updatedCommand = {
|
||||
@ -394,7 +389,7 @@ export default defineComponent({
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
/* 输入框内部样式优化 */
|
||||
/* 输入框内部样式美化 */
|
||||
.output-section :deep(.q-field__control) {
|
||||
height: 28px;
|
||||
min-height: 28px;
|
||||
|
@ -30,12 +30,10 @@
|
||||
>
|
||||
<ComposerCard
|
||||
:command="element"
|
||||
:available-outputs="getAvailableOutputs(index)"
|
||||
:placeholder="getPlaceholder(element, index)"
|
||||
@remove="removeCommand(index)"
|
||||
@toggle-output="toggleSaveOutput(index)"
|
||||
@update:argv="(val) => handleArgvChange(index, val)"
|
||||
@update:use-output="(val) => handleUseOutputChange(index, val)"
|
||||
@update:command="(val) => updateCommand(index, val)"
|
||||
/>
|
||||
</div>
|
||||
@ -54,7 +52,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "vue";
|
||||
import { defineComponent, inject } from "vue";
|
||||
import draggable from "vuedraggable";
|
||||
import ComposerCard from "./ComposerCard.vue";
|
||||
|
||||
@ -81,6 +79,13 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const removeVariable = inject("removeVariable");
|
||||
|
||||
return {
|
||||
removeVariable,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dragIndex: -1,
|
||||
@ -135,45 +140,59 @@ export default defineComponent({
|
||||
},
|
||||
|
||||
onDrop(event) {
|
||||
const actionData = JSON.parse(event.dataTransfer.getData("action"));
|
||||
const newCommand = {
|
||||
...actionData,
|
||||
id: Date.now(), // 或使用其他方式生成唯一ID
|
||||
argv: "",
|
||||
saveOutput: false,
|
||||
useOutput: null,
|
||||
cmd: actionData.value || actionData.cmd,
|
||||
value: actionData.value || actionData.cmd,
|
||||
};
|
||||
try {
|
||||
// 尝试获取拖拽数据
|
||||
const actionData = event.dataTransfer.getData("action");
|
||||
|
||||
const newCommands = [...this.commands];
|
||||
if (this.dragIndex >= 0) {
|
||||
newCommands.splice(this.dragIndex, 0, newCommand);
|
||||
} else {
|
||||
newCommands.push(newCommand);
|
||||
// 如果没有action数据,说明是内部排序,直接返回
|
||||
if (!actionData) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 解析外部拖入的新命令数据
|
||||
const parsedAction = JSON.parse(actionData);
|
||||
|
||||
const newCommand = {
|
||||
...parsedAction,
|
||||
id: Date.now(),
|
||||
argv: "",
|
||||
saveOutput: false,
|
||||
useOutput: null,
|
||||
outputVariable: null,
|
||||
cmd: parsedAction.value || parsedAction.cmd,
|
||||
value: parsedAction.value || parsedAction.cmd,
|
||||
};
|
||||
|
||||
const newCommands = [...this.commands];
|
||||
if (this.dragIndex >= 0) {
|
||||
newCommands.splice(this.dragIndex, 0, newCommand);
|
||||
} else {
|
||||
newCommands.push(newCommand);
|
||||
}
|
||||
|
||||
this.$emit("update:modelValue", newCommands);
|
||||
this.dragIndex = -1;
|
||||
|
||||
document.querySelectorAll(".dragging").forEach((el) => {
|
||||
el.classList.remove("dragging");
|
||||
});
|
||||
} catch (error) {
|
||||
// 忽略内部拖动排序的错误
|
||||
console.debug("Internal drag & drop reorder", error);
|
||||
}
|
||||
|
||||
this.$emit("update:modelValue", newCommands);
|
||||
this.dragIndex = -1;
|
||||
|
||||
document.querySelectorAll(".dragging").forEach((el) => {
|
||||
el.classList.remove("dragging");
|
||||
});
|
||||
},
|
||||
removeCommand(index) {
|
||||
const command = this.commands[index];
|
||||
// 如果命令有输出变量,需要先清理
|
||||
if (command.outputVariable) {
|
||||
this.removeVariable(command.outputVariable);
|
||||
}
|
||||
const newCommands = [...this.commands];
|
||||
newCommands.splice(index, 1);
|
||||
this.$emit("update:modelValue", newCommands);
|
||||
},
|
||||
getAvailableOutputs(currentIndex) {
|
||||
return this.commands
|
||||
.slice(0, currentIndex)
|
||||
.map((cmd, index) => ({
|
||||
label: `${cmd.label} 的输出`,
|
||||
value: index,
|
||||
disable: !cmd.saveOutput,
|
||||
}))
|
||||
.filter((item) => !item.disable);
|
||||
getPlaceholder(element, index) {
|
||||
return element.desc;
|
||||
},
|
||||
toggleSaveOutput(index) {
|
||||
const newCommands = [...this.commands];
|
||||
@ -195,22 +214,7 @@ export default defineComponent({
|
||||
};
|
||||
this.$emit("update:modelValue", newCommands);
|
||||
},
|
||||
handleUseOutputChange(index, value) {
|
||||
const newCommands = [...this.commands];
|
||||
newCommands[index].useOutput = value;
|
||||
if (value !== null) {
|
||||
newCommands[index].argv = "";
|
||||
}
|
||||
this.$emit("update:modelValue", newCommands);
|
||||
},
|
||||
getPlaceholder(element, index) {
|
||||
if (element.useOutput !== null) {
|
||||
return `使用 ${this.commands[element.useOutput].label} 的输出`;
|
||||
}
|
||||
return element.desc;
|
||||
},
|
||||
updateCommand(index, updatedCommand) {
|
||||
console.log("Command updated in flow:", updatedCommand);
|
||||
const newCommands = [...this.commands];
|
||||
newCommands[index] = {
|
||||
...newCommands[index],
|
||||
@ -289,7 +293,7 @@ export default defineComponent({
|
||||
border-color: #676666;
|
||||
}
|
||||
|
||||
/* 滑动淡出<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>画 */
|
||||
/* 滑动淡出动画 */
|
||||
.slide-fade-enter-active,
|
||||
.slide-fade-leave-active {
|
||||
transition: all 0.3s ease;
|
||||
|
@ -65,7 +65,7 @@ export default defineComponent({
|
||||
"action",
|
||||
JSON.stringify({
|
||||
...command,
|
||||
cmd: command.value || command.cmd,
|
||||
value: command.value,
|
||||
})
|
||||
);
|
||||
event.target.classList.add("dragging");
|
||||
|
@ -66,14 +66,10 @@
|
||||
<template #item="{ element, index }">
|
||||
<ComposerCard
|
||||
:command="element"
|
||||
:available-outputs="getAvailableOutputs(index, 'if')"
|
||||
:placeholder="getPlaceholder(element, index, 'if')"
|
||||
@remove="removeCommand('if', index)"
|
||||
@toggle-output="toggleSaveOutput('if', index)"
|
||||
@update:argv="(val) => handleArgvChange('if', index, val)"
|
||||
@update:use-output="
|
||||
(val) => handleUseOutputChange('if', index, val)
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
</draggable>
|
||||
@ -108,14 +104,10 @@
|
||||
<template #item="{ element, index }">
|
||||
<ComposerCard
|
||||
:command="element"
|
||||
:available-outputs="getAvailableOutputs(index, 'else')"
|
||||
:placeholder="getPlaceholder(element, index, 'else')"
|
||||
@remove="removeCommand('else', index)"
|
||||
@toggle-output="toggleSaveOutput('else', index)"
|
||||
@update:argv="(val) => handleArgvChange('else', index, val)"
|
||||
@update:use-output="
|
||||
(val) => handleUseOutputChange('else', index, val)
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
</draggable>
|
||||
@ -294,20 +286,6 @@ export default defineComponent({
|
||||
});
|
||||
},
|
||||
|
||||
getAvailableOutputs(currentIndex, branch) {
|
||||
// 获取当前分支的命令列表
|
||||
const commands = branch === "if" ? this.ifCommands : this.elseCommands;
|
||||
|
||||
return commands
|
||||
.slice(0, currentIndex)
|
||||
.map((cmd, index) => ({
|
||||
label: `${cmd.label} 的输出`,
|
||||
value: index,
|
||||
disable: !cmd.saveOutput,
|
||||
}))
|
||||
.filter((item) => !item.disable);
|
||||
},
|
||||
|
||||
toggleSaveOutput(branch, index) {
|
||||
const commands =
|
||||
branch === "if" ? [...this.ifCommands] : [...this.elseCommands];
|
||||
@ -338,19 +316,6 @@ export default defineComponent({
|
||||
});
|
||||
},
|
||||
|
||||
handleUseOutputChange(branch, index, value) {
|
||||
const commands =
|
||||
branch === "if" ? [...this.ifCommands] : [...this.elseCommands];
|
||||
commands[index].useOutput = value;
|
||||
if (value !== null) {
|
||||
commands[index].argv = "";
|
||||
}
|
||||
this.$emit("update:modelValue", {
|
||||
...this.modelValue,
|
||||
[branch]: commands,
|
||||
});
|
||||
},
|
||||
|
||||
getPlaceholder(element, index, branch) {
|
||||
if (element.useOutput !== null) {
|
||||
const commands = branch === "if" ? this.ifCommands : this.elseCommands;
|
||||
|
@ -1,33 +1,12 @@
|
||||
<template>
|
||||
<q-input
|
||||
v-model="inputValue"
|
||||
v-if="!isNumber"
|
||||
v-model="localValue"
|
||||
dense
|
||||
outlined
|
||||
:label="label"
|
||||
class="variable-input"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-btn
|
||||
flat
|
||||
dense
|
||||
round
|
||||
:icon="isString ? 'format_quote' : 'format_quote'"
|
||||
size="sm"
|
||||
:class="{
|
||||
'text-primary': isString,
|
||||
'text-grey-6': !isString,
|
||||
}"
|
||||
class="string-toggle"
|
||||
@click="toggleStringType"
|
||||
>
|
||||
<q-tooltip>{{
|
||||
isString
|
||||
? "当前类型是:字符串,点击切换"
|
||||
: "当前类型是:变量、数字、表达式等,点击切换"
|
||||
}}</q-tooltip>
|
||||
</q-btn>
|
||||
</template>
|
||||
|
||||
<template v-slot:append>
|
||||
<q-btn
|
||||
v-if="hasSelectedVariable"
|
||||
@ -41,11 +20,25 @@
|
||||
>
|
||||
<q-tooltip>清除选中的变量</q-tooltip>
|
||||
</q-btn>
|
||||
<q-btn
|
||||
flat
|
||||
dense
|
||||
round
|
||||
:icon="isString ? 'format_quote' : 'data_object'"
|
||||
size="sm"
|
||||
class="string-toggle"
|
||||
@click="toggleStringType"
|
||||
>
|
||||
<q-tooltip>{{
|
||||
isString
|
||||
? "当前类型是:字符串,点击切换"
|
||||
: "当前类型是:变量、数字、表达式等,点击切换"
|
||||
}}</q-tooltip>
|
||||
</q-btn>
|
||||
|
||||
<q-btn-dropdown
|
||||
flat
|
||||
dense
|
||||
:icon="hasSelectedVariable ? 'data_object' : 'functions'"
|
||||
:class="{
|
||||
'text-primary': hasSelectedVariable,
|
||||
'text-grey-6': !hasSelectedVariable,
|
||||
@ -92,84 +85,174 @@
|
||||
</q-list>
|
||||
</q-btn-dropdown>
|
||||
</template>
|
||||
<template v-slot:prepend>
|
||||
<q-icon :name="command.icon || commandIcons[command.value] || 'code'" />
|
||||
</template>
|
||||
</q-input>
|
||||
<!-- 强制为数字类型时,不支持切换类型 -->
|
||||
<q-input
|
||||
v-else
|
||||
type="number"
|
||||
v-model.number="localValue"
|
||||
dense
|
||||
outlined
|
||||
:label="label"
|
||||
class="number-input"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon :name="command.icon || commandIcons[command.value] || 'code'" />
|
||||
</template>
|
||||
<template v-slot:append>
|
||||
<!-- <q-icon name="pin" size="xs" /> -->
|
||||
<div class="column items-center number-controls">
|
||||
<q-btn
|
||||
flat
|
||||
dense
|
||||
round
|
||||
icon="keyboard_arrow_up"
|
||||
size="xs"
|
||||
class="number-btn"
|
||||
@click="updateNumber(100)"
|
||||
/>
|
||||
<q-btn
|
||||
flat
|
||||
dense
|
||||
round
|
||||
icon="keyboard_arrow_down"
|
||||
size="xs"
|
||||
class="number-btn"
|
||||
@click="updateNumber(-100)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</q-input>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, inject, computed } from "vue";
|
||||
import { defineComponent, inject } from "vue";
|
||||
import { commandIcons } from "js/composer/composerConfig";
|
||||
|
||||
export default defineComponent({
|
||||
name: "VariableInput",
|
||||
|
||||
props: {
|
||||
modelValue: String,
|
||||
modelValue: [String, Number],
|
||||
label: String,
|
||||
command: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
emits: ["update:modelValue", "update:type"],
|
||||
emits: ["update:modelValue"],
|
||||
|
||||
setup() {
|
||||
const variables = inject("composerVariables", []);
|
||||
return { variables };
|
||||
return { variables, commandIcons };
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
isString: true,
|
||||
selectedVariable: null,
|
||||
// 根据输入类型初始化字符串状态
|
||||
isString: this.command.inputType !== "number",
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
inputValue: {
|
||||
localValue: {
|
||||
get() {
|
||||
return this.modelValue;
|
||||
// 数字类型直接返回原值
|
||||
if (this.isNumber) return this.modelValue;
|
||||
// 非数字类型时,根据isString状态决定是否显示引号
|
||||
const val = this.modelValue || "";
|
||||
return this.isString ? val.replace(/^"|"$/g, "") : val;
|
||||
},
|
||||
set(value) {
|
||||
this.$emit("update:modelValue", value);
|
||||
this.$emit("update:modelValue", this.formatValue(value));
|
||||
},
|
||||
},
|
||||
|
||||
// 判断是否有选中的变量,用于控制UI显示和行为
|
||||
hasSelectedVariable() {
|
||||
return this.selectedVariable !== null;
|
||||
},
|
||||
|
||||
isNumber() {
|
||||
return this.command.inputType === "number";
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 格式化值,处理引号的添加和移除
|
||||
formatValue(value) {
|
||||
// 空值、变量模式或数字类型时不处理
|
||||
if (!value || this.hasSelectedVariable || this.isNumber) return value;
|
||||
// 根据isString状态添加或移除引号
|
||||
return this.isString
|
||||
? `"${value.replace(/^"|"$/g, "")}"`
|
||||
: value.replace(/^"|"$/g, "");
|
||||
},
|
||||
|
||||
// 切换字符串/非字符串模式
|
||||
toggleStringType() {
|
||||
if (!this.hasSelectedVariable) {
|
||||
this.isString = !this.isString;
|
||||
this.$emit("update:type", this.isString ? "string" : "number");
|
||||
// 有值时需要重新格式化
|
||||
if (this.modelValue) {
|
||||
this.$emit("update:modelValue", this.formatValue(this.modelValue));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 外部调用的方法,用于设置字符串状态
|
||||
setIsString(value) {
|
||||
if (!this.hasSelectedVariable && this.isString !== value) {
|
||||
this.isString = value;
|
||||
// 有值时需要重新格式化
|
||||
if (this.modelValue) {
|
||||
this.$emit("update:modelValue", this.formatValue(this.modelValue));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 插入变量时的处理
|
||||
insertVariable(variable) {
|
||||
this.selectedVariable = variable;
|
||||
this.isString = false;
|
||||
this.$emit("update:type", "variable");
|
||||
this.isString = false; // 变量模式下不需要字符串处理
|
||||
this.$emit("update:modelValue", variable.name);
|
||||
},
|
||||
|
||||
// 清除变量时的处理
|
||||
clearVariable() {
|
||||
this.selectedVariable = null;
|
||||
this.isString = true;
|
||||
this.$emit("update:type", "string");
|
||||
this.isString = true; // 恢复到字符串模式
|
||||
this.$emit("update:modelValue", "");
|
||||
},
|
||||
|
||||
// 数字类型特有的增减处理
|
||||
updateNumber(delta) {
|
||||
const current = Number(this.localValue) || 0;
|
||||
this.$emit("update:modelValue", current + delta);
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
modelValue(newVal) {
|
||||
if (this.selectedVariable && newVal !== this.selectedVariable.name) {
|
||||
this.selectedVariable = null;
|
||||
this.isString = true;
|
||||
this.$emit("update:type", "string");
|
||||
}
|
||||
// 解决通过外部传入值时,无法触发字符串处理的问题
|
||||
modelValue: {
|
||||
immediate: true,
|
||||
handler(newVal) {
|
||||
// 只在有值且非变量模式且非数字类型时处理
|
||||
if (newVal && !this.hasSelectedVariable && !this.isNumber) {
|
||||
const formattedValue = this.formatValue(newVal);
|
||||
// 只在值真正需要更新时才<EFBFBD><EFBFBD><EFBFBD>发更新
|
||||
if (formattedValue !== newVal) {
|
||||
this.$emit("update:modelValue", formattedValue);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$emit("update:type", "string");
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -223,7 +306,7 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
.variable-item:hover {
|
||||
background: rgba(var(--q-primary-rgb), 0.1);
|
||||
background: var(--q-primary-opacity-10);
|
||||
}
|
||||
|
||||
.variable-name {
|
||||
@ -252,4 +335,45 @@ export default defineComponent({
|
||||
transform: scale(1.1);
|
||||
color: var(--q-negative);
|
||||
}
|
||||
|
||||
/* 数字输入框样式 */
|
||||
.number-input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 隐藏默认的数字输入框箭头 - Chrome, Safari, Edge, Opera */
|
||||
.number-input :deep(input[type="number"]::-webkit-outer-spin-button),
|
||||
.number-input :deep(input[type="number"]::-webkit-inner-spin-button) {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.number-input :deep(.q-field__control) {
|
||||
padding-left: 8px;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.number-controls {
|
||||
border-left: 1px solid rgba(0, 0, 0, 0.12);
|
||||
margin-left: 4px;
|
||||
padding: 2px 4px;
|
||||
}
|
||||
|
||||
.body--dark .number-controls {
|
||||
border-left-color: rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
|
||||
.number-btn {
|
||||
opacity: 0.7;
|
||||
font-size: 18px;
|
||||
padding: 2px;
|
||||
margin: 0;
|
||||
min-height: 20px;
|
||||
min-width: 24px;
|
||||
}
|
||||
|
||||
.number-btn:hover {
|
||||
opacity: 1;
|
||||
color: var(--q-primary);
|
||||
}
|
||||
</style>
|
||||
|
@ -2,49 +2,34 @@
|
||||
<div class="row q-col-gutter-sm">
|
||||
<!-- 基础配置 -->
|
||||
<div class="col-12">
|
||||
<q-input
|
||||
<VariableInput
|
||||
v-model="localConfigs.goto.url"
|
||||
label="网址"
|
||||
dense
|
||||
outlined
|
||||
:command="{ icon: 'link' }"
|
||||
@update:model-value="updateConfigs"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="link" />
|
||||
</template>
|
||||
</q-input>
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Headers配置 -->
|
||||
<div class="col-12">
|
||||
<div class="row q-col-gutter-sm">
|
||||
<div class="col-12">
|
||||
<q-input
|
||||
<VariableInput
|
||||
v-model="localConfigs.goto.headers.Referer"
|
||||
label="Referer"
|
||||
dense
|
||||
outlined
|
||||
:command="{ icon: 'link' }"
|
||||
@update:model-value="updateConfigs"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="link" />
|
||||
</template>
|
||||
</q-input>
|
||||
/>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<div class="row q-col-gutter-sm">
|
||||
<div class="col">
|
||||
<q-input
|
||||
<VariableInput
|
||||
v-model="localConfigs.goto.headers.userAgent"
|
||||
label="User-Agent"
|
||||
dense
|
||||
outlined
|
||||
:command="{ icon: 'devices' }"
|
||||
@update:model-value="updateConfigs"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="devices" />
|
||||
</template>
|
||||
</q-input>
|
||||
/>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<q-select
|
||||
@ -70,27 +55,25 @@
|
||||
|
||||
<!-- 超时配置 -->
|
||||
<div class="col-12">
|
||||
<q-input
|
||||
v-model.number="localConfigs.goto.timeout"
|
||||
type="number"
|
||||
<VariableInput
|
||||
v-model="localConfigs.goto.timeout"
|
||||
:command="{ icon: 'timer', inputType: 'number' }"
|
||||
label="超时时间(ms)"
|
||||
dense
|
||||
outlined
|
||||
@update:model-value="updateConfigs"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="timer" />
|
||||
</template>
|
||||
</q-input>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "vue";
|
||||
import VariableInput from "components/editor/composer/VariableInput.vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "UBrowserBasic",
|
||||
components: {
|
||||
VariableInput,
|
||||
},
|
||||
props: {
|
||||
configs: {
|
||||
type: Object,
|
||||
|
@ -95,12 +95,11 @@ export default defineComponent({
|
||||
this.configs = newConfigs;
|
||||
},
|
||||
removeAction(action) {
|
||||
const index = this.selectedActions.findIndex(
|
||||
(a) => a.value === action.value
|
||||
);
|
||||
if (index > -1) {
|
||||
this.selectedActions.splice(index, 1);
|
||||
}
|
||||
const newActions = this.selectedActions.filter((a) => a.id !== action.id);
|
||||
this.selectedActions = newActions;
|
||||
const newConfigs = { ...this.configs };
|
||||
delete newConfigs[action.value];
|
||||
this.configs = newConfigs;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
|
@ -4,10 +4,8 @@
|
||||
<!-- 操作选择网格 -->
|
||||
<div class="row q-col-gutter-xs">
|
||||
<div
|
||||
v-for="[actionName, { label }] in Object.entries(
|
||||
ubrowserOperationConfigs
|
||||
)"
|
||||
:key="actionName"
|
||||
v-for="action in ubrowserOperationConfigs"
|
||||
:key="action.value"
|
||||
class="col-2"
|
||||
>
|
||||
<q-card
|
||||
@ -16,13 +14,13 @@
|
||||
class="action-card cursor-pointer"
|
||||
:class="{
|
||||
'action-selected': selectedActions.some(
|
||||
(a) => a.value === actionName
|
||||
(a) => a.value === action.value
|
||||
),
|
||||
}"
|
||||
@click="toggleAction({ value: actionName, label: label })"
|
||||
@click="toggleAction(action)"
|
||||
>
|
||||
<div class="q-pa-xs text-caption text-wrap text-center">
|
||||
{{ label }}
|
||||
{{ action.label }}
|
||||
</div>
|
||||
</q-card>
|
||||
</div>
|
||||
@ -45,9 +43,7 @@
|
||||
<q-avatar color="primary">
|
||||
<q-icon
|
||||
color="white"
|
||||
:name="
|
||||
ubrowserOperationConfigs[action.value].icon || 'touch_app'
|
||||
"
|
||||
:name="getActionProps(action, 'icon') || 'touch_app'"
|
||||
size="14px"
|
||||
/>
|
||||
</q-avatar>
|
||||
@ -76,11 +72,11 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="ubrowserOperationConfigs[action.value].config">
|
||||
<div v-if="getActionProps(action, 'config')">
|
||||
<UBrowserOperation
|
||||
:configs="configs"
|
||||
:action="action.value"
|
||||
:fields="ubrowserOperationConfigs[action.value].config"
|
||||
:fields="getActionProps(action, 'config')"
|
||||
@update:configs="$emit('update:configs', $event)"
|
||||
/>
|
||||
</div>
|
||||
@ -147,7 +143,7 @@ export default defineComponent({
|
||||
]);
|
||||
|
||||
// 初始化配置对象
|
||||
const { config } = this.ubrowserOperationConfigs[action.value];
|
||||
const { config } = action;
|
||||
if (config) {
|
||||
const newConfigs = { ...this.configs };
|
||||
if (!newConfigs[action.value]) {
|
||||
@ -168,6 +164,11 @@ export default defineComponent({
|
||||
this.$emit("update:selectedActions", newActions);
|
||||
}
|
||||
},
|
||||
getActionProps(action, key) {
|
||||
return this.ubrowserOperationConfigs.find(
|
||||
(a) => a.value === action.value
|
||||
)[key];
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@ -8,22 +8,20 @@
|
||||
>
|
||||
<div class="row items-center q-gutter-x-sm">
|
||||
<div class="col">
|
||||
<q-input
|
||||
<VariableInput
|
||||
:model-value="cookie.name"
|
||||
label="名称"
|
||||
dense
|
||||
outlined
|
||||
:command="{ icon: 'label' }"
|
||||
@update:model-value="
|
||||
(value) => handleUpdate(index, 'name', value)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<div class="col">
|
||||
<q-input
|
||||
<VariableInput
|
||||
:model-value="cookie.value"
|
||||
label="值"
|
||||
dense
|
||||
outlined
|
||||
:command="{ icon: 'edit' }"
|
||||
@update:model-value="
|
||||
(value) => handleUpdate(index, 'value', value)
|
||||
"
|
||||
@ -56,9 +54,13 @@
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "vue";
|
||||
import VariableInput from "components/editor/composer/VariableInput.vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "UBrowserCookieList",
|
||||
components: {
|
||||
VariableInput,
|
||||
},
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Array,
|
||||
|
@ -1,17 +1,12 @@
|
||||
<template>
|
||||
<div class="row q-col-gutter-sm">
|
||||
<div class="col">
|
||||
<q-input
|
||||
<VariableInput
|
||||
:command="{ icon: icon }"
|
||||
:model-value="modelValue"
|
||||
:label="label"
|
||||
dense
|
||||
outlined
|
||||
@update:model-value="$emit('update:modelValue', $event)"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon :name="icon" />
|
||||
</template>
|
||||
</q-input>
|
||||
/>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<q-select
|
||||
@ -36,9 +31,13 @@
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "vue";
|
||||
import VariableInput from "components/editor/composer/VariableInput.vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "UBrowserDeviceName",
|
||||
components: {
|
||||
VariableInput,
|
||||
},
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
|
@ -1,41 +1,33 @@
|
||||
<template>
|
||||
<div class="row q-col-gutter-sm">
|
||||
<div class="col-6">
|
||||
<q-input
|
||||
<VariableInput
|
||||
v-model.number="size.width"
|
||||
type="number"
|
||||
label="宽度"
|
||||
dense
|
||||
outlined
|
||||
:command="{ icon: 'width', inputType: 'number' }"
|
||||
@update:model-value="handleUpdate"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="width" />
|
||||
</template>
|
||||
</q-input>
|
||||
/>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<q-input
|
||||
<VariableInput
|
||||
v-model.number="size.height"
|
||||
type="number"
|
||||
label="高度"
|
||||
dense
|
||||
outlined
|
||||
:command="{ icon: 'height', inputType: 'number' }"
|
||||
@update:model-value="handleUpdate"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="height" />
|
||||
</template>
|
||||
</q-input>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "vue";
|
||||
import VariableInput from "components/editor/composer/VariableInput.vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "UBrowserDeviceSize",
|
||||
components: {
|
||||
VariableInput,
|
||||
},
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Object,
|
||||
|
@ -8,11 +8,10 @@
|
||||
>
|
||||
<div class="row q-col-gutter-sm">
|
||||
<div class="col">
|
||||
<q-input
|
||||
<VariableInput
|
||||
:model-value="modelValue[index]"
|
||||
label="文件路径"
|
||||
dense
|
||||
outlined
|
||||
:command="{ icon: 'folder' }"
|
||||
@update:model-value="(value) => handleUpdate(index, value)"
|
||||
/>
|
||||
</div>
|
||||
@ -43,9 +42,13 @@
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "vue";
|
||||
import VariableInput from "components/editor/composer/VariableInput.vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "UBrowserFileList",
|
||||
components: {
|
||||
VariableInput,
|
||||
},
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Array,
|
||||
|
@ -1,41 +0,0 @@
|
||||
<template>
|
||||
<q-input
|
||||
:model-value="modelValue"
|
||||
:label="label"
|
||||
:type="inputType"
|
||||
dense
|
||||
outlined
|
||||
@update:model-value="$emit('update:modelValue', $event)"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon :name="icon" />
|
||||
</template>
|
||||
</q-input>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "UBrowserInput",
|
||||
props: {
|
||||
modelValue: {
|
||||
type: [String, Number],
|
||||
default: "",
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
inputType: {
|
||||
type: String,
|
||||
default: "text",
|
||||
},
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
});
|
||||
</script>
|
@ -7,20 +7,18 @@
|
||||
class="row q-col-gutter-sm q-mb-sm"
|
||||
>
|
||||
<div class="col-5">
|
||||
<q-input
|
||||
<VariableInput
|
||||
:model-value="param.name"
|
||||
label="参数名"
|
||||
dense
|
||||
outlined
|
||||
:command="{ icon: 'label' }"
|
||||
@update:model-value="(value) => handleUpdate(index, 'name', value)"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-5">
|
||||
<q-input
|
||||
<VariableInput
|
||||
:model-value="param.value"
|
||||
label="传递给参数的值"
|
||||
dense
|
||||
outlined
|
||||
:command="{ icon: 'edit' }"
|
||||
@update:model-value="(value) => handleUpdate(index, 'value', value)"
|
||||
/>
|
||||
</div>
|
||||
@ -48,18 +46,19 @@
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "vue";
|
||||
import VariableInput from "components/editor/composer/VariableInput.vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "UBrowserNamedParamList",
|
||||
components: {
|
||||
VariableInput,
|
||||
},
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: () => [{ name: "", value: "" }],
|
||||
},
|
||||
label: String,
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
methods: {
|
||||
|
@ -36,11 +36,13 @@
|
||||
</template>
|
||||
<!-- 普通输入框 -->
|
||||
<template v-else>
|
||||
<UBrowserInput
|
||||
<VariableInput
|
||||
v-model="fieldValue[field.key]"
|
||||
:label="field.label"
|
||||
:icon="field.icon"
|
||||
:input-type="field.inputType"
|
||||
:command="{
|
||||
icon: field.icon,
|
||||
inputType: field.inputType,
|
||||
}"
|
||||
@update:model-value="updateValue(field.key, $event)"
|
||||
/>
|
||||
</template>
|
||||
@ -139,7 +141,7 @@ import UBrowserNamedParamList from "./UBrowserNamedParamList.vue";
|
||||
import UBrowserSelect from "./UBrowserSelect.vue";
|
||||
import UBrowserDeviceName from "./UBrowserDeviceName.vue";
|
||||
import UBrowserTextarea from "./UBrowserTextarea.vue";
|
||||
import UBrowserInput from "./UBrowserInput.vue";
|
||||
import VariableInput from "components/editor/composer/VariableInput.vue";
|
||||
import UBrowserCheckboxGroup from "./UBrowserCheckboxGroup.vue";
|
||||
|
||||
export default defineComponent({
|
||||
@ -155,7 +157,7 @@ export default defineComponent({
|
||||
UBrowserSelect,
|
||||
UBrowserDeviceName,
|
||||
UBrowserTextarea,
|
||||
UBrowserInput,
|
||||
VariableInput,
|
||||
UBrowserCheckboxGroup,
|
||||
},
|
||||
props: {
|
||||
|
@ -145,6 +145,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: "PersonalizeMenu",
|
||||
props: {
|
||||
|
150
src/js/common/variableValidator.js
Normal file
150
src/js/common/variableValidator.js
Normal file
@ -0,0 +1,150 @@
|
||||
// JavaScript 关键字和保留字列表
|
||||
const reservedWords = [
|
||||
// ES6+ JavaScript 语言关键字
|
||||
"break",
|
||||
"case",
|
||||
"catch",
|
||||
"class",
|
||||
"const",
|
||||
"continue",
|
||||
"debugger",
|
||||
"default",
|
||||
"delete",
|
||||
"do",
|
||||
"else",
|
||||
"export",
|
||||
"extends",
|
||||
"finally",
|
||||
"for",
|
||||
"function",
|
||||
"if",
|
||||
"import",
|
||||
"in",
|
||||
"instanceof",
|
||||
"new",
|
||||
"return",
|
||||
"super",
|
||||
"switch",
|
||||
"this",
|
||||
"throw",
|
||||
"try",
|
||||
"typeof",
|
||||
"var",
|
||||
"void",
|
||||
"while",
|
||||
"with",
|
||||
"yield",
|
||||
"let",
|
||||
"static",
|
||||
"await",
|
||||
"enum",
|
||||
|
||||
// 严格模式下的额外保留字
|
||||
"implements",
|
||||
"interface",
|
||||
"package",
|
||||
"private",
|
||||
"protected",
|
||||
"public",
|
||||
|
||||
// 历史遗留的保留字(可能在未来版本中使用)
|
||||
"abstract",
|
||||
"boolean",
|
||||
"byte",
|
||||
"char",
|
||||
"double",
|
||||
"final",
|
||||
"float",
|
||||
"goto",
|
||||
"int",
|
||||
"long",
|
||||
"native",
|
||||
"short",
|
||||
"synchronized",
|
||||
"throws",
|
||||
"transient",
|
||||
"volatile",
|
||||
|
||||
// JavaScript 内置的特殊值
|
||||
"null",
|
||||
"true",
|
||||
"false",
|
||||
"undefined",
|
||||
"NaN",
|
||||
"Infinity",
|
||||
|
||||
// 常用的全局对象和构造函数
|
||||
"Array",
|
||||
"Boolean",
|
||||
"Date",
|
||||
"Error",
|
||||
"Function",
|
||||
"JSON",
|
||||
"Math",
|
||||
"Number",
|
||||
"Object",
|
||||
"RegExp",
|
||||
"String",
|
||||
"Promise",
|
||||
"Proxy",
|
||||
"Map",
|
||||
"Set",
|
||||
"Symbol",
|
||||
"BigInt",
|
||||
|
||||
// 浏览器和 Node.js 环境的全局对象
|
||||
"window",
|
||||
"document",
|
||||
"console",
|
||||
"global",
|
||||
"process",
|
||||
"globalThis",
|
||||
|
||||
// 特殊的内置标识符
|
||||
"arguments",
|
||||
"eval",
|
||||
"hasOwnProperty",
|
||||
"isPrototypeOf",
|
||||
"propertyIsEnumerable",
|
||||
"toLocaleString",
|
||||
"toString",
|
||||
"valueOf",
|
||||
];
|
||||
|
||||
/**
|
||||
* 检查变量名是否合法
|
||||
* @param {string} name - 要检查的变量名
|
||||
* @returns {object} - 包含验证结果和错误信息的对象
|
||||
*/
|
||||
export function validateVariableName(name) {
|
||||
// 检查是否为空
|
||||
if (!name) {
|
||||
return {
|
||||
isValid: false,
|
||||
error: "变量名不能为空",
|
||||
};
|
||||
}
|
||||
|
||||
// 检查是否是保留字
|
||||
if (reservedWords.includes(name)) {
|
||||
return {
|
||||
isValid: false,
|
||||
error: `"${name}" 是 JavaScript 保留字,不能用作变量名`,
|
||||
};
|
||||
}
|
||||
|
||||
// 检查变量名格式是否合法
|
||||
const validNameRegex = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
||||
if (!validNameRegex.test(name)) {
|
||||
return {
|
||||
isValid: false,
|
||||
error:
|
||||
"变量名必须以字母、下划线或 $ 开头,只能包含字母、数字、下划线和 $",
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
isValid: true,
|
||||
error: null,
|
||||
};
|
||||
}
|
@ -109,6 +109,7 @@ export const commandCategories = [
|
||||
value: "quickcommand.sleep",
|
||||
label: "添加延时",
|
||||
desc: "延迟的毫秒数",
|
||||
inputType: "number",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -10,29 +10,33 @@ export function generateUBrowserCode(configs, selectedActions) {
|
||||
let code = "utools.ubrowser";
|
||||
|
||||
// 基础参数
|
||||
if (configs.useragent.value) {
|
||||
code += `\n .useragent('${configs.useragent.value}')`;
|
||||
}
|
||||
// if (configs.useragent.value) {
|
||||
// code += `\n .useragent('${configs.useragent.value}')`;
|
||||
// }
|
||||
|
||||
if (configs.goto.url) {
|
||||
const gotoOptions = {};
|
||||
if (configs.goto.headers.Referer) {
|
||||
gotoOptions.headers = gotoOptions.headers || {};
|
||||
gotoOptions.headers.Referer = configs.goto.headers.Referer;
|
||||
}
|
||||
if (configs.goto.headers.userAgent) {
|
||||
gotoOptions.headers = gotoOptions.headers || {};
|
||||
gotoOptions.headers["User-Agent"] = configs.goto.headers.userAgent;
|
||||
}
|
||||
if (configs.goto.timeout !== 60000) {
|
||||
gotoOptions.timeout = configs.goto.timeout;
|
||||
let gotoOptionsStr = `\n .goto(\n`;
|
||||
gotoOptionsStr += `${configs.goto.url}`;
|
||||
|
||||
if (configs.goto.headers.Referer || configs.goto.headers.userAgent) {
|
||||
gotoOptionsStr += ",\n{";
|
||||
if (configs.goto.headers.Referer) {
|
||||
gotoOptionsStr += `\nReferer: ${configs.goto.headers.Referer}`;
|
||||
}
|
||||
if (configs.goto.headers.userAgent) {
|
||||
gotoOptionsStr += `${
|
||||
configs.goto.headers.Referer ? "," : ""
|
||||
}\nuserAgent: ${configs.goto.headers.userAgent}`;
|
||||
}
|
||||
gotoOptionsStr += "\n}";
|
||||
}
|
||||
|
||||
code += `\n .goto('${configs.goto.url}'${
|
||||
Object.keys(gotoOptions).length
|
||||
? `,\n${JSON.stringify(gotoOptions, null, 2).replace(/\n/g, "\n ")}`
|
||||
: ""
|
||||
})`;
|
||||
if (configs.goto.timeout !== 60000) {
|
||||
gotoOptionsStr += `,\n${configs.goto.timeout}`;
|
||||
}
|
||||
|
||||
gotoOptionsStr += "\n)";
|
||||
code += gotoOptionsStr;
|
||||
}
|
||||
|
||||
// 浏览器操作
|
||||
@ -43,7 +47,7 @@ export function generateUBrowserCode(configs, selectedActions) {
|
||||
if (config.type === "time" && config.time) {
|
||||
code += `\n .wait(${config.time})`;
|
||||
} else if (config.type === "selector" && config.selector) {
|
||||
code += `\n .wait('${config.selector}'${
|
||||
code += `\n .wait(${config.selector}${
|
||||
config.timeout !== 60000 ? `, ${config.timeout}` : ""
|
||||
})`;
|
||||
} else if (config.type === "function" && config.function) {
|
||||
@ -66,13 +70,13 @@ export function generateUBrowserCode(configs, selectedActions) {
|
||||
|
||||
case "click":
|
||||
if (config.selector) {
|
||||
code += `\n .click('${config.selector}')`;
|
||||
code += `\n .click(${config.selector})`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "css":
|
||||
if (config.value) {
|
||||
code += `\n .css('${config.value}')`;
|
||||
code += `\n .css(${config.value})`;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -81,32 +85,31 @@ export function generateUBrowserCode(configs, selectedActions) {
|
||||
const modifiers = config.modifiers.length
|
||||
? `, ${JSON.stringify(config.modifiers)}`
|
||||
: "";
|
||||
code += `\n .press('${config.key}'${modifiers})`;
|
||||
code += `\n .press(${config.key}${modifiers})`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "paste":
|
||||
if (config.text) {
|
||||
code += `\n .paste('${config.text}')`;
|
||||
code += `\n .paste(${config.text})`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "screenshot":
|
||||
if (config.selector || config.savePath) {
|
||||
const options = {};
|
||||
if (config.selector) options.selector = config.selector;
|
||||
if (config.rect.width && config.rect.height) {
|
||||
options.rect = config.rect;
|
||||
}
|
||||
code += `\n .screenshot('${config.savePath}'${
|
||||
Object.keys(options).length ? `, ${JSON.stringify(options)}` : ""
|
||||
if (config.selector) {
|
||||
code += `\n .screenshot(${config.selector}${
|
||||
config.savePath ? `, '${config.savePath}'` : ""
|
||||
})`;
|
||||
} else if (config.rect) {
|
||||
code += `\n .screenshot(${JSON.stringify(config.rect)}${
|
||||
config.savePath ? `, ${config.savePath}` : ""
|
||||
})`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "pdf":
|
||||
if (config.savePath) {
|
||||
code += `\n .pdf('${config.savePath}'${
|
||||
code += `\n .pdf(${config.savePath}${
|
||||
config.options ? `, ${JSON.stringify(config.options)}` : ""
|
||||
})`;
|
||||
}
|
||||
@ -114,39 +117,56 @@ export function generateUBrowserCode(configs, selectedActions) {
|
||||
|
||||
case "device":
|
||||
if (config.type === "preset" && config.deviceName) {
|
||||
code += `\n .device('${config.deviceName}')`;
|
||||
code += `\n .device(${config.deviceName})`;
|
||||
} else if (config.type === "custom") {
|
||||
const options = {
|
||||
size: config.size,
|
||||
};
|
||||
if (config.useragent) options.useragent = config.useragent;
|
||||
code += `\n .device(${JSON.stringify(options, null, 2).replace(
|
||||
/\n/g,
|
||||
"\n "
|
||||
)})`;
|
||||
let deviceOptionsStr = `\n .device(\n{`;
|
||||
if (config.size) {
|
||||
deviceOptionsStr += `\nsize: ${JSON.stringify(config.size)}`;
|
||||
}
|
||||
if (config.useragent) {
|
||||
deviceOptionsStr += `${config.size ? "," : ""}\nuserAgent: ${
|
||||
config.useragent
|
||||
}`;
|
||||
}
|
||||
deviceOptionsStr += "\n}";
|
||||
code += deviceOptionsStr + "\n)";
|
||||
}
|
||||
break;
|
||||
|
||||
case "cookies":
|
||||
if (config.name) {
|
||||
code += `\n .cookies('${config.name}')`;
|
||||
code += `\n .cookies(${config.name})`;
|
||||
} else {
|
||||
code += `\n .cookies()`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "setCookies":
|
||||
if (config.items?.length) {
|
||||
code += `\n .setCookies(${JSON.stringify(config.items)})`;
|
||||
let cookiesStr = `\n .setCookies([\n`;
|
||||
config.items.forEach((item, index) => {
|
||||
cookiesStr += " {";
|
||||
if (item.name) cookiesStr += `\n name: ${item.name}`;
|
||||
if (item.value)
|
||||
cookiesStr += `${item.name ? "," : ""}\n value: ${
|
||||
item.value
|
||||
}}`;
|
||||
if (index < config.items.length - 1) cookiesStr += ",";
|
||||
cookiesStr += "\n";
|
||||
});
|
||||
cookiesStr += " ])";
|
||||
code += cookiesStr;
|
||||
}
|
||||
break;
|
||||
|
||||
case "removeCookies":
|
||||
if (config.name) {
|
||||
code += `\n .removeCookies('${config.name}')`;
|
||||
code += `\n .removeCookies(${config.name})`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "clearCookies":
|
||||
code += `\n .clearCookies(${config.url ? `'${config.url}'` : ""})`;
|
||||
code += `\n .clearCookies(${config.url || ""})`;
|
||||
break;
|
||||
|
||||
case "evaluate":
|
||||
@ -168,34 +188,38 @@ export function generateUBrowserCode(configs, selectedActions) {
|
||||
|
||||
case "when":
|
||||
if (config.condition) {
|
||||
code += `\n .when('${config.condition}')`;
|
||||
code += `\n .when(${config.condition})`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "mousedown":
|
||||
case "mouseup":
|
||||
if (config.selector) {
|
||||
code += `\n .${action.value}('${config.selector}')`;
|
||||
code += `\n .${action.value}(${config.selector})`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "file":
|
||||
if (config.selector && config.files?.length) {
|
||||
code += `\n .file('${config.selector}', ${JSON.stringify(
|
||||
config.files
|
||||
)})`;
|
||||
let filesStr = `\n .file(${config.selector}, [\n`;
|
||||
config.files.forEach((file, index) => {
|
||||
filesStr += ` ${file}`;
|
||||
if (index < config.files.length - 1) filesStr += ",\n";
|
||||
});
|
||||
filesStr += "\n ])";
|
||||
code += filesStr;
|
||||
}
|
||||
break;
|
||||
|
||||
case "value":
|
||||
if (config.selector) {
|
||||
code += `\n .value('${config.selector}', '${config.value}')`;
|
||||
code += `\n .value(${config.selector}, ${config.value})`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "check":
|
||||
if (config.selector) {
|
||||
code += `\n .check('${config.selector}'${
|
||||
code += `\n .check(${config.selector}${
|
||||
config.checked !== undefined ? `, ${config.checked}` : ""
|
||||
})`;
|
||||
}
|
||||
@ -203,13 +227,13 @@ export function generateUBrowserCode(configs, selectedActions) {
|
||||
|
||||
case "focus":
|
||||
if (config.selector) {
|
||||
code += `\n .focus('${config.selector}')`;
|
||||
code += `\n .focus(${config.selector})`;
|
||||
}
|
||||
break;
|
||||
|
||||
case "scroll":
|
||||
if (config.type === "element" && config.selector) {
|
||||
code += `\n .scroll('${config.selector}')`;
|
||||
code += `\n .scroll(${config.selector})`;
|
||||
} else if (config.type === "position") {
|
||||
if (config.x !== undefined && config.y !== undefined) {
|
||||
code += `\n .scroll(${config.x}, ${config.y})`;
|
||||
@ -221,8 +245,8 @@ export function generateUBrowserCode(configs, selectedActions) {
|
||||
|
||||
case "download":
|
||||
if (config.url) {
|
||||
code += `\n .download('${config.url}'${
|
||||
config.savePath ? `, '${config.savePath}'` : ""
|
||||
code += `\n .download(${config.url}${
|
||||
config.savePath ? `, ${config.savePath}` : ""
|
||||
})`;
|
||||
}
|
||||
break;
|
||||
@ -234,7 +258,7 @@ export function generateUBrowserCode(configs, selectedActions) {
|
||||
|
||||
case "devTools":
|
||||
if (config.mode) {
|
||||
code += `\n .devTools('${config.mode}')`;
|
||||
code += `\n .devTools(${config.mode})`;
|
||||
} else {
|
||||
code += `\n .devTools()`;
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
// ubrowser 浏览器操作配置
|
||||
export const ubrowserOperationConfigs = {
|
||||
wait: {
|
||||
export const ubrowserOperationConfigs = [
|
||||
{
|
||||
value: "wait",
|
||||
label: "等待",
|
||||
config: [
|
||||
{
|
||||
@ -56,7 +57,8 @@ export const ubrowserOperationConfigs = {
|
||||
],
|
||||
icon: "timer",
|
||||
},
|
||||
click: {
|
||||
{
|
||||
value: "click",
|
||||
label: "点击",
|
||||
config: [
|
||||
{
|
||||
@ -68,7 +70,8 @@ export const ubrowserOperationConfigs = {
|
||||
],
|
||||
icon: "mouse",
|
||||
},
|
||||
css: {
|
||||
{
|
||||
value: "css",
|
||||
label: "注入CSS",
|
||||
config: [
|
||||
{
|
||||
@ -80,7 +83,8 @@ export const ubrowserOperationConfigs = {
|
||||
],
|
||||
icon: "style",
|
||||
},
|
||||
press: {
|
||||
{
|
||||
value: "press",
|
||||
label: "按键",
|
||||
config: [
|
||||
{
|
||||
@ -106,7 +110,8 @@ export const ubrowserOperationConfigs = {
|
||||
],
|
||||
icon: "keyboard",
|
||||
},
|
||||
paste: {
|
||||
{
|
||||
value: "paste",
|
||||
label: "粘贴",
|
||||
config: [
|
||||
{
|
||||
@ -118,7 +123,8 @@ export const ubrowserOperationConfigs = {
|
||||
],
|
||||
icon: "content_paste",
|
||||
},
|
||||
viewport: {
|
||||
{
|
||||
value: "viewport",
|
||||
label: "视窗",
|
||||
config: [
|
||||
{
|
||||
@ -140,7 +146,8 @@ export const ubrowserOperationConfigs = {
|
||||
],
|
||||
icon: "crop",
|
||||
},
|
||||
screenshot: {
|
||||
{
|
||||
value: "screenshot",
|
||||
label: "截图",
|
||||
config: [
|
||||
{ key: "selector", label: "元素选择器", icon: "crop", type: "input" },
|
||||
@ -180,7 +187,8 @@ export const ubrowserOperationConfigs = {
|
||||
],
|
||||
icon: "picture_as_pdf",
|
||||
},
|
||||
pdf: {
|
||||
{
|
||||
value: "pdf",
|
||||
label: "导出PDF",
|
||||
config: [
|
||||
{
|
||||
@ -205,7 +213,8 @@ export const ubrowserOperationConfigs = {
|
||||
],
|
||||
icon: "devices",
|
||||
},
|
||||
device: {
|
||||
{
|
||||
value: "device",
|
||||
label: "模拟设备",
|
||||
config: [
|
||||
{
|
||||
@ -245,26 +254,44 @@ export const ubrowserOperationConfigs = {
|
||||
showValue: "custom",
|
||||
},
|
||||
],
|
||||
icon: "phone_iphone",
|
||||
},
|
||||
{
|
||||
value: "cookies",
|
||||
label: "获取Cookie",
|
||||
config: [
|
||||
{
|
||||
key: "name",
|
||||
label: "Cookie名称",
|
||||
icon: "cookie",
|
||||
type: "input",
|
||||
width: 12,
|
||||
},
|
||||
],
|
||||
icon: "cookie",
|
||||
},
|
||||
setCookies: {
|
||||
{
|
||||
value: "setCookies",
|
||||
label: "设置Cookie",
|
||||
config: [{ key: "items", label: "Cookie列表", type: "cookie-list" }],
|
||||
icon: "cookie",
|
||||
},
|
||||
removeCookies: {
|
||||
{
|
||||
value: "removeCookies",
|
||||
label: "删除Cookie",
|
||||
config: [
|
||||
{ key: "name", label: "Cookie名称", icon: "cookie", type: "input" },
|
||||
],
|
||||
icon: "cookie",
|
||||
},
|
||||
clearCookies: {
|
||||
{
|
||||
value: "clearCookies",
|
||||
label: "清空Cookie",
|
||||
config: [{ key: "url", label: "URL(可选)", icon: "link", type: "input" }],
|
||||
icon: "cookie",
|
||||
},
|
||||
evaluate: {
|
||||
{
|
||||
value: "evaluate",
|
||||
label: "执行代码",
|
||||
config: [
|
||||
{
|
||||
@ -277,7 +304,8 @@ export const ubrowserOperationConfigs = {
|
||||
],
|
||||
icon: "code",
|
||||
},
|
||||
when: {
|
||||
{
|
||||
value: "when",
|
||||
label: "条件判断",
|
||||
config: [
|
||||
{
|
||||
@ -322,12 +350,14 @@ export const ubrowserOperationConfigs = {
|
||||
],
|
||||
icon: "rule",
|
||||
},
|
||||
end: {
|
||||
{
|
||||
value: "end",
|
||||
label: "结束条件",
|
||||
config: [],
|
||||
icon: "stop",
|
||||
},
|
||||
mousedown: {
|
||||
{
|
||||
value: "mousedown",
|
||||
label: "按下鼠标",
|
||||
config: [
|
||||
{
|
||||
@ -339,7 +369,8 @@ export const ubrowserOperationConfigs = {
|
||||
],
|
||||
icon: "mouse",
|
||||
},
|
||||
mouseup: {
|
||||
{
|
||||
value: "mouseup",
|
||||
label: "释放鼠标",
|
||||
config: [
|
||||
{
|
||||
@ -351,7 +382,8 @@ export const ubrowserOperationConfigs = {
|
||||
],
|
||||
icon: "mouse",
|
||||
},
|
||||
file: {
|
||||
{
|
||||
value: "file",
|
||||
label: "上传文件",
|
||||
config: [
|
||||
{
|
||||
@ -364,7 +396,8 @@ export const ubrowserOperationConfigs = {
|
||||
],
|
||||
icon: "upload_file",
|
||||
},
|
||||
value: {
|
||||
{
|
||||
value: "value",
|
||||
label: "设置值",
|
||||
config: [
|
||||
{
|
||||
@ -384,7 +417,8 @@ export const ubrowserOperationConfigs = {
|
||||
],
|
||||
icon: "check_box",
|
||||
},
|
||||
check: {
|
||||
{
|
||||
value: "check",
|
||||
label: "设置选中",
|
||||
config: [
|
||||
{
|
||||
@ -404,7 +438,8 @@ export const ubrowserOperationConfigs = {
|
||||
],
|
||||
icon: "center_focus_strong",
|
||||
},
|
||||
focus: {
|
||||
{
|
||||
value: "focus",
|
||||
label: "聚焦元素",
|
||||
config: [
|
||||
{
|
||||
@ -416,7 +451,8 @@ export const ubrowserOperationConfigs = {
|
||||
],
|
||||
icon: "swap_vert",
|
||||
},
|
||||
scroll: {
|
||||
{
|
||||
value: "scroll",
|
||||
label: "滚动",
|
||||
config: [
|
||||
{
|
||||
@ -461,7 +497,8 @@ export const ubrowserOperationConfigs = {
|
||||
],
|
||||
icon: "download",
|
||||
},
|
||||
download: {
|
||||
{
|
||||
value: "download",
|
||||
label: "下载",
|
||||
config: [
|
||||
{
|
||||
@ -481,7 +518,8 @@ export const ubrowserOperationConfigs = {
|
||||
],
|
||||
icon: "download",
|
||||
},
|
||||
devTools: {
|
||||
{
|
||||
value: "devTools",
|
||||
label: "开发工具",
|
||||
config: [
|
||||
{
|
||||
@ -499,7 +537,19 @@ export const ubrowserOperationConfigs = {
|
||||
],
|
||||
icon: "developer_board",
|
||||
},
|
||||
};
|
||||
{
|
||||
value: "hide",
|
||||
label: "隐藏",
|
||||
config: [],
|
||||
icon: "visibility_off",
|
||||
},
|
||||
{
|
||||
value: "show",
|
||||
label: "显示",
|
||||
config: [],
|
||||
icon: "visibility",
|
||||
},
|
||||
];
|
||||
|
||||
// 添加默认运行配置
|
||||
const defaultUBrowserRunConfigs = {
|
||||
|
Loading…
x
Reference in New Issue
Block a user