Commit a7dce4c0 by 柏宁宁

合并分支 'renyuan-oee' 到 'master'

荏原 新增oee 时间利用率报表 个性化需求

查看合并请求 !19
parents f8f15897 a9d775ec
...@@ -31,6 +31,14 @@ class RokeMesThreeColourLight(http.Controller): ...@@ -31,6 +31,14 @@ class RokeMesThreeColourLight(http.Controller):
template = env.get_template('oee_analysis.html') template = env.get_template('oee_analysis.html')
return template.render(data) return template.render(data)
@http.route("/roke/three_color_light/oee_time_sequence_table", type="http", auth='none', cors='*', csrf=False)
def oee_time_sequence_table(self, **kwargs):
_self = http.request
factory_code = http.request.env(user=SUPERUSER_ID)['ir.config_parameter'].get_param('database.uuid', default="")
data = {"code": 1, "message": "请求通过", "data": {"factory_code": factory_code}}
template = env.get_template('oee_time_sequence_table.html')
return template.render(data)
@http.route('/roke/workstation/plant/tree', type='json', auth='none', csrf=False, cors="*") @http.route('/roke/workstation/plant/tree', type='json', auth='none', csrf=False, cors="*")
def get_roke_workstation_plant(self): def get_roke_workstation_plant(self):
_self = http.request _self = http.request
......
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>OEE时间利用率</title>
<meta
content="width=device-width,initial-scale=1.0, maximum-scale=1.0,user-scalable=0"
name="viewport"
/>
<!-- 本地资源引用 -->
<link rel="stylesheet" href="/roke_workstation_api/static/html/routing/element-ui/index.css" />
<script src="/roke_workstation_api/static/html/routing/js/echarts.min.js"></script>
<script src="/roke_workstation_api/static/html/routing/js/moment.min.js"></script>
<script src="/roke_workstation_api/static/html/routing/js/vue.js"></script>
<script src="/roke_workstation_api/static/html/routing/js/axios.min.js"></script>
<script src="/roke_workstation_api/static/html/routing/element-ui/index.js"></script>
</head>
<body id="bodyId" style="display: none">
<div id="app">
<!-- v-show="performanceShow" -->
<!-- v-loading.body.fullscreen.lock="idle_rate_loading" -->
<!-- 新增:多设备空闲率图表 -->
<!-- 多设备图表的设备选择器 -->
<div class="chart-select-wrapper">
<span class="select-label">设备</span>
<el-select
v-model="selectedMultiDevice"
placeholder="请选择设备"
size="small"
clearable
@change="multiDeviceChange"
class="chart-select"
>
<el-option
v-for="(item,index) in deviceList"
:label="item.name"
:value="item.code"
:key="index"
>
</el-option>
</el-select>
</div>
<div class="multi-utilization-chart glass-effect">
<div class="section-title">
<div class="title-bar"></div>
<span class="title-text"
>[[ selectedMultiDevice ? '设备空闲率概览' : '多设备空闲率对比' ]]</span
>
</div>
<div
id="multiUtilizationChart"
ref="multiUtilizationChart"
style="width: 100%; height: 280px"
></div>
</div>
<!-- 单设备图表的设备选择器 -->
<div class="chart-select-wrapper">
<span class="select-label">设备</span>
<el-select
v-model="selectedDevice"
placeholder="请选择设备"
size="small"
clearable
@change="selDeviceChange"
class="chart-select"
>
<el-option
v-for="(item,index) in deviceList"
:label="item.name"
:value="item.code"
:key="index"
>
</el-option>
</el-select>
</div>
<div class="utilization-chart glass-effect" v-if="selectedDevice">
<div class="section-title">
<div class="title-bar"></div>
<span class="title-text">设备空闲率</span>
</div>
<div id="utilizationChart" ref="utilizationChart" style="width: 100%; height: 280px"></div>
</div>
<!-- 底部状态图 -->
<!-- v-loading.body.fullscreen.lock="chart_loading" -->
<!-- 柱状图的设备选择器 -->
<div class="chart-select-wrapper">
<span class="select-label">设备</span>
<el-select
v-model="selectedBarChartDevice"
placeholder="请选择设备"
size="small"
@change="barChartDeviceChange"
class="chart-select"
>
<el-option
v-for="(item,index) in deviceList"
:label="item.name"
:value="item.code"
:key="index"
>
</el-option>
</el-select>
</div>
<div class="status-chart glass-effect">
<div class="section-title">
<div class="title-bar"></div>
<span class="title-text">日均运行时间统计</span>
</div>
<div class="chart-container">
<!-- 图例区域 -->
<div class="flex_legend">
<div class="single_sty">
<div style="background-color: red"></div>
<span>故障</span>
</div>
<div class="single_sty">
<div style="background-color: yellow"></div>
<span>等待</span>
</div>
<div class="single_sty">
<div style="background-color: green"></div>
<span>运行</span>
</div>
<div class="single_sty">
<div style="background-color: gray"></div>
<span>关机</span>
</div>
</div>
<!-- Y轴刻度 -->
<div class="y-axis">
<span v-for="(value, index) in yAxisValues" :key="index" class="y-axis-label">
[[value]]
<span class="unit">%</span>
</span>
</div>
<!-- 图表主体区域 -->
<div class="chart-content">
<div class="horizontal-lines">
<div v-for="(value, index) in yAxisValues" :key="index" class="horizontal-line"></div>
</div>
<!-- 改为单个容器包含柱状图和X轴标签 -->
<div class="columns-container">
<div
class="column-with-label"
v-for="(item, dayIndex) in pickingOrderList"
:key="dayIndex"
>
<!-- 柱状图部分 -->
<div class="column-group">
<div class="column-stack">
<div
class="column-segment"
v-for="(segment, stackIndex) in item"
:key="stackIndex"
:style="{
'height': calculateHeight(segment.duration_percentage) + '%',
'background-color': segment.state,
'margin-top': '0px'
}"
>
<el-tooltip
effect="dark"
:content="calculateHeight(segment.duration_percentage) + '%'"
placement="top"
>
<div style="height: 100%; width: 100%"></div>
</el-tooltip>
</div>
</div>
</div>
<!-- X轴标签部分 - 直接绑定在柱状图下方 -->
<div class="x-axis-label">[[ latestDateList[dayIndex] ]]</div>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
<script>
// 发送消息给父页面(关闭odoo的菜单弹窗)
document.addEventListener("click", () => {
window.parent.postMessage("hidePopover", "*");
});
let vue = new Vue({
el: "#app",
delimiters: ["[[", "]]"], // 替换原本vue的[[ key ]]取值方式(与odoo使用的jinja2冲突问题)
data() {
return {
windowHeight: window.innerHeight, // 窗口高度
// baseURL: "https://workstation.rokeris.com", // 基地址 https://workstation.rokeris.com
// baseURL: "http://qdry.dws.rokecloud.com",
baseURL: "", //
localURL: "https://dws-platform.xbg.rokeris.com/dev-api", // 基地址 https://workstation.rokeris.com
idle_rate_loading: false, // 加载效果
chart_loading: false,
deviceList: [], // 设备列表
selectedDevice: null, // 选中的设备
yAxisValues: ["100", "80", "60", "40", "20", "0"], // Y轴刻度值
pickingOrderList: [], // 拣选单列表
dateList: [], // 日期列表
start_time: "", // 开始时间
end_time: "", // 结束时间
utilizationChart: null, // 设备空闲率图表实例变量
multiUtilizationChart: null, // 新增:多设备空闲率图表实例变量
plan_time_list: null,
latestDateList: [],
factory_code: "8d8dec6e-0d44-11f0-9692-00163e04c506",
selectedMultiDevice: null, // 新增:多设备图表选中的设备
selectedBarChartDevice: null, // 新增:柱状图选中的设备
};
},
computed: {
// 选中设备的信息
selDeviceInfo() {
return this.deviceList.find((item) => item.id == this.selectedDevice);
},
},
created() {
if (this.getUrlSearch("factory_code")) {
this.factory_code = this.getUrlSearch("factory_code"); //截取url后面的参数
}
this.get_device_list();
// 先设置好日期范围,以便后续使用
this.latestDateList = this.getLastTenDays();
this.$nextTick(() => {
document.getElementById("bodyId").style.display = "block";
// 在DOM渲染后初始化图表
if (this.$refs.utilizationChart) {
this.initUtilizationChart();
}
// 初始化多设备空闲率图表
if (this.$refs.multiUtilizationChart) {
this.initMultiUtilizationChart();
}
});
window.addEventListener("resize", this.handleResize);
// 获取最后指定天数的日期
this.dateList = this.getLastAssignDays();
},
methods: {
// 获取所有已绑定设备数据
async getAllEquipmentData(data_list) {
try {
// 发送请求获取所有已绑定设备数据
const response = await axios({
method: "post",
url: this.baseURL + "/roke/workstation/equipment/get_equipment_data",
data: {},
headers: {
"Content-Type": "application/json",
},
});
// 处理JSON-RPC格式的响应
if (response.data && response.data.result && response.data.result.code === 0) {
// 存储所有设备数据
data_list.forEach((data_item) => {
response.data.result.data.forEach((item) => {
if (data_item.device_code == item.code) {
this.deviceList.push(item);
}
});
});
// 为柱状图设置默认设备
this.selectedBarChartDevice = this.deviceList[0].code;
this.get_plan_time();
this.get_daily_running_time();
console.log("获取到所有已绑定设备数据:", this.deviceList.length, "条");
} else {
const errorMsg = response.data.result
? response.data.result.message
: "获取所有已绑定设备数据失败";
throw new Error(errorMsg);
}
} catch (error) {
console.error("获取所有已绑定设备数据出错:", error);
throw error;
}
},
// 获取日均运行时间统计
get_daily_running_time() {
this.idle_rate_loading = true;
axios({
method: "post",
url: this.localURL + "/public/device_efficiency/daily_running_time",
data: {
device_code: this.selectedBarChartDevice,
},
headers: { "Content-Type": "application/json" },
})
.then((res) => {
if (res.data.code == 200) {
this.pickingOrderList = res.data.data.daily_running_list;
// 调用一致性检查
this.ensureAlignmentData();
} else {
this.$message.error("获取日均运行时间失败");
}
this.idle_rate_loading = false;
})
.catch((err) => {
this.idle_rate_loading = false;
this.$message.error("获取日均运行时间接口捕获到错误");
});
},
// 获取设备列表
get_device_list() {
axios({
method: "get",
url: this.localURL + "/public/device_efficiency/device_list/" + this.factory_code,
data: {},
headers: { "Content-Type": "application/json" },
})
.then((res) => {
if (res.data.code == 200) {
// this.deviceList = res.data.data
this.getAllEquipmentData(res.data.data);
} else {
this.$message.error("设备列表数据获取失败!");
}
})
.catch((err) => {});
},
// 获取设备编号 设备计划运行时间 plan_time_list 参数
get_plan_time() {
axios({
method: "post",
url: this.baseURL + "/roke/workstation/equipment/get_plan_time",
data: {},
headers: { "Content-Type": "application/json" },
})
.then((res) => {
if (res.data.result.code == 0) {
this.plan_time_list = res.data.result.data;
// 只有在选择了设备时才调用单设备空闲率接口
if (this.selectedDevice) {
this.get_series_utilization_rate();
}
// 如果多设备图表已初始化且没有选择设备,则调用多设备接口
if (this.multiUtilizationChart && !this.selectedMultiDevice) {
this.get_series_utilization_rate_top_5();
}
}
})
.catch((err) => {});
},
// 获取OEE报表:近十天设备空闲率
get_series_utilization_rate() {
this.chart_loading = true;
axios({
method: "post",
url: this.localURL + "/public/device_efficiency/series_utilization_rate",
data: {
device_code: this.selectedDevice,
plan_time_list: this.plan_time_list,
},
headers: { "Content-Type": "application/json" },
})
.then((res) => {
if (res.data.code == 200) {
this.initUtilizationChart(res.data.data);
} else {
this.$message.error("设备空闲率获取失败!");
}
this.chart_loading = false;
})
.catch((err) => {
this.chart_loading = false;
this.$message.error("设备空闲率捕获到错误!");
});
},
// 选择设备改变事件
selDeviceChange(el) {
// 只有在选择了设备时才调用接口
if (this.selectedDevice) {
this.get_series_utilization_rate();
}
},
// 处理窗口大小变化修改图表大小
handleResize() {
if (this.utilizationChart) this.utilizationChart.resize();
if (this.multiUtilizationChart) this.multiUtilizationChart.resize();
},
// 初始化设备空闲率图表
initUtilizationChart(data = []) {
// 延迟初始化,确保DOM已完全渲染
setTimeout(() => {
// 确保元素存在
const chartDom = document.getElementById("utilizationChart");
if (!chartDom) {
console.error("找不到utilizationChart元素");
return;
}
// 如果已有实例,先销毁
if (this.utilizationChart) {
this.utilizationChart.dispose();
}
// 初始化图表
this.utilizationChart = echarts.init(chartDom);
const option = {
grid: {
left: "3%",
right: "4%",
bottom: "3%",
top: "8%",
containLabel: true,
},
tooltip: {
trigger: "axis",
formatter: "{b} : {c}%",
},
xAxis: {
type: "category",
data: this.latestDateList,
axisLine: {
lineStyle: {
color: "#fff",
},
},
axisLabel: {
color: "#fff",
rotate: this.latestDateList[0].length > 5 ? 30 : 0, // 如果日期文本较长则旋转标签
},
},
yAxis: {
type: "value",
min: 0,
max: 100,
interval: 20,
axisLine: {
show: false,
},
axisTick: {
show: false,
},
splitLine: {
lineStyle: {
color: "rgba(255, 255, 255, 0.1)",
},
},
axisLabel: {
color: "#fff",
formatter: "{value}%",
},
},
series: [
{
name: "设备空闲率",
type: "line",
smooth: true,
symbol: "circle",
symbolSize: 8,
label: {
show: true,
position: "top",
color: "#FFFFFF",
fontSize: 12,
formatter: "{c}%",
},
lineStyle: {
color: "#36cfc9",
width: 3,
},
itemStyle: {
color: "#36cfc9",
},
data: data.map((item) => (item > 100 ? 100 : item)),
},
],
};
this.utilizationChart.setOption(option);
// 手动触发resize以确保正确渲染
this.utilizationChart.resize();
}, 300); // 延迟300ms初始化
},
// 计算高度百分比
calculateHeight(hours) {
// 直接使用百分比值
// yAxisValues = ["100", "80", "60", "40", "20", "0"],每个刻度区间是20%
// 确保高度在0-100%之间
return Math.min(Math.max(hours, 0), 100);
},
// 获取最后指定天数的日期
getLastAssignDays(num = 10) {
const dates = [];
for (let i = num - 1; i >= 0; i--) {
const date = new Date();
date.setDate(date.getDate() - i);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
dates.push(`${year}-${month}-${day}`);
}
this.start_time = dates[0]; // 第一天
this.end_time = dates[dates.length - 1]; // 最后一天
return dates;
},
// 改进日期格式化方法
getLastTenDays() {
const dates = [];
const formattedDates = [];
const endDate = new Date();
this.end_time = moment(endDate).format("YYYY-MM-DD");
const startDate = new Date();
startDate.setDate(startDate.getDate() - 9);
this.start_time = moment(startDate).format("YYYY-MM-DD");
for (let i = 0; i < 10; i++) {
const date = new Date(startDate);
date.setDate(startDate.getDate() + i);
dates.push(date);
// 修改为MM-DD格式
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
formattedDates.push(`${month}-${day}`);
}
this.dateList = dates;
return formattedDates;
},
// 在methods中添加
ensureAlignmentData() {
// 确保X轴标签数量与柱状图数量一致
this.$nextTick(() => {
if (this.pickingOrderList.length !== this.latestDateList.length) {
console.warn(
"柱状图数量与X轴标签数量不一致",
"柱状图:",
this.pickingOrderList.length,
"X轴标签:",
this.latestDateList.length
);
}
});
},
// 通过网址跳转过来的页面,截取后面的参数
getUrlSearch(name) {
// 未传参,返回空
if (!name) return "";
// 查询参数:先通过search取值,如果取不到就通过hash来取
var after = window.location.search;
after = after.substr(1) || window.location.hash.split("?")[1];
// 地址栏URL没有查询参数,返回空
if (!after) return null;
// 如果查询参数中没有"name",返回空
if (after.indexOf(name) === -1) return null;
var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
// 当地址栏参数存在中文时,需要解码,不然会乱码
var r = decodeURI(after).match(reg);
// 如果url中"name"没有值,返回空
if (!r) return null;
return r[2];
},
// 新增:初始化多设备空闲率图表
initMultiUtilizationChart() {
setTimeout(() => {
const chartDom = document.getElementById("multiUtilizationChart");
if (!chartDom) {
console.error("找不到multiUtilizationChart元素");
return;
}
if (this.multiUtilizationChart) {
this.multiUtilizationChart.dispose();
}
this.multiUtilizationChart = echarts.init(chartDom);
// 根据selectedMultiDevice来决定显示内容
if (!this.selectedMultiDevice) {
this.showMultiDeviceChart();
} else {
this.showSingleDeviceChart();
}
}, 300);
},
// 新增:显示多设备图表
showMultiDeviceChart() {
// 调用真实的多设备接口
this.get_series_utilization_rate_top_5();
},
// 新增:显示单设备图表
showSingleDeviceChart() {
// 这里复用现有的空闲率接口逻辑
this.get_series_utilization_rate_for_multi();
},
// 新增:为多设备图表获取单设备空闲率数据
get_series_utilization_rate_for_multi() {
axios({
method: "post",
url: this.localURL + "/public/device_efficiency/series_utilization_rate",
data: {
device_code: this.selectedMultiDevice,
plan_time_list: this.plan_time_list,
},
headers: { "Content-Type": "application/json" },
})
.then((res) => {
if (res.data.code == 200) {
this.updateMultiChartWithSingleDevice(res.data.data);
} else {
this.$message.error("多设备图表-设备空闲率获取失败!");
}
})
.catch((err) => {
this.$message.error("多设备图表-设备空闲率捕获到错误!");
});
},
// 新增:用单设备数据更新多设备图表
updateMultiChartWithSingleDevice(data) {
const selectedDeviceInfo = this.deviceList.find(
(device) => device.code === this.selectedMultiDevice
);
const deviceName = selectedDeviceInfo ? selectedDeviceInfo.name : "当前设备";
const option = {
grid: {
left: "3%",
right: "4%",
bottom: "3%",
top: "8%",
containLabel: true,
},
tooltip: {
trigger: "axis",
formatter: "{b} : {c}%",
},
legend: {
show: false,
},
xAxis: {
type: "category",
data: this.latestDateList,
axisLine: {
lineStyle: {
color: "#fff",
},
},
axisLabel: {
color: "#fff",
rotate: this.latestDateList[0].length > 5 ? 30 : 0,
},
},
yAxis: {
type: "value",
min: 0,
max: 100,
interval: 20,
axisLine: {
show: false,
},
axisTick: {
show: false,
},
splitLine: {
lineStyle: {
color: "rgba(255, 255, 255, 0.1)",
},
},
axisLabel: {
color: "#fff",
formatter: "{value}%",
},
},
series: [
{
name: deviceName,
type: "line",
smooth: true,
symbol: "circle",
symbolSize: 8,
label: {
show: true,
position: "top",
color: "#FFFFFF",
fontSize: 12,
formatter: "{c}%",
},
lineStyle: {
color: "#36cfc9",
width: 3,
},
itemStyle: {
color: "#36cfc9",
},
data: data.map((item) => (item > 100 ? 100 : item)),
},
],
};
// 使用 setOption 的第二个参数 true 来完全替换配置,清除之前的多设备折线
this.multiUtilizationChart.setOption(option, true);
this.multiUtilizationChart.resize();
},
// 新增:更新多设备图表
updateMultiUtilizationChart() {
if (!this.multiUtilizationChart) return;
if (this.selectedMultiDevice) {
this.showSingleDeviceChart();
} else {
this.showMultiDeviceChart();
}
},
// 新增:多设备图表设备选择变化事件
multiDeviceChange(el) {
this.updateMultiUtilizationChart();
},
// 新增:柱状图设备选择变化事件
barChartDeviceChange(el) {
this.get_daily_running_time();
},
// 新增:获取多设备空闲率数据(Top 5)
get_series_utilization_rate_top_5() {
// 构造设备列表参数
const device_code_list = this.deviceList.map((device) => ({
device_name: device.name,
device_code: device.code,
}));
axios({
method: "post",
url: this.localURL + "/public/device_efficiency/series_utilization_rate_top_5",
data: {
device_code_list: device_code_list,
plan_time_list: this.plan_time_list || {},
},
headers: { "Content-Type": "application/json" },
})
.then((res) => {
if (res.data.code == 200) {
this.updateMultiChartWithTopDevices(res.data.data);
} else {
this.$message.error("多设备空闲率获取失败!");
}
})
.catch((err) => {
this.$message.error("多设备空闲率接口捕获到错误!");
console.error("多设备空闲率接口错误:", err);
});
},
// 新增:用多设备数据更新图表
updateMultiChartWithTopDevices(data) {
if (!data.device_data || !Array.isArray(data.device_data)) {
console.error("多设备数据格式错误");
return;
}
const series = data.device_data.map((device) => ({
name: device.device_name,
type: "line",
smooth: true,
symbol: "circle",
symbolSize: 6,
lineStyle: {
width: 2,
},
data: device.utilization_rates.map((item) => (item > 100 ? 100 : item)),
}));
const option = {
grid: {
left: "3%",
right: "4%",
bottom: "3%",
top: "15%",
containLabel: true,
},
tooltip: {
trigger: "axis",
formatter: function (params) {
let result = params[0].axisValue + "<br/>";
params.forEach((param) => {
result += param.marker + param.seriesName + ": " + param.value + "%<br/>";
});
return result;
},
},
legend: {
top: "5%",
textStyle: {
color: "#fff",
},
},
xAxis: {
type: "category",
data: this.latestDateList,
axisLine: {
lineStyle: {
color: "#fff",
},
},
axisLabel: {
color: "#fff",
rotate: this.latestDateList[0].length > 5 ? 30 : 0,
},
},
yAxis: {
type: "value",
min: 0,
max: 100,
interval: 20,
axisLine: {
show: false,
},
axisTick: {
show: false,
},
splitLine: {
lineStyle: {
color: "rgba(255, 255, 255, 0.1)",
},
},
axisLabel: {
color: "#fff",
formatter: "{value}%",
},
},
series: series,
color: ["#36cfc9", "#1890ff", "#722ed1", "#eb2f96", "#fa8c16", "#52c41a", "#faad14"],
};
// 使用 setOption 的第二个参数 true 来完全替换配置,确保正确切换
this.multiUtilizationChart.setOption(option, true);
this.multiUtilizationChart.resize();
},
},
beforeDestroy() {
// 页面销毁移除resize事件监听
window.removeEventListener("resize", this.handleResize);
// 清理图表实例
if (this.utilizationChart) {
this.utilizationChart.dispose();
}
if (this.multiUtilizationChart) {
this.multiUtilizationChart.dispose();
}
},
});
</script>
<style>
/* 基础样式 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background-color: #0c1217;
background-size: cover;
color: #fff;
overflow: hidden;
}
#app {
/* width: 100vw; */
height: 100vh;
padding: 20px;
display: flex;
flex-direction: column;
gap: 10px;
overflow-y: auto;
}
/* 玻璃态效果 */
.glass-effect {
background: rgba(6, 82, 158, 0.65);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 15px;
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.37);
}
.section-title {
position: relative;
margin-bottom: 20px;
display: flex;
align-items: center;
justify-content: flex-start;
padding-left: 20px;
}
.title-text {
font-size: 16px;
font-weight: 500;
color: #fff;
}
.title-bar {
width: 4px;
height: 18px;
background-color: #1890ff;
margin-right: 8px;
border-radius: 2px;
}
.el-progress__text {
white-space: pre-wrap;
line-height: 1.5;
}
/* 底部状态图样式 */
.status-chart {
padding: 20px;
height: auto;
min-height: 500px;
position: relative;
margin-bottom: 10px;
}
.chart-header {
display: flex;
align-items: center;
gap: 20px;
margin: 10px 0;
}
.chart-container {
width: 100%;
margin: 0 auto;
position: relative;
padding: 10px 40px;
height: 360px;
}
.columns-container {
position: absolute;
top: 0;
left: 60px;
width: calc(100% - 70px);
height: 320px;
display: flex;
justify-content: space-between;
padding: 0 10px;
}
.column-group {
width: 30px;
height: 280px;
display: flex;
flex-direction: column;
align-items: center;
}
.column-stack {
width: 100%;
height: 100%;
display: flex;
flex-direction: column-reverse;
background-color: transparent;
}
/* Y轴样式 */
.y-axis {
position: absolute;
height: 280px;
width: 50px;
z-index: 2;
}
.y-axis-label {
position: absolute;
left: 10px;
font-size: 14px;
font-weight: normal;
text-align: right;
transform: translateY(-50%);
white-space: nowrap;
}
/* 水平网格线调整 */
.horizontal-lines {
position: absolute;
width: 100%;
height: 280px;
top: 0;
left: 0;
}
.horizontal-line {
position: absolute;
width: 100%;
height: 1px;
background-color: rgba(255, 255, 255, 0.1);
left: 0;
}
/* 精确调整Y轴刻度和水平线位置 */
.y-axis-label:nth-child(1),
.horizontal-line:nth-child(1) {
top: 0;
}
/* 100% */
.y-axis-label:nth-child(2),
.horizontal-line:nth-child(2) {
top: 56px;
}
/* 80% */
.y-axis-label:nth-child(3),
.horizontal-line:nth-child(3) {
top: 112px;
}
/* 60% */
.y-axis-label:nth-child(4),
.horizontal-line:nth-child(4) {
top: 168px;
}
/* 40% */
.y-axis-label:nth-child(5),
.horizontal-line:nth-child(5) {
top: 224px;
}
/* 20% */
.y-axis-label:nth-child(6),
.horizontal-line:nth-child(6) {
top: 280px;
}
/* 0% */
/* X轴样式调整 */
.x-axis {
display: none;
}
.grid-area {
width: 100%;
height: 280px;
position: relative;
overflow: visible;
}
/* 图例样式优化 */
.flex_legend {
display: flex;
justify-content: center;
align-items: center;
gap: 20px;
margin-bottom: 15px;
}
.single_sty {
display: flex;
align-items: center;
}
.single_sty div {
width: 20px;
height: 10px;
margin-right: 8px;
border-radius: 2px;
}
.single_sty span {
color: #fff;
font-size: 14px;
}
.chart-content {
position: relative;
width: calc(100% - 60px);
height: 280px;
margin-top: 10px;
margin-left: 60px;
}
.unit {
font-size: 12px;
margin-left: 4px;
color: rgba(255, 255, 255, 0.7);
}
/* 动画效果 */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.status-chart {
animation: fadeIn 0.8s ease-out;
}
/* Element UI 样式覆盖 */
.el-select {
width: 180px;
}
.chart-header h4 {
font-size: 14px;
color: #fff;
margin: 0;
}
/* Element UI 深色主题样式覆盖 */
.el-select {
width: 180px;
}
/* 输入框样式 */
.el-select .el-input__inner {
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
color: #fff;
}
/* 下拉图标颜色 */
.el-select .el-input__icon {
color: #fff;
}
/* 下拉面板样式 */
.el-select-dropdown {
background: rgba(31, 45, 61, 0.95);
border: 1px solid rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
}
/* 下拉选项样式 */
.el-select-dropdown__item {
color: #fff;
}
/* 下拉选项悬停样式 */
.el-select-dropdown__item.hover,
.el-select-dropdown__item:hover {
background-color: rgba(64, 158, 255, 0.2);
}
/* 选中项样式 */
.el-select-dropdown__item.selected {
color: #409eff;
background-color: rgba(64, 158, 255, 0.1);
}
/* 禁用项样式 */
.el-select-dropdown__item.is-disabled {
color: rgba(255, 255, 255, 0.3);
}
/* 聚焦时的边框颜色 */
.el-select .el-input.is-focus .el-input__inner {
border-color: #409eff;
}
/* 选择框占位符颜色 */
.el-select .el-input__inner::placeholder {
color: rgba(255, 255, 255, 0.5);
}
/* 在样式部分添加以下代码 */
.utilization-chart {
width: 100% !important;
padding: 15px 20px;
margin-bottom: 15px;
}
.utilization-chart .section-title {
display: flex;
align-items: center;
margin-bottom: 15px;
}
.utilization-chart .title-bar {
width: 4px;
height: 18px;
background-color: #1890ff;
margin-right: 8px;
border-radius: 2px;
}
.utilization-chart .title-text {
font-size: 16px;
font-weight: 500;
color: #fff;
}
/* 新增单元结构样式 */
.column-with-label {
display: flex;
flex-direction: column;
align-items: center;
width: 40px;
/* 稍微宽一点,给标签留足空间 */
}
/* 调整柱状图容器样式 */
.columns-container {
position: absolute;
top: 0;
left: 60px;
width: calc(100% - 70px);
height: 320px;
/* 增加高度,包含X轴标签 */
display: flex;
justify-content: space-between;
padding: 0 10px;
}
/* 柱状图组样式 */
.column-group {
width: 40px;
height: 280px;
/* 固定高度为图表区域高度 */
display: flex;
flex-direction: column;
align-items: center;
}
/* X轴标签样式 */
.x-axis-label {
margin-top: 10px;
font-size: 14px;
text-align: center;
width: 100%;
}
/* 移除原有X轴容器样式 */
.x-axis {
display: none;
}
/* 新增:多设备空闲率图表样式 */
.multi-utilization-chart {
width: 100% !important;
padding: 15px 20px;
margin-bottom: 15px;
}
.multi-utilization-chart .section-title {
display: flex;
align-items: center;
margin-bottom: 15px;
}
.multi-utilization-chart .title-bar {
width: 4px;
height: 18px;
background-color: #1890ff;
margin-right: 8px;
border-radius: 2px;
}
.multi-utilization-chart .title-text {
font-size: 16px;
font-weight: 500;
color: #fff;
}
.utilization-chart .section-title {
display: flex;
align-items: center;
margin-bottom: 15px;
}
/* 新增:图表选择器容器样式 */
.chart-select-container {
margin-left: auto;
margin-right: 20px;
display: flex;
align-items: center;
gap: 8px;
}
/* 新增:卡片外部选择器包装器样式 */
.chart-select-wrapper {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 8px;
padding-left: 4px;
}
.select-label {
color: #fff;
font-size: 14px;
white-space: nowrap;
}
.chart-select {
width: 250px;
}
/* 针对图表内选择器的Element UI样式覆盖 */
.chart-select .el-input__inner {
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
color: #fff;
font-size: 12px;
height: 32px;
}
.chart-select .el-input__icon {
color: #fff;
}
.chart-select .el-input__inner::placeholder {
color: rgba(255, 255, 255, 0.5);
}
.chart-select .el-input.is-focus .el-input__inner {
border-color: #409eff;
}
</style>
</html>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment