Compare commits

...

37 Commits

Author SHA1 Message Date
jianneng-fit2cloud
23d9b5f879
Merge pull request #13659 from dataease/pr@dev-v2@chart-map-color-style
fix(图表): 修复地图配色方案样式出现两个滚动条的问题
2024-11-28 17:16:29 +08:00
jianneng-fit2cloud
0feea4cc2c fix(图表): 修复地图配色方案样式出现两个滚动条的问题 2024-11-28 17:15:14 +08:00
王嘉豪
05827ca172
Merge pull request #13658 from dataease/pr@dev-v2@refactor_rich-text
refactor: 优化富文本查询速度
2024-11-28 17:03:42 +08:00
wangjiahao
5184f8b5e0 refactor: 优化富文本查询速度 2024-11-28 17:01:34 +08:00
jianneng-fit2cloud
f7f0b1bdad
Merge pull request #13655 from dataease/prdev-v2@chart-table-empty-style
feat(图表): 明细表、汇总表无数据时,显示暂无数据 #13037
2024-11-28 16:35:24 +08:00
jianneng-fit2cloud
1687b52419 feat(图表): 明细表、汇总表无数据时,显示暂无数据 #13037 2024-11-28 16:33:07 +08:00
王嘉豪
5f9f520236
Merge pull request #13652 from dataease/pr@dev-v2@refactor_new-tab-preview
refactor: 新页面预览不跟随系统设置
2024-11-28 16:07:54 +08:00
wangjiahao
e1c9fb5b55 refactor: 新页面预览不跟随系统设置 2024-11-28 16:06:49 +08:00
王嘉豪
506f2ca552
Merge pull request #13651 from dataease/pr@dev-v2@fix_group-move
fix: 修复多选组件无法使用快捷键移动问题
2024-11-28 16:00:11 +08:00
fit2cloudrd
e439bcafcb
Merge pull request #13650 from dataease/pr@dev-v2_fix_restore_script
fix: 修复 dectl 脚本在回复 MySQL 时可能会出现的无法启动问题
2024-11-28 15:59:53 +08:00
xuwei-fit2cloud
8438ba0aba fix: 修复 dectl 脚本在回复 MySQL 时可能会出现的无法启动问题 2024-11-28 15:58:04 +08:00
wangjiahao
e7993d7a0a fix: 修复多选组件无法使用快捷键移动问题 2024-11-28 15:57:23 +08:00
dataeaseShu
d80221033c
Merge pull request #13648 from dataease/pr@dev-v2@fixds
fix: 【数据导出】-导出仪表板图表数据,报用户权限已更改,导出未执行
2024-11-28 15:43:34 +08:00
dataeaseShu
98f737e781
Merge pull request #13649 from dataease/pr@dev-v2@style_adaptor
style: 样式调整
2024-11-28 15:43:16 +08:00
taojinlong
bcb9c9b263
Merge pull request #13647 from dataease/pr@dev-v2_st
fix(系统设置): 系统参数-基础设置-编辑状态缺失提示说明 icon,前后保持一致
2024-11-28 15:34:01 +08:00
taojinlong
204797ce24 fix: 【数据导出】-导出仪表板图表数据,报用户权限已更改,导出未执行 2024-11-28 15:32:58 +08:00
dataeaseShu
f04aed6745 fix(系统设置): 系统参数-基础设置-编辑状态缺失提示说明 icon,前后保持一致 2024-11-28 15:32:19 +08:00
王嘉豪
306c6711f7
Merge pull request #13646 from dataease/pr@dev-v2@refactor_tab-sort
refactor: tab支持图层侧拖拽排序
2024-11-28 15:31:29 +08:00
fit2cloud-chenyw
a8738bca9b
Merge pull request #13645 from dataease/pr@dev-v2@fix_user_batch_import
fix(系统设置): 用户批量导入邮箱无法包含下划线 #13176
2024-11-28 15:25:17 +08:00
fit2cloud-chenyw
1e771beeb5 fix(系统设置): 用户批量导入邮箱无法包含下划线 #13176 2024-11-28 15:23:09 +08:00
taojinlong
9104e3789c
Merge pull request #13644 from dataease/pr@dev-v2@fixds
fix: 【数据源】Excel数据源在替换数据时显示不正确
2024-11-28 15:22:53 +08:00
taojinlong
8bf0778b3e fix: 【数据源】Excel数据源在替换数据时显示不正确 2024-11-28 15:22:00 +08:00
dataeaseShu
b3ae2c9d8d
Merge pull request #13643 from dataease/pr@dev-v2@style_basic_setting
style(系统设置): 系统参数-基础设置-表单文案提示
2024-11-28 15:10:15 +08:00
fit2cloud-chenyw
d99bb92e1a style(系统设置): 系统参数-基础设置-表单文案提示 2024-11-28 15:06:55 +08:00
xuwei-fit2cloud
8918516508
Merge pull request #13639 from dataease/pr@dev-v2_dzz
fix(xpack): xpack UI库国际化不生效
2024-11-28 14:46:59 +08:00
王嘉豪
e16b4b7234
Merge pull request #13641 from dataease/pr@dev-v2@fix_add-event
fix: 修复单页面打开模式组件添加事件退出未销毁问题
2024-11-28 14:15:32 +08:00
dataeaseShu
c429eccd71 fix(xpack): xpack UI库国际化不生效 2024-11-28 13:46:45 +08:00
xuwei-fit2cloud
2e6a9e4f9c
Merge pull request #13635 from dataease/pr@dev-v2@fixds
Pr@dev v2@fixds
2024-11-28 13:43:06 +08:00
王嘉豪
693061d2ec
Merge pull request #13637 from dataease/pr@dev-v2@refactor_button
refactor: 修改悬浮按钮提示
2024-11-28 13:34:26 +08:00
王嘉豪
b5dda58118
Merge pull request #13636 from dataease/pr@dev-v2@refactor_style-set
fix: 修复样式设置时可能出现的样式影响其他组件问题 #13299
2024-11-28 13:21:20 +08:00
taojinlong
9b9cb8ff92
Merge pull request #13634 from dataease/pr@dev-v2@perf_setting_sort
perf: 系统参数-基础设置-设置项排序
2024-11-28 12:22:58 +08:00
taojinlong
5205972eb7 fix: 【数据源】数据源连接信息AES加密存储 2024-11-28 12:19:33 +08:00
fit2cloud-chenyw
2c724a9624 perf: 系统参数-基础设置-设置项排序 2024-11-28 11:55:32 +08:00
taojinlong
87d31f6907 fix: excel数据源创建页面,去掉 刷新数据 按钮 2024-11-28 11:48:53 +08:00
taojinlong
d655a77ece fix: 【数据源】替换 excel 失败 2024-11-28 11:47:57 +08:00
xuwei-fit2cloud
46c66b2395
Merge pull request #13633 from dataease/pr@dev-v2@perf_del_auto_sync
perf: 删除自动同步游离资源逻辑
2024-11-28 11:12:38 +08:00
fit2cloud-chenyw
3b95c6a945 perf: 删除自动同步游离资源逻辑 2024-11-28 11:08:58 +08:00
35 changed files with 201 additions and 445 deletions

