feat: 首页根据设计图修改
|
@ -16,6 +16,15 @@ export default [
|
||||||
},
|
},
|
||||||
component: () => import('views/configs/setting_dashboard/index.vue')
|
component: () => import('views/configs/setting_dashboard/index.vue')
|
||||||
},
|
},
|
||||||
|
// 旧主页
|
||||||
|
{
|
||||||
|
path: '/Oldresource_dashboard',
|
||||||
|
meta: {
|
||||||
|
title: '资源概览',
|
||||||
|
noTag: true
|
||||||
|
},
|
||||||
|
component: () => import('views/configs/setting_dashboard/indexOld.vue')
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'ProfileMessage',
|
name: 'ProfileMessage',
|
||||||
path: '/message',
|
path: '/message',
|
||||||
|
|
|
@ -0,0 +1,329 @@
|
||||||
|
import { request } from '@cmp/cmp-element'
|
||||||
|
import { wrapperParams } from 'utils'
|
||||||
|
|
||||||
|
// 指标列表
|
||||||
|
export function getMetrics(params) {
|
||||||
|
return request.get('/cms/v1/metrics', {
|
||||||
|
params: wrapperParams(params)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 设置监控IP
|
||||||
|
export function setIps(params) {
|
||||||
|
return request.post(`/cms/v1/vms/${params.id}/ips`, wrapperParams(params))
|
||||||
|
}
|
||||||
|
export function geIps(id) {
|
||||||
|
return request.get(`/cms/v1/vms/${id}/ips`)
|
||||||
|
}
|
||||||
|
// 分发策略
|
||||||
|
// 列表
|
||||||
|
export function getDistributions(params) {
|
||||||
|
return request.get('/cms/v1/distributions', {
|
||||||
|
params: params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 新增
|
||||||
|
export function createDistri(params) {
|
||||||
|
return request.post('/cms/v1/distributions', wrapperParams(params))
|
||||||
|
}
|
||||||
|
// 修改
|
||||||
|
export function modifyDistri(params) {
|
||||||
|
return request.put(`/cms/v1/distributions/${params.id}`, wrapperParams(params))
|
||||||
|
}
|
||||||
|
// 删除
|
||||||
|
export function removeDistri(id) {
|
||||||
|
return request.delete(`/cms/v1/distributions/${id}`)
|
||||||
|
}
|
||||||
|
// 详情
|
||||||
|
export function getDistriDetail(id) {
|
||||||
|
return request.get(`/cms/v1/distributions/${id}`)
|
||||||
|
}
|
||||||
|
// 告警模板
|
||||||
|
const tempUrl = '/cms/v1/templates'
|
||||||
|
// 列表
|
||||||
|
export function getTempList(params) {
|
||||||
|
return request.get(tempUrl, {
|
||||||
|
params: params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 新增
|
||||||
|
export function createTemp(params) {
|
||||||
|
return request.post(tempUrl, wrapperParams(params))
|
||||||
|
}
|
||||||
|
// 修改
|
||||||
|
export function modifyTemp(params) {
|
||||||
|
return request.put(`${tempUrl}/${params.id}`, wrapperParams(params))
|
||||||
|
}
|
||||||
|
// 删除
|
||||||
|
export function removeTemp(id) {
|
||||||
|
return request.delete(`${tempUrl}/${id}`)
|
||||||
|
}
|
||||||
|
// 批量删除
|
||||||
|
export function batchRemoveTemp(params) {
|
||||||
|
return request.delete(tempUrl, {
|
||||||
|
data: params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 详情
|
||||||
|
export function getTempDetail(id) {
|
||||||
|
return request.get(`${tempUrl}/${id}`)
|
||||||
|
}
|
||||||
|
// 告警列表
|
||||||
|
export function getAlarmList(params) {
|
||||||
|
return request.get('/cms/v1/alarms', {
|
||||||
|
params: params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function getAlarmDetail(id) {
|
||||||
|
return request.get(`/cms/v1/alarms/${id}`)
|
||||||
|
}
|
||||||
|
// 告警确认
|
||||||
|
export function alarmConfirm(params) {
|
||||||
|
return request.patch('/cms/v1/alarms', {
|
||||||
|
action: 'confirm',
|
||||||
|
...wrapperParams(params)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 告警解决
|
||||||
|
export function alarmSolve(params) {
|
||||||
|
return request.patch('/cms/v1/alarms', {
|
||||||
|
action: 'solve',
|
||||||
|
...wrapperParams(params)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function getAlarmChart(params) {
|
||||||
|
return request.get('/cms/v1/alarms/chart', {
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// vcenter主机资源概览
|
||||||
|
export function getVcHostOverview(id) {
|
||||||
|
return request.get(`/cms/v1/hosts/${id}`, {
|
||||||
|
params: wrapperParams({ type: 'VMWARE' })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// vcenter云主机资源概览
|
||||||
|
export function getVmOverview(id) {
|
||||||
|
return request.get(`/cms/v1/vms/${id}`)
|
||||||
|
}
|
||||||
|
// 云主机资源概览仪表盘
|
||||||
|
export function getHostDashboard(params) {
|
||||||
|
return request.get('/cms/v1/prometheus', {
|
||||||
|
params: wrapperParams(params)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 主机云主机图表
|
||||||
|
export function getCharts(params) {
|
||||||
|
return request.get('/cms/v1/charts', {
|
||||||
|
params: wrapperParams(params)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// openstack主机详情
|
||||||
|
export function getOpenstackHost(id) {
|
||||||
|
return request.get(`/cms/v1/hosts/${id}`, {
|
||||||
|
params: wrapperParams({ type: 'OPENSTACK' })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 主机CPU
|
||||||
|
export function getHostCpu(id) {
|
||||||
|
return request.get(`/cms/v1/hosts/${id}/metrics`, {
|
||||||
|
params: wrapperParams({ type: 'cpu' })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function getHostMem(id) {
|
||||||
|
return request.get(`/cms/v1/hosts/${id}/metrics`, {
|
||||||
|
params: wrapperParams({ type: 'mem' })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function getHostDisk(id) {
|
||||||
|
return request.get(`/cms/v1/hosts/${id}/metrics`, {
|
||||||
|
params: wrapperParams({ type: 'disk' })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 告警策略主机列表
|
||||||
|
export function getPolicyHosts(params) {
|
||||||
|
return request.get('/cms/v1/hosts', {
|
||||||
|
params: params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 云主机列表
|
||||||
|
export function getVms(params) {
|
||||||
|
return request.get('/cms/v1/vms', {
|
||||||
|
params: params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDataStore(params) {
|
||||||
|
return request.get('/cms/v1/datastores', {
|
||||||
|
params: params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 开启监控
|
||||||
|
export function openMonitor(params) {
|
||||||
|
return request.patch('/cms/v1/vendors', {
|
||||||
|
action: 'open',
|
||||||
|
...wrapperParams(params)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 关闭监控
|
||||||
|
export function closeMonitor(params) {
|
||||||
|
return request.patch('/cms/v1/vendors', {
|
||||||
|
action: 'close',
|
||||||
|
...wrapperParams(params)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function getRatio(params) {
|
||||||
|
return request.get('/cms/v1/vendors/ratio', {
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function ratioOk(params) {
|
||||||
|
return request.post('/cms/v1/vendors/ratio', wrapperParams(params))
|
||||||
|
}
|
||||||
|
// hmc主机分区列表
|
||||||
|
export function getServers(params) {
|
||||||
|
return request.get('/cms/v1/hmc/servers', {
|
||||||
|
params: params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function getPartitions(params) {
|
||||||
|
return request.get('/cms/v1/hmc/partitions', {
|
||||||
|
params: params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 运维认证
|
||||||
|
export function getAuthentications(vendorId) {
|
||||||
|
return request.get(`/cms/v1/vendors/${vendorId}/authentications`)
|
||||||
|
}
|
||||||
|
export function authenticationsOk(params) {
|
||||||
|
return request.post(`/cms/v1/vendors/${params.vendorId}/authentications`, wrapperParams(params))
|
||||||
|
}
|
||||||
|
export function getFusionHost(params) {
|
||||||
|
return request.get('/cms/v1/fusioncloud/hosts', {
|
||||||
|
params: params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function getFusionHostDetail(id, type) {
|
||||||
|
return request.get(`/cmp/plugins/${type}/v1/hosts/${id}`)
|
||||||
|
}
|
||||||
|
// 安装agent
|
||||||
|
export function installTaskExporter(params) {
|
||||||
|
return request.post('/cms/v1/agent', wrapperParams(params))
|
||||||
|
}
|
||||||
|
export function getOpenstackVm(type, id) {
|
||||||
|
return request.get(`/cmp/plugins/${type}/v1/vms/${id}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getSecurityGroup(type, params) {
|
||||||
|
return request.get(`/cmp/plugins/${type}/v1/vms/${params.id}/sgroups`, {
|
||||||
|
params: params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function getUsage(params) {
|
||||||
|
return request.get('/cms/v1/prometheus', {
|
||||||
|
params: wrapperParams(params)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function getPoolDatas(params) {
|
||||||
|
return request.get('/cms/v1/prometheus/filter', {
|
||||||
|
params: wrapperParams(params)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getServices(params) {
|
||||||
|
return request.get('/cms/v1/services', {
|
||||||
|
params: params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 资源利用率TOP5
|
||||||
|
export function getResTops(params) {
|
||||||
|
return request.get('/cms/v1/tops', {
|
||||||
|
params: wrapperParams(params)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPMI设置
|
||||||
|
export function getHosts(id) {
|
||||||
|
return request.get(`/cms/v1/hosts/ipmi/${id}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function patchHosts(url, params) {
|
||||||
|
return request.patch(url, { ...wrapperParams(params) })
|
||||||
|
}
|
||||||
|
// 数据源配置
|
||||||
|
export function configDataSource(vendorId, params) {
|
||||||
|
return request.post(`/cms/v1/vendors/${vendorId}/monitor`, wrapperParams(params))
|
||||||
|
}
|
||||||
|
export function getfilters(params) {
|
||||||
|
return request.get('/cms/v1/prometheus/filter', {
|
||||||
|
params: wrapperParams(params)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 告警策略
|
||||||
|
export function getRuleGroup(params) {
|
||||||
|
return request.get('/cms/v1/rulegroups', {
|
||||||
|
params: wrapperParams(params)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function createRuleGroup(params) {
|
||||||
|
return request.post('/cms/v1/rulegroups', wrapperParams(params))
|
||||||
|
}
|
||||||
|
export function modifyRuleGroup(params) {
|
||||||
|
return request.put(`/cms/v1/rulegroups/${params.id}`, wrapperParams(params))
|
||||||
|
}
|
||||||
|
export function removeRuleGroup(id) {
|
||||||
|
return request.delete(`/cms/v1/rulegroups/${id}`)
|
||||||
|
}
|
||||||
|
export function batchRemoveRuleGroup(params) {
|
||||||
|
return request.delete('/cms/v1/rulegroups', {
|
||||||
|
data: wrapperParams(params)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function getRuleGroupDetail(id) {
|
||||||
|
return request.get(`/cms/v1/rulegroups/${id}`)
|
||||||
|
}
|
||||||
|
export function ruleGroupEnable(params) {
|
||||||
|
return request.patch('/cms/v1/rulegroups/enable', { ...wrapperParams(params) })
|
||||||
|
}
|
||||||
|
export function rulegroupsBinding(params) {
|
||||||
|
return request.patch('/cms/v1/rulegroups/binding', { ...wrapperParams(params) })
|
||||||
|
}
|
||||||
|
export function rulegroupsUnBinding(params) {
|
||||||
|
return request.delete('/cms/v1/rulegroups/binding', {
|
||||||
|
data: wrapperParams(params)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function getRuleGroupBind(id) {
|
||||||
|
return request.get(`/cms/v1/rulegroups/${id}/resources`)
|
||||||
|
}
|
||||||
|
export function modifyAlarmStatus(params) {
|
||||||
|
return request.post('/cms/v1/alarmstatus', wrapperParams(params))
|
||||||
|
}
|
||||||
|
export function deleteAlarmStatus(params) {
|
||||||
|
return request.delete('/cms/v1/alarmstatus', {
|
||||||
|
data: wrapperParams(params)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function getVolumeByType(type1, params, type, projectName) {
|
||||||
|
return request.get(`/cmp/plugins/${type1}/v1/vendors/type/${type}/${projectName}/volumes`, {
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// smart主机列表
|
||||||
|
export function getSmartHosts(params) {
|
||||||
|
return request.get('/cms/v1/smartx/hosts', {
|
||||||
|
params: params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 华为云
|
||||||
|
export function getHuaweiResources(type, params) {
|
||||||
|
return request.get(`/cms/v1/hcso/${type}`, {
|
||||||
|
params: params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
export function getStretch(vendorId) {
|
||||||
|
return request.get(`/cms/v1/hcso/as/${vendorId}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMysqlRds(params) {
|
||||||
|
return request.get('/cms/v1/apsarastack/rds/mysql', { params })
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { request } from '@cmp/cmp-element'
|
||||||
|
|
||||||
|
const newsUrl = '/scms/v1/memorabilia'
|
||||||
|
|
||||||
|
export function mockHttp(params) {
|
||||||
|
return request.get(newsUrl, { params })
|
||||||
|
}
|
|
@ -1,91 +1,45 @@
|
||||||
<template>
|
<template>
|
||||||
<el-container class="setting-wrapper" :class="{ full: !isSetting }">
|
<el-container class="setting-wrapper">
|
||||||
<el-header class="setting-header" v-if="isSetting">
|
|
||||||
<span>自定义设置</span>
|
|
||||||
<div>
|
|
||||||
<cb-link @click="reset()">恢复默认设置</cb-link>
|
|
||||||
<!-- <el-button>取消</el-button> -->
|
|
||||||
<el-button type="primary" @click="savePanels" :loading="loading">保存</el-button>
|
|
||||||
</div>
|
|
||||||
</el-header>
|
|
||||||
<el-container class="setting-container">
|
<el-container class="setting-container">
|
||||||
<el-aside width="200px" class="setting-aside" v-if="isSetting">
|
|
||||||
<div class="aside-tool">
|
|
||||||
<span>隐藏已添加模块</span>
|
|
||||||
<el-switch class="pull-right" v-model="hideSelectedModule"></el-switch>
|
|
||||||
</div>
|
|
||||||
<el-scrollbar class="pool-scroll">
|
|
||||||
<draggable :list="poolList" v-bind="{ sort: false, group: { name: 'field', pull: 'clone', put: false } }" @end="drop">
|
|
||||||
<div class="pool-item" :class="forbidPoolCodes.includes(item.code) && 'forbid'" v-for="item in poolList" :key="item.id" :index="item.id" v-show="!(hideSelectedModule && forbidPoolCodes.includes(item.code))">
|
|
||||||
<span>{{ item.name }}</span>
|
|
||||||
<i class="icon" :class="forbidPoolCodes.includes(item.code) ? 'el-icon-success' : 'el-icon-plus'"></i>
|
|
||||||
</div>
|
|
||||||
</draggable>
|
|
||||||
</el-scrollbar>
|
|
||||||
</el-aside>
|
|
||||||
<el-main class="setting-main" id="setting-container">
|
<el-main class="setting-main" id="setting-container">
|
||||||
<el-scrollbar style="height: 100%">
|
<el-scrollbar style="height: 100%">
|
||||||
<div v-if="!isSetting" class="top-header">
|
<el-row :gutter="20">
|
||||||
<span>您好,{{ userData.name }} 欢迎您!</span>
|
<el-col :span="16">
|
||||||
<el-button class="pull-right" type="text" @click="goPage('/config/dashboard-setting')">首页设置</el-button>
|
<!-- 待办工单 -->
|
||||||
</div>
|
<TaskList class="m-b-md" height="918px" />
|
||||||
<grid-layout v-if="layoutData && layoutData.length" :layout="layoutData" :col-num="12" :row-height="16" :is-draggable="isSetting" :is-resizable="isSetting" :vertical-compact="true" :margin="[16, 16]" :use-css-transforms="true">
|
<!-- 平台容量统计 -->
|
||||||
<grid-item class="grid-item" v-for="(item, key) in layoutData" :key="item.i" :x="item.x" :y="item.y" :w="item.w" :h="item.h" :i="item.i" @resize="resizeEvent" @resized="resizeEvent">
|
<PlatformCapacity class="m-b-md" height="242px" />
|
||||||
<div class="view-card">
|
<!-- 告警处理 -->
|
||||||
<div class="card-title">
|
<AlarmHandling class="m-b-md" height="412px" />
|
||||||
<span>{{ item.config.title }}</span>
|
</el-col>
|
||||||
<div class="card-operate" v-if="isSetting">
|
<el-col :span="8">
|
||||||
<el-dropdown>
|
<UserInfo class="m-b-md" />
|
||||||
<span class="operate-icon"><i class="el-icon-setting"></i></span>
|
<!-- 数据展示 -->
|
||||||
<el-dropdown-menu slot="dropdown">
|
<StatisticsDisplay class="m-b-md" height="314px" />
|
||||||
<el-dropdown-item @click.native="handleEdit(item)">编辑</el-dropdown-item>
|
<!-- 工单统计 -->
|
||||||
<el-dropdown-item @click.native="handleDelete(key)">删除</el-dropdown-item>
|
<OrderStatistics class="m-b-md" height="226px" />
|
||||||
</el-dropdown-menu>
|
<!-- 公告 -->
|
||||||
</el-dropdown>
|
<NoticeList class="m-b-md" height="378px" />
|
||||||
</div>
|
<!-- 单日告警统计 -->
|
||||||
</div>
|
<DailyAlarmStatistics class="m-b-md" height="412px" />
|
||||||
<div class="card-body" v-if="item.ready">
|
</el-col>
|
||||||
<cg-loop-charts
|
</el-row>
|
||||||
v-if="item.config.type == 'LoopCharts'"
|
<el-row :gutter="20">
|
||||||
:setting="getChartConfig(item)"
|
<el-col :span="12">
|
||||||
ref="chartRef"
|
<!-- 应用 CPUTop5 -->
|
||||||
:id="item.i"
|
<CPUTop5 class="m-b-md" height="320px" />
|
||||||
:data="item.data"
|
</el-col>
|
||||||
width="100%"
|
<el-col :span="12">
|
||||||
height="100%"
|
<!-- 虚拟机 CPUTop5 -->
|
||||||
:theme="item.config.title"
|
<VMCPUTop5 class="m-b-md" height="320px" />
|
||||||
:unit="item.config.unit"
|
</el-col>
|
||||||
:type="['TodayAlarmCount'].includes(item.config.code) ? 'half' : ''"
|
</el-row>
|
||||||
></cg-loop-charts>
|
<el-row>
|
||||||
<cg-line-charts v-else-if="item.config.type == 'LineCharts'" ref="chartRef" :id="item.i" :data="item.data" width="100%" height="100%" :unit="item.config.unit" :setting="getChartConfig(item)"></cg-line-charts>
|
<el-col>
|
||||||
<cg-bar-reverse-charts v-else-if="item.config.type == 'BarReverseCharts'" ref="chartRef" :id="item.i" :data="item.data" width="100%" height="100%" :unit="item.config.unit" :setting="topSetting"></cg-bar-reverse-charts>
|
<!-- 告警列表 -->
|
||||||
<component v-else :is="getComponent(item.config)" :item-data="item" :is-setting="isSetting"></component>
|
<WarningList />
|
||||||
</div>
|
</el-col>
|
||||||
</div>
|
</el-row>
|
||||||
</grid-item>
|
|
||||||
</grid-layout>
|
|
||||||
<el-dialog title="卡片配置" :close-on-click-modal="false" :visible.sync="dialogVisible" width="800px">
|
|
||||||
<el-form label-width="90px">
|
|
||||||
<template v-for="item in configData">
|
|
||||||
<el-form-item :label="item.name + ':'" v-if="item.type === 'TEXT'" :key="item.name">
|
|
||||||
<el-input v-model="item.defaultValue"></el-input>
|
|
||||||
</el-form-item>
|
|
||||||
<select-vendor :itemData="item" :config-data="configData" v-else-if="item.type === 'SELECTVENDOR'" :key="item.name + 1"> </select-vendor>
|
|
||||||
</template>
|
|
||||||
</el-form>
|
|
||||||
<template #footer>
|
|
||||||
<div class="dialog-footer">
|
|
||||||
<el-button type="ghost" @click.native="dialogVisible = false">取消</el-button>
|
|
||||||
<el-button type="primary" @click.native="settingPanel">确定</el-button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-dialog>
|
|
||||||
<cb-empty class="empty" v-if="layoutData && layoutData.length === 0">
|
|
||||||
<div class="text-center">
|
|
||||||
<div class="m-b-xs">暂无数据</div>
|
|
||||||
<div>请按照所纳管的平台进入<router-link class="detail-href" :to="{ name: 'SettingDashboard' }">管理中心→系统配置→首页配置</router-link>中配置相关数据</div>
|
|
||||||
</div>
|
|
||||||
</cb-empty>
|
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
</el-main>
|
</el-main>
|
||||||
</el-container>
|
</el-container>
|
||||||
|
@ -93,350 +47,36 @@
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { computed, nextTick, ref, unref, watch } from 'vue'
|
import { computed, nextTick, ref, unref, watch } from 'vue'
|
||||||
import VueGridLayout from 'vue-grid-layout'
|
|
||||||
import draggable from 'vuedraggable'
|
|
||||||
import CommonOperation from './CommonOperation.vue'
|
|
||||||
import AccessControl from './AccessControl.vue'
|
|
||||||
import DataCenterOverview from './DataCenterOverview.vue'
|
|
||||||
import CountCard from './CountCard.vue'
|
|
||||||
import DataView from './DataView.vue'
|
|
||||||
import ResUsed from './ResUsed.vue'
|
|
||||||
import AlarmCount from './AlarmCount.vue'
|
|
||||||
import TaskHistory from './TaskHistory.vue'
|
|
||||||
import SelectVendor from './SelectVendor.vue'
|
|
||||||
import { getPanel, getPool, savePanel, getConfig, resetPanel } from 'services/system/portal'
|
import { getPanel, getPool, savePanel, getConfig, resetPanel } from 'services/system/portal'
|
||||||
import { request } from '@cmp/cmp-element'
|
|
||||||
import { wrapperParams } from 'utils'
|
|
||||||
import { Message, MessageBox } from 'element-ui'
|
import { Message, MessageBox } from 'element-ui'
|
||||||
import { getCardData } from './utils'
|
|
||||||
import { topSetting } from './data'
|
|
||||||
import { useRouter, useRoute, useStore, useInstance } from '@cmp/cmp-core'
|
import { useRouter, useRoute, useStore, useInstance } from '@cmp/cmp-core'
|
||||||
const GridLayout = VueGridLayout.GridLayout
|
import UserInfo from './new_dashboard_component/UserInfo.vue'
|
||||||
const GridItem = VueGridLayout.GridItem
|
import TaskList from './new_dashboard_component/TaskList.vue'
|
||||||
const colorMap = ['#1890FF', '#F84540', '#18BE6A', '#696BD8', '#FE9900', '#01b3eb']
|
import StatisticsDisplay from './new_dashboard_component/StatisticsDisplay.vue'
|
||||||
const color = ['#E03B3B', '#F09C2B', '#049BD3', '#1E54DE']
|
import OrderStatistics from './new_dashboard_component/OrderStatistics.vue'
|
||||||
|
import PlatformCapacity from './new_dashboard_component/PlatformCapacity.vue'
|
||||||
|
import NoticeList from './new_dashboard_component/NoticeList.vue'
|
||||||
|
import AlarmHandling from './new_dashboard_component/AlarmHandling.vue'
|
||||||
|
import DailyAlarmStatistics from './new_dashboard_component/DailyAlarmStatistics.vue'
|
||||||
|
import CPUTop5 from './new_dashboard_component/CPUTop5.vue'
|
||||||
|
import VMCPUTop5 from './new_dashboard_component/VMCPUTop5.vue'
|
||||||
|
import WarningList from './new_dashboard_component/WarningList.vue'
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
GridLayout,
|
UserInfo,
|
||||||
GridItem,
|
TaskList,
|
||||||
draggable,
|
StatisticsDisplay,
|
||||||
CommonOperation,
|
OrderStatistics,
|
||||||
AccessControl,
|
PlatformCapacity,
|
||||||
DataCenterOverview,
|
NoticeList,
|
||||||
CountCard,
|
AlarmHandling,
|
||||||
DataView,
|
DailyAlarmStatistics,
|
||||||
ResUsed,
|
CPUTop5,
|
||||||
AlarmCount,
|
VMCPUTop5,
|
||||||
TaskHistory,
|
WarningList
|
||||||
SelectVendor
|
|
||||||
},
|
},
|
||||||
setup(props, context) {
|
setup(props, context) {
|
||||||
const loading = ref(false)
|
return {}
|
||||||
// 是否可配置
|
|
||||||
const isSetting = ref(true)
|
|
||||||
const route = useRoute()
|
|
||||||
const ins = useInstance()
|
|
||||||
function init() {
|
|
||||||
getPanelList()
|
|
||||||
if (ins.$route.path === '/resource_dashboard') {
|
|
||||||
// 展示界面
|
|
||||||
isSetting.value = false
|
|
||||||
} else {
|
|
||||||
getPoolList()
|
|
||||||
isSetting.value = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
init()
|
|
||||||
watch(
|
|
||||||
() => ins.$route.path,
|
|
||||||
() => {
|
|
||||||
init()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
// 卡片池处理
|
|
||||||
const poolList = ref([])
|
|
||||||
const hideSelectedModule = ref(false)
|
|
||||||
const poolMap = computed(() => {
|
|
||||||
const map = {}
|
|
||||||
unref(poolList).forEach(item => {
|
|
||||||
map[item.id] = item
|
|
||||||
})
|
|
||||||
return map
|
|
||||||
})
|
|
||||||
async function getPoolList() {
|
|
||||||
const res = await getPool({ module: 'COS' })
|
|
||||||
if (res.success) {
|
|
||||||
poolList.value = res.data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 获取内容面板
|
|
||||||
const layoutData = ref([])
|
|
||||||
const forbidPoolCodes = computed(() => {
|
|
||||||
return unref(layoutData)
|
|
||||||
.map(item => item.config.code)
|
|
||||||
.filter(item => {
|
|
||||||
return !['PM', 'VM', 'DATAVIEW'].includes(item)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
// 获取组件数据
|
|
||||||
function getData(url, params, item) {
|
|
||||||
request
|
|
||||||
.get(url, {
|
|
||||||
params: wrapperParams(params)
|
|
||||||
})
|
|
||||||
.then(data => {
|
|
||||||
if (data.success) {
|
|
||||||
item.data = data.data
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
function handleListData(item) {
|
|
||||||
const config = (item.config = JSON.parse(item.config))
|
|
||||||
const location = item.location.split(',')
|
|
||||||
item.x = Number(location[0])
|
|
||||||
item.y = Number(location[1])
|
|
||||||
item.w = Number(location[2])
|
|
||||||
item.h = Number(location[3])
|
|
||||||
item.i = item.id.toString()
|
|
||||||
item.data = {}
|
|
||||||
item.ready = false
|
|
||||||
setTimeout(() => {
|
|
||||||
item.ready = true
|
|
||||||
})
|
|
||||||
getCardData(item)
|
|
||||||
config.url && getData(config.url, config.params, item)
|
|
||||||
return item
|
|
||||||
}
|
|
||||||
async function getPanelList() {
|
|
||||||
loading.value = true
|
|
||||||
const data = await getPanel({ module: 'COS' })
|
|
||||||
loading.value = false
|
|
||||||
if (data.success) {
|
|
||||||
const result = []
|
|
||||||
data.data.forEach(item => {
|
|
||||||
result.push(handleListData(item))
|
|
||||||
})
|
|
||||||
layoutData.value = result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 新建panel
|
|
||||||
function drop(e) {
|
|
||||||
const id = e.item.getAttribute('index')
|
|
||||||
const poolItem = poolMap.value[id]
|
|
||||||
if (forbidPoolCodes.value.includes(poolItem.code)) return
|
|
||||||
// 处理生成坐标
|
|
||||||
const colWidth = document.getElementById('setting-container').offsetWidth / 12
|
|
||||||
let x = Math.round((e.originalEvent.pageX - 300) / colWidth)
|
|
||||||
let y = Math.round((e.originalEvent.pageY - 92) / 80)
|
|
||||||
unref(layoutData).forEach(item => {
|
|
||||||
const xMax = item.x + item.w
|
|
||||||
const yMax = item.y + item.h
|
|
||||||
if (x >= item.x && x <= xMax && y >= item.y && y <= yMax) {
|
|
||||||
x = item.x
|
|
||||||
y = item.y
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
handleCreate(x, y, poolItem)
|
|
||||||
}
|
|
||||||
async function handleCreate(x, y, data) {
|
|
||||||
const { width, height, url, id, name, type, code, moreConfig } = data
|
|
||||||
if (x + width > 12) x = 12 - width // 数据处理防止坐标溢出
|
|
||||||
const obj = {
|
|
||||||
x: x,
|
|
||||||
y: y,
|
|
||||||
w: width,
|
|
||||||
h: height,
|
|
||||||
ready: false,
|
|
||||||
i: new Date().getTime().toString(),
|
|
||||||
config: {
|
|
||||||
url,
|
|
||||||
id,
|
|
||||||
title: name,
|
|
||||||
type,
|
|
||||||
moreConfig,
|
|
||||||
code
|
|
||||||
},
|
|
||||||
data: {}
|
|
||||||
}
|
|
||||||
const params = { code: data.code }
|
|
||||||
data.properties.forEach(item => {
|
|
||||||
const { code, defaultValue } = item
|
|
||||||
defaultValue && (obj.config[code] = defaultValue)
|
|
||||||
if (item.paramsd) params[code] = defaultValue
|
|
||||||
// if (item.code === 'color') {
|
|
||||||
// const color = []
|
|
||||||
// JSON.parse(item.config).forEach((cell) => {
|
|
||||||
// color.push(cell.value)
|
|
||||||
// })
|
|
||||||
// obj.config.color = color
|
|
||||||
// }
|
|
||||||
})
|
|
||||||
obj.config.params = params
|
|
||||||
getCardData(obj)
|
|
||||||
data.url && getData(data.url, params, obj)
|
|
||||||
layoutData.value.unshift(obj)
|
|
||||||
await nextTick()
|
|
||||||
obj.ready = true
|
|
||||||
}
|
|
||||||
function getComponent(config) {
|
|
||||||
const map = {
|
|
||||||
// PM: 'ServerStatusOverview',
|
|
||||||
// VM: 'ServerStatusOverview'
|
|
||||||
}
|
|
||||||
const component = map[config.code] || config.type
|
|
||||||
const copComponentList = ['TaskCount', 'InspectCategoryCount', 'InspectHistory', 'Top5']
|
|
||||||
if (!copComponentList.includes(component)) return component
|
|
||||||
}
|
|
||||||
// 保存配置
|
|
||||||
async function savePanels() {
|
|
||||||
const handleConfig = function () {
|
|
||||||
const config = []
|
|
||||||
layoutData.value.forEach(item => {
|
|
||||||
const obj = {
|
|
||||||
id: item.id,
|
|
||||||
module: 'CMC',
|
|
||||||
location: [item.x, item.y, item.w, item.h].join(','),
|
|
||||||
config: item.config
|
|
||||||
}
|
|
||||||
config.push(obj)
|
|
||||||
})
|
|
||||||
return config
|
|
||||||
}
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
const res = await savePanel({
|
|
||||||
module: 'COS',
|
|
||||||
config: handleConfig()
|
|
||||||
})
|
|
||||||
if (res.success) {
|
|
||||||
Message.success(res.message)
|
|
||||||
goPage('/resource_dashboard')
|
|
||||||
}
|
|
||||||
} catch (error) {}
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
// 恢复默认设置
|
|
||||||
async function reset() {
|
|
||||||
MessageBox.confirm('您确定要恢复默认设置吗?', '提示', {
|
|
||||||
confirmButtonClass: 'el-button--danger',
|
|
||||||
type: 'warning'
|
|
||||||
})
|
|
||||||
.then(async () => {
|
|
||||||
const res = await resetPanel('COS')
|
|
||||||
if (res.success) {
|
|
||||||
Message.success(res.message)
|
|
||||||
goPage('/resource_dashboard')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(e => {})
|
|
||||||
}
|
|
||||||
const store = useStore()
|
|
||||||
const userData = computed(() => store.getters.userData || {})
|
|
||||||
// 编辑删除卡片
|
|
||||||
const currentPanel = ref({})
|
|
||||||
const configData = ref({})
|
|
||||||
function handleDelete(index) {
|
|
||||||
layoutData.value.splice(index, 1)
|
|
||||||
}
|
|
||||||
const dialogVisible = ref(false)
|
|
||||||
async function handleEdit(item) {
|
|
||||||
currentPanel.value = item
|
|
||||||
const config = item.config
|
|
||||||
configData.value = [
|
|
||||||
{
|
|
||||||
code: 'title',
|
|
||||||
defaultValue: config.title,
|
|
||||||
name: '卡片标题',
|
|
||||||
paramsd: false,
|
|
||||||
type: 'TEXT'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
// 是否需要额外配置
|
|
||||||
if (config.moreConfig) {
|
|
||||||
const res = await getConfig({
|
|
||||||
poolId: config.id
|
|
||||||
})
|
|
||||||
if (res.success) {
|
|
||||||
configData.value = res.data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unref(configData).forEach(item => {
|
|
||||||
item.defaultValue = config[item.code]
|
|
||||||
if (item.config) item.config = JSON.parse(item.config)
|
|
||||||
if (item.code === 'color' && config.color) {
|
|
||||||
item.config.forEach((cell, key) => {
|
|
||||||
cell.value = config.color[key]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
dialogVisible.value = true
|
|
||||||
}
|
|
||||||
// 对组件进行设置
|
|
||||||
function settingPanel() {
|
|
||||||
const config = currentPanel.value.config
|
|
||||||
const params = { code: config.code }
|
|
||||||
unref(configData).forEach(item => {
|
|
||||||
config[item.code] = item.defaultValue
|
|
||||||
// 是否传参
|
|
||||||
if (item.paramsd) params[item.code] = item.defaultValue
|
|
||||||
if (item.code === 'color') {
|
|
||||||
config.color = []
|
|
||||||
item.config.forEach(cell => {
|
|
||||||
config.color.push(cell.value)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
dialogVisible.value = false
|
|
||||||
config.params = params
|
|
||||||
config.url && getData(config.url, params, currentPanel.value)
|
|
||||||
}
|
|
||||||
const chartRef = ref([])
|
|
||||||
// 改变大小
|
|
||||||
function resizeEvent(i) {
|
|
||||||
const chartCell = unref(chartRef).find(item => item.id === i)
|
|
||||||
if (chartCell) chartCell.chart.resize()
|
|
||||||
}
|
|
||||||
function goPage(path) {
|
|
||||||
const router = useRouter()
|
|
||||||
router.push(path)
|
|
||||||
// router.push({
|
|
||||||
// name: 'Redirect',
|
|
||||||
// query: {
|
|
||||||
// path
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
}
|
|
||||||
function getChartConfig(item) {
|
|
||||||
const { chartConfig } = item.config
|
|
||||||
return typeof chartConfig === 'string' ? JSON.parse(chartConfig) : chartConfig
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
topSetting,
|
|
||||||
loading,
|
|
||||||
userData,
|
|
||||||
poolList,
|
|
||||||
hideSelectedModule,
|
|
||||||
layoutData,
|
|
||||||
forbidPoolCodes,
|
|
||||||
drop,
|
|
||||||
getComponent,
|
|
||||||
isSetting,
|
|
||||||
savePanels,
|
|
||||||
reset,
|
|
||||||
getChartConfig,
|
|
||||||
// 卡片操作
|
|
||||||
dialogVisible,
|
|
||||||
currentPanel,
|
|
||||||
configData,
|
|
||||||
handleDelete,
|
|
||||||
handleEdit,
|
|
||||||
settingPanel,
|
|
||||||
chartRef,
|
|
||||||
resizeEvent,
|
|
||||||
color,
|
|
||||||
goPage
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -445,108 +85,77 @@ export default {
|
||||||
height: calc(100vh - 50px);
|
height: calc(100vh - 50px);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
&.full {
|
font-size: 14px !important;
|
||||||
margin: 0 -16px;
|
|
||||||
}
|
|
||||||
.setting-container {
|
.setting-container {
|
||||||
height: calc(100% - 50px);
|
height: calc(100% - 50px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.setting-header {
|
::v-deep {
|
||||||
background: #fff;
|
.el-table--group,
|
||||||
height: 50px !important;
|
.el-table--border {
|
||||||
display: flex;
|
border: none;
|
||||||
align-items: center;
|
|
||||||
border-bottom: 1px solid #e4e4e4;
|
|
||||||
& > span {
|
|
||||||
font-weight: bold;
|
|
||||||
flex: 1;
|
|
||||||
}
|
}
|
||||||
|
.el-table th.is-leaf,
|
||||||
|
.el-table td {
|
||||||
|
border: none;
|
||||||
}
|
}
|
||||||
.setting-aside {
|
|
||||||
background: #fff;
|
.el-table::before,
|
||||||
height: 100%;
|
.el-table--group::after,
|
||||||
padding: 16px;
|
.el-table--border::after {
|
||||||
.aside-tool {
|
content: '';
|
||||||
margin-bottom: 18px;
|
position: absolute;
|
||||||
|
background-color: #fff;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
.pool-scroll {
|
.table-container .table-header {
|
||||||
height: calc(100% - 50px);
|
background: linear-gradient(180deg, #f0f3ff 0%, #fafbff 100%);
|
||||||
}
|
|
||||||
.pool-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
border-radius: 4px;
|
|
||||||
height: 36px;
|
|
||||||
padding: 0 12px;
|
|
||||||
font-size: 12px;
|
|
||||||
cursor: move;
|
|
||||||
border: 1px solid #e6e6e6;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
&.forbid {
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
& > span {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
.icon {
|
|
||||||
color: #1e54de;
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.view-card {
|
|
||||||
height: 100%;
|
|
||||||
box-sizing: border-box;
|
|
||||||
border-radius: 4px;
|
|
||||||
background: #ffffff;
|
|
||||||
padding: 20px;
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
.card-title {
|
|
||||||
font-weight: bold;
|
|
||||||
color: #393b3e;
|
color: #393b3e;
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
}
|
||||||
.card-body {
|
// 按钮
|
||||||
height: calc(100% - 40px);
|
.el-button--primary.is-plain {
|
||||||
display: flex;
|
color: rgba(72, 144, 253, 1);
|
||||||
flex-direction: column;
|
background: rgba(72, 144, 253, 0.1);
|
||||||
justify-content: center;
|
border: none;
|
||||||
}
|
}
|
||||||
.card-operate {
|
.el-button--primary.is-plain:hover,
|
||||||
|
.el-button--primary.is-plain:focus {
|
||||||
|
background: rgba(72, 144, 253, 1);
|
||||||
|
border-color: rgba(72, 144, 253, 1);
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
.el-button--text {
|
||||||
|
color: #4890fd;
|
||||||
|
}
|
||||||
|
.el-button--text:hover,
|
||||||
|
.el-button--text:focus {
|
||||||
|
color: #4b76dd;
|
||||||
|
border-color: transparent;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表格
|
||||||
|
.pagination-container {
|
||||||
|
position: relative;
|
||||||
|
.el-pagination__total {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: -32.5px;
|
left: 0;
|
||||||
top: -32.5px;
|
}
|
||||||
display: flex;
|
.el-pagination__jump {
|
||||||
z-index: 2;
|
display: none;
|
||||||
align-items: center;
|
}
|
||||||
justify-content: center;
|
.el-pagination__sizes {
|
||||||
width: 65px;
|
margin-right: 0;
|
||||||
height: 65px;
|
}
|
||||||
border-radius: 50%;
|
.el-pagination.is-background .el-pager li:not(.disabled).active {
|
||||||
background: rgba(30, 84, 222, 0.25);
|
background: rgba(0, 144, 255, 0.1);
|
||||||
.operate-icon {
|
color: #1890ff;
|
||||||
color: #fff;
|
}
|
||||||
position: absolute;
|
.el-pagination.is-background .btn-prev,
|
||||||
bottom: -24px;
|
.el-pagination.is-background .btn-next,
|
||||||
left: -18px;
|
.el-pagination.is-background .el-pager li {
|
||||||
cursor: pointer;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.grid-item {
|
|
||||||
touch-action: none;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
.setting-main {
|
|
||||||
padding: 0;
|
|
||||||
height: 100%;
|
|
||||||
.top-header {
|
|
||||||
padding: 17px 17px 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.full-height {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -0,0 +1,552 @@
|
||||||
|
<template>
|
||||||
|
<el-container class="setting-wrapper" :class="{ full: !isSetting }">
|
||||||
|
<el-header class="setting-header" v-if="isSetting">
|
||||||
|
<span>自定义设置</span>
|
||||||
|
<div>
|
||||||
|
<cb-link @click="reset()">恢复默认设置</cb-link>
|
||||||
|
<!-- <el-button>取消</el-button> -->
|
||||||
|
<el-button type="primary" @click="savePanels" :loading="loading">保存</el-button>
|
||||||
|
</div>
|
||||||
|
</el-header>
|
||||||
|
<el-container class="setting-container">
|
||||||
|
<el-aside width="200px" class="setting-aside" v-if="isSetting">
|
||||||
|
<div class="aside-tool">
|
||||||
|
<span>隐藏已添加模块</span>
|
||||||
|
<el-switch class="pull-right" v-model="hideSelectedModule"></el-switch>
|
||||||
|
</div>
|
||||||
|
<el-scrollbar class="pool-scroll">
|
||||||
|
<draggable :list="poolList" v-bind="{ sort: false, group: { name: 'field', pull: 'clone', put: false } }" @end="drop">
|
||||||
|
<div class="pool-item" :class="forbidPoolCodes.includes(item.code) && 'forbid'" v-for="item in poolList" :key="item.id" :index="item.id" v-show="!(hideSelectedModule && forbidPoolCodes.includes(item.code))">
|
||||||
|
<span>{{ item.name }}</span>
|
||||||
|
<i class="icon" :class="forbidPoolCodes.includes(item.code) ? 'el-icon-success' : 'el-icon-plus'"></i>
|
||||||
|
</div>
|
||||||
|
</draggable>
|
||||||
|
</el-scrollbar>
|
||||||
|
</el-aside>
|
||||||
|
<el-main class="setting-main" id="setting-container">
|
||||||
|
<el-scrollbar style="height: 100%">
|
||||||
|
<div v-if="!isSetting" class="top-header">
|
||||||
|
<span>您好,{{ userData.name }} 欢迎您!</span>
|
||||||
|
<el-button class="pull-right" type="text" @click="goPage('/config/dashboard-setting')">首页设置</el-button>
|
||||||
|
</div>
|
||||||
|
<grid-layout v-if="layoutData && layoutData.length" :layout="layoutData" :col-num="12" :row-height="16" :is-draggable="isSetting" :is-resizable="isSetting" :vertical-compact="true" :margin="[16, 16]" :use-css-transforms="true">
|
||||||
|
<grid-item class="grid-item" v-for="(item, key) in layoutData" :key="item.i" :x="item.x" :y="item.y" :w="item.w" :h="item.h" :i="item.i" @resize="resizeEvent" @resized="resizeEvent">
|
||||||
|
<div class="view-card">
|
||||||
|
<div class="card-title">
|
||||||
|
<span>{{ item.config.title }}</span>
|
||||||
|
<div class="card-operate" v-if="isSetting">
|
||||||
|
<el-dropdown>
|
||||||
|
<span class="operate-icon"><i class="el-icon-setting"></i></span>
|
||||||
|
<el-dropdown-menu slot="dropdown">
|
||||||
|
<el-dropdown-item @click.native="handleEdit(item)">编辑</el-dropdown-item>
|
||||||
|
<el-dropdown-item @click.native="handleDelete(key)">删除</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</el-dropdown>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body" v-if="item.ready">
|
||||||
|
<cg-loop-charts
|
||||||
|
v-if="item.config.type == 'LoopCharts'"
|
||||||
|
:setting="getChartConfig(item)"
|
||||||
|
ref="chartRef"
|
||||||
|
:id="item.i"
|
||||||
|
:data="item.data"
|
||||||
|
width="100%"
|
||||||
|
height="100%"
|
||||||
|
:theme="item.config.title"
|
||||||
|
:unit="item.config.unit"
|
||||||
|
:type="['TodayAlarmCount'].includes(item.config.code) ? 'half' : ''"
|
||||||
|
></cg-loop-charts>
|
||||||
|
<cg-line-charts v-else-if="item.config.type == 'LineCharts'" ref="chartRef" :id="item.i" :data="item.data" width="100%" height="100%" :unit="item.config.unit" :setting="getChartConfig(item)"></cg-line-charts>
|
||||||
|
<cg-bar-reverse-charts v-else-if="item.config.type == 'BarReverseCharts'" ref="chartRef" :id="item.i" :data="item.data" width="100%" height="100%" :unit="item.config.unit" :setting="topSetting"></cg-bar-reverse-charts>
|
||||||
|
<component v-else :is="getComponent(item.config)" :item-data="item" :is-setting="isSetting"></component>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</grid-item>
|
||||||
|
</grid-layout>
|
||||||
|
<el-dialog title="卡片配置" :close-on-click-modal="false" :visible.sync="dialogVisible" width="800px">
|
||||||
|
<el-form label-width="90px">
|
||||||
|
<template v-for="item in configData">
|
||||||
|
<el-form-item :label="item.name + ':'" v-if="item.type === 'TEXT'" :key="item.name">
|
||||||
|
<el-input v-model="item.defaultValue"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<select-vendor :itemData="item" :config-data="configData" v-else-if="item.type === 'SELECTVENDOR'" :key="item.name + 1"> </select-vendor>
|
||||||
|
</template>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button type="ghost" @click.native="dialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click.native="settingPanel">确定</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
<cb-empty class="empty" v-if="layoutData && layoutData.length === 0">
|
||||||
|
<div class="text-center">
|
||||||
|
<div class="m-b-xs">暂无数据</div>
|
||||||
|
<div>请按照所纳管的平台进入<router-link class="detail-href" :to="{ name: 'SettingDashboard' }">管理中心→系统配置→首页配置</router-link>中配置相关数据</div>
|
||||||
|
</div>
|
||||||
|
</cb-empty>
|
||||||
|
</el-scrollbar>
|
||||||
|
</el-main>
|
||||||
|
</el-container>
|
||||||
|
</el-container>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import { computed, nextTick, ref, unref, watch } from 'vue'
|
||||||
|
import VueGridLayout from 'vue-grid-layout'
|
||||||
|
import draggable from 'vuedraggable'
|
||||||
|
import CommonOperation from './CommonOperation.vue'
|
||||||
|
import AccessControl from './AccessControl.vue'
|
||||||
|
import DataCenterOverview from './DataCenterOverview.vue'
|
||||||
|
import CountCard from './CountCard.vue'
|
||||||
|
import DataView from './DataView.vue'
|
||||||
|
import ResUsed from './ResUsed.vue'
|
||||||
|
import AlarmCount from './AlarmCount.vue'
|
||||||
|
import TaskHistory from './TaskHistory.vue'
|
||||||
|
import SelectVendor from './SelectVendor.vue'
|
||||||
|
import { getPanel, getPool, savePanel, getConfig, resetPanel } from 'services/system/portal'
|
||||||
|
import { request } from '@cmp/cmp-element'
|
||||||
|
import { wrapperParams } from 'utils'
|
||||||
|
import { Message, MessageBox } from 'element-ui'
|
||||||
|
import { getCardData } from './utils'
|
||||||
|
import { topSetting } from './data'
|
||||||
|
import { useRouter, useRoute, useStore, useInstance } from '@cmp/cmp-core'
|
||||||
|
const GridLayout = VueGridLayout.GridLayout
|
||||||
|
const GridItem = VueGridLayout.GridItem
|
||||||
|
const colorMap = ['#1890FF', '#F84540', '#18BE6A', '#696BD8', '#FE9900', '#01b3eb']
|
||||||
|
const color = ['#E03B3B', '#F09C2B', '#049BD3', '#1E54DE']
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
GridLayout,
|
||||||
|
GridItem,
|
||||||
|
draggable,
|
||||||
|
CommonOperation,
|
||||||
|
AccessControl,
|
||||||
|
DataCenterOverview,
|
||||||
|
CountCard,
|
||||||
|
DataView,
|
||||||
|
ResUsed,
|
||||||
|
AlarmCount,
|
||||||
|
TaskHistory,
|
||||||
|
SelectVendor
|
||||||
|
},
|
||||||
|
setup(props, context) {
|
||||||
|
const loading = ref(false)
|
||||||
|
// 是否可配置
|
||||||
|
const isSetting = ref(true)
|
||||||
|
const route = useRoute()
|
||||||
|
const ins = useInstance()
|
||||||
|
function init() {
|
||||||
|
getPanelList()
|
||||||
|
if (ins.$route.path === '/resource_dashboard') {
|
||||||
|
// 展示界面
|
||||||
|
isSetting.value = false
|
||||||
|
} else {
|
||||||
|
getPoolList()
|
||||||
|
isSetting.value = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
init()
|
||||||
|
watch(
|
||||||
|
() => ins.$route.path,
|
||||||
|
() => {
|
||||||
|
init()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// 卡片池处理
|
||||||
|
const poolList = ref([])
|
||||||
|
const hideSelectedModule = ref(false)
|
||||||
|
const poolMap = computed(() => {
|
||||||
|
const map = {}
|
||||||
|
unref(poolList).forEach(item => {
|
||||||
|
map[item.id] = item
|
||||||
|
})
|
||||||
|
return map
|
||||||
|
})
|
||||||
|
async function getPoolList() {
|
||||||
|
const res = await getPool({ module: 'COS' })
|
||||||
|
if (res.success) {
|
||||||
|
poolList.value = res.data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 获取内容面板
|
||||||
|
const layoutData = ref([])
|
||||||
|
const forbidPoolCodes = computed(() => {
|
||||||
|
return unref(layoutData)
|
||||||
|
.map(item => item.config.code)
|
||||||
|
.filter(item => {
|
||||||
|
return !['PM', 'VM', 'DATAVIEW'].includes(item)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
// 获取组件数据
|
||||||
|
function getData(url, params, item) {
|
||||||
|
request
|
||||||
|
.get(url, {
|
||||||
|
params: wrapperParams(params)
|
||||||
|
})
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
item.data = data.data
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function handleListData(item) {
|
||||||
|
const config = (item.config = JSON.parse(item.config))
|
||||||
|
const location = item.location.split(',')
|
||||||
|
item.x = Number(location[0])
|
||||||
|
item.y = Number(location[1])
|
||||||
|
item.w = Number(location[2])
|
||||||
|
item.h = Number(location[3])
|
||||||
|
item.i = item.id.toString()
|
||||||
|
item.data = {}
|
||||||
|
item.ready = false
|
||||||
|
setTimeout(() => {
|
||||||
|
item.ready = true
|
||||||
|
})
|
||||||
|
getCardData(item)
|
||||||
|
config.url && getData(config.url, config.params, item)
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
async function getPanelList() {
|
||||||
|
loading.value = true
|
||||||
|
const data = await getPanel({ module: 'COS' })
|
||||||
|
loading.value = false
|
||||||
|
if (data.success) {
|
||||||
|
const result = []
|
||||||
|
data.data.forEach(item => {
|
||||||
|
result.push(handleListData(item))
|
||||||
|
})
|
||||||
|
layoutData.value = result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 新建panel
|
||||||
|
function drop(e) {
|
||||||
|
const id = e.item.getAttribute('index')
|
||||||
|
const poolItem = poolMap.value[id]
|
||||||
|
if (forbidPoolCodes.value.includes(poolItem.code)) return
|
||||||
|
// 处理生成坐标
|
||||||
|
const colWidth = document.getElementById('setting-container').offsetWidth / 12
|
||||||
|
let x = Math.round((e.originalEvent.pageX - 300) / colWidth)
|
||||||
|
let y = Math.round((e.originalEvent.pageY - 92) / 80)
|
||||||
|
unref(layoutData).forEach(item => {
|
||||||
|
const xMax = item.x + item.w
|
||||||
|
const yMax = item.y + item.h
|
||||||
|
if (x >= item.x && x <= xMax && y >= item.y && y <= yMax) {
|
||||||
|
x = item.x
|
||||||
|
y = item.y
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
handleCreate(x, y, poolItem)
|
||||||
|
}
|
||||||
|
async function handleCreate(x, y, data) {
|
||||||
|
const { width, height, url, id, name, type, code, moreConfig } = data
|
||||||
|
if (x + width > 12) x = 12 - width // 数据处理防止坐标溢出
|
||||||
|
const obj = {
|
||||||
|
x: x,
|
||||||
|
y: y,
|
||||||
|
w: width,
|
||||||
|
h: height,
|
||||||
|
ready: false,
|
||||||
|
i: new Date().getTime().toString(),
|
||||||
|
config: {
|
||||||
|
url,
|
||||||
|
id,
|
||||||
|
title: name,
|
||||||
|
type,
|
||||||
|
moreConfig,
|
||||||
|
code
|
||||||
|
},
|
||||||
|
data: {}
|
||||||
|
}
|
||||||
|
const params = { code: data.code }
|
||||||
|
data.properties.forEach(item => {
|
||||||
|
const { code, defaultValue } = item
|
||||||
|
defaultValue && (obj.config[code] = defaultValue)
|
||||||
|
if (item.paramsd) params[code] = defaultValue
|
||||||
|
// if (item.code === 'color') {
|
||||||
|
// const color = []
|
||||||
|
// JSON.parse(item.config).forEach((cell) => {
|
||||||
|
// color.push(cell.value)
|
||||||
|
// })
|
||||||
|
// obj.config.color = color
|
||||||
|
// }
|
||||||
|
})
|
||||||
|
obj.config.params = params
|
||||||
|
getCardData(obj)
|
||||||
|
data.url && getData(data.url, params, obj)
|
||||||
|
layoutData.value.unshift(obj)
|
||||||
|
await nextTick()
|
||||||
|
obj.ready = true
|
||||||
|
}
|
||||||
|
function getComponent(config) {
|
||||||
|
const map = {
|
||||||
|
// PM: 'ServerStatusOverview',
|
||||||
|
// VM: 'ServerStatusOverview'
|
||||||
|
}
|
||||||
|
const component = map[config.code] || config.type
|
||||||
|
const copComponentList = ['TaskCount', 'InspectCategoryCount', 'InspectHistory', 'Top5']
|
||||||
|
if (!copComponentList.includes(component)) return component
|
||||||
|
}
|
||||||
|
// 保存配置
|
||||||
|
async function savePanels() {
|
||||||
|
const handleConfig = function () {
|
||||||
|
const config = []
|
||||||
|
layoutData.value.forEach(item => {
|
||||||
|
const obj = {
|
||||||
|
id: item.id,
|
||||||
|
module: 'CMC',
|
||||||
|
location: [item.x, item.y, item.w, item.h].join(','),
|
||||||
|
config: item.config
|
||||||
|
}
|
||||||
|
config.push(obj)
|
||||||
|
})
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const res = await savePanel({
|
||||||
|
module: 'COS',
|
||||||
|
config: handleConfig()
|
||||||
|
})
|
||||||
|
if (res.success) {
|
||||||
|
Message.success(res.message)
|
||||||
|
goPage('/resource_dashboard')
|
||||||
|
}
|
||||||
|
} catch (error) {}
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
// 恢复默认设置
|
||||||
|
async function reset() {
|
||||||
|
MessageBox.confirm('您确定要恢复默认设置吗?', '提示', {
|
||||||
|
confirmButtonClass: 'el-button--danger',
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
.then(async () => {
|
||||||
|
const res = await resetPanel('COS')
|
||||||
|
if (res.success) {
|
||||||
|
Message.success(res.message)
|
||||||
|
goPage('/resource_dashboard')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(e => {})
|
||||||
|
}
|
||||||
|
const store = useStore()
|
||||||
|
const userData = computed(() => store.getters.userData || {})
|
||||||
|
// 编辑删除卡片
|
||||||
|
const currentPanel = ref({})
|
||||||
|
const configData = ref({})
|
||||||
|
function handleDelete(index) {
|
||||||
|
layoutData.value.splice(index, 1)
|
||||||
|
}
|
||||||
|
const dialogVisible = ref(false)
|
||||||
|
async function handleEdit(item) {
|
||||||
|
currentPanel.value = item
|
||||||
|
const config = item.config
|
||||||
|
configData.value = [
|
||||||
|
{
|
||||||
|
code: 'title',
|
||||||
|
defaultValue: config.title,
|
||||||
|
name: '卡片标题',
|
||||||
|
paramsd: false,
|
||||||
|
type: 'TEXT'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
// 是否需要额外配置
|
||||||
|
if (config.moreConfig) {
|
||||||
|
const res = await getConfig({
|
||||||
|
poolId: config.id
|
||||||
|
})
|
||||||
|
if (res.success) {
|
||||||
|
configData.value = res.data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unref(configData).forEach(item => {
|
||||||
|
item.defaultValue = config[item.code]
|
||||||
|
if (item.config) item.config = JSON.parse(item.config)
|
||||||
|
if (item.code === 'color' && config.color) {
|
||||||
|
item.config.forEach((cell, key) => {
|
||||||
|
cell.value = config.color[key]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
dialogVisible.value = true
|
||||||
|
}
|
||||||
|
// 对组件进行设置
|
||||||
|
function settingPanel() {
|
||||||
|
const config = currentPanel.value.config
|
||||||
|
const params = { code: config.code }
|
||||||
|
unref(configData).forEach(item => {
|
||||||
|
config[item.code] = item.defaultValue
|
||||||
|
// 是否传参
|
||||||
|
if (item.paramsd) params[item.code] = item.defaultValue
|
||||||
|
if (item.code === 'color') {
|
||||||
|
config.color = []
|
||||||
|
item.config.forEach(cell => {
|
||||||
|
config.color.push(cell.value)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
dialogVisible.value = false
|
||||||
|
config.params = params
|
||||||
|
config.url && getData(config.url, params, currentPanel.value)
|
||||||
|
}
|
||||||
|
const chartRef = ref([])
|
||||||
|
// 改变大小
|
||||||
|
function resizeEvent(i) {
|
||||||
|
const chartCell = unref(chartRef).find(item => item.id === i)
|
||||||
|
if (chartCell) chartCell.chart.resize()
|
||||||
|
}
|
||||||
|
function goPage(path) {
|
||||||
|
const router = useRouter()
|
||||||
|
router.push(path)
|
||||||
|
// router.push({
|
||||||
|
// name: 'Redirect',
|
||||||
|
// query: {
|
||||||
|
// path
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
function getChartConfig(item) {
|
||||||
|
const { chartConfig } = item.config
|
||||||
|
return typeof chartConfig === 'string' ? JSON.parse(chartConfig) : chartConfig
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
topSetting,
|
||||||
|
loading,
|
||||||
|
userData,
|
||||||
|
poolList,
|
||||||
|
hideSelectedModule,
|
||||||
|
layoutData,
|
||||||
|
forbidPoolCodes,
|
||||||
|
drop,
|
||||||
|
getComponent,
|
||||||
|
isSetting,
|
||||||
|
savePanels,
|
||||||
|
reset,
|
||||||
|
getChartConfig,
|
||||||
|
// 卡片操作
|
||||||
|
dialogVisible,
|
||||||
|
currentPanel,
|
||||||
|
configData,
|
||||||
|
handleDelete,
|
||||||
|
handleEdit,
|
||||||
|
settingPanel,
|
||||||
|
chartRef,
|
||||||
|
resizeEvent,
|
||||||
|
color,
|
||||||
|
goPage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.setting-wrapper {
|
||||||
|
height: calc(100vh - 50px);
|
||||||
|
overflow: hidden;
|
||||||
|
flex-direction: column;
|
||||||
|
&.full {
|
||||||
|
margin: 0 -16px;
|
||||||
|
}
|
||||||
|
.setting-container {
|
||||||
|
height: calc(100% - 50px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.setting-header {
|
||||||
|
background: #fff;
|
||||||
|
height: 50px !important;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border-bottom: 1px solid #e4e4e4;
|
||||||
|
& > span {
|
||||||
|
font-weight: bold;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.setting-aside {
|
||||||
|
background: #fff;
|
||||||
|
height: 100%;
|
||||||
|
padding: 16px;
|
||||||
|
.aside-tool {
|
||||||
|
margin-bottom: 18px;
|
||||||
|
}
|
||||||
|
.pool-scroll {
|
||||||
|
height: calc(100% - 50px);
|
||||||
|
}
|
||||||
|
.pool-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 4px;
|
||||||
|
height: 36px;
|
||||||
|
padding: 0 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
cursor: move;
|
||||||
|
border: 1px solid #e6e6e6;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
&.forbid {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
& > span {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.icon {
|
||||||
|
color: #1e54de;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.view-card {
|
||||||
|
height: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: #ffffff;
|
||||||
|
padding: 20px;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
.card-title {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #393b3e;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.card-body {
|
||||||
|
height: calc(100% - 40px);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.card-operate {
|
||||||
|
position: absolute;
|
||||||
|
right: -32.5px;
|
||||||
|
top: -32.5px;
|
||||||
|
display: flex;
|
||||||
|
z-index: 2;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 65px;
|
||||||
|
height: 65px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: rgba(30, 84, 222, 0.25);
|
||||||
|
.operate-icon {
|
||||||
|
color: #fff;
|
||||||
|
position: absolute;
|
||||||
|
bottom: -24px;
|
||||||
|
left: -18px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.grid-item {
|
||||||
|
touch-action: none;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.setting-main {
|
||||||
|
padding: 0;
|
||||||
|
height: 100%;
|
||||||
|
.top-header {
|
||||||
|
padding: 17px 17px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.full-height {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,133 @@
|
||||||
|
<template>
|
||||||
|
<ItemCard title="告警处理" v-bind="$attrs">
|
||||||
|
<el-col :span="8">
|
||||||
|
<div class="cell" v-for="(item, index) in countData" :key="item.name">
|
||||||
|
<div class="mark" :style="{ background: colorMap[index] }"></div>
|
||||||
|
<span class="title">{{ item.name }}</span>
|
||||||
|
<span class="value">{{ item.value }}</span>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="16" class="full-height">
|
||||||
|
<span class="chart-title">近七日告警趋势统计</span>
|
||||||
|
<div style="height: calc(100% - 50px)">
|
||||||
|
<LineCharts v-if="lineData" :data="lineData" width="100%" height="100%" :setting="chartSetting"></LineCharts>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</ItemCard>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import LineCharts from './echarts/LineCharts.vue'
|
||||||
|
import ItemCard from './ItemCard.vue'
|
||||||
|
import { getAlarmChart } from 'services/monitor/index'
|
||||||
|
import * as echarts from 'echarts/core'
|
||||||
|
export const colorMap = ['rgba(255, 0, 0, 1)', 'rgba(245, 167, 45, 1)', 'rgba(18, 185, 242, 1)', 'rgba(24, 144, 255, 1)']
|
||||||
|
const chartSetting = {
|
||||||
|
color: colorMap,
|
||||||
|
legend: {
|
||||||
|
right: 0,
|
||||||
|
textStyle: {
|
||||||
|
color: 'rgba(140, 140, 140, 1)'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default {
|
||||||
|
components: { ItemCard, LineCharts },
|
||||||
|
setup(props, context) {
|
||||||
|
const countData = ref([
|
||||||
|
{
|
||||||
|
name: '紧急告警',
|
||||||
|
value: 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '重要告警',
|
||||||
|
value: 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '次要告警',
|
||||||
|
value: 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '提示告警',
|
||||||
|
value: 0
|
||||||
|
}
|
||||||
|
])
|
||||||
|
;(async function () {
|
||||||
|
const res = await getAlarmChart({ action: 'pieChart' })
|
||||||
|
countData.value = res.data
|
||||||
|
})()
|
||||||
|
const lineData = ref({
|
||||||
|
keys: [],
|
||||||
|
values: []
|
||||||
|
})
|
||||||
|
;(async function () {
|
||||||
|
const end = new Date().setHours(0, 0, 0, 0) / 1000
|
||||||
|
// 一天是86400秒
|
||||||
|
const res = await getAlarmChart({ action: 'barChart', start: end - 86400 * 7, end })
|
||||||
|
res.data.values = res.data.values.map((item, index) => {
|
||||||
|
item.series = {
|
||||||
|
areaStyle: {
|
||||||
|
opacity: 0.1,
|
||||||
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: colorMap[index]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: 'rgba(255,255,255, 0)'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return item
|
||||||
|
})
|
||||||
|
lineData.value = res.data
|
||||||
|
})()
|
||||||
|
return {
|
||||||
|
colorMap,
|
||||||
|
chartSetting,
|
||||||
|
countData,
|
||||||
|
lineData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.cell {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 18px;
|
||||||
|
margin-left: 8px;
|
||||||
|
height: 56px;
|
||||||
|
background: linear-gradient(180deg, rgba(240, 243, 255, 0.5) 0%, rgba(250, 251, 255, 0.5) 100%);
|
||||||
|
border-radius: 4px;
|
||||||
|
.title {
|
||||||
|
margin-left: 23px;
|
||||||
|
}
|
||||||
|
.value {
|
||||||
|
position: absolute;
|
||||||
|
left: 70%;
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.mark {
|
||||||
|
margin-left: 10px;
|
||||||
|
width: 4px;
|
||||||
|
height: 36px;
|
||||||
|
background: #ff0000;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.chart-title {
|
||||||
|
margin-left: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #092943;
|
||||||
|
}
|
||||||
|
.full-height {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,37 @@
|
||||||
|
<template>
|
||||||
|
<ItemCard title="应用CPU TOP5" v-bind="$attrs">
|
||||||
|
<BarReverseCharts width="100%" height="100%" :data="barData" unit="%" v-if="barData" :setting="chartSetting"></BarReverseCharts>
|
||||||
|
</ItemCard>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ItemCard from './ItemCard.vue'
|
||||||
|
import BarReverseCharts from './echarts/BarReverseCharts.vue'
|
||||||
|
import { getResTops } from 'services/monitor/index'
|
||||||
|
const chartSetting = {
|
||||||
|
barColor: ['rgba(255, 204, 77, 1)', 'rgba(59, 228, 218, 1)', 'rgba(93, 148, 255, 1)', 'rgba(93, 148, 255, 1)', 'rgba(93, 148, 255, 1)']
|
||||||
|
}
|
||||||
|
export default {
|
||||||
|
components: { ItemCard, BarReverseCharts },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
barData: null,
|
||||||
|
chartSetting
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getData()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getData() {
|
||||||
|
getResTops({
|
||||||
|
vendorId: 1,
|
||||||
|
type: 'vmMem',
|
||||||
|
limit: 5
|
||||||
|
}).then(data => {
|
||||||
|
this.barData = data.data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,39 @@
|
||||||
|
<template>
|
||||||
|
<ItemCard title="单日告警统计" v-bind="$attrs">
|
||||||
|
<LoopCharts class="full-height" id="alarm1" v-if="todayAlarmData" :data="todayAlarmData" width="100%" type="half" :setting="chartSetting"></LoopCharts>
|
||||||
|
</ItemCard>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ItemCard from './ItemCard.vue'
|
||||||
|
import { getAlarmChart } from 'services/monitor/index.js'
|
||||||
|
import LoopCharts from './echarts/LoopCharts.vue'
|
||||||
|
import { colorMap } from './AlarmHandling.vue'
|
||||||
|
const chartSetting = {
|
||||||
|
color: colorMap
|
||||||
|
}
|
||||||
|
export default {
|
||||||
|
components: { ItemCard, LoopCharts },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
todayAlarmData: null,
|
||||||
|
chartSetting
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this, this.getTodayAlarm()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getTodayAlarm() {
|
||||||
|
getAlarmChart({
|
||||||
|
action: 'pieChart',
|
||||||
|
time: 'TODAY'
|
||||||
|
}).then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
this.todayAlarmData = data.data
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,52 @@
|
||||||
|
<template>
|
||||||
|
<div class="view-card" :style="{ height }">
|
||||||
|
<div class="card-title">
|
||||||
|
<span>{{ title }}</span>
|
||||||
|
<div class="card-operate">
|
||||||
|
<slot name="operate"></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: String,
|
||||||
|
default: '100%'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.view-card {
|
||||||
|
height: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: #ffffff;
|
||||||
|
padding: 20px;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
.card-title {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #393b3e;
|
||||||
|
margin-bottom: 18px;
|
||||||
|
}
|
||||||
|
.card-body {
|
||||||
|
height: calc(100% - 40px);
|
||||||
|
}
|
||||||
|
.card-operate {
|
||||||
|
position: absolute;
|
||||||
|
right: 15px;
|
||||||
|
top: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,76 @@
|
||||||
|
<template>
|
||||||
|
<ItemCard title="公告列表" v-bind="$attrs">
|
||||||
|
<cb-table
|
||||||
|
:data="tableList"
|
||||||
|
:params="params"
|
||||||
|
:get-list="getItemList"
|
||||||
|
:total="total"
|
||||||
|
:otherProps="{
|
||||||
|
border: false
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<el-table-column show-overflow-tooltip label="标题" prop="title">
|
||||||
|
<template slot-scope="{ row }">
|
||||||
|
<div class="flex">
|
||||||
|
<img :src="imgMap[row.type]" alt="" />
|
||||||
|
<span>{{ row.title }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column show-overflow-tooltip label="内容" prop="content"></el-table-column>
|
||||||
|
<el-table-column show-overflow-tooltip label="时间" prop="date"></el-table-column>
|
||||||
|
<span slot="pagination"></span>
|
||||||
|
</cb-table>
|
||||||
|
</ItemCard>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ItemCard from './ItemCard.vue'
|
||||||
|
import { mockHttp } from 'services/system/dashboard'
|
||||||
|
import 红 from './images/通知-红.png'
|
||||||
|
import 黄 from './images/通知-黄.png'
|
||||||
|
import 蓝 from './images/通知-蓝.png'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { ItemCard },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
imgMap: {
|
||||||
|
红,
|
||||||
|
黄,
|
||||||
|
蓝
|
||||||
|
},
|
||||||
|
tableList: [
|
||||||
|
{
|
||||||
|
title: '测试标题',
|
||||||
|
date: '2020-01-01',
|
||||||
|
content: '测试内容',
|
||||||
|
type: '红'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
params: {
|
||||||
|
page: 1,
|
||||||
|
rows: 10
|
||||||
|
},
|
||||||
|
total: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getItemList() {
|
||||||
|
mockHttp(this.params).then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
this.tableList = data.data.rows
|
||||||
|
this.total = data.data.total
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.flex {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 13px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,53 @@
|
||||||
|
<template>
|
||||||
|
<ItemCard title="工单统计" v-bind="$attrs">
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="8" v-for="item in numList" :key="item.label">
|
||||||
|
<div class="num-item" :style="{ backgroundColor: item.background, backgroundImage: `url(${item.bgImg})` }">
|
||||||
|
<div class="value">{{ item.value }}</div>
|
||||||
|
<div class="title">{{ item.label }}</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</ItemCard>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ItemCard from './ItemCard.vue'
|
||||||
|
import 待办工单 from './images/icon-待办工单.png'
|
||||||
|
import 已办工单 from './images/icon-已办工单.png'
|
||||||
|
import 总数 from './images/icon-总数.png'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { ItemCard },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
numList: [
|
||||||
|
{ label: '待办工单', value: 36, bgImg: 待办工单, background: 'rgba(252, 182, 98, 0.1)' },
|
||||||
|
{ label: '已办工单', value: 20, bgImg: 已办工单, background: 'rgba(70, 216, 171, 0.1)' },
|
||||||
|
{ label: '总数', value: 20, bgImg: 总数, background: 'rgba(93, 148, 255, 0.1)' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
::v-deep {
|
||||||
|
.num-item {
|
||||||
|
color: #fff;
|
||||||
|
height: 110px;
|
||||||
|
padding: 30px 0 0 25px;
|
||||||
|
border-radius: 5px;
|
||||||
|
background-position: right bottom;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
.value {
|
||||||
|
color: #000;
|
||||||
|
font-size: 34px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.title {
|
||||||
|
color: #8c8da3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,57 @@
|
||||||
|
<template>
|
||||||
|
<ItemCard title="平台容量统计" v-bind="$attrs">
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="6" v-for="item in numList" :key="item.label">
|
||||||
|
<div class="num-item" :style="{ backgroundColor: item.background, backgroundImage: `url(${item.bgImg})` }">
|
||||||
|
<div class="title">{{ item.label }}</div>
|
||||||
|
<div class="value">{{ item.value }}</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</ItemCard>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ItemCard from './ItemCard.vue'
|
||||||
|
import 服务器 from './images/服务器(台).png'
|
||||||
|
import 虚拟机 from './images/虚拟机(台).png'
|
||||||
|
import 网络设备 from './images/网络设备(台).png'
|
||||||
|
import 安全设备 from './images/安全设备(台).png'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { ItemCard },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
numList: [
|
||||||
|
{ label: '服务器(台)', value: 36, bgImg: 服务器, background: 'rgba(231, 242, 252, 1)' },
|
||||||
|
{ label: '虚拟机(台)', value: 20, bgImg: 虚拟机, background: 'rgba(250, 243, 233, 1)' },
|
||||||
|
{ label: '网络设备(台)', value: 20, bgImg: 网络设备, background: 'rgba(227, 245, 252, 1)' },
|
||||||
|
{ label: '安全设备(台)', value: 20, bgImg: 安全设备, background: 'rgba(235, 246, 239, 1)' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
::v-deep {
|
||||||
|
.num-item {
|
||||||
|
color: #fff;
|
||||||
|
height: 110px;
|
||||||
|
padding: 30px 0 0;
|
||||||
|
padding-left: 40%;
|
||||||
|
border-radius: 5px;
|
||||||
|
background-position: 10% 40%;
|
||||||
|
background-size: 56px;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
.value {
|
||||||
|
color: #000;
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.title {
|
||||||
|
color: #8c8da3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,72 @@
|
||||||
|
<template>
|
||||||
|
<ItemCard title="数据展示" v-bind="$attrs">
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12" v-for="item in numList" :key="item.label">
|
||||||
|
<div class="num-item" :style="{ backgroundImage: `url(${item.bgImg})` }">
|
||||||
|
<div class="value">{{ item.value }}</div>
|
||||||
|
<div>{{ item.label }}</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="20" type="flex" justify="space-around">
|
||||||
|
<el-col :span="7" v-for="item in statistics" :key="item.label">
|
||||||
|
<div class="statistic-item">
|
||||||
|
<div class="value">{{ item.value }}</div>
|
||||||
|
<div class="title">{{ item.label }}</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</ItemCard>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ItemCard from './ItemCard.vue'
|
||||||
|
import 当前用户授权应用数 from './images/icon-当前用户授权应用数.png'
|
||||||
|
import 数据权限申请数量 from './images/icon-数据权限申请数量.png'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { ItemCard },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
numList: [
|
||||||
|
{ label: '当前用户授权应用数', value: 36, bgImg: 当前用户授权应用数 },
|
||||||
|
{ label: '数据权限申请数量', value: 20, bgImg: 数据权限申请数量 }
|
||||||
|
],
|
||||||
|
statistics: [
|
||||||
|
{ label: '数据调度任务量', value: 36 },
|
||||||
|
{ label: '运维任务申请表', value: 17 },
|
||||||
|
{ label: '日志审计任务量', value: 20 }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
::v-deep {
|
||||||
|
.num-item {
|
||||||
|
color: #fff;
|
||||||
|
height: 95px;
|
||||||
|
padding: 30px 0 0 25px;
|
||||||
|
background: #5d94ff;
|
||||||
|
border-radius: 5px;
|
||||||
|
background-position: right;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
.value {
|
||||||
|
font-size: 34px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.statistic-item {
|
||||||
|
margin-top: 25px;
|
||||||
|
.value {
|
||||||
|
font-size: 30px;
|
||||||
|
color: #002c46;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.title {
|
||||||
|
color: #8c8da3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,71 @@
|
||||||
|
<template>
|
||||||
|
<ItemCard title="待办工单" v-bind="$attrs">
|
||||||
|
<el-button slot="operate" type="primary" plain>查看更多</el-button>
|
||||||
|
<cb-table :data="tableList" :params="params" :get-list="getItemList" :total="total">
|
||||||
|
<el-table-column type="index" width="50" label="序号"> </el-table-column>
|
||||||
|
<el-table-column show-overflow-tooltip label="标题" prop="name">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span class="name">{{ scope.row.name }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column show-overflow-tooltip label="状态" prop="status"></el-table-column>
|
||||||
|
<el-table-column show-overflow-tooltip label="发起人" prop="progress"></el-table-column>
|
||||||
|
<el-table-column show-overflow-tooltip label="时间" prop="time"></el-table-column>
|
||||||
|
<el-table-column show-overflow-tooltip label="操作" width="60px">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<cb-link @click="handleOperation(scope.row)">审批</cb-link>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</cb-table>
|
||||||
|
</ItemCard>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ItemCard from './ItemCard.vue'
|
||||||
|
import { mockHttp } from 'services/system/dashboard'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { ItemCard },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tableList: [
|
||||||
|
{
|
||||||
|
name: '测试标题',
|
||||||
|
status: '待处理',
|
||||||
|
progress: '张三',
|
||||||
|
time: '2020-01-01'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
params: {
|
||||||
|
page: 1,
|
||||||
|
rows: 10
|
||||||
|
},
|
||||||
|
total: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getItemList() {
|
||||||
|
mockHttp(this.params).then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
this.tableList = data.data.rows
|
||||||
|
this.total = data.data.total
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleOperation(row) {
|
||||||
|
console.log('操作任务', row)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.name {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #344767;
|
||||||
|
}
|
||||||
|
v-deep {
|
||||||
|
.el-table--small {
|
||||||
|
font-size: 14px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,89 @@
|
||||||
|
<template>
|
||||||
|
<div class="user-info">
|
||||||
|
<div class="user-info__avatar">
|
||||||
|
<img :src="userData.portrait" alt="用户头像" />
|
||||||
|
</div>
|
||||||
|
<div class="user-info__details">
|
||||||
|
<div class="user-info__greeting">{{ greetingMessage }}</div>
|
||||||
|
<div>
|
||||||
|
<div class="user-info__role" v-for="role in userRole" :key="role">{{ role }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="user-info__login-time">登录时间:{{ loginTime }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { useRouter, useRoute, useStore, useInstance } from '@cmp/cmp-core'
|
||||||
|
import { computed, nextTick, ref, unref, watch } from 'vue'
|
||||||
|
export default {
|
||||||
|
setup() {
|
||||||
|
const store = useStore()
|
||||||
|
const userData = computed(() => store.getters.userData || {})
|
||||||
|
const greetingMessage = computed(() => (userData.value.name ? userData.value.name + ', 您好' : '--'))
|
||||||
|
const userRole = computed(() => userData.value.roleNames || [])
|
||||||
|
const loginTime = computed(() => userData.value.lastLoginDate)
|
||||||
|
return {
|
||||||
|
userData,
|
||||||
|
greetingMessage,
|
||||||
|
userRole,
|
||||||
|
loginTime
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.user-info {
|
||||||
|
background-image: url('./images/个人信息bg.png');
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: cover;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding-left: 40px;
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
border-radius: 8px;
|
||||||
|
height: 204px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-info__avatar {
|
||||||
|
margin-right: 30px;
|
||||||
|
border-radius: 12px;
|
||||||
|
overflow: hidden;
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-info__avatar img {
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-info__details {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-info__greeting {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-info__role {
|
||||||
|
display: inline-block;
|
||||||
|
color: #fff;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
line-height: 25px;
|
||||||
|
margin-right: 5px;
|
||||||
|
padding: 0 10px;
|
||||||
|
height: 25px;
|
||||||
|
background: #5d94ff;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-info__login-time {
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,37 @@
|
||||||
|
<template>
|
||||||
|
<ItemCard title="虚拟机CPU利用率 TOP5" v-bind="$attrs">
|
||||||
|
<BarReverseCharts width="100%" height="100%" :data="barData" unit="%" v-if="barData" :setting="chartSetting"></BarReverseCharts>
|
||||||
|
</ItemCard>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ItemCard from './ItemCard.vue'
|
||||||
|
import BarReverseCharts from './echarts/BarReverseCharts.vue'
|
||||||
|
import { getResTops } from 'services/monitor/index'
|
||||||
|
const chartSetting = {
|
||||||
|
barColor: ['rgba(255, 204, 77, 1)', 'rgba(59, 228, 218, 1)', 'rgba(93, 148, 255, 1)', 'rgba(93, 148, 255, 1)', 'rgba(93, 148, 255, 1)']
|
||||||
|
}
|
||||||
|
export default {
|
||||||
|
components: { ItemCard, BarReverseCharts },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
barData: null,
|
||||||
|
chartSetting
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.getData()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getData() {
|
||||||
|
getResTops({
|
||||||
|
vendorId: 1,
|
||||||
|
type: 'hostCpu',
|
||||||
|
limit: 5
|
||||||
|
}).then(data => {
|
||||||
|
this.barData = data.data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,49 @@
|
||||||
|
<template>
|
||||||
|
<ItemCard title="告警列表" v-bind="$attrs">
|
||||||
|
<cb-table :data="tableList" :params="params" :get-list="getItemList" :total="total">
|
||||||
|
<el-table-column type="index" width="50" label="序号"> </el-table-column>
|
||||||
|
<el-table-column show-overflow-tooltip label="标题" prop="name"></el-table-column>
|
||||||
|
<el-table-column show-overflow-tooltip label="级别" prop="level"></el-table-column>
|
||||||
|
<el-table-column show-overflow-tooltip label="宫颈类型" prop="type"></el-table-column>
|
||||||
|
<el-table-column show-overflow-tooltip label="告警来源" prop="time"></el-table-column>
|
||||||
|
<el-table-column show-overflow-tooltip label="时间" prop="time"></el-table-column>
|
||||||
|
<el-table-column show-overflow-tooltip label="操作" width="50">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<cb-link @click="handleWarningOperation(scope.row)">查看工单</cb-link>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</cb-table>
|
||||||
|
</ItemCard>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ItemCard from './ItemCard.vue'
|
||||||
|
import { mockHttp } from 'services/system/dashboard'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { ItemCard },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tableList: [],
|
||||||
|
params: {
|
||||||
|
page: 1,
|
||||||
|
rows: 10
|
||||||
|
},
|
||||||
|
total: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getItemList() {
|
||||||
|
mockHttp(this.params).then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
this.tableList = data.data.rows
|
||||||
|
this.total = data.data.total
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
handleWarningOperation(row) {
|
||||||
|
console.log('处理告警', row)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -0,0 +1,272 @@
|
||||||
|
<template>
|
||||||
|
<div :style="{ height, width }" class="chart-container">
|
||||||
|
<div class="chart" :class="{ hide: isNoData }" :id="id" style="width: 100%; height: 100%"></div>
|
||||||
|
<cb-empty class="chart-no-data" v-show="isNoData"></cb-empty>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import useEchart, { commonProps } from '@cmp/cmp-echarts/hooks/useChart'
|
||||||
|
import { merge } from 'lodash-es'
|
||||||
|
import * as echarts from 'echarts/core'
|
||||||
|
import 排名1 from '../images/排名 1.png'
|
||||||
|
import 排名2 from '../images/排名 2.png'
|
||||||
|
import 排名3 from '../images/排名 3.png'
|
||||||
|
import 排名4 from '../images/排名 4.png'
|
||||||
|
import 排名5 from '../images/排名 5.png'
|
||||||
|
const commonLinerColor = [
|
||||||
|
['#8699FF', '#4B66FF'],
|
||||||
|
['#23A3B0', '#1BB879'],
|
||||||
|
['#888B79', '#DC931F']
|
||||||
|
]
|
||||||
|
function getLinerColor(startColor, endColor) {
|
||||||
|
// 只有一种颜色返回单色
|
||||||
|
if (!endColor) return startColor
|
||||||
|
return new echarts.graphic.LinearGradient(0, 0, 1, 1, [
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: startColor
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: endColor
|
||||||
|
}
|
||||||
|
])
|
||||||
|
}
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
...commonProps
|
||||||
|
},
|
||||||
|
setup(props) {
|
||||||
|
// 数据处理
|
||||||
|
function handleData(data) {
|
||||||
|
const { legendLength = 60, series: seriesConfig = {}, colorMap = {} } = props.setting
|
||||||
|
const legends = []
|
||||||
|
const series = []
|
||||||
|
const { values = [], keys = [] } = data
|
||||||
|
values.forEach(item => {
|
||||||
|
if (!item.data) return
|
||||||
|
const { name } = item
|
||||||
|
const resName = name.substring(0, legendLength)
|
||||||
|
legends.push(name)
|
||||||
|
const data = item.data.map((cell, index) => {
|
||||||
|
return {
|
||||||
|
value: cell,
|
||||||
|
itemStyle: {
|
||||||
|
color: props.setting.barColor[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
series.push({
|
||||||
|
name: resName,
|
||||||
|
type: 'bar',
|
||||||
|
smooth: true,
|
||||||
|
barMaxWidth: 11,
|
||||||
|
showBackground: true,
|
||||||
|
backgroundStyle: {
|
||||||
|
color: 'rgba(245, 247, 250, 1)',
|
||||||
|
borderRadius: 50
|
||||||
|
},
|
||||||
|
itemStyle: {
|
||||||
|
borderRadius: 50
|
||||||
|
},
|
||||||
|
...seriesConfig,
|
||||||
|
data
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
legends,
|
||||||
|
series,
|
||||||
|
keys
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function updateChart() {
|
||||||
|
var _props$data
|
||||||
|
if (!((_props$data = props.data) !== null && _props$data !== void 0 && _props$data.values)) return
|
||||||
|
const { linerColor = commonLinerColor, color: scolor, legend = {}, xAxis = {}, yAxis = {}, grid = {} } = props.setting
|
||||||
|
const { legends, series, keys } = handleData(props.data)
|
||||||
|
const color =
|
||||||
|
scolor ||
|
||||||
|
(linerColor &&
|
||||||
|
linerColor.map(item => {
|
||||||
|
return getLinerColor(item[0], item[1])
|
||||||
|
}))
|
||||||
|
const options = {
|
||||||
|
color: color || commonColor,
|
||||||
|
legend: {
|
||||||
|
show: false,
|
||||||
|
data: legends,
|
||||||
|
...legend
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
top: 10,
|
||||||
|
left: '-150',
|
||||||
|
right: '2%',
|
||||||
|
bottom: 1,
|
||||||
|
containLabel: true,
|
||||||
|
...grid
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
axisPointer: {
|
||||||
|
type: 'shadow'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
xAxis: [
|
||||||
|
{
|
||||||
|
type: 'value',
|
||||||
|
position: 'bottom',
|
||||||
|
splitLine: {
|
||||||
|
lineStyle: {
|
||||||
|
type: 'dashed'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
axisLine: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisTick: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
...xAxis
|
||||||
|
}
|
||||||
|
],
|
||||||
|
yAxis: [
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
inverse: true,
|
||||||
|
// ...yAxis,
|
||||||
|
data: keys,
|
||||||
|
axisLine: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
position: 'left',
|
||||||
|
axisLabel: {
|
||||||
|
show: true,
|
||||||
|
margin: 150,
|
||||||
|
textStyle: {
|
||||||
|
align: 'left'
|
||||||
|
},
|
||||||
|
color: 'rgba(52, 71, 103, 1)',
|
||||||
|
formatter: function (value, index) {
|
||||||
|
return `{rang${index + 1}|}` + ' ' + value
|
||||||
|
},
|
||||||
|
rich: {
|
||||||
|
rang1: {
|
||||||
|
width: 24,
|
||||||
|
height: 28,
|
||||||
|
backgroundColor: {
|
||||||
|
image: 排名1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rang2: {
|
||||||
|
width: 24,
|
||||||
|
height: 28,
|
||||||
|
backgroundColor: {
|
||||||
|
image: 排名2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rang3: {
|
||||||
|
width: 24,
|
||||||
|
height: 28,
|
||||||
|
backgroundColor: {
|
||||||
|
image: 排名3
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rang4: {
|
||||||
|
width: 24,
|
||||||
|
height: 28,
|
||||||
|
backgroundColor: {
|
||||||
|
image: 排名4
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rang5: {
|
||||||
|
width: 24,
|
||||||
|
height: 28,
|
||||||
|
backgroundColor: {
|
||||||
|
image: 排名5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
axisTick: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
...yAxis
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
inverse: true,
|
||||||
|
// ...yAxis,
|
||||||
|
data: keys,
|
||||||
|
axisLine: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
show: true,
|
||||||
|
formatter: function (value, index) {
|
||||||
|
return props.data.values[0].data[index] + '%'
|
||||||
|
},
|
||||||
|
margin: 40,
|
||||||
|
width: 10,
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: 600
|
||||||
|
},
|
||||||
|
axisTick: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
...yAxis
|
||||||
|
}
|
||||||
|
],
|
||||||
|
series
|
||||||
|
}
|
||||||
|
chart.value && chart.value.setOption(merge(options, props.options), true)
|
||||||
|
}
|
||||||
|
const { chart } = useEchart(props, updateChart)
|
||||||
|
const isNoData = computed(() => !props.data?.keys?.length)
|
||||||
|
return {
|
||||||
|
updateChart,
|
||||||
|
chart,
|
||||||
|
isNoData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.chart-container {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
.chart-no-data {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.chart {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
&.hide {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,159 @@
|
||||||
|
<template>
|
||||||
|
<div :style="{ height, width }" class="chart-container">
|
||||||
|
<div class="chart" :class="{ hide: isNoData }" :id="id" style="width: 100%; height: 100%"></div>
|
||||||
|
<cb-empty class="chart-no-data" v-show="isNoData"></cb-empty>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import useEchart, { commonProps } from '@cmp/cmp-echarts/hooks/useChart'
|
||||||
|
import { merge } from 'lodash-es'
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
...commonProps
|
||||||
|
},
|
||||||
|
setup(props, context) {
|
||||||
|
const commonYAxis = {
|
||||||
|
type: 'value',
|
||||||
|
nameGap: 5,
|
||||||
|
min: 0,
|
||||||
|
splitLine: {
|
||||||
|
lineStyle: {
|
||||||
|
color: ['#ccc'],
|
||||||
|
type: 'solid'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
axisLine: {
|
||||||
|
lineStyle: {
|
||||||
|
color: ['#d9d9d9']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
axisTick: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
color: '#333'
|
||||||
|
},
|
||||||
|
nameTextStyle: {
|
||||||
|
color: '#333'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const commonGrid = {
|
||||||
|
left: 20,
|
||||||
|
right: 1,
|
||||||
|
top: 30,
|
||||||
|
bottom: 1,
|
||||||
|
containLabel: true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数据处理
|
||||||
|
function handleData(data) {
|
||||||
|
const { legendLength = 60 } = props.setting
|
||||||
|
const legends = []
|
||||||
|
const series = []
|
||||||
|
const { values = [], keys = [] } = data
|
||||||
|
values.forEach(item => {
|
||||||
|
const { name, data, series: seriesConfig = {} } = item
|
||||||
|
const resName = name?.substring(0, legendLength)
|
||||||
|
legends.push(name)
|
||||||
|
series.push({
|
||||||
|
name: resName,
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
showSymbol: false,
|
||||||
|
...seriesConfig,
|
||||||
|
data
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
legends,
|
||||||
|
series,
|
||||||
|
keys
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function updateChart() {
|
||||||
|
if (!props.data?.values) return
|
||||||
|
const { legends, series, keys } = handleData(props.data)
|
||||||
|
const { legend = {}, grid = {}, xAxis = {}, yAxis = {}, color = [] } = props.setting
|
||||||
|
const options = {
|
||||||
|
color,
|
||||||
|
legend: {
|
||||||
|
type: 'scroll',
|
||||||
|
data: legends,
|
||||||
|
...legend
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
confine: true
|
||||||
|
},
|
||||||
|
xAxis: [
|
||||||
|
{
|
||||||
|
axisTick: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
type: 'category',
|
||||||
|
data: keys.map(item => {
|
||||||
|
return item.replace(' ', '\n')
|
||||||
|
}),
|
||||||
|
axisLabel: {
|
||||||
|
color: 'rgba(140, 140, 140, 1)'
|
||||||
|
},
|
||||||
|
...xAxis
|
||||||
|
}
|
||||||
|
],
|
||||||
|
yAxis: [
|
||||||
|
{
|
||||||
|
...commonYAxis,
|
||||||
|
name: props.unit,
|
||||||
|
|
||||||
|
axisLabel: {
|
||||||
|
color: 'rgba(140, 140, 140, 1)'
|
||||||
|
},
|
||||||
|
...yAxis
|
||||||
|
}
|
||||||
|
],
|
||||||
|
grid: {
|
||||||
|
...commonGrid,
|
||||||
|
...grid
|
||||||
|
},
|
||||||
|
series
|
||||||
|
}
|
||||||
|
chart.value && chart.value.setOption(merge(options, props.options), true)
|
||||||
|
}
|
||||||
|
const { chart } = useEchart(props, updateChart)
|
||||||
|
const isNoData = computed(() => !props.data?.keys?.length)
|
||||||
|
return {
|
||||||
|
chart,
|
||||||
|
updateChart,
|
||||||
|
isNoData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.chart-container {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
.chart-no-data {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.chart {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
&.hide {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,221 @@
|
||||||
|
<template>
|
||||||
|
<div :style="{ height, width }" class="chart-container">
|
||||||
|
<div class="chart" :class="{ hide: isNoData }" :id="id" style="width: 100%; height: 100%"></div>
|
||||||
|
<cb-empty class="chart-no-data" v-show="isNoData"></cb-empty>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import useEchart, { commonProps } from '@cmp/cmp-echarts/hooks/useChart'
|
||||||
|
import { merge } from 'lodash-es'
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
...commonProps,
|
||||||
|
theme: {
|
||||||
|
type: String,
|
||||||
|
default: '数据统计'
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: String
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
isNoData() {
|
||||||
|
var _this$data
|
||||||
|
return !((_this$data = this.data) !== null && _this$data !== void 0 && _this$data.length)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setup(props, context) {
|
||||||
|
function getLegendOptions({ countMap, legend }) {
|
||||||
|
return {
|
||||||
|
icon: 'circle',
|
||||||
|
top: '80%',
|
||||||
|
left: 0,
|
||||||
|
type: 'scroll',
|
||||||
|
formatter: name => {
|
||||||
|
const { legendLength = 10 } = props.setting
|
||||||
|
const resultName = `${name.substr(0, legendLength)}${name.length > legendLength ? '...' : ''}`
|
||||||
|
return `{count|${countMap[name]}${props.unit || ''}}\n{name|${resultName}}`
|
||||||
|
},
|
||||||
|
textStyle: {
|
||||||
|
rich: {
|
||||||
|
name: {
|
||||||
|
color: 'rgba(140, 141, 163, 1)',
|
||||||
|
width: 'auto'
|
||||||
|
},
|
||||||
|
count: {
|
||||||
|
verticalAlign: 'middle',
|
||||||
|
lineHeight: 50,
|
||||||
|
color: '#000',
|
||||||
|
width: 70,
|
||||||
|
fontWeight: 800,
|
||||||
|
fontSize: '24px'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
...legend
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 数据处理
|
||||||
|
function handleData(data) {
|
||||||
|
let total = 0
|
||||||
|
const countMap = {}
|
||||||
|
// 将图例分为左右两份
|
||||||
|
const leftLegends = []
|
||||||
|
data.forEach((item, index) => {
|
||||||
|
const { name, value } = item
|
||||||
|
leftLegends.push(name)
|
||||||
|
|
||||||
|
total += value / 1
|
||||||
|
countMap[name] = value >= 0 ? value : 0
|
||||||
|
})
|
||||||
|
const { fixed = 0 } = props.setting
|
||||||
|
return {
|
||||||
|
total: total.toFixed(fixed) / 1,
|
||||||
|
leftLegends,
|
||||||
|
countMap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function getSeries(total) {
|
||||||
|
if (props.type === 'half') {
|
||||||
|
const data = [...props.data]
|
||||||
|
if (total === 0) {
|
||||||
|
data.push({
|
||||||
|
name: '',
|
||||||
|
value: 1,
|
||||||
|
tooltip: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
itemStyle: {
|
||||||
|
color: 'rgba(53, 114, 248, 1)',
|
||||||
|
borderColor: '#ecf0fc',
|
||||||
|
borderWidth: 15,
|
||||||
|
borderRadius: 0
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
data.push({
|
||||||
|
name: '',
|
||||||
|
value: total || 1,
|
||||||
|
tooltip: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
itemStyle: {
|
||||||
|
borderWidth: 0,
|
||||||
|
color: 'transparent'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
radius: props.setting.radius || ['80%', '100%'],
|
||||||
|
center: props.setting.center || ['50%', '55%'],
|
||||||
|
data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
radius: props.setting.radius || ['45%', '60%'],
|
||||||
|
center: props.setting.center || ['50%', '32%'],
|
||||||
|
data: props.data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function updateChart() {
|
||||||
|
var _props$data
|
||||||
|
if (!((_props$data = props.data) !== null && _props$data !== void 0 && _props$data.length)) return
|
||||||
|
const { leftLegends, total, countMap } = handleData(props.data)
|
||||||
|
const { legend = {}, color = [], series = {} } = props.setting
|
||||||
|
const legendOptions = getLegendOptions({
|
||||||
|
countMap,
|
||||||
|
legend
|
||||||
|
})
|
||||||
|
const options = {
|
||||||
|
color,
|
||||||
|
title: {
|
||||||
|
text: `{name|总数}\n{value|${total}}`,
|
||||||
|
top: props.type === 'half' ? '30%' : '25%',
|
||||||
|
left: 'center',
|
||||||
|
textStyle: {
|
||||||
|
rich: {
|
||||||
|
name: {
|
||||||
|
fontSize: '14px',
|
||||||
|
fontWeight: 'normal',
|
||||||
|
color: '#002C46'
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
fontSize: '26px',
|
||||||
|
lineHeight: 50,
|
||||||
|
fontWeight: 800,
|
||||||
|
color: '#002C46'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
legend: [
|
||||||
|
{
|
||||||
|
...legendOptions,
|
||||||
|
data: leftLegends,
|
||||||
|
left: '10%'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'item',
|
||||||
|
formatter: function (params) {
|
||||||
|
const str = params.seriesName + '</br>' + params.name + ':' + (params.data.value >= 0 ? params.data.value : 0) + (props.unit ? props.unit : '') + '(' + (params.percent >= 0 ? params.percent : 0) + '%)'
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: props.theme,
|
||||||
|
type: 'pie',
|
||||||
|
startAngle: props.type === 'half' ? 180 : 90,
|
||||||
|
avoidLabelOverlap: false,
|
||||||
|
label: {
|
||||||
|
show: false,
|
||||||
|
formatter: '{b} {c}',
|
||||||
|
...series.label
|
||||||
|
},
|
||||||
|
itemStyle: {
|
||||||
|
borderRadius: 5,
|
||||||
|
borderColor: '#fff',
|
||||||
|
borderWidth: 1
|
||||||
|
},
|
||||||
|
...series,
|
||||||
|
...getSeries(total)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
chart.value && chart.value.setOption(merge(options, props.options), true)
|
||||||
|
}
|
||||||
|
const { chart } = useEchart(props, updateChart)
|
||||||
|
return {
|
||||||
|
updateChart,
|
||||||
|
chart
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.chart-container {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
.chart-no-data {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.chart {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
&.hide {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 5.3 KiB |
After Width: | Height: | Size: 547 B |
After Width: | Height: | Size: 53 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 881 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 669 B |
After Width: | Height: | Size: 576 B |
After Width: | Height: | Size: 653 B |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 493 B |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 574 B |
After Width: | Height: | Size: 620 B |
After Width: | Height: | Size: 654 B |
After Width: | Height: | Size: 640 B |
After Width: | Height: | Size: 550 B |
After Width: | Height: | Size: 581 B |
After Width: | Height: | Size: 515 B |
After Width: | Height: | Size: 650 B |