el-table点击表格某一行添加到URL参数,访问带参URL加载表格内容并滚动到选中行位置 [Vue3] [Element-plus 2.3]

文章讲述了在Vue3和Element-Plus2.3的环境下,如何实现在表格中点击行数据时更新URL参数,以便于分享,并在页面加载时根据URL参数选中对应行。当表格数据过多出现滚动条时,还能自动滚动到选中行的位置。

写在最前

需求:有个表格列出了一些行数据,每个行数据点击后会加载出对应的详细数据,想要在点击了某一行后,能够将该点击反应到URL中,这样我复制这个URL发给其他人,他们打开时也能看到同样的行数据。

url会根据点击动态更新,大概是这样 xxx.com?param1=var1&param2=var2

主要版本:vue3,element-plus 2.3 (element-plus版本2.0就不行,已踩坑issue)

实现

URL参数的动态更新与访问加载

下面封装了更新URL参数获取URL参数的方法

// 更新URL参数
function updateURLWithParams(paramsObj: any) {
    // 入参检查
    if (typeof paramsObj !== 'object' || paramsObj === null) {
        console.error('Invalid input. params must be an object.');
        return;
    }
    // 获取当前URL
    const url = new URL(window.location.href);
    // 创建基于当前URL的不含参数的URL对象
    const newUrl = new URL(url.origin + url.pathname);
    // 创建新的URLSearchParams
    const params = new URLSearchParams();
    // 添加搜索参数
    for (const key in paramsObj) {
        params.set(key, paramsObj[key]);
    }
    // 更新到新URL对象
    newUrl.search = params.toString();
    // 更新到浏览器history(地址栏改变)
    window.history.pushState('', '', newUrl.toString());
}

// 获取URL参数
function getQueryParamsFromURL() {
    const urlObj = new URL(window.location.href);
    const queryParams = urlObj.searchParams;
    const params: { [key: string]: string } = {};

    for (const [key, value] of queryParams.entries()) {
        params[key] = value;
    }

    return params;
}

然后在监听点击行数据的方法中调用即可。

handleRowClick(row: any, event: any, column: any) {
      // 加载数据的code...

      // 更新参数到URL,这里假设把row的id放到参数里面
      updateURLWithParams({ "row_id": row.id});
    },

在初始化页面后,需要根据URL中参数加载出对应的行数据。对应的代码如下:

