feat(公共): 筛选面板组件调整

This commit is contained in:
xinxin.wu 2023-09-28 12:30:52 +08:00 committed by fit2-zhao
parent 4ee10e11dc
commit 7a191e2c40
6 changed files with 143 additions and 60 deletions

View File

@ -1,4 +1,5 @@
import { OPERATORS } from './operator'; import { OPERATORS } from './operator';
import type { SearchKeyType } from './type';
// eslint-disable-next-line no-shadow // eslint-disable-next-line no-shadow
export enum CaseKeyEnum { export enum CaseKeyEnum {
@ -17,7 +18,7 @@ export enum CaseKeyEnum {
} }
// 名称 // 名称
export const NAME = { export const NAME: SearchKeyType = {
key: CaseKeyEnum.NAME, // 对应字段key key: CaseKeyEnum.NAME, // 对应字段key
type: 'a-input', // Vue控件名称 type: 'a-input', // Vue控件名称
label: '显示名称', // 显示名称 label: '显示名称', // 显示名称
@ -28,7 +29,7 @@ export const NAME = {
}; };
// 标签 // 标签
export const TAGS = { export const TAGS: SearchKeyType = {
key: CaseKeyEnum.TAGS, key: CaseKeyEnum.TAGS,
type: 'a-input', type: 'a-input',
label: '标签', label: '标签',
@ -39,7 +40,7 @@ export const TAGS = {
}; };
// 所属模块 // 所属模块
export const MODULE = { export const MODULE: SearchKeyType = {
key: 'module', key: 'module',
type: 'a-tree-select', type: 'a-tree-select',
label: '所属模块', label: '所属模块',
@ -50,23 +51,27 @@ export const MODULE = {
}; };
// 创建时间 // 创建时间
export const CREATE_TIME = { export const CREATE_TIME: SearchKeyType = {
key: CaseKeyEnum.CREATE_TIME, key: CaseKeyEnum.CREATE_TIME,
type: 'time-select', // 时间选择器 type: 'time-select', // 时间选择器
label: '创建时间', label: '创建时间',
rules: [{ required: true, message: '请选择创建时间!' }],
props: {}, props: {},
operator: { operator: {
value: '',
options: [OPERATORS.BETWEEN, OPERATORS.GT, OPERATORS.LT], options: [OPERATORS.BETWEEN, OPERATORS.GT, OPERATORS.LT],
}, },
}; };
// 更新时间 // 更新时间
export const UPDATE_TIME = { export const UPDATE_TIME: SearchKeyType = {
key: CaseKeyEnum.UPDATE_TIME, key: CaseKeyEnum.UPDATE_TIME,
type: 'time-select', type: 'time-select',
label: '更新时间', label: '更新时间',
rules: [{ required: true, message: '请选择更新时间!' }],
props: {}, props: {},
operator: { operator: {
value: '',
options: [OPERATORS.BETWEEN, OPERATORS.GT, OPERATORS.LT], options: [OPERATORS.BETWEEN, OPERATORS.GT, OPERATORS.LT],
}, },
}; };

View File

@ -6,11 +6,11 @@
:is="form.searchKey.type" :is="form.searchKey.type"
v-bind="form.searchKey.props" v-bind="form.searchKey.props"
v-model="form.searchKey.value" v-model="form.searchKey.value"
@change="cate1ChangeHandler" @change="searchKeyChange"
> >
<a-optgroup <a-optgroup
v-for="(group, index) of props.selectGroupList" v-for="(group, i) of props.selectGroupList"
:key="`${group.label as string + index}`" :key="`${group.label as string}-${i}`"
:label="group.label" :label="group.label"
> >
<a-option <a-option
@ -36,35 +36,46 @@
</component> </component>
</div> </div>
<div class="flex flex-1"> <div class="flex flex-1">
<TimerSelect <a-form ref="queryContentFormRef" :model="form.queryContent">
v-if="getQueryContentType('time-select')" <a-form-item
:model-value="form.queryContent.value" required
v-bind="form.queryContent.props" field="value"
:operation-type="form.operatorCondition.value" :rules="form.queryContent.rules || [{ required: true, message: '请输入筛选内容' }]"
@update-time="updateTimeValue" hide-label
/> hide-asterisk
<component class="mb-0"
:is="form.queryContent.type" >
v-else <TimerSelect
v-bind="form.queryContent.props" v-if="form.queryContent.type === 'time-select'"
v-model="form.queryContent.value" :model-value="form.queryContent.value"
@change="filterKeyChange" v-bind="form.queryContent.props"
> :operation-type="form.operatorCondition.value"
<template v-if="form.queryContent.type === 'a-select'"> @update-time="updateTimeValue"
<a-option v-for="opt of form.queryContent.options" :key="opt.value" :value="opt.value">{{ />
opt.label <component
}}</a-option> :is="form.queryContent.type"
</template> v-else
<template v-else-if="form.queryContent.type === 'a-select-group'"> v-bind="form.queryContent.props"
<a-select v-model="form.queryContent.value" v-bind="form.queryContent.props"> v-model="form.queryContent.value"
<a-optgroup v-for="group of form.searchKey.options" :key="group.id" :label="group.label"> @change="filterKeyChange"
<a-option v-for="groupOptions of group.options" :key="groupOptions.id" :value="groupOptions.id">{{ >
groupOptions.label <template v-if="form.queryContent.type === 'a-select'">
<a-option v-for="opt of form.queryContent.options" :key="opt.value" :value="opt.value">{{
opt.label
}}</a-option> }}</a-option>
</a-optgroup> </template>
</a-select> <template v-else-if="form.queryContent.type === 'a-select-group'">
</template> <a-select v-model="form.queryContent.value" v-bind="form.queryContent.props">
</component> <a-optgroup v-for="group of form.searchKey.options" :key="group.id" :label="group.label">
<a-option v-for="groupOptions of group.options" :key="groupOptions.id" :value="groupOptions.id">{{
groupOptions.label
}}</a-option>
</a-optgroup>
</a-select>
</template>
</component>
</a-form-item>
</a-form>
<div class="minus"> <slot></slot></div> <div class="minus"> <slot></slot></div>
</div> </div>
</div> </div>
@ -78,6 +89,8 @@
import { TEST_PLAN_TEST_CASE } from './caseUtils'; import { TEST_PLAN_TEST_CASE } from './caseUtils';
import TimerSelect from './time-select.vue'; import TimerSelect from './time-select.vue';
import { SelectOptionData } from '@arco-design/web-vue'; import { SelectOptionData } from '@arco-design/web-vue';
import type { FormInstance } from '@arco-design/web-vue';
import type { SearchKeyType } from './type';
const { t } = useI18n(); const { t } = useI18n();
@ -93,20 +106,24 @@
const form = ref({ ...cloneDeep(props.formItem) }); const form = ref({ ...cloneDeep(props.formItem) });
watchEffect(() => { watchEffect(() => {
form.value.queryContent.value = props.formItem.queryContent.value; form.value = { ...cloneDeep(props.formItem) };
}); });
// //
const cate1ChangeHandler = (value: string) => { const searchKeyChange = (value: string) => {
const { operatorCondition, queryContent } = form.value; const { operatorCondition, queryContent } = form.value;
operatorCondition.value = ''; operatorCondition.value = '';
operatorCondition.options = []; operatorCondition.options = [];
queryContent.value = '';
// Key // Key
const currentKeysConfig = TEST_PLAN_TEST_CASE.find((item) => item.key === value); const currentKeysConfig = TEST_PLAN_TEST_CASE.find((item: any) => item.key === value);
if (currentKeysConfig) { if (currentKeysConfig) {
operatorCondition.options = currentKeysConfig.operator.options; operatorCondition.options = currentKeysConfig.operator.options;
operatorCondition.value = currentKeysConfig.operator.options[0].value; operatorCondition.value = currentKeysConfig.operator.options[0].value;
queryContent.type = currentKeysConfig.type; queryContent.type = currentKeysConfig.type;
if (currentKeysConfig.rules) {
queryContent.rules = currentKeysConfig.rules;
}
} }
emits('dataUpdated', form.value, props.index); emits('dataUpdated', form.value, props.index);
}; };
@ -132,17 +149,22 @@
form.value.queryContent.value = time; form.value.queryContent.value = time;
emits('dataUpdated', form.value, props.index); emits('dataUpdated', form.value, props.index);
}; };
const queryContentFormRef = ref<FormInstance>();
// //
const getQueryContentType = (type: string) => { const validateQueryContent = (callBack: (isSuccess: string) => void) => {
switch (type) { queryContentFormRef.value?.validate((errors) => {
// if (!errors) {
case 'time-select': callBack('ok');
return true; } else {
default: callBack('no');
return false; }
} });
}; };
defineExpose({
validateQueryContent,
});
</script> </script>
<style scoped></style> <style scoped></style>

View File

@ -15,6 +15,7 @@
<div v-for="(formItem, index) in formModels" :key="index" class="mb-[8px]"> <div v-for="(formItem, index) in formModels" :key="index" class="mb-[8px]">
<a-scrollbar class="overflow-y-auto" :style="{ 'max-height': props.maxHeight || '300px' }"> <a-scrollbar class="overflow-y-auto" :style="{ 'max-height': props.maxHeight || '300px' }">
<QueryFromItem <QueryFromItem
ref="queryFromRef"
:form-item="formItem" :form-item="formItem"
:form-list="formModels" :form-list="formModels"
:select-group-list="selectGroupList" :select-group-list="selectGroupList"
@ -162,6 +163,11 @@
type: 'a-input', type: 'a-input',
value: '', value: '',
field: 'condition', field: 'condition',
props: {
'max-length': 60,
'show-word-limit': true,
'allow-clear': true,
},
}, },
}; };
@ -169,17 +175,37 @@
// //
const removeField = (index: number) => { const removeField = (index: number) => {
debugger;
formModels.value.splice(index, 1); formModels.value.splice(index, 1);
}; };
//
const allValidateResult = ref<string[]>([]);
const queryFromRef = ref();
// //
const addField = () => { const addField = async () => {
allValidateResult.value = [];
//
await Promise.all(
queryFromRef.value.map((item: any) => {
return new Promise<void>((resolve) => {
item.validateQueryContent(async (isValidated: string) => {
allValidateResult.value.push(isValidated);
resolve();
});
});
})
);
const isValidateSuccess = allValidateResult.value.every((item: string) => item === 'ok');
const ishasCondition = formModels.value.some((item) => !item.searchKey.value); const ishasCondition = formModels.value.some((item) => !item.searchKey.value);
if (ishasCondition) { if (ishasCondition) {
Message.warning(t('searchPanel.selectTip')); Message.warning(t('searchPanel.selectTip'));
return;
} }
formModels.value.push(deaultTemplate); if (isValidateSuccess && !ishasCondition) {
formModels.value.push(deaultTemplate);
}
}; };
// //
@ -223,4 +249,7 @@
background: rgb(var(--primary-9)); background: rgb(var(--primary-9));
} }
} }
:deep(.arco-scrollbar-track-direction-vertical .arco-scrollbar-thumb-bar) {
margin: 7px;
}
</style> </style>

