vue3+ts+element封装一个简易的curd
闲来无趣做一个简单的table封装,不喜勿喷
首先创建一个公共的Table.vue/pagination.vue文件
Table.vue
<template>
<div class="red-row-class">
<el-table class="table" :data="tableData" v-loading="loading" :row-class-name="rowClassName" @selection-change="handleSelectionChange" border>
<!-- 多选 -->
<el-table-column v-if="selectionIsNeed" type="selection" width="55"> </el-table-column>
<!-- 序号 -->
<el-table-column label="序号" align="center" width="80" v-if="indexFlag" type="index"></el-table-column>
<!-- 表头 -->
<el-table-column v-for="column in columns" :key="column.prop" :prop="column.prop" :label="column.label" :min-width="column.minWidth"> </el-table-column>
<!-- 操作 -->
<el-table-column v-if="optionIsNeed" fixed="right" label="操作" :width="optionWidth">
<template #default="{ row, $index }">
<slot name="prev" :scope="row"></slot>
<el-button v-if="editIsNeed" @click="handleEdit($index, row)" type="text" size="small">编辑</el-button>
<el-button v-if="deleteIsNeed" @click="handleDelete($index, row)" type="text" size="small">删除</el-button>
<slot name="next" :scope="row"></slot>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script setup lang="ts">
type Props = {
columns: Array<{
prop: string;
label: string;
minWidth: string;
}>;
tableData: Array<any>;
loading: boolean;
selectionIsNeed: boolean;
optionIsNeed: boolean;
editIsNeed: boolean;
deleteIsNeed: boolean;
indexFlag: boolean;
optionWidth: string;
rowClassName: string;
};
//类型字面量模式
withDefaults(defineProps<Props>(), {
loading: false,
selectionIsNeed: false,
optionIsNeed: false,
editIsNeed: false,
deleteIsNeed: false,
indexFlag: false,
optionWidth: '100px',
rowClassName: '',
});
const emit = defineEmits(['handleEdit', 'handleDelete']);
//修改
function handleEdit(index: number, row: any[]) {
emit('handleEdit', index, row);
}
//删除
function handleDelete(index: number, row: any[]) {
emit('handleDelete', index, row);
}
</script>
<script lang="ts">
//区分组件
export default {
name: 'Table',
};
</script>
<style lang="less" scoped>
.table {
width: 100%;
font-size: 14px;
margin-top: 15px;
}
</style>
再来建一个 pagination.vue 组件来写 分页器
pagination.vue
<template>
<div class="pagination">
<el-pagination background layout="total, prev, pager, next" :total="totalNum" :current-page="currentPage" :page-size="pageSize" @current-change="currentChange">
</el-pagination>
</div>
</template>
<script setup lang="ts">
type Props = {
totalNum: number;
currentPage: number;
pageSize: number;
};
withDefaults(defineProps<Props>(), {
pageSize: 10,
currentPage: 1,
totalNum: 0,
});
const emit = defineEmits(['currentChange']);
//点击分页
function currentChange(val: number) {
emit('currentChange', val);
}
</script>
<script lang="ts">
//区分组件
export default {
name: 'Pagination',
};
</script>
<style lang="less" scoped>
.pagination {
margin-top: 20px;
display: flex;
justify-content: flex-end;
}
</style>
然后去正式的view文件夹下建一个list.vue文件来引入上面的公共组件
list.vue
<template>
<!-- 列表 -->
<Table
:columns="columns"
:tableData="tableList.list"
:optionWidth="200"
:optionIsNeed="true"
:editIsNeed="true"
:deleteIsNeed="true"
@handleEdit="handleEdit"
@handleDelete="handleDelete"
>
<template #end_day="slotProps"> 还剩 {{ slotProps.row.end_day }} 天过期 </template>
<template #status="slotProps">
{{ slotProps.row.status ? '正常' : '过期' }}
</template>
<template #prev="{ scope }">
<el-button type="text" size="small" @click="handleFly(scope)">查看</el-button>
</template>
</Table>
<!-- 分页 -->
<Pagination :currentPage="currentPage" :totalNum="totalNum" @currentChange="currentChange" />
<!-- 添加/修改 -->
<Poput :item="item.editRow" :Tips="item.Tips" :dialogVisible="item.dialogVisible" @on-close="close" @on-submit="submit" />
</template>
<script setup lang="ts">
import { ElMessage, ElMessageBox } from 'element-plus';
import Table from '../../components/Table/table.vue';
import Pagination from '../../components/Table/Pagination.vue';
import Poput from './Popup.vue';
//分页参数
const currentPage = ref(1);
const totalNum = ref(0);
//列表接口
interface TableItem {
date: string;
name: string;
state: string;
sex: string;
city: string;
address: string;
}
type Item = {
list: TableItem[];
};
//表头
const columns = reactive([
{ prop: 'name', label: '名称', minWidth: 120 },
{ prop: 'sex', label: '性别', minWidth: 120 },
{ prop: 'date', label: '日期', minWidth: 120 },
{ prop: 'state', label: '省份', minWidth: 120 },
{ prop: 'city', label: '城市', minWidth: 120 },
{ prop: 'address', label: '地址', minWidth: 120 },
]);
const tableList = reactive<Item>({
list: [],
});
//数据
tableList.list = [
{
date: '2021-10-10',
name: '张三',
sex: '男',
state: '江苏',
city: '南京',
address: '钟楼区',
},
];
//查看
function handleFly(row: TableItem) {
console.log(row);
}
const item = reactive({
editRow: {},
Tips: '',
dialogVisible: false,
index: -1,
});
//修改
function handleEdit(index: number, row: TableItem) {
item.editRow = JSON.parse(JSON.stringify(row));
item.Tips = '修改';
item.dialogVisible = true;
item.index = index;
}
function submit(row: TableItem) {
item.dialogVisible = false;
console.log(row);
}
//删除
function handleDelete(index: number, row: TableItem) {
console.log(index, row);
ElMessageBox.confirm('确定要删除该条信息吗?', 'Warning', {
confirmButtonText: 'OK',
cancelButtonText: 'Cancel',
type: 'warning',
})
.then(() => {
ElMessage({
type: 'success',
message: 'Delete success',
});
})
.catch(() => {
ElMessage({
type: 'info',
message: 'Delete canceled',
});
});
}
//分页
function currentChange(val: number) {
currentPage.value = val;
}
//关闭弹窗
function close(e: boolean) {
item.dialogVisible = e;
}
</script>
<style lang="less" scoped></style>
跟这个组件同级的弹窗组件 Popup.vue 也建立一下
Popup.vue
<template>
<div>
<el-dialog v-model="dialogVisible" :title="Tips" width="30%" :close-on-click-modal="false">
<el-form ref="addForm" :model="item" label-width="100px" :rules="rules">
<el-form-item label="时间" prop="date">
<el-date-picker v-model="item.date" type="date" placeholder="请输入时间" format-value="yy-mm-dd" />
</el-form-item>
<el-form-item label="姓名" prop="name">
<el-input v-model="item.name" placeholder="请输入姓名"></el-input>
</el-form-item>
<el-form-item label="性别" prop="sex">
<el-input v-model="item.sex" placeholder="请输入姓名"></el-input>
</el-form-item>
<el-form-item label="省份" prop="state">
<el-input v-model="item.state" placeholder="请输入省份"></el-input>
</el-form-item>
<el-form-item label="城市" prop="city">
<el-input v-model="item.city" placeholder="请输入城市"></el-input>
</el-form-item>
<el-form-item label="详细地址" prop="address">
<el-input v-model="item.address" placeholder="请输入详细地址"></el-input>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="close(addForm)">关闭</el-button>
<el-button type="primary" @click="submit(addForm)">确定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { FormInstance, FormItemRule } from 'element-plus';
const addForm = ref<FormInstance>();
interface TableItem {
date: string;
name: string;
state: string;
sex: string;
city: string;
address: string;
}
type Prop = {
item: TableItem;
dialogVisible: boolean;
Tips: string;
};
const emit = defineEmits(['on-close', 'on-submit']);
const data = withDefaults(defineProps<Prop>(), {
dialogVisible: false,
Tips: '弹窗',
});
function close(formEl: FormInstance | undefined) {
if (!formEl) return;
formEl.resetFields();
emit('on-close', false);
}
async function submit(formEl: FormInstance | undefined) {
if (!formEl) return;
await formEl.validate(valid => {
if (valid) {
emit('on-submit', data.item);
close(formEl);
}
});
}
</script>
<script lang="ts">
//区分组件
export default {
name: 'Poput',
};
</script>
<style lang="less" scoped></style>
基本的curd功能就完成了