View File

@ -97,7 +97,6 @@ public class TableInfoHandler extends DefaultChartHandler {
fieldDTO = allField;
}
}
assert fieldDTO != null;
if (fieldDTO != null && fieldDTO.isAgg()) {
sqlMeta.getXFields().get(i).setFieldName("'-'");
}

View File

@ -2,6 +2,7 @@ package io.dataease.config;
import io.dataease.commons.utils.MybatisInterceptorConfig;
import io.dataease.datasource.dao.auto.entity.CoreDatasource;
import io.dataease.datasource.dao.auto.entity.CoreDeEngine;
import io.dataease.interceptor.MybatisInterceptor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@ -22,6 +23,7 @@ public class MybatisConfig {
MybatisInterceptor interceptor = new MybatisInterceptor();
List<MybatisInterceptorConfig> configList = new ArrayList<>();
configList.add(new MybatisInterceptorConfig(CoreDeEngine.class, "configuration"));
configList.add(new MybatisInterceptorConfig(CoreDatasource.class, "configuration"));
interceptor.setInterceptorConfigList(configList);
return interceptor;
}

View File

@ -156,6 +156,13 @@ public class DataSourceManage {
coreOptRecentManage.saveOpt(sourceData.getId(), OptConstants.OPT_RESOURCE_TYPE.DATASOURCE, OptConstants.OPT_TYPE.UPDATE);
}
public void encryptDsConfig(){
coreDatasourceMapper.selectList(null).forEach(dataSource -> {
coreDatasourceMapper.updateById(dataSource);
});
}
public DatasourceDTO getDs(Long id) {
CoreDatasource coreDatasource = coreDatasourceMapper.selectById(id);
DatasourceDTO dto = new DatasourceDTO();

View File

@ -703,7 +703,7 @@ public class DatasourceServer implements DatasourceApi {
String datasourceId = req.get("datasourceId");
DatasetTableDTO datasetTableDTO = new DatasetTableDTO();
datasetTableDTO.setDatasourceId(Long.valueOf(datasourceId));
if (!getTables(datasetTableDTO).stream().map(DatasetTableDTO::getTableName).collect(Collectors.toList()).contains("tableName")) {
if (!getTables(datasetTableDTO).stream().map(DatasetTableDTO::getTableName).collect(Collectors.toList()).contains(tableName)) {
DEException.throwException("无效的表名!");
}
CoreDatasource coreDatasource = datasourceMapper.selectById(datasourceId);
@ -817,7 +817,7 @@ public class DatasourceServer implements DatasourceApi {
sheet.setDeTableName(datasetTableDTO.getTableName());
datasourceRequest.setTable(datasetTableDTO.getTableName());
List<TableField> oldTableFields = ExcelUtils.getTableFields(datasourceRequest);
mergeFields(sheet.getFields(), oldTableFields);
mergeFields(oldTableFields, sheet.getFields());
}
}
if (!find) {
@ -843,6 +843,10 @@ public class DatasourceServer implements DatasourceApi {
if (CollectionUtils.isEmpty(newTableFields) || CollectionUtils.isEmpty(oldTableFields)) {
return false;
}
boolean isHistory = oldTableFields.stream().filter(tableField -> !tableField.isChecked()).collect(Collectors.toList()).size() == oldTableFields.size();
if (isHistory) {
oldTableFields.forEach(tableField -> tableField.setChecked(true));
}
newTableFields.forEach(tableField -> tableField.setChecked(false));
for (TableField oldField : oldTableFields) {
if (!oldField.isChecked()) {
@ -866,7 +870,11 @@ public class DatasourceServer implements DatasourceApi {
}
private void mergeFields(List<TableField> oldFields, List<TableField> newFields) {
oldFields.forEach(tableField -> tableField.setChecked(false));
newFields.forEach(tableField -> tableField.setChecked(false));
boolean isHistory = oldFields.stream().filter(tableField -> !tableField.isChecked()).collect(Collectors.toList()).size() == oldFields.size();
if (isHistory) {
oldFields.forEach(tableField -> tableField.setChecked(true));
}
for (TableField newField : newFields) {
for (TableField oldField : oldFields) {
if (oldField.getName().equals(newField.getName())) {

View File

@ -1,6 +1,7 @@
package io.dataease.listener;
import io.dataease.datasource.dao.auto.entity.CoreDatasourceTask;
import io.dataease.datasource.manage.DataSourceManage;
import io.dataease.datasource.manage.DatasourceSyncManage;
import io.dataease.datasource.manage.EngineManage;
import io.dataease.datasource.provider.CalciteProvider;
@ -26,6 +27,8 @@ public class DataSourceInitStartListener implements ApplicationListener<Applicat
@Resource
private DatasourceServer datasourceServer;
@Resource
private DataSourceManage dataSourceManage;
@Resource
private DatasourceTaskServer datasourceTaskServer;
@Resource
private CalciteProvider calciteProvider;
@ -71,7 +74,7 @@ public class DataSourceInitStartListener implements ApplicationListener<Applicat
} catch (Exception e) {
e.printStackTrace();
}
dataSourceManage.encryptDsConfig();
}

View File

@ -1,12 +0,0 @@
package io.dataease.rmonitor.bo;
import lombok.Data;
import java.io.Serializable;
@Data
public class PerMonitorCheckBO implements Serializable {
private boolean valid;
private boolean emptyPermission;
}

View File

@ -1,24 +0,0 @@
package io.dataease.rmonitor.bo;
import io.dataease.model.TreeBaseModel;
import io.dataease.model.TreeResultModel;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
@Data
public class PerMonitorNodeBO implements TreeBaseModel<PerMonitorNodeBO>, TreeResultModel<PerMonitorNodeBO>, Serializable {
private Long id;
private String name;
private Long pid;
private boolean leaf;
private int extraFlag;
private List<PerMonitorNodeBO> children;
}

View File

@ -1,120 +0,0 @@
package io.dataease.rmonitor.manage;
import io.dataease.constant.DataSourceType;
import io.dataease.exception.DEException;
import io.dataease.rmonitor.bo.PerMonitorCheckBO;
import io.dataease.rmonitor.bo.PerMonitorNodeBO;
import io.dataease.rmonitor.mapper.ResourceMonitorMapper;
import io.dataease.rmonitor.mapper.entity.DatasetFreeResource;
import io.dataease.rmonitor.mapper.entity.DsFreeResource;
import io.dataease.rmonitor.mapper.entity.VisualFreeResource;
import io.dataease.utils.BeanUtils;
import io.dataease.utils.TreeUtils;
import jakarta.annotation.Resource;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Component("resourceMonitorManage")
public class ResourceMonitorManage {
@Resource(name = "resourceMonitorSyncManage")
private ResourceMonitorSyncManage resourceMonitorSyncManage;
@Resource
private ResourceMonitorMapper resourceMonitorMapper;
private boolean existFreeResource() {
int rCount = resourceMonitorMapper.dsCount() + resourceMonitorMapper.datasetCount() + resourceMonitorMapper.vCount();
return rCount > 0;
}
private Map<String, List<PerMonitorNodeBO>> freeResource() {
Map<String, List<PerMonitorNodeBO>> result = new HashMap<>();
List<DsFreeResource> dsFreeResources = resourceMonitorMapper.queryFreeDs();
if (CollectionUtils.isNotEmpty(dsFreeResources)) {
List<PerMonitorNodeBO> dsBos = dsFreeResources.stream().map(node -> {
PerMonitorNodeBO bo = BeanUtils.copyBean(new PerMonitorNodeBO(), node);
bo.setLeaf(!StringUtils.equals("folder", node.getType()));
bo.setExtraFlag(DataSourceType.valueOf(node.getType()).getFlag());
return bo;
}).collect(Collectors.toList());
List<PerMonitorNodeBO> dsTree = TreeUtils.mergeTree(dsBos, PerMonitorNodeBO.class, false);
result.put("datasource", dsTree);
}
List<DatasetFreeResource> datasetFreeResources = resourceMonitorMapper.queryFreeDataset();
if (CollectionUtils.isNotEmpty(datasetFreeResources)) {
List<PerMonitorNodeBO> datasetBos = datasetFreeResources.stream().map(node -> {
PerMonitorNodeBO bo = BeanUtils.copyBean(new PerMonitorNodeBO(), node);
bo.setLeaf(!StringUtils.equals("folder", node.getNodeType()));
return bo;
}).collect(Collectors.toList());
List<PerMonitorNodeBO> datasetTree = TreeUtils.mergeTree(datasetBos, PerMonitorNodeBO.class, false);
result.put("dataset", datasetTree);
}
List<VisualFreeResource> visualFreeResources = resourceMonitorMapper.queryFreeVusial();
if (CollectionUtils.isNotEmpty(visualFreeResources)) {
Map<String, List<VisualFreeResource>> baseMap = visualFreeResources.stream().collect(Collectors.groupingBy(VisualFreeResource::getType));
for (Map.Entry<String, List<VisualFreeResource>> entry : baseMap.entrySet()) {
List<VisualFreeResource> freeResource = entry.getValue();
List<PerMonitorNodeBO> visualBos = freeResource.stream().map(node -> {
PerMonitorNodeBO bo = BeanUtils.copyBean(new PerMonitorNodeBO(), node);
bo.setLeaf(!StringUtils.equals("folder", node.getNodeType()));
return bo;
}).collect(Collectors.toList());
result.put(convertBusiFlag(entry.getKey()), TreeUtils.mergeTree(visualBos, PerMonitorNodeBO.class, false));
}
}
return result;
}
private String convertBusiFlag(String key) {
if (StringUtils.equals("dashboard", key)) {
return "panel";
} else if (StringUtils.equals("dataV", key)) {
return "screen";
} else return key;
}
public boolean check() {
PerMonitorCheckBO checkBO = resourceMonitorSyncManage.checkXpackResource();
return checkBO.isValid() && checkBO.isEmptyPermission() && existFreeResource();
}
@Transactional
public void delete() {
boolean existFree = existFreeResource();
if (!existFree) DEException.throwException("无未同步资源!");
resourceMonitorMapper.delFreeDs();
resourceMonitorMapper.delFreeDataset();
resourceMonitorMapper.delFreeVisual();
}
public void sync() {
//1从xpack获取资源 如果xpack不存在 或者资源不为空 则直接返回 并且抛出异常仅支持首次导入lic同步
//2从core获取资源
//3根据类型分组 并组织成树形结构
//4分别遍历每一棵树 从上到下 同步到权限体系 给默认组织
PerMonitorCheckBO checkBO = resourceMonitorSyncManage.checkXpackResource();
if (!checkBO.isValid()) DEException.throwException("缺少许可证");
if (!checkBO.isEmptyPermission()) DEException.throwException("仅支持license首次导入同步");
Map<String, List<PerMonitorNodeBO>> freeResourceMap = freeResource();
if (MapUtils.isEmpty(freeResourceMap)) DEException.throwException("无未同步资源!");
for (Map.Entry<String, List<PerMonitorNodeBO>> entry : freeResourceMap.entrySet()) {
resourceMonitorSyncManage.sync(entry.getKey(), entry.getValue());
}
}
}

View File

@ -1,28 +0,0 @@
package io.dataease.rmonitor.manage;
import io.dataease.exception.DEException;
import io.dataease.license.config.XpackInteract;
import io.dataease.rmonitor.bo.PerMonitorCheckBO;
import io.dataease.rmonitor.bo.PerMonitorNodeBO;
import io.dataease.rmonitor.mapper.ResourceMonitorMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Component;
import java.util.List;
@Component("resourceMonitorSyncManage")
public class ResourceMonitorSyncManage {
@Resource(name = "resourceMonitorMapper")
private ResourceMonitorMapper resourceMonitorMapper;
@XpackInteract(value = "resourceMonitorSyncManage", replace = true)
public void sync(String flag, List<PerMonitorNodeBO> treeNodes) {
DEException.throwException("缺失许可证");
}
@XpackInteract(value = "resourceMonitorSyncManage", replace = true)
public PerMonitorCheckBO checkXpackResource() {
return new PerMonitorCheckBO();
}
}

View File

@ -1,41 +0,0 @@
package io.dataease.rmonitor.mapper;
import io.dataease.rmonitor.mapper.entity.DatasetFreeResource;
import io.dataease.rmonitor.mapper.entity.DsFreeResource;
import io.dataease.rmonitor.mapper.entity.VisualFreeResource;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface ResourceMonitorMapper {
@Select("select count(id) from core_datasource")
int dsCount();
@Select("select count(id) from core_dataset_group")
int datasetCount();
@Select("select count(id) from data_visualization_info where delete_flag = 0 and pid != -1")
int vCount();
@Select("select id, name, pid, type, status from core_datasource")
List<DsFreeResource> queryFreeDs();
@Select("select id, name, pid, node_type from core_dataset_group")
List<DatasetFreeResource> queryFreeDataset();
@Select("select id, name, pid, node_type, type from data_visualization_info where delete_flag = 0 and pid != -1")
List<VisualFreeResource> queryFreeVusial();
@Delete("delete from core_datasource")
void delFreeDs();
@Delete("delete from core_dataset_group")
void delFreeDataset();
@Delete("delete from data_visualization_info")
void delFreeVisual();
}

View File

@ -1,15 +0,0 @@
package io.dataease.rmonitor.mapper.entity;
import lombok.Data;
import java.io.Serializable;
@Data
public class BaseFreeResource implements Serializable {
private Long id;
private String name;
private Long pid;
}

View File

@ -1,13 +0,0 @@
package io.dataease.rmonitor.mapper.entity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
@EqualsAndHashCode(callSuper = true)
@Data
public class DatasetFreeResource extends BaseFreeResource implements Serializable {
private String nodeType;
}

View File

@ -1,16 +0,0 @@
package io.dataease.rmonitor.mapper.entity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
@EqualsAndHashCode(callSuper = true)
@Data
public class DsFreeResource extends BaseFreeResource implements Serializable {
private String type;
private String status;
}

View File

@ -1,15 +0,0 @@
package io.dataease.rmonitor.mapper.entity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
@EqualsAndHashCode(callSuper = true)
@Data
public class VisualFreeResource extends BaseFreeResource implements Serializable {
private String nodeType;
private String type;
}

View File

@ -1,30 +0,0 @@
package io.dataease.rmonitor.server;
import io.dataease.api.rmonitor.ResourceMonitorApi;
import io.dataease.rmonitor.manage.ResourceMonitorManage;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/rmonitor")
public class ResourceMonitorServer implements ResourceMonitorApi {
@Resource(name = "resourceMonitorManage")
private ResourceMonitorManage resourceMonitorManage;
@Override
public boolean existFree() {
return resourceMonitorManage.check();
}
@Override
public void delete() {
resourceMonitorManage.delete();
}
@Override
public void sync() {
resourceMonitorManage.sync();
}
}

View File

@ -52,3 +52,6 @@ create table core_custom_geo_sub_area
)
comment '自定义地理区域分区详情';
UPDATE `core_sys_setting` SET `sort` = 11 WHERE `pkey` = 'basic.dsIntervalTime';

View File

@ -3,7 +3,3 @@ import request from '@/config/axios'
export const validateApi = data => request.post({ url: '/license/validate', data })
export const buildVersionApi = () => request.get({ url: '/license/version' })
export const updateInfoApi = data => request.post({ url: '/license/update', data })
export const checkFreeApi = () => request.get({ url: '/rmonitor/existFree' })
export const syncFreeApi = () => request.post({ url: '/rmonitor/sync' })
export const delFreeApi = () => request.post({ url: '/rmonitor/delete' })

View File

@ -139,8 +139,7 @@ const previewOuter = () => {
}
canvasSave(() => {
const url = '#/preview?dvId=' + dvInfo.value.id + '&ignoreParams=true'
const openType = wsCache.get('open-backend') === '1' ? '_self' : '_blank'
const newWindow = window.open(url, openType)
const newWindow = window.open(url, '_blank')
initOpenHandler(newWindow)
})
}

View File

@ -3,6 +3,7 @@ import noLic from './nolic.vue'
import { ref, useAttrs, onMounted } from 'vue'
import { execute, randomKey, formatArray } from './convert'
import { load, loadDistributed, xpackModelApi } from '@/api/plugin'
import configGlobal from '@/components/config-global/src/ConfigGlobal.vue'
import { useCache } from '@/hooks/web/useCache'
import { i18n } from '@/plugins/vue-i18n'
import * as Vue from 'vue'
@ -152,13 +153,13 @@ onMounted(async () => {
</script>
<template>
<component
:key="attrs.jsname"
ref="pluginProxy"
:is="plugin"
v-loading="loading"
v-bind="attrs"
></component>
<configGlobal>
<component
:key="attrs.jsname"
ref="pluginProxy"
:is="plugin"
v-loading="loading"
v-bind="attrs"
></component>
</configGlobal>
</template>
<style lang="less" scoped></style>

View File

@ -453,7 +453,7 @@ const exportAsExcel = () => {
const viewDataInfo = dvMainStore.getViewDataDetails(element.value.id)
const chartExtRequest = dvMainStore.getLastViewRequestInfo(element.value.id)
const viewInfo = dvMainStore.getViewDetails(element.value.id)
const chart = { ...viewInfo, chartExtRequest, data: viewDataInfo }
const chart = { ...viewInfo, chartExtRequest, data: viewDataInfo, busiFlag: dvInfo.value.type }
exportExcelDownload(chart, () => {
openMessageLoading(callbackExport)
})

View File

@ -14,7 +14,12 @@ const { curComponent } = storeToRefs(dvMainStore)
</script>
<template>
<el-collapse-item :effect="themes" :title="t('visualization.position')" name="position" v-if="!dashboardActive">
<el-collapse-item
:effect="themes"
:title="t('visualization.position')"
name="position"
v-if="!dashboardActive"
>
<component-position :themes="themes" />
</el-collapse-item>
</template>

View File

@ -225,7 +225,7 @@ const init = ref({
//
originalHandle.style.display = ''
if (cloneHandle) {
cloneHandle.parentNode.removeChild(cloneHandle) //
cloneHandle.parentNode?.removeChild(cloneHandle) //
}
cloneHandle = null
originalHandle = null
@ -539,6 +539,9 @@ const calcData = (view: Chart, callback) => {
updateEmptyValue(view)
if (view.tableId || view['dataFrom'] === 'template') {
const v = JSON.parse(JSON.stringify(view))
v.type = 'table-info'
v.render = 'antv'
v.resultCount = 1
getData(v)
.then(res => {
if (res.code && res.code !== 0) {
@ -546,6 +549,8 @@ const calcData = (view: Chart, callback) => {
errMsg.value = res.msg
} else {
state.data = res?.data
res.type = 'rich-text'
res.render = 'custom'
state.viewDataInfo = res
state.totalItems = res?.totalItems
const curViewInfo = canvasViewInfo.value[element.value.id]

View File

@ -187,16 +187,16 @@ function move(keyCode) {
const scale = dvMainStore.canvasStyleData.scale / 100
if (keyCode === leftKey) {
curComponent.value.style.left = curComponent.value.style.left - scale
groupAreaAdaptor(-1, 0)
groupAreaAdaptor(-scale, 0)
} else if (keyCode === rightKey) {
curComponent.value.style.left = curComponent.value.style.left + scale
groupAreaAdaptor(1, 0)
groupAreaAdaptor(scale, 0)
} else if (keyCode === upKey) {
curComponent.value.style.top = curComponent.value.style.top - scale
groupAreaAdaptor(0, -1)
groupAreaAdaptor(0, -scale)
} else if (keyCode === downKey) {
curComponent.value.style.top = curComponent.value.style.top + scale
groupAreaAdaptor(0, 1)
groupAreaAdaptor(0, scale)
}
snapshotStore.recordSnapshotCache('key-move')
}
@ -212,6 +212,11 @@ function groupAreaAdaptor(leftOffset = 0, topOffset = 0) {
width: parentNode.offsetWidth,
height: parentNode.offsetHeight
})
} else if (curComponent.value.component === 'GroupArea' && areaData.value.components.length > 0) {
areaData.value.components.forEach(component => {
component.style.top = component.style.top + topOffset
component.style.left = component.style.left + leftOffset
})
}
}

View File

@ -1,18 +1,11 @@
<script lang="ts" setup>
import logo from '@/assets/svg/logo.svg'
import aboutBg from '@/assets/img/about-bg.png'
import { ref, reactive, onMounted, h } from 'vue'
import { ref, reactive, onMounted } from 'vue'
import { useUserStoreWithOut } from '@/store/modules/user'
import { F2CLicense } from './index'
import {
validateApi,
buildVersionApi,
updateInfoApi,
checkFreeApi,
syncFreeApi,
delFreeApi
} from '@/api/about'
import { ElMessage, ElMessageBox, Action } from 'element-plus-secondary'
import { validateApi, buildVersionApi, updateInfoApi } from '@/api/about'
import { ElMessage } from 'element-plus-secondary'
import { useI18n } from '@/hooks/web/useI18n'
import { useEmitt } from '@/hooks/web/useEmitt'
import { useCache } from '@/hooks/web/useCache'
@ -129,65 +122,11 @@ const update = (licKey: string) => {
ElMessage.success(t('about.update_success'))
const info = getLicense(response.data)
setLicense(info)
checkFree()
} else {
ElMessage.warning(response.data.message)
}
})
}
const autoSync = ref(true)
const checkFree = () => {
checkFreeApi().then(res => {
if (res.data) {
if (autoSync.value) {
syncFree()
return
}
// do something
const title = '存在未同步的资源数据,请谨慎操作!'
const childrenDomList = [h('strong', null, title)]
ElMessageBox.confirm('', {
confirmButtonType: 'primary',
type: 'warning',
autofocus: false,
dangerouslyUseHTMLString: true,
message: h('div', { class: 'free-sync-tip-box' }, childrenDomList),
showClose: false,
cancelButtonText: '删除',
cancelButtonClass: 'free-cancel-bt',
showCancelButton: false,
preButtonType: 'danger',
preButtonText: '删除',
showPreButton: true,
confirmButtonText: '同步',
callback: (action: Action) => {
if (action === 'confirm') {
syncFree()
} else {
delFree
}
}
})
}
})
}
const delFree = () => {
delFreeApi().then(res => {
if (!res.code && !res.msg) {
ElMessage.success(t('common.delete_success'))
}
})
}
const syncFree = () => {
syncFreeApi().then(res => {
if (!res.code && !res.msg) {
ElMessage.success('同步成功')
}
})
}
</script>
<template>

View File

@ -282,8 +282,7 @@ onMounted(() => {
:deep(.ed-tabs__content) {
height: calc(100% - 35px);
overflow-y: auto;
overflow-x: hidden;
overflow: hidden;
}
}
.padding-tab {

View File

@ -4,6 +4,7 @@ import {
S2Event,
S2Options,
S2Theme,
ScrollbarPositionType,
TableColCell,
TableSheet,
ViewMeta
@ -22,7 +23,8 @@ import {
calculateHeaderHeight,
SortTooltip,
configSummaryRow,
summaryRowStyle
summaryRowStyle,
configEmptyDataStyle
} from '@/views/chart/components/js/panel/common/common_table'
const { t } = useI18n()
@ -167,7 +169,10 @@ export class TableInfo extends S2ChartView<TableSheet> {
renderTooltip: sheet => new SortTooltip(sheet)
},
interaction: {
hoverHighlight: !(basicStyle.showHoverStyle === false)
hoverHighlight: !(basicStyle.showHoverStyle === false),
scrollbarPosition: newData.length
? ScrollbarPositionType.CONTENT
: ScrollbarPositionType.CANVAS
}
}
s2Options.style = this.configStyle(chart, s2DataConfig)
@ -335,6 +340,8 @@ export class TableInfo extends S2ChartView<TableSheet> {
ev.colsHierarchy.width = containerWidth
})
}
// 空数据时表格样式
configEmptyDataStyle(newChart, basicStyle, newData, container)
// click
newChart.on(S2Event.DATA_CELL_CLICK, ev => {
const cell = newChart.getCell(ev.target)

View File

@ -1,6 +1,7 @@
import { useI18n } from '@/hooks/web/useI18n'
import { formatterItem, valueFormatter } from '@/views/chart/components/js/formatter'
import {
configEmptyDataStyle,
configSummaryRow,
copyContent,
SortTooltip,
@ -13,6 +14,7 @@ import {
S2DataConfig,
S2Event,
S2Options,
ScrollbarPositionType,
TableColCell,
TableSheet,
ViewMeta
@ -145,7 +147,10 @@ export class TableNormal extends S2ChartView<TableSheet> {
renderTooltip: sheet => new SortTooltip(sheet)
},
interaction: {
hoverHighlight: !(basicStyle.showHoverStyle === false)
hoverHighlight: !(basicStyle.showHoverStyle === false),
scrollbarPosition: newData.length
? ScrollbarPositionType.CONTENT
: ScrollbarPositionType.CANVAS
}
}
// 列宽设置
@ -242,6 +247,7 @@ export class TableNormal extends S2ChartView<TableSheet> {
ev.colsHierarchy.width = containerWidth
})
}
configEmptyDataStyle(newChart, basicStyle, newData, container)
// click
newChart.on(S2Event.DATA_CELL_CLICK, ev => {
const cell = newChart.getCell(ev.target)

View File

@ -1671,6 +1671,7 @@ const drawTextShape = (cell, isHeader) => {
* @param layoutResult
*/
export const calculateHeaderHeight = (info, newChart, tableHeader, basicStyle, layoutResult) => {
if (tableHeader.showTableHeader === false ) return
const ev = layoutResult || newChart.facet.layoutResult
const maxLines = basicStyle.maxLines ?? 1
const textStyle = { ...newChart.theme.cornerCell.text }
@ -1763,7 +1764,7 @@ const getWrapTextHeight = (wrapText, textStyle, spreadsheet, maxLines) => {
* @param showSummary
*/
export const configSummaryRow = (chart, s2Options, newData, tableHeader, basicStyle, showSummary) =>{
if (!showSummary) return
if (!showSummary || !newData.length) return
// 设置汇总行高度和表头一致
const heightByField = {}
heightByField[newData.length] = tableHeader.tableTitleHeight
@ -1821,10 +1822,13 @@ export const configSummaryRow = (chart, s2Options, newData, tableHeader, basicSt
* @param showSummary
*/
export const summaryRowStyle = (newChart, newData, tableCell, tableHeader, showSummary) => {
if (!showSummary) return
if (!showSummary || !newData.length) return
newChart.on(S2Event.LAYOUT_BEFORE_RENDER, () => {
const showHeader = tableHeader.showTableHeader === true
// 不显示表头时减少一个表头的高度
const headerAndSummaryHeight = showHeader ? 2 : 1
const totalHeight =
tableHeader.tableTitleHeight * 2 + tableCell.tableItemHeight * (newData.length - 1)
tableHeader.tableTitleHeight * headerAndSummaryHeight + tableCell.tableItemHeight * (newData.length - 1)
if (totalHeight < newChart.options.height) {
// 6 是阴影高度
newChart.options.height =
@ -1844,3 +1848,41 @@ export class SummaryCell extends CustomDataCell {
return { backgroundColor, backgroundColorOpacity }
}
}
/**
* 配置空数据样式
* @param newChart
* @param basicStyle
* @param newData
* @param container
*/
export const configEmptyDataStyle = (newChart, basicStyle, newData, container) => {
/**
* 辅助函数移除空数据dom
*/
const removeEmptyDom = () => {
const emptyElement = document.getElementById(container + '_empty')
if (emptyElement) {
emptyElement.parentElement.removeChild(emptyElement)
}
}
removeEmptyDom()
if (newData.length) return
newChart.on(S2Event.LAYOUT_AFTER_HEADER_LAYOUT, (ev) => {
removeEmptyDom()
if (!newData.length) {
const emptyDom = document.createElement('div')
const left = Math.min(newChart.options.width, ev.colsHierarchy.width) / 2 - 32
emptyDom.id = container + '_empty'
emptyDom.textContent = t('data_set.no_data')
emptyDom.setAttribute(
'style',
`position: absolute;
left: ${left}px;
top: 50%;`
)
const parent = document.getElementById(container)
parent.insertBefore(emptyDom, parent.firstChild)
}
})
}

View File

@ -34,7 +34,7 @@ const favorited = ref(false)
const preview = () => {
const baseUrl = isDataEaseBi.value ? embeddedStore.baseUrl : ''
const url = baseUrl + '#/preview?dvId=' + dvInfo.value.id + '&ignoreParams=true'
const newWindow = window.open(url, openType)
const newWindow = window.open(url, '_blank')
initOpenHandler(newWindow)
}
const isDataEaseBi = computed(() => appStore.getIsDataEaseBi)

View File

@ -1,10 +1,19 @@
<script lang="ts" setup>
import { ref, reactive } from 'vue'
import { ref, reactive, PropType } from 'vue'
import { ElMessage, ElLoading } from 'element-plus-secondary'
import { useI18n } from '@/hooks/web/useI18n'
import type { FormInstance, FormRules } from 'element-plus-secondary'
import request from '@/config/axios'
import dvInfo from '@/assets/svg/dv-info.svg'
const { t } = useI18n()
const props = defineProps({
labelTooltips: {
type: Array as PropType<any[]>,
default: () => []
}
})
const dialogVisible = ref(false)
const loadingInstance = ref(null)
const basicForm = ref<FormInstance>()
@ -48,6 +57,14 @@ const state = reactive({
]
})
const tooltipItem = ref({})
const formatLabel = () => {
props.labelTooltips?.length &&
props.labelTooltips.forEach(tooltip => {
tooltipItem.value[tooltip.key] = tooltip.val
})
}
const rule = reactive<FormRules>({
dsIntervalTime: [
{
@ -229,6 +246,7 @@ const oidChange = () => {
state.form['platformRid'] = []
loadRoleOptions()
}
formatLabel()
defineExpose({
edit
})
@ -255,8 +273,22 @@ defineExpose({
:key="item.pkey"
:prop="item.pkey"
:class="{ 'setting-hidden-item': item.pkey === 'dsExecuteTime' }"
:label="t(item.label)"
>
<template v-slot:label>
<div class="basic-form-info-tips">
<span class="custom-form-item__label">{{ t(item.label) }}</span>
<el-tooltip
v-if="tooltipItem[`setting_basic.${item.pkey}`]"
effect="dark"
:content="tooltipItem[`setting_basic.${item.pkey}`]"
placement="top"
>
<el-icon
><Icon name="dv-info"><dvInfo class="svg-icon" /></Icon
></el-icon>
</el-tooltip>
</div>
</template>
<el-switch
class="de-basic-switch"
v-if="
@ -419,6 +451,32 @@ defineExpose({
.ed-form-item__label {
line-height: 22px !important;
height: 22px !important;
.basic-form-info-tips {
width: fit-content;
display: inline-flex;
align-items: center;
column-gap: 4px;
}
}
.ed-form-item {
&.is-required.asterisk-right {
.ed-form-item__label:after {
display: none;
}
.basic-form-info-tips {
.custom-form-item__label:after {
content: '*';
color: var(--ed-color-danger);
margin-left: 2px;
font-family: var(--de-custom_font, 'PingFang');
font-size: 14px;
font-style: normal;
font-weight: 400;
}
}
}
}
.ed-radio__label {
font-weight: 400;

View File

@ -13,6 +13,7 @@
"
/>
<InfoTemplate
v-if="loginInoSettings?.length"
ref="loginTemplate"
class="login-setting-template"
:label-tooltips="tooltips"
@ -28,6 +29,7 @@
/>
<InfoTemplate
v-if="thirdInfoSettings?.length"
ref="thirdTemplate"
class="login-setting-template"
:label-tooltips="tooltips"
@ -41,7 +43,7 @@
)
"
/>
<basic-edit ref="editor" @saved="refresh" />
<basic-edit ref="editor" :label-tooltips="tooltips" @saved="refresh" />
</template>
<script lang="ts" setup>
@ -249,9 +251,9 @@ const search = cb => {
const refresh = () => {
search(() => {
nextTick(() => {
infoTemplate?.value.init()
loginTemplate?.value.init()
thirdTemplate?.value.init()
infoTemplate?.value?.init()
loginTemplate?.value?.init()
thirdTemplate?.value?.init()
})
})
}

View File

@ -215,6 +215,13 @@ const saveExcelDs = (params, successCb, finallyCb) => {
if (selectNode[i].changeFiled) {
changeFiled = true
}
if (selectNode[i].fields.filter(field => field.checked).length == 0) {
ElMessage({
message: selectNode[i].excelLabel + t('datasource.api_field_not_empty'),
type: 'error'
})
return
}
for (let j = 0; j < selectNode[i].fields.length; j++) {
if (
selectNode[i].fields[j].checked &&
@ -653,14 +660,6 @@ defineExpose({
{{ t('data_set.field_selection') }}
</el-button>
</div>
<el-button @click="refreshData" secondary>
<template #icon>
<el-icon>
<Icon><icon_refresh_outlined class="svg-icon" /></Icon>
</el-icon>
</template>
{{ t('data_set.refresh_data') }}
</el-button>
</div>
<div class="info-table" v-if="isResize">
<el-auto-resizer v-if="currentMode === 'preview'">

@ -1 +1 @@
Subproject commit d0056943de6199f78d70b52b66174786e4d56519
Subproject commit 13ecdfab148086cf6e66fed2bcb41d88fdd5efab

View File

@ -371,6 +371,7 @@ function restore() {
mkdir -p $DE_RUNNING_BASE
fi
echo "恢复备份 $target"
rm -rf $DE_RUNNING_BASE/data/mysql/*
tar -zxf $target --directory=$DE_RUNNING_BASE
service dataease start
else

View File

@ -1,16 +0,0 @@
package io.dataease.api.rmonitor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
public interface ResourceMonitorApi {
@GetMapping("/existFree")
boolean existFree();
@PostMapping("/delete")
void delete();
@PostMapping("/sync")
void sync();
}