feat: 增加数据导出中心

This commit is contained in:
taojinlong 2024-05-24 17:37:49 +08:00
commit 3983b592bb
89 changed files with 1996 additions and 453 deletions

View File

@ -557,17 +557,11 @@ public class ChartDataManage {
} else if (StringUtils.containsIgnoreCase(view.getType(), "quadrant")) { } else if (StringUtils.containsIgnoreCase(view.getType(), "quadrant")) {
Dimension2SQLObj.dimension2sqlObj(sqlMeta, xAxis, transFields(allFields), crossDs, dsMap); Dimension2SQLObj.dimension2sqlObj(sqlMeta, xAxis, transFields(allFields), crossDs, dsMap);
yAxis.addAll(extBubble); yAxis.addAll(extBubble);
if(ObjectUtils.isNotEmpty(view.getExtTooltip())){
yAxis.addAll(new ArrayList<>(view.getExtTooltip()));
}
Quota2SQLObj.quota2sqlObj(sqlMeta, yAxis, transFields(allFields), crossDs, dsMap); Quota2SQLObj.quota2sqlObj(sqlMeta, yAxis, transFields(allFields), crossDs, dsMap);
querySql = SQLProvider.createQuerySQL(sqlMeta, true, needOrder, view); querySql = SQLProvider.createQuerySQL(sqlMeta, true, needOrder, view);
if (containDetailField(view) && ObjectUtils.isNotEmpty(viewFields)) {
detailFieldList.addAll(xAxis);
detailFieldList.addAll(viewFields);
Dimension2SQLObj.dimension2sqlObj(sqlMeta, detailFieldList, transFields(allFields), crossDs, dsMap);
String originSql = SQLProvider.createQuerySQL(sqlMeta, false, needOrder, view);
String limit = ((pageInfo.getGoPage() != null && pageInfo.getPageSize() != null) ? " LIMIT " + pageInfo.getPageSize() + " OFFSET " + (pageInfo.getGoPage() - 1) * pageInfo.getPageSize() : "");
detailFieldSql = originSql + limit;
}
} else if (StringUtils.equalsIgnoreCase("bar-range", view.getType())) { } else if (StringUtils.equalsIgnoreCase("bar-range", view.getType())) {
sqlMeta.setChartType(view.getType()); sqlMeta.setChartType(view.getType());
Dimension2SQLObj.dimension2sqlObj(sqlMeta, xAxis, transFields(allFields), crossDs, dsMap); Dimension2SQLObj.dimension2sqlObj(sqlMeta, xAxis, transFields(allFields), crossDs, dsMap);
@ -791,7 +785,7 @@ public class ChartDataManage {
} else if (StringUtils.containsIgnoreCase(view.getType(), "label")) { } else if (StringUtils.containsIgnoreCase(view.getType(), "label")) {
mapChart = ChartDataBuild.transLabelChartData(xAxis, yAxis, view, data, isDrill); mapChart = ChartDataBuild.transLabelChartData(xAxis, yAxis, view, data, isDrill);
} else if (StringUtils.containsIgnoreCase(view.getType(), "quadrant")) { } else if (StringUtils.containsIgnoreCase(view.getType(), "quadrant")) {
mapChart = ChartDataBuild.transQuadrantDataAntV(xAxis, yAxis, view, data, extBubble, isDrill); mapChart = ChartDataBuild.transMixChartDataAntV(xAxis, yAxis, view, data, isDrill);
} else if (StringUtils.equalsIgnoreCase(view.getType(), "bar-range")) { } else if (StringUtils.equalsIgnoreCase(view.getType(), "bar-range")) {
mapChart = ChartDataBuild.transBarRangeDataAntV(skipBarRange, barRangeDate, xAxisBase, xAxis, yAxis, view, data, isDrill); mapChart = ChartDataBuild.transBarRangeDataAntV(skipBarRange, barRangeDate, xAxisBase, xAxis, yAxis, view, data, isDrill);
} else { } else {

View File

@ -532,6 +532,13 @@ public class DatasetDataManage {
boolean crossDs = false; boolean crossDs = false;
Map<Long, DatasourceSchemaDTO> dsMap = null; Map<Long, DatasourceSchemaDTO> dsMap = null;
if (ObjectUtils.isNotEmpty(request.getSortId())) {
// 如果排序字段和查询字段显示字段不一致则加入到查询列表中
if (!request.getSortId().equals(request.getQueryId()) && !request.getSortId().equals(request.getDisplayId())) {
ids.add(request.getSortId());
}
}
for (Long id : ids) { for (Long id : ids) {
DatasetTableFieldDTO field = datasetTableFieldManage.selectById(id); DatasetTableFieldDTO field = datasetTableFieldManage.selectById(id);
if (field == null) { if (field == null) {

View File

@ -71,6 +71,9 @@ public class TemplateCenterManage {
public String marketGet(String url, String accessKey) { public String marketGet(String url, String accessKey) {
HttpClientConfig config = new HttpClientConfig(); HttpClientConfig config = new HttpClientConfig();
config.addHeader("API-Authorization", accessKey); config.addHeader("API-Authorization", accessKey);
config.setConnectTimeout(5000);
config.setSocketTimeout(10000);
config.setConnectionRequestTimeout(5000);
return HttpClientUtil.get(url, config); return HttpClientUtil.get(url, config);
} }

View File

@ -109,6 +109,7 @@ public class CoreVisualizationManage {
@XpackInteract(value = "visualizationResourceTree", before = false) @XpackInteract(value = "visualizationResourceTree", before = false)
public Long innerSave(DataVisualizationInfo visualizationInfo) { public Long innerSave(DataVisualizationInfo visualizationInfo) {
visualizationInfo.setVersion(3);
return preInnerSave(visualizationInfo); return preInnerSave(visualizationInfo);
} }
@ -123,7 +124,6 @@ public class CoreVisualizationManage {
visualizationInfo.setCreateTime(System.currentTimeMillis()); visualizationInfo.setCreateTime(System.currentTimeMillis());
visualizationInfo.setUpdateTime(System.currentTimeMillis()); visualizationInfo.setUpdateTime(System.currentTimeMillis());
visualizationInfo.setOrgId(AuthUtils.getUser().getDefaultOid()); visualizationInfo.setOrgId(AuthUtils.getUser().getDefaultOid());
visualizationInfo.setVersion(3);
mapper.insert(visualizationInfo); mapper.insert(visualizationInfo);
coreOptRecentManage.saveOpt(visualizationInfo.getId(), OptConstants.OPT_RESOURCE_TYPE.VISUALIZATION, OptConstants.OPT_TYPE.NEW); coreOptRecentManage.saveOpt(visualizationInfo.getId(), OptConstants.OPT_RESOURCE_TYPE.VISUALIZATION, OptConstants.OPT_TYPE.NEW);
return visualizationInfo.getId(); return visualizationInfo.getId();

View File

@ -15,7 +15,7 @@ INSERT INTO `core_sys_startup_job`
VALUES ('chartFilterMerge', 'chartFilterMerge', 'ready'); VALUES ('chartFilterMerge', 'chartFilterMerge', 'ready');
COMMIT; COMMIT;
TRUNCATE TABLE `xpack_setting_authentication`;
ALTER TABLE `xpack_setting_authentication` ALTER TABLE `xpack_setting_authentication`
ADD COLUMN `plugin_json` longtext NULL COMMENT '插件配置' AFTER `relational_ids`; ADD COLUMN `plugin_json` longtext NULL COMMENT '插件配置' AFTER `relational_ids`;
ALTER TABLE `xpack_setting_authentication` ALTER TABLE `xpack_setting_authentication`
@ -41,3 +41,14 @@ CREATE TABLE `core_export_task`
`params` longtext NOT NULL COMMENT '过滤参数', `params` longtext NOT NULL COMMENT '过滤参数',
PRIMARY KEY (`id`) PRIMARY KEY (`id`)
) COMMENT='导出任务表'; ) COMMENT='导出任务表';
DROP TABLE IF EXISTS `xpack_platform_token`;
CREATE TABLE `xpack_platform_token`
(
`id` int NOT NULL,
`token` varchar(255) NOT NULL,
`create_time` bigint NOT NULL,
`exp_time` bigint NOT NULL,
PRIMARY KEY (`id`)
);

View File

@ -25,7 +25,7 @@
"axios": "^1.3.3", "axios": "^1.3.3",
"crypto-js": "^4.1.1", "crypto-js": "^4.1.1",
"dayjs": "^1.11.9", "dayjs": "^1.11.9",
"element-plus-secondary": "^0.5.8", "element-plus-secondary": "^0.5.9",
"element-resize-detector": "^1.2.4", "element-resize-detector": "^1.2.4",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"flv.js": "^1.6.2", "flv.js": "^1.6.2",

View File

@ -0,0 +1,11 @@
<svg width="80" height="56" viewBox="0 0 80 56" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M41 2.00391V53.0016V54.0016H38V2.00391H41Z" fill="#434343"/>
<path d="M4.5 50C4.22386 50 4 49.7761 4 49.5L4 42.5C4 42.2239 4.22386 42 4.5 42H35.5C35.7761 42 36 42.2239 36 42.5V49.5C36 49.7761 35.7761 50 35.5 50H4.5Z" fill="#00D6B9"/>
<path d="M12.5 38C12.2239 38 12 37.7761 12 37.5V30.5C12 30.2239 12.2239 30 12.5 30H35.5C35.7761 30 36 30.2239 36 30.5V37.5C36 37.7761 35.7761 38 35.5 38H12.5Z" fill="#00D6B9"/>
<path d="M43.5 14C43.2239 14 43 13.7761 43 13.5V6.5C43 6.22386 43.2239 6 43.5 6L71.5 6C71.7761 6 72 6.22386 72 6.5V13.5C72 13.7761 71.7761 14 71.5 14L43.5 14Z" fill="#3370FF"/>
<path d="M43.5 26C43.2239 26 43 25.7761 43 25.5V18.5C43 18.2239 43.2239 18 43.5 18H62.5C62.7761 18 63 18.2239 63 18.5V25.5C63 25.7761 62.7761 26 62.5 26H43.5Z" fill="#3370FF"/>
<path d="M43.5 38C43.2239 38 43 37.7761 43 37.5V30.5C43 30.2239 43.2239 30 43.5 30H57.5C57.7761 30 58 30.2239 58 30.5V37.5C58 37.7761 57.7761 38 57.5 38H43.5Z" fill="#3370FF"/>
<path d="M43.5 50C43.2239 50 43 49.7761 43 49.5V42.5C43 42.2239 43.2239 42 43.5 42H75.5C75.7761 42 76 42.2239 76 42.5V49.5C76 49.7761 75.7761 50 75.5 50H43.5Z" fill="#3370FF"/>
<path d="M16.5 26C16.2239 26 16 25.7761 16 25.5V18.5C16 18.2239 16.2239 18 16.5 18H35.5C35.7761 18 36 18.2239 36 18.5V25.5C36 25.7761 35.7761 26 35.5 26H16.5Z" fill="#00D6B9"/>
<path d="M8.5 14C8.22386 14 8 13.7761 8 13.5V6.5C8 6.22386 8.22386 6 8.5 6L35.5 6C35.7761 6 36 6.22386 36 6.5V13.5C36 13.7761 35.7761 14 35.5 14L8.5 14Z" fill="#00D6B9"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,11 @@
<svg width="80" height="56" viewBox="0 0 80 56" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M41 2.00391V53.0016V54.0016H38V2.00391H41Z" fill="#DEE0E3"/>
<path d="M4.5 50C4.22386 50 4 49.7761 4 49.5L4 42.5C4 42.2239 4.22386 42 4.5 42H35.5C35.7761 42 36 42.2239 36 42.5V49.5C36 49.7761 35.7761 50 35.5 50H4.5Z" fill="#00D6B9"/>
<path d="M12.5 38C12.2239 38 12 37.7761 12 37.5V30.5C12 30.2239 12.2239 30 12.5 30H35.5C35.7761 30 36 30.2239 36 30.5V37.5C36 37.7761 35.7761 38 35.5 38H12.5Z" fill="#00D6B9"/>
<path d="M43.5 14C43.2239 14 43 13.7761 43 13.5V6.5C43 6.22386 43.2239 6 43.5 6L71.5 6C71.7761 6 72 6.22386 72 6.5V13.5C72 13.7761 71.7761 14 71.5 14L43.5 14Z" fill="#3370FF"/>
<path d="M43.5 26C43.2239 26 43 25.7761 43 25.5V18.5C43 18.2239 43.2239 18 43.5 18H62.5C62.7761 18 63 18.2239 63 18.5V25.5C63 25.7761 62.7761 26 62.5 26H43.5Z" fill="#3370FF"/>
<path d="M43.5 38C43.2239 38 43 37.7761 43 37.5V30.5C43 30.2239 43.2239 30 43.5 30H57.5C57.7761 30 58 30.2239 58 30.5V37.5C58 37.7761 57.7761 38 57.5 38H43.5Z" fill="#3370FF"/>
<path d="M43.5 50C43.2239 50 43 49.7761 43 49.5V42.5C43 42.2239 43.2239 42 43.5 42H75.5C75.7761 42 76 42.2239 76 42.5V49.5C76 49.7761 75.7761 50 75.5 50H43.5Z" fill="#3370FF"/>
<path d="M16.5 26C16.2239 26 16 25.7761 16 25.5V18.5C16 18.2239 16.2239 18 16.5 18H35.5C35.7761 18 36 18.2239 36 18.5V25.5C36 25.7761 35.7761 26 35.5 26H16.5Z" fill="#00D6B9"/>
<path d="M8.5 14C8.22386 14 8 13.7761 8 13.5V6.5C8 6.22386 8.22386 6 8.5 6L35.5 6C35.7761 6 36 6.22386 36 6.5V13.5C36 13.7761 35.7761 14 35.5 14L8.5 14Z" fill="#00D6B9"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1716540076970" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7068" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M912 480c-17.7 0-32 14.3-32 32 0 25-2.5 50-7.5 74.2-4.8 23.6-12 46.8-21.4 69-9.2 21.8-20.6 42.8-33.9 62.5-13.2 19.5-28.3 37.8-45 54.5s-35 31.8-54.5 45c-19.7 13.3-40.7 24.7-62.5 33.9-22.2 9.4-45.4 16.6-69 21.4-48.5 9.9-99.9 9.9-148.4 0-23.6-4.8-46.8-12-69-21.4-21.8-9.2-42.8-20.6-62.5-33.9-19.5-13.2-37.8-28.3-54.5-45s-31.8-35-45-54.5c-13.3-19.7-24.7-40.7-33.9-62.5-9.4-22.2-16.6-45.4-21.4-69-5-24.2-7.5-49.2-7.5-74.2s2.5-50 7.5-74.2c4.8-23.6 12-46.8 21.4-69 9.2-21.8 20.6-42.8 33.9-62.5 13.2-19.5 28.3-37.8 45-54.5s35-31.8 54.5-45c19.7-13.3 40.7-24.7 62.5-33.9 22.2-9.4 45.4-16.6 69-21.4 48.5-9.9 99.9-9.9 148.4 0 23.6 4.8 46.8 12 69 21.4 21.8 9.2 42.8 20.6 62.5 33.9 19.5 13.2 37.8 28.3 54.5 45 1.4 1.4 2.8 2.8 4.1 4.2H688c-17.7 0-32 14.3-32 32s14.3 32 32 32h160c17.7 0 32-14.3 32-32V128c0-17.7-14.3-32-32-32s-32 14.3-32 32v77.1c-19.2-19-40.1-36.2-62.4-51.3-23.1-15.6-47.8-29-73.4-39.8-26.1-11-53.4-19.5-81.1-25.2-56.9-11.6-117.1-11.6-174.1 0-27.8 5.7-55.1 14.2-81.1 25.2-25.6 10.8-50.3 24.2-73.4 39.8-22.9 15.4-44.4 33.2-63.9 52.7s-37.3 41-52.7 63.9c-15.6 23.1-29 47.8-39.8 73.4-11 26.1-19.5 53.4-25.2 81.1C83 453.4 80 482.7 80 512s3 58.6 8.8 87c5.7 27.8 14.2 55 25.2 81.1 10.8 25.6 24.2 50.3 39.8 73.4 15.4 22.9 33.2 44.4 52.7 63.9s41 37.3 63.9 52.7c23.1 15.6 47.8 29 73.4 39.8 26.1 11 53.4 19.5 81.1 25.2 28.5 5.8 57.7 8.8 87 8.8s58.6-3 87-8.8c27.8-5.7 55-14.2 81.1-25.2 25.6-10.8 50.3-24.2 73.4-39.8 22.9-15.5 44.4-33.2 63.9-52.7s37.3-41 52.7-63.9c15.6-23.1 29-47.8 39.8-73.4 11-26.1 19.5-53.4 25.2-81.1 5.8-28.5 8.8-57.7 8.8-87 0.2-17.7-14.1-32-31.8-32z" fill="#1875F0" p-id="7069"></path></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1716529317390" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1706" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M810.666667 128H213.333333c-46.933333 0-85.333333 38.4-85.333333 85.333333v597.333334c0 46.933333 38.4 85.333333 85.333333 85.333333h597.333334c46.933333 0 85.333333-38.4 85.333333-85.333333V213.333333c0-46.933333-38.4-85.333333-85.333333-85.333333z m0 640c0 23.466667-19.2 42.666667-42.666667 42.666667H256c-23.466667 0-42.666667-19.2-42.666667-42.666667V256c0-23.466667 19.2-42.666667 42.666667-42.666667h512c23.466667 0 42.666667 19.2 42.666667 42.666667v512z" fill="#297AFF" p-id="1707"></path><path d="M725.333333 317.866667c0-12.8-8.533333-21.333333-21.333333-21.333334H320c-12.8 0-21.333333 8.533333-21.333333 21.333334v42.666666c0 12.8 8.533333 21.333333 21.333333 21.333334h149.333333v234.666666c0 12.8 8.533333 21.333333 21.333334 21.333334h42.666666c12.8 0 21.333333-8.533333 21.333334-21.333334v-234.666666h149.333333c12.8 0 21.333333-8.533333 21.333333-21.333334v-42.666666zM714.666667 682.666667H358.4v-32c0-2.133333-4.266667-4.266667-6.4-2.133334l-40.533333 40.533334c-8.533333 8.533333-8.533333 21.333333 0 29.866666l40.533333 40.533334c2.133333 2.133333 6.4 0 6.4-2.133334V725.333333h356.266667c6.4 0 10.666667-4.266667 10.666666-10.666666v-21.333334c0-6.4-4.266667-10.666667-10.666666-10.666666z" fill="#297AFF" p-id="1708"></path></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1716538241941" class="icon" viewBox="0 0 1166 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4799" xmlns:xlink="http://www.w3.org/1999/xlink" width="227.734375" height="200"><path d="M1016.603881 149.959255A508.396905 508.396905 0 0 0 661.880392 0H47.123419a42.206376 42.206376 0 0 0-42.206376 42.206376 42.206376 42.206376 0 0 0 42.206376 42.206376h607.45527c235.764818 0 427.571694 191.806877 427.571695 427.571695S890.343507 939.577245 654.578689 939.577245s-427.571694-191.82798-427.571694-427.592798h105.51594a42.206376 42.206376 0 0 0 42.206376-42.206376 42.206376 42.206376 0 0 0-42.206376-42.206377H42.206376a42.206376 42.206376 0 0 0-42.206376 42.206377 42.206376 42.206376 0 0 0 42.206376 42.206376h100.387866a511.984447 511.984447 0 0 0 874.009639 362.046295 512.00555 512.00555 0 0 0 0-724.071487z" p-id="4800"></path><path d="M411.237827 298.166945a42.206376 42.206376 0 0 0 42.206376-42.206376 42.206376 42.206376 0 0 0-42.206376-42.206377H120.857958a42.206376 42.206376 0 0 0-42.206376 42.206377 42.206376 42.206376 0 0 0 42.206376 42.206376zM635.205962 232.388308a42.311892 42.311892 0 0 0-42.206376 42.206376v290.379868a42.311892 42.311892 0 0 0 42.206376 42.206377h228.737456a42.206376 42.206376 0 0 0 42.206377-42.206377 42.206376 42.206376 0 0 0-42.206377-42.206376h-186.657699V274.573581A42.206376 42.206376 0 0 0 635.205962 232.388308z" p-id="4801"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 739 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 742 KiB

View File

@ -0,0 +1,11 @@
<svg width="80" height="56" viewBox="0 0 80 56" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7 2.00391V53.0016V54.0016H4V2.00391H7Z" fill="#434343"/>
<path d="M9.5 14C9.22386 14 9 13.7761 9 13.5V6.5C9 6.22386 9.22386 6 9.5 6L71.5 6C71.7761 6 72 6.22386 72 6.5V13.5C72 13.7761 71.7761 14 71.5 14L9.5 14Z" fill="#434343"/>
<path d="M9.5 26C9.22386 26 9 25.7761 9 25.5V18.5C9 18.2239 9.22386 18 9.5 18H71.5C71.7761 18 72 18.2239 72 18.5V25.5C72 25.7761 71.7761 26 71.5 26L9.5 26Z" fill="#434343"/>
<path d="M9.5 38C9.22386 38 9 37.7761 9 37.5V30.5C9 30.2239 9.22386 30 9.5 30H71.5C71.7761 30 72 30.2239 72 30.5V37.5C72 37.7761 71.7761 38 71.5 38H9.5Z" fill="#434343"/>
<path d="M9.5 50C9.22386 50 9 49.7761 9 49.5V42.5C9 42.2239 9.22386 42 9.5 42H71.5C71.7761 42 72 42.2239 72 42.5V49.5C72 49.7761 71.7761 50 71.5 50H9.5Z" fill="#434343"/>
<path d="M9.5 14C9.22386 14 9 13.7761 9 13.5V6.5C9 6.22386 9.22386 6 9.5 6L59.5 6C59.7761 6 60 6.22386 60 6.5V13.5C60 13.7761 59.7761 14 59.5 14L9.5 14Z" fill="#3370FF"/>
<path d="M9.5 26C9.22386 26 9 25.7761 9 25.5V18.5C9 18.2239 9.22386 18 9.5 18H46.5C46.7761 18 47 18.2239 47 18.5V25.5C47 25.7761 46.7761 26 46.5 26H9.5Z" fill="#3370FF"/>
<path d="M9.5 38C9.22386 38 9 37.7761 9 37.5V30.5C9 30.2239 9.22386 30 9.5 30H37.5C37.7761 30 38 30.2239 38 30.5V37.5C38 37.7761 37.7761 38 37.5 38H9.5Z" fill="#3370FF"/>
<path d="M9.5 50C9.22386 50 9 49.7761 9 49.5V42.5C9 42.2239 9.22386 42 9.5 42H21.5C21.7761 42 22 42.2239 22 42.5V49.5C22 49.7761 21.7761 50 21.5 50H9.5Z" fill="#3370FF"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,11 @@
<svg width="80" height="56" viewBox="0 0 80 56" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7 2.00391V53.0016V54.0016H4V2.00391H7Z" fill="#DEE0E3"/>
<path d="M9.5 14C9.22386 14 9 13.7761 9 13.5V6.5C9 6.22386 9.22386 6 9.5 6L71.5 6C71.7761 6 72 6.22386 72 6.5V13.5C72 13.7761 71.7761 14 71.5 14L9.5 14Z" fill="#DEE0E3"/>
<path d="M9.5 26C9.22386 26 9 25.7761 9 25.5V18.5C9 18.2239 9.22386 18 9.5 18H71.5C71.7761 18 72 18.2239 72 18.5V25.5C72 25.7761 71.7761 26 71.5 26L9.5 26Z" fill="#DEE0E3"/>
<path d="M9.5 38C9.22386 38 9 37.7761 9 37.5V30.5C9 30.2239 9.22386 30 9.5 30H71.5C71.7761 30 72 30.2239 72 30.5V37.5C72 37.7761 71.7761 38 71.5 38H9.5Z" fill="#DEE0E3"/>
<path d="M9.5 50C9.22386 50 9 49.7761 9 49.5V42.5C9 42.2239 9.22386 42 9.5 42H71.5C71.7761 42 72 42.2239 72 42.5V49.5C72 49.7761 71.7761 50 71.5 50H9.5Z" fill="#DEE0E3"/>
<path d="M9.5 14C9.22386 14 9 13.7761 9 13.5V6.5C9 6.22386 9.22386 6 9.5 6L59.5 6C59.7761 6 60 6.22386 60 6.5V13.5C60 13.7761 59.7761 14 59.5 14L9.5 14Z" fill="#3370FF"/>
<path d="M9.5 26C9.22386 26 9 25.7761 9 25.5V18.5C9 18.2239 9.22386 18 9.5 18H46.5C46.7761 18 47 18.2239 47 18.5V25.5C47 25.7761 46.7761 26 46.5 26H9.5Z" fill="#3370FF"/>
<path d="M9.5 38C9.22386 38 9 37.7761 9 37.5V30.5C9 30.2239 9.22386 30 9.5 30H37.5C37.7761 30 38 30.2239 38 30.5V37.5C38 37.7761 37.7761 38 37.5 38H9.5Z" fill="#3370FF"/>
<path d="M9.5 50C9.22386 50 9 49.7761 9 49.5V42.5C9 42.2239 9.22386 42 9.5 42H21.5C21.7761 42 22 42.2239 22 42.5V49.5C22 49.7761 21.7761 50 21.5 50H9.5Z" fill="#3370FF"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1716529317390" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1706" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M810.666667 128H213.333333c-46.933333 0-85.333333 38.4-85.333333 85.333333v597.333334c0 46.933333 38.4 85.333333 85.333333 85.333333h597.333334c46.933333 0 85.333333-38.4 85.333333-85.333333V213.333333c0-46.933333-38.4-85.333333-85.333333-85.333333z m0 640c0 23.466667-19.2 42.666667-42.666667 42.666667H256c-23.466667 0-42.666667-19.2-42.666667-42.666667V256c0-23.466667 19.2-42.666667 42.666667-42.666667h512c23.466667 0 42.666667 19.2 42.666667 42.666667v512z" p-id="1707"></path><path d="M725.333333 317.866667c0-12.8-8.533333-21.333333-21.333333-21.333334H320c-12.8 0-21.333333 8.533333-21.333333 21.333334v42.666666c0 12.8 8.533333 21.333333 21.333333 21.333334h149.333333v234.666666c0 12.8 8.533333 21.333333 21.333334 21.333334h42.666666c12.8 0 21.333333-8.533333 21.333334-21.333334v-234.666666h149.333333c12.8 0 21.333333-8.533333 21.333333-21.333334v-42.666666zM714.666667 682.666667H358.4v-32c0-2.133333-4.266667-4.266667-6.4-2.133334l-40.533333 40.533334c-8.533333 8.533333-8.533333 21.333333 0 29.866666l40.533333 40.533334c2.133333 2.133333 6.4 0 6.4-2.133334V725.333333h356.266667c6.4 0 10.666667-4.266667 10.666666-10.666666v-21.333334c0-6.4-4.266667-10.666667-10.666666-10.666666z" p-id="1708"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -219,7 +219,7 @@ const openDataBoardSetting = () => {
} }
const openMobileSetting = () => { const openMobileSetting = () => {
if (!dvInfo.value.id) { if (!dvInfo.value.id || dvInfo.value.dataState === 'prepare') {
ElMessage.warning('请先保存当前页面') ElMessage.warning('请先保存当前页面')
return return
} }

View File

@ -91,7 +91,7 @@
<el-tooltip class="item" :effect="toolTip" placement="bottom"> <el-tooltip class="item" :effect="toolTip" placement="bottom">
<template #content> <template #content>
<div> <div>
{{ t('visualization.panel_view_result_tips') }} {{ t('visualization.panel_view_result_tips', [resourceType]) }}
</div> </div>
</template> </template>
<el-icon class="hint-icon" :class="{ 'hint-icon--dark': themes === 'dark' }"> <el-icon class="hint-icon" :class="{ 'hint-icon--dark': themes === 'dark' }">

View File

@ -227,7 +227,7 @@ eventBus.on('clearCanvas', clearCanvas)
> >
<query-group :dv-model="dvModel"></query-group> <query-group :dv-model="dvModel"></query-group>
</component-group> </component-group>
<component-group is-label :base-width="115" icon-name="dv-text" title="文本"> <component-group is-label :base-width="215" icon-name="dv-text" title="文本">
<text-group></text-group> <text-group></text-group>
</component-group> </component-group>
<component-group <component-group
@ -239,7 +239,7 @@ eventBus.on('clearCanvas', clearCanvas)
> >
<media-group></media-group> <media-group></media-group>
</component-group> </component-group>
<component-group is-label :base-width="115" icon-name="dv-more-com" title="更多"> <component-group is-label :base-width="215" icon-name="dv-more-com" title="更多">
<more-com-group></more-com-group> <more-com-group></more-com-group>
</component-group> </component-group>
<component-group is-label :base-width="410" icon-name="dv-material" title="素材"> <component-group is-label :base-width="410" icon-name="dv-material" title="素材">

View File

@ -1450,7 +1450,7 @@ defineExpose({
></canvas-opt-bar> ></canvas-opt-bar>
<!-- 网格线 --> <!-- 网格线 -->
<drag-shadow <drag-shadow
v-if="infoBox && infoBox.moveItem" v-if="infoBox && infoBox.moveItem && editMode !== 'preview'"
:base-height="baseHeight" :base-height="baseHeight"
:base-width="baseWidth" :base-width="baseWidth"
:cur-gap="curGap" :cur-gap="curGap"

View File

@ -294,7 +294,7 @@ const active = computed(() => {
}) })
const boardMoveActive = computed(() => { const boardMoveActive = computed(() => {
const CHARTS = ['map', 'bubble-map', 'table-info', 'table-normal', 'table-pivot'] const CHARTS = ['flow-map', 'map', 'bubble-map', 'table-info', 'table-normal', 'table-pivot']
return CHARTS.includes(element.value.innerType) return CHARTS.includes(element.value.innerType)
}) })

View File

@ -8,7 +8,7 @@
trigger="click" trigger="click"
> >
<div class="export-button"> <div class="export-button">
<el-select v-model="pixel" class="pixel-select" size="small"> <el-select v-if="optType === 'enlarge'" v-model="pixel" class="pixel-select" size="small">
<el-option-group v-for="group in pixelOptions" :key="group.label" :label="group.label"> <el-option-group v-for="group in pixelOptions" :key="group.label" :label="group.label">
<el-option <el-option
v-for="item in group.options" v-for="item in group.options"
@ -95,6 +95,7 @@ const optType = ref(null)
const chartComponentDetails = ref(null) const chartComponentDetails = ref(null)
const { dvInfo } = storeToRefs(dvMainStore) const { dvInfo } = storeToRefs(dvMainStore)
const exportLoading = ref(false) const exportLoading = ref(false)
const sourceViewType = ref()
const DETAIL_TABLE_ATTR: DeepPartial<ChartObj> = { const DETAIL_TABLE_ATTR: DeepPartial<ChartObj> = {
render: 'antv', render: 'antv',
type: 'table-info', type: 'table-info',
@ -165,6 +166,7 @@ const pixelOptions = [
} }
] ]
const dialogInit = (canvasStyle, view, item, opt) => { const dialogInit = (canvasStyle, view, item, opt) => {
sourceViewType.value = view.type
optType.value = opt optType.value = opt
dialogShow.value = true dialogShow.value = true
viewInfo.value = deepCopy(view) as DeepPartial<ChartObj> viewInfo.value = deepCopy(view) as DeepPartial<ChartObj>
@ -193,7 +195,12 @@ const downloadViewImage = () => {
const downloadViewDetails = () => { const downloadViewDetails = () => {
const viewDataInfo = dvMainStore.getViewDataDetails(viewInfo.value.id) const viewDataInfo = dvMainStore.getViewDataDetails(viewInfo.value.id)
const chartExtRequest = dvMainStore.getLastViewRequestInfo(viewInfo.value.id) const chartExtRequest = dvMainStore.getLastViewRequestInfo(viewInfo.value.id)
const chart = { ...viewInfo.value, chartExtRequest, data: viewDataInfo } const chart = {
...viewInfo.value,
chartExtRequest,
data: viewDataInfo,
type: sourceViewType.value
}
exportLoading.value = true exportLoading.value = true
exportExcelDownload(chart, () => { exportExcelDownload(chart, () => {
console.log('aa') console.log('aa')

View File

@ -84,7 +84,7 @@
@change="changeStyle" @change="changeStyle"
> >
<template #prefix> <template #prefix>
<el-icon> <el-icon :class="{ 'dark-icon': themes === 'dark' }">
<Icon :name="styleOptionKey.icon" /> <Icon :name="styleOptionKey.icon" />
</el-icon> </el-icon>
</template> </template>
@ -266,12 +266,28 @@ const styleMounted = ref({
opacity: 1, opacity: 1,
fontSize: 22, fontSize: 22,
activeFontSize: 22, activeFontSize: 22,
letterSpacing: 0,
scrollSpeed: 0,
fontWeight: 'normal', fontWeight: 'normal',
fontStyle: 'normal', fontStyle: 'normal',
textAlign: 'center', textAlign: 'center',
color: '#000000' color: '#000000'
}) })
const scrollSpeedList = [
{ name: '停止', value: 0 },
{ name: '1', value: 20 },
{ name: '2', value: 18 },
{ name: '3', value: 16 },
{ name: '4', value: 14 },
{ name: '5', value: 12 },
{ name: '6', value: 10 },
{ name: '7', value: 8 },
{ name: '8', value: 6 },
{ name: '9', value: 4 },
{ name: '10', value: 2 }
]
const opacitySizeList = [ const opacitySizeList = [
{ name: '0.1', value: 0.1 }, { name: '0.1', value: 0.1 },
{ name: '0.2', value: 0.2 }, { name: '0.2', value: 0.2 },
@ -310,6 +326,17 @@ const styleColorKeyArray = [
{ value: 'backgroundColor', label: '背景色', width: 90, icon: 'dv-style-backgroundColor' } { value: 'backgroundColor', label: '背景色', width: 90, icon: 'dv-style-backgroundColor' }
] ]
const letterSpacingList = computed(() => {
const arr = []
for (let i = 0; i <= 60; i = i + 1) {
arr.push({
name: i + '',
value: i
})
}
return arr
})
const fontSizeList = computed(() => { const fontSizeList = computed(() => {
const arr = [] const arr = []
for (let i = 10; i <= 60; i = i + 1) { for (let i = 10; i <= 60; i = i + 1) {
@ -351,6 +378,13 @@ const borderStyleList = [
// //
const styleOptionMountedKeyArray = [ const styleOptionMountedKeyArray = [
{
value: 'letterSpacing',
label: '字间距',
customOption: letterSpacingList.value,
width: '90px',
icon: 'dv-style-letterSpacing'
},
{ {
value: 'fontSize', value: 'fontSize',
label: '字体大小', label: '字体大小',
@ -369,6 +403,13 @@ const styleOptionMountedKeyArray = [
// //
const styleOptionKeyArray = [ const styleOptionKeyArray = [
{
value: 'scrollSpeed',
label: '滚动速度',
customOption: scrollSpeedList,
width: '90px',
icon: 'dv-style-scroll-speed'
},
{ {
value: 'opacity', value: 'opacity',
label: '不透明度', label: '不透明度',

View File

@ -28,8 +28,8 @@ const props = defineProps({
}) })
const { dvModel } = toRefs(props) const { dvModel } = toRefs(props)
const newComponent = () => { const newComponent = params => {
eventBus.emit('handleNew', { componentName: 'DeTimeClock', innerType: 'DeTimeClock' }) eventBus.emit('handleNew', { componentName: params, innerType: params })
} }
const handleDragStart = e => { const handleDragStart = e => {
@ -42,17 +42,20 @@ const handleDragEnd = e => {
</script> </script>
<template> <template>
<div <div class="group" @dragstart="handleDragStart" @dragend="handleDragEnd">
class="group"
@dragstart="handleDragStart"
@dragend="handleDragEnd"
v-on:click="newComponent"
>
<drag-component <drag-component
:themes="themes" :themes="themes"
name="YYYY-MM-DD 08:00:00" name="YYYY-MM-DD 08:00:00"
label="日期时间" label="日期时间"
drag-info="DeTimeClock&DeTimeClock" drag-info="DeTimeClock&DeTimeClock"
v-on:click="newComponent('DeTimeClock')"
></drag-component>
<drag-component
:themes="themes"
icon="db-more-web"
label="网页"
drag-info="DeFrame&DeFrame"
v-on:click="newComponent('DeFrame')"
></drag-component> ></drag-component>
</div> </div>
</template> </template>
@ -60,5 +63,6 @@ const handleDragEnd = e => {
<style lang="less" scoped> <style lang="less" scoped>
.group { .group {
padding: 12px 8px; padding: 12px 8px;
display: inline-flex;
} }
</style> </style>

View File

@ -11,7 +11,7 @@ const props = defineProps({
}, },
dvModel: { dvModel: {
type: String, type: String,
default: 'dv' default: 'dataV'
}, },
element: { element: {
type: Object, type: Object,
@ -37,23 +37,27 @@ const handleDragEnd = e => {
commonHandleDragEnd(e, dvModel.value) commonHandleDragEnd(e, dvModel.value)
} }
const newComponent = () => { const newComponent = (componentName, innerType) => {
eventBus.emit('handleNew', { componentName: 'UserView', innerType: 'rich-text' }) eventBus.emit('handleNew', { componentName: componentName, innerType: innerType })
} }
</script> </script>
<template> <template>
<div <div class="group" @dragstart="handleDragStart" @dragend="handleDragEnd">
class="group"
@dragstart="handleDragStart"
@dragend="handleDragEnd"
v-on:click="newComponent"
>
<drag-component <drag-component
:themes="themes" :themes="themes"
icon="dv-richText" icon="dv-richText"
label="富文本" label="富文本"
drag-info="UserView&rich-text" drag-info="UserView&rich-text"
v-on:click="newComponent('UserView', 'rich-text')"
></drag-component>
<drag-component
v-if="dvModel === 'dataV'"
:themes="themes"
icon="dv-scroll-text"
label="跑马灯"
drag-info="ScrollText&ScrollText"
v-on:click="newComponent('ScrollText', 'ScrollText')"
></drag-component> ></drag-component>
</div> </div>
</template> </template>
@ -61,10 +65,6 @@ const newComponent = () => {
<style lang="less" scoped> <style lang="less" scoped>
.group { .group {
padding: 12px 8px; padding: 12px 8px;
} display: inline-flex;
.custom_img {
width: 100px;
height: 70px;
cursor: pointer;
} }
</style> </style>

View File

@ -253,8 +253,8 @@ const list = [
}, },
{ {
component: 'DeVideo', component: 'DeVideo',
name: '媒体', name: '视频',
label: '媒体', label: '视频',
innerType: 'DeVideo', innerType: 'DeVideo',
editing: false, editing: false,
canvasActive: false, canvasActive: false,
@ -272,8 +272,8 @@ const list = [
}, },
{ {
component: 'DeStreamMedia', component: 'DeStreamMedia',
name: '媒体', name: '媒体',
label: '媒体', label: '媒体',
innerType: 'DeStreamMedia', innerType: 'DeStreamMedia',
editing: false, editing: false,
canvasActive: false, canvasActive: false,
@ -386,7 +386,7 @@ const list = [
}, },
{ {
component: 'CanvasBoard', component: 'CanvasBoard',
name: '图形', name: '边框',
label: '边框', label: '边框',
propValue: '', propValue: '',
icon: 'other_material_board', icon: 'other_material_board',
@ -477,6 +477,29 @@ const list = [
headFontColor: '#000000', headFontColor: '#000000',
headFontActiveColor: '#000000' headFontActiveColor: '#000000'
} }
},
{
component: 'ScrollText',
name: '跑马灯',
label: '跑马灯',
propValue: '双击编辑文字',
innerType: 'ScrollText',
icon: 'scroll-text',
x: 1,
y: 1,
sizeX: 36,
sizeY: 14,
style: {
width: 400,
height: 80,
fontSize: 14,
fontWeight: 400,
letterSpacing: 0,
color: '',
padding: 4,
verticalAlign: 'middle',
scrollSpeed: 0
}
} }
] ]

View File

@ -19,7 +19,7 @@
</el-tooltip> </el-tooltip>
</span> </span>
</template> </template>
<el-input v-model="state.linkInfoTemp.src" @blur="onBlur" /> <el-input :effect="themes" v-model="state.linkInfoTemp.src" @blur="onBlur" />
</el-form-item> </el-form-item>
</el-form> </el-form>
</el-row> </el-row>

View File

@ -93,9 +93,10 @@ const result = computed(() => {
const indicatorColor = ref(DEFAULT_INDICATOR_STYLE.color) const indicatorColor = ref(DEFAULT_INDICATOR_STYLE.color)
const thresholdColor = computed(() => { const thresholdColor = computed(() => {
let color = indicatorColor.value let color: string = indicatorColor.value
let backgroundColor: string = DEFAULT_INDICATOR_STYLE.backgroundColor
if (result.value === '-') { if (result.value === '-') {
return color return { color, backgroundColor }
} }
const value = result.value const value = result.value
if ( if (
@ -107,42 +108,47 @@ const thresholdColor = computed(() => {
for (let i = 0; i < senior.threshold.labelThreshold.length; i++) { for (let i = 0; i < senior.threshold.labelThreshold.length; i++) {
let flag = false let flag = false
const t = senior.threshold.labelThreshold[i] const t = senior.threshold.labelThreshold[i]
const tv = parseFloat(t.value) const tv = t.value
if (t.term === 'eq') { if (t.term === 'eq') {
if (value === tv) { if (value === tv) {
color = t.color color = t.color
backgroundColor = t.backgroundColor
flag = true flag = true
} }
} else if (t.term === 'not_eq') { } else if (t.term === 'not_eq') {
if (value !== tv) { if (value !== tv) {
color = t.color color = t.color
backgroundColor = t.backgroundColor
flag = true flag = true
} }
} else if (t.term === 'lt') { } else if (t.term === 'lt') {
if (value < tv) { if (value < tv) {
color = t.color color = t.color
backgroundColor = t.backgroundColor
flag = true flag = true
} }
} else if (t.term === 'gt') { } else if (t.term === 'gt') {
if (value > tv) { if (value > tv) {
color = t.color color = t.color
backgroundColor = t.backgroundColor
flag = true flag = true
} }
} else if (t.term === 'le') { } else if (t.term === 'le') {
if (value <= tv) { if (value <= tv) {
color = t.color color = t.color
backgroundColor = t.backgroundColor
flag = true flag = true
} }
} else if (t.term === 'ge') { } else if (t.term === 'ge') {
if (value >= tv) { if (value >= tv) {
color = t.color color = t.color
backgroundColor = t.backgroundColor
flag = true flag = true
} }
} else if (t.term === 'between') { } else if (t.term === 'between') {
const min = parseFloat(t.min) if (t.min <= value && value <= t.max) {
const max = parseFloat(t.max)
if (min <= value && value <= max) {
color = t.color color = t.color
backgroundColor = t.backgroundColor
flag = true flag = true
} }
} }
@ -151,7 +157,7 @@ const thresholdColor = computed(() => {
} }
} }
} }
return color return { color, backgroundColor }
}) })
const formattedResult = computed(() => { const formattedResult = computed(() => {
@ -175,11 +181,12 @@ const contentStyle = ref<CSSProperties>({
'flex-direction': 'column', 'flex-direction': 'column',
'align-items': 'center', 'align-items': 'center',
'justify-content': 'center', 'justify-content': 'center',
height: '100%' height: '100%',
'background-color': thresholdColor.value.backgroundColor
}) })
const indicatorClass = ref<CSSProperties>({ const indicatorClass = ref<CSSProperties>({
color: thresholdColor.value, color: thresholdColor.value.color,
'font-size': DEFAULT_INDICATOR_STYLE.fontSize + 'px', 'font-size': DEFAULT_INDICATOR_STYLE.fontSize + 'px',
'font-family': defaultTo( 'font-family': defaultTo(
CHART_FONT_FAMILY_MAP[DEFAULT_INDICATOR_STYLE.fontFamily], CHART_FONT_FAMILY_MAP[DEFAULT_INDICATOR_STYLE.fontFamily],
@ -280,7 +287,7 @@ const renderChart = async view => {
} }
indicatorClass.value = { indicatorClass.value = {
color: thresholdColor.value, color: thresholdColor.value.color,
'font-size': indicator.fontSize + 'px', 'font-size': indicator.fontSize + 'px',
'font-family': defaultTo( 'font-family': defaultTo(
CHART_FONT_FAMILY_MAP[indicator.fontFamily], CHART_FONT_FAMILY_MAP[indicator.fontFamily],
@ -292,6 +299,7 @@ const renderChart = async view => {
'text-shadow': indicator.fontShadow ? '2px 2px 4px' : 'none', 'text-shadow': indicator.fontShadow ? '2px 2px 4px' : 'none',
'font-synthesis': 'weight style' 'font-synthesis': 'weight style'
} }
contentStyle.value['background-color'] = thresholdColor.value.backgroundColor
indicatorSuffixClass.value = { indicatorSuffixClass.value = {
color: suffixColor, color: suffixColor,

View File

@ -0,0 +1,56 @@
<script setup lang="ts">
import CommonAttr from '@/custom-component/common/CommonAttr.vue'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { storeToRefs } from 'pinia'
import { toRefs } from 'vue'
const dvMainStore = dvMainStoreWithOut()
const { curComponent } = storeToRefs(dvMainStore)
const props = defineProps({
themes: {
type: String,
default: 'dark'
}
})
const { themes } = toRefs(props)
</script>
<template>
<div class="attr-list de-collapse-style">
<CommonAttr :themes="themes" :element="curComponent"></CommonAttr>
</div>
</template>
<style lang="less" scoped>
.content {
width: 100%;
font-size: 12px;
padding: 10px;
}
.de-collapse-style {
:deep(.ed-collapse-item__header) {
height: 34px !important;
line-height: 34px !important;
padding: 0 0 0 6px !important;
font-size: 12px !important;
font-weight: 400 !important;
}
:deep(.ed-collapse-item__content) {
padding: 16px !important;
}
:deep(.ed-form-item) {
display: block;
margin-bottom: 8px;
}
:deep(.ed-form-item__label) {
justify-content: flex-start;
}
}
:deep(.ed-textarea__inner) {
color: #ffffff;
background-color: #000000;
}
</style>

View File

@ -0,0 +1,197 @@
<script lang="ts" setup>
import { keycodes } from '@/utils/DeShortcutKey.js'
import eventBus from '@/utils/eventBus'
import { computed, nextTick, onBeforeUnmount, ref } from 'vue'
import { toRefs } from 'vue'
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
import { storeToRefs } from 'pinia'
const canEdit = ref(false)
const ctrlKey = ref(17)
const isCtrlDown = ref(false)
const emit = defineEmits(['input'])
const text = ref(null)
const props = defineProps({
propValue: {
type: String,
required: true,
default: ''
},
element: {
type: Object,
default() {
return {
id: null,
propValue: ''
}
}
}
})
const { element } = toRefs(props)
const dvMainStore = dvMainStoreWithOut()
const { editMode, curComponent } = storeToRefs(dvMainStore)
const onComponentClick = () => {
if (curComponent.value.id !== element.value.id) {
canEdit.value = false
}
}
const handleInput = e => {
emit('input', element.value, e.target.innerHTML)
}
const handleKeydown = e => {
//
canEdit.value && e.stopPropagation()
if (e.keyCode == ctrlKey.value) {
isCtrlDown.value = true
} else if (isCtrlDown.value && canEdit.value && keycodes.includes(e.keyCode)) {
e.stopPropagation()
} else if (e.keyCode == 46) {
// deleteKey
e.stopPropagation()
}
}
const handleKeyup = e => {
//
canEdit.value && e.stopPropagation()
if (e.keyCode == ctrlKey.value) {
isCtrlDown.value = false
}
}
const handleMousedown = e => {
if (canEdit.value) {
e.stopPropagation()
}
}
const clearStyle = e => {
e.preventDefault()
const clp = e.clipboardData
const text = clp.getData('text/plain') || ''
if (text !== '') {
document.execCommand('insertText', false, text)
}
emit('input', element.value, e.target.innerHTML)
}
const handleBlur = e => {
element.value.propValue = e.target.innerHTML || '&nbsp;'
const html = e.target.innerHTML
if (html !== '') {
element.value.propValue = e.target.innerHTML
} else {
element.value.propValue = ''
nextTick(function () {
element.value.propValue = '&nbsp;'
})
}
canEdit.value = false
}
const setEdit = () => {
if (element.value['isLock']) return
canEdit.value = true
//
selectText(text.value)
}
const selectText = element => {
const selection = window.getSelection()
const range = document.createRange()
range.selectNodeContents(element)
selection.removeAllRanges()
selection.addRange(range)
}
onBeforeUnmount(() => {
eventBus.off('componentClick', onComponentClick)
})
const varStyle = computed(() => [{ '--scroll-speed': `${element.value.style.scrollSpeed}s` }])
const textStyle = computed(() => {
return {
verticalAlign: element.value['style'].verticalAlign
}
})
</script>
<template>
<div
v-if="editMode == 'edit'"
:style="varStyle"
class="v-text"
@keydown="handleKeydown"
@keyup="handleKeyup"
>
<div
ref="text"
:contenteditable="canEdit"
:class="{ 'can-edit': canEdit, 'marquee-txt': !canEdit }"
tabindex="0"
:style="textStyle"
@dblclick="setEdit"
@paste="clearStyle"
@mousedown="handleMousedown"
@blur="handleBlur"
@input="handleInput"
v-html="element['propValue']"
></div>
</div>
<div v-else class="v-text preview">
<div class="marquee-txt" :style="textStyle" v-html="element['propValue']"></div>
</div>
</template>
<style lang="less" scoped>
.v-text {
width: 100%;
height: 100%;
display: table;
div {
display: table-cell;
width: 100%;
height: 100%;
outline: none;
word-break: break-all;
padding: 4px;
white-space: nowrap;
}
.can-edit {
cursor: text;
height: 100%;
}
}
.preview {
user-select: none;
}
.marquee {
margin-left: 100px;
width: 300px;
white-space: nowrap;
overflow: hidden;
border: 1px solid #4c7cee;
}
.marquee-txt {
display: inline-block;
padding-left: 100%; /* 从右至左开始滚动 */
animation: marqueeAnimation var(--scroll-speed) linear infinite;
}
@keyframes marqueeAnimation {
0% {
transform: translate(0, 0);
}
100% {
transform: translate(-100%, 0);
}
}
</style>

View File

@ -108,22 +108,11 @@ const isIndeterminate = ref(false)
const datasetTree = shallowRef([]) const datasetTree = shallowRef([])
const fields = ref<DatasetDetail[]>() const fields = ref<DatasetDetail[]>()
const parameters = ref([]) const parameters = ref([])
const parametersFilter = computed(() => {
return parameters.value.filter(ele => {
if (curComponent.value.displayType === '2') {
return [2, 3].includes(ele.deType)
}
if (curComponent.value.displayType === '7') {
return [1, 7].includes(ele.deType)
}
return ele.deType === +curComponent.value.displayType
})
})
const { queryElement } = toRefs(props) const { queryElement } = toRefs(props)
const getDetype = (id, arr) => { const getDetype = (id, arr) => {
return arr.find(ele => ele.id === id)?.deType return arr.flat().find(ele => ele.id === id)?.deType
} }
const visiblePopover = ref(false) const visiblePopover = ref(false)
const handleVisiblePopover = ev => { const handleVisiblePopover = ev => {
@ -142,11 +131,19 @@ const showTypeError = computed(() => {
if (!curComponent.value) return false if (!curComponent.value) return false
if (!curComponent.value.checkedFields?.length) return false if (!curComponent.value.checkedFields?.length) return false
if (!fields.value?.length) return false if (!fields.value?.length) return false
if (!!curComponent.value.parameters.length) {
const timeArr = curComponent.value.parameters.map(ele => ele.type[1])
if (timeArr.length !== new Set(timeArr).size) {
return true
}
}
let displayTypeField = null let displayTypeField = null
return curComponent.value.checkedFields.some(id => { return curComponent.value.checkedFields.some(id => {
const arr = fields.value.find(ele => ele.componentId === id) const arr = fields.value.find(ele => ele.componentId === id)
const checkId = curComponent.value.checkedFieldsMap?.[id] const checkId = curComponent.value.checkedFieldsMap?.[id]
const field = (arr?.list || []).find(ele => checkId === ele.id) const field = Object.values(arr?.fields || {})
.flat()
.find(ele => checkId === ele.id)
if (!field) return false if (!field) return false
if (displayTypeField === null) { if (displayTypeField === null) {
displayTypeField = field?.deType displayTypeField = field?.deType
@ -181,12 +178,32 @@ const handleCheckedFieldsChange = (value: string[]) => {
const inputCom = ref() const inputCom = ref()
const setParameters = () => {
const fieldArr = Object.values(curComponent.value.checkedFieldsMap).filter(ele => !!ele)
curComponent.value.parameters = fields.value
.map(ele => Object.values(ele?.fields || {}).flat())
.flat()
.filter(ele => fieldArr.includes(ele.id) && !!ele.variableName)
nextTick(() => {
if (isTimeParameter.value) {
curComponent.value.timeGranularity = typeTimeMap[curComponent.value.parameters[0].type[1]]
}
if (!!curComponent.value.parameters.length) {
curComponent.value.conditionType = 0
}
})
setType()
}
const setType = () => { const setType = () => {
if (curComponent.value.checkedFields?.length) { if (curComponent.value.checkedFields?.length) {
const [id] = curComponent.value.checkedFields const [id] = curComponent.value.checkedFields
const arr = fields.value.find(ele => ele.componentId === id) const arr = fields.value.find(ele => ele.componentId === id)
const checkId = curComponent.value.checkedFieldsMap?.[id] const checkId = curComponent.value.checkedFieldsMap?.[id]
const field = (arr?.list || []).find(ele => checkId === ele.id) const field = Object.values(arr?.fields || {})
.flat()
.find(ele => checkId === ele.id)
if (field?.deType !== undefined) { if (field?.deType !== undefined) {
let displayType = curComponent.value.displayType let displayType = curComponent.value.displayType
@ -220,6 +237,47 @@ const setTypeChange = () => {
}) })
} }
const isTimeParameter = computed(() => {
return curComponent.value.parameters?.some(ele => ele.deType === 1 && !!ele.variableName)
})
const timeList = [
{
label: '年',
value: 'year'
},
{
label: '年月',
value: 'month'
},
{
label: '年月日',
value: 'date'
},
{
label: '年月日时分秒',
value: 'datetime'
}
]
const typeTimeMap = {
'DATETIME-YEAR': 'year',
'YYYY-MM': 'month',
'YYYY/MM': 'month',
'YYYY-MM-DD': 'date',
'YYYY/MM/DD': 'date',
'YYYY-MM-DD HH:mm:ss': 'datetime',
'YYYY/MM/DD HH:mm:ss': 'datetime'
}
const timeParameterList = computed(() => {
if (!isTimeParameter.value) return timeList
const [_, y] = curComponent.value.parameters?.filter(
ele => ele.deType === 1 && !!ele.variableName
)[0].type
return timeList.filter(ele => ele.value === typeTimeMap[y])
})
const cancelClick = () => { const cancelClick = () => {
visiblePopover.value = false visiblePopover.value = false
dialogVisible.value = false dialogVisible.value = false
@ -542,7 +600,9 @@ const validate = () => {
ele.checkedFields.some(id => { ele.checkedFields.some(id => {
const arr = fields.value.find(itx => itx.componentId === id) const arr = fields.value.find(itx => itx.componentId === id)
const checkId = ele.checkedFieldsMap?.[id] const checkId = ele.checkedFieldsMap?.[id]
const field = (arr?.list || []).find(itx => checkId === itx.id) const field = Object.values(arr?.fields || {})
.flat()
.find(itx => checkId === itx.id)
if (!field) return false if (!field) return false
if (displayTypeField === null) { if (displayTypeField === null) {
displayTypeField = field?.deType displayTypeField = field?.deType
@ -642,14 +702,6 @@ const init = (queryId: string) => {
const datasetMapKeyList = Object.keys(datasetMap) const datasetMapKeyList = Object.keys(datasetMap)
nextTick(() => {
getSqlParams([
...new Set(datasetFieldList.value.map(ele => ele.tableId).filter(ele => !!ele))
]).then(res => {
parameters.value = res || []
})
})
if (datasetFieldIdList.every(ele => datasetMapKeyList.includes(ele))) { if (datasetFieldIdList.every(ele => datasetMapKeyList.includes(ele))) {
fields.value = datasetFieldList.value fields.value = datasetFieldList.value
.map(ele => { .map(ele => {
@ -660,13 +712,12 @@ const init = (queryId: string) => {
} }
const params = [...new Set(datasetFieldList.value.map(ele => ele.tableId).filter(ele => !!ele))] const params = [...new Set(datasetFieldList.value.map(ele => ele.tableId).filter(ele => !!ele))]
if (!params.length) return if (!params.length) return
getDsDetailsWithPerm(params) Promise.all([getDsDetailsWithPerm(params), getSqlParams(params)])
.then(res => { .then(([dq, p]) => {
res dq.filter(ele => !!ele).forEach(ele => {
.filter(ele => !!ele) ele.activelist = 'dimensionList'
.forEach(ele => { ele.fields.parameterList = p.filter(itx => itx.datasetGroupId === ele.id)
const { dimensionList, quotaList } = ele.fields ele.hasParameter = !!ele.fields.parameterList.length
ele.list = [...dimensionList, ...quotaList]
datasetMap[ele.id] = ele datasetMap[ele.id] = ele
}) })
fields.value = datasetFieldList.value fields.value = datasetFieldList.value
@ -1169,9 +1220,10 @@ defineExpose({
> >
<span class="dataset ellipsis">{{ field.name }}</span> <span class="dataset ellipsis">{{ field.name }}</span>
<el-select <el-select
@change="setType" @change="setParameters"
@focus="handleDialogClick" @focus="handleDialogClick"
style="margin-left: 12px" style="margin-left: 12px"
popper-class="field-select--dqp"
v-if="curComponent.checkedFields.includes(field.componentId)" v-if="curComponent.checkedFields.includes(field.componentId)"
v-model="curComponent.checkedFieldsMap[field.componentId]" v-model="curComponent.checkedFieldsMap[field.componentId]"
clearable clearable
@ -1181,21 +1233,38 @@ defineExpose({
<Icon <Icon
:name="`field_${ :name="`field_${
fieldType[ fieldType[
getDetype(curComponent.checkedFieldsMap[field.componentId], field.list) getDetype(
curComponent.checkedFieldsMap[field.componentId],
Object.values(field.fields)
)
] ]
}`" }`"
:className="`field-icon-${ :className="`field-icon-${
fieldType[ fieldType[
getDetype(curComponent.checkedFieldsMap[field.componentId], field.list) getDetype(
curComponent.checkedFieldsMap[field.componentId],
Object.values(field.fields)
)
] ]
}`" }`"
></Icon> ></Icon>
</el-icon> </el-icon>
</template> </template>
<template #header>
<el-tabs stretch class="params-select--header" v-model="field.activelist">
<el-tab-pane label="维度" name="dimensionList"></el-tab-pane>
<el-tab-pane label="指标" name="quotaList"></el-tab-pane>
<el-tab-pane
v-if="field.hasParameter"
label="参数"
name="parameterList"
></el-tab-pane>
</el-tabs>
</template>
<el-option <el-option
v-for="ele in field.list" v-for="ele in field.fields[field.activelist]"
:key="ele.id" :key="ele.id"
:label="ele.name" :label="ele.name || ele.variableName"
:value="ele.id" :value="ele.id"
:disabled="ele.desensitized" :disabled="ele.desensitized"
> >
@ -1209,8 +1278,8 @@ defineExpose({
:className="`field-icon-${fieldType[ele.deType]}`" :className="`field-icon-${fieldType[ele.deType]}`"
></Icon> ></Icon>
</el-icon> </el-icon>
<span> <span :title="ele.name || ele.variableName" class="ellipsis">
{{ ele.name }} {{ ele.name || ele.variableName }}
</span> </span>
</div> </div>
</el-option> </el-option>
@ -1276,7 +1345,7 @@ defineExpose({
<div class="list-item" v-if="['1', '7'].includes(curComponent.displayType)"> <div class="list-item" v-if="['1', '7'].includes(curComponent.displayType)">
<div class="label">时间粒度</div> <div class="label">时间粒度</div>
<div class="value"> <div class="value">
<template v-if="curComponent.displayType === '7'"> <template v-if="curComponent.displayType === '7' && !isTimeParameter">
<el-select <el-select
@change="timeGranularityMultipleChange" @change="timeGranularityMultipleChange"
placeholder="请选择时间粒度" placeholder="请选择时间粒度"
@ -1295,10 +1364,12 @@ defineExpose({
placeholder="请选择时间粒度" placeholder="请选择时间粒度"
v-model="curComponent.timeGranularity" v-model="curComponent.timeGranularity"
> >
<el-option label="年" value="year" /> <el-option
<el-option label="年月" value="month" /> v-for="ele in timeParameterList"
<el-option label="年月日" value="date" /> :key="ele.value"
<el-option label="年月日时分秒" value="datetime" /> :label="ele.label"
:value="ele.value"
/>
</el-select> </el-select>
</template> </template>
</div> </div>
@ -1571,8 +1642,12 @@ defineExpose({
<div class="value"> <div class="value">
<el-radio-group class="larger-radio" v-model="curComponent.conditionType"> <el-radio-group class="larger-radio" v-model="curComponent.conditionType">
<el-radio :label="0">单条件</el-radio> <el-radio :label="0">单条件</el-radio>
<el-radio :label="1">与条件</el-radio> <el-radio :label="1" :disabled="!!curComponent.parameters.length"
<el-radio :label="2">或条件</el-radio> >与条件</el-radio
>
<el-radio :label="2" :disabled="!!curComponent.parameters.length"
>或条件</el-radio
>
</el-radio-group> </el-radio-group>
</div> </div>
</div> </div>
@ -1698,95 +1773,6 @@ defineExpose({
</el-tooltip> </el-tooltip>
</div> </div>
</div> </div>
<div v-if="!['8'].includes(curComponent.displayType)" class="list-item">
<div class="label">
<el-tooltip
effect="dark"
content="空数据不支持传参数"
:disabled="!curComponent.showEmpty"
placement="top"
>
<el-checkbox
:disabled="curComponent.showEmpty"
v-model="curComponent.parametersCheck"
label="绑定参数"
/>
</el-tooltip>
</div>
<template v-if="curComponent.parametersCheck">
<div v-if="curComponent.displayType !== '7'" class="parameters">
<el-select
popper-class="dataset-parameters"
value-key="id"
multiple
collapse-tags
collapse-tags-tooltip
:max-collapse-tags="3"
@focus="handleDialogClick"
v-model="curComponent.parameters"
clearable
>
<el-option
v-for="item in parametersFilter"
:key="item.id"
:label="item.variableName"
:value="item"
>
<div class="variable-name ellipsis">{{ item.variableName }}</div>
<el-tooltip effect="dark" :content="item.datasetFullName" placement="top">
<div class="dataset-full-name ellipsis">{{ item.datasetFullName }}</div>
</el-tooltip>
</el-option>
</el-select>
</div>
<div v-else class="parameters-range">
<div class="range-title">开始时间</div>
<div class="range-title">结束时间</div>
<div class="params-start">
<el-select
popper-class="dataset-parameters"
value-key="id"
@focus="handleDialogClick"
v-model="curComponent.parametersStart"
clearable
>
<el-option
v-for="item in parametersFilter"
:key="item.id"
:label="item.variableName"
:value="item"
>
<div class="variable-name ellipsis">{{ item.variableName }}</div>
<el-tooltip effect="dark" :content="item.datasetFullName" placement="top">
<div class="dataset-full-name ellipsis">{{ item.datasetFullName }}</div>
</el-tooltip>
</el-option>
</el-select>
</div>
<div class="params-end">
<el-select
popper-class="dataset-parameters"
value-key="id"
@focus="handleDialogClick"
v-model="curComponent.parametersEnd"
clearable
>
<el-option
v-for="item in parametersFilter"
:key="item.id"
:label="item.variableName"
:value="item"
>
<div class="variable-name ellipsis">{{ item.variableName }}</div>
<el-tooltip effect="dark" :content="item.datasetFullName" placement="top">
<div class="dataset-full-name ellipsis">{{ item.datasetFullName }}</div>
</el-tooltip>
</el-option>
</el-select>
</div>
</div>
</template>
</div>
<div v-if="!['8'].includes(curComponent.displayType)" class="list-item"> <div v-if="!['8'].includes(curComponent.displayType)" class="list-item">
<div class="label"> <div class="label">
<el-checkbox v-model="curComponent.defaultValueCheck" label="设置默认值" /> <el-checkbox v-model="curComponent.defaultValueCheck" label="设置默认值" />
@ -1963,6 +1949,19 @@ defineExpose({
</template> </template>
<style lang="less"> <style lang="less">
.field-select--dqp {
width: 210px;
}
.ed-select-dropdown__header {
padding: 0 8px;
.params-select--header {
--ed-tabs-header-height: 32px;
.ed-tabs__item {
font-weight: 400;
font-size: 15px;
}
}
}
.condition-value-select-popper { .condition-value-select-popper {
.ed-select-dropdown__item.selected::after { .ed-select-dropdown__item.selected::after {
display: none; display: none;

View File

@ -180,7 +180,7 @@ const handleFieldIdChange = (val: EnumValue) => {
oldArr = [...selectValue.value] oldArr = [...selectValue.value]
oldEnumValueArr = setOldMapValue(oldArr) oldEnumValueArr = setOldMapValue(oldArr)
} }
enumValueArr = [...res, ...oldEnumValueArr] || [] enumValueArr = [...(res || []), ...oldEnumValueArr] || []
options.value = [ options.value = [
...new Set( ...new Set(
(res || []) (res || [])

View File

@ -204,7 +204,6 @@ export const searchQuery = (queryComponentList, filter, curComponentId, firstLoa
defaultMapValue, defaultMapValue,
mapValue, mapValue,
parameters = [], parameters = [],
parametersCheck = false,
isTree = false, isTree = false,
timeGranularity = 'date', timeGranularity = 'date',
displayType, displayType,
@ -307,16 +306,7 @@ export const searchQuery = (queryComponentList, filter, curComponentId, firstLoa
fieldId: item.checkedFieldsMap[curComponentId], fieldId: item.checkedFieldsMap[curComponentId],
operator, operator,
value: result, value: result,
parameters: parametersCheck parameters,
? +displayType === 7
? [
parametersStart,
parametersEnd?.id
? { ...parametersEnd, id: `${parametersEnd.id}_START_END_SPLIT` }
: parametersEnd
]
: parameters
: [],
isTree isTree
}) })
} }

View File

@ -25,6 +25,9 @@ interface LinkItem {
method?: string method?: string
} }
const linkList = ref([{ id: 5, label: t('common.about'), method: 'toAbout' }] as LinkItem[]) const linkList = ref([{ id: 5, label: t('common.about'), method: 'toAbout' }] as LinkItem[])
if (!appearanceStore.getShowAbout) {
linkList.value.splice(0, 1)
}
const inPlatformClient = computed(() => !!wsCache.get('de-platform-client')) const inPlatformClient = computed(() => !!wsCache.get('de-platform-client'))
@ -118,7 +121,7 @@ if (uid.value === '1') {
<el-popover <el-popover
ref="popoverRef" ref="popoverRef"
:virtual-ref="buttonRef" :virtual-ref="buttonRef"
trigger="hover" trigger="click"
title="" title=""
virtual-triggering virtual-triggering
placement="bottom-start" placement="bottom-start"

View File

@ -19,6 +19,7 @@ import AiComponent from '@/layout/components/AiComponent.vue'
import { findBaseParams } from '@/api/aiComponent' import { findBaseParams } from '@/api/aiComponent'
import ExportExcel from '@/views/visualized/data/dataset/ExportExcel.vue' import ExportExcel from '@/views/visualized/data/dataset/ExportExcel.vue'
import AiTips from '@/layout/components/AiTips.vue' import AiTips from '@/layout/components/AiTips.vue'
const appearanceStore = useAppearanceStoreWithOut() const appearanceStore = useAppearanceStoreWithOut()
const { push } = useRouter() const { push } = useRouter()
const route = useRoute() const route = useRoute()
@ -40,6 +41,7 @@ const activeIndex = computed(() => {
} }
return route.path return route.path
}) })
const permissionStore = usePermissionStore() const permissionStore = usePermissionStore()
const ExportExcelRef = ref() const ExportExcelRef = ref()
const downloadClick = () => { const downloadClick = () => {
@ -117,19 +119,30 @@ onMounted(() => {
</el-menu> </el-menu>
<div class="operate-setting" v-if="!desktop"> <div class="operate-setting" v-if="!desktop">
<XpackComponent jsname="c3dpdGNoZXI=" /> <XpackComponent jsname="c3dpdGNoZXI=" />
<el-icon style="margin: 0 10px" class="ai-icon" v-if="aiBaseUrl && !showOverlay"> <el-icon
style="margin: 0 10px"
class="ai-icon"
v-if="aiBaseUrl && !showOverlay && appearanceStore.getShowAi"
>
<Icon name="dv-ai" @click="handleAiClick" /> <Icon name="dv-ai" @click="handleAiClick" />
</el-icon> </el-icon>
<el-icon style="margin: 0 10px"> <el-icon style="margin: 0 10px">
<Icon name="dv-preview-download" @click="downloadClick" /> <Icon name="dv-preview-download" @click="downloadClick" />
</el-icon> </el-icon>
<ai-tips @confirm="aiTipsConfirm" v-if="showOverlay" class="ai-icon-tips"></ai-tips> <ai-tips
@confirm="aiTipsConfirm"
v-if="showOverlay && appearanceStore.getShowAi"
class="ai-icon-tips"
/>
<ToolboxCfg v-if="showToolbox" /> <ToolboxCfg v-if="showToolbox" />
<TopDoc /> <TopDoc v-if="appearanceStore.getShowDoc" />
<SystemCfg v-if="showSystem" /> <SystemCfg v-if="showSystem" />
<AccountOperator /> <AccountOperator />
<ai-component v-if="aiBaseUrl" :base-url="aiBaseUrl"></ai-component> <ai-component
<div v-if="showOverlay" class="overlay"></div> v-if="aiBaseUrl && appearanceStore.getShowAi"
:base-url="aiBaseUrl"
></ai-component>
<div v-if="showOverlay && appearanceStore.getShowAi" class="overlay"></div>
</div> </div>
</el-header> </el-header>
<ExportExcel ref="ExportExcelRef"></ExportExcel> <ExportExcel ref="ExportExcelRef"></ExportExcel>

View File

@ -2,8 +2,10 @@
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { useAppearanceStoreWithOut } from '@/store/modules/appearance' import { useAppearanceStoreWithOut } from '@/store/modules/appearance'
import { computed } from 'vue' import { computed } from 'vue'
const appearanceStore = useAppearanceStoreWithOut() const appearanceStore = useAppearanceStoreWithOut()
const navigateBg = computed(() => appearanceStore.getNavigateBg) const navigateBg = computed(() => appearanceStore.getNavigateBg)
const showDoc = computed(() => appearanceStore.getShowDoc)
const { push, resolve } = useRouter() const { push, resolve } = useRouter()
const redirectUser = () => { const redirectUser = () => {
const sysMenu = resolve('/system') const sysMenu = resolve('/system')
@ -14,7 +16,13 @@ const redirectUser = () => {
<template> <template>
<el-tooltip class="box-item" effect="dark" content="组织管理中心" placement="top"> <el-tooltip class="box-item" effect="dark" content="组织管理中心" placement="top">
<div class="sys-setting" :class="{ 'is-light-setting': navigateBg && navigateBg === 'light' }"> <div
class="sys-setting"
:class="{
'is-light-setting': navigateBg && navigateBg === 'light',
'in-iframe-setting': !showDoc
}"
>
<el-icon @click="redirectUser"> <el-icon @click="redirectUser">
<Icon class="icon-setting" name="icon-setting" /> <Icon class="icon-setting" name="icon-setting" />
</el-icon> </el-icon>
@ -35,6 +43,9 @@ const redirectUser = () => {
background-color: #1e2738; background-color: #1e2738;
} }
} }
.in-iframe-setting {
margin-left: 10px !important;
}
.is-light-setting { .is-light-setting {
&:hover { &:hover {
background-color: var(--ed-menu-hover-bg-color) !important; background-color: var(--ed-menu-hover-bg-color) !important;

View File

@ -2170,7 +2170,7 @@ export default {
link_add_tips_pre: '请在右侧配置网页信息..', link_add_tips_pre: '请在右侧配置网页信息..',
web_add_tips_suf: '添加网页信息...', web_add_tips_suf: '添加网页信息...',
panel_view_result_show: '图表结果', panel_view_result_show: '图表结果',
panel_view_result_tips: '选择仪表板会覆盖图表的结果展示数量取值范围1~10000', panel_view_result_tips: '选择{0}会覆盖图表的结果展示数量取值范围1~10000',
timeout_refresh: '请求超时稍后刷新...', timeout_refresh: '请求超时稍后刷新...',
mobile_layout: '移动端布局', mobile_layout: '移动端布局',
component_hidden: '隐藏的组件', component_hidden: '隐藏的组件',

View File

@ -232,7 +232,7 @@ declare interface ChartBasicStyle {
/** /**
* 对称柱状图方向 * 对称柱状图方向
*/ */
layout: 'horizontal' | 'vertical' layout?: 'horizontal' | 'vertical'
} }
/** /**
* 表头属性 * 表头属性
@ -482,6 +482,10 @@ declare interface ChartMiscAttr {
* 地图线条宽度 * 地图线条宽度
*/ */
mapLineWidth: number mapLineWidth: number
/**
* 流向地图动画
*/
mapLineAnimate?: boolean
/** /**
* 流向地图动画间隔 * 流向地图动画间隔
*/ */
@ -786,6 +790,10 @@ declare interface ChartIndicatorStyle {
* 字体颜色 * 字体颜色
*/ */
color: string color: string
/**
* 背景颜色
*/
backgroundColor: string
/** /**
* 水平位置 * 水平位置
*/ */

View File

@ -123,6 +123,10 @@ declare interface ChartThreshold {
* 仪表盘阈值: x,y,z * 仪表盘阈值: x,y,z
*/ */
gaugeThreshold: string gaugeThreshold: string
/**
* 水波图阈值: x,y,z
*/
liquidThreshold: string
/** /**
* 指标卡阈值 * 指标卡阈值
*/ */

View File

@ -117,6 +117,14 @@ declare interface Axis extends ChartViewField {
* 维度/指标分组类型 * 维度/指标分组类型
*/ */
groupType: 'q' | 'd' groupType: 'q' | 'd'
/**
* 排序规则
*/
sort: 'asc' | 'desc' | 'none' | 'custom_sort'
/**
* 自定义排序项
*/
customSort: string[]
} }
declare interface ChartViewField { declare interface ChartViewField {
/** /**

View File

@ -1,6 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { shallowRef, defineAsyncComponent } from 'vue' import { shallowRef, defineAsyncComponent } from 'vue'
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
import { useEmitt } from '@/hooks/web/useEmitt'
const VisualizationEditor = defineAsyncComponent( const VisualizationEditor = defineAsyncComponent(
() => import('@/views/data-visualization/index.vue') () => import('@/views/data-visualization/index.vue')
@ -33,6 +34,15 @@ const componentMap = {
DashboardPanel DashboardPanel
} }
const changeCurrentComponent = val => {
currentComponent.value = componentMap[val]
}
useEmitt({
name: 'changeCurrentComponent',
callback: changeCurrentComponent
})
currentComponent.value = componentMap[props.componentName] currentComponent.value = componentMap[props.componentName]
</script> </script>
<template> <template>

View File

@ -47,7 +47,9 @@ onBeforeMount(async () => {
// div // div
if (embeddedStore.outerParams) { if (embeddedStore.outerParams) {
try { try {
attachParam = JSON.parse(embeddedStore.outerParams) const outerPramsParse = JSON.parse(embeddedStore.outerParams)
attachParam = outerPramsParse.attachParam
dvMainStore.setEmbeddedCallBack(outerPramsParse.callBackFlag || 'no')
} catch (e) { } catch (e) {
console.error(e) console.error(e)
ElMessage.error(t('visualization.outer_param_decode_error')) ElMessage.error(t('visualization.outer_param_decode_error'))

View File

@ -66,7 +66,9 @@ onBeforeMount(async () => {
// div // div
if (embeddedStore.outerParams) { if (embeddedStore.outerParams) {
try { try {
attachParam = JSON.parse(embeddedStore.outerParams) const outerPramsParse = JSON.parse(embeddedStore.outerParams)
attachParam = outerPramsParse.attachParam
dvMainStore.setEmbeddedCallBack(outerPramsParse.callBackFlag || 'no')
} catch (e) { } catch (e) {
console.error(e) console.error(e)
ElMessage.error(t('visualization.outer_param_decode_error')) ElMessage.error(t('visualization.outer_param_decode_error'))

View File

@ -6,12 +6,16 @@ import colorFunctions from 'less/lib/less/functions/color.js'
import colorTree from 'less/lib/less/tree/color.js' import colorTree from 'less/lib/less/tree/color.js'
const basePath = import.meta.env.VITE_API_BASEPATH const basePath = import.meta.env.VITE_API_BASEPATH
const baseUrl = basePath + '/appearance/image/' const baseUrl = basePath + '/appearance/image/'
import { isBtnShow } from '@/utils/utils'
interface AppearanceState { interface AppearanceState {
themeColor?: string themeColor?: string
customColor?: string customColor?: string
navigateBg?: string navigateBg?: string
navigate?: string navigate?: string
help?: string help?: string
showAi?: string
showDoc?: string
showAbout?: string
bg?: string bg?: string
login?: string login?: string
slogan?: string slogan?: string
@ -33,6 +37,9 @@ export const useAppearanceStore = defineStore('appearanceStore', {
navigateBg: '', navigateBg: '',
navigate: '', navigate: '',
help: '', help: '',
showDoc: '0',
showAi: '0',
showAbout: '0',
bg: '', bg: '',
login: '', login: '',
slogan: '', slogan: '',
@ -106,6 +113,15 @@ export const useAppearanceStore = defineStore('appearanceStore', {
}, },
getCommunity(): boolean { getCommunity(): boolean {
return this.community return this.community
},
getShowAi(): boolean {
return isBtnShow(this.showAi)
},
getShowDoc(): boolean {
return isBtnShow(this.showDoc)
},
getShowAbout(): boolean {
return isBtnShow(this.showAbout)
} }
}, },
actions: { actions: {
@ -160,6 +176,9 @@ export const useAppearanceStore = defineStore('appearanceStore', {
} }
this.navigate = data.navigate this.navigate = data.navigate
this.help = data.help this.help = data.help
this.showAi = data.showAi
this.showDoc = data.showDoc
this.showAbout = data.showAbout
this.navigateBg = data.navigateBg this.navigateBg = data.navigateBg
this.themeColor = data.themeColor this.themeColor = data.themeColor
this.customColor = data.customColor this.customColor = data.customColor

View File

@ -32,6 +32,7 @@ export const dvMainStore = defineStore('dataVisualization', {
chartAreaCollapse: false, chartAreaCollapse: false,
datasetAreaCollapse: false datasetAreaCollapse: false
}, },
embeddedCallBack: 'no', // 嵌入模式是否允许反馈参数
editMode: 'edit', // 编辑器模式 edit preview editMode: 'edit', // 编辑器模式 edit preview
mobileInPc: false, mobileInPc: false,
firstLoadMap: [], firstLoadMap: [],
@ -173,6 +174,9 @@ export const dvMainStore = defineStore('dataVisualization', {
} }
}, },
actions: { actions: {
setEmbeddedCallBack(value) {
this.embeddedCallBack = value
},
setPublicLinkStatus(value) { setPublicLinkStatus(value) {
this.publicLinkStatus = value this.publicLinkStatus = value
}, },

View File

@ -1,5 +1,6 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { store } from '../index' import { store } from '../index'
import { clear } from '@/api/sync/syncTaskLog'
interface AppState { interface AppState {
type: string type: string
token: string token: string
@ -10,6 +11,9 @@ interface AppState {
pid: string pid: string
chartId: string chartId: string
resourceId: string resourceId: string
opt: string
createType: string
templateParams: string
} }
export const userStore = defineStore('embedded', { export const userStore = defineStore('embedded', {
@ -23,13 +27,22 @@ export const userStore = defineStore('embedded', {
dvId: '', dvId: '',
pid: '', pid: '',
chartId: '', chartId: '',
resourceId: '' resourceId: '',
opt: '',
createType: '',
templateParams: ''
} }
}, },
getters: { getters: {
getType(): string { getType(): string {
return this.type return this.type
}, },
getCreateType(): string {
return this.createType
},
getTemplateParams(): string {
return this.templateParams
},
getToken(): string { getToken(): string {
return this.token return this.token
}, },
@ -54,6 +67,9 @@ export const userStore = defineStore('embedded', {
getResourceId(): string { getResourceId(): string {
return this.resourceId return this.resourceId
}, },
getOpt(): string {
return this.opt
},
getIframeData(): any { getIframeData(): any {
return { return {
embeddedToken: this.token, embeddedToken: this.token,
@ -71,6 +87,12 @@ export const userStore = defineStore('embedded', {
setType(type: string) { setType(type: string) {
this.type = type this.type = type
}, },
setCreateType(createType: string) {
this.createType = createType
},
setTemplateParams(templateParams: string) {
this.templateParams = templateParams
},
setToken(token: string) { setToken(token: string) {
this.token = token this.token = token
}, },
@ -95,6 +117,9 @@ export const userStore = defineStore('embedded', {
setResourceId(resourceId: string) { setResourceId(resourceId: string) {
this.resourceId = resourceId this.resourceId = resourceId
}, },
setOpt(opt: string) {
this.opt = opt
},
setIframeData(data: any) { setIframeData(data: any) {
this.type = data['type'] this.type = data['type']
this.token = data['embeddedToken'] this.token = data['embeddedToken']
@ -104,6 +129,14 @@ export const userStore = defineStore('embedded', {
this.chartId = data['chartId'] this.chartId = data['chartId']
this.pid = data['pid'] this.pid = data['pid']
this.resourceId = data['resourceId'] this.resourceId = data['resourceId']
},
clearState() {
this.setPid('')
this.setOpt('')
this.setCreateType('')
this.setTemplateParams('')
this.setResourceId('')
this.setDvId('')
} }
} }
}) })

View File

@ -174,17 +174,30 @@ function move(keyCode) {
if (curComponent.value) { if (curComponent.value) {
if (keyCode === leftKey) { if (keyCode === leftKey) {
curComponent.value.style.left = --curComponent.value.style.left curComponent.value.style.left = --curComponent.value.style.left
groupAreaAdaptor(-1, 0)
} else if (keyCode === rightKey) { } else if (keyCode === rightKey) {
curComponent.value.style.left = ++curComponent.value.style.left curComponent.value.style.left = ++curComponent.value.style.left
groupAreaAdaptor(1, 0)
} else if (keyCode === upKey) { } else if (keyCode === upKey) {
curComponent.value.style.top = --curComponent.value.style.top curComponent.value.style.top = --curComponent.value.style.top
groupAreaAdaptor(0, -1)
} else if (keyCode === downKey) { } else if (keyCode === downKey) {
curComponent.value.style.top = ++curComponent.value.style.top curComponent.value.style.top = ++curComponent.value.style.top
groupAreaAdaptor(0, 1)
} }
snapshotStore.recordSnapshotCache('key-move') snapshotStore.recordSnapshotCache('key-move')
} }
} }
function groupAreaAdaptor(leftOffset = 0, topOffset = 0) {
if (curComponent.value.component === 'GroupArea') {
composeStore.areaData.components.forEach(component => {
component.style.top = component.style.top + topOffset
component.style.left = component.style.left + leftOffset
})
}
}
function cut() { function cut() {
copyStore.cut() copyStore.cut()
} }

View File

@ -7,7 +7,7 @@ import { groupSizeStyleAdaptor } from '@/utils/style'
const dvMainStore = dvMainStoreWithOut() const dvMainStore = dvMainStoreWithOut()
const { componentData, curComponentIndex, canvasStyleData } = storeToRefs(dvMainStore) const { componentData, curComponentIndex, canvasStyleData } = storeToRefs(dvMainStore)
const needToChangeAttrs = ['top', 'left', 'width', 'height', 'fontSize'] const needToChangeAttrs = ['top', 'left', 'width', 'height', 'fontSize', 'letterSpacing']
export function changeSizeWithScale(scale) { export function changeSizeWithScale(scale) {
return changeComponentsSizeWithScale(scale) return changeComponentsSizeWithScale(scale)

View File

@ -31,6 +31,8 @@ import DeVideo from '@/custom-component/de-video/Component.vue'
import DeVideoAttr from '@/custom-component/de-video/Attr.vue' import DeVideoAttr from '@/custom-component/de-video/Attr.vue'
import DeStreamMedia from '@/custom-component/de-stream-media/Component.vue' import DeStreamMedia from '@/custom-component/de-stream-media/Component.vue'
import DeStreamMediaAttr from '@/custom-component/de-stream-media/Attr.vue' import DeStreamMediaAttr from '@/custom-component/de-stream-media/Attr.vue'
import ScrollText from '@/custom-component/scroll-text/Component.vue'
import ScrollTextAttr from '@/custom-component/scroll-text/Attr.vue'
export const componentsMap = { export const componentsMap = {
VText: VText, VText: VText,
VQuery, VQuery,
@ -64,7 +66,9 @@ export const componentsMap = {
DeVideo: DeVideo, DeVideo: DeVideo,
DeVideoAttr: DeVideoAttr, DeVideoAttr: DeVideoAttr,
DeStreamMedia: DeStreamMedia, DeStreamMedia: DeStreamMedia,
DeStreamMediaAttr: DeStreamMediaAttr DeStreamMediaAttr: DeStreamMediaAttr,
ScrollText: ScrollText,
ScrollTextAttr: ScrollTextAttr
} }
export default function findComponent(key) { export default function findComponent(key) {

View File

@ -6,6 +6,7 @@ import { storeToRefs } from 'pinia'
import { findResourceAsBase64 } from '@/api/staticResource' import { findResourceAsBase64 } from '@/api/staticResource'
import FileSaver from 'file-saver' import FileSaver from 'file-saver'
import { deepCopy } from '@/utils/utils' import { deepCopy } from '@/utils/utils'
import { toPng } from 'html-to-image'
const embeddedStore = useEmbedded() const embeddedStore = useEmbedded()
const dvMainStore = dvMainStoreWithOut() const dvMainStore = dvMainStoreWithOut()
const { canvasStyleData, componentData, canvasViewInfo, canvasViewDataInfo, dvInfo } = const { canvasStyleData, componentData, canvasViewInfo, canvasViewDataInfo, dvInfo } =
@ -70,6 +71,35 @@ export function download2AppTemplate(downloadType, canvasDom, name, callBack?) {
} }
} }
export function downloadCanvas2(type, canvasDom, name, callBack?) {
toPng(canvasDom)
.then(dataUrl => {
const a = document.createElement('a')
a.setAttribute('download', name)
a.href = dataUrl
if (type === 'img') {
const a = document.createElement('a')
a.setAttribute('download', name)
a.href = dataUrl
a.click()
document.body.removeChild(a)
} else {
const contentWidth = canvasDom.offsetWidth
const contentHeight = canvasDom.offsetHeight
const lp = contentWidth > contentHeight ? 'l' : 'p'
const PDF = new JsPDF(lp, 'pt', [contentWidth, contentHeight])
PDF.addImage(dataUrl, 'PNG', 0, 0, contentWidth, contentHeight)
PDF.save(name + '.pdf')
}
if (callBack) {
callBack()
}
})
.catch(error => {
console.error('oops, something went wrong!', error)
})
}
export function downloadCanvas(type, canvasDom, name, callBack?) { export function downloadCanvas(type, canvasDom, name, callBack?) {
// const canvasDom = document.getElementById(canvasId) // const canvasDom = document.getElementById(canvasId)
if (canvasDom) { if (canvasDom) {

View File

@ -106,7 +106,24 @@ export const cleanPlatformFlag = () => {
wsCache.delete(platformKey) wsCache.delete(platformKey)
return false return false
} }
export const isInIframe = () => {
try {
return window.top !== window.self
} catch (error) {
console.error(error)
return true
}
}
export const isBtnShow = (val: string) => {
if (!val || val === '0') {
return true
} else if (val === '1') {
return false
} else {
return !isInIframe()
}
}
export function isMobile() { export function isMobile() {
return ( return (
navigator.userAgent.match( navigator.userAgent.match(

View File

@ -138,7 +138,9 @@ const canvasInit = (isFistLoad = true) => {
} }
// afterInit // afterInit
dvMainStore.setDataPrepareState(true) dvMainStore.setDataPrepareState(true)
isFistLoad && snapshotStore.recordSnapshotCache('renderChart') if (isMainCanvas(canvasId.value.id) && isFistLoad) {
snapshotStore.recordSnapshotCache('renderChart')
}
}, 500) }, 500)
} }

View File

@ -61,10 +61,10 @@ const init = () => {
const changeThreshold = () => { const changeThreshold = () => {
emit('onThresholdChange', state.thresholdForm) emit('onThresholdChange', state.thresholdForm)
} }
const gaugeThresholdChange = () => { const changeSplitThreshold = (threshold: string) => {
// check input // check input
if (state.thresholdForm.gaugeThreshold) { if (threshold) {
const arr = state.thresholdForm.gaugeThreshold.split(',') const arr = threshold.split(',')
for (let i = 0; i < arr.length; i++) { for (let i = 0; i < arr.length; i++) {
const ele = arr[i] const ele = arr[i]
if (parseFloat(ele).toString() === 'NaN' || parseFloat(ele) <= 0 || parseFloat(ele) >= 100) { if (parseFloat(ele).toString() === 'NaN' || parseFloat(ele) <= 0 || parseFloat(ele) >= 100) {
@ -246,7 +246,7 @@ init()
style="width: 100px; margin: 0 10px" style="width: 100px; margin: 0 10px"
size="small" size="small"
clearable clearable
@change="gaugeThresholdChange" @change="changeSplitThreshold"
/> />
<span>,100</span> <span>,100</span>
<el-tooltip effect="dark" placement="bottom"> <el-tooltip effect="dark" placement="bottom">
@ -260,6 +260,36 @@ init()
</el-form-item> </el-form-item>
</el-form> </el-form>
</el-col> </el-col>
<el-col v-show="showProperty('liquidThreshold')">
<el-form ref="thresholdForm" :model="state.thresholdForm" label-position="top">
<el-form-item
:label="t('chart.threshold_range') + '(%)'"
class="form-item"
label-width="auto"
>
<span>0,</span>
<el-input
:effect="themes"
:placeholder="t('chart.threshold_range')"
:disabled="!state.thresholdForm.enable"
v-model="state.thresholdForm.liquidThreshold"
style="width: 100px; margin: 0 10px"
size="small"
clearable
@change="changeSplitThreshold"
/>
<span>,100</span>
<el-tooltip effect="dark" placement="bottom">
<el-icon style="margin-left: 10px"><InfoFilled /></el-icon>
<template #content>
阈值设置决定水波图颜色为空则不开启阈值范围(0-100)逐级递增
<br />
例如输入 30,70表示分为3段分别为[0,30],(30,70],(70,100]
</template>
</el-tooltip>
</el-form-item>
</el-form>
</el-col>
<!--文本卡--> <!--文本卡-->
<el-col v-if="props.chart.type && props.chart.type === 'label'"> <el-col v-if="props.chart.type && props.chart.type === 'label'">
@ -409,14 +439,14 @@ init()
class="color-div" class="color-div"
:class="{ 'color-div-dark': themes === 'dark' }" :class="{ 'color-div-dark': themes === 'dark' }"
></div> ></div>
<!-- <div <div
:title="t('chart.backgroundColor')" :title="t('chart.backgroundColor')"
:style="{ :style="{
backgroundColor: item.backgroundColor backgroundColor: item.backgroundColor
}" }"
class="color-div" class="color-div"
:class="{ 'color-div-dark': themes === 'dark' }" :class="{ 'color-div-dark': themes === 'dark' }"
></div>--> ></div>
</div> </div>
</div> </div>
</el-col> </el-col>

View File

@ -24,6 +24,7 @@ const thresholdObj = {
field: '0', field: '0',
value: '0', value: '0',
color: '#ff0000ff', color: '#ff0000ff',
backgroundColor: '#fff',
min: '0', min: '0',
max: '1' max: '1'
} }
@ -175,7 +176,7 @@ init()
@change="changeThreshold" @change="changeThreshold"
/> />
</div> </div>
<!-- <div style="display: flex; align-items: center; justify-content: center; margin-left: 8px"> <div style="display: flex; align-items: center; justify-content: center; margin-left: 8px">
<div class="color-title">{{ t('chart.backgroundColor') }}</div> <div class="color-title">{{ t('chart.backgroundColor') }}</div>
<el-color-picker <el-color-picker
is-custom is-custom
@ -186,7 +187,7 @@ init()
:predefine="predefineColors" :predefine="predefineColors"
@change="changeThreshold" @change="changeThreshold"
/> />
</div>--> </div>
<div style="display: flex; align-items: center; justify-content: center; margin-left: 8px"> <div style="display: flex; align-items: center; justify-content: center; margin-left: 8px">
<el-button <el-button
class="circle-button" class="circle-button"

View File

@ -221,6 +221,7 @@ watch(
:themes="themes" :themes="themes"
:chart="chart" :chart="chart"
@onBasicStyleChange="onBasicStyleChange" @onBasicStyleChange="onBasicStyleChange"
@onMiscChange="onMiscChange"
/> />
</el-collapse-item> </el-collapse-item>
<collapse-switch-item <collapse-switch-item

View File

@ -1,6 +1,10 @@
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, PropType, reactive, watch } from 'vue' import { onMounted, PropType, reactive, watch } from 'vue'
import { COLOR_PANEL, DEFAULT_BASIC_STYLE } from '@/views/chart/components/editor/util/chart' import {
COLOR_PANEL,
DEFAULT_BASIC_STYLE,
DEFAULT_MISC
} from '@/views/chart/components/editor/util/chart'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import CustomColorStyleSelect from '@/views/chart/components/editor/editor-style/components/CustomColorStyleSelect.vue' import CustomColorStyleSelect from '@/views/chart/components/editor/editor-style/components/CustomColorStyleSelect.vue'
import { cloneDeep, defaultsDeep } from 'lodash-es' import { cloneDeep, defaultsDeep } from 'lodash-es'
@ -28,6 +32,7 @@ const showProperty = prop => props.propertyInner?.includes(prop)
const predefineColors = COLOR_PANEL const predefineColors = COLOR_PANEL
const state = reactive({ const state = reactive({
basicStyleForm: JSON.parse(JSON.stringify(DEFAULT_BASIC_STYLE)) as ChartBasicStyle, basicStyleForm: JSON.parse(JSON.stringify(DEFAULT_BASIC_STYLE)) as ChartBasicStyle,
miscForm: JSON.parse(JSON.stringify(DEFAULT_MISC)) as ChartMiscAttr,
customColor: null, customColor: null,
colorIndex: 0, colorIndex: 0,
fieldColumnWidth: { fieldColumnWidth: {
@ -38,6 +43,7 @@ const state = reactive({
watch( watch(
[ [
() => props.chart.customAttr.basicStyle, () => props.chart.customAttr.basicStyle,
() => props.chart.customAttr.misc,
() => props.chart.customAttr.tableHeader, () => props.chart.customAttr.tableHeader,
() => props.chart.xAxis, () => props.chart.xAxis,
() => props.chart.yAxis () => props.chart.yAxis
@ -47,14 +53,19 @@ watch(
}, },
{ deep: true } { deep: true }
) )
const emit = defineEmits(['onBasicStyleChange']) const emit = defineEmits(['onBasicStyleChange', 'onMiscChange'])
const changeBasicStyle = (prop?: string, requestData = false) => { const changeBasicStyle = (prop?: string, requestData = false) => {
emit('onBasicStyleChange', { data: state.basicStyleForm, requestData }, prop) emit('onBasicStyleChange', { data: state.basicStyleForm, requestData }, prop)
} }
const changeMisc = prop => {
emit('onMiscChange', { data: state.miscForm, requestData: true }, prop)
}
const init = () => { const init = () => {
const basicStyle = cloneDeep(props.chart.customAttr.basicStyle) const basicStyle = cloneDeep(props.chart.customAttr.basicStyle)
const miscStyle = cloneDeep(props.chart.customAttr.misc)
configCompat(basicStyle) configCompat(basicStyle)
state.basicStyleForm = defaultsDeep(basicStyle, cloneDeep(DEFAULT_BASIC_STYLE)) as ChartBasicStyle state.basicStyleForm = defaultsDeep(basicStyle, cloneDeep(DEFAULT_BASIC_STYLE)) as ChartBasicStyle
state.miscForm = defaultsDeep(miscStyle, cloneDeep(DEFAULT_MISC)) as ChartMiscAttr
if (!state.customColor) { if (!state.customColor) {
state.customColor = state.basicStyleForm.colors[0] state.customColor = state.basicStyleForm.colors[0]
state.colorIndex = 0 state.colorIndex = 0
@ -177,6 +188,12 @@ const mapStyleOptions = [
{ name: t('chart.map_style_wine'), value: 'wine' } { name: t('chart.map_style_wine'), value: 'wine' }
] ]
const flowLineTypeOptions = [
{ name: t('chart.map_line_type_line'), value: 'line' },
{ name: t('chart.map_line_type_arc'), value: 'arc' },
{ name: t('chart.map_line_type_arc_3d'), value: 'arc3d' }
]
onMounted(() => { onMounted(() => {
init() init()
}) })
@ -268,7 +285,236 @@ onMounted(() => {
<el-radio :effect="themes" label="vertical">{{ t('chart.vertical') }}</el-radio> <el-radio :effect="themes" label="vertical">{{ t('chart.vertical') }}</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<!--flow map begin-->
<div class="map-setting" v-if="showProperty('mapStyle')">
<div class="map-style">
<el-row style="flex: 1">
<el-col>
<el-form-item
:label="t('chart.chart_map') + t('chart.map_style')"
class="form-item"
:class="'form-item-' + themes"
>
<el-select
:effect="themes"
v-model="state.basicStyleForm.mapStyle"
@change="changeBasicStyle('mapStyle')"
>
<el-option
v-for="item in mapStyleOptions"
:key="item.name"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<div class="alpha-setting">
<label class="alpha-label" :class="{ dark: 'dark' === themes }">
{{ t('chart.chart_map') + t('chart.map_pitch') }}
</label>
<el-row style="flex: 1" :gutter="8">
<el-col>
<el-form-item class="form-item alpha-slider" :class="'form-item-' + themes">
<el-slider
:effect="themes"
:min="0"
:max="90"
v-model="state.miscForm.mapPitch"
@change="changeMisc('mapPitch')"
/>
</el-form-item>
</el-col>
</el-row>
</div>
</div>
<div class="map-flow-style">
<el-row style="flex: 1">
<el-col>
<el-form-item
:label="t('chart.line') + t('chart.map_line_type')"
class="form-item"
:class="'form-item-' + themes"
>
<el-select
:effect="themes"
v-model="state.miscForm.mapLineType"
@change="changeMisc('mapLineType')"
>
<el-option
v-for="item in flowLineTypeOptions"
:key="item.name"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<div class="alpha-setting">
<label class="alpha-label" :class="{ dark: 'dark' === themes }">
{{ t('chart.map_line_width') }}
</label>
<el-row style="flex: 1">
<el-col>
<el-form-item class="form-item alpha-slider" :class="'form-item-' + themes">
<el-slider
:effect="themes"
:min="1"
:max="10"
v-model="state.miscForm.mapLineWidth"
@change="changeMisc('mapLineWidth')"
/>
</el-form-item>
</el-col>
</el-row>
</div>
<el-row style="flex: 1">
<el-col>
<el-form-item class="form-item" :class="'form-item-' + themes">
<el-checkbox
size="small"
:effect="themes"
v-model="state.miscForm.mapLineGradient"
:predefine="predefineColors"
@change="changeMisc('mapLineGradient')"
>
{{ t('chart.line') + t('chart.map_line_linear') }}
</el-checkbox>
</el-form-item>
</el-col>
</el-row>
<div v-if="state.miscForm.mapLineGradient">
<el-row style="flex: 1" :gutter="8">
<el-col :span="13">
<el-form-item
class="form-item"
:class="'form-item-' + themes"
:label="t('chart.map_line_color_source_color')"
>
<el-color-picker
is-custom
class="color-picker-style"
v-model="state.miscForm.mapLineSourceColor"
:persistent="false"
:effect="themes"
:trigger-width="108"
:predefine="predefineColors"
@change="changeMisc('mapLineSourceColor')"
/>
</el-form-item>
</el-col>
<el-col :span="13">
<el-form-item
class="form-item"
:class="'form-item-' + themes"
:label="t('chart.map_line_color_target_color')"
>
<el-color-picker
is-custom
class="color-picker-style"
v-model="state.miscForm.mapLineTargetColor"
:persistent="false"
:effect="themes"
:trigger-width="108"
:predefine="predefineColors"
@change="changeMisc('mapLineTargetColor')"
/>
</el-form-item>
</el-col>
</el-row>
</div>
<div v-if="!state.miscForm.mapLineGradient">
<el-row style="flex: 1" :gutter="8">
<el-col>
<el-form-item
class="form-item"
:class="'form-item-' + themes"
:label="t('chart.color')"
>
<el-color-picker
is-custom
class="color-picker-style"
v-model="state.miscForm.mapLineSourceColor"
:persistent="false"
:effect="themes"
:trigger-width="108"
:predefine="predefineColors"
@change="changeMisc('mapLineSourceColor')"
/>
</el-form-item>
</el-col>
</el-row>
</div>
<div class="alpha-setting">
<label class="alpha-label" :class="{ dark: 'dark' === themes }">
{{ t('chart.not_alpha') }}
</label>
<el-row style="flex: 1" :gutter="8">
<el-col :span="13">
<el-form-item class="form-item alpha-slider" :class="'form-item-' + themes">
<el-slider
:effect="themes"
v-model="state.basicStyleForm.alpha"
@change="changeBasicStyle('alpha')"
/>
</el-form-item>
</el-col>
<el-col :span="11" style="padding-top: 2px">
<el-form-item class="form-item" :class="'form-item-' + themes">
<el-input
type="number"
:effect="themes"
v-model="state.basicStyleForm.alpha"
:min="0"
:max="100"
class="basic-input-number"
:controls="false"
@change="changeBasicStyle('alpha')"
>
<template #suffix> % </template>
</el-input>
</el-form-item>
</el-col>
</el-row>
</div>
<el-row style="flex: 1">
<el-col>
<el-form-item class="form-item" :class="'form-item-' + themes">
<el-checkbox
size="small"
:effect="themes"
v-model="state.miscForm.mapLineAnimate"
:predefine="predefineColors"
@change="changeMisc('mapLineAnimate')"
>
{{ t('chart.line') + t('chart.map_line_animate') }}
</el-checkbox>
</el-form-item>
</el-col>
</el-row>
<div class="alpha-setting" v-if="state.miscForm.mapLineAnimate">
<label class="alpha-label" :class="{ dark: 'dark' === themes }">
{{ t('chart.map_line_animate_duration') }}
</label>
<el-row style="flex: 1" :gutter="8">
<el-col>
<el-form-item class="form-item alpha-slider" :class="'form-item-' + themes">
<el-slider
:effect="themes"
:min="0"
:max="20"
v-model="state.miscForm.mapLineAnimateDuration"
@change="changeMisc('mapLineAnimateDuration')"
/>
</el-form-item>
</el-col>
</el-row>
</div>
</div>
</div>
<!--flow map end-->
<!--map start--> <!--map start-->
<el-row :gutter="8"> <el-row :gutter="8">
<el-col :span="12" v-if="showProperty('areaBorderColor')"> <el-col :span="12" v-if="showProperty('areaBorderColor')">
@ -658,27 +904,6 @@ onMounted(() => {
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<!--radar end--> <!--radar end-->
<!--flow map begin-->
<el-form-item
:label="t('chart.map_style')"
class="form-item"
:class="'form-item-' + themes"
v-if="showProperty('mapStyle')"
>
<el-select
:effect="themes"
v-model="state.basicStyleForm.mapStyle"
@change="changeBasicStyle('mapStyle')"
>
<el-option
v-for="item in mapStyleOptions"
:key="item.name"
:label="item.name"
:value="item.value"
/>
</el-select>
</el-form-item>
<!--flow map end-->
<!--scatter start--> <!--scatter start-->
<el-form-item <el-form-item
:label="t('chart.bubble_symbol')" :label="t('chart.bubble_symbol')"

View File

@ -224,7 +224,11 @@ onMounted(() => {
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<div class="position-divider" :class="'position-divider--' + themes"></div> <div
v-if="showProperty('orient')"
class="position-divider"
:class="'position-divider--' + themes"
></div>
<el-form-item <el-form-item
class="form-item" class="form-item"

View File

@ -137,11 +137,13 @@ onMounted(() => {
label-position="top" label-position="top"
> >
<template v-if="showProperty('lineStyle')"> <template v-if="showProperty('lineStyle')">
<label class="custom-form-item-label" :class="'custom-form-item-label--' + themes"
>{{ t('chart.quadrant') }}{{ t('chart.split_line') }}</label
>
<div style="display: flex"> <div style="display: flex">
<el-form-item class="form-item" :class="'form-item-' + themes" style="padding-right: 4px"> <el-form-item
:label="t('chart.split_line')"
class="form-item"
:class="'form-item-' + themes"
style="padding-right: 4px"
>
<el-color-picker <el-color-picker
v-model="state.quadrantForm.lineStyle.stroke" v-model="state.quadrantForm.lineStyle.stroke"
class="color-picker-style" class="color-picker-style"
@ -152,6 +154,7 @@ onMounted(() => {
/> />
</el-form-item> </el-form-item>
<el-form-item class="form-item" :class="'form-item-' + themes" style="padding-left: 4px"> <el-form-item class="form-item" :class="'form-item-' + themes" style="padding-left: 4px">
<template #label>&nbsp;</template>
<el-tooltip :content="t('chart.not_alpha')" :effect="toolTip" placement="top"> <el-tooltip :content="t('chart.not_alpha')" :effect="toolTip" placement="top">
<el-select <el-select
style="width: 53px" style="width: 53px"
@ -170,6 +173,7 @@ onMounted(() => {
</el-tooltip> </el-tooltip>
</el-form-item> </el-form-item>
<el-form-item class="form-item" :class="'form-item-' + themes" style="padding-left: 4px"> <el-form-item class="form-item" :class="'form-item-' + themes" style="padding-left: 4px">
<template #label>&nbsp;</template>
<el-tooltip :content="t('chart.funnel_width')" :effect="toolTip" placement="top"> <el-tooltip :content="t('chart.funnel_width')" :effect="toolTip" placement="top">
<el-input-number <el-input-number
style="width: 108px" style="width: 108px"
@ -229,18 +233,14 @@ onMounted(() => {
:label="t('chart.quadrant') + (index + 1)" :label="t('chart.quadrant') + (index + 1)"
class="padding-tab" class="padding-tab"
> >
<div style="flex-direction: row; justify-content: space-between"> <div style="margin-top: 8px">
<template v-if="showProperty('regionStyle')"> <template v-if="showProperty('regionStyle')">
<div style="display: flex">
<label class="custom-form-item-label" :class="'custom-form-item-label--' + themes">{{
t('chart.backgroundColor')
}}</label>
</div>
<div style="display: flex"> <div style="display: flex">
<el-form-item <el-form-item
class="form-item" class="form-item"
:class="'form-item-' + themes" :class="'form-item-' + themes"
style="padding-right: 4px" style="padding-right: 4px"
:label="t('chart.backgroundColor')"
> >
<el-color-picker <el-color-picker
v-model="state.quadrantForm.regionStyle[index].fill" v-model="state.quadrantForm.regionStyle[index].fill"
@ -256,6 +256,7 @@ onMounted(() => {
:class="'form-item-' + themes" :class="'form-item-' + themes"
style="padding-left: 4px" style="padding-left: 4px"
> >
<template #label>&nbsp;</template>
<el-tooltip :content="t('chart.not_alpha')" :effect="toolTip" placement="top"> <el-tooltip :content="t('chart.not_alpha')" :effect="toolTip" placement="top">
<el-select <el-select
style="width: 53px" style="width: 53px"
@ -285,14 +286,12 @@ onMounted(() => {
@blur="changeStyle()" @blur="changeStyle()"
/> />
</el-form-item> </el-form-item>
<label class="custom-form-item-label" :class="'custom-form-item-label--' + themes">
{{ t('chart.text') }}{{ t('chart.chart_style') }}</label
>
<div style="display: flex"> <div style="display: flex">
<el-form-item <el-form-item
class="form-item" class="form-item"
:class="'form-item-' + themes" :class="'form-item-' + themes"
style="padding-right: 4px" style="padding-right: 4px"
:label="t('chart.chart_style')"
> >
<el-color-picker <el-color-picker
v-model="l.style.fill" v-model="l.style.fill"
@ -308,6 +307,7 @@ onMounted(() => {
:class="'form-item-' + themes" :class="'form-item-' + themes"
style="padding-left: 4px" style="padding-left: 4px"
> >
<template #label>&nbsp;</template>
<el-tooltip :content="t('chart.not_alpha')" :effect="toolTip" placement="top"> <el-tooltip :content="t('chart.not_alpha')" :effect="toolTip" placement="top">
<el-select <el-select
style="width: 53px" style="width: 53px"
@ -330,6 +330,7 @@ onMounted(() => {
:class="'form-item-' + themes" :class="'form-item-' + themes"
style="padding-left: 4px" style="padding-left: 4px"
> >
<template #label>&nbsp;</template>
<el-tooltip :content="t('chart.font_size')" :effect="toolTip" placement="top"> <el-tooltip :content="t('chart.font_size')" :effect="toolTip" placement="top">
<el-select <el-select
style="width: 108px" style="width: 108px"

View File

@ -153,15 +153,12 @@ onMounted(() => {
/> />
</el-form-item> </el-form-item>
<label class="custom-form-item-label" :class="'custom-form-item-label--' + themes"
>{{ t('chart.name') }}{{ t('chart.text') }}</label
>
<div style="display: flex"> <div style="display: flex">
<el-form-item <el-form-item
class="form-item" class="form-item"
:class="'form-item-' + themes" :class="'form-item-' + themes"
v-if="showProperty('color')" v-if="showProperty('color')"
style="padding-right: 4px" :label="t('chart.chart_style')"
> >
<el-color-picker <el-color-picker
v-model="state.axisForm.color" v-model="state.axisForm.color"
@ -178,6 +175,7 @@ onMounted(() => {
v-if="showProperty('fontSize')" v-if="showProperty('fontSize')"
style="padding-left: 4px" style="padding-left: 4px"
> >
<template #label>&nbsp;</template>
<el-tooltip content="字号" :effect="toolTip" placement="top"> <el-tooltip content="字号" :effect="toolTip" placement="top">
<el-select <el-select
style="width: 108px" style="width: 108px"

View File

@ -129,15 +129,13 @@ onMounted(() => {
/> />
</el-form-item> </el-form-item>
<label class="custom-form-item-label" :class="'custom-form-item-label--' + themes"
>{{ t('chart.name') }}{{ t('chart.text') }}</label
>
<div style="display: flex"> <div style="display: flex">
<el-form-item <el-form-item
class="form-item" class="form-item"
:class="'form-item-' + themes" :class="'form-item-' + themes"
v-if="showProperty('color')" v-if="showProperty('color')"
style="padding-right: 4px" style="padding-right: 4px"
:label="t('chart.chart_style')"
> >
<el-color-picker <el-color-picker
v-model="state.axisForm.color" v-model="state.axisForm.color"
@ -154,6 +152,7 @@ onMounted(() => {
v-if="showProperty('fontSize')" v-if="showProperty('fontSize')"
style="padding-left: 4px" style="padding-left: 4px"
> >
<template #label>&nbsp;</template>
<el-tooltip content="字号" :effect="toolTip" placement="top"> <el-tooltip content="字号" :effect="toolTip" placement="top">
<el-select <el-select
style="width: 108px" style="width: 108px"

View File

@ -209,12 +209,7 @@ onMounted(() => {
</el-col> </el-col>
</el-row> </el-row>
<el-form-item <el-form-item class="form-item" :class="'form-item-' + themes" v-if="showProperty('showIndex')">
:label="t('chart.table_show_index')"
class="form-item"
:class="'form-item-' + themes"
v-if="showProperty('showIndex')"
>
<el-checkbox <el-checkbox
size="small" size="small"
:effect="themes" :effect="themes"

View File

@ -3044,6 +3044,35 @@ const drop = (ev: MouseEvent, type = 'xAxis') => {
border-top: none !important; border-top: none !important;
} }
:deep(.ed-tabs__nav-wrap::after) {
background-color: rgba(31, 35, 41, 0.15);
}
:deep(.ed-tabs__nav-scroll) {
.ed-tabs__item {
height: 35px;
line-height: 35px;
color: var(--ed-color-primary, #3370ff);
font-family: '阿里巴巴普惠体 3.0 55 Regular L3';
font-size: 12px;
font-style: normal;
font-weight: 500;
}
}
}
.query-style-tab {
width: 100%;
border-top: solid 1px @side-outline-border-color-light !important;
.tab-container {
.border-bottom-tab(8px);
}
margin-left: 0px !important;
:deep(.ed-tabs__header) {
border-top: none !important;
}
:deep(.ed-tabs__nav-wrap::after) { :deep(.ed-tabs__nav-wrap::after) {
background-color: rgba(31, 35, 41, 0.15); background-color: rgba(31, 35, 41, 0.15);
} }
@ -3082,6 +3111,35 @@ const drop = (ev: MouseEvent, type = 'xAxis') => {
box-shadow: 0 0 0 1px var(--ed-color-primary, #3370ff) inset !important; box-shadow: 0 0 0 1px var(--ed-color-primary, #3370ff) inset !important;
} }
} }
.query-style-tab {
width: 100%;
border-top: solid 1px @main-collapse-border-dark !important;
.tab-container {
.border-bottom-tab(8px);
}
margin-left: 0px !important;
:deep(.ed-tabs__header) {
border-top: none !important;
}
:deep(.ed-tabs__nav-wrap::after) {
background-color: rgba(31, 35, 41, 0.15);
}
:deep(.ed-tabs__nav-scroll) {
.ed-tabs__item {
height: 35px;
line-height: 35px;
color: var(--ed-color-primary, #3370ff);
font-family: '阿里巴巴普惠体 3.0 55 Regular L3';
font-size: 12px;
font-style: normal;
font-weight: 500;
}
}
}
} }
.chart-edit { .chart-edit {

View File

@ -388,6 +388,7 @@ export const DEFAULT_INDICATOR_STYLE: ChartIndicatorStyle = {
fontFamily: 'Microsoft YaHei', fontFamily: 'Microsoft YaHei',
letterSpace: 0, letterSpace: 0,
fontShadow: false, fontShadow: false,
backgroundColor: '#fff',
suffixEnable: true, suffixEnable: true,
suffix: '', suffix: '',
@ -675,6 +676,7 @@ export const DEFAULT_ASSIST_LINE_CFG: ChartAssistLineCfg = {
export const DEFAULT_THRESHOLD: ChartThreshold = { export const DEFAULT_THRESHOLD: ChartThreshold = {
enable: false, enable: false,
gaugeThreshold: '', gaugeThreshold: '',
liquidThreshold: '',
labelThreshold: [], labelThreshold: [],
tableThreshold: [], tableThreshold: [],
textLabelThreshold: [] textLabelThreshold: []
@ -1223,14 +1225,14 @@ export const CHART_TYPE_CONFIGS = [
category: 'compare', category: 'compare',
value: 'bidirectional-bar', value: 'bidirectional-bar',
title: t('chart.chart_bidirectional_bar'), title: t('chart.chart_bidirectional_bar'),
icon: 'percentage-bar-stack-horizontal' icon: 'bidirectional-bar'
}, },
{ {
render: 'antv', render: 'antv',
category: 'compare', category: 'compare',
value: 'progress-bar', value: 'progress-bar',
title: t('chart.chart_progress_bar'), title: t('chart.chart_progress_bar'),
icon: 'percentage-bar-stack-horizontal' icon: 'progress-bar'
} }
] ]
}, },
@ -1308,6 +1310,13 @@ export const CHART_TYPE_CONFIGS = [
value: 'bubble-map', value: 'bubble-map',
title: t('chart.chart_bubble_map'), title: t('chart.chart_bubble_map'),
icon: 'bubble-map' icon: 'bubble-map'
},
{
render: 'antv',
category: 'map',
value: 'flow-map',
title: t('chart.chart_flow_map'),
icon: 'flow-map'
} }
] ]
}, },

View File

@ -19,7 +19,8 @@ export class Liquid extends G2PlotChartView<LiquidOptions, G2Liquid> {
'basic-style-selector', 'basic-style-selector',
'label-selector', 'label-selector',
'misc-selector', 'misc-selector',
'title-selector' 'title-selector',
'threshold'
] ]
propertyInner: EditorPropertyInner = { propertyInner: EditorPropertyInner = {
'background-overall-component': ['all'], 'background-overall-component': ['all'],
@ -37,7 +38,8 @@ export class Liquid extends G2PlotChartView<LiquidOptions, G2Liquid> {
'fontFamily', 'fontFamily',
'letterSpace', 'letterSpace',
'fontShadow' 'fontShadow'
] ],
threshold: ['liquidThreshold']
} }
axis: AxisType[] = ['yAxis', 'filter'] axis: AxisType[] = ['yAxis', 'filter']
axisConfig: AxisConfig = { axisConfig: AxisConfig = {
@ -147,6 +149,31 @@ export class Liquid extends G2PlotChartView<LiquidOptions, G2Liquid> {
} }
} }
protected configThreshold(chart: Chart, options: LiquidOptions): LiquidOptions {
const senior = parseJson(chart.senior)
if (senior?.threshold?.enable) {
const { liquidThreshold } = senior?.threshold
if (liquidThreshold) {
const { paletteQualitative10: colors } = (options.theme as any).styleSheet
const liquidStyle = () => {
const thresholdArr = liquidThreshold.split(',')
let index = 0
thresholdArr.forEach((v, i) => {
if (options.percent > parseFloat(v) / 100) {
index = i + 1
}
})
return {
fill: colors[index % colors.length],
stroke: colors[index % colors.length]
}
}
return { ...options, liquidStyle }
}
}
return options
}
setupDefaultOptions(chart: ChartObj): ChartObj { setupDefaultOptions(chart: ChartObj): ChartObj {
chart.customAttr.label = { chart.customAttr.label = {
...chart.customAttr.label, ...chart.customAttr.label,
@ -162,7 +189,12 @@ export class Liquid extends G2PlotChartView<LiquidOptions, G2Liquid> {
} }
protected setupOptions(chart: Chart, options: LiquidOptions): LiquidOptions { protected setupOptions(chart: Chart, options: LiquidOptions): LiquidOptions {
return flow(this.configTheme, this.configMisc, this.configLabel)(chart, options) return flow(
this.configTheme,
this.configMisc,
this.configLabel,
this.configThreshold
)(chart, options)
} }
constructor() { constructor() {
super('liquid', DEFAULT_LIQUID_DATA) super('liquid', DEFAULT_LIQUID_DATA)

View File

@ -0,0 +1,138 @@
import { useI18n } from '@/hooks/web/useI18n'
import {
L7ChartView,
L7Config,
L7DrawConfig,
L7Wrapper
} from '@/views/chart/components/js/panel/types/impl/l7'
import { MAP_EDITOR_PROPERTY_INNER } from '@/views/chart/components/js/panel/charts/map/common'
import { flow, parseJson } from '@/views/chart/components/js/util'
import { deepCopy } from '@/utils/utils'
import { GaodeMap } from '@antv/l7-maps'
import { Scene } from '@antv/l7-scene'
import { LineLayer } from '@antv/l7-layers'
import { queryMapKeyApi } from '@/api/setting/sysParameter'
const { t } = useI18n()
/**
* 流向地图
*/
export class FlowMap extends L7ChartView<Scene, L7Config> {
properties: EditorProperty[] = [
'background-overall-component',
'basic-style-selector',
'title-selector'
]
propertyInner: EditorPropertyInner = {
...MAP_EDITOR_PROPERTY_INNER,
'basic-style-selector': ['mapStyle', 'zoom']
}
axis: AxisType[] = ['xAxis', 'xAxisExt', 'filter']
axisConfig: AxisConfig = {
xAxis: {
name: `起点经纬度 / ${t('chart.dimension')}`,
type: 'd',
limit: 2
},
xAxisExt: {
name: `终点经纬度 / ${t('chart.dimension')}`,
type: 'd',
limit: 2
}
}
constructor() {
super('flow-map', [])
}
async drawChart(drawOption: L7DrawConfig<L7Config>) {
const { chart, container } = drawOption
const xAxis = deepCopy(chart.xAxis)
const xAxisExt = deepCopy(chart.xAxisExt)
let basicStyle
let miscStyle
if (chart.customAttr) {
basicStyle = parseJson(chart.customAttr).basicStyle
miscStyle = parseJson(chart.customAttr).misc
}
const flowLineStyle = {
type: miscStyle.mapLineType,
size: miscStyle.mapLineWidth,
animate: miscStyle.mapLineAnimate,
animateDuration: miscStyle.mapLineAnimateDuration,
gradient: miscStyle.mapLineGradient,
sourceColor: miscStyle.mapLineSourceColor,
targetColor: miscStyle.mapLineTargetColor,
alpha: basicStyle.alpha
}
const mapStyle = `amap://styles/${basicStyle.mapStyle ? basicStyle.mapStyle : 'normal'}`
const key = await this.getMapKey()
// 底层
const scene = new Scene({
id: container,
logoVisible: false,
map: new GaodeMap({
token: key ?? undefined,
style: mapStyle,
pitch: miscStyle.mapPitch,
zoom: 2.5
})
})
if (xAxis?.length < 2 || xAxisExt?.length < 2) {
return new L7Wrapper(scene, undefined)
}
const config: L7Config = new LineLayer({
name: 'line',
blend: 'normal',
autoFit: true
})
.source(chart.data?.tableRow ? chart.data.tableRow : [], {
parser: {
type: 'json',
x: xAxis[0].dataeaseName,
y: xAxis[1].dataeaseName,
x1: xAxisExt[0].dataeaseName,
y1: xAxisExt[1].dataeaseName
}
})
.size(flowLineStyle.size)
.shape(flowLineStyle.type)
.animate({
enable: flowLineStyle.animate,
duration: flowLineStyle.animateDuration,
interval: 1,
trailLength: 1
})
if (flowLineStyle.gradient) {
config.style({
sourceColor: flowLineStyle.sourceColor,
targetColor: flowLineStyle.targetColor,
opacity: flowLineStyle.alpha / 100
})
} else {
config
.style({
opacity: flowLineStyle.alpha / 100
})
.color(flowLineStyle.sourceColor)
}
this.configZoomButton(chart, scene)
return new L7Wrapper(scene, config)
}
getMapKey = async () => {
const key = 'online-map-key'
if (!localStorage.getItem(key)) {
await queryMapKeyApi().then(res => localStorage.setItem(key, res.data))
}
return localStorage.getItem(key)
}
setupDefaultOptions(chart: ChartObj): ChartObj {
chart.customAttr.misc.mapLineAnimate = true
return chart
}
protected setupOptions(chart: Chart, config: L7Config): L7Config {
return flow(this.configEmptyDataStrategy)(chart, config)
}
}

View File

@ -4,9 +4,10 @@ import {
} from '@/views/chart/components/js/panel/types/impl/g2plot' } from '@/views/chart/components/js/panel/types/impl/g2plot'
import { ScatterOptions, Scatter as G2Scatter } from '@antv/g2plot/esm/plots/scatter' import { ScatterOptions, Scatter as G2Scatter } from '@antv/g2plot/esm/plots/scatter'
import { flow, parseJson } from '../../../util' import { flow, parseJson } from '../../../util'
import { valueFormatter } from '../../../formatter' import { valueFormatter } from '@/views/chart/components/js/formatter'
import { useI18n } from '@/hooks/web/useI18n' import { useI18n } from '@/hooks/web/useI18n'
import { isEmpty } from 'lodash-es' import { isEmpty, map } from 'lodash-es'
import { cloneDeep, defaultTo } from 'lodash-es'
const { t } = useI18n() const { t } = useI18n()
/** /**
@ -119,58 +120,40 @@ export class Quadrant extends G2PlotChartView<ScatterOptions, G2Scatter> {
if (!chart.data?.data) { if (!chart.data?.data) {
return return
} }
const { colorFieldObj, sizeFieldObj, xFieldObj, yFieldObj } = this.getFieldObject(chart) // data
if (!xFieldObj.id || !yFieldObj.id || yFieldObj.id === xFieldObj.id) { const sourceData: Array<any> = cloneDeep(chart.data.data)
return const data1 = defaultTo(sourceData[0]?.data, [])
const data2 = defaultTo(sourceData[1]?.data, [])
const data3 = defaultTo(sourceData[2]?.data, [])
const xData = data1.map(item => {
return {
...item,
id: item.quotaList[0]?.id,
field: item.field,
value: item.value
} }
const data: any[] = []
// 根据指标字段对数据列表进行分组
const groupedData = chart.data?.data
?.filter(item => item['category'] != null)
.reduce((result, item) => {
;(result[item['field']] = result[item['field']] || []).push(item)
return result
}, {})
// 维度字段数据分组
chart.data?.data
?.filter(item => item['category'] === null)
.forEach(item => {
;(groupedData[colorFieldObj.name] = groupedData[colorFieldObj.name] || []).push(
item['field']
)
}) })
// 去掉groupedData每个key中集合的对象重复项 const yData = data2.map(item => {
Object.keys(groupedData).forEach(key => { return {
groupedData[key] = Array.from(this.getUniqueObjects(groupedData[key])) ...item,
id: item.quotaList[0]?.id,
field: item.field,
value: item.value
}
}) })
// 一个指标字段的数据长度视为数据长度也就是有多少数据 const eData = data3.map(item => {
const dataLength = chart.data?.data.length / chart.data?.fields.length return {
for (let index = 0; index < dataLength; index++) { ...item,
const tmpData = { id: item.quotaList[0]?.id,
dimensionList: groupedData[xFieldObj.name][index].dimensionList, field: item.field,
quotaList: groupedData[xFieldObj.name][index].quotaList, value: item.value
[xFieldObj.name]: groupedData[xFieldObj.name][index].value
}
if (groupedData[yFieldObj.name]) {
tmpData[yFieldObj.name] = groupedData[yFieldObj.name][index].value
}
if (
groupedData[sizeFieldObj.name] &&
sizeFieldObj.name !== yFieldObj.name &&
sizeFieldObj.name !== xFieldObj.name
) {
tmpData[sizeFieldObj.name] = groupedData[sizeFieldObj.name]?.[index].value
}
if (groupedData[colorFieldObj.name]) {
tmpData[colorFieldObj.name] = groupedData[colorFieldObj.name][index]
}
data.push(tmpData)
} }
})
// x轴基准线 默认值 // x轴基准线 默认值
const xValues = data.map(item => item[xFieldObj.name]) const xValues = xData.map(item => item.value)
const xBaseline = ((Math.max(...xValues) + Math.min(...xValues)) / 2).toFixed() const xBaseline = ((Math.max(...xValues) + Math.min(...xValues)) / 2).toFixed()
// y轴基准线 默认值 // y轴基准线 默认值
const yValues = data.map(item => item[yFieldObj.name]) const yValues = yData.map(item => item.value)
const yBaseline = ((Math.max(...yValues) + Math.min(...yValues)) / 2).toFixed() const yBaseline = ((Math.max(...yValues) + Math.min(...yValues)) / 2).toFixed()
const defaultBaselineQuadrant = { const defaultBaselineQuadrant = {
...chart.customAttr['quadrant'] ...chart.customAttr['quadrant']
@ -181,15 +164,25 @@ export class Quadrant extends G2PlotChartView<ScatterOptions, G2Scatter> {
defaultBaselineQuadrant.xBaseline = xBaseline defaultBaselineQuadrant.xBaseline = xBaseline
defaultBaselineQuadrant.yBaseline = yBaseline defaultBaselineQuadrant.yBaseline = yBaseline
} }
const colorField = colorFieldObj.name ? { colorField: colorFieldObj.name } : {} const data = map(defaultTo(xData, []), d => {
return {
...d,
yAxis: d.value,
quotaList: d.quotaList
.concat(yData.find(item => item.field === d.field)?.quotaList)
.concat(eData.find(item => item.field === d.field)?.quotaList),
yAxisExt: yData.find(item => item.field === d.field)?.value,
extBubble: eData.find(item => item.field === d.field)?.value
}
})
const baseOptions: ScatterOptions = { const baseOptions: ScatterOptions = {
...colorField, colorField: 'field',
quadrant: { quadrant: {
...defaultBaselineQuadrant ...defaultBaselineQuadrant
}, },
data: data, data: data,
xField: xFieldObj.name, xField: 'yAxis',
yField: yFieldObj.name, yField: 'yAxisExt',
appendPadding: 30, appendPadding: 30,
pointStyle: { pointStyle: {
fillOpacity: 0.8, fillOpacity: 0.8,
@ -208,12 +201,11 @@ export class Quadrant extends G2PlotChartView<ScatterOptions, G2Scatter> {
protected configBasicStyle(chart: Chart, options: ScatterOptions): ScatterOptions { protected configBasicStyle(chart: Chart, options: ScatterOptions): ScatterOptions {
const customAttr = parseJson(chart.customAttr) const customAttr = parseJson(chart.customAttr)
const basicStyle = customAttr.basicStyle const basicStyle = customAttr.basicStyle
const extBubbleObj = { id: chart.extBubble[0]?.id, name: chart.extBubble[0]?.['originName'] }
if (chart.extBubble?.length) { if (chart.extBubble?.length) {
return { return {
...options, ...options,
size: [4, 30], size: [4, 30],
sizeField: extBubbleObj.name, sizeField: 'extBubble',
shape: basicStyle.scatterSymbol shape: basicStyle.scatterSymbol
} }
} }
@ -296,7 +288,7 @@ export class Quadrant extends G2PlotChartView<ScatterOptions, G2Scatter> {
fontSize: l.fontSize fontSize: l.fontSize
}, },
content: datum => { content: datum => {
return datum[chart.xAxis[0]?.['originName']] return datum['name']
}, },
layout: [{ type: 'limit-in-shape' }] layout: [{ type: 'limit-in-shape' }]
} }
@ -326,21 +318,20 @@ export class Quadrant extends G2PlotChartView<ScatterOptions, G2Scatter> {
pre[next['seriesId']] = next pre[next['seriesId']] = next
return pre return pre
}, {}) as Record<string, SeriesFormatter> }, {}) as Record<string, SeriesFormatter>
const optionsData = cloneDeep(options.data)
const tooltip: ScatterOptions['tooltip'] = { const tooltip: ScatterOptions['tooltip'] = {
showTitle: true, showTitle: true,
title: (_title, datum) => { title: (_title, datum) => {
return datum?.[xAxisTitle['originName']] return datum?.['name']
}, },
customItems(originalItems) { customItems(originalItems) {
if (!tooltipAttr.seriesTooltipFormatter?.length) { if (!tooltipAttr.seriesTooltipFormatter?.length) {
return originalItems return originalItems
} }
const result = [] const result = []
originalItems originalItems.forEach(item => {
?.filter(i => i.name !== xAxisTitle['originName'])
.forEach(item => {
Object.keys(formatterMap).forEach(key => { Object.keys(formatterMap).forEach(key => {
if (formatterMap[key]['originName'] === item.name) { if (key.endsWith(item.name)) {
const formatter = formatterMap[key] const formatter = formatterMap[key]
if (formatter) { if (formatter) {
const value = const value =
@ -355,6 +346,19 @@ export class Quadrant extends G2PlotChartView<ScatterOptions, G2Scatter> {
} }
}) })
}) })
const dynamicTooltipValue = optionsData.find(
d => d.field === originalItems[0]['title']
)?.dynamicTooltipValue
if (dynamicTooltipValue.length > 0) {
dynamicTooltipValue.forEach(dy => {
const q = tooltipAttr.seriesTooltipFormatter.filter(i => i.id === dy.fieldId)
if (q && q.length > 0) {
const value = valueFormatter(parseFloat(dy.value as string), q[0].formatterCfg)
const name = isEmpty(q[0].chartShowName) ? q[0].name : q[0].chartShowName
result.push({ color: 'grey', name, value })
}
})
}
return result return result
} }
} }

View File

@ -26,6 +26,7 @@ import type { Plot as L7Plot, PlotOptions } from '@antv/l7plot/dist/esm'
import { Zoom } from '@antv/l7' import { Zoom } from '@antv/l7'
import { createL7Icon } from '@antv/l7-component/es/utils/icon' import { createL7Icon } from '@antv/l7-component/es/utils/icon'
import { DOM } from '@antv/l7-utils' import { DOM } from '@antv/l7-utils'
import { Scene } from '@antv/l7-scene'
export function getPadding(chart: Chart): number[] { export function getPadding(chart: Chart): number[] {
if (chart.drill) { if (chart.drill) {
@ -1023,7 +1024,7 @@ class CustomZoom extends Zoom {
this['updateDisabled']() this['updateDisabled']()
} }
} }
export function configL7Zoom(chart: Chart, plot: L7Plot<PlotOptions>) { export function configL7Zoom(chart: Chart, plot: L7Plot<PlotOptions> | Scene) {
const { basicStyle } = parseJson(chart.customAttr) const { basicStyle } = parseJson(chart.customAttr)
if ( if (
(basicStyle.suspension === false && basicStyle.showZoom === undefined) || (basicStyle.suspension === false && basicStyle.showZoom === undefined) ||
@ -1031,14 +1032,15 @@ export function configL7Zoom(chart: Chart, plot: L7Plot<PlotOptions>) {
) { ) {
return return
} }
plot.once('loaded', () => { const plotScene = plot instanceof Scene ? plot : plot.scene
plotScene.once('loaded', () => {
const zoomOptions = { const zoomOptions = {
initZoom: plot.scene.getZoom(), initZoom: plotScene.getZoom(),
center: plot.scene.getCenter(), center: plotScene.getCenter(),
buttonColor: basicStyle.zoomButtonColor, buttonColor: basicStyle.zoomButtonColor,
buttonBackground: basicStyle.zoomBackground buttonBackground: basicStyle.zoomBackground
} as any } as any
plot.scene.addControl(new CustomZoom(zoomOptions)) plotScene.addControl(new CustomZoom(zoomOptions))
}) })
} }

View File

@ -0,0 +1,95 @@
import { Scene } from '@antv/l7-scene'
import {
AntVAbstractChartView,
AntVDrawOptions,
ChartLibraryType,
ChartWrapper
} from '@/views/chart/components/js/panel/types'
import { cloneDeep } from 'lodash-es'
import { parseJson } from '@/views/chart/components/js/util'
import { ILayer } from '@antv/l7plot'
import { configL7Zoom } from '@/views/chart/components/js/panel/common/common_antv'
export type L7DrawConfig<P> = AntVDrawOptions<P>
export interface L7Config extends ILayer {
handleConfig?: (arg0: Scene) => void
[key: string]: string | any
}
export class L7Wrapper<
O extends L7Config | Array<L7Config>,
S extends Scene
> extends ChartWrapper<S> {
private readonly config: O | Array<O>
private readonly scene: S | null = null
constructor(scene: S, l7config: O | Array<O> | undefined) {
super()
this.chartInstance = scene
this.config = l7config
this.scene = scene
}
destroy = () => {
if (!this.chartInstance) {
return
}
this.chartInstance?.destroy()
}
render = () => {
if (this.scene && this.config) {
this.scene.on('loaded', () => {
if (Array.isArray(this.config)) {
this.config?.forEach(p => {
this.handleConfig(p)
})
} else {
this.handleConfig(this.config)
}
})
}
}
handleConfig = (config: L7Config) => {
if (config.handleConfig) {
config.handleConfig?.(this.scene)
} else {
this.scene.addLayer(config)
}
}
}
export abstract class L7ChartView<
S extends Scene,
O extends L7Config
> extends AntVAbstractChartView {
public abstract drawChart(drawOption: L7DrawConfig<O>): L7Wrapper<O, S> | any
protected configEmptyDataStrategy(chart: Chart, options: O): O {
const { functionCfg } = parseJson(chart.senior)
const emptyDataStrategy = functionCfg.emptyDataStrategy
if (!emptyDataStrategy || emptyDataStrategy === 'breakLine') {
return options
}
const data = cloneDeep(options.sourceOption.data)
if (emptyDataStrategy === 'setZero') {
data.forEach(item => {
item.value === null && (item.value = 0)
})
}
if (emptyDataStrategy === 'ignoreData') {
for (let i = data.length - 1; i >= 0; i--) {
if (data[i].value === null) {
data.splice(i, 1)
}
}
}
options.sourceOption.data = data
return options
}
protected configZoomButton(chart: Chart, plot: S) {
configL7Zoom(chart, plot)
}
protected constructor(name: string, defaultData: any[]) {
super(ChartLibraryType.L7, name, defaultData)
}
protected abstract setupOptions(chart: Chart, options: O): O
}

View File

@ -11,6 +11,7 @@ export enum ChartRenderType {
export enum ChartLibraryType { export enum ChartLibraryType {
G2_PLOT = 'g2plot', G2_PLOT = 'g2plot',
L7_PLOT = 'l7plot', L7_PLOT = 'l7plot',
L7 = 'l7',
ECHARTS = 'echarts', ECHARTS = 'echarts',
S2 = 's2', S2 = 's2',
RICH_TEXT = 'rich-text', RICH_TEXT = 'rich-text',

View File

@ -17,9 +17,11 @@ import { customAttrTrans, customStyleTrans, recursionTransObj } from '@/utils/ca
import { deepCopy } from '@/utils/utils' import { deepCopy } from '@/utils/utils'
import { trackBarStyleCheck } from '@/utils/canvasUtils' import { trackBarStyleCheck } from '@/utils/canvasUtils'
import { useEmitt } from '@/hooks/web/useEmitt' import { useEmitt } from '@/hooks/web/useEmitt'
import { L7ChartView } from '@/views/chart/components/js/panel/types/impl/l7'
const dvMainStore = dvMainStoreWithOut() const dvMainStore = dvMainStoreWithOut()
const { nowPanelTrackInfo, nowPanelJumpInfo, mobileInPc } = storeToRefs(dvMainStore) const { nowPanelTrackInfo, nowPanelJumpInfo, mobileInPc, embeddedCallBack } =
storeToRefs(dvMainStore)
const { emitter } = useEmitt() const { emitter } = useEmitt()
const props = defineProps({ const props = defineProps({
element: { element: {
@ -54,7 +56,13 @@ const props = defineProps({
} }
}) })
const emit = defineEmits(['onChartClick', 'onDrillFilters', 'onJumpClick', 'resetLoading']) const emit = defineEmits([
'onPointClick',
'onChartClick',
'onDrillFilters',
'onJumpClick',
'resetLoading'
])
const { view, showPosition, scale, terminal } = toRefs(props) const { view, showPosition, scale, terminal } = toRefs(props)
@ -109,7 +117,7 @@ const calcData = async (view, callback) => {
callback?.() callback?.()
}) })
} else { } else {
if (['bubble-map', 'map'].includes(view.type)) { if (['bubble-map', 'map', 'flow-map'].includes(view.type)) {
await renderChart(view, callback) await renderChart(view, callback)
} }
callback?.() callback?.()
@ -134,6 +142,9 @@ const renderChart = async (view, callback?) => {
case ChartLibraryType.L7_PLOT: case ChartLibraryType.L7_PLOT:
await renderL7Plot(chart, chartView as L7PlotChartView<any, any>, callback) await renderL7Plot(chart, chartView as L7PlotChartView<any, any>, callback)
break break
case ChartLibraryType.L7:
await renderL7(chart, chartView as L7ChartView<any, any>, callback)
break
case ChartLibraryType.G2_PLOT: case ChartLibraryType.G2_PLOT:
renderG2Plot(chart, chartView as G2PlotChartView<any, any>) renderG2Plot(chart, chartView as G2PlotChartView<any, any>)
callback?.() callback?.()
@ -192,9 +203,34 @@ const renderL7Plot = async (chart: ChartObj, chartView: L7PlotChartView<any, any
}, 500) }, 500)
} }
let mapL7Timer: number
const renderL7 = async (chart: ChartObj, chartView: L7ChartView<any, any>, callback) => {
mapL7Timer && clearTimeout(mapL7Timer)
mapL7Timer = setTimeout(async () => {
myChart?.destroy()
myChart = await chartView.drawChart({
chartObj: myChart,
container: containerId,
chart: chart,
action
})
myChart?.render()
callback?.()
emit('resetLoading')
}, 500)
}
const pointClickTrans = () => {
if (embeddedCallBack.value === 'yes') {
trackClick('pointClick')
}
}
const action = param => { const action = param => {
//
state.pointParam = param.data state.pointParam = param.data
//
pointClickTrans()
//
state.linkageActiveParam = { state.linkageActiveParam = {
category: state.pointParam.data.category ? state.pointParam.data.category : 'NO_DATA', category: state.pointParam.data.category ? state.pointParam.data.category : 'NO_DATA',
name: state.pointParam.data.name ? state.pointParam.data.name : 'NO_DATA' name: state.pointParam.data.name ? state.pointParam.data.name : 'NO_DATA'
@ -246,7 +282,18 @@ const trackClick = trackAction => {
quotaList: quotaList quotaList: quotaList
} }
const clickParams = {
option: 'pointClick',
name: checkName,
viewId: view.value.id,
dimensionList: state.pointParam.data.dimensionList,
quotaList: quotaList
}
switch (trackAction) { switch (trackAction) {
case 'pointClick':
emit('onPointClick', clickParams)
break
case 'linkageAndDrill': case 'linkageAndDrill':
dvMainStore.addViewTrackFilter(linkageParam) dvMainStore.addViewTrackFilter(linkageParam)
emit('onChartClick', param) emit('onChartClick', param)
@ -307,7 +354,7 @@ defineExpose({
}) })
let resizeObserver let resizeObserver
const TOLERANCE = 0.01 const TOLERANCE = 0.01
const RESIZE_MONITOR_CHARTS = ['map', 'bubble-map'] const RESIZE_MONITOR_CHARTS = ['map', 'bubble-map', 'flow-map']
onMounted(() => { onMounted(() => {
const containerDom = document.getElementById(containerId) const containerDom = document.getElementById(containerId)
const { offsetWidth, offsetHeight } = containerDom const { offsetWidth, offsetHeight } = containerDom

View File

@ -29,7 +29,7 @@ import { useEmitt } from '@/hooks/web/useEmitt'
import { trackBarStyleCheck } from '@/utils/canvasUtils' import { trackBarStyleCheck } from '@/utils/canvasUtils'
const dvMainStore = dvMainStoreWithOut() const dvMainStore = dvMainStoreWithOut()
const { nowPanelTrackInfo, nowPanelJumpInfo, mobileInPc, canvasStyleData } = const { nowPanelTrackInfo, nowPanelJumpInfo, mobileInPc, canvasStyleData, embeddedCallBack } =
storeToRefs(dvMainStore) storeToRefs(dvMainStore)
const { emitter } = useEmitt() const { emitter } = useEmitt()
@ -66,7 +66,7 @@ const props = defineProps({
} }
}) })
const emit = defineEmits(['onChartClick', 'onDrillFilters', 'onJumpClick']) const emit = defineEmits(['onPointClick', 'onChartClick', 'onDrillFilters', 'onJumpClick'])
const { view, showPosition, scale, terminal } = toRefs(props) const { view, showPosition, scale, terminal } = toRefs(props)
@ -241,10 +241,17 @@ const handleCurrentChange = pageNum => {
const chart = { ...view.value, chartExtRequest: extReq } const chart = { ...view.value, chartExtRequest: extReq }
calcData(chart, null, false) calcData(chart, null, false)
} }
const pointClickTrans = () => {
if (embeddedCallBack.value === 'yes') {
trackClick('pointClick')
}
}
const action = param => { const action = param => {
//
state.pointParam = param state.pointParam = param
//
pointClickTrans()
//
if (trackMenu.value.length < 2) { if (trackMenu.value.length < 2) {
// //
trackClick(trackMenu.value[0]) trackClick(trackMenu.value[0])
@ -282,7 +289,19 @@ const trackClick = trackAction => {
quotaList: state.pointParam.data.quotaList, quotaList: state.pointParam.data.quotaList,
sourceType: state.pointParam.data.sourceType sourceType: state.pointParam.data.sourceType
} }
const clickParams = {
option: 'pointClick',
name: state.pointParam.data.name,
viewId: view.value.id,
dimensionList: state.pointParam.data.dimensionList,
quotaList: state.pointParam.data.quotaList
}
switch (trackAction) { switch (trackAction) {
case 'pointClick':
emit('onPointClick', clickParams)
break
case 'linkageAndDrill': case 'linkageAndDrill':
dvMainStore.addViewTrackFilter(linkageParam) dvMainStore.addViewTrackFilter(linkageParam)
emit('onChartClick', param) emit('onChartClick', param)

View File

@ -3,6 +3,7 @@ import { useI18n } from '@/hooks/web/useI18n'
import ChartComponentG2Plot from './components/ChartComponentG2Plot.vue' import ChartComponentG2Plot from './components/ChartComponentG2Plot.vue'
import DeIndicator from '@/custom-component/indicator/DeIndicator.vue' import DeIndicator from '@/custom-component/indicator/DeIndicator.vue'
import { useAppStoreWithOut } from '@/store/modules/app' import { useAppStoreWithOut } from '@/store/modules/app'
import { useEmbedded } from '@/store/modules/embedded'
import { XpackComponent } from '@/components/plugin' import { XpackComponent } from '@/components/plugin'
import { import {
computed, computed,
@ -47,6 +48,9 @@ const { t } = useI18n()
const dvMainStore = dvMainStoreWithOut() const dvMainStore = dvMainStoreWithOut()
let innerRefreshTimer = null let innerRefreshTimer = null
const appStore = useAppStoreWithOut()
const isDataEaseBi = computed(() => appStore.getIsDataEaseBi)
const isIframe = computed(() => appStore.getIsIframe)
const { nowPanelJumpInfo, publicLinkStatus, dvInfo, curComponent, canvasStyleData, mobileInPc } = const { nowPanelJumpInfo, publicLinkStatus, dvInfo, curComponent, canvasStyleData, mobileInPc } =
storeToRefs(dvMainStore) storeToRefs(dvMainStore)
@ -101,7 +105,6 @@ const props = defineProps({
}) })
const dynamicAreaId = ref('') const dynamicAreaId = ref('')
const { view, showPosition, element, active, searchCount, scale } = toRefs(props) const { view, showPosition, element, active, searchCount, scale } = toRefs(props)
const appStore = useAppStoreWithOut()
const titleShow = computed( const titleShow = computed(
() => () =>
@ -176,6 +179,7 @@ const resultCount = computed(() => {
return canvasStyleData.value.dashboard?.resultCount || null return canvasStyleData.value.dashboard?.resultCount || null
}) })
const embeddedStore = useEmbedded()
// //
const buildInnerRefreshTimer = ( const buildInnerRefreshTimer = (
refreshViewEnable = false, refreshViewEnable = false,
@ -238,7 +242,6 @@ watch([() => curComponent.value], () => {
}) })
} }
}) })
const isDataEaseBi = computed(() => appStore.getIsDataEaseBi)
const chartExtRequest = shallowRef(null) const chartExtRequest = shallowRef(null)
provide('chartExtRequest', chartExtRequest) provide('chartExtRequest', chartExtRequest)
@ -281,6 +284,21 @@ const drillJump = (index: number) => {
calcData(view.value) calcData(view.value)
} }
const onPointClick = param => {
try {
console.info('de_inner_params send')
const msg = {
type: 'de_inner_params',
sourceDvId: dvInfo.value.id,
sourceViewId: view.value.id,
message: Base64.encode(param)
}
window.parent.postMessage(msg, '*')
} catch (e) {
console.warn('de_inner_params send error')
}
}
const chartClick = param => { const chartClick = param => {
// //
const xIds = view.value.xAxis.map(ele => ele.id) const xIds = view.value.xAxis.map(ele => ele.id)
@ -332,7 +350,7 @@ const windowsJump = (url, jumpType) => {
try { try {
const newWindow = window.open(url, jumpType) const newWindow = window.open(url, jumpType)
initOpenHandler(newWindow) initOpenHandler(newWindow)
if (jumpType === '_self') { if (jumpType === '_self' && !embeddedStore.baseUrl) {
location.reload() location.reload()
} }
} catch (e) { } catch (e) {
@ -549,7 +567,7 @@ const chartAreaShow = computed(() => {
return true return true
} }
if (view.value.customAttr.map.id) { if (view.value.customAttr.map.id) {
const MAP_CHARTS = ['map', 'bubble-map'] const MAP_CHARTS = ['map', 'bubble-map', 'flow-map']
if (MAP_CHARTS.includes(view.value.type)) { if (MAP_CHARTS.includes(view.value.type)) {
return true return true
} }
@ -707,9 +725,12 @@ const titleIconStyle = computed(() => {
:view="view" :view="view"
:show-position="showPosition" :show-position="showPosition"
:element="element" :element="element"
v-else-if="showChartView(ChartLibraryType.G2_PLOT, ChartLibraryType.L7_PLOT)" v-else-if="
showChartView(ChartLibraryType.G2_PLOT, ChartLibraryType.L7_PLOT, ChartLibraryType.L7)
"
ref="chartComponent" ref="chartComponent"
@onChartClick="chartClick" @onChartClick="chartClick"
@onPointClick="onPointClick"
@onDrillFilters="onDrillFilters" @onDrillFilters="onDrillFilters"
@onJumpClick="jumpClick" @onJumpClick="jumpClick"
@resetLoading="() => (loading = false)" @resetLoading="() => (loading = false)"
@ -721,6 +742,7 @@ const titleIconStyle = computed(() => {
:element="element" :element="element"
v-else-if="showChartView(ChartLibraryType.S2)" v-else-if="showChartView(ChartLibraryType.S2)"
ref="chartComponent" ref="chartComponent"
@onPointClick="onPointClick"
@onChartClick="chartClick" @onChartClick="chartClick"
@onDrillFilters="onDrillFilters" @onDrillFilters="onDrillFilters"
@onJumpClick="jumpClick" @onJumpClick="jumpClick"

View File

@ -3,6 +3,7 @@ import { onMounted, reactive, ref, toRefs, watch, nextTick, computed } from 'vue
import { copyResource, deleteLogic, ResourceOrFolder } from '@/api/visualization/dataVisualization' import { copyResource, deleteLogic, ResourceOrFolder } from '@/api/visualization/dataVisualization'
import { ElIcon, ElMessage, ElMessageBox, ElScrollbar } from 'element-plus-secondary' import { ElIcon, ElMessage, ElMessageBox, ElScrollbar } from 'element-plus-secondary'
import { Icon } from '@/components/icon-custom' import { Icon } from '@/components/icon-custom'
import { useEmitt } from '@/hooks/web/useEmitt'
import { HandleMore } from '@/components/handle-more' import { HandleMore } from '@/components/handle-more'
import DeResourceGroupOpt from '@/views/common/DeResourceGroupOpt.vue' import DeResourceGroupOpt from '@/views/common/DeResourceGroupOpt.vue'
import { useEmbedded } from '@/store/modules/embedded' import { useEmbedded } from '@/store/modules/embedded'
@ -283,11 +284,23 @@ const operation = (cmd: string, data: BusiTreeNode, nodeType: string) => {
curCanvasType.value === 'dataV' curCanvasType.value === 'dataV'
? `#/dvCanvas?opt=copy&pid=${params.pid}&dvId=${data.data}` ? `#/dvCanvas?opt=copy&pid=${params.pid}&dvId=${data.data}`
: `#/dashboard?opt=copy&pid=${params.pid}&resourceId=${data.data}` : `#/dashboard?opt=copy&pid=${params.pid}&resourceId=${data.data}`
let embeddedBaseUrl = ''
if (isDataEaseBi.value) { if (isDataEaseBi.value) {
embeddedBaseUrl = embeddedStore.baseUrl embeddedStore.clearState()
embeddedStore.setPid(params.pid as string)
embeddedStore.setOpt('copy')
if (curCanvasType.value === 'dataV') {
embeddedStore.setDvId(data.data)
} else {
embeddedStore.setResourceId(data.data)
} }
const newWindow = window.open(embeddedBaseUrl + baseUrl, '_blank') useEmitt().emitter.emit(
'changeCurrentComponent',
curCanvasType.value === 'dataV' ? 'VisualizationEditor' : 'Dashboard'
)
return
}
const newWindow = window.open(baseUrl, '_blank')
initOpenHandler(newWindow) initOpenHandler(newWindow)
}) })
} else { } else {
@ -306,14 +319,22 @@ const addOperation = (
const baseUrl = const baseUrl =
curCanvasType.value === 'dataV' ? '#/dvCanvas?opt=create' : '#/dashboard?opt=create' curCanvasType.value === 'dataV' ? '#/dvCanvas?opt=create' : '#/dashboard?opt=create'
let newWindow = null let newWindow = null
let embeddedBaseUrl = ''
if (isDataEaseBi.value) { if (isDataEaseBi.value) {
embeddedBaseUrl = embeddedStore.baseUrl embeddedStore.clearState()
embeddedStore.setOpt('create')
if (data?.id) {
embeddedStore.setPid(data?.id as string)
}
useEmitt().emitter.emit(
'changeCurrentComponent',
curCanvasType.value === 'dataV' ? 'VisualizationEditor' : 'Dashboard'
)
return
} }
if (data?.id) { if (data?.id) {
newWindow = window.open(embeddedBaseUrl + baseUrl + `&pid=${data.id}`, '_blank') newWindow = window.open(baseUrl + `&pid=${data.id}`, '_blank')
} else { } else {
newWindow = window.open(embeddedBaseUrl + baseUrl, '_blank') newWindow = window.open(baseUrl, '_blank')
} }
initOpenHandler(newWindow) initOpenHandler(newWindow)
} else if (cmd === 'newFromTemplate') { } else if (cmd === 'newFromTemplate') {
@ -334,11 +355,20 @@ function createNewObject() {
const resourceEdit = resourceId => { const resourceEdit = resourceId => {
const baseUrl = curCanvasType.value === 'dataV' ? '#/dvCanvas?dvId=' : '#/dashboard?resourceId=' const baseUrl = curCanvasType.value === 'dataV' ? '#/dvCanvas?dvId=' : '#/dashboard?resourceId='
let embeddedBaseUrl = ''
if (isDataEaseBi.value) { if (isDataEaseBi.value) {
embeddedBaseUrl = embeddedStore.baseUrl embeddedStore.clearState()
if (curCanvasType.value === 'dataV') {
embeddedStore.setDvId(resourceId)
} else {
embeddedStore.setResourceId(resourceId)
} }
const newWindow = window.open(embeddedBaseUrl + baseUrl + resourceId, '_blank') useEmitt().emitter.emit(
'changeCurrentComponent',
curCanvasType.value === 'dataV' ? 'VisualizationEditor' : 'Dashboard'
)
return
}
const newWindow = window.open(baseUrl + resourceId, '_blank')
initOpenHandler(newWindow) initOpenHandler(newWindow)
} }
@ -354,14 +384,23 @@ const resourceCreateFinish = templateData => {
? '#/dvCanvas?opt=create&createType=template' ? '#/dvCanvas?opt=create&createType=template'
: '#/dashboard?opt=create&createType=template' : '#/dashboard?opt=create&createType=template'
let newWindow = null let newWindow = null
let embeddedBaseUrl = ''
if (isDataEaseBi.value) { if (isDataEaseBi.value) {
embeddedBaseUrl = embeddedStore.baseUrl embeddedStore.clearState()
embeddedStore.setOpt('create')
embeddedStore.setCreateType('template')
if (state.templateCreatePid) {
embeddedStore.setPid(state.templateCreatePid as unknown as string)
}
useEmitt().emitter.emit(
'changeCurrentComponent',
curCanvasType.value === 'dataV' ? 'VisualizationEditor' : 'Dashboard'
)
return
} }
if (state.templateCreatePid) { if (state.templateCreatePid) {
newWindow = window.open(embeddedBaseUrl + baseUrl + `&pid=${state.templateCreatePid}`, '_blank') newWindow = window.open(baseUrl + `&pid=${state.templateCreatePid}`, '_blank')
} else { } else {
newWindow = window.open(embeddedBaseUrl + baseUrl, '_blank') newWindow = window.open(baseUrl, '_blank')
} }
initOpenHandler(newWindow) initOpenHandler(newWindow)
} }

View File

@ -12,7 +12,7 @@ import { useRequestStoreWithOut } from '@/store/modules/request'
import { usePermissionStoreWithOut } from '@/store/modules/permission' import { usePermissionStoreWithOut } from '@/store/modules/permission'
import { useMoveLine } from '@/hooks/web/useMoveLine' import { useMoveLine } from '@/hooks/web/useMoveLine'
import { Icon } from '@/components/icon-custom' import { Icon } from '@/components/icon-custom'
import { download2AppTemplate, downloadCanvas } from '@/utils/imgUtils' import { download2AppTemplate, downloadCanvas, downloadCanvas2 } from '@/utils/imgUtils'
const dvMainStore = dvMainStoreWithOut() const dvMainStore = dvMainStoreWithOut()
const previewCanvasContainer = ref(null) const previewCanvasContainer = ref(null)
@ -98,7 +98,7 @@ const downloadH2 = type => {
downloadStatus.value = true downloadStatus.value = true
nextTick(() => { nextTick(() => {
const vueDom = previewCanvasContainer.value.querySelector('.canvas-container') const vueDom = previewCanvasContainer.value.querySelector('.canvas-container')
downloadCanvas(type, vueDom, state.dvInfo.name, () => { downloadCanvas2(type, vueDom, state.dvInfo.name, () => {
downloadStatus.value = false downloadStatus.value = false
}) })
}) })

View File

@ -34,7 +34,7 @@ const canvasCacheOutRef = ref(null)
const eventCheck = e => { const eventCheck = e => {
if (e.key === 'panel-weight' && !compareStorage(e.oldValue, e.newValue)) { if (e.key === 'panel-weight' && !compareStorage(e.oldValue, e.newValue)) {
const resourceId = embeddedStore.resourceId || router.currentRoute.value.query.resourceId const resourceId = embeddedStore.resourceId || router.currentRoute.value.query.resourceId
const { opt } = router.currentRoute.value.query const opt = embeddedStore.opt || router.currentRoute.value.query.opt
if (!(opt && opt === 'create')) { if (!(opt && opt === 'create')) {
check(wsCache.get('panel-weight'), resourceId as string, 4) check(wsCache.get('panel-weight'), resourceId as string, 4)
} }
@ -137,6 +137,13 @@ const initLocalCanvasData = () => {
dvInfo.value.pid = sourcePid dvInfo.value.pid = sourcePid
setTimeout(() => { setTimeout(() => {
snapshotStore.recordSnapshotCache() snapshotStore.recordSnapshotCache()
//
if (opt === 'copy') {
// 使
setTimeout(() => {
snapshotStore.recordSnapshotCache('renderChart')
}, 1000)
}
}, 1500) }, 1500)
} }
}) })
@ -156,7 +163,10 @@ onMounted(async () => {
window.addEventListener('storage', eventCheck) window.addEventListener('storage', eventCheck)
const resourceId = embeddedStore.resourceId || router.currentRoute.value.query.resourceId const resourceId = embeddedStore.resourceId || router.currentRoute.value.query.resourceId
const pid = embeddedStore.pid || router.currentRoute.value.query.pid const pid = embeddedStore.pid || router.currentRoute.value.query.pid
const { opt, createType, templateParams } = router.currentRoute.value.query const opt = embeddedStore.opt || router.currentRoute.value.query.opt
const createType = embeddedStore.createType || router.currentRoute.value.query.createType
const templateParams =
embeddedStore.templateParams || router.currentRoute.value.query.templateParams
const checkResult = await checkPer(resourceId) const checkResult = await checkPer(resourceId)
if (!checkResult) { if (!checkResult) {
return return
@ -269,12 +279,15 @@ onUnmounted(() => {
> >
<DbCanvasAttr></DbCanvasAttr> <DbCanvasAttr></DbCanvasAttr>
</dv-sidebar> </dv-sidebar>
<div v-show="viewEditorShow" style="height: 100%"> <div
v-show="viewEditorShow"
style="height: 100%"
:class="{ 'preview-aside': editMode === 'preview' }"
>
<view-editor <view-editor
:themes="'light'" :themes="'light'"
:view="canvasViewInfo[curComponent ? curComponent.id : 'default']" :view="canvasViewInfo[curComponent ? curComponent.id : 'default']"
:dataset-tree="state.datasetTree" :dataset-tree="state.datasetTree"
:class="{ 'preview-aside': editMode === 'preview' }"
></view-editor> ></view-editor>
</div> </div>
<dv-sidebar <dv-sidebar

View File

@ -105,11 +105,12 @@ let p = null
const XpackLoaded = () => p(true) const XpackLoaded = () => p(true)
onMounted(async () => { onMounted(async () => {
await new Promise(r => (p = r)) await new Promise(r => (p = r))
const { dvId, dvType } = router.currentRoute.value.query const { dvId, dvType, callBackFlag } = router.currentRoute.value.query
if (dvId) { if (dvId) {
loadCanvasDataAsync(dvId, dvType) loadCanvasDataAsync(dvId, dvType)
return return
} }
dvMainStore.setEmbeddedCallBack(callBackFlag || 'no')
dvMainStore.setPublicLinkStatus(props.publicLinkStatus) dvMainStore.setPublicLinkStatus(props.publicLinkStatus)
}) })

View File

@ -13,7 +13,7 @@ import { useRequestStoreWithOut } from '@/store/modules/request'
import { usePermissionStoreWithOut } from '@/store/modules/permission' import { usePermissionStoreWithOut } from '@/store/modules/permission'
import { useMoveLine } from '@/hooks/web/useMoveLine' import { useMoveLine } from '@/hooks/web/useMoveLine'
import { Icon } from '@/components/icon-custom' import { Icon } from '@/components/icon-custom'
import { download2AppTemplate, downloadCanvas } from '@/utils/imgUtils' import { download2AppTemplate, downloadCanvas, downloadCanvas2 } from '@/utils/imgUtils'
const dvMainStore = dvMainStoreWithOut() const dvMainStore = dvMainStoreWithOut()
const { dvInfo } = storeToRefs(dvMainStore) const { dvInfo } = storeToRefs(dvMainStore)
@ -86,7 +86,7 @@ const download = type => {
downloadStatus.value = true downloadStatus.value = true
setTimeout(() => { setTimeout(() => {
const vueDom = previewCanvasContainer.value.querySelector('.canvas-container') const vueDom = previewCanvasContainer.value.querySelector('.canvas-container')
downloadCanvas(type, vueDom, state.dvInfo.name, () => { downloadCanvas2(type, vueDom, state.dvInfo.name, () => {
downloadStatus.value = false downloadStatus.value = false
}) })
}, 200) }, 200)

View File

@ -43,7 +43,7 @@ const embeddedStore = useEmbedded()
const { wsCache } = useCache() const { wsCache } = useCache()
const eventCheck = e => { const eventCheck = e => {
if (e.key === 'screen-weight' && !compareStorage(e.oldValue, e.newValue)) { if (e.key === 'screen-weight' && !compareStorage(e.oldValue, e.newValue)) {
const { opt } = router.currentRoute.value.query const opt = embeddedStore.opt || router.currentRoute.value.query.opt
if (!(opt && opt === 'create')) { if (!(opt && opt === 'create')) {
check( check(
wsCache.get('screen-weight'), wsCache.get('screen-weight'),
@ -270,7 +270,10 @@ onMounted(async () => {
} }
const dvId = embeddedStore.dvId || router.currentRoute.value.query.dvId const dvId = embeddedStore.dvId || router.currentRoute.value.query.dvId
const pid = embeddedStore.pid || router.currentRoute.value.query.pid const pid = embeddedStore.pid || router.currentRoute.value.query.pid
const { opt, createType, templateParams } = router.currentRoute.value.query const templateParams =
embeddedStore.templateParams || router.currentRoute.value.query.templateParams
const createType = embeddedStore.createType || router.currentRoute.value.query.createType
const opt = embeddedStore.opt || router.currentRoute.value.query.opt
const checkResult = await checkPer(dvId) const checkResult = await checkPer(dvId)
if (!checkResult) { if (!checkResult) {
return return
@ -429,12 +432,15 @@ eventBus.on('handleNew', handleNew)
> >
<canvas-attr></canvas-attr> <canvas-attr></canvas-attr>
</dv-sidebar> </dv-sidebar>
<div v-show="viewsPropertiesShow" style="height: 100%"> <div
v-show="viewsPropertiesShow"
style="height: 100%"
:class="{ 'preview-aside': editMode === 'preview' }"
>
<editor <editor
:view="canvasViewInfo[curComponent ? curComponent.id : 'default']" :view="canvasViewInfo[curComponent ? curComponent.id : 'default']"
themes="dark" themes="dark"
:dataset-tree="state.datasetTree" :dataset-tree="state.datasetTree"
:class="{ 'preview-aside': editMode === 'preview' }"
></editor> ></editor>
</div> </div>
</div> </div>

View File

@ -327,6 +327,10 @@ const expChangeHandler = exp => {
}) })
} }
const beforeClose = async done => { const beforeClose = async done => {
if (!shareEnable.value) {
done()
return
}
const pwdValid = validatePwdFormat() const pwdValid = validatePwdFormat()
const uuidValid = await validateUuid() const uuidValid = await validateUuid()
if (pwdValid && uuidValid) { if (pwdValid && uuidValid) {

View File

@ -167,6 +167,10 @@ watch(
} }
) )
const hideShare = async () => { const hideShare = async () => {
if (!shareEnable.value) {
popoverVisible.value = false
return
}
const pwdValid = validatePwdFormat() const pwdValid = validatePwdFormat()
const uuidValid = await validateUuid() const uuidValid = await validateUuid()
if (pwdValid && uuidValid) { if (pwdValid && uuidValid) {

View File

@ -174,6 +174,7 @@ import { imgUrlTrans } from '@/utils/imgUtils'
import CategoryTemplateV2 from '@/views/template-market/component/CategoryTemplateV2.vue' import CategoryTemplateV2 from '@/views/template-market/component/CategoryTemplateV2.vue'
import { interactiveStoreWithOut } from '@/store/modules/interactive' import { interactiveStoreWithOut } from '@/store/modules/interactive'
import { XpackComponent } from '@/components/plugin' import { XpackComponent } from '@/components/plugin'
import { useEmitt } from '@/hooks/web/useEmitt'
import { Base64 } from 'js-base64' import { Base64 } from 'js-base64'
const { t } = useI18n() const { t } = useI18n()
const { wsCache } = useCache() const { wsCache } = useCache()
@ -434,14 +435,29 @@ const apply = template => {
'&templateParams=' + '&templateParams=' +
encodeURIComponent(Base64.encode(JSON.stringify(templateTemplate))) encodeURIComponent(Base64.encode(JSON.stringify(templateTemplate)))
let newWindow = null let newWindow = null
let embeddedBaseUrl = ''
if (isDataEaseBi.value) { if (isDataEaseBi.value) {
embeddedBaseUrl = embeddedStore.baseUrl embeddedStore.clearState()
embeddedStore.setCreateType('template')
embeddedStore.setTemplateParams(
encodeURIComponent(Base64.encode(JSON.stringify(templateTemplate)))
)
embeddedStore.setOpt('create')
if (state.pid) {
embeddedStore.setPid(state.pid)
}
useEmitt().emitter.emit(
'changeCurrentComponent',
['dataV', 'SCREEN'].includes(state.dvCreateForm.nodeType)
? 'VisualizationEditor'
: 'Dashboard'
)
return
} }
if (state.pid) { if (state.pid) {
newWindow = window.open(embeddedBaseUrl + baseUrl + `&pid=${state.pid}`, '_blank') newWindow = window.open(baseUrl + `&pid=${state.pid}`, '_blank')
} else { } else {
newWindow = window.open(embeddedBaseUrl + baseUrl, '_blank') newWindow = window.open(baseUrl, '_blank')
} }
initOpenHandler(newWindow) initOpenHandler(newWindow)
} }

View File

@ -313,7 +313,7 @@ const confirmCustomTime = () => {
} }
watch(searchTable, val => { watch(searchTable, val => {
state.tableData = tableList.filter(ele => ele.name.toLowerCase().includes(val.toLowerCase())) state.tableData = tableList.filter(ele => ele.tableName.toLowerCase().includes(val.toLowerCase()))
}) })
const editeSave = () => { const editeSave = () => {
const union = [] const union = []

View File

@ -693,7 +693,10 @@ defineExpose({
<el-option v-for="item in schemas" :key="item" :label="item" :value="item" /> <el-option v-for="item in schemas" :key="item" :label="item" :value="item" />
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item :label="t('datasource.extra_params')"> <el-form-item
:label="t('datasource.extra_params')"
v-if="form.configuration.urlType !== 'jdbcUrl'"
>
<el-input <el-input
:placeholder="t('common.inputText') + t('datasource.extra_params')" :placeholder="t('common.inputText') + t('datasource.extra_params')"
v-model="form.configuration.extraParams" v-model="form.configuration.extraParams"
@ -760,7 +763,7 @@ defineExpose({
/> />
</el-form-item> </el-form-item>
<el-form-item <el-form-item
v-if="form.type == 'oracle'" v-if="form.type == 'oracle' && form.configuration.urlType !== 'jdbcUrl'"
:label="t('datasource.connection_mode')" :label="t('datasource.connection_mode')"
prop="configuration.connectionType" prop="configuration.connectionType"
> >

View File

@ -1073,7 +1073,7 @@ const getMenuList = (val: boolean) => {
placement="top" placement="top"
> >
<el-button <el-button
@click.stop="createDataset(scope.row.name)" @click.stop="createDataset(scope.row.tableName)"
text text
v-permission="['dataset']" v-permission="['dataset']"
> >

@ -1 +1 @@
Subproject commit eac4bdc0d387c44088dbc7585a09f61b8e45ea12 Subproject commit 690faa2e32bf71f23fdda04a83c3b7a1ed0d368c

53
pom.xml
View File

@ -11,15 +11,19 @@
<modules> <modules>
<module>sdk</module> <module>sdk</module>
</modules> </modules>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>${spring-boot.version}</version>
</parent>
<properties> <properties>
<dataease.version>2.6.1</dataease.version> <dataease.version>2.6.1</dataease.version>
<java.version>17</java.version> <java.version>17</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-cloud-alibaba.version>2022.0.0.0</spring-cloud-alibaba.version> <spring-cloud-alibaba.version>2023.0.1.0</spring-cloud-alibaba.version>
<spring-cloud.version>2022.0.0</spring-cloud.version> <spring-cloud.version>2023.0.1</spring-cloud.version>
<spring-boot.version>3.0.2</spring-boot.version> <spring-boot.version>3.3.0</spring-boot.version>
<maven-compiler-plugin.version>3.9.0</maven-compiler-plugin.version>
<maven.compiler.source>17</maven.compiler.source> <maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target> <maven.compiler.target>17</maven.compiler.target>
<mybatis-plus.version>3.5.6</mybatis-plus.version> <mybatis-plus.version>3.5.6</mybatis-plus.version>
@ -30,17 +34,13 @@
<antlr.version>3.5.2</antlr.version> <antlr.version>3.5.2</antlr.version>
<java-jwt.version>3.12.1</java-jwt.version> <java-jwt.version>3.12.1</java-jwt.version>
<velocity.version>2.3</velocity.version> <velocity.version>2.3</velocity.version>
<maven.antrun.version>3.1.0</maven.antrun.version>
<ehcache.version>3.10.8</ehcache.version> <ehcache.version>3.10.8</ehcache.version>
<bcprov.version>1.78</bcprov.version> <bcprov.version>1.78</bcprov.version>
<junit.version>4.13.2</junit.version> <junit.version>4.13.2</junit.version>
<httpclient.version>4.5.14</httpclient.version> <httpclient.version>4.5.14</httpclient.version>
<httpcore.version>4.4.16</httpcore.version> <httpcore.version>4.4.16</httpcore.version>
<easyexcel.version>3.3.2</easyexcel.version> <easyexcel.version>3.3.4</easyexcel.version>
<maven.surefire.version>3.1.2</maven.surefire.version>
<maven.clean.version>3.2.0</maven.clean.version>
<flatten-maven.version>1.3.0</flatten-maven.version> <flatten-maven.version>1.3.0</flatten-maven.version>
<maven.resources.version>3.3.1</maven.resources.version>
<maven.exec.version>3.1.0</maven.exec.version> <maven.exec.version>3.1.0</maven.exec.version>
<guava.version>33.0.0-jre</guava.version> <guava.version>33.0.0-jre</guava.version>
<selenium-java.version>4.19.1</selenium-java.version> <selenium-java.version>4.19.1</selenium-java.version>
@ -48,6 +48,7 @@
<mysql-connector-j.version>8.2.0</mysql-connector-j.version> <mysql-connector-j.version>8.2.0</mysql-connector-j.version>
<itextpdf.version>8.0.4</itextpdf.version> <itextpdf.version>8.0.4</itextpdf.version>
<flexmark.version>0.62.2</flexmark.version> <flexmark.version>0.62.2</flexmark.version>
<mybatis-spring.version>3.0.3</mybatis-spring.version>
</properties> </properties>
<dependencyManagement> <dependencyManagement>
@ -73,6 +74,11 @@
<type>pom</type> <type>pom</type>
<scope>import</scope> <scope>import</scope>
</dependency> </dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>${mybatis-spring.version}</version>
</dependency>
<dependency> <dependency>
<groupId>com.baomidou</groupId> <groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId> <artifactId>mybatis-plus-boot-starter</artifactId>
@ -144,35 +150,6 @@
<version>${spring-boot.version}</version> <version>${spring-boot.version}</version>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>${maven.antrun.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven.surefire.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-clean-plugin</artifactId>
<version>${maven.clean.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>${maven.resources.version}</version>
</plugin>
<plugin> <plugin>
<groupId>org.codehaus.mojo</groupId> <groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId> <artifactId>exec-maven-plugin</artifactId>

View File

@ -0,0 +1,41 @@
package io.dataease.api.exportCenter;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import io.dataease.api.exportCenter.vo.ExportTaskDTO;
import io.dataease.auth.DeApiPath;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
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 static io.dataease.constant.AuthResourceEnum.DATASOURCE;
@Tag(name = "数据导出中心")
@ApiSupport(order = 971)
@DeApiPath(value = "/exportCenter", rt = DATASOURCE)
public interface ExportCenterApi {
@PostMapping("/exportTasks/{status}")
public List<ExportTaskDTO> exportTasks(@PathVariable String status) ;
@GetMapping("/delete/{id}")
public void delete(@PathVariable String id);
@PostMapping("/delete")
public void delete(@RequestBody List<String> ids);
@PostMapping("/deleteAll/{type}")
public void deleteAll(@PathVariable String type);
@GetMapping("/download/{id}")
public void download(@PathVariable String id, HttpServletResponse response) throws Exception ;
@PostMapping("/retry/{id}")
public void retry(@PathVariable String id);
}

View File

@ -0,0 +1,33 @@
package io.dataease.api.exportCenter.vo;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data;
@Data
public class ExportTaskDTO {
@JsonSerialize(using= ToStringSerializer.class)
private String id;
@JsonSerialize(using= ToStringSerializer.class)
private Long userId;
private String fileName;
private Double fileSize;
private String fileSizeUnit;
private String exportFrom;
private String exportStatus;
private String exportFromType;
private Long exportTime;
private String exportProgress;
private String exportMachineName;
private String exportFromName;
}

View File

@ -93,8 +93,14 @@ public class HttpClientUtil {
httpGet.addHeader(key, header.get(key)); httpGet.addHeader(key, header.get(key));
} }
HttpResponse response = httpClient.execute(httpGet); HttpResponse response = httpClient.execute(httpGet);
int statusCode = response.getStatusLine().getStatusCode(); if (response.getStatusLine().getStatusCode() >= 400) {
return statusCode <= 400; String msg = EntityUtils.toString(response.getEntity(), config.getCharset());
if (StringUtils.isEmpty(msg)) {
msg = "StatusCode: " + response.getStatusLine().getStatusCode();
}
throw new Exception(msg);
}
return true;
} catch (Exception e) { } catch (Exception e) {
logger.error("HttpClient查询失败", e); logger.error("HttpClient查询失败", e);
throw new DEException(SYSTEM_INNER_ERROR.code(), "HttpClient查询失败: " + e.getMessage()); throw new DEException(SYSTEM_INNER_ERROR.code(), "HttpClient查询失败: " + e.getMessage());