diff --git a/core/core-backend/src/main/java/io/dataease/datasource/dao/auto/entity/CoreDatasource.java b/core/core-backend/src/main/java/io/dataease/datasource/dao/auto/entity/CoreDatasource.java
index a141d413bf..ddf6d34292 100644
--- a/core/core-backend/src/main/java/io/dataease/datasource/dao/auto/entity/CoreDatasource.java
+++ b/core/core-backend/src/main/java/io/dataease/datasource/dao/auto/entity/CoreDatasource.java
@@ -7,11 +7,11 @@ import java.io.Serializable;
/**
*
- *
+ * 数据源表
*
*
* @author fit2cloud
- * @since 2023-09-26
+ * @since 2024-07-09
*/
@TableName("core_datasource")
public class CoreDatasource implements Serializable {
@@ -89,6 +89,11 @@ public class CoreDatasource implements Serializable {
*/
private String taskStatus;
+ /**
+ * 开启数据填报
+ */
+ private Boolean enableDataFill;
+
public Long getId() {
return id;
}
@@ -201,6 +206,14 @@ public class CoreDatasource implements Serializable {
this.taskStatus = taskStatus;
}
+ public Boolean getEnableDataFill() {
+ return enableDataFill;
+ }
+
+ public void setEnableDataFill(Boolean enableDataFill) {
+ this.enableDataFill = enableDataFill;
+ }
+
@Override
public String toString() {
return "CoreDatasource{" +
@@ -218,6 +231,7 @@ public class CoreDatasource implements Serializable {
", status = " + status +
", qrtzInstance = " + qrtzInstance +
", taskStatus = " + taskStatus +
+ ", enableDataFill = " + enableDataFill +
"}";
}
}
diff --git a/core/core-backend/src/main/java/io/dataease/datasource/dao/auto/mapper/CoreDatasourceMapper.java b/core/core-backend/src/main/java/io/dataease/datasource/dao/auto/mapper/CoreDatasourceMapper.java
index d6e7170bd1..a27bc9ad36 100644
--- a/core/core-backend/src/main/java/io/dataease/datasource/dao/auto/mapper/CoreDatasourceMapper.java
+++ b/core/core-backend/src/main/java/io/dataease/datasource/dao/auto/mapper/CoreDatasourceMapper.java
@@ -6,11 +6,11 @@ import org.apache.ibatis.annotations.Mapper;
/**
*
- * Mapper 接口
+ * 数据源表 Mapper 接口
*
*
* @author fit2cloud
- * @since 2023-09-26
+ * @since 2024-07-09
*/
@Mapper
public interface CoreDatasourceMapper extends BaseMapper {
diff --git a/core/core-backend/src/main/java/io/dataease/datasource/provider/CalciteProvider.java b/core/core-backend/src/main/java/io/dataease/datasource/provider/CalciteProvider.java
index a6ff053f04..b5acae0241 100644
--- a/core/core-backend/src/main/java/io/dataease/datasource/provider/CalciteProvider.java
+++ b/core/core-backend/src/main/java/io/dataease/datasource/provider/CalciteProvider.java
@@ -26,10 +26,10 @@ import org.apache.calcite.adapter.jdbc.JdbcSchema;
import org.apache.calcite.jdbc.CalciteConnection;
import org.apache.calcite.schema.Schema;
import org.apache.calcite.schema.SchemaPlus;
+import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
-import org.springframework.util.CollectionUtils;
import java.io.File;
import java.io.IOException;
@@ -54,6 +54,7 @@ public class CalciteProvider extends Provider {
private final String FILE_PATH = "/opt/dataease2.0/drivers";
private final String CUSTOM_PATH = "/opt/dataease2.0/custom-drivers/";
private static String split = "DE";
+
@Resource
private CommonThreadPool commonThreadPool;
@@ -214,7 +215,9 @@ public class CalciteProvider extends Provider {
List datasetTableFields = new ArrayList<>();
DatasourceSchemaDTO datasourceSchemaDTO = datasourceRequest.getDsList().entrySet().iterator().next().getValue();
datasourceRequest.setDatasource(datasourceSchemaDTO);
+
DatasourceConfiguration datasourceConfiguration = JsonUtil.parseObject(datasourceRequest.getDatasource().getConfiguration(), DatasourceConfiguration.class);
+
String table = datasourceRequest.getTable();
if (StringUtils.isEmpty(table)) {
ResultSet resultSet = null;
@@ -371,11 +374,20 @@ public class CalciteProvider extends Provider {
// schema
ResultSet resultSet = null;
try (ConnectionObj con = getConnection(datasourceRequest.getDatasource());
- Statement statement = getStatement(con.getConnection(), datasourceConfiguration.getQueryTimeout())) {
+ PreparedStatement statement = getPreparedStatement(con.getConnection(), datasourceConfiguration.getQueryTimeout(), datasourceRequest.getQuery())) {
if (DatasourceConfiguration.DatasourceType.valueOf(value.getType()) == DatasourceConfiguration.DatasourceType.oracle) {
statement.executeUpdate("ALTER SESSION SET CURRENT_SCHEMA = " + datasourceConfiguration.getSchema());
}
- resultSet = statement.executeQuery(datasourceRequest.getQuery());
+
+ if (CollectionUtils.isNotEmpty(datasourceRequest.getTableFieldWithValues())) {
+ LogUtil.info("execWithPreparedStatement sql: " + datasourceRequest.getQuery());
+ for (int i = 0; i < datasourceRequest.getTableFieldWithValues().size(); i++) {
+ statement.setObject(i + 1, datasourceRequest.getTableFieldWithValues().get(i).getValue(), datasourceRequest.getTableFieldWithValues().get(i).getType());
+ LogUtil.info("execWithPreparedStatement param[" + (i + 1) + "]: " + datasourceRequest.getTableFieldWithValues().get(i).getValue());
+ }
+ }
+
+ resultSet = statement.executeQuery();
fieldList = getField(resultSet, datasourceRequest);
dataList = getData(resultSet, datasourceRequest);
} catch (SQLException e) {
@@ -397,6 +409,88 @@ public class CalciteProvider extends Provider {
return map;
}
+ @Override
+ public void exec(DatasourceRequest datasourceRequest) throws DEException {
+ DatasourceSchemaDTO value = datasourceRequest.getDsList().entrySet().iterator().next().getValue();
+ datasourceRequest.setDatasource(value);
+
+ DatasourceConfiguration datasourceConfiguration = JsonUtil.parseObject(datasourceRequest.getDatasource().getConfiguration(), DatasourceConfiguration.class);
+
+ // schema
+ ResultSet resultSet = null;
+ try (ConnectionObj con = getConnection(datasourceRequest.getDatasource());
+ PreparedStatement statement = getPreparedStatement(con.getConnection(), datasourceConfiguration.getQueryTimeout(), datasourceRequest.getQuery())) {
+ if (DatasourceConfiguration.DatasourceType.valueOf(value.getType()) == DatasourceConfiguration.DatasourceType.oracle) {
+ statement.executeUpdate("ALTER SESSION SET CURRENT_SCHEMA = " + datasourceConfiguration.getSchema());
+ }
+
+ if (CollectionUtils.isNotEmpty(datasourceRequest.getTableFieldWithValues())) {
+ LogUtil.info("execWithPreparedStatement sql: " + datasourceRequest.getQuery());
+ for (int i = 0; i < datasourceRequest.getTableFieldWithValues().size(); i++) {
+ statement.setObject(i + 1, datasourceRequest.getTableFieldWithValues().get(i).getValue(), datasourceRequest.getTableFieldWithValues().get(i).getType());
+ LogUtil.info("execWithPreparedStatement param[" + (i + 1) + "]: " + datasourceRequest.getTableFieldWithValues().get(i).getValue());
+ }
+ }
+
+ statement.execute();
+
+ } catch (SQLException e) {
+ DEException.throwException("SQL ERROR: " + e.getMessage());
+ } catch (Exception e) {
+ DEException.throwException("Data source connection exception: " + e.getMessage());
+ } finally {
+ if (resultSet != null) {
+ try {
+ resultSet.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ @Override
+ public int executeUpdate(DatasourceRequest datasourceRequest) throws DEException {
+ DatasourceSchemaDTO value = datasourceRequest.getDsList().entrySet().iterator().next().getValue();
+ datasourceRequest.setDatasource(value);
+
+ DatasourceConfiguration datasourceConfiguration = JsonUtil.parseObject(datasourceRequest.getDatasource().getConfiguration(), DatasourceConfiguration.class);
+
+ // schema
+ ResultSet resultSet = null;
+ try (ConnectionObj con = getConnection(datasourceRequest.getDatasource());
+ PreparedStatement statement = getPreparedStatement(con.getConnection(), datasourceConfiguration.getQueryTimeout(), datasourceRequest.getQuery())) {
+ if (DatasourceConfiguration.DatasourceType.valueOf(value.getType()) == DatasourceConfiguration.DatasourceType.oracle) {
+ statement.executeUpdate("ALTER SESSION SET CURRENT_SCHEMA = " + datasourceConfiguration.getSchema());
+ }
+
+ if (CollectionUtils.isNotEmpty(datasourceRequest.getTableFieldWithValues())) {
+ LogUtil.info("execWithPreparedStatement sql: " + datasourceRequest.getQuery());
+ for (int i = 0; i < datasourceRequest.getTableFieldWithValues().size(); i++) {
+ statement.setObject(i + 1, datasourceRequest.getTableFieldWithValues().get(i).getValue(), datasourceRequest.getTableFieldWithValues().get(i).getType());
+ LogUtil.info("execWithPreparedStatement param[" + (i + 1) + "]: " + datasourceRequest.getTableFieldWithValues().get(i).getValue());
+ }
+ }
+
+ return statement.executeUpdate();
+
+ } catch (SQLException e) {
+ DEException.throwException("SQL ERROR: " + e.getMessage());
+ } catch (Exception e) {
+ DEException.throwException("Data source connection exception: " + e.getMessage());
+ } finally {
+ if (resultSet != null) {
+ try {
+ resultSet.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ return 0;
+ }
+
private List getField(ResultSet rs, DatasourceRequest datasourceRequest) throws Exception {
List fieldList = new ArrayList<>();
ResultSetMetaData metaData = rs.getMetaData();
@@ -588,14 +682,14 @@ public class CalciteProvider extends Provider {
String[] databasePrams = matcher.group(3).split("\\?");
database = databasePrams[0];
}
- sql = String.format("SELECT COLUMN_NAME,DATA_TYPE,COLUMN_COMMENT FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '%s' AND TABLE_NAME = '%s'", database, datasourceRequest.getTable());
+ sql = String.format("SELECT COLUMN_NAME,DATA_TYPE,COLUMN_COMMENT,IF(COLUMN_KEY='PRI',1,0) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '%s' AND TABLE_NAME = '%s'", database, datasourceRequest.getTable());
break;
case oracle:
configuration = JsonUtil.parseObject(datasourceRequest.getDatasource().getConfiguration(), Oracle.class);
if (StringUtils.isEmpty(configuration.getSchema())) {
DEException.throwException(Translator.get("i18n_schema_is_empty"));
}
- sql = String.format("SELECT a.COLUMN_NAME , a.DATA_TYPE , b.COMMENTS FROM all_tab_columns a LEFT JOIN all_col_comments b ON a.owner = b.owner AND a.table_name = b.table_name AND a.column_name = b.column_name WHERE a.owner = '%s' AND a.table_name = '%s' ORDER BY a.table_name, a.column_id", configuration.getSchema(), datasourceRequest.getTable());
+ sql = String.format("SELECT a.COLUMN_NAME , a.DATA_TYPE , b.COMMENTS ,0 FROM all_tab_columns a LEFT JOIN all_col_comments b ON a.owner = b.owner AND a.table_name = b.table_name AND a.column_name = b.column_name WHERE a.owner = '%s' AND a.table_name = '%s' ORDER BY a.table_name, a.column_id", configuration.getSchema(), datasourceRequest.getTable());
break;
case db2:
configuration = JsonUtil.parseObject(datasourceRequest.getDatasource().getConfiguration(), Db2.class);
@@ -611,7 +705,7 @@ public class CalciteProvider extends Provider {
}
sql = String.format("SELECT \n" +
- " c.name ,t.name ,ep.value \n" +
+ " c.name ,t.name ,ep.value, 0 \n" +
"FROM \n" +
" sys.columns AS c\n" +
"LEFT JOIN sys.extended_properties AS ep ON c.object_id = ep.major_id AND c.column_id = ep.minor_id\n" +
@@ -627,7 +721,8 @@ public class CalciteProvider extends Provider {
sql = String.format("SELECT\n" +
" a.attname AS ColumnName,\n" +
" t.typname,\n" +
- " b.description AS ColumnDescription\n" +
+ " b.description AS ColumnDescription,\n" +
+ " 0\n" +
"FROM\n" +
" pg_class c\n" +
" JOIN pg_attribute a ON a.attrelid = c.oid\n" +
@@ -646,7 +741,8 @@ public class CalciteProvider extends Provider {
sql = String.format("SELECT\n" +
" a.attname AS ColumnName,\n" +
" t.typname,\n" +
- " b.description AS ColumnDescription\n" +
+ " b.description AS ColumnDescription,\n" +
+ " 0\n" +
"FROM\n" +
" pg_class c\n" +
" JOIN pg_attribute a ON a.attrelid = c.oid\n" +
@@ -675,7 +771,8 @@ public class CalciteProvider extends Provider {
sql = String.format(" SELECT\n" +
" name,\n" +
" type,\n" +
- " comment\n" +
+ " comment,\n" +
+ " 0\n" +
"FROM\n" +
" system.columns\n" +
"WHERE\n" +
@@ -702,6 +799,10 @@ public class CalciteProvider extends Provider {
tableField.setDeExtractType(deType);
tableField.setDeType(deType);
tableField.setName(resultSet.getString(3));
+ try {
+ tableField.setPrimary(resultSet.getInt(4) > 0);
+ } catch (Exception e) {
+ }
return tableField;
}
@@ -1092,6 +1193,32 @@ public class CalciteProvider extends Provider {
}
}
+ public Statement getStatement(Connection connection, int queryTimeout) {
+ if (connection == null) {
+ DEException.throwException("Failed to get connection!");
+ }
+ Statement stat = null;
+ try {
+ stat = connection.createStatement();
+ stat.setQueryTimeout(queryTimeout);
+ } catch (Exception e) {
+ DEException.throwException(e.getMessage());
+ }
+ return stat;
+ }
+
+ public PreparedStatement getPreparedStatement(Connection connection, int queryTimeout, String sql) throws Exception {
+ if (connection == null) {
+ throw new Exception("Failed to get connection!");
+ }
+ PreparedStatement stat = connection.prepareStatement(sql);
+ try {
+ stat.setQueryTimeout(queryTimeout);
+ } catch (Exception e) {
+ }
+ return stat;
+ }
+
protected boolean isDefaultClassLoader(String customDriver) {
return StringUtils.isEmpty(customDriver) || customDriver.equalsIgnoreCase("default");
}
diff --git a/core/core-backend/src/main/java/io/dataease/datasource/server/DatasourceServer.java b/core/core-backend/src/main/java/io/dataease/datasource/server/DatasourceServer.java
index 8db4b316a6..e57cc64e7c 100644
--- a/core/core-backend/src/main/java/io/dataease/datasource/server/DatasourceServer.java
+++ b/core/core-backend/src/main/java/io/dataease/datasource/server/DatasourceServer.java
@@ -1,5 +1,6 @@
package io.dataease.datasource.server;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -356,7 +357,8 @@ public class DatasourceServer implements DatasourceApi {
List toCreateTables = new ArrayList<>();
List toDeleteTables = new ArrayList<>();
if (dataSourceDTO.getType().equals(DatasourceConfiguration.DatasourceType.API.name())) {
- List sourceTables = ApiUtils.getTables(sourceTableRequest).stream().map(DatasetTableDTO::getTableName).collect(Collectors.toList());
+ requestDatasource.setEnableDataFill(null);
+ List sourceTables = ApiUtils.getTables(sourceTableRequest).stream().map(DatasetTableDTO::getTableName).toList();
List datasetTableDTOS = ApiUtils.getTables(datasourceRequest);
List tables = datasetTableDTOS.stream().map(DatasetTableDTO::getTableName).collect(Collectors.toList());
checkName(datasetTableDTOS.stream().map(DatasetTableDTO::getName).collect(Collectors.toList()));
@@ -410,6 +412,7 @@ public class DatasourceServer implements DatasourceApi {
dataSourceManage.checkName(dataSourceDTO);
dataSourceManage.innerEdit(requestDatasource);
} else if (dataSourceDTO.getType().equals(DatasourceConfiguration.DatasourceType.Excel.name())) {
+ requestDatasource.setEnableDataFill(null);
List sourceTables = ExcelUtils.getTables(sourceTableRequest).stream().map(DatasetTableDTO::getTableName).collect(Collectors.toList());
List tables = ExcelUtils.getTables(datasourceRequest).stream().map(DatasetTableDTO::getTableName).collect(Collectors.toList());
if (dataSourceDTO.getEditType() == 0) {
@@ -439,6 +442,9 @@ public class DatasourceServer implements DatasourceApi {
dataSourceManage.innerEdit(requestDatasource);
}
} else {
+ if (!LicenseUtil.licenseValid()) {
+ requestDatasource.setEnableDataFill(null);
+ }
checkParams(dataSourceDTO.getConfiguration());
dataSourceManage.checkName(dataSourceDTO);
dataSourceManage.innerEdit(requestDatasource);
@@ -515,12 +521,39 @@ public class DatasourceServer implements DatasourceApi {
return getDatasourceDTOById(datasourceId, false);
}
+ @Override
+ public DatasourceDTO innerGet(Long datasourceId) throws DEException {
+ return getDatasourceDTOById(datasourceId, false);
+ }
+
+ @Override
+ public List innerList(List ids) throws DEException {
+ List list = new ArrayList<>();
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
+ if (ids != null) {
+ if (ids.isEmpty()) {
+ return list;
+ } else {
+ queryWrapper.in(CoreDatasource::getId, ids);
+ }
+ }
+ List dsList = datasourceMapper.selectList(queryWrapper);
+ for (CoreDatasource datasource : dsList) {
+ list.add(convertCoreDatasource(datasource.getId(), false, datasource));
+ }
+ return list;
+ }
+
private DatasourceDTO getDatasourceDTOById(Long datasourceId, boolean hidePw) throws DEException {
- DatasourceDTO datasourceDTO = new DatasourceDTO();
CoreDatasource datasource = datasourceMapper.selectById(datasourceId);
if (datasource == null) {
DEException.throwException("不存在的数据源!");
}
+ return convertCoreDatasource(datasourceId, hidePw, datasource);
+ }
+
+ private DatasourceDTO convertCoreDatasource(Long datasourceId, boolean hidePw, CoreDatasource datasource) {
+ DatasourceDTO datasourceDTO = new DatasourceDTO();
BeanUtils.copyBean(datasourceDTO, datasource);
if (datasourceDTO.getType().equalsIgnoreCase(DatasourceConfiguration.DatasourceType.API.toString())) {
diff --git a/core/core-backend/src/main/java/io/dataease/listener/XpackTaskStarter.java b/core/core-backend/src/main/java/io/dataease/listener/XpackTaskStarter.java
index 0154a29f6f..eb83895a7c 100644
--- a/core/core-backend/src/main/java/io/dataease/listener/XpackTaskStarter.java
+++ b/core/core-backend/src/main/java/io/dataease/listener/XpackTaskStarter.java
@@ -1,5 +1,6 @@
package io.dataease.listener;
+import io.dataease.job.schedule.DeDataFillingTaskExecutor;
import io.dataease.job.schedule.DeTaskExecutor;
import io.dataease.license.utils.LicenseUtil;
import io.dataease.utils.LogUtil;
@@ -16,6 +17,9 @@ public class XpackTaskStarter implements ApplicationRunner {
@Resource
private DeTaskExecutor deTaskExecutor;
+ @Resource
+ private DeDataFillingTaskExecutor deDataFillingTaskExecutor;
+
@Override
public void run(ApplicationArguments args) {
try {
@@ -24,5 +28,11 @@ public class XpackTaskStarter implements ApplicationRunner {
} catch (Exception e) {
LogUtil.error(e.getMessage(), e.getCause());
}
+ try {
+ LicenseUtil.validate();
+ deDataFillingTaskExecutor.init();
+ } catch (Exception e) {
+ LogUtil.error(e.getMessage(), e.getCause());
+ }
}
}
diff --git a/core/core-backend/src/main/java/io/dataease/menu/manage/MenuManage.java b/core/core-backend/src/main/java/io/dataease/menu/manage/MenuManage.java
index 3319213d2f..56c0b8c20a 100644
--- a/core/core-backend/src/main/java/io/dataease/menu/manage/MenuManage.java
+++ b/core/core-backend/src/main/java/io/dataease/menu/manage/MenuManage.java
@@ -100,6 +100,8 @@ public class MenuManage {
|| coreMenu.getId().equals(28L)
|| coreMenu.getId().equals(35L)
|| coreMenu.getId().equals(40L)
- || coreMenu.getId().equals(50L);
+ || coreMenu.getId().equals(50L)
+ || coreMenu.getId().equals(60L)
+ || coreMenu.getId().equals(61L);
}
}
diff --git a/core/core-backend/src/main/resources/i18n/core_zh_CN.properties b/core/core-backend/src/main/resources/i18n/core_zh_CN.properties
index d7b38b465d..92383858c6 100644
--- a/core/core-backend/src/main/resources/i18n/core_zh_CN.properties
+++ b/core/core-backend/src/main/resources/i18n/core_zh_CN.properties
@@ -12,6 +12,7 @@ i18n_menu.template-setting=\u6A21\u677F\u7BA1\u7406
i18n_menu.view=\u6570\u636E\u5C55\u793A
i18n_menu.data=\u6570\u636E\u51C6\u5907
i18n_menu.panel=\u4EEA\u8868\u677F
+i18n_menu.data-filling-manage=\u6570\u636E\u586B\u62A5
i18n_menu.screen=\u6570\u636E\u5927\u5C4F
i18n_menu.dataset=\u6570\u636E\u96C6
i18n_menu.datasource=\u6570\u636E\u6E90
@@ -43,7 +44,7 @@ i18n_table_duplicate=\u76F8\u540C\u8282\u70B9\u9700\u91CD\u65B0\u62D6\u5165\u624
i18n_no_column_permission=\u6CA1\u6709\u5217\u6743\u9650
i18n_fetch_error=SQL\u6267\u884C\u5931\u8D25\uFF0C\u8BF7\u68C0\u67E5\u8868\u3001\u5B57\u6BB5\u3001\u5173\u8054\u5173\u7CFB\u7B49\u4FE1\u606F\u662F\u5426\u6B63\u786E\u5E76\u91CD\u65B0\u7F16\u8F91\u3002
i18n_no_datasource_permission=\u65E0\u6570\u636E\u6E90\u8BBF\u95EE\u6743\u9650
-i18n_no_dataset_permission=\u65e0\u6570\u636e\u96c6\u8bbf\u95ee\u6743\u9650
+i18n_no_dataset_permission=\u65E0\u6570\u636E\u96C6\u8BBF\u95EE\u6743\u9650
i18n_not_full=\u5F53\u524D\u6570\u636E\u6E90\u4E0D\u652F\u6301\u5168\u8FDE\u63A5
i18n_field_circular_ref=\u5B57\u6BB5\u5B58\u5728\u5FAA\u73AF\u5F15\u7528
diff --git a/core/core-frontend/src/api/data-filling.ts b/core/core-frontend/src/api/data-filling.ts
new file mode 100644
index 0000000000..ff017ecf1d
--- /dev/null
+++ b/core/core-frontend/src/api/data-filling.ts
@@ -0,0 +1,226 @@
+import request from '@/config/axios'
+import dayjs from 'dayjs'
+
+export function formatDate(value, dateType) {
+ if (!value) {
+ return value
+ }
+ switch (dateType) {
+ case 'year':
+ return dayjs(value).format('YYYY')
+ case 'month':
+ case 'monthrange':
+ return dayjs(value).format('YYYY-MM')
+ case 'datetime':
+ case 'datetimerange':
+ return dayjs(value).format('YYYY-MM-DD HH:mm:ss')
+ default:
+ return dayjs(value).format('YYYY-MM-DD')
+ }
+}
+export interface ColumnItem {
+ props: string
+ label: string
+ date: boolean
+ dateType?: string
+ type: string
+ multiple: boolean
+ rangeIndex?: number
+ disabled?: boolean
+}
+export interface DataFillingOrFolder {
+ name: string
+ action?: string
+ id?: number | string
+ pid?: number | string
+ nodeType: 'folder' | 'data-filling'
+ union?: Array<{}>
+ allFields?: Array<{}>
+}
+
+export interface Tree {
+ name: string
+ value?: string | number
+ id: string | number
+ nodeType: string
+ createBy?: string
+ level: number
+ leaf?: boolean
+ pid: string | number
+ type?: string
+ createTime: number
+ children?: Tree[]
+ request: any
+}
+
+export interface DfFormSetting {
+ id?: string
+ name?: string
+ pid?: string
+ datasource?: string
+ tableName?: string
+ forms: Array
+ createIndex: boolean
+ tableIndexes: Array
+
+ creator?: string
+ updater?: string
+ createTime?: number
+ updateTime?: number
+ weight?: number
+}
+
+export interface DfFormItem {
+ type: string
+ typeName: string
+ icon: string
+ order?: number
+ value?: any
+ id?: string
+ settings: FormItemSetting
+ old?: boolean
+ removed?: boolean
+}
+
+export interface FormItemSetting {
+ name?: string
+ placeholder?: string
+ required?: boolean
+ unique?: boolean
+ inputType?: string
+ optionSourceType?: 1 | 2
+ optionDatasource?: number
+ optionTable?: string
+ optionColumn?: string
+ optionOrder?: string
+ multiple?: boolean
+ dateType?: 'date' | 'daterange'
+ rangeSeparator?: string
+ startPlaceholder?: string
+ endPlaceholder?: string
+ options?: Array
+ mapping: {
+ columnName?: string
+ columnName1?: string
+ columnName2?: string
+ type?: string
+ }
+}
+
+export interface FormItemSettingOptions {
+ name: string
+ value: string
+}
+
+export interface SimpleDatasource {
+ id: string
+ pid: string
+ name: string
+ type: string
+ typeAlias: string
+ status: string
+ enableDataFill: boolean
+}
+
+export const listDataFillingForms = async (data): Promise => {
+ return request
+ .post({ url: '/data-filling/tree', data: { ...data, ...{ busiFlag: 'data-filling' } } })
+ .then(res => {
+ return res?.data
+ })
+}
+
+export const createFolder = (data = {}): Promise => {
+ return request
+ .post({ url: '/data-filling/save', data: { ...data, nodeType: 'folder' } })
+ .then(res => {
+ return res?.data
+ })
+}
+export const save = (data = {}): Promise => {
+ return request.post({ url: '/data-filling/save', data }).then(res => {
+ return res?.data
+ })
+}
+
+export const move = (data = {}): Promise => {
+ return request.post({ url: '/data-filling/move', data }).then(res => {
+ return res?.data
+ })
+}
+
+export const reName = (data = {}): Promise => {
+ return request.post({ url: '/data-filling/rename', data }).then(res => {
+ return res?.data
+ })
+}
+export const listDatasourceList = (): Promise> => {
+ return request.get({ url: '/data-filling/datasource/list' }).then(res => {
+ return res?.data
+ })
+}
+
+export const getDataFilling = async (id: string): Promise => {
+ return request.get({ url: `/data-filling/get/${id}` }).then(res => {
+ return res?.data
+ })
+}
+
+export const deleteById = (id: string): Promise => {
+ return request.get({ url: '/data-filling/delete/' + id })
+}
+
+export const deleteRowData = (formId: string, id: number): Promise => {
+ return request.get({ url: `/data-filling/form/${formId}/delete/${id}` })
+}
+
+export const batchDeleteRowData = (formId: string, data: Array): Promise => {
+ return request.post({ url: `/data-filling/form/${formId}/batch-delete`, data })
+}
+
+export const getTableColumnData = (
+ optionDatasource,
+ optionTable,
+ optionColumn,
+ optionOrder
+): Promise => {
+ return request.post({
+ url: `/data-filling/form/${optionDatasource}/options`,
+ data: {
+ optionTable: optionTable,
+ optionColumn: optionColumn,
+ optionOrder: optionOrder
+ }
+ })
+}
+
+export const searchTable = (id, data): Promise => {
+ return request.post({
+ url: '/data-filling/form/' + id + '/tableData',
+ data
+ })
+}
+
+export const saveFormRowData = (formId, data): Promise => {
+ return request
+ .post({
+ url: '/data-filling/form/' + formId + '/rowData/save',
+ data
+ })
+ .then(res => {
+ return res?.data
+ })
+}
+
+export const saveTask = (data): Promise => {
+ return request.post({
+ url: `/data-filling/task/save`,
+ data
+ })
+}
+
+export const getTaskInfo = (taskId): Promise => {
+ return request.get({
+ url: `/data-filling/task/info/${taskId}`
+ })
+}
diff --git a/core/core-frontend/src/assets/svg/icon_file-doc_colorful.svg b/core/core-frontend/src/assets/svg/icon_file-doc_colorful.svg
new file mode 100644
index 0000000000..7371ef72ab
--- /dev/null
+++ b/core/core-frontend/src/assets/svg/icon_file-doc_colorful.svg
@@ -0,0 +1,5 @@
+
diff --git a/core/core-frontend/src/assets/svg/icon_multi-line_outlined.svg b/core/core-frontend/src/assets/svg/icon_multi-line_outlined.svg
new file mode 100644
index 0000000000..24c4b1cc2c
--- /dev/null
+++ b/core/core-frontend/src/assets/svg/icon_multi-line_outlined.svg
@@ -0,0 +1,11 @@
+
diff --git a/core/core-frontend/src/assets/svg/icon_radio_outlined.svg b/core/core-frontend/src/assets/svg/icon_radio_outlined.svg
new file mode 100644
index 0000000000..2206cec10f
--- /dev/null
+++ b/core/core-frontend/src/assets/svg/icon_radio_outlined.svg
@@ -0,0 +1,11 @@
+
diff --git a/core/core-frontend/src/assets/svg/icon_single-line_outlined.svg b/core/core-frontend/src/assets/svg/icon_single-line_outlined.svg
new file mode 100644
index 0000000000..5cd8e7b53d
--- /dev/null
+++ b/core/core-frontend/src/assets/svg/icon_single-line_outlined.svg
@@ -0,0 +1,11 @@
+
diff --git a/core/core-frontend/src/assets/svg/icon_todo_outlined.svg b/core/core-frontend/src/assets/svg/icon_todo_outlined.svg
new file mode 100644
index 0000000000..633b9b1ece
--- /dev/null
+++ b/core/core-frontend/src/assets/svg/icon_todo_outlined.svg
@@ -0,0 +1,3 @@
+
diff --git a/core/core-frontend/src/components/icon-custom/src/Icon.vue b/core/core-frontend/src/components/icon-custom/src/Icon.vue
index cd5c87b9e4..7d967ce2b3 100644
--- a/core/core-frontend/src/components/icon-custom/src/Icon.vue
+++ b/core/core-frontend/src/components/icon-custom/src/Icon.vue
@@ -661,6 +661,11 @@ import wizard_quick_start from '@/assets/svg/wizard_quick_start.svg'
import wordCloudDark from '@/assets/svg/word-cloud-dark.svg'
import wordCloudOrigin from '@/assets/svg/word-cloud-origin.svg'
import wordCloud from '@/assets/svg/word-cloud.svg'
+import icon_multi_line_outlined from '@/assets/svg/icon_multi-line_outlined.svg'
+import icon_radio_outlined from '@/assets/svg/icon_radio_outlined.svg'
+import icon_single_line_outlined from '@/assets/svg/icon_single-line_outlined.svg'
+import icon_todo_outlined from '@/assets/svg/icon_todo_outlined.svg'
+import icon_file_doc_colorful from '@/assets/svg/icon_file-doc_colorful.svg'
const iconMap = {
'401': _401,
'403': _403,
@@ -1062,6 +1067,10 @@ const iconMap = {
'icon_right-association': icon_rightAssociation,
icon_right_outlined: icon_right_outlined,
icon_scroll_filled: icon_scroll_filled,
+ icon_radio_outlined: icon_radio_outlined,
+ icon_todo_outlined: icon_todo_outlined,
+ 'icon_single-line_outlined': icon_single_line_outlined,
+ 'icon_multi-line_outlined': icon_multi_line_outlined,
'icon_search-outline_outlined': icon_searchOutline_outlined,
icon_search: icon_search,
'icon_share-label_filled': icon_shareLabel_filled,
@@ -1320,7 +1329,8 @@ const iconMap = {
wizard_quick_start: wizard_quick_start,
'word-cloud-dark': wordCloudDark,
'word-cloud-origin': wordCloudOrigin,
- 'word-cloud': wordCloud
+ 'word-cloud': wordCloud,
+ 'icon_file-doc_colorful': icon_file_doc_colorful
}
const props = defineProps({
diff --git a/core/core-frontend/src/hooks/web/useMoveLine.ts b/core/core-frontend/src/hooks/web/useMoveLine.ts
index 7602ad59e8..f1413f3c0d 100644
--- a/core/core-frontend/src/hooks/web/useMoveLine.ts
+++ b/core/core-frontend/src/hooks/web/useMoveLine.ts
@@ -1,7 +1,7 @@
import { ref, onBeforeUnmount, onMounted } from 'vue'
import { useCache } from '@/hooks/web/useCache'
-type Sidebar = 'DATASET' | 'DASHBOARD' | 'DATASOURCE'
+type Sidebar = 'DATASET' | 'DASHBOARD' | 'DATASOURCE' | 'DATA-FILLING'
export const useMoveLine = (type: Sidebar) => {
const { wsCache } = useCache('localStorage')
diff --git a/core/core-frontend/src/locales/en.ts b/core/core-frontend/src/locales/en.ts
index 06d6fefd59..191e0af9fa 100644
--- a/core/core-frontend/src/locales/en.ts
+++ b/core/core-frontend/src/locales/en.ts
@@ -1,5 +1,15 @@
export default {
common: {
+ component: {
+ input: 'Input',
+ textarea: 'Textarea',
+ select: 'Select',
+ radio: 'Radio',
+ checkbox: 'Checkbox',
+ date: 'Date Picker',
+ dateRange: 'Date Range Picker',
+ add_component_hint: 'Click or drag the component on the left to add a field'
+ },
inputText: 'Please input',
account: 'Account',
email: 'Email',
@@ -250,5 +260,163 @@ export default {
template_manage: {
name_already_exists_type: 'Classification name already exists',
the_same_category: 'The template name already exists under the same category'
+ },
+ data_fill: {
+ data_fill: 'Data Filling',
+ permission: 'Data Filling Permission',
+ enable: 'Enable',
+ enable_hint: 'Cannot disable after enable',
+ new_folder: 'New Folder',
+ form_manage: 'Form Manage',
+ my_job: 'My Job',
+ form: {
+ mobile_number_format_is_incorrect: 'Incorrect format of mobile phone number',
+ email_format_is_incorrect: 'The mailbox format is incorrect',
+ name: 'Name',
+ rename: 'Rename',
+ untitled: 'Untitled',
+ create_new_form: 'Create New Form',
+ copy_new_form: 'Copy Form',
+ edit_form: 'Edit Form',
+ title: 'Title',
+ no_form: 'Click to Create New',
+ form_list_name: 'Form List',
+ create_form: 'Create Form',
+ please_select: 'Please Select',
+ component: 'Component',
+ component_setting: 'Component Setting',
+ hint: 'Hint',
+ option: 'Option',
+ form_setting: 'Form Setting',
+ input_limit_50: 'No more than 50 characters',
+ confirm_delete: 'Confirm delete? (The tables created in database will not be deleted)',
+ list: 'Form List',
+ record: 'Fill Record',
+ task_manage: 'Task Manage',
+ form_name: 'Form Name',
+ commit_type: 'Form Commit Type',
+ commit_type_append: 'Append',
+ commit_type_update: 'Update',
+ commit_rule: 'Update Rules',
+ commit_rule_add: 'Add Update Rule',
+ commit_rule_settings: 'Update Rule Settings',
+ commit_rule_set: 'Set',
+ folder: 'Folder',
+ datasource: 'Datasource',
+ table: 'Table',
+ creator: 'Creator',
+ createTime: 'Create Time',
+ operation: 'Operation',
+ operator: 'Operator',
+ operate_time: 'Operate Time',
+ modify: 'Modify',
+ show: 'Show',
+ delete: 'Delete',
+ show_data: 'Show Data',
+ text: 'Text',
+ number: 'Number',
+ tel: 'Tel',
+ email: 'Email',
+ duplicate_error: 'Duplicate',
+ value_not_exists: 'Value Not Exists',
+ range_separator: 'Range Separator',
+ start_hint_word: 'Start Hint Word',
+ end_hint_word: 'End Hint Word',
+ input_type: 'Input Type',
+ date_type: 'Date Format',
+ check: 'Check',
+ set_required: 'Set Required',
+ set_unique: 'Set Unique',
+ set_multiple: 'Set Multiple',
+ use_datetime: 'Use Datetime',
+ custom: 'Custom',
+ use_datasource: 'Bind Datasource',
+ bind_column: 'Bind Column',
+ bind_complete: 'Bind',
+ option_value: 'Options',
+ add_option: 'Add Option',
+ form_name_cannot_none: 'Form name cannot be null',
+ form_update_rule_none: 'Update Rules cannot be null',
+ form_components_cannot_null: 'Form components cannot be null',
+ option_list_cannot_empty: 'Option list cannot be empty',
+ option_list_datasource_cannot_empty: 'Datasource Settings of option list cannot be empty',
+ component_setting_error: 'Component setting error',
+ table_name: 'Table',
+ form_column: 'Form Column',
+ column_name: 'Table Column',
+ column_type: 'Table Column Type',
+ create_index: 'Create Index',
+ add_index: 'Add Index',
+ index_name: 'Index Name',
+ create_index_hint:
+ 'MySQL versions earlier than 8.0 or MariaDB versions earlier than 10.8.0 do not support Descending indexes',
+ index_column: 'Index Column',
+ order: 'Sort',
+ order_asc: 'Asc',
+ order_desc: 'Desc',
+ order_none: 'Default Order',
+ add_column: 'Add Column',
+ please_insert_start: 'Start Time Column Name',
+ please_insert_end: 'End Time Column Name',
+ save_form: 'Save Form',
+ default: 'default',
+ default_built_in: 'Built-in Database'
+ },
+ database: {
+ nvarchar: 'Nvarchar',
+ text: 'Text',
+ number: 'Number',
+ decimal: 'Decimal',
+ datetime: 'Datetime'
+ },
+ data: {
+ confirm_delete: 'Confirm delete?',
+ add_data: 'Add Data',
+ download_template: 'Download Template',
+ insert_data: 'Insert Data',
+ update_data: 'Update Data',
+ delete_data: 'Delete Data',
+ recent_committer: 'Recent Committer',
+ recent_commit_time: 'Recent Commit Time',
+ start: 'Start',
+ end: 'End',
+ id_is: 'ID [',
+ data_not_found: '] Not Found'
+ },
+ task: {
+ name: 'Name',
+ creator: 'Creator',
+ create_time: 'Create Time',
+ rate_type: 'Rate Type',
+ task_status: 'Status',
+ add_task: 'Add Task',
+ task_name: 'Task Name',
+ task_remain_time: 'Remaining Validity',
+ task_sender: 'Task Sender',
+ start_filling: 'Start Filling',
+ task_distribute_time: 'Distribution Time',
+ task_expiration_time: 'Expiration Time',
+ task_finished_time: 'Finished Time',
+ task_end_time: 'End Time',
+ edit_data: 'Edit Data',
+ show_data: 'Show Data',
+ confirm_enable: 'Confirm enable task?',
+ confirm_disable: 'Confirm disable task?',
+ edit_task: 'Edit Task',
+ create_task: 'Create Task',
+ edit: 'Edit',
+ stop: 'Stop',
+ start: 'Start',
+ delete: 'Delete',
+ no_time_limit: 'No Time Limit',
+ todo: 'Todo',
+ finished: 'Committed',
+ expired: 'Expired',
+ task_finish_in: 'Task Finished in ',
+ task_finish_in_suffix: '',
+ open_sub_task: 'Open Sub Tasks'
+ },
+ on_the_left: 'Please select a form on the left',
+ search_by_commit_name: 'Search by operator name'
}
}
diff --git a/core/core-frontend/src/locales/tw.ts b/core/core-frontend/src/locales/tw.ts
index 68e07d703e..aaed10e25f 100644
--- a/core/core-frontend/src/locales/tw.ts
+++ b/core/core-frontend/src/locales/tw.ts
@@ -1,5 +1,15 @@
export default {
common: {
+ component: {
+ input: '單行輸入',
+ textarea: '多行輸入',
+ select: '下拉框',
+ radio: '單選',
+ checkbox: '多選框',
+ date: '日期',
+ dateRange: '時間范圍',
+ add_component_hint: '點擊或拖拽左側組件添加字段'
+ },
inputText: '请输入',
add: '添加',
account: '账号',
@@ -152,5 +162,162 @@ export default {
template_manage: {
name_already_exists_type: '分类名称已存在',
the_same_category: '同一分类下,该模板名称已存在'
+ },
+ data_fill: {
+ data_fill: '數據填報',
+ permission: '填報權限',
+ enable: '開啟',
+ enable_hint: '數據填報開啟后,可將表單數據存放至數據源中,一旦開啟后,后期不允許關閉。',
+ new_folder: '新建文件夾',
+ form_manage: '表單管理',
+ my_job: '我的填報',
+ form: {
+ mobile_number_format_is_incorrect: '手機號碼格式不正確',
+ email_format_is_incorrect: '郵箱格式不正確',
+ name: '名稱',
+ rename: '重命名',
+ untitled: '未命名表單',
+ create_new_form: '新建表單',
+ copy_new_form: '復制表單',
+ edit_form: '編輯表單',
+ title: '標題',
+ no_form: '暫無表單,點擊',
+ form_list_name: '填報表單',
+ create_form: '新建表單',
+ please_select: '請選擇',
+ component: '組件',
+ component_setting: '組件設置',
+ hint: '提示詞',
+ input_limit_50: '不超過50個字符',
+ option: '選項',
+ form_setting: '表單設置',
+ confirm_delete: '確認刪除?(不會刪除已創建的數據庫表)',
+ list: '表單數據',
+ record: '提交記錄',
+ task_manage: '任務管理',
+ form_name: '表單名稱',
+ commit_type: '表單提交方式',
+ commit_type_append: '數據追加',
+ commit_type_update: '數據更新',
+ commit_rule: '更新條件',
+ commit_rule_add: '添加更新規則',
+ commit_rule_settings: '更新規則設置',
+ commit_rule_set: '已設置',
+ folder: '所屬文件夾',
+ datasource: '數據源',
+ table: '數據庫表',
+ creator: '創建人',
+ createTime: '創建時間',
+ operation: '操作',
+ operator: '操作人',
+ operate_time: '操作時間',
+ modify: '修改',
+ show: '查看',
+ delete: '刪除',
+ show_data: '查看數據',
+ text: '普通文本',
+ number: '數字',
+ tel: '手機號',
+ email: '郵箱',
+ duplicate_error: '重復',
+ value_not_exists: '值不存在',
+ range_separator: '分割字符',
+ start_hint_word: '開始提示詞',
+ end_hint_word: '結束提示詞',
+ input_type: '格式類型',
+ date_type: '展示粒度',
+ check: '校驗',
+ set_required: '設置為必填項',
+ set_unique: '不允許重復值',
+ set_multiple: '允許多選',
+ use_datetime: '使用日期時間',
+ custom: '自定義',
+ use_datasource: '綁定數據源',
+ bind_column: '綁定字段',
+ bind_complete: '已綁定',
+ option_value: '選項值',
+ add_option: '添加選項值',
+ form_name_cannot_none: '表單名稱不能為空',
+ form_update_rule_none: '請配置更新規則',
+ form_components_cannot_null: '請添加表單組件',
+ option_list_cannot_empty: '選項值不能為空',
+ option_list_datasource_cannot_empty: '選項值綁定數據源配置不能為空',
+ component_setting_error: '組件設置錯誤',
+ table_name: '數據庫表名',
+ form_column: '表單字段',
+ column_name: '數據庫表字段名稱',
+ column_type: '數據庫字段類型',
+ create_index: '創建索引',
+ add_index: '新增索引',
+ index_name: '索引名稱',
+ create_index_hint: 'MySQL 8.0 或 MariaDB 10.8.0 以下版本不支持索引降序排序',
+ index_column: '索引字段',
+ order: '排序',
+ order_asc: '升序',
+ order_desc: '降序',
+ order_none: '默認排序',
+ add_column: '新增字段',
+ please_insert_start: '請輸入開始時間',
+ please_insert_end: '請輸入結束時間',
+ save_form: '保存表單',
+ default: '默認',
+ default_built_in: '內建數據庫'
+ },
+ database: {
+ nvarchar: '字符串',
+ text: '長文本',
+ number: '整型數字',
+ decimal: '小數數字',
+ datetime: '日期'
+ },
+ data: {
+ confirm_delete: '確認刪除?',
+ add_data: '添加數據',
+ download_template: '下載模板',
+ insert_data: '插入數據',
+ update_data: '更新數據',
+ delete_data: '刪除數據',
+ recent_committer: '最近提交人',
+ recent_commit_time: '最近提交時間',
+ start: '開始',
+ end: '結束',
+ id_is: 'ID為[',
+ data_not_found: ']的數據不存在'
+ },
+ task: {
+ name: '名稱',
+ creator: '創建人',
+ create_time: '創建時間',
+ rate_type: '任務下發模式',
+ task_status: '任務狀態',
+ add_task: '添加任務',
+ task_name: '任務名稱',
+ task_remain_time: '任務有效期',
+ task_sender: '任務下發人',
+ start_filling: '立即填報',
+ task_distribute_time: '任務下發時間',
+ task_expiration_time: '任務過期時間',
+ task_finished_time: '任務完成時間',
+ task_end_time: '任務截止時間',
+ edit_data: '編輯數據',
+ show_data: '查看數據',
+ confirm_enable: '確認啟動任務?(單次任務會新建下發任務)',
+ confirm_disable: '確認停止任務?',
+ edit_task: '編輯任務',
+ create_task: '新建任務',
+ edit: '編輯',
+ stop: '停止',
+ start: '啟動',
+ delete: '刪除',
+ no_time_limit: '不限時',
+ todo: '待辦項',
+ finished: '已提交',
+ expired: '已過期',
+ task_finish_in: '在任務下發',
+ task_finish_in_suffix: '內完成填報',
+ open_sub_task: '查看子任務'
+ },
+ on_the_left: '請在左側選擇表單',
+ search_by_commit_name: '根據操作人名稱搜索'
}
}
diff --git a/core/core-frontend/src/locales/zh-CN.ts b/core/core-frontend/src/locales/zh-CN.ts
index c887e48ef2..d86105cc7f 100644
--- a/core/core-frontend/src/locales/zh-CN.ts
+++ b/core/core-frontend/src/locales/zh-CN.ts
@@ -1,5 +1,15 @@
export default {
common: {
+ component: {
+ input: '单行输入',
+ textarea: '多行输入',
+ select: '下拉框',
+ radio: '单选',
+ checkbox: '多选框',
+ date: '日期',
+ dateRange: '时间范围',
+ add_component_hint: '点击或拖拽左侧组件添加字段'
+ },
inputText: '请输入',
add: '添加',
account: '账号',
@@ -2562,6 +2572,10 @@ export default {
once_a_day: '每天',
once_a_week: '每周',
once_a_month: '每月',
+ hour: '小时',
+ day: '天',
+ week: '周',
+ month: '月',
week_mon: '一',
week_tue: '二',
week_wed: '三',
@@ -2578,5 +2592,162 @@ export default {
variable: {
give_up: 's',
save_apply: '保存并应用'
+ },
+ data_fill: {
+ data_fill: '数据填报',
+ permission: '填报权限',
+ enable: '开启',
+ enable_hint: '数据填报开启后,可将表单数据存放至数据源中,一旦开启后,后期不允许关闭。',
+ new_folder: '新建文件夹',
+ form_manage: '表单管理',
+ my_job: '我的填报',
+ form: {
+ special_characters_are_not_supported: '不支持特殊字符',
+ mobile_number_format_is_incorrect: '手机号码格式不正确',
+ name: '名称',
+ rename: '重命名',
+ untitled: '未命名表单',
+ create_new_form: '新建表单',
+ copy_new_form: '复制表单',
+ edit_form: '编辑表单',
+ title: '标题',
+ no_form: '暂无表单,点击',
+ form_list_name: '填报表单',
+ create_form: '新建表单',
+ please_select: '请选择',
+ component: '组件',
+ component_setting: '组件设置',
+ hint: '提示词',
+ input_limit_50: '不超过50个字符',
+ option: '选项',
+ form_setting: '表单设置',
+ confirm_delete: '确认删除?(不会删除已创建的数据库表)',
+ list: '表单数据',
+ record: '提交记录',
+ task_manage: '任务管理',
+ form_name: '表单名称',
+ commit_type: '表单提交方式',
+ commit_type_append: '数据追加',
+ commit_type_update: '数据更新',
+ commit_rule: '更新条件',
+ commit_rule_add: '添加更新规则',
+ commit_rule_settings: '更新规则设置',
+ commit_rule_set: '已设置',
+ folder: '所属文件夹',
+ datasource: '数据源',
+ table: '数据库表',
+ creator: '创建人',
+ createTime: '创建时间',
+ operation: '操作',
+ operator: '操作人',
+ operate_time: '操作时间',
+ modify: '修改',
+ show: '查看',
+ delete: '删除',
+ show_data: '查看数据',
+ text: '普通文本',
+ number: '数字',
+ tel: '手机号',
+ email: '邮箱',
+ duplicate_error: '重复',
+ value_not_exists: '值不存在',
+ range_separator: '分割字符',
+ start_hint_word: '开始提示词',
+ end_hint_word: '结束提示词',
+ input_type: '格式类型',
+ date_type: '展示粒度',
+ check: '校验',
+ set_required: '设置为必填项',
+ set_unique: '不允许重复值',
+ set_multiple: '允许多选',
+ use_datetime: '使用日期时间',
+ custom: '自定义',
+ use_datasource: '绑定数据源',
+ bind_column: '绑定字段',
+ bind_complete: '已绑定',
+ option_value: '选项值',
+ add_option: '添加选项值',
+ form_name_cannot_none: '表单名称不能为空',
+ form_update_rule_none: '请配置更新规则',
+ form_components_cannot_null: '请添加表单组件',
+ option_list_cannot_empty: '选项值不能为空',
+ option_list_datasource_cannot_empty: '选项值绑定数据源配置不能为空',
+ component_setting_error: '组件设置错误',
+ table_name: '数据库表名',
+ form_column: '表单字段',
+ column_name: '数据库表字段名称',
+ column_type: '数据库字段类型',
+ create_index: '创建索引',
+ add_index: '新增索引',
+ index_name: '索引名称',
+ create_index_hint: 'MySQL 8.0 或 MariaDB 10.8.0 以下版本不支持索引降序排序',
+ index_column: '索引字段',
+ order: '排序',
+ order_asc: '升序',
+ order_desc: '降序',
+ order_none: '默认排序',
+ add_column: '新增字段',
+ please_insert_start: '请输入开始时间',
+ please_insert_end: '请输入结束时间',
+ save_form: '保存表单',
+ default: '默认',
+ default_built_in: '内建数据库'
+ },
+ database: {
+ nvarchar: '字符串',
+ text: '长文本',
+ number: '整型数字',
+ decimal: '小数数字',
+ datetime: '日期'
+ },
+ data: {
+ confirm_delete: '确认删除?',
+ add_data: '添加数据',
+ download_template: '下载模板',
+ insert_data: '插入数据',
+ update_data: '更新数据',
+ delete_data: '删除数据',
+ recent_committer: '最近提交人',
+ recent_commit_time: '最近提交时间',
+ start: '开始',
+ end: '结束',
+ id_is: 'ID为[',
+ data_not_found: ']的数据不存在'
+ },
+ task: {
+ name: '名称',
+ creator: '创建人',
+ create_time: '创建时间',
+ rate_type: '任务下发模式',
+ task_status: '任务状态',
+ task_name: '任务名称',
+ add_task: '添加任务',
+ task_remain_time: '任务有效期',
+ task_sender: '任务下发人',
+ start_filling: '立即填报',
+ task_distribute_time: '任务下发时间',
+ task_expiration_time: '任务过期时间',
+ task_finished_time: '任务完成时间',
+ task_end_time: '任务截止时间',
+ edit_data: '编辑数据',
+ show_data: '查看数据',
+ confirm_enable: '确认启动任务?(单次任务会新建下发任务)',
+ confirm_disable: '确认停止任务?',
+ edit_task: '编辑任务',
+ create_task: '新建任务',
+ edit: '编辑',
+ stop: '停止',
+ start: '启动',
+ delete: '删除',
+ no_time_limit: '不限时',
+ todo: '待办项',
+ finished: '已提交',
+ expired: '已过期',
+ task_finish_in: '在任务下发',
+ task_finish_in_suffix: '内完成填报',
+ open_sub_task: '查看子任务'
+ },
+ on_the_left: '请在左侧选择表单',
+ search_by_commit_name: '根据操作人名称搜索'
}
}
diff --git a/core/core-frontend/src/store/modules/interactive.ts b/core/core-frontend/src/store/modules/interactive.ts
index 40d7392e98..c1e8292a02 100644
--- a/core/core-frontend/src/store/modules/interactive.ts
+++ b/core/core-frontend/src/store/modules/interactive.ts
@@ -1,5 +1,5 @@
import { defineStore } from 'pinia'
-import { store } from '../index'
+import { store } from '@/store'
import { queryTreeApi } from '@/api/visualization/dataVisualization'
import { getDatasetTree } from '@/api/dataset'
import { listDatasources } from '@/api/datasource'
@@ -7,6 +7,7 @@ import type { BusiTreeRequest, BusiTreeNode } from '@/models/tree/TreeNode'
import { pathValid } from '@/store/modules/permission'
import { useCache } from '@/hooks/web/useCache'
import { useAppStoreWithOut } from '@/store/modules/app'
+import { listDataFillingForms } from '@/api/data-filling'
const appStore = useAppStoreWithOut()
const { wsCache } = useCache()
export interface InnerInteractive {
@@ -21,9 +22,9 @@ interface InteractiveState {
data: Record
}
-const apiMap = [queryTreeApi, queryTreeApi, getDatasetTree, listDatasources]
+const apiMap = [queryTreeApi, queryTreeApi, getDatasetTree, listDatasources, listDataFillingForms]
-const busiFlagMap = ['dashboard', 'dataV', 'dataset', 'datasource']
+const busiFlagMap = ['dashboard', 'dataV', 'dataset', 'datasource', 'data-filling']
export const interactiveStore = defineStore('interactive', {
state: (): InteractiveState => ({
diff --git a/core/core-frontend/src/utils/validate.ts b/core/core-frontend/src/utils/validate.ts
index 1cef68b90b..efb9858a7d 100644
--- a/core/core-frontend/src/utils/validate.ts
+++ b/core/core-frontend/src/utils/validate.ts
@@ -12,3 +12,5 @@ export function validUsername(str) {
}
export const PHONE_REGEX = '^1[3|4|5|7|8][0-9]{9}$'
+
+export const EMAIL_REGEX = '^[a-zA-Z0-9_._-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$'
diff --git a/core/core-frontend/src/views/visualized/data/datasource/form/EditorDetail.vue b/core/core-frontend/src/views/visualized/data/datasource/form/EditorDetail.vue
index 655a31d5ce..40b39e85ec 100644
--- a/core/core-frontend/src/views/visualized/data/datasource/form/EditorDetail.vue
+++ b/core/core-frontend/src/views/visualized/data/datasource/form/EditorDetail.vue
@@ -14,6 +14,7 @@ import { CustomPassword } from '@/components/custom-password'
import { ElForm, ElMessage, ElMessageBox } from 'element-plus-secondary'
import Cron from '@/components/cron/src/Cron.vue'
import { ComponentPublicInstance } from 'vue'
+import { XpackComponent } from '@/components/plugin'
const { t } = useI18n()
const prop = defineProps({
form: {
@@ -28,6 +29,7 @@ const prop = defineProps({
configuration?: Configuration
apiConfiguration?: ApiConfiguration[]
paramsConfiguration?: ApiConfiguration[]
+ enableDataFill?: boolean
}>({
id: 0,
name: '',
@@ -340,16 +342,16 @@ const setItemRef = (ele: ComponentPublicInstance | null | Element) => {
}
const copyItem = (item?: ApiConfiguration) => {
- var newItem = JSON.parse(JSON.stringify(item))
+ const newItem = JSON.parse(JSON.stringify(item))
newItem.deTableName = ''
newItem.serialNumber =
form.value.apiConfiguration[form.value.apiConfiguration.length - 1].serialNumber + 1
- var reg = new RegExp(item.name + '_copy_' + '([0-9]*)', 'gim')
- var number = 0
- for (var i = 1; i < form.value.apiConfiguration.length; i++) {
- var match = form.value.apiConfiguration[i].name.match(reg)
+ const reg = new RegExp(item.name + '_copy_' + '([0-9]*)', 'gim')
+ let number = 0
+ for (let i = 1; i < form.value.apiConfiguration.length; i++) {
+ const match = form.value.apiConfiguration[i].name.match(reg)
if (match !== null) {
- var num = match[0].substring(
+ const num = match[0].substring(
form.value.apiConfiguration[i].name.length + 5,
match[0].length - 1
)
@@ -413,7 +415,7 @@ const resetForm = () => {
}
const returnItem = apiItem => {
- var find = false
+ let find = false
if (apiItem.type !== 'params') {
apiItem.status = 'Success'
for (let i = 0; i < form.value.apiConfiguration.length; i++) {
@@ -1227,6 +1229,12 @@ defineExpose({
+
+
+
{
return
} else if (currentDsType.value === 'API') {
- for (var i = 0; i < request.apiConfiguration.length; i++) {
+ for (let i = 0; i < request.apiConfiguration.length; i++) {
if (
request.apiConfiguration[i].deTableName === '' ||
request.apiConfiguration[i].deTableName === undefined ||
@@ -445,7 +445,7 @@ const saveDS = () => {
uuid.v1().replaceAll('-', '').substring(0, 10)
}
request.apiConfiguration[i].jsonFields = []
- for (var j = 0; j < request.apiConfiguration[i].fields.length; j++) {
+ for (let j = 0; j < request.apiConfiguration[i].fields.length; j++) {
request.apiConfiguration[i].fields[j].value = []
}
}
diff --git a/core/core-frontend/src/views/visualized/data/datasource/index.vue b/core/core-frontend/src/views/visualized/data/datasource/index.vue
index 111f822778..97082ce1a2 100644
--- a/core/core-frontend/src/views/visualized/data/datasource/index.vue
+++ b/core/core-frontend/src/views/visualized/data/datasource/index.vue
@@ -366,7 +366,8 @@ const defaultInfo = {
configuration: null,
syncSetting: null,
apiConfiguration: [],
- weight: 0
+ weight: 0,
+ enableDataFill: false
}
const nodeInfo = reactive(cloneDeep(defaultInfo))
const infoList = computed(() => {
@@ -504,7 +505,8 @@ const handleNodeClick = data => {
fileName,
size,
description,
- lastSyncTime
+ lastSyncTime,
+ enableDataFill
} = res.data
if (configuration) {
configuration = JSON.parse(Base64.decode(configuration))
@@ -531,7 +533,8 @@ const handleNodeClick = data => {
apiConfiguration: apiConfigurationStr,
paramsConfiguration: paramsStr,
weight: data.weight,
- lastSyncTime
+ lastSyncTime,
+ enableDataFill
})
activeTab.value = ''
activeName.value = 'config'
@@ -628,7 +631,8 @@ const editDatasource = (editType?: number) => {
fileName,
size,
description,
- lastSyncTime
+ lastSyncTime,
+ enableDataFill
} = res.data
if (configuration) {
configuration = JSON.parse(Base64.decode(configuration))
@@ -656,6 +660,7 @@ const editDatasource = (editType?: number) => {
apiConfiguration: apiConfigurationStr,
paramsConfiguration: paramsStr,
lastSyncTime,
+ enableDataFill,
isPlugin: arr && arr.length > 0,
staticMap: arr[0]?.staticMap
})
diff --git a/core/core-frontend/src/views/workbranch/ShortcutTable.vue b/core/core-frontend/src/views/workbranch/ShortcutTable.vue
index 8d5ec781a5..f93c122177 100644
--- a/core/core-frontend/src/views/workbranch/ShortcutTable.vue
+++ b/core/core-frontend/src/views/workbranch/ShortcutTable.vue
@@ -15,6 +15,7 @@ import ShareGrid from '@/views/share/share/ShareGrid.vue'
import ShareHandler from '@/views/share/share/ShareHandler.vue'
import { useAppStoreWithOut } from '@/store/modules/app'
import { useEmbedded } from '@/store/modules/embedded'
+import { XpackComponent } from '@/components/plugin'
const userStore = useUserStoreWithOut()
const { resolve } = useRouter()
const { t } = useI18n()
@@ -263,6 +264,8 @@ const getEmptyDesc = (): string => {
+
+
@@ -419,6 +422,10 @@ const getEmptyDesc = (): string => {
+
innerList(List ids) throws DEException;
+
@DePermit({"#p0+':read'"})
@GetMapping("/hidePw/{datasourceId}")
DatasourceDTO hidePw(@PathVariable("datasourceId") Long datasourceId) throws DEException;
diff --git a/sdk/api/api-base/src/main/java/io/dataease/api/report/vo/ReportGridVO.java b/sdk/api/api-base/src/main/java/io/dataease/api/report/vo/ReportGridVO.java
index 50c7d59ff9..7dcb97e930 100644
--- a/sdk/api/api-base/src/main/java/io/dataease/api/report/vo/ReportGridVO.java
+++ b/sdk/api/api-base/src/main/java/io/dataease/api/report/vo/ReportGridVO.java
@@ -27,5 +27,7 @@ public class ReportGridVO implements Serializable {
private String creator;
+ private String updater;
+
private Long createTime;
}
diff --git a/sdk/api/api-base/src/main/java/io/dataease/api/xpack/dataFilling/DataFillingApi.java b/sdk/api/api-base/src/main/java/io/dataease/api/xpack/dataFilling/DataFillingApi.java
new file mode 100644
index 0000000000..d86b7baa59
--- /dev/null
+++ b/sdk/api/api-base/src/main/java/io/dataease/api/xpack/dataFilling/DataFillingApi.java
@@ -0,0 +1,111 @@
+package io.dataease.api.xpack.dataFilling;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+import io.dataease.api.report.dto.ReportInstanceMsgRequest;
+import io.dataease.api.report.vo.ReportGridVO;
+import io.dataease.api.xpack.dataFilling.dto.*;
+import io.dataease.auth.DeApiPath;
+import io.dataease.exception.DEException;
+import io.dataease.extensions.datasource.dto.SimpleDatasourceDTO;
+import io.dataease.model.BusiNodeRequest;
+import io.dataease.model.BusiNodeVO;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+
+import java.util.List;
+import java.util.Map;
+
+import static io.dataease.constant.AuthResourceEnum.DATA_FILLING;
+
+@Tag(name = "数据填报")
+@ApiSupport(order = 1000, author = "fit2cloud-someone")
+@DeApiPath(value = "/data-filling", rt = DATA_FILLING)
+public interface DataFillingApi {
+
+ @PostMapping("tree")
+ List tree(@RequestBody BusiNodeRequest request) throws DEException;
+
+ @GetMapping("/get/{id}")
+ DataFillingDTO get(@PathVariable("id") Long id);
+
+ @PostMapping("/move")
+ DataFillingDTO move(@RequestBody DataFillingDTO dataFillingDTO);
+
+ @PostMapping("/save")
+ DataFillingDTO save(@RequestBody DataFillingDTO dataFillingDTO) throws Exception;
+
+ @PostMapping("/rename")
+ DataFillingDTO rename(@RequestBody DataFillingDTO dataFillingDTO);
+
+ @GetMapping("delete/{id}")
+ void delete(@PathVariable("id") Long id);
+
+ @GetMapping("/datasource/list")
+ List listDatasourceList();
+
+ @PostMapping("/form/{optionDatasource}/options")
+ List listColumnData(@PathVariable("optionDatasource") Long optionDatasource, @RequestBody DatasourceOptionsRequest request) throws Exception;
+
+ @PostMapping("/form/{id}/tableData")
+ DataFillFormTableDataResponse tableData(@PathVariable Long id, @RequestBody DataFillFormTableDataRequest request) throws Exception;
+
+ @GetMapping("/form/{formId}/delete/{id}")
+ void deleteRowData(@PathVariable Long formId, @PathVariable Long id) throws Exception;
+
+ @PostMapping("/form/{formId}/batch-delete")
+ void batchDeleteRowData(@PathVariable Long formId, @RequestBody List ids) throws Exception;
+
+ @PostMapping("/form/{formId}/rowData/save")
+ DataFillFormTableDataResponse saveRowData(@PathVariable Long formId, @RequestBody Map data) throws Exception;
+
+
+ @GetMapping("/task/info/{taskId}")
+ TaskInfoVO info(@PathVariable("taskId") Long taskId);
+
+
+ @PostMapping("/task/save")
+ Long save(@RequestBody TaskInfoVO task);
+
+ @PostMapping("/task/logMsg")
+ String logMsg(@RequestBody ReportInstanceMsgRequest request);
+
+ @PostMapping("/task/page/{goPage}/{pageSize}")
+ IPage taskPager(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody DfTaskInfoRequest request);
+
+ @PostMapping("/sub-task/page/{goPage}/{pageSize}")
+ IPage subTaskPager(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody DfSubTaskInfoRequest request);
+
+ @PostMapping("/task/delete")
+ void batchDeleteTask(@RequestBody List ids) throws Exception;
+
+ @GetMapping("/task/{id}/stop")
+ void stopTask(@PathVariable Long id) throws Exception;
+
+ @GetMapping("/task/{id}/start")
+ void startTask(@PathVariable Long id) throws Exception;
+
+ @PostMapping("/sub-task/delete")
+ void batchDeleteSubTask(@RequestBody List ids) throws Exception;
+
+ @GetMapping("/sub-task/{id}/users/list/{type}")
+ List