mirror of
https://github.com/element-plus/element-plus.git
synced 2024-11-27 02:01:15 +08:00
refactor(transfer): transfer component refactoring completed
This commit is contained in:
parent
4e29aab1cf
commit
3daf1cd6ab
@ -30,6 +30,7 @@ import ElTabs from '@element-plus/tabs'
|
||||
import ElTooltip from '@element-plus/tooltip'
|
||||
import ElSlider from '@element-plus/slider'
|
||||
import ElInput from '@element-plus/input'
|
||||
import ElTransfer from '@element-plus/transfer'
|
||||
|
||||
export {
|
||||
ElAlert,
|
||||
@ -62,6 +63,7 @@ export {
|
||||
ElTooltip,
|
||||
ElSlider,
|
||||
ElInput,
|
||||
ElTransfer,
|
||||
}
|
||||
|
||||
export default function install(app: App): void {
|
||||
@ -96,4 +98,5 @@ export default function install(app: App): void {
|
||||
ElTooltip(app)
|
||||
ElSlider(app)
|
||||
ElInput(app)
|
||||
ElTransfer(app)
|
||||
}
|
||||
|
5
packages/transfer/index.ts
Normal file
5
packages/transfer/index.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { App } from 'vue'
|
||||
import Transfer from './src/index.vue'
|
||||
export default (app: App): void => {
|
||||
app.component(Transfer.name, Transfer)
|
||||
}
|
12
packages/transfer/package.json
Normal file
12
packages/transfer/package.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "@element-plus/transfer",
|
||||
"version": "0.0.0",
|
||||
"main": "dist/index.js",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"vue": "^3.0.0-rc.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/test-utils": "^2.0.0-beta.0"
|
||||
}
|
||||
}
|
235
packages/transfer/src/index.vue
Normal file
235
packages/transfer/src/index.vue
Normal file
@ -0,0 +1,235 @@
|
||||
<template>
|
||||
<div class="el-transfer">
|
||||
<transfer-panel
|
||||
ref="leftPanel"
|
||||
:data="sourceData"
|
||||
:render-content="renderContent"
|
||||
:placeholder="panelFilterPlaceholder"
|
||||
:title="leftPanelTitle"
|
||||
:filterable="filterable"
|
||||
:format="format"
|
||||
:filter-method="filterMethod"
|
||||
:default-checked="leftDefaultChecked"
|
||||
:props="props"
|
||||
@checked-change="onSourceCheckedChange"
|
||||
>
|
||||
<template #default>
|
||||
<slot name="left-footer"></slot>
|
||||
</template>
|
||||
</transfer-panel>
|
||||
<div class="el-transfer__buttons">
|
||||
<el-button
|
||||
type="primary"
|
||||
:class="['el-transfer__button', hasButtonTexts ? 'is-with-texts' : '']"
|
||||
:disabled="rightChecked.length === 0"
|
||||
@click="addToLeft"
|
||||
>
|
||||
<i class="el-icon-arrow-left"></i>
|
||||
<span v-if="buttonTexts[0] !== undefined">{{ buttonTexts[0] }}</span>
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
:class="['el-transfer__button', hasButtonTexts ? 'is-with-texts' : '']"
|
||||
:disabled="leftChecked.length === 0"
|
||||
@click="addToRight"
|
||||
>
|
||||
<span v-if="buttonTexts[1] !== undefined">{{ buttonTexts[1] }}</span>
|
||||
<i class="el-icon-arrow-right"></i>
|
||||
</el-button>
|
||||
</div>
|
||||
<transfer-panel
|
||||
ref="rightPanel"
|
||||
:data="targetData"
|
||||
:render-content="renderContent"
|
||||
:placeholder="panelFilterPlaceholder"
|
||||
:filterable="filterable"
|
||||
:format="format"
|
||||
:filter-method="filterMethod"
|
||||
:title="rightPanelTitle"
|
||||
:default-checked="rightDefaultChecked"
|
||||
:props="props"
|
||||
@checked-change="onTargetCheckedChange"
|
||||
>
|
||||
<template #default>
|
||||
<slot name="right-footer"></slot>
|
||||
</template>
|
||||
</transfer-panel>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, PropType, provide, reactive, ref, toRefs, VNode, watch } from 'vue'
|
||||
import { t } from '@element-plus/locale'
|
||||
import ElButton from '@element-plus/button/src/button.vue'
|
||||
import TransferPanel from './transfer-panel.vue'
|
||||
import { useComputedData } from './useComputedData'
|
||||
import { useCheckedChange } from './useCheckedChange'
|
||||
import { useMove } from './useMove'
|
||||
import { Key } from './transfer'
|
||||
|
||||
import { UPDATE_MODEL_EVENT } from '@element-plus/utils/constants'
|
||||
|
||||
export const CHANGE_EVENT = 'change'
|
||||
export const LEFT_CHECK_CHANGE_EVENT = 'left-check-change'
|
||||
export const RIGHT_CHECK_CHANGE_EVENT = 'right-check-change'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ElTransfer',
|
||||
|
||||
components: {
|
||||
TransferPanel,
|
||||
ElButton,
|
||||
},
|
||||
|
||||
props: {
|
||||
data: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
},
|
||||
},
|
||||
titles: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
},
|
||||
},
|
||||
buttonTexts: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
},
|
||||
},
|
||||
filterPlaceholder: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
filterMethod: {
|
||||
type: Function as PropType<(query: string, item: Record<string, any>) => boolean>,
|
||||
},
|
||||
leftDefaultChecked: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
},
|
||||
},
|
||||
rightDefaultChecked: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
},
|
||||
},
|
||||
renderContent: {
|
||||
type: Function as PropType<(h, option) => VNode>,
|
||||
},
|
||||
modelValue: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
},
|
||||
},
|
||||
format: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
},
|
||||
},
|
||||
filterable: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
props: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {
|
||||
label: 'label',
|
||||
key: 'key',
|
||||
disabled: 'disabled',
|
||||
}
|
||||
},
|
||||
},
|
||||
targetOrder: {
|
||||
type: String,
|
||||
default: 'original',
|
||||
validator: (val: string) => {
|
||||
return ['original', 'push', 'unshift'].includes(val)
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
emits: [
|
||||
UPDATE_MODEL_EVENT,
|
||||
CHANGE_EVENT,
|
||||
LEFT_CHECK_CHANGE_EVENT,
|
||||
RIGHT_CHECK_CHANGE_EVENT,
|
||||
],
|
||||
|
||||
setup(props, { emit, slots }) {
|
||||
const initData = reactive({
|
||||
leftChecked: [],
|
||||
rightChecked: [],
|
||||
})
|
||||
|
||||
const {
|
||||
propsKey,
|
||||
sourceData,
|
||||
targetData,
|
||||
} = useComputedData(props)
|
||||
|
||||
const {
|
||||
onSourceCheckedChange,
|
||||
onTargetCheckedChange,
|
||||
} = useCheckedChange(initData, emit)
|
||||
|
||||
const { addToLeft,
|
||||
addToRight,
|
||||
} = useMove(props, initData, propsKey, emit)
|
||||
|
||||
const leftPanel = ref(null)
|
||||
const rightPanel = ref(null)
|
||||
|
||||
const clearQuery = (which: 'left' | 'right') => {
|
||||
if (which === 'left') {
|
||||
leftPanel.value.query = ''
|
||||
} else if (which === 'right') {
|
||||
rightPanel.value.query = ''
|
||||
}
|
||||
}
|
||||
|
||||
const hasButtonTexts = computed(() => props.buttonTexts.length === 2)
|
||||
|
||||
const leftPanelTitle = computed(() => props.titles[0] || t('el.transfer.titles.0'))
|
||||
|
||||
const rightPanelTitle = computed(() => props.titles[1] || t('el.transfer.titles.1'))
|
||||
|
||||
const panelFilterPlaceholder = computed(() => props.filterPlaceholder || t('el.transfer.filterPlaceholder'))
|
||||
|
||||
watch(() => props.modelValue, (val: Key[]) => emit(UPDATE_MODEL_EVENT, val))
|
||||
|
||||
provide('defaultScopedSlots', computed(() => slots.default))
|
||||
|
||||
const {
|
||||
leftChecked,
|
||||
rightChecked,
|
||||
} = toRefs(initData)
|
||||
|
||||
return {
|
||||
sourceData,
|
||||
targetData,
|
||||
onSourceCheckedChange,
|
||||
onTargetCheckedChange,
|
||||
addToLeft,
|
||||
addToRight,
|
||||
|
||||
leftChecked,
|
||||
rightChecked,
|
||||
|
||||
hasButtonTexts,
|
||||
leftPanelTitle,
|
||||
rightPanelTitle,
|
||||
panelFilterPlaceholder,
|
||||
clearQuery,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
26
packages/transfer/src/option-content.vue
Normal file
26
packages/transfer/src/option-content.vue
Normal file
@ -0,0 +1,26 @@
|
||||
<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>
|
177
packages/transfer/src/transfer-panel.vue
Normal file
177
packages/transfer/src/transfer-panel.vue
Normal file
@ -0,0 +1,177 @@
|
||||
<template>
|
||||
<div class="el-transfer-panel">
|
||||
<p class="el-transfer-panel__header">
|
||||
<el-checkbox
|
||||
v-model="allChecked"
|
||||
:indeterminate="isIndeterminate"
|
||||
@change="handleAllCheckedChange"
|
||||
>
|
||||
{{ title }}
|
||||
<span>{{ checkedSummary }}</span>
|
||||
</el-checkbox>
|
||||
</p>
|
||||
|
||||
<div
|
||||
:class="['el-transfer-panel__body', hasFooter ? 'is-with-footer' : '']"
|
||||
>
|
||||
<el-input
|
||||
v-if="filterable"
|
||||
v-model="query"
|
||||
class="el-transfer-panel__filter"
|
||||
size="small"
|
||||
:placeholder="placeholder"
|
||||
@mouseenter="inputHover = true"
|
||||
@mouseleave="inputHover = false"
|
||||
>
|
||||
<template #prefix>
|
||||
<i :class="['el-input__icon', 'el-icon-' + inputIcon]" @click="clearQuery"></i>
|
||||
</template>
|
||||
</el-input>
|
||||
<el-checkbox-group
|
||||
v-show="!hasNoMatch && data.length > 0"
|
||||
v-model="checked"
|
||||
:class="{ 'is-filterable': filterable }"
|
||||
class="el-transfer-panel__list"
|
||||
>
|
||||
<el-checkbox
|
||||
v-for="item in filteredData"
|
||||
:key="item[keyProp]"
|
||||
class="el-transfer-panel__item"
|
||||
:label="item[keyProp]"
|
||||
:disabled="item[disabledProp]"
|
||||
>
|
||||
<option-content
|
||||
:option="item"
|
||||
:render-content="renderContent"
|
||||
:label-prop="labelProp"
|
||||
:key-prop="keyProp"
|
||||
/>
|
||||
</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>
|
||||
</div>
|
||||
<p v-if="hasFooter" class="el-transfer-panel__footer">
|
||||
<slot></slot>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, reactive, toRefs } from 'vue'
|
||||
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({
|
||||
name: 'ElTransferPanel',
|
||||
|
||||
components: {
|
||||
ElCheckboxGroup,
|
||||
ElCheckbox,
|
||||
ElInput,
|
||||
OptionContent,
|
||||
},
|
||||
|
||||
props: {
|
||||
data: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
},
|
||||
},
|
||||
renderContent: Function,
|
||||
placeholder: String,
|
||||
title: String,
|
||||
filterable: Boolean,
|
||||
format: Object,
|
||||
filterMethod: Function,
|
||||
defaultChecked: Array,
|
||||
props: Object,
|
||||
},
|
||||
|
||||
emits: [CHECKED_CHANGE_EVENT],
|
||||
|
||||
setup(props, { emit, slots }) {
|
||||
const initData = reactive({
|
||||
checked: [],
|
||||
allChecked: false,
|
||||
query: '',
|
||||
inputHover: false,
|
||||
checkChangeByUser: true,
|
||||
})
|
||||
|
||||
|
||||
const {
|
||||
labelProp,
|
||||
keyProp,
|
||||
disabledProp,
|
||||
filteredData,
|
||||
checkedSummary,
|
||||
isIndeterminate,
|
||||
handleAllCheckedChange,
|
||||
} = useCheck(props, initData, emit)
|
||||
|
||||
|
||||
const hasNoMatch = computed(() => initData.query.length > 0 && filteredData.value.length === 0)
|
||||
|
||||
const inputIcon = computed(() => {
|
||||
return initData.query.length > 0 && initData.inputHover
|
||||
? 'circle-close'
|
||||
: 'search'
|
||||
})
|
||||
|
||||
const hasFooter = computed(() => !!slots.default()[0].children.length)
|
||||
|
||||
const clearQuery = () => {
|
||||
if (inputIcon.value === 'circle-close') {
|
||||
initData.query = ''
|
||||
}
|
||||
}
|
||||
|
||||
const {
|
||||
checked,
|
||||
allChecked,
|
||||
query,
|
||||
inputHover,
|
||||
checkChangeByUser,
|
||||
} = toRefs(initData)
|
||||
|
||||
return {
|
||||
labelProp,
|
||||
keyProp,
|
||||
disabledProp,
|
||||
filteredData,
|
||||
checkedSummary,
|
||||
isIndeterminate,
|
||||
handleAllCheckedChange,
|
||||
|
||||
checked,
|
||||
allChecked,
|
||||
query,
|
||||
inputHover,
|
||||
checkChangeByUser,
|
||||
|
||||
hasNoMatch,
|
||||
inputIcon,
|
||||
hasFooter,
|
||||
clearQuery,
|
||||
|
||||
t,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
61
packages/transfer/src/transfer.d.ts
vendored
Normal file
61
packages/transfer/src/transfer.d.ts
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
import { VNode } from 'vue'
|
||||
|
||||
export declare type Key = string | number
|
||||
|
||||
export declare type DataItem = {
|
||||
key: Key
|
||||
label: string
|
||||
disabled: boolean
|
||||
}
|
||||
|
||||
export declare type Format = {
|
||||
noChecked: string
|
||||
hasChecked: string
|
||||
}
|
||||
|
||||
export declare type Props = {
|
||||
label: string
|
||||
key: string
|
||||
disabled: string
|
||||
}
|
||||
|
||||
export declare interface TransferProps {
|
||||
data: DataItem[]
|
||||
titles: [string, string]
|
||||
buttonTexts: [string, string]
|
||||
filterPlaceholder: string
|
||||
filterMethod: (query: string, item: DataItem) => boolean
|
||||
leftDefaultChecked: Key[]
|
||||
rightDefaultChecked: Key[]
|
||||
renderContent: (h, option) => VNode
|
||||
modelValue: Key[]
|
||||
format: Format
|
||||
filterable: boolean
|
||||
props: Props
|
||||
targetOrder: 'original' | 'push' | 'unshift'
|
||||
}
|
||||
|
||||
export declare interface TransferInitData {
|
||||
leftChecked: Key[]
|
||||
rightChecked: Key[]
|
||||
}
|
||||
|
||||
export declare interface TransferPanelProps {
|
||||
data: DataItem[]
|
||||
renderContent: (h, option) => VNode
|
||||
placeholder: string
|
||||
title: string
|
||||
filterable: boolean
|
||||
format: Format
|
||||
filterMethod: (query: string, item: DataItem) => boolean
|
||||
defaultChecked: Key[]
|
||||
props: Props
|
||||
}
|
||||
|
||||
export declare interface TransferPanelInitData {
|
||||
checked: Key[]
|
||||
allChecked: boolean
|
||||
query: string
|
||||
inputHover: boolean
|
||||
checkChangeByUser: boolean
|
||||
}
|
119
packages/transfer/src/useCheck.ts
Normal file
119
packages/transfer/src/useCheck.ts
Normal file
@ -0,0 +1,119 @@
|
||||
import { computed, watch } from 'vue'
|
||||
import { TransferPanelProps, TransferPanelInitData, Key } from './transfer'
|
||||
|
||||
import { CHECKED_CHANGE_EVENT } from './transfer-panel.vue'
|
||||
|
||||
export const useCheck = (props: TransferPanelProps, initData: TransferPanelInitData, emit) => {
|
||||
|
||||
const labelProp = computed(() => props.props.label || 'label')
|
||||
|
||||
const keyProp = computed(() => props.props.key || 'key')
|
||||
|
||||
const disabledProp = computed(() => props.props.disabled || 'disabled')
|
||||
|
||||
|
||||
const filteredData = computed(() => {
|
||||
return props.data.filter(item => {
|
||||
if (typeof props.filterMethod === 'function') {
|
||||
return props.filterMethod(initData.query, item)
|
||||
} else {
|
||||
const label = item[labelProp.value] || item[keyProp.value].toString()
|
||||
return label.toLowerCase().includes(initData.query.toLowerCase())
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
const checkableData = computed(() => filteredData.value.filter(item => !item[disabledProp.value]))
|
||||
|
||||
const checkedSummary = computed(() => {
|
||||
const checkedLength = initData.checked.length
|
||||
const dataLength = props.data.length
|
||||
const { noChecked, hasChecked } = props.format
|
||||
|
||||
if (noChecked && hasChecked) {
|
||||
return checkedLength > 0
|
||||
? hasChecked
|
||||
.replace(/\${checked}/g, checkedLength.toString())
|
||||
.replace(/\${total}/g, dataLength.toString())
|
||||
: noChecked.replace(/\${total}/g, dataLength.toString())
|
||||
} else {
|
||||
return `${ checkedLength }/${ dataLength }`
|
||||
}
|
||||
})
|
||||
|
||||
const isIndeterminate = computed(() => {
|
||||
const checkedLength = initData.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))
|
||||
}
|
||||
|
||||
const handleAllCheckedChange = (value: Key[]) => {
|
||||
initData.checked = value ? checkableData.value.map(item => item[keyProp.value]) : []
|
||||
}
|
||||
|
||||
watch(() => initData.checked, (val, oldVal) => {
|
||||
updateAllChecked()
|
||||
|
||||
if (initData.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
|
||||
}
|
||||
})
|
||||
|
||||
watch(checkableData, () => {
|
||||
updateAllChecked()
|
||||
})
|
||||
|
||||
|
||||
watch(() => props.data, () => {
|
||||
const checked = []
|
||||
const filteredDataKeys = filteredData.value.map(item => item[keyProp.value])
|
||||
initData.checked.forEach(item => {
|
||||
if (filteredDataKeys.includes(item)) {
|
||||
checked.push(item)
|
||||
}
|
||||
})
|
||||
initData.checkChangeByUser = false
|
||||
initData.checked = checked
|
||||
})
|
||||
|
||||
watch(() => props.defaultChecked, (val, oldVal) => {
|
||||
if (oldVal && val.length === oldVal.length && val.every(item => oldVal.includes(item))) return
|
||||
|
||||
const checked = []
|
||||
const checkableDataKeys = checkableData.value.map(item => item[keyProp.value])
|
||||
|
||||
val.forEach(item => {
|
||||
if (checkableDataKeys.includes(item)) {
|
||||
checked.push(item)
|
||||
}
|
||||
})
|
||||
initData.checkChangeByUser = false
|
||||
initData.checked = checked
|
||||
}, {
|
||||
immediate: true,
|
||||
})
|
||||
|
||||
|
||||
return {
|
||||
labelProp,
|
||||
keyProp,
|
||||
disabledProp,
|
||||
filteredData,
|
||||
checkableData,
|
||||
checkedSummary,
|
||||
isIndeterminate,
|
||||
updateAllChecked,
|
||||
handleAllCheckedChange,
|
||||
}
|
||||
}
|
22
packages/transfer/src/useCheckedChange.ts
Normal file
22
packages/transfer/src/useCheckedChange.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { TransferInitData, Key } from './transfer'
|
||||
|
||||
import { LEFT_CHECK_CHANGE_EVENT, RIGHT_CHECK_CHANGE_EVENT } from './index.vue'
|
||||
|
||||
export const useCheckedChange = (initData: TransferInitData, emit) => {
|
||||
const onSourceCheckedChange = (val: Key[], movedKeys: Key[]) => {
|
||||
initData.leftChecked = val
|
||||
if (movedKeys === undefined) return
|
||||
emit(LEFT_CHECK_CHANGE_EVENT, val, movedKeys)
|
||||
}
|
||||
|
||||
const onTargetCheckedChange = (val: Key[], movedKeys: Key[]) => {
|
||||
initData.rightChecked = val
|
||||
if (movedKeys === undefined) return
|
||||
emit(RIGHT_CHECK_CHANGE_EVENT, val, movedKeys)
|
||||
}
|
||||
|
||||
return {
|
||||
onSourceCheckedChange,
|
||||
onTargetCheckedChange,
|
||||
}
|
||||
}
|
34
packages/transfer/src/useComputedData.ts
Normal file
34
packages/transfer/src/useComputedData.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { computed } from 'vue'
|
||||
import { TransferProps } from './transfer'
|
||||
|
||||
export const useComputedData = (props: TransferProps) => {
|
||||
const propsKey = computed(() => props.props.key)
|
||||
|
||||
const dataObj = computed(() => {
|
||||
return props.data.reduce((o, cur) => (o[cur[propsKey.value]] = cur) && o, {})
|
||||
})
|
||||
|
||||
const sourceData = computed(() => {
|
||||
return props.data.filter(item => !props.modelValue.includes(item[propsKey.value]))
|
||||
})
|
||||
|
||||
const targetData = computed(() => {
|
||||
if (props.targetOrder === 'original') {
|
||||
return props.data.filter(item => props.modelValue.includes(item[propsKey.value]))
|
||||
} else {
|
||||
return props.modelValue.reduce((arr, cur) => {
|
||||
const val = dataObj.value[cur]
|
||||
if (val) {
|
||||
arr.push(val)
|
||||
}
|
||||
return arr
|
||||
}, [])
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
propsKey,
|
||||
sourceData,
|
||||
targetData,
|
||||
}
|
||||
}
|
45
packages/transfer/src/useMove.ts
Normal file
45
packages/transfer/src/useMove.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import { ComputedRef } from 'vue'
|
||||
import { UPDATE_MODEL_EVENT } from '../../utils/constants'
|
||||
import { CHANGE_EVENT } from './index.vue'
|
||||
import { TransferProps, TransferInitData, DataItem, Key } from './transfer'
|
||||
|
||||
export const useMove = (props: TransferProps, initData: TransferInitData, propsKey: ComputedRef<string>, emit) => {
|
||||
const _emit = (value, type: 'left' | 'right', checked: Key[]) => {
|
||||
emit(UPDATE_MODEL_EVENT, value)
|
||||
emit(CHANGE_EVENT, value, type, checked)
|
||||
}
|
||||
|
||||
const addToLeft = () => {
|
||||
const currentValue = props.modelValue.slice()
|
||||
|
||||
initData.rightChecked.forEach(item => {
|
||||
const index = currentValue.indexOf(item)
|
||||
if (index > -1) {
|
||||
currentValue.splice(index, 1)
|
||||
}
|
||||
})
|
||||
_emit(currentValue, 'left', initData.rightChecked)
|
||||
}
|
||||
|
||||
const addToRight = () => {
|
||||
let currentValue = props.modelValue.slice()
|
||||
|
||||
const itemsToBeMoved = props.data
|
||||
.filter((item: DataItem) => {
|
||||
const itemKey = item[propsKey.value]
|
||||
return initData.leftChecked.includes(itemKey) && !props.modelValue.includes(itemKey)
|
||||
})
|
||||
.map(item => item[propsKey.value])
|
||||
|
||||
currentValue = props.targetOrder === 'unshift'
|
||||
? itemsToBeMoved.concat(currentValue)
|
||||
: currentValue.concat(itemsToBeMoved)
|
||||
|
||||
_emit(currentValue, 'right', initData.leftChecked)
|
||||
}
|
||||
|
||||
return {
|
||||
addToLeft,
|
||||
addToRight,
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user