mirror of
https://github.com/fofolee/uTools-quickcommand.git
synced 2025-06-07 21:46:12 +08:00
重构CommandEditor组件
This commit is contained in:
parent
9ffa941e72
commit
c6b511696a
@ -1,73 +1,50 @@
|
||||
<template>
|
||||
<!-- 命令设置栏 -->
|
||||
<CommandSideBar
|
||||
ref="sidebar"
|
||||
:canCommandSave="canCommandSave"
|
||||
:quickcommandInfo="quickcommandInfo"
|
||||
:allQuickCommandTags="allQuickCommandTags"
|
||||
class="absolute-left shadow-1"
|
||||
:style="{
|
||||
zIndex: 1,
|
||||
transform: isFullscreen ? 'translateX(-100%)' : 'translateX(0)',
|
||||
transition: 'transform 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
|
||||
}"
|
||||
:sideBarWidth="sideBarWidth"
|
||||
v-if="showSidebar"
|
||||
@back="handleBack"
|
||||
></CommandSideBar>
|
||||
<div class="command-editor">
|
||||
<!-- 编程语言栏 -->
|
||||
<CommandLanguageBar
|
||||
v-model="quickcommandInfo"
|
||||
:canCommandSave="canCommandSave"
|
||||
:isRunCodePage="isRunCodePage"
|
||||
@action="handleAction"
|
||||
/>
|
||||
|
||||
<!-- 编程语言栏 -->
|
||||
<CommandLanguageBar
|
||||
class="absolute-top"
|
||||
:style="{
|
||||
left: showSidebar ? sideBarWidth + 'px' : 65,
|
||||
zIndex: 1,
|
||||
transform: isFullscreen ? 'translateY(-100%)' : 'translateY(0)',
|
||||
opacity: isFullscreen ? 0 : 1,
|
||||
transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
|
||||
}"
|
||||
v-model="quickcommandInfo"
|
||||
:height="languageBarHeight"
|
||||
:canCommandSave="canCommandSave"
|
||||
:isRunCodePage="isRunCodePage"
|
||||
@program-changed="programChanged"
|
||||
@run="runCurrentCommand"
|
||||
@save="saveCurrentCommand"
|
||||
@show-composer="showComposer = true"
|
||||
/>
|
||||
<!-- 命令设置栏 -->
|
||||
<CommandConfig
|
||||
v-if="!isRunCodePage"
|
||||
:model-value="commandConfig"
|
||||
@update:is-expanded="isConfigExpanded = $event"
|
||||
:expand-on-focus="true"
|
||||
class="command-config"
|
||||
@update:model-value="updateCommandConfig"
|
||||
/>
|
||||
|
||||
<!-- 编辑器 -->
|
||||
<MonacoEditor
|
||||
class="editor-transition"
|
||||
:placeholder="true"
|
||||
ref="editor"
|
||||
@loaded="monacoInit"
|
||||
@typing="(val) => monacoTyping(val)"
|
||||
@keyStroke="monacoKeyStroke"
|
||||
:style="{
|
||||
position: 'absolute',
|
||||
top: isFullscreen ? 0 : languageBarHeight + 'px',
|
||||
left: isFullscreen ? 0 : action.type === 'run' ? 0 : sideBarWidth + 'px',
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
}"
|
||||
/>
|
||||
<!-- 编辑器 -->
|
||||
<CodeEditor
|
||||
v-model="quickcommandInfo.cmd"
|
||||
:language="getLanguage()"
|
||||
:cursor-position="quickcommandInfo.cursorPosition"
|
||||
@update:cursor-position="quickcommandInfo.cursorPosition = $event"
|
||||
placeholder="请输入代码"
|
||||
class="codeEditor"
|
||||
ref="editor"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 编辑器工具按钮组 -->
|
||||
<EditorTools
|
||||
ref="editorTools"
|
||||
v-show="!isConfigExpanded"
|
||||
:commandCode="quickcommandInfo?.features?.code || 'temp'"
|
||||
:isFullscreen="isFullscreen"
|
||||
@restore="restoreHistory"
|
||||
@toggle-fullscreen="toggleFullscreen"
|
||||
/>
|
||||
|
||||
<!-- 可视化编排 -->
|
||||
<q-dialog v-model="showComposer" maximized>
|
||||
<CommandComposer
|
||||
ref="composer"
|
||||
v-model="composerInfo"
|
||||
@action="handleComposerAction"
|
||||
:model-value="{ flows }"
|
||||
:disabled-control-buttons="['save']"
|
||||
/>
|
||||
</q-dialog>
|
||||
|
||||
@ -76,8 +53,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineAsyncComponent } from "vue";
|
||||
import CommandSideBar from "components/editor/CommandSideBar";
|
||||
import { defineAsyncComponent, ref, computed } from "vue";
|
||||
import CommandConfig from "./editor/CommandConfig.vue";
|
||||
import CommandLanguageBar from "components/editor/CommandLanguageBar";
|
||||
import EditorTools from "components/editor/EditorTools";
|
||||
import CommandRunResult from "components/CommandRunResult";
|
||||
@ -86,68 +63,44 @@ import programs from "js/options/programs.js";
|
||||
import { dbManager } from "js/utools.js";
|
||||
|
||||
// 预加载 MonacoEditor
|
||||
const MonacoEditorPromise = import("components/editor/MonacoEditor");
|
||||
const CodeEditorPromise = import("components/editor/CodeEditor.vue");
|
||||
// 在空闲时预加载
|
||||
if (window.requestIdleCallback) {
|
||||
window.requestIdleCallback(() => {
|
||||
MonacoEditorPromise;
|
||||
CodeEditorPromise;
|
||||
});
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
MonacoEditorPromise;
|
||||
CodeEditorPromise;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
// Performance Scripting > 500ms
|
||||
const MonacoEditor = defineAsyncComponent({
|
||||
loader: () => MonacoEditorPromise,
|
||||
const CodeEditor = defineAsyncComponent({
|
||||
loader: () => CodeEditorPromise,
|
||||
timeout: 3000,
|
||||
});
|
||||
|
||||
// TODO: 对称加密声明,保存命令不需要设置
|
||||
export default {
|
||||
components: {
|
||||
MonacoEditor,
|
||||
CommandSideBar,
|
||||
CodeEditor,
|
||||
CommandConfig,
|
||||
CommandRunResult,
|
||||
CommandLanguageBar,
|
||||
CommandComposer,
|
||||
EditorTools,
|
||||
},
|
||||
emits: ["editorEvent"],
|
||||
data() {
|
||||
return {
|
||||
programLanguages: Object.keys(programs),
|
||||
sideBarWidth: 200,
|
||||
languageBarHeight: 40,
|
||||
showComposer: false,
|
||||
isRunCodePage: this.action.type === "run",
|
||||
canCommandSave: this.action.type !== "run",
|
||||
showSidebar: this.action.type !== "run",
|
||||
flows: [
|
||||
{
|
||||
id: "main",
|
||||
name: "main",
|
||||
label: "主流程",
|
||||
commands: [],
|
||||
customVariables: [],
|
||||
},
|
||||
],
|
||||
quickcommandInfo: {
|
||||
program: "quickcommand",
|
||||
cmd: "",
|
||||
scptarg: "",
|
||||
charset: {
|
||||
scriptCode: "",
|
||||
outputCode: "",
|
||||
},
|
||||
customOptions: {
|
||||
bin: "",
|
||||
argv: "",
|
||||
ext: "",
|
||||
},
|
||||
},
|
||||
resultMaxLength: 10000,
|
||||
listener: null,
|
||||
isFullscreen: false,
|
||||
isConfigExpanded: false,
|
||||
composerInfo: {
|
||||
program: "quickcomposer",
|
||||
},
|
||||
};
|
||||
},
|
||||
props: {
|
||||
@ -155,125 +108,105 @@ export default {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
allQuickCommandTags: Array,
|
||||
},
|
||||
setup(props) {
|
||||
const isRunCodePage = ref(props.action.type === "run");
|
||||
const canCommandSave = ref(!isRunCodePage.value);
|
||||
|
||||
const commandAction = window.lodashM.cloneDeep(props.action);
|
||||
const savedCommand = isRunCodePage.value
|
||||
? dbManager.getDB("cfg_codeHistory")
|
||||
: commandAction.data || {};
|
||||
|
||||
const defaultCommand = {
|
||||
program: "quickcommand",
|
||||
features: {
|
||||
icon: programs.quickcommand.icon,
|
||||
explain: "",
|
||||
platform: ["win32", "linux", "darwin"],
|
||||
mainPush: false,
|
||||
cmds: [],
|
||||
},
|
||||
output: "text",
|
||||
tags: [],
|
||||
cmd: "",
|
||||
scptarg: "",
|
||||
charset: {
|
||||
scriptCode: "",
|
||||
outputCode: "",
|
||||
},
|
||||
customOptions: {
|
||||
bin: "",
|
||||
argv: "",
|
||||
ext: "",
|
||||
},
|
||||
};
|
||||
const quickcommandInfo = ref({
|
||||
...defaultCommand,
|
||||
...savedCommand,
|
||||
});
|
||||
|
||||
// 默认命令不可编辑
|
||||
if (quickcommandInfo.value.tags?.includes("默认") && !utools.isDev()) {
|
||||
canCommandSave.value = false;
|
||||
}
|
||||
|
||||
const commandConfig = computed(() => {
|
||||
const { tags, output, features, program } = quickcommandInfo.value;
|
||||
return { tags, output, features, program };
|
||||
});
|
||||
|
||||
return {
|
||||
quickcommandInfo,
|
||||
isRunCodePage,
|
||||
canCommandSave,
|
||||
commandConfig,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.commandInit();
|
||||
this.sidebarInit();
|
||||
this.saveToHistory();
|
||||
document.addEventListener("keydown", this.handleKeydown);
|
||||
},
|
||||
beforeUnmount() {
|
||||
document.removeEventListener("keydown", this.handleKeydown);
|
||||
},
|
||||
methods: {
|
||||
// 命令初始化
|
||||
commandInit() {
|
||||
let quickCommandInfo = this.isRunCodePage
|
||||
? dbManager.getDB("cfg_codeHistory")
|
||||
: this.action.data;
|
||||
quickCommandInfo?.program &&
|
||||
Object.assign(
|
||||
this.quickcommandInfo,
|
||||
window.lodashM.cloneDeep(quickCommandInfo)
|
||||
);
|
||||
// 默认命令不可编辑
|
||||
if (this.quickcommandInfo.tags?.includes("默认") && !utools.isDev()) {
|
||||
this.canCommandSave = false;
|
||||
}
|
||||
},
|
||||
// 侧边栏初始化
|
||||
sidebarInit() {
|
||||
this.$refs.sidebar?.init();
|
||||
},
|
||||
// Monaco编辑器初始化,Monaco异步加载完执行
|
||||
monacoInit() {
|
||||
this.$refs.editor.setEditorValue(this.quickcommandInfo.cmd);
|
||||
this.setLanguage(this.quickcommandInfo.program);
|
||||
this.$refs.editor.setCursorPosition(this.quickcommandInfo.cursorPosition);
|
||||
|
||||
// 等待编辑器内容加载完成后再保存
|
||||
setTimeout(() => {
|
||||
this.saveToHistory();
|
||||
}, 1000); // 给予足够的时间让编辑器加载完成
|
||||
},
|
||||
programChanged(value) {
|
||||
this.setLanguage(value);
|
||||
if (value === "custom") this.$refs.settings.show();
|
||||
this.$refs.sidebar?.setIcon(value);
|
||||
},
|
||||
// 匹配编程语言
|
||||
matchLanguage() {
|
||||
if (!this.quickcommandInfo.customOptions.ext) return;
|
||||
let language = Object.values(programs).filter(
|
||||
(program) => program.ext === this.quickcommandInfo.customOptions.ext
|
||||
);
|
||||
if (language.length) {
|
||||
this.setLanguage(language[0].name);
|
||||
}
|
||||
},
|
||||
// 设置编程语言
|
||||
setLanguage(language) {
|
||||
let highlight = programs[language].highlight;
|
||||
this.$refs.editor.setEditorLanguage(highlight ? highlight : language);
|
||||
},
|
||||
insertText(text) {
|
||||
this.$refs.editor.repacleEditorSelection(text);
|
||||
this.$refs.editor.formatDocument();
|
||||
},
|
||||
replaceText(text) {
|
||||
this.$refs.editor.setEditorValue(text);
|
||||
this.$refs.editor.formatDocument();
|
||||
},
|
||||
handleComposerAction(actionType, actionData) {
|
||||
switch (actionType) {
|
||||
case "run":
|
||||
return this.runCurrentCommand(actionData);
|
||||
case "insert":
|
||||
return this.insertText(actionData);
|
||||
// actionData 完整命令
|
||||
this.runCurrentCommand(actionData);
|
||||
break;
|
||||
case "apply":
|
||||
return this.replaceText(actionData);
|
||||
// actionData 命令的cmd
|
||||
console.log(actionData);
|
||||
this.showComposer = false;
|
||||
this.quickcommandInfo.cmd = actionData;
|
||||
break;
|
||||
case "close":
|
||||
return (this.showComposer = false);
|
||||
this.showComposer = false;
|
||||
break;
|
||||
}
|
||||
},
|
||||
// 保存
|
||||
saveCurrentCommand(message = "保存成功") {
|
||||
let updatedData = this.$refs.sidebar?.SaveMenuData();
|
||||
if (!updatedData) return;
|
||||
Object.assign(
|
||||
this.quickcommandInfo,
|
||||
window.lodashM.cloneDeep(updatedData)
|
||||
);
|
||||
let newQuickcommandInfo = window.lodashM.cloneDeep(this.quickcommandInfo);
|
||||
dbManager.putDB(
|
||||
newQuickcommandInfo,
|
||||
"qc_" + this.quickcommandInfo.features.code
|
||||
);
|
||||
this.$emit("editorEvent", {
|
||||
type: "save",
|
||||
data: newQuickcommandInfo,
|
||||
});
|
||||
saveCurrentCommand() {
|
||||
this.$emit("editorEvent", "save", this.quickcommandInfo);
|
||||
this.saveToHistory(); // 保存时记录历史
|
||||
if (!message) return;
|
||||
quickcommand.showMessageBox(message, "success", 1000, "bottom-right");
|
||||
},
|
||||
// 运行
|
||||
runCurrentCommand(cmd) {
|
||||
this.saveToHistory(); // 运行时不保存但记录历史
|
||||
let command = window.lodashM.cloneDeep(this.quickcommandInfo);
|
||||
if (cmd) command.cmd = cmd;
|
||||
command.output =
|
||||
this.$refs.sidebar?.currentCommand.output ||
|
||||
(command.program === "html" ? "html" : "text");
|
||||
command.cmdType = this.$refs.sidebar?.cmdType.name;
|
||||
runCurrentCommand(command) {
|
||||
if (!command) {
|
||||
this.saveToHistory(); // 运行时不保存但记录历史
|
||||
command = { ...this.quickcommandInfo };
|
||||
}
|
||||
this.$refs.result.runCurrentCommand(command);
|
||||
},
|
||||
saveCodeHistory() {
|
||||
if (this.action.type !== "run") return;
|
||||
if (!this.isRunCodePage) return;
|
||||
let command = window.lodashM.cloneDeep(this.quickcommandInfo);
|
||||
command.cursorPosition = this.$refs.editor.getCursorPosition();
|
||||
dbManager.putDB(command, "cfg_codeHistory");
|
||||
},
|
||||
monacoTyping(val) {
|
||||
this.quickcommandInfo.cmd = val;
|
||||
},
|
||||
monacoKeyStroke(event, data) {
|
||||
handleAction(event, data) {
|
||||
switch (event) {
|
||||
case "run":
|
||||
this.runCurrentCommand();
|
||||
@ -281,28 +214,22 @@ export default {
|
||||
case "save":
|
||||
this.saveCurrentCommand();
|
||||
break;
|
||||
case "log":
|
||||
if (this.quickcommandInfo.program !== "quickcommand") return;
|
||||
this.runCurrentCommand(`console.log(${data})`);
|
||||
case "back":
|
||||
this.$emit("editorEvent", "back");
|
||||
break;
|
||||
case "fullscreen":
|
||||
this.toggleFullscreen();
|
||||
case "show-composer":
|
||||
this.showComposer = true;
|
||||
break;
|
||||
case "insert-text":
|
||||
this.$refs.editor.repacleEditorSelection(data);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
toggleFullscreen() {
|
||||
this.isFullscreen = !this.isFullscreen;
|
||||
|
||||
// 重新布局编辑器
|
||||
setTimeout(() => {
|
||||
this.$refs.editor.resizeEditor();
|
||||
}, 300);
|
||||
},
|
||||
saveToHistory() {
|
||||
this.$refs.editorTools.tryToSave(
|
||||
this.$refs.editor.getEditorValue(),
|
||||
this.quickcommandInfo.cmd,
|
||||
this.quickcommandInfo.program
|
||||
);
|
||||
},
|
||||
@ -311,27 +238,72 @@ export default {
|
||||
this.saveToHistory();
|
||||
|
||||
// 恢复历史内容
|
||||
this.$refs.editor.setEditorValue(item.content);
|
||||
this.quickcommandInfo.cmd = item.content;
|
||||
this.quickcommandInfo.program = item.program;
|
||||
},
|
||||
handleBack() {
|
||||
// 触发返回事件
|
||||
this.$emit("editorEvent", { type: "back" });
|
||||
updateCommandConfig(value) {
|
||||
this.quickcommandInfo = {
|
||||
...this.quickcommandInfo,
|
||||
...value,
|
||||
};
|
||||
},
|
||||
getLanguage() {
|
||||
if (this.quickcommandInfo.program !== "custom") {
|
||||
return this.quickcommandInfo.program;
|
||||
}
|
||||
if (!this.quickcommandInfo.customOptions.ext) return;
|
||||
let language = Object.values(programs).find(
|
||||
(program) => program.ext === this.quickcommandInfo.customOptions.ext
|
||||
);
|
||||
if (!language) return;
|
||||
return language.name;
|
||||
},
|
||||
// 添加快捷键处理
|
||||
handleKeydown(e) {
|
||||
// 检查是否按下 Ctrl 键 (Windows) 或 Command 键 (Mac)
|
||||
const isCmdOrCtrl = window.utools.isMacOS() ? e.metaKey : e.ctrlKey;
|
||||
if (!isCmdOrCtrl) return;
|
||||
|
||||
switch (e.key.toLowerCase()) {
|
||||
case "s":
|
||||
e.preventDefault();
|
||||
if (!this.canCommandSave) return;
|
||||
this.saveCurrentCommand();
|
||||
break;
|
||||
case "b":
|
||||
e.preventDefault();
|
||||
this.runCurrentCommand();
|
||||
break;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 统一过渡效果 */
|
||||
.sidebar-transition,
|
||||
.language-bar-transition {
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
will-change: transform, left, top, opacity;
|
||||
.command-editor {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
background-color: #fffffe;
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
}
|
||||
|
||||
/* 编辑器动画不一致,可以产生一个回弹效果 */
|
||||
.editor-transition {
|
||||
transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
will-change: transform, left, top, opacity;
|
||||
.body--dark .command-editor {
|
||||
background-color: #1e1e1e;
|
||||
}
|
||||
|
||||
.codeEditor {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
border-radius: 0 0 10px 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.command-config {
|
||||
padding: 4px 10px;
|
||||
}
|
||||
</style>
|
||||
|
@ -78,6 +78,7 @@ import ResultMenu from "components/popup/ResultMenu.vue";
|
||||
import { generateFlowsCode } from "js/composer/generateCode";
|
||||
import { getValidCommand } from "js/commandManager";
|
||||
import { dbManager } from "js/utools.js";
|
||||
import programs from "js/options/programs.js";
|
||||
|
||||
export default {
|
||||
components: { ResultArea, ResultMenu },
|
||||
|
@ -3,7 +3,7 @@
|
||||
ref="composer"
|
||||
@action="handleComposerAction"
|
||||
v-model="quickcommandInfo"
|
||||
:show-close-button="!isRunComposePage"
|
||||
:disabled-control-buttons="disabledControlButtons"
|
||||
class="fixed-full"
|
||||
/>
|
||||
<!-- 运行结果 -->
|
||||
@ -15,7 +15,7 @@ import CommandComposer from "components/composer/CommandComposer.vue";
|
||||
import CommandRunResult from "components/CommandRunResult";
|
||||
import { findCommandByValue } from "js/composer/composerConfig";
|
||||
import programs from "js/options/programs.js";
|
||||
import { ref } from "vue";
|
||||
import { ref, computed } from "vue";
|
||||
|
||||
export default {
|
||||
components: { CommandComposer, CommandRunResult },
|
||||
@ -82,15 +82,7 @@ export default {
|
||||
mainPush: false,
|
||||
cmds: [],
|
||||
},
|
||||
flows: [
|
||||
{
|
||||
id: "main",
|
||||
name: "main",
|
||||
label: "主流程",
|
||||
commands: [],
|
||||
customVariables: [],
|
||||
},
|
||||
],
|
||||
flows: [],
|
||||
output: "text",
|
||||
tags: [],
|
||||
};
|
||||
@ -99,12 +91,18 @@ export default {
|
||||
...retoreToFullCommand(savedCommand),
|
||||
});
|
||||
|
||||
const isRunComposePage = ref(props.action.type === "composer");
|
||||
const isRunComposePage = computed(() => {
|
||||
return props.action.type === "composer";
|
||||
});
|
||||
|
||||
const disabledControlButtons = computed(() => {
|
||||
return isRunComposePage.value ? ["close", "save", "apply"] : ["apply"];
|
||||
});
|
||||
|
||||
return {
|
||||
quickcommandInfo,
|
||||
getLitedComposerCommand,
|
||||
isRunComposePage,
|
||||
disabledControlButtons,
|
||||
};
|
||||
},
|
||||
emits: ["editorEvent"],
|
||||
@ -120,14 +118,13 @@ export default {
|
||||
case "run":
|
||||
return this.runCurrentCommand(command);
|
||||
case "close":
|
||||
return this.$emit("editorEvent", {
|
||||
type: "back",
|
||||
});
|
||||
return this.$emit("editorEvent", "back");
|
||||
case "save":
|
||||
return this.$emit("editorEvent", {
|
||||
type: "save",
|
||||
data: this.getLitedComposerCommand(command),
|
||||
});
|
||||
return this.$emit(
|
||||
"editorEvent",
|
||||
"save",
|
||||
this.getLitedComposerCommand(command)
|
||||
);
|
||||
}
|
||||
},
|
||||
runCurrentCommand(command) {
|
||||
|
@ -11,7 +11,7 @@
|
||||
<div class="col command-section">
|
||||
<FlowTabs
|
||||
@action="handleAction"
|
||||
:show-close-button="showCloseButton"
|
||||
:disabled-control-buttons="disabledControlButtons"
|
||||
:model-value="modelValue"
|
||||
@update:model-value="$emit('update:modelValue', $event)"
|
||||
/>
|
||||
@ -42,9 +42,9 @@ export default defineComponent({
|
||||
};
|
||||
},
|
||||
props: {
|
||||
showCloseButton: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
disabledControlButtons: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
modelValue: {
|
||||
type: Object,
|
||||
|
@ -197,16 +197,17 @@ export default defineComponent({
|
||||
};
|
||||
const consoleLogVars =
|
||||
this.getAvailableOutputVariableName(outputVariable);
|
||||
const tempFlows = [{
|
||||
name: "main",
|
||||
commands: [
|
||||
tempCommand,
|
||||
{
|
||||
//没有输出,则不打印
|
||||
code: `if(${consoleLogVars}!==undefined){
|
||||
const tempFlows = [
|
||||
{
|
||||
name: "main",
|
||||
commands: [
|
||||
tempCommand,
|
||||
{
|
||||
//没有输出,则不打印
|
||||
code: `if(${consoleLogVars}!==undefined){
|
||||
console.log(${consoleLogVars})
|
||||
}`,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
@ -314,6 +315,14 @@ export default defineComponent({
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.composer-card .q-card {
|
||||
background-color: rgba(0, 0, 0, 0.02);
|
||||
}
|
||||
|
||||
.body--dark .composer-card .q-card {
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
/* 控制流程组件样式 */
|
||||
.control-component {
|
||||
flex: 1;
|
||||
|
@ -60,7 +60,7 @@
|
||||
|
||||
<ComposerButtons
|
||||
:is-all-collapsed="isAllCollapsed"
|
||||
:show-close-button="showCloseButton"
|
||||
:disabled-buttons="disabledControlButtons"
|
||||
:flows="flows"
|
||||
@action="handleAction"
|
||||
/>
|
||||
@ -86,7 +86,7 @@
|
||||
ref="flowRefs"
|
||||
/>
|
||||
<FlowManager
|
||||
v-model="showVariableManager"
|
||||
v-model="showFlowManager"
|
||||
:flow="flow"
|
||||
:variables="flow.customVariables"
|
||||
@update-flow="updateFlows(flow)"
|
||||
@ -119,9 +119,9 @@ export default defineComponent({
|
||||
},
|
||||
emits: ["update:modelValue", "action"],
|
||||
props: {
|
||||
showCloseButton: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
disabledControlButtons: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
modelValue: {
|
||||
type: Object,
|
||||
@ -136,7 +136,26 @@ export default defineComponent({
|
||||
});
|
||||
};
|
||||
|
||||
const flows = computed(() => props.modelValue.flows);
|
||||
const defaultFlow = [
|
||||
{
|
||||
id: "main",
|
||||
name: "main",
|
||||
label: "主流程",
|
||||
commands: [],
|
||||
customVariables: [],
|
||||
},
|
||||
];
|
||||
|
||||
if (!props.modelValue.flows || props.modelValue.flows.length === 0) {
|
||||
updateFlows(defaultFlow);
|
||||
}
|
||||
|
||||
const flows = computed(() => props.modelValue.flows || []);
|
||||
|
||||
const commandConfig = computed(() => {
|
||||
const { tags, output, features, program } = props.modelValue;
|
||||
return { tags, output, features, program };
|
||||
});
|
||||
|
||||
const mainFlow = computed({
|
||||
get: () => flows.value[0],
|
||||
@ -235,6 +254,7 @@ export default defineComponent({
|
||||
flows,
|
||||
mainFlow,
|
||||
subFlows,
|
||||
commandConfig,
|
||||
activeTab,
|
||||
getOutputVariables,
|
||||
updateFlows,
|
||||
@ -243,16 +263,10 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
isAllCollapsed: false,
|
||||
showVariableManager: false,
|
||||
showFlowManager: false,
|
||||
outputVariables: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
commandConfig() {
|
||||
const { tags, output, features } = this.modelValue;
|
||||
return { tags, output, features };
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
generateFlowName(baseName = "func_") {
|
||||
return (
|
||||
@ -293,7 +307,7 @@ export default defineComponent({
|
||||
|
||||
this.activeTab = id;
|
||||
this.$nextTick(() => {
|
||||
this.toggleVariableManager();
|
||||
this.toggleFlowManager();
|
||||
});
|
||||
},
|
||||
removeFlow(flow) {
|
||||
@ -332,12 +346,15 @@ export default defineComponent({
|
||||
case "expandAll":
|
||||
this.expandAll();
|
||||
break;
|
||||
case "toggleVariableManager":
|
||||
this.toggleVariableManager();
|
||||
case "toggleFlowManager":
|
||||
this.toggleFlowManager();
|
||||
break;
|
||||
case "close":
|
||||
this.$emit("action", "close");
|
||||
break;
|
||||
case "apply":
|
||||
this.$emit("action", "apply", payload);
|
||||
break;
|
||||
case "addFlow":
|
||||
// 处理新函数创建
|
||||
const index = this.subFlows.findIndex((f) => f.name === payload.name);
|
||||
@ -352,8 +369,8 @@ export default defineComponent({
|
||||
this.$emit("action", type, payload);
|
||||
}
|
||||
},
|
||||
toggleVariableManager() {
|
||||
this.showVariableManager = !this.showVariableManager;
|
||||
toggleFlowManager() {
|
||||
this.showFlowManager = !this.showFlowManager;
|
||||
this.outputVariables = this.getOutputVariables();
|
||||
},
|
||||
saveFlows() {
|
||||
@ -383,7 +400,7 @@ export default defineComponent({
|
||||
},
|
||||
editFunction(flow) {
|
||||
this.activeTab = flow.id;
|
||||
this.toggleVariableManager();
|
||||
this.toggleFlowManager();
|
||||
},
|
||||
updateCommandConfig(newVal) {
|
||||
const newModelValue = {
|
||||
|
@ -5,8 +5,8 @@
|
||||
icon="close"
|
||||
dense
|
||||
flat
|
||||
v-if="showCloseButton"
|
||||
@click="$emit('action', 'close')"
|
||||
v-if="!disabledButtons.includes('close')"
|
||||
>
|
||||
<q-tooltip>退出可视化编排</q-tooltip>
|
||||
</q-btn>
|
||||
@ -15,6 +15,7 @@
|
||||
dense
|
||||
flat
|
||||
@click="$emit('action', isAllCollapsed ? 'expandAll' : 'collapseAll')"
|
||||
v-if="!disabledButtons.includes('expand')"
|
||||
>
|
||||
<q-tooltip>{{ isAllCollapsed ? "展开所有" : "折叠所有" }}</q-tooltip>
|
||||
</q-btn>
|
||||
@ -22,7 +23,8 @@
|
||||
icon="settings"
|
||||
dense
|
||||
flat
|
||||
@click="$emit('action', 'toggleVariableManager')"
|
||||
@click="$emit('action', 'toggleFlowManager')"
|
||||
v-if="!disabledButtons.includes('manager')"
|
||||
>
|
||||
<q-tooltip>流程管理</q-tooltip>
|
||||
</q-btn>
|
||||
@ -34,33 +36,40 @@
|
||||
v-if="isDev"
|
||||
>
|
||||
</q-btn>
|
||||
<!-- <q-btn
|
||||
dense
|
||||
icon="read_more"
|
||||
flat
|
||||
v-close-popup
|
||||
@click="$emit('action', 'insert')"
|
||||
v-if="showCloseButton"
|
||||
>
|
||||
<q-tooltip>插入到编辑器光标处</q-tooltip>
|
||||
</q-btn> -->
|
||||
<!-- <q-btn
|
||||
<q-btn
|
||||
dense
|
||||
flat
|
||||
v-close-popup
|
||||
icon="done_all"
|
||||
@click="$emit('action', 'apply')"
|
||||
v-if="showCloseButton"
|
||||
@click="handleApply"
|
||||
v-if="!disabledButtons.includes('apply')"
|
||||
>
|
||||
<q-tooltip>清空编辑器内容并插入</q-tooltip>
|
||||
</q-btn> -->
|
||||
<q-btn dense flat icon="save" @click="$emit('action', 'save')">
|
||||
</q-btn>
|
||||
<q-btn
|
||||
dense
|
||||
flat
|
||||
icon="save"
|
||||
@click="$emit('action', 'save')"
|
||||
v-if="!disabledButtons.includes('save')"
|
||||
>
|
||||
<q-tooltip>保存</q-tooltip>
|
||||
</q-btn>
|
||||
<q-btn flat dense icon="preview" @click="isVisible = true">
|
||||
<q-btn
|
||||
flat
|
||||
dense
|
||||
icon="preview"
|
||||
@click="handlePreviewCode"
|
||||
v-if="!disabledButtons.includes('preview')"
|
||||
>
|
||||
<q-tooltip>预览代码</q-tooltip>
|
||||
</q-btn>
|
||||
<q-btn dense flat icon="play_circle" @click="$emit('action', 'run')">
|
||||
<q-btn
|
||||
dense
|
||||
flat
|
||||
icon="play_circle"
|
||||
@click="$emit('action', 'run')"
|
||||
v-if="!disabledButtons.includes('run')"
|
||||
>
|
||||
<q-tooltip>运行</q-tooltip>
|
||||
</q-btn>
|
||||
</div>
|
||||
@ -99,9 +108,9 @@ export default defineComponent({
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
showCloseButton: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
disabledButtons: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
flows: {
|
||||
type: Array,
|
||||
@ -119,11 +128,15 @@ export default defineComponent({
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
isVisible(val) {
|
||||
if (val) {
|
||||
this.code = generateFlowsCode(this.flows);
|
||||
}
|
||||
methods: {
|
||||
handlePreviewCode() {
|
||||
this.code = generateFlowsCode(this.flows);
|
||||
this.isVisible = true;
|
||||
},
|
||||
// 清空编辑器内容并插入
|
||||
handleApply() {
|
||||
const code = generateFlowsCode(this.flows);
|
||||
this.$emit("action", "apply", code);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -26,7 +26,7 @@ import TimeInput from "components/composer/common/TimeInput.vue";
|
||||
import FunctionInput from "components/composer/common/FunctionInput.vue";
|
||||
import { QInput, QSelect, QToggle, QCheckbox } from "quasar";
|
||||
const CodeEditor = defineAsyncComponent(() =>
|
||||
import("components/composer/common/CodeEditor.vue")
|
||||
import("components/editor/CodeEditor.vue")
|
||||
);
|
||||
|
||||
export default defineComponent({
|
||||
|
@ -127,7 +127,7 @@
|
||||
<script>
|
||||
import { defineComponent } from "vue";
|
||||
import { newVarInputVal } from "js/composer/varInputValManager";
|
||||
import CodeEditor from "components/composer/common/CodeEditor.vue";
|
||||
import CodeEditor from "components/editor/CodeEditor.vue";
|
||||
import VariableInput from "components/composer/common/VariableInput.vue";
|
||||
import ArrayEditor from "components/composer/common/ArrayEditor.vue";
|
||||
import BorderLabel from "components/composer/common/BorderLabel.vue";
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="code-editor" :style="{ height: height }">
|
||||
<div ref="editorContainer" class="editor-container" />
|
||||
<div class="placeholder-wrapper" v-show="!value && placeholder">
|
||||
<div class="placeholder-wrapper" v-show="showPlaceholder">
|
||||
<div class="placeholder">
|
||||
{{ placeholder }}
|
||||
</div>
|
||||
@ -11,7 +11,6 @@
|
||||
|
||||
<script>
|
||||
import * as monaco from "monaco-editor";
|
||||
import { toRaw } from "vue";
|
||||
import importAll from "js/common/importAll.js";
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
@ -21,6 +20,7 @@ let languageCompletions = importAll(
|
||||
);
|
||||
|
||||
let monacoCompletionProviders = {};
|
||||
let editor = null;
|
||||
|
||||
// 声明文件映射
|
||||
const typeDefinitions = {
|
||||
@ -70,12 +70,15 @@ export default defineComponent({
|
||||
type: String,
|
||||
default: "请输入...",
|
||||
},
|
||||
// 光标位置
|
||||
cursorPosition: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
emits: ["update:modelValue", "change"],
|
||||
emits: ["update:modelValue", "update:cursorPosition"],
|
||||
data() {
|
||||
return {
|
||||
editor: null,
|
||||
value: null,
|
||||
resizeTimeout: null,
|
||||
defaultOptions: {
|
||||
value: "",
|
||||
@ -138,12 +141,19 @@ export default defineComponent({
|
||||
modelValue: {
|
||||
immediate: true,
|
||||
handler(newValue) {
|
||||
if (this.value !== newValue) {
|
||||
this.value = newValue;
|
||||
if (this.editor && this.editor.getValue() !== newValue) {
|
||||
this.editor.setValue(newValue || "");
|
||||
}
|
||||
}
|
||||
if (!editor || editor.getValue() === newValue) return;
|
||||
editor.setValue(newValue || "");
|
||||
},
|
||||
},
|
||||
cursorPosition: {
|
||||
immediate: true,
|
||||
handler(newValue) {
|
||||
if (!editor) return;
|
||||
const { lineNumber, column } = newValue;
|
||||
if (!lineNumber || !column) return;
|
||||
const pos = editor.getPosition();
|
||||
if (pos.lineNumber === lineNumber && pos.column === column) return;
|
||||
editor.setPosition(newValue);
|
||||
},
|
||||
},
|
||||
"$q.dark.isActive": {
|
||||
@ -155,11 +165,10 @@ export default defineComponent({
|
||||
language: {
|
||||
immediate: true,
|
||||
handler(newValue) {
|
||||
if (this.editor) {
|
||||
const language = this.getHighlighter(newValue);
|
||||
monaco.editor.setModelLanguage(this.rawEditor().getModel(), language);
|
||||
this.loadTypes();
|
||||
}
|
||||
if (!editor) return;
|
||||
const language = this.getHighlighter(newValue);
|
||||
monaco.editor.setModelLanguage(editor.getModel(), language);
|
||||
this.loadTypes();
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -180,16 +189,17 @@ export default defineComponent({
|
||||
const options = {
|
||||
...this.defaultOptions,
|
||||
...this.options,
|
||||
value: this.value || "",
|
||||
value: this.modelValue || "",
|
||||
language,
|
||||
theme: this.theme,
|
||||
};
|
||||
|
||||
this.editor = monaco.editor.create(this.$refs.editorContainer, options);
|
||||
editor = monaco.editor.create(this.$refs.editorContainer, options);
|
||||
this.listenEditorValue();
|
||||
this.loadTypes();
|
||||
this.registerLanguage();
|
||||
|
||||
this.bindKeys();
|
||||
this.setCursorPosition(this.cursorPosition);
|
||||
// 初始化完成后立即触发一次布局更新
|
||||
this.$nextTick(() => {
|
||||
this.resizeEditor();
|
||||
@ -197,11 +207,14 @@ export default defineComponent({
|
||||
},
|
||||
// 监听编辑器值变化
|
||||
listenEditorValue() {
|
||||
this.rawEditor().focus();
|
||||
this.rawEditor().onDidChangeModelContent(() => {
|
||||
this.value = this.getEditorValue();
|
||||
this.$emit("update:modelValue", this.value);
|
||||
this.$emit("change", this.value);
|
||||
editor.focus();
|
||||
editor.onDidChangeModelContent(() => {
|
||||
this.$emit("update:modelValue", editor.getValue());
|
||||
});
|
||||
|
||||
// 监听光标位置变化
|
||||
editor.onDidChangeCursorPosition((e) => {
|
||||
this.$emit("update:cursorPosition", e.position);
|
||||
});
|
||||
},
|
||||
// 处理窗口大小变化
|
||||
@ -210,50 +223,24 @@ export default defineComponent({
|
||||
clearTimeout(this.resizeTimeout);
|
||||
}
|
||||
this.resizeTimeout = setTimeout(() => {
|
||||
this.rawEditor().layout();
|
||||
editor.layout();
|
||||
}, 50);
|
||||
},
|
||||
// 销毁编辑器
|
||||
destroyEditor() {
|
||||
if (this.editor) {
|
||||
window.removeEventListener("resize", this.resizeEditor);
|
||||
this.rawEditor().dispose();
|
||||
this.editor = null;
|
||||
}
|
||||
},
|
||||
// 获取原始编辑器实例
|
||||
rawEditor() {
|
||||
return toRaw(this.editor);
|
||||
},
|
||||
// 获取编辑器实例
|
||||
getEditor() {
|
||||
return this.editor;
|
||||
},
|
||||
// 设置编辑器内容
|
||||
setValue(value) {
|
||||
if (this.editor) {
|
||||
this.editor.setValue(value || "");
|
||||
}
|
||||
},
|
||||
// 获取编辑器内容
|
||||
getValue() {
|
||||
return this.editor ? this.editor.getValue() : "";
|
||||
},
|
||||
// 获取编辑器内容
|
||||
getEditorValue() {
|
||||
return this.rawEditor().getValue();
|
||||
window.removeEventListener("resize", this.resizeEditor);
|
||||
if (!editor) return;
|
||||
editor.dispose();
|
||||
editor = null;
|
||||
},
|
||||
// 聚焦编辑器
|
||||
focus() {
|
||||
if (this.editor) {
|
||||
this.editor.focus();
|
||||
}
|
||||
editor && editor.focus();
|
||||
},
|
||||
registerLanguage() {
|
||||
let that = this;
|
||||
const identifierPattern = "([a-zA-Z_]\\w*)";
|
||||
let getTokens = (code) => {
|
||||
let identifier = new RegExp(identifierPattern, "g");
|
||||
const identifier = new RegExp(identifierPattern, "g");
|
||||
let tokens = [];
|
||||
let array1;
|
||||
while ((array1 = identifier.exec(code)) !== null) {
|
||||
@ -264,7 +251,7 @@ export default defineComponent({
|
||||
let createDependencyProposals = (range, keyWords, editor, curWord) => {
|
||||
let keys = [];
|
||||
// fix getValue of undefined
|
||||
let tokens = getTokens(toRaw(editor).getModel()?.getValue());
|
||||
const tokens = getTokens(editor.getModel()?.getValue());
|
||||
// 自定义变量、字符串
|
||||
for (const item of tokens) {
|
||||
if (item != curWord.word) {
|
||||
@ -312,7 +299,7 @@ export default defineComponent({
|
||||
suggestions: createDependencyProposals(
|
||||
range,
|
||||
languageCompletions[language].default,
|
||||
toRaw(that.editor),
|
||||
editor,
|
||||
word
|
||||
),
|
||||
};
|
||||
@ -366,11 +353,7 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
getHighlighter(language) {
|
||||
if (
|
||||
["quickcommand", "javascript", "webjavascript"].includes(
|
||||
language
|
||||
)
|
||||
) {
|
||||
if (["quickcommand", "javascript", "webjavascript"].includes(language)) {
|
||||
return "javascript";
|
||||
}
|
||||
if (language === "cmd") {
|
||||
@ -378,10 +361,38 @@ export default defineComponent({
|
||||
}
|
||||
return language;
|
||||
},
|
||||
setCursorPosition(position) {
|
||||
if (!position.lineNumber || !position.column) return;
|
||||
editor.setPosition(position);
|
||||
},
|
||||
bindKeys() {
|
||||
// alt + z 换行
|
||||
const revWordWrap = this.wordWrap === "on" ? "off" : "on";
|
||||
editor.addCommand(monaco.KeyMod.Alt | monaco.KeyCode.KeyZ, () => {
|
||||
editor.updateOptions({ wordWrap: revWordWrap });
|
||||
});
|
||||
},
|
||||
repacleEditorSelection(text) {
|
||||
var selection = editor.getSelection();
|
||||
var range = new monaco.Range(
|
||||
selection.startLineNumber,
|
||||
selection.startColumn,
|
||||
selection.endLineNumber,
|
||||
selection.endColumn
|
||||
);
|
||||
var id = { major: 1, minor: 1 };
|
||||
var op = {
|
||||
identifier: id,
|
||||
range: range,
|
||||
text: text,
|
||||
forceMoveMarkers: true,
|
||||
};
|
||||
editor.executeEdits("my-source", [op]);
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
showPlaceholder() {
|
||||
return this.placeholder && (!this.value || this.value.trim() === "");
|
||||
return this.placeholder && !this.modelValue;
|
||||
},
|
||||
},
|
||||
});
|
||||
@ -390,10 +401,9 @@ export default defineComponent({
|
||||
<style scoped>
|
||||
.code-editor {
|
||||
width: 100%;
|
||||
border: 1px solid rgba(0, 0, 0, 0.12);
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.editor-container {
|
||||
@ -406,7 +416,7 @@ export default defineComponent({
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
padding-left: 45px;
|
||||
padding-left: 40px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@ -414,12 +424,6 @@ export default defineComponent({
|
||||
font-size: 14px;
|
||||
font-family: sans-serif;
|
||||
user-select: none;
|
||||
font-style: italic;
|
||||
opacity: 0;
|
||||
transition: opacity 0.1s ease-in-out;
|
||||
}
|
||||
|
||||
.code-editor:focus-within .placeholder {
|
||||
opacity: 0.3;
|
||||
opacity: 0.4;
|
||||
}
|
||||
</style>
|
@ -1,33 +1,29 @@
|
||||
<template>
|
||||
<q-expansion-item
|
||||
v-model="isExpanded"
|
||||
class="command-config"
|
||||
@dragover="isExpanded = false"
|
||||
@update:model-value="$emit('update:is-expanded', $event)"
|
||||
class="command-composer command-config"
|
||||
>
|
||||
<template v-slot:header>
|
||||
<div class="row q-col-gutter-sm basic-config">
|
||||
<div class="col-auto">
|
||||
<q-avatar size="36px" square class="featureIco">
|
||||
<q-img
|
||||
@click.stop="showIconPicker = true"
|
||||
:src="currentCommand.features.icon"
|
||||
/>
|
||||
</q-avatar>
|
||||
</div>
|
||||
<div class="col">
|
||||
<q-input
|
||||
:model-value="currentCommand.features.explain"
|
||||
filled
|
||||
dense
|
||||
@update:model-value="updateCommand('features.explain', $event)"
|
||||
placeholder="名称"
|
||||
@click.stop
|
||||
>
|
||||
<template v-slot:append>
|
||||
<q-icon name="drive_file_rename_outline" />
|
||||
</template>
|
||||
</q-input>
|
||||
</div>
|
||||
<div class="row basic-config">
|
||||
<q-avatar size="36px" square class="featureIco">
|
||||
<q-img
|
||||
@click.stop="showIconPicker = true"
|
||||
:src="currentCommand.features.icon"
|
||||
/>
|
||||
</q-avatar>
|
||||
<q-input
|
||||
ref="explainInput"
|
||||
:model-value="currentCommand.features.explain"
|
||||
borderless
|
||||
dense
|
||||
@update:model-value="updateModelValue('features.explain', $event)"
|
||||
@click.stop
|
||||
placeholder="请输入名称"
|
||||
@focus="expandOnFocus && updateExpanded(true)"
|
||||
class="col"
|
||||
>
|
||||
</q-input>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -59,7 +55,7 @@
|
||||
<MatchRuleEditor
|
||||
:showJson="showMatchRuleJson"
|
||||
:model-value="currentCommand.features.cmds"
|
||||
@update:model-value="updateCommand('features.cmds', $event)"
|
||||
@update:model-value="updateModelValue('features.cmds', $event)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -71,7 +67,7 @@
|
||||
</div>
|
||||
<q-select
|
||||
:model-value="currentCommand.tags"
|
||||
@update:model-value="updateCommand('tags', $event)"
|
||||
@update:model-value="updateModelValue('tags', $event)"
|
||||
:options="allQuickCommandTags"
|
||||
dense
|
||||
options-dense
|
||||
@ -81,6 +77,7 @@
|
||||
multiple
|
||||
hide-dropdown-icon
|
||||
new-value-mode="add-unique"
|
||||
popup-content-class="command-tag-popup"
|
||||
placeholder="回车添加,最多3个"
|
||||
max-values="3"
|
||||
@new-value="tagVerify"
|
||||
@ -101,7 +98,7 @@
|
||||
<ButtonGroup
|
||||
:model-value="currentCommand.output"
|
||||
:options="outputTypesOptionsDy"
|
||||
@update:model-value="updateCommand('output', $event)"
|
||||
@update:model-value="updateModelValue('output', $event)"
|
||||
height="26px"
|
||||
/>
|
||||
</div>
|
||||
@ -138,7 +135,7 @@
|
||||
<CheckGroup
|
||||
:model-value="currentCommand.features.platform"
|
||||
:options="Object.values(platformTypes)"
|
||||
@update:model-value="updateCommand('features.platform', $event)"
|
||||
@update:model-value="handlePlatformChange"
|
||||
height="30px"
|
||||
/>
|
||||
</div>
|
||||
@ -147,7 +144,7 @@
|
||||
<!-- 图标选择对话框 -->
|
||||
<q-dialog v-model="showIconPicker" position="left">
|
||||
<iconPicker
|
||||
@iconChanged="(dataUrl) => updateCommand('features.icon', dataUrl)"
|
||||
@iconChanged="(dataUrl) => updateModelValue('features.icon', dataUrl)"
|
||||
ref="icon"
|
||||
/>
|
||||
</q-dialog>
|
||||
@ -155,7 +152,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent, computed } from "vue";
|
||||
import { defineComponent } from "vue";
|
||||
import iconPicker from "components/popup/IconPicker.vue";
|
||||
import outputTypes from "js/options/outputTypes.js";
|
||||
import platformTypes from "js/options/platformTypes.js";
|
||||
@ -178,8 +175,12 @@ export default defineComponent({
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
expandOnFocus: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
emits: ["update:modelValue", "update:is-expanded"],
|
||||
data() {
|
||||
return {
|
||||
commandManager: useCommandManager(),
|
||||
@ -205,29 +206,42 @@ export default defineComponent({
|
||||
currentCommand() {
|
||||
return this.modelValue;
|
||||
},
|
||||
commandTypesOptions() {
|
||||
const options = Object.values(this.commandTypes);
|
||||
return this.currentCommand.features.mainPush
|
||||
? options.map((cmdType) =>
|
||||
["regex", "over", "key"].includes(cmdType.name)
|
||||
? cmdType
|
||||
: { ...cmdType, disabled: true }
|
||||
)
|
||||
: options;
|
||||
},
|
||||
outputTypesOptionsDy() {
|
||||
const options = Object.values(this.outputTypes);
|
||||
return this.currentCommand.features.mainPush
|
||||
? options.map((outputType) =>
|
||||
outputType.name !== "text"
|
||||
? { ...outputType, disabled: true }
|
||||
: outputType
|
||||
)
|
||||
: options;
|
||||
if (this.currentCommand.features.mainPush) {
|
||||
return this.setOutputOptionDisabled(options, "text", false);
|
||||
}
|
||||
if (this.currentCommand.program === "html") {
|
||||
return this.setOutputOptionDisabled(options, "html", false);
|
||||
}
|
||||
if (
|
||||
["quickcommand", "quickcomposer"].includes(this.currentCommand.program)
|
||||
) {
|
||||
return this.setOutputOptionDisabled(options, "terminal", true);
|
||||
}
|
||||
return options;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
if (!this.modelValue.features.explain) {
|
||||
setTimeout(this.$refs.explainInput.focus);
|
||||
}
|
||||
// 添加全局点击事件监听器
|
||||
document.addEventListener("click", this.handleOutsideClick);
|
||||
},
|
||||
beforeUnmount() {
|
||||
// 组件销毁前移除监听器
|
||||
document.removeEventListener("click", this.handleOutsideClick);
|
||||
},
|
||||
methods: {
|
||||
updateCommand(path, value) {
|
||||
setOutputOptionDisabled(options, option, disabled = true) {
|
||||
return options.map((opt) =>
|
||||
opt.name === option
|
||||
? { ...opt, disabled }
|
||||
: { ...opt, disabled: !disabled }
|
||||
);
|
||||
},
|
||||
updateModelValue(path, value) {
|
||||
const newCommand = { ...this.currentCommand };
|
||||
const keys = path.split(".");
|
||||
const lastKey = keys.pop();
|
||||
@ -246,12 +260,6 @@ export default defineComponent({
|
||||
if (!inputValue) return;
|
||||
ref.add(inputValue, true);
|
||||
},
|
||||
handleMainPushChange(val) {
|
||||
this.updateCommand("features.mainPush", val);
|
||||
if (val) {
|
||||
this.updateCommand("output", "text");
|
||||
}
|
||||
},
|
||||
showMainPushHelp() {
|
||||
window.showUb.help("#u0e9f1430");
|
||||
},
|
||||
@ -262,6 +270,33 @@ export default defineComponent({
|
||||
)
|
||||
.run();
|
||||
},
|
||||
handleOutsideClick(event) {
|
||||
// 如果面板已经折叠,不需要处理
|
||||
if (!this.isExpanded) return;
|
||||
|
||||
// 检查点击是否在标签选择框的弹出菜单内
|
||||
const tagPopup = document.querySelector(".command-tag-popup");
|
||||
if (tagPopup?.contains(event.target)) return;
|
||||
|
||||
// 检查点击是否在组件内部
|
||||
const componentEl = this.$el;
|
||||
if (componentEl.contains(event.target)) return;
|
||||
this.updateExpanded(false);
|
||||
},
|
||||
handleMainPushChange(newMainPush) {
|
||||
this.updateModelValue("features.mainPush", newMainPush);
|
||||
if (newMainPush) {
|
||||
this.updateModelValue("output", "text");
|
||||
}
|
||||
},
|
||||
handlePlatformChange(newPlatform) {
|
||||
if (newPlatform.length === 0) return;
|
||||
this.updateModelValue("features.platform", newPlatform);
|
||||
},
|
||||
updateExpanded(value) {
|
||||
this.isExpanded = value;
|
||||
this.$emit("update:is-expanded", value);
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
@ -280,6 +315,11 @@ export default defineComponent({
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.basic-config :deep(.q-field__native),
|
||||
.basic-config :deep(.q-field__control) {
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.command-config :deep(.q-item) {
|
||||
padding: 0;
|
||||
min-height: unset;
|
||||
@ -314,6 +354,7 @@ export default defineComponent({
|
||||
.featureIco {
|
||||
cursor: pointer;
|
||||
transition: 0.2s;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.featureIco:hover {
|
||||
|
@ -1,168 +1,231 @@
|
||||
<template>
|
||||
<div class="row" v-show="!!height">
|
||||
<div class="col">
|
||||
<div>
|
||||
<q-select
|
||||
dense
|
||||
standout="bg-primary text-white"
|
||||
square
|
||||
hide-bottom-space
|
||||
color="primary"
|
||||
transition-show="jump-down"
|
||||
transition-hide="jump-up"
|
||||
@update:model-value="updateProgram"
|
||||
:model-value="modelValue.program"
|
||||
:options="programLanguages"
|
||||
<div class="command-language-bar">
|
||||
<q-select
|
||||
class="q-pl-xs"
|
||||
dense
|
||||
options-dense
|
||||
borderless
|
||||
square
|
||||
hide-bottom-space
|
||||
color="primary"
|
||||
transition-show="jump-down"
|
||||
transition-hide="jump-up"
|
||||
:model-value="currentCommand.program"
|
||||
@update:model-value="handleProgramChange"
|
||||
:options="Object.keys(programs)"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-badge
|
||||
label="环境"
|
||||
>
|
||||
<template v-slot:append>
|
||||
<q-avatar size="lg" square>
|
||||
<img :src="programs[modelValue.program].icon" />
|
||||
</q-avatar>
|
||||
</template>
|
||||
<template v-slot:option="scope">
|
||||
<q-item v-bind="scope.itemProps">
|
||||
<q-item-section avatar>
|
||||
<img width="32" :src="programs[scope.opt].icon" />
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label v-html="scope.opt" />
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
</q-select>
|
||||
</div>
|
||||
</div>
|
||||
<q-separator vertical />
|
||||
<div class="col-auto justify-end flex">
|
||||
<q-btn-group unelevated class="button-group">
|
||||
<template v-if="modelValue.program === 'quickcommand'">
|
||||
<q-btn
|
||||
v-for="(item, index) in [
|
||||
'help_center',
|
||||
'view_timeline',
|
||||
]"
|
||||
color="primary"
|
||||
text-color="white"
|
||||
class="q-ml-sm"
|
||||
style="height: 20px"
|
||||
/>
|
||||
</template>
|
||||
<template v-slot:append>
|
||||
<q-avatar size="20px" square v-if="isRunCodePage">
|
||||
<img :src="programs[currentCommand.program].icon" />
|
||||
</q-avatar>
|
||||
</template>
|
||||
<template v-slot:option="scope">
|
||||
<q-item v-bind="scope.itemProps">
|
||||
<q-item-section avatar>
|
||||
<img width="32" :src="programs[scope.opt].icon" />
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label v-html="scope.opt" />
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
</q-select>
|
||||
<q-btn-group unelevated class="button-group">
|
||||
<q-btn-dropdown
|
||||
class="special-var-btn"
|
||||
dense
|
||||
flat
|
||||
label="变量"
|
||||
color="primary"
|
||||
icon="data_object"
|
||||
>
|
||||
<q-list>
|
||||
<q-item
|
||||
v-for="(item, index) in Object.values(specialVars)"
|
||||
:key="index"
|
||||
dense
|
||||
flat
|
||||
color="primary"
|
||||
class="settings-btn"
|
||||
:icon="item"
|
||||
@click="handleQuickCommandAction(index)"
|
||||
clickable
|
||||
v-close-popup
|
||||
@click="handleSpecialVarClick(item)"
|
||||
>
|
||||
<q-tooltip>
|
||||
{{ ["查看文档", "可视化编排"][index] }}
|
||||
</q-tooltip>
|
||||
</q-btn>
|
||||
</template>
|
||||
<q-item-section>
|
||||
<q-item-label class="row items-center justify-between">
|
||||
<div v-text="item.label" />
|
||||
<div v-if="item.onlyCmdTypes" class="row">
|
||||
<q-badge color="grey-9" class="q-ml-xs"> 仅 </q-badge>
|
||||
<q-badge
|
||||
v-for="type in item.onlyCmdTypes"
|
||||
:key="type"
|
||||
class="q-ml-xs"
|
||||
v-text="commandTypes[type].label"
|
||||
color="grey-9"
|
||||
/>
|
||||
</div>
|
||||
</q-item-label>
|
||||
<q-tooltip v-if="item.tooltip">
|
||||
{{ item.tooltip }}
|
||||
</q-tooltip>
|
||||
<q-item-label caption>{{ item.desc }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-btn-dropdown>
|
||||
|
||||
<q-btn-dropdown
|
||||
v-else-if="modelValue.program !== 'html'"
|
||||
<template v-if="currentCommand.program === 'quickcommand'">
|
||||
<q-btn
|
||||
v-for="(item, index) in ['help_center', 'view_timeline']"
|
||||
:key="index"
|
||||
dense
|
||||
flat
|
||||
color="primary"
|
||||
class="settings-btn"
|
||||
dense
|
||||
flat
|
||||
ref="settings"
|
||||
color="primary"
|
||||
icon="settings"
|
||||
:label="['文档', '可视化'][index]"
|
||||
:icon="item"
|
||||
@click="handleQuickCommandAction(index)"
|
||||
>
|
||||
<q-list>
|
||||
<!-- 自定义解释器 -->
|
||||
<q-item
|
||||
v-for="(item, index) in Object.keys(modelValue.customOptions)"
|
||||
:key="index"
|
||||
v-show="modelValue.program === 'custom'"
|
||||
</q-btn>
|
||||
</template>
|
||||
|
||||
<q-btn-dropdown
|
||||
v-model="isSettingsVisible"
|
||||
v-else-if="currentCommand.program !== 'html'"
|
||||
class="settings-btn"
|
||||
dense
|
||||
flat
|
||||
label="设置"
|
||||
color="primary"
|
||||
icon="settings"
|
||||
>
|
||||
<q-list>
|
||||
<!-- 自定义解释器 -->
|
||||
<q-item
|
||||
v-for="(item, index) in Object.keys(currentCommand.customOptions)"
|
||||
:key="index"
|
||||
v-show="currentCommand.program === 'custom'"
|
||||
>
|
||||
<q-input
|
||||
stack-label
|
||||
autofocus
|
||||
dense
|
||||
outlined
|
||||
class="full-width"
|
||||
:label="
|
||||
[
|
||||
'解释器路径,如:/opt/python',
|
||||
'运行参数,如:-u',
|
||||
'脚本后缀,不含点,如:py',
|
||||
][index]
|
||||
"
|
||||
:model-value="currentCommand.customOptions[item]"
|
||||
@update:model-value="
|
||||
(val) => updateModelValue(`customOptions.${item}`, val)
|
||||
"
|
||||
>
|
||||
<q-input
|
||||
stack-label
|
||||
autofocus
|
||||
dense
|
||||
outlined
|
||||
class="full-width"
|
||||
@blur="matchLanguage"
|
||||
:label="
|
||||
[
|
||||
'解释器路径,如:/opt/python',
|
||||
'运行参数,如:-u',
|
||||
'脚本后缀,不含点,如:py',
|
||||
][index]
|
||||
"
|
||||
:model-value="modelValue.customOptions[item]"
|
||||
@update:model-value="(val) => updateCustomOption(item, val)"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="code" />
|
||||
</template>
|
||||
</q-input>
|
||||
</q-item>
|
||||
<!-- 脚本参数 -->
|
||||
<q-item v-show="modelValue.program !== 'quickcommand'">
|
||||
<q-input
|
||||
dense
|
||||
stack-label
|
||||
outlined
|
||||
label="脚本参数"
|
||||
class="full-width"
|
||||
:model-value="modelValue.scptarg"
|
||||
@update:model-value="updateScptarg"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="input" />
|
||||
</template>
|
||||
</q-input>
|
||||
</q-item>
|
||||
<!-- 编码设置 -->
|
||||
<q-item
|
||||
v-for="(item, index) in Object.keys(modelValue.charset)"
|
||||
:key="index"
|
||||
v-show="modelValue.program !== 'quickcommand'"
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="code" />
|
||||
</template>
|
||||
</q-input>
|
||||
</q-item>
|
||||
<!-- 脚本参数 -->
|
||||
<q-item v-show="currentCommand.program !== 'quickcommand'">
|
||||
<q-input
|
||||
dense
|
||||
stack-label
|
||||
outlined
|
||||
label="脚本参数"
|
||||
class="full-width"
|
||||
:model-value="currentCommand.scptarg"
|
||||
@update:model-value="updateModelValue('scptarg', $event)"
|
||||
>
|
||||
<q-select
|
||||
dense
|
||||
outlined
|
||||
stack-label
|
||||
clearable
|
||||
class="full-width"
|
||||
:label="['脚本编码', '输出编码'][index]"
|
||||
:model-value="modelValue.charset[item]"
|
||||
@update:model-value="(val) => updateCharset(item, val)"
|
||||
:options="['GBK', 'utf8', 'Big5']"
|
||||
type="text"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon :name="['format_size', 'output'][index]" />
|
||||
</template>
|
||||
</q-select>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-btn-dropdown>
|
||||
<q-separator vertical inset />
|
||||
<q-btn
|
||||
class="action-btn run-btn"
|
||||
dense
|
||||
flat
|
||||
color="primary"
|
||||
icon="play_arrow"
|
||||
label="运行"
|
||||
@click="$emit('run')"
|
||||
></q-btn>
|
||||
<q-btn
|
||||
class="action-btn save-btn"
|
||||
flat
|
||||
dense
|
||||
v-if="!isRunCodePage"
|
||||
:disable="!canCommandSave"
|
||||
:color="canCommandSave ? 'primary' : 'grey'"
|
||||
icon="save"
|
||||
label="保存"
|
||||
@click="$emit('save')"
|
||||
></q-btn>
|
||||
</q-btn-group>
|
||||
</div>
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="input" />
|
||||
</template>
|
||||
</q-input>
|
||||
</q-item>
|
||||
<!-- 编码设置 -->
|
||||
<q-item
|
||||
v-for="(item, index) in Object.keys(currentCommand.charset)"
|
||||
:key="index"
|
||||
v-show="currentCommand.program !== 'quickcommand'"
|
||||
>
|
||||
<q-select
|
||||
dense
|
||||
outlined
|
||||
stack-label
|
||||
clearable
|
||||
class="full-width"
|
||||
:label="['脚本编码', '输出编码'][index]"
|
||||
:model-value="currentCommand.charset[item]"
|
||||
@update:model-value="
|
||||
(val) => updateModelValue(`charset.${item}`, val)
|
||||
"
|
||||
:options="['GBK', 'utf8', 'Big5']"
|
||||
type="text"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon :name="['format_size', 'output'][index]" />
|
||||
</template>
|
||||
</q-select>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-btn-dropdown>
|
||||
<q-separator vertical inset />
|
||||
<q-btn
|
||||
v-if="!isRunCodePage"
|
||||
class="action-btn run-btn"
|
||||
dense
|
||||
flat
|
||||
color="primary"
|
||||
icon="arrow_back"
|
||||
label="退出"
|
||||
@click="$emit('action', 'back')"
|
||||
></q-btn>
|
||||
<q-btn
|
||||
class="action-btn run-btn"
|
||||
dense
|
||||
flat
|
||||
color="primary"
|
||||
icon="play_arrow"
|
||||
label="运行"
|
||||
@click="$emit('action', 'run')"
|
||||
></q-btn>
|
||||
<q-btn
|
||||
v-if="!isRunCodePage"
|
||||
:disable="!canCommandSave"
|
||||
:color="canCommandSave ? 'primary' : 'grey'"
|
||||
class="action-btn save-btn"
|
||||
flat
|
||||
dense
|
||||
icon="save"
|
||||
label="保存"
|
||||
@click="$emit('action', 'save')"
|
||||
></q-btn>
|
||||
</q-btn-group>
|
||||
<q-dialog v-model="showUserData">
|
||||
<UserData
|
||||
@insertText="
|
||||
insertSpecialVar($event);
|
||||
showUserData = false;
|
||||
"
|
||||
:showInsertBtn="true"
|
||||
/>
|
||||
</q-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import programs from "js/options/programs.js";
|
||||
import specialVars from "js/options/specialVars.js";
|
||||
import commandTypes from "js/options/commandTypes.js";
|
||||
import UserData from "components/popup/UserData.vue";
|
||||
|
||||
export default {
|
||||
name: "CommandLanguageBar",
|
||||
@ -171,10 +234,6 @@ export default {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
default: 40,
|
||||
},
|
||||
canCommandSave: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
@ -184,149 +243,121 @@ export default {
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: [
|
||||
"update:modelValue",
|
||||
"program-changed",
|
||||
"run",
|
||||
"save",
|
||||
"show-composer",
|
||||
],
|
||||
emits: ["update:modelValue", "action"],
|
||||
components: {
|
||||
UserData,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
programs,
|
||||
specialVars,
|
||||
commandTypes,
|
||||
isSettingsVisible: false,
|
||||
showUserData: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
programLanguages() {
|
||||
return Object.keys(this.programs);
|
||||
currentCommand() {
|
||||
return this.modelValue;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
updateProgram(value) {
|
||||
this.$emit("update:modelValue", {
|
||||
...this.modelValue,
|
||||
program: value,
|
||||
});
|
||||
this.programChanged(value);
|
||||
},
|
||||
updateCustomOption(key, value) {
|
||||
this.$emit("update:modelValue", {
|
||||
...this.modelValue,
|
||||
customOptions: {
|
||||
...this.modelValue.customOptions,
|
||||
[key]: value,
|
||||
},
|
||||
});
|
||||
},
|
||||
updateScptarg(value) {
|
||||
this.$emit("update:modelValue", {
|
||||
...this.modelValue,
|
||||
scptarg: value,
|
||||
});
|
||||
},
|
||||
updateCharset(key, value) {
|
||||
this.$emit("update:modelValue", {
|
||||
...this.modelValue,
|
||||
charset: {
|
||||
...this.modelValue.charset,
|
||||
[key]: value,
|
||||
},
|
||||
});
|
||||
},
|
||||
programChanged(value) {
|
||||
this.$emit("program-changed", value);
|
||||
if (value === "custom") {
|
||||
this.$refs.settings.show();
|
||||
handleProgramChange(newProgram) {
|
||||
const newCommand = { ...this.currentCommand };
|
||||
newCommand.program = newProgram;
|
||||
if (newProgram === "custom") {
|
||||
this.isSettingsVisible = true;
|
||||
}
|
||||
},
|
||||
matchLanguage() {
|
||||
if (!this.modelValue.customOptions.ext) return;
|
||||
let language = Object.values(this.programs).filter(
|
||||
(program) => program.ext === this.modelValue.customOptions.ext
|
||||
);
|
||||
if (language.length) {
|
||||
this.$emit("program-changed", language[0].name);
|
||||
if (newProgram === "html") {
|
||||
newCommand.output = "html";
|
||||
}
|
||||
const featuresIcon = this.currentCommand.features.icon || "";
|
||||
if (featuresIcon.slice(0, 10) !== "data:image") {
|
||||
newCommand.features.icon = this.programs[newProgram].icon;
|
||||
}
|
||||
this.$emit("update:modelValue", newCommand);
|
||||
},
|
||||
updateModelValue(keyPath, value) {
|
||||
const newModelValue = { ...this.modelValue };
|
||||
const keys = keyPath.split(".");
|
||||
const lastKey = keys.pop();
|
||||
const target = keys.reduce((obj, key) => obj[key], newModelValue);
|
||||
target[lastKey] = value;
|
||||
this.$emit("update:modelValue", newModelValue);
|
||||
},
|
||||
handleQuickCommandAction(index) {
|
||||
const actions = [
|
||||
() => this.showHelp(),
|
||||
() => this.$emit("show-composer"),
|
||||
() => this.$emit("action", "show-composer"),
|
||||
];
|
||||
actions[index]();
|
||||
},
|
||||
showHelp() {
|
||||
window.showUb.docs();
|
||||
},
|
||||
handleSpecialVarClick(item) {
|
||||
if (item.label === "{{usr:}}") this.showUserData = true;
|
||||
else this.insertSpecialVar(item.label);
|
||||
},
|
||||
insertSpecialVar(text) {
|
||||
if (!text) return;
|
||||
this.$emit("action", "insert-text", `"${text}"`);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.button-group {
|
||||
.action-btn {
|
||||
padding: 0 10px;
|
||||
}
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.button-group :deep(.q-focus-helper) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.button-group :deep(.q-btn__content) {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.button-group :deep(.q-btn-dropdown__arrow) {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.button-group .q-btn:hover {
|
||||
filter: brightness(1.2);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
/* 运行按钮动画 */
|
||||
.run-btn:hover :deep(.q-icon) {
|
||||
display: inline-block;
|
||||
animation: slideRight 1.5s infinite;
|
||||
animation: leftRight 1.5s infinite;
|
||||
}
|
||||
|
||||
/* 保存按钮动画 */
|
||||
.save-btn:not([disabled]):hover :deep(.q-icon) {
|
||||
display: inline-block;
|
||||
animation: saveAnimation 1.2s infinite;
|
||||
animation: upDown 1.2s infinite;
|
||||
}
|
||||
|
||||
/* 设置按钮动画 */
|
||||
.settings-btn :deep(.q-icon:first-child) {
|
||||
display: inline-block;
|
||||
transform: scale(1);
|
||||
transition: transform 0.5s ease;
|
||||
.command-language-bar {
|
||||
background-color: #fffffe;
|
||||
height: 30px;
|
||||
margin-bottom: 2px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.settings-btn:hover :deep(.q-icon:first-child) {
|
||||
transform: scale(1.05);
|
||||
.body--dark .command-language-bar {
|
||||
background-color: #1e1e1e;
|
||||
}
|
||||
|
||||
@keyframes slideRight {
|
||||
0% {
|
||||
transform: translateX(-2px);
|
||||
opacity: 0.7;
|
||||
}
|
||||
50% {
|
||||
transform: translateX(2px);
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: translateX(-2px);
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes saveAnimation {
|
||||
0% {
|
||||
transform: translateY(-1px);
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
transform: translateY(1px);
|
||||
opacity: 0.6;
|
||||
}
|
||||
75% {
|
||||
transform: translateY(0.5px);
|
||||
opacity: 0.8;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(-1px);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-btn:hover :deep(.q-icon) {
|
||||
display: inline-block;
|
||||
.command-language-bar :deep(.q-field__control),
|
||||
.command-language-bar :deep(.q-field__control > *),
|
||||
.command-language-bar :deep(.q-field__native) {
|
||||
max-height: 30px;
|
||||
min-height: 30px;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,739 +0,0 @@
|
||||
<template>
|
||||
<div
|
||||
class="command-side-bar"
|
||||
:style="{
|
||||
width: sideBarWidth + 'px',
|
||||
'--icon-url': `url(${currentCommand.features.icon})`,
|
||||
}"
|
||||
>
|
||||
<!-- 头部区域 -->
|
||||
<div class="header-section">
|
||||
<div class="header-content">
|
||||
<q-btn
|
||||
dense
|
||||
flat
|
||||
color="grey"
|
||||
icon="arrow_back_ios_new"
|
||||
v-close-popup
|
||||
class="back-btn"
|
||||
@click="$emit('back')"
|
||||
/>
|
||||
<div class="logo-container">
|
||||
<q-avatar size="64" square class="featureIco">
|
||||
<q-img
|
||||
@click="showIconPicker = true"
|
||||
:src="currentCommand.features.icon"
|
||||
/>
|
||||
</q-avatar>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 可滚动的内容区域 -->
|
||||
<q-scroll-area
|
||||
:thumb-style="{
|
||||
width: '3px',
|
||||
}"
|
||||
:horizontal-thumb-style="{
|
||||
height: '5px',
|
||||
}"
|
||||
class="scroll-area"
|
||||
>
|
||||
<div
|
||||
class="row"
|
||||
:style="{
|
||||
paddingLeft: sideBarPadding + 'px',
|
||||
paddingRight: sideBarPadding + 'px',
|
||||
paddingBottom: sideBarPadding + 'px',
|
||||
}"
|
||||
>
|
||||
<div class="col-12">
|
||||
<div class="row">
|
||||
<div
|
||||
class="command-side-bar-content"
|
||||
:style="{ width: sideBarWidth - sideBarPadding * 2 + 'px' }"
|
||||
>
|
||||
<!-- 说明 -->
|
||||
<q-input
|
||||
:disable="!canCommandSave"
|
||||
stack-label
|
||||
label-color="primary"
|
||||
borderless
|
||||
square
|
||||
v-model="currentCommand.features.explain"
|
||||
type="text"
|
||||
placeholder="请输入说明"
|
||||
label="说明"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon
|
||||
class="command-side-bar-icon"
|
||||
name="drive_file_rename_outline"
|
||||
/>
|
||||
</template>
|
||||
</q-input>
|
||||
<!-- 匹配类型 -->
|
||||
<q-select
|
||||
:disable="!canCommandSave"
|
||||
popup-content-class="side-bar-popup-content"
|
||||
hide-dropdown-icon
|
||||
stack-label
|
||||
label-color="primary"
|
||||
transition-show="jump-down"
|
||||
transition-hide="jump-up"
|
||||
borderless
|
||||
square
|
||||
@update:model-value="(val) => handleCmdTypeChange(val)"
|
||||
:options="commandTypesOptions"
|
||||
v-model="cmdType"
|
||||
type="text"
|
||||
label="匹配类型"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon class="command-side-bar-icon" :name="cmdType.icon" />
|
||||
</template>
|
||||
<template v-slot:option="scope">
|
||||
<q-item v-bind="scope.itemProps" class="row items-center">
|
||||
<q-icon :name="scope.opt.icon" class="q-mr-md" />
|
||||
<div>
|
||||
<q-item-label v-html="scope.opt.name" />
|
||||
<q-item-label caption>{{ scope.opt.desc }}</q-item-label>
|
||||
</div>
|
||||
</q-item>
|
||||
</template>
|
||||
</q-select>
|
||||
<!-- 匹配规则 -->
|
||||
<q-select
|
||||
v-if="cmdType.valueType === 'array'"
|
||||
:disable="!canCommandSave"
|
||||
popup-content-class="side-bar-popup-content"
|
||||
hide-dropdown-icon
|
||||
stack-label
|
||||
label-color="primary"
|
||||
transition-show="jump-down"
|
||||
transition-hide="jump-up"
|
||||
borderless
|
||||
square
|
||||
v-model="cmdMatch"
|
||||
max-values="3"
|
||||
type="text"
|
||||
placeholder="回车添加"
|
||||
use-input
|
||||
use-chips
|
||||
multiple
|
||||
new-value-mode="add-unique"
|
||||
input-debounce="0"
|
||||
:label="cmdType.matchLabel"
|
||||
ref="cmdMatchRef"
|
||||
@blur="(e) => autoAddInputVal(e, $refs.cmdMatchRef)"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon class="command-side-bar-icon" name="square_foot" />
|
||||
</template>
|
||||
</q-select>
|
||||
<q-input
|
||||
v-else
|
||||
:disable="!canCommandSave"
|
||||
autogrow
|
||||
borderless
|
||||
square
|
||||
v-model="cmdMatch"
|
||||
hide-bottom-space
|
||||
@blur="regexVerify"
|
||||
:readonly="!cmdType.valueType"
|
||||
type="text"
|
||||
:label="cmdType.matchLabel"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon class="command-side-bar-icon" name="square_foot" />
|
||||
</template>
|
||||
<template v-slot:append>
|
||||
<q-icon
|
||||
v-if="cmdType.name === 'files'"
|
||||
name="folder"
|
||||
size="xs"
|
||||
:color="isFileTypeDirectory ? 'primary' : ''"
|
||||
@click="isFileTypeDirectory = !isFileTypeDirectory"
|
||||
style="cursor: pointer"
|
||||
>
|
||||
<q-tooltip>
|
||||
切换匹配类型,当前:{{
|
||||
isFileTypeDirectory ? "文件夹" : "文件"
|
||||
}}
|
||||
</q-tooltip>
|
||||
</q-icon>
|
||||
</template>
|
||||
</q-input>
|
||||
<!-- 标签 -->
|
||||
<q-select
|
||||
:disable="!canCommandSave"
|
||||
hide-dropdown-icon
|
||||
stack-label
|
||||
popup-content-class="side-bar-popup-content"
|
||||
label-color="primary"
|
||||
transition-show="jump-down"
|
||||
transition-hide="jump-up"
|
||||
borderless
|
||||
square
|
||||
v-model="currentCommand.tags"
|
||||
max-values="3"
|
||||
type="text"
|
||||
label="标签"
|
||||
placeholder="回车添加"
|
||||
use-input
|
||||
use-chips
|
||||
multiple
|
||||
new-value-mode="add-unique"
|
||||
@new-value="tagVerify"
|
||||
input-debounce="0"
|
||||
:options="allQuickCommandTags"
|
||||
ref="commandTagRef"
|
||||
@blur="(e) => autoAddInputVal(e, $refs.commandTagRef)"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon class="command-side-bar-icon" name="label" />
|
||||
</template>
|
||||
</q-select>
|
||||
<!-- 特殊变量 -->
|
||||
<q-select
|
||||
:disable="!canCommandSave"
|
||||
hide-dropdown-icon
|
||||
popup-content-class="side-bar-popup-content"
|
||||
stack-label
|
||||
label-color="primary"
|
||||
transition-show="jump-down"
|
||||
transition-hide="jump-up"
|
||||
borderless
|
||||
@popup-hide="
|
||||
() => {
|
||||
if (specialVar.label === '{{usr:}}') showUserData = true;
|
||||
else insertSpecialVar(specialVar.label);
|
||||
}
|
||||
"
|
||||
square
|
||||
:options="specialVarsOptions"
|
||||
v-model="specialVar"
|
||||
input-debounce="0"
|
||||
type="text"
|
||||
label="特殊变量"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon class="command-side-bar-icon" name="attach_money" />
|
||||
</template>
|
||||
<template v-slot:option="scope">
|
||||
<q-item v-bind="scope.itemProps">
|
||||
<q-item-section>
|
||||
<q-item-label v-html="scope.opt.label" />
|
||||
<q-tooltip v-if="scope.opt.tooltip">
|
||||
{{ scope.opt.tooltip }}
|
||||
</q-tooltip>
|
||||
<q-item-label caption>{{ scope.opt.desc }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</template></q-select
|
||||
>
|
||||
<!-- 输出 -->
|
||||
<q-select
|
||||
:disable="!canCommandSave"
|
||||
hide-dropdown-icon
|
||||
stack-label
|
||||
label-color="primary"
|
||||
popup-content-class="side-bar-popup-content"
|
||||
transition-show="jump-down"
|
||||
transition-hide="jump-up"
|
||||
borderless
|
||||
square
|
||||
color="primary"
|
||||
v-model="currentCommand.output"
|
||||
:display-value="outputTypes[currentCommand.output].label"
|
||||
:options="outputTypesOptionsDy"
|
||||
label="输出"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon
|
||||
class="command-side-bar-icon"
|
||||
:name="outputTypes[currentCommand.output].icon"
|
||||
/>
|
||||
</template>
|
||||
<template v-slot:option="scope">
|
||||
<q-item v-bind="scope.itemProps" class="row items-center">
|
||||
<q-icon
|
||||
:name="outputTypes[scope.opt].icon"
|
||||
class="q-mr-md"
|
||||
/>
|
||||
<div>
|
||||
<q-item-label v-html="outputTypes[scope.opt].label" />
|
||||
</div>
|
||||
</q-item>
|
||||
</template>
|
||||
</q-select>
|
||||
<!-- 搜索面板推送 -->
|
||||
<q-select
|
||||
:disable="!canCommandSave"
|
||||
hide-dropdown-icon
|
||||
stack-label
|
||||
label-color="primary"
|
||||
popup-content-class="side-bar-popup-content"
|
||||
transition-show="jump-down"
|
||||
transition-hide="jump-up"
|
||||
borderless
|
||||
square
|
||||
v-model="searchPushValue"
|
||||
:options="searchPushOptions"
|
||||
label="搜索面板推送"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon class="command-side-bar-icon" name="search" />
|
||||
</template>
|
||||
<template v-slot:option="scope">
|
||||
<q-item v-bind="scope.itemProps">
|
||||
<q-item-section>
|
||||
<q-item-label>{{ scope.opt.label }}</q-item-label>
|
||||
<q-item-label caption>{{ scope.opt.desc }}</q-item-label>
|
||||
</q-item-section>
|
||||
<q-item-section side v-if="scope.opt.value">
|
||||
<q-btn
|
||||
flat
|
||||
round
|
||||
icon="help_outline"
|
||||
size="xs"
|
||||
dense
|
||||
@click.stop="showMainPushHelp"
|
||||
/>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
</q-select>
|
||||
<!-- 平台 -->
|
||||
<q-select
|
||||
:disable="!canCommandSave"
|
||||
hide-dropdown-icon
|
||||
stack-label
|
||||
label-color="primary"
|
||||
popup-content-class="side-bar-popup-content"
|
||||
transition-show="jump-down"
|
||||
transition-hide="jump-up"
|
||||
borderless
|
||||
square
|
||||
:options="Object.keys(platformTypes)"
|
||||
use-chips
|
||||
@blur="platformVerify()"
|
||||
v-model="currentCommand.features.platform"
|
||||
multiple
|
||||
label="平台"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon class="command-side-bar-icon" name="window" />
|
||||
</template>
|
||||
<template v-slot:selected-item="scope">
|
||||
<q-chip
|
||||
removable
|
||||
dense
|
||||
@remove="scope.removeAtIndex(scope.index)"
|
||||
:tabindex="scope.tabindex"
|
||||
>
|
||||
{{ platformTypes[scope.opt].label }}
|
||||
</q-chip>
|
||||
</template>
|
||||
<template v-slot:option="scope">
|
||||
<q-item v-bind="scope.itemProps" class="row items-center">
|
||||
<q-img
|
||||
:src="platformTypes[scope.opt].icon"
|
||||
width="24px"
|
||||
class="q-mr-md"
|
||||
/>
|
||||
<div>
|
||||
<q-item-label v-html="platformTypes[scope.opt].label" />
|
||||
<q-item-label caption>{{ scope.opt.desc }}</q-item-label>
|
||||
</div>
|
||||
</q-item>
|
||||
</template>
|
||||
</q-select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</q-scroll-area>
|
||||
|
||||
<!-- 对话框部分保持不变 -->
|
||||
<q-dialog v-model="showIconPicker" position="left">
|
||||
<iconPicker
|
||||
@iconChanged="(dataUrl) => (currentCommand.features.icon = dataUrl)"
|
||||
ref="icon"
|
||||
/>
|
||||
</q-dialog>
|
||||
<q-dialog v-model="showUserData">
|
||||
<UserData @insertText="insertSpecialVar" :showInsertBtn="true" />
|
||||
</q-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import commandTypes from "js/options/commandTypes.js";
|
||||
import outputTypes from "js/options/outputTypes.js";
|
||||
import specialVars from "js/options/specialVars.js";
|
||||
import platformTypes from "js/options/platformTypes.js";
|
||||
import iconPicker from "components/popup/IconPicker.vue";
|
||||
import UserData from "components/popup/UserData.vue";
|
||||
|
||||
export default {
|
||||
components: { iconPicker, UserData },
|
||||
data() {
|
||||
return {
|
||||
currentCommand: {
|
||||
tags: [],
|
||||
output: "text",
|
||||
features: {
|
||||
explain: "",
|
||||
platform: ["win32", "linux", "darwin"],
|
||||
icon: "",
|
||||
mainPush: false,
|
||||
},
|
||||
},
|
||||
searchPushOptions: [
|
||||
{ value: false, label: "禁用", desc: "需要进入插件才能执行命令" },
|
||||
{
|
||||
value: true,
|
||||
label: "启用",
|
||||
desc: "可以在uTools主搜索框直接执行命令",
|
||||
},
|
||||
],
|
||||
commandTypes: commandTypes,
|
||||
platformTypes: platformTypes,
|
||||
currentMatchType: "关键字",
|
||||
cmdType: {},
|
||||
cmdMatch: "",
|
||||
outputTypes: outputTypes,
|
||||
outputTypesOptions: Object.keys(outputTypes),
|
||||
specialVar: "{{}}",
|
||||
allQuickCommandTags: this.$parent.allQuickCommandTags,
|
||||
showIconPicker: false,
|
||||
showUserData: false,
|
||||
sideBarPadding: 20,
|
||||
isFileTypeDirectory: false,
|
||||
};
|
||||
},
|
||||
props: {
|
||||
quickcommandInfo: Object,
|
||||
canCommandSave: Boolean,
|
||||
sideBarWidth: Number,
|
||||
},
|
||||
computed: {
|
||||
commandTypesOptions() {
|
||||
return this.currentCommand.features.mainPush
|
||||
? Object.values(commandTypes).filter((x) =>
|
||||
["regex", "over", "key"].includes(x.name)
|
||||
)
|
||||
: Object.values(commandTypes);
|
||||
},
|
||||
specialVarsOptions() {
|
||||
if (this.currentCommand.features.mainPush) return [specialVars.input];
|
||||
let x = Object.values(specialVars).filter(
|
||||
(x) => !x.label.match(this.cmdType.disabledSpecialVars)
|
||||
);
|
||||
return x;
|
||||
},
|
||||
outputTypesOptionsDy() {
|
||||
if (this.currentCommand.features.mainPush) return ["text"];
|
||||
switch (this.$parent.quickcommandInfo.program) {
|
||||
case "quickcommand":
|
||||
return window.lodashM.without(this.outputTypesOptions, "terminal");
|
||||
case "html":
|
||||
return ["html"];
|
||||
default:
|
||||
return this.outputTypesOptions;
|
||||
}
|
||||
},
|
||||
searchPushValue: {
|
||||
get() {
|
||||
return (
|
||||
this.searchPushOptions.find(
|
||||
(opt) => opt.value === this.currentCommand.features.mainPush
|
||||
) || this.searchPushOptions[0]
|
||||
);
|
||||
},
|
||||
set(option) {
|
||||
this.currentCommand.features.mainPush = option.value;
|
||||
},
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
outputTypesOptionsDy(val) {
|
||||
if (!val.includes(this.currentCommand.output)) {
|
||||
this.currentCommand.output = val[0];
|
||||
}
|
||||
},
|
||||
commandTypesOptions(val) {
|
||||
if (!val.map((x) => x.name).includes(this.cmdType.name)) {
|
||||
this.cmdType = val[0];
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
let currentQuickCommandCmds = this.getCommandType();
|
||||
this.cmdType = this.commandTypes[currentQuickCommandCmds.type];
|
||||
this.cmdMatch = currentQuickCommandCmds.match;
|
||||
Object.assign(
|
||||
this.currentCommand,
|
||||
window.lodashM.cloneDeep(
|
||||
window.lodashM.pick(
|
||||
this.quickcommandInfo,
|
||||
"tags",
|
||||
"output",
|
||||
"features"
|
||||
)
|
||||
)
|
||||
);
|
||||
this.setIcon(this.quickcommandInfo.program);
|
||||
this.platformVerify();
|
||||
},
|
||||
setIcon(language) {
|
||||
this.currentCommand.features.icon?.slice(0, 10) === "data:image" ||
|
||||
(this.currentCommand.features.icon =
|
||||
this.$root.programs[language].icon);
|
||||
},
|
||||
getCommandType() {
|
||||
let data = { type: "key", match: [] };
|
||||
let cmds = this.quickcommandInfo.features?.cmds;
|
||||
if (!cmds) return data;
|
||||
if (cmds.length === 1) {
|
||||
let { type, match, fileType } = cmds[0];
|
||||
data.type = type ? type : "key";
|
||||
data.match =
|
||||
data.type === "key" ? cmds : match?.app ? match.app : match;
|
||||
this.isFileTypeDirectory = fileType === "directory";
|
||||
} else {
|
||||
data.type = cmds.filter((x) => !x.length).length
|
||||
? "professional"
|
||||
: "key";
|
||||
data.match = data.type === "key" ? cmds : JSON.stringify(cmds, null, 4);
|
||||
}
|
||||
return data;
|
||||
},
|
||||
handleCmdTypeChange(val) {
|
||||
this.cmdMatch =
|
||||
val.name === "professional"
|
||||
? JSON.stringify(val.jsonSample, null, 4)
|
||||
: null;
|
||||
},
|
||||
tagVerify(val, done) {
|
||||
if (
|
||||
[
|
||||
"默认",
|
||||
"未分类",
|
||||
"搜索结果",
|
||||
// "来自分享"
|
||||
].includes(val)
|
||||
) {
|
||||
return done(`_${val}_`);
|
||||
}
|
||||
done(val);
|
||||
},
|
||||
// 平台为空自动补充
|
||||
platformVerify() {
|
||||
this.currentCommand.features.platform?.length > 0 ||
|
||||
(this.currentCommand.features.platform = [window.processPlatform]);
|
||||
},
|
||||
// 正则不和规则自动加斜杠
|
||||
regexVerify() {
|
||||
if (
|
||||
this.cmdType.valueType === "regex" &&
|
||||
!/^\/.*?\/[igm]*$/.test(this.cmdMatch)
|
||||
)
|
||||
this.cmdMatch = `/${this.cmdMatch}/`;
|
||||
},
|
||||
autoAddInputVal(e, ref) {
|
||||
let inputValue = e.target.value;
|
||||
if (!inputValue) return;
|
||||
ref.add(inputValue, true);
|
||||
},
|
||||
insertSpecialVar(text) {
|
||||
if (!text) return;
|
||||
this.$parent.$refs.editor.repacleEditorSelection(`"${text}"`);
|
||||
},
|
||||
showMainPushHelp() {
|
||||
window.showUb.help("#u0e9f1430");
|
||||
},
|
||||
// 保存各类数据
|
||||
SaveMenuData() {
|
||||
let updateData = {
|
||||
features: this.currentCommand.features,
|
||||
output: this.currentCommand.output,
|
||||
tags: this.currentCommand.tags,
|
||||
cmd: "",
|
||||
};
|
||||
|
||||
// 说明为空填充一个空格
|
||||
updateData.features.explain || (updateData.features.explain = " ");
|
||||
|
||||
if (!updateData.features.code) {
|
||||
// 生成唯一code
|
||||
let uid = Number(
|
||||
Math.random().toString().substr(3, 3) + Date.now()
|
||||
).toString(36);
|
||||
updateData.features.code = `${this.cmdType.name}_${uid}`;
|
||||
}
|
||||
let verify = this.cmdType.verify(this.cmdMatch);
|
||||
if (verify !== true) {
|
||||
return quickcommand.showMessageBox(verify, "error");
|
||||
}
|
||||
|
||||
if (outputTypes[updateData.output].outPlugin) {
|
||||
updateData.features.mainHide = true;
|
||||
}
|
||||
// 匹配文件时,额外添加文件类型
|
||||
let rules = this.cmdMatch;
|
||||
if (this.cmdType.name === "files") {
|
||||
rules = {
|
||||
fileType: this.isFileTypeDirectory ? "directory" : "file",
|
||||
match: this.cmdMatch,
|
||||
};
|
||||
}
|
||||
updateData.features.cmds = this.cmdType.matchToCmds(
|
||||
rules,
|
||||
updateData.features.explain
|
||||
);
|
||||
|
||||
updateData.cmd = this.$parent.$refs.editor.getEditorValue();
|
||||
|
||||
let blackLisk = updateData.cmd.match(this.cmdType.disabledSpecialVars);
|
||||
if (blackLisk) {
|
||||
return quickcommand.showMessageBox(
|
||||
`当前模式无法使用${[...new Set(blackLisk)].join("、")}`,
|
||||
"error"
|
||||
);
|
||||
}
|
||||
|
||||
// 子输入框
|
||||
if (updateData.cmd.includes("{{subinput")) {
|
||||
updateData.hasSubInput = true;
|
||||
} else {
|
||||
updateData.hasSubInput = false;
|
||||
}
|
||||
return updateData;
|
||||
},
|
||||
},
|
||||
emits: ["back"],
|
||||
};
|
||||
</script>
|
||||
<style scoped>
|
||||
/* 其他样式从app.css中继承 */
|
||||
.featureIco {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.featureIco:hover {
|
||||
transform: scale(1.02) translateY(-2px);
|
||||
}
|
||||
|
||||
.featureIco:hover::after {
|
||||
opacity: 0.8;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.command-side-bar {
|
||||
height: 100%;
|
||||
background: #f4f4f4;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.header-section {
|
||||
height: 60px;
|
||||
min-height: 60px;
|
||||
background: inherit;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.header-content {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.back-btn {
|
||||
position: absolute;
|
||||
left: 20px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
height: 48px;
|
||||
width: 22px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.logo-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.scroll-area {
|
||||
flex: 1;
|
||||
background: inherit;
|
||||
}
|
||||
|
||||
.body--dark .command-side-bar {
|
||||
background: #303133;
|
||||
}
|
||||
|
||||
.commandLogo {
|
||||
cursor: pointer;
|
||||
transition: 0.2s;
|
||||
filter: drop-shadow(2px 1px 1px grey);
|
||||
}
|
||||
|
||||
.commandLogo:hover {
|
||||
transition: 0.5s;
|
||||
transform: translateY(-1px);
|
||||
filter: drop-shadow(2px 1px 5px grey);
|
||||
}
|
||||
|
||||
/* 输入框图标基础样式 */
|
||||
.command-side-bar-icon {
|
||||
background: var(--q-primary);
|
||||
border-radius: 8px;
|
||||
padding: 4px;
|
||||
color: #f4f4f4;
|
||||
font-size: 14px;
|
||||
/* 分开设置不同属性的过渡效果 */
|
||||
transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1),
|
||||
box-shadow 0.3s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
backface-visibility: hidden;
|
||||
transform-style: preserve-3d;
|
||||
will-change: transform;
|
||||
-webkit-font-smoothing: subpixel-antialiased;
|
||||
/* 添加初始transform状态 */
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
/* 输入框容器悬浮效果 */
|
||||
.q-field:hover .command-side-bar-icon {
|
||||
transform: scale(1.05) translateY(-1px) translateZ(0);
|
||||
background: var(--q-primary);
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
/* 输入框得焦点时的图标效果 */
|
||||
.q-field--focused .command-side-bar-icon {
|
||||
transform: scale(1.1) translateY(-1px) translateZ(0);
|
||||
background: var(--q-primary);
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.side-bar-popup-content .q-item .q-icon {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.command-side-bar-content .q-field,
|
||||
.side-bar-popup-content .q-item__label {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.side-bar-popup-content .q-item__label--caption,
|
||||
.command-side-bar-content :deep(.q-chip) {
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
@ -6,20 +6,6 @@
|
||||
:commandCode="commandCode"
|
||||
@restore="$emit('restore', $event)"
|
||||
/>
|
||||
|
||||
<!-- 全屏按钮 -->
|
||||
<q-btn
|
||||
round
|
||||
dense
|
||||
:icon="isFullscreen ? 'fullscreen_exit' : 'fullscreen'"
|
||||
@click="$emit('toggle-fullscreen')"
|
||||
class="fullscreen-btn"
|
||||
:class="{ 'btn-fullscreen': isFullscreen }"
|
||||
>
|
||||
<q-tooltip>{{
|
||||
isFullscreen ? "退出全屏 (F11)" : "全屏编辑 (F11)"
|
||||
}}</q-tooltip>
|
||||
</q-btn>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -36,12 +22,8 @@ export default {
|
||||
type: String,
|
||||
default: "temp",
|
||||
},
|
||||
isFullscreen: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: ["restore", "toggle-fullscreen"],
|
||||
emits: ["restore"],
|
||||
methods: {
|
||||
showHistory() {
|
||||
this.$refs.history.open();
|
||||
@ -58,46 +40,10 @@ export default {
|
||||
position: fixed;
|
||||
right: 24px;
|
||||
bottom: 24px;
|
||||
z-index: 1000;
|
||||
z-index: 500;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
}
|
||||
|
||||
.fullscreen-btn {
|
||||
z-index: 1000;
|
||||
transform-origin: center;
|
||||
color: #666;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
}
|
||||
|
||||
.fullscreen-btn:hover {
|
||||
transform: scale(1.1) translateY(-2px);
|
||||
}
|
||||
|
||||
.fullscreen-btn:active {
|
||||
transform: scale(0.95);
|
||||
transition-duration: 0.1s;
|
||||
}
|
||||
|
||||
.btn-fullscreen {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.btn-fullscreen:hover {
|
||||
transform: rotate(180deg) scale(1.1);
|
||||
}
|
||||
|
||||
.body--dark .fullscreen-btn {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
.body--dark .fullscreen-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
</style>
|
||||
|
@ -45,34 +45,32 @@
|
||||
:class="{ 'buttons-visible': isHovering[type.value] }"
|
||||
@mouseleave="setHovering(type.value, false)"
|
||||
>
|
||||
<q-btn-group outline>
|
||||
<q-btn
|
||||
dense
|
||||
outline
|
||||
:color="type.color"
|
||||
icon="add"
|
||||
class="hover-btn"
|
||||
@click="addRuleByType(type.value)"
|
||||
>
|
||||
<q-badge
|
||||
v-if="ruleTypeCounts[type.value]"
|
||||
floating
|
||||
:label="ruleTypeCounts[type.value]"
|
||||
/>
|
||||
</q-btn>
|
||||
<q-btn
|
||||
dense
|
||||
outline
|
||||
:color="
|
||||
!ruleTypeCounts[type.value] || modelValue.length === 1
|
||||
? 'grey'
|
||||
: type.color
|
||||
"
|
||||
icon="remove"
|
||||
class="hover-btn"
|
||||
@click="removeLastRuleByType(type.value)"
|
||||
<q-btn
|
||||
dense
|
||||
outline
|
||||
:color="type.color"
|
||||
icon="add"
|
||||
class="hover-btn"
|
||||
@click="addRuleByType(type.value)"
|
||||
>
|
||||
<q-badge
|
||||
v-if="ruleTypeCounts[type.value]"
|
||||
floating
|
||||
:label="ruleTypeCounts[type.value]"
|
||||
/>
|
||||
</q-btn-group>
|
||||
</q-btn>
|
||||
<q-btn
|
||||
dense
|
||||
outline
|
||||
:color="
|
||||
!ruleTypeCounts[type.value] || modelValue.length === 1
|
||||
? 'grey'
|
||||
: type.color
|
||||
"
|
||||
icon="remove"
|
||||
class="hover-btn"
|
||||
@click="removeLastRuleByType(type.value)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -140,7 +138,7 @@
|
||||
label="匹配文本正则表达式"
|
||||
placeholder="例:/xxx/,任意匹配的正则会被 uTools 忽略"
|
||||
class="col"
|
||||
@blur="validateRegex(rule)"
|
||||
@blur="validateRegex('match', rule)"
|
||||
/>
|
||||
<q-input
|
||||
v-model.number="rule.minLength"
|
||||
@ -173,7 +171,7 @@
|
||||
label="匹配文件(夹)名正则表达式"
|
||||
placeholder="可选,例:/xxx/"
|
||||
class="col"
|
||||
@blur="validateRegex(rule)"
|
||||
@blur="validateRegex('match', rule)"
|
||||
/>
|
||||
<q-select
|
||||
:model-value="rule.fileType || 'file'"
|
||||
@ -232,7 +230,7 @@
|
||||
label="匹配窗口标题正则表达式"
|
||||
placeholder="可选,例:/xxx/"
|
||||
class="col-5"
|
||||
@blur="validateRegex({ match: rule.match.title })"
|
||||
@blur="validateRegex('match.title', rule)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@ -256,7 +254,7 @@
|
||||
label="排除的正则表达式字符串"
|
||||
placeholder="可选,例:/xxx/"
|
||||
class="col"
|
||||
@blur="validateRegex({ match: rule.exclude })"
|
||||
@blur="validateRegex('exclude', rule)"
|
||||
/>
|
||||
<q-input
|
||||
v-model.number="rule.minLength"
|
||||
@ -350,17 +348,21 @@ export default defineComponent({
|
||||
},
|
||||
|
||||
methods: {
|
||||
validateRegex(rule) {
|
||||
const matchValue = rule.match;
|
||||
validateRegex(keyPath, rule) {
|
||||
const keys = keyPath.split(".");
|
||||
const lastKey = keys.pop();
|
||||
const target = keys.reduce((obj, key) => obj[key], rule);
|
||||
|
||||
const matchValue = target[lastKey];
|
||||
if (!matchValue) return;
|
||||
|
||||
try {
|
||||
if (!matchValue.startsWith("/")) {
|
||||
rule.match = `/${matchValue}/`;
|
||||
target[lastKey] = `/${matchValue}/`;
|
||||
}
|
||||
new RegExp(matchValue.replace(/^\/|\/[gimuy]*$/g, ""));
|
||||
} catch (e) {
|
||||
rule.match = "/./";
|
||||
target[lastKey] = "/./";
|
||||
}
|
||||
},
|
||||
|
||||
@ -450,7 +452,6 @@ export default defineComponent({
|
||||
.rule-type-buttons {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
/* 合并按钮基础样式 */
|
||||
@ -490,7 +491,7 @@ export default defineComponent({
|
||||
.key-input-wrapper :deep(.q-icon) {
|
||||
font-size: 16px;
|
||||
opacity: 0.7;
|
||||
transition: opacity 0.3s;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
.key-input-wrapper :deep(.q-icon:hover) {
|
||||
@ -518,8 +519,8 @@ export default defineComponent({
|
||||
.rule-type-btn-wrapper,
|
||||
.btn-container {
|
||||
position: relative;
|
||||
width: 85px;
|
||||
height: 24px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.btn-container {
|
||||
@ -531,7 +532,7 @@ export default defineComponent({
|
||||
.rule-type-btn {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
transition: opacity 0.3s ease;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.btn-hidden {
|
||||
@ -544,9 +545,9 @@ export default defineComponent({
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
justify-content: center;
|
||||
opacity: 0;
|
||||
transition: all 0.5s ease;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
@ -1,12 +1,17 @@
|
||||
<template>
|
||||
<div class="monaco-container">
|
||||
<div id="monacoEditor" class="monaco-editor-instance"></div>
|
||||
<div class="absolute-center flex" v-show="!value && placeholder">
|
||||
<div class="placeholder text-center q-gutter-md">
|
||||
<div v-for="shortCut in shortCuts" :key="shortCut">
|
||||
<span>{{ shortCut[0] }}</span
|
||||
><span class="shortcut-key">{{ shortCut[1] }}</span
|
||||
><span class="shortcut-key">{{ shortCut[2] }}</span>
|
||||
<div class="placeholder-container" v-show="showPlaceholder">
|
||||
{{ placeholder }}
|
||||
</div>
|
||||
<div class="shortcut-container" v-show="showPlaceholder">
|
||||
<div class="shortcut text-center row q-gutter-md items-center">
|
||||
<div
|
||||
v-for="shortCut in shortCuts"
|
||||
:key="shortCut"
|
||||
class="row q-gutter-xs"
|
||||
>
|
||||
<q-badge v-for="item in shortCut" :key="item">{{ item }}</q-badge>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -31,7 +36,6 @@ let languageCompletions = importAll(
|
||||
let monacoCompletionProviders = {};
|
||||
|
||||
let cmdCtrlKey = utools.isMacOs() ? "⌘" : "Ctrl";
|
||||
let optAltKey = utools.isMacOs() ? "⌥" : "Alt";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
@ -42,10 +46,14 @@ export default {
|
||||
shortCuts: [
|
||||
["保存", cmdCtrlKey, "S"],
|
||||
["运行", cmdCtrlKey, "B"],
|
||||
["换行", optAltKey, "Z"],
|
||||
],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
showPlaceholder() {
|
||||
return !this.value && !!this.placeholder;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.initEditor();
|
||||
// 手动监听窗口大小变化,解决Monaco自动调整大小时导致ResizeObserver loop limit exceeded错误
|
||||
@ -53,7 +61,7 @@ export default {
|
||||
this.$emit("loaded");
|
||||
},
|
||||
props: {
|
||||
placeholder: Boolean,
|
||||
placeholder: String,
|
||||
},
|
||||
methods: {
|
||||
initEditor() {
|
||||
@ -256,14 +264,7 @@ export default {
|
||||
});
|
||||
},
|
||||
bindKeys() {
|
||||
let that = this;
|
||||
// ctrl + b 运行
|
||||
this.rawEditor().addCommand(
|
||||
monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyB,
|
||||
() => {
|
||||
that.$emit("keyStroke", "run");
|
||||
}
|
||||
);
|
||||
const that = this;
|
||||
// alt + z 换行
|
||||
this.rawEditor().addCommand(
|
||||
monaco.KeyMod.Alt | monaco.KeyCode.KeyZ,
|
||||
@ -272,24 +273,6 @@ export default {
|
||||
that.rawEditor().updateOptions({ wordWrap: that.wordWrap });
|
||||
}
|
||||
);
|
||||
// ctrl + s 保存
|
||||
this.rawEditor().addCommand(
|
||||
monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS,
|
||||
() => {
|
||||
that.$emit("keyStroke", "save");
|
||||
}
|
||||
);
|
||||
// ctrl + e 快速 console.log
|
||||
this.rawEditor().addCommand(
|
||||
monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyE,
|
||||
() => {
|
||||
that.$emit("keyStroke", "log", that.getSelectionOrLineContent());
|
||||
}
|
||||
);
|
||||
// F11 全屏
|
||||
this.rawEditor().addCommand(monaco.KeyCode.F11, () => {
|
||||
this.$emit("keyStroke", "fullscreen");
|
||||
});
|
||||
},
|
||||
getSelectionOrLineContent() {
|
||||
let selection = this.rawEditor().getSelection();
|
||||
@ -318,11 +301,7 @@ export default {
|
||||
|
||||
<style scoped>
|
||||
.monaco-container {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.monaco-editor-instance {
|
||||
@ -330,22 +309,32 @@ export default {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
font-size: 14px;
|
||||
font-family: sans-serif;
|
||||
color: #535353;
|
||||
.placeholder-container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 40px;
|
||||
font-style: italic;
|
||||
color: grey;
|
||||
}
|
||||
|
||||
.shortcut-container {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 10px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.shortcut .q-badge {
|
||||
user-select: none;
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
color: rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
|
||||
.shortcut-key {
|
||||
background-color: #f3f4f6;
|
||||
border-radius: 0.25rem;
|
||||
margin-left: 0.5rem;
|
||||
padding-left: 0.25rem;
|
||||
padding-right: 0.25rem;
|
||||
}
|
||||
|
||||
.body--dark .shortcut-key {
|
||||
background-color: #262626;
|
||||
.body--dark .shortcut .q-badge {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
</style>
|
||||
|
@ -59,7 +59,7 @@ export const getValidCommand = (command) => {
|
||||
|
||||
// 生成唯一code
|
||||
if (!command.features.code) {
|
||||
command.features.code = getFeatureCode(cmds);
|
||||
command.features.code = getFeatureCode(command.features.cmds);
|
||||
}
|
||||
|
||||
return window.lodashM.cloneDeep(command);
|
||||
|
@ -5,7 +5,6 @@
|
||||
const programs = {
|
||||
quickcommand: {
|
||||
name: "quickcommand",
|
||||
highlight: "javascript",
|
||||
bin: "",
|
||||
argv: "",
|
||||
ext: "",
|
||||
@ -40,7 +39,6 @@ const programs = {
|
||||
},
|
||||
cmd: {
|
||||
name: "cmd",
|
||||
highlight: "bat",
|
||||
bin: "",
|
||||
argv: "",
|
||||
ext: "bat",
|
||||
|
@ -3,153 +3,155 @@
|
||||
*/
|
||||
|
||||
let handlingJsonVar = (jsonVar, name, payload) => {
|
||||
try {
|
||||
return window.evalCodeInSandbox(jsonVar.slice(2, -2), {
|
||||
[name]: payload
|
||||
})
|
||||
} catch (e) {
|
||||
return utools.showNotification(e)
|
||||
}
|
||||
}
|
||||
try {
|
||||
return window.evalCodeInSandbox(jsonVar.slice(2, -2), {
|
||||
[name]: payload,
|
||||
});
|
||||
} catch (e) {
|
||||
return utools.showNotification(e);
|
||||
}
|
||||
};
|
||||
|
||||
let handlingJsExpression = js => {
|
||||
try {
|
||||
return window.evalCodeInSandbox(js.slice(5, -2), {
|
||||
utools: window.getuToolsLite(),
|
||||
})
|
||||
} catch (e) {
|
||||
return utools.showNotification(e)
|
||||
}
|
||||
}
|
||||
let handlingJsExpression = (js) => {
|
||||
try {
|
||||
return window.evalCodeInSandbox(js.slice(5, -2), {
|
||||
utools: window.getuToolsLite(),
|
||||
});
|
||||
} catch (e) {
|
||||
return utools.showNotification(e);
|
||||
}
|
||||
};
|
||||
|
||||
const specialVars = {
|
||||
isWin: {
|
||||
name: "isWin",
|
||||
label: "{{isWin}}",
|
||||
desc: "是否为 windows 系统,返回 0 或 1",
|
||||
disabledType: [],
|
||||
match: /{{isWin}}/mg,
|
||||
repl: () => utools.isWindows() ? 1 : 0
|
||||
isWin: {
|
||||
name: "isWin",
|
||||
label: "{{isWin}}",
|
||||
desc: "是否为 windows 系统,返回 0 或 1",
|
||||
match: /{{isWin}}/gm,
|
||||
repl: () => (utools.isWindows() ? 1 : 0),
|
||||
},
|
||||
LocalId: {
|
||||
name: "LocalId",
|
||||
label: "{{LocalId}}",
|
||||
desc: "本机唯一ID",
|
||||
match: /{{LocalId}}/gm,
|
||||
repl: () => utools.getNativeId(),
|
||||
},
|
||||
BrowserUrl: {
|
||||
name: "BrowserUrl",
|
||||
label: "{{BrowserUrl}}",
|
||||
desc: "浏览器当前链接",
|
||||
match: /{{BrowserUrl}}/gm,
|
||||
repl: () => utools.getCurrentBrowserUrl(),
|
||||
},
|
||||
ClipText: {
|
||||
name: "ClipText",
|
||||
label: "{{ClipText}}",
|
||||
desc: "剪贴板内容",
|
||||
match: /{{ClipText}}/gm,
|
||||
repl: () => window.clipboardReadText(),
|
||||
},
|
||||
subinput: {
|
||||
name: "subinput",
|
||||
label: "{{subinput:请输入}}",
|
||||
desc: "子输入框的文本,冒号后为占位符",
|
||||
match: /{{subinput(:.+?){0,1}}}/gm,
|
||||
},
|
||||
input: {
|
||||
name: "input",
|
||||
label: "{{input}}",
|
||||
desc: "主输入框的文本",
|
||||
match: /{{input}}/gm,
|
||||
onlyCmdTypes: ["regex", "over"],
|
||||
repl: (text, enterData) => enterData.payload,
|
||||
},
|
||||
pwd: {
|
||||
name: "pwd",
|
||||
label: "{{pwd}}",
|
||||
desc: "文件管理器当前目录",
|
||||
match: /{{pwd}}/gm,
|
||||
repl: () => window.getCurrentFolderPathFix(),
|
||||
},
|
||||
WindowInfo: {
|
||||
name: "WindowInfo",
|
||||
label: "{{WindowInfo}}",
|
||||
desc: "当前窗口信息,JSON格式,可以指定键值,如{{WindowInfo.id}}",
|
||||
type: "json",
|
||||
match: /{{WindowInfo(.*?)}}/gm,
|
||||
onlyCmdTypes: ["window"],
|
||||
repl: (jsonVar, enterData) =>
|
||||
handlingJsonVar(jsonVar, "WindowInfo", enterData.payload),
|
||||
},
|
||||
MatchImage: {
|
||||
name: "MatchImage",
|
||||
label: "{{MatchImage}}",
|
||||
desc: "匹配到图片的 DataUrl",
|
||||
match: /{{MatchImage}}/gm,
|
||||
onlyCmdTypes: ["img"],
|
||||
repl: (text, enterData) => enterData.payload,
|
||||
},
|
||||
SelectFile: {
|
||||
name: "SelectFile",
|
||||
label: "{{SelectFile}}",
|
||||
desc: "文件管理器选中的文件,不支持Linux",
|
||||
match: /{{SelectFile}}/gm,
|
||||
repl: (text, enterData) => window.getSelectFile(enterData.payload.id),
|
||||
},
|
||||
MatchedFiles: {
|
||||
name: "MatchedFiles",
|
||||
label: "{{MatchedFiles}}",
|
||||
desc: "匹配的文件,JSON格式,可以指定键值,如{{MatchedFiles[0].path}}",
|
||||
type: "json",
|
||||
match: /{{MatchedFiles(.*?)}}/gm,
|
||||
onlyCmdTypes: ["files"],
|
||||
repl: (jsonVar, enterData) =>
|
||||
handlingJsonVar(jsonVar, "MatchedFiles", enterData.payload),
|
||||
},
|
||||
type: {
|
||||
name: "type",
|
||||
label: "{{type}}",
|
||||
desc: "onPluginEnter的type,匹配的类型",
|
||||
match: /{{type}}/gm,
|
||||
repl: (text, enterData) => enterData.type,
|
||||
},
|
||||
payload: {
|
||||
name: "payload",
|
||||
label: "{{payload}}",
|
||||
desc: "onPluginEnter的payload,当为JSON时可以指定键值,如{{payload.id}}",
|
||||
type: "json",
|
||||
match: /{{payload(.*?)}}/gm,
|
||||
repl: (jsonVar, enterData) =>
|
||||
handlingJsonVar(jsonVar, "payload", enterData.payload),
|
||||
},
|
||||
js: {
|
||||
name: "js",
|
||||
label: "{{js:}}",
|
||||
desc: "获取js表达式的值,如{{js:utools.isMacOs()}}",
|
||||
tooltip: "注意,必须为表达式而非语句,类似Vue的文本插值",
|
||||
type: "command",
|
||||
match: /{{js:(.*?)}}/gm,
|
||||
repl: (js) => handlingJsExpression(js),
|
||||
},
|
||||
python: {
|
||||
name: "python",
|
||||
label: "{{py:}}",
|
||||
desc: "模拟python -c,并获取返回值,如{{py:print(1)}}",
|
||||
tooltip: "只支持单行语句",
|
||||
type: "command",
|
||||
match: /{{py:(.*?)}}/gm,
|
||||
repl: (py) => window.runPythonCommand(py.slice(5, -2)),
|
||||
},
|
||||
userData: {
|
||||
name: "userData",
|
||||
label: "{{usr:}}",
|
||||
desc: "用户设置的变量,类似一个全局配置项",
|
||||
match: /{{usr:(.*?)}}/gm,
|
||||
repl: (text, userData) => {
|
||||
let filterd = userData.filter((x) => x.id === text.slice(6, -2));
|
||||
return filterd.length ? filterd[0].value : "";
|
||||
},
|
||||
LocalId: {
|
||||
name: "LocalId",
|
||||
label: "{{LocalId}}",
|
||||
desc: "本机唯一ID",
|
||||
disabledType: [],
|
||||
match: /{{LocalId}}/mg,
|
||||
repl: () => utools.getNativeId()
|
||||
},
|
||||
BrowserUrl: {
|
||||
name: "BrowserUrl",
|
||||
label: "{{BrowserUrl}}",
|
||||
disabledType: [],
|
||||
desc: "浏览器当前链接",
|
||||
match: /{{BrowserUrl}}/mg,
|
||||
repl: () => utools.getCurrentBrowserUrl()
|
||||
},
|
||||
ClipText: {
|
||||
name: "ClipText",
|
||||
label: "{{ClipText}}",
|
||||
disabledType: [],
|
||||
desc: "剪贴板内容",
|
||||
match: /{{ClipText}}/mg,
|
||||
repl: () => window.clipboardReadText()
|
||||
},
|
||||
subinput: {
|
||||
name: "subinput",
|
||||
label: "{{subinput:请输入}}",
|
||||
disabledType: [],
|
||||
desc: "子输入框的文本,冒号后为占位符",
|
||||
match: /{{subinput(:.+?){0,1}}}/mg,
|
||||
},
|
||||
input: {
|
||||
name: "input",
|
||||
label: "{{input}}",
|
||||
desc: "主输入框的文本",
|
||||
match: /{{input}}/mg,
|
||||
repl: (text, enterData) => enterData.payload
|
||||
},
|
||||
pwd: {
|
||||
name: "pwd",
|
||||
label: "{{pwd}}",
|
||||
desc: "文件管理器当前目录",
|
||||
match: /{{pwd}}/mg,
|
||||
repl: () => window.getCurrentFolderPathFix()
|
||||
},
|
||||
WindowInfo: {
|
||||
name: "WindowInfo",
|
||||
label: "{{WindowInfo}}",
|
||||
desc: "当前窗口信息,JSON格式,可以指定键值,如{{WindowInfo.id}}",
|
||||
type: "json",
|
||||
match: /{{WindowInfo(.*?)}}/mg,
|
||||
repl: (jsonVar, enterData) => handlingJsonVar(jsonVar, "WindowInfo", enterData.payload)
|
||||
},
|
||||
MatchImage: {
|
||||
name: "MatchImage",
|
||||
label: "{{MatchImage}}",
|
||||
desc: "匹配到图片的 DataUrl",
|
||||
match: /{{MatchImage}}/mg,
|
||||
repl: (text, enterData) => enterData.payload
|
||||
},
|
||||
SelectFile: {
|
||||
name: "SelectFile",
|
||||
label: "{{SelectFile}}",
|
||||
desc: "文件管理器选中的文件,不支持Linux",
|
||||
match: /{{SelectFile}}/mg,
|
||||
repl: (text, enterData) => window.getSelectFile(enterData.payload.id)
|
||||
},
|
||||
MatchedFiles: {
|
||||
name: "MatchedFiles",
|
||||
label: "{{MatchedFiles}}",
|
||||
desc: "匹配的文件,JSON格式,可以指定键值,如{{MatchedFiles[0].path}}",
|
||||
type: "json",
|
||||
match: /{{MatchedFiles(.*?)}}/mg,
|
||||
repl: (jsonVar, enterData) => handlingJsonVar(jsonVar, "MatchedFiles", enterData.payload)
|
||||
},
|
||||
type: {
|
||||
name: "type",
|
||||
label: "{{type}}",
|
||||
desc: "onPluginEnter的type,匹配的类型",
|
||||
match: /{{type}}/mg,
|
||||
repl: (text, enterData) => enterData.type
|
||||
},
|
||||
payload: {
|
||||
name: "payload",
|
||||
label: "{{payload}}",
|
||||
desc: "onPluginEnter的payload,当为JSON时可以指定键值,如{{payload.id}}",
|
||||
type: "json",
|
||||
match: /{{payload(.*?)}}/mg,
|
||||
repl: (jsonVar, enterData) => handlingJsonVar(jsonVar, "payload", enterData.payload)
|
||||
},
|
||||
js: {
|
||||
name: "js",
|
||||
label: "{{js:}}",
|
||||
desc: "获取js表达式的值,如{{js:utools.isMacOs()}}",
|
||||
tooltip: "注意,必须为表达式而非语句,类似Vue的文本插值",
|
||||
type: "command",
|
||||
match: /{{js:(.*?)}}/mg,
|
||||
repl: js => handlingJsExpression(js)
|
||||
},
|
||||
python: {
|
||||
name: "python",
|
||||
label: "{{py:}}",
|
||||
desc: "模拟python -c,并获取返回值,如{{py:print(1)}}",
|
||||
tooltip: "只支持单行语句",
|
||||
type: "command",
|
||||
match: /{{py:(.*?)}}/mg,
|
||||
repl: py => window.runPythonCommand(py.slice(5, -2))
|
||||
},
|
||||
userData: {
|
||||
name: "userData",
|
||||
label: "{{usr:}}",
|
||||
desc: "用户设置的变量,类似一个全局配置项",
|
||||
match: /{{usr:(.*?)}}/mg,
|
||||
repl: (text, userData) => {
|
||||
let filterd = userData.filter(x => x.id === text.slice(6, -2))
|
||||
return filterd.length ? filterd[0].value : ''
|
||||
},
|
||||
tooltip: "仅本机可用时,该变量值只在本机有效,否则,所有电脑有效",
|
||||
}
|
||||
}
|
||||
tooltip: "仅本机可用时,该变量值只在本机有效,否则,所有电脑有效",
|
||||
},
|
||||
};
|
||||
|
||||
export default specialVars
|
||||
export default specialVars;
|
||||
|
@ -24,7 +24,7 @@
|
||||
<component
|
||||
:is="commandEditorAction.component"
|
||||
:action="commandEditorAction"
|
||||
@editorEvent="editorEvent"
|
||||
@editorEvent="handleEditorEvent"
|
||||
/>
|
||||
</div>
|
||||
</transition>
|
||||
@ -40,7 +40,7 @@
|
||||
import { defineAsyncComponent } from "vue";
|
||||
import { useCommandManager } from "js/commandManager.js";
|
||||
import changeLog from "js/options/changeLog.js";
|
||||
import { utoolsFull, dbManager } from "js/utools.js";
|
||||
import { utoolsFull } from "js/utools.js";
|
||||
import CommandEditor from "components/CommandEditor";
|
||||
import ComposerEditor from "components/ComposerEditor";
|
||||
import FooterBar from "src/components/config/FooterBar.vue";
|
||||
@ -214,13 +214,14 @@ export default {
|
||||
},
|
||||
saveCommand(command) {
|
||||
const code = this.commandManager.saveCommand(command);
|
||||
if (!code) return;
|
||||
this.locateToCommand(command.tags, code);
|
||||
quickcommand.showMessageBox("保存成功!");
|
||||
},
|
||||
editorEvent(event) {
|
||||
switch (event.type) {
|
||||
handleEditorEvent(event, data) {
|
||||
switch (event) {
|
||||
case "save":
|
||||
this.saveCommand(event.data);
|
||||
this.saveCommand(data);
|
||||
break;
|
||||
case "back":
|
||||
this.isEditorShow = false;
|
||||
@ -265,20 +266,12 @@ export default {
|
||||
}
|
||||
|
||||
.editor-container {
|
||||
color: var(--utools-bright-font-color);
|
||||
color: var(--utools-font-color);
|
||||
overflow: hidden;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
inset: 0;
|
||||
z-index: 5000;
|
||||
background: var(--utools-bright-bg);
|
||||
}
|
||||
|
||||
.body--dark .editor-container {
|
||||
color: var(--utools-dark-font-color);
|
||||
background: var(--utools-dark-bg);
|
||||
background: var(--utools-bg-color);
|
||||
}
|
||||
|
||||
/* 编辑器容器动画 */
|
||||
|
@ -1,7 +1,5 @@
|
||||
<template>
|
||||
<div>
|
||||
<CommandEditor ref="commandEditor" :action="action"></CommandEditor>
|
||||
</div>
|
||||
<CommandEditor ref="commandEditor" :action="action" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -1,26 +1,12 @@
|
||||
<template>
|
||||
<div>
|
||||
<MonacoEditor
|
||||
:placeholder="false"
|
||||
class="absolute-top"
|
||||
ref="editor"
|
||||
@typing="
|
||||
(val) => {
|
||||
if (cmd === val) return;
|
||||
cmd = val;
|
||||
saveCode();
|
||||
}
|
||||
"
|
||||
:style="{
|
||||
bottom: bottomHeight + 'px',
|
||||
}"
|
||||
<div class="server-page">
|
||||
<CodeEditor
|
||||
v-model="cmd"
|
||||
@update:modelValue="saveCode"
|
||||
language="quickcommand"
|
||||
style="flex: 1"
|
||||
/>
|
||||
<div
|
||||
class="absolute-bottom flex items-center justify-between q-px-md shadow-10"
|
||||
:style="{
|
||||
height: bottomHeight + 'px',
|
||||
}"
|
||||
>
|
||||
<div class="flex items-center justify-between q-px-md shadow-10">
|
||||
<div class="q-gutter-xs flex items-center full-height content-center">
|
||||
<q-badge color="primary" dense square>POST</q-badge
|
||||
><q-badge color="primary" dense square>GET</q-badge>
|
||||
@ -73,22 +59,16 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MonacoEditor from "components/editor/MonacoEditor";
|
||||
import CodeEditor from "components/editor/CodeEditor.vue";
|
||||
import { dbManager } from "js/utools.js";
|
||||
import { ref } from "vue";
|
||||
|
||||
export default {
|
||||
components: { MonacoEditor },
|
||||
data() {
|
||||
return {
|
||||
bottomHeight: 40,
|
||||
saveCodeTimer: null,
|
||||
cmd: null,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.cmd = dbManager.getStorage("cfg_serverCode") || "";
|
||||
this.$refs.editor.setEditorValue(this.cmd);
|
||||
this.$refs.editor.setEditorLanguage("javascript");
|
||||
components: { CodeEditor },
|
||||
setup() {
|
||||
const cmd = ref(dbManager.getStorage("cfg_serverCode") || "");
|
||||
const saveCodeTimer = ref(null);
|
||||
return { cmd, saveCodeTimer };
|
||||
},
|
||||
methods: {
|
||||
enableServer() {
|
||||
@ -136,3 +116,12 @@ export default {
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.server-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
}
|
||||
</style>
|
||||
|
Loading…
x
Reference in New Issue
Block a user