使用场景 ​
用于在两个列表之间移动静态选项,模型表示右侧已选值。规范化模式消费 options;native 使用原始 data。
移动、检索与方向事件 ​
change 依次给出最新值、移动方向和移动值。filterable 启用两侧检索,titles/buttonTexts 定义标题及动作文本。
移动、检索与方向事件
<template>
<div class="example">
<AsamTransfer
ref="control"
v-bind="{
...{ filterable: true },
...{
options: options,
titles: ['待选', '已选'],
'button-texts': ['移除', '加入']
}
}"
v-model="value"
@change="(v, direction, moved) => (event = JSON.stringify({ v, direction, moved }))"
>
<template #default="{ data }">
{{ data?.label }}
</template>
<template #left-footer> 请选择左侧项目 </template>
<template #right-footer> 右侧为当前模型 </template>
</AsamTransfer>
<AsamButton type="default" @click="control?.clearQuery('left')"> 清空左侧检索 </AsamButton>
<output>值:{{ value }};{{ event }}</output>
</div>
</template>
<script lang="ts" setup>
defineOptions({ name: 'AsamTransferBasicExample' });
import { ref } from 'vue';
import { AsamTransfer, AsamButton } from '@asam-ui/ui';
import type { AsamTransferExpose } from '@asam-ui/ui';
const control = ref<AsamTransferExpose>();
const options = [
{ label: '设计', value: 'design', raw: { code: 'design' } },
{ label: '开发', value: 'dev', raw: { code: 'dev' } },
{ label: '停用', value: 'off', disabled: true, raw: { code: 'off' } }
];
const value = ref<string[]>(['design']);
const event = ref('尚未移动');
</script>
<style scoped>
.example {
display: grid;
gap: var(--asam-space-md, 16px);
min-width: 0;
}
.row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--asam-space-sm, 8px);
}
.density-grid {
display: grid;
gap: var(--asam-space-md, 16px);
grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
}
section {
display: grid;
gap: var(--asam-space-sm, 8px);
min-width: 0;
}
output {
overflow-wrap: anywhere;
color: var(--asam-color-text-secondary);
}
</style>
<template>
<div class="example">
<AsamTransfer
ref="control"
v-bind="{
...{ filterable: true },
...{
options: options,
titles: ['待选', '已选'],
'button-texts': ['移除', '加入']
}
}"
v-model="value"
@change="(v, direction, moved) => (event = JSON.stringify({ v, direction, moved }))"
>
<template #default="{ data }">
{{ data?.label }}
</template>
<template #left-footer> 请选择左侧项目 </template>
<template #right-footer> 右侧为当前模型 </template>
</AsamTransfer>
<AsamButton type="default" @click="control?.clearQuery('left')"> 清空左侧检索 </AsamButton>
<output>值:{{ value }};{{ event }}</output>
</div>
</template>
<script setup>
defineOptions({ name: 'AsamTransferBasicExample' });
import { ref } from 'vue';
import { AsamTransfer, AsamButton } from '@asam-ui/ui';
const control = ref();
const options = [
{ label: '设计', value: 'design', raw: { code: 'design' } },
{ label: '开发', value: 'dev', raw: { code: 'dev' } },
{ label: '停用', value: 'off', disabled: true, raw: { code: 'off' } }
];
const value = ref(['design']);
const event = ref('尚未移动');
</script>
<style scoped>
.example {
display: grid;
gap: var(--asam-space-md, 16px);
min-width: 0;
}
.row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--asam-space-sm, 8px);
}
.density-grid {
display: grid;
gap: var(--asam-space-md, 16px);
grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
}
section {
display: grid;
gap: var(--asam-space-sm, 8px);
min-width: 0;
}
output {
overflow-wrap: anywhere;
color: var(--asam-color-text-secondary);
}
</style>
字符串与对象存储 ​
穿梭框始终多选,无需 multiple 属性。字符串模式使用 separator;对象模式保存规范化选项数组,targetOrder 控制右侧排列。
字符串与对象存储
<template>
<div class="example">
<AsamTransfer
v-model="value"
:options="options"
v-bind="{ 'multiple-value-mode': 'string', separator: '|', 'target-order': 'push' }"
/>
<AsamTransfer v-model="objects" :options="options" value-mode="object" />
<output>字符串:{{ value }};对象:{{ objects }}</output>
</div>
</template>
<script lang="ts" setup>
defineOptions({ name: 'AsamTransferPoliciesExample' });
import { ref } from 'vue';
import { AsamTransfer } from '@asam-ui/ui';
const options = [
{ label: '设计', value: 'design', raw: { code: 'design' } },
{ label: '开发', value: 'dev', raw: { code: 'dev' } },
{ label: '停用', value: 'off', disabled: true, raw: { code: 'off' } }
];
const value = ref('design');
const objects = ref<(typeof options)[number][]>([]);
</script>
<style scoped>
.example {
display: grid;
gap: var(--asam-space-md, 16px);
min-width: 0;
}
.row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--asam-space-sm, 8px);
}
.density-grid {
display: grid;
gap: var(--asam-space-md, 16px);
grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
}
section {
display: grid;
gap: var(--asam-space-sm, 8px);
min-width: 0;
}
output {
overflow-wrap: anywhere;
color: var(--asam-color-text-secondary);
}
</style>
<template>
<div class="example">
<AsamTransfer
v-model="value"
:options="options"
v-bind="{ 'multiple-value-mode': 'string', separator: '|', 'target-order': 'push' }"
/>
<AsamTransfer v-model="objects" :options="options" value-mode="object" />
<output>字符串:{{ value }};对象:{{ objects }}</output>
</div>
</template>
<script setup>
defineOptions({ name: 'AsamTransferPoliciesExample' });
import { ref } from 'vue';
import { AsamTransfer } from '@asam-ui/ui';
const options = [
{ label: '设计', value: 'design', raw: { code: 'design' } },
{ label: '开发', value: 'dev', raw: { code: 'dev' } },
{ label: '停用', value: 'off', disabled: true, raw: { code: 'off' } }
];
const value = ref('design');
const objects = ref([]);
</script>
<style scoped>
.example {
display: grid;
gap: var(--asam-space-md, 16px);
min-width: 0;
}
.row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--asam-space-sm, 8px);
}
.density-grid {
display: grid;
gap: var(--asam-space-md, 16px);
grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
}
section {
display: grid;
gap: var(--asam-space-sm, 8px);
min-width: 0;
}
output {
overflow-wrap: anywhere;
color: var(--asam-color-text-secondary);
}
</style>
原始数据映射 ​
valueMode=native 配合 data 与 props 映射原始 key、label 和 disabled;模型使用原始键数组。
原始数据映射
<template>
<div class="example">
<div class="row">
<AsamButton type="default" @click="reset('original')">按来源顺序</AsamButton>
<AsamButton type="default" @click="reset('push')">新增项放末尾</AsamButton>
<AsamButton type="default" @click="reset('unshift')">新增项放开头</AsamButton>
</div>
<AsamTransfer
v-model="value"
v-bind="{
'value-mode': 'native',
data: data,
targetOrder: order,
buttonTexts: ['移出', '移入'],
props: { key: 'id', label: 'name', disabled: 'locked' }
}"
@change="recordMove"
/>
<output>原始键:{{ value }}</output>
<output>排列:{{ order }};移动:{{ lastMove }}</output>
</div>
</template>
<script lang="ts" setup>
defineOptions({ name: 'AsamTransferNativeExample' });
import { ref } from 'vue';
import { AsamButton, AsamTransfer } from '@asam-ui/ui';
const value = ref<number[]>([2]);
const order = ref<'original' | 'push' | 'unshift'>('original');
const lastMove = ref('');
function reset(next: 'original' | 'push' | 'unshift') {
order.value = next;
value.value = [2];
lastMove.value = '';
}
function recordMove(_value: unknown, direction: string, keys: readonly unknown[]) {
lastMove.value = `${direction}:${keys.join(',')}`;
}
const data = [
{ id: 1, name: '设计', locked: false },
{ id: 2, name: '开发', locked: false },
{ id: 3, name: '停用', locked: true }
];
</script>
<style scoped>
.example {
display: grid;
gap: var(--asam-space-md, 16px);
min-width: 0;
}
.row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--asam-space-sm, 8px);
}
.density-grid {
display: grid;
gap: var(--asam-space-md, 16px);
grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
}
section {
display: grid;
gap: var(--asam-space-sm, 8px);
min-width: 0;
}
output {
overflow-wrap: anywhere;
color: var(--asam-color-text-secondary);
}
</style>
<template>
<div class="example">
<div class="row">
<AsamButton type="default" @click="reset('original')">按来源顺序</AsamButton>
<AsamButton type="default" @click="reset('push')">新增项放末尾</AsamButton>
<AsamButton type="default" @click="reset('unshift')">新增项放开头</AsamButton>
</div>
<AsamTransfer
v-model="value"
v-bind="{
'value-mode': 'native',
data: data,
targetOrder: order,
buttonTexts: ['移出', '移入'],
props: { key: 'id', label: 'name', disabled: 'locked' }
}"
@change="recordMove"
/>
<output>原始键:{{ value }}</output>
<output>排列:{{ order }};移动:{{ lastMove }}</output>
</div>
</template>
<script setup>
defineOptions({ name: 'AsamTransferNativeExample' });
import { ref } from 'vue';
import { AsamButton, AsamTransfer } from '@asam-ui/ui';
const value = ref([2]);
const order = ref('original');
const lastMove = ref('');
function reset(next) {
order.value = next;
value.value = [2];
lastMove.value = '';
}
function recordMove(_value, direction, keys) {
lastMove.value = `${direction}:${keys.join(',')}`;
}
const data = [
{ id: 1, name: '设计', locked: false },
{ id: 2, name: '开发', locked: false },
{ id: 3, name: '停用', locked: true }
];
</script>
<style scoped>
.example {
display: grid;
gap: var(--asam-space-md, 16px);
min-width: 0;
}
.row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--asam-space-sm, 8px);
}
.density-grid {
display: grid;
gap: var(--asam-space-md, 16px);
grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
}
section {
display: grid;
gap: var(--asam-space-sm, 8px);
min-width: 0;
}
output {
overflow-wrap: anywhere;
color: var(--asam-color-text-secondary);
}
</style>
四档尺寸与上下文继承 ​
四个实例分别显式使用 large、big、default、small。下面省略 size 的实例随 ConfigProvider 响应切换;显式 size 覆盖祖先。面板本身没有 size 契约的入口不附加虚构尺寸属性。
四档尺寸与上下文继承
<template>
<div class="example">
<div class="density-grid">
<section data-density="large">
<strong> large </strong>
<AsamTransfer size="large" :options="options" :model-value="['design']" />
</section>
<section data-density="big">
<strong> big </strong>
<AsamTransfer size="big" :options="options" :model-value="['design']" />
</section>
<section data-density="default">
<strong> default </strong>
<AsamTransfer size="default" :options="options" :model-value="['design']" />
</section>
<section data-density="small">
<strong> small </strong>
<AsamTransfer size="small" :options="options" :model-value="['design']" />
</section>
</div>
<AsamConfigProvider :size="density">
<section data-density="inherited">
<AsamTransfer :options="options" :model-value="['design']" />
</section>
</AsamConfigProvider>
<div class="row">
<AsamButton type="default" @click="density = 'small'"> 继承 small </AsamButton>
<AsamButton type="default" @click="density = 'big'"> 继承 big </AsamButton>
</div>
<output>配置提供器:{{ density }}</output>
</div>
</template>
<script lang="ts" setup>
defineOptions({ name: 'AsamTransferSizesExample' });
import { ref } from 'vue';
import { AsamTransfer, AsamConfigProvider, AsamButton } from '@asam-ui/ui';
import type { AsamControlSize } from '@asam-ui/ui';
const density = ref<AsamControlSize>('big');
const options = [
{ label: '设计', value: 'design', raw: { code: 'design' } },
{ label: '开发', value: 'dev', raw: { code: 'dev' } },
{ label: '停用', value: 'off', disabled: true, raw: { code: 'off' } }
];
</script>
<style scoped>
.example {
display: grid;
gap: var(--asam-space-md, 16px);
min-width: 0;
}
.row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--asam-space-sm, 8px);
}
.density-grid {
display: grid;
gap: var(--asam-space-md, 16px);
grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
}
section {
display: grid;
gap: var(--asam-space-sm, 8px);
min-width: 0;
}
output {
overflow-wrap: anywhere;
color: var(--asam-color-text-secondary);
}
</style>
<template>
<div class="example">
<div class="density-grid">
<section data-density="large">
<strong> large </strong>
<AsamTransfer size="large" :options="options" :model-value="['design']" />
</section>
<section data-density="big">
<strong> big </strong>
<AsamTransfer size="big" :options="options" :model-value="['design']" />
</section>
<section data-density="default">
<strong> default </strong>
<AsamTransfer size="default" :options="options" :model-value="['design']" />
</section>
<section data-density="small">
<strong> small </strong>
<AsamTransfer size="small" :options="options" :model-value="['design']" />
</section>
</div>
<AsamConfigProvider :size="density">
<section data-density="inherited">
<AsamTransfer :options="options" :model-value="['design']" />
</section>
</AsamConfigProvider>
<div class="row">
<AsamButton type="default" @click="density = 'small'"> 继承 small </AsamButton>
<AsamButton type="default" @click="density = 'big'"> 继承 big </AsamButton>
</div>
<output>配置提供器:{{ density }}</output>
</div>
</template>
<script setup>
defineOptions({ name: 'AsamTransferSizesExample' });
import { ref } from 'vue';
import { AsamTransfer, AsamConfigProvider, AsamButton } from '@asam-ui/ui';
const density = ref('big');
const options = [
{ label: '设计', value: 'design', raw: { code: 'design' } },
{ label: '开发', value: 'dev', raw: { code: 'dev' } },
{ label: '停用', value: 'off', disabled: true, raw: { code: 'off' } }
];
</script>
<style scoped>
.example {
display: grid;
gap: var(--asam-space-md, 16px);
min-width: 0;
}
.row {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--asam-space-sm, 8px);
}
.density-grid {
display: grid;
gap: var(--asam-space-md, 16px);
grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
}
section {
display: grid;
gap: var(--asam-space-sm, 8px);
min-width: 0;
}
output {
overflow-wrap: anywhere;
color: var(--asam-color-text-secondary);
}
</style>
ASAM 扩展与迁移 ​
四档 density 通过 ConfigProvider/表单继承。 完整说明。
虚拟滚动与边界项 ​
virtualScroll 启用两侧虚拟列表,itemSize 是实际虚拟行高;筛选后被选项和移入右侧的回显都保留完整模型。 完整说明。
虚拟列表过滤与已选回显
<template>
<div class="example">
<AsamTransfer
ref="transfer"
v-model="selected"
:options="options"
virtual-scroll
:item-size="36"
filterable
:titles="['未选择', '已选择']"
:button-texts="['移回', '移入']"
@change="(_, direction, values) => (moved = `${direction}: ${values.join(', ')}`)"
>
<template #default="{ data }"
><strong>{{ data?.label }}</strong></template
>
<template #left-footer><small>输入「第 499 项」筛选边界项</small></template>
<template #right-footer
><small>已选 {{ selected.length }} 项</small></template
>
</AsamTransfer>
<AsamButton type="default" @click="transfer?.clearQuery('left')">清空左侧检索</AsamButton>
<output>右侧模型:{{ selected }};最近移动:{{ moved }}</output>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { AsamTransfer, AsamButton } from '@asam-ui/ui';
import type { AsamTransferExpose } from '@asam-ui/ui';
const options = Array.from({ length: 500 }, (_, index) => ({
value: `row-${index}`,
label: `第 ${index} 项`,
raw: { index },
disabled: index === 1
}));
const selected = ref<string[]>([]);
const moved = ref('');
const transfer = ref<AsamTransferExpose>();
defineOptions({ name: 'AsamTransferVirtualDemo' });
</script>
<style scoped>
.example {
display: grid;
gap: var(--asam-space-md, 16px);
}
</style>
<template>
<div class="example">
<AsamTransfer
ref="transfer"
v-model="selected"
:options="options"
virtual-scroll
:item-size="36"
filterable
:titles="['未选择', '已选择']"
:button-texts="['移回', '移入']"
@change="(_, direction, values) => (moved = `${direction}: ${values.join(', ')}`)"
>
<template #default="{ data }"
><strong>{{ data?.label }}</strong></template
>
<template #left-footer><small>输入「第 499 项」筛选边界项</small></template>
<template #right-footer
><small>已选 {{ selected.length }} 项</small></template
>
</AsamTransfer>
<AsamButton type="default" @click="transfer?.clearQuery('left')">清空左侧检索</AsamButton>
<output>右侧模型:{{ selected }};最近移动:{{ moved }}</output>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { AsamTransfer, AsamButton } from '@asam-ui/ui';
const options = Array.from({ length: 500 }, (_, index) => ({
value: `row-${index}`,
label: `第 ${index} 项`,
raw: { index },
disabled: index === 1
}));
const selected = ref([]);
const moved = ref('');
const transfer = ref();
defineOptions({ name: 'AsamTransferVirtualDemo' });
</script>
<style scoped>
.example {
display: grid;
gap: var(--asam-space-md, 16px);
}
</style>
渲染回调、默认勾选与空内容 ​
renderContent 使用传入的 h 渲染原始项,default 插槽则是另一种互斥渲染入口。 完整说明。
渲染回调、默认勾选与空内容
<template>
<div class="example">
<AsamTransfer
v-model="selected"
value-mode="native"
:data="effectiveData"
filterable
:filter-method="filter"
:left-default-checked="['b']"
:right-default-checked="['a']"
:render-content="render"
:titles="['候选', '目标']"
:button-texts="['移回', '移入']"
:format="{ noChecked: '共${total}项', hasChecked: '${checked}/${total}' }"
@change="(_, direction, keys) => (moved = direction + ':' + keys.join(','))"
>
<template #left-footer><small>使用默认勾选移入</small></template>
<template #right-footer><small>保留原始键</small></template>
<template #left-empty><p class="example-paragraph">候选为空</p></template>
<template #right-empty><p class="example-paragraph">尚无目标</p></template>
</AsamTransfer>
<AsamButton
type="default"
@click="
data = [];
selected = [];
"
>清空候选</AsamButton
>
<AsamButton
type="default"
@click="
data = original;
selected = ['a'];
"
>还原候选</AsamButton
>
<AsamCheckboxItem v-model="disabled">禁用全部数据项</AsamCheckboxItem>
<output>原始键:{{ selected }};移动:{{ moved }}</output>
</div>
</template>
<script lang="ts" setup>
import { computed, ref } from 'vue';
import { AsamTransfer, AsamButton, AsamCheckboxItem } from '@asam-ui/ui';
import type { AsamTransferProps } from '@asam-ui/ui';
const original = [
{ key: 'a', label: '设计', disabled: false },
{ key: 'b', label: '开发', disabled: false },
{ key: 'c', label: '测试', disabled: false },
{ key: 'd', label: '停用', disabled: true }
];
const data = ref(original);
const selected = ref<string[]>(['a']);
const disabled = ref(false);
const effectiveData = computed(() =>
data.value.map((item) => ({ ...item, disabled: disabled.value || item.disabled }))
);
const moved = ref('');
const render: NonNullable<AsamTransferProps['renderContent']> = (h, item) =>
h('strong', { class: 'rendered-choice' }, String(item.label));
const filter: NonNullable<AsamTransferProps['filterMethod']> = (query, item) =>
String(item.label).includes(query);
defineOptions({ name: 'AsamTransferCustomDemo' });
</script>
<style scoped>
.example {
display: grid;
gap: var(--asam-space-md, 16px);
}
</style>
<style scoped>
p:where(.example-paragraph) {
margin-block: 16px;
line-height: 1.8;
overflow-wrap: anywhere;
}
</style>
<template>
<div class="example">
<AsamTransfer
v-model="selected"
value-mode="native"
:data="effectiveData"
filterable
:filter-method="filter"
:left-default-checked="['b']"
:right-default-checked="['a']"
:render-content="render"
:titles="['候选', '目标']"
:button-texts="['移回', '移入']"
:format="{ noChecked: '共${total}项', hasChecked: '${checked}/${total}' }"
@change="(_, direction, keys) => (moved = direction + ':' + keys.join(','))"
>
<template #left-footer><small>使用默认勾选移入</small></template>
<template #right-footer><small>保留原始键</small></template>
<template #left-empty><p class="example-paragraph">候选为空</p></template>
<template #right-empty><p class="example-paragraph">尚无目标</p></template>
</AsamTransfer>
<AsamButton
type="default"
@click="
data = [];
selected = [];
"
>清空候选</AsamButton
>
<AsamButton
type="default"
@click="
data = original;
selected = ['a'];
"
>还原候选</AsamButton
>
<AsamCheckboxItem v-model="disabled">禁用全部数据项</AsamCheckboxItem>
<output>原始键:{{ selected }};移动:{{ moved }}</output>
</div>
</template>
<script setup>
import { computed, ref } from 'vue';
import { AsamTransfer, AsamButton, AsamCheckboxItem } from '@asam-ui/ui';
const original = [
{ key: 'a', label: '设计', disabled: false },
{ key: 'b', label: '开发', disabled: false },
{ key: 'c', label: '测试', disabled: false },
{ key: 'd', label: '停用', disabled: true }
];
const data = ref(original);
const selected = ref(['a']);
const disabled = ref(false);
const effectiveData = computed(() =>
data.value.map((item) => ({ ...item, disabled: disabled.value || item.disabled }))
);
const moved = ref('');
const render = (h, item) => h('strong', { class: 'rendered-choice' }, String(item.label));
const filter = (query, item) => String(item.label).includes(query);
defineOptions({ name: 'AsamTransferCustomDemo' });
</script>
<style scoped>
.example {
display: grid;
gap: var(--asam-space-md, 16px);
}
</style>
<style scoped>
p:where(.example-paragraph) {
margin-block: 16px;
line-height: 1.8;
overflow-wrap: anywhere;
}
</style>
注意:固定版本首次挂载缺少 footer 插槽时,后续动态新增可能不显示;请预先声明插槽。 完整说明。
API 阅读方式 ​
API 按公开类型与运行默认值生成;未知默认值不等于 false 或空值。 完整说明。
下方 Props、Events、Slots、Exposes 按当前实际公开类型与运行默认值生成。组合属性按分支生效,不能任意混搭值模式。标为“未知 / 未声明”的默认值不代表 false 或空值。源码区域提供对应语言的独立 Demo.vue,可直接复制或下载;本地辅助组件、数据和素材已内联到当前文件。
原生与高级入口 ​
valueMode="native" 直接委托原生穿梭框,保留省略的 titles、buttonTexts、targetOrder、modelValue 与原生默认值。 完整说明。
API
属性 AsamTransferProps
| 属性名 | 说明 | 类型 | 默认值 |
|---|---|---|---|
options | — | readonly AsamHierarchyOption<V, I>[] | undefined | () => [] |
disabled | — | boolean | undefined | false |
filterable | 是否可搜索 | boolean | undefined | false |
filterPlaceholder | 搜索框占位符 | string | undefined | — |
titles | 自定义列表标题 | readonly [string, string] | undefined | [string, string] | undefined | () => ['未选择', '已选择'] |
buttonTexts | 自定义按钮文案 | readonly [string, string] | undefined | [string, string] | undefined | () => ['', ''] |
targetOrder | 右侧列表元素的排序策略:若为 original,则保持与数据源相同的顺序;若为 push,则新加入的元素排在最后;若为 unshift,则新加入的元素排在最前 | enum"push" | "unshift" | "original"完整类型:"push" | "unshift" | "original" | undefined | (AsamTransferProps<V, I>)["targetOrder"] | 'push' |
separator | — | string | undefined | undefined | ',' |
itemSize | 虚拟滚动的节点高度 (px) | number | undefined | 30 |
format | 列表顶部勾选状态文案 | (AsamTransferProps<V, I>)["format"] | 工厂函数(每个实例创建) |
filterMethod | 自定义搜索方法 | (AsamTransferProps<V, I>)["filterMethod"] | — |
renderContent | 自定义数据项渲染函数 | (AsamTransferProps<V, I>)["renderContent"] | — |
validateEvent | 是否触发表单验证 | (AsamTransferProps<V, I>)["validateEvent"] | true |
virtualScroll | 是否为大数据启用虚拟滚动 | boolean | undefined | false |
leftDefaultChecked | 初始状态下左侧列表的已勾选项的 key 数组 | (AsamTransferProps<V, I>)["leftDefaultChecked"] | 工厂函数(每个实例创建) |
rightDefaultChecked | 初始状态下右侧列表的已勾选项的 key 数组 | (AsamTransferProps<V, I>)["rightDefaultChecked"] | 工厂函数(每个实例创建) |
size | — | enum"large" | "big" | "default" | "small"完整类型:AsamControlSize | undefined | undefined |
modelValue | 选中项绑定值 | string | undefined | readonly V[] | undefined | readonly AsamHierarchyOption<V, I>[] | undefined | (AsamTransferProps<V, I>)["modelValue"] | () => [] |
valueMode | — | enum"value" | "object" | "native"完整类型:"value" | undefined | "object" | "native" | 'value' |
multipleValueMode | — | enum"string" | "array"完整类型:"string" | "array" | undefined | undefined | undefined |
data | Transfer 的数据源 | PublicMember<AsamTransferProps<V, I>, "data"> | 工厂函数(每个实例创建) |
props | 数据源的字段别名 | PublicMember<AsamTransferProps<V, I>, "props"> | 工厂函数(每个实例创建) |
事件 AsamTransferEmits
| 属性名 | 说明 | 类型 | 默认值 |
|---|---|---|---|
update:modelValue | — | [value: string | readonly AsamHierarchyOption<AsamValue, object>[] | readonly AsamValue[]] | — |
change | — | [value: string | readonly AsamHierarchyOption<AsamValue, object>[] | readonly AsamValue[], direction: "right" | "left", movedValues: readonly AsamValue[]] | — |
left-check-change | — | [checked: readonly AsamValue[], moved?: readonly AsamValue[] | undefined] | — |
right-check-change | — | [checked: readonly AsamValue[], moved?: readonly AsamValue[] | undefined] | — |
插槽 AsamTransferSlots
| 属性名 | 说明 | 类型 | 默认值 |
|---|---|---|---|
default | — | (AsamTransferSlots)["default"] | — |
left-footer | — | (() => unknown) | undefined | — |
right-footer | — | (() => unknown) | undefined | — |
left-empty | — | (() => unknown) | undefined | — |
right-empty | — | (() => unknown) | undefined | — |
实例方法与状态 AsamTransferExpose
| 属性名 | 说明 | 类型 | 默认值 |
|---|---|---|---|
clearQuery | 清空某个面板的搜索关键词 | (AsamTransferExpose)["clearQuery"] | — |
leftPanel | — | (AsamTransferExpose)["leftPanel"] | — |
rightPanel | — | (AsamTransferExpose)["rightPanel"] | — |
用法与契约补充说明 ​
ASAM 扩展与迁移 ​
四档 density 通过 ConfigProvider/表单继承。ASAM value/object 与 array/string 遵守同一存储规则;native 保留原始 data 行为。公开 expose 为 clearQuery、leftPanel、rightPanel,footer/empty 插槽按左右侧提供。
虚拟滚动与边界项 ​
virtualScroll 启用两侧虚拟列表,itemSize 是实际虚拟行高;筛选后被选项和移入右侧的回显都保留完整模型。filterMethod(query, item) 可自定义匹配;renderContent 或默认插槽自定义每行。左右 footer 与 empty 插槽独立,defaultChecked、checked-change、targetOrder 在虚拟和普通列表中遵循相同规则。clearQuery('left'/'right') 清理相应查询,不改变右侧已选模型。
渲染回调、默认勾选与空内容 ​
renderContent 使用传入的 h 渲染原始项,default 插槽则是另一种互斥渲染入口。leftDefaultChecked/rightDefaultChecked 只初始化两侧勾选,不改变模型;format 展示勾选数,change 返回方向与原始移动键。leftEmpty/rightEmpty 提供两侧空内容;原生模式按每条 data 的 disabled 控制选项;本示例通过更新数据禁用全部项。ASAM 对整个控件的 disabled 补充边界只用于高级 value/object 模式。
API 阅读方式 ​
高级模式默认左右标题为“未选择 / 已选择”,按钮文本为空,targetOrder 为 push;native 模式采用底层依赖的标题与 targetOrder 默认值。显式 titles 和 buttonTexts 会覆盖默认值。original 按来源 data/options 顺序排列右侧,push 将新增项接在现有右侧之后,unshift 将新增项放在现有右侧之前;三者都保留原始键及所选值模式。
原生与高级入口 ​
valueMode="native" 直接委托原生穿梭框,保留省略的 titles、buttonTexts、targetOrder、modelValue 与原生默认值。原生 default 插槽仅接收 { option };高级 value/object 路径仍提供 { option, data }。left-check-change / right-check-change 保留第二实参(包含 undefined),change 保留值、方向、移动值的原顺序。clearQuery 保留原生返回值,leftPanel/rightPanel 读取 live 引用。原生入口不增加独立 disabled 协议;高级入口的 disabled、对象/字符串模型和表单校验属于 ASAM 组合。虚拟列表高度与移动后的缓存时序直接遵循原生,不再修补 VNode 或内部 setup。
更多参数与事件说明 ​
渲染回调、默认勾选与空内容 ​
在固定依赖 2.14.7 / Vue 3.5.34 的已核验输入中,首次挂载未提供 left-footer 或 right-footer 插槽,随后向同一实例动态新增相应插槽时,新内容未显示;原生穿梭框与 ASAM native 路径结果一致。两个插槽各自首次挂载即提供的新实例对照能够显示内容。因此这两项“初始缺省 → 动态新增”表现属于原生兼容限制,不保证新增内容可见。内容替换、删除以及 default、empty 等其他插槽按各自独立核验结果判断。
关联组件
参数与事件补充说明
组合契约按分支生效;请同时查看公开类型。
参数与事件说明
options
必填:按分支;声明来源:ASAM 声明。
默认值与来源
() => [](源码表达式;ASAM 声明)disabled
必填:按分支;声明来源:ASAM 声明。
disabled 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
契约分支 1
枚举:false / true
默认值与来源
false(隐式 Boolean 默认;已安装组件)filterable
必填:否;声明来源:ASAM 声明。
filterable 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:false / true
枚举:false / true
默认值与来源
false(隐式 Boolean 默认;已安装组件)来源分支 4
固定上游文档默认:false(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
filterPlaceholder
必填:否;声明来源:ASAM 声明。
默认值与来源
未知 / 未声明来源分支 4
固定上游文档默认:未声明(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
titles
必填:否;声明来源:ASAM 声明。
默认值与来源
() => ['未选择', '已选择'](源码表达式;ASAM 声明)来源分支 4
固定上游文档默认:[](2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
固定上游源码工厂(未执行):() => [](2.14.7;源码事实,未作为 ASAM 运行实测)
buttonTexts
必填:否;声明来源:ASAM 声明。
默认值与来源
() => ['', ''](源码表达式;ASAM 声明)来源分支 4
固定上游文档默认:[](2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
固定上游源码工厂(未执行):() => [](2.14.7;源码事实,未作为 ASAM 运行实测)
targetOrder
必填:否;声明来源:ASAM 声明。
targetOrder 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:"push" / "unshift" / "original"
枚举:"push" / "unshift" / "original"
默认值与来源
'push'(源码表达式;ASAM 声明)来源分支 4
固定上游文档默认:original(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
固定上游源码默认表达式:'original'(2.14.7;源码事实,未作为 ASAM 运行实测)
separator
必填:否;声明来源:ASAM 声明。
默认值与来源
','(源码表达式;ASAM 声明)itemSize
必填:否;声明来源:上游声明。
默认值与来源
30(已安装运行时;已安装组件)固定上游文档默认:30(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
固定上游源码默认表达式:30(2.14.7;源码事实,未作为 ASAM 运行实测)
format
必填:否;声明来源:上游声明。
format 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
format.noChecked:string | undefined— 上游声明format.hasChecked:string | undefined— 上游声明
默认值与来源
工厂函数(每个实例创建)(实例工厂;已安装组件)固定上游文档默认:{}(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
固定上游源码工厂(未执行):() => ({})(2.14.7;源码事实,未作为 ASAM 运行实测)
filterMethod
必填:否;声明来源:上游声明。
filterMethod 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(query: string, item: Parameters<Extract<NonNullable<(AsamTransferProps<V, I>)["filterMethod"]>, (...args: never[]) => unknown>>[1]): boolean
默认值与来源
未知 / 未声明固定上游文档默认:未声明(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
固定上游源码未声明 default(2.14.7;源码事实,未作为 ASAM 运行实测)
renderContent
必填:否;声明来源:上游声明。
renderContent 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(h: Parameters<Extract<NonNullable<(AsamTransferProps<V, I>)["renderContent"]>, (...args: never[]) => unknown>>[0], option: Parameters<Extract<NonNullable<(AsamTransferProps<V, I>)["renderContent"]>, (...args: never[]) => unknown>>[1]): ReturnType<Extract<NonNullable<(AsamTransferProps<V, I>)["renderContent"]>, (...args: never[]) => unknown>>
默认值与来源
未知 / 未声明固定上游文档默认:未声明(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
固定上游源码未声明 default(2.14.7;源码事实,未作为 ASAM 运行实测)
validateEvent
必填:否;声明来源:上游声明。
validateEvent 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:false / true
默认值与来源
true(已安装运行时;已安装组件)固定上游文档默认:true(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
固定上游源码默认表达式:true(2.14.7;源码事实,未作为 ASAM 运行实测)
virtualScroll
必填:否;声明来源:上游声明。
virtualScroll 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:false / true
默认值与来源
false(隐式 Boolean 默认;已安装组件)固定上游文档默认:false(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
leftDefaultChecked
必填:否;声明来源:上游声明。
默认值与来源
工厂函数(每个实例创建)(实例工厂;已安装组件)固定上游文档默认:[](2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
固定上游源码工厂(未执行):() => [](2.14.7;源码事实,未作为 ASAM 运行实测)
rightDefaultChecked
必填:否;声明来源:上游声明。
默认值与来源
工厂函数(每个实例创建)(实例工厂;已安装组件)固定上游文档默认:[](2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
固定上游源码工厂(未执行):() => [](2.14.7;源码事实,未作为 ASAM 运行实测)
size
必填:否;声明来源:ASAM 声明。
size 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:"large" / "big" / "default" / "small"
默认值与来源
undefined(源码表达式;ASAM 声明)modelValue
必填:否;声明来源:ASAM 声明。
默认值与来源
() => [](源码表达式;ASAM 声明)来源分支 4
固定上游文档默认:[](2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
固定上游源码工厂(未执行):() => [](2.14.7;源码事实,未作为 ASAM 运行实测)
valueMode
必填:否;声明来源:ASAM 声明。
valueMode 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:"value"
枚举:"object"
枚举:"native"
默认值与来源
'value'(源码表达式;ASAM 声明)multipleValueMode
必填:否;声明来源:ASAM 声明。
multipleValueMode 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:"string"
枚举:"array"
默认值与来源
undefined(源码表达式;ASAM 声明)data
必填:按分支;声明来源:上游声明。
默认值与来源
工厂函数(每个实例创建)(实例工厂;已安装组件)固定上游文档默认:[](2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
固定上游源码工厂(未执行):() => [](2.14.7;源码事实,未作为 ASAM 运行实测)
props
必填:按分支;声明来源:上游声明。
props 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
契约分支 4
props.label:string | undefined— 上游声明数据项标签的字段名;这是字段别名,不是数据项本身的值。
固定上游文档默认:
未声明(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)props.key:string | undefined— 上游声明数据项标识的字段名;这是字段别名,不是数据项本身的值。
固定上游文档默认:
未声明(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)props.disabled:string | undefined— 上游声明数据项禁用状态的字段名;这是字段别名,不是数据项本身的值。
固定上游文档默认:
未声明(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
默认值与来源
工厂函数(每个实例创建)(实例工厂;已安装组件)固定上游文档默认:未声明(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
固定上游源码工厂(未执行):() =>
mutable({
label: 'label',
key: 'key',
disabled: 'disabled',
} as const)(2.14.7;源码事实,未作为 ASAM 运行实测)
AsamTransferProps 公开类型视图
import type { VNode } from 'vue';
import type { AsamTransferProps, AsamValue, AsamHierarchyOption, AsamHierarchyRuntimeProps, AsamControlSize } from '@asam-ui/ui';
type PublicMember<T, K extends PropertyKey> = T extends unknown ? K extends keyof T ? T[K] : never : never;
// AsamTransferProps
type PublicContract<V extends AsamValue = AsamValue, I extends object = object> = AsamTransferProps<V, I>;
参数与事件说明
update:modelValue
必填:是;声明来源:ASAM 声明。
change
必填:是;声明来源:ASAM 声明。
left-check-change
必填:是;声明来源:ASAM 声明。
right-check-change
必填:是;声明来源:ASAM 声明。
AsamTransferEmits 公开类型视图
import type { AsamTransferEmits, AsamValue, AsamHierarchyOption } from '@asam-ui/ui';
// AsamTransferEmits
type PublicContract = {
"update:modelValue": [value: string | readonly AsamHierarchyOption<AsamValue, object>[] | readonly AsamValue[]];
"change": [value: string | readonly AsamHierarchyOption<AsamValue, object>[] | readonly AsamValue[], direction: "right" | "left", movedValues: readonly AsamValue[]];
"left-check-change": [checked: readonly AsamValue[], moved?: readonly AsamValue[] | undefined];
"right-check-change": [checked: readonly AsamValue[], moved?: readonly AsamValue[] | undefined];
};
参数与事件说明
default
必填:否;声明来源:ASAM 声明。
default 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(scope: Parameters<Extract<NonNullable<(AsamTransferSlots)["default"]>, (...args: never[]) => unknown>>[0]): unknown
left-footer
必填:否;声明来源:ASAM 声明。
left-footer 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(): unknown
right-footer
必填:否;声明来源:ASAM 声明。
right-footer 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(): unknown
left-empty
必填:否;声明来源:ASAM 声明。
left-empty 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(): unknown
right-empty
必填:否;声明来源:ASAM 声明。
right-empty 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(): unknown
AsamTransferSlots 公开类型视图
import type { AsamTransferSlots, AsamHierarchyOption } from '@asam-ui/ui';
// AsamTransferSlots
type PublicContract = {
"default"?: (AsamTransferSlots)["default"];
"left-footer"?: (() => unknown) | undefined;
"right-footer"?: (() => unknown) | undefined;
"left-empty"?: (() => unknown) | undefined;
"right-empty"?: (() => unknown) | undefined;
};
参数与事件说明
clearQuery
必填:是;声明来源:上游声明。
clearQuery 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(which: Parameters<Extract<NonNullable<(AsamTransferExpose)["clearQuery"]>, (...args: never[]) => unknown>>[0]): void
leftPanel
必填:是;声明来源:上游声明。
rightPanel
必填:是;声明来源:上游声明。
AsamTransferExpose 公开类型视图
import type { AsamTransferExpose } from '@asam-ui/ui';
// AsamTransferExpose
type PublicContract = {
"clearQuery": (AsamTransferExpose)["clearQuery"];
"leftPanel": (AsamTransferExpose)["leftPanel"];
"rightPanel": (AsamTransferExpose)["rightPanel"];
};