View File

@ -4,7 +4,6 @@
v-model:model-value="timeValue" v-model:model-value="timeValue"
class="w-[100%]" class="w-[100%]"
show-time show-time
allow-clear
:time-picker-props="{ defaultValue: '00:00:00' }" :time-picker-props="{ defaultValue: '00:00:00' }"
format="YYYY-MM-DD HH:mm:ss" format="YYYY-MM-DD HH:mm:ss"
position="br" position="br"
@ -15,6 +14,7 @@
v-model:model-value="timeRangeValue" v-model:model-value="timeRangeValue"
position="br" position="br"
show-time show-time
:allow-clear="false"
class="w-[100%]" class="w-[100%]"
format="YYYY-MM-DD HH:mm" format="YYYY-MM-DD HH:mm"
:time-picker-props="{ :time-picker-props="{
@ -26,12 +26,12 @@
<script setup lang="ts"> <script setup lang="ts">
import { CalendarValue } from '@arco-design/web-vue/es/date-picker/interface'; import { CalendarValue } from '@arco-design/web-vue/es/date-picker/interface';
import { ref, watch } from 'vue'; import { ref, watch, watchEffect } from 'vue';
type PickerType = 'between' | 'gt' | 'lt'; // | | type PickerType = 'between' | 'gt' | 'lt'; // | |
const props = defineProps<{ const props = defineProps<{
modelValue: [] | string; // modelValue: string[] | string; //
operationType: PickerType; // operationType: PickerType; //
}>(); }>();
@ -39,12 +39,19 @@
const timeValue = ref<string>(''); const timeValue = ref<string>('');
const timeRangeValue = ref([]); const timeRangeValue = ref<string[]>([]);
const changeHandler = (value: Date | string | number | undefined | (CalendarValue | undefined)[] | undefined) => { const changeHandler = (value: Date | string | number | undefined | (CalendarValue | undefined)[] | undefined) => {
emits('updateTime', value); emits('updateTime', value);
}; };
watchEffect(() => {
if (props.operationType === 'between') {
timeRangeValue.value = props.modelValue as string[];
}
timeValue.value = props.modelValue as string;
});
watch( watch(
() => props.modelValue, () => props.modelValue,
(val) => { (val) => {

View File

@ -1,4 +1,4 @@
import { SelectOptionData } from '@arco-design/web-vue'; import { SelectOptionData, FieldRule } from '@arco-design/web-vue';
export interface ConditionOptions { export interface ConditionOptions {
id: string; id: string;
@ -17,8 +17,8 @@ export interface QueryField {
type: 'a-select' | 'a-input' | 'a-input-number' | 'time-select' | 'a-tree-select'; type: 'a-select' | 'a-input' | 'a-input-number' | 'time-select' | 'a-tree-select';
value: string; value: string;
field: string; field: string;
rules?: FieldRule[];
props?: { props?: {
placeholder: string;
[key: string]: string | number | boolean; [key: string]: string | number | boolean;
}; };
options?: SelectOptionData[]; options?: SelectOptionData[];
@ -29,3 +29,23 @@ export interface QueryTemplate {
operatorCondition: QueryField; operatorCondition: QueryField;
queryContent: QueryField; queryContent: QueryField;
} }
export interface OptionsType {
label: string;
value: string;
}
export interface OperatorValue {
value: string; // 如果未设置value初始值则value初始值为options[0]
options: OptionsType[]; // 运算符选项
}
export interface SearchKeyType {
key: string; // 对应字段key
type: string; // Vue控件名称
label: string; // 显示名称
rules?: FieldRule[];
props?: {
[key: string]: string | number | boolean;
};
operator: OperatorValue;
}

View File

@ -2,9 +2,9 @@
<div class="page-header h-[34px]"> <div class="page-header h-[34px]">
<div class="text-[var(--color-text-1)]" <div class="text-[var(--color-text-1)]"
>{{ t('featureTest.featureCase.allCase') }} >{{ t('featureTest.featureCase.allCase') }}
<span class="text-[var(--color-text-4)]"> ({{ allCaseCount }}</span></div <span class="text-[var(--color-text-4)]"> ({{ allCaseCount }})</span></div
> >
<div> <div class="flex w-[80%] items-center justify-end">
<a-select class="w-[240px]" :placeholder="t('featureTest.featureCase.versionPlaceholder')"> <a-select class="w-[240px]" :placeholder="t('featureTest.featureCase.versionPlaceholder')">
<a-option v-for="version of versionOptions" :key="version.id" :value="version.id">{{ version.name }}</a-option> <a-option v-for="version of versionOptions" :key="version.id" :value="version.id">{{ version.name }}</a-option>
</a-select> </a-select>
@ -28,7 +28,7 @@
</MsTag> </MsTag>
<a-radio-group v-model:model-value="showType" type="button" class="file-show-type ml-[4px]"> <a-radio-group v-model:model-value="showType" type="button" class="file-show-type ml-[4px]">
<a-radio value="list" class="show-type-icon p-[2px]"><MsIcon type="icon-icon_view-list_outlined" /></a-radio> <a-radio value="list" class="show-type-icon p-[2px]"><MsIcon type="icon-icon_view-list_outlined" /></a-radio>
<a-radio value="xmind" class="show-type-icon p-[2px]"><icon-mind-mapping /></a-radio> <a-radio value="xmind" class="show-type-icon p-[2px]"><MsIcon type="icon-icon_mindnote_outlined" /></a-radio>
</a-radio-group> </a-radio-group>
</div> </div>
</div> </div>