Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(component): 🚀 在Protable中集成一个单元格可编辑的组件,(input)编辑,附加test.vue是使用示例 #228

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

130 changes: 130 additions & 0 deletions src/components/ProTable/components/EditableCell/index.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
<template>
<div class="table-edit-input" @mouseenter="evt => onMouseEnterHandler()" @mouseleave="evt => onMouseLeaveHandler()">
<div v-show="!isEdit" class="unedit-con">
<p>{{ props.scope.column.property ? handleRowAccordingToProp(props.scope.row, props.scope.column.property) : "" }}</p>
<el-icon v-show="isShowEditIcon" class="edit-icon" @click="evt => onEnterEditHandler()"><Edit /></el-icon>
</div>
<Transition
enter-active-class="animate__animated animate__fade-in-up animate__faster"
:on-after-enter="
() => {
inputIns?.focus();
}
"
>
<el-input v-show="isEdit" ref="inputIns" v-model="inputValue" v-bind="$attrs">
<template #suffix>
<el-icon style="cursor: pointer" @click="evt => onSaveHandler()"><Select /></el-icon>
</template>
</el-input>
</Transition>
</div>
</template>

<script setup lang="ts">
import { ElInput, InputProps, ElIcon, RenderRowData } from "element-plus";
import { Select, Edit } from "@element-plus/icons-vue";

import { ref, computed } from "vue";
import { handleRowAccordingToProp, sethandleRowAccordingToProp } from "@/utils";

interface Props extends Partial<InputProps> {
scope: RenderRowData<any>;
updateCol: (row: typeof props.scope.row, prop: string, oldValue: string, newValue: string, undo: () => void) => void;
}
defineOptions({ name: "EditableCell" });

const props = withDefaults(defineProps<Props>(), {});

const inputValue = ref("");
const inputIns = ref<InstanceType<typeof ElInput>>();

const isEdit = ref(false); //是否是编辑状态

const isMouseEnter = ref(false); //鼠标进入当前cell的标识

//是否显示进入编辑状态的图标
const isShowEditIcon = computed(() => {
return !isEdit.value && isMouseEnter.value;
});

const onMouseEnterHandler = () => {
isMouseEnter.value = true;
};

const onMouseLeaveHandler = () => {
isMouseEnter.value = false;
};

/**
* 进入编辑状态
*/
const onEnterEditHandler = () => {
inputValue.value = handleRowAccordingToProp(props.scope.row, props.scope.column.property);
isEdit.value = true;
};

/**
* 本次的编辑保存处理
* @param evt
*/
const onSaveHandler = () => {
const prop = props.scope.column.property;
const oldValue = handleRowAccordingToProp(props.scope.row, prop);
const undo = () => {
sethandleRowAccordingToProp(props.scope.row, prop, oldValue);
};
//emits("unactiveEdit", getTableCellFlag(props.scope), props.scope);
isEdit.value = false;
sethandleRowAccordingToProp(props.scope.row, prop, inputValue.value);

props.updateCol(props.scope.row, prop, oldValue, inputValue.value, undo);
};
</script>

<style lang="scss"></style>

<style scoped lang="scss">
$default-min-height: 28px;
$animate-duration: 1s;
.table-edit-input {
width: 100%;
min-height: $default-min-height;
.unedit-con {
position: relative;
width: 100%;
min-height: $default-min-height;
.edit-icon {
position: absolute;
top: 0;
right: 5px;
bottom: 0;
margin: auto 0;
cursor: pointer;
}
}
.animate__animated {
animation-duration: 1s;
animation-duration: $animate-duration;
animation-fill-mode: both;
}
.animate__animated.animate__faster {
animation-duration: calc(1s / 2);
animation-duration: calc($animate-duration / 2);
}
.animate__fade-in-up {
animation-name: fade-in-up;
}

@keyframes fade-in-up {
from {
opacity: 0;
transform: translate3d(0, 100%, 0);
}
to {
opacity: 1;
transform: translate3d(0, 0, 0);
}
}
}
</style>
118 changes: 118 additions & 0 deletions src/components/ProTable/components/EditableCell/test.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
<template>
<div>
<ProTable
ref="proTable"
title="用户列表"
:columns="columns"
:request-api="getTableList"
:init-param="initParam"
:data-callback="dataCallback"
>
<template #tableHeader>
<el-button type="primary" @click="logTableData">打印表格数据</el-button>
</template>
</ProTable>
</div>
</template>

<script setup lang="tsx">
import { ref, reactive } from "vue";
import ProTable from "@/components/ProTable/index.vue";
import { ColumnProps, ProTableInstance } from "@/components/ProTable/interface";

