vue3.0项目实战系列文章 - 普通列表查询、分页、增删改、表单的实现

目录

效果

​编辑

子页面


效果

  • el-button 的 icon及text有所改变

  • table 的 template有所改变

  • el-pagination写法注意

  • 引入子组件写法参照博主其他文章

 主页面(父组件)

  • 没法讲解了,看代码吧
  • 请求接口+分页的逻辑经过时间验证
<template>
    <div class="table-all-body">
        <div class="content-nav-title">
            <div class="content-nav-title-con">
                <span>菜单管理</span>
            </div>
        </div>
        <div class="content-box-bg">
            <div class="overall-handle-box">
                <el-select filterable v-model="data.now_name" style="width:300px;" @change="systemFlagChange">
                    <el-option v-for="item in data.systemFlags" :key="item.id" :label="item.name" :value="item.id">
                    </el-option>
                </el-select>
                <el-button class="margin-left12" :icon="CirclePlus" type="primary" @click="addRole">添加菜单</el-button>
            </div>
            <div class="table-body-height-common">
                <el-table border :data="data.historyData" row-key="id">
                    <template slot="empty">
                        <p>{{data.dataText}}</p>
                    </template>
                    <el-table-column prop="name" label="名称"></el-table-column>
                    <el-table-column label="图标" width="80">
                        <template #default="scope">
                            <i :class="scope.row.icon"></i>
                        </template>
                    </el-table-column>
                    <el-table-column label="菜单类型" width="120">
                        <template #default="scope">
                            <span v-if="scope.row.type=='1'">菜单</span>
                            <span v-if="scope.row.type=='2'">按钮</span>
                            <span v-if="scope.row.type=='0'">目录</span>
                        </template>
                    </el-table-column>
                    <el-table-column prop="flag" label="分类" width="80"></el-table-column>
                    <el-table-column prop="orderNum" label="排序" width="60"></el-table-column>
                    <el-table-column label="路由" width="130">
                        <template #default="scope">
                            {{scope.row.url==''?'—':scope.row.url}}
                        </template>
                    </el-table-column>
                    <el-table-column label="授权标识" width="300" show-overflow-tooltip>
                        <template #default="scope">
                            {{scope.row.perms==''?'—':scope.row.perms}}
                        </template>
                    </el-table-column>
                    <el-table-column label="操作" width="170">
                        <template #default="scope">
                            <el-button @click="handleClick(scope.row)" link size="small" type="primary">编辑</el-button>
                            <el-button @click="deleteCommon(scope.row)" link type="danger" size="small">删除</el-button>
                        </template>
                    </el-table-column>
                </el-table>
            </div>
            <el-pagination v-model:currentPage="data.curPage" v-model:page-size="data.pageSize"
                :page-sizes="[5,10, 20, 30, 40]" :small="data.small" layout="total, jumper, prev, pager, next, sizes"
                :total="data.dataCount" @size-change="handleSizeChange" @current-change="changepage"
                class="page-position-common" />
        </div>
        <addMenuPage v-on:onItemChange="getAllDatas" ref="addMenu"></addMenuPage>
    </div>
</template>

