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.rhkb.cn/news/271806.html

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

相关文章

智能指针基础知识【C++】【RAII思想 || unique_ptr || shared_ptrweak_ptr || 循环引用问题】

目录 一&#xff0c;为什么需要智能指针 二&#xff0c;内存泄露的基本认识 1. 内存泄露分类 2. 常见的内存检测工具 3&#xff0c;如何避免内存泄露 三&#xff0c;智能指针的使用与原理 1. RAII思想 2. 智能指针 &#xff08;1. unique_ptr &#xff08;2. shared_…

吴恩达deeplearning.ai:机器学习项目的完整周期伦理

以下内容有任何不理解可以翻看我之前的博客哦&#xff1a;吴恩达deeplearning.ai专栏 文章目录 语音识别部署公平、偏见、伦理 这节博客中&#xff0c;我们主要看看构建一个机器学习的完整周期是什么&#xff0c;也就是说&#xff0c;当你想构建一个有价值的机器学习系统时&am…

FPGA IBUFG

IBUFG和IBUFGDS的输入端仅仅与芯片的专用全局时钟输入管脚有物理连接&#xff0c;与普通IO和其它内部CLB等没有物理连接。 所以&#xff0c;IBUFG输入的不能直接接另外信号。 GTH transceiver primitives are called GTHE3_COMMON and GTHE3_CHANNEL in UltraScale FPGAs, an…

【PyTorch】进阶学习:探索BCEWithLogitsLoss的正确使用---二元分类问题中的logits与标签形状问题

【PyTorch】进阶学习&#xff1a;探索BCEWithLogitsLoss的正确使用—二元分类问题中的logits与标签形状问题 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、Py…

[C语言]——分支和循环(4)

目录 一.随机数生成 1.rand 2.srand 3.time 4.设置随机数的范围 猜数字游戏实现 写⼀个猜数字游戏 游戏要求&#xff1a; &#xff08;1&#xff09;电脑自动生成1~100的随机数 &#xff08;2&#xff09;玩家猜数字&#xff0c;猜数字的过程中&#xff0c;根据猜测数据的⼤…

网络协议栈--应用层--HTTP协议

目录 本节重点理解应用层的作用, 初识HTTP协议 一、应用层二、HTTP协议2.1 认识URL2.2 urlencode和urldecode2.3 HTTP协议格式2.4 HTTP的方法2.4 HTTP的状态码2.5 HTTP常见的Header属性 三、最简单的HTTP服务器3.1 HttpServer.hpp3.2 HttpServer.cc3.3 HttpClient.cc3.4 log.hp…

5G智能制造热力工厂数字孪生可视化平台,推进热力行业数字化转型

5G智能制造热力工厂数字孪生可视化平台&#xff0c;推进热力行业数字化转型。在当今这个信息化、数字化的时代&#xff0c;热力生产行业也迎来了转型的关键时刻。为了提升生产效率、降低成本、提高产品质量&#xff0c;越来越多的热力生产企业开始探索数字化转型之路。而5G智能…

备份 ChatGPT 的聊天纪录

备份 ChatGPT 的聊天纪录 ChatGPT 在前阵子发生了不少次对话纪录消失的情况&#xff0c;让许多用户觉得困扰不已&#xff0c;也担心自己想留存的聊天记录消失不见。 好消息是&#xff0c;OpenAI 在 2023 年 4 月 11 日推出了 ChatGPT 聊天记录备份功能&#xff0c;无论是免费…

Flink并行度

1、Task flink中每个算子就是一个Task&#xff0c;比如flatMap、map、sum是一个Task。 2、SubTask 算子有几个并行度SubTask的数量就是几&#xff0c;比如 3、算子并行度 算子并行度指的是每个算子的并行度&#xff0c;可用env.setParallelism(1);设置所有算子的并行度&am…

微服务架构 | 多级缓存

INDEX 通用设计概述2 优势3 最佳实践 通用设计概述 通用设计思路如下图 内容分发网络&#xff08;CDN&#xff09; 可以理解为一些服务器的副本&#xff0c;这些副本服务器可以广泛的部署在服务器提供服务的区域内&#xff0c;并存有服务器中的一些数据。 用户访问原始服务器…

