diff --git a/extensions/.gitignore b/extensions/.gitignore new file mode 100644 index 0000000000..30d79216dd --- /dev/null +++ b/extensions/.gitignore @@ -0,0 +1,58 @@ +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +#*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +/.idea/ +target/ +*.iml +.DS_Store +node_modules +/dist +node/ +static/ +# local env files +.env.demo +.env.*.local + +# Log files +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? +*.lock +*.classpath +*.project +*.factorypath +.settings/ +.lh + + +package-lock.json diff --git a/extensions/README.md b/extensions/README.md new file mode 100644 index 0000000000..50b8e1189f --- /dev/null +++ b/extensions/README.md @@ -0,0 +1,19 @@ +# DateEase 官方插件 + +此仓库用于放置 DataEase 官方插件,插件分为两种类型:数据源插件和视图插件。 + +当前以插件方式支持的数据源包括: + + - 达梦数据库 + - Apache Kylin + - 阿里云 MaxCompute + - MongoDB + - Presto + +当前已插件方式支持的视图包括: + + - 3dpie + - bubblemap + - symbolmap + +更多插件持续开发中,敬请期待。 diff --git a/extensions/dataease-extensions-datasource/dm/.gitignore b/extensions/dataease-extensions-datasource/dm/.gitignore new file mode 100644 index 0000000000..a5a2b0c056 --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/.gitignore @@ -0,0 +1,35 @@ +# Created by .ignore support plugin (hsz.mobi) +.DS_Store +node_modules +node/ +/dist + +# local env files +.env.local +.env.*.local + +# Log files +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Editor directories and files +.idea +*.iml +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + + +src/main/resources/static +src/main/resources/templates +src/test/ +target +.settings +.project +.classpath +.factorypath +src/main/resources/jmeter/lib/ \ No newline at end of file diff --git a/extensions/dataease-extensions-datasource/dm/build.sh b/extensions/dataease-extensions-datasource/dm/build.sh new file mode 100755 index 0000000000..e77b784305 --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/build.sh @@ -0,0 +1,6 @@ +#!/bin/sh +mvn clean package -U -Dmaven.test.skip=true + +cp dm-backend/target/dm-backend-1.18.0.jar . + +zip -r dm.zip ./dm-backend-1.18.0.jar ./dmDriver ./plugin.json diff --git a/extensions/dataease-extensions-datasource/dm/dm-backend/pom.xml b/extensions/dataease-extensions-datasource/dm/dm-backend/pom.xml new file mode 100644 index 0000000000..b92b653718 --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-backend/pom.xml @@ -0,0 +1,109 @@ + + + + dm + io.dataease + ${dataease.version} + + 4.0.0 + + dm-backend + + + + io.dataease + dataease-plugin-datasource + + + + + + + src/main/java + + **/*.properties + **/*.xml + + false + + + src/main/resources + + **/* + + false + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 11 + 11 + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.4 + + + **/server/** + **/*.properties + **/Application* + + + + + + maven-clean-plugin + + + + src/main/resources/static + + ** + + false + + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + main-class-placement + generate-resources + + + + + + + + + + + + run + + + + + + + + + + + diff --git a/extensions/dataease-extensions-datasource/dm/dm-backend/src/main/java/io/dataease/plugins/datasource/dm/provider/DmConfig.java b/extensions/dataease-extensions-datasource/dm/dm-backend/src/main/java/io/dataease/plugins/datasource/dm/provider/DmConfig.java new file mode 100644 index 0000000000..e1d07a3167 --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-backend/src/main/java/io/dataease/plugins/datasource/dm/provider/DmConfig.java @@ -0,0 +1,21 @@ +package io.dataease.plugins.datasource.dm.provider; + +import io.dataease.plugins.datasource.entity.JdbcConfiguration; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class DmConfig extends JdbcConfiguration { + + private String driver = "dm.jdbc.driver.DmDriver"; + private String extraParams; + + + public String getJdbc() { + return "jdbc:dm://HOST:PORT/DATABASE" + .replace("HOST", getHost().trim()) + .replace("PORT", getPort().toString()) + .replace("DATABASE", getDataBase().trim()); + } +} diff --git a/extensions/dataease-extensions-datasource/dm/dm-backend/src/main/java/io/dataease/plugins/datasource/dm/provider/DmDsProvider.java b/extensions/dataease-extensions-datasource/dm/dm-backend/src/main/java/io/dataease/plugins/datasource/dm/provider/DmDsProvider.java new file mode 100644 index 0000000000..35acaa981a --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-backend/src/main/java/io/dataease/plugins/datasource/dm/provider/DmDsProvider.java @@ -0,0 +1,226 @@ +package io.dataease.plugins.datasource.dm.provider; + +import com.google.gson.Gson; +import io.dataease.plugins.common.base.domain.DeDriver; +import io.dataease.plugins.common.base.mapper.DeDriverMapper; +import io.dataease.plugins.common.constants.DatasourceTypes; +import io.dataease.plugins.common.dto.datasource.TableDesc; +import io.dataease.plugins.common.dto.datasource.TableField; +import io.dataease.plugins.common.exception.DataEaseException; +import io.dataease.plugins.common.request.datasource.DatasourceRequest; +import io.dataease.plugins.datasource.entity.JdbcConfiguration; +import io.dataease.plugins.datasource.provider.DefaultJdbcProvider; +import io.dataease.plugins.datasource.provider.ExtendedJdbcClassLoader; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.lang.reflect.Method; +import java.sql.*; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Properties; + + +@Component() +public class DmDsProvider extends DefaultJdbcProvider { + @Resource + private DeDriverMapper deDriverMapper; + + @Override + public String getType() { + return "dm"; + } + + @Override + public boolean isUseDatasourcePool() { + return false; + } + + @Override + public Connection getConnection(DatasourceRequest datasourceRequest) throws Exception { + DmConfig dmConfig = new Gson().fromJson(datasourceRequest.getDatasource().getConfiguration(), DmConfig.class); + + String defaultDriver = dmConfig.getDriver(); + String customDriver = dmConfig.getCustomDriver(); + + String url = dmConfig.getJdbc(); + Properties props = new Properties(); + DeDriver deDriver = null; + if (StringUtils.isNotEmpty(dmConfig.getAuthMethod()) && dmConfig.getAuthMethod().equalsIgnoreCase("kerberos")) { + System.setProperty("java.security.krb5.conf", "/opt/dataease/conf/krb5.conf"); + ExtendedJdbcClassLoader classLoader; + if (isDefaultClassLoader(customDriver)) { + classLoader = extendedJdbcClassLoader; + } else { + deDriver = deDriverMapper.selectByPrimaryKey(customDriver); + classLoader = getCustomJdbcClassLoader(deDriver); + } + Class ConfigurationClass = classLoader.loadClass("org.apache.hadoop.conf.Configuration"); + Method set = ConfigurationClass.getMethod("set", String.class, String.class); + Object obj = ConfigurationClass.newInstance(); + set.invoke(obj, "hadoop.security.authentication", "Kerberos"); + + Class UserGroupInformationClass = classLoader.loadClass("org.apache.hadoop.security.UserGroupInformation"); + Method setConfiguration = UserGroupInformationClass.getMethod("setConfiguration", ConfigurationClass); + Method loginUserFromKeytab = UserGroupInformationClass.getMethod("loginUserFromKeytab", String.class, String.class); + setConfiguration.invoke(null, obj); + loginUserFromKeytab.invoke(null, dmConfig.getUsername(), "/opt/dataease/conf/" + dmConfig.getPassword()); + } else { + if (StringUtils.isNotBlank(dmConfig.getUsername())) { + props.setProperty("user", dmConfig.getUsername()); + if (StringUtils.isNotBlank(dmConfig.getPassword())) { + props.setProperty("password", dmConfig.getPassword()); + } + } + } + + Connection conn; + String driverClassName; + ExtendedJdbcClassLoader jdbcClassLoader; + if (isDefaultClassLoader(customDriver)) { + driverClassName = defaultDriver; + jdbcClassLoader = extendedJdbcClassLoader; + } else { + if (deDriver == null) { + deDriver = deDriverMapper.selectByPrimaryKey(customDriver); + } + driverClassName = deDriver.getDriverClass(); + jdbcClassLoader = getCustomJdbcClassLoader(deDriver); + } + + Driver driverClass = (Driver) jdbcClassLoader.loadClass(driverClassName).newInstance(); + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + try { + Thread.currentThread().setContextClassLoader(jdbcClassLoader); + conn = driverClass.connect(url, props); + } catch (Exception e) { + e.printStackTrace(); + throw e; + } finally { + Thread.currentThread().setContextClassLoader(classLoader); + } + return conn; + } + + @Override + public List getTables(DatasourceRequest datasourceRequest) throws Exception { + List tables = new ArrayList<>(); + String queryStr = getTablesSql(datasourceRequest); + JdbcConfiguration jdbcConfiguration = new Gson().fromJson(datasourceRequest.getDatasource().getConfiguration(), JdbcConfiguration.class); + int queryTimeout = jdbcConfiguration.getQueryTimeout() > 0 ? jdbcConfiguration.getQueryTimeout() : 0; + try (Connection con = getConnectionFromPool(datasourceRequest); Statement statement = getStatement(con, queryTimeout); ResultSet resultSet = statement.executeQuery(queryStr)) { + while (resultSet.next()) { + tables.add(getTableDesc(datasourceRequest, resultSet)); + } + } catch (Exception e) { + DataEaseException.throwException(e); + } + + return tables; + } + + private TableDesc getTableDesc(DatasourceRequest datasourceRequest, ResultSet resultSet) throws SQLException { + TableDesc tableDesc = new TableDesc(); + tableDesc.setName(resultSet.getString(1)); + return tableDesc; + } + + @Override + public List getTableFields(DatasourceRequest datasourceRequest) throws Exception { + List list = new LinkedList<>(); + try (Connection connection = getConnectionFromPool(datasourceRequest)) { + DatabaseMetaData databaseMetaData = connection.getMetaData(); + ResultSet resultSet = databaseMetaData.getColumns(null, "%", datasourceRequest.getTable(), "%"); + while (resultSet.next()) { + String tableName = resultSet.getString("TABLE_NAME"); + String database; + database = resultSet.getString("TABLE_CAT"); + if (database != null) { + if (tableName.equals(datasourceRequest.getTable()) && database.equalsIgnoreCase(getDatabase(datasourceRequest))) { + TableField tableField = getTableFiled(resultSet, datasourceRequest); + list.add(tableField); + } + } else { + if (tableName.equals(datasourceRequest.getTable())) { + TableField tableField = getTableFiled(resultSet, datasourceRequest); + list.add(tableField); + } + } + } + resultSet.close(); + } catch (SQLException e) { + DataEaseException.throwException(e); + } catch (Exception e) { + DataEaseException.throwException("Data source connection exception: " + e.getMessage()); + } + return list; + } + + private String getDatabase(DatasourceRequest datasourceRequest) { + JdbcConfiguration jdbcConfiguration = new Gson().fromJson(datasourceRequest.getDatasource().getConfiguration(), JdbcConfiguration.class); + return jdbcConfiguration.getDataBase(); + } + + + private TableField getTableFiled(ResultSet resultSet, DatasourceRequest datasourceRequest) throws SQLException { + TableField tableField = new TableField(); + String colName = resultSet.getString("COLUMN_NAME"); + tableField.setFieldName(colName); + String remarks = resultSet.getString("REMARKS"); + if (remarks == null || remarks.equals("")) { + remarks = colName; + } + tableField.setRemarks(remarks); + String dbType = resultSet.getString("TYPE_NAME").toUpperCase(); + tableField.setFieldType(dbType); + if (dbType.equalsIgnoreCase("LONG")) { + tableField.setFieldSize(65533); + } + if (StringUtils.isNotEmpty(dbType) && dbType.toLowerCase().contains("date") && tableField.getFieldSize() < 50) { + tableField.setFieldSize(50); + } + + if (datasourceRequest.getDatasource().getType().equalsIgnoreCase(DatasourceTypes.hive.name()) && tableField.getFieldType().equalsIgnoreCase("BOOLEAN")) { + tableField.setFieldSize(1); + } else { + String size = resultSet.getString("COLUMN_SIZE"); + if (size == null) { + tableField.setFieldSize(1); + } else { + tableField.setFieldSize(Integer.valueOf(size)); + } + } + return tableField; + } + + @Override + public String checkStatus(DatasourceRequest datasourceRequest) throws Exception { + String queryStr = getTablesSql(datasourceRequest); + JdbcConfiguration jdbcConfiguration = new Gson().fromJson(datasourceRequest.getDatasource().getConfiguration(), JdbcConfiguration.class); + int queryTimeout = jdbcConfiguration.getQueryTimeout() > 0 ? jdbcConfiguration.getQueryTimeout() : 0; + try (Connection con = getConnection(datasourceRequest); Statement statement = getStatement(con, queryTimeout); ResultSet resultSet = statement.executeQuery(queryStr)) { + } catch (Exception e) { + e.printStackTrace(); + DataEaseException.throwException(e.getMessage()); + } + return "Success"; + } + + + @Override + public String getTablesSql(DatasourceRequest datasourceRequest) throws Exception { + DmConfig dmConfig = new Gson().fromJson(datasourceRequest.getDatasource().getConfiguration(), DmConfig.class); + if (StringUtils.isEmpty(dmConfig.getSchema())) { + throw new Exception("Database schema is empty."); + } + return "select table_name from all_tab_comments where owner='OWNER' AND table_type = 'TABLE' ".replaceAll("OWNER", dmConfig.getSchema()); + } + + @Override + public String getSchemaSql(DatasourceRequest datasourceRequest) { + return "select OBJECT_NAME from dba_objects where object_type='SCH'"; + } + +} diff --git a/extensions/dataease-extensions-datasource/dm/dm-backend/src/main/java/io/dataease/plugins/datasource/dm/query/DmConstants.java b/extensions/dataease-extensions-datasource/dm/dm-backend/src/main/java/io/dataease/plugins/datasource/dm/query/DmConstants.java new file mode 100644 index 0000000000..d067905573 --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-backend/src/main/java/io/dataease/plugins/datasource/dm/query/DmConstants.java @@ -0,0 +1,61 @@ +package io.dataease.plugins.datasource.dm.query; + + + +import io.dataease.plugins.common.constants.datasource.SQLConstants; + +import static io.dataease.plugins.common.constants.DatasourceTypes.oracle; + +public class DmConstants extends SQLConstants { + + public static final String KEYWORD_TABLE = oracle.getKeywordPrefix() + "%s" + oracle.getKeywordSuffix(); + + public static final String KEYWORD_FIX = "%s." + oracle.getKeywordPrefix() + "%s" + oracle.getKeywordSuffix(); + + public static final String ALIAS_FIX = oracle.getAliasPrefix() + "%s" + oracle.getAliasSuffix(); + + public static final String UNIX_TIMESTAMP = "UNIX_TIMESTAMP(%s)"; + + public static final String DATE_FORMAT = "to_timestamp(%s,'%s')"; + + public static final String FROM_UNIXTIME = "FROM_UNIXTIME(%s,'%s')"; + + public static final String CAST = "CAST(%s AS %s)"; + + public static final String DEFAULT_DATE_FORMAT = "YYYY-MM-DD HH24:MI:SS"; + + public static final String DEFAULT_INT_FORMAT = "DECIMAL(20,0)"; + + public static final String DEFAULT_FLOAT_FORMAT = "DECIMAL(20,8)"; + + public static final String WHERE_VALUE_NULL = "(NULL,'')"; + + public static final String WHERE_VALUE_VALUE = "'%s'"; + + public static final String AGG_COUNT = "COUNT(*)"; + + public static final String AGG_FIELD = "%s(%s)"; + + public static final String WHERE_BETWEEN = "'%s' AND '%s'"; + + public static final String BRACKETS = "(%s)"; + + public static final String TO_NUMBER = "TO_NUMBER(%s)"; + + public static final String TO_DATE = "TO_DATE(%s,'%s')"; + + public static final String TO_CHAR = "TO_CHAR(%s,'%s')"; + + public static final String DEFAULT_START_DATE = "'1970-01-01 8:0:0'"; + + public static final String TO_MS = " * 24 * 60 * 60 * 100"; + + public static final String CALC_SUB = "%s - %s"; + + // public static final String GROUP_CONCAT = "vm_concat(%s)"; + public static final String GROUP_CONCAT = "to_char(listagg(%s,',' ) within GROUP (order by (%s)))"; + + public static final String NAME = "oracle"; + + +} diff --git a/extensions/dataease-extensions-datasource/dm/dm-backend/src/main/java/io/dataease/plugins/datasource/dm/query/DmQueryProvider.java b/extensions/dataease-extensions-datasource/dm/dm-backend/src/main/java/io/dataease/plugins/datasource/dm/query/DmQueryProvider.java new file mode 100644 index 0000000000..e43bea7a80 --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-backend/src/main/java/io/dataease/plugins/datasource/dm/query/DmQueryProvider.java @@ -0,0 +1,1558 @@ +package io.dataease.plugins.datasource.dm.query; + +import cn.hutool.json.JSONArray; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.Gson; +import io.dataease.plugins.common.base.domain.ChartViewWithBLOBs; +import io.dataease.plugins.common.base.domain.DatasetTableField; +import io.dataease.plugins.common.base.domain.DatasetTableFieldExample; +import io.dataease.plugins.common.base.domain.Datasource; +import io.dataease.plugins.common.base.mapper.DatasetTableFieldMapper; +import io.dataease.plugins.common.constants.DeTypeConstants; +import io.dataease.plugins.common.constants.datasource.OracleConstants; +import io.dataease.plugins.common.constants.datasource.SQLConstants; +import io.dataease.plugins.common.dto.chart.ChartCustomFilterItemDTO; +import io.dataease.plugins.common.dto.chart.ChartFieldCustomFilterDTO; +import io.dataease.plugins.common.dto.chart.ChartViewFieldDTO; +import io.dataease.plugins.common.dto.datasource.DeSortField; +import io.dataease.plugins.common.dto.sqlObj.SQLObj; +import io.dataease.plugins.common.request.chart.ChartExtFilterRequest; +import io.dataease.plugins.common.request.permission.DataSetRowPermissionsTreeDTO; +import io.dataease.plugins.common.request.permission.DatasetRowPermissionsTreeItem; +import io.dataease.plugins.datasource.dm.provider.DmConfig; +import io.dataease.plugins.datasource.entity.Dateformat; +import io.dataease.plugins.datasource.entity.JdbcConfiguration; +import io.dataease.plugins.datasource.entity.PageInfo; +import io.dataease.plugins.datasource.query.QueryProvider; +import io.dataease.plugins.datasource.query.Utils; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; +import org.stringtemplate.v4.ST; +import org.stringtemplate.v4.STGroup; +import org.stringtemplate.v4.STGroupFile; + +import javax.annotation.Resource; +import java.text.MessageFormat; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import static io.dataease.plugins.common.constants.datasource.SQLConstants.TABLE_ALIAS_PREFIX; + +/** + * @Author gin + * @Date 2021/5/17 2:43 下午 + */ +@Component() +public class DmQueryProvider extends QueryProvider { + + @Resource + private DatasetTableFieldMapper datasetTableFieldMapper; + + @Override + public Integer transFieldType(String field) { + switch (field) { + case "CHAR": + case "VARCHAR2": + case "VARCHAR": + case "TEXT": + case "TINYTEXT": + case "MEDIUMTEXT": + case "LONGTEXT": + case "ENUM": + case "LONG": + case "NVARCHAR2": + case "NCHAR": + return 0;// 文本 + case "DATE": + case "TIME": + case "YEAR": + case "DATETIME": + case "TIMESTAMP": + return 1;// 时间 + case "INT": + case "SMALLINT": + case "MEDIUMINT": + case "INTEGER": + case "BIGINT": + return 2;// 整型 + case "NUMBER": + case "FLOAT": + case "DOUBLE": + case "DECIMAL": + case "DEC": + case "NUMERIC": + return 3;// 浮点 + case "BIT": + case "TINYINT": + return 4;// 布尔 + default: + return 0; + } + } + + @Override + public String createSQLPreview(String sql, String orderBy) { + return "SELECT * FROM (" + sqlFix(sql) + ") DE_TMP " + " WHERE rownum <= 1000"; + } + + @Override + public String createQuerySQL(String table, List fields, boolean isGroup, Datasource ds, + List fieldCustomFilter, List rowPermissionsTree, List sortFields) { + SQLObj tableObj = SQLObj.builder() + .tableName((table.startsWith("(") && table.endsWith(")")) ? table + : String.format(OracleConstants.KEYWORD_TABLE, table)) + .tableAlias(String.format(OracleConstants.ALIAS_FIX, String.format(TABLE_ALIAS_PREFIX, 0))) + .build(); + + setSchema(tableObj, ds); + List xFields = xFields(table, fields); + + STGroup stg = new STGroupFile(SQLConstants.SQL_TEMPLATE); + ST st_sql = stg.getInstanceOf("previewSql"); + st_sql.add("isGroup", isGroup); + if (CollectionUtils.isNotEmpty(xFields)) + st_sql.add("groups", xFields); + if (ObjectUtils.isNotEmpty(tableObj)) + st_sql.add("table", tableObj); + String customWheres = transCustomFilterList(tableObj, fieldCustomFilter); + // row permissions tree + String whereTrees = transFilterTrees(tableObj, rowPermissionsTree); + List wheres = new ArrayList<>(); + if (customWheres != null) + wheres.add(customWheres); + if (whereTrees != null) + wheres.add(whereTrees); + if (CollectionUtils.isNotEmpty(wheres)) + st_sql.add("filters", wheres); + + List xOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(sortFields)) { + int step = fields.size(); + for (int i = step; i < (step + sortFields.size()); i++) { + DeSortField deSortField = sortFields.get(i - step); + SQLObj order = buildSortField(deSortField, tableObj, i); + xOrders.add(order); + } + } + if (ObjectUtils.isNotEmpty(xOrders)) { + st_sql.add("orders", xOrders); + } + return st_sql.render(); + } + + @Override + public String createQuerySQL(String table, List fields, boolean isGroup, Datasource ds, + List fieldCustomFilter, List rowPermissionsTree) { + return createQuerySQL(table, fields, isGroup, ds, fieldCustomFilter, rowPermissionsTree, null); + } + + @Override + public String createQuerySQLAsTmp(String sql, List fields, boolean isGroup, + List fieldCustomFilter, List rowPermissionsTree, List sortFields) { + return createQuerySQL("(" + sqlFix(sql) + ")", fields, isGroup, null, fieldCustomFilter, rowPermissionsTree, sortFields); + } + + public void setSchema(SQLObj tableObj, Datasource ds) { + if (ds != null && !tableObj.getTableName().startsWith("(") && !tableObj.getTableName().endsWith(")")) { + String schema = new Gson().fromJson(ds.getConfiguration(), JdbcConfiguration.class).getSchema(); + schema = String.format(OracleConstants.KEYWORD_TABLE, schema); + tableObj.setTableName(schema + "." + tableObj.getTableName()); + } + } + + private SQLObj buildSortField(DeSortField f, SQLObj tableObj, int index) { + String originField; + if (ObjectUtils.isNotEmpty(f.getExtField()) && f.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(f.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(f.getExtField()) && f.getExtField() == 1) { + originField = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), f.getOriginName()); + } else { + originField = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), f.getOriginName()); + } + String fieldAlias = String.format(OracleConstants.ALIAS_FIX, + String.format(SQLConstants.FIELD_ALIAS_X_PREFIX, index)); + String fieldName = ""; + // 处理横轴字段 + if (f.getDeExtractType() == 1) { + if (f.getDeType() == 2 || f.getDeType() == 3) { + fieldName = String.format(OracleConstants.UNIX_TIMESTAMP, originField) + "*1000"; + } else { + fieldName = originField; + } + } else if (f.getDeExtractType() == 0) { + if (f.getDeType() == 2) { + fieldName = String.format(OracleConstants.CAST, originField, OracleConstants.DEFAULT_INT_FORMAT); + } else if (f.getDeType() == 3) { + fieldName = String.format(OracleConstants.CAST, originField, OracleConstants.DEFAULT_FLOAT_FORMAT); + } else if (f.getDeType() == 1) { + fieldName = String.format(OracleConstants.DATE_FORMAT, originField, OracleConstants.DEFAULT_DATE_FORMAT); + } else { + fieldName = originField; + } + } else { + if (f.getDeType() == 1) { + String cast = String.format(OracleConstants.CAST, originField, OracleConstants.DEFAULT_INT_FORMAT) + + "/1000"; + fieldName = String.format(OracleConstants.FROM_UNIXTIME, cast, OracleConstants.DEFAULT_DATE_FORMAT); + } else if (f.getDeType() == 2) { + fieldName = String.format(OracleConstants.CAST, originField, OracleConstants.DEFAULT_INT_FORMAT); + } else { + fieldName = originField; + } + } + SQLObj result = SQLObj.builder().orderField(originField).orderAlias(originField) + .orderDirection(f.getOrderDirection()).build(); + return result; + } + + private List xFields(String table, List fields) { + SQLObj tableObj = SQLObj.builder() + .tableName((table.startsWith("(") && table.endsWith(")")) ? table + : String.format(OracleConstants.KEYWORD_TABLE, table)) + .tableAlias(String.format(OracleConstants.ALIAS_FIX, String.format(TABLE_ALIAS_PREFIX, 0))) + .build(); + List xFields = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(fields)) { + for (int i = 0; i < fields.size(); i++) { + DatasetTableField f = fields.get(i); + String originField; + if (ObjectUtils.isNotEmpty(f.getExtField()) && f.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(f.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(f.getExtField()) && f.getExtField() == 1) { + originField = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + f.getOriginName()); + } else { + originField = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + f.getOriginName()); + } + String fieldAlias = String.format(OracleConstants.ALIAS_FIX, + String.format(SQLConstants.FIELD_ALIAS_X_PREFIX, i)); + String fieldName = ""; + // 处理横轴字段 + if (f.getDeExtractType() == 1) { + if (f.getDeType() == 2 || f.getDeType() == 3) { + fieldName = String.format(OracleConstants.UNIX_TIMESTAMP, originField) + "*1000"; + } else { + fieldName = originField; + } + } else if (f.getDeExtractType() == 0) { + if (f.getDeType() == 2) { + fieldName = String.format(OracleConstants.CAST, originField, + OracleConstants.DEFAULT_INT_FORMAT); + } else if (f.getDeType() == 3) { + fieldName = String.format(OracleConstants.CAST, originField, + OracleConstants.DEFAULT_FLOAT_FORMAT); + } else if (f.getDeType() == 1) { + fieldName = String.format(OracleConstants.DATE_FORMAT, originField, + OracleConstants.DEFAULT_DATE_FORMAT); + } else { + fieldName = originField; + } + } else { + if (f.getDeType() == 1) { + String cast = String.format(OracleConstants.CAST, originField, + OracleConstants.DEFAULT_INT_FORMAT) + "/1000"; + fieldName = String.format(OracleConstants.FROM_UNIXTIME, cast, + OracleConstants.DEFAULT_DATE_FORMAT); + } else if (f.getDeType() == 2) { + fieldName = String.format(OracleConstants.CAST, originField, + OracleConstants.DEFAULT_INT_FORMAT); + } else { + fieldName = originField; + } + } + xFields.add(SQLObj.builder() + .fieldName(fieldName) + .fieldAlias(fieldAlias) + .build()); + } + } + return xFields; + } + + private String sqlColumn(List xFields) { + String[] array = xFields.stream().map(f -> { + return f.getFieldAlias(); + }).toArray(String[]::new); + return StringUtils.join(array, ","); + } + + @Override + public String createQuerySQLAsTmp(String sql, List fields, boolean isGroup, + List fieldCustomFilter, List rowPermissionsTree) { + return createQuerySQL("(" + sqlFix(sql) + ")", fields, isGroup, null, fieldCustomFilter, rowPermissionsTree); + } + + @Override + public String createQueryTableWithPage(String table, List fields, Integer page, Integer pageSize, + Integer realSize, boolean isGroup, Datasource ds, List fieldCustomFilter, List rowPermissionsTree) { + List xFields = xFields(table, fields); + + return MessageFormat.format( + "SELECT {0} FROM ( SELECT DE_TMP.*, rownum r FROM ( {1} ) DE_TMP WHERE rownum <= {2} ) WHERE r > {3} ", + sqlColumn(xFields), createQuerySQL(table, fields, isGroup, ds, fieldCustomFilter, rowPermissionsTree), + Integer.valueOf(page * realSize).toString(), Integer.valueOf((page - 1) * pageSize).toString()); + } + + @Override + public String createQuerySQLWithPage(String sql, List fields, Integer page, Integer pageSize, + Integer realSize, boolean isGroup, List fieldCustomFilter, List rowPermissionsTree) { + List xFields = xFields("(" + sqlFix(sql) + ")", fields); + return MessageFormat.format( + "SELECT {0} FROM ( SELECT DE_TMP.*, rownum r FROM ( {1} ) DE_TMP WHERE rownum <= {2} ) WHERE r > {3} ", + sqlColumn(xFields), createQuerySQLAsTmp(sql, fields, isGroup, fieldCustomFilter, rowPermissionsTree), + Integer.valueOf(page * realSize).toString(), Integer.valueOf((page - 1) * pageSize).toString()); + } + + @Override + public String createQueryTableWithLimit(String table, List fields, Integer limit, + boolean isGroup, Datasource ds, List fieldCustomFilter, List rowPermissionsTree) { + String schema = new Gson().fromJson(ds.getConfiguration(), JdbcConfiguration.class).getSchema(); + return String.format("SELECT * from %s WHERE rownum <= %s ", + schema + "." + String.format(OracleConstants.KEYWORD_TABLE, table), limit.toString()); + } + + @Override + public String createQuerySqlWithLimit(String sql, List fields, Integer limit, boolean isGroup, + List fieldCustomFilter, List rowPermissionsTree) { + return String.format("SELECT * from %s WHERE rownum <= %s ", "(" + sqlFix(sql) + ")", limit.toString()); + } + + @Override + public String getSQL(String table, List xAxis, List yAxis, + List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, + Datasource ds, ChartViewWithBLOBs view) { + SQLObj tableObj = SQLObj.builder() + .tableName((table.startsWith("(") && table.endsWith(")")) ? table + : String.format(OracleConstants.KEYWORD_TABLE, table)) + .tableAlias(String.format(OracleConstants.ALIAS_FIX, String.format(TABLE_ALIAS_PREFIX, 0))) + .build(); + setSchema(tableObj, ds); + List xFields = new ArrayList<>(); + List xOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(xAxis)) { + for (int i = 0; i < xAxis.size(); i++) { + ChartViewFieldDTO x = xAxis.get(i); + String originField; + if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(x.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 1) { + originField = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + x.getOriginName()); + } else { + originField = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + x.getOriginName()); + } + String fieldAlias = String.format(OracleConstants.ALIAS_FIX, + String.format(SQLConstants.FIELD_ALIAS_X_PREFIX, i)); + // 处理横轴字段 + xFields.add(getXFields(x, originField, fieldAlias)); + // 处理横轴排序 + if (StringUtils.isNotEmpty(x.getSort()) && Utils.joinSort(x.getSort())) { + xOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(x.getSort()) + .build()); + } + } + } + List yFields = new ArrayList<>(); + List yWheres = new ArrayList<>(); + List yOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(yAxis)) { + for (int i = 0; i < yAxis.size(); i++) { + ChartViewFieldDTO y = yAxis.get(i); + String originField; + if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(y.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 1) { + originField = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + y.getOriginName()); + } else { + originField = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + y.getOriginName()); + } + String fieldAlias = String.format(OracleConstants.ALIAS_FIX, + String.format(SQLConstants.FIELD_ALIAS_Y_PREFIX, i)); + // 处理纵轴字段 + yFields.add(getYFields(y, originField, fieldAlias)); + // 处理纵轴过滤 + yWheres.add(getYWheres(y, originField, fieldAlias)); + // 处理纵轴排序 + if (StringUtils.isNotEmpty(y.getSort()) && Utils.joinSort(y.getSort())) { + yOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(y.getSort()) + .build()); + } + } + } + // 处理视图中字段过滤 + String customWheres = transCustomFilterList(tableObj, fieldCustomFilter); + // 处理仪表板字段过滤 + String extWheres = transExtFilterList(tableObj, extFilterRequestList); + // row permissions tree + String whereTrees = transFilterTrees(tableObj, rowPermissionsTree); + // 构建sql所有参数 + List fields = new ArrayList<>(); + fields.addAll(xFields); + fields.addAll(yFields); + List wheres = new ArrayList<>(); + if (customWheres != null) + wheres.add(customWheres); + if (extWheres != null) + wheres.add(extWheres); + if (whereTrees != null) + wheres.add(whereTrees); + List groups = new ArrayList<>(); + groups.addAll(xFields); + // 外层再次套sql + List orders = new ArrayList<>(); + orders.addAll(xOrders); + orders.addAll(yOrders); + List aggWheres = new ArrayList<>(); + aggWheres.addAll(yWheres.stream().filter(ObjectUtils::isNotEmpty).collect(Collectors.toList())); + + STGroup stg = new STGroupFile(SQLConstants.SQL_TEMPLATE); + ST st_sql = stg.getInstanceOf("querySql"); + if (CollectionUtils.isNotEmpty(xFields)) + st_sql.add("groups", xFields); + if (CollectionUtils.isNotEmpty(yFields)) + st_sql.add("aggregators", yFields); + if (CollectionUtils.isNotEmpty(wheres)) + st_sql.add("filters", wheres); + if (ObjectUtils.isNotEmpty(tableObj)) + st_sql.add("table", tableObj); + String sql = st_sql.render(); + + ST st = stg.getInstanceOf("querySql"); + SQLObj tableSQL = SQLObj.builder() + .tableName(String.format(OracleConstants.BRACKETS, sql)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 1)) + .build(); + if (CollectionUtils.isNotEmpty(aggWheres)) + st.add("filters", aggWheres); + if (CollectionUtils.isNotEmpty(orders)) + st.add("orders", orders); + if (ObjectUtils.isNotEmpty(tableSQL)) + st.add("table", tableSQL); + return sqlLimit(st.render(), view); + } + + private String originalTableInfo(String table, List xAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, Datasource ds, ChartViewWithBLOBs view) { + SQLObj tableObj = SQLObj.builder() + .tableName((table.startsWith("(") && table.endsWith(")")) ? table + : String.format(OracleConstants.KEYWORD_TABLE, table)) + .tableAlias(String.format(OracleConstants.ALIAS_FIX, String.format(TABLE_ALIAS_PREFIX, 0))) + .build(); + setSchema(tableObj, ds); + List xFields = new ArrayList<>(); + List xOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(xAxis)) { + for (int i = 0; i < xAxis.size(); i++) { + ChartViewFieldDTO x = xAxis.get(i); + String originField; + if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(x.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 1) { + originField = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + x.getOriginName()); + } else { + if (x.getDeType() == 2 || x.getDeType() == 3) { + originField = String.format(OracleConstants.CAST, + String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), x.getOriginName()), + OracleConstants.DEFAULT_FLOAT_FORMAT); + } else { + originField = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + x.getOriginName()); + } + } + String fieldAlias = String.format(OracleConstants.ALIAS_FIX, + String.format(SQLConstants.FIELD_ALIAS_X_PREFIX, i)); + // 处理横轴字段 + xFields.add(getXFields(x, originField, fieldAlias)); + // 处理横轴排序 + if (StringUtils.isNotEmpty(x.getSort()) && Utils.joinSort(x.getSort())) { + xOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(x.getSort()) + .build()); + } + } + } + // 处理视图中字段过滤 + String customWheres = transCustomFilterList(tableObj, fieldCustomFilter); + // 处理仪表板字段过滤 + String extWheres = transExtFilterList(tableObj, extFilterRequestList); + // row permissions tree + String whereTrees = transFilterTrees(tableObj, rowPermissionsTree); + // 构建sql所有参数 + List fields = new ArrayList<>(); + fields.addAll(xFields); + List wheres = new ArrayList<>(); + if (customWheres != null) + wheres.add(customWheres); + if (extWheres != null) + wheres.add(extWheres); + if (whereTrees != null) + wheres.add(whereTrees); + List groups = new ArrayList<>(); + groups.addAll(xFields); + // 外层再次套sql + List orders = new ArrayList<>(); + orders.addAll(xOrders); + + STGroup stg = new STGroupFile(SQLConstants.SQL_TEMPLATE); + ST st_sql = stg.getInstanceOf("previewSql"); + st_sql.add("isGroup", false); + if (CollectionUtils.isNotEmpty(xFields)) + st_sql.add("groups", xFields); + if (CollectionUtils.isNotEmpty(wheres)) + st_sql.add("filters", wheres); + if (ObjectUtils.isNotEmpty(tableObj)) + st_sql.add("table", tableObj); + String sql = st_sql.render(); + + ST st = stg.getInstanceOf("previewSql"); + st.add("isGroup", false); + SQLObj tableSQL = SQLObj.builder() + .tableName(String.format(OracleConstants.BRACKETS, sql)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 1)) + .build(); + if (CollectionUtils.isNotEmpty(orders)) + st.add("orders", orders); + if (ObjectUtils.isNotEmpty(tableSQL)) + st.add("table", tableSQL); + return st.render(); + } + @Override + public String getSQLTableInfo(String table, List xAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, Datasource ds, ChartViewWithBLOBs view) { + return sqlLimit(originalTableInfo(table, xAxis, fieldCustomFilter, rowPermissionsTree, extFilterRequestList, ds, view), view); + } + + @Override + public String getSQLWithPage(boolean isTable, String table, List xAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, Datasource ds, ChartViewWithBLOBs view, PageInfo pageInfo) { + String limit = ((pageInfo.getGoPage() != null && pageInfo.getPageSize() != null) ? " LIMIT " + (pageInfo.getGoPage() - 1) * pageInfo.getPageSize() + "," + pageInfo.getPageSize() : ""); + if (isTable) { + return originalTableInfo(table, xAxis, fieldCustomFilter, rowPermissionsTree, extFilterRequestList, ds, view) + limit; + } else { + return originalTableInfo("(" + sqlFix(table) + ")", xAxis, fieldCustomFilter, rowPermissionsTree, extFilterRequestList, ds, view) + limit; + } + } + + @Override + public String getSQLAsTmpTableInfo(String sql, List xAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, Datasource ds, ChartViewWithBLOBs view) { + return getSQLTableInfo("(" + sqlFix(sql) + ")", xAxis, fieldCustomFilter, rowPermissionsTree, extFilterRequestList, null, view); + } + + @Override + public String getSQLAsTmp(String sql, List xAxis, List yAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, ChartViewWithBLOBs view) { + return getSQL("(" + sqlFix(sql) + ")", xAxis, yAxis, fieldCustomFilter, rowPermissionsTree, extFilterRequestList, null, view); + } + + @Override + public String getSQLStack(String table, List xAxis, List yAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, List extStack, Datasource ds, ChartViewWithBLOBs view) { + SQLObj tableObj = SQLObj.builder() + .tableName((table.startsWith("(") && table.endsWith(")")) ? table + : String.format(OracleConstants.KEYWORD_TABLE, table)) + .tableAlias(String.format(OracleConstants.ALIAS_FIX, String.format(TABLE_ALIAS_PREFIX, 0))) + .build(); + setSchema(tableObj, ds); + List xFields = new ArrayList<>(); + List xOrders = new ArrayList<>(); + List xList = new ArrayList<>(); + xList.addAll(xAxis); + xList.addAll(extStack); + if (CollectionUtils.isNotEmpty(xList)) { + for (int i = 0; i < xList.size(); i++) { + ChartViewFieldDTO x = xList.get(i); + String originField; + if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(x.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 1) { + originField = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + x.getOriginName()); + } else { + originField = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + x.getOriginName()); + } + String fieldAlias = String.format(OracleConstants.ALIAS_FIX, + String.format(SQLConstants.FIELD_ALIAS_X_PREFIX, i)); + // 处理横轴字段 + xFields.add(getXFields(x, originField, fieldAlias)); + // 处理横轴排序 + if (StringUtils.isNotEmpty(x.getSort()) && Utils.joinSort(x.getSort())) { + xOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(x.getSort()) + .build()); + } + } + } + List yFields = new ArrayList<>(); + List yWheres = new ArrayList<>(); + List yOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(yAxis)) { + for (int i = 0; i < yAxis.size(); i++) { + ChartViewFieldDTO y = yAxis.get(i); + String originField; + if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(y.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 1) { + originField = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + y.getOriginName()); + } else { + originField = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + y.getOriginName()); + } + String fieldAlias = String.format(OracleConstants.ALIAS_FIX, + String.format(SQLConstants.FIELD_ALIAS_Y_PREFIX, i)); + // 处理纵轴字段 + yFields.add(getYFields(y, originField, fieldAlias)); + // 处理纵轴过滤 + yWheres.add(getYWheres(y, originField, fieldAlias)); + // 处理纵轴排序 + if (StringUtils.isNotEmpty(y.getSort()) && Utils.joinSort(y.getSort())) { + yOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(y.getSort()) + .build()); + } + } + } + // 处理视图中字段过滤 + String customWheres = transCustomFilterList(tableObj, fieldCustomFilter); + // 处理仪表板字段过滤 + String extWheres = transExtFilterList(tableObj, extFilterRequestList); + // row permissions tree + String whereTrees = transFilterTrees(tableObj, rowPermissionsTree); + // 构建sql所有参数 + List fields = new ArrayList<>(); + fields.addAll(xFields); + fields.addAll(yFields); + List wheres = new ArrayList<>(); + if (customWheres != null) + wheres.add(customWheres); + if (extWheres != null) + wheres.add(extWheres); + if (whereTrees != null) + wheres.add(whereTrees); + List groups = new ArrayList<>(); + groups.addAll(xFields); + // 外层再次套sql + List orders = new ArrayList<>(); + orders.addAll(xOrders); + orders.addAll(yOrders); + List aggWheres = new ArrayList<>(); + aggWheres.addAll(yWheres.stream().filter(ObjectUtils::isNotEmpty).collect(Collectors.toList())); + + STGroup stg = new STGroupFile(SQLConstants.SQL_TEMPLATE); + ST st_sql = stg.getInstanceOf("querySql"); + if (CollectionUtils.isNotEmpty(xFields)) + st_sql.add("groups", xFields); + if (CollectionUtils.isNotEmpty(yFields)) + st_sql.add("aggregators", yFields); + if (CollectionUtils.isNotEmpty(wheres)) + st_sql.add("filters", wheres); + if (ObjectUtils.isNotEmpty(tableObj)) + st_sql.add("table", tableObj); + String sql = st_sql.render(); + + ST st = stg.getInstanceOf("querySql"); + SQLObj tableSQL = SQLObj.builder() + .tableName(String.format(OracleConstants.BRACKETS, sql)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 1)) + .build(); + if (CollectionUtils.isNotEmpty(aggWheres)) + st.add("filters", aggWheres); + if (CollectionUtils.isNotEmpty(orders)) + st.add("orders", orders); + if (ObjectUtils.isNotEmpty(tableSQL)) + st.add("table", tableSQL); + return sqlLimit(st.render(), view); + } + + @Override + public String getSQLAsTmpStack(String table, List xAxis, List yAxis, + List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, + List extStack, ChartViewWithBLOBs view) { + return getSQLStack("(" + sqlFix(table) + ")", xAxis, yAxis, fieldCustomFilter, rowPermissionsTree, extFilterRequestList, extStack, + null, view); + } + + @Override + public String getSQLScatter(String table, List xAxis, List yAxis, + List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, + List extBubble, Datasource ds, ChartViewWithBLOBs view) { + SQLObj tableObj = SQLObj.builder() + .tableName((table.startsWith("(") && table.endsWith(")")) ? table + : String.format(OracleConstants.KEYWORD_TABLE, table)) + .tableAlias(String.format(OracleConstants.ALIAS_FIX, String.format(TABLE_ALIAS_PREFIX, 0))) + .build(); + setSchema(tableObj, ds); + List xFields = new ArrayList<>(); + List xOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(xAxis)) { + for (int i = 0; i < xAxis.size(); i++) { + ChartViewFieldDTO x = xAxis.get(i); + String originField; + if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(x.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 1) { + originField = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + x.getOriginName()); + } else { + originField = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + x.getOriginName()); + } + String fieldAlias = String.format(OracleConstants.ALIAS_FIX, + String.format(SQLConstants.FIELD_ALIAS_X_PREFIX, i)); + // 处理横轴字段 + xFields.add(getXFields(x, originField, fieldAlias)); + // 处理横轴排序 + if (StringUtils.isNotEmpty(x.getSort()) && Utils.joinSort(x.getSort())) { + xOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(x.getSort()) + .build()); + } + } + } + List yFields = new ArrayList<>(); + List yWheres = new ArrayList<>(); + List yOrders = new ArrayList<>(); + List yList = new ArrayList<>(); + yList.addAll(yAxis); + yList.addAll(extBubble); + if (CollectionUtils.isNotEmpty(yList)) { + for (int i = 0; i < yList.size(); i++) { + ChartViewFieldDTO y = yList.get(i); + String originField; + if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(y.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 1) { + originField = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + y.getOriginName()); + } else { + originField = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + y.getOriginName()); + } + String fieldAlias = String.format(OracleConstants.ALIAS_FIX, + String.format(SQLConstants.FIELD_ALIAS_Y_PREFIX, i)); + // 处理纵轴字段 + yFields.add(getYFields(y, originField, fieldAlias)); + // 处理纵轴过滤 + yWheres.add(getYWheres(y, originField, fieldAlias)); + // 处理纵轴排序 + if (StringUtils.isNotEmpty(y.getSort()) && Utils.joinSort(y.getSort())) { + yOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(y.getSort()) + .build()); + } + } + } + // 处理视图中字段过滤 + String customWheres = transCustomFilterList(tableObj, fieldCustomFilter); + // 处理仪表板字段过滤 + String extWheres = transExtFilterList(tableObj, extFilterRequestList); + // row permissions tree + String whereTrees = transFilterTrees(tableObj, rowPermissionsTree); + // 构建sql所有参数 + List fields = new ArrayList<>(); + fields.addAll(xFields); + fields.addAll(yFields); + List wheres = new ArrayList<>(); + if (customWheres != null) + wheres.add(customWheres); + if (extWheres != null) + wheres.add(extWheres); + if (whereTrees != null) + wheres.add(whereTrees); + List groups = new ArrayList<>(); + groups.addAll(xFields); + // 外层再次套sql + List orders = new ArrayList<>(); + orders.addAll(xOrders); + orders.addAll(yOrders); + List aggWheres = new ArrayList<>(); + aggWheres.addAll(yWheres.stream().filter(ObjectUtils::isNotEmpty).collect(Collectors.toList())); + + STGroup stg = new STGroupFile(SQLConstants.SQL_TEMPLATE); + ST st_sql = stg.getInstanceOf("querySql"); + if (CollectionUtils.isNotEmpty(xFields)) + st_sql.add("groups", xFields); + if (CollectionUtils.isNotEmpty(yFields)) + st_sql.add("aggregators", yFields); + if (CollectionUtils.isNotEmpty(wheres)) + st_sql.add("filters", wheres); + if (ObjectUtils.isNotEmpty(tableObj)) + st_sql.add("table", tableObj); + String sql = st_sql.render(); + + ST st = stg.getInstanceOf("querySql"); + SQLObj tableSQL = SQLObj.builder() + .tableName(String.format(OracleConstants.BRACKETS, sql)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 1)) + .build(); + if (CollectionUtils.isNotEmpty(aggWheres)) + st.add("filters", aggWheres); + if (CollectionUtils.isNotEmpty(orders)) + st.add("orders", orders); + if (ObjectUtils.isNotEmpty(tableSQL)) + st.add("table", tableSQL); + return sqlLimit(st.render(), view); + } + + @Override + public String getSQLAsTmpScatter(String table, List xAxis, List yAxis, + List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, + List extBubble, ChartViewWithBLOBs view) { + return getSQLScatter("(" + sqlFix(table) + ")", xAxis, yAxis, fieldCustomFilter, rowPermissionsTree, extFilterRequestList, + extBubble, null, view); + } + + @Override + public String searchTable(String table) { + return "SELECT table_name FROM information_schema.TABLES WHERE table_name ='" + table + "'"; + } + + @Override + public String getSQLSummary(String table, List yAxis, + List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, + ChartViewWithBLOBs view, Datasource ds) { + // 字段汇总 排序等 + SQLObj tableObj = SQLObj.builder() + .tableName((table.startsWith("(") && table.endsWith(")")) ? table + : String.format(OracleConstants.KEYWORD_TABLE, table)) + .tableAlias(String.format(OracleConstants.ALIAS_FIX, String.format(TABLE_ALIAS_PREFIX, 0))) + .build(); + setSchema(tableObj, ds); + List yFields = new ArrayList<>(); + List yWheres = new ArrayList<>(); + List yOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(yAxis)) { + for (int i = 0; i < yAxis.size(); i++) { + ChartViewFieldDTO y = yAxis.get(i); + String originField; + if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(y.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 1) { + originField = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + y.getOriginName()); + } else { + originField = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + y.getOriginName()); + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_Y_PREFIX, i); + // 处理纵轴字段 + yFields.add(getYFields(y, originField, fieldAlias)); + // 处理纵轴过滤 + yWheres.add(getYWheres(y, originField, fieldAlias)); + // 处理纵轴排序 + if (StringUtils.isNotEmpty(y.getSort()) && Utils.joinSort(y.getSort())) { + yOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(y.getSort()) + .build()); + } + } + } + // 处理视图中字段过滤 + String customWheres = transCustomFilterList(tableObj, fieldCustomFilter); + // 处理仪表板字段过滤 + String extWheres = transExtFilterList(tableObj, extFilterRequestList); + // row permissions tree + String whereTrees = transFilterTrees(tableObj, rowPermissionsTree); + // 构建sql所有参数 + List fields = new ArrayList<>(); + fields.addAll(yFields); + List wheres = new ArrayList<>(); + if (customWheres != null) + wheres.add(customWheres); + if (extWheres != null) + wheres.add(extWheres); + if (whereTrees != null) + wheres.add(whereTrees); + // 外层再次套sql + List orders = new ArrayList<>(); + orders.addAll(yOrders); + List aggWheres = new ArrayList<>(); + aggWheres.addAll(yWheres.stream().filter(ObjectUtils::isNotEmpty).collect(Collectors.toList())); + + STGroup stg = new STGroupFile(SQLConstants.SQL_TEMPLATE); + ST st_sql = stg.getInstanceOf("querySql"); + if (CollectionUtils.isNotEmpty(yFields)) + st_sql.add("aggregators", yFields); + if (CollectionUtils.isNotEmpty(wheres)) + st_sql.add("filters", wheres); + if (ObjectUtils.isNotEmpty(tableObj)) + st_sql.add("table", tableObj); + String sql = st_sql.render(); + + ST st = stg.getInstanceOf("querySql"); + SQLObj tableSQL = SQLObj.builder() + .tableName(String.format(OracleConstants.BRACKETS, sql)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 1)) + .build(); + if (CollectionUtils.isNotEmpty(aggWheres)) + st.add("filters", aggWheres); + if (CollectionUtils.isNotEmpty(orders)) + st.add("orders", orders); + if (ObjectUtils.isNotEmpty(tableSQL)) + st.add("table", tableSQL); + return sqlLimit(st.render(), view); + } + + @Override + public String getSQLSummaryAsTmp(String sql, List yAxis, + List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, + ChartViewWithBLOBs view) { + return getSQLSummary("(" + sqlFix(sql) + ")", yAxis, fieldCustomFilter, rowPermissionsTree, extFilterRequestList, view, null); + } + + @Override + public String wrapSql(String sql) { + sql = sql.trim(); + if (sql.lastIndexOf(";") == (sql.length() - 1)) { + sql = sql.substring(0, sql.length() - 1); + } + String tmpSql = "SELECT * FROM (" + sql + ") DE_TMP " + " where rownum <= 0"; + return tmpSql; + } + + @Override + public String createRawQuerySQL(String table, List fields, Datasource ds) { + String[] array = fields.stream().map(f -> { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(" \"").append(f.getOriginName()).append("\""); + return stringBuilder.toString(); + }).toArray(String[]::new); + DmConfig dmConfig = new Gson().fromJson(ds.getConfiguration(), DmConfig.class); + return MessageFormat.format("SELECT {0} FROM {1}", StringUtils.join(array, ","), + dmConfig.getSchema() + ".\"" + table + "\""); + } + + @Override + public String createRawQuerySQLAsTmp(String sql, List fields) { + String[] array = fields.stream().map(f -> { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(" \"").append(f.getOriginName()).append("\""); + return stringBuilder.toString(); + }).toArray(String[]::new); + return MessageFormat.format("SELECT {0} FROM {1}", StringUtils.join(array, ","), + " (" + sqlFix(sql) + ") DE_TMP "); + } + + @Override + public String transTreeItem(SQLObj tableObj, DatasetRowPermissionsTreeItem item) { + String res = null; + DatasetTableField field = item.getField(); + if (ObjectUtils.isEmpty(field)) { + return null; + } + String whereName = ""; + String originName; + if (ObjectUtils.isNotEmpty(field.getExtField()) && field.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originName = calcFieldRegex(field.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(field.getExtField()) && field.getExtField() == 1) { + originName = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + field.getOriginName()); + } else { + originName = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + field.getOriginName()); + } + + if (field.getDeType() == 1) { + if (field.getDeExtractType() == 0 || field.getDeExtractType() == 5) { + whereName = String.format(OracleConstants.TO_DATE, originName, StringUtils.isNotEmpty(field.getDateFormat()) ? field.getDateFormat() : OracleConstants.DEFAULT_DATE_FORMAT); + } + if (field.getDeExtractType() == 2 || field.getDeExtractType() == 3 || field.getDeExtractType() == 4) { + String cast = String.format(OracleConstants.CAST, originName, OracleConstants.DEFAULT_INT_FORMAT) + + "/1000"; + whereName = String.format(OracleConstants.FROM_UNIXTIME, cast, OracleConstants.DEFAULT_DATE_FORMAT); + } + if (field.getDeExtractType() == 1) { + whereName = originName; + } + } else if (field.getDeType() == 2 || field.getDeType() == 3) { + if (field.getDeExtractType() == 0 || field.getDeExtractType() == 5) { + whereName = String.format(OracleConstants.CAST, originName, OracleConstants.DEFAULT_FLOAT_FORMAT); + } + if (field.getDeExtractType() == 1) { + whereName = String.format(OracleConstants.UNIX_TIMESTAMP, originName) + "*1000"; + } + if (field.getDeExtractType() == 2 || field.getDeExtractType() == 3 || field.getDeExtractType() == 4) { + whereName = originName; + } + } else { + whereName = originName; + } + + if (StringUtils.equalsIgnoreCase(item.getFilterType(), "enum")) { + if (CollectionUtils.isNotEmpty(item.getEnumValue())) { + res = "(" + whereName + " IN ('" + String.join("','", item.getEnumValue()) + "'))"; + } + } else { + String value = item.getValue(); + String whereTerm = transMysqlFilterTerm(item.getTerm()); + String whereValue = ""; + + if (StringUtils.equalsIgnoreCase(item.getTerm(), "null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(item.getTerm(), "not_null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(item.getTerm(), "empty")) { + whereValue = "''"; + } else if (StringUtils.equalsIgnoreCase(item.getTerm(), "not_empty")) { + whereValue = "''"; + } else if (StringUtils.containsIgnoreCase(item.getTerm(), "in") + || StringUtils.containsIgnoreCase(item.getTerm(), "not in")) { + whereValue = "('" + String.join("','", value.split(",")) + "')"; + } else if (StringUtils.containsIgnoreCase(item.getTerm(), "like")) { + whereValue = "'%" + value + "%'"; + } else { + if (field.getDeType() == 1) { + whereValue = String.format(OracleConstants.TO_DATE, "'" + value + "'", OracleConstants.DEFAULT_DATE_FORMAT); + } else { + whereValue = String.format(OracleConstants.WHERE_VALUE_VALUE, value); + } + } + SQLObj build = SQLObj.builder() + .whereField(whereName) + .whereTermAndValue(whereTerm + whereValue) + .build(); + res = build.getWhereField() + " " + build.getWhereTermAndValue(); + } + return res; + } + + @Override + public String convertTableToSql(String tableName, Datasource ds) { + String schema = new Gson().fromJson(ds.getConfiguration(), JdbcConfiguration.class).getSchema(); + schema = String.format(OracleConstants.KEYWORD_TABLE, schema); + return createSQLPreview( + "SELECT * FROM " + schema + "." + String.format(OracleConstants.KEYWORD_TABLE, tableName), null); + } + + public String transMysqlFilterTerm(String term) { + switch (term) { + case "eq": + return " = "; + case "not_eq": + return " <> "; + case "lt": + return " < "; + case "le": + return " <= "; + case "gt": + return " > "; + case "ge": + return " >= "; + case "in": + return " IN "; + case "not in": + return " NOT IN "; + case "like": + return " LIKE "; + case "not like": + return " NOT LIKE "; + case "null": + return " IS NULL "; + case "not_null": + return " IS NOT NULL "; + case "empty": + return " = "; + case "not_empty": + return " <> "; + case "between": + return " BETWEEN "; + default: + return ""; + } + } + + public String transCustomFilterList(SQLObj tableObj, List requestList) { + if (CollectionUtils.isEmpty(requestList)) { + return null; + } + List res = new ArrayList<>(); + for (ChartFieldCustomFilterDTO request : requestList) { + List list = new ArrayList<>(); + DatasetTableField field = request.getField(); + + if (ObjectUtils.isEmpty(field)) { + continue; + } + String whereName = ""; + String originName; + if (ObjectUtils.isNotEmpty(field.getExtField()) && field.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originName = calcFieldRegex(field.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(field.getExtField()) && field.getExtField() == 1) { + originName = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + field.getOriginName()); + } else { + originName = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + field.getOriginName()); + } + + if (field.getDeType() == 1) { + if (field.getDeExtractType() == 0 || field.getDeExtractType() == 5) { + whereName = String.format(OracleConstants.TO_DATE, originName, StringUtils.isNotEmpty(field.getDateFormat()) ? field.getDateFormat() : OracleConstants.DEFAULT_DATE_FORMAT); + } + if (field.getDeExtractType() == 2 || field.getDeExtractType() == 3 || field.getDeExtractType() == 4) { + String cast = String.format(OracleConstants.CAST, originName, OracleConstants.DEFAULT_INT_FORMAT) + + "/1000"; + whereName = String.format(OracleConstants.FROM_UNIXTIME, cast, OracleConstants.DEFAULT_DATE_FORMAT); + } + if (field.getDeExtractType() == 1) { + whereName = originName; + } + } else if (field.getDeType() == 2 || field.getDeType() == 3) { + if (field.getDeExtractType() == 0 || field.getDeExtractType() == 5) { + whereName = String.format(OracleConstants.CAST, originName, OracleConstants.DEFAULT_FLOAT_FORMAT); + } + if (field.getDeExtractType() == 1) { + whereName = String.format(OracleConstants.UNIX_TIMESTAMP, originName) + "*1000"; + } + if (field.getDeExtractType() == 2 || field.getDeExtractType() == 3 || field.getDeExtractType() == 4) { + whereName = originName; + } + } else { + whereName = originName; + } + + if (StringUtils.equalsIgnoreCase(request.getFilterType(), "enum")) { + if (CollectionUtils.isNotEmpty(request.getEnumCheckField())) { + res.add("(" + whereName + " IN ('" + String.join("','", request.getEnumCheckField()) + "'))"); + } + } else { + List filter = request.getFilter(); + for (ChartCustomFilterItemDTO filterItemDTO : filter) { + String value = filterItemDTO.getValue(); + String whereTerm = transMysqlFilterTerm(filterItemDTO.getTerm()); + String whereValue = ""; + + if (StringUtils.equalsIgnoreCase(filterItemDTO.getTerm(), "null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(filterItemDTO.getTerm(), "not_null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(filterItemDTO.getTerm(), "empty")) { + whereValue = "''"; + } else if (StringUtils.equalsIgnoreCase(filterItemDTO.getTerm(), "not_empty")) { + whereValue = "''"; + } else if (StringUtils.containsIgnoreCase(filterItemDTO.getTerm(), "in") + || StringUtils.containsIgnoreCase(filterItemDTO.getTerm(), "not in")) { + whereValue = "('" + String.join("','", value.split(",")) + "')"; + } else if (StringUtils.containsIgnoreCase(filterItemDTO.getTerm(), "like")) { + whereValue = "'%" + value + "%'"; + } else { + if (field.getDeType() == 1) { + whereValue = String.format(OracleConstants.TO_DATE, "'" + value + "'", StringUtils.isNotEmpty(field.getDateFormat()) ? field.getDateFormat() : OracleConstants.DEFAULT_DATE_FORMAT); + } else { + whereValue = String.format(OracleConstants.WHERE_VALUE_VALUE, value); + } + } + list.add(SQLObj.builder() + .whereField(whereName) + .whereTermAndValue(whereTerm + whereValue) + .build()); + } + + List strList = new ArrayList<>(); + list.forEach(ele -> strList.add(ele.getWhereField() + " " + ele.getWhereTermAndValue())); + if (CollectionUtils.isNotEmpty(list)) { + res.add("(" + String.join(" " + getLogic(request.getLogic()) + " ", strList) + ")"); + } + } + } + return CollectionUtils.isNotEmpty(res) ? "(" + String.join(" AND ", res) + ")" : null; + } + + public String transExtFilterList(SQLObj tableObj, List requestList) { + if (CollectionUtils.isEmpty(requestList)) { + return null; + } + List list = new ArrayList<>(); + for (ChartExtFilterRequest request : requestList) { + List value = request.getValue(); + + List whereNameList = new ArrayList<>(); + List fieldList = new ArrayList<>(); + if (request.getIsTree()) { + fieldList.addAll(request.getDatasetTableFieldList()); + } else { + fieldList.add(request.getDatasetTableField()); + } + + for (DatasetTableField field : fieldList) { + if (CollectionUtils.isEmpty(value) || ObjectUtils.isEmpty(field)) { + continue; + } + String whereName = ""; + + String originName; + if (ObjectUtils.isNotEmpty(field.getExtField()) && field.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originName = calcFieldRegex(field.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(field.getExtField()) && field.getExtField() == 1) { + originName = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + field.getOriginName()); + } else { + originName = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + field.getOriginName()); + } + + if (field.getDeType() == 1) { + if (field.getDeExtractType() == 0 || field.getDeExtractType() == 5) { + whereName = String.format(OracleConstants.TO_DATE, originName, StringUtils.isNotEmpty(field.getDateFormat()) ? field.getDateFormat() : OracleConstants.DEFAULT_DATE_FORMAT); + } + if (field.getDeExtractType() == 2 || field.getDeExtractType() == 3 + || field.getDeExtractType() == 4) { + String cast = String.format(OracleConstants.CAST, originName, + OracleConstants.DEFAULT_INT_FORMAT) + "/1000"; + whereName = String.format(OracleConstants.FROM_UNIXTIME, cast, + OracleConstants.DEFAULT_DATE_FORMAT); + } + if (field.getDeExtractType() == 1) { + whereName = originName; + } + } else if (field.getDeType() == 2 || field.getDeType() == 3) { + if (field.getDeExtractType() == 0 || field.getDeExtractType() == 5) { + whereName = String.format(OracleConstants.CAST, originName, + OracleConstants.DEFAULT_FLOAT_FORMAT); + } + if (field.getDeExtractType() == 1) { + whereName = String.format(OracleConstants.UNIX_TIMESTAMP, originName) + "*1000"; + } + if (field.getDeExtractType() == 2 || field.getDeExtractType() == 3 + || field.getDeExtractType() == 4) { + whereName = originName; + } + } else { + whereName = originName; + } + whereNameList.add(whereName); + } + + String whereName = ""; + if (request.getIsTree()) { + whereName = StringUtils.join(whereNameList, "||','||"); + } else { + whereName = whereNameList.get(0); + } + String whereTerm = transMysqlFilterTerm(request.getOperator()); + String whereValue = ""; + + if (StringUtils.containsIgnoreCase(request.getOperator(), "in")) { + whereValue = "('" + StringUtils.join(value, "','") + "')"; + } else if (StringUtils.containsIgnoreCase(request.getOperator(), "like")) { + String keyword = value.get(0).toUpperCase(); + whereValue = "'%" + keyword + "%'"; + whereName = "upper(" + whereName + ")"; + } else if (StringUtils.containsIgnoreCase(request.getOperator(), "between")) { + if (request.getDatasetTableField().getDeType() == 1) { + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String startTime = simpleDateFormat.format(new Date(Long.parseLong(value.get(0)))); + String endTime = simpleDateFormat.format(new Date(Long.parseLong(value.get(1)))); + String st = String.format(OracleConstants.TO_DATE, "'" + startTime + "'", + OracleConstants.DEFAULT_DATE_FORMAT); + String et = String.format(OracleConstants.TO_DATE, "'" + endTime + "'", + OracleConstants.DEFAULT_DATE_FORMAT); + whereValue = st + " AND " + et; + } else { + whereValue = String.format(OracleConstants.WHERE_BETWEEN, value.get(0), value.get(1)); + } + } else { + whereValue = String.format(OracleConstants.WHERE_VALUE_VALUE, value.get(0)); + } + list.add(SQLObj.builder() + .whereField(whereName) + .whereTermAndValue(whereTerm + whereValue) + .build()); + } + List strList = new ArrayList<>(); + list.forEach(ele -> strList.add(ele.getWhereField() + " " + ele.getWhereTermAndValue())); + return CollectionUtils.isNotEmpty(list) ? "(" + String.join(" AND ", strList) + ")" : null; + } + + private String sqlFix(String sql) { + if (sql.lastIndexOf(";") == (sql.length() - 1)) { + sql = sql.substring(0, sql.length() - 1); + } + return sql; + } + + private String transDateFormat(String dateStyle, String datePattern) { + String split = "-"; + if (StringUtils.equalsIgnoreCase(datePattern, "date_sub")) { + split = "-"; + } else if (StringUtils.equalsIgnoreCase(datePattern, "date_split")) { + split = "/"; + } else { + split = "-"; + } + + if (StringUtils.isEmpty(dateStyle)) { + return OracleConstants.DEFAULT_DATE_FORMAT; + } + + switch (dateStyle) { + case "y": + return "YYYY"; + case "y_M": + return "YYYY" + split + "MM"; + case "y_M_d": + return "YYYY" + split + "MM" + split + "DD"; + case "H_m_s": + return "HH24:MI:SS"; + case "y_M_d_H_m": + return "YYYY" + split + "MM" + split + "DD" + " HH24:MI"; + case "y_M_d_H_m_s": + return "YYYY" + split + "MM" + split + "DD" + " HH24:MI:SS"; + default: + return OracleConstants.DEFAULT_DATE_FORMAT; + } + } + + private SQLObj getXFields(ChartViewFieldDTO x, String originField, String fieldAlias) { + String fieldName = ""; + if (x.getDeExtractType() == DeTypeConstants.DE_TIME) { + if (x.getDeType() == DeTypeConstants.DE_INT || x.getDeType() == DeTypeConstants.DE_FLOAT) { // 时间转数值 + if (x.getType().equalsIgnoreCase("DATE")) { + String date = String.format(OracleConstants.CALC_SUB, originField, + String.format(OracleConstants.TO_DATE, OracleConstants.DEFAULT_START_DATE, + OracleConstants.DEFAULT_DATE_FORMAT)); + fieldName = String.format(OracleConstants.TO_NUMBER, date) + OracleConstants.TO_MS; + } else { + String toChar = String.format(OracleConstants.TO_CHAR, originField, + OracleConstants.DEFAULT_DATE_FORMAT); + String toDate = String.format(OracleConstants.TO_DATE, toChar, OracleConstants.DEFAULT_DATE_FORMAT); + String toDate1 = String.format(OracleConstants.TO_DATE, OracleConstants.DEFAULT_START_DATE, + OracleConstants.DEFAULT_DATE_FORMAT); + fieldName = String.format(OracleConstants.TO_NUMBER, + String.format(OracleConstants.CALC_SUB, toDate, toDate1)) + OracleConstants.TO_MS; + } + } else if (x.getDeType() == DeTypeConstants.DE_TIME) { // 格式化显示时间 + String format = transDateFormat(x.getDateStyle(), x.getDatePattern()); + if (x.getType().equalsIgnoreCase("DATE")) { + fieldName = String.format(OracleConstants.TO_CHAR, originField, format); + } else { + String toChar = String.format(OracleConstants.TO_CHAR, originField, + OracleConstants.DEFAULT_DATE_FORMAT); + String toDate = String.format(OracleConstants.TO_DATE, toChar, OracleConstants.DEFAULT_DATE_FORMAT); + fieldName = String.format(OracleConstants.TO_CHAR, toDate, format); + } + } else { + fieldName = originField; + } + } else { + if (x.getDeType() == DeTypeConstants.DE_TIME) { + String format = transDateFormat(x.getDateStyle(), x.getDatePattern()); + if (x.getDeExtractType() == DeTypeConstants.DE_STRING) { // 字符串转时间 + String toDate = String.format(OracleConstants.TO_DATE, originField, StringUtils.isNotEmpty(x.getDateFormat()) ? x.getDateFormat() : OracleConstants.DEFAULT_DATE_FORMAT); + fieldName = String.format(OracleConstants.TO_CHAR, toDate, format); + } else { // 数值转时间 + String date = originField + "/(1000 * 60 * 60 * 24)+" + String.format(OracleConstants.TO_DATE, + OracleConstants.DEFAULT_START_DATE, OracleConstants.DEFAULT_DATE_FORMAT); + fieldName = String.format(OracleConstants.TO_CHAR, date, format); + } + } else { + if (x.getDeType() == DeTypeConstants.DE_INT) { + fieldName = String.format(OracleConstants.CAST, originField, OracleConstants.DEFAULT_INT_FORMAT); + } else if (x.getDeType() == DeTypeConstants.DE_FLOAT) { + fieldName = String.format(OracleConstants.CAST, originField, OracleConstants.DEFAULT_FLOAT_FORMAT); + } else { + fieldName = originField; + } + } + } + return SQLObj.builder() + .fieldName(fieldName) + .fieldAlias(fieldAlias) + .build(); + } + + private List getXWheres(ChartViewFieldDTO x, String originField, String fieldAlias) { + List list = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(x.getFilter()) && x.getFilter().size() > 0) { + x.getFilter().forEach(f -> { + String whereName = ""; + String whereTerm = transMysqlFilterTerm(f.getTerm()); + String whereValue = ""; + if (x.getDeType() == 1 && x.getDeExtractType() != 1) { + String cast = String.format(OracleConstants.CAST, originField, OracleConstants.DEFAULT_INT_FORMAT) + + "/1000"; + whereName = String.format(OracleConstants.FROM_UNIXTIME, cast, OracleConstants.DEFAULT_DATE_FORMAT); + } else { + whereName = originField; + } + if (StringUtils.equalsIgnoreCase(f.getTerm(), "null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(f.getTerm(), "not_null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(f.getTerm(), "empty")) { + whereValue = "''"; + } else if (StringUtils.equalsIgnoreCase(f.getTerm(), "not_empty")) { + whereValue = "''"; + } else if (StringUtils.containsIgnoreCase(f.getTerm(), "in")) { + whereValue = "('" + StringUtils.join(f.getValue(), "','") + "')"; + } else if (StringUtils.containsIgnoreCase(f.getTerm(), "like")) { + whereValue = "'%" + f.getValue() + "%'"; + } else { + whereValue = String.format(OracleConstants.WHERE_VALUE_VALUE, f.getValue()); + } + list.add(SQLObj.builder() + .whereField(whereName) + .whereAlias(fieldAlias) + .whereTermAndValue(whereTerm + whereValue) + .build()); + }); + } + return list; + } + + private SQLObj getYFields(ChartViewFieldDTO y, String originField, String fieldAlias) { + String fieldName = ""; + if (StringUtils.equalsIgnoreCase(y.getOriginName(), "*")) { + fieldName = OracleConstants.AGG_COUNT; + } else if (SQLConstants.DIMENSION_TYPE.contains(y.getDeType())) { + if (StringUtils.equalsIgnoreCase(y.getSummary(), "count_distinct")) { + fieldName = String.format(OracleConstants.AGG_FIELD, "COUNT", "DISTINCT " + originField); + } else if (StringUtils.equalsIgnoreCase(y.getSummary(), "group_concat")) { + fieldName = String.format(OracleConstants.GROUP_CONCAT, originField, originField); + } else { + fieldName = String.format(OracleConstants.AGG_FIELD, y.getSummary(), originField); + } + } else { + if (StringUtils.equalsIgnoreCase(y.getSummary(), "avg") + || StringUtils.containsIgnoreCase(y.getSummary(), "pop")) { + String cast = String.format(OracleConstants.CAST, originField, + y.getDeType() == 2 ? OracleConstants.DEFAULT_INT_FORMAT : OracleConstants.DEFAULT_FLOAT_FORMAT); + String agg = String.format(OracleConstants.AGG_FIELD, y.getSummary(), cast); + fieldName = String.format(OracleConstants.CAST, agg, OracleConstants.DEFAULT_FLOAT_FORMAT); + } else { + String cast = String.format(OracleConstants.CAST, originField, + y.getDeType() == 2 ? OracleConstants.DEFAULT_INT_FORMAT : OracleConstants.DEFAULT_FLOAT_FORMAT); + if (StringUtils.equalsIgnoreCase(y.getSummary(), "count_distinct")) { + fieldName = String.format(OracleConstants.AGG_FIELD, "COUNT", "DISTINCT " + cast); + } else { + fieldName = String.format(OracleConstants.AGG_FIELD, y.getSummary(), cast); + } + } + } + return SQLObj.builder() + .fieldName(fieldName) + .fieldAlias(fieldAlias) + .build(); + } + + private String getYWheres(ChartViewFieldDTO y, String originField, String fieldAlias) { + List list = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(y.getFilter()) && y.getFilter().size() > 0) { + y.getFilter().forEach(f -> { + String whereTerm = transMysqlFilterTerm(f.getTerm()); + String whereValue = ""; + // 原始类型不是时间,在de中被转成时间的字段做处理 + if (StringUtils.equalsIgnoreCase(f.getTerm(), "null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(f.getTerm(), "not_null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(f.getTerm(), "empty")) { + whereValue = "''"; + } else if (StringUtils.equalsIgnoreCase(f.getTerm(), "not_empty")) { + whereValue = "''"; + } else if (StringUtils.containsIgnoreCase(f.getTerm(), "in")) { + whereValue = "('" + StringUtils.join(f.getValue(), "','") + "')"; + } else if (StringUtils.containsIgnoreCase(f.getTerm(), "like")) { + whereValue = "'%" + f.getValue() + "%'"; + } else { + whereValue = String.format(OracleConstants.WHERE_VALUE_VALUE, f.getValue()); + } + list.add(SQLObj.builder() + .whereField(fieldAlias) + .whereAlias(fieldAlias) + .whereTermAndValue(whereTerm + whereValue) + .build()); + }); + } + List strList = new ArrayList<>(); + list.forEach(ele -> strList.add(ele.getWhereField() + " " + ele.getWhereTermAndValue())); + return CollectionUtils.isNotEmpty(list) ? "(" + String.join(" " + getLogic(y.getLogic()) + " ", strList) + ")" + : null; + } + + private String calcFieldRegex(String originField, SQLObj tableObj) { + originField = originField.replaceAll("[\\t\\n\\r]]", ""); + // 正则提取[xxx] + String regex = "\\[(.*?)]"; + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(originField); + Set ids = new HashSet<>(); + while (matcher.find()) { + String id = matcher.group(1); + ids.add(id); + } + if (CollectionUtils.isEmpty(ids)) { + return originField; + } + DatasetTableFieldExample datasetTableFieldExample = new DatasetTableFieldExample(); + datasetTableFieldExample.createCriteria().andIdIn(new ArrayList<>(ids)); + List calcFields = datasetTableFieldMapper.selectByExample(datasetTableFieldExample); + for (DatasetTableField ele : calcFields) { + originField = originField.replaceAll("\\[" + ele.getId() + "]", + String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), ele.getOriginName())); + } + return originField; + } + + private String sqlLimit(String sql, ChartViewWithBLOBs view) { + if (StringUtils.equalsIgnoreCase(view.getResultMode(), "custom")) { + return "SELECT * FROM (" + sqlFix(sql) + ") DE_RESULT_TMP " + " WHERE rownum <= " + view.getResultCount(); + } else { + return sql; + } + } + + public List dateformat() { + ObjectMapper objectMapper = new ObjectMapper(); + List dateformats = new ArrayList<>(); + try{ + dateformats = objectMapper.readValue("[\n" + + "{\"dateformat\": \"YYYY-MM-DD\"},\n" + + "{\"dateformat\": \"YYYY/MM/DD\"},\n" + + "{\"dateformat\": \"YYYYMMDD\"},\n" + + "{\"dateformat\": \"YYYY-MM-DD HH24:MI:SS\"},\n" + + "{\"dateformat\": \"YYYY/MM/DD HH24:MI:SS\"},\n" + + "{\"dateformat\": \"YYYYMMDD HH24:MI:SS\"}\n" + + "]", new TypeReference>() {} ); + }catch (Exception e){} + return dateformats; + } +} diff --git a/extensions/dataease-extensions-datasource/dm/dm-backend/src/main/java/io/dataease/plugins/datasource/dm/service/DmService.java b/extensions/dataease-extensions-datasource/dm/dm-backend/src/main/java/io/dataease/plugins/datasource/dm/service/DmService.java new file mode 100644 index 0000000000..b259396354 --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-backend/src/main/java/io/dataease/plugins/datasource/dm/service/DmService.java @@ -0,0 +1,59 @@ +package io.dataease.plugins.datasource.dm.service; + +import io.dataease.plugins.common.constants.DatabaseClassification; +import io.dataease.plugins.common.constants.DatasourceCalculationMode; +import io.dataease.plugins.common.dto.StaticResource; +import io.dataease.plugins.common.dto.datasource.DataSourceType; +import io.dataease.plugins.datasource.service.DatasourceService; +import org.springframework.stereotype.Service; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +@Service +public class DmService extends DatasourceService { + + + @Override + public List components() { + List result = new ArrayList<>(); + result.add("dm"); + return result; + } + @Override + protected InputStream readContent(String s) { + InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("static/" + s); + return resourceAsStream; + } + + + @Override + public List staticResources() { + List results = new ArrayList<>(); + StaticResource staticResource = new StaticResource(); + staticResource.setName("dm"); + staticResource.setSuffix("jpg"); + results.add(staticResource); + results.add(pluginSvg()); + return results; + } + + @Override + public DataSourceType getDataSourceType() { + DataSourceType dataSourceType = new DataSourceType("dm", "DM" , true , "", DatasourceCalculationMode.DIRECT, true); + dataSourceType.setKeywordPrefix("\""); + dataSourceType.setKeywordSuffix("\""); + dataSourceType.setAliasPrefix("\""); + dataSourceType.setAliasSuffix("\""); + dataSourceType.setDatabaseClassification(DatabaseClassification.OLTP); + return dataSourceType; + } + + private StaticResource pluginSvg() { + StaticResource staticResource = new StaticResource(); + staticResource.setName("dm-backend"); + staticResource.setSuffix("svg"); + return staticResource; + } +} diff --git a/extensions/dataease-extensions-datasource/dm/dm-frontend/.babelrc b/extensions/dataease-extensions-datasource/dm/dm-frontend/.babelrc new file mode 100644 index 0000000000..3a280ba34b --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-frontend/.babelrc @@ -0,0 +1,12 @@ +{ + "presets": [ + ["env", { + "modules": false, + "targets": { + "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] + } + }], + "stage-2" + ], + "plugins": ["transform-vue-jsx", "transform-runtime"] +} diff --git a/extensions/dataease-extensions-datasource/dm/dm-frontend/.gitignore b/extensions/dataease-extensions-datasource/dm/dm-frontend/.gitignore new file mode 100644 index 0000000000..541a820f6c --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-frontend/.gitignore @@ -0,0 +1,14 @@ +.DS_Store +node_modules/ +/dist/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln diff --git a/extensions/dataease-extensions-datasource/dm/dm-frontend/README.md b/extensions/dataease-extensions-datasource/dm/dm-frontend/README.md new file mode 100644 index 0000000000..c1e4be95e0 --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-frontend/README.md @@ -0,0 +1,21 @@ +# deplugin-view-frontend + +> A Vue.js project + +## Build Setup + +``` bash +# install dependencies +npm install + +# serve with hot reload at localhost:8080 +npm run dev + +# build for production with minification +npm run build + +# build for production and view the bundle analyzer report +npm run build --report +``` + +For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader). diff --git a/extensions/dataease-extensions-datasource/dm/dm-frontend/build/build-async-plugins.js b/extensions/dataease-extensions-datasource/dm/dm-frontend/build/build-async-plugins.js new file mode 100644 index 0000000000..2303854531 --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-frontend/build/build-async-plugins.js @@ -0,0 +1,35 @@ +'use strict' +require('./check-versions')() + +process.env.NODE_ENV = 'production' + +const ora = require('ora') +const chalk = require('chalk') +const webpack = require('webpack') +const webpackConfig = require('./webpack.async-plugins') + +const spinner = ora('building for sync-plugins...') +spinner.start() + +webpack(webpackConfig, function (err, stats) { + spinner.stop() + if (err) throw err + process.stdout.write(stats.toString({ + colors: true, + modules: false, + children: false, + chunks: false, + chunkModules: false + }) + '\n\n') + + if (stats.hasErrors()) { + console.log(chalk.red(' Build failed with errors.\n')) + process.exit(1) + } + + console.log(chalk.cyan(' Build complete.\n')) + console.log(chalk.yellow( + ' Tip: built files are meant to be served over an HTTP server.\n' + + ' Opening index.html over file:// won\'t work.\n' + )) +}) diff --git a/extensions/dataease-extensions-datasource/dm/dm-frontend/build/build.js b/extensions/dataease-extensions-datasource/dm/dm-frontend/build/build.js new file mode 100644 index 0000000000..8f2ad8ad49 --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-frontend/build/build.js @@ -0,0 +1,41 @@ +'use strict' +require('./check-versions')() + +process.env.NODE_ENV = 'production' + +const ora = require('ora') +const rm = require('rimraf') +const path = require('path') +const chalk = require('chalk') +const webpack = require('webpack') +const config = require('../config') +const webpackConfig = require('./webpack.prod.conf') + +const spinner = ora('building for production...') +spinner.start() + +rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { + if (err) throw err + webpack(webpackConfig, (err, stats) => { + spinner.stop() + if (err) throw err + process.stdout.write(stats.toString({ + colors: true, + modules: false, + children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build. + chunks: false, + chunkModules: false + }) + '\n\n') + + if (stats.hasErrors()) { + console.log(chalk.red(' Build failed with errors.\n')) + process.exit(1) + } + + console.log(chalk.cyan(' Build complete.\n')) + console.log(chalk.yellow( + ' Tip: built files are meant to be served over an HTTP server.\n' + + ' Opening index.html over file:// won\'t work.\n' + )) + }) +}) diff --git a/extensions/dataease-extensions-datasource/dm/dm-frontend/build/check-versions.js b/extensions/dataease-extensions-datasource/dm/dm-frontend/build/check-versions.js new file mode 100644 index 0000000000..3ef972a08d --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-frontend/build/check-versions.js @@ -0,0 +1,54 @@ +'use strict' +const chalk = require('chalk') +const semver = require('semver') +const packageConfig = require('../package.json') +const shell = require('shelljs') + +function exec (cmd) { + return require('child_process').execSync(cmd).toString().trim() +} + +const versionRequirements = [ + { + name: 'node', + currentVersion: semver.clean(process.version), + versionRequirement: packageConfig.engines.node + } +] + +if (shell.which('npm')) { + versionRequirements.push({ + name: 'npm', + currentVersion: exec('npm --version'), + versionRequirement: packageConfig.engines.npm + }) +} + +module.exports = function () { + const warnings = [] + + for (let i = 0; i < versionRequirements.length; i++) { + const mod = versionRequirements[i] + + if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { + warnings.push(mod.name + ': ' + + chalk.red(mod.currentVersion) + ' should be ' + + chalk.green(mod.versionRequirement) + ) + } + } + + if (warnings.length) { + console.log('') + console.log(chalk.yellow('To use this template, you must update following to modules:')) + console.log() + + for (let i = 0; i < warnings.length; i++) { + const warning = warnings[i] + console.log(' ' + warning) + } + + console.log() + process.exit(1) + } +} diff --git a/extensions/dataease-extensions-datasource/dm/dm-frontend/build/logo.png b/extensions/dataease-extensions-datasource/dm/dm-frontend/build/logo.png new file mode 100644 index 0000000000..f3d2503fc2 Binary files /dev/null and b/extensions/dataease-extensions-datasource/dm/dm-frontend/build/logo.png differ diff --git a/extensions/dataease-extensions-datasource/dm/dm-frontend/build/utils.js b/extensions/dataease-extensions-datasource/dm/dm-frontend/build/utils.js new file mode 100644 index 0000000000..e534fb0fd6 --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-frontend/build/utils.js @@ -0,0 +1,101 @@ +'use strict' +const path = require('path') +const config = require('../config') +const ExtractTextPlugin = require('extract-text-webpack-plugin') +const packageConfig = require('../package.json') + +exports.assetsPath = function (_path) { + const assetsSubDirectory = process.env.NODE_ENV === 'production' + ? config.build.assetsSubDirectory + : config.dev.assetsSubDirectory + + return path.posix.join(assetsSubDirectory, _path) +} + +exports.cssLoaders = function (options) { + options = options || {} + + const cssLoader = { + loader: 'css-loader', + options: { + sourceMap: options.sourceMap + } + } + + const postcssLoader = { + loader: 'postcss-loader', + options: { + sourceMap: options.sourceMap + } + } + + // generate loader string to be used with extract text plugin + function generateLoaders (loader, loaderOptions) { + const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader] + + if (loader) { + loaders.push({ + loader: loader + '-loader', + options: Object.assign({}, loaderOptions, { + sourceMap: options.sourceMap + }) + }) + } + + // Extract CSS when that option is specified + // (which is the case during production build) + if (options.extract) { + return ExtractTextPlugin.extract({ + use: loaders, + fallback: 'vue-style-loader' + }) + } else { + return ['vue-style-loader'].concat(loaders) + } + } + + // https://vue-loader.vuejs.org/en/configurations/extract-css.html + return { + css: generateLoaders(), + postcss: generateLoaders(), + less: generateLoaders('less'), + sass: generateLoaders('sass', { indentedSyntax: true }), + scss: generateLoaders('sass'), + stylus: generateLoaders('stylus'), + styl: generateLoaders('stylus') + } +} + +// Generate loaders for standalone style files (outside of .vue) +exports.styleLoaders = function (options) { + const output = [] + const loaders = exports.cssLoaders(options) + + for (const extension in loaders) { + const loader = loaders[extension] + output.push({ + test: new RegExp('\\.' + extension + '$'), + use: loader + }) + } + + return output +} + +exports.createNotifierCallback = () => { + const notifier = require('node-notifier') + + return (severity, errors) => { + if (severity !== 'error') return + + const error = errors[0] + const filename = error.file && error.file.split('!').pop() + + notifier.notify({ + title: packageConfig.name, + message: severity + ': ' + error.name, + subtitle: filename || '', + icon: path.join(__dirname, 'logo.png') + }) + } +} diff --git a/extensions/dataease-extensions-datasource/dm/dm-frontend/build/vue-loader.conf.js b/extensions/dataease-extensions-datasource/dm/dm-frontend/build/vue-loader.conf.js new file mode 100644 index 0000000000..33ed58bc0a --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-frontend/build/vue-loader.conf.js @@ -0,0 +1,22 @@ +'use strict' +const utils = require('./utils') +const config = require('../config') +const isProduction = process.env.NODE_ENV === 'production' +const sourceMapEnabled = isProduction + ? config.build.productionSourceMap + : config.dev.cssSourceMap + +module.exports = { + loaders: utils.cssLoaders({ + sourceMap: sourceMapEnabled, + extract: isProduction + }), + cssSourceMap: sourceMapEnabled, + cacheBusting: config.dev.cacheBusting, + transformToRequire: { + video: ['src', 'poster'], + source: 'src', + img: 'src', + image: 'xlink:href' + } +} diff --git a/extensions/dataease-extensions-datasource/dm/dm-frontend/build/webpack.async-plugins.js b/extensions/dataease-extensions-datasource/dm/dm-frontend/build/webpack.async-plugins.js new file mode 100644 index 0000000000..a1587e3ce0 --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-frontend/build/webpack.async-plugins.js @@ -0,0 +1,91 @@ +const webpack = require('webpack') +const path = require('path') +const utils = require('./utils') +const CopyPlugin = require("copy-webpack-plugin"); +const VueLoaderPlugin = require('vue-loader/lib/plugin'); +function resolve (dir) { + return path.join(__dirname, '..', dir) +} + +module.exports = { + mode: 'development', + entry: { + 'dm': resolve('/src/views/dm.vue') + }, + output: { + path: resolve('/static/'), + filename: '[name].js' + }, + resolve: { + extensions: ['.js', '.vue', '.json'], + alias: { + 'vue$': 'vue/dist/vue.esm.js', + '@': resolve('src') + } + }, + externals: { + vue: 'vue' + }, + module: { + rules: [ + { + test: /\.vue$/, + loader: 'vue-loader', + options: { + transformAssetUrls: { + video: 'src', + source: 'src', + img: 'src', + image: 'xlink:href' + } + } + }, + { + test: /.(sa|sc|c)ss$/, + use: [ + {loader: 'vue-style-loader'}, + 'css-loader', + 'sass-loader' + ] + }, + { + test: /\.js$/, + loader: 'babel-loader', + include: [resolve('src'), resolve('test')] + }, + { + test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('img/[name].[hash:7].[ext]') + } + }, + { + test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('media/[name].[hash:7].[ext]') + } + }, + { + test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('fonts/[name].[hash:7].[ext]') + } + } + ] + }, + plugins: [ + new VueLoaderPlugin(), + new webpack.DefinePlugin({ + 'process.env.NODE_ENV': '"production"' + }), + new CopyPlugin([ + {from: 'src/icons/svg/'} + ]), + ] +} diff --git a/extensions/dataease-extensions-datasource/dm/dm-frontend/build/webpack.base.conf.js b/extensions/dataease-extensions-datasource/dm/dm-frontend/build/webpack.base.conf.js new file mode 100644 index 0000000000..7ef785a39e --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-frontend/build/webpack.base.conf.js @@ -0,0 +1,103 @@ +'use strict' +const path = require('path') +const utils = require('./utils') +const config = require('../config') +const vueLoaderConfig = require('./vue-loader.conf') + +function resolve (dir) { + return path.join(__dirname, '..', dir) +} + + + +module.exports = { + context: path.resolve(__dirname, '../'), + entry: { + app: './src/main.js' + }, + output: { + path: config.build.assetsRoot, + filename: '[name].js', + publicPath: process.env.NODE_ENV === 'production' + ? config.build.assetsPublicPath + : config.dev.assetsPublicPath + }, + resolve: { + extensions: ['.js', '.vue', '.json'], + alias: { + 'vue$': 'vue/dist/vue.esm.js', + '@': resolve('src'), + } + }, + module: { + rules: [ + { + test: /\.vue$/, + loader: 'vue-loader', + options: vueLoaderConfig + }, + { + test: /\.js$/, + loader: 'babel-loader', + include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')] + }, + { + test: /\.svg$/, + loader: 'svg-sprite-loader', + include: [resolve('src/icons')], + options: { + symbolId: 'icon-[name]' + } + }, + { + test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, + loader: 'url-loader', + exclude: [resolve('src/icons')], + options: { + limit: 10000, + name: utils.assetsPath('img/[name].[hash:7].[ext]') + } + }, + { + test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('media/[name].[hash:7].[ext]') + } + }, + { + test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('fonts/[name].[hash:7].[ext]') + } + }, + { + test: /\.svg$/, + include: [path.resolve('src/icons')], + use: [ + { + loader: 'svg-sprite-loader', + options: { + symbolId: 'icon-[name]', + }, + } + ], + } + ] + }, + node: { + // prevent webpack from injecting useless setImmediate polyfill because Vue + // source contains it (although only uses it if it's native). + setImmediate: false, + // prevent webpack from injecting mocks to Node native modules + // that does not make sense for the client + dgram: 'empty', + fs: 'empty', + net: 'empty', + tls: 'empty', + child_process: 'empty' + } +} diff --git a/extensions/dataease-extensions-datasource/dm/dm-frontend/build/webpack.dev.conf.js b/extensions/dataease-extensions-datasource/dm/dm-frontend/build/webpack.dev.conf.js new file mode 100755 index 0000000000..070ae221f3 --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-frontend/build/webpack.dev.conf.js @@ -0,0 +1,95 @@ +'use strict' +const utils = require('./utils') +const webpack = require('webpack') +const config = require('../config') +const merge = require('webpack-merge') +const path = require('path') +const baseWebpackConfig = require('./webpack.base.conf') +const CopyWebpackPlugin = require('copy-webpack-plugin') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') +const portfinder = require('portfinder') + +const HOST = process.env.HOST +const PORT = process.env.PORT && Number(process.env.PORT) + +const devWebpackConfig = merge(baseWebpackConfig, { + module: { + rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) + }, + // cheap-module-eval-source-map is faster for development + devtool: config.dev.devtool, + + // these devServer options should be customized in /config/index.js + devServer: { + clientLogLevel: 'warning', + historyApiFallback: { + rewrites: [ + { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') }, + ], + }, + hot: true, + contentBase: false, // since we use CopyWebpackPlugin. + compress: true, + host: HOST || config.dev.host, + port: PORT || config.dev.port, + open: config.dev.autoOpenBrowser, + overlay: config.dev.errorOverlay + ? { warnings: false, errors: true } + : false, + publicPath: config.dev.assetsPublicPath, + proxy: config.dev.proxyTable, + quiet: true, // necessary for FriendlyErrorsPlugin + watchOptions: { + poll: config.dev.poll, + } + }, + plugins: [ + new webpack.DefinePlugin({ + 'process.env': require('../config/dev.env') + }), + new webpack.HotModuleReplacementPlugin(), + new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update. + new webpack.NoEmitOnErrorsPlugin(), + // https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: 'index.html', + template: 'index.html', + inject: true + }), + // copy custom static assets + new CopyWebpackPlugin([ + { + from: path.resolve(__dirname, '../static'), + to: config.dev.assetsSubDirectory, + ignore: ['.*'] + } + ]) + ] +}) + +module.exports = new Promise((resolve, reject) => { + portfinder.basePort = process.env.PORT || config.dev.port + portfinder.getPort((err, port) => { + if (err) { + reject(err) + } else { + // publish the new Port, necessary for e2e tests + process.env.PORT = port + // add port to devServer config + devWebpackConfig.devServer.port = port + + // Add FriendlyErrorsPlugin + devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({ + compilationSuccessInfo: { + messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`], + }, + onErrors: config.dev.notifyOnErrors + ? utils.createNotifierCallback() + : undefined + })) + + resolve(devWebpackConfig) + } + }) +}) diff --git a/extensions/dataease-extensions-datasource/dm/dm-frontend/build/webpack.prod.conf.js b/extensions/dataease-extensions-datasource/dm/dm-frontend/build/webpack.prod.conf.js new file mode 100644 index 0000000000..3e25238ef6 --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-frontend/build/webpack.prod.conf.js @@ -0,0 +1,145 @@ +'use strict' +const path = require('path') +const utils = require('./utils') +const webpack = require('webpack') +const config = require('../config') +const merge = require('webpack-merge') +const baseWebpackConfig = require('./webpack.base.conf') +const CopyWebpackPlugin = require('copy-webpack-plugin') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const ExtractTextPlugin = require('extract-text-webpack-plugin') +const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') +const UglifyJsPlugin = require('uglifyjs-webpack-plugin') + +const env = require('../config/prod.env') + +const webpackConfig = merge(baseWebpackConfig, { + module: { + rules: utils.styleLoaders({ + sourceMap: config.build.productionSourceMap, + extract: true, + usePostCSS: true + }) + }, + devtool: config.build.productionSourceMap ? config.build.devtool : false, + output: { + path: config.build.assetsRoot, + filename: utils.assetsPath('js/[name].[chunkhash].js'), + chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') + }, + plugins: [ + // http://vuejs.github.io/vue-loader/en/workflow/production.html + new webpack.DefinePlugin({ + 'process.env': env + }), + new UglifyJsPlugin({ + uglifyOptions: { + compress: { + warnings: false + } + }, + sourceMap: config.build.productionSourceMap, + parallel: true + }), + // extract css into its own file + new ExtractTextPlugin({ + filename: utils.assetsPath('css/[name].[contenthash].css'), + // Setting the following option to `false` will not extract CSS from codesplit chunks. + // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack. + // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, + // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110 + allChunks: true, + }), + // Compress extracted CSS. We are using this plugin so that possible + // duplicated CSS from different components can be deduped. + new OptimizeCSSPlugin({ + cssProcessorOptions: config.build.productionSourceMap + ? { safe: true, map: { inline: false } } + : { safe: true } + }), + // generate dist index.html with correct asset hash for caching. + // you can customize output by editing /index.html + // see https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: config.build.index, + template: 'index.html', + inject: true, + minify: { + removeComments: true, + collapseWhitespace: true, + removeAttributeQuotes: true + // more options: + // https://github.com/kangax/html-minifier#options-quick-reference + }, + // necessary to consistently work with multiple chunks via CommonsChunkPlugin + chunksSortMode: 'dependency' + }), + // keep module.id stable when vendor modules does not change + new webpack.HashedModuleIdsPlugin(), + // enable scope hoisting + new webpack.optimize.ModuleConcatenationPlugin(), + // split vendor js into its own file + new webpack.optimize.CommonsChunkPlugin({ + name: 'vendor', + minChunks (module) { + // any required modules inside node_modules are extracted to vendor + return ( + module.resource && + /\.js$/.test(module.resource) && + module.resource.indexOf( + path.join(__dirname, '../node_modules') + ) === 0 + ) + } + }), + // extract webpack runtime and module manifest to its own file in order to + // prevent vendor hash from being updated whenever app bundle is updated + new webpack.optimize.CommonsChunkPlugin({ + name: 'manifest', + minChunks: Infinity + }), + // This instance extracts shared chunks from code split chunks and bundles them + // in a separate chunk, similar to the vendor chunk + // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk + new webpack.optimize.CommonsChunkPlugin({ + name: 'app', + async: 'vendor-async', + children: true, + minChunks: 3 + }), + + // copy custom static assets + new CopyWebpackPlugin([ + { + from: path.resolve(__dirname, '../static'), + to: config.build.assetsSubDirectory, + ignore: ['.*'] + } + ]) + ] +}) + +if (config.build.productionGzip) { + const CompressionWebpackPlugin = require('compression-webpack-plugin') + + webpackConfig.plugins.push( + new CompressionWebpackPlugin({ + asset: '[path].gz[query]', + algorithm: 'gzip', + test: new RegExp( + '\\.(' + + config.build.productionGzipExtensions.join('|') + + ')$' + ), + threshold: 10240, + minRatio: 0.8 + }) + ) +} + +if (config.build.bundleAnalyzerReport) { + const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin + webpackConfig.plugins.push(new BundleAnalyzerPlugin()) +} + +module.exports = webpackConfig diff --git a/extensions/dataease-extensions-datasource/dm/dm-frontend/config/dev.env.js b/extensions/dataease-extensions-datasource/dm/dm-frontend/config/dev.env.js new file mode 100644 index 0000000000..1e22973ae7 --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-frontend/config/dev.env.js @@ -0,0 +1,7 @@ +'use strict' +const merge = require('webpack-merge') +const prodEnv = require('./prod.env') + +module.exports = merge(prodEnv, { + NODE_ENV: '"development"' +}) diff --git a/extensions/dataease-extensions-datasource/dm/dm-frontend/config/index.js b/extensions/dataease-extensions-datasource/dm/dm-frontend/config/index.js new file mode 100644 index 0000000000..c5eded7f81 --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-frontend/config/index.js @@ -0,0 +1,69 @@ +'use strict' +// Template version: 1.3.1 +// see http://vuejs-templates.github.io/webpack for documentation. + +const path = require('path') + +module.exports = { + dev: { + + // Paths + assetsSubDirectory: 'static', + assetsPublicPath: '/', + proxyTable: {}, + + // Various Dev Server settings + host: 'localhost', // can be overwritten by process.env.HOST + port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined + autoOpenBrowser: false, + errorOverlay: true, + notifyOnErrors: true, + poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- + + + /** + * Source Maps + */ + + // https://webpack.js.org/configuration/devtool/#development + devtool: 'cheap-module-eval-source-map', + + // If you have problems debugging vue-files in devtools, + // set this to false - it *may* help + // https://vue-loader.vuejs.org/en/options.html#cachebusting + cacheBusting: true, + + cssSourceMap: true + }, + + build: { + // Template for index.html + index: path.resolve(__dirname, '../dist/index.html'), + + // Paths + assetsRoot: path.resolve(__dirname, '../dist'), + assetsSubDirectory: 'static', + assetsPublicPath: '/', + + /** + * Source Maps + */ + + productionSourceMap: true, + // https://webpack.js.org/configuration/devtool/#production + devtool: '#source-map', + + // Gzip off by default as many popular static hosts such as + // Surge or Netlify already gzip all static assets for you. + // Before setting to `true`, make sure to: + // npm install --save-dev compression-webpack-plugin + productionGzip: false, + productionGzipExtensions: ['js', 'css'], + + // Run the build command with an extra argument to + // View the bundle analyzer report after build finishes: + // `npm run build --report` + // Set to `true` or `false` to always turn it on or off + bundleAnalyzerReport: process.env.npm_config_report + } +} diff --git a/extensions/dataease-extensions-datasource/dm/dm-frontend/config/prod.env.js b/extensions/dataease-extensions-datasource/dm/dm-frontend/config/prod.env.js new file mode 100644 index 0000000000..a6f997616e --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-frontend/config/prod.env.js @@ -0,0 +1,4 @@ +'use strict' +module.exports = { + NODE_ENV: '"production"' +} diff --git a/extensions/dataease-extensions-datasource/dm/dm-frontend/index.html b/extensions/dataease-extensions-datasource/dm/dm-frontend/index.html new file mode 100644 index 0000000000..03ce3fdf27 --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-frontend/index.html @@ -0,0 +1,12 @@ + + + + + + deplugin-view-frontend + + +
+ + + diff --git a/extensions/dataease-extensions-datasource/dm/dm-frontend/package.json b/extensions/dataease-extensions-datasource/dm/dm-frontend/package.json new file mode 100644 index 0000000000..64a3ce7ad6 --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-frontend/package.json @@ -0,0 +1,75 @@ +{ + "name": "deplugin-datasource-frontend", + "version": "1.0.0", + "description": "A Vue.js project", + "author": "", + "private": true, + "scripts": { + "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", + "start": "npm run dev", + "build": "node build/build.js", + "buildPlugin": "node build/build-async-plugins.js" + }, + "dependencies": { + "@riophae/vue-treeselect": "0.4.0", + "highcharts": "^10.0.0", + "svg-sprite-loader": "^6.0.11", + "svgo": "1.2.2", + "svgo-loader": "^3.0.1", + "vue": "^2.5.2", + "vue-i18n": "7.3.2", + "vue-router": "^3.0.1", + "vue-uuid": "2.0.2", + "vuedraggable": "^2.24.3" + }, + "devDependencies": { + "autoprefixer": "^7.1.2", + "babel-core": "^6.22.1", + "babel-helper-vue-jsx-merge-props": "^2.0.3", + "babel-loader": "^7.1.1", + "babel-plugin-syntax-jsx": "^6.18.0", + "babel-plugin-transform-runtime": "^6.22.0", + "babel-plugin-transform-vue-jsx": "^3.5.0", + "babel-preset-env": "^1.3.2", + "babel-preset-stage-2": "^6.22.0", + "chalk": "^2.0.1", + "copy-webpack-plugin": "^4.6.0", + "css-loader": "^0.28.0", + "element-ui": "2.15.7", + "extract-text-webpack-plugin": "^4.0.0-beta.0", + "file-loader": "^1.1.4", + "friendly-errors-webpack-plugin": "^1.6.1", + "html-webpack-plugin": "^3.2.0", + "js-cookie": "2.2.0", + "node-notifier": "^8.0.1", + "optimize-css-assets-webpack-plugin": "^3.2.0", + "ora": "^1.2.0", + "portfinder": "^1.0.13", + "postcss-import": "^11.0.0", + "postcss-loader": "^2.0.8", + "postcss-url": "^7.2.1", + "rimraf": "^2.6.0", + "sass": "^1.33.0", + "sass-loader": "^7.3.1", + "semver": "^5.3.0", + "shelljs": "^0.8.5", + "uglifyjs-webpack-plugin": "^1.1.1", + "url-loader": "^0.5.8", + "vue-loader": "^15.6.4", + "vue-style-loader": "^4.1.2", + "vue-template-compiler": "^2.5.2", + "webpack": "^4.8.1", + "webpack-bundle-analyzer": "^3.3.2", + "webpack-dev-server": "^3.1.11", + "webpack-merge": "^4.1.0" + }, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + }, + "browserslist": [ + "> 1%", + "last 2 versions", + "not ie <= 8" + ] +} diff --git a/extensions/dataease-extensions-datasource/dm/dm-frontend/pom.xml b/extensions/dataease-extensions-datasource/dm/dm-frontend/pom.xml new file mode 100644 index 0000000000..eae8cd7ea0 --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-frontend/pom.xml @@ -0,0 +1,80 @@ + + + + + io.dataease + dm + ${dataease.version} + + + 4.0.0 + dm-frontend + + + UTF-8 + UTF-8 + 1.9.1 + + + + + + maven-clean-plugin + + + + static + + ** + + false + + + + + + + + com.github.eirslett + frontend-maven-plugin + ${frontend-maven-plugin.version} + + + install node and npm + + install-node-and-npm + + + + v16.20.2 + 7.6.3 + + + + + npm install + + npm + + + + install --force + + + + + npm run buildPlugin + + npm + + + run buildPlugin + + + + + + + diff --git a/extensions/dataease-extensions-datasource/dm/dm-frontend/src/App.vue b/extensions/dataease-extensions-datasource/dm/dm-frontend/src/App.vue new file mode 100644 index 0000000000..8542e07722 --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-frontend/src/App.vue @@ -0,0 +1,23 @@ + + + + + diff --git a/extensions/dataease-extensions-datasource/dm/dm-frontend/src/assets/logo.png b/extensions/dataease-extensions-datasource/dm/dm-frontend/src/assets/logo.png new file mode 100644 index 0000000000..f3d2503fc2 Binary files /dev/null and b/extensions/dataease-extensions-datasource/dm/dm-frontend/src/assets/logo.png differ diff --git a/extensions/dataease-extensions-datasource/dm/dm-frontend/src/components/HelloWorld.vue b/extensions/dataease-extensions-datasource/dm/dm-frontend/src/components/HelloWorld.vue new file mode 100644 index 0000000000..1c19f2a399 --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-frontend/src/components/HelloWorld.vue @@ -0,0 +1,113 @@ + + + + + + diff --git a/extensions/dataease-extensions-datasource/dm/dm-frontend/src/de-base/lang/en.js b/extensions/dataease-extensions-datasource/dm/dm-frontend/src/de-base/lang/en.js new file mode 100644 index 0000000000..3f7c408e8a --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-frontend/src/de-base/lang/en.js @@ -0,0 +1,16 @@ +export default { + plugin_view_3d_pie: { + type_title: '3D-PIE', + label: 'Label', + angle: 'Angle' + }, + host: 'Host', + port: 'Port', + dataBase: 'Catalog', + schema: 'Schema', + username: 'User', + password: 'Password', + get_schema: 'Get Schema', + please_choose_schema: 'Please select Schema', + query_timeout: 'Query timeout (seconds)' +} diff --git a/extensions/dataease-extensions-datasource/dm/dm-frontend/src/de-base/lang/index.js b/extensions/dataease-extensions-datasource/dm/dm-frontend/src/de-base/lang/index.js new file mode 100644 index 0000000000..e50d0478e6 --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-frontend/src/de-base/lang/index.js @@ -0,0 +1,49 @@ +import Vue from 'vue' +import VueI18n from 'vue-i18n' +import Cookies from 'js-cookie' +import elementEnLocale from 'element-ui/lib/locale/lang/en' // element-ui lang +import elementZhLocale from 'element-ui/lib/locale/lang/zh-CN'// element-ui lang +import elementTWLocale from 'element-ui/lib/locale/lang/zh-TW'// element-ui lang + +import localMessages from './messages' + + +Vue.use(VueI18n) + +const messages = { + en_US: { + ...localMessages['en_US'], + ...elementEnLocale + }, + zh_CN: { + ...localMessages['zh_CN'], + ...elementZhLocale + }, + zh_TW: { + ...localMessages['zh_TW'], + ...elementTWLocale + } +} +export function getLanguage () { + const chooseLanguage = Cookies.get('language') + if (chooseLanguage) return chooseLanguage + + // if has not choose language + const language = (navigator.language || navigator.browserLanguage).toLowerCase() + const locales = Object.keys(messages) + for (const locale of locales) { + if (language.indexOf(locale) > -1) { + return locale + } + } + return 'zh_CN' +} +const i18n = new VueI18n({ + // set locale + // options: en | zh | es + locale: getLanguage(), + // set locale messages + messages +}) + +export default i18n diff --git a/extensions/dataease-extensions-datasource/dm/dm-frontend/src/de-base/lang/messages.js b/extensions/dataease-extensions-datasource/dm/dm-frontend/src/de-base/lang/messages.js new file mode 100644 index 0000000000..844f8c0673 --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-frontend/src/de-base/lang/messages.js @@ -0,0 +1,17 @@ +import enLocale from './en' +import zhLocale from './zh' +import twLocale from './tw' + +const messages = { + en_US: { + ...enLocale + }, + zh_CN: { + ...zhLocale + }, + zh_TW: { + ...twLocale + } +} + +export default messages \ No newline at end of file diff --git a/extensions/dataease-extensions-datasource/dm/dm-frontend/src/de-base/lang/tw.js b/extensions/dataease-extensions-datasource/dm/dm-frontend/src/de-base/lang/tw.js new file mode 100644 index 0000000000..745a3cd2eb --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-frontend/src/de-base/lang/tw.js @@ -0,0 +1,16 @@ +export default { + plugin_view_3d_pie: { + type_title: '3D餅圖', + label: '標籤', + angle: '角度' + }, + host: '主機名', + port: '端口', + dataBase: '數據庫', + schema: 'Schema', + username: '用戶名', + password: '密碼', + get_schema: '获取 Schema', + please_choose_schema: '请选择 Schema', + query_timeout: '査詢超時(秒)' +} diff --git a/extensions/dataease-extensions-datasource/dm/dm-frontend/src/de-base/lang/zh.js b/extensions/dataease-extensions-datasource/dm/dm-frontend/src/de-base/lang/zh.js new file mode 100644 index 0000000000..ff8c95c1f1 --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-frontend/src/de-base/lang/zh.js @@ -0,0 +1,21 @@ +export default { + plugin_view_3d_pie: { + type_title: '3D饼图', + label: '标签', + angle: '角度' + }, + host: '主机名', + port: '端口', + dataBase: '数据库', + schema: 'Schema', + username: '用户名', + password: '密码', + get_schema: '获取 Schema', + please_choose_schema: '请选择 Schema', + second: '秒', + enter_the_port: '请输入端口', + one_user_name: '请输入用户名', + input_a_password: '请输入密码', + please_select: '请选择', + query_timeout: '查询超时' +} diff --git a/extensions/dataease-extensions-datasource/dm/dm-frontend/src/icons/index.js b/extensions/dataease-extensions-datasource/dm/dm-frontend/src/icons/index.js new file mode 100644 index 0000000000..2c6b309c96 --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-frontend/src/icons/index.js @@ -0,0 +1,9 @@ +import Vue from 'vue' +import SvgIcon from '@/components/SvgIcon'// svg component + +// register globally +Vue.component('svg-icon', SvgIcon) + +const req = require.context('./svg', false, /\.svg$/) +const requireAll = requireContext => requireContext.keys().map(requireContext) +requireAll(req) diff --git a/extensions/dataease-extensions-datasource/dm/dm-frontend/src/icons/svg/de_pwd_invisible.svg b/extensions/dataease-extensions-datasource/dm/dm-frontend/src/icons/svg/de_pwd_invisible.svg new file mode 100644 index 0000000000..661ebba33e --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-frontend/src/icons/svg/de_pwd_invisible.svg @@ -0,0 +1,3 @@ + + + diff --git a/extensions/dataease-extensions-datasource/dm/dm-frontend/src/icons/svg/de_pwd_visible.svg b/extensions/dataease-extensions-datasource/dm/dm-frontend/src/icons/svg/de_pwd_visible.svg new file mode 100644 index 0000000000..4e7ba5e799 --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-frontend/src/icons/svg/de_pwd_visible.svg @@ -0,0 +1,3 @@ + + + diff --git a/extensions/dataease-extensions-datasource/dm/dm-frontend/src/icons/svg/dm-backend.svg b/extensions/dataease-extensions-datasource/dm/dm-frontend/src/icons/svg/dm-backend.svg new file mode 100644 index 0000000000..5b3c9135fe --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-frontend/src/icons/svg/dm-backend.svg @@ -0,0 +1 @@ +【icon】插件管理-导出 diff --git a/extensions/dataease-extensions-datasource/dm/dm-frontend/src/icons/svg/dm.jpg b/extensions/dataease-extensions-datasource/dm/dm-frontend/src/icons/svg/dm.jpg new file mode 100644 index 0000000000..2d770a439d Binary files /dev/null and b/extensions/dataease-extensions-datasource/dm/dm-frontend/src/icons/svg/dm.jpg differ diff --git a/extensions/dataease-extensions-datasource/dm/dm-frontend/src/icons/svgo.yml b/extensions/dataease-extensions-datasource/dm/dm-frontend/src/icons/svgo.yml new file mode 100644 index 0000000000..d11906aec2 --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-frontend/src/icons/svgo.yml @@ -0,0 +1,22 @@ +# replace default config + +# multipass: true +# full: true + +plugins: + + # - name + # + # or: + # - name: false + # - name: true + # + # or: + # - name: + # param1: 1 + # param2: 2 + +- removeAttrs: + attrs: + - 'fill' + - 'fill-rule' diff --git a/extensions/dataease-extensions-datasource/dm/dm-frontend/src/main.js b/extensions/dataease-extensions-datasource/dm/dm-frontend/src/main.js new file mode 100644 index 0000000000..99c5626590 --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-frontend/src/main.js @@ -0,0 +1,33 @@ +// The Vue build version to load with the `import` command +// (runtime-only or standalone) has been set in webpack.base.conf with an alias. +import Vue from 'vue' +import App from './App' +import router from './router' +import ElementUI from 'element-ui' +import Cookies from 'js-cookie' +import i18n from './de-base/lang' +import draggable from 'vuedraggable' +import Treeselect from '@riophae/vue-treeselect' +import '@riophae/vue-treeselect/dist/vue-treeselect.css' +Vue.config.productionTip = false +Vue.use(ElementUI, { + size: Cookies.get('size') || 'medium', + i18n: (key, value) => i18n.t(key, value) +}) +Vue.component('Treeselect', Treeselect) +Vue.component('draggable', draggable) +Vue.prototype.hasDataPermission = function(pTarget, pSource) { + + if (pSource && pTarget) { + return pSource.indexOf(pTarget) > -1 + } + return false +} +/* eslint-disable no-new */ +new Vue({ + el: '#app', + router, + i18n, + components: { App }, + template: '' +}) diff --git a/extensions/dataease-extensions-datasource/dm/dm-frontend/src/router/index.js b/extensions/dataease-extensions-datasource/dm/dm-frontend/src/router/index.js new file mode 100644 index 0000000000..b5b55fa1fc --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-frontend/src/router/index.js @@ -0,0 +1,21 @@ +import Vue from 'vue' +import Router from 'vue-router' +import HelloWorld from '@/components/HelloWorld' +import maxcompute from '@/views/maxcompute' + +Vue.use(Router) + +export default new Router({ + routes: [ + { + path: '/', + name: 'HelloWorld', + component: HelloWorld + }, + { + path: '/maxcompute', + name: 'maxcompute', + component: maxcompute + } + ] +}) diff --git a/extensions/dataease-extensions-datasource/dm/dm-frontend/src/utils/compare.js b/extensions/dataease-extensions-datasource/dm/dm-frontend/src/utils/compare.js new file mode 100644 index 0000000000..b06a104a10 --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-frontend/src/utils/compare.js @@ -0,0 +1,29 @@ +export const compareItem = { + type: 'none', // year-yoy/month-yoy等 + resultData: 'percent', // 对比差sub,百分比percent等 + field: '', + custom: { + field: '', + calcType: '0', // 0-增长值,1-增长率 + timeType: '0', // 0-固定日期,1-日期区间 + currentTime: '', + compareTime: '', + currentTimeRange: [], + compareTimeRange: [] + } +} + +export const compareYearList = [ + { name: 'year_mom', value: 'year_mom' } +] + +export const compareMonthList = [ + { name: 'month_mom', value: 'month_mom' }, + { name: 'year_yoy', value: 'year_yoy' } +] + +export const compareDayList = [ + { name: 'day_mom', value: 'day_mom' }, + { name: 'month_yoy', value: 'month_yoy' }, + { name: 'year_yoy', value: 'year_yoy' } +] diff --git a/extensions/dataease-extensions-datasource/dm/dm-frontend/src/utils/validate.js b/extensions/dataease-extensions-datasource/dm/dm-frontend/src/utils/validate.js new file mode 100644 index 0000000000..3e8ffa9448 --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-frontend/src/utils/validate.js @@ -0,0 +1,15 @@ +/** + * Created by PanJiaChen on 16/11/18. + */ + +/** + * @param {string} path + * @returns {Boolean} + */ +export function isExternal(path) { + return /^(https?:|mailto:|tel:)/.test(path) || /^(http?:|mailto:|tel:)/.test(path) || path.startsWith('/api/pluginCommon/staticInfo') +} + + + + diff --git a/extensions/dataease-extensions-datasource/dm/dm-frontend/src/views/dePwd.vue b/extensions/dataease-extensions-datasource/dm/dm-frontend/src/views/dePwd.vue new file mode 100644 index 0000000000..61bf3e88ee --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-frontend/src/views/dePwd.vue @@ -0,0 +1,75 @@ + + + + \ No newline at end of file diff --git a/extensions/dataease-extensions-datasource/dm/dm-frontend/src/views/dm.vue b/extensions/dataease-extensions-datasource/dm/dm-frontend/src/views/dm.vue new file mode 100644 index 0000000000..35dc3badef --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/dm-frontend/src/views/dm.vue @@ -0,0 +1,202 @@ + + + + + diff --git a/extensions/dataease-extensions-datasource/dm/plugin.json b/extensions/dataease-extensions-datasource/dm/plugin.json new file mode 100644 index 0000000000..137eaef7b9 --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/plugin.json @@ -0,0 +1,13 @@ +{ + "name":"达梦数据源插件", + "free":0, + "store":"default", + "cost":0, + "category":"datasource", + "descript":"达梦插件,值得拥有", + "version":"1.18.0", + "creator":"DATAEASE", + "moduleName":"dm-backend", + "require":"1.12.0", + "dsType":"dm" +} diff --git a/extensions/dataease-extensions-datasource/dm/pom.xml b/extensions/dataease-extensions-datasource/dm/pom.xml new file mode 100644 index 0000000000..0b467c4e29 --- /dev/null +++ b/extensions/dataease-extensions-datasource/dm/pom.xml @@ -0,0 +1,19 @@ + + + + dataease-extensions-datasource + io.dataease + ${dataease.version} + + 4.0.0 + + dm + pom + + dm-frontend + dm-backend + + + diff --git a/extensions/dataease-extensions-datasource/kingbase/build.sh b/extensions/dataease-extensions-datasource/kingbase/build.sh new file mode 100755 index 0000000000..b37400980a --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/build.sh @@ -0,0 +1,6 @@ +#!/bin/sh +mvn clean package -U -Dmaven.test.skip=true + +cp kingbase-backend/target/kingbase-backend-1.18.0.jar . + +zip -r kingbase.zip ./kingbase-backend-1.18.0.jar ./kingbaseDriver ./plugin.json diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-backend/pom.xml b/extensions/dataease-extensions-datasource/kingbase/kingbase-backend/pom.xml new file mode 100644 index 0000000000..4544d15cc9 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-backend/pom.xml @@ -0,0 +1,109 @@ + + + + kingbase + io.dataease + ${dataease.version} + + 4.0.0 + + kingbase-backend + + + + io.dataease + dataease-plugin-datasource + + + + + + + src/main/java + + **/*.properties + **/*.xml + + false + + + src/main/resources + + **/* + + false + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 11 + 11 + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.4 + + + **/server/** + **/*.properties + **/Application* + + + + + + maven-clean-plugin + + + + src/main/resources/static + + ** + + false + + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + main-class-placement + generate-resources + + + + + + + + + + + + run + + + + + + + + + + + diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-backend/src/main/java/io/dataease/plugins/datasource/kingbase/provider/KingbaseConfig.java b/extensions/dataease-extensions-datasource/kingbase/kingbase-backend/src/main/java/io/dataease/plugins/datasource/kingbase/provider/KingbaseConfig.java new file mode 100644 index 0000000000..b83467e5e4 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-backend/src/main/java/io/dataease/plugins/datasource/kingbase/provider/KingbaseConfig.java @@ -0,0 +1,27 @@ +package io.dataease.plugins.datasource.kingbase.provider; + +import io.dataease.plugins.datasource.entity.JdbcConfiguration; +import lombok.Getter; +import lombok.Setter; + +/** + * 连接配置信息 + */ +@Getter +@Setter +public class KingbaseConfig extends JdbcConfiguration { + + private String driver = "com.kingbase8.Driver";//驱动类名 + private String extraParams; + + + /** + * JDBC 拼接 + */ + public String getJdbc() { + return "jdbc:kingbase8://HOST:PORT/DATABASE" + .replace("HOST", getHost().trim()) + .replace("PORT", getPort().toString()) + .replace("DATABASE", getDataBase().trim()); + } +} diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-backend/src/main/java/io/dataease/plugins/datasource/kingbase/provider/KingbaseDsProvider.java b/extensions/dataease-extensions-datasource/kingbase/kingbase-backend/src/main/java/io/dataease/plugins/datasource/kingbase/provider/KingbaseDsProvider.java new file mode 100644 index 0000000000..d3f2b843cb --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-backend/src/main/java/io/dataease/plugins/datasource/kingbase/provider/KingbaseDsProvider.java @@ -0,0 +1,264 @@ +package io.dataease.plugins.datasource.kingbase.provider; + +import com.google.gson.Gson; +import io.dataease.plugins.common.base.domain.DeDriver; +import io.dataease.plugins.common.base.mapper.DeDriverMapper; +import io.dataease.plugins.common.constants.DatasourceTypes; +import io.dataease.plugins.common.dto.datasource.TableDesc; +import io.dataease.plugins.common.dto.datasource.TableField; +import io.dataease.plugins.common.exception.DataEaseException; +import io.dataease.plugins.common.request.datasource.DatasourceRequest; +import io.dataease.plugins.datasource.entity.JdbcConfiguration; +import io.dataease.plugins.datasource.provider.DefaultJdbcProvider; +import io.dataease.plugins.datasource.provider.ExtendedJdbcClassLoader; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.lang.reflect.Method; +import java.sql.*; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Properties; + + +@Component() +public class KingbaseDsProvider extends DefaultJdbcProvider { + @Resource + private DeDriverMapper deDriverMapper; + + @Override + public String getType() { + return "kingbase"; + } + + @Override + public boolean isUseDatasourcePool() { + return false; + } + + /** + * 连接数据源 + */ + @Override + public Connection getConnection(DatasourceRequest datasourceRequest) throws Exception { + KingbaseConfig kingbaseConfig = new Gson().fromJson(datasourceRequest.getDatasource().getConfiguration(), + KingbaseConfig.class); + + String defaultDriver = kingbaseConfig.getDriver(); + String customDriver = kingbaseConfig.getCustomDriver(); + + String url = kingbaseConfig.getJdbc(); + Properties props = new Properties(); + DeDriver deDriver = null; + if (StringUtils.isNotEmpty(kingbaseConfig.getAuthMethod()) && kingbaseConfig.getAuthMethod().equalsIgnoreCase("kerberos")) { + System.setProperty("java.security.krb5.conf", "/opt/dataease/conf/krb5.conf"); + ExtendedJdbcClassLoader classLoader; + if (isDefaultClassLoader(customDriver)) { + classLoader = extendedJdbcClassLoader; + } else { + deDriver = deDriverMapper.selectByPrimaryKey(customDriver); + classLoader = getCustomJdbcClassLoader(deDriver); + } + Class ConfigurationClass = classLoader.loadClass("org.apache.hadoop.conf.Configuration"); + Method set = ConfigurationClass.getMethod("set", String.class, String.class); + Object obj = ConfigurationClass.newInstance(); + set.invoke(obj, "hadoop.security.authentication", "Kerberos"); + + Class UserGroupInformationClass = classLoader.loadClass("org.apache.hadoop.security" + + ".UserGroupInformation"); + Method setConfiguration = UserGroupInformationClass.getMethod("setConfiguration", ConfigurationClass); + Method loginUserFromKeytab = UserGroupInformationClass.getMethod("loginUserFromKeytab", String.class, + String.class); + setConfiguration.invoke(null, obj); + loginUserFromKeytab.invoke(null, kingbaseConfig.getUsername(), + "/opt/dataease/conf/" + kingbaseConfig.getPassword()); + } else { + if (StringUtils.isNotBlank(kingbaseConfig.getUsername())) { + props.setProperty("user", kingbaseConfig.getUsername()); + if (StringUtils.isNotBlank(kingbaseConfig.getPassword())) { + props.setProperty("password", kingbaseConfig.getPassword()); + } + } + } + + Connection conn; + String driverClassName; + ExtendedJdbcClassLoader jdbcClassLoader; + if (isDefaultClassLoader(customDriver)) { + driverClassName = defaultDriver; + jdbcClassLoader = extendedJdbcClassLoader; + } else { + if (deDriver == null) { + deDriver = deDriverMapper.selectByPrimaryKey(customDriver); + } + driverClassName = deDriver.getDriverClass(); + jdbcClassLoader = getCustomJdbcClassLoader(deDriver); + } + + Driver driverClass = (Driver) jdbcClassLoader.loadClass(driverClassName).newInstance(); + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + try { + Thread.currentThread().setContextClassLoader(jdbcClassLoader); + conn = driverClass.connect(url, props); + } catch (Exception e) { + e.printStackTrace(); + throw e; + } finally { + Thread.currentThread().setContextClassLoader(classLoader); + } + return conn; + } + + /** + * 获取表名称 + */ + @Override + public List getTables(DatasourceRequest datasourceRequest) throws Exception { + List tables = new ArrayList<>(); + String queryStr = getTablesSql(datasourceRequest); + JdbcConfiguration jdbcConfiguration = + new Gson().fromJson(datasourceRequest.getDatasource().getConfiguration(), JdbcConfiguration.class); + int queryTimeout = Math.max(jdbcConfiguration.getQueryTimeout(), 0); + try (Connection con = getConnectionFromPool(datasourceRequest); Statement statement = getStatement(con, + queryTimeout); ResultSet resultSet = statement.executeQuery(queryStr)) { + while (resultSet.next()) { + tables.add(getTableDesc(datasourceRequest, resultSet)); + } + } catch (Exception e) { + DataEaseException.throwException(e); + } + + return tables; + } + + /** + * 获取表名称 + */ + private TableDesc getTableDesc(DatasourceRequest datasourceRequest, ResultSet resultSet) throws SQLException { + TableDesc tableDesc = new TableDesc(); + tableDesc.setName(resultSet.getString(1)); + return tableDesc; + } + + /** + * 获取表字段信息 + */ + @Override + public List getTableFields(DatasourceRequest datasourceRequest) throws Exception { + List list = new LinkedList<>(); + try (Connection connection = getConnectionFromPool(datasourceRequest)) { + DatabaseMetaData databaseMetaData = connection.getMetaData(); + ResultSet resultSet = databaseMetaData.getColumns(null, null, datasourceRequest.getTable(), "%"); + while (resultSet.next()) { + String tableName = resultSet.getString("TABLE_NAME").toUpperCase(); + String database; + database = resultSet.getString("TABLE_CAT"); + if (database != null) { + if (tableName.equals(datasourceRequest.getTable()) && database.equalsIgnoreCase(getDatabase(datasourceRequest))) { + TableField tableField = getTableFiled(resultSet, datasourceRequest); + list.add(tableField); + } + } else { + if (tableName.equals(datasourceRequest.getTable())) { + TableField tableField = getTableFiled(resultSet, datasourceRequest); + list.add(tableField); + } + } + } + resultSet.close(); + } catch (SQLException e) { + DataEaseException.throwException(e); + } catch (Exception e) { + DataEaseException.throwException("Data source connection exception: " + e.getMessage()); + } + return list; + } + + /** + * 获取数据源 + */ + private String getDatabase(DatasourceRequest datasourceRequest) { + JdbcConfiguration jdbcConfiguration = + new Gson().fromJson(datasourceRequest.getDatasource().getConfiguration(), JdbcConfiguration.class); + return jdbcConfiguration.getDataBase(); + } + + /** + * 获取表字段 + */ + private TableField getTableFiled(ResultSet resultSet, DatasourceRequest datasourceRequest) throws SQLException { + TableField tableField = new TableField(); + String colName = resultSet.getString("COLUMN_NAME"); + tableField.setFieldName(colName); + String remarks = resultSet.getString("REMARKS"); + if (remarks == null || remarks.equals("")) { + remarks = colName; + } + tableField.setRemarks(remarks); + String dbType = resultSet.getString("TYPE_NAME").toUpperCase(); + tableField.setFieldType(dbType); + if (dbType.equalsIgnoreCase("LONG")) { + tableField.setFieldSize(65533); + } + if (StringUtils.isNotEmpty(dbType) && dbType.toLowerCase().contains("date") && tableField.getFieldSize() < 50) { + tableField.setFieldSize(50); + } + + if (datasourceRequest.getDatasource().getType().equalsIgnoreCase(DatasourceTypes.hive.name()) && tableField.getFieldType().equalsIgnoreCase("BOOLEAN")) { + tableField.setFieldSize(1); + } else { + String size = resultSet.getString("COLUMN_SIZE"); + if (size == null) { + tableField.setFieldSize(1); + } else { + tableField.setFieldSize(Integer.valueOf(size)); + } + } + return tableField; + } + + /** + * 检验数据源状态 + */ + @Override + public String checkStatus(DatasourceRequest datasourceRequest) throws Exception { + String queryStr = getTablesSql(datasourceRequest); + JdbcConfiguration jdbcConfiguration = + new Gson().fromJson(datasourceRequest.getDatasource().getConfiguration(), JdbcConfiguration.class); + int queryTimeout = Math.max(jdbcConfiguration.getQueryTimeout(), 0); + try (Connection con = getConnection(datasourceRequest); Statement statement = getStatement(con, queryTimeout); ResultSet resultSet = statement.executeQuery(queryStr)) { + } catch (Exception e) { + e.printStackTrace(); + DataEaseException.throwException(e.getMessage()); + } + return "Success"; + } + + /** + * 显示对应的表的 SQL 语句 + */ + @Override + public String getTablesSql(DatasourceRequest datasourceRequest) throws Exception { + KingbaseConfig kingbaseConfig = new Gson().fromJson(datasourceRequest.getDatasource().getConfiguration(), + KingbaseConfig.class); + if (StringUtils.isEmpty(kingbaseConfig.getSchema())) { + throw new Exception("Database schema is empty."); + } + /*return "select a.table_name, b.comments from all_tables a, user_tab_comments b where a.table_name = b + .table_name and owner=upper('OWNER') ".replaceAll("OWNER", + kingbaseConfig.getSchema());*/ + return ("select table_name from all_tables where owner=upper('OWNER') ").replaceAll("OWNER", + kingbaseConfig.getSchema()); + } + + /** + * 获取所有的用户 + */ + @Override + public String getSchemaSql(DatasourceRequest datasourceRequest) { + return "select * from all_users"; + } + +} diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-backend/src/main/java/io/dataease/plugins/datasource/kingbase/query/KingbaseConstants.java b/extensions/dataease-extensions-datasource/kingbase/kingbase-backend/src/main/java/io/dataease/plugins/datasource/kingbase/query/KingbaseConstants.java new file mode 100644 index 0000000000..911924b8a2 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-backend/src/main/java/io/dataease/plugins/datasource/kingbase/query/KingbaseConstants.java @@ -0,0 +1,60 @@ +package io.dataease.plugins.datasource.kingbase.query; + + +import io.dataease.plugins.common.constants.datasource.SQLConstants; + +import static io.dataease.plugins.common.constants.DatasourceTypes.oracle; + +public class KingbaseConstants extends SQLConstants { + + public static final String KEYWORD_TABLE = oracle.getKeywordPrefix() + "%s" + oracle.getKeywordSuffix(); + + public static final String KEYWORD_FIX = "%s." + oracle.getKeywordPrefix() + "%s" + oracle.getKeywordSuffix(); + + public static final String ALIAS_FIX = oracle.getAliasPrefix() + "%s" + oracle.getAliasSuffix(); + + public static final String UNIX_TIMESTAMP = "UNIX_TIMESTAMP(%s)"; + + public static final String DATE_FORMAT = "to_timestamp(%s,'%s')"; + + public static final String FROM_UNIXTIME = "FROM_UNIXTIME(%s,'%s')"; + + public static final String CAST = "CAST(%s AS %s)"; + + public static final String DEFAULT_DATE_FORMAT = "YYYY-MM-DD HH24:MI:SS"; + + public static final String DEFAULT_INT_FORMAT = "DECIMAL(20,0)"; + + public static final String DEFAULT_FLOAT_FORMAT = "DECIMAL(20,8)"; + + public static final String WHERE_VALUE_NULL = "(NULL,'')"; + + public static final String WHERE_VALUE_VALUE = "'%s'"; + + public static final String AGG_COUNT = "COUNT(*)"; + + public static final String AGG_FIELD = "%s(%s)"; + + public static final String WHERE_BETWEEN = "'%s' AND '%s'"; + + public static final String BRACKETS = "(%s)"; + + public static final String TO_NUMBER = "TO_NUMBER(%s)"; + + public static final String TO_DATE = "TO_DATE(%s,'%s')"; + + public static final String TO_CHAR = "TO_CHAR(%s,'%s')"; + + public static final String DEFAULT_START_DATE = "'1970-01-01 8:0:0'"; + + public static final String TO_MS = " * 24 * 60 * 60 * 100"; + + public static final String CALC_SUB = "%s - %s"; + + // public static final String GROUP_CONCAT = "vm_concat(%s)"; + public static final String GROUP_CONCAT = "to_char(listagg(%s,',' ) within GROUP (order by (%s)))"; + + public static final String NAME = "oracle"; + + +} diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-backend/src/main/java/io/dataease/plugins/datasource/kingbase/query/KingbaseQueryProvider.java b/extensions/dataease-extensions-datasource/kingbase/kingbase-backend/src/main/java/io/dataease/plugins/datasource/kingbase/query/KingbaseQueryProvider.java new file mode 100644 index 0000000000..7a35f8895e --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-backend/src/main/java/io/dataease/plugins/datasource/kingbase/query/KingbaseQueryProvider.java @@ -0,0 +1,1707 @@ +package io.dataease.plugins.datasource.kingbase.query; + +import cn.hutool.json.JSONArray; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.Gson; +import io.dataease.plugins.common.base.domain.ChartViewWithBLOBs; +import io.dataease.plugins.common.base.domain.DatasetTableField; +import io.dataease.plugins.common.base.domain.DatasetTableFieldExample; +import io.dataease.plugins.common.base.domain.Datasource; +import io.dataease.plugins.common.base.mapper.DatasetTableFieldMapper; +import io.dataease.plugins.common.constants.DeTypeConstants; +import io.dataease.plugins.common.constants.datasource.OracleConstants; +import io.dataease.plugins.common.constants.datasource.SQLConstants; +import io.dataease.plugins.common.dto.chart.ChartCustomFilterItemDTO; +import io.dataease.plugins.common.dto.chart.ChartFieldCustomFilterDTO; +import io.dataease.plugins.common.dto.chart.ChartViewFieldDTO; +import io.dataease.plugins.common.dto.datasource.DeSortField; +import io.dataease.plugins.common.dto.sqlObj.SQLObj; +import io.dataease.plugins.common.request.chart.ChartExtFilterRequest; +import io.dataease.plugins.common.request.permission.DataSetRowPermissionsTreeDTO; +import io.dataease.plugins.common.request.permission.DatasetRowPermissionsTreeItem; +import io.dataease.plugins.datasource.kingbase.provider.KingbaseConfig; +import io.dataease.plugins.datasource.entity.Dateformat; +import io.dataease.plugins.datasource.entity.JdbcConfiguration; +import io.dataease.plugins.datasource.entity.PageInfo; +import io.dataease.plugins.datasource.query.QueryProvider; +import io.dataease.plugins.datasource.query.Utils; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; +import org.stringtemplate.v4.ST; +import org.stringtemplate.v4.STGroup; +import org.stringtemplate.v4.STGroupFile; + +import javax.annotation.Resource; +import java.text.MessageFormat; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import static io.dataease.plugins.common.constants.datasource.SQLConstants.TABLE_ALIAS_PREFIX; + +@Component() +public class KingbaseQueryProvider extends QueryProvider { + + @Resource + private DatasetTableFieldMapper datasetTableFieldMapper; + + /** + * 字段类型 + */ + @Override + public Integer transFieldType(String field) { + switch (field) { + case "CHAR": + case "VARCHAR2": + case "VARCHAR": + case "TEXT": + case "TINYTEXT": + case "MEDIUMTEXT": + case "LONGTEXT": + case "ENUM": + case "LONG": + case "NVARCHAR2": + case "NCHAR": + return 0;// 文本 + case "DATE": + case "TIME": + case "YEAR": + case "DATETIME": + case "TIMESTAMP": + return 1;// 时间 + case "INT": + case "INT2": + case "INT4": + case "INT8": + case "SMALLINT": + case "MEDIUMINT": + case "INTEGER": + case "BIGINT": + return 2;// 整型 + case "NUMBER": + case "FLOAT": + case "FLOAT4": + case "FLOAT8": + case "DOUBLE": + case "DECIMAL": + case "DEC": + case "NUMERIC": + return 3;// 浮点 + case "BIT": + case "TINYINT": + return 4;// 布尔 + default: + return 0; + } + } + + /** + * 查询一千行数据 + * + * @param sql + * @param orderBy + * @return + */ + @Override + public String createSQLPreview(String sql, String orderBy) { + return "SELECT * FROM (" + sqlFix(sql) + ") DE_TMP " + " WHERE rownum <= 1000"; + } + + /** + * 创建查询 SQL + * + * @param table + * @param fields + * @param isGroup + * @param ds + * @param fieldCustomFilter + * @param rowPermissionsTree + * @param sortFields + * @return + */ + @Override + public String createQuerySQL(String table, List fields, boolean isGroup, Datasource ds, + List fieldCustomFilter, + List rowPermissionsTree, List sortFields) { + SQLObj tableObj = SQLObj.builder() + .tableName((table.startsWith("(") && table.endsWith(")")) ? table + : String.format(OracleConstants.KEYWORD_TABLE, table)) + .tableAlias(String.format(OracleConstants.ALIAS_FIX, String.format(TABLE_ALIAS_PREFIX, 0))) + .build(); + + //setSchema(tableObj, ds); + List xFields = xFields(table, fields); + + STGroup stg = new STGroupFile(SQLConstants.SQL_TEMPLATE); + ST st_sql = stg.getInstanceOf("previewSql"); + st_sql.add("isGroup", isGroup); + if (CollectionUtils.isNotEmpty(xFields)) + st_sql.add("groups", xFields); + if (ObjectUtils.isNotEmpty(tableObj)) + st_sql.add("table", tableObj); + String customWheres = transCustomFilterList(tableObj, fieldCustomFilter); + // row permissions tree + String whereTrees = transFilterTrees(tableObj, rowPermissionsTree); + List wheres = new ArrayList<>(); + if (customWheres != null) + wheres.add(customWheres); + if (whereTrees != null) + wheres.add(whereTrees); + if (CollectionUtils.isNotEmpty(wheres)) + st_sql.add("filters", wheres); + + List xOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(sortFields)) { + int step = fields.size(); + for (int i = step; i < (step + sortFields.size()); i++) { + DeSortField deSortField = sortFields.get(i - step); + SQLObj order = buildSortField(deSortField, tableObj, i); + xOrders.add(order); + } + } + if (ObjectUtils.isNotEmpty(xOrders)) { + st_sql.add("orders", xOrders); + } + return st_sql.render(); + } + + @Override + public String createQuerySQL(String table, List fields, boolean isGroup, Datasource ds, + List fieldCustomFilter, + List rowPermissionsTree) { + + return createQuerySQL(table, fields, isGroup, ds, fieldCustomFilter, rowPermissionsTree, null); + } + + /** + * 以临时表的方式创建查询 SQL + * + * @param sql + * @param fields + * @param isGroup + * @param fieldCustomFilter + * @param rowPermissionsTree + * @param sortFields + * @return + */ + @Override + public String createQuerySQLAsTmp(String sql, List fields, boolean isGroup, + List fieldCustomFilter, + List rowPermissionsTree, + List sortFields) { + return createQuerySQL("(" + sqlFix(sql) + ")", fields, isGroup, null, fieldCustomFilter, rowPermissionsTree, + sortFields); + } + + /** + * 获取表名 schema + ".表名" + * + * @param tableObj + * @param ds + */ + public void setSchema(SQLObj tableObj, Datasource ds) { + if (ds != null && !tableObj.getTableName().startsWith("(") && !tableObj.getTableName().endsWith(")")) { + String schema = new Gson().fromJson(ds.getConfiguration(), JdbcConfiguration.class).getSchema(); + schema = String.format(OracleConstants.KEYWORD_TABLE, schema); + tableObj.setTableName(schema + "." + tableObj.getTableName()); + } + } + + private SQLObj buildSortField(DeSortField f, SQLObj tableObj, int index) { + String originField; + if (ObjectUtils.isNotEmpty(f.getExtField()) && f.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(f.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(f.getExtField()) && f.getExtField() == 1) { + originField = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), f.getOriginName()); + } else { + originField = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), f.getOriginName()); + } + String fieldAlias = String.format(OracleConstants.ALIAS_FIX, + String.format(SQLConstants.FIELD_ALIAS_X_PREFIX, index)); + String fieldName = ""; + // 处理横轴字段 + if (f.getDeExtractType() == 1) { + if (f.getDeType() == 2 || f.getDeType() == 3) { + fieldName = String.format(OracleConstants.UNIX_TIMESTAMP, originField) + "*1000"; + } else { + fieldName = originField; + } + } else if (f.getDeExtractType() == 0) { + if (f.getDeType() == 2) { + fieldName = String.format(OracleConstants.CAST, originField, OracleConstants.DEFAULT_INT_FORMAT); + } else if (f.getDeType() == 3) { + fieldName = String.format(OracleConstants.CAST, originField, OracleConstants.DEFAULT_FLOAT_FORMAT); + } else if (f.getDeType() == 1) { + fieldName = String.format(OracleConstants.DATE_FORMAT, originField, + OracleConstants.DEFAULT_DATE_FORMAT); + } else { + fieldName = originField; + } + } else { + if (f.getDeType() == 1) { + String cast = String.format(OracleConstants.CAST, originField, OracleConstants.DEFAULT_INT_FORMAT) + + "/1000"; + fieldName = String.format(OracleConstants.FROM_UNIXTIME, cast, OracleConstants.DEFAULT_DATE_FORMAT); + } else if (f.getDeType() == 2) { + fieldName = String.format(OracleConstants.CAST, originField, OracleConstants.DEFAULT_INT_FORMAT); + } else { + fieldName = originField; + } + } + SQLObj result = SQLObj.builder().orderField(originField).orderAlias(originField) + .orderDirection(f.getOrderDirection()).build(); + return result; + } + + private List xFields(String table, List fields) { + SQLObj tableObj = SQLObj.builder() + .tableName((table.startsWith("(") && table.endsWith(")")) ? table + : String.format(OracleConstants.KEYWORD_TABLE, table)) + .tableAlias(String.format(OracleConstants.ALIAS_FIX, String.format(TABLE_ALIAS_PREFIX, 0))) + .build(); + List xFields = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(fields)) { + for (int i = 0; i < fields.size(); i++) { + DatasetTableField f = fields.get(i); + String originField; + if (ObjectUtils.isNotEmpty(f.getExtField()) && f.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(f.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(f.getExtField()) && f.getExtField() == 1) { + originField = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + f.getOriginName()); + } else { + originField = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + f.getOriginName()); + } + String fieldAlias = String.format(OracleConstants.ALIAS_FIX, + String.format(SQLConstants.FIELD_ALIAS_X_PREFIX, i)); + String fieldName = ""; + // 处理横轴字段 + if (f.getDeExtractType() == 1) { + if (f.getDeType() == 2 || f.getDeType() == 3) { + fieldName = String.format(OracleConstants.UNIX_TIMESTAMP, originField) + "*1000"; + } else { + fieldName = originField; + } + } else if (f.getDeExtractType() == 0) { + if (f.getDeType() == 2) { + fieldName = String.format(OracleConstants.CAST, originField, + OracleConstants.DEFAULT_INT_FORMAT); + } else if (f.getDeType() == 3) { + fieldName = String.format(OracleConstants.CAST, originField, + OracleConstants.DEFAULT_FLOAT_FORMAT); + } else if (f.getDeType() == 1) { + fieldName = String.format(OracleConstants.DATE_FORMAT, originField, + OracleConstants.DEFAULT_DATE_FORMAT); + } else { + fieldName = originField; + } + } else { + if (f.getDeType() == 1) { + String cast = String.format(OracleConstants.CAST, originField, + OracleConstants.DEFAULT_INT_FORMAT) + "/1000"; + fieldName = String.format(OracleConstants.FROM_UNIXTIME, cast, + OracleConstants.DEFAULT_DATE_FORMAT); + } else if (f.getDeType() == 2) { + fieldName = String.format(OracleConstants.CAST, originField, + OracleConstants.DEFAULT_INT_FORMAT); + } else { + fieldName = originField; + } + } + xFields.add(SQLObj.builder() + .fieldName(fieldName) + .fieldAlias(fieldAlias) + .build()); + } + } + return xFields; + } + + private String sqlColumn(List xFields) { + String[] array = xFields.stream().map(f -> { + return f.getFieldAlias(); + }).toArray(String[]::new); + return StringUtils.join(array, ","); + } + + @Override + public String createQuerySQLAsTmp(String sql, List fields, boolean isGroup, + List fieldCustomFilter, + List rowPermissionsTree) { + return createQuerySQL("(" + sqlFix(sql) + ")", fields, isGroup, null, fieldCustomFilter, rowPermissionsTree); + } + + @Override + public String createQueryTableWithPage(String table, List fields, Integer page, Integer pageSize, + Integer realSize, boolean isGroup, Datasource ds, + List fieldCustomFilter, + List rowPermissionsTree) { + List xFields = xFields(table, fields); + + return MessageFormat.format( + "SELECT {0} FROM ( SELECT DE_TMP.*, rownum r FROM ( {1} ) DE_TMP WHERE rownum <= {2} ) WHERE r > {3} ", + sqlColumn(xFields), createQuerySQL(table, fields, isGroup, ds, fieldCustomFilter, rowPermissionsTree), + Integer.valueOf(page * realSize).toString(), Integer.valueOf((page - 1) * pageSize).toString()); + } + + @Override + public String createQuerySQLWithPage(String sql, List fields, Integer page, Integer pageSize, + Integer realSize, boolean isGroup, + List fieldCustomFilter, + List rowPermissionsTree) { + List xFields = xFields("(" + sqlFix(sql) + ")", fields); + return MessageFormat.format( + "SELECT {0} FROM ( SELECT DE_TMP.*, rownum r FROM ( {1} ) DE_TMP WHERE rownum <= {2} ) WHERE r > {3} ", + sqlColumn(xFields), createQuerySQLAsTmp(sql, fields, isGroup, fieldCustomFilter, rowPermissionsTree), + Integer.valueOf(page * realSize).toString(), Integer.valueOf((page - 1) * pageSize).toString()); + } + + @Override + public String createQueryTableWithLimit(String table, List fields, Integer limit, + boolean isGroup, Datasource ds, + List fieldCustomFilter, + List rowPermissionsTree) { + /*String schema = new Gson().fromJson(ds.getConfiguration(), JdbcConfiguration.class).getSchema(); + return String.format("SELECT * from %s WHERE rownum <= %s ", + schema + "." + String.format(OracleConstants.KEYWORD_TABLE, table), limit.toString());*/ + return String.format("SELECT * from %s WHERE rownum <= %s ", + String.format(OracleConstants.KEYWORD_TABLE, table), limit.toString()); + } + + @Override + public String createQuerySqlWithLimit(String sql, List fields, Integer limit, boolean isGroup, + List fieldCustomFilter, + List rowPermissionsTree) { + return String.format("SELECT * from %s WHERE rownum <= %s ", "(" + sqlFix(sql) + ")", limit.toString()); + } + + @Override + public String getSQL(String table, List xAxis, List yAxis, + List fieldCustomFilter, + List rowPermissionsTree, + List extFilterRequestList, + Datasource ds, ChartViewWithBLOBs view) { + SQLObj tableObj = SQLObj.builder() + .tableName((table.startsWith("(") && table.endsWith(")")) ? table + : String.format(OracleConstants.KEYWORD_TABLE, table)) + .tableAlias(String.format(OracleConstants.ALIAS_FIX, String.format(TABLE_ALIAS_PREFIX, 0))) + .build(); + //setSchema(tableObj, ds); + List xFields = new ArrayList<>(); + List xOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(xAxis)) { + for (int i = 0; i < xAxis.size(); i++) { + ChartViewFieldDTO x = xAxis.get(i); + String originField; + if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(x.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 1) { + originField = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + x.getOriginName()); + } else { + originField = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + x.getOriginName()); + } + String fieldAlias = String.format(OracleConstants.ALIAS_FIX, + String.format(SQLConstants.FIELD_ALIAS_X_PREFIX, i)); + // 处理横轴字段 + xFields.add(getXFields(x, originField, fieldAlias)); + // 处理横轴排序 + if (StringUtils.isNotEmpty(x.getSort()) && Utils.joinSort(x.getSort())) { + xOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(x.getSort()) + .build()); + } + } + } + List yFields = new ArrayList<>(); + List yWheres = new ArrayList<>(); + List yOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(yAxis)) { + for (int i = 0; i < yAxis.size(); i++) { + ChartViewFieldDTO y = yAxis.get(i); + String originField; + if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(y.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 1) { + originField = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + y.getOriginName()); + } else { + originField = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + y.getOriginName()); + } + String fieldAlias = String.format(OracleConstants.ALIAS_FIX, + String.format(SQLConstants.FIELD_ALIAS_Y_PREFIX, i)); + // 处理纵轴字段 + yFields.add(getYFields(y, originField, fieldAlias)); + // 处理纵轴过滤 + yWheres.add(getYWheres(y, originField, fieldAlias)); + // 处理纵轴排序 + if (StringUtils.isNotEmpty(y.getSort()) && Utils.joinSort(y.getSort())) { + yOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(y.getSort()) + .build()); + } + } + } + // 处理视图中字段过滤 + String customWheres = transCustomFilterList(tableObj, fieldCustomFilter); + // 处理仪表板字段过滤 + String extWheres = transExtFilterList(tableObj, extFilterRequestList); + // row permissions tree + String whereTrees = transFilterTrees(tableObj, rowPermissionsTree); + // 构建sql所有参数 + List fields = new ArrayList<>(); + fields.addAll(xFields); + fields.addAll(yFields); + List wheres = new ArrayList<>(); + if (customWheres != null) + wheres.add(customWheres); + if (extWheres != null) + wheres.add(extWheres); + if (whereTrees != null) + wheres.add(whereTrees); + List groups = new ArrayList<>(); + groups.addAll(xFields); + // 外层再次套sql + List orders = new ArrayList<>(); + orders.addAll(xOrders); + orders.addAll(yOrders); + List aggWheres = new ArrayList<>(); + aggWheres.addAll(yWheres.stream().filter(ObjectUtils::isNotEmpty).collect(Collectors.toList())); + + STGroup stg = new STGroupFile(SQLConstants.SQL_TEMPLATE); + ST st_sql = stg.getInstanceOf("querySql"); + if (CollectionUtils.isNotEmpty(xFields)) + st_sql.add("groups", xFields); + if (CollectionUtils.isNotEmpty(yFields)) + st_sql.add("aggregators", yFields); + if (CollectionUtils.isNotEmpty(wheres)) + st_sql.add("filters", wheres); + if (ObjectUtils.isNotEmpty(tableObj)) + st_sql.add("table", tableObj); + String sql = st_sql.render(); + + ST st = stg.getInstanceOf("querySql"); + SQLObj tableSQL = SQLObj.builder() + .tableName(String.format(OracleConstants.BRACKETS, sql)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 1)) + .build(); + if (CollectionUtils.isNotEmpty(aggWheres)) + st.add("filters", aggWheres); + if (CollectionUtils.isNotEmpty(orders)) + st.add("orders", orders); + if (ObjectUtils.isNotEmpty(tableSQL)) + st.add("table", tableSQL); + return sqlLimit(st.render(), view); + } + + private String originalTableInfo(String table, List xAxis, + List fieldCustomFilter, + List rowPermissionsTree, + List extFilterRequestList, Datasource ds, + ChartViewWithBLOBs view) { + SQLObj tableObj = SQLObj.builder() + .tableName((table.startsWith("(") && table.endsWith(")")) ? table + : String.format(OracleConstants.KEYWORD_TABLE, table)) + .tableAlias(String.format(OracleConstants.ALIAS_FIX, String.format(TABLE_ALIAS_PREFIX, 0))) + .build(); + //setSchema(tableObj, ds); + List xFields = new ArrayList<>(); + List xOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(xAxis)) { + for (int i = 0; i < xAxis.size(); i++) { + ChartViewFieldDTO x = xAxis.get(i); + String originField; + if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(x.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 1) { + originField = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + x.getOriginName()); + } else { + if (x.getDeType() == 2 || x.getDeType() == 3) { + originField = String.format(OracleConstants.CAST, + String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), x.getOriginName()), + OracleConstants.DEFAULT_FLOAT_FORMAT); + } else { + originField = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + x.getOriginName()); + } + } + String fieldAlias = String.format(OracleConstants.ALIAS_FIX, + String.format(SQLConstants.FIELD_ALIAS_X_PREFIX, i)); + // 处理横轴字段 + xFields.add(getXFields(x, originField, fieldAlias)); + // 处理横轴排序 + if (StringUtils.isNotEmpty(x.getSort()) && Utils.joinSort(x.getSort())) { + xOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(x.getSort()) + .build()); + } + } + } + // 处理视图中字段过滤 + String customWheres = transCustomFilterList(tableObj, fieldCustomFilter); + // 处理仪表板字段过滤 + String extWheres = transExtFilterList(tableObj, extFilterRequestList); + // row permissions tree + String whereTrees = transFilterTrees(tableObj, rowPermissionsTree); + // 构建sql所有参数 + List fields = new ArrayList<>(); + fields.addAll(xFields); + List wheres = new ArrayList<>(); + if (customWheres != null) + wheres.add(customWheres); + if (extWheres != null) + wheres.add(extWheres); + if (whereTrees != null) + wheres.add(whereTrees); + List groups = new ArrayList<>(); + groups.addAll(xFields); + // 外层再次套sql + List orders = new ArrayList<>(); + orders.addAll(xOrders); + + STGroup stg = new STGroupFile(SQLConstants.SQL_TEMPLATE); + ST st_sql = stg.getInstanceOf("previewSql"); + st_sql.add("isGroup", false); + if (CollectionUtils.isNotEmpty(xFields)) + st_sql.add("groups", xFields); + if (CollectionUtils.isNotEmpty(wheres)) + st_sql.add("filters", wheres); + if (ObjectUtils.isNotEmpty(tableObj)) + st_sql.add("table", tableObj); + String sql = st_sql.render(); + + ST st = stg.getInstanceOf("previewSql"); + st.add("isGroup", false); + SQLObj tableSQL = SQLObj.builder() + .tableName(String.format(OracleConstants.BRACKETS, sql)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 1)) + .build(); + if (CollectionUtils.isNotEmpty(orders)) + st.add("orders", orders); + if (ObjectUtils.isNotEmpty(tableSQL)) + st.add("table", tableSQL); + return st.render(); + } + + @Override + public String getSQLTableInfo(String table, List xAxis, + List fieldCustomFilter, + List rowPermissionsTree, + List extFilterRequestList, Datasource ds, + ChartViewWithBLOBs view) { + return sqlLimit(originalTableInfo(table, xAxis, fieldCustomFilter, rowPermissionsTree, extFilterRequestList, + ds, view), view); + } + + @Override + public String getSQLWithPage(boolean isTable, String table, List xAxis, + List fieldCustomFilter, + List rowPermissionsTree, + List extFilterRequestList, Datasource ds, + ChartViewWithBLOBs view, PageInfo pageInfo) { + String limit = ((pageInfo.getGoPage() != null && pageInfo.getPageSize() != null) ? + " LIMIT " + (pageInfo.getGoPage() - 1) * pageInfo.getPageSize() + "," + pageInfo.getPageSize() : ""); + if (isTable) { + return originalTableInfo(table, xAxis, fieldCustomFilter, rowPermissionsTree, extFilterRequestList, ds, + view) + limit; + } else { + return originalTableInfo("(" + sqlFix(table) + ")", xAxis, fieldCustomFilter, rowPermissionsTree, + extFilterRequestList, ds, view) + limit; + } + } + + @Override + public String getSQLAsTmpTableInfo(String sql, List xAxis, + List fieldCustomFilter, + List rowPermissionsTree, + List extFilterRequestList, Datasource ds, + ChartViewWithBLOBs view) { + return getSQLTableInfo("(" + sqlFix(sql) + ")", xAxis, fieldCustomFilter, rowPermissionsTree, + extFilterRequestList, null, view); + } + + @Override + public String getSQLAsTmp(String sql, List xAxis, List yAxis, + List fieldCustomFilter, + List rowPermissionsTree, + List extFilterRequestList, ChartViewWithBLOBs view) { + return getSQL("(" + sqlFix(sql) + ")", xAxis, yAxis, fieldCustomFilter, rowPermissionsTree, + extFilterRequestList, null, view); + } + + @Override + public String getSQLStack(String table, List xAxis, List yAxis, + List fieldCustomFilter, + List rowPermissionsTree, + List extFilterRequestList, + List extStack, Datasource ds, ChartViewWithBLOBs view) { + SQLObj tableObj = SQLObj.builder() + .tableName((table.startsWith("(") && table.endsWith(")")) ? table + : String.format(OracleConstants.KEYWORD_TABLE, table)) + .tableAlias(String.format(OracleConstants.ALIAS_FIX, String.format(TABLE_ALIAS_PREFIX, 0))) + .build(); + //setSchema(tableObj, ds); + List xFields = new ArrayList<>(); + List xOrders = new ArrayList<>(); + List xList = new ArrayList<>(); + xList.addAll(xAxis); + xList.addAll(extStack); + if (CollectionUtils.isNotEmpty(xList)) { + for (int i = 0; i < xList.size(); i++) { + ChartViewFieldDTO x = xList.get(i); + String originField; + if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(x.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 1) { + originField = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + x.getOriginName()); + } else { + originField = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + x.getOriginName()); + } + String fieldAlias = String.format(OracleConstants.ALIAS_FIX, + String.format(SQLConstants.FIELD_ALIAS_X_PREFIX, i)); + // 处理横轴字段 + xFields.add(getXFields(x, originField, fieldAlias)); + // 处理横轴排序 + if (StringUtils.isNotEmpty(x.getSort()) && Utils.joinSort(x.getSort())) { + xOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(x.getSort()) + .build()); + } + } + } + List yFields = new ArrayList<>(); + List yWheres = new ArrayList<>(); + List yOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(yAxis)) { + for (int i = 0; i < yAxis.size(); i++) { + ChartViewFieldDTO y = yAxis.get(i); + String originField; + if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(y.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 1) { + originField = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + y.getOriginName()); + } else { + originField = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + y.getOriginName()); + } + String fieldAlias = String.format(OracleConstants.ALIAS_FIX, + String.format(SQLConstants.FIELD_ALIAS_Y_PREFIX, i)); + // 处理纵轴字段 + yFields.add(getYFields(y, originField, fieldAlias)); + // 处理纵轴过滤 + yWheres.add(getYWheres(y, originField, fieldAlias)); + // 处理纵轴排序 + if (StringUtils.isNotEmpty(y.getSort()) && Utils.joinSort(y.getSort())) { + yOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(y.getSort()) + .build()); + } + } + } + // 处理视图中字段过滤 + String customWheres = transCustomFilterList(tableObj, fieldCustomFilter); + // 处理仪表板字段过滤 + String extWheres = transExtFilterList(tableObj, extFilterRequestList); + // row permissions tree + String whereTrees = transFilterTrees(tableObj, rowPermissionsTree); + // 构建sql所有参数 + List fields = new ArrayList<>(); + fields.addAll(xFields); + fields.addAll(yFields); + List wheres = new ArrayList<>(); + if (customWheres != null) + wheres.add(customWheres); + if (extWheres != null) + wheres.add(extWheres); + if (whereTrees != null) + wheres.add(whereTrees); + List groups = new ArrayList<>(); + groups.addAll(xFields); + // 外层再次套sql + List orders = new ArrayList<>(); + orders.addAll(xOrders); + orders.addAll(yOrders); + List aggWheres = new ArrayList<>(); + aggWheres.addAll(yWheres.stream().filter(ObjectUtils::isNotEmpty).collect(Collectors.toList())); + + STGroup stg = new STGroupFile(SQLConstants.SQL_TEMPLATE); + ST st_sql = stg.getInstanceOf("querySql"); + if (CollectionUtils.isNotEmpty(xFields)) + st_sql.add("groups", xFields); + if (CollectionUtils.isNotEmpty(yFields)) + st_sql.add("aggregators", yFields); + if (CollectionUtils.isNotEmpty(wheres)) + st_sql.add("filters", wheres); + if (ObjectUtils.isNotEmpty(tableObj)) + st_sql.add("table", tableObj); + String sql = st_sql.render(); + + ST st = stg.getInstanceOf("querySql"); + SQLObj tableSQL = SQLObj.builder() + .tableName(String.format(OracleConstants.BRACKETS, sql)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 1)) + .build(); + if (CollectionUtils.isNotEmpty(aggWheres)) + st.add("filters", aggWheres); + if (CollectionUtils.isNotEmpty(orders)) + st.add("orders", orders); + if (ObjectUtils.isNotEmpty(tableSQL)) + st.add("table", tableSQL); + return sqlLimit(st.render(), view); + } + + @Override + public String getSQLAsTmpStack(String table, List xAxis, List yAxis, + List fieldCustomFilter, + List rowPermissionsTree, + List extFilterRequestList, + List extStack, ChartViewWithBLOBs view) { + return getSQLStack("(" + sqlFix(table) + ")", xAxis, yAxis, fieldCustomFilter, rowPermissionsTree, + extFilterRequestList, extStack, + null, view); + } + + /** + * 获取 SQL 函数 + * + * @param table + * @param xAxis + * @param yAxis + * @param fieldCustomFilter + * @param rowPermissionsTree + * @param extFilterRequestList + * @param extBubble + * @param ds + * @param view + * @return + */ + @Override + public String getSQLScatter(String table, List xAxis, List yAxis, + List fieldCustomFilter, + List rowPermissionsTree, + List extFilterRequestList, + List extBubble, Datasource ds, ChartViewWithBLOBs view) { + SQLObj tableObj = SQLObj.builder() + .tableName((table.startsWith("(") && table.endsWith(")")) ? table + : String.format(OracleConstants.KEYWORD_TABLE, table)) + .tableAlias(String.format(OracleConstants.ALIAS_FIX, String.format(TABLE_ALIAS_PREFIX, 0))) + .build(); + //setSchema(tableObj, ds); + List xFields = new ArrayList<>(); + List xOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(xAxis)) { + for (int i = 0; i < xAxis.size(); i++) { + ChartViewFieldDTO x = xAxis.get(i); + String originField; + if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(x.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 1) { + originField = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + x.getOriginName()); + } else { + originField = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + x.getOriginName()); + } + String fieldAlias = String.format(OracleConstants.ALIAS_FIX, + String.format(SQLConstants.FIELD_ALIAS_X_PREFIX, i)); + // 处理横轴字段 + xFields.add(getXFields(x, originField, fieldAlias)); + // 处理横轴排序 + if (StringUtils.isNotEmpty(x.getSort()) && Utils.joinSort(x.getSort())) { + xOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(x.getSort()) + .build()); + } + } + } + List yFields = new ArrayList<>(); + List yWheres = new ArrayList<>(); + List yOrders = new ArrayList<>(); + List yList = new ArrayList<>(); + yList.addAll(yAxis); + yList.addAll(extBubble); + if (CollectionUtils.isNotEmpty(yList)) { + for (int i = 0; i < yList.size(); i++) { + ChartViewFieldDTO y = yList.get(i); + String originField; + if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(y.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 1) { + originField = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + y.getOriginName()); + } else { + originField = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + y.getOriginName()); + } + String fieldAlias = String.format(OracleConstants.ALIAS_FIX, + String.format(SQLConstants.FIELD_ALIAS_Y_PREFIX, i)); + // 处理纵轴字段 + yFields.add(getYFields(y, originField, fieldAlias)); + // 处理纵轴过滤 + yWheres.add(getYWheres(y, originField, fieldAlias)); + // 处理纵轴排序 + if (StringUtils.isNotEmpty(y.getSort()) && Utils.joinSort(y.getSort())) { + yOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(y.getSort()) + .build()); + } + } + } + // 处理视图中字段过滤 + String customWheres = transCustomFilterList(tableObj, fieldCustomFilter); + // 处理仪表板字段过滤 + String extWheres = transExtFilterList(tableObj, extFilterRequestList); + // row permissions tree + String whereTrees = transFilterTrees(tableObj, rowPermissionsTree); + // 构建sql所有参数 + List fields = new ArrayList<>(); + fields.addAll(xFields); + fields.addAll(yFields); + List wheres = new ArrayList<>(); + if (customWheres != null) + wheres.add(customWheres); + if (extWheres != null) + wheres.add(extWheres); + if (whereTrees != null) + wheres.add(whereTrees); + List groups = new ArrayList<>(); + groups.addAll(xFields); + // 外层再次套sql + List orders = new ArrayList<>(); + orders.addAll(xOrders); + orders.addAll(yOrders); + List aggWheres = new ArrayList<>(); + aggWheres.addAll(yWheres.stream().filter(ObjectUtils::isNotEmpty).collect(Collectors.toList())); + + STGroup stg = new STGroupFile(SQLConstants.SQL_TEMPLATE); + ST st_sql = stg.getInstanceOf("querySql"); + if (CollectionUtils.isNotEmpty(xFields)) + st_sql.add("groups", xFields); + if (CollectionUtils.isNotEmpty(yFields)) + st_sql.add("aggregators", yFields); + if (CollectionUtils.isNotEmpty(wheres)) + st_sql.add("filters", wheres); + if (ObjectUtils.isNotEmpty(tableObj)) + st_sql.add("table", tableObj); + String sql = st_sql.render(); + + ST st = stg.getInstanceOf("querySql"); + SQLObj tableSQL = SQLObj.builder() + .tableName(String.format(OracleConstants.BRACKETS, sql)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 1)) + .build(); + if (CollectionUtils.isNotEmpty(aggWheres)) + st.add("filters", aggWheres); + if (CollectionUtils.isNotEmpty(orders)) + st.add("orders", orders); + if (ObjectUtils.isNotEmpty(tableSQL)) + st.add("table", tableSQL); + return sqlLimit(st.render(), view); + } + + @Override + public String getSQLAsTmpScatter(String table, List xAxis, List yAxis, + List fieldCustomFilter, + List rowPermissionsTree, + List extFilterRequestList, + List extBubble, ChartViewWithBLOBs view) { + return getSQLScatter("(" + sqlFix(table) + ")", xAxis, yAxis, fieldCustomFilter, rowPermissionsTree, + extFilterRequestList, + extBubble, null, view); + } + + /** + * 查询表 + * + * @param table + * @return + */ + @Override + public String searchTable(String table) { + return "SELECT table_name FROM information_schema.TABLES WHERE table_name ='" + table + "'"; + } + + @Override + public String getSQLSummary(String table, List yAxis, + List fieldCustomFilter, + List rowPermissionsTree, + List extFilterRequestList, + ChartViewWithBLOBs view, Datasource ds) { + // 字段汇总 排序等 + SQLObj tableObj = SQLObj.builder() + .tableName((table.startsWith("(") && table.endsWith(")")) ? table + : String.format(OracleConstants.KEYWORD_TABLE, table)) + .tableAlias(String.format(OracleConstants.ALIAS_FIX, String.format(TABLE_ALIAS_PREFIX, 0))) + .build(); + //setSchema(tableObj, ds); + List yFields = new ArrayList<>(); + List yWheres = new ArrayList<>(); + List yOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(yAxis)) { + for (int i = 0; i < yAxis.size(); i++) { + ChartViewFieldDTO y = yAxis.get(i); + String originField; + if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(y.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 1) { + originField = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + y.getOriginName()); + } else { + originField = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + y.getOriginName()); + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_Y_PREFIX, i); + // 处理纵轴字段 + yFields.add(getYFields(y, originField, fieldAlias)); + // 处理纵轴过滤 + yWheres.add(getYWheres(y, originField, fieldAlias)); + // 处理纵轴排序 + if (StringUtils.isNotEmpty(y.getSort()) && Utils.joinSort(y.getSort())) { + yOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(y.getSort()) + .build()); + } + } + } + // 处理视图中字段过滤 + String customWheres = transCustomFilterList(tableObj, fieldCustomFilter); + // 处理仪表板字段过滤 + String extWheres = transExtFilterList(tableObj, extFilterRequestList); + // row permissions tree + String whereTrees = transFilterTrees(tableObj, rowPermissionsTree); + // 构建sql所有参数 + List fields = new ArrayList<>(); + fields.addAll(yFields); + List wheres = new ArrayList<>(); + if (customWheres != null) + wheres.add(customWheres); + if (extWheres != null) + wheres.add(extWheres); + if (whereTrees != null) + wheres.add(whereTrees); + // 外层再次套sql + List orders = new ArrayList<>(); + orders.addAll(yOrders); + List aggWheres = new ArrayList<>(); + aggWheres.addAll(yWheres.stream().filter(ObjectUtils::isNotEmpty).collect(Collectors.toList())); + + STGroup stg = new STGroupFile(SQLConstants.SQL_TEMPLATE); + ST st_sql = stg.getInstanceOf("querySql"); + if (CollectionUtils.isNotEmpty(yFields)) + st_sql.add("aggregators", yFields); + if (CollectionUtils.isNotEmpty(wheres)) + st_sql.add("filters", wheres); + if (ObjectUtils.isNotEmpty(tableObj)) + st_sql.add("table", tableObj); + String sql = st_sql.render(); + + ST st = stg.getInstanceOf("querySql"); + SQLObj tableSQL = SQLObj.builder() + .tableName(String.format(OracleConstants.BRACKETS, sql)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 1)) + .build(); + if (CollectionUtils.isNotEmpty(aggWheres)) + st.add("filters", aggWheres); + if (CollectionUtils.isNotEmpty(orders)) + st.add("orders", orders); + if (ObjectUtils.isNotEmpty(tableSQL)) + st.add("table", tableSQL); + return sqlLimit(st.render(), view); + } + + @Override + public String getSQLSummaryAsTmp(String sql, List yAxis, + List fieldCustomFilter, + List rowPermissionsTree, + List extFilterRequestList, + ChartViewWithBLOBs view) { + return getSQLSummary("(" + sqlFix(sql) + ")", yAxis, fieldCustomFilter, rowPermissionsTree, + extFilterRequestList, view, null); + } + + @Override + public String wrapSql(String sql) { + sql = sql.trim(); + if (sql.lastIndexOf(";") == (sql.length() - 1)) { + sql = sql.substring(0, sql.length() - 1); + } + String tmpSql = "SELECT * FROM (" + sql + ") DE_TMP " + " where rownum <= 0"; + return tmpSql; + } + + @Override + public String createRawQuerySQL(String table, List fields, Datasource ds) { + String[] array = fields.stream().map(f -> { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("\"").append(f.getOriginName()).append("\""); + return stringBuilder.toString(); + }).toArray(String[]::new); + KingbaseConfig kingbaseConfig = new Gson().fromJson(ds.getConfiguration(), KingbaseConfig.class); + /*return MessageFormat.format("SELECT {0} FROM {1}", StringUtils.join(array, ","), + kingbaseConfig.getSchema() + ".\"" + table + "\"");*/ + return MessageFormat.format("SELECT {0} FROM {1}", StringUtils.join(array, ","), + table + "\""); + } + + @Override + public String createRawQuerySQLAsTmp(String sql, List fields) { + String[] array = fields.stream().map(f -> { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(" \"").append(f.getOriginName()).append("\""); + return stringBuilder.toString(); + }).toArray(String[]::new); + return MessageFormat.format("SELECT {0} FROM {1}", StringUtils.join(array, ","), + " (" + sqlFix(sql) + ") DE_TMP "); + } + + @Override + public String transTreeItem(SQLObj tableObj, DatasetRowPermissionsTreeItem item) { + String res = null; + DatasetTableField field = item.getField(); + if (ObjectUtils.isEmpty(field)) { + return null; + } + String whereName = ""; + String originName; + if (ObjectUtils.isNotEmpty(field.getExtField()) && field.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originName = calcFieldRegex(field.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(field.getExtField()) && field.getExtField() == 1) { + originName = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + field.getOriginName()); + } else { + originName = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + field.getOriginName()); + } + + if (field.getDeType() == 1) { + if (field.getDeExtractType() == 0 || field.getDeExtractType() == 5) { + whereName = String.format(OracleConstants.TO_DATE, originName, + StringUtils.isNotEmpty(field.getDateFormat()) ? field.getDateFormat() : + OracleConstants.DEFAULT_DATE_FORMAT); + } + if (field.getDeExtractType() == 2 || field.getDeExtractType() == 3 || field.getDeExtractType() == 4) { + String cast = String.format(OracleConstants.CAST, originName, OracleConstants.DEFAULT_INT_FORMAT) + + "/1000"; + whereName = String.format(OracleConstants.FROM_UNIXTIME, cast, OracleConstants.DEFAULT_DATE_FORMAT); + } + if (field.getDeExtractType() == 1) { + whereName = originName; + } + } else if (field.getDeType() == 2 || field.getDeType() == 3) { + if (field.getDeExtractType() == 0 || field.getDeExtractType() == 5) { + whereName = String.format(OracleConstants.CAST, originName, OracleConstants.DEFAULT_FLOAT_FORMAT); + } + if (field.getDeExtractType() == 1) { + whereName = String.format(OracleConstants.UNIX_TIMESTAMP, originName) + "*1000"; + } + if (field.getDeExtractType() == 2 || field.getDeExtractType() == 3 || field.getDeExtractType() == 4) { + whereName = originName; + } + } else { + whereName = originName; + } + + if (StringUtils.equalsIgnoreCase(item.getFilterType(), "enum")) { + if (CollectionUtils.isNotEmpty(item.getEnumValue())) { + res = "(" + whereName + " IN ('" + String.join("','", item.getEnumValue()) + "'))"; + } + } else { + String value = item.getValue(); + String whereTerm = transMysqlFilterTerm(item.getTerm()); + String whereValue = ""; + + if (StringUtils.equalsIgnoreCase(item.getTerm(), "null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(item.getTerm(), "not_null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(item.getTerm(), "empty")) { + whereValue = "''"; + } else if (StringUtils.equalsIgnoreCase(item.getTerm(), "not_empty")) { + whereValue = "''"; + } else if (StringUtils.containsIgnoreCase(item.getTerm(), "in") + || StringUtils.containsIgnoreCase(item.getTerm(), "not in")) { + whereValue = "('" + String.join("','", value.split(",")) + "')"; + } else if (StringUtils.containsIgnoreCase(item.getTerm(), "like")) { + whereValue = "'%" + value + "%'"; + } else { + if (field.getDeType() == 1) { + whereValue = String.format(OracleConstants.TO_DATE, "'" + value + "'", + OracleConstants.DEFAULT_DATE_FORMAT); + } else { + whereValue = String.format(OracleConstants.WHERE_VALUE_VALUE, value); + } + } + SQLObj build = SQLObj.builder() + .whereField(whereName) + .whereTermAndValue(whereTerm + whereValue) + .build(); + res = build.getWhereField() + " " + build.getWhereTermAndValue(); + } + return res; + } + + @Override + public String convertTableToSql(String tableName, Datasource ds) { + /*String schema = new Gson().fromJson(ds.getConfiguration(), JdbcConfiguration.class).getSchema(); + schema = String.format(OracleConstants.KEYWORD_TABLE, schema); + return createSQLPreview( + "SELECT * FROM " + schema + "." + String.format(OracleConstants.KEYWORD_TABLE, tableName), null);*/ + return createSQLPreview( + "SELECT * FROM " + String.format(OracleConstants.KEYWORD_TABLE, tableName), null); + } + + public String transMysqlFilterTerm(String term) { + switch (term) { + case "eq": + return " = "; + case "not_eq": + return " <> "; + case "lt": + return " < "; + case "le": + return " <= "; + case "gt": + return " > "; + case "ge": + return " >= "; + case "in": + return " IN "; + case "not in": + return " NOT IN "; + case "like": + return " LIKE "; + case "not like": + return " NOT LIKE "; + case "null": + return " IS NULL "; + case "not_null": + return " IS NOT NULL "; + case "empty": + return " = "; + case "not_empty": + return " <> "; + case "between": + return " BETWEEN "; + default: + return ""; + } + } + + public String transCustomFilterList(SQLObj tableObj, List requestList) { + if (CollectionUtils.isEmpty(requestList)) { + return null; + } + List res = new ArrayList<>(); + for (ChartFieldCustomFilterDTO request : requestList) { + List list = new ArrayList<>(); + DatasetTableField field = request.getField(); + + if (ObjectUtils.isEmpty(field)) { + continue; + } + String whereName = ""; + String originName; + if (ObjectUtils.isNotEmpty(field.getExtField()) && field.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originName = calcFieldRegex(field.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(field.getExtField()) && field.getExtField() == 1) { + originName = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + field.getOriginName()); + } else { + originName = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + field.getOriginName()); + } + + if (field.getDeType() == 1) { + if (field.getDeExtractType() == 0 || field.getDeExtractType() == 5) { + whereName = String.format(OracleConstants.TO_DATE, originName, + StringUtils.isNotEmpty(field.getDateFormat()) ? field.getDateFormat() : + OracleConstants.DEFAULT_DATE_FORMAT); + } + if (field.getDeExtractType() == 2 || field.getDeExtractType() == 3 || field.getDeExtractType() == 4) { + String cast = String.format(OracleConstants.CAST, originName, OracleConstants.DEFAULT_INT_FORMAT) + + "/1000"; + whereName = String.format(OracleConstants.FROM_UNIXTIME, cast, OracleConstants.DEFAULT_DATE_FORMAT); + } + if (field.getDeExtractType() == 1) { + whereName = originName; + } + } else if (field.getDeType() == 2 || field.getDeType() == 3) { + if (field.getDeExtractType() == 0 || field.getDeExtractType() == 5) { + whereName = String.format(OracleConstants.CAST, originName, OracleConstants.DEFAULT_FLOAT_FORMAT); + } + if (field.getDeExtractType() == 1) { + whereName = String.format(OracleConstants.UNIX_TIMESTAMP, originName) + "*1000"; + } + if (field.getDeExtractType() == 2 || field.getDeExtractType() == 3 || field.getDeExtractType() == 4) { + whereName = originName; + } + } else { + whereName = originName; + } + + if (StringUtils.equalsIgnoreCase(request.getFilterType(), "enum")) { + if (CollectionUtils.isNotEmpty(request.getEnumCheckField())) { + res.add("(" + whereName + " IN ('" + String.join("','", request.getEnumCheckField()) + "'))"); + } + } else { + List filter = request.getFilter(); + for (ChartCustomFilterItemDTO filterItemDTO : filter) { + String value = filterItemDTO.getValue(); + String whereTerm = transMysqlFilterTerm(filterItemDTO.getTerm()); + String whereValue = ""; + + if (StringUtils.equalsIgnoreCase(filterItemDTO.getTerm(), "null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(filterItemDTO.getTerm(), "not_null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(filterItemDTO.getTerm(), "empty")) { + whereValue = "''"; + } else if (StringUtils.equalsIgnoreCase(filterItemDTO.getTerm(), "not_empty")) { + whereValue = "''"; + } else if (StringUtils.containsIgnoreCase(filterItemDTO.getTerm(), "in") + || StringUtils.containsIgnoreCase(filterItemDTO.getTerm(), "not in")) { + whereValue = "('" + String.join("','", value.split(",")) + "')"; + } else if (StringUtils.containsIgnoreCase(filterItemDTO.getTerm(), "like")) { + whereValue = "'%" + value + "%'"; + } else { + if (field.getDeType() == 1) { + whereValue = String.format(OracleConstants.TO_DATE, "'" + value + "'", + OracleConstants.DEFAULT_DATE_FORMAT); + } else { + whereValue = String.format(OracleConstants.WHERE_VALUE_VALUE, value); + } + } + list.add(SQLObj.builder() + .whereField(whereName) + .whereTermAndValue(whereTerm + whereValue) + .build()); + } + + List strList = new ArrayList<>(); + list.forEach(ele -> strList.add(ele.getWhereField() + " " + ele.getWhereTermAndValue())); + if (CollectionUtils.isNotEmpty(list)) { + res.add("(" + String.join(" " + getLogic(request.getLogic()) + " ", strList) + ")"); + } + } + } + return CollectionUtils.isNotEmpty(res) ? "(" + String.join(" AND ", res) + ")" : null; + } + + public String transExtFilterList(SQLObj tableObj, List requestList) { + if (CollectionUtils.isEmpty(requestList)) { + return null; + } + List list = new ArrayList<>(); + for (ChartExtFilterRequest request : requestList) { + List value = request.getValue(); + + List whereNameList = new ArrayList<>(); + List fieldList = new ArrayList<>(); + if (request.getIsTree()) { + fieldList.addAll(request.getDatasetTableFieldList()); + } else { + fieldList.add(request.getDatasetTableField()); + } + + for (DatasetTableField field : fieldList) { + if (CollectionUtils.isEmpty(value) || ObjectUtils.isEmpty(field)) { + continue; + } + String whereName = ""; + + String originName; + if (ObjectUtils.isNotEmpty(field.getExtField()) && field.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originName = calcFieldRegex(field.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(field.getExtField()) && field.getExtField() == 1) { + originName = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + field.getOriginName()); + } else { + originName = String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), + field.getOriginName()); + } + + if (field.getDeType() == 1) { + if (field.getDeExtractType() == 0 || field.getDeExtractType() == 5) { + whereName = String.format(OracleConstants.TO_DATE, originName, + OracleConstants.DEFAULT_DATE_FORMAT); + } + if (field.getDeExtractType() == 2 || field.getDeExtractType() == 3 + || field.getDeExtractType() == 4) { + String cast = String.format(OracleConstants.CAST, originName, + OracleConstants.DEFAULT_INT_FORMAT) + "/1000"; + whereName = String.format(OracleConstants.FROM_UNIXTIME, cast, + OracleConstants.DEFAULT_DATE_FORMAT); + } + if (field.getDeExtractType() == 1) { + whereName = originName; + } + } else if (field.getDeType() == 2 || field.getDeType() == 3) { + if (field.getDeExtractType() == 0 || field.getDeExtractType() == 5) { + whereName = String.format(OracleConstants.CAST, originName, + OracleConstants.DEFAULT_FLOAT_FORMAT); + } + if (field.getDeExtractType() == 1) { + whereName = String.format(OracleConstants.UNIX_TIMESTAMP, originName) + "*1000"; + } + if (field.getDeExtractType() == 2 || field.getDeExtractType() == 3 + || field.getDeExtractType() == 4) { + whereName = originName; + } + } else { + whereName = originName; + } + whereNameList.add(whereName); + } + + String whereName = ""; + if (request.getIsTree()) { + whereName = StringUtils.join(whereNameList, "||','||"); + } else { + whereName = whereNameList.get(0); + } + String whereTerm = transMysqlFilterTerm(request.getOperator()); + String whereValue = ""; + + if (StringUtils.containsIgnoreCase(request.getOperator(), "in")) { + whereValue = "('" + StringUtils.join(value, "','") + "')"; + } else if (StringUtils.containsIgnoreCase(request.getOperator(), "like")) { + String keyword = value.get(0).toUpperCase(); + whereValue = "'%" + keyword + "%'"; + whereName = "upper(" + whereName + ")"; + } else if (StringUtils.containsIgnoreCase(request.getOperator(), "between")) { + if (request.getDatasetTableField().getDeType() == 1) { + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String startTime = simpleDateFormat.format(new Date(Long.parseLong(value.get(0)))); + String endTime = simpleDateFormat.format(new Date(Long.parseLong(value.get(1)))); + String st = String.format(OracleConstants.TO_DATE, "'" + startTime + "'", + OracleConstants.DEFAULT_DATE_FORMAT); + String et = String.format(OracleConstants.TO_DATE, "'" + endTime + "'", + OracleConstants.DEFAULT_DATE_FORMAT); + whereValue = st + " AND " + et; + } else { + whereValue = String.format(OracleConstants.WHERE_BETWEEN, value.get(0), value.get(1)); + } + } else { + whereValue = String.format(OracleConstants.WHERE_VALUE_VALUE, value.get(0)); + } + list.add(SQLObj.builder() + .whereField(whereName) + .whereTermAndValue(whereTerm + whereValue) + .build()); + } + List strList = new ArrayList<>(); + list.forEach(ele -> strList.add(ele.getWhereField() + " " + ele.getWhereTermAndValue())); + return CollectionUtils.isNotEmpty(list) ? "(" + String.join(" AND ", strList) + ")" : null; + } + + /** + * 去除 SQL 语句的 ";" + * + * @param sql + * @return + */ + private String sqlFix(String sql) { + if (sql.lastIndexOf(";") == (sql.length() - 1)) { + sql = sql.substring(0, sql.length() - 1); + } + return sql; + } + + private String transDateFormat(String dateStyle, String datePattern) { + String split = "-"; + if (StringUtils.equalsIgnoreCase(datePattern, "date_sub")) { + split = "-"; + } else if (StringUtils.equalsIgnoreCase(datePattern, "date_split")) { + split = "/"; + } else { + split = "-"; + } + + if (StringUtils.isEmpty(dateStyle)) { + return OracleConstants.DEFAULT_DATE_FORMAT; + } + + switch (dateStyle) { + case "y": + return "YYYY"; + case "y_M": + return "YYYY" + split + "MM"; + case "y_M_d": + return "YYYY" + split + "MM" + split + "DD"; + case "H_m_s": + return "HH24:MI:SS"; + case "y_M_d_H_m": + return "YYYY" + split + "MM" + split + "DD" + " HH24:MI"; + case "y_M_d_H_m_s": + return "YYYY" + split + "MM" + split + "DD" + " HH24:MI:SS"; + default: + return OracleConstants.DEFAULT_DATE_FORMAT; + } + } + + private SQLObj getXFields(ChartViewFieldDTO x, String originField, String fieldAlias) { + String fieldName = ""; + if (x.getDeExtractType() == DeTypeConstants.DE_TIME) { + if (x.getDeType() == DeTypeConstants.DE_INT || x.getDeType() == DeTypeConstants.DE_FLOAT) { // 时间转数值 + if (x.getType().equalsIgnoreCase("DATE")) { + String date = String.format(OracleConstants.CALC_SUB, originField, + String.format(OracleConstants.TO_DATE, OracleConstants.DEFAULT_START_DATE, + OracleConstants.DEFAULT_DATE_FORMAT)); + fieldName = String.format(OracleConstants.TO_NUMBER, date) + OracleConstants.TO_MS; + } else { + String toChar = String.format(OracleConstants.TO_CHAR, originField, + OracleConstants.DEFAULT_DATE_FORMAT); + String toDate = String.format(OracleConstants.TO_DATE, toChar, OracleConstants.DEFAULT_DATE_FORMAT); + String toDate1 = String.format(OracleConstants.TO_DATE, OracleConstants.DEFAULT_START_DATE, + OracleConstants.DEFAULT_DATE_FORMAT); + fieldName = String.format(OracleConstants.TO_NUMBER, + String.format(OracleConstants.CALC_SUB, toDate, toDate1)) + OracleConstants.TO_MS; + } + } else if (x.getDeType() == DeTypeConstants.DE_TIME) { // 格式化显示时间 + String format = transDateFormat(x.getDateStyle(), x.getDatePattern()); + if (x.getType().equalsIgnoreCase("DATE")) { + fieldName = String.format(OracleConstants.TO_CHAR, originField, format); + } else { + String toChar = String.format(OracleConstants.TO_CHAR, originField, + OracleConstants.DEFAULT_DATE_FORMAT); + String toDate = String.format(OracleConstants.TO_DATE, toChar, OracleConstants.DEFAULT_DATE_FORMAT); + fieldName = String.format(OracleConstants.TO_CHAR, toDate, format); + } + } else { + fieldName = originField; + } + } else { + if (x.getDeType() == DeTypeConstants.DE_TIME) { + String format = transDateFormat(x.getDateStyle(), x.getDatePattern()); + if (x.getDeExtractType() == DeTypeConstants.DE_STRING) { // 字符串转时间 + String toDate = String.format(OracleConstants.TO_DATE, originField, + OracleConstants.DEFAULT_DATE_FORMAT); + fieldName = String.format(OracleConstants.TO_CHAR, toDate, format); + } else { // 数值转时间 + String date = originField + "/(1000 * 60 * 60 * 24)+" + String.format(OracleConstants.TO_DATE, + OracleConstants.DEFAULT_START_DATE, OracleConstants.DEFAULT_DATE_FORMAT); + fieldName = String.format(OracleConstants.TO_CHAR, date, format); + } + } else { + if (x.getDeType() == DeTypeConstants.DE_INT) { + fieldName = String.format(OracleConstants.CAST, originField, OracleConstants.DEFAULT_INT_FORMAT); + } else if (x.getDeType() == DeTypeConstants.DE_FLOAT) { + fieldName = String.format(OracleConstants.CAST, originField, OracleConstants.DEFAULT_FLOAT_FORMAT); + } else { + fieldName = originField; + } + } + } + return SQLObj.builder() + .fieldName(fieldName) + .fieldAlias(fieldAlias) + .build(); + } + + private List getXWheres(ChartViewFieldDTO x, String originField, String fieldAlias) { + List list = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(x.getFilter()) && x.getFilter().size() > 0) { + x.getFilter().forEach(f -> { + String whereName = ""; + String whereTerm = transMysqlFilterTerm(f.getTerm()); + String whereValue = ""; + if (x.getDeType() == 1 && x.getDeExtractType() != 1) { + String cast = String.format(OracleConstants.CAST, originField, OracleConstants.DEFAULT_INT_FORMAT) + + "/1000"; + whereName = String.format(OracleConstants.FROM_UNIXTIME, cast, OracleConstants.DEFAULT_DATE_FORMAT); + } else { + whereName = originField; + } + if (StringUtils.equalsIgnoreCase(f.getTerm(), "null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(f.getTerm(), "not_null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(f.getTerm(), "empty")) { + whereValue = "''"; + } else if (StringUtils.equalsIgnoreCase(f.getTerm(), "not_empty")) { + whereValue = "''"; + } else if (StringUtils.containsIgnoreCase(f.getTerm(), "in")) { + whereValue = "('" + StringUtils.join(f.getValue(), "','") + "')"; + } else if (StringUtils.containsIgnoreCase(f.getTerm(), "like")) { + whereValue = "'%" + f.getValue() + "%'"; + } else { + whereValue = String.format(OracleConstants.WHERE_VALUE_VALUE, f.getValue()); + } + list.add(SQLObj.builder() + .whereField(whereName) + .whereAlias(fieldAlias) + .whereTermAndValue(whereTerm + whereValue) + .build()); + }); + } + return list; + } + + private SQLObj getYFields(ChartViewFieldDTO y, String originField, String fieldAlias) { + String fieldName = ""; + if (StringUtils.equalsIgnoreCase(y.getOriginName(), "*")) { + fieldName = OracleConstants.AGG_COUNT; + } else if (SQLConstants.DIMENSION_TYPE.contains(y.getDeType())) { + if (StringUtils.equalsIgnoreCase(y.getSummary(), "count_distinct")) { + fieldName = String.format(OracleConstants.AGG_FIELD, "COUNT", "DISTINCT " + originField); + } else if (StringUtils.equalsIgnoreCase(y.getSummary(), "group_concat")) { + fieldName = String.format(OracleConstants.GROUP_CONCAT, originField, originField); + } else { + fieldName = String.format(OracleConstants.AGG_FIELD, y.getSummary(), originField); + } + } else { + if (StringUtils.equalsIgnoreCase(y.getSummary(), "avg") + || StringUtils.containsIgnoreCase(y.getSummary(), "pop")) { + String cast = String.format(OracleConstants.CAST, originField, + y.getDeType() == 2 ? OracleConstants.DEFAULT_INT_FORMAT : OracleConstants.DEFAULT_FLOAT_FORMAT); + String agg = String.format(OracleConstants.AGG_FIELD, y.getSummary(), cast); + fieldName = String.format(OracleConstants.CAST, agg, OracleConstants.DEFAULT_FLOAT_FORMAT); + } else { + String cast = String.format(OracleConstants.CAST, originField, + y.getDeType() == 2 ? OracleConstants.DEFAULT_INT_FORMAT : OracleConstants.DEFAULT_FLOAT_FORMAT); + if (StringUtils.equalsIgnoreCase(y.getSummary(), "count_distinct")) { + fieldName = String.format(OracleConstants.AGG_FIELD, "COUNT", "DISTINCT " + cast); + } else { + fieldName = String.format(OracleConstants.AGG_FIELD, y.getSummary(), cast); + } + } + } + return SQLObj.builder() + .fieldName(fieldName) + .fieldAlias(fieldAlias) + .build(); + } + + private String getYWheres(ChartViewFieldDTO y, String originField, String fieldAlias) { + List list = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(y.getFilter()) && y.getFilter().size() > 0) { + y.getFilter().forEach(f -> { + String whereTerm = transMysqlFilterTerm(f.getTerm()); + String whereValue = ""; + // 原始类型不是时间,在de中被转成时间的字段做处理 + if (StringUtils.equalsIgnoreCase(f.getTerm(), "null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(f.getTerm(), "not_null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(f.getTerm(), "empty")) { + whereValue = "''"; + } else if (StringUtils.equalsIgnoreCase(f.getTerm(), "not_empty")) { + whereValue = "''"; + } else if (StringUtils.containsIgnoreCase(f.getTerm(), "in")) { + whereValue = "('" + StringUtils.join(f.getValue(), "','") + "')"; + } else if (StringUtils.containsIgnoreCase(f.getTerm(), "like")) { + whereValue = "'%" + f.getValue() + "%'"; + } else { + whereValue = String.format(OracleConstants.WHERE_VALUE_VALUE, f.getValue()); + } + list.add(SQLObj.builder() + .whereField(fieldAlias) + .whereAlias(fieldAlias) + .whereTermAndValue(whereTerm + whereValue) + .build()); + }); + } + List strList = new ArrayList<>(); + list.forEach(ele -> strList.add(ele.getWhereField() + " " + ele.getWhereTermAndValue())); + return CollectionUtils.isNotEmpty(list) ? "(" + String.join(" " + getLogic(y.getLogic()) + " ", strList) + ")" + : null; + } + + private String calcFieldRegex(String originField, SQLObj tableObj) { + originField = originField.replaceAll("[\\t\\n\\r]]", ""); + // 正则提取[xxx] + String regex = "\\[(.*?)]"; + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(originField); + Set ids = new HashSet<>(); + while (matcher.find()) { + String id = matcher.group(1); + ids.add(id); + } + if (CollectionUtils.isEmpty(ids)) { + return originField; + } + DatasetTableFieldExample datasetTableFieldExample = new DatasetTableFieldExample(); + datasetTableFieldExample.createCriteria().andIdIn(new ArrayList<>(ids)); + List calcFields = datasetTableFieldMapper.selectByExample(datasetTableFieldExample); + for (DatasetTableField ele : calcFields) { + originField = originField.replaceAll("\\[" + ele.getId() + "]", + String.format(OracleConstants.KEYWORD_FIX, tableObj.getTableAlias(), ele.getOriginName())); + } + return originField; + } + + /** + * 数据条数限制 + * + * @param sql + * @param view + * @return + */ + private String sqlLimit(String sql, ChartViewWithBLOBs view) { + if (StringUtils.equalsIgnoreCase(view.getResultMode(), "custom")) { + return "SELECT * FROM (" + sqlFix(sql) + ") DE_RESULT_TMP " + " WHERE rownum <= " + view.getResultCount(); + } else { + return sql; + } + } + + public List dateformat() { + ObjectMapper objectMapper = new ObjectMapper(); + List dateformats = new ArrayList<>(); + try { + dateformats = objectMapper.readValue("[\n" + + "{\"dateformat\": \"YYYY-MM-DD\"},\n" + + "{\"dateformat\": \"YYYY/MM/DD\"},\n" + + "{\"dateformat\": \"YYYYMMDD\"},\n" + + "{\"dateformat\": \"YYYY-MM-DD HH24:MI:SS\"},\n" + + "{\"dateformat\": \"YYYY/MM/DD HH24:MI:SS\"},\n" + + "{\"dateformat\": \"YYYYMMDD HH24:MI:SS\"}\n" + + "]", new TypeReference>() { + }); + } catch (Exception e) { + } + return dateformats; + } +} diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-backend/src/main/java/io/dataease/plugins/datasource/kingbase/service/KingbaseService.java b/extensions/dataease-extensions-datasource/kingbase/kingbase-backend/src/main/java/io/dataease/plugins/datasource/kingbase/service/KingbaseService.java new file mode 100644 index 0000000000..e5f546a68e --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-backend/src/main/java/io/dataease/plugins/datasource/kingbase/service/KingbaseService.java @@ -0,0 +1,71 @@ +package io.dataease.plugins.datasource.kingbase.service; + +import io.dataease.plugins.common.constants.DatabaseClassification; +import io.dataease.plugins.common.constants.DatasourceCalculationMode; +import io.dataease.plugins.common.dto.StaticResource; +import io.dataease.plugins.common.dto.datasource.DataSourceType; +import io.dataease.plugins.datasource.service.DatasourceService; +import org.springframework.stereotype.Service; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +@Service +public class KingbaseService extends DatasourceService { + + + /** + * 添加数据源类型 + */ + @Override + public List components() { + List result = new ArrayList<>(); + result.add("kingbase"); + return result; + } + + /** + * 读取静态资源 + */ + @Override + protected InputStream readContent(String s) { + return this.getClass().getClassLoader().getResourceAsStream("static/" + s); + } + + /** + * 映射 Logo 资源 + */ + @Override + public List staticResources() { + List results = new ArrayList<>(); + StaticResource staticResource = new StaticResource(); + staticResource.setName("kingbase"); + staticResource.setSuffix("jpg"); + results.add(staticResource); + results.add(pluginSvg()); + return results; + } + + /** + * 用户填写的数据源信息 + */ + @Override + public DataSourceType getDataSourceType() { + DataSourceType dataSourceType = new DataSourceType("kingbase", "KingBase", true, "", + DatasourceCalculationMode.DIRECT_AND_SYNC, true); + dataSourceType.setKeywordPrefix("\""); + dataSourceType.setKeywordSuffix("\""); + dataSourceType.setAliasPrefix("\""); + dataSourceType.setAliasSuffix("\""); + dataSourceType.setDatabaseClassification(DatabaseClassification.OLTP); + return dataSourceType; + } + + private StaticResource pluginSvg() { + StaticResource staticResource = new StaticResource(); + staticResource.setName("kingbase-backend"); + staticResource.setSuffix("svg"); + return staticResource; + } +} diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/.babelrc b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/.babelrc new file mode 100644 index 0000000000..3a280ba34b --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/.babelrc @@ -0,0 +1,12 @@ +{ + "presets": [ + ["env", { + "modules": false, + "targets": { + "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] + } + }], + "stage-2" + ], + "plugins": ["transform-vue-jsx", "transform-runtime"] +} diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/.gitignore b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/.gitignore new file mode 100644 index 0000000000..541a820f6c --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/.gitignore @@ -0,0 +1,14 @@ +.DS_Store +node_modules/ +/dist/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/README.md b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/README.md new file mode 100644 index 0000000000..c1e4be95e0 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/README.md @@ -0,0 +1,21 @@ +# deplugin-view-frontend + +> A Vue.js project + +## Build Setup + +``` bash +# install dependencies +npm install + +# serve with hot reload at localhost:8080 +npm run dev + +# build for production with minification +npm run build + +# build for production and view the bundle analyzer report +npm run build --report +``` + +For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader). diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/build/build-async-plugins.js b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/build/build-async-plugins.js new file mode 100644 index 0000000000..2303854531 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/build/build-async-plugins.js @@ -0,0 +1,35 @@ +'use strict' +require('./check-versions')() + +process.env.NODE_ENV = 'production' + +const ora = require('ora') +const chalk = require('chalk') +const webpack = require('webpack') +const webpackConfig = require('./webpack.async-plugins') + +const spinner = ora('building for sync-plugins...') +spinner.start() + +webpack(webpackConfig, function (err, stats) { + spinner.stop() + if (err) throw err + process.stdout.write(stats.toString({ + colors: true, + modules: false, + children: false, + chunks: false, + chunkModules: false + }) + '\n\n') + + if (stats.hasErrors()) { + console.log(chalk.red(' Build failed with errors.\n')) + process.exit(1) + } + + console.log(chalk.cyan(' Build complete.\n')) + console.log(chalk.yellow( + ' Tip: built files are meant to be served over an HTTP server.\n' + + ' Opening index.html over file:// won\'t work.\n' + )) +}) diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/build/build.js b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/build/build.js new file mode 100644 index 0000000000..8f2ad8ad49 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/build/build.js @@ -0,0 +1,41 @@ +'use strict' +require('./check-versions')() + +process.env.NODE_ENV = 'production' + +const ora = require('ora') +const rm = require('rimraf') +const path = require('path') +const chalk = require('chalk') +const webpack = require('webpack') +const config = require('../config') +const webpackConfig = require('./webpack.prod.conf') + +const spinner = ora('building for production...') +spinner.start() + +rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { + if (err) throw err + webpack(webpackConfig, (err, stats) => { + spinner.stop() + if (err) throw err + process.stdout.write(stats.toString({ + colors: true, + modules: false, + children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build. + chunks: false, + chunkModules: false + }) + '\n\n') + + if (stats.hasErrors()) { + console.log(chalk.red(' Build failed with errors.\n')) + process.exit(1) + } + + console.log(chalk.cyan(' Build complete.\n')) + console.log(chalk.yellow( + ' Tip: built files are meant to be served over an HTTP server.\n' + + ' Opening index.html over file:// won\'t work.\n' + )) + }) +}) diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/build/check-versions.js b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/build/check-versions.js new file mode 100644 index 0000000000..3ef972a08d --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/build/check-versions.js @@ -0,0 +1,54 @@ +'use strict' +const chalk = require('chalk') +const semver = require('semver') +const packageConfig = require('../package.json') +const shell = require('shelljs') + +function exec (cmd) { + return require('child_process').execSync(cmd).toString().trim() +} + +const versionRequirements = [ + { + name: 'node', + currentVersion: semver.clean(process.version), + versionRequirement: packageConfig.engines.node + } +] + +if (shell.which('npm')) { + versionRequirements.push({ + name: 'npm', + currentVersion: exec('npm --version'), + versionRequirement: packageConfig.engines.npm + }) +} + +module.exports = function () { + const warnings = [] + + for (let i = 0; i < versionRequirements.length; i++) { + const mod = versionRequirements[i] + + if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { + warnings.push(mod.name + ': ' + + chalk.red(mod.currentVersion) + ' should be ' + + chalk.green(mod.versionRequirement) + ) + } + } + + if (warnings.length) { + console.log('') + console.log(chalk.yellow('To use this template, you must update following to modules:')) + console.log() + + for (let i = 0; i < warnings.length; i++) { + const warning = warnings[i] + console.log(' ' + warning) + } + + console.log() + process.exit(1) + } +} diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/build/logo.png b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/build/logo.png new file mode 100644 index 0000000000..f3d2503fc2 Binary files /dev/null and b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/build/logo.png differ diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/build/utils.js b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/build/utils.js new file mode 100644 index 0000000000..e534fb0fd6 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/build/utils.js @@ -0,0 +1,101 @@ +'use strict' +const path = require('path') +const config = require('../config') +const ExtractTextPlugin = require('extract-text-webpack-plugin') +const packageConfig = require('../package.json') + +exports.assetsPath = function (_path) { + const assetsSubDirectory = process.env.NODE_ENV === 'production' + ? config.build.assetsSubDirectory + : config.dev.assetsSubDirectory + + return path.posix.join(assetsSubDirectory, _path) +} + +exports.cssLoaders = function (options) { + options = options || {} + + const cssLoader = { + loader: 'css-loader', + options: { + sourceMap: options.sourceMap + } + } + + const postcssLoader = { + loader: 'postcss-loader', + options: { + sourceMap: options.sourceMap + } + } + + // generate loader string to be used with extract text plugin + function generateLoaders (loader, loaderOptions) { + const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader] + + if (loader) { + loaders.push({ + loader: loader + '-loader', + options: Object.assign({}, loaderOptions, { + sourceMap: options.sourceMap + }) + }) + } + + // Extract CSS when that option is specified + // (which is the case during production build) + if (options.extract) { + return ExtractTextPlugin.extract({ + use: loaders, + fallback: 'vue-style-loader' + }) + } else { + return ['vue-style-loader'].concat(loaders) + } + } + + // https://vue-loader.vuejs.org/en/configurations/extract-css.html + return { + css: generateLoaders(), + postcss: generateLoaders(), + less: generateLoaders('less'), + sass: generateLoaders('sass', { indentedSyntax: true }), + scss: generateLoaders('sass'), + stylus: generateLoaders('stylus'), + styl: generateLoaders('stylus') + } +} + +// Generate loaders for standalone style files (outside of .vue) +exports.styleLoaders = function (options) { + const output = [] + const loaders = exports.cssLoaders(options) + + for (const extension in loaders) { + const loader = loaders[extension] + output.push({ + test: new RegExp('\\.' + extension + '$'), + use: loader + }) + } + + return output +} + +exports.createNotifierCallback = () => { + const notifier = require('node-notifier') + + return (severity, errors) => { + if (severity !== 'error') return + + const error = errors[0] + const filename = error.file && error.file.split('!').pop() + + notifier.notify({ + title: packageConfig.name, + message: severity + ': ' + error.name, + subtitle: filename || '', + icon: path.join(__dirname, 'logo.png') + }) + } +} diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/build/vue-loader.conf.js b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/build/vue-loader.conf.js new file mode 100644 index 0000000000..33ed58bc0a --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/build/vue-loader.conf.js @@ -0,0 +1,22 @@ +'use strict' +const utils = require('./utils') +const config = require('../config') +const isProduction = process.env.NODE_ENV === 'production' +const sourceMapEnabled = isProduction + ? config.build.productionSourceMap + : config.dev.cssSourceMap + +module.exports = { + loaders: utils.cssLoaders({ + sourceMap: sourceMapEnabled, + extract: isProduction + }), + cssSourceMap: sourceMapEnabled, + cacheBusting: config.dev.cacheBusting, + transformToRequire: { + video: ['src', 'poster'], + source: 'src', + img: 'src', + image: 'xlink:href' + } +} diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/build/webpack.async-plugins.js b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/build/webpack.async-plugins.js new file mode 100644 index 0000000000..a037511cbe --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/build/webpack.async-plugins.js @@ -0,0 +1,91 @@ +const webpack = require('webpack') +const path = require('path') +const utils = require('./utils') +const CopyPlugin = require("copy-webpack-plugin"); +const VueLoaderPlugin = require('vue-loader/lib/plugin'); +function resolve (dir) { + return path.join(__dirname, '..', dir) +} + +module.exports = { + mode: 'development', + entry: { + 'kingbase': resolve('/src/views/kingbase.vue') + }, + output: { + path: resolve('/static/'), + filename: '[name].js' + }, + resolve: { + extensions: ['.js', '.vue', '.json'], + alias: { + 'vue$': 'vue/dist/vue.esm.js', + '@': resolve('src') + } + }, + externals: { + vue: 'vue' + }, + module: { + rules: [ + { + test: /\.vue$/, + loader: 'vue-loader', + options: { + transformAssetUrls: { + video: 'src', + source: 'src', + img: 'src', + image: 'xlink:href' + } + } + }, + { + test: /.(sa|sc|c)ss$/, + use: [ + {loader: 'vue-style-loader'}, + 'css-loader', + 'sass-loader' + ] + }, + { + test: /\.js$/, + loader: 'babel-loader', + include: [resolve('src'), resolve('test')] + }, + { + test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('img/[name].[hash:7].[ext]') + } + }, + { + test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('media/[name].[hash:7].[ext]') + } + }, + { + test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('fonts/[name].[hash:7].[ext]') + } + } + ] + }, + plugins: [ + new VueLoaderPlugin(), + new webpack.DefinePlugin({ + 'process.env.NODE_ENV': '"production"' + }), + new CopyPlugin([ + {from: 'src/icons/svg/'} + ]), + ] +} diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/build/webpack.base.conf.js b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/build/webpack.base.conf.js new file mode 100644 index 0000000000..7ef785a39e --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/build/webpack.base.conf.js @@ -0,0 +1,103 @@ +'use strict' +const path = require('path') +const utils = require('./utils') +const config = require('../config') +const vueLoaderConfig = require('./vue-loader.conf') + +function resolve (dir) { + return path.join(__dirname, '..', dir) +} + + + +module.exports = { + context: path.resolve(__dirname, '../'), + entry: { + app: './src/main.js' + }, + output: { + path: config.build.assetsRoot, + filename: '[name].js', + publicPath: process.env.NODE_ENV === 'production' + ? config.build.assetsPublicPath + : config.dev.assetsPublicPath + }, + resolve: { + extensions: ['.js', '.vue', '.json'], + alias: { + 'vue$': 'vue/dist/vue.esm.js', + '@': resolve('src'), + } + }, + module: { + rules: [ + { + test: /\.vue$/, + loader: 'vue-loader', + options: vueLoaderConfig + }, + { + test: /\.js$/, + loader: 'babel-loader', + include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')] + }, + { + test: /\.svg$/, + loader: 'svg-sprite-loader', + include: [resolve('src/icons')], + options: { + symbolId: 'icon-[name]' + } + }, + { + test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, + loader: 'url-loader', + exclude: [resolve('src/icons')], + options: { + limit: 10000, + name: utils.assetsPath('img/[name].[hash:7].[ext]') + } + }, + { + test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('media/[name].[hash:7].[ext]') + } + }, + { + test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('fonts/[name].[hash:7].[ext]') + } + }, + { + test: /\.svg$/, + include: [path.resolve('src/icons')], + use: [ + { + loader: 'svg-sprite-loader', + options: { + symbolId: 'icon-[name]', + }, + } + ], + } + ] + }, + node: { + // prevent webpack from injecting useless setImmediate polyfill because Vue + // source contains it (although only uses it if it's native). + setImmediate: false, + // prevent webpack from injecting mocks to Node native modules + // that does not make sense for the client + dgram: 'empty', + fs: 'empty', + net: 'empty', + tls: 'empty', + child_process: 'empty' + } +} diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/build/webpack.dev.conf.js b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/build/webpack.dev.conf.js new file mode 100755 index 0000000000..070ae221f3 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/build/webpack.dev.conf.js @@ -0,0 +1,95 @@ +'use strict' +const utils = require('./utils') +const webpack = require('webpack') +const config = require('../config') +const merge = require('webpack-merge') +const path = require('path') +const baseWebpackConfig = require('./webpack.base.conf') +const CopyWebpackPlugin = require('copy-webpack-plugin') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') +const portfinder = require('portfinder') + +const HOST = process.env.HOST +const PORT = process.env.PORT && Number(process.env.PORT) + +const devWebpackConfig = merge(baseWebpackConfig, { + module: { + rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) + }, + // cheap-module-eval-source-map is faster for development + devtool: config.dev.devtool, + + // these devServer options should be customized in /config/index.js + devServer: { + clientLogLevel: 'warning', + historyApiFallback: { + rewrites: [ + { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') }, + ], + }, + hot: true, + contentBase: false, // since we use CopyWebpackPlugin. + compress: true, + host: HOST || config.dev.host, + port: PORT || config.dev.port, + open: config.dev.autoOpenBrowser, + overlay: config.dev.errorOverlay + ? { warnings: false, errors: true } + : false, + publicPath: config.dev.assetsPublicPath, + proxy: config.dev.proxyTable, + quiet: true, // necessary for FriendlyErrorsPlugin + watchOptions: { + poll: config.dev.poll, + } + }, + plugins: [ + new webpack.DefinePlugin({ + 'process.env': require('../config/dev.env') + }), + new webpack.HotModuleReplacementPlugin(), + new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update. + new webpack.NoEmitOnErrorsPlugin(), + // https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: 'index.html', + template: 'index.html', + inject: true + }), + // copy custom static assets + new CopyWebpackPlugin([ + { + from: path.resolve(__dirname, '../static'), + to: config.dev.assetsSubDirectory, + ignore: ['.*'] + } + ]) + ] +}) + +module.exports = new Promise((resolve, reject) => { + portfinder.basePort = process.env.PORT || config.dev.port + portfinder.getPort((err, port) => { + if (err) { + reject(err) + } else { + // publish the new Port, necessary for e2e tests + process.env.PORT = port + // add port to devServer config + devWebpackConfig.devServer.port = port + + // Add FriendlyErrorsPlugin + devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({ + compilationSuccessInfo: { + messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`], + }, + onErrors: config.dev.notifyOnErrors + ? utils.createNotifierCallback() + : undefined + })) + + resolve(devWebpackConfig) + } + }) +}) diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/build/webpack.prod.conf.js b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/build/webpack.prod.conf.js new file mode 100644 index 0000000000..3e25238ef6 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/build/webpack.prod.conf.js @@ -0,0 +1,145 @@ +'use strict' +const path = require('path') +const utils = require('./utils') +const webpack = require('webpack') +const config = require('../config') +const merge = require('webpack-merge') +const baseWebpackConfig = require('./webpack.base.conf') +const CopyWebpackPlugin = require('copy-webpack-plugin') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const ExtractTextPlugin = require('extract-text-webpack-plugin') +const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') +const UglifyJsPlugin = require('uglifyjs-webpack-plugin') + +const env = require('../config/prod.env') + +const webpackConfig = merge(baseWebpackConfig, { + module: { + rules: utils.styleLoaders({ + sourceMap: config.build.productionSourceMap, + extract: true, + usePostCSS: true + }) + }, + devtool: config.build.productionSourceMap ? config.build.devtool : false, + output: { + path: config.build.assetsRoot, + filename: utils.assetsPath('js/[name].[chunkhash].js'), + chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') + }, + plugins: [ + // http://vuejs.github.io/vue-loader/en/workflow/production.html + new webpack.DefinePlugin({ + 'process.env': env + }), + new UglifyJsPlugin({ + uglifyOptions: { + compress: { + warnings: false + } + }, + sourceMap: config.build.productionSourceMap, + parallel: true + }), + // extract css into its own file + new ExtractTextPlugin({ + filename: utils.assetsPath('css/[name].[contenthash].css'), + // Setting the following option to `false` will not extract CSS from codesplit chunks. + // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack. + // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, + // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110 + allChunks: true, + }), + // Compress extracted CSS. We are using this plugin so that possible + // duplicated CSS from different components can be deduped. + new OptimizeCSSPlugin({ + cssProcessorOptions: config.build.productionSourceMap + ? { safe: true, map: { inline: false } } + : { safe: true } + }), + // generate dist index.html with correct asset hash for caching. + // you can customize output by editing /index.html + // see https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: config.build.index, + template: 'index.html', + inject: true, + minify: { + removeComments: true, + collapseWhitespace: true, + removeAttributeQuotes: true + // more options: + // https://github.com/kangax/html-minifier#options-quick-reference + }, + // necessary to consistently work with multiple chunks via CommonsChunkPlugin + chunksSortMode: 'dependency' + }), + // keep module.id stable when vendor modules does not change + new webpack.HashedModuleIdsPlugin(), + // enable scope hoisting + new webpack.optimize.ModuleConcatenationPlugin(), + // split vendor js into its own file + new webpack.optimize.CommonsChunkPlugin({ + name: 'vendor', + minChunks (module) { + // any required modules inside node_modules are extracted to vendor + return ( + module.resource && + /\.js$/.test(module.resource) && + module.resource.indexOf( + path.join(__dirname, '../node_modules') + ) === 0 + ) + } + }), + // extract webpack runtime and module manifest to its own file in order to + // prevent vendor hash from being updated whenever app bundle is updated + new webpack.optimize.CommonsChunkPlugin({ + name: 'manifest', + minChunks: Infinity + }), + // This instance extracts shared chunks from code split chunks and bundles them + // in a separate chunk, similar to the vendor chunk + // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk + new webpack.optimize.CommonsChunkPlugin({ + name: 'app', + async: 'vendor-async', + children: true, + minChunks: 3 + }), + + // copy custom static assets + new CopyWebpackPlugin([ + { + from: path.resolve(__dirname, '../static'), + to: config.build.assetsSubDirectory, + ignore: ['.*'] + } + ]) + ] +}) + +if (config.build.productionGzip) { + const CompressionWebpackPlugin = require('compression-webpack-plugin') + + webpackConfig.plugins.push( + new CompressionWebpackPlugin({ + asset: '[path].gz[query]', + algorithm: 'gzip', + test: new RegExp( + '\\.(' + + config.build.productionGzipExtensions.join('|') + + ')$' + ), + threshold: 10240, + minRatio: 0.8 + }) + ) +} + +if (config.build.bundleAnalyzerReport) { + const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin + webpackConfig.plugins.push(new BundleAnalyzerPlugin()) +} + +module.exports = webpackConfig diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/config/dev.env.js b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/config/dev.env.js new file mode 100644 index 0000000000..1e22973ae7 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/config/dev.env.js @@ -0,0 +1,7 @@ +'use strict' +const merge = require('webpack-merge') +const prodEnv = require('./prod.env') + +module.exports = merge(prodEnv, { + NODE_ENV: '"development"' +}) diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/config/index.js b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/config/index.js new file mode 100644 index 0000000000..c5eded7f81 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/config/index.js @@ -0,0 +1,69 @@ +'use strict' +// Template version: 1.3.1 +// see http://vuejs-templates.github.io/webpack for documentation. + +const path = require('path') + +module.exports = { + dev: { + + // Paths + assetsSubDirectory: 'static', + assetsPublicPath: '/', + proxyTable: {}, + + // Various Dev Server settings + host: 'localhost', // can be overwritten by process.env.HOST + port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined + autoOpenBrowser: false, + errorOverlay: true, + notifyOnErrors: true, + poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- + + + /** + * Source Maps + */ + + // https://webpack.js.org/configuration/devtool/#development + devtool: 'cheap-module-eval-source-map', + + // If you have problems debugging vue-files in devtools, + // set this to false - it *may* help + // https://vue-loader.vuejs.org/en/options.html#cachebusting + cacheBusting: true, + + cssSourceMap: true + }, + + build: { + // Template for index.html + index: path.resolve(__dirname, '../dist/index.html'), + + // Paths + assetsRoot: path.resolve(__dirname, '../dist'), + assetsSubDirectory: 'static', + assetsPublicPath: '/', + + /** + * Source Maps + */ + + productionSourceMap: true, + // https://webpack.js.org/configuration/devtool/#production + devtool: '#source-map', + + // Gzip off by default as many popular static hosts such as + // Surge or Netlify already gzip all static assets for you. + // Before setting to `true`, make sure to: + // npm install --save-dev compression-webpack-plugin + productionGzip: false, + productionGzipExtensions: ['js', 'css'], + + // Run the build command with an extra argument to + // View the bundle analyzer report after build finishes: + // `npm run build --report` + // Set to `true` or `false` to always turn it on or off + bundleAnalyzerReport: process.env.npm_config_report + } +} diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/config/prod.env.js b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/config/prod.env.js new file mode 100644 index 0000000000..a6f997616e --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/config/prod.env.js @@ -0,0 +1,4 @@ +'use strict' +module.exports = { + NODE_ENV: '"production"' +} diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/index.html b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/index.html new file mode 100644 index 0000000000..03ce3fdf27 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/index.html @@ -0,0 +1,12 @@ + + + + + + deplugin-view-frontend + + +
+ + + diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/package.json b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/package.json new file mode 100644 index 0000000000..64a3ce7ad6 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/package.json @@ -0,0 +1,75 @@ +{ + "name": "deplugin-datasource-frontend", + "version": "1.0.0", + "description": "A Vue.js project", + "author": "", + "private": true, + "scripts": { + "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", + "start": "npm run dev", + "build": "node build/build.js", + "buildPlugin": "node build/build-async-plugins.js" + }, + "dependencies": { + "@riophae/vue-treeselect": "0.4.0", + "highcharts": "^10.0.0", + "svg-sprite-loader": "^6.0.11", + "svgo": "1.2.2", + "svgo-loader": "^3.0.1", + "vue": "^2.5.2", + "vue-i18n": "7.3.2", + "vue-router": "^3.0.1", + "vue-uuid": "2.0.2", + "vuedraggable": "^2.24.3" + }, + "devDependencies": { + "autoprefixer": "^7.1.2", + "babel-core": "^6.22.1", + "babel-helper-vue-jsx-merge-props": "^2.0.3", + "babel-loader": "^7.1.1", + "babel-plugin-syntax-jsx": "^6.18.0", + "babel-plugin-transform-runtime": "^6.22.0", + "babel-plugin-transform-vue-jsx": "^3.5.0", + "babel-preset-env": "^1.3.2", + "babel-preset-stage-2": "^6.22.0", + "chalk": "^2.0.1", + "copy-webpack-plugin": "^4.6.0", + "css-loader": "^0.28.0", + "element-ui": "2.15.7", + "extract-text-webpack-plugin": "^4.0.0-beta.0", + "file-loader": "^1.1.4", + "friendly-errors-webpack-plugin": "^1.6.1", + "html-webpack-plugin": "^3.2.0", + "js-cookie": "2.2.0", + "node-notifier": "^8.0.1", + "optimize-css-assets-webpack-plugin": "^3.2.0", + "ora": "^1.2.0", + "portfinder": "^1.0.13", + "postcss-import": "^11.0.0", + "postcss-loader": "^2.0.8", + "postcss-url": "^7.2.1", + "rimraf": "^2.6.0", + "sass": "^1.33.0", + "sass-loader": "^7.3.1", + "semver": "^5.3.0", + "shelljs": "^0.8.5", + "uglifyjs-webpack-plugin": "^1.1.1", + "url-loader": "^0.5.8", + "vue-loader": "^15.6.4", + "vue-style-loader": "^4.1.2", + "vue-template-compiler": "^2.5.2", + "webpack": "^4.8.1", + "webpack-bundle-analyzer": "^3.3.2", + "webpack-dev-server": "^3.1.11", + "webpack-merge": "^4.1.0" + }, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + }, + "browserslist": [ + "> 1%", + "last 2 versions", + "not ie <= 8" + ] +} diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/pom.xml b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/pom.xml new file mode 100644 index 0000000000..899a5b0d25 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/pom.xml @@ -0,0 +1,80 @@ + + + + + io.dataease + kingbase + ${dataease.version} + + + 4.0.0 + kingbase-frontend + + + UTF-8 + UTF-8 + 1.9.1 + + + + + + maven-clean-plugin + + + + static + + ** + + false + + + + + + + + com.github.eirslett + frontend-maven-plugin + ${frontend-maven-plugin.version} + + + install node and npm + + install-node-and-npm + + + + v16.20.2 + 7.6.3 + + + + + npm install + + npm + + + + install --force + + + + + npm run buildPlugin + + npm + + + run buildPlugin + + + + + + + diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/postcss.config.js b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/postcss.config.js new file mode 100644 index 0000000000..ccd0905482 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/postcss.config.js @@ -0,0 +1,5 @@ +module.exports = { + plugins: { + 'autoprefixer': { browsers: 'last 5 version' } + } +} \ No newline at end of file diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/App.vue b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/App.vue new file mode 100644 index 0000000000..8542e07722 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/App.vue @@ -0,0 +1,23 @@ + + + + + diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/assets/logo.png b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/assets/logo.png new file mode 100644 index 0000000000..f3d2503fc2 Binary files /dev/null and b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/assets/logo.png differ diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/components/HelloWorld.vue b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/components/HelloWorld.vue new file mode 100644 index 0000000000..1c19f2a399 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/components/HelloWorld.vue @@ -0,0 +1,113 @@ + + + + + + diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/de-base/lang/en.js b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/de-base/lang/en.js new file mode 100644 index 0000000000..e5084e7ed1 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/de-base/lang/en.js @@ -0,0 +1,31 @@ +export default { + host: 'Host name/IP address', + dataBase: 'Database name', + connection_mode: 'Connection mode', + oracle_sid: 'SID', + oracle_service_name: 'Service name', + username: 'User name', + password: 'Password', + port: 'Port', + schema: 'Schema', + get_schema: 'Get Schema', + charset: 'Character set', + targetCharset: 'Target character set', + priority: 'Advanced settings', + initial_pool_size: 'Initial connections', + min_pool_size: 'Minimum connections', + max_pool_size: 'Maximum connections', + query_timeout: 'Query timeout', + one_user_name: 'Please enter the user name', + input_a_password: 'Please enter the password', + enter_the_port: 'Please enter the port', + please_choose_schema: 'Please select Schema', + please_choose_charset: 'Please select the database character set', + please_choose_targetCharset: 'Please select the target character set', + please_input_initial_pool_size: 'Please enter the initial number of connections', + please_input_min_pool_size: 'Please enter the minimum number of connections', + please_input_max_pool_size: 'Please enter the maximum number of connections', + please_input_query_timeout: 'Please enter the query timeout', + second: 'Second', + please_select: 'Please select' +} diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/de-base/lang/index.js b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/de-base/lang/index.js new file mode 100644 index 0000000000..e50d0478e6 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/de-base/lang/index.js @@ -0,0 +1,49 @@ +import Vue from 'vue' +import VueI18n from 'vue-i18n' +import Cookies from 'js-cookie' +import elementEnLocale from 'element-ui/lib/locale/lang/en' // element-ui lang +import elementZhLocale from 'element-ui/lib/locale/lang/zh-CN'// element-ui lang +import elementTWLocale from 'element-ui/lib/locale/lang/zh-TW'// element-ui lang + +import localMessages from './messages' + + +Vue.use(VueI18n) + +const messages = { + en_US: { + ...localMessages['en_US'], + ...elementEnLocale + }, + zh_CN: { + ...localMessages['zh_CN'], + ...elementZhLocale + }, + zh_TW: { + ...localMessages['zh_TW'], + ...elementTWLocale + } +} +export function getLanguage () { + const chooseLanguage = Cookies.get('language') + if (chooseLanguage) return chooseLanguage + + // if has not choose language + const language = (navigator.language || navigator.browserLanguage).toLowerCase() + const locales = Object.keys(messages) + for (const locale of locales) { + if (language.indexOf(locale) > -1) { + return locale + } + } + return 'zh_CN' +} +const i18n = new VueI18n({ + // set locale + // options: en | zh | es + locale: getLanguage(), + // set locale messages + messages +}) + +export default i18n diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/de-base/lang/messages.js b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/de-base/lang/messages.js new file mode 100644 index 0000000000..844f8c0673 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/de-base/lang/messages.js @@ -0,0 +1,17 @@ +import enLocale from './en' +import zhLocale from './zh' +import twLocale from './tw' + +const messages = { + en_US: { + ...enLocale + }, + zh_CN: { + ...zhLocale + }, + zh_TW: { + ...twLocale + } +} + +export default messages \ No newline at end of file diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/de-base/lang/tw.js b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/de-base/lang/tw.js new file mode 100644 index 0000000000..96ddbd7a11 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/de-base/lang/tw.js @@ -0,0 +1,31 @@ +export default { + host: '主機名/IP地址', + dataBase: '數據庫名稱', + connection_mode: '連接方式', + oracle_sid: 'SID', + oracle_service_name: '服務名', + username: '用戶名', + password: '密碼', + port: '端口', + schema: 'Schema', + get_schema: '獲取 Schema', + charset: '字符集', + targetCharset: '目標字符集', + priority: '高級設置', + initial_pool_size: '初始連接數', + min_pool_size: '最小連接數', + max_pool_size: '最大連接數', + query_timeout: '查詢超時', + one_user_name: '請輸入用戶名', + input_a_password: '請輸入密碼', + enter_the_port: '請輸入端口', + please_choose_schema: '請選擇 Schema', + please_choose_charset: '請選擇數據庫字符集', + please_choose_targetCharset: '請選擇目標字符集', + please_input_initial_pool_size: '請輸入初始連接數', + please_input_min_pool_size: '請輸入最小連接數', + please_input_max_pool_size: '請輸入最大連接數', + please_input_query_timeout: '請輸入查詢超時', + second: '秒', + please_select: '請選擇' +} diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/de-base/lang/zh.js b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/de-base/lang/zh.js new file mode 100644 index 0000000000..46aaa52af8 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/de-base/lang/zh.js @@ -0,0 +1,31 @@ +export default { + host: '主机名/IP地址', + dataBase: '数据库名称', + connection_mode: '连接方式', + oracle_sid: 'SID', + oracle_service_name: '服务名', + username: '用户名', + password: '密码', + port: '端口', + schema: 'Schema', + get_schema: '获取 Schema', + charset: '字符集', + targetCharset: '目标字符集', + priority: '高级设置', + initial_pool_size: '初始连接数', + min_pool_size: '最小连接数', + max_pool_size: '最大连接数', + query_timeout: '查询超时', + one_user_name: '请输入用户名', + input_a_password: '请输入密码', + enter_the_port: '请输入端口', + please_choose_schema: '请选择 Schema', + please_choose_charset: '请选择数据库字符集', + please_choose_targetCharset: '请选择目标字符集', + please_input_initial_pool_size: '请输入初始连接数', + please_input_min_pool_size: '请输入最小连接数', + please_input_max_pool_size: '请输入最大连接数', + please_input_query_timeout: '请输入查询超时', + second: '秒', + please_select: '请选择' +} diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/icons/index.js b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/icons/index.js new file mode 100644 index 0000000000..2c6b309c96 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/icons/index.js @@ -0,0 +1,9 @@ +import Vue from 'vue' +import SvgIcon from '@/components/SvgIcon'// svg component + +// register globally +Vue.component('svg-icon', SvgIcon) + +const req = require.context('./svg', false, /\.svg$/) +const requireAll = requireContext => requireContext.keys().map(requireContext) +requireAll(req) diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/icons/svg/de_pwd_invisible.svg b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/icons/svg/de_pwd_invisible.svg new file mode 100644 index 0000000000..661ebba33e --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/icons/svg/de_pwd_invisible.svg @@ -0,0 +1,3 @@ + + + diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/icons/svg/de_pwd_visible.svg b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/icons/svg/de_pwd_visible.svg new file mode 100644 index 0000000000..4e7ba5e799 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/icons/svg/de_pwd_visible.svg @@ -0,0 +1,3 @@ + + + diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/icons/svg/kingbase-backend.svg b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/icons/svg/kingbase-backend.svg new file mode 100644 index 0000000000..15b98f1f23 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/icons/svg/kingbase-backend.svg @@ -0,0 +1 @@ +【icon】人大金仓 \ No newline at end of file diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/icons/svg/kingbase.jpg b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/icons/svg/kingbase.jpg new file mode 100644 index 0000000000..3ba452f593 Binary files /dev/null and b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/icons/svg/kingbase.jpg differ diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/icons/svgo.yml b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/icons/svgo.yml new file mode 100644 index 0000000000..d11906aec2 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/icons/svgo.yml @@ -0,0 +1,22 @@ +# replace default config + +# multipass: true +# full: true + +plugins: + + # - name + # + # or: + # - name: false + # - name: true + # + # or: + # - name: + # param1: 1 + # param2: 2 + +- removeAttrs: + attrs: + - 'fill' + - 'fill-rule' diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/main.js b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/main.js new file mode 100644 index 0000000000..99c5626590 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/main.js @@ -0,0 +1,33 @@ +// The Vue build version to load with the `import` command +// (runtime-only or standalone) has been set in webpack.base.conf with an alias. +import Vue from 'vue' +import App from './App' +import router from './router' +import ElementUI from 'element-ui' +import Cookies from 'js-cookie' +import i18n from './de-base/lang' +import draggable from 'vuedraggable' +import Treeselect from '@riophae/vue-treeselect' +import '@riophae/vue-treeselect/dist/vue-treeselect.css' +Vue.config.productionTip = false +Vue.use(ElementUI, { + size: Cookies.get('size') || 'medium', + i18n: (key, value) => i18n.t(key, value) +}) +Vue.component('Treeselect', Treeselect) +Vue.component('draggable', draggable) +Vue.prototype.hasDataPermission = function(pTarget, pSource) { + + if (pSource && pTarget) { + return pSource.indexOf(pTarget) > -1 + } + return false +} +/* eslint-disable no-new */ +new Vue({ + el: '#app', + router, + i18n, + components: { App }, + template: '' +}) diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/router/index.js b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/router/index.js new file mode 100644 index 0000000000..78245d2d42 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/router/index.js @@ -0,0 +1,21 @@ +import Vue from 'vue' +import Router from 'vue-router' +import HelloWorld from '@/components/HelloWorld' +import kingbase from '@/views/kingbase' + +Vue.use(Router) + +export default new Router({ + routes: [ + { + path: '/', + name: 'HelloWorld', + component: HelloWorld + }, + { + path: '/kingbase', + name: 'kingbase', + component: kingbase + } + ] +}) diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/utils/compare.js b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/utils/compare.js new file mode 100644 index 0000000000..b06a104a10 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/utils/compare.js @@ -0,0 +1,29 @@ +export const compareItem = { + type: 'none', // year-yoy/month-yoy等 + resultData: 'percent', // 对比差sub,百分比percent等 + field: '', + custom: { + field: '', + calcType: '0', // 0-增长值,1-增长率 + timeType: '0', // 0-固定日期,1-日期区间 + currentTime: '', + compareTime: '', + currentTimeRange: [], + compareTimeRange: [] + } +} + +export const compareYearList = [ + { name: 'year_mom', value: 'year_mom' } +] + +export const compareMonthList = [ + { name: 'month_mom', value: 'month_mom' }, + { name: 'year_yoy', value: 'year_yoy' } +] + +export const compareDayList = [ + { name: 'day_mom', value: 'day_mom' }, + { name: 'month_yoy', value: 'month_yoy' }, + { name: 'year_yoy', value: 'year_yoy' } +] diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/utils/validate.js b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/utils/validate.js new file mode 100644 index 0000000000..3e8ffa9448 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/utils/validate.js @@ -0,0 +1,15 @@ +/** + * Created by PanJiaChen on 16/11/18. + */ + +/** + * @param {string} path + * @returns {Boolean} + */ +export function isExternal(path) { + return /^(https?:|mailto:|tel:)/.test(path) || /^(http?:|mailto:|tel:)/.test(path) || path.startsWith('/api/pluginCommon/staticInfo') +} + + + + diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/views/dePwd.vue b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/views/dePwd.vue new file mode 100644 index 0000000000..61bf3e88ee --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/views/dePwd.vue @@ -0,0 +1,75 @@ + + + + \ No newline at end of file diff --git a/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/views/kingbase.vue b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/views/kingbase.vue new file mode 100644 index 0000000000..4058b3b40a --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/kingbase-frontend/src/views/kingbase.vue @@ -0,0 +1,202 @@ + + + + + diff --git a/extensions/dataease-extensions-datasource/kingbase/plugin.json b/extensions/dataease-extensions-datasource/kingbase/plugin.json new file mode 100644 index 0000000000..9da57b4c99 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/plugin.json @@ -0,0 +1,13 @@ +{ + "name":"kingbase-ora 数据源插件", + "free":0, + "store":"default", + "cost":0, + "category":"datasource", + "descript":"人大金仓插件,值得拥有", + "version":"1.18.0", + "creator":"DATAEASE", + "moduleName":"kingbase-backend", + "require":"1.17.0", + "dsType":"kingbase" +} diff --git a/extensions/dataease-extensions-datasource/kingbase/pom.xml b/extensions/dataease-extensions-datasource/kingbase/pom.xml new file mode 100644 index 0000000000..28bcdb261d --- /dev/null +++ b/extensions/dataease-extensions-datasource/kingbase/pom.xml @@ -0,0 +1,19 @@ + + + + dataease-extensions-datasource + io.dataease + ${dataease.version} + + 4.0.0 + + kingbase + pom + + kingbase-frontend + kingbase-backend + + + diff --git a/extensions/dataease-extensions-datasource/kylin/build.sh b/extensions/dataease-extensions-datasource/kylin/build.sh new file mode 100755 index 0000000000..6c0f20b902 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/build.sh @@ -0,0 +1,6 @@ +#!/bin/sh + mvn clean package -U -Dmaven.test.skip=true + +cp kylin-backend/target/kylin-backend-1.18.0.jar . + +zip -r kylin.zip ./kylin-backend-1.18.0.jar ./kylinDriver ./plugin.json diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-backend/pom.xml b/extensions/dataease-extensions-datasource/kylin/kylin-backend/pom.xml new file mode 100644 index 0000000000..5380575ba1 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-backend/pom.xml @@ -0,0 +1,108 @@ + + + + kylin + io.dataease + ${dataease.version} + + 4.0.0 + + kylin-backend + + + + io.dataease + dataease-plugin-datasource + + + + + + src/main/java + + **/*.properties + **/*.xml + + false + + + src/main/resources + + **/* + + false + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 11 + 11 + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.4 + + + **/server/** + **/*.properties + **/Application* + + + + + + maven-clean-plugin + + + + src/main/resources/static + + ** + + false + + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + main-class-placement + generate-resources + + + + + + + + + + + + run + + + + + + + + + + + diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-backend/src/main/java/io/dataease/plugins/datasource/kylin/provider/KylinConfig.java b/extensions/dataease-extensions-datasource/kylin/kylin-backend/src/main/java/io/dataease/plugins/datasource/kylin/provider/KylinConfig.java new file mode 100644 index 0000000000..77e434d4f7 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-backend/src/main/java/io/dataease/plugins/datasource/kylin/provider/KylinConfig.java @@ -0,0 +1,19 @@ +package io.dataease.plugins.datasource.kylin.provider; + +import io.dataease.plugins.datasource.entity.JdbcConfiguration; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class KylinConfig extends JdbcConfiguration { + + private String driver = "org.apache.kylin.jdbc.Driver"; + + public String getJdbc() { + return "jdbc:kylin://HOSTNAME:PORT/DATABASE" + .replace("HOSTNAME", getHost().trim()) + .replace("PORT", getPort().toString().trim()) + .replace("DATABASE", getDataBase().trim()); + } +} diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-backend/src/main/java/io/dataease/plugins/datasource/kylin/provider/KylinDsProvider.java b/extensions/dataease-extensions-datasource/kylin/kylin-backend/src/main/java/io/dataease/plugins/datasource/kylin/provider/KylinDsProvider.java new file mode 100644 index 0000000000..81862855a3 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-backend/src/main/java/io/dataease/plugins/datasource/kylin/provider/KylinDsProvider.java @@ -0,0 +1,189 @@ +package io.dataease.plugins.datasource.kylin.provider; + +import com.alibaba.druid.pool.DruidPooledConnection; +import com.google.gson.Gson; +import io.dataease.plugins.common.base.domain.DeDriver; +import io.dataease.plugins.common.base.mapper.DeDriverMapper; +import io.dataease.plugins.common.constants.DatasourceTypes; +import io.dataease.plugins.common.dto.datasource.TableDesc; +import io.dataease.plugins.common.dto.datasource.TableField; +import io.dataease.plugins.common.exception.DataEaseException; +import io.dataease.plugins.common.request.datasource.DatasourceRequest; +import io.dataease.plugins.datasource.entity.JdbcConfiguration; +import io.dataease.plugins.datasource.provider.DefaultJdbcProvider; +import io.dataease.plugins.datasource.provider.ExtendedJdbcClassLoader; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.sql.*; +import java.util.*; + + +@Component() +public class KylinDsProvider extends DefaultJdbcProvider { + @Resource + private DeDriverMapper deDriverMapper; + + @Override + public String getType() { + return "kylin"; + } + + @Override + public boolean isUseDatasourcePool() { + return false; + } + + @Override + public Connection getConnection(DatasourceRequest datasourceRequest) throws Exception { + io.dataease.plugins.datasource.kylin.provider.KylinConfig prestoConfig = new Gson().fromJson(datasourceRequest.getDatasource().getConfiguration(), KylinConfig.class); + + String defaultDriver = prestoConfig.getDriver(); + String customDriver = prestoConfig.getCustomDriver(); + + String url = prestoConfig.getJdbc(); + Properties props = new Properties(); + DeDriver deDriver = null; + if (StringUtils.isNotBlank(prestoConfig.getUsername())) { + props.setProperty("user", prestoConfig.getUsername()); + if (StringUtils.isNotBlank(prestoConfig.getPassword())) { + props.setProperty("password", prestoConfig.getPassword()); + } + } + + Connection conn; + String driverClassName ; + ExtendedJdbcClassLoader jdbcClassLoader; + if(isDefaultClassLoader(customDriver)){ + driverClassName = defaultDriver; + jdbcClassLoader = extendedJdbcClassLoader; + }else { + if(deDriver == null){ + deDriver = deDriverMapper.selectByPrimaryKey(customDriver); + } + driverClassName = deDriver.getDriverClass(); + jdbcClassLoader = getCustomJdbcClassLoader(deDriver); + } + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + try { + Thread.currentThread().setContextClassLoader(jdbcClassLoader); + Driver driverClass = (Driver) jdbcClassLoader.loadClass(driverClassName).newInstance(); + conn= driverClass.connect(url, props); + }catch (Exception e){ + e.printStackTrace(); + throw e; + }finally { + Thread.currentThread().setContextClassLoader(classLoader); + } + return conn; + } + + @Override + public List getTables(DatasourceRequest datasourceRequest) throws Exception { + List tables = new ArrayList<>(); + + List tableNames = new ArrayList<>(); + try (Connection con = getConnectionFromPool(datasourceRequest)) { + DatabaseMetaData databaseMetaData = con.getMetaData(); + ResultSet resultSet = databaseMetaData.getColumns(null, "%", "%", "%"); + while (resultSet.next()) { + String tableName = resultSet.getString("TABLE_NAME"); + if(!tableNames.contains(tableName)){ + tableNames.add(tableName); + TableDesc tableDesc = new TableDesc(); + tableDesc.setName(tableName); + tables.add(tableDesc); + } + } + } catch (Exception e) { + DataEaseException.throwException(e); + } + + return tables; + } + + + @Override + public List getTableFields(DatasourceRequest datasourceRequest) throws Exception { + List list = new LinkedList<>(); + try (Connection connection = getConnectionFromPool(datasourceRequest)) { + DatabaseMetaData databaseMetaData = connection.getMetaData(); + String tableNamePattern = datasourceRequest.getTable(); + ResultSet resultSet = databaseMetaData.getColumns(null, "%", tableNamePattern, "%"); + while (resultSet.next()) { + String tableName = resultSet.getString("TABLE_NAME"); + String database; + if (datasourceRequest.getDatasource().getType().equalsIgnoreCase(DatasourceTypes.pg.name()) || datasourceRequest.getDatasource().getType().equalsIgnoreCase(DatasourceTypes.ck.name()) || datasourceRequest.getDatasource().getType().equalsIgnoreCase(DatasourceTypes.impala.name())) { + database = resultSet.getString("TABLE_SCHEM"); + } else { + database = resultSet.getString("TABLE_CAT"); + } + if (tableName.equals(datasourceRequest.getTable())) { + TableField tableField = getTableFiled(resultSet, datasourceRequest); + list.add(tableField); + } + + } + resultSet.close(); + } catch (SQLException e) { + DataEaseException.throwException(e); + } catch (Exception e) { + } + return list; + } + + private TableField getTableFiled(ResultSet resultSet, DatasourceRequest datasourceRequest) throws SQLException { + TableField tableField = new TableField(); + String colName = resultSet.getString("COLUMN_NAME"); + tableField.setFieldName(colName); + String remarks = resultSet.getString("REMARKS"); + if (remarks == null || remarks.equals("")) { + remarks = colName; + } + tableField.setRemarks(remarks); + String dbType = resultSet.getString("TYPE_NAME").toUpperCase(); + tableField.setFieldType(dbType); + if (dbType.equalsIgnoreCase("LONG")) { + tableField.setFieldSize(65533); + } + if (StringUtils.isNotEmpty(dbType) && dbType.toLowerCase().contains("date") && tableField.getFieldSize() < 50) { + tableField.setFieldSize(50); + } + + String size = resultSet.getString("COLUMN_SIZE"); + if (size == null) { + tableField.setFieldSize(1); + } else { + tableField.setFieldSize(Integer.valueOf(size)); + } + + if (StringUtils.isNotEmpty(tableField.getFieldType()) && tableField.getFieldType().equalsIgnoreCase("DECIMAL")) { + tableField.setAccuracy(Integer.valueOf(resultSet.getString("DECIMAL_DIGITS"))); + } + return tableField; + } + + @Override + public String checkStatus(DatasourceRequest datasourceRequest) throws Exception { + try (Connection con = getConnection(datasourceRequest)) { + List tables = new ArrayList<>(); + List tableNames = new ArrayList<>(); + ResultSet resultSet = con.getMetaData().getColumns(null, "%", "%", "%"); + while (resultSet.next()) { + String tableName = resultSet.getString("TABLE_NAME"); + if(!tableNames.contains(tableName)){ + tableNames.add(tableName); + TableDesc tableDesc = new TableDesc(); + tableDesc.setName(tableName); + tables.add(tableDesc); + } + } + } catch (Exception e) { + DataEaseException.throwException(e.getMessage()); + } + return "Success"; + } + + +} diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-backend/src/main/java/io/dataease/plugins/datasource/kylin/query/KylinConstants.java b/extensions/dataease-extensions-datasource/kylin/kylin-backend/src/main/java/io/dataease/plugins/datasource/kylin/query/KylinConstants.java new file mode 100644 index 0000000000..51ab017a64 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-backend/src/main/java/io/dataease/plugins/datasource/kylin/query/KylinConstants.java @@ -0,0 +1,38 @@ +package io.dataease.plugins.datasource.kylin.query; + +import io.dataease.plugins.common.constants.datasource.SQLConstants; + + +public class KylinConstants extends SQLConstants { + + public static final String KEYWORD_TABLE = "%s" ; + + public static final String KEYWORD_FIX = "%s." + "%s"; + + + public static final String CAST = "CAST(%s AS %s)"; + + public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss"; + + public static final String DEFAULT_DATE_TYPE = "TIMESTAMP"; + + public static final String DEFAULT_INT_FORMAT = "BIGINT"; + + public static final String DEFAULT_FLOAT_FORMAT = "DOUBLE"; + + public static final String WHERE_VALUE_NULL = "(NULL,'')"; + + public static final String WHERE_VALUE_VALUE = "'%s'"; + + public static final String AGG_COUNT = "COUNT(*)"; + + public static final String AGG_FIELD = "%s(%s)"; + + public static final String WHERE_BETWEEN = "'%s' AND '%s'"; + + public static final String BRACKETS = "(%s)"; + + public static final String NAME = "pg"; + + +} diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-backend/src/main/java/io/dataease/plugins/datasource/kylin/query/KylinQueryProvider.java b/extensions/dataease-extensions-datasource/kylin/kylin-backend/src/main/java/io/dataease/plugins/datasource/kylin/query/KylinQueryProvider.java new file mode 100644 index 0000000000..d7b455cb0d --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-backend/src/main/java/io/dataease/plugins/datasource/kylin/query/KylinQueryProvider.java @@ -0,0 +1,1256 @@ +package io.dataease.plugins.datasource.kylin.query; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.Gson; +import io.dataease.plugins.common.base.domain.ChartViewWithBLOBs; +import io.dataease.plugins.common.base.domain.DatasetTableField; +import io.dataease.plugins.common.base.domain.DatasetTableFieldExample; +import io.dataease.plugins.common.base.domain.Datasource; +import io.dataease.plugins.common.base.mapper.DatasetTableFieldMapper; +import io.dataease.plugins.common.constants.DeTypeConstants; +import io.dataease.plugins.common.constants.datasource.SQLConstants; +import io.dataease.plugins.common.constants.datasource.SqlServerSQLConstants; +import io.dataease.plugins.common.dto.chart.ChartCustomFilterItemDTO; +import io.dataease.plugins.common.dto.chart.ChartFieldCustomFilterDTO; +import io.dataease.plugins.common.dto.chart.ChartViewFieldDTO; +import io.dataease.plugins.common.dto.datasource.DeSortField; +import io.dataease.plugins.common.dto.sqlObj.SQLObj; +import io.dataease.plugins.common.request.chart.ChartExtFilterRequest; +import io.dataease.plugins.common.request.permission.DataSetRowPermissionsTreeDTO; +import io.dataease.plugins.common.request.permission.DatasetRowPermissionsTreeItem; +import io.dataease.plugins.datasource.entity.Dateformat; +import io.dataease.plugins.datasource.entity.JdbcConfiguration; +import io.dataease.plugins.datasource.query.QueryProvider; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; +import org.stringtemplate.v4.ST; +import org.stringtemplate.v4.STGroup; +import org.stringtemplate.v4.STGroupFile; + +import javax.annotation.Resource; +import java.text.MessageFormat; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import static io.dataease.plugins.common.constants.datasource.SQLConstants.TABLE_ALIAS_PREFIX; + + +@Component() +public class KylinQueryProvider extends QueryProvider { + @Resource + private DatasetTableFieldMapper datasetTableFieldMapper; + + private static final Gson json = new Gson(); + + @Override + public Integer transFieldType(String field) { + field = field.contains("(") ? field.split("\\(")[0] : field; + field = field.toUpperCase(); + switch (field) { + case "DATE": + case "DATETIME": + case "TIMESTAMP": + case "TIME": + return DeTypeConstants.DE_TIME;// 时间 + case "TINYINT": + case "SMALLINT": + case "INT": + case "BIGINT": + case "INTEGER": + return DeTypeConstants.DE_INT;// 整型 + case "FLOAT": + case "DOUBLE": + case "DECIMAL": + case "NUMERIC": + return DeTypeConstants.DE_FLOAT;// 浮点 + case "BOOLEAN": + return DeTypeConstants.DE_BOOL;// 布尔 + case "BINARY": + return DeTypeConstants.DE_BINARY;// 二进制 + default: + return DeTypeConstants.DE_STRING; + } + } + + @Override + public String createSQLPreview(String sql, String orderBy) { + return "SELECT * FROM (" + sqlFix(sql) + ") AS DE_TMP " + " LIMIT 1000"; + } + + @Override + public String createQuerySQL(String table, List fields, boolean isGroup, Datasource ds, List fieldCustomFilter, List rowPermissionsTree) { + return createQuerySQL(table, fields, isGroup, ds, fieldCustomFilter, rowPermissionsTree, null); + } + + @Override + public String createQuerySQL(String table, List fields, boolean isGroup, Datasource ds, List fieldCustomFilter, List rowPermissionsTree, List sortFields) { + SQLObj tableObj = SQLObj.builder() + .tableName((table.startsWith("(") && table.endsWith(")")) ? table : String.format(KylinConstants.KEYWORD_TABLE, table)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 0)) + .build(); + List xFields = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(fields)) { + for (int i = 0; i < fields.size(); i++) { + DatasetTableField f = fields.get(i); + String originField; + if (ObjectUtils.isNotEmpty(f.getExtField()) && f.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(f.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(f.getExtField()) && f.getExtField() == 1) { + originField = String.format(KylinConstants.KEYWORD_FIX, tableObj.getTableAlias(), f.getOriginName()); + } else { + originField = String.format(KylinConstants.KEYWORD_FIX, tableObj.getTableAlias(), f.getOriginName()); + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_X_PREFIX, i); + String fieldName = ""; + // 处理横轴字段 + if (f.getDeExtractType() == DeTypeConstants.DE_TIME) { +// if (f.getDeType() == DeTypeConstants.DE_INT || f.getDeType() == DeTypeConstants.DE_FLOAT) { +// fieldName = String.format(KylinConstants.UNIX_TIMESTAMP, originField); +// } else { +// fieldName = originField; +// } + fieldName = originField; + } else if (f.getDeExtractType() == DeTypeConstants.DE_STRING) { + if (f.getDeType() == DeTypeConstants.DE_INT) { + fieldName = String.format(KylinConstants.CAST, originField, KylinConstants.DEFAULT_INT_FORMAT); + } else if (f.getDeType() == DeTypeConstants.DE_FLOAT) { + fieldName = String.format(KylinConstants.CAST, originField, KylinConstants.DEFAULT_FLOAT_FORMAT); + } else if (f.getDeType() == DeTypeConstants.DE_TIME) { + fieldName = String.format(KylinConstants.CAST, originField, KylinConstants.DEFAULT_DATE_TYPE); +// fieldName = String.format(KylinConstants.date_parse, originField, StringUtils.isNotEmpty(f.getDateFormat()) ? f.getDateFormat() : KylinConstants.DEFAULT_DATE_FORMAT); + } else { + fieldName = originField; + } + } else { + fieldName = originField; +// if (f.getDeType() == DeTypeConstants.DE_TIME) { +// fieldName = String.format(KylinConstants.FORMAT_DATETIME, String.format(KylinConstants.FROM_UNIXTIME, originField + "/1000"), KylinConstants.DEFAULT_DATE_FORMAT); +// } else if (f.getDeType() == DeTypeConstants.DE_INT) { +// fieldName = String.format(KylinConstants.CAST, originField, KylinConstants.DEFAULT_INT_FORMAT); +// } else { +// fieldName = originField; +// } + } + xFields.add(SQLObj.builder() + .fieldName(fieldName) + .fieldAlias(fieldAlias) + .build()); + } + } + + STGroup stg = new STGroupFile(SQLConstants.SQL_TEMPLATE); + ST st_sql = stg.getInstanceOf("previewSql"); + st_sql.add("isGroup", isGroup); + if (CollectionUtils.isNotEmpty(xFields)) st_sql.add("groups", xFields); + if (ObjectUtils.isNotEmpty(tableObj)) st_sql.add("table", tableObj); + String customWheres = transCustomFilterList(tableObj, fieldCustomFilter); + // row permissions tree + String whereTrees = transFilterTrees(tableObj, rowPermissionsTree); + List wheres = new ArrayList<>(); + if (customWheres != null) wheres.add(customWheres); + if (whereTrees != null) wheres.add(whereTrees); + if (CollectionUtils.isNotEmpty(wheres)) st_sql.add("filters", wheres); + + List xOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(sortFields)) { + int step = fields.size(); + for (int i = step; i < (step + sortFields.size()); i++) { + DeSortField deSortField = sortFields.get(i - step); + SQLObj order = buildSortField(deSortField, tableObj, i); + xOrders.add(order); + } + } + if (ObjectUtils.isNotEmpty(xOrders)) { + st_sql.add("orders", xOrders); + } + + return st_sql.render(); + } + + private SQLObj buildSortField(DeSortField f, SQLObj tableObj, int i) { + String originField; + if (ObjectUtils.isNotEmpty(f.getExtField()) && f.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(f.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(f.getExtField()) && f.getExtField() == 1) { + originField = String.format(KylinConstants.KEYWORD_FIX, tableObj.getTableAlias(), f.getOriginName()); + } else { + originField = String.format(KylinConstants.KEYWORD_FIX, tableObj.getTableAlias(), f.getOriginName()); + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_X_PREFIX, i); + String fieldName = ""; + // 处理横轴字段 + if (f.getDeExtractType() == DeTypeConstants.DE_TIME) { + if (f.getDeType() == DeTypeConstants.DE_INT || f.getDeType() == DeTypeConstants.DE_FLOAT) { + fieldName = String.format(KylinConstants.CAST, originField, KylinConstants.DEFAULT_INT_FORMAT); + } else { + fieldName = originField; + } + } else if (f.getDeExtractType() == DeTypeConstants.DE_STRING) { + if (f.getDeType() == DeTypeConstants.DE_INT) { + fieldName = String.format(KylinConstants.CAST, originField, KylinConstants.DEFAULT_INT_FORMAT); + } else if (f.getDeType() == DeTypeConstants.DE_FLOAT) { + fieldName = String.format(KylinConstants.CAST, originField, KylinConstants.DEFAULT_FLOAT_FORMAT); + } else if (f.getDeType() == DeTypeConstants.DE_TIME) { + fieldName = String.format(KylinConstants.CAST, originField, KylinConstants.DEFAULT_DATE_TYPE); + } else { + fieldName = originField; + } + } else { + if (f.getDeType() == DeTypeConstants.DE_TIME) { + fieldName = String.format(KylinConstants.CAST, originField, KylinConstants.DEFAULT_DATE_TYPE); + } else if (f.getDeType() == DeTypeConstants.DE_INT) { + fieldName = String.format(KylinConstants.CAST, originField, KylinConstants.DEFAULT_INT_FORMAT); + } else { + fieldName = originField; + } + } + SQLObj result = SQLObj.builder().orderField(originField).orderAlias(originField).orderDirection(f.getOrderDirection()).build(); + return result; + } + + @Override + public String createQuerySQLAsTmp(String sql, List fields, boolean isGroup, List fieldCustomFilter, List rowPermissionsTree, List sortFields) { + return createQuerySQL("(" + sqlFix(sql) + ")", fields, isGroup, null, fieldCustomFilter, rowPermissionsTree, sortFields); + } + + @Override + public String createQuerySQLAsTmp(String sql, List fields, boolean isGroup, List fieldCustomFilter, List rowPermissionsTree) { + return createQuerySQL("(" + sqlFix(sql) + ")", fields, isGroup, null, fieldCustomFilter, rowPermissionsTree); + } + + @Override + public String createQueryTableWithPage(String table, List fields, Integer page, Integer pageSize, Integer realSize, boolean isGroup, Datasource ds, List fieldCustomFilter, List rowPermissionsTree) { + return createQuerySQL(table, fields, isGroup, ds, fieldCustomFilter, rowPermissionsTree) + " LIMIT " + realSize; + } + + @Override + public String createQuerySQLWithPage(String sql, List fields, Integer page, Integer pageSize, Integer realSize, boolean isGroup, List fieldCustomFilter, List rowPermissionsTree) { + return createQuerySQLAsTmp(sql, fields, isGroup, fieldCustomFilter, rowPermissionsTree) + " LIMIT " + realSize; + } + + @Override + public String createQueryTableWithLimit(String table, List fields, Integer limit, boolean isGroup, Datasource ds, List fieldCustomFilter, List rowPermissionsTree) { + return createQuerySQL(table, fields, isGroup, ds, fieldCustomFilter, rowPermissionsTree) + " LIMIT " + limit; + } + + @Override + public String createQuerySqlWithLimit(String sql, List fields, Integer limit, boolean isGroup, List fieldCustomFilter, List rowPermissionsTree) { + return createQuerySQLAsTmp(sql, fields, isGroup, fieldCustomFilter, rowPermissionsTree) + " LIMIT " + limit; + } + + @Override + public String getSQL(String table, List xAxis, List yAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, Datasource ds, ChartViewWithBLOBs view) { + SQLObj tableObj = SQLObj.builder() + .tableName((table.startsWith("(") && table.endsWith(")")) ? table : String.format(KylinConstants.KEYWORD_TABLE, table)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 0)) + .build(); + setSchema(tableObj, ds); + List xFields = new ArrayList<>(); + List xOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(xAxis)) { + for (int i = 0; i < xAxis.size(); i++) { + ChartViewFieldDTO x = xAxis.get(i); + String originField; + if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(x.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 1) { + originField = String.format(KylinConstants.KEYWORD_FIX, tableObj.getTableAlias(), x.getOriginName()); + } else { + originField = String.format(KylinConstants.KEYWORD_FIX, tableObj.getTableAlias(), x.getOriginName()); + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_X_PREFIX, i); + // 处理横轴字段 + xFields.add(getXFields(x, originField, fieldAlias)); + // 处理横轴排序 + if (StringUtils.isNotEmpty(x.getSort()) && !StringUtils.equalsIgnoreCase(x.getSort(), "none")) { + xOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(x.getSort()) + .build()); + } + } + } + List yFields = new ArrayList<>(); + List yWheres = new ArrayList<>(); + List yOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(yAxis)) { + for (int i = 0; i < yAxis.size(); i++) { + ChartViewFieldDTO y = yAxis.get(i); + String originField; + if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(y.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 1) { + originField = String.format(KylinConstants.KEYWORD_FIX, tableObj.getTableAlias(), y.getOriginName()); + } else { + originField = String.format(KylinConstants.KEYWORD_FIX, tableObj.getTableAlias(), y.getOriginName()); + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_Y_PREFIX, i); + // 处理纵轴字段 + yFields.add(getYFields(y, originField, fieldAlias)); + // 处理纵轴过滤 + yWheres.add(getYWheres(y, originField, fieldAlias)); + // 处理纵轴排序 + if (StringUtils.isNotEmpty(y.getSort()) && !StringUtils.equalsIgnoreCase(y.getSort(), "none")) { + yOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(y.getSort()) + .build()); + } + } + } + // 处理视图中字段过滤 + String customWheres = transCustomFilterList(tableObj, fieldCustomFilter); + // 处理仪表板字段过滤 + String extWheres = transExtFilterList(tableObj, extFilterRequestList); + // row permissions tree + String whereTrees = transFilterTrees(tableObj, rowPermissionsTree); + // 构建sql所有参数 + List fields = new ArrayList<>(); + fields.addAll(xFields); + fields.addAll(yFields); + List wheres = new ArrayList<>(); + if (customWheres != null) wheres.add(customWheres); + if (extWheres != null) wheres.add(extWheres); + if (whereTrees != null) wheres.add(whereTrees); + List groups = new ArrayList<>(); + groups.addAll(xFields); + // 外层再次套sql + List orders = new ArrayList<>(); + orders.addAll(xOrders); + orders.addAll(yOrders); + List aggWheres = new ArrayList<>(); + aggWheres.addAll(yWheres.stream().filter(ObjectUtils::isNotEmpty).collect(Collectors.toList())); + + STGroup stg = new STGroupFile(SQLConstants.SQL_TEMPLATE); + ST st_sql = stg.getInstanceOf("querySql"); + if (CollectionUtils.isNotEmpty(xFields)) st_sql.add("groups", xFields); + if (CollectionUtils.isNotEmpty(yFields)) st_sql.add("aggregators", yFields); + if (CollectionUtils.isNotEmpty(wheres)) st_sql.add("filters", wheres); + if (ObjectUtils.isNotEmpty(tableObj)) st_sql.add("table", tableObj); + String sql = st_sql.render(); + + ST st = stg.getInstanceOf("querySql"); + SQLObj tableSQL = SQLObj.builder() + .tableName(String.format(KylinConstants.BRACKETS, sql)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 1)) + .build(); + if (CollectionUtils.isNotEmpty(aggWheres)) st.add("filters", aggWheres); + if (CollectionUtils.isNotEmpty(orders)) st.add("orders", orders); + if (ObjectUtils.isNotEmpty(tableSQL)) st.add("table", tableSQL); + return sqlLimit(st.render(), view); + } + + @Override + public String getSQLTableInfo(String table, List xAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, Datasource ds, ChartViewWithBLOBs view) { + SQLObj tableObj = SQLObj.builder() + .tableName((table.startsWith("(") && table.endsWith(")")) ? table : String.format(KylinConstants.KEYWORD_TABLE, table)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 0)) + .build(); + setSchema(tableObj, ds); + List xFields = new ArrayList<>(); + List xOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(xAxis)) { + for (int i = 0; i < xAxis.size(); i++) { + ChartViewFieldDTO x = xAxis.get(i); + String originField; + if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(x.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 1) { + originField = String.format(KylinConstants.KEYWORD_FIX, tableObj.getTableAlias(), x.getOriginName()); + } else { + if (x.getDeType() == 2 || x.getDeType() == 3) { + originField = String.format(KylinConstants.CAST, String.format(KylinConstants.KEYWORD_FIX, tableObj.getTableAlias(), x.getOriginName()), KylinConstants.DEFAULT_FLOAT_FORMAT); + } else { + originField = String.format(KylinConstants.KEYWORD_FIX, tableObj.getTableAlias(), x.getOriginName()); + } + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_X_PREFIX, i); + // 处理横轴字段 + xFields.add(getXFields(x, originField, fieldAlias)); + // 处理横轴排序 + if (StringUtils.isNotEmpty(x.getSort()) && !StringUtils.equalsIgnoreCase(x.getSort(), "none")) { + xOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(x.getSort()) + .build()); + } + } + } + // 处理视图中字段过滤 + String customWheres = transCustomFilterList(tableObj, fieldCustomFilter); + // 处理仪表板字段过滤 + String extWheres = transExtFilterList(tableObj, extFilterRequestList); + // row permissions tree + String whereTrees = transFilterTrees(tableObj, rowPermissionsTree); + // 构建sql所有参数 + List fields = new ArrayList<>(); + fields.addAll(xFields); + List wheres = new ArrayList<>(); + if (customWheres != null) wheres.add(customWheres); + if (extWheres != null) wheres.add(extWheres); + if (whereTrees != null) wheres.add(whereTrees); + List groups = new ArrayList<>(); + groups.addAll(xFields); + // 外层再次套sql + List orders = new ArrayList<>(); + orders.addAll(xOrders); + + STGroup stg = new STGroupFile(SQLConstants.SQL_TEMPLATE); + ST st_sql = stg.getInstanceOf("previewSql"); + st_sql.add("isGroup", false); + if (CollectionUtils.isNotEmpty(xFields)) st_sql.add("groups", xFields); + if (CollectionUtils.isNotEmpty(wheres)) st_sql.add("filters", wheres); + if (ObjectUtils.isNotEmpty(tableObj)) st_sql.add("table", tableObj); + String sql = st_sql.render(); + + ST st = stg.getInstanceOf("previewSql"); + st.add("isGroup", false); + SQLObj tableSQL = SQLObj.builder() + .tableName(String.format(KylinConstants.BRACKETS, sql)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 1)) + .build(); + if (CollectionUtils.isNotEmpty(orders)) st.add("orders", orders); + if (ObjectUtils.isNotEmpty(tableSQL)) st.add("table", tableSQL); + return sqlLimit(st.render(), view); + } + + @Override + public String getSQLAsTmpTableInfo(String sql, List xAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, Datasource ds, ChartViewWithBLOBs view) { + return getSQLTableInfo("(" + sqlFix(sql) + ")", xAxis, fieldCustomFilter, rowPermissionsTree, extFilterRequestList, ds, view); + } + + + @Override + public String getSQLAsTmp(String sql, List xAxis, List yAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, ChartViewWithBLOBs view) { + return getSQL("(" + sqlFix(sql) + ")", xAxis, yAxis, fieldCustomFilter, rowPermissionsTree, extFilterRequestList, null, view); + } + + @Override + public String getSQLStack(String table, List xAxis, List yAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, List extStack, Datasource ds, ChartViewWithBLOBs view) { + SQLObj tableObj = SQLObj.builder() + .tableName((table.startsWith("(") && table.endsWith(")")) ? table : String.format(KylinConstants.KEYWORD_TABLE, table)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 0)) + .build(); + setSchema(tableObj, ds); + List xFields = new ArrayList<>(); + List xOrders = new ArrayList<>(); + List xList = new ArrayList<>(); + xList.addAll(xAxis); + xList.addAll(extStack); + if (CollectionUtils.isNotEmpty(xList)) { + for (int i = 0; i < xList.size(); i++) { + ChartViewFieldDTO x = xList.get(i); + String originField; + if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(x.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 1) { + originField = String.format(KylinConstants.KEYWORD_FIX, tableObj.getTableAlias(), x.getOriginName()); + } else { + originField = String.format(KylinConstants.KEYWORD_FIX, tableObj.getTableAlias(), x.getOriginName()); + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_X_PREFIX, i); + // 处理横轴字段 + xFields.add(getXFields(x, originField, fieldAlias)); + // 处理横轴排序 + if (StringUtils.isNotEmpty(x.getSort()) && !StringUtils.equalsIgnoreCase(x.getSort(), "none")) { + xOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(x.getSort()) + .build()); + } + } + } + List yFields = new ArrayList<>(); + List yWheres = new ArrayList<>(); + List yOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(yAxis)) { + for (int i = 0; i < yAxis.size(); i++) { + ChartViewFieldDTO y = yAxis.get(i); + String originField; + if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(y.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 1) { + originField = String.format(KylinConstants.KEYWORD_FIX, tableObj.getTableAlias(), y.getOriginName()); + } else { + originField = String.format(KylinConstants.KEYWORD_FIX, tableObj.getTableAlias(), y.getOriginName()); + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_Y_PREFIX, i); + // 处理纵轴字段 + yFields.add(getYFields(y, originField, fieldAlias)); + // 处理纵轴过滤 + yWheres.add(getYWheres(y, originField, fieldAlias)); + // 处理纵轴排序 + if (StringUtils.isNotEmpty(y.getSort()) && !StringUtils.equalsIgnoreCase(y.getSort(), "none")) { + yOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(y.getSort()) + .build()); + } + } + } + // 处理视图中字段过滤 + String customWheres = transCustomFilterList(tableObj, fieldCustomFilter); + // 处理仪表板字段过滤 + String extWheres = transExtFilterList(tableObj, extFilterRequestList); + // row permissions tree + String whereTrees = transFilterTrees(tableObj, rowPermissionsTree); + // 构建sql所有参数 + List fields = new ArrayList<>(); + fields.addAll(xFields); + fields.addAll(yFields); + List wheres = new ArrayList<>(); + if (customWheres != null) wheres.add(customWheres); + if (extWheres != null) wheres.add(extWheres); + if (whereTrees != null) wheres.add(whereTrees); + List groups = new ArrayList<>(); + groups.addAll(xFields); + // 外层再次套sql + List orders = new ArrayList<>(); + orders.addAll(xOrders); + orders.addAll(yOrders); + List aggWheres = new ArrayList<>(); + aggWheres.addAll(yWheres.stream().filter(ObjectUtils::isNotEmpty).collect(Collectors.toList())); + + STGroup stg = new STGroupFile(SQLConstants.SQL_TEMPLATE); + ST st_sql = stg.getInstanceOf("querySql"); + if (CollectionUtils.isNotEmpty(xFields)) st_sql.add("groups", xFields); + if (CollectionUtils.isNotEmpty(yFields)) st_sql.add("aggregators", yFields); + if (CollectionUtils.isNotEmpty(wheres)) st_sql.add("filters", wheres); + if (ObjectUtils.isNotEmpty(tableObj)) st_sql.add("table", tableObj); + String sql = st_sql.render(); + + ST st = stg.getInstanceOf("querySql"); + SQLObj tableSQL = SQLObj.builder() + .tableName(String.format(KylinConstants.BRACKETS, sql)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 1)) + .build(); + if (CollectionUtils.isNotEmpty(aggWheres)) st.add("filters", aggWheres); + if (CollectionUtils.isNotEmpty(orders)) st.add("orders", orders); + if (ObjectUtils.isNotEmpty(tableSQL)) st.add("table", tableSQL); + return sqlLimit(st.render(), view); + } + + @Override + public String getSQLAsTmpStack(String table, List xAxis, List yAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, List extStack, ChartViewWithBLOBs view) { + return getSQLStack("(" + sqlFix(table) + ")", xAxis, yAxis, fieldCustomFilter, rowPermissionsTree, extFilterRequestList, extStack, null, view); + } + + @Override + public String getSQLScatter(String table, List xAxis, List yAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, List extBubble, Datasource ds, ChartViewWithBLOBs view) { + SQLObj tableObj = SQLObj.builder() + .tableName((table.startsWith("(") && table.endsWith(")")) ? table : String.format(KylinConstants.KEYWORD_TABLE, table)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 0)) + .build(); + setSchema(tableObj, ds); + List xFields = new ArrayList<>(); + List xOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(xAxis)) { + for (int i = 0; i < xAxis.size(); i++) { + ChartViewFieldDTO x = xAxis.get(i); + String originField; + if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(x.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 1) { + originField = String.format(KylinConstants.KEYWORD_FIX, tableObj.getTableAlias(), x.getOriginName()); + } else { + originField = String.format(KylinConstants.KEYWORD_FIX, tableObj.getTableAlias(), x.getOriginName()); + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_X_PREFIX, i); + // 处理横轴字段 + xFields.add(getXFields(x, originField, fieldAlias)); + // 处理横轴排序 + if (StringUtils.isNotEmpty(x.getSort()) && !StringUtils.equalsIgnoreCase(x.getSort(), "none")) { + xOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(x.getSort()) + .build()); + } + } + } + List yFields = new ArrayList<>(); + List yWheres = new ArrayList<>(); + List yOrders = new ArrayList<>(); + List yList = new ArrayList<>(); + yList.addAll(yAxis); + yList.addAll(extBubble); + if (CollectionUtils.isNotEmpty(yList)) { + for (int i = 0; i < yList.size(); i++) { + ChartViewFieldDTO y = yList.get(i); + String originField; + if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(y.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 1) { + originField = String.format(KylinConstants.KEYWORD_FIX, tableObj.getTableAlias(), y.getOriginName()); + } else { + originField = String.format(KylinConstants.KEYWORD_FIX, tableObj.getTableAlias(), y.getOriginName()); + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_Y_PREFIX, i); + // 处理纵轴字段 + yFields.add(getYFields(y, originField, fieldAlias)); + // 处理纵轴过滤 + yWheres.add(getYWheres(y, originField, fieldAlias)); + // 处理纵轴排序 + if (StringUtils.isNotEmpty(y.getSort()) && !StringUtils.equalsIgnoreCase(y.getSort(), "none")) { + yOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(y.getSort()) + .build()); + } + } + } + // 处理视图中字段过滤 + String customWheres = transCustomFilterList(tableObj, fieldCustomFilter); + // 处理仪表板字段过滤 + String extWheres = transExtFilterList(tableObj, extFilterRequestList); + // row permissions tree + String whereTrees = transFilterTrees(tableObj, rowPermissionsTree); + // 构建sql所有参数 + List fields = new ArrayList<>(); + fields.addAll(xFields); + fields.addAll(yFields); + List wheres = new ArrayList<>(); + if (customWheres != null) wheres.add(customWheres); + if (extWheres != null) wheres.add(extWheres); + if (whereTrees != null) wheres.add(whereTrees); + List groups = new ArrayList<>(); + groups.addAll(xFields); + // 外层再次套sql + List orders = new ArrayList<>(); + orders.addAll(xOrders); + orders.addAll(yOrders); + List aggWheres = new ArrayList<>(); + aggWheres.addAll(yWheres.stream().filter(ObjectUtils::isNotEmpty).collect(Collectors.toList())); + + STGroup stg = new STGroupFile(SQLConstants.SQL_TEMPLATE); + ST st_sql = stg.getInstanceOf("querySql"); + if (CollectionUtils.isNotEmpty(xFields)) st_sql.add("groups", xFields); + if (CollectionUtils.isNotEmpty(yFields)) st_sql.add("aggregators", yFields); + if (CollectionUtils.isNotEmpty(wheres)) st_sql.add("filters", wheres); + if (ObjectUtils.isNotEmpty(tableObj)) st_sql.add("table", tableObj); + String sql = st_sql.render(); + + ST st = stg.getInstanceOf("querySql"); + SQLObj tableSQL = SQLObj.builder() + .tableName(String.format(KylinConstants.BRACKETS, sql)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 1)) + .build(); + if (CollectionUtils.isNotEmpty(aggWheres)) st.add("filters", aggWheres); + if (CollectionUtils.isNotEmpty(orders)) st.add("orders", orders); + if (ObjectUtils.isNotEmpty(tableSQL)) st.add("table", tableSQL); + return sqlLimit(st.render(), view); + } + + @Override + public String getSQLAsTmpScatter(String table, List xAxis, List yAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, List extBubble, ChartViewWithBLOBs view) { + return getSQLScatter("(" + sqlFix(table) + ")", xAxis, yAxis, fieldCustomFilter, rowPermissionsTree, extFilterRequestList, extBubble, null, view); + } + + @Override + public String searchTable(String table) { + return "SELECT table_name FROM information_schema.TABLES WHERE table_name ='" + table + "'"; + } + + @Override + public String getSQLSummary(String table, List yAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, ChartViewWithBLOBs view, Datasource ds) { + // 字段汇总 排序等 + SQLObj tableObj = SQLObj.builder() + .tableName((table.startsWith("(") && table.endsWith(")")) ? table : String.format(KylinConstants.KEYWORD_TABLE, table)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 0)) + .build(); + setSchema(tableObj, ds); + List yFields = new ArrayList<>(); + List yWheres = new ArrayList<>(); + List yOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(yAxis)) { + for (int i = 0; i < yAxis.size(); i++) { + ChartViewFieldDTO y = yAxis.get(i); + String originField; + if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(y.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 1) { + originField = String.format(KylinConstants.KEYWORD_FIX, tableObj.getTableAlias(), y.getOriginName()); + } else { + originField = String.format(KylinConstants.KEYWORD_FIX, tableObj.getTableAlias(), y.getOriginName()); + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_Y_PREFIX, i); + // 处理纵轴字段 + yFields.add(getYFields(y, originField, fieldAlias)); + // 处理纵轴过滤 + yWheres.add(getYWheres(y, originField, fieldAlias)); + // 处理纵轴排序 + if (StringUtils.isNotEmpty(y.getSort()) && !StringUtils.equalsIgnoreCase(y.getSort(), "none")) { + yOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(y.getSort()) + .build()); + } + } + } + // 处理视图中字段过滤 + String customWheres = transCustomFilterList(tableObj, fieldCustomFilter); + // 处理仪表板字段过滤 + String extWheres = transExtFilterList(tableObj, extFilterRequestList); + // row permissions tree + String whereTrees = transFilterTrees(tableObj, rowPermissionsTree); + // 构建sql所有参数 + List fields = new ArrayList<>(); + fields.addAll(yFields); + List wheres = new ArrayList<>(); + if (customWheres != null) wheres.add(customWheres); + if (extWheres != null) wheres.add(extWheres); + if (whereTrees != null) wheres.add(whereTrees); + List groups = new ArrayList<>(); + // 外层再次套sql + List orders = new ArrayList<>(); + orders.addAll(yOrders); + List aggWheres = new ArrayList<>(); + aggWheres.addAll(yWheres.stream().filter(ObjectUtils::isNotEmpty).collect(Collectors.toList())); + + STGroup stg = new STGroupFile(SQLConstants.SQL_TEMPLATE); + ST st_sql = stg.getInstanceOf("querySql"); + if (CollectionUtils.isNotEmpty(yFields)) st_sql.add("aggregators", yFields); + if (CollectionUtils.isNotEmpty(wheres)) st_sql.add("filters", wheres); + if (ObjectUtils.isNotEmpty(tableObj)) st_sql.add("table", tableObj); + String sql = st_sql.render(); + + ST st = stg.getInstanceOf("querySql"); + SQLObj tableSQL = SQLObj.builder() + .tableName(String.format(KylinConstants.BRACKETS, sql)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 1)) + .build(); + if (CollectionUtils.isNotEmpty(aggWheres)) st.add("filters", aggWheres); + if (CollectionUtils.isNotEmpty(orders)) st.add("orders", orders); + if (ObjectUtils.isNotEmpty(tableSQL)) st.add("table", tableSQL); + return sqlLimit(st.render(), view); + } + + @Override + public String getSQLSummaryAsTmp(String sql, List yAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, ChartViewWithBLOBs view) { + return getSQLSummary("(" + sqlFix(sql) + ")", yAxis, fieldCustomFilter, rowPermissionsTree, extFilterRequestList, view, null); + } + + @Override + public String wrapSql(String sql) { + sql = sql.trim(); + if (sql.lastIndexOf(";") == (sql.length() - 1)) { + sql = sql.substring(0, sql.length() - 1); + } + String tmpSql = "SELECT * FROM (" + sql + ") AS tmp " + " LIMIT 0 "; + return tmpSql; + } + + @Override + public String createRawQuerySQL(String table, List fields, Datasource ds) { + String[] array = fields.stream().map(f -> { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("\"").append(f.getOriginName()).append("\" AS ").append(f.getDataeaseName()); + return stringBuilder.toString(); + }).toArray(String[]::new); + if (ds != null) { + String schema = new Gson().fromJson(ds.getConfiguration(), JdbcConfiguration.class).getSchema(); + String tableWithSchema = String.format(SqlServerSQLConstants.KEYWORD_TABLE, schema) + "." + String.format(SqlServerSQLConstants.KEYWORD_TABLE, table); + return MessageFormat.format("SELECT {0} FROM {1} ", StringUtils.join(array, ","), tableWithSchema); + } else { + return MessageFormat.format("SELECT {0} FROM {1} ", StringUtils.join(array, ","), table); + } + } + + @Override + public String createRawQuerySQLAsTmp(String sql, List fields) { + return createRawQuerySQL(" (" + sqlFix(sql) + ") AS tmp ", fields, null); + } + + @Override + public String transTreeItem(SQLObj tableObj, DatasetRowPermissionsTreeItem item) { + String res = null; + DatasetTableField field = item.getField(); + if (ObjectUtils.isEmpty(field)) { + return null; + } + String whereName = ""; + String originName; + if (ObjectUtils.isNotEmpty(field.getExtField()) && field.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originName = calcFieldRegex(field.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(field.getExtField()) && field.getExtField() == 1) { + originName = String.format(KylinConstants.KEYWORD_FIX, tableObj.getTableAlias(), field.getOriginName()); + } else { + originName = String.format(KylinConstants.KEYWORD_FIX, tableObj.getTableAlias(), field.getOriginName()); + } + if (field.getDeType() == DeTypeConstants.DE_TIME) { + if (field.getDeExtractType() == DeTypeConstants.DE_STRING || field.getDeExtractType() == 5) { + whereName = String.format(KylinConstants.CAST, originName, KylinConstants.DEFAULT_DATE_TYPE); + } + if (field.getDeExtractType() == DeTypeConstants.DE_INT || field.getDeExtractType() == DeTypeConstants.DE_FLOAT || field.getDeExtractType() == 4) { + String cast = String.format(KylinConstants.CAST, originName, KylinConstants.DEFAULT_INT_FORMAT); + whereName = String.format(KylinConstants.CAST, originName, KylinConstants.DEFAULT_DATE_TYPE); + } + if (field.getDeExtractType() == DeTypeConstants.DE_STRING) { + whereName = originName; + } + } else if (field.getDeType() == DeTypeConstants.DE_INT || field.getDeType() == DeTypeConstants.DE_FLOAT) { + if (field.getDeExtractType() == DeTypeConstants.DE_STRING || field.getDeExtractType() == 5) { + whereName = String.format(KylinConstants.CAST, originName, KylinConstants.DEFAULT_FLOAT_FORMAT); + } + if (field.getDeExtractType() == DeTypeConstants.DE_TIME) { + whereName = String.format(KylinConstants.CAST, originName, KylinConstants.DEFAULT_DATE_TYPE); + } + if (field.getDeExtractType() == DeTypeConstants.DE_INT || field.getDeExtractType() == DeTypeConstants.DE_FLOAT || field.getDeExtractType() == 4) { + whereName = originName; + } + } else { + whereName = originName; + } + + if (StringUtils.equalsIgnoreCase(item.getFilterType(), "enum")) { + if (CollectionUtils.isNotEmpty(item.getEnumValue())) { + res = "(" + whereName + " IN ('" + String.join("','", item.getEnumValue()) + "'))"; + } + } else { + String value = item.getValue(); + String whereTerm = transMysqlFilterTerm(item.getTerm()); + String whereValue = ""; + + if (StringUtils.equalsIgnoreCase(item.getTerm(), "null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(item.getTerm(), "not_null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(item.getTerm(), "empty")) { + whereValue = "''"; + } else if (StringUtils.equalsIgnoreCase(item.getTerm(), "not_empty")) { + whereValue = "''"; + } else if (StringUtils.containsIgnoreCase(item.getTerm(), "in") || StringUtils.containsIgnoreCase(item.getTerm(), "not in")) { + whereValue = "('" + String.join("','", value.split(",")) + "')"; + } else if (StringUtils.containsIgnoreCase(item.getTerm(), "like")) { + whereValue = "'%" + value + "%'"; + } else { + whereValue = String.format(KylinConstants.WHERE_VALUE_VALUE, value); + } + SQLObj build = SQLObj.builder() + .whereField(whereName) + .whereTermAndValue(whereTerm + whereValue) + .build(); + res = build.getWhereField() + " " + build.getWhereTermAndValue(); + } + return res; + } + + @Override + public String convertTableToSql(String tableName, Datasource ds) { + String schema = new Gson().fromJson(ds.getConfiguration(), JdbcConfiguration.class).getSchema(); + schema = String.format(KylinConstants.KEYWORD_TABLE, schema); + return createSQLPreview("SELECT * FROM " + schema + "." + String.format(KylinConstants.KEYWORD_TABLE, tableName), null); + } + + public String transMysqlFilterTerm(String term) { + switch (term) { + case "eq": + return " = "; + case "not_eq": + return " <> "; + case "lt": + return " < "; + case "le": + return " <= "; + case "gt": + return " > "; + case "ge": + return " >= "; + case "in": + return " IN "; + case "not in": + return " NOT IN "; + case "like": + return " LIKE "; + case "not like": + return " NOT LIKE "; + case "null": + return " IS NULL "; + case "not_null": + return " IS NOT NULL "; + case "empty": + return " = "; + case "not_empty": + return " <> "; + case "between": + return " BETWEEN "; + default: + return ""; + } + } + + public String transCustomFilterList(SQLObj tableObj, List requestList) { + if (CollectionUtils.isEmpty(requestList)) { + return null; + } + List res = new ArrayList<>(); + for (ChartFieldCustomFilterDTO request : requestList) { + List list = new ArrayList<>(); + DatasetTableField field = request.getField(); + + if (ObjectUtils.isEmpty(field)) { + continue; + } + String whereName = ""; + String originName; + if (ObjectUtils.isNotEmpty(field.getExtField()) && field.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originName = calcFieldRegex(field.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(field.getExtField()) && field.getExtField() == 1) { + originName = String.format(KylinConstants.KEYWORD_FIX, tableObj.getTableAlias(), field.getOriginName()); + } else { + originName = String.format(KylinConstants.KEYWORD_FIX, tableObj.getTableAlias(), field.getOriginName()); + } + if (field.getDeType() == DeTypeConstants.DE_TIME) { + if (field.getDeExtractType() == DeTypeConstants.DE_STRING || field.getDeExtractType() == 5) { + whereName = String.format(KylinConstants.CAST, originName, KylinConstants.DEFAULT_DATE_TYPE); + } + if (field.getDeExtractType() == DeTypeConstants.DE_INT || field.getDeExtractType() == DeTypeConstants.DE_FLOAT || field.getDeExtractType() == DeTypeConstants.DE_BOOL) { + whereName = String.format(KylinConstants.CAST, originName, KylinConstants.DEFAULT_DATE_TYPE); + } + if (field.getDeExtractType() == DeTypeConstants.DE_STRING) { + whereName = originName; + } + } else if (field.getDeType() == DeTypeConstants.DE_INT || field.getDeType() == DeTypeConstants.DE_FLOAT) { + if (field.getDeExtractType() == DeTypeConstants.DE_STRING || field.getDeExtractType() == 5) { + whereName = String.format(KylinConstants.CAST, originName, KylinConstants.DEFAULT_FLOAT_FORMAT); + } + if (field.getDeExtractType() == DeTypeConstants.DE_TIME) { + whereName = String.format(KylinConstants.CAST, originName, KylinConstants.DEFAULT_INT_FORMAT); + } + if (field.getDeExtractType() == DeTypeConstants.DE_INT || field.getDeExtractType() == DeTypeConstants.DE_FLOAT || field.getDeExtractType() == 4) { + whereName = originName; + } + } else { + whereName = originName; + } + + if (StringUtils.equalsIgnoreCase(request.getFilterType(), "enum")) { + if (CollectionUtils.isNotEmpty(request.getEnumCheckField())) { + res.add("(" + whereName + " IN ('" + String.join("','", request.getEnumCheckField()) + "'))"); + } + } else { + List filter = request.getFilter(); + for (ChartCustomFilterItemDTO filterItemDTO : filter) { + String value = filterItemDTO.getValue(); + String whereTerm = transMysqlFilterTerm(filterItemDTO.getTerm()); + String whereValue = ""; + + if (StringUtils.equalsIgnoreCase(filterItemDTO.getTerm(), "null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(filterItemDTO.getTerm(), "not_null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(filterItemDTO.getTerm(), "empty")) { + whereValue = "''"; + } else if (StringUtils.equalsIgnoreCase(filterItemDTO.getTerm(), "not_empty")) { + whereValue = "''"; + } else if (StringUtils.containsIgnoreCase(filterItemDTO.getTerm(), "in") || StringUtils.containsIgnoreCase(filterItemDTO.getTerm(), "not in")) { + whereValue = "('" + String.join("','", value.split(",")) + "')"; + } else if (StringUtils.containsIgnoreCase(filterItemDTO.getTerm(), "like")) { + whereValue = "'%" + value + "%'"; + } else { + whereValue = String.format(KylinConstants.WHERE_VALUE_VALUE, value); + } + list.add(SQLObj.builder() + .whereField(whereName) + .whereTermAndValue(whereTerm + whereValue) + .build()); + } + + List strList = new ArrayList<>(); + list.forEach(ele -> strList.add(ele.getWhereField() + " " + ele.getWhereTermAndValue())); + if (CollectionUtils.isNotEmpty(list)) { + res.add("(" + String.join(" " + getLogic(request.getLogic()) + " ", strList) + ")"); + } + } + } + return CollectionUtils.isNotEmpty(res) ? "(" + String.join(" AND ", res) + ")" : null; + } + + public String transExtFilterList(SQLObj tableObj, List requestList) { + if (CollectionUtils.isEmpty(requestList)) { + return null; + } + List list = new ArrayList<>(); + for (ChartExtFilterRequest request : requestList) { + List value = request.getValue(); + + List whereNameList = new ArrayList<>(); + List fieldList = new ArrayList<>(); + if (request.getIsTree()) { + fieldList.addAll(request.getDatasetTableFieldList()); + } else { + fieldList.add(request.getDatasetTableField()); + } + + for (DatasetTableField field : fieldList) { + if (CollectionUtils.isEmpty(value) || ObjectUtils.isEmpty(field)) { + continue; + } + String whereName = ""; + + String originName; + if (ObjectUtils.isNotEmpty(field.getExtField()) && field.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originName = calcFieldRegex(field.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(field.getExtField()) && field.getExtField() == 1) { + originName = String.format(KylinConstants.KEYWORD_FIX, tableObj.getTableAlias(), field.getOriginName()); + } else { + originName = String.format(KylinConstants.KEYWORD_FIX, tableObj.getTableAlias(), field.getOriginName()); + } + + if (field.getDeType() == DeTypeConstants.DE_TIME) { + if (field.getDeExtractType() == DeTypeConstants.DE_STRING || field.getDeExtractType() == 5) { + whereName = String.format(KylinConstants.CAST, originName, KylinConstants.DEFAULT_DATE_TYPE); + } + if (field.getDeExtractType() == DeTypeConstants.DE_INT || field.getDeExtractType() == DeTypeConstants.DE_FLOAT || field.getDeExtractType() == 4) { + whereName = String.format(KylinConstants.CAST, originName, KylinConstants.DEFAULT_DATE_TYPE); + } + if (field.getDeExtractType() == 1) { + whereName = originName; + } + } else if (field.getDeType() == DeTypeConstants.DE_INT || field.getDeType() == DeTypeConstants.DE_FLOAT) { + if (field.getDeExtractType() == 0 || field.getDeExtractType() == 5) { + whereName = String.format(KylinConstants.CAST, originName, KylinConstants.DEFAULT_FLOAT_FORMAT); + } + if (field.getDeExtractType() == DeTypeConstants.DE_TIME) { + whereName = String.format(KylinConstants.CAST, originName, KylinConstants.DEFAULT_FLOAT_FORMAT); + } + if (field.getDeExtractType() == 2 || field.getDeExtractType() == 3 || field.getDeExtractType() == 4) { + whereName = originName; + } + } else { + whereName = originName; + } + whereNameList.add(whereName); + } + + String whereName = ""; + if (request.getIsTree()) { + whereName = "CONCAT(" + StringUtils.join(whereNameList, ",',',") + ")"; + } else { + whereName = whereNameList.get(0); + } + String whereTerm = transMysqlFilterTerm(request.getOperator()); + String whereValue = ""; + + if (StringUtils.containsIgnoreCase(request.getOperator(), "in")) { + whereValue = "('" + StringUtils.join(value, "','") + "')"; + } else if (StringUtils.containsIgnoreCase(request.getOperator(), "like")) { + String keyword = value.get(0).toUpperCase(); + whereValue = "'%" + keyword + "%'"; + whereName = "upper(" + whereName + ")"; + } else if (StringUtils.containsIgnoreCase(request.getOperator(), "between")) { + if (request.getDatasetTableField().getDeType() == 1) { + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String startTime = simpleDateFormat.format(new Date(Long.parseLong(value.get(0)))); + String endTime = simpleDateFormat.format(new Date(Long.parseLong(value.get(1)))); + whereValue = String.format(KylinConstants.WHERE_BETWEEN, startTime, endTime); + } else { + whereValue = String.format(KylinConstants.WHERE_BETWEEN, value.get(0), value.get(1)); + } + } else { + whereValue = String.format(KylinConstants.WHERE_VALUE_VALUE, value.get(0)); + } + list.add(SQLObj.builder() + .whereField(whereName) + .whereTermAndValue(whereTerm + whereValue) + .build()); + } + List strList = new ArrayList<>(); + list.forEach(ele -> strList.add(ele.getWhereField() + " " + ele.getWhereTermAndValue())); + return CollectionUtils.isNotEmpty(list) ? "(" + String.join(" AND ", strList) + ")" : null; + } + + private String sqlFix(String sql) { + if (sql.lastIndexOf(";") == (sql.length() - 1)) { + sql = sql.substring(0, sql.length() - 1); + } + return sql; + } + + private String transDateFormat(String dateStyle, String datePattern) { + String split = "-"; + if (StringUtils.equalsIgnoreCase(datePattern, "date_sub")) { + split = "-"; + } else if (StringUtils.equalsIgnoreCase(datePattern, "date_split")) { + split = "/"; + } else { + split = "-"; + } + + if (StringUtils.isEmpty(dateStyle)) { + return "yyyy-MM-dd HH:mm:ss"; + } + + switch (dateStyle) { + case "y": + return "yyyy"; + case "y_M": + return "yyyy" + split + "MM"; + case "y_M_d": + return "yyyy" + split + "MM" + split + "dd"; + case "H_m_s": + return "hh:mi:ss"; + case "y_M_d_H_m": + return "yyyy" + split + "MM" + split + "dd" + " HH:mm"; + case "y_M_d_H_m_s": + return "yyyy" + split + "MM" + split + "dd" + " HH:mm:ss"; + default: + return "yyyy-MM-dd HH:mm:s"; + } + } + + private SQLObj getXFields(ChartViewFieldDTO x, String originField, String fieldAlias) { + String fieldName = ""; + if (x.getDeExtractType() == DeTypeConstants.DE_TIME) { +// if (x.getDeType() == DeTypeConstants.DE_INT || x.getDeType() == DeTypeConstants.DE_FLOAT) { +// fieldName = String.format(KylinConstants.UNIX_TIMESTAMP, originField); +// } else if (x.getDeType() == DeTypeConstants.DE_TIME) { +// String format = transDateFormat(x.getDateStyle(), x.getDatePattern()); +// fieldName = String.format(KylinConstants.FORMAT_DATETIME, originField, format); +// } else { +// fieldName = originField; +// } + fieldName = originField; + } else { + if (x.getDeType() == DeTypeConstants.DE_TIME) { + String format = transDateFormat(x.getDateStyle(), x.getDatePattern()); + if (x.getDeExtractType() == DeTypeConstants.DE_STRING) { + fieldName = String.format(KylinConstants.CAST, originField, KylinConstants.DEFAULT_DATE_TYPE); + } else { + fieldName = originField; + } + } else { + if (x.getDeType() == DeTypeConstants.DE_INT) { + fieldName = String.format(KylinConstants.CAST, originField, KylinConstants.DEFAULT_INT_FORMAT); + } else if (x.getDeType() == DeTypeConstants.DE_FLOAT) { + fieldName = String.format(KylinConstants.CAST, originField, KylinConstants.DEFAULT_FLOAT_FORMAT); + } else { + fieldName = originField; + } + } + } + return SQLObj.builder() + .fieldName(fieldName) + .fieldAlias(fieldAlias) + .build(); + } + + private SQLObj getYFields(ChartViewFieldDTO y, String originField, String fieldAlias) { + String fieldName = ""; + if (StringUtils.equalsIgnoreCase(y.getOriginName(), "*")) { + fieldName = KylinConstants.AGG_COUNT; + } else if (SQLConstants.DIMENSION_TYPE.contains(y.getDeType())) { + fieldName = String.format(KylinConstants.AGG_FIELD, y.getSummary(), originField); + } else { + if (StringUtils.equalsIgnoreCase(y.getSummary(), "avg") || StringUtils.containsIgnoreCase(y.getSummary(), "pop")) { + String cast = String.format(KylinConstants.CAST, originField, y.getDeType() == DeTypeConstants.DE_INT ? KylinConstants.DEFAULT_INT_FORMAT : KylinConstants.DEFAULT_FLOAT_FORMAT); + String agg = String.format(KylinConstants.AGG_FIELD, y.getSummary(), cast); + fieldName = String.format(KylinConstants.CAST, agg, KylinConstants.DEFAULT_FLOAT_FORMAT); + } else { + String cast = String.format(KylinConstants.CAST, originField, y.getDeType() == DeTypeConstants.DE_INT ? KylinConstants.DEFAULT_INT_FORMAT : KylinConstants.DEFAULT_FLOAT_FORMAT); + fieldName = String.format(KylinConstants.AGG_FIELD, y.getSummary(), cast); + } + } + return SQLObj.builder() + .fieldName(fieldName) + .fieldAlias(fieldAlias) + .build(); + } + + private String getYWheres(ChartViewFieldDTO y, String originField, String fieldAlias) { + List list = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(y.getFilter()) && y.getFilter().size() > 0) { + y.getFilter().forEach(f -> { + String whereTerm = transMysqlFilterTerm(f.getTerm()); + String whereValue = ""; + // 原始类型不是时间,在de中被转成时间的字段做处理 + if (StringUtils.equalsIgnoreCase(f.getTerm(), "null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(f.getTerm(), "not_null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(f.getTerm(), "empty")) { + whereValue = "''"; + } else if (StringUtils.equalsIgnoreCase(f.getTerm(), "not_empty")) { + whereValue = "''"; + } else if (StringUtils.containsIgnoreCase(f.getTerm(), "in")) { + whereValue = "('" + StringUtils.join(f.getValue(), "','") + "')"; + } else if (StringUtils.containsIgnoreCase(f.getTerm(), "like")) { + whereValue = "'%" + f.getValue() + "%'"; + } else { + whereValue = String.format(KylinConstants.WHERE_VALUE_VALUE, f.getValue()); + } + list.add(SQLObj.builder() + .whereField(fieldAlias) + .whereAlias(fieldAlias) + .whereTermAndValue(whereTerm + whereValue) + .build()); + }); + } + List strList = new ArrayList<>(); + list.forEach(ele -> strList.add(ele.getWhereField() + " " + ele.getWhereTermAndValue())); + return CollectionUtils.isNotEmpty(list) ? "(" + String.join(" " + getLogic(y.getLogic()) + " ", strList) + ")" : null; + } + + private String calcFieldRegex(String originField, SQLObj tableObj) { + originField = originField.replaceAll("[\\t\\n\\r]]", ""); + // 正则提取[xxx] + String regex = "\\[(.*?)]"; + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(originField); + Set ids = new HashSet<>(); + while (matcher.find()) { + String id = matcher.group(1); + ids.add(id); + } + if (CollectionUtils.isEmpty(ids)) { + return originField; + } + DatasetTableFieldExample datasetTableFieldExample = new DatasetTableFieldExample(); + datasetTableFieldExample.createCriteria().andIdIn(new ArrayList<>(ids)); + List calcFields = datasetTableFieldMapper.selectByExample(datasetTableFieldExample); + for (DatasetTableField ele : calcFields) { + originField = originField.replaceAll("\\[" + ele.getId() + "]", + String.format(KylinConstants.KEYWORD_FIX, tableObj.getTableAlias(), ele.getOriginName())); + } + return originField; + } + + private String sqlLimit(String sql, ChartViewWithBLOBs view) { + if (StringUtils.equalsIgnoreCase(view.getResultMode(), "custom")) { + return sql + " LIMIT " + view.getResultCount(); + } else { + return sql; + } + } + + public String getResultCount(boolean isTable, String sql, List xAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, Datasource ds, ChartViewWithBLOBs view) { + return null; + } + +} diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-backend/src/main/java/io/dataease/plugins/datasource/kylin/service/KylinService.java b/extensions/dataease-extensions-datasource/kylin/kylin-backend/src/main/java/io/dataease/plugins/datasource/kylin/service/KylinService.java new file mode 100644 index 0000000000..a54757342f --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-backend/src/main/java/io/dataease/plugins/datasource/kylin/service/KylinService.java @@ -0,0 +1,54 @@ +package io.dataease.plugins.datasource.kylin.service; + +import io.dataease.plugins.common.constants.DatabaseClassification; +import io.dataease.plugins.common.constants.DatasourceCalculationMode; +import io.dataease.plugins.common.dto.StaticResource; +import io.dataease.plugins.common.dto.datasource.DataSourceType; +import io.dataease.plugins.datasource.service.DatasourceService; +import org.springframework.stereotype.Service; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +@Service +public class KylinService extends DatasourceService { + + + @Override + public List components() { + List result = new ArrayList<>(); + result.add("kylin"); + return result; + } + @Override + protected InputStream readContent(String s) { + InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("static/" + s); + return resourceAsStream; + } + + @Override + public List staticResources() { + List results = new ArrayList<>(); + StaticResource staticResource = new StaticResource(); + staticResource.setName("kylin"); + staticResource.setSuffix("jpg"); + results.add(staticResource); + results.add(pluginSvg()); + return results; + } + + @Override + public DataSourceType getDataSourceType() { + DataSourceType dataSourceType = new DataSourceType("kylin", "Kylin" , true , "", DatasourceCalculationMode.DIRECT, true); + dataSourceType.setDatabaseClassification(DatabaseClassification.DL); + return dataSourceType; + } + + private StaticResource pluginSvg() { + StaticResource staticResource = new StaticResource(); + staticResource.setName("kylin-backend"); + staticResource.setSuffix("svg"); + return staticResource; + } +} diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-frontend/.babelrc b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/.babelrc new file mode 100644 index 0000000000..3a280ba34b --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/.babelrc @@ -0,0 +1,12 @@ +{ + "presets": [ + ["env", { + "modules": false, + "targets": { + "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] + } + }], + "stage-2" + ], + "plugins": ["transform-vue-jsx", "transform-runtime"] +} diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-frontend/.gitignore b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/.gitignore new file mode 100644 index 0000000000..541a820f6c --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/.gitignore @@ -0,0 +1,14 @@ +.DS_Store +node_modules/ +/dist/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-frontend/README.md b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/README.md new file mode 100644 index 0000000000..c1e4be95e0 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/README.md @@ -0,0 +1,21 @@ +# deplugin-view-frontend + +> A Vue.js project + +## Build Setup + +``` bash +# install dependencies +npm install + +# serve with hot reload at localhost:8080 +npm run dev + +# build for production with minification +npm run build + +# build for production and view the bundle analyzer report +npm run build --report +``` + +For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader). diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-frontend/build/build-async-plugins.js b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/build/build-async-plugins.js new file mode 100644 index 0000000000..2303854531 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/build/build-async-plugins.js @@ -0,0 +1,35 @@ +'use strict' +require('./check-versions')() + +process.env.NODE_ENV = 'production' + +const ora = require('ora') +const chalk = require('chalk') +const webpack = require('webpack') +const webpackConfig = require('./webpack.async-plugins') + +const spinner = ora('building for sync-plugins...') +spinner.start() + +webpack(webpackConfig, function (err, stats) { + spinner.stop() + if (err) throw err + process.stdout.write(stats.toString({ + colors: true, + modules: false, + children: false, + chunks: false, + chunkModules: false + }) + '\n\n') + + if (stats.hasErrors()) { + console.log(chalk.red(' Build failed with errors.\n')) + process.exit(1) + } + + console.log(chalk.cyan(' Build complete.\n')) + console.log(chalk.yellow( + ' Tip: built files are meant to be served over an HTTP server.\n' + + ' Opening index.html over file:// won\'t work.\n' + )) +}) diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-frontend/build/build.js b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/build/build.js new file mode 100644 index 0000000000..8f2ad8ad49 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/build/build.js @@ -0,0 +1,41 @@ +'use strict' +require('./check-versions')() + +process.env.NODE_ENV = 'production' + +const ora = require('ora') +const rm = require('rimraf') +const path = require('path') +const chalk = require('chalk') +const webpack = require('webpack') +const config = require('../config') +const webpackConfig = require('./webpack.prod.conf') + +const spinner = ora('building for production...') +spinner.start() + +rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { + if (err) throw err + webpack(webpackConfig, (err, stats) => { + spinner.stop() + if (err) throw err + process.stdout.write(stats.toString({ + colors: true, + modules: false, + children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build. + chunks: false, + chunkModules: false + }) + '\n\n') + + if (stats.hasErrors()) { + console.log(chalk.red(' Build failed with errors.\n')) + process.exit(1) + } + + console.log(chalk.cyan(' Build complete.\n')) + console.log(chalk.yellow( + ' Tip: built files are meant to be served over an HTTP server.\n' + + ' Opening index.html over file:// won\'t work.\n' + )) + }) +}) diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-frontend/build/check-versions.js b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/build/check-versions.js new file mode 100644 index 0000000000..3ef972a08d --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/build/check-versions.js @@ -0,0 +1,54 @@ +'use strict' +const chalk = require('chalk') +const semver = require('semver') +const packageConfig = require('../package.json') +const shell = require('shelljs') + +function exec (cmd) { + return require('child_process').execSync(cmd).toString().trim() +} + +const versionRequirements = [ + { + name: 'node', + currentVersion: semver.clean(process.version), + versionRequirement: packageConfig.engines.node + } +] + +if (shell.which('npm')) { + versionRequirements.push({ + name: 'npm', + currentVersion: exec('npm --version'), + versionRequirement: packageConfig.engines.npm + }) +} + +module.exports = function () { + const warnings = [] + + for (let i = 0; i < versionRequirements.length; i++) { + const mod = versionRequirements[i] + + if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { + warnings.push(mod.name + ': ' + + chalk.red(mod.currentVersion) + ' should be ' + + chalk.green(mod.versionRequirement) + ) + } + } + + if (warnings.length) { + console.log('') + console.log(chalk.yellow('To use this template, you must update following to modules:')) + console.log() + + for (let i = 0; i < warnings.length; i++) { + const warning = warnings[i] + console.log(' ' + warning) + } + + console.log() + process.exit(1) + } +} diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-frontend/build/logo.png b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/build/logo.png new file mode 100644 index 0000000000..f3d2503fc2 Binary files /dev/null and b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/build/logo.png differ diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-frontend/build/utils.js b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/build/utils.js new file mode 100644 index 0000000000..e534fb0fd6 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/build/utils.js @@ -0,0 +1,101 @@ +'use strict' +const path = require('path') +const config = require('../config') +const ExtractTextPlugin = require('extract-text-webpack-plugin') +const packageConfig = require('../package.json') + +exports.assetsPath = function (_path) { + const assetsSubDirectory = process.env.NODE_ENV === 'production' + ? config.build.assetsSubDirectory + : config.dev.assetsSubDirectory + + return path.posix.join(assetsSubDirectory, _path) +} + +exports.cssLoaders = function (options) { + options = options || {} + + const cssLoader = { + loader: 'css-loader', + options: { + sourceMap: options.sourceMap + } + } + + const postcssLoader = { + loader: 'postcss-loader', + options: { + sourceMap: options.sourceMap + } + } + + // generate loader string to be used with extract text plugin + function generateLoaders (loader, loaderOptions) { + const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader] + + if (loader) { + loaders.push({ + loader: loader + '-loader', + options: Object.assign({}, loaderOptions, { + sourceMap: options.sourceMap + }) + }) + } + + // Extract CSS when that option is specified + // (which is the case during production build) + if (options.extract) { + return ExtractTextPlugin.extract({ + use: loaders, + fallback: 'vue-style-loader' + }) + } else { + return ['vue-style-loader'].concat(loaders) + } + } + + // https://vue-loader.vuejs.org/en/configurations/extract-css.html + return { + css: generateLoaders(), + postcss: generateLoaders(), + less: generateLoaders('less'), + sass: generateLoaders('sass', { indentedSyntax: true }), + scss: generateLoaders('sass'), + stylus: generateLoaders('stylus'), + styl: generateLoaders('stylus') + } +} + +// Generate loaders for standalone style files (outside of .vue) +exports.styleLoaders = function (options) { + const output = [] + const loaders = exports.cssLoaders(options) + + for (const extension in loaders) { + const loader = loaders[extension] + output.push({ + test: new RegExp('\\.' + extension + '$'), + use: loader + }) + } + + return output +} + +exports.createNotifierCallback = () => { + const notifier = require('node-notifier') + + return (severity, errors) => { + if (severity !== 'error') return + + const error = errors[0] + const filename = error.file && error.file.split('!').pop() + + notifier.notify({ + title: packageConfig.name, + message: severity + ': ' + error.name, + subtitle: filename || '', + icon: path.join(__dirname, 'logo.png') + }) + } +} diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-frontend/build/vue-loader.conf.js b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/build/vue-loader.conf.js new file mode 100644 index 0000000000..33ed58bc0a --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/build/vue-loader.conf.js @@ -0,0 +1,22 @@ +'use strict' +const utils = require('./utils') +const config = require('../config') +const isProduction = process.env.NODE_ENV === 'production' +const sourceMapEnabled = isProduction + ? config.build.productionSourceMap + : config.dev.cssSourceMap + +module.exports = { + loaders: utils.cssLoaders({ + sourceMap: sourceMapEnabled, + extract: isProduction + }), + cssSourceMap: sourceMapEnabled, + cacheBusting: config.dev.cacheBusting, + transformToRequire: { + video: ['src', 'poster'], + source: 'src', + img: 'src', + image: 'xlink:href' + } +} diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-frontend/build/webpack.async-plugins.js b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/build/webpack.async-plugins.js new file mode 100644 index 0000000000..43dfa8b1b0 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/build/webpack.async-plugins.js @@ -0,0 +1,85 @@ +const webpack = require('webpack') +const path = require('path') +const utils = require('./utils') +const CopyPlugin = require("copy-webpack-plugin"); +function resolve (dir) { + return path.join(__dirname, '..', dir) +} + +module.exports = { + entry: { + 'kylin': resolve('/src/views/kylin.vue') + }, + output: { + path: resolve('/static/'), + filename: '[name].js' + }, + resolve: { + extensions: ['.js', '.vue', '.json'], + alias: { + 'vue$': 'vue/dist/vue.esm.js', + '@': resolve('src') + } + }, + externals: { + vue: 'vue' + }, + module: { + rules: [ + { + test: /\.vue$/, + loader: 'vue-loader', + options: { + esModule: false, // vue-loader v13 更新 默认值为 true v12及之前版本为 false, 此项配置影响 vue 自身异步组件写法以及 webpack 打包结果 + loaders: utils.cssLoaders({ + sourceMap: true, + extract: false // css 不做提取 + }), + transformToRequire: { + video: 'src', + source: 'src', + img: 'src', + image: 'xlink:href' + } + } + }, + { + test: /\.js$/, + loader: 'babel-loader', + include: [resolve('src'), resolve('test')] + }, + { + test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('img/[name].[hash:7].[ext]') + } + }, + { + test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('media/[name].[hash:7].[ext]') + } + }, + { + test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('fonts/[name].[hash:7].[ext]') + } + } + ] + }, + plugins: [ + new webpack.DefinePlugin({ + 'process.env.NODE_ENV': '"production"' + }), + new CopyPlugin([ + {from: 'src/icons/svg/'} + ]), + ] +} diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-frontend/build/webpack.base.conf.js b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/build/webpack.base.conf.js new file mode 100644 index 0000000000..7ef785a39e --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/build/webpack.base.conf.js @@ -0,0 +1,103 @@ +'use strict' +const path = require('path') +const utils = require('./utils') +const config = require('../config') +const vueLoaderConfig = require('./vue-loader.conf') + +function resolve (dir) { + return path.join(__dirname, '..', dir) +} + + + +module.exports = { + context: path.resolve(__dirname, '../'), + entry: { + app: './src/main.js' + }, + output: { + path: config.build.assetsRoot, + filename: '[name].js', + publicPath: process.env.NODE_ENV === 'production' + ? config.build.assetsPublicPath + : config.dev.assetsPublicPath + }, + resolve: { + extensions: ['.js', '.vue', '.json'], + alias: { + 'vue$': 'vue/dist/vue.esm.js', + '@': resolve('src'), + } + }, + module: { + rules: [ + { + test: /\.vue$/, + loader: 'vue-loader', + options: vueLoaderConfig + }, + { + test: /\.js$/, + loader: 'babel-loader', + include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')] + }, + { + test: /\.svg$/, + loader: 'svg-sprite-loader', + include: [resolve('src/icons')], + options: { + symbolId: 'icon-[name]' + } + }, + { + test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, + loader: 'url-loader', + exclude: [resolve('src/icons')], + options: { + limit: 10000, + name: utils.assetsPath('img/[name].[hash:7].[ext]') + } + }, + { + test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('media/[name].[hash:7].[ext]') + } + }, + { + test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('fonts/[name].[hash:7].[ext]') + } + }, + { + test: /\.svg$/, + include: [path.resolve('src/icons')], + use: [ + { + loader: 'svg-sprite-loader', + options: { + symbolId: 'icon-[name]', + }, + } + ], + } + ] + }, + node: { + // prevent webpack from injecting useless setImmediate polyfill because Vue + // source contains it (although only uses it if it's native). + setImmediate: false, + // prevent webpack from injecting mocks to Node native modules + // that does not make sense for the client + dgram: 'empty', + fs: 'empty', + net: 'empty', + tls: 'empty', + child_process: 'empty' + } +} diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-frontend/build/webpack.dev.conf.js b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/build/webpack.dev.conf.js new file mode 100755 index 0000000000..070ae221f3 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/build/webpack.dev.conf.js @@ -0,0 +1,95 @@ +'use strict' +const utils = require('./utils') +const webpack = require('webpack') +const config = require('../config') +const merge = require('webpack-merge') +const path = require('path') +const baseWebpackConfig = require('./webpack.base.conf') +const CopyWebpackPlugin = require('copy-webpack-plugin') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') +const portfinder = require('portfinder') + +const HOST = process.env.HOST +const PORT = process.env.PORT && Number(process.env.PORT) + +const devWebpackConfig = merge(baseWebpackConfig, { + module: { + rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) + }, + // cheap-module-eval-source-map is faster for development + devtool: config.dev.devtool, + + // these devServer options should be customized in /config/index.js + devServer: { + clientLogLevel: 'warning', + historyApiFallback: { + rewrites: [ + { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') }, + ], + }, + hot: true, + contentBase: false, // since we use CopyWebpackPlugin. + compress: true, + host: HOST || config.dev.host, + port: PORT || config.dev.port, + open: config.dev.autoOpenBrowser, + overlay: config.dev.errorOverlay + ? { warnings: false, errors: true } + : false, + publicPath: config.dev.assetsPublicPath, + proxy: config.dev.proxyTable, + quiet: true, // necessary for FriendlyErrorsPlugin + watchOptions: { + poll: config.dev.poll, + } + }, + plugins: [ + new webpack.DefinePlugin({ + 'process.env': require('../config/dev.env') + }), + new webpack.HotModuleReplacementPlugin(), + new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update. + new webpack.NoEmitOnErrorsPlugin(), + // https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: 'index.html', + template: 'index.html', + inject: true + }), + // copy custom static assets + new CopyWebpackPlugin([ + { + from: path.resolve(__dirname, '../static'), + to: config.dev.assetsSubDirectory, + ignore: ['.*'] + } + ]) + ] +}) + +module.exports = new Promise((resolve, reject) => { + portfinder.basePort = process.env.PORT || config.dev.port + portfinder.getPort((err, port) => { + if (err) { + reject(err) + } else { + // publish the new Port, necessary for e2e tests + process.env.PORT = port + // add port to devServer config + devWebpackConfig.devServer.port = port + + // Add FriendlyErrorsPlugin + devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({ + compilationSuccessInfo: { + messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`], + }, + onErrors: config.dev.notifyOnErrors + ? utils.createNotifierCallback() + : undefined + })) + + resolve(devWebpackConfig) + } + }) +}) diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-frontend/build/webpack.prod.conf.js b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/build/webpack.prod.conf.js new file mode 100644 index 0000000000..3e25238ef6 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/build/webpack.prod.conf.js @@ -0,0 +1,145 @@ +'use strict' +const path = require('path') +const utils = require('./utils') +const webpack = require('webpack') +const config = require('../config') +const merge = require('webpack-merge') +const baseWebpackConfig = require('./webpack.base.conf') +const CopyWebpackPlugin = require('copy-webpack-plugin') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const ExtractTextPlugin = require('extract-text-webpack-plugin') +const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') +const UglifyJsPlugin = require('uglifyjs-webpack-plugin') + +const env = require('../config/prod.env') + +const webpackConfig = merge(baseWebpackConfig, { + module: { + rules: utils.styleLoaders({ + sourceMap: config.build.productionSourceMap, + extract: true, + usePostCSS: true + }) + }, + devtool: config.build.productionSourceMap ? config.build.devtool : false, + output: { + path: config.build.assetsRoot, + filename: utils.assetsPath('js/[name].[chunkhash].js'), + chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') + }, + plugins: [ + // http://vuejs.github.io/vue-loader/en/workflow/production.html + new webpack.DefinePlugin({ + 'process.env': env + }), + new UglifyJsPlugin({ + uglifyOptions: { + compress: { + warnings: false + } + }, + sourceMap: config.build.productionSourceMap, + parallel: true + }), + // extract css into its own file + new ExtractTextPlugin({ + filename: utils.assetsPath('css/[name].[contenthash].css'), + // Setting the following option to `false` will not extract CSS from codesplit chunks. + // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack. + // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, + // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110 + allChunks: true, + }), + // Compress extracted CSS. We are using this plugin so that possible + // duplicated CSS from different components can be deduped. + new OptimizeCSSPlugin({ + cssProcessorOptions: config.build.productionSourceMap + ? { safe: true, map: { inline: false } } + : { safe: true } + }), + // generate dist index.html with correct asset hash for caching. + // you can customize output by editing /index.html + // see https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: config.build.index, + template: 'index.html', + inject: true, + minify: { + removeComments: true, + collapseWhitespace: true, + removeAttributeQuotes: true + // more options: + // https://github.com/kangax/html-minifier#options-quick-reference + }, + // necessary to consistently work with multiple chunks via CommonsChunkPlugin + chunksSortMode: 'dependency' + }), + // keep module.id stable when vendor modules does not change + new webpack.HashedModuleIdsPlugin(), + // enable scope hoisting + new webpack.optimize.ModuleConcatenationPlugin(), + // split vendor js into its own file + new webpack.optimize.CommonsChunkPlugin({ + name: 'vendor', + minChunks (module) { + // any required modules inside node_modules are extracted to vendor + return ( + module.resource && + /\.js$/.test(module.resource) && + module.resource.indexOf( + path.join(__dirname, '../node_modules') + ) === 0 + ) + } + }), + // extract webpack runtime and module manifest to its own file in order to + // prevent vendor hash from being updated whenever app bundle is updated + new webpack.optimize.CommonsChunkPlugin({ + name: 'manifest', + minChunks: Infinity + }), + // This instance extracts shared chunks from code split chunks and bundles them + // in a separate chunk, similar to the vendor chunk + // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk + new webpack.optimize.CommonsChunkPlugin({ + name: 'app', + async: 'vendor-async', + children: true, + minChunks: 3 + }), + + // copy custom static assets + new CopyWebpackPlugin([ + { + from: path.resolve(__dirname, '../static'), + to: config.build.assetsSubDirectory, + ignore: ['.*'] + } + ]) + ] +}) + +if (config.build.productionGzip) { + const CompressionWebpackPlugin = require('compression-webpack-plugin') + + webpackConfig.plugins.push( + new CompressionWebpackPlugin({ + asset: '[path].gz[query]', + algorithm: 'gzip', + test: new RegExp( + '\\.(' + + config.build.productionGzipExtensions.join('|') + + ')$' + ), + threshold: 10240, + minRatio: 0.8 + }) + ) +} + +if (config.build.bundleAnalyzerReport) { + const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin + webpackConfig.plugins.push(new BundleAnalyzerPlugin()) +} + +module.exports = webpackConfig diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-frontend/config/dev.env.js b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/config/dev.env.js new file mode 100644 index 0000000000..1e22973ae7 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/config/dev.env.js @@ -0,0 +1,7 @@ +'use strict' +const merge = require('webpack-merge') +const prodEnv = require('./prod.env') + +module.exports = merge(prodEnv, { + NODE_ENV: '"development"' +}) diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-frontend/config/index.js b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/config/index.js new file mode 100644 index 0000000000..c5eded7f81 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/config/index.js @@ -0,0 +1,69 @@ +'use strict' +// Template version: 1.3.1 +// see http://vuejs-templates.github.io/webpack for documentation. + +const path = require('path') + +module.exports = { + dev: { + + // Paths + assetsSubDirectory: 'static', + assetsPublicPath: '/', + proxyTable: {}, + + // Various Dev Server settings + host: 'localhost', // can be overwritten by process.env.HOST + port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined + autoOpenBrowser: false, + errorOverlay: true, + notifyOnErrors: true, + poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- + + + /** + * Source Maps + */ + + // https://webpack.js.org/configuration/devtool/#development + devtool: 'cheap-module-eval-source-map', + + // If you have problems debugging vue-files in devtools, + // set this to false - it *may* help + // https://vue-loader.vuejs.org/en/options.html#cachebusting + cacheBusting: true, + + cssSourceMap: true + }, + + build: { + // Template for index.html + index: path.resolve(__dirname, '../dist/index.html'), + + // Paths + assetsRoot: path.resolve(__dirname, '../dist'), + assetsSubDirectory: 'static', + assetsPublicPath: '/', + + /** + * Source Maps + */ + + productionSourceMap: true, + // https://webpack.js.org/configuration/devtool/#production + devtool: '#source-map', + + // Gzip off by default as many popular static hosts such as + // Surge or Netlify already gzip all static assets for you. + // Before setting to `true`, make sure to: + // npm install --save-dev compression-webpack-plugin + productionGzip: false, + productionGzipExtensions: ['js', 'css'], + + // Run the build command with an extra argument to + // View the bundle analyzer report after build finishes: + // `npm run build --report` + // Set to `true` or `false` to always turn it on or off + bundleAnalyzerReport: process.env.npm_config_report + } +} diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-frontend/config/prod.env.js b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/config/prod.env.js new file mode 100644 index 0000000000..a6f997616e --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/config/prod.env.js @@ -0,0 +1,4 @@ +'use strict' +module.exports = { + NODE_ENV: '"production"' +} diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-frontend/index.html b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/index.html new file mode 100644 index 0000000000..03ce3fdf27 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/index.html @@ -0,0 +1,12 @@ + + + + + + deplugin-view-frontend + + +
+ + + diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-frontend/package.json b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/package.json new file mode 100644 index 0000000000..d75d0e3c44 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/package.json @@ -0,0 +1,75 @@ +{ + "name": "deplugin-datasource-frontend", + "version": "1.0.0", + "description": "A Vue.js project", + "author": "", + "private": true, + "scripts": { + "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", + "start": "npm run dev", + "build": "node build/build.js", + "buildPlugin": "node build/build-async-plugins.js" + }, + "dependencies": { + "@riophae/vue-treeselect": "0.4.0", + "highcharts": "^10.0.0", + "svg-sprite-loader": "^6.0.11", + "svgo": "1.2.2", + "svgo-loader": "^3.0.1", + "vue": "^2.5.2", + "vue-i18n": "7.3.2", + "vue-router": "^3.0.1", + "vue-uuid": "2.0.2", + "vuedraggable": "^2.24.3" + }, + "devDependencies": { + "autoprefixer": "^7.1.2", + "babel-core": "^6.22.1", + "babel-helper-vue-jsx-merge-props": "^2.0.3", + "babel-loader": "^7.1.1", + "babel-plugin-syntax-jsx": "^6.18.0", + "babel-plugin-transform-runtime": "^6.22.0", + "babel-plugin-transform-vue-jsx": "^3.5.0", + "babel-preset-env": "^1.3.2", + "babel-preset-stage-2": "^6.22.0", + "chalk": "^2.0.1", + "copy-webpack-plugin": "^4.6.0", + "css-loader": "^0.28.0", + "element-ui": "2.15.7", + "extract-text-webpack-plugin": "^3.0.0", + "file-loader": "^1.1.4", + "friendly-errors-webpack-plugin": "^1.6.1", + "html-webpack-plugin": "^2.30.1", + "js-cookie": "2.2.0", + "node-notifier": "^8.0.1", + "optimize-css-assets-webpack-plugin": "^3.2.0", + "ora": "^1.2.0", + "portfinder": "^1.0.13", + "postcss-import": "^11.0.0", + "postcss-loader": "^2.0.8", + "postcss-url": "^7.2.1", + "rimraf": "^2.6.0", + "sass": "^1.33.0", + "sass-loader": "^7.3.1", + "semver": "^5.3.0", + "shelljs": "^0.8.5", + "uglifyjs-webpack-plugin": "^1.1.1", + "url-loader": "^0.5.8", + "vue-loader": "^13.3.0", + "vue-style-loader": "^3.0.1", + "vue-template-compiler": "^2.5.2", + "webpack": "^3.6.0", + "webpack-bundle-analyzer": "^3.3.2", + "webpack-dev-server": "^3.1.11", + "webpack-merge": "^4.1.0" + }, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + }, + "browserslist": [ + "> 1%", + "last 2 versions", + "not ie <= 8" + ] +} diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-frontend/pom.xml b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/pom.xml new file mode 100644 index 0000000000..c2f2313802 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/pom.xml @@ -0,0 +1,80 @@ + + + + + io.dataease + kylin + ${dataease.version} + + + 4.0.0 + kylin-frontend + + + UTF-8 + UTF-8 + 1.9.1 + + + + + + maven-clean-plugin + + + + static + + ** + + false + + + + + + + + com.github.eirslett + frontend-maven-plugin + ${frontend-maven-plugin.version} + + + install node and npm + + install-node-and-npm + + + + v16.20.2 + 7.6.3 + + + + + npm install + + npm + + + + install --force + + + + + npm run buildPlugin + + npm + + + run buildPlugin + + + + + + + diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/App.vue b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/App.vue new file mode 100644 index 0000000000..8542e07722 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/App.vue @@ -0,0 +1,23 @@ + + + + + diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/assets/logo.png b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/assets/logo.png new file mode 100644 index 0000000000..f3d2503fc2 Binary files /dev/null and b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/assets/logo.png differ diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/components/HelloWorld.vue b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/components/HelloWorld.vue new file mode 100644 index 0000000000..1c19f2a399 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/components/HelloWorld.vue @@ -0,0 +1,113 @@ + + + + + + diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/de-base/lang/en.js b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/de-base/lang/en.js new file mode 100644 index 0000000000..0fcfd86f22 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/de-base/lang/en.js @@ -0,0 +1,19 @@ +export default { + plugin_view_3d_pie: { + type_title: '3D-PIE', + label: 'Label', + angle: 'Angle' + }, + host: 'Host', + port: 'Port', + dataBase: 'Catalog', + schema: 'Schema', + username: 'User', + password: 'Password', + query_timeout: 'Query timeout (seconds)', + second: 'second', + please_select: 'Please select', + enter_the_port: 'Please enter the port', + one_user_name: 'enter one user name', + input_a_password: 'Please input a password' +} diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/de-base/lang/index.js b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/de-base/lang/index.js new file mode 100644 index 0000000000..e50d0478e6 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/de-base/lang/index.js @@ -0,0 +1,49 @@ +import Vue from 'vue' +import VueI18n from 'vue-i18n' +import Cookies from 'js-cookie' +import elementEnLocale from 'element-ui/lib/locale/lang/en' // element-ui lang +import elementZhLocale from 'element-ui/lib/locale/lang/zh-CN'// element-ui lang +import elementTWLocale from 'element-ui/lib/locale/lang/zh-TW'// element-ui lang + +import localMessages from './messages' + + +Vue.use(VueI18n) + +const messages = { + en_US: { + ...localMessages['en_US'], + ...elementEnLocale + }, + zh_CN: { + ...localMessages['zh_CN'], + ...elementZhLocale + }, + zh_TW: { + ...localMessages['zh_TW'], + ...elementTWLocale + } +} +export function getLanguage () { + const chooseLanguage = Cookies.get('language') + if (chooseLanguage) return chooseLanguage + + // if has not choose language + const language = (navigator.language || navigator.browserLanguage).toLowerCase() + const locales = Object.keys(messages) + for (const locale of locales) { + if (language.indexOf(locale) > -1) { + return locale + } + } + return 'zh_CN' +} +const i18n = new VueI18n({ + // set locale + // options: en | zh | es + locale: getLanguage(), + // set locale messages + messages +}) + +export default i18n diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/de-base/lang/messages.js b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/de-base/lang/messages.js new file mode 100644 index 0000000000..844f8c0673 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/de-base/lang/messages.js @@ -0,0 +1,17 @@ +import enLocale from './en' +import zhLocale from './zh' +import twLocale from './tw' + +const messages = { + en_US: { + ...enLocale + }, + zh_CN: { + ...zhLocale + }, + zh_TW: { + ...twLocale + } +} + +export default messages \ No newline at end of file diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/de-base/lang/tw.js b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/de-base/lang/tw.js new file mode 100644 index 0000000000..c4dc7d0b2a --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/de-base/lang/tw.js @@ -0,0 +1,19 @@ +export default { + plugin_view_3d_pie: { + type_title: '3D餅圖', + label: '標籤', + angle: '角度' + }, + host: '主機名', + port: '端口', + dataBase: '數據庫', + schema: 'Schema', + username: '用戶名', + password: '密碼', + query_timeout: '査詢超時(秒)', + second: '秒', + please_select: '請選擇', + enter_the_port: '請輸入埠', + one_user_name: '請輸入用戶名', + input_a_password: '請輸入密碼' +} diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/de-base/lang/zh.js b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/de-base/lang/zh.js new file mode 100644 index 0000000000..165700b887 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/de-base/lang/zh.js @@ -0,0 +1,19 @@ +export default { + plugin_view_3d_pie: { + type_title: '3D饼图', + label: '标签', + angle: '角度' + }, + host: '主机名', + port: '端口', + dataBase: '数据库', + schema: 'Schema', + username: '用户名', + password: '密码', + query_timeout: '查询超时(秒)', + second: '秒', + please_select: '请选择', + enter_the_port: '请输入端口', + one_user_name: '请输入用户名', + input_a_password: '请输入密码' +} diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/icons/index.js b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/icons/index.js new file mode 100644 index 0000000000..2c6b309c96 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/icons/index.js @@ -0,0 +1,9 @@ +import Vue from 'vue' +import SvgIcon from '@/components/SvgIcon'// svg component + +// register globally +Vue.component('svg-icon', SvgIcon) + +const req = require.context('./svg', false, /\.svg$/) +const requireAll = requireContext => requireContext.keys().map(requireContext) +requireAll(req) diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/icons/svg/de_pwd_invisible.svg b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/icons/svg/de_pwd_invisible.svg new file mode 100644 index 0000000000..661ebba33e --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/icons/svg/de_pwd_invisible.svg @@ -0,0 +1,3 @@ + + + diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/icons/svg/de_pwd_visible.svg b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/icons/svg/de_pwd_visible.svg new file mode 100644 index 0000000000..4e7ba5e799 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/icons/svg/de_pwd_visible.svg @@ -0,0 +1,3 @@ + + + diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/icons/svg/kylin-backend.svg b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/icons/svg/kylin-backend.svg new file mode 100644 index 0000000000..a8de85ce6e --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/icons/svg/kylin-backend.svg @@ -0,0 +1 @@ +【icon】插件管理-导出 \ No newline at end of file diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/icons/svg/kylin.jpg b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/icons/svg/kylin.jpg new file mode 100644 index 0000000000..299e0af8e8 Binary files /dev/null and b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/icons/svg/kylin.jpg differ diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/icons/svgo.yml b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/icons/svgo.yml new file mode 100644 index 0000000000..d11906aec2 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/icons/svgo.yml @@ -0,0 +1,22 @@ +# replace default config + +# multipass: true +# full: true + +plugins: + + # - name + # + # or: + # - name: false + # - name: true + # + # or: + # - name: + # param1: 1 + # param2: 2 + +- removeAttrs: + attrs: + - 'fill' + - 'fill-rule' diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/main.js b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/main.js new file mode 100644 index 0000000000..99c5626590 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/main.js @@ -0,0 +1,33 @@ +// The Vue build version to load with the `import` command +// (runtime-only or standalone) has been set in webpack.base.conf with an alias. +import Vue from 'vue' +import App from './App' +import router from './router' +import ElementUI from 'element-ui' +import Cookies from 'js-cookie' +import i18n from './de-base/lang' +import draggable from 'vuedraggable' +import Treeselect from '@riophae/vue-treeselect' +import '@riophae/vue-treeselect/dist/vue-treeselect.css' +Vue.config.productionTip = false +Vue.use(ElementUI, { + size: Cookies.get('size') || 'medium', + i18n: (key, value) => i18n.t(key, value) +}) +Vue.component('Treeselect', Treeselect) +Vue.component('draggable', draggable) +Vue.prototype.hasDataPermission = function(pTarget, pSource) { + + if (pSource && pTarget) { + return pSource.indexOf(pTarget) > -1 + } + return false +} +/* eslint-disable no-new */ +new Vue({ + el: '#app', + router, + i18n, + components: { App }, + template: '' +}) diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/router/index.js b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/router/index.js new file mode 100644 index 0000000000..b5b55fa1fc --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/router/index.js @@ -0,0 +1,21 @@ +import Vue from 'vue' +import Router from 'vue-router' +import HelloWorld from '@/components/HelloWorld' +import maxcompute from '@/views/maxcompute' + +Vue.use(Router) + +export default new Router({ + routes: [ + { + path: '/', + name: 'HelloWorld', + component: HelloWorld + }, + { + path: '/maxcompute', + name: 'maxcompute', + component: maxcompute + } + ] +}) diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/utils/compare.js b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/utils/compare.js new file mode 100644 index 0000000000..b06a104a10 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/utils/compare.js @@ -0,0 +1,29 @@ +export const compareItem = { + type: 'none', // year-yoy/month-yoy等 + resultData: 'percent', // 对比差sub,百分比percent等 + field: '', + custom: { + field: '', + calcType: '0', // 0-增长值,1-增长率 + timeType: '0', // 0-固定日期,1-日期区间 + currentTime: '', + compareTime: '', + currentTimeRange: [], + compareTimeRange: [] + } +} + +export const compareYearList = [ + { name: 'year_mom', value: 'year_mom' } +] + +export const compareMonthList = [ + { name: 'month_mom', value: 'month_mom' }, + { name: 'year_yoy', value: 'year_yoy' } +] + +export const compareDayList = [ + { name: 'day_mom', value: 'day_mom' }, + { name: 'month_yoy', value: 'month_yoy' }, + { name: 'year_yoy', value: 'year_yoy' } +] diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/utils/validate.js b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/utils/validate.js new file mode 100644 index 0000000000..3e8ffa9448 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/utils/validate.js @@ -0,0 +1,15 @@ +/** + * Created by PanJiaChen on 16/11/18. + */ + +/** + * @param {string} path + * @returns {Boolean} + */ +export function isExternal(path) { + return /^(https?:|mailto:|tel:)/.test(path) || /^(http?:|mailto:|tel:)/.test(path) || path.startsWith('/api/pluginCommon/staticInfo') +} + + + + diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/views/dePwd.vue b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/views/dePwd.vue new file mode 100644 index 0000000000..61bf3e88ee --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/views/dePwd.vue @@ -0,0 +1,75 @@ + + + + \ No newline at end of file diff --git a/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/views/kylin.vue b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/views/kylin.vue new file mode 100644 index 0000000000..c327be8d96 --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/kylin-frontend/src/views/kylin.vue @@ -0,0 +1,163 @@ + + + + + diff --git a/extensions/dataease-extensions-datasource/kylin/plugin.json b/extensions/dataease-extensions-datasource/kylin/plugin.json new file mode 100644 index 0000000000..f8258694ad --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/plugin.json @@ -0,0 +1,13 @@ +{ + "name":"Kylin 数据源插件", + "free":0, + "store":"default", + "cost":0, + "category":"datasource", + "descript":"Kylin 插件,值得拥有", + "version":"1.18.0", + "creator":"DATAEASE", + "moduleName":"kylin-backend", + "require":"1.16.0", + "dsType":"kylin" +} diff --git a/extensions/dataease-extensions-datasource/kylin/pom.xml b/extensions/dataease-extensions-datasource/kylin/pom.xml new file mode 100644 index 0000000000..57779a138e --- /dev/null +++ b/extensions/dataease-extensions-datasource/kylin/pom.xml @@ -0,0 +1,19 @@ + + + + dataease-extensions-datasource + io.dataease + ${dataease.version} + + 4.0.0 + + kylin + pom + + kylin-frontend + kylin-backend + + + diff --git a/extensions/dataease-extensions-datasource/maxcompute/.gitignore b/extensions/dataease-extensions-datasource/maxcompute/.gitignore new file mode 100644 index 0000000000..a5a2b0c056 --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/.gitignore @@ -0,0 +1,35 @@ +# Created by .ignore support plugin (hsz.mobi) +.DS_Store +node_modules +node/ +/dist + +# local env files +.env.local +.env.*.local + +# Log files +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Editor directories and files +.idea +*.iml +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + + +src/main/resources/static +src/main/resources/templates +src/test/ +target +.settings +.project +.classpath +.factorypath +src/main/resources/jmeter/lib/ \ No newline at end of file diff --git a/extensions/dataease-extensions-datasource/maxcompute/build.sh b/extensions/dataease-extensions-datasource/maxcompute/build.sh new file mode 100755 index 0000000000..a765c3b02b --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/build.sh @@ -0,0 +1,6 @@ +#!/bin/sh +mvn clean package + +cp maxcompute-backend/target/maxcompute-backend-1.18.0.jar . + +zip -r maxcompute.zip ./maxcompute-backend-1.18.0.jar ./maxcomputeDriver ./plugin.json diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-backend/pom.xml b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-backend/pom.xml new file mode 100644 index 0000000000..be472e9f84 --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-backend/pom.xml @@ -0,0 +1,109 @@ + + + + maxcompute + io.dataease + ${dataease.version} + + 4.0.0 + + maxcompute-backend + + + + io.dataease + dataease-plugin-datasource + + + + + + + src/main/java + + **/*.properties + **/*.xml + + false + + + src/main/resources + + **/* + + false + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 11 + 11 + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.4 + + + **/server/** + **/*.properties + **/Application* + + + + + + maven-clean-plugin + + + + src/main/resources/static + + ** + + false + + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + main-class-placement + generate-resources + + + + + + + + + + + + run + + + + + + + + + + + diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-backend/src/main/java/io/dataease/plugins/datasource/dm/provider/MaxcomputeConfig.java b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-backend/src/main/java/io/dataease/plugins/datasource/dm/provider/MaxcomputeConfig.java new file mode 100644 index 0000000000..16d3a7ffab --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-backend/src/main/java/io/dataease/plugins/datasource/dm/provider/MaxcomputeConfig.java @@ -0,0 +1,32 @@ +package io.dataease.plugins.datasource.dm.provider; + +import io.dataease.plugins.datasource.entity.JdbcConfiguration; +import lombok.Getter; +import lombok.Setter; +import org.apache.commons.lang3.StringUtils; + +@Getter +@Setter +public class MaxcomputeConfig extends JdbcConfiguration { + + private String driver = "com.aliyun.odps.jdbc.OdpsDriver"; + private String projectName; + private String access_id; + private String access_key; + private String end_point; + private String extraParams; + + + public String getJdbc() { + if(StringUtils.isEmpty(getExtraParams())){ + return "jdbc:odps:END_POINT?project=PROJECT_NAME" + .replace("END_POINT", getEnd_point().trim()) + .replace("PROJECT_NAME", getProjectName().trim()); + }else { + return "jdbc:odps:END_POINT?project=PROJECT_NAME&EXTRA_PARAMS" + .replace("END_POINT", getEnd_point().trim()) + .replace("PROJECT_NAME", getProjectName().trim()) + .replace("EXTRA_PARAMS", getExtraParams().trim()); + } + } +} diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-backend/src/main/java/io/dataease/plugins/datasource/dm/provider/MaxcomputeDsProvider.java b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-backend/src/main/java/io/dataease/plugins/datasource/dm/provider/MaxcomputeDsProvider.java new file mode 100644 index 0000000000..9f0b324390 --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-backend/src/main/java/io/dataease/plugins/datasource/dm/provider/MaxcomputeDsProvider.java @@ -0,0 +1,104 @@ +package io.dataease.plugins.datasource.dm.provider; + +import com.google.gson.Gson; +import io.dataease.plugins.common.base.domain.DeDriver; +import io.dataease.plugins.common.base.mapper.DeDriverMapper; +import io.dataease.plugins.common.dto.datasource.TableDesc; +import io.dataease.plugins.common.dto.datasource.TableField; +import io.dataease.plugins.common.exception.DataEaseException; +import io.dataease.plugins.common.request.datasource.DatasourceRequest; +import io.dataease.plugins.datasource.entity.JdbcConfiguration; +import io.dataease.plugins.datasource.provider.DefaultJdbcProvider; +import io.dataease.plugins.datasource.provider.ExtendedJdbcClassLoader; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.sql.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + + +@Component() +public class MaxcomputeDsProvider extends DefaultJdbcProvider { + + @Resource + private DeDriverMapper deDriverMapper; + + @Override + public String getType() { + return "maxcompute"; + } + + @Override + public boolean isUseDatasourcePool() { + return false; + } + + @Override + public Connection getConnection(DatasourceRequest datasourceRequest) throws Exception { + + MaxcomputeConfig maxcomputeConfig = new Gson().fromJson(datasourceRequest.getDatasource().getConfiguration(), MaxcomputeConfig.class); + String username = maxcomputeConfig.getAccess_id(); + String password = maxcomputeConfig.getAccess_key(); + + Properties props = new Properties(); + if (StringUtils.isNotBlank(username)) { + props.setProperty("user", username); + if (StringUtils.isNotBlank(password)) { + props.setProperty("password", password); + } + } + + String defaultDriver = maxcomputeConfig.getDriver(); + String customDriver = maxcomputeConfig.getCustomDriver(); + String url = maxcomputeConfig.getJdbc(); + + DeDriver deDriver; + String driverClassName ; + ExtendedJdbcClassLoader jdbcClassLoader; + if(!isDefaultClassLoader(customDriver)){ + deDriver = deDriverMapper.selectByPrimaryKey(customDriver); + driverClassName = deDriver.getDriverClass(); + jdbcClassLoader = getCustomJdbcClassLoader(deDriver); + }else { + driverClassName = defaultDriver; + jdbcClassLoader = extendedJdbcClassLoader; + } + + Driver driverClass = (Driver) jdbcClassLoader.loadClass(driverClassName).newInstance(); + Connection conn = driverClass.connect(url, props); + return conn; + } + + @Override + public List getTables(DatasourceRequest datasourceRequest) throws Exception { + List tables = new ArrayList<>(); + String queryStr = getTablesSql(datasourceRequest); + JdbcConfiguration jdbcConfiguration = new Gson().fromJson(datasourceRequest.getDatasource().getConfiguration(), JdbcConfiguration.class); + int queryTimeout = jdbcConfiguration.getQueryTimeout() > 0 ? jdbcConfiguration.getQueryTimeout() : 0; + try (Connection con = getConnectionFromPool(datasourceRequest); Statement statement = getStatement(con, queryTimeout); ResultSet resultSet = statement.executeQuery(queryStr)) { + while (resultSet.next()) { + tables.add(getTableDesc(datasourceRequest, resultSet)); + } + } catch (Exception e) { + DataEaseException.throwException(e); + } + + return tables; + } + + private TableDesc getTableDesc(DatasourceRequest datasourceRequest, ResultSet resultSet) throws SQLException { + TableDesc tableDesc = new TableDesc(); + tableDesc.setName(resultSet.getString(1)); + return tableDesc; + } + + @Override + public List getTableFields(DatasourceRequest datasourceRequest) throws Exception { + datasourceRequest.setQuery("select * from " + datasourceRequest.getTable() + " limit 0"); + return fetchResultField(datasourceRequest); + } + +} diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-backend/src/main/java/io/dataease/plugins/datasource/dm/query/MaxConstants.java b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-backend/src/main/java/io/dataease/plugins/datasource/dm/query/MaxConstants.java new file mode 100644 index 0000000000..b7b80fdf69 --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-backend/src/main/java/io/dataease/plugins/datasource/dm/query/MaxConstants.java @@ -0,0 +1,45 @@ +package io.dataease.plugins.datasource.dm.query; + + + +import io.dataease.plugins.common.constants.datasource.SQLConstants; + + +public class MaxConstants extends SQLConstants { + + public static final String KEYWORD_TABLE = "%s" ; + + public static final String KEYWORD_FIX = "%s." + "%s"; + + public static final String UNIX_TIMESTAMP = "TO_MILLIS(%s)"; + + public static final String DATE_FORMAT = "to_char(%s, '%s')"; + + public static final String FROM_UNIXTIME = "from_unixtime(%s)"; + + public static final String CAST = "CAST(%s AS %s)"; + + public static final String TO_DATE = "to_date(%s, '%s')"; + + public static final String DEFAULT_DATE_FORMAT = "YYYY-MM-DD HH24:MI:SS"; + + public static final String DEFAULT_INT_FORMAT = "bigint"; + + public static final String DEFAULT_FLOAT_FORMAT = "DOUBLE"; + + public static final String WHERE_VALUE_NULL = "(NULL,'')"; + + public static final String WHERE_VALUE_VALUE = "'%s'"; + + public static final String AGG_COUNT = "COUNT(*)"; + + public static final String AGG_FIELD = "%s(%s)"; + + public static final String WHERE_BETWEEN = "'%s' AND '%s'"; + + public static final String BRACKETS = "(%s)"; + + public static final String NAME = "pg"; + + +} diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-backend/src/main/java/io/dataease/plugins/datasource/dm/query/MaxcomputeQueryProvider.java b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-backend/src/main/java/io/dataease/plugins/datasource/dm/query/MaxcomputeQueryProvider.java new file mode 100644 index 0000000000..a7e7fe14fa --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-backend/src/main/java/io/dataease/plugins/datasource/dm/query/MaxcomputeQueryProvider.java @@ -0,0 +1,1285 @@ +package io.dataease.plugins.datasource.dm.query; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.Gson; +import io.dataease.plugins.common.base.domain.ChartViewWithBLOBs; +import io.dataease.plugins.common.base.domain.DatasetTableField; +import io.dataease.plugins.common.base.domain.DatasetTableFieldExample; +import io.dataease.plugins.common.base.domain.Datasource; +import io.dataease.plugins.common.base.mapper.DatasetTableFieldMapper; +import io.dataease.plugins.common.constants.DeTypeConstants; +import io.dataease.plugins.common.constants.datasource.SQLConstants; +import io.dataease.plugins.common.constants.datasource.SqlServerSQLConstants; +import io.dataease.plugins.common.dto.chart.ChartCustomFilterItemDTO; +import io.dataease.plugins.common.dto.chart.ChartFieldCustomFilterDTO; +import io.dataease.plugins.common.dto.chart.ChartViewFieldDTO; +import io.dataease.plugins.common.dto.datasource.DeSortField; +import io.dataease.plugins.common.dto.sqlObj.SQLObj; +import io.dataease.plugins.common.request.chart.ChartExtFilterRequest; +import io.dataease.plugins.common.request.permission.DataSetRowPermissionsTreeDTO; +import io.dataease.plugins.common.request.permission.DatasetRowPermissionsTreeItem; +import io.dataease.plugins.datasource.entity.Dateformat; +import io.dataease.plugins.datasource.entity.JdbcConfiguration; +import io.dataease.plugins.datasource.entity.PageInfo; +import io.dataease.plugins.datasource.query.QueryProvider; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; +import org.stringtemplate.v4.ST; +import org.stringtemplate.v4.STGroup; +import org.stringtemplate.v4.STGroupFile; + +import javax.annotation.Resource; +import java.text.MessageFormat; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import static io.dataease.plugins.common.constants.datasource.SQLConstants.TABLE_ALIAS_PREFIX; + + +@Component() +public class MaxcomputeQueryProvider extends QueryProvider { + @Resource + private DatasetTableFieldMapper datasetTableFieldMapper; + + @Override + public Integer transFieldType(String field) { + field = field.contains("(") ? field.split("\\(")[0] : field; + switch (field) { + // 文本 + case "DATE": + case "DATETIME": + case "TIMESTAMP": + return DeTypeConstants.DE_TIME;// 时间 + case "TINYINT": + case "SMALLINT": + case "INT": + case "BIGINT": + return DeTypeConstants.DE_INT;// 整型 + case "FLOAT": + case "DOUBLE": + case "DECIMAL": + return DeTypeConstants.DE_FLOAT;// 浮点 + case "BOOLEAN": + return DeTypeConstants.DE_BOOL;// 布尔 + case "BINARY": + return DeTypeConstants.DE_BINARY;// 二进制 + default: + return DeTypeConstants.DE_STRING; + } + } + + @Override + public String createSQLPreview(String sql, String orderBy) { + return "SELECT * FROM (" + sqlFix(sql) + ") AS tmp " + " LIMIT 1000 offset 0"; + } + + @Override + public String createQuerySQL(String table, List fields, boolean isGroup, Datasource ds, List fieldCustomFilter, List rowPermissionsTree) { + return createQuerySQL(table, fields, isGroup, ds, fieldCustomFilter, rowPermissionsTree, null); + } + + @Override + public String createQuerySQL(String table, List fields, boolean isGroup, Datasource ds, List fieldCustomFilter, List rowPermissionsTree, List sortFields) { + SQLObj tableObj = SQLObj.builder() + .tableName((table.startsWith("(") && table.endsWith(")")) ? table : String.format(MaxConstants.KEYWORD_TABLE, table)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 0)) + .build(); + + + List xFields = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(fields)) { + for (int i = 0; i < fields.size(); i++) { + DatasetTableField f = fields.get(i); + String originField; + if (ObjectUtils.isNotEmpty(f.getExtField()) && f.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(f.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(f.getExtField()) && f.getExtField() == 1) { + originField = String.format(MaxConstants.KEYWORD_FIX, tableObj.getTableAlias(), f.getOriginName()); + } else { + originField = String.format(MaxConstants.KEYWORD_FIX, tableObj.getTableAlias(), f.getOriginName()); + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_X_PREFIX, i); + String fieldName = ""; + // 处理横轴字段 + if (f.getDeExtractType() == DeTypeConstants.DE_TIME) { + if (f.getDeType() == DeTypeConstants.DE_INT || f.getDeType() == DeTypeConstants.DE_FLOAT) { + fieldName = String.format(MaxConstants.UNIX_TIMESTAMP, originField); + } else { + fieldName = originField; + } + } else if (f.getDeExtractType() == DeTypeConstants.DE_STRING) { + if (f.getDeType() == DeTypeConstants.DE_INT) { + fieldName = String.format(MaxConstants.CAST, originField, MaxConstants.DEFAULT_INT_FORMAT); + } else if (f.getDeType() == DeTypeConstants.DE_FLOAT) { + fieldName = String.format(MaxConstants.CAST, originField, MaxConstants.DEFAULT_FLOAT_FORMAT); + } else if (f.getDeType() == DeTypeConstants.DE_TIME) { + fieldName = String.format(MaxConstants.TO_DATE, originField, StringUtils.isNotEmpty(f.getDateFormat()) ? f.getDateFormat() : MaxConstants.DEFAULT_DATE_FORMAT); + } else { + fieldName = originField; + } + } else { + if (f.getDeType() == DeTypeConstants.DE_TIME) { + String cast = String.format(MaxConstants.CAST, originField, "bigint"); + fieldName = String.format(MaxConstants.FROM_UNIXTIME, cast); + } else if (f.getDeType() == DeTypeConstants.DE_INT) { + fieldName = String.format(MaxConstants.CAST, originField, MaxConstants.DEFAULT_INT_FORMAT); + } else { + fieldName = originField; + } + } + xFields.add(SQLObj.builder() + .fieldName(fieldName) + .fieldAlias(fieldAlias) + .build()); + } + } + + STGroup stg = new STGroupFile(SQLConstants.SQL_TEMPLATE); + ST st_sql = stg.getInstanceOf("previewSql"); + st_sql.add("isGroup", isGroup); + if (CollectionUtils.isNotEmpty(xFields)) st_sql.add("groups", xFields); + if (ObjectUtils.isNotEmpty(tableObj)) st_sql.add("table", tableObj); + String customWheres = transCustomFilterList(tableObj, fieldCustomFilter); + // row permissions tree + String whereTrees = transFilterTrees(tableObj, rowPermissionsTree); + List wheres = new ArrayList<>(); + if (customWheres != null) wheres.add(customWheres); + if (whereTrees != null) wheres.add(whereTrees); + if (CollectionUtils.isNotEmpty(wheres)) st_sql.add("filters", wheres); + + List xOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(sortFields)) { + int step = fields.size(); + for (int i = step; i < (step + sortFields.size()); i++) { + DeSortField deSortField = sortFields.get(i - step); + SQLObj order = buildSortField(deSortField, tableObj, i); + xOrders.add(order); + } + } + if (ObjectUtils.isNotEmpty(xOrders)) { + st_sql.add("orders", xOrders); + String sql = st_sql.render(); + if (!StringUtils.containsIgnoreCase(sql, "LIMIT") && !StringUtils.containsIgnoreCase(sql, "offset")) { + sql += "LIMIT 1000000 offset 0"; + } + return sql; + } + return st_sql.render(); + } + + private SQLObj buildSortField(DeSortField f, SQLObj tableObj, int i) { + String originField; + if (ObjectUtils.isNotEmpty(f.getExtField()) && f.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(f.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(f.getExtField()) && f.getExtField() == 1) { + originField = String.format(MaxConstants.KEYWORD_FIX, tableObj.getTableAlias(), f.getOriginName()); + } else { + originField = String.format(MaxConstants.KEYWORD_FIX, tableObj.getTableAlias(), f.getOriginName()); + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_X_PREFIX, i); + String fieldName = ""; + // 处理横轴字段 + if (f.getDeExtractType() == DeTypeConstants.DE_TIME) { + if (f.getDeType() == DeTypeConstants.DE_INT || f.getDeType() == DeTypeConstants.DE_FLOAT) { + fieldName = String.format(MaxConstants.UNIX_TIMESTAMP, originField); + } else { + fieldName = originField; + } + } else if (f.getDeExtractType() == DeTypeConstants.DE_STRING) { + if (f.getDeType() == DeTypeConstants.DE_INT) { + fieldName = String.format(MaxConstants.CAST, originField, MaxConstants.DEFAULT_INT_FORMAT); + } else if (f.getDeType() == DeTypeConstants.DE_FLOAT) { + fieldName = String.format(MaxConstants.CAST, originField, MaxConstants.DEFAULT_FLOAT_FORMAT); + } else if (f.getDeType() == DeTypeConstants.DE_TIME) { + fieldName = String.format(MaxConstants.TO_DATE, originField, StringUtils.isNotEmpty(f.getDateFormat()) ? f.getDateFormat() : MaxConstants.DEFAULT_DATE_FORMAT); + } else { + fieldName = originField; + } + } else { + if (f.getDeType() == DeTypeConstants.DE_TIME) { + String cast = String.format(MaxConstants.CAST, originField, "bigint"); + fieldName = String.format(MaxConstants.FROM_UNIXTIME, cast); + } else if (f.getDeType() == DeTypeConstants.DE_INT) { + fieldName = String.format(MaxConstants.CAST, originField, MaxConstants.DEFAULT_INT_FORMAT); + } else { + fieldName = originField; + } + } + SQLObj result = SQLObj.builder().orderField(originField).orderAlias(originField).orderDirection(f.getOrderDirection()).build(); + return result; + } + + @Override + public String createQuerySQLAsTmp(String sql, List fields, boolean isGroup, List fieldCustomFilter, List rowPermissionsTree, List sortFields) { + return createQuerySQL("(" + sqlFix(sql) + ")", fields, isGroup, null, fieldCustomFilter, rowPermissionsTree, sortFields); + } + + @Override + public String createQuerySQLAsTmp(String sql, List fields, boolean isGroup, List fieldCustomFilter, List rowPermissionsTree) { + return createQuerySQL("(" + sqlFix(sql) + ")", fields, isGroup, null, fieldCustomFilter, rowPermissionsTree); + } + + @Override + public String createQueryTableWithPage(String table, List fields, Integer page, Integer pageSize, Integer realSize, boolean isGroup, Datasource ds, List fieldCustomFilter, List rowPermissionsTree) { + return createQuerySQL(table, fields, isGroup, ds, fieldCustomFilter, rowPermissionsTree) + " LIMIT " + realSize + " offset " + (page - 1) * pageSize; + } + + @Override + public String createQuerySQLWithPage(String sql, List fields, Integer page, Integer pageSize, Integer realSize, boolean isGroup, List fieldCustomFilter, List rowPermissionsTree) { + return createQuerySQLAsTmp(sql, fields, isGroup, fieldCustomFilter, rowPermissionsTree) + " LIMIT " + realSize + " offset " + (page - 1) * pageSize; + } + + @Override + public String createQueryTableWithLimit(String table, List fields, Integer limit, boolean isGroup, Datasource ds, List fieldCustomFilter, List rowPermissionsTree) { + return createQuerySQL(table, fields, isGroup, ds, fieldCustomFilter, rowPermissionsTree) + " LIMIT " + limit + " offset 0"; + } + + @Override + public String createQuerySqlWithLimit(String sql, List fields, Integer limit, boolean isGroup, List fieldCustomFilter, List rowPermissionsTree) { + return createQuerySQLAsTmp(sql, fields, isGroup, fieldCustomFilter, rowPermissionsTree) + " LIMIT " + limit + " offset 0"; + } + + @Override + public String getSQL(String table, List xAxis, List yAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, Datasource ds, ChartViewWithBLOBs view) { + SQLObj tableObj = SQLObj.builder() + .tableName((table.startsWith("(") && table.endsWith(")")) ? table : String.format(MaxConstants.KEYWORD_TABLE, table)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 0)) + .build(); + + List xFields = new ArrayList<>(); + List xOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(xAxis)) { + for (int i = 0; i < xAxis.size(); i++) { + ChartViewFieldDTO x = xAxis.get(i); + String originField; + if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(x.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 1) { + originField = String.format(MaxConstants.KEYWORD_FIX, tableObj.getTableAlias(), x.getOriginName()); + } else { + originField = String.format(MaxConstants.KEYWORD_FIX, tableObj.getTableAlias(), x.getOriginName()); + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_X_PREFIX, i); + // 处理横轴字段 + xFields.add(getXFields(x, originField, fieldAlias)); + // 处理横轴排序 + if (StringUtils.isNotEmpty(x.getSort()) && !StringUtils.equalsIgnoreCase(x.getSort(), "none")) { + xOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(x.getSort()) + .build()); + } + } + } + List yFields = new ArrayList<>(); + List yWheres = new ArrayList<>(); + List yOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(yAxis)) { + for (int i = 0; i < yAxis.size(); i++) { + ChartViewFieldDTO y = yAxis.get(i); + String originField; + if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(y.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 1) { + originField = String.format(MaxConstants.KEYWORD_FIX, tableObj.getTableAlias(), y.getOriginName()); + } else { + originField = String.format(MaxConstants.KEYWORD_FIX, tableObj.getTableAlias(), y.getOriginName()); + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_Y_PREFIX, i); + // 处理纵轴字段 + yFields.add(getYFields(y, originField, fieldAlias)); + // 处理纵轴过滤 + yWheres.add(getYWheres(y, originField, fieldAlias)); + // 处理纵轴排序 + if (StringUtils.isNotEmpty(y.getSort()) && !StringUtils.equalsIgnoreCase(y.getSort(), "none")) { + yOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(y.getSort()) + .build()); + } + } + } + // 处理视图中字段过滤 + String customWheres = transCustomFilterList(tableObj, fieldCustomFilter); + // 处理仪表板字段过滤 + String extWheres = transExtFilterList(tableObj, extFilterRequestList); + // row permissions tree + String whereTrees = transFilterTrees(tableObj, rowPermissionsTree); + // 构建sql所有参数 + List fields = new ArrayList<>(); + fields.addAll(xFields); + fields.addAll(yFields); + List wheres = new ArrayList<>(); + if (customWheres != null) wheres.add(customWheres); + if (extWheres != null) wheres.add(extWheres); + if (whereTrees != null) wheres.add(whereTrees); + List groups = new ArrayList<>(); + groups.addAll(xFields); + // 外层再次套sql + List orders = new ArrayList<>(); + orders.addAll(xOrders); + orders.addAll(yOrders); + List aggWheres = new ArrayList<>(); + aggWheres.addAll(yWheres.stream().filter(ObjectUtils::isNotEmpty).collect(Collectors.toList())); + + STGroup stg = new STGroupFile(SQLConstants.SQL_TEMPLATE); + ST st_sql = stg.getInstanceOf("querySql"); + if (CollectionUtils.isNotEmpty(xFields)) st_sql.add("groups", xFields); + if (CollectionUtils.isNotEmpty(yFields)) st_sql.add("aggregators", yFields); + if (CollectionUtils.isNotEmpty(wheres)) st_sql.add("filters", wheres); + if (ObjectUtils.isNotEmpty(tableObj)) st_sql.add("table", tableObj); + String sql = st_sql.render(); + + ST st = stg.getInstanceOf("querySql"); + SQLObj tableSQL = SQLObj.builder() + .tableName(String.format(MaxConstants.BRACKETS, sql)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 1)) + .build(); + if (CollectionUtils.isNotEmpty(aggWheres)) st.add("filters", aggWheres); + if (CollectionUtils.isNotEmpty(orders)) st.add("orders", orders); + if (ObjectUtils.isNotEmpty(tableSQL)) st.add("table", tableSQL); + return sqlLimit(st.render(), view); + } + + @Override + public String getSQLWithPage(boolean isTable, String table, List xAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, Datasource ds, ChartViewWithBLOBs view, PageInfo pageInfo) { + String limit = ((pageInfo.getGoPage() != null && pageInfo.getPageSize() != null) ? " LIMIT " + pageInfo.getPageSize() + " offset " + (pageInfo.getGoPage() - 1) * pageInfo.getPageSize() : ""); + if (isTable) { + return originalTableInfo(table, xAxis, fieldCustomFilter, rowPermissionsTree, extFilterRequestList, ds, view) + limit; + } else { + return originalTableInfo("(" + sqlFix(table) + ")", xAxis, fieldCustomFilter, rowPermissionsTree, extFilterRequestList, ds, view) + limit; + } + } + + private String originalTableInfo(String table, List xAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, Datasource ds, ChartViewWithBLOBs view) { + SQLObj tableObj = SQLObj.builder() + .tableName((table.startsWith("(") && table.endsWith(")")) ? table : String.format(MaxConstants.KEYWORD_TABLE, table)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 0)) + .build(); + + List xFields = new ArrayList<>(); + List xOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(xAxis)) { + for (int i = 0; i < xAxis.size(); i++) { + ChartViewFieldDTO x = xAxis.get(i); + String originField; + if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(x.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 1) { + originField = String.format(MaxConstants.KEYWORD_FIX, tableObj.getTableAlias(), x.getOriginName()); + } else { + if (x.getDeType() == 2 || x.getDeType() == 3) { + originField = String.format(MaxConstants.CAST, String.format(MaxConstants.KEYWORD_FIX, tableObj.getTableAlias(), x.getOriginName()), MaxConstants.DEFAULT_FLOAT_FORMAT); + } else { + originField = String.format(MaxConstants.KEYWORD_FIX, tableObj.getTableAlias(), x.getOriginName()); + } + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_X_PREFIX, i); + // 处理横轴字段 + xFields.add(getXFields(x, originField, fieldAlias)); + // 处理横轴排序 + if (StringUtils.isNotEmpty(x.getSort()) && !StringUtils.equalsIgnoreCase(x.getSort(), "none")) { + xOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(x.getSort()) + .build()); + } + } + } + // 处理视图中字段过滤 + String customWheres = transCustomFilterList(tableObj, fieldCustomFilter); + // 处理仪表板字段过滤 + String extWheres = transExtFilterList(tableObj, extFilterRequestList); + // row permissions tree + String whereTrees = transFilterTrees(tableObj, rowPermissionsTree); + // 构建sql所有参数 + List fields = new ArrayList<>(); + fields.addAll(xFields); + List wheres = new ArrayList<>(); + if (customWheres != null) wheres.add(customWheres); + if (extWheres != null) wheres.add(extWheres); + if (whereTrees != null) wheres.add(whereTrees); + List groups = new ArrayList<>(); + groups.addAll(xFields); + // 外层再次套sql + List orders = new ArrayList<>(); + orders.addAll(xOrders); + + STGroup stg = new STGroupFile(SQLConstants.SQL_TEMPLATE); + ST st_sql = stg.getInstanceOf("previewSql"); + st_sql.add("isGroup", false); + if (CollectionUtils.isNotEmpty(xFields)) st_sql.add("groups", xFields); + if (CollectionUtils.isNotEmpty(wheres)) st_sql.add("filters", wheres); + if (ObjectUtils.isNotEmpty(tableObj)) st_sql.add("table", tableObj); + String sql = st_sql.render(); + + ST st = stg.getInstanceOf("previewSql"); + st.add("isGroup", false); + SQLObj tableSQL = SQLObj.builder() + .tableName(String.format(MaxConstants.BRACKETS, sql)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 1)) + .build(); + if (CollectionUtils.isNotEmpty(orders)) st.add("orders", orders); + if (ObjectUtils.isNotEmpty(tableSQL)) st.add("table", tableSQL); + return st.render(); + } + + @Override + public String getSQLTableInfo(String table, List xAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, Datasource ds, ChartViewWithBLOBs view) { + return sqlLimit(originalTableInfo(table, xAxis, fieldCustomFilter, rowPermissionsTree, extFilterRequestList, ds, view), view); + } + + @Override + public String getSQLAsTmpTableInfo(String sql, List xAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, Datasource ds, ChartViewWithBLOBs view) { + return getSQLTableInfo("(" + sqlFix(sql) + ")", xAxis, fieldCustomFilter, rowPermissionsTree, extFilterRequestList, ds, view); + } + + + @Override + public String getSQLAsTmp(String sql, List xAxis, List yAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, ChartViewWithBLOBs view) { + return getSQL("(" + sqlFix(sql) + ")", xAxis, yAxis, fieldCustomFilter, rowPermissionsTree, extFilterRequestList, null, view); + } + + @Override + public String getSQLStack(String table, List xAxis, List yAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, List extStack, Datasource ds, ChartViewWithBLOBs view) { + SQLObj tableObj = SQLObj.builder() + .tableName((table.startsWith("(") && table.endsWith(")")) ? table : String.format(MaxConstants.KEYWORD_TABLE, table)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 0)) + .build(); + + List xFields = new ArrayList<>(); + List xOrders = new ArrayList<>(); + List xList = new ArrayList<>(); + xList.addAll(xAxis); + xList.addAll(extStack); + if (CollectionUtils.isNotEmpty(xList)) { + for (int i = 0; i < xList.size(); i++) { + ChartViewFieldDTO x = xList.get(i); + String originField; + if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(x.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 1) { + originField = String.format(MaxConstants.KEYWORD_FIX, tableObj.getTableAlias(), x.getOriginName()); + } else { + originField = String.format(MaxConstants.KEYWORD_FIX, tableObj.getTableAlias(), x.getOriginName()); + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_X_PREFIX, i); + // 处理横轴字段 + xFields.add(getXFields(x, originField, fieldAlias)); + // 处理横轴排序 + if (StringUtils.isNotEmpty(x.getSort()) && !StringUtils.equalsIgnoreCase(x.getSort(), "none")) { + xOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(x.getSort()) + .build()); + } + } + } + List yFields = new ArrayList<>(); + List yWheres = new ArrayList<>(); + List yOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(yAxis)) { + for (int i = 0; i < yAxis.size(); i++) { + ChartViewFieldDTO y = yAxis.get(i); + String originField; + if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(y.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 1) { + originField = String.format(MaxConstants.KEYWORD_FIX, tableObj.getTableAlias(), y.getOriginName()); + } else { + originField = String.format(MaxConstants.KEYWORD_FIX, tableObj.getTableAlias(), y.getOriginName()); + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_Y_PREFIX, i); + // 处理纵轴字段 + yFields.add(getYFields(y, originField, fieldAlias)); + // 处理纵轴过滤 + yWheres.add(getYWheres(y, originField, fieldAlias)); + // 处理纵轴排序 + if (StringUtils.isNotEmpty(y.getSort()) && !StringUtils.equalsIgnoreCase(y.getSort(), "none")) { + yOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(y.getSort()) + .build()); + } + } + } + // 处理视图中字段过滤 + String customWheres = transCustomFilterList(tableObj, fieldCustomFilter); + // 处理仪表板字段过滤 + String extWheres = transExtFilterList(tableObj, extFilterRequestList); + // row permissions tree + String whereTrees = transFilterTrees(tableObj, rowPermissionsTree); + // 构建sql所有参数 + List fields = new ArrayList<>(); + fields.addAll(xFields); + fields.addAll(yFields); + List wheres = new ArrayList<>(); + if (customWheres != null) wheres.add(customWheres); + if (extWheres != null) wheres.add(extWheres); + if (whereTrees != null) wheres.add(whereTrees); + List groups = new ArrayList<>(); + groups.addAll(xFields); + // 外层再次套sql + List orders = new ArrayList<>(); + orders.addAll(xOrders); + orders.addAll(yOrders); + List aggWheres = new ArrayList<>(); + aggWheres.addAll(yWheres.stream().filter(ObjectUtils::isNotEmpty).collect(Collectors.toList())); + + STGroup stg = new STGroupFile(SQLConstants.SQL_TEMPLATE); + ST st_sql = stg.getInstanceOf("querySql"); + if (CollectionUtils.isNotEmpty(xFields)) st_sql.add("groups", xFields); + if (CollectionUtils.isNotEmpty(yFields)) st_sql.add("aggregators", yFields); + if (CollectionUtils.isNotEmpty(wheres)) st_sql.add("filters", wheres); + if (ObjectUtils.isNotEmpty(tableObj)) st_sql.add("table", tableObj); + String sql = st_sql.render(); + + ST st = stg.getInstanceOf("querySql"); + SQLObj tableSQL = SQLObj.builder() + .tableName(String.format(MaxConstants.BRACKETS, sql)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 1)) + .build(); + if (CollectionUtils.isNotEmpty(aggWheres)) st.add("filters", aggWheres); + if (CollectionUtils.isNotEmpty(orders)) st.add("orders", orders); + if (ObjectUtils.isNotEmpty(tableSQL)) st.add("table", tableSQL); + return sqlLimit(st.render(), view); + } + + @Override + public String getSQLAsTmpStack(String table, List xAxis, List yAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, List extStack, ChartViewWithBLOBs view) { + return getSQLStack("(" + sqlFix(table) + ")", xAxis, yAxis, fieldCustomFilter, rowPermissionsTree, extFilterRequestList, extStack, null, view); + } + + @Override + public String getSQLScatter(String table, List xAxis, List yAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, List extBubble, Datasource ds, ChartViewWithBLOBs view) { + SQLObj tableObj = SQLObj.builder() + .tableName((table.startsWith("(") && table.endsWith(")")) ? table : String.format(MaxConstants.KEYWORD_TABLE, table)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 0)) + .build(); + + List xFields = new ArrayList<>(); + List xOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(xAxis)) { + for (int i = 0; i < xAxis.size(); i++) { + ChartViewFieldDTO x = xAxis.get(i); + String originField; + if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(x.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 1) { + originField = String.format(MaxConstants.KEYWORD_FIX, tableObj.getTableAlias(), x.getOriginName()); + } else { + originField = String.format(MaxConstants.KEYWORD_FIX, tableObj.getTableAlias(), x.getOriginName()); + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_X_PREFIX, i); + // 处理横轴字段 + xFields.add(getXFields(x, originField, fieldAlias)); + // 处理横轴排序 + if (StringUtils.isNotEmpty(x.getSort()) && !StringUtils.equalsIgnoreCase(x.getSort(), "none")) { + xOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(x.getSort()) + .build()); + } + } + } + List yFields = new ArrayList<>(); + List yWheres = new ArrayList<>(); + List yOrders = new ArrayList<>(); + List yList = new ArrayList<>(); + yList.addAll(yAxis); + yList.addAll(extBubble); + if (CollectionUtils.isNotEmpty(yList)) { + for (int i = 0; i < yList.size(); i++) { + ChartViewFieldDTO y = yList.get(i); + String originField; + if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(y.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 1) { + originField = String.format(MaxConstants.KEYWORD_FIX, tableObj.getTableAlias(), y.getOriginName()); + } else { + originField = String.format(MaxConstants.KEYWORD_FIX, tableObj.getTableAlias(), y.getOriginName()); + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_Y_PREFIX, i); + // 处理纵轴字段 + yFields.add(getYFields(y, originField, fieldAlias)); + // 处理纵轴过滤 + yWheres.add(getYWheres(y, originField, fieldAlias)); + // 处理纵轴排序 + if (StringUtils.isNotEmpty(y.getSort()) && !StringUtils.equalsIgnoreCase(y.getSort(), "none")) { + yOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(y.getSort()) + .build()); + } + } + } + // 处理视图中字段过滤 + String customWheres = transCustomFilterList(tableObj, fieldCustomFilter); + // 处理仪表板字段过滤 + String extWheres = transExtFilterList(tableObj, extFilterRequestList); + // row permissions tree + String whereTrees = transFilterTrees(tableObj, rowPermissionsTree); + // 构建sql所有参数 + List fields = new ArrayList<>(); + fields.addAll(xFields); + fields.addAll(yFields); + List wheres = new ArrayList<>(); + if (customWheres != null) wheres.add(customWheres); + if (extWheres != null) wheres.add(extWheres); + if (whereTrees != null) wheres.add(whereTrees); + List groups = new ArrayList<>(); + groups.addAll(xFields); + // 外层再次套sql + List orders = new ArrayList<>(); + orders.addAll(xOrders); + orders.addAll(yOrders); + List aggWheres = new ArrayList<>(); + aggWheres.addAll(yWheres.stream().filter(ObjectUtils::isNotEmpty).collect(Collectors.toList())); + + STGroup stg = new STGroupFile(SQLConstants.SQL_TEMPLATE); + ST st_sql = stg.getInstanceOf("querySql"); + if (CollectionUtils.isNotEmpty(xFields)) st_sql.add("groups", xFields); + if (CollectionUtils.isNotEmpty(yFields)) st_sql.add("aggregators", yFields); + if (CollectionUtils.isNotEmpty(wheres)) st_sql.add("filters", wheres); + if (ObjectUtils.isNotEmpty(tableObj)) st_sql.add("table", tableObj); + String sql = st_sql.render(); + + ST st = stg.getInstanceOf("querySql"); + SQLObj tableSQL = SQLObj.builder() + .tableName(String.format(MaxConstants.BRACKETS, sql)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 1)) + .build(); + if (CollectionUtils.isNotEmpty(aggWheres)) st.add("filters", aggWheres); + if (CollectionUtils.isNotEmpty(orders)) st.add("orders", orders); + if (ObjectUtils.isNotEmpty(tableSQL)) st.add("table", tableSQL); + return sqlLimit(st.render(), view); + } + + @Override + public String getSQLAsTmpScatter(String table, List xAxis, List yAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, List extBubble, ChartViewWithBLOBs view) { + return getSQLScatter("(" + sqlFix(table) + ")", xAxis, yAxis, fieldCustomFilter, rowPermissionsTree, extFilterRequestList, extBubble, null, view); + } + + @Override + public String searchTable(String table) { + return "SELECT table_name FROM information_schema.TABLES WHERE table_name ='" + table + "'"; + } + + @Override + public String getSQLSummary(String table, List yAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, ChartViewWithBLOBs view, Datasource ds) { + // 字段汇总 排序等 + SQLObj tableObj = SQLObj.builder() + .tableName((table.startsWith("(") && table.endsWith(")")) ? table : String.format(MaxConstants.KEYWORD_TABLE, table)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 0)) + .build(); + + List yFields = new ArrayList<>(); + List yWheres = new ArrayList<>(); + List yOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(yAxis)) { + for (int i = 0; i < yAxis.size(); i++) { + ChartViewFieldDTO y = yAxis.get(i); + String originField; + if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(y.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 1) { + originField = String.format(MaxConstants.KEYWORD_FIX, tableObj.getTableAlias(), y.getOriginName()); + } else { + originField = String.format(MaxConstants.KEYWORD_FIX, tableObj.getTableAlias(), y.getOriginName()); + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_Y_PREFIX, i); + // 处理纵轴字段 + yFields.add(getYFields(y, originField, fieldAlias)); + // 处理纵轴过滤 + yWheres.add(getYWheres(y, originField, fieldAlias)); + // 处理纵轴排序 + if (StringUtils.isNotEmpty(y.getSort()) && !StringUtils.equalsIgnoreCase(y.getSort(), "none")) { + yOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(y.getSort()) + .build()); + } + } + } + // 处理视图中字段过滤 + String customWheres = transCustomFilterList(tableObj, fieldCustomFilter); + // 处理仪表板字段过滤 + String extWheres = transExtFilterList(tableObj, extFilterRequestList); + // row permissions tree + String whereTrees = transFilterTrees(tableObj, rowPermissionsTree); + // 构建sql所有参数 + List fields = new ArrayList<>(); + fields.addAll(yFields); + List wheres = new ArrayList<>(); + if (customWheres != null) wheres.add(customWheres); + if (extWheres != null) wheres.add(extWheres); + if (whereTrees != null) wheres.add(whereTrees); + List groups = new ArrayList<>(); + // 外层再次套sql + List orders = new ArrayList<>(); + orders.addAll(yOrders); + List aggWheres = new ArrayList<>(); + aggWheres.addAll(yWheres.stream().filter(ObjectUtils::isNotEmpty).collect(Collectors.toList())); + + STGroup stg = new STGroupFile(SQLConstants.SQL_TEMPLATE); + ST st_sql = stg.getInstanceOf("querySql"); + if (CollectionUtils.isNotEmpty(yFields)) st_sql.add("aggregators", yFields); + if (CollectionUtils.isNotEmpty(wheres)) st_sql.add("filters", wheres); + if (ObjectUtils.isNotEmpty(tableObj)) st_sql.add("table", tableObj); + String sql = st_sql.render(); + + ST st = stg.getInstanceOf("querySql"); + SQLObj tableSQL = SQLObj.builder() + .tableName(String.format(MaxConstants.BRACKETS, sql)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 1)) + .build(); + if (CollectionUtils.isNotEmpty(aggWheres)) st.add("filters", aggWheres); + if (CollectionUtils.isNotEmpty(orders)) st.add("orders", orders); + if (ObjectUtils.isNotEmpty(tableSQL)) st.add("table", tableSQL); + return sqlLimit(st.render(), view); + } + + @Override + public String getSQLSummaryAsTmp(String sql, List yAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, ChartViewWithBLOBs view) { + return getSQLSummary("(" + sqlFix(sql) + ")", yAxis, fieldCustomFilter, rowPermissionsTree, extFilterRequestList, view, null); + } + + @Override + public String wrapSql(String sql) { + sql = sql.trim(); + if (sql.lastIndexOf(";") == (sql.length() - 1)) { + sql = sql.substring(0, sql.length() - 1); + } + String tmpSql = "SELECT * FROM (" + sql + ") AS tmp " + " LIMIT 0 "; + return tmpSql; + } + + @Override + public String createRawQuerySQL(String table, List fields, Datasource ds) { + String[] array = fields.stream().map(f -> { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("\"").append(f.getOriginName()).append("\" AS ").append(f.getDataeaseName()); + return stringBuilder.toString(); + }).toArray(String[]::new); + if (ds != null) { + String schema = new Gson().fromJson(ds.getConfiguration(), JdbcConfiguration.class).getSchema(); + String tableWithSchema = String.format(SqlServerSQLConstants.KEYWORD_TABLE, schema) + "." + String.format(SqlServerSQLConstants.KEYWORD_TABLE, table); + return MessageFormat.format("SELECT {0} FROM {1} ", StringUtils.join(array, ","), tableWithSchema); + } else { + return MessageFormat.format("SELECT {0} FROM {1} ", StringUtils.join(array, ","), table); + } + } + + @Override + public String createRawQuerySQLAsTmp(String sql, List fields) { + return createRawQuerySQL(" (" + sqlFix(sql) + ") AS tmp ", fields, null); + } + + @Override + public String transTreeItem(SQLObj tableObj, DatasetRowPermissionsTreeItem item) { + String res = null; + DatasetTableField field = item.getField(); + if (ObjectUtils.isEmpty(field)) { + return null; + } + String whereName = ""; + String originName; + if (ObjectUtils.isNotEmpty(field.getExtField()) && field.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originName = calcFieldRegex(field.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(field.getExtField()) && field.getExtField() == 1) { + originName = String.format(MaxConstants.KEYWORD_FIX, tableObj.getTableAlias(), field.getOriginName()); + } else { + originName = String.format(MaxConstants.KEYWORD_FIX, tableObj.getTableAlias(), field.getOriginName()); + } + if (field.getDeType() == 1) { + if (field.getDeExtractType() == 0 || field.getDeExtractType() == 5) { + whereName = String.format(MaxConstants.TO_DATE, originName, StringUtils.isNotEmpty(field.getDateFormat()) ? field.getDateFormat() : MaxConstants.DEFAULT_DATE_FORMAT); + } + if (field.getDeExtractType() == 2 || field.getDeExtractType() == 3 || field.getDeExtractType() == 4) { + String cast = String.format(MaxConstants.CAST, originName, "bigint"); + whereName = String.format(MaxConstants.FROM_UNIXTIME, cast); + } + if (field.getDeExtractType() == 1) { + whereName = originName; + } + } else if (field.getDeType() == 2 || field.getDeType() == 3) { + if (field.getDeExtractType() == 0 || field.getDeExtractType() == 5) { + whereName = String.format(MaxConstants.CAST, originName, MaxConstants.DEFAULT_FLOAT_FORMAT); + } + if (field.getDeExtractType() == 1) { + whereName = String.format(MaxConstants.UNIX_TIMESTAMP, originName); + } + if (field.getDeExtractType() == 2 || field.getDeExtractType() == 3 || field.getDeExtractType() == 4) { + whereName = originName; + } + } else { + whereName = originName; + } + + if (StringUtils.equalsIgnoreCase(item.getFilterType(), "enum")) { + if (CollectionUtils.isNotEmpty(item.getEnumValue())) { + res = "(" + whereName + " IN ('" + String.join("','", item.getEnumValue()) + "'))"; + } + } else { + String value = item.getValue(); + String whereTerm = transMysqlFilterTerm(item.getTerm()); + String whereValue = ""; + + if (StringUtils.equalsIgnoreCase(item.getTerm(), "null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(item.getTerm(), "not_null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(item.getTerm(), "empty")) { + whereValue = "''"; + } else if (StringUtils.equalsIgnoreCase(item.getTerm(), "not_empty")) { + whereValue = "''"; + } else if (StringUtils.containsIgnoreCase(item.getTerm(), "in") || StringUtils.containsIgnoreCase(item.getTerm(), "not in")) { + whereValue = "('" + String.join("','", value.split(",")) + "')"; + } else if (StringUtils.containsIgnoreCase(item.getTerm(), "like")) { + whereValue = "'%" + value + "%'"; + } else { + whereValue = String.format(MaxConstants.WHERE_VALUE_VALUE, value); + } + SQLObj build = SQLObj.builder() + .whereField(whereName) + .whereTermAndValue(whereTerm + whereValue) + .build(); + res = build.getWhereField() + " " + build.getWhereTermAndValue(); + } + return res; + } + + @Override + public String convertTableToSql(String tableName, Datasource ds) { + String schema = new Gson().fromJson(ds.getConfiguration(), JdbcConfiguration.class).getSchema(); + schema = String.format(MaxConstants.KEYWORD_TABLE, schema); + return createSQLPreview("SELECT * FROM " + schema + "." + String.format(MaxConstants.KEYWORD_TABLE, tableName), null); + } + + public String transMysqlFilterTerm(String term) { + switch (term) { + case "eq": + return " = "; + case "not_eq": + return " <> "; + case "lt": + return " < "; + case "le": + return " <= "; + case "gt": + return " > "; + case "ge": + return " >= "; + case "in": + return " IN "; + case "not in": + return " NOT IN "; + case "like": + return " LIKE "; + case "not like": + return " NOT LIKE "; + case "null": + return " IS NULL "; + case "not_null": + return " IS NOT NULL "; + case "empty": + return " = "; + case "not_empty": + return " <> "; + case "between": + return " BETWEEN "; + default: + return ""; + } + } + + public String transCustomFilterList(SQLObj tableObj, List requestList) { + if (CollectionUtils.isEmpty(requestList)) { + return null; + } + List res = new ArrayList<>(); + for (ChartFieldCustomFilterDTO request : requestList) { + List list = new ArrayList<>(); + DatasetTableField field = request.getField(); + + if (ObjectUtils.isEmpty(field)) { + continue; + } + String whereName = ""; + String originName; + if (ObjectUtils.isNotEmpty(field.getExtField()) && field.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originName = calcFieldRegex(field.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(field.getExtField()) && field.getExtField() == 1) { + originName = String.format(MaxConstants.KEYWORD_FIX, tableObj.getTableAlias(), field.getOriginName()); + } else { + originName = String.format(MaxConstants.KEYWORD_FIX, tableObj.getTableAlias(), field.getOriginName()); + } + if (field.getDeType() == 1) { + if (field.getDeExtractType() == 0 || field.getDeExtractType() == 5) { + whereName = String.format(MaxConstants.TO_DATE, originName, StringUtils.isNotEmpty(field.getDateFormat()) ? field.getDateFormat() : MaxConstants.DEFAULT_DATE_FORMAT); + } + if (field.getDeExtractType() == 2 || field.getDeExtractType() == 3 || field.getDeExtractType() == 4) { + String cast = String.format(MaxConstants.CAST, originName, "bigint"); + whereName = String.format(MaxConstants.FROM_UNIXTIME, cast); + } + if (field.getDeExtractType() == 1) { + whereName = originName; + } + } else if (field.getDeType() == 2 || field.getDeType() == 3) { + if (field.getDeExtractType() == 0 || field.getDeExtractType() == 5) { + whereName = String.format(MaxConstants.CAST, originName, MaxConstants.DEFAULT_FLOAT_FORMAT); + } + if (field.getDeExtractType() == 1) { + whereName = String.format(MaxConstants.UNIX_TIMESTAMP, originName); + } + if (field.getDeExtractType() == 2 || field.getDeExtractType() == 3 || field.getDeExtractType() == 4) { + whereName = originName; + } + } else { + whereName = originName; + } + + if (StringUtils.equalsIgnoreCase(request.getFilterType(), "enum")) { + if (CollectionUtils.isNotEmpty(request.getEnumCheckField())) { + res.add("(" + whereName + " IN ('" + String.join("','", request.getEnumCheckField()) + "'))"); + } + } else { + List filter = request.getFilter(); + for (ChartCustomFilterItemDTO filterItemDTO : filter) { + String value = filterItemDTO.getValue(); + String whereTerm = transMysqlFilterTerm(filterItemDTO.getTerm()); + String whereValue = ""; + + if (StringUtils.equalsIgnoreCase(filterItemDTO.getTerm(), "null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(filterItemDTO.getTerm(), "not_null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(filterItemDTO.getTerm(), "empty")) { + whereValue = "''"; + } else if (StringUtils.equalsIgnoreCase(filterItemDTO.getTerm(), "not_empty")) { + whereValue = "''"; + } else if (StringUtils.containsIgnoreCase(filterItemDTO.getTerm(), "in") || StringUtils.containsIgnoreCase(filterItemDTO.getTerm(), "not in")) { + whereValue = "('" + String.join("','", value.split(",")) + "')"; + } else if (StringUtils.containsIgnoreCase(filterItemDTO.getTerm(), "like")) { + whereValue = "'%" + value + "%'"; + } else { + whereValue = String.format(MaxConstants.WHERE_VALUE_VALUE, value); + } + list.add(SQLObj.builder() + .whereField(whereName) + .whereTermAndValue(whereTerm + whereValue) + .build()); + } + + List strList = new ArrayList<>(); + list.forEach(ele -> strList.add(ele.getWhereField() + " " + ele.getWhereTermAndValue())); + if (CollectionUtils.isNotEmpty(list)) { + res.add("(" + String.join(" " + getLogic(request.getLogic()) + " ", strList) + ")"); + } + } + } + return CollectionUtils.isNotEmpty(res) ? "(" + String.join(" AND ", res) + ")" : null; + } + + public String transExtFilterList(SQLObj tableObj, List requestList) { + if (CollectionUtils.isEmpty(requestList)) { + return null; + } + List list = new ArrayList<>(); + for (ChartExtFilterRequest request : requestList) { + List value = request.getValue(); + + List whereNameList = new ArrayList<>(); + List fieldList = new ArrayList<>(); + if (request.getIsTree()) { + fieldList.addAll(request.getDatasetTableFieldList()); + } else { + fieldList.add(request.getDatasetTableField()); + } + + for (DatasetTableField field : fieldList) { + if (CollectionUtils.isEmpty(value) || ObjectUtils.isEmpty(field)) { + continue; + } + String whereName = ""; + + String originName; + if (ObjectUtils.isNotEmpty(field.getExtField()) && field.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originName = calcFieldRegex(field.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(field.getExtField()) && field.getExtField() == 1) { + originName = String.format(MaxConstants.KEYWORD_FIX, tableObj.getTableAlias(), field.getOriginName()); + } else { + originName = String.format(MaxConstants.KEYWORD_FIX, tableObj.getTableAlias(), field.getOriginName()); + } + + if (field.getDeType() == 1) { + if (field.getDeExtractType() == 0 || field.getDeExtractType() == 5) { + whereName = String.format(MaxConstants.TO_DATE, originName, StringUtils.isNotEmpty(field.getDateFormat()) ? field.getDateFormat() : MaxConstants.DEFAULT_DATE_FORMAT); + } + if (field.getDeExtractType() == 2 || field.getDeExtractType() == 3 || field.getDeExtractType() == 4) { + String cast = String.format(MaxConstants.CAST, originName, "bigint"); + whereName = String.format(MaxConstants.FROM_UNIXTIME, cast); + } + if (field.getDeExtractType() == 1) { + whereName = originName; + } + } else if (field.getDeType() == 2 || field.getDeType() == 3) { + if (field.getDeExtractType() == 0 || field.getDeExtractType() == 5) { + whereName = String.format(MaxConstants.CAST, originName, MaxConstants.DEFAULT_FLOAT_FORMAT); + } + if (field.getDeExtractType() == 1) { + whereName = String.format(MaxConstants.UNIX_TIMESTAMP, originName); + } + if (field.getDeExtractType() == 2 || field.getDeExtractType() == 3 || field.getDeExtractType() == 4) { + whereName = originName; + } + } else { + whereName = originName; + } + whereNameList.add(whereName); + } + + String whereName = ""; + if (request.getIsTree()) { + whereName = "CONCAT(" + StringUtils.join(whereNameList, ",',',") + ")"; + } else { + whereName = whereNameList.get(0); + } + String whereTerm = transMysqlFilterTerm(request.getOperator()); + String whereValue = ""; + + if (StringUtils.containsIgnoreCase(request.getOperator(), "in")) { + whereValue = "('" + StringUtils.join(value, "','") + "')"; + } else if (StringUtils.containsIgnoreCase(request.getOperator(), "like")) { + String keyword = value.get(0).toUpperCase(); + whereValue = "'%" + keyword + "%'"; + whereName = "upper(" + whereName + ")"; + } else if (StringUtils.containsIgnoreCase(request.getOperator(), "between")) { + if (request.getDatasetTableField().getDeType() == 1) { + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String startTime = simpleDateFormat.format(new Date(Long.parseLong(value.get(0)))); + String endTime = simpleDateFormat.format(new Date(Long.parseLong(value.get(1)))); + whereValue = String.format(MaxConstants.WHERE_BETWEEN, startTime, endTime); + } else { + whereValue = String.format(MaxConstants.WHERE_BETWEEN, value.get(0), value.get(1)); + } + } else { + whereValue = String.format(MaxConstants.WHERE_VALUE_VALUE, value.get(0)); + } + list.add(SQLObj.builder() + .whereField(whereName) + .whereTermAndValue(whereTerm + whereValue) + .build()); + } + List strList = new ArrayList<>(); + list.forEach(ele -> strList.add(ele.getWhereField() + " " + ele.getWhereTermAndValue())); + return CollectionUtils.isNotEmpty(list) ? "(" + String.join(" AND ", strList) + ")" : null; + } + + private String sqlFix(String sql) { + if (sql.lastIndexOf(";") == (sql.length() - 1)) { + sql = sql.substring(0, sql.length() - 1); + } + return sql; + } + + private String transDateFormat(String dateStyle, String datePattern) { + String split = "-"; + if (StringUtils.equalsIgnoreCase(datePattern, "date_sub")) { + split = "-"; + } else if (StringUtils.equalsIgnoreCase(datePattern, "date_split")) { + split = "/"; + } else { + split = "-"; + } + + if (StringUtils.isEmpty(dateStyle)) { + return "yyyy-mm-dd hh:mi:ss"; + } + + switch (dateStyle) { + case "y": + return "yyyy"; + case "y_M": + return "yyyy" + split + "mm"; + case "y_M_d": + return "yyyy" + split + "mm" + split + "dd"; + case "H_m_s": + return "hh:mi:ss"; + case "y_M_d_H_m": + return "yyyy" + split + "mm" + split + "dd" + " hh:mi"; + case "y_M_d_H_m_s": + return "yyyy" + split + "mm" + split + "dd" + " hh:mi:ss"; + default: + return "yyyy-mm-dd hh:mi:s"; + } + } + + private SQLObj getXFields(ChartViewFieldDTO x, String originField, String fieldAlias) { + String fieldName = ""; + if (x.getDeExtractType() == DeTypeConstants.DE_TIME) { + if (x.getDeType() == 2 || x.getDeType() == 3) { + fieldName = String.format(MaxConstants.UNIX_TIMESTAMP, originField); + } else if (x.getDeType() == DeTypeConstants.DE_TIME) { + String format = transDateFormat(x.getDateStyle(), x.getDatePattern()); + fieldName = String.format(MaxConstants.DATE_FORMAT, originField, format); + } else { + fieldName = originField; + } + } else { + if (x.getDeType() == DeTypeConstants.DE_TIME) { + String format = transDateFormat(x.getDateStyle(), x.getDatePattern()); + if (x.getDeExtractType() == DeTypeConstants.DE_STRING) { + fieldName = String.format(MaxConstants.TO_DATE, originField, StringUtils.isNotEmpty(x.getDateFormat()) ? x.getDateFormat() : MaxConstants.DEFAULT_DATE_FORMAT); + } else { + String cast = String.format(MaxConstants.CAST, originField, "bigint"); + String from_unixtime = String.format(MaxConstants.FROM_UNIXTIME, cast); + fieldName = String.format(MaxConstants.DATE_FORMAT, from_unixtime, format); + } + } else { + if (x.getDeType() == DeTypeConstants.DE_INT) { + fieldName = String.format(MaxConstants.CAST, originField, MaxConstants.DEFAULT_INT_FORMAT); + } else if (x.getDeType() == DeTypeConstants.DE_FLOAT) { + fieldName = String.format(MaxConstants.CAST, originField, MaxConstants.DEFAULT_FLOAT_FORMAT); + } else { + fieldName = originField; + } + } + } + return SQLObj.builder() + .fieldName(fieldName) + .fieldAlias(fieldAlias) + .build(); + } + + private SQLObj getYFields(ChartViewFieldDTO y, String originField, String fieldAlias) { + String fieldName = ""; + if (StringUtils.equalsIgnoreCase(y.getOriginName(), "*")) { + fieldName = MaxConstants.AGG_COUNT; + } else if (SQLConstants.DIMENSION_TYPE.contains(y.getDeType())) { + fieldName = String.format(MaxConstants.AGG_FIELD, y.getSummary(), originField); + } else { + if (StringUtils.equalsIgnoreCase(y.getSummary(), "avg") || StringUtils.containsIgnoreCase(y.getSummary(), "pop")) { + String cast = String.format(MaxConstants.CAST, originField, y.getDeType() == DeTypeConstants.DE_INT ? MaxConstants.DEFAULT_INT_FORMAT : MaxConstants.DEFAULT_FLOAT_FORMAT); + String agg = String.format(MaxConstants.AGG_FIELD, y.getSummary(), cast); + fieldName = String.format(MaxConstants.CAST, agg, MaxConstants.DEFAULT_FLOAT_FORMAT); + } else { + String cast = String.format(MaxConstants.CAST, originField, y.getDeType() == DeTypeConstants.DE_INT ? MaxConstants.DEFAULT_INT_FORMAT : MaxConstants.DEFAULT_FLOAT_FORMAT); + fieldName = String.format(MaxConstants.AGG_FIELD, y.getSummary(), cast); + } + } + return SQLObj.builder() + .fieldName(fieldName) + .fieldAlias(fieldAlias) + .build(); + } + + private String getYWheres(ChartViewFieldDTO y, String originField, String fieldAlias) { + List list = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(y.getFilter()) && y.getFilter().size() > 0) { + y.getFilter().forEach(f -> { + String whereTerm = transMysqlFilterTerm(f.getTerm()); + String whereValue = ""; + // 原始类型不是时间,在de中被转成时间的字段做处理 + if (StringUtils.equalsIgnoreCase(f.getTerm(), "null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(f.getTerm(), "not_null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(f.getTerm(), "empty")) { + whereValue = "''"; + } else if (StringUtils.equalsIgnoreCase(f.getTerm(), "not_empty")) { + whereValue = "''"; + } else if (StringUtils.containsIgnoreCase(f.getTerm(), "in")) { + whereValue = "('" + StringUtils.join(f.getValue(), "','") + "')"; + } else if (StringUtils.containsIgnoreCase(f.getTerm(), "like")) { + whereValue = "'%" + f.getValue() + "%'"; + } else { + whereValue = String.format(MaxConstants.WHERE_VALUE_VALUE, f.getValue()); + } + list.add(SQLObj.builder() + .whereField(fieldAlias) + .whereAlias(fieldAlias) + .whereTermAndValue(whereTerm + whereValue) + .build()); + }); + } + List strList = new ArrayList<>(); + list.forEach(ele -> strList.add(ele.getWhereField() + " " + ele.getWhereTermAndValue())); + return CollectionUtils.isNotEmpty(list) ? "(" + String.join(" " + getLogic(y.getLogic()) + " ", strList) + ")" : null; + } + + private String calcFieldRegex(String originField, SQLObj tableObj) { + originField = originField.replaceAll("[\\t\\n\\r]]", ""); + // 正则提取[xxx] + String regex = "\\[(.*?)]"; + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(originField); + Set ids = new HashSet<>(); + while (matcher.find()) { + String id = matcher.group(1); + ids.add(id); + } + if (CollectionUtils.isEmpty(ids)) { + return originField; + } + DatasetTableFieldExample datasetTableFieldExample = new DatasetTableFieldExample(); + datasetTableFieldExample.createCriteria().andIdIn(new ArrayList<>(ids)); + List calcFields = datasetTableFieldMapper.selectByExample(datasetTableFieldExample); + for (DatasetTableField ele : calcFields) { + originField = originField.replaceAll("\\[" + ele.getId() + "]", + String.format(MaxConstants.KEYWORD_FIX, tableObj.getTableAlias(), ele.getOriginName())); + } + return originField; + } + + private String sqlLimit(String sql, ChartViewWithBLOBs view) { + if (StringUtils.equalsIgnoreCase(view.getResultMode(), "custom")) { + return sql + " LIMIT " + view.getResultCount() + " offset 0"; + } else { + return sql; + } + } + + public List dateformat() { + ObjectMapper objectMapper = new ObjectMapper(); + List dateformats = new ArrayList<>(); + try{ + dateformats = objectMapper.readValue("[\n" + + "{\"dateformat\": \"yyyymmdd\"},\n" + + "{\"dateformat\": \"yyyy/mm/dd\"},\n" + + "{\"dateformat\": \"yyyy-mm-dd\"},\n" + + "{\"dateformat\": \"yyyymmdd hh:mi:s\"},\n" + + "{\"dateformat\": \"yyyy/mm/dd hh:mi:s\"},\n" + + "{\"dateformat\": \"yyyy-mm-dd hh:mi:s\"}\n" + + "]", new TypeReference>() {} ); + }catch (Exception e){} + return dateformats; + } +} diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-backend/src/main/java/io/dataease/plugins/datasource/dm/service/MaxcomputeService.java b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-backend/src/main/java/io/dataease/plugins/datasource/dm/service/MaxcomputeService.java new file mode 100644 index 0000000000..a6217a4f1d --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-backend/src/main/java/io/dataease/plugins/datasource/dm/service/MaxcomputeService.java @@ -0,0 +1,58 @@ +package io.dataease.plugins.datasource.dm.service; + +import io.dataease.plugins.common.constants.DatabaseClassification; +import io.dataease.plugins.common.constants.DatasourceCalculationMode; +import io.dataease.plugins.common.dto.StaticResource; +import io.dataease.plugins.common.dto.datasource.DataSourceType; +import io.dataease.plugins.datasource.service.DatasourceService; +import org.springframework.stereotype.Service; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +@Service +public class MaxcomputeService extends DatasourceService { + + + @Override + public List components() { + List result = new ArrayList<>(); + result.add("maxcompute"); + return result; + } + @Override + protected InputStream readContent(String s) { + InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("static/" + s); + return resourceAsStream; + } + + @Override + public List staticResources() { + List results = new ArrayList<>(); + StaticResource staticResource = new StaticResource(); + staticResource.setName("maxcompute"); + staticResource.setSuffix("jpg"); + results.add(staticResource); + results.add(pluginSvg()); + return results; + } + + @Override + public DataSourceType getDataSourceType() { + DataSourceType dataSourceType = new DataSourceType("maxcompute", "Maxcompute" , true , "", DatasourceCalculationMode.DIRECT, true); + dataSourceType.setKeywordPrefix("\""); + dataSourceType.setKeywordSuffix("\""); + dataSourceType.setAliasPrefix("\""); + dataSourceType.setAliasSuffix("\""); + dataSourceType.setDatabaseClassification(DatabaseClassification.DL); + return dataSourceType; + } + + private StaticResource pluginSvg() { + StaticResource staticResource = new StaticResource(); + staticResource.setName("maxcompute-backend"); + staticResource.setSuffix("svg"); + return staticResource; + } +} diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/.babelrc b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/.babelrc new file mode 100644 index 0000000000..3a280ba34b --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/.babelrc @@ -0,0 +1,12 @@ +{ + "presets": [ + ["env", { + "modules": false, + "targets": { + "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] + } + }], + "stage-2" + ], + "plugins": ["transform-vue-jsx", "transform-runtime"] +} diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/.editorconfig b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/.editorconfig new file mode 100644 index 0000000000..9d08a1a828 --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/.gitignore b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/.gitignore new file mode 100644 index 0000000000..541a820f6c --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/.gitignore @@ -0,0 +1,14 @@ +.DS_Store +node_modules/ +/dist/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/.postcssrc.js b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/.postcssrc.js new file mode 100644 index 0000000000..eee3e92d7f --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/.postcssrc.js @@ -0,0 +1,10 @@ +// https://github.com/michael-ciniawsky/postcss-load-config + +module.exports = { + "plugins": { + "postcss-import": {}, + "postcss-url": {}, + // to edit target browsers: use "browserslist" field in package.json + "autoprefixer": {} + } +} diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/README.md b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/README.md new file mode 100644 index 0000000000..c1e4be95e0 --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/README.md @@ -0,0 +1,21 @@ +# deplugin-view-frontend + +> A Vue.js project + +## Build Setup + +``` bash +# install dependencies +npm install + +# serve with hot reload at localhost:8080 +npm run dev + +# build for production with minification +npm run build + +# build for production and view the bundle analyzer report +npm run build --report +``` + +For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader). diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/build/build-async-plugins.js b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/build/build-async-plugins.js new file mode 100644 index 0000000000..2303854531 --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/build/build-async-plugins.js @@ -0,0 +1,35 @@ +'use strict' +require('./check-versions')() + +process.env.NODE_ENV = 'production' + +const ora = require('ora') +const chalk = require('chalk') +const webpack = require('webpack') +const webpackConfig = require('./webpack.async-plugins') + +const spinner = ora('building for sync-plugins...') +spinner.start() + +webpack(webpackConfig, function (err, stats) { + spinner.stop() + if (err) throw err + process.stdout.write(stats.toString({ + colors: true, + modules: false, + children: false, + chunks: false, + chunkModules: false + }) + '\n\n') + + if (stats.hasErrors()) { + console.log(chalk.red(' Build failed with errors.\n')) + process.exit(1) + } + + console.log(chalk.cyan(' Build complete.\n')) + console.log(chalk.yellow( + ' Tip: built files are meant to be served over an HTTP server.\n' + + ' Opening index.html over file:// won\'t work.\n' + )) +}) diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/build/build.js b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/build/build.js new file mode 100644 index 0000000000..8f2ad8ad49 --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/build/build.js @@ -0,0 +1,41 @@ +'use strict' +require('./check-versions')() + +process.env.NODE_ENV = 'production' + +const ora = require('ora') +const rm = require('rimraf') +const path = require('path') +const chalk = require('chalk') +const webpack = require('webpack') +const config = require('../config') +const webpackConfig = require('./webpack.prod.conf') + +const spinner = ora('building for production...') +spinner.start() + +rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { + if (err) throw err + webpack(webpackConfig, (err, stats) => { + spinner.stop() + if (err) throw err + process.stdout.write(stats.toString({ + colors: true, + modules: false, + children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build. + chunks: false, + chunkModules: false + }) + '\n\n') + + if (stats.hasErrors()) { + console.log(chalk.red(' Build failed with errors.\n')) + process.exit(1) + } + + console.log(chalk.cyan(' Build complete.\n')) + console.log(chalk.yellow( + ' Tip: built files are meant to be served over an HTTP server.\n' + + ' Opening index.html over file:// won\'t work.\n' + )) + }) +}) diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/build/check-versions.js b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/build/check-versions.js new file mode 100644 index 0000000000..3ef972a08d --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/build/check-versions.js @@ -0,0 +1,54 @@ +'use strict' +const chalk = require('chalk') +const semver = require('semver') +const packageConfig = require('../package.json') +const shell = require('shelljs') + +function exec (cmd) { + return require('child_process').execSync(cmd).toString().trim() +} + +const versionRequirements = [ + { + name: 'node', + currentVersion: semver.clean(process.version), + versionRequirement: packageConfig.engines.node + } +] + +if (shell.which('npm')) { + versionRequirements.push({ + name: 'npm', + currentVersion: exec('npm --version'), + versionRequirement: packageConfig.engines.npm + }) +} + +module.exports = function () { + const warnings = [] + + for (let i = 0; i < versionRequirements.length; i++) { + const mod = versionRequirements[i] + + if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { + warnings.push(mod.name + ': ' + + chalk.red(mod.currentVersion) + ' should be ' + + chalk.green(mod.versionRequirement) + ) + } + } + + if (warnings.length) { + console.log('') + console.log(chalk.yellow('To use this template, you must update following to modules:')) + console.log() + + for (let i = 0; i < warnings.length; i++) { + const warning = warnings[i] + console.log(' ' + warning) + } + + console.log() + process.exit(1) + } +} diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/build/logo.png b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/build/logo.png new file mode 100644 index 0000000000..f3d2503fc2 Binary files /dev/null and b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/build/logo.png differ diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/build/utils.js b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/build/utils.js new file mode 100644 index 0000000000..e534fb0fd6 --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/build/utils.js @@ -0,0 +1,101 @@ +'use strict' +const path = require('path') +const config = require('../config') +const ExtractTextPlugin = require('extract-text-webpack-plugin') +const packageConfig = require('../package.json') + +exports.assetsPath = function (_path) { + const assetsSubDirectory = process.env.NODE_ENV === 'production' + ? config.build.assetsSubDirectory + : config.dev.assetsSubDirectory + + return path.posix.join(assetsSubDirectory, _path) +} + +exports.cssLoaders = function (options) { + options = options || {} + + const cssLoader = { + loader: 'css-loader', + options: { + sourceMap: options.sourceMap + } + } + + const postcssLoader = { + loader: 'postcss-loader', + options: { + sourceMap: options.sourceMap + } + } + + // generate loader string to be used with extract text plugin + function generateLoaders (loader, loaderOptions) { + const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader] + + if (loader) { + loaders.push({ + loader: loader + '-loader', + options: Object.assign({}, loaderOptions, { + sourceMap: options.sourceMap + }) + }) + } + + // Extract CSS when that option is specified + // (which is the case during production build) + if (options.extract) { + return ExtractTextPlugin.extract({ + use: loaders, + fallback: 'vue-style-loader' + }) + } else { + return ['vue-style-loader'].concat(loaders) + } + } + + // https://vue-loader.vuejs.org/en/configurations/extract-css.html + return { + css: generateLoaders(), + postcss: generateLoaders(), + less: generateLoaders('less'), + sass: generateLoaders('sass', { indentedSyntax: true }), + scss: generateLoaders('sass'), + stylus: generateLoaders('stylus'), + styl: generateLoaders('stylus') + } +} + +// Generate loaders for standalone style files (outside of .vue) +exports.styleLoaders = function (options) { + const output = [] + const loaders = exports.cssLoaders(options) + + for (const extension in loaders) { + const loader = loaders[extension] + output.push({ + test: new RegExp('\\.' + extension + '$'), + use: loader + }) + } + + return output +} + +exports.createNotifierCallback = () => { + const notifier = require('node-notifier') + + return (severity, errors) => { + if (severity !== 'error') return + + const error = errors[0] + const filename = error.file && error.file.split('!').pop() + + notifier.notify({ + title: packageConfig.name, + message: severity + ': ' + error.name, + subtitle: filename || '', + icon: path.join(__dirname, 'logo.png') + }) + } +} diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/build/vue-loader.conf.js b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/build/vue-loader.conf.js new file mode 100644 index 0000000000..33ed58bc0a --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/build/vue-loader.conf.js @@ -0,0 +1,22 @@ +'use strict' +const utils = require('./utils') +const config = require('../config') +const isProduction = process.env.NODE_ENV === 'production' +const sourceMapEnabled = isProduction + ? config.build.productionSourceMap + : config.dev.cssSourceMap + +module.exports = { + loaders: utils.cssLoaders({ + sourceMap: sourceMapEnabled, + extract: isProduction + }), + cssSourceMap: sourceMapEnabled, + cacheBusting: config.dev.cacheBusting, + transformToRequire: { + video: ['src', 'poster'], + source: 'src', + img: 'src', + image: 'xlink:href' + } +} diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/build/webpack.async-plugins.js b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/build/webpack.async-plugins.js new file mode 100644 index 0000000000..437ebaccdc --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/build/webpack.async-plugins.js @@ -0,0 +1,91 @@ +const webpack = require('webpack') +const path = require('path') +const utils = require('./utils') +const CopyPlugin = require("copy-webpack-plugin"); +const VueLoaderPlugin = require('vue-loader/lib/plugin'); +function resolve (dir) { + return path.join(__dirname, '..', dir) +} + +module.exports = { + mode: 'development', + entry: { + 'maxcompute': resolve('/src/views/maxcompute.vue') + }, + output: { + path: resolve('/static/'), + filename: '[name].js' + }, + resolve: { + extensions: ['.js', '.vue', '.json'], + alias: { + 'vue$': 'vue/dist/vue.esm.js', + '@': resolve('src') + } + }, + externals: { + vue: 'vue' + }, + module: { + rules: [ + { + test: /\.vue$/, + loader: 'vue-loader', + options: { + transformAssetUrls: { + video: 'src', + source: 'src', + img: 'src', + image: 'xlink:href' + } + } + }, + { + test: /.(sa|sc|c)ss$/, + use: [ + {loader: 'vue-style-loader'}, + 'css-loader', + 'sass-loader' + ] + }, + { + test: /\.js$/, + loader: 'babel-loader', + include: [resolve('src'), resolve('test')] + }, + { + test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('img/[name].[hash:7].[ext]') + } + }, + { + test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('media/[name].[hash:7].[ext]') + } + }, + { + test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('fonts/[name].[hash:7].[ext]') + } + } + ] + }, + plugins: [ + new VueLoaderPlugin(), + new webpack.DefinePlugin({ + 'process.env.NODE_ENV': '"production"' + }), + new CopyPlugin([ + {from: 'src/icons/svg/'} + ]), + ] +} diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/build/webpack.base.conf.js b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/build/webpack.base.conf.js new file mode 100644 index 0000000000..6ccc02dab4 --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/build/webpack.base.conf.js @@ -0,0 +1,91 @@ +'use strict' +const path = require('path') +const utils = require('./utils') +const config = require('../config') +const vueLoaderConfig = require('./vue-loader.conf') + +function resolve (dir) { + return path.join(__dirname, '..', dir) +} + + + +module.exports = { + context: path.resolve(__dirname, '../'), + entry: { + app: './src/main.js' + }, + output: { + path: config.build.assetsRoot, + filename: '[name].js', + publicPath: process.env.NODE_ENV === 'production' + ? config.build.assetsPublicPath + : config.dev.assetsPublicPath + }, + resolve: { + extensions: ['.js', '.vue', '.json'], + alias: { + 'vue$': 'vue/dist/vue.esm.js', + '@': resolve('src'), + } + }, + module: { + rules: [ + { + test: /\.vue$/, + loader: 'vue-loader', + options: vueLoaderConfig + }, + { + test: /\.js$/, + loader: 'babel-loader', + include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')] + }, + { + test: /\.svg$/, + loader: 'svg-sprite-loader', + include: [resolve('src/icons')], + options: { + symbolId: 'icon-[name]' + } + }, + { + test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, + loader: 'url-loader', + exclude: [resolve('src/icons')], + options: { + limit: 10000, + name: utils.assetsPath('img/[name].[hash:7].[ext]') + } + }, + { + test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('media/[name].[hash:7].[ext]') + } + }, + { + test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('fonts/[name].[hash:7].[ext]') + } + } + ] + }, + node: { + // prevent webpack from injecting useless setImmediate polyfill because Vue + // source contains it (although only uses it if it's native). + setImmediate: false, + // prevent webpack from injecting mocks to Node native modules + // that does not make sense for the client + dgram: 'empty', + fs: 'empty', + net: 'empty', + tls: 'empty', + child_process: 'empty' + } +} diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/build/webpack.dev.conf.js b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/build/webpack.dev.conf.js new file mode 100755 index 0000000000..070ae221f3 --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/build/webpack.dev.conf.js @@ -0,0 +1,95 @@ +'use strict' +const utils = require('./utils') +const webpack = require('webpack') +const config = require('../config') +const merge = require('webpack-merge') +const path = require('path') +const baseWebpackConfig = require('./webpack.base.conf') +const CopyWebpackPlugin = require('copy-webpack-plugin') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') +const portfinder = require('portfinder') + +const HOST = process.env.HOST +const PORT = process.env.PORT && Number(process.env.PORT) + +const devWebpackConfig = merge(baseWebpackConfig, { + module: { + rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) + }, + // cheap-module-eval-source-map is faster for development + devtool: config.dev.devtool, + + // these devServer options should be customized in /config/index.js + devServer: { + clientLogLevel: 'warning', + historyApiFallback: { + rewrites: [ + { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') }, + ], + }, + hot: true, + contentBase: false, // since we use CopyWebpackPlugin. + compress: true, + host: HOST || config.dev.host, + port: PORT || config.dev.port, + open: config.dev.autoOpenBrowser, + overlay: config.dev.errorOverlay + ? { warnings: false, errors: true } + : false, + publicPath: config.dev.assetsPublicPath, + proxy: config.dev.proxyTable, + quiet: true, // necessary for FriendlyErrorsPlugin + watchOptions: { + poll: config.dev.poll, + } + }, + plugins: [ + new webpack.DefinePlugin({ + 'process.env': require('../config/dev.env') + }), + new webpack.HotModuleReplacementPlugin(), + new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update. + new webpack.NoEmitOnErrorsPlugin(), + // https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: 'index.html', + template: 'index.html', + inject: true + }), + // copy custom static assets + new CopyWebpackPlugin([ + { + from: path.resolve(__dirname, '../static'), + to: config.dev.assetsSubDirectory, + ignore: ['.*'] + } + ]) + ] +}) + +module.exports = new Promise((resolve, reject) => { + portfinder.basePort = process.env.PORT || config.dev.port + portfinder.getPort((err, port) => { + if (err) { + reject(err) + } else { + // publish the new Port, necessary for e2e tests + process.env.PORT = port + // add port to devServer config + devWebpackConfig.devServer.port = port + + // Add FriendlyErrorsPlugin + devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({ + compilationSuccessInfo: { + messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`], + }, + onErrors: config.dev.notifyOnErrors + ? utils.createNotifierCallback() + : undefined + })) + + resolve(devWebpackConfig) + } + }) +}) diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/build/webpack.prod.conf.js b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/build/webpack.prod.conf.js new file mode 100644 index 0000000000..3e25238ef6 --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/build/webpack.prod.conf.js @@ -0,0 +1,145 @@ +'use strict' +const path = require('path') +const utils = require('./utils') +const webpack = require('webpack') +const config = require('../config') +const merge = require('webpack-merge') +const baseWebpackConfig = require('./webpack.base.conf') +const CopyWebpackPlugin = require('copy-webpack-plugin') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const ExtractTextPlugin = require('extract-text-webpack-plugin') +const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') +const UglifyJsPlugin = require('uglifyjs-webpack-plugin') + +const env = require('../config/prod.env') + +const webpackConfig = merge(baseWebpackConfig, { + module: { + rules: utils.styleLoaders({ + sourceMap: config.build.productionSourceMap, + extract: true, + usePostCSS: true + }) + }, + devtool: config.build.productionSourceMap ? config.build.devtool : false, + output: { + path: config.build.assetsRoot, + filename: utils.assetsPath('js/[name].[chunkhash].js'), + chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') + }, + plugins: [ + // http://vuejs.github.io/vue-loader/en/workflow/production.html + new webpack.DefinePlugin({ + 'process.env': env + }), + new UglifyJsPlugin({ + uglifyOptions: { + compress: { + warnings: false + } + }, + sourceMap: config.build.productionSourceMap, + parallel: true + }), + // extract css into its own file + new ExtractTextPlugin({ + filename: utils.assetsPath('css/[name].[contenthash].css'), + // Setting the following option to `false` will not extract CSS from codesplit chunks. + // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack. + // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, + // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110 + allChunks: true, + }), + // Compress extracted CSS. We are using this plugin so that possible + // duplicated CSS from different components can be deduped. + new OptimizeCSSPlugin({ + cssProcessorOptions: config.build.productionSourceMap + ? { safe: true, map: { inline: false } } + : { safe: true } + }), + // generate dist index.html with correct asset hash for caching. + // you can customize output by editing /index.html + // see https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: config.build.index, + template: 'index.html', + inject: true, + minify: { + removeComments: true, + collapseWhitespace: true, + removeAttributeQuotes: true + // more options: + // https://github.com/kangax/html-minifier#options-quick-reference + }, + // necessary to consistently work with multiple chunks via CommonsChunkPlugin + chunksSortMode: 'dependency' + }), + // keep module.id stable when vendor modules does not change + new webpack.HashedModuleIdsPlugin(), + // enable scope hoisting + new webpack.optimize.ModuleConcatenationPlugin(), + // split vendor js into its own file + new webpack.optimize.CommonsChunkPlugin({ + name: 'vendor', + minChunks (module) { + // any required modules inside node_modules are extracted to vendor + return ( + module.resource && + /\.js$/.test(module.resource) && + module.resource.indexOf( + path.join(__dirname, '../node_modules') + ) === 0 + ) + } + }), + // extract webpack runtime and module manifest to its own file in order to + // prevent vendor hash from being updated whenever app bundle is updated + new webpack.optimize.CommonsChunkPlugin({ + name: 'manifest', + minChunks: Infinity + }), + // This instance extracts shared chunks from code split chunks and bundles them + // in a separate chunk, similar to the vendor chunk + // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk + new webpack.optimize.CommonsChunkPlugin({ + name: 'app', + async: 'vendor-async', + children: true, + minChunks: 3 + }), + + // copy custom static assets + new CopyWebpackPlugin([ + { + from: path.resolve(__dirname, '../static'), + to: config.build.assetsSubDirectory, + ignore: ['.*'] + } + ]) + ] +}) + +if (config.build.productionGzip) { + const CompressionWebpackPlugin = require('compression-webpack-plugin') + + webpackConfig.plugins.push( + new CompressionWebpackPlugin({ + asset: '[path].gz[query]', + algorithm: 'gzip', + test: new RegExp( + '\\.(' + + config.build.productionGzipExtensions.join('|') + + ')$' + ), + threshold: 10240, + minRatio: 0.8 + }) + ) +} + +if (config.build.bundleAnalyzerReport) { + const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin + webpackConfig.plugins.push(new BundleAnalyzerPlugin()) +} + +module.exports = webpackConfig diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/config/dev.env.js b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/config/dev.env.js new file mode 100644 index 0000000000..1e22973ae7 --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/config/dev.env.js @@ -0,0 +1,7 @@ +'use strict' +const merge = require('webpack-merge') +const prodEnv = require('./prod.env') + +module.exports = merge(prodEnv, { + NODE_ENV: '"development"' +}) diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/config/index.js b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/config/index.js new file mode 100644 index 0000000000..c5eded7f81 --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/config/index.js @@ -0,0 +1,69 @@ +'use strict' +// Template version: 1.3.1 +// see http://vuejs-templates.github.io/webpack for documentation. + +const path = require('path') + +module.exports = { + dev: { + + // Paths + assetsSubDirectory: 'static', + assetsPublicPath: '/', + proxyTable: {}, + + // Various Dev Server settings + host: 'localhost', // can be overwritten by process.env.HOST + port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined + autoOpenBrowser: false, + errorOverlay: true, + notifyOnErrors: true, + poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- + + + /** + * Source Maps + */ + + // https://webpack.js.org/configuration/devtool/#development + devtool: 'cheap-module-eval-source-map', + + // If you have problems debugging vue-files in devtools, + // set this to false - it *may* help + // https://vue-loader.vuejs.org/en/options.html#cachebusting + cacheBusting: true, + + cssSourceMap: true + }, + + build: { + // Template for index.html + index: path.resolve(__dirname, '../dist/index.html'), + + // Paths + assetsRoot: path.resolve(__dirname, '../dist'), + assetsSubDirectory: 'static', + assetsPublicPath: '/', + + /** + * Source Maps + */ + + productionSourceMap: true, + // https://webpack.js.org/configuration/devtool/#production + devtool: '#source-map', + + // Gzip off by default as many popular static hosts such as + // Surge or Netlify already gzip all static assets for you. + // Before setting to `true`, make sure to: + // npm install --save-dev compression-webpack-plugin + productionGzip: false, + productionGzipExtensions: ['js', 'css'], + + // Run the build command with an extra argument to + // View the bundle analyzer report after build finishes: + // `npm run build --report` + // Set to `true` or `false` to always turn it on or off + bundleAnalyzerReport: process.env.npm_config_report + } +} diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/config/prod.env.js b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/config/prod.env.js new file mode 100644 index 0000000000..a6f997616e --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/config/prod.env.js @@ -0,0 +1,4 @@ +'use strict' +module.exports = { + NODE_ENV: '"production"' +} diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/index.html b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/index.html new file mode 100644 index 0000000000..03ce3fdf27 --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/index.html @@ -0,0 +1,12 @@ + + + + + + deplugin-view-frontend + + +
+ + + diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/package.json b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/package.json new file mode 100644 index 0000000000..c7e40d6f1b --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/package.json @@ -0,0 +1,73 @@ +{ + "name": "deplugin-datasource-frontend", + "version": "1.0.0", + "description": "A Vue.js project", + "author": "", + "private": true, + "scripts": { + "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", + "start": "npm run dev", + "build": "node build/build.js", + "buildPlugin": "node build/build-async-plugins.js" + }, + "dependencies": { + "@riophae/vue-treeselect": "0.4.0", + "highcharts": "^10.0.0", + "vue": "^2.5.2", + "vue-i18n": "7.3.2", + "vue-router": "^3.0.1", + "vue-uuid": "2.0.2", + "vuedraggable": "^2.24.3" + }, + "devDependencies": { + "autoprefixer": "^7.1.2", + "babel-core": "^6.22.1", + "babel-helper-vue-jsx-merge-props": "^2.0.3", + "babel-loader": "^7.1.1", + "babel-plugin-syntax-jsx": "^6.18.0", + "babel-plugin-transform-runtime": "^6.22.0", + "babel-plugin-transform-vue-jsx": "^3.5.0", + "babel-preset-env": "^1.3.2", + "babel-preset-stage-2": "^6.22.0", + "chalk": "^2.0.1", + "copy-webpack-plugin": "^4.6.0", + "css-loader": "^0.28.0", + "element-ui": "2.15.7", + "extract-text-webpack-plugin": "^4.0.0-beta.0", + "file-loader": "^1.1.4", + "friendly-errors-webpack-plugin": "^1.6.1", + "html-webpack-plugin": "^3.2.0", + "js-cookie": "2.2.0", + "node-notifier": "^8.0.1", + "optimize-css-assets-webpack-plugin": "^3.2.0", + "ora": "^1.2.0", + "portfinder": "^1.0.13", + "postcss-import": "^11.0.0", + "postcss-loader": "^2.0.8", + "postcss-url": "^7.2.1", + "rimraf": "^2.6.0", + "sass": "^1.33.0", + "sass-loader": "^7.3.1", + "semver": "^5.3.0", + "shelljs": "^0.8.5", + "svg-sprite-loader": "4.1.3", + "uglifyjs-webpack-plugin": "^1.1.1", + "url-loader": "^0.5.8", + "vue-loader": "^15.6.4", + "vue-style-loader": "^4.1.2", + "vue-template-compiler": "^2.5.2", + "webpack": "^4.8.1", + "webpack-bundle-analyzer": "^3.3.2", + "webpack-dev-server": "^3.1.11", + "webpack-merge": "^4.1.0" + }, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + }, + "browserslist": [ + "> 1%", + "last 2 versions", + "not ie <= 8" + ] +} diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/pom.xml b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/pom.xml new file mode 100644 index 0000000000..80e4c1305f --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/pom.xml @@ -0,0 +1,80 @@ + + + + + io.dataease + maxcompute + ${dataease.version} + + + 4.0.0 + maxcompute-frontend + + + UTF-8 + UTF-8 + 1.9.1 + + + + + + maven-clean-plugin + + + + static + + ** + + false + + + + + + + + com.github.eirslett + frontend-maven-plugin + ${frontend-maven-plugin.version} + + + install node and npm + + install-node-and-npm + + + + v16.20.2 + 7.6.3 + + + + + npm install + + npm + + + + install --force + + + + + npm run buildPlugin + + npm + + + run buildPlugin + + + + + + + diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/App.vue b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/App.vue new file mode 100644 index 0000000000..8542e07722 --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/App.vue @@ -0,0 +1,23 @@ + + + + + diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/assets/logo.png b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/assets/logo.png new file mode 100644 index 0000000000..f3d2503fc2 Binary files /dev/null and b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/assets/logo.png differ diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/components/HelloWorld.vue b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/components/HelloWorld.vue new file mode 100644 index 0000000000..1c19f2a399 --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/components/HelloWorld.vue @@ -0,0 +1,113 @@ + + + + + + diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/de-base/lang/en.js b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/de-base/lang/en.js new file mode 100644 index 0000000000..f8e5328b3d --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/de-base/lang/en.js @@ -0,0 +1,15 @@ +export default { + plugin_view_3d_pie: { + type_title: '3D-PIE', + label: 'Label', + angle: 'Angle' + }, + end_point: 'End Point', + projectName: 'Project Name', + access_id: 'Access ID', + access_key: 'Access Key', + query_timeout: 'Query timeout (seconds)', + second: 'second', + please_select: 'Please select', + query_timeout: 'query timeout', +} diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/de-base/lang/index.js b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/de-base/lang/index.js new file mode 100644 index 0000000000..e50d0478e6 --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/de-base/lang/index.js @@ -0,0 +1,49 @@ +import Vue from 'vue' +import VueI18n from 'vue-i18n' +import Cookies from 'js-cookie' +import elementEnLocale from 'element-ui/lib/locale/lang/en' // element-ui lang +import elementZhLocale from 'element-ui/lib/locale/lang/zh-CN'// element-ui lang +import elementTWLocale from 'element-ui/lib/locale/lang/zh-TW'// element-ui lang + +import localMessages from './messages' + + +Vue.use(VueI18n) + +const messages = { + en_US: { + ...localMessages['en_US'], + ...elementEnLocale + }, + zh_CN: { + ...localMessages['zh_CN'], + ...elementZhLocale + }, + zh_TW: { + ...localMessages['zh_TW'], + ...elementTWLocale + } +} +export function getLanguage () { + const chooseLanguage = Cookies.get('language') + if (chooseLanguage) return chooseLanguage + + // if has not choose language + const language = (navigator.language || navigator.browserLanguage).toLowerCase() + const locales = Object.keys(messages) + for (const locale of locales) { + if (language.indexOf(locale) > -1) { + return locale + } + } + return 'zh_CN' +} +const i18n = new VueI18n({ + // set locale + // options: en | zh | es + locale: getLanguage(), + // set locale messages + messages +}) + +export default i18n diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/de-base/lang/messages.js b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/de-base/lang/messages.js new file mode 100644 index 0000000000..844f8c0673 --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/de-base/lang/messages.js @@ -0,0 +1,17 @@ +import enLocale from './en' +import zhLocale from './zh' +import twLocale from './tw' + +const messages = { + en_US: { + ...enLocale + }, + zh_CN: { + ...zhLocale + }, + zh_TW: { + ...twLocale + } +} + +export default messages \ No newline at end of file diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/de-base/lang/tw.js b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/de-base/lang/tw.js new file mode 100644 index 0000000000..5f64c42884 --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/de-base/lang/tw.js @@ -0,0 +1,15 @@ +export default { + plugin_view_3d_pie: { + type_title: '3D餅圖', + label: '標籤', + angle: '角度' + }, + end_point: 'End Point', + projectName: '項目名稱', + access_id: 'Access ID', + access_key: 'Access Key', + query_timeout: '査詢超時(秒)', + second: '秒', + please_select: '請選擇', + query_timeout: '査詢超時', +} diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/de-base/lang/zh.js b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/de-base/lang/zh.js new file mode 100644 index 0000000000..901bfe2881 --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/de-base/lang/zh.js @@ -0,0 +1,15 @@ +export default { + plugin_view_3d_pie: { + type_title: '3D饼图', + label: '标签', + angle: '角度' + }, + end_point: 'End Point', + projectName: '项目名称', + access_id: 'Access ID', + access_key: 'Access Key', + query_timeout: '查询超时(秒)', + second: '秒', + please_select: '请选择', + query_timeout: '查询超时' +} diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/icons/index.js b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/icons/index.js new file mode 100644 index 0000000000..2c6b309c96 --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/icons/index.js @@ -0,0 +1,9 @@ +import Vue from 'vue' +import SvgIcon from '@/components/SvgIcon'// svg component + +// register globally +Vue.component('svg-icon', SvgIcon) + +const req = require.context('./svg', false, /\.svg$/) +const requireAll = requireContext => requireContext.keys().map(requireContext) +requireAll(req) diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/icons/svg/maxcompute-backend.svg b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/icons/svg/maxcompute-backend.svg new file mode 100644 index 0000000000..0075728ce5 --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/icons/svg/maxcompute-backend.svg @@ -0,0 +1 @@ +【icon】插件管理-导出 diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/icons/svg/maxcompute.jpg b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/icons/svg/maxcompute.jpg new file mode 100644 index 0000000000..679e0fea02 Binary files /dev/null and b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/icons/svg/maxcompute.jpg differ diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/icons/svgo.yml b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/icons/svgo.yml new file mode 100644 index 0000000000..d11906aec2 --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/icons/svgo.yml @@ -0,0 +1,22 @@ +# replace default config + +# multipass: true +# full: true + +plugins: + + # - name + # + # or: + # - name: false + # - name: true + # + # or: + # - name: + # param1: 1 + # param2: 2 + +- removeAttrs: + attrs: + - 'fill' + - 'fill-rule' diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/main.js b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/main.js new file mode 100644 index 0000000000..99c5626590 --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/main.js @@ -0,0 +1,33 @@ +// The Vue build version to load with the `import` command +// (runtime-only or standalone) has been set in webpack.base.conf with an alias. +import Vue from 'vue' +import App from './App' +import router from './router' +import ElementUI from 'element-ui' +import Cookies from 'js-cookie' +import i18n from './de-base/lang' +import draggable from 'vuedraggable' +import Treeselect from '@riophae/vue-treeselect' +import '@riophae/vue-treeselect/dist/vue-treeselect.css' +Vue.config.productionTip = false +Vue.use(ElementUI, { + size: Cookies.get('size') || 'medium', + i18n: (key, value) => i18n.t(key, value) +}) +Vue.component('Treeselect', Treeselect) +Vue.component('draggable', draggable) +Vue.prototype.hasDataPermission = function(pTarget, pSource) { + + if (pSource && pTarget) { + return pSource.indexOf(pTarget) > -1 + } + return false +} +/* eslint-disable no-new */ +new Vue({ + el: '#app', + router, + i18n, + components: { App }, + template: '' +}) diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/router/index.js b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/router/index.js new file mode 100644 index 0000000000..b5b55fa1fc --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/router/index.js @@ -0,0 +1,21 @@ +import Vue from 'vue' +import Router from 'vue-router' +import HelloWorld from '@/components/HelloWorld' +import maxcompute from '@/views/maxcompute' + +Vue.use(Router) + +export default new Router({ + routes: [ + { + path: '/', + name: 'HelloWorld', + component: HelloWorld + }, + { + path: '/maxcompute', + name: 'maxcompute', + component: maxcompute + } + ] +}) diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/utils/compare.js b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/utils/compare.js new file mode 100644 index 0000000000..b06a104a10 --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/utils/compare.js @@ -0,0 +1,29 @@ +export const compareItem = { + type: 'none', // year-yoy/month-yoy等 + resultData: 'percent', // 对比差sub,百分比percent等 + field: '', + custom: { + field: '', + calcType: '0', // 0-增长值,1-增长率 + timeType: '0', // 0-固定日期,1-日期区间 + currentTime: '', + compareTime: '', + currentTimeRange: [], + compareTimeRange: [] + } +} + +export const compareYearList = [ + { name: 'year_mom', value: 'year_mom' } +] + +export const compareMonthList = [ + { name: 'month_mom', value: 'month_mom' }, + { name: 'year_yoy', value: 'year_yoy' } +] + +export const compareDayList = [ + { name: 'day_mom', value: 'day_mom' }, + { name: 'month_yoy', value: 'month_yoy' }, + { name: 'year_yoy', value: 'year_yoy' } +] diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/utils/validate.js b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/utils/validate.js new file mode 100644 index 0000000000..3e8ffa9448 --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/utils/validate.js @@ -0,0 +1,15 @@ +/** + * Created by PanJiaChen on 16/11/18. + */ + +/** + * @param {string} path + * @returns {Boolean} + */ +export function isExternal(path) { + return /^(https?:|mailto:|tel:)/.test(path) || /^(http?:|mailto:|tel:)/.test(path) || path.startsWith('/api/pluginCommon/staticInfo') +} + + + + diff --git a/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/views/maxcompute.vue b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/views/maxcompute.vue new file mode 100644 index 0000000000..a155689f5a --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/maxcompute-frontend/src/views/maxcompute.vue @@ -0,0 +1,164 @@ + + + + + diff --git a/extensions/dataease-extensions-datasource/maxcompute/plugin.json b/extensions/dataease-extensions-datasource/maxcompute/plugin.json new file mode 100644 index 0000000000..e7ea9b7ab2 --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/plugin.json @@ -0,0 +1,13 @@ +{ + "name":"Maxcompute 数据源插件", + "free":0, + "store":"default", + "cost":0, + "category":"datasource", + "descript":"Maxcompute 插件,值得拥有", + "version":"1.18.0", + "creator":"DATAEASE", + "moduleName":"maxcompute-backend", + "require":"1.10.0", + "dsType":"maxcompute" +} diff --git a/extensions/dataease-extensions-datasource/maxcompute/pom.xml b/extensions/dataease-extensions-datasource/maxcompute/pom.xml new file mode 100644 index 0000000000..5b6a677dbc --- /dev/null +++ b/extensions/dataease-extensions-datasource/maxcompute/pom.xml @@ -0,0 +1,19 @@ + + + + dataease-extensions-datasource + io.dataease + ${dataease.version} + + 4.0.0 + + maxcompute + pom + + maxcompute-frontend + maxcompute-backend + + + diff --git a/extensions/dataease-extensions-datasource/mongo/build.sh b/extensions/dataease-extensions-datasource/mongo/build.sh new file mode 100755 index 0000000000..99feee776d --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/build.sh @@ -0,0 +1,6 @@ +#!/bin/sh +mvn clean package -U -Dmaven.test.skip=true + +cp mongo-backend/target/mongo-backend-1.18.0.jar . + +zip -r mongo.zip ./mongo-backend-1.18.0.jar ./mongobiDriver ./plugin.json diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-backend/pom.xml b/extensions/dataease-extensions-datasource/mongo/mongo-backend/pom.xml new file mode 100644 index 0000000000..e52800fdb0 --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/mongo-backend/pom.xml @@ -0,0 +1,109 @@ + + + + mongo + io.dataease + ${dataease.version} + + 4.0.0 + + mongo-backend + + + + io.dataease + dataease-plugin-datasource + + + + + + + src/main/java + + **/*.properties + **/*.xml + + false + + + src/main/resources + + **/* + + false + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 11 + 11 + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.4 + + + **/server/** + **/*.properties + **/Application* + + + + + + maven-clean-plugin + + + + src/main/resources/static + + ** + + false + + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + main-class-placement + generate-resources + + + + + + + + + + + + run + + + + + + + + + + + diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-backend/src/main/java/io/dataease/plugins/datasource/mongo/provider/MongoConfig.java b/extensions/dataease-extensions-datasource/mongo/mongo-backend/src/main/java/io/dataease/plugins/datasource/mongo/provider/MongoConfig.java new file mode 100644 index 0000000000..d6ef05151a --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/mongo-backend/src/main/java/io/dataease/plugins/datasource/mongo/provider/MongoConfig.java @@ -0,0 +1,23 @@ +package io.dataease.plugins.datasource.mongo.provider; + +import io.dataease.plugins.datasource.entity.JdbcConfiguration; +import lombok.Getter; +import lombok.Setter; +import org.apache.commons.lang3.StringUtils; + +@Getter +@Setter +public class MongoConfig extends JdbcConfiguration { + + private String driver = "com.mysql.jdbc.Driver"; + + private String extraParams; + + public String getJdbc() { + return "jdbc:mysql://HOSTNAME:PORT/DATABASE?EXTRA_PARAMS" + .replace("HOSTNAME", getHost().trim()) + .replace("PORT", getPort().toString().trim()) + .replace("DATABASE", getDataBase().trim()) + .replace("EXTRA_PARAMS", getExtraParams().trim()); + } +} diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-backend/src/main/java/io/dataease/plugins/datasource/mongo/provider/MongobiDsProvider.java b/extensions/dataease-extensions-datasource/mongo/mongo-backend/src/main/java/io/dataease/plugins/datasource/mongo/provider/MongobiDsProvider.java new file mode 100644 index 0000000000..979ee476d2 --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/mongo-backend/src/main/java/io/dataease/plugins/datasource/mongo/provider/MongobiDsProvider.java @@ -0,0 +1,134 @@ +package io.dataease.plugins.datasource.mongo.provider; + +import com.google.gson.Gson; +import io.dataease.plugins.common.base.domain.DeDriver; +import io.dataease.plugins.common.base.mapper.DeDriverMapper; +import io.dataease.plugins.common.dto.datasource.TableDesc; +import io.dataease.plugins.common.dto.datasource.TableField; +import io.dataease.plugins.common.exception.DataEaseException; +import io.dataease.plugins.common.request.datasource.DatasourceRequest; +import io.dataease.plugins.datasource.entity.JdbcConfiguration; +import io.dataease.plugins.datasource.mongo.query.MongoConstants; +import io.dataease.plugins.datasource.provider.DefaultJdbcProvider; +import io.dataease.plugins.datasource.provider.ExtendedJdbcClassLoader; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.lang.reflect.Method; +import java.sql.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + + +@Component() +public class MongobiDsProvider extends DefaultJdbcProvider { + @Resource + private DeDriverMapper deDriverMapper; + + @Override + public String getType() { + return "mongobi"; + } + + @Override + public boolean isUseDatasourcePool() { + return false; + } + + @Override + public Connection getConnection(DatasourceRequest datasourceRequest) throws Exception { + MongoConfig mongoConfig = new Gson().fromJson(datasourceRequest.getDatasource().getConfiguration(), MongoConfig.class); + + String defaultDriver = mongoConfig.getDriver(); + String customDriver = mongoConfig.getCustomDriver(); + + String url = mongoConfig.getJdbc(); + Properties props = new Properties(); + DeDriver deDriver = null; + if (StringUtils.isNotBlank(mongoConfig.getUsername())) { + props.setProperty("user", mongoConfig.getUsername()); + if (StringUtils.isNotBlank(mongoConfig.getPassword())) { + props.setProperty("password", mongoConfig.getPassword()); + } + } + + Connection conn; + String driverClassName ; + ExtendedJdbcClassLoader jdbcClassLoader; + if(isDefaultClassLoader(customDriver)){ + driverClassName = defaultDriver; + jdbcClassLoader = extendedJdbcClassLoader; + }else { + if(deDriver == null){ + deDriver = deDriverMapper.selectByPrimaryKey(customDriver); + } + driverClassName = deDriver.getDriverClass(); + jdbcClassLoader = getCustomJdbcClassLoader(deDriver); + } + + Driver driverClass = (Driver) jdbcClassLoader.loadClass(driverClassName).newInstance(); + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + try { + Thread.currentThread().setContextClassLoader(jdbcClassLoader); + conn= driverClass.connect(url, props); + }catch (Exception e){ + e.printStackTrace(); + throw e; + }finally { + Thread.currentThread().setContextClassLoader(classLoader); + } + return conn; + } + + @Override + public List getTables(DatasourceRequest datasourceRequest) throws Exception { + List tables = new ArrayList<>(); + String queryStr = getTablesSql(datasourceRequest); + JdbcConfiguration jdbcConfiguration = new Gson().fromJson(datasourceRequest.getDatasource().getConfiguration(), JdbcConfiguration.class); + int queryTimeout = jdbcConfiguration.getQueryTimeout() > 0 ? jdbcConfiguration.getQueryTimeout() : 0; + try (Connection con = getConnectionFromPool(datasourceRequest); Statement statement = getStatement(con, queryTimeout); ResultSet resultSet = statement.executeQuery(queryStr)) { + while (resultSet.next()) { + tables.add(getTableDesc(datasourceRequest, resultSet)); + } + } catch (Exception e) { + DataEaseException.throwException(e); + } + + return tables; + } + + private TableDesc getTableDesc(DatasourceRequest datasourceRequest, ResultSet resultSet) throws SQLException { + TableDesc tableDesc = new TableDesc(); + tableDesc.setName(resultSet.getString(1)); + return tableDesc; + } + + @Override + public List getTableFields(DatasourceRequest datasourceRequest) throws Exception { + + datasourceRequest.setQuery("select * from " + String.format(MongoConstants.KEYWORD_TABLE, datasourceRequest.getTable()) + " limit 0"); + return fetchResultField(datasourceRequest); + } + + @Override + public String checkStatus(DatasourceRequest datasourceRequest) throws Exception { + String queryStr = getTablesSql(datasourceRequest); + JdbcConfiguration jdbcConfiguration = new Gson().fromJson(datasourceRequest.getDatasource().getConfiguration(), JdbcConfiguration.class); + int queryTimeout = jdbcConfiguration.getQueryTimeout() > 0 ? jdbcConfiguration.getQueryTimeout() : 0; + try (Connection con = getConnection(datasourceRequest); Statement statement = getStatement(con, queryTimeout); ResultSet resultSet = statement.executeQuery(queryStr)) { + } catch (Exception e) { + e.printStackTrace(); + DataEaseException.throwException(e.getMessage()); + } + return "Success"; + } + + + @Override + public String getTablesSql(DatasourceRequest datasourceRequest) throws Exception { + return "show tables"; + } + +} diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-backend/src/main/java/io/dataease/plugins/datasource/mongo/query/MongoConstants.java b/extensions/dataease-extensions-datasource/mongo/mongo-backend/src/main/java/io/dataease/plugins/datasource/mongo/query/MongoConstants.java new file mode 100644 index 0000000000..a6610fad69 --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/mongo-backend/src/main/java/io/dataease/plugins/datasource/mongo/query/MongoConstants.java @@ -0,0 +1,51 @@ +package io.dataease.plugins.datasource.mongo.query; + + + +import io.dataease.plugins.common.constants.datasource.SQLConstants; + +import static io.dataease.plugins.common.constants.DatasourceTypes.mysql; + +public class MongoConstants extends SQLConstants { + + public static final String KEYWORD_TABLE = mysql.getKeywordPrefix() + "%s" + mysql.getKeywordSuffix(); + + public static final String KEYWORD_FIX = "%s." + mysql.getKeywordPrefix() + "%s" + mysql.getKeywordSuffix(); + + public static final String ALIAS_FIX = mysql.getAliasPrefix() + "%s" + mysql.getAliasSuffix(); + + public static final String UNIX_TIMESTAMP = "UNIX_TIMESTAMP(%s)"; + + public static final String DATE_FORMAT = "DATE_FORMAT(%s,'%s')"; + + public static final String FROM_UNIXTIME = "FROM_UNIXTIME(%s,'%s')"; + + public static final String STR_TO_DATE = "STR_TO_DATE(%s,'%s')"; + + public static final String CAST = "CAST(%s AS %s)"; + + public static final String DEFAULT_DATE_FORMAT = "%Y-%m-%d %H:%i:%S"; + + public static final String DEFAULT_INT_FORMAT = "DECIMAL(20,0)"; + + public static final String DEFAULT_FLOAT_FORMAT = "DECIMAL(20,8)"; + + public static final String WHERE_VALUE_NULL = "(NULL,'')"; + + public static final String WHERE_VALUE_VALUE = "'%s'"; + + public static final String AGG_COUNT = "COUNT(*)"; + + public static final String AGG_FIELD = "%s(%s)"; + + public static final String WHERE_BETWEEN = "'%s' AND '%s'"; + + public static final String BRACKETS = "(%s)"; + + public static final String NAME = "mysql"; + + public static final String GROUP_CONCAT = "group_concat(%s)"; + + + +} diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-backend/src/main/java/io/dataease/plugins/datasource/mongo/query/MongobiQueryProvider.java b/extensions/dataease-extensions-datasource/mongo/mongo-backend/src/main/java/io/dataease/plugins/datasource/mongo/query/MongobiQueryProvider.java new file mode 100644 index 0000000000..f79e43bafa --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/mongo-backend/src/main/java/io/dataease/plugins/datasource/mongo/query/MongobiQueryProvider.java @@ -0,0 +1,1331 @@ +package io.dataease.plugins.datasource.mongo.query; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.dataease.plugins.common.base.domain.ChartViewWithBLOBs; +import io.dataease.plugins.common.base.domain.DatasetTableField; +import io.dataease.plugins.common.base.domain.DatasetTableFieldExample; +import io.dataease.plugins.common.base.domain.Datasource; +import io.dataease.plugins.common.base.mapper.DatasetTableFieldMapper; +import io.dataease.plugins.common.constants.DeTypeConstants; +import io.dataease.plugins.common.constants.datasource.SQLConstants; +import io.dataease.plugins.common.dto.chart.ChartCustomFilterItemDTO; +import io.dataease.plugins.common.dto.chart.ChartFieldCustomFilterDTO; +import io.dataease.plugins.common.dto.chart.ChartViewFieldDTO; +import io.dataease.plugins.common.dto.datasource.DeSortField; +import io.dataease.plugins.common.dto.sqlObj.SQLObj; +import io.dataease.plugins.common.request.chart.ChartExtFilterRequest; +import io.dataease.plugins.common.request.permission.DataSetRowPermissionsTreeDTO; +import io.dataease.plugins.common.request.permission.DatasetRowPermissionsTreeItem; +import io.dataease.plugins.datasource.entity.Dateformat; +import io.dataease.plugins.datasource.entity.PageInfo; +import io.dataease.plugins.datasource.query.QueryProvider; +import io.dataease.plugins.datasource.query.Utils; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; +import org.stringtemplate.v4.ST; +import org.stringtemplate.v4.STGroup; +import org.stringtemplate.v4.STGroupFile; + +import javax.annotation.Resource; +import java.text.MessageFormat; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import static io.dataease.plugins.common.constants.datasource.SQLConstants.TABLE_ALIAS_PREFIX; + + +@Component() +public class MongobiQueryProvider extends QueryProvider { + @Resource + private DatasetTableFieldMapper datasetTableFieldMapper; + + @Override + public Integer transFieldType(String field) { + switch (field) { + case "CHAR": + case "VARCHAR": + case "TEXT": + case "TINYTEXT": + case "MEDIUMTEXT": + case "LONGTEXT": + case "ENUM": + return 0;// 文本 + case "DATE": + case "TIME": + case "YEAR": + case "DATETIME": + case "TIMESTAMP": + return 1;// 时间 + case "INT": + case "SMALLINT": + case "MEDIUMINT": + case "INTEGER": + case "BIGINT": + case "LONG": //增加了LONG类型 + return 2;// 整型 + case "FLOAT": + case "DOUBLE": + case "DECIMAL": + return 3;// 浮点 + case "BIT": + case "TINYINT": + return 4;// 布尔 + default: + return 0; + } + } + + @Override + public String createSQLPreview(String sql, String orderBy) { + return "SELECT * FROM (" + sqlFix(sql) + ") AS tmp " + " LIMIT 0,1000"; + } + + @Override + public String createQuerySQL(String table, List fields, boolean isGroup, Datasource ds, List fieldCustomFilter, List rowPermissionsTree) { + return createQuerySQL(table, fields, isGroup, ds, fieldCustomFilter, rowPermissionsTree, null); + } + + @Override + public String createQuerySQLAsTmp(String sql, List fields, boolean isGroup, List fieldCustomFilter, List rowPermissionsTree) { + return createQuerySQL("(" + sqlFix(sql) + ")", fields, isGroup, null, fieldCustomFilter, rowPermissionsTree, null); + } + + private SQLObj buildSortField(DeSortField f, SQLObj tableObj, int index) { + String originField; + if (ObjectUtils.isNotEmpty(f.getExtField()) && f.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(f.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(f.getExtField()) && f.getExtField() == 1) { + originField = String.format(MongoConstants.KEYWORD_FIX, tableObj.getTableAlias(), f.getOriginName()); + } else { + originField = String.format(MongoConstants.KEYWORD_FIX, tableObj.getTableAlias(), f.getOriginName()); + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_X_PREFIX, index); + String fieldName = ""; + // 处理横轴字段 + if (f.getDeExtractType() == 1) { + if (f.getDeType() == 2 || f.getDeType() == 3) { + fieldName = String.format(MongoConstants.UNIX_TIMESTAMP, originField) + "*1000"; + } else { + fieldName = String.format(MongoConstants.DATE_FORMAT, originField, MongoConstants.DEFAULT_DATE_FORMAT); + } + } else if (f.getDeExtractType() == 0) { + if (f.getDeType() == 2) { + fieldName = String.format(MongoConstants.CAST, originField, MongoConstants.DEFAULT_INT_FORMAT); + } else if (f.getDeType() == 3) { + fieldName = String.format(MongoConstants.CAST, originField, MongoConstants.DEFAULT_FLOAT_FORMAT); + } else if (f.getDeType() == 1) { + fieldName = String.format(MongoConstants.STR_TO_DATE, originField, StringUtils.isNotEmpty(f.getDateFormat()) ? f.getDateFormat() : MongoConstants.DEFAULT_DATE_FORMAT); + } else { + fieldName = originField; + } + } else { + if (f.getDeType() == 1) { + String cast = String.format(MongoConstants.CAST, originField, MongoConstants.DEFAULT_INT_FORMAT) + "/1000"; + fieldName = String.format(MongoConstants.FROM_UNIXTIME, cast, MongoConstants.DEFAULT_DATE_FORMAT); + } else if (f.getDeType() == 2) { + fieldName = String.format(MongoConstants.CAST, originField, MongoConstants.DEFAULT_INT_FORMAT); + } else { + fieldName = originField; + } + } + SQLObj result = SQLObj.builder().orderField(originField).orderAlias(originField).orderDirection(f.getOrderDirection()).build(); + return result; + + } + + @Override + public String createQuerySQL(String table, List fields, boolean isGroup, Datasource ds, List fieldCustomFilter, List rowPermissionsTree, List sortFields) { + SQLObj tableObj = SQLObj.builder() + .tableName((table.startsWith("(") && table.endsWith(")")) ? table : String.format(MongoConstants.KEYWORD_TABLE, table)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 0)) + .build(); + List xFields = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(fields)) { + for (int i = 0; i < fields.size(); i++) { + DatasetTableField f = fields.get(i); + String originField; + if (ObjectUtils.isNotEmpty(f.getExtField()) && f.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(f.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(f.getExtField()) && f.getExtField() == 1) { + originField = String.format(MongoConstants.KEYWORD_FIX, tableObj.getTableAlias(), f.getOriginName()); + } else { + originField = String.format(MongoConstants.KEYWORD_FIX, tableObj.getTableAlias(), f.getOriginName()); + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_X_PREFIX, i); + String fieldName = ""; + // 处理横轴字段 + if (f.getDeExtractType() == 1) { + if (f.getDeType() == 2 || f.getDeType() == 3) { + fieldName = String.format(MongoConstants.UNIX_TIMESTAMP, originField) + "*1000"; + } else { + fieldName = String.format(MongoConstants.DATE_FORMAT, originField, MongoConstants.DEFAULT_DATE_FORMAT); + } + } else if (f.getDeExtractType() == 0) { + if (f.getDeType() == 2) { + fieldName = String.format(MongoConstants.CAST, originField, MongoConstants.DEFAULT_INT_FORMAT); + } else if (f.getDeType() == 3) { + fieldName = String.format(MongoConstants.CAST, originField, MongoConstants.DEFAULT_FLOAT_FORMAT); + } else if (f.getDeType() == 1) { + fieldName = String.format(MongoConstants.STR_TO_DATE, originField, StringUtils.isNotEmpty(f.getDateFormat()) ? f.getDateFormat() : MongoConstants.DEFAULT_DATE_FORMAT); + } else { + fieldName = originField; + } + } else { + if (f.getDeType() == 1) { + String cast = String.format(MongoConstants.CAST, originField, MongoConstants.DEFAULT_INT_FORMAT) + "/1000"; + fieldName = String.format(MongoConstants.FROM_UNIXTIME, cast, MongoConstants.DEFAULT_DATE_FORMAT); + } else if (f.getDeType() == 2) { + fieldName = String.format(MongoConstants.CAST, originField, MongoConstants.DEFAULT_INT_FORMAT); + } else { + fieldName = originField; + } + } + xFields.add(SQLObj.builder() + .fieldName(fieldName) + .fieldAlias(fieldAlias) + .build()); + } + } + + + STGroup stg = new STGroupFile(SQLConstants.SQL_TEMPLATE); + ST st_sql = stg.getInstanceOf("previewSql"); + st_sql.add("isGroup", isGroup); + if (CollectionUtils.isNotEmpty(xFields)) st_sql.add("groups", xFields); + if (ObjectUtils.isNotEmpty(tableObj)) st_sql.add("table", tableObj); + + String customWheres = transCustomFilterList(tableObj, fieldCustomFilter); + // row permissions tree + String whereTrees = transFilterTrees(tableObj, rowPermissionsTree); + List wheres = new ArrayList<>(); + if (customWheres != null) wheres.add(customWheres); + if (whereTrees != null) wheres.add(whereTrees); + if (CollectionUtils.isNotEmpty(wheres)) st_sql.add("filters", wheres); + + List xOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(sortFields)) { + int step = fields.size(); + for (int i = step; i < (step + sortFields.size()); i++) { + DeSortField deSortField = sortFields.get(i - step); + SQLObj order = buildSortField(deSortField, tableObj, i); + xOrders.add(order); + } + } + if (ObjectUtils.isNotEmpty(xOrders)) { + st_sql.add("orders", xOrders); + } + + return st_sql.render(); + } + + @Override + public String createQuerySQLAsTmp(String sql, List fields, boolean isGroup, List fieldCustomFilter, List rowPermissionsTree, List sortFields) { + return createQuerySQL("(" + sqlFix(sql) + ")", fields, isGroup, null, fieldCustomFilter, rowPermissionsTree, sortFields); + } + + @Override + public String createQueryTableWithPage(String table, List fields, Integer page, Integer pageSize, Integer realSize, boolean isGroup, Datasource ds, List fieldCustomFilter, List rowPermissionsTree) { + return createQuerySQL(table, fields, isGroup, null, fieldCustomFilter, rowPermissionsTree) + " LIMIT " + (page - 1) * pageSize + "," + realSize; + } + + @Override + public String createQueryTableWithLimit(String table, List fields, Integer limit, boolean isGroup, Datasource ds, List fieldCustomFilter, List rowPermissionsTree) { + return createQuerySQL(table, fields, isGroup, null, fieldCustomFilter, rowPermissionsTree) + " LIMIT 0," + limit; + } + + @Override + public String createQuerySqlWithLimit(String sql, List fields, Integer limit, boolean isGroup, List fieldCustomFilter, List rowPermissionsTree) { + return createQuerySQLAsTmp(sql, fields, isGroup, fieldCustomFilter, rowPermissionsTree) + " LIMIT 0," + limit; + } + + @Override + public String createQuerySQLWithPage(String sql, List fields, Integer page, Integer pageSize, Integer realSize, boolean isGroup, List fieldCustomFilter, List rowPermissionsTree) { + return createQuerySQLAsTmp(sql, fields, isGroup, fieldCustomFilter, rowPermissionsTree) + " LIMIT " + (page - 1) * pageSize + "," + realSize; + } + + @Override + public String getSQL(String table, List xAxis, List yAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, Datasource ds, ChartViewWithBLOBs view) { + SQLObj tableObj = SQLObj.builder() + .tableName((table.startsWith("(") && table.endsWith(")")) ? table : String.format(MongoConstants.KEYWORD_TABLE, table)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 0)) + .build(); + List xFields = new ArrayList<>(); + List xOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(xAxis)) { + for (int i = 0; i < xAxis.size(); i++) { + ChartViewFieldDTO x = xAxis.get(i); + String originField; + if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(x.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 1) { + originField = String.format(MongoConstants.KEYWORD_FIX, tableObj.getTableAlias(), x.getOriginName()); + } else { + originField = String.format(MongoConstants.KEYWORD_FIX, tableObj.getTableAlias(), x.getOriginName()); + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_X_PREFIX, i); + // 处理横轴字段 + xFields.add(getXFields(x, originField, fieldAlias)); + // 处理横轴排序 + if (StringUtils.isNotEmpty(x.getSort()) && Utils.joinSort(x.getSort())) { + xOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(x.getSort()) + .build()); + } + } + } + List yFields = new ArrayList<>(); + List yWheres = new ArrayList<>(); + List yOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(yAxis)) { + for (int i = 0; i < yAxis.size(); i++) { + ChartViewFieldDTO y = yAxis.get(i); + String originField; + if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(y.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 1) { + originField = String.format(MongoConstants.KEYWORD_FIX, tableObj.getTableAlias(), y.getOriginName()); + } else { + originField = String.format(MongoConstants.KEYWORD_FIX, tableObj.getTableAlias(), y.getOriginName()); + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_Y_PREFIX, i); + // 处理纵轴字段 + yFields.add(getYFields(y, originField, fieldAlias)); + // 处理纵轴过滤 + yWheres.add(getYWheres(y, originField, fieldAlias)); + // 处理纵轴排序 + if (StringUtils.isNotEmpty(y.getSort()) && Utils.joinSort(y.getSort())) { + yOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(y.getSort()) + .build()); + } + } + } + // 处理视图中字段过滤 + String customWheres = transCustomFilterList(tableObj, fieldCustomFilter); + // 处理仪表板字段过滤 + String extWheres = transExtFilterList(tableObj, extFilterRequestList); + // row permissions tree + String whereTrees = transFilterTrees(tableObj, rowPermissionsTree); + // 构建sql所有参数 + List fields = new ArrayList<>(); + fields.addAll(xFields); + fields.addAll(yFields); + List wheres = new ArrayList<>(); + if (customWheres != null) wheres.add(customWheres); + if (extWheres != null) wheres.add(extWheres); + if (whereTrees != null) wheres.add(whereTrees); + List groups = new ArrayList<>(); + groups.addAll(xFields); + // 外层再次套sql + List orders = new ArrayList<>(); + orders.addAll(xOrders); + orders.addAll(yOrders); + List aggWheres = new ArrayList<>(); + aggWheres.addAll(yWheres.stream().filter(ObjectUtils::isNotEmpty).collect(Collectors.toList())); + + STGroup stg = new STGroupFile(SQLConstants.SQL_TEMPLATE); + ST st_sql = stg.getInstanceOf("querySql"); + if (CollectionUtils.isNotEmpty(xFields)) st_sql.add("groups", xFields); + if (CollectionUtils.isNotEmpty(yFields)) st_sql.add("aggregators", yFields); + if (CollectionUtils.isNotEmpty(wheres)) st_sql.add("filters", wheres); + if (ObjectUtils.isNotEmpty(tableObj)) st_sql.add("table", tableObj); + String sql = st_sql.render(); + + ST st = stg.getInstanceOf("querySql"); + SQLObj tableSQL = SQLObj.builder() + .tableName(String.format(MongoConstants.BRACKETS, sql)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 1)) + .build(); + if (CollectionUtils.isNotEmpty(aggWheres)) st.add("filters", aggWheres); + if (CollectionUtils.isNotEmpty(orders)) st.add("orders", orders); + if (ObjectUtils.isNotEmpty(tableSQL)) st.add("table", tableSQL); + return sqlLimit(st.render(), view); + } + + @Override + public String getSQLWithPage(boolean isTable, String table, List xAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, Datasource ds, ChartViewWithBLOBs view, PageInfo pageInfo) { + String limit = ((pageInfo.getGoPage() != null && pageInfo.getPageSize() != null) ? " LIMIT " + (pageInfo.getGoPage() - 1) * pageInfo.getPageSize() + "," + pageInfo.getPageSize() : ""); + if (isTable) { + return originalTableInfo(table, xAxis, fieldCustomFilter, rowPermissionsTree, extFilterRequestList, ds, view) + limit; + } else { + return originalTableInfo("(" + sqlFix(table) + ")", xAxis, fieldCustomFilter, rowPermissionsTree, extFilterRequestList, ds, view) + limit; + } + } + + private String originalTableInfo(String table, List xAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, Datasource ds, ChartViewWithBLOBs view) { + SQLObj tableObj = SQLObj.builder() + .tableName((table.startsWith("(") && table.endsWith(")")) ? table : String.format(MongoConstants.KEYWORD_TABLE, table)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 0)) + .build(); + List xFields = new ArrayList<>(); + List xOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(xAxis)) { + for (int i = 0; i < xAxis.size(); i++) { + ChartViewFieldDTO x = xAxis.get(i); + String originField; + if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(x.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 1) { + originField = String.format(MongoConstants.KEYWORD_FIX, tableObj.getTableAlias(), x.getOriginName()); + } else { + if (x.getDeType() == 2 || x.getDeType() == 3) { + originField = String.format(MongoConstants.CAST, String.format(MongoConstants.KEYWORD_FIX, tableObj.getTableAlias(), x.getOriginName()), MongoConstants.DEFAULT_FLOAT_FORMAT); + } else { + originField = String.format(MongoConstants.KEYWORD_FIX, tableObj.getTableAlias(), x.getOriginName()); + } + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_X_PREFIX, i); + // 处理横轴字段 + xFields.add(getXFields(x, originField, fieldAlias)); + // 处理横轴排序 + if (StringUtils.isNotEmpty(x.getSort()) && Utils.joinSort(x.getSort())) { + xOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(x.getSort()) + .build()); + } + } + } + // 处理视图中字段过滤 + String customWheres = transCustomFilterList(tableObj, fieldCustomFilter); + // 处理仪表板字段过滤 + String extWheres = transExtFilterList(tableObj, extFilterRequestList); + // row permissions tree + String whereTrees = transFilterTrees(tableObj, rowPermissionsTree); + // 构建sql所有参数 + List fields = new ArrayList<>(); + fields.addAll(xFields); + List wheres = new ArrayList<>(); + if (customWheres != null) wheres.add(customWheres); + if (extWheres != null) wheres.add(extWheres); + if (whereTrees != null) wheres.add(whereTrees); + List groups = new ArrayList<>(); + groups.addAll(xFields); + // 外层再次套sql + List orders = new ArrayList<>(); + orders.addAll(xOrders); + + STGroup stg = new STGroupFile(SQLConstants.SQL_TEMPLATE); + ST st_sql = stg.getInstanceOf("previewSql"); + st_sql.add("isGroup", false); + if (CollectionUtils.isNotEmpty(xFields)) st_sql.add("groups", xFields); + if (CollectionUtils.isNotEmpty(wheres)) st_sql.add("filters", wheres); + if (ObjectUtils.isNotEmpty(tableObj)) st_sql.add("table", tableObj); + String sql = st_sql.render(); + + ST st = stg.getInstanceOf("previewSql"); + st.add("isGroup", false); + SQLObj tableSQL = SQLObj.builder() + .tableName(String.format(MongoConstants.BRACKETS, sql)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 1)) + .build(); + if (CollectionUtils.isNotEmpty(orders)) st.add("orders", orders); + if (ObjectUtils.isNotEmpty(tableSQL)) st.add("table", tableSQL); + return st.render(); + } + @Override + public String getSQLTableInfo(String table, List xAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, Datasource ds, ChartViewWithBLOBs view) { + return sqlLimit(originalTableInfo(table, xAxis, fieldCustomFilter, rowPermissionsTree, extFilterRequestList, ds, view), view); + } + + @Override + public String getSQLAsTmpTableInfo(String sql, List xAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, Datasource ds, ChartViewWithBLOBs view) { + return getSQLTableInfo("(" + sqlFix(sql) + ")", xAxis, fieldCustomFilter, rowPermissionsTree, extFilterRequestList, null, view); + } + + + @Override + public String getSQLAsTmp(String sql, List xAxis, List yAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, ChartViewWithBLOBs view) { + return getSQL("(" + sqlFix(sql) + ")", xAxis, yAxis, fieldCustomFilter, rowPermissionsTree, extFilterRequestList, null, view); + } + + @Override + public String getSQLStack(String table, List xAxis, List yAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, List extStack, Datasource ds, ChartViewWithBLOBs view) { + SQLObj tableObj = SQLObj.builder() + .tableName((table.startsWith("(") && table.endsWith(")")) ? table : String.format(MongoConstants.KEYWORD_TABLE, table)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 0)) + .build(); + List xFields = new ArrayList<>(); + List xOrders = new ArrayList<>(); + List xList = new ArrayList<>(); + xList.addAll(xAxis); + xList.addAll(extStack); + if (CollectionUtils.isNotEmpty(xList)) { + for (int i = 0; i < xList.size(); i++) { + ChartViewFieldDTO x = xList.get(i); + String originField; + if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(x.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 1) { + originField = String.format(MongoConstants.KEYWORD_FIX, tableObj.getTableAlias(), x.getOriginName()); + } else { + originField = String.format(MongoConstants.KEYWORD_FIX, tableObj.getTableAlias(), x.getOriginName()); + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_X_PREFIX, i); + // 处理横轴字段 + xFields.add(getXFields(x, originField, fieldAlias)); + // 处理横轴排序 + if (StringUtils.isNotEmpty(x.getSort()) && Utils.joinSort(x.getSort())) { + xOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(x.getSort()) + .build()); + } + } + } + List yFields = new ArrayList<>(); + List yWheres = new ArrayList<>(); + List yOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(yAxis)) { + for (int i = 0; i < yAxis.size(); i++) { + ChartViewFieldDTO y = yAxis.get(i); + String originField; + if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(y.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 1) { + originField = String.format(MongoConstants.KEYWORD_FIX, tableObj.getTableAlias(), y.getOriginName()); + } else { + originField = String.format(MongoConstants.KEYWORD_FIX, tableObj.getTableAlias(), y.getOriginName()); + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_Y_PREFIX, i); + // 处理纵轴字段 + yFields.add(getYFields(y, originField, fieldAlias)); + // 处理纵轴过滤 + yWheres.add(getYWheres(y, originField, fieldAlias)); + // 处理纵轴排序 + if (StringUtils.isNotEmpty(y.getSort()) && Utils.joinSort(y.getSort())) { + yOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(y.getSort()) + .build()); + } + } + } + // 处理视图中字段过滤 + String customWheres = transCustomFilterList(tableObj, fieldCustomFilter); + // 处理仪表板字段过滤 + String extWheres = transExtFilterList(tableObj, extFilterRequestList); + // row permissions tree + String whereTrees = transFilterTrees(tableObj, rowPermissionsTree); + // 构建sql所有参数 + List fields = new ArrayList<>(); + fields.addAll(xFields); + fields.addAll(yFields); + List wheres = new ArrayList<>(); + if (customWheres != null) wheres.add(customWheres); + if (extWheres != null) wheres.add(extWheres); + if (whereTrees != null) wheres.add(whereTrees); + List groups = new ArrayList<>(); + groups.addAll(xFields); + // 外层再次套sql + List orders = new ArrayList<>(); + orders.addAll(xOrders); + orders.addAll(yOrders); + List aggWheres = new ArrayList<>(); + aggWheres.addAll(yWheres.stream().filter(ObjectUtils::isNotEmpty).collect(Collectors.toList())); + + STGroup stg = new STGroupFile(SQLConstants.SQL_TEMPLATE); + ST st_sql = stg.getInstanceOf("querySql"); + if (CollectionUtils.isNotEmpty(xFields)) st_sql.add("groups", xFields); + if (CollectionUtils.isNotEmpty(yFields)) st_sql.add("aggregators", yFields); + if (CollectionUtils.isNotEmpty(wheres)) st_sql.add("filters", wheres); + if (ObjectUtils.isNotEmpty(tableObj)) st_sql.add("table", tableObj); + String sql = st_sql.render(); + + ST st = stg.getInstanceOf("querySql"); + SQLObj tableSQL = SQLObj.builder() + .tableName(String.format(MongoConstants.BRACKETS, sql)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 1)) + .build(); + if (CollectionUtils.isNotEmpty(aggWheres)) st.add("filters", aggWheres); + if (CollectionUtils.isNotEmpty(orders)) st.add("orders", orders); + if (ObjectUtils.isNotEmpty(tableSQL)) st.add("table", tableSQL); + return sqlLimit(st.render(), view); + } + + @Override + public String getSQLAsTmpStack(String table, List xAxis, List yAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, List extStack, ChartViewWithBLOBs view) { + return getSQLStack("(" + sqlFix(table) + ")", xAxis, yAxis, fieldCustomFilter, rowPermissionsTree, extFilterRequestList, extStack, null, view); + } + + @Override + public String getSQLScatter(String table, List xAxis, List yAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, List extBubble, Datasource ds, ChartViewWithBLOBs view) { + SQLObj tableObj = SQLObj.builder() + .tableName((table.startsWith("(") && table.endsWith(")")) ? table : String.format(MongoConstants.KEYWORD_TABLE, table)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 0)) + .build(); + List xFields = new ArrayList<>(); + List xOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(xAxis)) { + for (int i = 0; i < xAxis.size(); i++) { + ChartViewFieldDTO x = xAxis.get(i); + String originField; + if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(x.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 1) { + originField = String.format(MongoConstants.KEYWORD_FIX, tableObj.getTableAlias(), x.getOriginName()); + } else { + originField = String.format(MongoConstants.KEYWORD_FIX, tableObj.getTableAlias(), x.getOriginName()); + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_X_PREFIX, i); + // 处理横轴字段 + xFields.add(getXFields(x, originField, fieldAlias)); + // 处理横轴排序 + if (StringUtils.isNotEmpty(x.getSort()) && Utils.joinSort(x.getSort())) { + xOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(x.getSort()) + .build()); + } + } + } + List yFields = new ArrayList<>(); + List yWheres = new ArrayList<>(); + List yOrders = new ArrayList<>(); + List yList = new ArrayList<>(); + yList.addAll(yAxis); + yList.addAll(extBubble); + if (CollectionUtils.isNotEmpty(yList)) { + for (int i = 0; i < yList.size(); i++) { + ChartViewFieldDTO y = yList.get(i); + String originField; + if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(y.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 1) { + originField = String.format(MongoConstants.KEYWORD_FIX, tableObj.getTableAlias(), y.getOriginName()); + } else { + originField = String.format(MongoConstants.KEYWORD_FIX, tableObj.getTableAlias(), y.getOriginName()); + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_Y_PREFIX, i); + // 处理纵轴字段 + yFields.add(getYFields(y, originField, fieldAlias)); + // 处理纵轴过滤 + yWheres.add(getYWheres(y, originField, fieldAlias)); + // 处理纵轴排序 + if (StringUtils.isNotEmpty(y.getSort()) && Utils.joinSort(y.getSort())) { + yOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(y.getSort()) + .build()); + } + } + } + // 处理视图中字段过滤 + String customWheres = transCustomFilterList(tableObj, fieldCustomFilter); + // 处理仪表板字段过滤 + String extWheres = transExtFilterList(tableObj, extFilterRequestList); + // row permissions tree + String whereTrees = transFilterTrees(tableObj, rowPermissionsTree); + // 构建sql所有参数 + List fields = new ArrayList<>(); + fields.addAll(xFields); + fields.addAll(yFields); + List wheres = new ArrayList<>(); + if (customWheres != null) wheres.add(customWheres); + if (extWheres != null) wheres.add(extWheres); + if (whereTrees != null) wheres.add(whereTrees); + List groups = new ArrayList<>(); + groups.addAll(xFields); + // 外层再次套sql + List orders = new ArrayList<>(); + orders.addAll(xOrders); + orders.addAll(yOrders); + List aggWheres = new ArrayList<>(); + aggWheres.addAll(yWheres.stream().filter(ObjectUtils::isNotEmpty).collect(Collectors.toList())); + + STGroup stg = new STGroupFile(SQLConstants.SQL_TEMPLATE); + ST st_sql = stg.getInstanceOf("querySql"); + if (CollectionUtils.isNotEmpty(xFields)) st_sql.add("groups", xFields); + if (CollectionUtils.isNotEmpty(yFields)) st_sql.add("aggregators", yFields); + if (CollectionUtils.isNotEmpty(wheres)) st_sql.add("filters", wheres); + if (ObjectUtils.isNotEmpty(tableObj)) st_sql.add("table", tableObj); + String sql = st_sql.render(); + + ST st = stg.getInstanceOf("querySql"); + SQLObj tableSQL = SQLObj.builder() + .tableName(String.format(MongoConstants.BRACKETS, sql)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 1)) + .build(); + if (CollectionUtils.isNotEmpty(aggWheres)) st.add("filters", aggWheres); + if (CollectionUtils.isNotEmpty(orders)) st.add("orders", orders); + if (ObjectUtils.isNotEmpty(tableSQL)) st.add("table", tableSQL); + return sqlLimit(st.render(), view); + } + + @Override + public String getSQLAsTmpScatter(String table, List xAxis, List yAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, List extBubble, ChartViewWithBLOBs view) { + return getSQLScatter("(" + sqlFix(table) + ")", xAxis, yAxis, fieldCustomFilter, rowPermissionsTree, extFilterRequestList, extBubble, null, view); + } + + @Override + public String searchTable(String table) { + return "SELECT table_name FROM information_schema.TABLES WHERE table_name ='" + table + "'"; + } + + @Override + public String getSQLSummary(String table, List yAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, ChartViewWithBLOBs view, Datasource ds) { + // 字段汇总 排序等 + SQLObj tableObj = SQLObj.builder() + .tableName((table.startsWith("(") && table.endsWith(")")) ? table : String.format(MongoConstants.KEYWORD_TABLE, table)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 0)) + .build(); + List yFields = new ArrayList<>(); + List yWheres = new ArrayList<>(); + List yOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(yAxis)) { + for (int i = 0; i < yAxis.size(); i++) { + ChartViewFieldDTO y = yAxis.get(i); + String originField; + if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(y.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 1) { + originField = String.format(MongoConstants.KEYWORD_FIX, tableObj.getTableAlias(), y.getOriginName()); + } else { + originField = String.format(MongoConstants.KEYWORD_FIX, tableObj.getTableAlias(), y.getOriginName()); + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_Y_PREFIX, i); + // 处理纵轴字段 + yFields.add(getYFields(y, originField, fieldAlias)); + // 处理纵轴过滤 + yWheres.add(getYWheres(y, originField, fieldAlias)); + // 处理纵轴排序 + if (StringUtils.isNotEmpty(y.getSort()) && Utils.joinSort(y.getSort())) { + yOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(y.getSort()) + .build()); + } + } + } + // 处理视图中字段过滤 + String customWheres = transCustomFilterList(tableObj, fieldCustomFilter); + // 处理仪表板字段过滤 + String extWheres = transExtFilterList(tableObj, extFilterRequestList); + // row permissions tree + String whereTrees = transFilterTrees(tableObj, rowPermissionsTree); + // 构建sql所有参数 + List fields = new ArrayList<>(); + fields.addAll(yFields); + List wheres = new ArrayList<>(); + if (customWheres != null) wheres.add(customWheres); + if (extWheres != null) wheres.add(extWheres); + if (whereTrees != null) wheres.add(whereTrees); + List groups = new ArrayList<>(); + // 外层再次套sql + List orders = new ArrayList<>(); + orders.addAll(yOrders); + List aggWheres = new ArrayList<>(); + aggWheres.addAll(yWheres.stream().filter(ObjectUtils::isNotEmpty).collect(Collectors.toList())); + + STGroup stg = new STGroupFile(SQLConstants.SQL_TEMPLATE); + ST st_sql = stg.getInstanceOf("querySql"); + if (CollectionUtils.isNotEmpty(yFields)) st_sql.add("aggregators", yFields); + if (CollectionUtils.isNotEmpty(wheres)) st_sql.add("filters", wheres); + if (ObjectUtils.isNotEmpty(tableObj)) st_sql.add("table", tableObj); + String sql = st_sql.render(); + + ST st = stg.getInstanceOf("querySql"); + SQLObj tableSQL = SQLObj.builder() + .tableName(String.format(MongoConstants.BRACKETS, sql)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 1)) + .build(); + if (CollectionUtils.isNotEmpty(aggWheres)) st.add("filters", aggWheres); + if (CollectionUtils.isNotEmpty(orders)) st.add("orders", orders); + if (ObjectUtils.isNotEmpty(tableSQL)) st.add("table", tableSQL); + return sqlLimit(st.render(), view); + } + + @Override + public String getSQLSummaryAsTmp(String sql, List yAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, ChartViewWithBLOBs view) { + return getSQLSummary("(" + sqlFix(sql) + ")", yAxis, fieldCustomFilter, rowPermissionsTree, extFilterRequestList, view, null); + } + + @Override + public String wrapSql(String sql) { + sql = sql.trim(); + if (sql.lastIndexOf(";") == (sql.length() - 1)) { + sql = sql.substring(0, sql.length() - 1); + } + String tmpSql = "SELECT * FROM (" + sql + ") AS tmp " + " LIMIT 0"; + return tmpSql; + } + + @Override + public String createRawQuerySQL(String table, List fields, Datasource ds) { + String[] array = fields.stream().map(f -> { + StringBuilder stringBuilder = new StringBuilder(); + if (f.getDeExtractType() == 4) { // 处理 tinyint + stringBuilder.append("concat(`").append(f.getOriginName()).append("`,'') AS ").append(f.getDataeaseName()); + } else { + stringBuilder.append("`").append(f.getOriginName()).append("` AS ").append(f.getDataeaseName()); + } + return stringBuilder.toString(); + }).toArray(String[]::new); + return MessageFormat.format("SELECT {0} FROM {1}", StringUtils.join(array, ","), table); + } + + @Override + public String createRawQuerySQLAsTmp(String sql, List fields) { + return createRawQuerySQL(" (" + sqlFix(sql) + ") AS tmp ", fields, null); + } + + @Override + public String transTreeItem(SQLObj tableObj, DatasetRowPermissionsTreeItem item) { + String res = null; + DatasetTableField field = item.getField(); + if (ObjectUtils.isEmpty(field)) { + return null; + } + String whereName = ""; + String originName; + if (ObjectUtils.isNotEmpty(field.getExtField()) && field.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originName = calcFieldRegex(field.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(field.getExtField()) && field.getExtField() == 1) { + originName = String.format(MongoConstants.KEYWORD_FIX, tableObj.getTableAlias(), field.getOriginName()); + } else { + originName = String.format(MongoConstants.KEYWORD_FIX, tableObj.getTableAlias(), field.getOriginName()); + } + if (field.getDeType() == 1) { + if (field.getDeExtractType() == 0 || field.getDeExtractType() == 5) { + whereName = String.format(MongoConstants.STR_TO_DATE, originName, StringUtils.isNotEmpty(field.getDateFormat()) ? field.getDateFormat() : MongoConstants.DEFAULT_DATE_FORMAT); + } + if (field.getDeExtractType() == 2 || field.getDeExtractType() == 3 || field.getDeExtractType() == 4) { + String cast = String.format(MongoConstants.CAST, originName, MongoConstants.DEFAULT_INT_FORMAT) + "/1000"; + whereName = String.format(MongoConstants.FROM_UNIXTIME, cast, MongoConstants.DEFAULT_DATE_FORMAT); + } + if (field.getDeExtractType() == 1) { + whereName = originName; + } + } else if (field.getDeType() == 2 || field.getDeType() == 3) { + if (field.getDeExtractType() == 0 || field.getDeExtractType() == 5) { + whereName = String.format(MongoConstants.CAST, originName, MongoConstants.DEFAULT_FLOAT_FORMAT); + } + if (field.getDeExtractType() == 1) { + whereName = String.format(MongoConstants.UNIX_TIMESTAMP, originName); + } + if (field.getDeExtractType() == 2 || field.getDeExtractType() == 3 || field.getDeExtractType() == 4) { + whereName = originName; + } + } else { + whereName = originName; + } + + if (StringUtils.equalsIgnoreCase(item.getFilterType(), "enum")) { + if (CollectionUtils.isNotEmpty(item.getEnumValue())) { + res = "(" + whereName + " IN ('" + String.join("','", item.getEnumValue()) + "'))"; + } + } else { + String value = item.getValue(); + String whereTerm = transMysqlFilterTerm(item.getTerm()); + String whereValue = ""; + + if (StringUtils.equalsIgnoreCase(item.getTerm(), "null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(item.getTerm(), "not_null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(item.getTerm(), "empty")) { + whereValue = "''"; + } else if (StringUtils.equalsIgnoreCase(item.getTerm(), "not_empty")) { + whereValue = "''"; + } else if (StringUtils.containsIgnoreCase(item.getTerm(), "in") || StringUtils.containsIgnoreCase(item.getTerm(), "not in")) { + whereValue = "('" + String.join("','", value.split(",")) + "')"; + } else if (StringUtils.containsIgnoreCase(item.getTerm(), "like")) { + whereValue = "'%" + value + "%'"; + } else { + whereValue = String.format(MongoConstants.WHERE_VALUE_VALUE, value); + } + SQLObj build = SQLObj.builder() + .whereField(whereName) + .whereTermAndValue(whereTerm + whereValue) + .build(); + res = build.getWhereField() + " " + build.getWhereTermAndValue(); + } + return res; + } + + @Override + public String convertTableToSql(String tableName, Datasource ds) { + return createSQLPreview("SELECT * FROM " + String.format(MongoConstants.KEYWORD_TABLE, tableName), null); + } + + public String transMysqlFilterTerm(String term) { + switch (term) { + case "eq": + return " = "; + case "not_eq": + return " <> "; + case "lt": + return " < "; + case "le": + return " <= "; + case "gt": + return " > "; + case "ge": + return " >= "; + case "in": + return " IN "; + case "not in": + return " NOT IN "; + case "like": + return " LIKE "; + case "not like": + return " NOT LIKE "; + case "null": + return " IS NULL "; + case "not_null": + return " IS NOT NULL "; + case "empty": + return " = "; + case "not_empty": + return " <> "; + case "between": + return " BETWEEN "; + default: + return ""; + } + } + + public String transCustomFilterList(SQLObj tableObj, List requestList) { + if (CollectionUtils.isEmpty(requestList)) { + return null; + } + List res = new ArrayList<>(); + for (ChartFieldCustomFilterDTO request : requestList) { + List list = new ArrayList<>(); + DatasetTableField field = request.getField(); + + if (ObjectUtils.isEmpty(field)) { + continue; + } + String whereName = ""; + String originName; + if (ObjectUtils.isNotEmpty(field.getExtField()) && field.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originName = calcFieldRegex(field.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(field.getExtField()) && field.getExtField() == 1) { + originName = String.format(MongoConstants.KEYWORD_FIX, tableObj.getTableAlias(), field.getOriginName()); + } else { + originName = String.format(MongoConstants.KEYWORD_FIX, tableObj.getTableAlias(), field.getOriginName()); + } + if (field.getDeType() == 1) { + if (field.getDeExtractType() == 0 || field.getDeExtractType() == 5) { + whereName = String.format(MongoConstants.STR_TO_DATE, originName, StringUtils.isNotEmpty(field.getDateFormat()) ? field.getDateFormat() : MongoConstants.DEFAULT_DATE_FORMAT); + } + if (field.getDeExtractType() == 2 || field.getDeExtractType() == 3 || field.getDeExtractType() == 4) { + String cast = String.format(MongoConstants.CAST, originName, MongoConstants.DEFAULT_INT_FORMAT) + "/1000"; + whereName = String.format(MongoConstants.FROM_UNIXTIME, cast, MongoConstants.DEFAULT_DATE_FORMAT); + } + if (field.getDeExtractType() == 1) { + whereName = originName; + } + } else if (field.getDeType() == 2 || field.getDeType() == 3) { + if (field.getDeExtractType() == 0 || field.getDeExtractType() == 5) { + whereName = String.format(MongoConstants.CAST, originName, MongoConstants.DEFAULT_FLOAT_FORMAT); + } + if (field.getDeExtractType() == 1) { + whereName = String.format(MongoConstants.UNIX_TIMESTAMP, originName); + } + if (field.getDeExtractType() == 2 || field.getDeExtractType() == 3 || field.getDeExtractType() == 4) { + whereName = originName; + } + } else { + whereName = originName; + } + + if (StringUtils.equalsIgnoreCase(request.getFilterType(), "enum")) { + if (CollectionUtils.isNotEmpty(request.getEnumCheckField())) { + res.add("(" + whereName + " IN ('" + String.join("','", request.getEnumCheckField()) + "'))"); + } + } else { + List filter = request.getFilter(); + for (ChartCustomFilterItemDTO filterItemDTO : filter) { + String value = filterItemDTO.getValue(); + String whereTerm = transMysqlFilterTerm(filterItemDTO.getTerm()); + String whereValue = ""; + + if (StringUtils.equalsIgnoreCase(filterItemDTO.getTerm(), "null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(filterItemDTO.getTerm(), "not_null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(filterItemDTO.getTerm(), "empty")) { + whereValue = "''"; + } else if (StringUtils.equalsIgnoreCase(filterItemDTO.getTerm(), "not_empty")) { + whereValue = "''"; + } else if (StringUtils.containsIgnoreCase(filterItemDTO.getTerm(), "in") || StringUtils.containsIgnoreCase(filterItemDTO.getTerm(), "not in")) { + whereValue = "('" + String.join("','", value.split(",")) + "')"; + } else if (StringUtils.containsIgnoreCase(filterItemDTO.getTerm(), "like")) { + whereValue = "'%" + value + "%'"; + } else { + whereValue = String.format(MongoConstants.WHERE_VALUE_VALUE, value); + } + list.add(SQLObj.builder() + .whereField(whereName) + .whereTermAndValue(whereTerm + whereValue) + .build()); + } + List strList = new ArrayList<>(); + list.forEach(ele -> strList.add(ele.getWhereField() + " " + ele.getWhereTermAndValue())); + if (CollectionUtils.isNotEmpty(list)) { + res.add("(" + String.join(" " + getLogic(request.getLogic()) + " ", strList) + ")"); + } + } + } + return CollectionUtils.isNotEmpty(res) ? "(" + String.join(" AND ", res) + ")" : null; + } + + public String transExtFilterList(SQLObj tableObj, List requestList) { + if (CollectionUtils.isEmpty(requestList)) { + return null; + } + List list = new ArrayList<>(); + for (ChartExtFilterRequest request : requestList) { + List value = request.getValue(); + + List whereNameList = new ArrayList<>(); + List fieldList = new ArrayList<>(); + if (request.getIsTree()) { + fieldList.addAll(request.getDatasetTableFieldList()); + } else { + fieldList.add(request.getDatasetTableField()); + } + + for (DatasetTableField field : fieldList) { + if (CollectionUtils.isEmpty(value) || ObjectUtils.isEmpty(field)) { + continue; + } + String whereName = ""; + + String originName; + if (ObjectUtils.isNotEmpty(field.getExtField()) && field.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originName = calcFieldRegex(field.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(field.getExtField()) && field.getExtField() == 1) { + originName = String.format(MongoConstants.KEYWORD_FIX, tableObj.getTableAlias(), field.getOriginName()); + } else { + originName = String.format(MongoConstants.KEYWORD_FIX, tableObj.getTableAlias(), field.getOriginName()); + } + + if (field.getDeType() == 1) { + if (field.getDeExtractType() == 0 || field.getDeExtractType() == 5) { + whereName = String.format(MongoConstants.STR_TO_DATE, originName, StringUtils.isNotEmpty(field.getDateFormat()) ? field.getDateFormat() : MongoConstants.DEFAULT_DATE_FORMAT); + } + if (field.getDeExtractType() == 2 || field.getDeExtractType() == 3 || field.getDeExtractType() == 4) { + String cast = String.format(MongoConstants.CAST, originName, MongoConstants.DEFAULT_INT_FORMAT) + "/1000"; + whereName = String.format(MongoConstants.FROM_UNIXTIME, cast, MongoConstants.DEFAULT_DATE_FORMAT); + } + if (field.getDeExtractType() == 1) { + whereName = originName; + } + } else if (field.getDeType() == 2 || field.getDeType() == 3) { + if (field.getDeExtractType() == 0 || field.getDeExtractType() == 5) { + whereName = String.format(MongoConstants.CAST, originName, MongoConstants.DEFAULT_FLOAT_FORMAT); + } + if (field.getDeExtractType() == 1) { + whereName = String.format(MongoConstants.UNIX_TIMESTAMP, originName); + } + if (field.getDeExtractType() == 2 || field.getDeExtractType() == 3 || field.getDeExtractType() == 4) { + whereName = originName; + } + } else { + whereName = originName; + } + whereNameList.add(whereName); + } + + String whereName = ""; + if (request.getIsTree()) { + whereName = "CONCAT(" + StringUtils.join(whereNameList, ",',',") + ")"; + } else { + whereName = whereNameList.get(0); + } + String whereTerm = transMysqlFilterTerm(request.getOperator()); + String whereValue = ""; + + if (StringUtils.containsIgnoreCase(request.getOperator(), "in")) { + whereValue = "('" + StringUtils.join(value, "','") + "')"; + } else if (StringUtils.containsIgnoreCase(request.getOperator(), "like")) { + String keyword = value.get(0).toUpperCase(); + whereValue = "'%" + keyword + "%'"; + whereName = "upper(" + whereName + ")"; + } else if (StringUtils.containsIgnoreCase(request.getOperator(), "between")) { + if (request.getDatasetTableField().getDeType() == 1) { + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String startTime = simpleDateFormat.format(new Date(Long.parseLong(value.get(0)))); + String endTime = simpleDateFormat.format(new Date(Long.parseLong(value.get(1)))); + whereValue = String.format(MongoConstants.WHERE_BETWEEN, startTime, endTime); + } else { + whereValue = String.format(MongoConstants.WHERE_BETWEEN, value.get(0), value.get(1)); + } + } else { + whereValue = String.format(MongoConstants.WHERE_VALUE_VALUE, value.get(0)); + } + list.add(SQLObj.builder() + .whereField(whereName) + .whereTermAndValue(whereTerm + whereValue) + .build()); + } + List strList = new ArrayList<>(); + list.forEach(ele -> strList.add(ele.getWhereField() + " " + ele.getWhereTermAndValue())); + return CollectionUtils.isNotEmpty(list) ? "(" + String.join(" AND ", strList) + ")" : null; + } + + private String sqlFix(String sql) { + if (sql.lastIndexOf(";") == (sql.length() - 1)) { + sql = sql.substring(0, sql.length() - 1); + } + return sql; + } + + private String transDateFormat(String dateStyle, String datePattern) { + String split = "-"; + if (StringUtils.equalsIgnoreCase(datePattern, "date_sub")) { + split = "-"; + } else if (StringUtils.equalsIgnoreCase(datePattern, "date_split")) { + split = "/"; + } else { + split = "-"; + } + + if (StringUtils.isEmpty(dateStyle)) { + return "%Y-%m-%d %H:%i:%S"; + } + + switch (dateStyle) { + case "y": + return "%Y"; + case "y_M": + return "%Y" + split + "%m"; + case "y_M_d": + return "%Y" + split + "%m" + split + "%d"; + case "H_m_s": + return "%H:%i:%S"; + case "y_M_d_H_m": + return "%Y" + split + "%m" + split + "%d" + " %H:%i"; + case "y_M_d_H_m_s": + return "%Y" + split + "%m" + split + "%d" + " %H:%i:%S"; + default: + return "%Y-%m-%d %H:%i:%S"; + } + } + + private SQLObj getXFields(ChartViewFieldDTO x, String originField, String fieldAlias) { + String fieldName = ""; + if (x.getDeExtractType() == 1) { + if (x.getDeType() == 2 || x.getDeType() == 3) { + fieldName = String.format(MongoConstants.UNIX_TIMESTAMP, originField) + "*1000"; + } else if (x.getDeType() == 1) { + String format = transDateFormat(x.getDateStyle(), x.getDatePattern()); + fieldName = String.format(MongoConstants.DATE_FORMAT, originField, format); + } else { + fieldName = originField; + } + } else { + if (x.getDeType() == 1) { + String format = transDateFormat(x.getDateStyle(), x.getDatePattern()); + if (x.getDeExtractType() == 0) { + fieldName = String.format(MongoConstants.DATE_FORMAT, String.format(MongoConstants.STR_TO_DATE, originField, StringUtils.isNotEmpty(x.getDateFormat()) ? x.getDateFormat() : MongoConstants.DEFAULT_DATE_FORMAT), format); + } else { + String cast = String.format(MongoConstants.CAST, originField, MongoConstants.DEFAULT_INT_FORMAT) + "/1000"; + String from_unixtime = String.format(MongoConstants.FROM_UNIXTIME, cast, MongoConstants.DEFAULT_DATE_FORMAT); + fieldName = String.format(MongoConstants.DATE_FORMAT, from_unixtime, format); + } + } else { + if (x.getDeType() == DeTypeConstants.DE_INT) { + fieldName = String.format(MongoConstants.CAST, originField, MongoConstants.DEFAULT_INT_FORMAT); + } else if (x.getDeType() == DeTypeConstants.DE_FLOAT) { + fieldName = String.format(MongoConstants.CAST, originField, MongoConstants.DEFAULT_FLOAT_FORMAT); + } else { + fieldName = originField; + } + } + } + return SQLObj.builder() + .fieldName(fieldName) + .fieldAlias(fieldAlias) + .build(); + } + + private List getXWheres(ChartViewFieldDTO x, String originField, String fieldAlias) { + List list = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(x.getFilter()) && x.getFilter().size() > 0) { + x.getFilter().forEach(f -> { + String whereName = ""; + String whereTerm = transMysqlFilterTerm(f.getTerm()); + String whereValue = ""; + if (x.getDeType() == 1 && x.getDeExtractType() != 1) { + String cast = String.format(MongoConstants.CAST, originField, MongoConstants.DEFAULT_INT_FORMAT) + "/1000"; + whereName = String.format(MongoConstants.FROM_UNIXTIME, cast, MongoConstants.DEFAULT_DATE_FORMAT); + } else { + whereName = originField; + } + if (StringUtils.equalsIgnoreCase(f.getTerm(), "null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(f.getTerm(), "not_null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(f.getTerm(), "empty")) { + whereValue = "''"; + } else if (StringUtils.equalsIgnoreCase(f.getTerm(), "not_empty")) { + whereValue = "''"; + } else if (StringUtils.containsIgnoreCase(f.getTerm(), "in")) { + whereValue = "('" + StringUtils.join(f.getValue(), "','") + "')"; + } else if (StringUtils.containsIgnoreCase(f.getTerm(), "like")) { + whereValue = "'%" + f.getValue() + "%'"; + } else { + whereValue = String.format(MongoConstants.WHERE_VALUE_VALUE, f.getValue()); + } + list.add(SQLObj.builder() + .whereField(whereName) + .whereAlias(fieldAlias) + .whereTermAndValue(whereTerm + whereValue) + .build()); + }); + } + return list; + } + + private SQLObj getYFields(ChartViewFieldDTO y, String originField, String fieldAlias) { + String fieldName = ""; + if (StringUtils.equalsIgnoreCase(y.getOriginName(), "*")) { + fieldName = MongoConstants.AGG_COUNT; + } else if (SQLConstants.DIMENSION_TYPE.contains(y.getDeType())) { + if (StringUtils.equalsIgnoreCase(y.getSummary(), "count_distinct")) { + fieldName = String.format(MongoConstants.AGG_FIELD, "COUNT", "DISTINCT " + originField); + } else if (StringUtils.equalsIgnoreCase(y.getSummary(), "group_concat")) { + fieldName = String.format(MongoConstants.GROUP_CONCAT, originField); + } else { + fieldName = String.format(MongoConstants.AGG_FIELD, y.getSummary(), originField); + } + } else { + if (StringUtils.equalsIgnoreCase(y.getSummary(), "avg") || StringUtils.containsIgnoreCase(y.getSummary(), "pop")) { + String cast = String.format(MongoConstants.CAST, originField, y.getDeType() == 2 ? MongoConstants.DEFAULT_INT_FORMAT : MongoConstants.DEFAULT_FLOAT_FORMAT); + String agg = String.format(MongoConstants.AGG_FIELD, y.getSummary(), cast); + fieldName = String.format(MongoConstants.CAST, agg, MongoConstants.DEFAULT_FLOAT_FORMAT); + } else { + String cast = String.format(MongoConstants.CAST, originField, y.getDeType() == 2 ? MongoConstants.DEFAULT_INT_FORMAT : MongoConstants.DEFAULT_FLOAT_FORMAT); + if (StringUtils.equalsIgnoreCase(y.getSummary(), "count_distinct")) { + fieldName = String.format(MongoConstants.AGG_FIELD, "COUNT", "DISTINCT " + cast); + } else { + fieldName = String.format(MongoConstants.AGG_FIELD, y.getSummary(), cast); + } + } + } + return SQLObj.builder() + .fieldName(fieldName) + .fieldAlias(fieldAlias) + .build(); + } + + private String getYWheres(ChartViewFieldDTO y, String originField, String fieldAlias) { + List list = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(y.getFilter()) && y.getFilter().size() > 0) { + y.getFilter().forEach(f -> { + String whereTerm = transMysqlFilterTerm(f.getTerm()); + String whereValue = ""; + // 原始类型不是时间,在de中被转成时间的字段做处理 + if (StringUtils.equalsIgnoreCase(f.getTerm(), "null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(f.getTerm(), "not_null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(f.getTerm(), "empty")) { + whereValue = "''"; + } else if (StringUtils.equalsIgnoreCase(f.getTerm(), "not_empty")) { + whereValue = "''"; + } else if (StringUtils.containsIgnoreCase(f.getTerm(), "in")) { + whereValue = "('" + StringUtils.join(f.getValue(), "','") + "')"; + } else if (StringUtils.containsIgnoreCase(f.getTerm(), "like")) { + whereValue = "'%" + f.getValue() + "%'"; + } else { + whereValue = String.format(MongoConstants.WHERE_VALUE_VALUE, f.getValue()); + } + list.add(SQLObj.builder() + .whereField(fieldAlias) + .whereAlias(fieldAlias) + .whereTermAndValue(whereTerm + whereValue) + .build()); + }); + } + List strList = new ArrayList<>(); + list.forEach(ele -> strList.add(ele.getWhereField() + " " + ele.getWhereTermAndValue())); + return CollectionUtils.isNotEmpty(list) ? "(" + String.join(" " + getLogic(y.getLogic()) + " ", strList) + ")" : null; + } + + private String calcFieldRegex(String originField, SQLObj tableObj) { + originField = originField.replaceAll("[\\t\\n\\r]]", ""); + // 正则提取[xxx] + String regex = "\\[(.*?)]"; + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(originField); + Set ids = new HashSet<>(); + while (matcher.find()) { + String id = matcher.group(1); + ids.add(id); + } + if (CollectionUtils.isEmpty(ids)) { + return originField; + } + DatasetTableFieldExample datasetTableFieldExample = new DatasetTableFieldExample(); + datasetTableFieldExample.createCriteria().andIdIn(new ArrayList<>(ids)); + List calcFields = datasetTableFieldMapper.selectByExample(datasetTableFieldExample); + for (DatasetTableField ele : calcFields) { + originField = originField.replaceAll("\\[" + ele.getId() + "]", + String.format(MongoConstants.KEYWORD_FIX, tableObj.getTableAlias(), ele.getOriginName())); + } + return originField; + } + + private String sqlLimit(String sql, ChartViewWithBLOBs view) { + if (StringUtils.equalsIgnoreCase(view.getResultMode(), "custom")) { + return sql + " LIMIT 0," + view.getResultCount(); + } else { + return sql; + } + } + + @Override + public String sqlForPreview(String table, Datasource ds) { + return "SELECT * FROM " + String.format(MongoConstants.KEYWORD_TABLE, table); + } + + public List dateformat() { + ObjectMapper objectMapper = new ObjectMapper(); + List dateformats = new ArrayList<>(); + try{ + dateformats = objectMapper.readValue("[\n" + + "{\"dateformat\": \"%Y%m%d\"},\n" + + "{\"dateformat\": \"%Y-%m-%d\"},\n" + + "{\"dateformat\": \"%Y/%m/%d\"},\n" + + "{\"dateformat\": \"%Y%m%d %H:%i:%S\"},\n" + + "{\"dateformat\": \"%Y/%m/%d %H:%i:%S\"},\n" + + "{\"dateformat\": \"%Y-%m-%d %H:%i:%S\"}\n" + + "]", new TypeReference>() {} ); + }catch (Exception e){} + return dateformats; + } +} diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-backend/src/main/java/io/dataease/plugins/datasource/mongo/service/MongoService.java b/extensions/dataease-extensions-datasource/mongo/mongo-backend/src/main/java/io/dataease/plugins/datasource/mongo/service/MongoService.java new file mode 100644 index 0000000000..69ae136824 --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/mongo-backend/src/main/java/io/dataease/plugins/datasource/mongo/service/MongoService.java @@ -0,0 +1,58 @@ +package io.dataease.plugins.datasource.mongo.service; + +import io.dataease.plugins.common.constants.DatabaseClassification; +import io.dataease.plugins.common.constants.DatasourceCalculationMode; +import io.dataease.plugins.common.dto.StaticResource; +import io.dataease.plugins.common.dto.datasource.DataSourceType; +import io.dataease.plugins.datasource.service.DatasourceService; +import org.springframework.stereotype.Service; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +@Service +public class MongoService extends DatasourceService { + + + @Override + public List components() { + List result = new ArrayList<>(); + result.add("mongobi"); + return result; + } + @Override + protected InputStream readContent(String s) { + InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("static/" + s); + return resourceAsStream; + } + + @Override + public List staticResources() { + List results = new ArrayList<>(); + StaticResource staticResource = new StaticResource(); + staticResource.setName("mongobi"); + staticResource.setSuffix("jpg"); + results.add(staticResource); + results.add(pluginSvg()); + return results; + } + + @Override + public DataSourceType getDataSourceType() { + DataSourceType dataSourceType = new DataSourceType("mongobi", "Mongodb-BI" , true , "characterEncoding=UTF-8&connectTimeout=5000&useSSL=true&allowPublicKeyRetrieval=true&verifyServerCertificate=false", DatasourceCalculationMode.DIRECT, true); + dataSourceType.setKeywordPrefix("`"); + dataSourceType.setKeywordSuffix("`"); + dataSourceType.setAliasPrefix(""); + dataSourceType.setAliasSuffix(""); + dataSourceType.setDatabaseClassification(DatabaseClassification.OLTP); + return dataSourceType; + } + + private StaticResource pluginSvg() { + StaticResource staticResource = new StaticResource(); + staticResource.setName("mongo-backend"); + staticResource.setSuffix("svg"); + return staticResource; + } +} diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-frontend/.babelrc b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/.babelrc new file mode 100644 index 0000000000..3a280ba34b --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/.babelrc @@ -0,0 +1,12 @@ +{ + "presets": [ + ["env", { + "modules": false, + "targets": { + "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] + } + }], + "stage-2" + ], + "plugins": ["transform-vue-jsx", "transform-runtime"] +} diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-frontend/.gitignore b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/.gitignore new file mode 100644 index 0000000000..541a820f6c --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/.gitignore @@ -0,0 +1,14 @@ +.DS_Store +node_modules/ +/dist/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-frontend/README.md b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/README.md new file mode 100644 index 0000000000..c1e4be95e0 --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/README.md @@ -0,0 +1,21 @@ +# deplugin-view-frontend + +> A Vue.js project + +## Build Setup + +``` bash +# install dependencies +npm install + +# serve with hot reload at localhost:8080 +npm run dev + +# build for production with minification +npm run build + +# build for production and view the bundle analyzer report +npm run build --report +``` + +For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader). diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-frontend/build/build-async-plugins.js b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/build/build-async-plugins.js new file mode 100644 index 0000000000..2303854531 --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/build/build-async-plugins.js @@ -0,0 +1,35 @@ +'use strict' +require('./check-versions')() + +process.env.NODE_ENV = 'production' + +const ora = require('ora') +const chalk = require('chalk') +const webpack = require('webpack') +const webpackConfig = require('./webpack.async-plugins') + +const spinner = ora('building for sync-plugins...') +spinner.start() + +webpack(webpackConfig, function (err, stats) { + spinner.stop() + if (err) throw err + process.stdout.write(stats.toString({ + colors: true, + modules: false, + children: false, + chunks: false, + chunkModules: false + }) + '\n\n') + + if (stats.hasErrors()) { + console.log(chalk.red(' Build failed with errors.\n')) + process.exit(1) + } + + console.log(chalk.cyan(' Build complete.\n')) + console.log(chalk.yellow( + ' Tip: built files are meant to be served over an HTTP server.\n' + + ' Opening index.html over file:// won\'t work.\n' + )) +}) diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-frontend/build/build.js b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/build/build.js new file mode 100644 index 0000000000..8f2ad8ad49 --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/build/build.js @@ -0,0 +1,41 @@ +'use strict' +require('./check-versions')() + +process.env.NODE_ENV = 'production' + +const ora = require('ora') +const rm = require('rimraf') +const path = require('path') +const chalk = require('chalk') +const webpack = require('webpack') +const config = require('../config') +const webpackConfig = require('./webpack.prod.conf') + +const spinner = ora('building for production...') +spinner.start() + +rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { + if (err) throw err + webpack(webpackConfig, (err, stats) => { + spinner.stop() + if (err) throw err + process.stdout.write(stats.toString({ + colors: true, + modules: false, + children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build. + chunks: false, + chunkModules: false + }) + '\n\n') + + if (stats.hasErrors()) { + console.log(chalk.red(' Build failed with errors.\n')) + process.exit(1) + } + + console.log(chalk.cyan(' Build complete.\n')) + console.log(chalk.yellow( + ' Tip: built files are meant to be served over an HTTP server.\n' + + ' Opening index.html over file:// won\'t work.\n' + )) + }) +}) diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-frontend/build/check-versions.js b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/build/check-versions.js new file mode 100644 index 0000000000..3ef972a08d --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/build/check-versions.js @@ -0,0 +1,54 @@ +'use strict' +const chalk = require('chalk') +const semver = require('semver') +const packageConfig = require('../package.json') +const shell = require('shelljs') + +function exec (cmd) { + return require('child_process').execSync(cmd).toString().trim() +} + +const versionRequirements = [ + { + name: 'node', + currentVersion: semver.clean(process.version), + versionRequirement: packageConfig.engines.node + } +] + +if (shell.which('npm')) { + versionRequirements.push({ + name: 'npm', + currentVersion: exec('npm --version'), + versionRequirement: packageConfig.engines.npm + }) +} + +module.exports = function () { + const warnings = [] + + for (let i = 0; i < versionRequirements.length; i++) { + const mod = versionRequirements[i] + + if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { + warnings.push(mod.name + ': ' + + chalk.red(mod.currentVersion) + ' should be ' + + chalk.green(mod.versionRequirement) + ) + } + } + + if (warnings.length) { + console.log('') + console.log(chalk.yellow('To use this template, you must update following to modules:')) + console.log() + + for (let i = 0; i < warnings.length; i++) { + const warning = warnings[i] + console.log(' ' + warning) + } + + console.log() + process.exit(1) + } +} diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-frontend/build/logo.png b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/build/logo.png new file mode 100644 index 0000000000..f3d2503fc2 Binary files /dev/null and b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/build/logo.png differ diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-frontend/build/utils.js b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/build/utils.js new file mode 100644 index 0000000000..e534fb0fd6 --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/build/utils.js @@ -0,0 +1,101 @@ +'use strict' +const path = require('path') +const config = require('../config') +const ExtractTextPlugin = require('extract-text-webpack-plugin') +const packageConfig = require('../package.json') + +exports.assetsPath = function (_path) { + const assetsSubDirectory = process.env.NODE_ENV === 'production' + ? config.build.assetsSubDirectory + : config.dev.assetsSubDirectory + + return path.posix.join(assetsSubDirectory, _path) +} + +exports.cssLoaders = function (options) { + options = options || {} + + const cssLoader = { + loader: 'css-loader', + options: { + sourceMap: options.sourceMap + } + } + + const postcssLoader = { + loader: 'postcss-loader', + options: { + sourceMap: options.sourceMap + } + } + + // generate loader string to be used with extract text plugin + function generateLoaders (loader, loaderOptions) { + const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader] + + if (loader) { + loaders.push({ + loader: loader + '-loader', + options: Object.assign({}, loaderOptions, { + sourceMap: options.sourceMap + }) + }) + } + + // Extract CSS when that option is specified + // (which is the case during production build) + if (options.extract) { + return ExtractTextPlugin.extract({ + use: loaders, + fallback: 'vue-style-loader' + }) + } else { + return ['vue-style-loader'].concat(loaders) + } + } + + // https://vue-loader.vuejs.org/en/configurations/extract-css.html + return { + css: generateLoaders(), + postcss: generateLoaders(), + less: generateLoaders('less'), + sass: generateLoaders('sass', { indentedSyntax: true }), + scss: generateLoaders('sass'), + stylus: generateLoaders('stylus'), + styl: generateLoaders('stylus') + } +} + +// Generate loaders for standalone style files (outside of .vue) +exports.styleLoaders = function (options) { + const output = [] + const loaders = exports.cssLoaders(options) + + for (const extension in loaders) { + const loader = loaders[extension] + output.push({ + test: new RegExp('\\.' + extension + '$'), + use: loader + }) + } + + return output +} + +exports.createNotifierCallback = () => { + const notifier = require('node-notifier') + + return (severity, errors) => { + if (severity !== 'error') return + + const error = errors[0] + const filename = error.file && error.file.split('!').pop() + + notifier.notify({ + title: packageConfig.name, + message: severity + ': ' + error.name, + subtitle: filename || '', + icon: path.join(__dirname, 'logo.png') + }) + } +} diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-frontend/build/vue-loader.conf.js b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/build/vue-loader.conf.js new file mode 100644 index 0000000000..33ed58bc0a --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/build/vue-loader.conf.js @@ -0,0 +1,22 @@ +'use strict' +const utils = require('./utils') +const config = require('../config') +const isProduction = process.env.NODE_ENV === 'production' +const sourceMapEnabled = isProduction + ? config.build.productionSourceMap + : config.dev.cssSourceMap + +module.exports = { + loaders: utils.cssLoaders({ + sourceMap: sourceMapEnabled, + extract: isProduction + }), + cssSourceMap: sourceMapEnabled, + cacheBusting: config.dev.cacheBusting, + transformToRequire: { + video: ['src', 'poster'], + source: 'src', + img: 'src', + image: 'xlink:href' + } +} diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-frontend/build/webpack.async-plugins.js b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/build/webpack.async-plugins.js new file mode 100644 index 0000000000..04eef77f4c --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/build/webpack.async-plugins.js @@ -0,0 +1,103 @@ +const webpack = require('webpack') +const path = require('path') +const utils = require('./utils') +const CopyPlugin = require("copy-webpack-plugin"); +const VueLoaderPlugin = require('vue-loader/lib/plugin'); +function resolve (dir) { + return path.join(__dirname, '..', dir) +} + +module.exports = { + mode: 'development', + entry: { + 'mongobi': resolve('/src/views/mongobi.vue') + }, + output: { + path: resolve('/static/'), + filename: '[name].js' + }, + resolve: { + extensions: ['.js', '.vue', '.json'], + alias: { + 'vue$': 'vue/dist/vue.esm.js', + '@': resolve('src') + } + }, + externals: { + vue: 'vue' + }, + module: { + rules: [ + { + test: /\.vue$/, + loader: 'vue-loader', + options: { + transformAssetUrls: { + video: 'src', + source: 'src', + img: 'src', + image: 'xlink:href' + } + } + }, + { + test: /.(sa|sc|c)ss$/, + use: [ + {loader: 'vue-style-loader'}, + 'css-loader', + 'sass-loader' + ] + }, + { + test: /\.js$/, + loader: 'babel-loader', + include: [resolve('src'), resolve('test')] + }, + { + test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('img/[name].[hash:7].[ext]') + } + }, + { + test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('media/[name].[hash:7].[ext]') + } + }, + { + test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('fonts/[name].[hash:7].[ext]') + } + }, + { + test: /\.svg$/, + include: [path.resolve('src/icons')], + use: [ + { + loader: 'svg-sprite-loader', + options: { + symbolId: 'icon-[name]', + }, + } + ], + } + ] + }, + plugins: [ + new VueLoaderPlugin(), + new webpack.DefinePlugin({ + 'process.env.NODE_ENV': '"production"' + }), + new CopyPlugin([ + {from: 'src/icons/svg/'} + ]), + ] +} diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-frontend/build/webpack.base.conf.js b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/build/webpack.base.conf.js new file mode 100644 index 0000000000..6ccc02dab4 --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/build/webpack.base.conf.js @@ -0,0 +1,91 @@ +'use strict' +const path = require('path') +const utils = require('./utils') +const config = require('../config') +const vueLoaderConfig = require('./vue-loader.conf') + +function resolve (dir) { + return path.join(__dirname, '..', dir) +} + + + +module.exports = { + context: path.resolve(__dirname, '../'), + entry: { + app: './src/main.js' + }, + output: { + path: config.build.assetsRoot, + filename: '[name].js', + publicPath: process.env.NODE_ENV === 'production' + ? config.build.assetsPublicPath + : config.dev.assetsPublicPath + }, + resolve: { + extensions: ['.js', '.vue', '.json'], + alias: { + 'vue$': 'vue/dist/vue.esm.js', + '@': resolve('src'), + } + }, + module: { + rules: [ + { + test: /\.vue$/, + loader: 'vue-loader', + options: vueLoaderConfig + }, + { + test: /\.js$/, + loader: 'babel-loader', + include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')] + }, + { + test: /\.svg$/, + loader: 'svg-sprite-loader', + include: [resolve('src/icons')], + options: { + symbolId: 'icon-[name]' + } + }, + { + test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, + loader: 'url-loader', + exclude: [resolve('src/icons')], + options: { + limit: 10000, + name: utils.assetsPath('img/[name].[hash:7].[ext]') + } + }, + { + test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('media/[name].[hash:7].[ext]') + } + }, + { + test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('fonts/[name].[hash:7].[ext]') + } + } + ] + }, + node: { + // prevent webpack from injecting useless setImmediate polyfill because Vue + // source contains it (although only uses it if it's native). + setImmediate: false, + // prevent webpack from injecting mocks to Node native modules + // that does not make sense for the client + dgram: 'empty', + fs: 'empty', + net: 'empty', + tls: 'empty', + child_process: 'empty' + } +} diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-frontend/build/webpack.dev.conf.js b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/build/webpack.dev.conf.js new file mode 100755 index 0000000000..070ae221f3 --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/build/webpack.dev.conf.js @@ -0,0 +1,95 @@ +'use strict' +const utils = require('./utils') +const webpack = require('webpack') +const config = require('../config') +const merge = require('webpack-merge') +const path = require('path') +const baseWebpackConfig = require('./webpack.base.conf') +const CopyWebpackPlugin = require('copy-webpack-plugin') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') +const portfinder = require('portfinder') + +const HOST = process.env.HOST +const PORT = process.env.PORT && Number(process.env.PORT) + +const devWebpackConfig = merge(baseWebpackConfig, { + module: { + rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) + }, + // cheap-module-eval-source-map is faster for development + devtool: config.dev.devtool, + + // these devServer options should be customized in /config/index.js + devServer: { + clientLogLevel: 'warning', + historyApiFallback: { + rewrites: [ + { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') }, + ], + }, + hot: true, + contentBase: false, // since we use CopyWebpackPlugin. + compress: true, + host: HOST || config.dev.host, + port: PORT || config.dev.port, + open: config.dev.autoOpenBrowser, + overlay: config.dev.errorOverlay + ? { warnings: false, errors: true } + : false, + publicPath: config.dev.assetsPublicPath, + proxy: config.dev.proxyTable, + quiet: true, // necessary for FriendlyErrorsPlugin + watchOptions: { + poll: config.dev.poll, + } + }, + plugins: [ + new webpack.DefinePlugin({ + 'process.env': require('../config/dev.env') + }), + new webpack.HotModuleReplacementPlugin(), + new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update. + new webpack.NoEmitOnErrorsPlugin(), + // https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: 'index.html', + template: 'index.html', + inject: true + }), + // copy custom static assets + new CopyWebpackPlugin([ + { + from: path.resolve(__dirname, '../static'), + to: config.dev.assetsSubDirectory, + ignore: ['.*'] + } + ]) + ] +}) + +module.exports = new Promise((resolve, reject) => { + portfinder.basePort = process.env.PORT || config.dev.port + portfinder.getPort((err, port) => { + if (err) { + reject(err) + } else { + // publish the new Port, necessary for e2e tests + process.env.PORT = port + // add port to devServer config + devWebpackConfig.devServer.port = port + + // Add FriendlyErrorsPlugin + devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({ + compilationSuccessInfo: { + messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`], + }, + onErrors: config.dev.notifyOnErrors + ? utils.createNotifierCallback() + : undefined + })) + + resolve(devWebpackConfig) + } + }) +}) diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-frontend/build/webpack.prod.conf.js b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/build/webpack.prod.conf.js new file mode 100644 index 0000000000..3e25238ef6 --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/build/webpack.prod.conf.js @@ -0,0 +1,145 @@ +'use strict' +const path = require('path') +const utils = require('./utils') +const webpack = require('webpack') +const config = require('../config') +const merge = require('webpack-merge') +const baseWebpackConfig = require('./webpack.base.conf') +const CopyWebpackPlugin = require('copy-webpack-plugin') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const ExtractTextPlugin = require('extract-text-webpack-plugin') +const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') +const UglifyJsPlugin = require('uglifyjs-webpack-plugin') + +const env = require('../config/prod.env') + +const webpackConfig = merge(baseWebpackConfig, { + module: { + rules: utils.styleLoaders({ + sourceMap: config.build.productionSourceMap, + extract: true, + usePostCSS: true + }) + }, + devtool: config.build.productionSourceMap ? config.build.devtool : false, + output: { + path: config.build.assetsRoot, + filename: utils.assetsPath('js/[name].[chunkhash].js'), + chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') + }, + plugins: [ + // http://vuejs.github.io/vue-loader/en/workflow/production.html + new webpack.DefinePlugin({ + 'process.env': env + }), + new UglifyJsPlugin({ + uglifyOptions: { + compress: { + warnings: false + } + }, + sourceMap: config.build.productionSourceMap, + parallel: true + }), + // extract css into its own file + new ExtractTextPlugin({ + filename: utils.assetsPath('css/[name].[contenthash].css'), + // Setting the following option to `false` will not extract CSS from codesplit chunks. + // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack. + // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, + // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110 + allChunks: true, + }), + // Compress extracted CSS. We are using this plugin so that possible + // duplicated CSS from different components can be deduped. + new OptimizeCSSPlugin({ + cssProcessorOptions: config.build.productionSourceMap + ? { safe: true, map: { inline: false } } + : { safe: true } + }), + // generate dist index.html with correct asset hash for caching. + // you can customize output by editing /index.html + // see https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: config.build.index, + template: 'index.html', + inject: true, + minify: { + removeComments: true, + collapseWhitespace: true, + removeAttributeQuotes: true + // more options: + // https://github.com/kangax/html-minifier#options-quick-reference + }, + // necessary to consistently work with multiple chunks via CommonsChunkPlugin + chunksSortMode: 'dependency' + }), + // keep module.id stable when vendor modules does not change + new webpack.HashedModuleIdsPlugin(), + // enable scope hoisting + new webpack.optimize.ModuleConcatenationPlugin(), + // split vendor js into its own file + new webpack.optimize.CommonsChunkPlugin({ + name: 'vendor', + minChunks (module) { + // any required modules inside node_modules are extracted to vendor + return ( + module.resource && + /\.js$/.test(module.resource) && + module.resource.indexOf( + path.join(__dirname, '../node_modules') + ) === 0 + ) + } + }), + // extract webpack runtime and module manifest to its own file in order to + // prevent vendor hash from being updated whenever app bundle is updated + new webpack.optimize.CommonsChunkPlugin({ + name: 'manifest', + minChunks: Infinity + }), + // This instance extracts shared chunks from code split chunks and bundles them + // in a separate chunk, similar to the vendor chunk + // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk + new webpack.optimize.CommonsChunkPlugin({ + name: 'app', + async: 'vendor-async', + children: true, + minChunks: 3 + }), + + // copy custom static assets + new CopyWebpackPlugin([ + { + from: path.resolve(__dirname, '../static'), + to: config.build.assetsSubDirectory, + ignore: ['.*'] + } + ]) + ] +}) + +if (config.build.productionGzip) { + const CompressionWebpackPlugin = require('compression-webpack-plugin') + + webpackConfig.plugins.push( + new CompressionWebpackPlugin({ + asset: '[path].gz[query]', + algorithm: 'gzip', + test: new RegExp( + '\\.(' + + config.build.productionGzipExtensions.join('|') + + ')$' + ), + threshold: 10240, + minRatio: 0.8 + }) + ) +} + +if (config.build.bundleAnalyzerReport) { + const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin + webpackConfig.plugins.push(new BundleAnalyzerPlugin()) +} + +module.exports = webpackConfig diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-frontend/config/dev.env.js b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/config/dev.env.js new file mode 100644 index 0000000000..1e22973ae7 --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/config/dev.env.js @@ -0,0 +1,7 @@ +'use strict' +const merge = require('webpack-merge') +const prodEnv = require('./prod.env') + +module.exports = merge(prodEnv, { + NODE_ENV: '"development"' +}) diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-frontend/config/index.js b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/config/index.js new file mode 100644 index 0000000000..c5eded7f81 --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/config/index.js @@ -0,0 +1,69 @@ +'use strict' +// Template version: 1.3.1 +// see http://vuejs-templates.github.io/webpack for documentation. + +const path = require('path') + +module.exports = { + dev: { + + // Paths + assetsSubDirectory: 'static', + assetsPublicPath: '/', + proxyTable: {}, + + // Various Dev Server settings + host: 'localhost', // can be overwritten by process.env.HOST + port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined + autoOpenBrowser: false, + errorOverlay: true, + notifyOnErrors: true, + poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- + + + /** + * Source Maps + */ + + // https://webpack.js.org/configuration/devtool/#development + devtool: 'cheap-module-eval-source-map', + + // If you have problems debugging vue-files in devtools, + // set this to false - it *may* help + // https://vue-loader.vuejs.org/en/options.html#cachebusting + cacheBusting: true, + + cssSourceMap: true + }, + + build: { + // Template for index.html + index: path.resolve(__dirname, '../dist/index.html'), + + // Paths + assetsRoot: path.resolve(__dirname, '../dist'), + assetsSubDirectory: 'static', + assetsPublicPath: '/', + + /** + * Source Maps + */ + + productionSourceMap: true, + // https://webpack.js.org/configuration/devtool/#production + devtool: '#source-map', + + // Gzip off by default as many popular static hosts such as + // Surge or Netlify already gzip all static assets for you. + // Before setting to `true`, make sure to: + // npm install --save-dev compression-webpack-plugin + productionGzip: false, + productionGzipExtensions: ['js', 'css'], + + // Run the build command with an extra argument to + // View the bundle analyzer report after build finishes: + // `npm run build --report` + // Set to `true` or `false` to always turn it on or off + bundleAnalyzerReport: process.env.npm_config_report + } +} diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-frontend/config/prod.env.js b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/config/prod.env.js new file mode 100644 index 0000000000..a6f997616e --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/config/prod.env.js @@ -0,0 +1,4 @@ +'use strict' +module.exports = { + NODE_ENV: '"production"' +} diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-frontend/index.html b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/index.html new file mode 100644 index 0000000000..03ce3fdf27 --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/index.html @@ -0,0 +1,12 @@ + + + + + + deplugin-view-frontend + + +
+ + + diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-frontend/package.json b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/package.json new file mode 100644 index 0000000000..64a3ce7ad6 --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/package.json @@ -0,0 +1,75 @@ +{ + "name": "deplugin-datasource-frontend", + "version": "1.0.0", + "description": "A Vue.js project", + "author": "", + "private": true, + "scripts": { + "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", + "start": "npm run dev", + "build": "node build/build.js", + "buildPlugin": "node build/build-async-plugins.js" + }, + "dependencies": { + "@riophae/vue-treeselect": "0.4.0", + "highcharts": "^10.0.0", + "svg-sprite-loader": "^6.0.11", + "svgo": "1.2.2", + "svgo-loader": "^3.0.1", + "vue": "^2.5.2", + "vue-i18n": "7.3.2", + "vue-router": "^3.0.1", + "vue-uuid": "2.0.2", + "vuedraggable": "^2.24.3" + }, + "devDependencies": { + "autoprefixer": "^7.1.2", + "babel-core": "^6.22.1", + "babel-helper-vue-jsx-merge-props": "^2.0.3", + "babel-loader": "^7.1.1", + "babel-plugin-syntax-jsx": "^6.18.0", + "babel-plugin-transform-runtime": "^6.22.0", + "babel-plugin-transform-vue-jsx": "^3.5.0", + "babel-preset-env": "^1.3.2", + "babel-preset-stage-2": "^6.22.0", + "chalk": "^2.0.1", + "copy-webpack-plugin": "^4.6.0", + "css-loader": "^0.28.0", + "element-ui": "2.15.7", + "extract-text-webpack-plugin": "^4.0.0-beta.0", + "file-loader": "^1.1.4", + "friendly-errors-webpack-plugin": "^1.6.1", + "html-webpack-plugin": "^3.2.0", + "js-cookie": "2.2.0", + "node-notifier": "^8.0.1", + "optimize-css-assets-webpack-plugin": "^3.2.0", + "ora": "^1.2.0", + "portfinder": "^1.0.13", + "postcss-import": "^11.0.0", + "postcss-loader": "^2.0.8", + "postcss-url": "^7.2.1", + "rimraf": "^2.6.0", + "sass": "^1.33.0", + "sass-loader": "^7.3.1", + "semver": "^5.3.0", + "shelljs": "^0.8.5", + "uglifyjs-webpack-plugin": "^1.1.1", + "url-loader": "^0.5.8", + "vue-loader": "^15.6.4", + "vue-style-loader": "^4.1.2", + "vue-template-compiler": "^2.5.2", + "webpack": "^4.8.1", + "webpack-bundle-analyzer": "^3.3.2", + "webpack-dev-server": "^3.1.11", + "webpack-merge": "^4.1.0" + }, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + }, + "browserslist": [ + "> 1%", + "last 2 versions", + "not ie <= 8" + ] +} diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-frontend/pom.xml b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/pom.xml new file mode 100644 index 0000000000..686065e7cc --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/pom.xml @@ -0,0 +1,80 @@ + + + + + io.dataease + mongo + ${dataease.version} + + + 4.0.0 + mongo-frontend + + + UTF-8 + UTF-8 + 1.9.1 + + + + + + maven-clean-plugin + + + + static + + ** + + false + + + + + + + + com.github.eirslett + frontend-maven-plugin + ${frontend-maven-plugin.version} + + + install node and npm + + install-node-and-npm + + + + v16.20.2 + 7.6.3 + + + + + npm install + + npm + + + + install --force + + + + + npm run buildPlugin + + npm + + + run buildPlugin + + + + + + + diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/App.vue b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/App.vue new file mode 100644 index 0000000000..8542e07722 --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/App.vue @@ -0,0 +1,23 @@ + + + + + diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/assets/logo.png b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/assets/logo.png new file mode 100644 index 0000000000..f3d2503fc2 Binary files /dev/null and b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/assets/logo.png differ diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/components/HelloWorld.vue b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/components/HelloWorld.vue new file mode 100644 index 0000000000..1c19f2a399 --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/components/HelloWorld.vue @@ -0,0 +1,113 @@ + + + + + + diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/de-base/lang/en.js b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/de-base/lang/en.js new file mode 100644 index 0000000000..f1dae13210 --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/de-base/lang/en.js @@ -0,0 +1,23 @@ +export default { + plugin_view_3d_pie: { + type_title: '3D-PIE', + label: 'Label', + angle: 'Angle' + }, + host: 'Host', + port: 'Port', + dataBase: 'Catalog', + schema: 'Schema', + username: 'User', + password: 'Password', + get_schema: 'Get Schema', + please_choose_schema: 'Please select Schema', + query_timeout: 'Query timeout (seconds)', + extra_params: 'Extra JDBC connection string', + second: 'second', + please_select: 'Please select', + query_timeout: 'query timeout', + enter_the_port: 'Please enter the port', + one_user_name: 'enter one user name', + input_a_password: 'Please input a password' +} diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/de-base/lang/index.js b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/de-base/lang/index.js new file mode 100644 index 0000000000..e50d0478e6 --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/de-base/lang/index.js @@ -0,0 +1,49 @@ +import Vue from 'vue' +import VueI18n from 'vue-i18n' +import Cookies from 'js-cookie' +import elementEnLocale from 'element-ui/lib/locale/lang/en' // element-ui lang +import elementZhLocale from 'element-ui/lib/locale/lang/zh-CN'// element-ui lang +import elementTWLocale from 'element-ui/lib/locale/lang/zh-TW'// element-ui lang + +import localMessages from './messages' + + +Vue.use(VueI18n) + +const messages = { + en_US: { + ...localMessages['en_US'], + ...elementEnLocale + }, + zh_CN: { + ...localMessages['zh_CN'], + ...elementZhLocale + }, + zh_TW: { + ...localMessages['zh_TW'], + ...elementTWLocale + } +} +export function getLanguage () { + const chooseLanguage = Cookies.get('language') + if (chooseLanguage) return chooseLanguage + + // if has not choose language + const language = (navigator.language || navigator.browserLanguage).toLowerCase() + const locales = Object.keys(messages) + for (const locale of locales) { + if (language.indexOf(locale) > -1) { + return locale + } + } + return 'zh_CN' +} +const i18n = new VueI18n({ + // set locale + // options: en | zh | es + locale: getLanguage(), + // set locale messages + messages +}) + +export default i18n diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/de-base/lang/messages.js b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/de-base/lang/messages.js new file mode 100644 index 0000000000..844f8c0673 --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/de-base/lang/messages.js @@ -0,0 +1,17 @@ +import enLocale from './en' +import zhLocale from './zh' +import twLocale from './tw' + +const messages = { + en_US: { + ...enLocale + }, + zh_CN: { + ...zhLocale + }, + zh_TW: { + ...twLocale + } +} + +export default messages \ No newline at end of file diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/de-base/lang/tw.js b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/de-base/lang/tw.js new file mode 100644 index 0000000000..a64099c861 --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/de-base/lang/tw.js @@ -0,0 +1,23 @@ +export default { + plugin_view_3d_pie: { + type_title: '3D餅圖', + label: '標籤', + angle: '角度' + }, + host: '主機名', + port: '端口', + dataBase: '數據庫', + schema: 'Schema', + username: '用戶名', + password: '密碼', + get_schema: '获取 Schema', + please_choose_schema: '请选择 Schema', + query_timeout: '査詢超時(秒)', + extra_params: '額外的JDBC連接字符串', + second: '秒', + please_select: '請選擇', + query_timeout: '査詢超時', + enter_the_port: '請輸入埠', + one_user_name: '請輸入用戶名', + input_a_password: '請輸入密碼' +} diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/de-base/lang/zh.js b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/de-base/lang/zh.js new file mode 100644 index 0000000000..678ec2d364 --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/de-base/lang/zh.js @@ -0,0 +1,23 @@ +export default { + plugin_view_3d_pie: { + type_title: '3D饼图', + label: '标签', + angle: '角度' + }, + host: '主机名', + port: '端口', + dataBase: '数据库', + schema: 'Schema', + username: '用户名', + password: '密码', + get_schema: '获取 Schema', + please_choose_schema: '请选择 Schema', + query_timeout: '查询超时(秒)', + extra_params: '额外的JDBC连接字符串', + second: '秒', + please_select: '请选择', + query_timeout: '查询超时', + enter_the_port: '请输入端口', + one_user_name: '请输入用户名', + input_a_password: '请输入密码' +} diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/icons/index.js b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/icons/index.js new file mode 100644 index 0000000000..2c6b309c96 --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/icons/index.js @@ -0,0 +1,9 @@ +import Vue from 'vue' +import SvgIcon from '@/components/SvgIcon'// svg component + +// register globally +Vue.component('svg-icon', SvgIcon) + +const req = require.context('./svg', false, /\.svg$/) +const requireAll = requireContext => requireContext.keys().map(requireContext) +requireAll(req) diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/icons/svg/mongo-backend.svg b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/icons/svg/mongo-backend.svg new file mode 100644 index 0000000000..f61c4a1a00 --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/icons/svg/mongo-backend.svg @@ -0,0 +1 @@ +【icon】插件管理-导出 diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/icons/svg/mongobi.jpg b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/icons/svg/mongobi.jpg new file mode 100644 index 0000000000..dfb14739d6 Binary files /dev/null and b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/icons/svg/mongobi.jpg differ diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/icons/svgo.yml b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/icons/svgo.yml new file mode 100644 index 0000000000..d11906aec2 --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/icons/svgo.yml @@ -0,0 +1,22 @@ +# replace default config + +# multipass: true +# full: true + +plugins: + + # - name + # + # or: + # - name: false + # - name: true + # + # or: + # - name: + # param1: 1 + # param2: 2 + +- removeAttrs: + attrs: + - 'fill' + - 'fill-rule' diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/main.js b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/main.js new file mode 100644 index 0000000000..99c5626590 --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/main.js @@ -0,0 +1,33 @@ +// The Vue build version to load with the `import` command +// (runtime-only or standalone) has been set in webpack.base.conf with an alias. +import Vue from 'vue' +import App from './App' +import router from './router' +import ElementUI from 'element-ui' +import Cookies from 'js-cookie' +import i18n from './de-base/lang' +import draggable from 'vuedraggable' +import Treeselect from '@riophae/vue-treeselect' +import '@riophae/vue-treeselect/dist/vue-treeselect.css' +Vue.config.productionTip = false +Vue.use(ElementUI, { + size: Cookies.get('size') || 'medium', + i18n: (key, value) => i18n.t(key, value) +}) +Vue.component('Treeselect', Treeselect) +Vue.component('draggable', draggable) +Vue.prototype.hasDataPermission = function(pTarget, pSource) { + + if (pSource && pTarget) { + return pSource.indexOf(pTarget) > -1 + } + return false +} +/* eslint-disable no-new */ +new Vue({ + el: '#app', + router, + i18n, + components: { App }, + template: '' +}) diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/router/index.js b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/router/index.js new file mode 100644 index 0000000000..b5b55fa1fc --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/router/index.js @@ -0,0 +1,21 @@ +import Vue from 'vue' +import Router from 'vue-router' +import HelloWorld from '@/components/HelloWorld' +import maxcompute from '@/views/maxcompute' + +Vue.use(Router) + +export default new Router({ + routes: [ + { + path: '/', + name: 'HelloWorld', + component: HelloWorld + }, + { + path: '/maxcompute', + name: 'maxcompute', + component: maxcompute + } + ] +}) diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/utils/compare.js b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/utils/compare.js new file mode 100644 index 0000000000..b06a104a10 --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/utils/compare.js @@ -0,0 +1,29 @@ +export const compareItem = { + type: 'none', // year-yoy/month-yoy等 + resultData: 'percent', // 对比差sub,百分比percent等 + field: '', + custom: { + field: '', + calcType: '0', // 0-增长值,1-增长率 + timeType: '0', // 0-固定日期,1-日期区间 + currentTime: '', + compareTime: '', + currentTimeRange: [], + compareTimeRange: [] + } +} + +export const compareYearList = [ + { name: 'year_mom', value: 'year_mom' } +] + +export const compareMonthList = [ + { name: 'month_mom', value: 'month_mom' }, + { name: 'year_yoy', value: 'year_yoy' } +] + +export const compareDayList = [ + { name: 'day_mom', value: 'day_mom' }, + { name: 'month_yoy', value: 'month_yoy' }, + { name: 'year_yoy', value: 'year_yoy' } +] diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/utils/validate.js b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/utils/validate.js new file mode 100644 index 0000000000..3e8ffa9448 --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/utils/validate.js @@ -0,0 +1,15 @@ +/** + * Created by PanJiaChen on 16/11/18. + */ + +/** + * @param {string} path + * @returns {Boolean} + */ +export function isExternal(path) { + return /^(https?:|mailto:|tel:)/.test(path) || /^(http?:|mailto:|tel:)/.test(path) || path.startsWith('/api/pluginCommon/staticInfo') +} + + + + diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/views/dePwd.vue b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/views/dePwd.vue new file mode 100644 index 0000000000..61bf3e88ee --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/views/dePwd.vue @@ -0,0 +1,75 @@ + + + + \ No newline at end of file diff --git a/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/views/mongobi.vue b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/views/mongobi.vue new file mode 100644 index 0000000000..831421428a --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/mongo-frontend/src/views/mongobi.vue @@ -0,0 +1,177 @@ + + + + + diff --git a/extensions/dataease-extensions-datasource/mongo/plugin.json b/extensions/dataease-extensions-datasource/mongo/plugin.json new file mode 100644 index 0000000000..0bd1dc1bdc --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/plugin.json @@ -0,0 +1,13 @@ +{ + "name":"Mongo 数据源插件", + "free":0, + "store":"default", + "cost":0, + "category":"datasource", + "descript":"Mongo 插件,值得拥有", + "version":"1.18.0", + "creator":"DATAEASE", + "moduleName":"mongo-backend", + "require":"1.12.0", + "dsType":"mongobi" +} diff --git a/extensions/dataease-extensions-datasource/mongo/pom.xml b/extensions/dataease-extensions-datasource/mongo/pom.xml new file mode 100644 index 0000000000..75541936ef --- /dev/null +++ b/extensions/dataease-extensions-datasource/mongo/pom.xml @@ -0,0 +1,19 @@ + + + + dataease-extensions-datasource + io.dataease + ${dataease.version} + + 4.0.0 + + mongo + pom + + mongo-frontend + mongo-backend + + + diff --git a/extensions/dataease-extensions-datasource/pom.xml b/extensions/dataease-extensions-datasource/pom.xml new file mode 100644 index 0000000000..07ba2430b0 --- /dev/null +++ b/extensions/dataease-extensions-datasource/pom.xml @@ -0,0 +1,24 @@ + + + + dataease-extensions + io.dataease + ${dataease.version} + + 4.0.0 + pom + + maxcompute + presto + dm + mongo + kylin + kingbase + + + dataease-extensions-datasource + + + diff --git a/extensions/dataease-extensions-datasource/presto/build.sh b/extensions/dataease-extensions-datasource/presto/build.sh new file mode 100755 index 0000000000..e6af1535ef --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/build.sh @@ -0,0 +1,6 @@ +#!/bin/sh +mvn clean package + +cp presto-backend/target/presto-backend-1.18.0.jar . + +zip -r presto.zip ./presto-backend-1.18.0.jar ./prestoDriver ./plugin.json diff --git a/extensions/dataease-extensions-datasource/presto/plugin.json b/extensions/dataease-extensions-datasource/presto/plugin.json new file mode 100644 index 0000000000..d823556ea1 --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/plugin.json @@ -0,0 +1,13 @@ +{ + "name":"Presto 数据源插件", + "free":0, + "store":"default", + "cost":0, + "category":"datasource", + "descript":"Presto 插件,值得拥有", + "version":"1.18.0", + "creator":"DATAEASE", + "moduleName":"presto-backend", + "require":"1.11.0", + "dsType":"presto" +} diff --git a/extensions/dataease-extensions-datasource/presto/pom.xml b/extensions/dataease-extensions-datasource/presto/pom.xml new file mode 100644 index 0000000000..0758a805b8 --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/pom.xml @@ -0,0 +1,19 @@ + + + + dataease-extensions-datasource + io.dataease + ${dataease.version} + + 4.0.0 + + presto + pom + + presto-frontend + presto-backend + + + diff --git a/extensions/dataease-extensions-datasource/presto/presto-backend/pom.xml b/extensions/dataease-extensions-datasource/presto/presto-backend/pom.xml new file mode 100644 index 0000000000..d1d3734857 --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-backend/pom.xml @@ -0,0 +1,109 @@ + + + + presto + io.dataease + ${dataease.version} + + 4.0.0 + + presto-backend + + + + io.dataease + dataease-plugin-datasource + + + + + + + src/main/java + + **/*.properties + **/*.xml + + false + + + src/main/resources + + **/* + + false + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 11 + 11 + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.4 + + + **/server/** + **/*.properties + **/Application* + + + + + + maven-clean-plugin + + + + src/main/resources/static + + ** + + false + + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + main-class-placement + generate-resources + + + + + + + + + + + + run + + + + + + + + + + + diff --git a/extensions/dataease-extensions-datasource/presto/presto-backend/src/main/java/io/dataease/plugins/datasource/dm/provider/PrestoConfig.java b/extensions/dataease-extensions-datasource/presto/presto-backend/src/main/java/io/dataease/plugins/datasource/dm/provider/PrestoConfig.java new file mode 100644 index 0000000000..4a4692174f --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-backend/src/main/java/io/dataease/plugins/datasource/dm/provider/PrestoConfig.java @@ -0,0 +1,22 @@ +package io.dataease.plugins.datasource.dm.provider; + +import io.dataease.plugins.datasource.entity.JdbcConfiguration; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class PrestoConfig extends JdbcConfiguration { + + private String driver = "io.prestosql.jdbc.PrestoDriver"; + private String extraParams; + + + public String getJdbc() { + return "jdbc:presto://HOST:PORT/DATABASE/SCHEMA" + .replace("HOST", getHost().trim()) + .replace("PORT", getPort().toString()) + .replace("DATABASE", getDataBase().trim()) + .replace("SCHEMA", getSchema().trim()); + } +} diff --git a/extensions/dataease-extensions-datasource/presto/presto-backend/src/main/java/io/dataease/plugins/datasource/dm/provider/PrestoDsProvider.java b/extensions/dataease-extensions-datasource/presto/presto-backend/src/main/java/io/dataease/plugins/datasource/dm/provider/PrestoDsProvider.java new file mode 100644 index 0000000000..4277d77128 --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-backend/src/main/java/io/dataease/plugins/datasource/dm/provider/PrestoDsProvider.java @@ -0,0 +1,154 @@ +package io.dataease.plugins.datasource.dm.provider; + +import com.google.gson.Gson; +import io.dataease.plugins.common.base.domain.DeDriver; +import io.dataease.plugins.common.base.mapper.DeDriverMapper; +import io.dataease.plugins.common.dto.datasource.TableDesc; +import io.dataease.plugins.common.dto.datasource.TableField; +import io.dataease.plugins.common.exception.DataEaseException; +import io.dataease.plugins.common.request.datasource.DatasourceRequest; +import io.dataease.plugins.datasource.entity.JdbcConfiguration; +import io.dataease.plugins.datasource.provider.DefaultJdbcProvider; +import io.dataease.plugins.datasource.provider.ExtendedJdbcClassLoader; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.lang.reflect.Method; +import java.sql.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + + +@Component() +public class PrestoDsProvider extends DefaultJdbcProvider { + @Resource + private DeDriverMapper deDriverMapper; + + @Override + public String getType() { + return "presto"; + } + + @Override + public boolean isUseDatasourcePool() { + return false; + } + + @Override + public Connection getConnection(DatasourceRequest datasourceRequest) throws Exception { + PrestoConfig prestoConfig = new Gson().fromJson(datasourceRequest.getDatasource().getConfiguration(), PrestoConfig.class); + + String defaultDriver = prestoConfig.getDriver(); + String customDriver = prestoConfig.getCustomDriver(); + + String url = prestoConfig.getJdbc(); + Properties props = new Properties(); + DeDriver deDriver = null; + if(StringUtils.isNotEmpty(prestoConfig.getAuthMethod()) && prestoConfig.getAuthMethod().equalsIgnoreCase("kerberos")){ + System.setProperty("java.security.krb5.conf", "/opt/dataease/conf/krb5.conf"); + ExtendedJdbcClassLoader classLoader; + if(isDefaultClassLoader(customDriver)){ + classLoader = extendedJdbcClassLoader; + }else { + deDriver = deDriverMapper.selectByPrimaryKey(customDriver); + classLoader = getCustomJdbcClassLoader(deDriver); + } + Class ConfigurationClass = classLoader.loadClass("org.apache.hadoop.conf.Configuration"); + Method set = ConfigurationClass.getMethod("set",String.class, String.class) ; + Object obj = ConfigurationClass.newInstance(); + set.invoke(obj, "hadoop.security.authentication", "Kerberos"); + + Class UserGroupInformationClass = classLoader.loadClass("org.apache.hadoop.security.UserGroupInformation"); + Method setConfiguration = UserGroupInformationClass.getMethod("setConfiguration",ConfigurationClass) ; + Method loginUserFromKeytab = UserGroupInformationClass.getMethod("loginUserFromKeytab",String.class, String.class) ; + setConfiguration.invoke(null, obj); + loginUserFromKeytab.invoke(null, prestoConfig.getUsername(), "/opt/dataease/conf/" + prestoConfig.getPassword()); + }else { + if (StringUtils.isNotBlank(prestoConfig.getUsername())) { + props.setProperty("user", prestoConfig.getUsername()); + if (StringUtils.isNotBlank(prestoConfig.getPassword())) { + props.setProperty("password", prestoConfig.getPassword()); + } + } + } + + Connection conn; + String driverClassName ; + ExtendedJdbcClassLoader jdbcClassLoader; + if(isDefaultClassLoader(customDriver)){ + driverClassName = defaultDriver; + jdbcClassLoader = extendedJdbcClassLoader; + }else { + if(deDriver == null){ + deDriver = deDriverMapper.selectByPrimaryKey(customDriver); + } + driverClassName = deDriver.getDriverClass(); + jdbcClassLoader = getCustomJdbcClassLoader(deDriver); + } + + Driver driverClass = (Driver) jdbcClassLoader.loadClass(driverClassName).newInstance(); + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + try { + Thread.currentThread().setContextClassLoader(jdbcClassLoader); + conn= driverClass.connect(url, props); + }catch (Exception e){ + e.printStackTrace(); + throw e; + }finally { + Thread.currentThread().setContextClassLoader(classLoader); + } + return conn; + } + + @Override + public List getTables(DatasourceRequest datasourceRequest) throws Exception { + List tables = new ArrayList<>(); + String queryStr = getTablesSql(datasourceRequest); + JdbcConfiguration jdbcConfiguration = new Gson().fromJson(datasourceRequest.getDatasource().getConfiguration(), JdbcConfiguration.class); + int queryTimeout = jdbcConfiguration.getQueryTimeout() > 0 ? jdbcConfiguration.getQueryTimeout() : 0; + try (Connection con = getConnectionFromPool(datasourceRequest); Statement statement = getStatement(con, queryTimeout); ResultSet resultSet = statement.executeQuery(queryStr)) { + while (resultSet.next()) { + tables.add(getTableDesc(datasourceRequest, resultSet)); + } + } catch (Exception e) { + DataEaseException.throwException(e); + } + + return tables; + } + + private TableDesc getTableDesc(DatasourceRequest datasourceRequest, ResultSet resultSet) throws SQLException { + TableDesc tableDesc = new TableDesc(); + tableDesc.setName(resultSet.getString(1)); + return tableDesc; + } + + @Override + public List getTableFields(DatasourceRequest datasourceRequest) throws Exception { + datasourceRequest.setQuery("select * from " + datasourceRequest.getTable() + " limit 0"); + return fetchResultField(datasourceRequest); + } + + @Override + public String checkStatus(DatasourceRequest datasourceRequest) throws Exception { + String queryStr = getTablesSql(datasourceRequest); + JdbcConfiguration jdbcConfiguration = new Gson().fromJson(datasourceRequest.getDatasource().getConfiguration(), JdbcConfiguration.class); + int queryTimeout = jdbcConfiguration.getQueryTimeout() > 0 ? jdbcConfiguration.getQueryTimeout() : 0; + try (Connection con = getConnection(datasourceRequest); Statement statement = getStatement(con, queryTimeout); ResultSet resultSet = statement.executeQuery(queryStr)) { + } catch (Exception e) { + e.printStackTrace(); + DataEaseException.throwException(e.getMessage()); + } + return "Success"; + } + + + @Override + public String getTablesSql(DatasourceRequest datasourceRequest) throws Exception { + PrestoConfig prestoConfig = new Gson().fromJson(datasourceRequest.getDatasource().getConfiguration(), PrestoConfig.class); + return "show tables in " + prestoConfig.getDataBase() + "." + prestoConfig.getSchema(); + } + +} diff --git a/extensions/dataease-extensions-datasource/presto/presto-backend/src/main/java/io/dataease/plugins/datasource/dm/query/PrestoConstants.java b/extensions/dataease-extensions-datasource/presto/presto-backend/src/main/java/io/dataease/plugins/datasource/dm/query/PrestoConstants.java new file mode 100644 index 0000000000..a71916ecfb --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-backend/src/main/java/io/dataease/plugins/datasource/dm/query/PrestoConstants.java @@ -0,0 +1,45 @@ +package io.dataease.plugins.datasource.dm.query; + + + +import io.dataease.plugins.common.constants.datasource.SQLConstants; + + +public class PrestoConstants extends SQLConstants { + + public static final String KEYWORD_TABLE = "%s" ; + + public static final String KEYWORD_FIX = "%s." + "%s"; + + public static final String UNIX_TIMESTAMP = "to_unixtime(%s)"; + + public static final String FROM_UNIXTIME = "from_unixtime(%s)"; + + public static final String CAST = "CAST(%s AS %s)"; + + public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss"; + + public static final String FORMAT_DATETIME = "format_datetime(%s, '%s')"; + + public static final String date_parse = "date_parse(%s, '%s')"; + + public static final String DEFAULT_INT_FORMAT = "bigint"; + + public static final String DEFAULT_FLOAT_FORMAT = "DOUBLE"; + + public static final String WHERE_VALUE_NULL = "(NULL,'')"; + + public static final String WHERE_VALUE_VALUE = "'%s'"; + + public static final String AGG_COUNT = "COUNT(*)"; + + public static final String AGG_FIELD = "%s(%s)"; + + public static final String WHERE_BETWEEN = "'%s' AND '%s'"; + + public static final String BRACKETS = "(%s)"; + + public static final String NAME = "pg"; + + +} diff --git a/extensions/dataease-extensions-datasource/presto/presto-backend/src/main/java/io/dataease/plugins/datasource/dm/query/PrestoQueryProvider.java b/extensions/dataease-extensions-datasource/presto/presto-backend/src/main/java/io/dataease/plugins/datasource/dm/query/PrestoQueryProvider.java new file mode 100644 index 0000000000..c2ec9926d6 --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-backend/src/main/java/io/dataease/plugins/datasource/dm/query/PrestoQueryProvider.java @@ -0,0 +1,1287 @@ +package io.dataease.plugins.datasource.dm.query; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.gson.Gson; +import io.dataease.plugins.common.base.domain.ChartViewWithBLOBs; +import io.dataease.plugins.common.base.domain.DatasetTableField; +import io.dataease.plugins.common.base.domain.DatasetTableFieldExample; +import io.dataease.plugins.common.base.domain.Datasource; +import io.dataease.plugins.common.base.mapper.DatasetTableFieldMapper; +import io.dataease.plugins.common.constants.DeTypeConstants; +import io.dataease.plugins.common.constants.datasource.SQLConstants; +import io.dataease.plugins.common.constants.datasource.SqlServerSQLConstants; +import io.dataease.plugins.common.dto.chart.ChartCustomFilterItemDTO; +import io.dataease.plugins.common.dto.chart.ChartFieldCustomFilterDTO; +import io.dataease.plugins.common.dto.chart.ChartViewFieldDTO; +import io.dataease.plugins.common.dto.datasource.DeSortField; +import io.dataease.plugins.common.dto.sqlObj.SQLObj; +import io.dataease.plugins.common.request.chart.ChartExtFilterRequest; +import io.dataease.plugins.common.request.permission.DataSetRowPermissionsTreeDTO; +import io.dataease.plugins.common.request.permission.DatasetRowPermissionsTreeItem; +import io.dataease.plugins.datasource.dm.provider.PrestoConfig; +import io.dataease.plugins.datasource.entity.Dateformat; +import io.dataease.plugins.datasource.entity.JdbcConfiguration; +import io.dataease.plugins.datasource.entity.PageInfo; +import io.dataease.plugins.datasource.query.QueryProvider; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; +import org.stringtemplate.v4.ST; +import org.stringtemplate.v4.STGroup; +import org.stringtemplate.v4.STGroupFile; + +import javax.annotation.Resource; +import java.text.MessageFormat; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import static io.dataease.plugins.common.constants.datasource.SQLConstants.TABLE_ALIAS_PREFIX; + + +@Component() +public class PrestoQueryProvider extends QueryProvider { + @Resource + private DatasetTableFieldMapper datasetTableFieldMapper; + + private static final Gson json = new Gson(); + + @Override + public Integer transFieldType(String field) { + field = field.contains("(") ? field.split("\\(")[0] : field; + field = field.toUpperCase(); + switch (field) { + case "DATE": + case "DATETIME": + case "TIMESTAMP": + case "TIME": + return DeTypeConstants.DE_TIME;// 时间 + case "TINYINT": + case "SMALLINT": + case "INT": + case "BIGINT": + case "INTEGER": + return DeTypeConstants.DE_INT;// 整型 + case "FLOAT": + case "DOUBLE": + case "DECIMAL": + return DeTypeConstants.DE_FLOAT;// 浮点 + case "BOOLEAN": + return DeTypeConstants.DE_BOOL;// 布尔 + case "BINARY": + return DeTypeConstants.DE_BINARY;// 二进制 + default: + return DeTypeConstants.DE_STRING; + } + } + + @Override + public String createSQLPreview(String sql, String orderBy) { + return "SELECT * FROM (" + sqlFix(sql) + ") AS DE_TMP " + " LIMIT 1000"; + } + + @Override + public String createQuerySQL(String table, List fields, boolean isGroup, Datasource ds, List fieldCustomFilter, List rowPermissionsTree) { + return createQuerySQL(table, fields, isGroup, ds, fieldCustomFilter, rowPermissionsTree, null); + } + + @Override + public String createQuerySQL(String table, List fields, boolean isGroup, Datasource ds, List fieldCustomFilter, List rowPermissionsTree, List sortFields) { + SQLObj tableObj = SQLObj.builder() + .tableName((table.startsWith("(") && table.endsWith(")")) ? table : String.format(PrestoConstants.KEYWORD_TABLE, table)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 0)) + .build(); + + setSchema(tableObj, ds); + List xFields = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(fields)) { + for (int i = 0; i < fields.size(); i++) { + DatasetTableField f = fields.get(i); + String originField; + if (ObjectUtils.isNotEmpty(f.getExtField()) && f.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(f.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(f.getExtField()) && f.getExtField() == 1) { + originField = String.format(PrestoConstants.KEYWORD_FIX, tableObj.getTableAlias(), f.getOriginName()); + } else { + originField = String.format(PrestoConstants.KEYWORD_FIX, tableObj.getTableAlias(), f.getOriginName()); + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_X_PREFIX, i); + String fieldName = ""; + // 处理横轴字段 + if (f.getDeExtractType() == DeTypeConstants.DE_TIME) { + if (f.getDeType() == DeTypeConstants.DE_INT || f.getDeType() == DeTypeConstants.DE_FLOAT) { + fieldName = String.format(PrestoConstants.UNIX_TIMESTAMP, originField); + } else { + fieldName = originField; + } + } else if (f.getDeExtractType() == DeTypeConstants.DE_STRING) { + if (f.getDeType() == DeTypeConstants.DE_INT) { + fieldName = String.format(PrestoConstants.CAST, originField, PrestoConstants.DEFAULT_INT_FORMAT); + } else if (f.getDeType() == DeTypeConstants.DE_FLOAT) { + fieldName = String.format(PrestoConstants.CAST, originField, PrestoConstants.DEFAULT_FLOAT_FORMAT); + } else if (f.getDeType() == DeTypeConstants.DE_TIME) { + fieldName = String.format(PrestoConstants.date_parse, originField, StringUtils.isNotEmpty(f.getDateFormat()) ? f.getDateFormat() : PrestoConstants.DEFAULT_DATE_FORMAT); + } else { + fieldName = originField; + } + } else { + if (f.getDeType() == DeTypeConstants.DE_TIME) { + fieldName = String.format(PrestoConstants.FORMAT_DATETIME, String.format(PrestoConstants.FROM_UNIXTIME, originField + "/1000"), PrestoConstants.DEFAULT_DATE_FORMAT); + } else if (f.getDeType() == DeTypeConstants.DE_INT) { + fieldName = String.format(PrestoConstants.CAST, originField, PrestoConstants.DEFAULT_INT_FORMAT); + } else { + fieldName = originField; + } + } + xFields.add(SQLObj.builder() + .fieldName(fieldName) + .fieldAlias(fieldAlias) + .build()); + } + } + + STGroup stg = new STGroupFile(SQLConstants.SQL_TEMPLATE); + ST st_sql = stg.getInstanceOf("previewSql"); + st_sql.add("isGroup", isGroup); + if (CollectionUtils.isNotEmpty(xFields)) st_sql.add("groups", xFields); + if (ObjectUtils.isNotEmpty(tableObj)) st_sql.add("table", tableObj); + String customWheres = transCustomFilterList(tableObj, fieldCustomFilter); + // row permissions tree + String whereTrees = transFilterTrees(tableObj, rowPermissionsTree); + List wheres = new ArrayList<>(); + if (customWheres != null) wheres.add(customWheres); + if (whereTrees != null) wheres.add(whereTrees); + if (CollectionUtils.isNotEmpty(wheres)) st_sql.add("filters", wheres); + + List xOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(sortFields)) { + int step = fields.size(); + for (int i = step; i < (step + sortFields.size()); i++) { + DeSortField deSortField = sortFields.get(i - step); + SQLObj order = buildSortField(deSortField, tableObj, i); + xOrders.add(order); + } + } + if (ObjectUtils.isNotEmpty(xOrders)) { + st_sql.add("orders", xOrders); + } + + return st_sql.render(); + } + + private SQLObj buildSortField(DeSortField f, SQLObj tableObj, int i) { + String originField; + if (ObjectUtils.isNotEmpty(f.getExtField()) && f.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(f.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(f.getExtField()) && f.getExtField() == 1) { + originField = String.format(PrestoConstants.KEYWORD_FIX, tableObj.getTableAlias(), f.getOriginName()); + } else { + originField = String.format(PrestoConstants.KEYWORD_FIX, tableObj.getTableAlias(), f.getOriginName()); + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_X_PREFIX, i); + String fieldName = ""; + // 处理横轴字段 + if (f.getDeExtractType() == DeTypeConstants.DE_TIME) { + if (f.getDeType() == DeTypeConstants.DE_INT || f.getDeType() == DeTypeConstants.DE_FLOAT) { + fieldName = String.format(PrestoConstants.UNIX_TIMESTAMP, originField); + } else { + fieldName = originField; + } + } else if (f.getDeExtractType() == DeTypeConstants.DE_STRING) { + if (f.getDeType() == DeTypeConstants.DE_INT) { + fieldName = String.format(PrestoConstants.CAST, originField, PrestoConstants.DEFAULT_INT_FORMAT); + } else if (f.getDeType() == DeTypeConstants.DE_FLOAT) { + fieldName = String.format(PrestoConstants.CAST, originField, PrestoConstants.DEFAULT_FLOAT_FORMAT); + } else if (f.getDeType() == DeTypeConstants.DE_TIME) { + fieldName = String.format(PrestoConstants.date_parse, originField, StringUtils.isNotEmpty(f.getDateFormat()) ? f.getDateFormat() : PrestoConstants.DEFAULT_DATE_FORMAT); + } else { + fieldName = originField; + } + } else { + if (f.getDeType() == DeTypeConstants.DE_TIME) { + fieldName = String.format(PrestoConstants.FORMAT_DATETIME, String.format(PrestoConstants.FROM_UNIXTIME, originField + "/1000"), PrestoConstants.DEFAULT_DATE_FORMAT); + } else if (f.getDeType() == DeTypeConstants.DE_INT) { + fieldName = String.format(PrestoConstants.CAST, originField, PrestoConstants.DEFAULT_INT_FORMAT); + } else { + fieldName = originField; + } + } + SQLObj result = SQLObj.builder().orderField(originField).orderAlias(originField).orderDirection(f.getOrderDirection()).build(); + return result; + } + + @Override + public String createQuerySQLAsTmp(String sql, List fields, boolean isGroup, List fieldCustomFilter, List rowPermissionsTree, List sortFields) { + return createQuerySQL("(" + sqlFix(sql) + ")", fields, isGroup, null, fieldCustomFilter, rowPermissionsTree, sortFields); + } + + @Override + public String createQuerySQLAsTmp(String sql, List fields, boolean isGroup, List fieldCustomFilter, List rowPermissionsTree) { + return createQuerySQL("(" + sqlFix(sql) + ")", fields, isGroup, null, fieldCustomFilter, rowPermissionsTree); + } + + @Override + public String createQueryTableWithPage(String table, List fields, Integer page, Integer pageSize, Integer realSize, boolean isGroup, Datasource ds, List fieldCustomFilter, List rowPermissionsTree) { + return createQuerySQL(table, fields, isGroup, ds, fieldCustomFilter, rowPermissionsTree) + " LIMIT " + realSize; + } + + @Override + public String createQuerySQLWithPage(String sql, List fields, Integer page, Integer pageSize, Integer realSize, boolean isGroup, List fieldCustomFilter, List rowPermissionsTree) { + return createQuerySQLAsTmp(sql, fields, isGroup, fieldCustomFilter, rowPermissionsTree) + " LIMIT " + realSize; + } + + @Override + public String createQueryTableWithLimit(String table, List fields, Integer limit, boolean isGroup, Datasource ds, List fieldCustomFilter, List rowPermissionsTree) { + return createQuerySQL(table, fields, isGroup, ds, fieldCustomFilter, rowPermissionsTree) + " LIMIT " + limit; + } + + @Override + public String createQuerySqlWithLimit(String sql, List fields, Integer limit, boolean isGroup, List fieldCustomFilter, List rowPermissionsTree) { + return createQuerySQLAsTmp(sql, fields, isGroup, fieldCustomFilter, rowPermissionsTree) + " LIMIT " + limit; + } + + @Override + public String getSQL(String table, List xAxis, List yAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, Datasource ds, ChartViewWithBLOBs view) { + SQLObj tableObj = SQLObj.builder() + .tableName((table.startsWith("(") && table.endsWith(")")) ? table : String.format(PrestoConstants.KEYWORD_TABLE, table)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 0)) + .build(); + setSchema(tableObj, ds); + List xFields = new ArrayList<>(); + List xOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(xAxis)) { + for (int i = 0; i < xAxis.size(); i++) { + ChartViewFieldDTO x = xAxis.get(i); + String originField; + if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(x.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 1) { + originField = String.format(PrestoConstants.KEYWORD_FIX, tableObj.getTableAlias(), x.getOriginName()); + } else { + originField = String.format(PrestoConstants.KEYWORD_FIX, tableObj.getTableAlias(), x.getOriginName()); + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_X_PREFIX, i); + // 处理横轴字段 + xFields.add(getXFields(x, originField, fieldAlias)); + // 处理横轴排序 + if (StringUtils.isNotEmpty(x.getSort()) && !StringUtils.equalsIgnoreCase(x.getSort(), "none")) { + xOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(x.getSort()) + .build()); + } + } + } + List yFields = new ArrayList<>(); + List yWheres = new ArrayList<>(); + List yOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(yAxis)) { + for (int i = 0; i < yAxis.size(); i++) { + ChartViewFieldDTO y = yAxis.get(i); + String originField; + if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(y.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 1) { + originField = String.format(PrestoConstants.KEYWORD_FIX, tableObj.getTableAlias(), y.getOriginName()); + } else { + originField = String.format(PrestoConstants.KEYWORD_FIX, tableObj.getTableAlias(), y.getOriginName()); + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_Y_PREFIX, i); + // 处理纵轴字段 + yFields.add(getYFields(y, originField, fieldAlias)); + // 处理纵轴过滤 + yWheres.add(getYWheres(y, originField, fieldAlias)); + // 处理纵轴排序 + if (StringUtils.isNotEmpty(y.getSort()) && !StringUtils.equalsIgnoreCase(y.getSort(), "none")) { + yOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(y.getSort()) + .build()); + } + } + } + // 处理视图中字段过滤 + String customWheres = transCustomFilterList(tableObj, fieldCustomFilter); + // 处理仪表板字段过滤 + String extWheres = transExtFilterList(tableObj, extFilterRequestList); + // row permissions tree + String whereTrees = transFilterTrees(tableObj, rowPermissionsTree); + // 构建sql所有参数 + List fields = new ArrayList<>(); + fields.addAll(xFields); + fields.addAll(yFields); + List wheres = new ArrayList<>(); + if (customWheres != null) wheres.add(customWheres); + if (extWheres != null) wheres.add(extWheres); + if (whereTrees != null) wheres.add(whereTrees); + List groups = new ArrayList<>(); + groups.addAll(xFields); + // 外层再次套sql + List orders = new ArrayList<>(); + orders.addAll(xOrders); + orders.addAll(yOrders); + List aggWheres = new ArrayList<>(); + aggWheres.addAll(yWheres.stream().filter(ObjectUtils::isNotEmpty).collect(Collectors.toList())); + + STGroup stg = new STGroupFile(SQLConstants.SQL_TEMPLATE); + ST st_sql = stg.getInstanceOf("querySql"); + if (CollectionUtils.isNotEmpty(xFields)) st_sql.add("groups", xFields); + if (CollectionUtils.isNotEmpty(yFields)) st_sql.add("aggregators", yFields); + if (CollectionUtils.isNotEmpty(wheres)) st_sql.add("filters", wheres); + if (ObjectUtils.isNotEmpty(tableObj)) st_sql.add("table", tableObj); + String sql = st_sql.render(); + + ST st = stg.getInstanceOf("querySql"); + SQLObj tableSQL = SQLObj.builder() + .tableName(String.format(PrestoConstants.BRACKETS, sql)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 1)) + .build(); + if (CollectionUtils.isNotEmpty(aggWheres)) st.add("filters", aggWheres); + if (CollectionUtils.isNotEmpty(orders)) st.add("orders", orders); + if (ObjectUtils.isNotEmpty(tableSQL)) st.add("table", tableSQL); + return sqlLimit(st.render(), view); + } + + + private String originalTableInfo(String table, List xAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, Datasource ds, ChartViewWithBLOBs view) { + SQLObj tableObj = SQLObj.builder() + .tableName((table.startsWith("(") && table.endsWith(")")) ? table : String.format(PrestoConstants.KEYWORD_TABLE, table)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 0)) + .build(); + setSchema(tableObj, ds); + List xFields = new ArrayList<>(); + List xOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(xAxis)) { + for (int i = 0; i < xAxis.size(); i++) { + ChartViewFieldDTO x = xAxis.get(i); + String originField; + if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(x.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 1) { + originField = String.format(PrestoConstants.KEYWORD_FIX, tableObj.getTableAlias(), x.getOriginName()); + } else { + if (x.getDeType() == 2 || x.getDeType() == 3) { + originField = String.format(PrestoConstants.CAST, String.format(PrestoConstants.KEYWORD_FIX, tableObj.getTableAlias(), x.getOriginName()), PrestoConstants.DEFAULT_FLOAT_FORMAT); + } else { + originField = String.format(PrestoConstants.KEYWORD_FIX, tableObj.getTableAlias(), x.getOriginName()); + } + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_X_PREFIX, i); + // 处理横轴字段 + xFields.add(getXFields(x, originField, fieldAlias)); + // 处理横轴排序 + if (StringUtils.isNotEmpty(x.getSort()) && !StringUtils.equalsIgnoreCase(x.getSort(), "none")) { + xOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(x.getSort()) + .build()); + } + } + } + // 处理视图中字段过滤 + String customWheres = transCustomFilterList(tableObj, fieldCustomFilter); + // 处理仪表板字段过滤 + String extWheres = transExtFilterList(tableObj, extFilterRequestList); + // row permissions tree + String whereTrees = transFilterTrees(tableObj, rowPermissionsTree); + // 构建sql所有参数 + List fields = new ArrayList<>(); + fields.addAll(xFields); + List wheres = new ArrayList<>(); + if (customWheres != null) wheres.add(customWheres); + if (extWheres != null) wheres.add(extWheres); + if (whereTrees != null) wheres.add(whereTrees); + List groups = new ArrayList<>(); + groups.addAll(xFields); + // 外层再次套sql + List orders = new ArrayList<>(); + orders.addAll(xOrders); + + STGroup stg = new STGroupFile(SQLConstants.SQL_TEMPLATE); + ST st_sql = stg.getInstanceOf("previewSql"); + st_sql.add("isGroup", false); + if (CollectionUtils.isNotEmpty(xFields)) st_sql.add("groups", xFields); + if (CollectionUtils.isNotEmpty(wheres)) st_sql.add("filters", wheres); + if (ObjectUtils.isNotEmpty(tableObj)) st_sql.add("table", tableObj); + String sql = st_sql.render(); + + ST st = stg.getInstanceOf("previewSql"); + st.add("isGroup", false); + SQLObj tableSQL = SQLObj.builder() + .tableName(String.format(PrestoConstants.BRACKETS, sql)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 1)) + .build(); + if (CollectionUtils.isNotEmpty(orders)) st.add("orders", orders); + if (ObjectUtils.isNotEmpty(tableSQL)) st.add("table", tableSQL); + return st.render(); + } + + @Override + public String getSQLTableInfo(String table, List xAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, Datasource ds, ChartViewWithBLOBs view) { + return sqlLimit(originalTableInfo(table, xAxis, fieldCustomFilter, rowPermissionsTree, extFilterRequestList, ds, view), view); + } + + @Override + public String getSQLAsTmpTableInfo(String sql, List xAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, Datasource ds, ChartViewWithBLOBs view) { + return getSQLTableInfo("(" + sqlFix(sql) + ")", xAxis, fieldCustomFilter, rowPermissionsTree, extFilterRequestList, ds, view); + } + + + @Override + public String getSQLAsTmp(String sql, List xAxis, List yAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, ChartViewWithBLOBs view) { + return getSQL("(" + sqlFix(sql) + ")", xAxis, yAxis, fieldCustomFilter, rowPermissionsTree, extFilterRequestList, null, view); + } + + @Override + public String getSQLStack(String table, List xAxis, List yAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, List extStack, Datasource ds, ChartViewWithBLOBs view) { + SQLObj tableObj = SQLObj.builder() + .tableName((table.startsWith("(") && table.endsWith(")")) ? table : String.format(PrestoConstants.KEYWORD_TABLE, table)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 0)) + .build(); + setSchema(tableObj, ds); + List xFields = new ArrayList<>(); + List xOrders = new ArrayList<>(); + List xList = new ArrayList<>(); + xList.addAll(xAxis); + xList.addAll(extStack); + if (CollectionUtils.isNotEmpty(xList)) { + for (int i = 0; i < xList.size(); i++) { + ChartViewFieldDTO x = xList.get(i); + String originField; + if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(x.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 1) { + originField = String.format(PrestoConstants.KEYWORD_FIX, tableObj.getTableAlias(), x.getOriginName()); + } else { + originField = String.format(PrestoConstants.KEYWORD_FIX, tableObj.getTableAlias(), x.getOriginName()); + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_X_PREFIX, i); + // 处理横轴字段 + xFields.add(getXFields(x, originField, fieldAlias)); + // 处理横轴排序 + if (StringUtils.isNotEmpty(x.getSort()) && !StringUtils.equalsIgnoreCase(x.getSort(), "none")) { + xOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(x.getSort()) + .build()); + } + } + } + List yFields = new ArrayList<>(); + List yWheres = new ArrayList<>(); + List yOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(yAxis)) { + for (int i = 0; i < yAxis.size(); i++) { + ChartViewFieldDTO y = yAxis.get(i); + String originField; + if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(y.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 1) { + originField = String.format(PrestoConstants.KEYWORD_FIX, tableObj.getTableAlias(), y.getOriginName()); + } else { + originField = String.format(PrestoConstants.KEYWORD_FIX, tableObj.getTableAlias(), y.getOriginName()); + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_Y_PREFIX, i); + // 处理纵轴字段 + yFields.add(getYFields(y, originField, fieldAlias)); + // 处理纵轴过滤 + yWheres.add(getYWheres(y, originField, fieldAlias)); + // 处理纵轴排序 + if (StringUtils.isNotEmpty(y.getSort()) && !StringUtils.equalsIgnoreCase(y.getSort(), "none")) { + yOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(y.getSort()) + .build()); + } + } + } + // 处理视图中字段过滤 + String customWheres = transCustomFilterList(tableObj, fieldCustomFilter); + // 处理仪表板字段过滤 + String extWheres = transExtFilterList(tableObj, extFilterRequestList); + // row permissions tree + String whereTrees = transFilterTrees(tableObj, rowPermissionsTree); + // 构建sql所有参数 + List fields = new ArrayList<>(); + fields.addAll(xFields); + fields.addAll(yFields); + List wheres = new ArrayList<>(); + if (customWheres != null) wheres.add(customWheres); + if (extWheres != null) wheres.add(extWheres); + if (whereTrees != null) wheres.add(whereTrees); + List groups = new ArrayList<>(); + groups.addAll(xFields); + // 外层再次套sql + List orders = new ArrayList<>(); + orders.addAll(xOrders); + orders.addAll(yOrders); + List aggWheres = new ArrayList<>(); + aggWheres.addAll(yWheres.stream().filter(ObjectUtils::isNotEmpty).collect(Collectors.toList())); + + STGroup stg = new STGroupFile(SQLConstants.SQL_TEMPLATE); + ST st_sql = stg.getInstanceOf("querySql"); + if (CollectionUtils.isNotEmpty(xFields)) st_sql.add("groups", xFields); + if (CollectionUtils.isNotEmpty(yFields)) st_sql.add("aggregators", yFields); + if (CollectionUtils.isNotEmpty(wheres)) st_sql.add("filters", wheres); + if (ObjectUtils.isNotEmpty(tableObj)) st_sql.add("table", tableObj); + String sql = st_sql.render(); + + ST st = stg.getInstanceOf("querySql"); + SQLObj tableSQL = SQLObj.builder() + .tableName(String.format(PrestoConstants.BRACKETS, sql)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 1)) + .build(); + if (CollectionUtils.isNotEmpty(aggWheres)) st.add("filters", aggWheres); + if (CollectionUtils.isNotEmpty(orders)) st.add("orders", orders); + if (ObjectUtils.isNotEmpty(tableSQL)) st.add("table", tableSQL); + return sqlLimit(st.render(), view); + } + + @Override + public String getSQLAsTmpStack(String table, List xAxis, List yAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, List extStack, ChartViewWithBLOBs view) { + return getSQLStack("(" + sqlFix(table) + ")", xAxis, yAxis, fieldCustomFilter, rowPermissionsTree, extFilterRequestList, extStack, null, view); + } + + @Override + public String getSQLScatter(String table, List xAxis, List yAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, List extBubble, Datasource ds, ChartViewWithBLOBs view) { + SQLObj tableObj = SQLObj.builder() + .tableName((table.startsWith("(") && table.endsWith(")")) ? table : String.format(PrestoConstants.KEYWORD_TABLE, table)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 0)) + .build(); + setSchema(tableObj, ds); + List xFields = new ArrayList<>(); + List xOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(xAxis)) { + for (int i = 0; i < xAxis.size(); i++) { + ChartViewFieldDTO x = xAxis.get(i); + String originField; + if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(x.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(x.getExtField()) && x.getExtField() == 1) { + originField = String.format(PrestoConstants.KEYWORD_FIX, tableObj.getTableAlias(), x.getOriginName()); + } else { + originField = String.format(PrestoConstants.KEYWORD_FIX, tableObj.getTableAlias(), x.getOriginName()); + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_X_PREFIX, i); + // 处理横轴字段 + xFields.add(getXFields(x, originField, fieldAlias)); + // 处理横轴排序 + if (StringUtils.isNotEmpty(x.getSort()) && !StringUtils.equalsIgnoreCase(x.getSort(), "none")) { + xOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(x.getSort()) + .build()); + } + } + } + List yFields = new ArrayList<>(); + List yWheres = new ArrayList<>(); + List yOrders = new ArrayList<>(); + List yList = new ArrayList<>(); + yList.addAll(yAxis); + yList.addAll(extBubble); + if (CollectionUtils.isNotEmpty(yList)) { + for (int i = 0; i < yList.size(); i++) { + ChartViewFieldDTO y = yList.get(i); + String originField; + if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(y.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 1) { + originField = String.format(PrestoConstants.KEYWORD_FIX, tableObj.getTableAlias(), y.getOriginName()); + } else { + originField = String.format(PrestoConstants.KEYWORD_FIX, tableObj.getTableAlias(), y.getOriginName()); + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_Y_PREFIX, i); + // 处理纵轴字段 + yFields.add(getYFields(y, originField, fieldAlias)); + // 处理纵轴过滤 + yWheres.add(getYWheres(y, originField, fieldAlias)); + // 处理纵轴排序 + if (StringUtils.isNotEmpty(y.getSort()) && !StringUtils.equalsIgnoreCase(y.getSort(), "none")) { + yOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(y.getSort()) + .build()); + } + } + } + // 处理视图中字段过滤 + String customWheres = transCustomFilterList(tableObj, fieldCustomFilter); + // 处理仪表板字段过滤 + String extWheres = transExtFilterList(tableObj, extFilterRequestList); + // row permissions tree + String whereTrees = transFilterTrees(tableObj, rowPermissionsTree); + // 构建sql所有参数 + List fields = new ArrayList<>(); + fields.addAll(xFields); + fields.addAll(yFields); + List wheres = new ArrayList<>(); + if (customWheres != null) wheres.add(customWheres); + if (extWheres != null) wheres.add(extWheres); + if (whereTrees != null) wheres.add(whereTrees); + List groups = new ArrayList<>(); + groups.addAll(xFields); + // 外层再次套sql + List orders = new ArrayList<>(); + orders.addAll(xOrders); + orders.addAll(yOrders); + List aggWheres = new ArrayList<>(); + aggWheres.addAll(yWheres.stream().filter(ObjectUtils::isNotEmpty).collect(Collectors.toList())); + + STGroup stg = new STGroupFile(SQLConstants.SQL_TEMPLATE); + ST st_sql = stg.getInstanceOf("querySql"); + if (CollectionUtils.isNotEmpty(xFields)) st_sql.add("groups", xFields); + if (CollectionUtils.isNotEmpty(yFields)) st_sql.add("aggregators", yFields); + if (CollectionUtils.isNotEmpty(wheres)) st_sql.add("filters", wheres); + if (ObjectUtils.isNotEmpty(tableObj)) st_sql.add("table", tableObj); + String sql = st_sql.render(); + + ST st = stg.getInstanceOf("querySql"); + SQLObj tableSQL = SQLObj.builder() + .tableName(String.format(PrestoConstants.BRACKETS, sql)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 1)) + .build(); + if (CollectionUtils.isNotEmpty(aggWheres)) st.add("filters", aggWheres); + if (CollectionUtils.isNotEmpty(orders)) st.add("orders", orders); + if (ObjectUtils.isNotEmpty(tableSQL)) st.add("table", tableSQL); + return sqlLimit(st.render(), view); + } + + @Override + public String getSQLAsTmpScatter(String table, List xAxis, List yAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, List extBubble, ChartViewWithBLOBs view) { + return getSQLScatter("(" + sqlFix(table) + ")", xAxis, yAxis, fieldCustomFilter, rowPermissionsTree, extFilterRequestList, extBubble, null, view); + } + + @Override + public String searchTable(String table) { + return "SELECT table_name FROM information_schema.TABLES WHERE table_name ='" + table + "'"; + } + + @Override + public String getSQLSummary(String table, List yAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, ChartViewWithBLOBs view, Datasource ds) { + // 字段汇总 排序等 + SQLObj tableObj = SQLObj.builder() + .tableName((table.startsWith("(") && table.endsWith(")")) ? table : String.format(PrestoConstants.KEYWORD_TABLE, table)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 0)) + .build(); + setSchema(tableObj, ds); + List yFields = new ArrayList<>(); + List yWheres = new ArrayList<>(); + List yOrders = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(yAxis)) { + for (int i = 0; i < yAxis.size(); i++) { + ChartViewFieldDTO y = yAxis.get(i); + String originField; + if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originField = calcFieldRegex(y.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(y.getExtField()) && y.getExtField() == 1) { + originField = String.format(PrestoConstants.KEYWORD_FIX, tableObj.getTableAlias(), y.getOriginName()); + } else { + originField = String.format(PrestoConstants.KEYWORD_FIX, tableObj.getTableAlias(), y.getOriginName()); + } + String fieldAlias = String.format(SQLConstants.FIELD_ALIAS_Y_PREFIX, i); + // 处理纵轴字段 + yFields.add(getYFields(y, originField, fieldAlias)); + // 处理纵轴过滤 + yWheres.add(getYWheres(y, originField, fieldAlias)); + // 处理纵轴排序 + if (StringUtils.isNotEmpty(y.getSort()) && !StringUtils.equalsIgnoreCase(y.getSort(), "none")) { + yOrders.add(SQLObj.builder() + .orderField(originField) + .orderAlias(fieldAlias) + .orderDirection(y.getSort()) + .build()); + } + } + } + // 处理视图中字段过滤 + String customWheres = transCustomFilterList(tableObj, fieldCustomFilter); + // 处理仪表板字段过滤 + String extWheres = transExtFilterList(tableObj, extFilterRequestList); + // row permissions tree + String whereTrees = transFilterTrees(tableObj, rowPermissionsTree); + // 构建sql所有参数 + List fields = new ArrayList<>(); + fields.addAll(yFields); + List wheres = new ArrayList<>(); + if (customWheres != null) wheres.add(customWheres); + if (extWheres != null) wheres.add(extWheres); + if (whereTrees != null) wheres.add(whereTrees); + List groups = new ArrayList<>(); + // 外层再次套sql + List orders = new ArrayList<>(); + orders.addAll(yOrders); + List aggWheres = new ArrayList<>(); + aggWheres.addAll(yWheres.stream().filter(ObjectUtils::isNotEmpty).collect(Collectors.toList())); + + STGroup stg = new STGroupFile(SQLConstants.SQL_TEMPLATE); + ST st_sql = stg.getInstanceOf("querySql"); + if (CollectionUtils.isNotEmpty(yFields)) st_sql.add("aggregators", yFields); + if (CollectionUtils.isNotEmpty(wheres)) st_sql.add("filters", wheres); + if (ObjectUtils.isNotEmpty(tableObj)) st_sql.add("table", tableObj); + String sql = st_sql.render(); + + ST st = stg.getInstanceOf("querySql"); + SQLObj tableSQL = SQLObj.builder() + .tableName(String.format(PrestoConstants.BRACKETS, sql)) + .tableAlias(String.format(TABLE_ALIAS_PREFIX, 1)) + .build(); + if (CollectionUtils.isNotEmpty(aggWheres)) st.add("filters", aggWheres); + if (CollectionUtils.isNotEmpty(orders)) st.add("orders", orders); + if (ObjectUtils.isNotEmpty(tableSQL)) st.add("table", tableSQL); + return sqlLimit(st.render(), view); + } + + @Override + public String getSQLSummaryAsTmp(String sql, List yAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, ChartViewWithBLOBs view) { + return getSQLSummary("(" + sqlFix(sql) + ")", yAxis, fieldCustomFilter, rowPermissionsTree, extFilterRequestList, view, null); + } + + @Override + public String wrapSql(String sql) { + sql = sql.trim(); + if (sql.lastIndexOf(";") == (sql.length() - 1)) { + sql = sql.substring(0, sql.length() - 1); + } + String tmpSql = "SELECT * FROM (" + sql + ") AS tmp " + " LIMIT 0 "; + return tmpSql; + } + + @Override + public String createRawQuerySQL(String table, List fields, Datasource ds) { + String[] array = fields.stream().map(f -> { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("\"").append(f.getOriginName()).append("\" AS ").append(f.getDataeaseName()); + return stringBuilder.toString(); + }).toArray(String[]::new); + if (ds != null) { + String schema = new Gson().fromJson(ds.getConfiguration(), JdbcConfiguration.class).getSchema(); + String tableWithSchema = String.format(SqlServerSQLConstants.KEYWORD_TABLE, schema) + "." + String.format(SqlServerSQLConstants.KEYWORD_TABLE, table); + return MessageFormat.format("SELECT {0} FROM {1} ", StringUtils.join(array, ","), tableWithSchema); + } else { + return MessageFormat.format("SELECT {0} FROM {1} ", StringUtils.join(array, ","), table); + } + } + + @Override + public String createRawQuerySQLAsTmp(String sql, List fields) { + return createRawQuerySQL(" (" + sqlFix(sql) + ") AS tmp ", fields, null); + } + + @Override + public String transTreeItem(SQLObj tableObj, DatasetRowPermissionsTreeItem item) { + String res = null; + DatasetTableField field = item.getField(); + if (ObjectUtils.isEmpty(field)) { + return null; + } + String whereName = ""; + String originName; + if (ObjectUtils.isNotEmpty(field.getExtField()) && field.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originName = calcFieldRegex(field.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(field.getExtField()) && field.getExtField() == 1) { + originName = String.format(PrestoConstants.KEYWORD_FIX, tableObj.getTableAlias(), field.getOriginName()); + } else { + originName = String.format(PrestoConstants.KEYWORD_FIX, tableObj.getTableAlias(), field.getOriginName()); + } + if (field.getDeType() == 1) { + if (field.getDeExtractType() == 0 || field.getDeExtractType() == 5) { + whereName = String.format(PrestoConstants.CAST, originName, "timestamp"); + } + if (field.getDeExtractType() == 2 || field.getDeExtractType() == 3 || field.getDeExtractType() == 4) { + String cast = String.format(PrestoConstants.CAST, originName, "bigint"); + whereName = String.format(PrestoConstants.FROM_UNIXTIME, cast); + } + if (field.getDeExtractType() == 1) { + whereName = originName; + } + } else if (field.getDeType() == 2 || field.getDeType() == 3) { + if (field.getDeExtractType() == 0 || field.getDeExtractType() == 5) { + whereName = String.format(PrestoConstants.CAST, originName, PrestoConstants.DEFAULT_FLOAT_FORMAT); + } + if (field.getDeExtractType() == 1) { + whereName = String.format(PrestoConstants.UNIX_TIMESTAMP, originName); + } + if (field.getDeExtractType() == 2 || field.getDeExtractType() == 3 || field.getDeExtractType() == 4) { + whereName = originName; + } + } else { + whereName = originName; + } + + if (StringUtils.equalsIgnoreCase(item.getFilterType(), "enum")) { + if (CollectionUtils.isNotEmpty(item.getEnumValue())) { + res = "(" + whereName + " IN ('" + String.join("','", item.getEnumValue()) + "'))"; + } + } else { + String value = item.getValue(); + String whereTerm = transMysqlFilterTerm(item.getTerm()); + String whereValue = ""; + + if (StringUtils.equalsIgnoreCase(item.getTerm(), "null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(item.getTerm(), "not_null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(item.getTerm(), "empty")) { + whereValue = "''"; + } else if (StringUtils.equalsIgnoreCase(item.getTerm(), "not_empty")) { + whereValue = "''"; + } else if (StringUtils.containsIgnoreCase(item.getTerm(), "in") || StringUtils.containsIgnoreCase(item.getTerm(), "not in")) { + whereValue = "('" + String.join("','", value.split(",")) + "')"; + } else if (StringUtils.containsIgnoreCase(item.getTerm(), "like")) { + whereValue = "'%" + value + "%'"; + } else { + whereValue = String.format(PrestoConstants.WHERE_VALUE_VALUE, value); + } + SQLObj build = SQLObj.builder() + .whereField(whereName) + .whereTermAndValue(whereTerm + whereValue) + .build(); + res = build.getWhereField() + " " + build.getWhereTermAndValue(); + } + return res; + } + + @Override + public String convertTableToSql(String tableName, Datasource ds) { + String schema = new Gson().fromJson(ds.getConfiguration(), JdbcConfiguration.class).getSchema(); + schema = String.format(PrestoConstants.KEYWORD_TABLE, schema); + return createSQLPreview("SELECT * FROM " + schema + "." + String.format(PrestoConstants.KEYWORD_TABLE, tableName), null); + } + + public String transMysqlFilterTerm(String term) { + switch (term) { + case "eq": + return " = "; + case "not_eq": + return " <> "; + case "lt": + return " < "; + case "le": + return " <= "; + case "gt": + return " > "; + case "ge": + return " >= "; + case "in": + return " IN "; + case "not in": + return " NOT IN "; + case "like": + return " LIKE "; + case "not like": + return " NOT LIKE "; + case "null": + return " IS NULL "; + case "not_null": + return " IS NOT NULL "; + case "empty": + return " = "; + case "not_empty": + return " <> "; + case "between": + return " BETWEEN "; + default: + return ""; + } + } + + public String transCustomFilterList(SQLObj tableObj, List requestList) { + if (CollectionUtils.isEmpty(requestList)) { + return null; + } + List res = new ArrayList<>(); + for (ChartFieldCustomFilterDTO request : requestList) { + List list = new ArrayList<>(); + DatasetTableField field = request.getField(); + + if (ObjectUtils.isEmpty(field)) { + continue; + } + String whereName = ""; + String originName; + if (ObjectUtils.isNotEmpty(field.getExtField()) && field.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originName = calcFieldRegex(field.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(field.getExtField()) && field.getExtField() == 1) { + originName = String.format(PrestoConstants.KEYWORD_FIX, tableObj.getTableAlias(), field.getOriginName()); + } else { + originName = String.format(PrestoConstants.KEYWORD_FIX, tableObj.getTableAlias(), field.getOriginName()); + } + if (field.getDeType() == 1) { + if (field.getDeExtractType() == 0 || field.getDeExtractType() == 5) { + whereName = String.format(PrestoConstants.date_parse, originName, StringUtils.isNotEmpty(field.getDateFormat()) ? field.getDateFormat() : PrestoConstants.DEFAULT_DATE_FORMAT); + } + if (field.getDeExtractType() == 2 || field.getDeExtractType() == 3 || field.getDeExtractType() == 4) { + String cast = String.format(PrestoConstants.CAST, originName, "bigint"); + whereName = String.format(PrestoConstants.FROM_UNIXTIME, cast); + } + if (field.getDeExtractType() == 1) { + whereName = originName; + } + } else if (field.getDeType() == 2 || field.getDeType() == 3) { + if (field.getDeExtractType() == 0 || field.getDeExtractType() == 5) { + whereName = String.format(PrestoConstants.CAST, originName, PrestoConstants.DEFAULT_FLOAT_FORMAT); + } + if (field.getDeExtractType() == 1) { + whereName = String.format(PrestoConstants.UNIX_TIMESTAMP, originName); + } + if (field.getDeExtractType() == 2 || field.getDeExtractType() == 3 || field.getDeExtractType() == 4) { + whereName = originName; + } + } else { + whereName = originName; + } + + if (StringUtils.equalsIgnoreCase(request.getFilterType(), "enum")) { + if (CollectionUtils.isNotEmpty(request.getEnumCheckField())) { + res.add("(" + whereName + " IN ('" + String.join("','", request.getEnumCheckField()) + "'))"); + } + } else { + List filter = request.getFilter(); + for (ChartCustomFilterItemDTO filterItemDTO : filter) { + String value = filterItemDTO.getValue(); + String whereTerm = transMysqlFilterTerm(filterItemDTO.getTerm()); + String whereValue = ""; + + if (StringUtils.equalsIgnoreCase(filterItemDTO.getTerm(), "null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(filterItemDTO.getTerm(), "not_null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(filterItemDTO.getTerm(), "empty")) { + whereValue = "''"; + } else if (StringUtils.equalsIgnoreCase(filterItemDTO.getTerm(), "not_empty")) { + whereValue = "''"; + } else if (StringUtils.containsIgnoreCase(filterItemDTO.getTerm(), "in") || StringUtils.containsIgnoreCase(filterItemDTO.getTerm(), "not in")) { + whereValue = "('" + String.join("','", value.split(",")) + "')"; + } else if (StringUtils.containsIgnoreCase(filterItemDTO.getTerm(), "like")) { + whereValue = "'%" + value + "%'"; + } else { + whereValue = String.format(PrestoConstants.WHERE_VALUE_VALUE, value); + } + list.add(SQLObj.builder() + .whereField(whereName) + .whereTermAndValue(whereTerm + whereValue) + .build()); + } + + List strList = new ArrayList<>(); + list.forEach(ele -> strList.add(ele.getWhereField() + " " + ele.getWhereTermAndValue())); + if (CollectionUtils.isNotEmpty(list)) { + res.add("(" + String.join(" " + getLogic(request.getLogic()) + " ", strList) + ")"); + } + } + } + return CollectionUtils.isNotEmpty(res) ? "(" + String.join(" AND ", res) + ")" : null; + } + + public String transExtFilterList(SQLObj tableObj, List requestList) { + if (CollectionUtils.isEmpty(requestList)) { + return null; + } + List list = new ArrayList<>(); + for (ChartExtFilterRequest request : requestList) { + List value = request.getValue(); + + List whereNameList = new ArrayList<>(); + List fieldList = new ArrayList<>(); + if (request.getIsTree()) { + fieldList.addAll(request.getDatasetTableFieldList()); + } else { + fieldList.add(request.getDatasetTableField()); + } + + for (DatasetTableField field : fieldList) { + if (CollectionUtils.isEmpty(value) || ObjectUtils.isEmpty(field)) { + continue; + } + String whereName = ""; + + String originName; + if (ObjectUtils.isNotEmpty(field.getExtField()) && field.getExtField() == 2) { + // 解析origin name中有关联的字段生成sql表达式 + originName = calcFieldRegex(field.getOriginName(), tableObj); + } else if (ObjectUtils.isNotEmpty(field.getExtField()) && field.getExtField() == 1) { + originName = String.format(PrestoConstants.KEYWORD_FIX, tableObj.getTableAlias(), field.getOriginName()); + } else { + originName = String.format(PrestoConstants.KEYWORD_FIX, tableObj.getTableAlias(), field.getOriginName()); + } + + if (field.getDeType() == 1) { + if (field.getDeExtractType() == 0 || field.getDeExtractType() == 5) { + whereName = String.format(PrestoConstants.date_parse, originName, StringUtils.isNotEmpty(field.getDateFormat()) ? field.getDateFormat() : PrestoConstants.DEFAULT_DATE_FORMAT); + } + if (field.getDeExtractType() == 2 || field.getDeExtractType() == 3 || field.getDeExtractType() == 4) { + String cast = String.format(PrestoConstants.CAST, originName, "bigint"); + whereName = String.format(PrestoConstants.FROM_UNIXTIME, cast); + } + if (field.getDeExtractType() == 1) { + whereName = originName; + } + } else if (field.getDeType() == 2 || field.getDeType() == 3) { + if (field.getDeExtractType() == 0 || field.getDeExtractType() == 5) { + whereName = String.format(PrestoConstants.CAST, originName, PrestoConstants.DEFAULT_FLOAT_FORMAT); + } + if (field.getDeExtractType() == 1) { + whereName = String.format(PrestoConstants.UNIX_TIMESTAMP, originName); + } + if (field.getDeExtractType() == 2 || field.getDeExtractType() == 3 || field.getDeExtractType() == 4) { + whereName = originName; + } + } else { + whereName = originName; + } + whereNameList.add(whereName); + } + + String whereName = ""; + if (request.getIsTree()) { + whereName = "CONCAT(" + StringUtils.join(whereNameList, ",',',") + ")"; + } else { + whereName = whereNameList.get(0); + } + String whereTerm = transMysqlFilterTerm(request.getOperator()); + String whereValue = ""; + + if (StringUtils.containsIgnoreCase(request.getOperator(), "in")) { + whereValue = "('" + StringUtils.join(value, "','") + "')"; + } else if (StringUtils.containsIgnoreCase(request.getOperator(), "like")) { + String keyword = value.get(0).toUpperCase(); + whereValue = "'%" + keyword + "%'"; + whereName = "upper(" + whereName + ")"; + } else if (StringUtils.containsIgnoreCase(request.getOperator(), "between")) { + if (request.getDatasetTableField().getDeType() == 1) { + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String startTime = simpleDateFormat.format(new Date(Long.parseLong(value.get(0)))); + String endTime = simpleDateFormat.format(new Date(Long.parseLong(value.get(1)))); + whereValue = String.format(PrestoConstants.WHERE_BETWEEN, startTime, endTime); + } else { + whereValue = String.format(PrestoConstants.WHERE_BETWEEN, value.get(0), value.get(1)); + } + } else { + whereValue = String.format(PrestoConstants.WHERE_VALUE_VALUE, value.get(0)); + } + list.add(SQLObj.builder() + .whereField(whereName) + .whereTermAndValue(whereTerm + whereValue) + .build()); + } + List strList = new ArrayList<>(); + list.forEach(ele -> strList.add(ele.getWhereField() + " " + ele.getWhereTermAndValue())); + return CollectionUtils.isNotEmpty(list) ? "(" + String.join(" AND ", strList) + ")" : null; + } + + private String sqlFix(String sql) { + if (sql.lastIndexOf(";") == (sql.length() - 1)) { + sql = sql.substring(0, sql.length() - 1); + } + return sql; + } + + private String transDateFormat(String dateStyle, String datePattern) { + String split = "-"; + if (StringUtils.equalsIgnoreCase(datePattern, "date_sub")) { + split = "-"; + } else if (StringUtils.equalsIgnoreCase(datePattern, "date_split")) { + split = "/"; + } else { + split = "-"; + } + + if (StringUtils.isEmpty(dateStyle)) { + return "yyyy-MM-dd HH:mm:ss"; + } + + switch (dateStyle) { + case "y": + return "yyyy"; + case "y_M": + return "yyyy" + split + "MM"; + case "y_M_d": + return "yyyy" + split + "MM" + split + "dd"; + case "H_m_s": + return "hh:mi:ss"; + case "y_M_d_H_m": + return "yyyy" + split + "MM" + split + "dd" + " HH:mm"; + case "y_M_d_H_m_s": + return "yyyy" + split + "MM" + split + "dd" + " HH:mm:ss"; + default: + return "yyyy-MM-dd HH:mm:s"; + } + } + + private SQLObj getXFields(ChartViewFieldDTO x, String originField, String fieldAlias) { + String fieldName = ""; + if (x.getDeExtractType() == DeTypeConstants.DE_TIME) { + if (x.getDeType() == 2 || x.getDeType() == 3) { + fieldName = String.format(PrestoConstants.UNIX_TIMESTAMP, originField); + } else if (x.getDeType() == DeTypeConstants.DE_TIME) { + String format = transDateFormat(x.getDateStyle(), x.getDatePattern()); + fieldName = String.format(PrestoConstants.FORMAT_DATETIME, originField, format); + } else { + fieldName = originField; + } + } else { + if (x.getDeType() == DeTypeConstants.DE_TIME) { + String format = transDateFormat(x.getDateStyle(), x.getDatePattern()); + if (x.getDeExtractType() == DeTypeConstants.DE_STRING) { + fieldName = String.format(PrestoConstants.FORMAT_DATETIME, String.format(PrestoConstants.date_parse, originField, StringUtils.isNotEmpty(x.getDateFormat()) ? x.getDateFormat() : PrestoConstants.DEFAULT_DATE_FORMAT), format); + } else { + String from_unixtime = String.format(PrestoConstants.FROM_UNIXTIME, originField + "/1000"); + fieldName = String.format(PrestoConstants.FORMAT_DATETIME, from_unixtime, format); + } + } else { + if (x.getDeType() == DeTypeConstants.DE_INT) { + fieldName = String.format(PrestoConstants.CAST, originField, PrestoConstants.DEFAULT_INT_FORMAT); + } else if (x.getDeType() == DeTypeConstants.DE_FLOAT) { + fieldName = String.format(PrestoConstants.CAST, originField, PrestoConstants.DEFAULT_FLOAT_FORMAT); + } else { + fieldName = originField; + } + } + } + return SQLObj.builder() + .fieldName(fieldName) + .fieldAlias(fieldAlias) + .build(); + } + + private SQLObj getYFields(ChartViewFieldDTO y, String originField, String fieldAlias) { + String fieldName = ""; + if (StringUtils.equalsIgnoreCase(y.getOriginName(), "*")) { + fieldName = PrestoConstants.AGG_COUNT; + } else if (SQLConstants.DIMENSION_TYPE.contains(y.getDeType())) { + fieldName = String.format(PrestoConstants.AGG_FIELD, y.getSummary(), originField); + } else { + if (StringUtils.equalsIgnoreCase(y.getSummary(), "avg") || StringUtils.containsIgnoreCase(y.getSummary(), "pop")) { + String cast = String.format(PrestoConstants.CAST, originField, y.getDeType() == DeTypeConstants.DE_INT ? PrestoConstants.DEFAULT_INT_FORMAT : PrestoConstants.DEFAULT_FLOAT_FORMAT); + String agg = String.format(PrestoConstants.AGG_FIELD, y.getSummary(), cast); + fieldName = String.format(PrestoConstants.CAST, agg, PrestoConstants.DEFAULT_FLOAT_FORMAT); + } else { + String cast = String.format(PrestoConstants.CAST, originField, y.getDeType() == DeTypeConstants.DE_INT ? PrestoConstants.DEFAULT_INT_FORMAT : PrestoConstants.DEFAULT_FLOAT_FORMAT); + fieldName = String.format(PrestoConstants.AGG_FIELD, y.getSummary(), cast); + } + } + return SQLObj.builder() + .fieldName(fieldName) + .fieldAlias(fieldAlias) + .build(); + } + + private String getYWheres(ChartViewFieldDTO y, String originField, String fieldAlias) { + List list = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(y.getFilter()) && y.getFilter().size() > 0) { + y.getFilter().forEach(f -> { + String whereTerm = transMysqlFilterTerm(f.getTerm()); + String whereValue = ""; + // 原始类型不是时间,在de中被转成时间的字段做处理 + if (StringUtils.equalsIgnoreCase(f.getTerm(), "null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(f.getTerm(), "not_null")) { + whereValue = ""; + } else if (StringUtils.equalsIgnoreCase(f.getTerm(), "empty")) { + whereValue = "''"; + } else if (StringUtils.equalsIgnoreCase(f.getTerm(), "not_empty")) { + whereValue = "''"; + } else if (StringUtils.containsIgnoreCase(f.getTerm(), "in")) { + whereValue = "('" + StringUtils.join(f.getValue(), "','") + "')"; + } else if (StringUtils.containsIgnoreCase(f.getTerm(), "like")) { + whereValue = "'%" + f.getValue() + "%'"; + } else { + whereValue = String.format(PrestoConstants.WHERE_VALUE_VALUE, f.getValue()); + } + list.add(SQLObj.builder() + .whereField(fieldAlias) + .whereAlias(fieldAlias) + .whereTermAndValue(whereTerm + whereValue) + .build()); + }); + } + List strList = new ArrayList<>(); + list.forEach(ele -> strList.add(ele.getWhereField() + " " + ele.getWhereTermAndValue())); + return CollectionUtils.isNotEmpty(list) ? "(" + String.join(" " + getLogic(y.getLogic()) + " ", strList) + ")" : null; + } + + private String calcFieldRegex(String originField, SQLObj tableObj) { + originField = originField.replaceAll("[\\t\\n\\r]]", ""); + // 正则提取[xxx] + String regex = "\\[(.*?)]"; + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(originField); + Set ids = new HashSet<>(); + while (matcher.find()) { + String id = matcher.group(1); + ids.add(id); + } + if (CollectionUtils.isEmpty(ids)) { + return originField; + } + DatasetTableFieldExample datasetTableFieldExample = new DatasetTableFieldExample(); + datasetTableFieldExample.createCriteria().andIdIn(new ArrayList<>(ids)); + List calcFields = datasetTableFieldMapper.selectByExample(datasetTableFieldExample); + for (DatasetTableField ele : calcFields) { + originField = originField.replaceAll("\\[" + ele.getId() + "]", + String.format(PrestoConstants.KEYWORD_FIX, tableObj.getTableAlias(), ele.getOriginName())); + } + return originField; + } + + private String sqlLimit(String sql, ChartViewWithBLOBs view) { + if (StringUtils.equalsIgnoreCase(view.getResultMode(), "custom")) { + return sql + " LIMIT " + view.getResultCount(); + } else { + return sql; + } + } + + public void setSchema(SQLObj tableObj, Datasource ds) { + if (ds != null && !tableObj.getTableName().startsWith("(") && !tableObj.getTableName().endsWith(")")) { + PrestoConfig prestoConfig = json.fromJson(ds.getConfiguration(), PrestoConfig.class); + String schema = prestoConfig.getSchema(); + String database = prestoConfig.getDataBase(); + tableObj.setTableName(database + "." + schema + "." + tableObj.getTableName()); + } + } + + public List dateformat() { + ObjectMapper objectMapper = new ObjectMapper(); + List dateformats = new ArrayList<>(); + try{ + dateformats = objectMapper.readValue("[\n" + + "{\"dateformat\": \"yyyy-MM-dd\"},\n" + + "{\"dateformat\": \"yyyy/MM/dd\"},\n" + + "{\"dateformat\": \"YyyyyMMdd\"},\n" + + "{\"dateformat\": \"yyyy-MM-dd HH:mm:s\"},\n" + + "{\"dateformat\": \"yyyy-MM-dd HH:mm:s\"},\n" + + "{\"dateformat\": \"yyyy-MM-dd HH:mm:s\"}\n" + + "]", new TypeReference>() {} ); + }catch (Exception e){} + return dateformats; + } + + public String getResultCount(boolean isTable, String sql, List xAxis, List fieldCustomFilter, List rowPermissionsTree, List extFilterRequestList, Datasource ds, ChartViewWithBLOBs view) { + return null; + } +} diff --git a/extensions/dataease-extensions-datasource/presto/presto-backend/src/main/java/io/dataease/plugins/datasource/dm/service/PrestoService.java b/extensions/dataease-extensions-datasource/presto/presto-backend/src/main/java/io/dataease/plugins/datasource/dm/service/PrestoService.java new file mode 100644 index 0000000000..4bc58d98a0 --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-backend/src/main/java/io/dataease/plugins/datasource/dm/service/PrestoService.java @@ -0,0 +1,54 @@ +package io.dataease.plugins.datasource.dm.service; + +import io.dataease.plugins.common.constants.DatabaseClassification; +import io.dataease.plugins.common.constants.DatasourceCalculationMode; +import io.dataease.plugins.common.dto.StaticResource; +import io.dataease.plugins.common.dto.datasource.DataSourceType; +import io.dataease.plugins.datasource.service.DatasourceService; +import org.springframework.stereotype.Service; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +@Service +public class PrestoService extends DatasourceService { + + + @Override + public List components() { + List result = new ArrayList<>(); + result.add("presto"); + return result; + } + @Override + protected InputStream readContent(String s) { + InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("static/" + s); + return resourceAsStream; + } + + @Override + public List staticResources() { + List results = new ArrayList<>(); + StaticResource staticResource = new StaticResource(); + staticResource.setName("presto"); + staticResource.setSuffix("jpg"); + results.add(staticResource); + results.add(pluginSvg()); + return results; + } + + @Override + public DataSourceType getDataSourceType() { + DataSourceType dataSourceType = new DataSourceType("presto", "Presto" , true , "", DatasourceCalculationMode.DIRECT, true); + dataSourceType.setDatabaseClassification(DatabaseClassification.OLAP); + return dataSourceType; + } + + private StaticResource pluginSvg() { + StaticResource staticResource = new StaticResource(); + staticResource.setName("presto-backend"); + staticResource.setSuffix("svg"); + return staticResource; + } +} diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/.babelrc b/extensions/dataease-extensions-datasource/presto/presto-frontend/.babelrc new file mode 100644 index 0000000000..3a280ba34b --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-frontend/.babelrc @@ -0,0 +1,12 @@ +{ + "presets": [ + ["env", { + "modules": false, + "targets": { + "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] + } + }], + "stage-2" + ], + "plugins": ["transform-vue-jsx", "transform-runtime"] +} diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/.editorconfig b/extensions/dataease-extensions-datasource/presto/presto-frontend/.editorconfig new file mode 100644 index 0000000000..9d08a1a828 --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-frontend/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/.gitignore b/extensions/dataease-extensions-datasource/presto/presto-frontend/.gitignore new file mode 100644 index 0000000000..541a820f6c --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-frontend/.gitignore @@ -0,0 +1,14 @@ +.DS_Store +node_modules/ +/dist/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/.postcssrc.js b/extensions/dataease-extensions-datasource/presto/presto-frontend/.postcssrc.js new file mode 100644 index 0000000000..eee3e92d7f --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-frontend/.postcssrc.js @@ -0,0 +1,10 @@ +// https://github.com/michael-ciniawsky/postcss-load-config + +module.exports = { + "plugins": { + "postcss-import": {}, + "postcss-url": {}, + // to edit target browsers: use "browserslist" field in package.json + "autoprefixer": {} + } +} diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/README.md b/extensions/dataease-extensions-datasource/presto/presto-frontend/README.md new file mode 100644 index 0000000000..c1e4be95e0 --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-frontend/README.md @@ -0,0 +1,21 @@ +# deplugin-view-frontend + +> A Vue.js project + +## Build Setup + +``` bash +# install dependencies +npm install + +# serve with hot reload at localhost:8080 +npm run dev + +# build for production with minification +npm run build + +# build for production and view the bundle analyzer report +npm run build --report +``` + +For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader). diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/build/build-async-plugins.js b/extensions/dataease-extensions-datasource/presto/presto-frontend/build/build-async-plugins.js new file mode 100644 index 0000000000..2303854531 --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-frontend/build/build-async-plugins.js @@ -0,0 +1,35 @@ +'use strict' +require('./check-versions')() + +process.env.NODE_ENV = 'production' + +const ora = require('ora') +const chalk = require('chalk') +const webpack = require('webpack') +const webpackConfig = require('./webpack.async-plugins') + +const spinner = ora('building for sync-plugins...') +spinner.start() + +webpack(webpackConfig, function (err, stats) { + spinner.stop() + if (err) throw err + process.stdout.write(stats.toString({ + colors: true, + modules: false, + children: false, + chunks: false, + chunkModules: false + }) + '\n\n') + + if (stats.hasErrors()) { + console.log(chalk.red(' Build failed with errors.\n')) + process.exit(1) + } + + console.log(chalk.cyan(' Build complete.\n')) + console.log(chalk.yellow( + ' Tip: built files are meant to be served over an HTTP server.\n' + + ' Opening index.html over file:// won\'t work.\n' + )) +}) diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/build/build.js b/extensions/dataease-extensions-datasource/presto/presto-frontend/build/build.js new file mode 100644 index 0000000000..8f2ad8ad49 --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-frontend/build/build.js @@ -0,0 +1,41 @@ +'use strict' +require('./check-versions')() + +process.env.NODE_ENV = 'production' + +const ora = require('ora') +const rm = require('rimraf') +const path = require('path') +const chalk = require('chalk') +const webpack = require('webpack') +const config = require('../config') +const webpackConfig = require('./webpack.prod.conf') + +const spinner = ora('building for production...') +spinner.start() + +rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { + if (err) throw err + webpack(webpackConfig, (err, stats) => { + spinner.stop() + if (err) throw err + process.stdout.write(stats.toString({ + colors: true, + modules: false, + children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build. + chunks: false, + chunkModules: false + }) + '\n\n') + + if (stats.hasErrors()) { + console.log(chalk.red(' Build failed with errors.\n')) + process.exit(1) + } + + console.log(chalk.cyan(' Build complete.\n')) + console.log(chalk.yellow( + ' Tip: built files are meant to be served over an HTTP server.\n' + + ' Opening index.html over file:// won\'t work.\n' + )) + }) +}) diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/build/check-versions.js b/extensions/dataease-extensions-datasource/presto/presto-frontend/build/check-versions.js new file mode 100644 index 0000000000..3ef972a08d --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-frontend/build/check-versions.js @@ -0,0 +1,54 @@ +'use strict' +const chalk = require('chalk') +const semver = require('semver') +const packageConfig = require('../package.json') +const shell = require('shelljs') + +function exec (cmd) { + return require('child_process').execSync(cmd).toString().trim() +} + +const versionRequirements = [ + { + name: 'node', + currentVersion: semver.clean(process.version), + versionRequirement: packageConfig.engines.node + } +] + +if (shell.which('npm')) { + versionRequirements.push({ + name: 'npm', + currentVersion: exec('npm --version'), + versionRequirement: packageConfig.engines.npm + }) +} + +module.exports = function () { + const warnings = [] + + for (let i = 0; i < versionRequirements.length; i++) { + const mod = versionRequirements[i] + + if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { + warnings.push(mod.name + ': ' + + chalk.red(mod.currentVersion) + ' should be ' + + chalk.green(mod.versionRequirement) + ) + } + } + + if (warnings.length) { + console.log('') + console.log(chalk.yellow('To use this template, you must update following to modules:')) + console.log() + + for (let i = 0; i < warnings.length; i++) { + const warning = warnings[i] + console.log(' ' + warning) + } + + console.log() + process.exit(1) + } +} diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/build/logo.png b/extensions/dataease-extensions-datasource/presto/presto-frontend/build/logo.png new file mode 100644 index 0000000000..f3d2503fc2 Binary files /dev/null and b/extensions/dataease-extensions-datasource/presto/presto-frontend/build/logo.png differ diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/build/utils.js b/extensions/dataease-extensions-datasource/presto/presto-frontend/build/utils.js new file mode 100644 index 0000000000..e534fb0fd6 --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-frontend/build/utils.js @@ -0,0 +1,101 @@ +'use strict' +const path = require('path') +const config = require('../config') +const ExtractTextPlugin = require('extract-text-webpack-plugin') +const packageConfig = require('../package.json') + +exports.assetsPath = function (_path) { + const assetsSubDirectory = process.env.NODE_ENV === 'production' + ? config.build.assetsSubDirectory + : config.dev.assetsSubDirectory + + return path.posix.join(assetsSubDirectory, _path) +} + +exports.cssLoaders = function (options) { + options = options || {} + + const cssLoader = { + loader: 'css-loader', + options: { + sourceMap: options.sourceMap + } + } + + const postcssLoader = { + loader: 'postcss-loader', + options: { + sourceMap: options.sourceMap + } + } + + // generate loader string to be used with extract text plugin + function generateLoaders (loader, loaderOptions) { + const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader] + + if (loader) { + loaders.push({ + loader: loader + '-loader', + options: Object.assign({}, loaderOptions, { + sourceMap: options.sourceMap + }) + }) + } + + // Extract CSS when that option is specified + // (which is the case during production build) + if (options.extract) { + return ExtractTextPlugin.extract({ + use: loaders, + fallback: 'vue-style-loader' + }) + } else { + return ['vue-style-loader'].concat(loaders) + } + } + + // https://vue-loader.vuejs.org/en/configurations/extract-css.html + return { + css: generateLoaders(), + postcss: generateLoaders(), + less: generateLoaders('less'), + sass: generateLoaders('sass', { indentedSyntax: true }), + scss: generateLoaders('sass'), + stylus: generateLoaders('stylus'), + styl: generateLoaders('stylus') + } +} + +// Generate loaders for standalone style files (outside of .vue) +exports.styleLoaders = function (options) { + const output = [] + const loaders = exports.cssLoaders(options) + + for (const extension in loaders) { + const loader = loaders[extension] + output.push({ + test: new RegExp('\\.' + extension + '$'), + use: loader + }) + } + + return output +} + +exports.createNotifierCallback = () => { + const notifier = require('node-notifier') + + return (severity, errors) => { + if (severity !== 'error') return + + const error = errors[0] + const filename = error.file && error.file.split('!').pop() + + notifier.notify({ + title: packageConfig.name, + message: severity + ': ' + error.name, + subtitle: filename || '', + icon: path.join(__dirname, 'logo.png') + }) + } +} diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/build/vue-loader.conf.js b/extensions/dataease-extensions-datasource/presto/presto-frontend/build/vue-loader.conf.js new file mode 100644 index 0000000000..33ed58bc0a --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-frontend/build/vue-loader.conf.js @@ -0,0 +1,22 @@ +'use strict' +const utils = require('./utils') +const config = require('../config') +const isProduction = process.env.NODE_ENV === 'production' +const sourceMapEnabled = isProduction + ? config.build.productionSourceMap + : config.dev.cssSourceMap + +module.exports = { + loaders: utils.cssLoaders({ + sourceMap: sourceMapEnabled, + extract: isProduction + }), + cssSourceMap: sourceMapEnabled, + cacheBusting: config.dev.cacheBusting, + transformToRequire: { + video: ['src', 'poster'], + source: 'src', + img: 'src', + image: 'xlink:href' + } +} diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/build/webpack.async-plugins.js b/extensions/dataease-extensions-datasource/presto/presto-frontend/build/webpack.async-plugins.js new file mode 100644 index 0000000000..e021615a24 --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-frontend/build/webpack.async-plugins.js @@ -0,0 +1,91 @@ +const webpack = require('webpack') +const path = require('path') +const utils = require('./utils') +const CopyPlugin = require("copy-webpack-plugin"); +const VueLoaderPlugin = require('vue-loader/lib/plugin'); +function resolve (dir) { + return path.join(__dirname, '..', dir) +} + +module.exports = { + mode: 'development', + entry: { + 'presto': resolve('/src/views/presto.vue') + }, + output: { + path: resolve('/static/'), + filename: '[name].js' + }, + resolve: { + extensions: ['.js', '.vue', '.json'], + alias: { + 'vue$': 'vue/dist/vue.esm.js', + '@': resolve('src') + } + }, + externals: { + vue: 'vue' + }, + module: { + rules: [ + { + test: /\.vue$/, + loader: 'vue-loader', + options: { + transformAssetUrls: { + video: 'src', + source: 'src', + img: 'src', + image: 'xlink:href' + } + } + }, + { + test: /.(sa|sc|c)ss$/, + use: [ + {loader: 'vue-style-loader'}, + 'css-loader', + 'sass-loader' + ] + }, + { + test: /\.js$/, + loader: 'babel-loader', + include: [resolve('src'), resolve('test')] + }, + { + test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('img/[name].[hash:7].[ext]') + } + }, + { + test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('media/[name].[hash:7].[ext]') + } + }, + { + test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('fonts/[name].[hash:7].[ext]') + } + } + ] + }, + plugins: [ + new VueLoaderPlugin(), + new webpack.DefinePlugin({ + 'process.env.NODE_ENV': '"production"' + }), + new CopyPlugin([ + {from: 'src/icons/svg/'} + ]), + ] +} diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/build/webpack.base.conf.js b/extensions/dataease-extensions-datasource/presto/presto-frontend/build/webpack.base.conf.js new file mode 100644 index 0000000000..7ef785a39e --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-frontend/build/webpack.base.conf.js @@ -0,0 +1,103 @@ +'use strict' +const path = require('path') +const utils = require('./utils') +const config = require('../config') +const vueLoaderConfig = require('./vue-loader.conf') + +function resolve (dir) { + return path.join(__dirname, '..', dir) +} + + + +module.exports = { + context: path.resolve(__dirname, '../'), + entry: { + app: './src/main.js' + }, + output: { + path: config.build.assetsRoot, + filename: '[name].js', + publicPath: process.env.NODE_ENV === 'production' + ? config.build.assetsPublicPath + : config.dev.assetsPublicPath + }, + resolve: { + extensions: ['.js', '.vue', '.json'], + alias: { + 'vue$': 'vue/dist/vue.esm.js', + '@': resolve('src'), + } + }, + module: { + rules: [ + { + test: /\.vue$/, + loader: 'vue-loader', + options: vueLoaderConfig + }, + { + test: /\.js$/, + loader: 'babel-loader', + include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')] + }, + { + test: /\.svg$/, + loader: 'svg-sprite-loader', + include: [resolve('src/icons')], + options: { + symbolId: 'icon-[name]' + } + }, + { + test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, + loader: 'url-loader', + exclude: [resolve('src/icons')], + options: { + limit: 10000, + name: utils.assetsPath('img/[name].[hash:7].[ext]') + } + }, + { + test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('media/[name].[hash:7].[ext]') + } + }, + { + test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('fonts/[name].[hash:7].[ext]') + } + }, + { + test: /\.svg$/, + include: [path.resolve('src/icons')], + use: [ + { + loader: 'svg-sprite-loader', + options: { + symbolId: 'icon-[name]', + }, + } + ], + } + ] + }, + node: { + // prevent webpack from injecting useless setImmediate polyfill because Vue + // source contains it (although only uses it if it's native). + setImmediate: false, + // prevent webpack from injecting mocks to Node native modules + // that does not make sense for the client + dgram: 'empty', + fs: 'empty', + net: 'empty', + tls: 'empty', + child_process: 'empty' + } +} diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/build/webpack.dev.conf.js b/extensions/dataease-extensions-datasource/presto/presto-frontend/build/webpack.dev.conf.js new file mode 100755 index 0000000000..070ae221f3 --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-frontend/build/webpack.dev.conf.js @@ -0,0 +1,95 @@ +'use strict' +const utils = require('./utils') +const webpack = require('webpack') +const config = require('../config') +const merge = require('webpack-merge') +const path = require('path') +const baseWebpackConfig = require('./webpack.base.conf') +const CopyWebpackPlugin = require('copy-webpack-plugin') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') +const portfinder = require('portfinder') + +const HOST = process.env.HOST +const PORT = process.env.PORT && Number(process.env.PORT) + +const devWebpackConfig = merge(baseWebpackConfig, { + module: { + rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) + }, + // cheap-module-eval-source-map is faster for development + devtool: config.dev.devtool, + + // these devServer options should be customized in /config/index.js + devServer: { + clientLogLevel: 'warning', + historyApiFallback: { + rewrites: [ + { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') }, + ], + }, + hot: true, + contentBase: false, // since we use CopyWebpackPlugin. + compress: true, + host: HOST || config.dev.host, + port: PORT || config.dev.port, + open: config.dev.autoOpenBrowser, + overlay: config.dev.errorOverlay + ? { warnings: false, errors: true } + : false, + publicPath: config.dev.assetsPublicPath, + proxy: config.dev.proxyTable, + quiet: true, // necessary for FriendlyErrorsPlugin + watchOptions: { + poll: config.dev.poll, + } + }, + plugins: [ + new webpack.DefinePlugin({ + 'process.env': require('../config/dev.env') + }), + new webpack.HotModuleReplacementPlugin(), + new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update. + new webpack.NoEmitOnErrorsPlugin(), + // https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: 'index.html', + template: 'index.html', + inject: true + }), + // copy custom static assets + new CopyWebpackPlugin([ + { + from: path.resolve(__dirname, '../static'), + to: config.dev.assetsSubDirectory, + ignore: ['.*'] + } + ]) + ] +}) + +module.exports = new Promise((resolve, reject) => { + portfinder.basePort = process.env.PORT || config.dev.port + portfinder.getPort((err, port) => { + if (err) { + reject(err) + } else { + // publish the new Port, necessary for e2e tests + process.env.PORT = port + // add port to devServer config + devWebpackConfig.devServer.port = port + + // Add FriendlyErrorsPlugin + devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({ + compilationSuccessInfo: { + messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`], + }, + onErrors: config.dev.notifyOnErrors + ? utils.createNotifierCallback() + : undefined + })) + + resolve(devWebpackConfig) + } + }) +}) diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/build/webpack.prod.conf.js b/extensions/dataease-extensions-datasource/presto/presto-frontend/build/webpack.prod.conf.js new file mode 100644 index 0000000000..3e25238ef6 --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-frontend/build/webpack.prod.conf.js @@ -0,0 +1,145 @@ +'use strict' +const path = require('path') +const utils = require('./utils') +const webpack = require('webpack') +const config = require('../config') +const merge = require('webpack-merge') +const baseWebpackConfig = require('./webpack.base.conf') +const CopyWebpackPlugin = require('copy-webpack-plugin') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const ExtractTextPlugin = require('extract-text-webpack-plugin') +const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') +const UglifyJsPlugin = require('uglifyjs-webpack-plugin') + +const env = require('../config/prod.env') + +const webpackConfig = merge(baseWebpackConfig, { + module: { + rules: utils.styleLoaders({ + sourceMap: config.build.productionSourceMap, + extract: true, + usePostCSS: true + }) + }, + devtool: config.build.productionSourceMap ? config.build.devtool : false, + output: { + path: config.build.assetsRoot, + filename: utils.assetsPath('js/[name].[chunkhash].js'), + chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') + }, + plugins: [ + // http://vuejs.github.io/vue-loader/en/workflow/production.html + new webpack.DefinePlugin({ + 'process.env': env + }), + new UglifyJsPlugin({ + uglifyOptions: { + compress: { + warnings: false + } + }, + sourceMap: config.build.productionSourceMap, + parallel: true + }), + // extract css into its own file + new ExtractTextPlugin({ + filename: utils.assetsPath('css/[name].[contenthash].css'), + // Setting the following option to `false` will not extract CSS from codesplit chunks. + // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack. + // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, + // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110 + allChunks: true, + }), + // Compress extracted CSS. We are using this plugin so that possible + // duplicated CSS from different components can be deduped. + new OptimizeCSSPlugin({ + cssProcessorOptions: config.build.productionSourceMap + ? { safe: true, map: { inline: false } } + : { safe: true } + }), + // generate dist index.html with correct asset hash for caching. + // you can customize output by editing /index.html + // see https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: config.build.index, + template: 'index.html', + inject: true, + minify: { + removeComments: true, + collapseWhitespace: true, + removeAttributeQuotes: true + // more options: + // https://github.com/kangax/html-minifier#options-quick-reference + }, + // necessary to consistently work with multiple chunks via CommonsChunkPlugin + chunksSortMode: 'dependency' + }), + // keep module.id stable when vendor modules does not change + new webpack.HashedModuleIdsPlugin(), + // enable scope hoisting + new webpack.optimize.ModuleConcatenationPlugin(), + // split vendor js into its own file + new webpack.optimize.CommonsChunkPlugin({ + name: 'vendor', + minChunks (module) { + // any required modules inside node_modules are extracted to vendor + return ( + module.resource && + /\.js$/.test(module.resource) && + module.resource.indexOf( + path.join(__dirname, '../node_modules') + ) === 0 + ) + } + }), + // extract webpack runtime and module manifest to its own file in order to + // prevent vendor hash from being updated whenever app bundle is updated + new webpack.optimize.CommonsChunkPlugin({ + name: 'manifest', + minChunks: Infinity + }), + // This instance extracts shared chunks from code split chunks and bundles them + // in a separate chunk, similar to the vendor chunk + // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk + new webpack.optimize.CommonsChunkPlugin({ + name: 'app', + async: 'vendor-async', + children: true, + minChunks: 3 + }), + + // copy custom static assets + new CopyWebpackPlugin([ + { + from: path.resolve(__dirname, '../static'), + to: config.build.assetsSubDirectory, + ignore: ['.*'] + } + ]) + ] +}) + +if (config.build.productionGzip) { + const CompressionWebpackPlugin = require('compression-webpack-plugin') + + webpackConfig.plugins.push( + new CompressionWebpackPlugin({ + asset: '[path].gz[query]', + algorithm: 'gzip', + test: new RegExp( + '\\.(' + + config.build.productionGzipExtensions.join('|') + + ')$' + ), + threshold: 10240, + minRatio: 0.8 + }) + ) +} + +if (config.build.bundleAnalyzerReport) { + const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin + webpackConfig.plugins.push(new BundleAnalyzerPlugin()) +} + +module.exports = webpackConfig diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/config/dev.env.js b/extensions/dataease-extensions-datasource/presto/presto-frontend/config/dev.env.js new file mode 100644 index 0000000000..1e22973ae7 --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-frontend/config/dev.env.js @@ -0,0 +1,7 @@ +'use strict' +const merge = require('webpack-merge') +const prodEnv = require('./prod.env') + +module.exports = merge(prodEnv, { + NODE_ENV: '"development"' +}) diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/config/index.js b/extensions/dataease-extensions-datasource/presto/presto-frontend/config/index.js new file mode 100644 index 0000000000..c5eded7f81 --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-frontend/config/index.js @@ -0,0 +1,69 @@ +'use strict' +// Template version: 1.3.1 +// see http://vuejs-templates.github.io/webpack for documentation. + +const path = require('path') + +module.exports = { + dev: { + + // Paths + assetsSubDirectory: 'static', + assetsPublicPath: '/', + proxyTable: {}, + + // Various Dev Server settings + host: 'localhost', // can be overwritten by process.env.HOST + port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined + autoOpenBrowser: false, + errorOverlay: true, + notifyOnErrors: true, + poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- + + + /** + * Source Maps + */ + + // https://webpack.js.org/configuration/devtool/#development + devtool: 'cheap-module-eval-source-map', + + // If you have problems debugging vue-files in devtools, + // set this to false - it *may* help + // https://vue-loader.vuejs.org/en/options.html#cachebusting + cacheBusting: true, + + cssSourceMap: true + }, + + build: { + // Template for index.html + index: path.resolve(__dirname, '../dist/index.html'), + + // Paths + assetsRoot: path.resolve(__dirname, '../dist'), + assetsSubDirectory: 'static', + assetsPublicPath: '/', + + /** + * Source Maps + */ + + productionSourceMap: true, + // https://webpack.js.org/configuration/devtool/#production + devtool: '#source-map', + + // Gzip off by default as many popular static hosts such as + // Surge or Netlify already gzip all static assets for you. + // Before setting to `true`, make sure to: + // npm install --save-dev compression-webpack-plugin + productionGzip: false, + productionGzipExtensions: ['js', 'css'], + + // Run the build command with an extra argument to + // View the bundle analyzer report after build finishes: + // `npm run build --report` + // Set to `true` or `false` to always turn it on or off + bundleAnalyzerReport: process.env.npm_config_report + } +} diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/config/prod.env.js b/extensions/dataease-extensions-datasource/presto/presto-frontend/config/prod.env.js new file mode 100644 index 0000000000..a6f997616e --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-frontend/config/prod.env.js @@ -0,0 +1,4 @@ +'use strict' +module.exports = { + NODE_ENV: '"production"' +} diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/index.html b/extensions/dataease-extensions-datasource/presto/presto-frontend/index.html new file mode 100644 index 0000000000..03ce3fdf27 --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-frontend/index.html @@ -0,0 +1,12 @@ + + + + + + deplugin-view-frontend + + +
+ + + diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/package.json b/extensions/dataease-extensions-datasource/presto/presto-frontend/package.json new file mode 100644 index 0000000000..64a3ce7ad6 --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-frontend/package.json @@ -0,0 +1,75 @@ +{ + "name": "deplugin-datasource-frontend", + "version": "1.0.0", + "description": "A Vue.js project", + "author": "", + "private": true, + "scripts": { + "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", + "start": "npm run dev", + "build": "node build/build.js", + "buildPlugin": "node build/build-async-plugins.js" + }, + "dependencies": { + "@riophae/vue-treeselect": "0.4.0", + "highcharts": "^10.0.0", + "svg-sprite-loader": "^6.0.11", + "svgo": "1.2.2", + "svgo-loader": "^3.0.1", + "vue": "^2.5.2", + "vue-i18n": "7.3.2", + "vue-router": "^3.0.1", + "vue-uuid": "2.0.2", + "vuedraggable": "^2.24.3" + }, + "devDependencies": { + "autoprefixer": "^7.1.2", + "babel-core": "^6.22.1", + "babel-helper-vue-jsx-merge-props": "^2.0.3", + "babel-loader": "^7.1.1", + "babel-plugin-syntax-jsx": "^6.18.0", + "babel-plugin-transform-runtime": "^6.22.0", + "babel-plugin-transform-vue-jsx": "^3.5.0", + "babel-preset-env": "^1.3.2", + "babel-preset-stage-2": "^6.22.0", + "chalk": "^2.0.1", + "copy-webpack-plugin": "^4.6.0", + "css-loader": "^0.28.0", + "element-ui": "2.15.7", + "extract-text-webpack-plugin": "^4.0.0-beta.0", + "file-loader": "^1.1.4", + "friendly-errors-webpack-plugin": "^1.6.1", + "html-webpack-plugin": "^3.2.0", + "js-cookie": "2.2.0", + "node-notifier": "^8.0.1", + "optimize-css-assets-webpack-plugin": "^3.2.0", + "ora": "^1.2.0", + "portfinder": "^1.0.13", + "postcss-import": "^11.0.0", + "postcss-loader": "^2.0.8", + "postcss-url": "^7.2.1", + "rimraf": "^2.6.0", + "sass": "^1.33.0", + "sass-loader": "^7.3.1", + "semver": "^5.3.0", + "shelljs": "^0.8.5", + "uglifyjs-webpack-plugin": "^1.1.1", + "url-loader": "^0.5.8", + "vue-loader": "^15.6.4", + "vue-style-loader": "^4.1.2", + "vue-template-compiler": "^2.5.2", + "webpack": "^4.8.1", + "webpack-bundle-analyzer": "^3.3.2", + "webpack-dev-server": "^3.1.11", + "webpack-merge": "^4.1.0" + }, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + }, + "browserslist": [ + "> 1%", + "last 2 versions", + "not ie <= 8" + ] +} diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/pom.xml b/extensions/dataease-extensions-datasource/presto/presto-frontend/pom.xml new file mode 100644 index 0000000000..ecdd508d28 --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-frontend/pom.xml @@ -0,0 +1,80 @@ + + + + + io.dataease + presto + ${dataease.version} + + + 4.0.0 + presto-frontend + + + UTF-8 + UTF-8 + 1.9.1 + + + + + + maven-clean-plugin + + + + static + + ** + + false + + + + + + + + com.github.eirslett + frontend-maven-plugin + ${frontend-maven-plugin.version} + + + install node and npm + + install-node-and-npm + + + + v16.20.2 + 7.6.3 + + + + + npm install + + npm + + + + install --force + + + + + npm run buildPlugin + + npm + + + run buildPlugin + + + + + + + diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/src/App.vue b/extensions/dataease-extensions-datasource/presto/presto-frontend/src/App.vue new file mode 100644 index 0000000000..8542e07722 --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-frontend/src/App.vue @@ -0,0 +1,23 @@ + + + + + diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/src/assets/logo.png b/extensions/dataease-extensions-datasource/presto/presto-frontend/src/assets/logo.png new file mode 100644 index 0000000000..f3d2503fc2 Binary files /dev/null and b/extensions/dataease-extensions-datasource/presto/presto-frontend/src/assets/logo.png differ diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/src/components/HelloWorld.vue b/extensions/dataease-extensions-datasource/presto/presto-frontend/src/components/HelloWorld.vue new file mode 100644 index 0000000000..1c19f2a399 --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-frontend/src/components/HelloWorld.vue @@ -0,0 +1,113 @@ + + + + + + diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/src/de-base/lang/en.js b/extensions/dataease-extensions-datasource/presto/presto-frontend/src/de-base/lang/en.js new file mode 100644 index 0000000000..ddb290ce52 --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-frontend/src/de-base/lang/en.js @@ -0,0 +1,20 @@ +export default { + plugin_view_3d_pie: { + type_title: '3D-PIE', + label: 'Label', + angle: 'Angle' + }, + host: 'Host', + port: 'Port', + dataBase: 'Catalog', + schema: 'Schema', + username: 'User', + password: 'Password', + query_timeout: 'Query timeout (seconds)', + second: 'second', + please_select: 'Please select', + query_timeout: 'query timeout', + enter_the_port: 'Please enter the port', + one_user_name: 'enter one user name', + input_a_password: 'Please input a password' +} diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/src/de-base/lang/index.js b/extensions/dataease-extensions-datasource/presto/presto-frontend/src/de-base/lang/index.js new file mode 100644 index 0000000000..e50d0478e6 --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-frontend/src/de-base/lang/index.js @@ -0,0 +1,49 @@ +import Vue from 'vue' +import VueI18n from 'vue-i18n' +import Cookies from 'js-cookie' +import elementEnLocale from 'element-ui/lib/locale/lang/en' // element-ui lang +import elementZhLocale from 'element-ui/lib/locale/lang/zh-CN'// element-ui lang +import elementTWLocale from 'element-ui/lib/locale/lang/zh-TW'// element-ui lang + +import localMessages from './messages' + + +Vue.use(VueI18n) + +const messages = { + en_US: { + ...localMessages['en_US'], + ...elementEnLocale + }, + zh_CN: { + ...localMessages['zh_CN'], + ...elementZhLocale + }, + zh_TW: { + ...localMessages['zh_TW'], + ...elementTWLocale + } +} +export function getLanguage () { + const chooseLanguage = Cookies.get('language') + if (chooseLanguage) return chooseLanguage + + // if has not choose language + const language = (navigator.language || navigator.browserLanguage).toLowerCase() + const locales = Object.keys(messages) + for (const locale of locales) { + if (language.indexOf(locale) > -1) { + return locale + } + } + return 'zh_CN' +} +const i18n = new VueI18n({ + // set locale + // options: en | zh | es + locale: getLanguage(), + // set locale messages + messages +}) + +export default i18n diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/src/de-base/lang/messages.js b/extensions/dataease-extensions-datasource/presto/presto-frontend/src/de-base/lang/messages.js new file mode 100644 index 0000000000..844f8c0673 --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-frontend/src/de-base/lang/messages.js @@ -0,0 +1,17 @@ +import enLocale from './en' +import zhLocale from './zh' +import twLocale from './tw' + +const messages = { + en_US: { + ...enLocale + }, + zh_CN: { + ...zhLocale + }, + zh_TW: { + ...twLocale + } +} + +export default messages \ No newline at end of file diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/src/de-base/lang/tw.js b/extensions/dataease-extensions-datasource/presto/presto-frontend/src/de-base/lang/tw.js new file mode 100644 index 0000000000..032938d07f --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-frontend/src/de-base/lang/tw.js @@ -0,0 +1,20 @@ +export default { + plugin_view_3d_pie: { + type_title: '3D餅圖', + label: '標籤', + angle: '角度' + }, + host: '主機名', + port: '端口', + dataBase: '數據庫', + schema: 'Schema', + username: '用戶名', + password: '密碼', + query_timeout: '査詢超時(秒)', + second: '秒', + please_select: '請選擇', + query_timeout: '査詢超時', + enter_the_port: '請輸入埠', + one_user_name: '請輸入用戶名', + input_a_password: '請輸入密碼' +} diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/src/de-base/lang/zh.js b/extensions/dataease-extensions-datasource/presto/presto-frontend/src/de-base/lang/zh.js new file mode 100644 index 0000000000..2e0294a68c --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-frontend/src/de-base/lang/zh.js @@ -0,0 +1,20 @@ +export default { + plugin_view_3d_pie: { + type_title: '3D饼图', + label: '标签', + angle: '角度' + }, + host: '主机名', + port: '端口', + dataBase: '数据库', + schema: 'Schema', + username: '用户名', + password: '密码', + query_timeout: '查询超时(秒)', + second: '秒', + please_select: '请选择', + query_timeout: '查询超时', + enter_the_port: '请输入端口', + one_user_name: '请输入用户名', + input_a_password: '请输入密码' +} diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/src/icons/index.js b/extensions/dataease-extensions-datasource/presto/presto-frontend/src/icons/index.js new file mode 100644 index 0000000000..2c6b309c96 --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-frontend/src/icons/index.js @@ -0,0 +1,9 @@ +import Vue from 'vue' +import SvgIcon from '@/components/SvgIcon'// svg component + +// register globally +Vue.component('svg-icon', SvgIcon) + +const req = require.context('./svg', false, /\.svg$/) +const requireAll = requireContext => requireContext.keys().map(requireContext) +requireAll(req) diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/src/icons/svg/de_pwd_invisible.svg b/extensions/dataease-extensions-datasource/presto/presto-frontend/src/icons/svg/de_pwd_invisible.svg new file mode 100644 index 0000000000..661ebba33e --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-frontend/src/icons/svg/de_pwd_invisible.svg @@ -0,0 +1,3 @@ + + + diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/src/icons/svg/de_pwd_visible.svg b/extensions/dataease-extensions-datasource/presto/presto-frontend/src/icons/svg/de_pwd_visible.svg new file mode 100644 index 0000000000..4e7ba5e799 --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-frontend/src/icons/svg/de_pwd_visible.svg @@ -0,0 +1,3 @@ + + + diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/src/icons/svg/presto-backend.svg b/extensions/dataease-extensions-datasource/presto/presto-frontend/src/icons/svg/presto-backend.svg new file mode 100644 index 0000000000..0612c80c26 --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-frontend/src/icons/svg/presto-backend.svg @@ -0,0 +1 @@ +【icon】插件管理-导出 diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/src/icons/svg/presto.jpg b/extensions/dataease-extensions-datasource/presto/presto-frontend/src/icons/svg/presto.jpg new file mode 100644 index 0000000000..f1193f0b3e Binary files /dev/null and b/extensions/dataease-extensions-datasource/presto/presto-frontend/src/icons/svg/presto.jpg differ diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/src/icons/svgo.yml b/extensions/dataease-extensions-datasource/presto/presto-frontend/src/icons/svgo.yml new file mode 100644 index 0000000000..d11906aec2 --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-frontend/src/icons/svgo.yml @@ -0,0 +1,22 @@ +# replace default config + +# multipass: true +# full: true + +plugins: + + # - name + # + # or: + # - name: false + # - name: true + # + # or: + # - name: + # param1: 1 + # param2: 2 + +- removeAttrs: + attrs: + - 'fill' + - 'fill-rule' diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/src/main.js b/extensions/dataease-extensions-datasource/presto/presto-frontend/src/main.js new file mode 100644 index 0000000000..99c5626590 --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-frontend/src/main.js @@ -0,0 +1,33 @@ +// The Vue build version to load with the `import` command +// (runtime-only or standalone) has been set in webpack.base.conf with an alias. +import Vue from 'vue' +import App from './App' +import router from './router' +import ElementUI from 'element-ui' +import Cookies from 'js-cookie' +import i18n from './de-base/lang' +import draggable from 'vuedraggable' +import Treeselect from '@riophae/vue-treeselect' +import '@riophae/vue-treeselect/dist/vue-treeselect.css' +Vue.config.productionTip = false +Vue.use(ElementUI, { + size: Cookies.get('size') || 'medium', + i18n: (key, value) => i18n.t(key, value) +}) +Vue.component('Treeselect', Treeselect) +Vue.component('draggable', draggable) +Vue.prototype.hasDataPermission = function(pTarget, pSource) { + + if (pSource && pTarget) { + return pSource.indexOf(pTarget) > -1 + } + return false +} +/* eslint-disable no-new */ +new Vue({ + el: '#app', + router, + i18n, + components: { App }, + template: '' +}) diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/src/router/index.js b/extensions/dataease-extensions-datasource/presto/presto-frontend/src/router/index.js new file mode 100644 index 0000000000..b5b55fa1fc --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-frontend/src/router/index.js @@ -0,0 +1,21 @@ +import Vue from 'vue' +import Router from 'vue-router' +import HelloWorld from '@/components/HelloWorld' +import maxcompute from '@/views/maxcompute' + +Vue.use(Router) + +export default new Router({ + routes: [ + { + path: '/', + name: 'HelloWorld', + component: HelloWorld + }, + { + path: '/maxcompute', + name: 'maxcompute', + component: maxcompute + } + ] +}) diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/src/utils/compare.js b/extensions/dataease-extensions-datasource/presto/presto-frontend/src/utils/compare.js new file mode 100644 index 0000000000..b06a104a10 --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-frontend/src/utils/compare.js @@ -0,0 +1,29 @@ +export const compareItem = { + type: 'none', // year-yoy/month-yoy等 + resultData: 'percent', // 对比差sub,百分比percent等 + field: '', + custom: { + field: '', + calcType: '0', // 0-增长值,1-增长率 + timeType: '0', // 0-固定日期,1-日期区间 + currentTime: '', + compareTime: '', + currentTimeRange: [], + compareTimeRange: [] + } +} + +export const compareYearList = [ + { name: 'year_mom', value: 'year_mom' } +] + +export const compareMonthList = [ + { name: 'month_mom', value: 'month_mom' }, + { name: 'year_yoy', value: 'year_yoy' } +] + +export const compareDayList = [ + { name: 'day_mom', value: 'day_mom' }, + { name: 'month_yoy', value: 'month_yoy' }, + { name: 'year_yoy', value: 'year_yoy' } +] diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/src/utils/validate.js b/extensions/dataease-extensions-datasource/presto/presto-frontend/src/utils/validate.js new file mode 100644 index 0000000000..3e8ffa9448 --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-frontend/src/utils/validate.js @@ -0,0 +1,15 @@ +/** + * Created by PanJiaChen on 16/11/18. + */ + +/** + * @param {string} path + * @returns {Boolean} + */ +export function isExternal(path) { + return /^(https?:|mailto:|tel:)/.test(path) || /^(http?:|mailto:|tel:)/.test(path) || path.startsWith('/api/pluginCommon/staticInfo') +} + + + + diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/src/views/dePwd.vue b/extensions/dataease-extensions-datasource/presto/presto-frontend/src/views/dePwd.vue new file mode 100644 index 0000000000..61bf3e88ee --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-frontend/src/views/dePwd.vue @@ -0,0 +1,75 @@ + + + + \ No newline at end of file diff --git a/extensions/dataease-extensions-datasource/presto/presto-frontend/src/views/presto.vue b/extensions/dataease-extensions-datasource/presto/presto-frontend/src/views/presto.vue new file mode 100644 index 0000000000..a1d2aa15f7 --- /dev/null +++ b/extensions/dataease-extensions-datasource/presto/presto-frontend/src/views/presto.vue @@ -0,0 +1,167 @@ + + + + + diff --git a/extensions/dataease-extensions-view/pom.xml b/extensions/dataease-extensions-view/pom.xml new file mode 100644 index 0000000000..0df3753f94 --- /dev/null +++ b/extensions/dataease-extensions-view/pom.xml @@ -0,0 +1,24 @@ + + + + dataease-extensions + io.dataease + ${dataease.version} + + 4.0.0 + pom + + view-bubblemap + view-3dpie + view-symbolmap + view-sankey + view-chartmix + view-racebar + + + dataease-extensions-view + + + diff --git a/extensions/dataease-extensions-view/view-3dpie/build.sh b/extensions/dataease-extensions-view/view-3dpie/build.sh new file mode 100644 index 0000000000..738ec8be7d --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/build.sh @@ -0,0 +1,8 @@ +#!/bin/sh +mvn clean package + +cp view-3dpie-backend/target/view-3dpie-backend-1.18.3.jar . + +zip -r 3dpie.zip ./view-3dpie-backend-1.18.3.jar ./plugin.json + +rm -f ./view-3dpie-backend-1.18.3.jar diff --git a/extensions/dataease-extensions-view/view-3dpie/plugin.json b/extensions/dataease-extensions-view/view-3dpie/plugin.json new file mode 100644 index 0000000000..1151b075c4 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/plugin.json @@ -0,0 +1,13 @@ +{ + "name": "3D 饼图插件", + "free": 0, + "store": "thirdpart", + "cost": 0, + "category": "view", + "descript": "High Chart 3D 饼图插件", + "version": "1.18.3", + "creator": "DATAEASE", + "moduleName": "view-3dpie-backend", + "require": "1.10.0", + "dsType": "" +} \ No newline at end of file diff --git a/extensions/dataease-extensions-view/view-3dpie/pom.xml b/extensions/dataease-extensions-view/view-3dpie/pom.xml new file mode 100644 index 0000000000..0d0c0f335a --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/pom.xml @@ -0,0 +1,19 @@ + + + + dataease-extensions-view + io.dataease + ${dataease.version} + + 4.0.0 + + view-3dpie + pom + + view-3dpie-frontend + view-3dpie-backend + + + diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-backend/pom.xml b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-backend/pom.xml new file mode 100644 index 0000000000..18bd300376 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-backend/pom.xml @@ -0,0 +1,109 @@ + + + + view-3dpie + io.dataease + ${dataease.version} + + 4.0.0 + + view-3dpie-backend + + + + io.dataease + dataease-plugin-view + + + + + + + src/main/java + + **/*.properties + **/*.xml + + false + + + src/main/resources + + **/* + + false + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 11 + 11 + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.4 + + + **/server/** + **/*.properties + **/Application* + + + + + + maven-clean-plugin + + + + src/main/resources/static + + ** + + false + + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + main-class-placement + generate-resources + + + + + + + + + + + + run + + + + + + + + + + + diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-backend/src/main/java/io/dataease/plugins/view/official/impl/Pie3DService.java b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-backend/src/main/java/io/dataease/plugins/view/official/impl/Pie3DService.java new file mode 100644 index 0000000000..8b0914b20b --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-backend/src/main/java/io/dataease/plugins/view/official/impl/Pie3DService.java @@ -0,0 +1,88 @@ +package io.dataease.plugins.view.official.impl; + +import io.dataease.plugins.common.dto.StaticResource; +import io.dataease.plugins.view.entity.PluginViewField; +import io.dataease.plugins.view.entity.PluginViewParam; +import io.dataease.plugins.view.entity.PluginViewType; +import io.dataease.plugins.view.service.ViewPluginService; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.stereotype.Service; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +@Service +public class Pie3DService extends ViewPluginService { + + private static final String VIEW_TYPE_VALUE = "3d-pie"; + /*下版这些常量移到sdk*/ + private static final String TYPE = "-type"; + private static final String DATA = "-data"; + private static final String STYLE = "-style"; + private static final String VIEW = "-view"; + private static final String SUFFIX = "svg"; + /*下版这些常量移到sdk*/ + private static final String VIEW_TYPE = VIEW_TYPE_VALUE + TYPE; + private static final String VIEW_DATA = VIEW_TYPE_VALUE + DATA; + private static final String VIEW_STYLE = VIEW_TYPE_VALUE + STYLE; + private static final String VIEW_VIEW = VIEW_TYPE_VALUE + VIEW; + + @Override + public PluginViewType viewType() { + PluginViewType pluginViewType = new PluginViewType(); + pluginViewType.setRender("highcharts"); + pluginViewType.setCategory("chart.chart_type_distribute"); + pluginViewType.setValue(VIEW_TYPE_VALUE); + return pluginViewType; + } + + @Override + public Object format(Object o) { + return null; + } + + @Override + public List components() { + List results = new ArrayList<>(); + results.add(VIEW_VIEW); + results.add(VIEW_DATA); + results.add(VIEW_TYPE); + results.add(VIEW_STYLE); + return results; + } + + @Override + public List staticResources() { + List results = new ArrayList<>(); + StaticResource staticResource = new StaticResource(); + staticResource.setName(VIEW_TYPE_VALUE); + staticResource.setSuffix(SUFFIX); + results.add(staticResource); + results.add(pluginSvg()); + return results; + } + + private StaticResource pluginSvg() { + StaticResource staticResource = new StaticResource(); + staticResource.setName("view-3dpie-backend"); + staticResource.setSuffix("svg"); + return staticResource; + } + + @Override + protected InputStream readContent(String s) { + InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("static/" + s); + return resourceAsStream; + } + + @Override + public String generateSQL(PluginViewParam param) { + List xAxis = param.getFieldsByType("xAxis"); + List yAxis = param.getFieldsByType("yAxis"); + if (CollectionUtils.isEmpty(xAxis) || CollectionUtils.isEmpty(yAxis)){ + return null; + } + return super.generateSQL(param); + } +} diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/.babelrc b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/.babelrc new file mode 100644 index 0000000000..3a280ba34b --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/.babelrc @@ -0,0 +1,12 @@ +{ + "presets": [ + ["env", { + "modules": false, + "targets": { + "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] + } + }], + "stage-2" + ], + "plugins": ["transform-vue-jsx", "transform-runtime"] +} diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/.editorconfig b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/.editorconfig new file mode 100644 index 0000000000..9d08a1a828 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/.gitignore b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/.gitignore new file mode 100644 index 0000000000..541a820f6c --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/.gitignore @@ -0,0 +1,14 @@ +.DS_Store +node_modules/ +/dist/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/.postcssrc.js b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/.postcssrc.js new file mode 100644 index 0000000000..eee3e92d7f --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/.postcssrc.js @@ -0,0 +1,10 @@ +// https://github.com/michael-ciniawsky/postcss-load-config + +module.exports = { + "plugins": { + "postcss-import": {}, + "postcss-url": {}, + // to edit target browsers: use "browserslist" field in package.json + "autoprefixer": {} + } +} diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/README.md b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/README.md new file mode 100644 index 0000000000..c1e4be95e0 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/README.md @@ -0,0 +1,21 @@ +# deplugin-view-frontend + +> A Vue.js project + +## Build Setup + +``` bash +# install dependencies +npm install + +# serve with hot reload at localhost:8080 +npm run dev + +# build for production with minification +npm run build + +# build for production and view the bundle analyzer report +npm run build --report +``` + +For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader). diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/build/build-async-plugins.js b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/build/build-async-plugins.js new file mode 100644 index 0000000000..2303854531 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/build/build-async-plugins.js @@ -0,0 +1,35 @@ +'use strict' +require('./check-versions')() + +process.env.NODE_ENV = 'production' + +const ora = require('ora') +const chalk = require('chalk') +const webpack = require('webpack') +const webpackConfig = require('./webpack.async-plugins') + +const spinner = ora('building for sync-plugins...') +spinner.start() + +webpack(webpackConfig, function (err, stats) { + spinner.stop() + if (err) throw err + process.stdout.write(stats.toString({ + colors: true, + modules: false, + children: false, + chunks: false, + chunkModules: false + }) + '\n\n') + + if (stats.hasErrors()) { + console.log(chalk.red(' Build failed with errors.\n')) + process.exit(1) + } + + console.log(chalk.cyan(' Build complete.\n')) + console.log(chalk.yellow( + ' Tip: built files are meant to be served over an HTTP server.\n' + + ' Opening index.html over file:// won\'t work.\n' + )) +}) diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/build/build.js b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/build/build.js new file mode 100644 index 0000000000..8f2ad8ad49 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/build/build.js @@ -0,0 +1,41 @@ +'use strict' +require('./check-versions')() + +process.env.NODE_ENV = 'production' + +const ora = require('ora') +const rm = require('rimraf') +const path = require('path') +const chalk = require('chalk') +const webpack = require('webpack') +const config = require('../config') +const webpackConfig = require('./webpack.prod.conf') + +const spinner = ora('building for production...') +spinner.start() + +rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { + if (err) throw err + webpack(webpackConfig, (err, stats) => { + spinner.stop() + if (err) throw err + process.stdout.write(stats.toString({ + colors: true, + modules: false, + children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build. + chunks: false, + chunkModules: false + }) + '\n\n') + + if (stats.hasErrors()) { + console.log(chalk.red(' Build failed with errors.\n')) + process.exit(1) + } + + console.log(chalk.cyan(' Build complete.\n')) + console.log(chalk.yellow( + ' Tip: built files are meant to be served over an HTTP server.\n' + + ' Opening index.html over file:// won\'t work.\n' + )) + }) +}) diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/build/check-versions.js b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/build/check-versions.js new file mode 100644 index 0000000000..3ef972a08d --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/build/check-versions.js @@ -0,0 +1,54 @@ +'use strict' +const chalk = require('chalk') +const semver = require('semver') +const packageConfig = require('../package.json') +const shell = require('shelljs') + +function exec (cmd) { + return require('child_process').execSync(cmd).toString().trim() +} + +const versionRequirements = [ + { + name: 'node', + currentVersion: semver.clean(process.version), + versionRequirement: packageConfig.engines.node + } +] + +if (shell.which('npm')) { + versionRequirements.push({ + name: 'npm', + currentVersion: exec('npm --version'), + versionRequirement: packageConfig.engines.npm + }) +} + +module.exports = function () { + const warnings = [] + + for (let i = 0; i < versionRequirements.length; i++) { + const mod = versionRequirements[i] + + if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { + warnings.push(mod.name + ': ' + + chalk.red(mod.currentVersion) + ' should be ' + + chalk.green(mod.versionRequirement) + ) + } + } + + if (warnings.length) { + console.log('') + console.log(chalk.yellow('To use this template, you must update following to modules:')) + console.log() + + for (let i = 0; i < warnings.length; i++) { + const warning = warnings[i] + console.log(' ' + warning) + } + + console.log() + process.exit(1) + } +} diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/build/logo.png b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/build/logo.png new file mode 100644 index 0000000000..f3d2503fc2 Binary files /dev/null and b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/build/logo.png differ diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/build/utils.js b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/build/utils.js new file mode 100644 index 0000000000..e534fb0fd6 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/build/utils.js @@ -0,0 +1,101 @@ +'use strict' +const path = require('path') +const config = require('../config') +const ExtractTextPlugin = require('extract-text-webpack-plugin') +const packageConfig = require('../package.json') + +exports.assetsPath = function (_path) { + const assetsSubDirectory = process.env.NODE_ENV === 'production' + ? config.build.assetsSubDirectory + : config.dev.assetsSubDirectory + + return path.posix.join(assetsSubDirectory, _path) +} + +exports.cssLoaders = function (options) { + options = options || {} + + const cssLoader = { + loader: 'css-loader', + options: { + sourceMap: options.sourceMap + } + } + + const postcssLoader = { + loader: 'postcss-loader', + options: { + sourceMap: options.sourceMap + } + } + + // generate loader string to be used with extract text plugin + function generateLoaders (loader, loaderOptions) { + const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader] + + if (loader) { + loaders.push({ + loader: loader + '-loader', + options: Object.assign({}, loaderOptions, { + sourceMap: options.sourceMap + }) + }) + } + + // Extract CSS when that option is specified + // (which is the case during production build) + if (options.extract) { + return ExtractTextPlugin.extract({ + use: loaders, + fallback: 'vue-style-loader' + }) + } else { + return ['vue-style-loader'].concat(loaders) + } + } + + // https://vue-loader.vuejs.org/en/configurations/extract-css.html + return { + css: generateLoaders(), + postcss: generateLoaders(), + less: generateLoaders('less'), + sass: generateLoaders('sass', { indentedSyntax: true }), + scss: generateLoaders('sass'), + stylus: generateLoaders('stylus'), + styl: generateLoaders('stylus') + } +} + +// Generate loaders for standalone style files (outside of .vue) +exports.styleLoaders = function (options) { + const output = [] + const loaders = exports.cssLoaders(options) + + for (const extension in loaders) { + const loader = loaders[extension] + output.push({ + test: new RegExp('\\.' + extension + '$'), + use: loader + }) + } + + return output +} + +exports.createNotifierCallback = () => { + const notifier = require('node-notifier') + + return (severity, errors) => { + if (severity !== 'error') return + + const error = errors[0] + const filename = error.file && error.file.split('!').pop() + + notifier.notify({ + title: packageConfig.name, + message: severity + ': ' + error.name, + subtitle: filename || '', + icon: path.join(__dirname, 'logo.png') + }) + } +} diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/build/vue-loader.conf.js b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/build/vue-loader.conf.js new file mode 100644 index 0000000000..33ed58bc0a --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/build/vue-loader.conf.js @@ -0,0 +1,22 @@ +'use strict' +const utils = require('./utils') +const config = require('../config') +const isProduction = process.env.NODE_ENV === 'production' +const sourceMapEnabled = isProduction + ? config.build.productionSourceMap + : config.dev.cssSourceMap + +module.exports = { + loaders: utils.cssLoaders({ + sourceMap: sourceMapEnabled, + extract: isProduction + }), + cssSourceMap: sourceMapEnabled, + cacheBusting: config.dev.cacheBusting, + transformToRequire: { + video: ['src', 'poster'], + source: 'src', + img: 'src', + image: 'xlink:href' + } +} diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/build/webpack.async-plugins.js b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/build/webpack.async-plugins.js new file mode 100644 index 0000000000..2f67ecbe56 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/build/webpack.async-plugins.js @@ -0,0 +1,97 @@ +const webpack = require('webpack') +const path = require('path') +const utils = require('./utils') +const CopyPlugin = require("copy-webpack-plugin"); +const VueLoaderPlugin = require('vue-loader/lib/plugin'); +function resolve (dir) { + return path.join(__dirname, '..', dir) +} + +module.exports = { + mode: 'development', + entry: { + + // PluginDemo: resolve('/src/views/PluginDemo.vue') + '3d-pie-view': resolve('/src/views/highcharts/3dpie/index.vue'), + '3d-pie-data': resolve('/src/views/highcharts/3dpie/data.vue'), + '3d-pie-type': resolve('/src/views/highcharts/3dpie/type.vue'), + '3d-pie-style': resolve('/src/views/highcharts/3dpie/style.vue') + + }, + output: { + path: resolve('/static/'), + filename: '[name].js' + }, + resolve: { + extensions: ['.js', '.vue', '.json'], + alias: { + 'vue$': 'vue/dist/vue.esm.js', + '@': resolve('src') + } + }, + externals: { + vue: 'vue' + }, + module: { + rules: [ + { + test: /\.vue$/, + loader: 'vue-loader', + options: { + transformAssetUrls: { + video: 'src', + source: 'src', + img: 'src', + image: 'xlink:href' + } + } + }, + { + test: /.(sa|sc|c)ss$/, + use: [ + {loader: 'vue-style-loader'}, + 'css-loader', + 'sass-loader' + ] + }, + { + test: /\.js$/, + loader: 'babel-loader', + include: [resolve('src'), resolve('test')] + }, + { + test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('img/[name].[hash:7].[ext]') + } + }, + { + test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('media/[name].[hash:7].[ext]') + } + }, + { + test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('fonts/[name].[hash:7].[ext]') + } + } + ] + }, + plugins: [ + new VueLoaderPlugin(), + new webpack.DefinePlugin({ + 'process.env.NODE_ENV': '"production"' + }), + new CopyPlugin([ + {from: 'src/icons/svg/'} + ]), + ] +} diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/build/webpack.base.conf.js b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/build/webpack.base.conf.js new file mode 100644 index 0000000000..6ccc02dab4 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/build/webpack.base.conf.js @@ -0,0 +1,91 @@ +'use strict' +const path = require('path') +const utils = require('./utils') +const config = require('../config') +const vueLoaderConfig = require('./vue-loader.conf') + +function resolve (dir) { + return path.join(__dirname, '..', dir) +} + + + +module.exports = { + context: path.resolve(__dirname, '../'), + entry: { + app: './src/main.js' + }, + output: { + path: config.build.assetsRoot, + filename: '[name].js', + publicPath: process.env.NODE_ENV === 'production' + ? config.build.assetsPublicPath + : config.dev.assetsPublicPath + }, + resolve: { + extensions: ['.js', '.vue', '.json'], + alias: { + 'vue$': 'vue/dist/vue.esm.js', + '@': resolve('src'), + } + }, + module: { + rules: [ + { + test: /\.vue$/, + loader: 'vue-loader', + options: vueLoaderConfig + }, + { + test: /\.js$/, + loader: 'babel-loader', + include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')] + }, + { + test: /\.svg$/, + loader: 'svg-sprite-loader', + include: [resolve('src/icons')], + options: { + symbolId: 'icon-[name]' + } + }, + { + test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, + loader: 'url-loader', + exclude: [resolve('src/icons')], + options: { + limit: 10000, + name: utils.assetsPath('img/[name].[hash:7].[ext]') + } + }, + { + test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('media/[name].[hash:7].[ext]') + } + }, + { + test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('fonts/[name].[hash:7].[ext]') + } + } + ] + }, + node: { + // prevent webpack from injecting useless setImmediate polyfill because Vue + // source contains it (although only uses it if it's native). + setImmediate: false, + // prevent webpack from injecting mocks to Node native modules + // that does not make sense for the client + dgram: 'empty', + fs: 'empty', + net: 'empty', + tls: 'empty', + child_process: 'empty' + } +} diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/build/webpack.dev.conf.js b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/build/webpack.dev.conf.js new file mode 100755 index 0000000000..070ae221f3 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/build/webpack.dev.conf.js @@ -0,0 +1,95 @@ +'use strict' +const utils = require('./utils') +const webpack = require('webpack') +const config = require('../config') +const merge = require('webpack-merge') +const path = require('path') +const baseWebpackConfig = require('./webpack.base.conf') +const CopyWebpackPlugin = require('copy-webpack-plugin') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') +const portfinder = require('portfinder') + +const HOST = process.env.HOST +const PORT = process.env.PORT && Number(process.env.PORT) + +const devWebpackConfig = merge(baseWebpackConfig, { + module: { + rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) + }, + // cheap-module-eval-source-map is faster for development + devtool: config.dev.devtool, + + // these devServer options should be customized in /config/index.js + devServer: { + clientLogLevel: 'warning', + historyApiFallback: { + rewrites: [ + { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') }, + ], + }, + hot: true, + contentBase: false, // since we use CopyWebpackPlugin. + compress: true, + host: HOST || config.dev.host, + port: PORT || config.dev.port, + open: config.dev.autoOpenBrowser, + overlay: config.dev.errorOverlay + ? { warnings: false, errors: true } + : false, + publicPath: config.dev.assetsPublicPath, + proxy: config.dev.proxyTable, + quiet: true, // necessary for FriendlyErrorsPlugin + watchOptions: { + poll: config.dev.poll, + } + }, + plugins: [ + new webpack.DefinePlugin({ + 'process.env': require('../config/dev.env') + }), + new webpack.HotModuleReplacementPlugin(), + new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update. + new webpack.NoEmitOnErrorsPlugin(), + // https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: 'index.html', + template: 'index.html', + inject: true + }), + // copy custom static assets + new CopyWebpackPlugin([ + { + from: path.resolve(__dirname, '../static'), + to: config.dev.assetsSubDirectory, + ignore: ['.*'] + } + ]) + ] +}) + +module.exports = new Promise((resolve, reject) => { + portfinder.basePort = process.env.PORT || config.dev.port + portfinder.getPort((err, port) => { + if (err) { + reject(err) + } else { + // publish the new Port, necessary for e2e tests + process.env.PORT = port + // add port to devServer config + devWebpackConfig.devServer.port = port + + // Add FriendlyErrorsPlugin + devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({ + compilationSuccessInfo: { + messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`], + }, + onErrors: config.dev.notifyOnErrors + ? utils.createNotifierCallback() + : undefined + })) + + resolve(devWebpackConfig) + } + }) +}) diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/build/webpack.prod.conf.js b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/build/webpack.prod.conf.js new file mode 100644 index 0000000000..3e25238ef6 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/build/webpack.prod.conf.js @@ -0,0 +1,145 @@ +'use strict' +const path = require('path') +const utils = require('./utils') +const webpack = require('webpack') +const config = require('../config') +const merge = require('webpack-merge') +const baseWebpackConfig = require('./webpack.base.conf') +const CopyWebpackPlugin = require('copy-webpack-plugin') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const ExtractTextPlugin = require('extract-text-webpack-plugin') +const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') +const UglifyJsPlugin = require('uglifyjs-webpack-plugin') + +const env = require('../config/prod.env') + +const webpackConfig = merge(baseWebpackConfig, { + module: { + rules: utils.styleLoaders({ + sourceMap: config.build.productionSourceMap, + extract: true, + usePostCSS: true + }) + }, + devtool: config.build.productionSourceMap ? config.build.devtool : false, + output: { + path: config.build.assetsRoot, + filename: utils.assetsPath('js/[name].[chunkhash].js'), + chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') + }, + plugins: [ + // http://vuejs.github.io/vue-loader/en/workflow/production.html + new webpack.DefinePlugin({ + 'process.env': env + }), + new UglifyJsPlugin({ + uglifyOptions: { + compress: { + warnings: false + } + }, + sourceMap: config.build.productionSourceMap, + parallel: true + }), + // extract css into its own file + new ExtractTextPlugin({ + filename: utils.assetsPath('css/[name].[contenthash].css'), + // Setting the following option to `false` will not extract CSS from codesplit chunks. + // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack. + // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, + // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110 + allChunks: true, + }), + // Compress extracted CSS. We are using this plugin so that possible + // duplicated CSS from different components can be deduped. + new OptimizeCSSPlugin({ + cssProcessorOptions: config.build.productionSourceMap + ? { safe: true, map: { inline: false } } + : { safe: true } + }), + // generate dist index.html with correct asset hash for caching. + // you can customize output by editing /index.html + // see https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: config.build.index, + template: 'index.html', + inject: true, + minify: { + removeComments: true, + collapseWhitespace: true, + removeAttributeQuotes: true + // more options: + // https://github.com/kangax/html-minifier#options-quick-reference + }, + // necessary to consistently work with multiple chunks via CommonsChunkPlugin + chunksSortMode: 'dependency' + }), + // keep module.id stable when vendor modules does not change + new webpack.HashedModuleIdsPlugin(), + // enable scope hoisting + new webpack.optimize.ModuleConcatenationPlugin(), + // split vendor js into its own file + new webpack.optimize.CommonsChunkPlugin({ + name: 'vendor', + minChunks (module) { + // any required modules inside node_modules are extracted to vendor + return ( + module.resource && + /\.js$/.test(module.resource) && + module.resource.indexOf( + path.join(__dirname, '../node_modules') + ) === 0 + ) + } + }), + // extract webpack runtime and module manifest to its own file in order to + // prevent vendor hash from being updated whenever app bundle is updated + new webpack.optimize.CommonsChunkPlugin({ + name: 'manifest', + minChunks: Infinity + }), + // This instance extracts shared chunks from code split chunks and bundles them + // in a separate chunk, similar to the vendor chunk + // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk + new webpack.optimize.CommonsChunkPlugin({ + name: 'app', + async: 'vendor-async', + children: true, + minChunks: 3 + }), + + // copy custom static assets + new CopyWebpackPlugin([ + { + from: path.resolve(__dirname, '../static'), + to: config.build.assetsSubDirectory, + ignore: ['.*'] + } + ]) + ] +}) + +if (config.build.productionGzip) { + const CompressionWebpackPlugin = require('compression-webpack-plugin') + + webpackConfig.plugins.push( + new CompressionWebpackPlugin({ + asset: '[path].gz[query]', + algorithm: 'gzip', + test: new RegExp( + '\\.(' + + config.build.productionGzipExtensions.join('|') + + ')$' + ), + threshold: 10240, + minRatio: 0.8 + }) + ) +} + +if (config.build.bundleAnalyzerReport) { + const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin + webpackConfig.plugins.push(new BundleAnalyzerPlugin()) +} + +module.exports = webpackConfig diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/config/dev.env.js b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/config/dev.env.js new file mode 100644 index 0000000000..1e22973ae7 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/config/dev.env.js @@ -0,0 +1,7 @@ +'use strict' +const merge = require('webpack-merge') +const prodEnv = require('./prod.env') + +module.exports = merge(prodEnv, { + NODE_ENV: '"development"' +}) diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/config/index.js b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/config/index.js new file mode 100644 index 0000000000..c5eded7f81 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/config/index.js @@ -0,0 +1,69 @@ +'use strict' +// Template version: 1.3.1 +// see http://vuejs-templates.github.io/webpack for documentation. + +const path = require('path') + +module.exports = { + dev: { + + // Paths + assetsSubDirectory: 'static', + assetsPublicPath: '/', + proxyTable: {}, + + // Various Dev Server settings + host: 'localhost', // can be overwritten by process.env.HOST + port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined + autoOpenBrowser: false, + errorOverlay: true, + notifyOnErrors: true, + poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- + + + /** + * Source Maps + */ + + // https://webpack.js.org/configuration/devtool/#development + devtool: 'cheap-module-eval-source-map', + + // If you have problems debugging vue-files in devtools, + // set this to false - it *may* help + // https://vue-loader.vuejs.org/en/options.html#cachebusting + cacheBusting: true, + + cssSourceMap: true + }, + + build: { + // Template for index.html + index: path.resolve(__dirname, '../dist/index.html'), + + // Paths + assetsRoot: path.resolve(__dirname, '../dist'), + assetsSubDirectory: 'static', + assetsPublicPath: '/', + + /** + * Source Maps + */ + + productionSourceMap: true, + // https://webpack.js.org/configuration/devtool/#production + devtool: '#source-map', + + // Gzip off by default as many popular static hosts such as + // Surge or Netlify already gzip all static assets for you. + // Before setting to `true`, make sure to: + // npm install --save-dev compression-webpack-plugin + productionGzip: false, + productionGzipExtensions: ['js', 'css'], + + // Run the build command with an extra argument to + // View the bundle analyzer report after build finishes: + // `npm run build --report` + // Set to `true` or `false` to always turn it on or off + bundleAnalyzerReport: process.env.npm_config_report + } +} diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/config/prod.env.js b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/config/prod.env.js new file mode 100644 index 0000000000..a6f997616e --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/config/prod.env.js @@ -0,0 +1,4 @@ +'use strict' +module.exports = { + NODE_ENV: '"production"' +} diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/index.html b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/index.html new file mode 100644 index 0000000000..03ce3fdf27 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/index.html @@ -0,0 +1,12 @@ + + + + + + deplugin-view-frontend + + +
+ + + diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/package.json b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/package.json new file mode 100644 index 0000000000..2fd98cc1aa --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/package.json @@ -0,0 +1,73 @@ +{ + "name": "deplugin-view-frontend", + "version": "1.0.0", + "description": "A Vue.js project", + "author": "", + "private": true, + "scripts": { + "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", + "start": "npm run dev", + "build": "node build/build.js", + "buildPlugin": "node build/build-async-plugins.js" + }, + "dependencies": { + "@riophae/vue-treeselect": "0.4.0", + "highcharts": "^10.0.0", + "vue": "^2.5.2", + "vue-i18n": "7.3.2", + "vue-router": "^3.0.1", + "vue-uuid": "2.0.2", + "vuedraggable": "^2.24.3" + }, + "devDependencies": { + "autoprefixer": "^7.1.2", + "babel-core": "^6.22.1", + "babel-helper-vue-jsx-merge-props": "^2.0.3", + "babel-loader": "^7.1.1", + "babel-plugin-syntax-jsx": "^6.18.0", + "babel-plugin-transform-runtime": "^6.22.0", + "babel-plugin-transform-vue-jsx": "^3.5.0", + "babel-preset-env": "^1.3.2", + "babel-preset-stage-2": "^6.22.0", + "chalk": "^2.0.1", + "copy-webpack-plugin": "^4.6.0", + "css-loader": "^0.28.0", + "element-ui": "2.15.7", + "extract-text-webpack-plugin": "^4.0.0-beta.0", + "file-loader": "^1.1.4", + "friendly-errors-webpack-plugin": "^1.6.1", + "html-webpack-plugin": "^3.2.0", + "js-cookie": "2.2.0", + "node-notifier": "^8.0.1", + "optimize-css-assets-webpack-plugin": "^3.2.0", + "ora": "^1.2.0", + "portfinder": "^1.0.13", + "postcss-import": "^11.0.0", + "postcss-loader": "^2.0.8", + "postcss-url": "^7.2.1", + "rimraf": "^2.6.0", + "sass": "^1.33.0", + "sass-loader": "^7.3.1", + "semver": "^5.3.0", + "shelljs": "^0.8.5", + "svg-sprite-loader": "4.1.3", + "uglifyjs-webpack-plugin": "^1.1.1", + "url-loader": "^0.5.8", + "vue-loader": "^15.6.4", + "vue-style-loader": "^4.1.2", + "vue-template-compiler": "^2.5.2", + "webpack": "^4.8.1", + "webpack-bundle-analyzer": "^3.3.2", + "webpack-dev-server": "^3.1.11", + "webpack-merge": "^4.1.0" + }, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + }, + "browserslist": [ + "> 1%", + "last 2 versions", + "not ie <= 8" + ] +} diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/pom.xml b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/pom.xml new file mode 100644 index 0000000000..c5c4a10107 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/pom.xml @@ -0,0 +1,80 @@ + + + + + io.dataease + view-3dpie + ${dataease.version} + + + 4.0.0 + view-3dpie-frontend + + + UTF-8 + UTF-8 + 1.9.1 + + + + + + maven-clean-plugin + + + + static + + ** + + false + + + + + + + + com.github.eirslett + frontend-maven-plugin + ${frontend-maven-plugin.version} + + + install node and npm + + install-node-and-npm + + + + v16.20.2 + 7.6.3 + + + + + npm install + + npm + + + + install --force + + + + + npm run buildPlugin + + npm + + + run buildPlugin + + + + + + + diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/App.vue b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/App.vue new file mode 100644 index 0000000000..8542e07722 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/App.vue @@ -0,0 +1,23 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/assets/logo.png b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/assets/logo.png new file mode 100644 index 0000000000..f3d2503fc2 Binary files /dev/null and b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/assets/logo.png differ diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/HelloWorld.vue b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/HelloWorld.vue new file mode 100644 index 0000000000..1c19f2a399 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/HelloWorld.vue @@ -0,0 +1,113 @@ + + + + + + diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/SvgIcon/index.vue b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/SvgIcon/index.vue new file mode 100644 index 0000000000..f81b67f667 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/SvgIcon/index.vue @@ -0,0 +1,60 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/selector/BackgroundColorSelector.vue b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/selector/BackgroundColorSelector.vue new file mode 100644 index 0000000000..e1cbf5fe18 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/selector/BackgroundColorSelector.vue @@ -0,0 +1,102 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/selector/ColorSelector.vue b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/selector/ColorSelector.vue new file mode 100644 index 0000000000..326e9c8e77 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/selector/ColorSelector.vue @@ -0,0 +1,290 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/selector/LabelSelector.vue b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/selector/LabelSelector.vue new file mode 100644 index 0000000000..a160bb7231 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/selector/LabelSelector.vue @@ -0,0 +1,165 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/selector/LegendSelector.vue b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/selector/LegendSelector.vue new file mode 100644 index 0000000000..5125412616 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/selector/LegendSelector.vue @@ -0,0 +1,165 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/selector/TitleSelector.vue b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/selector/TitleSelector.vue new file mode 100644 index 0000000000..8cb2e7c160 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/selector/TitleSelector.vue @@ -0,0 +1,164 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/selector/TooltipSelector.vue b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/selector/TooltipSelector.vue new file mode 100644 index 0000000000..fbd16a64e3 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/selector/TooltipSelector.vue @@ -0,0 +1,144 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/views/ChartDragItem.vue b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/views/ChartDragItem.vue new file mode 100644 index 0000000000..53ae1e2ad0 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/views/ChartDragItem.vue @@ -0,0 +1,278 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/views/DimensionExtItem.vue b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/views/DimensionExtItem.vue new file mode 100644 index 0000000000..c414ad2d36 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/views/DimensionExtItem.vue @@ -0,0 +1,256 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/views/DimensionItem.vue b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/views/DimensionItem.vue new file mode 100644 index 0000000000..3e37f94967 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/views/DimensionItem.vue @@ -0,0 +1,256 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/views/DrillItem.vue b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/views/DrillItem.vue new file mode 100644 index 0000000000..7321b8a08c --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/views/DrillItem.vue @@ -0,0 +1,146 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/views/FieldErrorTips.vue b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/views/FieldErrorTips.vue new file mode 100644 index 0000000000..9d7b574721 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/views/FieldErrorTips.vue @@ -0,0 +1,21 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/views/FilterItem.vue b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/views/FilterItem.vue new file mode 100644 index 0000000000..9db047f3e5 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/views/FilterItem.vue @@ -0,0 +1,153 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/views/QuotaExtItem.vue b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/views/QuotaExtItem.vue new file mode 100644 index 0000000000..c88a060a84 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/views/QuotaExtItem.vue @@ -0,0 +1,363 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/views/QuotaItem.vue b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/views/QuotaItem.vue new file mode 100644 index 0000000000..c22da7323a --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/views/QuotaItem.vue @@ -0,0 +1,344 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/views/ViewTrackBar.vue b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/views/ViewTrackBar.vue new file mode 100644 index 0000000000..605c0627b5 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/views/ViewTrackBar.vue @@ -0,0 +1,59 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/views/utils.js b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/views/utils.js new file mode 100644 index 0000000000..0290965c94 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/components/views/utils.js @@ -0,0 +1,34 @@ +export function getItemType(dimensionData, quotaData, item) { + // 将item的字段在数据集维度、指标字段中查询一遍,如果遇到id不存在、字段类型不一致、维度指标不一致,则提示 + const status = item.groupType + let checked = false + if (status === 'd') { + for (let i = 0; i < dimensionData.length; i++) { + const ele = dimensionData[i] + if (ele.id === item.id && ele.deType === item.deType && ele.groupType === item.groupType) { + checked = true + break + } + } + } + if (status === 'q') { + for (let i = 0; i < quotaData.length; i++) { + const ele = quotaData[i] + if (ele.id === item.id && ele.deType === item.deType && ele.groupType === item.groupType) { + checked = true + break + } + } + } + + if (checked) { + if (status === 'd') { + return '' + } else if (status === 'q') { + return 'success' + } + } else { + return 'danger' + } + } + \ No newline at end of file diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/de-base/lang/en.js b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/de-base/lang/en.js new file mode 100644 index 0000000000..6a8f3edb98 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/de-base/lang/en.js @@ -0,0 +1,7 @@ +export default { + plugin_view_3d_pie: { + type_title: '3D-PIE', + label: 'Label', + angle: 'Angle' + } +} \ No newline at end of file diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/de-base/lang/index.js b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/de-base/lang/index.js new file mode 100644 index 0000000000..e50d0478e6 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/de-base/lang/index.js @@ -0,0 +1,49 @@ +import Vue from 'vue' +import VueI18n from 'vue-i18n' +import Cookies from 'js-cookie' +import elementEnLocale from 'element-ui/lib/locale/lang/en' // element-ui lang +import elementZhLocale from 'element-ui/lib/locale/lang/zh-CN'// element-ui lang +import elementTWLocale from 'element-ui/lib/locale/lang/zh-TW'// element-ui lang + +import localMessages from './messages' + + +Vue.use(VueI18n) + +const messages = { + en_US: { + ...localMessages['en_US'], + ...elementEnLocale + }, + zh_CN: { + ...localMessages['zh_CN'], + ...elementZhLocale + }, + zh_TW: { + ...localMessages['zh_TW'], + ...elementTWLocale + } +} +export function getLanguage () { + const chooseLanguage = Cookies.get('language') + if (chooseLanguage) return chooseLanguage + + // if has not choose language + const language = (navigator.language || navigator.browserLanguage).toLowerCase() + const locales = Object.keys(messages) + for (const locale of locales) { + if (language.indexOf(locale) > -1) { + return locale + } + } + return 'zh_CN' +} +const i18n = new VueI18n({ + // set locale + // options: en | zh | es + locale: getLanguage(), + // set locale messages + messages +}) + +export default i18n diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/de-base/lang/messages.js b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/de-base/lang/messages.js new file mode 100644 index 0000000000..844f8c0673 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/de-base/lang/messages.js @@ -0,0 +1,17 @@ +import enLocale from './en' +import zhLocale from './zh' +import twLocale from './tw' + +const messages = { + en_US: { + ...enLocale + }, + zh_CN: { + ...zhLocale + }, + zh_TW: { + ...twLocale + } +} + +export default messages \ No newline at end of file diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/de-base/lang/tw.js b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/de-base/lang/tw.js new file mode 100644 index 0000000000..02e895c68e --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/de-base/lang/tw.js @@ -0,0 +1,7 @@ +export default { + plugin_view_3d_pie: { + type_title: '3D餅圖', + label: '標籤', + angle: '角度' + } +} \ No newline at end of file diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/de-base/lang/zh.js b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/de-base/lang/zh.js new file mode 100644 index 0000000000..cb58cc1f90 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/de-base/lang/zh.js @@ -0,0 +1,7 @@ +export default { + plugin_view_3d_pie: { + type_title: '3D饼图', + label: '标签', + angle: '角度' + } +} diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/icons/index.js b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/icons/index.js new file mode 100644 index 0000000000..2c6b309c96 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/icons/index.js @@ -0,0 +1,9 @@ +import Vue from 'vue' +import SvgIcon from '@/components/SvgIcon'// svg component + +// register globally +Vue.component('svg-icon', SvgIcon) + +const req = require.context('./svg', false, /\.svg$/) +const requireAll = requireContext => requireContext.keys().map(requireContext) +requireAll(req) diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/icons/svg/3d-pie.svg b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/icons/svg/3d-pie.svg new file mode 100644 index 0000000000..d1cccbf81c --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/icons/svg/3d-pie.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/icons/svg/view-3dpie-backend.svg b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/icons/svg/view-3dpie-backend.svg new file mode 100644 index 0000000000..4c3af7e10c --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/icons/svg/view-3dpie-backend.svg @@ -0,0 +1 @@ +【icon】插件管理-导出 diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/icons/svgo.yml b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/icons/svgo.yml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/main.js b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/main.js new file mode 100644 index 0000000000..fb8f35373c --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/main.js @@ -0,0 +1,34 @@ +// The Vue build version to load with the `import` command +// (runtime-only or standalone) has been set in webpack.base.conf with an alias. +import Vue from 'vue' +import App from './App' +import router from './router' +import ElementUI from 'element-ui' +import Cookies from 'js-cookie' +import i18n from './de-base/lang' +import draggable from 'vuedraggable' +import Treeselect from '@riophae/vue-treeselect' +import '@riophae/vue-treeselect/dist/vue-treeselect.css' +import '@/icons' // icon +Vue.config.productionTip = false +Vue.use(ElementUI, { + size: Cookies.get('size') || 'medium', + i18n: (key, value) => i18n.t(key, value) +}) +Vue.component('Treeselect', Treeselect) +Vue.component('draggable', draggable) +Vue.prototype.hasDataPermission = function(pTarget, pSource) { + + if (pSource && pTarget) { + return pSource.indexOf(pTarget) > -1 + } + return false +} +/* eslint-disable no-new */ +new Vue({ + el: '#app', + router, + i18n, + components: { App }, + template: '' +}) diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/router/index.js b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/router/index.js new file mode 100644 index 0000000000..5e83b013fd --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/router/index.js @@ -0,0 +1,21 @@ +import Vue from 'vue' +import Router from 'vue-router' +import HelloWorld from '@/components/HelloWorld' +import Pie3D from '@/views/highcharts/3dpie/type' + +Vue.use(Router) + +export default new Router({ + routes: [ + { + path: '/', + name: 'HelloWorld', + component: HelloWorld + }, + { + path: '/3d-pie', + name: '3d-pie', + component: Pie3D + } + ] +}) diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/utils/3dpie.js b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/utils/3dpie.js new file mode 100644 index 0000000000..46b4ffbf14 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/utils/3dpie.js @@ -0,0 +1,268 @@ +export const DEFAULT_COLOR_CASE = { + value: 'default', + colors: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'], + alpha: 100, + tableHeaderBgColor: '#e1eaff', + tableItemBgColor: '#ffffff', + tableFontColor: '#000000', + tableStripe: true, + dimensionColor: '#000000', + quotaColor: '#000000', + tableBorderColor: '#cfdaf4' +} + +export const COLOR_PANEL = [ + '#ff4500', + '#ff8c00', + '#ffd700', + '#90ee90', + '#00ced1', + '#1e90ff', + '#c71585', + '#999999', + '#000000', + '#FFFFFF' +] + +export const DEFAULT_LABEL = { + show: false, + position: 'top', + color: '#909399', + fontSize: '10', + formatter: '{c}', + gaugeFormatter: '{value}', + labelLine: { + show: true + } +} +export const DEFAULT_TOOLTIP = { + show: true, + trigger: 'item', + confine: true, + textStyle: { + fontSize: '10', + color: '#909399' + }, + formatter: '' +} +export const DEFAULT_TITLE_STYLE = { + show: true, + fontSize: '18', + color: '#303133', + hPosition: 'center', + vPosition: 'top', + isItalic: false, + isBolder: false +} +export const DEFAULT_BACKGROUND_COLOR = { + color: '#ffffff', + alpha: 100, + borderRadius: 5 +} +export const BASE_PIE = { + chart: { + options3d: { + enabled: true, + alpha: 45, + beta: 0 + } + }, + credits: { + enabled: false + }, + title: { + text: '', + style: { + fontWeight: 'normal' + } + }, + legend: {}, + + plotOptions: { + pie: { + allowPointSelect: true, + cursor: 'pointer', + depth: 35, + dataLabels: { + enabled: true + }, + showInLegend: false + } + }, + + + tooltip: {}, + + series: [ + { + name: '', + type: 'pie', + data: [] + } + ] +} + + +let terminalType = 'pc' +export function basePieOption(chart_option, chart, terminal = 'pc') { + console.log('apple......') + terminalType = terminal + let customAttr = {} + if (chart.customAttr) { + customAttr = JSON.parse(chart.customAttr) + if (customAttr.color) { + chart_option.colors = customAttr.color.colors + } + // tooltip + if (customAttr.tooltip) { + const tooltip = JSON.parse(JSON.stringify(customAttr.tooltip)) + const reg = new RegExp('\n', 'g') + tooltip.formatter = tooltip.formatter.replace(reg, '
') + + chart_option.tooltip.enabled = tooltip.show + chart_option.tooltip.style = {fontSize: tooltip.textStyle.fontSize, color: tooltip.textStyle.color} + let formatter = tooltip.formatter + formatter = formatter.replace('{a}', '{series.name}') + formatter = formatter.replace('{b}', '{point.name}') + formatter = formatter.replace('{c}', '{point.y}') + formatter = formatter.replace('{d', '{point.percentage') + chart_option.tooltip.formatter = formatter + + } + + // label + if (customAttr.label) { + const dataLabels = {} + dataLabels.enabled = customAttr.label.show + dataLabels.color = customAttr.label.color + dataLabels.style = {color: customAttr.label.color, fontSize: customAttr.label.fontSize} + const reg = new RegExp('\n', 'g') + let formatter = customAttr.label.formatter.replace(reg, '
') + formatter = formatter.replace('{a}', '{series.name}') + formatter = formatter.replace('{b}', '{point.name}') + formatter = formatter.replace('{c}', '{point.y}') + formatter = formatter.replace('{d', '{point.percentage') + dataLabels.format = formatter + + chart_option.plotOptions.pie.dataLabels = dataLabels + + } + } + + // 处理data + if (chart.data) { + chart_option.title.text = chart.title + if (chart.data.series.length > 0) { + chart_option.series[0].name = chart.data.series[0].name + // size + /*if (customAttr.size) { + chart_option.series[0].radius = [customAttr.size.pieInnerRadius + '%', customAttr.size.pieOuterRadius + '%'] + }*/ + + const valueArr = chart.data.series[0].data + for (let i = 0; i < valueArr.length; i++) { + + const y = valueArr[i] + y.name = chart.data.x[i] + y.y = y.value + + chart_option.series[0].data.push(y) + } + } + } + + componentStyle(chart_option, chart) + return chart_option +} +export function componentStyle(chart_option, chart) { + const padding = '8px' + if (chart.customStyle) { + const customStyle = JSON.parse(chart.customStyle) + + if (customStyle.text) { + chart_option.title.text = customStyle.text.show ? chart.title : '' + const style = chart_option.title.style ? chart_option.title.style : {} + style.fontSize = customStyle.text.fontSize + style.color = customStyle.text.color + customStyle.text.isItalic ? style.fontStyle = 'italic' : style.fontStyle = 'normal' + customStyle.text.isBolder ? style.fontWeight = 'bold' : style.fontWeight = 'normal' + chart_option.title.textStyle = style + chart_option.title.align = customStyle.text.hPosition + chart_option.title.verticalAlign = customStyle.text.vPosition + } + + if (customStyle.legend && chart_option.legend) { + chart_option.plotOptions.pie.showInLegend = customStyle.legend.show + // chart_option.legend.padding = padding + chart_option.legend.layout = customStyle.legend.orient + chart_option.legend.verticalAlign = customStyle.legend.vPosition + chart_option.legend.align = customStyle.legend.hPosition + + chart_option.legend.itemStyle = customStyle.legend.textStyle + + } + + if (customStyle.background) { + + chart_option.chart.backgroundColor = hexColorToRGBA(customStyle.background.color, customStyle.background.alpha) + } + } +} +export function hexColorToRGBA(hex, alpha) { + const rgb = [] // 定义rgb数组 + if (/^\#[0-9A-F]{3}$/i.test(hex)) { // 判断传入是否为#三位十六进制数 + let sixHex = '#' + hex.replace(/[0-9A-F]/ig, function(kw) { + sixHex += kw + kw // 把三位16进制数转化为六位 + }) + hex = sixHex // 保存回hex + } + if (/^#[0-9A-F]{6}$/i.test(hex)) { // 判断传入是否为#六位十六进制数 + hex.replace(/[0-9A-F]{2}/ig, function(kw) { + // eslint-disable-next-line no-eval + rgb.push(eval('0x' + kw)) // 十六进制转化为十进制并存如数组 + }) + return `rgba(${rgb.join(',')},${alpha / 100})` // 输出RGB格式颜色 + } else { + return 'rgb(0,0,0)' + } +} + + + +export const DEFAULT_YAXIS_EXT_STYLE = { + show: true, + position: 'right', + name: '', + nameTextStyle: { + color: '#333333', + fontSize: 12 + }, + axisLabel: { + show: true, + color: '#333333', + fontSize: '12', + rotate: 0, + formatter: '{value}' + }, + splitLine: { + show: true, + lineStyle: { + color: '#cccccc', + width: 1, + style: 'solid' + } + }, + axisValue: { + auto: true, + min: null, + max: null, + split: null, + splitCount: null + } +} + +export function uuid() { + return (((1+Math.random())*0x10000)|0).toString(16).substring(1); +} + diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/utils/compare.js b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/utils/compare.js new file mode 100644 index 0000000000..b06a104a10 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/utils/compare.js @@ -0,0 +1,29 @@ +export const compareItem = { + type: 'none', // year-yoy/month-yoy等 + resultData: 'percent', // 对比差sub,百分比percent等 + field: '', + custom: { + field: '', + calcType: '0', // 0-增长值,1-增长率 + timeType: '0', // 0-固定日期,1-日期区间 + currentTime: '', + compareTime: '', + currentTimeRange: [], + compareTimeRange: [] + } +} + +export const compareYearList = [ + { name: 'year_mom', value: 'year_mom' } +] + +export const compareMonthList = [ + { name: 'month_mom', value: 'month_mom' }, + { name: 'year_yoy', value: 'year_yoy' } +] + +export const compareDayList = [ + { name: 'day_mom', value: 'day_mom' }, + { name: 'month_yoy', value: 'month_yoy' }, + { name: 'year_yoy', value: 'year_yoy' } +] diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/utils/map.js b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/utils/map.js new file mode 100644 index 0000000000..b851cc4d87 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/utils/map.js @@ -0,0 +1,350 @@ +export const DEFAULT_COLOR_CASE = { + value: 'default', + colors: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'], + alpha: 100, + tableHeaderBgColor: '#e1eaff', + tableItemBgColor: '#ffffff', + tableFontColor: '#000000', + tableStripe: true, + dimensionColor: '#000000', + quotaColor: '#000000', + tableBorderColor: '#cfdaf4' +} + +export const COLOR_PANEL = [ + '#ff4500', + '#ff8c00', + '#ffd700', + '#90ee90', + '#00ced1', + '#1e90ff', + '#c71585', + '#999999', + '#000000', + '#FFFFFF' +] + +export const DEFAULT_LABEL = { + show: false, + position: 'top', + color: '#909399', + fontSize: '10', + formatter: '{c}', + gaugeFormatter: '{value}', + labelLine: { + show: true + } + } + export const DEFAULT_TOOLTIP = { + show: true, + trigger: 'item', + confine: true, + textStyle: { + fontSize: '10', + color: '#909399' + }, + formatter: '' + } + export const DEFAULT_TITLE_STYLE = { + show: true, + fontSize: '18', + color: '#303133', + hPosition: 'center', + vPosition: 'top', + isItalic: false, + isBolder: false + } + export const DEFAULT_LEGEND_STYLE = { + show: true, + hPosition: 'center', + vPosition: 'bottom', + orient: 'horizontal', + icon: 'circle', + textStyle: { + color: '#333333', + fontSize: '12' + } + } + export const DEFAULT_BACKGROUND_COLOR = { + color: '#ffffff', + alpha: 100, + borderRadius: 5 + } +export const BASE_MAP = { + title: { + text: '', + textStyle: { + fontWeight: 'normal' + } + }, + visualMap: { + min: 50, + max: 52, + text: ['High', 'Low'], + realtime: false, + calculable: true, + inRange: { + color: ['lightskyblue', 'yellow', 'orangered'] + }, + right: 0 + }, + + tooltip: {}, + geo: [ + { + show: true, + map: 'BUDDLE_MAP_BORDER', + + emphasis: { + disabled: true + }, + itemStyle: { + borderWidth: 2, + borderColor: '#d1d1d1', + borderType: 'solid' + }, + + roam: false + }, + { + show: true, + map: 'BUDDLE_MAP', + label: { + normal: { + show: false + }, + emphasis: { + show: false + } + }, + itemStyle: { + areaColor: '#f3f3f3', + borderType: 'dashed', + borderColor: '#fff' + }, + + roam: false + } + ], + series: [ + { + name: '', + type: 'scatter', + coordinateSystem: 'geo', + data: [] + } + ] +} +const convertData = (mapData, chart) => { + let maxVal = 0 + const k = terminalType === 'pc' ? 30 : 15 + const names = chart.data.x + const results = [] + for (let index = 0; index < names.length; index++) { + const name = names[index]; + const item = chart.data.series[0].data[index] + results.push({name, value: (mapData[name] ? mapData[name].concat(item.value) : []), dimensionList: item.dimensionList, quotaList: item.quotaList}) + maxVal = Math.max(maxVal, item.value) + } + const rate = k / maxVal + + return { + value: results, + rate: rate + } +} + +let terminalType = 'pc' +export function baseMapOption(chart_option, chart, mapData, terminal = 'pc') { + terminalType = terminal + // 处理shape attr + let customAttr = {} + if (chart.customAttr) { + customAttr = JSON.parse(chart.customAttr) + if (customAttr.color) { + chart_option.color = customAttr.color.colors + } + // tooltip + if (customAttr.tooltip) { + const tooltip = JSON.parse(JSON.stringify(customAttr.tooltip)) + const reg = new RegExp('\n', 'g') + const text = tooltip.formatter.replace(reg, '
') + tooltip.formatter = function(params) { + const a = params.seriesName + const b = params.name + const c = params.value ? params.value[2] : '' + return text.replace(new RegExp('{a}', 'g'), a).replace(new RegExp('{b}', 'g'), b).replace(new RegExp('{c}', 'g'), c) + } + chart_option.tooltip = tooltip + } + } + // 处理data + if (chart.data) { + chart_option.title.text = chart.title + if (chart.data.series && chart.data.series.length > 0) { + chart_option.series[0].name = chart.data.series[0].name + // label + if (customAttr.label) { + const text = customAttr.label.formatter + chart_option.series[0].label = customAttr.label + chart_option.series[0].label.formatter = function(params) { + const a = params.seriesName + const b = params.name + const c = params.value ? params.value[2] : '' + return text.replace(new RegExp('{a}', 'g'), a).replace(new RegExp('{b}', 'g'), b).replace(new RegExp('{c}', 'g'), c) + } + chart_option.series[0].labelLine = customAttr.label.labelLine + } + + // visualMap + const valueArr = chart.data.series[0].data + if (valueArr && valueArr.length > 0) { + const values = [] + valueArr.forEach(function(ele) { + values.push(ele.value) + }) + chart_option.visualMap.min = Math.min(...values) + chart_option.visualMap.max = Math.max(...values) + if (chart_option.visualMap.min === chart_option.visualMap.max) { + chart_option.visualMap.min = 0 + } + } else { + chart_option.visualMap.min = 0 + chart_option.visualMap.max = 0 + } + if (chart_option.visualMap.min === 0 && chart_option.visualMap.max === 0) { + chart_option.visualMap.max = 100 + } + // color + if (customAttr.color && customAttr.color.colors) { + chart_option.visualMap.inRange.color = customAttr.color.colors + chart_option.visualMap.inRange.colorAlpha = customAttr.color.alpha / 100 + } + // chart_option.visualMap = null + + const convert = convertData(mapData, chart) + chart_option.series[0].data = convert.value + chart_option.series[0].symbolSize = val => val[2] * convert.rate + + } + } + // console.log(chart_option); + componentStyle(chart_option, chart) + return chart_option +} +export function componentStyle(chart_option, chart) { + const padding = '8px' + if (chart.customStyle) { + const customStyle = JSON.parse(chart.customStyle) + if (customStyle.text) { + chart_option.title.show = customStyle.text.show + // 水平方向 + if (customStyle.text.hPosition === 'left') { + chart_option.title.left = padding + } else if (customStyle.text.hPosition === 'right') { + chart_option.title.right = padding + } else { + chart_option.title.left = customStyle.text.hPosition + } + // 垂直方向 + if (customStyle.text.vPosition === 'top') { + chart_option.title.top = padding + } else if (customStyle.text.vPosition === 'bottom') { + chart_option.title.bottom = padding + } else { + chart_option.title.top = customStyle.text.vPosition + } + const style = chart_option.title.textStyle ? chart_option.title.textStyle : {} + style.fontSize = customStyle.text.fontSize + style.color = customStyle.text.color + customStyle.text.isItalic ? style.fontStyle = 'italic' : style.fontStyle = 'normal' + customStyle.text.isBolder ? style.fontWeight = 'bold' : style.fontWeight = 'normal' + chart_option.title.textStyle = style + } + if (customStyle.legend && chart_option.legend) { + chart_option.legend.show = customStyle.legend.show + // 水平方向 + if (customStyle.legend.hPosition === 'left') { + chart_option.legend.left = padding + } else if (customStyle.legend.hPosition === 'right') { + chart_option.legend.right = padding + } else { + chart_option.legend.left = customStyle.legend.hPosition + } + // 垂直方向 + if (customStyle.legend.vPosition === 'top') { + chart_option.legend.top = padding + } else if (customStyle.legend.vPosition === 'bottom') { + chart_option.legend.bottom = padding + } else { + chart_option.legend.top = customStyle.legend.vPosition + } + chart_option.legend.orient = customStyle.legend.orient + chart_option.legend.icon = customStyle.legend.icon + chart_option.legend.textStyle = customStyle.legend.textStyle + + } + + if (customStyle.background) { + chart_option.backgroundColor = hexColorToRGBA(customStyle.background.color, customStyle.background.alpha) + } + } +} +export function hexColorToRGBA(hex, alpha) { + const rgb = [] // 定义rgb数组 + if (/^\#[0-9A-F]{3}$/i.test(hex)) { // 判断传入是否为#三位十六进制数 + let sixHex = '#' + hex.replace(/[0-9A-F]/ig, function(kw) { + sixHex += kw + kw // 把三位16进制数转化为六位 + }) + hex = sixHex // 保存回hex + } + if (/^#[0-9A-F]{6}$/i.test(hex)) { // 判断传入是否为#六位十六进制数 + hex.replace(/[0-9A-F]{2}/ig, function(kw) { + // eslint-disable-next-line no-eval + rgb.push(eval('0x' + kw)) // 十六进制转化为十进制并存如数组 + }) + return `rgba(${rgb.join(',')},${alpha / 100})` // 输出RGB格式颜色 + } else { + return 'rgb(0,0,0)' + } +} + + + +export const DEFAULT_YAXIS_EXT_STYLE = { + show: true, + position: 'right', + name: '', + nameTextStyle: { + color: '#333333', + fontSize: 12 + }, + axisLabel: { + show: true, + color: '#333333', + fontSize: '12', + rotate: 0, + formatter: '{value}' + }, + splitLine: { + show: true, + lineStyle: { + color: '#cccccc', + width: 1, + style: 'solid' + } + }, + axisValue: { + auto: true, + min: null, + max: null, + split: null, + splitCount: null + } +} + +export function uuid() { + return (((1+Math.random())*0x10000)|0).toString(16).substring(1); +} + diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/utils/validate.js b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/utils/validate.js new file mode 100644 index 0000000000..3e8ffa9448 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/utils/validate.js @@ -0,0 +1,15 @@ +/** + * Created by PanJiaChen on 16/11/18. + */ + +/** + * @param {string} path + * @returns {Boolean} + */ +export function isExternal(path) { + return /^(https?:|mailto:|tel:)/.test(path) || /^(http?:|mailto:|tel:)/.test(path) || path.startsWith('/api/pluginCommon/staticInfo') +} + + + + diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/views/PluginDemo.vue b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/views/PluginDemo.vue new file mode 100644 index 0000000000..1164dfab1d --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/views/PluginDemo.vue @@ -0,0 +1,55 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/views/highcharts/3dpie/data.vue b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/views/highcharts/3dpie/data.vue new file mode 100644 index 0000000000..593b84a29b --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/views/highcharts/3dpie/data.vue @@ -0,0 +1,680 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/views/highcharts/3dpie/index.vue b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/views/highcharts/3dpie/index.vue new file mode 100644 index 0000000000..e938e799da --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/views/highcharts/3dpie/index.vue @@ -0,0 +1,244 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/views/highcharts/3dpie/style.vue b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/views/highcharts/3dpie/style.vue new file mode 100644 index 0000000000..e6c10d8d33 --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/views/highcharts/3dpie/style.vue @@ -0,0 +1,162 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/views/highcharts/3dpie/type.vue b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/views/highcharts/3dpie/type.vue new file mode 100644 index 0000000000..c227610e1f --- /dev/null +++ b/extensions/dataease-extensions-view/view-3dpie/view-3dpie-frontend/src/views/highcharts/3dpie/type.vue @@ -0,0 +1,63 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-bubblemap/build.sh b/extensions/dataease-extensions-view/view-bubblemap/build.sh new file mode 100644 index 0000000000..1ebadcac5f --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/build.sh @@ -0,0 +1,8 @@ +#!/bin/sh +mvn clean package + +cp view-bubblemap-backend/target/view-bubblemap-backend-1.18.3.jar . + +zip -r bubblemap.zip ./view-bubblemap-backend-1.18.3.jar ./plugin.json + +rm -f ./view-bubblemap-backend-1.18.3.jar diff --git a/extensions/dataease-extensions-view/view-bubblemap/plugin.json b/extensions/dataease-extensions-view/view-bubblemap/plugin.json new file mode 100644 index 0000000000..0b54a66529 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/plugin.json @@ -0,0 +1,13 @@ +{ + "name": "气泡地图", + "free": 0, + "store": "thirdpart", + "cost": 0, + "category": "view", + "descript": "ECharts 气泡地图插件", + "version": "1.18.3", + "creator": "DATAEASE", + "moduleName": "view-bubblemap-backend", + "require": "1.10.0", + "dsType": "" +} \ No newline at end of file diff --git a/extensions/dataease-extensions-view/view-bubblemap/pom.xml b/extensions/dataease-extensions-view/view-bubblemap/pom.xml new file mode 100644 index 0000000000..c5f77e0809 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/pom.xml @@ -0,0 +1,22 @@ + + + + dataease-extensions-view + io.dataease + ${dataease.version} + + 4.0.0 + pom + + view-bubblemap + + + + view-bubblemap-frontend + view-bubblemap-backend + + + + diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-backend/pom.xml b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-backend/pom.xml new file mode 100644 index 0000000000..ee13ffb8e8 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-backend/pom.xml @@ -0,0 +1,161 @@ + + + + io.dataease + view-bubblemap + ${dataease.version} + + 4.0.0 + + + 11 + + + view-bubblemap-backend + + + + io.dataease + dataease-plugin-view + + + + + + + src/main/java + + **/*.properties + **/*.xml + + false + + + src/main/resources + + **/* + + false + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 11 + 11 + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.4 + + + **/server/** + **/*.properties + **/Application* + + + + + + maven-clean-plugin + + + + src/main/resources/static + + ** + + false + + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + main-class-placement + generate-resources + + + + + + + + + + + + run + + + + + + + org.apache.maven.plugins + maven-resources-plugin + + + copy-and-filter-allatori-config + package + + copy-resources + + + ${basedir}/target + + + ${basedir}/allatori + + allatori.xml + + true + + + + + + + + org.codehaus.mojo + exec-maven-plugin + 3.1.0 + + + run-allatori + package + + exec + + + + + java + + -Xms128m + -Xmx512m + -jar + ${basedir}/target/classes/allatori/allatori.jar + ${basedir}/target/classes/allatori/allatori.xml + + + + + + + + diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-backend/src/main/java/io/dataease/plugins/view/official/impl/BubbleMapService.java b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-backend/src/main/java/io/dataease/plugins/view/official/impl/BubbleMapService.java new file mode 100644 index 0000000000..49b5635b69 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-backend/src/main/java/io/dataease/plugins/view/official/impl/BubbleMapService.java @@ -0,0 +1,113 @@ +package io.dataease.plugins.view.official.impl; + +import io.dataease.plugins.common.dto.StaticResource; +import io.dataease.plugins.view.entity.PluginViewField; +import io.dataease.plugins.view.entity.PluginViewParam; +import io.dataease.plugins.view.entity.PluginViewType; +import io.dataease.plugins.view.service.ViewPluginService; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.stereotype.Service; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Service +public class BubbleMapService extends ViewPluginService { + + private static final String VIEW_TYPE_VALUE = "buddle-map"; + private static final String[] VIEW_STYLE_PROPERTIES = + { + "color-selector", + "label-selector", + "tooltip-selector", + "title-selector", + "suspension-selector" + }; + + private static final Map VIEW_STYLE_PROPERTY_INNER = new HashMap(); + + static { + VIEW_STYLE_PROPERTY_INNER.put("color-selector", new String[]{"value", "alpha"}); + VIEW_STYLE_PROPERTY_INNER.put("label-selector", new String[]{"show", "fontSize", "color", "position", "formatter"}); + VIEW_STYLE_PROPERTY_INNER.put("tooltip-selector", new String[]{"show", "textStyle", "formatter"}); + VIEW_STYLE_PROPERTY_INNER.put("title-selector", new String[]{"show", "title", "fontSize", "color", "hPosition", "vPosition", "isItalic", "isBolder"}); + VIEW_STYLE_PROPERTY_INNER.put("suspension-selector", new String[]{"show"}); + } + + /*下版这些常量移到sdk*/ + private static final String TYPE = "-type"; + private static final String DATA = "-data"; + private static final String STYLE = "-style"; + private static final String VIEW = "-view"; + private static final String SUFFIX = "svg"; + /*下版这些常量移到sdk*/ + private static final String VIEW_TYPE = VIEW_TYPE_VALUE + TYPE; + private static final String VIEW_DATA = VIEW_TYPE_VALUE + DATA; + private static final String VIEW_STYLE = VIEW_TYPE_VALUE + STYLE; + private static final String VIEW_VIEW = VIEW_TYPE_VALUE + VIEW; + + + @Override + public PluginViewType viewType() { + PluginViewType pluginViewType = new PluginViewType(); + pluginViewType.setRender("echarts"); + pluginViewType.setCategory("chart.chart_type_space"); + pluginViewType.setValue(VIEW_TYPE_VALUE); + pluginViewType.setProperties(VIEW_STYLE_PROPERTIES); + pluginViewType.setPropertyInner(VIEW_STYLE_PROPERTY_INNER); + return pluginViewType; + } + + @Override + public Object format(Object o) { + return null; + } + + @Override + public List components() { + List results = new ArrayList<>(); + results.add(VIEW_VIEW); + results.add(VIEW_DATA); + results.add(VIEW_TYPE); + results.add(VIEW_STYLE); + return results; + } + + @Override + public List staticResources() { + List results = new ArrayList<>(); + StaticResource staticResource = new StaticResource(); + staticResource.setName(VIEW_TYPE_VALUE); + staticResource.setSuffix(SUFFIX); + results.add(staticResource); + results.add(pluginSvg()); + return results; + } + + private StaticResource pluginSvg() { + StaticResource staticResource = new StaticResource(); + staticResource.setName("view-bubblemap-backend"); + staticResource.setSuffix("svg"); + return staticResource; + } + + @Override + protected InputStream readContent(String s) { + InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("static/" + s); + return resourceAsStream; + } + + @Override + public String generateSQL(PluginViewParam param) { + List xAxis = param.getFieldsByType("xAxis"); + List yAxis = param.getFieldsByType("yAxis"); + if (CollectionUtils.isEmpty(xAxis) || CollectionUtils.isEmpty(yAxis)) { + return null; + } + return super.generateSQL(param); + } + +} diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-backend/src/main/resources/allatori/allatori.xml b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-backend/src/main/resources/allatori/allatori.xml new file mode 100644 index 0000000000..f97cb84fef --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-backend/src/main/resources/allatori/allatori.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-backend/src/main/resources/application.properties b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-backend/src/main/resources/application.properties new file mode 100644 index 0000000000..0e9c1c78c1 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-backend/src/main/resources/application.properties @@ -0,0 +1,28 @@ +server.port=8083 +# Hikari +spring.datasource.type=com.zaxxer.hikari.HikariDataSource + +# 数据库配置 +spring.datasource.url=jdbc:mysql://rm-uf6m17586w7yuwm805o.mysql.rds.aliyuncs.com:3306/dataease?autoReconnect=false&useUnicode=true&characterEncoding=UTF-8&characterSetResults=UTF-8&zeroDateTimeBehavior=convertToNull&useSSL=false +spring.datasource.username=root +spring.datasource.password=Password123@mysql +spring.datasource.hikari.minimum-idle=5 +spring.datasource.hikari.maximum-pool-size=15 +spring.datasource.hikari.auto-commit=true +spring.datasource.hikari.idle-timeout=30000 +spring.datasource.hikari.pool-name=DatebookHikariCP +spring.datasource.hikari.max-lifetime=1800000 +spring.datasource.hikari.connection-timeout=30000 +spring.datasource.hikari.connection-test-query=SELECT 1 + +# mybatis +mybatis.configuration.cache-enabled=true +mybatis.configuration.lazy-loading-enabled=false +mybatis.configuration.aggressive-lazy-loading=true +mybatis.configuration.multiple-result-sets-enabled=true +mybatis.configuration.use-column-label=true +mybatis.configuration.auto-mapping-behavior=full +mybatis.configuration.default-statement-timeout=25000 +mybatis.configuration.map-underscore-to-camel-case=true + + diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/.babelrc b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/.babelrc new file mode 100644 index 0000000000..3a280ba34b --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/.babelrc @@ -0,0 +1,12 @@ +{ + "presets": [ + ["env", { + "modules": false, + "targets": { + "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] + } + }], + "stage-2" + ], + "plugins": ["transform-vue-jsx", "transform-runtime"] +} diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/.editorconfig b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/.editorconfig new file mode 100644 index 0000000000..9d08a1a828 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/.gitignore b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/.gitignore new file mode 100644 index 0000000000..541a820f6c --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/.gitignore @@ -0,0 +1,14 @@ +.DS_Store +node_modules/ +/dist/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/.postcssrc.js b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/.postcssrc.js new file mode 100644 index 0000000000..eee3e92d7f --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/.postcssrc.js @@ -0,0 +1,10 @@ +// https://github.com/michael-ciniawsky/postcss-load-config + +module.exports = { + "plugins": { + "postcss-import": {}, + "postcss-url": {}, + // to edit target browsers: use "browserslist" field in package.json + "autoprefixer": {} + } +} diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/README.md b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/README.md new file mode 100644 index 0000000000..c1e4be95e0 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/README.md @@ -0,0 +1,21 @@ +# deplugin-view-frontend + +> A Vue.js project + +## Build Setup + +``` bash +# install dependencies +npm install + +# serve with hot reload at localhost:8080 +npm run dev + +# build for production with minification +npm run build + +# build for production and view the bundle analyzer report +npm run build --report +``` + +For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader). diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/build/build-async-plugins.js b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/build/build-async-plugins.js new file mode 100644 index 0000000000..2303854531 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/build/build-async-plugins.js @@ -0,0 +1,35 @@ +'use strict' +require('./check-versions')() + +process.env.NODE_ENV = 'production' + +const ora = require('ora') +const chalk = require('chalk') +const webpack = require('webpack') +const webpackConfig = require('./webpack.async-plugins') + +const spinner = ora('building for sync-plugins...') +spinner.start() + +webpack(webpackConfig, function (err, stats) { + spinner.stop() + if (err) throw err + process.stdout.write(stats.toString({ + colors: true, + modules: false, + children: false, + chunks: false, + chunkModules: false + }) + '\n\n') + + if (stats.hasErrors()) { + console.log(chalk.red(' Build failed with errors.\n')) + process.exit(1) + } + + console.log(chalk.cyan(' Build complete.\n')) + console.log(chalk.yellow( + ' Tip: built files are meant to be served over an HTTP server.\n' + + ' Opening index.html over file:// won\'t work.\n' + )) +}) diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/build/build.js b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/build/build.js new file mode 100644 index 0000000000..8f2ad8ad49 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/build/build.js @@ -0,0 +1,41 @@ +'use strict' +require('./check-versions')() + +process.env.NODE_ENV = 'production' + +const ora = require('ora') +const rm = require('rimraf') +const path = require('path') +const chalk = require('chalk') +const webpack = require('webpack') +const config = require('../config') +const webpackConfig = require('./webpack.prod.conf') + +const spinner = ora('building for production...') +spinner.start() + +rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { + if (err) throw err + webpack(webpackConfig, (err, stats) => { + spinner.stop() + if (err) throw err + process.stdout.write(stats.toString({ + colors: true, + modules: false, + children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build. + chunks: false, + chunkModules: false + }) + '\n\n') + + if (stats.hasErrors()) { + console.log(chalk.red(' Build failed with errors.\n')) + process.exit(1) + } + + console.log(chalk.cyan(' Build complete.\n')) + console.log(chalk.yellow( + ' Tip: built files are meant to be served over an HTTP server.\n' + + ' Opening index.html over file:// won\'t work.\n' + )) + }) +}) diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/build/check-versions.js b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/build/check-versions.js new file mode 100644 index 0000000000..3ef972a08d --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/build/check-versions.js @@ -0,0 +1,54 @@ +'use strict' +const chalk = require('chalk') +const semver = require('semver') +const packageConfig = require('../package.json') +const shell = require('shelljs') + +function exec (cmd) { + return require('child_process').execSync(cmd).toString().trim() +} + +const versionRequirements = [ + { + name: 'node', + currentVersion: semver.clean(process.version), + versionRequirement: packageConfig.engines.node + } +] + +if (shell.which('npm')) { + versionRequirements.push({ + name: 'npm', + currentVersion: exec('npm --version'), + versionRequirement: packageConfig.engines.npm + }) +} + +module.exports = function () { + const warnings = [] + + for (let i = 0; i < versionRequirements.length; i++) { + const mod = versionRequirements[i] + + if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { + warnings.push(mod.name + ': ' + + chalk.red(mod.currentVersion) + ' should be ' + + chalk.green(mod.versionRequirement) + ) + } + } + + if (warnings.length) { + console.log('') + console.log(chalk.yellow('To use this template, you must update following to modules:')) + console.log() + + for (let i = 0; i < warnings.length; i++) { + const warning = warnings[i] + console.log(' ' + warning) + } + + console.log() + process.exit(1) + } +} diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/build/logo.png b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/build/logo.png new file mode 100644 index 0000000000..f3d2503fc2 Binary files /dev/null and b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/build/logo.png differ diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/build/utils.js b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/build/utils.js new file mode 100644 index 0000000000..e534fb0fd6 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/build/utils.js @@ -0,0 +1,101 @@ +'use strict' +const path = require('path') +const config = require('../config') +const ExtractTextPlugin = require('extract-text-webpack-plugin') +const packageConfig = require('../package.json') + +exports.assetsPath = function (_path) { + const assetsSubDirectory = process.env.NODE_ENV === 'production' + ? config.build.assetsSubDirectory + : config.dev.assetsSubDirectory + + return path.posix.join(assetsSubDirectory, _path) +} + +exports.cssLoaders = function (options) { + options = options || {} + + const cssLoader = { + loader: 'css-loader', + options: { + sourceMap: options.sourceMap + } + } + + const postcssLoader = { + loader: 'postcss-loader', + options: { + sourceMap: options.sourceMap + } + } + + // generate loader string to be used with extract text plugin + function generateLoaders (loader, loaderOptions) { + const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader] + + if (loader) { + loaders.push({ + loader: loader + '-loader', + options: Object.assign({}, loaderOptions, { + sourceMap: options.sourceMap + }) + }) + } + + // Extract CSS when that option is specified + // (which is the case during production build) + if (options.extract) { + return ExtractTextPlugin.extract({ + use: loaders, + fallback: 'vue-style-loader' + }) + } else { + return ['vue-style-loader'].concat(loaders) + } + } + + // https://vue-loader.vuejs.org/en/configurations/extract-css.html + return { + css: generateLoaders(), + postcss: generateLoaders(), + less: generateLoaders('less'), + sass: generateLoaders('sass', { indentedSyntax: true }), + scss: generateLoaders('sass'), + stylus: generateLoaders('stylus'), + styl: generateLoaders('stylus') + } +} + +// Generate loaders for standalone style files (outside of .vue) +exports.styleLoaders = function (options) { + const output = [] + const loaders = exports.cssLoaders(options) + + for (const extension in loaders) { + const loader = loaders[extension] + output.push({ + test: new RegExp('\\.' + extension + '$'), + use: loader + }) + } + + return output +} + +exports.createNotifierCallback = () => { + const notifier = require('node-notifier') + + return (severity, errors) => { + if (severity !== 'error') return + + const error = errors[0] + const filename = error.file && error.file.split('!').pop() + + notifier.notify({ + title: packageConfig.name, + message: severity + ': ' + error.name, + subtitle: filename || '', + icon: path.join(__dirname, 'logo.png') + }) + } +} diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/build/vue-loader.conf.js b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/build/vue-loader.conf.js new file mode 100644 index 0000000000..33ed58bc0a --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/build/vue-loader.conf.js @@ -0,0 +1,22 @@ +'use strict' +const utils = require('./utils') +const config = require('../config') +const isProduction = process.env.NODE_ENV === 'production' +const sourceMapEnabled = isProduction + ? config.build.productionSourceMap + : config.dev.cssSourceMap + +module.exports = { + loaders: utils.cssLoaders({ + sourceMap: sourceMapEnabled, + extract: isProduction + }), + cssSourceMap: sourceMapEnabled, + cacheBusting: config.dev.cacheBusting, + transformToRequire: { + video: ['src', 'poster'], + source: 'src', + img: 'src', + image: 'xlink:href' + } +} diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/build/webpack.async-plugins.js b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/build/webpack.async-plugins.js new file mode 100644 index 0000000000..12d537649b --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/build/webpack.async-plugins.js @@ -0,0 +1,97 @@ +const webpack = require('webpack') +const path = require('path') +const utils = require('./utils') +const CopyPlugin = require("copy-webpack-plugin"); +const VueLoaderPlugin = require('vue-loader/lib/plugin'); +function resolve (dir) { + return path.join(__dirname, '..', dir) +} + +module.exports = { + mode: 'development', + entry: { + + 'buddle-map-view': resolve('/src/views/echarts/map/buddle/index.vue'), + 'buddle-map-data': resolve('/src/views/echarts/map/buddle/data.vue'), + 'buddle-map-type': resolve('/src/views/echarts/map/buddle/type.vue'), + 'buddle-map-style': resolve('/src/views/echarts/map/buddle/style.vue') + + }, + output: { + path: resolve('/static/'), + filename: '[name].js' + }, + resolve: { + extensions: ['.js', '.vue', '.json'], + alias: { + 'vue$': 'vue/dist/vue.esm.js', + '@': resolve('src') + } + }, + externals: { + vue: 'vue' + }, + module: { + rules: [ + { + test: /\.vue$/, + loader: 'vue-loader', + options: { + transformAssetUrls: { + video: 'src', + source: 'src', + img: 'src', + image: 'xlink:href' + } + } + }, + { + test: /.(sa|sc|c)ss$/, + use: [ + {loader: 'vue-style-loader'}, + 'css-loader', + 'sass-loader' + ] + }, + { + test: /\.js$/, + loader: 'babel-loader', + include: [resolve('src'), resolve('test')] + }, + { + test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('img/[name].[hash:7].[ext]') + } + }, + { + test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('media/[name].[hash:7].[ext]') + } + }, + { + test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('fonts/[name].[hash:7].[ext]') + } + } + ] + }, + plugins: [ + new VueLoaderPlugin(), + new webpack.DefinePlugin({ + 'process.env.NODE_ENV': '"production"' + }), + new CopyPlugin([ + {from: 'src/icons/svg/'} + ]) + + ] +} diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/build/webpack.base.conf.js b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/build/webpack.base.conf.js new file mode 100644 index 0000000000..6ccc02dab4 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/build/webpack.base.conf.js @@ -0,0 +1,91 @@ +'use strict' +const path = require('path') +const utils = require('./utils') +const config = require('../config') +const vueLoaderConfig = require('./vue-loader.conf') + +function resolve (dir) { + return path.join(__dirname, '..', dir) +} + + + +module.exports = { + context: path.resolve(__dirname, '../'), + entry: { + app: './src/main.js' + }, + output: { + path: config.build.assetsRoot, + filename: '[name].js', + publicPath: process.env.NODE_ENV === 'production' + ? config.build.assetsPublicPath + : config.dev.assetsPublicPath + }, + resolve: { + extensions: ['.js', '.vue', '.json'], + alias: { + 'vue$': 'vue/dist/vue.esm.js', + '@': resolve('src'), + } + }, + module: { + rules: [ + { + test: /\.vue$/, + loader: 'vue-loader', + options: vueLoaderConfig + }, + { + test: /\.js$/, + loader: 'babel-loader', + include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')] + }, + { + test: /\.svg$/, + loader: 'svg-sprite-loader', + include: [resolve('src/icons')], + options: { + symbolId: 'icon-[name]' + } + }, + { + test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, + loader: 'url-loader', + exclude: [resolve('src/icons')], + options: { + limit: 10000, + name: utils.assetsPath('img/[name].[hash:7].[ext]') + } + }, + { + test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('media/[name].[hash:7].[ext]') + } + }, + { + test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('fonts/[name].[hash:7].[ext]') + } + } + ] + }, + node: { + // prevent webpack from injecting useless setImmediate polyfill because Vue + // source contains it (although only uses it if it's native). + setImmediate: false, + // prevent webpack from injecting mocks to Node native modules + // that does not make sense for the client + dgram: 'empty', + fs: 'empty', + net: 'empty', + tls: 'empty', + child_process: 'empty' + } +} diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/build/webpack.dev.conf.js b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/build/webpack.dev.conf.js new file mode 100755 index 0000000000..070ae221f3 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/build/webpack.dev.conf.js @@ -0,0 +1,95 @@ +'use strict' +const utils = require('./utils') +const webpack = require('webpack') +const config = require('../config') +const merge = require('webpack-merge') +const path = require('path') +const baseWebpackConfig = require('./webpack.base.conf') +const CopyWebpackPlugin = require('copy-webpack-plugin') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') +const portfinder = require('portfinder') + +const HOST = process.env.HOST +const PORT = process.env.PORT && Number(process.env.PORT) + +const devWebpackConfig = merge(baseWebpackConfig, { + module: { + rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) + }, + // cheap-module-eval-source-map is faster for development + devtool: config.dev.devtool, + + // these devServer options should be customized in /config/index.js + devServer: { + clientLogLevel: 'warning', + historyApiFallback: { + rewrites: [ + { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') }, + ], + }, + hot: true, + contentBase: false, // since we use CopyWebpackPlugin. + compress: true, + host: HOST || config.dev.host, + port: PORT || config.dev.port, + open: config.dev.autoOpenBrowser, + overlay: config.dev.errorOverlay + ? { warnings: false, errors: true } + : false, + publicPath: config.dev.assetsPublicPath, + proxy: config.dev.proxyTable, + quiet: true, // necessary for FriendlyErrorsPlugin + watchOptions: { + poll: config.dev.poll, + } + }, + plugins: [ + new webpack.DefinePlugin({ + 'process.env': require('../config/dev.env') + }), + new webpack.HotModuleReplacementPlugin(), + new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update. + new webpack.NoEmitOnErrorsPlugin(), + // https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: 'index.html', + template: 'index.html', + inject: true + }), + // copy custom static assets + new CopyWebpackPlugin([ + { + from: path.resolve(__dirname, '../static'), + to: config.dev.assetsSubDirectory, + ignore: ['.*'] + } + ]) + ] +}) + +module.exports = new Promise((resolve, reject) => { + portfinder.basePort = process.env.PORT || config.dev.port + portfinder.getPort((err, port) => { + if (err) { + reject(err) + } else { + // publish the new Port, necessary for e2e tests + process.env.PORT = port + // add port to devServer config + devWebpackConfig.devServer.port = port + + // Add FriendlyErrorsPlugin + devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({ + compilationSuccessInfo: { + messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`], + }, + onErrors: config.dev.notifyOnErrors + ? utils.createNotifierCallback() + : undefined + })) + + resolve(devWebpackConfig) + } + }) +}) diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/build/webpack.prod.conf.js b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/build/webpack.prod.conf.js new file mode 100644 index 0000000000..3e25238ef6 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/build/webpack.prod.conf.js @@ -0,0 +1,145 @@ +'use strict' +const path = require('path') +const utils = require('./utils') +const webpack = require('webpack') +const config = require('../config') +const merge = require('webpack-merge') +const baseWebpackConfig = require('./webpack.base.conf') +const CopyWebpackPlugin = require('copy-webpack-plugin') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const ExtractTextPlugin = require('extract-text-webpack-plugin') +const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') +const UglifyJsPlugin = require('uglifyjs-webpack-plugin') + +const env = require('../config/prod.env') + +const webpackConfig = merge(baseWebpackConfig, { + module: { + rules: utils.styleLoaders({ + sourceMap: config.build.productionSourceMap, + extract: true, + usePostCSS: true + }) + }, + devtool: config.build.productionSourceMap ? config.build.devtool : false, + output: { + path: config.build.assetsRoot, + filename: utils.assetsPath('js/[name].[chunkhash].js'), + chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') + }, + plugins: [ + // http://vuejs.github.io/vue-loader/en/workflow/production.html + new webpack.DefinePlugin({ + 'process.env': env + }), + new UglifyJsPlugin({ + uglifyOptions: { + compress: { + warnings: false + } + }, + sourceMap: config.build.productionSourceMap, + parallel: true + }), + // extract css into its own file + new ExtractTextPlugin({ + filename: utils.assetsPath('css/[name].[contenthash].css'), + // Setting the following option to `false` will not extract CSS from codesplit chunks. + // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack. + // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, + // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110 + allChunks: true, + }), + // Compress extracted CSS. We are using this plugin so that possible + // duplicated CSS from different components can be deduped. + new OptimizeCSSPlugin({ + cssProcessorOptions: config.build.productionSourceMap + ? { safe: true, map: { inline: false } } + : { safe: true } + }), + // generate dist index.html with correct asset hash for caching. + // you can customize output by editing /index.html + // see https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: config.build.index, + template: 'index.html', + inject: true, + minify: { + removeComments: true, + collapseWhitespace: true, + removeAttributeQuotes: true + // more options: + // https://github.com/kangax/html-minifier#options-quick-reference + }, + // necessary to consistently work with multiple chunks via CommonsChunkPlugin + chunksSortMode: 'dependency' + }), + // keep module.id stable when vendor modules does not change + new webpack.HashedModuleIdsPlugin(), + // enable scope hoisting + new webpack.optimize.ModuleConcatenationPlugin(), + // split vendor js into its own file + new webpack.optimize.CommonsChunkPlugin({ + name: 'vendor', + minChunks (module) { + // any required modules inside node_modules are extracted to vendor + return ( + module.resource && + /\.js$/.test(module.resource) && + module.resource.indexOf( + path.join(__dirname, '../node_modules') + ) === 0 + ) + } + }), + // extract webpack runtime and module manifest to its own file in order to + // prevent vendor hash from being updated whenever app bundle is updated + new webpack.optimize.CommonsChunkPlugin({ + name: 'manifest', + minChunks: Infinity + }), + // This instance extracts shared chunks from code split chunks and bundles them + // in a separate chunk, similar to the vendor chunk + // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk + new webpack.optimize.CommonsChunkPlugin({ + name: 'app', + async: 'vendor-async', + children: true, + minChunks: 3 + }), + + // copy custom static assets + new CopyWebpackPlugin([ + { + from: path.resolve(__dirname, '../static'), + to: config.build.assetsSubDirectory, + ignore: ['.*'] + } + ]) + ] +}) + +if (config.build.productionGzip) { + const CompressionWebpackPlugin = require('compression-webpack-plugin') + + webpackConfig.plugins.push( + new CompressionWebpackPlugin({ + asset: '[path].gz[query]', + algorithm: 'gzip', + test: new RegExp( + '\\.(' + + config.build.productionGzipExtensions.join('|') + + ')$' + ), + threshold: 10240, + minRatio: 0.8 + }) + ) +} + +if (config.build.bundleAnalyzerReport) { + const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin + webpackConfig.plugins.push(new BundleAnalyzerPlugin()) +} + +module.exports = webpackConfig diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/config/dev.env.js b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/config/dev.env.js new file mode 100644 index 0000000000..1e22973ae7 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/config/dev.env.js @@ -0,0 +1,7 @@ +'use strict' +const merge = require('webpack-merge') +const prodEnv = require('./prod.env') + +module.exports = merge(prodEnv, { + NODE_ENV: '"development"' +}) diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/config/index.js b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/config/index.js new file mode 100644 index 0000000000..c5eded7f81 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/config/index.js @@ -0,0 +1,69 @@ +'use strict' +// Template version: 1.3.1 +// see http://vuejs-templates.github.io/webpack for documentation. + +const path = require('path') + +module.exports = { + dev: { + + // Paths + assetsSubDirectory: 'static', + assetsPublicPath: '/', + proxyTable: {}, + + // Various Dev Server settings + host: 'localhost', // can be overwritten by process.env.HOST + port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined + autoOpenBrowser: false, + errorOverlay: true, + notifyOnErrors: true, + poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- + + + /** + * Source Maps + */ + + // https://webpack.js.org/configuration/devtool/#development + devtool: 'cheap-module-eval-source-map', + + // If you have problems debugging vue-files in devtools, + // set this to false - it *may* help + // https://vue-loader.vuejs.org/en/options.html#cachebusting + cacheBusting: true, + + cssSourceMap: true + }, + + build: { + // Template for index.html + index: path.resolve(__dirname, '../dist/index.html'), + + // Paths + assetsRoot: path.resolve(__dirname, '../dist'), + assetsSubDirectory: 'static', + assetsPublicPath: '/', + + /** + * Source Maps + */ + + productionSourceMap: true, + // https://webpack.js.org/configuration/devtool/#production + devtool: '#source-map', + + // Gzip off by default as many popular static hosts such as + // Surge or Netlify already gzip all static assets for you. + // Before setting to `true`, make sure to: + // npm install --save-dev compression-webpack-plugin + productionGzip: false, + productionGzipExtensions: ['js', 'css'], + + // Run the build command with an extra argument to + // View the bundle analyzer report after build finishes: + // `npm run build --report` + // Set to `true` or `false` to always turn it on or off + bundleAnalyzerReport: process.env.npm_config_report + } +} diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/config/prod.env.js b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/config/prod.env.js new file mode 100644 index 0000000000..a6f997616e --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/config/prod.env.js @@ -0,0 +1,4 @@ +'use strict' +module.exports = { + NODE_ENV: '"production"' +} diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/index.html b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/index.html new file mode 100644 index 0000000000..03ce3fdf27 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/index.html @@ -0,0 +1,12 @@ + + + + + + deplugin-view-frontend + + +
+ + + diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/package.json b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/package.json new file mode 100644 index 0000000000..9f3f16ac31 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/package.json @@ -0,0 +1,72 @@ +{ + "name": "deplugin-view-frontend", + "version": "1.0.0", + "description": "A Vue.js project", + "author": "", + "private": true, + "scripts": { + "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", + "start": "npm run dev", + "build": "node build/build.js", + "buildPlugin": "node build/build-async-plugins.js" + }, + "dependencies": { + "@riophae/vue-treeselect": "0.4.0", + "vue": "^2.5.2", + "vue-i18n": "7.3.2", + "vue-router": "^3.0.1", + "vue-uuid": "2.0.2", + "vuedraggable": "^2.24.3" + }, + "devDependencies": { + "autoprefixer": "^7.1.2", + "babel-core": "^6.22.1", + "babel-helper-vue-jsx-merge-props": "^2.0.3", + "babel-loader": "^7.1.1", + "babel-plugin-syntax-jsx": "^6.18.0", + "babel-plugin-transform-runtime": "^6.22.0", + "babel-plugin-transform-vue-jsx": "^3.5.0", + "babel-preset-env": "^1.3.2", + "babel-preset-stage-2": "^6.22.0", + "chalk": "^2.0.1", + "copy-webpack-plugin": "^4.6.0", + "css-loader": "^0.28.0", + "element-ui": "2.15.7", + "extract-text-webpack-plugin": "^4.0.0-beta.0", + "file-loader": "^1.1.4", + "friendly-errors-webpack-plugin": "^1.6.1", + "html-webpack-plugin": "^3.2.0", + "js-cookie": "2.2.0", + "node-notifier": "^8.0.1", + "optimize-css-assets-webpack-plugin": "^3.2.0", + "ora": "^1.2.0", + "portfinder": "^1.0.13", + "postcss-import": "^11.0.0", + "postcss-loader": "^2.0.8", + "postcss-url": "^7.2.1", + "rimraf": "^2.6.0", + "sass": "^1.33.0", + "sass-loader": "^7.3.1", + "semver": "^5.3.0", + "shelljs": "^0.8.5", + "svg-sprite-loader": "4.1.3", + "uglifyjs-webpack-plugin": "^1.1.1", + "url-loader": "^0.5.8", + "vue-loader": "^15.6.4", + "vue-style-loader": "^4.1.2", + "vue-template-compiler": "^2.5.2", + "webpack": "^4.8.1", + "webpack-bundle-analyzer": "^3.3.2", + "webpack-dev-server": "^3.1.11", + "webpack-merge": "^4.1.0" + }, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + }, + "browserslist": [ + "> 1%", + "last 2 versions", + "not ie <= 8" + ] +} diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/pom.xml b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/pom.xml new file mode 100644 index 0000000000..aa8c91048e --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/pom.xml @@ -0,0 +1,80 @@ + + + + + io.dataease + view-bubblemap + ${dataease.version} + + + 4.0.0 + view-bubblemap-frontend + + + UTF-8 + UTF-8 + 1.9.1 + + + + + + maven-clean-plugin + + + + static + + ** + + false + + + + + + + + com.github.eirslett + frontend-maven-plugin + ${frontend-maven-plugin.version} + + + install node and npm + + install-node-and-npm + + + + v16.20.2 + 7.6.3 + + + + + npm install + + npm + + + + install --force + + + + + npm run buildPlugin + + npm + + + run buildPlugin + + + + + + + diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/App.vue b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/App.vue new file mode 100644 index 0000000000..8542e07722 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/App.vue @@ -0,0 +1,23 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/assets/logo.png b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/assets/logo.png new file mode 100644 index 0000000000..f3d2503fc2 Binary files /dev/null and b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/assets/logo.png differ diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/HelloWorld.vue b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/HelloWorld.vue new file mode 100644 index 0000000000..1c19f2a399 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/HelloWorld.vue @@ -0,0 +1,113 @@ + + + + + + diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/SvgIcon/index.vue b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/SvgIcon/index.vue new file mode 100644 index 0000000000..0446ae9079 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/SvgIcon/index.vue @@ -0,0 +1,68 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/selector/BackgroundColorSelector.vue b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/selector/BackgroundColorSelector.vue new file mode 100644 index 0000000000..2b63c34b80 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/selector/BackgroundColorSelector.vue @@ -0,0 +1,102 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/selector/ColorSelector.vue b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/selector/ColorSelector.vue new file mode 100644 index 0000000000..818c86da35 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/selector/ColorSelector.vue @@ -0,0 +1,335 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/selector/LabelSelector.vue b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/selector/LabelSelector.vue new file mode 100644 index 0000000000..b50acc1763 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/selector/LabelSelector.vue @@ -0,0 +1,203 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/selector/SuspensionSelector.vue b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/selector/SuspensionSelector.vue new file mode 100644 index 0000000000..547d0bcad0 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/selector/SuspensionSelector.vue @@ -0,0 +1,116 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/selector/TitleSelector.vue b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/selector/TitleSelector.vue new file mode 100644 index 0000000000..ce0c34bedc --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/selector/TitleSelector.vue @@ -0,0 +1,164 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/selector/TooltipSelector.vue b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/selector/TooltipSelector.vue new file mode 100644 index 0000000000..af8cab1259 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/selector/TooltipSelector.vue @@ -0,0 +1,159 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/views/ChartDragItem.vue b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/views/ChartDragItem.vue new file mode 100644 index 0000000000..53ae1e2ad0 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/views/ChartDragItem.vue @@ -0,0 +1,278 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/views/DimensionExtItem.vue b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/views/DimensionExtItem.vue new file mode 100644 index 0000000000..c414ad2d36 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/views/DimensionExtItem.vue @@ -0,0 +1,256 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/views/DimensionItem.vue b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/views/DimensionItem.vue new file mode 100644 index 0000000000..156e95f5c0 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/views/DimensionItem.vue @@ -0,0 +1,256 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/views/DrillItem.vue b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/views/DrillItem.vue new file mode 100644 index 0000000000..4049ac49c0 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/views/DrillItem.vue @@ -0,0 +1,146 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/views/FieldErrorTips.vue b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/views/FieldErrorTips.vue new file mode 100644 index 0000000000..9d7b574721 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/views/FieldErrorTips.vue @@ -0,0 +1,21 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/views/FilterItem.vue b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/views/FilterItem.vue new file mode 100644 index 0000000000..3dd5952d69 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/views/FilterItem.vue @@ -0,0 +1,154 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/views/QuotaExtItem.vue b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/views/QuotaExtItem.vue new file mode 100644 index 0000000000..c88a060a84 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/views/QuotaExtItem.vue @@ -0,0 +1,363 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/views/QuotaItem.vue b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/views/QuotaItem.vue new file mode 100644 index 0000000000..b081f99c2f --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/views/QuotaItem.vue @@ -0,0 +1,345 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/views/ViewTrackBar.vue b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/views/ViewTrackBar.vue new file mode 100644 index 0000000000..605c0627b5 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/views/ViewTrackBar.vue @@ -0,0 +1,59 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/views/utils.js b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/views/utils.js new file mode 100644 index 0000000000..0290965c94 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/components/views/utils.js @@ -0,0 +1,34 @@ +export function getItemType(dimensionData, quotaData, item) { + // 将item的字段在数据集维度、指标字段中查询一遍,如果遇到id不存在、字段类型不一致、维度指标不一致,则提示 + const status = item.groupType + let checked = false + if (status === 'd') { + for (let i = 0; i < dimensionData.length; i++) { + const ele = dimensionData[i] + if (ele.id === item.id && ele.deType === item.deType && ele.groupType === item.groupType) { + checked = true + break + } + } + } + if (status === 'q') { + for (let i = 0; i < quotaData.length; i++) { + const ele = quotaData[i] + if (ele.id === item.id && ele.deType === item.deType && ele.groupType === item.groupType) { + checked = true + break + } + } + } + + if (checked) { + if (status === 'd') { + return '' + } else if (status === 'q') { + return 'success' + } + } else { + return 'danger' + } + } + \ No newline at end of file diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/de-base/lang/en.js b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/de-base/lang/en.js new file mode 100644 index 0000000000..72c77c6ece --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/de-base/lang/en.js @@ -0,0 +1,8 @@ +export default { + plugin_view_buddle_map: { + type_title: 'Bubble Map', + area: 'Area', + buddle_size: 'Bubble Size', + area_range: 'Map Ranger' + } +} \ No newline at end of file diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/de-base/lang/index.js b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/de-base/lang/index.js new file mode 100644 index 0000000000..37660077b2 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/de-base/lang/index.js @@ -0,0 +1,48 @@ +import Vue from 'vue' +import VueI18n from 'vue-i18n' +import Cookies from 'js-cookie' +import elementEnLocale from 'element-ui/lib/locale/lang/en' // element-ui lang +import elementZhLocale from 'element-ui/lib/locale/lang/zh-CN'// element-ui lang +import elementTWLocale from 'element-ui/lib/locale/lang/zh-TW'// element-ui lang + +import localMessages from './messages' + +Vue.use(VueI18n) + +const messages = { + en_US: { + ...localMessages['en_US'], + ...elementEnLocale + }, + zh_CN: { + ...localMessages['zh_CN'], + ...elementZhLocale + }, + zh_TW: { + ...localMessages['zh_TW'], + ...elementTWLocale + } +} +export function getLanguage () { + const chooseLanguage = Cookies.get('language') + if (chooseLanguage) return chooseLanguage + + // if has not choose language + const language = (navigator.language || navigator.browserLanguage).toLowerCase() + const locales = Object.keys(messages) + for (const locale of locales) { + if (language.indexOf(locale) > -1) { + return locale + } + } + return 'zh_CN' +} +const i18n = new VueI18n({ + // set locale + // options: en | zh | es + locale: getLanguage(), + // set locale messages + messages +}) + +export default i18n diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/de-base/lang/messages.js b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/de-base/lang/messages.js new file mode 100644 index 0000000000..844f8c0673 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/de-base/lang/messages.js @@ -0,0 +1,17 @@ +import enLocale from './en' +import zhLocale from './zh' +import twLocale from './tw' + +const messages = { + en_US: { + ...enLocale + }, + zh_CN: { + ...zhLocale + }, + zh_TW: { + ...twLocale + } +} + +export default messages \ No newline at end of file diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/de-base/lang/tw.js b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/de-base/lang/tw.js new file mode 100644 index 0000000000..5f17cae602 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/de-base/lang/tw.js @@ -0,0 +1,8 @@ +export default { + plugin_view_buddle_map: { + type_title: '氣泡地圖', + area: '地區', + buddle_size: '氣泡大小', + area_range: '地圖範圍' + } +} \ No newline at end of file diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/de-base/lang/zh.js b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/de-base/lang/zh.js new file mode 100644 index 0000000000..9386792797 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/de-base/lang/zh.js @@ -0,0 +1,8 @@ +export default { + plugin_view_buddle_map: { + type_title: '气泡地图', + area: '地区', + buddle_size: '气泡大小', + area_range: '地图范围' + } +} \ No newline at end of file diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/icons/index.js b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/icons/index.js new file mode 100644 index 0000000000..2c6b309c96 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/icons/index.js @@ -0,0 +1,9 @@ +import Vue from 'vue' +import SvgIcon from '@/components/SvgIcon'// svg component + +// register globally +Vue.component('svg-icon', SvgIcon) + +const req = require.context('./svg', false, /\.svg$/) +const requireAll = requireContext => requireContext.keys().map(requireContext) +requireAll(req) diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/icons/svg/buddle-map.svg b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/icons/svg/buddle-map.svg new file mode 100644 index 0000000000..5b10c334fa --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/icons/svg/buddle-map.svg @@ -0,0 +1 @@ + diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/icons/svg/view-bubblemap-backend.svg b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/icons/svg/view-bubblemap-backend.svg new file mode 100644 index 0000000000..58f6a07f97 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/icons/svg/view-bubblemap-backend.svg @@ -0,0 +1 @@ +【icon】插件管理-导出 \ No newline at end of file diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/icons/svgo.yml b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/icons/svgo.yml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/main.js b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/main.js new file mode 100644 index 0000000000..fb8f35373c --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/main.js @@ -0,0 +1,34 @@ +// The Vue build version to load with the `import` command +// (runtime-only or standalone) has been set in webpack.base.conf with an alias. +import Vue from 'vue' +import App from './App' +import router from './router' +import ElementUI from 'element-ui' +import Cookies from 'js-cookie' +import i18n from './de-base/lang' +import draggable from 'vuedraggable' +import Treeselect from '@riophae/vue-treeselect' +import '@riophae/vue-treeselect/dist/vue-treeselect.css' +import '@/icons' // icon +Vue.config.productionTip = false +Vue.use(ElementUI, { + size: Cookies.get('size') || 'medium', + i18n: (key, value) => i18n.t(key, value) +}) +Vue.component('Treeselect', Treeselect) +Vue.component('draggable', draggable) +Vue.prototype.hasDataPermission = function(pTarget, pSource) { + + if (pSource && pTarget) { + return pSource.indexOf(pTarget) > -1 + } + return false +} +/* eslint-disable no-new */ +new Vue({ + el: '#app', + router, + i18n, + components: { App }, + template: '' +}) diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/router/index.js b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/router/index.js new file mode 100644 index 0000000000..2ed15dc60d --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/router/index.js @@ -0,0 +1,27 @@ +import Vue from 'vue' +import Router from 'vue-router' +import HelloWorld from '@/components/HelloWorld' +import BuddleType from '@/views/echarts/map/buddle/type' +import BuddleData from '@/views/echarts/map/test' + +Vue.use(Router) + +export default new Router({ + routes: [ + { + path: '/', + name: 'HelloWorld', + component: HelloWorld + }, + { + path: '/buddle-type', + name: 'buddle-type', + component: BuddleType + }, + { + path: '/buddle-data', + name: 'buddle-data', + component: BuddleData + } + ] +}) diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/utils/compare.js b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/utils/compare.js new file mode 100644 index 0000000000..b06a104a10 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/utils/compare.js @@ -0,0 +1,29 @@ +export const compareItem = { + type: 'none', // year-yoy/month-yoy等 + resultData: 'percent', // 对比差sub,百分比percent等 + field: '', + custom: { + field: '', + calcType: '0', // 0-增长值,1-增长率 + timeType: '0', // 0-固定日期,1-日期区间 + currentTime: '', + compareTime: '', + currentTimeRange: [], + compareTimeRange: [] + } +} + +export const compareYearList = [ + { name: 'year_mom', value: 'year_mom' } +] + +export const compareMonthList = [ + { name: 'month_mom', value: 'month_mom' }, + { name: 'year_yoy', value: 'year_yoy' } +] + +export const compareDayList = [ + { name: 'day_mom', value: 'day_mom' }, + { name: 'month_yoy', value: 'month_yoy' }, + { name: 'year_yoy', value: 'year_yoy' } +] diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/utils/map.js b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/utils/map.js new file mode 100644 index 0000000000..3dea431749 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/utils/map.js @@ -0,0 +1,370 @@ +export const DEFAULT_COLOR_CASE = { + value: 'default', + colors: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'], + alpha: 100, + tableHeaderBgColor: '#e1eaff', + tableItemBgColor: '#ffffff', + tableFontColor: '#000000', + tableStripe: true, + dimensionColor: '#000000', + quotaColor: '#000000', + tableBorderColor: '#cfdaf4', + areaBorderColor: '#303133', + areaBaseColor: '#ffffff' +} + +export const COLOR_PANEL = [ + '#ff4500', + '#ff8c00', + '#ffd700', + '#90ee90', + '#00ced1', + '#1e90ff', + '#c71585', + '#999999', + '#000000', + '#FFFFFF' +] + +export const DEFAULT_LABEL = { + show: false, + position: 'top', + color: '#909399', + fontSize: '10', + formatter: '{c}', + gaugeFormatter: '{value}', + labelLine: { + show: true + } +} +export const DEFAULT_TOOLTIP = { + show: true, + trigger: 'item', + confine: true, + textStyle: { + fontSize: '10', + color: '#909399' + }, + formatter: '' +} +export const DEFAULT_TITLE_STYLE = { + show: true, + fontSize: '18', + color: '#303133', + hPosition: 'center', + vPosition: 'top', + isItalic: false, + isBolder: false +} +export const DEFAULT_BACKGROUND_COLOR = { + color: '#ffffff', + alpha: 100, + borderRadius: 5 +} +export const DEFAULT_SUSPENSION = { + show: true +} +export const BASE_MAP = { + title: { + text: '', + textStyle: { + fontWeight: 'normal' + } + }, + visualMap: { + min: 50, + max: 52, + text: ['High', 'Low'], + realtime: false, + calculable: true, + inRange: { + color: ['lightskyblue', 'yellow', 'orangered'] + }, + right: 0, + textStyle: {} + }, + + tooltip: {}, + geo: [ + { + show: true, + roam: true, + map: 'BUDDLE_MAP', + label: { + normal: { + show: false + }, + emphasis: { + show: false + } + }, + itemStyle: { + areaColor: '#f3f3f3', + borderType: 'dashed', + borderColor: '#fff' + } + } + ], + series: [{ + name: '', + type: 'scatter', + coordinateSystem: 'geo', + data: [] + }] +} +const convertData = (mapData, chart, curAreaCode) => { + if (chart.senior) { + const senior = JSON.parse(chart.senior) + const mapMapping = senior && senior.mapMapping && senior.mapMapping[curAreaCode] + if (mapMapping) { + for (const key in mapMapping) { + if (Object.hasOwnProperty.call(mapMapping, key)) { + const element = mapMapping[key] + if (element && mapData[key]) { + mapData[element] = mapData[key] + } + + } + } + } + } + let maxVal = 0 + const k = terminalType === 'pc' ? 30 : 15 + const names = chart.data.x + const results = [] + for (let index = 0; index < names.length; index++) { + const name = names[index]; + const item = chart.data.series[0].data[index] + results.push({ + name, + value: (mapData[name] ? mapData[name].concat(item.value) : []), + dimensionList: item.dimensionList, + quotaList: item.quotaList + }) + maxVal = Math.max(maxVal, item.value) + } + const rate = k / maxVal + + return { + value: results, + rate: rate + } +} + +let terminalType = 'pc' +export function baseMapOption(chart_option, chart, mapData, terminal = 'pc', themeStyle, curAreaCode) { + terminalType = terminal + let customAttr = {} + if (chart.customAttr) { + customAttr = JSON.parse(chart.customAttr) + if (customAttr.color) { + chart_option.color = customAttr.color.colors + if (customAttr.color.areaBorderColor) { + chart_option.geo.forEach(item => { + item.itemStyle.borderColor = customAttr.color.areaBorderColor + }) + } + if (customAttr.color.areaBaseColor) { + chart_option.geo[chart_option.geo.length - 1].itemStyle.areaColor = customAttr.color.areaBaseColor + chart_option.geo[chart_option.geo.length - 1].itemStyle.opacity = 0.7 + chart_option.geo[chart_option.geo.length - 1].emphasis = { + itemStyle: { + areaColor: customAttr.color.areaBaseColor, + opacity: 1 + } + } + } + } + if (customAttr.tooltip) { + const tooltip = JSON.parse(JSON.stringify(customAttr.tooltip)) + const reg = new RegExp('\n', 'g') + const text = tooltip.formatter.replace(reg, '
') + tooltip.formatter = function (params) { + const a = params.seriesName + const b = params.name + const c = params.value ? params.value[2] : '' + return text.replace(new RegExp('{a}', 'g'), a).replace(new RegExp('{b}', 'g'), b).replace(new RegExp('{c}', 'g'), c) + } + chart_option.tooltip = tooltip + } + } + chart_option.title.text = chart.title + if (chart.data) { + if (chart.data.series && chart.data.series.length > 0) { + chart_option.series[0].name = chart.data.series[0].name + if (customAttr.label) { + const text = customAttr.label.formatter + chart_option.series[0].label = customAttr.label + chart_option.series[0].label.formatter = function (params) { + const a = params.seriesName + const b = params.name + const c = params.value ? params.value[2] : '' + return text.replace(new RegExp('{a}', 'g'), a).replace(new RegExp('{b}', 'g'), b).replace(new RegExp('{c}', 'g'), c) + } + chart_option.series[0].labelLine = customAttr.label.labelLine + } + + const valueArr = chart.data.series[0].data + if (valueArr && valueArr.length > 0) { + const values = [] + valueArr.forEach(function (ele) { + values.push(ele.value) + }) + chart_option.visualMap.min = Math.min(...values) + chart_option.visualMap.max = Math.max(...values) + if (chart_option.visualMap.min === chart_option.visualMap.max) { + chart_option.visualMap.min = 0 + } + } else { + chart_option.visualMap.min = 0 + chart_option.visualMap.max = 0 + } + if (chart_option.visualMap.min === 0 && chart_option.visualMap.max === 0) { + chart_option.visualMap.max = 100 + } + if (customAttr.color && customAttr.color.colors) { + chart_option.visualMap.inRange.color = customAttr.color.colors + chart_option.visualMap.inRange.colorAlpha = customAttr.color.alpha / 100 + } + if (themeStyle) { + + chart_option.visualMap.textStyle = { + color: themeStyle + } + } + if (customAttr.suspension && !customAttr.suspension.show) { + chart_option.visualMap.show = false + } else if ('show' in chart_option.visualMap) { + delete chart_option.visualMap.show + } + const convert = convertData(mapData, chart, curAreaCode) + chart_option.series[0].data = convert.value + chart_option.series[0].symbolSize = val => val[2] * convert.rate + + if (chart.senior) { + const senior = JSON.parse(chart.senior) + + senior && senior.mapMapping && senior.mapMapping[curAreaCode] && (chart_option.series[0].nameMap = senior.mapMapping[curAreaCode]) + } + + } + } + componentStyle(chart_option, chart) + return chart_option +} +export function componentStyle(chart_option, chart) { + const padding = '8px' + if (chart.customStyle) { + const customStyle = JSON.parse(chart.customStyle) + if (customStyle.text) { + chart_option.title.show = customStyle.text.show + if (customStyle.text.hPosition === 'left') { + chart_option.title.left = padding + } else if (customStyle.text.hPosition === 'right') { + chart_option.title.right = padding + } else { + chart_option.title.left = customStyle.text.hPosition + } + if (customStyle.text.vPosition === 'top') { + chart_option.title.top = padding + } else if (customStyle.text.vPosition === 'bottom') { + chart_option.title.bottom = padding + } else { + chart_option.title.top = customStyle.text.vPosition + } + const style = chart_option.title.textStyle ? chart_option.title.textStyle : {} + style.fontSize = customStyle.text.fontSize + style.color = customStyle.text.color + customStyle.text.isItalic ? style.fontStyle = 'italic' : style.fontStyle = 'normal' + customStyle.text.isBolder ? style.fontWeight = 'bold' : style.fontWeight = 'normal' + chart_option.title.textStyle = style + } + if (customStyle.legend && chart_option.legend) { + chart_option.legend.show = customStyle.legend.show + if (customStyle.legend.hPosition === 'left') { + chart_option.legend.left = padding + } else if (customStyle.legend.hPosition === 'right') { + chart_option.legend.right = padding + } else { + chart_option.legend.left = customStyle.legend.hPosition + } + if (customStyle.legend.vPosition === 'top') { + chart_option.legend.top = padding + } else if (customStyle.legend.vPosition === 'bottom') { + chart_option.legend.bottom = padding + } else { + chart_option.legend.top = customStyle.legend.vPosition + } + chart_option.legend.orient = customStyle.legend.orient + chart_option.legend.icon = customStyle.legend.icon + chart_option.legend.textStyle = customStyle.legend.textStyle + + } + + if (customStyle.background) { + chart_option.backgroundColor = hexColorToRGBA(customStyle.background.color, customStyle.background.alpha) + } + } +} +export function hexColorToRGBA(hex, alpha) { + const rgb = [] + if (/^\#[0-9A-F]{3}$/i.test(hex)) { + let sixHex = '#' + hex.replace(/[0-9A-F]/ig, function (kw) { + sixHex += kw + kw + }) + hex = sixHex + } + if (/^#[0-9A-F]{6}$/i.test(hex)) { + hex.replace(/[0-9A-F]{2}/ig, function (kw) { + rgb.push(eval('0x' + kw)) + }) + return `rgba(${rgb.join(',')},${alpha / 100})` + } else { + return 'rgb(0,0,0)' + } +} + + + +export const DEFAULT_YAXIS_EXT_STYLE = { + show: true, + position: 'right', + name: '', + nameTextStyle: { + color: '#333333', + fontSize: 12 + }, + axisLabel: { + show: true, + color: '#333333', + fontSize: '12', + rotate: 0, + formatter: '{value}' + }, + splitLine: { + show: true, + lineStyle: { + color: '#cccccc', + width: 1, + style: 'solid' + } + }, + axisValue: { + auto: true, + min: null, + max: null, + split: null, + splitCount: null + } +} + +export function uuid() { + return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1); +} + +export const reverseColor = colorValue => { + colorValue = '0x' + colorValue.replace(/#/g, '') + const str = '000000' + (0xFFFFFF - colorValue).toString(16) + return '#' + str.substring(str.length - 6, str.length) +} diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/utils/nationalCenterPoint.js b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/utils/nationalCenterPoint.js new file mode 100644 index 0000000000..f328ebb850 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/utils/nationalCenterPoint.js @@ -0,0 +1,750 @@ +export const centerPointJson = { + "Angola": [ + 17.87, + -11.2 + ], + "Afghanistan": [ + 67.71, + 33.94 + ], + "Albania": [ + 20.17, + 41.15 + ], + "Algeria": [ + 1.66, + 28.03 + ], + "Andorra": [ + 1.52, + 42.51 + ], + "Anguilla": [ + -63.07, + 18.22 + ], + "Argentina": [ + -63.62, + -38.42 + ], + "Armenia": [ + 45.04, + 40.07 + ], + "Ascension": [ + -90.94, + 30.2 + ], + "Australia": [ + 133.78, + -25.27 + ], + "Austria": [ + 14.55, + 47.52 + ], + "Azerbaijan": [ + 47.58, + 40.14 + ], + "Bahamas": [ + -77.4, + 25.03 + ], + "Bahrain": [ + 50.56, + 26.07 + ], + "Bangladesh": [ + 90.36, + 23.68 + ], + "Barbados": [ + -59.54, + 13.19 + ], + "Belarus": [ + 27.95, + 53.71 + ], + "Belgium": [ + 4.47, + 50.5 + ], + "Belize": [ + -88.5, + 17.19 + ], + "Benin": [ + 2.32, + 9.31 + ], + "Bermuda Is": [ + -64.75, + 32.31 + ], + "Bolivia": [ + -63.59, + -16.29 + ], + "Botswana": [ + 24.68, + -22.33 + ], + "Brazil": [ + -51.93, + -14.24 + ], + "Brunei": [ + 114.73, + 4.54 + ], + "Bulgaria": [ + 25.49, + 42.73 + ], + "Burkina-faso": [ + -1.56, + 12.24 + ], + "Burma": [ + 95.96, + 21.92 + ], + "Burundi": [ + 29.92, + -3.37 + ], + "Cameroon": [ + 12.35, + 7.37 + ], + "Canada": [ + -106.35, + 56.13 + ], + "Cayman Is": [ + -117.64, + 33.64 + ], + "Central African Republic": [ + 20.94, + 6.61 + ], + "Chad": [ + 18.73, + 15.45 + ], + "Chile": [ + -71.54, + -35.68 + ], + "China": [ + 104.2, + 35.86 + ], + "Colombia": [ + -74.3, + 4.57 + ], + "Cook Is": [ + -90.49, + 47.61 + ], + "Costa Rica": [ + -83.75, + 9.75 + ], + "Croatia": [ + 15.2, + 45.1 + ], + "Cuba": [ + -77.78, + 21.52 + ], + "Cyprus": [ + 33.43, + 35.13 + ], + "Czech Republic": [ + 15.47, + 49.82 + ], + "Denmark": [ + 9.5, + 56.26 + ], + "Djibouti": [ + 42.59, + 11.83 + ], + "Dominica Rep": [ + -0.19, + 51.52 + ], + "Ecuador": [ + -78.18, + -1.83 + ], + "Egypt": [ + 30.8, + 26.82 + ], + "EI Salvador": [ + -88.9, + 13.79 + ], + "Estonia": [ + 25.01, + 58.6 + ], + "Ethiopia": [ + 40.49, + 9.15 + ], + "Fiji": [ + 178.07, + -17.71 + ], + "Finland": [ + 25.75, + 61.92 + ], + "France": [ + 2.21, + 46.23 + ], + "French Guiana": [ + -53.13, + 3.93 + ], + "Gabon": [ + 11.61, + -0.8 + ], + "Gambia": [ + -15.31, + 13.44 + ], + "Georgia": [ + -82.9, + 32.17 + ], + "Germany": [ + 10.45, + 51.17 + ], + "Ghana": [ + -1.02, + 7.95 + ], + "Gibraltar": [ + -5.35, + 36.14 + ], + "Greece": [ + 21.82, + 39.07 + ], + "Grenada": [ + -61.68, + 12.12 + ], + "Guam": [ + 144.79, + 13.44 + ], + "Guatemala": [ + -90.23, + 15.78 + ], + "Guinea": [ + -9.7, + 9.95 + ], + "Guyana": [ + -58.93, + 4.86 + ], + "Haiti": [ + -72.29, + 18.97 + ], + "Honduras": [ + -86.24, + 15.2 + ], + "Hong Kong": [ + 114.11, + 22.4 + ], + "Hungary": [ + 19.5, + 47.16 + ], + "Iceland": [ + -19.02, + 64.96 + ], + "India": [ + 78.96, + 20.59 + ], + "Indonesia": [ + 113.92, + -0.79 + ], + "Iran": [ + 53.69, + 32.43 + ], + "Iraq": [ + 43.68, + 33.22 + ], + "Ireland": [ + -8.24, + 53.41 + ], + "Israel": [ + 34.85, + 31.05 + ], + "Italy": [ + 12.57, + 41.87 + ], + "Ivory Coast": [ + -5.55, + 7.54 + ], + "Japan": [ + 138.25, + 36.2 + ], + "Jordan": [ + 36.24, + 30.59 + ], + "Kampuchea": [ + 105.46, + 12 + ], + "Kazakhstan": [ + 66.92, + 48.02 + ], + "Kenya": [ + 37.91, + -0.02 + ], + "Korea": [ + 127.98, + 37.66 + ], + "Kuwait": [ + 47.48, + 29.31 + ], + "Kyrgyzstan": [ + 74.77, + 41.2 + ], + "Laos": [ + 102.5, + 19.86 + ], + "Latvia": [ + 24.6, + 56.88 + ], + "Lebanon": [ + 35.86, + 33.85 + ], + "Lesotho": [ + 28.23, + -29.61 + ], + "Liberia": [ + -9.43, + 6.43 + ], + "Libya": [ + 17.23, + 26.34 + ], + "Liechtenstein": [ + 9.56, + 47.17 + ], + "Lithuania": [ + 23.88, + 55.17 + ], + "Luxembourg": [ + 6.13, + 49.82 + ], + "Macao": [ + 113.54, + 22.2 + ], + "Madagascar": [ + 46.87, + -18.77 + ], + "Malawi": [ + 34.3, + -13.25 + ], + "Malaysia": [ + 101.98, + 4.21 + ], + "Maldives": [ + 73.54, + 1.98 + ], + "Mali": [ + -4, + 17.57 + ], + "Malta": [ + 14.38, + 35.94 + ], + "Mariana Is": [ + -43.41, + -20.37 + ], + "Martinique": [ + -61.02, + 14.64 + ], + "Mauritius": [ + 57.55, + -20.35 + ], + "Mexico": [ + -102.55, + 23.63 + ], + "Monaco": [ + 7.42, + 43.74 + ], + "Mongolia": [ + 103.85, + 46.86 + ], + "Montserrat Is": [ + -62.19, + 16.74 + ], + "Morocco": [ + -7.09, + 31.79 + ], + "Mozambique": [ + 35.53, + -18.67 + ], + "Namibia": [ + 18.49, + -22.96 + ], + "Nauru": [ + 166.93, + -0.52 + ], + "Nepal": [ + 84.12, + 28.39 + ], + "Netheriands Antilles": [ + -68.26, + 12.2 + ], + "Netherlands": [ + 5.29, + 52.13 + ], + "New Zealand": [ + 174.89, + -40.9 + ], + "Nicaragua": [ + -85.21, + 12.87 + ], + "Niger": [ + 8.08, + 17.61 + ], + "Nigeria": [ + 8.68, + 9.08 + ], + "North Korea": [ + 127.51, + 40.34 + ], + "Oman": [ + 55.98, + 21.47 + ], + "Pakistan": [ + 69.35, + 30.38 + ], + "Panama": [ + -80.78, + 8.54 + ], + "Papua New Cuinea": [ + 143.96, + -6.31 + ], + "Paraguay": [ + -58.44, + -23.44 + ], + "Peru": [ + -75.02, + -9.19 + ], + "Philippines": [ + 121.77, + 12.88 + ], + "Poland": [ + 19.15, + 51.92 + ], + "French Polynesia": [ + -149.41, + -17.68 + ], + "Portugal": [ + -8.22, + 39.4 + ], + "Puerto Rico": [ + -66.59, + 18.22 + ], + "Qatar": [ + 51.18, + 25.35 + ], + "Reunion": [ + 55.54, + -21.12 + ], + "Romania": [ + 24.97, + 45.94 + ], + "Russia": [ + 105.32, + 61.52 + ], + "Saint Lueia": [ + -60.98, + 13.91 + ], + "Saint Vincent": [ + 7.64, + 45.75 + ], + "Samoa Eastern": [ + -121.83, + 37.35 + ], + "Samoa Western": [ + -124.15, + 40.8 + ], + "San Marino": [ + 12.46, + 43.94 + ], + "Sao Tome and Principe": [ + 6.61, + 0.19 + ], + "Saudi Arabia": [ + 45.08, + 23.89 + ], + "Senegal": [ + -14.45, + 14.5 + ], + "Seychelles": [ + 55.49, + -4.68 + ], + "Sierra Leone": [ + -11.78, + 8.46 + ], + "Singapore": [ + 103.82, + 1.35 + ], + "Slovakia": [ + 19.7, + 48.67 + ], + "Slovenia": [ + 15, + 46.15 + ], + "Solomon Is": [ + -97.37, + 38.92 + ], + "Somali": [ + 46.2, + 5.15 + ], + "South Africa": [ + 22.94, + -30.56 + ], + "Spain": [ + -3.75, + 40.46 + ], + "Sri Lanka": [ + 80.77, + 7.87 + ], + "St.Lucia": [ + -60.98, + 13.91 + ], + "St.Vincent": [ + -91.06, + 29.99 + ], + "Sudan": [ + 30.22, + 12.86 + ], + "Suriname": [ + -56.03, + 3.92 + ], + "Swaziland": [ + 31.47, + -26.52 + ], + "Sweden": [ + 18.64, + 60.13 + ], + "Switzerland": [ + 8.23, + 46.82 + ], + "Syria": [ + 39, + 34.8 + ], + "Taiwan": [ + 120.96, + 23.7 + ], + "Tajikstan": [ + 71.28, + 38.86 + ], + "Thailand": [ + 100.99, + 15.87 + ], + "Togo": [ + 0.82, + 8.62 + ], + "Tonga": [ + -175.2, + -21.18 + ], + "Trinidad and Tobago": [ + -61.22, + 10.69 + ], + "Tunisia": [ + 9.54, + 33.89 + ], + "Turkey": [ + 35.24, + 38.96 + ], + "Turkmenistan": [ + 59.56, + 38.97 + ], + "Uganda": [ + 32.29, + 1.37 + ], + "Ukraine": [ + 31.17, + 48.38 + ], + "United Arab Emirates": [ + 53.85, + 23.42 + ], + "United Kingdom": [ + -3.44, + 55.38 + ], + "United States": [ + -95.71, + 37.09 + ], + "Uruguay": [ + -55.77, + -32.52 + ], + "Uzbekistan": [ + 64.59, + 41.38 + ], + "Venezuela": [ + -66.59, + 6.42 + ], + "Vietnam": [ + 108.28, + 14.06 + ], + "Yemen": [ + 48.52, + 15.55 + ], + "Yugoslavia": [ + 121.02, + 14.48 + ], + "Zimbabwe": [ + 29.15, + -19.02 + ], + "Zaire": [ + 21.76, + -4.04 + ], + "Zambia": [ + 27.85, + -13.13 + ] +} \ No newline at end of file diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/utils/validate.js b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/utils/validate.js new file mode 100644 index 0000000000..9becfa3b87 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/utils/validate.js @@ -0,0 +1,16 @@ +/** + * Created by PanJiaChen on 16/11/18. + */ + +/** + * @param {string} path + * @returns {Boolean} + */ +export function isExternal(path) { + return /^(https?:|mailto:|tel:)/.test(path) +} + + + + + \ No newline at end of file diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/views/PluginDemo.vue b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/views/PluginDemo.vue new file mode 100644 index 0000000000..1164dfab1d --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/views/PluginDemo.vue @@ -0,0 +1,55 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/views/echarts/map/buddle/data.vue b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/views/echarts/map/buddle/data.vue new file mode 100644 index 0000000000..b4e0834faa --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/views/echarts/map/buddle/data.vue @@ -0,0 +1,721 @@ + + + + + \ No newline at end of file diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/views/echarts/map/buddle/format.js b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/views/echarts/map/buddle/format.js new file mode 100644 index 0000000000..92567b67dc --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/views/echarts/map/buddle/format.js @@ -0,0 +1,47 @@ +export default class BuddleMap { + + constructor(param, view) { + this.param = param + this.view = view + this.widgets = [ + { + labels: ['地区', '维度'], + value: this.view.xaxis, + dragMove: this.onMove, + dragAdd: this.addXaxis, + dragUpdate: this.calcData, + solt: 'dimension-item' + },{ + labels: ['数据', '指标'], + value: this.view.yaxis, + dragMove: this.onMove, + dragAdd: this.addYaxis, + dragUpdate: this.calcData, + solt: 'quota-item' + } + ] + } + + + calcData(cache) { + + } + + onMove(e) { + + } + + addXaxis(e) { + + } + + addYaxis(e) { + + } + + + + + + +} \ No newline at end of file diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/views/echarts/map/buddle/index.vue b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/views/echarts/map/buddle/index.vue new file mode 100644 index 0000000000..951824a1c9 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/views/echarts/map/buddle/index.vue @@ -0,0 +1,442 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/views/echarts/map/buddle/style.vue b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/views/echarts/map/buddle/style.vue new file mode 100644 index 0000000000..5e91499364 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/views/echarts/map/buddle/style.vue @@ -0,0 +1,160 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/views/echarts/map/buddle/type.vue b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/views/echarts/map/buddle/type.vue new file mode 100644 index 0000000000..571c882c93 --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/views/echarts/map/buddle/type.vue @@ -0,0 +1,58 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/views/echarts/map/test.vue b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/views/echarts/map/test.vue new file mode 100644 index 0000000000..cfe2c7926b --- /dev/null +++ b/extensions/dataease-extensions-view/view-bubblemap/view-bubblemap-frontend/src/views/echarts/map/test.vue @@ -0,0 +1,21 @@ + + + + + \ No newline at end of file diff --git a/extensions/dataease-extensions-view/view-chartmix/build.sh b/extensions/dataease-extensions-view/view-chartmix/build.sh new file mode 100644 index 0000000000..f66a518028 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/build.sh @@ -0,0 +1,8 @@ +#!/bin/sh +mvn clean package + +cp view-chartmix-backend/target/view-chartmix-backend-1.18.9.jar . + +zip -r chartmix.zip ./view-chartmix-backend-1.18.9.jar ./plugin.json + +rm -f ./view-chartmix-backend-1.18.9.jar diff --git a/extensions/dataease-extensions-view/view-chartmix/plugin.json b/extensions/dataease-extensions-view/view-chartmix/plugin.json new file mode 100644 index 0000000000..80a3f79aee --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/plugin.json @@ -0,0 +1,13 @@ +{ + "name": "AntV 组合图", + "free": 0, + "store": "default", + "cost": 0, + "category": "view", + "descript": "AntV G2Plot 组合图插件", + "version": "1.18.9", + "creator": "DATAEASE", + "moduleName": "view-chartmix-backend", + "require": "1.18.9", + "dsType": "" +} diff --git a/extensions/dataease-extensions-view/view-chartmix/pom.xml b/extensions/dataease-extensions-view/view-chartmix/pom.xml new file mode 100644 index 0000000000..3554e02c3e --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/pom.xml @@ -0,0 +1,20 @@ + + + + dataease-extensions-view + io.dataease + ${dataease.version} + + 4.0.0 + + view-chartmix + pom + + view-chartmix-frontend + view-chartmix-backend + + + + diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-backend/pom.xml b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-backend/pom.xml new file mode 100644 index 0000000000..7aae2b3e53 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-backend/pom.xml @@ -0,0 +1,109 @@ + + + + view-chartmix + io.dataease + ${dataease.version} + + 4.0.0 + + view-chartmix-backend + + + + io.dataease + dataease-plugin-view + + + + + + + src/main/java + + **/*.properties + **/*.xml + + false + + + src/main/resources + + **/* + + false + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 11 + 11 + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.4 + + + **/server/** + **/*.properties + **/Application* + + + + + + maven-clean-plugin + + + + src/main/resources/static + + ** + + false + + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + main-class-placement + generate-resources + + + + + + + + + + + + run + + + + + + + + + + + diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-backend/src/main/java/io/dataease/plugins/view/official/handler/DefaultViewStatHandler.java b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-backend/src/main/java/io/dataease/plugins/view/official/handler/DefaultViewStatHandler.java new file mode 100644 index 0000000000..0f37d52e44 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-backend/src/main/java/io/dataease/plugins/view/official/handler/DefaultViewStatHandler.java @@ -0,0 +1,103 @@ +package io.dataease.plugins.view.official.handler; + +import io.dataease.plugins.common.constants.datasource.SQLConstants; +import io.dataease.plugins.common.request.permission.DataSetRowPermissionsTreeDTO; +import io.dataease.plugins.common.util.ConstantsUtil; +import io.dataease.plugins.view.entity.*; +import io.dataease.plugins.view.handler.PluginViewStatHandler; +import io.dataease.plugins.view.service.ViewPluginBaseService; +import io.dataease.plugins.view.service.ViewPluginService; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.stringtemplate.v4.ST; +import org.stringtemplate.v4.STGroup; +import org.stringtemplate.v4.STGroupFile; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class DefaultViewStatHandler implements PluginViewStatHandler { + + @Override + public String build(PluginViewParam pluginViewParam, ViewPluginService viewPluginService) { + ViewPluginBaseService baseService = viewPluginService.getBaseService(); + PluginViewSet pluginViewSet = pluginViewParam.getPluginViewSet(); + List rowPermissionsTree = pluginViewParam.getRowPermissionsTree(); + String dsType = pluginViewSet.getDsType(); + PluginViewSQL tableObj = baseService.getTableObj(pluginViewSet); + + Map> fieldSQLMap = new HashMap<>(); + + + for (int i = 0; i < pluginViewParam.getPluginViewFields().size(); i++) { + PluginViewField pluginViewField = pluginViewParam.getPluginViewFields().get(i); + String typeKey = pluginViewField.getTypeField(); + PluginSingleField pluginSingleField = baseService.buildField(dsType, pluginViewField, tableObj, i); + List lists = fieldSQLMap.getOrDefault(typeKey, new ArrayList<>()); + lists.add(pluginSingleField); + fieldSQLMap.put(typeKey, lists); + } + + List xFields = fieldSQLMap.getOrDefault("xAxis", new ArrayList<>()).stream().filter(singleField -> ObjectUtils.isNotEmpty(singleField.getField())).map(PluginSingleField::getField).collect(Collectors.toList()); + List xOrders = fieldSQLMap.getOrDefault("xAxis", new ArrayList<>()).stream().filter(singleField -> ObjectUtils.isNotEmpty(singleField.getSort())).map(PluginSingleField::getSort).collect(Collectors.toList()); + // List xWheres = fieldSQLMap.get("xAxis").stream().map(singleField -> singleField.getWhere()).collect(Collectors.toList()); + + List yFields = fieldSQLMap.getOrDefault("yAxis", new ArrayList<>()).stream().filter(singleField -> ObjectUtils.isNotEmpty(singleField.getField())).map(PluginSingleField::getField).collect(Collectors.toList()); + List yOrders = fieldSQLMap.getOrDefault("yAxis", new ArrayList<>()).stream().filter(singleField -> ObjectUtils.isNotEmpty(singleField.getSort())).map(PluginSingleField::getSort).collect(Collectors.toList()); + List yWheres = fieldSQLMap.getOrDefault("yAxis", new ArrayList<>()).stream().filter(singleField -> ObjectUtils.isNotEmpty(singleField.getWhere())).map(PluginSingleField::getWhere).collect(Collectors.toList()); + + /*List yExtFields = fieldSQLMap.getOrDefault("yAxisExt", new ArrayList<>()).stream().filter(singleField -> ObjectUtils.isNotEmpty(singleField.getField())).map(PluginSingleField::getField).collect(Collectors.toList()); + List yExtOrders = fieldSQLMap.getOrDefault("yAxisExt", new ArrayList<>()).stream().filter(singleField -> ObjectUtils.isNotEmpty(singleField.getSort())).map(PluginSingleField::getSort).collect(Collectors.toList()); + List yExtWheres = fieldSQLMap.getOrDefault("yAxisExt", new ArrayList<>()).stream().filter(singleField -> ObjectUtils.isNotEmpty(singleField.getWhere())).map(PluginSingleField::getWhere).collect(Collectors.toList()); + + yFields.addAll(yExtFields); + yOrders.addAll(yExtOrders); + yWheres.addAll(yExtWheres);*/ + + // 处理视图中字段过滤 + String customWheres = baseService.customWhere(dsType, pluginViewParam.getPluginChartFieldCustomFilters(), tableObj); + // 处理仪表板字段过滤 + String panelWheres = baseService.panelWhere(dsType, pluginViewParam.getPluginChartExtFilters(), tableObj); + // 构建sql所有参数 + + String permissionWhere = baseService.permissionWhere(dsType, rowPermissionsTree, tableObj); + List wheres = new ArrayList<>(); + if (customWheres != null) wheres.add(customWheres); + if (panelWheres != null) wheres.add(panelWheres); + if (permissionWhere != null) wheres.add(permissionWhere); + List groups = new ArrayList<>(); + groups.addAll(xFields); + + // 外层再次套sql + List orders = new ArrayList<>(); + orders.addAll(xOrders); + orders.addAll(yOrders); + List aggWheres = new ArrayList<>(); + aggWheres.addAll(yWheres.stream().filter(ObjectUtils::isNotEmpty).collect(Collectors.toList())); + + STGroup stg = new STGroupFile(SQLConstants.SQL_TEMPLATE); + ST st_sql = stg.getInstanceOf("querySql"); + if (CollectionUtils.isNotEmpty(xFields)) st_sql.add("groups", xFields); + if (CollectionUtils.isNotEmpty(yFields)) st_sql.add("aggregators", yFields); + if (CollectionUtils.isNotEmpty(wheres)) st_sql.add("filters", wheres); + if (ObjectUtils.isNotEmpty(tableObj)) st_sql.add("table", tableObj); + String sql = st_sql.render(); + + String brackets = ConstantsUtil.constantsValue(dsType, "BRACKETS"); + String table_alias_prefix = ConstantsUtil.constantsValue(dsType, "TABLE_ALIAS_PREFIX"); + + ST st = stg.getInstanceOf("querySql"); + PluginViewSQL tableSQL = PluginViewSQL.builder() + .tableName(String.format(brackets, sql)) + .tableAlias(String.format(table_alias_prefix, 1)) + .build(); + if (CollectionUtils.isNotEmpty(aggWheres)) st.add("filters", aggWheres); + if (CollectionUtils.isNotEmpty(orders)) st.add("orders", orders); + if (ObjectUtils.isNotEmpty(tableSQL)) st.add("table", tableSQL); + return baseService.sqlLimit(dsType, st.render(), pluginViewParam.getPluginViewLimit()); + } + +} diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-backend/src/main/java/io/dataease/plugins/view/official/impl/ChartMixService.java b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-backend/src/main/java/io/dataease/plugins/view/official/impl/ChartMixService.java new file mode 100644 index 0000000000..df669c06be --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-backend/src/main/java/io/dataease/plugins/view/official/impl/ChartMixService.java @@ -0,0 +1,192 @@ +package io.dataease.plugins.view.official.impl; + +import com.google.gson.Gson; +import io.dataease.plugins.common.dto.StaticResource; +import io.dataease.plugins.view.entity.*; +import io.dataease.plugins.view.official.handler.DefaultViewStatHandler; +import io.dataease.plugins.view.service.ViewPluginService; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.io.InputStream; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Service +public class ChartMixService extends ViewPluginService { + private static final String VIEW_TYPE_VALUE = "chart-mix"; + + /* 下版这些常量移到sdk */ + private static final String TYPE = "-type"; + private static final String DATA = "-data"; + private static final String STYLE = "-style"; + private static final String VIEW = "-view"; + private static final String SUFFIX = "svg"; + /* 下版这些常量移到sdk */ + private static final String VIEW_TYPE = VIEW_TYPE_VALUE + TYPE; + private static final String VIEW_DATA = VIEW_TYPE_VALUE + DATA; + private static final String VIEW_STYLE = VIEW_TYPE_VALUE + STYLE; + private static final String VIEW_VIEW = VIEW_TYPE_VALUE + VIEW; + + private static final String[] VIEW_STYLE_PROPERTIES = { + "color-selector", + "label-selector", + "tooltip-selector-ant-v", + "title-selector-ant-v" + }; + + private static final Map VIEW_STYLE_PROPERTY_INNER = new HashMap<>(); + + static { + VIEW_STYLE_PROPERTY_INNER.put("color-selector", new String[]{"value"}); + VIEW_STYLE_PROPERTY_INNER.put("label-selector", new String[]{"show", "fontSize", "color"}); + VIEW_STYLE_PROPERTY_INNER.put("tooltip-selector-ant-v", new String[]{"show", "fontSize", "color", "backgroundColor"}); + VIEW_STYLE_PROPERTY_INNER.put("title-selector-ant-v", new String[]{"show", "title", "fontSize", "color", "hPosition", "vPosition", "isItalic", "isBolder"}); + } + + @Override + public PluginViewType viewType() { + PluginViewType pluginViewType = new PluginViewType(); + pluginViewType.setRender("antv"); + pluginViewType.setCategory("chart.chart_type_trend"); + pluginViewType.setValue(VIEW_TYPE_VALUE); + pluginViewType.setProperties(VIEW_STYLE_PROPERTIES); + pluginViewType.setPropertyInner(VIEW_STYLE_PROPERTY_INNER); + return pluginViewType; + } + + @Override + public Object format(Object o) { + return null; + } + + @Override + public List components() { + List results = new ArrayList<>(); + results.add(VIEW_VIEW); + results.add(VIEW_DATA); + results.add(VIEW_TYPE); + results.add(VIEW_STYLE); + return results; + } + + @Override + public List staticResources() { + List results = new ArrayList<>(); + StaticResource staticResource = new StaticResource(); + staticResource.setName(VIEW_TYPE_VALUE); + staticResource.setSuffix(SUFFIX); + results.add(staticResource); + results.add(pluginSvg()); + return results; + } + + private StaticResource pluginSvg() { + StaticResource staticResource = new StaticResource(); + staticResource.setName("view-chartmix-backend"); + staticResource.setSuffix("svg"); + return staticResource; + } + + + @Override + protected InputStream readContent(String s) { + InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("static/" + s); + return resourceAsStream; + } + + @Override + public String generateSQL(PluginViewParam param) { + List xAxis = param.getFieldsByType("xAxis"); + List yAxis = param.getFieldsByType("yAxis"); + /*if (yAxis == null) { + yAxis = new ArrayList<>(); + } + List yAxisExt = param.getFieldsByType("yAxisExt"); + if (CollectionUtils.isNotEmpty(yAxisExt)) { + yAxis.addAll(yAxisExt); + }*/ + System.out.println(new Gson().toJson(yAxis)); + if (CollectionUtils.isEmpty(xAxis) || CollectionUtils.isEmpty(yAxis)) { + return null; + } + String sql = new DefaultViewStatHandler().build(param, this); + System.out.println(sql); + return sql; + + } + + + @Override + public Map formatResult(PluginViewParam pluginViewParam, List data, Boolean isDrill) { + List xAxis = new ArrayList<>(); + List yAxis = new ArrayList<>(); + + System.out.println("pluginViewParam: " + new Gson().toJson(pluginViewParam)); + + pluginViewParam.getPluginViewFields().forEach(pluginViewField -> { + if (StringUtils.equals(pluginViewField.getTypeField(), "xAxis")) { + xAxis.add(pluginViewField); + } + if (StringUtils.equals(pluginViewField.getTypeField(), "yAxis")) { + yAxis.add(pluginViewField); + } + }); + Map map = new HashMap<>(); + + List series = format(pluginViewParam.getPluginViewLimit().getType(), data, xAxis, yAxis); + + map.put("data", series); + + System.out.println(new Gson().toJson(map)); + + return map; + } + + private List format(String type, List data, List xAxis, List yAxis) { + List series = new ArrayList<>(); + for (PluginViewField y : yAxis) { + PluginSeries series1 = new PluginSeries(); + series1.setName(y.getName()); + series1.setType(type); + series1.setData(new ArrayList<>()); + series.add(series1); + } + for (int i1 = 0; i1 < data.size(); i1++) { + String[] d = data.get(i1); + + for (int i = xAxis.size(); i < xAxis.size() + yAxis.size(); i++) { + List dimensionList = new ArrayList<>(); + List quotaList = new ArrayList<>(); + PluginAxisChartData axisChartDataDTO = new PluginAxisChartData(); + + for (int j = 0; j < xAxis.size(); j++) { + PluginChartDimension chartDimensionDTO = new PluginChartDimension(); + chartDimensionDTO.setId(xAxis.get(j).getId()); + chartDimensionDTO.setValue(d[j]); + dimensionList.add(chartDimensionDTO); + } + axisChartDataDTO.setDimensionList(dimensionList); + + int j = i - xAxis.size(); + PluginChartQuota chartQuotaDTO = new PluginChartQuota(); + chartQuotaDTO.setId(yAxis.get(j).getId()); + quotaList.add(chartQuotaDTO); + axisChartDataDTO.setQuotaList(quotaList); + try { + axisChartDataDTO.setValue(StringUtils.isEmpty(d[i]) ? null : new BigDecimal(d[i])); + } catch (Exception e) { + axisChartDataDTO.setValue(new BigDecimal(0)); + } + series.get(j).getData().add(axisChartDataDTO); + } + + } + return series; + } + +} diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/.babelrc b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/.babelrc new file mode 100644 index 0000000000..3a280ba34b --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/.babelrc @@ -0,0 +1,12 @@ +{ + "presets": [ + ["env", { + "modules": false, + "targets": { + "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] + } + }], + "stage-2" + ], + "plugins": ["transform-vue-jsx", "transform-runtime"] +} diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/.editorconfig b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/.editorconfig new file mode 100644 index 0000000000..9d08a1a828 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/.gitignore b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/.gitignore new file mode 100644 index 0000000000..541a820f6c --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/.gitignore @@ -0,0 +1,14 @@ +.DS_Store +node_modules/ +/dist/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/.postcssrc.js b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/.postcssrc.js new file mode 100644 index 0000000000..eee3e92d7f --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/.postcssrc.js @@ -0,0 +1,10 @@ +// https://github.com/michael-ciniawsky/postcss-load-config + +module.exports = { + "plugins": { + "postcss-import": {}, + "postcss-url": {}, + // to edit target browsers: use "browserslist" field in package.json + "autoprefixer": {} + } +} diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/README.md b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/README.md new file mode 100644 index 0000000000..c1e4be95e0 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/README.md @@ -0,0 +1,21 @@ +# deplugin-view-frontend + +> A Vue.js project + +## Build Setup + +``` bash +# install dependencies +npm install + +# serve with hot reload at localhost:8080 +npm run dev + +# build for production with minification +npm run build + +# build for production and view the bundle analyzer report +npm run build --report +``` + +For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader). diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/build/build-async-plugins.js b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/build/build-async-plugins.js new file mode 100644 index 0000000000..2303854531 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/build/build-async-plugins.js @@ -0,0 +1,35 @@ +'use strict' +require('./check-versions')() + +process.env.NODE_ENV = 'production' + +const ora = require('ora') +const chalk = require('chalk') +const webpack = require('webpack') +const webpackConfig = require('./webpack.async-plugins') + +const spinner = ora('building for sync-plugins...') +spinner.start() + +webpack(webpackConfig, function (err, stats) { + spinner.stop() + if (err) throw err + process.stdout.write(stats.toString({ + colors: true, + modules: false, + children: false, + chunks: false, + chunkModules: false + }) + '\n\n') + + if (stats.hasErrors()) { + console.log(chalk.red(' Build failed with errors.\n')) + process.exit(1) + } + + console.log(chalk.cyan(' Build complete.\n')) + console.log(chalk.yellow( + ' Tip: built files are meant to be served over an HTTP server.\n' + + ' Opening index.html over file:// won\'t work.\n' + )) +}) diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/build/build.js b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/build/build.js new file mode 100644 index 0000000000..8f2ad8ad49 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/build/build.js @@ -0,0 +1,41 @@ +'use strict' +require('./check-versions')() + +process.env.NODE_ENV = 'production' + +const ora = require('ora') +const rm = require('rimraf') +const path = require('path') +const chalk = require('chalk') +const webpack = require('webpack') +const config = require('../config') +const webpackConfig = require('./webpack.prod.conf') + +const spinner = ora('building for production...') +spinner.start() + +rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { + if (err) throw err + webpack(webpackConfig, (err, stats) => { + spinner.stop() + if (err) throw err + process.stdout.write(stats.toString({ + colors: true, + modules: false, + children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build. + chunks: false, + chunkModules: false + }) + '\n\n') + + if (stats.hasErrors()) { + console.log(chalk.red(' Build failed with errors.\n')) + process.exit(1) + } + + console.log(chalk.cyan(' Build complete.\n')) + console.log(chalk.yellow( + ' Tip: built files are meant to be served over an HTTP server.\n' + + ' Opening index.html over file:// won\'t work.\n' + )) + }) +}) diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/build/check-versions.js b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/build/check-versions.js new file mode 100644 index 0000000000..3ef972a08d --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/build/check-versions.js @@ -0,0 +1,54 @@ +'use strict' +const chalk = require('chalk') +const semver = require('semver') +const packageConfig = require('../package.json') +const shell = require('shelljs') + +function exec (cmd) { + return require('child_process').execSync(cmd).toString().trim() +} + +const versionRequirements = [ + { + name: 'node', + currentVersion: semver.clean(process.version), + versionRequirement: packageConfig.engines.node + } +] + +if (shell.which('npm')) { + versionRequirements.push({ + name: 'npm', + currentVersion: exec('npm --version'), + versionRequirement: packageConfig.engines.npm + }) +} + +module.exports = function () { + const warnings = [] + + for (let i = 0; i < versionRequirements.length; i++) { + const mod = versionRequirements[i] + + if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { + warnings.push(mod.name + ': ' + + chalk.red(mod.currentVersion) + ' should be ' + + chalk.green(mod.versionRequirement) + ) + } + } + + if (warnings.length) { + console.log('') + console.log(chalk.yellow('To use this template, you must update following to modules:')) + console.log() + + for (let i = 0; i < warnings.length; i++) { + const warning = warnings[i] + console.log(' ' + warning) + } + + console.log() + process.exit(1) + } +} diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/build/logo.png b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/build/logo.png new file mode 100644 index 0000000000..f3d2503fc2 Binary files /dev/null and b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/build/logo.png differ diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/build/utils.js b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/build/utils.js new file mode 100644 index 0000000000..e534fb0fd6 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/build/utils.js @@ -0,0 +1,101 @@ +'use strict' +const path = require('path') +const config = require('../config') +const ExtractTextPlugin = require('extract-text-webpack-plugin') +const packageConfig = require('../package.json') + +exports.assetsPath = function (_path) { + const assetsSubDirectory = process.env.NODE_ENV === 'production' + ? config.build.assetsSubDirectory + : config.dev.assetsSubDirectory + + return path.posix.join(assetsSubDirectory, _path) +} + +exports.cssLoaders = function (options) { + options = options || {} + + const cssLoader = { + loader: 'css-loader', + options: { + sourceMap: options.sourceMap + } + } + + const postcssLoader = { + loader: 'postcss-loader', + options: { + sourceMap: options.sourceMap + } + } + + // generate loader string to be used with extract text plugin + function generateLoaders (loader, loaderOptions) { + const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader] + + if (loader) { + loaders.push({ + loader: loader + '-loader', + options: Object.assign({}, loaderOptions, { + sourceMap: options.sourceMap + }) + }) + } + + // Extract CSS when that option is specified + // (which is the case during production build) + if (options.extract) { + return ExtractTextPlugin.extract({ + use: loaders, + fallback: 'vue-style-loader' + }) + } else { + return ['vue-style-loader'].concat(loaders) + } + } + + // https://vue-loader.vuejs.org/en/configurations/extract-css.html + return { + css: generateLoaders(), + postcss: generateLoaders(), + less: generateLoaders('less'), + sass: generateLoaders('sass', { indentedSyntax: true }), + scss: generateLoaders('sass'), + stylus: generateLoaders('stylus'), + styl: generateLoaders('stylus') + } +} + +// Generate loaders for standalone style files (outside of .vue) +exports.styleLoaders = function (options) { + const output = [] + const loaders = exports.cssLoaders(options) + + for (const extension in loaders) { + const loader = loaders[extension] + output.push({ + test: new RegExp('\\.' + extension + '$'), + use: loader + }) + } + + return output +} + +exports.createNotifierCallback = () => { + const notifier = require('node-notifier') + + return (severity, errors) => { + if (severity !== 'error') return + + const error = errors[0] + const filename = error.file && error.file.split('!').pop() + + notifier.notify({ + title: packageConfig.name, + message: severity + ': ' + error.name, + subtitle: filename || '', + icon: path.join(__dirname, 'logo.png') + }) + } +} diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/build/vue-loader.conf.js b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/build/vue-loader.conf.js new file mode 100644 index 0000000000..33ed58bc0a --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/build/vue-loader.conf.js @@ -0,0 +1,22 @@ +'use strict' +const utils = require('./utils') +const config = require('../config') +const isProduction = process.env.NODE_ENV === 'production' +const sourceMapEnabled = isProduction + ? config.build.productionSourceMap + : config.dev.cssSourceMap + +module.exports = { + loaders: utils.cssLoaders({ + sourceMap: sourceMapEnabled, + extract: isProduction + }), + cssSourceMap: sourceMapEnabled, + cacheBusting: config.dev.cacheBusting, + transformToRequire: { + video: ['src', 'poster'], + source: 'src', + img: 'src', + image: 'xlink:href' + } +} diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/build/webpack.async-plugins.js b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/build/webpack.async-plugins.js new file mode 100644 index 0000000000..3751023bdf --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/build/webpack.async-plugins.js @@ -0,0 +1,97 @@ +const webpack = require('webpack') +const path = require('path') +const utils = require('./utils') +const CopyPlugin = require("copy-webpack-plugin"); +const VueLoaderPlugin = require('vue-loader/lib/plugin'); +function resolve (dir) { + return path.join(__dirname, '..', dir) +} + +module.exports = { + mode: 'development', + entry: { + + // PluginDemo: resolve('/src/views/PluginDemo.vue') + 'chart-mix-view': resolve('/src/views/antv/chartmix/index.vue'), + 'chart-mix-data': resolve('/src/views/antv/chartmix/data.vue'), + 'chart-mix-type': resolve('/src/views/antv/chartmix/type.vue'), + 'chart-mix-style': resolve('/src/views/antv/chartmix/style.vue') + + }, + output: { + path: resolve('/static/'), + filename: '[name].js' + }, + resolve: { + extensions: ['.js', '.vue', '.json'], + alias: { + 'vue$': 'vue/dist/vue.esm.js', + '@': resolve('src') + } + }, + externals: { + vue: 'vue' + }, + module: { + rules: [ + { + test: /\.vue$/, + loader: 'vue-loader', + options: { + transformAssetUrls: { + video: 'src', + source: 'src', + img: 'src', + image: 'xlink:href' + } + } + }, + { + test: /.(sa|sc|c)ss$/, + use: [ + {loader: 'vue-style-loader'}, + 'css-loader', + 'sass-loader' + ] + }, + { + test: /\.js$/, + loader: 'babel-loader', + include: [resolve('src'), resolve('test')] + }, + { + test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('img/[name].[hash:7].[ext]') + } + }, + { + test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('media/[name].[hash:7].[ext]') + } + }, + { + test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('fonts/[name].[hash:7].[ext]') + } + } + ] + }, + plugins: [ + new VueLoaderPlugin(), + new webpack.DefinePlugin({ + 'process.env.NODE_ENV': '"production"' + }), + new CopyPlugin([ + {from: 'src/icons/svg/'} + ]), + ] +} diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/build/webpack.base.conf.js b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/build/webpack.base.conf.js new file mode 100644 index 0000000000..6ccc02dab4 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/build/webpack.base.conf.js @@ -0,0 +1,91 @@ +'use strict' +const path = require('path') +const utils = require('./utils') +const config = require('../config') +const vueLoaderConfig = require('./vue-loader.conf') + +function resolve (dir) { + return path.join(__dirname, '..', dir) +} + + + +module.exports = { + context: path.resolve(__dirname, '../'), + entry: { + app: './src/main.js' + }, + output: { + path: config.build.assetsRoot, + filename: '[name].js', + publicPath: process.env.NODE_ENV === 'production' + ? config.build.assetsPublicPath + : config.dev.assetsPublicPath + }, + resolve: { + extensions: ['.js', '.vue', '.json'], + alias: { + 'vue$': 'vue/dist/vue.esm.js', + '@': resolve('src'), + } + }, + module: { + rules: [ + { + test: /\.vue$/, + loader: 'vue-loader', + options: vueLoaderConfig + }, + { + test: /\.js$/, + loader: 'babel-loader', + include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')] + }, + { + test: /\.svg$/, + loader: 'svg-sprite-loader', + include: [resolve('src/icons')], + options: { + symbolId: 'icon-[name]' + } + }, + { + test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, + loader: 'url-loader', + exclude: [resolve('src/icons')], + options: { + limit: 10000, + name: utils.assetsPath('img/[name].[hash:7].[ext]') + } + }, + { + test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('media/[name].[hash:7].[ext]') + } + }, + { + test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('fonts/[name].[hash:7].[ext]') + } + } + ] + }, + node: { + // prevent webpack from injecting useless setImmediate polyfill because Vue + // source contains it (although only uses it if it's native). + setImmediate: false, + // prevent webpack from injecting mocks to Node native modules + // that does not make sense for the client + dgram: 'empty', + fs: 'empty', + net: 'empty', + tls: 'empty', + child_process: 'empty' + } +} diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/build/webpack.dev.conf.js b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/build/webpack.dev.conf.js new file mode 100755 index 0000000000..070ae221f3 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/build/webpack.dev.conf.js @@ -0,0 +1,95 @@ +'use strict' +const utils = require('./utils') +const webpack = require('webpack') +const config = require('../config') +const merge = require('webpack-merge') +const path = require('path') +const baseWebpackConfig = require('./webpack.base.conf') +const CopyWebpackPlugin = require('copy-webpack-plugin') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') +const portfinder = require('portfinder') + +const HOST = process.env.HOST +const PORT = process.env.PORT && Number(process.env.PORT) + +const devWebpackConfig = merge(baseWebpackConfig, { + module: { + rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) + }, + // cheap-module-eval-source-map is faster for development + devtool: config.dev.devtool, + + // these devServer options should be customized in /config/index.js + devServer: { + clientLogLevel: 'warning', + historyApiFallback: { + rewrites: [ + { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') }, + ], + }, + hot: true, + contentBase: false, // since we use CopyWebpackPlugin. + compress: true, + host: HOST || config.dev.host, + port: PORT || config.dev.port, + open: config.dev.autoOpenBrowser, + overlay: config.dev.errorOverlay + ? { warnings: false, errors: true } + : false, + publicPath: config.dev.assetsPublicPath, + proxy: config.dev.proxyTable, + quiet: true, // necessary for FriendlyErrorsPlugin + watchOptions: { + poll: config.dev.poll, + } + }, + plugins: [ + new webpack.DefinePlugin({ + 'process.env': require('../config/dev.env') + }), + new webpack.HotModuleReplacementPlugin(), + new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update. + new webpack.NoEmitOnErrorsPlugin(), + // https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: 'index.html', + template: 'index.html', + inject: true + }), + // copy custom static assets + new CopyWebpackPlugin([ + { + from: path.resolve(__dirname, '../static'), + to: config.dev.assetsSubDirectory, + ignore: ['.*'] + } + ]) + ] +}) + +module.exports = new Promise((resolve, reject) => { + portfinder.basePort = process.env.PORT || config.dev.port + portfinder.getPort((err, port) => { + if (err) { + reject(err) + } else { + // publish the new Port, necessary for e2e tests + process.env.PORT = port + // add port to devServer config + devWebpackConfig.devServer.port = port + + // Add FriendlyErrorsPlugin + devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({ + compilationSuccessInfo: { + messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`], + }, + onErrors: config.dev.notifyOnErrors + ? utils.createNotifierCallback() + : undefined + })) + + resolve(devWebpackConfig) + } + }) +}) diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/build/webpack.prod.conf.js b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/build/webpack.prod.conf.js new file mode 100644 index 0000000000..1f71ad6454 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/build/webpack.prod.conf.js @@ -0,0 +1,147 @@ +'use strict' +const path = require('path') +const utils = require('./utils') +const webpack = require('webpack') +const config = require('../config') +const merge = require('webpack-merge') +const baseWebpackConfig = require('./webpack.base.conf') +const CopyWebpackPlugin = require('copy-webpack-plugin') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const ExtractTextPlugin = require('extract-text-webpack-plugin') +const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') +const UglifyJsPlugin = require('uglifyjs-webpack-plugin') +const VueLoaderPlugin = require('vue-loader/lib/plugin'); + +const env = require('../config/prod.env') + +const webpackConfig = merge(baseWebpackConfig, { + module: { + rules: utils.styleLoaders({ + sourceMap: config.build.productionSourceMap, + extract: true, + usePostCSS: true + }) + }, + devtool: config.build.productionSourceMap ? config.build.devtool : false, + output: { + path: config.build.assetsRoot, + filename: utils.assetsPath('js/[name].[chunkhash].js'), + chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') + }, + plugins: [ + new VueLoaderPlugin(), + // http://vuejs.github.io/vue-loader/en/workflow/production.html + new webpack.DefinePlugin({ + 'process.env': env + }), + new UglifyJsPlugin({ + uglifyOptions: { + compress: { + warnings: false + } + }, + sourceMap: config.build.productionSourceMap, + parallel: true + }), + // extract css into its own file + new ExtractTextPlugin({ + filename: utils.assetsPath('css/[name].[contenthash].css'), + // Setting the following option to `false` will not extract CSS from codesplit chunks. + // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack. + // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, + // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110 + allChunks: true, + }), + // Compress extracted CSS. We are using this plugin so that possible + // duplicated CSS from different components can be deduped. + new OptimizeCSSPlugin({ + cssProcessorOptions: config.build.productionSourceMap + ? { safe: true, map: { inline: false } } + : { safe: true } + }), + // generate dist index.html with correct asset hash for caching. + // you can customize output by editing /index.html + // see https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: config.build.index, + template: 'index.html', + inject: true, + minify: { + removeComments: true, + collapseWhitespace: true, + removeAttributeQuotes: true + // more options: + // https://github.com/kangax/html-minifier#options-quick-reference + }, + // necessary to consistently work with multiple chunks via CommonsChunkPlugin + chunksSortMode: 'dependency' + }), + // keep module.id stable when vendor modules does not change + new webpack.HashedModuleIdsPlugin(), + // enable scope hoisting + new webpack.optimize.ModuleConcatenationPlugin(), + // split vendor js into its own file + new webpack.optimize.CommonsChunkPlugin({ + name: 'vendor', + minChunks (module) { + // any required modules inside node_modules are extracted to vendor + return ( + module.resource && + /\.js$/.test(module.resource) && + module.resource.indexOf( + path.join(__dirname, '../node_modules') + ) === 0 + ) + } + }), + // extract webpack runtime and module manifest to its own file in order to + // prevent vendor hash from being updated whenever app bundle is updated + new webpack.optimize.CommonsChunkPlugin({ + name: 'manifest', + minChunks: Infinity + }), + // This instance extracts shared chunks from code split chunks and bundles them + // in a separate chunk, similar to the vendor chunk + // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk + new webpack.optimize.CommonsChunkPlugin({ + name: 'app', + async: 'vendor-async', + children: true, + minChunks: 3 + }), + + // copy custom static assets + new CopyWebpackPlugin([ + { + from: path.resolve(__dirname, '../static'), + to: config.build.assetsSubDirectory, + ignore: ['.*'] + } + ]) + ] +}) + +if (config.build.productionGzip) { + const CompressionWebpackPlugin = require('compression-webpack-plugin') + + webpackConfig.plugins.push( + new CompressionWebpackPlugin({ + asset: '[path].gz[query]', + algorithm: 'gzip', + test: new RegExp( + '\\.(' + + config.build.productionGzipExtensions.join('|') + + ')$' + ), + threshold: 10240, + minRatio: 0.8 + }) + ) +} + +if (config.build.bundleAnalyzerReport) { + const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin + webpackConfig.plugins.push(new BundleAnalyzerPlugin()) +} + +module.exports = webpackConfig diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/config/dev.env.js b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/config/dev.env.js new file mode 100644 index 0000000000..1e22973ae7 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/config/dev.env.js @@ -0,0 +1,7 @@ +'use strict' +const merge = require('webpack-merge') +const prodEnv = require('./prod.env') + +module.exports = merge(prodEnv, { + NODE_ENV: '"development"' +}) diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/config/index.js b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/config/index.js new file mode 100644 index 0000000000..c5eded7f81 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/config/index.js @@ -0,0 +1,69 @@ +'use strict' +// Template version: 1.3.1 +// see http://vuejs-templates.github.io/webpack for documentation. + +const path = require('path') + +module.exports = { + dev: { + + // Paths + assetsSubDirectory: 'static', + assetsPublicPath: '/', + proxyTable: {}, + + // Various Dev Server settings + host: 'localhost', // can be overwritten by process.env.HOST + port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined + autoOpenBrowser: false, + errorOverlay: true, + notifyOnErrors: true, + poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- + + + /** + * Source Maps + */ + + // https://webpack.js.org/configuration/devtool/#development + devtool: 'cheap-module-eval-source-map', + + // If you have problems debugging vue-files in devtools, + // set this to false - it *may* help + // https://vue-loader.vuejs.org/en/options.html#cachebusting + cacheBusting: true, + + cssSourceMap: true + }, + + build: { + // Template for index.html + index: path.resolve(__dirname, '../dist/index.html'), + + // Paths + assetsRoot: path.resolve(__dirname, '../dist'), + assetsSubDirectory: 'static', + assetsPublicPath: '/', + + /** + * Source Maps + */ + + productionSourceMap: true, + // https://webpack.js.org/configuration/devtool/#production + devtool: '#source-map', + + // Gzip off by default as many popular static hosts such as + // Surge or Netlify already gzip all static assets for you. + // Before setting to `true`, make sure to: + // npm install --save-dev compression-webpack-plugin + productionGzip: false, + productionGzipExtensions: ['js', 'css'], + + // Run the build command with an extra argument to + // View the bundle analyzer report after build finishes: + // `npm run build --report` + // Set to `true` or `false` to always turn it on or off + bundleAnalyzerReport: process.env.npm_config_report + } +} diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/config/prod.env.js b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/config/prod.env.js new file mode 100644 index 0000000000..a6f997616e --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/config/prod.env.js @@ -0,0 +1,4 @@ +'use strict' +module.exports = { + NODE_ENV: '"production"' +} diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/index.html b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/index.html new file mode 100644 index 0000000000..03ce3fdf27 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/index.html @@ -0,0 +1,12 @@ + + + + + + deplugin-view-frontend + + +
+ + + diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/package.json b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/package.json new file mode 100644 index 0000000000..dd121e3144 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/package.json @@ -0,0 +1,75 @@ +{ + "name": "deplugin-view-frontend", + "version": "1.0.0", + "description": "A Vue.js project", + "author": "", + "private": true, + "scripts": { + "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", + "start": "npm run dev", + "build": "node build/build.js", + "buildPlugin": "node build/build-async-plugins.js" + }, + "dependencies": { + "@antv/g2plot": "^2.4.31", + "@riophae/vue-treeselect": "0.4.0", + "lodash": "^4.17.21", + "vue": "2.6.10", + "vue-i18n": "7.3.2", + "vue-router": "^3.0.1", + "vue-uuid": "2.0.2", + "vuedraggable": "^2.24.3", + "vuex": "3.1.0" + }, + "devDependencies": { + "autoprefixer": "^7.1.2", + "babel-core": "^6.22.1", + "babel-helper-vue-jsx-merge-props": "^2.0.3", + "babel-loader": "^7.1.1", + "babel-plugin-syntax-jsx": "^6.18.0", + "babel-plugin-transform-runtime": "^6.22.0", + "babel-plugin-transform-vue-jsx": "^3.5.0", + "babel-preset-env": "^1.3.2", + "babel-preset-stage-2": "^6.22.0", + "chalk": "^2.0.1", + "copy-webpack-plugin": "^4.6.0", + "css-loader": "^0.28.0", + "element-ui": "2.15.7", + "extract-text-webpack-plugin": "^4.0.0-beta.0", + "file-loader": "^1.1.4", + "friendly-errors-webpack-plugin": "^1.6.1", + "html-webpack-plugin": "^3.2.0", + "js-cookie": "2.2.0", + "node-notifier": "^8.0.1", + "optimize-css-assets-webpack-plugin": "^3.2.0", + "ora": "^1.2.0", + "portfinder": "^1.0.13", + "postcss-import": "^11.0.0", + "postcss-loader": "^2.0.8", + "postcss-url": "^7.2.1", + "rimraf": "^2.6.0", + "sass": "^1.33.0", + "sass-loader": "^7.3.1", + "semver": "^5.3.0", + "shelljs": "^0.8.5", + "svg-sprite-loader": "4.1.3", + "uglifyjs-webpack-plugin": "^1.1.1", + "url-loader": "^0.5.8", + "vue-loader": "^15.6.4", + "vue-style-loader": "^4.1.2", + "vue-template-compiler": "2.6.10", + "webpack": "^4.8.1", + "webpack-bundle-analyzer": "^3.3.2", + "webpack-dev-server": "^3.1.11", + "webpack-merge": "^4.1.0" + }, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + }, + "browserslist": [ + "> 1%", + "last 2 versions", + "not ie <= 8" + ] +} diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/pom.xml b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/pom.xml new file mode 100644 index 0000000000..21ee6745e0 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/pom.xml @@ -0,0 +1,80 @@ + + + + + io.dataease + view-chartmix + ${dataease.version} + + + 4.0.0 + view-chartmix-frontend + + + UTF-8 + UTF-8 + 1.9.1 + + + + + + maven-clean-plugin + + + + static + + ** + + false + + + + + + + + com.github.eirslett + frontend-maven-plugin + ${frontend-maven-plugin.version} + + + install node and npm + + install-node-and-npm + + + + v16.20.2 + 7.6.3 + + + + + npm install + + npm + + + + install + + + + + npm run buildPlugin + + npm + + + run buildPlugin + + + + + + + diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/App.vue b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/App.vue new file mode 100644 index 0000000000..8542e07722 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/App.vue @@ -0,0 +1,23 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/assets/logo.png b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/assets/logo.png new file mode 100644 index 0000000000..f3d2503fc2 Binary files /dev/null and b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/assets/logo.png differ diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/HelloWorld.vue b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/HelloWorld.vue new file mode 100644 index 0000000000..1c19f2a399 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/HelloWorld.vue @@ -0,0 +1,113 @@ + + + + + + diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/SvgIcon/index.vue b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/SvgIcon/index.vue new file mode 100644 index 0000000000..f81b67f667 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/SvgIcon/index.vue @@ -0,0 +1,60 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/selector/BackgroundColorSelector.vue b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/selector/BackgroundColorSelector.vue new file mode 100644 index 0000000000..e1cbf5fe18 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/selector/BackgroundColorSelector.vue @@ -0,0 +1,102 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/selector/ColorSelector.vue b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/selector/ColorSelector.vue new file mode 100644 index 0000000000..3a579e8889 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/selector/ColorSelector.vue @@ -0,0 +1,306 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/selector/LabelSelector.vue b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/selector/LabelSelector.vue new file mode 100644 index 0000000000..16b95033cc --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/selector/LabelSelector.vue @@ -0,0 +1,259 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/selector/LegendSelector.vue b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/selector/LegendSelector.vue new file mode 100644 index 0000000000..5125412616 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/selector/LegendSelector.vue @@ -0,0 +1,165 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/selector/SizeSelectorAntV.vue b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/selector/SizeSelectorAntV.vue new file mode 100644 index 0000000000..7ca0b08909 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/selector/SizeSelectorAntV.vue @@ -0,0 +1,130 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/selector/TitleSelector.vue b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/selector/TitleSelector.vue new file mode 100644 index 0000000000..938dd29c36 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/selector/TitleSelector.vue @@ -0,0 +1,269 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/selector/TooltipSelector.vue b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/selector/TooltipSelector.vue new file mode 100644 index 0000000000..c59e0f6185 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/selector/TooltipSelector.vue @@ -0,0 +1,144 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/selector/TooltipSelectorAntV.vue b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/selector/TooltipSelectorAntV.vue new file mode 100644 index 0000000000..111041be39 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/selector/TooltipSelectorAntV.vue @@ -0,0 +1,228 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/views/ChartDragItem.vue b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/views/ChartDragItem.vue new file mode 100644 index 0000000000..53ae1e2ad0 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/views/ChartDragItem.vue @@ -0,0 +1,278 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/views/ChartTitleUpdate.vue b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/views/ChartTitleUpdate.vue new file mode 100644 index 0000000000..efadca90bb --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/views/ChartTitleUpdate.vue @@ -0,0 +1,480 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/views/DimensionExtItem.vue b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/views/DimensionExtItem.vue new file mode 100644 index 0000000000..c414ad2d36 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/views/DimensionExtItem.vue @@ -0,0 +1,256 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/views/DrillItem.vue b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/views/DrillItem.vue new file mode 100644 index 0000000000..7321b8a08c --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/views/DrillItem.vue @@ -0,0 +1,146 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/views/FieldErrorTips.vue b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/views/FieldErrorTips.vue new file mode 100644 index 0000000000..9d7b574721 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/views/FieldErrorTips.vue @@ -0,0 +1,21 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/views/FilterItem.vue b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/views/FilterItem.vue new file mode 100644 index 0000000000..3dd5952d69 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/views/FilterItem.vue @@ -0,0 +1,154 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/views/LocationLineItem.vue b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/views/LocationLineItem.vue new file mode 100644 index 0000000000..7b2985d08f --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/views/LocationLineItem.vue @@ -0,0 +1,170 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/views/QuotaExtItem.vue b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/views/QuotaExtItem.vue new file mode 100644 index 0000000000..826d1394e9 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/views/QuotaExtItem.vue @@ -0,0 +1,414 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/views/QuotaItem.vue b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/views/QuotaItem.vue new file mode 100644 index 0000000000..9eb9dbaf68 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/views/QuotaItem.vue @@ -0,0 +1,414 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/views/SankeyDimensionItem.vue b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/views/SankeyDimensionItem.vue new file mode 100644 index 0000000000..74a88f39bc --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/views/SankeyDimensionItem.vue @@ -0,0 +1,420 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/views/ViewTrackBar.vue b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/views/ViewTrackBar.vue new file mode 100644 index 0000000000..a58cad9770 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/views/ViewTrackBar.vue @@ -0,0 +1,80 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/views/utils.js b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/views/utils.js new file mode 100644 index 0000000000..307c4f637e --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/components/views/utils.js @@ -0,0 +1,99 @@ +export function getItemType(dimensionData, quotaData, item) { + // Check whether the current view is in template status + // ( dimensionData and quotaData have no data). If yes, return 'success' directly + if (dimensionData.length === 0 && quotaData.length === 0) { + return 'success' + } + // 将item的字段在数据集维度、指标字段中查询一遍,如果遇到id不存在、字段类型不一致、维度指标不一致,则提示 + const status = item.groupType + let checked = false + if (status === 'd') { + for (let i = 0; i < dimensionData.length; i++) { + const ele = dimensionData[i] + if (item.chartId) { + if (ele.dataeaseName === item.dataeaseName && ele.deType === item.deType && ele.groupType === item.groupType) { + checked = true + break + } + } else { + if (ele.id === item.id && ele.deType === item.deType && ele.groupType === item.groupType) { + checked = true + break + } + } + } + } + if (status === 'q') { + for (let i = 0; i < quotaData.length; i++) { + const ele = quotaData[i] + if (item.chartId) { + if (ele.dataeaseName === item.dataeaseName && ele.deType === item.deType && ele.groupType === item.groupType) { + checked = true + break + } + } else { + if (ele.id === item.id && ele.deType === item.deType && ele.groupType === item.groupType) { + checked = true + break + } + } + } + } + + if (checked) { + if (status === 'd') { + return '' + } else if (status === 'q') { + return 'success' + } + } else { + return 'danger' + } +} + +export function getRemark(chart) { + const remark = {} + if (chart.customStyle) { + const customStyle = JSON.parse(chart.customStyle) + if (customStyle.text) { + const title = JSON.parse(JSON.stringify(customStyle.text)) + remark.show = title.remarkShow ? title.remarkShow : false + remark.content = title.remark ? title.remark : '' + remark.bgFill = title.remarkBackgroundColor ? title.remarkBackgroundColor : '#ffffffff' + } + } + return remark +} + +export function getOriginFieldName(dimensionList, quotaList, field) { + let originName = '' + for (let i = 0; i < dimensionList.length; i++) { + const item = dimensionList[i] + if (item.id === field.id) { + originName = item.name + break + } + } + for (let i = 0; i < quotaList.length; i++) { + const item = quotaList[i] + if (item.id === field.id) { + originName = item.name + break + } + } + return originName +} + +export function resetValueFormatter(item) { + if (item) { + item.formatterCfg = { + type: 'auto', // auto,value,percent + unit: 1, // 换算单位 + suffix: '', // 单位后缀 + decimalCount: 2, // 小数位数 + thousandSeparator: true// 千分符 + } + } +} + +export const quotaViews = ['label', 'richTextView', 'text', 'gauge', 'liquid'] diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/de-base/lang/en.js b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/de-base/lang/en.js new file mode 100644 index 0000000000..f48bb572e7 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/de-base/lang/en.js @@ -0,0 +1,22 @@ +export default { + plugin_view_chartmix: { + source: 'Source', + target: 'Target', + map_type: 'Map Type', + link_line: 'Link Line', + mark_size: 'Weight', + type_title: 'ChartMix', + label: 'Area', + angle: 'Symbol', + marker: 'Marker', + pentagon: 'Pentagon', + hexagon: 'Hexagon', + octagon: 'Octagon', + hexagram: 'Hexagram', + border: 'Border', + light: 'Light', + dark: 'Dark', + label_format_tip: 'The field value can be read in the form of {field Name}, the fields in the label and the tips are interchangeable, and the built-in latitude and longitude related fields', + tooltip_format_tip: 'The field value can be read in the form of {field Name}, the fields in the label and the tips are interchangeable, and the built-in latitude and longitude related fields.(the label does not support line breaks)', + } +} diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/de-base/lang/index.js b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/de-base/lang/index.js new file mode 100644 index 0000000000..e50d0478e6 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/de-base/lang/index.js @@ -0,0 +1,49 @@ +import Vue from 'vue' +import VueI18n from 'vue-i18n' +import Cookies from 'js-cookie' +import elementEnLocale from 'element-ui/lib/locale/lang/en' // element-ui lang +import elementZhLocale from 'element-ui/lib/locale/lang/zh-CN'// element-ui lang +import elementTWLocale from 'element-ui/lib/locale/lang/zh-TW'// element-ui lang + +import localMessages from './messages' + + +Vue.use(VueI18n) + +const messages = { + en_US: { + ...localMessages['en_US'], + ...elementEnLocale + }, + zh_CN: { + ...localMessages['zh_CN'], + ...elementZhLocale + }, + zh_TW: { + ...localMessages['zh_TW'], + ...elementTWLocale + } +} +export function getLanguage () { + const chooseLanguage = Cookies.get('language') + if (chooseLanguage) return chooseLanguage + + // if has not choose language + const language = (navigator.language || navigator.browserLanguage).toLowerCase() + const locales = Object.keys(messages) + for (const locale of locales) { + if (language.indexOf(locale) > -1) { + return locale + } + } + return 'zh_CN' +} +const i18n = new VueI18n({ + // set locale + // options: en | zh | es + locale: getLanguage(), + // set locale messages + messages +}) + +export default i18n diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/de-base/lang/messages.js b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/de-base/lang/messages.js new file mode 100644 index 0000000000..844f8c0673 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/de-base/lang/messages.js @@ -0,0 +1,17 @@ +import enLocale from './en' +import zhLocale from './zh' +import twLocale from './tw' + +const messages = { + en_US: { + ...enLocale + }, + zh_CN: { + ...zhLocale + }, + zh_TW: { + ...twLocale + } +} + +export default messages \ No newline at end of file diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/de-base/lang/tw.js b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/de-base/lang/tw.js new file mode 100644 index 0000000000..cd211e19d0 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/de-base/lang/tw.js @@ -0,0 +1,23 @@ +export default { + plugin_view_chartmix: { + type_title: '組合圖', + source: '源', + target: '目標', + map_type: '類型', + link_line: '連線', + mark_size: '權重', + label: '區域', + angle: '符號', + marker: '標記', + pentagon: '五邊形', + hexagon: '六邊形', + + octagon: '八邊形', + hexagram: '六角星', + border: '邊框', + light: '亮色', + dark: '暗色', + label_format_tip: '可以${fieldName}的形式讀取字段值,標籤和提示中的字段互相通用,內置經緯度相關字段', + tooltip_format_tip: '可以${fieldName}的形式讀取字段值,標籤和提示中的字段互相通用,內置經緯度相關字段(標籤不支持換行)', + } +} diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/de-base/lang/zh.js b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/de-base/lang/zh.js new file mode 100644 index 0000000000..894e2ed166 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/de-base/lang/zh.js @@ -0,0 +1,21 @@ +export default { + plugin_view_chartmix: { + type_title: '组合图', + source: '源', + target: '目标', + map_type: '类型', + link_line: '连线', + mark_size: '权重', + angle: '符号', + marker: '标记', + pentagon: '五角形', + hexagon: '六角形', + octagon: '八角形', + hexagram: '六角星', + border: '边框', + light: '亮色', + dark: '暗色', + label_format_tip: '可以${fieldName}的形式读取字段值,标签和提示中的字段互相通用,内置经纬度相关字段', + tooltip_format_tip: '可以${fieldName}的形式读取字段值,标签和提示中的字段互相通用,内置经纬度相关字段(标签不支持换行)', + } +} diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/icons/index.js b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/icons/index.js new file mode 100644 index 0000000000..2c6b309c96 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/icons/index.js @@ -0,0 +1,9 @@ +import Vue from 'vue' +import SvgIcon from '@/components/SvgIcon'// svg component + +// register globally +Vue.component('svg-icon', SvgIcon) + +const req = require.context('./svg', false, /\.svg$/) +const requireAll = requireContext => requireContext.keys().map(requireContext) +requireAll(req) diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/icons/svg/chart-mix.svg b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/icons/svg/chart-mix.svg new file mode 100644 index 0000000000..634d13d261 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/icons/svg/chart-mix.svg @@ -0,0 +1 @@ + diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/icons/svg/view-chart-mix-backend.svg b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/icons/svg/view-chart-mix-backend.svg new file mode 100644 index 0000000000..becfdb6caf --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/icons/svg/view-chart-mix-backend.svg @@ -0,0 +1 @@ +【icon】插件管理-导出 \ No newline at end of file diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/icons/svgo.yml b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/icons/svgo.yml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/main.js b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/main.js new file mode 100644 index 0000000000..b1d6ebdf5f --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/main.js @@ -0,0 +1,36 @@ +// The Vue build version to load with the `import` command +// (runtime-only or standalone) has been set in webpack.base.conf with an alias. +import Vue from 'vue' +import App from './App' +import router from './router' +import ElementUI from 'element-ui' +import Cookies from 'js-cookie' +import i18n from './de-base/lang' +import draggable from 'vuedraggable' +import Treeselect from '@riophae/vue-treeselect' +import '@riophae/vue-treeselect/dist/vue-treeselect.css' +import '@/icons' // icon +import { GaodeMap } from '@antv/l7-maps' +Vue.config.productionTip = false +Vue.use(ElementUI, { + size: Cookies.get('size') || 'medium', + i18n: (key, value) => i18n.t(key, value) +}) +Vue.component('Treeselect', Treeselect) +Vue.component('draggable', draggable) +Vue.prototype.hasDataPermission = function(pTarget, pSource) { + + if (pSource && pTarget) { + return pSource.indexOf(pTarget) > -1 + } + return false +} +Vue.prototype.$gaodeMap = GaodeMap +/* eslint-disable no-new */ +new Vue({ + el: '#app', + router, + i18n, + components: { App }, + template: '' +}) diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/router/index.js b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/router/index.js new file mode 100644 index 0000000000..5a43526962 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/router/index.js @@ -0,0 +1,21 @@ +import Vue from 'vue' +import Router from 'vue-router' +import HelloWorld from '@/components/HelloWorld' +import SymbolMap from '@/views/antv/chartmix/type' + +Vue.use(Router) + +export default new Router({ + routes: [ + { + path: '/', + name: 'HelloWorld', + component: HelloWorld + }, + { + path: '/chart-mix', + name: 'chart-mix', + component: SymbolMap + } + ] +}) diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/utils/chartmix.js b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/utils/chartmix.js new file mode 100644 index 0000000000..697800dd8d --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/utils/chartmix.js @@ -0,0 +1,73 @@ +// import {PointLayer, Popup } from '@antv/l7'; + +export const DEFAULT_COLOR_CASE = { + value: 'default', + colors: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'], + alpha: 100, + tableHeaderBgColor: '#e1eaff', + tableItemBgColor: '#ffffff', + tableFontColor: '#000000', + tableStripe: true, + dimensionColor: '#000000', + quotaColor: '#000000', + tableBorderColor: '#cfdaf4' +} + +export const COLOR_PANEL = [ + '#ff4500', + '#ff8c00', + '#ffd700', + '#90ee90', + '#00ced1', + '#1e90ff', + '#c71585', + '#999999', + '#000000', + '#FFFFFF' +] + + +export const DEFAULT_BACKGROUND_COLOR = { + color: '#ffffff', + alpha: 100, + borderRadius: 5 +} + + + + +export function baseSymbolMap(scene, container, chart, action, $pointLayer, $popup) { + let _self = this + + +} + + +export function hexColorToRGBA(hex, alpha) { + const rgb = [] // 定义rgb数组 + if (/^\#[0-9A-F]{3}$/i.test(hex)) { // 判断传入是否为#三位十六进制数 + let sixHex = '#' + hex.replace(/[0-9A-F]/ig, function(kw) { + sixHex += kw + kw // 把三位16进制数转化为六位 + }) + hex = sixHex // 保存回hex + } + if (/^#[0-9A-F]{6}$/i.test(hex)) { // 判断传入是否为#六位十六进制数 + hex.replace(/[0-9A-F]{2}/ig, function(kw) { + // eslint-disable-next-line no-eval + rgb.push(eval('0x' + kw)) // 十六进制转化为十进制并存如数组 + }) + return `rgba(${rgb.join(',')},${alpha / 100})` // 输出RGB格式颜色 + } else { + return 'rgb(0,0,0)' + } +} + + + + + +export function uuid() { + return (((1+Math.random())*0x10000)|0).toString(16).substring(1); +} + diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/utils/clickoutside.js b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/utils/clickoutside.js new file mode 100644 index 0000000000..fb030a98eb --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/utils/clickoutside.js @@ -0,0 +1,68 @@ +const nodeList = []; +const ctx = '@@clickoutsideContext'; + +let startClick; +let seed = 0; + + +function createDocumentHandler(el, binding, vnode) { + return function(mouseup = {}, mousedown = {}) { + if (!vnode || + !vnode.context || + !mouseup.target || + !mousedown.target || + el.contains(mouseup.target) || + el.contains(mousedown.target) || + el === mouseup.target || + (vnode.context.popperElm && + (vnode.context.popperElm.contains(mouseup.target) || + vnode.context.popperElm.contains(mousedown.target)))) return; + + if (binding.expression && + el[ctx].methodName && + vnode.context[el[ctx].methodName]) { + vnode.context[el[ctx].methodName](); + } else { + el[ctx].bindingFn && el[ctx].bindingFn(); + } + }; +} + +/** + * v-clickoutside + * @desc 点击元素外面才会触发的事件 + * @example + * ```vue + *
+ * ``` + */ +export default { + bind(el, binding, vnode) { + nodeList.push(el); + const id = seed++; + el[ctx] = { + id, + documentHandler: createDocumentHandler(el, binding, vnode), + methodName: binding.expression, + bindingFn: binding.value + }; + }, + + update(el, binding, vnode) { + el[ctx].documentHandler = createDocumentHandler(el, binding, vnode); + el[ctx].methodName = binding.expression; + el[ctx].bindingFn = binding.value; + }, + + unbind(el) { + let len = nodeList.length; + + for (let i = 0; i < len; i++) { + if (nodeList[i][ctx].id === el[ctx].id) { + nodeList.splice(i, 1); + break; + } + } + delete el[ctx]; + } +}; diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/utils/compare.js b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/utils/compare.js new file mode 100644 index 0000000000..b06a104a10 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/utils/compare.js @@ -0,0 +1,29 @@ +export const compareItem = { + type: 'none', // year-yoy/month-yoy等 + resultData: 'percent', // 对比差sub,百分比percent等 + field: '', + custom: { + field: '', + calcType: '0', // 0-增长值,1-增长率 + timeType: '0', // 0-固定日期,1-日期区间 + currentTime: '', + compareTime: '', + currentTimeRange: [], + compareTimeRange: [] + } +} + +export const compareYearList = [ + { name: 'year_mom', value: 'year_mom' } +] + +export const compareMonthList = [ + { name: 'month_mom', value: 'month_mom' }, + { name: 'year_yoy', value: 'year_yoy' } +] + +export const compareDayList = [ + { name: 'day_mom', value: 'day_mom' }, + { name: 'month_yoy', value: 'month_yoy' }, + { name: 'year_yoy', value: 'year_yoy' } +] diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/utils/formatter.js b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/utils/formatter.js new file mode 100644 index 0000000000..5a51c89dda --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/utils/formatter.js @@ -0,0 +1,74 @@ +export const formatterItem = { + type: 'auto', // auto,value,percent + unit: 1, // 换算单位 + suffix: '', // 单位后缀 + decimalCount: 2, // 小数位数 + thousandSeparator: true// 千分符 +} + +// 单位list +export const unitList = [ + { name: 'unit_none', value: 1 }, + { name: 'unit_thousand', value: 1000 }, + { name: 'unit_ten_thousand', value: 10000 }, + { name: 'unit_million', value: 1000000 }, + { name: 'unit_hundred_million', value: 100000000 } +] + +// 格式化方式 +export const formatterType = [ + { name: 'value_formatter_auto', value: 'auto' }, + { name: 'value_formatter_value', value: 'value' }, + { name: 'value_formatter_percent', value: 'percent' } +] + +export function valueFormatter(value, formatter) { + if (value === null || value === undefined) { + return null + } + // 1.unit 2.decimal 3.thousand separator and suffix + let result + if (formatter.type === 'auto') { + result = transSeparatorAndSuffix(transUnit(value, formatter), formatter) + } else if (formatter.type === 'value') { + result = transSeparatorAndSuffix(transDecimal(transUnit(value, formatter), formatter), formatter) + } else if (formatter.type === 'percent') { + value = value * 100 + result = transSeparatorAndSuffix(transDecimal(value, formatter), formatter) + } else { + result = value + } + return result +} + +function transUnit(value, formatter) { + return value / formatter.unit +} + +function transDecimal(value, formatter) { + return value.toFixed(formatter.decimalCount) +} + +function transSeparatorAndSuffix(value, formatter) { + let str = value + '' + if (formatter.thousandSeparator) { + const thousandsReg = /(\d)(?=(\d{3})+$)/g + const numArr = str.split('.') + numArr[0] = numArr[0].replace(thousandsReg, '$1,') + str = numArr.join('.') + } + if (formatter.type === 'percent') { + str += '%' + } else { + if (formatter.unit === 1000) { + str += '千' + } else if (formatter.unit === 10000) { + str += '万' + } else if (formatter.unit === 1000000) { + str += '百万' + } else if (formatter.unit === 100000000) { + str += '亿' + } + } + return str + formatter.suffix.replace(/(^\s*)|(\s*$)/g, '') +} diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/utils/map.js b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/utils/map.js new file mode 100644 index 0000000000..6030857ca0 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/utils/map.js @@ -0,0 +1,595 @@ +export const DEFAULT_COLOR_CASE = { + value: 'default', + colors: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'], + alpha: 100, + tableHeaderBgColor: '#e1eaff', + tableItemBgColor: '#ffffff', + tableFontColor: '#000000', + tableStripe: true, + dimensionColor: '#000000', + quotaColor: '#000000', + tableBorderColor: '#cfdaf4' +} +export const DEFAULT_SIZE = { + barDefault: true, + barWidth: 40, + barGap: 0.4, + lineWidth: 2, + lineType: 'solid', + lineSymbol: 'marker', + lineSymbolSize: 4, + lineSmooth: true, + lineArea: false, + pieInnerRadius: 0, + pieOuterRadius: 80, + pieRoseType: 'radius', + pieRoseRadius: 5, + funnelWidth: 80, + radarShape: 'polygon', + radarSize: 80, + tableTitleFontSize: 12, + tableItemFontSize: 12, + tableTitleHeight: 36, + tableItemHeight: 36, + tablePageSize: '20', + tableColumnMode: 'custom', + tableColumnWidth: 100, + tableHeaderAlign: 'left', + tableItemAlign: 'right', + gaugeMin: 0, + gaugeMax: 100, + gaugeStartAngle: 225, + gaugeEndAngle: -45, + dimensionFontSize: 18, + quotaFontSize: 18, + spaceSplit: 10, + dimensionShow: true, + quotaShow: true, + scatterSymbol: 'marker', + scatterSymbolSize: 15, + symbolOpacity: 5, + symbolStrokeWidth: 1, + treemapWidth: 80, + treemapHeight: 80, + liquidMax: 100, + liquidSize: 80, + liquidOutlineBorder: 4, + liquidOutlineDistance: 8, + liquidWaveLength: 128, + liquidWaveCount: 3, + liquidShape: 'circle', + tablePageMode: 'page' +} +export const COLOR_PANEL = [ + '#ff4500', + '#ff8c00', + '#ffd700', + '#90ee90', + '#00ced1', + '#1e90ff', + '#c71585', + '#999999', + '#000000', + '#FFFFFF' +] + +export const DEFAULT_LABEL = { + show: true, + position: 'middle', + color: '#000000', + fontSize: '10', + formatter: '{c}', + gaugeFormatter: '{value}', + labelLine: { + show: true + }, + fields: null, + labelTemplate: '{busiValue}', + initialized: true +} +export const DEFAULT_TOOLTIP = { + show: true, + trigger: 'item', + confine: true, + textStyle: { + fontSize: '10', + color: '#909399' + }, + backgroundColor: '#ffffff', + formatter: '', + tooltipTemplate: '{busiValue}' +} +export const DEFAULT_TITLE_STYLE = { + show: true, + fontSize: '18', + color: '#000000', + hPosition: 'left', + vPosition: 'top', + isItalic: false, + isBolder: true, + remarkShow: false, + remark: '', + remarkBackgroundColor: '#ffffffff', + fontFamily: 'Microsoft YaHei', + letterSpace: '0', + fontShadow: false +} +export const DEFAULT_LEGEND_STYLE = { + show: true, + hPosition: 'center', + vPosition: 'bottom', + orient: 'horizontal', + icon: 'circle', + textStyle: { + color: '#333333', + fontSize: '12' + } +} +export const DEFAULT_BACKGROUND_COLOR = { + color: '#ffffff', + alpha: 100, + borderRadius: 5 +} +export const DEFAULT_BASE_MAP_STYLE = { + baseMapTheme: 'light' +} +export const BASE_MAP = { + title: { + text: '', + textStyle: { + fontWeight: 'normal' + } + }, + visualMap: { + min: 50, + max: 52, + text: ['High', 'Low'], + realtime: false, + calculable: true, + inRange: { + color: ['lightskyblue', 'yellow', 'orangered'] + }, + right: 0 + }, + + tooltip: {}, + geo: [ + { + show: true, + map: 'BUDDLE_MAP_BORDER', + + emphasis: { + disabled: true + }, + itemStyle: { + borderWidth: 2, + borderColor: '#d1d1d1', + borderType: 'solid' + }, + + roam: false + }, + { + show: true, + map: 'BUDDLE_MAP', + label: { + normal: { + show: false + }, + emphasis: { + show: false + } + }, + itemStyle: { + areaColor: '#f3f3f3', + borderType: 'dashed', + borderColor: '#fff' + }, + + roam: false + } + ], + series: [ + { + name: '', + type: 'scatter', + coordinateSystem: 'geo', + data: [] + } + ] +} + +export const compareItem = { + type: 'none', // year-yoy/month-yoy等 + resultData: 'percent', // 对比差sub,百分比percent等 + field: '', + custom: { + field: '', + calcType: '0', // 0-增长值,1-增长率 + timeType: '0', // 0-固定日期,1-日期区间 + currentTime: '', + compareTime: '', + currentTimeRange: [], + compareTimeRange: [] + } +} + +export const formatterItem = { + type: 'auto', // auto,value,percent + unit: 1, // 换算单位 + suffix: '', // 单位后缀 + decimalCount: 2, // 小数位数 + thousandSeparator: true// 千分符 +} + +const convertData = (mapData, chart) => { + let maxVal = 0 + const k = terminalType === 'pc' ? 30 : 15 + const names = chart.data.x + const results = [] + for (let index = 0; index < names.length; index++) { + const name = names[index]; + const item = chart.data.series[0].data[index] + results.push({ + name, + value: (mapData[name] ? mapData[name].concat(item.value) : []), + dimensionList: item.dimensionList, + quotaList: item.quotaList + }) + maxVal = Math.max(maxVal, item.value) + } + const rate = k / maxVal + + return { + value: results, + rate: rate + } +} + +let terminalType = 'pc' + +export function baseMapOption(chart_option, chart, mapData, terminal = 'pc') { + terminalType = terminal + // 处理shape attr + let customAttr = {} + if (chart.customAttr) { + customAttr = JSON.parse(chart.customAttr) + if (customAttr.color) { + chart_option.color = customAttr.color.colors + } + // tooltip + if (customAttr.tooltip) { + const tooltip = JSON.parse(JSON.stringify(customAttr.tooltip)) + const reg = new RegExp('\n', 'g') + const text = tooltip.formatter.replace(reg, '
') + tooltip.formatter = function (params) { + const a = params.seriesName + const b = params.name + const c = params.value ? params.value[2] : '' + return text.replace(new RegExp('{a}', 'g'), a).replace(new RegExp('{b}', 'g'), b).replace(new RegExp('{c}', 'g'), c) + } + chart_option.tooltip = tooltip + } + } + // 处理data + if (chart.data) { + chart_option.title.text = chart.title + if (chart.data.series && chart.data.series.length > 0) { + chart_option.series[0].name = chart.data.series[0].name + // label + if (customAttr.label) { + const text = customAttr.label.formatter + chart_option.series[0].label = customAttr.label + chart_option.series[0].label.formatter = function (params) { + const a = params.seriesName + const b = params.name + const c = params.value ? params.value[2] : '' + return text.replace(new RegExp('{a}', 'g'), a).replace(new RegExp('{b}', 'g'), b).replace(new RegExp('{c}', 'g'), c) + } + chart_option.series[0].labelLine = customAttr.label.labelLine + } + + // visualMap + const valueArr = chart.data.series[0].data + if (valueArr && valueArr.length > 0) { + const values = [] + valueArr.forEach(function (ele) { + values.push(ele.value) + }) + chart_option.visualMap.min = Math.min(...values) + chart_option.visualMap.max = Math.max(...values) + if (chart_option.visualMap.min === chart_option.visualMap.max) { + chart_option.visualMap.min = 0 + } + } else { + chart_option.visualMap.min = 0 + chart_option.visualMap.max = 0 + } + if (chart_option.visualMap.min === 0 && chart_option.visualMap.max === 0) { + chart_option.visualMap.max = 100 + } + // color + if (customAttr.color && customAttr.color.colors) { + chart_option.visualMap.inRange.color = customAttr.color.colors + chart_option.visualMap.inRange.colorAlpha = customAttr.color.alpha / 100 + } + // chart_option.visualMap = null + + const convert = convertData(mapData, chart) + chart_option.series[0].data = convert.value + chart_option.series[0].symbolSize = val => val[2] * convert.rate + + } + } + // console.log(chart_option); + componentStyle(chart_option, chart) + return chart_option +} + +export function componentStyle(chart_option, chart) { + const padding = '8px' + if (chart.customStyle) { + const customStyle = JSON.parse(chart.customStyle) + if (customStyle.text) { + chart_option.title.show = customStyle.text.show + // 水平方向 + if (customStyle.text.hPosition === 'left') { + chart_option.title.left = padding + } else if (customStyle.text.hPosition === 'right') { + chart_option.title.right = padding + } else { + chart_option.title.left = customStyle.text.hPosition + } + // 垂直方向 + if (customStyle.text.vPosition === 'top') { + chart_option.title.top = padding + } else if (customStyle.text.vPosition === 'bottom') { + chart_option.title.bottom = padding + } else { + chart_option.title.top = customStyle.text.vPosition + } + const style = chart_option.title.textStyle ? chart_option.title.textStyle : {} + style.fontSize = customStyle.text.fontSize + style.color = customStyle.text.color + customStyle.text.isItalic ? style.fontStyle = 'italic' : style.fontStyle = 'normal' + customStyle.text.isBolder ? style.fontWeight = 'bold' : style.fontWeight = 'normal' + chart_option.title.textStyle = style + } + if (customStyle.legend && chart_option.legend) { + chart_option.legend.show = customStyle.legend.show + // 水平方向 + if (customStyle.legend.hPosition === 'left') { + chart_option.legend.left = padding + } else if (customStyle.legend.hPosition === 'right') { + chart_option.legend.right = padding + } else { + chart_option.legend.left = customStyle.legend.hPosition + } + // 垂直方向 + if (customStyle.legend.vPosition === 'top') { + chart_option.legend.top = padding + } else if (customStyle.legend.vPosition === 'bottom') { + chart_option.legend.bottom = padding + } else { + chart_option.legend.top = customStyle.legend.vPosition + } + chart_option.legend.orient = customStyle.legend.orient + chart_option.legend.icon = customStyle.legend.icon + chart_option.legend.textStyle = customStyle.legend.textStyle + + } + + if (customStyle.background) { + chart_option.backgroundColor = hexColorToRGBA(customStyle.background.color, customStyle.background.alpha) + } + } +} + +export function hexColorToRGBA(hex, alpha) { + const rgb = [] // 定义rgb数组 + if (/^\#[0-9A-F]{3}$/i.test(hex)) { // 判断传入是否为#三位十六进制数 + let sixHex = '#' + hex.replace(/[0-9A-F]/ig, function (kw) { + sixHex += kw + kw // 把三位16进制数转化为六位 + }) + hex = sixHex // 保存回hex + } + if (/^#[0-9A-F]{6}$/i.test(hex)) { // 判断传入是否为#六位十六进制数 + hex.replace(/[0-9A-F]{2}/ig, function (kw) { + // eslint-disable-next-line no-eval + rgb.push(eval('0x' + kw)) // 十六进制转化为十进制并存如数组 + }) + return `rgba(${rgb.join(',')},${alpha / 100})` // 输出RGB格式颜色 + } else { + return 'rgb(0,0,0)' + } +} + + +export const DEFAULT_YAXIS_EXT_STYLE = { + show: true, + position: 'right', + name: '', + nameTextStyle: { + color: '#333333', + fontSize: 12 + }, + axisLabel: { + show: true, + color: '#333333', + fontSize: '12', + rotate: 0, + formatter: '{value}' + }, + splitLine: { + show: true, + lineStyle: { + color: '#cccccc', + width: 1, + style: 'solid' + } + }, + axisValue: { + auto: true, + min: null, + max: null, + split: null, + splitCount: null + } +} + +export const getDefaultTemplate = (chart, type, feed, showKey) => { + if (!chart || !chart.viewFields || !type) return null; + let viewFields = [] + if (chart.viewFields instanceof Array) { + viewFields = JSON.parse(JSON.stringify(chart.viewFields)) + } else { + viewFields = JSON.parse(chart.viewFields) + } + const separator = feed ? '\n' : ' ' + return viewFields.filter(field => field.busiType && field.busiType === type).map(field => { + const fieldName = field.name + let template = "${" + field.name + "}" + if (showKey) { + template = fieldName + ":${" + field.name + "}" + } + return template + }).join(separator) +} + +export function uuid() { + return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1); +} + +export const reverseColor = colorValue => { + colorValue = '0x' + colorValue.replace(/#/g, '') + const str = '000000' + (0xFFFFFF - colorValue).toString(16) + return '#' + str.substring(str.length - 6, str.length) +} + + +export const DEFAULT_XAXIS_STYLE = { + show: true, + position: 'bottom', + name: '', + nameTextStyle: { + color: '#333333', + fontSize: 12 + }, + axisLabel: { + show: true, + color: '#333333', + fontSize: '12', + rotate: 0, + formatter: '{value}' + }, + axisLine: { + show: true, + lineStyle: { + color: '#cccccc', + width: 1, + style: 'solid' + } + }, + splitLine: { + show: false, + lineStyle: { + color: '#cccccc', + width: 1, + style: 'solid' + } + }, + axisValue: { + auto: true, + min: null, + max: null, + split: null, + splitCount: null + }, + axisLabelFormatter: { + type: 'auto', // auto,value,percent + unit: 1, // 换算单位 + suffix: '', // 单位后缀 + decimalCount: 2, // 小数位数 + thousandSeparator: true// 千分符 + } +} + +export const DEFAULT_YAXIS_STYLE = { + show: true, + position: 'left', + name: '', + nameTextStyle: { + color: '#333333', + fontSize: 12 + }, + axisLabel: { + show: true, + color: '#333333', + fontSize: '12', + rotate: 0, + formatter: '{value}' + }, + axisLine: { + show: false, + lineStyle: { + color: '#cccccc', + width: 1, + style: 'solid' + } + }, + splitLine: { + show: true, + lineStyle: { + color: '#cccccc', + width: 1, + style: 'solid' + } + }, + axisValue: { + auto: true, + min: null, + max: null, + split: null, + splitCount: null + }, + axisLabelFormatter: { + type: 'auto', // auto,value,percent + unit: 1, // 换算单位 + suffix: '', // 单位后缀 + decimalCount: 2, // 小数位数 + thousandSeparator: true// 千分符 + } +} + +export function transAxisPosition(chart, axis) { + if (chart.type.includes('horizontal')) { + switch (axis.position) { + case 'top': + return 'left' + case 'bottom': + return 'right' + case 'left': + return 'bottom' + case 'right': + return 'top' + default: + return axis.position + } + } else { + return axis.position + } +} + +export function getLineDash(type) { + switch (type) { + case 'solid': + return [0, 0] + case 'dashed': + return [10, 8] + case 'dotted': + return [2, 2] + default: + return [0, 0] + } +} diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/utils/validate.js b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/utils/validate.js new file mode 100644 index 0000000000..3e8ffa9448 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/utils/validate.js @@ -0,0 +1,15 @@ +/** + * Created by PanJiaChen on 16/11/18. + */ + +/** + * @param {string} path + * @returns {Boolean} + */ +export function isExternal(path) { + return /^(https?:|mailto:|tel:)/.test(path) || /^(http?:|mailto:|tel:)/.test(path) || path.startsWith('/api/pluginCommon/staticInfo') +} + + + + diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/views/PluginDemo.vue b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/views/PluginDemo.vue new file mode 100644 index 0000000000..1164dfab1d --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/views/PluginDemo.vue @@ -0,0 +1,55 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/views/antv/chartmix/data.vue b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/views/antv/chartmix/data.vue new file mode 100644 index 0000000000..678959c715 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/views/antv/chartmix/data.vue @@ -0,0 +1,663 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/views/antv/chartmix/index.vue b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/views/antv/chartmix/index.vue new file mode 100644 index 0000000000..88e62a3d6e --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/views/antv/chartmix/index.vue @@ -0,0 +1,717 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/views/antv/chartmix/style.vue b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/views/antv/chartmix/style.vue new file mode 100644 index 0000000000..34a13fcf87 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/views/antv/chartmix/style.vue @@ -0,0 +1,175 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/views/antv/chartmix/type.vue b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/views/antv/chartmix/type.vue new file mode 100644 index 0000000000..db7ca40b93 --- /dev/null +++ b/extensions/dataease-extensions-view/view-chartmix/view-chartmix-frontend/src/views/antv/chartmix/type.vue @@ -0,0 +1,63 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-racebar/build.sh b/extensions/dataease-extensions-view/view-racebar/build.sh new file mode 100644 index 0000000000..d9fca1ae58 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/build.sh @@ -0,0 +1,8 @@ +#!/bin/sh +mvn clean package + +cp view-racebar-backend/target/view-racebar-backend-1.18.10.jar . + +zip -r racebar.zip ./view-racebar-backend-1.18.10.jar ./plugin.json + +rm -f ./view-racebar-backend-1.18.10.jar diff --git a/extensions/dataease-extensions-view/view-racebar/plugin.json b/extensions/dataease-extensions-view/view-racebar/plugin.json new file mode 100644 index 0000000000..6eacf6d870 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/plugin.json @@ -0,0 +1,13 @@ +{ + "name": "ECharts 动态排序图", + "free": 0, + "store": "thirdpart", + "cost": 0, + "category": "view", + "descript": "ECharts 动态排序图插件", + "version": "1.18.10", + "creator": "DATAEASE", + "moduleName": "view-racebar-backend", + "require": "1.18.10", + "dsType": "" +} \ No newline at end of file diff --git a/extensions/dataease-extensions-view/view-racebar/pom.xml b/extensions/dataease-extensions-view/view-racebar/pom.xml new file mode 100644 index 0000000000..30fba28ad2 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/pom.xml @@ -0,0 +1,20 @@ + + + + dataease-extensions-view + io.dataease + ${dataease.version} + + 4.0.0 + + view-racebar + pom + + view-racebar-frontend + view-racebar-backend + + + + diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-backend/pom.xml b/extensions/dataease-extensions-view/view-racebar/view-racebar-backend/pom.xml new file mode 100644 index 0000000000..027a76cf9e --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-backend/pom.xml @@ -0,0 +1,109 @@ + + + + view-racebar + io.dataease + ${dataease.version} + + 4.0.0 + + view-racebar-backend + + + + io.dataease + dataease-plugin-view + + + + + + + src/main/java + + **/*.properties + **/*.xml + + false + + + src/main/resources + + **/* + + false + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 11 + 11 + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.4 + + + **/server/** + **/*.properties + **/Application* + + + + + + maven-clean-plugin + + + + src/main/resources/static + + ** + + false + + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + main-class-placement + generate-resources + + + + + + + + + + + + run + + + + + + + + + + + diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-backend/src/main/java/io/dataease/plugins/view/official/handler/DefaultViewStatHandler.java b/extensions/dataease-extensions-view/view-racebar/view-racebar-backend/src/main/java/io/dataease/plugins/view/official/handler/DefaultViewStatHandler.java new file mode 100644 index 0000000000..176a1e7ecd --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-backend/src/main/java/io/dataease/plugins/view/official/handler/DefaultViewStatHandler.java @@ -0,0 +1,107 @@ +package io.dataease.plugins.view.official.handler; + +import com.google.gson.Gson; +import io.dataease.plugins.common.constants.datasource.SQLConstants; +import io.dataease.plugins.common.request.permission.DataSetRowPermissionsTreeDTO; +import io.dataease.plugins.common.util.ConstantsUtil; +import io.dataease.plugins.view.entity.*; +import io.dataease.plugins.view.handler.PluginViewStatHandler; +import io.dataease.plugins.view.service.ViewPluginBaseService; +import io.dataease.plugins.view.service.ViewPluginService; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.stringtemplate.v4.ST; +import org.stringtemplate.v4.STGroup; +import org.stringtemplate.v4.STGroupFile; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class DefaultViewStatHandler implements PluginViewStatHandler { + + @Override + public String build(PluginViewParam pluginViewParam, ViewPluginService viewPluginService) { + + + ViewPluginBaseService baseService = viewPluginService.getBaseService(); + PluginViewSet pluginViewSet = pluginViewParam.getPluginViewSet(); + List rowPermissionsTree = pluginViewParam.getRowPermissionsTree(); + String dsType = pluginViewSet.getDsType(); + PluginViewSQL tableObj = baseService.getTableObj(pluginViewSet); + + Map> fieldSQLMap = new HashMap<>(); + + + for (int i = 0; i < pluginViewParam.getPluginViewFields().size(); i++) { + PluginViewField pluginViewField = pluginViewParam.getPluginViewFields().get(i); + if (StringUtils.equals(pluginViewField.getTypeField(), "xAxisExt")) { + pluginViewField.setTypeField("xAxis"); + pluginViewField.setExtField(1); + } + String typeKey = pluginViewField.getTypeField(); + PluginSingleField pluginSingleField = baseService.buildField(dsType, pluginViewField, tableObj, i); + List lists = fieldSQLMap.getOrDefault(typeKey, new ArrayList<>()); + lists.add(pluginSingleField); + fieldSQLMap.put(typeKey, lists); + } + + + List xFields = fieldSQLMap.getOrDefault("xAxis", new ArrayList<>()).stream().filter(singleField -> ObjectUtils.isNotEmpty(singleField.getField())).map(PluginSingleField::getField).collect(Collectors.toList()); + + // List xWheres = fieldSQLMap.get("xAxis").stream().map(singleField -> singleField.getWhere()).collect(Collectors.toList()); + + List yFields = fieldSQLMap.getOrDefault("yAxis", new ArrayList<>()).stream().filter(singleField -> ObjectUtils.isNotEmpty(singleField.getField())).map(PluginSingleField::getField).collect(Collectors.toList()); + List yWheres = fieldSQLMap.getOrDefault("yAxis", new ArrayList<>()).stream().filter(singleField -> ObjectUtils.isNotEmpty(singleField.getWhere())).map(PluginSingleField::getWhere).collect(Collectors.toList()); + + /*List yExtFields = fieldSQLMap.getOrDefault("yAxisExt", new ArrayList<>()).stream().filter(singleField -> ObjectUtils.isNotEmpty(singleField.getField())).map(PluginSingleField::getField).collect(Collectors.toList()); + List yExtOrders = fieldSQLMap.getOrDefault("yAxisExt", new ArrayList<>()).stream().filter(singleField -> ObjectUtils.isNotEmpty(singleField.getSort())).map(PluginSingleField::getSort).collect(Collectors.toList()); + List yExtWheres = fieldSQLMap.getOrDefault("yAxisExt", new ArrayList<>()).stream().filter(singleField -> ObjectUtils.isNotEmpty(singleField.getWhere())).map(PluginSingleField::getWhere).collect(Collectors.toList()); + + yFields.addAll(yExtFields); + yOrders.addAll(yExtOrders); + yWheres.addAll(yExtWheres);*/ + + // 处理视图中字段过滤 + String customWheres = baseService.customWhere(dsType, pluginViewParam.getPluginChartFieldCustomFilters(), tableObj); + // 处理仪表板字段过滤 + String panelWheres = baseService.panelWhere(dsType, pluginViewParam.getPluginChartExtFilters(), tableObj); + // 构建sql所有参数 + + String permissionWhere = baseService.permissionWhere(dsType, rowPermissionsTree, tableObj); + List wheres = new ArrayList<>(); + if (customWheres != null) wheres.add(customWheres); + if (panelWheres != null) wheres.add(panelWheres); + if (permissionWhere != null) wheres.add(permissionWhere); + List groups = new ArrayList<>(); + groups.addAll(xFields); + + // 外层再次套sql + List aggWheres = new ArrayList<>(); + aggWheres.addAll(yWheres.stream().filter(ObjectUtils::isNotEmpty).collect(Collectors.toList())); + + STGroup stg = new STGroupFile(SQLConstants.SQL_TEMPLATE); + ST st_sql = stg.getInstanceOf("querySql"); + if (CollectionUtils.isNotEmpty(groups)) st_sql.add("groups", groups); + if (CollectionUtils.isNotEmpty(yFields)) st_sql.add("aggregators", yFields); + if (CollectionUtils.isNotEmpty(wheres)) st_sql.add("filters", wheres); + if (ObjectUtils.isNotEmpty(tableObj)) st_sql.add("table", tableObj); + String sql = st_sql.render(); + + String brackets = ConstantsUtil.constantsValue(dsType, "BRACKETS"); + String table_alias_prefix = ConstantsUtil.constantsValue(dsType, "TABLE_ALIAS_PREFIX"); + + ST st = stg.getInstanceOf("querySql"); + PluginViewSQL tableSQL = PluginViewSQL.builder() + .tableName(String.format(brackets, sql)) + .tableAlias(String.format(table_alias_prefix, 1)) + .build(); + if (CollectionUtils.isNotEmpty(aggWheres)) st.add("filters", aggWheres); + if (ObjectUtils.isNotEmpty(tableSQL)) st.add("table", tableSQL); + return baseService.sqlLimit(dsType, st.render(), pluginViewParam.getPluginViewLimit()); + } + +} diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-backend/src/main/java/io/dataease/plugins/view/official/impl/RaceBarService.java b/extensions/dataease-extensions-view/view-racebar/view-racebar-backend/src/main/java/io/dataease/plugins/view/official/impl/RaceBarService.java new file mode 100644 index 0000000000..d5819a48b9 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-backend/src/main/java/io/dataease/plugins/view/official/impl/RaceBarService.java @@ -0,0 +1,186 @@ +package io.dataease.plugins.view.official.impl; + +import com.google.gson.Gson; +import io.dataease.plugins.common.dto.StaticResource; +import io.dataease.plugins.view.entity.PluginViewField; +import io.dataease.plugins.view.entity.PluginViewParam; +import io.dataease.plugins.view.entity.PluginViewType; +import io.dataease.plugins.view.official.handler.DefaultViewStatHandler; +import io.dataease.plugins.view.service.ViewPluginService; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.io.InputStream; +import java.util.*; +import java.util.stream.Collectors; + +@Service +public class RaceBarService extends ViewPluginService { + private static final String VIEW_TYPE_VALUE = "race-bar"; + + /* 下版这些常量移到sdk */ + private static final String TYPE = "-type"; + private static final String DATA = "-data"; + private static final String STYLE = "-style"; + private static final String VIEW = "-view"; + private static final String SUFFIX = "svg"; + /* 下版这些常量移到sdk */ + private static final String VIEW_TYPE = VIEW_TYPE_VALUE + TYPE; + private static final String VIEW_DATA = VIEW_TYPE_VALUE + DATA; + private static final String VIEW_STYLE = VIEW_TYPE_VALUE + STYLE; + private static final String VIEW_VIEW = VIEW_TYPE_VALUE + VIEW; + + private static final String[] VIEW_STYLE_PROPERTIES = { + "color-selector", + "label-selector", + "tooltip-selector", + "title-selector", + }; + + private static final Map VIEW_STYLE_PROPERTY_INNER = new HashMap<>(); + + static { + VIEW_STYLE_PROPERTY_INNER.put("color-selector", new String[]{"value", "alpha"}); + VIEW_STYLE_PROPERTY_INNER.put("label-selector", new String[]{"show", "fontSize", "color", "position", "formatter"}); + VIEW_STYLE_PROPERTY_INNER.put("tooltip-selector", new String[]{"show", "textStyle", "formatter"}); + VIEW_STYLE_PROPERTY_INNER.put("title-selector", new String[]{"show", "title", "fontSize", "color", "hPosition", "vPosition", "isItalic", "isBolder"}); + } + + @Override + public PluginViewType viewType() { + PluginViewType pluginViewType = new PluginViewType(); + pluginViewType.setRender("echarts"); + pluginViewType.setCategory("chart.chart_type_compare"); + pluginViewType.setValue(VIEW_TYPE_VALUE); + pluginViewType.setProperties(VIEW_STYLE_PROPERTIES); + pluginViewType.setPropertyInner(VIEW_STYLE_PROPERTY_INNER); + return pluginViewType; + } + + @Override + public Object format(Object o) { + return null; + } + + @Override + public List components() { + List results = new ArrayList<>(); + results.add(VIEW_VIEW); + results.add(VIEW_DATA); + results.add(VIEW_TYPE); + results.add(VIEW_STYLE); + return results; + } + + @Override + public List staticResources() { + List results = new ArrayList<>(); + StaticResource staticResource = new StaticResource(); + staticResource.setName(VIEW_TYPE_VALUE); + staticResource.setSuffix(SUFFIX); + results.add(staticResource); + results.add(pluginSvg()); + return results; + } + + private StaticResource pluginSvg() { + StaticResource staticResource = new StaticResource(); + staticResource.setName("view-racebar-backend"); + staticResource.setSuffix("svg"); + return staticResource; + } + + + @Override + protected InputStream readContent(String s) { + InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("static/" + s); + return resourceAsStream; + } + + @Override + public String generateSQL(PluginViewParam param) { + + List xAxis = param.getFieldsByType("xAxis"); + List yAxis = param.getFieldsByType("yAxis"); + + if (CollectionUtils.isEmpty(xAxis) || CollectionUtils.isEmpty(yAxis) || xAxis.size() < 2) { + return null; + } + String sql = new DefaultViewStatHandler().build(param, this); + return sql; + + } + + @Override + public Map formatResult(PluginViewParam pluginViewParam, List data, Boolean isDrill) { + return format(pluginViewParam, data, isDrill); + } + + + public Map format(PluginViewParam pluginViewParam, List data, boolean isDrill) { + + Map map = new HashMap<>(); + + map.put("data", data); + + Map encode = new HashMap<>(); + + String type = null; + + for (int i = 0; i < pluginViewParam.getPluginViewFields().size(); i++) { + PluginViewField p = pluginViewParam.getPluginViewFields().get(i); + if (StringUtils.equals(p.getTypeField(), "yAxis")) { + encode.put("x", i); + type = p.getType(); + } else if (StringUtils.equals(p.getTypeField(), "xAxis")) { + if (p.getExtField() == 1) { + map.put("extIndex", i); + } else { + encode.put("y", i); + } + } + } + map.put("encode", encode); + + Set xs = new HashSet<>(); + data.forEach(ss -> { + xs.add(ss[encode.get("y")]); + }); + + Map> groupData = data.stream().collect(Collectors.toMap( + k -> k[(Integer) map.get("extIndex")], + v -> { + List list = new ArrayList<>(); + list.add(v); + return list; + }, + (oldList, newList) -> { + oldList.addAll(newList); + return oldList; + }) + ); + + for (String key : groupData.keySet()) { + String finalType = type; + groupData.put(key, groupData.get(key).stream().sorted((o1, o2) -> { + if (StringUtils.equals(finalType, "LONG")) { + return Long.valueOf(o2[encode.get("x")]).compareTo(Long.valueOf(o1[encode.get("x")])); + } else if (StringUtils.equals(finalType, "DOUBLE")) { + return Double.valueOf(o2[encode.get("x")]).compareTo(Double.valueOf(o1[encode.get("x")])); + } + return o2[encode.get("x")].compareTo(o1[encode.get("x")]); + }).collect(Collectors.toList())); + + } + + map.put("groupData", groupData); + + map.put("extXs", new ArrayList<>(groupData.keySet()).stream().sorted().collect(Collectors.toList())); + + map.put("xs", new ArrayList<>(xs).stream().sorted().collect(Collectors.toList())); + + return map; + } + +} diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/.babelrc b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/.babelrc new file mode 100644 index 0000000000..3a280ba34b --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/.babelrc @@ -0,0 +1,12 @@ +{ + "presets": [ + ["env", { + "modules": false, + "targets": { + "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] + } + }], + "stage-2" + ], + "plugins": ["transform-vue-jsx", "transform-runtime"] +} diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/.editorconfig b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/.editorconfig new file mode 100644 index 0000000000..9d08a1a828 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/.gitignore b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/.gitignore new file mode 100644 index 0000000000..541a820f6c --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/.gitignore @@ -0,0 +1,14 @@ +.DS_Store +node_modules/ +/dist/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/.postcssrc.js b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/.postcssrc.js new file mode 100644 index 0000000000..eee3e92d7f --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/.postcssrc.js @@ -0,0 +1,10 @@ +// https://github.com/michael-ciniawsky/postcss-load-config + +module.exports = { + "plugins": { + "postcss-import": {}, + "postcss-url": {}, + // to edit target browsers: use "browserslist" field in package.json + "autoprefixer": {} + } +} diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/README.md b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/README.md new file mode 100644 index 0000000000..c1e4be95e0 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/README.md @@ -0,0 +1,21 @@ +# deplugin-view-frontend + +> A Vue.js project + +## Build Setup + +``` bash +# install dependencies +npm install + +# serve with hot reload at localhost:8080 +npm run dev + +# build for production with minification +npm run build + +# build for production and view the bundle analyzer report +npm run build --report +``` + +For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader). diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/build/build-async-plugins.js b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/build/build-async-plugins.js new file mode 100644 index 0000000000..2303854531 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/build/build-async-plugins.js @@ -0,0 +1,35 @@ +'use strict' +require('./check-versions')() + +process.env.NODE_ENV = 'production' + +const ora = require('ora') +const chalk = require('chalk') +const webpack = require('webpack') +const webpackConfig = require('./webpack.async-plugins') + +const spinner = ora('building for sync-plugins...') +spinner.start() + +webpack(webpackConfig, function (err, stats) { + spinner.stop() + if (err) throw err + process.stdout.write(stats.toString({ + colors: true, + modules: false, + children: false, + chunks: false, + chunkModules: false + }) + '\n\n') + + if (stats.hasErrors()) { + console.log(chalk.red(' Build failed with errors.\n')) + process.exit(1) + } + + console.log(chalk.cyan(' Build complete.\n')) + console.log(chalk.yellow( + ' Tip: built files are meant to be served over an HTTP server.\n' + + ' Opening index.html over file:// won\'t work.\n' + )) +}) diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/build/build.js b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/build/build.js new file mode 100644 index 0000000000..8f2ad8ad49 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/build/build.js @@ -0,0 +1,41 @@ +'use strict' +require('./check-versions')() + +process.env.NODE_ENV = 'production' + +const ora = require('ora') +const rm = require('rimraf') +const path = require('path') +const chalk = require('chalk') +const webpack = require('webpack') +const config = require('../config') +const webpackConfig = require('./webpack.prod.conf') + +const spinner = ora('building for production...') +spinner.start() + +rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { + if (err) throw err + webpack(webpackConfig, (err, stats) => { + spinner.stop() + if (err) throw err + process.stdout.write(stats.toString({ + colors: true, + modules: false, + children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build. + chunks: false, + chunkModules: false + }) + '\n\n') + + if (stats.hasErrors()) { + console.log(chalk.red(' Build failed with errors.\n')) + process.exit(1) + } + + console.log(chalk.cyan(' Build complete.\n')) + console.log(chalk.yellow( + ' Tip: built files are meant to be served over an HTTP server.\n' + + ' Opening index.html over file:// won\'t work.\n' + )) + }) +}) diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/build/check-versions.js b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/build/check-versions.js new file mode 100644 index 0000000000..3ef972a08d --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/build/check-versions.js @@ -0,0 +1,54 @@ +'use strict' +const chalk = require('chalk') +const semver = require('semver') +const packageConfig = require('../package.json') +const shell = require('shelljs') + +function exec (cmd) { + return require('child_process').execSync(cmd).toString().trim() +} + +const versionRequirements = [ + { + name: 'node', + currentVersion: semver.clean(process.version), + versionRequirement: packageConfig.engines.node + } +] + +if (shell.which('npm')) { + versionRequirements.push({ + name: 'npm', + currentVersion: exec('npm --version'), + versionRequirement: packageConfig.engines.npm + }) +} + +module.exports = function () { + const warnings = [] + + for (let i = 0; i < versionRequirements.length; i++) { + const mod = versionRequirements[i] + + if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { + warnings.push(mod.name + ': ' + + chalk.red(mod.currentVersion) + ' should be ' + + chalk.green(mod.versionRequirement) + ) + } + } + + if (warnings.length) { + console.log('') + console.log(chalk.yellow('To use this template, you must update following to modules:')) + console.log() + + for (let i = 0; i < warnings.length; i++) { + const warning = warnings[i] + console.log(' ' + warning) + } + + console.log() + process.exit(1) + } +} diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/build/logo.png b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/build/logo.png new file mode 100644 index 0000000000..f3d2503fc2 Binary files /dev/null and b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/build/logo.png differ diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/build/utils.js b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/build/utils.js new file mode 100644 index 0000000000..e534fb0fd6 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/build/utils.js @@ -0,0 +1,101 @@ +'use strict' +const path = require('path') +const config = require('../config') +const ExtractTextPlugin = require('extract-text-webpack-plugin') +const packageConfig = require('../package.json') + +exports.assetsPath = function (_path) { + const assetsSubDirectory = process.env.NODE_ENV === 'production' + ? config.build.assetsSubDirectory + : config.dev.assetsSubDirectory + + return path.posix.join(assetsSubDirectory, _path) +} + +exports.cssLoaders = function (options) { + options = options || {} + + const cssLoader = { + loader: 'css-loader', + options: { + sourceMap: options.sourceMap + } + } + + const postcssLoader = { + loader: 'postcss-loader', + options: { + sourceMap: options.sourceMap + } + } + + // generate loader string to be used with extract text plugin + function generateLoaders (loader, loaderOptions) { + const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader] + + if (loader) { + loaders.push({ + loader: loader + '-loader', + options: Object.assign({}, loaderOptions, { + sourceMap: options.sourceMap + }) + }) + } + + // Extract CSS when that option is specified + // (which is the case during production build) + if (options.extract) { + return ExtractTextPlugin.extract({ + use: loaders, + fallback: 'vue-style-loader' + }) + } else { + return ['vue-style-loader'].concat(loaders) + } + } + + // https://vue-loader.vuejs.org/en/configurations/extract-css.html + return { + css: generateLoaders(), + postcss: generateLoaders(), + less: generateLoaders('less'), + sass: generateLoaders('sass', { indentedSyntax: true }), + scss: generateLoaders('sass'), + stylus: generateLoaders('stylus'), + styl: generateLoaders('stylus') + } +} + +// Generate loaders for standalone style files (outside of .vue) +exports.styleLoaders = function (options) { + const output = [] + const loaders = exports.cssLoaders(options) + + for (const extension in loaders) { + const loader = loaders[extension] + output.push({ + test: new RegExp('\\.' + extension + '$'), + use: loader + }) + } + + return output +} + +exports.createNotifierCallback = () => { + const notifier = require('node-notifier') + + return (severity, errors) => { + if (severity !== 'error') return + + const error = errors[0] + const filename = error.file && error.file.split('!').pop() + + notifier.notify({ + title: packageConfig.name, + message: severity + ': ' + error.name, + subtitle: filename || '', + icon: path.join(__dirname, 'logo.png') + }) + } +} diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/build/vue-loader.conf.js b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/build/vue-loader.conf.js new file mode 100644 index 0000000000..33ed58bc0a --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/build/vue-loader.conf.js @@ -0,0 +1,22 @@ +'use strict' +const utils = require('./utils') +const config = require('../config') +const isProduction = process.env.NODE_ENV === 'production' +const sourceMapEnabled = isProduction + ? config.build.productionSourceMap + : config.dev.cssSourceMap + +module.exports = { + loaders: utils.cssLoaders({ + sourceMap: sourceMapEnabled, + extract: isProduction + }), + cssSourceMap: sourceMapEnabled, + cacheBusting: config.dev.cacheBusting, + transformToRequire: { + video: ['src', 'poster'], + source: 'src', + img: 'src', + image: 'xlink:href' + } +} diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/build/webpack.async-plugins.js b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/build/webpack.async-plugins.js new file mode 100644 index 0000000000..71b6c82d3d --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/build/webpack.async-plugins.js @@ -0,0 +1,97 @@ +const webpack = require('webpack') +const path = require('path') +const utils = require('./utils') +const CopyPlugin = require("copy-webpack-plugin"); +const VueLoaderPlugin = require('vue-loader/lib/plugin'); +function resolve (dir) { + return path.join(__dirname, '..', dir) +} + +module.exports = { + mode: 'development', + entry: { + + // PluginDemo: resolve('/src/views/PluginDemo.vue') + 'race-bar-view': resolve('/src/views/antv/racebar/index.vue'), + 'race-bar-data': resolve('/src/views/antv/racebar/data.vue'), + 'race-bar-type': resolve('/src/views/antv/racebar/type.vue'), + 'race-bar-style': resolve('/src/views/antv/racebar/style.vue') + + }, + output: { + path: resolve('/static/'), + filename: '[name].js' + }, + resolve: { + extensions: ['.js', '.vue', '.json'], + alias: { + 'vue$': 'vue/dist/vue.esm.js', + '@': resolve('src') + } + }, + externals: { + vue: 'vue' + }, + module: { + rules: [ + { + test: /\.vue$/, + loader: 'vue-loader', + options: { + transformAssetUrls: { + video: 'src', + source: 'src', + img: 'src', + image: 'xlink:href' + } + } + }, + { + test: /.(sa|sc|c)ss$/, + use: [ + {loader: 'vue-style-loader'}, + 'css-loader', + 'sass-loader' + ] + }, + { + test: /\.js$/, + loader: 'babel-loader', + include: [resolve('src'), resolve('test')] + }, + { + test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('img/[name].[hash:7].[ext]') + } + }, + { + test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('media/[name].[hash:7].[ext]') + } + }, + { + test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('fonts/[name].[hash:7].[ext]') + } + } + ] + }, + plugins: [ + new VueLoaderPlugin(), + new webpack.DefinePlugin({ + 'process.env.NODE_ENV': '"production"' + }), + new CopyPlugin([ + {from: 'src/icons/svg/'} + ]), + ] +} diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/build/webpack.base.conf.js b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/build/webpack.base.conf.js new file mode 100644 index 0000000000..6ccc02dab4 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/build/webpack.base.conf.js @@ -0,0 +1,91 @@ +'use strict' +const path = require('path') +const utils = require('./utils') +const config = require('../config') +const vueLoaderConfig = require('./vue-loader.conf') + +function resolve (dir) { + return path.join(__dirname, '..', dir) +} + + + +module.exports = { + context: path.resolve(__dirname, '../'), + entry: { + app: './src/main.js' + }, + output: { + path: config.build.assetsRoot, + filename: '[name].js', + publicPath: process.env.NODE_ENV === 'production' + ? config.build.assetsPublicPath + : config.dev.assetsPublicPath + }, + resolve: { + extensions: ['.js', '.vue', '.json'], + alias: { + 'vue$': 'vue/dist/vue.esm.js', + '@': resolve('src'), + } + }, + module: { + rules: [ + { + test: /\.vue$/, + loader: 'vue-loader', + options: vueLoaderConfig + }, + { + test: /\.js$/, + loader: 'babel-loader', + include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')] + }, + { + test: /\.svg$/, + loader: 'svg-sprite-loader', + include: [resolve('src/icons')], + options: { + symbolId: 'icon-[name]' + } + }, + { + test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, + loader: 'url-loader', + exclude: [resolve('src/icons')], + options: { + limit: 10000, + name: utils.assetsPath('img/[name].[hash:7].[ext]') + } + }, + { + test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('media/[name].[hash:7].[ext]') + } + }, + { + test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('fonts/[name].[hash:7].[ext]') + } + } + ] + }, + node: { + // prevent webpack from injecting useless setImmediate polyfill because Vue + // source contains it (although only uses it if it's native). + setImmediate: false, + // prevent webpack from injecting mocks to Node native modules + // that does not make sense for the client + dgram: 'empty', + fs: 'empty', + net: 'empty', + tls: 'empty', + child_process: 'empty' + } +} diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/build/webpack.dev.conf.js b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/build/webpack.dev.conf.js new file mode 100755 index 0000000000..070ae221f3 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/build/webpack.dev.conf.js @@ -0,0 +1,95 @@ +'use strict' +const utils = require('./utils') +const webpack = require('webpack') +const config = require('../config') +const merge = require('webpack-merge') +const path = require('path') +const baseWebpackConfig = require('./webpack.base.conf') +const CopyWebpackPlugin = require('copy-webpack-plugin') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') +const portfinder = require('portfinder') + +const HOST = process.env.HOST +const PORT = process.env.PORT && Number(process.env.PORT) + +const devWebpackConfig = merge(baseWebpackConfig, { + module: { + rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) + }, + // cheap-module-eval-source-map is faster for development + devtool: config.dev.devtool, + + // these devServer options should be customized in /config/index.js + devServer: { + clientLogLevel: 'warning', + historyApiFallback: { + rewrites: [ + { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') }, + ], + }, + hot: true, + contentBase: false, // since we use CopyWebpackPlugin. + compress: true, + host: HOST || config.dev.host, + port: PORT || config.dev.port, + open: config.dev.autoOpenBrowser, + overlay: config.dev.errorOverlay + ? { warnings: false, errors: true } + : false, + publicPath: config.dev.assetsPublicPath, + proxy: config.dev.proxyTable, + quiet: true, // necessary for FriendlyErrorsPlugin + watchOptions: { + poll: config.dev.poll, + } + }, + plugins: [ + new webpack.DefinePlugin({ + 'process.env': require('../config/dev.env') + }), + new webpack.HotModuleReplacementPlugin(), + new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update. + new webpack.NoEmitOnErrorsPlugin(), + // https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: 'index.html', + template: 'index.html', + inject: true + }), + // copy custom static assets + new CopyWebpackPlugin([ + { + from: path.resolve(__dirname, '../static'), + to: config.dev.assetsSubDirectory, + ignore: ['.*'] + } + ]) + ] +}) + +module.exports = new Promise((resolve, reject) => { + portfinder.basePort = process.env.PORT || config.dev.port + portfinder.getPort((err, port) => { + if (err) { + reject(err) + } else { + // publish the new Port, necessary for e2e tests + process.env.PORT = port + // add port to devServer config + devWebpackConfig.devServer.port = port + + // Add FriendlyErrorsPlugin + devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({ + compilationSuccessInfo: { + messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`], + }, + onErrors: config.dev.notifyOnErrors + ? utils.createNotifierCallback() + : undefined + })) + + resolve(devWebpackConfig) + } + }) +}) diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/build/webpack.prod.conf.js b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/build/webpack.prod.conf.js new file mode 100644 index 0000000000..1f71ad6454 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/build/webpack.prod.conf.js @@ -0,0 +1,147 @@ +'use strict' +const path = require('path') +const utils = require('./utils') +const webpack = require('webpack') +const config = require('../config') +const merge = require('webpack-merge') +const baseWebpackConfig = require('./webpack.base.conf') +const CopyWebpackPlugin = require('copy-webpack-plugin') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const ExtractTextPlugin = require('extract-text-webpack-plugin') +const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') +const UglifyJsPlugin = require('uglifyjs-webpack-plugin') +const VueLoaderPlugin = require('vue-loader/lib/plugin'); + +const env = require('../config/prod.env') + +const webpackConfig = merge(baseWebpackConfig, { + module: { + rules: utils.styleLoaders({ + sourceMap: config.build.productionSourceMap, + extract: true, + usePostCSS: true + }) + }, + devtool: config.build.productionSourceMap ? config.build.devtool : false, + output: { + path: config.build.assetsRoot, + filename: utils.assetsPath('js/[name].[chunkhash].js'), + chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') + }, + plugins: [ + new VueLoaderPlugin(), + // http://vuejs.github.io/vue-loader/en/workflow/production.html + new webpack.DefinePlugin({ + 'process.env': env + }), + new UglifyJsPlugin({ + uglifyOptions: { + compress: { + warnings: false + } + }, + sourceMap: config.build.productionSourceMap, + parallel: true + }), + // extract css into its own file + new ExtractTextPlugin({ + filename: utils.assetsPath('css/[name].[contenthash].css'), + // Setting the following option to `false` will not extract CSS from codesplit chunks. + // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack. + // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, + // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110 + allChunks: true, + }), + // Compress extracted CSS. We are using this plugin so that possible + // duplicated CSS from different components can be deduped. + new OptimizeCSSPlugin({ + cssProcessorOptions: config.build.productionSourceMap + ? { safe: true, map: { inline: false } } + : { safe: true } + }), + // generate dist index.html with correct asset hash for caching. + // you can customize output by editing /index.html + // see https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: config.build.index, + template: 'index.html', + inject: true, + minify: { + removeComments: true, + collapseWhitespace: true, + removeAttributeQuotes: true + // more options: + // https://github.com/kangax/html-minifier#options-quick-reference + }, + // necessary to consistently work with multiple chunks via CommonsChunkPlugin + chunksSortMode: 'dependency' + }), + // keep module.id stable when vendor modules does not change + new webpack.HashedModuleIdsPlugin(), + // enable scope hoisting + new webpack.optimize.ModuleConcatenationPlugin(), + // split vendor js into its own file + new webpack.optimize.CommonsChunkPlugin({ + name: 'vendor', + minChunks (module) { + // any required modules inside node_modules are extracted to vendor + return ( + module.resource && + /\.js$/.test(module.resource) && + module.resource.indexOf( + path.join(__dirname, '../node_modules') + ) === 0 + ) + } + }), + // extract webpack runtime and module manifest to its own file in order to + // prevent vendor hash from being updated whenever app bundle is updated + new webpack.optimize.CommonsChunkPlugin({ + name: 'manifest', + minChunks: Infinity + }), + // This instance extracts shared chunks from code split chunks and bundles them + // in a separate chunk, similar to the vendor chunk + // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk + new webpack.optimize.CommonsChunkPlugin({ + name: 'app', + async: 'vendor-async', + children: true, + minChunks: 3 + }), + + // copy custom static assets + new CopyWebpackPlugin([ + { + from: path.resolve(__dirname, '../static'), + to: config.build.assetsSubDirectory, + ignore: ['.*'] + } + ]) + ] +}) + +if (config.build.productionGzip) { + const CompressionWebpackPlugin = require('compression-webpack-plugin') + + webpackConfig.plugins.push( + new CompressionWebpackPlugin({ + asset: '[path].gz[query]', + algorithm: 'gzip', + test: new RegExp( + '\\.(' + + config.build.productionGzipExtensions.join('|') + + ')$' + ), + threshold: 10240, + minRatio: 0.8 + }) + ) +} + +if (config.build.bundleAnalyzerReport) { + const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin + webpackConfig.plugins.push(new BundleAnalyzerPlugin()) +} + +module.exports = webpackConfig diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/config/dev.env.js b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/config/dev.env.js new file mode 100644 index 0000000000..1e22973ae7 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/config/dev.env.js @@ -0,0 +1,7 @@ +'use strict' +const merge = require('webpack-merge') +const prodEnv = require('./prod.env') + +module.exports = merge(prodEnv, { + NODE_ENV: '"development"' +}) diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/config/index.js b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/config/index.js new file mode 100644 index 0000000000..c5eded7f81 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/config/index.js @@ -0,0 +1,69 @@ +'use strict' +// Template version: 1.3.1 +// see http://vuejs-templates.github.io/webpack for documentation. + +const path = require('path') + +module.exports = { + dev: { + + // Paths + assetsSubDirectory: 'static', + assetsPublicPath: '/', + proxyTable: {}, + + // Various Dev Server settings + host: 'localhost', // can be overwritten by process.env.HOST + port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined + autoOpenBrowser: false, + errorOverlay: true, + notifyOnErrors: true, + poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- + + + /** + * Source Maps + */ + + // https://webpack.js.org/configuration/devtool/#development + devtool: 'cheap-module-eval-source-map', + + // If you have problems debugging vue-files in devtools, + // set this to false - it *may* help + // https://vue-loader.vuejs.org/en/options.html#cachebusting + cacheBusting: true, + + cssSourceMap: true + }, + + build: { + // Template for index.html + index: path.resolve(__dirname, '../dist/index.html'), + + // Paths + assetsRoot: path.resolve(__dirname, '../dist'), + assetsSubDirectory: 'static', + assetsPublicPath: '/', + + /** + * Source Maps + */ + + productionSourceMap: true, + // https://webpack.js.org/configuration/devtool/#production + devtool: '#source-map', + + // Gzip off by default as many popular static hosts such as + // Surge or Netlify already gzip all static assets for you. + // Before setting to `true`, make sure to: + // npm install --save-dev compression-webpack-plugin + productionGzip: false, + productionGzipExtensions: ['js', 'css'], + + // Run the build command with an extra argument to + // View the bundle analyzer report after build finishes: + // `npm run build --report` + // Set to `true` or `false` to always turn it on or off + bundleAnalyzerReport: process.env.npm_config_report + } +} diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/config/prod.env.js b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/config/prod.env.js new file mode 100644 index 0000000000..a6f997616e --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/config/prod.env.js @@ -0,0 +1,4 @@ +'use strict' +module.exports = { + NODE_ENV: '"production"' +} diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/index.html b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/index.html new file mode 100644 index 0000000000..03ce3fdf27 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/index.html @@ -0,0 +1,12 @@ + + + + + + deplugin-view-frontend + + +
+ + + diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/package.json b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/package.json new file mode 100644 index 0000000000..01a456b08b --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/package.json @@ -0,0 +1,74 @@ +{ + "name": "deplugin-view-frontend", + "version": "1.0.0", + "description": "A Vue.js project", + "author": "", + "private": true, + "scripts": { + "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", + "start": "npm run dev", + "build": "node build/build.js", + "buildPlugin": "node build/build-async-plugins.js" + }, + "dependencies": { + "@riophae/vue-treeselect": "0.4.0", + "lodash": "^4.17.21", + "vue": "2.6.10", + "vue-i18n": "7.3.2", + "vue-router": "^3.0.1", + "vue-uuid": "2.0.2", + "vuedraggable": "^2.24.3", + "vuex": "3.1.0" + }, + "devDependencies": { + "autoprefixer": "^7.1.2", + "babel-core": "^6.22.1", + "babel-helper-vue-jsx-merge-props": "^2.0.3", + "babel-loader": "^7.1.1", + "babel-plugin-syntax-jsx": "^6.18.0", + "babel-plugin-transform-runtime": "^6.22.0", + "babel-plugin-transform-vue-jsx": "^3.5.0", + "babel-preset-env": "^1.3.2", + "babel-preset-stage-2": "^6.22.0", + "chalk": "^2.0.1", + "copy-webpack-plugin": "^4.6.0", + "css-loader": "^0.28.0", + "element-ui": "2.15.7", + "extract-text-webpack-plugin": "^4.0.0-beta.0", + "file-loader": "^1.1.4", + "friendly-errors-webpack-plugin": "^1.6.1", + "html-webpack-plugin": "^3.2.0", + "js-cookie": "2.2.0", + "node-notifier": "^8.0.1", + "optimize-css-assets-webpack-plugin": "^3.2.0", + "ora": "^1.2.0", + "portfinder": "^1.0.13", + "postcss-import": "^11.0.0", + "postcss-loader": "^2.0.8", + "postcss-url": "^7.2.1", + "rimraf": "^2.6.0", + "sass": "^1.33.0", + "sass-loader": "^7.3.1", + "semver": "^5.3.0", + "shelljs": "^0.8.5", + "svg-sprite-loader": "4.1.3", + "uglifyjs-webpack-plugin": "^1.1.1", + "url-loader": "^0.5.8", + "vue-loader": "^15.6.4", + "vue-style-loader": "^4.1.2", + "vue-template-compiler": "2.6.10", + "webpack": "^4.8.1", + "webpack-bundle-analyzer": "^3.3.2", + "webpack-dev-server": "^3.1.11", + "webpack-merge": "^4.1.0" + }, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + }, + "browserslist": [ + "> 1%", + "last 2 versions", + "not ie <= 8" + ] +} diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/pom.xml b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/pom.xml new file mode 100644 index 0000000000..3bc0fa25ec --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/pom.xml @@ -0,0 +1,80 @@ + + + + + io.dataease + view-racebar + ${dataease.version} + + + 4.0.0 + view-racebar-frontend + + + UTF-8 + UTF-8 + 1.9.1 + + + + + + maven-clean-plugin + + + + static + + ** + + false + + + + + + + + com.github.eirslett + frontend-maven-plugin + ${frontend-maven-plugin.version} + + + install node and npm + + install-node-and-npm + + + + v16.20.2 + 7.6.3 + + + + + npm install + + npm + + + + install + + + + + npm run buildPlugin + + npm + + + run buildPlugin + + + + + + + diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/App.vue b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/App.vue new file mode 100644 index 0000000000..8542e07722 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/App.vue @@ -0,0 +1,23 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/assets/logo.png b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/assets/logo.png new file mode 100644 index 0000000000..f3d2503fc2 Binary files /dev/null and b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/assets/logo.png differ diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/HelloWorld.vue b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/HelloWorld.vue new file mode 100644 index 0000000000..1c19f2a399 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/HelloWorld.vue @@ -0,0 +1,113 @@ + + + + + + diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/SvgIcon/index.vue b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/SvgIcon/index.vue new file mode 100644 index 0000000000..f81b67f667 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/SvgIcon/index.vue @@ -0,0 +1,60 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/selector/BackgroundColorSelector.vue b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/selector/BackgroundColorSelector.vue new file mode 100644 index 0000000000..e1cbf5fe18 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/selector/BackgroundColorSelector.vue @@ -0,0 +1,102 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/selector/ColorSelector.vue b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/selector/ColorSelector.vue new file mode 100644 index 0000000000..3a579e8889 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/selector/ColorSelector.vue @@ -0,0 +1,306 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/selector/GraphicSetting.vue b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/selector/GraphicSetting.vue new file mode 100644 index 0000000000..a1e4f0e4fe --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/selector/GraphicSetting.vue @@ -0,0 +1,177 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/selector/LabelSelector.vue b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/selector/LabelSelector.vue new file mode 100644 index 0000000000..354d70f2eb --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/selector/LabelSelector.vue @@ -0,0 +1,220 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/selector/LegendSelector.vue b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/selector/LegendSelector.vue new file mode 100644 index 0000000000..5125412616 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/selector/LegendSelector.vue @@ -0,0 +1,165 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/selector/MarginSelector.vue b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/selector/MarginSelector.vue new file mode 100644 index 0000000000..7eb26d6a70 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/selector/MarginSelector.vue @@ -0,0 +1,280 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/selector/SizeSelectorAntV.vue b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/selector/SizeSelectorAntV.vue new file mode 100644 index 0000000000..4cacee920a --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/selector/SizeSelectorAntV.vue @@ -0,0 +1,130 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/selector/SliderSetting.vue b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/selector/SliderSetting.vue new file mode 100644 index 0000000000..147bd6f3dd --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/selector/SliderSetting.vue @@ -0,0 +1,159 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/selector/TitleSelector.vue b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/selector/TitleSelector.vue new file mode 100644 index 0000000000..c77337e92a --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/selector/TitleSelector.vue @@ -0,0 +1,319 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/selector/TooltipSelector.vue b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/selector/TooltipSelector.vue new file mode 100644 index 0000000000..bba74bfd46 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/selector/TooltipSelector.vue @@ -0,0 +1,152 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/selector/XAxisSelector.vue b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/selector/XAxisSelector.vue new file mode 100644 index 0000000000..c21be86386 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/selector/XAxisSelector.vue @@ -0,0 +1,493 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/selector/YAxisSelector.vue b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/selector/YAxisSelector.vue new file mode 100644 index 0000000000..e7b29e2b54 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/selector/YAxisSelector.vue @@ -0,0 +1,493 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/views/ChartDragItem.vue b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/views/ChartDragItem.vue new file mode 100644 index 0000000000..53ae1e2ad0 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/views/ChartDragItem.vue @@ -0,0 +1,278 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/views/ChartTitleUpdate.vue b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/views/ChartTitleUpdate.vue new file mode 100644 index 0000000000..ed97a67067 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/views/ChartTitleUpdate.vue @@ -0,0 +1,480 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/views/DimensionExtItem.vue b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/views/DimensionExtItem.vue new file mode 100644 index 0000000000..a62c724092 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/views/DimensionExtItem.vue @@ -0,0 +1,371 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/views/DimensionItem.vue b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/views/DimensionItem.vue new file mode 100644 index 0000000000..d4c34cdbd6 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/views/DimensionItem.vue @@ -0,0 +1,415 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/views/DrillItem.vue b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/views/DrillItem.vue new file mode 100644 index 0000000000..7321b8a08c --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/views/DrillItem.vue @@ -0,0 +1,146 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/views/FieldErrorTips.vue b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/views/FieldErrorTips.vue new file mode 100644 index 0000000000..9d7b574721 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/views/FieldErrorTips.vue @@ -0,0 +1,21 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/views/FilterItem.vue b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/views/FilterItem.vue new file mode 100644 index 0000000000..3dd5952d69 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/views/FilterItem.vue @@ -0,0 +1,154 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/views/LocationLineItem.vue b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/views/LocationLineItem.vue new file mode 100644 index 0000000000..7b2985d08f --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/views/LocationLineItem.vue @@ -0,0 +1,170 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/views/QuotaExtItem.vue b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/views/QuotaExtItem.vue new file mode 100644 index 0000000000..79b2ff1867 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/views/QuotaExtItem.vue @@ -0,0 +1,410 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/views/QuotaItem.vue b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/views/QuotaItem.vue new file mode 100644 index 0000000000..84f4ca7bc9 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/views/QuotaItem.vue @@ -0,0 +1,395 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/views/ViewTrackBar.vue b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/views/ViewTrackBar.vue new file mode 100644 index 0000000000..a58cad9770 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/views/ViewTrackBar.vue @@ -0,0 +1,80 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/views/utils.js b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/views/utils.js new file mode 100644 index 0000000000..307c4f637e --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/components/views/utils.js @@ -0,0 +1,99 @@ +export function getItemType(dimensionData, quotaData, item) { + // Check whether the current view is in template status + // ( dimensionData and quotaData have no data). If yes, return 'success' directly + if (dimensionData.length === 0 && quotaData.length === 0) { + return 'success' + } + // 将item的字段在数据集维度、指标字段中查询一遍,如果遇到id不存在、字段类型不一致、维度指标不一致,则提示 + const status = item.groupType + let checked = false + if (status === 'd') { + for (let i = 0; i < dimensionData.length; i++) { + const ele = dimensionData[i] + if (item.chartId) { + if (ele.dataeaseName === item.dataeaseName && ele.deType === item.deType && ele.groupType === item.groupType) { + checked = true + break + } + } else { + if (ele.id === item.id && ele.deType === item.deType && ele.groupType === item.groupType) { + checked = true + break + } + } + } + } + if (status === 'q') { + for (let i = 0; i < quotaData.length; i++) { + const ele = quotaData[i] + if (item.chartId) { + if (ele.dataeaseName === item.dataeaseName && ele.deType === item.deType && ele.groupType === item.groupType) { + checked = true + break + } + } else { + if (ele.id === item.id && ele.deType === item.deType && ele.groupType === item.groupType) { + checked = true + break + } + } + } + } + + if (checked) { + if (status === 'd') { + return '' + } else if (status === 'q') { + return 'success' + } + } else { + return 'danger' + } +} + +export function getRemark(chart) { + const remark = {} + if (chart.customStyle) { + const customStyle = JSON.parse(chart.customStyle) + if (customStyle.text) { + const title = JSON.parse(JSON.stringify(customStyle.text)) + remark.show = title.remarkShow ? title.remarkShow : false + remark.content = title.remark ? title.remark : '' + remark.bgFill = title.remarkBackgroundColor ? title.remarkBackgroundColor : '#ffffffff' + } + } + return remark +} + +export function getOriginFieldName(dimensionList, quotaList, field) { + let originName = '' + for (let i = 0; i < dimensionList.length; i++) { + const item = dimensionList[i] + if (item.id === field.id) { + originName = item.name + break + } + } + for (let i = 0; i < quotaList.length; i++) { + const item = quotaList[i] + if (item.id === field.id) { + originName = item.name + break + } + } + return originName +} + +export function resetValueFormatter(item) { + if (item) { + item.formatterCfg = { + type: 'auto', // auto,value,percent + unit: 1, // 换算单位 + suffix: '', // 单位后缀 + decimalCount: 2, // 小数位数 + thousandSeparator: true// 千分符 + } + } +} + +export const quotaViews = ['label', 'richTextView', 'text', 'gauge', 'liquid'] diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/de-base/lang/en.js b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/de-base/lang/en.js new file mode 100644 index 0000000000..40dc579ea8 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/de-base/lang/en.js @@ -0,0 +1,29 @@ +export default { + plugin_view_racebar: { + source: 'XAxis', + sourceExt: 'Group', + target: 'YAxis', + map_type: 'Map Type', + link_line: 'Link Line', + mark_size: 'YAxis', + type_title: 'RaceBar', + label: 'Area', + angle: 'Symbol', + marker: 'Marker', + pentagon: 'Pentagon', + hexagon: 'Hexagon', + octagon: 'Octagon', + hexagram: 'Hexagram', + border: 'Border', + light: 'Light', + dark: 'Dark', + label_format_tip: 'The field value can be read in the form of {field Name}, the fields in the label and the tips are interchangeable, and the built-in latitude and longitude related fields', + tooltip_format_tip: 'The field value can be read in the form of {field Name}, the fields in the label and the tips are interchangeable, and the built-in latitude and longitude related fields.(the label does not support line breaks)', + graphic: 'Group Setting', + slider: 'RaceBar Setting', + slider_auto: 'Auto', + slider_repeat: 'Repeat', + slider_timeout: 'Timeout', + slider_max: 'Max Number', + } +} diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/de-base/lang/index.js b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/de-base/lang/index.js new file mode 100644 index 0000000000..e50d0478e6 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/de-base/lang/index.js @@ -0,0 +1,49 @@ +import Vue from 'vue' +import VueI18n from 'vue-i18n' +import Cookies from 'js-cookie' +import elementEnLocale from 'element-ui/lib/locale/lang/en' // element-ui lang +import elementZhLocale from 'element-ui/lib/locale/lang/zh-CN'// element-ui lang +import elementTWLocale from 'element-ui/lib/locale/lang/zh-TW'// element-ui lang + +import localMessages from './messages' + + +Vue.use(VueI18n) + +const messages = { + en_US: { + ...localMessages['en_US'], + ...elementEnLocale + }, + zh_CN: { + ...localMessages['zh_CN'], + ...elementZhLocale + }, + zh_TW: { + ...localMessages['zh_TW'], + ...elementTWLocale + } +} +export function getLanguage () { + const chooseLanguage = Cookies.get('language') + if (chooseLanguage) return chooseLanguage + + // if has not choose language + const language = (navigator.language || navigator.browserLanguage).toLowerCase() + const locales = Object.keys(messages) + for (const locale of locales) { + if (language.indexOf(locale) > -1) { + return locale + } + } + return 'zh_CN' +} +const i18n = new VueI18n({ + // set locale + // options: en | zh | es + locale: getLanguage(), + // set locale messages + messages +}) + +export default i18n diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/de-base/lang/messages.js b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/de-base/lang/messages.js new file mode 100644 index 0000000000..844f8c0673 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/de-base/lang/messages.js @@ -0,0 +1,17 @@ +import enLocale from './en' +import zhLocale from './zh' +import twLocale from './tw' + +const messages = { + en_US: { + ...enLocale + }, + zh_CN: { + ...zhLocale + }, + zh_TW: { + ...twLocale + } +} + +export default messages \ No newline at end of file diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/de-base/lang/tw.js b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/de-base/lang/tw.js new file mode 100644 index 0000000000..72ad095c3b --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/de-base/lang/tw.js @@ -0,0 +1,30 @@ +export default { + plugin_view_racebar: { + type_title: '動態排序圖', + source: '主軸', + sourceExt: '分組', + target: '值', + map_type: '類型', + link_line: '連線', + mark_size: '值', + label: '區域', + angle: '符號', + marker: '標記', + pentagon: '五邊形', + hexagon: '六邊形', + + octagon: '八邊形', + hexagram: '六角星', + border: '邊框', + light: '亮色', + dark: '暗色', + label_format_tip: '可以${fieldName}的形式讀取字段值,標籤和提示中的字段互相通用,內置經緯度相關字段', + tooltip_format_tip: '可以${fieldName}的形式讀取字段值,標籤和提示中的字段互相通用,內置經緯度相關字段(標籤不支持換行)', + graphic: '動態分組', + slider: '動態排序配置', + slider_auto: '自動', + slider_repeat: '重復', + slider_timeout: '間隔', + slider_max: '顯示個數', + } +} diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/de-base/lang/zh.js b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/de-base/lang/zh.js new file mode 100644 index 0000000000..f69931bb1a --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/de-base/lang/zh.js @@ -0,0 +1,28 @@ +export default { + plugin_view_racebar: { + type_title: '动态排序图', + source: '主轴', + sourceExt: '分组', + target: '值', + map_type: '类型', + link_line: '连线', + mark_size: '值', + angle: '符号', + marker: '标记', + pentagon: '五角形', + hexagon: '六角形', + octagon: '八角形', + hexagram: '六角星', + border: '边框', + light: '亮色', + dark: '暗色', + label_format_tip: '可以${fieldName}的形式读取字段值,标签和提示中的字段互相通用,内置经纬度相关字段', + tooltip_format_tip: '可以${fieldName}的形式读取字段值,标签和提示中的字段互相通用,内置经纬度相关字段(标签不支持换行)', + graphic: '动态分组', + slider: '动态排序配置', + slider_auto: '自动', + slider_repeat: '重复', + slider_timeout: '间隔', + slider_max: '显示个数', + } +} diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/icons/index.js b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/icons/index.js new file mode 100644 index 0000000000..2c6b309c96 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/icons/index.js @@ -0,0 +1,9 @@ +import Vue from 'vue' +import SvgIcon from '@/components/SvgIcon'// svg component + +// register globally +Vue.component('svg-icon', SvgIcon) + +const req = require.context('./svg', false, /\.svg$/) +const requireAll = requireContext => requireContext.keys().map(requireContext) +requireAll(req) diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/icons/svg/race-bar.svg b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/icons/svg/race-bar.svg new file mode 100644 index 0000000000..392d871658 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/icons/svg/race-bar.svg @@ -0,0 +1 @@ + diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/icons/svg/view-race-bar-backend.svg b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/icons/svg/view-race-bar-backend.svg new file mode 100644 index 0000000000..becfdb6caf --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/icons/svg/view-race-bar-backend.svg @@ -0,0 +1 @@ +【icon】插件管理-导出 \ No newline at end of file diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/icons/svgo.yml b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/icons/svgo.yml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/main.js b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/main.js new file mode 100644 index 0000000000..b1d6ebdf5f --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/main.js @@ -0,0 +1,36 @@ +// The Vue build version to load with the `import` command +// (runtime-only or standalone) has been set in webpack.base.conf with an alias. +import Vue from 'vue' +import App from './App' +import router from './router' +import ElementUI from 'element-ui' +import Cookies from 'js-cookie' +import i18n from './de-base/lang' +import draggable from 'vuedraggable' +import Treeselect from '@riophae/vue-treeselect' +import '@riophae/vue-treeselect/dist/vue-treeselect.css' +import '@/icons' // icon +import { GaodeMap } from '@antv/l7-maps' +Vue.config.productionTip = false +Vue.use(ElementUI, { + size: Cookies.get('size') || 'medium', + i18n: (key, value) => i18n.t(key, value) +}) +Vue.component('Treeselect', Treeselect) +Vue.component('draggable', draggable) +Vue.prototype.hasDataPermission = function(pTarget, pSource) { + + if (pSource && pTarget) { + return pSource.indexOf(pTarget) > -1 + } + return false +} +Vue.prototype.$gaodeMap = GaodeMap +/* eslint-disable no-new */ +new Vue({ + el: '#app', + router, + i18n, + components: { App }, + template: '' +}) diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/router/index.js b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/router/index.js new file mode 100644 index 0000000000..58f8665bbc --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/router/index.js @@ -0,0 +1,21 @@ +import Vue from 'vue' +import Router from 'vue-router' +import HelloWorld from '@/components/HelloWorld' +import SymbolMap from '@/views/antv/racebar/type' + +Vue.use(Router) + +export default new Router({ + routes: [ + { + path: '/', + name: 'HelloWorld', + component: HelloWorld + }, + { + path: '/race-bar', + name: 'race-bar', + component: SymbolMap + } + ] +}) diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/utils/clickoutside.js b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/utils/clickoutside.js new file mode 100644 index 0000000000..fb030a98eb --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/utils/clickoutside.js @@ -0,0 +1,68 @@ +const nodeList = []; +const ctx = '@@clickoutsideContext'; + +let startClick; +let seed = 0; + + +function createDocumentHandler(el, binding, vnode) { + return function(mouseup = {}, mousedown = {}) { + if (!vnode || + !vnode.context || + !mouseup.target || + !mousedown.target || + el.contains(mouseup.target) || + el.contains(mousedown.target) || + el === mouseup.target || + (vnode.context.popperElm && + (vnode.context.popperElm.contains(mouseup.target) || + vnode.context.popperElm.contains(mousedown.target)))) return; + + if (binding.expression && + el[ctx].methodName && + vnode.context[el[ctx].methodName]) { + vnode.context[el[ctx].methodName](); + } else { + el[ctx].bindingFn && el[ctx].bindingFn(); + } + }; +} + +/** + * v-clickoutside + * @desc 点击元素外面才会触发的事件 + * @example + * ```vue + *
+ * ``` + */ +export default { + bind(el, binding, vnode) { + nodeList.push(el); + const id = seed++; + el[ctx] = { + id, + documentHandler: createDocumentHandler(el, binding, vnode), + methodName: binding.expression, + bindingFn: binding.value + }; + }, + + update(el, binding, vnode) { + el[ctx].documentHandler = createDocumentHandler(el, binding, vnode); + el[ctx].methodName = binding.expression; + el[ctx].bindingFn = binding.value; + }, + + unbind(el) { + let len = nodeList.length; + + for (let i = 0; i < len; i++) { + if (nodeList[i][ctx].id === el[ctx].id) { + nodeList.splice(i, 1); + break; + } + } + delete el[ctx]; + } +}; diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/utils/compare.js b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/utils/compare.js new file mode 100644 index 0000000000..b06a104a10 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/utils/compare.js @@ -0,0 +1,29 @@ +export const compareItem = { + type: 'none', // year-yoy/month-yoy等 + resultData: 'percent', // 对比差sub,百分比percent等 + field: '', + custom: { + field: '', + calcType: '0', // 0-增长值,1-增长率 + timeType: '0', // 0-固定日期,1-日期区间 + currentTime: '', + compareTime: '', + currentTimeRange: [], + compareTimeRange: [] + } +} + +export const compareYearList = [ + { name: 'year_mom', value: 'year_mom' } +] + +export const compareMonthList = [ + { name: 'month_mom', value: 'month_mom' }, + { name: 'year_yoy', value: 'year_yoy' } +] + +export const compareDayList = [ + { name: 'day_mom', value: 'day_mom' }, + { name: 'month_yoy', value: 'month_yoy' }, + { name: 'year_yoy', value: 'year_yoy' } +] diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/utils/formatter.js b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/utils/formatter.js new file mode 100644 index 0000000000..dfe5bc792b --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/utils/formatter.js @@ -0,0 +1,77 @@ +export const formatterItem = { + type: 'auto', // auto,value,percent + unit: 1, // 换算单位 + suffix: '', // 单位后缀 + decimalCount: 2, // 小数位数 + thousandSeparator: true// 千分符 +} + +// 单位list +export const unitList = [ + { name: 'unit_none', value: 1 }, + { name: 'unit_thousand', value: 1000 }, + { name: 'unit_ten_thousand', value: 10000 }, + { name: 'unit_million', value: 1000000 }, + { name: 'unit_hundred_million', value: 100000000 } +] + +// 格式化方式 +export const formatterType = [ + { name: 'value_formatter_auto', value: 'auto' }, + { name: 'value_formatter_value', value: 'value' }, + { name: 'value_formatter_percent', value: 'percent' } +] + +export function valueFormatter(value, formatter) { + if (value === null || value === undefined) { + return null + } + if(formatter == undefined){ + return value; + } + // 1.unit 2.decimal 3.thousand separator and suffix + let result + if (formatter.type === 'auto') { + result = transSeparatorAndSuffix(transUnit(value, formatter), formatter) + } else if (formatter.type === 'value') { + result = transSeparatorAndSuffix(transDecimal(transUnit(value, formatter), formatter), formatter) + } else if (formatter.type === 'percent') { + value = value * 100 + result = transSeparatorAndSuffix(transDecimal(value, formatter), formatter) + } else { + result = value + } + return result +} + +function transUnit(value, formatter) { + return value / formatter.unit +} + +function transDecimal(value, formatter) { + return value.toFixed(formatter.decimalCount) +} + +function transSeparatorAndSuffix(value, formatter) { + let str = value + '' + if (formatter.thousandSeparator) { + const thousandsReg = /(\d)(?=(\d{3})+$)/g + const numArr = str.split('.') + numArr[0] = numArr[0].replace(thousandsReg, '$1,') + str = numArr.join('.') + } + if (formatter.type === 'percent') { + str += '%' + } else { + if (formatter.unit === 1000) { + str += '千' + } else if (formatter.unit === 10000) { + str += '万' + } else if (formatter.unit === 1000000) { + str += '百万' + } else if (formatter.unit === 100000000) { + str += '亿' + } + } + return str + formatter.suffix.replace(/(^\s*)|(\s*$)/g, '') +} diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/utils/map.js b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/utils/map.js new file mode 100644 index 0000000000..caa4c17e11 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/utils/map.js @@ -0,0 +1,849 @@ +import {valueFormatter} from "./formatter"; + +export const DEFAULT_COLOR_CASE = { + value: 'default', + colors: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'], + alpha: 100, + tableHeaderBgColor: '#e1eaff', + tableItemBgColor: '#ffffff', + tableFontColor: '#000000', + tableStripe: true, + dimensionColor: '#000000', + quotaColor: '#000000', + tableBorderColor: '#cfdaf4' +} +export const DEFAULT_SIZE = { + barDefault: true, + barWidth: 40, + barGap: 0.4, + lineWidth: 2, + lineType: 'solid', + lineSymbol: 'marker', + lineSymbolSize: 4, + lineSmooth: true, + lineArea: false, + pieInnerRadius: 0, + pieOuterRadius: 80, + pieRoseType: 'radius', + pieRoseRadius: 5, + funnelWidth: 80, + radarShape: 'polygon', + radarSize: 80, + tableTitleFontSize: 12, + tableItemFontSize: 12, + tableTitleHeight: 36, + tableItemHeight: 36, + tablePageSize: '20', + tableColumnMode: 'custom', + tableColumnWidth: 100, + tableHeaderAlign: 'left', + tableItemAlign: 'right', + gaugeMin: 0, + gaugeMax: 100, + gaugeStartAngle: 225, + gaugeEndAngle: -45, + dimensionFontSize: 18, + quotaFontSize: 18, + spaceSplit: 10, + dimensionShow: true, + quotaShow: true, + scatterSymbol: 'marker', + scatterSymbolSize: 15, + symbolOpacity: 5, + symbolStrokeWidth: 1, + treemapWidth: 80, + treemapHeight: 80, + liquidMax: 100, + liquidSize: 80, + liquidOutlineBorder: 4, + liquidOutlineDistance: 8, + liquidWaveLength: 128, + liquidWaveCount: 3, + liquidShape: 'circle', + tablePageMode: 'page' +} +export const COLOR_PANEL = [ + '#ff4500', + '#ff8c00', + '#ffd700', + '#90ee90', + '#00ced1', + '#1e90ff', + '#c71585', + '#999999', + '#000000', + '#FFFFFF' +] + +export const DEFAULT_SLIDER = { + show: false, + auto: true, + repeat: true, + timeout: 2, + fontSize: '10', + color: '#000000', + max: 10, +} +export const DEFAULT_Graphic = { + show: true, + fontSize: '60', + color: '#000000', + alpha: 25, + bottom: 90, + right: 60, +} + +export const DEFAULT_LABEL = { + show: true, + position: 'middle', + color: '#000000', + fontSize: '10', + labelLine: { + show: true + }, + fields: null, + initialized: true +} +export const DEFAULT_TOOLTIP = { + show: true, + trigger: 'item', + confine: true, + textStyle: { + fontSize: '10', + color: '#909399' + }, + backgroundColor: '#ffffff', + formatter: '', +} + +export const BASE_ECHARTS_SELECT = { + itemStyle: { + shadowBlur: 2 + } +} + +export const CHART_FONT_FAMILY = [ + {name: '微软雅黑', value: 'Microsoft YaHei'}, + {name: '宋体', value: 'SimSun'}, + {name: '黑体', value: 'SimHei'}, + {name: '楷体', value: 'KaiTi'} +] + +export const CHART_FONT_LETTER_SPACE = [ + {name: '0px', value: '0'}, + {name: '1px', value: '1'}, + {name: '2px', value: '2'}, + {name: '3px', value: '3'}, + {name: '4px', value: '4'}, + {name: '5px', value: '5'}, + {name: '6px', value: '6'}, + {name: '7px', value: '7'}, + {name: '8px', value: '8'}, + {name: '9px', value: '9'}, + {name: '10px', value: '10'} +] + +export const DEFAULT_TITLE_STYLE = { + show: true, + fontSize: '18', + color: '#000000', + hPosition: 'left', + vPosition: 'top', + isItalic: false, + isBolder: true, + remarkShow: false, + remark: '', + remarkBackgroundColor: '#ffffffff', + fontFamily: 'Microsoft YaHei', + letterSpace: '0', + fontShadow: false +} +export const DEFAULT_LEGEND_STYLE = { + show: true, + hPosition: 'center', + vPosition: 'bottom', + orient: 'horizontal', + icon: 'circle', + textStyle: { + color: '#333333', + fontSize: '12' + } +} +export const DEFAULT_BACKGROUND_COLOR = { + color: '#ffffff', + alpha: 100, + borderRadius: 5 +} +export const DEFAULT_BASE_MAP_STYLE = { + baseMapTheme: 'light' +} +export const BASE_MAP = { + title: { + text: '', + textStyle: { + fontWeight: 'normal' + } + }, + visualMap: { + min: 50, + max: 52, + text: ['High', 'Low'], + realtime: false, + calculable: true, + inRange: { + color: ['lightskyblue', 'yellow', 'orangered'] + }, + right: 0 + }, + + tooltip: {}, + geo: [ + { + show: true, + map: 'BUDDLE_MAP_BORDER', + + emphasis: { + disabled: true + }, + itemStyle: { + borderWidth: 2, + borderColor: '#d1d1d1', + borderType: 'solid' + }, + + roam: false + }, + { + show: true, + map: 'BUDDLE_MAP', + label: { + normal: { + show: false + }, + emphasis: { + show: false + } + }, + itemStyle: { + areaColor: '#f3f3f3', + borderType: 'dashed', + borderColor: '#fff' + }, + + roam: false + } + ], + series: [ + { + name: '', + type: 'scatter', + coordinateSystem: 'geo', + data: [] + } + ] +} + +export const compareItem = { + type: 'none', // year-yoy/month-yoy等 + resultData: 'percent', // 对比差sub,百分比percent等 + field: '', + custom: { + field: '', + calcType: '0', // 0-增长值,1-增长率 + timeType: '0', // 0-固定日期,1-日期区间 + currentTime: '', + compareTime: '', + currentTimeRange: [], + compareTimeRange: [] + } +} + +export const formatterItem = { + type: 'auto', // auto,value,percent + unit: 1, // 换算单位 + suffix: '', // 单位后缀 + decimalCount: 2, // 小数位数 + thousandSeparator: true// 千分符 +} + +export const DEFAULT_XAXIS_STYLE = { + show: true, + position: 'bottom', + name: '', + nameTextStyle: { + color: '#333333', + fontSize: 12 + }, + axisLabel: { + show: true, + color: '#333333', + fontSize: '12', + rotate: 0, + formatter: '{value}' + }, + axisLine: { + show: true, + lineStyle: { + color: '#cccccc', + width: 1, + style: 'solid' + } + }, + splitLine: { + show: false, + lineStyle: { + color: '#cccccc', + width: 1, + style: 'solid' + } + }, + axisValue: { + auto: true, + min: null, + max: null, + split: null, + splitCount: null + }, + axisLabelFormatter: { + type: 'auto', // auto,value,percent + unit: 1, // 换算单位 + suffix: '', // 单位后缀 + decimalCount: 2, // 小数位数 + thousandSeparator: true// 千分符 + } +} + +export const DEFAULT_YAXIS_STYLE = { + show: true, + position: 'left', + name: '', + nameTextStyle: { + color: '#333333', + fontSize: 12 + }, + axisLabel: { + show: true, + color: '#333333', + fontSize: '12', + rotate: 0, + formatter: '{value}' + }, + axisLine: { + show: false, + lineStyle: { + color: '#cccccc', + width: 1, + style: 'solid' + } + }, + splitLine: { + show: true, + lineStyle: { + color: '#cccccc', + width: 1, + style: 'solid' + } + }, + axisValue: { + auto: true, + min: null, + max: null, + split: null, + splitCount: null + }, + axisLabelFormatter: { + type: 'auto', // auto,value,percent + unit: 1, // 换算单位 + suffix: '', // 单位后缀 + decimalCount: 2, // 小数位数 + thousandSeparator: true// 千分符 + } +} + +export const DEFAULT_MARGIN_STYLE = { + marginModel: 'auto', + marginTop: 40, + marginBottom: 44, + marginLeft: 15, + marginRight: 10 +} + +export const HORIZONTAL_BAR = { + title: { + text: '', + textStyle: { + fontWeight: 'normal' + } + }, + grid: { + // top: 40, + // bottom: 44, + // left: 15, + // right: 40, + //containLabel: true + }, + tooltip: {}, + legend: { + show: true, + type: 'scroll', + itemWidth: 10, + itemHeight: 10, + icon: 'rect', + data: [] + }, + xAxis: { + max: 'dataMax', + axisLabel: { + formatter: function (n) { + return Math.round(n) + ''; + } + } + }, + yAxis: { + type: 'category', + inverse: true, + max: 10, + animationDuration: 300, + animationDurationUpdate: 300 + }, + dataset: { + source: [] + }, + series: [ + { + realtimeSort: true, + seriesLayoutBy: 'column', + type: 'bar', + } + ], + // Disable init animation. + animationDuration: 0, + animationDurationUpdate: 2000, + animationEasing: 'linear', + animationEasingUpdate: 'linear', + graphic: { + elements: [ + { + type: 'text', + right: 60, + bottom: 60, + style: { + font: 'bolder 50px monospace', + fill: 'rgba(100, 100, 100, 0.25)' + }, + z: 100 + } + ] + }, + dataZoom: [ + { + type: 'slider', + show: false, + xAxisIndex: [0], + start: 0, + end: 100 + }, + { + type: 'slider', + show: false, + yAxisIndex: [0], + left: '93%', + start: 0, + end: 100 + }, + { + type: 'inside', + disabled: true, + xAxisIndex: [0], + start: 0, + end: 100 + }, + { + type: 'inside', + disabled: true, + yAxisIndex: [0], + start: 0, + end: 100 + } + ] +} + +export function transAxisPosition(chart, axis) { + if (chart.type.includes('horizontal')) { + switch (axis.position) { + case 'top': + return 'left' + case 'bottom': + return 'right' + case 'left': + return 'bottom' + case 'right': + return 'top' + default: + return axis.position + } + } else { + return axis.position + } +} + +const convertData = (mapData, chart) => { + let maxVal = 0 + const k = terminalType === 'pc' ? 30 : 15 + const names = chart.data.x + const results = [] + for (let index = 0; index < names.length; index++) { + const name = names[index]; + const item = chart.data.series[0].data[index] + results.push({ + name, + value: (mapData[name] ? mapData[name].concat(item.value) : []), + dimensionList: item.dimensionList, + quotaList: item.quotaList + }) + maxVal = Math.max(maxVal, item.value) + } + const rate = k / maxVal + + return { + value: results, + rate: rate + } +} + +let terminalType = 'pc' + + +export function componentStyle(chart_option, chart) { + let xAxisLabelFormatter = null + let yAxisLabelFormatter = null + let yExtAxisLabelFormatter = null + const xFormatter = function (value) { + if (!xAxisLabelFormatter) { + return valueFormatter(value, formatterItem) + } else { + return valueFormatter(value, xAxisLabelFormatter) + } + } + + const yFormatter = function (value) { + if (!yAxisLabelFormatter) { + return valueFormatter(value, formatterItem) + } else { + return valueFormatter(value, yAxisLabelFormatter) + } + } + + const yExtFormatter = function (value) { + if (!yExtAxisLabelFormatter) { + return valueFormatter(value, formatterItem) + } else { + return valueFormatter(value, yExtAxisLabelFormatter) + } + } + + const padding = '8px' + if (chart.customStyle) { + const customStyle = JSON.parse(chart.customStyle) + if (customStyle.text) { + chart_option.title.show = customStyle.text.show + // 水平方向 + if (customStyle.text.hPosition === 'left') { + chart_option.title.left = padding + } else if (customStyle.text.hPosition === 'right') { + chart_option.title.right = padding + } else { + chart_option.title.left = customStyle.text.hPosition + } + // 垂直方向 + if (customStyle.text.vPosition === 'top') { + chart_option.title.top = padding + } else if (customStyle.text.vPosition === 'bottom') { + chart_option.title.bottom = padding + } else { + chart_option.title.top = customStyle.text.vPosition + } + const style = chart_option.title.textStyle ? chart_option.title.textStyle : {} + style.fontSize = customStyle.text.fontSize + style.color = customStyle.text.color + customStyle.text.isItalic ? style.fontStyle = 'italic' : style.fontStyle = 'normal' + customStyle.text.isBolder ? style.fontWeight = 'bold' : style.fontWeight = 'normal' + chart_option.title.textStyle = style + } + if (customStyle.legend && chart_option.legend) { + chart_option.legend.show = customStyle.legend.show + // 水平方向 + if (customStyle.legend.hPosition === 'left') { + chart_option.legend.left = padding + } else if (customStyle.legend.hPosition === 'right') { + chart_option.legend.right = padding + } else { + chart_option.legend.left = customStyle.legend.hPosition + } + // 垂直方向 + if (customStyle.legend.vPosition === 'top') { + chart_option.legend.top = padding + } else if (customStyle.legend.vPosition === 'bottom') { + chart_option.legend.bottom = padding + } else { + chart_option.legend.top = customStyle.legend.vPosition + } + chart_option.legend.orient = customStyle.legend.orient + chart_option.legend.icon = customStyle.legend.icon + chart_option.legend.textStyle = customStyle.legend.textStyle + if (chart.type === 'treemap' || chart.type === 'gauge') { + chart_option.legend.show = false + } + } + + if (customStyle.margin && customStyle.margin.marginModel && customStyle.margin.marginModel !== 'auto') { + const unit = getMarginUnit(customStyle.margin) + const result = {containLabel: true} + const realUnit = (unit === '%' ? unit : '') + if (customStyle.margin.marginTop != null) { + result.top = customStyle.margin.marginTop + realUnit + } + if (customStyle.margin.marginBottom != null) { + result.bottom = customStyle.margin.marginBottom + realUnit + } + if (customStyle.margin.marginLeft != null) { + result.left = customStyle.margin.marginLeft + realUnit + } + if (customStyle.margin.marginRight != null) { + result.right = customStyle.margin.marginRight + realUnit + } + if (!chart_option.grid) { + chart_option.grid = {} + } + Object.assign(chart_option.grid, JSON.parse(JSON.stringify(result))) + } + if (customStyle.background) { + chart_option.backgroundColor = hexColorToRGBA(customStyle.background.color, customStyle.background.alpha) + } + } +} + +export function hexColorToRGBA(hex, alpha) { + const rgb = [] // 定义rgb数组 + if (/^\#[0-9A-F]{3}$/i.test(hex)) { // 判断传入是否为#三位十六进制数 + let sixHex = '#' + hex.replace(/[0-9A-F]/ig, function (kw) { + sixHex += kw + kw // 把三位16进制数转化为六位 + }) + hex = sixHex // 保存回hex + } + if (/^#[0-9A-F]{6}$/i.test(hex)) { // 判断传入是否为#六位十六进制数 + hex.replace(/[0-9A-F]{2}/ig, function (kw) { + // eslint-disable-next-line no-eval + rgb.push(eval('0x' + kw)) // 十六进制转化为十进制并存如数组 + }) + return `rgba(${rgb.join(',')},${alpha / 100})` // 输出RGB格式颜色 + } else { + return 'rgb(0,0,0)' + } +} + + +export const DEFAULT_YAXIS_EXT_STYLE = { + show: true, + position: 'right', + name: '', + nameTextStyle: { + color: '#333333', + fontSize: 12 + }, + axisLabel: { + show: true, + color: '#333333', + fontSize: '12', + rotate: 0, + formatter: '{value}' + }, + splitLine: { + show: true, + lineStyle: { + color: '#cccccc', + width: 1, + style: 'solid' + } + }, + axisValue: { + auto: true, + min: null, + max: null, + split: null, + splitCount: null + } +} + +export const getDefaultTemplate = (chart, type, feed, showKey) => { + if (!chart || !chart.viewFields || !type) return null; + let viewFields = [] + if (chart.viewFields instanceof Array) { + viewFields = JSON.parse(JSON.stringify(chart.viewFields)) + } else { + viewFields = JSON.parse(chart.viewFields) + } + const separator = feed ? '\n' : ' ' + return viewFields.filter(field => field.busiType && field.busiType === type).map(field => { + const fieldName = field.name + let template = "${" + field.name + "}" + if (showKey) { + template = fieldName + ":${" + field.name + "}" + } + return template + }).join(separator) +} + +export function uuid() { + return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1); +} + +export const reverseColor = colorValue => { + colorValue = '0x' + colorValue.replace(/#/g, '') + const str = '000000' + (0xFFFFFF - colorValue).toString(16) + return '#' + str.substring(str.length - 6, str.length) +} + +export function seniorCfg(chart_option, chart) { + if (chart.senior && chart.type && (chart.type.includes('bar') || chart.type.includes('line') || chart.type.includes('mix'))) { + const senior = JSON.parse(chart.senior) + if (senior.functionCfg) { + if (senior.functionCfg.sliderShow) { + chart_option.dataZoom = [ + { + type: 'inside', + start: parseInt(senior.functionCfg.sliderRange[0]), + end: parseInt(senior.functionCfg.sliderRange[1]) + }, + { + type: 'slider', + start: parseInt(senior.functionCfg.sliderRange[0]), + end: parseInt(senior.functionCfg.sliderRange[1]) + } + ] + if (senior.functionCfg.sliderBg) { + chart_option.dataZoom[1].dataBackground = { + lineStyle: {color: hexToRgba(senior.functionCfg.sliderBg, 0.5)}, + areaStyle: {color: hexToRgba(senior.functionCfg.sliderBg, 0.5)} + } + chart_option.dataZoom[1].borderColor = hexToRgba(senior.functionCfg.sliderBg, 0.3) + } + if (senior.functionCfg.sliderFillBg) { + chart_option.dataZoom[1].selectedDataBackground = { + lineStyle: {color: senior.functionCfg.sliderFillBg}, + areaStyle: {color: senior.functionCfg.sliderFillBg} + } + const rgba = hexToRgba(senior.functionCfg.sliderFillBg, 0.2) + chart_option.dataZoom[1].fillerColor = rgba + } + if (senior.functionCfg.sliderTextClolor) { + chart_option.dataZoom[1].textStyle = {color: senior.functionCfg.sliderTextClolor} + const rgba = hexToRgba(senior.functionCfg.sliderTextClolor, 0.5) + chart_option.dataZoom[1].handleStyle = {color: rgba} + } + + if (chart.type.includes('horizontal')) { + chart_option.dataZoom[0].yAxisIndex = [0] + chart_option.dataZoom[1].yAxisIndex = [0] + chart_option.dataZoom[1].left = '10px' + } + } + } + // begin mark line settings + if (chart_option.series && chart_option.series.length > 0) { + chart_option.series[0].markLine = { + symbol: 'none', + data: [] + } + } + if (senior.assistLine && senior.assistLine.length > 0) { + if (chart_option.series && chart_option.series.length > 0) { + const customStyle = JSON.parse(chart.customStyle) + let xAxis, yAxis, axisFormatterCfg + if (customStyle.xAxis) { + xAxis = JSON.parse(JSON.stringify(customStyle.xAxis)) + if (chart.type.includes('horizontal')) { + axisFormatterCfg = xAxis.axisLabelFormatter ? xAxis.axisLabelFormatter : DEFAULT_XAXIS_STYLE.axisLabelFormatter + } + } + if (customStyle.yAxis) { + yAxis = JSON.parse(JSON.stringify(customStyle.yAxis)) + if (!chart.type.includes('horizontal')) { + axisFormatterCfg = yAxis.axisLabelFormatter ? yAxis.axisLabelFormatter : DEFAULT_YAXIS_STYLE.axisLabelFormatter + } + } + + const fixedLines = senior.assistLine.filter(ele => ele.field === '0') + const dynamicLines = chart.data.dynamicAssistLines + const lines = fixedLines.concat(dynamicLines) + + lines.forEach(ele => { + if (chart.type.includes('horizontal')) { + chart_option.series[0].markLine.data.push({ + symbol: 'none', + xAxis: parseFloat(ele.value), + name: ele.name, + lineStyle: { + color: ele.color, + type: ele.lineType + }, + label: { + show: true, + color: ele.color, + fontSize: ele.fontSize ? parseInt(ele.fontSize) : 10, + position: xAxis.position === 'bottom' ? 'insideStartTop' : 'insideEndTop', + formatter: function (param) { + return ele.name + ' : ' + valueFormatter(param.value, axisFormatterCfg) + } + }, + tooltip: { + show: false + } + }) + } else { + chart_option.series[0].markLine.data.push({ + symbol: 'none', + yAxis: parseFloat(ele.value), + name: ele.name, + lineStyle: { + color: ele.color, + type: ele.lineType + }, + label: { + show: true, + color: ele.color, + fontSize: ele.fontSize ? parseInt(ele.fontSize) : 10, + position: yAxis.position === 'left' ? 'insideStartTop' : 'insideEndTop', + formatter: function (param) { + return ele.name + ' : ' + valueFormatter(param.value, axisFormatterCfg) + } + }, + tooltip: { + show: false + } + }) + } + }) + } + } + } +} + +const hexToRgba = (hex, opacity) => { + let rgbaColor = '' + const reg = /^#[\da-f]{6}$/i + if (reg.test(hex)) { + rgbaColor = `rgba(${parseInt('0x' + hex.slice(1, 3))},${parseInt( + '0x' + hex.slice(3, 5) + )},${parseInt('0x' + hex.slice(5, 7))},${opacity})` + } + return rgbaColor +} + +export const getMarginUnit = marginForm => { + if (!marginForm.marginModel || marginForm.marginModel === 'auto') return null + if (marginForm.marginModel === 'absolute') return 'px' + if (marginForm.marginModel === 'relative') return '%' + return null +} diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/utils/validate.js b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/utils/validate.js new file mode 100644 index 0000000000..3e8ffa9448 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/utils/validate.js @@ -0,0 +1,15 @@ +/** + * Created by PanJiaChen on 16/11/18. + */ + +/** + * @param {string} path + * @returns {Boolean} + */ +export function isExternal(path) { + return /^(https?:|mailto:|tel:)/.test(path) || /^(http?:|mailto:|tel:)/.test(path) || path.startsWith('/api/pluginCommon/staticInfo') +} + + + + diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/views/PluginDemo.vue b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/views/PluginDemo.vue new file mode 100644 index 0000000000..1164dfab1d --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/views/PluginDemo.vue @@ -0,0 +1,55 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/views/antv/racebar/data.vue b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/views/antv/racebar/data.vue new file mode 100644 index 0000000000..499f1c3b58 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/views/antv/racebar/data.vue @@ -0,0 +1,682 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/views/antv/racebar/index.vue b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/views/antv/racebar/index.vue new file mode 100644 index 0000000000..6bb96cde53 --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/views/antv/racebar/index.vue @@ -0,0 +1,690 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/views/antv/racebar/style.vue b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/views/antv/racebar/style.vue new file mode 100644 index 0000000000..3db2d2a63d --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/views/antv/racebar/style.vue @@ -0,0 +1,251 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/views/antv/racebar/type.vue b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/views/antv/racebar/type.vue new file mode 100644 index 0000000000..b1702e363b --- /dev/null +++ b/extensions/dataease-extensions-view/view-racebar/view-racebar-frontend/src/views/antv/racebar/type.vue @@ -0,0 +1,63 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-sankey/build.sh b/extensions/dataease-extensions-view/view-sankey/build.sh new file mode 100644 index 0000000000..8f59a26490 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/build.sh @@ -0,0 +1,8 @@ +#!/bin/sh +mvn clean package + +cp view-sankey-backend/target/view-sankey-backend-1.18.9.jar . + +zip -r sankey.zip ./view-sankey-backend-1.18.9.jar ./plugin.json + +rm -f ./view-sankey-backend-1.18.9.jar diff --git a/extensions/dataease-extensions-view/view-sankey/plugin.json b/extensions/dataease-extensions-view/view-sankey/plugin.json new file mode 100644 index 0000000000..a05fd1497d --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/plugin.json @@ -0,0 +1,13 @@ +{ + "name": "桑基图", + "free": 0, + "store": "default", + "cost": 0, + "category": "view", + "descript": "AntV G2Plot 桑基图插件", + "version": "1.18.9", + "creator": "DATAEASE", + "moduleName": "view-sankey-backend", + "require": "1.18.9", + "dsType": "" +} diff --git a/extensions/dataease-extensions-view/view-sankey/pom.xml b/extensions/dataease-extensions-view/view-sankey/pom.xml new file mode 100644 index 0000000000..2e3ac1d6f3 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/pom.xml @@ -0,0 +1,20 @@ + + + + dataease-extensions-view + io.dataease + ${dataease.version} + + 4.0.0 + + view-sankey + pom + + view-sankey-frontend + view-sankey-backend + + + + diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-backend/pom.xml b/extensions/dataease-extensions-view/view-sankey/view-sankey-backend/pom.xml new file mode 100644 index 0000000000..a47e0dbe55 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-backend/pom.xml @@ -0,0 +1,109 @@ + + + + view-sankey + io.dataease + ${dataease.version} + + 4.0.0 + + view-sankey-backend + + + + io.dataease + dataease-plugin-view + + + + + + + src/main/java + + **/*.properties + **/*.xml + + false + + + src/main/resources + + **/* + + false + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 11 + 11 + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.4 + + + **/server/** + **/*.properties + **/Application* + + + + + + maven-clean-plugin + + + + src/main/resources/static + + ** + + false + + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + main-class-placement + generate-resources + + + + + + + + + + + + run + + + + + + + + + + + diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-backend/src/main/java/io/dataease/plugins/view/official/impl/SankeyService.java b/extensions/dataease-extensions-view/view-sankey/view-sankey-backend/src/main/java/io/dataease/plugins/view/official/impl/SankeyService.java new file mode 100644 index 0000000000..9aa7f6142d --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-backend/src/main/java/io/dataease/plugins/view/official/impl/SankeyService.java @@ -0,0 +1,122 @@ +package io.dataease.plugins.view.official.impl; + +import io.dataease.plugins.common.dto.StaticResource; +import io.dataease.plugins.view.entity.PluginViewField; +import io.dataease.plugins.view.entity.PluginViewParam; +import io.dataease.plugins.view.entity.PluginViewType; +import io.dataease.plugins.view.service.ViewPluginService; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.stereotype.Service; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Service +public class SankeyService extends ViewPluginService { + + private static final String VIEW_TYPE_VALUE = "sankey"; + + /* 下版这些常量移到sdk */ + private static final String TYPE = "-type"; + private static final String DATA = "-data"; + private static final String STYLE = "-style"; + private static final String VIEW = "-view"; + private static final String SUFFIX = "svg"; + /* 下版这些常量移到sdk */ + private static final String VIEW_TYPE = VIEW_TYPE_VALUE + TYPE; + private static final String VIEW_DATA = VIEW_TYPE_VALUE + DATA; + private static final String VIEW_STYLE = VIEW_TYPE_VALUE + STYLE; + private static final String VIEW_VIEW = VIEW_TYPE_VALUE + VIEW; + + private static final String[] VIEW_STYLE_PROPERTIES = { + "color-selector", + "label-selector", + "tooltip-selector-ant-v", + "title-selector-ant-v" + }; + + private static final Map VIEW_STYLE_PROPERTY_INNER = new HashMap<>(); + + static { + VIEW_STYLE_PROPERTY_INNER.put("color-selector", new String[]{"value"}); + VIEW_STYLE_PROPERTY_INNER.put("label-selector", new String[]{"show", "fontSize", "color"}); + VIEW_STYLE_PROPERTY_INNER.put("tooltip-selector-ant-v", new String[]{"show", "fontSize", "color", "backgroundColor"}); + VIEW_STYLE_PROPERTY_INNER.put("title-selector-ant-v", new String[]{"show", "title", "fontSize", "color", "hPosition", "vPosition", "isItalic", "isBolder"}); + } + + @Override + public PluginViewType viewType() { + PluginViewType pluginViewType = new PluginViewType(); + pluginViewType.setRender("antv"); + pluginViewType.setCategory("chart.chart_type_relation"); + pluginViewType.setValue(VIEW_TYPE_VALUE); + pluginViewType.setProperties(VIEW_STYLE_PROPERTIES); + pluginViewType.setPropertyInner(VIEW_STYLE_PROPERTY_INNER); + return pluginViewType; + } + + @Override + public Object format(Object o) { + return null; + } + + @Override + public List components() { + List results = new ArrayList<>(); + results.add(VIEW_VIEW); + results.add(VIEW_DATA); + results.add(VIEW_TYPE); + results.add(VIEW_STYLE); + return results; + } + + @Override + public List staticResources() { + List results = new ArrayList<>(); + StaticResource staticResource = new StaticResource(); + staticResource.setName(VIEW_TYPE_VALUE); + staticResource.setSuffix(SUFFIX); + results.add(staticResource); + results.add(pluginSvg()); + return results; + } + + private StaticResource pluginSvg() { + StaticResource staticResource = new StaticResource(); + staticResource.setName("view-sankey-backend"); + staticResource.setSuffix("svg"); + return staticResource; + } + + + @Override + protected InputStream readContent(String s) { + InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("static/" + s); + return resourceAsStream; + } + + @Override + public String generateSQL(PluginViewParam param) { + System.out.println("************* generateSQL **************"); + List xAxis = param.getFieldsByType("xAxis"); + List yAxis = param.getFieldsByType("yAxis"); + if (CollectionUtils.isEmpty(xAxis) || CollectionUtils.isEmpty(yAxis)) { + return null; + } + return super.generateSQL(param); + + } + + @Override + public Map formatResult(PluginViewParam pluginViewParam, List data, Boolean isDrill) { + Map map = super.formatResult(pluginViewParam, data, isDrill); + + map.put("data", map.get("series")); + + return map; + } + +} diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/.babelrc b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/.babelrc new file mode 100644 index 0000000000..3a280ba34b --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/.babelrc @@ -0,0 +1,12 @@ +{ + "presets": [ + ["env", { + "modules": false, + "targets": { + "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] + } + }], + "stage-2" + ], + "plugins": ["transform-vue-jsx", "transform-runtime"] +} diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/.editorconfig b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/.editorconfig new file mode 100644 index 0000000000..9d08a1a828 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/.gitignore b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/.gitignore new file mode 100644 index 0000000000..541a820f6c --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/.gitignore @@ -0,0 +1,14 @@ +.DS_Store +node_modules/ +/dist/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/.postcssrc.js b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/.postcssrc.js new file mode 100644 index 0000000000..eee3e92d7f --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/.postcssrc.js @@ -0,0 +1,10 @@ +// https://github.com/michael-ciniawsky/postcss-load-config + +module.exports = { + "plugins": { + "postcss-import": {}, + "postcss-url": {}, + // to edit target browsers: use "browserslist" field in package.json + "autoprefixer": {} + } +} diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/README.md b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/README.md new file mode 100644 index 0000000000..c1e4be95e0 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/README.md @@ -0,0 +1,21 @@ +# deplugin-view-frontend + +> A Vue.js project + +## Build Setup + +``` bash +# install dependencies +npm install + +# serve with hot reload at localhost:8080 +npm run dev + +# build for production with minification +npm run build + +# build for production and view the bundle analyzer report +npm run build --report +``` + +For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader). diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/build/build-async-plugins.js b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/build/build-async-plugins.js new file mode 100644 index 0000000000..2303854531 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/build/build-async-plugins.js @@ -0,0 +1,35 @@ +'use strict' +require('./check-versions')() + +process.env.NODE_ENV = 'production' + +const ora = require('ora') +const chalk = require('chalk') +const webpack = require('webpack') +const webpackConfig = require('./webpack.async-plugins') + +const spinner = ora('building for sync-plugins...') +spinner.start() + +webpack(webpackConfig, function (err, stats) { + spinner.stop() + if (err) throw err + process.stdout.write(stats.toString({ + colors: true, + modules: false, + children: false, + chunks: false, + chunkModules: false + }) + '\n\n') + + if (stats.hasErrors()) { + console.log(chalk.red(' Build failed with errors.\n')) + process.exit(1) + } + + console.log(chalk.cyan(' Build complete.\n')) + console.log(chalk.yellow( + ' Tip: built files are meant to be served over an HTTP server.\n' + + ' Opening index.html over file:// won\'t work.\n' + )) +}) diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/build/build.js b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/build/build.js new file mode 100644 index 0000000000..8f2ad8ad49 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/build/build.js @@ -0,0 +1,41 @@ +'use strict' +require('./check-versions')() + +process.env.NODE_ENV = 'production' + +const ora = require('ora') +const rm = require('rimraf') +const path = require('path') +const chalk = require('chalk') +const webpack = require('webpack') +const config = require('../config') +const webpackConfig = require('./webpack.prod.conf') + +const spinner = ora('building for production...') +spinner.start() + +rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { + if (err) throw err + webpack(webpackConfig, (err, stats) => { + spinner.stop() + if (err) throw err + process.stdout.write(stats.toString({ + colors: true, + modules: false, + children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build. + chunks: false, + chunkModules: false + }) + '\n\n') + + if (stats.hasErrors()) { + console.log(chalk.red(' Build failed with errors.\n')) + process.exit(1) + } + + console.log(chalk.cyan(' Build complete.\n')) + console.log(chalk.yellow( + ' Tip: built files are meant to be served over an HTTP server.\n' + + ' Opening index.html over file:// won\'t work.\n' + )) + }) +}) diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/build/check-versions.js b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/build/check-versions.js new file mode 100644 index 0000000000..3ef972a08d --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/build/check-versions.js @@ -0,0 +1,54 @@ +'use strict' +const chalk = require('chalk') +const semver = require('semver') +const packageConfig = require('../package.json') +const shell = require('shelljs') + +function exec (cmd) { + return require('child_process').execSync(cmd).toString().trim() +} + +const versionRequirements = [ + { + name: 'node', + currentVersion: semver.clean(process.version), + versionRequirement: packageConfig.engines.node + } +] + +if (shell.which('npm')) { + versionRequirements.push({ + name: 'npm', + currentVersion: exec('npm --version'), + versionRequirement: packageConfig.engines.npm + }) +} + +module.exports = function () { + const warnings = [] + + for (let i = 0; i < versionRequirements.length; i++) { + const mod = versionRequirements[i] + + if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { + warnings.push(mod.name + ': ' + + chalk.red(mod.currentVersion) + ' should be ' + + chalk.green(mod.versionRequirement) + ) + } + } + + if (warnings.length) { + console.log('') + console.log(chalk.yellow('To use this template, you must update following to modules:')) + console.log() + + for (let i = 0; i < warnings.length; i++) { + const warning = warnings[i] + console.log(' ' + warning) + } + + console.log() + process.exit(1) + } +} diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/build/logo.png b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/build/logo.png new file mode 100644 index 0000000000..f3d2503fc2 Binary files /dev/null and b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/build/logo.png differ diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/build/utils.js b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/build/utils.js new file mode 100644 index 0000000000..e534fb0fd6 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/build/utils.js @@ -0,0 +1,101 @@ +'use strict' +const path = require('path') +const config = require('../config') +const ExtractTextPlugin = require('extract-text-webpack-plugin') +const packageConfig = require('../package.json') + +exports.assetsPath = function (_path) { + const assetsSubDirectory = process.env.NODE_ENV === 'production' + ? config.build.assetsSubDirectory + : config.dev.assetsSubDirectory + + return path.posix.join(assetsSubDirectory, _path) +} + +exports.cssLoaders = function (options) { + options = options || {} + + const cssLoader = { + loader: 'css-loader', + options: { + sourceMap: options.sourceMap + } + } + + const postcssLoader = { + loader: 'postcss-loader', + options: { + sourceMap: options.sourceMap + } + } + + // generate loader string to be used with extract text plugin + function generateLoaders (loader, loaderOptions) { + const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader] + + if (loader) { + loaders.push({ + loader: loader + '-loader', + options: Object.assign({}, loaderOptions, { + sourceMap: options.sourceMap + }) + }) + } + + // Extract CSS when that option is specified + // (which is the case during production build) + if (options.extract) { + return ExtractTextPlugin.extract({ + use: loaders, + fallback: 'vue-style-loader' + }) + } else { + return ['vue-style-loader'].concat(loaders) + } + } + + // https://vue-loader.vuejs.org/en/configurations/extract-css.html + return { + css: generateLoaders(), + postcss: generateLoaders(), + less: generateLoaders('less'), + sass: generateLoaders('sass', { indentedSyntax: true }), + scss: generateLoaders('sass'), + stylus: generateLoaders('stylus'), + styl: generateLoaders('stylus') + } +} + +// Generate loaders for standalone style files (outside of .vue) +exports.styleLoaders = function (options) { + const output = [] + const loaders = exports.cssLoaders(options) + + for (const extension in loaders) { + const loader = loaders[extension] + output.push({ + test: new RegExp('\\.' + extension + '$'), + use: loader + }) + } + + return output +} + +exports.createNotifierCallback = () => { + const notifier = require('node-notifier') + + return (severity, errors) => { + if (severity !== 'error') return + + const error = errors[0] + const filename = error.file && error.file.split('!').pop() + + notifier.notify({ + title: packageConfig.name, + message: severity + ': ' + error.name, + subtitle: filename || '', + icon: path.join(__dirname, 'logo.png') + }) + } +} diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/build/vue-loader.conf.js b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/build/vue-loader.conf.js new file mode 100644 index 0000000000..33ed58bc0a --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/build/vue-loader.conf.js @@ -0,0 +1,22 @@ +'use strict' +const utils = require('./utils') +const config = require('../config') +const isProduction = process.env.NODE_ENV === 'production' +const sourceMapEnabled = isProduction + ? config.build.productionSourceMap + : config.dev.cssSourceMap + +module.exports = { + loaders: utils.cssLoaders({ + sourceMap: sourceMapEnabled, + extract: isProduction + }), + cssSourceMap: sourceMapEnabled, + cacheBusting: config.dev.cacheBusting, + transformToRequire: { + video: ['src', 'poster'], + source: 'src', + img: 'src', + image: 'xlink:href' + } +} diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/build/webpack.async-plugins.js b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/build/webpack.async-plugins.js new file mode 100644 index 0000000000..aa7c48ee27 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/build/webpack.async-plugins.js @@ -0,0 +1,97 @@ +const webpack = require('webpack') +const path = require('path') +const utils = require('./utils') +const CopyPlugin = require("copy-webpack-plugin"); +const VueLoaderPlugin = require('vue-loader/lib/plugin'); +function resolve (dir) { + return path.join(__dirname, '..', dir) +} + +module.exports = { + mode: 'development', + entry: { + + // PluginDemo: resolve('/src/views/PluginDemo.vue') + 'sankey-view': resolve('/src/views/antv/sankey/index.vue'), + 'sankey-data': resolve('/src/views/antv/sankey/data.vue'), + 'sankey-type': resolve('/src/views/antv/sankey/type.vue'), + 'sankey-style': resolve('/src/views/antv/sankey/style.vue') + + }, + output: { + path: resolve('/static/'), + filename: '[name].js' + }, + resolve: { + extensions: ['.js', '.vue', '.json'], + alias: { + 'vue$': 'vue/dist/vue.esm.js', + '@': resolve('src') + } + }, + externals: { + vue: 'vue' + }, + module: { + rules: [ + { + test: /\.vue$/, + loader: 'vue-loader', + options: { + transformAssetUrls: { + video: 'src', + source: 'src', + img: 'src', + image: 'xlink:href' + } + } + }, + { + test: /.(sa|sc|c)ss$/, + use: [ + {loader: 'vue-style-loader'}, + 'css-loader', + 'sass-loader' + ] + }, + { + test: /\.js$/, + loader: 'babel-loader', + include: [resolve('src'), resolve('test')] + }, + { + test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('img/[name].[hash:7].[ext]') + } + }, + { + test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('media/[name].[hash:7].[ext]') + } + }, + { + test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('fonts/[name].[hash:7].[ext]') + } + } + ] + }, + plugins: [ + new VueLoaderPlugin(), + new webpack.DefinePlugin({ + 'process.env.NODE_ENV': '"production"' + }), + new CopyPlugin([ + {from: 'src/icons/svg/'} + ]), + ] +} diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/build/webpack.base.conf.js b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/build/webpack.base.conf.js new file mode 100644 index 0000000000..6ccc02dab4 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/build/webpack.base.conf.js @@ -0,0 +1,91 @@ +'use strict' +const path = require('path') +const utils = require('./utils') +const config = require('../config') +const vueLoaderConfig = require('./vue-loader.conf') + +function resolve (dir) { + return path.join(__dirname, '..', dir) +} + + + +module.exports = { + context: path.resolve(__dirname, '../'), + entry: { + app: './src/main.js' + }, + output: { + path: config.build.assetsRoot, + filename: '[name].js', + publicPath: process.env.NODE_ENV === 'production' + ? config.build.assetsPublicPath + : config.dev.assetsPublicPath + }, + resolve: { + extensions: ['.js', '.vue', '.json'], + alias: { + 'vue$': 'vue/dist/vue.esm.js', + '@': resolve('src'), + } + }, + module: { + rules: [ + { + test: /\.vue$/, + loader: 'vue-loader', + options: vueLoaderConfig + }, + { + test: /\.js$/, + loader: 'babel-loader', + include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')] + }, + { + test: /\.svg$/, + loader: 'svg-sprite-loader', + include: [resolve('src/icons')], + options: { + symbolId: 'icon-[name]' + } + }, + { + test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, + loader: 'url-loader', + exclude: [resolve('src/icons')], + options: { + limit: 10000, + name: utils.assetsPath('img/[name].[hash:7].[ext]') + } + }, + { + test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('media/[name].[hash:7].[ext]') + } + }, + { + test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('fonts/[name].[hash:7].[ext]') + } + } + ] + }, + node: { + // prevent webpack from injecting useless setImmediate polyfill because Vue + // source contains it (although only uses it if it's native). + setImmediate: false, + // prevent webpack from injecting mocks to Node native modules + // that does not make sense for the client + dgram: 'empty', + fs: 'empty', + net: 'empty', + tls: 'empty', + child_process: 'empty' + } +} diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/build/webpack.dev.conf.js b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/build/webpack.dev.conf.js new file mode 100755 index 0000000000..070ae221f3 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/build/webpack.dev.conf.js @@ -0,0 +1,95 @@ +'use strict' +const utils = require('./utils') +const webpack = require('webpack') +const config = require('../config') +const merge = require('webpack-merge') +const path = require('path') +const baseWebpackConfig = require('./webpack.base.conf') +const CopyWebpackPlugin = require('copy-webpack-plugin') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') +const portfinder = require('portfinder') + +const HOST = process.env.HOST +const PORT = process.env.PORT && Number(process.env.PORT) + +const devWebpackConfig = merge(baseWebpackConfig, { + module: { + rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) + }, + // cheap-module-eval-source-map is faster for development + devtool: config.dev.devtool, + + // these devServer options should be customized in /config/index.js + devServer: { + clientLogLevel: 'warning', + historyApiFallback: { + rewrites: [ + { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') }, + ], + }, + hot: true, + contentBase: false, // since we use CopyWebpackPlugin. + compress: true, + host: HOST || config.dev.host, + port: PORT || config.dev.port, + open: config.dev.autoOpenBrowser, + overlay: config.dev.errorOverlay + ? { warnings: false, errors: true } + : false, + publicPath: config.dev.assetsPublicPath, + proxy: config.dev.proxyTable, + quiet: true, // necessary for FriendlyErrorsPlugin + watchOptions: { + poll: config.dev.poll, + } + }, + plugins: [ + new webpack.DefinePlugin({ + 'process.env': require('../config/dev.env') + }), + new webpack.HotModuleReplacementPlugin(), + new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update. + new webpack.NoEmitOnErrorsPlugin(), + // https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: 'index.html', + template: 'index.html', + inject: true + }), + // copy custom static assets + new CopyWebpackPlugin([ + { + from: path.resolve(__dirname, '../static'), + to: config.dev.assetsSubDirectory, + ignore: ['.*'] + } + ]) + ] +}) + +module.exports = new Promise((resolve, reject) => { + portfinder.basePort = process.env.PORT || config.dev.port + portfinder.getPort((err, port) => { + if (err) { + reject(err) + } else { + // publish the new Port, necessary for e2e tests + process.env.PORT = port + // add port to devServer config + devWebpackConfig.devServer.port = port + + // Add FriendlyErrorsPlugin + devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({ + compilationSuccessInfo: { + messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`], + }, + onErrors: config.dev.notifyOnErrors + ? utils.createNotifierCallback() + : undefined + })) + + resolve(devWebpackConfig) + } + }) +}) diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/build/webpack.prod.conf.js b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/build/webpack.prod.conf.js new file mode 100644 index 0000000000..1f71ad6454 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/build/webpack.prod.conf.js @@ -0,0 +1,147 @@ +'use strict' +const path = require('path') +const utils = require('./utils') +const webpack = require('webpack') +const config = require('../config') +const merge = require('webpack-merge') +const baseWebpackConfig = require('./webpack.base.conf') +const CopyWebpackPlugin = require('copy-webpack-plugin') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const ExtractTextPlugin = require('extract-text-webpack-plugin') +const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') +const UglifyJsPlugin = require('uglifyjs-webpack-plugin') +const VueLoaderPlugin = require('vue-loader/lib/plugin'); + +const env = require('../config/prod.env') + +const webpackConfig = merge(baseWebpackConfig, { + module: { + rules: utils.styleLoaders({ + sourceMap: config.build.productionSourceMap, + extract: true, + usePostCSS: true + }) + }, + devtool: config.build.productionSourceMap ? config.build.devtool : false, + output: { + path: config.build.assetsRoot, + filename: utils.assetsPath('js/[name].[chunkhash].js'), + chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') + }, + plugins: [ + new VueLoaderPlugin(), + // http://vuejs.github.io/vue-loader/en/workflow/production.html + new webpack.DefinePlugin({ + 'process.env': env + }), + new UglifyJsPlugin({ + uglifyOptions: { + compress: { + warnings: false + } + }, + sourceMap: config.build.productionSourceMap, + parallel: true + }), + // extract css into its own file + new ExtractTextPlugin({ + filename: utils.assetsPath('css/[name].[contenthash].css'), + // Setting the following option to `false` will not extract CSS from codesplit chunks. + // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack. + // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, + // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110 + allChunks: true, + }), + // Compress extracted CSS. We are using this plugin so that possible + // duplicated CSS from different components can be deduped. + new OptimizeCSSPlugin({ + cssProcessorOptions: config.build.productionSourceMap + ? { safe: true, map: { inline: false } } + : { safe: true } + }), + // generate dist index.html with correct asset hash for caching. + // you can customize output by editing /index.html + // see https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: config.build.index, + template: 'index.html', + inject: true, + minify: { + removeComments: true, + collapseWhitespace: true, + removeAttributeQuotes: true + // more options: + // https://github.com/kangax/html-minifier#options-quick-reference + }, + // necessary to consistently work with multiple chunks via CommonsChunkPlugin + chunksSortMode: 'dependency' + }), + // keep module.id stable when vendor modules does not change + new webpack.HashedModuleIdsPlugin(), + // enable scope hoisting + new webpack.optimize.ModuleConcatenationPlugin(), + // split vendor js into its own file + new webpack.optimize.CommonsChunkPlugin({ + name: 'vendor', + minChunks (module) { + // any required modules inside node_modules are extracted to vendor + return ( + module.resource && + /\.js$/.test(module.resource) && + module.resource.indexOf( + path.join(__dirname, '../node_modules') + ) === 0 + ) + } + }), + // extract webpack runtime and module manifest to its own file in order to + // prevent vendor hash from being updated whenever app bundle is updated + new webpack.optimize.CommonsChunkPlugin({ + name: 'manifest', + minChunks: Infinity + }), + // This instance extracts shared chunks from code split chunks and bundles them + // in a separate chunk, similar to the vendor chunk + // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk + new webpack.optimize.CommonsChunkPlugin({ + name: 'app', + async: 'vendor-async', + children: true, + minChunks: 3 + }), + + // copy custom static assets + new CopyWebpackPlugin([ + { + from: path.resolve(__dirname, '../static'), + to: config.build.assetsSubDirectory, + ignore: ['.*'] + } + ]) + ] +}) + +if (config.build.productionGzip) { + const CompressionWebpackPlugin = require('compression-webpack-plugin') + + webpackConfig.plugins.push( + new CompressionWebpackPlugin({ + asset: '[path].gz[query]', + algorithm: 'gzip', + test: new RegExp( + '\\.(' + + config.build.productionGzipExtensions.join('|') + + ')$' + ), + threshold: 10240, + minRatio: 0.8 + }) + ) +} + +if (config.build.bundleAnalyzerReport) { + const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin + webpackConfig.plugins.push(new BundleAnalyzerPlugin()) +} + +module.exports = webpackConfig diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/config/dev.env.js b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/config/dev.env.js new file mode 100644 index 0000000000..1e22973ae7 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/config/dev.env.js @@ -0,0 +1,7 @@ +'use strict' +const merge = require('webpack-merge') +const prodEnv = require('./prod.env') + +module.exports = merge(prodEnv, { + NODE_ENV: '"development"' +}) diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/config/index.js b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/config/index.js new file mode 100644 index 0000000000..c5eded7f81 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/config/index.js @@ -0,0 +1,69 @@ +'use strict' +// Template version: 1.3.1 +// see http://vuejs-templates.github.io/webpack for documentation. + +const path = require('path') + +module.exports = { + dev: { + + // Paths + assetsSubDirectory: 'static', + assetsPublicPath: '/', + proxyTable: {}, + + // Various Dev Server settings + host: 'localhost', // can be overwritten by process.env.HOST + port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined + autoOpenBrowser: false, + errorOverlay: true, + notifyOnErrors: true, + poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- + + + /** + * Source Maps + */ + + // https://webpack.js.org/configuration/devtool/#development + devtool: 'cheap-module-eval-source-map', + + // If you have problems debugging vue-files in devtools, + // set this to false - it *may* help + // https://vue-loader.vuejs.org/en/options.html#cachebusting + cacheBusting: true, + + cssSourceMap: true + }, + + build: { + // Template for index.html + index: path.resolve(__dirname, '../dist/index.html'), + + // Paths + assetsRoot: path.resolve(__dirname, '../dist'), + assetsSubDirectory: 'static', + assetsPublicPath: '/', + + /** + * Source Maps + */ + + productionSourceMap: true, + // https://webpack.js.org/configuration/devtool/#production + devtool: '#source-map', + + // Gzip off by default as many popular static hosts such as + // Surge or Netlify already gzip all static assets for you. + // Before setting to `true`, make sure to: + // npm install --save-dev compression-webpack-plugin + productionGzip: false, + productionGzipExtensions: ['js', 'css'], + + // Run the build command with an extra argument to + // View the bundle analyzer report after build finishes: + // `npm run build --report` + // Set to `true` or `false` to always turn it on or off + bundleAnalyzerReport: process.env.npm_config_report + } +} diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/config/prod.env.js b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/config/prod.env.js new file mode 100644 index 0000000000..a6f997616e --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/config/prod.env.js @@ -0,0 +1,4 @@ +'use strict' +module.exports = { + NODE_ENV: '"production"' +} diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/index.html b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/index.html new file mode 100644 index 0000000000..03ce3fdf27 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/index.html @@ -0,0 +1,12 @@ + + + + + + deplugin-view-frontend + + +
+ + + diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/package.json b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/package.json new file mode 100644 index 0000000000..dd121e3144 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/package.json @@ -0,0 +1,75 @@ +{ + "name": "deplugin-view-frontend", + "version": "1.0.0", + "description": "A Vue.js project", + "author": "", + "private": true, + "scripts": { + "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", + "start": "npm run dev", + "build": "node build/build.js", + "buildPlugin": "node build/build-async-plugins.js" + }, + "dependencies": { + "@antv/g2plot": "^2.4.31", + "@riophae/vue-treeselect": "0.4.0", + "lodash": "^4.17.21", + "vue": "2.6.10", + "vue-i18n": "7.3.2", + "vue-router": "^3.0.1", + "vue-uuid": "2.0.2", + "vuedraggable": "^2.24.3", + "vuex": "3.1.0" + }, + "devDependencies": { + "autoprefixer": "^7.1.2", + "babel-core": "^6.22.1", + "babel-helper-vue-jsx-merge-props": "^2.0.3", + "babel-loader": "^7.1.1", + "babel-plugin-syntax-jsx": "^6.18.0", + "babel-plugin-transform-runtime": "^6.22.0", + "babel-plugin-transform-vue-jsx": "^3.5.0", + "babel-preset-env": "^1.3.2", + "babel-preset-stage-2": "^6.22.0", + "chalk": "^2.0.1", + "copy-webpack-plugin": "^4.6.0", + "css-loader": "^0.28.0", + "element-ui": "2.15.7", + "extract-text-webpack-plugin": "^4.0.0-beta.0", + "file-loader": "^1.1.4", + "friendly-errors-webpack-plugin": "^1.6.1", + "html-webpack-plugin": "^3.2.0", + "js-cookie": "2.2.0", + "node-notifier": "^8.0.1", + "optimize-css-assets-webpack-plugin": "^3.2.0", + "ora": "^1.2.0", + "portfinder": "^1.0.13", + "postcss-import": "^11.0.0", + "postcss-loader": "^2.0.8", + "postcss-url": "^7.2.1", + "rimraf": "^2.6.0", + "sass": "^1.33.0", + "sass-loader": "^7.3.1", + "semver": "^5.3.0", + "shelljs": "^0.8.5", + "svg-sprite-loader": "4.1.3", + "uglifyjs-webpack-plugin": "^1.1.1", + "url-loader": "^0.5.8", + "vue-loader": "^15.6.4", + "vue-style-loader": "^4.1.2", + "vue-template-compiler": "2.6.10", + "webpack": "^4.8.1", + "webpack-bundle-analyzer": "^3.3.2", + "webpack-dev-server": "^3.1.11", + "webpack-merge": "^4.1.0" + }, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + }, + "browserslist": [ + "> 1%", + "last 2 versions", + "not ie <= 8" + ] +} diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/pom.xml b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/pom.xml new file mode 100644 index 0000000000..a44dc1de4b --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/pom.xml @@ -0,0 +1,80 @@ + + + + + io.dataease + view-sankey + ${dataease.version} + + + 4.0.0 + view-sankey-frontend + + + UTF-8 + UTF-8 + 1.9.1 + + + + + + maven-clean-plugin + + + + static + + ** + + false + + + + + + + + com.github.eirslett + frontend-maven-plugin + ${frontend-maven-plugin.version} + + + install node and npm + + install-node-and-npm + + + + v16.20.2 + 7.6.3 + + + + + npm install + + npm + + + + install + + + + + npm run buildPlugin + + npm + + + run buildPlugin + + + + + + + diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/App.vue b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/App.vue new file mode 100644 index 0000000000..8542e07722 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/App.vue @@ -0,0 +1,23 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/assets/logo.png b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/assets/logo.png new file mode 100644 index 0000000000..f3d2503fc2 Binary files /dev/null and b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/assets/logo.png differ diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/HelloWorld.vue b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/HelloWorld.vue new file mode 100644 index 0000000000..1c19f2a399 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/HelloWorld.vue @@ -0,0 +1,113 @@ + + + + + + diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/SvgIcon/index.vue b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/SvgIcon/index.vue new file mode 100644 index 0000000000..f81b67f667 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/SvgIcon/index.vue @@ -0,0 +1,60 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/selector/BackgroundColorSelector.vue b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/selector/BackgroundColorSelector.vue new file mode 100644 index 0000000000..e1cbf5fe18 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/selector/BackgroundColorSelector.vue @@ -0,0 +1,102 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/selector/ColorSelector.vue b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/selector/ColorSelector.vue new file mode 100644 index 0000000000..3a579e8889 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/selector/ColorSelector.vue @@ -0,0 +1,306 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/selector/LabelSelector.vue b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/selector/LabelSelector.vue new file mode 100644 index 0000000000..d05c4bd3a0 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/selector/LabelSelector.vue @@ -0,0 +1,259 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/selector/LegendSelector.vue b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/selector/LegendSelector.vue new file mode 100644 index 0000000000..5125412616 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/selector/LegendSelector.vue @@ -0,0 +1,165 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/selector/SizeSelectorAntV.vue b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/selector/SizeSelectorAntV.vue new file mode 100644 index 0000000000..859824039c --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/selector/SizeSelectorAntV.vue @@ -0,0 +1,130 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/selector/TitleSelector.vue b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/selector/TitleSelector.vue new file mode 100644 index 0000000000..938dd29c36 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/selector/TitleSelector.vue @@ -0,0 +1,269 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/selector/TooltipSelector.vue b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/selector/TooltipSelector.vue new file mode 100644 index 0000000000..c59e0f6185 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/selector/TooltipSelector.vue @@ -0,0 +1,144 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/selector/TooltipSelectorAntV.vue b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/selector/TooltipSelectorAntV.vue new file mode 100644 index 0000000000..a10b229c16 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/selector/TooltipSelectorAntV.vue @@ -0,0 +1,228 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/views/ChartDragItem.vue b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/views/ChartDragItem.vue new file mode 100644 index 0000000000..53ae1e2ad0 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/views/ChartDragItem.vue @@ -0,0 +1,278 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/views/ChartTitleUpdate.vue b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/views/ChartTitleUpdate.vue new file mode 100644 index 0000000000..efadca90bb --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/views/ChartTitleUpdate.vue @@ -0,0 +1,480 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/views/DimensionExtItem.vue b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/views/DimensionExtItem.vue new file mode 100644 index 0000000000..c414ad2d36 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/views/DimensionExtItem.vue @@ -0,0 +1,256 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/views/DrillItem.vue b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/views/DrillItem.vue new file mode 100644 index 0000000000..7321b8a08c --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/views/DrillItem.vue @@ -0,0 +1,146 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/views/FieldErrorTips.vue b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/views/FieldErrorTips.vue new file mode 100644 index 0000000000..9d7b574721 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/views/FieldErrorTips.vue @@ -0,0 +1,21 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/views/FilterItem.vue b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/views/FilterItem.vue new file mode 100644 index 0000000000..3dd5952d69 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/views/FilterItem.vue @@ -0,0 +1,154 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/views/LocationLineItem.vue b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/views/LocationLineItem.vue new file mode 100644 index 0000000000..7b2985d08f --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/views/LocationLineItem.vue @@ -0,0 +1,170 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/views/QuotaExtItem.vue b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/views/QuotaExtItem.vue new file mode 100644 index 0000000000..1e2cb07397 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/views/QuotaExtItem.vue @@ -0,0 +1,363 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/views/SankeyDimensionItem.vue b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/views/SankeyDimensionItem.vue new file mode 100644 index 0000000000..5ee652a9c3 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/views/SankeyDimensionItem.vue @@ -0,0 +1,342 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/views/SankeyQuotaItem.vue b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/views/SankeyQuotaItem.vue new file mode 100644 index 0000000000..aac90dda1a --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/views/SankeyQuotaItem.vue @@ -0,0 +1,333 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/views/ViewTrackBar.vue b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/views/ViewTrackBar.vue new file mode 100644 index 0000000000..a58cad9770 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/views/ViewTrackBar.vue @@ -0,0 +1,80 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/views/utils.js b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/views/utils.js new file mode 100644 index 0000000000..499c79b3d7 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/components/views/utils.js @@ -0,0 +1,86 @@ +export function getItemType(dimensionData, quotaData, item) { + // Check whether the current view is in template status + // ( dimensionData and quotaData have no data). If yes, return 'success' directly + if (dimensionData.length === 0 && quotaData.length === 0) { + return 'success' + } + // 将item的字段在数据集维度、指标字段中查询一遍,如果遇到id不存在、字段类型不一致、维度指标不一致,则提示 + const status = item.groupType + let checked = false + if (status === 'd') { + for (let i = 0; i < dimensionData.length; i++) { + const ele = dimensionData[i] + if (item.chartId) { + if (ele.dataeaseName === item.dataeaseName && ele.deType === item.deType && ele.groupType === item.groupType) { + checked = true + break + } + } else { + if (ele.id === item.id && ele.deType === item.deType && ele.groupType === item.groupType) { + checked = true + break + } + } + } + } + if (status === 'q') { + for (let i = 0; i < quotaData.length; i++) { + const ele = quotaData[i] + if (item.chartId) { + if (ele.dataeaseName === item.dataeaseName && ele.deType === item.deType && ele.groupType === item.groupType) { + checked = true + break + } + } else { + if (ele.id === item.id && ele.deType === item.deType && ele.groupType === item.groupType) { + checked = true + break + } + } + } + } + + if (checked) { + if (status === 'd') { + return '' + } else if (status === 'q') { + return 'success' + } + } else { + return 'danger' + } +} + +export function getRemark(chart) { + const remark = {} + if (chart.customStyle) { + const customStyle = JSON.parse(chart.customStyle) + if (customStyle.text) { + const title = JSON.parse(JSON.stringify(customStyle.text)) + remark.show = title.remarkShow ? title.remarkShow : false + remark.content = title.remark ? title.remark : '' + remark.bgFill = title.remarkBackgroundColor ? title.remarkBackgroundColor : '#ffffffff' + } + } + return remark +} + +export function getOriginFieldName(dimensionList, quotaList, field) { + let originName = '' + for (let i = 0; i < dimensionList.length; i++) { + const item = dimensionList[i] + if (item.id === field.id) { + originName = item.name + break + } + } + for (let i = 0; i < quotaList.length; i++) { + const item = quotaList[i] + if (item.id === field.id) { + originName = item.name + break + } + } + return originName +} + diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/de-base/lang/en.js b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/de-base/lang/en.js new file mode 100644 index 0000000000..f95dd2fd17 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/de-base/lang/en.js @@ -0,0 +1,22 @@ +export default { + plugin_view_sankey: { + source: 'Source', + target: 'Target', + map_type: 'Map Type', + link_line: 'Link Line', + mark_size: 'Weight', + type_title: 'Sankey', + label: 'Area', + angle: 'Symbol', + marker: 'Marker', + pentagon: 'Pentagon', + hexagon: 'Hexagon', + octagon: 'Octagon', + hexagram: 'Hexagram', + border: 'Border', + light: 'Light', + dark: 'Dark', + label_format_tip: 'The field value can be read in the form of {field Name}, the fields in the label and the tips are interchangeable, and the built-in latitude and longitude related fields', + tooltip_format_tip: 'The field value can be read in the form of {field Name}, the fields in the label and the tips are interchangeable, and the built-in latitude and longitude related fields.(the label does not support line breaks)', + } +} diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/de-base/lang/index.js b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/de-base/lang/index.js new file mode 100644 index 0000000000..e50d0478e6 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/de-base/lang/index.js @@ -0,0 +1,49 @@ +import Vue from 'vue' +import VueI18n from 'vue-i18n' +import Cookies from 'js-cookie' +import elementEnLocale from 'element-ui/lib/locale/lang/en' // element-ui lang +import elementZhLocale from 'element-ui/lib/locale/lang/zh-CN'// element-ui lang +import elementTWLocale from 'element-ui/lib/locale/lang/zh-TW'// element-ui lang + +import localMessages from './messages' + + +Vue.use(VueI18n) + +const messages = { + en_US: { + ...localMessages['en_US'], + ...elementEnLocale + }, + zh_CN: { + ...localMessages['zh_CN'], + ...elementZhLocale + }, + zh_TW: { + ...localMessages['zh_TW'], + ...elementTWLocale + } +} +export function getLanguage () { + const chooseLanguage = Cookies.get('language') + if (chooseLanguage) return chooseLanguage + + // if has not choose language + const language = (navigator.language || navigator.browserLanguage).toLowerCase() + const locales = Object.keys(messages) + for (const locale of locales) { + if (language.indexOf(locale) > -1) { + return locale + } + } + return 'zh_CN' +} +const i18n = new VueI18n({ + // set locale + // options: en | zh | es + locale: getLanguage(), + // set locale messages + messages +}) + +export default i18n diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/de-base/lang/messages.js b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/de-base/lang/messages.js new file mode 100644 index 0000000000..844f8c0673 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/de-base/lang/messages.js @@ -0,0 +1,17 @@ +import enLocale from './en' +import zhLocale from './zh' +import twLocale from './tw' + +const messages = { + en_US: { + ...enLocale + }, + zh_CN: { + ...zhLocale + }, + zh_TW: { + ...twLocale + } +} + +export default messages \ No newline at end of file diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/de-base/lang/tw.js b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/de-base/lang/tw.js new file mode 100644 index 0000000000..de6b2135d0 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/de-base/lang/tw.js @@ -0,0 +1,23 @@ +export default { + plugin_view_sankey: { + type_title: '桑基圖', + source: '源', + target: '目標', + map_type: '類型', + link_line: '連線', + mark_size: '權重', + label: '區域', + angle: '符號', + marker: '標記', + pentagon: '五邊形', + hexagon: '六邊形', + + octagon: '八邊形', + hexagram: '六角星', + border: '邊框', + light: '亮色', + dark: '暗色', + label_format_tip: '可以${fieldName}的形式讀取字段值,標籤和提示中的字段互相通用,內置經緯度相關字段', + tooltip_format_tip: '可以${fieldName}的形式讀取字段值,標籤和提示中的字段互相通用,內置經緯度相關字段(標籤不支持換行)', + } +} diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/de-base/lang/zh.js b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/de-base/lang/zh.js new file mode 100644 index 0000000000..3258839384 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/de-base/lang/zh.js @@ -0,0 +1,21 @@ +export default { + plugin_view_sankey: { + type_title: '桑基图', + source: '源', + target: '目标', + map_type: '类型', + link_line: '连线', + mark_size: '权重', + angle: '符号', + marker: '标记', + pentagon: '五角形', + hexagon: '六角形', + octagon: '八角形', + hexagram: '六角星', + border: '边框', + light: '亮色', + dark: '暗色', + label_format_tip: '可以${fieldName}的形式读取字段值,标签和提示中的字段互相通用,内置经纬度相关字段', + tooltip_format_tip: '可以${fieldName}的形式读取字段值,标签和提示中的字段互相通用,内置经纬度相关字段(标签不支持换行)', + } +} diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/icons/index.js b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/icons/index.js new file mode 100644 index 0000000000..2c6b309c96 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/icons/index.js @@ -0,0 +1,9 @@ +import Vue from 'vue' +import SvgIcon from '@/components/SvgIcon'// svg component + +// register globally +Vue.component('svg-icon', SvgIcon) + +const req = require.context('./svg', false, /\.svg$/) +const requireAll = requireContext => requireContext.keys().map(requireContext) +requireAll(req) diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/icons/svg/sankey.svg b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/icons/svg/sankey.svg new file mode 100644 index 0000000000..a07394207c --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/icons/svg/sankey.svg @@ -0,0 +1 @@ + diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/icons/svg/view-sankey-backend.svg b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/icons/svg/view-sankey-backend.svg new file mode 100644 index 0000000000..becfdb6caf --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/icons/svg/view-sankey-backend.svg @@ -0,0 +1 @@ +【icon】插件管理-导出 \ No newline at end of file diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/icons/svgo.yml b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/icons/svgo.yml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/main.js b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/main.js new file mode 100644 index 0000000000..b1d6ebdf5f --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/main.js @@ -0,0 +1,36 @@ +// The Vue build version to load with the `import` command +// (runtime-only or standalone) has been set in webpack.base.conf with an alias. +import Vue from 'vue' +import App from './App' +import router from './router' +import ElementUI from 'element-ui' +import Cookies from 'js-cookie' +import i18n from './de-base/lang' +import draggable from 'vuedraggable' +import Treeselect from '@riophae/vue-treeselect' +import '@riophae/vue-treeselect/dist/vue-treeselect.css' +import '@/icons' // icon +import { GaodeMap } from '@antv/l7-maps' +Vue.config.productionTip = false +Vue.use(ElementUI, { + size: Cookies.get('size') || 'medium', + i18n: (key, value) => i18n.t(key, value) +}) +Vue.component('Treeselect', Treeselect) +Vue.component('draggable', draggable) +Vue.prototype.hasDataPermission = function(pTarget, pSource) { + + if (pSource && pTarget) { + return pSource.indexOf(pTarget) > -1 + } + return false +} +Vue.prototype.$gaodeMap = GaodeMap +/* eslint-disable no-new */ +new Vue({ + el: '#app', + router, + i18n, + components: { App }, + template: '' +}) diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/router/index.js b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/router/index.js new file mode 100644 index 0000000000..30a99f3140 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/router/index.js @@ -0,0 +1,21 @@ +import Vue from 'vue' +import Router from 'vue-router' +import HelloWorld from '@/components/HelloWorld' +import SymbolMap from '@/views/antv/sankey/type' + +Vue.use(Router) + +export default new Router({ + routes: [ + { + path: '/', + name: 'HelloWorld', + component: HelloWorld + }, + { + path: '/sankey', + name: 'sankey', + component: SymbolMap + } + ] +}) diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/utils/clickoutside.js b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/utils/clickoutside.js new file mode 100644 index 0000000000..fb030a98eb --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/utils/clickoutside.js @@ -0,0 +1,68 @@ +const nodeList = []; +const ctx = '@@clickoutsideContext'; + +let startClick; +let seed = 0; + + +function createDocumentHandler(el, binding, vnode) { + return function(mouseup = {}, mousedown = {}) { + if (!vnode || + !vnode.context || + !mouseup.target || + !mousedown.target || + el.contains(mouseup.target) || + el.contains(mousedown.target) || + el === mouseup.target || + (vnode.context.popperElm && + (vnode.context.popperElm.contains(mouseup.target) || + vnode.context.popperElm.contains(mousedown.target)))) return; + + if (binding.expression && + el[ctx].methodName && + vnode.context[el[ctx].methodName]) { + vnode.context[el[ctx].methodName](); + } else { + el[ctx].bindingFn && el[ctx].bindingFn(); + } + }; +} + +/** + * v-clickoutside + * @desc 点击元素外面才会触发的事件 + * @example + * ```vue + *
+ * ``` + */ +export default { + bind(el, binding, vnode) { + nodeList.push(el); + const id = seed++; + el[ctx] = { + id, + documentHandler: createDocumentHandler(el, binding, vnode), + methodName: binding.expression, + bindingFn: binding.value + }; + }, + + update(el, binding, vnode) { + el[ctx].documentHandler = createDocumentHandler(el, binding, vnode); + el[ctx].methodName = binding.expression; + el[ctx].bindingFn = binding.value; + }, + + unbind(el) { + let len = nodeList.length; + + for (let i = 0; i < len; i++) { + if (nodeList[i][ctx].id === el[ctx].id) { + nodeList.splice(i, 1); + break; + } + } + delete el[ctx]; + } +}; diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/utils/compare.js b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/utils/compare.js new file mode 100644 index 0000000000..b06a104a10 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/utils/compare.js @@ -0,0 +1,29 @@ +export const compareItem = { + type: 'none', // year-yoy/month-yoy等 + resultData: 'percent', // 对比差sub,百分比percent等 + field: '', + custom: { + field: '', + calcType: '0', // 0-增长值,1-增长率 + timeType: '0', // 0-固定日期,1-日期区间 + currentTime: '', + compareTime: '', + currentTimeRange: [], + compareTimeRange: [] + } +} + +export const compareYearList = [ + { name: 'year_mom', value: 'year_mom' } +] + +export const compareMonthList = [ + { name: 'month_mom', value: 'month_mom' }, + { name: 'year_yoy', value: 'year_yoy' } +] + +export const compareDayList = [ + { name: 'day_mom', value: 'day_mom' }, + { name: 'month_yoy', value: 'month_yoy' }, + { name: 'year_yoy', value: 'year_yoy' } +] diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/utils/map.js b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/utils/map.js new file mode 100644 index 0000000000..d8fd412e2f --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/utils/map.js @@ -0,0 +1,469 @@ +export const DEFAULT_COLOR_CASE = { + value: 'default', + colors: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'], + alpha: 100, + tableHeaderBgColor: '#e1eaff', + tableItemBgColor: '#ffffff', + tableFontColor: '#000000', + tableStripe: true, + dimensionColor: '#000000', + quotaColor: '#000000', + tableBorderColor: '#cfdaf4' +} +export const DEFAULT_SIZE = { + barDefault: true, + barWidth: 40, + barGap: 0.4, + lineWidth: 2, + lineType: 'solid', + lineSymbol: 'marker', + lineSymbolSize: 4, + lineSmooth: true, + lineArea: false, + pieInnerRadius: 0, + pieOuterRadius: 80, + pieRoseType: 'radius', + pieRoseRadius: 5, + funnelWidth: 80, + radarShape: 'polygon', + radarSize: 80, + tableTitleFontSize: 12, + tableItemFontSize: 12, + tableTitleHeight: 36, + tableItemHeight: 36, + tablePageSize: '20', + tableColumnMode: 'custom', + tableColumnWidth: 100, + tableHeaderAlign: 'left', + tableItemAlign: 'right', + gaugeMin: 0, + gaugeMax: 100, + gaugeStartAngle: 225, + gaugeEndAngle: -45, + dimensionFontSize: 18, + quotaFontSize: 18, + spaceSplit: 10, + dimensionShow: true, + quotaShow: true, + scatterSymbol: 'marker', + scatterSymbolSize: 15, + symbolOpacity: 5, + symbolStrokeWidth: 1, + treemapWidth: 80, + treemapHeight: 80, + liquidMax: 100, + liquidSize: 80, + liquidOutlineBorder: 4, + liquidOutlineDistance: 8, + liquidWaveLength: 128, + liquidWaveCount: 3, + liquidShape: 'circle', + tablePageMode: 'page' +} +export const COLOR_PANEL = [ + '#ff4500', + '#ff8c00', + '#ffd700', + '#90ee90', + '#00ced1', + '#1e90ff', + '#c71585', + '#999999', + '#000000', + '#FFFFFF' +] + +export const DEFAULT_LABEL = { + show: true, + position: 'middle', + color: '#000000', + fontSize: '10', + formatter: '{c}', + gaugeFormatter: '{value}', + labelLine: { + show: true + }, + fields: null, + labelTemplate: '{busiValue}', + initialized: true +} +export const DEFAULT_TOOLTIP = { + show: true, + trigger: 'item', + confine: true, + textStyle: { + fontSize: '10', + color: '#909399' + }, + backgroundColor: '#ffffff', + formatter: '', + tooltipTemplate: '{busiValue}' +} +export const DEFAULT_TITLE_STYLE = { + show: true, + fontSize: '18', + color: '#000000', + hPosition: 'left', + vPosition: 'top', + isItalic: false, + isBolder: true, + remarkShow: false, + remark: '', + remarkBackgroundColor: '#ffffffff', + fontFamily: 'Microsoft YaHei', + letterSpace: '0', + fontShadow: false +} +export const DEFAULT_LEGEND_STYLE = { + show: true, + hPosition: 'center', + vPosition: 'bottom', + orient: 'horizontal', + icon: 'circle', + textStyle: { + color: '#333333', + fontSize: '12' + } +} +export const DEFAULT_BACKGROUND_COLOR = { + color: '#ffffff', + alpha: 100, + borderRadius: 5 +} +export const DEFAULT_BASE_MAP_STYLE = { + baseMapTheme: 'light' +} +export const BASE_MAP = { + title: { + text: '', + textStyle: { + fontWeight: 'normal' + } + }, + visualMap: { + min: 50, + max: 52, + text: ['High', 'Low'], + realtime: false, + calculable: true, + inRange: { + color: ['lightskyblue', 'yellow', 'orangered'] + }, + right: 0 + }, + + tooltip: {}, + geo: [ + { + show: true, + map: 'BUDDLE_MAP_BORDER', + + emphasis: { + disabled: true + }, + itemStyle: { + borderWidth: 2, + borderColor: '#d1d1d1', + borderType: 'solid' + }, + + roam: false + }, + { + show: true, + map: 'BUDDLE_MAP', + label: { + normal: { + show: false + }, + emphasis: { + show: false + } + }, + itemStyle: { + areaColor: '#f3f3f3', + borderType: 'dashed', + borderColor: '#fff' + }, + + roam: false + } + ], + series: [ + { + name: '', + type: 'scatter', + coordinateSystem: 'geo', + data: [] + } + ] +} + +export const compareItem = { + type: 'none', // year-yoy/month-yoy等 + resultData: 'percent', // 对比差sub,百分比percent等 + field: '', + custom: { + field: '', + calcType: '0', // 0-增长值,1-增长率 + timeType: '0', // 0-固定日期,1-日期区间 + currentTime: '', + compareTime: '', + currentTimeRange: [], + compareTimeRange: [] + } +} + +export const formatterItem = { + type: 'auto', // auto,value,percent + unit: 1, // 换算单位 + suffix: '', // 单位后缀 + decimalCount: 2, // 小数位数 + thousandSeparator: true// 千分符 +} + +const convertData = (mapData, chart) => { + let maxVal = 0 + const k = terminalType === 'pc' ? 30 : 15 + const names = chart.data.x + const results = [] + for (let index = 0; index < names.length; index++) { + const name = names[index]; + const item = chart.data.series[0].data[index] + results.push({ + name, + value: (mapData[name] ? mapData[name].concat(item.value) : []), + dimensionList: item.dimensionList, + quotaList: item.quotaList + }) + maxVal = Math.max(maxVal, item.value) + } + const rate = k / maxVal + + return { + value: results, + rate: rate + } +} + +let terminalType = 'pc' + +export function baseMapOption(chart_option, chart, mapData, terminal = 'pc') { + terminalType = terminal + // 处理shape attr + let customAttr = {} + if (chart.customAttr) { + customAttr = JSON.parse(chart.customAttr) + if (customAttr.color) { + chart_option.color = customAttr.color.colors + } + // tooltip + if (customAttr.tooltip) { + const tooltip = JSON.parse(JSON.stringify(customAttr.tooltip)) + const reg = new RegExp('\n', 'g') + const text = tooltip.formatter.replace(reg, '
') + tooltip.formatter = function (params) { + const a = params.seriesName + const b = params.name + const c = params.value ? params.value[2] : '' + return text.replace(new RegExp('{a}', 'g'), a).replace(new RegExp('{b}', 'g'), b).replace(new RegExp('{c}', 'g'), c) + } + chart_option.tooltip = tooltip + } + } + // 处理data + if (chart.data) { + chart_option.title.text = chart.title + if (chart.data.series && chart.data.series.length > 0) { + chart_option.series[0].name = chart.data.series[0].name + // label + if (customAttr.label) { + const text = customAttr.label.formatter + chart_option.series[0].label = customAttr.label + chart_option.series[0].label.formatter = function (params) { + const a = params.seriesName + const b = params.name + const c = params.value ? params.value[2] : '' + return text.replace(new RegExp('{a}', 'g'), a).replace(new RegExp('{b}', 'g'), b).replace(new RegExp('{c}', 'g'), c) + } + chart_option.series[0].labelLine = customAttr.label.labelLine + } + + // visualMap + const valueArr = chart.data.series[0].data + if (valueArr && valueArr.length > 0) { + const values = [] + valueArr.forEach(function (ele) { + values.push(ele.value) + }) + chart_option.visualMap.min = Math.min(...values) + chart_option.visualMap.max = Math.max(...values) + if (chart_option.visualMap.min === chart_option.visualMap.max) { + chart_option.visualMap.min = 0 + } + } else { + chart_option.visualMap.min = 0 + chart_option.visualMap.max = 0 + } + if (chart_option.visualMap.min === 0 && chart_option.visualMap.max === 0) { + chart_option.visualMap.max = 100 + } + // color + if (customAttr.color && customAttr.color.colors) { + chart_option.visualMap.inRange.color = customAttr.color.colors + chart_option.visualMap.inRange.colorAlpha = customAttr.color.alpha / 100 + } + // chart_option.visualMap = null + + const convert = convertData(mapData, chart) + chart_option.series[0].data = convert.value + chart_option.series[0].symbolSize = val => val[2] * convert.rate + + } + } + // console.log(chart_option); + componentStyle(chart_option, chart) + return chart_option +} + +export function componentStyle(chart_option, chart) { + const padding = '8px' + if (chart.customStyle) { + const customStyle = JSON.parse(chart.customStyle) + if (customStyle.text) { + chart_option.title.show = customStyle.text.show + // 水平方向 + if (customStyle.text.hPosition === 'left') { + chart_option.title.left = padding + } else if (customStyle.text.hPosition === 'right') { + chart_option.title.right = padding + } else { + chart_option.title.left = customStyle.text.hPosition + } + // 垂直方向 + if (customStyle.text.vPosition === 'top') { + chart_option.title.top = padding + } else if (customStyle.text.vPosition === 'bottom') { + chart_option.title.bottom = padding + } else { + chart_option.title.top = customStyle.text.vPosition + } + const style = chart_option.title.textStyle ? chart_option.title.textStyle : {} + style.fontSize = customStyle.text.fontSize + style.color = customStyle.text.color + customStyle.text.isItalic ? style.fontStyle = 'italic' : style.fontStyle = 'normal' + customStyle.text.isBolder ? style.fontWeight = 'bold' : style.fontWeight = 'normal' + chart_option.title.textStyle = style + } + if (customStyle.legend && chart_option.legend) { + chart_option.legend.show = customStyle.legend.show + // 水平方向 + if (customStyle.legend.hPosition === 'left') { + chart_option.legend.left = padding + } else if (customStyle.legend.hPosition === 'right') { + chart_option.legend.right = padding + } else { + chart_option.legend.left = customStyle.legend.hPosition + } + // 垂直方向 + if (customStyle.legend.vPosition === 'top') { + chart_option.legend.top = padding + } else if (customStyle.legend.vPosition === 'bottom') { + chart_option.legend.bottom = padding + } else { + chart_option.legend.top = customStyle.legend.vPosition + } + chart_option.legend.orient = customStyle.legend.orient + chart_option.legend.icon = customStyle.legend.icon + chart_option.legend.textStyle = customStyle.legend.textStyle + + } + + if (customStyle.background) { + chart_option.backgroundColor = hexColorToRGBA(customStyle.background.color, customStyle.background.alpha) + } + } +} + +export function hexColorToRGBA(hex, alpha) { + const rgb = [] // 定义rgb数组 + if (/^\#[0-9A-F]{3}$/i.test(hex)) { // 判断传入是否为#三位十六进制数 + let sixHex = '#' + hex.replace(/[0-9A-F]/ig, function (kw) { + sixHex += kw + kw // 把三位16进制数转化为六位 + }) + hex = sixHex // 保存回hex + } + if (/^#[0-9A-F]{6}$/i.test(hex)) { // 判断传入是否为#六位十六进制数 + hex.replace(/[0-9A-F]{2}/ig, function (kw) { + // eslint-disable-next-line no-eval + rgb.push(eval('0x' + kw)) // 十六进制转化为十进制并存如数组 + }) + return `rgba(${rgb.join(',')},${alpha / 100})` // 输出RGB格式颜色 + } else { + return 'rgb(0,0,0)' + } +} + + +export const DEFAULT_YAXIS_EXT_STYLE = { + show: true, + position: 'right', + name: '', + nameTextStyle: { + color: '#333333', + fontSize: 12 + }, + axisLabel: { + show: true, + color: '#333333', + fontSize: '12', + rotate: 0, + formatter: '{value}' + }, + splitLine: { + show: true, + lineStyle: { + color: '#cccccc', + width: 1, + style: 'solid' + } + }, + axisValue: { + auto: true, + min: null, + max: null, + split: null, + splitCount: null + } +} + +export const getDefaultTemplate = (chart, type, feed, showKey) => { + if (!chart || !chart.viewFields || !type) return null; + let viewFields = [] + if (chart.viewFields instanceof Array) { + viewFields = JSON.parse(JSON.stringify(chart.viewFields)) + } else { + viewFields = JSON.parse(chart.viewFields) + } + const separator = feed ? '\n' : ' ' + return viewFields.filter(field => field.busiType && field.busiType === type).map(field => { + const fieldName = field.name + let template = "${" + field.name + "}" + if (showKey) { + template = fieldName + ":${" + field.name + "}" + } + return template + }).join(separator) +} + +export function uuid() { + return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1); +} + +export const reverseColor = colorValue => { + colorValue = '0x' + colorValue.replace(/#/g, '') + const str = '000000' + (0xFFFFFF - colorValue).toString(16) + return '#' + str.substring(str.length - 6, str.length) +} + diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/utils/sankey.js b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/utils/sankey.js new file mode 100644 index 0000000000..697800dd8d --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/utils/sankey.js @@ -0,0 +1,73 @@ +// import {PointLayer, Popup } from '@antv/l7'; + +export const DEFAULT_COLOR_CASE = { + value: 'default', + colors: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'], + alpha: 100, + tableHeaderBgColor: '#e1eaff', + tableItemBgColor: '#ffffff', + tableFontColor: '#000000', + tableStripe: true, + dimensionColor: '#000000', + quotaColor: '#000000', + tableBorderColor: '#cfdaf4' +} + +export const COLOR_PANEL = [ + '#ff4500', + '#ff8c00', + '#ffd700', + '#90ee90', + '#00ced1', + '#1e90ff', + '#c71585', + '#999999', + '#000000', + '#FFFFFF' +] + + +export const DEFAULT_BACKGROUND_COLOR = { + color: '#ffffff', + alpha: 100, + borderRadius: 5 +} + + + + +export function baseSymbolMap(scene, container, chart, action, $pointLayer, $popup) { + let _self = this + + +} + + +export function hexColorToRGBA(hex, alpha) { + const rgb = [] // 定义rgb数组 + if (/^\#[0-9A-F]{3}$/i.test(hex)) { // 判断传入是否为#三位十六进制数 + let sixHex = '#' + hex.replace(/[0-9A-F]/ig, function(kw) { + sixHex += kw + kw // 把三位16进制数转化为六位 + }) + hex = sixHex // 保存回hex + } + if (/^#[0-9A-F]{6}$/i.test(hex)) { // 判断传入是否为#六位十六进制数 + hex.replace(/[0-9A-F]{2}/ig, function(kw) { + // eslint-disable-next-line no-eval + rgb.push(eval('0x' + kw)) // 十六进制转化为十进制并存如数组 + }) + return `rgba(${rgb.join(',')},${alpha / 100})` // 输出RGB格式颜色 + } else { + return 'rgb(0,0,0)' + } +} + + + + + +export function uuid() { + return (((1+Math.random())*0x10000)|0).toString(16).substring(1); +} + diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/utils/validate.js b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/utils/validate.js new file mode 100644 index 0000000000..3e8ffa9448 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/utils/validate.js @@ -0,0 +1,15 @@ +/** + * Created by PanJiaChen on 16/11/18. + */ + +/** + * @param {string} path + * @returns {Boolean} + */ +export function isExternal(path) { + return /^(https?:|mailto:|tel:)/.test(path) || /^(http?:|mailto:|tel:)/.test(path) || path.startsWith('/api/pluginCommon/staticInfo') +} + + + + diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/views/PluginDemo.vue b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/views/PluginDemo.vue new file mode 100644 index 0000000000..1164dfab1d --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/views/PluginDemo.vue @@ -0,0 +1,55 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/views/antv/sankey/data.vue b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/views/antv/sankey/data.vue new file mode 100644 index 0000000000..8c91a0b4ab --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/views/antv/sankey/data.vue @@ -0,0 +1,717 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/views/antv/sankey/index.vue b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/views/antv/sankey/index.vue new file mode 100644 index 0000000000..799b4aa88e --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/views/antv/sankey/index.vue @@ -0,0 +1,527 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/views/antv/sankey/style.vue b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/views/antv/sankey/style.vue new file mode 100644 index 0000000000..34a13fcf87 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/views/antv/sankey/style.vue @@ -0,0 +1,175 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/views/antv/sankey/type.vue b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/views/antv/sankey/type.vue new file mode 100644 index 0000000000..29e668e134 --- /dev/null +++ b/extensions/dataease-extensions-view/view-sankey/view-sankey-frontend/src/views/antv/sankey/type.vue @@ -0,0 +1,63 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-symbolmap/build.sh b/extensions/dataease-extensions-view/view-symbolmap/build.sh new file mode 100644 index 0000000000..b3f74b429f --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/build.sh @@ -0,0 +1,8 @@ +#!/bin/sh +mvn clean package + +cp view-symbolmap-backend/target/view-symbolmap-backend-1.18.3.jar . + +zip -r symbolmap.zip ./view-symbolmap-backend-1.18.3.jar ./plugin.json + +rm -f ./view-symbolmap-backend-1.18.3.jar diff --git a/extensions/dataease-extensions-view/view-symbolmap/plugin.json b/extensions/dataease-extensions-view/view-symbolmap/plugin.json new file mode 100644 index 0000000000..a015116b9f --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/plugin.json @@ -0,0 +1,13 @@ +{ + "name": "符号地图", + "free": 0, + "store": "thirdpart", + "cost": 0, + "category": "view", + "descript": "AntV L7 符号地图插件", + "version": "1.18.3", + "creator": "DATAEASE", + "moduleName": "view-symbolmap-backend", + "require": "1.10.0", + "dsType": "" +} \ No newline at end of file diff --git a/extensions/dataease-extensions-view/view-symbolmap/pom.xml b/extensions/dataease-extensions-view/view-symbolmap/pom.xml new file mode 100644 index 0000000000..51c5253fca --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/pom.xml @@ -0,0 +1,20 @@ + + + + dataease-extensions-view + io.dataease + ${dataease.version} + + 4.0.0 + + view-symbolmap + pom + + view-symbolmap-frontend + view-symbolmap-backend + + + + diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-backend/pom.xml b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-backend/pom.xml new file mode 100644 index 0000000000..7f9f77bce6 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-backend/pom.xml @@ -0,0 +1,109 @@ + + + + view-symbolmap + io.dataease + ${dataease.version} + + 4.0.0 + + view-symbolmap-backend + + + + io.dataease + dataease-plugin-view + + + + + + + src/main/java + + **/*.properties + **/*.xml + + false + + + src/main/resources + + **/* + + false + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 11 + 11 + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.4 + + + **/server/** + **/*.properties + **/Application* + + + + + + maven-clean-plugin + + + + src/main/resources/static + + ** + + false + + + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + + + main-class-placement + generate-resources + + + + + + + + + + + + run + + + + + + + + + + + diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-backend/src/main/java/io/dataease/plugins/view/official/dto/SymbolMapResultDTO.java b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-backend/src/main/java/io/dataease/plugins/view/official/dto/SymbolMapResultDTO.java new file mode 100644 index 0000000000..63a1189dc1 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-backend/src/main/java/io/dataease/plugins/view/official/dto/SymbolMapResultDTO.java @@ -0,0 +1,18 @@ +package io.dataease.plugins.view.official.dto; + +import java.util.Map; + +import io.dataease.plugins.common.dto.chart.AxisChartDataAntVDTO; +import lombok.Data; + +@Data +public class SymbolMapResultDTO extends AxisChartDataAntVDTO { + + private String longitude; + + private String latitude; + + private Object busiValue; + + private Map properties; +} diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-backend/src/main/java/io/dataease/plugins/view/official/handler/SymbolMapRSHandler.java b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-backend/src/main/java/io/dataease/plugins/view/official/handler/SymbolMapRSHandler.java new file mode 100644 index 0000000000..3c83a67450 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-backend/src/main/java/io/dataease/plugins/view/official/handler/SymbolMapRSHandler.java @@ -0,0 +1,142 @@ +package io.dataease.plugins.view.official.handler; + +import io.dataease.plugins.common.dto.chart.AxisChartDataAntVDTO; +import io.dataease.plugins.common.dto.chart.ChartDimensionDTO; +import io.dataease.plugins.common.dto.chart.ChartQuotaDTO; +import io.dataease.plugins.view.entity.*; +import io.dataease.plugins.view.handler.PluginViewRSHandler; +import io.dataease.plugins.view.official.dto.SymbolMapResultDTO; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.annotation.PostConstruct; + +@Component +public class SymbolMapRSHandler implements PluginViewRSHandler { + + private static List trans2Ykeys = new ArrayList(); + + @PostConstruct + public void init() { + trans2Ykeys.add("labelAxis"); + trans2Ykeys.add("tooltipAxis"); + } + + public List getTrans2Ykeys() { + return trans2Ykeys; + } + + @Override + public Map format(PluginViewParam pluginViewParam, List data, boolean isDrill) { + List xAxis = new ArrayList<>(); + List yAxis = new ArrayList<>(); + + pluginViewParam.getPluginViewFields().forEach(pluginViewField -> { + if (StringUtils.equals(pluginViewField.getTypeField(), "xAxis")) { + xAxis.add(pluginViewField); + } + if (StringUtils.equals(pluginViewField.getTypeField(), "yAxis") + || trans2Ykeys.contains(pluginViewField.getTypeField())) { + yAxis.add(pluginViewField); + } + }); + Map map = new HashMap<>(); + List datalist = new ArrayList<>(); + + for (int i1 = 0; i1 < data.size(); i1++) { + String[] row = data.get(i1); + + StringBuilder a = new StringBuilder(); + if (isDrill) { + a.append(row[xAxis.size() - 1]); + } else { + for (int i = 0; i < xAxis.size(); i++) { + if (i == xAxis.size() - 1) { + a.append(row[i]); + } else { + a.append(row[i]).append("\n"); + } + } + } + + if (CollectionUtils.isEmpty(yAxis)) { + for (int i = 0; i < xAxis.size() + yAxis.size(); i++) { + SymbolMapResultDTO axisChartDataDTO = new SymbolMapResultDTO(); + axisChartDataDTO.setField(a.toString()); + axisChartDataDTO.setName(a.toString()); + + List dimensionList = new ArrayList<>(); + List quotaList = new ArrayList<>(); + + for (int j = 0; j < xAxis.size(); j++) { + ChartDimensionDTO chartDimensionDTO = new ChartDimensionDTO(); + chartDimensionDTO.setId(xAxis.get(j).getId()); + chartDimensionDTO.setValue(row[j]); + dimensionList.add(chartDimensionDTO); + } + axisChartDataDTO.setDimensionList(dimensionList); + + int j = i - xAxis.size(); + if (j > -1) { + ChartQuotaDTO chartQuotaDTO = new ChartQuotaDTO(); + chartQuotaDTO.setId(yAxis.get(j).getId()); + quotaList.add(chartQuotaDTO); + axisChartDataDTO.setQuotaList(quotaList); + try { + axisChartDataDTO.setBusiValue(row[i]); + axisChartDataDTO.setValue(StringUtils.isEmpty(row[i]) ? null : new BigDecimal(row[i])); + } catch (Exception e) { + axisChartDataDTO.setValue(new BigDecimal(0)); + } + axisChartDataDTO.setCategory(yAxis.get(j).getName()); + } + axisChartDataDTO.setLongitude(dimensionList.get(0).getValue()); + axisChartDataDTO.setLatitude(dimensionList.get(1).getValue()); + datalist.add(axisChartDataDTO); + } + } else { + SymbolMapResultDTO axisChartDataDTO = new SymbolMapResultDTO(); + axisChartDataDTO.setField(a.toString()); + axisChartDataDTO.setName(a.toString()); + + List dimensionList = new ArrayList<>(); + + for (int j = 0; j < xAxis.size(); j++) { + ChartDimensionDTO chartDimensionDTO = new ChartDimensionDTO(); + chartDimensionDTO.setId(xAxis.get(j).getId()); + chartDimensionDTO.setValue(row[j]); + dimensionList.add(chartDimensionDTO); + } + axisChartDataDTO.setDimensionList(dimensionList); + axisChartDataDTO.setQuotaList(new ArrayList<>()); + axisChartDataDTO.setProperties(new HashMap<>()); + int step = xAxis.size(); + Boolean valueFilled = false; + for (int i = 0; i < yAxis.size(); i++) { + PluginViewField curY = yAxis.get(i); + ChartQuotaDTO chartQuotaDTO = new ChartQuotaDTO(); + chartQuotaDTO.setId(curY.getId()); + axisChartDataDTO.getQuotaList().add(chartQuotaDTO); + axisChartDataDTO.getProperties().put(curY.getName(), row[i + step]); + axisChartDataDTO.setLongitude(dimensionList.get(0).getValue()); + axisChartDataDTO.setLatitude(dimensionList.get(1).getValue()); + if (StringUtils.equals(curY.getTypeField(), "yAxis") && !valueFilled) { + axisChartDataDTO.setCategory(curY.getName()); + axisChartDataDTO.setBusiValue(row[i + step]); + valueFilled = true; + } + } + datalist.add(axisChartDataDTO); + } + } + map.put("data", datalist); + return map; + } +} diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-backend/src/main/java/io/dataease/plugins/view/official/handler/SymbolMapStatHandler.java b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-backend/src/main/java/io/dataease/plugins/view/official/handler/SymbolMapStatHandler.java new file mode 100644 index 0000000000..76f02f9de4 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-backend/src/main/java/io/dataease/plugins/view/official/handler/SymbolMapStatHandler.java @@ -0,0 +1,101 @@ +package io.dataease.plugins.view.official.handler; + +import io.dataease.plugins.common.constants.datasource.SQLConstants; +import io.dataease.plugins.common.util.ConstantsUtil; +import io.dataease.plugins.view.entity.*; +import io.dataease.plugins.view.handler.PluginViewStatHandler; +import io.dataease.plugins.view.service.ViewPluginBaseService; +import io.dataease.plugins.view.service.ViewPluginService; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; +import org.stringtemplate.v4.ST; +import org.stringtemplate.v4.STGroup; +import org.stringtemplate.v4.STGroupFile; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Component +public class SymbolMapStatHandler implements PluginViewStatHandler { + + @Override + public String build(PluginViewParam pluginViewParam, ViewPluginService viewPluginService) { + ViewPluginBaseService baseService = viewPluginService.getBaseService(); + PluginViewSet pluginViewSet = pluginViewParam.getPluginViewSet(); + String dsType = pluginViewSet.getDsType(); + PluginViewSQL tableObj = baseService.getTableObj(pluginViewSet); + + Map> fieldSQLMap = new HashMap<>(); + + for (int i = 0; i < pluginViewParam.getPluginViewFields().size(); i++) { + PluginViewField pluginViewField = pluginViewParam.getPluginViewFields().get(i); + String typeKey = pluginViewField.getTypeField(); + PluginSingleField pluginSingleField = baseService.buildField(dsType, pluginViewField, tableObj, i); + List lists = fieldSQLMap.getOrDefault(typeKey, new ArrayList<>()); + lists.add(pluginSingleField); + fieldSQLMap.put(typeKey, lists); + } + + List xFields = fieldSQLMap.get("xAxis").stream() + .filter(singleField -> ObjectUtils.isNotEmpty(singleField.getField())) + .map(singleField -> singleField.getField()).collect(Collectors.toList()); + List xOrders = fieldSQLMap.get("xAxis").stream() + .filter(singleField -> ObjectUtils.isNotEmpty(singleField.getSort())) + .map(singleField -> singleField.getSort()).collect(Collectors.toList()); + + // 处理视图中字段过滤 + String customWheres = baseService.customWhere(dsType, pluginViewParam.getPluginChartFieldCustomFilters(), + tableObj); + // 处理仪表板字段过滤 + String panelWheres = baseService.panelWhere(dsType, pluginViewParam.getPluginChartExtFilters(), tableObj); + // 构建sql所有参数 + + String permissionWhere = baseService.permissionWhere(dsType, pluginViewParam.getRowPermissionsTree(), tableObj); + List wheres = new ArrayList<>(); + if (customWheres != null) + wheres.add(customWheres); + if (panelWheres != null) + wheres.add(panelWheres); + if (StringUtils.isNotBlank(permissionWhere)) { + wheres.add(permissionWhere); + } + List groups = new ArrayList<>(); + groups.addAll(xFields); + // 外层再次套sql + List orders = new ArrayList<>(); + orders.addAll(xOrders); + + STGroup stg = new STGroupFile(SQLConstants.SQL_TEMPLATE); + + ST st_sql = stg.getInstanceOf("previewSql"); + st_sql.add("isGroup", false); + if (CollectionUtils.isNotEmpty(xFields)) + st_sql.add("groups", xFields); + if (CollectionUtils.isNotEmpty(wheres)) + st_sql.add("filters", wheres); + if (ObjectUtils.isNotEmpty(tableObj)) + st_sql.add("table", tableObj); + String sql = st_sql.render(); + + String brackets = ConstantsUtil.constantsValue(dsType, "BRACKETS"); + String table_alias_prefix = ConstantsUtil.constantsValue(dsType, "TABLE_ALIAS_PREFIX"); + + ST st = stg.getInstanceOf("previewSql"); + st.add("isGroup", false); + PluginViewSQL tableSQL = PluginViewSQL.builder() + .tableName(String.format(brackets, sql)) + .tableAlias(String.format(table_alias_prefix, 1)) + .build(); + if (CollectionUtils.isNotEmpty(orders)) + st.add("orders", orders); + if (ObjectUtils.isNotEmpty(tableSQL)) + st.add("table", tableSQL); + return baseService.sqlLimit(dsType, st.render(), pluginViewParam.getPluginViewLimit()); + } + +} diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-backend/src/main/java/io/dataease/plugins/view/official/impl/SymbolMapService.java b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-backend/src/main/java/io/dataease/plugins/view/official/impl/SymbolMapService.java new file mode 100644 index 0000000000..a4f5f46d5d --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-backend/src/main/java/io/dataease/plugins/view/official/impl/SymbolMapService.java @@ -0,0 +1,161 @@ +package io.dataease.plugins.view.official.impl; + +import io.dataease.plugins.common.dto.StaticResource; + +import io.dataease.plugins.view.entity.*; +import io.dataease.plugins.view.official.handler.SymbolMapRSHandler; +import io.dataease.plugins.view.official.handler.SymbolMapStatHandler; +import io.dataease.plugins.view.service.ViewPluginService; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; + +import com.google.common.reflect.TypeToken; +import com.google.gson.Gson; + +import java.io.InputStream; +import java.lang.reflect.Type; +import java.util.*; + +@Service +public class SymbolMapService extends ViewPluginService { + + private static Gson gson = new Gson(); + + @Resource + private SymbolMapStatHandler symbolMapStatHandler; + + @Resource + private SymbolMapRSHandler symbolMapRSHandler; + + private static final String VIEW_TYPE_VALUE = "symbol-map"; + + private static final String[] VIEW_STYLE_PROPERTIES = { + "color-selector", + "size-selector-ant-v", + "tooltip-selector-ant-v", + "title-selector-ant-v" + }; + + private static final Map VIEW_STYLE_PROPERTY_INNER = new HashMap(); + + static { + VIEW_STYLE_PROPERTY_INNER.put("color-selector", new String[] { "value", "alpha" }); + VIEW_STYLE_PROPERTY_INNER.put("size-selector-ant-v", + new String[] { "scatterSymbolSize", "symbolOpacity", "symbolStrokeWidth" }); + // VIEW_STYLE_PROPERTY_INNER.put("size-selector-ant-v", new + // String[]{"scatterSymbol", + // "scatterSymbolSize","symbolOpacity","symbolStrokeWidth"}); + VIEW_STYLE_PROPERTY_INNER.put("tooltip-selector-ant-v", new String[] { "show", "textStyle", "formatter" }); + VIEW_STYLE_PROPERTY_INNER.put("title-selector-ant-v", new String[] { "show", "title", "fontSize", "color", + "hPosition", "vPosition", "isItalic", "isBolder" }); + } + + /* 下版这些常量移到sdk */ + private static final String TYPE = "-type"; + private static final String DATA = "-data"; + private static final String STYLE = "-style"; + private static final String VIEW = "-view"; + private static final String SUFFIX = "svg"; + /* 下版这些常量移到sdk */ + private static final String VIEW_TYPE = VIEW_TYPE_VALUE + TYPE; + private static final String VIEW_DATA = VIEW_TYPE_VALUE + DATA; + private static final String VIEW_STYLE = VIEW_TYPE_VALUE + STYLE; + private static final String VIEW_VIEW = VIEW_TYPE_VALUE + VIEW; + + @Override + public PluginViewType viewType() { + PluginViewType pluginViewType = new PluginViewType(); + pluginViewType.setRender("antv"); + pluginViewType.setCategory("chart.chart_type_space"); + pluginViewType.setValue(VIEW_TYPE_VALUE); + pluginViewType.setProperties(VIEW_STYLE_PROPERTIES); + pluginViewType.setPropertyInner(VIEW_STYLE_PROPERTY_INNER); + return pluginViewType; + } + + @Override + public Object format(Object o) { + return null; + } + + @Override + public List components() { + List results = new ArrayList<>(); + results.add(VIEW_VIEW); + results.add(VIEW_DATA); + results.add(VIEW_TYPE); + results.add(VIEW_STYLE); + return results; + } + + @Override + public List staticResources() { + List results = new ArrayList<>(); + StaticResource staticResource = new StaticResource(); + staticResource.setName(VIEW_TYPE_VALUE); + staticResource.setSuffix(SUFFIX); + results.add(staticResource); + addImage(results); + results.add(pluginSvg()); + return results; + } + + private StaticResource pluginSvg() { + StaticResource staticResource = new StaticResource(); + staticResource.setName("view-symbolmap-backend"); + staticResource.setSuffix("svg"); + return staticResource; + } + + private void addImage(List results) { + StaticResource staticResource = new StaticResource(); + staticResource.setName("map-marker"); + staticResource.setSuffix(SUFFIX); + results.add(staticResource); + } + + @Override + protected InputStream readContent(String s) { + InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("static/" + s); + return resourceAsStream; + } + + @Override + public String generateSQL(PluginViewParam pluginViewParam) { + Type tokenType = new TypeToken>() { + }.getType(); + + List pluginViewFields = gson.fromJson(gson.toJson(pluginViewParam.getPluginViewFields()), + tokenType); + List trans2Ykeys = symbolMapRSHandler.getTrans2Ykeys(); + pluginViewParam.getPluginViewFields().forEach(field -> { + if (trans2Ykeys.contains(field.getTypeField())) { + field.setTypeField("yAxis"); + } + }); + List xAxis = pluginViewParam.getFieldsByType("xAxis"); + List yAxis = pluginViewParam.getFieldsByType("yAxis"); + if (CollectionUtils.isEmpty(xAxis) || xAxis.size() < 2) { + return null; + } + + if (CollectionUtils.isNotEmpty(yAxis)) { + String generateSQL = super.generateSQL(pluginViewParam); + pluginViewParam.setPluginViewFields(pluginViewFields); + return generateSQL; + } + + // 下面考虑符号大小为空的情况 + String result = symbolMapStatHandler.build(pluginViewParam, this); + // pluginViewParam.setPluginViewFields(pluginViewFields); + return result; + + } + + @Override + public Map formatResult(PluginViewParam pluginViewParam, List data, Boolean isDrill) { + return symbolMapRSHandler.format(pluginViewParam, data, isDrill); + } +} diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/.babelrc b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/.babelrc new file mode 100644 index 0000000000..3a280ba34b --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/.babelrc @@ -0,0 +1,12 @@ +{ + "presets": [ + ["env", { + "modules": false, + "targets": { + "browsers": ["> 1%", "last 2 versions", "not ie <= 8"] + } + }], + "stage-2" + ], + "plugins": ["transform-vue-jsx", "transform-runtime"] +} diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/.editorconfig b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/.editorconfig new file mode 100644 index 0000000000..9d08a1a828 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/.gitignore b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/.gitignore new file mode 100644 index 0000000000..541a820f6c --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/.gitignore @@ -0,0 +1,14 @@ +.DS_Store +node_modules/ +/dist/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/.postcssrc.js b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/.postcssrc.js new file mode 100644 index 0000000000..eee3e92d7f --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/.postcssrc.js @@ -0,0 +1,10 @@ +// https://github.com/michael-ciniawsky/postcss-load-config + +module.exports = { + "plugins": { + "postcss-import": {}, + "postcss-url": {}, + // to edit target browsers: use "browserslist" field in package.json + "autoprefixer": {} + } +} diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/README.md b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/README.md new file mode 100644 index 0000000000..c1e4be95e0 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/README.md @@ -0,0 +1,21 @@ +# deplugin-view-frontend + +> A Vue.js project + +## Build Setup + +``` bash +# install dependencies +npm install + +# serve with hot reload at localhost:8080 +npm run dev + +# build for production with minification +npm run build + +# build for production and view the bundle analyzer report +npm run build --report +``` + +For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader). diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/build/build-async-plugins.js b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/build/build-async-plugins.js new file mode 100644 index 0000000000..2303854531 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/build/build-async-plugins.js @@ -0,0 +1,35 @@ +'use strict' +require('./check-versions')() + +process.env.NODE_ENV = 'production' + +const ora = require('ora') +const chalk = require('chalk') +const webpack = require('webpack') +const webpackConfig = require('./webpack.async-plugins') + +const spinner = ora('building for sync-plugins...') +spinner.start() + +webpack(webpackConfig, function (err, stats) { + spinner.stop() + if (err) throw err + process.stdout.write(stats.toString({ + colors: true, + modules: false, + children: false, + chunks: false, + chunkModules: false + }) + '\n\n') + + if (stats.hasErrors()) { + console.log(chalk.red(' Build failed with errors.\n')) + process.exit(1) + } + + console.log(chalk.cyan(' Build complete.\n')) + console.log(chalk.yellow( + ' Tip: built files are meant to be served over an HTTP server.\n' + + ' Opening index.html over file:// won\'t work.\n' + )) +}) diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/build/build.js b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/build/build.js new file mode 100644 index 0000000000..8f2ad8ad49 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/build/build.js @@ -0,0 +1,41 @@ +'use strict' +require('./check-versions')() + +process.env.NODE_ENV = 'production' + +const ora = require('ora') +const rm = require('rimraf') +const path = require('path') +const chalk = require('chalk') +const webpack = require('webpack') +const config = require('../config') +const webpackConfig = require('./webpack.prod.conf') + +const spinner = ora('building for production...') +spinner.start() + +rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => { + if (err) throw err + webpack(webpackConfig, (err, stats) => { + spinner.stop() + if (err) throw err + process.stdout.write(stats.toString({ + colors: true, + modules: false, + children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build. + chunks: false, + chunkModules: false + }) + '\n\n') + + if (stats.hasErrors()) { + console.log(chalk.red(' Build failed with errors.\n')) + process.exit(1) + } + + console.log(chalk.cyan(' Build complete.\n')) + console.log(chalk.yellow( + ' Tip: built files are meant to be served over an HTTP server.\n' + + ' Opening index.html over file:// won\'t work.\n' + )) + }) +}) diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/build/check-versions.js b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/build/check-versions.js new file mode 100644 index 0000000000..3ef972a08d --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/build/check-versions.js @@ -0,0 +1,54 @@ +'use strict' +const chalk = require('chalk') +const semver = require('semver') +const packageConfig = require('../package.json') +const shell = require('shelljs') + +function exec (cmd) { + return require('child_process').execSync(cmd).toString().trim() +} + +const versionRequirements = [ + { + name: 'node', + currentVersion: semver.clean(process.version), + versionRequirement: packageConfig.engines.node + } +] + +if (shell.which('npm')) { + versionRequirements.push({ + name: 'npm', + currentVersion: exec('npm --version'), + versionRequirement: packageConfig.engines.npm + }) +} + +module.exports = function () { + const warnings = [] + + for (let i = 0; i < versionRequirements.length; i++) { + const mod = versionRequirements[i] + + if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { + warnings.push(mod.name + ': ' + + chalk.red(mod.currentVersion) + ' should be ' + + chalk.green(mod.versionRequirement) + ) + } + } + + if (warnings.length) { + console.log('') + console.log(chalk.yellow('To use this template, you must update following to modules:')) + console.log() + + for (let i = 0; i < warnings.length; i++) { + const warning = warnings[i] + console.log(' ' + warning) + } + + console.log() + process.exit(1) + } +} diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/build/logo.png b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/build/logo.png new file mode 100644 index 0000000000..f3d2503fc2 Binary files /dev/null and b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/build/logo.png differ diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/build/utils.js b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/build/utils.js new file mode 100644 index 0000000000..e534fb0fd6 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/build/utils.js @@ -0,0 +1,101 @@ +'use strict' +const path = require('path') +const config = require('../config') +const ExtractTextPlugin = require('extract-text-webpack-plugin') +const packageConfig = require('../package.json') + +exports.assetsPath = function (_path) { + const assetsSubDirectory = process.env.NODE_ENV === 'production' + ? config.build.assetsSubDirectory + : config.dev.assetsSubDirectory + + return path.posix.join(assetsSubDirectory, _path) +} + +exports.cssLoaders = function (options) { + options = options || {} + + const cssLoader = { + loader: 'css-loader', + options: { + sourceMap: options.sourceMap + } + } + + const postcssLoader = { + loader: 'postcss-loader', + options: { + sourceMap: options.sourceMap + } + } + + // generate loader string to be used with extract text plugin + function generateLoaders (loader, loaderOptions) { + const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader] + + if (loader) { + loaders.push({ + loader: loader + '-loader', + options: Object.assign({}, loaderOptions, { + sourceMap: options.sourceMap + }) + }) + } + + // Extract CSS when that option is specified + // (which is the case during production build) + if (options.extract) { + return ExtractTextPlugin.extract({ + use: loaders, + fallback: 'vue-style-loader' + }) + } else { + return ['vue-style-loader'].concat(loaders) + } + } + + // https://vue-loader.vuejs.org/en/configurations/extract-css.html + return { + css: generateLoaders(), + postcss: generateLoaders(), + less: generateLoaders('less'), + sass: generateLoaders('sass', { indentedSyntax: true }), + scss: generateLoaders('sass'), + stylus: generateLoaders('stylus'), + styl: generateLoaders('stylus') + } +} + +// Generate loaders for standalone style files (outside of .vue) +exports.styleLoaders = function (options) { + const output = [] + const loaders = exports.cssLoaders(options) + + for (const extension in loaders) { + const loader = loaders[extension] + output.push({ + test: new RegExp('\\.' + extension + '$'), + use: loader + }) + } + + return output +} + +exports.createNotifierCallback = () => { + const notifier = require('node-notifier') + + return (severity, errors) => { + if (severity !== 'error') return + + const error = errors[0] + const filename = error.file && error.file.split('!').pop() + + notifier.notify({ + title: packageConfig.name, + message: severity + ': ' + error.name, + subtitle: filename || '', + icon: path.join(__dirname, 'logo.png') + }) + } +} diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/build/vue-loader.conf.js b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/build/vue-loader.conf.js new file mode 100644 index 0000000000..33ed58bc0a --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/build/vue-loader.conf.js @@ -0,0 +1,22 @@ +'use strict' +const utils = require('./utils') +const config = require('../config') +const isProduction = process.env.NODE_ENV === 'production' +const sourceMapEnabled = isProduction + ? config.build.productionSourceMap + : config.dev.cssSourceMap + +module.exports = { + loaders: utils.cssLoaders({ + sourceMap: sourceMapEnabled, + extract: isProduction + }), + cssSourceMap: sourceMapEnabled, + cacheBusting: config.dev.cacheBusting, + transformToRequire: { + video: ['src', 'poster'], + source: 'src', + img: 'src', + image: 'xlink:href' + } +} diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/build/webpack.async-plugins.js b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/build/webpack.async-plugins.js new file mode 100644 index 0000000000..add7b2a25a --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/build/webpack.async-plugins.js @@ -0,0 +1,97 @@ +const webpack = require('webpack') +const path = require('path') +const utils = require('./utils') +const CopyPlugin = require("copy-webpack-plugin"); +const VueLoaderPlugin = require('vue-loader/lib/plugin'); +function resolve (dir) { + return path.join(__dirname, '..', dir) +} + +module.exports = { + mode: 'development', + entry: { + + // PluginDemo: resolve('/src/views/PluginDemo.vue') + 'symbol-map-view': resolve('/src/views/antv/symbolmap/index.vue'), + 'symbol-map-data': resolve('/src/views/antv/symbolmap/data.vue'), + 'symbol-map-type': resolve('/src/views/antv/symbolmap/type.vue'), + 'symbol-map-style': resolve('/src/views/antv/symbolmap/style.vue') + + }, + output: { + path: resolve('/static/'), + filename: '[name].js' + }, + resolve: { + extensions: ['.js', '.vue', '.json'], + alias: { + 'vue$': 'vue/dist/vue.esm.js', + '@': resolve('src') + } + }, + externals: { + vue: 'vue' + }, + module: { + rules: [ + { + test: /\.vue$/, + loader: 'vue-loader', + options: { + transformAssetUrls: { + video: 'src', + source: 'src', + img: 'src', + image: 'xlink:href' + } + } + }, + { + test: /.(sa|sc|c)ss$/, + use: [ + {loader: 'vue-style-loader'}, + 'css-loader', + 'sass-loader' + ] + }, + { + test: /\.js$/, + loader: 'babel-loader', + include: [resolve('src'), resolve('test')] + }, + { + test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('img/[name].[hash:7].[ext]') + } + }, + { + test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('media/[name].[hash:7].[ext]') + } + }, + { + test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('fonts/[name].[hash:7].[ext]') + } + } + ] + }, + plugins: [ + new VueLoaderPlugin(), + new webpack.DefinePlugin({ + 'process.env.NODE_ENV': '"production"' + }), + new CopyPlugin([ + {from: 'src/icons/svg/'} + ]), + ] +} diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/build/webpack.base.conf.js b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/build/webpack.base.conf.js new file mode 100644 index 0000000000..6ccc02dab4 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/build/webpack.base.conf.js @@ -0,0 +1,91 @@ +'use strict' +const path = require('path') +const utils = require('./utils') +const config = require('../config') +const vueLoaderConfig = require('./vue-loader.conf') + +function resolve (dir) { + return path.join(__dirname, '..', dir) +} + + + +module.exports = { + context: path.resolve(__dirname, '../'), + entry: { + app: './src/main.js' + }, + output: { + path: config.build.assetsRoot, + filename: '[name].js', + publicPath: process.env.NODE_ENV === 'production' + ? config.build.assetsPublicPath + : config.dev.assetsPublicPath + }, + resolve: { + extensions: ['.js', '.vue', '.json'], + alias: { + 'vue$': 'vue/dist/vue.esm.js', + '@': resolve('src'), + } + }, + module: { + rules: [ + { + test: /\.vue$/, + loader: 'vue-loader', + options: vueLoaderConfig + }, + { + test: /\.js$/, + loader: 'babel-loader', + include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')] + }, + { + test: /\.svg$/, + loader: 'svg-sprite-loader', + include: [resolve('src/icons')], + options: { + symbolId: 'icon-[name]' + } + }, + { + test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, + loader: 'url-loader', + exclude: [resolve('src/icons')], + options: { + limit: 10000, + name: utils.assetsPath('img/[name].[hash:7].[ext]') + } + }, + { + test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('media/[name].[hash:7].[ext]') + } + }, + { + test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, + loader: 'url-loader', + options: { + limit: 10000, + name: utils.assetsPath('fonts/[name].[hash:7].[ext]') + } + } + ] + }, + node: { + // prevent webpack from injecting useless setImmediate polyfill because Vue + // source contains it (although only uses it if it's native). + setImmediate: false, + // prevent webpack from injecting mocks to Node native modules + // that does not make sense for the client + dgram: 'empty', + fs: 'empty', + net: 'empty', + tls: 'empty', + child_process: 'empty' + } +} diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/build/webpack.dev.conf.js b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/build/webpack.dev.conf.js new file mode 100755 index 0000000000..070ae221f3 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/build/webpack.dev.conf.js @@ -0,0 +1,95 @@ +'use strict' +const utils = require('./utils') +const webpack = require('webpack') +const config = require('../config') +const merge = require('webpack-merge') +const path = require('path') +const baseWebpackConfig = require('./webpack.base.conf') +const CopyWebpackPlugin = require('copy-webpack-plugin') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin') +const portfinder = require('portfinder') + +const HOST = process.env.HOST +const PORT = process.env.PORT && Number(process.env.PORT) + +const devWebpackConfig = merge(baseWebpackConfig, { + module: { + rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true }) + }, + // cheap-module-eval-source-map is faster for development + devtool: config.dev.devtool, + + // these devServer options should be customized in /config/index.js + devServer: { + clientLogLevel: 'warning', + historyApiFallback: { + rewrites: [ + { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') }, + ], + }, + hot: true, + contentBase: false, // since we use CopyWebpackPlugin. + compress: true, + host: HOST || config.dev.host, + port: PORT || config.dev.port, + open: config.dev.autoOpenBrowser, + overlay: config.dev.errorOverlay + ? { warnings: false, errors: true } + : false, + publicPath: config.dev.assetsPublicPath, + proxy: config.dev.proxyTable, + quiet: true, // necessary for FriendlyErrorsPlugin + watchOptions: { + poll: config.dev.poll, + } + }, + plugins: [ + new webpack.DefinePlugin({ + 'process.env': require('../config/dev.env') + }), + new webpack.HotModuleReplacementPlugin(), + new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update. + new webpack.NoEmitOnErrorsPlugin(), + // https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: 'index.html', + template: 'index.html', + inject: true + }), + // copy custom static assets + new CopyWebpackPlugin([ + { + from: path.resolve(__dirname, '../static'), + to: config.dev.assetsSubDirectory, + ignore: ['.*'] + } + ]) + ] +}) + +module.exports = new Promise((resolve, reject) => { + portfinder.basePort = process.env.PORT || config.dev.port + portfinder.getPort((err, port) => { + if (err) { + reject(err) + } else { + // publish the new Port, necessary for e2e tests + process.env.PORT = port + // add port to devServer config + devWebpackConfig.devServer.port = port + + // Add FriendlyErrorsPlugin + devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({ + compilationSuccessInfo: { + messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`], + }, + onErrors: config.dev.notifyOnErrors + ? utils.createNotifierCallback() + : undefined + })) + + resolve(devWebpackConfig) + } + }) +}) diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/build/webpack.prod.conf.js b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/build/webpack.prod.conf.js new file mode 100644 index 0000000000..1f71ad6454 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/build/webpack.prod.conf.js @@ -0,0 +1,147 @@ +'use strict' +const path = require('path') +const utils = require('./utils') +const webpack = require('webpack') +const config = require('../config') +const merge = require('webpack-merge') +const baseWebpackConfig = require('./webpack.base.conf') +const CopyWebpackPlugin = require('copy-webpack-plugin') +const HtmlWebpackPlugin = require('html-webpack-plugin') +const ExtractTextPlugin = require('extract-text-webpack-plugin') +const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin') +const UglifyJsPlugin = require('uglifyjs-webpack-plugin') +const VueLoaderPlugin = require('vue-loader/lib/plugin'); + +const env = require('../config/prod.env') + +const webpackConfig = merge(baseWebpackConfig, { + module: { + rules: utils.styleLoaders({ + sourceMap: config.build.productionSourceMap, + extract: true, + usePostCSS: true + }) + }, + devtool: config.build.productionSourceMap ? config.build.devtool : false, + output: { + path: config.build.assetsRoot, + filename: utils.assetsPath('js/[name].[chunkhash].js'), + chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') + }, + plugins: [ + new VueLoaderPlugin(), + // http://vuejs.github.io/vue-loader/en/workflow/production.html + new webpack.DefinePlugin({ + 'process.env': env + }), + new UglifyJsPlugin({ + uglifyOptions: { + compress: { + warnings: false + } + }, + sourceMap: config.build.productionSourceMap, + parallel: true + }), + // extract css into its own file + new ExtractTextPlugin({ + filename: utils.assetsPath('css/[name].[contenthash].css'), + // Setting the following option to `false` will not extract CSS from codesplit chunks. + // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack. + // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, + // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110 + allChunks: true, + }), + // Compress extracted CSS. We are using this plugin so that possible + // duplicated CSS from different components can be deduped. + new OptimizeCSSPlugin({ + cssProcessorOptions: config.build.productionSourceMap + ? { safe: true, map: { inline: false } } + : { safe: true } + }), + // generate dist index.html with correct asset hash for caching. + // you can customize output by editing /index.html + // see https://github.com/ampedandwired/html-webpack-plugin + new HtmlWebpackPlugin({ + filename: config.build.index, + template: 'index.html', + inject: true, + minify: { + removeComments: true, + collapseWhitespace: true, + removeAttributeQuotes: true + // more options: + // https://github.com/kangax/html-minifier#options-quick-reference + }, + // necessary to consistently work with multiple chunks via CommonsChunkPlugin + chunksSortMode: 'dependency' + }), + // keep module.id stable when vendor modules does not change + new webpack.HashedModuleIdsPlugin(), + // enable scope hoisting + new webpack.optimize.ModuleConcatenationPlugin(), + // split vendor js into its own file + new webpack.optimize.CommonsChunkPlugin({ + name: 'vendor', + minChunks (module) { + // any required modules inside node_modules are extracted to vendor + return ( + module.resource && + /\.js$/.test(module.resource) && + module.resource.indexOf( + path.join(__dirname, '../node_modules') + ) === 0 + ) + } + }), + // extract webpack runtime and module manifest to its own file in order to + // prevent vendor hash from being updated whenever app bundle is updated + new webpack.optimize.CommonsChunkPlugin({ + name: 'manifest', + minChunks: Infinity + }), + // This instance extracts shared chunks from code split chunks and bundles them + // in a separate chunk, similar to the vendor chunk + // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk + new webpack.optimize.CommonsChunkPlugin({ + name: 'app', + async: 'vendor-async', + children: true, + minChunks: 3 + }), + + // copy custom static assets + new CopyWebpackPlugin([ + { + from: path.resolve(__dirname, '../static'), + to: config.build.assetsSubDirectory, + ignore: ['.*'] + } + ]) + ] +}) + +if (config.build.productionGzip) { + const CompressionWebpackPlugin = require('compression-webpack-plugin') + + webpackConfig.plugins.push( + new CompressionWebpackPlugin({ + asset: '[path].gz[query]', + algorithm: 'gzip', + test: new RegExp( + '\\.(' + + config.build.productionGzipExtensions.join('|') + + ')$' + ), + threshold: 10240, + minRatio: 0.8 + }) + ) +} + +if (config.build.bundleAnalyzerReport) { + const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin + webpackConfig.plugins.push(new BundleAnalyzerPlugin()) +} + +module.exports = webpackConfig diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/config/dev.env.js b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/config/dev.env.js new file mode 100644 index 0000000000..1e22973ae7 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/config/dev.env.js @@ -0,0 +1,7 @@ +'use strict' +const merge = require('webpack-merge') +const prodEnv = require('./prod.env') + +module.exports = merge(prodEnv, { + NODE_ENV: '"development"' +}) diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/config/index.js b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/config/index.js new file mode 100644 index 0000000000..c5eded7f81 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/config/index.js @@ -0,0 +1,69 @@ +'use strict' +// Template version: 1.3.1 +// see http://vuejs-templates.github.io/webpack for documentation. + +const path = require('path') + +module.exports = { + dev: { + + // Paths + assetsSubDirectory: 'static', + assetsPublicPath: '/', + proxyTable: {}, + + // Various Dev Server settings + host: 'localhost', // can be overwritten by process.env.HOST + port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined + autoOpenBrowser: false, + errorOverlay: true, + notifyOnErrors: true, + poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions- + + + /** + * Source Maps + */ + + // https://webpack.js.org/configuration/devtool/#development + devtool: 'cheap-module-eval-source-map', + + // If you have problems debugging vue-files in devtools, + // set this to false - it *may* help + // https://vue-loader.vuejs.org/en/options.html#cachebusting + cacheBusting: true, + + cssSourceMap: true + }, + + build: { + // Template for index.html + index: path.resolve(__dirname, '../dist/index.html'), + + // Paths + assetsRoot: path.resolve(__dirname, '../dist'), + assetsSubDirectory: 'static', + assetsPublicPath: '/', + + /** + * Source Maps + */ + + productionSourceMap: true, + // https://webpack.js.org/configuration/devtool/#production + devtool: '#source-map', + + // Gzip off by default as many popular static hosts such as + // Surge or Netlify already gzip all static assets for you. + // Before setting to `true`, make sure to: + // npm install --save-dev compression-webpack-plugin + productionGzip: false, + productionGzipExtensions: ['js', 'css'], + + // Run the build command with an extra argument to + // View the bundle analyzer report after build finishes: + // `npm run build --report` + // Set to `true` or `false` to always turn it on or off + bundleAnalyzerReport: process.env.npm_config_report + } +} diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/config/prod.env.js b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/config/prod.env.js new file mode 100644 index 0000000000..a6f997616e --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/config/prod.env.js @@ -0,0 +1,4 @@ +'use strict' +module.exports = { + NODE_ENV: '"production"' +} diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/index.html b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/index.html new file mode 100644 index 0000000000..03ce3fdf27 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/index.html @@ -0,0 +1,12 @@ + + + + + + deplugin-view-frontend + + +
+ + + diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/package.json b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/package.json new file mode 100644 index 0000000000..48531de436 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/package.json @@ -0,0 +1,82 @@ +{ + "name": "deplugin-view-frontend", + "version": "1.0.0", + "description": "A Vue.js project", + "author": "", + "private": true, + "scripts": { + "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", + "start": "npm run dev", + "build": "node build/build.js", + "buildPlugin": "node build/build-async-plugins.js" + }, + "dependencies": { + "@antv/l7": "2.15.0", + "@antv/l7-component": "2.15.0", + "@antv/l7-core": "2.15.0", + "@antv/l7-layers": "2.15.0", + "@antv/l7-maps": "2.15.0", + "@antv/l7-renderer": "2.15.0", + "@antv/l7-scene": "2.15.0", + "@antv/l7-source": "2.15.0", + "@antv/l7-utils": "2.15.0", + "@riophae/vue-treeselect": "0.4.0", + "highcharts": "^10.0.0", + "vue": "^2.5.2", + "vue-i18n": "7.3.2", + "vue-router": "^3.0.1", + "vue-uuid": "2.0.2", + "vuedraggable": "^2.24.3" + }, + "devDependencies": { + "autoprefixer": "^7.1.2", + "babel-core": "^6.22.1", + "babel-helper-vue-jsx-merge-props": "^2.0.3", + "babel-loader": "^7.1.1", + "babel-plugin-syntax-jsx": "^6.18.0", + "babel-plugin-transform-runtime": "^6.22.0", + "babel-plugin-transform-vue-jsx": "^3.5.0", + "babel-preset-env": "^1.3.2", + "babel-preset-stage-2": "^6.22.0", + "chalk": "^2.0.1", + "copy-webpack-plugin": "^4.6.0", + "css-loader": "^0.28.0", + "element-ui": "2.15.7", + "extract-text-webpack-plugin": "^4.0.0-beta.0", + "file-loader": "^1.1.4", + "friendly-errors-webpack-plugin": "^1.6.1", + "html-webpack-plugin": "^3.2.0", + "js-cookie": "2.2.0", + "node-notifier": "^8.0.1", + "optimize-css-assets-webpack-plugin": "^3.2.0", + "ora": "^1.2.0", + "portfinder": "^1.0.13", + "postcss-import": "^11.0.0", + "postcss-loader": "^2.0.8", + "postcss-url": "^7.2.1", + "rimraf": "^2.6.0", + "sass": "^1.33.0", + "sass-loader": "^7.3.1", + "semver": "^5.3.0", + "shelljs": "^0.8.5", + "svg-sprite-loader": "4.1.3", + "uglifyjs-webpack-plugin": "^1.1.1", + "url-loader": "^0.5.8", + "vue-loader": "^15.6.4", + "vue-style-loader": "^4.1.2", + "vue-template-compiler": "^2.5.2", + "webpack": "^4.8.1", + "webpack-bundle-analyzer": "^3.3.2", + "webpack-dev-server": "^3.1.11", + "webpack-merge": "^4.1.0" + }, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + }, + "browserslist": [ + "> 1%", + "last 2 versions", + "not ie <= 8" + ] +} diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/pom.xml b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/pom.xml new file mode 100644 index 0000000000..ff15b278bc --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/pom.xml @@ -0,0 +1,80 @@ + + + + + io.dataease + view-symbolmap + ${dataease.version} + + + 4.0.0 + view-symbolmap-frontend + + + UTF-8 + UTF-8 + 1.9.1 + + + + + + maven-clean-plugin + + + + static + + ** + + false + + + + + + + + com.github.eirslett + frontend-maven-plugin + ${frontend-maven-plugin.version} + + + install node and npm + + install-node-and-npm + + + + v16.20.2 + 7.6.3 + + + + + npm install + + npm + + + + install + + + + + npm run buildPlugin + + npm + + + run buildPlugin + + + + + + + diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/App.vue b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/App.vue new file mode 100644 index 0000000000..8542e07722 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/App.vue @@ -0,0 +1,23 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/assets/logo.png b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/assets/logo.png new file mode 100644 index 0000000000..f3d2503fc2 Binary files /dev/null and b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/assets/logo.png differ diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/HelloWorld.vue b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/HelloWorld.vue new file mode 100644 index 0000000000..1c19f2a399 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/HelloWorld.vue @@ -0,0 +1,113 @@ + + + + + + diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/SvgIcon/index.vue b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/SvgIcon/index.vue new file mode 100644 index 0000000000..f81b67f667 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/SvgIcon/index.vue @@ -0,0 +1,60 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/selector/BackgroundColorSelector.vue b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/selector/BackgroundColorSelector.vue new file mode 100644 index 0000000000..e1cbf5fe18 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/selector/BackgroundColorSelector.vue @@ -0,0 +1,102 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/selector/BaseMapStyleSelector.vue b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/selector/BaseMapStyleSelector.vue new file mode 100644 index 0000000000..819a284785 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/selector/BaseMapStyleSelector.vue @@ -0,0 +1,99 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/selector/ColorSelector.vue b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/selector/ColorSelector.vue new file mode 100644 index 0000000000..326e9c8e77 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/selector/ColorSelector.vue @@ -0,0 +1,290 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/selector/LabelSelector.vue b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/selector/LabelSelector.vue new file mode 100644 index 0000000000..2a24e2f573 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/selector/LabelSelector.vue @@ -0,0 +1,244 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/selector/LegendSelector.vue b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/selector/LegendSelector.vue new file mode 100644 index 0000000000..5125412616 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/selector/LegendSelector.vue @@ -0,0 +1,165 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/selector/SizeSelectorAntV.vue b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/selector/SizeSelectorAntV.vue new file mode 100644 index 0000000000..b2d4348efa --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/selector/SizeSelectorAntV.vue @@ -0,0 +1,130 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/selector/TitleSelector.vue b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/selector/TitleSelector.vue new file mode 100644 index 0000000000..938dd29c36 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/selector/TitleSelector.vue @@ -0,0 +1,269 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/selector/TooltipSelector.vue b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/selector/TooltipSelector.vue new file mode 100644 index 0000000000..fbd16a64e3 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/selector/TooltipSelector.vue @@ -0,0 +1,144 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/selector/TooltipSelectorAntV.vue b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/selector/TooltipSelectorAntV.vue new file mode 100644 index 0000000000..334d6cf03f --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/selector/TooltipSelectorAntV.vue @@ -0,0 +1,225 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/views/ChartDragItem.vue b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/views/ChartDragItem.vue new file mode 100644 index 0000000000..53ae1e2ad0 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/views/ChartDragItem.vue @@ -0,0 +1,278 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/views/DimensionExtItem.vue b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/views/DimensionExtItem.vue new file mode 100644 index 0000000000..c414ad2d36 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/views/DimensionExtItem.vue @@ -0,0 +1,256 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/views/DrillItem.vue b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/views/DrillItem.vue new file mode 100644 index 0000000000..7321b8a08c --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/views/DrillItem.vue @@ -0,0 +1,146 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/views/FieldErrorTips.vue b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/views/FieldErrorTips.vue new file mode 100644 index 0000000000..9d7b574721 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/views/FieldErrorTips.vue @@ -0,0 +1,21 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/views/FilterItem.vue b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/views/FilterItem.vue new file mode 100644 index 0000000000..3dd5952d69 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/views/FilterItem.vue @@ -0,0 +1,154 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/views/LocationLineItem.vue b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/views/LocationLineItem.vue new file mode 100644 index 0000000000..7b2985d08f --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/views/LocationLineItem.vue @@ -0,0 +1,170 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/views/LocationXItem.vue b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/views/LocationXItem.vue new file mode 100644 index 0000000000..fcd54e70e1 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/views/LocationXItem.vue @@ -0,0 +1,170 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/views/LocationYItem.vue b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/views/LocationYItem.vue new file mode 100644 index 0000000000..93e30656e1 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/views/LocationYItem.vue @@ -0,0 +1,170 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/views/QuotaExtItem.vue b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/views/QuotaExtItem.vue new file mode 100644 index 0000000000..c88a060a84 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/views/QuotaExtItem.vue @@ -0,0 +1,363 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/views/QuotaItem.vue b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/views/QuotaItem.vue new file mode 100644 index 0000000000..775f98eae0 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/views/QuotaItem.vue @@ -0,0 +1,310 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/views/ViewTrackBar.vue b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/views/ViewTrackBar.vue new file mode 100644 index 0000000000..605c0627b5 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/views/ViewTrackBar.vue @@ -0,0 +1,59 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/views/utils.js b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/views/utils.js new file mode 100644 index 0000000000..bceed82dcc --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/components/views/utils.js @@ -0,0 +1,48 @@ +export function getItemType(dimensionData, quotaData, item) { + // 将item的字段在数据集维度、指标字段中查询一遍,如果遇到id不存在、字段类型不一致、维度指标不一致,则提示 + const status = item.groupType + let checked = false + if (status === 'd') { + for (let i = 0; i < dimensionData.length; i++) { + const ele = dimensionData[i] + if (ele.id === item.id && ele.deType === item.deType && ele.groupType === item.groupType) { + checked = true + break + } + } + } + if (status === 'q') { + for (let i = 0; i < quotaData.length; i++) { + const ele = quotaData[i] + if (ele.id === item.id && ele.deType === item.deType && ele.groupType === item.groupType) { + checked = true + break + } + } + } + + if (checked) { + if (status === 'd') { + return '' + } else if (status === 'q') { + return 'success' + } + } else { + return 'danger' + } +} + +export function getRemark(chart) { + const remark = {} + if (chart.customStyle) { + const customStyle = JSON.parse(chart.customStyle) + if (customStyle.text) { + const title = JSON.parse(JSON.stringify(customStyle.text)) + remark.show = title.remarkShow ? title.remarkShow : false + remark.content = title.remark ? title.remark : '' + remark.bgFill = title.remarkBackgroundColor ? title.remarkBackgroundColor : '#ffffffff' + } + } + return remark +} + diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/de-base/lang/en.js b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/de-base/lang/en.js new file mode 100644 index 0000000000..4f5692dbfd --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/de-base/lang/en.js @@ -0,0 +1,23 @@ +export default { + plugin_view_symbol_map: { + longitude: 'Longitude', + latitude: 'Latitude', + map_type: 'Map Type', + link_line: 'Link Line', + mark_size: 'Mark Size', + type_title: 'Symbol Map', + label: 'Area', + angle: 'Symbol', + marker: 'Marker', + pentagon: 'Pentagon', + hexagon: 'Hexagon', + octagon: 'Octagon', + hexagram: 'Hexagram', + border: 'Border', + light: 'Light', + dark: 'Dark', + label_format_tip: 'The field value can be read in the form of {field Name}, the fields in the label and the tips are interchangeable, and the built-in latitude and longitude related fields', + tooltip_format_tip: 'The field value can be read in the form of {field Name}, the fields in the label and the tips are interchangeable, and the built-in latitude and longitude related fields.(the label does not support line breaks)', + mark_size_tip: 'When this quota is in effect, the bubble size attribute in the style size will be invalid' + } +} diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/de-base/lang/index.js b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/de-base/lang/index.js new file mode 100644 index 0000000000..e50d0478e6 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/de-base/lang/index.js @@ -0,0 +1,49 @@ +import Vue from 'vue' +import VueI18n from 'vue-i18n' +import Cookies from 'js-cookie' +import elementEnLocale from 'element-ui/lib/locale/lang/en' // element-ui lang +import elementZhLocale from 'element-ui/lib/locale/lang/zh-CN'// element-ui lang +import elementTWLocale from 'element-ui/lib/locale/lang/zh-TW'// element-ui lang + +import localMessages from './messages' + + +Vue.use(VueI18n) + +const messages = { + en_US: { + ...localMessages['en_US'], + ...elementEnLocale + }, + zh_CN: { + ...localMessages['zh_CN'], + ...elementZhLocale + }, + zh_TW: { + ...localMessages['zh_TW'], + ...elementTWLocale + } +} +export function getLanguage () { + const chooseLanguage = Cookies.get('language') + if (chooseLanguage) return chooseLanguage + + // if has not choose language + const language = (navigator.language || navigator.browserLanguage).toLowerCase() + const locales = Object.keys(messages) + for (const locale of locales) { + if (language.indexOf(locale) > -1) { + return locale + } + } + return 'zh_CN' +} +const i18n = new VueI18n({ + // set locale + // options: en | zh | es + locale: getLanguage(), + // set locale messages + messages +}) + +export default i18n diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/de-base/lang/messages.js b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/de-base/lang/messages.js new file mode 100644 index 0000000000..844f8c0673 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/de-base/lang/messages.js @@ -0,0 +1,17 @@ +import enLocale from './en' +import zhLocale from './zh' +import twLocale from './tw' + +const messages = { + en_US: { + ...enLocale + }, + zh_CN: { + ...zhLocale + }, + zh_TW: { + ...twLocale + } +} + +export default messages \ No newline at end of file diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/de-base/lang/tw.js b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/de-base/lang/tw.js new file mode 100644 index 0000000000..c9c64494c2 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/de-base/lang/tw.js @@ -0,0 +1,24 @@ +export default { + plugin_view_symbol_map: { + type_title: '符號地圖', + longitude: '經度', + latitude: '緯度', + map_type: '類型', + link_line: '連線', + mark_size: '標記大小', + label: '區域', + angle: '符號', + marker: '標記', + pentagon: '五邊形', + hexagon: '六邊形', + + octagon: '八邊形', + hexagram: '六角星', + border: '邊框', + light: '亮色', + dark: '暗色', + label_format_tip: '可以${fieldName}的形式讀取字段值,標籤和提示中的字段互相通用,內置經緯度相關字段', + tooltip_format_tip: '可以${fieldName}的形式讀取字段值,標籤和提示中的字段互相通用,內置經緯度相關字段(標籤不支持換行)', + mark_size_tip: '該指標生效時,樣式大小中的氣泡大小屬性將失效' + } +} diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/de-base/lang/zh.js b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/de-base/lang/zh.js new file mode 100644 index 0000000000..dec48dde04 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/de-base/lang/zh.js @@ -0,0 +1,22 @@ +export default { + plugin_view_symbol_map: { + type_title: '符号地图', + longitude: '经度', + latitude: '纬度', + map_type: '类型', + link_line: '连线', + mark_size: '标记大小', + angle: '符号', + marker: '标记', + pentagon: '五角形', + hexagon: '六角形', + octagon: '八角形', + hexagram: '六角星', + border: '边框', + light: '亮色', + dark: '暗色', + label_format_tip: '可以${fieldName}的形式读取字段值,标签和提示中的字段互相通用,内置经纬度相关字段', + tooltip_format_tip: '可以${fieldName}的形式读取字段值,标签和提示中的字段互相通用,内置经纬度相关字段(标签不支持换行)', + mark_size_tip: '该指标生效时,样式大小中的气泡大小属性将失效' + } +} diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/icons/index.js b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/icons/index.js new file mode 100644 index 0000000000..2c6b309c96 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/icons/index.js @@ -0,0 +1,9 @@ +import Vue from 'vue' +import SvgIcon from '@/components/SvgIcon'// svg component + +// register globally +Vue.component('svg-icon', SvgIcon) + +const req = require.context('./svg', false, /\.svg$/) +const requireAll = requireContext => requireContext.keys().map(requireContext) +requireAll(req) diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/icons/svg/map-marker-old.svg b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/icons/svg/map-marker-old.svg new file mode 100644 index 0000000000..5e43dbcdff --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/icons/svg/map-marker-old.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/icons/svg/map-marker.png b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/icons/svg/map-marker.png new file mode 100644 index 0000000000..af954a045b Binary files /dev/null and b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/icons/svg/map-marker.png differ diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/icons/svg/map-marker.svg b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/icons/svg/map-marker.svg new file mode 100644 index 0000000000..25f3802de5 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/icons/svg/map-marker.svg @@ -0,0 +1,3 @@ + + + diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/icons/svg/symbol-map.svg b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/icons/svg/symbol-map.svg new file mode 100644 index 0000000000..81fa53d188 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/icons/svg/symbol-map.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/icons/svg/view-symbolmap-backend.svg b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/icons/svg/view-symbolmap-backend.svg new file mode 100644 index 0000000000..becfdb6caf --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/icons/svg/view-symbolmap-backend.svg @@ -0,0 +1 @@ +【icon】插件管理-导出 \ No newline at end of file diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/icons/svgo.yml b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/icons/svgo.yml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/main.js b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/main.js new file mode 100644 index 0000000000..a57d2a8368 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/main.js @@ -0,0 +1,36 @@ +// The Vue build version to load with the `import` command +// (runtime-only or standalone) has been set in webpack.base.conf with an alias. +import Vue from 'vue' +import App from './App' +import router from './router' +import ElementUI from 'element-ui' +import Cookies from 'js-cookie' +import i18n from './de-base/lang' +import draggable from 'vuedraggable' +import Treeselect from '@riophae/vue-treeselect' +import '@riophae/vue-treeselect/dist/vue-treeselect.css' +import '@/icons' // icon +import { GaodeMap } from '@antv/l7-maps' +Vue.config.productionTip = false +Vue.use(ElementUI, { + size: Cookies.get('size') || 'medium', + i18n: (key, value) => i18n.t(key, value) +}) +Vue.component('Treeselect', Treeselect) +Vue.component('draggable', draggable) +Vue.prototype.hasDataPermission = function(pTarget, pSource) { + + if (pSource && pTarget) { + return pSource.indexOf(pTarget) > -1 + } + return false +} +Vue.prototype.$gaodeMap = GaodeMap +/* eslint-disable no-new */ +new Vue({ + el: '#app', + router, + i18n, + components: { App }, + template: '' +}) diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/router/index.js b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/router/index.js new file mode 100644 index 0000000000..28855997b4 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/router/index.js @@ -0,0 +1,21 @@ +import Vue from 'vue' +import Router from 'vue-router' +import HelloWorld from '@/components/HelloWorld' +import SymbolMap from '@/views/antv/symbolmap/type' + +Vue.use(Router) + +export default new Router({ + routes: [ + { + path: '/', + name: 'HelloWorld', + component: HelloWorld + }, + { + path: '/symbol-map', + name: 'symbol-map', + component: SymbolMap + } + ] +}) diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/utils/compare.js b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/utils/compare.js new file mode 100644 index 0000000000..b06a104a10 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/utils/compare.js @@ -0,0 +1,29 @@ +export const compareItem = { + type: 'none', // year-yoy/month-yoy等 + resultData: 'percent', // 对比差sub,百分比percent等 + field: '', + custom: { + field: '', + calcType: '0', // 0-增长值,1-增长率 + timeType: '0', // 0-固定日期,1-日期区间 + currentTime: '', + compareTime: '', + currentTimeRange: [], + compareTimeRange: [] + } +} + +export const compareYearList = [ + { name: 'year_mom', value: 'year_mom' } +] + +export const compareMonthList = [ + { name: 'month_mom', value: 'month_mom' }, + { name: 'year_yoy', value: 'year_yoy' } +] + +export const compareDayList = [ + { name: 'day_mom', value: 'day_mom' }, + { name: 'month_yoy', value: 'month_yoy' }, + { name: 'year_yoy', value: 'year_yoy' } +] diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/utils/map.js b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/utils/map.js new file mode 100644 index 0000000000..19f9155c56 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/utils/map.js @@ -0,0 +1,430 @@ +export const DEFAULT_COLOR_CASE = { + value: 'default', + colors: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'], + alpha: 100, + tableHeaderBgColor: '#e1eaff', + tableItemBgColor: '#ffffff', + tableFontColor: '#000000', + tableStripe: true, + dimensionColor: '#000000', + quotaColor: '#000000', + tableBorderColor: '#cfdaf4' +} +export const DEFAULT_SIZE = { + barDefault: true, + barWidth: 40, + barGap: 0.4, + lineWidth: 2, + lineType: 'solid', + lineSymbol: 'marker', + lineSymbolSize: 4, + lineSmooth: true, + lineArea: false, + pieInnerRadius: 0, + pieOuterRadius: 80, + pieRoseType: 'radius', + pieRoseRadius: 5, + funnelWidth: 80, + radarShape: 'polygon', + radarSize: 80, + tableTitleFontSize: 12, + tableItemFontSize: 12, + tableTitleHeight: 36, + tableItemHeight: 36, + tablePageSize: '20', + tableColumnMode: 'custom', + tableColumnWidth: 100, + tableHeaderAlign: 'left', + tableItemAlign: 'right', + gaugeMin: 0, + gaugeMax: 100, + gaugeStartAngle: 225, + gaugeEndAngle: -45, + dimensionFontSize: 18, + quotaFontSize: 18, + spaceSplit: 10, + dimensionShow: true, + quotaShow: true, + scatterSymbol: 'marker', + scatterSymbolSize: 15, + symbolOpacity: 5, + symbolStrokeWidth: 1, + treemapWidth: 80, + treemapHeight: 80, + liquidMax: 100, + liquidSize: 80, + liquidOutlineBorder: 4, + liquidOutlineDistance: 8, + liquidWaveLength: 128, + liquidWaveCount: 3, + liquidShape: 'circle', + tablePageMode: 'page' +} +export const COLOR_PANEL = [ + '#ff4500', + '#ff8c00', + '#ffd700', + '#90ee90', + '#00ced1', + '#1e90ff', + '#c71585', + '#999999', + '#000000', + '#FFFFFF' +] + +export const DEFAULT_LABEL = { + show: false, + position: 'top', + color: '#909399', + fontSize: '10', + formatter: '{c}', + gaugeFormatter: '{value}', + labelLine: { + show: true + }, + fields: null, + labelTemplate: '{busiValue}' + } + export const DEFAULT_TOOLTIP = { + show: true, + trigger: 'item', + confine: true, + textStyle: { + fontSize: '10', + color: '#909399' + }, + formatter: '', + tooltipTemplate: '{busiValue}' + } + export const DEFAULT_TITLE_STYLE = { + show: true, + fontSize: '18', + color: '#303133', + hPosition: 'center', + vPosition: 'top', + isItalic: false, + isBolder: false + } + export const DEFAULT_LEGEND_STYLE = { + show: true, + hPosition: 'center', + vPosition: 'bottom', + orient: 'horizontal', + icon: 'circle', + textStyle: { + color: '#333333', + fontSize: '12' + } + } + export const DEFAULT_BACKGROUND_COLOR = { + color: '#ffffff', + alpha: 100, + borderRadius: 5 + } + export const DEFAULT_BASE_MAP_STYLE = { + baseMapTheme: 'light' + } +export const BASE_MAP = { + title: { + text: '', + textStyle: { + fontWeight: 'normal' + } + }, + visualMap: { + min: 50, + max: 52, + text: ['High', 'Low'], + realtime: false, + calculable: true, + inRange: { + color: ['lightskyblue', 'yellow', 'orangered'] + }, + right: 0 + }, + + tooltip: {}, + geo: [ + { + show: true, + map: 'BUDDLE_MAP_BORDER', + + emphasis: { + disabled: true + }, + itemStyle: { + borderWidth: 2, + borderColor: '#d1d1d1', + borderType: 'solid' + }, + + roam: false + }, + { + show: true, + map: 'BUDDLE_MAP', + label: { + normal: { + show: false + }, + emphasis: { + show: false + } + }, + itemStyle: { + areaColor: '#f3f3f3', + borderType: 'dashed', + borderColor: '#fff' + }, + + roam: false + } + ], + series: [ + { + name: '', + type: 'scatter', + coordinateSystem: 'geo', + data: [] + } + ] +} +const convertData = (mapData, chart) => { + let maxVal = 0 + const k = terminalType === 'pc' ? 30 : 15 + const names = chart.data.x + const results = [] + for (let index = 0; index < names.length; index++) { + const name = names[index]; + const item = chart.data.series[0].data[index] + results.push({name, value: (mapData[name] ? mapData[name].concat(item.value) : []), dimensionList: item.dimensionList, quotaList: item.quotaList}) + maxVal = Math.max(maxVal, item.value) + } + const rate = k / maxVal + + return { + value: results, + rate: rate + } +} + +let terminalType = 'pc' +export function baseMapOption(chart_option, chart, mapData, terminal = 'pc') { + terminalType = terminal + // 处理shape attr + let customAttr = {} + if (chart.customAttr) { + customAttr = JSON.parse(chart.customAttr) + if (customAttr.color) { + chart_option.color = customAttr.color.colors + } + // tooltip + if (customAttr.tooltip) { + const tooltip = JSON.parse(JSON.stringify(customAttr.tooltip)) + const reg = new RegExp('\n', 'g') + const text = tooltip.formatter.replace(reg, '
') + tooltip.formatter = function(params) { + const a = params.seriesName + const b = params.name + const c = params.value ? params.value[2] : '' + return text.replace(new RegExp('{a}', 'g'), a).replace(new RegExp('{b}', 'g'), b).replace(new RegExp('{c}', 'g'), c) + } + chart_option.tooltip = tooltip + } + } + // 处理data + if (chart.data) { + chart_option.title.text = chart.title + if (chart.data.series && chart.data.series.length > 0) { + chart_option.series[0].name = chart.data.series[0].name + // label + if (customAttr.label) { + const text = customAttr.label.formatter + chart_option.series[0].label = customAttr.label + chart_option.series[0].label.formatter = function(params) { + const a = params.seriesName + const b = params.name + const c = params.value ? params.value[2] : '' + return text.replace(new RegExp('{a}', 'g'), a).replace(new RegExp('{b}', 'g'), b).replace(new RegExp('{c}', 'g'), c) + } + chart_option.series[0].labelLine = customAttr.label.labelLine + } + + // visualMap + const valueArr = chart.data.series[0].data + if (valueArr && valueArr.length > 0) { + const values = [] + valueArr.forEach(function(ele) { + values.push(ele.value) + }) + chart_option.visualMap.min = Math.min(...values) + chart_option.visualMap.max = Math.max(...values) + if (chart_option.visualMap.min === chart_option.visualMap.max) { + chart_option.visualMap.min = 0 + } + } else { + chart_option.visualMap.min = 0 + chart_option.visualMap.max = 0 + } + if (chart_option.visualMap.min === 0 && chart_option.visualMap.max === 0) { + chart_option.visualMap.max = 100 + } + // color + if (customAttr.color && customAttr.color.colors) { + chart_option.visualMap.inRange.color = customAttr.color.colors + chart_option.visualMap.inRange.colorAlpha = customAttr.color.alpha / 100 + } + // chart_option.visualMap = null + + const convert = convertData(mapData, chart) + chart_option.series[0].data = convert.value + chart_option.series[0].symbolSize = val => val[2] * convert.rate + + } + } + // console.log(chart_option); + componentStyle(chart_option, chart) + return chart_option +} +export function componentStyle(chart_option, chart) { + const padding = '8px' + if (chart.customStyle) { + const customStyle = JSON.parse(chart.customStyle) + if (customStyle.text) { + chart_option.title.show = customStyle.text.show + // 水平方向 + if (customStyle.text.hPosition === 'left') { + chart_option.title.left = padding + } else if (customStyle.text.hPosition === 'right') { + chart_option.title.right = padding + } else { + chart_option.title.left = customStyle.text.hPosition + } + // 垂直方向 + if (customStyle.text.vPosition === 'top') { + chart_option.title.top = padding + } else if (customStyle.text.vPosition === 'bottom') { + chart_option.title.bottom = padding + } else { + chart_option.title.top = customStyle.text.vPosition + } + const style = chart_option.title.textStyle ? chart_option.title.textStyle : {} + style.fontSize = customStyle.text.fontSize + style.color = customStyle.text.color + customStyle.text.isItalic ? style.fontStyle = 'italic' : style.fontStyle = 'normal' + customStyle.text.isBolder ? style.fontWeight = 'bold' : style.fontWeight = 'normal' + chart_option.title.textStyle = style + } + if (customStyle.legend && chart_option.legend) { + chart_option.legend.show = customStyle.legend.show + // 水平方向 + if (customStyle.legend.hPosition === 'left') { + chart_option.legend.left = padding + } else if (customStyle.legend.hPosition === 'right') { + chart_option.legend.right = padding + } else { + chart_option.legend.left = customStyle.legend.hPosition + } + // 垂直方向 + if (customStyle.legend.vPosition === 'top') { + chart_option.legend.top = padding + } else if (customStyle.legend.vPosition === 'bottom') { + chart_option.legend.bottom = padding + } else { + chart_option.legend.top = customStyle.legend.vPosition + } + chart_option.legend.orient = customStyle.legend.orient + chart_option.legend.icon = customStyle.legend.icon + chart_option.legend.textStyle = customStyle.legend.textStyle + + } + + if (customStyle.background) { + chart_option.backgroundColor = hexColorToRGBA(customStyle.background.color, customStyle.background.alpha) + } + } +} +export function hexColorToRGBA(hex, alpha) { + const rgb = [] // 定义rgb数组 + if (/^\#[0-9A-F]{3}$/i.test(hex)) { // 判断传入是否为#三位十六进制数 + let sixHex = '#' + hex.replace(/[0-9A-F]/ig, function(kw) { + sixHex += kw + kw // 把三位16进制数转化为六位 + }) + hex = sixHex // 保存回hex + } + if (/^#[0-9A-F]{6}$/i.test(hex)) { // 判断传入是否为#六位十六进制数 + hex.replace(/[0-9A-F]{2}/ig, function(kw) { + // eslint-disable-next-line no-eval + rgb.push(eval('0x' + kw)) // 十六进制转化为十进制并存如数组 + }) + return `rgba(${rgb.join(',')},${alpha / 100})` // 输出RGB格式颜色 + } else { + return 'rgb(0,0,0)' + } +} + + + +export const DEFAULT_YAXIS_EXT_STYLE = { + show: true, + position: 'right', + name: '', + nameTextStyle: { + color: '#333333', + fontSize: 12 + }, + axisLabel: { + show: true, + color: '#333333', + fontSize: '12', + rotate: 0, + formatter: '{value}' + }, + splitLine: { + show: true, + lineStyle: { + color: '#cccccc', + width: 1, + style: 'solid' + } + }, + axisValue: { + auto: true, + min: null, + max: null, + split: null, + splitCount: null + } +} + +export const getDefaultTemplate = (chart, type, feed , showKey) => { + if(!chart || !chart.viewFields || !type) return null; + let viewFields = [] + if (chart.viewFields instanceof Array) { + viewFields = JSON.parse(JSON.stringify(chart.viewFields)) + }else { + viewFields = JSON.parse(chart.viewFields) + } + const separator = feed ? '\n' : ' ' + return viewFields.filter(field => field.busiType && field.busiType === type).map(field => { + const fieldName = field.name + let template = "${"+ field.name +"}" + if(showKey) { + template = fieldName + ":${"+ field.name +"}" + } + return template + }).join(separator) +} + +export function uuid() { + return (((1+Math.random())*0x10000)|0).toString(16).substring(1); +} + +export const reverseColor = colorValue => { + colorValue = '0x' + colorValue.replace(/#/g, '') + const str = '000000' + (0xFFFFFF - colorValue).toString(16) + return '#' + str.substring(str.length - 6, str.length) +} + diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/utils/symbolmap.js b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/utils/symbolmap.js new file mode 100644 index 0000000000..697800dd8d --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/utils/symbolmap.js @@ -0,0 +1,73 @@ +// import {PointLayer, Popup } from '@antv/l7'; + +export const DEFAULT_COLOR_CASE = { + value: 'default', + colors: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'], + alpha: 100, + tableHeaderBgColor: '#e1eaff', + tableItemBgColor: '#ffffff', + tableFontColor: '#000000', + tableStripe: true, + dimensionColor: '#000000', + quotaColor: '#000000', + tableBorderColor: '#cfdaf4' +} + +export const COLOR_PANEL = [ + '#ff4500', + '#ff8c00', + '#ffd700', + '#90ee90', + '#00ced1', + '#1e90ff', + '#c71585', + '#999999', + '#000000', + '#FFFFFF' +] + + +export const DEFAULT_BACKGROUND_COLOR = { + color: '#ffffff', + alpha: 100, + borderRadius: 5 +} + + + + +export function baseSymbolMap(scene, container, chart, action, $pointLayer, $popup) { + let _self = this + + +} + + +export function hexColorToRGBA(hex, alpha) { + const rgb = [] // 定义rgb数组 + if (/^\#[0-9A-F]{3}$/i.test(hex)) { // 判断传入是否为#三位十六进制数 + let sixHex = '#' + hex.replace(/[0-9A-F]/ig, function(kw) { + sixHex += kw + kw // 把三位16进制数转化为六位 + }) + hex = sixHex // 保存回hex + } + if (/^#[0-9A-F]{6}$/i.test(hex)) { // 判断传入是否为#六位十六进制数 + hex.replace(/[0-9A-F]{2}/ig, function(kw) { + // eslint-disable-next-line no-eval + rgb.push(eval('0x' + kw)) // 十六进制转化为十进制并存如数组 + }) + return `rgba(${rgb.join(',')},${alpha / 100})` // 输出RGB格式颜色 + } else { + return 'rgb(0,0,0)' + } +} + + + + + +export function uuid() { + return (((1+Math.random())*0x10000)|0).toString(16).substring(1); +} + diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/utils/validate.js b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/utils/validate.js new file mode 100644 index 0000000000..3e8ffa9448 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/utils/validate.js @@ -0,0 +1,15 @@ +/** + * Created by PanJiaChen on 16/11/18. + */ + +/** + * @param {string} path + * @returns {Boolean} + */ +export function isExternal(path) { + return /^(https?:|mailto:|tel:)/.test(path) || /^(http?:|mailto:|tel:)/.test(path) || path.startsWith('/api/pluginCommon/staticInfo') +} + + + + diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/views/PluginDemo.vue b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/views/PluginDemo.vue new file mode 100644 index 0000000000..1164dfab1d --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/views/PluginDemo.vue @@ -0,0 +1,55 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/views/antv/symbolmap/data.vue b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/views/antv/symbolmap/data.vue new file mode 100644 index 0000000000..3b5e8a4620 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/views/antv/symbolmap/data.vue @@ -0,0 +1,648 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/views/antv/symbolmap/index.vue b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/views/antv/symbolmap/index.vue new file mode 100644 index 0000000000..b85247228f --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/views/antv/symbolmap/index.vue @@ -0,0 +1,633 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/views/antv/symbolmap/style.vue b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/views/antv/symbolmap/style.vue new file mode 100644 index 0000000000..88fdbbfb45 --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/views/antv/symbolmap/style.vue @@ -0,0 +1,199 @@ + + + + + diff --git a/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/views/antv/symbolmap/type.vue b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/views/antv/symbolmap/type.vue new file mode 100644 index 0000000000..982e0cb1ef --- /dev/null +++ b/extensions/dataease-extensions-view/view-symbolmap/view-symbolmap-frontend/src/views/antv/symbolmap/type.vue @@ -0,0 +1,63 @@ + + + + + diff --git a/extensions/pom.xml b/extensions/pom.xml new file mode 100644 index 0000000000..9c727cf479 --- /dev/null +++ b/extensions/pom.xml @@ -0,0 +1,43 @@ + + + 4.0.0 + + io.dataease + dataease-extensions + ${dataease.version} + pom + + + + io.dataease + dataease + ${dataease.version} + + + + + + io.dataease + dataease-plugin-datasource + ${dataease.version} + + + io.dataease + dataease-plugin-view + ${dataease.version} + + + + + + dataease-extensions-view + dataease-extensions-datasource + + diff --git a/pom.xml b/pom.xml index 5b6b65d898..fba16d54d7 100644 --- a/pom.xml +++ b/pom.xml @@ -17,6 +17,7 @@ sdk core + extensions