1. 指标子集映射

This commit is contained in:
李玉东 2025-08-13 14:35:20 +08:00
parent 8913652362
commit 41c6b179fa
42 changed files with 1525 additions and 484 deletions

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.hshh</groupId>
<artifactId>manager</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
</parent>
<artifactId>manager-admin</artifactId>
@ -22,8 +22,8 @@
<dependency>
<groupId>com.hshh</groupId>
<artifactId>manager-system</artifactId>
<version>1.0-SNAPSHOT</version>
<artifactId>system</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
@ -32,4 +32,19 @@
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,189 +1,241 @@
<div class="page-body" data-page="metric">
<div class="container-xl">
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8"/>
<title>AHP 两两比较矩阵Tabler风格</title>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<!-- Tabler Core CSS如果项目已全局引入可移除 -->
<link href="https://cdn.jsdelivr.net/npm/@tabler/core@1.0.0-beta20/dist/css/tabler.min.css" rel="stylesheet"/>
<style>
.matrix-wrap {
max-height: 60vh;
overflow: auto; /* 同时允许横向+纵向滚动 */
border: 1px solid #e3e8ee;
border-radius: .5rem;
background: #fff;
}
table.matrix { min-width: 720px; table-layout: fixed; }
.matrix thead th {
position: sticky; top: 0; z-index: 3;
background: #fff; box-shadow: 0 2px 0 rgba(0,0,0,.03);
}
.matrix th:first-child, .matrix td:first-child {
position: sticky; left: 0; z-index: 2; background: #fff;
box-shadow: 2px 0 0 rgba(0,0,0,.03);
}
.matrix .diag { background: #f6f8fb; text-align: center; font-weight: 600; }
.w-col { width: 110px; }
.select-judge { width: 100%; }
.cell-readonly { background: #fafbfc; color:#666; }
.small-note { font-size: .875rem; color: #666; }
.badge-cr { margin-left: .5rem; }
</style>
</head>
<body class="layout-fluid">
<div class="page">
<div class="page-body">
<div class="container-xl">
<!-- 面包屑 -->
<nav aria-label="breadcrumb" class="mb-4">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="#">能力评估</a></li>
<li class="breadcrumb-item"><a href="#">映射集</a></li>
</ol>
</nav>
<div class="row row-cards">
<div class="card">
<div class="card-header">
<div class="card-title">指标数据映射集设置</div>
<h3 class="card-title">两两比较矩阵</h3>
<div class="ms-auto">
<span class="small-note">选择“行 相对 列”的重要性1=同等3/5/7/9=逐级更重要)</span>
</div>
</div>
<div class="card-body">
<!-- 1. 选择指标(根指标)- 下拉 -->
<div class="card mb-3">
<div class="card-header">
<div class="card-title">1. 选择指标(根指标)</div>
</div>
<div class="card-body">
<div class="row g-2 align-items-center">
<div class="col-auto">
<label class="form-label m-0">根指标</label>
</div>
<div class="col-3">
<select id="rootSelect" class="form-select">
<option value="quality" selected>质量指标</option>
<option value="safety">安全指标</option>
</select>
</div>
</div>
<div class="text-muted small mt-2">切换根指标会刷新下面的“子指标映射表”。</div>
</div>
<div id="matrixContainer" class="matrix-wrap">
<table id="matrixTable" class="table table-vcenter card-table matrix">
<!-- JS 动态生成 -->
</table>
</div>
<!-- 2. 上传 CSV模拟解析结果 -->
<div class="card mb-3">
<div class="card-header">
<div class="card-title">2. 上传 CSV 文件</div>
</div>
<div class="card-body">
<input type="file" class="form-control mb-3" accept=".csv">
<div class="fw-bold mb-2">已解析表头(模拟 50 列)</div>
<div id="csvHeads" class="list-group list-group-flush csv-heads"></div>
</div>
<div class="mt-3 d-flex flex-wrap gap-2">
<button id="btnEven" class="btn btn-outline-secondary">上三角置1同等重要</button>
<button id="btnReset" class="btn btn-outline-warning">重置</button>
<button id="btnExport" class="btn btn-outline-primary">导出矩阵JSON</button>
<span id="crBadge" class="badge badge-cr">CR: --</span>
</div>
<!-- 3. 子指标映射设置 -->
<div class="card">
<div class="card-header">
<div class="card-title">3. 子指标映射设置</div>
<div class="ms-auto">
<div class="btn-list">
<button class="btn btn-outline-primary" id="allFormBtn">全部切到 Form</button>
<button class="btn btn-outline-primary" id="allCsvBtn">全部切到 CSV</button>
</div>
</div>
</div>
<div class="card-body">
<div class="map-scroller">
<table class="table table-vcenter map-table" id="mapTable">
<thead>
<tr>
<th class="sticky-top" style="width:48px;">#</th>
<th class="indicator-col sticky-top" style="width:200px;">子指标名称</th>
<th class="sticky-top" style="width:130px;">来源</th>
<th class="sticky-top" style="width:320px;">表单字段</th>
<th class="sticky-top" style="width:320px;">CSV 表头</th>
</tr>
</thead>
<tbody id="mapTableBody"><!-- JS 渲染 --></tbody>
</table>
</div>
</div>
</div>
</div><!-- /card-body -->
</div>
</div>
</div>
</div>
</div>
<!-- 样式:滚动与粘性 -->
<style>
.csv-heads{ max-height:150px; overflow:auto; }
.map-scroller{ max-height:65vh; overflow:auto; border-radius:.5rem; }
.map-table{ table-layout:fixed; width:max-content; min-width:1200px; border-collapse:separate; border-spacing:0; }
.map-table th, .map-table td{ white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.sticky-top{ position:sticky; top:0; z-index:3; background:#fff; }
.indicator-col{ position:sticky; left:0; z-index:4; background:#fff; box-shadow:2px 0 0 rgba(0,0,0,.06); }
.btn-group .btn{ min-width:52px; }
</style>
<!-- 依赖:若你项目里已全局引入,可删掉下面两行 CDN -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/tom-select/dist/css/tom-select.bootstrap5.min.css">
<script src="https://cdn.jsdelivr.net/npm/tom-select/dist/js/tom-select.complete.min.js"></script>
<!-- jQuery如已全局引入可删 -->
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script>
// ===== 静态数据 =====
const formFields = ["qualifiedRate","defectCount","inspectionDate","checkPerson","location","comment"];
const csvCols = Array.from({length:50}, (_,i)=>`col_${i+1}`);
const indicatorsByRoot = {
quality: Array.from({length:20}, (_,i)=>({ id:i+1, name:`质量_子指标_${i+1}` })),
safety: Array.from({length:20}, (_,i)=>({ id:i+1, name:`安全_子指标_${i+1}` })),
};
/** ===== 示例你的指标列表id + name ===== */
const indicators = [
{ id: 'A', name: '性能' },
{ id: 'B', name: '价格' },
{ id: 'C', name: '服务' },
{ id: 'D', name: '交付周期' }
];
// CSV 表头预览
document.getElementById('csvHeads').innerHTML =
csvCols.map(c=>`<div class="list-group-item">${c}</div>`).join('');
// 渲染子指标表格
function renderTable(rootKey){
const tbody = document.getElementById('mapTableBody');
tbody.innerHTML = indicatorsByRoot[rootKey].map((it,idx)=>`
<tr data-id="${it.id}">
<td>${idx+1}</td>
<td class="indicator-col">${it.name}</td>
<td>
<div class="btn-group btn-group-sm" role="group">
<input type="radio" class="btn-check" name="src-${it.id}" id="src-form-${it.id}" value="FORM" checked>
<label class="btn btn-outline-primary" for="src-form-${it.id}">Form</label>
<input type="radio" class="btn-check" name="src-${it.id}" id="src-csv-${it.id}" value="CSV">
<label class="btn btn-outline-primary" for="src-csv-${it.id}">CSV</label>
</div>
</td>
<td>
<select class="form-select form-select-sm form-sel">
<option value="">— 选择表单字段 —</option>
${formFields.map(f=>`<option>${f}</option>`).join('')}
</select>
</td>
<td>
<select class="form-select form-select-sm csv-sel" disabled>
<option value="">— 选择 CSV 表头 —</option>
${csvCols.map(c=>`<option>${c}</option>`).join('')}
</select>
</td>
</tr>
`).join('');
// 绑定单行切换
tbody.querySelectorAll('input[type=radio][name^=src-]').forEach(r=>{
r.addEventListener('change', onSourceChange);
});
// 重新初始化所有下拉的搜索TomSelect
document.querySelectorAll('#mapTableBody select').forEach(sel=>{
if(sel.tomselect){ sel.tomselect.destroy(); }
new TomSelect(sel, { create:false, sortField:{field:"text",direction:"asc"} });
});
/** ===== AHP 工具:几何平均法权重 + CI/CR ===== */
function geometricMeanWeights(A) {
const n = A.length;
const gm = new Array(n).fill(0);
for (let i=0;i<n;i++) {
let prod = 1;
for (let j=0;j<n;j++) prod *= A[i][j];
gm[i] = Math.pow(prod, 1/n);
}
const sum = gm.reduce((a,b)=>a+b,0);
return gm.map(x=>x/sum);
}
function lambdaMaxApprox(A, w) {
// 近似 λmax = 平均((A w)_i / w_i)
const n = A.length;
const Aw = new Array(n).fill(0);
for (let i=0;i<n;i++) {
let s=0; for (let j=0;j<n;j++) s += A[i][j]*w[j];
Aw[i] = s;
}
let acc = 0;
for (let i=0;i<n;i++) acc += Aw[i]/w[i];
return acc/n;
}
function ci(lambdaMax, n){ return (lambdaMax - n) / (n - 1); }
function cr(lambdaMax, n){
// Saaty RIn=1..10
const RI = [0.00,0.00,0.58,0.90,1.12,1.24,1.32,1.41,1.45,1.49];
if (n<1 || n>10) return NaN;
const v = ci(lambdaMax, n);
return RI[n-1]===0 ? 0 : v/RI[n-1];
}
function onSourceChange(e){
const tr = e.target.closest('tr');
const formSel = tr.querySelector('.form-sel');
const csvSel = tr.querySelector('.csv-sel');
const formTS = formSel.tomselect;
const csvTS = csvSel.tomselect;
/** ===== UI1..9 标度(上三角选择),自动倒数到下三角 ===== */
const scaleOptions = [
{v:1/9, t:'1/9 极端不如'},
{v:1/8, t:'1/8'},
{v:1/7, t:'1/7 很不如'},
{v:1/6, t:'1/6'},
{v:1/5, t:'1/5 明显不如'},
{v:1/4, t:'1/4'},
{v:1/3, t:'1/3 略不如'},
{v:1/2, t:'1/2'},
{v:1, t:'1 同等'},
{v:2, t:'2'},
{v:3, t:'3 略重要'},
{v:4, t:'4'},
{v:5, t:'5 明显重要'},
{v:6, t:'6'},
{v:7, t:'7 很重要'},
{v:8, t:'8'},
{v:9, t:'9 极端重要'}
];
if(e.target.value === 'FORM'){
formSel.disabled=false; formTS.enable();
csvSel.disabled=true; csvTS.disable(); csvTS.clear(true);
}else{
csvSel.disabled=false; csvTS.enable();
formSel.disabled=true; formTS.disable(); formTS.clear(true);
function buildMatrix(list){
const n = list.length;
const $table = $("#matrixTable");
let thead = '<thead><tr><th style="min-width:200px;"></th>';
for (let j=0;j<n;j++) thead += `<th title="${list[j].id}">${list[j].name}</th>`;
thead += `<th class="w-col">权重</th></tr></thead>`;
let tbody = '<tbody>';
for (let i=0;i<n;i++){
tbody += `<tr data-row="${i}"><th>${list[i].name}</th>`;
for (let j=0;j<n;j++){
if (i===j) {
tbody += `<td class="diag">1</td>`;
} else if (i<j) {
// 上三角:选择器
tbody += `<td data-cell="${i}-${j}">
<select class="form-select form-select-sm select-judge" data-i="${i}" data-j="${j}">
${scaleOptions.map(o=>`<option value="${o.v}">${o.t}</option>`).join('')}
</select>
</td>`;
} else { // 下三角:只读,显示倒数
tbody += `<td data-cell="${i}-${j}" class="cell-readonly text-end"></td>`;
}
}
tbody += `<td class="w-col text-end" data-weight="${i}"></td>`;
tbody += `</tr>`;
}
tbody += '</tbody>';
$table.html(thead + tbody);
// 默认上三角全 1同等
$table.find('.select-judge').val('1');
// 绑定联动
$table.on('change', '.select-judge', function(){
const i = parseInt(this.dataset.i,10), j = parseInt(this.dataset.j,10);
const v = parseFloat(this.value);
// 设置下三角为倒数
const recip = 1 / v;
const $mirror = $table.find(`[data-cell="${j}-${i}"]`);
$mirror.text(recip.toFixed(6));
recalcWeights();
});
// 初次同步一次下三角并计算
$table.find('.select-judge').trigger('change');
}
/** 读取矩阵为二维数组 */
function readMatrix(){
const n = $("#matrixTable thead th").length - 2; // 去掉左上空格和“权重”列
const A = Array.from({length:n}, ()=>Array(n).fill(1));
for (let i=0;i<n;i++){
for (let j=0;j<n;j++){
if (i===j) { A[i][j]=1; continue; }
if (i<j){
const v = parseFloat($(`.select-judge[data-i="${i}"][data-j="${j}"]`).val());
A[i][j] = v;
A[j][i] = 1/v;
}
}
}
return A;
}
/** 计算权重/CR 并渲染 */
function recalcWeights(){
const A = readMatrix();
const w = geometricMeanWeights(A);
const lm = lambdaMaxApprox(A, w);
const CR = cr(lm, A.length);
// 渲染权重列
for (let i=0;i<w.length;i++){
$(`[data-weight="${i}"]`).text(w[i].toFixed(4));
}
// 渲染 CR
const $b = $("#crBadge");
$b.text(`CR: ${isNaN(CR) ? '--' : CR.toFixed(4)}`);
$b.removeClass('bg-green bg-red bg-blue');
if (!isNaN(CR)) {
$b.addClass(CR < 0.10 ? 'bg-green' : 'bg-red');
} else {
$b.addClass('bg-blue');
}
}
// 批量切换
document.getElementById('allFormBtn').addEventListener('click', ()=>{
document.querySelectorAll('input[id^="src-form-"]').forEach(r=>{ r.checked=true; r.dispatchEvent(new Event('change')); });
});
document.getElementById('allCsvBtn').addEventListener('click', ()=>{
document.querySelectorAll('input[id^="src-csv-"]').forEach(r=>{ r.checked=true; r.dispatchEvent(new Event('change')); });
});
/** 事件:置同等、重置、导出 */
$(function(){
buildMatrix(indicators);
// 根指标选择(下拉)
document.getElementById('rootSelect').addEventListener('change', (e)=>{
renderTable(e.target.value);
});
$("#btnEven").on('click', function(){
$('.select-judge').val('1').trigger('change');
});
// 初始
renderTable('quality');
$("#btnReset").on('click', function(){
$("#matrixTable").off(); // 解绑旧事件
buildMatrix(indicators); // 重建
});
$("#btnExport").on('click', function(){
const A = readMatrix();
console.log('Matrix:', A);
alert('矩阵已输出到控制台。');
});
});
</script>
</body>
</html>

View File

@ -10,9 +10,9 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
* @author LiDongYU
* @since 2025/7/16
*/
@SpringBootApplication
@MapperScan({"com.hshh.system.**.mapper", "com.hshh.model.**.mapper",
"com.hshh.indicator.**.mapper"})
@MapperScan({"com.hshh.**.mapper"})
public class Application {
/**

View File

@ -0,0 +1,14 @@
package com.hshh.data.controller;
/**
* [类的简要说明]
* <p>
* [详细描述可选]
* <p>
*
* @author LiDongYU
* @since 2025/7/22
*/
public class TestController {
}

View File

@ -0,0 +1,140 @@
package com.hshh.evaluation.controller;
import com.hshh.evaluation.entity.EvaluationProject;
import com.hshh.evaluation.service.EvaluationProjectService;
import com.hshh.indicator.entity.Indicator;
import com.hshh.indicator.service.IndicatorService;
import com.hshh.system.common.bean.BaseController;
import com.hshh.system.common.bean.JsTree;
import com.hshh.system.common.bean.OperateResult;
import com.hshh.system.common.bean.PaginationBean;
import com.hshh.system.common.enums.ErrorCode;
import com.hshh.system.common.enums.ErrorMessage;
import io.swagger.v3.oas.annotations.Operation;
import java.time.LocalDateTime;
import java.util.List;
import javax.annotation.Resource;
import javax.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* 评估工程.
*
* @author liDongYu
* @since 2025-08-11
*/
@Controller
@RequestMapping("/evaluation/project")
public class EvaluationProjectController extends BaseController {
@Resource
private EvaluationProjectService evaluationProjectService;
@Resource
private IndicatorService indicatorService;
/**
* 默认页.
*
* @return evaluation/list.html
*/
@GetMapping("/")
@Operation(summary = "评价工程首页", description = "导航到评价工程首页")
public String list(PaginationBean request, Model model) {
setNavigateTitle(model, "/evaluation/project/");
List<EvaluationProject> list = evaluationProjectService.list(request);
Long total = evaluationProjectService.count(request);
//设置分页信息
setPaginationInfo(request, list, total, model);
return "evaluation/list";
}
/**
* 导航到增加页面.
*
* @param model session容器
* @return evaluation/add.html
*/
@GetMapping("/add")
public String add(Model model) {
List<Indicator> rootList = indicatorService.queryRootList();
model.addAttribute("rootList", rootList);
return "evaluation/add";
}
/**
* 保存评估工程.
*
* @param project 评估工程数据
* @param bindingResult 绑定的错误信息
* @return 操作结果
*/
@ResponseBody
@PostMapping("/save")
public OperateResult<Object> save(@Valid @RequestBody EvaluationProject project,
BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return errorsInputHandle(bindingResult);
}
List<EvaluationProject> projectList = evaluationProjectService.queryListByName(
project.getProjectName());
if (project.getId() == null) {
if (!projectList.isEmpty()) {
return OperateResult.error(null, ErrorMessage.NAME_OR_CODE_EXIT.getMessage(),
ErrorCode.BUSINESS_ERROR.getCode());
}
project.setCreateDate(LocalDateTime.now());
evaluationProjectService.save(project);
} else {
if (!projectList.isEmpty()) {
if (!project.getId().equals(projectList.get(0).getId())) {
return OperateResult.error(null, ErrorMessage.NAME_OR_CODE_EXIT.getMessage(),
ErrorCode.BUSINESS_ERROR.getCode());
}
evaluationProjectService.updateById(project);
}
}
return OperateResult.success();
}
/**
* 开始评估页面.
*
* @return 开始评估页面
*/
@GetMapping("/startEvaluation/{id}")
public String startEvaluation(@PathVariable("id") Integer projectId, Model model) {
EvaluationProject project = evaluationProjectService.getById(projectId);
List<Indicator> children = indicatorService.queryChildren(project.getIndicatorTopId());
model.addAttribute("children", children);
return "evaluation/start_weight_evaluation";
}
/**
* 获取指标树.
*
* @param projectId 项目ID
* @return 指标树
*/
@GetMapping("/metricTree/{id}")
@ResponseBody
public OperateResult<List<JsTree>> metricTree(@PathVariable("id") Integer projectId) {
EvaluationProject project = evaluationProjectService.getById(projectId);
if (project == null) {
return OperateResult.error(null, ErrorMessage.ID_NOT_EXIT.getMessage(),
ErrorCode.BUSINESS_ERROR.getCode());
}
return OperateResult.success(indicatorService.metricTree(project.getIndicatorTopId())
);
}
}

View File

@ -0,0 +1,44 @@
package com.hshh.evaluation.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.hshh.system.common.bean.BaseBean;
import java.time.LocalDateTime;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 工程服务类.
*
* @author liDongYu
* @since 2025-08-11
*/
@EqualsAndHashCode(callSuper = true)
@TableName("m_data_evaluation_project")
@Data
public class EvaluationProject extends BaseBean {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
@NotBlank(message = "工程名称不能为空")
@Size(max = 50, message = "名称不能超过50字符")
private String projectName;
@Size(max = 255, message = "备注不能超过255字符")
private String projectMemo;
private Integer indicatorTopId;
@TableField(exist = false)
private String indicatorTopName;
private LocalDateTime createDate;
}

View File

@ -0,0 +1,31 @@
package com.hshh.evaluation.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.hshh.evaluation.entity.EvaluationProject;
import com.hshh.system.common.bean.PaginationBean;
import java.util.List;
/**
* Mapper 接口.
*
* @author liDongYu
* @since 2025-08-11
*/
public interface EvaluationProjectMapper extends BaseMapper<EvaluationProject> {
/**
* 获取查询你结果列表.
*
* @param paginationBean 查询参数
* @return 结果列表
*/
List<EvaluationProject> list(PaginationBean paginationBean);
/**
* 总条数.
*
* @param paginationBean 查询参数
* @return 总条数
*/
Long count(PaginationBean paginationBean);
}

View File

@ -0,0 +1,39 @@
package com.hshh.evaluation.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.hshh.evaluation.entity.EvaluationProject;
import com.hshh.system.common.bean.PaginationBean;
import java.util.List;
/**
* 评价工程服务服务类.
*
* @author liDongYu
* @since 2025-08-11
*/
public interface EvaluationProjectService extends IService<EvaluationProject> {
/**
* 获取查询你结果列表.
*
* @param paginationBean 查询参数
* @return 结果列表
*/
List<EvaluationProject> list(PaginationBean paginationBean);
/**
* 总条数.
*
* @param paginationBean 查询参数
* @return 总条数
*/
Long count(PaginationBean paginationBean);
/**
* 按名称查询.
*
* @param name 工程名称
* @return 查询结果
*/
List<EvaluationProject> queryListByName(String name);
}

View File

@ -0,0 +1,40 @@
package com.hshh.evaluation.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hshh.evaluation.entity.EvaluationProject;
import com.hshh.evaluation.mapper.EvaluationProjectMapper;
import com.hshh.evaluation.service.EvaluationProjectService;
import com.hshh.system.common.bean.PaginationBean;
import java.util.List;
import org.springframework.stereotype.Service;
/**
* 服务实现类.
*
* @author liDongYu
* @since 2025-08-11
*/
@Service
public class EvaluationProjectServiceImpl extends
ServiceImpl<EvaluationProjectMapper, EvaluationProject> implements
EvaluationProjectService {
@Override
public List<EvaluationProject> list(PaginationBean paginationBean) {
return this.baseMapper.list(paginationBean);
}
@Override
public Long count(PaginationBean paginationBean) {
return this.baseMapper.count(paginationBean);
}
@Override
public List<EvaluationProject> queryListByName(String name) {
QueryWrapper<EvaluationProject> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("project_name", name);
return this.list(queryWrapper);
}
}

View File

@ -0,0 +1,157 @@
package com.hshh.indicator.controller;
import com.hshh.indicator.entity.Indicator;
import com.hshh.indicator.entity.IndicatorEvalItem;
import com.hshh.indicator.service.IndicatorEvalItemService;
import com.hshh.indicator.service.IndicatorService;
import com.hshh.system.common.bean.BaseController;
import com.hshh.system.common.bean.OperateResult;
import com.hshh.system.common.enums.ErrorCode;
import com.hshh.system.common.enums.ErrorMessage;
import io.swagger.v3.oas.annotations.Operation;
import java.util.List;
import javax.annotation.Resource;
import javax.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* 评价集处理类.
*
* @author LiDongYU
* @since 2025/7/22
*/
@Controller
@RequestMapping("/evaluation")
public class EvaluationController extends BaseController {
/**
* 指标服务类. 查询所有指标列表
*/
@Resource
private IndicatorService indicatorService;
/**
* 评价服务类.
*/
@Resource
private IndicatorEvalItemService indicatorEvalItemService;
/**
* 导航到评价集设置页面.
*
* @return indicator/evaluation_list.html
*/
@GetMapping("/evaluationList")
public String evaluationList(Integer topIndicatorId, Integer indicatorId, Model model) {
setNavigateTitle(model, "/indicator/evaluationList");
List<Indicator> rootList = indicatorService.queryRootList();
if (rootList != null && !rootList.isEmpty()) {
if (topIndicatorId != null) {
for (Indicator indicator : rootList) {
if (indicator.getId().equals(topIndicatorId)) {
indicator.setChecked(true);
}
}
} else {
topIndicatorId = rootList.get(0).getId();
rootList.get(0).setChecked(true);
}
}
//查询所有没有孩子的子节点
List<Indicator> indicatorListWithoutChildren = indicatorService.selectNoChildByTopId(
topIndicatorId);
if (!indicatorListWithoutChildren.isEmpty()) {
if (indicatorId != null) {
for (Indicator indicator : indicatorListWithoutChildren) {
if (indicator.getId().equals(indicatorId)) {
indicator.setChecked(true);
}
}
} else {
indicatorListWithoutChildren.get(0).setChecked(true);
}
}
//查询子指标的评价集
List<IndicatorEvalItem> evaluationList = indicatorEvalItemService.queryListByIndicatorId(
indicatorId == null ? (indicatorListWithoutChildren.isEmpty() ? 0
: indicatorListWithoutChildren.get(0).getId()) : indicatorId);
model.addAttribute("evaluationList", evaluationList);
model.addAttribute("rootList", rootList);
model.addAttribute("indicatorListWithoutChildren", indicatorListWithoutChildren);
return "indicator/evaluation_list";
}
/**
* 导航到评价集增加页面.
*
* @return indicator/add_evaluation.html
*/
@RequestMapping("/evaluationAdd")
@Operation(summary = "导航到评价集增加页面", description = "导航到评价集增加页面")
public String evaluationAdd() {
return "indicator/add_evaluation";
}
/**
* 保存评价集.
*
* @param item 评价集数据
* @param bindingResult 字段错误信息
* @return 操作结果
*/
@PostMapping("/save")
@ResponseBody
public OperateResult<Object> save(@Valid @RequestBody IndicatorEvalItem item,
BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return errorsInputHandle(bindingResult);
}
indicatorEvalItemService.saveOrUpdate(item);
return OperateResult.success();
}
/**
* 根据id删除评价.
*
* @param id 要删除的id
* @return 操作结果
*/
@GetMapping("/remove/{id}")
@ResponseBody
public OperateResult<Void> remove(@PathVariable("id") Integer id) {
IndicatorEvalItem evaluation = indicatorEvalItemService.getById(id);
if (evaluation == null) {
return OperateResult.error(null, ErrorMessage.ID_NOT_EXIT.getMessage(),
ErrorCode.BUSINESS_ERROR.getCode());
}
indicatorEvalItemService.removeById(id);
return OperateResult.success();
}
/**
* 根据ID查询评价信息.
*
* @param id 评价ID
* @return id对应的评价
*/
@GetMapping("/{id}")
@ResponseBody
public OperateResult<IndicatorEvalItem> view(@PathVariable("id") Integer id) {
IndicatorEvalItem evaluation = indicatorEvalItemService.getById(id);
if (evaluation == null) {
return OperateResult.error(null, ErrorMessage.ID_NOT_EXIT.getMessage(),
ErrorCode.BUSINESS_ERROR.getCode());
}
return OperateResult.success(evaluation, ErrorMessage.SUCCESS.getMessage());
}
}

View File

@ -219,60 +219,6 @@ public class IndicatorController extends BaseController {
}
/**
* 导航到评价集设置页面.
*
* @return indicator/evaluation_list.html
*/
@GetMapping("/evaluationList")
public String evaluationList(Integer topIndicatorId, Integer indicatorId, Model model) {
setNavigateTitle(model, "/indicator/evaluationList");
List<Indicator> rootList = indicatorService.queryRootList();
if (rootList != null && !rootList.isEmpty()) {
if (topIndicatorId != null) {
for (Indicator indicator : rootList) {
if (indicator.getId().equals(topIndicatorId)) {
indicator.setChecked(true);
}
}
} else {
topIndicatorId = rootList.get(0).getId();
rootList.get(0).setChecked(true);
}
}
//查询所有没有孩子的子节点
List<Indicator> indicatorListWithoutChildren = indicatorService.selectNoChildByTopId(
topIndicatorId);
if (!indicatorListWithoutChildren.isEmpty()) {
if (indicatorId != null) {
for (Indicator indicator : indicatorListWithoutChildren) {
if (indicator.getId().equals(indicatorId)) {
indicator.setChecked(true);
}
}
} else {
indicatorListWithoutChildren.get(0).setChecked(true);
}
}
model.addAttribute("rootList", rootList);
model.addAttribute("indicatorListWithoutChildren", indicatorListWithoutChildren);
return "indicator/evaluation_list";
}
/**
* 导航到评价集增加页面.
*
* @return indicator/add_evaluation.html
*/
@RequestMapping("/evaluationAdd")
@Operation(summary = "导航到评价集增加页面", description = "导航到评价集增加页面")
public String evaluationAdd() {
return "indicator/add_evaluation";
}
/**
* 导航到mapper页面.

View File

@ -5,7 +5,6 @@ import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.hshh.system.common.bean.CheckedBean;
import java.io.Serializable;
import java.util.List;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
@ -42,6 +41,9 @@ public class Indicator extends CheckedBean {
private Integer sortOrder;
public String getTitle() {
return this.name;
}
}

View File

@ -1,5 +1,7 @@
package com.hshh.indicator.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
@ -16,7 +18,7 @@ import java.io.Serializable;
public class IndicatorBottomCsvMapper implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;

View File

@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import lombok.Data;
/**
* <p>
@ -16,7 +17,7 @@ import javax.validation.constraints.Size;
* @since 2025-08-04
*/
@TableName("m_data_indicator_eval_item")
@Data
public class IndicatorEvalItem implements Serializable {
private static final long serialVersionUID = 1L;
@ -52,90 +53,5 @@ public class IndicatorEvalItem implements Serializable {
@Size(max = 10, message = "值不能超过10字符")
private String maxValue;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getIndicatorTopId() {
return indicatorTopId;
}
public void setIndicatorTopId(Integer indicatorTopId) {
this.indicatorTopId = indicatorTopId;
}
public Integer getIndicatorId() {
return indicatorId;
}
public void setIndicatorId(Integer indicatorId) {
this.indicatorId = indicatorId;
}
public Integer getSortOrder() {
return sortOrder;
}
public void setSortOrder(Integer sortOrder) {
this.sortOrder = sortOrder;
}
public String getEvaluationName() {
return evaluationName;
}
public void setEvaluationName(String evaluationName) {
this.evaluationName = evaluationName;
}
public String getMinSymbol() {
return minSymbol;
}
public void setMinSymbol(String minSymbol) {
this.minSymbol = minSymbol;
}
public String getMinValue() {
return minValue;
}
public void setMinValue(String minValue) {
this.minValue = minValue;
}
public String getMaxSymbol() {
return maxSymbol;
}
public void setMaxSymbol(String maxSymbol) {
this.maxSymbol = maxSymbol;
}
public String getMaxValue() {
return maxValue;
}
public void setMaxValue(String maxValue) {
this.maxValue = maxValue;
}
@Override
public String toString() {
return "IndicatorEvalItem{" +
"id = " + id +
", indicatorTopId = " + indicatorTopId +
", indicatorId = " + indicatorId +
", sortOrder = " + sortOrder +
", evaluationName = " + evaluationName +
", minSymbol = " + minSymbol +
", minValue = " + minValue +
", maxSymbol = " + maxSymbol +
", maxValue = " + maxValue +
"}";
}
}

View File

@ -20,4 +20,12 @@ public interface IndicatorEvalItemService extends IService<IndicatorEvalItem> {
* @return 记录
*/
List<IndicatorEvalItem> queryListByNameAndIndicatorId(Integer indicatorId, String name);
/**
* 根据子指标查询他的评价集.
*
* @param indicatorId 子指标
* @return 评价集
*/
List<IndicatorEvalItem> queryListByIndicatorId(Integer indicatorId);
}

View File

@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
import com.hshh.indicator.bean.IndicatorMapperBean;
import com.hshh.indicator.entity.Indicator;
import com.hshh.indicator.entity.IndicatorCsvColumn;
import com.hshh.system.common.bean.JsTree;
import java.io.IOException;
import java.util.List;
import org.springframework.web.multipart.MultipartFile;
@ -74,4 +75,12 @@ public interface IndicatorService extends IService<Indicator> {
* @param mapperBean 映射数据
*/
void saveBottomMapper(IndicatorMapperBean mapperBean);
/**
* 获取指定ID的树及所有的孩子及孩子的孩子...
*
* @param topId 根指标ID
* @return 树数据
*/
List<JsTree> metricTree(Integer topId);
}

View File

@ -16,14 +16,23 @@ import org.springframework.stereotype.Service;
*/
@Service
public class IndicatorEvalItemServiceImpl extends
ServiceImpl<IndicatorEvalItemMapper, IndicatorEvalItem> implements
IndicatorEvalItemService {
ServiceImpl<IndicatorEvalItemMapper, IndicatorEvalItem> implements IndicatorEvalItemService {
@Override
public List<IndicatorEvalItem> queryListByNameAndIndicatorId(Integer indicatorId, String name) {
QueryWrapper<IndicatorEvalItem> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("indicator_id", indicatorId);
queryWrapper.eq("evaluation_name", name);
return this.list(queryWrapper);
}
@Override
public List<IndicatorEvalItem> queryListByIndicatorId(Integer indicatorId) {
QueryWrapper<IndicatorEvalItem> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("indicator_id", indicatorId);
queryWrapper.orderByAsc("sort_order", "id");
return this.list(queryWrapper);
}
}

View File

@ -11,6 +11,7 @@ import com.hshh.indicator.service.IndicatorBottomFormMapperService;
import com.hshh.indicator.service.IndicatorCsvColumnService;
import com.hshh.indicator.service.IndicatorCsvService;
import com.hshh.indicator.service.IndicatorService;
import com.hshh.system.common.bean.JsTree;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
@ -155,4 +156,13 @@ public class IndicatorServiceImpl extends ServiceImpl<IndicatorMapper, Indicator
}
}
@Override
public List<JsTree> metricTree(Integer topId) {
Indicator top = getIndicator(topId);
List<Indicator> rootList = new ArrayList<>();
rootList.add(top);
return JsTree.getJsTree(rootList);
}
}

View File

@ -21,7 +21,7 @@ import org.apache.commons.lang3.StringUtils;
* @author liDongYu
* @since 2025-07-31
*/
@TableName("m_data_form_field_config")
@TableName("m_data_model_field_config")
@Data
public class FormFieldConfig implements Serializable {

View File

@ -18,7 +18,7 @@ import lombok.Data;
* @author liDongYu
* @since 2025-08-01
*/
@TableName("m_data_form_value")
@TableName("m_data_model_field_value")
@Data
public class FormValue extends BaseBean {

View File

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.hshh.evaluation.mapper.EvaluationProjectMapper">
<select id="list" resultType="com.hshh.evaluation.entity.EvaluationProject"
parameterType="com.hshh.system.common.bean.PaginationBean" databaseId="mysql">
SELECT
@rownum := @rownum + 1 AS seq,
t.*
FROM (
SELECT T1.*,T2.name as indicatorTopName FROM m_data_evaluation_project T1 left join m_data_indicator T2 on T1.indicator_top_id=T2.id
<where>
<if test="search != null and search !='' ">
T1.project_name LIKE CONCAT('%',#{search},'%')
</if>
</where>
order by T1.id asc ) t, ( SELECT @rownum := #{start} ) r limit
#{start},#{pageSize}
</select>
<select id="list" resultType="com.hshh.evaluation.entity.EvaluationProject"
parameterType="com.hshh.system.common.bean.PaginationBean" databaseId="dm">SELECT
t.seq,
t.*
FROM (
SELECT
ROW_NUMBER() OVER (ORDER BY id ASC) AS seq,
a.*,
a1.name as indicatorTopName
FROM m_data_evaluation_project a left join m_data_indicator a1 on a.indicator_top_id=a1.name
<where>
<if test="search != null and search !='' ">
a.project_name LIKE '%'||#{search}||'%'
</if>
</where>
) t
WHERE t.seq > #{start} AND t.seq &lt;= (#{start} + #{pageSize})
</select>
<select id="count" resultType="java.lang.Long" databaseId="dm">
select count(id) from m_data_evaluation_project
<where>
<if test="search != null and search !=''">
(project_name LIKE '%'||#{search}||'%') )
</if>
</where>
</select>
<select id="count" resultType="java.lang.Long" databaseId="mysql">
select count(id) from m_data_evaluation_project
<where>
<if test="search != null and search !=''">
project_name LIKE CONCAT('%',#{search},'%')
</if>
</where>
</select>
</mapper>

View File

@ -7,7 +7,7 @@
@rownum := @rownum + 1 AS seq,
t.*
FROM (
SELECT * FROM m_data_form_value
SELECT * FROM m_data_model_field_value
<where>
<if test="id!=null">
and model_define_id = #{id}
@ -27,7 +27,7 @@
SELECT
ROW_NUMBER() OVER (ORDER BY id ASC) AS seq,
a.*
FROM m_data_form_value a
FROM m_data_model_field_value a
<where>
<if test="id!=null">
and model_define_id = #{id}
@ -40,7 +40,7 @@
WHERE t.seq > #{start} AND t.seq &lt;= (#{start} + #{pageSize})
</select>
<select id="count" resultType="java.lang.Long" databaseId="dm">
select count(id) from m_data_form_value
select count(id) from m_data_model_field_value
<where>
<if test="id!=null">
and model_define_id = #{id}
@ -51,7 +51,7 @@
</where>
</select>
<select id="count" resultType="java.lang.Long" databaseId="mysql">
select count(id) from m_data_form_value
select count(id) from m_data_model_field_value
<where>
<if test="id!=null">
and model_define_id = #{id}

View File

@ -0,0 +1,24 @@
<div class="row">
<div class="col-xl-12">
<input type="hidden" name="id" id="id">
<div class="mb-3">
<label class="form-label required" for="projectName">工程名称:</label>
<input type="text" class="form-control" name="projectName" id="projectName"
placeholder="工程名称">
<div class="invalid-feedback" id="projectName_error_tip"></div>
</div>
<div class="mb-3">
<label class="form-label required" for="indicatorTopId">指标:</label>
<select name="indicatorTopId" id="indicatorTopId" class="form-control">
<option th:each="item:${rootList}" th:value="${item.id}" th:text="${item.name}"></option>
</select>
</div>
<div class="mb-3">
<label class="form-label " for="projectMemo">描述:</label>
<textarea class="form-control" name="projectMemo" id="projectMemo"
placeholder="描述信息"></textarea>
<div class="invalid-feedback" id="projectMemo_error_tip"></div>
</div>
</div>
</div>

View File

@ -0,0 +1,173 @@
<!-- Page body -->
<div class="page-body">
<div class="container-xl">
<!-- 面包屑导航 -->
<div th:replace="fragments/dialog::navigateDialog(${chainMenuList})"></div>
<div class="row g-4 mb-4">
<div class="col-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">评估工程列表</h3>
<div class="card-actions">
<a href="javascript:void(0)" class="btn btn-primary" onclick="_toAdd()">
<!-- Download SVG icon from http://tabler-icons.io/i/plus -->
<svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24"
viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none"
stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M12 5l0 14"></path>
<path d="M5 12l14 0"></path>
</svg>
新增
</a>
</div>
</div>
<div th:replace="fragments/dialog::searchConditionDialog(${condition})"></div>
<div class="table-responsive">
<table class="table card-table table-vcenter text-nowrap datatable">
<thead>
<tr>
<th class="w-1">No.
<!-- Download SVG icon from http://tabler-icons.io/i/chevron-up -->
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-sm icon-thick" width="24"
height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor"
fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M6 15l6 -6l6 6"></path>
</svg>
</th>
<th>工程名称</th>
<th>指标名称</th>
<th>创建时间</th>
<th></th>
</tr>
</thead>
<tbody>
<tr th:if="${result.list!=null}" th:each="project : ${result.list}">
<td th:text="${project.seq}"></td>
<td th:text="${project.projectName}"></td>
<td th:text="${project.indicatorTopName}"></td>
<td th:text="${#temporals.format(project.createDate, 'yyyy-MM-dd HH:mm:ss')}"></td>
<td>
<div class="btn-list flex-nowrap">
<a href="javascript:void(0)" class="btn btn-primary"
th:onclick="|_startEvaluation('${project.id}')|">
开始评估
</a>
<a href="javascript:void(0)" class="btn btn-info"
th:onclick="|_projectHistory('${project.id}')|">
评估结果
</a>
<a href="javascript:void(0)" class=" btn btn-danger"
th:onclick="|_projectDelete('${project.id}')|">
删除
</a>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div th:replace="fragments/dialog::paginationDialog(${result})"></div>
</div>
</div>
</div>
</div>
</div>
<div th:replace="fragments/dialog::confirmationDialog"></div>
<form id="projectForm">
<div th:replace="fragments/dialog::addSimpleFormDialog"></div>
</form>
<form id="stepForm">
<input type="hidden" id="projectId" name="projectId">
<div th:replace="fragments/dialog::addFullScreenFormDialog"></div>
</form>
<script>
function _pageSearch() {
let query = formObjToObject(document.getElementById("searchForm"));
document.getElementById("_evaluation_project_").setAttribute("hx-vals",
JSON.stringify(query))
document.getElementById("_evaluation_project_").click();
}
function _toAdd(data) {
let url = document.getElementById("_rootPath").value + "evaluation/project/add";
let http = new HttpClient();
http.get(url, function (error, res, xhr) {
document.getElementById("simpleFormBody").innerHTML = res;
openDialog("simple-form-model");
if (data) {
//给窗体赋值
fillData(data);
}
document.getElementById("SimpleFormDialog_save").onclick = function () {
saveProject();
}
}, "projectForm")
}
function saveProject() {
let url = document.getElementById("_rootPath").value + "evaluation/project/save";
let http = new HttpClient();
let postObj = formObjToObject(document.getElementById("projectForm"));
http.post(url, postObj, function (error, response, body) {
closeDialog("simple-form-model");
document.getElementById("_evaluation_project_").click();
}, "projectForm", "simple-form-model")
}
//第一步加载指标树
function _startEvaluation(projectId) {
let url = document.getElementById("_rootPath").value + "evaluation/project/startEvaluation/"+projectId;
let http = new HttpClient();
document.getElementById("stepForm")["projectId"].value = projectId;
http.get(url, function (error, response, xhr) {
document.getElementById("add-full-screen-form-modal-body").innerHTML = response;
openDialog("modal-full-width");
initTreeMetric();
}, "stepForm")
}
function _projectDelete(id) {
}
function _projectEdit(id) {
}
function _projectHistory(id) {
}
//初始化指标树
function initTreeMetric() {
let projectId = document.getElementById("stepForm")['projectId'].value;
let url = document.getElementById("_rootPath").value + "evaluation/project/metricTree/"
+ projectId;
let http = new HttpClient();
http.get(url, function (error, response, body) {
$('#project_metric_tree').jstree({
'core': {
'data': response.result
}
}).on("select_node.jstree", function (e, data) {
});
}, null);
}
</script>

View File

@ -0,0 +1,81 @@
<div class="row g-4" data-page="start_evaluation">
<div class="card-body">
<ul class="steps steps-green my-4">
<li class="step-item active">设置/加载 权重</li>
<li class="step-item ">选择/上传 数据集</li>
<li class="step-item">执行评估</li>
<li class="step-item">本次结果</li>
</ul>
</div>
</div>
<div class="row g-4" id="evaluation-start-page">
<div class="col-4" id="project_metric_tree" style="overflow-y: scroll; min-height: 600px;">
</div>
<div class="col-8">
<div class="card" style="overflow-y: scroll; min-height: 600px;">
<div class="card-header">
<div class="card-title">权重设置区</div>
<div class="card-actions">
<a href="#" class="btn btn-primary">
<!-- Download SVG icon from http://tabler-icons.io/i/plus -->
<svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24"
viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none"
stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M12 5l0 14"></path>
<path d="M5 12l14 0"></path>
</svg>
保存
</a>
</div>
</div>
<div class="card-body">
<div class="map-scroller">
<table class="table table-vcenter table-striped table-hover align-middle map-table">
<thead>
<tr>
<th></th>
<th th:each="item:${children}" th:text="${item.name}" style="font-weight: bold;"></th>
</tr>
</thead>
<tbody class="autonum">
<tr th:each="item:${children}">
<td th:text="${item.name}" style="font-weight: bold;"></td>
<td th:each="innerItem:${children}" >
<select class="form-select" >
<option value="9" >极端重要(9)</option>
<option value="8">更强烈重要(8)</option>
<option value="7">强烈重要(7)</option>
<option value="6">十分重要(6)</option>
<option value="5">明显重要(5)</option>
<option value="4">更为重要(4)</option>
<option value="3">稍微重要(3)</option>
<option value="2">微小重要(2)</option>
<option value="1" th:selected="${item.id==innerItem.id}">同样重要(1)</option>
<option value="0.5">微小次要(0.5)</option>
<option value="0.3333">稍微次要(0.3333)</option>
<option value="0.25">更为次要(0.25)</option>
<option value="0.2">明显次要(0.2)</option>
<option value="0.16667">十分次要(0.16667)</option>
<option value="0.14286">强烈次要(0.14286)</option>
<option value="0.125">更强烈次要(0.125)</option>
<option value="0.11111">极端次要(0.11111)</option>
</select>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>

View File

@ -90,6 +90,97 @@
.alert {
margin-bottom: 1rem;
}
:root {
--col-index-w: 48px; /* 首列宽度(与 th/td 一致) */
}
/* CSV 预览区域 */
.csv-heads {
max-height: 300px;
overflow: auto;
}
/* 滚动容器 */
.map-scroller {
max-height: 65vh;
overflow: auto;
border-radius: .5rem;
background: #fff;
}
/* 表格与单元格 */
.map-table {
table-layout: fixed;
width: max-content;
min-width: 1200px;
border-collapse: separate;
border-spacing: 0;
}
.map-table th, .map-table td {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
padding-top: .55rem;
padding-bottom: .55rem;
}
/* ===== 表头:粘顶 + 粘左(前两列) —— 层级最高 ===== */
.sticky-header {
position: sticky;
top: 0;
z-index: 10;
background: #fff;
border-bottom: 1px solid #e9ecef;
box-shadow: 0 2px 0 rgba(0, 0, 0, .04);
font-weight: bold;
}
.sticky-header.sticky-col-1 {
left: 0;
z-index: 11;
}
.sticky-header.sticky-col-2 {
left: var(--col-index-w);
z-index: 11;
}
/* ===== 表体:粘左(前两列) —— 层级低于表头 ===== */
.sticky-body-col-1 {
position: sticky;
left: 0;
z-index: 4;
background: #fff;
}
.sticky-body-col-2 {
position: sticky;
left: var(--col-index-w);
z-index: 4;
background: #fff;
box-shadow: 2px 0 0 rgba(0, 0, 0, .06); /* 分隔线效果 */
}
/* 自动编号tbody 开始计数tr 自增;序号/名称自动输出数值 */
.autonum {
counter-reset: row;
}
.autonum tr {
counter-increment: row;
}
.autonum .col-index::before {
content: counter(row);
}
.autonum .col-name::after {
}
.map-table th {
font-weight: bold !important;
}
</style>
</head>
<body>
@ -174,6 +265,7 @@
// htmx等局部刷新后确保DOM插入再init
document.body.addEventListener('htmx:afterSwap', function (evt) {
if (evt.detail.target.id === 'main-content') {
if (evt.detail.target.querySelector('[data-page="menu-list"]')) {
setTimeout(initMenu, 0); // 保证DOM已插入
@ -193,6 +285,7 @@
hideContextMenu && hideContextMenu();
});
}
//指标映射
if(evt.detail.target.querySelector('[data-page="indicator_mapper"]')){
if(typeof csvListen === 'function'){

View File

@ -13,25 +13,39 @@
</div>
<div class="mb-3">
<label class="form-label required">符号(最小):</label>
<input type="text" class="form-control" name="minSymbol" id="minSymbol"
placeholder="开始符号">
<label class="form-label required" for="minSymbol">符号:</label>
<select name="minSymbol" id="minSymbol" class="form-select">
<option value="">默认空</option>
<option value="==">等于</option>
<option value=">=">大于等于</option>
<option value=">">大于</option>
<option value="<">小于</option>
<option value="<=">小于等于</option>
</select>
<div class="invalid-feedback" id="minSymbol_error_tip"></div>
</div>
<div class="mb-3">
<label class="form-label required">值最小):</label>
<label class="form-label required">(下限):</label>
<input type="text" class="form-control" name="minValue" id="minValue"
placeholder="开始值">
<div class="invalid-feedback" id="minValue_error_tip"></div>
</div>
<div class="mb-3">
<label class="form-label required">符号(最大):</label>
<input type="text" class="form-control" name="maxSymbol" id="maxSymbol"
placeholder="开始符号">
<label class="form-label required" for="maxSymbol">符号:</label>
<select name="maxSymbol" id="maxSymbol" class="form-select">
<option value="">默认空</option>
<option value="==">等于</option>
<option value=">=">大于等于</option>
<option value=">">大于</option>
<option value="<">小于</option>
<option value="<=">小于等于</option>
</select>
<div class="invalid-feedback" id="maxSymbol_error_tip"></div>
</div>
<div class="mb-3">
<label class="form-label required">值(最大):</label>
<label class="form-label required">值(上限):</label>
<input type="text" class="form-control" name="maxValue" id="maxValue"
placeholder="开始值">
<div class="invalid-feedback" id="maxValue_error_tip"></div>

View File

@ -1,10 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
</body>
</html>

View File

@ -29,7 +29,8 @@
</div>
<!-- 新增:上次上传文件 -->
<div class="text-muted small mt-2">说明: 切换不同的指标可以设置当前指标子项的评价标准</div>
<div class="text-muted small mt-2">说明: 切换不同的指标可以设置当前指标子项的评价标准
</div>
</div>
<div class="row">
<div class="col-md-3">
@ -45,7 +46,7 @@
<label class="form-check form-check-inline">
<input class="form-check-input" type="radio"
name="radios-inline-indicator-no-child" th:value="${item.getId()}"
th:checked="${item.checked}">
th:checked="${item.checked}" onclick="changeIndicator()">
<span class="form-check-label" th:text="${item.getName()}"></span>
</label>
@ -57,6 +58,7 @@
</div>
<div class="col-md-9">
<div class="row">
<div class="text-end">
<a href="#" class="btn btn-primary active w-10" onclick="evaluation_add()">
增加评价
@ -70,13 +72,27 @@
<th>序号</th>
<th>名称</th>
<th>符号</th>
<th></th>
<th>下限</th>
<th>符号</th>
<th class="w-1"></th>
<th>上限值</th>
<th ></th>
</tr>
</thead>
<tbody>
<tr th:each="item, stats :${evaluationList}">
<td th:text="${stats.count}"></td>
<td th:text="${item.getEvaluationName()}"></td>
<td th:text="${item.getMinSymbol()}"></td>
<td th:text="${item.getMinValue()}"></td>
<td th:text="${item.getMaxSymbol()}"></td>
<td th:text="${item.getMaxValue()}"></td>
<td class="w-15">
<a href="javascript:void(0)"
th:onclick="|editEvaluation('${item.id}')|">编辑</a>
<a href="javascript:void(0)"
th:onclick="|removeEvaluation('${item.id}')|">删除</a>
</td>
</tr>
</tbody>
</table>
</div>
@ -93,9 +109,10 @@
<form id="evaluationForm">
<div th:replace="fragments/dialog::addSimpleFormDialog"></div>
</form>
<div th:replace="fragments/dialog::confirmationDialog"></div>
<script>
function evaluation_add(data) {
let url = document.getElementById("_rootPath").value + "indicator/evaluationAdd";
let url = document.getElementById("_rootPath").value + "evaluation/evaluationAdd";
let http = new HttpClient();
http.get(url, function (err, res, xhr) {
@ -125,7 +142,52 @@
}
function evaluation_save() {
let url = document.getElementById("_rootPath").value + "evaluation/save";
let http = new HttpClient();
let form = document.getElementById("evaluationForm");
http.post(url, formObjToObject(form), function (err, res, xhr) {
closeDialog("simple-form-model");
//重新刷新页面
let topId = $('input[type=radio][name="radios-inline"]:checked').val();
let subId = $(
'input[name="radios-inline-indicator-no-child"]:checked').val();
document.getElementById("_evaluation_evaluationList").setAttribute("hx-vals",
JSON.stringify({topIndicatorId: topId, indicatorId: subId}))
document.getElementById("_evaluation_evaluationList").click();
}, "evaluationForm", "simple-form-model");
}
//编辑
function editEvaluation(id) {
let url = document.getElementById("_rootPath").value + "evaluation/" + id;
let http = new HttpClient();
http.get(url, function (error, res, xhr) {
evaluation_add(res.result);
}, "evaluationForm", "simple-form-model");
}
//删除
function removeEvaluation(id) {
openDialog("modal-danger");
document.getElementById('delete-confirm-9999').onclick = function () {
let url = document.getElementById("_rootPath").value + "evaluation/remove/" + id;
let http = new HttpClient();
http.get(url, (err, res, xhr) => {
if (!err) {
//模拟点击菜单
document.getElementById("_evaluation_evaluationList").click();
}
})
};
}
function changeIndicator(){
let topId = $('input[type=radio][name="radios-inline"]:checked').val();
let subId = $(
'input[name="radios-inline-indicator-no-child"]:checked').val();
document.getElementById("_evaluation_evaluationList").setAttribute("hx-vals",
JSON.stringify({topIndicatorId: topId, indicatorId: subId}))
document.getElementById("_evaluation_evaluationList").click();
}
</script>

View File

@ -50,12 +50,12 @@
<!-- 1. 根指标(静态下拉) -->
<div class="card mb-3">
<div class="card-header">
<div class="card-title">2. 选择form表单</div>
<div class="card-title">2. 选择设施</div>
</div>
<div class="card-body">
<div class="row g-2 align-items-center">
<div class="col-auto">
<label for="modelId" class="form-label m-0">form名称</label>
<label for="modelId" class="form-label m-0">设施名称</label>
</div>
<div class="col-3">
<select id="modelId" class="form-select" onchange="formModelChange(this)">
@ -80,7 +80,7 @@
<!-- 新增:上次上传文件 -->
<div class="mb-2" th:if="${csvMapper!=null}">
<span class="fw-bold">上次上传文件:</span>
<a th:href="@{/indicator/download/{id}(id=${csvMapper.getId()})" target="_blank"
<a th:href="@{/indicator/download/{id}(id=${csvMapper.id})}" target="_blank"
th:text="${csvMapper.getCsvName()}"></a>
</div>
<div class="fw-bold mb-2" id="csvHeader-total-num"></div>
@ -104,7 +104,7 @@
<th class="sticky-header sticky-col-1" style="width:48px;">#</th>
<th class="sticky-header sticky-col-2" style="width:200px;">子指标名称</th>
<th class="sticky-header" style="width:320px;">表单字段</th>
<th class="sticky-header" style="width:320px;">设施字段</th>
<th class="sticky-header" style="width:320px;">CSV 表头</th>
</tr>
</thead>
@ -154,93 +154,7 @@
<!-- ====== 样式(关键:表头与表体分层;首两列分别粘左) ====== -->
<style>
:root {
--col-index-w: 48px; /* 首列宽度(与 th/td 一致) */
}
/* CSV 预览区域 */
.csv-heads {
max-height: 300px;
overflow: auto;
}
/* 滚动容器 */
.map-scroller {
max-height: 65vh;
overflow: auto;
border-radius: .5rem;
background: #fff;
}
/* 表格与单元格 */
.map-table {
table-layout: fixed;
width: max-content;
min-width: 1200px;
border-collapse: separate;
border-spacing: 0;
}
.map-table th, .map-table td {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
padding-top: .55rem;
padding-bottom: .55rem;
}
/* ===== 表头:粘顶 + 粘左(前两列) —— 层级最高 ===== */
.sticky-header {
position: sticky;
top: 0;
z-index: 10;
background: #fff;
border-bottom: 1px solid #e9ecef;
box-shadow: 0 2px 0 rgba(0, 0, 0, .04);
}
.sticky-header.sticky-col-1 {
left: 0;
z-index: 11;
}
.sticky-header.sticky-col-2 {
left: var(--col-index-w);
z-index: 11;
}
/* ===== 表体:粘左(前两列) —— 层级低于表头 ===== */
.sticky-body-col-1 {
position: sticky;
left: 0;
z-index: 4;
background: #fff;
}
.sticky-body-col-2 {
position: sticky;
left: var(--col-index-w);
z-index: 4;
background: #fff;
box-shadow: 2px 0 0 rgba(0, 0, 0, .06); /* 分隔线效果 */
}
/* 自动编号tbody 开始计数tr 自增;序号/名称自动输出数值 */
.autonum {
counter-reset: row;
}
.autonum tr {
counter-increment: row;
}
.autonum .col-index::before {
content: counter(row);
}
.autonum .col-name::after {
}
</style>
<script>

View File

@ -2,7 +2,7 @@
<style>
#tree-container {
height: 800px;
height: 100%;
min-width: 600px;
/* 不写width: 100%否则有兼容问题直接让col-md-8撑其宽 */
overflow-x: auto;
@ -190,8 +190,12 @@
</div>
</div>
</div>
<div class="col-md-9" id="tree-container" style="padding-left:0;">
<div class="col-md-9" id="tree-container" style="padding-left:0; min-height: 50em ;max-height: 60em ">
<div class="tree-toolbar" id="tree-toolbar">
<button id="zoom-in" title="放大" class="btn btn-sm btn-outline-primary">+</button>
<button id="zoom-out" title="缩小" class="btn btn-sm btn-outline-primary">-</button>
<button id="zoom-reset" title="重置" class="btn btn-sm btn-outline-secondary">重置</button>
</div>
</div>
</div>
</div>
@ -315,12 +319,39 @@
.size([width - 120, height - 100]);
treeLayout(root);
// 1. 创建SVG和主g
const svg = container.append("svg")
.attr("width", width)
.attr("height", height);
.attr("height", height)
.style("display", "block")
.style("margin", "0 auto");
// 普通竖向曲线注意y加marginTop
svg.append("g")
// 2. 创建主g用于缩放
const mainG = svg.append("g").attr("class", "main-g");
// 3. 缩放行为
const zoom = d3.zoom()
.scaleExtent([0.2, 3])
.on("zoom", (event) => {
mainG.attr("transform", event.transform);
});
svg.call(zoom);
// 4. 绑定按钮事件
document.getElementById("zoom-in").onclick = function () {
svg.transition().call(zoom.scaleBy, 1.2);
};
document.getElementById("zoom-out").onclick = function () {
svg.transition().call(zoom.scaleBy, 0.8);
};
document.getElementById("zoom-reset").onclick = function () {
svg.transition().call(zoom.scaleTo, 1);
// svg.transition().call(zoom.translateTo, width / 2, marginTop); // 如需重置到居中
};
// 5. 后续所有g内容都加到mainG上
// 绘制连线
mainG.append("g")
.selectAll("path")
.data(root.links())
.join("path")
@ -330,8 +361,8 @@
.y(d => d.y + marginTop)
);
// 先渲染文字
const nodeG = svg.append("g")
// 绘制节点
const nodeG = mainG.append("g")
.selectAll("g")
.data(root.descendants())
.join("g")
@ -346,7 +377,7 @@
.attr("dominant-baseline", "middle")
.text(d => d.data.name);
// 再根据文字宽高画自适应rect
// 自适应rect
nodeG.each(function (d) {
const text = d3.select(this).select("text");
const bbox = text.node().getBBox();

View File

@ -5,12 +5,10 @@
<parent>
<groupId>com.hshh</groupId>
<artifactId>manager</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
</parent>
<artifactId>manager-system</artifactId>
<artifactId>system</artifactId>
<packaging>jar</packaging>
<name>manager-system</name>
<url>http://maven.apache.org</url>

View File

@ -14,7 +14,6 @@ import java.util.List;
import javax.annotation.Resource;
import javax.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

View File

@ -0,0 +1,131 @@
package com.hshh.system.common.algorithm;
import java.util.Arrays;
/**
* 权重算法.
*
* @author LiDongYU
* @since 2025/7/22
*/
public class Ahp {
/**
* 最大特征值 + 主特征向量已做 L1 归一化分量和=1直接可作权重.
*/
public static final class EigenResult {
public final double lambdaMax;
public final double[] w; // 权重向量=1
public EigenResult(double lambdaMax, double[] w) {
this.lambdaMax = lambdaMax;
this.w = w;
}
@Override
public String toString() {
return "lambdaMax=" + lambdaMax + ", w=" + Arrays.toString(w);
}
}
/**
* 幂法主特征值/向量
*/
public static EigenResult principalEigen(double[][] A, double tol, int maxIter) {
int n = A.length;
if (n == 0 || A[0].length != n) {
throw new IllegalArgumentException("A must be square");
}
double[] v = new double[n];
Arrays.fill(v, 1.0 / n); // 初始向量=1
double[] Av = new double[n];
double lambdaOld = 0.0;
for (int it = 0; it < maxIter; it++) {
// Av = A * v
for (int i = 0; i < n; i++) {
double s = 0.0;
for (int j = 0; j < n; j++) {
s += A[i][j] * v[j];
}
Av[i] = s;
}
// Rayleigh 商近似特征值
double num = 0.0, den = 0.0;
for (int i = 0; i < n; i++) {
num += v[i] * Av[i];
den += v[i] * v[i];
}
double lambda = num / den;
// L1 归一直接得到可用权重
double sum = 0.0;
for (double x : Av) {
sum += Math.abs(x);
}
if (sum == 0.0) {
throw new ArithmeticException("Av collapsed to zero.");
}
for (int i = 0; i < n; i++) {
v[i] = Av[i] / sum;
}
if (Math.abs(lambda - lambdaOld) < tol) {
return new EigenResult(lambda, v.clone());
}
lambdaOld = lambda;
}
return new EigenResult(lambdaOld, v.clone());
}
/**
* AHP由判断矩阵得到局部权重向量=1
*/
public static double[] localWeights(double[][] A) {
return principalEigen(A, 1e-12, 10000).w;
}
/**
* 父权重 × 子局部权重 -> 子全局权重
*/
public static double[] propagate(double parentGlobal, double[] childLocal) {
double[] g = new double[childLocal.length];
for (int i = 0; i < g.length; i++) {
g[i] = parentGlobal * childLocal[i];
}
return g;
}
/**
* 一致性CI CRRI Saaty 常用表
*/
public static double consistencyIndex(double lambdaMax, int n) {
return (lambdaMax - n) / (n - 1);
}
public static double consistencyRatio(double lambdaMax, int n) {
// n=1..10
double[] RI = {0.0, 0.0, 0.58, 0.90, 1.12, 1.24, 1.32, 1.41, 1.45, 1.49};
if (n < 1 || n > 10) {
return Double.NaN;
}
double ci = consistencyIndex(lambdaMax, n);
double ri = RI[n - 1];
return ri == 0 ? 0.0 : ci / ri;
}
/**
* 小工具格式化向量
*/
public static String fmt(double[] v) {
StringBuilder sb = new StringBuilder("[");
for (int i = 0; i < v.length; i++) {
if (i > 0) {
sb.append(", ");
}
sb.append(String.format("%.4f", v[i]));
}
return sb.append("]").toString();
}
}

View File

@ -10,7 +10,7 @@ import lombok.Data;
* @since 2025/7/22
*/
@Data
public class CheckedBean {
public class CheckedBean {
@TableField(exist = false)
private boolean checked;

View File

@ -1,18 +0,0 @@
package com.hshh.system.common.bean;
import lombok.Data;
/**
* JsMindBean. 前端jsmind对象
*
* @author LiDongYU
* @date 2025/7/18
* @description
*/
@Data
public class JsMindBean {
private String id;
private boolean isroot;
private String topic;
private String parentid;
}

View File

@ -1,6 +1,6 @@
package com.hshh.system.common.bean;
import com.hshh.system.base.entity.Menus;
import com.hshh.system.reflect.GetterUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@ -71,14 +71,14 @@ public class JsTree {
* @param topMenus 内部菜单
* @return jsTree菜单列表
*/
public static List<JsTree> getJsTree(List<Menus> topMenus) {
public static <T> List<JsTree> getJsTree(List<T> topMenus) {
List<JsTree> jsTreeList = new ArrayList<>();
if (topMenus == null || topMenus.isEmpty()) {
jsTreeList.add(createRoot());
return jsTreeList;
}
for (Menus menu : topMenus) {
for (Object menu : topMenus) {
JsTree jsTree = createJsMenu(menu);
jsTreeList.add(jsTree);
buildJsTree(menu, jsTree);
@ -95,10 +95,11 @@ public class JsTree {
return jsTreeList;
}
private static void buildJsTree(Menus menu, JsTree jsTree) {
if (menu.getChildren() != null && !menu.getChildren().isEmpty()) {
private static void buildJsTree(Object menu, JsTree jsTree) {
if (GetterUtils.getValue(menu, "children", List.class) != null) {
List<JsTree> children = new ArrayList<>();
for (Menus child : menu.getChildren()) {
for (Object child : GetterUtils.getValue(menu, "children", List.class)) {
JsTree jsChild = createJsMenu(child);
children.add(jsChild);
@ -118,14 +119,15 @@ public class JsTree {
return jsTree;
}
private static JsTree createJsMenu(Menus menu) {
private static JsTree createJsMenu(Object menu) {
JsTree jsTree = new JsTree();
jsTree.setId("tree_" + menu.getId());
jsTree.setOriginalId(menu.getId());
jsTree.setText(menu.getTitle());
jsTree.setId("tree_" + GetterUtils.getValue(menu, "id", Integer.class));
jsTree.setOriginalId(GetterUtils.getValue(menu, "id", Integer.class));
jsTree.setText(GetterUtils.getValue(menu, "title", String.class));
State state = new State();
state.setOpened(true);
jsTree.setState(state);
return jsTree;
}
}

View File

@ -72,6 +72,12 @@ public class OperateResult<T> {
}
public static <T> OperateResult<T> success(T result) {
return new OperateResult<T>(result, ErrorMessage.SUCCESS.getMessage(),
ErrorCode.SUCCESS.getCode());
}
/**
* 成功响应.
*
@ -79,7 +85,8 @@ public class OperateResult<T> {
* @return 成功结果对象
*/
public static <T> OperateResult<T> success() {
return new OperateResult<T>(null, ErrorMessage.SUCCESS.getMessage(), ErrorCode.SUCCESS.getCode());
return new OperateResult<T>(null, ErrorMessage.SUCCESS.getMessage(),
ErrorCode.SUCCESS.getCode());
}

View File

@ -0,0 +1,49 @@
package com.hshh.system.reflect;
import java.lang.reflect.Method;
/**
* 反射调用类的get方法.
*
* @author LiDongYU
* @since 2025/7/22
*/
public class GetterUtils {
/**
* 调用对象的 getter 方法返回值.
*
* @param obj 目标对象
* @param fieldName 字段名 "name"自动拼接成 getName()
* @return getter 返回的值
*/
public static <T> T getValue(Object obj, String fieldName,Class<T> type) {
if (obj == null) {
throw new IllegalArgumentException("对象不能为空");
}
if (fieldName == null || fieldName.trim().isEmpty()) {
throw new IllegalArgumentException("字段名不能为空");
}
try {
// 构造 getter 方法名
String methodName = "get"
+ fieldName.substring(0, 1).toUpperCase()
+ fieldName.substring(1);
Class<?> clazz = obj.getClass();
Method method;
try {
method = clazz.getMethod(methodName); // 优先找 public
} catch (NoSuchMethodException e) {
method = clazz.getDeclaredMethod(methodName); // 找不到再尝试私有
method.setAccessible(true);
}
return type.cast(method.invoke(obj)) ;
} catch (Exception e) {
throw new RuntimeException("调用 getter 失败: " + fieldName, e);
}
}
}

View File

@ -5,7 +5,7 @@
<parent>
<groupId>com.hshh</groupId>
<artifactId>manager</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
</parent>
<artifactId>manager-z-generation</artifactId>

View File

@ -24,12 +24,12 @@ public class CodeGenerator {
})
.packageConfig(builder -> {
builder.parent("com.hshh") // 设置父包名
.moduleName("indicator") // 设置模块名可选
.moduleName("evaluation") // 设置模块名可选
.pathInfo(Collections.singletonMap(OutputFile.xml,
basePath + "/src/main/resources/mapper")); // 设置mapperXml生成路径
})
.strategyConfig(builder -> {
builder.addInclude("m_data_indicator_bottom_csv_mapper") // 设置需要生成的表名多个用逗号分隔
builder.addInclude("m_data_evaluation_template") // 设置需要生成的表名多个用逗号分隔
.addTablePrefix("m_data_"); // 设置过滤表前缀
})
.execute();

10
pom.xml
View File

@ -9,9 +9,8 @@
</parent>
<groupId>com.hshh</groupId>
<artifactId>manager</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
<packaging>pom</packaging>
<name>manager</name>
<url>http://maven.apache.org</url>
<modules>
@ -43,7 +42,11 @@
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.hshh</groupId>
<artifactId>system</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
@ -118,4 +121,5 @@
</dependency>
</dependencies>
</dependencyManagement>
</project>