内联函数|auto关键字|范围for的语法|指针空值

文章目录 一、内联函数1.1概念1.2特性 二、auto关键字2.2类型别名思考2.3auto简介2.4auto使用细则2.4 auto不能推导的场景 三、基于范围的for循环(C11)3.1 范围for的语法 四、指针空值nullptr(C11)4.1 C98中的指针空值 所属专栏:C初阶 一、内联函数 1.1概念 以inline修饰的函…

【Spring云原生系列】Spring RabbitMQ:异步处理机制的基础--消息队列 原理讲解+使用教程

&#x1f389;&#x1f389;欢迎光临&#xff0c;终于等到你啦&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;持续更新的专栏《Spring 狂野之旅&#xff1a;从入门到入魔》 &a…

JAVA虚拟机实战篇之内存调优[4](内存溢出问题案例)

文章目录 版权声明修复问题内存溢出问题分类 分页查询文章接口的内存溢出问题背景解决思路问题根源解决思路 Mybatis导致的内存溢出问题背景问题根源解决思路 导出大文件内存溢出问题背景问题根源解决思路 ThreadLocal占用大量内存问题背景问题根源解决思路 文章内容审核接口的…

2024 GoLand激活,分享几个GoLand激活的方案

文章目录 GoLand公司简介我这边使用GoLand的理由GoLand 最新变化GoLand 2023.3 最新变化AI Assistant 正式版GoLand 中的 AI Assistant&#xff1a;_Rename_&#xff08;重命名&#xff09;GoLand 中的 AI Assistant&#xff1a;_Write documentation_&#xff08;编写文档&…

【工具】Raycast – Mac提效工具

引入 以前看到同事们锁屏的时候&#xff0c;不知按了什么键&#xff0c;直接调出这个框&#xff0c;然后输入lock屏幕就锁了。 跟我习惯的按Mac开机键不大一样。个人觉得还是蛮炫酷的&#xff5e; 调研 但是由于之前比较繁忙&#xff0c;这件事其实都忘的差不多了&#xff0…

C# Winform画图绘制圆形

一、因为绘制的圆形灯需要根据不同的状态切换颜色,所以就将圆形灯创建为用户控件 二、圆形灯用户控件 1、创建用户控件UCLight 2、设值用户控件大小(30,30)。放一个label标签,AutoSize为false(不自动调整大小),Dock为Fill(填充),textaglign为居中显示。 private Color R…

ReentrantLock

文章目录 ReentrantLockReentrantLock 是什么&#xff1f;公平锁和非公平锁有什么区别&#xff1f;synchronized 和 ReentrantLock 有什么区别&#xff1f;两者都是可重入锁synchronized 依赖于 JVM 而 ReentrantLock 依赖于 APIReentrantLock 比 synchronized 增加了一些高级功…

RabbitMQ的web控制端介绍

2.1 web管理界面介绍 connections&#xff1a;无论生产者还是消费者&#xff0c;都需要与RabbitMQ建立连接后才可以完成消息的生产和消费&#xff0c;在这里可以查看连接情况channels&#xff1a;通道&#xff0c;建立连接后&#xff0c;会形成通道&#xff0c;消息的投递、获取…

Chrome安装Axure插件

打开原型目录/resources/chrome&#xff0c;重命名axure-chrome-extension.crx&#xff0c;修改后缀为rar&#xff0c;axure-chrome-extension.rar 解压到axure-chrome-extension目录打开Chrome&#xff0c;更多工具->扩展程序&#xff0c;打开开发者模式&#xff0c;选择加…

支持向量机 SVM | 线性可分:软间隔模型

目录 一. 软间隔模型1. 松弛因子的解释小节 2. SVM软间隔模型总结 线性可分SVM中&#xff0c;若想找到分类的超平面&#xff0c;数据必须是线性可分的&#xff1b;但在实际情况中&#xff0c;线性数据集存在少量的异常点&#xff0c;导致SVM无法对数据集线性划分 也就是说&…