import { useEditableCell } from "@/components/ProTable/components/EditableCell/useEditableCell";
import { ElMessage } from "element-plus";

import { getUserList, getUserGender } from "@/api/modules/user";
import { User } from "@/api/interface";

const proTable = ref<ProTableInstance>();

const updateCol = (row: any, prop: string, oldValue: string, newValue: string, undo: () => void) => {
// debugger;
const flag = Math.random() > 0.5;
if (!flag) {
setTimeout(() => {
undo();
ElMessage.error("随机测试撤销修改");
}, 1000);
}
// console.log(row, prop, oldValue, newValue);
console.log(` 当前更改的属性值:${prop},oldValue:${oldValue},newValue:${newValue}`, JSON.parse(JSON.stringify(row)));
};

const { editColRender } = useEditableCell({ updateCol });
const initParam = reactive({ type: 1 });

// 表格配置项
const columns: ColumnProps<User.ResUserList>[] = [
{ type: "selection", fixed: "left", width: 80 },
{ type: "index", label: "#", width: 80 },
{ type: "expand", label: "Expand", width: 100 },
{
prop: "username",
label: "用户姓名(可编辑)",
search: { el: "input" },
render: editColRender()
},
{
prop: "gender",
label: "性别",
// 字典数据
// enum: genderType,
// 字典请求不带参数
enum: getUserGender,
// 字典请求携带参数
// enum: () => getUserGender({ id: 1 }),
search: { el: "select", props: { filterable: true } },
fieldNames: { label: "genderLabel", value: "genderValue" }
},
{
// 多级 prop
prop: "user.detail.age",
label: "年龄",
render: editColRender(),
search: {
// 自定义 search 显示内容
render: ({ searchParam }) => {
return (
<div class="flx-center">
<el-input vModel_trim={searchParam.minAge} placeholder="最小年龄" />
<span class="mr10 ml10">-</span>
<el-input vModel_trim={searchParam.maxAge} placeholder="最大年龄" />
</div>
);
}
}
},
{ prop: "idCard", label: "身份证号", render: editColRender(), search: { el: "input" } },
{ prop: "email", render: editColRender(), label: "邮箱" },
{ prop: "address", label: "居住地址", render: editColRender() },

{ prop: "operation", label: "操作", fixed: "right", width: 330 }
];

// 如果你想在请求之前对当前请求参数做一些操作,可以自定义如下函数:params 为当前所有的请求参数(包括分页),最后返回请求列表接口
// 默认不做操作就直接在 ProTable 组件上绑定 :requestApi="getUserList"
const getTableList = (params: any) => {
let newParams = JSON.parse(JSON.stringify(params));
newParams.createTime && (newParams.startTime = newParams.createTime[0]);
newParams.createTime && (newParams.endTime = newParams.createTime[1]);
delete newParams.createTime;
return getUserList(newParams);
};
const dataCallback = (data: any) => {
return {
list: data.list,
total: data.total,
pageNum: data.pageNum,
pageSize: data.pageSize
};
};

const logTableData = () => {
console.log(JSON.parse(JSON.stringify(proTable.value?.tableData)));
};
</script>

<style scoped></style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import EditableCell from "./index.vue";
export interface Options {
/**
*
* @param row 当前行数据
* @param prop 操作的属性
* @param oldValue 更改之前的值
* @param newValue 当前值
* @param undo 撤销更改操作
* @returns
*/
updateCol: (row: any, prop: string, oldValue: string, newValue: string, undo: () => void) => void;
}

export const useEditableCell = (options: Options) => {
//为单元格某列开启输入编辑
const editColRender = () => {
return (scope: any) => {
return <EditableCell scope={scope} updateCol={options.updateCol} />;
};
};
return {
editColRender
};
};
20 changes: 20 additions & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,26 @@ export function handleRowAccordingToProp(row: { [key: string]: any }, prop: stri
return row;
}

/**
* 处理 prop 为多级嵌套的情况,设置值 (列如: prop: user.name)
* @param {Object} row 当前行数据
* @param {String} prop 当前 prop
* @param {any} value 要设置的值
* @returns
*/
export function sethandleRowAccordingToProp(row: { [key: string]: any }, prop: string, value: any) {
if (!prop.includes(".")) {
row[prop] = value;
}
const list = prop.split(".");
const lastProp = list.pop();
let tmpRow = row;
for (let i = 0; i < list.length; i++) {
tmpRow = tmpRow[list[i]];
}
if (lastProp) tmpRow[lastProp] = value;
}

/**
* @description 处理 prop,当 prop 为多级嵌套时 ==> 返回最后一级 prop
* @param {String} prop 当前 prop
Expand Down