基于react ant design pro typescript 技术框架已经重磅推出

预览地址

系统介绍

前言

本框架是基于十多年项目开发经验积累,用最佳实践和流行技术开发现代前后端分离的通用项目模板。适合web应用,微信、手机应用管理端及服务端。

预览地址:http://47.94.229.181:81/jrtechapp/
服务器CPU1核内存1G带宽1M,访问比较慢,望谅解
系统更多截图:https://blog.csdn.net/qq_20986029/article/details/82184652

后端多模块

 后端多模块

后端映射请求自动生成前端JS方法调用

减少后端和前端的调用阻抗

后端处理映射

@RestController
@RequestMapping("/authapi/base_log")
@Display("操作日志")
public class Base_LogController {
    @PostMapping("/get")
    @Log(disabled = true)
    @PreAuthorize("hasAuthority('menu_base_log')")
    public Base_Log get(String id) {
        Base_Log result =  service.get(id);

        return result;
    }
}

自动注册为前端JS函数,前端调用方式如下:

  tapp.services.base_Log.get(id).then(function(result) {
          self.model = result;
  });

数据字典组件

后端数据字典自动生成为前端JS对象,前端提供数据字典组件,仅需要指定数据字典类别就能完成选择功能。

效果:
性别选择组件

前端代码:

  <el-col :span="8">
     <!-- 性别选择组件-->
      <el-form-item label="性别" prop="sexId" verify  >
        <t-dic-select dicType="public_sex" v-model="headerEntity.sexId"></t-dic-select>
      </el-form-item>
    </el-col>

统一的异常处理

前端几乎不需要自己写异常处理代码

后端服务方法抛出UserFriendlyException(用户异常友好化)异常:

@Service
public class Base_UserServiceImpl extends BaseServiceImpl implements Base_UserService {
  @Override
	@Transactional
	public void delete(String id) {

		Base_User entity = userRepository.selectById(id);
		if (entity == null) {
			return;
		}
		if (!entity.canDelete()) {
			throw new UserFriendlyException(MessageFormat.format("用户{0}禁止删除,无法删除!", entity.getName()));
		}
 ...
	} 
}		

前端代码:

<!--无须异常处理-->
tapp.services.base_User.batchDelete(ids).then(function(result) {
          self.$notify.success({
            title: '系统删除成功',
            message: '用户信息已删除成功!'
          });
          self.$refs.searchReulstList.refresh();
        })

前端会直接的显示给用户
用户禁止删除
其它系统错,系统会在后端记录,前端提示用户重试或联系系统管理员
系统错误
后端日志记录
在这里插入图片描述

后端定义菜单自动注册到Vue-router

后端定义的菜单自动会显示在系统导航,无须在vue-router中注册,无须前端重复处理

在这里插入图片描述

功能强大的grid组件

前端提供功能强大的grid组件,仅需要调用 后端映射请求自动生成的JS方法,即能完成获取数据,分页,排序,导出等功能

左边树,右边列表

效果
在这里插入图片描述
代码

<template>
<div>
  <el-row :gutter="20">
    <el-col :span="8">
      <el-card class="box-card">
        <div slot="header" class="clearfix">
          <span>表名</span>
        </div>
        <div class="text item">
          <!-- 定义树组件 -->
          <t-tree ref="categoryTree" :options="categoryTreeOptons" @node-click="handleNodeClick">
          </t-tree>
        </div>
      </el-card>
    </el-col>
    <el-col :span="16">
      <el-card class="box-card">
        <div slot="header" class="clearfix">
          <span>{{selectedCategoryItemName}}-列</span>
          <div style="float: right; padding: 3px 0">
            <el-button icon="el-icon-download"  @click="doExportExcel()">导出</el-button>
          </div>
        </div>
        <div class="text item">
            <!-- 定义列表组件 -->
          <t-grid ref="searchReulstList" :options="gridOptions">
          </t-grid>
        </div>
      </el-card>
    </el-col>
  </el-row>
