使用组件而非插件的形式重写 quickcommandUI

This commit is contained in:
fofolee
2022-04-25 01:43:57 +08:00
parent b0d06875b3
commit 6d8034d950
18 changed files with 426 additions and 493 deletions

View File

@@ -113,14 +113,21 @@ export default {
utools.outPlugin();
}, 500);
if (currentCommand.program === "quickcommand") {
window.runCodeInSandbox(currentCommand.cmd, (stdout, stderr) => {
if (stderr) {
return quitBeforeShowResult
? alert(stderr)
: this.showRunResult(stderr, false, action);
}
!outPlugin && this.showRunResult(stdout, true, action);
});
let qc = _.cloneDeep(quickcommand);
qc.enterData = this.$root.enterData;
qc.type = this.$root.enterData.type;
window.runCodeInSandbox(
currentCommand.cmd,
(stdout, stderr) => {
if (stderr) {
return quitBeforeShowResult
? alert(stderr)
: this.showRunResult(stderr, false, action);
}
!outPlugin && this.showRunResult(stdout, true, action);
},
{ quickcommand: qc }
);
} else if (currentCommand.program === "html") {
this.showRunResult(currentCommand.cmd, true, action);
} else {
@@ -150,7 +157,9 @@ export default {
let spVars = _.filter(specialVars, (sp) => sp.repl);
_.forIn(spVars, (val, key) => {
if (cmd.includes(val.label.slice(0, -2))) {
cmd = cmd.replace(val.match, (x) => val.repl(x));
cmd = cmd.replace(val.match, (x) =>
val.repl(x, this.$root.enterData)
);
}
});
return cmd;
@@ -208,7 +217,7 @@ export default {
if (!this.needTempPayload) return;
let type =
currentCommand.cmdType || currentCommand.features?.cmds[0].type;
quickcommand.enterData = {
this.$root.enterData = {
type: type || "text",
payload: await commandTypes[type]?.tempPayload?.(),
};
@@ -255,14 +264,11 @@ export default {
this.clear();
},
clear() {
if (!!this.quickcommandListener) {
document.removeEventListener("keydown", this.quickcommandListener);
}
if (!!this.childProcess) {
quickcommand.kill(this.childProcess.pid);
}
quickcommand.closeWaitBtn?.();
quickcommand.closeWaitBtn = () => {};
quickcommand.removeListener();
quickcommand.closeWaitButton();
},
frameLoad(initHeight) {
this.outputAutoHeight(this.fromUtools);
@@ -272,11 +278,5 @@ export default {
unmounted() {
this.stopRun();
},
mounted() {
quickcommand.listenKeydown = (callback) => {
this.quickcommandListener = callback;
document.addEventListener("keydown", callback);
};
},
};
</script>

View File

@@ -2,7 +2,7 @@
export default {
mounted() {
utools.setExpendHeight(0);
quickcommand.enterData.payload.forEach((file) => {
this.$root.payload.forEach((file) => {
let uid = this.getUid();
let fileInfo = window.getFileInfo({
type: "file",

View File

@@ -24,7 +24,7 @@ export default {
);
url = choise.text + "://" + url;
}
let title = quickcommand.enterData.payload.title
let title = this.$root.payload.title
.replace(/和另外 \d+ 个页面.*/, "")
.replace(/[-|—] .*?[Edge|Firefox|Chrome].*/, "")
.trim();

View File

@@ -1,21 +1,20 @@
<template>
<q-dialog ref="dialog" @hide="onDialogHide">
<q-card class="q-dialog-plugin">
<q-card-section>
<div class="text-h5" align="center" v-text="title"></div>
</q-card-section>
<q-card-section class="q-gutter-lg">
<div v-for="(label, index) in labels" :key="index">
<q-btn
class="full-width"
color="primary"
:label="label"
@click="onOKClick(label, index)"
/>
</div>
</q-card-section>
</q-card>
</q-dialog>
<q-card class="q-dialog-plugin">
<q-card-section>
<div class="text-h5" align="center" v-text="options.title"></div>
</q-card-section>
<q-card-section class="q-gutter-lg">
<div v-for="(label, index) in options.labels" :key="index">
<q-btn
class="full-width"
color="primary"
:label="label"
v-close-popup
@click="clickOK(label, index)"
/>
</div>
</q-card-section>
</q-card>
</template>
<script>
@@ -26,29 +25,11 @@ export default {
};
},
props: {
labels: Array,
title: String,
options: Object,
},
emits: ["ok", "hide"],
methods: {
show() {
this.$refs.dialog.show();
},
hide() {
this.$refs.dialog.hide();
},
onDialogHide() {
this.$emit("hide");
},
onOKClick(label, index) {
this.$emit("ok", { id: index, text: label });
this.hide();
},
onCancelClick() {
this.hide();
clickOK(label, index) {
this.$emit("clickOK", { id: index, text: label });
},
},
};

View File

@@ -0,0 +1,34 @@
<template>
<q-card class="q-dialog-plugin">
<q-card-section>
<div class="text-h5" align="left" v-text="options.title"></div>
</q-card-section>
<q-card-section>
{{ options.message }}
</q-card-section>
<q-card-section class="flex justify-end q-gutter-sm">
<q-btn flat label="取消" color="grey" v-close-popup />
<q-btn
flat
autofocus
label="确定"
color="primary"
v-close-popup
@click="clickOK()"
/>
</q-card-section>
</q-card>
</template>
<script>
export default {
methods: {
clickOK() {
this.$emit("clickOK");
},
},
props: {
options: Object,
},
};
</script>

View File

@@ -1,63 +1,41 @@
<template>
<q-dialog ref="dialog" @hide="onDialogHide">
<q-card class="q-dialog-plugin">
<q-card-section>
<div class="text-h5" align="center" v-text="title"></div>
</q-card-section>
<q-card-section class="q-gutter-sm">
<div v-for="(label, index) in labels" :key="index">
<q-input
outlined
v-model="results[index]"
:label="label"
:hint="hints[index]"
hide-hint
autofocus
@keyup.enter="onOKClick"
/>
</div>
</q-card-section>
<q-card-actions align="right">
<q-btn color="blue-9" label="确定" @click="onOKClick" />
<q-btn color="negative" label="取消" @click="onCancelClick" />
</q-card-actions>
</q-card>
</q-dialog>
<q-card class="q-dialog-plugin">
<q-card-section>
<div class="text-h5" align="center" v-text="options.title"></div>
</q-card-section>
<q-card-section class="q-gutter-sm">
<div v-for="(label, index) in options.labels" :key="index">
<q-input
outlined
v-model="results[index]"
:label="label"
:hint="options.hints[index]"
hide-hint
autofocus
@keyup.enter="clickOK"
/>
</div>
</q-card-section>
<q-card-actions align="right">
<q-btn color="blue-9" label="确定" @click="clickOK" v-close-popup />
<q-btn color="negative" label="取消" v-close-popup />
</q-card-actions>
</q-card>
</template>
<script>
export default {
data() {
return {
results: this.values,
results: this.options.values,
};
},
props: {
labels: Array,
title: String,
hints: Array,
values: Array
options: Object,
},
emits: ["ok", "hide"],
methods: {
show() {
this.$refs.dialog.show();
},
hide() {
this.$refs.dialog.hide();
},
onDialogHide() {
this.$emit("hide");
},
onOKClick() {
this.$emit("ok", this.results);
this.hide();
},
onCancelClick() {
this.hide();
clickOK() {
this.$emit("clickOK", this.results);
},
},
};

View File

@@ -0,0 +1,156 @@
<template>
<q-dialog
v-model="showDialog"
:maximized="maximized"
:transition-show="maximized ? 'fade' : 'scale'"
:transition-hide="maximized ? 'fade' : 'scale'"
>
<component
ref="ui"
:is="currentUI"
:options="options"
@clickOK="clickOK"
@hide="showDialog = false"
/>
</q-dialog>
<!-- waitButton 单独一个 -->
<q-dialog seamless position="right" style="z-index: 9999" v-model="showWb">
<q-card>
<q-btn color="primary" :label="wbLabel" @click="wbEvent" v-close-popup />
</q-card>
</q-dialog>
</template>
<script>
import { Notify } from "quasar";
import { markRaw } from "vue";
import InputBox from "components/quickcommandUI/InputBox";
import ButtonBox from "components/quickcommandUI/ButtonBox";
import ConfirmBox from "components/quickcommandUI/ConfirmBox";
import TextArea from "components/quickcommandUI/TextArea";
import SelectList from "components/quickcommandUI/SelectList";
export default {
components: {
InputBox: markRaw(InputBox),
ButtonBox: markRaw(ButtonBox),
ConfirmBox: markRaw(ConfirmBox),
TextArea: markRaw(TextArea),
SelectList: markRaw(SelectList),
},
data() {
return {
currentUI: null,
options: {},
showDialog: false,
maximized: false,
showWb: false,
wbLabel: "",
listeners: [],
};
},
mounted() {
const quickcommandUI = {
showInputBox: (options = ["请输入"], title = "") =>
new Promise((reslove, reject) => {
let props = {
labels: [],
values: [],
hints: [],
title: title,
};
if (!_.isObject(options))
return reject(new TypeError(`应为 Object, 而非 ${typeof options}`));
if (_.isArray(options)) props.labels = options;
else Object.assign(props, options);
this.showUI(InputBox, props, false, reslove);
}),
showButtonBox: (labels = ["确定"], title = "") =>
new Promise((reslove, reject) => {
if (!_.isArray(labels))
return reject(new TypeError(`应为 Array, 而非 ${typeof labels}`));
this.showUI(ButtonBox, { labels, title }, false, reslove);
}),
showConfirmBox: (message = "", title = "提示") =>
new Promise((reslove, reject) => {
this.showUI(ConfirmBox, { message, title }, false, reslove);
}),
showMessageBox: (message, icon = "success", time = 3000) => {
if (icon === "success") icon = "positive";
if (icon === "error") icon = "negative";
Notify.create({
type: icon,
message: message,
timeout: time,
position: "top",
});
},
showTextArea: (placeholder = "", value = "") =>
new Promise((reslove, reject) => {
this.showUI(TextArea, { placeholder, value }, true, reslove);
}),
showSelectList: (initItems, options = {}) =>
new Promise((reslove, reject) => {
if (!_.isArray(initItems))
return reject(
new TypeError(`应为 Array, 而非 ${typeof initItems}`)
);
let defaultOptions = {
placeholder: "输入进行筛选,支持拼音",
optionType: "plaintext",
enableSearch: true,
showCancelButton: false,
closeOnSelect: true,
};
options = Object.assign(defaultOptions, options);
this.showUI(SelectList, { initItems, options }, true, reslove);
}),
showWaitButton: (callback, label = "确定") => {
this.wbLabel = label;
this.showWb = true;
this.wbEvent = callback;
},
closeWaitButton: () => {
this.showWb = false;
},
updateSelectList: (opt, id) => {
if (this.currentUI !== SelectList) throw "请先创建 selectList";
if (typeof id === "undefined") this.$refs.ui.items.push(opt);
else this.$refs.ui.items[id] = opt;
},
listenKeydown: (callback) => {
this.listeners.push(callback);
document.addEventListener("keydown", callback);
},
removeListener: () => {
this.listeners.forEach((listener) => {
document.removeEventListener("keydown", listener);
});
},
};
Object.assign(window.quickcommand, quickcommandUI);
Object.freeze(quickcommand);
},
methods: {
clickOK() {},
wbEvent() {},
showUI(uiComponent, options, maximized, reslove) {
this.showDialog = true;
this.options = options;
this.maximized = maximized;
this.currentUI = uiComponent;
this.clickOK = reslove;
},
},
};
</script>

View File

@@ -1,66 +1,54 @@
<template>
<q-dialog
@keydown="keyEvent"
maximized
ref="dialog"
transition-show="fade"
transition-hide="fade"
@hide="onDialogHide"
>
<q-card>
<q-virtual-scroll
ref="scrollBar"
:style="{ maxHeight: listMaxHeight + 'px', height: '100vh' }"
:virtual-scroll-slice-size="lazyItemSize"
:virtual-scroll-item-size="itemHeight"
@virtual-scroll="scrollEvent"
:items="matchedItems"
>
<template v-slot="{ item, index }">
<q-item
:key="index"
clickable
v-ripple
@mousemove="currentIndex = index"
@click="onOKClick"
manual-focus
:focused="index === currentIndex"
:active="index === currentIndex"
:style="{
height: itemHeight + 'px',
}"
>
<q-item-section v-if="isText">
<q-item-label lines="1">{{ item }}</q-item-label>
</q-item-section>
<q-item-section
v-else-if="isJson"
class="content-start q-gutter-md"
>
<q-avatar size="40px" v-if="item.icon">
<q-img :src="item.icon" />
</q-avatar>
<q-item-label lines="1">{{ item.title }}</q-item-label>
<q-item-label lines="1" caption>{{
item.description
}}</q-item-label>
</q-item-section>
<q-item-section v-else-if="isHtml">
<div v-html="item"></div>
</q-item-section>
</q-item>
</template>
</q-virtual-scroll>
<q-btn
v-if="options.showCancelButton"
class="absolute-bottom-right q-ma-xs"
round
color="primary"
icon="close"
@click="onCancelClick"
/>
</q-card>
</q-dialog>
<q-card @keydown="keyEvent">
<q-virtual-scroll
ref="scrollBar"
:style="{ maxHeight: listMaxHeight + 'px', height: '100vh' }"
:virtual-scroll-slice-size="lazyItemSize"
:virtual-scroll-item-size="itemHeight"
@virtual-scroll="scrollEvent"
:items="matchedItems"
>
<template v-slot="{ item, index }">
<q-item
:key="index"
clickable
v-ripple
@mousemove="currentIndex = index"
@click="clickOK"
manual-focus
:focused="index === currentIndex"
:active="index === currentIndex"
:style="{
height: itemHeight + 'px',
}"
>
<q-item-section v-if="isText">
<q-item-label lines="1">{{ item }}</q-item-label>
</q-item-section>
<q-item-section v-else-if="isJson" class="content-start q-gutter-md">
<q-avatar size="40px" v-if="item.icon">
<q-img :src="item.icon" />
</q-avatar>
<q-item-label lines="1">{{ item.title }}</q-item-label>
<q-item-label lines="1" caption>{{
item.description
}}</q-item-label>
</q-item-section>
<q-item-section v-else-if="isHtml">
<div v-html="item"></div>
</q-item-section>
</q-item>
</template>
</q-virtual-scroll>
<q-btn
v-if="options.showCancelButton"
class="absolute-bottom-right q-ma-xs"
round
color="primary"
icon="close"
@click="hide"
/>
</q-card>
</template>
<script>
@@ -69,7 +57,7 @@ import pinyinMatch from "pinyin-match";
export default {
data() {
return {
items: this.initItems,
items: this.options.initItems,
listMaxHeight: 500,
currentIndex: 0,
itemHeight: 50,
@@ -78,11 +66,7 @@ export default {
};
},
mounted() {
quickcommand.updateSelectList = (opt, id) => {
if (typeof id === "undefined") this.items.push(opt);
else this.items[id] = opt;
};
this.options.enableSearch && this.setSubInput();
this.options.options.enableSearch && this.setSubInput();
this.setUtoolsHeight(this.itemHeight * this.matchedItemsSize);
},
unmounted() {
@@ -102,48 +86,33 @@ export default {
return this.matchedItems.length;
},
isJson() {
return this.options.optionType === "json";
return this.options.options.optionType === "json";
},
isHtml() {
return this.options.optionType === "html";
return this.options.options.optionType === "html";
},
isText() {
return this.options.optionType === "plaintext";
return this.options.options.optionType === "plaintext";
},
},
props: {
options: Object,
initItems: Array,
},
emits: ["ok", "hide"],
methods: {
show() {
this.$refs.dialog.show();
},
hide() {
this.$refs.dialog.hide();
},
onDialogHide() {
this.clear();
this.$emit("hide");
},
onOKClick() {
let selected =
this.options.optionType === "json"
? this.matchedItems[this.currentIndex]
: {
id: this.currentIndex,
text: this.matchedItems[this.currentIndex],
};
this.$emit("ok", selected);
this.options.closeOnSelect && this.hide();
},
onCancelClick() {
this.hide();
clickOK() {
let selected = this.isJson
? this.matchedItems[this.currentIndex]
: {
id: this.currentIndex,
text: this.matchedItems[this.currentIndex],
};
this.$emit("clickOK", selected);
this.options.options.closeOnSelect && this.hide();
},
keyEvent(e) {
@@ -159,7 +128,7 @@ export default {
);
break;
case 13:
this.onOKClick();
this.clickOK();
return;
}
this.$refs.scrollBar.scrollTo(this.currentIndex);
@@ -181,7 +150,7 @@ export default {
setSubInput() {
utools.setSubInput(({ text }) => {
this.searchWords = text;
}, this.options.placeholder);
}, this.options.options.placeholder);
},
setUtoolsHeight(height) {
@@ -191,8 +160,13 @@ export default {
clear() {
utools.removeSubInput();
this.setUtoolsHeight(this.listMaxHeight);
quickcommand.updateSelectList = () => {};
},
},
};
</script>
<style scoped>
.q-card--dark {
background: var(--q-dark-page);
}
</style>

View File

@@ -1,65 +1,43 @@
<template>
<q-dialog maximized ref="dialog" @hide="onDialogHide">
<q-card class="q-dialog-plugin">
<textarea
v-model="result"
:placeholder="placeholder"
autofocus
class="fixed"
:style="{
top: 0,
bottom: 0,
left: 0,
right: 0,
background: '#00000000',
color: $q.dark.isActive ? 'white' : 'black',
fontSize: '16px',
outline: 'none',
border: '3px solid #3577cb',
borderRadius: '5px',
}"
/>
<div
class="fixed-bottom-right q-pa-md q-gutter-sm"
>
<q-btn round color="blue-grey" icon="arrow_back" @click="onCancelClick" />
<q-btn round color="primary" icon="done" @click="onOKClick" />
</div>
</q-card>
</q-dialog>
<q-card class="q-dialog-plugin">
<textarea
v-model="result"
:placeholder="options.placeholder"
autofocus
class="fixed"
:style="{
top: 0,
bottom: 0,
left: 0,
right: 0,
background: '#00000000',
color: $q.dark.isActive ? 'white' : 'black',
fontSize: '16px',
outline: 'none',
border: '3px solid #3577cb',
borderRadius: '5px',
}"
/>
<div class="fixed-bottom-right q-pa-md q-gutter-sm">
<q-btn round color="blue-grey" icon="arrow_back" v-close-popup />
<q-btn round color="primary" icon="done" @click="clickOK" />
</div>
</q-card>
</template>
<script>
export default {
data() {
return {
result: this.value,
result: this.options.value,
};
},
props: {
placeholder: String,
value: String,
options: Object,
},
emits: ["ok", "hide"],
methods: {
show() {
this.$refs.dialog.show();
},
hide() {
this.$refs.dialog.hide();
},
onDialogHide() {
this.$emit("hide");
},
onOKClick() {
this.$emit("ok", this.result);
this.hide();
},
onCancelClick() {
this.hide();
clickOK() {
this.$emit("clickOK", this.result);
},
},
};

View File

@@ -1,47 +0,0 @@
<template>
<q-dialog
ref="dialog"
seamless
position="right"
@hide="onDialogHide"
style="z-index: 9999"
>
<q-card>
<q-btn color="primary" :label="label" @click="onOKClick" v-close-popup />
</q-card>
</q-dialog>
</template>
<script>
export default {
props: {
label: String,
},
mounted() {
quickcommand.closeWaitBtn = () => {
this.$refs?.dialog?.hide();
};
},
emits: ["ok", "hide"],
methods: {
show() {
this.$refs.dialog.show();
},
hide() {
this.$refs.dialog.hide();
},
onDialogHide() {
this.$emit("hide");
},
onOKClick() {
this.$emit("ok");
},
onCancelClick() {
this.hide();
},
},
};
</script>