Skip to content

Commit

Permalink
Merge pull request #203 from People-Sea/feat(select)
Browse files Browse the repository at this point in the history
 feat(form-select): implement select all and clear options in multiple select
  • Loading branch information
kanyxmo authored Aug 26, 2024
2 parents 00ca0b0 + ace244d commit 318d787
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
<a-popover :content-style="{padding: '0px',width: '256px'}" position="rt" trigger="click" v-if="isBoolean(props.component.multipleTools) || props.component.multipleTools.showSelectAll">
<a-button class="ml-2" size="mini">已选 {{ value.length }}</a-button>
<template #title>
<a-space fill style="padding: 12px;">
<a-space fill style="padding: 12px 12px 8px 12px;">
<a-button
:disabled="loading || !value.length"
size="mini"
Expand Down Expand Up @@ -136,7 +136,6 @@ const handleSelectAll = (status) => {
value.value = []
}
if (status) {
// 使用 Set 的 difference 来找出需要添加的值
const currentSet = new Set(value.value)
const newValues = dicts.value[dictIndex].filter(item => !currentSet.has(item.value)).map(item => item.value)
value.value = [...value.value, ...newValues]
Expand Down
151 changes: 133 additions & 18 deletions src/components/ma-form/formItem/form-select.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,19 @@
:readonly="props.component.readonly"
:error="props.component.error"
:placeholder="props.component.placeholder ?? `请选择${props.component.title}`"
:loading="props.component.loading"
:loading="props.component.loading ?? loading"
:allow-search="props.component.allowSearch ?? true"
:allow-create="props.component.allowCreate"
:max-tag-count="props.component.maxTagCount"
:max-tag-count="props.component.maxTagCount ?? 1"
:bordered="props.component.bordered"
:unmount-on-close="props.component.unmountOnClose"
:popup-container="props.component.popupContainer"
:filter-option="props.component.filterOption"
:virtual-list-props="props.component.virtualListProps"
:trigger-props="props.component.triggerProps"
:format-label="props.component.formatLabel"
:fallback-option="props.component.fallbackOption"
:show-extra-options="props.component.showExtraOptions"
:fallback-option="props.component.fallbackOption ?? handlerFallback"
:show-extra-options="props.component.showExtraOptions ?? false"
:value-key="props.component.valueKey"
:search-delay="props.component.searchDelay"
:limit="props.component.limit"
Expand All @@ -51,11 +51,54 @@
@exceed-limit="rv('onExceedLimit', $event)"
@search="rv('onSearch', $event)"
>
<template #header v-if="props.component.multiple && props.component.selectAll === true">
<template #header v-if="props.component.multiple && props.component.multipleTools">
<div style="padding: 6px 12px;" >
<a-space>
<a-checkbox :value="false" @change="handleSelectAll">全选/清除</a-checkbox>
<a-button size="mini" type="outline" @click="handleInverse">反选</a-button>
<a-space fill>
<a-checkbox
v-if="isBoolean(props.component.multipleTools) || props.component.multipleTools.selectAll"
:model-value="checkedAll"
:indeterminate="indeterminate"
:disabled="loading"
@change="handleSelectAll">全选/清除</a-checkbox>

<a-button
v-if="isBoolean(props.component.multipleTools) || props.component.multipleTools.inverse"
class="ml-2"
size="mini"
type="outline"
:disabled="loading"
@click="handleInverse">反选</a-button>

<a-popover :content-style="{padding: '0px',width: '256px'}" position="rt" trigger="click" v-if="isBoolean(props.component.multipleTools) || props.component.multipleTools.showSelectAll">
<a-button class="ml-2" size="mini">已选 {{ value.length }}</a-button>
<template #title>
<a-space fill style="padding: 12px 12px 8px 12px;">
<a-button
:disabled="loading || !value.length"
size="mini"
status="danger"
@click="value = []">清空 {{ value.length }}</a-button>
<a-input-search
v-model="keyword"
size="mini"
allow-clear
/>
</a-space>
</template>
<template #content>
<a-scrollbar style="height: 200px;overflow: auto;">
<a-checkbox-group
v-if="(value.length && keyword === '') || Object.keys(filteredOptions).length > 0"
direction="vertical"
v-model="value">
<div v-for="item in filteredOptions" class="select-all-options">
<a-checkbox :value="item.value">{{ item.label }}</a-checkbox>
</div>
</a-checkbox-group>
<a-empty v-else />
</a-scrollbar>
</template>
</a-popover>
</a-space>
</div>
</template>
Expand All @@ -66,6 +109,7 @@
size="mini"
:total="dataTotal"
:page-size="props.component.dict.pageOption.pageSize"
:disabled="loading"
simple
@change="handlePage"
>
Expand All @@ -81,9 +125,9 @@
</template>

<script setup>
import { ref, inject, onMounted, nextTick, watch } from 'vue'
import { ref, inject, onMounted, nextTick, watch, computed } from 'vue'
import MaFormItem from './form-item.vue'
import { get, isUndefined, set, xor, isObject, indexOf } from 'lodash'
import { get, isUndefined, set, xor, isObject, isBoolean } from 'lodash'
import { runEvent } from '../js/event.js'
import { handlerCascader, loadDict } from '../js/networkRequest.js'

Expand All @@ -104,38 +148,80 @@ const dictList = inject('dictList')
const formLoading = inject('formLoading')
const columns = inject('columns')
const getColumnService= inject('getColumnService')
const rv = async (ev, value = undefined) => await runEvent(props.component, ev, { formModel, getColumnService, columns }, value)
const rv = async (ev, value = '') => await runEvent(props.component, ev, { formModel, getColumnService, columns }, value)

const index = props.customField ?? props.component.dataIndex
const dictIndex = index.match(/^(\w+\.)\d+\./) ? index.match(/^(\w+\.)\d+\./)[1] + props.component.dataIndex : props.component.dataIndex
const value = ref(get(formModel.value, index, ''))
const dataTotal = ref(0)
const loading = ref(false)
const optionMap = ref({})
const keyword = ref('')

watch( () => get(formModel.value, index), vl => value.value = vl )
watch( () => value.value, v => {
if (props.component.multiple) {
v.forEach(k => {
if ( !optionMap.value[k] ) {
optionMap.value[k] = dictList.value[dictIndex].find(i => i.value === k)
}
})
for(const k in optionMap.value) {
if ( !v.includes(k) ) delete optionMap.value[k]
}
}
set(formModel.value, index, v)
index.indexOf('.') > -1 && delete formModel.value[index]
} )
watch( () => dictList.value[index] , async v => {
dataTotal.value = v?.pageInfo?.total || 0
})

const checkedAll = computed(() => {
const { multiple, multipleTools } = props.component
const currentDicts = dictList.value[dictIndex]

if (multiple && multipleTools && currentDicts) {
return currentDicts.every(item => value.value.includes(item.value))
}

return false
})

const filteredOptions = computed(() => {
const { multiple, multipleTools } = props.component
if (multiple && multipleTools && keyword.value !== '') {
const lowerCaseKeyword = keyword.value.toLowerCase()
return Object.values(optionMap.value).filter(option =>
option.label.toLowerCase().includes(lowerCaseKeyword)
)
}
return optionMap.value
})

const indeterminate = computed(() => {
if (props.component.multiple && props.component.multipleTools && checkedAll.value == false) {
const currentDicts = dictList.value[dictIndex]
return currentDicts.some(item => value.value.includes(item.value))
}
return false
})

if (value.value === '') {
value.value = undefined
value.value = props.component.multiple === true? []: ''
} else if (! isUndefined(value.value) && props.component.dict && (props.component.dict.name || props.component.dict.data) && !props.component.multiple) {
value.value = value.value + ''
}

if ( isUndefined(props.component.multipleTools) ) props.component.multipleTools = true
const handleSelectAll = (status) => {
if (isUndefined(value.value)) {
value.value = []
}
if (status) {
dictList.value[dictIndex].map(item=>{
if(indexOf(value.value, item.value) === -1) {
value.value.push(item.value)
}
})
const currentSet = new Set(value.value)
const newValues = dictList.value[dictIndex].filter(item => !currentSet.has(item.value)).map(item => item.value)
value.value = [...value.value, ...newValues]
} else {
value.value = []
}
Expand All @@ -147,14 +233,19 @@ const handleInverse = () => {
}
const ids = []
dictList.value[dictIndex].map( item => ids.push(item.value) )
value.value = xor(ids, value.value)
value.value = xor(value.value, ids)
}

const handlePage = async (page) => {
loading.value = true
props.component.dict.pageOption.page = page
await loadDict(dictList.value, props.component)
loading.value = false
}

const handlerFallback = (key) => {
return optionMap.value[key] || key
}
const handleCascaderChangeEvent = async (value) => {
formLoading.value = true
const component = props.component
Expand All @@ -175,3 +266,27 @@ rv('onCreated')
onMounted(() => {
})
</script>
<style lang="less" scoped>
.arco-checkbox-group {
width: 100%;
.select-all-options {
width: 100%;
height: 36px;
padding: 0 15px 0 7px;
color: inherit;
background-color: transparent;
transition: all 0.1s cubic-bezier(0, 0, 1, 1);
::v-deep(.arco-checkbox .arco-checkbox-label) {
height: 36px;
line-height: 36px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
&:hover {
color: var(--color-text-1);
background-color: var(--color-fill-2);
}
}
}
</style>

0 comments on commit 318d787

Please sign in to comment.