35 changed files with 1099 additions and 454 deletions
@ -0,0 +1,55 @@ |
|||||
|
<template> |
||||
|
<EditTableHeaderCell v-if="getIsEdit"> |
||||
|
{{ getTitle }} |
||||
|
</EditTableHeaderCell> |
||||
|
<span v-else>{{ getTitle }}</span> |
||||
|
<BasicHelp v-if="getHelpMessage" :text="getHelpMessage" :class="`${prefixCls}__help`" /> |
||||
|
</template> |
||||
|
<script lang="ts"> |
||||
|
import type { PropType } from 'vue'; |
||||
|
import type { BasicColumn } from '../types/table'; |
||||
|
|
||||
|
import { defineComponent, computed } from 'vue'; |
||||
|
|
||||
|
import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent'; |
||||
|
import { useDesign } from '/@/hooks/web/useDesign'; |
||||
|
export default defineComponent({ |
||||
|
name: 'TableHeaderCell', |
||||
|
components: { |
||||
|
EditTableHeaderCell: createAsyncComponent(() => import('./EditTableHeaderIcon.vue')), |
||||
|
BasicHelp: createAsyncComponent(() => import('/@/components/Basic/src/BasicHelp.vue')), |
||||
|
}, |
||||
|
props: { |
||||
|
column: { |
||||
|
type: Object as PropType<BasicColumn>, |
||||
|
default: {}, |
||||
|
}, |
||||
|
}, |
||||
|
setup(props) { |
||||
|
const { prefixCls } = useDesign('basic-table-header-cell'); |
||||
|
const getIsEdit = computed(() => { |
||||
|
return !!props.column?.edit; |
||||
|
}); |
||||
|
|
||||
|
const getTitle = computed(() => { |
||||
|
return props.column?.customTitle; |
||||
|
}); |
||||
|
|
||||
|
const getHelpMessage = computed(() => { |
||||
|
return props.column?.helpMessage; |
||||
|
}); |
||||
|
|
||||
|
return { prefixCls, getIsEdit, getTitle, getHelpMessage }; |
||||
|
}, |
||||
|
}); |
||||
|
</script> |
||||
|
<style lang="less"> |
||||
|
@prefix-cls: ~'@{namespace}-basic-table-header-cell'; |
||||
|
|
||||
|
.@{prefix-cls} { |
||||
|
&__help { |
||||
|
margin-left: 8px; |
||||
|
color: rgba(0, 0, 0, 0.65) !important; |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,33 @@ |
|||||
|
import type { FunctionalComponent, defineComponent } from 'vue'; |
||||
|
import type { ComponentType } from '../../types/componentType'; |
||||
|
import { componentMap } from '/@/components/Table/src/componentMap'; |
||||
|
|
||||
|
import { Popover } from 'ant-design-vue'; |
||||
|
import { h } from 'vue'; |
||||
|
|
||||
|
export interface ComponentProps { |
||||
|
component: ComponentType; |
||||
|
rule: boolean; |
||||
|
popoverVisible: boolean; |
||||
|
ruleMessage: string; |
||||
|
} |
||||
|
|
||||
|
export const CellComponent: FunctionalComponent = ( |
||||
|
{ component = 'Input', rule = true, ruleMessage, popoverVisible }: ComponentProps, |
||||
|
{ attrs } |
||||
|
) => { |
||||
|
const Comp = componentMap.get(component) as typeof defineComponent; |
||||
|
|
||||
|
const DefaultComp = h(Comp, attrs); |
||||
|
if (!rule) { |
||||
|
return DefaultComp; |
||||
|
} |
||||
|
return h( |
||||
|
Popover, |
||||
|
{ overlayClassName: 'edit-cell-rule-popover', visible: !!popoverVisible }, |
||||
|
{ |
||||
|
default: () => DefaultComp, |
||||
|
content: () => ruleMessage, |
||||
|
} |
||||
|
); |
||||
|
}; |
||||
@ -0,0 +1,359 @@ |
|||||
|
<template> |
||||
|
<div :class="prefixCls"> |
||||
|
<div v-show="!isEdit" :class="`${prefixCls}__normal`" @click="handleEdit"> |
||||
|
{{ value || ' ' }} |
||||
|
<FormOutlined :class="`${prefixCls}__normal-icon`" v-if="!column.editRow" /> |
||||
|
</div> |
||||
|
|
||||
|
<div v-if="isEdit" :class="`${prefixCls}__wrapper`" v-click-outside="onClickOutside"> |
||||
|
<CellComponent |
||||
|
v-bind="getComponentProps" |
||||
|
:component="getComponent" |
||||
|
:style="getWrapperStyle" |
||||
|
:popoverVisible="getRuleVisible" |
||||
|
:rule="getRule" |
||||
|
:ruleMessage="ruleMessage" |
||||
|
size="small" |
||||
|
ref="elRef" |
||||
|
@change="handleChange" |
||||
|
@options-change="handleOptionsChange" |
||||
|
@pressEnter="handleSubmit" |
||||
|
> |
||||
|
</CellComponent> |
||||
|
<div :class="`${prefixCls}__action`" v-if="!getRowEditable"> |
||||
|
<CheckOutlined :class="[`${prefixCls}__icon`, 'mx-2']" @click="handleSubmit" /> |
||||
|
<CloseOutlined :class="`${prefixCls}__icon `" @click="handleCancel" /> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
<script lang="ts"> |
||||
|
import type { CSSProperties, PropType } from 'vue'; |
||||
|
import type { BasicColumn } from '../../types/table'; |
||||
|
|
||||
|
import { defineComponent, ref, unref, nextTick, computed, watchEffect, toRaw } from 'vue'; |
||||
|
import { FormOutlined, CloseOutlined, CheckOutlined } from '@ant-design/icons-vue'; |
||||
|
|
||||
|
import { useDesign } from '/@/hooks/web/useDesign'; |
||||
|
import { isString, isBoolean, isFunction, isNumber, isArray } from '/@/utils/is'; |
||||
|
import clickOutside from '/@/directives/clickOutside'; |
||||
|
|
||||
|
import { CellComponent } from './CellComponent'; |
||||
|
import { useTableContext } from '../../hooks/useTableContext'; |
||||
|
import { propTypes } from '/@/utils/propTypes'; |
||||
|
import { createPlaceholderMessage } from './helper'; |
||||
|
|
||||
|
import type { EditRecordRow } from './index'; |
||||
|
|
||||
|
export default defineComponent({ |
||||
|
name: 'EditableCell', |
||||
|
components: { FormOutlined, CloseOutlined, CheckOutlined, CellComponent }, |
||||
|
props: { |
||||
|
value: { |
||||
|
type: [String, Number, Boolean, Object] as PropType<string | number | boolean | Recordable>, |
||||
|
default: '', |
||||
|
}, |
||||
|
record: { |
||||
|
type: Object as PropType<EditRecordRow>, |
||||
|
}, |
||||
|
column: { |
||||
|
type: Object as PropType<BasicColumn>, |
||||
|
default: {}, |
||||
|
}, |
||||
|
index: propTypes.number, |
||||
|
}, |
||||
|
directives: { |
||||
|
clickOutside, |
||||
|
}, |
||||
|
|
||||
|
setup(props) { |
||||
|
const table = useTableContext(); |
||||
|
const isEdit = ref(false); |
||||
|
const elRef = ref<any>(null); |
||||
|
const ruleVisible = ref(false); |
||||
|
const ruleMessage = ref(''); |
||||
|
const optionsRef = ref<LabelValueOptions>([]); |
||||
|
const currentValueRef = ref<any>(props.value); |
||||
|
const defaultValueRef = ref<any>(props.value); |
||||
|
|
||||
|
const { prefixCls } = useDesign('editable-cell'); |
||||
|
|
||||
|
const getComponent = computed(() => props.column?.editComponent || 'Input'); |
||||
|
const getRule = computed(() => props.column?.editRule); |
||||
|
|
||||
|
const getRuleVisible = computed(() => { |
||||
|
return unref(ruleMessage) && unref(ruleVisible); |
||||
|
}); |
||||
|
|
||||
|
const getIsCheckComp = computed(() => { |
||||
|
const component = unref(getComponent); |
||||
|
return ['Checkbox', 'Switch'].includes(component); |
||||
|
}); |
||||
|
|
||||
|
const getComponentProps = computed(() => { |
||||
|
const compProps = props.column?.editComponentProps ?? {}; |
||||
|
const component = unref(getComponent); |
||||
|
const apiSelectProps: Recordable = {}; |
||||
|
if (component === 'ApiSelect') { |
||||
|
apiSelectProps.cache = true; |
||||
|
} |
||||
|
|
||||
|
const isCheckValue = unref(getIsCheckComp); |
||||
|
|
||||
|
const valueField = isCheckValue ? 'checked' : 'value'; |
||||
|
const val = unref(currentValueRef); |
||||
|
|
||||
|
const value = isCheckValue ? (isNumber(val) && isBoolean(val) ? val : !!val) : val; |
||||
|
|
||||
|
return { |
||||
|
placeholder: createPlaceholderMessage(unref(getComponent)), |
||||
|
...apiSelectProps, |
||||
|
...compProps, |
||||
|
[valueField]: value, |
||||
|
}; |
||||
|
}); |
||||
|
|
||||
|
const getValues = computed(() => { |
||||
|
const { editComponentProps, editValueMap } = props.column; |
||||
|
|
||||
|
const value = unref(currentValueRef); |
||||
|
|
||||
|
if (editValueMap && isFunction(editValueMap)) { |
||||
|
return editValueMap(value); |
||||
|
} |
||||
|
|
||||
|
const component = unref(getComponent); |
||||
|
if (!component.includes('Select')) { |
||||
|
return value; |
||||
|
} |
||||
|
const options: LabelValueOptions = editComponentProps?.options ?? (unref(optionsRef) || []); |
||||
|
const option = options.find((item) => `${item.value}` === `${value}`); |
||||
|
return option?.label; |
||||
|
}); |
||||
|
|
||||
|
const getWrapperStyle = computed( |
||||
|
(): CSSProperties => { |
||||
|
if (unref(getIsCheckComp) || unref(getRowEditable)) { |
||||
|
return {}; |
||||
|
} |
||||
|
return { |
||||
|
width: 'calc(100% - 48px)', |
||||
|
}; |
||||
|
} |
||||
|
); |
||||
|
|
||||
|
const getRowEditable = computed(() => { |
||||
|
const { editable } = props.record || {}; |
||||
|
return !!editable; |
||||
|
}); |
||||
|
|
||||
|
watchEffect(() => { |
||||
|
defaultValueRef.value = props.value; |
||||
|
}); |
||||
|
|
||||
|
watchEffect(() => { |
||||
|
const { editable } = props.column; |
||||
|
if (isBoolean(editable) || isBoolean(unref(getRowEditable))) { |
||||
|
isEdit.value = !!editable || unref(getRowEditable); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
function handleEdit() { |
||||
|
if (unref(getRowEditable) || unref(props.column?.editRow)) return; |
||||
|
ruleMessage.value = ''; |
||||
|
isEdit.value = true; |
||||
|
nextTick(() => { |
||||
|
const el = unref(elRef); |
||||
|
el?.focus?.(); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
async function handleChange(e: any) { |
||||
|
const component = unref(getComponent); |
||||
|
if (e?.target && Reflect.has(e.target, 'value')) { |
||||
|
currentValueRef.value = (e as ChangeEvent).target.value; |
||||
|
} |
||||
|
if (component === 'Checkbox') { |
||||
|
currentValueRef.value = (e as ChangeEvent).target.checked; |
||||
|
} else if (isString(e) || isBoolean(e) || isNumber(e)) { |
||||
|
currentValueRef.value = e; |
||||
|
} |
||||
|
handleSubmiRule(); |
||||
|
} |
||||
|
|
||||
|
async function handleSubmiRule() { |
||||
|
const { column, record } = props; |
||||
|
const { editRule } = column; |
||||
|
const currentValue = unref(currentValueRef); |
||||
|
|
||||
|
if (editRule) { |
||||
|
if (isBoolean(editRule) && !currentValue && !isNumber(currentValue)) { |
||||
|
ruleVisible.value = true; |
||||
|
const component = unref(getComponent); |
||||
|
const message = createPlaceholderMessage(component); |
||||
|
ruleMessage.value = message; |
||||
|
return false; |
||||
|
} |
||||
|
if (isFunction(editRule)) { |
||||
|
const res = await editRule(currentValue, record as Recordable); |
||||
|
if (!!res) { |
||||
|
ruleMessage.value = res; |
||||
|
ruleVisible.value = true; |
||||
|
return false; |
||||
|
} else { |
||||
|
ruleMessage.value = ''; |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
ruleMessage.value = ''; |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
async function handleSubmit() { |
||||
|
const isPass = await handleSubmiRule(); |
||||
|
if (!isPass) return false; |
||||
|
const { column, index } = props; |
||||
|
const { key, dataIndex } = column; |
||||
|
// const value = unref(currentValueRef); |
||||
|
if (!key || !dataIndex) return; |
||||
|
const dataKey = (dataIndex || key) as string; |
||||
|
|
||||
|
const record = await table.updateTableData(index, dataKey, unref(getValues)); |
||||
|
table.emit?.('edit-end', { record, index, key, value: unref(currentValueRef) }); |
||||
|
isEdit.value = false; |
||||
|
} |
||||
|
|
||||
|
function handleCancel() { |
||||
|
isEdit.value = false; |
||||
|
currentValueRef.value = defaultValueRef.value; |
||||
|
table.emit?.('edit-cancel', unref(currentValueRef)); |
||||
|
} |
||||
|
|
||||
|
function onClickOutside() { |
||||
|
if (props.column?.editable || unref(getRowEditable)) { |
||||
|
return; |
||||
|
} |
||||
|
const component = unref(getComponent); |
||||
|
|
||||
|
if (component.includes('Input')) { |
||||
|
handleCancel(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// only ApiSelect |
||||
|
function handleOptionsChange(options: LabelValueOptions) { |
||||
|
optionsRef.value = options; |
||||
|
} |
||||
|
|
||||
|
function initCbs(cbs: 'submitCbs' | 'validCbs' | 'cancelCbs', handle: Fn) { |
||||
|
if (props.record) { |
||||
|
/* eslint-disable */ |
||||
|
isArray(props.record[cbs]) |
||||
|
? props.record[cbs].push(handle) |
||||
|
: (props.record[cbs] = [handle]); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (props.record) { |
||||
|
initCbs('submitCbs', handleSubmit); |
||||
|
initCbs('validCbs', handleSubmiRule); |
||||
|
initCbs('cancelCbs', handleCancel); |
||||
|
|
||||
|
/* eslint-disable */ |
||||
|
props.record.onCancelEdit = () => { |
||||
|
isArray(props.record?.cancelCbs) && props.record?.cancelCbs.forEach((fn) => fn()); |
||||
|
}; |
||||
|
/* eslint-disable */ |
||||
|
props.record.onSubmitEdit = async () => { |
||||
|
if (isArray(props.record?.submitCbs)) { |
||||
|
const validFns = props.record?.validCbs || []; |
||||
|
|
||||
|
const res = await Promise.all(validFns.map((fn) => fn())); |
||||
|
const pass = res.every((item) => !!item); |
||||
|
|
||||
|
if (!pass) return; |
||||
|
const submitFns = props.record?.submitCbs || []; |
||||
|
submitFns.forEach((fn) => fn()); |
||||
|
return true; |
||||
|
} |
||||
|
// isArray(props.record?.submitCbs) && props.record?.submitCbs.forEach((fn) => fn()); |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
return { |
||||
|
isEdit, |
||||
|
prefixCls, |
||||
|
handleEdit, |
||||
|
currentValueRef, |
||||
|
handleSubmit, |
||||
|
handleChange, |
||||
|
handleCancel, |
||||
|
elRef, |
||||
|
getComponent, |
||||
|
getRule, |
||||
|
onClickOutside, |
||||
|
ruleMessage, |
||||
|
getRuleVisible, |
||||
|
getComponentProps, |
||||
|
handleOptionsChange, |
||||
|
getWrapperStyle, |
||||
|
getRowEditable, |
||||
|
}; |
||||
|
}, |
||||
|
}); |
||||
|
</script> |
||||
|
<style lang="less"> |
||||
|
@prefix-cls: ~'@{namespace}-editable-cell'; |
||||
|
|
||||
|
.edit-cell-rule-popover { |
||||
|
// .ant-popover-arrow { |
||||
|
// // border-color: transparent @error-color @error-color transparent !important; |
||||
|
// } |
||||
|
|
||||
|
.ant-popover-inner-content { |
||||
|
padding: 4px 8px; |
||||
|
color: @error-color; |
||||
|
// border: 1px solid @error-color; |
||||
|
border-radius: 2px; |
||||
|
} |
||||
|
} |
||||
|
.@{prefix-cls} { |
||||
|
position: relative; |
||||
|
|
||||
|
&__wrapper { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
&__icon { |
||||
|
&:hover { |
||||
|
transform: scale(1.2); |
||||
|
|
||||
|
svg { |
||||
|
color: @primary-color; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
&__normal { |
||||
|
padding-right: 48px; |
||||
|
|
||||
|
&-icon { |
||||
|
position: absolute; |
||||
|
top: 4px; |
||||
|
right: 0; |
||||
|
display: none; |
||||
|
width: 20px; |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
&:hover { |
||||
|
.@{prefix-cls}__normal-icon { |
||||
|
display: inline-block; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,26 @@ |
|||||
|
import { ComponentType } from '../../types/componentType'; |
||||
|
import { useI18n } from '/@/hooks/web/useI18n'; |
||||
|
|
||||
|
const { t } = useI18n(); |
||||
|
|
||||
|
/** |
||||
|
* @description: 生成placeholder |
||||
|
*/ |
||||
|
export function createPlaceholderMessage(component: ComponentType) { |
||||
|
if (component.includes('Input')) { |
||||
|
return t('component.form.input'); |
||||
|
} |
||||
|
if (component.includes('Picker')) { |
||||
|
return t('component.form.choose'); |
||||
|
} |
||||
|
|
||||
|
if ( |
||||
|
component.includes('Select') || |
||||
|
component.includes('Checkbox') || |
||||
|
component.includes('Radio') || |
||||
|
component.includes('Switch') |
||||
|
) { |
||||
|
return t('component.form.choose'); |
||||
|
} |
||||
|
return ''; |
||||
|
} |
||||
@ -0,0 +1,52 @@ |
|||||
|
import type { BasicColumn } from '/@/components/Table/src/types/table'; |
||||
|
|
||||
|
import { h } from 'vue'; |
||||
|
|
||||
|
import EditableCell from './EditableCell.vue'; |
||||
|
|
||||
|
interface Params { |
||||
|
text: string; |
||||
|
record: Recordable; |
||||
|
index: number; |
||||
|
} |
||||
|
|
||||
|
export function renderEditCell(column: BasicColumn) { |
||||
|
return ({ text: value, record, index }: Params) => { |
||||
|
record.onEdit = async (edit: boolean, submit = false) => { |
||||
|
if (!submit) { |
||||
|
record.editable = edit; |
||||
|
} |
||||
|
|
||||
|
if (!edit && submit) { |
||||
|
const res = await record.onSubmitEdit?.(); |
||||
|
if (res) { |
||||
|
record.editable = false; |
||||
|
return true; |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
// cancel
|
||||
|
if (!edit && !submit) { |
||||
|
record.onCancelEdit?.(); |
||||
|
} |
||||
|
return true; |
||||
|
}; |
||||
|
|
||||
|
return h(EditableCell, { |
||||
|
value, |
||||
|
record, |
||||
|
column, |
||||
|
index, |
||||
|
}); |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
export type EditRecordRow<T = Hash<any>> = { |
||||
|
onEdit: (editable: boolean, submit?: boolean) => Promise<boolean>; |
||||
|
editable: boolean; |
||||
|
onCancel: Fn; |
||||
|
onSubmit: Fn; |
||||
|
submitCbs: Fn[]; |
||||
|
cancelCbs: Fn[]; |
||||
|
validCbs: Fn[]; |
||||
|
} & T; |
||||
@ -1,241 +0,0 @@ |
|||||
import '../style/editable-cell.less'; |
|
||||
|
|
||||
import { defineComponent, PropType, ref, unref, nextTick, watchEffect } from 'vue'; |
|
||||
import { ClickOutSide } from '/@/components/ClickOutSide'; |
|
||||
|
|
||||
import { RenderEditableCellParams } from '../types/table'; |
|
||||
import { ComponentType } from '../types/componentType'; |
|
||||
|
|
||||
import { componentMap } from '../componentMap'; |
|
||||
import { isString, isBoolean, isArray } from '/@/utils/is'; |
|
||||
import { FormOutlined, CloseOutlined, CheckOutlined } from '@ant-design/icons-vue'; |
|
||||
|
|
||||
const prefixCls = 'editable-cell'; |
|
||||
const EditableCell = defineComponent({ |
|
||||
name: 'EditableCell', |
|
||||
props: { |
|
||||
value: { |
|
||||
type: String as PropType<string>, |
|
||||
default: '', |
|
||||
}, |
|
||||
componentProps: { |
|
||||
type: Object as PropType<any>, |
|
||||
default: null, |
|
||||
}, |
|
||||
|
|
||||
dataKey: { |
|
||||
type: String as PropType<string>, |
|
||||
default: '', |
|
||||
}, |
|
||||
|
|
||||
dataIndex: { |
|
||||
type: String as PropType<string>, |
|
||||
default: '', |
|
||||
}, |
|
||||
|
|
||||
component: { |
|
||||
type: String as PropType<ComponentType>, |
|
||||
default: 'Input', |
|
||||
}, |
|
||||
editable: { |
|
||||
type: Boolean as PropType<boolean>, |
|
||||
default: false, |
|
||||
}, |
|
||||
editRow: { |
|
||||
type: Boolean as PropType<boolean>, |
|
||||
default: false, |
|
||||
}, |
|
||||
record: { |
|
||||
type: Object as PropType<EditRecordRow>, |
|
||||
}, |
|
||||
placeholder: { |
|
||||
type: String as PropType<string>, |
|
||||
default: '', |
|
||||
}, |
|
||||
}, |
|
||||
emits: ['submit', 'cancel'], |
|
||||
setup(props, { attrs, emit }) { |
|
||||
const elRef = ref<any>(null); |
|
||||
|
|
||||
const isEditRef = ref(false); |
|
||||
const currentValueRef = ref<string | boolean>(props.value); |
|
||||
const defaultValueRef = ref<string | boolean>(props.value); |
|
||||
|
|
||||
watchEffect(() => { |
|
||||
defaultValueRef.value = props.value; |
|
||||
if (isBoolean(props.editable)) { |
|
||||
isEditRef.value = props.editable; |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
function handleChange(e: any) { |
|
||||
if (e && e.target && Reflect.has(e.target, 'value')) { |
|
||||
currentValueRef.value = (e as ChangeEvent).target.value; |
|
||||
} |
|
||||
if (isString(e) || isBoolean(e)) { |
|
||||
currentValueRef.value = e; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
function handleEdit() { |
|
||||
isEditRef.value = true; |
|
||||
nextTick(() => { |
|
||||
const el = unref(elRef); |
|
||||
el && el.focus(); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
function handleCancel() { |
|
||||
isEditRef.value = false; |
|
||||
currentValueRef.value = defaultValueRef.value; |
|
||||
emit('cancel'); |
|
||||
} |
|
||||
|
|
||||
if (props.record) { |
|
||||
/* eslint-disable */ |
|
||||
isArray(props.record.submitCbs) |
|
||||
? props.record.submitCbs.push(handleSubmit) |
|
||||
: (props.record.submitCbs = [handleSubmit]); |
|
||||
/* eslint-disable */ |
|
||||
isArray(props.record.cancelCbs) |
|
||||
? props.record.cancelCbs.push(handleCancel) |
|
||||
: (props.record.cancelCbs = [handleCancel]); |
|
||||
|
|
||||
/* eslint-disable */ |
|
||||
props.record.onCancel = () => { |
|
||||
isArray(props.record?.cancelCbs) && props.record?.cancelCbs.forEach((fn) => fn()); |
|
||||
}; |
|
||||
/* eslint-disable */ |
|
||||
props.record.onSubmit = () => { |
|
||||
isArray(props.record?.submitCbs) && props.record?.submitCbs.forEach((fn) => fn()); |
|
||||
}; |
|
||||
} |
|
||||
|
|
||||
function handleSubmit() { |
|
||||
const { dataKey, dataIndex } = props; |
|
||||
if (!dataKey || !dataIndex) return; |
|
||||
|
|
||||
if (props.record) { |
|
||||
/* eslint-disable */ |
|
||||
props.record[dataIndex] = unref(currentValueRef) as string; |
|
||||
} |
|
||||
isEditRef.value = false; |
|
||||
} |
|
||||
|
|
||||
function onClickOutside() { |
|
||||
if (props.editRow) return; |
|
||||
const { component } = props; |
|
||||
|
|
||||
if (component && component.includes('Input')) { |
|
||||
handleCancel(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
function renderValue() { |
|
||||
const { value } = props; |
|
||||
if (props.editRow) { |
|
||||
return !unref(isEditRef) ? value : null; |
|
||||
} |
|
||||
return ( |
|
||||
!unref(isEditRef) && ( |
|
||||
<div class={`${prefixCls}__normal`} onClick={handleEdit}> |
|
||||
{value} |
|
||||
<FormOutlined class={`${prefixCls}__normal-icon`} /> |
|
||||
</div> |
|
||||
) |
|
||||
); |
|
||||
} |
|
||||
return () => { |
|
||||
const { component, componentProps = {} } = props; |
|
||||
|
|
||||
const Comp = componentMap.get(component!) as any; |
|
||||
return ( |
|
||||
<div class={prefixCls}> |
|
||||
{unref(isEditRef) && ( |
|
||||
<ClickOutSide onClickOutside={onClickOutside}> |
|
||||
{() => ( |
|
||||
<div class={`${prefixCls}__wrapper`}> |
|
||||
<Comp |
|
||||
placeholder={props.placeholder} |
|
||||
{...{ |
|
||||
...attrs, |
|
||||
...componentProps, |
|
||||
}} |
|
||||
style={{ width: 'calc(100% - 48px)' }} |
|
||||
ref={elRef} |
|
||||
value={unref(currentValueRef)} |
|
||||
size="small" |
|
||||
onChange={handleChange} |
|
||||
onPressEnter={handleSubmit} |
|
||||
/> |
|
||||
{!props.editRow && ( |
|
||||
<div class={`${prefixCls}__action`}> |
|
||||
<CheckOutlined |
|
||||
class={[`${prefixCls}__icon`, 'mx-2']} |
|
||||
onClick={handleSubmit} |
|
||||
/> |
|
||||
<CloseOutlined class={[`${prefixCls}__icon `]} onClick={handleCancel} /> |
|
||||
</div> |
|
||||
)} |
|
||||
</div> |
|
||||
)} |
|
||||
</ClickOutSide> |
|
||||
)} |
|
||||
{renderValue()} |
|
||||
</div> |
|
||||
); |
|
||||
}; |
|
||||
}, |
|
||||
}); |
|
||||
|
|
||||
export function renderEditableCell({ |
|
||||
dataIndex, |
|
||||
component, |
|
||||
componentProps = {}, |
|
||||
placeholder, |
|
||||
}: RenderEditableCellParams) { |
|
||||
return ({ text, record }: { text: string; record: EditRecordRow }) => { |
|
||||
return ( |
|
||||
<EditableCell |
|
||||
{...componentProps} |
|
||||
placeholder={placeholder} |
|
||||
value={text} |
|
||||
record={record} |
|
||||
dataKey={record.key} |
|
||||
dataIndex={dataIndex} |
|
||||
component={component} |
|
||||
/> |
|
||||
); |
|
||||
}; |
|
||||
} |
|
||||
|
|
||||
export function renderEditableRow({ |
|
||||
dataIndex, |
|
||||
component, |
|
||||
componentProps = {}, |
|
||||
placeholder, |
|
||||
}: RenderEditableCellParams) { |
|
||||
return ({ text, record }: { text: string; record: EditRecordRow }) => { |
|
||||
return ( |
|
||||
<EditableCell |
|
||||
{...componentProps} |
|
||||
value={text} |
|
||||
placeholder={placeholder} |
|
||||
editRow={true} |
|
||||
editable={record.editable} |
|
||||
dataKey={record.key} |
|
||||
record={record} |
|
||||
dataIndex={dataIndex} |
|
||||
component={component} |
|
||||
/> |
|
||||
); |
|
||||
}; |
|
||||
} |
|
||||
|
|
||||
export type EditRecordRow<T = Hash<any>> = { |
|
||||
editable: boolean; |
|
||||
onCancel: Fn; |
|
||||
onSubmit: Fn; |
|
||||
submitCbs: Fn[]; |
|
||||
cancelCbs: Fn[]; |
|
||||
} & T; |
|
||||
@ -1,14 +0,0 @@ |
|||||
import { BasicArrow } from '/@/components/Basic'; |
|
||||
|
|
||||
export default () => { |
|
||||
return (props: Recordable) => { |
|
||||
return ( |
|
||||
<BasicArrow |
|
||||
onClick={(e: Event) => { |
|
||||
props.onExpand(props.record, e); |
|
||||
}} |
|
||||
expand={props.expanded} |
|
||||
/> |
|
||||
); |
|
||||
}; |
|
||||
}; |
|
||||
@ -1,39 +0,0 @@ |
|||||
@prefix-cls: ~'editable-cell'; |
|
||||
|
|
||||
.@{prefix-cls} { |
|
||||
position: relative; |
|
||||
|
|
||||
&__wrapper { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
} |
|
||||
|
|
||||
&__icon { |
|
||||
&:hover { |
|
||||
transform: scale(1.2); |
|
||||
|
|
||||
svg { |
|
||||
color: @primary-color; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
&__normal { |
|
||||
padding-right: 48px; |
|
||||
|
|
||||
&-icon { |
|
||||
position: absolute; |
|
||||
top: 4px; |
|
||||
right: 0; |
|
||||
display: none; |
|
||||
width: 20px; |
|
||||
cursor: pointer; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
&:hover { |
|
||||
.@{prefix-cls}__normal-icon { |
|
||||
display: inline-block; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,8 +1,7 @@ |
|||||
export type ComponentType = |
export type ComponentType = |
||||
| 'Input' |
| 'Input' |
||||
| 'InputPassword' |
|
||||
| 'InputNumber' |
| 'InputNumber' |
||||
| 'Select' |
| 'Select' |
||||
|
| 'ApiSelect' |
||||
| 'Checkbox' |
| 'Checkbox' |
||||
| 'CheckboxGroup' |
|
||||
| 'Switch'; |
| 'Switch'; |
||||
|
|||||
Loading…
Reference in new issue