Merge branch 'dev-v2' into pr@dev-v2_cascade
This commit is contained in:
commit
e69f17e549
@ -214,6 +214,7 @@ public class ChartDataManage {
|
||||
|| StringUtils.equalsIgnoreCase(view.getType(), "flow-map")
|
||||
|| StringUtils.equalsIgnoreCase(view.getType(), "sankey")
|
||||
|| StringUtils.containsIgnoreCase(view.getType(), "chart-mix")
|
||||
|| StringUtils.equalsIgnoreCase(view.getType(), "symbolic-map")
|
||||
) {
|
||||
xAxis.addAll(xAxisExt);
|
||||
}
|
||||
@ -740,6 +741,28 @@ public class ChartDataManage {
|
||||
ExtWhere2Str.extWhere2sqlOjb(sqlMeta, yoyFilterList, transFields(allFields), crossDs, dsMap);
|
||||
yoySql = SQLProvider.createQuerySQL(sqlMeta, true, needOrder, view);
|
||||
}
|
||||
} else if (StringUtils.equalsIgnoreCase("symbolic-map", view.getType())) {
|
||||
Dimension2SQLObj.dimension2sqlObj(sqlMeta, xAxis, transFields(allFields), crossDs, dsMap);
|
||||
Quota2SQLObj.quota2sqlObj(sqlMeta, yAxis, transFields(allFields), crossDs, dsMap);
|
||||
List<ChartViewFieldDTO> yFields = new ArrayList<>();
|
||||
yFields.addAll(chartViewManege.transFieldDTO(Collections.singletonList(chartViewManege.createCountField(view.getTableId()))));
|
||||
yFields.addAll(extBubble);
|
||||
yAxis.addAll(yFields);
|
||||
Quota2SQLObj.quota2sqlObj(sqlMeta, yAxis, transFields(allFields), crossDs, dsMap);
|
||||
querySql = SQLProvider.createQuerySQL(sqlMeta, true, needOrder, view);
|
||||
List<Long> xAxisIds = xAxis.stream().map(ChartViewFieldDTO::getId).toList();
|
||||
viewFields.addAll(xAxis);
|
||||
viewFields.addAll(allFields.stream().filter(field -> !xAxisIds.contains(field.getId())).toList());
|
||||
if (ObjectUtils.isNotEmpty(viewFields)) {
|
||||
detailFieldList.addAll(viewFields);
|
||||
SQLMeta sqlMeta1 = new SQLMeta();
|
||||
BeanUtils.copyBean(sqlMeta1, sqlMeta);
|
||||
sqlMeta1.setYFields(new ArrayList<>());
|
||||
Dimension2SQLObj.dimension2sqlObj(sqlMeta1, detailFieldList, transFields(allFields), crossDs, dsMap);
|
||||
String originSql = SQLProvider.createQuerySQL(sqlMeta1, false, needOrder, view);
|
||||
String limit = ((pageInfo.getGoPage() != null && pageInfo.getPageSize() != null) ? " LIMIT " + pageInfo.getPageSize() + " OFFSET " + (pageInfo.getGoPage() - 1) * pageInfo.getPageSize() : "");
|
||||
detailFieldSql = originSql + limit;
|
||||
}
|
||||
} else {
|
||||
Dimension2SQLObj.dimension2sqlObj(sqlMeta, xAxis, transFields(allFields), crossDs, dsMap);
|
||||
Quota2SQLObj.quota2sqlObj(sqlMeta, yAxis, transFields(allFields), crossDs, dsMap);
|
||||
@ -782,6 +805,7 @@ public class ChartDataManage {
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(detailFieldSql)) {
|
||||
detailFieldSql = SqlUtils.rebuildSQL(detailFieldSql, sqlMeta, crossDs, dsMap);
|
||||
datasourceRequest.setQuery(detailFieldSql);
|
||||
detailData = (List<String[]>) calciteProvider.fetchResultField(datasourceRequest).get("data");
|
||||
}
|
||||
@ -920,12 +944,12 @@ public class ChartDataManage {
|
||||
List<String[]> resultData = new ArrayList<>();
|
||||
for (String[] res1 : data) {
|
||||
StringBuilder x1 = new StringBuilder();
|
||||
for (int i = 0; i < xAxis.size() + xAxisExt.size(); i++) {
|
||||
for (int i = 0; i < xAxis.size(); i++) {
|
||||
x1.append(res1[i]);
|
||||
}
|
||||
for (String[] res2 : yoyData) {
|
||||
StringBuilder x2 = new StringBuilder();
|
||||
for (int i = 0; i < xAxis.size() + xAxisExt.size(); i++) {
|
||||
for (int i = 0; i < xAxis.size(); i++) {
|
||||
x2.append(res2[i]);
|
||||
}
|
||||
if (StringUtils.equals(x1, x2)) {
|
||||
|
||||
@ -1193,7 +1193,7 @@ public class ChartDataBuild {
|
||||
Map<String, Object> map = transTableNormal(fields, null, data, desensitizationList);
|
||||
List<Map<String, Object>> tableRow = (List<Map<String, Object>>) map.get("tableRow");
|
||||
final int xEndIndex = detailIndex;
|
||||
Map<String, List<String[]>> groupDataList = detailData.stream().collect(Collectors.groupingBy(item -> "(" + StringUtils.join(ArrayUtils.subarray(item, 0, xEndIndex), "-de-") + ")"));
|
||||
Map<String, List<String[]>> groupDataList = detailData.stream().collect(Collectors.groupingBy(item -> "(" + StringUtils.join(ArrayUtils.subarray(item, 0, xEndIndex), ")-de-(") + ")"));
|
||||
|
||||
tableRow.forEach(row -> {
|
||||
String key = xAxis.stream().map(x -> String.format(format, row.get(x.getDataeaseName()).toString())).collect(Collectors.joining("-de-"));
|
||||
|
||||
@ -2,17 +2,17 @@ package io.dataease.dataset.manage;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import io.dataease.api.chart.dto.ColumnPermissionItem;
|
||||
import io.dataease.extensions.view.model.SQLObj;
|
||||
import io.dataease.dataset.dao.auto.entity.CoreDatasetTableField;
|
||||
import io.dataease.dataset.dao.auto.mapper.CoreDatasetGroupMapper;
|
||||
import io.dataease.dataset.dao.auto.mapper.CoreDatasetTableFieldMapper;
|
||||
import io.dataease.dataset.utils.TableUtils;
|
||||
import io.dataease.datasource.provider.CalciteProvider;
|
||||
import io.dataease.extensions.view.dto.DatasetTableFieldDTO;
|
||||
import io.dataease.engine.constant.ExtFieldConstant;
|
||||
import io.dataease.engine.func.FunctionConstant;
|
||||
import io.dataease.engine.utils.Utils;
|
||||
import io.dataease.exception.DEException;
|
||||
import io.dataease.extensions.view.dto.DatasetTableFieldDTO;
|
||||
import io.dataease.extensions.view.model.SQLObj;
|
||||
import io.dataease.i18n.Translator;
|
||||
import io.dataease.utils.AuthUtils;
|
||||
import io.dataease.utils.BeanUtils;
|
||||
@ -160,6 +160,19 @@ public class DatasetTableFieldManage {
|
||||
return transDTO(coreDatasetTableFieldMapper.selectList(wrapper));
|
||||
}
|
||||
|
||||
public Map<String, List<DatasetTableFieldDTO>> selectByDatasetGroupIds(List<Long> ids) {
|
||||
Map<String, List<DatasetTableFieldDTO>> map = new HashMap<>();
|
||||
for (Long id : ids) {
|
||||
QueryWrapper<CoreDatasetTableField> wrapper = new QueryWrapper<>();
|
||||
wrapper.eq("dataset_group_id", id);
|
||||
wrapper.eq("checked", true);
|
||||
wrapper.isNull("chart_id");
|
||||
wrapper.eq("ext_field", 0);
|
||||
map.put(String.valueOf(id), transDTO(coreDatasetTableFieldMapper.selectList(wrapper)));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public List<DatasetTableFieldDTO> selectByFieldIds(List<Long> ids) {
|
||||
QueryWrapper<CoreDatasetTableField> wrapper = new QueryWrapper<>();
|
||||
wrapper.in("id", ids);
|
||||
|
||||
@ -43,6 +43,11 @@ public class DatasetFieldServer implements DatasetTableApi {
|
||||
return datasetTableFieldManage.selectByDatasetGroupId(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<DatasetTableFieldDTO>> listByDsIds(List<Long> ids) {
|
||||
return datasetTableFieldManage.selectByDatasetGroupIds(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Long id) {
|
||||
datasetTableFieldManage.deleteById(id);
|
||||
|
||||
@ -0,0 +1,7 @@
|
||||
package io.dataease.datasource.provider;
|
||||
|
||||
/**
|
||||
* @Author Junjun
|
||||
*/
|
||||
public abstract class Provider {
|
||||
}
|
||||
@ -14,7 +14,7 @@ export default {
|
||||
],
|
||||
build: {
|
||||
rollupOptions: {
|
||||
external: id => /de-xpack/.test(id),
|
||||
external: id => /de-xpack/.test(id) || /extensions-view-3dpie/.test(id),
|
||||
output: {
|
||||
// 用于命名代码拆分时创建的共享块的输出命名
|
||||
chunkFileNames: `assets/chunk/[name]-${pkg.version}-${pkg.name}.js`,
|
||||
|
||||
93
core/core-frontend/src/assets/svg/symbolic-map.svg
Normal file
93
core/core-frontend/src/assets/svg/symbolic-map.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 742 KiB |
@ -47,6 +47,7 @@ const {
|
||||
} = storeToRefs(dvMainStore)
|
||||
const dvModel = 'dashboard'
|
||||
const multiplexingRef = ref(null)
|
||||
const fullScreeRef = ref(null)
|
||||
let nameEdit = ref(false)
|
||||
let inputName = ref('')
|
||||
let nameInput = ref(null)
|
||||
@ -97,7 +98,7 @@ const redo = () => {
|
||||
}
|
||||
|
||||
const previewInner = () => {
|
||||
dvMainStore.setEditMode('preview')
|
||||
fullScreeRef.value.toggleFullscreen()
|
||||
}
|
||||
|
||||
const previewOuter = () => {
|
||||
@ -522,7 +523,12 @@ const initOpenHandler = newWindow => {
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu class="drop-style">
|
||||
<de-fullscreen :show-position="'edit'"></de-fullscreen>
|
||||
<el-dropdown-item @click="previewInner">
|
||||
<el-icon style="margin-right: 8px; font-size: 16px">
|
||||
<Icon name="icon_pc_fullscreen" />
|
||||
</el-icon>
|
||||
全屏预览
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item @click="previewOuter()">
|
||||
<el-icon style="margin-right: 8px; font-size: 16px">
|
||||
<Icon name="dv-preview-outer" />
|
||||
@ -615,6 +621,7 @@ const initOpenHandler = newWindow => {
|
||||
/>
|
||||
<outer-params-set ref="outerParamsSetRef"> </outer-params-set>
|
||||
</div>
|
||||
<de-fullscreen show-position="edit" ref="fullScreeRef"></de-fullscreen>
|
||||
<XpackComponent ref="openHandler" jsname="L2NvbXBvbmVudC9lbWJlZGRlZC1pZnJhbWUvT3BlbkhhbmRsZXI=" />
|
||||
</template>
|
||||
|
||||
|
||||
@ -94,6 +94,13 @@ onMounted(() => {
|
||||
useEmitt().emitter.emit('initScroll')
|
||||
})
|
||||
}, 1000)
|
||||
useEmitt({
|
||||
name: 'canvasScrollRestore',
|
||||
callback: function () {
|
||||
// 用于全屏后还原编辑状态大小
|
||||
changeSizeWithScale(scale.value)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
|
||||
@ -23,6 +23,7 @@ import ComponentButton from '@/components/visualization/ComponentButton.vue'
|
||||
import OuterParamsSet from '@/components/visualization/OuterParamsSet.vue'
|
||||
import MultiplexingCanvas from '@/views/common/MultiplexingCanvas.vue'
|
||||
import ComponentButtonLabel from '@/components/visualization/ComponentButtonLabel.vue'
|
||||
import DeFullscreen from '@/components/visualization/common/DeFullscreen.vue'
|
||||
let nameEdit = ref(false)
|
||||
let inputName = ref('')
|
||||
let nameInput = ref(null)
|
||||
@ -36,6 +37,7 @@ let scaleEdit = 100
|
||||
const { wsCache } = useCache('localStorage')
|
||||
const dvModel = 'dataV'
|
||||
const outerParamsSetRef = ref(null)
|
||||
const fullScreeRef = ref(null)
|
||||
|
||||
const closeEditCanvasName = () => {
|
||||
nameEdit.value = false
|
||||
@ -303,7 +305,12 @@ const multiplexingCanvasOpen = () => {
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button v-else class="preview-button" @click="preview()" style="float: right">
|
||||
<el-button
|
||||
v-else
|
||||
class="preview-button"
|
||||
@click="() => fullScreeRef.toggleFullscreen()"
|
||||
style="float: right"
|
||||
>
|
||||
预览
|
||||
</el-button>
|
||||
<el-button
|
||||
@ -335,6 +342,7 @@ const multiplexingCanvasOpen = () => {
|
||||
ref="resourceGroupOpt"
|
||||
/>
|
||||
</div>
|
||||
<de-fullscreen ref="fullScreeRef" show-position="dvEdit"></de-fullscreen>
|
||||
<multiplexing-canvas ref="multiplexingRef"></multiplexing-canvas>
|
||||
<outer-params-set ref="outerParamsSetRef"> </outer-params-set>
|
||||
<XpackComponent ref="openHandler" jsname="L2NvbXBvbmVudC9lbWJlZGRlZC1pZnJhbWUvT3BlbkhhbmRsZXI=" />
|
||||
|
||||
@ -294,7 +294,15 @@ const active = computed(() => {
|
||||
})
|
||||
|
||||
const boardMoveActive = computed(() => {
|
||||
const CHARTS = ['flow-map', 'map', 'bubble-map', 'table-info', 'table-normal', 'table-pivot']
|
||||
const CHARTS = [
|
||||
'flow-map',
|
||||
'map',
|
||||
'bubble-map',
|
||||
'table-info',
|
||||
'table-normal',
|
||||
'table-pivot',
|
||||
'symbolic-map'
|
||||
]
|
||||
return CHARTS.includes(element.value.innerType)
|
||||
})
|
||||
|
||||
|
||||
@ -33,10 +33,15 @@ const generateRamStr = (len: number) => {
|
||||
}
|
||||
|
||||
const importProxy = (bytesArray: any[]) => {
|
||||
const promise = import(
|
||||
/* const promise = import(
|
||||
`../../../../../../../${formatArray(bytesArray[7])}/${formatArray(bytesArray[8])}/${formatArray(
|
||||
bytesArray[9]
|
||||
)}/${formatArray(bytesArray[10])}/${formatArray(bytesArray[11])}.vue`
|
||||
) */
|
||||
const promise = import(
|
||||
`../../../../../../../extensions-view-3dpie/${formatArray(bytesArray[8])}/${formatArray(
|
||||
bytesArray[9]
|
||||
)}/${formatArray(bytesArray[10])}/${formatArray(bytesArray[11])}.vue`
|
||||
)
|
||||
promise
|
||||
.then((res: any) => {
|
||||
|
||||
@ -40,17 +40,32 @@
|
||||
>仅看已选 <el-switch size="small" v-model="state.showSelected" />
|
||||
</span>
|
||||
</el-row>
|
||||
<el-row class="tree-dataset-head" v-show="sameDsShow"
|
||||
><span
|
||||
><el-icon class="toggle-icon" @click="() => (toggleSameDs = !toggleSameDs)">
|
||||
<CaretBottom v-show="toggleSameDs" />
|
||||
<CaretRight v-show="!toggleSameDs" /> </el-icon
|
||||
><span>同数据集</span></span
|
||||
>
|
||||
<el-checkbox
|
||||
v-model="sameDatasetComponentCheckAll"
|
||||
:indeterminate="checkAllIsIndeterminate"
|
||||
@change="batchSelectChange"
|
||||
>全选</el-checkbox
|
||||
></el-row
|
||||
>
|
||||
<el-tree
|
||||
v-show="toggleSameDs && sameDsShow"
|
||||
class="custom-tree"
|
||||
menu
|
||||
ref="linkageInfoTree"
|
||||
:empty-text="'暂无可用图表'"
|
||||
:filter-node-method="filterNodeMethod"
|
||||
:data="curLinkageTargetViewsInfo"
|
||||
:data="curLinkageTargetViewsInfoSameDs"
|
||||
node-key="targetViewId"
|
||||
highlight-current
|
||||
:props="state.treeProp"
|
||||
@node-click="nodeClick"
|
||||
@node-click="nodeClickPre($event, 'sameDs')"
|
||||
>
|
||||
<template #default="{ data }">
|
||||
<span class="custom-tree-node">
|
||||
@ -60,7 +75,54 @@
|
||||
<!--???-->
|
||||
<el-checkbox
|
||||
v-model="data.linkageActive"
|
||||
@change="targetViewCheckedChange(data)"
|
||||
@change="targetViewCheckedChange('sameDs', data)"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
<span>
|
||||
<span class="tree-select-field">
|
||||
<Icon
|
||||
class-name="view-type-icon"
|
||||
style="margin-right: 4px"
|
||||
:name="data.targetViewType"
|
||||
/>
|
||||
{{ data.targetViewName }}
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
<el-row class="tree-dataset-head tree-dataset-head-top" v-show="diffDsShow"
|
||||
><span
|
||||
><el-icon class="toggle-icon" @click="() => (toggleDiffDs = !toggleDiffDs)">
|
||||
<CaretBottom v-show="toggleDiffDs" />
|
||||
<CaretRight v-show="!toggleDiffDs" /> </el-icon
|
||||
><span>不同数据集</span></span
|
||||
>
|
||||
</el-row>
|
||||
<el-tree
|
||||
v-show="toggleDiffDs && diffDsShow"
|
||||
class="custom-tree"
|
||||
menu
|
||||
ref="linkageInfoTreeDiffDs"
|
||||
:empty-text="'暂无可用图表'"
|
||||
:filter-node-method="filterNodeMethod"
|
||||
:data="curLinkageTargetViewsInfoDiffDs"
|
||||
node-key="targetViewId"
|
||||
highlight-current
|
||||
:props="state.treeProp"
|
||||
@node-click="nodeClickPre($event, 'diffDs')"
|
||||
>
|
||||
<template #default="{ data }">
|
||||
<span class="custom-tree-node">
|
||||
<span>
|
||||
<div @click.stop>
|
||||
<span class="auth-span">
|
||||
<!--???-->
|
||||
<el-checkbox
|
||||
v-model="data.linkageActive"
|
||||
@change="targetViewCheckedChange('diffDs', data)"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
@ -208,10 +270,13 @@ import { ACTION_SELECTION } from '@/custom-component/component-list'
|
||||
const dvMainStore = dvMainStoreWithOut()
|
||||
const { dvInfo, canvasViewInfo, componentData, curComponent } = storeToRefs(dvMainStore)
|
||||
const linkageInfoTree = ref(null)
|
||||
const linkageInfoTreeDiffDs = ref(null)
|
||||
const { t } = useI18n()
|
||||
const dialogShow = ref(false)
|
||||
const loading = ref(false)
|
||||
const curLinkageTargetViewsInfo = ref([])
|
||||
const curLinkageTargetViewsInfoSameDs = ref([])
|
||||
const curLinkageTargetViewsInfoDiffDs = ref([])
|
||||
const snapshotStore = snapshotStoreWithOut()
|
||||
const state = reactive({
|
||||
sourceLinkageInfo: {},
|
||||
@ -220,6 +285,7 @@ const state = reactive({
|
||||
curDatasetInfo: {},
|
||||
initState: false,
|
||||
viewId: null,
|
||||
tableId: null,
|
||||
treeProp: {
|
||||
id: 'targetViewId',
|
||||
label: 'targetViewName',
|
||||
@ -227,9 +293,48 @@ const state = reactive({
|
||||
},
|
||||
linkageInfo: null
|
||||
})
|
||||
const sameDatasetComponentCheckAll = ref(false)
|
||||
|
||||
const checkAllIsIndeterminate = ref(false)
|
||||
|
||||
const customLinkageActive = ref(deepCopy(ACTION_SELECTION))
|
||||
|
||||
const toggleSameDs = ref(true)
|
||||
|
||||
const toggleDiffDs = ref(true)
|
||||
|
||||
const sameDsTreeSelectedChange = () => {
|
||||
const checkedCount = curLinkageTargetViewsInfoSameDs.value.filter(
|
||||
viewInfo => viewInfo.linkageActive
|
||||
).length
|
||||
sameDatasetComponentCheckAll.value = checkedCount === curLinkageTargetViewsInfoSameDs.value.length
|
||||
checkAllIsIndeterminate.value =
|
||||
checkedCount > 0 && checkedCount < curLinkageTargetViewsInfoSameDs.value.length
|
||||
}
|
||||
|
||||
const batchSelectChange = value => {
|
||||
// do change
|
||||
curLinkageTargetViewsInfoSameDs.value.forEach(viewInfo => {
|
||||
if (value) {
|
||||
viewInfo.linkageActive = true
|
||||
sameDatasetComponentCheckAll.value = true
|
||||
linkageFieldAdaptor(viewInfo)
|
||||
} else {
|
||||
viewInfo.linkageActive = false
|
||||
sameDatasetComponentCheckAll.value = false
|
||||
}
|
||||
})
|
||||
checkAllIsIndeterminate.value = false
|
||||
}
|
||||
|
||||
const sameDsShow = computed(
|
||||
() => curLinkageTargetViewsInfoSameDs.value && curLinkageTargetViewsInfoSameDs.value.length > 0
|
||||
)
|
||||
|
||||
const diffDsShow = computed(
|
||||
() => curLinkageTargetViewsInfoDiffDs.value && curLinkageTargetViewsInfoDiffDs.value.length > 0
|
||||
)
|
||||
|
||||
const dialogInit = viewItem => {
|
||||
state.showSelected = false
|
||||
dialogShow.value = true
|
||||
@ -260,16 +365,36 @@ const linkageSetting = curViewId => {
|
||||
curLinkageTargetViewsInfo.value = curLinkageTargetViewsInfo.value.filter(
|
||||
viewInfo => viewInfo.targetViewId !== state.viewId
|
||||
)
|
||||
|
||||
curLinkageTargetViewsInfoSameDs.value = curLinkageTargetViewsInfo.value.filter(
|
||||
viewInfo => viewInfo.tableId === state.tableId
|
||||
)
|
||||
|
||||
curLinkageTargetViewsInfoDiffDs.value = curLinkageTargetViewsInfo.value.filter(
|
||||
viewInfo => viewInfo.tableId !== state.tableId
|
||||
)
|
||||
|
||||
let firstNode
|
||||
if (curLinkageTargetViewsInfo.value && curLinkageTargetViewsInfo.value.length > 0) {
|
||||
firstNode = curLinkageTargetViewsInfo.value[0]
|
||||
let linkageTreeName
|
||||
if (curLinkageTargetViewsInfoSameDs.value && curLinkageTargetViewsInfoSameDs.value.length > 0) {
|
||||
firstNode = curLinkageTargetViewsInfoSameDs.value[0]
|
||||
linkageTreeName = 'sameDs'
|
||||
} else if (
|
||||
curLinkageTargetViewsInfoDiffDs.value &&
|
||||
curLinkageTargetViewsInfoDiffDs.value.length > 0
|
||||
) {
|
||||
firstNode = curLinkageTargetViewsInfoDiffDs.value[0]
|
||||
linkageTreeName = 'diffDs'
|
||||
}
|
||||
state.initState = true
|
||||
nextTick(() => {
|
||||
if (firstNode) {
|
||||
linkageInfoTree.value.setCurrentKey(firstNode.targetViewId)
|
||||
const linkageTree =
|
||||
linkageTreeName === 'sameDs' ? linkageInfoTree.value : linkageInfoTreeDiffDs.value
|
||||
linkageTree.setCurrentKey(firstNode.targetViewId)
|
||||
}
|
||||
nodeClick(firstNode)
|
||||
sameDsTreeSelectedChange()
|
||||
})
|
||||
})
|
||||
}
|
||||
@ -281,6 +406,7 @@ const init = viewItem => {
|
||||
const chartDetails = canvasViewInfo.value[state.viewId]
|
||||
state.curLinkageViewInfo = chartDetails
|
||||
if (chartDetails.tableId) {
|
||||
state.tableId = chartDetails.tableId
|
||||
// 获取当前数据集信息
|
||||
getDatasetDetails(chartDetails.tableId).then(res => {
|
||||
state.curDatasetInfo = res || {}
|
||||
@ -348,10 +474,29 @@ const cancelLinkageSetting = () => {
|
||||
dvMainStore.clearLinkageSettingInfo()
|
||||
}
|
||||
|
||||
const nodeClickPre = (data, treeName) => {
|
||||
if (treeName === 'sameDs') {
|
||||
linkageInfoTree.value.setCurrentKey(data.targetViewId)
|
||||
linkageInfoTreeDiffDs.value.setCurrentKey(null)
|
||||
} else {
|
||||
linkageInfoTree.value.setCurrentKey(null)
|
||||
linkageInfoTreeDiffDs.value.setCurrentKey(data.targetViewId)
|
||||
}
|
||||
nodeClick(data)
|
||||
}
|
||||
|
||||
const nodeClick = data => {
|
||||
state.linkageInfo = data
|
||||
}
|
||||
|
||||
const addLinkageFieldAdaptor = (data, sourceFieldId?, targetFieldId?) => {
|
||||
const linkageFieldItem = {
|
||||
sourceField: sourceFieldId,
|
||||
targetField: targetFieldId
|
||||
}
|
||||
data.linkageFields.push(linkageFieldItem)
|
||||
}
|
||||
|
||||
const addLinkageField = (sourceFieldId?, targetFieldId?) => {
|
||||
const linkageFieldItem = {
|
||||
sourceField: sourceFieldId,
|
||||
@ -369,18 +514,25 @@ const linkageFieldAdaptor = async data => {
|
||||
const targetChartDetails = canvasViewInfo.value[data.targetViewId]
|
||||
if (targetChartDetails && targetChartDetails.tableId && data.linkageFields.length === 0) {
|
||||
if (state.curLinkageViewInfo.tableId === targetChartDetails.tableId) {
|
||||
const curCheckAllAxisStr =
|
||||
JSON.stringify(state.curLinkageViewInfo.xAxis) +
|
||||
JSON.stringify(state.curLinkageViewInfo.xAxisExt)
|
||||
const targetCheckAllAxisStr =
|
||||
JSON.stringify(targetChartDetails.xAxis) + JSON.stringify(targetChartDetails.xAxisExt)
|
||||
state.sourceLinkageInfo.targetViewFields.forEach(item => {
|
||||
if (curCheckAllAxisStr.includes(item.id) && targetCheckAllAxisStr.includes(item.id)) {
|
||||
addLinkageField(item.id, item.id)
|
||||
}
|
||||
})
|
||||
// 只匹配联动字段为0的 避免已经匹配过的重新匹配
|
||||
if (data.linkageFields && data.linkageFields.length === 0) {
|
||||
const curCheckAllAxisStr =
|
||||
JSON.stringify(state.curLinkageViewInfo.xAxis) +
|
||||
JSON.stringify(state.curLinkageViewInfo.xAxisExt)
|
||||
const targetCheckAllAxisStr =
|
||||
JSON.stringify(targetChartDetails.xAxis) + JSON.stringify(targetChartDetails.xAxisExt)
|
||||
state.sourceLinkageInfo.targetViewFields.forEach(item => {
|
||||
if (
|
||||
curCheckAllAxisStr.includes(item.id) &&
|
||||
targetCheckAllAxisStr.includes(item.id) &&
|
||||
data.linkageFields
|
||||
) {
|
||||
addLinkageFieldAdaptor(data, item.id, item.id)
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
addLinkageField('', '')
|
||||
addLinkageFieldAdaptor(data, '', '')
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -399,11 +551,18 @@ const sourceLinkageInfoFilter = computed(() => {
|
||||
}
|
||||
})
|
||||
|
||||
const targetViewCheckedChange = data => {
|
||||
const targetViewCheckedChange = (treeName, data) => {
|
||||
nextTick(() => {
|
||||
linkageInfoTree.value.setCurrentKey(data.targetViewId)
|
||||
if (treeName === 'sameDs') {
|
||||
linkageInfoTree.value.setCurrentKey(data.targetViewId)
|
||||
linkageInfoTreeDiffDs.value.setCurrentKey(null)
|
||||
} else {
|
||||
linkageInfoTree.value.setCurrentKey(null)
|
||||
linkageInfoTreeDiffDs.value.setCurrentKey(data.targetViewId)
|
||||
}
|
||||
nodeClick(data)
|
||||
linkageFieldAdaptor(data)
|
||||
sameDsTreeSelectedChange()
|
||||
})
|
||||
}
|
||||
const cancel = () => {
|
||||
@ -419,6 +578,7 @@ watch(
|
||||
() => state.showSelected,
|
||||
newValue => {
|
||||
linkageInfoTree.value?.filter(newValue)
|
||||
linkageInfoTreeDiffDs.value?.filter(newValue)
|
||||
}
|
||||
)
|
||||
|
||||
@ -734,7 +894,7 @@ span {
|
||||
}
|
||||
|
||||
.custom-tree {
|
||||
height: 100%;
|
||||
max-height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.m-del-icon-btn {
|
||||
@ -758,4 +918,27 @@ span {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.tree-dataset-head {
|
||||
height: 40px;
|
||||
font-size: 14px;
|
||||
align-items: center;
|
||||
padding: 0 14px;
|
||||
justify-content: space-between;
|
||||
span {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
text-align: left;
|
||||
color: #646a73;
|
||||
}
|
||||
}
|
||||
|
||||
.tree-dataset-head-top {
|
||||
border-top: 1px solid rgba(31, 35, 41, 0.15);
|
||||
}
|
||||
|
||||
.toggle-icon {
|
||||
cursor: pointer;
|
||||
margin-right: 8px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -175,7 +175,8 @@ const state = reactive({
|
||||
'label',
|
||||
'word-cloud',
|
||||
'flow-map',
|
||||
'bidirectional-bar'
|
||||
'bidirectional-bar',
|
||||
'symbolic-map'
|
||||
],
|
||||
linkageExcludeViewType: [
|
||||
'richTextView',
|
||||
@ -185,7 +186,8 @@ const state = reactive({
|
||||
'label',
|
||||
'word-cloud',
|
||||
'flow-map',
|
||||
'bidirectional-bar'
|
||||
'bidirectional-bar',
|
||||
'symbolic-map'
|
||||
],
|
||||
copyData: null,
|
||||
hyperlinksSetVisible: false,
|
||||
|
||||
@ -4,6 +4,7 @@ import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
|
||||
const dvMainStore = dvMainStoreWithOut()
|
||||
import screenfull from 'screenfull'
|
||||
import { onBeforeUnmount, onMounted, toRefs } from 'vue'
|
||||
import { useEmitt } from '@/hooks/web/useEmitt'
|
||||
|
||||
const props = defineProps({
|
||||
themes: {
|
||||
@ -25,6 +26,18 @@ const { themes, componentType } = toRefs(props)
|
||||
const fullscreenChange = () => {
|
||||
if (screenfull.isEnabled) {
|
||||
dvMainStore.setFullscreenFlag(screenfull.isFullscreen)
|
||||
// 编辑界面使用
|
||||
if (props.showPosition === 'edit') {
|
||||
if (screenfull.isFullscreen) {
|
||||
dvMainStore.setEditMode('preview')
|
||||
} else {
|
||||
dvMainStore.setEditMode('edit')
|
||||
}
|
||||
}
|
||||
// 大屏编辑使用
|
||||
if (props.showPosition === 'dvEdit') {
|
||||
useEmitt().emitter.emit('canvasScrollRestore')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,11 +48,6 @@ const toggleFullscreen = () => {
|
||||
}
|
||||
}
|
||||
|
||||
const editToggleFullscreen = () => {
|
||||
dvMainStore.setEditMode('preview')
|
||||
toggleFullscreen()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (screenfull.isEnabled) {
|
||||
screenfull.on('change', fullscreenChange)
|
||||
@ -49,21 +57,12 @@ onMounted(() => {
|
||||
onBeforeUnmount(() => {
|
||||
screenfull.off('change', fullscreenChange)
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
toggleFullscreen
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-button v-if="showPosition === 'preview'" secondary @click="toggleFullscreen">
|
||||
<template #icon>
|
||||
<icon name="icon_pc_fullscreen"></icon>
|
||||
</template>
|
||||
全屏</el-button
|
||||
>
|
||||
<el-dropdown-item v-else @click="editToggleFullscreen()">
|
||||
<el-icon style="margin-right: 8px; font-size: 16px">
|
||||
<icon name="icon_pc_fullscreen"></icon>
|
||||
</el-icon>
|
||||
全屏预览
|
||||
</el-dropdown-item>
|
||||
</template>
|
||||
<template><span></span></template>
|
||||
|
||||
<style lang="less" scoped></style>
|
||||
|
||||
@ -1229,7 +1229,12 @@ export default {
|
||||
progress_current: '实际值',
|
||||
gauge_axis_label: '显示刻度',
|
||||
gauge_percentage_tick: '百分比刻度',
|
||||
add_style: '添加样式'
|
||||
add_style: '添加样式',
|
||||
map_symbol_marker: '标记',
|
||||
map_symbol_pentagon: '五角形',
|
||||
map_symbol_hexagon: '六角形',
|
||||
map_symbol_octagon: '八角形',
|
||||
map_symbol_hexagram: '菱形'
|
||||
},
|
||||
dataset: {
|
||||
scope_edit: '仅编辑时生效',
|
||||
|
||||
@ -698,6 +698,18 @@ declare interface ChartLabelAttr {
|
||||
*/
|
||||
seriesLabelFormatter: SeriesFormatter[]
|
||||
|
||||
/**
|
||||
* 显示字段,通过字段名称显示对应的值
|
||||
* @example
|
||||
* ['name', 'value']
|
||||
*/
|
||||
showFields?: string[]
|
||||
|
||||
/**
|
||||
* 自定义显示内容
|
||||
*/
|
||||
customContent?: string
|
||||
|
||||
showGap?: boolean
|
||||
}
|
||||
/**
|
||||
@ -732,6 +744,18 @@ declare interface ChartTooltipAttr {
|
||||
seriesTooltipFormatter: SeriesFormatter[]
|
||||
|
||||
showGap?: boolean
|
||||
|
||||
/**
|
||||
* 显示字段,通过字段名称显示对应的值
|
||||
* @example
|
||||
* ['name', 'value']
|
||||
*/
|
||||
showFields?: string[]
|
||||
|
||||
/**
|
||||
* 自定义显示内容
|
||||
*/
|
||||
customContent?: string
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -24,6 +24,7 @@ declare type EditorProperty =
|
||||
| 'indicator-value-selector'
|
||||
| 'indicator-name-selector'
|
||||
| 'quadrant-selector'
|
||||
| 'map-symbolic-selector'
|
||||
declare type EditorPropertyInner = {
|
||||
[key in EditorProperty]?: string[]
|
||||
}
|
||||
|
||||
@ -4,7 +4,8 @@ import { deepCopy } from '@/utils/utils'
|
||||
import {
|
||||
BASE_VIEW_CONFIG,
|
||||
DEFAULT_INDICATOR_NAME_STYLE,
|
||||
DEFAULT_INDICATOR_STYLE
|
||||
DEFAULT_INDICATOR_STYLE,
|
||||
SENIOR_STYLE_SETTING_LIGHT
|
||||
} from '@/views/chart/components/editor/util/chart'
|
||||
import {
|
||||
DEFAULT_CANVAS_STYLE_DATA_DARK,
|
||||
@ -224,6 +225,8 @@ export const dvMainStore = defineStore('dataVisualization', {
|
||||
},
|
||||
|
||||
setCanvasStyle(style) {
|
||||
style.component['seniorStyleSetting'] =
|
||||
style.component['seniorStyleSetting'] || deepCopy(SENIOR_STYLE_SETTING_LIGHT)
|
||||
this.canvasStyleData = style
|
||||
},
|
||||
setCanvasViewInfo(canvasViewInfo) {
|
||||
|
||||
@ -20,7 +20,7 @@ const props = defineProps({
|
||||
v-else-if="
|
||||
props.view.type &&
|
||||
(includesAny(props.view.type, 'bar', 'line', 'scatter') ||
|
||||
equalsAny(props.view.type, 'waterfall', 'area', 'area-stack', 'flow-map'))
|
||||
equalsAny(props.view.type, 'waterfall', 'area', 'area-stack', 'flow-map', 'symbolic-map'))
|
||||
"
|
||||
>{{ t('chart.drag_block_value_axis') }}</span
|
||||
>
|
||||
|
||||
@ -75,6 +75,10 @@ const props = defineProps({
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
allFields: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
@ -350,6 +354,7 @@ watch(
|
||||
:themes="themes"
|
||||
class="attr-selector"
|
||||
:chart="chart"
|
||||
:all-fields="props.allFields"
|
||||
@onLabelChange="onLabelChange"
|
||||
/>
|
||||
</collapse-switch-item>
|
||||
@ -368,6 +373,7 @@ watch(
|
||||
:property-inner="propertyInnerAll['tooltip-selector']"
|
||||
:themes="themes"
|
||||
:chart="chart"
|
||||
:all-fields="props.allFields"
|
||||
@onTooltipChange="onTooltipChange"
|
||||
@onExtTooltipChange="onExtTooltipChange"
|
||||
/>
|
||||
|
||||
@ -198,6 +198,16 @@ const flowLineTypeOptions = [
|
||||
{ name: t('chart.map_line_type_arc_3d'), value: 'arc3d' }
|
||||
]
|
||||
|
||||
const mapSymbolOptions = [
|
||||
{ name: t('chart.line_symbol_circle'), value: 'circle' },
|
||||
{ name: t('chart.line_symbol_rect'), value: 'square' },
|
||||
{ name: t('chart.line_symbol_triangle'), value: 'triangle' },
|
||||
{ name: t('chart.map_symbol_pentagon'), value: 'pentagon' },
|
||||
{ name: t('chart.map_symbol_hexagon'), value: 'hexagon' },
|
||||
{ name: t('chart.map_symbol_octagon'), value: 'octogon' },
|
||||
{ name: t('chart.line_symbol_diamond'), value: 'rhombus' }
|
||||
]
|
||||
|
||||
onMounted(() => {
|
||||
init()
|
||||
})
|
||||
@ -326,7 +336,7 @@ onMounted(() => {
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<div class="map-style" v-if="showProperty('mapStyle') || showProperty('heatMapStyle')">
|
||||
<div class="map-style" v-if="showProperty('mapBaseStyle') || showProperty('heatMapStyle')">
|
||||
<el-row style="flex: 1">
|
||||
<el-col>
|
||||
<el-form-item
|
||||
@ -337,7 +347,7 @@ onMounted(() => {
|
||||
<el-select
|
||||
:effect="themes"
|
||||
v-model="state.basicStyleForm.mapStyle"
|
||||
@change="changeBasicStyle('mapStyle')"
|
||||
@change="changeBasicStyle('mapBaseStyle')"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in mapStyleOptions"
|
||||
@ -368,7 +378,7 @@ onMounted(() => {
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
<div class="map-flow-style" v-if="showProperty('mapStyle')">
|
||||
<div class="map-flow-style" v-if="showProperty('mapLineStyle')">
|
||||
<el-row style="flex: 1">
|
||||
<el-col>
|
||||
<el-form-item
|
||||
@ -588,6 +598,80 @@ onMounted(() => {
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<div class="map-flow-style" v-if="showProperty('symbolicMapStyle')">
|
||||
<el-row style="flex: 1">
|
||||
<el-col>
|
||||
<el-form-item :label="'符号形状'" class="form-item" :class="'form-item-' + themes">
|
||||
<el-select
|
||||
:effect="themes"
|
||||
v-model="state.basicStyleForm.mapSymbol"
|
||||
@change="changeBasicStyle('mapSymbol')"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in mapSymbolOptions"
|
||||
: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.size') }}
|
||||
</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="40"
|
||||
v-model="state.basicStyleForm.mapSymbolSize"
|
||||
@change="changeBasicStyle('mapSymbolSize')"
|
||||
/>
|
||||
</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">
|
||||
<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.basicStyleForm.mapSymbolOpacity"
|
||||
@change="changeBasicStyle('mapSymbolOpacity')"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<div class="alpha-setting">
|
||||
<label class="alpha-label" :class="{ dark: 'dark' === themes }">
|
||||
{{ t('visualization.borderWidth') }}
|
||||
</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="5"
|
||||
v-model="state.basicStyleForm.mapSymbolStrokeWidth"
|
||||
@change="changeBasicStyle('mapSymbolStrokeWidth')"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
<!--flow map end-->
|
||||
<!--map start-->
|
||||
<el-row :gutter="8">
|
||||
|
||||
@ -1,14 +1,16 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted, PropType, reactive, ref, watch } from 'vue'
|
||||
import { computed, inject, onMounted, PropType, reactive, ref, watch } from 'vue'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { COLOR_PANEL, DEFAULT_LABEL } from '@/views/chart/components/editor/util/chart'
|
||||
import { ElSpace } from 'element-plus-secondary'
|
||||
import { ElIcon, ElSpace } from 'element-plus-secondary'
|
||||
import { formatterType, unitType } from '../../../js/formatter'
|
||||
import { defaultsDeep, cloneDeep, intersection, union, defaultTo } from 'lodash-es'
|
||||
import { includesAny } from '../../util/StringUtils'
|
||||
import { fieldType } from '@/utils/attr'
|
||||
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import Icon from '../../../../../../components/icon-custom/src/Icon.vue'
|
||||
import { deepCopy } from '@/utils/utils'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
@ -17,10 +19,22 @@ const props = defineProps({
|
||||
type: Object as PropType<ChartObj>,
|
||||
required: true
|
||||
},
|
||||
dimensionData: {
|
||||
type: Array<any>,
|
||||
required: false
|
||||
},
|
||||
quotaData: {
|
||||
type: Array<any>,
|
||||
required: false
|
||||
},
|
||||
themes: {
|
||||
type: String as PropType<EditorTheme>,
|
||||
default: 'dark'
|
||||
},
|
||||
allFields: {
|
||||
type: Array<any>,
|
||||
required: false
|
||||
},
|
||||
propertyInner: {
|
||||
type: Array<string>
|
||||
}
|
||||
@ -48,6 +62,7 @@ watch(
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
const curSeriesFormatter = ref<Partial<SeriesFormatter>>({})
|
||||
const formatterEditable = computed(() => {
|
||||
return showProperty('seriesLabelFormatter') && yAxis.value?.length
|
||||
@ -150,7 +165,6 @@ const state = reactive<{ labelForm: ChartLabelAttr | any }>({
|
||||
})
|
||||
|
||||
const emit = defineEmits(['onLabelChange'])
|
||||
|
||||
const changeLabelAttr = prop => {
|
||||
emit('onLabelChange', state.labelForm, prop)
|
||||
}
|
||||
@ -263,6 +277,25 @@ watch(
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
const allFields = computed(() => {
|
||||
return defaultTo(props.allFields, []).map(item => ({
|
||||
key: item.dataeaseName,
|
||||
name: item.name,
|
||||
value: `${item.dataeaseName}@${item.name}`,
|
||||
disabled: false
|
||||
}))
|
||||
})
|
||||
|
||||
const defaultPlaceholder = computed(() => {
|
||||
if (state.labelForm.showFields && state.labelForm.showFields.length > 0) {
|
||||
return state.labelForm.showFields
|
||||
.map(field => {
|
||||
return '${' + field.split('@')[1] + '}'
|
||||
})
|
||||
.join(',')
|
||||
}
|
||||
return ''
|
||||
})
|
||||
onMounted(() => {
|
||||
init()
|
||||
})
|
||||
@ -275,7 +308,7 @@ onMounted(() => {
|
||||
:model="state.labelForm"
|
||||
label-position="top"
|
||||
>
|
||||
<el-row v-show="showEmpty" style="margin-bottom: 12px"> 无其他可设置的属性 </el-row>
|
||||
<el-row v-show="showEmpty" style="margin-bottom: 12px"> 无其他可设置的属性</el-row>
|
||||
<el-space>
|
||||
<el-form-item
|
||||
class="form-item"
|
||||
@ -317,10 +350,56 @@ onMounted(() => {
|
||||
</el-tooltip>
|
||||
</el-form-item>
|
||||
</el-space>
|
||||
|
||||
<div v-if="showProperty('showFields')">
|
||||
<el-form-item :label="t('chart.label')" class="form-item" :class="'form-item-' + themes">
|
||||
<el-select
|
||||
size="small"
|
||||
:effect="themes"
|
||||
filterable
|
||||
multiple
|
||||
collapse-tags
|
||||
collapse-tags-tooltip
|
||||
v-model="state.labelForm.showFields"
|
||||
@change="changeLabelAttr('showFields')"
|
||||
>
|
||||
<el-option
|
||||
v-for="option in allFields"
|
||||
:key="option.key"
|
||||
:label="option.name"
|
||||
:value="option.value"
|
||||
:disabled="option.disabled"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="showProperty('customContent')" class="form-item">
|
||||
<template #label>
|
||||
<span class="data-area-label">
|
||||
<span>
|
||||
{{ t('chart.content_formatter') }}
|
||||
</span>
|
||||
<el-tooltip class="item" :effect="toolTip" placement="bottom">
|
||||
<template #content>
|
||||
<div>可以${fieldName}的形式读取字段值(不支持换行)</div>
|
||||
</template>
|
||||
<el-icon class="hint-icon" :class="{ 'hint-icon--dark': themes === 'dark' }">
|
||||
<Icon name="icon_info_outlined" />
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
</template>
|
||||
<el-input
|
||||
style="font-size: smaller; font-weight: normal"
|
||||
v-model="state.labelForm.customContent"
|
||||
type="textarea"
|
||||
:autosize="{ minRows: 4, maxRows: 4 }"
|
||||
:placeholder="defaultPlaceholder"
|
||||
@blur="changeLabelAttr('customContent')"
|
||||
/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<el-form-item
|
||||
v-if="showProperty('rPosition')"
|
||||
:label="t('chart.label_position')"
|
||||
:label="t('chart.label')"
|
||||
class="form-item"
|
||||
:class="'form-item-' + themes"
|
||||
>
|
||||
@ -876,22 +955,26 @@ onMounted(() => {
|
||||
.form-item-checkbox {
|
||||
margin-bottom: 8px !important;
|
||||
}
|
||||
|
||||
.series-select {
|
||||
:deep(.ed-select__prefix--light) {
|
||||
padding-right: unset;
|
||||
border-right: unset;
|
||||
}
|
||||
|
||||
:deep(.ed-select__prefix--dark) {
|
||||
padding-right: unset;
|
||||
border-right: unset;
|
||||
}
|
||||
}
|
||||
|
||||
.series-select-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding: 0 11px;
|
||||
}
|
||||
|
||||
.m-divider {
|
||||
margin: 0 0 16px;
|
||||
border-color: rgba(31, 35, 41, 0.15);
|
||||
|
||||
@ -2,16 +2,18 @@
|
||||
import { PropType, computed, onMounted, reactive, watch, ref, inject } from 'vue'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { COLOR_PANEL, DEFAULT_TOOLTIP } from '@/views/chart/components/editor/util/chart'
|
||||
import { ElSpace } from 'element-plus-secondary'
|
||||
import { ElIcon, ElSpace } from 'element-plus-secondary'
|
||||
import cloneDeep from 'lodash-es/cloneDeep'
|
||||
import defaultsDeep from 'lodash-es/defaultsDeep'
|
||||
import { formatterType, unitType } from '../../../js/formatter'
|
||||
import { fieldType } from '@/utils/attr'
|
||||
import { partition } from 'lodash-es'
|
||||
import { defaultTo, partition } from 'lodash-es'
|
||||
import chartViewManager from '../../../js/panel'
|
||||
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useEmitt } from '@/hooks/web/useEmitt'
|
||||
import Icon from '../../../../../../components/icon-custom/src/Icon.vue'
|
||||
import { deepCopy } from '@/utils/utils'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
@ -24,6 +26,10 @@ const props = defineProps({
|
||||
type: String as PropType<EditorTheme>,
|
||||
default: 'dark'
|
||||
},
|
||||
allFields: {
|
||||
type: Array<any>,
|
||||
required: false
|
||||
},
|
||||
propertyInner: {
|
||||
type: Array<string>
|
||||
}
|
||||
@ -40,6 +46,7 @@ const quotaData = ref<Axis[]>(inject('quotaData'))
|
||||
const showSeriesTooltipFormatter = computed(() => {
|
||||
return showProperty('seriesTooltipFormatter') && !batchOptStatus.value && props.chart.id
|
||||
})
|
||||
|
||||
// 切换图表类型直接重置为默认
|
||||
const changeChartType = () => {
|
||||
if (!showSeriesTooltipFormatter.value) {
|
||||
@ -89,6 +96,7 @@ const changeDataset = () => {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const AXIS_PROP: AxisType[] = ['yAxis', 'yAxisExt', 'extBubble']
|
||||
const quotaAxis = computed(() => {
|
||||
let result = []
|
||||
@ -356,6 +364,20 @@ const updateAxis = (form: AxisEditForm) => {
|
||||
}
|
||||
})
|
||||
}
|
||||
const allFields = computed(() => {
|
||||
return defaultTo(props.allFields, [])
|
||||
})
|
||||
const defaultPlaceholder = computed(() => {
|
||||
if (state.tooltipForm.showFields && state.tooltipForm.showFields.length > 0) {
|
||||
return state.tooltipForm.showFields
|
||||
.map(field => {
|
||||
const v = field.split('@')
|
||||
return v[1] + ': ${' + field.split('@')[1] + '}'
|
||||
})
|
||||
.join('\n')
|
||||
}
|
||||
return ''
|
||||
})
|
||||
onMounted(() => {
|
||||
init()
|
||||
useEmitt({ name: 'addAxis', callback: updateSeriesTooltipFormatter })
|
||||
@ -374,6 +396,7 @@ onMounted(() => {
|
||||
label-position="top"
|
||||
>
|
||||
<el-form-item
|
||||
v-if="props.chart.type !== 'symbolic-map'"
|
||||
:label="t('chart.background') + t('chart.color')"
|
||||
class="form-item"
|
||||
:class="'form-item-' + themes"
|
||||
@ -430,6 +453,54 @@ onMounted(() => {
|
||||
</el-tooltip>
|
||||
</el-form-item>
|
||||
</el-space>
|
||||
|
||||
<div v-if="showProperty('showFields')">
|
||||
<el-form-item :label="t('chart.tooltip')" class="form-item" :class="'form-item-' + themes">
|
||||
<el-select
|
||||
size="small"
|
||||
:effect="themes"
|
||||
filterable
|
||||
multiple
|
||||
collapse-tags
|
||||
collapse-tags-tooltip
|
||||
v-model="state.tooltipForm.showFields"
|
||||
@change="changeTooltipAttr('showFields')"
|
||||
>
|
||||
<el-option
|
||||
v-for="option in allFields"
|
||||
:key="option.dataeaseName"
|
||||
:label="option.name"
|
||||
:value="option.dataeaseName + '@' + option.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="showProperty('customContent')" class="form-item">
|
||||
<template #label>
|
||||
<span class="data-area-label">
|
||||
<span>
|
||||
{{ t('chart.content_formatter') }}
|
||||
</span>
|
||||
<el-tooltip class="item" :effect="toolTip" placement="bottom">
|
||||
<template #content>
|
||||
<div>可以${fieldName}的形式读取字段值(支持HTML)</div>
|
||||
</template>
|
||||
<el-icon class="hint-icon" :class="{ 'hint-icon--dark': themes === 'dark' }">
|
||||
<Icon name="icon_info_outlined" />
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
</template>
|
||||
<el-input
|
||||
style="font-size: smaller; font-weight: normal"
|
||||
v-model="state.tooltipForm.customContent"
|
||||
type="textarea"
|
||||
:autosize="{ minRows: 4, maxRows: 4 }"
|
||||
:placeholder="defaultPlaceholder"
|
||||
@blur="changeTooltipAttr('customContent')"
|
||||
/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<template v-if="showProperty('tooltipFormatter') && !isBarRangeTime">
|
||||
<el-form-item
|
||||
:label="t('chart.value_formatter_type')"
|
||||
|
||||
@ -761,7 +761,9 @@ const calcData = (view, resetDrill = false, updateQuery = '') => {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const updateChartDataTest = arg => {
|
||||
updateChartData(arg)
|
||||
}
|
||||
const updateChartData = view => {
|
||||
curComponent.value['state'] = 'ready'
|
||||
calcData(view, true, 'updateQuery')
|
||||
@ -1608,7 +1610,7 @@ const deleteChartFieldItem = id => {
|
||||
:dimension="state.dimension"
|
||||
:quota="state.quota"
|
||||
:themes="themes"
|
||||
@update-chart-data="updateChartData"
|
||||
@update-chart-data-test="updateChartDataTest"
|
||||
/>
|
||||
<el-tabs
|
||||
v-else
|
||||
@ -2458,6 +2460,7 @@ const deleteChartFieldItem = id => {
|
||||
:themes="themes"
|
||||
:dimension-data="state.dimension"
|
||||
:quota-data="state.quota"
|
||||
:all-fields="allFields"
|
||||
@onColorChange="onColorChange"
|
||||
@onMiscChange="onMiscChange"
|
||||
@onLabelChange="onLabelChange"
|
||||
|
||||
@ -1325,6 +1325,13 @@ export const CHART_TYPE_CONFIGS = [
|
||||
value: 'heat-map',
|
||||
title: t('chart.chart_heat_map'),
|
||||
icon: 'heat-map'
|
||||
},
|
||||
{
|
||||
render: 'antv',
|
||||
category: 'map',
|
||||
value: 'symbolic-map',
|
||||
title: '符号地图',
|
||||
icon: 'symbolic-map'
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -1454,7 +1461,7 @@ export const DEFAULT_BASIC_STYLE: ChartBasicStyle = {
|
||||
mapSymbolOpacity: 0.7,
|
||||
mapSymbolStrokeWidth: 2,
|
||||
mapSymbol: 'circle',
|
||||
mapSymbolSize: 20,
|
||||
mapSymbolSize: 6,
|
||||
radius: 80,
|
||||
innerRadius: 60,
|
||||
showZoom: true,
|
||||
|
||||
@ -25,7 +25,7 @@ export class FlowMap extends L7ChartView<Scene, L7Config> {
|
||||
]
|
||||
propertyInner: EditorPropertyInner = {
|
||||
...MAP_EDITOR_PROPERTY_INNER,
|
||||
'basic-style-selector': ['mapStyle', 'zoom']
|
||||
'basic-style-selector': ['mapBaseStyle', 'mapLineStyle', 'zoom']
|
||||
}
|
||||
axis: AxisType[] = ['xAxis', 'xAxisExt', 'filter']
|
||||
axisConfig: AxisConfig = {
|
||||
|
||||
@ -0,0 +1,339 @@
|
||||
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, hexColorToRGBA, 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 { PointLayer } from '@antv/l7-layers'
|
||||
import { LayerPopup } from '@antv/l7'
|
||||
import { queryMapKeyApi } from '@/api/setting/sysParameter'
|
||||
const { t } = useI18n()
|
||||
|
||||
/**
|
||||
* 符号地图
|
||||
*/
|
||||
export class SymbolicMap extends L7ChartView<Scene, L7Config> {
|
||||
properties: EditorProperty[] = [
|
||||
'background-overall-component',
|
||||
'basic-style-selector',
|
||||
'title-selector',
|
||||
'label-selector',
|
||||
'tooltip-selector',
|
||||
'jump-set',
|
||||
'linkage'
|
||||
]
|
||||
propertyInner: EditorPropertyInner = {
|
||||
...MAP_EDITOR_PROPERTY_INNER,
|
||||
'basic-style-selector': ['colors', 'alpha', 'mapBaseStyle', 'symbolicMapStyle', 'zoom'],
|
||||
'label-selector': ['color', 'fontSize', 'showFields', 'customContent'],
|
||||
'tooltip-selector': ['color', 'fontSize', 'showFields', 'customContent', 'show']
|
||||
}
|
||||
axis: AxisType[] = ['xAxis', 'xAxisExt', 'extBubble', 'filter', 'extLabel', 'extTooltip']
|
||||
axisConfig: AxisConfig = {
|
||||
xAxis: {
|
||||
name: `经纬度 / ${t('chart.dimension')}`,
|
||||
type: 'd',
|
||||
limit: 2
|
||||
},
|
||||
xAxisExt: {
|
||||
name: `颜色 / ${t('chart.dimension')}`,
|
||||
type: 'd',
|
||||
limit: 1
|
||||
},
|
||||
extBubble: {
|
||||
name: `${t('chart.bubble_size')} / ${t('chart.quota')}`,
|
||||
type: 'q',
|
||||
limit: 1
|
||||
}
|
||||
}
|
||||
constructor() {
|
||||
super('symbolic-map', [])
|
||||
}
|
||||
|
||||
async drawChart(drawOption: L7DrawConfig<L7Config>) {
|
||||
const { chart, container, action } = 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 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,
|
||||
center: [104.434765, 38.256735],
|
||||
zoom: 1.8
|
||||
})
|
||||
})
|
||||
if (xAxis?.length < 2 || xAxisExt?.length < 1) {
|
||||
return new L7Wrapper(scene, undefined)
|
||||
}
|
||||
const configList: L7Config[] = []
|
||||
const symbolicLayer = this.buildSymbolicLayer(chart, basicStyle)
|
||||
configList.push(symbolicLayer)
|
||||
const tooltipLayer = this.buildTooltip(chart, symbolicLayer)
|
||||
if (tooltipLayer) {
|
||||
scene.addPopup(tooltipLayer)
|
||||
}
|
||||
this.buildLabel(chart, configList)
|
||||
this.configZoomButton(chart, scene)
|
||||
symbolicLayer.on('click', ev => {
|
||||
const data = ev.feature
|
||||
action({
|
||||
x: ev.x,
|
||||
y: ev.y,
|
||||
data: {
|
||||
data: {
|
||||
...data,
|
||||
dimensionList: chart.data.data.filter(item => item.field === ev.feature.field)?.[0]
|
||||
?.dimensionList,
|
||||
quotaList: chart.data.data.filter(item => item.field === ev.feature.field)?.[0]
|
||||
?.quotaList
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
return new L7Wrapper(scene, configList)
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建符号图层
|
||||
* @param chart
|
||||
*/
|
||||
buildSymbolicLayer = (chart, basicStyle) => {
|
||||
const xAxis = deepCopy(chart.xAxis)
|
||||
const xAxisExt = deepCopy(chart.xAxisExt)
|
||||
const extBubble = deepCopy(chart.extBubble)
|
||||
const { mapSymbolOpacity, mapSymbolSize, mapSymbol, mapSymbolStrokeWidth, colors, alpha } =
|
||||
deepCopy(basicStyle)
|
||||
const c = []
|
||||
colors.forEach(ele => {
|
||||
c.push(hexColorToRGBA(ele, alpha))
|
||||
})
|
||||
const sizeKey = extBubble.length > 0 ? extBubble[0].dataeaseName : ''
|
||||
const data = chart.data?.tableRow
|
||||
? chart.data?.tableRow.map((item, index) => ({
|
||||
...item,
|
||||
color: c[index % c.length],
|
||||
size: item[sizeKey] ? item[sizeKey] : mapSymbolSize,
|
||||
field:
|
||||
item[xAxis[0].dataeaseName] +
|
||||
'000\n' +
|
||||
item[xAxis[1].dataeaseName] +
|
||||
'000\n' +
|
||||
item[xAxisExt[0].dataeaseName],
|
||||
name: item[xAxisExt[0].dataeaseName]
|
||||
}))
|
||||
: []
|
||||
const color = xAxisExt && xAxisExt.length > 0 ? 'color' : c[0]
|
||||
const pointLayer = new PointLayer()
|
||||
.source(data, {
|
||||
parser: {
|
||||
type: 'json',
|
||||
x: xAxis[0].dataeaseName,
|
||||
y: xAxis[1].dataeaseName
|
||||
}
|
||||
})
|
||||
.shape(mapSymbol)
|
||||
.color(color)
|
||||
.style({
|
||||
stroke: {
|
||||
field: 'color'
|
||||
},
|
||||
strokeWidth: mapSymbolStrokeWidth,
|
||||
opacity: {
|
||||
field: (mapSymbolOpacity / 100) * 10
|
||||
}
|
||||
})
|
||||
.active(true)
|
||||
if (sizeKey) {
|
||||
pointLayer.size('size', [4, 30])
|
||||
} else {
|
||||
pointLayer.size(mapSymbolSize)
|
||||
}
|
||||
return pointLayer
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并详情到 map
|
||||
* @param details
|
||||
* @returns {Map<string, any>}
|
||||
*/
|
||||
mergeDetailsToMap = details => {
|
||||
const resultMap = new Map()
|
||||
details.forEach(item => {
|
||||
Object.entries(item).forEach(([key, value]) => {
|
||||
if (resultMap.has(key)) {
|
||||
const existingValue = resultMap.get(key)
|
||||
if (existingValue !== value) {
|
||||
resultMap.set(key, `${existingValue}, ${value}`)
|
||||
}
|
||||
} else {
|
||||
resultMap.set(key, value)
|
||||
}
|
||||
})
|
||||
})
|
||||
return resultMap
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建 tooltip
|
||||
* @param chart
|
||||
* @param pointLayer
|
||||
*/
|
||||
buildTooltip = (chart, pointLayer) => {
|
||||
const customAttr = chart.customAttr ? parseJson(chart.customAttr) : null
|
||||
if (customAttr?.tooltip?.show) {
|
||||
const { tooltip } = deepCopy(customAttr)
|
||||
let showFields = tooltip.showFields || []
|
||||
if (!tooltip.showFields || tooltip.showFields.length === 0) {
|
||||
showFields = [
|
||||
...chart.xAxisExt.map(i => `${i.dataeaseName}@${i.name}`),
|
||||
...chart.xAxis.map(i => `${i.dataeaseName}@${i.name}`)
|
||||
]
|
||||
}
|
||||
const htmlPrefix = `<div style='font-size:${tooltip.fontSize}px;color:${tooltip.color}'>`
|
||||
const htmlSuffix = '</div>'
|
||||
|
||||
return new LayerPopup({
|
||||
items: [
|
||||
{
|
||||
layer: pointLayer,
|
||||
customContent: item => {
|
||||
const fieldData = {
|
||||
...item,
|
||||
...Object.fromEntries(this.mergeDetailsToMap(item.details))
|
||||
}
|
||||
const content = this.buildTooltipContent(tooltip, fieldData, showFields)
|
||||
return `${htmlPrefix}${content}${htmlSuffix}`
|
||||
}
|
||||
}
|
||||
],
|
||||
trigger: 'hover'
|
||||
})
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建 tooltip 内容
|
||||
* @param tooltip
|
||||
* @param fieldData
|
||||
* @param showFields
|
||||
* @returns {string}
|
||||
*/
|
||||
buildTooltipContent = (tooltip, fieldData, showFields) => {
|
||||
let content = ''
|
||||
if (tooltip.customContent) {
|
||||
content = tooltip.customContent
|
||||
showFields.forEach(field => {
|
||||
content = content.replace(`\${${field.split('@')[1]}}`, fieldData[field.split('@')[0]])
|
||||
})
|
||||
} else {
|
||||
showFields.forEach(field => {
|
||||
//const value = ${fieldData[field.split('@')[0]] as string
|
||||
content += `${field.split('@')[1]}: ${fieldData[field.split('@')[0]]}<br>`
|
||||
})
|
||||
}
|
||||
return content
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建 label
|
||||
* @param chart
|
||||
* @param configList
|
||||
*/
|
||||
buildLabel = (chart, configList) => {
|
||||
const xAxis = deepCopy(chart.xAxis)
|
||||
|
||||
const customAttr = chart.customAttr ? parseJson(chart.customAttr) : null
|
||||
if (customAttr?.label?.show) {
|
||||
const { label } = customAttr
|
||||
const data = chart.data?.tableRow || []
|
||||
let showFields = label.showFields || []
|
||||
if (!label.showFields || label.showFields.length === 0) {
|
||||
showFields = [
|
||||
...chart.xAxisExt.map(i => `${i.dataeaseName}@${i.name}`),
|
||||
...chart.xAxis.map(i => `${i.dataeaseName}@${i.name}`)
|
||||
]
|
||||
}
|
||||
data.forEach(item => {
|
||||
const fieldData = {
|
||||
...item,
|
||||
...Object.fromEntries(this.mergeDetailsToMap(item.details))
|
||||
}
|
||||
let content = label.customContent || ''
|
||||
|
||||
if (content) {
|
||||
showFields.forEach(field => {
|
||||
const [fieldKey, fieldName] = field.split('@')
|
||||
content = content.replace(`\${${fieldName}}`, fieldData[fieldKey])
|
||||
})
|
||||
} else {
|
||||
content = showFields.map(field => fieldData[field.split('@')[0]]).join(',')
|
||||
}
|
||||
|
||||
content = content.replace(/\n/g, '')
|
||||
item.textLayerContent = content
|
||||
})
|
||||
|
||||
configList.push(
|
||||
new PointLayer()
|
||||
.source(data, {
|
||||
parser: {
|
||||
type: 'json',
|
||||
x: xAxis[0].dataeaseName,
|
||||
y: xAxis[1].dataeaseName
|
||||
}
|
||||
})
|
||||
.shape('textLayerContent', 'text')
|
||||
.color(label.color)
|
||||
.size(label.fontSize)
|
||||
.style({
|
||||
textAnchor: 'center',
|
||||
textOffset: [0, 0]
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
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.label = {
|
||||
...chart.customAttr.label,
|
||||
show: false
|
||||
}
|
||||
chart.customAttr.basicStyle = {
|
||||
...chart.customAttr.basicStyle,
|
||||
mapSymbolOpacity: 5
|
||||
}
|
||||
return chart
|
||||
}
|
||||
|
||||
protected setupOptions(chart: Chart, config: L7Config): L7Config {
|
||||
return flow(this.configEmptyDataStrategy, this.configTooltip, this.configLabel)(chart, config)
|
||||
}
|
||||
}
|
||||
@ -5,10 +5,14 @@ import {
|
||||
ChartLibraryType,
|
||||
ChartWrapper
|
||||
} from '@/views/chart/components/js/panel/types'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { cloneDeep, defaultsDeep } 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'
|
||||
import {
|
||||
configL7Label,
|
||||
configL7Tooltip,
|
||||
configL7Zoom
|
||||
} from '@/views/chart/components/js/panel/common/common_antv'
|
||||
|
||||
export type L7DrawConfig<P> = AntVDrawOptions<P>
|
||||
export interface L7Config extends ILayer {
|
||||
@ -48,10 +52,12 @@ export class L7Wrapper<
|
||||
}
|
||||
|
||||
handleConfig = (config: L7Config) => {
|
||||
if (config.handleConfig) {
|
||||
config.handleConfig?.(this.scene)
|
||||
} else {
|
||||
this.scene.addLayer(config)
|
||||
if (config) {
|
||||
if (config.handleConfig) {
|
||||
config.handleConfig?.(this.scene)
|
||||
} else {
|
||||
this.scene.addLayer(config)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -88,6 +94,18 @@ export abstract class L7ChartView<
|
||||
configL7Zoom(chart, plot)
|
||||
}
|
||||
|
||||
protected configLabel(chart: Chart, options: O): O {
|
||||
const label = configL7Label(chart)
|
||||
defaultsDeep(options.label, label)
|
||||
return options
|
||||
}
|
||||
|
||||
protected configTooltip(chart: Chart, options: O): O {
|
||||
const tooltip = configL7Tooltip(chart)
|
||||
defaultsDeep(options.tooltip, tooltip)
|
||||
return options
|
||||
}
|
||||
|
||||
protected constructor(name: string, defaultData: any[]) {
|
||||
super(ChartLibraryType.L7, name, defaultData)
|
||||
}
|
||||
|
||||
@ -306,7 +306,11 @@ const action = param => {
|
||||
}
|
||||
trackBarStyleCheck(props.element, barStyleTemp, props.scale, trackMenu.value.length)
|
||||
state.trackBarStyle.left = barStyleTemp.left + 'px'
|
||||
state.trackBarStyle.top = barStyleTemp.top + 'px'
|
||||
if (curView.type === 'symbolic-map') {
|
||||
state.trackBarStyle.top = param.y + 10 + 'px'
|
||||
} else {
|
||||
state.trackBarStyle.top = barStyleTemp.top + 'px'
|
||||
}
|
||||
viewTrack.value.trackButtonClick()
|
||||
}
|
||||
}
|
||||
|
||||
@ -603,6 +603,9 @@ const chartAreaShow = computed(() => {
|
||||
if (view.value.type === 'rich-text') {
|
||||
return true
|
||||
}
|
||||
if (view.value?.plugin?.isPlugin) {
|
||||
return true
|
||||
}
|
||||
if (view.value['dataFrom'] === 'template') {
|
||||
return true
|
||||
}
|
||||
|
||||
@ -43,6 +43,7 @@ const eventCheck = e => {
|
||||
const dvMainStore = dvMainStoreWithOut()
|
||||
const snapshotStore = snapshotStoreWithOut()
|
||||
const {
|
||||
fullscreenFlag,
|
||||
componentData,
|
||||
curComponent,
|
||||
canvasStyleData,
|
||||
@ -234,7 +235,7 @@ onUnmounted(() => {
|
||||
:class="{ 'preview-content': editMode === 'preview' }"
|
||||
>
|
||||
<!-- 中间画布 -->
|
||||
<main class="center">
|
||||
<main class="center" :class="{ 'de-screen-full': fullscreenFlag }">
|
||||
<de-canvas
|
||||
style="overflow-x: hidden"
|
||||
v-if="dataInitState"
|
||||
@ -257,7 +258,6 @@ onUnmounted(() => {
|
||||
:side-name="'componentProp'"
|
||||
:aside-position="'right'"
|
||||
class="left-sidebar"
|
||||
:class="{ 'preview-aside': editMode === 'preview' }"
|
||||
>
|
||||
<component :is="findComponent(curComponent['component'] + 'Attr')" :themes="'light'" />
|
||||
</dv-sidebar>
|
||||
@ -268,15 +268,10 @@ onUnmounted(() => {
|
||||
:width="420"
|
||||
aside-position="right"
|
||||
class="left-sidebar"
|
||||
:class="{ 'preview-aside': editMode === 'preview' }"
|
||||
>
|
||||
<DbCanvasAttr></DbCanvasAttr>
|
||||
</dv-sidebar>
|
||||
<div
|
||||
v-show="viewEditorShow"
|
||||
style="height: 100%"
|
||||
:class="{ 'preview-aside': editMode === 'preview' }"
|
||||
>
|
||||
<div v-show="viewEditorShow" style="height: 100%">
|
||||
<view-editor
|
||||
:themes="'light'"
|
||||
:view="canvasViewInfo[curComponent ? curComponent.id : 'default']"
|
||||
@ -291,7 +286,6 @@ onUnmounted(() => {
|
||||
aside-position="right"
|
||||
class="left-sidebar"
|
||||
:side-name="'batchOpt'"
|
||||
:class="{ 'preview-aside': editMode === 'preview' }"
|
||||
>
|
||||
<chart-style-batch-set></chart-style-batch-set>
|
||||
</dv-sidebar>
|
||||
|
||||
@ -0,0 +1,89 @@
|
||||
<script setup lang="ts">
|
||||
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
|
||||
import { ref } from 'vue'
|
||||
import DePreview from '@/components/data-visualization/canvas/DePreview.vue'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
const dvMainStore = dvMainStoreWithOut()
|
||||
const { fullscreenFlag } = storeToRefs(dvMainStore)
|
||||
const dePreviewRef = ref(null)
|
||||
const dataInitState = ref(true)
|
||||
defineProps({
|
||||
canvasStylePreview: {
|
||||
required: true,
|
||||
type: Object
|
||||
},
|
||||
canvasDataPreview: {
|
||||
required: true,
|
||||
type: Object
|
||||
},
|
||||
canvasViewInfoPreview: {
|
||||
required: true,
|
||||
type: Object
|
||||
},
|
||||
dvInfo: {
|
||||
required: true,
|
||||
type: Object
|
||||
},
|
||||
curPreviewGap: {
|
||||
required: false,
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
showPosition: {
|
||||
required: false,
|
||||
type: String,
|
||||
default: 'preview'
|
||||
},
|
||||
downloadStatus: {
|
||||
required: false,
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const restore = () => {
|
||||
dePreviewRef.value.restore()
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
restore
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="de-preview-content" :class="{ 'de-screen-full': fullscreenFlag }" class="content-outer">
|
||||
<div class="content-inner">
|
||||
<de-preview
|
||||
ref="dePreviewRef"
|
||||
v-if="canvasStylePreview && dataInitState"
|
||||
:component-data="canvasDataPreview"
|
||||
:canvas-style-data="canvasStylePreview"
|
||||
:canvas-view-info="canvasViewInfoPreview"
|
||||
:dv-info="dvInfo"
|
||||
:cur-gap="curPreviewGap"
|
||||
:show-position="showPosition"
|
||||
:download-status="downloadStatus"
|
||||
></de-preview>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less">
|
||||
.content-outer {
|
||||
width: 100%;
|
||||
height: calc(100vh - 112px);
|
||||
background: #f5f6f7;
|
||||
display: flex;
|
||||
overflow-y: auto;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
justify-content: center; /* 上下居中 */
|
||||
.content-inner {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -16,6 +16,7 @@ const emit = defineEmits(['reload', 'download', 'downloadAsAppTemplate'])
|
||||
const { t } = useI18n()
|
||||
|
||||
const favorited = ref(false)
|
||||
const fullScreeRef = ref(null)
|
||||
const preview = () => {
|
||||
const url = '#/preview?dvId=' + dvInfo.value.id
|
||||
const newWindow = window.open(url, '_blank')
|
||||
@ -103,7 +104,13 @@ const initOpenHandler = newWindow => {
|
||||
</el-popover>
|
||||
</div>
|
||||
<div class="canvas-opt-button">
|
||||
<de-fullscreen v-if="!isDataEaseBi"></de-fullscreen>
|
||||
<de-fullscreen ref="fullScreeRef"></de-fullscreen>
|
||||
<el-button v-if="!isDataEaseBi" secondary @click="() => fullScreeRef.toggleFullscreen()">
|
||||
<template #icon>
|
||||
<icon name="icon_pc_fullscreen"></icon>
|
||||
</template>
|
||||
全屏</el-button
|
||||
>
|
||||
<el-button secondary v-if="!isDataEaseBi" @click="preview()">
|
||||
<template #icon>
|
||||
<icon name="icon_pc_outlined"></icon>
|
||||
|
||||
@ -3,7 +3,6 @@ import DeResourceTree from '@/views/common/DeResourceTree.vue'
|
||||
import { dvMainStoreWithOut } from '@/store/modules/data-visualization/dvMain'
|
||||
import ArrowSide from '@/views/common/DeResourceArrow.vue'
|
||||
import { nextTick, onBeforeMount, reactive, ref, computed } from 'vue'
|
||||
import DePreview from '@/components/data-visualization/canvas/DePreview.vue'
|
||||
import PreviewHead from '@/views/data-visualization/PreviewHead.vue'
|
||||
import EmptyBackground from '@/components/empty-background/src/EmptyBackground.vue'
|
||||
import { storeToRefs } from 'pinia'
|
||||
@ -15,11 +14,12 @@ import { useMoveLine } from '@/hooks/web/useMoveLine'
|
||||
import { Icon } from '@/components/icon-custom'
|
||||
import { download2AppTemplate, downloadCanvas2 } from '@/utils/imgUtils'
|
||||
import MultiplexPreviewShow from '@/views/data-visualization/MultiplexPreviewShow.vue'
|
||||
import DvPreview from '@/views/data-visualization/DvPreview.vue'
|
||||
|
||||
const dvMainStore = dvMainStoreWithOut()
|
||||
const { dvInfo, fullscreenFlag } = storeToRefs(dvMainStore)
|
||||
const { dvInfo } = storeToRefs(dvMainStore)
|
||||
const previewCanvasContainer = ref(null)
|
||||
const dvPreview = ref(null)
|
||||
const dvPreviewRef = ref(null)
|
||||
const slideShow = ref(true)
|
||||
const requestStore = useRequestStoreWithOut()
|
||||
const permissionStore = usePermissionStoreWithOut()
|
||||
@ -83,7 +83,7 @@ const loadCanvasData = (dvId, weight?) => {
|
||||
if (props.showPosition === 'preview') {
|
||||
dvMainStore.updateCurDvInfo(dvInfo)
|
||||
nextTick(() => {
|
||||
dvPreview.value?.restore()
|
||||
dvPreviewRef.value?.restore()
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -216,25 +216,17 @@ onBeforeMount(() => {
|
||||
></multiplex-preview-show>
|
||||
</div>
|
||||
<div v-if="showPosition === 'preview'" ref="previewCanvasContainer" class="content">
|
||||
<div
|
||||
id="de-preview-content"
|
||||
:class="{ 'de-screen-full': fullscreenFlag }"
|
||||
class="content-outer"
|
||||
>
|
||||
<div class="content-inner">
|
||||
<de-preview
|
||||
ref="dvPreview"
|
||||
v-if="state.canvasStylePreview && dataInitState"
|
||||
:component-data="state.canvasDataPreview"
|
||||
:canvas-style-data="state.canvasStylePreview"
|
||||
:canvas-view-info="state.canvasViewInfoPreview"
|
||||
:dv-info="state.dvInfo"
|
||||
:cur-gap="state.curPreviewGap"
|
||||
:show-position="showPosition"
|
||||
:download-status="downloadStatus"
|
||||
></de-preview>
|
||||
</div>
|
||||
</div>
|
||||
<dv-preview
|
||||
ref="dvPreviewRef"
|
||||
v-if="state.canvasStylePreview && dataInitState"
|
||||
:show-position="showPosition"
|
||||
:canvas-data-preview="state.canvasDataPreview"
|
||||
:canvas-style-preview="state.canvasStylePreview"
|
||||
:canvas-view-info-preview="state.canvasViewInfoPreview"
|
||||
:dv-info="state.dvInfo"
|
||||
:cur-preview-gap="state.curPreviewGap"
|
||||
:download-status="downloadStatus"
|
||||
></dv-preview>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="hasTreeData && mounted">
|
||||
|
||||
@ -38,9 +38,11 @@ import { XpackComponent } from '@/components/plugin'
|
||||
import { Base64 } from 'js-base64'
|
||||
import CanvasCacheDialog from '@/components/visualization/CanvasCacheDialog.vue'
|
||||
import { deepCopy } from '@/utils/utils'
|
||||
import DvPreview from '@/views/data-visualization/DvPreview.vue'
|
||||
const interactiveStore = interactiveStoreWithOut()
|
||||
const embeddedStore = useEmbedded()
|
||||
const { wsCache } = useCache()
|
||||
const dvPreviewRef = ref(null)
|
||||
const eventCheck = e => {
|
||||
if (e.key === 'screen-weight' && !compareStorage(e.oldValue, e.newValue)) {
|
||||
const opt = embeddedStore.opt || router.currentRoute.value.query.opt
|
||||
@ -63,6 +65,7 @@ const composeStore = composeStoreWithOut()
|
||||
const canvasCacheOutRef = ref(null)
|
||||
|
||||
const {
|
||||
fullscreenFlag,
|
||||
componentData,
|
||||
curComponent,
|
||||
isClickComponent,
|
||||
@ -454,6 +457,15 @@ eventBus.on('handleNew', handleNew)
|
||||
@load-fail="XpackLoaded"
|
||||
/>
|
||||
<canvas-cache-dialog ref="canvasCacheOutRef" @doUseCache="doUseCache"></canvas-cache-dialog>
|
||||
<dv-preview
|
||||
v-if="fullscreenFlag"
|
||||
style="z-index: 10"
|
||||
ref="dvPreviewRef"
|
||||
:canvas-data-preview="componentData"
|
||||
:canvas-style-preview="canvasStyleData"
|
||||
:canvas-view-info-preview="canvasViewInfo"
|
||||
:dv-info="dvInfo"
|
||||
></dv-preview>
|
||||
</template>
|
||||
|
||||
<style lang="less">
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
<script lang="tsx" setup>
|
||||
import { ref, reactive, onMounted, PropType, toRefs, watch, onBeforeUnmount } from 'vue'
|
||||
import { ref, reactive, onMounted, PropType, toRefs, watch, onBeforeUnmount, shallowRef } from 'vue'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { Base64 } from 'js-base64'
|
||||
import FixedSizeList from 'element-plus-secondary/es/components/virtual-list/src/components/fixed-size-list.mjs'
|
||||
import { useWindowSize } from '@vueuse/core'
|
||||
import useClipboard from 'vue-clipboard3'
|
||||
import { ElMessage, ElMessageBox, ElIcon } from 'element-plus-secondary'
|
||||
import { Icon } from '@/components/icon-custom'
|
||||
@ -56,7 +58,6 @@ const state = reactive({
|
||||
sqlData: [],
|
||||
variablesTmp: [],
|
||||
dataSourceList: [],
|
||||
tableData: [],
|
||||
table: {
|
||||
name: '',
|
||||
id: ''
|
||||
@ -66,6 +67,8 @@ const state = reactive({
|
||||
}
|
||||
})
|
||||
|
||||
const datasourceTableData = shallowRef([])
|
||||
|
||||
const paginationConfig = reactive({
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
@ -76,6 +79,7 @@ const setActiveName = ({ name, enableCheck }) => {
|
||||
if (!enableCheck) return
|
||||
activeName.value = name
|
||||
}
|
||||
const { height: windowHeight } = useWindowSize()
|
||||
|
||||
const generateColumns = (arr: Field[]) =>
|
||||
arr.map(ele => ({
|
||||
@ -295,7 +299,7 @@ const getSQLPreview = () => {
|
||||
|
||||
let tableList = []
|
||||
watch(searchTable, val => {
|
||||
state.tableData = tableList.filter(ele => ele.tableName.includes(val))
|
||||
datasourceTableData.value = tableList.filter(ele => ele.tableName.includes(val))
|
||||
})
|
||||
|
||||
const getIconName = (type: string) => {
|
||||
@ -328,7 +332,7 @@ const dsChange = (val: string) => {
|
||||
getTables({ datasourceId: val })
|
||||
.then(res => {
|
||||
tableList = res || []
|
||||
state.tableData = [...tableList]
|
||||
datasourceTableData.value = [...tableList]
|
||||
})
|
||||
.finally(() => {
|
||||
dsLoading.value = false
|
||||
@ -473,7 +477,7 @@ const mousedownDrag = () => {
|
||||
<el-icon class="icon-color">
|
||||
<Icon name="reference-table"></Icon>
|
||||
</el-icon>
|
||||
{{ state.tableData.length }}
|
||||
{{ datasourceTableData.length }}
|
||||
</span>
|
||||
</p>
|
||||
<el-input
|
||||
@ -489,7 +493,7 @@ const mousedownDrag = () => {
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div v-if="!state.tableData.length && searchTable !== ''" class="el-empty">
|
||||
<div v-if="!datasourceTableData.length && searchTable !== ''" class="el-empty">
|
||||
<div
|
||||
class="el-empty__description"
|
||||
style="margin-top: 80px; color: #5e6d82; text-align: center"
|
||||
@ -498,97 +502,118 @@ const mousedownDrag = () => {
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="table-checkbox-list">
|
||||
<template v-for="ele in state.tableData" :key="ele.tableName">
|
||||
<div
|
||||
:class="[{ active: activeName === ele.tableName }]"
|
||||
class="list-item_primary"
|
||||
:title="ele.tableName"
|
||||
@click="setActiveName(ele)"
|
||||
>
|
||||
<el-icon class="icon-color">
|
||||
<Icon name="icon_form_outlined"></Icon>
|
||||
</el-icon>
|
||||
<span class="label">{{ ele.tableName }}</span>
|
||||
<span class="name-copy">
|
||||
<el-tooltip effect="dark" :content="t('common.copy')" placement="top">
|
||||
<el-icon class="hover-icon" @click="copyInfo(ele.tableName)">
|
||||
<Icon name="icon_copy_outlined"></Icon>
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
|
||||
<el-popover
|
||||
popper-class="sql-table-info"
|
||||
placement="right"
|
||||
:width="502"
|
||||
:persistent="false"
|
||||
@show="getNodeField(ele)"
|
||||
trigger="click"
|
||||
>
|
||||
<template #reference>
|
||||
<el-icon class="hover-icon">
|
||||
<Icon name="icon_info_outlined"></Icon>
|
||||
<FixedSizeList
|
||||
:itemSize="40"
|
||||
:data="datasourceTableData"
|
||||
:total="datasourceTableData.length"
|
||||
:width="223"
|
||||
:height="windowHeight - 350"
|
||||
:scrollbarAlwaysOn="false"
|
||||
class-name="el-select-dropdown__list"
|
||||
layout="vertical"
|
||||
>
|
||||
<template #default="{ index, style }">
|
||||
<div
|
||||
:class="[{ active: activeName === datasourceTableData[index].tableName }]"
|
||||
class="list-item_primary"
|
||||
:style="style"
|
||||
:title="datasourceTableData[index].tableName"
|
||||
@click="setActiveName(datasourceTableData[index])"
|
||||
>
|
||||
<el-icon class="icon-color">
|
||||
<Icon name="icon_form_outlined"></Icon>
|
||||
</el-icon>
|
||||
<span class="label">{{ datasourceTableData[index].tableName }}</span>
|
||||
<span class="name-copy">
|
||||
<el-tooltip effect="dark" :content="t('common.copy')" placement="top">
|
||||
<el-icon
|
||||
class="hover-icon"
|
||||
@click="copyInfo(datasourceTableData[index].tableName)"
|
||||
>
|
||||
<Icon name="icon_copy_outlined"></Icon>
|
||||
</el-icon>
|
||||
</template>
|
||||
<div class="table-filed" v-loading="gridDataLoading">
|
||||
<div class="top flex-align-center">
|
||||
<div class="title ellipsis">
|
||||
{{ ele.name || ele.tableName }}
|
||||
</div>
|
||||
<el-icon
|
||||
class="hover-icon de-hover-icon-primary"
|
||||
@click.stop="copyInfo(ele.name || ele.tableName)"
|
||||
>
|
||||
<Icon name="icon_copy_outlined"></Icon>
|
||||
</el-tooltip>
|
||||
|
||||
<el-popover
|
||||
popper-class="sql-table-info"
|
||||
placement="right"
|
||||
:width="502"
|
||||
:persistent="false"
|
||||
@show="getNodeField(datasourceTableData[index])"
|
||||
trigger="click"
|
||||
>
|
||||
<template #reference>
|
||||
<el-icon class="hover-icon">
|
||||
<Icon name="icon_info_outlined"></Icon>
|
||||
</el-icon>
|
||||
<div class="num flex-align-center">
|
||||
<el-icon>
|
||||
<Icon name="icon_text-box_outlined"></Icon>
|
||||
</template>
|
||||
<div class="table-filed" v-loading="gridDataLoading">
|
||||
<div class="top flex-align-center">
|
||||
<div class="title ellipsis">
|
||||
{{
|
||||
datasourceTableData[index].name || datasourceTableData[index].tableName
|
||||
}}
|
||||
</div>
|
||||
<el-icon
|
||||
class="hover-icon de-hover-icon-primary"
|
||||
@click.stop="
|
||||
copyInfo(
|
||||
datasourceTableData[index].name || datasourceTableData[index].tableName
|
||||
)
|
||||
"
|
||||
>
|
||||
<Icon name="icon_copy_outlined"></Icon>
|
||||
</el-icon>
|
||||
{{ gridData.length }}
|
||||
<div class="num flex-align-center">
|
||||
<el-icon>
|
||||
<Icon name="icon_text-box_outlined"></Icon>
|
||||
</el-icon>
|
||||
{{ gridData.length }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-grid">
|
||||
<el-table
|
||||
height="405"
|
||||
style="width: 100%"
|
||||
header-cell-class-name="header-cell"
|
||||
:data="gridData"
|
||||
>
|
||||
<el-table-column label="物理字段名">
|
||||
<template #default="scope">
|
||||
<div class="flex-align-center icon">
|
||||
<el-icon>
|
||||
<Icon
|
||||
:className="`field-icon-${fieldType[scope.row.deType]}`"
|
||||
:name="`field_${fieldType[scope.row.deType]}`"
|
||||
></Icon>
|
||||
</el-icon>
|
||||
{{ scope.row.originName }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('common.label')">
|
||||
<template #default="scope">
|
||||
{{ scope.row.description || '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('common.operate')">
|
||||
<template #default="scope">
|
||||
<el-icon
|
||||
class="hover-icon de-hover-icon-primary"
|
||||
@click.stop="copyInfo(scope.row.originName)"
|
||||
>
|
||||
<Icon name="icon_copy_outlined"></Icon>
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-grid">
|
||||
<el-table
|
||||
height="405"
|
||||
style="width: 100%"
|
||||
header-cell-class-name="header-cell"
|
||||
:data="gridData"
|
||||
>
|
||||
<el-table-column label="物理字段名">
|
||||
<template #default="scope">
|
||||
<div class="flex-align-center icon">
|
||||
<el-icon>
|
||||
<Icon
|
||||
:className="`field-icon-${fieldType[scope.row.deType]}`"
|
||||
:name="`field_${fieldType[scope.row.deType]}`"
|
||||
></Icon>
|
||||
</el-icon>
|
||||
{{ scope.row.originName }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('common.label')">
|
||||
<template #default="scope">
|
||||
{{ scope.row.description || '-' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="t('common.operate')">
|
||||
<template #default="scope">
|
||||
<el-icon
|
||||
class="hover-icon de-hover-icon-primary"
|
||||
@click.stop="copyInfo(scope.row.originName)"
|
||||
>
|
||||
<Icon name="icon_copy_outlined"></Icon>
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</el-popover>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-popover>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</FixedSizeList>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sql-code-right" :style="{ width: `calc(100% - ${showLeft ? LeftWidth : 0}px)` }">
|
||||
|
||||
@ -15,10 +15,12 @@ import {
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { useEmitt } from '@/hooks/web/useEmitt'
|
||||
import { ElIcon, ElMessageBox, ElMessage } from 'element-plus-secondary'
|
||||
import FixedSizeList from 'element-plus-secondary/es/components/virtual-list/src/components/fixed-size-list.mjs'
|
||||
import type { Action } from 'element-plus-secondary'
|
||||
import FieldMore from './FieldMore.vue'
|
||||
import EmptyBackground from '@/components/empty-background/src/EmptyBackground.vue'
|
||||
import { Icon } from '@/components/icon-custom'
|
||||
import { useWindowSize } from '@vueuse/core'
|
||||
import CalcFieldEdit from './CalcFieldEdit.vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import UnionEdit from './UnionEdit.vue'
|
||||
@ -201,6 +203,8 @@ const dfsName = (arr, id) => {
|
||||
return name
|
||||
}
|
||||
|
||||
const { height } = useWindowSize()
|
||||
|
||||
const dfsChild = arr => {
|
||||
return arr.filter(ele => {
|
||||
if (ele.leaf) {
|
||||
@ -305,7 +309,9 @@ const confirmCustomTime = () => {
|
||||
}
|
||||
|
||||
watch(searchTable, val => {
|
||||
state.tableData = tableList.filter(ele => ele.tableName.toLowerCase().includes(val.toLowerCase()))
|
||||
datasourceTableData.value = tableList.filter(ele =>
|
||||
ele.tableName.toLowerCase().includes(val.toLowerCase())
|
||||
)
|
||||
})
|
||||
const editeSave = () => {
|
||||
const union = []
|
||||
@ -560,7 +566,7 @@ const dsChange = (val: string) => {
|
||||
return getTables({ datasourceId: val })
|
||||
.then(res => {
|
||||
tableList = res || []
|
||||
state.tableData = [...tableList]
|
||||
datasourceTableData.value = [...tableList]
|
||||
})
|
||||
.finally(() => {
|
||||
dsLoading.value = false
|
||||
@ -664,10 +670,10 @@ const state = reactive({
|
||||
nodeNameList: [],
|
||||
editArr: [],
|
||||
dataSourceList: [],
|
||||
tableData: [],
|
||||
fieldCollapse: ['dimension', 'quota']
|
||||
})
|
||||
|
||||
const datasourceTableData = shallowRef([])
|
||||
const getIconName = (type: number) => {
|
||||
if (type === 1) {
|
||||
return 'time'
|
||||
@ -1281,7 +1287,7 @@ const getDsIconName = data => {
|
||||
<el-icon class="icon-color">
|
||||
<Icon name="reference-table"></Icon>
|
||||
</el-icon>
|
||||
{{ state.tableData.length }}
|
||||
{{ datasourceTableData.length }}
|
||||
</span>
|
||||
</p>
|
||||
<el-input
|
||||
@ -1297,7 +1303,7 @@ const getDsIconName = data => {
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div v-if="!state.tableData.length && searchTable !== ''" class="el-empty">
|
||||
<div v-if="!datasourceTableData.length && searchTable !== ''" class="el-empty">
|
||||
<div
|
||||
class="el-empty__description"
|
||||
style="margin-top: 80px; color: #5e6d82; text-align: center"
|
||||
@ -1319,21 +1325,33 @@ const getDsIconName = data => {
|
||||
</el-icon>
|
||||
<span class="label">自定义SQL</span>
|
||||
</div>
|
||||
<template v-for="ele in state.tableData" :key="ele.tableName">
|
||||
<div
|
||||
class="list-item_primary"
|
||||
:title="ele.tableName"
|
||||
@dragstart="$event => dragstart($event, ele)"
|
||||
@dragend="maskShow = false"
|
||||
:draggable="true"
|
||||
@click="setActiveName(ele)"
|
||||
>
|
||||
<el-icon class="icon-color">
|
||||
<Icon name="reference-table"></Icon>
|
||||
</el-icon>
|
||||
<span class="label">{{ ele.tableName }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<FixedSizeList
|
||||
:itemSize="40"
|
||||
:data="datasourceTableData"
|
||||
:total="datasourceTableData.length"
|
||||
:width="223"
|
||||
:height="height - 305"
|
||||
:scrollbarAlwaysOn="false"
|
||||
class-name="el-select-dropdown__list"
|
||||
layout="vertical"
|
||||
>
|
||||
<template #default="{ index, style }">
|
||||
<div
|
||||
class="list-item_primary"
|
||||
:style="style"
|
||||
:title="datasourceTableData[index].tableName"
|
||||
@dragstart="$event => dragstart($event, datasourceTableData[index])"
|
||||
@dragend="maskShow = false"
|
||||
:draggable="true"
|
||||
@click="setActiveName(datasourceTableData[index])"
|
||||
>
|
||||
<el-icon class="icon-color">
|
||||
<Icon name="reference-table"></Icon>
|
||||
</el-icon>
|
||||
<span class="label">{{ datasourceTableData[index].tableName }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</FixedSizeList>
|
||||
</div>
|
||||
</div>
|
||||
<div class="drag-right" :style="{ width: `calc(100vw - ${showLeft ? LeftWidth : 0}px)` }">
|
||||
@ -1469,7 +1487,7 @@ const getDsIconName = data => {
|
||||
style="width: 100%; height: 100%"
|
||||
>
|
||||
<el-table-column
|
||||
:key="column.dataKey"
|
||||
:key="column.dataKey + column.deType"
|
||||
v-for="(column, index) in columns"
|
||||
:prop="column.dataKey"
|
||||
:label="column.title"
|
||||
|
||||
2
de-xpack
2
de-xpack
@ -1 +1 @@
|
||||
Subproject commit 070ee75a6da1e59e8b021462fe0604cbc04b0f64
|
||||
Subproject commit 0bfb0e20ead5a4711cb54957003b63388067eb8b
|
||||
@ -67,7 +67,7 @@ function _check_apisix_init() {
|
||||
function _prepare_apisix() {
|
||||
if [[ -z $DE_APISIX_KEY ]];then
|
||||
need_init_apisix=true
|
||||
DE_APISIX_KEY=$(head -c 32 /dev/urandom | base64)
|
||||
DE_APISIX_KEY=$(head -c 32 /dev/urandom | base64 | sed 's#/#+#g')
|
||||
export DE_APISIX_KEY=$DE_APISIX_KEY
|
||||
cd $DE_RUNNING_BASE
|
||||
env | grep DE_ >.env
|
||||
@ -420,6 +420,9 @@ function main() {
|
||||
--help)
|
||||
usage
|
||||
;;
|
||||
"")
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
echo "不支持的参数,请使用 help 或 --help 参数获取帮助"
|
||||
;;
|
||||
|
||||
@ -40,6 +40,10 @@ public interface DatasetTableApi {
|
||||
@PostMapping("listByDatasetGroup/{id}")
|
||||
List<DatasetTableFieldDTO> listByDatasetGroup(@PathVariable Long id);
|
||||
|
||||
@Operation(summary = "获取数据集字段map")
|
||||
@PostMapping("listByDsIds")
|
||||
Map<String, List<DatasetTableFieldDTO>> listByDsIds(@RequestBody List<Long> ids);
|
||||
|
||||
@Operation(summary = "删除字段")
|
||||
@PostMapping("delete/{id}")
|
||||
void delete(@PathVariable Long id);
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package io.dataease.api.xpack.component;
|
||||
|
||||
import io.dataease.api.xpack.component.vo.XpackMenuVO;
|
||||
import io.dataease.api.xpack.component.vo.XpackPluginsViewVO;
|
||||
import io.dataease.extensions.view.vo.XpackPluginsViewVO;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
|
||||
|
||||
@ -1,9 +1,14 @@
|
||||
package io.dataease.api.xpack.plugin;
|
||||
|
||||
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
|
||||
import io.dataease.api.xpack.plugin.dto.PluginEditor;
|
||||
import io.dataease.api.xpack.plugin.vo.PluginVO;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestPart;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -14,4 +19,12 @@ public interface PluginApi {
|
||||
@GetMapping("/query")
|
||||
List<PluginVO> query();
|
||||
|
||||
@PostMapping("/install")
|
||||
void install(@RequestPart(value = "file") MultipartFile file);
|
||||
|
||||
@PostMapping("/uninstall/{id}")
|
||||
void uninstall(@PathVariable("id") String id);
|
||||
|
||||
void update(@RequestPart("request") PluginEditor request, @RequestPart(value = "file") MultipartFile file);
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,14 @@
|
||||
package io.dataease.api.xpack.plugin.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
public class PluginEditor implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = -1793403914368070138L;
|
||||
|
||||
private String id;
|
||||
}
|
||||
@ -19,6 +19,7 @@ public class StaticResourceConstants {
|
||||
public static String CUSTOM_MAP_DIR = ensureSuffix(USER_HOME, FILE_SEPARATOR) + "geo";
|
||||
public static String APPEARANCE_DIR = ensureSuffix(USER_HOME, FILE_SEPARATOR) + "appearance";
|
||||
public static String REPORT_DIR = ensureSuffix(USER_HOME, FILE_SEPARATOR) + "report";
|
||||
public static String PLUGIN_DIR = ensureSuffix(USER_HOME, FILE_SEPARATOR) + "plugin";
|
||||
|
||||
public static String MAP_URL = "/map";
|
||||
public static String GEO_URL = "/geo";
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package io.dataease.extensions.view.factory;
|
||||
|
||||
import io.dataease.extensions.view.template.PluginsChartTemplate;
|
||||
import io.dataease.extensions.view.vo.XpackPluginsViewVO;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -21,7 +22,7 @@ public class PluginsChartFactory {
|
||||
templateMap.put(key, template);
|
||||
}
|
||||
|
||||
public static List<String> getViewConfigList() {
|
||||
public static List<XpackPluginsViewVO> getViewConfigList() {
|
||||
return templateMap.values().stream().map(PluginsChartTemplate::getConfig).toList();
|
||||
}
|
||||
|
||||
|
||||
@ -3,40 +3,34 @@ package io.dataease.extensions.view.template;
|
||||
import io.dataease.extensions.view.dto.ChartViewDTO;
|
||||
import io.dataease.extensions.view.dto.ChartViewFieldDTO;
|
||||
import io.dataease.extensions.view.dto.DatasetTableFieldDTO;
|
||||
import io.dataease.extensions.view.factory.PluginsChartFactory;
|
||||
import io.dataease.extensions.view.model.SQLMeta;
|
||||
import io.dataease.extensions.view.vo.XpackPluginsViewVO;
|
||||
import io.dataease.license.utils.JsonUtil;
|
||||
import io.dataease.plugins.template.DataEasePlugin;
|
||||
import io.dataease.plugins.vo.DataEasePluginVO;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class PluginsChartTemplate {
|
||||
public abstract class PluginsChartTemplate implements DataEasePlugin {
|
||||
|
||||
public abstract String getConfig();
|
||||
@Override
|
||||
public void loadPlugin() {
|
||||
XpackPluginsViewVO viewConfig = getConfig();
|
||||
PluginsChartFactory.loadTemplate(viewConfig.getRender(), viewConfig.getCategory(), this);
|
||||
}
|
||||
|
||||
|
||||
public XpackPluginsViewVO getConfig() {
|
||||
DataEasePluginVO pluginInfo = getPluginInfo();
|
||||
String config = pluginInfo.getConfig();
|
||||
return JsonUtil.parseObject(config, XpackPluginsViewVO.class);
|
||||
}
|
||||
|
||||
|
||||
public abstract Map<String, List<ChartViewFieldDTO>> formatChartAxis(ChartViewDTO view);
|
||||
|
||||
/*public Map<String, List<ChartViewFieldDTO>> formatChartAxis(ChartViewDTO view) {
|
||||
Map<String, List<ChartViewFieldDTO>> result = new HashMap<>();
|
||||
|
||||
List<ChartViewFieldDTO> xAxisBase = new ArrayList<>(view.getXAxis());
|
||||
result.put("xAxisBase", xAxisBase);
|
||||
List<ChartViewFieldDTO> xAxis = new ArrayList<>(view.getXAxis());
|
||||
result.put("xAxis", xAxis);
|
||||
List<ChartViewFieldDTO> xAxisExt = new ArrayList<>(view.getXAxisExt());
|
||||
result.put("xAxisExt", xAxisExt);
|
||||
List<ChartViewFieldDTO> yAxis = new ArrayList<>(view.getYAxis());
|
||||
result.put("yAxis", yAxis);
|
||||
List<ChartViewFieldDTO> extStack = new ArrayList<>(view.getExtStack());
|
||||
result.put("extStack", extStack);
|
||||
List<ChartViewFieldDTO> extBubble = new ArrayList<>(view.getExtBubble());
|
||||
result.put("extBubble", extBubble);
|
||||
List<ChartViewFieldDTO> drill = new ArrayList<>(view.getDrillFields());
|
||||
result.put("drill", drill);
|
||||
List<ChartViewFieldDTO> viewFields = new ArrayList<>(view.getViewFields());
|
||||
result.put("viewFields", viewFields);
|
||||
return result;
|
||||
}*/
|
||||
|
||||
public abstract ChartViewDTO calcResult(SQLMeta sqlMeta, List<ChartViewFieldDTO> xaxis, List<ChartViewFieldDTO> yaxis,
|
||||
List<DatasetTableFieldDTO> allFields, boolean crossDs, Map<Long, String> dsTypeMap);
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package io.dataease.api.xpack.component.vo;
|
||||
package io.dataease.extensions.view.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@ -27,6 +27,12 @@
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.dataease</groupId>
|
||||
<artifactId>dataease-license-sdk</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
Loading…
Reference in New Issue
Block a user