<template>
|
<div class="relative">
|
<HeaderBox :title="projectName" />
|
<!-- 地图 -->
|
<div id="container" style="width: 1920px; height: 995px; background-color: #001635"></div>
|
<!-- 左边 内容 -->
|
<div class="left-card">
|
<div class="mt--16">
|
<TitleBox>
|
<template #title> 项目信息 </template>
|
<template #content>
|
<div class="project-info">
|
<div class="info-item">
|
<div class="flex2 flex">
|
<div class="label">项目编号</div>
|
<div class="value">SPS-2021-01-01</div>
|
</div>
|
<div class="flex1 flex">
|
<div class="label">工程类型</div>
|
<div class="value">工程类型</div>
|
</div>
|
</div>
|
<div class="info-item">
|
<div class="flex2 flex">
|
<div class="label">项目地址</div>
|
<div class="value">西藏自治区拉萨市达孜县健康大道489号</div>
|
</div>
|
<div class="flex1 flex">
|
<div class="label">项目阶段</div>
|
<div class="value">工程类型</div>
|
</div>
|
</div>
|
<div class="info-item">
|
<div class="flex2 flex">
|
<div class="label">施工单位</div>
|
<div class="value">SPS-2021-01-01</div>
|
</div>
|
<div class="flex1 flex">
|
<div class="label">项目经理</div>
|
<div class="value">工程类型</div>
|
</div>
|
</div>
|
<div class="info-item">
|
<div class="flex2 flex">
|
<div class="label">监理单位</div>
|
<div class="value">SPS-2021-01-01</div>
|
</div>
|
<div class="flex1 flex">
|
<div class="label">总监</div>
|
<div class="value">工程类型</div>
|
</div>
|
</div>
|
<div class="info-item">
|
<div class="flex2 flex">
|
<div class="label">建设单位</div>
|
<div class="value">SPS-2021-01-01</div>
|
</div>
|
<div class="flex1 flex">
|
<div class="label">项目负责人</div>
|
<div class="value">工程类</div>
|
</div>
|
</div>
|
<div class="info-item">
|
<div class="flex2 flex">
|
<div class="label dept">设备租赁单位</div>
|
<div class="value">SPS-2021-01-01</div>
|
</div>
|
<div class="flex1 flex">
|
<div class="label">联系人</div>
|
<div class="value">工程类型</div>
|
</div>
|
</div>
|
<div class="info-item">
|
<div class="flex">
|
<div class="label time">最近一次检查时间</div>
|
<div class="value">SPS-2021-01-01</div>
|
</div>
|
</div>
|
</div>
|
</template>
|
</TitleBox>
|
</div>
|
<div class="mt--30" @click="toEquipmentInfo">
|
<TitleBox>
|
<template #title> 设备信息 </template>
|
<template #content>
|
<div class="equipment-info">
|
<img src="../../assets//img/project/chartBg.png" class="chart-bg" alt="" />
|
<div class="pie-canvas" ref="pieChart"></div>
|
<div class="legend">
|
<div class="legend-item" v-for="(it, idx) in legendData" :key="idx">
|
<div class="dot" :style="{ backgroundColor: it.itemStyle.color }"></div>
|
<div class="legend-info">
|
<div class="legend-name" :title="it.name">{{ it.name }}</div>
|
<div class="legend-number">{{ it.value }}</div>
|
</div>
|
</div>
|
</div>
|
</div>
|
</template>
|
</TitleBox>
|
</div>
|
<div class="mt--30">
|
<TitleBox>
|
<template #title>各分项隐患趋势 </template>
|
<template #right>
|
<div @click="toRiskTrend()" class="card-more">
|
<span>详情</span>
|
<img src="../../assets/img/project/more.png" />
|
</div>
|
</template>
|
<template #content>
|
<div class="danger-info">
|
<div class="info-header">
|
<span>隐患类型</span>
|
<div class="header-line">
|
<span>隐患数量</span>
|
<img src="../../assets/img/project/up.png" />
|
</div>
|
<div class="header-line">
|
<span>趋势</span>
|
<img src="../../assets/img/project/down.png" />
|
</div>
|
</div>
|
<div class="info-body">
|
<div class="info-item" v-for="item in 12" :key="item">
|
<div class="item-type">文明施工</div>
|
<div class="item-number">
|
<div class="flex a--center">
|
<div class="flex a--center">
|
<span>本月</span>
|
<div class="number">25</div>
|
</div>
|
<div class="now"></div>
|
</div>
|
<div class="flex a--center">
|
<div class="flex a--center">
|
<span>上月</span>
|
<div class="number">25</div>
|
</div>
|
<div class="before"></div>
|
</div>
|
</div>
|
<div class="item-trend">50%</div>
|
</div>
|
</div>
|
|
<div class="info-bottom"></div>
|
</div>
|
</template>
|
</TitleBox>
|
</div>
|
</div>
|
<!-- 中间内容 -->
|
<div class="mid-card">
|
<div @click="toHiddenDanger(1)" class="mid-info">
|
<div class="flex a--center">
|
<div @click.stop="toHiddenDanger(2)" class="mid-title">隐患数量</div>
|
<div class="flex a--center tab-border">
|
<div class="mid-tab">所有隐患数</div>
|
<div class="mid-tab active-tab">重大隐患数</div>
|
</div>
|
</div>
|
<div class="mid-content">
|
<div class="mid-img">
|
<div class="number">199</div>
|
<div class="name">所有隐患数</div>
|
</div>
|
<div class="mid-total">
|
<div class="mid-item">
|
<div class="mid-txt">已整改</div>
|
<div class="mid-num">62</div>
|
</div>
|
<div class="mid-item">
|
<div class="mid-txt">未整改</div>
|
<div class="mid-num">62</div>
|
</div>
|
</div>
|
</div>
|
</div>
|
<div @click="toPlanOfAudit" class="mid-more">查看报告</div>
|
</div>
|
<!-- 右边 -->
|
<div class="right-card">
|
<div class="mt--16">
|
<TitleBox>
|
<template #title>人员在岗率 </template>
|
<template #right>
|
<div @click="toPersonnelOnDuty" class="card-more">
|
<span>详情</span>
|
<img src="../../assets/img/project/more.png" />
|
</div>
|
</template>
|
<template #content>
|
<div class="people-info">
|
<div class="people-card">
|
<div class="people-item">
|
<div class="people-chart">
|
<el-progress width="74" type="circle" :show-text="false" :percentage="25" define-back-color="#000"
|
color="#32C5FF" :stroke-linecap="square"></el-progress>
|
<div class="progress-card">
|
<div class="progress-num">25.4</div>
|
<div class="progress-unit">%</div>
|
</div>
|
</div>
|
<div class="people-name">项目经理</div>
|
</div>
|
<div class="people-item">
|
<div class="people-chart">
|
<el-progress width="74" type="circle" :show-text="false" :percentage="25" define-back-color="#000"
|
color="#32C5FF" :stroke-linecap="square"></el-progress>
|
<div class="progress-card">
|
<div class="progress-num">25.4</div>
|
<div class="progress-unit">%</div>
|
</div>
|
</div>
|
<div class="people-name">项目经理</div>
|
</div>
|
<div class="people-item">
|
<div class="people-chart">
|
<el-progress width="74" type="circle" :show-text="false" :percentage="25" define-back-color="#000"
|
color="#32C5FF" :stroke-linecap="square"></el-progress>
|
<div class="progress-card">
|
<div class="progress-num">25.4</div>
|
<div class="progress-unit">%</div>
|
</div>
|
</div>
|
<div class="people-name">项目经理</div>
|
</div>
|
</div>
|
<div class="people-card2">
|
<div class="people-item mr--67">
|
<div class="people-chart">
|
<el-progress width="74" type="circle" :show-text="false" :percentage="25" define-back-color="#000"
|
color="#32C5FF" :stroke-linecap="square"></el-progress>
|
<div class="progress-card">
|
<div class="progress-num">25.4</div>
|
<div class="progress-unit">%</div>
|
</div>
|
</div>
|
<div class="people-name">项目经理</div>
|
</div>
|
<div class="people-item">
|
<div class="people-chart">
|
<el-progress width="74" type="circle" :show-text="false" :percentage="25" define-back-color="#000"
|
color="#32C5FF" :stroke-linecap="square"></el-progress>
|
<div class="progress-card">
|
<div class="progress-num">25.4</div>
|
<div class="progress-unit">%</div>
|
</div>
|
</div>
|
<div class="people-name">项目经理</div>
|
</div>
|
</div>
|
</div>
|
</template>
|
</TitleBox>
|
</div>
|
<div class="mt--30">
|
<TitleBox>
|
<template #title>重大风险源 </template>
|
<template #right>
|
<div @click="toMajorRiskList" class="card-more">
|
<span>详情</span>
|
<img src="../../assets/img/project/more.png" />
|
</div>
|
</template>
|
<template #content>
|
<div class="risk-info">
|
<div class="risk-top">
|
<div class="label card1">序号</div>
|
<div class="label card2">风险规模</div>
|
<div class="label flex1">风险源内容</div>
|
<div class="label card4">实施情况</div>
|
</div>
|
<div class="risk-body">
|
<div class="risk-item" v-for="(item, index) in riskSourceList" :key="index">
|
<div class="label card1">{{ index + 1 }}</div>
|
<div class="label card2" :class="{
|
'merged-cell': item.showRiskScale && item.rowspan > 1,
|
'hidden-cell': !item.showRiskScale
|
}">
|
<div v-if="item.showRiskScale" class="merged-content" :style="{
|
height: `${item.rowspan * 36}px`,
|
lineHeight: `${item.rowspan * 36}px`
|
}">
|
{{ item.riskScale }}
|
</div>
|
</div>
|
<div class="label flex1">{{ item.content }}</div>
|
<div class="label card4">{{ item.status }}</div>
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
</TitleBox>
|
</div>
|
<div class="mt--30">
|
<TitleBox>
|
<template #title>项目风险趋势 </template>
|
<template #right>
|
<div @click="handleClick" class="risk-picker">
|
<el-date-picker v-model="dateRange" ref="datePicker" type="monthrange" range-separator=""
|
start-placeholder="" end-placeholder="">
|
</el-date-picker>
|
<div class="risk-ab">
|
<img src="../../assets/img/home/Shape@2x (6).png" class="pick-img" />
|
|
<div class="pick-text">
|
{{ dateTxt }}
|
</div>
|
</div>
|
|
</div>
|
</template>
|
<template #content>
|
<div class="risk-trend">
|
<div id="riskTrendChart" style="width: 100%; height: 234px"></div>
|
</div>
|
</template>
|
</TitleBox>
|
</div>
|
</div>
|
</div>
|
</template>
|
|
<script>
|
import HeaderBox from "../header/index.vue";
|
import moment from "moment";
|
import * as echarts from "echarts";
|
export default {
|
components: {
|
HeaderBox,
|
},
|
data() {
|
return {
|
mapView: null,
|
mapPromise: null,
|
dateRange: [moment().startOf("month"), moment().endOf("month")], //默认本月
|
markers: [],
|
projectId: '',
|
projectName: '',
|
|
// pie数据
|
legendData: [
|
{ name: "正常", value: 10, itemStyle: { color: "#00FF00" } },
|
{ name: "异常", value: 20, itemStyle: { color: "#FF0000" } },
|
{ name: "待处理", value: 30, itemStyle: { color: "#FFA500" } },
|
{ name: "待完成", value: 30, itemStyle: { color: "#FF00FF" } },
|
{ name: "已完成", value: 30, itemStyle: { color: "#00FFFF" } },
|
],
|
dateTxt: '',
|
riskTrandList: [],
|
// 风险源数据
|
riskSourceData: [
|
{ riskScale: '一般', content: '项目风险源内容1', status: '未开始' },
|
{ riskScale: '一般', content: '项目风险源内容2', status: '进行中' },
|
{ riskScale: '一般', content: '项目风险源内容3', status: '已完成' },
|
{ riskScale: '重大', content: '项目风险源内容4', status: '未开始' },
|
{ riskScale: '重大', content: '项目风险源内容5', status: '进行中' },
|
]
|
};
|
},
|
computed: {
|
// 处理风险源数据,添加合并单元格的逻辑
|
riskSourceList() {
|
if (!this.riskSourceData || this.riskSourceData.length === 0) {
|
return [];
|
}
|
|
const result = [];
|
let i = 0;
|
|
while (i < this.riskSourceData.length) {
|
const currentRiskScale = this.riskSourceData[i].riskScale;
|
let rowspan = 1;
|
|
// 计算相同风险规模的行数
|
for (let j = i + 1; j < this.riskSourceData.length; j++) {
|
if (this.riskSourceData[j].riskScale === currentRiskScale) {
|
rowspan++;
|
} else {
|
break;
|
}
|
}
|
|
// 添加第一行,显示风险规模
|
result.push({
|
...this.riskSourceData[i],
|
showRiskScale: true,
|
rowspan: rowspan
|
});
|
|
// 添加后续相同风险规模的行,不显示风险规模
|
for (let k = 1; k < rowspan; k++) {
|
result.push({
|
...this.riskSourceData[i + k],
|
showRiskScale: false,
|
rowspan: 0
|
});
|
}
|
|
i += rowspan;
|
}
|
|
return result;
|
}
|
},
|
watch: {
|
dateRange: {
|
handler(newVal, oldVal) {
|
if (newVal.length > 0) {
|
this.dateTxt =
|
moment(newVal[0]).format("YYYY -MM") +
|
" - " +
|
moment(newVal[1]).format("YYYY -MM");
|
}
|
},
|
immediate: true,
|
},
|
},
|
created() {
|
this.mapPromise = AMapLoader.load({
|
key: "232929db1cb31f26d42b3645a7456154",
|
version: "2.0",
|
});
|
this.projectId = this.$route.query.projectId || ''
|
this.projectName = this.$route.query.projectName || ''
|
},
|
mounted() {
|
this.setMapload()
|
this.initPie3D()
|
this.initRiskChart()
|
},
|
beforeDestroy() { },
|
methods: {
|
toPersonnelOnDuty() {
|
this.$router.push({
|
path: "/personnelOnDuty",
|
query: {
|
projectId: 111,
|
projectName: '项目1',
|
},
|
});
|
},
|
toHiddenDanger(type) {
|
this.$router.push({
|
path: "/hiddenDanger",
|
query: {
|
projectId: 111,
|
projectName: '项目1',
|
activeTab: type,
|
},
|
});
|
},
|
toEquipmentInfo() {
|
this.$router.push({
|
path: "/equipmentList",
|
query: {
|
projectId: 111,
|
projectName: '项目1',
|
},
|
});
|
},
|
toMajorRiskList() {
|
this.$router.push({
|
path: "/majorRisk",
|
query: {
|
projectId: 111,
|
projectName: '项目1',
|
},
|
});
|
},
|
toPlanOfAudit() {
|
this.$router.push({
|
path: "/planOfAudit",
|
query: {
|
projectId: 111,
|
projectName: '项目1',
|
},
|
});
|
},
|
setMapload() {
|
if (!this.mapPromise) {
|
this.mapPromise = AMapLoader.load({
|
key: "232929db1cb31f26d42b3645a7456154",
|
version: "2.0",
|
});
|
}
|
this.mapPromise
|
.then((AMap) => {
|
// 微信建筑图层
|
let satellite = new AMap.TileLayer.Satellite();
|
//创建路网图层
|
let roadNet = new AMap.TileLayer.RoadNet();
|
this.mapView = new AMap.Map("container", {
|
zoom: 13,
|
center: [108.357471, 22.828643], // 初始化地图中心点
|
// mapStyle: "amap://styles/darkblue",
|
layers: [satellite],
|
});
|
this.initCategoryMarkers(AMap);
|
})
|
.catch((e) => {
|
console.error(e); //加载错误提示
|
});
|
},
|
// 初始化 3D 环饼图(echarts-gl)
|
initPie3D() {
|
const el = this.$refs.pieChart;
|
if (!el) return;
|
this._pie = echarts.init(el);
|
|
// 使用动态数据或默认数据
|
const data = this.legendData.length > 0 ? this.legendData : [];
|
|
// 生成 3D 饼图 option(internalDiameterRatio 控制环形内径比)
|
this.legendData = data; // 用于右侧自定义 legend
|
const series = this.getPie3D(data, 0.7);
|
|
const option = {
|
legend: {
|
tooltip: {
|
show: true,
|
},
|
data: data.map((item) => item.name),
|
right: "2%",
|
top: "center",
|
orient: "vertical",
|
itemGap: 20,
|
textStyle: {
|
color: "#fff",
|
fontSize: 12,
|
},
|
show: false,
|
},
|
graphic: {
|
elements: [
|
{
|
type: "circle",
|
shape: {
|
cx: "50%",
|
cy: "50%",
|
r: "35%",
|
},
|
style: {
|
fill: "transparent",
|
shadowBlur: 20,
|
shadowColor: "rgba(0, 191, 255, 0.4)",
|
opacity: 0.6,
|
},
|
z: -1,
|
},
|
],
|
},
|
animation: true,
|
tooltip: {
|
formatter: (params) => {
|
if (
|
params.seriesName !== "mouseoutSeries" &&
|
params.seriesName !== "pie2d"
|
) {
|
const series = option.series[params.seriesIndex];
|
if (series && series.pieData) {
|
return `${params.seriesName}<br/><span style="display:inline-block;margin-right:5px;border-radius:10px;width:10px;height:10px;background-color:${params.color};"></span>${series.pieData.value}`;
|
}
|
}
|
},
|
textStyle: {
|
fontSize: 12,
|
},
|
},
|
title: {
|
x: "center",
|
top: "20",
|
textStyle: {
|
color: "#fff",
|
fontSize: 12,
|
},
|
},
|
backgroundColor: "transparent",
|
labelLine: {
|
show: false,
|
lineStyle: {
|
color: "transparent",
|
},
|
},
|
label: {
|
show: false,
|
color: "transparent",
|
position: "outside",
|
formatter: "{b} \n{c} {d}%",
|
},
|
xAxis3D: {
|
min: -1,
|
max: 1,
|
},
|
yAxis3D: {
|
min: -1,
|
max: 1,
|
},
|
zAxis3D: {
|
min: -1,
|
max: 1,
|
},
|
grid3D: {
|
show: false,
|
boxHeight: 2,
|
bottom: "-10%",
|
left: "-15%",
|
viewControl: {
|
alpha: 18,
|
beta: 50,
|
distance: 115,
|
autoRotate: true,
|
rotateSensitivity: 0,
|
zoomSensitivity: 0,
|
panSensitivity: 0,
|
},
|
},
|
series: series,
|
};
|
|
this._pie.clear();
|
this._pie.setOption(option);
|
|
// 鼠标事件交互
|
let selectedIndex = "";
|
let hoveredIndex = "";
|
|
this._pie.on("click", (params) => {
|
if (params.seriesName === "mouseoutSeries") return;
|
const s = option.series[params.seriesIndex];
|
if (!s || !s.pieData || !s.pieStatus) return;
|
|
const isSelected = !s.pieStatus.selected;
|
const isHovered = s.pieStatus.hovered;
|
const k = s.pieStatus.k;
|
const startRatio = s.pieData.startRatio;
|
const endRatio = s.pieData.endRatio;
|
|
if (selectedIndex !== "" && selectedIndex !== params.seriesIndex) {
|
const prev = option.series[selectedIndex];
|
if (prev && prev.pieData && prev.pieStatus) {
|
prev.parametricEquation = this.getParametricEquation(
|
prev.pieData.startRatio,
|
prev.pieData.endRatio,
|
false,
|
false,
|
k,
|
prev.pieData.value
|
);
|
prev.pieStatus.selected = false;
|
}
|
}
|
|
s.parametricEquation = this.getParametricEquation(
|
startRatio,
|
endRatio,
|
isSelected,
|
isHovered,
|
k,
|
s.pieData.value
|
);
|
s.pieStatus.selected = isSelected;
|
if (isSelected) selectedIndex = params.seriesIndex;
|
|
this._pie.setOption(option);
|
});
|
|
this._pie.on("mouseover", (params) => {
|
if (hoveredIndex === params.seriesIndex) return;
|
|
// 取消之前的高亮
|
if (hoveredIndex !== "") {
|
let prev = option.series[hoveredIndex];
|
if (prev && prev.pieData && prev.pieStatus) {
|
const k = prev.pieStatus.k;
|
prev.parametricEquation = this.getParametricEquation(
|
prev.pieData.startRatio,
|
prev.pieData.endRatio,
|
prev.pieStatus.selected,
|
false,
|
k,
|
prev.pieData.value
|
);
|
prev.pieStatus.hovered = false;
|
hoveredIndex = "";
|
}
|
}
|
|
// 当前高亮(排除透明圆环)
|
if (params.seriesName !== "mouseoutSeries") {
|
let cur = option.series[params.seriesIndex];
|
if (cur && cur.pieData && cur.pieStatus) {
|
const k = cur.pieStatus.k;
|
cur.parametricEquation = this.getParametricEquation(
|
cur.pieData.startRatio,
|
cur.pieData.endRatio,
|
cur.pieStatus.selected,
|
true,
|
k,
|
cur.pieData.value
|
);
|
cur.pieStatus.hovered = true;
|
hoveredIndex = params.seriesIndex;
|
}
|
}
|
|
this._pie.setOption(option);
|
});
|
|
this._pie.on("globalout", () => {
|
if (hoveredIndex !== "") {
|
let prev = option.series[hoveredIndex];
|
if (prev && prev.pieData && prev.pieStatus) {
|
const k = prev.pieStatus.k;
|
prev.parametricEquation = this.getParametricEquation(
|
prev.pieData.startRatio,
|
prev.pieData.endRatio,
|
prev.pieStatus.selected,
|
false,
|
k,
|
prev.pieData.value
|
);
|
prev.pieStatus.hovered = false;
|
hoveredIndex = "";
|
this._pie.setOption(option);
|
}
|
}
|
});
|
},
|
|
// 生成扇形的曲面参数方程
|
getParametricEquation(
|
startRatio,
|
endRatio,
|
isSelected,
|
isHovered,
|
k,
|
height
|
) {
|
// 计算
|
let midRatio = (startRatio + endRatio) / 2;
|
|
let startRadian = startRatio * Math.PI * 2;
|
let endRadian = endRatio * Math.PI * 2;
|
let midRadian = midRatio * Math.PI * 2;
|
|
// 如果只有一个扇形,则不实现选中效果。
|
if (startRatio === 0 && endRatio === 1) {
|
isSelected = false;
|
}
|
|
// 通过扇形内径/外径的值,换算出辅助参数 k(默认值 1/3)
|
k = typeof k !== "undefined" ? k : 1 / 3;
|
|
// 计算选中效果分别在 x 轴、y 轴方向上的位移(未选中,则位移均为 0)
|
let offsetX = isSelected ? Math.cos(midRadian) * 0.1 : 0;
|
let offsetY = isSelected ? Math.sin(midRadian) * 0.1 : 0;
|
|
// 计算高亮效果的放大比例(未高亮,则比例为 1)
|
let hoverRate = isHovered ? 1.05 : 1;
|
|
// 返回曲面参数方程
|
return {
|
u: {
|
min: -Math.PI,
|
max: Math.PI * 3,
|
step: Math.PI / 32,
|
},
|
v: {
|
min: 0,
|
max: Math.PI * 2,
|
step: Math.PI / 20,
|
},
|
x: function (u, v) {
|
if (u < startRadian) {
|
return (
|
offsetX +
|
Math.cos(startRadian) * (1 + Math.cos(v) * k) * hoverRate
|
);
|
}
|
if (u > endRadian) {
|
return (
|
offsetX + Math.cos(endRadian) * (1 + Math.cos(v) * k) * hoverRate
|
);
|
}
|
return offsetX + Math.cos(u) * (1 + Math.cos(v) * k) * hoverRate;
|
},
|
y: function (u, v) {
|
if (u < startRadian) {
|
return (
|
offsetY +
|
Math.sin(startRadian) * (1 + Math.cos(v) * k) * hoverRate
|
);
|
}
|
if (u > endRadian) {
|
return (
|
offsetY + Math.sin(endRadian) * (1 + Math.cos(v) * k) * hoverRate
|
);
|
}
|
return offsetY + Math.sin(u) * (1 + Math.cos(v) * k) * hoverRate;
|
},
|
z: function (u, v) {
|
if (u < -Math.PI * 0.5) {
|
return Math.sin(u);
|
}
|
if (u > Math.PI * 2.5) {
|
return Math.sin(u);
|
}
|
return Math.sin(v) > 0 ? 0.6 * height : -0.6;
|
},
|
};
|
},
|
|
// 生成模拟 3D 饼图的配置项
|
getPie3D(pieData, internalDiameterRatio) {
|
let series = [];
|
let sumValue = 0;
|
let startValue = 0;
|
let endValue = 0;
|
let legendData = [];
|
let k =
|
typeof internalDiameterRatio !== "undefined"
|
? (1 - internalDiameterRatio) / (1 + internalDiameterRatio)
|
: 1 / 3;
|
|
// 为每一个饼图数据,生成一个 series-surface 配置
|
for (let i = 0; i < pieData.length; i++) {
|
sumValue += pieData[i].value;
|
|
let seriesItem = {
|
name:
|
typeof pieData[i].name === "undefined"
|
? `series${i}`
|
: pieData[i].name,
|
type: "surface",
|
parametric: true,
|
wireframe: {
|
show: false,
|
},
|
pieData: pieData[i],
|
pieStatus: {
|
selected: false,
|
hovered: false,
|
k: k,
|
},
|
};
|
|
if (typeof pieData[i].itemStyle != "undefined") {
|
let itemStyle = {};
|
|
typeof pieData[i].itemStyle.color != "undefined"
|
? (itemStyle.color = pieData[i].itemStyle.color)
|
: null;
|
typeof pieData[i].itemStyle.opacity != "undefined"
|
? (itemStyle.opacity = pieData[i].itemStyle.opacity)
|
: null;
|
|
seriesItem.itemStyle = itemStyle;
|
}
|
series.push(seriesItem);
|
}
|
|
// 使用上一次遍历时,计算出的数据和 sumValue,调用 getParametricEquation 函数,
|
// 向每个 series-surface 传入不同的参数方程 series-surface.parametricEquation,也就是实现每一个扇形。
|
for (let i = 0; i < series.length; i++) {
|
endValue = startValue + series[i].pieData.value;
|
console.log(series[i]);
|
series[i].pieData.startRatio = startValue / sumValue;
|
series[i].pieData.endRatio = endValue / sumValue;
|
series[i].parametricEquation = this.getParametricEquation(
|
series[i].pieData.startRatio,
|
series[i].pieData.endRatio,
|
false,
|
false,
|
k,
|
series[i].pieData.value
|
);
|
|
startValue = endValue;
|
|
legendData.push(series[i].name);
|
}
|
// series.push({
|
// name: 'mouseoutSeries',
|
// type: 'surface',
|
// parametric: true,
|
// wireframe: {
|
// show: false,
|
// },
|
// itemStyle: {
|
// opacity: 0.1,
|
// color: "#008fff",
|
// },
|
// parametricEquation: {
|
// u: {
|
// min: 0,
|
// max: Math.PI * 2,
|
// step: Math.PI / 20,
|
// },
|
// v: {
|
// min: 0,
|
// max: Math.PI,
|
// step: Math.PI / 20,
|
// },
|
// x: function (u, v) {
|
// return ((Math.sin(v) * Math.sin(u) + Math.sin(u)) / Math.PI) * 2.2;
|
// },
|
// y: function (u, v) {
|
// return ((Math.sin(v) * Math.cos(u) + Math.cos(u)) / Math.PI) * 2.2;
|
// },
|
// z: function (u, v) {
|
// return Math.cos(v) > 0 ? -7 : -7;
|
// },
|
// },
|
// });
|
return series;
|
},
|
handleClick() {
|
this.$refs.datePicker.focus();
|
},
|
// 初始化折线图
|
initRiskChart() {
|
const riskChart = echarts.init(document.getElementById('riskTrendChart'))
|
|
// 文本换行处理函数
|
const formatLabel = (text, maxWidth = 4) => {
|
if (!text) return ''
|
const textStr = String(text)
|
// 如果文本长度超过最大宽度,进行换行
|
if (textStr.length > maxWidth) {
|
let result = ''
|
for (let i = 0; i < textStr.length; i += maxWidth) {
|
result += textStr.substring(i, i + maxWidth) + '\n'
|
}
|
return result.trim()
|
}
|
return textStr
|
}
|
|
let option = {
|
tooltip: {
|
trigger: 'axis',
|
show: false
|
},
|
legend: {
|
show: false
|
},
|
grid: {
|
top: '5%',
|
bottom: '5%',
|
left: '5%',
|
right: '5%',
|
containLabel: true
|
},
|
xAxis: {
|
data: ['2025年1月检查', '2025年2月检查', '2025年3月检查', '2025年4月检查', '2025年5月检查'],
|
axisTick: { show: true, alignWithLabel: true },
|
axisLine: {
|
lineStyle: {
|
color: '#666'
|
}
|
},
|
axisLabel: {
|
color: '#fff',
|
fontSize: 12,
|
interval: 0, // 强制显示所有标签
|
rotate: 0, // 不旋转
|
formatter: function (value) {
|
// X轴标签格式化,最多7个字符,超出换行
|
return formatLabel(value, 7)
|
},
|
// 文本对齐方式
|
align: 'center',
|
verticalAlign: 'top',
|
lineHeight: 14,
|
// 设置标签的最大宽度
|
width: 60,
|
overflow: 'break' // 超出换行
|
}
|
},
|
yAxis: {
|
type: 'value',
|
splitLine: {
|
show: true,
|
lineStyle: {
|
type: 'dashed', // 虚线
|
color: 'rgba(255, 255, 255, .2)' // 固定灰色
|
}
|
},
|
axisLine: {
|
show: false
|
},
|
axisTick: {
|
show: false
|
},
|
axisLabel: {
|
color: '#fff',
|
fontSize: 12,
|
formatter: function (value) {
|
// Y轴标签格式化,最多3个字符,超出换行
|
const text = String(value)
|
return formatLabel(text, 3)
|
},
|
// 文本对齐方式
|
align: 'right',
|
verticalAlign: 'middle',
|
lineHeight: 14,
|
// 设置标签的最大宽度
|
width: 30,
|
overflow: 'break' // 超出换行
|
}
|
},
|
series: [
|
{
|
name: '风险等级',
|
type: 'line',
|
data: [10, 52, 20, 33, 39],
|
smooth: false, // 不使用圆滑过渡
|
showSymbol: false, // 不显示转折点
|
lineStyle: {
|
width: 5, // 线条宽度
|
color: {
|
type: 'linear',
|
x: 0,
|
y: 0,
|
x2: 1,
|
y2: 0,
|
colorStops: [
|
{ offset: 0, color: 'rgba(36, 81, 242, 1)' }, // 起始颜色
|
{ offset: 0.5, color: 'rgba(11, 204, 254, 1)' }, // 中间颜色
|
{ offset: 1, color: 'rgba(199, 160, 254, 1)' } // 结束颜色
|
]
|
}
|
}
|
}
|
]
|
}
|
riskChart.setOption(option)
|
},
|
toRiskTrend() {
|
this.$router.push({ path: '/subentry', query: { projectId: '212222', projectName: '新项目' } })
|
}
|
}
|
}
|
</script>
|
|
<style lang="less" scoped>
|
.a--center {
|
align-items: center;
|
}
|
|
.flex1 {
|
flex: 1;
|
}
|
|
.card-more {
|
width: 62px;
|
height: 24px;
|
background: rgba(14, 93, 251, 0.5);
|
border-radius: 4px;
|
padding: 6px 6px 6px 12px;
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
cursor: pointer;
|
|
span {
|
font-weight: 400;
|
font-size: 12px;
|
color: #ffffff;
|
line-height: 12px;
|
}
|
|
img {
|
width: 12px;
|
height: 12px;
|
}
|
}
|
|
.left-card {
|
width: 380px;
|
position: absolute;
|
top: 109px;
|
left: 20px;
|
|
.project-info {
|
padding: 14px;
|
height: 204px;
|
display: flex;
|
flex-direction: column;
|
justify-content: space-between;
|
|
.flex1 {
|
flex: 1;
|
}
|
|
.flex2 {
|
flex: 2;
|
}
|
|
.info-item {
|
display: flex;
|
}
|
|
.label {
|
font-weight: 500;
|
font-size: 12px;
|
color: #dcdfe6;
|
flex-shrink: 0;
|
width: 64px;
|
}
|
|
.dept {
|
width: 76px;
|
}
|
|
.time {
|
width: 100px;
|
}
|
|
.value {
|
font-weight: bold;
|
font-size: 12px;
|
color: #4cefe6;
|
max-width: 144px;
|
}
|
}
|
|
.equipment-info {
|
height: 154px;
|
position: relative;
|
|
.chart-bg {
|
width: 170px;
|
height: 104px;
|
position: absolute;
|
bottom: 0;
|
left: 0;
|
}
|
|
.pie-canvas {
|
position: absolute;
|
left: -37px;
|
bottom: 20px;
|
width: 362px;
|
height: 112px;
|
z-index: 2;
|
}
|
|
.legend {
|
position: absolute;
|
right: 23px;
|
bottom: 5px;
|
max-height: 126px; // 3个item的高度:(32px + 10px) * 3 = 126px
|
overflow-y: auto;
|
overflow-x: hidden;
|
z-index: 33;
|
|
// 自定义滚动条样式
|
&::-webkit-scrollbar {
|
width: 4px;
|
}
|
|
&::-webkit-scrollbar-track {
|
background: rgba(255, 255, 255, 0.1);
|
border-radius: 2px;
|
}
|
|
&::-webkit-scrollbar-thumb {
|
background: rgba(59, 130, 246, 0.6);
|
border-radius: 2px;
|
|
&:hover {
|
background: rgba(59, 130, 246, 0.8);
|
}
|
}
|
|
.legend-item {
|
display: flex;
|
margin-bottom: 10px;
|
|
width: 180px;
|
height: 32px;
|
background-image: url('../../../src/assets/img/project/equipmentBg.png');
|
background-size: 100% 100%;
|
|
position: relative;
|
align-items: center;
|
justify-content: space-between;
|
|
.dot {
|
width: 6px;
|
height: 6px;
|
margin-left: 5px;
|
flex-shrink: 0;
|
}
|
|
.legend-info {
|
width: 130px;
|
height: 32px;
|
padding: 7px 11px;
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
|
.legend-name {
|
max-width: 70px;
|
font-weight: 400;
|
font-size: 14px;
|
color: #CCE0F8;
|
overflow: hidden;
|
text-overflow: ellipsis;
|
white-space: nowrap; // 确保单行显示
|
cursor: pointer; // 鼠标悬停时显示手型
|
transition: color 0.3s ease;
|
|
&:hover {
|
color: #FFFFFF; // 悬停时文字变亮
|
}
|
}
|
|
.legend-number {
|
font-weight: bold;
|
font-size: 18px;
|
color: #1BDDFF;
|
flex-shrink: 0;
|
}
|
}
|
}
|
}
|
|
|
}
|
|
.danger-info {
|
height: 400px;
|
position: relative;
|
|
.info-header {
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
padding: 6px 17px 6px 38px;
|
|
.header-line {
|
display: flex;
|
align-items: center;
|
|
img {
|
width: 7px;
|
height: 10px;
|
margin-left: 2px;
|
}
|
}
|
|
span {
|
font-weight: 400;
|
font-size: 12px;
|
color: #ffffff;
|
}
|
}
|
|
.info-body {
|
height: 363px;
|
overflow-y: auto;
|
}
|
|
.info-item {
|
height: 30px;
|
padding: 0 17px 0 38px;
|
margin-top: 3px;
|
display: flex;
|
align-items: center;
|
|
&:nth-child(2n) {
|
background: rgba(14, 95, 255, 0.2);
|
}
|
|
.item-type {
|
width: 82px;
|
text-align: left;
|
font-weight: bold;
|
font-size: 12px;
|
color: #ffffff;
|
line-height: 18px;
|
}
|
|
.item-number {
|
flex: 1;
|
padding: 2px 12px;
|
display: flex;
|
flex-direction: column;
|
justify-content: space-between;
|
|
.number {
|
font-weight: 400;
|
font-size: 10px;
|
color: rgba(255, 255, 255, 1);
|
line-height: 15px;
|
}
|
|
span {
|
font-weight: 400;
|
font-size: 10px;
|
line-height: 15px;
|
color: rgba(255, 255, 255, 0.8);
|
}
|
|
.now {
|
width: 114px;
|
height: 8px;
|
background: linear-gradient(90deg, #ffad0e 0%, #fce003 100%);
|
border-radius: 20px;
|
margin-left: 8px;
|
}
|
|
.before {
|
width: 60px;
|
height: 8px;
|
background: linear-gradient(90deg, #008366 0%, #00ffce 100%);
|
border-radius: 20px;
|
margin-left: 8px;
|
}
|
}
|
|
.item-trend {
|
width: 53px;
|
text-align: right;
|
|
font-weight: 800;
|
font-size: 12px;
|
color: #66ffff;
|
line-height: 14px;
|
font-style: normal;
|
}
|
|
.down {
|
color: rgba(253, 66, 62, 1);
|
}
|
}
|
|
.info-bottom {
|
position: absolute;
|
bottom: 0;
|
left: 0;
|
width: 100%;
|
height: 32px;
|
background-image: url(../../assets/img/project/bottom.png);
|
background-size: 100% 100%;
|
}
|
}
|
|
.a--center {
|
align-items: center;
|
}
|
}
|
|
.mid-card {
|
position: absolute;
|
top: 133px;
|
left: 0;
|
right: 0;
|
margin: 0 auto;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
|
.mid-info {
|
cursor: pointer;
|
// width: 272px;
|
// height: 149px;
|
margin-right: 22px;
|
background: rgba(2, 0, 77, 0.8);
|
box-shadow: inset 0px 0px 34px 0px rgba(17, 40, 255, 0.66);
|
border: 2px solid #0d53b7;
|
padding: 12px 12px 12px 15px;
|
|
.mid-title {
|
cursor: pointer;
|
font-weight: bold;
|
font-size: 16px;
|
color: #ffffff;
|
line-height: 24px;
|
margin-right: 40px;
|
}
|
|
.tab-border {
|
border: 1px solid rgba(50, 197, 255, 0.22);
|
}
|
|
.mid-tab {
|
border-radius: 4px;
|
padding: 6px 5px;
|
font-weight: 400;
|
font-size: 12px;
|
color: #ffffff;
|
}
|
|
.active-tab {
|
background: #0600e4;
|
box-shadow: inset 0px 0px 20px 0px rgba(102, 255, 255, 0.52);
|
}
|
|
.mid-content {
|
margin-top: 11px;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
|
.mid-img {
|
width: 90px;
|
height: 90px;
|
margin-right: 20px;
|
background-image: url(../../assets/img/project/chart.png);
|
background-size: 100% 100%;
|
display: flex;
|
flex-direction: column;
|
align-items: center;
|
justify-content: center;
|
|
.number {
|
font-weight: 400;
|
font-size: 24px;
|
// color: linear-gradient(90deg, #66FFFF 0%, #F7B500 100%, #F7B500 100%);
|
line-height: 28px;
|
background: linear-gradient(90deg,
|
#66ffff 0%,
|
#f7b500 100%,
|
#f7b500 100%);
|
/*设置渐变的方向从左到右 颜色从ff0000到ffff00*/
|
-webkit-background-clip: text;
|
/*将设置的背景颜色限制在文字中*/
|
-webkit-text-fill-color: transparent;
|
/*给文字设置成透明*/
|
// background: linear-gradient(90deg, #66FFFF 0%, #F7B500 100%, #F7B500 100%);
|
}
|
|
.name {
|
font-weight: 500;
|
font-size: 10px;
|
color: #66ffff;
|
line-height: 15px;
|
}
|
}
|
|
.mid-item {
|
width: 100px;
|
height: 30px;
|
padding: 9px 10px;
|
background-image: url(../../assets/img/project/chartNum.png);
|
background-size: 100% 100%;
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
|
&:nth-child(2) {
|
margin-top: 16px;
|
}
|
|
.mid-txt {
|
font-weight: 400;
|
font-size: 12px;
|
color: #ffffff;
|
}
|
|
.mid-num {
|
font-weight: bold;
|
font-size: 20px;
|
color: #ffffff;
|
}
|
}
|
}
|
}
|
|
.mid-more {
|
width: 42px;
|
height: 150px;
|
box-sizing: border-box;
|
background: linear-gradient(90deg,
|
#040622 0%,
|
#0952cf 22.29%,
|
#155edf 53.43%,
|
#0952cf 81.31%,
|
#011845 100%);
|
border-radius: 96px;
|
border: 2px solid;
|
border-image: linear-gradient(90deg,
|
rgba(159, 255, 255, 0),
|
rgba(133, 255, 255, 1),
|
rgba(102, 255, 255, 0)) 3 1;
|
padding: 24px 12px;
|
font-weight: bold;
|
font-size: 17px;
|
color: #ffc500;
|
line-height: 24px;
|
text-shadow: 0px 0px 10px #612b00;
|
}
|
}
|
|
.right-card {
|
width: 462px;
|
position: absolute;
|
top: 109px;
|
right: 20px;
|
|
.people-info {
|
height: 280px;
|
padding: 18px 32px 20px 32px;
|
display: flex;
|
flex-direction: column;
|
justify-content: space-between;
|
|
.people-card {
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
}
|
|
.people-card2 {
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
}
|
|
.people-item {
|
display: flex;
|
flex-direction: column;
|
align-items: center;
|
|
.people-chart {
|
width: 82px;
|
height: 82px;
|
margin-bottom: 7px;
|
background-image: url(../../assets/img/project/peopleChart.png);
|
background-size: 100% 100%;
|
position: relative;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
|
.progress-card {
|
position: absolute;
|
top: 0;
|
left: 0;
|
width: 100%;
|
height: 100%;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
}
|
|
.progress-num {
|
font-weight: 800;
|
font-size: 18px;
|
color: #66ffff;
|
}
|
|
.progress-unit {
|
font-weight: 800;
|
font-size: 12px;
|
color: #66ffff;
|
margin-left: 2px;
|
}
|
}
|
|
.people-name {
|
font-weight: 500;
|
font-size: 14px;
|
color: #ffffff;
|
line-height: 15px;
|
}
|
}
|
}
|
|
.risk-info {
|
height: 244px;
|
padding: 11px 33px 11px 33px;
|
|
.card1 {
|
width: 60px;
|
}
|
|
.card2 {
|
width: 107px;
|
}
|
|
.label {
|
font-weight: 400;
|
font-size: 12px;
|
color: #FFFFFF;
|
text-align: center;
|
}
|
|
.card4 {
|
width: 72px;
|
text-align: left;
|
}
|
|
.risk-top {
|
padding: 6px 0;
|
display: flex;
|
align-items: center;
|
margin-bottom: 7px;
|
}
|
|
.risk-body {
|
.risk-item {
|
display: flex;
|
align-items: stretch;
|
|
&:nth-last-child(1) {
|
// border-right: 1px solid #337AB6;
|
|
.label {
|
border-bottom: 1px solid #337AB6;
|
}
|
}
|
|
.label {
|
&:nth-last-child(1) {
|
border-right: 1px solid #337AB6;
|
border-left: unset !important;
|
padding-left: 6px;
|
font-weight: bold;
|
font-size: 12px;
|
color: #66FFFF;
|
font-style: normal;
|
}
|
|
line-height: 36px;
|
border-top: 1px solid #337AB6;
|
border-left: 1px solid #337AB6;
|
position: relative;
|
|
&.merged-cell {
|
position: relative;
|
|
.merged-content {
|
position: absolute;
|
top: 0;
|
left: 0;
|
width: 100%;
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
// border-bottom: 1px solid #337AB6;
|
}
|
}
|
|
&.hidden-cell {
|
border-top: none;
|
// border-left: none;
|
}
|
}
|
}
|
|
}
|
|
}
|
|
.risk-trend {
|
height: 234px;
|
}
|
|
.risk-picker {
|
width: 190px;
|
height: 26px;
|
background: rgba(14, 93, 251, 0.5);
|
border-radius: 4px;
|
position: relative;
|
display: flex;
|
align-items: center;
|
|
::v-deep .el-input__icon {
|
display: none;
|
}
|
|
::v-deep .el-input__inner {
|
background: transparent;
|
border: none;
|
color: transparent;
|
}
|
|
::v-deep .el-range-input {
|
background: transparent;
|
border: none;
|
color: transparent;
|
}
|
|
.risk-ab {
|
position: absolute;
|
top: 0;
|
left: 0;
|
width: 100%;
|
height: 100%;
|
padding: 6px 26px 6px 10px;
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
|
}
|
|
.pick-text {
|
|
font-weight: 400;
|
font-size: 12px;
|
color: #FFFFFF;
|
line-height: 18px;
|
}
|
|
.pick-img {
|
width: 12px;
|
height: 12px;
|
margin-right: 12px;
|
}
|
}
|
|
}
|
</style>
|