1. 指标子集映射

This commit is contained in:
李玉东 2025-08-10 15:30:13 +08:00
parent 75328fc0c5
commit 92fb41f419
36 changed files with 753 additions and 413 deletions

View File

@ -1,274 +1,189 @@
<!-- Page body -->
<style>
#tree-container {
width: 100%;
height: 600px;
overflow: auto;
background: #fff;
border-radius: .5rem;
box-shadow: 0 1px 3px rgba(0,0,0,.08);
padding: 1rem;
position: relative;
}
.node-rect {
fill: #fff;
stroke: #206bc4;
stroke-width: 2px;
rx: 8;
ry: 8;
filter: drop-shadow(0px 2px 6px #206bc420);
}
.node-text {
font-size: 15px;
fill: #206bc4;
text-anchor: middle;
font-family: 'Inter', 'Segoe UI', Arial, sans-serif;
dominant-baseline: middle;
pointer-events: none;
}
.link {
fill: none;
stroke: #b7c4d2;
stroke-width: 2px;
}
@media (max-width: 600px) {
#tree-container {
height: 400px;
padding: .5rem;
}
}
.center-btn {
position: absolute;
left: 50%;
top: 45%;
transform: translate(-50%, -50%);
}
.context-menu {
position: absolute;
z-index: 9999;
min-width: 140px;
background: #fff;
border-radius: 6px;
box-shadow: 0 4px 18px rgba(60,72,88,0.12);
border: 1px solid #e9ecef;
font-size: 15px;
padding: 4px 0;
display: none;
}
.context-menu-item {
cursor: pointer;
padding: 8px 18px;
transition: background 0.18s;
color: #206bc4;
border: none;
background: none;
text-align: left;
width: 100%;
outline: none;
}
.context-menu-item:hover {
background: #f1f7fe;
color: #206bc4;
}
</style>
<link href="https://unpkg.com/@tabler/core@1.0.0-beta24/dist/css/tabler.min.css" rel="stylesheet">
<script src="https://d3js.org/d3.v7.min.js"></script>
<div class="page-body">
<div class="page-body" data-page="metric">
<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>
<li class="breadcrumb-item"><a href="#">映射集</a></li>
</ol>
</nav>
<div class="row g-4 mb-4">
<div class="col-md-12" id="tree-container"></div>
<div class="row row-cards">
<div class="card">
<div class="card-header">
<div class="card-title">指标数据映射集设置</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>
<!-- 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>
<!-- 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 id="context-menu" class="context-menu"></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>
<script>
let treeData = null;
// ===== 静态数据 =====
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}` })),
};
/**
* 右键菜单渲染
*/
function showContextMenu(x, y, nodeData) {
// 定义菜单项
const menuItems = [
{ text: "添加子节点", action: "add" },
{ text: "编辑", action: "edit" },
{ text: "删除", action: "delete" }
];
// CSV 表头预览
document.getElementById('csvHeads').innerHTML =
csvCols.map(c=>`<div class="list-group-item">${c}</div>`).join('');
const menu = document.getElementById("context-menu");
menu.innerHTML = "";
menuItems.forEach(item => {
const btn = document.createElement("button");
btn.className = "context-menu-item";
btn.textContent = item.text;
btn.onclick = function(e) {
e.stopPropagation();
menu.style.display = "none";
handleMenuAction(item.action, nodeData);
};
menu.appendChild(btn);
// 渲染子指标表格
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);
});
// 适应视窗,避免溢出
const winW = window.innerWidth, winH = window.innerHeight;
menu.style.left = (x + 150 > winW ? winW - 160 : x) + "px";
menu.style.top = (y + 120 > winH ? winH - 130 : y) + "px";
menu.style.display = "block";
}
function hideContextMenu() {
document.getElementById("context-menu").style.display = "none";
}
// 右键菜单回调
function handleMenuAction(action, nodeData) {
if (action === "add") {
// 简单示例: 添加子节点
if (!nodeData.children) nodeData.children = [];
const n = (nodeData.children.length || 0) + 1;
nodeData.children.push({ name: `新节点${n}`, weight: "0%" });
renderTree();
} else if (action === "edit") {
// 弹窗简单编辑
const newName = prompt("请输入节点名称:", nodeData.name);
if (newName && newName.trim()) {
nodeData.name = newName.trim();
renderTree();
}
} else if (action === "delete") {
// 删除节点(防止删根)
if (!nodeData.parent) {
alert("根节点不能删除!");
return;
}
const children = nodeData.parent.children;
const idx = children.indexOf(nodeData);
if (idx >= 0) {
children.splice(idx, 1);
renderTree();
}
}
}
// D3渲染
function renderTree() {
const container = d3.select("#tree-container");
container.selectAll("*").remove();
hideContextMenu();
if (!treeData) {
container.append("button")
.attr("type", "button")
.attr("class", "btn btn-primary center-btn")
.text("添加根节点")
.on("click", function() {
treeData = {
name: "根节点",
weight: "100%",
children: []
};
renderTree();
});
return;
}
const width = Math.max(600, window.innerWidth * 0.9);
const height = Math.max(400, window.innerHeight * 0.65);
const svg = container.append("svg")
.attr("width", "100%")
.attr("height", height)
.attr("viewBox", [0, 0, width, height]);
// d3.hierarchy
const root = d3.hierarchy(treeData);
root.each(d => d.id = Math.random().toString(36).slice(2)); // 给每个节点一个唯一id
// 需要保留parent引用以便删除
function setParent(d, parent) {
d.parent = parent;
if (d.children) d.children.forEach(c => setParent(c, d));
}
setParent(treeData, null);
const treeLayout = d3.tree()
.size([width - 80, height - 100])
.separation((a, b) => a.parent === b.parent ? 1.2 : 1.8);
treeLayout(root);
// 连线
svg.append("g")
.selectAll("path")
.data(root.links())
.join("path")
.attr("class", "link")
.attr("d", d3.linkVertical()
.x(d => d.x + 40)
.y(d => d.y)
);
// 节点
const node = svg.append("g")
.selectAll("g")
.data(root.descendants())
.join("g")
.attr("transform", d => `translate(${d.x},${d.y})`);
const rectW = 80, rectH = 40;
// 方框
node.append("rect")
.attr("class", "node-rect")
.attr("width", rectW)
.attr("height", rectH)
.attr("x", 0)
.attr("y", 0)
.on("contextmenu", (event, d) => {
event.preventDefault();
showContextMenu(event.clientX, event.clientY, d.data);
});
// 名称
node.append("text")
.attr("class", "node-text")
.attr("x", rectW / 2)
.attr("y", rectH / 2 - 6)
.text(d => d.data.name);
// 权重
node.append("text")
.attr("class", "node-text")
.attr("x", rectW / 2)
.attr("y", rectH / 2 + 12)
.attr("font-size", "12px")
.attr("fill", "#425466")
.text(d => d.data.weight ? d.data.weight : "");
// 高亮交互
node.style("cursor", "pointer")
.on("mouseover", function() {
d3.select(this).select("rect").attr("stroke", "#2fb344").attr("stroke-width", 3);
})
.on("mouseout", function() {
d3.select(this).select("rect").attr("stroke", "#206bc4").attr("stroke-width", 2);
// 重新初始化所有下拉的搜索TomSelect
document.querySelectorAll('#mapTableBody select').forEach(sel=>{
if(sel.tomselect){ sel.tomselect.destroy(); }
new TomSelect(sel, { create:false, sortField:{field:"text",direction:"asc"} });
});
}
// 点击其它地方关闭菜单
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;
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);
}
}
// 批量切换
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')); });
});
</script>
// 根指标选择(下拉)
document.getElementById('rootSelect').addEventListener('change', (e)=>{
renderTable(e.target.value);
});
// 初始
renderTable('quality');
</script>

View File

@ -73,6 +73,7 @@ public class DataController extends BaseController {
@GetMapping("/list")
@Operation(summary = "导航到数据管理页面", description = "导航到数据管理页面")
public String index(PaginationBean request, Model model) {
setNavigateTitle(model, "/data/list");
List<ModelDefine> modelDefineList = modelDefineService.list(); //查询所有数据模型列表
modelDefineList.sort(Comparator.comparing(ModelDefine::getSortOrder)); //对模型数据列表排序

View File

@ -1,19 +1,35 @@
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.entity.IndicatorBottomCsvMapper;
import com.hshh.indicator.entity.IndicatorBottomFormMapper;
import com.hshh.indicator.entity.IndicatorCsv;
import com.hshh.indicator.entity.IndicatorFormMapper;
import com.hshh.indicator.service.IndicatorBottomCsvMapperService;
import com.hshh.indicator.service.IndicatorBottomFormMapperService;
import com.hshh.indicator.service.IndicatorCsvColumnService;
import com.hshh.indicator.service.IndicatorCsvService;
import com.hshh.indicator.service.IndicatorFromMapperService;
import com.hshh.indicator.service.IndicatorService;
import com.hshh.model.entity.FormFieldConfig;
import com.hshh.model.entity.ModelDefine;
import com.hshh.model.service.FormFieldConfigService;
import com.hshh.model.service.ModelDefineService;
import com.hshh.system.common.bean.BaseController;
import com.hshh.system.common.bean.CheckedBean;
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 io.swagger.v3.oas.annotations.tags.Tag;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
@ -23,7 +39,9 @@ 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.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
/**
* 指标接口服务接口.
@ -37,20 +55,46 @@ import org.springframework.web.bind.annotation.ResponseBody;
public class IndicatorController extends BaseController {
/**
* 指标服务类.
* 指标服务类. 查询所有指标列表
*/
@Resource
private IndicatorService indicatorService;
/**
* 模型定义服务类.
* 模型定义服务类. 查询所有表单
*/
@Resource
private ModelDefineService modelDefineService;
/**
* 评价集服务类.
* 顶级指标对应的form和csv记录服务.
*/
@Resource
private IndicatorEvalItemService indicatorEvalItemService;
private IndicatorFromMapperService indicatorTopMapperService;
/**
* 表单中字段服务类.
*/
@Resource
private FormFieldConfigService formFieldConfigService;
/**
* 顶级指标和csv的对应服务类.
*/
@Resource
private IndicatorCsvService indicatorCsvService;
/**
* 指标csv列服务.
*/
@Resource
private IndicatorCsvColumnService indicatorCsvColumnService;
/**
* 底部指标和表单的的对应服务类.
*/
@Resource
private IndicatorBottomFormMapperService bottomFormMapperService;
/**
* 底部指标和csv的对应关系.
*/
@Resource
private IndicatorBottomCsvMapperService bottomCsvMapperService;
/**
* 导航到列表页面. 获取所有的root指标显示到页面左侧.
@ -61,6 +105,7 @@ public class IndicatorController extends BaseController {
*/
@RequestMapping("/list")
public String list(Model model, Indicator indicator) {
setNavigateTitle(model, "/indicator/list");
List<Indicator> rootList = indicatorService.queryRootList();
if (rootList != null && !rootList.isEmpty()) {
if (indicator.getId() != null) {
@ -179,6 +224,7 @@ public class IndicatorController extends BaseController {
*/
@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) {
@ -227,16 +273,142 @@ public class IndicatorController extends BaseController {
}
/**
* 保存评价集.
* 导航到mapper页面. 1. 顶级指标放入session . 2. 底层指标放入session容器. 3. form表单字段放入session容器 .
* 4.form表单列表放入session容器 5. csv对应session容器 6. csv字段session容器 7. 底部指标对应form字段session容器 8.
* 底部指标对应csv列session容器.
*
* @param item
* @param bindingResult
* @return
* @param model session容器
* @param formId 表单ID
* @param csvId csvID
* @param indicatorTopId 指标顶级ID
* @return /indicator/mapper.html
*/
// @PostMapping("/evalItem/save")
// @ResponseBody
// public OperateResult<IndicatorEvalItem> saveEval(@Valid @RequestBody IndicatorEvalItem item,
// BindingResult bindingResult) {
//
// }
@GetMapping("/mapper")
public String mapper(Model model, final Integer indicatorTopId, final Integer formId,
final Integer csvId) {
setNavigateTitle(model, "/indicator/mapper"); //设置导航
List<Indicator> rootList = indicatorService.queryRootList(); //查询所有根指标
setChecked(rootList, indicatorTopId); //设置根指标的选中状态
model.addAttribute("rootList", rootList); // 1. 顶级指标放入session容器
model.addAttribute("childrenIndicator",
indicatorService.selectNoChildByTopId(indicatorTopId)); // 2. 底层指标放入session容器
//处理form表单
modelForm(model, indicatorTopId, formId); // 3. form表单字段放入session容器.4.form表单列表放入session容器
//处理csv file
csvFile(model, indicatorTopId); // 5. csv对应session容器 6. csv字段session容器
//放入底部指标和form表单字段对应关系到容器
List<IndicatorBottomFormMapper> bottomList = bottomFormMapperService.queryListByIndicatorId(
indicatorTopId);
Map<Integer, IndicatorBottomFormMapper> bottomFormMap = bottomList.stream()
.collect(Collectors.toMap(IndicatorBottomFormMapper::getFormFieldId, a -> a));
model.addAttribute("bottomFormMap", bottomFormMap); // 7. 底部指标对应form字段session容器
//放入底部指标和csv列对应关系到容器
List<IndicatorBottomCsvMapper> bottomCsvColumnList = bottomCsvMapperService.queryListByIndicatorTopId(
indicatorTopId);
Map<Integer, IndicatorBottomCsvMapper> bottomCsvColumnMap = bottomCsvColumnList.stream()
.collect(Collectors.toMap(IndicatorBottomCsvMapper::getCsvColumnId, a -> a));
model.addAttribute("bottomCsvColumnMap", bottomCsvColumnMap); // 8.底部指标对应csv列session容器
return "/indicator/mapper";
}
private void modelForm(Model model, final Integer indicatorTopId, Integer formId) {
//表单相关
List<IndicatorFormMapper> mapperList = indicatorTopMapperService.selectModelAndCsvNameByIndicator(
indicatorTopId); //查看form和顶指标映射关系
//查询form表单字段
if (formId != null) {
model.addAttribute("formFieldList",
formFieldConfigService.getFormFieldConfigByModelId(formId));//3.form表单字段放入session容器
} else if (!mapperList.isEmpty()) {
List<FormFieldConfig> formFieldList = formFieldConfigService.getFormFieldConfigByModelId(
mapperList.get(0).getIndicatorModelId());
model.addAttribute("formFieldList", formFieldList); //3.form表单字段放入session容器
}
List<ModelDefine> modelList = modelDefineService.list(); //查询所有form表单列表
setChecked(modelList, mapperList.isEmpty() ? null
: (formId != null ? formId : mapperList.get(0).getIndicatorModelId())); //设置form表单列表的选中状态
model.addAttribute("modelList", modelList); //4.所有form表单放入session容器
}
private void csvFile(Model model, final Integer id) {
IndicatorCsv csvMapper = indicatorCsvService.selectByIndicatorId(id); //5.csv对应放入session
model.addAttribute("csvMapper", csvMapper);
//查询csv列的列表
model.addAttribute("csvColumns",
indicatorCsvColumnService.listByIdOrderByColumn(csvMapper == null ? 0 : csvMapper.getId()));
}
/**
* 下载上一次的csv模板.
*
* @param id csv映射ID
* @param response 响应
* @throws IOException 异常
*/
@GetMapping("/download/{id}")
public void downloadFile(@PathVariable Integer id, HttpServletResponse response)
throws IOException {
IndicatorCsv csvFile = indicatorCsvService.getById(id);
// 设置响应头
response.setContentType("text/csv; charset=UTF-8");
response.setHeader("Content-Disposition",
"attachment; filename=\"" + java.net.URLEncoder.encode(csvFile.getCsvName(),
StandardCharsets.UTF_8) + "\"");
// 写入文件流
try (OutputStream os = response.getOutputStream()) {
os.write(csvFile.getCsvData());
os.flush();
}
}
/**
* 上传csv文件.记录顶级指标和文件及表单id的映射关系记录在表m_data_indicator_top_mapper.
*
* @param file 文件
* @param indicatorTopId 指标
* @param formId modelID
* @return 操作结果
*/
@PostMapping("/uploadCsvAndSetTopMapper")
public OperateResult<Void> uploadCsvAndSetTopMapper(@RequestParam("file") MultipartFile file,
Integer indicatorTopId, Integer formId) {
indicatorService.saveIndicatorTopCsvMapper(indicatorTopId, formId, file);
return OperateResult.success();
}
/**
* 保存指标和form表单ID的映射关系.
*
* @param indicatorTopId 指标顶级ID
* @param formId 表单ID
* @return 操作结果
*/
@PostMapping("/indicatorFormMapper")
public OperateResult<Void> saveIndicatorFormMapper(Integer indicatorTopId, Integer formId) {
indicatorTopMapperService.saveFormMapper(indicatorTopId, formId);
return OperateResult.success();
}
private <T extends CheckedBean> void setChecked(List<T> list, Integer id) {
if (list != null && !list.isEmpty()) {
if (id == null || id.equals(0)) {
list.get(0).setChecked(true);
return;
}
for (T bean : list) {
if (bean.getId().equals(id)) {
bean.setChecked(true);
}
}
}
}
}

View File

@ -1,18 +0,0 @@
package com.hshh.indicator.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.stereotype.Controller;
/**
* <p>
* 指标规则表 前端控制器
* </p>
*
* @author liDongYu
* @since 2025-08-04
*/
@Controller
@RequestMapping("/indicator/indicatorEvalItem")
public class IndicatorEvalItemController {
}

View File

@ -4,6 +4,7 @@ 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.CheckedBean;
import java.io.Serializable;
import java.util.List;
import javax.validation.constraints.NotBlank;
@ -20,7 +21,7 @@ import lombok.Data;
*/
@TableName("m_data_indicator")
@Data
public class Indicator implements Serializable {
public class Indicator extends CheckedBean {
private static final long serialVersionUID = 1L;
@ -39,9 +40,8 @@ public class Indicator implements Serializable {
private Integer topId; //顶级父ID
private Integer sortOrder;
@TableField(exist = false)
private boolean checked;
private Integer modelId;
}

View File

@ -3,6 +3,7 @@ package com.hshh.indicator.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.hshh.indicator.entity.Indicator;
import java.util.List;
import org.springframework.web.multipart.MultipartFile;
/**
* 指标表 服务类.
@ -53,4 +54,13 @@ public interface IndicatorService extends IService<Indicator> {
* @return 指标集
*/
List<Indicator> selectNoChildByTopId(Integer topId);
/**
* 保存顶级映射关系记录对应的表单ID和csv信息.
*
* @param topId 顶级指标ID
* @param modelId 表单ID
* @param file csv文件
*/
void saveIndicatorTopCsvMapper(Integer topId, Integer modelId, MultipartFile file);
}

View File

@ -10,6 +10,8 @@ import java.util.Map;
import java.util.stream.Collectors;
import javax.validation.constraints.NotNull;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
/**
* <p>
@ -82,4 +84,19 @@ public class IndicatorServiceImpl extends ServiceImpl<IndicatorMapper, Indicator
public List<Indicator> selectNoChildByTopId(Integer topId) {
return this.baseMapper.selectNoChildByTopId(topId);
}
@Transactional(rollbackFor = Exception.class)
@Override
public void saveIndicatorTopCsvMapper(Integer topId, Integer modelId, MultipartFile file) {
//删除表中m_data_indicator_csv原来topID对应记录
//添加m_data_indicator_csv记录
//删除m_data_indicator_csv_column 原topID对应的记录
//添加对应记录
}
}

View File

@ -1,18 +0,0 @@
package com.hshh.model.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.stereotype.Controller;
/**
* <p>
* 动态表单实际对象记录表 前端控制器
* </p>
*
* @author liDongYu
* @since 2025-08-01
*/
@Controller
@RequestMapping("/model/formValue")
public class FormValueController {
}

View File

@ -82,6 +82,7 @@ public class ModelDefineController extends BaseController {
@Operation(summary = "导航到数据模型定义页面", description = "导航到数据模型定义页面")
@GetMapping("/list")
public String list(ModelDefine modelDefine, Model model) {
setNavigateTitle(model, "/model/list");
List<ModelDefine> modelList = modelDefineService.list();
List<FormFieldConfig> fieldList = new ArrayList<>();
if (modelList != null) {

View File

@ -4,6 +4,7 @@ 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.CheckedBean;
import java.io.Serializable;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
@ -20,7 +21,7 @@ import lombok.Data;
*/
@TableName("m_data_model_define")
@Data
public class ModelDefine implements Serializable {
public class ModelDefine extends CheckedBean {
private static final long serialVersionUID = 1L;
@ -37,8 +38,7 @@ public class ModelDefine implements Serializable {
private String modelName;
@TableField(exist = false)
private boolean checked;
private Integer sortOrder;
/**
* 每行显示几个字段.

View File

@ -23,6 +23,7 @@ public class FormFieldConfigServiceImpl extends
public List<FormFieldConfig> getFormFieldConfigByModelId(Integer modelId) {
QueryWrapper<FormFieldConfig> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("data_model_id", modelId);
queryWrapper.orderByAsc("sort_order");
return this.list(queryWrapper);
}

View File

@ -2,14 +2,7 @@
<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>
</ol>
</nav>
<div th:replace="fragments/dialog::navigateDialog(${chainMenuList})"></div>
<div class="row">
<div class="card">
<div class="card-header">

View File

@ -184,4 +184,16 @@
</form>
</div>
</div>
<div th:fragment="navigateDialog(naviList)" id="navigateDialog">
<!-- 面包屑导航 -->
<nav aria-label="breadcrumb" class="mb-4">
<ol class="breadcrumb">
<li class="breadcrumb-item" th:each="m:${naviList}">
<a href="#" th:text="${m.title}">系统管理</a>
</li>
</ol>
</nav>
</div>

View File

@ -4,13 +4,7 @@
<input type="hidden" name="parentId" id="parentId" >
<input type="hidden" name="topId" id="topId" >
<div class="mb-3" th:if="${modelDefineList != null}">
<label class="form-label required">基础设施:</label>
<select class="form-select" name="modelId" id="modelId" >
<option th:each="item:${modelDefineList}" th:value="${item.id}" th:text="${item.getModelName()}"></option>
</select>
<div class="invalid-feedback" id="modelId_error_tip"></div>
</div>
<div class="mb-3">
<label class="form-label required">指标名称:</label>
<input type="text" class="form-control" name="name" id="name"

View File

@ -11,14 +11,7 @@
</style>
<div class="page-body" data-page="metric">
<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 th:replace="fragments/dialog::navigateDialog(${chainMenuList})"></div>
<div class="row row-cards">
<div class="card">
<div class="card-header">
@ -35,13 +28,16 @@
</label>
</div>
<!-- 新增:上次上传文件 -->
<div class="text-muted small mt-2">说明: 切换不同的指标可以设置当前指标子项的评价标准</div>
</div>
<div class="row">
<div class="col-md-3">
<div class="col-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">指标子集</h3>
<h3 class="card-title" th:each="item : ${rootList}"
th:if="${item.checked == true}">[[${item.name}]]子集</h3>
</div>
<div class="list-group list-group-flush overflow-auto" style="max-height: 35rem">
<div class="list-group-item" th:each="item:${indicatorListWithoutChildren}">

View File

@ -128,14 +128,7 @@
<div class="page-body" data-page="metric">
<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 th:replace="fragments/dialog::navigateDialog(${chainMenuList})"></div>
<div class="row g-0 mb-4">
<div class="col-md-3" style="min-height: 40em">
<div class="card mb-4">

View File

@ -0,0 +1,246 @@
<!-- ====== 页面主体(纯静态,无 JS/Thymeleaf ====== -->
<div class="page-body" data-page="mapper">
<div class="container-xl">
<!-- 面包屑(静态示例) -->
<div th:replace="fragments/dialog::navigateDialog(${chainMenuList})"></div>
<div class="row row-cards">
<div class="card">
<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">
<!-- 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 for="indicationId" class="form-label m-0">根指标</label>
</div>
<div class="col-3">
<select id="indicationId" class="form-select" name="indicationId">
<option value="0">-选择根指标-</option>
<option th:each="item:${rootList}" th:value="${item.id}"
th:text="${item.getName()}" th:selected="${item.checked}"></option>
</select>
</div>
</div>
</div>
</div>
<!-- 1. 根指标(静态下拉) -->
<div class="card mb-3">
<div class="card-header">
<div class="card-title">2. 选择form表单</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>
</div>
<div class="col-3">
<select id="modelId" class="form-select" onchange="formModelChange(this)">
<option value="0">-选择form表单-</option>
<option th:each="item:${modelList}" th:value="${item.id}"
th:text="${item.getModelName()}" th:selected="${item.checked}"></option>
</select>
</div>
</div>
</div>
</div>
<!-- 2. CSV 表头(静态 50 列) -->
<div class="card mb-3">
<div class="card-header">
<div class="card-title">3. 上传 CSV 文件</div>
</div>
<div class="card-body">
<input type="file" class="form-control mb-3" accept=".csv">
<!-- 新增:上次上传文件 -->
<div class="mb-2" th:if="${csvMapper!=null}">
<span class="fw-bold">上次上传文件:</span>
<a th:href="@{/indicator/download/{id}(id=${csvMapper.getId()})" target="_blank"
th:text="${csvMapper.getCsvName()}"></a>
</div>
</div>
</div>
<!-- 3. 子指标映射(静态 20 行;首两列粘左;自动编号) -->
<div class="card">
<div class="card-header">
<div class="card-title">4. 子指标映射设置</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 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;">CSV 表头</th>
</tr>
</thead>
<!-- 自动编号 tbody不用写数字CSS 计数器会生成 1..20 -->
<tbody class="autonum">
<!-- 02~20同上复制即可编号与名称尾号自动增长 -->
<tr th:each="item:${childrenIndicator}">
<td class="sticky-body-col-1 col-index"></td>
<td class="sticky-body-col-2 col-name" th:text="${item.getName()}"></td>
<td><select class="form-select form-select-sm">
<option>— 选择表单字段 —</option>
<option th:if="${formFieldList.size()>0}"
th:each="field:${formFieldList}" th:value="${field.id}"
th:text="${field.getFieldName()}"></option>
>
</option>
</select></td>
<td><select class="form-select form-select-sm">
<option>— 选择 CSV 表头 —</option>
<option th:if="${csvColumns.size()>0}" th:each="column:${csvColumns}"
th:value="${column.id}"
th:text="${column.getCsvColumnName()}"></option>
>
</option>
</select></td>
</tr>
</tbody>
</table>
</div>
<div class="text-muted small mt-2">说明选择子指标对应表单字段或者CSV某列</div>
</div>
</div>
</div><!-- /card-body -->
</div>
</div>
</div>
</div>
<!-- ====== 样式(关键:表头与表体分层;首两列分别粘左) ====== -->
<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>
function formModelChange(object) {
document.getElementById("_indicator_mapper").setAttribute("hx-vals",
JSON.stringify({indicatorTopId: $('#indicationId').val(), formId: object.value}))
document.getElementById("_indicator_mapper").click();
}
</script>

View File

@ -2,14 +2,7 @@
<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>
</ol>
</nav>
<div th:replace="fragments/dialog::navigateDialog(${chainMenuList})"></div>
<div class="row g-4 mb-4">
<!-- 左侧字典类型 -->
<div class="col-md-3">

View File

@ -2,14 +2,7 @@
<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 th:replace="fragments/dialog::navigateDialog(${chainMenuList})"></div>
<div class="row g-4 mb-4">
<!-- 左侧字典类型 -->
<div class="col-md-5">

View File

@ -2,14 +2,7 @@
<div class="page-body">
<div class="container-xl" data-page="menu-list">
<!-- 面包屑导航 -->
<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 th:replace="fragments/dialog::navigateDialog(${chainMenuList})"></div>
<div class="row g-4 mb-4">
<div class="card">
<div class="card-header">

View File

@ -2,14 +2,7 @@
<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 th:replace="fragments/dialog::navigateDialog(${chainMenuList})"></div>
<div class="row g-4 mb-4">
<div class="col-12">

View File

@ -2,14 +2,7 @@
<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 th:replace="fragments/dialog::navigateDialog(${chainMenuList})"></div>
<div class="row g-4 mb-4">
<div class="col-12">
<div class="card">

View File

@ -2,14 +2,7 @@
<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 th:replace="fragments/dialog::navigateDialog(${chainMenuList})"></div>
<div class="row g-4 mb-4">
<div class="col-12">

View File

@ -14,6 +14,7 @@ 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;
@ -46,6 +47,7 @@ public class DictItemController extends BaseController {
@Operation(summary = "获取字典条目增加页面内容", description = "用户点击新增字典条目时执行")
@GetMapping("/add")
public String add() {
return "system/dict/add_dict_item";
}

View File

@ -58,6 +58,9 @@ public class DictTypeController extends BaseController {
@GetMapping("/")
@Operation(summary = "导航到列表页面", description = "用户点击菜单时,执行")
public String index(Model model, DictType type) {
setNavigateTitle(model, "/base/dict/");
List<DictType> dictList = dictTypeService.list();
dictList.sort(Comparator.comparingInt(DictType::getSortOrder));

View File

@ -19,6 +19,7 @@ import javax.validation.Valid;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
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;
@ -53,7 +54,9 @@ public class MenuController extends BaseController {
*/
@GetMapping("/")
@Operation(summary = "菜单首页", description = "导航到默认页")
public String list() {
public String list(Model model) {
setNavigateTitle(model, "/base/menu/");
return "system/menu/list";
}

View File

@ -55,6 +55,7 @@ public class PermissionController extends BaseController {
@GetMapping("/")
@Operation(summary = "权限首页", description = "导航到默认页")
public String list(PaginationBean request, Model model) {
setNavigateTitle(model, "/base/permission/");
List<Permissions> list = permissionsService.list(request);
Long total = permissionsService.count(request);
//设置分页信息

View File

@ -55,6 +55,7 @@ public class RoleController extends BaseController {
@GetMapping("/")
@Operation(summary = "角色首页", description = "导航到默认页")
public String list(PaginationBean request, Model model) {
setNavigateTitle(model, "/base/role/");
List<Roles> list = rolesService.list(request);
Long total = rolesService.count(request);
//设置分页信息

View File

@ -1,18 +0,0 @@
package com.hshh.system.base.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.stereotype.Controller;
/**
* <p>
* 前端控制器
* </p>
*
* @author liDongYu
* @since 2025-07-29
*/
@Controller
@RequestMapping("/base/tableRelations")
public class TableRelationsController {
}

View File

@ -66,6 +66,7 @@ public class UserController extends BaseController {
@GetMapping("/")
@Operation(summary = "用户首页", description = "导航到默认页")
public String list(PaginationBean request, Model model) {
setNavigateTitle(model, "/base/user/");
List<Users> list = usersService.list(request);
Long total = usersService.count(request);
//设置分页信息

View File

@ -42,4 +42,12 @@ public interface MenusService extends IService<Menus> {
* @return 菜单列表
*/
List<Menus> queryListByPid(Integer parentId);
/**
* 根据菜单地址获取菜单对象. 比如菜单A,父亲时B,B的父亲是C.则返回列表 C,B,A.
*
* @param href 菜单连接地址
* @return 菜单对象列表从父-...-孩子
*/
List<Menus> getMenus(String href);
}

View File

@ -6,6 +6,7 @@ import com.hshh.system.base.entity.Menus;
import com.hshh.system.base.mapper.MenusMapper;
import com.hshh.system.base.service.MenusService;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@ -125,4 +126,38 @@ public class MenusServiceImpl extends ServiceImpl<MenusMapper, Menus> implements
queryWrapper.eq("parent_id", parentId);
return this.list(queryWrapper);
}
@Override
public List<Menus> getMenus(String href) {
List<Menus> resultList = new ArrayList<>();
List<Menus> menuList = this.list();
menuList.forEach(a -> {
if (a.getParentId() == null) {
a.setParentId(0);
}
});
//用key=path
Map<String, Menus> keyIsPathMenusMap = menuList.stream().filter(a -> a.getPath() != null)
.collect(Collectors.toMap(Menus::getPath, a -> a));
Menus menus = keyIsPathMenusMap.get(href);
if (menus == null) {
return resultList;
}
resultList.add(menus);
//key=id
Map<Integer, Menus> idMap = menuList.stream()
.collect(Collectors.toMap(Menus::getId, a -> a));
addParent(resultList, menus.getParentId(), idMap);
//反转
Collections.reverse(resultList);
return resultList;
}
private void addParent(List<Menus> resultList, Integer parentId, Map<Integer, Menus> allMenuMap) {
if (parentId != null && allMenuMap.get(parentId) != null) {
resultList.add(allMenuMap.get(parentId));
addParent(resultList, allMenuMap.get(parentId).getParentId(), allMenuMap);
}
}
}

View File

@ -6,7 +6,7 @@ import lombok.Getter;
import lombok.Setter;
/**
* 分页基础参数
* 分页基础参数.
*/
public class BaseBean implements Serializable {

View File

@ -1,6 +1,8 @@
package com.hshh.system.common.bean;
import com.hshh.system.base.entity.Menus;
import com.hshh.system.base.service.impl.MenusServiceImpl;
import java.util.ArrayList;
import java.util.List;
import org.springframework.ui.Model;
@ -45,4 +47,15 @@ public class BaseController {
model.addAttribute("condition", request);
}
/**
* 设置页面上的导航信息.
*
* @param model session容器.
* @param href 页面地址
*/
protected void setNavigateTitle(Model model, String href) {
MenusServiceImpl menuService = SpringContextHolder.getBean(MenusServiceImpl.class);
List<Menus> menuList = menuService.getMenus(href);
model.addAttribute("chainMenuList", menuList);
}
}

View File

@ -0,0 +1,18 @@
package com.hshh.system.common.bean;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
/**
* 标记checked.
*
* @author LiDongYU
* @since 2025/7/22
*/
@Data
public class CheckedBean {
@TableField(exist = false)
private boolean checked;
private Integer id;
}

View File

@ -29,7 +29,7 @@ public class CodeGenerator {
basePath + "/src/main/resources/mapper")); // 设置mapperXml生成路径
})
.strategyConfig(builder -> {
builder.addInclude("m_data_indicator_eval_item") // 设置需要生成的表名多个用逗号分隔
builder.addInclude("m_data_indicator_bottom_csv_mapper") // 设置需要生成的表名多个用逗号分隔
.addTablePrefix("m_data_"); // 设置过滤表前缀
})
.execute();