SpringBoot+Vue实现el-table表头筛选排序(附源码)

👨‍💻作者简介:在笑大学牲

🎟️个人主页:无所谓^_^

 ps:点赞是免费的,却可以让写博客的作者开心好几天😎

前言

后台系统对table组件的需求是最常见的,不过element-ui的el-table组件只是能满足最基本的需求而已。本篇文章就是对table组件进行自定义,实现列的自定义排序、筛选功能。替换掉原来的输入框搜索。

一、项目介绍

项目下载(本篇文章的源码在工程目录通用后台管理系统中)

gitee:https://gitee.com/wusupweilgy/springboot-vue.git

1.项目运行效果

2.技术栈

前端:vue2、element-ui组件、axios

后端:springboot、mybatis-plus、redis

3.功能

  • 表头自定义筛选
  • 表头自定义排序

4.流程图

二、前端实现

1.定义表头筛选组件:

该组件在src/components/FilterHeader/inddex.vue中,可以实现字典、数字、文本、日期的筛选排序功能

<template><div class="app-container"><el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px"><el-form-item label="字典名称" prop="dictName"><el-inputv-model="queryParams.dictName"placeholder="请输入字典名称"clearablestyle="width: 240px"@keyup.enter.native="handleQuery"/></el-form-item><el-form-item label="字典类型" prop="dictType"><el-inputv-model="queryParams.dictType"placeholder="请输入字典类型"clearablestyle="width: 240px"@keyup.enter.native="handleQuery"/></el-form-item><el-form-item label="状态" prop="status"><el-selectv-model="queryParams.status"placeholder="字典状态"clearablestyle="width: 240px"><el-optionv-for="dict in dict.type.sys_normal_disable":key="dict.value":label="dict.label":value="dict.value"/></el-select></el-form-item><el-form-item label="创建时间"><el-date-pickerv-model="dateRange"style="width: 240px"value-format="yyyy-MM-dd"type="daterange"range-separator="-"start-placeholder="开始日期"end-placeholder="结束日期"></el-date-picker></el-form-item><el-form-item><el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button><el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button></el-form-item></el-form><el-row :gutter="10" class="mb8"><el-col :span="1.5"><el-buttontype="primary"plainicon="el-icon-plus"size="mini"@click="handleAdd"v-hasPermi="['system:dict:add']">新增</el-button></el-col><el-col :span="1.5"><el-buttontype="success"plainicon="el-icon-edit"size="mini":disabled="single"@click="handleUpdate"v-hasPermi="['system:dict:edit']">修改</el-button></el-col><el-col :span="1.5"><el-buttontype="danger"plainicon="el-icon-delete"size="mini":disabled="multiple"@click="handleDelete"v-hasPermi="['system:dict:remove']">删除</el-button></el-col><el-col :span="1.5"><el-buttontype="warning"plainicon="el-icon-download"size="mini"@click="handleExport"v-hasPermi="['system:dict:export']">导出</el-button></el-col><el-col :span="1.5"><el-buttontype="danger"plainicon="el-icon-refresh"size="mini"@click="handleRefreshCache"v-hasPermi="['system:dict:remove']">刷新缓存</el-button></el-col><right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar></el-row><el-table v-loading="loading" :data="typeList" @selection-change="handleSelectionChange"><el-table-column type="selection" width="55" align="center" /><el-table-column label="字典编号" align="center" prop="dictId" /><el-table-column label="字典名称" align="center" prop="dictName" :show-overflow-tooltip="true" /><el-table-column label="字典类型" align="center" :show-overflow-tooltip="true"><template slot-scope="scope"><router-link :to="'/system/dict-data/index/' + scope.row.dictId" class="link-type"><span>{{ scope.row.dictType }}</span></router-link></template></el-table-column><el-table-column label="状态" align="center" prop="status"><template slot-scope="scope"><dict-tag :options="dict.type.sys_normal_disable" :value="scope.row.status"/></template></el-table-column><el-table-column label="备注" align="center" prop="remark" :show-overflow-tooltip="true" /><el-table-column label="创建时间" align="center" prop="createTime" width="180"><template slot-scope="scope"><span>{{ parseTime(scope.row.createTime) }}</span></template></el-table-column><el-table-column label="操作" align="center" class-name="small-padding fixed-width"><template slot-scope="scope"><el-buttonsize="mini"type="text"icon="el-icon-edit"@click="handleUpdate(scope.row)"v-hasPermi="['system:dict:edit']">修改</el-button><el-buttonsize="mini"type="text"icon="el-icon-delete"@click="handleDelete(scope.row)"v-hasPermi="['system:dict:remove']">删除</el-button></template></el-table-column></el-table><paginationv-show="total>0":total="total":page.sync="queryParams.pageNum":limit.sync="queryParams.pageSize"@pagination="getList"/><!-- 添加或修改参数配置对话框 --><el-dialog :title="title" :visible.sync="open" width="500px" append-to-body><el-form ref="form" :model="form" :rules="rules" label-width="80px"><el-form-item label="字典名称" prop="dictName"><el-input v-model="form.dictName" placeholder="请输入字典名称" /></el-form-item><el-form-item label="字典类型" prop="dictType"><el-input v-model="form.dictType" placeholder="请输入字典类型" /></el-form-item><el-form-item label="状态" prop="status"><el-radio-group v-model="form.status"><el-radiov-for="dict in dict.type.sys_normal_disable":key="dict.value":label="dict.value">{{dict.label}}</el-radio></el-radio-group></el-form-item><el-form-item label="备注" prop="remark"><el-input v-model="form.remark" type="textarea" placeholder="请输入内容"></el-input></el-form-item></el-form><div slot="footer" class="dialog-footer"><el-button type="primary" @click="submitForm">确 定</el-button><el-button @click="cancel">取 消</el-button></div></el-dialog></div>
</template><script>
import { listType, getType, delType, addType, updateType, refreshCache } from "@/api/system/dict/type";export default {name: "Dict",dicts: ['sys_normal_disable'],data() {return {// 遮罩层loading: true,// 选中数组ids: [],// 非单个禁用single: true,// 非多个禁用multiple: true,// 显示搜索条件showSearch: true,// 总条数total: 0,// 字典表格数据typeList: [],// 弹出层标题title: "",// 是否显示弹出层open: false,// 日期范围dateRange: [],// 查询参数queryParams: {pageNum: 1,pageSize: 10,dictName: undefined,dictType: undefined,status: undefined},// 表单参数form: {},// 表单校验rules: {dictName: [{ required: true, message: "字典名称不能为空", trigger: "blur" }],dictType: [{ required: true, message: "字典类型不能为空", trigger: "blur" }]}};},created() {this.getList();},methods: {/** 查询字典类型列表 */getList() {this.loading = true;listType(this.addDateRange(this.queryParams, this.dateRange)).then(response => {this.typeList = response.rows;this.total = response.total;this.loading = false;});},// 取消按钮cancel() {this.open = false;this.reset();},// 表单重置reset() {this.form = {dictId: undefined,dictName: undefined,dictType: undefined,status: "0",remark: undefined};this.resetForm("form");},/** 搜索按钮操作 */handleQuery() {this.queryParams.pageNum = 1;this.getList();},/** 重置按钮操作 */resetQuery() {this.dateRange = [];this.resetForm("queryForm");this.handleQuery();},/** 新增按钮操作 */handleAdd() {this.reset();this.open = true;this.title = "添加字典类型";},// 多选框选中数据handleSelectionChange(selection) {this.ids = selection.map(item => item.dictId)this.single = selection.length!=1this.multiple = !selection.length},/** 修改按钮操作 */handleUpdate(row) {this.reset();const dictId = row.dictId || this.idsgetType(dictId).then(response => {this.form = response.data;this.open = true;this.title = "修改字典类型";});},/** 提交按钮 */submitForm: function() {this.$refs["form"].validate(valid => {if (valid) {if (this.form.dictId != undefined) {updateType(this.form).then(response => {this.$modal.msgSuccess("修改成功");this.open = false;this.getList();});} else {addType(this.form).then(response => {this.$modal.msgSuccess("新增成功");this.open = false;this.getList();});}}});},/** 删除按钮操作 */handleDelete(row) {const dictIds = row.dictId || this.ids;this.$modal.confirm('是否确认删除字典编号为"' + dictIds + '"的数据项?').then(function() {return delType(dictIds);}).then(() => {this.getList();this.$modal.msgSuccess("删除成功");}).catch(() => {});},/** 导出按钮操作 */handleExport() {this.download('system/dict/type/export', {...this.queryParams}, `type_${new Date().getTime()}.xlsx`)},/** 刷新缓存按钮操作 */handleRefreshCache() {refreshCache().then(() => {this.$modal.msgSuccess("刷新成功");});}}
};
</script>

2.在main.js中全局注册组件:

并且还需要注册全局事件总线用于组件之间的通信

// 表头筛选组件
import FilterHeader from '@/components/FilterHeader'
Vue.component('FilterHeader', FilterHeader)
new Vue({router,store,render: h => h(App),beforeCreate(){Vue.prototype.$bus = this	//安装全局事件总线}
}).$mount('#app')
/*** @param: fileName - 文件名称* @param: 数据返回 1) 无后缀匹配 - false* @param: 数据返回 2) 匹配图片 - image* @param: 数据返回 3) 匹配 txt - txt* @param: 数据返回 4) 匹配 excel - excel* @param: 数据返回 5) 匹配 word - word* @param: 数据返回 6) 匹配 pdf - pdf* @param: 数据返回 7) 匹配 ppt - ppt* @param: 数据返回 8) 匹配 视频 - video* @param: 数据返回 9) 匹配 音频 - radio* @param: 数据返回 10) 其他匹配项 - other* @author: ljw**/export function fileSuffixTypeUtil(fileName){// 后缀获取var suffix = "";// 获取类型结果var result = "";try {var flieArr = fileName.split(".");suffix = flieArr[flieArr.length - 1];} catch (err) {suffix = "";}// fileName无后缀返回 falseif (!suffix) {result = false;return result;}// 图片格式var imglist = ["png", "jpg", "jpeg", "bmp", "gif"];// 进行图片匹配result = imglist.some(function (item) {return item == suffix;});if (result) {result = "image";return result;}// 匹配txtvar txtlist = ["txt"];result = txtlist.some(function (item) {return item == suffix;});if (result) {result = "txt";return result;}// 匹配 excelvar excelist = ["xls", "xlsx"];result = excelist.some(function (item) {return item == suffix;});if (result) {result = "excel";return result;}// 匹配 wordvar wordlist = ["doc", "docx"];result = wordlist.some(function (item) {return item == suffix;});if (result) {result = "word";return result;}// 匹配 pdfvar pdflist = ["pdf"];result = pdflist.some(function (item) {return item == suffix;});if (result) {result = "pdf";return result;}// 匹配 pptvar pptlist = ["ppt"];result = pptlist.some(function (item) {return item == suffix;});if (result) {result = "ppt";return result;}// 匹配 视频var videolist = ["mp4", "m2v", "mkv","ogg", "flv", "avi", "wmv", "rmvb"];result = videolist.some(function (item) {return item == suffix;});if (result) {result = "video";return result;}// 匹配 音频var radiolist = ["mp3", "wav", "wmv"];result = radiolist.some(function (item) {return item == suffix;});if (result) {result = "radio";return result;}// 其他 文件类型result = "other";return result;
};

三、后端实现

1.表头筛选数据库结构

CREATE TABLE `sys_header_filter` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',`user_name` varchar(32) DEFAULT NULL COMMENT '用户名',`page_name` varchar(32) DEFAULT NULL COMMENT '当前页面的路由名',`table_name` varchar(32) DEFAULT NULL COMMENT '字段所属表',`field_name` varchar(32) DEFAULT NULL COMMENT '属性名称',`condition_type` varchar(16) DEFAULT NULL COMMENT '条件',`text` varchar(64) DEFAULT NULL COMMENT '文本值',`start_value` varchar(64) DEFAULT '\0' COMMENT '开始值',`del_flag` varbinary(16) DEFAULT '0' COMMENT '逻辑删除',`end_value` varchar(64) DEFAULT NULL COMMENT '结束值',`type` varchar(16) DEFAULT NULL COMMENT '类型',`order_type` varchar(16) DEFAULT NULL COMMENT '排序条件',`checkbox` varchar(64) DEFAULT NULL COMMENT '多选框值',`create_by` varchar(32) DEFAULT NULL COMMENT '创建人',`update_by` varchar(32) DEFAULT NULL COMMENT '最后更新人',`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP COMMENT '最后更新时间',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=295 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='用户默认表头筛选表'

2.后端主要代码:Mybatis自定义实现sql拦截器

package com.wusuowei.common.utils.mybatis;import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLOrderBy;
import com.alibaba.druid.sql.ast.SQLOrderingSpecification;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr;
import com.alibaba.druid.sql.ast.expr.SQLBinaryOperator;
import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;
import com.alibaba.druid.sql.ast.statement.SQLSelect;
import com.alibaba.druid.sql.ast.statement.SQLSelectOrderByItem;
import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock;
import com.alibaba.druid.sql.ast.statement.SQLSelectStatement;
import com.alibaba.druid.sql.parser.SQLExprParser;
import com.alibaba.druid.sql.parser.SQLParserUtils;
import com.alibaba.druid.sql.parser.SQLStatementParser;
import com.alibaba.druid.util.JdbcUtils;
import com.alibaba.fastjson2.JSON;
import com.wusuowei.common.utils.ServletUtils;
import com.wusuowei.common.utils.StringUtils;
import com.wusuowei.common.web.domain.BaseEntity;
import com.wusuowei.common.web.page.ConditionDomain;
import com.wusuowei.common.web.page.TableSupport;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.util.Iterator;
import java.util.List;/*** Mybagis拦截器,拦截分页查询带筛选条件的请求,该拦截器在分页拦截器之后执行** @author liguangyao* @date 2023/9/5*/
@Component
//拦截StatementHandler类中参数类型为Statement的prepare方法(prepare=在预编译SQL前加入修改的逻辑)
//即拦截 Statement prepare(Connection var1, Integer var2) 方法
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class HeadFilterInterceptor implements Interceptor {private static final Logger log = LoggerFactory.getLogger(HeadFilterInterceptor.class);/*** 获取分页请求表头筛选的条件** @param request* @return*/private BaseEntity getParamsPlusFromRequest(HttpServletRequest request) {if (HttpMethod.GET.name().equals(request.getMethod()) && ObjectUtil.isNotNull(request.getParameter(TableSupport.PARAMS_PLUS))) {BaseEntity baseEntity = new BaseEntity();baseEntity.setParamsPlus(request.getParameter(TableSupport.PARAMS_PLUS));baseEntity.setDatabaseTable(request.getParameter(TableSupport.DATABASE_TABLE));baseEntity.setPageNum(request.getParameter(TableSupport.PAGE_NUM));baseEntity.setPageSize(request.getParameter(TableSupport.PAGE_SIZE));return baseEntity;} else if (HttpMethod.POST.name().equals(request.getMethod())) {BaseEntity baseEntity = new BaseEntity();StringBuilder sb = new StringBuilder();try (BufferedReader reader = request.getReader();) {char[] buff = new char[1024];int len;while ((len = reader.read(buff)) != -1) {sb.append(buff, 0, len);}if (StrUtil.isBlank(sb)) {return null;}// 判断是否是分页请求if (!sb.toString().contains(TableSupport.PAGE_NUM) || !sb.toString().contains(TableSupport.PAGE_SIZE) || !sb.toString().contains(TableSupport.PARAMS_PLUS)) {return null;}baseEntity = JSON.parseObject(sb.toString(), BaseEntity.class);if (StringUtils.isBlank(baseEntity.getPageNum()) || StringUtils.isBlank(baseEntity.getPageSize())) {return null;}} catch (Exception e) {log.error("表头筛选参数JSON转换异常:{}", e);}// 判断是否存在sql注入if (ObjectUtil.isNull(baseEntity) || MySqlUtil.sqlInjectionVerification(baseEntity.getParamsPlus()) || StringUtils.isBlank(baseEntity.getParamsPlus())) {return null;}// 将json格式的筛选条件字符串转换成集合return baseEntity;}return null;}@Overridepublic Object intercept(Invocation invocation) throws Throwable {// 判断是否是前台的请求if (ObjectUtil.isEmpty(ServletUtils.getRequestAttributes())) {return invocation.proceed();}HttpServletRequest request = ServletUtils.getRequest();// 获取表头筛选条件BaseEntity baseEntity = this.getParamsPlusFromRequest(request);if (ObjectUtil.isNull(baseEntity)) {return invocation.proceed();}List<ConditionDomain> paramsPlus = JSON.parseArray(baseEntity.getParamsPlus(), ConditionDomain.class);StatementHandler statementHandler = (StatementHandler) invocation.getTarget();BoundSql boundSql = statementHandler.getBoundSql();// 获取到原始sql语句String sql = boundSql.getSql();// 如果获取不到该sql中的数据库名,执行原语句String tableName = MySqlUtil.getTableName(sql, baseEntity.getDatabaseTable());if (StringUtils.isBlank(tableName)) {return invocation.proceed();}// 根据条件拼接sqlString mSql = resetSQL(tableName, sql, paramsPlus);// 如果拼接的sql不正确直接执行原sqlif (!MySqlUtil.isSqlValid(mSql)) {return invocation.proceed();}// 通过反射修改sql语句Field field = boundSql.getClass().getDeclaredField("sql");field.setAccessible(true);field.set(boundSql, mSql);log.debug("原来的SQL====>" + sql);log.debug("拼接后的SQL====>" + mSql);return invocation.proceed();}/*** 获取拼接后的完整sql** @param tableName* @param sql* @param paramsPlus* @return*/private String resetSQL(String tableName, String sql, List<ConditionDomain> paramsPlus) {// 获取表的别名String tableAliases = tableName + ".";SQLStatementParser parser = SQLParserUtils.createSQLStatementParser(sql, JdbcUtils.MYSQL);List<SQLStatement> stmtList = parser.parseStatementList();SQLStatement stmt = stmtList.get(0);if (stmt instanceof SQLSelectStatement) {// 根据参数拼接的where条件sqlString whereStr = splicingWhereSQL(tableAliases, sql, paramsPlus);// 拿到SQLSelectSQLSelectStatement selectStmt = (SQLSelectStatement) stmt;SQLSelect sqlselect = selectStmt.getSelect();SQLSelectQueryBlock query = (SQLSelectQueryBlock) sqlselect.getQuery();if (ObjectUtil.isNotEmpty(whereStr)) {SQLExprParser constraintsParser = SQLParserUtils.createExprParser(whereStr, JdbcUtils.MYSQL);SQLExpr constraintsExpr = constraintsParser.expr();SQLExpr whereExpr = query.getWhere();// 修改where表达式if (whereExpr == null) {query.setWhere(constraintsExpr);} else {SQLBinaryOpExpr newWhereExpr = new SQLBinaryOpExpr(whereExpr, SQLBinaryOperator.BooleanAnd, constraintsExpr);query.setWhere(newWhereExpr);}}// 创建新的排序项for (ConditionDomain item : paramsPlus) {SQLIdentifierExpr newOrderByExpr = new SQLIdentifierExpr(tableAliases + StringUtils.toUnderScoreCase(item.getFieldName()));SQLSelectOrderByItem newOrderByItem = null;// 判断字段升序降序boolean isAsc = SQLOrderingSpecification.ASC.toString().equalsIgnoreCase(item.getOrderType());if (isAsc) {newOrderByItem = new SQLSelectOrderByItem(newOrderByExpr, SQLOrderingSpecification.ASC);} else {newOrderByItem = new SQLSelectOrderByItem(newOrderByExpr, SQLOrderingSpecification.DESC);}// 将新的排序项添加到已有的排序项后面SQLOrderBy orderBy = query.getOrderBy();// 判断原sql是否有排序规则if (orderBy == null) {SQLOrderBy sqlOrderBy = new SQLOrderBy();sqlOrderBy.addItem(newOrderByItem);query.addOrderBy(sqlOrderBy);} else {orderBy.addItem(newOrderByItem);}}return sqlselect.toString();}return sql;}/*** where条件拼接sql (table.name = '李四' AND table.age = 18) 带括号和表名称的格式** @param paramsPlus* @param tableAliases* @return*/private String splicingWhereSQL(String tableAliases, String sql, List<ConditionDomain> paramsPlus) {StringBuffer whereBuffer = new StringBuffer();Iterator<ConditionDomain> keyIter = paramsPlus.iterator();// 找到第一个where条件进行拼接while (keyIter.hasNext()) {ConditionDomain conditionDomain = keyIter.next();String codition = ConditionChoseMap.getCodition(conditionDomain);if (ObjectUtil.isNotEmpty(conditionDomain.getTableName())) {whereBuffer.append(MySqlUtil.getTableAlias(conditionDomain.getTableName(), sql)).append(".").append(StringUtils.toUnderScoreCase(conditionDomain.getFieldName())).append(ConditionChoseMap.getCodition(conditionDomain));break;}// 如果查询if (ObjectUtil.isNotEmpty(codition)) {whereBuffer.append(tableAliases).append(StringUtils.toUnderScoreCase(conditionDomain.getFieldName())).append(ConditionChoseMap.getCodition(conditionDomain));break;}}// 后面的where条件用AND进行拼接while (keyIter.hasNext()) {ConditionDomain conditionDomain = keyIter.next();String codition = ConditionChoseMap.getCodition(conditionDomain);if (ObjectUtil.isNotEmpty(conditionDomain.getTableName())) {whereBuffer.append(MySqlUtil.getTableAlias(conditionDomain.getTableName(), sql)).append(".").append(StringUtils.toUnderScoreCase(conditionDomain.getFieldName())).append(ConditionChoseMap.getCodition(conditionDomain));break;}if (ObjectUtil.isNotEmpty(codition)) {whereBuffer.append(" AND ").append(tableAliases).append(StringUtils.toUnderScoreCase(conditionDomain.getFieldName())).append(ConditionChoseMap.getCodition(conditionDomain));}}return whereBuffer.toString();}
}

四、使用

<template><div><div style="margin: 10px 0"><right-toolbar @handleQuery="handleQuery" @resetFilter="resetFilter" @queryTable="getList"></right-toolbar></div><div style="margin: 10px 0"><el-buttontype="primary"plainicon="el-icon-top"size="mini"@click="showUploadDialog">点击上传</el-button><el-popconfirmclass="ml-5"confirm-button-text='确定'cancel-button-text='我再想想'icon="el-icon-info"icon-color="red"title="您确定批量删除这些数据吗?"@confirm="delBatch"><el-button style="margin: 0 5px" icon="el-icon-delete" size="mini" type="danger" slot="reference" plain>批量删除</el-button></el-popconfirm></div><el-table :data="tableData" border stripe v-loading="loading"@selection-change="handleSelectionChange"><el-table-column type="selection" width="55"></el-table-column><el-table-column prop="id" label="ID" width="80"><template slot="header" slot-scope="scope"><filter-header@handleQuery="handleQuery":paramsPlusTemp.sync="paramsPlusTemp":column="scope.column"field-name="type"filter-type="number"></filter-header></template></el-table-column><el-table-column prop="fileName" width="160" label="文件名称" :show-overflow-tooltip="true"><template slot="header" slot-scope="scope"><filter-header@handleQuery="handleQuery":paramsPlusTemp.sync="paramsPlusTemp":column="scope.column"field-name="type"filter-type="text"></filter-header></template><template slot-scope="scope" v-if="scope.row.fileName"><span @click="copyText(scope.row.fileName)" style="cursor: pointer">{{scope.row.fileName}}</span></template></el-table-column><el-table-column prop="fileType" align="center" label="文件类型"><template slot="header" slot-scope="scope"><filter-header@handleQuery="handleQuery":paramsPlusTemp.sync="paramsPlusTemp":column="scope.column":customArrList="dict.type.sys_file_type"field-name="type"filter-type="checkbox"></filter-header></template><template v-slot="scope"><dict-tag :options="dict.type.sys_file_type" :value="scope.row.fileType"/></template></el-table-column><el-table-column prop="fileSize" label="文件大小(mb)"><template slot="header" slot-scope="scope"><filter-header@handleQuery="handleQuery":paramsPlusTemp.sync="paramsPlusTemp":column="scope.column"field-name="type"filter-type="number"></filter-header></template><template slot-scope="scope">{{ scope.row.fileSize | transformByte }}</template></el-table-column><el-table-column prop="nickName" label="上传用户" :show-overflow-tooltip="true"><template slot="header" slot-scope="scope"><filter-header@handleQuery="handleQuery":paramsPlusTemp.sync="paramsPlusTemp":column="scope.column"field-name="type"filter-type="text"></filter-header></template></el-table-column><el-table-column prop="createTime" label="上传时间"><template slot="header" slot-scope="scope"><filter-header@handleQuery="handleQuery":paramsPlusTemp.sync="paramsPlusTemp":column="scope.column"field-name="type"filter-type="date"></filter-header></template></el-table-column><el-table-column prop="enable" width="80" label="启用"><template slot="header" slot-scope="scope"><filter-header@handleQuery="handleQuery":paramsPlusTemp.sync="paramsPlusTemp":column="scope.column":customArrList="dict.type.sys_file_status"field-name="type"filter-type="checkbox"></filter-header></template><template slot-scope="scope"><el-switch v-model="scope.row.enable" active-color="#13ce66" inactive-color="#ccc"@change="changeEnable(scope.row)"></el-switch></template></el-table-column><el-table-column fixed="right" label="操作" width="200" align="center"><template slot-scope="scope"><el-buttonsize="mini"type="text"@click="lookonline(scope.row.url)"slot="reference"><i class="el-icon-view"></i>预览</el-button><el-buttonsize="mini"type="text"@click="download(scope.row)"slot="reference"><i class="el-icon-download"></i>下载</el-button><el-popconfirmconfirm-button-text='确定'cancel-button-text='我再想想'icon="el-icon-info"icon-color="red"title="您确定删除吗?"@confirm="del(scope.row)"><el-button style="margin: 0 10px" size="mini"type="text"slot="reference"><i class="el-icon-delete"></i>删除</el-button></el-popconfirm></template></el-table-column></el-table><div style="padding: 10px 0"><el-pagination@size-change="handleSizeChange"@current-change="handleCurrentChange":current-page="pagequery.pageNum":page-sizes="[2, 5, 10, 20]":page-size="pagequery.pageSize"layout="total, sizes, prev, pager, next, jumper":total="total"></el-pagination></div><FileUpload :fileTableVisible="fileTableVisible" @uploadFileList="uploadFileList"@changeFileDialogVisible="changeFileDialogVisible"></FileUpload></div>
</template><script>
import FileUpload from "@/components/FileUpload";
import {getFilesPage, delFilesByIds, delFileById, updateFile} from '@/api/system/file'
import axios from "axios";
import * as base64Encode from "js-base64";
import {getDefaultHeaderFilter, uploadDefaultHeaderFilter} from "@/api/system/headerFilter";
import {copyText} from "@/utils";export default {name: "File",components: {FileUpload},dicts: ['sys_file_type','sys_file_status'],data() {return {// 查询参数queryParams: {pageNum: 1,pageSize: 10,databaseTable: 'sys_file',},// 筛选和排序条件paramsPlusTemp: [],dateRange: '',// 遮罩层loading: true,fileTypes: [{label: '图片', value: 'image'}, {label: '文本', value: 'txt'}, {label: '视频',value: 'video'}, {label: '音频', value: 'radio'}, {label: 'Excel', value: 'excel'}, {label: 'Word',value: 'word'}, {label: 'pdf', value: 'pdf'}, {label: 'PPT', value: 'ppt'}, {label: '其他', value: 'other'}],pagequery: {  //分页查询条件pageNum: 1,pageSize: 5,},fileTableVisible: false,uploadHeaders: localStorage.getItem("token"),tableData: [],multipleSelection: [],total: 0,headers: localStorage.getItem('token'),}},created() {getDefaultHeaderFilter().then(res => {if (res.data) {localStorage.setItem("defaultHeader", JSON.stringify(res.data))this.paramsPlusTemp = res.data[this.$route.name]}this.getList();})},methods: {copyText,/** 刷新按钮操作 */resetQuery() {this.resetForm("queryForm");this.handleQuery();},/** 重置更新所有表头筛选组件 */resetFilter() {this.$bus.$emit('resetFilter')this.paramsPlusTemp = []this.queryParams.paramsPlus = nulluploadDefaultHeaderFilter(this.$route.name, null).then(res => {localStorage.removeItem('defaultHeader')})this.getList()},getList() {this.loading = true;this.queryParams.paramsPlus = this.paramsPlusTemp && this.paramsPlusTemp.length === 0 ? null : JSON.stringify(this.paramsPlusTemp)getFilesPage(this.queryParams).then((res) => {//console.log("resp:", res);this.total = res.totalthis.tableData = res.rowsthis.loading = false;});},showUploadDialog() {//如果有文件没有上传成功就保留这个文件,这样下次打开弹框还有记录this.fileTableVisible = true},changeFileDialogVisible(value) {this.fileTableVisible = value},uploadFileList() {this.getList()},changeEnable(row) {updateFile(row).then(res => {if (res.code === 200) {this.$message.success("操作成功")}})},del(file) {delFileById(file).then(res => {if (res.code === 200) {this.$message.success("删除成功")this.getList()}})},handleSelectionChange(val) {this.multipleSelection = val},delBatch() {if (this.multipleSelection.length === 0) {this.$message.warning("请选择删除的文件")return}delFilesByIds(this.multipleSelection).then(res => {if (res.code === 200) {this.$message.success("批量删除成功")this.getList()}})},reset() {this.dateRange = ''this.pagequery = {pageNum: 1,pageSize: 5,}this.getList()},handleSizeChange(pageSize) {this.pagequery.pageSize = pageSizethis.getList()},handleCurrentChange(pageNum) {this.pagequery.pageNum = pageNumthis.getList()},// 下载文件download(row) {axios.get(row.url,{responseType: 'blob'}).then((res) => {console.log('文件下载成功');const blob = new Blob([res.data]);const fileName = row.fileName;//对于<a>标签,只有 Firefox 和 Chrome(内核) 支持 download 属性//IE10以上支持blob,但是依然不支持downloadif ('download' in document.createElement('a')) {//支持a标签download的浏览器const link = document.createElement('a');//创建a标签link.download = fileName;//a标签添加属性link.style.display = 'none';link.href = URL.createObjectURL(blob);document.body.appendChild(link);link.click();//执行下载URL.revokeObjectURL(link.href); //释放urldocument.body.removeChild(link);//释放标签} else {navigator.msSaveBlob(blob, fileName);}}).catch((res) => {console.log('文件下载失败');});},lookonline(url) {console.log(url)window.open('http://127.0.0.1:8012/onlinePreview?url=' + encodeURIComponent(base64Encode.encode(url)));},/** 搜索按钮操作 */handleQuery() {this.queryParams.pageNum = 1;this.getList();uploadDefaultHeaderFilter(this.$route.name, this.queryParams.paramsPlus).then(res => {if (res.data) {localStorage.setItem('defaultHeader', JSON.stringify(res.data))this.paramsPlusTemp = res.data[this.$route.name]}})this.getList();},//获取时间区间方法dateFormat(picker) {this.pagequery.startTime = picker[0]this.pagequery.endTime = picker[1]},},filters: {transformByte(size) {if (!size) {return '0B'}const unitSize = 1024// if (size < unitSize) {//   return size + ' B'// }// // KB// if (size < Math.pow(unitSize, 2)) {//   return (size / unitSize).toFixed(2) + ' K';// }// MB// if (size < Math.pow(unitSize, 3)) {return (size / Math.pow(unitSize, 2)).toFixed(2) + ' MB'// }// // GB// if (size < Math.pow(unitSize, 4)) {//   return (size / Math.pow(unitSize, 3)).toFixed(2) + ' GB';// }// // TB// return (size / Math.pow(unitSize, 4)).toFixed(2) + ' TB';},transformType(filename) {return filename.substr(filename.lastIndexOf('.') + 1)}}
}
</script><style scoped></style>

五、注意点

本片文章的大概交互流程是,前端当前页面的表头筛选组件(子组件),将数据传递到当前组件中(当前页面父组件),并且请求了后端,持久化了表头筛选数据,发送列表请求。后台根据参数修改原sql。然后下次再查询当前页面或刷新时,回先从redis缓存中获取全部的表头筛选数据,获取成功后放入浏览器缓存,进而每个页面就能获取到对应的表头筛选数据进行数据渲染。

小结

文章贴的代码有点多了,大家可以去我的gitee上下载下来运行。项目的功能我测试优化了很多次,如果代码有何异常或者不完整欢迎在评论区留言。如果这篇文章有幸帮助到你,希望读者大大们可以给作者点个赞呀😶‍🌫️😶‍🌫️😶‍🌫️

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/718656.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Grpc项目集成到java方式调用实践

背景&#xff1a;由于项目要对接到grcp 的框架&#xff0c;然后需要对接老外的东西&#xff0c;还有签名和证书刚开始没有接触其实有点懵逼。 gRPC 是由 Google 开发的高性能、开源的远程过程调用&#xff08;RPC&#xff09;框架。它建立在 HTTP/2 协议之上&#xff0c;使用 …

D7805 正电压稳压电路应用——体积小,成本低,性能好

D7805 构成的 5V 稳压电源为输出电压5V&#xff0c;输出电流 1000mA 的稳压电源它由滤波电容 C1,C3,防止自激电容 C2、C3 和一只固定三端稳压器&#xff08;7805&#xff09;后级加 LC 滤波极为简洁方便地搭成&#xff0c;输入直流电压范围为 7~35V&#xff0c;此直流电压经过D…

yolov8-更换卷积模块-ContextGuidedBlock_Down

源码解读 class ContextGuidedBlock_Down(nn.Module):"""the size of feature map divided 2, (H,W,C)---->(H/2, W/2, 2C)"""def __init__(self, nIn, dilation_rate2, reduction16):"""args:nIn: the channel of input fea…

Hack The Box-Bizness

目录 信息收集 nmap dirsearch WEB Get shell 提权 get user flag get root flag 信息收集 nmap 端口扫描┌──(root㉿ru)-[~/kali/hackthebox] └─# nmap -p- 10.10.11.252 --min-rate 10000 -oA port Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-03-04 1…

力扣 第 387 场周赛 解题报告 | 珂学家 | 离散化树状数组 + 模拟场

前言 整体评价 手速场模拟场&#xff0c;思路和解法都蛮直接的。 所以搞点活 如果T2&#xff0c;如果不固定左上角&#xff0c;批量查询某个点为左上角&#xff0c;求满足总和 ≤ k \le k ≤k的子矩阵个数 如果T2&#xff0c;如果不固定左上角&#xff0c;求总和 ≤ k \le k…

Spring的Bean的生命周期 | 有图有案例

Spring的Bean的生命周期 Spring的Bean的生命周期整体过程实例化初始化服务销毁循环依赖问题 完整生命周期演示 Spring的Bean的生命周期 Spring Bean的生命周期&#xff1a;从Bean的实例化之后&#xff0c;通过反射创建出对象之后&#xff0c;到Bean称为一个完整的对象&#xf…

快上车:什么是人工智能?人工智能和普通程序的区别

什么是人工智能&#xff1f; 虽然AI历史很悠久&#xff0c;上个世纪50年代就有各种概念&#xff0c;但是发展很慢。第一次对人类的冲击就是1997年IBM深蓝击败国际象棋世界冠军&#xff0c;引起了人们的广泛关注&#xff0c;之后又销声匿迹。突然间2016人工智能alphaGO战胜了围…

具身智能计算系统,机器人时代的 Android | 新程序员

【导读】具身智能作为一种新兴的研究视角和方法论&#xff0c;正在刷新我们对智能本质及其发展的理解&#xff1a;传统的 AI 模型往往将智能视为一种独立于实体存在的抽象能力&#xff0c;而具身智能则主张智能是实体与其环境持续互动的结果。 本文深度剖析了具身智能计算系统…

程序员如何选择职业赛道?

一、自我评估与兴趣探索 程序员选择职业赛道时&#xff0c;可以考虑以下几个关键因素&#xff1a; 1、兴趣与热情&#xff1a;首先要考虑自己的兴趣和热情&#xff0c;选择符合个人喜好和激情的领域&#xff0c;能够激励自己持续学习和进步。 2、技术能力&am…

mysql5.7配置主从

原理&#xff1a; MySQL主从复制的工作原理如下:1. 主服务器产生Binlog日志当主服务器的数据库发生数据修改操作时,如INSERT、UPDATE、DELETE语句执行,主服务器会记录这些操作的日志信息到二进制日志文件中。2. 从服务器读取Binlog日志 从服务器会向主服务器发送请求,主服务器把…

微信小程序开发学习笔记《18》uni-app框架-网络请求与轮播图

微信小程序开发学习笔记《18》uni-app框架-网络请求 博主正在学习微信小程序开发&#xff0c;希望记录自己学习过程同时与广大网友共同学习讨论。建议仔细阅读uni-app对应官方文档 一、下载网络请求包 这个包是以前黑马程序员老师写的一个包&#xff0c;跟着课程学习&#x…

Open3D(C++) 指定点数的体素滤波

目录 一、算法原理1、算法过程2、参考文献二、代码实现三、结果展示本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫与GPT。 一、算法原理 1、算法过程 对于数据量较大的点云,在后期进行配准时会影响计算效率。而体素格网…

LCR 170. 交易逆序对的总数

解题思路&#xff1a; 归并排序&#xff0c;在归并的过程中不断计算逆序对的个数 count mid -i 1&#xff1b;的来源见下图&#xff0c;因为两个数组都是单调递增的&#xff0c;所以如果第一个数组的前一个元素大于第二个数组的对应元素&#xff0c;那么第一个数组的这一元素…

借助Aspose.SVG图像控件,在 C# 中将图像转换为 Base64

Base64 编码是一种二进制到文本的编码方案&#xff0c;可有效地将二进制数据转换为 ASCII 字符&#xff0c;为数据交换提供通用格式。在某些情况下&#xff0c;我们可能需要将JPG或PNG图像转换为 Base64 字符串数据。在这篇博文中&#xff0c;我们将学习如何在 C# 中将图像转换…

分享经典、现代和前沿软件工程课程

随着信息技术的发展&#xff0c;软件已经深入到人类社会生产和生活的各个方面。软件工程是将工程化的方法运用到软件的开发、运行和维护之中&#xff0c;以达到提高软件质量&#xff0c;降低开发成本的目的。软件工程已经成为当今最活跃、最热门的学科之一。 本次软件工程MOOC课…

现在如何才能开通微信公众号留言功能?

为什么公众号没有留言功能&#xff1f;2018年2月12日之后直到现在&#xff0c;新注册公众号的运营者会发现一个问题&#xff1a;无论是个人还是企业的公众号&#xff0c;在后台都找不到留言功能了。这对公众号来说绝对是一个极差的体验&#xff0c;少了一个这么重要的功能&…

万村乐数字乡村系统开源代码:革命性引领,助推乡村振兴新篇章

如今&#xff0c;国际社会普遍认为信息化、数字化已是重大且不可逆转的发展趋势&#xff0c;如何让广大农村地区充分分享到这个发展带来的红利&#xff0c;从而提升农村的经济活力&#xff0c;确保村民生活质量不断优化&#xff0c;已然成为我们需要认真研究并积极解决的重大议…

Window下编写的sh文件在Linux/Docker中无法使用

Window下编写的sh文件在Linux/Docker中无法使用 一、sh文件目的1.1 初始状态1.2 目的 二、过程与异常2.1 首先获取标准ubuntu20.04 - 正常2.2 启动ubuntu20.04容器 - 正常2.3 执行windows下写的preInstall文件 - 报错 三、检查和处理3.1 评估异常3.2 处理异常3.3 调整后运行测试…

uniapp+vue基于Android的图书馆借阅系统qb4y3-nodejs-php-pyton

uni-app框架&#xff1a;使用Vue.js开发跨平台应用的前端框架&#xff0c;编写一套代码&#xff0c;可编译到Android、小程序等平台。 框架支持:springboot/django/php/Ssm/flask/express均支持 前端开发:vue 语言&#xff1a;pythonjavanode.jsphp均支持 运行软件:idea/eclip…

2023天津公租房网上登记流程图,注册到信息填写

2023年天津市公共租赁住房网上登记流程图 小编为大家整理了天津市公共租赁住房网上登记流程&#xff0c;从登记到填写信息。 想要体验的朋友请看一下。 申请天津公共租赁住房时拒绝申报家庭情况会怎样&#xff1f; 天津市住房保障家庭在享受住房保障期间&#xff0c;如在应申…