</div>
</template>
<script>
export default {
  data() {
    return {
      selectedCategoryItem: null,
      data: [],
      categoryTreeOptons: { //定义树组件选项
        tree: {
          data: [], //数据源
          showCheckbox: false,, //不允许多选
          defaultCheckedKeys: [] //默认选中ids
        }
      },
      gridOptions: { //定列表组件选项
        dataSource: [], //数据源
        grid: {
          pageable: false, //不分页
          columns: [{ //定义列
              prop: 'columnName',
              label: '列名',
              sortable: true,
              width: 200
            },
            {
              prop: 'columnType',
              label: '数据类型',
              sortable: true,
              width: 120
            },
            {
              prop: 'isNullable',
              label: '允许非空',
              sortable: false,
              width: 60
            },
            {
              prop: 'columnKey',
              label: '主键约束',
              sortable: false,
              width: 60
            },
            {
              prop: 'columnComment',
              label: '备注',
              sortable: false,
            },
          ], // 需要展示的列
          defaultSort: {
            prop: 'ordinalPosition',
            order: 'ascending'
          },
        }
      }
    }
  },
  components: {
  },
  created() {
    let self = this;
    //加载数据
    tapp.services.base_DBDictionary.queryDBDictionary().then(function(result) {
      self.gridOptions.dataSource = result; //指定列表组件数据源
      let categoryTreeData = result.map(p => {
        return {
          id: p.tableName,
          name: p.tableComment + '(' + p.tableName + ')',
          parentId: null,
          level: 1,
          items: [],
        }
      });
      //设置树组件数据源
      self.categoryTreeOptons.tree.data = categoryTreeData;
      self.$nextTick(() => {
        self.$refs.categoryTree.refresh();
      });
    });
  },
  methods: {
    handleNodeClick(dataItem, node, el) {
      this.selectedCategoryItem = dataItem;
      let selectedCategoryData = this.data.find(p => p.tableName === dataItem.id);
      let gridData = selectedCategoryData.columns;
      this.gridOptions.dataSource = gridData; 
    },
    doSearch() {
      this.$refs.searchReulstList.refresh();
    },
    //列表数据导出
    doExportExcel() {
      this.$refs.searchReulstList.exportCSV(this.selectedCategoryItemName + '-列');
    },
  }
}
</script>
<style > 
</style>


分页查询,删除,导出

效果
在这里插入图片描述
代码

<template>
<div class="mod-role">
   <!-- 定义查询条件 -->
  <el-form :inline="true" @keyup.enter.native="doSearch()">
    <el-form-item>
      <el-input  prefix-icon="el-icon-search" v-model="gridOptions.dataSource.serviceInstanceInputParameters.searchKey" placeholder="登陆名或者姓名" clearable></el-input>
    </el-form-item>
    <el-form-item>
      <el-button @click="doSearch()" icon="el-icon-search">查询</el-button>
      <el-button  icon="el-icon-plus" type="primary" @click="doNew()">新增</el-button>
      <el-button  icon="el-icon-delete" type="danger" @click="doBatchDelete()" :disabled="selectedRows.length <= 0">批量删除</el-button>
      <el-button   icon="el-icon-download" @click="doExportExcel()">导出</el-button>
      <el-button   icon="el-icon-upload2" @click="doExportExcel()" v-if="false">导入</el-button>
    </el-form-item>
  </el-form>
   <!-- 定义列表组件 -->
  <t-grid ref="searchReulstList" :options="gridOptions" @selection-change="handleSelectionChange">
  </t-grid>
</div>
</template>

<script>
export default {
  data() {
    return {
      selectedRows: [],
      gridOptions: {//定列表组件选项
        dataSource: { //数据源
          //定义要调用的后端映射请求自动生成的JS方法
          serviceInstance: tapp.services.base_User.getAllUsers,
          //定义要调用的后端映射请求自动生成的JS方法参数
          serviceInstanceInputParameters: {
            searchKey: null,
          }
        },
        grid: {
          operates: { //定义列表组件的操作按钮
            width: 120,
            fixed: 'left',
            list: [{
                type: 'text',
                show: true,
                label: '查看',
                method: this.doEdit,
              },
              {
                type: 'text',
                show: true,
                label: '修改密码',
                method: this.doAdminChangePassword,
              },
            ]
          }, // 定义列表组件的列
          columns: [{
              prop: 'loginId',
              label: '登陆名',
              sortable: true,
              width: 120
            }, 
           ...
            },
            {
              prop: 'departmentNames',
              label: '所属营业部',
              sortable: true,
            },

          ], // 需要展示的列
          defaultSort: {
            prop: 'id',
            order: 'ascending'
          },
        }
      }
    }
  },
  components: {
  },
  created() {

  },
  methods: {
    handleSelectionChange(val) {
      this.selectedRows = val;
    },
    doExportExcel() {
      this.$refs.searchReulstList.exportCSV('用户列表');
    },
    doSearch() {
      this.$refs.searchReulstList.refresh();
    }
  }
}
</script>

合计

效果
在这里插入图片描述
代码

 <template>
<div>
  <el-form :inline="true">
    <el-form-item>
      <el-button icon="el-icon-download" @click="doExportExcel()">
        <i class="fa fa-lg fa-level-down"></i>导出
      </el-button>
    </el-form-item>
  </el-form>
  <t-grid ref="repaymentScheduleReulstList" :options="repaymentScheduleGridOptions">
  </t-grid>