<script lang="ts" setup>
    import addMenuPage from "./children/addMenu.vue";
    import {
        ElMessage
    } from "element-plus";
    import {
        CirclePlus
    } from '@element-plus/icons-vue'
    import {
        menuList,
        menuDelete
    } from "../../request/api.js";
    import {
        ref,
        reactive,
        onMounted,
    } from 'vue';
    const addMenu = ref(); //ref方法是让我们的简单类型数据变成响应式数据;在方法里改变时需要.value来获取
    interface User {
        icon: string
        type: string
        flag: string
        orderNum: string
        url: string
        perms: string
        id: string
    }
    // reactive 数据双向绑定;让我们的复杂类型数据变成响应式数据;不需要.value来获取
    const data = reactive({
        dataText: "", //表格开始置空
        dataCount: 0, //共几条
        pageSize: 10, // 每页显示多少条
        curPage: 1, //第几页
        now_name: "",
        systemFlags: [{
            name: '集约式直饮水智能售水系统',
            id: 'water'
        }, {
            name: '污染源在线监测平台',
            id: 'pollution'
        }, {
            name: '原位实时水质自动监测系统',
            id: 'site'
        }, {
            name: '地表水数字化监测信息管理系统',
            id: 'digitalRiver'
        }, {
            name: '农村污水处理站运行动态管控系统',
            id: 'ruralSewage'
        }, {
            name: '超标废水应急自动拦截系统',
            id: 'interception'
        }, {
            name: '饮用水安全在线监管系统',
            id: 'dwp'
        }, {
            name: 'DR303C',
            id: 'DR303C'
        }],
        historyData: [], //回显到表格的数据
        small: false,
    });
    //获取初始数据
    onMounted(() => {
        if (localStorage.getItem("systemFlag") == undefined) {
            data.now_name = data.systemFlags[0].id;
        } else {
            data.now_name = localStorage.getItem("systemFlag") || '';
        };
        getAllDatas()
    });
    const systemFlagChange = (val: string) => {
        localStorage.setItem("systemFlag", val)
        getAllDatas();
    };
    //表格初始查询
    const getAllDatas = () => {
        data.dataText = "";
        data.historyData = [];
        data.curPage = 1;
        getAllListLink();
    };
    //获取表格列表数据
    const getAllListLink = () => {
        let params = {
            systemFlag: data.now_name,
            pageNum: data.curPage,
            pageSize: data.pageSize,
        }
        menuList(params).then(res => {
            if (res.code == 0) {
                data.dataCount = res.data.total
                if (res.data.total != 0) {
                    data.historyData = res.data.list;
                } else {
                    data.dataText = "暂无数据";
                }
            } else if (res.code !== 0) {
                data.dataText = "暂无数据";
                ElMessage({
                    showClose: true,
                    message: res.msg,
                    type: 'error',
                })
            }
        })
    };
    //分页改变
    const changepage = (index: number) => {
        data.curPage = index;
        getAllListLink();
    };
    //每页条数切换
    const handleSizeChange = (val: number) => {
        data.curPage = 1;
        data.pageSize = val;
        getAllListLink();
    };
    //删除-接口
    const deleteCommon = (row: User) => {
        let params = {
            id: row.id
        }
        menuDelete(params)
            .then(res => {
                if (res.code == 0) {
                    ElMessage({
                        showClose: true,
                        message: '操作成功',
                        type: 'success',
                    })
                    getAllDatas();
                } else if (res.code !== 0) {
                    ElMessage({
                        showClose: true,
                        message: res.msg,
                        type: 'error',
                    })
                }
            })
    };
    //添加
    const addRole = () => {
        addMenu.value.addItemShow(data.now_name)
    };
    //编辑
    const handleClick = (row: User) => {
        addMenu.value.addItemShow2(row, data.now_name)
    };
</script>
<style scoped>

</style>

子页面

展现

 注意:

  • defineExpose  注意暴露!!
  • el-dialog 需要v-model
  • form 的 ref="ruleFormRef" :rules="rules" :model="ruleForm"
  • onSubmit(ruleFormRef)

  • import type { FormInstance } from 'element-plus'编辑器插件版本过高会报错,我直接注释了,等待再次升级!

  • as any[]注意他的使用

  • nextTick的使用

  • push的使用

  • 表单验证

源代码:

