Skip to content

Commit

Permalink
feat(components): [dynamic-table] support cell edit
Browse files Browse the repository at this point in the history
  • Loading branch information
buqiyuan committed Aug 28, 2022
1 parent b24e25a commit 0eccb9a
Show file tree
Hide file tree
Showing 24 changed files with 775 additions and 289 deletions.
17 changes: 9 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,14 @@
"@vueuse/core": "~9.1.1",
"ant-design-vue": "3.2.11",
"axios": "~0.27.2",
"core-js": "~3.24.1",
"core-js": "~3.25.0",
"dayjs": "~1.11.5",
"file-saver": "~2.0.5",
"lodash-es": "~4.17.21",
"mitt": "~3.0.0",
"mockjs": "~1.1.0",
"nprogress": "~1.0.0-1",
"pinia": "~2.0.20",
"pinia": "~2.0.21",
"qs": "~6.11.0",
"socket.io-client": "~4.5.1",
"sortablejs": "~1.15.0",
Expand All @@ -55,13 +55,13 @@
"xlsx": "~0.18.5"
},
"devDependencies": {
"@commitlint/cli": "~17.0.3",
"@commitlint/config-conventional": "~17.0.3",
"@commitlint/cli": "~17.1.1",
"@commitlint/config-conventional": "~17.1.0",
"@types/lodash-es": "~4.17.6",
"@types/node": "~18.7.13",
"@types/webpack-env": "~1.18.0",
"@typescript-eslint/eslint-plugin": "~5.34.0",
"@typescript-eslint/parser": "~5.34.0",
"@typescript-eslint/eslint-plugin": "~5.35.1",
"@typescript-eslint/parser": "~5.35.1",
"@vue/cli-plugin-babel": "~5.0.8",
"@vue/cli-plugin-eslint": "~5.0.8",
"@vue/cli-plugin-router": "~5.0.8",
Expand All @@ -74,7 +74,7 @@
"cross-env": "~7.0.3",
"cz-git": "~1.3.11",
"czg": "~1.3.11",
"eslint": "~8.22.0",
"eslint": "~8.23.0",
"eslint-config-prettier": "~8.5.0",
"eslint-plugin-import": "2.26.0",
"eslint-plugin-prettier": "~4.2.1",
Expand All @@ -98,11 +98,12 @@
"stylelint-config-standard": "~28.0.0",
"stylelint-order": "~5.0.0",
"svg-sprite-loader": "~6.0.11",
"ts-node": "^10.9.1",
"typescript": "~4.7.4",
"unplugin-vue-define-options": "~0.7.3",
"vue-cli-plugin-windicss": "~1.1.6",
"vue-eslint-parser": "~9.0.3",
"vue-tsc": "^0.40.1"
"vue-tsc": "^0.40.2"
},
"__npminstall_done": false,
"repository": {
Expand Down
197 changes: 158 additions & 39 deletions src/components/core/dynamic-table/src/components/editable-cell/index.vue
Original file line number Diff line number Diff line change
@@ -1,60 +1,179 @@
<template>
<Popover :visible="!!errorMsgs?.length" placement="topRight">
<template #content>
<template v-for="err in errorMsgs" :key="err">
<Typography.Text type="danger">{{ err }}</Typography.Text>
<Spin :spinning="saving">
<div class="editable-cell">
<Popover :visible="!!errorMsgs?.length" placement="topRight">
<template #content>
<template v-for="err in errorMsgs" :key="err">
<a-typography-text type="danger">{{ err }}</a-typography-text>
</template>
</template>
<a-row type="flex" :gutter="[8]">
<SchemaFormItem
v-if="(getIsEditable || getIsCellEdit) && getSchema"
v-model:form-model="editFormModel"
:schema="getSchema"
:table-instance="tableContext"
:table-row-key="rowKey"
>
<template v-for="item in Object.keys($slots)" #[item]="data" :key="item">
<slot :name="item" v-bind="data || {}"></slot>
</template>
</SchemaFormItem>
<a-col v-if="getIsCellEdit" :span="4" class="flex items-center">
<CheckOutlined @click="handleSaveCell" />
<CloseOutlined @click="handleCancelSaveCell" />
</a-col>
</a-row>
</Popover>
<template v-if="!isCellEdit && editableType === 'cell'">
<slot />
<EditOutlined @click="startEditCell" />
</template>
</template>
<SchemaFormItem
v-if="getIsEditable && getSchema"
:key="rowKey"
:schema="getSchema"
:form-model="editFormModel"
:table-instance="tableContext"
:table-row-key="rowKey"
>
<template v-for="item in Object.keys($slots)" #[item]="data" :key="item">
<slot :name="item" v-bind="data || {}"></slot>
</template>
</SchemaFormItem>
</Popover>
</div>
</Spin>
</template>

<script lang="ts" setup>
import { computed } from 'vue';
import { Popover, Typography } from 'ant-design-vue';
<script lang="ts">
import { defineComponent, computed, ref } from 'vue';
import { EditOutlined, CloseOutlined, CheckOutlined } from '@ant-design/icons-vue';
import { Popover, Typography, Row, Col, Spin } from 'ant-design-vue';
import { useTableContext } from '../../hooks';
import type { PropType } from 'vue';
import type { CustomRenderParams, EditableType } from '@/components/core/dynamic-table/src/types';
import { schemaFormItemProps, SchemaFormItem } from '@/components/core/schema-form';
import { isAsyncFunction } from '@/utils/is';
defineOptions({
export default defineComponent({
name: 'EditableCell',
});
components: {
SchemaFormItem,
EditOutlined,
CloseOutlined,
CheckOutlined,
Popover,
Spin,
[Col.name]: Col,
[Row.name]: Row,
'a-typography-text': Typography.Text,
},
props: {
...schemaFormItemProps,
rowKey: [String, Number] as PropType<Key>,
editableType: [String] as PropType<EditableType>,
column: [Object] as PropType<CustomRenderParams>,
},
setup(props) {
const saving = ref(false);
const isCellEdit = ref(props.column?.column?.defaultEditable);
const props = defineProps({
...schemaFormItemProps,
rowKey: [String, Number] as PropType<Key>,
});
const tableContext = useTableContext();
const {
editFormModel,
editTableFormRef,
editFormErrorMsgs,
editableCellKeys,
isEditable,
startCellEditable,
cancelCellEditable,
validateCell,
} = tableContext;
const tableContext = useTableContext();
const { editFormModel, editTableFormRef, editFormErrorMsgs, isEditable } = tableContext;
const dataIndex = computed(() => {
return String(props.column?.column?.dataIndex);
});
const getSchema = computed(() => {
const field = props.schema.field;
return editTableFormRef.value?.getSchemaByFiled(field) || props.schema;
});
const getSchema = computed(() => {
const field = props.schema.field;
const schema = editTableFormRef.value?.getSchemaByFiled(field) || props.schema;
return {
...schema,
colProps: {
...schema.colProps,
span: props.editableType === 'cell' ? 20 : 24,
},
};
});
const getIsEditable = computed(() => {
return props.rowKey && isEditable(props.rowKey);
});
const getIsEditable = computed(() => {
return props.rowKey && isEditable(props.rowKey);
});
const getIsCellEdit = computed(() => {
const { rowKey } = props;
return (
isCellEdit.value &&
props.editableType === 'cell' &&
editableCellKeys.value.has(`${rowKey}.${dataIndex.value}`)
);
});
const errorMsgs = computed(() => {
const field = props.schema.field;
return editFormErrorMsgs.value.get(field);
});
const errorMsgs = computed(() => {
const field = props.schema.field;
return editFormErrorMsgs.value.get(field);
const startEditCell = () => {
startCellEditable(props.rowKey!, dataIndex.value, props.column?.record);
isCellEdit.value = true;
};
const handleSaveCell = async () => {
const { rowKey, column } = props;
await validateCell(rowKey!, dataIndex.value);
if (isAsyncFunction(tableContext?.onSave)) {
saving.value = true;
await tableContext
.onSave(rowKey!, editFormModel.value[rowKey!], column?.record)
.finally(() => (saving.value = false));
cancelCellEditable(rowKey!, dataIndex.value);
isCellEdit.value = false;
}
};
const handleCancelSaveCell = () => {
const { rowKey, column } = props;
tableContext?.onCancel?.(rowKey!, editFormModel.value[rowKey!], column?.record);
isCellEdit.value = false;
cancelCellEditable(props.rowKey!, dataIndex.value);
};
return {
saving,
isCellEdit,
editableCellKeys,
editFormModel,
getSchema,
getIsEditable,
getIsCellEdit,
errorMsgs,
tableContext,
startEditCell,
handleSaveCell,
handleCancelSaveCell,
};
},
});
</script>

<style lang="less" scoped>
.editable-cell {
position: relative;
&:hover {
.anticon-edit {
display: block;
}
}
.anticon-edit {
display: none;
position: absolute;
top: 50%;
right: 0;
transform: translateY(-50%);
}
}
:deep(.ant-form-item-explain) {
display: none;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
</template>

<script lang="ts" setup>
import { computed, nextTick, ref, unref, watchEffect } from 'vue';
import { computed, nextTick, ref, unref, watch } from 'vue';
import {
SettingOutlined,
VerticalRightOutlined,
Expand Down Expand Up @@ -125,9 +125,15 @@
);
});
watchEffect(() => {
table.setProps({ columns: tableColumns.value });
});
watch(
tableColumns,
(columns) => {
table.setProps({ columns });
},
{
deep: true,
},
);
// 设置序号列
const handleIndexCheckChange = (e) => {
table.setProps({ showIndex: e.target.checked });
Expand Down
26 changes: 23 additions & 3 deletions src/components/core/dynamic-table/src/dynamic-table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@ import { tableProps } from 'ant-design-vue/es/table';
import type DynamicTable from './dynamic-table.vue';
import type { PropType, ExtractPropTypes } from 'vue';
import type { BookType } from 'xlsx';
import type { LoadDataParams, TableColumn, OnChangeCallbackParams } from './types/';
import type {
LoadDataParams,
TableColumn,
OnChangeCallbackParams,
EditableType,
OnSave,
OnCancel,
} from './types/';
import type { SchemaFormProps } from '@/components/core/schema-form';
import type { GetRowKey } from 'ant-design-vue/es/table/interface';
import { isBoolean } from '@/utils/is';
Expand Down Expand Up @@ -85,11 +92,24 @@ export const dynamicTableProps = {
>,
default: null,
},
/** 编辑行类型,支持 只能同时编辑一行 | 同时编辑多行 */
/** 编辑行类型
* @const `single`: 只能同时编辑一行
* @const `multiple`: 同时编辑多行
* @const `cell`: 可编辑单元格
* @defaultValue `single`
*/
editableType: {
type: String as PropType<'single' | 'multiple'>,
type: String as PropType<EditableType>,
default: 'single',
},
/** 单元格保存编辑回调 */
onSave: {
type: Function as PropType<OnSave>,
},
/** 单元格取消编辑回调 */
onCancel: {
type: Function as PropType<OnCancel>,
},
/** 只能编辑一行的的提示 */
onlyOneLineEditorAlertMessage: String,
} as const;
Expand Down
5 changes: 3 additions & 2 deletions src/components/core/dynamic-table/src/dynamic-table.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<SchemaForm
v-if="search"
ref="queryFormRef"
class="bg-white dark:bg-black mb-16px pt-24px pr-24px"
class="bg-white dark:bg-black mb-16px !pt-24px pr-24px"
submit-on-reset
v-bind="getFormProps"
:table-instance="tableAction"
Expand All @@ -27,7 +27,7 @@
</template>
</ToolBar>
<component
:is="Object.keys(editFormModel).length ? SchemaForm : 'div'"
:is="editableType === 'cell' || Object.keys(editFormModel).length ? SchemaForm : 'div'"
ref="editTableFormRef"
layout="inline"
no-style
Expand Down Expand Up @@ -128,6 +128,7 @@
...tableMethods,
...editableHooks,
...exportData2ExcelHooks,
emit,
};
createTableContext(instance);
Expand Down
7 changes: 5 additions & 2 deletions src/components/core/dynamic-table/src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { TableState } from './useTableState';
import type { TableMethods } from './useTableMethods';
import type { ExportData2Excel } from './useExportData2Excel';
import type { DynamicTableProps } from '../dynamic-table';
import type { DynamicTableProps, DynamicTableEmitFn } from '../dynamic-table';

export * from './useTable';
export * from './useTableContext';
Expand All @@ -12,4 +12,7 @@ export * from './useTableMethods';
export * from './useColumns';
export * from './useEditable';

export type DynamicTableType = DynamicTableProps & TableState & TableMethods & ExportData2Excel;
export type DynamicTableType = DynamicTableProps &
TableState &
TableMethods &
ExportData2Excel & { emit: DynamicTableEmitFn };
Loading

1 comment on commit 0eccb9a

@vercel
Copy link

@vercel vercel bot commented on 0eccb9a Aug 28, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

vue3-antd-admin – ./

vue3-antd-admin.vercel.app
vue3-antd-admin-buqiyuan.vercel.app
vue3-antd-admin-git-main-buqiyuan.vercel.app

Please sign in to comment.