mirror of
https://github.com/fofolee/uTools-quickcommand.git
synced 2025-06-09 23:16:18 +08:00
重构CommandEditor组件
This commit is contained in:
parent
9ffa941e72
commit
c6b511696a
@ -1,73 +1,50 @@
|
|||||||
<template>
|
<template>
|
||||||
<!-- 命令设置栏 -->
|
<div class="command-editor">
|
||||||
<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>
|
|
||||||
|
|
||||||
<!-- 编程语言栏 -->
|
<!-- 编程语言栏 -->
|
||||||
<CommandLanguageBar
|
<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"
|
v-model="quickcommandInfo"
|
||||||
:height="languageBarHeight"
|
|
||||||
:canCommandSave="canCommandSave"
|
:canCommandSave="canCommandSave"
|
||||||
:isRunCodePage="isRunCodePage"
|
:isRunCodePage="isRunCodePage"
|
||||||
@program-changed="programChanged"
|
@action="handleAction"
|
||||||
@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
|
<CodeEditor
|
||||||
class="editor-transition"
|
v-model="quickcommandInfo.cmd"
|
||||||
:placeholder="true"
|
:language="getLanguage()"
|
||||||
|
:cursor-position="quickcommandInfo.cursorPosition"
|
||||||
|
@update:cursor-position="quickcommandInfo.cursorPosition = $event"
|
||||||
|
placeholder="请输入代码"
|
||||||
|
class="codeEditor"
|
||||||
ref="editor"
|
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,
|
|
||||||
}"
|
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 编辑器工具按钮组 -->
|
<!-- 编辑器工具按钮组 -->
|
||||||
<EditorTools
|
<EditorTools
|
||||||
ref="editorTools"
|
ref="editorTools"
|
||||||
|
v-show="!isConfigExpanded"
|
||||||
:commandCode="quickcommandInfo?.features?.code || 'temp'"
|
:commandCode="quickcommandInfo?.features?.code || 'temp'"
|
||||||
:isFullscreen="isFullscreen"
|
|
||||||
@restore="restoreHistory"
|
@restore="restoreHistory"
|
||||||
@toggle-fullscreen="toggleFullscreen"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- 可视化编排 -->
|
<!-- 可视化编排 -->
|
||||||
<q-dialog v-model="showComposer" maximized>
|
<q-dialog v-model="showComposer" maximized>
|
||||||
<CommandComposer
|
<CommandComposer
|
||||||
ref="composer"
|
ref="composer"
|
||||||
|
v-model="composerInfo"
|
||||||
@action="handleComposerAction"
|
@action="handleComposerAction"
|
||||||
:model-value="{ flows }"
|
:disabled-control-buttons="['save']"
|
||||||
/>
|
/>
|
||||||
</q-dialog>
|
</q-dialog>
|
||||||
|
|
||||||
@ -76,8 +53,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { defineAsyncComponent } from "vue";
|
import { defineAsyncComponent, ref, computed } from "vue";
|
||||||
import CommandSideBar from "components/editor/CommandSideBar";
|
import CommandConfig from "./editor/CommandConfig.vue";
|
||||||
import CommandLanguageBar from "components/editor/CommandLanguageBar";
|
import CommandLanguageBar from "components/editor/CommandLanguageBar";
|
||||||
import EditorTools from "components/editor/EditorTools";
|
import EditorTools from "components/editor/EditorTools";
|
||||||
import CommandRunResult from "components/CommandRunResult";
|
import CommandRunResult from "components/CommandRunResult";
|
||||||
@ -86,53 +63,72 @@ import programs from "js/options/programs.js";
|
|||||||
import { dbManager } from "js/utools.js";
|
import { dbManager } from "js/utools.js";
|
||||||
|
|
||||||
// 预加载 MonacoEditor
|
// 预加载 MonacoEditor
|
||||||
const MonacoEditorPromise = import("components/editor/MonacoEditor");
|
const CodeEditorPromise = import("components/editor/CodeEditor.vue");
|
||||||
// 在空闲时预加载
|
// 在空闲时预加载
|
||||||
if (window.requestIdleCallback) {
|
if (window.requestIdleCallback) {
|
||||||
window.requestIdleCallback(() => {
|
window.requestIdleCallback(() => {
|
||||||
MonacoEditorPromise;
|
CodeEditorPromise;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
MonacoEditorPromise;
|
CodeEditorPromise;
|
||||||
}, 0);
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Performance Scripting > 500ms
|
// Performance Scripting > 500ms
|
||||||
const MonacoEditor = defineAsyncComponent({
|
const CodeEditor = defineAsyncComponent({
|
||||||
loader: () => MonacoEditorPromise,
|
loader: () => CodeEditorPromise,
|
||||||
timeout: 3000,
|
timeout: 3000,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// TODO: 对称加密声明,保存命令不需要设置
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
MonacoEditor,
|
CodeEditor,
|
||||||
CommandSideBar,
|
CommandConfig,
|
||||||
CommandRunResult,
|
CommandRunResult,
|
||||||
CommandLanguageBar,
|
CommandLanguageBar,
|
||||||
CommandComposer,
|
CommandComposer,
|
||||||
EditorTools,
|
EditorTools,
|
||||||
},
|
},
|
||||||
|
emits: ["editorEvent"],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
programLanguages: Object.keys(programs),
|
programLanguages: Object.keys(programs),
|
||||||
sideBarWidth: 200,
|
|
||||||
languageBarHeight: 40,
|
|
||||||
showComposer: false,
|
showComposer: false,
|
||||||
isRunCodePage: this.action.type === "run",
|
listener: null,
|
||||||
canCommandSave: this.action.type !== "run",
|
isConfigExpanded: false,
|
||||||
showSidebar: this.action.type !== "run",
|
composerInfo: {
|
||||||
flows: [
|
program: "quickcomposer",
|
||||||
{
|
|
||||||
id: "main",
|
|
||||||
name: "main",
|
|
||||||
label: "主流程",
|
|
||||||
commands: [],
|
|
||||||
customVariables: [],
|
|
||||||
},
|
},
|
||||||
],
|
};
|
||||||
quickcommandInfo: {
|
},
|
||||||
|
props: {
|
||||||
|
action: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
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",
|
program: "quickcommand",
|
||||||
|
features: {
|
||||||
|
icon: programs.quickcommand.icon,
|
||||||
|
explain: "",
|
||||||
|
platform: ["win32", "linux", "darwin"],
|
||||||
|
mainPush: false,
|
||||||
|
cmds: [],
|
||||||
|
},
|
||||||
|
output: "text",
|
||||||
|
tags: [],
|
||||||
cmd: "",
|
cmd: "",
|
||||||
scptarg: "",
|
scptarg: "",
|
||||||
charset: {
|
charset: {
|
||||||
@ -144,136 +140,73 @@ export default {
|
|||||||
argv: "",
|
argv: "",
|
||||||
ext: "",
|
ext: "",
|
||||||
},
|
},
|
||||||
},
|
};
|
||||||
resultMaxLength: 10000,
|
const quickcommandInfo = ref({
|
||||||
listener: null,
|
...defaultCommand,
|
||||||
isFullscreen: false,
|
...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,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
props: {
|
|
||||||
action: {
|
|
||||||
type: Object,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
allQuickCommandTags: Array,
|
|
||||||
},
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.commandInit();
|
this.saveToHistory();
|
||||||
this.sidebarInit();
|
document.addEventListener("keydown", this.handleKeydown);
|
||||||
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
document.removeEventListener("keydown", this.handleKeydown);
|
||||||
},
|
},
|
||||||
methods: {
|
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) {
|
handleComposerAction(actionType, actionData) {
|
||||||
switch (actionType) {
|
switch (actionType) {
|
||||||
case "run":
|
case "run":
|
||||||
return this.runCurrentCommand(actionData);
|
// actionData 完整命令
|
||||||
case "insert":
|
this.runCurrentCommand(actionData);
|
||||||
return this.insertText(actionData);
|
break;
|
||||||
case "apply":
|
case "apply":
|
||||||
return this.replaceText(actionData);
|
// actionData 命令的cmd
|
||||||
|
console.log(actionData);
|
||||||
|
this.showComposer = false;
|
||||||
|
this.quickcommandInfo.cmd = actionData;
|
||||||
|
break;
|
||||||
case "close":
|
case "close":
|
||||||
return (this.showComposer = false);
|
this.showComposer = false;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// 保存
|
// 保存
|
||||||
saveCurrentCommand(message = "保存成功") {
|
saveCurrentCommand() {
|
||||||
let updatedData = this.$refs.sidebar?.SaveMenuData();
|
this.$emit("editorEvent", "save", this.quickcommandInfo);
|
||||||
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,
|
|
||||||
});
|
|
||||||
this.saveToHistory(); // 保存时记录历史
|
this.saveToHistory(); // 保存时记录历史
|
||||||
if (!message) return;
|
|
||||||
quickcommand.showMessageBox(message, "success", 1000, "bottom-right");
|
|
||||||
},
|
},
|
||||||
// 运行
|
// 运行
|
||||||
runCurrentCommand(cmd) {
|
runCurrentCommand(command) {
|
||||||
|
if (!command) {
|
||||||
this.saveToHistory(); // 运行时不保存但记录历史
|
this.saveToHistory(); // 运行时不保存但记录历史
|
||||||
let command = window.lodashM.cloneDeep(this.quickcommandInfo);
|
command = { ...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;
|
|
||||||
this.$refs.result.runCurrentCommand(command);
|
this.$refs.result.runCurrentCommand(command);
|
||||||
},
|
},
|
||||||
saveCodeHistory() {
|
saveCodeHistory() {
|
||||||
if (this.action.type !== "run") return;
|
if (!this.isRunCodePage) return;
|
||||||
let command = window.lodashM.cloneDeep(this.quickcommandInfo);
|
let command = window.lodashM.cloneDeep(this.quickcommandInfo);
|
||||||
command.cursorPosition = this.$refs.editor.getCursorPosition();
|
|
||||||
dbManager.putDB(command, "cfg_codeHistory");
|
dbManager.putDB(command, "cfg_codeHistory");
|
||||||
},
|
},
|
||||||
monacoTyping(val) {
|
handleAction(event, data) {
|
||||||
this.quickcommandInfo.cmd = val;
|
|
||||||
},
|
|
||||||
monacoKeyStroke(event, data) {
|
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case "run":
|
case "run":
|
||||||
this.runCurrentCommand();
|
this.runCurrentCommand();
|
||||||
@ -281,28 +214,22 @@ export default {
|
|||||||
case "save":
|
case "save":
|
||||||
this.saveCurrentCommand();
|
this.saveCurrentCommand();
|
||||||
break;
|
break;
|
||||||
case "log":
|
case "back":
|
||||||
if (this.quickcommandInfo.program !== "quickcommand") return;
|
this.$emit("editorEvent", "back");
|
||||||
this.runCurrentCommand(`console.log(${data})`);
|
|
||||||
break;
|
break;
|
||||||
case "fullscreen":
|
case "show-composer":
|
||||||
this.toggleFullscreen();
|
this.showComposer = true;
|
||||||
|
break;
|
||||||
|
case "insert-text":
|
||||||
|
this.$refs.editor.repacleEditorSelection(data);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
toggleFullscreen() {
|
|
||||||
this.isFullscreen = !this.isFullscreen;
|
|
||||||
|
|
||||||
// 重新布局编辑器
|
|
||||||
setTimeout(() => {
|
|
||||||
this.$refs.editor.resizeEditor();
|
|
||||||
}, 300);
|
|
||||||
},
|
|
||||||
saveToHistory() {
|
saveToHistory() {
|
||||||
this.$refs.editorTools.tryToSave(
|
this.$refs.editorTools.tryToSave(
|
||||||
this.$refs.editor.getEditorValue(),
|
this.quickcommandInfo.cmd,
|
||||||
this.quickcommandInfo.program
|
this.quickcommandInfo.program
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -311,27 +238,72 @@ export default {
|
|||||||
this.saveToHistory();
|
this.saveToHistory();
|
||||||
|
|
||||||
// 恢复历史内容
|
// 恢复历史内容
|
||||||
this.$refs.editor.setEditorValue(item.content);
|
this.quickcommandInfo.cmd = item.content;
|
||||||
|
this.quickcommandInfo.program = item.program;
|
||||||
},
|
},
|
||||||
handleBack() {
|
updateCommandConfig(value) {
|
||||||
// 触发返回事件
|
this.quickcommandInfo = {
|
||||||
this.$emit("editorEvent", { type: "back" });
|
...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>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
/* 统一过渡效果 */
|
.command-editor {
|
||||||
.sidebar-transition,
|
display: flex;
|
||||||
.language-bar-transition {
|
flex-direction: column;
|
||||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
height: 100%;
|
||||||
will-change: transform, left, top, opacity;
|
border-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: #fffffe;
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 编辑器动画不一致,可以产生一个回弹效果 */
|
.body--dark .command-editor {
|
||||||
.editor-transition {
|
background-color: #1e1e1e;
|
||||||
transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
|
}
|
||||||
will-change: transform, left, top, opacity;
|
|
||||||
|
.codeEditor {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
border-radius: 0 0 10px 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.command-config {
|
||||||
|
padding: 4px 10px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -78,6 +78,7 @@ import ResultMenu from "components/popup/ResultMenu.vue";
|
|||||||
import { generateFlowsCode } from "js/composer/generateCode";
|
import { generateFlowsCode } from "js/composer/generateCode";
|
||||||
import { getValidCommand } from "js/commandManager";
|
import { getValidCommand } from "js/commandManager";
|
||||||
import { dbManager } from "js/utools.js";
|
import { dbManager } from "js/utools.js";
|
||||||
|
import programs from "js/options/programs.js";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { ResultArea, ResultMenu },
|
components: { ResultArea, ResultMenu },
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
ref="composer"
|
ref="composer"
|
||||||
@action="handleComposerAction"
|
@action="handleComposerAction"
|
||||||
v-model="quickcommandInfo"
|
v-model="quickcommandInfo"
|
||||||
:show-close-button="!isRunComposePage"
|
:disabled-control-buttons="disabledControlButtons"
|
||||||
class="fixed-full"
|
class="fixed-full"
|
||||||
/>
|
/>
|
||||||
<!-- 运行结果 -->
|
<!-- 运行结果 -->
|
||||||
@ -15,7 +15,7 @@ import CommandComposer from "components/composer/CommandComposer.vue";
|
|||||||
import CommandRunResult from "components/CommandRunResult";
|
import CommandRunResult from "components/CommandRunResult";
|
||||||
import { findCommandByValue } from "js/composer/composerConfig";
|
import { findCommandByValue } from "js/composer/composerConfig";
|
||||||
import programs from "js/options/programs.js";
|
import programs from "js/options/programs.js";
|
||||||
import { ref } from "vue";
|
import { ref, computed } from "vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { CommandComposer, CommandRunResult },
|
components: { CommandComposer, CommandRunResult },
|
||||||
@ -82,15 +82,7 @@ export default {
|
|||||||
mainPush: false,
|
mainPush: false,
|
||||||
cmds: [],
|
cmds: [],
|
||||||
},
|
},
|
||||||
flows: [
|
flows: [],
|
||||||
{
|
|
||||||
id: "main",
|
|
||||||
name: "main",
|
|
||||||
label: "主流程",
|
|
||||||
commands: [],
|
|
||||||
customVariables: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
output: "text",
|
output: "text",
|
||||||
tags: [],
|
tags: [],
|
||||||
};
|
};
|
||||||
@ -99,12 +91,18 @@ export default {
|
|||||||
...retoreToFullCommand(savedCommand),
|
...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 {
|
return {
|
||||||
quickcommandInfo,
|
quickcommandInfo,
|
||||||
getLitedComposerCommand,
|
getLitedComposerCommand,
|
||||||
isRunComposePage,
|
disabledControlButtons,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
emits: ["editorEvent"],
|
emits: ["editorEvent"],
|
||||||
@ -120,14 +118,13 @@ export default {
|
|||||||
case "run":
|
case "run":
|
||||||
return this.runCurrentCommand(command);
|
return this.runCurrentCommand(command);
|
||||||
case "close":
|
case "close":
|
||||||
return this.$emit("editorEvent", {
|
return this.$emit("editorEvent", "back");
|
||||||
type: "back",
|
|
||||||
});
|
|
||||||
case "save":
|
case "save":
|
||||||
return this.$emit("editorEvent", {
|
return this.$emit(
|
||||||
type: "save",
|
"editorEvent",
|
||||||
data: this.getLitedComposerCommand(command),
|
"save",
|
||||||
});
|
this.getLitedComposerCommand(command)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
runCurrentCommand(command) {
|
runCurrentCommand(command) {
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
<div class="col command-section">
|
<div class="col command-section">
|
||||||
<FlowTabs
|
<FlowTabs
|
||||||
@action="handleAction"
|
@action="handleAction"
|
||||||
:show-close-button="showCloseButton"
|
:disabled-control-buttons="disabledControlButtons"
|
||||||
:model-value="modelValue"
|
:model-value="modelValue"
|
||||||
@update:model-value="$emit('update:modelValue', $event)"
|
@update:model-value="$emit('update:modelValue', $event)"
|
||||||
/>
|
/>
|
||||||
@ -42,9 +42,9 @@ export default defineComponent({
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
showCloseButton: {
|
disabledControlButtons: {
|
||||||
type: Boolean,
|
type: Array,
|
||||||
default: true,
|
default: () => [],
|
||||||
},
|
},
|
||||||
modelValue: {
|
modelValue: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -197,7 +197,8 @@ export default defineComponent({
|
|||||||
};
|
};
|
||||||
const consoleLogVars =
|
const consoleLogVars =
|
||||||
this.getAvailableOutputVariableName(outputVariable);
|
this.getAvailableOutputVariableName(outputVariable);
|
||||||
const tempFlows = [{
|
const tempFlows = [
|
||||||
|
{
|
||||||
name: "main",
|
name: "main",
|
||||||
commands: [
|
commands: [
|
||||||
tempCommand,
|
tempCommand,
|
||||||
@ -314,6 +315,14 @@ export default defineComponent({
|
|||||||
position: relative;
|
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 {
|
.control-component {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
@ -60,7 +60,7 @@
|
|||||||
|
|
||||||
<ComposerButtons
|
<ComposerButtons
|
||||||
:is-all-collapsed="isAllCollapsed"
|
:is-all-collapsed="isAllCollapsed"
|
||||||
:show-close-button="showCloseButton"
|
:disabled-buttons="disabledControlButtons"
|
||||||
:flows="flows"
|
:flows="flows"
|
||||||
@action="handleAction"
|
@action="handleAction"
|
||||||
/>
|
/>
|
||||||
@ -86,7 +86,7 @@
|
|||||||
ref="flowRefs"
|
ref="flowRefs"
|
||||||
/>
|
/>
|
||||||
<FlowManager
|
<FlowManager
|
||||||
v-model="showVariableManager"
|
v-model="showFlowManager"
|
||||||
:flow="flow"
|
:flow="flow"
|
||||||
:variables="flow.customVariables"
|
:variables="flow.customVariables"
|
||||||
@update-flow="updateFlows(flow)"
|
@update-flow="updateFlows(flow)"
|
||||||
@ -119,9 +119,9 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
emits: ["update:modelValue", "action"],
|
emits: ["update:modelValue", "action"],
|
||||||
props: {
|
props: {
|
||||||
showCloseButton: {
|
disabledControlButtons: {
|
||||||
type: Boolean,
|
type: Array,
|
||||||
default: true,
|
default: () => [],
|
||||||
},
|
},
|
||||||
modelValue: {
|
modelValue: {
|
||||||
type: Object,
|
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({
|
const mainFlow = computed({
|
||||||
get: () => flows.value[0],
|
get: () => flows.value[0],
|
||||||
@ -235,6 +254,7 @@ export default defineComponent({
|
|||||||
flows,
|
flows,
|
||||||
mainFlow,
|
mainFlow,
|
||||||
subFlows,
|
subFlows,
|
||||||
|
commandConfig,
|
||||||
activeTab,
|
activeTab,
|
||||||
getOutputVariables,
|
getOutputVariables,
|
||||||
updateFlows,
|
updateFlows,
|
||||||
@ -243,16 +263,10 @@ export default defineComponent({
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
isAllCollapsed: false,
|
isAllCollapsed: false,
|
||||||
showVariableManager: false,
|
showFlowManager: false,
|
||||||
outputVariables: [],
|
outputVariables: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
|
||||||
commandConfig() {
|
|
||||||
const { tags, output, features } = this.modelValue;
|
|
||||||
return { tags, output, features };
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
generateFlowName(baseName = "func_") {
|
generateFlowName(baseName = "func_") {
|
||||||
return (
|
return (
|
||||||
@ -293,7 +307,7 @@ export default defineComponent({
|
|||||||
|
|
||||||
this.activeTab = id;
|
this.activeTab = id;
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.toggleVariableManager();
|
this.toggleFlowManager();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
removeFlow(flow) {
|
removeFlow(flow) {
|
||||||
@ -332,12 +346,15 @@ export default defineComponent({
|
|||||||
case "expandAll":
|
case "expandAll":
|
||||||
this.expandAll();
|
this.expandAll();
|
||||||
break;
|
break;
|
||||||
case "toggleVariableManager":
|
case "toggleFlowManager":
|
||||||
this.toggleVariableManager();
|
this.toggleFlowManager();
|
||||||
break;
|
break;
|
||||||
case "close":
|
case "close":
|
||||||
this.$emit("action", "close");
|
this.$emit("action", "close");
|
||||||
break;
|
break;
|
||||||
|
case "apply":
|
||||||
|
this.$emit("action", "apply", payload);
|
||||||
|
break;
|
||||||
case "addFlow":
|
case "addFlow":
|
||||||
// 处理新函数创建
|
// 处理新函数创建
|
||||||
const index = this.subFlows.findIndex((f) => f.name === payload.name);
|
const index = this.subFlows.findIndex((f) => f.name === payload.name);
|
||||||
@ -352,8 +369,8 @@ export default defineComponent({
|
|||||||
this.$emit("action", type, payload);
|
this.$emit("action", type, payload);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
toggleVariableManager() {
|
toggleFlowManager() {
|
||||||
this.showVariableManager = !this.showVariableManager;
|
this.showFlowManager = !this.showFlowManager;
|
||||||
this.outputVariables = this.getOutputVariables();
|
this.outputVariables = this.getOutputVariables();
|
||||||
},
|
},
|
||||||
saveFlows() {
|
saveFlows() {
|
||||||
@ -383,7 +400,7 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
editFunction(flow) {
|
editFunction(flow) {
|
||||||
this.activeTab = flow.id;
|
this.activeTab = flow.id;
|
||||||
this.toggleVariableManager();
|
this.toggleFlowManager();
|
||||||
},
|
},
|
||||||
updateCommandConfig(newVal) {
|
updateCommandConfig(newVal) {
|
||||||
const newModelValue = {
|
const newModelValue = {
|
||||||
|
@ -5,8 +5,8 @@
|
|||||||
icon="close"
|
icon="close"
|
||||||
dense
|
dense
|
||||||
flat
|
flat
|
||||||
v-if="showCloseButton"
|
|
||||||
@click="$emit('action', 'close')"
|
@click="$emit('action', 'close')"
|
||||||
|
v-if="!disabledButtons.includes('close')"
|
||||||
>
|
>
|
||||||
<q-tooltip>退出可视化编排</q-tooltip>
|
<q-tooltip>退出可视化编排</q-tooltip>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
@ -15,6 +15,7 @@
|
|||||||
dense
|
dense
|
||||||
flat
|
flat
|
||||||
@click="$emit('action', isAllCollapsed ? 'expandAll' : 'collapseAll')"
|
@click="$emit('action', isAllCollapsed ? 'expandAll' : 'collapseAll')"
|
||||||
|
v-if="!disabledButtons.includes('expand')"
|
||||||
>
|
>
|
||||||
<q-tooltip>{{ isAllCollapsed ? "展开所有" : "折叠所有" }}</q-tooltip>
|
<q-tooltip>{{ isAllCollapsed ? "展开所有" : "折叠所有" }}</q-tooltip>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
@ -22,7 +23,8 @@
|
|||||||
icon="settings"
|
icon="settings"
|
||||||
dense
|
dense
|
||||||
flat
|
flat
|
||||||
@click="$emit('action', 'toggleVariableManager')"
|
@click="$emit('action', 'toggleFlowManager')"
|
||||||
|
v-if="!disabledButtons.includes('manager')"
|
||||||
>
|
>
|
||||||
<q-tooltip>流程管理</q-tooltip>
|
<q-tooltip>流程管理</q-tooltip>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
@ -34,33 +36,40 @@
|
|||||||
v-if="isDev"
|
v-if="isDev"
|
||||||
>
|
>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
<!-- <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
|
|
||||||
dense
|
dense
|
||||||
flat
|
flat
|
||||||
v-close-popup
|
|
||||||
icon="done_all"
|
icon="done_all"
|
||||||
@click="$emit('action', 'apply')"
|
@click="handleApply"
|
||||||
v-if="showCloseButton"
|
v-if="!disabledButtons.includes('apply')"
|
||||||
>
|
>
|
||||||
<q-tooltip>清空编辑器内容并插入</q-tooltip>
|
<q-tooltip>清空编辑器内容并插入</q-tooltip>
|
||||||
</q-btn> -->
|
</q-btn>
|
||||||
<q-btn dense flat icon="save" @click="$emit('action', 'save')">
|
<q-btn
|
||||||
|
dense
|
||||||
|
flat
|
||||||
|
icon="save"
|
||||||
|
@click="$emit('action', 'save')"
|
||||||
|
v-if="!disabledButtons.includes('save')"
|
||||||
|
>
|
||||||
<q-tooltip>保存</q-tooltip>
|
<q-tooltip>保存</q-tooltip>
|
||||||
</q-btn>
|
</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-tooltip>预览代码</q-tooltip>
|
||||||
</q-btn>
|
</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-tooltip>运行</q-tooltip>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
</div>
|
</div>
|
||||||
@ -99,9 +108,9 @@ export default defineComponent({
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
showCloseButton: {
|
disabledButtons: {
|
||||||
type: Boolean,
|
type: Array,
|
||||||
default: true,
|
default: () => [],
|
||||||
},
|
},
|
||||||
flows: {
|
flows: {
|
||||||
type: Array,
|
type: Array,
|
||||||
@ -119,11 +128,15 @@ export default defineComponent({
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
watch: {
|
methods: {
|
||||||
isVisible(val) {
|
handlePreviewCode() {
|
||||||
if (val) {
|
|
||||||
this.code = generateFlowsCode(this.flows);
|
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 FunctionInput from "components/composer/common/FunctionInput.vue";
|
||||||
import { QInput, QSelect, QToggle, QCheckbox } from "quasar";
|
import { QInput, QSelect, QToggle, QCheckbox } from "quasar";
|
||||||
const CodeEditor = defineAsyncComponent(() =>
|
const CodeEditor = defineAsyncComponent(() =>
|
||||||
import("components/composer/common/CodeEditor.vue")
|
import("components/editor/CodeEditor.vue")
|
||||||
);
|
);
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
@ -127,7 +127,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { defineComponent } from "vue";
|
import { defineComponent } from "vue";
|
||||||
import { newVarInputVal } from "js/composer/varInputValManager";
|
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 VariableInput from "components/composer/common/VariableInput.vue";
|
||||||
import ArrayEditor from "components/composer/common/ArrayEditor.vue";
|
import ArrayEditor from "components/composer/common/ArrayEditor.vue";
|
||||||
import BorderLabel from "components/composer/common/BorderLabel.vue";
|
import BorderLabel from "components/composer/common/BorderLabel.vue";
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="code-editor" :style="{ height: height }">
|
<div class="code-editor" :style="{ height: height }">
|
||||||
<div ref="editorContainer" class="editor-container" />
|
<div ref="editorContainer" class="editor-container" />
|
||||||
<div class="placeholder-wrapper" v-show="!value && placeholder">
|
<div class="placeholder-wrapper" v-show="showPlaceholder">
|
||||||
<div class="placeholder">
|
<div class="placeholder">
|
||||||
{{ placeholder }}
|
{{ placeholder }}
|
||||||
</div>
|
</div>
|
||||||
@ -11,7 +11,6 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import * as monaco from "monaco-editor";
|
import * as monaco from "monaco-editor";
|
||||||
import { toRaw } from "vue";
|
|
||||||
import importAll from "js/common/importAll.js";
|
import importAll from "js/common/importAll.js";
|
||||||
import { defineComponent } from "vue";
|
import { defineComponent } from "vue";
|
||||||
|
|
||||||
@ -21,6 +20,7 @@ let languageCompletions = importAll(
|
|||||||
);
|
);
|
||||||
|
|
||||||
let monacoCompletionProviders = {};
|
let monacoCompletionProviders = {};
|
||||||
|
let editor = null;
|
||||||
|
|
||||||
// 声明文件映射
|
// 声明文件映射
|
||||||
const typeDefinitions = {
|
const typeDefinitions = {
|
||||||
@ -70,12 +70,15 @@ export default defineComponent({
|
|||||||
type: String,
|
type: String,
|
||||||
default: "请输入...",
|
default: "请输入...",
|
||||||
},
|
},
|
||||||
|
// 光标位置
|
||||||
|
cursorPosition: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
},
|
},
|
||||||
emits: ["update:modelValue", "change"],
|
},
|
||||||
|
emits: ["update:modelValue", "update:cursorPosition"],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
editor: null,
|
|
||||||
value: null,
|
|
||||||
resizeTimeout: null,
|
resizeTimeout: null,
|
||||||
defaultOptions: {
|
defaultOptions: {
|
||||||
value: "",
|
value: "",
|
||||||
@ -138,12 +141,19 @@ export default defineComponent({
|
|||||||
modelValue: {
|
modelValue: {
|
||||||
immediate: true,
|
immediate: true,
|
||||||
handler(newValue) {
|
handler(newValue) {
|
||||||
if (this.value !== newValue) {
|
if (!editor || editor.getValue() === newValue) return;
|
||||||
this.value = newValue;
|
editor.setValue(newValue || "");
|
||||||
if (this.editor && this.editor.getValue() !== newValue) {
|
},
|
||||||
this.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": {
|
"$q.dark.isActive": {
|
||||||
@ -155,11 +165,10 @@ export default defineComponent({
|
|||||||
language: {
|
language: {
|
||||||
immediate: true,
|
immediate: true,
|
||||||
handler(newValue) {
|
handler(newValue) {
|
||||||
if (this.editor) {
|
if (!editor) return;
|
||||||
const language = this.getHighlighter(newValue);
|
const language = this.getHighlighter(newValue);
|
||||||
monaco.editor.setModelLanguage(this.rawEditor().getModel(), language);
|
monaco.editor.setModelLanguage(editor.getModel(), language);
|
||||||
this.loadTypes();
|
this.loadTypes();
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -180,16 +189,17 @@ export default defineComponent({
|
|||||||
const options = {
|
const options = {
|
||||||
...this.defaultOptions,
|
...this.defaultOptions,
|
||||||
...this.options,
|
...this.options,
|
||||||
value: this.value || "",
|
value: this.modelValue || "",
|
||||||
language,
|
language,
|
||||||
theme: this.theme,
|
theme: this.theme,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.editor = monaco.editor.create(this.$refs.editorContainer, options);
|
editor = monaco.editor.create(this.$refs.editorContainer, options);
|
||||||
this.listenEditorValue();
|
this.listenEditorValue();
|
||||||
this.loadTypes();
|
this.loadTypes();
|
||||||
this.registerLanguage();
|
this.registerLanguage();
|
||||||
|
this.bindKeys();
|
||||||
|
this.setCursorPosition(this.cursorPosition);
|
||||||
// 初始化完成后立即触发一次布局更新
|
// 初始化完成后立即触发一次布局更新
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.resizeEditor();
|
this.resizeEditor();
|
||||||
@ -197,11 +207,14 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
// 监听编辑器值变化
|
// 监听编辑器值变化
|
||||||
listenEditorValue() {
|
listenEditorValue() {
|
||||||
this.rawEditor().focus();
|
editor.focus();
|
||||||
this.rawEditor().onDidChangeModelContent(() => {
|
editor.onDidChangeModelContent(() => {
|
||||||
this.value = this.getEditorValue();
|
this.$emit("update:modelValue", editor.getValue());
|
||||||
this.$emit("update:modelValue", this.value);
|
});
|
||||||
this.$emit("change", this.value);
|
|
||||||
|
// 监听光标位置变化
|
||||||
|
editor.onDidChangeCursorPosition((e) => {
|
||||||
|
this.$emit("update:cursorPosition", e.position);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// 处理窗口大小变化
|
// 处理窗口大小变化
|
||||||
@ -210,50 +223,24 @@ export default defineComponent({
|
|||||||
clearTimeout(this.resizeTimeout);
|
clearTimeout(this.resizeTimeout);
|
||||||
}
|
}
|
||||||
this.resizeTimeout = setTimeout(() => {
|
this.resizeTimeout = setTimeout(() => {
|
||||||
this.rawEditor().layout();
|
editor.layout();
|
||||||
}, 50);
|
}, 50);
|
||||||
},
|
},
|
||||||
// 销毁编辑器
|
// 销毁编辑器
|
||||||
destroyEditor() {
|
destroyEditor() {
|
||||||
if (this.editor) {
|
|
||||||
window.removeEventListener("resize", this.resizeEditor);
|
window.removeEventListener("resize", this.resizeEditor);
|
||||||
this.rawEditor().dispose();
|
if (!editor) return;
|
||||||
this.editor = null;
|
editor.dispose();
|
||||||
}
|
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();
|
|
||||||
},
|
},
|
||||||
// 聚焦编辑器
|
// 聚焦编辑器
|
||||||
focus() {
|
focus() {
|
||||||
if (this.editor) {
|
editor && editor.focus();
|
||||||
this.editor.focus();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
registerLanguage() {
|
registerLanguage() {
|
||||||
let that = this;
|
|
||||||
const identifierPattern = "([a-zA-Z_]\\w*)";
|
const identifierPattern = "([a-zA-Z_]\\w*)";
|
||||||
let getTokens = (code) => {
|
let getTokens = (code) => {
|
||||||
let identifier = new RegExp(identifierPattern, "g");
|
const identifier = new RegExp(identifierPattern, "g");
|
||||||
let tokens = [];
|
let tokens = [];
|
||||||
let array1;
|
let array1;
|
||||||
while ((array1 = identifier.exec(code)) !== null) {
|
while ((array1 = identifier.exec(code)) !== null) {
|
||||||
@ -264,7 +251,7 @@ export default defineComponent({
|
|||||||
let createDependencyProposals = (range, keyWords, editor, curWord) => {
|
let createDependencyProposals = (range, keyWords, editor, curWord) => {
|
||||||
let keys = [];
|
let keys = [];
|
||||||
// fix getValue of undefined
|
// fix getValue of undefined
|
||||||
let tokens = getTokens(toRaw(editor).getModel()?.getValue());
|
const tokens = getTokens(editor.getModel()?.getValue());
|
||||||
// 自定义变量、字符串
|
// 自定义变量、字符串
|
||||||
for (const item of tokens) {
|
for (const item of tokens) {
|
||||||
if (item != curWord.word) {
|
if (item != curWord.word) {
|
||||||
@ -312,7 +299,7 @@ export default defineComponent({
|
|||||||
suggestions: createDependencyProposals(
|
suggestions: createDependencyProposals(
|
||||||
range,
|
range,
|
||||||
languageCompletions[language].default,
|
languageCompletions[language].default,
|
||||||
toRaw(that.editor),
|
editor,
|
||||||
word
|
word
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
@ -366,11 +353,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
getHighlighter(language) {
|
getHighlighter(language) {
|
||||||
if (
|
if (["quickcommand", "javascript", "webjavascript"].includes(language)) {
|
||||||
["quickcommand", "javascript", "webjavascript"].includes(
|
|
||||||
language
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return "javascript";
|
return "javascript";
|
||||||
}
|
}
|
||||||
if (language === "cmd") {
|
if (language === "cmd") {
|
||||||
@ -378,10 +361,38 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
return language;
|
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: {
|
computed: {
|
||||||
showPlaceholder() {
|
showPlaceholder() {
|
||||||
return this.placeholder && (!this.value || this.value.trim() === "");
|
return this.placeholder && !this.modelValue;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -390,10 +401,9 @@ export default defineComponent({
|
|||||||
<style scoped>
|
<style scoped>
|
||||||
.code-editor {
|
.code-editor {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border: 1px solid rgba(0, 0, 0, 0.12);
|
|
||||||
border-radius: 4px;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.editor-container {
|
.editor-container {
|
||||||
@ -406,7 +416,7 @@ export default defineComponent({
|
|||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
padding-left: 45px;
|
padding-left: 40px;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -414,12 +424,6 @@ export default defineComponent({
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
font-style: italic;
|
opacity: 0.4;
|
||||||
opacity: 0;
|
|
||||||
transition: opacity 0.1s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.code-editor:focus-within .placeholder {
|
|
||||||
opacity: 0.3;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
@ -1,34 +1,30 @@
|
|||||||
<template>
|
<template>
|
||||||
<q-expansion-item
|
<q-expansion-item
|
||||||
v-model="isExpanded"
|
v-model="isExpanded"
|
||||||
class="command-config"
|
@update:model-value="$emit('update:is-expanded', $event)"
|
||||||
@dragover="isExpanded = false"
|
class="command-composer command-config"
|
||||||
>
|
>
|
||||||
<template v-slot:header>
|
<template v-slot:header>
|
||||||
<div class="row q-col-gutter-sm basic-config">
|
<div class="row basic-config">
|
||||||
<div class="col-auto">
|
|
||||||
<q-avatar size="36px" square class="featureIco">
|
<q-avatar size="36px" square class="featureIco">
|
||||||
<q-img
|
<q-img
|
||||||
@click.stop="showIconPicker = true"
|
@click.stop="showIconPicker = true"
|
||||||
:src="currentCommand.features.icon"
|
:src="currentCommand.features.icon"
|
||||||
/>
|
/>
|
||||||
</q-avatar>
|
</q-avatar>
|
||||||
</div>
|
|
||||||
<div class="col">
|
|
||||||
<q-input
|
<q-input
|
||||||
|
ref="explainInput"
|
||||||
:model-value="currentCommand.features.explain"
|
:model-value="currentCommand.features.explain"
|
||||||
filled
|
borderless
|
||||||
dense
|
dense
|
||||||
@update:model-value="updateCommand('features.explain', $event)"
|
@update:model-value="updateModelValue('features.explain', $event)"
|
||||||
placeholder="名称"
|
|
||||||
@click.stop
|
@click.stop
|
||||||
|
placeholder="请输入名称"
|
||||||
|
@focus="expandOnFocus && updateExpanded(true)"
|
||||||
|
class="col"
|
||||||
>
|
>
|
||||||
<template v-slot:append>
|
|
||||||
<q-icon name="drive_file_rename_outline" />
|
|
||||||
</template>
|
|
||||||
</q-input>
|
</q-input>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 展开的配置项 -->
|
<!-- 展开的配置项 -->
|
||||||
@ -59,7 +55,7 @@
|
|||||||
<MatchRuleEditor
|
<MatchRuleEditor
|
||||||
:showJson="showMatchRuleJson"
|
:showJson="showMatchRuleJson"
|
||||||
:model-value="currentCommand.features.cmds"
|
:model-value="currentCommand.features.cmds"
|
||||||
@update:model-value="updateCommand('features.cmds', $event)"
|
@update:model-value="updateModelValue('features.cmds', $event)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -71,7 +67,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<q-select
|
<q-select
|
||||||
:model-value="currentCommand.tags"
|
:model-value="currentCommand.tags"
|
||||||
@update:model-value="updateCommand('tags', $event)"
|
@update:model-value="updateModelValue('tags', $event)"
|
||||||
:options="allQuickCommandTags"
|
:options="allQuickCommandTags"
|
||||||
dense
|
dense
|
||||||
options-dense
|
options-dense
|
||||||
@ -81,6 +77,7 @@
|
|||||||
multiple
|
multiple
|
||||||
hide-dropdown-icon
|
hide-dropdown-icon
|
||||||
new-value-mode="add-unique"
|
new-value-mode="add-unique"
|
||||||
|
popup-content-class="command-tag-popup"
|
||||||
placeholder="回车添加,最多3个"
|
placeholder="回车添加,最多3个"
|
||||||
max-values="3"
|
max-values="3"
|
||||||
@new-value="tagVerify"
|
@new-value="tagVerify"
|
||||||
@ -101,7 +98,7 @@
|
|||||||
<ButtonGroup
|
<ButtonGroup
|
||||||
:model-value="currentCommand.output"
|
:model-value="currentCommand.output"
|
||||||
:options="outputTypesOptionsDy"
|
:options="outputTypesOptionsDy"
|
||||||
@update:model-value="updateCommand('output', $event)"
|
@update:model-value="updateModelValue('output', $event)"
|
||||||
height="26px"
|
height="26px"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -138,7 +135,7 @@
|
|||||||
<CheckGroup
|
<CheckGroup
|
||||||
:model-value="currentCommand.features.platform"
|
:model-value="currentCommand.features.platform"
|
||||||
:options="Object.values(platformTypes)"
|
:options="Object.values(platformTypes)"
|
||||||
@update:model-value="updateCommand('features.platform', $event)"
|
@update:model-value="handlePlatformChange"
|
||||||
height="30px"
|
height="30px"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -147,7 +144,7 @@
|
|||||||
<!-- 图标选择对话框 -->
|
<!-- 图标选择对话框 -->
|
||||||
<q-dialog v-model="showIconPicker" position="left">
|
<q-dialog v-model="showIconPicker" position="left">
|
||||||
<iconPicker
|
<iconPicker
|
||||||
@iconChanged="(dataUrl) => updateCommand('features.icon', dataUrl)"
|
@iconChanged="(dataUrl) => updateModelValue('features.icon', dataUrl)"
|
||||||
ref="icon"
|
ref="icon"
|
||||||
/>
|
/>
|
||||||
</q-dialog>
|
</q-dialog>
|
||||||
@ -155,7 +152,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { defineComponent, computed } from "vue";
|
import { defineComponent } from "vue";
|
||||||
import iconPicker from "components/popup/IconPicker.vue";
|
import iconPicker from "components/popup/IconPicker.vue";
|
||||||
import outputTypes from "js/options/outputTypes.js";
|
import outputTypes from "js/options/outputTypes.js";
|
||||||
import platformTypes from "js/options/platformTypes.js";
|
import platformTypes from "js/options/platformTypes.js";
|
||||||
@ -178,8 +175,12 @@ export default defineComponent({
|
|||||||
type: Object,
|
type: Object,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
expandOnFocus: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
},
|
},
|
||||||
emits: ["update:modelValue"],
|
},
|
||||||
|
emits: ["update:modelValue", "update:is-expanded"],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
commandManager: useCommandManager(),
|
commandManager: useCommandManager(),
|
||||||
@ -205,29 +206,42 @@ export default defineComponent({
|
|||||||
currentCommand() {
|
currentCommand() {
|
||||||
return this.modelValue;
|
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() {
|
outputTypesOptionsDy() {
|
||||||
const options = Object.values(this.outputTypes);
|
const options = Object.values(this.outputTypes);
|
||||||
return this.currentCommand.features.mainPush
|
if (this.currentCommand.features.mainPush) {
|
||||||
? options.map((outputType) =>
|
return this.setOutputOptionDisabled(options, "text", false);
|
||||||
outputType.name !== "text"
|
}
|
||||||
? { ...outputType, disabled: true }
|
if (this.currentCommand.program === "html") {
|
||||||
: outputType
|
return this.setOutputOptionDisabled(options, "html", false);
|
||||||
)
|
}
|
||||||
: options;
|
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: {
|
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 newCommand = { ...this.currentCommand };
|
||||||
const keys = path.split(".");
|
const keys = path.split(".");
|
||||||
const lastKey = keys.pop();
|
const lastKey = keys.pop();
|
||||||
@ -246,12 +260,6 @@ export default defineComponent({
|
|||||||
if (!inputValue) return;
|
if (!inputValue) return;
|
||||||
ref.add(inputValue, true);
|
ref.add(inputValue, true);
|
||||||
},
|
},
|
||||||
handleMainPushChange(val) {
|
|
||||||
this.updateCommand("features.mainPush", val);
|
|
||||||
if (val) {
|
|
||||||
this.updateCommand("output", "text");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
showMainPushHelp() {
|
showMainPushHelp() {
|
||||||
window.showUb.help("#u0e9f1430");
|
window.showUb.help("#u0e9f1430");
|
||||||
},
|
},
|
||||||
@ -262,6 +270,33 @@ export default defineComponent({
|
|||||||
)
|
)
|
||||||
.run();
|
.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>
|
</script>
|
||||||
@ -280,6 +315,11 @@ export default defineComponent({
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.basic-config :deep(.q-field__native),
|
||||||
|
.basic-config :deep(.q-field__control) {
|
||||||
|
height: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
.command-config :deep(.q-item) {
|
.command-config :deep(.q-item) {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
min-height: unset;
|
min-height: unset;
|
||||||
@ -314,6 +354,7 @@ export default defineComponent({
|
|||||||
.featureIco {
|
.featureIco {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: 0.2s;
|
transition: 0.2s;
|
||||||
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.featureIco:hover {
|
.featureIco:hover {
|
||||||
|
@ -1,23 +1,31 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="row" v-show="!!height">
|
<div class="command-language-bar">
|
||||||
<div class="col">
|
|
||||||
<div>
|
|
||||||
<q-select
|
<q-select
|
||||||
|
class="q-pl-xs"
|
||||||
dense
|
dense
|
||||||
standout="bg-primary text-white"
|
options-dense
|
||||||
|
borderless
|
||||||
square
|
square
|
||||||
hide-bottom-space
|
hide-bottom-space
|
||||||
color="primary"
|
color="primary"
|
||||||
transition-show="jump-down"
|
transition-show="jump-down"
|
||||||
transition-hide="jump-up"
|
transition-hide="jump-up"
|
||||||
@update:model-value="updateProgram"
|
:model-value="currentCommand.program"
|
||||||
:model-value="modelValue.program"
|
@update:model-value="handleProgramChange"
|
||||||
:options="programLanguages"
|
:options="Object.keys(programs)"
|
||||||
label="环境"
|
|
||||||
>
|
>
|
||||||
|
<template v-slot:prepend>
|
||||||
|
<q-badge
|
||||||
|
label="环境"
|
||||||
|
color="primary"
|
||||||
|
text-color="white"
|
||||||
|
class="q-ml-sm"
|
||||||
|
style="height: 20px"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
<template v-slot:append>
|
<template v-slot:append>
|
||||||
<q-avatar size="lg" square>
|
<q-avatar size="20px" square v-if="isRunCodePage">
|
||||||
<img :src="programs[modelValue.program].icon" />
|
<img :src="programs[currentCommand.program].icon" />
|
||||||
</q-avatar>
|
</q-avatar>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:option="scope">
|
<template v-slot:option="scope">
|
||||||
@ -31,46 +39,77 @@
|
|||||||
</q-item>
|
</q-item>
|
||||||
</template>
|
</template>
|
||||||
</q-select>
|
</q-select>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<q-separator vertical />
|
|
||||||
<div class="col-auto justify-end flex">
|
|
||||||
<q-btn-group unelevated class="button-group">
|
<q-btn-group unelevated class="button-group">
|
||||||
<template v-if="modelValue.program === 'quickcommand'">
|
<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"
|
||||||
|
clickable
|
||||||
|
v-close-popup
|
||||||
|
@click="handleSpecialVarClick(item)"
|
||||||
|
>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<template v-if="currentCommand.program === 'quickcommand'">
|
||||||
<q-btn
|
<q-btn
|
||||||
v-for="(item, index) in [
|
v-for="(item, index) in ['help_center', 'view_timeline']"
|
||||||
'help_center',
|
|
||||||
'view_timeline',
|
|
||||||
]"
|
|
||||||
:key="index"
|
:key="index"
|
||||||
dense
|
dense
|
||||||
flat
|
flat
|
||||||
color="primary"
|
color="primary"
|
||||||
class="settings-btn"
|
class="settings-btn"
|
||||||
|
:label="['文档', '可视化'][index]"
|
||||||
:icon="item"
|
:icon="item"
|
||||||
@click="handleQuickCommandAction(index)"
|
@click="handleQuickCommandAction(index)"
|
||||||
>
|
>
|
||||||
<q-tooltip>
|
|
||||||
{{ ["查看文档", "可视化编排"][index] }}
|
|
||||||
</q-tooltip>
|
|
||||||
</q-btn>
|
</q-btn>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<q-btn-dropdown
|
<q-btn-dropdown
|
||||||
v-else-if="modelValue.program !== 'html'"
|
v-model="isSettingsVisible"
|
||||||
|
v-else-if="currentCommand.program !== 'html'"
|
||||||
class="settings-btn"
|
class="settings-btn"
|
||||||
dense
|
dense
|
||||||
flat
|
flat
|
||||||
ref="settings"
|
label="设置"
|
||||||
color="primary"
|
color="primary"
|
||||||
icon="settings"
|
icon="settings"
|
||||||
>
|
>
|
||||||
<q-list>
|
<q-list>
|
||||||
<!-- 自定义解释器 -->
|
<!-- 自定义解释器 -->
|
||||||
<q-item
|
<q-item
|
||||||
v-for="(item, index) in Object.keys(modelValue.customOptions)"
|
v-for="(item, index) in Object.keys(currentCommand.customOptions)"
|
||||||
:key="index"
|
:key="index"
|
||||||
v-show="modelValue.program === 'custom'"
|
v-show="currentCommand.program === 'custom'"
|
||||||
>
|
>
|
||||||
<q-input
|
<q-input
|
||||||
stack-label
|
stack-label
|
||||||
@ -78,7 +117,6 @@
|
|||||||
dense
|
dense
|
||||||
outlined
|
outlined
|
||||||
class="full-width"
|
class="full-width"
|
||||||
@blur="matchLanguage"
|
|
||||||
:label="
|
:label="
|
||||||
[
|
[
|
||||||
'解释器路径,如:/opt/python',
|
'解释器路径,如:/opt/python',
|
||||||
@ -86,8 +124,10 @@
|
|||||||
'脚本后缀,不含点,如:py',
|
'脚本后缀,不含点,如:py',
|
||||||
][index]
|
][index]
|
||||||
"
|
"
|
||||||
:model-value="modelValue.customOptions[item]"
|
:model-value="currentCommand.customOptions[item]"
|
||||||
@update:model-value="(val) => updateCustomOption(item, val)"
|
@update:model-value="
|
||||||
|
(val) => updateModelValue(`customOptions.${item}`, val)
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<template v-slot:prepend>
|
<template v-slot:prepend>
|
||||||
<q-icon name="code" />
|
<q-icon name="code" />
|
||||||
@ -95,15 +135,15 @@
|
|||||||
</q-input>
|
</q-input>
|
||||||
</q-item>
|
</q-item>
|
||||||
<!-- 脚本参数 -->
|
<!-- 脚本参数 -->
|
||||||
<q-item v-show="modelValue.program !== 'quickcommand'">
|
<q-item v-show="currentCommand.program !== 'quickcommand'">
|
||||||
<q-input
|
<q-input
|
||||||
dense
|
dense
|
||||||
stack-label
|
stack-label
|
||||||
outlined
|
outlined
|
||||||
label="脚本参数"
|
label="脚本参数"
|
||||||
class="full-width"
|
class="full-width"
|
||||||
:model-value="modelValue.scptarg"
|
:model-value="currentCommand.scptarg"
|
||||||
@update:model-value="updateScptarg"
|
@update:model-value="updateModelValue('scptarg', $event)"
|
||||||
>
|
>
|
||||||
<template v-slot:prepend>
|
<template v-slot:prepend>
|
||||||
<q-icon name="input" />
|
<q-icon name="input" />
|
||||||
@ -112,9 +152,9 @@
|
|||||||
</q-item>
|
</q-item>
|
||||||
<!-- 编码设置 -->
|
<!-- 编码设置 -->
|
||||||
<q-item
|
<q-item
|
||||||
v-for="(item, index) in Object.keys(modelValue.charset)"
|
v-for="(item, index) in Object.keys(currentCommand.charset)"
|
||||||
:key="index"
|
:key="index"
|
||||||
v-show="modelValue.program !== 'quickcommand'"
|
v-show="currentCommand.program !== 'quickcommand'"
|
||||||
>
|
>
|
||||||
<q-select
|
<q-select
|
||||||
dense
|
dense
|
||||||
@ -123,8 +163,10 @@
|
|||||||
clearable
|
clearable
|
||||||
class="full-width"
|
class="full-width"
|
||||||
:label="['脚本编码', '输出编码'][index]"
|
:label="['脚本编码', '输出编码'][index]"
|
||||||
:model-value="modelValue.charset[item]"
|
:model-value="currentCommand.charset[item]"
|
||||||
@update:model-value="(val) => updateCharset(item, val)"
|
@update:model-value="
|
||||||
|
(val) => updateModelValue(`charset.${item}`, val)
|
||||||
|
"
|
||||||
:options="['GBK', 'utf8', 'Big5']"
|
:options="['GBK', 'utf8', 'Big5']"
|
||||||
type="text"
|
type="text"
|
||||||
>
|
>
|
||||||
@ -136,6 +178,16 @@
|
|||||||
</q-list>
|
</q-list>
|
||||||
</q-btn-dropdown>
|
</q-btn-dropdown>
|
||||||
<q-separator vertical inset />
|
<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
|
<q-btn
|
||||||
class="action-btn run-btn"
|
class="action-btn run-btn"
|
||||||
dense
|
dense
|
||||||
@ -143,26 +195,37 @@
|
|||||||
color="primary"
|
color="primary"
|
||||||
icon="play_arrow"
|
icon="play_arrow"
|
||||||
label="运行"
|
label="运行"
|
||||||
@click="$emit('run')"
|
@click="$emit('action', 'run')"
|
||||||
></q-btn>
|
></q-btn>
|
||||||
<q-btn
|
<q-btn
|
||||||
class="action-btn save-btn"
|
|
||||||
flat
|
|
||||||
dense
|
|
||||||
v-if="!isRunCodePage"
|
v-if="!isRunCodePage"
|
||||||
:disable="!canCommandSave"
|
:disable="!canCommandSave"
|
||||||
:color="canCommandSave ? 'primary' : 'grey'"
|
:color="canCommandSave ? 'primary' : 'grey'"
|
||||||
|
class="action-btn save-btn"
|
||||||
|
flat
|
||||||
|
dense
|
||||||
icon="save"
|
icon="save"
|
||||||
label="保存"
|
label="保存"
|
||||||
@click="$emit('save')"
|
@click="$emit('action', 'save')"
|
||||||
></q-btn>
|
></q-btn>
|
||||||
</q-btn-group>
|
</q-btn-group>
|
||||||
</div>
|
<q-dialog v-model="showUserData">
|
||||||
|
<UserData
|
||||||
|
@insertText="
|
||||||
|
insertSpecialVar($event);
|
||||||
|
showUserData = false;
|
||||||
|
"
|
||||||
|
:showInsertBtn="true"
|
||||||
|
/>
|
||||||
|
</q-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import programs from "js/options/programs.js";
|
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 {
|
export default {
|
||||||
name: "CommandLanguageBar",
|
name: "CommandLanguageBar",
|
||||||
@ -171,10 +234,6 @@ export default {
|
|||||||
type: Object,
|
type: Object,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
height: {
|
|
||||||
type: Number,
|
|
||||||
default: 40,
|
|
||||||
},
|
|
||||||
canCommandSave: {
|
canCommandSave: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true,
|
||||||
@ -184,149 +243,121 @@ export default {
|
|||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
emits: [
|
emits: ["update:modelValue", "action"],
|
||||||
"update:modelValue",
|
components: {
|
||||||
"program-changed",
|
UserData,
|
||||||
"run",
|
},
|
||||||
"save",
|
|
||||||
"show-composer",
|
|
||||||
],
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
programs,
|
programs,
|
||||||
|
specialVars,
|
||||||
|
commandTypes,
|
||||||
|
isSettingsVisible: false,
|
||||||
|
showUserData: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
programLanguages() {
|
currentCommand() {
|
||||||
return Object.keys(this.programs);
|
return this.modelValue;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
updateProgram(value) {
|
handleProgramChange(newProgram) {
|
||||||
this.$emit("update:modelValue", {
|
const newCommand = { ...this.currentCommand };
|
||||||
...this.modelValue,
|
newCommand.program = newProgram;
|
||||||
program: value,
|
if (newProgram === "custom") {
|
||||||
});
|
this.isSettingsVisible = true;
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
},
|
if (newProgram === "html") {
|
||||||
matchLanguage() {
|
newCommand.output = "html";
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
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) {
|
handleQuickCommandAction(index) {
|
||||||
const actions = [
|
const actions = [
|
||||||
() => this.showHelp(),
|
() => this.showHelp(),
|
||||||
() => this.$emit("show-composer"),
|
() => this.$emit("action", "show-composer"),
|
||||||
];
|
];
|
||||||
actions[index]();
|
actions[index]();
|
||||||
},
|
},
|
||||||
showHelp() {
|
showHelp() {
|
||||||
window.showUb.docs();
|
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>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.button-group {
|
.button-group {
|
||||||
.action-btn {
|
padding: 0 5px;
|
||||||
padding: 0 10px;
|
}
|
||||||
}
|
|
||||||
|
.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) {
|
.run-btn:hover :deep(.q-icon) {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
animation: slideRight 1.5s infinite;
|
animation: leftRight 1.5s infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 保存按钮动画 */
|
/* 保存按钮动画 */
|
||||||
.save-btn:not([disabled]):hover :deep(.q-icon) {
|
.save-btn:not([disabled]):hover :deep(.q-icon) {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
animation: saveAnimation 1.2s infinite;
|
animation: upDown 1.2s infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 设置按钮动画 */
|
.command-language-bar {
|
||||||
.settings-btn :deep(.q-icon:first-child) {
|
background-color: #fffffe;
|
||||||
display: inline-block;
|
height: 30px;
|
||||||
transform: scale(1);
|
margin-bottom: 2px;
|
||||||
transition: transform 0.5s ease;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-btn:hover :deep(.q-icon:first-child) {
|
.body--dark .command-language-bar {
|
||||||
transform: scale(1.05);
|
background-color: #1e1e1e;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes slideRight {
|
.command-language-bar :deep(.q-field__control),
|
||||||
0% {
|
.command-language-bar :deep(.q-field__control > *),
|
||||||
transform: translateX(-2px);
|
.command-language-bar :deep(.q-field__native) {
|
||||||
opacity: 0.7;
|
max-height: 30px;
|
||||||
}
|
min-height: 30px;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
</style>
|
</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"
|
:commandCode="commandCode"
|
||||||
@restore="$emit('restore', $event)"
|
@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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -36,12 +22,8 @@ export default {
|
|||||||
type: String,
|
type: String,
|
||||||
default: "temp",
|
default: "temp",
|
||||||
},
|
},
|
||||||
isFullscreen: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
},
|
||||||
},
|
emits: ["restore"],
|
||||||
emits: ["restore", "toggle-fullscreen"],
|
|
||||||
methods: {
|
methods: {
|
||||||
showHistory() {
|
showHistory() {
|
||||||
this.$refs.history.open();
|
this.$refs.history.open();
|
||||||
@ -58,46 +40,10 @@ export default {
|
|||||||
position: fixed;
|
position: fixed;
|
||||||
right: 24px;
|
right: 24px;
|
||||||
bottom: 24px;
|
bottom: 24px;
|
||||||
z-index: 1000;
|
z-index: 500;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
transition: all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
|
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>
|
</style>
|
||||||
|
@ -45,7 +45,6 @@
|
|||||||
:class="{ 'buttons-visible': isHovering[type.value] }"
|
:class="{ 'buttons-visible': isHovering[type.value] }"
|
||||||
@mouseleave="setHovering(type.value, false)"
|
@mouseleave="setHovering(type.value, false)"
|
||||||
>
|
>
|
||||||
<q-btn-group outline>
|
|
||||||
<q-btn
|
<q-btn
|
||||||
dense
|
dense
|
||||||
outline
|
outline
|
||||||
@ -72,7 +71,6 @@
|
|||||||
class="hover-btn"
|
class="hover-btn"
|
||||||
@click="removeLastRuleByType(type.value)"
|
@click="removeLastRuleByType(type.value)"
|
||||||
/>
|
/>
|
||||||
</q-btn-group>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -140,7 +138,7 @@
|
|||||||
label="匹配文本正则表达式"
|
label="匹配文本正则表达式"
|
||||||
placeholder="例:/xxx/,任意匹配的正则会被 uTools 忽略"
|
placeholder="例:/xxx/,任意匹配的正则会被 uTools 忽略"
|
||||||
class="col"
|
class="col"
|
||||||
@blur="validateRegex(rule)"
|
@blur="validateRegex('match', rule)"
|
||||||
/>
|
/>
|
||||||
<q-input
|
<q-input
|
||||||
v-model.number="rule.minLength"
|
v-model.number="rule.minLength"
|
||||||
@ -173,7 +171,7 @@
|
|||||||
label="匹配文件(夹)名正则表达式"
|
label="匹配文件(夹)名正则表达式"
|
||||||
placeholder="可选,例:/xxx/"
|
placeholder="可选,例:/xxx/"
|
||||||
class="col"
|
class="col"
|
||||||
@blur="validateRegex(rule)"
|
@blur="validateRegex('match', rule)"
|
||||||
/>
|
/>
|
||||||
<q-select
|
<q-select
|
||||||
:model-value="rule.fileType || 'file'"
|
:model-value="rule.fileType || 'file'"
|
||||||
@ -232,7 +230,7 @@
|
|||||||
label="匹配窗口标题正则表达式"
|
label="匹配窗口标题正则表达式"
|
||||||
placeholder="可选,例:/xxx/"
|
placeholder="可选,例:/xxx/"
|
||||||
class="col-5"
|
class="col-5"
|
||||||
@blur="validateRegex({ match: rule.match.title })"
|
@blur="validateRegex('match.title', rule)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -256,7 +254,7 @@
|
|||||||
label="排除的正则表达式字符串"
|
label="排除的正则表达式字符串"
|
||||||
placeholder="可选,例:/xxx/"
|
placeholder="可选,例:/xxx/"
|
||||||
class="col"
|
class="col"
|
||||||
@blur="validateRegex({ match: rule.exclude })"
|
@blur="validateRegex('exclude', rule)"
|
||||||
/>
|
/>
|
||||||
<q-input
|
<q-input
|
||||||
v-model.number="rule.minLength"
|
v-model.number="rule.minLength"
|
||||||
@ -350,17 +348,21 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
validateRegex(rule) {
|
validateRegex(keyPath, rule) {
|
||||||
const matchValue = rule.match;
|
const keys = keyPath.split(".");
|
||||||
|
const lastKey = keys.pop();
|
||||||
|
const target = keys.reduce((obj, key) => obj[key], rule);
|
||||||
|
|
||||||
|
const matchValue = target[lastKey];
|
||||||
if (!matchValue) return;
|
if (!matchValue) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!matchValue.startsWith("/")) {
|
if (!matchValue.startsWith("/")) {
|
||||||
rule.match = `/${matchValue}/`;
|
target[lastKey] = `/${matchValue}/`;
|
||||||
}
|
}
|
||||||
new RegExp(matchValue.replace(/^\/|\/[gimuy]*$/g, ""));
|
new RegExp(matchValue.replace(/^\/|\/[gimuy]*$/g, ""));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
rule.match = "/./";
|
target[lastKey] = "/./";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -450,7 +452,6 @@ export default defineComponent({
|
|||||||
.rule-type-buttons {
|
.rule-type-buttons {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
justify-content: space-between;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 合并按钮基础样式 */
|
/* 合并按钮基础样式 */
|
||||||
@ -490,7 +491,7 @@ export default defineComponent({
|
|||||||
.key-input-wrapper :deep(.q-icon) {
|
.key-input-wrapper :deep(.q-icon) {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
transition: opacity 0.3s;
|
transition: opacity 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.key-input-wrapper :deep(.q-icon:hover) {
|
.key-input-wrapper :deep(.q-icon:hover) {
|
||||||
@ -518,8 +519,8 @@ export default defineComponent({
|
|||||||
.rule-type-btn-wrapper,
|
.rule-type-btn-wrapper,
|
||||||
.btn-container {
|
.btn-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 85px;
|
|
||||||
height: 24px;
|
height: 24px;
|
||||||
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-container {
|
.btn-container {
|
||||||
@ -531,7 +532,7 @@ export default defineComponent({
|
|||||||
.rule-type-btn {
|
.rule-type-btn {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
transition: opacity 0.3s ease;
|
transition: opacity 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-hidden {
|
.btn-hidden {
|
||||||
@ -544,9 +545,9 @@ export default defineComponent({
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: all 0.5s ease;
|
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="monaco-container">
|
<div class="monaco-container">
|
||||||
<div id="monacoEditor" class="monaco-editor-instance"></div>
|
<div id="monacoEditor" class="monaco-editor-instance"></div>
|
||||||
<div class="absolute-center flex" v-show="!value && placeholder">
|
<div class="placeholder-container" v-show="showPlaceholder">
|
||||||
<div class="placeholder text-center q-gutter-md">
|
{{ placeholder }}
|
||||||
<div v-for="shortCut in shortCuts" :key="shortCut">
|
</div>
|
||||||
<span>{{ shortCut[0] }}</span
|
<div class="shortcut-container" v-show="showPlaceholder">
|
||||||
><span class="shortcut-key">{{ shortCut[1] }}</span
|
<div class="shortcut text-center row q-gutter-md items-center">
|
||||||
><span class="shortcut-key">{{ shortCut[2] }}</span>
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -31,7 +36,6 @@ let languageCompletions = importAll(
|
|||||||
let monacoCompletionProviders = {};
|
let monacoCompletionProviders = {};
|
||||||
|
|
||||||
let cmdCtrlKey = utools.isMacOs() ? "⌘" : "Ctrl";
|
let cmdCtrlKey = utools.isMacOs() ? "⌘" : "Ctrl";
|
||||||
let optAltKey = utools.isMacOs() ? "⌥" : "Alt";
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
@ -42,10 +46,14 @@ export default {
|
|||||||
shortCuts: [
|
shortCuts: [
|
||||||
["保存", cmdCtrlKey, "S"],
|
["保存", cmdCtrlKey, "S"],
|
||||||
["运行", cmdCtrlKey, "B"],
|
["运行", cmdCtrlKey, "B"],
|
||||||
["换行", optAltKey, "Z"],
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
showPlaceholder() {
|
||||||
|
return !this.value && !!this.placeholder;
|
||||||
|
},
|
||||||
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.initEditor();
|
this.initEditor();
|
||||||
// 手动监听窗口大小变化,解决Monaco自动调整大小时导致ResizeObserver loop limit exceeded错误
|
// 手动监听窗口大小变化,解决Monaco自动调整大小时导致ResizeObserver loop limit exceeded错误
|
||||||
@ -53,7 +61,7 @@ export default {
|
|||||||
this.$emit("loaded");
|
this.$emit("loaded");
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
placeholder: Boolean,
|
placeholder: String,
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
initEditor() {
|
initEditor() {
|
||||||
@ -256,14 +264,7 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
bindKeys() {
|
bindKeys() {
|
||||||
let that = this;
|
const that = this;
|
||||||
// ctrl + b 运行
|
|
||||||
this.rawEditor().addCommand(
|
|
||||||
monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyB,
|
|
||||||
() => {
|
|
||||||
that.$emit("keyStroke", "run");
|
|
||||||
}
|
|
||||||
);
|
|
||||||
// alt + z 换行
|
// alt + z 换行
|
||||||
this.rawEditor().addCommand(
|
this.rawEditor().addCommand(
|
||||||
monaco.KeyMod.Alt | monaco.KeyCode.KeyZ,
|
monaco.KeyMod.Alt | monaco.KeyCode.KeyZ,
|
||||||
@ -272,24 +273,6 @@ export default {
|
|||||||
that.rawEditor().updateOptions({ wordWrap: that.wordWrap });
|
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() {
|
getSelectionOrLineContent() {
|
||||||
let selection = this.rawEditor().getSelection();
|
let selection = this.rawEditor().getSelection();
|
||||||
@ -318,11 +301,7 @@ export default {
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.monaco-container {
|
.monaco-container {
|
||||||
position: absolute;
|
position: relative;
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.monaco-editor-instance {
|
.monaco-editor-instance {
|
||||||
@ -330,22 +309,32 @@ export default {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.placeholder {
|
.placeholder-container {
|
||||||
font-size: 14px;
|
position: absolute;
|
||||||
font-family: sans-serif;
|
top: 0;
|
||||||
color: #535353;
|
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;
|
user-select: none;
|
||||||
|
background-color: rgba(0, 0, 0, 0.1);
|
||||||
|
color: rgba(0, 0, 0, 0.6);
|
||||||
}
|
}
|
||||||
|
|
||||||
.shortcut-key {
|
.body--dark .shortcut .q-badge {
|
||||||
background-color: #f3f4f6;
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
border-radius: 0.25rem;
|
color: rgba(255, 255, 255, 0.8);
|
||||||
margin-left: 0.5rem;
|
|
||||||
padding-left: 0.25rem;
|
|
||||||
padding-right: 0.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.body--dark .shortcut-key {
|
|
||||||
background-color: #262626;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -59,7 +59,7 @@ export const getValidCommand = (command) => {
|
|||||||
|
|
||||||
// 生成唯一code
|
// 生成唯一code
|
||||||
if (!command.features.code) {
|
if (!command.features.code) {
|
||||||
command.features.code = getFeatureCode(cmds);
|
command.features.code = getFeatureCode(command.features.cmds);
|
||||||
}
|
}
|
||||||
|
|
||||||
return window.lodashM.cloneDeep(command);
|
return window.lodashM.cloneDeep(command);
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
const programs = {
|
const programs = {
|
||||||
quickcommand: {
|
quickcommand: {
|
||||||
name: "quickcommand",
|
name: "quickcommand",
|
||||||
highlight: "javascript",
|
|
||||||
bin: "",
|
bin: "",
|
||||||
argv: "",
|
argv: "",
|
||||||
ext: "",
|
ext: "",
|
||||||
@ -40,7 +39,6 @@ const programs = {
|
|||||||
},
|
},
|
||||||
cmd: {
|
cmd: {
|
||||||
name: "cmd",
|
name: "cmd",
|
||||||
highlight: "bat",
|
|
||||||
bin: "",
|
bin: "",
|
||||||
argv: "",
|
argv: "",
|
||||||
ext: "bat",
|
ext: "bat",
|
||||||
|
@ -5,121 +5,123 @@
|
|||||||
let handlingJsonVar = (jsonVar, name, payload) => {
|
let handlingJsonVar = (jsonVar, name, payload) => {
|
||||||
try {
|
try {
|
||||||
return window.evalCodeInSandbox(jsonVar.slice(2, -2), {
|
return window.evalCodeInSandbox(jsonVar.slice(2, -2), {
|
||||||
[name]: payload
|
[name]: payload,
|
||||||
})
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return utools.showNotification(e)
|
return utools.showNotification(e);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
let handlingJsExpression = js => {
|
let handlingJsExpression = (js) => {
|
||||||
try {
|
try {
|
||||||
return window.evalCodeInSandbox(js.slice(5, -2), {
|
return window.evalCodeInSandbox(js.slice(5, -2), {
|
||||||
utools: window.getuToolsLite(),
|
utools: window.getuToolsLite(),
|
||||||
})
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return utools.showNotification(e)
|
return utools.showNotification(e);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const specialVars = {
|
const specialVars = {
|
||||||
isWin: {
|
isWin: {
|
||||||
name: "isWin",
|
name: "isWin",
|
||||||
label: "{{isWin}}",
|
label: "{{isWin}}",
|
||||||
desc: "是否为 windows 系统,返回 0 或 1",
|
desc: "是否为 windows 系统,返回 0 或 1",
|
||||||
disabledType: [],
|
match: /{{isWin}}/gm,
|
||||||
match: /{{isWin}}/mg,
|
repl: () => (utools.isWindows() ? 1 : 0),
|
||||||
repl: () => utools.isWindows() ? 1 : 0
|
|
||||||
},
|
},
|
||||||
LocalId: {
|
LocalId: {
|
||||||
name: "LocalId",
|
name: "LocalId",
|
||||||
label: "{{LocalId}}",
|
label: "{{LocalId}}",
|
||||||
desc: "本机唯一ID",
|
desc: "本机唯一ID",
|
||||||
disabledType: [],
|
match: /{{LocalId}}/gm,
|
||||||
match: /{{LocalId}}/mg,
|
repl: () => utools.getNativeId(),
|
||||||
repl: () => utools.getNativeId()
|
|
||||||
},
|
},
|
||||||
BrowserUrl: {
|
BrowserUrl: {
|
||||||
name: "BrowserUrl",
|
name: "BrowserUrl",
|
||||||
label: "{{BrowserUrl}}",
|
label: "{{BrowserUrl}}",
|
||||||
disabledType: [],
|
|
||||||
desc: "浏览器当前链接",
|
desc: "浏览器当前链接",
|
||||||
match: /{{BrowserUrl}}/mg,
|
match: /{{BrowserUrl}}/gm,
|
||||||
repl: () => utools.getCurrentBrowserUrl()
|
repl: () => utools.getCurrentBrowserUrl(),
|
||||||
},
|
},
|
||||||
ClipText: {
|
ClipText: {
|
||||||
name: "ClipText",
|
name: "ClipText",
|
||||||
label: "{{ClipText}}",
|
label: "{{ClipText}}",
|
||||||
disabledType: [],
|
|
||||||
desc: "剪贴板内容",
|
desc: "剪贴板内容",
|
||||||
match: /{{ClipText}}/mg,
|
match: /{{ClipText}}/gm,
|
||||||
repl: () => window.clipboardReadText()
|
repl: () => window.clipboardReadText(),
|
||||||
},
|
},
|
||||||
subinput: {
|
subinput: {
|
||||||
name: "subinput",
|
name: "subinput",
|
||||||
label: "{{subinput:请输入}}",
|
label: "{{subinput:请输入}}",
|
||||||
disabledType: [],
|
|
||||||
desc: "子输入框的文本,冒号后为占位符",
|
desc: "子输入框的文本,冒号后为占位符",
|
||||||
match: /{{subinput(:.+?){0,1}}}/mg,
|
match: /{{subinput(:.+?){0,1}}}/gm,
|
||||||
},
|
},
|
||||||
input: {
|
input: {
|
||||||
name: "input",
|
name: "input",
|
||||||
label: "{{input}}",
|
label: "{{input}}",
|
||||||
desc: "主输入框的文本",
|
desc: "主输入框的文本",
|
||||||
match: /{{input}}/mg,
|
match: /{{input}}/gm,
|
||||||
repl: (text, enterData) => enterData.payload
|
onlyCmdTypes: ["regex", "over"],
|
||||||
|
repl: (text, enterData) => enterData.payload,
|
||||||
},
|
},
|
||||||
pwd: {
|
pwd: {
|
||||||
name: "pwd",
|
name: "pwd",
|
||||||
label: "{{pwd}}",
|
label: "{{pwd}}",
|
||||||
desc: "文件管理器当前目录",
|
desc: "文件管理器当前目录",
|
||||||
match: /{{pwd}}/mg,
|
match: /{{pwd}}/gm,
|
||||||
repl: () => window.getCurrentFolderPathFix()
|
repl: () => window.getCurrentFolderPathFix(),
|
||||||
},
|
},
|
||||||
WindowInfo: {
|
WindowInfo: {
|
||||||
name: "WindowInfo",
|
name: "WindowInfo",
|
||||||
label: "{{WindowInfo}}",
|
label: "{{WindowInfo}}",
|
||||||
desc: "当前窗口信息,JSON格式,可以指定键值,如{{WindowInfo.id}}",
|
desc: "当前窗口信息,JSON格式,可以指定键值,如{{WindowInfo.id}}",
|
||||||
type: "json",
|
type: "json",
|
||||||
match: /{{WindowInfo(.*?)}}/mg,
|
match: /{{WindowInfo(.*?)}}/gm,
|
||||||
repl: (jsonVar, enterData) => handlingJsonVar(jsonVar, "WindowInfo", enterData.payload)
|
onlyCmdTypes: ["window"],
|
||||||
|
repl: (jsonVar, enterData) =>
|
||||||
|
handlingJsonVar(jsonVar, "WindowInfo", enterData.payload),
|
||||||
},
|
},
|
||||||
MatchImage: {
|
MatchImage: {
|
||||||
name: "MatchImage",
|
name: "MatchImage",
|
||||||
label: "{{MatchImage}}",
|
label: "{{MatchImage}}",
|
||||||
desc: "匹配到图片的 DataUrl",
|
desc: "匹配到图片的 DataUrl",
|
||||||
match: /{{MatchImage}}/mg,
|
match: /{{MatchImage}}/gm,
|
||||||
repl: (text, enterData) => enterData.payload
|
onlyCmdTypes: ["img"],
|
||||||
|
repl: (text, enterData) => enterData.payload,
|
||||||
},
|
},
|
||||||
SelectFile: {
|
SelectFile: {
|
||||||
name: "SelectFile",
|
name: "SelectFile",
|
||||||
label: "{{SelectFile}}",
|
label: "{{SelectFile}}",
|
||||||
desc: "文件管理器选中的文件,不支持Linux",
|
desc: "文件管理器选中的文件,不支持Linux",
|
||||||
match: /{{SelectFile}}/mg,
|
match: /{{SelectFile}}/gm,
|
||||||
repl: (text, enterData) => window.getSelectFile(enterData.payload.id)
|
repl: (text, enterData) => window.getSelectFile(enterData.payload.id),
|
||||||
},
|
},
|
||||||
MatchedFiles: {
|
MatchedFiles: {
|
||||||
name: "MatchedFiles",
|
name: "MatchedFiles",
|
||||||
label: "{{MatchedFiles}}",
|
label: "{{MatchedFiles}}",
|
||||||
desc: "匹配的文件,JSON格式,可以指定键值,如{{MatchedFiles[0].path}}",
|
desc: "匹配的文件,JSON格式,可以指定键值,如{{MatchedFiles[0].path}}",
|
||||||
type: "json",
|
type: "json",
|
||||||
match: /{{MatchedFiles(.*?)}}/mg,
|
match: /{{MatchedFiles(.*?)}}/gm,
|
||||||
repl: (jsonVar, enterData) => handlingJsonVar(jsonVar, "MatchedFiles", enterData.payload)
|
onlyCmdTypes: ["files"],
|
||||||
|
repl: (jsonVar, enterData) =>
|
||||||
|
handlingJsonVar(jsonVar, "MatchedFiles", enterData.payload),
|
||||||
},
|
},
|
||||||
type: {
|
type: {
|
||||||
name: "type",
|
name: "type",
|
||||||
label: "{{type}}",
|
label: "{{type}}",
|
||||||
desc: "onPluginEnter的type,匹配的类型",
|
desc: "onPluginEnter的type,匹配的类型",
|
||||||
match: /{{type}}/mg,
|
match: /{{type}}/gm,
|
||||||
repl: (text, enterData) => enterData.type
|
repl: (text, enterData) => enterData.type,
|
||||||
},
|
},
|
||||||
payload: {
|
payload: {
|
||||||
name: "payload",
|
name: "payload",
|
||||||
label: "{{payload}}",
|
label: "{{payload}}",
|
||||||
desc: "onPluginEnter的payload,当为JSON时可以指定键值,如{{payload.id}}",
|
desc: "onPluginEnter的payload,当为JSON时可以指定键值,如{{payload.id}}",
|
||||||
type: "json",
|
type: "json",
|
||||||
match: /{{payload(.*?)}}/mg,
|
match: /{{payload(.*?)}}/gm,
|
||||||
repl: (jsonVar, enterData) => handlingJsonVar(jsonVar, "payload", enterData.payload)
|
repl: (jsonVar, enterData) =>
|
||||||
|
handlingJsonVar(jsonVar, "payload", enterData.payload),
|
||||||
},
|
},
|
||||||
js: {
|
js: {
|
||||||
name: "js",
|
name: "js",
|
||||||
@ -127,8 +129,8 @@ const specialVars = {
|
|||||||
desc: "获取js表达式的值,如{{js:utools.isMacOs()}}",
|
desc: "获取js表达式的值,如{{js:utools.isMacOs()}}",
|
||||||
tooltip: "注意,必须为表达式而非语句,类似Vue的文本插值",
|
tooltip: "注意,必须为表达式而非语句,类似Vue的文本插值",
|
||||||
type: "command",
|
type: "command",
|
||||||
match: /{{js:(.*?)}}/mg,
|
match: /{{js:(.*?)}}/gm,
|
||||||
repl: js => handlingJsExpression(js)
|
repl: (js) => handlingJsExpression(js),
|
||||||
},
|
},
|
||||||
python: {
|
python: {
|
||||||
name: "python",
|
name: "python",
|
||||||
@ -136,20 +138,20 @@ const specialVars = {
|
|||||||
desc: "模拟python -c,并获取返回值,如{{py:print(1)}}",
|
desc: "模拟python -c,并获取返回值,如{{py:print(1)}}",
|
||||||
tooltip: "只支持单行语句",
|
tooltip: "只支持单行语句",
|
||||||
type: "command",
|
type: "command",
|
||||||
match: /{{py:(.*?)}}/mg,
|
match: /{{py:(.*?)}}/gm,
|
||||||
repl: py => window.runPythonCommand(py.slice(5, -2))
|
repl: (py) => window.runPythonCommand(py.slice(5, -2)),
|
||||||
},
|
},
|
||||||
userData: {
|
userData: {
|
||||||
name: "userData",
|
name: "userData",
|
||||||
label: "{{usr:}}",
|
label: "{{usr:}}",
|
||||||
desc: "用户设置的变量,类似一个全局配置项",
|
desc: "用户设置的变量,类似一个全局配置项",
|
||||||
match: /{{usr:(.*?)}}/mg,
|
match: /{{usr:(.*?)}}/gm,
|
||||||
repl: (text, userData) => {
|
repl: (text, userData) => {
|
||||||
let filterd = userData.filter(x => x.id === text.slice(6, -2))
|
let filterd = userData.filter((x) => x.id === text.slice(6, -2));
|
||||||
return filterd.length ? filterd[0].value : ''
|
return filterd.length ? filterd[0].value : "";
|
||||||
},
|
},
|
||||||
tooltip: "仅本机可用时,该变量值只在本机有效,否则,所有电脑有效",
|
tooltip: "仅本机可用时,该变量值只在本机有效,否则,所有电脑有效",
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
|
|
||||||
export default specialVars
|
export default specialVars;
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
<component
|
<component
|
||||||
:is="commandEditorAction.component"
|
:is="commandEditorAction.component"
|
||||||
:action="commandEditorAction"
|
:action="commandEditorAction"
|
||||||
@editorEvent="editorEvent"
|
@editorEvent="handleEditorEvent"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
@ -40,7 +40,7 @@
|
|||||||
import { defineAsyncComponent } from "vue";
|
import { defineAsyncComponent } from "vue";
|
||||||
import { useCommandManager } from "js/commandManager.js";
|
import { useCommandManager } from "js/commandManager.js";
|
||||||
import changeLog from "js/options/changeLog.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 CommandEditor from "components/CommandEditor";
|
||||||
import ComposerEditor from "components/ComposerEditor";
|
import ComposerEditor from "components/ComposerEditor";
|
||||||
import FooterBar from "src/components/config/FooterBar.vue";
|
import FooterBar from "src/components/config/FooterBar.vue";
|
||||||
@ -214,13 +214,14 @@ export default {
|
|||||||
},
|
},
|
||||||
saveCommand(command) {
|
saveCommand(command) {
|
||||||
const code = this.commandManager.saveCommand(command);
|
const code = this.commandManager.saveCommand(command);
|
||||||
|
if (!code) return;
|
||||||
this.locateToCommand(command.tags, code);
|
this.locateToCommand(command.tags, code);
|
||||||
quickcommand.showMessageBox("保存成功!");
|
quickcommand.showMessageBox("保存成功!");
|
||||||
},
|
},
|
||||||
editorEvent(event) {
|
handleEditorEvent(event, data) {
|
||||||
switch (event.type) {
|
switch (event) {
|
||||||
case "save":
|
case "save":
|
||||||
this.saveCommand(event.data);
|
this.saveCommand(data);
|
||||||
break;
|
break;
|
||||||
case "back":
|
case "back":
|
||||||
this.isEditorShow = false;
|
this.isEditorShow = false;
|
||||||
@ -265,20 +266,12 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.editor-container {
|
.editor-container {
|
||||||
color: var(--utools-bright-font-color);
|
color: var(--utools-font-color);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
inset: 0;
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
z-index: 5000;
|
z-index: 5000;
|
||||||
background: var(--utools-bright-bg);
|
background: var(--utools-bg-color);
|
||||||
}
|
|
||||||
|
|
||||||
.body--dark .editor-container {
|
|
||||||
color: var(--utools-dark-font-color);
|
|
||||||
background: var(--utools-dark-bg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 编辑器容器动画 */
|
/* 编辑器容器动画 */
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<CommandEditor ref="commandEditor" :action="action" />
|
||||||
<CommandEditor ref="commandEditor" :action="action"></CommandEditor>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -1,26 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="server-page">
|
||||||
<MonacoEditor
|
<CodeEditor
|
||||||
:placeholder="false"
|
v-model="cmd"
|
||||||
class="absolute-top"
|
@update:modelValue="saveCode"
|
||||||
ref="editor"
|
language="quickcommand"
|
||||||
@typing="
|
style="flex: 1"
|
||||||
(val) => {
|
|
||||||
if (cmd === val) return;
|
|
||||||
cmd = val;
|
|
||||||
saveCode();
|
|
||||||
}
|
|
||||||
"
|
|
||||||
:style="{
|
|
||||||
bottom: bottomHeight + 'px',
|
|
||||||
}"
|
|
||||||
/>
|
/>
|
||||||
<div
|
<div class="flex items-center justify-between q-px-md shadow-10">
|
||||||
class="absolute-bottom flex items-center justify-between q-px-md shadow-10"
|
|
||||||
:style="{
|
|
||||||
height: bottomHeight + 'px',
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<div class="q-gutter-xs flex items-center full-height content-center">
|
<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>POST</q-badge
|
||||||
><q-badge color="primary" dense square>GET</q-badge>
|
><q-badge color="primary" dense square>GET</q-badge>
|
||||||
@ -73,22 +59,16 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import MonacoEditor from "components/editor/MonacoEditor";
|
import CodeEditor from "components/editor/CodeEditor.vue";
|
||||||
import { dbManager } from "js/utools.js";
|
import { dbManager } from "js/utools.js";
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { MonacoEditor },
|
components: { CodeEditor },
|
||||||
data() {
|
setup() {
|
||||||
return {
|
const cmd = ref(dbManager.getStorage("cfg_serverCode") || "");
|
||||||
bottomHeight: 40,
|
const saveCodeTimer = ref(null);
|
||||||
saveCodeTimer: null,
|
return { cmd, saveCodeTimer };
|
||||||
cmd: null,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.cmd = dbManager.getStorage("cfg_serverCode") || "";
|
|
||||||
this.$refs.editor.setEditorValue(this.cmd);
|
|
||||||
this.$refs.editor.setEditorLanguage("javascript");
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
enableServer() {
|
enableServer() {
|
||||||
@ -136,3 +116,12 @@ export default {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.server-page {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user