Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/web-vue/components/transfer/README.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ description: A two-column multi-select component that moves elements from one co

@import ./__demo__/custom-header.md

@import ./__demo__/order.md

## API


Expand All @@ -43,6 +45,7 @@ description: A two-column multi-select component that moves elements from one co
|title|The title of the source and target selection boxes|`string[]`|`['Source', 'Target']`||
|source-input-search-props|Search box configuration for source selection box|`object`|`-`|2.51.1|
|target-input-search-props|Search box configuration for target selection box|`object`|`-`|2.51.1|
|target-order|Sorting strategy for the right list element,`original`: No sorting, `push`: Insert at the end, `unshift`: Insert at the beginning, or use a custom sorting method|`'original'\| 'push'\| 'unshift'\| ((a: TransferItem, b: TransferItem) => number)`|`'original'`|2.57.1|
### `<transfer>` Events

|Event Name|Description|Parameters|
Expand Down
3 changes: 3 additions & 0 deletions packages/web-vue/components/transfer/README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ description: 两栏布局的多选组件,将元素从一栏即时移到另一

@import ./__demo__/custom-header.md

@import ./__demo__/order.md

## API


Expand All @@ -41,6 +43,7 @@ description: 两栏布局的多选组件,将元素从一栏即时移到另一
|title|源选择框和目标选择框的标题|`string[]`|`['Source', 'Target']`||
|source-input-search-props|源选择框的搜索框配置|`object`|`-`|2.51.1|
|target-input-search-props|目标选择框的搜索框配置|`object`|`-`|2.51.1|
|target-order|右侧列表元素的排序策略,`original`保持与数据源相同的顺序,`push`插入到最后,`unshift`插入到最前面,或使用自定义排序方法|`'original'\| 'push'\| 'unshift'\| ((a: TransferItem, b: TransferItem) => number)`|`'original'`|2.57.1|
### `<transfer>` Events

|事件名|描述|参数|
Expand Down
2 changes: 2 additions & 0 deletions packages/web-vue/components/transfer/TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ description: A two-column multi-select component that moves elements from one co

@import ./__demo__/custom-header.md

@import ./__demo__/order.md

## API

%%API(transfer.vue)%%
Expand Down
63 changes: 63 additions & 0 deletions packages/web-vue/components/transfer/__demo__/order.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
```yaml
title:
zh-CN: 排序
en-US: Order
```

## zh-CN

通过设置 `target-order` ,修改右侧列表元素的排序。

---

## en-US

By setting `target-order`, modify the ordering of the elements in the list on the right.

---

```vue
<template>
<a-space direction="vertical">
<a-typography-title :heading="6">排序策略</a-typography-title>
<a-space>
<a-radio-group v-model="order" type="button" @change="value1 = []">
<a-radio value="push">Push</a-radio>
<a-radio value="unshift">Unshift</a-radio>
<a-radio value="original">Original</a-radio>
</a-radio-group>
</a-space>
<a-transfer v-model:model-value="value1" :data="data1" :target-order="order" />
<a-typography-title :heading="6">自定义排序</a-typography-title>
<a-transfer
v-model:model-value="value2"
:data="data2"
:target-order="(a, b) => b.label.localeCompare(a.label)"
/>
</a-space>
</template>

<script setup lang="ts">
import { ref } from 'vue';

const value1 = ref<any[]>([]);
const value2 = ref<any[]>([]);
const order = ref<'push' | 'unshift' | 'original'>('original');
const data1: TransferItem[] = Array.from({ length: 16 }, (_, i) => ({
value: `${i}`,
label: `Option ${i}`,
}));
const data2: TransferItem[] = [
'California',
'Illinois',
'Maryland',
'Texas',
'Florida',
'Colorado',
'Connecticut ',
].map((item, index) => ({
value: `${index}`,
label: item,
}));
</script>
```
128 changes: 99 additions & 29 deletions packages/web-vue/components/transfer/transfer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ import IconRight from '../icon/icon-right';
import { DataInfo, TransferItem } from './interface';
import { transferInjectionKey } from './context';
import { useFormItem } from '../_hooks/use-form-item';
import { isFunction } from '../_utils/is';