</div>
</template>
<script>
import util from '@/util'
export default {
  components: {},
  props: {
    repaymentScheduleList: null,
  },
  data() {
    return {
      repaymentScheduleGridOptions: {
        dataSource: [],
        grid: {
          mutiSelect: false,
          pageable: false,
          reduceMethod: this.getRepaymentScheduleSummaries,
          columns: [{
              prop: 'planSettleDate',
              label: '结算日期',
              sortable: true,
              width: 120,
              formatter: (row, column, cellValue) => {
                return this.$util.dateFormat(row.planSettleDate);
              }
            },
         ...
          ], // 需要展示的列
          defaultSort: {
            prop: 'id',
            order: 'ascending'
          },
        }
      }
    }
  },
  watch: {
    repaymentScheduleList: {    
      handler(newValue, oldValue) {
        this.repaymentScheduleGridOptions.dataSource = newValue; 
      },
      deep: true  
    }
  },
  created() {},
  mounted() {},
  computed: {},
  mounted() {},
  methods: {
    doExportExcel() {
      this.$refs.repaymentScheduleReulstList.exportCSV('还款计划表');
    },
    getRepaymentScheduleSummaries(param) {
      const {
        columns,
        data
      } = param;

      if (data == null || data.length == 0) {
        return [];
      }
      const sums = [];
      sums[0] = '合计';
      let repaymentScheduleList = data;
      let sumPlanCapitalAmount = repaymentScheduleList.map(function(item) {
        return item.planCapitalAmount;
      }).reduce(function(a, b, index, arr) {
        return Number((a || 0)) + Number((b || 0));
      });
      ....

      return sums;
    },

  }
}
</script>

多列头及合计

效果
在这里插入图片描述
代码

 <template>
<t-grid ref="loanRecoveryDocImplList" :options="loanRecoveryDocImplListGridOptions">
  <template slot="columnDataHeader">
<el-table-column
  prop="businessDate"
  label="还款日期"
  width="160" :formatter="dateFormat">
  </el-table-column>
  <el-table-column prop="docOperator" label="操作人" width="100">
  </el-table-column>
  <el-table-column prop="returnMoneyReturnModeId" label="业务类型" width="100" :formatter="returnMoneyReturnModeFormat">
  </el-table-column>
  <el-table-column prop="overdueDays" label="逾期天数" width="100">
  </el-table-column>
  <el-table-column label="本金">
    <el-table-column prop="planCapitalAmount" label="应还" width="120" :formatter="moneyFormat">
    </el-table-column>
    <el-table-column prop="returnCapitalAmount" label="实还" width="120" :formatter="moneyFormat">
    </el-table-column>
    <el-table-column prop="remainCapitalAmount" label="剩余" width="120" :formatter="moneyFormat">
    </el-table-column>
  </el-table-column>
   ...
  </el-table-column>
</template>
</t-grid>
</template>
<script>
import util from '@/util'
export default {
  components: {},
  props: {
    loanDocId: {
      type: String,
      default: null,
    },

  },
  data() {
    return {
      loanRecoveryDocImplListGridOptions: {
        dataSource: {
          loadDataOnFirst: false,
          serviceInstance: tapp.services.PL_LoanRecoveryDoc.getImplListByLoanDocId,
          serviceInstanceInputParameters: this.loanDocId,
        },
        grid: {
          customColumnDataHeader: true,
          reduceMethod: this.getRecoveryDocImplSummaries,
          pageable: false,
          mutiSelect: false,
          defaultSort: {
            prop: 'id',
            order: 'ascending'
          },
        }
      }
    }
  },
  watch: {
    loanDocId(value) {
      if (!value) {
        return;
      }
      this.loanRecoveryDocImplListGridOptions.dataSource.serviceInstanceInputParameters = value;
    }
  },
  created() {

  },
  mounted() {},
  computed: {},
  mounted() {},
  methods: {

    refresh() {
      if (!this.loanDocId) {
        return;
      }
      this.$refs.loanRecoveryDocImplList.refresh(); 
    },
    getRecoveryDocImplSummaries(param) {
      const {
        columns,
        data
      } = param;

      if (data == null || data.length == 0) {
        return [];
      }
      const sums = [];
      sums[0] = '合计';
      let recoveryDocImplList = data;
      let sumReturnCapitalAmount = recoveryDocImplList.map(function(item) {
        return item.returnCapitalAmount;
      }).reduce(function(a, b, index, arr) {
        return Number((a || 0)) + Number((b || 0));
      });
      sums[6] = util.moneyFormat(sumReturnCapitalAmount);
      ...
      return sums;
    },
  }
}
</script>


