mirror of
https://github.com/fofolee/uTools-quickcommand.git
synced 2025-12-21 03:25:41 +08:00
重构CommandEditor组件
This commit is contained in:
@@ -1,168 +1,231 @@
|
||||
<template>
|
||||
<div class="row" v-show="!!height">
|
||||
<div class="col">
|
||||
<div>
|
||||
<q-select
|
||||
dense
|
||||
standout="bg-primary text-white"
|
||||
square
|
||||
hide-bottom-space
|
||||
color="primary"
|
||||
transition-show="jump-down"
|
||||
transition-hide="jump-up"
|
||||
@update:model-value="updateProgram"
|
||||
:model-value="modelValue.program"
|
||||
:options="programLanguages"
|
||||
<div class="command-language-bar">
|
||||
<q-select
|
||||
class="q-pl-xs"
|
||||
dense
|
||||
options-dense
|
||||
borderless
|
||||
square
|
||||
hide-bottom-space
|
||||
color="primary"
|
||||
transition-show="jump-down"
|
||||
transition-hide="jump-up"
|
||||
:model-value="currentCommand.program"
|
||||
@update:model-value="handleProgramChange"
|
||||
:options="Object.keys(programs)"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-badge
|
||||
label="环境"
|
||||
>
|
||||
<template v-slot:append>
|
||||
<q-avatar size="lg" square>
|
||||
<img :src="programs[modelValue.program].icon" />
|
||||
</q-avatar>
|
||||
</template>
|
||||
<template v-slot:option="scope">
|
||||
<q-item v-bind="scope.itemProps">
|
||||
<q-item-section avatar>
|
||||
<img width="32" :src="programs[scope.opt].icon" />
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label v-html="scope.opt" />
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
</q-select>
|
||||
</div>
|
||||
</div>
|
||||
<q-separator vertical />
|
||||
<div class="col-auto justify-end flex">
|
||||
<q-btn-group unelevated class="button-group">
|
||||
<template v-if="modelValue.program === 'quickcommand'">
|
||||
<q-btn
|
||||
v-for="(item, index) in [
|
||||
'help_center',
|
||||
'view_timeline',
|
||||
]"
|
||||
color="primary"
|
||||
text-color="white"
|
||||
class="q-ml-sm"
|
||||
style="height: 20px"
|
||||
/>
|
||||
</template>
|
||||
<template v-slot:append>
|
||||
<q-avatar size="20px" square v-if="isRunCodePage">
|
||||
<img :src="programs[currentCommand.program].icon" />
|
||||
</q-avatar>
|
||||
</template>
|
||||
<template v-slot:option="scope">
|
||||
<q-item v-bind="scope.itemProps">
|
||||
<q-item-section avatar>
|
||||
<img width="32" :src="programs[scope.opt].icon" />
|
||||
</q-item-section>
|
||||
<q-item-section>
|
||||
<q-item-label v-html="scope.opt" />
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
</q-select>
|
||||
<q-btn-group unelevated class="button-group">
|
||||
<q-btn-dropdown
|
||||
class="special-var-btn"
|
||||
dense
|
||||
flat
|
||||
label="变量"
|
||||
color="primary"
|
||||
icon="data_object"
|
||||
>
|
||||
<q-list>
|
||||
<q-item
|
||||
v-for="(item, index) in Object.values(specialVars)"
|
||||
:key="index"
|
||||
dense
|
||||
flat
|
||||
color="primary"
|
||||
class="settings-btn"
|
||||
:icon="item"
|
||||
@click="handleQuickCommandAction(index)"
|
||||
clickable
|
||||
v-close-popup
|
||||
@click="handleSpecialVarClick(item)"
|
||||
>
|
||||
<q-tooltip>
|
||||
{{ ["查看文档", "可视化编排"][index] }}
|
||||
</q-tooltip>
|
||||
</q-btn>
|
||||
</template>
|
||||
<q-item-section>
|
||||
<q-item-label class="row items-center justify-between">
|
||||
<div v-text="item.label" />
|
||||
<div v-if="item.onlyCmdTypes" class="row">
|
||||
<q-badge color="grey-9" class="q-ml-xs"> 仅 </q-badge>
|
||||
<q-badge
|
||||
v-for="type in item.onlyCmdTypes"
|
||||
:key="type"
|
||||
class="q-ml-xs"
|
||||
v-text="commandTypes[type].label"
|
||||
color="grey-9"
|
||||
/>
|
||||
</div>
|
||||
</q-item-label>
|
||||
<q-tooltip v-if="item.tooltip">
|
||||
{{ item.tooltip }}
|
||||
</q-tooltip>
|
||||
<q-item-label caption>{{ item.desc }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-btn-dropdown>
|
||||
|
||||
<q-btn-dropdown
|
||||
v-else-if="modelValue.program !== 'html'"
|
||||
<template v-if="currentCommand.program === 'quickcommand'">
|
||||
<q-btn
|
||||
v-for="(item, index) in ['help_center', 'view_timeline']"
|
||||
:key="index"
|
||||
dense
|
||||
flat
|
||||
color="primary"
|
||||
class="settings-btn"
|
||||
dense
|
||||
flat
|
||||
ref="settings"
|
||||
color="primary"
|
||||
icon="settings"
|
||||
:label="['文档', '可视化'][index]"
|
||||
:icon="item"
|
||||
@click="handleQuickCommandAction(index)"
|
||||
>
|
||||
<q-list>
|
||||
<!-- 自定义解释器 -->
|
||||
<q-item
|
||||
v-for="(item, index) in Object.keys(modelValue.customOptions)"
|
||||
:key="index"
|
||||
v-show="modelValue.program === 'custom'"
|
||||
</q-btn>
|
||||
</template>
|
||||
|
||||
<q-btn-dropdown
|
||||
v-model="isSettingsVisible"
|
||||
v-else-if="currentCommand.program !== 'html'"
|
||||
class="settings-btn"
|
||||
dense
|
||||
flat
|
||||
label="设置"
|
||||
color="primary"
|
||||
icon="settings"
|
||||
>
|
||||
<q-list>
|
||||
<!-- 自定义解释器 -->
|
||||
<q-item
|
||||
v-for="(item, index) in Object.keys(currentCommand.customOptions)"
|
||||
:key="index"
|
||||
v-show="currentCommand.program === 'custom'"
|
||||
>
|
||||
<q-input
|
||||
stack-label
|
||||
autofocus
|
||||
dense
|
||||
outlined
|
||||
class="full-width"
|
||||
:label="
|
||||
[
|
||||
'解释器路径,如:/opt/python',
|
||||
'运行参数,如:-u',
|
||||
'脚本后缀,不含点,如:py',
|
||||
][index]
|
||||
"
|
||||
:model-value="currentCommand.customOptions[item]"
|
||||
@update:model-value="
|
||||
(val) => updateModelValue(`customOptions.${item}`, val)
|
||||
"
|
||||
>
|
||||
<q-input
|
||||
stack-label
|
||||
autofocus
|
||||
dense
|
||||
outlined
|
||||
class="full-width"
|
||||
@blur="matchLanguage"
|
||||
:label="
|
||||
[
|
||||
'解释器路径,如:/opt/python',
|
||||
'运行参数,如:-u',
|
||||
'脚本后缀,不含点,如:py',
|
||||
][index]
|
||||
"
|
||||
:model-value="modelValue.customOptions[item]"
|
||||
@update:model-value="(val) => updateCustomOption(item, val)"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="code" />
|
||||
</template>
|
||||
</q-input>
|
||||
</q-item>
|
||||
<!-- 脚本参数 -->
|
||||
<q-item v-show="modelValue.program !== 'quickcommand'">
|
||||
<q-input
|
||||
dense
|
||||
stack-label
|
||||
outlined
|
||||
label="脚本参数"
|
||||
class="full-width"
|
||||
:model-value="modelValue.scptarg"
|
||||
@update:model-value="updateScptarg"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="input" />
|
||||
</template>
|
||||
</q-input>
|
||||
</q-item>
|
||||
<!-- 编码设置 -->
|
||||
<q-item
|
||||
v-for="(item, index) in Object.keys(modelValue.charset)"
|
||||
:key="index"
|
||||
v-show="modelValue.program !== 'quickcommand'"
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="code" />
|
||||
</template>
|
||||
</q-input>
|
||||
</q-item>
|
||||
<!-- 脚本参数 -->
|
||||
<q-item v-show="currentCommand.program !== 'quickcommand'">
|
||||
<q-input
|
||||
dense
|
||||
stack-label
|
||||
outlined
|
||||
label="脚本参数"
|
||||
class="full-width"
|
||||
:model-value="currentCommand.scptarg"
|
||||
@update:model-value="updateModelValue('scptarg', $event)"
|
||||
>
|
||||
<q-select
|
||||
dense
|
||||
outlined
|
||||
stack-label
|
||||
clearable
|
||||
class="full-width"
|
||||
:label="['脚本编码', '输出编码'][index]"
|
||||
:model-value="modelValue.charset[item]"
|
||||
@update:model-value="(val) => updateCharset(item, val)"
|
||||
:options="['GBK', 'utf8', 'Big5']"
|
||||
type="text"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon :name="['format_size', 'output'][index]" />
|
||||
</template>
|
||||
</q-select>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-btn-dropdown>
|
||||
<q-separator vertical inset />
|
||||
<q-btn
|
||||
class="action-btn run-btn"
|
||||
dense
|
||||
flat
|
||||
color="primary"
|
||||
icon="play_arrow"
|
||||
label="运行"
|
||||
@click="$emit('run')"
|
||||
></q-btn>
|
||||
<q-btn
|
||||
class="action-btn save-btn"
|
||||
flat
|
||||
dense
|
||||
v-if="!isRunCodePage"
|
||||
:disable="!canCommandSave"
|
||||
:color="canCommandSave ? 'primary' : 'grey'"
|
||||
icon="save"
|
||||
label="保存"
|
||||
@click="$emit('save')"
|
||||
></q-btn>
|
||||
</q-btn-group>
|
||||
</div>
|
||||
<template v-slot:prepend>
|
||||
<q-icon name="input" />
|
||||
</template>
|
||||
</q-input>
|
||||
</q-item>
|
||||
<!-- 编码设置 -->
|
||||
<q-item
|
||||
v-for="(item, index) in Object.keys(currentCommand.charset)"
|
||||
:key="index"
|
||||
v-show="currentCommand.program !== 'quickcommand'"
|
||||
>
|
||||
<q-select
|
||||
dense
|
||||
outlined
|
||||
stack-label
|
||||
clearable
|
||||
class="full-width"
|
||||
:label="['脚本编码', '输出编码'][index]"
|
||||
:model-value="currentCommand.charset[item]"
|
||||
@update:model-value="
|
||||
(val) => updateModelValue(`charset.${item}`, val)
|
||||
"
|
||||
:options="['GBK', 'utf8', 'Big5']"
|
||||
type="text"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<q-icon :name="['format_size', 'output'][index]" />
|
||||
</template>
|
||||
</q-select>
|
||||
</q-item>
|
||||
</q-list>
|
||||
</q-btn-dropdown>
|
||||
<q-separator vertical inset />
|
||||
<q-btn
|
||||
v-if="!isRunCodePage"
|
||||
class="action-btn run-btn"
|
||||
dense
|
||||
flat
|
||||
color="primary"
|
||||
icon="arrow_back"
|
||||
label="退出"
|
||||
@click="$emit('action', 'back')"
|
||||
></q-btn>
|
||||
<q-btn
|
||||
class="action-btn run-btn"
|
||||
dense
|
||||
flat
|
||||
color="primary"
|
||||
icon="play_arrow"
|
||||
label="运行"
|
||||
@click="$emit('action', 'run')"
|
||||
></q-btn>
|
||||
<q-btn
|
||||
v-if="!isRunCodePage"
|
||||
:disable="!canCommandSave"
|
||||
:color="canCommandSave ? 'primary' : 'grey'"
|
||||
class="action-btn save-btn"
|
||||
flat
|
||||
dense
|
||||
icon="save"
|
||||
label="保存"
|
||||
@click="$emit('action', 'save')"
|
||||
></q-btn>
|
||||
</q-btn-group>
|
||||
<q-dialog v-model="showUserData">
|
||||
<UserData
|
||||
@insertText="
|
||||
insertSpecialVar($event);
|
||||
showUserData = false;
|
||||
"
|
||||
:showInsertBtn="true"
|
||||
/>
|
||||
</q-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import programs from "js/options/programs.js";
|
||||
import specialVars from "js/options/specialVars.js";
|
||||
import commandTypes from "js/options/commandTypes.js";
|
||||
import UserData from "components/popup/UserData.vue";
|
||||
|
||||
export default {
|
||||
name: "CommandLanguageBar",
|
||||
@@ -171,10 +234,6 @@ export default {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
default: 40,
|
||||
},
|
||||
canCommandSave: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
@@ -184,149 +243,121 @@ export default {
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
emits: [
|
||||
"update:modelValue",
|
||||
"program-changed",
|
||||
"run",
|
||||
"save",
|
||||
"show-composer",
|
||||
],
|
||||
emits: ["update:modelValue", "action"],
|
||||
components: {
|
||||
UserData,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
programs,
|
||||
specialVars,
|
||||
commandTypes,
|
||||
isSettingsVisible: false,
|
||||
showUserData: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
programLanguages() {
|
||||
return Object.keys(this.programs);
|
||||
currentCommand() {
|
||||
return this.modelValue;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
updateProgram(value) {
|
||||
this.$emit("update:modelValue", {
|
||||
...this.modelValue,
|
||||
program: value,
|
||||
});
|
||||
this.programChanged(value);
|
||||
},
|
||||
updateCustomOption(key, value) {
|
||||
this.$emit("update:modelValue", {
|
||||
...this.modelValue,
|
||||
customOptions: {
|
||||
...this.modelValue.customOptions,
|
||||
[key]: value,
|
||||
},
|
||||
});
|
||||
},
|
||||
updateScptarg(value) {
|
||||
this.$emit("update:modelValue", {
|
||||
...this.modelValue,
|
||||
scptarg: value,
|
||||
});
|
||||
},
|
||||
updateCharset(key, value) {
|
||||
this.$emit("update:modelValue", {
|
||||
...this.modelValue,
|
||||
charset: {
|
||||
...this.modelValue.charset,
|
||||
[key]: value,
|
||||
},
|
||||
});
|
||||
},
|
||||
programChanged(value) {
|
||||
this.$emit("program-changed", value);
|
||||
if (value === "custom") {
|
||||
this.$refs.settings.show();
|
||||
handleProgramChange(newProgram) {
|
||||
const newCommand = { ...this.currentCommand };
|
||||
newCommand.program = newProgram;
|
||||
if (newProgram === "custom") {
|
||||
this.isSettingsVisible = true;
|
||||
}
|
||||
},
|
||||
matchLanguage() {
|
||||
if (!this.modelValue.customOptions.ext) return;
|
||||
let language = Object.values(this.programs).filter(
|
||||
(program) => program.ext === this.modelValue.customOptions.ext
|
||||
);
|
||||
if (language.length) {
|
||||
this.$emit("program-changed", language[0].name);
|
||||
if (newProgram === "html") {
|
||||
newCommand.output = "html";
|
||||
}
|
||||
const featuresIcon = this.currentCommand.features.icon || "";
|
||||
if (featuresIcon.slice(0, 10) !== "data:image") {
|
||||
newCommand.features.icon = this.programs[newProgram].icon;
|
||||
}
|
||||
this.$emit("update:modelValue", newCommand);
|
||||
},
|
||||
updateModelValue(keyPath, value) {
|
||||
const newModelValue = { ...this.modelValue };
|
||||
const keys = keyPath.split(".");
|
||||
const lastKey = keys.pop();
|
||||
const target = keys.reduce((obj, key) => obj[key], newModelValue);
|
||||
target[lastKey] = value;
|
||||
this.$emit("update:modelValue", newModelValue);
|
||||
},
|
||||
handleQuickCommandAction(index) {
|
||||
const actions = [
|
||||
() => this.showHelp(),
|
||||
() => this.$emit("show-composer"),
|
||||
() => this.$emit("action", "show-composer"),
|
||||
];
|
||||
actions[index]();
|
||||
},
|
||||
showHelp() {
|
||||
window.showUb.docs();
|
||||
},
|
||||
handleSpecialVarClick(item) {
|
||||
if (item.label === "{{usr:}}") this.showUserData = true;
|
||||
else this.insertSpecialVar(item.label);
|
||||
},
|
||||
insertSpecialVar(text) {
|
||||
if (!text) return;
|
||||
this.$emit("action", "insert-text", `"${text}"`);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.button-group {
|
||||
.action-btn {
|
||||
padding: 0 10px;
|
||||
}
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.button-group :deep(.q-focus-helper) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.button-group :deep(.q-btn__content) {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.button-group :deep(.q-btn-dropdown__arrow) {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.button-group .q-btn:hover {
|
||||
filter: brightness(1.2);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
/* 运行按钮动画 */
|
||||
.run-btn:hover :deep(.q-icon) {
|
||||
display: inline-block;
|
||||
animation: slideRight 1.5s infinite;
|
||||
animation: leftRight 1.5s infinite;
|
||||
}
|
||||
|
||||
/* 保存按钮动画 */
|
||||
.save-btn:not([disabled]):hover :deep(.q-icon) {
|
||||
display: inline-block;
|
||||
animation: saveAnimation 1.2s infinite;
|
||||
animation: upDown 1.2s infinite;
|
||||
}
|
||||
|
||||
/* 设置按钮动画 */
|
||||
.settings-btn :deep(.q-icon:first-child) {
|
||||
display: inline-block;
|
||||
transform: scale(1);
|
||||
transition: transform 0.5s ease;
|
||||
.command-language-bar {
|
||||
background-color: #fffffe;
|
||||
height: 30px;
|
||||
margin-bottom: 2px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.settings-btn:hover :deep(.q-icon:first-child) {
|
||||
transform: scale(1.05);
|
||||
.body--dark .command-language-bar {
|
||||
background-color: #1e1e1e;
|
||||
}
|
||||
|
||||
@keyframes slideRight {
|
||||
0% {
|
||||
transform: translateX(-2px);
|
||||
opacity: 0.7;
|
||||
}
|
||||
50% {
|
||||
transform: translateX(2px);
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: translateX(-2px);
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes saveAnimation {
|
||||
0% {
|
||||
transform: translateY(-1px);
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
transform: translateY(1px);
|
||||
opacity: 0.6;
|
||||
}
|
||||
75% {
|
||||
transform: translateY(0.5px);
|
||||
opacity: 0.8;
|
||||
}
|
||||
100% {
|
||||
transform: translateY(-1px);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-btn:hover :deep(.q-icon) {
|
||||
display: inline-block;
|
||||
.command-language-bar :deep(.q-field__control),
|
||||
.command-language-bar :deep(.q-field__control > *),
|
||||
.command-language-bar :deep(.q-field__native) {
|
||||
max-height: 30px;
|
||||
min-height: 30px;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user