更新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']"
@update:model-value="updateArgvs('headers.User-Agent', $event)"
label="User Agent"
:options="userAgentOptions"
icon="devices"
class="col-grow"
/>
</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">
<DictEditor
@ -168,10 +152,7 @@ import { defineComponent } from "vue";
import VariableInput from "components/composer/ui/VariableInput.vue";
import NumberInput from "components/composer/ui/NumberInput.vue";
import DictEditor from "components/composer/ui/DictEditor.vue";
import {
stringifyArgv,
parseFunction,
} from "js/composer/formatString";
import { stringifyArgv, parseFunction } from "js/composer/formatString";
import {
userAgent,
commonHeaders,
@ -471,11 +452,9 @@ export default defineComponent({
? `, ${stringifyArgv(restConfig)}`
: "";
return `${
this.modelValue.value
}.${method.toLowerCase()}(${stringifyArgv(url)}${
this.hasRequestData ? `, ${stringifyArgv(data)}` : ""
}${configStr})`;
return `${this.modelValue.value}.${method.toLowerCase()}(${stringifyArgv(
url
)}${this.hasRequestData ? `, ${stringifyArgv(data)}` : ""}${configStr})`;
},
updateArgvs(key, value) {
const argvs = { ...this.argvs };

View File

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

View File

@ -26,7 +26,7 @@
:icon="isString ? 'format_quote' : 'data_object'"
size="sm"
class="string-toggle"
@click="isString = !isString"
@click="toggleType"
v-if="!hasSelectedVariable"
>
<q-tooltip>{{
@ -35,7 +35,32 @@
: "当前类型是:变量、数字、表达式等,点击切换"
}}</q-tooltip>
</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
flat
dense
@ -93,6 +118,7 @@ import { defineComponent, inject } from "vue";
* @property {Object} modelValue - 输入框的值对象
* @property {String} label - 输入框标签
* @property {String} icon - 输入框图标
* @property {String[]} [options] - 可选的下拉选项列表
*
* @example
* // modelValue
@ -117,6 +143,20 @@ export default defineComponent({
},
label: 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"],
@ -137,6 +177,18 @@ export default defineComponent({
return this.selectedVariable !== null;
},
isString: {
get() {
return this.modelValue.isString;
},
set(value) {
this.$emit("update:modelValue", {
...this.modelValue,
isString: value,
});
},
},
inputValue: {
get() {
return this.modelValue.value;
@ -148,16 +200,17 @@ export default defineComponent({
});
},
},
isString: {
get() {
return this.modelValue.isString;
},
set(value) {
this.$emit("update:modelValue", {
...this.modelValue,
isString: value,
});
},
//
normalizedOptions() {
console.log(this.options);
if (!this.options) return [];
return this.options.map((option) => {
if (typeof option === "string") {
return { label: option, value: option };
}
return option;
});
},
},
@ -183,6 +236,31 @@ export default defineComponent({
__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>
@ -274,4 +352,42 @@ export default defineComponent({
transform: scale(1.1);
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>