mirror of
https://github.com/element-plus/element-plus.git
synced 2026-03-13 07:51:17 +08:00
refactor(transfer): correct requested changes
This commit is contained in:
@@ -7,7 +7,7 @@ describe('Transfer', () => {
|
||||
for (let i = 1; i <= 15; i++) {
|
||||
data.push({
|
||||
key: i,
|
||||
label: `备选项 ${ i }`,
|
||||
label: `备选项 ${i}`,
|
||||
disabled: i % 4 === 0,
|
||||
})
|
||||
}
|
||||
@@ -136,8 +136,8 @@ describe('Transfer', () => {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
renderFunc(h, option) {
|
||||
return h('span', `${ option.key } - ${ option.label }`)
|
||||
renderFunc(h ,option) {
|
||||
return h('span', `${option.key} - ${option.label}`)
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<template>
|
||||
<p style="text-align: center; margin: 0 0 20px">Customize data items using render-content</p>
|
||||
<div style="text-align: center">
|
||||
<p style="text-align: center; margin: 0 0 20px;">
|
||||
Customize data items using render-content
|
||||
</p>
|
||||
<div style="text-align: center;">
|
||||
<el-transfer
|
||||
v-model="value"
|
||||
style="text-align: left; display: inline-block"
|
||||
style="text-align: left; display: inline-block;"
|
||||
filterable
|
||||
:left-default-checked="['2', '3']"
|
||||
:right-default-checked="['1']"
|
||||
@@ -12,7 +14,7 @@
|
||||
:button-texts="['To left', 'To right']"
|
||||
:format="{
|
||||
noChecked: '${total}',
|
||||
hasChecked: '${checked}/${total}'
|
||||
hasChecked: '${checked}/${total}',
|
||||
}"
|
||||
:data="data"
|
||||
@change="handleChange"
|
||||
@@ -24,11 +26,13 @@
|
||||
<el-button class="transfer-footer" size="small">Operation</el-button>
|
||||
</template>
|
||||
</el-transfer>
|
||||
<p style="text-align: center; margin: 50px 0 20px">Customize data items using scoped slot</p>
|
||||
<div style="text-align: center">
|
||||
<p style="text-align: center; margin: 50px 0 20px;">
|
||||
Customize data items using scoped slot
|
||||
</p>
|
||||
<div style="text-align: center;">
|
||||
<el-transfer
|
||||
v-model="value4"
|
||||
style="text-align: left; display: inline-block"
|
||||
style="text-align: left; display: inline-block;"
|
||||
filterable
|
||||
:left-default-checked="['2', '3']"
|
||||
:right-default-checked="['1']"
|
||||
@@ -36,7 +40,7 @@
|
||||
:button-texts="['To left', 'To right']"
|
||||
:format="{
|
||||
noChecked: '${total}',
|
||||
hasChecked: '${checked}/${total}'
|
||||
hasChecked: '${checked}/${total}',
|
||||
}"
|
||||
:data="data"
|
||||
@change="handleChange"
|
||||
@@ -63,7 +67,7 @@ export default {
|
||||
for (let i = 1; i <= 15; i++) {
|
||||
data.push({
|
||||
key: i + '',
|
||||
label: `Option ${ i }`,
|
||||
label: `Option ${i}`,
|
||||
disabled: i % 4 === 0,
|
||||
})
|
||||
}
|
||||
@@ -74,7 +78,7 @@ export default {
|
||||
value: ['1'],
|
||||
value4: ['1'],
|
||||
renderFunc(h, option) {
|
||||
return h('span', `${ option.key } - ${ option.label }`)
|
||||
return h('span', `${option.key} - ${option.label}`)
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<transfer-panel
|
||||
ref="leftPanel"
|
||||
:data="sourceData"
|
||||
:render-content="renderContent"
|
||||
:option-render="optionRender"
|
||||
:placeholder="panelFilterPlaceholder"
|
||||
:title="leftPanelTitle"
|
||||
:filterable="filterable"
|
||||
@@ -13,9 +13,7 @@
|
||||
:props="props"
|
||||
@checked-change="onSourceCheckedChange"
|
||||
>
|
||||
<template #default>
|
||||
<slot name="left-footer"></slot>
|
||||
</template>
|
||||
<slot name="left-footer"></slot>
|
||||
</transfer-panel>
|
||||
<div class="el-transfer__buttons">
|
||||
<el-button
|
||||
@@ -40,7 +38,7 @@
|
||||
<transfer-panel
|
||||
ref="rightPanel"
|
||||
:data="targetData"
|
||||
:render-content="renderContent"
|
||||
:option-render="optionRender"
|
||||
:placeholder="panelFilterPlaceholder"
|
||||
:filterable="filterable"
|
||||
:format="format"
|
||||
@@ -50,15 +48,13 @@
|
||||
:props="props"
|
||||
@checked-change="onTargetCheckedChange"
|
||||
>
|
||||
<template #default>
|
||||
<slot name="right-footer"></slot>
|
||||
</template>
|
||||
<slot name="right-footer"></slot>
|
||||
</transfer-panel>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, PropType, provide, reactive, ref, toRefs, VNode, watch } from 'vue'
|
||||
import { computed, defineComponent, h, reactive, ref, toRefs, watch, VNode, PropType } from 'vue'
|
||||
import { t } from '@element-plus/locale'
|
||||
import ElButton from '@element-plus/button/src/button.vue'
|
||||
import TransferPanel from './transfer-panel.vue'
|
||||
@@ -165,7 +161,7 @@ export default defineComponent({
|
||||
],
|
||||
|
||||
setup(props, { emit, slots }) {
|
||||
const initData = reactive({
|
||||
const checkedState = reactive({
|
||||
leftChecked: [],
|
||||
rightChecked: [],
|
||||
})
|
||||
@@ -179,11 +175,12 @@ export default defineComponent({
|
||||
const {
|
||||
onSourceCheckedChange,
|
||||
onTargetCheckedChange,
|
||||
} = useCheckedChange(initData, emit)
|
||||
} = useCheckedChange(checkedState, emit)
|
||||
|
||||
const { addToLeft,
|
||||
const {
|
||||
addToLeft,
|
||||
addToRight,
|
||||
} = useMove(props, initData, propsKey, emit)
|
||||
} = useMove(props, checkedState, propsKey, emit)
|
||||
|
||||
const leftPanel = ref(null)
|
||||
const rightPanel = ref(null)
|
||||
@@ -206,12 +203,13 @@ export default defineComponent({
|
||||
|
||||
watch(() => props.modelValue, (val: Key[]) => emit(UPDATE_MODEL_EVENT, val))
|
||||
|
||||
provide('defaultScopedSlots', computed(() => slots.default))
|
||||
const optionRender = computed(() => option => {
|
||||
if (props.renderContent) return props.renderContent(h, option)
|
||||
|
||||
const {
|
||||
leftChecked,
|
||||
rightChecked,
|
||||
} = toRefs(initData)
|
||||
if (slots.default) return slots.default({ option })
|
||||
|
||||
return h('span', option[props.props.label] || option[porps.props.key])
|
||||
})
|
||||
|
||||
return {
|
||||
sourceData,
|
||||
@@ -221,14 +219,15 @@ export default defineComponent({
|
||||
addToLeft,
|
||||
addToRight,
|
||||
|
||||
leftChecked,
|
||||
rightChecked,
|
||||
...toRefs(checkedState),
|
||||
|
||||
hasButtonTexts,
|
||||
leftPanelTitle,
|
||||
rightPanelTitle,
|
||||
panelFilterPlaceholder,
|
||||
clearQuery,
|
||||
|
||||
optionRender,
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { ComputedRef, defineComponent, h, inject, Slot } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'OptionContent',
|
||||
props: {
|
||||
option: Object,
|
||||
renderContent: Function,
|
||||
labelProp: String,
|
||||
keyProp: String,
|
||||
},
|
||||
setup() {
|
||||
const defaultScopedSlots: ComputedRef<undefined | Slot> = inject('defaultScopedSlots')
|
||||
return {
|
||||
defaultScopedSlots,
|
||||
}
|
||||
},
|
||||
render() {
|
||||
return this.renderContent
|
||||
? this.renderContent(h, this.option)
|
||||
: this.defaultScopedSlots
|
||||
? this.defaultScopedSlots({ option: this.option })
|
||||
: h('span', this.option[this.labelProp] || this.option[this.keyProp])
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -24,7 +24,10 @@
|
||||
@mouseleave="inputHover = false"
|
||||
>
|
||||
<template #prefix>
|
||||
<i :class="['el-input__icon', 'el-icon-' + inputIcon]" @click="clearQuery"></i>
|
||||
<i
|
||||
:class="['el-input__icon', 'el-icon-' + inputIcon]"
|
||||
@click="clearQuery"
|
||||
></i>
|
||||
</template>
|
||||
</el-input>
|
||||
<el-checkbox-group
|
||||
@@ -41,21 +44,12 @@
|
||||
:disabled="item[disabledProp]"
|
||||
>
|
||||
<option-content
|
||||
:option="item"
|
||||
:render-content="renderContent"
|
||||
:label-prop="labelProp"
|
||||
:key-prop="keyProp"
|
||||
:option="optionRender(item)"
|
||||
/>
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
<p v-show="hasNoMatch" class="el-transfer-panel__empty">
|
||||
{{ t('el.transfer.noMatch') }}
|
||||
</p>
|
||||
<p
|
||||
v-show="data.length === 0 && !hasNoMatch"
|
||||
class="el-transfer-panel__empty"
|
||||
>
|
||||
{{ t('el.transfer.noData') }}
|
||||
<p v-show="hasNoMatch || data.length === 0" class="el-transfer-panel__empty">
|
||||
{{ hasNoMatch ? t('el.transfer.noMatch') : t('el.transfer.noData') }}
|
||||
</p>
|
||||
</div>
|
||||
<p v-if="hasFooter" class="el-transfer-panel__footer">
|
||||
@@ -70,10 +64,8 @@ import { t } from '@element-plus/locale'
|
||||
import ElCheckboxGroup from '@element-plus/checkbox/src/checkbox-group.vue'
|
||||
import ElCheckbox from '@element-plus/checkbox/src/checkbox.vue'
|
||||
import ElInput from '@element-plus/input/src/index.vue'
|
||||
import OptionContent from './option-content.vue'
|
||||
import { useCheck } from './useCheck'
|
||||
|
||||
|
||||
export const CHECKED_CHANGE_EVENT = 'checked-change'
|
||||
|
||||
export default defineComponent({
|
||||
@@ -83,7 +75,7 @@ export default defineComponent({
|
||||
ElCheckboxGroup,
|
||||
ElCheckbox,
|
||||
ElInput,
|
||||
OptionContent,
|
||||
OptionContent: ({ option }) => option,
|
||||
},
|
||||
|
||||
props: {
|
||||
@@ -93,7 +85,7 @@ export default defineComponent({
|
||||
return []
|
||||
},
|
||||
},
|
||||
renderContent: Function,
|
||||
optionRender: Function,
|
||||
placeholder: String,
|
||||
title: String,
|
||||
filterable: Boolean,
|
||||
@@ -106,7 +98,7 @@ export default defineComponent({
|
||||
emits: [CHECKED_CHANGE_EVENT],
|
||||
|
||||
setup(props, { emit, slots }) {
|
||||
const initData = reactive({
|
||||
const panelState = reactive({
|
||||
checked: [],
|
||||
allChecked: false,
|
||||
query: '',
|
||||
@@ -114,7 +106,6 @@ export default defineComponent({
|
||||
checkChangeByUser: true,
|
||||
})
|
||||
|
||||
|
||||
const {
|
||||
labelProp,
|
||||
keyProp,
|
||||
@@ -123,13 +114,14 @@ export default defineComponent({
|
||||
checkedSummary,
|
||||
isIndeterminate,
|
||||
handleAllCheckedChange,
|
||||
} = useCheck(props, initData, emit)
|
||||
} = useCheck(props, panelState, emit)
|
||||
|
||||
|
||||
const hasNoMatch = computed(() => initData.query.length > 0 && filteredData.value.length === 0)
|
||||
const hasNoMatch = computed(() => {
|
||||
return panelState.query.length > 0 && filteredData.value.length === 0
|
||||
})
|
||||
|
||||
const inputIcon = computed(() => {
|
||||
return initData.query.length > 0 && initData.inputHover
|
||||
return panelState.query.length > 0 && panelState.inputHover
|
||||
? 'circle-close'
|
||||
: 'search'
|
||||
})
|
||||
@@ -138,7 +130,7 @@ export default defineComponent({
|
||||
|
||||
const clearQuery = () => {
|
||||
if (inputIcon.value === 'circle-close') {
|
||||
initData.query = ''
|
||||
panelState.query = ''
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,7 +140,7 @@ export default defineComponent({
|
||||
query,
|
||||
inputHover,
|
||||
checkChangeByUser,
|
||||
} = toRefs(initData)
|
||||
} = toRefs(panelState)
|
||||
|
||||
return {
|
||||
labelProp,
|
||||
|
||||
6
packages/transfer/src/transfer.d.ts
vendored
6
packages/transfer/src/transfer.d.ts
vendored
@@ -35,14 +35,14 @@ export declare interface TransferProps {
|
||||
targetOrder: 'original' | 'push' | 'unshift'
|
||||
}
|
||||
|
||||
export declare interface TransferInitData {
|
||||
export declare interface TransferCheckedState {
|
||||
leftChecked: Key[]
|
||||
rightChecked: Key[]
|
||||
}
|
||||
|
||||
export declare interface TransferPanelProps {
|
||||
data: DataItem[]
|
||||
renderContent: (h, option) => VNode
|
||||
optionRender: ({ option: VNode }) => VNode
|
||||
placeholder: string
|
||||
title: string
|
||||
filterable: boolean
|
||||
@@ -52,7 +52,7 @@ export declare interface TransferPanelProps {
|
||||
props: Props
|
||||
}
|
||||
|
||||
export declare interface TransferPanelInitData {
|
||||
export declare interface TransferPanelState {
|
||||
checked: Key[]
|
||||
allChecked: boolean
|
||||
query: string
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { computed, watch } from 'vue'
|
||||
import { TransferPanelProps, TransferPanelInitData, Key } from './transfer'
|
||||
import { TransferPanelProps, TransferPanelState, Key } from './transfer'
|
||||
|
||||
import { CHECKED_CHANGE_EVENT } from './transfer-panel.vue'
|
||||
|
||||
export const useCheck = (props: TransferPanelProps, initData: TransferPanelInitData, emit) => {
|
||||
export const useCheck = (props: TransferPanelProps, panelState: TransferPanelState, emit) => {
|
||||
|
||||
const labelProp = computed(() => props.props.label || 'label')
|
||||
|
||||
@@ -15,18 +15,20 @@ export const useCheck = (props: TransferPanelProps, initData: TransferPanelInitD
|
||||
const filteredData = computed(() => {
|
||||
return props.data.filter(item => {
|
||||
if (typeof props.filterMethod === 'function') {
|
||||
return props.filterMethod(initData.query, item)
|
||||
return props.filterMethod(panelState.query, item)
|
||||
} else {
|
||||
const label = item[labelProp.value] || item[keyProp.value].toString()
|
||||
return label.toLowerCase().includes(initData.query.toLowerCase())
|
||||
return label.toLowerCase().includes(panelState.query.toLowerCase())
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
const checkableData = computed(() => filteredData.value.filter(item => !item[disabledProp.value]))
|
||||
const checkableData = computed(() => {
|
||||
return filteredData.value.filter(item => !item[disabledProp.value])
|
||||
})
|
||||
|
||||
const checkedSummary = computed(() => {
|
||||
const checkedLength = initData.checked.length
|
||||
const checkedLength = panelState.checked.length
|
||||
const dataLength = props.data.length
|
||||
const { noChecked, hasChecked } = props.format
|
||||
|
||||
@@ -42,31 +44,31 @@ export const useCheck = (props: TransferPanelProps, initData: TransferPanelInitD
|
||||
})
|
||||
|
||||
const isIndeterminate = computed(() => {
|
||||
const checkedLength = initData.checked.length
|
||||
const checkedLength = panelState.checked.length
|
||||
return checkedLength > 0 && checkedLength < checkableData.value.length
|
||||
})
|
||||
|
||||
|
||||
const updateAllChecked = () => {
|
||||
const checkableDataKeys = checkableData.value.map(item => item[keyProp.value])
|
||||
initData.allChecked = checkableDataKeys.length > 0 && checkableDataKeys.every(item => initData.checked.includes(item))
|
||||
panelState.allChecked = checkableDataKeys.length > 0 && checkableDataKeys.every(item => panelState.checked.includes(item))
|
||||
}
|
||||
|
||||
const handleAllCheckedChange = (value: Key[]) => {
|
||||
initData.checked = value ? checkableData.value.map(item => item[keyProp.value]) : []
|
||||
panelState.checked = value ? checkableData.value.map(item => item[keyProp.value]) : []
|
||||
}
|
||||
|
||||
watch(() => initData.checked, (val, oldVal) => {
|
||||
watch(() => panelState.checked, (val, oldVal) => {
|
||||
updateAllChecked()
|
||||
|
||||
if (initData.checkChangeByUser) {
|
||||
if (panelState.checkChangeByUser) {
|
||||
const movedKeys = val
|
||||
.concat(oldVal)
|
||||
.filter(v => !val.includes(v) || !oldVal.includes(v))
|
||||
emit(CHECKED_CHANGE_EVENT, val, movedKeys)
|
||||
} else {
|
||||
emit(CHECKED_CHANGE_EVENT, val)
|
||||
initData.checkChangeByUser = true
|
||||
panelState.checkChangeByUser = true
|
||||
}
|
||||
})
|
||||
|
||||
@@ -78,13 +80,13 @@ export const useCheck = (props: TransferPanelProps, initData: TransferPanelInitD
|
||||
watch(() => props.data, () => {
|
||||
const checked = []
|
||||
const filteredDataKeys = filteredData.value.map(item => item[keyProp.value])
|
||||
initData.checked.forEach(item => {
|
||||
panelState.checked.forEach(item => {
|
||||
if (filteredDataKeys.includes(item)) {
|
||||
checked.push(item)
|
||||
}
|
||||
})
|
||||
initData.checkChangeByUser = false
|
||||
initData.checked = checked
|
||||
panelState.checkChangeByUser = false
|
||||
panelState.checked = checked
|
||||
})
|
||||
|
||||
watch(() => props.defaultChecked, (val, oldVal) => {
|
||||
@@ -98,8 +100,8 @@ export const useCheck = (props: TransferPanelProps, initData: TransferPanelInitD
|
||||
checked.push(item)
|
||||
}
|
||||
})
|
||||
initData.checkChangeByUser = false
|
||||
initData.checked = checked
|
||||
panelState.checkChangeByUser = false
|
||||
panelState.checked = checked
|
||||
}, {
|
||||
immediate: true,
|
||||
})
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { TransferInitData, Key } from './transfer'
|
||||
import { TransferCheckedState, Key } from './transfer'
|
||||
|
||||
import { LEFT_CHECK_CHANGE_EVENT, RIGHT_CHECK_CHANGE_EVENT } from './index.vue'
|
||||
|
||||
export const useCheckedChange = (initData: TransferInitData, emit) => {
|
||||
export const useCheckedChange = (checkedState: TransferCheckedState, emit) => {
|
||||
const onSourceCheckedChange = (val: Key[], movedKeys: Key[]) => {
|
||||
initData.leftChecked = val
|
||||
checkedState.leftChecked = val
|
||||
if (movedKeys === undefined) return
|
||||
emit(LEFT_CHECK_CHANGE_EVENT, val, movedKeys)
|
||||
}
|
||||
|
||||
const onTargetCheckedChange = (val: Key[], movedKeys: Key[]) => {
|
||||
initData.rightChecked = val
|
||||
checkedState.rightChecked = val
|
||||
if (movedKeys === undefined) return
|
||||
emit(RIGHT_CHECK_CHANGE_EVENT, val, movedKeys)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { ComputedRef } from 'vue'
|
||||
import { UPDATE_MODEL_EVENT } from '../../utils/constants'
|
||||
import { CHANGE_EVENT } from './index.vue'
|
||||
import { TransferProps, TransferInitData, DataItem, Key } from './transfer'
|
||||
import { TransferProps, TransferCheckedState, DataItem, Key } from './transfer'
|
||||
|
||||
export const useMove = (props: TransferProps, initData: TransferInitData, propsKey: ComputedRef<string>, emit) => {
|
||||
export const useMove = (props: TransferProps, checkedState: TransferCheckedState, propsKey: ComputedRef<string>, emit) => {
|
||||
const _emit = (value, type: 'left' | 'right', checked: Key[]) => {
|
||||
emit(UPDATE_MODEL_EVENT, value)
|
||||
emit(CHANGE_EVENT, value, type, checked)
|
||||
@@ -12,13 +12,13 @@ export const useMove = (props: TransferProps, initData: TransferInitData, propsK
|
||||
const addToLeft = () => {
|
||||
const currentValue = props.modelValue.slice()
|
||||
|
||||
initData.rightChecked.forEach(item => {
|
||||
checkedState.rightChecked.forEach(item => {
|
||||
const index = currentValue.indexOf(item)
|
||||
if (index > -1) {
|
||||
currentValue.splice(index, 1)
|
||||
}
|
||||
})
|
||||
_emit(currentValue, 'left', initData.rightChecked)
|
||||
_emit(currentValue, 'left', checkedState.rightChecked)
|
||||
}
|
||||
|
||||
const addToRight = () => {
|
||||
@@ -27,7 +27,7 @@ export const useMove = (props: TransferProps, initData: TransferInitData, propsK
|
||||
const itemsToBeMoved = props.data
|
||||
.filter((item: DataItem) => {
|
||||
const itemKey = item[propsKey.value]
|
||||
return initData.leftChecked.includes(itemKey) && !props.modelValue.includes(itemKey)
|
||||
return checkedState.leftChecked.includes(itemKey) && !props.modelValue.includes(itemKey)
|
||||
})
|
||||
.map(item => item[propsKey.value])
|
||||
|
||||
@@ -35,7 +35,7 @@ export const useMove = (props: TransferProps, initData: TransferInitData, propsK
|
||||
? itemsToBeMoved.concat(currentValue)
|
||||
: currentValue.concat(itemsToBeMoved)
|
||||
|
||||
_emit(currentValue, 'right', initData.leftChecked)
|
||||
_emit(currentValue, 'right', checkedState.leftChecked)
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user