mirror of https://github.com/veops/cmdb.git
dashboard ui update
This commit is contained in:
parent
737b29f7d6
commit
47dbe5ba18
|
@ -68,7 +68,8 @@ export default {
|
|||
},
|
||||
|
||||
methods: {
|
||||
visibleChange(open) {
|
||||
visibleChange(open, isInitOne = true) {
|
||||
// isInitOne 初始化exp为空时,ruleList是否默认给一条
|
||||
// const regQ = /(?<=q=).+(?=&)|(?<=q=).+$/g
|
||||
const exp = this.expression.match(new RegExp(this.regQ, 'g'))
|
||||
? this.expression.match(new RegExp(this.regQ, 'g'))[0]
|
||||
|
@ -151,15 +152,20 @@ export default {
|
|||
})
|
||||
this.ruleList = [...expArray]
|
||||
} else if (open) {
|
||||
this.ruleList = [
|
||||
{
|
||||
id: uuidv4(),
|
||||
type: 'and',
|
||||
property: this.canSearchPreferenceAttrList[0].name,
|
||||
exp: 'is',
|
||||
value: null,
|
||||
},
|
||||
]
|
||||
this.ruleList = isInitOne
|
||||
? [
|
||||
{
|
||||
id: uuidv4(),
|
||||
type: 'and',
|
||||
property:
|
||||
this.canSearchPreferenceAttrList && this.canSearchPreferenceAttrList.length
|
||||
? this.canSearchPreferenceAttrList[0].name
|
||||
: undefined,
|
||||
exp: 'is',
|
||||
value: null,
|
||||
},
|
||||
]
|
||||
: []
|
||||
}
|
||||
},
|
||||
handleClear() {
|
||||
|
|
|
@ -77,6 +77,14 @@ export function getCITypeAttributesByTypeIds(params) {
|
|||
})
|
||||
}
|
||||
|
||||
export function getCITypeCommonAttributesByTypeIds(params) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_types/common_attributes`,
|
||||
method: 'get',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除属性
|
||||
* @param attrId
|
||||
|
|
|
@ -61,3 +61,10 @@ export function revokeTypeRelation(first_type_id, second_type_id, rid, data) {
|
|||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function getRecursive_level2children(type_id) {
|
||||
return axios({
|
||||
url: `/v0.1/ci_type_relations/${type_id}/recursive_level2children`,
|
||||
method: 'GET'
|
||||
})
|
||||
}
|
||||
|
|
|
@ -37,3 +37,11 @@ export function batchUpdateCustomDashboard(data) {
|
|||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function postCustomDashboardPreview(data) {
|
||||
return axios({
|
||||
url: '/v0.1/custom_dashboard/preview',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,11 +1,55 @@
|
|||
<template>
|
||||
<div :style="{ width: '100%', height: 'calc(100% - 2.2vw)' }">
|
||||
<div v-if="category === 0" class="cmdb-dashboard-grid-item-chart">
|
||||
<div
|
||||
:id="`cmdb-dashboard-${chartId}-${editable}-${isPreview}`"
|
||||
:style="{ width: '100%', height: 'calc(100% - 2.2vw)' }"
|
||||
>
|
||||
<div
|
||||
v-if="options.chartType === 'count'"
|
||||
:style="{ color: options.fontColor || '#fff' }"
|
||||
class="cmdb-dashboard-grid-item-chart"
|
||||
>
|
||||
<div class="cmdb-dashboard-grid-item-chart-icon" v-if="options.showIcon && ciType">
|
||||
<template v-if="ciType.icon">
|
||||
<img v-if="ciType.icon.split('$$')[2]" :src="`/api/common-setting/v1/file/${ciType.icon.split('$$')[3]}`" />
|
||||
<ops-icon
|
||||
v-else
|
||||
:style="{
|
||||
color: ciType.icon.split('$$')[1],
|
||||
}"
|
||||
:type="ciType.icon.split('$$')[0]"
|
||||
/>
|
||||
</template>
|
||||
<span :style="{ color: '#2f54eb' }" v-else>{{ ciType.name[0].toUpperCase() }}</span>
|
||||
</div>
|
||||
<span :style="{ ...options.fontConfig }">{{ toThousands(data) }}</span>
|
||||
</div>
|
||||
<vxe-table
|
||||
:max-height="tableHeight"
|
||||
:data="tableData"
|
||||
:stripe="!!options.ret"
|
||||
size="mini"
|
||||
class="ops-stripe-table"
|
||||
v-if="options.chartType === 'table'"
|
||||
:span-method="mergeRowMethod"
|
||||
:border="!options.ret"
|
||||
:show-header="!!options.ret"
|
||||
>
|
||||
<template v-if="options.ret">
|
||||
<vxe-column v-for="col in columns" :key="col" :title="col" :field="col"></vxe-column>
|
||||
</template>
|
||||
<template v-else>
|
||||
<vxe-column
|
||||
v-for="(key, index) in Array(keyLength)"
|
||||
:key="`key${index}`"
|
||||
:title="`key${index}`"
|
||||
:field="`key${index}`"
|
||||
></vxe-column>
|
||||
<vxe-column field="value" title="value"></vxe-column>
|
||||
</template>
|
||||
</vxe-table>
|
||||
<div
|
||||
:id="`cmdb-dashboard-${chartId}-${editable}`"
|
||||
v-if="category === 1 || category === 2"
|
||||
v-else-if="category === 1 || category === 2"
|
||||
class="cmdb-dashboard-grid-item-chart"
|
||||
></div>
|
||||
</div>
|
||||
|
@ -15,17 +59,27 @@
|
|||
import * as echarts from 'echarts'
|
||||
import { mixin } from '@/utils/mixin'
|
||||
import { toThousands } from '../../utils/helper'
|
||||
import { category_1_bar_options, category_1_pie_options, category_2_bar_options } from './chartOptions'
|
||||
import {
|
||||
category_1_bar_options,
|
||||
category_1_line_options,
|
||||
category_1_pie_options,
|
||||
category_2_bar_options,
|
||||
category_2_pie_options,
|
||||
} from './chartOptions'
|
||||
export default {
|
||||
name: 'Chart',
|
||||
mixins: [mixin],
|
||||
props: {
|
||||
ci_types: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
chartId: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
data: {
|
||||
type: [Number, Object],
|
||||
type: [Number, Object, Array],
|
||||
default: 0,
|
||||
},
|
||||
category: {
|
||||
|
@ -40,20 +94,65 @@ export default {
|
|||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
type_id: {
|
||||
type: [Number, Array],
|
||||
default: null,
|
||||
},
|
||||
isPreview: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chart: null,
|
||||
columns: [],
|
||||
tableHeight: '',
|
||||
tableData: [],
|
||||
keyLength: 0,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
ciType() {
|
||||
if (this.type_id || this.options?.type_ids) {
|
||||
const _find = this.ci_types.find((item) => item.id === this.type_id || item.id === this.options?.type_ids[0])
|
||||
return _find || null
|
||||
}
|
||||
return null
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
data: {
|
||||
immediate: true,
|
||||
deep: true,
|
||||
handler(newValue, oldValue) {
|
||||
if (this.category === 1 || this.category === 2) {
|
||||
if (Object.prototype.toString.call(newValue) === '[object Object]') {
|
||||
this.setChart()
|
||||
if (this.options.chartType !== 'table' && Object.prototype.toString.call(newValue) === '[object Object]') {
|
||||
if (this.isPreview) {
|
||||
this.$nextTick(() => {
|
||||
this.setChart()
|
||||
})
|
||||
} else {
|
||||
this.setChart()
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.options.chartType === 'table') {
|
||||
this.$nextTick(() => {
|
||||
const dom = document.getElementById(`cmdb-dashboard-${this.chartId}-${this.editable}-${this.isPreview}`)
|
||||
this.tableHeight = dom.offsetHeight
|
||||
})
|
||||
if (this.options.ret) {
|
||||
const excludeKeys = ['_X_ROW_KEY', 'ci_type', 'ci_type_alias', 'unique', 'unique_alias', '_id', '_type']
|
||||
if (newValue && newValue.length) {
|
||||
this.columns = Object.keys(newValue[0]).filter((keys) => !excludeKeys.includes(keys))
|
||||
this.tableData = newValue
|
||||
}
|
||||
} else {
|
||||
const _data = []
|
||||
this.keyLength = this.options?.attr_ids?.length ?? 0
|
||||
this.formatTableData(_data, this.data, {})
|
||||
this.tableData = _data
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -81,13 +180,19 @@ export default {
|
|||
this.chart = echarts.init(document.getElementById(`cmdb-dashboard-${this.chartId}-${this.editable}`))
|
||||
}
|
||||
if (this.category === 1 && this.options.chartType === 'bar') {
|
||||
this.chart.setOption(category_1_bar_options(this.data), true)
|
||||
this.chart.setOption(category_1_bar_options(this.data, this.options), true)
|
||||
}
|
||||
if (this.category === 1 && this.options.chartType === 'line') {
|
||||
this.chart.setOption(category_1_line_options(this.data, this.options), true)
|
||||
}
|
||||
if (this.category === 1 && this.options.chartType === 'pie') {
|
||||
this.chart.setOption(category_1_pie_options(this.data), true)
|
||||
this.chart.setOption(category_1_pie_options(this.data, this.options), true)
|
||||
}
|
||||
if (this.category === 2) {
|
||||
this.chart.setOption(category_2_bar_options(this.data), true)
|
||||
if (this.category === 2 && ['bar', 'line'].includes(this.options.chartType)) {
|
||||
this.chart.setOption(category_2_bar_options(this.data, this.options, this.options.chartType), true)
|
||||
}
|
||||
if (this.category === 2 && this.options.chartType === 'pie') {
|
||||
this.chart.setOption(category_2_pie_options(this.data, this.options), true)
|
||||
}
|
||||
},
|
||||
resizeChart() {
|
||||
|
@ -97,6 +202,34 @@ export default {
|
|||
}
|
||||
})
|
||||
},
|
||||
formatTableData(_data, data, obj) {
|
||||
Object.keys(data).forEach((k) => {
|
||||
if (typeof data[k] === 'number') {
|
||||
_data.push({ ...obj, [`key${Object.keys(obj).length}`]: k, value: data[k] })
|
||||
} else {
|
||||
this.formatTableData(_data, data[k], { ...obj, [`key${Object.keys(obj).length}`]: k })
|
||||
}
|
||||
})
|
||||
},
|
||||
mergeRowMethod({ row, _rowIndex, column, visibleData }) {
|
||||
const fields = ['key0', 'key1', 'key2']
|
||||
const cellValue = row[column.field]
|
||||
if (cellValue && fields.includes(column.field)) {
|
||||
const prevRow = visibleData[_rowIndex - 1]
|
||||
let nextRow = visibleData[_rowIndex + 1]
|
||||
if (prevRow && prevRow[column.field] === cellValue) {
|
||||
return { rowspan: 0, colspan: 0 }
|
||||
} else {
|
||||
let countRowspan = 1
|
||||
while (nextRow && nextRow[column.field] === cellValue) {
|
||||
nextRow = visibleData[++countRowspan + _rowIndex]
|
||||
}
|
||||
if (countRowspan > 1) {
|
||||
return { rowspan: countRowspan, colspan: 1 }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
@ -106,14 +239,28 @@ export default {
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
> span {
|
||||
font-size: 50px;
|
||||
font-weight: 700;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
.cmdb-dashboard-grid-item-chart-icon {
|
||||
> i {
|
||||
font-size: 4vw;
|
||||
}
|
||||
> img {
|
||||
width: 4vw;
|
||||
}
|
||||
> span {
|
||||
display: inline-block;
|
||||
width: 4vw;
|
||||
height: 4vw;
|
||||
font-size: 50px;
|
||||
text-align: center;
|
||||
line-height: 50px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,65 +1,307 @@
|
|||
<template>
|
||||
<a-modal :title="`${type === 'add' ? '新增' : '编辑'}图表`" :visible="visible" @cancel="handleclose" @ok="handleok">
|
||||
<a-form-model ref="chartForm" :model="form" :rules="rules" :label-col="{ span: 6 }" :wrapper-col="{ span: 14 }">
|
||||
<a-form-model-item label="类型" prop="category">
|
||||
<a-select v-model="form.category" @change="changeDashboardCategory">
|
||||
<a-select-option v-for="cate in Object.keys(dashboardCategory)" :key="cate" :value="Number(cate)">{{
|
||||
dashboardCategory[cate].label
|
||||
}}</a-select-option>
|
||||
</a-select>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item v-if="form.category !== 0" label="名称" prop="name">
|
||||
<a-input v-model="form.name" placeholder="请输入图表名称"></a-input>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="模型" prop="type_id">
|
||||
<a-select
|
||||
show-search
|
||||
optionFilterProp="children"
|
||||
@change="changeCIType"
|
||||
v-model="form.type_id"
|
||||
placeholder="请选择模型"
|
||||
>
|
||||
<a-select-option v-for="ci_type in ci_types" :key="ci_type.id" :value="ci_type.id">{{
|
||||
ci_type.alias || ci_type.name
|
||||
}}</a-select-option>
|
||||
</a-select>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item v-if="form.category === 1" label="模型属性" prop="attr_id">
|
||||
<a-select show-search optionFilterProp="children" v-model="form.attr_id" placeholder="请选择模型属性">
|
||||
<a-select-option v-for="attr in attributes" :key="attr.id" :value="attr.id">{{
|
||||
attr.alias || attr.name
|
||||
}}</a-select-option>
|
||||
</a-select>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item v-if="form.category === 1" label="图表类型" prop="chartType">
|
||||
<a-radio-group v-model="chartType">
|
||||
<a-radio value="bar">
|
||||
柱状图
|
||||
</a-radio>
|
||||
<a-radio value="pie">
|
||||
饼图
|
||||
</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item v-if="form.category === 2" label="关系层级" prop="level">
|
||||
<a-input v-model="form.level" placeholder="请输入关系层级"></a-input>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item v-if="form.category === 0" label="字体">
|
||||
<FontConfig ref="fontConfig" />
|
||||
</a-form-model-item>
|
||||
</a-form-model>
|
||||
<a-modal
|
||||
width="1100px"
|
||||
:title="`${type === 'add' ? '新增' : '编辑'}图表`"
|
||||
:visible="visible"
|
||||
@cancel="handleclose"
|
||||
@ok="handleok"
|
||||
:bodyStyle="{ paddingTop: 0 }"
|
||||
>
|
||||
<div class="chart-wrapper">
|
||||
<div class="chart-left">
|
||||
<a-form-model ref="chartForm" :model="form" :rules="rules" :label-col="{ span: 4 }" :wrapper-col="{ span: 18 }">
|
||||
<a-form-model-item label="标题" prop="name">
|
||||
<a-input v-model="form.name" placeholder="请输入图表标题"></a-input>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="类型" prop="category" v-if="chartType !== 'count' && chartType !== 'table'">
|
||||
<a-radio-group
|
||||
@change="
|
||||
() => {
|
||||
resetForm()
|
||||
}
|
||||
"
|
||||
:default-value="1"
|
||||
v-model="form.category"
|
||||
>
|
||||
<a-radio-button :value="Number(key)" :key="key" v-for="key in Object.keys(dashboardCategory)">
|
||||
{{ dashboardCategory[key].label }}
|
||||
</a-radio-button>
|
||||
</a-radio-group>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="类型" prop="tableCategory" v-if="chartType === 'table'">
|
||||
<a-radio-group
|
||||
@change="
|
||||
() => {
|
||||
resetForm()
|
||||
}
|
||||
"
|
||||
:default-value="1"
|
||||
v-model="form.tableCategory"
|
||||
>
|
||||
<a-radio-button :value="1">
|
||||
计算指标
|
||||
</a-radio-button>
|
||||
<a-radio-button :value="2">
|
||||
资源数据
|
||||
</a-radio-button>
|
||||
</a-radio-group>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item
|
||||
v-if="(chartType !== 'table' && form.category !== 2) || (chartType === 'table' && form.tableCategory === 1)"
|
||||
label="模型"
|
||||
prop="type_ids"
|
||||
>
|
||||
<a-select
|
||||
show-search
|
||||
optionFilterProp="children"
|
||||
@change="changeCIType"
|
||||
v-model="form.type_ids"
|
||||
placeholder="请选择模型"
|
||||
mode="multiple"
|
||||
>
|
||||
<a-select-option v-for="ci_type in ci_types" :key="ci_type.id" :value="ci_type.id">{{
|
||||
ci_type.alias || ci_type.name
|
||||
}}</a-select-option>
|
||||
</a-select>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item v-else label="模型" prop="type_id">
|
||||
<a-select
|
||||
show-search
|
||||
optionFilterProp="children"
|
||||
@change="changeCIType"
|
||||
v-model="form.type_id"
|
||||
placeholder="请选择模型"
|
||||
>
|
||||
<a-select-option v-for="ci_type in ci_types" :key="ci_type.id" :value="ci_type.id">{{
|
||||
ci_type.alias || ci_type.name
|
||||
}}</a-select-option>
|
||||
</a-select>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item
|
||||
label="维度"
|
||||
prop="attr_ids"
|
||||
v-if="(['bar', 'line', 'pie'].includes(chartType) && form.category === 1) || chartType === 'table'"
|
||||
>
|
||||
<a-select @change="changeAttr" v-model="form.attr_ids" placeholder="请选择维度" mode="multiple" show-search>
|
||||
<a-select-option v-for="attr in commonAttributes" :key="attr.id" :value="attr.id">{{
|
||||
attr.alias || attr.name
|
||||
}}</a-select-option>
|
||||
</a-select>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item
|
||||
prop="type_ids"
|
||||
label="关系模型"
|
||||
v-if="['bar', 'line', 'pie'].includes(chartType) && form.category === 2"
|
||||
>
|
||||
<a-select
|
||||
show-search
|
||||
optionFilterProp="children"
|
||||
mode="multiple"
|
||||
v-model="form.type_ids"
|
||||
placeholder="请选择模型"
|
||||
>
|
||||
<a-select-opt-group
|
||||
v-for="(key, index) in Object.keys(level2children)"
|
||||
:key="key"
|
||||
:label="`层级${index + 1}`"
|
||||
>
|
||||
<a-select-option
|
||||
@click="(e) => clickLevel2children(e, citype, index + 1)"
|
||||
v-for="citype in level2children[key]"
|
||||
:key="citype.id"
|
||||
:value="citype.id"
|
||||
>
|
||||
{{ citype.alias || citype.name }}
|
||||
</a-select-option>
|
||||
</a-select-opt-group>
|
||||
</a-select>
|
||||
</a-form-model-item>
|
||||
<div class="chart-left-preview">
|
||||
<span class="chart-left-preview-operation" @click="showPreview"><a-icon type="play-circle" /> 预览</span>
|
||||
<template v-if="isShowPreview">
|
||||
<div v-if="chartType !== 'count'" class="cmdb-dashboard-grid-item-title">
|
||||
<template v-if="form.showIcon && ciType">
|
||||
<template v-if="ciType.icon">
|
||||
<img
|
||||
v-if="ciType.icon.split('$$')[2]"
|
||||
:src="`/api/common-setting/v1/file/${ciType.icon.split('$$')[3]}`"
|
||||
/>
|
||||
<ops-icon
|
||||
v-else
|
||||
:style="{
|
||||
color: ciType.icon.split('$$')[1],
|
||||
}"
|
||||
:type="ciType.icon.split('$$')[0]"
|
||||
/>
|
||||
</template>
|
||||
<span :style="{ color: '#2f54eb' }" v-else>{{ ciType.name[0].toUpperCase() }}</span>
|
||||
</template>
|
||||
<span :style="{ color: '#000' }"> {{ form.name }}</span>
|
||||
</div>
|
||||
<div
|
||||
class="chart-left-preview-box"
|
||||
:style="{
|
||||
height: chartType === 'count' ? '120px' : '',
|
||||
marginTop: chartType === 'count' ? '80px' : '',
|
||||
background:
|
||||
chartType === 'count'
|
||||
? Array.isArray(bgColor)
|
||||
? `linear-gradient(to bottom, ${bgColor[0]} 0%, ${bgColor[1]} 100%)`
|
||||
: bgColor
|
||||
: '#fafafa',
|
||||
}"
|
||||
>
|
||||
<div :style="{ color: fontColor }">{{ form.name }}</div>
|
||||
<Chart
|
||||
:ref="`chart_${item.id}`"
|
||||
:chartId="item.id"
|
||||
:data="previewData"
|
||||
:category="form.category"
|
||||
:options="{
|
||||
...item.options,
|
||||
name: form.name,
|
||||
fontColor: fontColor,
|
||||
bgColor: bgColor,
|
||||
chartType: chartType,
|
||||
showIcon: form.showIcon,
|
||||
barDirection: barDirection,
|
||||
barStack: barStack,
|
||||
chartColor: chartColor,
|
||||
type_ids: form.type_ids,
|
||||
attr_ids: form.attr_ids,
|
||||
isShadow: isShadow,
|
||||
}"
|
||||
:editable="false"
|
||||
:ci_types="ci_types"
|
||||
:type_id="form.type_id || form.type_ids"
|
||||
isPreview
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<a-form-model-item label="是否显示icon" prop="showIcon" :label-col="{ span: 5 }" :wrapper-col="{ span: 18 }">
|
||||
<a-switch v-model="form.showIcon"></a-switch>
|
||||
</a-form-model-item>
|
||||
</a-form-model>
|
||||
</div>
|
||||
|
||||
<div class="chart-right">
|
||||
<h4>图表类型</h4>
|
||||
<div class="chart-right-type">
|
||||
<div
|
||||
:class="{ 'chart-right-type-box': true, 'chart-right-type-box-selected': chartType === t.value }"
|
||||
v-for="t in chartTypeList"
|
||||
:key="t.value"
|
||||
@click="changeChartType(t)"
|
||||
>
|
||||
<ops-icon :type="`cmdb-${t.value}`" />
|
||||
<span>{{ t.label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<h4>数据筛选</h4>
|
||||
<FilterComp
|
||||
ref="filterComp"
|
||||
:isDropdown="false"
|
||||
:canSearchPreferenceAttrList="attributes"
|
||||
@setExpFromFilter="setExpFromFilter"
|
||||
:expression="filterExp ? `q=${filterExp}` : ''"
|
||||
/>
|
||||
<h4>格式</h4>
|
||||
<a-form-model :colon="false" :label-col="{ span: 4 }" :wrapper-col="{ span: 20 }">
|
||||
<a-form-model-item label="字体颜色" v-if="chartType === 'count'">
|
||||
<ColorPicker
|
||||
v-model="fontColor"
|
||||
:colorList="[
|
||||
'#1D2129',
|
||||
'#4E5969',
|
||||
'#103C93',
|
||||
'#86909C',
|
||||
'#ffffff',
|
||||
'#C9F2FF',
|
||||
'#FFEAC0',
|
||||
'#D6FFE6',
|
||||
'#F2DEFF',
|
||||
]"
|
||||
/>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="背景颜色" v-if="chartType === 'count'">
|
||||
<ColorPicker
|
||||
v-model="bgColor"
|
||||
:colorList="[
|
||||
['#6ABFFE', '#5375EB'],
|
||||
['#C69EFF', '#A377F9'],
|
||||
['#85EBC9', '#4AB8D8'],
|
||||
['#FEB58B', '#DF6463'],
|
||||
'#ffffff',
|
||||
'#FFFBF0',
|
||||
'#FFF1EC',
|
||||
'#E5FFFE',
|
||||
'#E5E7FF',
|
||||
]"
|
||||
/>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="图表颜色" v-else-if="chartType !== 'table'">
|
||||
<ColorListPicker v-model="chartColor" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="图表长度(%)">
|
||||
<a-radio-group class="chart-width" style="width:100%;" v-model="width">
|
||||
<a-radio-button :value="3">
|
||||
25
|
||||
</a-radio-button>
|
||||
<a-radio-button :value="6">
|
||||
50
|
||||
</a-radio-button>
|
||||
<a-radio-button :value="9">
|
||||
75
|
||||
</a-radio-button>
|
||||
<a-radio-button :value="12">
|
||||
100
|
||||
</a-radio-button>
|
||||
</a-radio-group>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="柱状图类型" v-if="chartType === 'bar'">
|
||||
<a-radio-group v-model="barStack">
|
||||
<a-radio value="total">
|
||||
堆积柱状图
|
||||
</a-radio>
|
||||
<a-radio value="">
|
||||
多系列柱状图
|
||||
</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="方向" v-if="chartType === 'bar'">
|
||||
<a-radio-group v-model="barDirection">
|
||||
<a-radio value="x">
|
||||
X轴
|
||||
</a-radio>
|
||||
<a-radio value="y">
|
||||
y轴
|
||||
</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="下方阴影" v-if="chartType === 'line'">
|
||||
<a-switch v-model="isShadow" />
|
||||
</a-form-model-item>
|
||||
</a-form-model>
|
||||
</div>
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Chart from './chart.vue'
|
||||
import { dashboardCategory } from './constant'
|
||||
import { postCustomDashboard, putCustomDashboard } from '../../api/customDashboard'
|
||||
import { getCITypeAttributesById } from '../../api/CITypeAttr'
|
||||
import { postCustomDashboard, putCustomDashboard, postCustomDashboardPreview } from '../../api/customDashboard'
|
||||
import { getCITypeAttributesByTypeIds, getCITypeCommonAttributesByTypeIds } from '../../api/CITypeAttr'
|
||||
import { getRecursive_level2children } from '../../api/CITypeRelation'
|
||||
import { getLastLayout } from '../../utils/helper'
|
||||
import FontConfig from './fontConfig.vue'
|
||||
import FilterComp from '@/components/CMDBFilterComp'
|
||||
import ColorPicker from './colorPicker.vue'
|
||||
import ColorListPicker from './colorListPicker.vue'
|
||||
|
||||
export default {
|
||||
name: 'ChartForm',
|
||||
components: { FontConfig },
|
||||
components: { Chart, FilterComp, ColorPicker, ColorListPicker },
|
||||
props: {
|
||||
ci_types: {
|
||||
type: Array,
|
||||
|
@ -67,100 +309,226 @@ export default {
|
|||
},
|
||||
},
|
||||
data() {
|
||||
const chartTypeList = [
|
||||
{
|
||||
value: 'count',
|
||||
label: '指标',
|
||||
},
|
||||
{
|
||||
value: 'bar',
|
||||
label: '柱状图',
|
||||
},
|
||||
{
|
||||
value: 'line',
|
||||
label: '折线图',
|
||||
},
|
||||
{
|
||||
value: 'pie',
|
||||
label: '饼状图',
|
||||
},
|
||||
{
|
||||
value: 'table',
|
||||
label: '表格',
|
||||
},
|
||||
]
|
||||
return {
|
||||
dashboardCategory,
|
||||
chartTypeList,
|
||||
visible: false,
|
||||
attributes: [],
|
||||
type: 'add',
|
||||
form: {
|
||||
category: 0,
|
||||
tableCategory: 1,
|
||||
name: undefined,
|
||||
type_id: undefined,
|
||||
attr_id: undefined,
|
||||
type_ids: undefined,
|
||||
attr_ids: undefined,
|
||||
level: undefined,
|
||||
showIcon: false,
|
||||
},
|
||||
rules: {
|
||||
category: [{ required: true, trigger: 'change' }],
|
||||
name: [{ required: true, message: '请输入图表名称' }],
|
||||
type_id: [{ required: true, message: '请选择模型', trigger: 'change' }],
|
||||
attr_id: [{ required: true, message: '请选择模型属性', trigger: 'change' }],
|
||||
type_ids: [{ required: true, message: '请选择模型', trigger: 'change' }],
|
||||
attr_ids: [{ required: true, message: '请选择模型属性', trigger: 'change' }],
|
||||
level: [{ required: true, message: '请输入关系层级' }],
|
||||
showIcon: [{ required: false }],
|
||||
},
|
||||
item: {},
|
||||
chartType: 'bar',
|
||||
chartType: 'count', // table,bar,line,pie,count
|
||||
width: 3,
|
||||
fontColor: '#ffffff',
|
||||
bgColor: ['#6ABFFE', '#5375EB'],
|
||||
chartColor: '#6592FD,#6EE3EB,#44C2FD,#5F59F7,#1A348F,#7D8FCF,#A6D1E5,#8E56DD', // 图表颜色
|
||||
isShowPreview: false,
|
||||
filterExp: undefined,
|
||||
previewData: null,
|
||||
barStack: 'total',
|
||||
barDirection: 'y',
|
||||
commonAttributes: [],
|
||||
level2children: {},
|
||||
isShadow: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
ciType() {
|
||||
if (this.form.type_id || this.form.type_ids) {
|
||||
const _find = this.ci_types.find((item) => item.id === this.form.type_id || item.id === this.form.type_ids[0])
|
||||
return _find || null
|
||||
}
|
||||
return null
|
||||
},
|
||||
},
|
||||
inject: ['layout'],
|
||||
methods: {
|
||||
open(type, item = {}) {
|
||||
async open(type, item = {}) {
|
||||
this.visible = true
|
||||
this.type = type
|
||||
this.item = item
|
||||
const { category = 0, name, type_id, attr_id, level } = item
|
||||
const chartType = (item.options || {}).chartType || 'bar'
|
||||
const chartType = (item.options || {}).chartType || 'count'
|
||||
const fontColor = (item.options || {}).fontColor || '#ffffff'
|
||||
const bgColor = (item.options || {}).bgColor || ['#6ABFFE', '#5375EB']
|
||||
const width = (item.options || {}).w
|
||||
const showIcon = (item.options || {}).showIcon
|
||||
const type_ids = item?.options?.type_ids || []
|
||||
const attr_ids = item?.options?.attr_ids || []
|
||||
const ret = item?.options?.ret || ''
|
||||
this.width = width
|
||||
this.chartType = chartType
|
||||
if (type_id && attr_id) {
|
||||
getCITypeAttributesById(type_id).then((res) => {
|
||||
this.filterExp = item?.options?.filter ?? ''
|
||||
this.chartColor = item?.options?.chartColor ?? '#6592FD,#6EE3EB,#44C2FD,#5F59F7,#1A348F,#7D8FCF,#A6D1E5,#8E56DD'
|
||||
this.isShadow = item?.options?.isShadow ?? false
|
||||
|
||||
if (chartType === 'count') {
|
||||
this.fontColor = fontColor
|
||||
this.bgColor = bgColor
|
||||
}
|
||||
if (type_ids && type_ids.length) {
|
||||
await getCITypeAttributesByTypeIds({ type_ids: type_ids.join(',') }).then((res) => {
|
||||
this.attributes = res.attributes
|
||||
})
|
||||
if ((['bar', 'line', 'pie'].includes(chartType) && category === 1) || chartType === 'table') {
|
||||
this.barDirection = item?.options?.barDirection ?? 'y'
|
||||
this.barStack = item?.options?.barStack ?? 'total'
|
||||
await getCITypeCommonAttributesByTypeIds({
|
||||
type_ids: type_ids.join(','),
|
||||
}).then((res) => {
|
||||
this.commonAttributes = res.attributes
|
||||
})
|
||||
}
|
||||
}
|
||||
if (type_id) {
|
||||
getRecursive_level2children(type_id).then((res) => {
|
||||
this.level2children = res
|
||||
})
|
||||
await getCITypeCommonAttributesByTypeIds({
|
||||
type_ids: type_id,
|
||||
}).then((res) => {
|
||||
this.commonAttributes = res.attributes
|
||||
})
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
this.$refs.filterComp.visibleChange(true, false)
|
||||
})
|
||||
const default_form = {
|
||||
category: 0,
|
||||
name: undefined,
|
||||
type_id: undefined,
|
||||
attr_id: undefined,
|
||||
type_ids: undefined,
|
||||
attr_ids: undefined,
|
||||
level: undefined,
|
||||
showIcon: false,
|
||||
tableCategory: 1,
|
||||
}
|
||||
this.form = {
|
||||
...default_form,
|
||||
category,
|
||||
name,
|
||||
type_id,
|
||||
attr_id,
|
||||
type_ids,
|
||||
attr_ids,
|
||||
level,
|
||||
}
|
||||
if (category === 0) {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.fontConfig.setConfig((item.options || {}).fontConfig)
|
||||
})
|
||||
showIcon,
|
||||
tableCategory: ret === 'cis' ? 2 : 1,
|
||||
}
|
||||
},
|
||||
handleclose() {
|
||||
this.attributes = []
|
||||
this.$refs.chartForm.clearValidate()
|
||||
this.isShowPreview = false
|
||||
this.visible = false
|
||||
},
|
||||
changeCIType(value) {
|
||||
getCITypeAttributesById(value).then((res) => {
|
||||
this.form.attr_ids = []
|
||||
this.commonAttributes = []
|
||||
getCITypeAttributesByTypeIds({ type_ids: Array.isArray(value) ? value.join(',') : value }).then((res) => {
|
||||
this.attributes = res.attributes
|
||||
this.form = {
|
||||
...this.form,
|
||||
attr_id: undefined,
|
||||
}
|
||||
})
|
||||
if (!Array.isArray(value)) {
|
||||
getRecursive_level2children(value).then((res) => {
|
||||
this.level2children = res
|
||||
})
|
||||
}
|
||||
if ((['bar', 'line', 'pie'].includes(this.chartType) && this.form.category === 1) || this.chartType === 'table') {
|
||||
getCITypeCommonAttributesByTypeIds({ type_ids: Array.isArray(value) ? value.join(',') : value }).then((res) => {
|
||||
this.commonAttributes = res.attributes
|
||||
})
|
||||
}
|
||||
},
|
||||
handleok() {
|
||||
this.$refs.chartForm.validate(async (valid) => {
|
||||
if (valid) {
|
||||
const fontConfig = this.form.category === 0 ? this.$refs.fontConfig.getConfig() : undefined
|
||||
const _find = this.ci_types.find((attr) => attr.id === this.form.type_id)
|
||||
const name = this.form.name || (_find || {}).alias || (_find || {}).name
|
||||
const name = this.form.name
|
||||
const { chartType, fontColor, bgColor } = this
|
||||
this.$refs.filterComp.handleSubmit()
|
||||
if (this.item.id) {
|
||||
await putCustomDashboard(this.item.id, {
|
||||
const params = {
|
||||
...this.form,
|
||||
options: {
|
||||
...this.item.options,
|
||||
name,
|
||||
fontConfig,
|
||||
w: this.width,
|
||||
chartType: this.chartType,
|
||||
showIcon: this.form.showIcon,
|
||||
type_ids: this.form.type_ids,
|
||||
filter: this.filterExp,
|
||||
isShadow: this.isShadow,
|
||||
},
|
||||
})
|
||||
}
|
||||
if (chartType === 'count') {
|
||||
params.options.fontColor = fontColor
|
||||
params.options.bgColor = bgColor
|
||||
}
|
||||
if (['bar', 'line', 'pie'].includes(chartType)) {
|
||||
if (this.form.category === 1) {
|
||||
params.options.attr_ids = this.form.attr_ids
|
||||
}
|
||||
params.options.chartColor = this.chartColor
|
||||
}
|
||||
if (chartType === 'bar') {
|
||||
params.options.barDirection = this.barDirection
|
||||
params.options.barStack = this.barStack
|
||||
}
|
||||
if (chartType === 'table') {
|
||||
params.options.attr_ids = this.form.attr_ids
|
||||
if (this.form.tableCategory === 2) {
|
||||
params.options.ret = 'cis'
|
||||
}
|
||||
}
|
||||
delete params.showIcon
|
||||
delete params.type_ids
|
||||
delete params.attr_ids
|
||||
delete params.tableCategory
|
||||
await putCustomDashboard(this.item.id, params)
|
||||
} else {
|
||||
const { xLast, yLast, wLast } = getLastLayout(this.layout())
|
||||
const w = 3
|
||||
const w = this.width
|
||||
const x = xLast + wLast + w > 12 ? 0 : xLast + wLast
|
||||
const y = xLast + wLast + w > 12 ? yLast + 1 : yLast
|
||||
await postCustomDashboard({
|
||||
const params = {
|
||||
...this.form,
|
||||
options: {
|
||||
x,
|
||||
|
@ -169,23 +537,216 @@ export default {
|
|||
h: this.form.category === 0 ? 3 : 5,
|
||||
name,
|
||||
chartType: this.chartType,
|
||||
fontConfig,
|
||||
showIcon: this.form.showIcon,
|
||||
type_ids: this.form.type_ids,
|
||||
filter: this.filterExp,
|
||||
isShadow: this.isShadow,
|
||||
},
|
||||
})
|
||||
}
|
||||
if (chartType === 'count') {
|
||||
params.options.fontColor = fontColor
|
||||
params.options.bgColor = bgColor
|
||||
}
|
||||
if (['bar', 'line', 'pie'].includes(chartType)) {
|
||||
if (this.form.category === 1) {
|
||||
params.options.attr_ids = this.form.attr_ids
|
||||
}
|
||||
params.options.chartColor = this.chartColor
|
||||
}
|
||||
if (chartType === 'bar') {
|
||||
params.options.barDirection = this.barDirection
|
||||
params.options.barStack = this.barStack
|
||||
}
|
||||
if (chartType === 'table') {
|
||||
params.options.attr_ids = this.form.attr_ids
|
||||
if (this.form.tableCategory === 2) {
|
||||
params.options.ret = 'cis'
|
||||
}
|
||||
}
|
||||
delete params.showIcon
|
||||
delete params.type_ids
|
||||
delete params.attr_ids
|
||||
delete params.tableCategory
|
||||
await postCustomDashboard(params)
|
||||
}
|
||||
this.handleclose()
|
||||
this.$emit('refresh')
|
||||
}
|
||||
})
|
||||
},
|
||||
changeDashboardCategory(value) {
|
||||
this.$refs.chartForm.clearValidate()
|
||||
if (value === 1 && this.form.type_id) {
|
||||
this.changeCIType(this.form.type_id)
|
||||
// changeDashboardCategory(value) {
|
||||
// this.$refs.chartForm.clearValidate()
|
||||
// if (value === 1 && this.form.type_id) {
|
||||
// this.changeCIType(this.form.type_id)
|
||||
// }
|
||||
// },
|
||||
changeChartType(t) {
|
||||
this.chartType = t.value
|
||||
this.isShowPreview = false
|
||||
if (t.value === 'count') {
|
||||
this.form.category = 0
|
||||
} else {
|
||||
this.form.category = 1
|
||||
}
|
||||
this.resetForm()
|
||||
},
|
||||
showPreview() {
|
||||
this.$refs.chartForm.validate(async (valid) => {
|
||||
if (valid) {
|
||||
this.isShowPreview = false
|
||||
const name = this.form.name
|
||||
const { chartType, fontColor, bgColor } = this
|
||||
this.$refs.filterComp.handleSubmit()
|
||||
const params = {
|
||||
...this.form,
|
||||
options: {
|
||||
name,
|
||||
chartType,
|
||||
showIcon: this.form.showIcon,
|
||||
type_ids: this.form.type_ids,
|
||||
filter: this.filterExp,
|
||||
isShadow: this.isShadow,
|
||||
},
|
||||
}
|
||||
if (chartType === 'count') {
|
||||
params.options.fontColor = fontColor
|
||||
params.options.bgColor = bgColor
|
||||
}
|
||||
if (['bar', 'line', 'pie'].includes(chartType)) {
|
||||
if (this.form.category === 1) {
|
||||
params.options.attr_ids = this.form.attr_ids
|
||||
}
|
||||
params.options.chartColor = this.chartColor
|
||||
}
|
||||
if (chartType === 'bar') {
|
||||
params.options.barDirection = this.barDirection
|
||||
params.options.barStack = this.barStack
|
||||
}
|
||||
if (chartType === 'table') {
|
||||
params.options.attr_ids = this.form.attr_ids
|
||||
if (this.form.tableCategory === 2) {
|
||||
params.options.ret = 'cis'
|
||||
}
|
||||
}
|
||||
delete params.showIcon
|
||||
delete params.type_ids
|
||||
delete params.attr_ids
|
||||
delete params.tableCategory
|
||||
postCustomDashboardPreview(params).then((res) => {
|
||||
this.isShowPreview = true
|
||||
this.previewData = res.counter
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
setExpFromFilter(filterExp) {
|
||||
if (filterExp) {
|
||||
this.filterExp = `${filterExp}`
|
||||
} else {
|
||||
this.filterExp = undefined
|
||||
}
|
||||
},
|
||||
resetForm() {
|
||||
this.form.type_id = undefined
|
||||
this.form.type_ids = []
|
||||
this.form.attr_ids = []
|
||||
this.$refs.chartForm.clearValidate()
|
||||
},
|
||||
changeAttr(value) {
|
||||
if (value && value.length) {
|
||||
if (['line', 'pie'].includes(this.chartType)) {
|
||||
this.form.attr_ids = [value[value.length - 1]]
|
||||
}
|
||||
if (['bar'].includes(this.chartType) && value.length > 2) {
|
||||
this.form.attr_ids = value.slice(value.length - 2, value.length)
|
||||
}
|
||||
if (['table'].includes(this.chartType) && value.length > 3) {
|
||||
this.form.attr_ids = value.slice(value.length - 3, value.length)
|
||||
}
|
||||
}
|
||||
},
|
||||
clickLevel2children(e, citype, level) {
|
||||
if (this.form.level !== level) {
|
||||
this.$nextTick(() => {
|
||||
this.form.type_ids = [citype.id]
|
||||
})
|
||||
}
|
||||
this.form.level = level
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
<style lang="less" scoped>
|
||||
.chart-wrapper {
|
||||
display: flex;
|
||||
.chart-left {
|
||||
width: 50%;
|
||||
.chart-left-preview {
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 2px;
|
||||
height: 280px;
|
||||
width: 92%;
|
||||
position: relative;
|
||||
padding: 12px;
|
||||
.chart-left-preview-operation {
|
||||
color: #86909c;
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
right: 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.chart-left-preview-box {
|
||||
padding: 6px 12px;
|
||||
height: 250px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.chart-right {
|
||||
width: 50%;
|
||||
h4 {
|
||||
font-weight: 700;
|
||||
color: #000;
|
||||
}
|
||||
.chart-right-type {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
background-color: #f0f5ff;
|
||||
padding: 6px 12px;
|
||||
.chart-right-type-box {
|
||||
cursor: pointer;
|
||||
width: 70px;
|
||||
height: 60px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
> i {
|
||||
font-size: 32px;
|
||||
}
|
||||
> span {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
.chart-right-type-box-selected {
|
||||
background-color: #e5f1ff;
|
||||
}
|
||||
}
|
||||
.chart-width {
|
||||
width: 100%;
|
||||
> label {
|
||||
width: 25%;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style lang="less">
|
||||
.chart-wrapper {
|
||||
.ant-form-item {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,23 +1,61 @@
|
|||
export const colorList = ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc']
|
||||
|
||||
export const category_1_bar_options = (data) => {
|
||||
export const category_1_bar_options = (data, options) => {
|
||||
// 计算一级分类
|
||||
const xData = Object.keys(data)
|
||||
// 计算共有多少二级分类
|
||||
const secondCategory = {}
|
||||
Object.keys(data).forEach(key => {
|
||||
if (Object.prototype.toString.call(data[key]) === '[object Object]') {
|
||||
Object.keys(data[key]).forEach(key1 => {
|
||||
secondCategory[key1] = Array.from({ length: xData.length }).fill(0)
|
||||
})
|
||||
} else {
|
||||
secondCategory['其他'] = Array.from({ length: xData.length }).fill(0)
|
||||
}
|
||||
})
|
||||
Object.keys(secondCategory).forEach(key => {
|
||||
xData.forEach((x, idx) => {
|
||||
if (data[x][key]) {
|
||||
secondCategory[key][idx] = data[x][key]
|
||||
}
|
||||
if (typeof data[x] === 'number') {
|
||||
secondCategory['其他'][idx] = data[x]
|
||||
}
|
||||
})
|
||||
})
|
||||
return {
|
||||
color: options.chartColor.split(','),
|
||||
grid: {
|
||||
top: 15,
|
||||
left: 'left',
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
right: 10,
|
||||
bottom: 20,
|
||||
containLabel: true,
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: Object.keys(data)
|
||||
legend: {
|
||||
data: Object.keys(secondCategory),
|
||||
bottom: 0,
|
||||
type: 'scroll',
|
||||
},
|
||||
yAxis: {
|
||||
xAxis: options.barDirection === 'y' ? {
|
||||
type: 'category',
|
||||
axisTick: { show: false },
|
||||
data: xData
|
||||
}
|
||||
: {
|
||||
type: 'value',
|
||||
splitLine: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
yAxis: options.barDirection === 'y' ? {
|
||||
type: 'value',
|
||||
splitLine: {
|
||||
show: false
|
||||
}
|
||||
} : {
|
||||
type: 'category',
|
||||
axisTick: { show: false },
|
||||
data: xData
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
|
@ -25,34 +63,76 @@ export const category_1_bar_options = (data) => {
|
|||
type: 'shadow'
|
||||
}
|
||||
},
|
||||
series: Object.keys(secondCategory).map(key => {
|
||||
return {
|
||||
name: key,
|
||||
type: 'bar',
|
||||
stack: options?.barStack ?? 'total',
|
||||
barGap: 0,
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
},
|
||||
data: secondCategory[key]
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export const category_1_line_options = (data, options) => {
|
||||
const xData = Object.keys(data)
|
||||
return {
|
||||
color: options.chartColor.split(','),
|
||||
grid: {
|
||||
top: 15,
|
||||
left: 'left',
|
||||
right: 10,
|
||||
bottom: 20,
|
||||
containLabel: true,
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: xData
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: Object.keys(data).map((key, index) => {
|
||||
return {
|
||||
value: data[key],
|
||||
itemStyle: { color: colorList[0] }
|
||||
data: xData.map(item => data[item]),
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
showSymbol: false,
|
||||
areaStyle: options?.isShadow ? {
|
||||
opacity: 0.5,
|
||||
color: {
|
||||
type: 'linear',
|
||||
x: 0,
|
||||
y: 0,
|
||||
x2: 0,
|
||||
y2: 1,
|
||||
colorStops: [{
|
||||
offset: 0, color: options.chartColor.split(',')[0] // 0% 处的颜色
|
||||
}, {
|
||||
offset: 1, color: '#ffffff' // 100% 处的颜色
|
||||
}],
|
||||
global: false // 缺省为 false
|
||||
}
|
||||
}),
|
||||
type: 'bar',
|
||||
label: {
|
||||
show: true,
|
||||
position: 'top',
|
||||
fontSize: 10,
|
||||
formatter(data) {
|
||||
return `${data.value || ''}`
|
||||
}
|
||||
},
|
||||
} : null
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
export const category_1_pie_options = (data) => {
|
||||
export const category_1_pie_options = (data, options) => {
|
||||
return {
|
||||
color: options.chartColor.split(','),
|
||||
grid: {
|
||||
top: 10,
|
||||
left: 'left',
|
||||
right: 0,
|
||||
right: 10,
|
||||
bottom: 0,
|
||||
containLabel: true,
|
||||
},
|
||||
|
@ -89,7 +169,7 @@ export const category_1_pie_options = (data) => {
|
|||
}
|
||||
}
|
||||
|
||||
export const category_2_bar_options = (data) => {
|
||||
export const category_2_bar_options = (data, options, chartType) => {
|
||||
const xAxisData = Object.keys(data.detail)
|
||||
const _legend = []
|
||||
xAxisData.forEach(key => {
|
||||
|
@ -97,10 +177,11 @@ export const category_2_bar_options = (data) => {
|
|||
})
|
||||
const legend = [...new Set(_legend)]
|
||||
return {
|
||||
color: options.chartColor.split(','),
|
||||
grid: {
|
||||
top: 15,
|
||||
left: 'left',
|
||||
right: 0,
|
||||
right: 10,
|
||||
bottom: 20,
|
||||
containLabel: true,
|
||||
},
|
||||
|
@ -116,41 +197,110 @@ export const category_2_bar_options = (data) => {
|
|||
type: 'scroll',
|
||||
data: legend
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
axisTick: { show: false },
|
||||
data: xAxisData
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
xAxis: options.barDirection === 'y' || chartType === 'line' ? {
|
||||
type: 'category',
|
||||
axisTick: { show: false },
|
||||
data: xAxisData
|
||||
}
|
||||
: {
|
||||
type: 'value',
|
||||
splitLine: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
yAxis: options.barDirection === 'y' || chartType === 'line' ? {
|
||||
type: 'value',
|
||||
splitLine: {
|
||||
show: false
|
||||
}
|
||||
],
|
||||
series: legend.map(le => {
|
||||
} : {
|
||||
type: 'category',
|
||||
axisTick: { show: false },
|
||||
data: xAxisData
|
||||
},
|
||||
series: legend.map((le, index) => {
|
||||
return {
|
||||
name: le,
|
||||
type: 'bar',
|
||||
type: chartType,
|
||||
barGap: 0,
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
},
|
||||
stack: chartType === 'line' ? '' : options?.barStack ?? 'total',
|
||||
data: xAxisData.map(x => {
|
||||
return data.detail[x][le] || 0
|
||||
}),
|
||||
smooth: true,
|
||||
showSymbol: false,
|
||||
label: {
|
||||
show: true,
|
||||
position: 'top',
|
||||
fontSize: 10,
|
||||
formatter(data) {
|
||||
return `${data.value || ''}`
|
||||
}
|
||||
show: false,
|
||||
},
|
||||
areaStyle: chartType === 'line' && options?.isShadow ? {
|
||||
opacity: 0.5,
|
||||
color: {
|
||||
type: 'linear',
|
||||
x: 0,
|
||||
y: 0,
|
||||
x2: 0,
|
||||
y2: 1,
|
||||
colorStops: [{
|
||||
offset: 0, color: options.chartColor.split(',')[index % 8] // 0% 处的颜色
|
||||
}, {
|
||||
offset: 1, color: '#ffffff' // 100% 处的颜色
|
||||
}],
|
||||
global: false // 缺省为 false
|
||||
}
|
||||
} : null
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export const category_2_pie_options = (data, options) => {
|
||||
console.log(1111, options)
|
||||
const _legend = []
|
||||
Object.keys(data.detail).forEach(key => {
|
||||
Object.keys(data.detail[key]).forEach(key2 => {
|
||||
_legend.push({ value: data.detail[key][key2], name: `${key}-${key2}` })
|
||||
})
|
||||
})
|
||||
return {
|
||||
color: options.chartColor.split(','),
|
||||
grid: {
|
||||
top: 15,
|
||||
left: 'left',
|
||||
right: 10,
|
||||
bottom: 20,
|
||||
containLabel: true,
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item'
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 'left',
|
||||
type: 'scroll',
|
||||
formatter: function (name) {
|
||||
const _find = _legend.find(item => item.name === name)
|
||||
return `${name}:${_find.value}`
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'pie',
|
||||
radius: '90%',
|
||||
data: _legend,
|
||||
label: {
|
||||
show: false,
|
||||
},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
<template>
|
||||
<a-select v-model="currenColor">
|
||||
<a-select-option v-for="i in list" :value="i" :key="i">
|
||||
<div>
|
||||
<span :style="{ backgroundColor: color }" class="color-box" v-for="color in i.split(',')" :key="color"></span>
|
||||
</div>
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ColorListPicker',
|
||||
model: {
|
||||
prop: 'value',
|
||||
event: 'change',
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
type: [String, Array],
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
list: [
|
||||
'#6592FD,#6EE3EB,#44C2FD,#5F59F7,#1A348F,#7D8FCF,#A6D1E5,#8E56DD',
|
||||
'#C1A9DC,#E2B5CD,#EE8EBC,#8483C3,#4D66BD,#213764,#D9B6E9,#DD88EB',
|
||||
'#6FC4DF,#9FE8CE,#16B4BE,#86E6FB,#1871A3,#E1BF8D,#ED8D8D,#DD88EB',
|
||||
'#F8B751,#FC9054,#FFE380,#DF963F,#AB5200,#EA9387,#FFBB7C,#D27467',
|
||||
],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
currenColor: {
|
||||
get() {
|
||||
return this.value
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('change', val)
|
||||
return val
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.color-box {
|
||||
display: inline-block;
|
||||
width: 40px;
|
||||
height: 10px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,79 @@
|
|||
<template>
|
||||
<div class="color-picker">
|
||||
<div
|
||||
:style="{
|
||||
background: Array.isArray(item) ? `linear-gradient(to bottom, ${item[0]} 0%, ${item[1]} 100%)` : item,
|
||||
}"
|
||||
:class="{ 'color-picker-box': true, 'color-picker-box-selected': isEqual(currenColor, item) }"
|
||||
v-for="item in colorList"
|
||||
:key="Array.isArray(item) ? item.join() : item"
|
||||
@click="changeColor(item)"
|
||||
></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import _ from 'lodash'
|
||||
export default {
|
||||
name: 'ColorPicker',
|
||||
model: {
|
||||
prop: 'value',
|
||||
event: 'change',
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
type: [String, Array],
|
||||
default: null,
|
||||
},
|
||||
colorList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
currenColor: {
|
||||
get() {
|
||||
return this.value
|
||||
},
|
||||
set(val) {
|
||||
this.$emit('change', val)
|
||||
return val
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
isEqual: _.isEqual,
|
||||
changeColor(item) {
|
||||
this.$emit('change', item)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.color-picker {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 10px;
|
||||
.color-picker-box {
|
||||
width: 19px;
|
||||
height: 19px;
|
||||
border: 1px solid #dae2e7;
|
||||
border-radius: 1px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.color-picker-box-selected {
|
||||
position: relative;
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border: 1px solid #43bbff;
|
||||
top: -3px;
|
||||
left: -3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,5 +1,4 @@
|
|||
export const dashboardCategory = {
|
||||
0: { label: 'CI数统计' },
|
||||
1: { label: '按属性值分类统计' },
|
||||
2: { label: '关系统计' }
|
||||
1: { label: '默认' },
|
||||
2: { label: '关系' }
|
||||
}
|
||||
|
|
|
@ -11,8 +11,8 @@
|
|||
<template v-if="layout && layout.length">
|
||||
<div v-if="editable">
|
||||
<a-button
|
||||
:style="{ marginLeft: '10px' }"
|
||||
@click="openChartForm('add', {})"
|
||||
:style="{ marginLeft: '22px', marginTop: '20px' }"
|
||||
@click="openChartForm('add', { options: { w: 3 } })"
|
||||
ghost
|
||||
type="primary"
|
||||
size="small"
|
||||
|
@ -39,11 +39,44 @@
|
|||
:h="item.h"
|
||||
:i="item.i"
|
||||
:key="item.i"
|
||||
:style="{ backgroundColor: '#fafafa' }"
|
||||
:style="{
|
||||
background:
|
||||
item.options.chartType === 'count'
|
||||
? Array.isArray(item.options.bgColor)
|
||||
? `linear-gradient(to bottom, ${item.options.bgColor[0]} 0%, ${item.options.bgColor[1]} 100%)`
|
||||
: item.options.bgColor
|
||||
: '#fafafa',
|
||||
}"
|
||||
>
|
||||
<CardTitle>{{ item.options.name }}</CardTitle>
|
||||
<div class="cmdb-dashboard-grid-item-title">
|
||||
<template v-if="item.options.chartType !== 'count' && item.options.showIcon && getCiType(item)">
|
||||
<template v-if="getCiType(item).icon">
|
||||
<img
|
||||
v-if="getCiType(item).icon.split('$$')[2]"
|
||||
:src="`/api/common-setting/v1/file/${getCiType(item).icon.split('$$')[3]}`"
|
||||
/>
|
||||
<ops-icon
|
||||
v-else
|
||||
:style="{
|
||||
color: getCiType(item).icon.split('$$')[1],
|
||||
}"
|
||||
:type="getCiType(item).icon.split('$$')[0]"
|
||||
/>
|
||||
</template>
|
||||
<span :style="{ color: '#2f54eb' }" v-else>{{ getCiType(item).name[0].toUpperCase() }}</span>
|
||||
</template>
|
||||
<span :style="{ color: item.options.chartType === 'count' ? item.options.fontColor : '#000' }">{{
|
||||
item.options.name
|
||||
}}</span>
|
||||
</div>
|
||||
<a-dropdown v-if="editable">
|
||||
<a class="cmdb-dashboard-grid-item-operation"><a-icon type="menu"></a-icon></a>
|
||||
<a
|
||||
class="cmdb-dashboard-grid-item-operation"
|
||||
:style="{
|
||||
color: item.options.chartType === 'count' ? item.options.fontColor : '',
|
||||
}"
|
||||
><a-icon type="menu"></a-icon
|
||||
></a>
|
||||
<a-menu slot="overlay">
|
||||
<a-menu-item>
|
||||
<a @click="() => openChartForm('edit', item)"><a-icon style="margin-right:5px" type="edit" />编辑</a>
|
||||
|
@ -53,13 +86,13 @@
|
|||
</a-menu-item>
|
||||
</a-menu>
|
||||
</a-dropdown>
|
||||
<a
|
||||
<!-- <a
|
||||
v-if="editable && item.category === 1"
|
||||
class="cmdb-dashboard-grid-item-chart-type"
|
||||
@click="changeChartType(item)"
|
||||
><a-icon
|
||||
:type="item.options.chartType === 'bar' ? 'bar-chart' : 'pie-chart'"
|
||||
/></a>
|
||||
/></a> -->
|
||||
<Chart
|
||||
:ref="`chart_${item.id}`"
|
||||
:chartId="item.id"
|
||||
|
@ -67,18 +100,26 @@
|
|||
:category="item.category"
|
||||
:options="item.options"
|
||||
:editable="editable"
|
||||
:ci_types="ci_types"
|
||||
:type_id="item.type_id"
|
||||
/>
|
||||
</GridItem>
|
||||
</GridLayout>
|
||||
</template>
|
||||
<div v-else class="dashboard-empty">
|
||||
<a-empty :image="emptyImage" description=""></a-empty>
|
||||
<a-button @click="openChartForm('add', {})" v-if="editable" size="small" type="primary" icon="plus">
|
||||
<a-button
|
||||
@click="openChartForm('add', { options: { w: 3 } })"
|
||||
v-if="editable"
|
||||
size="small"
|
||||
type="primary"
|
||||
icon="plus"
|
||||
>
|
||||
定制仪表盘
|
||||
</a-button>
|
||||
<span v-else>管理员暂未定制仪表盘</span>
|
||||
</div>
|
||||
<ChartForm ref="chartForm" @refresh="refresh" :ci_types="ci_types" />
|
||||
<ChartForm ref="chartForm" @refresh="refresh" :ci_types="ci_types" :totalData="totalData" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -127,12 +168,14 @@ export default {
|
|||
},
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getLayout()
|
||||
created() {
|
||||
getCITypes().then((res) => {
|
||||
this.ci_types = res.ci_types
|
||||
})
|
||||
},
|
||||
mounted() {
|
||||
this.getLayout()
|
||||
},
|
||||
methods: {
|
||||
async getLayout() {
|
||||
const res = await getCustomDashboard()
|
||||
|
@ -196,6 +239,13 @@ export default {
|
|||
})
|
||||
}
|
||||
},
|
||||
getCiType(item) {
|
||||
if (item.type_id || item.options?.type_ids) {
|
||||
const _find = this.ci_types.find((type) => type.id === item.type_id || type.id === item.options?.type_ids[0])
|
||||
return _find || null
|
||||
}
|
||||
return null
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
@ -206,15 +256,18 @@ export default {
|
|||
text-align: center;
|
||||
}
|
||||
.cmdb-dashboard-grid-item {
|
||||
border-radius: 15px;
|
||||
border-radius: 8px;
|
||||
padding: 6px 12px;
|
||||
.cmdb-dashboard-grid-item-title {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-weight: 700;
|
||||
padding-left: 6px;
|
||||
color: #000000bd;
|
||||
color: #000000;
|
||||
}
|
||||
.cmdb-dashboard-grid-item-operation {
|
||||
position: absolute;
|
||||
right: 6px;
|
||||
right: 12px;
|
||||
top: 6px;
|
||||
}
|
||||
.cmdb-dashboard-grid-item-chart-type {
|
||||
|
@ -224,3 +277,26 @@ export default {
|
|||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="less">
|
||||
.cmdb-dashboard-grid-item-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
> i {
|
||||
font-size: 16px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
> img {
|
||||
width: 16px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
> span:not(:last-child) {
|
||||
display: inline-block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
Loading…
Reference in New Issue