refactor(transfer): correct requested changes

This commit is contained in:
Ryan
2020-09-05 15:38:55 +08:00
committed by jeremywu
parent 81d17d4cd5
commit 6d53ef87c4
9 changed files with 85 additions and 114 deletions

View File

@@ -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}`)
},
},
})

View File

@@ -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}`)
},
}
},

View File

@@ -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,
}
},
})

View File

@@ -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>

View File

@@ -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,

View File

@@ -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

View File

@@ -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,
})

View File

@@ -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)
}

View File

@@ -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 {