UNPKG

vxe-table-select-area

Version:

一个基于 vxe-table 的可区域选中复制、粘贴的组件

693 lines (646 loc) 21.4 kB
import XEUtils from 'xe-utils' import GlobalConfig from '../../v-x-e-table/src/conf' import UtilTools, { getFuncText } from '../../tools/utils' import { warnLog } from '../../tools/log' const defaultCompProps = { transfer: true } const componentDefaultModelProp = 'value' function isEmptyValue (cellValue) { return cellValue === null || cellValue === undefined || cellValue === '' } function getChangeEvent (renderOpts) { switch (renderOpts.name) { case 'input': case 'textarea': case '$input': case '$textarea': return 'input' } return 'change' } function parseDate (value, props) { return value && props.valueFormat ? XEUtils.toStringDate(value, props.valueFormat) : value } function getFormatDate (value, props, defaultFormat) { const { dateConfig = {} } = props return XEUtils.toDateString(parseDate(value, props), dateConfig.labelFormat || defaultFormat) } function getLabelFormatDate (value, props) { return getFormatDate(value, props, GlobalConfig.i18n(`vxe.input.date.labelFormat.${props.type}`)) } function getDefaultComponentName ({ name }) { return `vxe-${name.replace('$', '')}` } function handleConfirmFilter (params, checked, option) { const { $panel } = params $panel.changeOption({}, checked, option) } function getNativeAttrs ({ name, attrs }) { if (name === 'input') { attrs = Object.assign({ type: 'text' }, attrs) } return attrs } function getInputImmediateModel (renderOpts) { const { name, immediate, props } = renderOpts if (!immediate) { if (name === '$input') { const { type } = props || {} return !(!type || type === 'text' || type === 'number' || type === 'integer' || type === 'float') } if (name === 'input' || name === 'textarea' || name === '$textarea') { return false } return true } return immediate } function isImmediateCell (renderOpts, params) { return params.$type === 'cell' || getInputImmediateModel(renderOpts) } function getCellEditProps (renderOpts, params, value, defaultProps) { const { vSize } = params.$table return XEUtils.assign({ immediate: getInputImmediateModel(renderOpts) }, vSize ? { size: vSize } : {}, defaultCompProps, defaultProps, renderOpts.props, { [componentDefaultModelProp]: value }) } function getFilterProps (renderOpts, params, value, defaultProps) { const { vSize } = params.$table return XEUtils.assign(vSize ? { size: vSize } : {}, defaultCompProps, defaultProps, renderOpts.props, { [componentDefaultModelProp]: value }) } function getItemProps (renderOpts, params, value, defaultProps) { const { vSize } = params.$form return XEUtils.assign(vSize ? { size: vSize } : {}, defaultCompProps, defaultProps, renderOpts.props, { [componentDefaultModelProp]: value }) } function getCellLabelVNs (h, renderOpts, params, cellLabel) { const { placeholder } = renderOpts return [ h('span', { class: 'vxe-cell--label' }, placeholder && isEmptyValue(cellLabel) ? [ h('span', { class: 'vxe-cell--placeholder' }, UtilTools.formatText(getFuncText(placeholder), 1)) ] : UtilTools.formatText(cellLabel, 1)) ] } function getNativeOns (renderOpts, params) { const { nativeEvents } = renderOpts const nativeOns = {} XEUtils.objectEach(nativeEvents, (func, key) => { nativeOns[key] = function (...args) { func(params, ...args) } }) return nativeOns } function getOns (renderOpts, params, inputFunc, changeFunc) { const { name, events } = renderOpts const modelEvent = 'input' const changeEvent = getChangeEvent(renderOpts) const isSameEvent = changeEvent === modelEvent const ons = {} XEUtils.objectEach(events, (func, key) => { ons[key] = function (...args) { func(params, ...args) } }) if (inputFunc) { ons[modelEvent] = function (targetEvnt) { // 对输入框进行优化 inputFunc(name === '$input' || name === '$textarea' ? targetEvnt.value : targetEvnt) if (events && events[modelEvent]) { events[modelEvent](params, targetEvnt) } if (isSameEvent && changeFunc) { changeFunc(targetEvnt) } } } if (!isSameEvent && changeFunc) { ons[changeEvent] = function (...args) { changeFunc(...args) if (events && events[changeEvent]) { events[changeEvent](params, ...args) } } } return ons } function getEditOns (renderOpts, params) { const { $table, row, column } = params const { name } = renderOpts const { model } = column const isImmediate = isImmediateCell(renderOpts, params) return getOns(renderOpts, params, (cellValue) => { // 处理 model 值双向绑定 if (isImmediate) { UtilTools.setCellValue(row, column, cellValue) } else { model.update = true model.value = cellValue } }, (eventParams) => { // 处理 change 事件相关逻辑 if (!isImmediate && (name === '$input' || name === '$textarea')) { $table.updateStatus(params, eventParams.value) } else { $table.updateStatus(params) } }) } function getFilterOns (renderOpts, params, option) { return getOns(renderOpts, params, (value) => { // 处理 model 值双向绑定 option.data = value }, () => { handleConfirmFilter(params, !XEUtils.eqNull(option.data), option) }) } function getItemOns (renderOpts, params) { const { $form, data, property } = params return getOns(renderOpts, params, (value) => { // 处理 model 值双向绑定 XEUtils.set(data, property, value) }, () => { // 处理 change 事件相关逻辑 $form.updateStatus(params) }) } function getNativeEditOns (renderOpts, params) { const { $table, row, column } = params const { model } = column return getOns(renderOpts, params, (evnt) => { // 处理 model 值双向绑定 const cellValue = evnt.target.value if (isImmediateCell(renderOpts, params)) { UtilTools.setCellValue(row, column, cellValue) } else { model.update = true model.value = cellValue } }, (evnt) => { // 处理 change 事件相关逻辑 const cellValue = evnt.target.value $table.updateStatus(params, cellValue) }) } function getNativeFilterOns (renderOpts, params, option) { return getOns(renderOpts, params, (evnt) => { // 处理 model 值双向绑定 option.data = evnt.target.value }, () => { handleConfirmFilter(params, !XEUtils.eqNull(option.data), option) }) } function getNativeItemOns (renderOpts, params) { const { $form, data, property } = params return getOns(renderOpts, params, (evnt) => { // 处理 model 值双向绑定 const itemValue = evnt.target.value XEUtils.set(data, property, itemValue) }, () => { // 处理 change 事件相关逻辑 $form.updateStatus(params) }) } /** * 单元格可编辑渲染-原生的标签 * input、textarea、select */ function nativeEditRender (h, renderOpts, params) { const { row, column } = params const { name } = renderOpts const attrs = getNativeAttrs(renderOpts) const cellValue = isImmediateCell(renderOpts, params) ? UtilTools.getCellValue(row, column) : column.model.value return [ h(name, { class: `vxe-default-${name}`, attrs, domProps: { value: cellValue }, on: getNativeEditOns(renderOpts, params) }) ] } function defaultEditRender (h, renderOpts, params) { const { row, column } = params const cellValue = UtilTools.getCellValue(row, column) return [ h(getDefaultComponentName(renderOpts), { props: getCellEditProps(renderOpts, params, cellValue), on: getEditOns(renderOpts, params), nativeOn: getNativeOns(renderOpts, params) }) ] } function defaultButtonEditRender (h, renderOpts, params) { return [ h('vxe-button', { props: getCellEditProps(renderOpts, params), on: getOns(renderOpts, params), nativeOn: getNativeOns(renderOpts, params) }) ] } function defaultButtonsEditRender (h, renderOpts, params) { return renderOpts.children.map(childRenderOpts => defaultButtonEditRender(h, childRenderOpts, params)[0]) } function renderNativeOptgroups (h, renderOpts, params, renderOptionsMethods) { const { optionGroups, optionGroupProps = {} } = renderOpts const groupOptions = optionGroupProps.options || 'options' const groupLabel = optionGroupProps.label || 'label' return optionGroups.map((group, gIndex) => { return h('optgroup', { key: gIndex, domProps: { label: group[groupLabel] } }, renderOptionsMethods(h, group[groupOptions], renderOpts, params)) }) } /** * 渲染原生的 option 标签 */ function renderNativeOptions (h, options, renderOpts, params) { const { optionProps = {} } = renderOpts const { row, column } = params const labelProp = optionProps.label || 'label' const valueProp = optionProps.value || 'value' const disabledProp = optionProps.disabled || 'disabled' const cellValue = isImmediateCell(renderOpts, params) ? UtilTools.getCellValue(row, column) : column.model.value return options.map((option, oIndex) => { return h('option', { key: oIndex, attrs: { value: option[valueProp], disabled: option[disabledProp] }, domProps: { /* eslint-disable eqeqeq */ selected: option[valueProp] == cellValue } }, option[labelProp]) }) } function nativeFilterRender (h, renderOpts, params) { const { column } = params const { name } = renderOpts const attrs = getNativeAttrs(renderOpts) return column.filters.map((option, oIndex) => { return h(name, { key: oIndex, class: `vxe-default-${name}`, attrs, domProps: { value: option.data }, on: getNativeFilterOns(renderOpts, params, option) }) }) } function defaultFilterRender (h, renderOpts, params) { const { column } = params return column.filters.map((option, oIndex) => { const optionValue = option.data return h(getDefaultComponentName(renderOpts), { key: oIndex, props: getFilterProps(renderOpts, renderOpts, optionValue), on: getFilterOns(renderOpts, params, option) }) }) } function handleFilterMethod ({ option, row, column }) { const { data } = option const cellValue = XEUtils.get(row, column.property) /* eslint-disable eqeqeq */ return cellValue == data } function nativeSelectEditRender (h, renderOpts, params) { return [ h('select', { class: 'vxe-default-select', attrs: getNativeAttrs(renderOpts), on: getNativeEditOns(renderOpts, params) }, renderOpts.optionGroups ? renderNativeOptgroups(h, renderOpts, params, renderNativeOptions) : renderNativeOptions(h, renderOpts.options, renderOpts, params)) ] } function defaultSelectEditRender (h, renderOpts, params) { const { row, column } = params const { options, optionProps, optionGroups, optionGroupProps } = renderOpts const cellValue = UtilTools.getCellValue(row, column) return [ h(getDefaultComponentName(renderOpts), { props: getCellEditProps(renderOpts, params, cellValue, { options, optionProps, optionGroups, optionGroupProps }), on: getEditOns(renderOpts, params) }) ] } function getSelectCellValue (renderOpts, { row, column }) { const { props = {}, options, optionGroups, optionProps = {}, optionGroupProps = {} } = renderOpts const cellValue = XEUtils.get(row, column.property) let selectItem const labelProp = optionProps.label || 'label' const valueProp = optionProps.value || 'value' if (!isEmptyValue(cellValue)) { return XEUtils.map(props.multiple ? cellValue : [cellValue], optionGroups ? (value) => { const groupOptions = optionGroupProps.options || 'options' for (let index = 0; index < optionGroups.length; index++) { /* eslint-disable eqeqeq */ selectItem = XEUtils.find(optionGroups[index][groupOptions], item => item[valueProp] == value) if (selectItem) { break } } return selectItem ? selectItem[labelProp] : value } : (value) => { /* eslint-disable eqeqeq */ selectItem = XEUtils.find(options, item => item[valueProp] == value) return selectItem ? selectItem[labelProp] : value }).join(', ') } return null } /** * 渲染表单-项 * 用于渲染原生的标签 */ function nativeItemRender (h, renderOpts, params) { const { data, property } = params const { name } = renderOpts const attrs = getNativeAttrs(renderOpts) const itemValue = XEUtils.get(data, property) return [ h(name, { class: `vxe-default-${name}`, attrs, domProps: attrs && name === 'input' && (attrs.type === 'submit' || attrs.type === 'reset') ? null : { value: itemValue }, on: getNativeItemOns(renderOpts, params) }) ] } function defaultItemRender (h, renderOpts, params) { const { data, property } = params const itemValue = XEUtils.get(data, property) return [ h(getDefaultComponentName(renderOpts), { props: getItemProps(renderOpts, params, itemValue), on: getItemOns(renderOpts, params), nativeOn: getNativeOns(renderOpts, params) }) ] } function defaultButtonItemRender (h, renderOpts, params) { return [ h('vxe-button', { props: getItemProps(renderOpts, params), on: getOns(renderOpts, params), nativeOn: getNativeOns(renderOpts, params) }) ] } function defaultButtonsItemRender (h, renderOpts, params) { return renderOpts.children.map(childRenderOpts => defaultButtonItemRender(h, childRenderOpts, params)[0]) } /** * 渲染原生的 select 标签 */ function renderNativeFormOptions (h, options, renderOpts, params) { const { data, property } = params const { optionProps = {} } = renderOpts const labelProp = optionProps.label || 'label' const valueProp = optionProps.value || 'value' const disabledProp = optionProps.disabled || 'disabled' const cellValue = XEUtils.get(data, property) return options.map((item, oIndex) => { return h('option', { key: oIndex, attrs: { value: item[valueProp], disabled: item[disabledProp] }, domProps: { /* eslint-disable eqeqeq */ selected: item[valueProp] == cellValue } }, item[labelProp]) }) } function handleExportSelectMethod (params) { const { row, column, options } = params return options.original ? UtilTools.getCellValue(row, column) : getSelectCellValue(column.editRender || column.cellRender, params) } /** * 渲染表单-项中 * 单选框和复选框 */ function defaultFormItemRadioAndCheckboxRender (h, renderOpts, params) { const { options, optionProps = {} } = renderOpts const { data, property } = params const labelProp = optionProps.label || 'label' const valueProp = optionProps.value || 'value' const disabledProp = optionProps.disabled || 'disabled' const itemValue = XEUtils.get(data, property) const name = getDefaultComponentName(renderOpts) // 如果是分组 if (options) { return [ h(`${name}-group`, { props: getItemProps(renderOpts, params, itemValue), on: getItemOns(renderOpts, params), nativeOn: getNativeOns(renderOpts, params) }, options.map((item, index) => { return h(name, { key: index, props: { label: item[valueProp], content: item[labelProp], disabled: item[disabledProp] } }) })) ] } return [ h(name, { props: getItemProps(renderOpts, params, itemValue), on: getItemOns(renderOpts, params), nativeOn: getNativeOns(renderOpts, params) }) ] } /** * 内置的组件渲染 */ const renderMap = { input: { autofocus: 'input', renderEdit: nativeEditRender, renderDefault: nativeEditRender, renderFilter: nativeFilterRender, defaultFilterMethod: handleFilterMethod, renderItemContent: nativeItemRender }, textarea: { autofocus: 'textarea', renderEdit: nativeEditRender, renderItemContent: nativeItemRender }, select: { renderEdit: nativeSelectEditRender, renderDefault: nativeSelectEditRender, renderCell (h, renderOpts, params) { return getCellLabelVNs(h, renderOpts, params, getSelectCellValue(renderOpts, params)) }, renderFilter (h, renderOpts, params) { const { column } = params return column.filters.map((option, oIndex) => { return h('select', { key: oIndex, class: 'vxe-default-select', attrs: getNativeAttrs(renderOpts), on: getNativeFilterOns(renderOpts, params, option) }, renderOpts.optionGroups ? renderNativeOptgroups(h, renderOpts, params, renderNativeOptions) : renderNativeOptions(h, renderOpts.options, renderOpts, params)) }) }, defaultFilterMethod: handleFilterMethod, renderItemContent (h, renderOpts, params) { return [ h('select', { class: 'vxe-default-select', attrs: getNativeAttrs(renderOpts), on: getNativeItemOns(renderOpts, params) }, renderOpts.optionGroups ? renderNativeOptgroups(h, renderOpts, params, renderNativeFormOptions) : renderNativeFormOptions(h, renderOpts.options, renderOpts, params)) ] }, cellExportMethod: handleExportSelectMethod }, $input: { autofocus: '.vxe-input--inner', renderEdit: defaultEditRender, renderCell (h, renderOpts, params) { const { props = {} } = renderOpts const { row, column } = params const digits = props.digits || GlobalConfig.input.digits let cellValue = XEUtils.get(row, column.property) if (cellValue) { switch (props.type) { case 'date': case 'week': case 'month': case 'year': cellValue = getLabelFormatDate(cellValue, props) break case 'float': cellValue = XEUtils.toFixed(XEUtils.floor(cellValue, digits), digits) break } } return getCellLabelVNs(h, renderOpts, params, cellValue) }, renderDefault: defaultEditRender, renderFilter: defaultFilterRender, defaultFilterMethod: handleFilterMethod, renderItemContent: defaultItemRender }, $textarea: { autofocus: '.vxe-textarea--inner', renderItemContent: defaultItemRender }, $button: { renderDefault: defaultButtonEditRender, renderItemContent: defaultButtonItemRender }, $buttons: { renderDefault: defaultButtonsEditRender, renderItemContent: defaultButtonsItemRender }, $select: { autofocus: '.vxe-input--inner', renderEdit: defaultSelectEditRender, renderDefault: defaultSelectEditRender, renderCell (h, renderOpts, params) { return getCellLabelVNs(h, renderOpts, params, getSelectCellValue(renderOpts, params)) }, renderFilter (h, renderOpts, params) { const { column } = params const { options, optionProps, optionGroups, optionGroupProps } = renderOpts const nativeOn = getNativeOns(renderOpts, params) return column.filters.map((option, oIndex) => { const optionValue = option.data return h(getDefaultComponentName(renderOpts), { key: oIndex, props: getFilterProps(renderOpts, params, optionValue, { options, optionProps, optionGroups, optionGroupProps }), on: getFilterOns(renderOpts, params, option), nativeOn }) }) }, defaultFilterMethod: handleFilterMethod, renderItemContent (h, renderOpts, params) { const { data, property } = params const { options, optionProps, optionGroups, optionGroupProps } = renderOpts const itemValue = XEUtils.get(data, property) return [ h(getDefaultComponentName(renderOpts), { props: getItemProps(renderOpts, params, itemValue, { options, optionProps, optionGroups, optionGroupProps }), on: getItemOns(renderOpts, params), nativeOn: getNativeOns(renderOpts, params) }) ] }, cellExportMethod: handleExportSelectMethod }, $radio: { autofocus: '.vxe-radio--input', renderItemContent: defaultFormItemRadioAndCheckboxRender }, $checkbox: { autofocus: '.vxe-checkbox--input', renderItemContent: defaultFormItemRadioAndCheckboxRender }, $switch: { autofocus: '.vxe-switch--button', renderEdit: defaultEditRender, renderDefault: defaultEditRender, renderItemContent: defaultItemRender } } /** * 全局渲染器 */ export const renderer = { mixin (map) { XEUtils.each(map, (options, name) => renderer.add(name, options)) return renderer }, get (name) { return renderMap[name] || null }, add (name, options) { if (name && options) { const renders = renderMap[name] if (renders) { // 检测是否覆盖 if (process.env.VUE_APP_VXE_TABLE_ENV === 'development') { XEUtils.each(options, (val, key) => { if (!XEUtils.eqNull(renders[key]) && renders[key] !== val) { warnLog('vxe.error.coverProp', [`Renderer.${name}`, key]) } }) } Object.assign(renders, options) } else { renderMap[name] = options } } return renderer }, delete (name) { delete renderMap[name] return renderer } }