3.若依前后端分离版开发用户自定义配置表格功能

一、背景

在项目上线测试的时候,关于同一个界面的表格,不同的用户会出现不同的字段排列需求,有些用户希望把A字段排在最前面,有些用户则希望A字段不显示。针对这种情况,开发一个表格自定义配置的功能,每个用户根据自己的需求自己去设定表单字段的显示、隐藏、字段的宽度、左右固定等。

二、效果图

1.配置界面,可以拖动字段的顺序,是否显示、宽度、左右固定
在这里插入图片描述

三、实现方式
3.1 整体实现思路

将前端表格配置好的字段方式的Json存到数据库,用户打开界面的时候去数据库读取这个配置,前端用循环动态的渲染elemnet-table的表头即可。如果这个用户没有配置过,则读取默认的配置,默认配置写在前端一个JSON里面,每个vue界面的table设置一个table key用来标识table列表,数据库层面查找的时候根据用户code和tablekey来找到这个用户关于这个表格的配置。
最后’在前端进行动态渲染表头即可。整个功能的重点在于前端动态的拿到后端的json跟默认的存在前端的Json进行比较,然后生成最终的一个配置的json(注意:要以前端的默认Json为准,因为要考虑到字段的新增和删除的情况

3.2 建立配置表 test_table_config
 CREATE TABLE `test_table_config` (
  `Id` int NOT NULL AUTO_INCREMENT,
  `UserCode` varchar(100) COLLATE utf8mb4_bin NOT NULL COMMENT '用户编码',
  `TableKey` varchar(200) COLLATE utf8mb4_bin NOT NULL COMMENT '表名key',
  `TableConifgJsonStr` varchar(8000) COLLATE utf8mb4_bin NOT NULL COMMENT '配置的字段Json',
  `IsDelete` tinyint NOT NULL COMMENT '是否删除',
  `CreateTime` datetime DEFAULT NULL COMMENT '创建时间',
  `CreateUserId` int DEFAULT NULL COMMENT '创建人id',
  `UpdateTime` datetime DEFAULT NULL COMMENT '更新时间',
  `DeleteTime` datetime DEFAULT NULL COMMENT '删除时间',
  `UpdateUserId` int DEFAULT NULL COMMENT '更新用户',
  `DeleteUserId` int DEFAULT NULL COMMENT '删除人',
  PRIMARY KEY (`Id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='table配置表';
3.3 服务端test_table_config的crud
    /**
     * 新增table配置
     */
   // @PreAuthorize("@ss.hasPermi('system:tableconfig:add')")
    public AjaxResult add( TestTableAddInput testTableAddInput)
    {

        TestTableConfig  testTableConfig = new TestTableConfig();
        testTableConfig.setUserCode(this.getUsername());
        testTableConfig.setTableKey(testTableAddInput.getTableKey());
        testTableConfig.setTableConifgJsonStr(testTableAddInput.getTableConifgJsonStr());
        testTableConfig.setCreateUserId(this.getUserId());
        testTableConfig.setCreateBy(this.getUsername());
        testTableConfig.setCreateTime(new Date());
        testTableConfig.setIsDelete(0l);
        return toAjax(testTableConfigService.insertTestTableConfig(testTableConfig));
    }


    @Log(title = "编辑table配置", businessType = BusinessType.INSERT)
    @PostMapping
    @Anonymous
    @ApiOperation("编辑table配置")
    public   AjaxResult addAndUpdate(@RequestBody TestTableAddInput testTableAddInput)
    {
        TestTableConfig  testTableConfig = new TestTableConfig();

        if(testTableAddInput.getTableConifgJsonStr() == null || testTableAddInput.getTableConifgJsonStr().length()==0)
        {
            return  error("没有传入配置Json");
        }
      // 先查询
        testTableConfig = testTableConfigService.selectTestTableConfigByUserAndTableKey(this.getUsername(),testTableAddInput.getTableKey());
        if(testTableConfig==null) // 更新
        {
            return   add(testTableAddInput);
        }
        else
        {
            testTableConfig.setTableConifgJsonStr(testTableAddInput.getTableConifgJsonStr());
            // 修改
            return   edit(testTableConfig);
        }

    }


    /**
     * 修改table配置
     */
  //  @PreAuthorize("@ss.hasPermi('system:tableconfig:edit')")
    public AjaxResult edit(TestTableConfig testTableConfig)
    {

        testTableConfig.setUpdateUserId(this.getUserId());
        testTableConfig.setUserCode(this.getUsername());
        testTableConfig.setUpdateTime(new Date());
        testTableConfig.setIsDelete(0l);
        return toAjax(testTableConfigService.updateTestTableConfig(testTableConfig));
    }
3.4 前端配置组件界面
<template>
    <div>
<div>
    <vuedraggable v-model="paramClounm">
                      <transition-group>
                          <div v-for="(item,index) in paramClounm" :key="item.key" style="margin:10px; text-align: left;">
                            <el-row :gutter="24">
                            <el-col :span="8"> <el-checkbox  :label="item.label" v-model="item.visable"></el-checkbox></el-col>
                            <el-col :span="6"> <el-input-number v-model="item.width" placeholder="宽度"></el-input-number></el-col>
                            <el-col :span="4">  <el-checkbox v-model="item.isfix" label="固定"></el-checkbox></el-col>
                            <el-col :span="6">  <el-switch :span="8" v-model="item.fixlorr" active-text="固定右侧" inactive-text="固定左侧"></el-switch></el-col>
                         
                          </el-row>
                          </div> 
                      </transition-group>
                  </vuedraggable>
  </div>
    
<div>

    <span slot="footer" class="dialog-footer">
              <el-button class="buttonLeft" type="primary" plain @click="recoverChecked">恢复默认</el-button>
              <!-- <el-button @click="dialogVisible = false" class="buttonRight">取 消</el-button> -->
              <el-button type="primary" @click="submit" class="buttonRight" >确 定</el-button>
          </span>

</div>

</div>

</template>
<script>
  import vuedraggable from 'vuedraggable';
  import { getConfig, addAndUpdate  } from "@/api/system/tableconfig";

  export default{
    name: 'configTest',
      components: {
      vuedraggable
    },
    data(){
        return{
            paramClounm:[], // 参数列
            dialogVisible:false,

        }
       
    },

    // 计算属性
    computed:{
      activeFields: function(){
        //  alert(0);
          return this.fields.filter((item)=>{
              return item.visible;
          })
      }
    },

    // 加载完
    mounted(){

       // 加载列名 ;  
       this.loadingTableClounm(Object.assign([],this.$parent.$parent.teacherTableClonms));
    },

    methods:{      

        getRowKey(v){
        return v.id
      },

      // 指令之前
      beforeHandleCommand(index,row,command) {
        //alert(0);
          return {
              'index':index,
              'row':row,
              'command':command
          }
      },


         // 移动操作
        handleCommand(command) {
          switch(command.command) {
              case "search":
                  this.search(command.index);
                  break;
              case "edit":
                  this.edit(command.index);
                  break;
              case "delete":
                  this.rowOperation(command.row.indexLine,"delete",command.row);
                  break;
              case "moveTop":
                  this.rowOperation(command.row.indexLine,"moveTop",command.row);
                  break;
              case "moveUp":
                  this.rowOperation(command.row.indexLine,"moveUp",command.row);
                  break;
              case "moveDown":
                  this.rowOperation(command.row.indexLine,"moveDown",command.row);
                  break;
              case "moveBottom":
                  this.rowOperation(command.row.indexLine,"moveBottom",command.row);
                  break;
          };
        },
       
        // 加载表格的列
        loadingTableClounm(teacherTableClonms){
            this.paramClounm= Object.assign([],teacherTableClonms) ;
        },

        // 保存配置
     submit(){

        // alert(JSON.stringify(this.paramClounm));

        var objList = this.paramClounm;
        // 移除不需要存入数据库的属性,然后将Json存到数据库
        for(var i=0;i<objList.length;i++)
        {
           var obj=objList[i];
           obj.sortNum =i; // 排序编号
           delete obj.ifRender;
           delete obj.renderFun;
        }
        
        // alert(JSON.stringify(objList));
        
        var inputData={
          tableConifgJsonStr:JSON.stringify(objList),
          tableKey: this.$parent.$parent.tableKey
        };
        

        addAndUpdate(inputData).then(res=>{
          this.$modal.msgSuccess("操作成功");
          window.location.reload();
        });
      },

      // 恢复默认
      recoverChecked(){
        //  this.checkedColumns=['编号','计划开始日期','计划完成日期','实际开始日期','实际完成日期'];
        //  this.fieldList=columnOptions;

        this.paramClounm= [];

        // 为了解决子组件改变导致父组件也跟着改变的问题
         this.paramClounm = Object.assign([],this.$parent.$parent.teacherTableDefaultClonms) ;  
        // alert(JSON.stringify(this.$parent.$parent.teacherTableDefaultClonms) );
        //window.location.reload();
      },

      edit(index,row){
  
      },
     
    },
  }


</script>
3.5 前端Table界面对应table表头的配置Json
/*********************************************
 * 功  能:teacher表列名
 * 创建人:cola
 * 创建时间:2023-08-11
 ******************************************/
import Vue from 'vue'
const vm = new Vue()
export const 
columnOptions=
[
   {
      key:"id",
      label:"Id",
      visable : true,
      width: 200,
      isfix : false,
      fixlorr: 0,
      ifRender:false,
      renderFun:''
   },
   {
    key:"name",
    label:"姓名",
    visable : true,
    width: 200,
    isfix : false,
    fixlorr: 0,
    ifRender:false,
    renderFun:''
 },
 {
    key:"teachercode",
    label:"教师工号",
    visable : true,
    width: 200,
    isfix : false,
    fixlorr: 0,
    ifRender:false,
    renderFun:''
 },
 {
    key:"fex",
    label:"性别",
    visable : true,
    width: 200,
    isfix : false,
    fixlorr: 0,
    ifRender:false,
    renderFun:''
 },
 {
    key:"age",
    label:"年龄",
    visable : true,
    width: 200,
    isfix : false,
    fixlorr: 0,
    ifRender:false,
    renderFun:''
 },
 
 {
   key:"createtime",
   label:"创建时间",
   visable : true,
   width: 200,
   isfix : false,
   fixlorr: 0,
   ifRender:false,
   renderFun:''
},
 {
    key:"isdelete",
    label:"删除",
    visable : true,
    width: 200,
    isfix : false,
    fixlorr: 0,
    ifRender:false,
    renderFun:''
 },
 {
   key:"deletetime",
   label:"删除时间",
   visable : true,
   width: 200,
   isfix : false,
   fixlorr: 0,
   ifRender:false,
   renderFun:''
},
{
   key:"updatetime",
   label:"更新时间",
   visable : true,
   width: 200,
   isfix : false,
   fixlorr: 0,
   ifRender:true,
   renderFun:(scope)=>{
     let   html="";
     html+="<span>";
     html+=  vm.parseTime(scope.row.updatetime,'{y}-{m}-{d}');
     html+= "</span>"
     return html;
   }
},
{
   key:"updateuserid",
   label:"更新人",
   visable : true,
   width: 200,
   isfix : false,
   fixlorr: 0,
   ifRender:false,
   renderFun:''
},
{
   key:"deleteuserid",
   label:"删除人",
   visable : true,
   width: 200,
   isfix : false,
   fixlorr: 0,
   ifRender:false,
   renderFun:''
},

];


3.6 前端Table界面读取配置进行动态展示
<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="name">
        <el-input
          v-model="queryParams.name"
          placeholder="请输入姓名"
          clearable
          @keyup.enter.native="handleQuery"
        />
      </el-form-item>
      <el-form-item label="教师工号" prop="teachercode">
        <el-input
          v-model="queryParams.teachercode"
          placeholder="请输入教师工号"
          clearable
          @keyup.enter.native="handleQuery"
        />
      </el-form-item>

      
      <el-form-item label="性别 0 女 1男" prop="fex">
        <el-input
          v-model="queryParams.fex"
          placeholder="请输入性别 0 女 1男"
          clearable
          @keyup.enter.native="handleQuery"
        />
      </el-form-item>
      <el-form-item label="年龄" prop="age">
        <el-input
          v-model="queryParams.age"
          placeholder="请输入年龄"
          clearable
          @keyup.enter.native="handleQuery"
        />
      </el-form-item>
      <el-form-item label="是否删除" prop="isdelete">
        <el-input
          v-model="queryParams.isdelete"
          placeholder="请输入是否删除"
          clearable
          @keyup.enter.native="handleQuery"
        />
      </el-form-item>
      <el-form-item label="创建时间" prop="createtime">
        <el-date-picker clearable
          v-model="queryParams.createtime"
          type="date"
          value-format="yyyy-MM-dd"
          placeholder="请选择创建时间">
        </el-date-picker>
      </el-form-item>
      <el-form-item label="更新时间" prop="updatetime">
        <el-input
          v-model="queryParams.updatetime"
          placeholder="请输入更新时间"
          clearable
          @keyup.enter.native="handleQuery"
        />
      </el-form-item>
      <el-form-item label="删除时间" prop="deletetime">
        <el-date-picker clearable
          v-model="queryParams.deletetime"
          type="date"
          value-format="yyyy-MM-dd"
          placeholder="请选择删除时间">
        </el-date-picker>
      </el-form-item>
      <el-form-item label="创建人id" prop="createuserid">
        <el-input
          v-model="queryParams.createuserid"
          placeholder="请输入创建人id"
          clearable
          @keyup.enter.native="handleQuery"
        />
      </el-form-item>
      <el-form-item label="修改人id" prop="updateuserid">
        <el-input
          v-model="queryParams.updateuserid"
          placeholder="请输入修改人id"
          clearable
          @keyup.enter.native="handleQuery"
        />
      </el-form-item>
      <el-form-item label="删除人id" prop="deleteuserid">
        <el-input
          v-model="queryParams.deleteuserid"
          placeholder="请输入删除人id"
          clearable
          @keyup.enter.native="handleQuery"
        />
      </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-button
          type="primary"
          plain
          icon="el-icon-plus"
          size="mini"
          @click="handleAdd"

        >新增</el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button
          type="success"
          plain
          icon="el-icon-edit"
          size="mini"
          :disabled="single"
          @click="handleUpdate"

        >修改</el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button
          type="danger"
          plain
          icon="el-icon-delete"
          size="mini"
          :disabled="multiple"
          @click="handleDelete"
        
        >删除</el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button
          type="warning"
          plain
          icon="el-icon-download"
          size="mini"
          @click="handleExport"

        >导出</el-button>
      </el-col>

      
      <el-col :span="1.5">
        <el-button
          type="warning"
          plain
          icon="el-icon-download"
          size="mini"
          @click="openconifg"

        >配置</el-button>
      </el-col>

      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
    </el-row>
    <!-- :data="teacherList" v-loading="loading"  :fixed="item.fixlorr==0? 'left':'right'"-->
    <el-table :data="teacherList" v-loading="loading"   @selection-change="handleSelectionChange"  style="width: 400">
      <!-- <el-table-column type="selection" width="55" align="center"  :fixed="true" /> 
     
<template slot-name=''></template>

      </el-table-column> -->
      
      <el-table-column   v-for="item in this.teacherTableClonms " :key="item.key" :label="item.label"  :prop="item.key" :width="item.width" 
       v-if="item.ifRender && item.visable" :fixed="item.isfix?item.fixlorr==0?left:right :false">
    
        <template slot-scope="scope" >
          <div  v-html="item.renderFun? item.renderFun(scope) : ''"></div>
        </template>
      </el-table-column>

      <el-table-column :label="item.label" align="center" :prop="item.key" :width="item.width"  
       v-else-if="item.ifRender==false && item.visable" :fixed="item.isfix?item.fixlorr==0?'left':'right' :false" />


      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" >
        <template slot-scope="scope">
          <el-button
            size="mini"
            type="text"
            icon="el-icon-edit"
            @click="handleUpdate(scope.row)"
          
          >修改</el-button>
          <el-button
            size="mini"
            type="text"
            icon="el-icon-delete"
            @click="handleDelete(scope.row)"
      
          >删除</el-button>
        </template>
      </el-table-column>
      <!-- <el-table-column label="id" align="center" prop="id" />
      <el-table-column label="姓名" align="center" prop="name" />
      <el-table-column label="教师工号" align="center" prop="teachercode" />
      <el-table-column label="性别 0 女 1男" align="center" prop="fex" />
      <el-table-column label="年龄" align="center" prop="age" />
      <el-table-column label="是否删除" align="center" prop="isdelete" />
      <el-table-column label="创建时间" align="center" prop="createtime" width="180">
        <template slot-scope="scope">
          <span>{{ parseTime(scope.row.createtime, '{y}-{m}-{d}') }}</span>
        </template>
      </el-table-column>
      <el-table-column label="更新时间" align="center" prop="updatetime" />
      <el-table-column label="删除时间" align="center" prop="deletetime" width="180">
        <template slot-scope="scope">
          <span>{{ parseTime(scope.row.deletetime, '{y}-{m}-{d}') }}</span>
        </template>
      </el-table-column>
      <el-table-column  label="创建人id" align="center" prop="createuserid" />
      <el-table-column label="修改人id" align="center" prop="updateuserid" />
      <el-table-column label="删除人id" align="center" prop="deleteuserid" />
  -->

    </el-table>
    
    <pagination
      v-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="name">
          <el-input v-model="form.name" placeholder="请输入姓名" />
        </el-form-item>
        <el-form-item label="教师工号" prop="teachercode">
          <el-input v-model="form.teachercode" placeholder="请输入教师工号" />
        </el-form-item>
        <el-form-item label="性别 0 女 1男" prop="fex">
          <el-input v-model="form.fex" placeholder="请输入性别 0 女 1男" />
        </el-form-item>
        <el-form-item label="年龄" prop="age">
          <el-input v-model="form.age" placeholder="请输入年龄" />
        </el-form-item>
        <el-form-item label="是否删除" prop="isdelete">
          <el-input v-model="form.isdelete" placeholder="请输入是否删除" />
        </el-form-item>
        <el-form-item label="删除时间" prop="deletetime">
          <el-date-picker clearable
            v-model="form.deletetime"
            type="date"
            value-format="yyyy-MM-dd"
            placeholder="请选择删除时间">
          </el-date-picker>
        </el-form-item>
        <el-form-item label="创建人id" prop="createuserid">
          <el-input v-model="form.createuserid" placeholder="请输入创建人id" />
        </el-form-item>
        <el-form-item label="修改人id" prop="updateuserid">
          <el-input v-model="form.updateuserid" placeholder="请输入修改人id" />
        </el-form-item>
        <el-form-item label="删除人id" prop="deleteuserid">
          <el-input v-model="form.deleteuserid" placeholder="请输入删除人id" />
        </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>

      <!-- 传入 参数-->
  
    <el-dialog  title="配置表格列" :visible.sync="cofigshow" width="900px" append-to-body>
    
    <ConfigTableTest :tableclounms="this.teacherTableClonms"></ConfigTableTest>
  </el-dialog> 


  </div>
</template>

<script>
import { listTeacher, getTeacher, delTeacher, addTeacher, updateTeacher } from "@/api/system/teacher";
import { getTableConfigByUserCodeAndTabelKey } from "@/api/system/tableconfig";
import  { columnOptions }  from "../../../tableclounm/teacherclounm";
   import ConfigTableTest from "../tableconfig/configtabletest.vue";

   import TableCloumRender from "../tableconfig/tableclounmrender.vue";
export default {
  name: "Teacher",
  components: {
   ConfigTableTest,

   TableCloumRender
},
  data() {
    return {
      // 遮罩层
      loading: true,
      // 选中数组
      ids: [],
      // 非单个禁用
      single: true,
      // 非多个禁用
      multiple: true,
      // 显示搜索条件
      showSearch: true,
      // 总条数
      total: 0,
      // 测试_教师表格数据
      teacherList: [],
      // 弹出层标题
      title: "",
      // 是否显示弹出层
      open: false,

      // 配置显示层
      cofigshow: false,

      // 表格的Key,用来自定义配置的标识 to do 命名规范定义 功能模_块功能界面_Table ?
      tableKey:'TeacherIndexTable',

      // 查询参数
      queryParams: {
        pageNum: 1,
        pageSize: 10,
        name: null,
        teachercode: null,
        fex: null,
        age: null,
        isdelete: null,
        createtime: null,
        updatetime: null,
        deletetime: null,
        createuserid: null,
        updateuserid: null,
        deleteuserid: null
      },
      // 表单参数
      form: {},
      // 表单校验
      rules: {
      },
      teacherTableClonms : [], // 获取后端返回的当前用户个性化配置的列名
      teacherTableDefaultClonms:[] //  获取js里面配置的列名
    };
  },
  created() {
    this.getTableConfig(); // 获取用户列表配置
   // alert(JSON.stringify(this.teacherTableClonms) );
    this.getList();
    
  },
  methods: {
    /** 查询测试_教师列表 */
    getList() {
      this.loading = true;
      listTeacher(this.queryParams).then(response => {
        this.teacherList = response.rows;
        this.total = response.total;
        this.loading = false;
      });
    },

    // 获取当前用户这个列表对应的配置:
    // 逻辑:
    // 1.先查询配置。
    // 2.如果这个用户还没配置 则取JS里面的默认配置。
    // 3.如果这个用户已经配置了,则取数据库里面保存的配置,然后循环比对存在js里面的字段,
    // 生成最后对应的配置(因为数据库里面没有存渲染的代码,考虑到字段的长度限制 8000 风险点 1 如果大宽表,需要修改数据库字段类型)。

    getTableConfig(){
 
      // 默认的配置 Object.assign防止  this.teacherTableDefaultClonms的变动影响columnOptions
      this.teacherTableDefaultClonms =  JSON.parse(JSON.stringify(columnOptions))  ; 
     // alert(JSON.stringify(this.teacherTableDefaultClonms ));
      var param={
        tableKey:this.tableKey  // 表格的key 每个表格唯一
      };

      // 默认的配置 Object.assign防止  jsConfigClounms的变动影响columnOptions
       let  jsConfigClounms =   Object.assign([],columnOptions); 
 
       let  newClounms =[]; // 拼接成新的一个配置数组
      getTableConfigByUserCodeAndTabelKey(param).then(res=>{
            
           if(!!res){

            var data =JSON.parse(res.tableConifgJsonStr) ;
             // 以配置在JS里面的配置为循环的基础(需要考虑到增减字段)
             for(var  i=0;i<jsConfigClounms.length;i++)
             {
                var jsClounmItem = jsConfigClounms[i];
                var newItem = jsClounmItem; // 新的项以JS的配置为基础,能设置的配置项取数据库里面的
                // 查询对应的配置
                // r
                for(var j=0;j<data.length;j++)
                {
                    var dataItem = data[j];
                    if(newItem.key == dataItem.key)
                    {
                       // 说明匹配上了
                     if(dataItem.visable!=null)
                     {
                      newItem.visable = dataItem.visable; // 是否显示
                     }
                     if(dataItem.width!=null)
                     {
                      newItem.width = dataItem.width;   // 宽度
                     }
                     if(dataItem.isfix!=null)
                     {
                      newItem.isfix = dataItem.isfix;     // 是否固定
                     }

                     if(dataItem.fixlorr!=null)
                     {
                      newItem.fixlorr = dataItem.fixlorr; // 固定左边or右边
                     }

                     if(dataItem.sortNum!=null)
                     {
                       newItem.sortNum = dataItem.sortNum ; //排序编号
                     }
                    
                      break; // 匹配上了,跳出一层循环
                    } 
                    else
                    {
                       // 没有匹配上,则说明是新加的字段,把这些新加的字段放在最后
                        newItem.sortNum = 10000-i; // 把排序号增加到最大
                    } 
                } // for 1 end

                newClounms.push(newItem);
             }// for2 end

             // 这个时候需要排序一下,恢复顺序
              newClounms = newClounms.sort(this.sortByNum);
           }
           else{
             // 说明数据库里面没有配置,则直接取JS里面的配置
             newClounms = jsConfigClounms;
           }
           this.teacherTableClonms = newClounms;


          // alert(JSON.stringify(this.teacherTableDefaultClonms ));
      });

    },

// 根据排序编码号进行排序
sortByNum(a, b) {
  return a.sortNum - b.sortNum;
},
    // 取消按钮
    cancel() {
      this.open = false;
      this.reset();
    },
    // 表单重置
    reset() {
      this.form = {
        id: null,
        name: null,
        teachercode: null,
        fex: null,
        age: null,
        isdelete: null,
        createtime: null,
        updatetime: null,
        deletetime: null,
        createuserid: null,
        updateuserid: null,
        deleteuserid: null
      };
      this.resetForm("form");
    },
    /** 搜索按钮操作 */
    handleQuery() {
      this.queryParams.pageNum = 1;
      this.getList();
    },
    /** 重置按钮操作 */
    resetQuery() {
      this.resetForm("queryForm");
      this.handleQuery();
    },
    // 多选框选中数据
    handleSelectionChange(selection) {
      this.ids = selection.map(item => item.id)
      this.single = selection.length!==1
      this.multiple = !selection.length
    },
    /** 新增按钮操作 */
    handleAdd() {
      this.reset();
      this.open = true;
      this.title = "添加测试_教师";
    },
    /** 修改按钮操作 */
    handleUpdate(row) {
      this.reset();
      const id = row.id || this.ids
      getTeacher(id).then(response => {
        this.form = response.data;
        this.open = true;
        this.title = "修改测试_教师";
      });
    },
    /** 提交按钮 */
    submitForm() {
      this.$refs["form"].validate(valid => {
        if (valid) {
          if (this.form.id != null) {
            updateTeacher(this.form).then(response => {
              this.$modal.msgSuccess("修改成功");
              this.open = false;
              this.getList();
             //
            });
          } else {
            addTeacher(this.form).then(response => {
              this.$modal.msgSuccess("新增成功");
              this.open = false;
              this.getList();
            });
          }
        }
      });
    },
    /** 删除按钮操作 */
    handleDelete(row) {
      const ids = row.id || this.ids;
      this.$modal.confirm('是否确认删除测试_教师编号为"' + ids + '"的数据项?').then(function() {
        return delTeacher(ids);
      }).then(() => {
        this.getList();
        this.$modal.msgSuccess("删除成功");
      }).catch(() => {});
    },
    /** 导出按钮操作 */
    handleExport() {
      this.download('system/teacher/export', {
        ...this.queryParams
      }, `teacher_${new Date().getTime()}.xlsx`)
    },

    /**打开配置 */
    openconifg(){
      this.cofigshow = true;
    }
  }
};
</script>

3.7 功能完成

在这里插入图片描述

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 由于涉及到HDFS的操作,需要使用Java编写后端代码,以下是一个简单的示例: 前端代码: ```html <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>HDFS文件管理</title> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script> </head> <body> <h1>HDFS文件管理</h1> <table> <thead> <tr> <th>文件名</th> <th>大小</th> <th>修改时间</th> <th>操作</th> </tr> </thead> <tbody id="fileList"> </tbody> </table> <div> <button onclick="listFiles()">返回上一级</button> <button onclick="uploadFile()">上传文件</button> </div> <div> <input type="text" id="fileName"> <button onclick="createFile()">新建文件</button> </div> <script> var currentPath = "/"; function listFiles() { if (currentPath != "/") { currentPath = currentPath.substring(0, currentPath.lastIndexOf("/")); } getFileList(currentPath); } function enterFolder(path) { currentPath = path; getFileList(currentPath); } function getFileList(path) { $.ajax({ url: "/file/list", data: {"path": path}, dataType: "json", success: function(data) { var fileList = data.data; var html = ""; for (var i = 0; i < fileList.length; i++) { var file = fileList[i]; var icon = "<span></span>"; if (file.type == "DIRECTORY") { icon = "<span></span>"; } var fileSize = "-"; if (file.type != "DIRECTORY") { fileSize = formatSize(file.size); } var modifyTime = "-"; if (file.modifyTime != null) { modifyTime = new Date(file.modifyTime).toLocaleString(); } var operate = ""; if (file.type == "DIRECTORY") { operate = "<button onclick='enterFolder(\"" + file.path + "\")'>进入</button>"; } else { operate = "<button onclick='downloadFile(\"" + file.path + "\")'>下载</button>" + "<button onclick='deleteFile(\"" + file.path + "\")'>删除</button>"; } html += "<tr><td>" + icon + file.name + "</td><td>" + fileSize + "</td><td>" + modifyTime + "</td><td>" + operate + "</td></tr>"; } $("#fileList").html(html); } }); } function uploadFile() { var formData = new FormData(); formData.append("path", currentPath); formData.append("file", $("#fileInput")[0].files[0]); $.ajax({ url: "/file/upload", type: "POST", data: formData, processData: false, contentType: false, success: function() { getFileList(currentPath); alert("上传成功!"); } }); } function createFile() { var fileName = $("#fileName").val(); if (fileName == "") { alert("请输入文件名!"); return; } $.ajax({ url: "/file/create", data: {"path": currentPath + "/" + fileName}, dataType: "json", success: function(data) { if (data.code == 0) { getFileList(currentPath); alert("创建成功!"); } else { alert(data.msg); } } }); } function deleteFile(path) { if (confirm("确定要删除该文件吗?")) { $.ajax({ url: "/file/delete", data: {"path": path}, dataType: "json", success: function(data) { if (data.code == 0) { getFileList(currentPath); alert("删除成功!"); } else { alert(data.msg); } } }); } } function downloadFile(path) { window.location.href = "/file/download?path=" + path; } function formatSize(size) { if (size < 1024) { return size + "B"; } else if (size < 1024 * 1024) { return (size / 1024).toFixed(2) + "KB"; } else if (size < 1024 * 1024 * 1024) { return (size / 1024 / 1024).toFixed(2) + "MB"; } else { return (size / 1024 / 1024 / 1024).toFixed(2) + "GB"; } } </script> <input type="file" id="fileInput" style="display:none;"> </body> </html> ``` 后端代码: ```java @Controller @RequestMapping("/file") public class HdfsFileController { @Autowired private FileSystem fileSystem; @RequestMapping("/list") @ResponseBody public Result listFiles(String path) { try { List<HdfsFile> fileList = new ArrayList<>(); FileStatus[] fileStatuses = fileSystem.listStatus(new Path(path)); for (FileStatus fileStatus : fileStatuses) { HdfsFile file = new HdfsFile(); file.setName(fileStatus.getPath().getName()); file.setSize(fileStatus.isFile() ? fileStatus.getLen() : -1); file.setModifyTime(fileStatus.getModificationTime()); file.setType(fileStatus.isDirectory() ? "DIRECTORY" : "FILE"); file.setPath(fileStatus.getPath().toUri().getPath()); fileList.add(file); } return Result.success(fileList); } catch (Exception e) { e.printStackTrace(); return Result.error("获取文件列表失败!"); } } @PostMapping("/upload") @ResponseBody public Result uploadFile(String path, MultipartFile file) { try { Path hdfsPath = new Path(path + "/" + file.getOriginalFilename()); FSDataOutputStream outputStream = fileSystem.create(hdfsPath); IOUtils.copy(file.getInputStream(), outputStream); outputStream.close(); return Result.success(); } catch (Exception e) { e.printStackTrace(); return Result.error("上传文件失败!"); } } @RequestMapping("/create") @ResponseBody public Result createFile(String path) { try { Path hdfsPath = new Path(path); if (!fileSystem.exists(hdfsPath)) { fileSystem.create(hdfsPath).close(); } return Result.success(); } catch (Exception e) { e.printStackTrace(); return Result.error("创建文件失败!"); } } @RequestMapping("/delete") @ResponseBody public Result deleteFile(String path) { try { Path hdfsPath = new Path(path); fileSystem.delete(hdfsPath, true); return Result.success(); } catch (Exception e) { e.printStackTrace(); return Result.error("删除文件失败!"); } } @RequestMapping("/download") public void downloadFile(String path, HttpServletResponse response) { try { Path hdfsPath = new Path(path); FSDataInputStream inputStream = fileSystem.open(hdfsPath); response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(hdfsPath.getName(), "UTF-8")); IOUtils.copy(inputStream, response.getOutputStream()); inputStream.close(); } catch (Exception e) { e.printStackTrace(); } } } ``` 其中`HdfsFile`是一个自定义的文件类,包含文件名、大小、修改时间、类型和路径等信息。`Result`是一个通用的返回结果类,用于封装返回的数据和错误信息。这里使用了Spring MVC框架,`@Controller`注解表示这是一个控制器,`@RequestMapping`注解表示请求路径,`@ResponseBody`注解表示返回结果直接写入响应体中。 以上代码仅供参考,具体实现需要根据实际情况进行修改。同时需要注意Hadoop和Spring本的兼容性问题。 ### 回答2: SSM框架(Spring+SpringMVC+MyBatis)是一个Java Web开发框架,用于实现前后端分离的项目。在使用SSM框架分页展示HDFS文件列表并实现返回上一级、进入下一级、上传文件、删除文件和下载文件功能时,通常需要以下的前后端代码。 后端代码(Java): 1. 创建一个HDFSUtil工具类用于连接HDFS: ```java Configuration configuration = new Configuration(); configuration.set("fs.defaultFS", "hdfs://localhost:9000"); FileSystem fileSystem = FileSystem.get(configuration); ``` 2. 获取指定路径下的文件列表并进行分页展示: ```java public List<String> getFileList(String path, int page, int pageSize) throws IOException { // 根据path获取FileStatus数组 FileStatus[] fileStatusArray = fileSystem.listStatus(new Path(path)); // 根据page和pageSize计算起始索引和结束索引 int startIndex = (page - 1) * pageSize; int endIndex = Math.min(startIndex + pageSize, fileStatusArray.length); // 将文件路径存入列表中 List<String> fileList = new ArrayList<>(); for (int i = startIndex; i < endIndex; i++) { fileList.add(fileStatusArray[i].getPath().toString()); } return fileList; } ``` 3. 进入下一级和返回上一级功能: ```java public String getParentPath(String currentPath) { // 获取当前路径的父路径 Path parentPath = new Path(currentPath).getParent(); if (parentPath != null) { return parentPath.toString(); } else { return "/"; } } ``` 4. 上传文件: ```java public void uploadFile(MultipartFile file, String targetPath) throws IOException { // 根据targetPath创建输出流 FSDataOutputStream outputStream = fileSystem.create(new Path(targetPath)); // 从MultipartFile对象中读取数据并写入输出流 outputStream.write(file.getBytes()); outputStream.close(); } ``` 5. 删除文件: ```java public boolean deleteFile(String filePath) throws IOException { // 根据filePath创建路径对象 Path path = new Path(filePath); // 删除文件或目录 return fileSystem.delete(path, true); } ``` 前端代码(HTML、JavaScript): 1. 分页展示文件列表: ```html <ul id="fileList"> <!-- 文件列表内容将通过JavaScript动态生成 --> </ul> <div id="pagination"> <!-- 分页按钮将通过JavaScript动态生成 --> </div> ``` 2. 返回上一级和进入下一级按钮事件处理: ```javascript // 返回上一级按钮点击事件 $("#btnBack").click(function() { // 发送Ajax请求,获取上一级路径的文件列表,并重新渲染文件列表和分页按钮 }); // 进入下一级按钮点击事件 $("#btnNext").click(function() { // 发送Ajax请求,获取下一级路径的文件列表,并重新渲染文件列表和分页按钮 }); ``` 3. 上传文件和删除文件按钮事件处理(借助jQuery和FormData): ```javascript // 上传文件按钮点击事件 $("#btnUpload").click(function() { let file = $("#fileInput")[0].files[0]; let targetPath = $("#currentPath").val(); let formData = new FormData(); formData.append("file", file); formData.append("targetPath", targetPath); $.ajax({ url: "/uploadFile", type: "POST", data: formData, processData: false, contentType: false, success: function (data) { // 文件上传成功后的处理逻辑 } }); }); // 删除文件按钮点击事件 $(".btnDelete").click(function() { let filePath = $(this).attr("data-file"); $.ajax({ url: "/deleteFile", type: "POST", data: { filePath: filePath }, success: function (data) { // 文件删除成功后的处理逻辑 } }); }); ``` 4. 文件下载链接: ```html <a href="/downloadFile?filePath=文件路径">下载</a> ``` 以上是简要的SSM框架分页展示HDFS文件列表并实现返回上一级、进入下一级、上传文件、删除文件和下载文件的前后端代码示例。具体的实现逻辑还需根据项目的具体需求进行调整。 ### 回答3: SSM框架是指Spring + SpringMVC + MyBatis的组合,用于搭建JavaWeb应用的开发框架。下面是一个简单的示例,演示如何使用SSM框架来实现HDFS文件列表的分页展示、返回上一级、进入下一级、上传文件、删除文件和下载文件的功能。 1. 前端代码(HTML、CSS、JavaScript): 在前端页面中,可以使用表格来展示HDFS文件列表,同时添加一些按钮和输入框来实现分页、返回上一级、进入下一级、上传文件、删除文件和下载文件等操作。 示例中使用的是JQuery来发送AJAX请求,获取后端接口返回的数据,并进行渲染展示。 2. 后端代码(Java): a. 控制器层(Controller): 在控制器层中,可以使用SpringMVC的注解来处理前端请求,并调用业务逻辑层相应的方法。 示例中,可以定义一个文件管理的控制器,包含处理分页展示、返回上一级、进入下一级、上传文件、删除文件和下载文件的方法。 b. 业务逻辑层(Service): 在业务逻辑层中,可以使用Spring的注解来管理HDFS文件的增删改查操作。 示例中,可以定义一个文件管理的服务类,包含获取HDFS文件列表、返回上一级、进入下一级、上传文件、删除文件和下载文件的方法。 c. 数据访问层(DAO): 在数据访问层中,可以使用MyBatis的注解或者XML配置来操作数据库,并处理HDFS文件的读写操作。 示例中,可以定义一个文件管理的数据访问类,包含获取HDFS文件列表、返回上一级、进入下一级、上传文件、删除文件和下载文件的方法。 3. 配置文件: 在配置文件中,可以配置Spring、SpringMVC和MyBatis的相关配置信息。 示例中,可以配置数据库的连接信息和HDFS的连接信息等。 以上是一个简化的示例,演示了如何使用SSM框架来实现HDFS文件列表的分页展示、返回上一级、进入下一级、上传文件、删除文件和下载文件的功能。具体的实现要根据具体的项目需求进行调整。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

代码写到35岁

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

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

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

打赏作者

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

抵扣说明:

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

余额充值