1. 指标子集映射
This commit is contained in:
parent
8913652362
commit
41c6b179fa
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 RI(n=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;
|
||||
/** ===== UI:1..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>
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
package com.hshh.data.controller;
|
||||
|
||||
/**
|
||||
* [类的简要说明]
|
||||
* <p>
|
||||
* [详细描述,可选]
|
||||
* <p>
|
||||
*
|
||||
* @author LiDongYU
|
||||
* @since 2025/7/22
|
||||
*/
|
||||
public class TestController {
|
||||
|
||||
}
|
||||
|
|
@ -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())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
@ -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页面.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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 +
|
||||
"}";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
||||
|
|
|
|||
|
|
@ -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 <= (#{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>
|
||||
|
|
@ -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 <= (#{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}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
173
manager-admin/src/main/resources/templates/evaluation/list.html
Normal file
173
manager-admin/src/main/resources/templates/evaluation/list.html
Normal 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>
|
||||
|
|
@ -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>
|
||||
|
||||
|
||||
|
||||
|
|
@ -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'){
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -1,10 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 与 CR(RI 取 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();
|
||||
}
|
||||
}
|
||||
|
|
@ -10,7 +10,7 @@ import lombok.Data;
|
|||
* @since 2025/7/22
|
||||
*/
|
||||
@Data
|
||||
public class CheckedBean {
|
||||
public class CheckedBean {
|
||||
|
||||
@TableField(exist = false)
|
||||
private boolean checked;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
10
pom.xml
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user