export default defineComponent({
name: 'Transfer',
Expand Down Expand Up @@ -208,6 +209,20 @@ export default defineComponent({
targetInputSearchProps: {
type: Object,
},
/**
* @zh 右侧列表元素的排序策略,`original`保持与数据源相同的顺序,`push`插入到最后,`unshift`插入到最前面,或使用自定义排序方法
* @en Sorting strategy for the right list element,`original`: No sorting, `push`: Insert at the end, `unshift`: Insert at the beginning, or use a custom sorting method
* @version 2.57.1
*/
targetOrder: {
type: [String, Function] as PropType<
| 'original'
| 'push'
| 'unshift'
| ((a: TransferItem, b: TransferItem) => number)
>,
default: 'original',
},
},
emits: {
'update:modelValue': (value: string[]) => true,
Expand Down Expand Up @@ -309,6 +324,50 @@ export default defineComponent({
const sourceTitle = computed(() => props.title?.[0]);
const targetTitle = computed(() => props.title?.[1]);

const getTargetData = () => {
if (isFunction(props.targetOrder)) {
return props.data
.filter((item) => computedTarget.value.includes(item.value))
.sort(props.targetOrder);
}
if (props.targetOrder === 'original') {
return props.data.filter((item) =>
computedTarget.value.includes(item.value)
);
}
return computedTarget.value.reduce((arr: TransferItem[], cur: string) => {
const val = props.data.find((item) => item.value === cur);
if (val) arr.push(val);
return arr;
}, []);
};

const processItems = (items: TransferItem[], selectedSet: Set<string>) => {
return items.reduce(
(acc: DataInfo, item: TransferItem) => {
acc.data.push(item);

if (!item.disabled) {
acc.allValidValues.push(item.value);
}

if (selectedSet.has(item.value)) {
acc.selected.push(item.value);
if (!item.disabled) {
acc.validSelected.push(item.value);
}
}
return acc;
},
{
data: [],
allValidValues: [],
selected: [],
validSelected: [],
}
);
};

const dataInfo = computed(() => {
const sourceInfo: DataInfo = {
data: [],
Expand All @@ -323,32 +382,20 @@ export default defineComponent({
selected: [],
validSelected: [],
};
const targetData = getTargetData();
const targetValues = new Set(targetData.map((item) => item.value));
const selectedSet = new Set(computedSelected.value);
// 处理目标数据部分
Object.assign(targetInfo, processItems(targetData, selectedSet));

for (const item of props.data) {
if (computedTarget.value.includes(item.value)) {
targetInfo.data.push(item);
if (!item.disabled) {
targetInfo.allValidValues.push(item.value);
}
if (computedSelected.value.includes(item.value)) {
targetInfo.selected.push(item.value);
if (!item.disabled) {
targetInfo.validSelected.push(item.value);
}
}
} else {
sourceInfo.data.push(item);
if (!item.disabled) {
sourceInfo.allValidValues.push(item.value);
}
if (computedSelected.value.includes(item.value)) {
sourceInfo.selected.push(item.value);
if (!item.disabled) {
sourceInfo.validSelected.push(item.value);
}
}
}
}
// 处理源数据部分(props.data 中不在 targetData 中的项)
Object.assign(
sourceInfo,
processItems(
props.data.filter((item) => !targetValues.has(item.value)),
selectedSet
)
);

return {
sourceInfo,
Expand All @@ -361,10 +408,33 @@ export default defineComponent({
};

const moveTo = (values: string[], target: 'target' | 'source') => {
const newTarget =
target === 'target'
? [...computedTarget.value, ...values]
: computedTarget.value.filter((value) => !values.includes(value));
let newTarget: string[] = [];
if (target === 'target') {
const merged = [...computedTarget.value, ...values];
if (isFunction(props.targetOrder)) {
// 自定义排序逻辑
newTarget = merged
.map((v) => props.data.find((item) => item.value === v)) // 转换为完整对象
.filter(Boolean) // 过滤无效项
.filter((item): item is TransferItem => item !== undefined) // 过滤无效项
.sort(props.targetOrder) // 执行自定义排序
.map((item) => item.value); // 转换回值数组
} else {
newTarget =
props.targetOrder === 'unshift'
? values.concat(computedTarget.value)
: computedTarget.value.concat(values);
if (props.targetOrder === 'original') {
newTarget = props.data
.filter((item) => newTarget.includes(item.value))
.map((item) => item.value);
}
}
} else {
newTarget.push(
...computedTarget.value.filter((value) => !values.includes(value))
);
}
handleSelect(
dataInfo.value[target === 'target' ? 'targetInfo' : 'sourceInfo']
.selected
Expand Down