完善编辑命令界面

This commit is contained in:
fofolee 2022-04-07 00:37:56 +08:00
parent 5d630b5c1f
commit c6cce49b82
5 changed files with 540 additions and 237 deletions

View File

@ -110,7 +110,7 @@
<q-badge rounded :color="cmdBadgeColor()"
><q-icon
class="q-mr-xs"
:name="commandTypes.keyword.icon"
:name="commandTypes.key.icon"
/>{{ getShortStrByByte(cmd) }}</q-badge
>
<q-tooltip>
@ -217,7 +217,7 @@ export default {
maxCmdStingLen: 8,
commandTypes: commandTypes,
cmdBadgeSheet: {
keyword: "primary",
key: "primary",
files: "light-blue",
window: "indigo",
regex: "cyan",

View File

@ -1,10 +1,9 @@
<template>
<div class="relative">
<!-- 命令设置栏 -->
<q-scroll-area
:thumb-style="{
width: '3px',
}"
<CommandMenu
ref="menu"
:quickcommandInfo="quickcommandInfo"
class="absolute-left shadow-10"
:style="{
width: sideBarWidth,
@ -12,154 +11,7 @@
zIndex: 1,
}"
v-if="showSidebar"
>
<div class="row q-pa-md q-gutter-md">
<q-btn
dense
flat
color="grey"
icon="arrow_back_ios_new"
@click="closeEditor"
/>
<q-img class="commandLogo" width="64px" :src="commandIcon" />
<div class="row">
<div>
<!-- 说明 -->
<q-input
standout="bg-primary text-white"
square
v-model="quickcommandInfo.features.explain"
type="text"
label="说明"
>
<template v-slot:prepend>
<q-icon name="drive_file_rename_outline" />
</template>
</q-input>
<!-- 匹配类型 -->
<q-select
standout="bg-primary text-white"
square
options-dense
@update:model-value="cmdMatch = null"
:options="commandTypesOptions"
v-model="cmdType"
type="text"
label="匹配类型"
>
<template v-slot:prepend>
<q-icon :name="cmdType.icon" />
</template>
<template v-slot:option="scope">
<q-item v-bind="scope.itemProps" v-on="scope.itemEvents">
<q-item-section avatar>
<q-icon :name="scope.opt.icon" />
</q-item-section>
<q-item-section>
<q-item-label v-html="scope.opt.name" />
<q-item-label caption>{{ scope.opt.desc }}</q-item-label>
</q-item-section>
</q-item>
</template></q-select
>
<!-- 匹配规则 -->
<q-select
v-if="cmdType.valueType === 'array'"
standout="bg-primary text-white"
square
v-model="cmdMatch"
use-input
use-chips
multiple
hide-dropdown-icon
input-debounce="0"
new-value-mode="add-unique"
type="text"
placeholder="回车添加"
:label="cmdType.matchLabel"
>
<template v-slot:prepend>
<q-icon name="square_foot" />
</template>
</q-select>
<q-input
v-else
autogrow
standout="bg-primary text-white"
square
v-model="cmdMatch"
hide-bottom-space
@blur="cmdMatchVerify"
:readonly="cmdType.valueType === 'null'"
type="text"
:label="cmdType.matchLabel"
>
<template v-slot:prepend>
<q-icon name="square_foot" />
</template>
</q-input>
<!-- 标签 -->
<q-select
standout="bg-primary text-white"
square
v-model="quickcommandInfo.tags"
type="text"
label="标签"
use-input
use-chips
multiple
new-value-mode="add-unique"
input-debounce="0"
:options="allQuickCommandTags"
/>
<!-- 特殊变量 -->
<q-select
standout="bg-primary text-white"
square
label="特殊变量"
/>
<!-- 输出 -->
<q-select
standout="bg-primary text-white"
square
options-dense
color="primary"
v-model="quickcommandInfo.output"
:display-value="outputTypes[quickcommandInfo.output].label"
:options="outputTypesOptions"
label="输出"
>
<template v-slot:prepend>
<q-icon :name="outputTypes[quickcommandInfo.output].icon" />
</template>
<template v-slot:option="scope">
<q-item v-bind="scope.itemProps">
<q-item-section avatar>
<q-icon :name="outputTypes[scope.opt].icon" />
</q-item-section>
<q-item-section>
<q-item-label v-html="outputTypes[scope.opt].label" />
</q-item-section>
</q-item> </template
></q-select>
<!-- 平台 -->
<q-select
standout="bg-primary text-white"
square
:options="['win32', 'darwin', 'linux']"
use-chips
@blur="platformVerify()"
v-model="quickcommandInfo.features.platform"
multiple
options-dense
label="平台"
><template v-slot:prepend> <q-icon name="window" /> </template
></q-select>
</div>
</div>
</div>
<div></div>
</q-scroll-area>
></CommandMenu>
<!-- 编程语言栏 -->
<div
class="absolute-top"
@ -185,13 +37,13 @@
>
<template v-slot:append>
<q-avatar size="lg" square>
<img :src="currentProgramLogo" />
<img :src="getLanguageIcon(quickcommandInfo.program)" />
</q-avatar>
</template>
<template v-slot:option="scope">
<q-item v-bind="scope.itemProps">
<q-item-section avatar>
<img width="32" :src="'/logo/' + scope.opt + '.png'" />
<img width="32" :src="getLanguageIcon(scope.opt)" />
</q-item-section>
<q-item-section>
<q-item-label v-html="scope.opt" />
@ -252,6 +104,7 @@
<q-btn
flat
v-if="!isRunCodePage"
:disable="!canCommandSave"
color="primary"
icon="save"
@click="saveCurrentCommand()"
@ -299,30 +152,20 @@
<script>
import MonocaEditor from "components/MonocaEditor";
import commandTypes from "../js/options/commandTypes.js";
import outputTypes from "../js/options/outputTypes.js";
let commandTypesOptions = Object.values(commandTypes);
import CommandMenu from "components/CommandMenu";
export default {
components: { MonocaEditor },
components: { MonocaEditor, CommandMenu },
data() {
return {
programLanguages: Object.keys(this.$programmings),
currentMatchType: "关键字",
sideBarWidth: "240px",
sideBarWidth: "290px",
languageBarHeight: "40px",
commandTypes: commandTypes,
commandTypesOptions: commandTypesOptions,
cmdType: commandTypesOptions[0],
cmdMatch: "",
outputTypes: outputTypes,
outputTypesOptions: Object.keys(outputTypes),
specilaVar: "{{input}}",
canCommandSave: this.action.type === "edit" ? true : false,
quickcommandInfo: {
features: {
explain: "",
platform: [],
platform: ["win32", "linux", "darwin"],
},
program: "quickcommand",
cmd: "",
@ -346,8 +189,8 @@ export default {
resultMaxLength: 10000,
showSidebar: this.action.type !== "run",
isRunCodePage: this.action.type === "run",
parent: this.$parent.$parent.$parent.$parent,
commandString: this.$q.platform.is.mac ? "⌘" : "ctrl",
configurationPage: this.$parent.$parent.$parent.$parent,
};
},
props: {
@ -355,16 +198,11 @@ export default {
},
mounted() {
this.init();
this.$refs.menu?.init();
},
computed: {
commandIcon() {
return this.quickcommandInfo.features?.icon || this.currentProgramLogo;
},
currentProgramLogo() {
return "/logo/" + this.quickcommandInfo.program + ".png";
},
allQuickCommandTags() {
return this.configurationPage.allQuickCommandTags.filter(
return this.parentPage.allQuickCommandTags.filter(
(x) => x !== "默认" && x !== "未分类" && x !== "搜索结果"
);
},
@ -382,14 +220,13 @@ export default {
this.quickcommandInfo,
JSON.parse(JSON.stringify(quickCommandInfo))
);
//
let currentQuickCommandCmds = this.getCommandType();
// Object
this.cmdType = this.commandTypes[currentQuickCommandCmds.type];
this.cmdMatch = currentQuickCommandCmds.match;
// monoca
this.setLanguage(this.quickcommandInfo.program);
this.$refs.editor.setEditorValue(quickCommandInfo.cmd);
//
if (this.quickcommandInfo.tags?.includes("默认") && !utools.isDev()) {
this.canCommandSave = false;
}
//
if (this.action.type === "edit") return;
utools.onPluginOut(() => {
@ -411,6 +248,7 @@ export default {
},
programChanged(value) {
this.setLanguage(value);
this.$refs.menu?.setIcon(value);
},
//
matchLanguage() {
@ -426,6 +264,9 @@ export default {
let highlight = this.$programmings[language].highlight;
this.$refs.editor.setEditorLanguage(highlight ? highlight : language);
},
getLanguageIcon(program) {
return `/logo/${program}.png`;
},
//
showHelp() {
utools.createBrowserWindow("./helps/quickcommand.html", {
@ -523,7 +364,7 @@ export default {
//
async replaceTempInputVals(cmd) {
let tempInputVals = [];
let specilaVals = [
let needInputVal = [
"input",
"subinput",
"pwd",
@ -531,7 +372,7 @@ export default {
"WindowInfo",
"MatchedFiles",
];
specilaVals.forEach((x) => {
needInputVal.forEach((x) => {
let m = cmd.match(new RegExp("{{" + x + ".*?}}", "g"));
m &&
m.forEach((y) => tempInputVals.includes(y) || tempInputVals.push(y));
@ -568,47 +409,6 @@ export default {
},
//
saveCurrentCommand() {},
//
getCommandType() {
let data = {};
let cmds = this.quickcommandInfo.features.cmds;
if (cmds.length === 1) {
let { type, match } = cmds[0];
data.type = type ? type : "keyword";
data.match =
data.type === "keyword" ? cmds[0] : match?.app ? match.app : match;
} else {
data.type = cmds.filter((x) => !x.length).length
? "professional"
: "keyword";
data.match =
data.type === "keyword" ? cmds : JSON.stringify(cmds, null, 4);
}
return data;
},
//
cmdMatchVerify() {
this.cmdType.valueType === "regex" &&
!/^\/.*?\/[igm]*$/.test(this.cmdMatch) &&
(this.cmdMatch = `/${this.cmdMatch}/`);
},
//
platformVerify() {
this.quickcommandInfo.features.platform > 0 ||
this.quickcommandInfo.features.platform.push(window.processPlatform);
},
},
};
</script>
<style scoped>
.commandLogo {
cursor: pointer;
transition: 10s;
filter: drop-shadow(2px 1px 1px grey);
}
.commandLogo:hover {
transition: 10s;
transform: rotate(360deg);
}
</style>

View File

@ -0,0 +1,352 @@
<template>
<q-scroll-area
:thumb-style="{
width: '3px',
}"
>
<div class="row q-pa-md q-gutter-md">
<q-btn
dense
flat
color="grey"
icon="arrow_back_ios_new"
@click="$parent.closeEditor"
/>
<q-img
class="commandLogo"
width="64px"
:src="currentCommand.features.icon"
/>
<div class="row">
<div>
<!-- 说明 -->
<q-input
stack-label
label-color="primary"
borderless
square
v-model="currentCommand.features.explain"
type="text"
placeholder="请输入说明"
label="说明"
>
<template v-slot:prepend>
<q-icon color="primary" name="drive_file_rename_outline" />
</template>
</q-input>
<!-- 匹配类型 -->
<q-select
hide-dropdown-icon
stack-label
label-color="primary"
transition-show="jump-down"
transition-hide="jump-up"
borderless
square
@update:model-value="
(val) =>
(cmdMatch =
val.name === 'professional'
? JSON.stringify(val.jsonSample, null, 4)
: null)
"
:options="commandTypesOptions"
v-model="cmdType"
type="text"
label="匹配类型"
>
<template v-slot:prepend>
<q-icon color="primary" :name="cmdType.icon" />
</template>
<template v-slot:option="scope">
<q-item v-bind="scope.itemProps">
<q-item-section avatar>
<q-icon :name="scope.opt.icon" />
</q-item-section>
<q-item-section>
<q-item-label v-html="scope.opt.name" />
<q-item-label caption>{{ scope.opt.desc }}</q-item-label>
</q-item-section>
</q-item>
</template>
</q-select>
<!-- 匹配规则 -->
<q-select
hide-dropdown-icon
stack-label
label-color="primary"
transition-show="jump-down"
transition-hide="jump-up"
v-if="cmdType.valueType === 'array'"
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"
>
<template v-slot:prepend>
<q-icon color="primary" name="square_foot" />
</template>
</q-select>
<q-input
v-else
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 color="primary" name="square_foot" />
</template>
</q-input>
<!-- 标签 -->
<q-select
hide-dropdown-icon
stack-label
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"
input-debounce="0"
:options="allQuickCommandTags"
>
<template v-slot:prepend>
<q-icon color="primary" name="label" />
</template>
</q-select>
<!-- 特殊变量 -->
<q-select
hide-dropdown-icon
stack-label
label-color="primary"
transition-show="jump-down"
transition-hide="jump-up"
borderless
square
:options="specialVarsOptions"
v-model="specialVar"
input-debounce="0"
type="text"
label="特殊变量"
>
<template v-slot:prepend>
<q-icon color="primary" 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.name" />
<q-item-label caption>{{ scope.opt.desc }}</q-item-label>
</q-item-section>
</q-item>
</template></q-select
>
<!-- 输出 -->
<q-select
hide-dropdown-icon
stack-label
label-color="primary"
transition-show="jump-down"
transition-hide="jump-up"
borderless
square
color="primary"
v-model="currentCommand.output"
:display-value="outputTypes[currentCommand.output].label"
:options="outputTypesOptions"
label="输出"
>
<template v-slot:prepend>
<q-icon
color="primary"
:name="outputTypes[currentCommand.output].icon"
/>
</template>
<template v-slot:option="scope">
<q-item v-bind="scope.itemProps">
<q-item-section avatar>
<q-icon :name="outputTypes[scope.opt].icon" />
</q-item-section>
<q-item-section>
<q-item-label v-html="outputTypes[scope.opt].label" />
</q-item-section>
</q-item>
</template>
</q-select>
<!-- 平台 -->
<q-select
hide-dropdown-icon
stack-label
label-color="primary"
transition-show="jump-down"
transition-hide="jump-up"
borderless
square
:options="['win32', 'linux', 'darwin']"
use-chips
@blur="platformVerify()"
v-model="currentCommand.features.platform"
multiple
label="平台"
>
<template v-slot:prepend>
<q-icon color="primary" name="window" />
</template>
</q-select>
</div>
</div>
</div>
</q-scroll-area>
</template>
<script>
import commandTypes from "../js/options/commandTypes.js";
import outputTypes from "../js/options/outputTypes.js";
import specialVars from "../js/options/specialVars.js";
let commandTypesOptions = Object.values(commandTypes);
export default {
data() {
return {
currentCommand: {
tags: [],
output: "text",
features: {
explain: "",
platform: ["win32", "linux", "darwin"],
icon: "",
},
},
commandTypes: commandTypes,
commandTypesOptions: commandTypesOptions,
currentMatchType: "关键字",
cmdType: commandTypesOptions[0],
cmdMatch: "",
outputTypes: outputTypes,
outputTypesOptions: Object.keys(outputTypes),
specialVar: "{{}}",
specialVarsOptions: Object.values(specialVars),
allQuickCommandTags: this.$parent.parent.allQuickCommandTags,
};
},
props: {
quickcommandInfo: Object,
},
mounted() {
window.CommandMenu = this;
},
computed: {},
methods: {
init() {
let currentQuickCommandCmds = this.getCommandType();
this.cmdType = this.commandTypes[currentQuickCommandCmds.type];
this.cmdMatch = currentQuickCommandCmds.match;
Object.assign(
this.currentCommand,
JSON.parse(JSON.stringify(this.quickcommandInfo))
);
this.setIcon(this.currentCommand.program);
this.platformVerify();
},
// 使
setIcon(language) {
this.currentCommand.features.icon?.slice(0, 5) === "data:" ||
(this.currentCommand.features.icon =
this.$parent.getLanguageIcon(language));
},
getCommandType() {
let data = { type: "key", match: [] };
let cmds = this.quickcommandInfo.features?.cmds;
if (!cmds) return data;
if (cmds.length === 1) {
let { type, match } = cmds[0];
data.type = type ? type : "key";
data.match =
data.type === "key" ? cmds : match?.app ? match.app : match;
} else {
data.type = cmds.filter((x) => !x.length).length
? "professional"
: "key";
data.match = data.type === "key" ? cmds : JSON.stringify(cmds, null, 4);
}
return data;
},
//
platformVerify() {
this.currentCommand.features.platform?.length > 0 ||
this.currentCommand.features.platform.push(window.processPlatform);
},
//
regexVerify() {
console.log(1);
if (
this.cmdType.valueType === "regex" &&
!/^\/.*?\/[igm]*$/.test(this.cmdMatch)
)
this.cmdMatch = `/${this.cmdMatch}/`;
},
//
SaveMenuData() {
let updateData = {
features: this.currentCommand.features,
output: this.currentCommand.output,
tags: this.currentCommand.tags,
};
//
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");
}
updateData.features.cmds = this.cmdType.matchToCmds(
this.cmdMatch,
updateData.features.explain
);
return updateData;
},
},
};
</script>
<style scoped>
.commandLogo {
cursor: pointer;
transition: 10s;
filter: drop-shadow(2px 1px 1px grey);
}
.commandLogo:hover {
transition: 10s;
transform: rotate(360deg);
}
</style>

View File

@ -1,11 +1,13 @@
const commandTypes = {
keyword: {
name: "keyword",
key: {
name: "key",
label: "关键词",
icon: "font_download",
matchLabel: "关键词",
desc: "在主输入框输入对应关键字进入插件,最通用的一种模式,关键字可以设置多个",
valueType: "array"
valueType: "array",
matchToCmds: (rules, desc) => rules,
verify: (rules) => rules.length > 0 || "关键词不能为空"
},
regex: {
name: "regex",
@ -13,7 +15,14 @@ const commandTypes = {
matchLabel: "正则",
icon: "rule",
desc: "匹配主输入框或超级面板选中的文本,可以获取输入框文本或选中文本作为变量",
valueType: "regex"
valueType: "regex",
matchToCmds: (rules, desc) => [{
label: desc,
type: "regex",
match: rules,
minNum: 1,
}, ],
verify: rules => !!rules > 0 || "正则不能为空"
},
over: {
name: "over",
@ -21,7 +30,13 @@ const commandTypes = {
matchLabel: "无需设置",
icon: "emergency",
desc: "匹配主输入框的所有文本,但只有在该文本未设置对应的插件或功能时才生效",
valueType: "null"
valueType: null,
matchToCmds: (rules, desc) => [{
label: desc,
type: "over",
minNum: 1,
}],
verify: rules => true
},
window: {
name: "window",
@ -29,7 +44,15 @@ const commandTypes = {
matchLabel: "进程名",
icon: "widgets",
desc: "匹配呼出uTools前或唤出超级面板时的活动窗口可以获取窗口的信息或文件夹路径作为变量",
valueType: "array"
valueType: "array",
matchToCmds: (rules, desc) => [{
type: "window",
label: desc,
match: {
"app": rules
}
}],
verify: rules => rules.length > 0 || "进程名不能为空"
},
img: {
name: "img",
@ -37,7 +60,12 @@ const commandTypes = {
matchLabel: "无需配置",
icon: "panorama",
desc: "匹配主输入框或超级面板选中的图片,并返回图片的 base64",
valueType: "null"
valueType: null,
matchToCmds: (rules, desc) => [{
label: desc,
type: "img",
}],
verify: rules => true
},
files: {
name: "files",
@ -45,7 +73,14 @@ const commandTypes = {
matchLabel: "正则",
icon: "description",
desc: "匹配主输入框或超级面板选中的文件,可以获取复制及选中的文件信息作为变量",
valueType: "regex"
valueType: "regex",
matchToCmds: (rules, desc) => [{
type: "files",
label: desc,
match: rules,
"minLength": 1,
}, ],
verify: rules => !!rules > 0 || "正则不能为空"
},
professional: {
@ -54,8 +89,60 @@ const commandTypes = {
matchLabel: "json配置",
icon: "construction",
desc: "通过json格式的配置实现同时匹配关键字、窗口、文件甚至图片或者指定文件数量、窗口类等",
valueType: "json"
valueType: "json",
matchToCmds: (rules, desc) => JSON.parse(rules),
verify: rules => {
try {
JSON.parse(rules);
return true
} catch (error) {
return "专业模式json配置错误"
}
},
jsonSample: [
"关键词",
{
"type": "img",
"label": "图片匹配"
},
{
"type": "files",
"label": "文件匹配",
"fileType": "file",
"match": "/aaa/",
"minLength": 1,
"maxLength": 99
},
{
"type": "regex",
"label": "文本正则匹配",
"match": "/bbb/i",
"minLength": 1,
"maxLength": 99
},
{
"type": "over",
"label": "无匹配时",
"exclude": "/ccc/i",
"minLength": 1,
"maxLength": 99
},
{
"type": "window",
"label": "窗口动作",
"match": {
"app": [
"ddd.app",
"eee.exe"
],
"title": "/fff/",
"class": [
"ggg"
]
}
}
]
}
}
export default commandTypes
export default commandTypes

View File

@ -0,0 +1,64 @@
const specialVars = {
isWin: {
name: "isWin",
label: "{{isWin}}",
desc: "是否Window系统, 返回1或0"
},
LocalId: {
name: "LocalId",
label: "{{LocalId}}",
desc: "本机唯一ID"
},
BrowserUrl: {
name: "BrowserUrl",
label: "{{BrowserUrl}}",
desc: "浏览器当前链接"
},
ClipText: {
name: "ClipText",
label: "{{ClipText}}",
desc: "剪贴板内容"
},
subinput: {
name: "subinput",
label: "{{subinput}}",
desc: "子输入框的文本"
},
input: {
name: "input",
label: "{{input}}",
desc: "主输入框的文本"
},
pwd: {
name: "pwd",
label: "{{pwd}}",
desc: "文件管理器当前目录"
},
WindowInfo: {
name: "WindowInfo",
label: "{{WindowInfo}}",
desc: "当前窗口信息JSON格式"
},
SelectFile: {
name: "SelectFile",
label: "{{SelectFile}}",
desc: "文件管理器选中的文件不支持Linux"
},
MatchedFiles: {
name: "MatchedFiles",
label: "{{MatchedFiles}}",
desc: "匹配的文件JSON格式"
},
type: {
name: "type",
label: "{{type}}",
desc: "专业模式的type"
},
payload: {
name: "payload",
label: "{{payload}}",
desc: "专业模式的payload,JSON格式"
}
}
export default specialVars