diff --git a/klp-admin/src/main/java/com/klp/framework/client/SqlServerApiClient.java b/klp-admin/src/main/java/com/klp/framework/client/SqlServerApiClient.java new file mode 100644 index 00000000..2a4b39a5 --- /dev/null +++ b/klp-admin/src/main/java/com/klp/framework/client/SqlServerApiClient.java @@ -0,0 +1,411 @@ +package com.klp.framework.client; + +import com.klp.framework.config.SqlServerApiProperties; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import java.net.URI; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * sql-server-api 统一调用客户端。 + *

+ * 对齐 {@code sql-server-api/app.py} 的接口规范: + *

+ */ +@Component +public class SqlServerApiClient { + + private final RestTemplate restTemplate; + private final SqlServerApiProperties properties; + + public SqlServerApiClient(RestTemplate sqlServerApiRestTemplate, SqlServerApiProperties properties) { + this.restTemplate = sqlServerApiRestTemplate; + this.properties = properties; + } + + public String getBaseUrl() { + return normalizeBaseUrl(); + } + + public Map health() { + return getForMap("/health"); + } + + public Map heartbeat() { + return getForMap("/heartbeat"); + } + + public TableSchemaResponse tableSchema(String tableType, String tableName) { + return postForObject("/table-schema", new TableSchemaRequest(tableType, tableName), TableSchemaResponse.class); + } + + public ExecuteSqlResponse executeSql(String tableType, String sql, Map params) { + return postForObject("/execute-sql", new ExecuteSqlRequest(tableType, sql, params), ExecuteSqlResponse.class); + } + + public T get(String path, Class responseType) { + return restTemplate.getForObject(buildUri(path), responseType); + } + + public T get(String path, Map queryParams, Class responseType) { + URI uri = UriComponentsBuilder.fromHttpUrl(normalizeBaseUrl()) + .path(normalizePath(path)) + .queryParams(convertToQueryParams(queryParams)) + .build(true) + .toUri(); + return restTemplate.getForObject(uri, responseType); + } + + public T post(String path, R requestBody, Class responseType) { + return restTemplate.postForObject(buildUri(path), requestBody, responseType); + } + + public static class TableSchemaRequest { + private String tableType; + private String tableName; + + public TableSchemaRequest() { + } + + public TableSchemaRequest(String tableType, String tableName) { + this.tableType = tableType; + this.tableName = tableName; + } + + public String getTableType() { + return tableType; + } + + public void setTableType(String tableType) { + this.tableType = tableType; + } + + public String getTableName() { + return tableName; + } + + public void setTableName(String tableName) { + this.tableName = tableName; + } + } + + public static class ExecuteSqlRequest { + private String tableType; + private String sql; + private Map params; + + public ExecuteSqlRequest() { + } + + public ExecuteSqlRequest(String tableType, String sql, Map params) { + this.tableType = tableType; + this.sql = sql; + this.params = params; + } + + public String getTableType() { + return tableType; + } + + public void setTableType(String tableType) { + this.tableType = tableType; + } + + public String getSql() { + return sql; + } + + public void setSql(String sql) { + this.sql = sql; + } + + public Map getParams() { + return params; + } + + public void setParams(Map params) { + this.params = params; + } + } + + public static class TableSchemaResponse { + private String tableType; + private String tableName; + private Schema schema; + + public String getTableType() { + return tableType; + } + + public void setTableType(String tableType) { + this.tableType = tableType; + } + + public String getTableName() { + return tableName; + } + + public void setTableName(String tableName) { + this.tableName = tableName; + } + + public Schema getSchema() { + return schema; + } + + public void setSchema(Schema schema) { + this.schema = schema; + } + + public static class Schema { + private List columns = Collections.emptyList(); + + public List getColumns() { + return columns; + } + + public void setColumns(List columns) { + this.columns = columns; + } + } + + public static class Column { + private String name; + private String type; + private boolean nullable; + private Object defaultValue; + private boolean primaryKey; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public boolean isNullable() { + return nullable; + } + + public void setNullable(boolean nullable) { + this.nullable = nullable; + } + + public Object getDefaultValue() { + return defaultValue; + } + + public void setDefaultValue(Object defaultValue) { + this.defaultValue = defaultValue; + } + + public boolean isPrimaryKey() { + return primaryKey; + } + + public void setPrimaryKey(boolean primaryKey) { + this.primaryKey = primaryKey; + } + } + } + + public static class ExecuteSqlResponse { + private String tableType; + private String resultType; + private Integer rowCount; + private Object result; + + public String getTableType() { + return tableType; + } + + public void setTableType(String tableType) { + this.tableType = tableType; + } + + public String getResultType() { + return resultType; + } + + public void setResultType(String resultType) { + this.resultType = resultType; + } + + public Integer getRowCount() { + return rowCount; + } + + public void setRowCount(Integer rowCount) { + this.rowCount = rowCount; + } + + public Object getResult() { + return result; + } + + public void setResult(Object result) { + this.result = result; + } + } + + private Map getForMap(String path) { + ResponseEntity responseEntity = restTemplate.exchange(buildUri(path), HttpMethod.GET, HttpEntity.EMPTY, Map.class); + Map body = responseEntity.getBody(); + return body == null ? Collections.emptyMap() : body; + } + + private T postForObject(String path, Object requestBody, Class responseType) { + return restTemplate.postForObject(buildUri(path), requestBody, responseType); + } + + private URI buildUri(String path) { + return UriComponentsBuilder.fromHttpUrl(normalizeBaseUrl()) + .path(normalizePath(path)) + .build(true) + .toUri(); + } + + private String normalizeBaseUrl() { + if (properties.getBaseUrl() != null && properties.getBaseUrl().trim().length() > 0) { + return properties.getBaseUrl(); + } + if (properties.getHost() == null || properties.getHost().trim().length() == 0 || properties.getPort() == null) { + throw new IllegalStateException("sql-server-api 配置缺失,请检查 host/port/base-url"); + } + return "http://" + properties.getHost() + ":" + properties.getPort(); + } + + private String normalizePath(String path) { + if (path == null || path.trim().length() == 0) { + return ""; + } + return path.startsWith("/") ? path : "/" + path; + } + + public ExecuteSqlResponse queryPlanByCoilId(String coilId) { + return executeSql( + "oracle", + "select * from JXPLTCM.PLTCM_PDI_PLAN where COILID = :coilId", + singletonParam("coilId", coilId) + ); + } + + public ExecuteSqlResponse queryPlanList() { + return executeSql( + "oracle", + "select * from JXPLTCM.PLTCM_PDI_PLAN order by INSDATE desc", + emptyParams() + ); + } + + public ExecuteSqlResponse queryPlanListByProcessCode(String processCode) { + return executeSql( + "oracle", + "select * from JXPLTCM.PLTCM_PDI_PLAN where PROCESS_CODE = :processCode order by INSDATE desc", + singletonParam("processCode", processCode) + ); + } + + public ExecuteSqlResponse queryPlanListByStatus(String status) { + return executeSql( + "oracle", + "select * from JXPLTCM.PLTCM_PDI_PLAN where STATUS = :status order by INSDATE desc", + singletonParam("status", status) + ); + } + + public ExecuteSqlResponse queryPlanByHotCoilId(String hotCoilId) { + return executeSql( + "oracle", + "select * from JXPLTCM.PLTCM_PDI_PLAN where HOT_COILID = :hotCoilId", + singletonParam("hotCoilId", hotCoilId) + ); + } + + public ExecuteSqlResponse queryProSegByEncoilId(String encoilId) { + return executeSql( + "oracle", + "select * from JXPLTCM.PLTCM_PRO_SEG where ENCOILID = :encoilId order by SEGNO", + singletonParam("encoilId", encoilId) + ); + } + + public ExecuteSqlResponse queryProSegByExcoilId(String excoilId) { + return executeSql( + "oracle", + "select * from JXPLTCM.PLTCM_PRO_SEG where EXCOILID = :excoilId order by SEGNO", + singletonParam("excoilId", excoilId) + ); + } + + public ExecuteSqlResponse queryQualityByExcoilId(String excoilId) { + return executeSql( + "oracle", + "select * from JXPLTCM.V_PLTCM_PDO_QUALITY where EXCOILID = :excoilId order by END_DATE desc", + singletonParam("excoilId", excoilId) + ); + } + + public ExecuteSqlResponse queryGaugeByMatId(String matId) { + return executeSql( + "oracle", + "select * from JXPLTCM.V_VBDA_GAUGE where MATID = :matId order by XTIME asc", + singletonParam("matId", matId) + ); + } + + public ExecuteSqlResponse queryShapeByMatId(String matId) { + return executeSql( + "oracle", + "select * from JXPLTCM.V_VBDA_SHAPE where MATID = :matId order by XTIME asc", + singletonParam("matId", matId) + ); + } + + private org.springframework.util.MultiValueMap convertToQueryParams(Map queryParams) { + org.springframework.util.LinkedMultiValueMap multiValueMap = new org.springframework.util.LinkedMultiValueMap<>(); + if (queryParams == null || queryParams.isEmpty()) { + return multiValueMap; + } + for (Map.Entry entry : queryParams.entrySet()) { + Object value = entry.getValue(); + if (value != null) { + multiValueMap.add(entry.getKey(), String.valueOf(value)); + } + } + return multiValueMap; + } + + private Map singletonParam(String key, Object value) { + java.util.HashMap params = new java.util.HashMap(); + if (value != null) { + params.put(key, value); + } + return params; + } + + private Map emptyParams() { + return new java.util.HashMap(); + } +} diff --git a/klp-admin/src/main/java/com/klp/framework/config/SqlServerApiClientConfig.java b/klp-admin/src/main/java/com/klp/framework/config/SqlServerApiClientConfig.java new file mode 100644 index 00000000..b6f520b2 --- /dev/null +++ b/klp-admin/src/main/java/com/klp/framework/config/SqlServerApiClientConfig.java @@ -0,0 +1,23 @@ +package com.klp.framework.config; + +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +import java.time.Duration; + +/** + * sql-server-api HTTP 客户端配置 + */ +@Configuration +public class SqlServerApiClientConfig { + + @Bean + public RestTemplate sqlServerApiRestTemplate(RestTemplateBuilder builder) { + return builder + .setConnectTimeout(Duration.ofSeconds(5)) + .setReadTimeout(Duration.ofSeconds(30)) + .build(); + } +} diff --git a/klp-admin/src/main/java/com/klp/framework/config/SqlServerApiProperties.java b/klp-admin/src/main/java/com/klp/framework/config/SqlServerApiProperties.java new file mode 100644 index 00000000..6d995192 --- /dev/null +++ b/klp-admin/src/main/java/com/klp/framework/config/SqlServerApiProperties.java @@ -0,0 +1,51 @@ +package com.klp.framework.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * sql-server-api 中间件配置 + */ +@Component +@ConfigurationProperties(prefix = "sql-server-api") +public class SqlServerApiProperties { + + /** + * 请求地址 + */ + private String host; + + /** + * 请求端口 + */ + private Integer port; + + /** + * 基础访问地址 + */ + private String baseUrl; + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public Integer getPort() { + return port; + } + + public void setPort(Integer port) { + this.port = port; + } + + public String getBaseUrl() { + return baseUrl; + } + + public void setBaseUrl(String baseUrl) { + this.baseUrl = baseUrl; + } +} diff --git a/klp-admin/src/main/java/com/klp/framework/service/SqlServerApiBusinessService.java b/klp-admin/src/main/java/com/klp/framework/service/SqlServerApiBusinessService.java new file mode 100644 index 00000000..141ad459 --- /dev/null +++ b/klp-admin/src/main/java/com/klp/framework/service/SqlServerApiBusinessService.java @@ -0,0 +1,306 @@ +package com.klp.framework.service; + +import com.klp.framework.client.SqlServerApiClient; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 面向业务场景的 sql-server-api 查询服务。 + *