分页及服务器端计算合计

效果
在这里插入图片描述
代码

 <template>
<div class="mod-role">
  <el-form ref="ruleForm" @keyup.enter.native="doSearch()" label-width="120px">
    <el-row :gutter="20">
      <el-col :span="9">
        <el-form-item label="组织机构">
          <base-organization-select v-model="gridOptions.dataSource.serviceInstanceInputParameters.organizationId" placeholder="请选择">
          </base-organization-select>
        </el-form-item>
      </el-col>
      <el-col :span="7">
        <el-form-item label="产品类别">
          <pl-loanProducttype-select v-model="gridOptions.dataSource.serviceInstanceInputParameters.loanProductSubTypeId" />
        </el-form-item>
      </el-col>
      <el-col :span="8">
        <el-form-item label="客户经理">
          <base-user-select role-category="base_rolecategory_trackingpersoninfomr" v-model="gridOptions.dataSource.serviceInstanceInputParameters.docOwnUserId" placeholder="请选择">
          </base-user-select>
        </el-form-item>
      </el-col>
    </el-row>
    <el-row :gutter="20">
      <el-col :span="9">
        <el-form-item label="还款日期">
          <t-datetime-range-picker v-model="dateRange" @change="onDateRangeChanged">
          </t-datetime-range-picker>
        </el-form-item>
      </el-col>
      <el-col :span="7">
        <el-form-item label="通用查询">
          <el-input v-model="gridOptions.dataSource.serviceInstanceInputParameters.searchKey" placeholder="申请编号、客户名称、身份证号" clearable></el-input>
        </el-form-item>
      </el-col>
      <el-col :span="8">
        <el-form-item>
          <el-button icon="el-icon-search" type="primary" @click="doSearch()">查询</el-button>
          <el-button icon="el-icon-download" @click="doExportExcel()">导出</el-button>
        </el-form-item>
      </el-col>
    </el-row>
  </el-form>
  <t-grid ref="searchReulstList" :options="gridOptions">
  </t-grid>
</div>
</template>

<script>
import util from '@/util'
export default {
  data() {
    return {
      dateRange: null,
      gridOptions: {
        dataSource: {
          serviceInstance: tapp.services.PL_Report.getLoanRecoveryProfitQuery,
          serviceInstanceInputParameters: {
            searchKey: null,
          }
        },
        grid: {
          mutiSelect: false,
          reduceMethod: this.getRecoveryDocImplSummaries,
          columns: [{
              prop: 'customerCode',
              label: '申请编号',
              sortable: true,
              width: 120
            },
            ...
            {
              prop: 'docOwnDepartmentName',
              label: '所属营业部',
              sortable: true,
              minWidth: 150,
            },
          ], // 需要展示的列
          defaultSort: {
            prop: 'id',
            order: 'descending'
          },
        }
      }
    }
  },
  components: {},
  created() {

  },
  methods: {
    onDateRangeChanged(val) {
      this.gridOptions.dataSource.serviceInstanceInputParameters.startDate = val[0];
      this.gridOptions.dataSource.serviceInstanceInputParameters.endDate = val[1];
    },
    getRecoveryDocImplSummaries(param) {
      const {
        columns,
        data,
        reduces
      } = param;

      if (reduces == null) {
        return [];
      }
      const sums = [];
      sums[0] = '合计';
      sums[5] = util.moneyFormat(reduces.sumLoanMoneyAmount);
      
      return sums;
    },
    doExportExcel() {
      this.$refs.searchReulstList.exportCSV('收入明细');
    },
    doSearch() {
      this.$refs.searchReulstList.refresh();
    }
  }
}
</script>



简单实用的form表单验证

前端验证抛弃element ui 繁琐的form表单验证方式,使用简单的HTML标记实现验证

必输验证

效果
在这里插入图片描述
代码

  <el-form-item label="姓名" prop="name" verify  :maxLength="50" class="is-required">
              <el-input v-model="model.name" ></el-input>
            </el-form-item>

身份证号码必输验证

效果
在这里插入图片描述
代码

  <el-col :span="8">
      <el-form-item label="身份证号" prop="customerCardNO" verify idcard  >
        <el-input v-model="headerEntity.customerCardNO"></el-input>
      </el-form-item>
    </el-col>

未完待续。。。

Logo

Authing 是一款以开发者为中心的全场景身份云产品,集成了所有主流身份认证协议,为企业和开发者提供完善安全的用户认证和访问管理服务

更多推荐