👨?💻作者簡介:在笑大學牲
🎟?個人主頁:無所謂^_^
?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上下載下來運行。項目的功能我測試優化了很多次,如果代碼有何異常或者不完整歡迎在評論區留言。如果這篇文章有幸幫助到你,希望讀者大大們可以給作者點個贊呀😶?🌫?😶?🌫?😶?🌫?