+ * 当前抽象为三个功能: + *

    + *
  1. 计划查询
  2. + *
  3. 钢卷实际 / SEG 查询
  4. + *
  5. 实时数据(Gauge / Shape)查询
  6. + *
+ */ +@Service +public class SqlServerApiBusinessService { + + private final SqlServerApiClient client; + + public SqlServerApiBusinessService(SqlServerApiClient client) { + this.client = client; + } + + /** + * 计划列表:查询所有计划,按时间倒序。 + *

+ * 这是后续按 coilId 关联 SEG、实时数据的入口。 + */ + public PlanListView getPlanList() { + return PlanListView.fromExecuteSqlResponse(client.queryPlanList()); + } + + /** + * 计划列表:按工艺流程编码筛选。 + */ + public PlanListView getPlanListByProcessCode(String processCode) { + return PlanListView.fromExecuteSqlResponse(client.queryPlanListByProcessCode(processCode)); + } + + /** + * 计划列表:按状态筛选。 + */ + public PlanListView getPlanListByStatus(String status) { + return PlanListView.fromExecuteSqlResponse(client.queryPlanListByStatus(status)); + } + + /** + * 计划详情:按成品卷号查询单条计划。 + */ + public PlanDetailView getPlanByCoilId(String coilId) { + return PlanDetailView.fromExecuteSqlResponse(coilId, client.queryPlanByCoilId(coilId)); + } + + /** + * 计划详情:按热卷号查询。 + */ + public PlanDetailView getPlanByHotCoilId(String hotCoilId) { + return PlanDetailView.fromExecuteSqlResponse(hotCoilId, client.queryPlanByHotCoilId(hotCoilId)); + } + + /** + * 钢卷实际 SEG 查询:按入口卷号查询。 + *

+ * 返回的是按 SEGNO 排序后的原始行数据。 + */ + public SegSeriesView getSegByEncoilId(String encoilId) { + return SegSeriesView.fromExecuteSqlResponse(encoilId, client.queryProSegByEncoilId(encoilId)); + } + + /** + * 钢卷实际 SEG 查询:按出口卷号查询。 + */ + public SegSeriesView getSegByExcoilId(String excoilId) { + return SegSeriesView.fromExecuteSqlResponse(excoilId, client.queryProSegByExcoilId(excoilId)); + } + + /** + * 钢卷 SEG 视图数据:把同一钢卷下按 SEGNO 排序的多行数据,整理成“一个钢卷 id + 一个属性对应一个列表”的结构。 + *

+ * 例如:厚度、张力、速度都会被整理成数组,方便前端直接画表或曲线。 + */ + public SegSeriesView getSegSeriesViewByEncoilId(String encoilId) { + return getSegByEncoilId(encoilId); + } + + /** + * 实时数据:厚度/仪表数据。 + */ + public SqlServerApiClient.ExecuteSqlResponse getRealtimeGaugeByMatId(String matId) { + return client.queryGaugeByMatId(matId); + } + + /** + * 实时数据:板形/形状数据。 + */ + public SqlServerApiClient.ExecuteSqlResponse getRealtimeShapeByMatId(String matId) { + return client.queryShapeByMatId(matId); + } + + /** + * 实时数据总入口:一次性返回厚度与板形两类数据。 + */ + public RealtimeDataBundle getRealtimeData(String matId) { + return new RealtimeDataBundle( + getRealtimeGaugeByMatId(matId), + getRealtimeShapeByMatId(matId) + ); + } + + private static PlanListView toPlanListView(SqlServerApiClient.ExecuteSqlResponse response) { + return PlanListView.fromExecuteSqlResponse(response); + } + + private static List> asRowList(SqlServerApiClient.ExecuteSqlResponse response) { + if (response == null || response.getResult() == null) { + return Collections.emptyList(); + } + Object result = response.getResult(); + if (result instanceof List) { + List list = (List) result; + List> rows = new ArrayList>(); + for (Object item : list) { + if (item instanceof Map) { + rows.add((Map) item); + } + } + return rows; + } + return Collections.emptyList(); + } + + private static Number asNumber(Object value) { + if (value instanceof Number) { + return (Number) value; + } + if (value == null) { + return null; + } + try { + return new BigDecimal(String.valueOf(value)); + } catch (Exception ex) { + return null; + } + } + + private static String asString(Object value) { + return value == null ? null : String.valueOf(value); + } + + public static class PlanListView { + private final List coilIds; + private final List> rows; + + public PlanListView(List coilIds, List> rows) { + this.coilIds = coilIds; + this.rows = rows; + } + + public List getCoilIds() { + return coilIds; + } + + public List> getRows() { + return rows; + } + + public static PlanListView fromExecuteSqlResponse(SqlServerApiClient.ExecuteSqlResponse response) { + List> rows = asRowList(response); + List coilIds = rows.stream() + .map(row -> asString(row.get("COILID"))) + .filter(value -> value != null && value.trim().length() > 0) + .distinct() + .collect(Collectors.toList()); + return new PlanListView(coilIds, rows); + } + } + + public static class PlanDetailView { + private final String coilId; + private final List> rows; + private final Map firstRow; + + public PlanDetailView(String coilId, List> rows, Map firstRow) { + this.coilId = coilId; + this.rows = rows; + this.firstRow = firstRow; + } + + public String getCoilId() { + return coilId; + } + + public List> getRows() { + return rows; + } + + public Map getFirstRow() { + return firstRow; + } + + public static PlanDetailView fromExecuteSqlResponse(String coilId, SqlServerApiClient.ExecuteSqlResponse response) { + List> rows = asRowList(response); + Map firstRow = rows.isEmpty() ? Collections.emptyMap() : rows.get(0); + return new PlanDetailView(coilId, rows, firstRow); + } + } + + public static class SegSeriesView { + private final String coilId; + private final List segNo; + private final Map> series; + private final List> rows; + + public SegSeriesView(String coilId, List segNo, Map> series, List> rows) { + this.coilId = coilId; + this.segNo = segNo; + this.series = series; + this.rows = rows; + } + + public String getCoilId() { + return coilId; + } + + public List getSegNo() { + return segNo; + } + + public Map> getSeries() { + return series; + } + + public List> getRows() { + return rows; + } + + @SuppressWarnings("unchecked") + public static SegSeriesView fromExecuteSqlResponse(String coilId, SqlServerApiClient.ExecuteSqlResponse response) { + List> rows = asRowList(response); + Collections.sort(rows, new Comparator>() { + @Override + public int compare(Map left, Map right) { + Number leftSeg = asNumber(left.get("SEGNO")); + Number rightSeg = asNumber(right.get("SEGNO")); + int leftValue = leftSeg == null ? Integer.MAX_VALUE : leftSeg.intValue(); + int rightValue = rightSeg == null ? Integer.MAX_VALUE : rightSeg.intValue(); + if (leftValue > rightValue) { + return 1; + } + if (leftValue < rightValue) { + return -1; + } + return 0; + } + }); + + List segNo = new ArrayList(); + for (Map row : rows) { + Number seg = asNumber(row.get("SEGNO")); + segNo.add(seg == null ? null : seg.intValue()); + } + + Map> series = new LinkedHashMap>(); + for (Map row : rows) { + for (Map.Entry entry : row.entrySet()) { + String key = entry.getKey(); + if ("SEGNO".equalsIgnoreCase(key)) { + continue; + } + series.computeIfAbsent(key, ignored -> new ArrayList<>()).add(entry.getValue()); + } + } + + return new SegSeriesView(coilId, segNo, series, rows); + } + } + + public static class RealtimeDataBundle { + private final SqlServerApiClient.ExecuteSqlResponse gauge; + private final SqlServerApiClient.ExecuteSqlResponse shape; + + public RealtimeDataBundle(SqlServerApiClient.ExecuteSqlResponse gauge, + SqlServerApiClient.ExecuteSqlResponse shape) { + this.gauge = gauge; + this.shape = shape; + } + + public SqlServerApiClient.ExecuteSqlResponse getGauge() { + return gauge; + } + + public SqlServerApiClient.ExecuteSqlResponse getShape() { + return shape; + } + } +} diff --git a/klp-admin/src/main/java/com/klp/framework/sqlserver/SqlServerApiController.java b/klp-admin/src/main/java/com/klp/framework/sqlserver/SqlServerApiController.java new file mode 100644 index 00000000..f499d74c --- /dev/null +++ b/klp-admin/src/main/java/com/klp/framework/sqlserver/SqlServerApiController.java @@ -0,0 +1,62 @@ +package com.klp.web.controller.sqlserver; + +import com.klp.common.core.domain.R; +import com.klp.framework.service.SqlServerApiBusinessService; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * sql-server-api 业务查询接口。 + *