<template>
  <div>
    <el-dialog :title="data.titleName" v-model="data.children_page" :close-on-click-modal='false'
     :append-to-body='true' width="800px">
      <el-form ref="ruleFormRef" :rules="rules" :model="ruleForm" label-width="150px">
        <el-form-item v-if="data.isAdd" label="所属系统:" prop="system">
          <el-select v-model="ruleForm.system" @change="changeSystem" placeholder="请选择" style="width:300px;">
            <el-option v-for="item in data.systems" :key="item.id" :label="item.name" :value="item.id"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="角色分类:" prop="type">
          <el-select v-model="ruleForm.type" style="width:300px;">
            <el-option v-for="item in data.types" :key="item.id" :label="item.name" :value="item.id"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="角色名称:" prop="name">
          <el-input v-model.trim="ruleForm.name" style="width:300px;" />
        </el-form-item>
        <el-form-item label="角色字符:" prop="key">
          <el-input v-model.trim="ruleForm.key" style="width:300px;" /> ( 定义角色唯一性 )
        </el-form-item>
        <el-form-item label="菜单配置:">
          <div class="jurisdiction-border">
            <el-tree show-checkbox :data="data.treeDatas" node-key="id" ref="treeRef" :check-strictly="true"
              :default-checked-keys="data.nowKeys">
              <template #default="{ node, data }">
                <span class="custom-tree-node">
                  <span v-if="data.type == 0||data.type == 1">
                    <el-tag :type="data.flag=='app'?'success':''" size="small"> {{data.flag=='app'?'app-菜单':'pc-菜单'}}
                    </el-tag>
                    <span :title="data.name" class='tree-name-leave'> {{data.name}} </span>
                  </span>
                  <span v-else>
                    <el-tag type="warning" size="small"> {{data.flag}}-按钮 </el-tag>
                    <span :title="data.name" class='tree-name-leave'> {{data.name}} </span>
                  </span>
                </span>
              </template>
            </el-tree>
          </div>
        </el-form-item>
        <el-form-item>
          <el-button @click="data.children_page = false">取 消</el-button>
          <el-button type="primary" @click="onSubmit(ruleFormRef)">确 定</el-button>
        </el-form-item>
      </el-form>
    </el-dialog>
  </div>
