diff --git a/core/pom.xml b/core/pom.xml index b72b3a56251f9023f4b7b0012976c02d60957443..d82c12a555d466f4efed3fc230f48a859e91f256 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -5,7 +5,7 @@ datart-parent datart - 1.0.0-rc.1 + 1.0.0-rc.2 4.0.0 diff --git a/core/src/main/java/datart/core/entity/poi/format/PoiNumFormat.java b/core/src/main/java/datart/core/entity/poi/format/PoiNumFormat.java index 5dc2a34741ed1952dc140d4058d6bfb9ab67a17e..78b04a9c9b395e426086f488846acadfbded5608 100644 --- a/core/src/main/java/datart/core/entity/poi/format/PoiNumFormat.java +++ b/core/src/main/java/datart/core/entity/poi/format/PoiNumFormat.java @@ -43,7 +43,7 @@ public class PoiNumFormat { } public Object parseValue(Object obj){ - if (obj!=null && StringUtils.isNotBlank(this.unitKey)){ + if (obj!=null && org.apache.commons.lang.math.NumberUtils.isNumber(obj.toString()) && StringUtils.isNotBlank(this.unitKey)){ UnitKey unitKey = UnitKey.getUnitKeyByValue(this.unitKey); BigDecimal val = new BigDecimal(obj.toString()).divide(new BigDecimal(unitKey.getUnit())); obj = val.setScale(getDecimalPlacesNum(), BigDecimal.ROUND_HALF_UP); diff --git a/core/src/main/java/datart/core/mappers/ext/ScheduleMapperExt.java b/core/src/main/java/datart/core/mappers/ext/ScheduleMapperExt.java index 9b0a224d54e92471151ae06a6490c9ee93c4e912..4ed7c4265a90b412d9359a558ae55bd46bf09ac8 100644 --- a/core/src/main/java/datart/core/mappers/ext/ScheduleMapperExt.java +++ b/core/src/main/java/datart/core/mappers/ext/ScheduleMapperExt.java @@ -20,4 +20,22 @@ public interface ScheduleMapperExt extends ScheduleMapper { }) List selectArchived(String orgId); + @Select({ + "SELECT COUNT(*) FROM `schedule` WHERE parent_id = #{scheduleId} AND `status`!=0" + }) + int checkReference(String scheduleId); + + @Select({ + "", + }) + List checkName(String orgId, String name, String parentId); + } diff --git a/core/src/main/java/datart/core/mappers/ext/SourceMapperExt.java b/core/src/main/java/datart/core/mappers/ext/SourceMapperExt.java index 0960360e47586a5d9f5ef55113f0fd3e39462692..5086d6489c572efbb2a59f822820a3bc5572e535 100644 --- a/core/src/main/java/datart/core/mappers/ext/SourceMapperExt.java +++ b/core/src/main/java/datart/core/mappers/ext/SourceMapperExt.java @@ -38,4 +38,9 @@ public interface SourceMapperExt extends SourceMapper { }) Organization getOrgById(@Param("sourceId") String sourceId); + @Select({ + "SELECT COUNT(*) FROM `source` WHERE parent_id = #{sourceId} AND `status`!=0" + }) + int checkReference(String sourceId); + } diff --git a/data-providers/data-provider-base/pom.xml b/data-providers/data-provider-base/pom.xml index 88a8e56e4d4323e740d2e4b974bee281207814b2..14565a9a8e012dc5dda16decfae4d3b7c66f1fe1 100644 --- a/data-providers/data-provider-base/pom.xml +++ b/data-providers/data-provider-base/pom.xml @@ -5,7 +5,7 @@ datart-data-provider datart - 1.0.0-rc.1 + 1.0.0-rc.2 4.0.0 diff --git a/data-providers/data-provider-base/src/main/java/datart/data/provider/calcite/SqlNodeUtils.java b/data-providers/data-provider-base/src/main/java/datart/data/provider/calcite/SqlNodeUtils.java index d896abe3fbf232dbb7341e039c9ad33fe962917e..94467770434199a6ea5d2eabfc32e9fc740935ee 100644 --- a/data-providers/data-provider-base/src/main/java/datart/data/provider/calcite/SqlNodeUtils.java +++ b/data-providers/data-provider-base/src/main/java/datart/data/provider/calcite/SqlNodeUtils.java @@ -18,17 +18,13 @@ package datart.data.provider.calcite; import datart.core.base.exception.Exceptions; -import datart.core.common.DateUtils; import datart.core.data.provider.ScriptVariable; import datart.core.data.provider.SingleTypedValue; import datart.data.provider.calcite.custom.SqlSimpleStringLiteral; import org.apache.calcite.sql.*; import org.apache.calcite.sql.fun.SqlStdOperatorTable; import org.apache.calcite.sql.parser.SqlParserPos; -import org.apache.calcite.util.DateString; -import org.apache.calcite.util.TimestampString; import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.lang3.StringUtils; import java.util.Arrays; import java.util.Collections; @@ -132,15 +128,8 @@ public class SqlNodeUtils { } private static SqlNode createDateSqlNode(String value, String format) { - if (StringUtils.isBlank(format)) { - return SqlLiteral.createTimestamp(new TimestampString(value), 0, SqlParserPos.ZERO); - } else if (DateUtils.isDateFormat(format)) { - return SqlLiteral.createDate(new DateString(value), SqlParserPos.ZERO); - } else if (DateUtils.isDateTimeFormat(format)) { - return SqlLiteral.createTimestamp(new TimestampString(value), 0, SqlParserPos.ZERO); - } else { - return new SqlSimpleStringLiteral(value); - } + // After 1.0.0-RC.1, date type parameters are treated as strings directly + return new SqlSimpleStringLiteral(value); } /** diff --git a/data-providers/data-provider-base/src/main/java/datart/data/provider/calcite/SqlQueryScriptProcessor.java b/data-providers/data-provider-base/src/main/java/datart/data/provider/calcite/SqlQueryScriptProcessor.java index b78a60b2287ba8dbf8bf409947327824c8711e9c..bef12ccc55a04b841426aa44e022a3dac8f9c1a3 100644 --- a/data-providers/data-provider-base/src/main/java/datart/data/provider/calcite/SqlQueryScriptProcessor.java +++ b/data-providers/data-provider-base/src/main/java/datart/data/provider/calcite/SqlQueryScriptProcessor.java @@ -94,7 +94,7 @@ public class SqlQueryScriptProcessor implements QueryScriptProcessor { selectSql = StringUtils.prependIfMissing(selectSql, " ", " "); SqlBasicCall sqlBasicCall = new SqlBasicCall(SqlStdOperatorTable.AS - , new SqlNode[]{new SqlFragment("(" + selectSql + ")"), new SqlIdentifier(T, SqlParserPos.ZERO.withQuoting(true))} + , new SqlNode[]{new SqlFragment("(" + selectSql + ")"), new SqlIdentifier(T, SqlParserPos.ZERO)} , SqlParserPos.ZERO); QueryScriptProcessResult result = new QueryScriptProcessResult(); result.setFrom(sqlBasicCall); diff --git a/data-providers/data-provider-base/src/main/java/datart/data/provider/jdbc/DataTypeUtils.java b/data-providers/data-provider-base/src/main/java/datart/data/provider/jdbc/DataTypeUtils.java index 53829091a65febda31d31746c764ff39abe8ee60..60d5aec7039346c3e4488a67eb7a0567eef981fa 100644 --- a/data-providers/data-provider-base/src/main/java/datart/data/provider/jdbc/DataTypeUtils.java +++ b/data-providers/data-provider-base/src/main/java/datart/data/provider/jdbc/DataTypeUtils.java @@ -17,9 +17,8 @@ */ package datart.data.provider.jdbc; -import datart.core.base.consts.ValueType; import datart.core.base.consts.JavaType; -import datart.data.provider.calcite.custom.CustomSqlTypeName; +import datart.core.base.consts.ValueType; import org.apache.calcite.sql.type.SqlTypeFamily; import org.apache.calcite.sql.type.SqlTypeName; @@ -28,12 +27,34 @@ import java.util.Date; public class DataTypeUtils { - public static ValueType sqlType2DataType(String sqlType) { - sqlType = sqlType.toUpperCase(); - SqlTypeName sqlTypeName = SqlTypeName.get(sqlType); +// public static ValueType sqlType2DataType(String sqlType) { +// sqlType = sqlType.toUpperCase(); +// SqlTypeName sqlTypeName = SqlTypeName.get(sqlType); +// SqlTypeFamily family; +// if (sqlTypeName == null) { +// family = CustomSqlTypeName.SQL_TYPE_FAMILY_MAP.getOrDefault(sqlType, CustomSqlTypeName.ANY).getFamily(); +// } else { +// family = sqlTypeName.getFamily(); +// } +// switch (family) { +// case NUMERIC: +// return ValueType.NUMERIC; +// case DATE: +// case TIME: +// case TIMESTAMP: +// case DATETIME: +// return ValueType.DATE; +// default: +// return ValueType.STRING; +// } +// } + + + public static ValueType jdbcType2DataType(int jdbcType) { + SqlTypeName sqlTypeName = SqlTypeName.getNameForJdbcType(jdbcType); SqlTypeFamily family; if (sqlTypeName == null) { - family = CustomSqlTypeName.SQL_TYPE_FAMILY_MAP.getOrDefault(sqlType, CustomSqlTypeName.ANY).getFamily(); + family = SqlTypeFamily.getFamilyForJdbcType(jdbcType); } else { family = sqlTypeName.getFamily(); } diff --git a/data-providers/data-provider-base/src/main/java/datart/data/provider/jdbc/ResultSetMapper.java b/data-providers/data-provider-base/src/main/java/datart/data/provider/jdbc/ResultSetMapper.java index 3883753493eefa1cc96e5dc06d153c13389398c4..5a6f9fc4706dabe087e2828eaeb56a4c26cd2d72 100644 --- a/data-providers/data-provider-base/src/main/java/datart/data/provider/jdbc/ResultSetMapper.java +++ b/data-providers/data-provider-base/src/main/java/datart/data/provider/jdbc/ResultSetMapper.java @@ -32,10 +32,10 @@ public class ResultSetMapper { public static List getColumns(ResultSet rs) throws SQLException { ArrayList columns = new ArrayList<>(); for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) { - String columnTypeName = rs.getMetaData().getColumnTypeName(i); + int columnType = rs.getMetaData().getColumnType(i); String columnName = rs.getMetaData().getColumnLabel(i); - ValueType valueType = DataTypeUtils.sqlType2DataType(columnTypeName); - columns.add(Column.of(valueType,columnName)); + ValueType valueType = DataTypeUtils.jdbcType2DataType(columnType); + columns.add(Column.of(valueType, columnName)); } return columns; } diff --git a/data-providers/file-data-provider/pom.xml b/data-providers/file-data-provider/pom.xml index 1ab8df73eed6f65988e2c64273ba9e968650fa26..54eb2750bb271a3cae573ff76d4df9dadd4ba3b0 100644 --- a/data-providers/file-data-provider/pom.xml +++ b/data-providers/file-data-provider/pom.xml @@ -5,7 +5,7 @@ datart-data-provider datart - 1.0.0-rc.1 + 1.0.0-rc.2 4.0.0 diff --git a/data-providers/http-data-provider/pom.xml b/data-providers/http-data-provider/pom.xml index 36e73af55303dceb217bfe2a0e3589517b6cfc64..49724e1172eeec1181df48ab29057588c9c67f11 100644 --- a/data-providers/http-data-provider/pom.xml +++ b/data-providers/http-data-provider/pom.xml @@ -5,7 +5,7 @@ datart-data-provider datart - 1.0.0-rc.1 + 1.0.0-rc.2 4.0.0 diff --git a/data-providers/jdbc-data-provider/pom.xml b/data-providers/jdbc-data-provider/pom.xml index 618931b95f82c463b8b564949decba2bb39e4ff1..343333df400b188d73389b1e41e9fc5f4628222c 100644 --- a/data-providers/jdbc-data-provider/pom.xml +++ b/data-providers/jdbc-data-provider/pom.xml @@ -5,7 +5,7 @@ datart-data-provider datart - 1.0.0-rc.1 + 1.0.0-rc.2 4.0.0 diff --git a/data-providers/jdbc-data-provider/src/main/java/datart/data/provider/jdbc/adapters/JdbcDataProviderAdapter.java b/data-providers/jdbc-data-provider/src/main/java/datart/data/provider/jdbc/adapters/JdbcDataProviderAdapter.java index 133363b4cd6d2d3c0030381f04a49cb852afe54c..4e7b6711ff520990510c53c3766924a716d78f33 100644 --- a/data-providers/jdbc-data-provider/src/main/java/datart/data/provider/jdbc/adapters/JdbcDataProviderAdapter.java +++ b/data-providers/jdbc-data-provider/src/main/java/datart/data/provider/jdbc/adapters/JdbcDataProviderAdapter.java @@ -173,7 +173,7 @@ public class JdbcDataProviderAdapter implements Closeable { try (ResultSet columns = metadata.getColumns(database, null, table, null)) { while (columns.next()) { Column column = readTableColumn(columns); - column.setForeignKeys(importedKeys.get(column.getName())); + column.setForeignKeys(importedKeys.get(column.columnKey())); columnSet.add(column); } } @@ -182,14 +182,14 @@ public class JdbcDataProviderAdapter implements Closeable { } public String getQueryKey(QueryScript script, ExecuteParam executeParam) throws SqlParseException { - SqlScriptRender render = new SqlScriptRender(script, executeParam, getSqlDialect(), jdbcProperties.isEnableSpecialSql()); + SqlScriptRender render = new SqlScriptRender(script, executeParam, getSqlDialect(), jdbcProperties.isEnableSpecialSql(), driverInfo.getQuoteIdentifiers()); return "Q" + DigestUtils.md5Hex(render.render(true, supportPaging(), true)); } protected Column readTableColumn(ResultSet columnMetadata) throws SQLException { Column column = new Column(); column.setName(columnMetadata.getString(4)); - column.setType(DataTypeUtils.sqlType2DataType(columnMetadata.getString(6))); + column.setType(DataTypeUtils.jdbcType2DataType(columnMetadata.getInt(5))); return column; } @@ -394,9 +394,9 @@ public class JdbcDataProviderAdapter implements Closeable { protected List getColumns(ResultSet rs) throws SQLException { ArrayList columns = new ArrayList<>(); for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) { - String columnTypeName = rs.getMetaData().getColumnTypeName(i); + int columnType = rs.getMetaData().getColumnType(i); String columnName = rs.getMetaData().getColumnLabel(i); - ValueType valueType = DataTypeUtils.sqlType2DataType(columnTypeName); + ValueType valueType = DataTypeUtils.jdbcType2DataType(columnType); columns.add(Column.of(valueType, columnName)); } return columns; @@ -427,7 +427,9 @@ public class JdbcDataProviderAdapter implements Closeable { SqlScriptRender render = new SqlScriptRender(script , tempExecuteParam - , getSqlDialect()); + , getSqlDialect() + , jdbcProperties.isEnableSpecialSql() + , driverInfo.getQuoteIdentifiers()); String sql = render.render(true, false, false); Dataframe data = execute(sql); @@ -451,7 +453,8 @@ public class JdbcDataProviderAdapter implements Closeable { SqlScriptRender render = new SqlScriptRender(script , executeParam , getSqlDialect() - , jdbcProperties.isEnableSpecialSql()); + , jdbcProperties.isEnableSpecialSql() + , driverInfo.getQuoteIdentifiers()); if (supportPaging()) { sql = render.render(true, true, false); diff --git a/data-providers/jdbc-data-provider/src/main/java/datart/data/provider/jdbc/adapters/OracleDataProviderAdapter.java b/data-providers/jdbc-data-provider/src/main/java/datart/data/provider/jdbc/adapters/OracleDataProviderAdapter.java index 81c4dde406ea467277fca624e975bcb38f064bf0..eed05a28078daf2a68d52a42834c78ca6e8d2c46 100644 --- a/data-providers/jdbc-data-provider/src/main/java/datart/data/provider/jdbc/adapters/OracleDataProviderAdapter.java +++ b/data-providers/jdbc-data-provider/src/main/java/datart/data/provider/jdbc/adapters/OracleDataProviderAdapter.java @@ -53,7 +53,7 @@ public class OracleDataProviderAdapter extends JdbcDataProviderAdapter { Dataframe dataframe = new Dataframe(); List columns = getColumns(rs); int start = 1; - if ("V_R_N".equals(columns.get(0).getName())) { + if ("V_R_N".equals(columns.get(0).columnKey())) { start = 2; columns.remove(0); } diff --git a/data-providers/pom.xml b/data-providers/pom.xml index 2aeac70ee5e488127943025a39c8b16e27bb39c7..fdf664ef00379dd3052f0238aee92cb7785f9073 100644 --- a/data-providers/pom.xml +++ b/data-providers/pom.xml @@ -5,7 +5,7 @@ datart-parent datart - 1.0.0-rc.1 + 1.0.0-rc.2 4.0.0 diff --git a/frontend/package-lock.json b/frontend/package-lock.json index a091e2e544fc9b2ffbc004eca5cab337d91428cd..8486a934af808b299c1b782e76a2e8a4bd893d61 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@ant-design/icons": "^4.5.0", "@ant-design/pro-table": "2.60.1", - "@antv/s2": "1.18", + "@antv/s2": "^1.19.0", "@antv/s2-react": "1.18.0", "@dinero.js/currencies": "^2.0.0-alpha.8", "@reduxjs/toolkit": "^1.8.0", @@ -60,7 +60,7 @@ "redux-undo": "^1.0.1", "reveal.js": "^4.1.0", "split.js": "^1.6.4", - "sql-formatter": "^4.0.2", + "sql-formatter": "^9.2.0", "uuid": "^8.3.2", "video-react": "^0.15.0" }, @@ -357,9 +357,9 @@ } }, "node_modules/@antv/s2": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/@antv/s2/-/s2-1.18.0.tgz", - "integrity": "sha512-FviTsrVapHwFjhGjzC+O5jYWdvfic5ikLFLKABuOx0HhUTPVEe5KAjFGZwq0Fo+V1ev+2DLD5uBMRelVfygT3w==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@antv/s2/-/s2-1.19.0.tgz", + "integrity": "sha512-KHzJjK57Il2D7jTMwv8zDcmxz4U64PJnCN+YWOvgIF9RNCgnOYUlHCxnUE9alrJN5IZKL0JxpzulCPHrKy2A0g==", "dependencies": { "@antv/event-emitter": "^0.1.3", "@antv/g-canvas": "^0.5.12", @@ -26112,14 +26112,14 @@ "dev": true }, "node_modules/sql-formatter": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/sql-formatter/-/sql-formatter-4.0.2.tgz", - "integrity": "sha512-R6u9GJRiXZLr/lDo8p56L+OyyN2QFJPCDnsyEOsbdIpsnDKL8gubYFo7lNR7Zx7hfdWT80SfkoVS0CMaF/DE2w==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/sql-formatter/-/sql-formatter-9.2.0.tgz", + "integrity": "sha512-Dn4lEpUeAhfNDR2LnEs9Uaq92TSHjhcNrzhllPuMnp188P4sLU7UcdcB9UqIfMfcN62gWXJlJ3KocaAf/SOzXQ==", "dependencies": { "argparse": "^2.0.1" }, "bin": { - "sql-formatter": "bin/sqlfmt.js" + "sql-formatter": "bin/sql-formatter-cli.js" } }, "node_modules/ssri": { @@ -30406,9 +30406,9 @@ } }, "@antv/s2": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/@antv/s2/-/s2-1.18.0.tgz", - "integrity": "sha512-FviTsrVapHwFjhGjzC+O5jYWdvfic5ikLFLKABuOx0HhUTPVEe5KAjFGZwq0Fo+V1ev+2DLD5uBMRelVfygT3w==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@antv/s2/-/s2-1.19.0.tgz", + "integrity": "sha512-KHzJjK57Il2D7jTMwv8zDcmxz4U64PJnCN+YWOvgIF9RNCgnOYUlHCxnUE9alrJN5IZKL0JxpzulCPHrKy2A0g==", "requires": { "@antv/event-emitter": "^0.1.3", "@antv/g-canvas": "^0.5.12", @@ -51826,9 +51826,9 @@ "dev": true }, "sql-formatter": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/sql-formatter/-/sql-formatter-4.0.2.tgz", - "integrity": "sha512-R6u9GJRiXZLr/lDo8p56L+OyyN2QFJPCDnsyEOsbdIpsnDKL8gubYFo7lNR7Zx7hfdWT80SfkoVS0CMaF/DE2w==", + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/sql-formatter/-/sql-formatter-9.2.0.tgz", + "integrity": "sha512-Dn4lEpUeAhfNDR2LnEs9Uaq92TSHjhcNrzhllPuMnp188P4sLU7UcdcB9UqIfMfcN62gWXJlJ3KocaAf/SOzXQ==", "requires": { "argparse": "^2.0.1" } diff --git a/frontend/package.json b/frontend/package.json index e036d228a1e675d1225e04bf95236aafbdeb54b1..1ad629e9e21e0c40e6f8a0ba4c684a9faf253c69 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -27,8 +27,8 @@ "eslint": "eslint --ext js,ts,tsx", "lint": "npm run eslint src", "lint:fix": "npm run eslint --fix src", - "lint:css": "npx stylelint 'src/**/*.css'", - "lint:style": "npx stylelint 'src/**/*.{js,ts,jsx,tsx}'", + "lint:css": "npx stylelint \"src/**/*.css\"", + "lint:style": "npx stylelint \"src/**/*.{js,ts,jsx,tsx}\"", "prettify": "prettier --write src", "prepare": "cd .. && husky install frontend/.husky", "eject": "react-scripts eject", @@ -92,7 +92,7 @@ "dependencies": { "@ant-design/icons": "^4.5.0", "@ant-design/pro-table": "2.60.1", - "@antv/s2": "1.18", + "@antv/s2": "1.19.0", "@antv/s2-react": "1.18.0", "@dinero.js/currencies": "^2.0.0-alpha.8", "@reduxjs/toolkit": "^1.8.0", @@ -141,7 +141,7 @@ "redux-undo": "^1.0.1", "reveal.js": "^4.1.0", "split.js": "^1.6.4", - "sql-formatter": "^4.0.2", + "sql-formatter": "9.2.0", "uuid": "^8.3.2", "video-react": "^0.15.0" }, diff --git a/frontend/public/custom-chart-plugins/README.md b/frontend/public/custom-chart-plugins/README.md new file mode 100644 index 0000000000000000000000000000000000000000..ea46cd7bbe055be35b579398c28b8113e8d8383b --- /dev/null +++ b/frontend/public/custom-chart-plugins/README.md @@ -0,0 +1,9 @@ +# Welcome👏 + +**This folder host plugins charts, you can put plugin-charts here, then it will auto detect.** + +## Resouce + +[Github Plugin Charts Repository] + +[Gitee Plugin Charts Repository] diff --git a/frontend/src/__tests__/task.test.ts b/frontend/src/__tests__/task.test.ts index 344ca4457e988946c925af5af356aa8bc92fe1a7..894e8e976a848da305d050dd7df65aca6af4d25e 100644 --- a/frontend/src/__tests__/task.test.ts +++ b/frontend/src/__tests__/task.test.ts @@ -111,7 +111,7 @@ describe('Test getQueryData', () => { test('should get Chart Query data has computed', () => { const dataStr = JSON.stringify({ config: - '{"aggregation":true,"chartConfig":{"datas":[{"actions":{"NUMERIC":["aggregate","alias","format","sortable"],"STRING":["alias","sortable"],"DATE":["alias","sortable","dateLevel"]},"label":"mixed","key":"mixed","required":true,"type":"mixed","rows":[{"uid":"8ee5af74-3927-433f-806c-21469eafcdbd","field":"root.birthday","colName":"root.birthday(Year)","type":"DATE","category":"dateLevelComputedField","expression":"AGG_DATE_YEAR([root].[birthday])"},{"uid":"ac82d0ef-b162-4ec3-a534-4c48229c448b","colName":"root.age","type":"STRING","category":"field","children":[]},{"uid":"4090e82e-cc8a-4af1-b664-191a14067549","colName":"viewComputerField_age","type":"NUMERIC","category":"computedField","children":[],"aggregate":"SUM"},{"uid":"52a13c93-a529-4f9b-889c-9ec5c3825a79","colName":"root.salary","type":"NUMERIC","category":"field","children":[],"aggregate":"SUM"}]},{"label":"filter","key":"filter","type":"filter","disableAggregate":true}],"styles":[{"label":"header.title","key":"header","comType":"group","rows":[{"label":"header.open","key":"modal","comType":"group","options":{"type":"modal","modalSize":"middle"},"rows":[{"label":"header.styleAndGroup","key":"tableHeaders","comType":"tableHeader"}]}]},{"label":"column.conditionalStyle","key":"column","comType":"group","rows":[{"label":"column.open","key":"modal","comType":"group","options":{"type":"modal","modalSize":"middle"},"rows":[{"label":"column.list","key":"list","comType":"listTemplate","rows":[],"options":{},"template":{"label":"column.listItem","key":"listItem","comType":"group","rows":[{"label":"column.columnStyle","key":"columnStyle","comType":"group","options":{"expand":true},"rows":[{"label":"column.useColumnWidth","key":"useColumnWidth","default":false,"comType":"checkbox"},{"label":"column.columnWidth","key":"columnWidth","default":100,"options":{"min":0},"watcher":{"deps":["useColumnWidth"]},"comType":"inputNumber"},{"label":"style.align","key":"align","default":"default","comType":"fontAlignment","options":{"translateItemLabel":true,"items":[{"label":"@global@.style.alignDefault","value":"default"},{"label":"viz.common.enum.fontAlignment.left","value":"left"},{"label":"viz.common.enum.fontAlignment.center","value":"center"},{"label":"viz.common.enum.fontAlignment.right","value":"right"}]}}]},{"label":"column.conditionalStyle","key":"conditionalStyle","comType":"group","options":{"expand":true},"rows":[{"label":"column.conditionalStylePanel","key":"conditionalStylePanel","comType":"conditionalStylePanel"}]}]}}]}]},{"label":"style.title","key":"style","comType":"group","rows":[{"label":"style.enableFixedHeader","key":"enableFixedHeader","default":true,"comType":"checkbox","value":true},{"label":"style.enableBorder","key":"enableBorder","default":true,"comType":"checkbox","value":true},{"label":"style.enableRowNumber","key":"enableRowNumber","default":false,"comType":"checkbox","value":false},{"label":"style.leftFixedColumns","key":"leftFixedColumns","default":0,"options":{"min":0},"comType":"inputNumber","value":0},{"label":"style.rightFixedColumns","key":"rightFixedColumns","default":0,"options":{"min":0},"comType":"inputNumber","value":0},{"label":"style.autoMergeFields","key":"autoMergeFields","comType":"select","options":{"mode":"multiple"}},{"label":"style.tableSize","key":"tableSize","default":"small","comType":"select","options":{"translateItemLabel":true,"items":[{"label":"@global@.tableSize.default","value":"default"},{"label":"@global@.tableSize.middle","value":"middle"},{"label":"@global@.tableSize.small","value":"small"}]},"value":"small"}]},{"label":"style.tableHeaderStyle","key":"tableHeaderStyle","comType":"group","rows":[{"label":"common.backgroundColor","key":"bgColor","default":"#f8f9fa","comType":"fontColor","value":"#f8f9fa"},{"label":"style.font","key":"font","comType":"font","default":{"fontFamily":"-apple-system, \\"Segoe UI\\", Roboto, \\"Helvetica Neue\\", Arial, \\"Noto Sans\\", sans-serif, \\"Apple Color Emoji\\", \\"Segoe UI Emoji\\", \\"Segoe UI Symbol\\", \\"Noto Color Emoji\\"","fontSize":12,"fontWeight":"bold","fontStyle":"normal","color":"#495057"},"value":{"fontFamily":"-apple-system, \\"Segoe UI\\", Roboto, \\"Helvetica Neue\\", Arial, \\"Noto Sans\\", sans-serif, \\"Apple Color Emoji\\", \\"Segoe UI Emoji\\", \\"Segoe UI Symbol\\", \\"Noto Color Emoji\\"","fontSize":12,"fontWeight":"bold","fontStyle":"normal","color":"#495057"}},{"label":"style.align","key":"align","default":"left","comType":"fontAlignment","value":"left"}]},{"label":"style.tableBodyStyle","key":"tableBodyStyle","comType":"group","rows":[{"label":"style.oddFontColor","key":"oddFontColor","default":"#000","comType":"fontColor","value":"#000"},{"label":"style.oddBgColor","key":"oddBgColor","default":"rgba(0,0,0,0)","comType":"fontColor","value":"rgba(0,0,0,0)"},{"label":"style.evenFontColor","key":"evenFontColor","default":"#000","comType":"fontColor","value":"#000"},{"label":"style.evenBgColor","key":"evenBgColor","default":"rgba(0,0,0,0)","comType":"fontColor","value":"rgba(0,0,0,0)"},{"label":"style.fontSize","key":"fontSize","comType":"fontSize","default":12,"value":12},{"label":"style.fontFamily","key":"fontFamily","comType":"fontFamily","default":"-apple-system, \\"Segoe UI\\", Roboto, \\"Helvetica Neue\\", Arial, \\"Noto Sans\\", sans-serif, \\"Apple Color Emoji\\", \\"Segoe UI Emoji\\", \\"Segoe UI Symbol\\", \\"Noto Color Emoji\\"","value":"-apple-system, \\"Segoe UI\\", Roboto, \\"Helvetica Neue\\", Arial, \\"Noto Sans\\", sans-serif, \\"Apple Color Emoji\\", \\"Segoe UI Emoji\\", \\"Segoe UI Symbol\\", \\"Noto Color Emoji\\""},{"label":"style.fontWeight","key":"fontWeight","comType":"fontWeight","default":"normal","value":"normal"},{"label":"style.fontStyle","key":"fontStyle","comType":"fontStyle","default":"normal","value":"normal"},{"label":"style.align","key":"align","default":"default","comType":"fontAlignment","options":{"translateItemLabel":true,"items":[{"label":"@global@.style.alignDefault","value":"default"},{"label":"viz.common.enum.fontAlignment.left","value":"left"},{"label":"viz.common.enum.fontAlignment.center","value":"center"},{"label":"viz.common.enum.fontAlignment.right","value":"right"}]},"value":"default"}]}],"settings":[{"label":"paging.title","key":"paging","comType":"group","rows":[{"label":"paging.enablePaging","key":"enablePaging","default":true,"comType":"checkbox","options":{"needRefresh":true},"value":true},{"label":"paging.pageSize","key":"pageSize","default":100,"comType":"inputNumber","options":{"needRefresh":true,"step":1,"min":0},"watcher":{"deps":["enablePaging"]},"value":100}]},{"label":"summary.title","key":"summary","comType":"group","rows":[{"label":"summary.aggregateFields","key":"aggregateFields","comType":"select","options":{"mode":"multiple"}},{"label":"common.backgroundColor","key":"summaryBcColor","default":"rgba(0, 0, 0, 0)","comType":"fontColor","value":"rgba(0, 0, 0, 0)"},{"label":"viz.palette.style.font","key":"summaryFont","comType":"font","default":{"fontFamily":"-apple-system, \\"Segoe UI\\", Roboto, \\"Helvetica Neue\\", Arial, \\"Noto Sans\\", sans-serif, \\"Apple Color Emoji\\", \\"Segoe UI Emoji\\", \\"Segoe UI Symbol\\", \\"Noto Color Emoji\\"","fontSize":"14","fontWeight":"normal","fontStyle":"normal","color":"black"},"value":{"fontFamily":"-apple-system, \\"Segoe UI\\", Roboto, \\"Helvetica Neue\\", Arial, \\"Noto Sans\\", sans-serif, \\"Apple Color Emoji\\", \\"Segoe UI Emoji\\", \\"Segoe UI Symbol\\", \\"Noto Color Emoji\\"","fontSize":"14","fontWeight":"normal","fontStyle":"normal","color":"black"}}]}],"interactions":[{"label":"drillThrough.title","key":"drillThrough","comType":"checkboxModal","default":false,"options":{"modalSize":"middle"},"rows":[{"label":"drillThrough.title","key":"setting","comType":"interaction.drillThrough"}],"value":false},{"label":"viewDetail.title","key":"viewDetail","comType":"checkboxModal","default":false,"options":{"modalSize":"middle"},"rows":[{"label":"viewDetail.title","key":"setting","comType":"interaction.viewDetail"}],"value":false}],"i18ns":[{"lang":"zh-CN","translation":{"common":{"backgroundColor":"背景颜色"},"header":{"title":"表头分组","open":"打开","styleAndGroup":"表头分组"},"column":{"open":"打开样式设置","list":"字段列表","sortAndFilter":"排序与过滤","enableSort":"开启列排序","basicStyle":"基础样式","useColumnWidth":"启用固定列宽","columnWidth":"列宽","columnStyle":"列样式","columnStylePanel":"列样式配置器","conditionalStyle":"条件样式","conditionalStylePanel":"条件样式配置器","align":"对齐方式","enableFixedCol":"开启固定列宽","fixedColWidth":"固定列宽度设置","font":"字体与样式"},"style":{"title":"表格样式","enableFixedHeader":"固定表头","enableBorder":"显示边框","enableRowNumber":"启用行号","leftFixedColumns":"左侧固定列","rightFixedColumns":"右侧固定列","autoMergeFields":"自动合并列内容","tableSize":"表格大小","tableHeaderStyle":"表头样式","tableBodyStyle":"表体样式","bgColor":"背景颜色","font":"字体","align":"对齐方式","alignDefault":"默认","fontWeight":"字体粗细","fontFamily":"字体","oddBgColor":"奇行背景色","oddFontColor":"奇行字体色","evenBgColor":"偶行背景色","evenFontColor":"偶行字体色","fontSize":"字体大小","fontStyle":"字体样式"},"tableSize":{"default":"默认","middle":"中","small":"小"},"summary":{"title":"数据汇总","aggregateFields":"汇总列"},"paging":{"title":"常规","enablePaging":"启用分页","pageSize":"每页行数"}}},{"lang":"en-US","translation":{"common":{"backgroundColor":"Background Color"},"header":{"title":"Table Header Group","open":"Open","styleAndGroup":"Header Group"},"column":{"open":"Open Style Setting","list":"Field List","sortAndFilter":"Sort and Filter","enableSort":"Enable Sort","basicStyle":"Baisc Style","useColumnWidth":"Use Column Width","columnWidth":"Column Width","columnStyle":"Column Style","columnStylePanel":"Column Style Panel","conditionalStyle":"Conditional Style","conditionalStylePanel":"Conditional Style Panel","align":"Align","enableFixedCol":"Enable Fixed Column","fixedColWidth":"Fixed Column Width","font":"Font and Style"},"style":{"title":"Table Style","enableFixedHeader":"Enable Fixed Header","enableBorder":"Show Border","enableRowNumber":"Enable Row Number","leftFixedColumns":"LeftFixed Columns","rightFixedColumns":"Right Fixed Columns","autoMergeFields":"Auto Merge Column Content","tableSize":"Table Size","tableHeaderStyle":"Table Header Style","tableBodyStyle":"Table Body Style","font":"Font","align":"Align","alignDefault":"Default","fontWeight":"Font Weight","fontFamily":"Font Family","oddBgColor":"Odd Row Background Color","evenBgColor":"Even Row Background Color","oddFontColor":"Odd Row Font Color","evenFontColor":"Even Row Font Color","fontSize":"Font Size","fontStyle":"Font Style"},"tableSize":{"default":"Default","middle":"Middle","small":"Small"},"summary":{"title":"Summary","aggregateFields":"Summary Fields"},"paging":{"title":"Paging","enablePaging":"Enable Paging","pageSize":"Page Size"}}}]},"chartGraphId":"mingxi-table","computedFields":[{"category":"dateLevelComputedField","name":"root.birthday(Year)","type":"DATE","expression":"AGG_DATE_YEAR([root].[birthday])"}]}', + '{"aggregation":true,"chartConfig":{"datas":[{"actions":{"NUMERIC":["aggregate","alias","format","sortable"],"STRING":["alias","sortable"],"DATE":["alias","sortable","dateLevel"]},"label":"mixed","key":"mixed","required":true,"type":"mixed","rows":[{"uid":"8ee5af74-3927-433f-806c-21469eafcdbd","field":"root.birthday","colName":"root.birthday@date_level_delimiter@AGG_DATE_YEAR","type":"DATE","category":"dateLevelComputedField","expression":"AGG_DATE_YEAR([root].[birthday])"},{"uid":"ac82d0ef-b162-4ec3-a534-4c48229c448b","colName":"root.age","type":"STRING","category":"field","children":[]},{"uid":"4090e82e-cc8a-4af1-b664-191a14067549","colName":"viewComputerField_age","type":"NUMERIC","category":"computedField","children":[],"aggregate":"SUM"},{"uid":"52a13c93-a529-4f9b-889c-9ec5c3825a79","colName":"root.salary","type":"NUMERIC","category":"field","children":[],"aggregate":"SUM"}]},{"label":"filter","key":"filter","type":"filter","disableAggregate":true}],"styles":[{"label":"header.title","key":"header","comType":"group","rows":[{"label":"header.open","key":"modal","comType":"group","options":{"type":"modal","modalSize":"middle"},"rows":[{"label":"header.styleAndGroup","key":"tableHeaders","comType":"tableHeader"}]}]},{"label":"column.conditionalStyle","key":"column","comType":"group","rows":[{"label":"column.open","key":"modal","comType":"group","options":{"type":"modal","modalSize":"middle"},"rows":[{"label":"column.list","key":"list","comType":"listTemplate","rows":[],"options":{},"template":{"label":"column.listItem","key":"listItem","comType":"group","rows":[{"label":"column.columnStyle","key":"columnStyle","comType":"group","options":{"expand":true},"rows":[{"label":"column.useColumnWidth","key":"useColumnWidth","default":false,"comType":"checkbox"},{"label":"column.columnWidth","key":"columnWidth","default":100,"options":{"min":0},"watcher":{"deps":["useColumnWidth"]},"comType":"inputNumber"},{"label":"style.align","key":"align","default":"default","comType":"fontAlignment","options":{"translateItemLabel":true,"items":[{"label":"@global@.style.alignDefault","value":"default"},{"label":"viz.common.enum.fontAlignment.left","value":"left"},{"label":"viz.common.enum.fontAlignment.center","value":"center"},{"label":"viz.common.enum.fontAlignment.right","value":"right"}]}}]},{"label":"column.conditionalStyle","key":"conditionalStyle","comType":"group","options":{"expand":true},"rows":[{"label":"column.conditionalStylePanel","key":"conditionalStylePanel","comType":"conditionalStylePanel"}]}]}}]}]},{"label":"style.title","key":"style","comType":"group","rows":[{"label":"style.enableFixedHeader","key":"enableFixedHeader","default":true,"comType":"checkbox","value":true},{"label":"style.enableBorder","key":"enableBorder","default":true,"comType":"checkbox","value":true},{"label":"style.enableRowNumber","key":"enableRowNumber","default":false,"comType":"checkbox","value":false},{"label":"style.leftFixedColumns","key":"leftFixedColumns","default":0,"options":{"min":0},"comType":"inputNumber","value":0},{"label":"style.rightFixedColumns","key":"rightFixedColumns","default":0,"options":{"min":0},"comType":"inputNumber","value":0},{"label":"style.autoMergeFields","key":"autoMergeFields","comType":"select","options":{"mode":"multiple"}},{"label":"style.tableSize","key":"tableSize","default":"small","comType":"select","options":{"translateItemLabel":true,"items":[{"label":"@global@.tableSize.default","value":"default"},{"label":"@global@.tableSize.middle","value":"middle"},{"label":"@global@.tableSize.small","value":"small"}]},"value":"small"}]},{"label":"style.tableHeaderStyle","key":"tableHeaderStyle","comType":"group","rows":[{"label":"common.backgroundColor","key":"bgColor","default":"#f8f9fa","comType":"fontColor","value":"#f8f9fa"},{"label":"style.font","key":"font","comType":"font","default":{"fontFamily":"-apple-system, \\"Segoe UI\\", Roboto, \\"Helvetica Neue\\", Arial, \\"Noto Sans\\", sans-serif, \\"Apple Color Emoji\\", \\"Segoe UI Emoji\\", \\"Segoe UI Symbol\\", \\"Noto Color Emoji\\"","fontSize":12,"fontWeight":"bold","fontStyle":"normal","color":"#495057"},"value":{"fontFamily":"-apple-system, \\"Segoe UI\\", Roboto, \\"Helvetica Neue\\", Arial, \\"Noto Sans\\", sans-serif, \\"Apple Color Emoji\\", \\"Segoe UI Emoji\\", \\"Segoe UI Symbol\\", \\"Noto Color Emoji\\"","fontSize":12,"fontWeight":"bold","fontStyle":"normal","color":"#495057"}},{"label":"style.align","key":"align","default":"left","comType":"fontAlignment","value":"left"}]},{"label":"style.tableBodyStyle","key":"tableBodyStyle","comType":"group","rows":[{"label":"style.oddFontColor","key":"oddFontColor","default":"#000","comType":"fontColor","value":"#000"},{"label":"style.oddBgColor","key":"oddBgColor","default":"rgba(0,0,0,0)","comType":"fontColor","value":"rgba(0,0,0,0)"},{"label":"style.evenFontColor","key":"evenFontColor","default":"#000","comType":"fontColor","value":"#000"},{"label":"style.evenBgColor","key":"evenBgColor","default":"rgba(0,0,0,0)","comType":"fontColor","value":"rgba(0,0,0,0)"},{"label":"style.fontSize","key":"fontSize","comType":"fontSize","default":12,"value":12},{"label":"style.fontFamily","key":"fontFamily","comType":"fontFamily","default":"-apple-system, \\"Segoe UI\\", Roboto, \\"Helvetica Neue\\", Arial, \\"Noto Sans\\", sans-serif, \\"Apple Color Emoji\\", \\"Segoe UI Emoji\\", \\"Segoe UI Symbol\\", \\"Noto Color Emoji\\"","value":"-apple-system, \\"Segoe UI\\", Roboto, \\"Helvetica Neue\\", Arial, \\"Noto Sans\\", sans-serif, \\"Apple Color Emoji\\", \\"Segoe UI Emoji\\", \\"Segoe UI Symbol\\", \\"Noto Color Emoji\\""},{"label":"style.fontWeight","key":"fontWeight","comType":"fontWeight","default":"normal","value":"normal"},{"label":"style.fontStyle","key":"fontStyle","comType":"fontStyle","default":"normal","value":"normal"},{"label":"style.align","key":"align","default":"default","comType":"fontAlignment","options":{"translateItemLabel":true,"items":[{"label":"@global@.style.alignDefault","value":"default"},{"label":"viz.common.enum.fontAlignment.left","value":"left"},{"label":"viz.common.enum.fontAlignment.center","value":"center"},{"label":"viz.common.enum.fontAlignment.right","value":"right"}]},"value":"default"}]}],"settings":[{"label":"paging.title","key":"paging","comType":"group","rows":[{"label":"paging.enablePaging","key":"enablePaging","default":true,"comType":"checkbox","options":{"needRefresh":true},"value":true},{"label":"paging.pageSize","key":"pageSize","default":100,"comType":"inputNumber","options":{"needRefresh":true,"step":1,"min":0},"watcher":{"deps":["enablePaging"]},"value":100}]},{"label":"summary.title","key":"summary","comType":"group","rows":[{"label":"summary.aggregateFields","key":"aggregateFields","comType":"select","options":{"mode":"multiple"}},{"label":"common.backgroundColor","key":"summaryBcColor","default":"rgba(0, 0, 0, 0)","comType":"fontColor","value":"rgba(0, 0, 0, 0)"},{"label":"viz.palette.style.font","key":"summaryFont","comType":"font","default":{"fontFamily":"-apple-system, \\"Segoe UI\\", Roboto, \\"Helvetica Neue\\", Arial, \\"Noto Sans\\", sans-serif, \\"Apple Color Emoji\\", \\"Segoe UI Emoji\\", \\"Segoe UI Symbol\\", \\"Noto Color Emoji\\"","fontSize":"14","fontWeight":"normal","fontStyle":"normal","color":"black"},"value":{"fontFamily":"-apple-system, \\"Segoe UI\\", Roboto, \\"Helvetica Neue\\", Arial, \\"Noto Sans\\", sans-serif, \\"Apple Color Emoji\\", \\"Segoe UI Emoji\\", \\"Segoe UI Symbol\\", \\"Noto Color Emoji\\"","fontSize":"14","fontWeight":"normal","fontStyle":"normal","color":"black"}}]}],"interactions":[{"label":"drillThrough.title","key":"drillThrough","comType":"checkboxModal","default":false,"options":{"modalSize":"middle"},"rows":[{"label":"drillThrough.title","key":"setting","comType":"interaction.drillThrough"}],"value":false},{"label":"viewDetail.title","key":"viewDetail","comType":"checkboxModal","default":false,"options":{"modalSize":"middle"},"rows":[{"label":"viewDetail.title","key":"setting","comType":"interaction.viewDetail"}],"value":false}],"i18ns":[{"lang":"zh-CN","translation":{"common":{"backgroundColor":"背景颜色"},"header":{"title":"表头分组","open":"打开","styleAndGroup":"表头分组"},"column":{"open":"打开样式设置","list":"字段列表","sortAndFilter":"排序与过滤","enableSort":"开启列排序","basicStyle":"基础样式","useColumnWidth":"启用固定列宽","columnWidth":"列宽","columnStyle":"列样式","columnStylePanel":"列样式配置器","conditionalStyle":"条件样式","conditionalStylePanel":"条件样式配置器","align":"对齐方式","enableFixedCol":"开启固定列宽","fixedColWidth":"固定列宽度设置","font":"字体与样式"},"style":{"title":"表格样式","enableFixedHeader":"固定表头","enableBorder":"显示边框","enableRowNumber":"启用行号","leftFixedColumns":"左侧固定列","rightFixedColumns":"右侧固定列","autoMergeFields":"自动合并列内容","tableSize":"表格大小","tableHeaderStyle":"表头样式","tableBodyStyle":"表体样式","bgColor":"背景颜色","font":"字体","align":"对齐方式","alignDefault":"默认","fontWeight":"字体粗细","fontFamily":"字体","oddBgColor":"奇行背景色","oddFontColor":"奇行字体色","evenBgColor":"偶行背景色","evenFontColor":"偶行字体色","fontSize":"字体大小","fontStyle":"字体样式"},"tableSize":{"default":"默认","middle":"中","small":"小"},"summary":{"title":"数据汇总","aggregateFields":"汇总列"},"paging":{"title":"常规","enablePaging":"启用分页","pageSize":"每页行数"}}},{"lang":"en-US","translation":{"common":{"backgroundColor":"Background Color"},"header":{"title":"Table Header Group","open":"Open","styleAndGroup":"Header Group"},"column":{"open":"Open Style Setting","list":"Field List","sortAndFilter":"Sort and Filter","enableSort":"Enable Sort","basicStyle":"Baisc Style","useColumnWidth":"Use Column Width","columnWidth":"Column Width","columnStyle":"Column Style","columnStylePanel":"Column Style Panel","conditionalStyle":"Conditional Style","conditionalStylePanel":"Conditional Style Panel","align":"Align","enableFixedCol":"Enable Fixed Column","fixedColWidth":"Fixed Column Width","font":"Font and Style"},"style":{"title":"Table Style","enableFixedHeader":"Enable Fixed Header","enableBorder":"Show Border","enableRowNumber":"Enable Row Number","leftFixedColumns":"LeftFixed Columns","rightFixedColumns":"Right Fixed Columns","autoMergeFields":"Auto Merge Column Content","tableSize":"Table Size","tableHeaderStyle":"Table Header Style","tableBodyStyle":"Table Body Style","font":"Font","align":"Align","alignDefault":"Default","fontWeight":"Font Weight","fontFamily":"Font Family","oddBgColor":"Odd Row Background Color","evenBgColor":"Even Row Background Color","oddFontColor":"Odd Row Font Color","evenFontColor":"Even Row Font Color","fontSize":"Font Size","fontStyle":"Font Style"},"tableSize":{"default":"Default","middle":"Middle","small":"Small"},"summary":{"title":"Summary","aggregateFields":"Summary Fields"},"paging":{"title":"Paging","enablePaging":"Enable Paging","pageSize":"Page Size"}}}]},"chartGraphId":"mingxi-table","computedFields":[{"category":"dateLevelComputedField","name":"root.birthday(Year)","type":"DATE","expression":"AGG_DATE_YEAR([root].[birthday])"}]}', createBy: '06b6305528694968bc7085ecdabe4a5a', createTime: '2022-07-25 11:40:30', description: null, @@ -174,7 +174,10 @@ describe('Test getQueryData', () => { }, ], groups: [ - { alias: 'root.birthday(Year)', column: ['root.birthday(Year)'] }, + { + alias: 'root.birthday@date_level_delimiter@AGG_DATE_YEAR', + column: ['root.birthday@date_level_delimiter@AGG_DATE_YEAR'], + }, { alias: 'root.age', column: ['root', 'age'] }, ], filters: [], @@ -182,7 +185,7 @@ describe('Test getQueryData', () => { pageInfo: { countTotal: true, pageSize: 100 }, functionColumns: [ { - alias: 'root.birthday(Year)', + alias: 'root.birthday@date_level_delimiter@AGG_DATE_YEAR', snippet: 'AGG_DATE_YEAR([root].[birthday])', }, { alias: 'viewComputerField_age', snippet: '[root].[age]' }, diff --git a/frontend/src/app/assets/fonts/iconfont.css b/frontend/src/app/assets/fonts/iconfont.css index f49ff8f0a64608cec99e7e20461fd5cfa9aa29fc..329a3d8c4c5313afeca03ea41a70763d0001dc65 100644 --- a/frontend/src/app/assets/fonts/iconfont.css +++ b/frontend/src/app/assets/fonts/iconfont.css @@ -1,8 +1,8 @@ @font-face { font-family: 'iconfont'; /* Project id 2869064 */ - src: url('iconfont.woff2?t=1653903844873') format('woff2'), - url('iconfont.woff?t=1653903844873') format('woff'), - url('iconfont.ttf?t=1653903844873') format('truetype'); + src: url('iconfont.woff2?t=1661773321580') format('woff2'), + url('iconfont.woff?t=1661773321580') format('woff'), + url('iconfont.ttf?t=1661773321580') format('truetype'); } .iconfont { @@ -14,6 +14,110 @@ -moz-osx-font-smoothing: grayscale; } +.icon-HTTP:before { + content: '\e892'; +} + +.icon-ORACLE:before { + content: '\ec48'; +} + +.icon-SPARK:before { + content: '\ed2e'; +} + +.icon-a-3Dqipaoditu:before { + content: '\e7d6'; +} + +.icon-ditu:before { + content: '\e62d'; +} + +.icon-MYSQL:before { + content: '\ec6d'; +} + +.icon-DB2:before { + content: '\e610'; +} + +.icon-MSSQL:before { + content: '\e664'; +} + +.icon-BIG_QUERY:before { + content: '\e759'; +} + +.icon-NEOVIEW:before { + content: '\ebc0'; +} + +.icon-ACCESS:before { + content: '\ec16'; +} + +.icon-POSTGRESQL:before { + content: '\ec5d'; +} + +.icon-H2:before { + content: '\e6f6'; +} + +.icon-HIVE:before { + content: '\e891'; +} + +.icon-SYBASE:before { + content: '\e682'; +} + +.icon-INFORMIX:before { + content: '\e61c'; +} + +.icon-CLICKHOUSE:before { + content: '\e61d'; +} + +.icon-DERBY:before { + content: '\e61e'; +} + +.icon-DORIS:before { + content: '\e61f'; +} + +.icon-PRESTO:before { + content: '\e622'; +} + +.icon-SQLSTREAM:before { + content: '\e624'; +} + +.icon-PHOENIX:before { + content: '\e626'; +} + +.icon-NETEZZA:before { + content: '\e627'; +} + +.icon-IMPALA:before { + content: '\e629'; +} + +.icon-FIREBIRD:before { + content: '\e62a'; +} + +.icon-INTERBASE:before { + content: '\e62c'; +} + .icon-group-widget:before { content: '\e8b6'; } @@ -138,14 +242,6 @@ content: '\e7af'; } -.icon-fsux_tubiao_ditu:before { - content: '\e610'; -} - -.icon-ditu:before { - content: '\e72e'; -} - .icon-fsux_tubiao_ciyun:before { content: '\e60f'; } diff --git a/frontend/src/app/assets/fonts/iconfont.ttf b/frontend/src/app/assets/fonts/iconfont.ttf index f5c5eb4e5ee4f4ef3429b23082f954ad0a09943c..f36eea170ea7f14dfa41c9134b83989e8245ba8b 100644 Binary files a/frontend/src/app/assets/fonts/iconfont.ttf and b/frontend/src/app/assets/fonts/iconfont.ttf differ diff --git a/frontend/src/app/assets/fonts/iconfont.woff b/frontend/src/app/assets/fonts/iconfont.woff index b98b59e17fcaea75fa7d8a14e7c9e0238943fdc0..a2f3aca4e6bf0db298f273c4e9c3b9c0cd142393 100644 Binary files a/frontend/src/app/assets/fonts/iconfont.woff and b/frontend/src/app/assets/fonts/iconfont.woff differ diff --git a/frontend/src/app/assets/fonts/iconfont.woff2 b/frontend/src/app/assets/fonts/iconfont.woff2 index 6964a939a0299a29bf0b10e28da0043f8d520d49..63eb9a4532818b1ee728ec8d70dcfed8864466cf 100644 Binary files a/frontend/src/app/assets/fonts/iconfont.woff2 and b/frontend/src/app/assets/fonts/iconfont.woff2 differ diff --git a/frontend/src/app/components/ChartDrill/ChartDrillContextMenu.tsx b/frontend/src/app/components/ChartDrill/ChartDrillContextMenu.tsx index ec665e9a083076ab2f276c2601bfdbb9deceffd5..2731bc811e055f98fb4e4f44a91d879ed4de7155 100644 --- a/frontend/src/app/components/ChartDrill/ChartDrillContextMenu.tsx +++ b/frontend/src/app/components/ChartDrill/ChartDrillContextMenu.tsx @@ -28,6 +28,7 @@ import ChartDrillContext from 'app/contexts/ChartDrillContext'; import useI18NPrefix from 'app/hooks/useI18NPrefix'; import { DrillMode } from 'app/models/ChartDrillOption'; import DateLevelMenuItems from 'app/pages/ChartWorkbenchPage/components/ChartOperationPanel/components/ChartFieldAction/DateLevelAction/DateLevelMenuItems'; +import { handleDateLevelsName } from 'app/pages/ChartWorkbenchPage/components/ChartOperationPanel/utils'; import { ChartConfig, ChartDataSectionField } from 'app/types/ChartConfig'; import { ChartDataViewMeta } from 'app/types/ChartDataViewMeta'; import { getRuntimeDateLevelFields } from 'app/utils/chartHelper'; @@ -214,12 +215,19 @@ const ChartDrillContextMenu: FC<{ ].includes(f.category), ) ?.map((v, i) => { + const config = v[RUNTIME_DATE_LEVEL_KEY] || v; return ( - + diff --git a/frontend/src/app/components/ChartGraph/BasicBarChart/BasicBarChart.tsx b/frontend/src/app/components/ChartGraph/BasicBarChart/BasicBarChart.tsx index 1f1e51b28df96b9d826e287f6a80cbc6772eb452..adfebe76b2d8e847bd4a4d36bcd32106ab704c42 100644 --- a/frontend/src/app/components/ChartGraph/BasicBarChart/BasicBarChart.tsx +++ b/frontend/src/app/components/ChartGraph/BasicBarChart/BasicBarChart.tsx @@ -113,7 +113,7 @@ class BasicBarChart extends Chart implements IChartLifecycle { this.selectionManager.attachZRenderListeners(this.chart); this.selectionManager.attachEChartsListeners(this.chart); - // TODO(Stephen): refactor to chart data zoom manager model + // TODO(TL): refactor to chart data zoom manager model this.chart.on('datazoom', ({ end, start }) => { this.dataZoomConfig.showConfig = { end, diff --git a/frontend/src/app/components/ChartGraph/BasicLineChart/BasicLineChart.tsx b/frontend/src/app/components/ChartGraph/BasicLineChart/BasicLineChart.tsx index 95baf6185f29321b43a83c66bf122f1cc49675e4..8ae2ca14c3d2fad6ddde7d857c8d03086426f7ec 100644 --- a/frontend/src/app/components/ChartGraph/BasicLineChart/BasicLineChart.tsx +++ b/frontend/src/app/components/ChartGraph/BasicLineChart/BasicLineChart.tsx @@ -106,6 +106,8 @@ class BasicLineChart extends Chart { this.selectionManager.attachWindowListeners(context.window); this.selectionManager.attachZRenderListeners(this.chart); this.selectionManager.attachEChartsListeners(this.chart); + + // TODO(TL): refactor to chart data zoom manager model this.chart.on('datazoom', ({ end, start }) => { this.dataZoomConfig.showConfig = { end, diff --git a/frontend/src/app/components/ChartGraph/PivotSheetChart/AntVS2Wrapper.tsx b/frontend/src/app/components/ChartGraph/PivotSheetChart/AntVS2Wrapper.tsx index 8260eb6b6f83ff8cae1ef17d7d4e713abafa0b15..6d7ec021b4fbd56e5f352479c6268357cf2c4995 100644 --- a/frontend/src/app/components/ChartGraph/PivotSheetChart/AntVS2Wrapper.tsx +++ b/frontend/src/app/components/ChartGraph/PivotSheetChart/AntVS2Wrapper.tsx @@ -16,13 +16,17 @@ * limitations under the License. */ +import { setLang } from '@antv/s2'; import { SheetComponent } from '@antv/s2-react'; import '@antv/s2-react/dist/style.min.css'; +import { getLang } from 'locales/i18n'; import { FC, memo } from 'react'; import styled from 'styled-components/macro'; import { FONT_SIZE_LABEL } from 'styles/StyleConstants'; import { AndvS2Config } from './types'; +setLang(['zh_CN', 'en_US'].find(lang => lang.includes(getLang()!)) as any); + const AntVS2Wrapper: FC = memo( ({ dataCfg, diff --git a/frontend/src/app/components/ChartGraph/ScatterOutlineMapChart/ScatterOutlineMapChart.tsx b/frontend/src/app/components/ChartGraph/ScatterOutlineMapChart/ScatterOutlineMapChart.tsx index bbaca9e0c48949b64bef6f04baf1e05380ceca4a..169604b99162f1e988ad7a3a8a81624577a961db 100644 --- a/frontend/src/app/components/ChartGraph/ScatterOutlineMapChart/ScatterOutlineMapChart.tsx +++ b/frontend/src/app/components/ChartGraph/ScatterOutlineMapChart/ScatterOutlineMapChart.tsx @@ -27,7 +27,7 @@ class ScatterOutlineMapChart extends BasicOutlineMapChart { super({ id: 'scatter-outline-map-chart', name: 'viz.palette.graph.names.scatterOutlineMap', - icon: 'fsux_tubiao_ditu', + icon: 'a-3Dqipaoditu', requirements: [ { group: 1, diff --git a/frontend/src/app/components/FormGenerator/Customize/Interaction/BoardRelationList.tsx b/frontend/src/app/components/FormGenerator/Customize/Interaction/BoardRelationList.tsx index 1b272b24607055f43441363715a601f1d251f688..670e690087a656ba3eec06aae41fd13a73fde9a1 100644 --- a/frontend/src/app/components/FormGenerator/Customize/Interaction/BoardRelationList.tsx +++ b/frontend/src/app/components/FormGenerator/Customize/Interaction/BoardRelationList.tsx @@ -19,6 +19,7 @@ import { Button, Radio, Select, Table } from 'antd'; import { ColumnsType } from 'antd/lib/table'; import useMount from 'app/hooks/useMount'; +import { handleDateLevelsName } from 'app/pages/ChartWorkbenchPage/components/ChartOperationPanel/utils'; import { Widget } from 'app/pages/DashBoardPage/types/widgetTypes'; import { Variable } from 'app/pages/MainPage/pages/VariablePage/slice/types'; import ChartDataView from 'app/types/ChartDataView'; @@ -86,28 +87,38 @@ const BoardRelationList: FC< ); }; - const handleDeleteRelation = index => { - if (index > -1) { + const handleDeleteRelation = id => { + if (id) { const newRelations = updateBy(relations, draft => { - draft?.splice(index, 1); + const index = draft!.findIndex(v => v.id === id); + if (index > -1) { + draft?.splice(index, 1); + } }); onRelationChange(newRelations); } }; - const handleRelationChange = (index, key, value) => { - if (index > -1) { + const handleRelationChange = (id, key, value) => { + if (id) { const newRelations = updateBy(relations, draft => { - draft![index][key] = value; + const config = draft!.find(v => v.id === id); + config && (config[key] = value); }); onRelationChange(newRelations); } }; - const handleRelationTypeChange = (index, value) => { - if (index > -1) { + const handleRelationTypeChange = (id, value) => { + if (id) { const newRelations = updateBy(relations, draft => { - draft![index] = { id: uuidv4(), type: value }; + const index = draft!.findIndex(v => v.id === id); + if (index > -1) { + draft![index] = { + id: uuidv4(), + type: value, + }; + } }); onRelationChange(newRelations); } @@ -122,12 +133,12 @@ const BoardRelationList: FC< title: t('drillThrough.rule.relation.type'), dataIndex: 'type', key: 'type', - render: (value, _, index) => ( + render: (value, record) => ( handleRelationTypeChange(index, e.target.value)} + onChange={e => handleRelationTypeChange(record.id, e.target.value)} > {t('drillThrough.rule.relation.field')} @@ -142,15 +153,19 @@ const BoardRelationList: FC< title: t('drillThrough.rule.relation.source'), dataIndex: 'source', key: 'source', - render: (value, record, index) => ( + render: (value, record) => ( ), @@ -159,15 +174,19 @@ const BoardRelationList: FC< title: t('drillThrough.rule.relation.target'), dataIndex: 'target', key: 'target', - render: (value, record, index) => ( + render: (value, record) => ( ), @@ -175,8 +194,8 @@ const BoardRelationList: FC< { key: 'operation', width: 50, - render: (_1, _2, index) => ( - ), diff --git a/frontend/src/app/components/FormGenerator/Customize/Interaction/ChartRelationList.tsx b/frontend/src/app/components/FormGenerator/Customize/Interaction/ChartRelationList.tsx index dffc6145349c691cdc5bdec3fcc76828bc856a35..e726e1ea754e4deb5dcdad6d25dc9b2185298ab2 100644 --- a/frontend/src/app/components/FormGenerator/Customize/Interaction/ChartRelationList.tsx +++ b/frontend/src/app/components/FormGenerator/Customize/Interaction/ChartRelationList.tsx @@ -19,6 +19,7 @@ import { Button, Radio, Select, Table } from 'antd'; import { ColumnsType } from 'antd/lib/table'; import useMount from 'app/hooks/useMount'; +import { handleDateLevelsName } from 'app/pages/ChartWorkbenchPage/components/ChartOperationPanel/utils'; import { Variable } from 'app/pages/MainPage/pages/VariablePage/slice/types'; import { ChartDataViewMeta } from 'app/types/ChartDataViewMeta'; import { @@ -76,28 +77,38 @@ const ChartRelationList: FC< ); }; - const handleDeleteRelation = index => { - if (index > -1) { + const handleDeleteRelation = id => { + if (id) { const newRelations = updateBy(relations, draft => { - draft?.splice(index, 1); + const index = draft!.findIndex(v => v.id === id); + if (index > -1) { + draft?.splice(index, 1); + } }); onRelationChange(newRelations); } }; - const handleRelationChange = (index, key, value) => { - if (index > -1) { + const handleRelationChange = (id, key, value) => { + if (id) { const newRelations = updateBy(relations, draft => { - draft![index][key] = value; + const config = draft!.find(v => v.id === id); + config && (config[key] = value); }); onRelationChange(newRelations); } }; - const handleRelationTypeChange = (index, value) => { - if (index > -1) { + const handleRelationTypeChange = (id, value) => { + if (id) { const newRelations = updateBy(relations, draft => { - draft![index] = { id: uuidv4(), type: value }; + const index = draft!.findIndex(v => v.id === id); + if (index > -1) { + draft![index] = { + id: uuidv4(), + type: value, + }; + } }); onRelationChange(newRelations); } @@ -112,12 +123,12 @@ const ChartRelationList: FC< title: t('drillThrough.rule.relation.type'), dataIndex: 'type', key: 'type', - render: (value, _, index) => ( + render: (value, record) => ( handleRelationTypeChange(index, e.target.value)} + onChange={e => handleRelationTypeChange(record.id, e.target.value)} > {t('drillThrough.rule.relation.field')} @@ -132,15 +143,19 @@ const ChartRelationList: FC< title: t('drillThrough.rule.relation.source'), dataIndex: 'source', key: 'source', - render: (value, record, index) => ( + render: (value, record) => ( ), @@ -149,15 +164,19 @@ const ChartRelationList: FC< title: t('drillThrough.rule.relation.target'), dataIndex: 'target', key: 'target', - render: (value, record, index) => ( + render: (value, record) => ( ), @@ -165,8 +184,8 @@ const ChartRelationList: FC< { key: 'operation', width: 50, - render: (_1, _2, index) => ( - ), diff --git a/frontend/src/app/components/FormGenerator/Customize/Interaction/ControllerList.tsx b/frontend/src/app/components/FormGenerator/Customize/Interaction/ControllerList.tsx index b58b75689a56eeda394c65b41ec0d64b60c6beba..c883b6abccc5796412903163e5f8af575c786470 100644 --- a/frontend/src/app/components/FormGenerator/Customize/Interaction/ControllerList.tsx +++ b/frontend/src/app/components/FormGenerator/Customize/Interaction/ControllerList.tsx @@ -19,6 +19,7 @@ import { Button, Radio, Select, Table } from 'antd'; import { ColumnsType } from 'antd/lib/table'; import useMount from 'app/hooks/useMount'; +import { handleDateLevelsName } from 'app/pages/ChartWorkbenchPage/components/ChartOperationPanel/utils'; import { ChartDataViewMeta } from 'app/types/ChartDataViewMeta'; import { fetchDashboardDetail } from 'app/utils/fetch'; import { updateBy } from 'app/utils/mutation'; @@ -27,7 +28,6 @@ import styled from 'styled-components/macro'; import { uuidv4 } from 'utils/utils'; import { InteractionRelationType } from '../../constants'; import { CustomizeRelation, I18nTranslator } from './types'; - const ControllerList: FC< { targetRelId?: string; @@ -66,28 +66,38 @@ const ControllerList: FC< ); }; - const handleDeleteRelation = index => { - if (index > -1) { + const handleDeleteRelation = id => { + if (id) { const newRelations = updateBy(relations, draft => { - draft?.splice(index, 1); + const index = draft!.findIndex(v => v.id === id); + if (index > -1) { + draft?.splice(index, 1); + } }); onRelationChange(newRelations); } }; - const handleRelationChange = (index, key, value) => { - if (index > -1) { + const handleRelationChange = (id, key, value) => { + if (id) { const newRelations = updateBy(relations, draft => { - draft![index][key] = value; + const config = draft!.find(v => v.id === id); + config && (config[key] = value); }); onRelationChange(newRelations); } }; - const handleRelationTypeChange = (index, value) => { - if (index > -1) { + const handleRelationTypeChange = (id, value) => { + if (id) { const newRelations = updateBy(relations, draft => { - draft![index] = { id: uuidv4(), type: value }; + const index = draft!.findIndex(v => v.id === id); + if (index > -1) { + draft![index] = { + id: uuidv4(), + type: value, + }; + } }); onRelationChange(newRelations); } @@ -102,12 +112,12 @@ const ControllerList: FC< title: t('drillThrough.rule.relation.type'), dataIndex: 'type', key: 'type', - render: (value, _, index) => ( + render: (value, record) => ( handleRelationTypeChange(index, e.target.value)} + onChange={e => handleRelationTypeChange(record.id, e.target.value)} > {t('drillThrough.rule.relation.field')} @@ -122,15 +132,19 @@ const ControllerList: FC< title: t('drillThrough.rule.relation.source'), dataIndex: 'source', key: 'source', - render: (value, record, index) => ( + render: (value, record) => ( ), @@ -139,11 +153,11 @@ const ControllerList: FC< title: t('drillThrough.rule.relation.controller'), dataIndex: 'target', key: 'target', - render: (value, record, index) => ( + render: (value, record) => ( onValueChange({ ...value, ...{ relId } })} diff --git a/frontend/src/app/components/FormGenerator/Customize/Interaction/JumpToDashboard.tsx b/frontend/src/app/components/FormGenerator/Customize/Interaction/JumpToDashboard.tsx index 464a427f317932b709423bfb5dbecb9151497c5f..d5d33c7221abe75aea137da7d040632d00243826 100644 --- a/frontend/src/app/components/FormGenerator/Customize/Interaction/JumpToDashboard.tsx +++ b/frontend/src/app/components/FormGenerator/Customize/Interaction/JumpToDashboard.tsx @@ -48,6 +48,11 @@ const JumpToDashboard: FC< return ( handleRelationChange(index, 'source', value)} + onChange={value => handleRelationChange(record.id, 'source', value)} dropdownMatchSelectWidth={false} > {(isFieldType(record) ? sourceFields : sourceVariables)?.map(sf => { - return {sf?.name}; + return ( + + {handleDateLevelsName(sf)} + + ); })} ), @@ -121,18 +136,20 @@ const UrlParamList: FC< title: t('drillThrough.rule.relation.target'), dataIndex: 'target', key: 'target', - render: (value, record, index) => ( + render: (value, record) => ( handleRelationChange(index, 'target', e.target.value)} + onChange={e => + handleRelationChange(record.id, 'target', e.target.value) + } /> ), }, { key: 'operation', width: 50, - render: (_1, _2, index) => ( - ), diff --git a/frontend/src/app/components/FormGenerator/Customize/Interaction/ViewDetailPanel.tsx b/frontend/src/app/components/FormGenerator/Customize/Interaction/ViewDetailPanel.tsx index 4fc8e47917471c3b484b3506f15e6a55e1e2a44e..70fa3d38e8150076f1503ddec985d93e55422cd0 100644 --- a/frontend/src/app/components/FormGenerator/Customize/Interaction/ViewDetailPanel.tsx +++ b/frontend/src/app/components/FormGenerator/Customize/Interaction/ViewDetailPanel.tsx @@ -107,6 +107,7 @@ const ViewDetailPanel: FC> = memo( + + ); +} + +const TreeSetterWrapper = styled.div``; + +export default TreeSetter; diff --git a/frontend/src/app/pages/DashBoardPage/pages/BoardEditor/components/ControllerWidgetPanel/ControllerConfig/ValuesSetter/TreeSetter/index.tsx b/frontend/src/app/pages/DashBoardPage/pages/BoardEditor/components/ControllerWidgetPanel/ControllerConfig/ValuesSetter/TreeSetter/index.tsx new file mode 100644 index 0000000000000000000000000000000000000000..d87775837583b2a0cfe490d55ef86b6542d3237f --- /dev/null +++ b/frontend/src/app/pages/DashBoardPage/pages/BoardEditor/components/ControllerWidgetPanel/ControllerConfig/ValuesSetter/TreeSetter/index.tsx @@ -0,0 +1,20 @@ +/** + * Datart + * + * Copyright 2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import TreeSetter from './TreeSetter'; + +export default TreeSetter; diff --git a/frontend/src/app/pages/DashBoardPage/pages/BoardEditor/components/ControllerWidgetPanel/ControllerConfig/ValuesSetter/TreeTypeSetter/TreeTypeSetter.tsx b/frontend/src/app/pages/DashBoardPage/pages/BoardEditor/components/ControllerWidgetPanel/ControllerConfig/ValuesSetter/TreeTypeSetter/TreeTypeSetter.tsx new file mode 100644 index 0000000000000000000000000000000000000000..5c7eecb04a95ae9e7835453025f5497f92d3ef5c --- /dev/null +++ b/frontend/src/app/pages/DashBoardPage/pages/BoardEditor/components/ControllerWidgetPanel/ControllerConfig/ValuesSetter/TreeTypeSetter/TreeTypeSetter.tsx @@ -0,0 +1,73 @@ +/** + * Datart + * + * Copyright 2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { QuestionCircleOutlined } from '@ant-design/icons'; +import { Form, FormInstance, Radio, Tooltip } from 'antd'; +import useI18NPrefix from 'app/hooks/useI18NPrefix'; +import { ControllerWidgetContent } from 'app/pages/DashBoardPage/pages/Board/slice/types'; +import { useCallback } from 'react'; +import { ControllerConfig } from '../../../types'; + +export interface TreeTypeSetterProps { + form: FormInstance | undefined; +} + +function TreeTypeSetter({ form }: TreeTypeSetterProps) { + const t = useI18NPrefix('viz.control'); + + const getControllerConfig = useCallback(() => { + return form?.getFieldValue('config') as ControllerConfig; + }, [form]); + + const handleChangeFn = useCallback(() => { + form?.setFieldsValue({ + config: { + ...getControllerConfig, + parentField: undefined, + controllerValues: [], + valueOptions: [], + assistViewFields: [], + }, + }); + }, [form, getControllerConfig]); + + return ( + + + + {t('treeControl') + ' '} + + + + + + + {t('treeSelect') + ' '} + + + + + + + ); +} + +export default TreeTypeSetter; diff --git a/frontend/src/app/pages/DashBoardPage/pages/BoardEditor/components/ControllerWidgetPanel/ControllerConfig/ValuesSetter/TreeTypeSetter/index.tsx b/frontend/src/app/pages/DashBoardPage/pages/BoardEditor/components/ControllerWidgetPanel/ControllerConfig/ValuesSetter/TreeTypeSetter/index.tsx new file mode 100644 index 0000000000000000000000000000000000000000..cff87028dbfd435c0916bafb0b07739b9aefc960 --- /dev/null +++ b/frontend/src/app/pages/DashBoardPage/pages/BoardEditor/components/ControllerWidgetPanel/ControllerConfig/ValuesSetter/TreeTypeSetter/index.tsx @@ -0,0 +1,20 @@ +/** + * Datart + * + * Copyright 2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import TreeTypeSetter from './TreeTypeSetter'; + +export default TreeTypeSetter; diff --git a/frontend/src/app/pages/DashBoardPage/pages/BoardEditor/components/ControllerWidgetPanel/ControllerConfig/ValuesSetter/ValuesOptionsSetter/CustomOptions.tsx b/frontend/src/app/pages/DashBoardPage/pages/BoardEditor/components/ControllerWidgetPanel/ControllerConfig/ValuesSetter/ValuesOptionsSetter/CustomOptions.tsx index 9cae2291677445cdcc513bf6f27d35577319390e..9eadbefc8d72ad895f6fc756bb23c4bafeff7369 100644 --- a/frontend/src/app/pages/DashBoardPage/pages/BoardEditor/components/ControllerWidgetPanel/ControllerConfig/ValuesSetter/ValuesOptionsSetter/CustomOptions.tsx +++ b/frontend/src/app/pages/DashBoardPage/pages/BoardEditor/components/ControllerWidgetPanel/ControllerConfig/ValuesSetter/ValuesOptionsSetter/CustomOptions.tsx @@ -37,6 +37,7 @@ export const CustomOptions: React.FC = memo( setRows(rows); const config = getControllerConfig(); const valueOptions = [...rows.slice()]; + form?.setFieldsValue({ config: { ...config, @@ -56,7 +57,12 @@ export const CustomOptions: React.FC = memo( (row: RelationFilterValue) => { const newRows = [...rows]; const targetIndex = newRows.findIndex(r => r.index === row.index); - newRows.splice(targetIndex, 1, row); + + if (row.childIndex !== undefined) { + newRows[targetIndex]?.children?.splice(row.childIndex, 1, row); + } else { + newRows.splice(targetIndex, 1, row); + } onChangeFilterOptions(newRows); }, [onChangeFilterOptions, rows], @@ -132,6 +138,7 @@ export const CustomOptions: React.FC = memo( if (!col.editable) { return col; } + return { ...col, onCell: (record: RelationFilterValue) => ({ diff --git a/frontend/src/app/pages/DashBoardPage/pages/BoardEditor/components/ControllerWidgetPanel/ControllerConfig/ValuesSetter/ValuesOptionsSetter/ValuesOptionsSetter.tsx b/frontend/src/app/pages/DashBoardPage/pages/BoardEditor/components/ControllerWidgetPanel/ControllerConfig/ValuesSetter/ValuesOptionsSetter/ValuesOptionsSetter.tsx index 7a013198a5f93185398a16a688d0633c12e04953..74f95d601b491e631a1276eb73ad31785ea465ad 100644 --- a/frontend/src/app/pages/DashBoardPage/pages/BoardEditor/components/ControllerWidgetPanel/ControllerConfig/ValuesSetter/ValuesOptionsSetter/ValuesOptionsSetter.tsx +++ b/frontend/src/app/pages/DashBoardPage/pages/BoardEditor/components/ControllerWidgetPanel/ControllerConfig/ValuesSetter/ValuesOptionsSetter/ValuesOptionsSetter.tsx @@ -16,7 +16,15 @@ * limitations under the License. */ -import { Form, FormInstance, Radio, Select, Space } from 'antd'; +import { + Form, + FormInstance, + Radio, + Select, + Space, + Tree, + TreeSelect, +} from 'antd'; import { CascaderOptionType } from 'antd/lib/cascader'; import { ControllerFacadeTypes } from 'app/constants'; import useI18NPrefix from 'app/hooks/useI18NPrefix'; @@ -45,7 +53,9 @@ import { } from 'react'; import styled from 'styled-components/macro'; import { request2 } from 'utils/request'; +import { convertToTree } from '../../../../../../../utils/widget'; import { ControllerConfig } from '../../../types'; +import TreeSetter from '../TreeSetter'; import { AssistViewFields } from './AssistViewFields'; import { CustomOptions } from './CustomOptions'; @@ -69,6 +79,10 @@ const ValuesOptionsSetter: FC<{ const [labelKey, setLabelKey] = useState(); const [viewList, setViewList] = useState([]); + const isTree = useMemo(() => { + return controllerType === ControllerFacadeTypes.DropDownTree; + }, [controllerType]); + const getViewList = useCallback(async orgId => { const { data } = await request2(`/views?orgId=${orgId}`); const views = data.map(item => { @@ -84,8 +98,19 @@ const ValuesOptionsSetter: FC<{ return form?.getFieldValue('config') as ControllerConfig; }, [form]); + const getParentField = useCallback(() => { + return form?.getFieldValue('config')?.parentField as string[]; + }, [form]); + + const getTreeType = useCallback(() => { + return form?.getFieldValue('config')?.treeType as string; + }, [form]); + const isMultiple = useMemo(() => { - return controllerType === ControllerFacadeTypes.MultiDropdownList; + return ( + controllerType === ControllerFacadeTypes.MultiDropdownList || + controllerType === ControllerFacadeTypes.DropDownTree + ); }, [controllerType]); const convertToList = useCallback(collection => { @@ -136,14 +161,15 @@ const ValuesOptionsSetter: FC<{ const onTargetKeyChange = useCallback( nextTargetKeys => { - setTargetKeys(nextTargetKeys); - const nextControllerOpt: ControllerConfig = { - ...getControllerConfig(), - controllerValues: nextTargetKeys, - }; + const config: ControllerConfig = getControllerConfig(); + form?.setFieldsValue({ - config: nextControllerOpt, + config: { + ...config, + controllerValues: nextTargetKeys, + }, }); + setTargetKeys(nextTargetKeys); }, [form, getControllerConfig], ); @@ -161,17 +187,51 @@ const ValuesOptionsSetter: FC<{ [], ); + const getViewData = useCallback( + async (value: string[], type?) => { + const { option: options, dataView } = await getViewOption(value[0]); + setLabelOptions(options); + + if (type !== 'view') { + const [viewId, ...columns] = value; + const parentField = getParentField(); + const treeType = getTreeType(); + + if (parentField) { + columns.push(...parentField); + } + const dataset = await fetchNewDataset(viewId, columns, dataView); + + setOptionValues( + parentField?.length > 0 + ? convertToTree(dataset?.rows, treeType) + : convertToList(dataset?.rows), + ); + } + }, + [ + convertToList, + fetchNewDataset, + getParentField, + getViewOption, + getTreeType, + ], + ); + const onViewFieldChange = useCallback( async (value: string[], type?) => { setOptionValues([]); setTargetKeys([]); setLabelOptions([]); + setLabelKey(undefined); + const config = getControllerConfig(); if (!value || !value?.[0]) { form?.setFieldsValue({ config: { - ...getControllerConfig(), + ...config, assistViewFields: [], controllerValues: [], + parentField: undefined, }, }); return; @@ -179,22 +239,15 @@ const ValuesOptionsSetter: FC<{ form?.setFieldsValue({ config: { - ...getControllerConfig(), + ...config, assistViewFields: value, controllerValues: [], + parentField: undefined, }, }); - - const { option: options, dataView } = await getViewOption(value[0]); - setLabelOptions(options); - - if (type !== 'view') { - const [viewId, ...columns] = value; - const dataset = await fetchNewDataset(viewId, columns, dataView); - setOptionValues(convertToList(dataset?.rows)); - } + getViewData(value, type); }, - [convertToList, fetchNewDataset, form, getControllerConfig, getViewOption], + [form, getControllerConfig, getViewData], ); const onLabelChange = useCallback( @@ -212,19 +265,27 @@ const ValuesOptionsSetter: FC<{ form?.setFieldsValue({ config: nextControllerOpt, }); - onViewFieldChange(nextAssistViewFields); + getViewData(nextAssistViewFields); }, - [form, getControllerConfig, onViewFieldChange], + [form, getControllerConfig, getViewData], ); const onInitOptions = useCallback( - async (value: string[]) => { + async (value: string[], parentField?: string[]) => { const [viewId, ...columns] = value; const { option: options, dataView } = await getViewOption(viewId); + if (parentField) { + columns.push(...parentField); + } const dataset = await fetchNewDataset(viewId, columns, dataView); const config: ControllerConfig = getControllerConfig(); + const treeType = getTreeType(); - setOptionValues(convertToList(dataset?.rows)); + setOptionValues( + parentField + ? convertToTree(dataset?.rows, treeType) + : convertToList(dataset?.rows), + ); setLabelOptions(options); if (config.valueOptionType === 'common') { @@ -234,7 +295,13 @@ const ValuesOptionsSetter: FC<{ } } }, - [convertToList, fetchNewDataset, getControllerConfig, getViewOption], + [ + convertToList, + fetchNewDataset, + getControllerConfig, + getViewOption, + getTreeType, + ], ); const updateOptions = useCallback(() => { @@ -246,8 +313,9 @@ const ValuesOptionsSetter: FC<{ } const assistViewFields = config?.assistViewFields; + const parentField = config?.parentField; if (assistViewFields && assistViewFields[0] && assistViewFields[1]) { - onInitOptions(assistViewFields); + onInitOptions(assistViewFields, parentField); } }, [form, getControllerConfig, onInitOptions]); @@ -255,6 +323,20 @@ const ValuesOptionsSetter: FC<{ return getControllerConfig()?.valueOptionType as ValueOptionType; }, [getControllerConfig]); + const onParentFieldChange = useCallback( + (val: string | string[]) => { + const config: ControllerConfig = getControllerConfig(); + form?.setFieldsValue({ + config: { + ...config, + parentField: Array.isArray(val) ? val : [val], + }, + }); + getViewData(config.assistViewFields || []); + }, + [getControllerConfig, getViewData, form], + ); + useEffect(() => { setTimeout(() => { updateOptions(); @@ -300,51 +382,106 @@ const ValuesOptionsSetter: FC<{ style={{ margin: '6px 0' }} /> + {isTree && ( + + + + )} + {getOptionType() === 'common' && ( - - + {item.label} + + ))} + + )} + + {getParentField()?.length ? ( + { + return ( + + onTargetKeyChange(checkedObj?.checked) + } + checkable + checkStrictly + titleRender={node => { + return ( +
+ {node.title || node.key} + {node.key} +
+ ); + }} + treeData={optionValues} + /> + ); + }} + /> + ) : ( + + )} +
)} {getOptionType() === 'custom' && ( @@ -374,3 +511,12 @@ const Wrapper = styled.div` const FieldKey = styled.span` color: ${p => p.theme.textColorDisabled}; `; + +const TreeSelectProps = styled(Tree)` + .ant-tree-node-content-wrapper { + width: 100%; + } + .ant-tree-treenode { + width: 100%; + } +`; diff --git a/frontend/src/app/pages/DashBoardPage/pages/BoardEditor/components/ControllerWidgetPanel/ControllerConfig/ValuesSetter/ValuesSetter.tsx b/frontend/src/app/pages/DashBoardPage/pages/BoardEditor/components/ControllerWidgetPanel/ControllerConfig/ValuesSetter/ValuesSetter.tsx index f4a85256bf211890f5f37d197d44d259d35965f5..3c4e46cc08aab8491df1c5c0d0726ea16ae4259e 100644 --- a/frontend/src/app/pages/DashBoardPage/pages/BoardEditor/components/ControllerWidgetPanel/ControllerConfig/ValuesSetter/ValuesSetter.tsx +++ b/frontend/src/app/pages/DashBoardPage/pages/BoardEditor/components/ControllerWidgetPanel/ControllerConfig/ValuesSetter/ValuesSetter.tsx @@ -80,6 +80,7 @@ export const ValuesSetter: React.FC<{ showMarks: config?.sliderConfig?.showMarks || false, }; }; + return ( <> + {valueType !== VariableValueTypes.Expression && ( - (d as Moment).format(TIME_FORMATTER), + (d as Moment).format(values.dateFormat), ); } @@ -226,14 +226,19 @@ export function VariablePage() { const saveRelations = useCallback( async (changedRowPermissions: RowPermission[]) => { - let changedRowPermissionsRaw = changedRowPermissions.map(cr => ({ - ...cr, - value: - cr.value && - (editingVariable?.valueType === VariableValueTypes.Date - ? cr.value.map(m => (m as Moment).format(TIME_FORMATTER)) - : cr.value), - })); + let changedRowPermissionsRaw = changedRowPermissions.map(cr => { + const dateFormat = + variables.find(v => v.id === cr.variableId)?.dateFormat || + TIME_FORMATTER; + return { + ...cr, + value: + cr.value && + (editingVariable?.valueType === VariableValueTypes.Date + ? cr.value.map(m => (m as Moment).format(dateFormat)) + : cr.value), + }; + }); if (rowPermissions) { const { created, updated, deleted } = getDiffParams( @@ -270,7 +275,7 @@ export function VariablePage() { } } }, - [rowPermissions, editingVariable, tg], + [rowPermissions, editingVariable, tg, variables], ); const columns: TableColumnProps[] = useMemo( diff --git a/frontend/src/app/pages/MainPage/pages/VariablePage/slice/types.ts b/frontend/src/app/pages/MainPage/pages/VariablePage/slice/types.ts index fdd76cb91d2ea98af23d8517a2360bd4d8b249d9..30a92d80a316db3cc57b7dbe48e60549231ec08d 100644 --- a/frontend/src/app/pages/MainPage/pages/VariablePage/slice/types.ts +++ b/frontend/src/app/pages/MainPage/pages/VariablePage/slice/types.ts @@ -16,6 +16,7 @@ * limitations under the License. */ +import { DateFormat } from 'app/constants'; import { SubjectTypes } from '../../PermissionPage/constants'; import { VariableTypes, VariableValueTypes } from '../constants'; @@ -42,6 +43,7 @@ export interface Variable { createTime?: string; updateBy?: string; updateTime?: string; + dateFormat?: DateFormat; } export interface VariableViewModel extends Variable { diff --git a/frontend/src/app/pages/MainPage/pages/ViewPage/Main/Editor/Toolbar.tsx b/frontend/src/app/pages/MainPage/pages/ViewPage/Main/Editor/Toolbar.tsx index fe288fe12b352fcd6bf7146c004106a2cafaae0d..532555abbf9119a9e3561d772d1bc2c53492b024 100644 --- a/frontend/src/app/pages/MainPage/pages/ViewPage/Main/Editor/Toolbar.tsx +++ b/frontend/src/app/pages/MainPage/pages/ViewPage/Main/Editor/Toolbar.tsx @@ -122,7 +122,10 @@ export const Toolbar = memo( const formatSQL = useCallback(() => { dispatch( actions.changeCurrentEditingView({ - script: format(script), + script: format(script, { + denseOperators: true, + logicalOperatorNewline: 'before', + }), }), ); }, [dispatch, actions, script]); diff --git a/frontend/src/app/pages/MainPage/pages/ViewPage/Main/Outputs/Results.tsx b/frontend/src/app/pages/MainPage/pages/ViewPage/Main/Outputs/Results.tsx index fa41dd33cea4031b43578dd9401a3dc1e0203cc6..da394d0c8b4352f02d6fed1e9a49a1d171193c8d 100644 --- a/frontend/src/app/pages/MainPage/pages/ViewPage/Main/Outputs/Results.tsx +++ b/frontend/src/app/pages/MainPage/pages/ViewPage/Main/Outputs/Results.tsx @@ -81,13 +81,15 @@ export const Results = memo(({ height = 0, width = 0 }: ResultsProps) => { const modelChange = useCallback( (columnName: string, column: Omit) => - ({ key }) => { + (keyPath: string[]) => { let value; - if (key.includes('category')) { - const category = key.split('-')[1]; + if (keyPath[0].includes('category')) { + const category = keyPath[0].split('-')[1]; value = { ...column, category }; + } else if (keyPath.includes('DATE')) { + value = { ...column, type: keyPath[1], dateFormat: keyPath[0] }; } else { - value = { ...column, type: key }; + value = { ...column, type: keyPath[0] }; } const clonedHierarchyModel = CloneValueDeep(model.hierarchy || {}); if (columnName in clonedHierarchyModel) { diff --git a/frontend/src/app/pages/MainPage/pages/ViewPage/Main/Properties/DataModelTree/DataModelBranch.tsx b/frontend/src/app/pages/MainPage/pages/ViewPage/Main/Properties/DataModelTree/DataModelBranch.tsx index ae5d16ffab702c2359126f7dcd419a6c790c0c55..aa6bd59690565d4c79800b6238c2974f0de2e5e5 100644 --- a/frontend/src/app/pages/MainPage/pages/ViewPage/Main/Properties/DataModelTree/DataModelBranch.tsx +++ b/frontend/src/app/pages/MainPage/pages/ViewPage/Main/Properties/DataModelTree/DataModelBranch.tsx @@ -43,7 +43,7 @@ import DataModelNode from './DataModelNode'; const DataModelBranch: FC<{ node: Column; - onNodeTypeChange: (type: any, name: string) => void; + onNodeTypeChange: (type: string[], name: string) => void; onMoveToHierarchy: (node: Column) => void; onEditBranch; onDelete: (node: Column) => void; diff --git a/frontend/src/app/pages/MainPage/pages/ViewPage/Main/Properties/DataModelTree/DataModelNode.tsx b/frontend/src/app/pages/MainPage/pages/ViewPage/Main/Properties/DataModelTree/DataModelNode.tsx index 78dbbb246ec17b17e6ce5c41c7f5724bded1db27..8c8f2beecc01dcba7b90136547e0e0c40f9f7843 100644 --- a/frontend/src/app/pages/MainPage/pages/ViewPage/Main/Properties/DataModelTree/DataModelNode.tsx +++ b/frontend/src/app/pages/MainPage/pages/ViewPage/Main/Properties/DataModelTree/DataModelNode.tsx @@ -25,7 +25,7 @@ import { NumberOutlined, SisternodeOutlined, } from '@ant-design/icons'; -import { Button, Dropdown, Menu, Tooltip } from 'antd'; +import { Button, Tooltip } from 'antd'; import { IW } from 'app/components'; import { DataViewFieldType } from 'app/constants'; import useI18NPrefix from 'app/hooks/useI18NPrefix'; @@ -43,15 +43,14 @@ import { SUCCESS, WARNING, } from 'styles/StyleConstants'; -import { ColumnCategories } from '../../../constants'; +import SetFieldType from '../../../components/SetFieldType'; import { Column, ColumnRole } from '../../../slice/types'; import { ALLOW_COMBINE_COLUMN_TYPES } from './constant'; - const DataModelNode: FC<{ node: Column; className?: string; branchRole?: ColumnRole; - onNodeTypeChange: (type: any, name: string) => void; + onNodeTypeChange: (type: string[], name: string) => void; onMoveToHierarchy: (node: Column) => void; onCreateHierarchy?: (node: Column) => void; onDeleteFromHierarchy?: (node: Column) => void; @@ -66,7 +65,6 @@ const DataModelNode: FC<{ onDeleteFromHierarchy, }) => { const t = useI18NPrefix('view.model'); - const tg = useI18NPrefix('global'); const [isHover, setIsHover] = useState(false); const hasCategory = true; @@ -111,45 +109,12 @@ const DataModelNode: FC<{ setIsHover(false); }} > - onNodeTypeChange(key, node?.name)} - > - {Object.values(DataViewFieldType).map(t => ( - - {tg(`columnType.${t.toLowerCase()}`)} - - ))} - {hasCategory && ( - <> - - - {Object.values(ColumnCategories).map(t => ( - - {tg(`columnCategory.${t.toLowerCase()}`)} - - ))} - - - )} - - } - > - - {icon} - - + {icon}} + /> {branchRole === ColumnRole.Hierarchy ? node.name : node.displayName} diff --git a/frontend/src/app/pages/MainPage/pages/ViewPage/Main/Properties/DataModelTree/DataModelTree.tsx b/frontend/src/app/pages/MainPage/pages/ViewPage/Main/Properties/DataModelTree/DataModelTree.tsx index 9dad1407cb88ac16f0439e7d362bbd68cbb782bc..dc6e5418d5d73e33e97b166eba43b958f545300d 100644 --- a/frontend/src/app/pages/MainPage/pages/ViewPage/Main/Properties/DataModelTree/DataModelTree.tsx +++ b/frontend/src/app/pages/MainPage/pages/ViewPage/Main/Properties/DataModelTree/DataModelTree.tsx @@ -17,7 +17,7 @@ */ import { Form, Input, message, Select } from 'antd'; -import { DataViewFieldType } from 'app/constants'; +import { DataViewFieldType, DateFormat } from 'app/constants'; import useI18NPrefix from 'app/hooks/useI18NPrefix'; import useStateModal, { StateModalSize } from 'app/hooks/useStateModal'; import { APP_CURRENT_VERSION } from 'app/migration/constants'; @@ -33,7 +33,7 @@ import { SPACE_LG } from 'styles/StyleConstants'; import { Nullable } from 'types'; import { CloneValueDeep, isEmpty, isEmptyArray } from 'utils/object'; import { modelListFormsTreeByTableName } from 'utils/utils'; -import { ViewViewModelStages } from '../../../constants'; +import { ColumnCategories, ViewViewModelStages } from '../../../constants'; import { useViewSlice } from '../../../slice'; import { selectCurrentEditingView, @@ -167,15 +167,17 @@ const DataModelTree: FC = memo(() => { handleDataModelHierarchyChange(newHierarchy); }; - const handleNodeTypeChange = (type, name) => { + const handleNodeTypeChange = (type: string[], name) => { const targetNode = tableColumns?.find(n => n.name === name); if (targetNode) { let newNode; - if (type.includes('category')) { - const category = type.split('-')[1]; + if (type[0].includes('category')) { + const category = type[0].split('-')[1]; newNode = { ...targetNode, category }; + } else if (type.includes('DATE')) { + newNode = { ...targetNode, type: type[1], dateFormat: type[0] }; } else { - newNode = { ...targetNode, type: type }; + newNode = { ...targetNode, type: type[0] }; } const newHierarchy = updateNode( tableColumns, @@ -196,11 +198,17 @@ const DataModelTree: FC = memo(() => { const newTargetBranch = CloneValueDeep(targetBranch); if (newTargetBranch.children) { let newNode = newTargetBranch.children[newNodeIndex]; - if (type.includes('category')) { - const category = type.split('-')[1]; + if (type[0].includes('category')) { + const category = type[0].split('-')[1] as ColumnCategories; newNode = { ...newNode, category }; + } else if (type.includes('DATE')) { + newNode = { + ...newNode, + type: type[1] as DataViewFieldType, + dateFormat: type[0] as DateFormat, + }; } else { - newNode = { ...newNode, type: type }; + newNode = { ...newNode, type: type[0] as DataViewFieldType }; } newTargetBranch.children[newNodeIndex] = newNode; const newHierarchy = updateNode( diff --git a/frontend/src/app/pages/MainPage/pages/ViewPage/Main/Properties/Variables.tsx b/frontend/src/app/pages/MainPage/pages/ViewPage/Main/Properties/Variables.tsx index 3f240a991e8b9156a9e11173f8a5b68ab25fe95d..1bb9842e44f949f8af8675803b03314b2ca3d78b 100644 --- a/frontend/src/app/pages/MainPage/pages/ViewPage/Main/Properties/Variables.tsx +++ b/frontend/src/app/pages/MainPage/pages/ViewPage/Main/Properties/Variables.tsx @@ -182,7 +182,7 @@ export const Variables = memo(() => { let defaultValue: any = values.defaultValue; if (values.valueType === VariableValueTypes.Date && !values.expression) { defaultValue = values.defaultValue.map(d => - (d as Moment).format(TIME_FORMATTER), + (d as Moment).format(values.dateFormat), ); } @@ -236,15 +236,20 @@ export const Variables = memo(() => { const saveRelations = useCallback( (changedRowPermissions: RowPermission[]) => { try { - const changedRowPermissionsRaw = changedRowPermissions.map(cr => ({ - ...cr, - value: JSON.stringify( - cr.value && - (editingVariable?.valueType === VariableValueTypes.Date - ? cr.value.map(d => (d as Moment).format(TIME_FORMATTER)) - : cr.value), - ), - })); + const changedRowPermissionsRaw = changedRowPermissions.map(cr => { + const dateFormat = + variables.find(v => v.id === cr.variableId)?.dateFormat || + TIME_FORMATTER; + return { + ...cr, + value: JSON.stringify( + cr.value && + (editingVariable?.valueType === VariableValueTypes.Date + ? cr.value.map(d => (d as Moment).format(dateFormat)) + : cr.value), + ), + }; + }); if ( !comparePermissionChange( editingVariable?.relVariableSubjects || [], diff --git a/frontend/src/app/pages/MainPage/pages/ViewPage/Sidebar/FolderTree.tsx b/frontend/src/app/pages/MainPage/pages/ViewPage/Sidebar/FolderTree.tsx index 0d31da9f7587cfc573540af052f309a04f99cf9f..57004fe6189237d85cd61d04502544b6968d523d 100644 --- a/frontend/src/app/pages/MainPage/pages/ViewPage/Sidebar/FolderTree.tsx +++ b/frontend/src/app/pages/MainPage/pages/ViewPage/Sidebar/FolderTree.tsx @@ -33,7 +33,13 @@ import { selectPermissionMap, } from 'app/pages/MainPage/slice/selectors'; import { CommonFormTypes } from 'globalConstants'; -import React, { memo, useCallback, useContext, useEffect } from 'react'; +import React, { + memo, + useCallback, + useContext, + useEffect, + useState, +} from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { useHistory } from 'react-router-dom'; import { getInsertedNodeIndex, onDropTreeFn, stopPPG } from 'utils/utils'; @@ -63,6 +69,7 @@ interface FolderTreeProps { export const FolderTree = memo(({ treeData }: FolderTreeProps) => { const dispatch = useDispatch(); + const [expandedKeys, setExpandedKeys] = useState([]); const history = useHistory(); const { showSaveForm } = useContext(SaveFormContext); const loading = useSelector(selectViewListLoading); @@ -253,11 +260,18 @@ export const FolderTree = memo(({ treeData }: FolderTreeProps) => { const treeSelect = useCallback( (_, { node }) => { + if (node.isFolder) { + if (expandedKeys?.includes(node.key)) { + setExpandedKeys(expandedKeys.filter(k => k !== node.key)); + } else { + setExpandedKeys([node.key].concat(expandedKeys)); + } + } if (!node.isFolder && node.id !== currentEditingViewKey) { history.push(`/organizations/${orgId}/views/${node.id}`); } }, - [history, orgId, currentEditingViewKey], + [history, orgId, currentEditingViewKey, expandedKeys], ); const onDrop = info => { @@ -280,6 +294,10 @@ export const FolderTree = memo(({ treeData }: FolderTreeProps) => { }); }; + const handleExpandTreeNode = expandedKeys => { + setExpandedKeys(expandedKeys); + }; + return ( { titleRender={renderTreeTitle} selectedKeys={[currentEditingViewKey]} onSelect={treeSelect} - defaultExpandAll onDrop={onDrop} + expandedKeys={expandedKeys} + onExpand={handleExpandTreeNode} draggable /> ); diff --git a/frontend/src/app/pages/MainPage/pages/ViewPage/Sidebar/Recycle.tsx b/frontend/src/app/pages/MainPage/pages/ViewPage/Sidebar/Recycle.tsx index a6a427ad4c490b51aa801b460f2feb732983cdd9..590e78857f924b14eb8f9c7b253f9b18209d1f85 100644 --- a/frontend/src/app/pages/MainPage/pages/ViewPage/Sidebar/Recycle.tsx +++ b/frontend/src/app/pages/MainPage/pages/ViewPage/Sidebar/Recycle.tsx @@ -24,21 +24,15 @@ import { import { Menu, message, Popconfirm, TreeDataNode } from 'antd'; import { MenuListItem, Popup, Tree, TreeTitle } from 'app/components'; import useI18NPrefix from 'app/hooks/useI18NPrefix'; -import { getCascadeAccess } from 'app/pages/MainPage/Access'; import { selectIsOrgOwner, selectOrgId, - selectPermissionMap, } from 'app/pages/MainPage/slice/selectors'; import { CommonFormTypes } from 'globalConstants'; import { memo, useCallback, useContext, useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { useHistory } from 'react-router-dom'; -import { getInsertedNodeIndex, getPath, stopPPG } from 'utils/utils'; -import { - PermissionLevels, - ResourceTypes, -} from '../../PermissionPage/constants'; +import { getInsertedNodeIndex, stopPPG } from 'utils/utils'; import { SaveFormContext } from '../SaveFormContext'; import { selectArchivedListLoading, @@ -65,7 +59,6 @@ export const Recycle = memo(({ list }: RecycleProps) => { const currentEditingViewKey = useSelector(selectCurrentEditingViewKey); const views = useSelector(selectViews); const isOwner = useSelector(selectIsOrgOwner); - const permissionMap = useSelector(selectPermissionMap); const t = useI18NPrefix('view.saveForm'); const tg = useI18NPrefix('global'); @@ -137,25 +130,11 @@ export const Recycle = memo(({ list }: RecycleProps) => { ); const renderTreeTitle = useCallback( - ({ key, title, parentId }) => { - const path = views - ? getPath( - views as Array<{ id: string; parentId: string }>, - { id: key, parentId }, - ResourceTypes.View, - ) - : []; - const allowManage = getCascadeAccess( - isOwner, - permissionMap, - ResourceTypes.View, - path, - PermissionLevels.Manage, - ); + ({ key, title }) => { return (

{`${title}`}

- {allowManage && ( + {isOwner && ( {
); }, - [moreMenuClick, del, views, isOwner, permissionMap, tg], + [moreMenuClick, del, isOwner, tg], ); const treeSelect = useCallback( diff --git a/frontend/src/app/pages/MainPage/pages/ViewPage/Sidebar/index.tsx b/frontend/src/app/pages/MainPage/pages/ViewPage/Sidebar/index.tsx index f0f76be3fec86099e39b1703d5901c9330154fb3..ec2b2f51d26bea92ecb27f3f19d3a1e3cc25146f 100644 --- a/frontend/src/app/pages/MainPage/pages/ViewPage/Sidebar/index.tsx +++ b/frontend/src/app/pages/MainPage/pages/ViewPage/Sidebar/index.tsx @@ -19,10 +19,8 @@ import { CodeFilled, DeleteOutlined, - FileOutlined, FolderFilled, FolderOpenFilled, - FolderOutlined, MenuFoldOutlined, MenuUnfoldOutlined, } from '@ant-design/icons'; @@ -95,7 +93,6 @@ export const Sidebar = memo( key: id, title: name, parentId, - icon: isFolder ? : , isFolder, disabled: deleteLoading, })), diff --git a/frontend/src/app/pages/MainPage/pages/ViewPage/components/SchemaTable.tsx b/frontend/src/app/pages/MainPage/pages/ViewPage/components/SchemaTable.tsx index 2653902e4ffe5ba71094fbf989d586da80e99dd7..e998e8b8ba2ab115f99177611c234df3b0f94b88 100644 --- a/frontend/src/app/pages/MainPage/pages/ViewPage/components/SchemaTable.tsx +++ b/frontend/src/app/pages/MainPage/pages/ViewPage/components/SchemaTable.tsx @@ -21,11 +21,10 @@ import { FieldStringOutlined, NumberOutlined, } from '@ant-design/icons'; -import { Dropdown, Menu, TableColumnType, TableProps, Tooltip } from 'antd'; +import { TableColumnType, TableProps } from 'antd'; import { ToolbarButton } from 'app/components'; import { VirtualTable } from 'app/components/VirtualTable'; import { DataViewFieldType } from 'app/constants'; -import useI18NPrefix from 'app/hooks/useI18NPrefix'; import { TABLE_DATA_INDEX } from 'globalConstants'; import { memo, ReactElement, useMemo } from 'react'; import styled from 'styled-components/macro'; @@ -36,9 +35,9 @@ import { WARNING, } from 'styles/StyleConstants'; import { uuidv4 } from 'utils/utils'; -import { ColumnCategories } from '../constants'; import { Column, ColumnsModel, Model } from '../slice/types'; import { getColumnWidthMap, getHierarchyColumn } from '../utils'; +import SetFieldType from './SetFieldType'; const ROW_KEY = 'DATART_ROW_KEY'; @@ -49,14 +48,15 @@ interface SchemaTableProps extends TableProps { hierarchy: Model; dataSource?: object[]; hasCategory?: boolean; + hasFormat?: boolean; getExtraHeaderActions?: ( name: string, column: Omit, ) => ReactElement[]; onSchemaTypeChange: ( - name: string, + namePath: string, column: Omit, - ) => (e) => void; + ) => (namePath: string[]) => void; } export const SchemaTable = memo( @@ -66,6 +66,7 @@ export const SchemaTable = memo( model, hierarchy, dataSource, + hasFormat, hasCategory, getExtraHeaderActions, onSchemaTypeChange, @@ -84,8 +85,6 @@ export const SchemaTable = memo( () => getColumnWidthMap(model, dataSource || []), [model, dataSource], ); - const t = useI18NPrefix('view.schemaTable'); - const tg = useI18NPrefix('global'); const indexColumnWidth = 50; const { columns, @@ -120,50 +119,20 @@ export const SchemaTable = memo( const title = ( <> {name} - - {Object.values(DataViewFieldType).map(t => ( - - {tg(`columnType.${t.toLowerCase()}`)} - - ))} - {hasCategory && ( - <> - - - {Object.values(ColumnCategories).map(t => ( - - {tg(`columnCategory.${t.toLowerCase()}`)} - - ))} - - - )} - - } - > - + - - + } + /> {extraActions} ); @@ -195,10 +164,9 @@ export const SchemaTable = memo( hierarchy, columnWidthMap, hasCategory, + hasFormat, getExtraHeaderActions, onSchemaTypeChange, - t, - tg, ]); return ( diff --git a/frontend/src/app/pages/MainPage/pages/ViewPage/components/SetFieldType.tsx b/frontend/src/app/pages/MainPage/pages/ViewPage/components/SetFieldType.tsx new file mode 100644 index 0000000000000000000000000000000000000000..f794b08cbd4e1026272e0f5dbcf9ac13c0dded28 --- /dev/null +++ b/frontend/src/app/pages/MainPage/pages/ViewPage/components/SetFieldType.tsx @@ -0,0 +1,106 @@ +/** + * Datart + * + * Copyright 2021 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Dropdown, Menu, Tooltip } from 'antd'; +import { DataViewFieldType, DateFormat } from 'app/constants'; +import useI18NPrefix from 'app/hooks/useI18NPrefix'; +import { memo, ReactNode } from 'react'; +import { ColumnCategories } from '../constants'; +import { Column } from '../slice/types'; + +interface SetFieldTypeProps { + onChange: (keyPath: string[], name: string) => void; + field: Column; + icon: ReactNode; + hasCategory?: boolean; + hasFormat?: boolean; +} + +const SetFieldType = memo( + ({ + onChange, + field, + hasCategory, + icon, + hasFormat = true, + }: SetFieldTypeProps) => { + const t = useI18NPrefix('view.schemaTable'); + const tg = useI18NPrefix('global'); + return ( + onChange(keyPath, field?.name)} + > + {Object.values(DataViewFieldType).map(t => { + if (t === DataViewFieldType.DATE && hasFormat) { + return ( + + {Object.values(DateFormat).map(format => { + return {format}; + })} + + ); + } + return ( + + {tg(`columnType.${t.toLowerCase()}`)} + + ); + })} + {hasCategory && ( + <> + + + {Object.values(ColumnCategories).map(t => ( + + {tg(`columnCategory.${t.toLowerCase()}`)} + + ))} + + + )} + + } + > + + {icon} + + + ); + }, +); +export default SetFieldType; diff --git a/frontend/src/app/pages/MainPage/pages/ViewPage/slice/thunks.ts b/frontend/src/app/pages/MainPage/pages/ViewPage/slice/thunks.ts index 27dd2fb81b25fd14ea01cc23111052b0073eee70..32c1296a4ffd1475753f3b984d91f4852b64a039 100644 --- a/frontend/src/app/pages/MainPage/pages/ViewPage/slice/thunks.ts +++ b/frontend/src/app/pages/MainPage/pages/ViewPage/slice/thunks.ts @@ -202,6 +202,12 @@ export const runSql = createAsyncThunk< ); } + let reqColumns = ''; + + if (type === 'STRUCT') { + reqColumns = buildRequestColumns(structure!); + } + const response = await request2( { url: '/data-provider/execute/test', @@ -211,7 +217,7 @@ export const runSql = createAsyncThunk< sourceId, size, scriptType: type, - columns: type === 'STRUCT' ? buildRequestColumns(structure!) : '', + columns: reqColumns, variables: variables.map( ({ name, type, valueType, defaultValue, expression }) => ({ name, @@ -235,7 +241,11 @@ export const runSql = createAsyncThunk< }, }, ); - return { ...response?.data, warnings: response?.warnings }; + return { + ...response?.data, + warnings: response?.warnings, + reqColumns: reqColumns, + }; }); export const saveView = createAsyncThunk< diff --git a/frontend/src/app/pages/MainPage/pages/ViewPage/slice/types.ts b/frontend/src/app/pages/MainPage/pages/ViewPage/slice/types.ts index 2f4e24bd20682c28d66004504639fdd745350b24..41d49ff3814b5d1688956d6abfffff665b13874d 100644 --- a/frontend/src/app/pages/MainPage/pages/ViewPage/slice/types.ts +++ b/frontend/src/app/pages/MainPage/pages/ViewPage/slice/types.ts @@ -17,7 +17,7 @@ */ import { TreeDataNode, TreeNodeProps } from 'antd'; -import { DataViewFieldType } from 'app/constants'; +import { DataViewFieldType, DateFormat } from 'app/constants'; import { ChartDataViewMeta } from 'app/types/ChartDataViewMeta'; import { ReactElement } from 'react'; import { View } from '../../../../../types/View'; @@ -110,6 +110,7 @@ export interface QueryResult { pageInfo: PageInfo; script?: string; warnings?: string[] | null; + reqColumns?: { column: []; alias: string }[]; } export interface PageInfo { pageNo: number; @@ -131,7 +132,7 @@ export enum ColumnRole { export interface Column extends Schema { category?: ColumnCategories; index?: number; - + dateFormat?: DateFormat; role?: ColumnRole; children?: Column[]; path?: string[]; diff --git a/frontend/src/app/pages/MainPage/pages/ViewPage/utils.tsx b/frontend/src/app/pages/MainPage/pages/ViewPage/utils.tsx index 21fbbf3ba20edee6ab0a2da773b30ee5f63eb6ad..34b0a47e5a965c7da65e7e816c673226c2ee539d 100644 --- a/frontend/src/app/pages/MainPage/pages/ViewPage/utils.tsx +++ b/frontend/src/app/pages/MainPage/pages/ViewPage/utils.tsx @@ -105,17 +105,23 @@ export function transformQueryResultToModelAndDataSource( model: HierarchyModel; dataSource: object[]; } { - const { rows = [], columns = [] } = data || {}; + const { rows = [], columns = [], reqColumns } = data || {}; const newColumns = columns.reduce((obj, { name, type, primaryKey }) => { const hierarchyColumn = getHierarchyColumn( name, lastModel?.hierarchy || {}, ); - const key = viewType === 'STRUCT' ? JSON.parse(name).join('.') : name; - const _name = viewType === 'STRUCT' ? JSON.parse(name) : name; + + let _name: any = []; + if (viewType === 'STRUCT') { + _name = reqColumns?.find(column => column.alias === name[0])?.column; + } else { + _name = name; + } + return { ...obj, - [key]: { + [name]: { name: _name, type: hierarchyColumn?.type || type, primaryKey, @@ -125,10 +131,7 @@ export function transformQueryResultToModelAndDataSource( }, {}); const dataSource = rows.map(arr => arr.reduce((obj, val, index) => { - const key = - viewType === 'STRUCT' - ? JSON.parse(columns[index].name).join('.') - : columns[index].name; + const key = columns[index].name; return { ...obj, [key]: val, @@ -389,6 +392,7 @@ export const diffMergeHierarchyModel = ( ) => { const hierarchy = model?.hierarchy || {}; const columns = model?.columns || {}; + const allHierarchyColumnNames = Object.keys(hierarchy).flatMap(name => { if (!isEmptyArray(hierarchy[name].children)) { return hierarchy[name].children!.map(child => child.name); @@ -485,7 +489,7 @@ export function buildRequestColumns(tableJSON: StructViewQueryProps) { tableJSON.columns.forEach((v, i) => { const table = tableJSON.table || []; columns.push({ - alias: JSON.stringify([...table, v]), + alias: [...table, v].join('.'), column: [...table, v], }); }); @@ -493,7 +497,7 @@ export function buildRequestColumns(tableJSON: StructViewQueryProps) { const table = join.table || []; join.columns?.forEach(column => { columns.push({ - alias: JSON.stringify([...table, column]), + alias: [...table, column].join('.'), column: [...table, column], }); }); diff --git a/frontend/src/app/pages/MainPage/pages/VizPage/ChartPreview/ChartPreviewBoard.tsx b/frontend/src/app/pages/MainPage/pages/VizPage/ChartPreview/ChartPreviewBoard.tsx index a9ccccf8794de1a65f00054486bbed8ca1e23951..0a6bd31c788fb352a5d32492f45577db7d517bc4 100644 --- a/frontend/src/app/pages/MainPage/pages/VizPage/ChartPreview/ChartPreviewBoard.tsx +++ b/frontend/src/app/pages/MainPage/pages/VizPage/ChartPreview/ChartPreviewBoard.tsx @@ -23,6 +23,7 @@ import ChartDrillPaths from 'app/components/ChartDrill/ChartDrillPaths'; import { ChartIFrameContainer } from 'app/components/ChartIFrameContainer'; import { InteractionMouseEvent } from 'app/components/FormGenerator/constants'; import { VizHeader } from 'app/components/VizHeader'; +import { ChartInteractionEvent } from 'app/constants'; import ChartDrillContext from 'app/contexts/ChartDrillContext'; import { useCacheWidthHeight } from 'app/hooks/useCacheWidthHeight'; import useChartInteractions from 'app/hooks/useChartInteractions'; @@ -35,7 +36,7 @@ import { selectAvailableSourceFunctions } from 'app/pages/ChartWorkbenchPage/sli import { fetchAvailableSourceFunctionsForChart } from 'app/pages/ChartWorkbenchPage/slice/thunks'; import { useMainSlice } from 'app/pages/MainPage/slice'; import { IChart } from 'app/types/Chart'; -import { ChartDataRequestFilter } from 'app/types/ChartDataRequest'; +import { PendingChartDataRequestFilter } from 'app/types/ChartDataRequest'; import { IChartDrillOption } from 'app/types/ChartDrillOption'; import { chartSelectionEventListener, @@ -154,7 +155,7 @@ const ChartPreviewBoard: FC<{ }); useEffect(() => { - const jumpFilterParams: ChartDataRequestFilter[] = parse( + const jumpFilterParams: PendingChartDataRequestFilter[] = parse( filterSearchUrl, { ignoreQueryPrefix: true }, )?.filters; @@ -384,6 +385,20 @@ const ChartPreviewBoard: FC<{ { name: 'click', callback: param => { + if ( + param?.interactionType === ChartInteractionEvent.PagingOrSort + ) { + tablePagingAndSortEventListener(param, p => { + dispatch( + fetchDataSetByPreviewChartAction({ + ...p, + backendChartId, + }), + ); + }); + return; + } + handleDrillThroughEvent( buildDrillThroughEventParams(param, InteractionMouseEvent.Left), ); @@ -394,14 +409,6 @@ const ChartPreviewBoard: FC<{ drillOptionRef.current = p; handleDrillOptionChange?.(p); }); - tablePagingAndSortEventListener(param, p => { - dispatch( - fetchDataSetByPreviewChartAction({ - ...p, - backendChartId, - }), - ); - }); pivotTableDrillEventListener(param, p => { handleDrillOptionChange(p); }); diff --git a/frontend/src/app/pages/MainPage/pages/VizPage/SaveForm.tsx b/frontend/src/app/pages/MainPage/pages/VizPage/SaveForm.tsx index 1a5542fb89e3c37216316a373ee044c99d339e96..0193e961701aaad92886621e6a2b7e9735cbc8bd 100644 --- a/frontend/src/app/pages/MainPage/pages/VizPage/SaveForm.tsx +++ b/frontend/src/app/pages/MainPage/pages/VizPage/SaveForm.tsx @@ -22,6 +22,7 @@ import { PermissionLevels, ResourceTypes } from '../PermissionPage/constants'; import { FileUpload } from '../ResourceMigrationPage/FileUpload'; import { SaveFormContext } from './SaveFormContext'; import { + makeSelectStoryboradFolderTree, makeSelectVizFolderTree, selectSaveFolderLoading, selectSaveStoryboardLoading, @@ -40,6 +41,10 @@ export function SaveForm({ formProps, ...modalProps }: SaveFormProps) { onAfterClose, } = useContext(SaveFormContext); const selectVizFolderTree = useMemo(makeSelectVizFolderTree, []); + const selectStoryboradFolderTree = useMemo( + makeSelectStoryboradFolderTree, + [], + ); const saveFolderLoading = useSelector(selectSaveFolderLoading); const saveStoryboardLoading = useSelector(selectSaveStoryboardLoading); const orgId = useSelector(selectOrgId); @@ -61,9 +66,12 @@ export function SaveForm({ formProps, ...modalProps }: SaveFormProps) { [isOwner, permissionMap], ); - const treeData = useSelector(state => + const folderTreeData = useSelector(state => selectVizFolderTree(state, { id: initialValues?.id, getDisabled }), ); + const storyboardTreeData = useSelector(state => + selectStoryboradFolderTree(state, { id: initialValues?.id, getDisabled }), + ); const save = useCallback( values => { @@ -132,7 +140,7 @@ export function SaveForm({ formProps, ...modalProps }: SaveFormProps) { const data = { name: value, orgId, - vizType: 'FOLDER', + vizType, parentId: parentId || null, }; return fetchCheckName('viz', data); @@ -191,7 +199,19 @@ export function SaveForm({ formProps, ...modalProps }: SaveFormProps) { { + formRef.current?.validateFields(); + }} + /> + + )} + {vizType === 'STORYBOARD' && ( + + { formRef.current?.validateFields(); diff --git a/frontend/src/app/pages/MainPage/pages/VizPage/Sidebar/Folders/index.tsx b/frontend/src/app/pages/MainPage/pages/VizPage/Sidebar/Folders/index.tsx index 6af1e23fa53e13955e6cf950de09f1d4418be511..3a5405fa8fa623db97e76ae4d5ccb061796c3eed 100644 --- a/frontend/src/app/pages/MainPage/pages/VizPage/Sidebar/Folders/index.tsx +++ b/frontend/src/app/pages/MainPage/pages/VizPage/Sidebar/Folders/index.tsx @@ -18,17 +18,17 @@ import { SPACE_XS } from 'styles/StyleConstants'; import { useAddViz } from '../../hooks/useAddViz'; import { SaveFormContext } from '../../SaveFormContext'; import { + makeSelectArchivedDashboardsTree, + makeSelectArchivedDatachartsTree, makeSelectVizTree, selectArchivedDashboardLoading, - selectArchivedDashboards, selectArchivedDatachartLoading, - selectArchivedDatacharts, } from '../../slice/selectors'; import { getArchivedDashboards, getArchivedDatacharts, } from '../../slice/thunks'; -import { FolderViewModel } from '../../slice/types'; +import { ArchivedViz, FolderViewModel } from '../../slice/types'; import { Recycle } from '../Recycle'; import { FolderTree } from './FolderTree'; @@ -70,8 +70,27 @@ export const Folders = memo( useDebouncedSearch(treeData, (keywords, d) => d.title.toLowerCase().includes(keywords.toLowerCase()), ); - const archivedDatacharts = useSelector(selectArchivedDatacharts); - const archivedDashboards = useSelector(selectArchivedDashboards); + + const selectArchivedDatachartsTree = useMemo( + makeSelectArchivedDatachartsTree, + [], + ); + const selectArchivedDashboardsTree = useMemo( + makeSelectArchivedDashboardsTree, + [], + ); + + const getArchivedDisabled = useCallback( + ({ deleteLoading }: ArchivedViz) => deleteLoading, + [], + ); + + const archivedDatachartsTreeData = useSelector(state => + selectArchivedDatachartsTree(state, { getDisabled: getArchivedDisabled }), + ); + const archivedDashboardsTreeData = useSelector(state => + selectArchivedDashboardsTree(state, { getDisabled: getArchivedDisabled }), + ); const archivedDataChartLoading = useSelector( selectArchivedDatachartLoading, ); @@ -80,8 +99,8 @@ export const Folders = memo( ); const { filteredData: filteredListData, debouncedSearch: listSearch } = useDebouncedSearch( - archivedDatacharts.concat(archivedDashboards), - (keywords, d) => d.name.toLowerCase().includes(keywords.toLowerCase()), + (archivedDatachartsTreeData || []).concat(archivedDashboardsTreeData), + (keywords, d) => d.title.toLowerCase().includes(keywords.toLowerCase()), ); const recycleInit = useCallback(() => { diff --git a/frontend/src/app/pages/MainPage/pages/VizPage/Sidebar/Recycle.tsx b/frontend/src/app/pages/MainPage/pages/VizPage/Sidebar/Recycle.tsx index 4f8674a387a9ff639eed79f660021f4e7239fc40..883b8315d75fc45f77ed177242e6f313b6114acc 100644 --- a/frontend/src/app/pages/MainPage/pages/VizPage/Sidebar/Recycle.tsx +++ b/frontend/src/app/pages/MainPage/pages/VizPage/Sidebar/Recycle.tsx @@ -1,39 +1,26 @@ import { DeleteOutlined, - LoadingOutlined, MoreOutlined, ReloadOutlined, } from '@ant-design/icons'; -import { Button, List, Menu, message, Popconfirm } from 'antd'; -import { ListItem, MenuListItem, Popup } from 'app/components'; +import { Menu, message, Popconfirm, TreeDataNode } from 'antd'; +import { MenuListItem, Popup, Tree, TreeTitle } from 'app/components'; import useI18NPrefix from 'app/hooks/useI18NPrefix'; -import { calcAc, getCascadeAccess } from 'app/pages/MainPage/Access'; -import { - selectIsOrgOwner, - selectPermissionMap, -} from 'app/pages/MainPage/slice/selectors'; -import classnames from 'classnames'; +import { selectIsOrgOwner } from 'app/pages/MainPage/slice/selectors'; import { CommonFormTypes } from 'globalConstants'; import { memo, useCallback, useContext, useEffect } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { useHistory } from 'react-router-dom'; -import styled from 'styled-components/macro'; -import { getInsertedNodeIndex, getPath, stopPPG } from 'utils/utils'; -import { - PermissionLevels, - ResourceTypes, - VizResourceSubTypes, -} from '../../PermissionPage/constants'; +import { getInsertedNodeIndex, stopPPG } from 'utils/utils'; import { SaveFormContext } from '../SaveFormContext'; import { selectVizs } from '../slice/selectors'; import { deleteViz, removeTab, unarchiveViz } from '../slice/thunks'; -import { ArchivedViz } from '../slice/types'; interface RecycleProps { type: 'viz' | 'storyboard'; orgId: string; selectedId?: string; - list?: ArchivedViz[]; + list?: TreeDataNode[]; listLoading: boolean; onInit: () => void; } @@ -45,7 +32,6 @@ export const Recycle = memo( const { showSaveForm } = useContext(SaveFormContext); const vizs = useSelector(selectVizs); const isOwner = useSelector(selectIsOrgOwner); - const permissionMap = useSelector(selectPermissionMap); const tg = useI18NPrefix('global'); useEffect(() => { @@ -120,105 +106,68 @@ export const Recycle = memo( [dispatch, showSaveForm, redirect, vizs, tg], ); - const toDetail = useCallback( - id => () => { - history.push(`/organizations/${orgId}/vizs/${id}`); + const treeSelect = useCallback( + (_, { node }) => { + if (node.id !== selectedId) { + history.push(`/organizations/${orgId}/vizs/${node.id}`); + } }, - [history, orgId], + [history, orgId, selectedId], ); - return ( - - }} - renderItem={({ id, name, vizType, loading }) => { - let allowManage = false; - if (type === 'viz') { - const viz = vizs.find(v => v.id === id); - const path = viz - ? getPath( - vizs as Array<{ id: string; parentId: string }>, - { id, parentId: viz.parentId }, - VizResourceSubTypes.Folder, - ) - : [id]; - allowManage = getCascadeAccess( - isOwner, - permissionMap, - ResourceTypes.View, - path, - PermissionLevels.Manage, - ); - } else { - allowManage = !!calcAc( - isOwner, - permissionMap, - ResourceTypes.Viz, - PermissionLevels.Manage, - id, - ); - } - return ( - - } - > - {tg('button.restore')} - - } - > - - {tg('button.delete')} - - - - } + const renderTreeTitle = useCallback( + ({ key, title, vizType }) => { + return ( + +

{`${title}`}

+ {isOwner && ( + + } + > + {tg('button.restore')} + + } > -