+ * 提供计划、SEG、实时数据三个入口,方便前端联动展示。 + */ +@RequiredArgsConstructor +@RestController +@RequestMapping("/sql-server-api") +public class SqlServerApiController { + + private final SqlServerApiBusinessService businessService; + + /** + * 计划列表。 + */ + @GetMapping("/plans") + public R planList() { + return R.ok(businessService.getPlanList()); + } + + /** + * 计划详情。 + */ + @GetMapping("/plans/{coilId}") + public R planDetail(@PathVariable String coilId) { + return R.ok(businessService.getPlanByCoilId(coilId)); + } + + /** + * 钢卷实际 SEG,按入口卷号查询。 + */ + @GetMapping("/seg/{encoilId}") + public R segByEncoilId(@PathVariable String encoilId) { + return R.ok(businessService.getSegSeriesViewByEncoilId(encoilId)); + } + + /** + * 钢卷实际 SEG,按出口卷号查询。 + */ + @GetMapping("/seg-by-excoil/{excoilId}") + public R segByExcoilId(@PathVariable String excoilId) { + return R.ok(businessService.getSegByExcoilId(excoilId)); + } + + /** + * 实时数据:厚度、板形。 + */ + @GetMapping("/realtime/{matId}") + public R realtime(@PathVariable String matId) { + return R.ok(businessService.getRealtimeData(matId)); + } +} diff --git a/klp-admin/src/main/resources/application.yml b/klp-admin/src/main/resources/application.yml index 4bd867fa..4b95c60e 100644 --- a/klp-admin/src/main/resources/application.yml +++ b/klp-admin/src/main/resources/application.yml @@ -65,6 +65,15 @@ user: # 密码锁定时间(默认10分钟) lockTime: 10 +# sql-server-api 中间件配置 +sql-server-api: + # 请求地址,请替换为实际 IP + host: 127.0.0.1 + # 请求端口,请替换为实际端口 + port: 8080 + # 基础访问地址(可直接注入使用) + base-url: http://${sql-server-api.host}:${sql-server-api.port} + # Spring配置 spring: application: diff --git a/klp-ui/src/api/l2/timing.js b/klp-ui/src/api/l2/timing.js new file mode 100644 index 00000000..d2f865c0 --- /dev/null +++ b/klp-ui/src/api/l2/timing.js @@ -0,0 +1,36 @@ +import axios from 'axios' + +export default function createTimingFetch(url) { + const request = axios.create({ + baseURL: 'http://' + url, + headers: { + 'Content-Type': 'application/json' + }, + timeout: 10000 + }) + + request.interceptors.response.use(response => response.data) + + return { + getPlanList: () => request({ + url: '/sql-server-api/plans', + method: 'get' + }), + getPlanDetail: (coilId) => request({ + url: `/sql-server-api/plans/${coilId}`, + method: 'get' + }), + getSegByEncoilId: (encoilId) => request({ + url: `/sql-server-api/seg/${encoilId}`, + method: 'get' + }), + getSegByExcoilId: (excoilId) => request({ + url: `/sql-server-api/seg-by-excoil/${excoilId}`, + method: 'get' + }), + getRealtimeData: (matId) => request({ + url: `/sql-server-api/realtime/${matId}`, + method: 'get' + }) + } +} diff --git a/klp-ui/src/views/timing/acid/index.vue b/klp-ui/src/views/timing/acid/index.vue new file mode 100644 index 00000000..0f7d1bc9 --- /dev/null +++ b/klp-ui/src/views/timing/acid/index.vue @@ -0,0 +1,189 @@ + + + + + diff --git a/klp-ui/src/views/timing/realtime/index.vue b/klp-ui/src/views/timing/realtime/index.vue new file mode 100644 index 00000000..e9e0f9cd --- /dev/null +++ b/klp-ui/src/views/timing/realtime/index.vue @@ -0,0 +1,110 @@ + + + + +