更新Varinput支持下拉选项

This commit is contained in:
fofolee 2025-01-06 10:00:21 +08:00
parent 2dbd6f0c50
commit b8d48cb102
3 changed files with 157 additions and 136 deletions

View File

@ -90,27 +90,11 @@
:model-value="argvs.headers['User-Agent']" :model-value="argvs.headers['User-Agent']"
@update:model-value="updateArgvs('headers.User-Agent', $event)" @update:model-value="updateArgvs('headers.User-Agent', $event)"
label="User Agent" label="User Agent"
:options="userAgentOptions"
icon="devices" icon="devices"
class="col-grow" class="col-grow"
/> />
</div> </div>
<div class="col-auto flex items-center">
<q-btn-dropdown flat dense dropdown-icon="menu">
<q-list>
<q-item
v-for="ua in userAgentOptions"
:key="ua.value"
clickable
v-close-popup
@click="setUserAgent(ua.value)"
>
<q-item-section>
<q-item-label>{{ ua.label }}</q-item-label>
</q-item-section>
</q-item>
</q-list>
</q-btn-dropdown>
</div>
<div class="col-12"> <div class="col-12">
<DictEditor <DictEditor
@ -168,10 +152,7 @@ import { defineComponent } from "vue";
import VariableInput from "components/composer/ui/VariableInput.vue"; import VariableInput from "components/composer/ui/VariableInput.vue";
import NumberInput from "components/composer/ui/NumberInput.vue"; import NumberInput from "components/composer/ui/NumberInput.vue";
import DictEditor from "components/composer/ui/DictEditor.vue"; import DictEditor from "components/composer/ui/DictEditor.vue";
import { import { stringifyArgv, parseFunction } from "js/composer/formatString";
stringifyArgv,
parseFunction,
} from "js/composer/formatString";
import { import {
userAgent, userAgent,
commonHeaders, commonHeaders,
@ -471,11 +452,9 @@ export default defineComponent({
? `, ${stringifyArgv(restConfig)}` ? `, ${stringifyArgv(restConfig)}`
: ""; : "";
return `${ return `${this.modelValue.value}.${method.toLowerCase()}(${stringifyArgv(
this.modelValue.value url
}.${method.toLowerCase()}(${stringifyArgv(url)}${ )}${this.hasRequestData ? `, ${stringifyArgv(data)}` : ""}${configStr})`;
this.hasRequestData ? `, ${stringifyArgv(data)}` : ""
}${configStr})`;
}, },
updateArgvs(key, value) { updateArgvs(key, value) {
const argvs = { ...this.argvs }; const argvs = { ...this.argvs };

View File

@ -1,11 +1,11 @@
<template> <template>
<div class="array-editor"> <div class="array-editor">
<div v-for="(item, index) in items" :key="index" class="row items-center"> <div v-for="(item, index) in items" :key="index" class="row items-center">
<template v-if="options?.keys"> <template v-if="optionsKeys">
<div <div
v-for="key in options.keys" v-for="key in optionsKeys"
:key="key" :key="key"
:class="['col', options.keys.length > 1 ? 'q-pr-sm' : '']" :class="['col', optionsKeys.length > 1 ? 'q-pr-sm' : '']"
> >
<VariableInput <VariableInput
:model-value="item[key]" :model-value="item[key]"
@ -17,34 +17,13 @@
</template> </template>
<template v-else> <template v-else>
<div class="col"> <div class="col">
<template v-if="options?.items"> <VariableInput
<q-select :model-value="item"
:model-value="item.value" :label="`${label || '项目'} ${index + 1}`"
:options="filterOptions" :icon="icon || 'code'"
:label="`${label || '项目'} ${index + 1}`" :options="options"
dense @update:model-value="(val) => updateItemValue(index, val)"
filled />
use-input
input-debounce="0"
:hide-selected="!!inputValue"
@filter="filterFn"
@update:model-value="(val) => handleSelect(val, index)"
@input-value="(val) => handleInput(val, index)"
@blur="handleBlur"
>
<template v-slot:prepend>
<q-icon :name="icon || 'code'" />
</template>
</q-select>
</template>
<template v-else>
<VariableInput
:model-value="item"
:label="`${label || '项目'} ${index + 1}`"
:icon="icon || 'code'"
@update:model-value="(val) => updateItemValue(index, val)"
/>
</template>
</div> </div>
</template> </template>
<div class="col-auto"> <div class="col-auto">
@ -102,8 +81,8 @@
* @property {String} label - 输入框标签 * @property {String} label - 输入框标签
* @property {String} icon - 输入框图标 * @property {String} icon - 输入框图标
* @property {Object} options - 配置选项 * @property {Object} options - 配置选项
* @property {String[]} [options.keys] - 多键对象模式的键名列表 * @property {String[]} [optionsKeys] - 多键对象模式的键名列表
* @property {String[]} [options.items] - 下拉选择模式的选项列表 * @property {String[]} [options] - 下拉选择模式的选项列表
* *
* @example * @example
* // * //
@ -116,7 +95,7 @@
* ] * ]
* *
* // * //
* options.keys = ['name', 'age', 'email'] * optionsKeys = ['name', 'age', 'email']
* [ * [
* { * {
* name: { value: "张三", isString: true, __varInputVal__: true }, * name: { value: "张三", isString: true, __varInputVal__: true },
@ -126,7 +105,7 @@
* ] * ]
* *
* // * //
* options.items = ['选项1', '选项2', '选项3'] * options = ['选项1', '选项2', '选项3']
* [ * [
* { * {
* value: "选项1", * value: "选项1",
@ -157,7 +136,11 @@ export default defineComponent({
default: "", default: "",
}, },
options: { options: {
type: Object, type: Array,
default: null,
},
optionsKeys: {
type: Array,
default: null, default: null,
}, },
}, },
@ -166,10 +149,6 @@ export default defineComponent({
return { return {
// //
localItems: this.initializeItems(), localItems: this.initializeItems(),
//
filterOptions: this.options?.items || [],
//
inputValue: "",
}; };
}, },
computed: { computed: {
@ -191,7 +170,7 @@ export default defineComponent({
if (this.options?.keys) { if (this.options?.keys) {
const item = {}; const item = {};
this.options.keys.forEach((key) => { this.optionsKeys.forEach((key) => {
item[key] = { item[key] = {
value: "", value: "",
isString: false, isString: false,
@ -214,9 +193,9 @@ export default defineComponent({
* 根据配置创建相应的数据结构 * 根据配置创建相应的数据结构
*/ */
addItem() { addItem() {
if (this.options?.keys) { if (this.optionsKeys) {
const newItem = {}; const newItem = {};
this.options.keys.forEach((key) => { this.optionsKeys.forEach((key) => {
newItem[key] = { newItem[key] = {
value: "", value: "",
isString: false, isString: false,
@ -243,9 +222,9 @@ export default defineComponent({
const newItems = [...this.items]; const newItems = [...this.items];
newItems.splice(index, 1); newItems.splice(index, 1);
if (newItems.length === 0) { if (newItems.length === 0) {
if (this.options?.keys) { if (this.optionsKeys) {
const newItem = {}; const newItem = {};
this.options.keys.forEach((key) => { this.optionsKeys.forEach((key) => {
newItem[key] = { newItem[key] = {
value: "", value: "",
isString: false, isString: false,
@ -282,59 +261,6 @@ export default defineComponent({
}; };
this.items = newItems; this.items = newItems;
}, },
/**
* 选项模式下的输入处理
* 当输入的值不在选项中时创建新值
*/
handleInput(val, index) {
this.inputValue = val;
if (val && !this.filterOptions.includes(val)) {
const newItems = [...this.items];
newItems[index] = {
value: val,
isString: false,
__varInputVal__: true,
};
this.items = newItems;
}
},
/**
* 选项模式下的选择处理
*/
handleSelect(val, index) {
this.inputValue = "";
const newItems = [...this.items];
newItems[index] = {
value: val,
isString: false,
__varInputVal__: true,
};
this.items = newItems;
},
/**
* 选项模式下的失焦处理
*/
handleBlur() {
this.inputValue = "";
},
/**
* 选项模式下的过滤处理
* 根据输入值过滤可选项
*/
filterFn(val, update) {
if (!this.options?.items) return;
update(() => {
if (val === "") {
this.filterOptions = this.options.items;
} else {
const needle = val.toLowerCase();
this.filterOptions = this.options.items.filter(
(v) => v.toLowerCase().indexOf(needle) > -1
);
}
});
},
}, },
}); });
</script> </script>

View File

@ -26,7 +26,7 @@
:icon="isString ? 'format_quote' : 'data_object'" :icon="isString ? 'format_quote' : 'data_object'"
size="sm" size="sm"
class="string-toggle" class="string-toggle"
@click="isString = !isString" @click="toggleType"
v-if="!hasSelectedVariable" v-if="!hasSelectedVariable"
> >
<q-tooltip>{{ <q-tooltip>{{
@ -35,7 +35,32 @@
: "当前类型是:变量、数字、表达式等,点击切换" : "当前类型是:变量、数字、表达式等,点击切换"
}}</q-tooltip> }}</q-tooltip>
</q-btn> </q-btn>
<!-- 选项下拉按钮 -->
<q-btn-dropdown
v-if="options && !hasSelectedVariable"
flat
dense
size="sm"
dropdown-icon="menu"
no-icon-animation
class="options-dropdown"
>
<q-list class="options-list">
<q-item
v-for="option in normalizedOptions"
:key="getOptionValue(option)"
clickable
v-close-popup
@click="selectOption(option)"
class="option-item"
>
<q-item-section>
{{ getOptionLabel(option) }}
</q-item-section>
</q-item>
</q-list>
</q-btn-dropdown>
<!-- 变量选择下拉 -->
<q-btn-dropdown <q-btn-dropdown
flat flat
dense dense
@ -93,6 +118,7 @@ import { defineComponent, inject } from "vue";
* @property {Object} modelValue - 输入框的值对象 * @property {Object} modelValue - 输入框的值对象
* @property {String} label - 输入框标签 * @property {String} label - 输入框标签
* @property {String} icon - 输入框图标 * @property {String} icon - 输入框图标
* @property {String[]} [options] - 可选的下拉选项列表
* *
* @example * @example
* // modelValue * // modelValue
@ -117,6 +143,20 @@ export default defineComponent({
}, },
label: String, label: String,
icon: String, icon: String,
options: {
type: Array,
default: null,
validator(value) {
if (!value) return true;
//
return value.every((item) => {
return (
typeof item === "string" || // ["xxx", "yyy"]
(typeof item === "object" && "label" in item && "value" in item) // [{label: "xxx", value: "yyy"}]
);
});
},
},
}, },
emits: ["update:modelValue"], emits: ["update:modelValue"],
@ -137,6 +177,18 @@ export default defineComponent({
return this.selectedVariable !== null; return this.selectedVariable !== null;
}, },
isString: {
get() {
return this.modelValue.isString;
},
set(value) {
this.$emit("update:modelValue", {
...this.modelValue,
isString: value,
});
},
},
inputValue: { inputValue: {
get() { get() {
return this.modelValue.value; return this.modelValue.value;
@ -148,16 +200,17 @@ export default defineComponent({
}); });
}, },
}, },
isString: {
get() { //
return this.modelValue.isString; normalizedOptions() {
}, console.log(this.options);
set(value) { if (!this.options) return [];
this.$emit("update:modelValue", { return this.options.map((option) => {
...this.modelValue, if (typeof option === "string") {
isString: value, return { label: option, value: option };
}); }
}, return option;
});
}, },
}, },
@ -183,6 +236,31 @@ export default defineComponent({
__varInputVal__: true, __varInputVal__: true,
}); });
}, },
//
toggleType() {
this.$emit("update:modelValue", {
...this.modelValue,
isString: !this.modelValue.isString,
});
},
getOptionLabel(option) {
return typeof option === "string" ? option : option.label;
},
getOptionValue(option) {
return typeof option === "string" ? option : option.value;
},
selectOption(option) {
const value = this.getOptionValue(option);
this.$emit("update:modelValue", {
value,
isString: true,
__varInputVal__: true,
});
},
}, },
}); });
</script> </script>
@ -274,4 +352,42 @@ export default defineComponent({
transform: scale(1.1); transform: scale(1.1);
color: var(--q-negative); color: var(--q-negative);
} }
/* 选项下拉框样式 */
.options-dropdown {
min-width: 32px;
padding: 4px;
opacity: 0.8;
transition: all 0.3s ease;
margin-left: 4px;
}
.options-dropdown:hover {
opacity: 1;
transform: scale(1.05);
}
.options-list {
min-width: 120px;
padding: 4px;
}
.option-item {
border-radius: 4px;
padding: 0px 16px;
transition: all 0.3s ease;
min-height: 40px;
font-size: 12px;
display: flex;
align-items: center;
}
.option-item:hover {
background: var(--q-primary-opacity-10);
}
/* 暗色模式适配 */
.body--dark .option-item:hover {
background: rgba(255, 255, 255, 0.1);
}
</style> </style>