commit
294bc1123e
@ -86,7 +86,7 @@ public class CasStrategy implements UrlPatternMatcherStrategy {
|
||||
try {
|
||||
DecodedJWT jwt = JWT.decode(token);
|
||||
Claim forShot = jwt.getClaim("forShot");
|
||||
return ObjectUtils.isNotEmpty(forShot) && forShot.asBoolean();
|
||||
return ObjectUtils.isNotEmpty(forShot) && !forShot.isNull() && forShot.asBoolean();
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e.getMessage());
|
||||
return false;
|
||||
|
||||
@ -9,6 +9,7 @@ import io.dataease.commons.model.AuthURD;
|
||||
import io.dataease.commons.utils.*;
|
||||
import io.dataease.dto.PermissionProxy;
|
||||
import io.dataease.dto.chart.ViewOption;
|
||||
import io.dataease.dto.panel.PanelGroupDTO;
|
||||
import io.dataease.ext.ExtTaskMapper;
|
||||
import io.dataease.job.sechedule.ScheduleManager;
|
||||
import io.dataease.job.sechedule.strategy.TaskHandler;
|
||||
@ -31,6 +32,7 @@ import io.dataease.plugins.xpack.wecom.dto.entity.WecomMsgResult;
|
||||
import io.dataease.plugins.xpack.wecom.service.WecomXpackService;
|
||||
import io.dataease.service.chart.ChartViewService;
|
||||
import io.dataease.service.chart.ViewExportExcel;
|
||||
import io.dataease.service.panel.PanelGroupService;
|
||||
import io.dataease.service.sys.SysUserService;
|
||||
import io.dataease.service.system.EmailService;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
@ -269,7 +271,9 @@ public class EmailTaskHandler extends TaskHandler implements Job {
|
||||
emailService.sendWithImageAndFiles(recipients, emailTemplateDTO.getTitle(), contentStr, bytes, files);
|
||||
} else {
|
||||
bytes = emailXpackService.printPdf(url, token, xpackPixelEntity, false, true);
|
||||
emailService.sendPdfWithFiles(recipients, emailTemplateDTO.getTitle(), contentStr, bytes, files);
|
||||
PanelGroupDTO panelInfo = CommonBeanFactory.getBean(PanelGroupService.class).findOne(panelId);
|
||||
String pdfFileName = panelInfo.getName() + "pdf";
|
||||
emailService.sendPdfWithFiles(recipients, emailTemplateDTO.getTitle(), contentStr, bytes, files, pdfFileName);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
|
||||
@ -520,7 +520,7 @@ public class ChartViewService {
|
||||
QueryProvider qp = ProviderFactory.getQueryProvider(ds.getType());
|
||||
if (StringUtils.equalsIgnoreCase(table.getType(), DatasetType.DB.name())) {
|
||||
datasourceRequest.setTable(dataTableInfoDTO.getTable());
|
||||
if (StringUtils.equalsIgnoreCase("text", view.getType()) || StringUtils.equalsIgnoreCase("gauge", view.getType()) || StringUtils.equalsIgnoreCase("liquid", view.getType())) {
|
||||
if (StringUtils.equalsIgnoreCase("gauge", view.getType()) || StringUtils.equalsIgnoreCase("liquid", view.getType())) {
|
||||
datasourceRequest.setQuery(qp.getSQLSummary(dataTableInfoDTO.getTable(), yAxis, fieldCustomFilter, rowPermissionsTree, extFilterList, view, ds));
|
||||
} else if (StringUtils.containsIgnoreCase(view.getType(), "stack")) {
|
||||
datasourceRequest.setQuery(qp.getSQLStack(dataTableInfoDTO.getTable(), xAxis, yAxis, fieldCustomFilter, rowPermissionsTree, extFilterList, extStack, ds, view));
|
||||
@ -539,7 +539,7 @@ public class ChartViewService {
|
||||
} else if (StringUtils.equalsIgnoreCase(table.getType(), DatasetType.SQL.name())) {
|
||||
String sql = dataTableInfoDTO.isBase64Encryption() ? new String(java.util.Base64.getDecoder().decode(dataTableInfoDTO.getSql())) : dataTableInfoDTO.getSql();
|
||||
sql = handleVariable(sql, requestList, qp, table, ds);
|
||||
if (StringUtils.equalsIgnoreCase("text", view.getType()) || StringUtils.equalsIgnoreCase("gauge", view.getType()) || StringUtils.equalsIgnoreCase("liquid", view.getType())) {
|
||||
if (StringUtils.equalsIgnoreCase("gauge", view.getType()) || StringUtils.equalsIgnoreCase("liquid", view.getType())) {
|
||||
datasourceRequest.setQuery(qp.getSQLSummaryAsTmp(sql, yAxis, fieldCustomFilter, rowPermissionsTree, extFilterList, view));
|
||||
} else if (StringUtils.containsIgnoreCase(view.getType(), "stack")) {
|
||||
datasourceRequest.setQuery(qp.getSQLAsTmpStack(sql, xAxis, yAxis, fieldCustomFilter, rowPermissionsTree, extFilterList, extStack, view));
|
||||
@ -559,7 +559,7 @@ public class ChartViewService {
|
||||
DataTableInfoDTO dt = gson.fromJson(table.getInfo(), DataTableInfoDTO.class);
|
||||
List<DataSetTableUnionDTO> list = dataSetTableUnionService.listByTableId(dt.getList().get(0).getTableId());
|
||||
String sql = dataSetTableService.getCustomSQLDatasource(dt, list, ds);
|
||||
if (StringUtils.equalsIgnoreCase("text", view.getType()) || StringUtils.equalsIgnoreCase("gauge", view.getType()) || StringUtils.equalsIgnoreCase("liquid", view.getType())) {
|
||||
if (StringUtils.equalsIgnoreCase("gauge", view.getType()) || StringUtils.equalsIgnoreCase("liquid", view.getType())) {
|
||||
datasourceRequest.setQuery(qp.getSQLSummaryAsTmp(sql, yAxis, fieldCustomFilter, rowPermissionsTree, extFilterList, view));
|
||||
} else if (StringUtils.containsIgnoreCase(view.getType(), "stack")) {
|
||||
datasourceRequest.setQuery(qp.getSQLAsTmpStack(sql, xAxis, yAxis, fieldCustomFilter, rowPermissionsTree, extFilterList, extStack, view));
|
||||
@ -580,7 +580,7 @@ public class ChartViewService {
|
||||
Map<String, Object> sqlMap = dataSetTableService.getUnionSQLDatasource(dt, ds);
|
||||
String sql = (String) sqlMap.get("sql");
|
||||
|
||||
if (StringUtils.equalsIgnoreCase("text", view.getType()) || StringUtils.equalsIgnoreCase("gauge", view.getType()) || StringUtils.equalsIgnoreCase("liquid", view.getType())) {
|
||||
if (StringUtils.equalsIgnoreCase("gauge", view.getType()) || StringUtils.equalsIgnoreCase("liquid", view.getType())) {
|
||||
datasourceRequest.setQuery(qp.getSQLSummaryAsTmp(sql, yAxis, fieldCustomFilter, rowPermissionsTree, extFilterList, view));
|
||||
} else if (StringUtils.containsIgnoreCase(view.getType(), "stack")) {
|
||||
datasourceRequest.setQuery(qp.getSQLAsTmpStack(sql, xAxis, yAxis, fieldCustomFilter, rowPermissionsTree, extFilterList, extStack, view));
|
||||
@ -610,7 +610,7 @@ public class ChartViewService {
|
||||
String tableName = "ds_" + table.getId().replaceAll("-", "_");
|
||||
datasourceRequest.setTable(tableName);
|
||||
QueryProvider qp = ProviderFactory.getQueryProvider(ds.getType());
|
||||
if (StringUtils.equalsIgnoreCase("text", view.getType()) || StringUtils.equalsIgnoreCase("gauge", view.getType()) || StringUtils.equalsIgnoreCase("liquid", view.getType())) {
|
||||
if (StringUtils.equalsIgnoreCase("gauge", view.getType()) || StringUtils.equalsIgnoreCase("liquid", view.getType())) {
|
||||
datasourceRequest.setQuery(qp.getSQLSummary(tableName, yAxis, fieldCustomFilter, rowPermissionsTree, extFilterList, view, ds));
|
||||
} else if (StringUtils.containsIgnoreCase(view.getType(), "stack")) {
|
||||
datasourceRequest.setQuery(qp.getSQLStack(tableName, xAxis, yAxis, fieldCustomFilter, rowPermissionsTree, extFilterList, extStack, ds, view));
|
||||
@ -781,6 +781,28 @@ public class ChartViewService {
|
||||
}
|
||||
break;
|
||||
case "text":
|
||||
xAxis = new ArrayList<>();
|
||||
yAxis = yAxis.stream().filter(item -> chartViewFieldNameList.contains(item.getDataeaseName()) || (!desensitizationList.keySet().contains(item.getDataeaseName()) && dataeaseNames.contains(item.getDataeaseName()))).collect(Collectors.toList());
|
||||
if (CollectionUtils.isEmpty(yAxis)) {
|
||||
return emptyChartViewDTO(view);
|
||||
}
|
||||
ChartFieldCompareDTO compareCalc = yAxis.get(0).getCompareCalc();
|
||||
boolean isYoy = StringUtils.isNotEmpty(compareCalc.getType()) && !StringUtils.equalsIgnoreCase(compareCalc.getType(),"none");
|
||||
if(isYoy){
|
||||
List<DatasetTableField> xField = fields.stream().filter(item->StringUtils.equalsIgnoreCase(item.getId(),compareCalc.getField())).collect(Collectors.toList());
|
||||
if(CollectionUtils.isNotEmpty(xField)){
|
||||
ChartViewFieldDTO xFieldChartViewFieldDTO = new ChartViewFieldDTO();
|
||||
org.springframework.beans.BeanUtils.copyProperties(xField.get(0), xFieldChartViewFieldDTO);
|
||||
xAxis.add(xFieldChartViewFieldDTO);
|
||||
xAxis.get(0).setSort("desc");
|
||||
if(Objects.isNull(compareCalc.getCustom())){
|
||||
xAxis.get(0).setDateStyle("y_M_d");
|
||||
}else{
|
||||
xAxis.get(0).setDateStyle(compareCalc.getCustom().getTimeType());
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "gauge":
|
||||
case "liquid":
|
||||
xAxis = new ArrayList<>();
|
||||
@ -1229,7 +1251,7 @@ public class ChartViewService {
|
||||
QueryProvider qp = ProviderFactory.getQueryProvider(ds.getType());
|
||||
if (StringUtils.equalsIgnoreCase(table.getType(), DatasetType.DB.name())) {
|
||||
datasourceRequest.setTable(dataTableInfoDTO.getTable());
|
||||
if (StringUtils.equalsAnyIgnoreCase(view.getType(), "text", "gauge", "liquid")) {
|
||||
if (StringUtils.equalsAnyIgnoreCase(view.getType(), "gauge", "liquid")) {
|
||||
querySql = qp.getSQLSummary(dataTableInfoDTO.getTable(), yAxis, fieldCustomFilter, rowPermissionsTree, extFilterList, view, ds);
|
||||
if (isYOY) {
|
||||
yoySql = qp.getSQLSummary(dataTableInfoDTO.getTable(), yAxis, fieldCustomFilter, rowPermissionsTree, yoyFilterList, view, ds);
|
||||
@ -1269,7 +1291,7 @@ public class ChartViewService {
|
||||
} else if (StringUtils.equalsIgnoreCase(table.getType(), DatasetType.SQL.name())) {
|
||||
String sql = dataTableInfoDTO.isBase64Encryption() ? new String(java.util.Base64.getDecoder().decode(dataTableInfoDTO.getSql())) : dataTableInfoDTO.getSql();
|
||||
sql = handleVariable(sql, chartExtRequest, qp, table, ds);
|
||||
if (StringUtils.equalsAnyIgnoreCase(view.getType(), "text", "gauge", "liquid")) {
|
||||
if (StringUtils.equalsAnyIgnoreCase(view.getType(), "gauge", "liquid")) {
|
||||
querySql = qp.getSQLSummaryAsTmp(sql, yAxis, fieldCustomFilter, rowPermissionsTree, extFilterList, view);
|
||||
if (isYOY) {
|
||||
yoySql = qp.getSQLSummaryAsTmp(sql, yAxis, fieldCustomFilter, rowPermissionsTree, yoyFilterList, view);
|
||||
@ -1311,7 +1333,7 @@ public class ChartViewService {
|
||||
DataTableInfoDTO dt = gson.fromJson(table.getInfo(), DataTableInfoDTO.class);
|
||||
List<DataSetTableUnionDTO> list = dataSetTableUnionService.listByTableId(dt.getList().get(0).getTableId());
|
||||
String sql = dataSetTableService.getCustomSQLDatasource(dt, list, ds);
|
||||
if (StringUtils.equalsAnyIgnoreCase(view.getType(), "text", "gauge", "liquid")) {
|
||||
if (StringUtils.equalsAnyIgnoreCase(view.getType(), "gauge", "liquid")) {
|
||||
querySql = qp.getSQLSummaryAsTmp(sql, yAxis, fieldCustomFilter, rowPermissionsTree, extFilterList, view);
|
||||
if (isYOY) {
|
||||
yoySql = qp.getSQLSummaryAsTmp(sql, yAxis, fieldCustomFilter, rowPermissionsTree, yoyFilterList, view);
|
||||
@ -1352,7 +1374,7 @@ public class ChartViewService {
|
||||
DataTableInfoDTO dt = gson.fromJson(table.getInfo(), DataTableInfoDTO.class);
|
||||
Map<String, Object> sqlMap = dataSetTableService.getUnionSQLDatasource(dt, ds);
|
||||
String sql = (String) sqlMap.get("sql");
|
||||
if (StringUtils.equalsAnyIgnoreCase(view.getType(), "text", "gauge", "liquid")) {
|
||||
if (StringUtils.equalsAnyIgnoreCase(view.getType(), "gauge", "liquid")) {
|
||||
querySql = qp.getSQLSummaryAsTmp(sql, yAxis, fieldCustomFilter, rowPermissionsTree, extFilterList, view);
|
||||
if (isYOY) {
|
||||
yoySql = qp.getSQLSummaryAsTmp(sql, yAxis, fieldCustomFilter, rowPermissionsTree, yoyFilterList, view);
|
||||
@ -1433,7 +1455,7 @@ public class ChartViewService {
|
||||
String tableName = "ds_" + table.getId().replaceAll("-", "_");
|
||||
datasourceRequest.setTable(tableName);
|
||||
QueryProvider qp = ProviderFactory.getQueryProvider(ds.getType());
|
||||
if (StringUtils.equalsAnyIgnoreCase(view.getType(), "text", "gauge", "liquid")) {
|
||||
if (StringUtils.equalsAnyIgnoreCase(view.getType(), "gauge", "liquid")) {
|
||||
datasourceRequest.setQuery(qp.getSQLSummary(tableName, yAxis, fieldCustomFilter, rowPermissionsTree, extFilterList, view, ds));
|
||||
if (isYOY) {
|
||||
yoySql = qp.getSQLSummary(tableName, yAxis, fieldCustomFilter, rowPermissionsTree, yoyFilterList, view, ds);
|
||||
|
||||
@ -7,6 +7,7 @@ import io.dataease.auth.service.ProxyAuthService;
|
||||
import io.dataease.commons.constants.ParamConstants;
|
||||
import io.dataease.commons.constants.SysLogConstants;
|
||||
import io.dataease.commons.utils.*;
|
||||
import io.dataease.controller.chart.ChartViewController;
|
||||
import io.dataease.controller.request.chart.ChartExtRequest;
|
||||
import io.dataease.controller.request.dataset.DataSetExportRequest;
|
||||
import io.dataease.controller.request.dataset.DataSetTableRequest;
|
||||
@ -120,6 +121,8 @@ public class ExportCenterService {
|
||||
@Resource
|
||||
private ChartViewService chartViewService;
|
||||
@Resource
|
||||
private ChartViewController chartViewController;
|
||||
@Resource
|
||||
private DataSetGroupService dataSetGroupService;
|
||||
@Resource
|
||||
private PanelGroupService panelGroupService;
|
||||
@ -332,7 +335,7 @@ public class ExportCenterService {
|
||||
componentFilterInfo.setProxy(request.getProxy());
|
||||
componentFilterInfo.setUser(request.getUserId());
|
||||
componentFilterInfo.setDownloadType(request.getDownloadType());
|
||||
ChartViewDTO chartViewInfo = chartViewService.getData(request.getViewId(), componentFilterInfo);
|
||||
ChartViewDTO chartViewInfo = chartViewController.getData(request.getViewId(), null, componentFilterInfo);
|
||||
List<Object[]> tableRow = (List) chartViewInfo.getData().get("sourceData");
|
||||
request.setDetails(tableRow);
|
||||
if ("dataset".equals(request.getDownloadType())) {
|
||||
|
||||
@ -83,7 +83,7 @@ public class EmailService {
|
||||
}
|
||||
|
||||
|
||||
public void sendPdfWithFiles(String to, String title, String content, byte[] bytes, List<File> files) {
|
||||
public void sendPdfWithFiles(String to, String title, String content, byte[] bytes, List<File> files, String pdfFileName) {
|
||||
if (ArrayUtils.isEmpty(bytes)) {
|
||||
send(to, title, content);
|
||||
return;
|
||||
@ -98,7 +98,7 @@ public class EmailService {
|
||||
MimeMessage mimeMessage = driver.createMimeMessage();
|
||||
try {
|
||||
multipart = addContent(multipart, content);
|
||||
multipart = addPdf(multipart, bytes);
|
||||
multipart = addPdf(multipart, bytes, pdfFileName);
|
||||
if (CollectionUtils.isNotEmpty(files)) {
|
||||
multipart = addFiles(multipart, files);
|
||||
}
|
||||
@ -165,11 +165,11 @@ public class EmailService {
|
||||
return multipart;
|
||||
}
|
||||
|
||||
private MimeMultipart addPdf(MimeMultipart multipart, byte[] bytes) throws Exception {
|
||||
private MimeMultipart addPdf(MimeMultipart multipart, byte[] bytes, String pdfFileName) throws Exception {
|
||||
MimeBodyPart attach = new MimeBodyPart();
|
||||
ByteArrayDataSource fileDataSource = new ByteArrayDataSource(bytes, "application/pdf");
|
||||
attach.setDataHandler(new DataHandler(fileDataSource));
|
||||
attach.setFileName(MimeUtility.encodeText("panel.pdf", "gb2312", null));
|
||||
attach.setFileName(MimeUtility.encodeText(pdfFileName, "gb2312", null));
|
||||
multipart.addBodyPart(attach);
|
||||
multipart.setSubType("related");
|
||||
return multipart;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "dataease",
|
||||
"version": "1.18.24",
|
||||
"version": "1.18.25",
|
||||
"description": "dataease front",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
@ -45,6 +45,7 @@
|
||||
"echarts": "^5.0.1",
|
||||
"element-resize-detector": "^1.2.3",
|
||||
"element-ui": "2.15.7",
|
||||
"exceljs": "^4.4.0",
|
||||
"file-saver": "^2.0.5",
|
||||
"fit2cloud-ui": "^1.8.0",
|
||||
"flv.js": "^1.6.2",
|
||||
|
||||
@ -89,6 +89,16 @@
|
||||
@click.stop="exportExcelDownload()"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
v-if="exportFormattedExcelShow"
|
||||
:title="$t('chart.export_formatted_excel')"
|
||||
@click.stop="exportFormattedExcel()"
|
||||
>
|
||||
<svg-icon
|
||||
style="font-size: 14px; color: white; margin-right: 3px"
|
||||
icon-class="ds-excel-format"
|
||||
/>
|
||||
</span>
|
||||
<setting-menu
|
||||
v-if="activeModel==='edit'"
|
||||
style="float: right;height: 24px!important;"
|
||||
@ -207,6 +217,7 @@ import eventBus from '@/components/canvas/utils/eventBus'
|
||||
import { hasDataPermission } from '@/utils/permission'
|
||||
import { exportExcelDownload } from '@/components/canvas/utils/utils'
|
||||
import { Button } from 'element-ui'
|
||||
import { exportPivotExcel } from '@/views/chart/chart/common/common_table'
|
||||
|
||||
export default {
|
||||
components: { Background, LinkJumpSet, FieldsList, SettingMenu, LinkageField, MapLayerController },
|
||||
@ -298,6 +309,13 @@ export default {
|
||||
exportExcelShow() {
|
||||
return this.detailsShow && hasDataPermission('export', this.$store.state.panel.panelInfo.privileges) && this.chart && this.chart.dataFrom !== 'template'
|
||||
},
|
||||
exportFormattedExcelShow() {
|
||||
return this.detailsShow &&
|
||||
hasDataPermission('export', this.$store.state.panel.panelInfo.privileges) &&
|
||||
this.chart &&
|
||||
this.chart.dataFrom !== 'template' &&
|
||||
JSON.parse(this.chart.customAttr).size?.tableLayoutMode !== 'tree'
|
||||
},
|
||||
enlargeShow() {
|
||||
return this.curComponent.type === 'view' && this.curComponent.propValue.innerType && this.curComponent.propValue.innerType !== 'richTextView' && !this.curComponent.propValue.innerType.includes('table')
|
||||
},
|
||||
@ -493,7 +511,7 @@ export default {
|
||||
Button,
|
||||
{
|
||||
props: {
|
||||
type: 'text',
|
||||
type: 'text'
|
||||
},
|
||||
class: 'btn-text',
|
||||
on: {
|
||||
@ -522,7 +540,7 @@ export default {
|
||||
Button,
|
||||
{
|
||||
props: {
|
||||
type: 'text',
|
||||
type: 'text'
|
||||
},
|
||||
class: 'btn-text',
|
||||
on: {
|
||||
@ -542,6 +560,13 @@ export default {
|
||||
exportExcelDownload() {
|
||||
exportExcelDownload(this.chart, null, null, null, null, null, this.exportDataCb)
|
||||
},
|
||||
exportFormattedExcel() {
|
||||
const instance = this.$store.state.chart.tableInstance[this.chart.id]
|
||||
if (!instance) {
|
||||
return
|
||||
}
|
||||
exportPivotExcel(instance, this.chart)
|
||||
},
|
||||
auxiliaryMatrixChange() {
|
||||
if (this.curComponent.auxiliaryMatrix) {
|
||||
this.curComponent.auxiliaryMatrix = false
|
||||
|
||||
@ -601,6 +601,7 @@ export default {
|
||||
'cfilters': {
|
||||
handler: function(val1, val2) {
|
||||
if (isChange(val1, val2) && !this.isFirstLoad) {
|
||||
this.currentPage.page = 1
|
||||
this.getData(this.element.propValue.viewId)
|
||||
this.getDataLoading = true
|
||||
}
|
||||
@ -782,7 +783,7 @@ export default {
|
||||
Button,
|
||||
{
|
||||
props: {
|
||||
type: 'text',
|
||||
type: 'text'
|
||||
},
|
||||
class: 'btn-text',
|
||||
on: {
|
||||
@ -811,7 +812,7 @@ export default {
|
||||
Button,
|
||||
{
|
||||
props: {
|
||||
type: 'text',
|
||||
type: 'text'
|
||||
},
|
||||
class: 'btn-text',
|
||||
on: {
|
||||
@ -999,7 +1000,7 @@ export default {
|
||||
},
|
||||
getData(id, cache = true, dataBroadcast = false) {
|
||||
// Err1001 已删除的不在重复请求
|
||||
if (this.requestStatus === 'waiting' || (this.message && this.message.indexOf('Err1001')> -1)) {
|
||||
if (this.requestStatus === 'waiting' || (this.message && this.message.indexOf('Err1001') > -1)) {
|
||||
return
|
||||
}
|
||||
if (id) {
|
||||
|
||||
@ -420,10 +420,14 @@ export default {
|
||||
if (!token && linkToken) {
|
||||
method = linkMultFieldValues
|
||||
}
|
||||
method({
|
||||
const param = {
|
||||
fieldIds: this.element.options.attrs.fieldId.split(this.separator),
|
||||
sort: this.element.options.attrs.sort
|
||||
}).then(res => {
|
||||
}
|
||||
if (this.panelInfo.proxy) {
|
||||
param.userId = this.panelInfo.proxy
|
||||
}
|
||||
method(param).then(res => {
|
||||
this.data = this.optionData(res.data)
|
||||
bus.$emit('valid-values-change', true)
|
||||
cb && cb()
|
||||
|
||||
@ -364,10 +364,14 @@ export default {
|
||||
if (!token && linkToken) {
|
||||
method = linkMultFieldValues
|
||||
}
|
||||
method({
|
||||
const param = {
|
||||
fieldIds: this.element.options.attrs.fieldId.split(','),
|
||||
sort: this.element.options.attrs.sort, keyword: this.keyWord
|
||||
}).then(res => {
|
||||
}
|
||||
if (this.panelInfo.proxy) {
|
||||
param.userId = this.panelInfo.proxy
|
||||
}
|
||||
method(param).then(res => {
|
||||
this.data = this.optionData(res.data)
|
||||
this.changeInputStyle()
|
||||
if (this.element.options.attrs.multiple) {
|
||||
|
||||
@ -293,10 +293,14 @@ export default {
|
||||
if (!token && linkToken) {
|
||||
method = linkMappingFieldValues
|
||||
}
|
||||
method({
|
||||
const param = {
|
||||
fieldIds: this.element.options.attrs.fieldId.split(','),
|
||||
sort: this.element.options.attrs.sort
|
||||
}).then(res => {
|
||||
}
|
||||
if (this.panelInfo.proxy) {
|
||||
param.userId = this.panelInfo.proxy
|
||||
}
|
||||
method(param).then(res => {
|
||||
this.data = this.optionData(res.data)
|
||||
this.$nextTick(() => {
|
||||
this.$refs.deSelectTree && this.$refs.deSelectTree.treeDataUpdateFun(this.data)
|
||||
|
||||
1
core/frontend/src/icons/svg/ds-excel-format.svg
Normal file
1
core/frontend/src/icons/svg/ds-excel-format.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg t="1726630203120" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9498" width="32" height="32"><path d="M901.347556 805.546667c-16.839111 0-30.72 12.970667-32.085334 29.354666v118.328889H106.268444v-119.921778c0.227556-1.592889 0.455111-3.185778 0.455112-5.006222V468.764444c0-1.592889-0.227556-3.413333-0.455112-5.006222V70.314667h552.049778v171.804444s3.185778 30.037333 31.857778 30.037333H869.262222v172.714667c1.820444 16.156444 15.473778 28.899556 32.085334 28.899556 14.791111 0 27.306667-10.012444 31.175111-23.438223V254.862222l-1.137778-1.137778c-1.137778-5.233778-3.640889-14.336-8.192-17.976888l-225.28-227.100445s-4.096-4.551111-16.839111-6.826667c-0.910222-0.682667-2.048-1.365333-2.958222-1.820444H95.573333C66.218667 0 42.552889 23.665778 42.552889 53.020444v463.985778c0.227556 0.455111 0.227556 1.137778 0.455111 1.592889V829.895111c-0.227556 0.682667-0.455111 1.137778-0.455111 1.820445v139.264c0 29.127111 23.665778 53.020444 53.020444 53.020444h783.928889c29.354667 0 53.020444-23.665778 53.020445-53.020444v-141.994667c-3.868444-13.425778-16.384-23.438222-31.175111-23.438222zM836.494222 218.680889l-130.389333 0.910222-0.910222-132.664889 131.299555 131.754667z" p-id="9499"></path><path d="M963.015111 611.214222l-95.573333-114.460444c-20.935111-14.336-34.133333 2.958222-34.133334 2.958222-13.880889 16.611556-0.682667 41.187556-0.682666 41.187556l56.206222 67.356444H604.842667c-16.611556 0-30.037333 16.156444-30.037334 35.953778 0 19.797333 13.425778 35.953778 30.037334 35.953778h282.851555l-58.254222 70.314666c-12.743111 27.989333 5.688889 43.235556 5.688889 43.235556 13.198222 16.611556 33.450667-1.365333 33.450667-1.365334l12.060444-14.563555 8.192-9.102222 83.057778-100.124445c25.713778-31.175111-8.874667-57.344-8.874667-57.344zM178.858667 276.935111c-16.611556 0-29.809778 14.336-29.809778 31.857778s13.425778 31.857778 29.809778 31.857778h370.460444c16.611556 0 29.809778-14.336 29.809778-31.857778s-13.425778-31.857778-29.809778-31.857778H178.858667zM780.743111 435.313778c0-18.887111-19.569778-34.133333-43.463111-34.133334H198.428444c-24.120889 0-43.463111 15.246222-43.463111 34.133334s19.569778 34.133333 43.463111 34.133333h538.624c24.120889 0 43.690667-15.246222 43.690667-34.133333zM544.768 155.420444H174.307556c-16.611556 0-29.809778 14.336-29.809778 31.857778s13.425778 31.857778 29.809778 31.857778h370.460444c16.611556 0 29.809778-14.336 29.809778-31.857778 0.227556-17.521778-13.198222-31.857778-29.809778-31.857778zM427.804444 531.797333l-84.423111 84.423111-84.423111-84.423111H142.222222l142.677334 142.904889-142.677334 142.904889h116.736l84.423111-84.423111 84.423111 84.423111h116.963556l-142.904889-142.904889 142.904889-142.904889z" p-id="9500"></path></svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
11
core/frontend/src/icons/svg/stock-line.svg
Normal file
11
core/frontend/src/icons/svg/stock-line.svg
Normal file
@ -0,0 +1,11 @@
|
||||
<svg width="80" height="56" viewBox="0 0 80 56" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="15" y="27" width="2" height="22" fill="#BBBFC4"/>
|
||||
<rect x="31" y="17" width="2" height="28" fill="#646A73"/>
|
||||
<rect x="47" y="4" width="2" height="41" fill="#BBBFC4"/>
|
||||
<rect x="63" y="4" width="2" height="45" fill="#646A73"/>
|
||||
<path d="M11 37.5C11 37.2239 11.2239 37 11.5 37H20.5C20.7761 37 21 37.2239 21 37.5V46.5C21 46.7761 20.7761 47 20.5 47H11.5C11.2239 47 11 46.7761 11 46.5V37.5Z" fill="#BBBFC4"/>
|
||||
<path d="M27 27.5C27 27.2239 27.2239 27 27.5 27H36.5C36.7761 27 37 27.2239 37 27.5V38.5C37 38.7761 36.7761 39 36.5 39H27.5C27.2239 39 27 38.7761 27 38.5V27.5Z" fill="#646A73"/>
|
||||
<path d="M43 8.5C43 8.22386 43.2239 8 43.5 8H52.5C52.7761 8 53 8.22386 53 8.5V31.5C53 31.7761 52.7761 32 52.5 32H43.5C43.2239 32 43 31.7761 43 31.5V8.5Z" fill="#BBBFC4"/>
|
||||
<path d="M59 8.5C59 8.22386 59.2239 8 59.5 8H68.5C68.7761 8 69 8.22386 69 8.5V45.5C69 45.7761 68.7761 46 68.5 46H59.5C59.2239 46 59 45.7761 59 45.5V8.5Z" fill="#646A73"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M5 51H76L76 54H4V51H5Z" fill="#646A73"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@ -1412,6 +1412,7 @@ export default {
|
||||
chart_bar_stack_horizontal: 'Stack Horizontal Bar',
|
||||
chart_percentage_bar_stack_horizontal: 'Horizontal Percentage Stack Bar',
|
||||
chart_bidirectional_bar: 'Bidirectional Bar',
|
||||
chart_stock_line: 'Stock Line',
|
||||
chart_line: 'Base Line',
|
||||
chart_line_stack: 'Stack Line',
|
||||
chart_pie: 'Pie',
|
||||
@ -1858,7 +1859,9 @@ export default {
|
||||
polynomial_regression: 'Polynomial regression',
|
||||
show_summary: 'Show summary',
|
||||
summary_label: 'Summary label',
|
||||
tip: 'Tip'
|
||||
tip: 'Tip',
|
||||
pivot_export_empty_fields: 'Can not export without row dimension or quota',
|
||||
export_formatted_excel: 'Export formatted excel'
|
||||
},
|
||||
dataset: {
|
||||
scope_edit: 'Effective only when editing',
|
||||
|
||||
@ -1411,6 +1411,7 @@ export default {
|
||||
chart_bar_stack_horizontal: '橫嚮堆疊柱狀圖',
|
||||
chart_percentage_bar_stack_horizontal: '橫嚮百分比柱狀圖',
|
||||
chart_bidirectional_bar: '對稱柱狀圖',
|
||||
chart_stock_line: 'K 線圖',
|
||||
chart_line: '基礎摺線圖',
|
||||
chart_line_stack: '堆疊摺線圖',
|
||||
chart_pie: '餅圖',
|
||||
@ -1851,7 +1852,9 @@ export default {
|
||||
polynomial_regression: '多項式擬合',
|
||||
show_summary: '顯示總計',
|
||||
summary_label: '總計標籤',
|
||||
tip: '提示'
|
||||
tip: '提示',
|
||||
pivot_export_empty_fields: '行維度或指標維度為空不可導出',
|
||||
export_formatted_excel: '導出 Excel (帶格式)'
|
||||
},
|
||||
dataset: {
|
||||
scope_edit: '僅編輯時生效',
|
||||
|
||||
@ -1408,6 +1408,7 @@ export default {
|
||||
chart_bar_stack_horizontal: '横向堆叠柱状图',
|
||||
chart_percentage_bar_stack_horizontal: '横向百分比柱状图',
|
||||
chart_bidirectional_bar: '对称柱状图',
|
||||
chart_stock_line: 'K 线图',
|
||||
chart_line: '基础折线图',
|
||||
chart_line_stack: '堆叠折线图',
|
||||
chart_pie: '饼图',
|
||||
@ -1848,7 +1849,9 @@ export default {
|
||||
polynomial_regression: '多项式拟合',
|
||||
show_summary: '显示总计',
|
||||
summary_label: '总计标签',
|
||||
tip: '提示'
|
||||
tip: '提示',
|
||||
pivot_export_empty_fields: '行维度或指标维度为空不可导出',
|
||||
export_formatted_excel: '导出 Excel (带格式)'
|
||||
},
|
||||
dataset: {
|
||||
goto: ', 前往 ',
|
||||
|
||||
@ -5,7 +5,8 @@ const getDefaultState = () => {
|
||||
sceneId: {},
|
||||
viewId: null,
|
||||
tableId: {},
|
||||
chartSceneData: {}
|
||||
chartSceneData: {},
|
||||
tableInstance: {}
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,6 +30,9 @@ const mutations = {
|
||||
},
|
||||
setChartSceneData: (state, chartSceneData) => {
|
||||
state.chartSceneData = chartSceneData
|
||||
},
|
||||
setTableInstance: (state, { viewId, tableInstance }) => {
|
||||
state.tableInstance[viewId] = tableInstance
|
||||
}
|
||||
}
|
||||
|
||||
@ -50,6 +54,9 @@ const actions = {
|
||||
},
|
||||
setChartSceneData: ({ commit }, chartSceneData) => {
|
||||
commit('setChartSceneData', chartSceneData)
|
||||
},
|
||||
setTableInstance: ({ commit }, { viewId, tableInstance }) => {
|
||||
commit('setTableInstance', { viewId, tableInstance })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -114,6 +114,7 @@ service.setTimeOut = time => {
|
||||
|
||||
// 请根据实际需求修改
|
||||
service.interceptors.response.use(response => {
|
||||
checkCasRedirect(response)
|
||||
response.config.loading && tryHideLoading(store.getters.currentPath)
|
||||
checkAuth(response)
|
||||
Vue.prototype.$currentHttpRequestList.delete(response.config.url)
|
||||
@ -191,4 +192,17 @@ const checkAuth = response => {
|
||||
store.dispatch('user/setLinkToken', linkToken)
|
||||
}
|
||||
}
|
||||
const checkCasRedirect = (response) => {
|
||||
if (!response || !response.data) {
|
||||
return
|
||||
}
|
||||
const resData = response.data
|
||||
const routine = resData.hasOwnProperty('success')
|
||||
const redirectUrl = response?.request?.responseURL
|
||||
if (resData && !routine && resData.startsWith('<!') && redirectUrl?.includes('cas/login')) {
|
||||
store.dispatch('user/logout').finally(() => {
|
||||
location.reload()
|
||||
})
|
||||
}
|
||||
}
|
||||
export default service
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Column, Bar, BidirectionalBar } from '@antv/g2plot'
|
||||
import {Column, Bar, BidirectionalBar, Mix} from '@antv/g2plot'
|
||||
import {
|
||||
getTheme,
|
||||
getLabel,
|
||||
@ -17,6 +17,14 @@ import {
|
||||
import { antVCustomColor, getColors, handleEmptyDataStrategy, hexColorToRGBA, handleStackSort } from '@/views/chart/chart/util'
|
||||
import { cloneDeep, find, groupBy, each } from 'lodash-es'
|
||||
import { formatterItem, valueFormatter } from '@/views/chart/chart/formatter'
|
||||
import {
|
||||
calculateMinMax,
|
||||
calculateMovingAverage,
|
||||
configXAxis, configYAxis,
|
||||
configBasicStyle,
|
||||
configTooltip,
|
||||
registerEvent, customConfigEmptyDataStrategy
|
||||
} from "@/views/chart/chart/bar/stock_line_util";
|
||||
|
||||
export function baseBarOptionAntV(container, chart, action, isGroup, isStack) {
|
||||
// theme
|
||||
@ -587,3 +595,201 @@ export function baseBidirectionalBarOptionAntV(container, chart, action, isGroup
|
||||
configPlotTooltipEvent(chart, plot)
|
||||
return plot
|
||||
}
|
||||
|
||||
export function stockLineOptionAntV(container, chart, action) {
|
||||
if (!chart.data?.data?.length) {
|
||||
return
|
||||
}
|
||||
const xAxis = JSON.parse(chart.xaxis)
|
||||
const yAxis = JSON.parse(chart.yaxis)
|
||||
if (yAxis.length !== 4) {
|
||||
return
|
||||
}
|
||||
const theme = getTheme(chart)
|
||||
const legend = getLegend(chart)
|
||||
const basicStyle = JSON.parse(chart.customAttr).color
|
||||
const colors = []
|
||||
const alpha = basicStyle.alpha
|
||||
basicStyle.colors.forEach(ele => {
|
||||
colors.push(hexColorToRGBA(ele, alpha))
|
||||
})
|
||||
const data = cloneDeep(chart.data?.tableRow ?? [])
|
||||
|
||||
// 时间字段
|
||||
const xAxisDataeaseName = xAxis[0].dataeaseName
|
||||
const averages = [5, 10, 20, 60, 120, 180]
|
||||
const legendItems = [
|
||||
{
|
||||
name: '日K',
|
||||
value: 'k',
|
||||
marker: {
|
||||
symbol: (x, y, r) => {
|
||||
const width = r * 1
|
||||
const height = r
|
||||
return [
|
||||
// 矩形框
|
||||
['M', x - width - 1 / 2, y - height / 2],
|
||||
['L', x + width + 1 / 2, y - height / 2],
|
||||
['L', x + width + 1 / 2, y + height / 2],
|
||||
['L', x - width - 1 / 2, y + height / 2],
|
||||
['Z'],
|
||||
// 中线
|
||||
['M', x, y + 10 / 2],
|
||||
['L', x, y - 10 / 2]
|
||||
]
|
||||
},
|
||||
style: { fill: 'red', stroke: 'red', lineWidth: 2 }
|
||||
}
|
||||
}
|
||||
]
|
||||
// 计算均线数据
|
||||
const averagesLineData = new Map()
|
||||
averages.forEach(item => {
|
||||
averagesLineData.set('ma' + item, calculateMovingAverage(data, item, chart))
|
||||
})
|
||||
|
||||
// 将均线数据设置到主数据中
|
||||
data.forEach((item) => {
|
||||
const date = item[xAxisDataeaseName]
|
||||
for (const [key, value] of averagesLineData) {
|
||||
item[key] = value.find(m => m[xAxisDataeaseName] === date)?.value
|
||||
}
|
||||
})
|
||||
|
||||
const averageLines = []
|
||||
let index = 0
|
||||
const start = 0.5
|
||||
const end = 1
|
||||
const startIndex = Math.floor(start * data.length)
|
||||
const endIndex = Math.ceil(end * data.length)
|
||||
const filteredData = data.slice(startIndex, endIndex)
|
||||
const { maxValue, minValue } = calculateMinMax(filteredData)
|
||||
for (const key of averagesLineData.keys()) {
|
||||
index++
|
||||
averageLines.push({
|
||||
type: 'line',
|
||||
top: true,
|
||||
options: {
|
||||
smooth: false,
|
||||
xField: xAxisDataeaseName,
|
||||
yField: key,
|
||||
color: colors[index - 1],
|
||||
xAxis: null,
|
||||
yAxis: {
|
||||
label: false,
|
||||
min: minValue,
|
||||
max: maxValue,
|
||||
grid: null,
|
||||
line: null
|
||||
},
|
||||
lineStyle: {
|
||||
lineWidth: 2
|
||||
}
|
||||
}
|
||||
})
|
||||
legendItems.push({
|
||||
name: key.toUpperCase(),
|
||||
value: key,
|
||||
marker: { symbol: 'hyphen', style: { stroke: colors[index - 1], lineWidth: 2 } }
|
||||
})
|
||||
}
|
||||
const axis =JSON.parse(chart.xaxis) ?? []
|
||||
let dateFormat
|
||||
const dateSplit = axis[0]?.datePattern === 'date_split' ? '/' : '-'
|
||||
switch (axis[0]?.dateStyle) {
|
||||
case 'y':
|
||||
dateFormat = 'YYYY'
|
||||
break
|
||||
case 'y_M':
|
||||
dateFormat = 'YYYY' + dateSplit + 'MM'
|
||||
break
|
||||
case 'y_M_d':
|
||||
dateFormat = 'YYYY' + dateSplit + 'MM' + dateSplit + 'DD'
|
||||
break
|
||||
case 'y_M_d_H':
|
||||
dateFormat = 'YYYY' + dateSplit + 'MM' + dateSplit + 'DD' + ' HH'
|
||||
break
|
||||
case 'y_M_d_H_m':
|
||||
dateFormat = 'YYYY' + dateSplit + 'MM' + dateSplit + 'DD' + ' HH:mm'
|
||||
break
|
||||
case 'y_M_d_H_m_s':
|
||||
dateFormat = 'YYYY' + dateSplit + 'MM' + dateSplit + 'DD' + ' HH:mm:ss'
|
||||
break
|
||||
default:
|
||||
dateFormat = 'YYYY-MM-dd HH:mm:ss'
|
||||
}
|
||||
let option = {
|
||||
data,
|
||||
theme,
|
||||
appendPadding: getPadding(chart),
|
||||
slider: {
|
||||
start: 0.5,
|
||||
end: 1
|
||||
},
|
||||
plots: [
|
||||
...averageLines,
|
||||
{
|
||||
type: 'stock',
|
||||
top: true,
|
||||
options: {
|
||||
meta: {
|
||||
[xAxisDataeaseName]: {
|
||||
mask: dateFormat
|
||||
}
|
||||
},
|
||||
stockStyle: {
|
||||
stroke: 'black',
|
||||
lineWidth: 0.5
|
||||
},
|
||||
xField: xAxisDataeaseName,
|
||||
yField: [
|
||||
yAxis[0].dataeaseName,
|
||||
yAxis[1].dataeaseName,
|
||||
yAxis[2].dataeaseName,
|
||||
yAxis[3].dataeaseName
|
||||
],
|
||||
legend: !legend?false:{
|
||||
position: 'top',
|
||||
custom: true,
|
||||
items: legendItems
|
||||
},
|
||||
fallingFill: hexColorToRGBA('#ef5350', alpha),
|
||||
risingFill: hexColorToRGBA('#26a69a', alpha),
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
option = configBasicStyle(chart, option)
|
||||
option = configXAxis(chart, option)
|
||||
option = configYAxis(chart, option)
|
||||
option = configTooltip(chart, option)
|
||||
option = customConfigEmptyDataStrategy(chart,option)
|
||||
const plot = new Mix(container, option)
|
||||
registerEvent(data, plot, averagesLineData)
|
||||
plot.on('schema:click', evt => {
|
||||
const selectSchema = evt.data.data[xAxisDataeaseName]
|
||||
const paramData = cloneDeep(chart.data?.data ?? [])
|
||||
const selectData = paramData.filter(item => item.field === selectSchema)
|
||||
const quotaList = []
|
||||
selectData.forEach(item => {
|
||||
quotaList.push({ ...item.quotaList[0], value: item.value })
|
||||
})
|
||||
if (selectData.length) {
|
||||
const param = {
|
||||
x: evt.x,
|
||||
y: evt.y,
|
||||
data: {
|
||||
data: {
|
||||
...evt.data.data,
|
||||
value: quotaList[0].value,
|
||||
name: selectSchema,
|
||||
dimensionList: selectData[0].dimensionList,
|
||||
quotaList: quotaList
|
||||
}
|
||||
}
|
||||
}
|
||||
action(param)
|
||||
}
|
||||
})
|
||||
return plot
|
||||
}
|
||||
|
||||
423
core/frontend/src/views/chart/chart/bar/stock_line_util.js
Normal file
423
core/frontend/src/views/chart/chart/bar/stock_line_util.js
Normal file
@ -0,0 +1,423 @@
|
||||
import {getXAxis, getYAxis} from '@/views/chart/chart/common/common_antv'
|
||||
import { valueFormatter } from '@/views/chart/chart/formatter'
|
||||
import {cloneDeep} from "lodash";
|
||||
import {handleEmptyDataStrategy} from "@/views/chart/chart/util";
|
||||
|
||||
/**
|
||||
* 计算收盘价平均值
|
||||
* @param data
|
||||
* @param dayCount
|
||||
* @param chart
|
||||
*/
|
||||
export const calculateMovingAverage = (data, dayCount, chart) => {
|
||||
const xAxis = JSON.parse(chart.xaxis)
|
||||
const yAxis = JSON.parse(chart.yaxis)
|
||||
// 时间字段
|
||||
const xAxisDataeaseName = xAxis[0].dataeaseName
|
||||
// 收盘价字段
|
||||
const yAxisDataeaseName = yAxis[1].dataeaseName
|
||||
const result = []
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
if (i < dayCount) {
|
||||
result.push({
|
||||
[xAxisDataeaseName]: data[i][xAxisDataeaseName],
|
||||
value: null
|
||||
})
|
||||
} else {
|
||||
const sum = data
|
||||
.slice(i - dayCount + 1, i + 1)
|
||||
.reduce((sum, item) => sum + item[yAxisDataeaseName], 0)
|
||||
result.push({
|
||||
[xAxisDataeaseName]: data[i][xAxisDataeaseName],
|
||||
value: parseFloat((sum / dayCount).toFixed(3))
|
||||
})
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据集合中对象属性值的最大最小值
|
||||
* @param data
|
||||
*/
|
||||
export const calculateMinMax = data => {
|
||||
return data.reduce(
|
||||
(acc, current) => {
|
||||
// 获取 current 对象的所有属性值
|
||||
const values = Object.values(current)
|
||||
// 过滤出数字值
|
||||
const numericValues = values.filter(value => typeof value === 'number') ?? []
|
||||
// 找到 current 对象的数字属性值中的最大值和最小值
|
||||
// 如果存在数字值,则计算当前对象的最大值和最小值
|
||||
if (numericValues.length > 0) {
|
||||
const currentMax = Math.max(...numericValues)
|
||||
const currentMin = Math.min(...numericValues)
|
||||
// 更新全局最大值和最小值
|
||||
acc.maxValue = Math.max(acc.maxValue, currentMax)
|
||||
acc.minValue = Math.min(acc.minValue, currentMin)
|
||||
}
|
||||
return acc
|
||||
},
|
||||
{ maxValue: Number.NEGATIVE_INFINITY, minValue: Number.POSITIVE_INFINITY }
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册图表事件
|
||||
* @param data
|
||||
* @param plot
|
||||
* @param averagesLineData
|
||||
*/
|
||||
export const registerEvent = (data, plot, averagesLineData) => {
|
||||
// 监听图例点击事件,显示隐藏
|
||||
let risingVisible = true
|
||||
plot.on('legend-item:click', evt => {
|
||||
const { value } = evt.target.get('delegateObject').item
|
||||
if (value === 'k') {
|
||||
risingVisible = !risingVisible
|
||||
plot.chart.geometries.forEach(geom => {
|
||||
if (geom.type === 'schema') {
|
||||
geom.changeVisible(risingVisible)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
const lines = plot.chart.geometries.filter(item => item.type === 'line')
|
||||
const points = plot.chart.geometries.filter(item => item.type === 'point')
|
||||
let lineIndex = 0
|
||||
for (const key of averagesLineData.keys()) {
|
||||
lineIndex++
|
||||
if (key === value) {
|
||||
lines[lineIndex - 1].changeVisible(!lines[lineIndex - 1].visible)
|
||||
points[lineIndex - 1].changeVisible(!points[lineIndex - 1].visible)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
// 监听图表渲染事件
|
||||
plot.on('afterrender', e => {
|
||||
let first = false
|
||||
if (plot.chart.options.slider.start === 0.5 && plot.chart.options.slider.end === 1) {
|
||||
first = true
|
||||
}
|
||||
if (e.view?.options?.scales) {
|
||||
const startIndex = Math.floor(0.5 * data.length)
|
||||
const endIndex = Math.ceil(1 * data.length)
|
||||
const filteredData = data.slice(startIndex, endIndex)
|
||||
const { maxValue, minValue } = calculateMinMax(
|
||||
first ? filteredData : e.view.filteredData
|
||||
)
|
||||
const a = e.view.options.scales
|
||||
Object.keys(a).forEach(item => {
|
||||
if (a[item].max) {
|
||||
a[item].max = maxValue
|
||||
}
|
||||
if (a[item].min) {
|
||||
a[item].min = minValue
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
// 监听图例组点击事件,设置缩放
|
||||
plot.on('legend-item-group:click', e => {
|
||||
if (e.view?.options?.scales) {
|
||||
const { maxValue, minValue } = calculateMinMax(e.view.filteredData)
|
||||
const a = e.view.options.scales
|
||||
Object.keys(a).forEach(item => {
|
||||
if (a[item].max) {
|
||||
a[item].max = maxValue
|
||||
}
|
||||
if (a[item].min) {
|
||||
a[item].min = minValue
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
// 监听滑块事件,设置缩放
|
||||
plot.on('slider:valuechanged', e => {
|
||||
const start = e.gEvent.currentTarget.cfg.component.cfg.start
|
||||
const end = e.gEvent.currentTarget.cfg.component.cfg.end
|
||||
plot.chart.options.slider.start = start
|
||||
plot.chart.options.slider.end = end
|
||||
const startIndex = Math.floor(start * data.length)
|
||||
const endIndex = Math.ceil(end * data.length)
|
||||
const filteredData = data.slice(startIndex, endIndex)
|
||||
const { maxValue, minValue } = calculateMinMax(filteredData)
|
||||
const a = e.view.options.scales
|
||||
Object.keys(a).forEach(item => {
|
||||
if (a[item].max) {
|
||||
a[item].max = maxValue
|
||||
}
|
||||
if (a[item].min) {
|
||||
a[item].min = minValue
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export const configBasicStyle = (chart, options) => {
|
||||
// size
|
||||
const customAttr = JSON.parse(chart.customAttr)
|
||||
const s = JSON.parse(JSON.stringify(customAttr.size))
|
||||
const smooth = s.lineSmooth
|
||||
const point = {
|
||||
size: s.lineSymbolSize,
|
||||
shape: s.lineSymbol
|
||||
}
|
||||
const lineStyle = {
|
||||
lineWidth: s.lineWidth
|
||||
}
|
||||
const plots = []
|
||||
options.plots.forEach(item => {
|
||||
if (item.type === 'line') {
|
||||
plots.push({ ...item, options: { ...item.options, smooth, point, lineStyle } })
|
||||
}
|
||||
if (item.type === 'stock') {
|
||||
plots.push({ ...item })
|
||||
}
|
||||
})
|
||||
return {
|
||||
...options,
|
||||
plots
|
||||
}
|
||||
}
|
||||
|
||||
export const configTooltip = (chart, options)=> {
|
||||
const tooltipAttr = JSON.parse(chart.customAttr).tooltip
|
||||
const newPlots = []
|
||||
const linePlotList = options.plots.filter(item => item.type === 'line')
|
||||
linePlotList.forEach(item => {
|
||||
newPlots.push(item)
|
||||
})
|
||||
const stockPlot = options.plots.filter(item => item.type === 'stock')[0]
|
||||
if (!tooltipAttr.show) {
|
||||
const stockOption = {
|
||||
...stockPlot.options,
|
||||
tooltip: {
|
||||
showContent: false
|
||||
}
|
||||
}
|
||||
newPlots.push({ ...stockPlot, options: stockOption })
|
||||
return {
|
||||
...options,
|
||||
plots: newPlots
|
||||
}
|
||||
}
|
||||
|
||||
const showFiled = chart.data.fields
|
||||
const yAxis = cloneDeep(JSON.parse(chart.yaxis))
|
||||
const customTooltipItems = originalItems => {
|
||||
const formattedItems = originalItems.map(item => {
|
||||
const fieldObj = showFiled.find(q => q.dataeaseName === item.name)
|
||||
const displayName = fieldObj?.chartShowName || fieldObj?.name || item.name
|
||||
const formattedName = displayName.startsWith('ma') ? displayName.toUpperCase() : displayName
|
||||
if(!yAxis[0].formatterCfg){
|
||||
yAxis[0].formatterCfg = {
|
||||
type: 'value', // auto,value,percent
|
||||
unit: 1, // 换算单位
|
||||
suffix: '', // 单位后缀
|
||||
decimalCount: 3, // 小数位数
|
||||
thousandSeparator: true// 千分符
|
||||
}
|
||||
}
|
||||
if(yAxis[0].formatterCfg.type === 'auto'){
|
||||
yAxis[0].formatterCfg.type = 'value'
|
||||
yAxis[0].formatterCfg.decimalCount = 3
|
||||
}
|
||||
const formattedValue = valueFormatter(item.value, yAxis[0].formatterCfg)
|
||||
return {
|
||||
...item,
|
||||
name: formattedName,
|
||||
value: formattedValue,
|
||||
color: item.color
|
||||
}
|
||||
})
|
||||
|
||||
const hasKLine = formattedItems.some(item => !item.name.startsWith('MA'))
|
||||
const kLines = formattedItems.filter(item => !item.name.startsWith('MA'))
|
||||
return hasKLine
|
||||
? [
|
||||
{ name: '日K', value: '', marker: true, color: kLines[0]?.color },
|
||||
...kLines,
|
||||
...formattedItems.filter(item => item.name.startsWith('MA'))
|
||||
]
|
||||
: formattedItems
|
||||
}
|
||||
const formatTooltipItem = (item) => {
|
||||
const size = item.name.startsWith('MA') || !item.value ? 10 : 5
|
||||
const markerMarginRight = item.name.startsWith('MA') || !item.value ? 5 : 9
|
||||
const markerMarginLeft = item.name.startsWith('MA') || !item.value ? 0 : 2
|
||||
return `
|
||||
<li style="display: flex; align-items: center; margin-bottom: 10px;">
|
||||
<div>
|
||||
<span
|
||||
style="
|
||||
background-color: ${item.color};
|
||||
width: ${size}px;
|
||||
height: ${size}px;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
margin-right: ${markerMarginRight}px;
|
||||
margin-left: ${markerMarginLeft}px;
|
||||
"></span>
|
||||
</div>
|
||||
<div style="display: flex; justify-content: space-between; width: 100%;">
|
||||
<span style="margin-right: 15px;">${item.name}</span>
|
||||
<span>${item.name.startsWith('MA') && item.value === '0' ? '-' : item.value}</span>
|
||||
</div>
|
||||
</li>
|
||||
`
|
||||
}
|
||||
const generateCustomTooltipContent = (title, items) => {
|
||||
return `
|
||||
<div style="padding: 10px 0;">
|
||||
<div style="margin-bottom: 10px;">${title}</div>
|
||||
<ul style="list-style: none; padding: 0;">
|
||||
${items.map(formatTooltipItem).join('')}
|
||||
</ul>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
const stockOption = {
|
||||
...stockPlot.options,
|
||||
tooltip: {
|
||||
showMarkers: true,
|
||||
showCrosshairs: true,
|
||||
showNil: true,
|
||||
crosshairs: {
|
||||
follow: true,
|
||||
text: (axisType, value, data) => {
|
||||
if (axisType === 'y') {
|
||||
return { content: value ? value.toFixed(0) : value }
|
||||
}
|
||||
return { content: data[0].title, position: 'end' }
|
||||
}
|
||||
},
|
||||
showContent: true,
|
||||
customItems: customTooltipItems,
|
||||
customContent: generateCustomTooltipContent
|
||||
}
|
||||
}
|
||||
newPlots.push({ ...stockPlot, options: stockOption })
|
||||
return {
|
||||
...options,
|
||||
plots: newPlots
|
||||
}
|
||||
}
|
||||
|
||||
export const configXAxis = (chart, options) => {
|
||||
const xAxisOptions = getXAxis(chart)
|
||||
if (!xAxisOptions) {
|
||||
options.plots.forEach(item => {
|
||||
if(item.type === 'stock'){
|
||||
item.options.xAxis = false
|
||||
}
|
||||
})
|
||||
return options
|
||||
}
|
||||
const newPlots = []
|
||||
const linePlotList = options.plots.filter(item => item.type === 'line')
|
||||
|
||||
const stockPlot = options.plots.filter(item => item.type === 'stock')[0]
|
||||
const newStockPlot = {
|
||||
...stockPlot,
|
||||
options: {
|
||||
...stockPlot.options,
|
||||
xAxis: xAxisOptions
|
||||
? {
|
||||
...stockPlot.options['xAxis'],
|
||||
...xAxisOptions
|
||||
}
|
||||
: {
|
||||
label: false,
|
||||
line: null
|
||||
}
|
||||
}
|
||||
}
|
||||
newPlots.push(newStockPlot)
|
||||
linePlotList.forEach(item => {
|
||||
newPlots.push(item)
|
||||
})
|
||||
return {
|
||||
...options,
|
||||
plots: newPlots
|
||||
}
|
||||
}
|
||||
|
||||
export const configYAxis = (chart, options) => {
|
||||
const yAxisOptions = getYAxis(chart)
|
||||
if (!yAxisOptions) {
|
||||
options.plots.forEach(item => {
|
||||
if(item.type === 'stock'){
|
||||
item.options.yAxis = false
|
||||
}
|
||||
})
|
||||
return options
|
||||
}
|
||||
const yAxis = JSON.parse(chart.yaxis)
|
||||
const newPlots = []
|
||||
const linePlotList = options.plots.filter(item => item.type === 'line')
|
||||
|
||||
const stockPlot = options.plots.filter(item => item.type === 'stock')[0]
|
||||
let label = false
|
||||
if (yAxisOptions.label) {
|
||||
label = {
|
||||
...yAxisOptions.label
|
||||
}
|
||||
}
|
||||
const newStockPlot = {
|
||||
...stockPlot,
|
||||
options: {
|
||||
...stockPlot.options,
|
||||
yAxis: label
|
||||
? {
|
||||
...stockPlot.options['yAxis'],
|
||||
...yAxisOptions,
|
||||
label
|
||||
}
|
||||
: {
|
||||
label,
|
||||
grid: null,
|
||||
line: null
|
||||
}
|
||||
}
|
||||
}
|
||||
newPlots.push(newStockPlot)
|
||||
linePlotList.forEach(item => {
|
||||
newPlots.push(item)
|
||||
})
|
||||
return {
|
||||
...options,
|
||||
plots: newPlots
|
||||
}
|
||||
}
|
||||
|
||||
export const customConfigEmptyDataStrategy = (chart, options) => {
|
||||
const { data } = options
|
||||
if (!data?.length) {
|
||||
return options
|
||||
}
|
||||
const strategy = JSON.parse(chart.senior)?.functionCfg?.emptyDataStrategy
|
||||
if (strategy === 'ignoreData') {
|
||||
for (let i = data.length - 1; i >= 0; i--) {
|
||||
const item = data[i]
|
||||
Object.keys(item).forEach(key => {
|
||||
if (key.startsWith('C_') && item[key] === null) {
|
||||
data.splice(i, 1)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
const updateValues = (strategy, data) => {
|
||||
data.forEach(obj => {
|
||||
Object.keys(obj).forEach(key => {
|
||||
if (key.startsWith('C_') && obj[key] === null) {
|
||||
obj[key] = strategy === 'breakLine' ? null : 0
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
if (strategy === 'breakLine' || strategy === 'setZero') {
|
||||
updateValues(strategy, data)
|
||||
}
|
||||
return options
|
||||
}
|
||||
@ -1,5 +1,9 @@
|
||||
import { hexColorToRGBA, resetRgbOpacity } from '@/views/chart/chart/util'
|
||||
import { DEFAULT_COLOR_CASE, DEFAULT_SIZE } from '@/views/chart/chart/chart'
|
||||
import Exceljs from 'exceljs'
|
||||
import { saveAs } from 'file-saver'
|
||||
import i18n from '@/lang'
|
||||
import {Message} from "element-ui";
|
||||
|
||||
export function getCustomTheme(chart) {
|
||||
const headerColor = hexColorToRGBA(DEFAULT_COLOR_CASE.tableHeaderBgColor, DEFAULT_COLOR_CASE.alpha)
|
||||
@ -292,3 +296,168 @@ export function getSize(chart) {
|
||||
|
||||
return size
|
||||
}
|
||||
|
||||
export async function exportPivotExcel(instance, chart) {
|
||||
const { meta, fields } = instance.dataCfg
|
||||
const rowLength = fields?.rows?.length || 0
|
||||
const colLength = fields?.columns?.length || 0
|
||||
const valueLength = fields?.values?.length || 0
|
||||
if (!(rowLength && valueLength)) {
|
||||
Message.warning({
|
||||
message: i18n.t('chart.pivot_export_empty_fields'),
|
||||
type: 'warning',
|
||||
showClose: true,
|
||||
duration: 5000
|
||||
})
|
||||
return
|
||||
}
|
||||
const workbook = new Exceljs.Workbook()
|
||||
const worksheet = workbook.addWorksheet(chart.title)
|
||||
const metaMap = meta?.reduce((p, n) => {
|
||||
if (n.field) {
|
||||
p[n.field] = n
|
||||
}
|
||||
return p
|
||||
}, {})
|
||||
// 角头
|
||||
fields.columns?.forEach((column, index) => {
|
||||
const cell = worksheet.getCell(index + 1, 1)
|
||||
cell.value = metaMap[column]?.name ?? column
|
||||
cell.alignment = { vertical: 'middle', horizontal: 'center' }
|
||||
if (rowLength >= 2) {
|
||||
worksheet.mergeCells(index + 1, 1, index + 1, rowLength)
|
||||
}
|
||||
})
|
||||
fields?.rows?.forEach((row, index) => {
|
||||
const cell = worksheet.getCell(colLength + 1, index + 1)
|
||||
cell.value = metaMap[row]?.name ?? row
|
||||
cell.alignment = { vertical: 'middle', horizontal: 'center' }
|
||||
})
|
||||
const { layoutResult } = instance.facet
|
||||
// 行头
|
||||
const { rowLeafNodes, rowsHierarchy, rowNodes } = layoutResult
|
||||
const maxColIndex = rowsHierarchy.maxLevel + 1
|
||||
const notLeafNodeHeightMap = {}
|
||||
rowLeafNodes.forEach(node => {
|
||||
// 行头的高度由子节点相加决定,也就是行头子节点中包含的叶子节点数量
|
||||
let curNode = node.parent
|
||||
while (curNode) {
|
||||
const height = notLeafNodeHeightMap[curNode.id] ?? 0
|
||||
notLeafNodeHeightMap[curNode.id] = height + 1
|
||||
curNode = curNode.parent
|
||||
}
|
||||
const { rowIndex } = node
|
||||
const writeRowIndex = rowIndex + 1 + colLength + 1
|
||||
const writeColIndex = node.level + 1
|
||||
const cell = worksheet.getCell(writeRowIndex, writeColIndex)
|
||||
cell.value = node.label
|
||||
cell.alignment = { vertical: 'middle', horizontal: 'center' }
|
||||
if (writeColIndex < maxColIndex) {
|
||||
worksheet.mergeCells(writeRowIndex, writeColIndex, writeRowIndex, maxColIndex)
|
||||
}
|
||||
})
|
||||
|
||||
const getNodeStartRowIndex = (node) => {
|
||||
if (!node.children?.length) {
|
||||
return node.rowIndex + 1
|
||||
} else {
|
||||
return getNodeStartRowIndex(node.children[0])
|
||||
}
|
||||
}
|
||||
rowNodes?.forEach(node => {
|
||||
if (node.isLeaf) {
|
||||
return
|
||||
}
|
||||
const rowIndex = getNodeStartRowIndex(node)
|
||||
const height = notLeafNodeHeightMap[node.id]
|
||||
const writeRowIndex = rowIndex + colLength + 1
|
||||
const mergeColCount = node.children[0].level - node.level
|
||||
const value = node.label
|
||||
const cell = worksheet.getCell(writeRowIndex, node.level + 1)
|
||||
cell.value = value
|
||||
cell.alignment = { vertical: 'middle', horizontal: 'center' }
|
||||
if (mergeColCount > 1 || height > 1) {
|
||||
worksheet.mergeCells(
|
||||
writeRowIndex,
|
||||
node.level + 1,
|
||||
writeRowIndex + height - 1,
|
||||
node.level + mergeColCount
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
// 列头
|
||||
const { colLeafNodes, colNodes, colsHierarchy } = layoutResult
|
||||
const maxColHeight = colsHierarchy.maxLevel + 1
|
||||
const notLeafNodeWidthMap = {}
|
||||
colLeafNodes.forEach(node => {
|
||||
// 列头的宽度由子节点相加决定,也就是列头子节点中包含的叶子节点数量
|
||||
let curNode = node.parent
|
||||
while (curNode) {
|
||||
const width = notLeafNodeWidthMap[curNode.id] ?? 0
|
||||
notLeafNodeWidthMap[curNode.id] = width + 1
|
||||
curNode = curNode.parent
|
||||
}
|
||||
const { colIndex } = node
|
||||
const writeRowIndex = node.level + 1
|
||||
const writeColIndex = colIndex + 1 + rowLength
|
||||
const cell = worksheet.getCell(writeRowIndex, writeColIndex)
|
||||
let value = node.label
|
||||
if (node.field === '$$extra$$' && metaMap[value]?.name) {
|
||||
value = metaMap[value].name
|
||||
}
|
||||
cell.value = value
|
||||
cell.alignment = { vertical: 'middle', horizontal: 'center' }
|
||||
if (writeRowIndex < maxColHeight) {
|
||||
worksheet.mergeCells(writeRowIndex, writeColIndex, maxColHeight, writeColIndex)
|
||||
}
|
||||
})
|
||||
const getNodeStartColIndex = (node) => {
|
||||
if (!node.children?.length) {
|
||||
return node.colIndex + 1
|
||||
} else {
|
||||
return getNodeStartColIndex(node.children[0])
|
||||
}
|
||||
}
|
||||
colNodes.forEach(node => {
|
||||
if (node.isLeaf) {
|
||||
return
|
||||
}
|
||||
const colIndex = getNodeStartColIndex(node)
|
||||
const width = notLeafNodeWidthMap[node.id]
|
||||
const writeRowIndex = node.level + 1
|
||||
const mergeRowCount = node.children[0].level - node.level
|
||||
const value = node.label
|
||||
const writeColIndex = colIndex + rowLength
|
||||
const cell = worksheet.getCell(writeRowIndex, writeColIndex)
|
||||
cell.value = value
|
||||
cell.alignment = { vertical: 'middle', horizontal: 'center' }
|
||||
if (mergeRowCount > 1 || width > 1) {
|
||||
worksheet.mergeCells(
|
||||
writeRowIndex,
|
||||
writeColIndex,
|
||||
writeRowIndex + mergeRowCount - 1,
|
||||
writeColIndex + width - 1
|
||||
)
|
||||
}
|
||||
})
|
||||
// 单元格数据
|
||||
for (let rowIndex = 0; rowIndex < rowLeafNodes.length; rowIndex++) {
|
||||
for (let colIndex = 0; colIndex < colLeafNodes.length; colIndex++) {
|
||||
const dataCellMeta = layoutResult.getCellMeta(rowIndex, colIndex)
|
||||
const { fieldValue } = dataCellMeta
|
||||
if (fieldValue) {
|
||||
const meta = metaMap[dataCellMeta.valueField]
|
||||
const cell = worksheet.getCell(rowIndex + maxColHeight + 1, rowLength + colIndex + 1)
|
||||
const value = meta?.formatter?.(fieldValue) || fieldValue.toString()
|
||||
cell.alignment = { vertical: 'middle', horizontal: 'center' }
|
||||
cell.value = value
|
||||
}
|
||||
}
|
||||
}
|
||||
const buffer = await workbook.xlsx.writeBuffer()
|
||||
const dataBlob = new Blob([buffer], {
|
||||
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8'
|
||||
})
|
||||
saveAs(dataBlob, `${chart.title ?? '透视表'}.xlsx`)
|
||||
}
|
||||
|
||||
@ -1738,7 +1738,74 @@ export const TYPE_CONFIGS = [
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
render: 'antv',
|
||||
category: 'chart.chart_type_compare',
|
||||
value: 'stock-line',
|
||||
title: 'chart.chart_stock_line',
|
||||
icon: 'stock-line',
|
||||
properties: [
|
||||
'color-selector',
|
||||
'size-selector',
|
||||
'tooltip-selector-ant-v',
|
||||
'x-axis-selector-ant-v',
|
||||
'y-axis-selector-ant-v',
|
||||
'title-selector-ant-v',
|
||||
'legend-selector-ant-v'
|
||||
],
|
||||
propertyInner: {
|
||||
'color-selector': [
|
||||
'value',
|
||||
'colorPanel',
|
||||
'customColor',
|
||||
'alpha'
|
||||
],
|
||||
'size-selector': [
|
||||
'lineWidth',
|
||||
'lineSymbol',
|
||||
'lineSymbolSize',
|
||||
'lineSmooth'
|
||||
],
|
||||
'tooltip-selector-ant-v': [
|
||||
'show',
|
||||
'textStyle'
|
||||
],
|
||||
'x-axis-selector-ant-v': [
|
||||
'show',
|
||||
'position',
|
||||
'splitLine',
|
||||
'axisForm',
|
||||
'axisLabel'
|
||||
],
|
||||
'y-axis-selector-ant-v': [
|
||||
'show',
|
||||
'position',
|
||||
'name',
|
||||
'nameTextStyle',
|
||||
'axisValue',
|
||||
'splitLine',
|
||||
'axisForm',
|
||||
'axisLabel'
|
||||
],
|
||||
'title-selector-ant-v': [
|
||||
'show',
|
||||
'title',
|
||||
'fontSize',
|
||||
'color',
|
||||
'hPosition',
|
||||
'isItalic',
|
||||
'isBolder',
|
||||
'remarkShow',
|
||||
'fontFamily',
|
||||
'letterSpace',
|
||||
'fontShadow'
|
||||
],
|
||||
'legend-selector-ant-v': [
|
||||
'show',
|
||||
'textStyle',
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
render: 'antv',
|
||||
category: 'chart.chart_type_distribute',
|
||||
|
||||
@ -45,7 +45,13 @@ import { baseLiquid } from '@/views/chart/chart/liquid/liquid'
|
||||
import { uuid } from 'vue-uuid'
|
||||
import ViewTrackBar from '@/components/canvas/components/editor/ViewTrackBar'
|
||||
import { adjustPosition, getRemark, hexColorToRGBA } from '@/views/chart/chart/util'
|
||||
import { baseBarOptionAntV, hBaseBarOptionAntV, baseBidirectionalBarOptionAntV, timeRangeBarOptionAntV } from '@/views/chart/chart/bar/bar_antv'
|
||||
import {
|
||||
baseBarOptionAntV,
|
||||
hBaseBarOptionAntV,
|
||||
baseBidirectionalBarOptionAntV,
|
||||
timeRangeBarOptionAntV,
|
||||
stockLineOptionAntV
|
||||
} from '@/views/chart/chart/bar/bar_antv'
|
||||
import { baseAreaOptionAntV, baseLineOptionAntV } from '@/views/chart/chart/line/line_antv'
|
||||
import { basePieOptionAntV, basePieRoseOptionAntV } from '@/views/chart/chart/pie/pie_antv'
|
||||
import { baseScatterOptionAntV } from '@/views/chart/chart/scatter/scatter_antv'
|
||||
@ -298,6 +304,8 @@ export default {
|
||||
this.myChart = await baseFlowMapOption(this.chartId, chart, this.antVAction)
|
||||
} else if (chart.type === 'bidirectional-bar') {
|
||||
this.myChart = baseBidirectionalBarOptionAntV(this.chartId, chart, this.antVAction)
|
||||
} else if (chart.type === 'stock-line'){
|
||||
this.myChart = stockLineOptionAntV(this.chartId, chart, this.antVAction)
|
||||
}
|
||||
|
||||
if (this.myChart && !equalsAny(chart.type, 'liquid', 'flow-map') && this.searchCount > 0) {
|
||||
|
||||
@ -308,6 +308,7 @@ export default {
|
||||
this.myChart = baseTableNormal(this.chartId, chart, this.antVAction, this.tableData, this, this.columnResize)
|
||||
} else if (chart.type === 'table-pivot') {
|
||||
this.myChart = baseTablePivot(this.chartId, chart, this.antVAction, this.tableHeaderClick, this.tableData)
|
||||
this.$store.dispatch('chart/setTableInstance', { viewId: this.chart.id, tableInstance: this.myChart })
|
||||
}
|
||||
|
||||
if (this.myChart && this.searchCount > 0) {
|
||||
|
||||
@ -17,12 +17,30 @@
|
||||
<el-option
|
||||
v-for="field in fieldList"
|
||||
:key="field.id"
|
||||
:label="field.name + '(' + $t('chart.' + field.dateStyle) + ')'"
|
||||
:label="fieldFormatter(field)"
|
||||
:value="field.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item
|
||||
v-if="isIndicator"
|
||||
:label="$t('chart.datePattern')"
|
||||
>
|
||||
<el-select
|
||||
v-model="dateFormatter"
|
||||
:placeholder="$t('chart.datePattern')"
|
||||
@change="changeDateFormatter"
|
||||
>
|
||||
<el-option
|
||||
v-for="field in dateFormatterList"
|
||||
:key="field.value"
|
||||
:label="field.name"
|
||||
:value="field.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="$t('chart.compare_type')">
|
||||
<el-radio-group v-model="compareItem.compareCalc.type">
|
||||
<el-radio
|
||||
@ -73,23 +91,40 @@ export default {
|
||||
chart: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
dimensionData: {
|
||||
type: Array,
|
||||
required: false
|
||||
},
|
||||
quotaData: {
|
||||
type: Array,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
fieldList: [],
|
||||
compareList: []
|
||||
compareList: [],
|
||||
dateFormatter: 'y_M_d',
|
||||
dateFormatterList: [
|
||||
{ name: this.$t('chart.y'), value: 'y' },
|
||||
{ name: this.$t('chart.y_W'), value: 'y_W' },
|
||||
{ name: this.$t('chart.y_M'), value: 'y_M' },
|
||||
{ name: this.$t('chart.y_M_d'), value: 'y_M_d' }
|
||||
]
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'chart': function() {
|
||||
this.initFieldList()
|
||||
this.initCompareType()
|
||||
this.initDateFormatter()
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initFieldList()
|
||||
this.initCompareType()
|
||||
this.initDateFormatter()
|
||||
},
|
||||
methods: {
|
||||
// 过滤xaxis,extStack所有日期字段
|
||||
@ -114,6 +149,12 @@ export default {
|
||||
t1.push(...t2)
|
||||
}
|
||||
|
||||
if (this.isIndicator) {
|
||||
t1.length = 0
|
||||
t1.push(...this.dimensionData.filter(ele => ele.deType === 1))
|
||||
t1.push(...this.quotaData.filter(ele => ele.deType === 1))
|
||||
}
|
||||
|
||||
this.fieldList = t1
|
||||
// 如果没有选中字段,则默认选中第一个
|
||||
if ((!this.compareItem.compareCalc.field || this.compareItem.compareCalc.field === '') && this.fieldList.length > 0) {
|
||||
@ -147,6 +188,36 @@ export default {
|
||||
if ((!this.compareItem.compareCalc.type || this.compareItem.compareCalc.type === '' || this.compareItem.compareCalc.type === 'none') && this.compareList.length > 0) {
|
||||
this.compareItem.compareCalc.type = this.compareList[0].value
|
||||
}
|
||||
},
|
||||
changeDateFormatter() {
|
||||
const checkedField = this.fieldList.filter(ele => ele.id === this.compareItem.compareCalc.field)
|
||||
if (checkedField && checkedField.length > 0) {
|
||||
checkedField[0].dateStyle = this.dateFormatter
|
||||
if (!this.compareItem.compareCalc.custom) {
|
||||
this.compareItem.compareCalc.custom = { timeType: 'y_M_d' }
|
||||
}
|
||||
this.compareItem.compareCalc.custom.timeType = this.dateFormatter
|
||||
}
|
||||
this.initCompareType()
|
||||
},
|
||||
initDateFormatter() {
|
||||
const timeType = this.compareItem.compareCalc.custom?.timeType
|
||||
if (this.isIndicator && timeType) {
|
||||
this.dateFormatter = timeType==='0'?'y_M_d':timeType
|
||||
this.changeDateFormatter()
|
||||
}
|
||||
},
|
||||
fieldFormatter(field) {
|
||||
if (this.isIndicator) {
|
||||
return field.name
|
||||
} else {
|
||||
return field.name + '(' + this.$t('chart.' + field.dateStyle) + ')'
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isIndicator() {
|
||||
return this.chart.type === 'text'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -323,6 +323,11 @@ export default {
|
||||
}
|
||||
},
|
||||
isEnableCompare() {
|
||||
// 指标卡直接放行同环比配置
|
||||
if (this.chart.type === 'text') {
|
||||
this.disableEditCompare = false
|
||||
return
|
||||
}
|
||||
let xAxis = null
|
||||
if (Object.prototype.toString.call(this.chart.xaxis) === '[object Array]') {
|
||||
xAxis = JSON.parse(JSON.stringify(this.chart.xaxis))
|
||||
|
||||
@ -78,7 +78,7 @@
|
||||
v-model="functionForm.emptyDataStrategy"
|
||||
@change="changeFunctionCfg"
|
||||
>
|
||||
<el-radio :label="'breakLine'">{{ $t('chart.break_line') }}</el-radio>
|
||||
<el-radio v-show="showBreakOption" :label="'breakLine'">{{ $t('chart.break_line') }}</el-radio>
|
||||
<el-radio :label="'setZero'">{{ $t('chart.set_zero') }}</el-radio>
|
||||
<el-radio
|
||||
v-show="showIgnoreOption"
|
||||
@ -114,6 +114,7 @@
|
||||
<script>
|
||||
import { DEFAULT_FUNCTION_CFG, COLOR_PANEL } from '../../chart/chart'
|
||||
import { equalsAny, includesAny } from '@/utils/StringUtils'
|
||||
import {cloneDeep} from "lodash";
|
||||
|
||||
export default {
|
||||
name: 'FunctionCfg',
|
||||
@ -134,7 +135,7 @@ export default {
|
||||
showSlider() {
|
||||
return this.chart.type !== 'bidirectional-bar' &&
|
||||
!equalsAny(this.chart.type, 'map') &&
|
||||
!includesAny(this.chart.type, 'table', 'text')
|
||||
!includesAny(this.chart.type, 'table', 'text','stock-line')
|
||||
},
|
||||
showEmptyStrategy() {
|
||||
return (this.chart.render === 'antv' &&
|
||||
@ -148,7 +149,10 @@ export default {
|
||||
return this.showEmptyStrategy &&
|
||||
includesAny(this.chart.type, 'table') &&
|
||||
this.functionForm.emptyDataStrategy !== 'breakLine'
|
||||
}
|
||||
},
|
||||
showBreakOption() {
|
||||
return !equalsAny(this.chart.type, 'stock-line')
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
'chart': {
|
||||
@ -170,10 +174,14 @@ export default {
|
||||
} else {
|
||||
senior = JSON.parse(chart.senior)
|
||||
}
|
||||
const defaultFunctionCfg = cloneDeep(DEFAULT_FUNCTION_CFG)
|
||||
if (equalsAny(this.chart.type, 'stock-line')) {
|
||||
defaultFunctionCfg.emptyDataStrategy = 'setZero'
|
||||
}
|
||||
if (senior.functionCfg) {
|
||||
this.functionForm = { ...DEFAULT_FUNCTION_CFG, ...senior.functionCfg }
|
||||
this.functionForm = { ...defaultFunctionCfg, ...senior.functionCfg }
|
||||
} else {
|
||||
this.functionForm = JSON.parse(JSON.stringify(DEFAULT_FUNCTION_CFG))
|
||||
this.functionForm = JSON.parse(JSON.stringify(defaultFunctionCfg))
|
||||
}
|
||||
this.initFieldCtrl()
|
||||
}
|
||||
|
||||
@ -1772,6 +1772,8 @@
|
||||
<compare-edit
|
||||
:compare-item="quotaItemCompare"
|
||||
:chart="chart"
|
||||
:dimension-data="dimensionData"
|
||||
:quota-data="quotaData"
|
||||
/>
|
||||
<div
|
||||
slot="footer"
|
||||
@ -2192,7 +2194,7 @@ export default {
|
||||
return equalsAny(this.view.type, 'table-normal', 'table-info', 'table-pivot')
|
||||
},
|
||||
showAnalyseCfg() {
|
||||
if (this.view.type === 'bidirectional-bar' || this.view.type === 'bar-time-range') {
|
||||
if (this.view.type === 'bidirectional-bar' || this.view.type === 'bar-time-range' || this.view.type === 'stock-line') {
|
||||
return false
|
||||
}
|
||||
return includesAny(this.view.type, 'bar', 'line', 'area', 'gauge', 'liquid') ||
|
||||
|
||||
@ -161,6 +161,10 @@ export default {
|
||||
margin:5px;
|
||||
}
|
||||
|
||||
.el-radio:last-child{
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.radio-row{
|
||||
width: 100%;
|
||||
display: flex;
|
||||
|
||||
@ -438,6 +438,7 @@ export default {
|
||||
:placeholder="item.settings.placeholder"
|
||||
style="width: 100%"
|
||||
size="small"
|
||||
filterable
|
||||
:multiple="item.settings.multiple"
|
||||
clearable
|
||||
>
|
||||
|
||||
@ -980,6 +980,7 @@ export default {
|
||||
:placeholder="item.settings.placeholder"
|
||||
style="width: 100%"
|
||||
size="small"
|
||||
filterable
|
||||
:multiple="item.settings.multiple"
|
||||
clearable
|
||||
>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "dataease-mobile",
|
||||
"version": "1.18.24",
|
||||
"version": "1.18.25",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "npm run dev:h5",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user