</template>
<script lang="ts" setup>
  import {
    roleInsert,
    roleUpdate,
    menuAll,
    menuRole
  } from "../../../request/api.js";
  import {
    ElMessage,
    ElTree
  } from "element-plus";
  // import type { FormInstance } from 'element-plus' 
  import {
    ref,
    reactive,
    nextTick,
    // defineProps,//defineProps声明props;具备完整的类型推断;<script setup>直接可用
    // defineEmits,//defineEmits声明事件;具备完整的类型推断;<script setup>直接可用
    // defineExpose//暴露出去;<script setup>直接可用
  } from 'vue';
  const ruleFormRef = ref();//表单ref
  //获取tree选中的值
  const treeRef = ref < InstanceType < typeof ElTree >> ();
  const emit = defineEmits(["onItemChange"]);//声明事件(父级页面上添加的 @监听事件)
  //数据双向绑定;复杂类型数据=>响应式数据
  const data = reactive({
    titleName: '',
    checkAll: false,
    children_page: false,
    isAdd: false,
    systems: [{
      name: '集约式直饮水智能售水系统',
      id: 'water'
    }, {
      name: '污染源在线监测平台',
      id: 'pollution'
    }, {
      name: '原位实时水质自动监测系统',
      id: 'site'
    }, {
      name: '地表水数字化监测信息管理系统',
      id: 'digitalRiver'
    }, {
      name: '农村污水处理站运行动态管控系统',
      id: 'ruralSewage'
    }, {
      name: '超标废水应急自动拦截系统',
      id: 'interception'
    }, {
      name: '饮用水安全在线监管系统',
      id: 'dwp'
    }, {
      name: 'DR303C',
      id: 'DR303C'
    }],
    types: [{
      name: '系统',
      id: 'system'
    }, {
      name: '运维',
      id: 'operational'
    }, {
      name: '客户',
      id: 'customer'
    }, {
      name: '普通用户',
      id: 'common'
    }],
    nowKeys: [] as any[], // 属性需要做类型断言处理 //树-选中数据
    treeDatas: [], //树-总数据
    id: '',
  });
  //ts类型断言
  //定义类型
  interface User {
    name: string
    type: string
    key: string
    id: string
  };
  const ruleForm = reactive({
    system: "",
    type: "",
    name: "",
    key: "",
  });
  const rules = ref({
    name: [{
        required: true,
        message: '请输入角色名称',
        trigger: 'blur'
      },
      {
        min: 1,
        max: 50,
        message: '长度在 1 到 50 个字符',
        trigger: 'blur'
      }
    ],
    key: [{
        required: true,
        message: '请输入角色字符',
        trigger: 'blur'
      },
      {
        min: 1,
        max: 50,
        message: '长度在 1 到 50 个字符',
        trigger: 'blur'
      }
    ],
    system: [{
      required: true,
      message: '请选择所属系统',
      trigger: 'change'
    }],
  });
  //父调子的子
  const addItemShow = (now_name: string) => {
    data.titleName = '添加角色';
    data.children_page = true;
    data.isAdd = true;
    ruleForm.system = now_name;
    ruleForm.type = '';
    data.nowKeys = []; //树-选中数据
    getTree();
    nextTick(() => {
    ruleFormRef.value.resetFields();//重置表单
    })
  };
  //父调子的子
  const addItemShow2 = (row: User, now_name: string) => {
    data.titleName = '编辑角色';
    data.children_page = true;
    data.isAdd = false;
    nextTick(() => {
      ruleFormRef.value.resetFields();//重置表单
      ruleForm.name = row.name;
      ruleForm.key = row.key;
      ruleForm.system = now_name;
      ruleForm.type = row.type;
      data.id = row.id;
      data.nowKeys = [] //树-选中数据
      data.treeDatas = [] //树-总数据
      menuRoleGet()
    })
  };
  //回显树-选中数据
  const menuRoleGet = () => {
    let params = {
      id: data.id,
    }
    menuRole(params).then(res => {
      if (res.code == 0) {
        if (res.data.list.length != 0) {
          for (var i in res.data.list) {
            data.nowKeys.push(res.data.list[i].id)
          }
        }
        getTree()
      } else if (res.code !== 0) {
        ElMessage.error(res.msg);
      }
    })
  };
  //改变系统时=>获取树数据
  const changeSystem = () => {
    getTree();
  };
  //获取树数据
  const getTree = () => {
    data.treeDatas = [];
    let params = {
      systemFlag: ruleForm.system,
    }
    menuAll(params).then(res => {
      if (res.code == 0) {
        data.treeDatas = res.data.list
      } else if (res.code !== 0) {
        ElMessage.error(res.msg);
      }
    })
  };
  //添加/修改   : FormInstance | undefined
  const onSubmit = async (formEl) => {
    if (!formEl) return
    formEl.validate((valid) => {
      if (valid) {
        if (data.isAdd) {
          addRoles();
        } else {
          changeRoles();
        }
      } else {
        return false;
      }
    });
  };
  //添加接口
  const addRoles = () => {
    let params = {
      systemFlag: ruleForm.system,
      name: ruleForm.name,
      key: ruleForm.key,
      type: ruleForm.type,
      menuIds: treeRef.value!.getCheckedKeys(false),
    }
    roleInsert(params).then(res => {
      if (res.code == 0) {
        ElMessage({
          showClose: true,
          message: '操作成功',
          type: 'success',
        })
        data.children_page = false;
        emit("onItemChange");
      } else if (res.code !== 0) {
        ElMessage.error(res.msg);
      }
    })
  };
  //修改接口
  const changeRoles = () => {
    let params = {
      id: data.id,
      name: ruleForm.name,
      key: ruleForm.key,
      type: ruleForm.type,
      menuIds: treeRef.value!.getCheckedKeys(false),
    }
    roleUpdate(params).then(res => {
      if (res.code == 0) {
        ElMessage({
          showClose: true,
          message: '操作成功',
          type: 'success',
        })
        data.children_page = false;
        emit("onItemChange");
      } else if (res.code !== 0) {
        ElMessage.error(res.msg);
      }
    })
  };
  defineExpose({
    addItemShow,
    addItemShow2
  }) //将子组件方法暴露出来,让父组件调用
</script>
<style scoped>
  .jurisdiction-border {
    width: 500px;
    height: 300px;
    padding: 10px;
    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
    overflow: auto;
  }

  .jurisdiction-border::-webkit-scrollbar {
    width: 7px;
    height: 7px;
    background-color: rgb(241, 241, 241);
    border-radius: 3px;
  }

  .jurisdiction-border::-webkit-scrollbar-thumb {
    background-color: rgb(193, 193, 193);
    border-radius: 3px;
  }

  .tree-name-leave {
    margin-leave: 5px;
  }
</style>

  • 1
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

BMG-Princess

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

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

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

打赏作者

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

抵扣说明:

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

余额充值