mirror of
https://github.com/fofolee/uTools-quickcommand.git
synced 2025-07-01 05:23:54 +08:00
用户交互新增选择对话框,VarInput支持多选,新增CheckGroup组件
This commit is contained in:
parent
fe26f98809
commit
036b6fa934
@ -6,6 +6,7 @@ const quickcomposer = {
|
|||||||
network: require("./quickcomposer/network"),
|
network: require("./quickcomposer/network"),
|
||||||
coding: require("./quickcomposer/coding"),
|
coding: require("./quickcomposer/coding"),
|
||||||
math: require("./quickcomposer/math"),
|
math: require("./quickcomposer/math"),
|
||||||
|
ui: require("./quickcomposer/ui"),
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = quickcomposer;
|
module.exports = quickcomposer;
|
||||||
|
50
plugin/lib/quickcomposer/ui/dialog.js
Normal file
50
plugin/lib/quickcomposer/ui/dialog.js
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
const showSaveDialog = (
|
||||||
|
title,
|
||||||
|
defaultPath,
|
||||||
|
buttonLabel,
|
||||||
|
message,
|
||||||
|
extensions,
|
||||||
|
properties
|
||||||
|
) => {
|
||||||
|
return window.utools.showSaveDialog({
|
||||||
|
title,
|
||||||
|
defaultPath,
|
||||||
|
buttonLabel,
|
||||||
|
message,
|
||||||
|
properties,
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
name: "文件",
|
||||||
|
extensions,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const showOpenDialog = (
|
||||||
|
title,
|
||||||
|
defaultPath,
|
||||||
|
buttonLabel,
|
||||||
|
message,
|
||||||
|
extensions,
|
||||||
|
properties
|
||||||
|
) => {
|
||||||
|
return window.utools.showOpenDialog({
|
||||||
|
title,
|
||||||
|
defaultPath,
|
||||||
|
buttonLabel,
|
||||||
|
message,
|
||||||
|
properties,
|
||||||
|
filters: [
|
||||||
|
{
|
||||||
|
name: "文件",
|
||||||
|
extensions,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
showSaveDialog,
|
||||||
|
showOpenDialog,
|
||||||
|
};
|
6
plugin/lib/quickcomposer/ui/index.js
Normal file
6
plugin/lib/quickcomposer/ui/index.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
const { showSaveDialog, showOpenDialog } = require("./dialog");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
showSaveDialog,
|
||||||
|
showOpenDialog,
|
||||||
|
};
|
@ -197,7 +197,6 @@ export default defineComponent({
|
|||||||
.composer-card .command-item {
|
.composer-card .command-item {
|
||||||
transition: none !important;
|
transition: none !important;
|
||||||
transform: none !important;
|
transform: none !important;
|
||||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05) !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.command-item {
|
.command-item {
|
||||||
|
@ -113,7 +113,6 @@ export default defineComponent({
|
|||||||
* 变量模式stringify后,null变成'null', ''保持''
|
* 变量模式stringify后,null变成'null', ''保持''
|
||||||
*/
|
*/
|
||||||
const stringifiedArgvs = argvs.map((argv) => stringifyArgv(argv));
|
const stringifiedArgvs = argvs.map((argv) => stringifyArgv(argv));
|
||||||
|
|
||||||
/* 空值处理:
|
/* 空值处理:
|
||||||
* 1. 去掉 undefined,'', null
|
* 1. 去掉 undefined,'', null
|
||||||
* 2. varInput在字符串模式下,留空为'""',所以不会被处理
|
* 2. varInput在字符串模式下,留空为'""',所以不会被处理
|
||||||
|
@ -17,9 +17,11 @@
|
|||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
<VariableInput
|
<VariableInput
|
||||||
:model-value="item[key.value]"
|
:model-value="item[key.value] || key.defaultValue"
|
||||||
:label="key.label"
|
:label="key.label"
|
||||||
:no-icon="true"
|
:no-icon="true"
|
||||||
|
:placeholder="key.placeholder"
|
||||||
|
:options="key.options"
|
||||||
@update:model-value="
|
@update:model-value="
|
||||||
(val) => updateItemKeyValue(index, key.value, val)
|
(val) => updateItemKeyValue(index, key.value, val)
|
||||||
"
|
"
|
||||||
@ -32,6 +34,7 @@
|
|||||||
:model-value="item"
|
:model-value="item"
|
||||||
:label="`${label || '项目'} ${index + 1}`"
|
:label="`${label || '项目'} ${index + 1}`"
|
||||||
:icon="icon || 'code'"
|
:icon="icon || 'code'"
|
||||||
|
:placeholder="placeholder"
|
||||||
:options="{
|
:options="{
|
||||||
items: options.items,
|
items: options.items,
|
||||||
}"
|
}"
|
||||||
@ -95,9 +98,16 @@
|
|||||||
* @property {String} label - 输入框标签
|
* @property {String} label - 输入框标签
|
||||||
* @property {String} icon - 输入框图标
|
* @property {String} icon - 输入框图标
|
||||||
* @property {Object} options - 配置选项
|
* @property {Object} options - 配置选项
|
||||||
* @property {String[]} [options.keys] - 多键对象模式的键名列表
|
|
||||||
* @property {String[]} [options.items] - 下拉选择模式的选项列表
|
|
||||||
*
|
*
|
||||||
|
* @property {String[]} [options.keys] - 多键对象模式的键名列表
|
||||||
|
* @property {String[]} [options.keys.value] - 元素为对象时,对象的键名
|
||||||
|
* @property {String[]} [options.keys.label] - 对应varInput的label
|
||||||
|
* @property {String[]} [options.keys.placeholder] - 对应varInput的placeholder
|
||||||
|
* @property {String[]} [options.keys.defaultValue] - 对应varInput的defaultValue
|
||||||
|
* @property {Object} [options.keys.options] - 对应varInput的options
|
||||||
|
*
|
||||||
|
* @property {String[]} [options.items] - 下拉选择模式的选项列表
|
||||||
|
* @property {Object} [options.defaultValue] - 初始化时,默认的值,决定显示几个元素,对应元素内容
|
||||||
* @example
|
* @example
|
||||||
* // 基础数组
|
* // 基础数组
|
||||||
* [
|
* [
|
||||||
@ -106,6 +116,19 @@
|
|||||||
*
|
*
|
||||||
* // 多键对象数组
|
* // 多键对象数组
|
||||||
* options.keys = ['name', 'age', 'email']
|
* options.keys = ['name', 'age', 'email']
|
||||||
|
*
|
||||||
|
* options.keys= [
|
||||||
|
* {
|
||||||
|
* label: "姓名",
|
||||||
|
* value: "name",
|
||||||
|
* placeholder: "姓名",
|
||||||
|
* options: {
|
||||||
|
* items: ["张三", "李四", "王五"],
|
||||||
|
* multiSelect: true,
|
||||||
|
* },
|
||||||
|
* }
|
||||||
|
* ]
|
||||||
|
*
|
||||||
* [
|
* [
|
||||||
* {
|
* {
|
||||||
* name: newVarInputVal("str", "张三"),
|
* name: newVarInputVal("str", "张三"),
|
||||||
@ -159,6 +182,10 @@ export default defineComponent({
|
|||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({}),
|
default: () => ({}),
|
||||||
},
|
},
|
||||||
|
placeholder: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
emits: ["update:modelValue"],
|
emits: ["update:modelValue"],
|
||||||
computed: {
|
computed: {
|
||||||
@ -169,9 +196,9 @@ export default defineComponent({
|
|||||||
return (
|
return (
|
||||||
this.options?.keys?.map((key) => {
|
this.options?.keys?.map((key) => {
|
||||||
return {
|
return {
|
||||||
|
...key,
|
||||||
value: key.value || key,
|
value: key.value || key,
|
||||||
label: key.label || key,
|
label: key.label || key,
|
||||||
width: key.width,
|
|
||||||
};
|
};
|
||||||
}) || []
|
}) || []
|
||||||
);
|
);
|
||||||
|
@ -1,20 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="border-label" :class="{ collapsed }" :data-label="label">
|
<div class="border-label" :class="{ collapsed }" :data-label="label">
|
||||||
<div class="label-header row items-center" @click="toggleCollapse">
|
<div class="label-header row items-center" @click="toggleCollapse">
|
||||||
|
<q-icon
|
||||||
|
v-if="icon"
|
||||||
|
:name="icon"
|
||||||
|
size="16px"
|
||||||
|
class="collapse-icon"
|
||||||
|
/>
|
||||||
|
<div class="label-text">{{ label }}</div>
|
||||||
<q-icon
|
<q-icon
|
||||||
:name="collapsed ? 'expand_more' : 'expand_less'"
|
:name="collapsed ? 'expand_more' : 'expand_less'"
|
||||||
size="16px"
|
size="16px"
|
||||||
class="collapse-icon"
|
class="collapse-icon"
|
||||||
/>
|
/>
|
||||||
<div class="label-text row items-center">
|
|
||||||
<q-icon
|
|
||||||
v-if="icon"
|
|
||||||
:name="icon"
|
|
||||||
size="16px"
|
|
||||||
class="collapse-icon q-pl-sm"
|
|
||||||
/>
|
|
||||||
<div class="label-text">{{ label }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="content" :class="{ collapsed }">
|
<div class="content" :class="{ collapsed }">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
|
181
src/components/composer/common/CheckGroup.vue
Normal file
181
src/components/composer/common/CheckGroup.vue
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
<template>
|
||||||
|
<component
|
||||||
|
:is="!!label ? 'BorderLabel' : 'div'"
|
||||||
|
:label="label"
|
||||||
|
:icon="icon"
|
||||||
|
:model-value="isCollapse"
|
||||||
|
>
|
||||||
|
<div class="check-btn-group">
|
||||||
|
<q-btn
|
||||||
|
v-for="option in options"
|
||||||
|
:key="option.value"
|
||||||
|
:color="isSelected(option.value) ? 'primary' : 'grey-7'"
|
||||||
|
:flat="!isSelected(option.value)"
|
||||||
|
:outline="isSelected(option.value)"
|
||||||
|
dense
|
||||||
|
:class="[
|
||||||
|
'check-btn',
|
||||||
|
{ 'check-btn--selected': isSelected(option.value) },
|
||||||
|
]"
|
||||||
|
:style="{
|
||||||
|
flex: `1 0 ${100 / options.length}%`,
|
||||||
|
}"
|
||||||
|
@click="toggleOption(option.value)"
|
||||||
|
>
|
||||||
|
<template #default>
|
||||||
|
<div class="row items-center full-width">
|
||||||
|
<div class="check-btn-content">
|
||||||
|
<div class="check-btn-label">{{ option.label }}</div>
|
||||||
|
</div>
|
||||||
|
<q-icon
|
||||||
|
:name="
|
||||||
|
isSelected(option.value)
|
||||||
|
? 'check_circle'
|
||||||
|
: 'radio_button_unchecked'
|
||||||
|
"
|
||||||
|
size="14px"
|
||||||
|
class="q-ml-xs check-btn-icon"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<q-tooltip v-if="option.tooltip">{{ option.tooltip }}</q-tooltip>
|
||||||
|
</template>
|
||||||
|
</q-btn>
|
||||||
|
</div>
|
||||||
|
</component>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { defineComponent } from "vue";
|
||||||
|
import BorderLabel from "components/composer/common/BorderLabel.vue";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "CheckGroup",
|
||||||
|
components: { BorderLabel },
|
||||||
|
props: {
|
||||||
|
modelValue: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
|
validator: (options) =>
|
||||||
|
options.every((opt) => opt.label && opt.value !== undefined),
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
isCollapse: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emits: ["update:model-value"],
|
||||||
|
methods: {
|
||||||
|
isSelected(value) {
|
||||||
|
return this.modelValue.includes(value);
|
||||||
|
},
|
||||||
|
toggleOption(value) {
|
||||||
|
const newValue = [...this.modelValue];
|
||||||
|
const index = newValue.indexOf(value);
|
||||||
|
if (index === -1) {
|
||||||
|
newValue.push(value);
|
||||||
|
} else {
|
||||||
|
newValue.splice(index, 1);
|
||||||
|
}
|
||||||
|
this.$emit("update:model-value", newValue);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.check-btn-group {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 4px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-btn {
|
||||||
|
min-width: fit-content !important;
|
||||||
|
max-width: 100% !important;
|
||||||
|
height: auto !important;
|
||||||
|
min-height: 32px;
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 4px 12px;
|
||||||
|
border-radius: 4px !important;
|
||||||
|
transition: all 0.3s;
|
||||||
|
background-color: rgba(0, 0, 0, 0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-btn :deep(.q-btn__content) {
|
||||||
|
min-width: 0;
|
||||||
|
height: auto;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-btn-content {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-btn-label {
|
||||||
|
text-align: center;
|
||||||
|
line-height: 1.2;
|
||||||
|
word-break: break-word;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-btn-icon {
|
||||||
|
flex: none;
|
||||||
|
opacity: 0.8;
|
||||||
|
transition: all 0.3s;
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-btn--selected .check-btn-icon {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 移除按钮组默认的边框合并样式 */
|
||||||
|
.check-btn:hover {
|
||||||
|
opacity: 0.9;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
border-color: var(--q-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 暗色模式适配 */
|
||||||
|
.body--dark .check-btn {
|
||||||
|
background-color: rgba(255, 255, 255, 0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-btn--selected {
|
||||||
|
background-color: transparent !important;
|
||||||
|
border-color: var(--q-primary) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-btn.q-btn--flat {
|
||||||
|
color: var(--q-primary);
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.body--dark .check-btn.q-btn--flat {
|
||||||
|
color: rgba(255, 255, 255, 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 选中状态的按钮样式 */
|
||||||
|
.check-btn.q-btn--outline {
|
||||||
|
opacity: 1;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
</style>
|
@ -152,6 +152,10 @@ export default defineComponent({
|
|||||||
type: String,
|
type: String,
|
||||||
default: "",
|
default: "",
|
||||||
},
|
},
|
||||||
|
placeholder: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
isCollapse: {
|
isCollapse: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true,
|
||||||
|
@ -10,51 +10,37 @@
|
|||||||
v-if="config.type === 'controlInput'"
|
v-if="config.type === 'controlInput'"
|
||||||
:model-value="values[index]"
|
:model-value="values[index]"
|
||||||
@update:model-value="$emit('update', index, $event)"
|
@update:model-value="$emit('update', index, $event)"
|
||||||
:label="config.label"
|
v-bind="config"
|
||||||
:placeholder="config.placeholder"
|
|
||||||
:icon="config.icon"
|
|
||||||
/>
|
/>
|
||||||
<VariableInput
|
<VariableInput
|
||||||
v-else-if="config.type === 'varInput'"
|
v-else-if="config.type === 'varInput'"
|
||||||
:model-value="values[index]"
|
:model-value="values[index]"
|
||||||
@update:model-value="$emit('update', index, $event)"
|
@update:model-value="$emit('update', index, $event)"
|
||||||
:label="config.label"
|
v-bind="config"
|
||||||
:placeholder="config.placeholder"
|
|
||||||
:icon="config.icon"
|
|
||||||
:options="config.options"
|
|
||||||
/>
|
/>
|
||||||
<NumberInput
|
<NumberInput
|
||||||
v-else-if="config.type === 'numInput'"
|
v-else-if="config.type === 'numInput'"
|
||||||
:model-value="values[index]"
|
:model-value="values[index]"
|
||||||
@update:model-value="$emit('update', index, $event)"
|
@update:model-value="$emit('update', index, $event)"
|
||||||
:label="config.label"
|
v-bind="config"
|
||||||
:icon="config.icon"
|
|
||||||
:placeholder="config.placeholder"
|
|
||||||
/>
|
/>
|
||||||
<ArrayEditor
|
<ArrayEditor
|
||||||
v-else-if="config.type === 'arrayEditor'"
|
v-else-if="config.type === 'arrayEditor'"
|
||||||
:model-value="values[index]"
|
:model-value="values[index]"
|
||||||
@update:model-value="$emit('update', index, $event)"
|
@update:model-value="$emit('update', index, $event)"
|
||||||
:label="config.label"
|
v-bind="config"
|
||||||
:options="config.options"
|
|
||||||
:icon="config.icon"
|
|
||||||
:is-collapse="config.isCollapse"
|
|
||||||
/>
|
/>
|
||||||
<DictEditor
|
<DictEditor
|
||||||
v-else-if="config.type === 'dictEditor'"
|
v-else-if="config.type === 'dictEditor'"
|
||||||
:model-value="values[index]"
|
:model-value="values[index]"
|
||||||
@update:model-value="$emit('update', index, $event)"
|
@update:model-value="$emit('update', index, $event)"
|
||||||
:label="config.label"
|
v-bind="config"
|
||||||
:options="config.options"
|
|
||||||
:icon="config.icon"
|
|
||||||
:is-collapse="config.isCollapse"
|
|
||||||
/>
|
/>
|
||||||
<q-toggle
|
<q-toggle
|
||||||
v-else-if="config.type === 'switch'"
|
v-else-if="config.type === 'switch'"
|
||||||
:model-value="values[index]"
|
:model-value="values[index]"
|
||||||
@update:model-value="$emit('update', index, $event)"
|
@update:model-value="$emit('update', index, $event)"
|
||||||
:label="config.label"
|
v-bind="config"
|
||||||
:icon="config.icon"
|
|
||||||
/>
|
/>
|
||||||
<q-select
|
<q-select
|
||||||
v-else-if="config.type === 'select'"
|
v-else-if="config.type === 'select'"
|
||||||
@ -63,7 +49,7 @@
|
|||||||
map-options
|
map-options
|
||||||
:model-value="values[index]"
|
:model-value="values[index]"
|
||||||
@update:model-value="$emit('update', index, $event)"
|
@update:model-value="$emit('update', index, $event)"
|
||||||
:options="config.options"
|
v-bind="config"
|
||||||
>
|
>
|
||||||
<template v-slot:prepend>
|
<template v-slot:prepend>
|
||||||
<q-icon :name="config.icon || 'code'" />
|
<q-icon :name="config.icon || 'code'" />
|
||||||
@ -74,9 +60,7 @@
|
|||||||
filled
|
filled
|
||||||
:model-value="values[index]"
|
:model-value="values[index]"
|
||||||
@update:model-value="$emit('update', index, $event)"
|
@update:model-value="$emit('update', index, $event)"
|
||||||
:label="config.label"
|
v-bind="config"
|
||||||
:icon="config.icon"
|
|
||||||
:placeholder="config.placeholder"
|
|
||||||
>
|
>
|
||||||
<template v-slot:prepend>
|
<template v-slot:prepend>
|
||||||
<q-icon :name="config.icon || 'code'" />
|
<q-icon :name="config.icon || 'code'" />
|
||||||
@ -86,14 +70,19 @@
|
|||||||
v-else-if="config.type === 'checkbox'"
|
v-else-if="config.type === 'checkbox'"
|
||||||
:model-value="values[index]"
|
:model-value="values[index]"
|
||||||
@update:model-value="$emit('update', index, $event)"
|
@update:model-value="$emit('update', index, $event)"
|
||||||
:label="config.label"
|
v-bind="config"
|
||||||
:icon="config.icon"
|
|
||||||
/>
|
/>
|
||||||
<ButtonGroup
|
<ButtonGroup
|
||||||
v-else-if="config.type === 'buttonGroup'"
|
v-else-if="config.type === 'buttonGroup'"
|
||||||
:model-value="values[index]"
|
:model-value="values[index]"
|
||||||
@update:model-value="$emit('update', index, $event)"
|
@update:model-value="$emit('update', index, $event)"
|
||||||
:options="config.options"
|
v-bind="config"
|
||||||
|
/>
|
||||||
|
<CheckGroup
|
||||||
|
v-else-if="config.type === 'checkGroup'"
|
||||||
|
:model-value="values[index]"
|
||||||
|
@update:model-value="$emit('update', index, $event)"
|
||||||
|
v-bind="config"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -107,6 +96,7 @@ import ArrayEditor from "./ArrayEditor.vue";
|
|||||||
import DictEditor from "./DictEditor.vue";
|
import DictEditor from "./DictEditor.vue";
|
||||||
import ButtonGroup from "./ButtonGroup.vue";
|
import ButtonGroup from "./ButtonGroup.vue";
|
||||||
import ControlInput from "./ControlInput.vue";
|
import ControlInput from "./ControlInput.vue";
|
||||||
|
import CheckGroup from "./CheckGroup.vue";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 参数输入组件
|
* 参数输入组件
|
||||||
@ -128,6 +118,7 @@ export default defineComponent({
|
|||||||
DictEditor,
|
DictEditor,
|
||||||
ButtonGroup,
|
ButtonGroup,
|
||||||
ControlInput,
|
ControlInput,
|
||||||
|
CheckGroup,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
configs: {
|
configs: {
|
||||||
|
@ -45,18 +45,47 @@
|
|||||||
class="options-dropdown prepend-btn"
|
class="options-dropdown prepend-btn"
|
||||||
>
|
>
|
||||||
<q-list class="options-item-list">
|
<q-list class="options-item-list">
|
||||||
<q-item
|
<template v-if="options.multiSelect">
|
||||||
v-for="item in normalizedItems"
|
<q-item
|
||||||
:key="getItemValue(item)"
|
v-for="item in normalizedItems"
|
||||||
clickable
|
:key="getItemValue(item)"
|
||||||
v-close-popup
|
clickable
|
||||||
@click="selectItem(item)"
|
class="option-item"
|
||||||
class="option-item"
|
@click="toggleSelectItem(item)"
|
||||||
>
|
>
|
||||||
<q-item-section>
|
<q-checkbox
|
||||||
{{ getItemLabel(item) }}
|
size="xs"
|
||||||
</q-item-section>
|
:model-value="isItemSelected(item)"
|
||||||
</q-item>
|
@update:model-value="toggleSelectItem(item)"
|
||||||
|
/>
|
||||||
|
<div class="option-item-label">{{ getItemLabel(item) }}</div>
|
||||||
|
</q-item>
|
||||||
|
<q-separator />
|
||||||
|
<q-item
|
||||||
|
clickable
|
||||||
|
class="option-item"
|
||||||
|
@click="confirmMultiSelect"
|
||||||
|
v-close-popup
|
||||||
|
>
|
||||||
|
<q-item-section class="text-primary">
|
||||||
|
确定 (已选择 {{ selectedItems.length }} 项)
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<q-item
|
||||||
|
v-for="item in normalizedItems"
|
||||||
|
:key="getItemValue(item)"
|
||||||
|
clickable
|
||||||
|
v-close-popup
|
||||||
|
@click="selectItem(item)"
|
||||||
|
class="option-item"
|
||||||
|
>
|
||||||
|
<q-item-section>
|
||||||
|
{{ getItemLabel(item) }}
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</template>
|
||||||
</q-list>
|
</q-list>
|
||||||
</q-btn-dropdown>
|
</q-btn-dropdown>
|
||||||
<q-btn
|
<q-btn
|
||||||
@ -147,8 +176,11 @@ import { newVarInputVal } from "js/composer/varInputValManager";
|
|||||||
* @property {Object} modelValue - 输入框的值对象
|
* @property {Object} modelValue - 输入框的值对象
|
||||||
* @property {String} label - 输入框标签
|
* @property {String} label - 输入框标签
|
||||||
* @property {String} icon - 输入框图标
|
* @property {String} icon - 输入框图标
|
||||||
|
*
|
||||||
* @property {Object} [options] - 可选的配置对象
|
* @property {Object} [options] - 可选的配置对象
|
||||||
* @property {Array} [options.items] - 选项列表
|
* @property {Array} [options.items] - 选项列表,默认单选,选中后,插入值且设置为字符模式
|
||||||
|
* @property {Boolean} [options.multiSelect] - 选项列表支持多选,选中后,插入选择的数组且设置为变量模式
|
||||||
|
*
|
||||||
* @property {Boolean} [options.dialog] - 是否显示文件选择对话框
|
* @property {Boolean} [options.dialog] - 是否显示文件选择对话框
|
||||||
* @property {Object} [options.dialog] - 文件选择对话框配置
|
* @property {Object} [options.dialog] - 文件选择对话框配置
|
||||||
* @property {String} [options.dialog.type] - 对话框类型,open 或 save
|
* @property {String} [options.dialog.type] - 对话框类型,open 或 save
|
||||||
@ -213,6 +245,7 @@ export default defineComponent({
|
|||||||
return {
|
return {
|
||||||
selectedVariable: null,
|
selectedVariable: null,
|
||||||
variables: [],
|
variables: [],
|
||||||
|
selectedItems: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -290,8 +323,12 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
selectItem(option) {
|
selectItem(option) {
|
||||||
const value = this.getItemValue(option);
|
if (this.options.multiSelect) {
|
||||||
this.$emit("update:modelValue", newVarInputVal("str", value));
|
this.toggleSelectItem(option);
|
||||||
|
} else {
|
||||||
|
const value = this.getItemValue(option);
|
||||||
|
this.$emit("update:modelValue", newVarInputVal("str", value));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
handleFileOpen(dialog) {
|
handleFileOpen(dialog) {
|
||||||
let { type, options } = window.lodashM.cloneDeep(dialog);
|
let { type, options } = window.lodashM.cloneDeep(dialog);
|
||||||
@ -310,6 +347,40 @@ export default defineComponent({
|
|||||||
this.$emit("update:modelValue", newVarInputVal("str", file));
|
this.$emit("update:modelValue", newVarInputVal("str", file));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 新增:判断选项是否被选中
|
||||||
|
isItemSelected(item) {
|
||||||
|
return this.selectedItems.includes(this.getItemValue(item));
|
||||||
|
},
|
||||||
|
|
||||||
|
// 新增:切换选项选中状态
|
||||||
|
toggleSelectItem(item) {
|
||||||
|
const value = this.getItemValue(item);
|
||||||
|
const index = this.selectedItems.indexOf(value);
|
||||||
|
if (index === -1) {
|
||||||
|
this.selectedItems.push(value);
|
||||||
|
} else {
|
||||||
|
this.selectedItems.splice(index, 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 新增:确认多选
|
||||||
|
confirmMultiSelect() {
|
||||||
|
if (this.selectedItems.length === 0) return;
|
||||||
|
this.$emit(
|
||||||
|
"update:modelValue",
|
||||||
|
newVarInputVal(
|
||||||
|
"var",
|
||||||
|
JSON.stringify(this.selectedItems).replace(/,/g, ", ")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
this.selectedItems = []; // 清空选择
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// 在组件销毁时清空选择
|
||||||
|
beforeUnmount() {
|
||||||
|
this.selectedItems = [];
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@ -333,7 +404,6 @@ export default defineComponent({
|
|||||||
|
|
||||||
.prepend-btn:hover {
|
.prepend-btn:hover {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transform: scale(1.05);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.clear-btn:hover {
|
.clear-btn:hover {
|
||||||
@ -398,6 +468,11 @@ export default defineComponent({
|
|||||||
background-color: var(--q-primary-opacity-10);
|
background-color: var(--q-primary-opacity-10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.option-item-label {
|
||||||
|
text-align: center;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* 暗色模式适配 */
|
/* 暗色模式适配 */
|
||||||
.body--dark .option-item:hover {
|
.body--dark .option-item:hover {
|
||||||
background-color: rgba(255, 255, 255, 0.1);
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
@ -413,4 +488,15 @@ export default defineComponent({
|
|||||||
.empty-variables-tip:hover {
|
.empty-variables-tip:hover {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 多选确认按钮样式 */
|
||||||
|
.option-item.text-primary {
|
||||||
|
justify-content: center;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 多选项样式 */
|
||||||
|
.option-item .q-checkbox {
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -120,7 +120,7 @@
|
|||||||
:model-value="argvs.options.env"
|
:model-value="argvs.options.env"
|
||||||
@update:model-value="(val) => updateArgvs('options.env', val)"
|
@update:model-value="(val) => updateArgvs('options.env', val)"
|
||||||
label="环境变量"
|
label="环境变量"
|
||||||
icon="environment"
|
icon="attach_money"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -158,5 +158,110 @@ export const uiCommands = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
value: "quickcomposer.ui.showOpenDialog",
|
||||||
|
label: "文件选择框",
|
||||||
|
desc: "显示一个文件选择框,返回选择的文件路径",
|
||||||
|
outputVariable: "filePaths",
|
||||||
|
saveOutput: true,
|
||||||
|
config: [
|
||||||
|
{
|
||||||
|
label: "标题",
|
||||||
|
type: "varInput",
|
||||||
|
defaultValue: newVarInputVal("str", "请选择文件"),
|
||||||
|
width: 6,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "默认路径",
|
||||||
|
type: "varInput",
|
||||||
|
defaultValue: newVarInputVal("str"),
|
||||||
|
width: 6,
|
||||||
|
placeholder: "默认打开的路径",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "按钮文本",
|
||||||
|
type: "varInput",
|
||||||
|
defaultValue: newVarInputVal("str", "选择"),
|
||||||
|
width: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "提示信息",
|
||||||
|
type: "varInput",
|
||||||
|
defaultValue: newVarInputVal("str"),
|
||||||
|
width: 3,
|
||||||
|
placeholder: "对话框底部的提示信息",
|
||||||
|
defaultValue: newVarInputVal("str", "请选择"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "扩展名",
|
||||||
|
type: "varInput",
|
||||||
|
width: 6,
|
||||||
|
options: {
|
||||||
|
items: ["*", "jpg", "png", "gif", "txt", "json", "exe"],
|
||||||
|
multiSelect: true,
|
||||||
|
},
|
||||||
|
defaultValue: newVarInputVal("var", '["*"]'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
subCommands: [
|
||||||
|
{
|
||||||
|
value: "quickcomposer.ui.showOpenDialog",
|
||||||
|
label: "打开文件对话框",
|
||||||
|
desc: "打开文件对话框",
|
||||||
|
icon: "folder_open",
|
||||||
|
config: [
|
||||||
|
{
|
||||||
|
label: "选择选项",
|
||||||
|
type: "checkGroup",
|
||||||
|
icon: "settings",
|
||||||
|
width: 12,
|
||||||
|
options: [
|
||||||
|
{ label: "选择文件", value: "openFile" },
|
||||||
|
{ label: "选择文件夹", value: "openDirectory" },
|
||||||
|
{ label: "允许多选", value: "multiSelections" },
|
||||||
|
{ label: "显示隐藏文件", value: "showHiddenFiles" },
|
||||||
|
{ label: "提示新建路径(Win)", value: "promptToCreate" },
|
||||||
|
{ label: "不添加到最近(Win)", value: "dontAddToRecent" },
|
||||||
|
{ label: "允许创建文件夹(Mac)", value: "createDirectory" },
|
||||||
|
{ label: "不解析符号链接(Mac)", value: "noResolveAliases" },
|
||||||
|
{
|
||||||
|
label: "将.App作为目录(Mac)",
|
||||||
|
value: "treatPackageAsDirectory",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
defaultValue: ["openFile", "showHiddenFiles"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "quickcomposer.ui.showSaveDialog",
|
||||||
|
label: "保存文件对话框",
|
||||||
|
desc: "保存文件对话框",
|
||||||
|
icon: "save",
|
||||||
|
config: [
|
||||||
|
{
|
||||||
|
label: "选择选项",
|
||||||
|
type: "checkGroup",
|
||||||
|
icon: "settings",
|
||||||
|
width: 12,
|
||||||
|
options: [
|
||||||
|
{ label: "显示隐藏文件", value: "showHiddenFiles" },
|
||||||
|
{ label: "允许创建文件夹(Mac)", value: "createDirectory" },
|
||||||
|
{
|
||||||
|
label: "将.App作为目录(Mac)",
|
||||||
|
value: "treatPackageAsDirectory",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "显示覆盖确认(Linux)",
|
||||||
|
value: "showOverwriteConfirmation",
|
||||||
|
},
|
||||||
|
{ label: "不添加到最近(Win)", value: "dontAddToRecent" },
|
||||||
|
],
|
||||||
|
defaultValue: ["showHiddenFiles"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
@ -81,7 +81,7 @@ const stringifyObject = (jsonObj) => {
|
|||||||
return stringifyVarInputVal(jsonObj);
|
return stringifyVarInputVal(jsonObj);
|
||||||
}
|
}
|
||||||
if (jsonObj instanceof Array) {
|
if (jsonObj instanceof Array) {
|
||||||
return `[${jsonObj.map((item) => stringifyObject(item)).join(",")}]`;
|
return `[${jsonObj.map((item) => stringifyArgv(item)).join(",")}]`;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return processObject(jsonObj);
|
return processObject(jsonObj);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user