feat: 血缘关系 缺少全局scss变量导致的样式问题 #4151

This commit is contained in:
dataeaseShu 2022-12-23 17:37:00 +08:00
parent e4555c8312
commit 49690a655b
8 changed files with 1017 additions and 9 deletions

View File

@ -152,3 +152,30 @@ export const viewOptions = panelId => {
method: 'post'
})
}
export function getDatasourceRelationship(id) {
return request({
url: `/api/relation/datasource/${id}`,
method: 'get',
loading: true
})
}
export function getDatasetRelationship(id) {
return request({
url: `/api/relation/dataset/${id}`,
method: 'get',
loading: true
})
}
export function getPanelRelationship(id) {
return request({
url: `/api/relation/panel/${id}`,
method: 'get',
loading: true
})
}

View File

@ -86,6 +86,23 @@ export function listDatasource() {
})
}
export function getDatasetList() {
return request({
url: 'dataset/table/list',
loading: false,
method: 'post',
data: {}
})
}
export function getPanelGroupList() {
return request({
url: '/panel/group/list',
loading: false,
method: 'get'
})
}
export function listApiDatasource() {
return request({
url: '/datasource/list/api',

View File

@ -135,6 +135,7 @@ export default {
default_login: 'Normal'
},
commons: {
consanguinity: 'Consanguinity',
collapse_navigation: 'Collapse navigation',
operate_cancelled: 'Operation cancelled',
bind: 'Bind',

View File

@ -135,6 +135,7 @@ export default {
default_login: '普通登錄'
},
commons: {
consanguinity: '血緣關係',
collapse_navigation: '收起導航',
operate_cancelled: '已取消操作',
bind: '綁定',

View File

@ -135,6 +135,7 @@ export default {
default_login: '普通登录'
},
commons: {
consanguinity: '血缘关系',
collapse_navigation: '收起导航',
operate_cancelled: '已取消操作',
bind: '绑定',

View File

@ -554,7 +554,7 @@ export default {
}
&.active {
background-color: var(--deWhiteHover, #3370ff);
background-color: var(--deWhiteHover, #e0eaff);
color: var(--primary, #3370ff);
}

View File

@ -0,0 +1,478 @@
<template>
<div id="main" :style="chartSize"></div>
</template>
<script>
import * as echarts from 'echarts'
import {
getDatasourceRelationship,
getDatasetRelationship,
getPanelRelationship
} from '@/api/chart/chart.js'
export default {
props: {
current: {
type: Object,
default: () => {}
},
chartSize: {
type: Object,
default: () => {}
},
},
data() {
return {
measureText: null,
treeData: [],
myChart: null,
datasourcePanel: {
datasource: 'panel',
panel: 'datasource',
dataset: 'panel'
}
}
},
watch: {
chartSize: {
handler(val) {
if (this.myChart) {
this.myChart.resize(val)
}
},
deep: true
}
},
methods: {
getChartData(id) {
switch (this.current.queryType) {
case 'datasource':
this.getDatasourceRelationship(id)
break
case 'dataset':
this.getDatasetRelationship(id)
break
case 'panel':
this.getPanelRelationship(id)
break
default:
break
}
},
getDatasourceRelationship(id) {
getDatasourceRelationship(id).then((res) => {
const arr = res.data || []
this.treeData = []
this.dfsTree(arr, this.current.num)
this.initEchart()
})
},
getDatasetRelationship(id) {
getDatasetRelationship(id).then((res) => {
const arr = res.data ? [res.data] : []
this.treeData = []
this.dfsTree(arr, this.current.num)
this.initEchart()
})
},
getPanelRelationship(id) {
getPanelRelationship(id).then((res) => {
const arr = res.data || []
this.treeData = []
this.dfsTreeFlip(arr)
this.initEchart()
})
},
dfsTreeFlip(arr = [], obj) {
arr.forEach((ele) => {
const { id, name, type, subRelation = [] } = ele
if (subRelation.length) {
this.dfsTreeFlip(subRelation, { id, name })
} else if (type === 'dataset') {
this.treeData.push({ id, name, type, pid: this.current.num })
this.treeData.push({
id: obj.id,
name: obj.name,
type: 'datasource',
pid: id
})
}
})
},
dfsTree(arr = [], pid = 0) {
arr.forEach((ele) => {
const { id, name, type, subRelation = [] } = ele
this.treeData.push({ id, name, type, pid })
if (subRelation.length) {
this.dfsTree(subRelation, id)
}
})
},
deleteRepeat(arr = []) {
let list = JSON.parse(JSON.stringify(arr))
const repeatPanel = {}
list.forEach((ele) => {
if (ele[6] === this.datasourcePanel[this.current.queryType]) {
if (repeatPanel[ele[4]]) {
repeatPanel[ele[4]].push(ele[0])
} else {
repeatPanel[ele[4]] = [ele[0]]
}
}
})
Object.keys(repeatPanel).forEach((ele) => {
if (repeatPanel[ele].length === 1) {
repeatPanel[ele] = undefined
return
}
repeatPanel[ele] = repeatPanel[ele].sort((a, b) => {
return a - b
})[Math.floor(repeatPanel[ele].length / 2)]
})
return list.filter((ele) => {
if (repeatPanel[ele[4]] === undefined) return true
return repeatPanel[ele[4]] === ele[0]
})
},
calculatedLine(arr = []) {
if (!arr.length || arr.length === 1) return
const repeatPanel = {}
const dataItemListMap = {}
const list = []
const root = arr.find((ele) => ele[6] === this.current.queryType)
arr.forEach((ele) => {
const [index, start, end, width, id, name, type, pid] = ele
dataItemListMap[id] = {
index,
start,
end,
width,
id,
name,
type,
pid
}
if (type === 'dataset' && this.current.queryType !== 'dataset') {
list.push([root[0], root[1], start, index, root[3], type, id])
}
})
arr.forEach((ele) => {
const [index, start, end, width, id, name, type, pid] = ele
if (type === this.datasourcePanel[this.current.queryType]) {
let dataset = dataItemListMap[pid]
if (repeatPanel[id]) {
repeatPanel[id].push({ index, start })
} else {
repeatPanel[id] = [{ index, start }]
}
list.push([
dataset.index,
dataset.start,
start,
index,
dataset.width,
type,
id
])
}
})
Object.keys(repeatPanel).forEach((ele) => {
if (repeatPanel[ele].length === 1) {
repeatPanel[ele] = null
return
}
repeatPanel[ele] = repeatPanel[ele].sort((a, b) => {
return a.index - b.index
})[Math.floor(repeatPanel[ele].length / 2)]
})
list.forEach((ele) => {
if (!repeatPanel[ele[6]]) return
const { start, index } = repeatPanel[ele[6]]
ele[2] = start
ele[3] = index
})
return list
},
calculatedWidth(arr = []) {
const c = document.createElement('canvas')
const ctx = c.getContext('2d')
ctx.font = '14px Arial'
const dataItemList = []
let list = []
const repeatPanel = {}
let max = {
dataset: 0,
datasource: 0,
panel: 0
}
const { queryType, num, label } = this.current
max[queryType] = ctx.measureText(label).width + 10
if (!arr.length)
return [
[[0, 0, max[queryType], max[queryType], num, label, queryType, 0]],
{
dataset: 0,
datasource: 0,
panel: 0
}
]
arr.forEach((ele, index) => {
const { id, name, type, pid } = ele
let width = ctx.measureText(name).width + 10
max[type] = Math.max(width, max[type])
dataItemList.push([width, id, name, type, pid])
})
let inserted = false
dataItemList.forEach((ele, index) => {
if (index === Math.floor(dataItemList.length / 2)) {
inserted = true
list.push([
index,
0,
max[queryType],
max[queryType],
num,
label,
queryType,
0
])
}
const [width, id, name, type, pid] = ele
if (type === 'dataset' && this.current.queryType !== 'dataset') {
list.push([
inserted ? index + 1 : index,
max[this.current.queryType],
max[this.current.queryType] + width,
width,
id,
name,
type,
pid
])
}
if (type === 'panel' && this.current.queryType !== 'panel') {
list.push([
inserted ? index + 1 : index,
max.dataset + max.datasource,
max.dataset + max.datasource + width,
width,
id,
name,
type,
pid
])
}
if (type === 'datasource' && this.current.queryType !== 'datasource') {
list.push([
inserted ? index + 1 : index,
max.dataset + max.panel,
max.dataset + max.panel + width,
width,
id,
name,
type,
pid
])
}
})
function maxGap(source, target) {
return Math.min(
Math.max(
list.filter((ele) => ele[6] === source).length * 5,
max[target] / 2,
30
),
30
)
}
let gap = {
dataset: maxGap('dataset', 'datasource'),
datasource: maxGap('datasource', 'datasource'),
panel: maxGap('panel', 'dataset')
}
return [list, gap]
},
initEchart() {
if (this.myChart) {
this.myChart.dispose()
this.myChart = null
}
this.myChart = echarts.init(document.getElementById('main'))
const that = this
let [data, gap] = this.calculatedWidth(this.treeData)
const gapDetail = {
dataset: gap.dataset,
datasource: gap.dataset + gap.panel,
panel: gap.panel + gap.dataset
}
gapDetail[this.current.queryType] = 0
let lineData = this.calculatedLine(data)
data = this.deleteRepeat(data)
let option = {
xAxis: {
show: false, //线,
splitLine: {
show: false //线
}
},
grid: {
top: 10,
bottom: 10,
left: '5%'
},
tooltip: {
show: true,
trigger: 'item',
formatter: (a) => {
return a.value[5]
}
},
yAxis: {
show: false,
type: 'category',
splitLine: {
show: false //线
}
},
series: [
{
type: 'custom',
encode: {
// data 12 X
x: [1, 2],
// data 0 Y
y: 0
},
roam: true,
renderItem: function (params, api) {
let categoryIndex = api.value(0)
let startPoint = api.coord([api.value(1), categoryIndex])
let width = api.value(3)
let height = 22
return {
type: 'group', //group
children: [
{
type: 'text',
position: [
startPoint[0] + gapDetail[api.value(6)],
startPoint[1] - height / 2
], //
z2: 10,
style: {
text: api.value(5), //data
color: '#1F2329',
x: 5,
y: 5
}
},
{
// 'circle', 'sector', 'polygon'
type: 'rect',
z2: 2,
// shape
// echarts.graphic.clipRectByRect
//
shape: echarts.graphic.clipRectByRect(
{
//
x: startPoint[0] + gapDetail[api.value(6)],
y: startPoint[1] - height / 2,
width,
height: height
},
{
//
x: params.coordSys.x,
y: params.coordSys.y,
width: params.coordSys.width,
height: params.coordSys.height
}
),
style: {
...api.style(),
fill: api.value(4) === that.current.num ? '#c2d4ff' : 'none',
stroke: '#3370FF'
}
}
]
}
},
data
},
{
type: 'custom',
encode: {
x: [1, 2],
y: [0, 3]
},
roam: true,
renderItem: function (params, api) {
let categoryIndex = api.value(0)
let categoryIndex2 = api.value(3)
let startPoint = api.coord([api.value(1), categoryIndex])
let endPoint = api.coord([api.value(2), categoryIndex2])
let height = 20
function startPointX1() {
if (api.value(5) === 'dataset') {
return startPoint[0] + api.value(4)
}
if (
api.value(5) === that.datasourcePanel[that.current.queryType]
) {
return startPoint[0] + api.value(4) + gapDetail.dataset
}
if (api.value(5) === that.current.queryType) {
return 0
}
}
let x1 = startPointX1()
return {
type: 'group', //group
children: [
{
type: 'line',
silent: true,
shape: {
x1,
y1: startPoint[1],
x2: endPoint[0] + gapDetail[api.value(5)],
y2: endPoint[1],
percent: 1
},
style: {
stroke: '#3370FF',
lineWidth: 1
}
}
]
}
},
data: lineData
}
]
}
this.myChart.setOption(option, true)
}
}
}
</script>

View File

@ -1,24 +1,507 @@
<template>
<div>
<h2>this is blood relationship page for shutong</h2>
<span>{{ text }}</span>
<div class="consanguinity">
<div class="route-title">{{ $t('commons.consanguinity') }}</div>
<div class="container-wrapper">
<el-form
:rules="rules"
:inline="true"
ref="form"
:model="formInline"
class="de-form-inline"
>
<el-form-item
prop="queryType"
:label="$t('commons.adv_search.search') + $t('table.type')"
>
<el-select
v-model="formInline.queryType"
@change="queryTypeChange"
:placeholder="$t('fu.search_bar.please_select')"
>
<el-option
v-for="item in queryTypeNameList"
:key="item.value"
:label="$t(item.label)"
:value="item.value"
>
</el-option>
</el-select>
</el-form-item>
<el-form-item prop="dataSourceName" :label="queryTypeTitle">
<el-select
v-model="formInline.dataSourceName"
filterable
:placeholder="$t('fu.search_bar.please_select')"
>
<el-option
v-for="item in dataSourceNameList"
:key="item.value"
:label="item.label"
:value="item.value"
>
</el-option>
</el-select>
</el-form-item>
<el-form-item style="float: right">
<de-btn type="primary" @click="onSubmit">{{
$t('commons.adv_search.search')
}}</de-btn>
</el-form-item>
</el-form>
<div class="select-icon">
<i
@click="activeIcon = 'date'"
:class="[activeIcon === 'date' ? 'active-icon' : '']"
class="el-icon-date"
></i>
<svg-icon
icon-class="sys-relationship"
:class="[activeIcon === 'share' ? 'active-icon' : '']"
@click="activeIcon = 'share'"
/>
</div>
<div v-show="activeIcon === 'share'" id="consanguinity">
<consanguinity
:chartSize="chartSize"
:current="current"
ref="consanguinity"
/>
</div>
<div v-show="activeIcon === 'date'" class="consanguinity-table">
<grid-table
v-loading="loading"
:table-data="tableData"
:pagination="paginationConfig"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
>
<el-table-column :label="$t('table.id')" type="index" width="50" />
<el-table-column
prop="datasource"
:formatter="formatter"
show-overflow-tooltip
:label="$t('commons.datasource') + $t('commons.name')"
/>
<el-table-column
prop="dataset"
show-overflow-tooltip
:formatter="formatter"
:label="$t('dataset.name')"
/>
<el-table-column
prop="panel"
show-overflow-tooltip
:formatter="formatter"
:label="$t('app_template.panel_name')"
/>
</grid-table>
</div>
</div>
</div>
</template>
<script>
import {
getDatasourceRelationship,
getDatasetRelationship,
getPanelRelationship
} from '@/api/chart/chart.js'
import {
listDatasource,
getDatasetList,
getPanelGroupList
} from '@/api/dataset/dataset'
import _ from 'lodash'
import GridTable from '@/components/gridTable/index.vue'
import consanguinity from './consanguinity.vue'
import { log } from '@antv/g2plot/lib/utils'
export default {
name: 'Relationship',
name: 'Consanguinity',
components: { GridTable, consanguinity },
data() {
return {
text: ''
formInline: {
queryType: 'datasource',
dataSourceName: ''
},
rules: {
queryType: [{ required: true, trigger: 'blur' }],
dataSourceName: [{ required: true, trigger: 'blur', message: this.$t('chart.name_can_not_empty') }]
},
dataSourceNameList: [],
queryTypeNameList: [
{
label: 'commons.datasource',
value: 'datasource'
},
{
label: 'dataset.datalist',
value: 'dataset'
},
{
label: 'panel.panel',
value: 'panel'
}
],
chartSize: {
height: 0,
width: 0
},
treeData: [],
loading: false,
activeIcon: 'date',
paginationConfig: {
currentPage: 1,
pageSize: 10,
total: 0
}
}
},
computed: {
current() {
const { queryType = '', dataSourceName } = this.formInline
const obj =
this.dataSourceNameList.find((ele) => dataSourceName === ele.value) ||
{}
return {
queryType,
num: obj.value,
label: obj.label
}
},
queryTypeTitle() {
return (
this.$t(
this.queryTypeNameList.find(
(ele) => ele.value === this.formInline.queryType
).label
) + this.$t('commons.name')
)
},
tableData() {
const { currentPage, pageSize } = this.paginationConfig
return this.treeData.slice(
(currentPage - 1) * pageSize,
currentPage * pageSize
)
}
},
created() {
this.init
this.listDatasource()
},
beforeDestroy() {
window.removeEventListener('resize', this.getChartSize)
},
mounted() {
window.addEventListener('resize', this.getChartSize)
this.getChartSize()
},
methods: {
init() {
this.text = 'shutong is a handsome guy'
getChartData() {
const { queryType, dataSourceName: id } = this.formInline
switch (queryType) {
case 'datasource':
this.getDatasourceRelationship(id)
break
case 'dataset':
this.getDatasetRelationship(id)
break
case 'panel':
this.getPanelRelationship(id)
break
default:
break
}
},
getDatasourceRelationship(id) {
getDatasourceRelationship(id).then((res) => {
const arr = res.data || []
this.treeData = []
this.dfsTree(arr, this.current)
this.initTable()
})
},
getDatasetRelationship(id) {
getDatasetRelationship(id).then((res) => {
const arr = res.data ? [res.data] : []
this.treeData = []
this.dfsTree(arr, this.current)
this.initTable()
})
},
getPanelRelationship(id) {
getPanelRelationship(id).then((res) => {
const arr = res.data || []
this.treeData = []
this.dfsTreeFlip(arr, this.current)
this.initTable()
})
},
formatter(row, column, cellValue) {
return cellValue ? cellValue : '-'
},
initTable() {
this.paginationConfig.total = this.treeData.length
},
dfsTreeFlip(arr = [], { queryType, label }) {
arr.forEach((ele) => {
const { name, type, subRelation = [] } = ele
if (subRelation.length) {
this.dfsTreeFlip(subRelation, { label: name })
} else if (type === 'dataset') {
const obj = {}
obj[type] = name
obj.datasource = label
obj.panel = this.current.label
this.treeData.push(obj)
}
})
},
dfsTree(arr = [], { queryType, label }, item) {
arr.forEach((ele) => {
const { name, type, subRelation = [] } = ele
const obj = {}
obj[type] = name
obj[queryType] = label
if (subRelation.length) {
this.dfsTree(subRelation, { queryType: type, label: name }, obj)
} else {
this.treeData.push({ ...item, ...obj })
}
})
},
getChartSize: _.debounce(function () {
const dom = document.querySelector(
this.activeIcon === 'date' ? '.consanguinity-table' : '#consanguinity'
)
this.chartSize = {
height: dom.offsetHeight + 'px',
width: dom.offsetWidth + 'px'
}
}, 200),
listDatasource() {
listDatasource().then((res) => {
const arr = res?.data || []
this.dataSourceNameList = arr.map((ele) => ({
value: ele.id,
label: ele.name
}))
})
},
getDatasetList() {
getDatasetList().then((res) => {
const arr = res?.data || []
this.dataSourceNameList = arr.map((ele) => ({
value: ele.id,
label: ele.name
}))
})
},
getPanelGroupList() {
getPanelGroupList().then((res) => {
const arr = res?.data || []
this.dataSourceNameList = arr.map((ele) => ({
value: ele.id,
label: ele.name
}))
})
},
queryTypeChange(val) {
this.formInline.dataSourceName = ''
switch (val) {
case 'datasource':
this.listDatasource()
break
case 'dataset':
this.getDatasetList()
break
case 'panel':
this.getPanelGroupList()
break
default:
break
}
},
onSubmit() {
this.$refs.form.validate((valid) => {
if (valid) {
if (this.activeIcon === 'date') {
this.getChartData()
} else {
this.$refs.consanguinity.getChartData(
this.formInline.dataSourceName
)
}
}
})
},
handleSizeChange(pageSize) {
this.paginationConfig.currentPage = 1
this.paginationConfig.pageSize = pageSize
this.onSubmit()
},
handleCurrentChange(currentPage) {
this.paginationConfig.currentPage = currentPage
this.onSubmit()
},
initTree() {
const chartDom = document.getElementById('consanguinity')
this.treeChart = this.$echarts.init(chartDom)
const data = {
name: 'flare',
children: [
{
name: 'data',
children: [
{
name: 'converters',
children: [
{ name: 'Converters', value: 721 },
{ name: 'DelimitedTextConverter', value: 4294 }
]
},
{
name: 'DataUtil',
value: 3322
}
]
},
{
name: 'display',
children: [
{ name: 'DirtySprite', value: 8833 },
{ name: 'LineSprite', value: 1732 },
{ name: 'RectSprite', value: 3623 }
]
},
{
name: 'flex',
children: [{ name: 'FlareVis', value: 4116 }]
},
{
name: 'scale',
children: [
{ name: 'IScaleMap', value: 2105 },
{ name: 'LinearScale', value: 1316 },
{ name: 'LogScale', value: 3151 },
{ name: 'OrdinalScale', value: 3770 },
{ name: 'QuantileScale', value: 2435 },
{ name: 'QuantitativeScale', value: 4839 },
{ name: 'RootScale', value: 1756 },
{ name: 'Scale', value: 4268 },
{ name: 'ScaleType', value: 1821 },
{ name: 'TimeScale', value: 5833 }
]
}
]
}
const option = {
tooltip: {
trigger: 'item',
triggerOn: 'mousemove'
},
series: [
{
type: 'tree',
id: 0,
name: 'tree1',
data: [data],
top: '10%',
left: '8%',
bottom: '22%',
right: '20%',
symbolSize: 0,
edgeShape: 'polyline',
edgeForkPosition: '50%',
initialTreeDepth: 3,
lineStyle: {
width: 2
},
label: {
formatter: function (a, b) {
return [`{a|${a.data.name}a}`].join('\n')
},
rich: {
a: {
color: 'red',
backgroundColor: '#fff',
lineHeight: 10,
borderColor: '#DCDFE6',
borderWidth: '1',
borderRadius: 2,
padding: [5, 10]
}
}
},
leaves: {
label: {
position: 'right',
verticalAlign: 'middle',
align: 'left'
}
},
expandAndCollapse: true,
animationDuration: 550,
animationDurationUpdate: 750
}
]
}
this.treeChart.setOption(option)
}
}
}
</script>
<style lang="less">
.consanguinity {
.active-icon {
color: var(--primary, #3370ff);
}
box-sizing: border-box;
background-color: var(--MainBG, #f5f6f7);
overflow: hidden;
padding: 24px 24px 24px 24px;
height: 100%;
display: flex;
flex-direction: column;
position: relative;
.consanguinity-table {
height: calc(100% - 100px);
width: 100%;
}
.route-title {
font-family: PingFang SC;
font-size: 20px;
font-weight: 500;
line-height: 28px;
text-align: left;
color: var(--TextPrimary, #1f2329);
width: 100%;
margin: 0;
}
.container-wrapper {
width: 100%;
overflow: auto;
background-color: var(--ContentBG, #ffffff);
margin-top: 24px;
padding: 24px;
flex: 1;
.select-icon {
margin-bottom: 16px;
i,
svg {
font-size: 16px;
cursor: pointer;
margin-right: 10px;
}
}
}
}
</style>