mirror of
https://github.com/dataease/dataease.git
synced 2025-02-24 11:32:57 +08:00
Merge branch 'dev-v2' into pr@dev-v2_export_data
This commit is contained in:
commit
539d18581b
@ -115,7 +115,9 @@ public class ChartDataManage {
|
||||
if (StringUtils.equalsIgnoreCase(view.getType(), "table-pivot")
|
||||
|| StringUtils.containsIgnoreCase(view.getType(), "group")
|
||||
|| ("antv".equalsIgnoreCase(view.getRender()) && "line".equalsIgnoreCase(view.getType()))
|
||||
|| StringUtils.equalsIgnoreCase(view.getType(), "flow-map")) {
|
||||
|| StringUtils.equalsIgnoreCase(view.getType(), "flow-map")
|
||||
|| StringUtils.equalsIgnoreCase(view.getType(), "sankey")
|
||||
) {
|
||||
xAxis.addAll(xAxisExt);
|
||||
}
|
||||
List<ChartViewFieldDTO> yAxis = new ArrayList<>(view.getYAxis());
|
||||
|
@ -59,7 +59,7 @@ public class ApiUtils {
|
||||
if (apiDefinition == null) {
|
||||
DEException.throwException("未找到");
|
||||
}
|
||||
String response = execHttpRequest(apiDefinition, 10);
|
||||
String response = execHttpRequest(apiDefinition, apiDefinition.getApiQueryTimeout() == null || apiDefinition.getApiQueryTimeout() <= 0 ? 10 : apiDefinition.getApiQueryTimeout());
|
||||
fieldList = getTableFields(apiDefinition);
|
||||
result.put("fieldList", fieldList);
|
||||
dataList = fetchResult(response, apiDefinition);
|
||||
@ -116,7 +116,7 @@ public class ApiUtils {
|
||||
if (apiDefinition == null) {
|
||||
DEException.throwException("未找到");
|
||||
}
|
||||
String response = execHttpRequest(apiDefinition, 10);
|
||||
String response = execHttpRequest(apiDefinition, apiDefinition.getApiQueryTimeout() == null || apiDefinition.getApiQueryTimeout() <= 0 ? 10 : apiDefinition.getApiQueryTimeout());
|
||||
return fetchResult(response, apiDefinition);
|
||||
}
|
||||
|
||||
@ -194,6 +194,26 @@ public class ApiUtils {
|
||||
return response;
|
||||
}
|
||||
|
||||
private static void previewNum(List<Map<String, Object>> field){
|
||||
for (Map<String, Object> stringObjectMap : field) {
|
||||
JSONArray newArray = new JSONArray();
|
||||
if (stringObjectMap.get("value") != null) {
|
||||
try {
|
||||
TypeReference<JSONArray> listTypeReference = new TypeReference<JSONArray>() {
|
||||
};
|
||||
JSONArray array = objectMapper.readValue(stringObjectMap.get("value").toString(), listTypeReference);
|
||||
if(array.size() > 100){
|
||||
for (int i = 0; i < Math.min(100, array.size()); i++) {
|
||||
newArray.add(array.get(i));
|
||||
}
|
||||
stringObjectMap.put("value", newArray);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static ApiDefinition checkApiDefinition(ApiDefinition apiDefinition, String response) throws DEException {
|
||||
if (StringUtils.isEmpty(response)) {
|
||||
@ -217,6 +237,7 @@ public class ApiUtils {
|
||||
rootPath = "$";
|
||||
handleStr(apiDefinition, response, fields, rootPath);
|
||||
}
|
||||
previewNum(fields);
|
||||
apiDefinition.setJsonFields(fields);
|
||||
return apiDefinition;
|
||||
} else {
|
||||
|
@ -39,6 +39,8 @@ import java.math.BigDecimal;
|
||||
import java.net.URL;
|
||||
import java.sql.*;
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
@ -115,7 +117,11 @@ public class CalciteProvider {
|
||||
tableDesc.setDatasourceId(datasourceRequest.getDatasource().getId());
|
||||
tableDesc.setType("db");
|
||||
tableDesc.setTableName(resultSet.getString(1));
|
||||
if(resultSet.getMetaData().getColumnCount() > 1){
|
||||
tableDesc.setName(resultSet.getString(2));
|
||||
}else {
|
||||
tableDesc.setName(resultSet.getString(1));
|
||||
}
|
||||
return tableDesc;
|
||||
}
|
||||
|
||||
@ -313,6 +319,7 @@ public class CalciteProvider {
|
||||
private String getTableFiledSql(DatasourceRequest datasourceRequest) {
|
||||
String sql = "";
|
||||
DatasourceConfiguration configuration = null;
|
||||
String database="";
|
||||
DatasourceType datasourceType = DatasourceType.valueOf(datasourceRequest.getDatasource().getType());
|
||||
switch (datasourceType) {
|
||||
case mysql:
|
||||
@ -322,7 +329,16 @@ public class CalciteProvider {
|
||||
case StarRocks:
|
||||
case doris:
|
||||
configuration = JsonUtil.parseObject(datasourceRequest.getDatasource().getConfiguration(), Mysql.class);
|
||||
sql = String.format("SELECT COLUMN_NAME,DATA_TYPE,COLUMN_COMMENT FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '%s' AND TABLE_NAME = '%s'", configuration.getDataBase(), datasourceRequest.getTable());
|
||||
if (StringUtils.isEmpty(configuration.getUrlType()) || configuration.getUrlType().equalsIgnoreCase("hostName")) {
|
||||
database = configuration.getDataBase();
|
||||
} else {
|
||||
Pattern WITH_SQL_FRAGMENT = Pattern.compile("jdbc:mysql://(.*):(\\d+)/(.*)");
|
||||
Matcher matcher = WITH_SQL_FRAGMENT.matcher(configuration.getJdbcUrl());
|
||||
matcher.find();
|
||||
String[] databasePrams = matcher.group(3).split("\\?");
|
||||
database = databasePrams[0];
|
||||
}
|
||||
sql = String.format("SELECT COLUMN_NAME,DATA_TYPE,COLUMN_COMMENT FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '%s' AND TABLE_NAME = '%s'", database, datasourceRequest.getTable());
|
||||
break;
|
||||
case oracle:
|
||||
configuration = JsonUtil.parseObject(datasourceRequest.getDatasource().getConfiguration(), Oracle.class);
|
||||
@ -394,6 +410,16 @@ public class CalciteProvider {
|
||||
break;
|
||||
case ck:
|
||||
configuration = JsonUtil.parseObject(datasourceRequest.getDatasource().getConfiguration(), CK.class);
|
||||
|
||||
if (StringUtils.isEmpty(configuration.getUrlType()) || configuration.getUrlType().equalsIgnoreCase("hostName")) {
|
||||
database = configuration.getDataBase();
|
||||
} else {
|
||||
Pattern WITH_SQL_FRAGMENT = Pattern.compile("jdbc:clickhouse://(.*):(\\d+)/(.*)");
|
||||
Matcher matcher = WITH_SQL_FRAGMENT.matcher(configuration.getJdbcUrl());
|
||||
matcher.find();
|
||||
String[] databasePrams = matcher.group(3).split("\\?");
|
||||
database = databasePrams[0];
|
||||
}
|
||||
sql = String.format(" SELECT\n" +
|
||||
" name,\n" +
|
||||
" type,\n" +
|
||||
@ -402,7 +428,7 @@ public class CalciteProvider {
|
||||
" system.columns\n" +
|
||||
"WHERE\n" +
|
||||
" database = '%s' \n" +
|
||||
" AND table = '%s' ", configuration.getDataBase(), datasourceRequest.getTable());
|
||||
" AND table = '%s' ", database, datasourceRequest.getTable());
|
||||
break;
|
||||
case impala:
|
||||
sql = String.format("DESCRIBE `%s`", datasourceRequest.getTable());
|
||||
@ -731,6 +757,7 @@ public class CalciteProvider {
|
||||
List<String> tableSqls = new ArrayList<>();
|
||||
DatasourceConfiguration.DatasourceType datasourceType = DatasourceConfiguration.DatasourceType.valueOf(datasourceRequest.getDatasource().getType());
|
||||
DatasourceConfiguration configuration = null;
|
||||
String database = "";
|
||||
switch (datasourceType) {
|
||||
case mysql:
|
||||
case mongo:
|
||||
@ -739,33 +766,62 @@ public class CalciteProvider {
|
||||
case StarRocks:
|
||||
case doris:
|
||||
configuration = JsonUtil.parseObject(datasourceRequest.getDatasource().getConfiguration(), Mysql.class);
|
||||
tableSqls.add(String.format("SELECT TABLE_NAME,TABLE_COMMENT FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '%s' ;", configuration.getDataBase()));
|
||||
if (StringUtils.isEmpty(configuration.getUrlType()) || configuration.getUrlType().equalsIgnoreCase("hostName")) {
|
||||
database = configuration.getDataBase();
|
||||
} else {
|
||||
Pattern WITH_SQL_FRAGMENT = Pattern.compile("jdbc:mysql://(.*):(\\d+)/(.*)");
|
||||
Matcher matcher = WITH_SQL_FRAGMENT.matcher(configuration.getJdbcUrl());
|
||||
matcher.find();
|
||||
String[] databasePrams = matcher.group(3).split("\\?");
|
||||
database = databasePrams[0];
|
||||
}
|
||||
tableSqls.add(String.format("SELECT TABLE_NAME,TABLE_COMMENT FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '%s' ;", database));
|
||||
break;
|
||||
case oracle:
|
||||
configuration = JsonUtil.parseObject(datasourceRequest.getDatasource().getConfiguration(), Oracle.class);
|
||||
if (StringUtils.isEmpty(configuration.getSchema())) {
|
||||
DEException.throwException(Translator.get("i18n_schema_is_empty"));
|
||||
}
|
||||
tableSqls.add("select table_name, owner, comments from all_tab_comments where owner='" + configuration.getSchema() + "' AND table_type = 'TABLE'");
|
||||
tableSqls.add("select table_name, owner, comments from all_tab_comments where owner='" + configuration.getSchema() + "' AND table_type = 'VIEW'");
|
||||
tableSqls.add("select table_name, comments, owner from all_tab_comments where owner='" + configuration.getSchema() + "' AND table_type = 'TABLE'");
|
||||
tableSqls.add("select table_name, comments, owner from all_tab_comments where owner='" + configuration.getSchema() + "' AND table_type = 'VIEW'");
|
||||
break;
|
||||
case db2:
|
||||
configuration = JsonUtil.parseObject(datasourceRequest.getDatasource().getConfiguration(), Db2.class);
|
||||
if (StringUtils.isEmpty(configuration.getSchema())) {
|
||||
DEException.throwException(Translator.get("i18n_schema_is_empty"));
|
||||
}
|
||||
tableSqls.add("select TABNAME from syscat.tables WHERE TABSCHEMA ='DE_SCHEMA' AND \"TYPE\" = 'T'".replace("DE_SCHEMA", configuration.getSchema()));
|
||||
tableSqls.add("select TABNAME, REMARKS from syscat.tables WHERE TABSCHEMA ='DE_SCHEMA' AND \"TYPE\" = 'T'".replace("DE_SCHEMA", configuration.getSchema()));
|
||||
break;
|
||||
case sqlServer:
|
||||
configuration = JsonUtil.parseObject(datasourceRequest.getDatasource().getConfiguration(), Sqlserver.class);
|
||||
if (StringUtils.isEmpty(configuration.getSchema())) {
|
||||
DEException.throwException(Translator.get("i18n_schema_is_empty"));
|
||||
}
|
||||
tableSqls.add("SELECT TABLE_NAME FROM \"DATABASE\".INFORMATION_SCHEMA.VIEWS WHERE TABLE_SCHEMA = 'DS_SCHEMA' ;"
|
||||
.replace("DATABASE", configuration.getDataBase())
|
||||
tableSqls.add("SELECT \n" +
|
||||
" t.name AS TableName, \n" +
|
||||
" ep.value AS TableDescription \n" +
|
||||
"FROM \n" +
|
||||
" sys.tables t \n" +
|
||||
"LEFT OUTER JOIN sys.schemas sc ON sc.schema_id =t.schema_id \n" +
|
||||
"LEFT OUTER JOIN \n" +
|
||||
" sys.extended_properties ep ON t.object_id = ep.major_id \n" +
|
||||
" AND ep.minor_id = 0 \n" +
|
||||
" AND ep.class = 1 \n" +
|
||||
" AND ep.name = 'MS_Description'\n" +
|
||||
"where sc.name ='DS_SCHEMA'"
|
||||
.replace("DS_SCHEMA", configuration.getSchema()));
|
||||
tableSqls.add("SELECT TABLE_NAME FROM \"DATABASE\".INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_SCHEMA = 'DS_SCHEMA' ;"
|
||||
.replace("DATABASE", configuration.getDataBase())
|
||||
tableSqls.add("SELECT \n" +
|
||||
" t.name AS TableName, \n" +
|
||||
" ep.value AS TableDescription \n" +
|
||||
"FROM \n" +
|
||||
" sys.views t \n" +
|
||||
"LEFT OUTER JOIN sys.schemas sc ON sc.schema_id =t.schema_id \n" +
|
||||
"LEFT OUTER JOIN \n" +
|
||||
" sys.extended_properties ep ON t.object_id = ep.major_id \n" +
|
||||
" AND ep.minor_id = 0 \n" +
|
||||
" AND ep.class = 1 \n" +
|
||||
" AND ep.name = 'MS_Description'\n" +
|
||||
"where sc.name ='DS_SCHEMA'"
|
||||
.replace("DS_SCHEMA", configuration.getSchema()));
|
||||
break;
|
||||
case pg:
|
||||
@ -773,15 +829,39 @@ public class CalciteProvider {
|
||||
if (StringUtils.isEmpty(configuration.getSchema())) {
|
||||
DEException.throwException(Translator.get("i18n_schema_is_empty"));
|
||||
}
|
||||
tableSqls.add("SELECT tablename FROM pg_tables WHERE schemaname='SCHEMA' ;".replace("SCHEMA", configuration.getSchema()));
|
||||
tableSqls.add("SELECT \n" +
|
||||
" relname AS TableName, \n" +
|
||||
" obj_description(relfilenode::regclass, 'pg_class') AS TableDescription \n" +
|
||||
"FROM \n" +
|
||||
" pg_class \n" +
|
||||
"WHERE \n" +
|
||||
" relkind = 'r' \n" +
|
||||
" AND relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'SCHEMA') ".replace("SCHEMA", configuration.getSchema()));
|
||||
break;
|
||||
case redshift:
|
||||
configuration = JsonUtil.parseObject(datasourceRequest.getDatasource().getConfiguration(), CK.class);
|
||||
tableSqls.add("SELECT tablename FROM pg_tables WHERE schemaname='SCHEMA' ;".replace("SCHEMA", configuration.getSchema()));
|
||||
tableSqls.add("SELECT \n" +
|
||||
" relname AS TableName, \n" +
|
||||
" obj_description(relfilenode::regclass, 'pg_class') AS TableDescription \n" +
|
||||
"FROM \n" +
|
||||
" pg_class \n" +
|
||||
"WHERE \n" +
|
||||
" relkind = 'r' \n" +
|
||||
" AND relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'SCHEMA') ".replace("SCHEMA", configuration.getSchema()));
|
||||
break;
|
||||
case ck:
|
||||
configuration = JsonUtil.parseObject(datasourceRequest.getDatasource().getConfiguration(), CK.class);
|
||||
tableSqls.add("SELECT name FROM system.tables where database='DATABASE';".replace("DATABASE", configuration.getDataBase()));
|
||||
if (StringUtils.isEmpty(configuration.getUrlType()) || configuration.getUrlType().equalsIgnoreCase("hostName")) {
|
||||
database = configuration.getDataBase();
|
||||
} else {
|
||||
Pattern WITH_SQL_FRAGMENT = Pattern.compile("jdbc:clickhouse://(.*):(\\d+)/(.*)");
|
||||
Matcher matcher = WITH_SQL_FRAGMENT.matcher(configuration.getJdbcUrl());
|
||||
matcher.find();
|
||||
String[] databasePrams = matcher.group(3).split("\\?");
|
||||
database = databasePrams[0];
|
||||
}
|
||||
tableSqls.add("SELECT name, comment FROM system.tables where database='DATABASE';".replace("DATABASE", database));
|
||||
|
||||
break;
|
||||
default:
|
||||
tableSqls.add("show tables");
|
||||
|
@ -493,6 +493,9 @@ public class DatasourceServer implements DatasourceApi {
|
||||
public DatasourceDTO get(Long datasourceId) throws DEException {
|
||||
DatasourceDTO datasourceDTO = new DatasourceDTO();
|
||||
CoreDatasource datasource = datasourceMapper.selectById(datasourceId);
|
||||
if(datasource == null){
|
||||
DEException.throwException("不存在的数据源!");
|
||||
}
|
||||
BeanUtils.copyBean(datasourceDTO, datasource);
|
||||
TypeReference<List<ApiDefinition>> listTypeReference = new TypeReference<List<ApiDefinition>>() {
|
||||
};
|
||||
@ -834,10 +837,11 @@ public class DatasourceServer implements DatasourceApi {
|
||||
|
||||
public ApiDefinition checkApiDatasource(Map<String, String> request) throws DEException {
|
||||
ApiDefinition apiDefinition = JsonUtil.parseObject(new String(java.util.Base64.getDecoder().decode(request.get("data"))), ApiDefinition.class);
|
||||
String response = ApiUtils.execHttpRequest(apiDefinition, 10);
|
||||
String response = ApiUtils.execHttpRequest(apiDefinition, apiDefinition.getApiQueryTimeout() == null || apiDefinition.getApiQueryTimeout() <= 0 ? 10 : apiDefinition.getApiQueryTimeout());
|
||||
if (request.keySet().contains("type") && request.get("type").equals("apiStructure")) {
|
||||
apiDefinition.setShowApiStructure(true);
|
||||
}
|
||||
|
||||
ApiUtils.checkApiDefinition(apiDefinition, response);
|
||||
if (apiDefinition.getRequest().getAuthManager() != null && StringUtils.isNotBlank(apiDefinition.getRequest().getAuthManager().getUsername()) && StringUtils.isNotBlank(apiDefinition.getRequest().getAuthManager().getPassword()) && apiDefinition.getRequest().getAuthManager().getVerification().equals("Basic Auth")) {
|
||||
apiDefinition.getRequest().getAuthManager().setUsername(new String(Base64.getEncoder().encode(apiDefinition.getRequest().getAuthManager().getUsername().getBytes())));
|
||||
|
@ -12,6 +12,9 @@ public class CK extends DatasourceConfiguration {
|
||||
private String extraParams = "";
|
||||
|
||||
public String getJdbc() {
|
||||
if(StringUtils.isNoneEmpty(getUrlType()) && !getUrlType().equalsIgnoreCase("hostName")){
|
||||
return getJdbcUrl();
|
||||
}
|
||||
if(StringUtils.isEmpty(extraParams.trim())){
|
||||
return "jdbc:clickhouse://HOSTNAME:PORT/DATABASE"
|
||||
.replace("HOSTNAME", getHost().trim())
|
||||
|
@ -12,6 +12,9 @@ public class Db2 extends DatasourceConfiguration {
|
||||
private String extraParams = "";
|
||||
|
||||
public String getJdbc() {
|
||||
if(StringUtils.isNoneEmpty(getUrlType()) && !getUrlType().equalsIgnoreCase("hostName")){
|
||||
return getJdbcUrl();
|
||||
}
|
||||
if(StringUtils.isEmpty(extraParams.trim())){
|
||||
if (StringUtils.isEmpty(getSchema())) {
|
||||
return "jdbc:db2://HOSTNAME:PORT/DATABASE"
|
||||
|
@ -17,6 +17,9 @@ public class Impala extends DatasourceConfiguration {
|
||||
private List<String> showTableSqls = Arrays.asList("show tables");
|
||||
|
||||
public String getJdbc() {
|
||||
if(StringUtils.isNoneEmpty(getUrlType()) && !getUrlType().equalsIgnoreCase("hostName")){
|
||||
return getJdbcUrl();
|
||||
}
|
||||
if(StringUtils.isEmpty(extraParams.trim())){
|
||||
return "jdbc:impala://HOSTNAME:PORT/DATABASE"
|
||||
.replace("HOSTNAME", getHost().trim())
|
||||
|
@ -17,6 +17,9 @@ public class Mongo extends DatasourceConfiguration {
|
||||
private List<String> showTableSqls = Arrays.asList("show tables");
|
||||
|
||||
public String getJdbc() {
|
||||
if(StringUtils.isNoneEmpty(getUrlType()) && !getUrlType().equalsIgnoreCase("hostName")){
|
||||
return getJdbcUrl();
|
||||
}
|
||||
if (StringUtils.isEmpty(extraParams.trim())) {
|
||||
return "jdbc:mysql://HOSTNAME:PORT/DATABASE"
|
||||
.replace("HOSTNAME", getHost().trim())
|
||||
|
@ -19,6 +19,9 @@ public class Mysql extends DatasourceConfiguration {
|
||||
private List<String> showTableSqls = Arrays.asList("show tables");
|
||||
|
||||
public String getJdbc() {
|
||||
if(StringUtils.isNoneEmpty(getUrlType()) && !getUrlType().equalsIgnoreCase("hostName")){
|
||||
return getJdbcUrl();
|
||||
}
|
||||
if (StringUtils.isEmpty(extraParams.trim())) {
|
||||
return "jdbc:mysql://HOSTNAME:PORT/DATABASE"
|
||||
.replace("HOSTNAME", getHost().trim())
|
||||
|
@ -12,6 +12,9 @@ public class Oracle extends DatasourceConfiguration {
|
||||
private String extraParams = "";
|
||||
|
||||
public String getJdbc() {
|
||||
if(StringUtils.isNoneEmpty(getUrlType()) && !getUrlType().equalsIgnoreCase("hostName")){
|
||||
return getJdbcUrl();
|
||||
}
|
||||
if (StringUtils.isNotEmpty(getConnectionType()) && getConnectionType().equalsIgnoreCase("serviceName")) {
|
||||
return "jdbc:oracle:thin:@HOSTNAME:PORT/DATABASE"
|
||||
.replace("HOSTNAME", getHost().trim())
|
||||
|
@ -12,6 +12,9 @@ public class Pg extends DatasourceConfiguration {
|
||||
private String extraParams = "";
|
||||
|
||||
public String getJdbc() {
|
||||
if(StringUtils.isNoneEmpty(getUrlType()) && !getUrlType().equalsIgnoreCase("hostName")){
|
||||
return getJdbcUrl();
|
||||
}
|
||||
if(StringUtils.isEmpty(extraParams.trim())){
|
||||
if (StringUtils.isEmpty(getSchema())) {
|
||||
return "jdbc:postgresql://HOSTNAME:PORT/DATABASE"
|
||||
|
@ -12,6 +12,9 @@ public class Redshift extends DatasourceConfiguration {
|
||||
private String extraParams = "";
|
||||
|
||||
public String getJdbc() {
|
||||
if(StringUtils.isNoneEmpty(getUrlType()) && !getUrlType().equalsIgnoreCase("hostName")){
|
||||
return getJdbcUrl();
|
||||
}
|
||||
return "jdbc:redshift://HOSTNAME:PORT/DATABASE"
|
||||
.replace("HOSTNAME", getHost().trim())
|
||||
.replace("PORT", getPort().toString().trim())
|
||||
|
@ -17,6 +17,9 @@ public class Sqlserver extends DatasourceConfiguration {
|
||||
private List<String> showTableSqls = Arrays.asList("show tables");
|
||||
|
||||
public String getJdbc() {
|
||||
if(StringUtils.isNoneEmpty(getUrlType()) && !getUrlType().equalsIgnoreCase("hostName")){
|
||||
return getJdbcUrl();
|
||||
}
|
||||
if (StringUtils.isEmpty(extraParams.trim())) {
|
||||
return "jdbc:sqlserver://HOSTNAME:PORT;DatabaseName=DATABASE"
|
||||
.replace("HOSTNAME", getHost().trim())
|
||||
|
@ -48,7 +48,7 @@ dataease:
|
||||
version: '@project.version@'
|
||||
origin-list: http://192.168.2.70:9080
|
||||
apisix-api:
|
||||
domain: http://192.168.0.121:9180
|
||||
domain: http://192.168.2.70:9180
|
||||
key: edd1c9f034335f136f87ad84b625c8f1
|
||||
|
||||
# springdoc-openapi项目配置
|
||||
|
@ -11,5 +11,14 @@ CREATE TABLE `core_sys_startup_job`
|
||||
) COMMENT ='项目启动任务';
|
||||
|
||||
BEGIN;
|
||||
INSERT INTO `core_sys_startup_job` VALUES ('chartFilterMerge', 'chartFilterMerge', 'ready');
|
||||
INSERT INTO `core_sys_startup_job`
|
||||
VALUES ('chartFilterMerge', 'chartFilterMerge', 'ready');
|
||||
COMMIT;
|
||||
|
||||
|
||||
ALTER TABLE `xpack_setting_authentication`
|
||||
ADD COLUMN `plugin_json` longtext NULL COMMENT '插件配置' AFTER `relational_ids`;
|
||||
ALTER TABLE `xpack_setting_authentication`
|
||||
ADD COLUMN `synced` tinyint(1) NOT NULL DEFAULT 0 COMMENT '已同步' AFTER `plugin_json`;
|
||||
ALTER TABLE `xpack_setting_authentication`
|
||||
ADD COLUMN `valid` tinyint(1) NOT NULL DEFAULT 0 COMMENT '有效' AFTER `synced`;
|
@ -152,6 +152,7 @@ export const uploadFile = async (data): Promise<IResponse> => {
|
||||
.post({
|
||||
url: '/datasource/uploadFile',
|
||||
data,
|
||||
loading: true,
|
||||
headersType: 'multipart/form-data;'
|
||||
})
|
||||
.then(res => {
|
||||
|
5
core/core-frontend/src/assets/svg/sankey.svg
Normal file
5
core/core-frontend/src/assets/svg/sankey.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg width="80" height="56" viewBox="0 0 80 56" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13 32.7862C35.0501 32.7862 43.9248 11.0972 67.002 10.375C67.0341 17.043 66.8736 16.9467 67.002 23.2296C45.7703 23.2296 43.0421 45.6408 13 45.6408C13.0642 40.0801 13.0963 40.0159 13 32.7862Z" fill="#00D6B9"/>
|
||||
<path d="M13 27.4899C34.6891 27.1288 37.1926 46.0576 67.002 47.9994C67.0341 41.0987 66.8736 51.4819 67.002 44.9744C38.7974 42.3826 36.4624 24.4648 13 24.4648C13.0642 30.2502 13.0963 20.0275 13 27.4899Z" fill="#3370FF"/>
|
||||
<path d="M13 20.9829C35.0501 20.9829 43.9247 40.2086 67.002 40.9629C67.8204 33.2036 66.8736 34.327 67.002 27.9799C45.7703 27.9799 43.0421 8 13 8C13.0642 13.6249 13.0963 13.7212 13 20.9829Z" fill="#3370FF"/>
|
||||
</svg>
|
After Width: | Height: | Size: 748 B |
@ -146,13 +146,6 @@ const saveSelfSubject = () => {
|
||||
>
|
||||
<filter-style-simple-selector />
|
||||
</el-collapse-item>
|
||||
<el-collapse-item
|
||||
:title="t('visualization.filter_component')"
|
||||
name="filterComponent"
|
||||
class="no-padding no-border-bottom"
|
||||
>
|
||||
<filter-style-simple-selector />
|
||||
</el-collapse-item>
|
||||
<el-collapse-item
|
||||
title="高级样式设置"
|
||||
name="seniorStyleSetting"
|
||||
|
@ -158,6 +158,21 @@
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="分页器配色" class="form-item">
|
||||
<el-color-picker
|
||||
:trigger-width="colorPickerWidth"
|
||||
v-model="seniorForm.pagerColor"
|
||||
size="small"
|
||||
:predefine="predefineColors"
|
||||
color-format="rgb"
|
||||
:effect="themes"
|
||||
show-alpha
|
||||
is-custom
|
||||
@change="changePagerColorChange"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
@ -174,7 +189,10 @@ import eventBus from '@/utils/eventBus'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import CustomColorStyleSelect from '@/views/chart/components/editor/editor-style/components/CustomColorStyleSelect.vue'
|
||||
import elementResizeDetectorMaker from 'element-resize-detector'
|
||||
import { snapshotStoreWithOut } from '@/store/modules/data-visualization/snapshot'
|
||||
const { t } = useI18n()
|
||||
const snapshotStore = snapshotStoreWithOut()
|
||||
|
||||
const props = defineProps({
|
||||
themes: {
|
||||
type: String as PropType<EditorTheme>,
|
||||
@ -191,6 +209,9 @@ const colorFormRef = ref(null)
|
||||
const colorForm = computed(
|
||||
() => canvasStyleData.value.component.chartColor as DeepPartial<ChartAttr>
|
||||
)
|
||||
|
||||
const seniorForm = computed(() => canvasStyleData.value.component.seniorStyleSetting)
|
||||
|
||||
const predefineColors = COLOR_PANEL
|
||||
|
||||
const state = reactive({
|
||||
@ -215,6 +236,10 @@ const changeColorOption = (modifyName = 'value') => {
|
||||
changeColorCase(modifyName)
|
||||
}
|
||||
|
||||
const changePagerColorChange = () => {
|
||||
snapshotStore.recordSnapshotCache()
|
||||
}
|
||||
|
||||
const changeColorCase = modifyName => {
|
||||
colorForm.value['modifyName'] = modifyName
|
||||
emits('onColorChange', colorForm.value)
|
||||
|
@ -2,25 +2,37 @@
|
||||
<div style="width: 100%; padding-bottom: 8px">
|
||||
<el-form label-position="top" style="width: 100%">
|
||||
<div style="width: 100%; padding: 16px 8px 0">
|
||||
<el-col :span="12">
|
||||
<el-form-item class="form-item" label="联动、钻取、调整的图标颜色">
|
||||
<el-col :span="24">
|
||||
<el-form-item
|
||||
:effect="themes"
|
||||
class="form-item"
|
||||
:class="'form-item-' + themes"
|
||||
label="联动、钻取、跳转的图标颜色"
|
||||
>
|
||||
<el-color-picker
|
||||
v-model="filterStyle.titleColor"
|
||||
:effect="themes"
|
||||
v-model="seniorStyleSetting.linkageIconColor"
|
||||
:trigger-width="197"
|
||||
is-custom
|
||||
:predefine="state.predefineColors"
|
||||
@change="themeChange('titleColor')"
|
||||
@change="themeChange"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item class="form-item" label="钻取层级展示颜色">
|
||||
<el-col :span="24">
|
||||
<el-form-item
|
||||
:effect="themes"
|
||||
class="form-item"
|
||||
:class="'form-item-' + themes"
|
||||
label="钻取层级展示颜色"
|
||||
>
|
||||
<el-color-picker
|
||||
v-model="filterStyle.labelColor"
|
||||
v-model="seniorStyleSetting.drillLayerColor"
|
||||
:effect="themes"
|
||||
:trigger-width="197"
|
||||
is-custom
|
||||
:predefine="state.predefineColors"
|
||||
@change="themeChange('labelColor')"
|
||||
@change="themeChange"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@ -35,17 +47,21 @@ import { COLOR_PANEL } from '@/views/chart/components/editor/util/chart'
|
||||
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { adaptCurThemeFilterStyleAll } from '@/utils/canvasStyle'
|
||||
import { snapshotStoreWithOut } from '@/store/modules/data-visualization/snapshot'
|
||||
import eventBus from '@/utils/eventBus'
|
||||
import { ElIcon } from 'element-plus-secondary'
|
||||
const { t } = useI18n()
|
||||
const dvMainStore = dvMainStoreWithOut()
|
||||
const snapshotStore = snapshotStoreWithOut()
|
||||
const filterStyle = computed<any>(() => {
|
||||
return dvMainStore.canvasStyleData.component.filterStyle
|
||||
const seniorStyleSetting = computed<any>(() => {
|
||||
return dvMainStore.canvasStyleData.component.seniorStyleSetting
|
||||
})
|
||||
|
||||
const props = defineProps({
|
||||
themes: {
|
||||
type: String,
|
||||
default: 'light'
|
||||
}
|
||||
})
|
||||
const state = reactive({
|
||||
fontSize: [],
|
||||
isSetting: false,
|
||||
@ -55,15 +71,9 @@ const state = reactive({
|
||||
const initForm = () => {
|
||||
// do
|
||||
}
|
||||
const themeChange = styleKey => {
|
||||
dvMainStore.canvasStyleData.component.filterStyle = cloneDeep(filterStyle.value)
|
||||
adaptCurThemeFilterStyleAll(styleKey)
|
||||
snapshotStore.recordSnapshotCache('filterStyleSimpleSelector-themeChange')
|
||||
}
|
||||
|
||||
const handleHorizontalChange = (type, horizontal = 'titleLayout') => {
|
||||
filterStyle.value[horizontal] = type
|
||||
themeChange(horizontal)
|
||||
const themeChange = () => {
|
||||
dvMainStore.canvasStyleData.component.seniorStyleSetting = cloneDeep(seniorStyleSetting.value)
|
||||
snapshotStore.recordSnapshotCache('seniorStyleSettingSimpleSelector-themeChange')
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
@ -106,4 +116,12 @@ onMounted(() => {
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
.form-item-dark {
|
||||
:deep(.ed-form-item__label) {
|
||||
color: #6a6a6a;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -10,6 +10,7 @@ import { useEmitt } from '@/hooks/web/useEmitt'
|
||||
import ComponentColorSelector from '@/components/dashboard/subject-setting/dashboard-style/ComponentColorSelector.vue'
|
||||
import OverallSetting from '@/components/dashboard/subject-setting/dashboard-style/OverallSetting.vue'
|
||||
import CanvasBackground from '@/components/visualization/component-background/CanvasBackground.vue'
|
||||
import SeniorStyleSetting from '@/components/dashboard/subject-setting/dashboard-style/SeniorStyleSetting.vue'
|
||||
const dvMainStore = dvMainStoreWithOut()
|
||||
const snapshotStore = snapshotStoreWithOut()
|
||||
const { canvasStyleData, canvasViewInfo } = storeToRefs(dvMainStore)
|
||||
@ -102,6 +103,14 @@ onMounted(() => {
|
||||
<el-collapse-item effect="dark" title="刷新配置" name="overallSetting">
|
||||
<overall-setting themes="dark" />
|
||||
</el-collapse-item>
|
||||
<el-collapse-item
|
||||
effect="dark"
|
||||
title="高级样式设置"
|
||||
name="seniorStyleSetting"
|
||||
class="no-padding no-border-bottom"
|
||||
>
|
||||
<senior-style-setting themes="dark"></senior-style-setting>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -42,6 +42,7 @@ import PointShadow from '@/components/data-visualization/canvas/PointShadow.vue'
|
||||
import DragInfo from '@/components/visualization/common/DragInfo.vue'
|
||||
import { activeWatermark } from '@/components/watermark/watermark'
|
||||
import { personInfoApi } from '@/api/user'
|
||||
import ComponentHangPopver from '@/custom-component/independent-hang/ComponentHangPopver.vue'
|
||||
const snapshotStore = snapshotStoreWithOut()
|
||||
const dvMainStore = dvMainStoreWithOut()
|
||||
const composeStore = composeStoreWithOut()
|
||||
|
@ -236,6 +236,23 @@ const initWatermark = (waterDomId = 'preview-canvas-main') => {
|
||||
}
|
||||
}
|
||||
|
||||
// 目标校验: 需要校验targetSourceId 是否是当前可视化资源ID
|
||||
const winMsgHandle = event => {
|
||||
console.info('PostMessage Params Received')
|
||||
const msgInfo = event.data
|
||||
// 校验targetSourceId
|
||||
if (
|
||||
msgInfo &&
|
||||
msgInfo.type === 'attachParams' &&
|
||||
msgInfo.targetSourceId === dvInfo.value.id + ''
|
||||
) {
|
||||
const attachParam = msgInfo.params
|
||||
if (attachParam) {
|
||||
dvMainStore.addOuterParamsFilter(attachParam, componentData.value, 'outer')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initRefreshTimer()
|
||||
resetLayout()
|
||||
@ -245,10 +262,12 @@ onMounted(() => {
|
||||
restore()
|
||||
initWatermark()
|
||||
})
|
||||
window.addEventListener('message', winMsgHandle)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
clearInterval(refreshTimer.value)
|
||||
window.removeEventListener('message', winMsgHandle)
|
||||
})
|
||||
|
||||
const userViewEnlargeOpen = (opt, item) => {
|
||||
|
@ -210,7 +210,7 @@ service.interceptors.response.use(
|
||||
) {
|
||||
ElMessage({
|
||||
type: 'error',
|
||||
message: error.message,
|
||||
message: error.response?.data?.msg ? error.response?.data?.msg : error.message,
|
||||
showClose: true
|
||||
})
|
||||
} else if (error?.config?.url.startsWith('/xpackComponent/content')) {
|
||||
|
@ -211,6 +211,7 @@ const list = [
|
||||
propValue: '',
|
||||
icon: 'other_text',
|
||||
innerType: 'VQuery',
|
||||
isHang: false,
|
||||
x: 1,
|
||||
y: 1,
|
||||
sizeX: 72,
|
||||
|
@ -3,7 +3,7 @@
|
||||
<div v-if="element.frameLinks.src" class="main-frame">
|
||||
<iframe
|
||||
v-if="state.frameShow"
|
||||
id="iframe"
|
||||
:id="'iframe-' + element.id"
|
||||
:src="element.frameLinks.src"
|
||||
scrolling="auto"
|
||||
frameborder="0"
|
||||
|
@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<div class="hang-main" @keydown.stop @keyup.stop>
|
||||
<div :key="index" v-for="(config, index) in hangComponentData">
|
||||
<component
|
||||
:is="findComponent(config['component'])"
|
||||
:view="canvasViewInfo[config['id']]"
|
||||
ref="component"
|
||||
class="component"
|
||||
:element="config"
|
||||
:scale="deepScale"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, toRefs } from 'vue'
|
||||
import findComponent from '@/utils/components'
|
||||
const props = defineProps({
|
||||
hangComponentData: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
canvasViewInfo: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
scale: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 100
|
||||
}
|
||||
})
|
||||
|
||||
const { hangComponentData, scale } = toRefs(props)
|
||||
const deepScale = computed(() => scale.value / 100)
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.hang-main {
|
||||
overflow: auto;
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,58 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, toRefs } from 'vue'
|
||||
import ComponentHang from '@/custom-component/independent-hang/ComponentHang.vue'
|
||||
|
||||
const props = defineProps({
|
||||
componentData: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
canvasViewInfo: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
themes: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: 'dark'
|
||||
}
|
||||
})
|
||||
|
||||
const { componentData, canvasViewInfo } = toRefs(props)
|
||||
|
||||
const hangComponentData = computed(() =>
|
||||
componentData.value.filter(ele => {
|
||||
return ele.component === 'VQuery' && ele.isHang === true
|
||||
})
|
||||
)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-popover
|
||||
width="300"
|
||||
trigger="click"
|
||||
:show-arrow="false"
|
||||
:popper-class="'custom-popover-' + themes"
|
||||
>
|
||||
<template #reference>
|
||||
<div class="reference-position">
|
||||
<el-button style="margin-right: 16px">隐藏按钮</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<div>
|
||||
<component-hang
|
||||
:hang-component-data="hangComponentData"
|
||||
:canvas-view-info="canvasViewInfo"
|
||||
:scale="100"
|
||||
>
|
||||
</component-hang>
|
||||
</div>
|
||||
</el-popover>
|
||||
</template>
|
||||
<style lang="less" scoped>
|
||||
.reference-position {
|
||||
position: absolute;
|
||||
right: 100px;
|
||||
top: 100px;
|
||||
}
|
||||
</style>
|
@ -150,8 +150,7 @@ const getOperator = (
|
||||
conditionValueF,
|
||||
conditionValueOperatorS,
|
||||
conditionValueS,
|
||||
firstLoad,
|
||||
optionValueSource
|
||||
firstLoad
|
||||
) => {
|
||||
const valueF = firstLoad ? defaultConditionValueF : conditionValueF
|
||||
const valueS = firstLoad ? defaultConditionValueS : conditionValueS
|
||||
@ -170,11 +169,7 @@ const getOperator = (
|
||||
return valueF === '' ? operatorS : operatorF
|
||||
}
|
||||
|
||||
return [1, 7].includes(+displayType)
|
||||
? 'between'
|
||||
: multiple || optionValueSource === 1
|
||||
? 'in'
|
||||
: 'eq'
|
||||
return [1, 7].includes(+displayType) ? 'between' : multiple ? 'in' : 'eq'
|
||||
}
|
||||
|
||||
export const searchQuery = (queryComponentList, filter, curComponentId, firstLoad) => {
|
||||
@ -304,8 +299,7 @@ export const searchQuery = (queryComponentList, filter, curComponentId, firstLoa
|
||||
conditionValueF,
|
||||
conditionValueOperatorS,
|
||||
conditionValueS,
|
||||
firstLoad,
|
||||
optionValueSource
|
||||
firstLoad
|
||||
)
|
||||
if (result?.length) {
|
||||
filter.push({
|
||||
|
@ -8,6 +8,7 @@ import Main from './components/Main.vue'
|
||||
import CollapseBar from './components/CollapseBar.vue'
|
||||
import { ElContainer } from 'element-plus-secondary'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { XpackComponent } from '@/components/plugin'
|
||||
const route = useRoute()
|
||||
const systemMenu = computed(() => route.path.includes('system'))
|
||||
const settingMenu = computed(() => route.path.includes('sys-setting'))
|
||||
@ -49,6 +50,7 @@ const setCollapse = () => {
|
||||
></Main>
|
||||
</el-container>
|
||||
</div>
|
||||
<XpackComponent jsname="L2NvbXBvbmVudC9sb2dpbi9Qd2RJbnZhbGlkVGlwcw==" />
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
@ -280,7 +280,7 @@ export default {
|
||||
remark: '备注',
|
||||
column_name: '字段名',
|
||||
field_type: '字段类型',
|
||||
field_description: '字段描述',
|
||||
field_description: '字段备注',
|
||||
dl: '数据湖',
|
||||
other: '其他',
|
||||
local_file: '本地文件',
|
||||
@ -315,6 +315,7 @@ export default {
|
||||
datasource_url: '地址',
|
||||
please_input_datasource_url: '请输入 Elasticsearch 地址,如: http://es_host:es_port',
|
||||
please_input_data_base: '请输入数据库名称',
|
||||
please_input_jdbc_url: '请输入JDBC 连接',
|
||||
please_select_oracle_type: '选择连接类型',
|
||||
please_input_user_name: '请输入用户名',
|
||||
please_input_password: '请输入密码',
|
||||
@ -368,6 +369,7 @@ export default {
|
||||
extract: '抽取模式',
|
||||
all_compute_mode: '直连、抽取模式',
|
||||
extra_params: '额外的JDBC连接字符串',
|
||||
jdbcUrl: 'JDBC 连接',
|
||||
please_input_dataPath: '请输入 JsonPath 数据路径',
|
||||
show_api_data: '查看API数据结构',
|
||||
warning: '包含无效数据表',
|
||||
@ -423,7 +425,7 @@ export default {
|
||||
no_data_table: '暂无数据表',
|
||||
on_the_left: '请在左侧选择数据源',
|
||||
create_dataset: '创建数据集',
|
||||
table_description: '表描述',
|
||||
table_description: '表备注',
|
||||
relational_database: '关系型数据库',
|
||||
data_warehouse_lake: '数仓/数据湖',
|
||||
non_relational_database: '非关系型数据库',
|
||||
@ -721,6 +723,7 @@ export default {
|
||||
chart_pie_rose: '玫瑰图',
|
||||
chart_pie_donut_rose: '玫瑰环形图',
|
||||
chart_funnel: '漏斗图',
|
||||
chart_sankey: '桑基图',
|
||||
chart_radar: '雷达图',
|
||||
chart_gauge: '仪表盘',
|
||||
chart_map: '地图',
|
||||
@ -795,6 +798,8 @@ export default {
|
||||
chart_data: '数据',
|
||||
chart_style: '样式',
|
||||
drag_block_type_axis: '类别轴',
|
||||
drag_block_type_axis_start: '源',
|
||||
drag_block_type_axis_end: '目的',
|
||||
drag_block_value_axis: '值轴',
|
||||
drag_block_value_start: '开始值',
|
||||
drag_block_value_end: '结束值',
|
||||
@ -1562,6 +1567,7 @@ export default {
|
||||
day_limit: '天不能小于1,大于31'
|
||||
},
|
||||
commons: {
|
||||
test_connect: '测试连接',
|
||||
consanguinity: '血缘关系',
|
||||
collapse_navigation: '收起导航',
|
||||
operate_cancelled: '已取消操作',
|
||||
@ -2211,7 +2217,10 @@ export default {
|
||||
frontTimeOut: '请求超时时间(秒)',
|
||||
logLiveTime: '操作日志保留时间(天)',
|
||||
platformOid: '第三方平台用户组织',
|
||||
platformRid: '第三方平台用户角色'
|
||||
platformRid: '第三方平台用户角色',
|
||||
pwdStrategy: '开启密码策略',
|
||||
dip: '禁用初始密码',
|
||||
pvp: '密码有效期'
|
||||
},
|
||||
setting_email: {
|
||||
title: '邮件设置',
|
||||
|
@ -5,10 +5,15 @@ import { interactiveStoreWithOut } from '@/store/modules/interactive'
|
||||
import { useEmbedded } from '@/store/modules/embedded'
|
||||
import { check } from '@/utils/CrossPermission'
|
||||
import { useCache } from '@/hooks/web/useCache'
|
||||
import { getOuterParamsInfo } from '@/api/visualization/outerParams'
|
||||
import { ElMessage } from 'element-plus-secondary'
|
||||
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
const { wsCache } = useCache()
|
||||
const interactiveStore = interactiveStoreWithOut()
|
||||
const embeddedStore = useEmbedded()
|
||||
const dashboardPreview = ref(null)
|
||||
const { t } = useI18n()
|
||||
const state = reactive({
|
||||
canvasDataPreview: null,
|
||||
canvasStylePreview: null,
|
||||
@ -16,6 +21,8 @@ const state = reactive({
|
||||
dvInfo: null,
|
||||
curPreviewGap: 0
|
||||
})
|
||||
const dvMainStore = dvMainStoreWithOut()
|
||||
|
||||
const checkPer = async resourceId => {
|
||||
if (!window.DataEaseBi || !resourceId) {
|
||||
return true
|
||||
@ -30,6 +37,23 @@ onBeforeMount(async () => {
|
||||
if (!checkResult) {
|
||||
return
|
||||
}
|
||||
|
||||
// 添加外部参数
|
||||
let attachParam
|
||||
await getOuterParamsInfo(embeddedStore.dvId).then(rsp => {
|
||||
dvMainStore.setNowPanelOuterParamsInfo(rsp.data)
|
||||
})
|
||||
|
||||
// div嵌入
|
||||
if (embeddedStore.outerParams) {
|
||||
try {
|
||||
attachParam = JSON.parse(embeddedStore.outerParams)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
ElMessage.error(t('visualization.outer_param_decode_error'))
|
||||
}
|
||||
}
|
||||
|
||||
initCanvasData(
|
||||
embeddedStore.dvId,
|
||||
embeddedStore.busiFlag,
|
||||
@ -48,6 +72,9 @@ onBeforeMount(async () => {
|
||||
nextTick(() => {
|
||||
dashboardPreview.value.restore()
|
||||
})
|
||||
if (attachParam) {
|
||||
dvMainStore.addOuterParamsFilter(attachParam, canvasDataResult, 'outer')
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
|
@ -5,21 +5,41 @@ import { interactiveStoreWithOut } from '@/store/modules/interactive'
|
||||
import { useEmbedded } from '@/store/modules/embedded'
|
||||
import { check } from '@/utils/CrossPermission'
|
||||
import { useCache } from '@/hooks/web/useCache'
|
||||
import { getOuterParamsInfo } from '@/api/visualization/outerParams'
|
||||
import { ElMessage } from 'element-plus-secondary'
|
||||
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
const { wsCache } = useCache()
|
||||
const interactiveStore = interactiveStoreWithOut()
|
||||
const embeddedStore = useEmbedded()
|
||||
const config = ref()
|
||||
const viewInfo = ref()
|
||||
const userViewEnlargeRef = ref()
|
||||
const dvMainStore = dvMainStoreWithOut()
|
||||
const { t } = useI18n()
|
||||
|
||||
const state = reactive({
|
||||
canvasDataPreview: null,
|
||||
canvasStylePreview: null,
|
||||
canvasViewInfoPreview: null,
|
||||
dvInfo: null,
|
||||
curPreviewGap: 0
|
||||
curPreviewGap: 0,
|
||||
chartId: null
|
||||
})
|
||||
|
||||
// 目标校验: 需要校验targetSourceId 是否是当前可视化资源ID
|
||||
const winMsgHandle = event => {
|
||||
console.info('PostMessage Params Received')
|
||||
const msgInfo = event.data
|
||||
// 校验targetSourceId
|
||||
if (msgInfo && msgInfo.type === 'attachParams' && msgInfo.targetSourceId === state.chartId + '') {
|
||||
const attachParam = msgInfo.params
|
||||
if (attachParam) {
|
||||
dvMainStore.addOuterParamsFilter(attachParam, state.canvasDataPreview, 'outer')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const checkPer = async resourceId => {
|
||||
if (!window.DataEaseBi || !resourceId) {
|
||||
return true
|
||||
@ -34,6 +54,25 @@ onBeforeMount(async () => {
|
||||
if (!checkResult) {
|
||||
return
|
||||
}
|
||||
state.chartId = embeddedStore.dvId
|
||||
window.addEventListener('message', winMsgHandle)
|
||||
|
||||
// 添加外部参数
|
||||
let attachParam
|
||||
await getOuterParamsInfo(embeddedStore.dvId).then(rsp => {
|
||||
dvMainStore.setNowPanelOuterParamsInfo(rsp.data)
|
||||
})
|
||||
|
||||
// div嵌入
|
||||
if (embeddedStore.outerParams) {
|
||||
try {
|
||||
attachParam = JSON.parse(embeddedStore.outerParams)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
ElMessage.error(t('visualization.outer_param_decode_error'))
|
||||
}
|
||||
}
|
||||
|
||||
initCanvasDataPrepare(
|
||||
embeddedStore.dvId,
|
||||
embeddedStore.busiFlag,
|
||||
@ -49,6 +88,9 @@ onBeforeMount(async () => {
|
||||
state.canvasViewInfoPreview = canvasViewInfoPreview
|
||||
state.dvInfo = dvInfo
|
||||
state.curPreviewGap = curPreviewGap
|
||||
if (attachParam) {
|
||||
dvMainStore.addOuterParamsFilter(attachParam, canvasDataResult)
|
||||
}
|
||||
|
||||
viewInfo.value = canvasViewInfoPreview[embeddedStore.chartId]
|
||||
;(
|
||||
|
@ -70,6 +70,7 @@ const setupAll = async (
|
||||
dom: string,
|
||||
type: string,
|
||||
busiFlag: string,
|
||||
outerParams: string,
|
||||
token: string,
|
||||
baseUrl: string,
|
||||
dvId: string,
|
||||
@ -86,6 +87,7 @@ const setupAll = async (
|
||||
const embeddedStore = useEmbedded()
|
||||
embeddedStore.setType(type)
|
||||
embeddedStore.setBusiFlag(busiFlag)
|
||||
embeddedStore.setOuterParams(outerParams)
|
||||
embeddedStore.setToken(token)
|
||||
embeddedStore.setBaseUrl(baseUrl)
|
||||
embeddedStore.setDvId(dvId)
|
||||
@ -129,6 +131,7 @@ class DataEaseBi {
|
||||
| 'DashboardPanel'
|
||||
dvId: string
|
||||
busiFlag: 'dashboard' | 'dataV'
|
||||
outerParams: string
|
||||
resourceId: string
|
||||
pid: string
|
||||
chartId: string
|
||||
@ -139,6 +142,7 @@ class DataEaseBi {
|
||||
this.type = type
|
||||
this.token = options.token
|
||||
this.busiFlag = options.busiFlag
|
||||
this.outerParams = options.outerParams
|
||||
this.baseUrl = options.baseUrl
|
||||
this.dvId = options.dvId
|
||||
this.pid = options.pid
|
||||
@ -152,6 +156,7 @@ class DataEaseBi {
|
||||
this.deOptions.container,
|
||||
this.type,
|
||||
this.busiFlag,
|
||||
this.outerParams,
|
||||
this.token,
|
||||
this.baseUrl,
|
||||
this.dvId,
|
||||
@ -169,6 +174,7 @@ class DataEaseBi {
|
||||
const embeddedStore = useEmbedded()
|
||||
embeddedStore.setType(null)
|
||||
embeddedStore.setBusiFlag(null)
|
||||
embeddedStore.setOuterParams(null)
|
||||
embeddedStore.setToken(null)
|
||||
embeddedStore.setBaseUrl(null)
|
||||
embeddedStore.setDvId(null)
|
||||
@ -179,6 +185,7 @@ class DataEaseBi {
|
||||
this.type = null
|
||||
this.token = null
|
||||
this.busiFlag = null
|
||||
this.outerParams = null
|
||||
this.baseUrl = null
|
||||
this.dvId = null
|
||||
this.pid = null
|
||||
|
@ -7,7 +7,7 @@ import { usePermissionStoreWithOut, pathValid, getFirstAuthMenu } from '@/store/
|
||||
import { usePageLoading } from '@/hooks/web/usePageLoading'
|
||||
import { getRoleRouters } from '@/api/common'
|
||||
import { useCache } from '@/hooks/web/useCache'
|
||||
import { isMobile, checkPlatform } from '@/utils/utils'
|
||||
import { isMobile, checkPlatform, isLarkPlatform, isPlatformClient } from '@/utils/utils'
|
||||
import { interactiveStoreWithOut } from '@/store/modules/interactive'
|
||||
import { useAppearanceStoreWithOut } from '@/store/modules/appearance'
|
||||
import { useEmbedded } from '@/store/modules/embedded'
|
||||
@ -34,7 +34,7 @@ router.beforeEach(async (to, from, next) => {
|
||||
loadDone()
|
||||
if (to.name === 'link') {
|
||||
window.location.href = window.origin + '/mobile.html#' + to.path
|
||||
} else {
|
||||
} else if (!isPlatformClient() && !isLarkPlatform()) {
|
||||
window.location.href = window.origin + '/mobile.html#/index'
|
||||
}
|
||||
}
|
||||
|
@ -362,8 +362,15 @@ export const dvMainStore = defineStore('dataVisualization', {
|
||||
}
|
||||
//如果当前的组件是UserView 图表,则想canvasView中增加一项 UserView ID 和componentID保持一致
|
||||
if (component.component === 'UserView') {
|
||||
const defaultConfig = JSON.parse(JSON.stringify(BASE_VIEW_CONFIG))
|
||||
if (component.innerType === 'bar-range') {
|
||||
defaultConfig.customStyle.xAxis.axisLine.show = false
|
||||
defaultConfig.customStyle.xAxis.splitLine.show = true
|
||||
defaultConfig.customStyle.yAxis.axisLine.show = true
|
||||
defaultConfig.customStyle.yAxis.splitLine.show = false
|
||||
}
|
||||
let newView = {
|
||||
...JSON.parse(JSON.stringify(BASE_VIEW_CONFIG)),
|
||||
...defaultConfig,
|
||||
id: component.id,
|
||||
type: component.innerType,
|
||||
render: component.render
|
||||
@ -843,10 +850,10 @@ export const dvMainStore = defineStore('dataVisualization', {
|
||||
})
|
||||
},
|
||||
// 添加外部参数的过滤条件
|
||||
addOuterParamsFilter(params) {
|
||||
addOuterParamsFilter(params, curComponentData = this.componentData, source = 'inner') {
|
||||
// params 结构 {key1:value1,key2:value2}
|
||||
const curComponentData = this.componentData
|
||||
if (params) {
|
||||
const preActiveComponentIds = []
|
||||
const trackInfo = this.nowPanelOuterParamsInfo
|
||||
for (let index = 0; index < curComponentData.length; index++) {
|
||||
const element = curComponentData[index]
|
||||
@ -899,6 +906,7 @@ export const dvMainStore = defineStore('dataVisualization', {
|
||||
// 不存在该条件 且 条件有效 直接保存该条件
|
||||
// !filterExist && vValid && currentFilters.push(condition)
|
||||
currentFilters.push(condition)
|
||||
preActiveComponentIds.push(element.id)
|
||||
}
|
||||
if (element.component === 'VQuery') {
|
||||
element.propValue.forEach(filterItem => {
|
||||
@ -944,6 +952,11 @@ export const dvMainStore = defineStore('dataVisualization', {
|
||||
curComponentData[index] = element
|
||||
})
|
||||
}
|
||||
if (source === 'outer') {
|
||||
preActiveComponentIds.forEach(viewId => {
|
||||
useEmitt().emitter.emit('query-data-' + viewId)
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
trackFilterCursor(element, checkQDList, trackInfo, preActiveComponentIds, viewId) {
|
||||
|
@ -4,6 +4,7 @@ interface AppState {
|
||||
type: string
|
||||
token: string
|
||||
busiFlag: string
|
||||
outerParams: string
|
||||
baseUrl: string
|
||||
dvId: string
|
||||
pid: string
|
||||
@ -17,6 +18,7 @@ export const userStore = defineStore('embedded', {
|
||||
type: '',
|
||||
token: '',
|
||||
busiFlag: '',
|
||||
outerParams: '',
|
||||
baseUrl: '',
|
||||
dvId: '',
|
||||
pid: '',
|
||||
@ -34,6 +36,9 @@ export const userStore = defineStore('embedded', {
|
||||
getBusiFlag(): string {
|
||||
return this.busiFlag
|
||||
},
|
||||
getOuterParams(): string {
|
||||
return this.outerParams
|
||||
},
|
||||
getBaseUrl(): string {
|
||||
return this.baseUrl
|
||||
},
|
||||
@ -53,6 +58,7 @@ export const userStore = defineStore('embedded', {
|
||||
return {
|
||||
embeddedToken: this.token,
|
||||
busiFlag: this.busiFlag,
|
||||
outerParams: this.outerParams,
|
||||
type: this.type,
|
||||
dvId: this.dvId,
|
||||
chartId: this.chartId,
|
||||
@ -71,6 +77,9 @@ export const userStore = defineStore('embedded', {
|
||||
setBusiFlag(busiFlag: string) {
|
||||
this.busiFlag = busiFlag
|
||||
},
|
||||
setOuterParams(outerParams: string) {
|
||||
this.outerParams = outerParams
|
||||
},
|
||||
setBaseUrl(baseUrl: string) {
|
||||
this.baseUrl = baseUrl
|
||||
},
|
||||
@ -90,6 +99,7 @@ export const userStore = defineStore('embedded', {
|
||||
this.type = data['type']
|
||||
this.token = data['embeddedToken']
|
||||
this.busiFlag = data['busiFlag']
|
||||
this.outerParams = data['outerParams']
|
||||
this.dvId = data['dvId']
|
||||
this.chartId = data['chartId']
|
||||
this.pid = data['pid']
|
||||
|
@ -39,7 +39,11 @@ export const logoutHandler = (justClean?: boolean) => {
|
||||
const removeCache = () => {
|
||||
const keys = Object.keys(wsCache['storage'])
|
||||
keys.forEach(key => {
|
||||
if (key.startsWith('de-plugin-') || key === 'de-platform-client') {
|
||||
if (
|
||||
key.startsWith('de-plugin-') ||
|
||||
key === 'de-platform-client' ||
|
||||
key === 'pwd-validity-period'
|
||||
) {
|
||||
wsCache.delete(key)
|
||||
}
|
||||
})
|
||||
|
@ -127,9 +127,14 @@ const update = (licKey: string) => {
|
||||
})
|
||||
}
|
||||
|
||||
const autoSync = ref(true)
|
||||
const checkFree = () => {
|
||||
checkFreeApi().then(res => {
|
||||
if (res.data) {
|
||||
if (autoSync.value) {
|
||||
syncFree()
|
||||
return
|
||||
}
|
||||
// do something
|
||||
const title = '存在未同步的资源数据,请谨慎操作!'
|
||||
const childrenDomList = [h('strong', null, title)]
|
||||
|
@ -9,6 +9,10 @@ const state = {
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
element: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
chart: {
|
||||
type: Object,
|
||||
required: true
|
||||
@ -24,7 +28,7 @@ const props = defineProps({
|
||||
})
|
||||
const predefineColors = COLOR_PANEL
|
||||
|
||||
const { chart, commonBackgroundPop } = toRefs(props)
|
||||
const { element, chart, commonBackgroundPop } = toRefs(props)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -33,6 +37,11 @@ const { chart, commonBackgroundPop } = toRefs(props)
|
||||
<el-collapse v-model="state.styleActiveNames" class="style-collapse">
|
||||
<el-collapse-item :effect="themes" name="component" :title="t('visualization.module')">
|
||||
<el-form label-position="top">
|
||||
<el-form-item class="form-item margin-bottom-8" :class="'form-item-' + themes">
|
||||
<el-checkbox :effect="themes" v-model="element.isHang" size="small">
|
||||
隐藏组件
|
||||
</el-checkbox>
|
||||
</el-form-item>
|
||||
<el-form-item class="form-item margin-bottom-8" :class="'form-item-' + themes">
|
||||
<el-checkbox
|
||||
:effect="themes"
|
||||
|
@ -52,6 +52,12 @@ const fontSizeList = computed(() => {
|
||||
return arr
|
||||
})
|
||||
|
||||
const splitLineStyle = [
|
||||
{ label: t('chart.line_type_solid'), value: 'solid' },
|
||||
{ label: t('chart.line_type_dashed'), value: 'dashed' },
|
||||
{ label: t('chart.line_type_dotted'), value: 'dotted' }
|
||||
]
|
||||
|
||||
const isBarRangeTime = computed<boolean>(() => {
|
||||
if (props.chart.type === 'bar-range') {
|
||||
const tempYAxis = props.chart.yAxis[0]
|
||||
@ -326,6 +332,22 @@ onMounted(() => {
|
||||
/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<el-form-item class="form-item" :class="'form-item-' + themes">
|
||||
<el-select
|
||||
:disabled="!state.axisForm.splitLine.show"
|
||||
style="width: 108px"
|
||||
:effect="props.themes"
|
||||
v-model="state.axisForm.splitLine.lineStyle.style"
|
||||
@change="changeAxisStyle('splitLine.lineStyle')"
|
||||
>
|
||||
<el-option
|
||||
v-for="option in splitLineStyle"
|
||||
:key="option.value"
|
||||
:value="option.value"
|
||||
:label="option.label"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<el-divider class="m-divider" :class="'m-divider--' + themes" />
|
||||
<el-form-item
|
||||
|
@ -52,6 +52,12 @@ const fontSizeList = computed(() => {
|
||||
return arr
|
||||
})
|
||||
|
||||
const splitLineStyle = [
|
||||
{ label: t('chart.line_type_solid'), value: 'solid' },
|
||||
{ label: t('chart.line_type_dashed'), value: 'dashed' },
|
||||
{ label: t('chart.line_type_dotted'), value: 'dotted' }
|
||||
]
|
||||
|
||||
const changeAxisStyle = prop => {
|
||||
if (
|
||||
state.axisForm.axisValue.splitCount &&
|
||||
@ -303,6 +309,22 @@ onMounted(() => {
|
||||
/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<el-form-item class="form-item" :class="'form-item-' + themes">
|
||||
<el-select
|
||||
:disabled="!state.axisForm.splitLine.show"
|
||||
style="width: 108px"
|
||||
:effect="props.themes"
|
||||
v-model="state.axisForm.splitLine.lineStyle.style"
|
||||
@change="changeAxisStyle('splitLine.lineStyle')"
|
||||
>
|
||||
<el-option
|
||||
v-for="option in splitLineStyle"
|
||||
:key="option.value"
|
||||
:value="option.value"
|
||||
:label="option.label"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<el-divider class="m-divider" :class="'m-divider--' + themes" />
|
||||
<el-form-item
|
||||
|
@ -1512,15 +1512,20 @@ const drop = (ev: MouseEvent, type = 'xAxis') => {
|
||||
</div>
|
||||
<div v-if="!canvasCollapse.chartAreaCollapse" style="width: 240px" class="view-panel-row">
|
||||
<el-row class="editor-title">
|
||||
<span class="name-area" @dblclick="editComponentName" id="component-name">{{
|
||||
view.title
|
||||
}}</span>
|
||||
<span
|
||||
class="name-area"
|
||||
:class="{ 'component-name-dark': themes === 'dark' }"
|
||||
@dblclick="editComponentName"
|
||||
id="component-name"
|
||||
>{{ view.title }}</span
|
||||
>
|
||||
</el-row>
|
||||
|
||||
<el-row style="height: calc(100vh - 110px); overflow-y: auto">
|
||||
<div class="query-style-tab" v-if="view.type === 'VQuery'">
|
||||
<div class="query-style-tab" v-if="view.type === 'VQuery' && curComponent">
|
||||
<div style="padding-top: 1px">
|
||||
<VQueryChartStyle
|
||||
:element="curComponent"
|
||||
:common-background-pop="curComponent?.commonBackground"
|
||||
:chart="view"
|
||||
:themes="themes"
|
||||
@ -2168,7 +2173,7 @@ const drop = (ev: MouseEvent, type = 'xAxis') => {
|
||||
</div>
|
||||
<div
|
||||
class="tree-btn"
|
||||
:class="isFilterActive && 'active'"
|
||||
:class="{ 'tree-btn--dark': themes === 'dark', active: isFilterActive }"
|
||||
@click="openTreeFilter"
|
||||
>
|
||||
<el-icon>
|
||||
@ -2889,6 +2894,7 @@ const drop = (ev: MouseEvent, type = 'xAxis') => {
|
||||
<XpackComponent ref="openHandler" jsname="L2NvbXBvbmVudC9lbWJlZGRlZC1pZnJhbWUvT3BlbkhhbmRsZXI=" />
|
||||
<Teleport v-if="componentNameEdit" :to="'#component-name'">
|
||||
<input
|
||||
:effect="themes"
|
||||
width="100%"
|
||||
@change="onComponentNameChange"
|
||||
ref="componentNameInput"
|
||||
@ -3483,6 +3489,10 @@ span {
|
||||
cursor: pointer;
|
||||
justify-content: center;
|
||||
font-size: 12px;
|
||||
&.tree-btn--dark {
|
||||
background: rgba(235, 235, 235, 0.05);
|
||||
border-color: #5f5f5f;
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: #3370ff;
|
||||
@ -4019,4 +4029,19 @@ span {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.component-name-dark {
|
||||
input {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
color: @dv-canvas-main-font-color;
|
||||
background-color: #050e21;
|
||||
outline: none;
|
||||
border: 1px solid #295acc;
|
||||
border-radius: 4px;
|
||||
padding: 0 4px;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -158,7 +158,8 @@ export const SENIOR_STYLE_SETTING_LIGHT = {
|
||||
|
||||
export const SENIOR_STYLE_SETTING_DARK = {
|
||||
linkageIconColor: '#ffffff',
|
||||
drillLayerColor: '#ffffff'
|
||||
drillLayerColor: '#ffffff',
|
||||
pagerColor: '#ffffff'
|
||||
}
|
||||
|
||||
export const FILTER_COMMON_STYLE_BASE = {
|
||||
@ -1335,6 +1336,13 @@ export const CHART_TYPE_CONFIGS = [
|
||||
value: 'funnel',
|
||||
title: t('chart.chart_funnel'),
|
||||
icon: 'funnel'
|
||||
},
|
||||
{
|
||||
render: 'antv',
|
||||
category: 'relation',
|
||||
value: 'sankey',
|
||||
title: t('chart.chart_sankey'),
|
||||
icon: 'sankey'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -0,0 +1,38 @@
|
||||
export const SANKEY_EDITOR_PROPERTY: EditorProperty[] = [
|
||||
'background-overall-component',
|
||||
'basic-style-selector',
|
||||
'label-selector',
|
||||
'tooltip-selector',
|
||||
'title-selector',
|
||||
'jump-set',
|
||||
'linkage'
|
||||
]
|
||||
|
||||
export const SANKEY_EDITOR_PROPERTY_INNER: EditorPropertyInner = {
|
||||
'background-overall-component': ['all'],
|
||||
'basic-style-selector': ['colors', 'alpha', 'gradient'],
|
||||
'label-selector': ['fontSize', 'color', 'labelFormatter'],
|
||||
'tooltip-selector': ['fontSize', 'color', 'tooltipFormatter'],
|
||||
'title-selector': [
|
||||
'title',
|
||||
'fontSize',
|
||||
'color',
|
||||
'hPosition',
|
||||
'isItalic',
|
||||
'isBolder',
|
||||
'remarkShow',
|
||||
'fontFamily',
|
||||
'letterSpace',
|
||||
'fontShadow'
|
||||
],
|
||||
'function-cfg': ['slider', 'emptyDataStrategy']
|
||||
}
|
||||
|
||||
export const SANKEY_AXIS_TYPE: AxisType[] = [
|
||||
'xAxis',
|
||||
'xAxisExt',
|
||||
'yAxis',
|
||||
'filter',
|
||||
'extLabel',
|
||||
'extTooltip'
|
||||
]
|
@ -0,0 +1,280 @@
|
||||
import {
|
||||
G2PlotChartView,
|
||||
G2PlotDrawOptions
|
||||
} from '@/views/chart/components/js/panel/types/impl/g2plot'
|
||||
import { Sankey, SankeyOptions } from '@antv/g2plot/esm/plots/sankey'
|
||||
import { getPadding, setGradientColor } from '@/views/chart/components/js/panel/common/common_antv'
|
||||
import { cloneDeep, get } from 'lodash-es'
|
||||
import { flow, hexColorToRGBA, parseJson } from '@/views/chart/components/js/util'
|
||||
import { valueFormatter } from '@/views/chart/components/js/formatter'
|
||||
|
||||
import { Datum } from '@antv/g2plot/esm/types/common'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import {
|
||||
SANKEY_AXIS_TYPE,
|
||||
SANKEY_EDITOR_PROPERTY,
|
||||
SANKEY_EDITOR_PROPERTY_INNER
|
||||
} from '@/views/chart/components/js/panel/charts/others/sankey-common'
|
||||
|
||||
const { t } = useI18n()
|
||||
const DEFAULT_DATA = []
|
||||
|
||||
/**
|
||||
* 区间条形图
|
||||
*/
|
||||
export class RangeBar extends G2PlotChartView<SankeyOptions, Sankey> {
|
||||
axisConfig = {
|
||||
...this['axisConfig'],
|
||||
xAxis: {
|
||||
name: `${t('chart.drag_block_type_axis_start')} / ${t('chart.dimension')}`,
|
||||
limit: 1,
|
||||
type: 'd'
|
||||
},
|
||||
xAxisExt: {
|
||||
name: `${t('chart.drag_block_type_axis_end')} / ${t('chart.dimension')}`,
|
||||
limit: 1,
|
||||
type: 'd'
|
||||
},
|
||||
yAxis: {
|
||||
name: `${t('chart.chart_data')} / ${t('chart.quota')}`,
|
||||
limit: 1,
|
||||
type: 'q'
|
||||
}
|
||||
}
|
||||
properties = SANKEY_EDITOR_PROPERTY
|
||||
propertyInner = {
|
||||
...SANKEY_EDITOR_PROPERTY_INNER,
|
||||
'label-selector': ['color', 'fontSize'],
|
||||
'tooltip-selector': ['fontSize', 'color', 'backgroundColor', 'tooltipFormatter']
|
||||
}
|
||||
axis: AxisType[] = [...SANKEY_AXIS_TYPE]
|
||||
protected baseOptions: SankeyOptions = {
|
||||
data: [],
|
||||
sourceField: 'source',
|
||||
targetField: 'target',
|
||||
weightField: 'value',
|
||||
rawFields: ['dimensionList', 'quotaList'],
|
||||
interactions: [
|
||||
{
|
||||
type: 'legend-active',
|
||||
cfg: {
|
||||
start: [{ trigger: 'legend-item:mouseenter', action: ['element-active:reset'] }],
|
||||
end: [{ trigger: 'legend-item:mouseleave', action: ['element-active:reset'] }]
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'legend-filter',
|
||||
cfg: {
|
||||
start: [
|
||||
{
|
||||
trigger: 'legend-item:click',
|
||||
action: [
|
||||
'list-unchecked:toggle',
|
||||
'data-filter:filter',
|
||||
'element-active:reset',
|
||||
'element-highlight:reset'
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'tooltip',
|
||||
cfg: {
|
||||
start: [{ trigger: 'interval:mousemove', action: 'tooltip:show' }],
|
||||
end: [{ trigger: 'interval:mouseleave', action: 'tooltip:hide' }]
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'active-region',
|
||||
cfg: {
|
||||
start: [{ trigger: 'interval:mousemove', action: 'active-region:show' }],
|
||||
end: [{ trigger: 'interval:mouseleave', action: 'active-region:hide' }]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
drawChart(drawOptions: G2PlotDrawOptions<Sankey>): Sankey {
|
||||
const { chart, container, action } = drawOptions
|
||||
if (!chart.data?.data?.length) {
|
||||
return
|
||||
}
|
||||
// data
|
||||
const data: Array<any> = cloneDeep(chart.data.data)
|
||||
|
||||
data.forEach(d => {
|
||||
if (d.dimensionList) {
|
||||
if (d.dimensionList[0]) {
|
||||
d.source = d.dimensionList[0].value
|
||||
}
|
||||
if (d.dimensionList[1]) {
|
||||
d.target = d.dimensionList[1].value
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// options
|
||||
const initOptions: SankeyOptions = {
|
||||
...this.baseOptions,
|
||||
appendPadding: getPadding(chart),
|
||||
data,
|
||||
nodeSort: (a, b) => {
|
||||
// 这里是前端自己排序
|
||||
if (chart.yAxis && chart.yAxis[0]) {
|
||||
if (chart.yAxis[0].sort === 'asc') {
|
||||
return a.value - b.value
|
||||
} else if (chart.yAxis[0].sort === 'desc') {
|
||||
return b.value - a.value
|
||||
}
|
||||
}
|
||||
|
||||
if (chart.xAxis && chart.xAxis[0] && a.sourceLinks.length > 0) {
|
||||
if (chart.xAxis[0].sort === 'custom_sort' && chart.xAxis[0].customSort) {
|
||||
return (
|
||||
chart.xAxis[0].customSort.indexOf(a.name) - chart.xAxis[0].customSort.indexOf(b.name)
|
||||
)
|
||||
} else if (chart.xAxis[0].sort === 'asc') {
|
||||
return a.name.localeCompare(b.name)
|
||||
} else if (chart.xAxis[0].sort === 'desc') {
|
||||
return b.name.localeCompare(a.name)
|
||||
}
|
||||
}
|
||||
if (chart.xAxisExt && chart.xAxisExt[0] && a.targetLinks.length > 0) {
|
||||
if (chart.xAxisExt[0].sort === 'custom_sort' && chart.xAxisExt[0].customSort) {
|
||||
return (
|
||||
chart.xAxisExt[0].customSort.indexOf(a.name) -
|
||||
chart.xAxisExt[0].customSort.indexOf(b.name)
|
||||
)
|
||||
} else if (chart.xAxisExt[0].sort === 'asc') {
|
||||
return a.name.localeCompare(b.name)
|
||||
} else if (chart.xAxisExt[0].sort === 'desc') {
|
||||
return b.name.localeCompare(a.name)
|
||||
}
|
||||
}
|
||||
|
||||
return b.value - a.value
|
||||
}
|
||||
}
|
||||
|
||||
const options = this.setupOptions(chart, initOptions)
|
||||
|
||||
// 开始渲染
|
||||
const newChart = new Sankey(container, options)
|
||||
|
||||
newChart.on('edge:click', action)
|
||||
|
||||
return newChart
|
||||
}
|
||||
|
||||
protected configTooltip(chart: Chart, options: SankeyOptions): SankeyOptions {
|
||||
let tooltip
|
||||
let customAttr: DeepPartial<ChartAttr>
|
||||
if (chart.customAttr) {
|
||||
customAttr = parseJson(chart.customAttr)
|
||||
// tooltip
|
||||
if (customAttr.tooltip) {
|
||||
const t = JSON.parse(JSON.stringify(customAttr.tooltip))
|
||||
if (t.show) {
|
||||
tooltip = {
|
||||
showTitle: false,
|
||||
showMarkers: false,
|
||||
shared: false,
|
||||
// 内置:node 不显示 tooltip,edge 显示 tooltip
|
||||
showContent: items => {
|
||||
return !get(items, [0, 'data', 'isNode'])
|
||||
},
|
||||
formatter: (datum: Datum) => {
|
||||
const { source, target, value } = datum
|
||||
return {
|
||||
name: source + ' -> ' + target,
|
||||
value: valueFormatter(value, t.tooltipFormatter)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tooltip = false
|
||||
}
|
||||
}
|
||||
}
|
||||
return { ...options, tooltip }
|
||||
}
|
||||
|
||||
protected configBasicStyle(chart: Chart, options: SankeyOptions): SankeyOptions {
|
||||
const basicStyle = parseJson(chart.customAttr).basicStyle
|
||||
|
||||
let color = basicStyle.colors
|
||||
color = color.map(ele => {
|
||||
const tmp = hexColorToRGBA(ele, basicStyle.alpha)
|
||||
if (basicStyle.gradient) {
|
||||
return setGradientColor(tmp, true)
|
||||
} else {
|
||||
return tmp
|
||||
}
|
||||
})
|
||||
|
||||
options = {
|
||||
...options,
|
||||
color
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
setupDefaultOptions(chart: ChartObj): ChartObj {
|
||||
const { customAttr, senior } = chart
|
||||
const { label } = customAttr
|
||||
if (!['left', 'middle', 'right'].includes(label.position)) {
|
||||
label.position = 'middle'
|
||||
}
|
||||
senior.functionCfg.emptyDataStrategy = 'ignoreData'
|
||||
return chart
|
||||
}
|
||||
|
||||
protected configLabel(chart: Chart, options: SankeyOptions): SankeyOptions {
|
||||
const labelAttr = parseJson(chart.customAttr).label
|
||||
if (labelAttr.show) {
|
||||
const label = {
|
||||
//...tmpOptions.label,
|
||||
formatter: ({ name }) => name,
|
||||
callback: (x: number[]) => {
|
||||
const isLast = x[1] === 1 // 最后一列靠边的节点
|
||||
return {
|
||||
style: {
|
||||
fill: labelAttr.color,
|
||||
fontSize: labelAttr.fontSize,
|
||||
textAlign: isLast ? 'end' : 'start'
|
||||
},
|
||||
offsetX: isLast ? -8 : 8
|
||||
}
|
||||
},
|
||||
layout: [{ type: 'hide-overlap' }, { type: 'limit-in-canvas' }]
|
||||
}
|
||||
return {
|
||||
...options,
|
||||
label
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
...options,
|
||||
label: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected setupOptions(chart: Chart, options: SankeyOptions): SankeyOptions {
|
||||
return flow(
|
||||
this.configTheme,
|
||||
this.configBasicStyle,
|
||||
this.configLabel,
|
||||
this.configTooltip,
|
||||
this.configLegend,
|
||||
this.configSlider,
|
||||
this.configAnalyseHorizontal,
|
||||
this.configEmptyDataStrategy
|
||||
)(chart, options)
|
||||
}
|
||||
|
||||
constructor(name = 'sankey') {
|
||||
super(name, DEFAULT_DATA)
|
||||
}
|
||||
}
|
@ -357,7 +357,8 @@ export function getXAxis(chart: Chart) {
|
||||
line: {
|
||||
style: {
|
||||
stroke: a.splitLine.lineStyle.color,
|
||||
lineWidth: a.splitLine.lineStyle.width
|
||||
lineWidth: a.splitLine.lineStyle.width,
|
||||
lineDash: getLineDash(a.splitLine.lineStyle.style)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -435,7 +436,8 @@ export function getYAxis(chart: Chart) {
|
||||
line: {
|
||||
style: {
|
||||
stroke: yAxis.splitLine.lineStyle.color,
|
||||
lineWidth: yAxis.splitLine.lineStyle.width
|
||||
lineWidth: yAxis.splitLine.lineStyle.width,
|
||||
lineDash: getLineDash(yAxis.splitLine.lineStyle.style)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -159,7 +159,6 @@ const renderG2Plot = (chart, chartView: G2PlotChartView<any, any>) => {
|
||||
const dynamicAreaId = ref('')
|
||||
const country = ref('')
|
||||
const appStore = useAppStoreWithOut()
|
||||
const isDataEaseBi = computed(() => appStore.getIsDataEaseBi)
|
||||
const chartContainer = ref<HTMLElement>(null)
|
||||
let mapTimer: number
|
||||
const renderL7Plot = async (chart: ChartObj, chartView: L7PlotChartView<any, any>, callback) => {
|
||||
@ -259,7 +258,7 @@ const trackClick = trackAction => {
|
||||
dvMainStore.addViewTrackFilter(linkageParam)
|
||||
break
|
||||
case 'jump':
|
||||
if (isDataEaseBi.value || mobileInPc.value) return
|
||||
if (mobileInPc.value) return
|
||||
emit('onJumpClick', jumpParam)
|
||||
break
|
||||
default:
|
||||
@ -282,11 +281,7 @@ const trackMenu = computed(() => {
|
||||
jumpCount++
|
||||
}
|
||||
})
|
||||
jumpCount &&
|
||||
view.value?.jumpActive &&
|
||||
!isDataEaseBi.value &&
|
||||
!mobileInPc.value &&
|
||||
trackMenuInfo.push('jump')
|
||||
jumpCount && view.value?.jumpActive && !mobileInPc.value && trackMenuInfo.push('jump')
|
||||
linkageCount && view.value?.linkageActive && trackMenuInfo.push('linkage')
|
||||
view.value.drillFields.length && trackMenuInfo.push('drill')
|
||||
// 如果同时配置jump linkage drill 切配置联动时同时下钻 在实际只显示两个 '跳转' '联动和下钻'
|
||||
|
@ -29,7 +29,8 @@ import { useEmitt } from '@/hooks/web/useEmitt'
|
||||
import { trackBarStyleCheck } from '@/utils/canvasUtils'
|
||||
|
||||
const dvMainStore = dvMainStoreWithOut()
|
||||
const { nowPanelTrackInfo, nowPanelJumpInfo, mobileInPc } = storeToRefs(dvMainStore)
|
||||
const { nowPanelTrackInfo, nowPanelJumpInfo, mobileInPc, canvasStyleData } =
|
||||
storeToRefs(dvMainStore)
|
||||
const { emitter } = useEmitt()
|
||||
|
||||
const props = defineProps({
|
||||
@ -261,8 +262,6 @@ const action = param => {
|
||||
}
|
||||
const appStore = useAppStoreWithOut()
|
||||
|
||||
const isDataEaseBi = computed(() => appStore.getIsDataEaseBi)
|
||||
|
||||
const trackClick = trackAction => {
|
||||
const param = state.pointParam
|
||||
if (!param?.data?.dimensionList) {
|
||||
@ -295,7 +294,7 @@ const trackClick = trackAction => {
|
||||
dvMainStore.addViewTrackFilter(linkageParam)
|
||||
break
|
||||
case 'jump':
|
||||
if (isDataEaseBi.value || mobileInPc.value) return
|
||||
if (mobileInPc.value) return
|
||||
emit('onJumpClick', jumpParam)
|
||||
break
|
||||
default:
|
||||
@ -319,11 +318,7 @@ const trackMenu = computed(() => {
|
||||
jumpCount++
|
||||
}
|
||||
})
|
||||
jumpCount &&
|
||||
view.value?.jumpActive &&
|
||||
!isDataEaseBi.value &&
|
||||
!mobileInPc.value &&
|
||||
trackMenuInfo.push('jump')
|
||||
jumpCount && view.value?.jumpActive && !mobileInPc.value && trackMenuInfo.push('jump')
|
||||
linkageCount && view.value?.linkageActive && trackMenuInfo.push('linkage')
|
||||
view.value.drillFields.length && trackMenuInfo.push('drill')
|
||||
// 如果同时配置jump linkage drill 切配置联动时同时下钻 在实际只显示两个 '跳转' '联动和下钻'
|
||||
@ -420,6 +415,10 @@ const autoHeightStyle = computed(() => {
|
||||
height: 20 * scale.value + 8 + 'px'
|
||||
}
|
||||
})
|
||||
|
||||
const tabStyle = computed(() => [
|
||||
{ '--de-pager-color': canvasStyleData.value.component.seniorStyleSetting.pagerColor }
|
||||
])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@ -435,7 +434,7 @@ const autoHeightStyle = computed(() => {
|
||||
<div style="position: relative; height: 100%" :id="containerId"></div>
|
||||
</div>
|
||||
<el-row :style="autoStyle" v-if="showPage && !isError">
|
||||
<div class="table-page-info">
|
||||
<div class="table-page-info" :style="tabStyle">
|
||||
<div>共{{ state.pageInfo.total }}条</div>
|
||||
<el-pagination
|
||||
class="table-page-content"
|
||||
@ -474,16 +473,16 @@ const autoHeightStyle = computed(() => {
|
||||
height: 20px;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
color: grey;
|
||||
color: var(--de-pager-color);
|
||||
:deep(.table-page-content) {
|
||||
button,
|
||||
button[disabled] {
|
||||
color: grey;
|
||||
color: var(--de-pager-color);
|
||||
background: transparent !important;
|
||||
}
|
||||
ul li {
|
||||
&:not(.is-active) {
|
||||
color: grey;
|
||||
color: var(--de-pager-color);
|
||||
}
|
||||
background: transparent !important;
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
<script lang="tsx" setup>
|
||||
import { reactive, watch } from 'vue'
|
||||
import { computed, reactive, watch } from 'vue'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { reverseColor } from '../util/util'
|
||||
import { ArrowRight } from '@element-plus/icons-vue'
|
||||
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
|
||||
const dvMainStore = dvMainStoreWithOut()
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
@ -24,14 +26,18 @@ const state = reactive({
|
||||
textColor: '#bbbfc4'
|
||||
})
|
||||
|
||||
watch(
|
||||
[() => props.themeStyle?.backgroundColorSelect, () => props.themeStyle?.color],
|
||||
() => {
|
||||
loadThemeStyle()
|
||||
},
|
||||
{ deep: true }
|
||||
const textColor = computed(
|
||||
() => dvMainStore.canvasStyleData.component.seniorStyleSetting.drillLayerColor
|
||||
)
|
||||
|
||||
// watch(
|
||||
// [() => props.themeStyle?.backgroundColorSelect, () => props.themeStyle?.color],
|
||||
// () => {
|
||||
// loadThemeStyle()
|
||||
// },
|
||||
// { deep: true }
|
||||
// )
|
||||
|
||||
const drillJump = index => {
|
||||
if (index < props.drillFilters.length) {
|
||||
emit('onDrillJump', index)
|
||||
@ -67,7 +73,7 @@ const loadThemeStyle = () => {
|
||||
<div v-if="props.drillFilters && props.drillFilters.length > 0" class="drill">
|
||||
<el-breadcrumb :separator-icon="ArrowRight" class="drill-style">
|
||||
<el-breadcrumb-item class="drill-item" @click="drillJump(0)">
|
||||
<span :style="{ color: state.textColor }">{{ t('commons.all') }}</span>
|
||||
<span :style="{ color: textColor }">{{ t('commons.all') }}</span>
|
||||
</el-breadcrumb-item>
|
||||
<el-breadcrumb-item
|
||||
v-for="(filter, index) in props.drillFilters"
|
||||
@ -75,7 +81,7 @@ const loadThemeStyle = () => {
|
||||
class="drill-item"
|
||||
@click="drillJump(index + 1)"
|
||||
>
|
||||
<span :style="{ color: state.textColor }">{{ filter.value[0] }}</span>
|
||||
<span :style="{ color: textColor }">{{ filter.value[0] }}</span>
|
||||
</el-breadcrumb-item>
|
||||
</el-breadcrumb>
|
||||
</div>
|
||||
|
@ -2,6 +2,8 @@
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import ChartComponentG2Plot from './components/ChartComponentG2Plot.vue'
|
||||
import DeIndicator from '@/custom-component/indicator/DeIndicator.vue'
|
||||
import { useAppStoreWithOut } from '@/store/modules/app'
|
||||
import { XpackComponent } from '@/components/plugin'
|
||||
import {
|
||||
computed,
|
||||
CSSProperties,
|
||||
@ -99,6 +101,7 @@ const props = defineProps({
|
||||
})
|
||||
const dynamicAreaId = ref('')
|
||||
const { view, showPosition, element, active, searchCount, scale } = toRefs(props)
|
||||
const appStore = useAppStoreWithOut()
|
||||
|
||||
const titleShow = computed(
|
||||
() =>
|
||||
@ -235,6 +238,7 @@ watch([() => curComponent.value], () => {
|
||||
})
|
||||
}
|
||||
})
|
||||
const isDataEaseBi = computed(() => appStore.getIsDataEaseBi)
|
||||
|
||||
const chartExtRequest = shallowRef(null)
|
||||
provide('chartExtRequest', chartExtRequest)
|
||||
@ -313,10 +317,21 @@ const filter = (firstLoad?: boolean) => {
|
||||
const onDrillFilters = param => {
|
||||
state.drillFilters = param ? param : []
|
||||
}
|
||||
const openHandler = ref(null)
|
||||
const initOpenHandler = newWindow => {
|
||||
if (openHandler?.value) {
|
||||
const pm = {
|
||||
methodName: 'initOpenHandler',
|
||||
args: newWindow
|
||||
}
|
||||
openHandler.value.invokeMethod(pm)
|
||||
}
|
||||
}
|
||||
|
||||
const windowsJump = (url, jumpType) => {
|
||||
try {
|
||||
window.open(url, jumpType)
|
||||
const newWindow = window.open(url, jumpType)
|
||||
initOpenHandler(newWindow)
|
||||
if (jumpType === '_self') {
|
||||
location.reload()
|
||||
}
|
||||
@ -350,15 +365,19 @@ const jumpClick = param => {
|
||||
param.sourceDvId = dvInfo.value.id
|
||||
param.sourceViewId = param.viewId
|
||||
param.sourceFieldId = dimension.id
|
||||
let embeddedBaseUrl = ''
|
||||
if (isDataEaseBi.value) {
|
||||
embeddedBaseUrl = embeddedStore.baseUrl
|
||||
}
|
||||
// 内部仪表板跳转
|
||||
if (jumpInfo.linkType === 'inner') {
|
||||
if (jumpInfo.targetDvId) {
|
||||
if (publicLinkStatus.value) {
|
||||
// 判断是否有公共链接ID
|
||||
if (jumpInfo.publicJumpId) {
|
||||
const url = `#/de-link/${jumpInfo.publicJumpId}?jumpInfoParam=${encodeURIComponent(
|
||||
Base64.encode(JSON.stringify(param))
|
||||
)}`
|
||||
const url = `${embeddedBaseUrl}#/de-link/${
|
||||
jumpInfo.publicJumpId
|
||||
}?jumpInfoParam=${encodeURIComponent(Base64.encode(JSON.stringify(param)))}`
|
||||
const currentUrl = window.location.href
|
||||
localStorage.setItem('beforeJumpUrl', currentUrl)
|
||||
windowsJump(url, jumpInfo.jumpType)
|
||||
@ -366,9 +385,9 @@ const jumpClick = param => {
|
||||
ElMessage.warning(t('visualization.public_link_tips'))
|
||||
}
|
||||
} else {
|
||||
const url = `#/preview?dvId=${jumpInfo.targetDvId}&jumpInfoParam=${encodeURIComponent(
|
||||
Base64.encode(JSON.stringify(param))
|
||||
)}`
|
||||
const url = `${embeddedBaseUrl}#/preview?dvId=${
|
||||
jumpInfo.targetDvId
|
||||
}&jumpInfoParam=${encodeURIComponent(Base64.encode(JSON.stringify(param)))}`
|
||||
windowsJump(url, jumpInfo.jumpType)
|
||||
}
|
||||
} else {
|
||||
@ -713,6 +732,10 @@ const titleIconStyle = computed(() => {
|
||||
:view-icon="view.type"
|
||||
></chart-empty-info>
|
||||
<drill-path :drill-filters="state.drillFilters" @onDrillJump="drillJump" />
|
||||
<XpackComponent
|
||||
ref="openHandler"
|
||||
jsname="L2NvbXBvbmVudC9lbWJlZGRlZC1pZnJhbWUvT3BlbkhhbmRsZXI="
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -741,11 +764,11 @@ const titleIconStyle = computed(() => {
|
||||
flex-wrap: nowrap;
|
||||
gap: 8px;
|
||||
|
||||
//color: #646a73;
|
||||
//
|
||||
//&.icons-container__dark {
|
||||
// color: #a6a6a6;
|
||||
//}
|
||||
color: #646a73;
|
||||
|
||||
&.icons-container__dark {
|
||||
color: #a6a6a6;
|
||||
}
|
||||
|
||||
&.is-editing {
|
||||
gap: 6px;
|
||||
|
@ -10,6 +10,7 @@ import { getOuterParamsInfo } from '@/api/visualization/outerParams'
|
||||
import { ElMessage } from 'element-plus-secondary'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { XpackComponent } from '@/components/plugin'
|
||||
|
||||
const dvMainStore = dvMainStoreWithOut()
|
||||
const { t } = useI18n()
|
||||
const state = reactive({
|
||||
@ -55,14 +56,16 @@ const loadCanvasDataAsync = async (dvId, dvType) => {
|
||||
}
|
||||
|
||||
// 添加外部参数
|
||||
const attachParamsEncode = router.currentRoute.value.query.attachParams
|
||||
let attachParam
|
||||
if (attachParamsEncode) {
|
||||
try {
|
||||
attachParam = JSON.parse(Base64.decode(decodeURIComponent(attachParamsEncode)))
|
||||
await getOuterParamsInfo(dvId).then(rsp => {
|
||||
dvMainStore.setNowPanelOuterParamsInfo(rsp.data)
|
||||
})
|
||||
|
||||
// 外部参数(iframe 或者 iframe嵌入)
|
||||
const attachParamsEncode = router.currentRoute.value.query.attachParams
|
||||
if (attachParamsEncode) {
|
||||
try {
|
||||
attachParam = JSON.parse(Base64.decode(decodeURIComponent(attachParamsEncode)))
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
ElMessage.error(t('visualization.outer_param_decode_error'))
|
||||
@ -97,6 +100,7 @@ const loadCanvasDataAsync = async (dvId, dvType) => {
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
let p = null
|
||||
const XpackLoaded = () => p(true)
|
||||
onMounted(async () => {
|
||||
@ -108,6 +112,7 @@ onMounted(async () => {
|
||||
}
|
||||
dvMainStore.setPublicLinkStatus(props.publicLinkStatus)
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
loadCanvasDataAsync
|
||||
})
|
||||
|
@ -409,7 +409,7 @@ eventBus.on('handleNew', handleNew)
|
||||
<div style="width: auto; height: 100%" ref="leftSidebarRef">
|
||||
<dv-sidebar
|
||||
v-if="commonPropertiesShow"
|
||||
:title="'属性'"
|
||||
:title="curComponent['name']"
|
||||
:width="240"
|
||||
:side-name="'componentProp'"
|
||||
:aside-position="'right'"
|
||||
|
@ -36,6 +36,7 @@ const footContent = ref(null)
|
||||
const loginErrorMsg = ref('')
|
||||
const xpackLoginHandler = ref()
|
||||
const showDempTips = ref(false)
|
||||
const xpackInvalidPwd = ref()
|
||||
const demoTips = computed(() => {
|
||||
if (!showDempTips.value) {
|
||||
return ''
|
||||
@ -113,6 +114,16 @@ const handleLogin = () => {
|
||||
const { token, exp } = res.data
|
||||
userStore.setToken(token)
|
||||
userStore.setExp(exp)
|
||||
if (!xpackLoadFail.value && xpackInvalidPwd.value?.invokeMethod) {
|
||||
const param = {
|
||||
methodName: 'init',
|
||||
args: () => {
|
||||
duringLogin.value = false
|
||||
}
|
||||
}
|
||||
xpackInvalidPwd?.value.invokeMethod(param)
|
||||
return
|
||||
}
|
||||
const queryRedirectPath = getCurLocation()
|
||||
router.push({ path: queryRedirectPath })
|
||||
})
|
||||
@ -135,6 +146,7 @@ const tablePaneList = ref([{ title: '普通登录', name: 'simple' }])
|
||||
const xpackLoaded = info => {
|
||||
tablePaneList.value.push(info)
|
||||
}
|
||||
const xpackLoadFail = ref(false)
|
||||
|
||||
const loginContainer = ref()
|
||||
const loginContainerWidth = ref(0)
|
||||
@ -256,7 +268,13 @@ onMounted(() => {
|
||||
</div>
|
||||
<div class="login-form-content" v-loading="loading">
|
||||
<div class="login-form-center">
|
||||
<el-form ref="formRef" :model="state.loginForm" :rules="rules" size="default">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="state.loginForm"
|
||||
:rules="rules"
|
||||
size="default"
|
||||
:disabled="preheat"
|
||||
>
|
||||
<div class="login-logo">
|
||||
<Icon
|
||||
v-if="!loginLogoUrl && axiosFinished"
|
||||
@ -328,6 +346,11 @@ onMounted(() => {
|
||||
jsname="L2NvbXBvbmVudC9sb2dpbi9IYW5kbGVy"
|
||||
@loaded="xpackLoaded"
|
||||
/>
|
||||
<XpackComponent
|
||||
ref="xpackInvalidPwd"
|
||||
jsname="L2NvbXBvbmVudC9sb2dpbi9JbnZhbGlkUHdk"
|
||||
@load-fail="() => (xpackLoadFail = true)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="login-msg">
|
||||
|
@ -12,6 +12,13 @@ const options = [
|
||||
{ value: 'minute', label: '分钟(执行时间:0秒)' },
|
||||
{ value: 'hour', label: '小时(执行时间:0分0秒)' }
|
||||
]
|
||||
const pvpOptions = [
|
||||
{ value: '0', label: '永久' },
|
||||
{ value: '1', label: '一年' },
|
||||
{ value: '2', label: '半年' },
|
||||
{ value: '3', label: '三个月' },
|
||||
{ value: '4', label: '一个月' }
|
||||
]
|
||||
const state = reactive({
|
||||
form: reactive({
|
||||
dsIntervalTime: '30',
|
||||
@ -207,7 +214,9 @@ defineExpose({
|
||||
>
|
||||
<el-switch
|
||||
class="de-basic-switch"
|
||||
v-if="item.pkey === 'autoCreateUser'"
|
||||
v-if="
|
||||
item.pkey === 'autoCreateUser' || item.pkey === 'pwdStrategy' || item.pkey === 'dip'
|
||||
"
|
||||
active-value="true"
|
||||
inactive-value="false"
|
||||
v-model="state.form[item.pkey]"
|
||||
@ -282,6 +291,16 @@ defineExpose({
|
||||
check-on-click-node
|
||||
/>
|
||||
</div>
|
||||
<div v-else-if="item.pkey === 'pvp'">
|
||||
<el-select v-model="state.form[item.pkey]" class="edit-all-line">
|
||||
<el-option
|
||||
v-for="item in pvpOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
<v-else />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
@ -22,6 +22,13 @@ import { useI18n } from '@/hooks/web/useI18n'
|
||||
const { t } = useI18n()
|
||||
const editor = ref()
|
||||
const infoTemplate = ref()
|
||||
const pvpOptions = [
|
||||
{ value: '0', label: '永久' },
|
||||
{ value: '1', label: '一年' },
|
||||
{ value: '2', label: '半年' },
|
||||
{ value: '3', label: '三个月' },
|
||||
{ value: '4', label: '一个月' }
|
||||
]
|
||||
const tooltips = [
|
||||
{
|
||||
key: 'setting_basic.frontTimeOut',
|
||||
@ -59,6 +66,7 @@ const selectedOid = ref('')
|
||||
const selectedOName = ref('')
|
||||
const selectedRid = ref<string[]>([])
|
||||
const selectedRName = ref<string[]>([])
|
||||
const selectedPvp = ref('0')
|
||||
const search = cb => {
|
||||
const url = '/sysParameter/basic/query'
|
||||
originData = []
|
||||
@ -68,7 +76,11 @@ const search = cb => {
|
||||
const data = res.data
|
||||
for (let index = 0; index < data.length; index++) {
|
||||
const item = data[index]
|
||||
if (item.pkey === 'basic.autoCreateUser') {
|
||||
if (
|
||||
item.pkey === 'basic.autoCreateUser' ||
|
||||
item.pkey === 'basic.dip' ||
|
||||
item.pkey === 'basic.pwdStrategy'
|
||||
) {
|
||||
item.pval = item.pval === 'true' ? '开启' : '未开启'
|
||||
} else if (item.pkey === 'basic.platformOid') {
|
||||
selectedOid.value = item.pval
|
||||
@ -89,6 +101,9 @@ const search = cb => {
|
||||
selectedRid.value = []
|
||||
item.pval = '普通角色'
|
||||
}
|
||||
} else if (item.pkey === 'basic.pvp') {
|
||||
selectedPvp.value = item.pval || '0'
|
||||
item.pval = pvpOptions.filter(cur => cur.value === selectedPvp.value)[0].label
|
||||
} else {
|
||||
item.pval = item.pval
|
||||
}
|
||||
|
@ -708,12 +708,13 @@ const dragenter_handler = ev => {
|
||||
const drop_handler = ev => {
|
||||
ev.preventDefault()
|
||||
let data = ev.dataTransfer.getData('text')
|
||||
const { tableName, type, datasourceId } = JSON.parse(data) as Table
|
||||
const { tableName, type, datasourceId, name: noteName } = JSON.parse(data) as Table
|
||||
const extraData = {
|
||||
info: JSON.stringify({
|
||||
table: tableName,
|
||||
sql: ''
|
||||
}),
|
||||
noteName,
|
||||
unionType: 'left',
|
||||
unionFields: [],
|
||||
currentDsFields: [],
|
||||
@ -1029,10 +1030,19 @@ const emits = defineEmits(['addComplete', 'joinEditor', 'updateAllfields', 'chan
|
||||
direction="rtl"
|
||||
>
|
||||
<template #header v-if="currentNode">
|
||||
<div class="info-content">
|
||||
<div class="info">
|
||||
<span class="label">表名</span>
|
||||
<span :title="currentNode.tableName" class="name ellipsis">{{
|
||||
currentNode.tableName
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="info">
|
||||
<span class="label">表备注</span>
|
||||
<span :title="currentNode.noteName" class="name ellipsis">{{
|
||||
currentNode.noteName || '-'
|
||||
}}</span>
|
||||
</div>
|
||||
<span :title="getDsName(currentNode.datasourceId)" class="ds ellipsis"
|
||||
>{{ t('auth.datasource') }}:{{ getDsName(currentNode.datasourceId) }}</span
|
||||
>
|
||||
@ -1082,15 +1092,25 @@ const emits = defineEmits(['addComplete', 'joinEditor', 'updateAllfields', 'chan
|
||||
top: 26px;
|
||||
}
|
||||
|
||||
.info-content {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.name {
|
||||
width: 50%;
|
||||
.label {
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
color: #1f2329;
|
||||
max-width: 500px;
|
||||
}
|
||||
.name {
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
}
|
||||
.ds {
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
|
@ -31,6 +31,7 @@ export interface ApiItem {
|
||||
fields: Field[]
|
||||
jsonFields: JsonField[]
|
||||
useJsonPath: boolean
|
||||
apiQueryTimeout: number
|
||||
showApiStructure: boolean
|
||||
jsonPath: string
|
||||
serialNumber: number
|
||||
@ -81,6 +82,7 @@ let apiItem = reactive<ApiItem>({
|
||||
fields: [],
|
||||
jsonFields: [],
|
||||
useJsonPath: false,
|
||||
apiQueryTimeout: 10,
|
||||
showApiStructure: false,
|
||||
jsonPath: '',
|
||||
serialNumber: -1
|
||||
@ -95,6 +97,24 @@ const loading = ref(false)
|
||||
const columns = shallowRef([])
|
||||
const tableData = shallowRef([])
|
||||
const apiItemBasicInfo = ref<FormInstance>()
|
||||
const isNumber = (rule, value, callback) => {
|
||||
if (!value) {
|
||||
callback(new Error(t('datasource.please_input_query_timeout')))
|
||||
return
|
||||
}
|
||||
let isNumber = false
|
||||
var reg = /^\d+$/
|
||||
isNumber = reg.test(value)
|
||||
if (!isNumber) {
|
||||
callback(new Error(t('datasource.please_input_query_timeout')))
|
||||
return
|
||||
}
|
||||
if (value <= 0 || value > 300) {
|
||||
callback(new Error(t('datasource.please_input_query_timeout')))
|
||||
return
|
||||
}
|
||||
callback()
|
||||
}
|
||||
const rule = reactive<FormRules>({
|
||||
name: [
|
||||
{
|
||||
@ -109,6 +129,13 @@ const rule = reactive<FormRules>({
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
apiQueryTimeout: [
|
||||
{
|
||||
required: true,
|
||||
validator: isNumber,
|
||||
trigger: ['blur', 'change']
|
||||
}
|
||||
],
|
||||
url: [
|
||||
{
|
||||
required: true,
|
||||
@ -266,8 +293,8 @@ const previewData = () => {
|
||||
for (let i = 0; i < apiItem.fields.length; i++) {
|
||||
for (let j = 0; j < apiItem.fields[i].value.length; j++) {
|
||||
data[j][apiItem.fields[i].name] = apiItem.fields[i].value[j]
|
||||
data[j]['id'] = apiItem.fields[i].name
|
||||
}
|
||||
|
||||
columnTmp.push({
|
||||
key: apiItem.fields[i].name,
|
||||
dataKey: apiItem.fields[i].name,
|
||||
@ -429,7 +456,11 @@ defineExpose({
|
||||
/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<el-form-item :label="$t('datasource.query_timeout')" prop="apiQueryTimeout">
|
||||
<el-input v-model="apiItem.apiQueryTimeout" autocomplete="off" type="number" :min="0">
|
||||
<template v-slot:append>{{ $t('panel.second') }}</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<div class="title-form_primary request-info">
|
||||
<span>{{ t('datasource.isUseJsonPath') }}</span>
|
||||
</div>
|
||||
|
@ -5,7 +5,7 @@ import type { FormInstance, FormRules } from 'element-plus-secondary'
|
||||
import EmptyBackground from '@/components/empty-background/src/EmptyBackground.vue'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import ApiHttpRequestDraw from './ApiHttpRequestDraw.vue'
|
||||
import { Configuration, ApiConfiguration, SyncSetting } from './index.vue'
|
||||
import type { Configuration, ApiConfiguration, SyncSetting } from './option'
|
||||
import { Icon } from '@/components/icon-custom'
|
||||
import { getSchema } from '@/api/datasource'
|
||||
import { Base64 } from 'js-base64'
|
||||
@ -102,6 +102,8 @@ const initForm = type => {
|
||||
if (type !== 'API') {
|
||||
form.value.configuration = {
|
||||
dataBase: '',
|
||||
jdbcUrl: '',
|
||||
urlType: 'hostName',
|
||||
extraParams: '',
|
||||
username: '',
|
||||
password: '',
|
||||
@ -153,6 +155,13 @@ const authMethodList = [
|
||||
]
|
||||
const setRules = () => {
|
||||
const configRules = {
|
||||
'configuration.jdbcUrl': [
|
||||
{
|
||||
required: true,
|
||||
message: t('datasource.please_input_jdbc_url'),
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
'configuration.dataBase': [
|
||||
{
|
||||
required: true,
|
||||
@ -602,14 +611,41 @@ defineExpose({
|
||||
</template>
|
||||
</template>
|
||||
<template v-if="notapiexcelconfig">
|
||||
<el-form-item :label="t('datasource.host')" prop="configuration.host">
|
||||
<el-form-item label="连接方式" prop="type">
|
||||
<el-radio-group v-model="form.configuration.urlType">
|
||||
<el-radio label="hostName">主机名</el-radio>
|
||||
<el-radio label="jdbcUrl">JDBC 连接</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item
|
||||
label="JDBC连接字符串"
|
||||
prop="configuration.jdbcUrl"
|
||||
v-if="form.configuration.urlType === 'jdbcUrl'"
|
||||
>
|
||||
<el-input
|
||||
v-model="form.configuration.jdbcUrl"
|
||||
placeholder="JDBC连接字符串"
|
||||
autocomplete="off"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item
|
||||
:label="t('datasource.host')"
|
||||
prop="configuration.host"
|
||||
v-if="form.configuration.urlType !== 'jdbcUrl'"
|
||||
>
|
||||
<el-input
|
||||
v-model="form.configuration.host"
|
||||
:placeholder="t('datasource._ip_address')"
|
||||
autocomplete="off"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('datasource.port')" prop="configuration.port">
|
||||
<el-form-item
|
||||
:label="t('datasource.port')"
|
||||
prop="configuration.port"
|
||||
v-if="form.configuration.urlType !== 'jdbcUrl'"
|
||||
>
|
||||
<el-input-number
|
||||
v-model="form.configuration.port"
|
||||
autocomplete="off"
|
||||
@ -621,13 +657,49 @@ defineExpose({
|
||||
type="number"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('datasource.data_base')" prop="configuration.dataBase">
|
||||
<el-form-item
|
||||
:label="t('datasource.data_base')"
|
||||
prop="configuration.dataBase"
|
||||
v-if="form.configuration.urlType !== 'jdbcUrl'"
|
||||
>
|
||||
<el-input
|
||||
v-model="form.configuration.dataBase"
|
||||
:placeholder="t('datasource.please_input_data_base')"
|
||||
autocomplete="off"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="['oracle', 'sqlServer', 'pg', 'redshift', 'db2'].includes(form.type)"
|
||||
class="schema-label"
|
||||
:prop="showSchema ? '' : 'configuration.schema'"
|
||||
>
|
||||
<template v-slot:label>
|
||||
<span class="name">{{ t('datasource.schema') }}<i class="required" /></span>
|
||||
<el-button text size="small" @click="getDsSchema()">
|
||||
<template #icon>
|
||||
<Icon name="icon_add_outlined"></Icon>
|
||||
</template>
|
||||
{{ t('datasource.get_schema') }}
|
||||
</el-button>
|
||||
</template>
|
||||
<el-select
|
||||
v-model="form.configuration.schema"
|
||||
filterable
|
||||
:placeholder="t('common.please_select')"
|
||||
class="de-select"
|
||||
@change="validatorSchema"
|
||||
@blur="validatorSchema"
|
||||
>
|
||||
<el-option v-for="item in schemas" :key="item" :label="item" :value="item" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('datasource.extra_params')">
|
||||
<el-input
|
||||
:placeholder="t('common.inputText') + t('datasource.extra_params')"
|
||||
v-model="form.configuration.extraParams"
|
||||
autocomplete="off"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
:label="t('datasource.auth_method')"
|
||||
prop="configuration.authMethod"
|
||||
@ -687,13 +759,6 @@ defineExpose({
|
||||
v-model="form.configuration.password"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="t('datasource.extra_params')">
|
||||
<el-input
|
||||
:placeholder="t('common.inputText') + t('datasource.extra_params')"
|
||||
v-model="form.configuration.extraParams"
|
||||
autocomplete="off"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="form.type == 'oracle'"
|
||||
:label="t('datasource.connection_mode')"
|
||||
@ -706,31 +771,6 @@ defineExpose({
|
||||
{{ t('datasource.oracle_service_name') }}
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="['oracle', 'sqlServer', 'pg', 'redshift', 'db2'].includes(form.type)"
|
||||
class="schema-label"
|
||||
:prop="showSchema ? '' : 'configuration.schema'"
|
||||
>
|
||||
<template v-slot:label>
|
||||
<span class="name">{{ t('datasource.schema') }}<i class="required" /></span>
|
||||
<el-button text size="small" @click="getDsSchema()">
|
||||
<template #icon>
|
||||
<Icon name="icon_add_outlined"></Icon>
|
||||
</template>
|
||||
{{ t('datasource.get_schema') }}
|
||||
</el-button>
|
||||
</template>
|
||||
<el-select
|
||||
v-model="form.configuration.schema"
|
||||
filterable
|
||||
:placeholder="t('common.please_select')"
|
||||
class="de-select"
|
||||
@change="validatorSchema"
|
||||
@blur="validatorSchema"
|
||||
>
|
||||
<el-option v-for="item in schemas" :key="item" :label="item" :value="item" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<span
|
||||
v-if="!['es', 'api'].includes(form.type)"
|
||||
class="de-expand"
|
||||
|
@ -354,10 +354,23 @@ const uploadExcel = () => {
|
||||
formData.append('type', '')
|
||||
formData.append('editType', param.value.editType)
|
||||
formData.append('id', param.value.id || 0)
|
||||
return uploadFile(formData).then(res => {
|
||||
loading.value = true
|
||||
return uploadFile(formData)
|
||||
.then(res => {
|
||||
upload.value?.clearFiles()
|
||||
uploadAgain.value?.clearFiles()
|
||||
uploadSuccess(res)
|
||||
loading.value = false
|
||||
})
|
||||
.catch(error => {
|
||||
if (error.code === 'ECONNABORTED') {
|
||||
ElMessage({
|
||||
type: 'error',
|
||||
message: error.message,
|
||||
showClose: true
|
||||
})
|
||||
}
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
const excelForm = ref()
|
||||
@ -394,6 +407,7 @@ defineExpose({
|
||||
require-asterisk-position="right"
|
||||
:model="param"
|
||||
label-position="top"
|
||||
v-loading="loading"
|
||||
>
|
||||
<el-form-item
|
||||
v-if="sheetFile.name"
|
||||
|
@ -465,6 +465,9 @@ const init = (nodeInfo: Form | Param, id?: string, res?: object) => {
|
||||
Object.assign(form2, cloneDeep(nodeInfo))
|
||||
} else {
|
||||
Object.assign(form, cloneDeep(nodeInfo))
|
||||
if (form.hasOwnProperty('configuration') && form.configuration.urlType == undefined) {
|
||||
form.configuration.urlType = 'hostName'
|
||||
}
|
||||
}
|
||||
pid.value = nodeInfo.pid || '0'
|
||||
} else {
|
||||
|
@ -122,6 +122,8 @@ export const nameMap = {
|
||||
|
||||
export interface Configuration {
|
||||
dataBase: string
|
||||
jdbcUrl: string
|
||||
urlType: string
|
||||
connectionType: string
|
||||
schema: string
|
||||
extraParams: string
|
||||
|
@ -91,7 +91,7 @@ const { width, node } = useMoveLine('DATASOURCE')
|
||||
|
||||
const dsTableDetail = reactive({
|
||||
tableName: '',
|
||||
remark: ''
|
||||
name: ''
|
||||
})
|
||||
const rootManage = ref(false)
|
||||
const nickName = ref('')
|
||||
@ -1122,14 +1122,14 @@ const getMenuList = (val: boolean) => {
|
||||
</el-col>
|
||||
</el-row>
|
||||
<template v-if="!['Excel', 'API'].includes(nodeInfo.type)">
|
||||
<el-row :gutter="24">
|
||||
<el-row :gutter="24" v-show="nodeInfo.configuration.urlType !== 'jdbcUrl'">
|
||||
<el-col :span="12">
|
||||
<BaseInfoItem :label="t('datasource.host')">{{
|
||||
nodeInfo.configuration.host
|
||||
}}</BaseInfoItem>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="24">
|
||||
<el-row :gutter="24" v-show="nodeInfo.configuration.urlType !== 'jdbcUrl'">
|
||||
<el-col :span="12">
|
||||
<BaseInfoItem :label="t('datasource.port')">{{
|
||||
nodeInfo.configuration.port
|
||||
@ -1141,7 +1141,7 @@ const getMenuList = (val: boolean) => {
|
||||
}}</BaseInfoItem>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="24">
|
||||
<el-row :gutter="24" v-show="nodeInfo.configuration.urlType !== 'jdbcUrl'">
|
||||
<el-col :span="12">
|
||||
<BaseInfoItem :label="t('datasource.user_name')">{{
|
||||
nodeInfo.configuration.username
|
||||
@ -1153,6 +1153,20 @@ const getMenuList = (val: boolean) => {
|
||||
}}</BaseInfoItem>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="24" v-show="nodeInfo.configuration.urlType === 'jdbcUrl'">
|
||||
<el-col :span="12">
|
||||
<BaseInfoItem :label="t('datasource.jdbcUrl')">{{
|
||||
nodeInfo.configuration.jdbcUrl
|
||||
}}</BaseInfoItem>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="24" v-show="nodeInfo.configuration.urlType === 'jdbcUrl'">
|
||||
<el-col :span="12">
|
||||
<BaseInfoItem :label="t('datasource.user_name')">{{
|
||||
nodeInfo.configuration.username
|
||||
}}</BaseInfoItem>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<span
|
||||
v-if="!['es', 'api'].includes(nodeInfo.type.toLowerCase())"
|
||||
class="de-expand"
|
||||
@ -1342,7 +1356,7 @@ const getMenuList = (val: boolean) => {
|
||||
{{ t('datasource.table_description') }}
|
||||
</p>
|
||||
<p class="table-value">
|
||||
{{ dsTableDetail.remark || '-' }}
|
||||
{{ dsTableDetail.name || '-' }}
|
||||
</p>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
2
de-xpack
2
de-xpack
@ -1 +1 @@
|
||||
Subproject commit 84ddcd508b614bab67e08ca7bdc5e7a37573f59c
|
||||
Subproject commit eac4bdc0d387c44088dbc7585a09f61b8e45ea12
|
@ -354,7 +354,7 @@ function backup() {
|
||||
echo "备份失败"
|
||||
exit 1
|
||||
else
|
||||
echo "备份成功,备份文件 : $backup_file_name"
|
||||
echo "备份成功,备份文件 : $(pwd)/$backup_file_name"
|
||||
fi
|
||||
|
||||
if [[ $need_stop == 1 ]];then
|
||||
|
@ -19,10 +19,8 @@ public class ApiDefinition {
|
||||
private ApiDefinitionRequest request;
|
||||
private String status;
|
||||
private List<Map<String, Object>> data = new ArrayList<>();
|
||||
|
||||
|
||||
private int previewNum = 10;
|
||||
private int maxPreviewNum = 10;
|
||||
private Integer apiQueryTimeout = 10;
|
||||
private int previewNum = 100;
|
||||
private int serialNumber;
|
||||
private boolean useJsonPath;
|
||||
private String jsonPath;
|
||||
|
@ -2,8 +2,6 @@ package io.dataease.api.ds.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class Configuration {
|
||||
private String type;
|
||||
@ -17,6 +15,8 @@ public class Configuration {
|
||||
private String aliasSuffix = "";
|
||||
private String jdbc;
|
||||
private String host;
|
||||
private String jdbcUrl;
|
||||
private String urlType;
|
||||
private Integer port;
|
||||
private String username;
|
||||
private String password;
|
||||
|
@ -2,11 +2,14 @@ package io.dataease.api.xpack.settings;
|
||||
|
||||
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
|
||||
import io.dataease.api.xpack.settings.request.XpackAuthenticationEditor;
|
||||
import io.dataease.api.xpack.settings.vo.XpackCasVO;
|
||||
import io.dataease.api.xpack.settings.vo.XpackOidcVO;
|
||||
import io.dataease.api.xpack.settings.vo.XpackAuthenticationStatusVO;
|
||||
import io.dataease.api.xpack.settings.vo.XpackAuthenticationVO;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
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;
|
||||
|
||||
@ -16,17 +19,37 @@ import java.util.List;
|
||||
@ApiSupport(order = 899)
|
||||
public interface XpackAuthenticationApi {
|
||||
|
||||
@Operation(summary = "同步")
|
||||
@GetMapping("/sync")
|
||||
void sync();
|
||||
|
||||
@Operation(summary = "查询列表")
|
||||
@GetMapping("/grid")
|
||||
List<XpackAuthenticationVO> grid();
|
||||
|
||||
@Operation(summary = "切换状态")
|
||||
@PostMapping("/update")
|
||||
void update(@RequestBody XpackAuthenticationEditor editor);
|
||||
@PostMapping("/switchEnable")
|
||||
void switchEnable(@RequestBody XpackAuthenticationEditor editor);
|
||||
|
||||
@PostMapping("/save/oidc")
|
||||
String saveOidc(@RequestBody XpackOidcVO editor);
|
||||
|
||||
@PostMapping("/save/cas")
|
||||
String saveCas(@RequestBody XpackCasVO editor);
|
||||
|
||||
|
||||
@GetMapping("/info/oidc")
|
||||
XpackOidcVO oidcInfo();
|
||||
|
||||
@GetMapping("/info/cas")
|
||||
XpackCasVO casInfo();
|
||||
|
||||
|
||||
@PostMapping("/validate/oidc")
|
||||
String validateOidc(@RequestBody XpackOidcVO editor);
|
||||
|
||||
@PostMapping("/validate/cas")
|
||||
String validateCas(@RequestBody XpackCasVO editor);
|
||||
|
||||
@PostMapping("/validateId/{id}")
|
||||
String validate(@PathVariable("id") Long id);
|
||||
|
||||
@Operation(summary = "查询状态")
|
||||
@GetMapping("/status")
|
||||
|
@ -19,6 +19,8 @@ public class XpackAuthenticationVO implements Serializable {
|
||||
private Long id;
|
||||
@Schema(description = "名称")
|
||||
private String name;
|
||||
@Schema(description = "状态")
|
||||
@Schema(description = "开启")
|
||||
private boolean enable;
|
||||
@Schema(description = "有效")
|
||||
private boolean valid;
|
||||
}
|
||||
|
@ -0,0 +1,14 @@
|
||||
package io.dataease.api.xpack.settings.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
public class XpackCasVO implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 3748231475265743038L;
|
||||
|
||||
private String idpUri;
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package io.dataease.api.xpack.settings.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
public class XpackOidcVO implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 3386314375628396307L;
|
||||
|
||||
private String discovery;
|
||||
|
||||
private String clientId;
|
||||
|
||||
private String clientSecret;
|
||||
|
||||
private String realm;
|
||||
|
||||
private String scope;
|
||||
|
||||
private boolean usePkce;
|
||||
|
||||
private String redirectUri;
|
||||
}
|
@ -185,4 +185,9 @@ public interface UserApi {
|
||||
@Hidden
|
||||
@GetMapping("/orgAdmin")
|
||||
boolean orgAdmin();
|
||||
|
||||
@Hidden
|
||||
@GetMapping("/invalidPwd")
|
||||
InvalidPwdVO invalidPwd();
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,16 @@
|
||||
package io.dataease.api.permissions.user.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
public class InvalidPwdVO implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 3684394012648654165L;
|
||||
|
||||
private boolean invalid;
|
||||
|
||||
private Long validityPeriod;
|
||||
}
|
@ -93,8 +93,10 @@ public class DefaultCacheImpl implements DECacheService {
|
||||
@Override
|
||||
public void keyRemove(String cacheName, String key) {
|
||||
Cache<Object, Object> cache = cacheManager.getCache(cacheName);
|
||||
if(cache != null){
|
||||
cache.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
|
@ -7,4 +7,6 @@ public class XpackSettingConstants {
|
||||
public static final String Front_Time_Out = "basic.frontTimeOut";
|
||||
public static final String PLATFORM_OID = "basic.platformOid";
|
||||
public static final String PLATFORM_RID = "basic.platformRid";
|
||||
public static final String DIP = "basic.dip";
|
||||
public static final String PVP = "basic.pvp";
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package io.dataease.filter;
|
||||
|
||||
import io.dataease.result.ResultMessage;
|
||||
import io.dataease.utils.JsonUtil;
|
||||
import jakarta.servlet.*;
|
||||
import jakarta.servlet.FilterConfig;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
@ -36,7 +38,14 @@ public class HtmlResourceFilter implements Filter, Ordered {
|
||||
httpResponse.setHeader(HttpHeaders.EXPIRES, "0");
|
||||
}
|
||||
// 继续执行过滤器链
|
||||
try {
|
||||
filterChain.doFilter(servletRequest, httpResponse);
|
||||
}catch (Exception e){
|
||||
httpResponse.setContentType("application/json");
|
||||
httpResponse.setCharacterEncoding("UTF-8");
|
||||
httpResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST);
|
||||
httpResponse.getWriter().write(JsonUtil.toJSONString(new ResultMessage(HttpServletResponse.SC_BAD_REQUEST,e.getMessage())).toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,16 @@
|
||||
package io.dataease.utils;
|
||||
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
public class CalendarUtils {
|
||||
|
||||
public static Long getTimeAfterMonth(Long time, int months) {
|
||||
if (ObjectUtils.isEmpty(time)) time = System.currentTimeMillis();
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTimeInMillis(time);
|
||||
calendar.add(Calendar.MONTH, months);
|
||||
return calendar.getTimeInMillis();
|
||||
}
|
||||
}
|
@ -6,9 +6,9 @@ import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.entity.mime.MultipartEntityBuilder;
|
||||
import org.apache.http.client.entity.EntityBuilder;
|
||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||
import org.apache.http.client.methods.HttpDelete;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPatch;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
@ -20,6 +20,7 @@ import org.apache.http.conn.socket.PlainConnectionSocketFactory;
|
||||
import org.apache.http.conn.ssl.NoopHostnameVerifier;
|
||||
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.mime.MultipartEntityBuilder;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
@ -77,6 +78,37 @@ public class HttpClientUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean validateUrl(String url, HttpClientConfig config) {
|
||||
CloseableHttpClient httpClient = null;
|
||||
try {
|
||||
httpClient = buildHttpClient(url);
|
||||
HttpGet httpGet = new HttpGet(url);
|
||||
if (config == null) {
|
||||
config = new HttpClientConfig();
|
||||
}
|
||||
httpGet.setConfig(config.buildRequestConfig());
|
||||
|
||||
Map<String, String> header = config.getHeader();
|
||||
for (String key : header.keySet()) {
|
||||
httpGet.addHeader(key, header.get(key));
|
||||
}
|
||||
HttpResponse response = httpClient.execute(httpGet);
|
||||
int statusCode = response.getStatusLine().getStatusCode();
|
||||
return statusCode <= 400;
|
||||
} catch (Exception e) {
|
||||
logger.error("HttpClient查询失败", e);
|
||||
throw new DEException(SYSTEM_INNER_ERROR.code(), "HttpClient查询失败: " + e.getMessage());
|
||||
} finally {
|
||||
try {
|
||||
if (httpClient != null) {
|
||||
httpClient.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("HttpClient关闭连接失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get http请求
|
||||
*
|
||||
@ -342,4 +374,35 @@ public class HttpClientUtil {
|
||||
config.addHeader(entry.getKey(), entry.getValue().toString());
|
||||
}
|
||||
}
|
||||
|
||||
public static String delete(String url, HttpClientConfig config) {
|
||||
CloseableHttpClient httpClient = null;
|
||||
try {
|
||||
httpClient = buildHttpClient(url);
|
||||
HttpDelete httpDelete = new HttpDelete(url);
|
||||
|
||||
if (config == null) {
|
||||
config = new HttpClientConfig();
|
||||
}
|
||||
httpDelete.setConfig(config.buildRequestConfig());
|
||||
|
||||
Map<String, String> header = config.getHeader();
|
||||
for (String key : header.keySet()) {
|
||||
httpDelete.addHeader(key, header.get(key));
|
||||
}
|
||||
HttpResponse response = httpClient.execute(httpDelete);
|
||||
return getResponseStr(response, config);
|
||||
} catch (Exception e) {
|
||||
logger.error("HttpClient查询失败", e);
|
||||
throw new DEException(SYSTEM_INNER_ERROR.code(), "HttpClient查询失败: " + e.getMessage());
|
||||
} finally {
|
||||
try {
|
||||
if (httpClient != null) {
|
||||
httpClient.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("HttpClient关闭连接失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,8 @@ public class SystemSettingUtils {
|
||||
List<String> xpackSettingList = List.of(XpackSettingConstants.AUTO_CREATE_USER,
|
||||
XpackSettingConstants.LOG_LIVE_TIME,
|
||||
XpackSettingConstants.PLATFORM_OID,
|
||||
XpackSettingConstants.DIP,
|
||||
XpackSettingConstants.PVP,
|
||||
XpackSettingConstants.PLATFORM_RID);
|
||||
return xpackSettingList.contains(pkey);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user