vue3+ts+element封装一个简易的curd

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功能就完成了

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,我会尽力回答你的问题。 首先,Element Plus 是一个基于 Vue 3 的 UI 组件库,而 TypeScript 则是一种强类型的 JavaScript 扩展语言。在 Vue 3 中,我们可以通过 Composition API 来编写组件。 针对你的需求,我们可以封装一个 Tree 组件,代码如下: ```typescript <template> <el-tree :data="data" :props="props" :expand-on-click-node="false" @node-click="handleNodeClick"> <slot name="default" v-bind="{ node, data }" /> </el-tree> </template> <script lang="ts"> import { defineComponent, PropType } from 'vue' import { ElTree } from 'element-plus' interface TreeNode { label: string children?: TreeNode[] } interface Props { data: TreeNode[] props: Record<string, any> } export default defineComponent({ name: 'MyTree', components: { ElTree }, props: { data: { type: Array as PropType<TreeNode[]>, required: true }, props: { type: Object as PropType<Record<string, any>>, default: () => ({ label: 'label', children: 'children' }) }, }, setup(props: Props, { emit }) { const handleNodeClick = (data: any, node: any, instance: any) => { emit('node-click', data, node, instance) } return { handleNodeClick, } }, }) </script> ``` 在这个组件中,我们使用了 Element Plus 的 `el-tree` 组件,并使用了 `slot` 来插入自定义节点内容。通过 TypeScript 的类型定义,我们可以确保传入的 `data` 和 `props` 属性是正确的格式。同时,我们为 `node-click` 事件添加了一个自定义的处理函数。 使用这个组件的方式非常简单: ```vue <template> <my-tree :data="data" @node-click="handleNodeClick"> <template #default="{ node, data }"> <span>{{ node.label }}</span> </template> </my-tree> </template> <script lang="ts"> import { defineComponent } from 'vue' import MyTree from '@/components/MyTree.vue' interface TreeNode { label: string children?: TreeNode[] } export default defineComponent({ components: { MyTree }, setup() { const data: TreeNode[] = [ { label: '一级 1', children: [ { label: '二级 1-1', children: [ { label: '三级 1-1-1' }, { label: '三级 1-1-2' }, ], }, { label: '二级 1-2', children: [ { label: '三级 1-2-1' }, { label: '三级 1-2-2' }, ], }, ], }, { label: '一级 2', children: [ { label: '二级 2-1', children: [ { label: '三级 2-1-1' }, { label: '三级 2-1-2' }, ], }, { label: '二级 2-2', children: [ { label: '三级 2-2-1' }, { label: '三级 2-2-2' }, ], }, ], }, ] const handleNodeClick = (data: any, node: any, instance: any) => { console.log('node-click', data, node, instance) } return { data, handleNodeClick, } }, }) </script> ``` 在这个示例中,我们使用了自定义的 `MyTree` 组件,并在 `slot` 中渲染了节点的标签文本。 希望这个示例能够帮助到你。如果你还有其他问题,可以随时问我。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cheng Lucky

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值