created() {
  // 从URL中拿到搜索参数
  const row_id = getQueryParamsFromURL()["row_id"];
  // 如果参数不为空,遍历表格数据找到对应的行
  if (!!row_id) {
  // tableData是el-table绑定的表格数据,tableRef是绑定的引用对象
  // <el-table :data="tableData" ref="tableRef">
  for (let index = 0; index < this.tableData.length; index++) {
     if (this.tableData[index].id==row_id) {
         // 设置表格当前行为参数中指定的行,如果表格设置了高亮,则同时会高亮当前行
         this.$refs.tableRef.setCurrentRow(this.tableData[index]);
         // 然后可以加载对应的行数据
         // coding...
         break;
     }
  }
}

至此已经实现了,点击行数据更新URL参数,访问带参的URL会选中指定的行并加载对应数据。

但是如果表格数据过多,有滚动条了,这时候还不能自动滚动到当前选中的行。

所以需要手动实现。

获取选中行的偏移高度并滚动到该处

el-table提供了滚动到指定位置的方法,但是需要输入坐标或者偏移量。

Table 表格 | Element Plus (element-plus.org)

 这里使用setScrollTop方法,所以我们需要获取当前已选中行的偏移高度并设置为滚动位置,代码如下:

// 获取偏移高度, tableRef是table的引用对象,index是行的索引
const offsetTop = this.$refs.tableRef.$el.getElementsByClassName('el-table__row')[index].offsetTop;
// 设置滚动位置
this.$refs.tableRef.setScrollTop(offsetTop);
           

结合上面找到并选中参数中指定行的代码,最终实现如下:

created() {
  // 从URL中拿到搜索参数
  const row_id = getQueryParamsFromURL()["row_id"];
  // 如果参数不为空,遍历表格数据找到对应的行
  if (!!row_id) {
  // tableData是el-table绑定的表格数据,tableRef是绑定的引用对象
  // <el-table :data="tableData" ref="tableRef">
  for (let index = 0; index < this.tableData.length; index++) {
     if (this.tableData[index].id==row_id) {
         // 设置表格当前行为参数中指定的行,如果表格设置了高亮,则同时会高亮当前行
         this.$refs.tableRef.setCurrentRow(this.tableData[index]);
         // 获取偏移高度, tableRef是table的引用对象,index是行的索引
         const offsetTop = this.$refs.tableRef.$el.getElementsByClassName('el-table__row') [index].offsetTop;
         // 设置滚动位置
         this.$refs.tableRef.setScrollTop(offsetTop);
                    
         // 然后可以加载对应的行数据
         // coding...
         break;
     }
  }
}

总结

element-plus虽然很方便,但有时候不能直接满足需求,需要多查资料多摸索。另外本人不是专业前端,这里只是记录了一次有趣的解决问题的过程,如果有更优雅的解决方案,欢迎分享噢。

<template> <div class="all"> <div class="head"> <!-- <i class="el-icon-search"></i> --> <el-button plain icon="el-icon-search" type="success" size="mini" @click="searchByDataCode">查询</el-button> <el-button plain icon="el-icon-circle-plus-outline" type="success" size="mini" @click="dialogFormVisible = true">新增</el-button> <!-- 新增的弹出框 --> <el-dialog title="新增数据字典" :visible.sync="dialogFormVisible"> <el-form :model="formSub" label-width="80px"> <el-row> <el-col :span="8"> <el-form-item label="字典编码"> <el-input v-model="formSub.data_code" style="width:150px;"></el-input> </el-form-item> </el-col> <el-col :span="8"> <el-form-item label="字典名称"> <el-input v-model="formSub.data_name" style="width:150px;"></el-input> </el-form-item> </el-col> <el-col :span="8"> <el-form-item label="参数级别" > <el-input v-model="formSub.parameter_level" style="width:150px;"></el-input> </el-form-item> </el-col> </el-row> </el-form> <!-- 子项目表格 --> <el-button type="primary" size="mini" @click="addRow()">新增</el-button> <el-table :data="tableData2" border style="margin-top:5px;"> <el-table-column prop="item" label="编号" width="165"> <template slot-scope="scope"> <el-input ref="item" v-model="scope.row.item_id" style="width:150px;"></el-input> </template>> </el-table-column> <el-table-column prop="item" label="item编码" width="165"> <template slot-scope="scope"> <el-input ref="item" v-model="scope.row.item_code" style="width:150px;"></el-input> </template>> </el-table-column> <el-table-column prop="item" label="item状态" width="165"> <template slot-scope="scope"> <el-input ref="item" v-model="scope.row.item_state" style="width:150px;"></el-input> </template>> </el-table-column> <el-table-column prop="item" label="字典编码" width="180"> <template slot-scope="scope"> <el-input ref="item" v-model="scope.row.data_code" style="width:150px;"></el-input> </template>> </el-table-column> </el-table> <div slot="footer" class="dialog-footer"> <el-button @click="dialogFormVisible = false">取 消</el-button> <el-button type="primary" @click="addDataDictionary">确 定</el-button> </div> </el-dialog> <!-- <el-button type="warning" size="mini" @click="dialogFormVisible1 = true">修改</el-button> <el-button type="danger" size="mini">删除</el-button> --> </div> <el-form ref="form" :model="form" label-width="80px" style="margin-top:20px;"> <el-form-item label="字典编码:"> <el-input v-model="form.codeInput" placeholder="请输入" style="width:150px;"></el-input> </el-form-item> </el-form> <!-- 表格 --> <el-table ref="singleTable" :data="tableData" highlight-current-row @current-change="handleCurrentChange" @expand-change="listAllItemByDataCode" style="width: 100%;" border> <el-table-column type="expand"> <template slot-scope="props"> <!-- 展示子项目 --> <el-table :data="tableData1" max-height="800" border> <el-table-column prop="item_id" label="编号" ></el-table-column> <el-table-column prop="item_code" label="item编码" ></el-table-column> <el-table-column prop="item_state" label="item状态" ></el-table-column> <el-table-column prop="create_person" label="建档人" ></el-table-column> <el-table-column prop="create_time" label="建档时间" width="190"></el-table-column> <el-table-column prop="update_person" label="修改人" ></el-table-column> <el-table-column prop="update_time" label="修改时间" width="190"></el-table-column> <el-table-column prop="data_code" label="字典编码" ></el-table-column> </el-table> </template> </el-table-column> <el-table-column prop="data_code" label="字典编码" width="150"></el-table-column> <el-table-column prop="data_name" label="字典名称" ></el-table-column> <el-table-column prop="parameter_level" label="参数级别" ></el-table-column> <el-table-column prop="create_person" label="建档人" ></el-table-column> <el-table-column prop="create_time" label="建档时间" width="190" ></el-table-column> <el-table-column prop="update_person" label="修改人" ></el-table-column> <el-table-column prop="update_time" label="修改时间" width="190"></el-table-column> <el-table-column label="操作" width="200" fixed="right"> <template slot-scope="scope" > <el-button plain type="warning" size="small" @click="handleEdit(scope.$index, scope.row)">编辑</el-button> <el-button plain type="danger" size="small" @click="open(scope.row)">删除</el-button> <!-- deleteDataDictionary(scope.row) --> </template> </el-table-column> </el-table> <!-- 编辑弹出框 --> <el-dialog title="编辑数据字典" :visible.sync="editFormVisible" :height="800"> <el-form :model="formEdit" label-width="80px">· <el-row> <el-col :span="8"> <el-form-item label="字典编码"> <el-input v-model="formEdit.data_code" :readonly="true" ></el-input> </el-form-item> </el-col> <el-col :span="8"> <el-form-item label="字典名称"> <el-input v-model="formEdit.data_name" ></el-input> </el-form-item> </el-col> <el-col :span="8"> <el-form-item label="参数级别" > <el-input v-model="formEdit.parameter_level" ></el-input> </el-form-item> </el-col> </el-row> </el-form> <!-- 子项目表格 --> <el-button type="primary" size="mini" @click="addRow1()">新增</el-button> <el-button type="danger" size="mini" @click="deleteItem()">删除</el-button> <el-table :data="tableData3" ref="multipleTable" @selection-change="handleSelectionChange" border style="margin-top:5px;"> <el-table-column type="selection" width="55"> </el-table-column> <el-table-column prop="item" label="编号" > <template slot-scope="scope"> <el-input ref="item" v-model="scope.row.item_id" :readonly="true" style="width:150px;"></el-input> </template> </el-table-column> <el-table-column prop="item" label="item编号" > <template slot-scope="scope"> <el-input ref="item" v-model="scope.row.item_code" style="width:150px;"></el-input> </template> </el-table-column> <el-table-column prop="item" label="item状态" > <template slot-scope="scope"> <el-input ref="item" v-model="scope.row.item_state" style="width:150px;"></el-input> </template> </el-table-column> <el-table-column prop="item" label="字典编码" > <template slot-scope="scope"> <el-input ref="item" v-model="scope.row.data_code" style="width:150px;"></el-input> </template> </el-table-column> </el-table> <div slot="footer" class="dialog-footer"> <el-button @click="editFormVisible = false">取 消</el-button> <el-button type="primary" @click="updateDataDictionary()">确 定</el-button> </div> </el-dialog> <!-- 分页 --> <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange1" :current-page="page" :page-sizes="[5, 10, 20]" :page-size="limit" layout="total, sizes, prev, pager, next, jumper" :total="count"> </el-pagination> </div> </template> <script> import axios from &#39;axios&#39;; import { request } from "../../network/request"; export default { name: "DataDictionary", data() { return { //codeInput: &#39;&#39;, tableData: [],//外层表格初始值 tableData1:[],//子项表格初始值 tableData2:[{ item_id: &#39;&#39;, item_code: &#39;&#39;, item_state: &#39;&#39;, data_code: &#39;&#39; }],//新增表格初始值 page:1, limit:5, count: 0, form: { codeInput: &#39;&#39; }, dialogFormVisible: false,//弹出层初始为false formSub: { data_code:&#39;&#39;, data_name:&#39;&#39;, parameter_level:&#39;&#39; }, //编辑初始值设置 editFormVisible:false, formEdit:{ data_code:&#39;&#39;, data_name:&#39;&#39;, parameter_level:&#39;&#39; }, tableData3: [{ item_id: &#39;&#39;, item_code: &#39;&#39;, item_state: &#39;&#39;, data_code: &#39;&#39; }], dialogDelVisible: false, multipleSelection: [], deleteItemsIds:&#39;&#39; } }, mounted() { // 发ajax请求,用以获取表格初始数据 const url = &#39;http://localhost:9091/systemmanagement/dataDictionary/selectAll/&#39;+this.page+&#39;/&#39;+this.limit; axios.get(url).then( response => { //alert("查询成功"); //初始数据 this.tableData = response.data.data.dataDictionaryList; this.count = response.data.data.count; } ).catch( response => { console.log(err); }, ); }, methods: { // 初始页currentPage、初始每页数据数pagesize和数据data handleSizeChange: function (size) { this.limit = size; console.log(this.limit) //每页下拉显示数据 //发送请求新的数据 this.getDataByPagination(); }, handleCurrentChange1: function(page){ this.page = page; console.log(this.page) //点击第几页 this.getDataByPagination(); }, //分页修改后的查询 getDataByPagination(){ const url = &#39;http://localhost:9091/systemmanagement/dataDictionary/selectAll/&#39;+this.page+&#39;/&#39;+this.limit; axios.get(url).then( response => { //alert("查询成功"); //初始数据 this.tableData = response.data.data.dataDictionaryList; this.count = response.data.data.count; } ).catch( response => { console.log(err); }, ); }, // 中当前行 setCurrent(row) { this.$refs.singleTable.setCurrentRow(row); }, handleCurrentChange(val) { this.currentRow = val; }, // 根据DataCode查询数据 searchByDataCode(){ request({ url: "/systemmanagement/dataDictionary/searchByDataCode", method: "post", data: { data_code: this.form.codeInput } }) .then(response => { //成功 this.tableData = response.data.data.dataDictionaryList; this.count = response.data.data.count; }) .catch(err => { //失败 console.log(err); }); }, //查询子项目数据 listAllItemByDataCode(row,expandedRows){ request({ url: "/systemmanagement/dataDictionaryItem/selectAll/"+row.data_code // method: "post", // data: { // data_code: row.data_code // } }) .then(response => { //成功 this.tableData1 = response.data.data; }) .catch(err => { //失败 console.log(err); }); }, //添加数据字典 addDataDictionary(){ //var token = window.localStorage.getItem("token"); request({ url: "/systemmanagement/dataDictionary/addDataDictionary", method:"post", //headers:{"token":token}, data: { data_code: this.formSub.data_code, data_name: this.formSub.data_name, parameter_level: this.formSub.parameter_level } }) .then(response => { //成功 this.addDataItem(); }) .catch(err => { //失败 console.log(err); }); }, //添加子项目 addDataItem(){ //var token = window.localStorage.getItem("token");//获取登陆token this.dialogFormVisible = false; console.log(this.tableData2); request({ url: "/systemmanagement/dataDictionaryItem/addDataItem", method:"post", //headers:{"token":token}, data: { dataDictionaryItems: this.tableData2 } }) .then(response => { //成功 alert(response.data.message); this.tableData2=[], this.formSub.data_name = &#39;&#39;, this.formSub.data_code = &#39;&#39;, this.formSub.parameter_level = &#39;&#39; location.reload(); }) .catch(err => { //失败 console.log(err); }); }, //表格新增行 addRow() { this.tableData2.push({ item_id: &#39;&#39;, item_code: &#39;&#39;, item_state: &#39;&#39;, data_code: &#39;&#39; }); }, //点击按钮触发编辑 handleEdit(index, row) { this.editFormVisible = true; this.formEdit = Object.assign({}, row); console.log(this.formEdit); request({ url: "/systemmanagement/dataDictionaryItem/selectAll/"+row.data_code }) .then(response => { //成功 this.tableData3 = response.data.data; }) .catch(err => { //失败 console.log(err); }); }, //表格新增行 addRow1() { this.tableData3.push({ item_id: &#39;&#39;, item_code: &#39;&#39;, item_state: &#39;&#39;, data_code: &#39;&#39; }); }, //删除确认提示 open(row) { this.$confirm(&#39;此操作将永久删除该文件, 是否继续?&#39;, &#39;提示&#39;, { confirmButtonText: &#39;确定&#39;, cancelButtonText: &#39;取消&#39;, type: &#39;warning&#39; }).then(() => { //删除 this.dialogDelVisible = false; request({ url: "/systemmanagement/dataDictionary/deleteDataDictionary/"+row.data_code }) .then(response => { //成功 alert(response.data.message); location.reload(); }) .catch(err => { //失败 console.log(err); }); }).catch(() => { this.$message({ type: &#39;info&#39;, message: &#39;已取消删除&#39; }); }); }, //点击确认删除,删除DataDictionary deleteDataDictionary(row){ this.dialogDelVisible = false; request({ url: "/systemmanagement/dataDictionary/deleteDataDictionary/"+row.data_code }) .then(response => { //成功 alert(response.data.message); location.reload(); }) .catch(err => { //失败 console.log(err); }); }, //获取中的数据 handleSelectionChange(val) { this.multipleSelection = val; }, //删除编辑弹窗中的子项目 deleteItem(){ let items = this.multipleSelection; items.forEach(element => { this.deleteItemsIds = element.item_id+&#39;,&#39;; }); console.log(this.deleteItemsIds); // this.deleteItemsIds = items // .map(item => item.item_id) // .join(); request({ url: "/systemmanagement/dataDictionaryItem/deleteItem/"+this.deleteItemsIds }) .then(res => { this.$message({ message: res.data.message, type: "success" }); location.reload(); }) .catch(err => { this.$message.error(err); }); }, //修改数据 updateDataDictionary(){ //var token = window.localStorage.getItem("token"); this.editFormVisible = false; request({ url: "/systemmanagement/dataDictionary/updateDataDictionary", method:"post", //headers:{"token":token}, data: { data_code: this.formEdit.data_code, data_name: this.formEdit.data_name, parameter_level: this.formEdit.parameter_level } }) .then(response => { //成功 //location.reload(); this.updateItem(); }) .catch(err => { //失败 console.log(err); }); }, //修改子项数据 updateItem(){ //var token = window.localStorage.getItem("token"); //获取行数据 let item = this.multipleSelection; console.log(item[0].item_id); request({ url: "/systemmanagement/dataDictionaryItem/updateItem", method:"post", //headers:{"token":token}, data: { item_id: item[0].item_id, item_code: item[0].item_code, item_state: item[0].item_state, data_code: item[0].data_code } }) .then(response => { //成功 //location.reload(); }) .catch(err => { //失败 console.log(err); }); } } } </script> <style> .all{ margin-left: 20px; } .search-module{ margin-top: 20px; float: left; } .el-table td, .el-table th { text-align: center; padding: 5px 0; } </style>
08-19
<template> <el-container style="height: 100vh; overflow: hidden;"> <!-- 侧边栏 --> <el-aside width="200px" style="background-color: #304156;"> <el-menu default-active="1" class="el-menu-vertical-demo" background-color="#304156" text-color="#bfcbd9" active-text-color="#409EFF"> <el-menu-item index="1"> <i class="el-icon-user"></i> <span>员工管理</span> </el-menu-item> </el-menu> </el-aside> <!--内容--> <el-container> <!-- 头部 --> <el-header class="app-header"> <h2 class="app-title">员工管理系统</h2> </el-header> <!-- 主要内容 --> <el-main class="app-content"> <!-- 查询条件 --> <div class="query-section"> <el-row :gutter="20"> <el-col :span="4"> <el-input v-model="queryParams.name" placeholder="请输入姓名" clearable /> </el-col> <el-col :span="4"> <el-select v-model="queryParams.gender" placeholder="请择性别" clearable> <el-option label="男" :value="0" /> <el-option label="女" :value="1" /> </el-select> </el-col> <el-col :span="4"> <el-select v-model="queryParams.job" placeholder="请择职位" clearable> <el-option v-for="item in jobOptions" :key="item.value" :label="item.label" :value="item.value" /> </el-select> </el-col> <el-col :span="6"> <el-date-picker v-model="queryParams.dateRange" type="daterange" start-placeholder="开始日期" end-placeholder="结束日期" format="YYYY-MM-DD" value-format="YYYY-MM-DD" /> </el-col> <el-col :span="6" class="action-buttons"> <el-button type="primary" @click="fetchData" icon="el-icon-search">搜索</el-button> <el-button type="success" @click="showAddDialog" icon="el-icon-plus">新增</el-button> <el-button type="danger" @click="batchDelete" icon="el-icon-delete">批量删除</el-button> </el-col> </el-row> </div> <!-- 数据表格 --> <div class="table-section"> <el-table :data="tableData" border stripe style="width: 100%" @selection-change="handleSelectionChange"> <el-table-column type="selection" width="55" align="center" /> <el-table-column prop="id" label="ID" width="80" align="center" /> <el-table-column prop="name" label="姓名" width="120" /> <el-table-column label="头像" width="100" align="center"> <template #default="scope"> <el-avatar v-if="scope.row.image" :src="scope.row.image" :size="50" shape="square" :preview-src-list="[scope.row.image]" /> <el-avatar v-else :size="50" shape="square"> <i class="el-icon-user-solid" style="font-size: 24px;" /> </el-avatar> </template> </el-table-column> <el-table-column prop="gender" label="性别" width="80" align="center"> <template #default="scope"> <el-tag :type="scope.row.gender === 0 ? &#39;primary&#39; : &#39;danger&#39;"> {{ scope.row.gender === 0 ? &#39;男&#39; : &#39;女&#39; }} </el-tag> </template> </el-table-column> <el-table-column prop="job" label="职位" width="150"> <template #default="scope"> <el-tag :type="getJobTagType(scope.row.job)"> {{ jobMap[scope.row.job] || &#39;未知&#39; }} </el-tag> </template> </el-table-column> <el-table-column prop="entrydate" label="入职时间" width="120" align="center" /> <el-table-column prop="createTime" label="创建时间" width="180" align="center" /> <!-- 添加最后修改时间列 --> <el-table-column prop="updateTime" label="最后修改时间" width="180" align="center" /> <el-table-column label="操作" width="200" align="center" fixed="right"> <template #default="scope"> <el-button size="small" type="primary" icon="el-icon-edit" @click="handleEdit(scope.row)">编辑</el-button> <el-button size="small" type="danger" icon="el-icon-delete" @click="handleDelete(scope.row.id)">删除</el-button> </template> </el-table-column> </el-table> </div> <!-- 分页组件 --> <div class="pagination-section"> <el-pagination background layout="total, prev, pager, next, sizes" :total="total" :page-sizes="[5, 10, 20, 50]" :page-size="queryParams.pageSize" v-model:current-page="queryParams.pageNum" @current-change="fetchData" @size-change="handleSizeChange" /> </div> </el-main> </el-container> </el-container> <!-- 新增/编辑对话框 - 优化样式 --> <el-dialog v-model="dialogVisible" :title="dialogTitle" width="700px" :close-on-click-modal="false"> <el-form :model="form" ref="formRef" :rules="formRules" label-width="100px" label-position="left"> <el-row :gutter="20"> <el-col :span="12"> <el-form-item label="姓名" prop="name"> <el-input v-model="form.name" /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="用户名" prop="username"> <el-input v-model="form.username" /> </el-form-item> </el-col> </el-row> <el-row :gutter="20"> <el-col :span="12"> <el-form-item label="性别" prop="gender"> <el-select v-model="form.gender" placeholder="请择性别" style="width: 100%"> <el-option label="男" :value="0" /> <el-option label="女" :value="1" /> </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="职位" prop="job"> <el-select v-model="form.job" placeholder="请择职位" style="width: 100%"> <el-option v-for="item in jobOptions" :key="item.value" :label="item.label" :value="item.value" /> </el-select> </el-form-item> </el-col> </el-row> <el-row :gutter="20"> <el-col :span="12"> <el-form-item label="密码" prop="password" v-if="!isEdit"> <el-input v-model="form.password" show-password /> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="入职时间" prop="entrydate"> <el-date-picker v-model="form.entrydate" type="date" placeholder="择日期" format="YYYY-MM-DD" value-format="YYYY-MM-DD" style="width: 100%" /> </el-form-item> </el-col> </el-row> <el-form-item label="头像链接" prop="image"> <el-input v-model="form.image" placeholder="请输入图片URL" /> <div class="avatar-preview" v-if="form.image"> <el-image :src="form.image" fit="cover" style="width: 100px; height: 100px; margin-top: 10px;" :preview-src-list="[form.image]" /> </div> </el-form-item> <el-form-item class="form-actions"> <el-button type="primary" @click="submitForm">提交</el-button> <el-button @click="dialogVisible = false">取消</el-button> </el-form-item> </el-form> </el-dialog> </template> <script setup> import { ref, reactive, onMounted } from &#39;vue&#39;; import axios from &#39;axios&#39;; import { ElMessage, ElMessageBox } from &#39;element-plus&#39;; // 设置 axios 实例 const apiClient = axios.create({ baseURL: &#39;http://localhost:8080/emps&#39;, timeout: 5000, }); // 表格数据 const tableData = ref([]); const total = ref(0); const selectedRows = ref([]); const formRef = ref(null); // 查询参数 const queryParams = reactive({ pageNum: 1, pageSize: 5, name: &#39;&#39;, gender: null, job: null, dateRange: [] }); // 表单数据 const form = reactive({ id: null, username: &#39;&#39;, password: &#39;&#39;, name: &#39;&#39;, gender: null, job: null, image: &#39;&#39;, entrydate: &#39;&#39; }); // 表单验证规则 const formRules = { name: [{ required: true, message: &#39;请输入姓名&#39;, trigger: &#39;blur&#39; }], username: [{ required: true, message: &#39;请输入用户名&#39;, trigger: &#39;blur&#39; }], gender: [{ required: true, message: &#39;请择性别&#39;, trigger: &#39;change&#39; }], job: [{ required: true, message: &#39;请择职位&#39;, trigger: &#39;change&#39; }], password: [{ required: true, message: &#39;请输入密码&#39;, trigger: &#39;blur&#39; }], entrydate: [{ required: true, message: &#39;请择入职日期&#39;, trigger: &#39;change&#39; }] }; // 对话框控制 const dialogVisible = ref(false); const dialogTitle = ref(&#39;新增员工&#39;); const isEdit = ref(false); // 职位映射表 const jobMap = { 1: &#39;班主任&#39;, 2: &#39;讲师&#39;, 3: &#39;学工主管&#39;, 4: &#39;教研主管&#39;, 5: &#39;咨询师&#39; }; // 职位项 const jobOptions = [ { label: &#39;班主任&#39;, value: 1 }, { label: &#39;讲师&#39;, value: 2 }, { label: &#39;学工主管&#39;, value: 3 }, { label: &#39;教研主管&#39;, value: 4 }, { label: &#39;咨询师&#39;, value: 5 } ]; // 获取职位标签类型 const getJobTagType = (job) => { const types = [&#39;&#39;, &#39;success&#39;, &#39;warning&#39;, &#39;danger&#39;, &#39;info&#39;, &#39;primary&#39;]; return types[job] || &#39;info&#39;; }; // 获取数据 const fetchData = async () => { const params = { page: queryParams.pageNum, pageSize: queryParams.pageSize, name: queryParams.name, gender: queryParams.gender, job: queryParams.job, begin: queryParams.dateRange[0] || &#39;&#39;, end: queryParams.dateRange[1] || &#39;&#39; }; try { const res = await apiClient.get(&#39;&#39;, { params }); if (res.data.code === 1) { tableData.value = res.data.data.rows.map(item => ({ ...item, // 格式化最后修改时间 updateTime: formatDateTime(item.updateTime) })); total.value = res.data.data.total; } else { ElMessage.error(&#39;获取数据失败:&#39; + res.data.msg); } } catch (error) { console.error(&#39;请求出错:&#39;, error); ElMessage.error(&#39;网络请求失败,请检查后端是否运行正常&#39;); } }; // 时间格式化函数 const formatDateTime = (dateTime) => { if (!dateTime) return &#39;&#39;; // 如果是字符串直接返回 if (typeof dateTime === &#39;string&#39;) { // 尝试解析ISO格式时间 try { const date = new Date(dateTime); return date.toLocaleString(&#39;zh-CN&#39;, { year: &#39;numeric&#39;, month: &#39;2-digit&#39;, day: &#39;2-digit&#39;, hour: &#39;2-digit&#39;, minute: &#39;2-digit&#39;, second: &#39;2-digit&#39; }).replace(/\//g, &#39;-&#39;); } catch (e) { return dateTime; } } // 如果是Date对象或时间戳 const date = new Date(dateTime); return date.toLocaleString(&#39;zh-CN&#39;, { year: &#39;numeric&#39;, month: &#39;2-digit&#39;, day: &#39;2-digit&#39;, hour: &#39;2-digit&#39;, minute: &#39;2-digit&#39;, second: &#39;2-digit&#39; }).replace(/\//g, &#39;-&#39;); }; // 显示新增对话框 const showAddDialog = () => { dialogTitle.value = &#39;新增员工&#39;; isEdit.value = false; Object.assign(form, { id: null, username: &#39;&#39;, password: &#39;&#39;, name: &#39;&#39;, gender: null, job: null, image: &#39;&#39;, entrydate: &#39;&#39; }); dialogVisible.value = true; }; // 显示编辑对话框 const handleEdit = (row) => { dialogTitle.value = &#39;编辑员工&#39;; isEdit.value = true; Object.assign(form, { ...row }); dialogVisible.value = true; }; // 提交表单 const submitForm = async () => { try { await formRef.value.validate(); if (isEdit.value) { await apiClient.put(&#39;&#39;, form); ElMessage.success(&#39;员工信息更新成功&#39;); } else { await apiClient.post(&#39;&#39;, form); ElMessage.success(&#39;员工添加成功&#39;); } dialogVisible.value = false; fetchData(); } catch (error) { if (error.name !== &#39;Error&#39;) { console.error(&#39;保存失败:&#39;, error); ElMessage.error(&#39;操作失败:&#39; + (error.response?.data?.message || error.message)); } } }; // 删除员工 const handleDelete = async (id) => { try { await ElMessageBox.confirm(`确认删除ID为 ${id} 的员工吗?`, &#39;删除确认&#39;, { confirmButtonText: &#39;确定&#39;, cancelButtonText: &#39;取消&#39;, type: &#39;warning&#39; }); await apiClient.delete(`/${[id]}`); ElMessage.success(&#39;删除成功&#39;); fetchData(); } catch (error) { if (error !== &#39;cancel&#39;) { console.error(&#39;删除失败:&#39;, error); ElMessage.error(&#39;删除失败:&#39; + (error.response?.data?.message || error.message)); } } }; // 批量删除 const batchDelete = async () => { if (selectedRows.value.length === 0) { ElMessage.warning(&#39;请至少择一条记录&#39;); return; } try { const ids = selectedRows.value.map(item => item.id); await ElMessageBox.confirm(`确认删除中的 ${ids.length} 条员工吗?`, &#39;批量删除确认&#39;, { confirmButtonText: &#39;确定&#39;, cancelButtonText: &#39;取消&#39;, type: &#39;warning&#39; }); await apiClient.delete(`/${ids}`); ElMessage.success(`成功删除 ${ids.length} 条记录`); fetchData(); } catch (error) { if (error !== &#39;cancel&#39;) { console.error(&#39;批量删除失败:&#39;, error); ElMessage.error(&#39;删除失败:&#39; + (error.response?.data?.message || error.message)); } } }; // 表格择监听 const handleSelectionChange = (rows) => { selectedRows.value = rows; }; // 处理分页大小变化 const handleSizeChange = (size) => { queryParams.pageSize = size; fetchData(); }; // 初始化加载数据 onMounted(() => { fetchData(); }); </script> <style scoped> /* 全局样式 */ .app-header { background-color: #fff; box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08); display: flex; align-items: center; padding: 0 20px; z-index: 1; } .app-title { margin: 0; font-size: 18px; font-weight: 600; color: #333; } .app-content { padding: 20px; background-color: #f0f2f5; height: calc(100vh - 60px); overflow-y: auto; } .query-section { background: #fff; padding: 20px; border-radius: 4px; margin-bottom: 20px; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); } .table-section { background: #fff; padding: 20px; border-radius: 4px; margin-bottom: 20px; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); } .pagination-section { display: flex; justify-content: center; background: #fff; padding: 15px 0; border-radius: 4px; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); } .action-buttons { display: flex; justify-content: flex-end; } .form-actions { display: flex; justify-content: center; margin-top: 20px; } .avatar-preview { display: flex; justify-content: center; margin-top: 10px; } .el-avatar { background-color: #f5f7fa; display: flex; align-items: center; justify-content: center; } .el-avatar i { color: #909399; } /* 优化对话框样式 */ .el-dialog__body { padding: 20px 25px; } /* 调整操作列按钮间距 */ .el-table .el-button { margin: 0 5px; } /* 确保择器宽度100% */ .el-select { width: 100%; } </style> 优化一下代码使代码量减少,更简洁,解决表格右侧空白的问题
07-15
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值