Commit a2415e75 by wangkangjie

Merge branch 'master' of https://git.rokedata.com/dws/dwsproject

parents faff2243 0706a370
...@@ -22,6 +22,14 @@ class RokeMesThreeColourLight(http.Controller): ...@@ -22,6 +22,14 @@ class RokeMesThreeColourLight(http.Controller):
data = {"code": 1, "message": "请求通过", "data": {"factory_code": factory_code}} data = {"code": 1, "message": "请求通过", "data": {"factory_code": factory_code}}
template = env.get_template('equipment_status_qdry.html') template = env.get_template('equipment_status_qdry.html')
return template.render(data) return template.render(data)
@http.route("/roke/three_color_light/device_analysis", type="http", auth='none', cors='*', csrf=False)
def device_analysis(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_analysis.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):
......
<!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" />
<!-- /roke_workstation_api/static/html/routing -->
<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-loading.body.fullscreen.lock="loading">
<!-- 页面标题 -->
<div class="dashboard-header">
<div class="header-content">
<img src="/roke_workstation_api/static/html/routing/image/header_bg.png" class="header-bg"
alt="header background" />
<span class="header-text">OEE时间利用率分析</span>
</div>
</div>
<!-- 替换原有的产线选择器 -->
<div class="floating-selector" id="floatingSelector">
<div class="floating-button" @click="showSelectionPanel">
<i class="el-icon-menu"></i>
</div>
<!-- 选择面板 -->
<div class="selection-panel" v-show="selectionPanelVisible">
<div class="panel-header">
<div class="back-button" v-if="selectionStep === 'line'" @click="selectionStep = 'workshop'">
<i class="el-icon-arrow-left"></i>
</div>
<div class="panel-title">
[[ selectionStep === 'workshop' ? '车间名称' : '产线名称' ]]
</div>
<div class="close-button" @click="selectionPanelVisible = false">
<i class="el-icon-close"></i>
</div>
</div>
<!-- 添加当前选择信息 -->
<div class="current-selection" v-if="selectedWorkshop || selectedLine">
<div class="selection-info" v-if="selectedWorkshop">
<span class="info-label">当前车间:</span>
<span class="info-value">[[ selectedWorkshop.name ]]</span>
</div>
<div class="selection-info" v-if="selectedLine">
<span class="info-label">当前产线:</span>
<span class="info-value">[[ getCurrentLineName() ]]</span>
</div>
</div>
<div class="panel-content">
<!-- 车间选择 -->
<div class="selection-list" v-if="selectionStep === 'workshop'">
<div class="selection-item" v-for="workshop in workshops" :key="workshop.id"
@click="selectWorkshop(workshop)"
:class="{'selected': selectedWorkshop && selectedWorkshop.id === workshop.id}">
[[ workshop.name ]]
<i class="el-icon-arrow-right"></i>
</div>
</div>
<!-- 产线选择 -->
<div class="selection-list" v-else>
<div class="selection-item" v-for="line in filteredProductionLines" :key="line.id"
@click="selectProductionLine(line)" :class="{'selected': selectedLine === line.id}">
[[ line.name ]]
</div>
</div>
</div>
</div>
</div>
<!-- 图表网格布局 -->
<div class="chart-grid">
<!-- 当前机台开机实时状态 -->
<div class="chart-card">
<div class="card-header">
<h3>当前机台开机实时状态</h3>
<span class="current-date">[[currentDate]]</span>
</div>
<div id="statusPieChart" class="chart-container"></div>
</div>
<!-- 设备OEE排名 -->
<div class="chart-card">
<div class="card-header">
<h3>设备OEE时间利用率TOP5</h3>
<el-select v-model="timeRanges.oeeRank" size="small" @change="updateChart('oeeRank')">
<el-option v-for="item in timeOptions" :key="item.value" :label="item.label" :value="item.value">
</el-option>
</el-select>
</div>
<div id="oeeRankChart" class="chart-container"></div>
</div>
<!-- 设备效率等待损失量 -->
<div class="chart-card">
<div class="card-header">
<h3>设备效率等待损失量(分钟)</h3>
<el-select v-model="timeRanges.waitingLoss" size="small" @change="updateChart('waitingLoss')">
<el-option v-for="item in timeOptions" :key="item.value" :label="item.label" :value="item.value">
</el-option>
</el-select>
</div>
<div id="waitingLossChart" class="chart-container"></div>
</div>
<!-- 设备OEE综合趋势 -->
<div class="chart-card">
<div class="card-header">
<h3>设备OEE时间利用率综合趋势</h3>
<div class="header-controls">
<el-select v-model="selectedDevice" size="small" placeholder="选择设备" @change="updateChart('oeeTrend')"
style="margin-right: 10px">
<el-option v-for="device in devices" :key="device.id" :label="device.name" :value="device.id">
</el-option>
</el-select>
<el-select v-model="timeRanges.oeeTrend" size="small" @change="updateChart('oeeTrend')">
<el-option v-for="item in timeOptions" :key="item.value" :label="item.label" :value="item.value">
</el-option>
</el-select>
<span class="unit-label">单位:%</span>
</div>
</div>
<div id="oeeTrendChart" class="chart-container"></div>
</div>
<!-- 设备效率故障损失量 -->
<div class="chart-card">
<div class="card-header">
<h3>设备效率故障损失量(分钟)</h3>
<el-select v-model="timeRanges.faultLoss" size="small" @change="updateChart('faultLoss')">
<el-option v-for="item in timeOptions" :key="item.value" :label="item.label" :value="item.value">
</el-option>
</el-select>
</div>
<div id="faultLossChart" class="chart-container"></div>
</div>
</div>
</div>
</body>
<script>
// 发送消息给父页面(关闭odoo的菜单弹窗)
document.addEventListener("click", () => {
window.parent.postMessage("hidePopover", "*");
});
let vue = new Vue({
el: "#app",
delimiters: ["[[", "]]"],
data() {
return {
loading: false,
currentDate: moment().format("YYYY/MM/DD"),
// dwsURL: "https://workstation.rokeris.com", // 基地址
dwsURL: "", // 基地址
baseURL: "https://dws-platform.xbg.rokeris.com/dev-api", // 基地址
factory_code: "", // 工厂编码
// 产线数据
selectedLine: null,
productionLines: [], // 将从接口获取
// 设备数据
selectedDevice: null,
devices: [],
// 图表实例
charts: {
statusPie: null,
oeeRank: null,
waitingLoss: null,
oeeTrend: null,
faultLoss: null,
},
// 时间范围选择
timeRanges: {
oeeRank: "today",
waitingLoss: "today",
faultLoss: "today",
oeeTrend: "today",
},
timeOptions: [
{ value: "today", label: "今天" },
{ value: "week", label: "本周" },
{ value: "month", label: "本月" },
{ value: "lastMonth", label: "上月" },
],
// 添加接口数据存储
apiData: {
deviceStates: [],
planTimeData: null,
plantTree: [], // 存储车间和产线树形数据
allEquipmentData: [], // 存储所有已绑定设备数据
currentLineEquipment: [], // 存储当前产线的设备数据
waitingLossData: [], // 存储等待损失数据
faultLossData: [], // 存储故障损失数据
oeeTrendData: [], // 存储OEE趋势数据
oeeRankData: [], // 存储OEE排名数据
},
// 浮动选择器相关
selectionPanelVisible: false,
selectionStep: "workshop", // 'workshop' 或 'line'
selectedWorkshop: null,
workshops: [], // 将从接口获取
};
},
created() {
if (this.getUrlSearch("factory_code")) {
this.factory_code = this.getUrlSearch("factory_code"); //截取url后面的参数
}
},
computed: {
filteredProductionLines() {
if (!this.selectedWorkshop) return [];
// 从apiData.plantTree中筛选当前选中车间的产线
const workshop = this.apiData.plantTree.find((w) => w.id === this.selectedWorkshop.id);
return workshop ? workshop.children || [] : [];
},
},
mounted() {
console.log("组件挂载,准备初始化...");
this.init(); // 直接调用init方法
this.$nextTick(() => {
// 这些初始化操作可能会与init方法中的操作重复,可以考虑移除
document.getElementById("bodyId").style.display = "block";
this.charts.statusPie = this.initStatusPieChart();
this.charts.oeeRank = this.initOEERankChart();
this.charts.waitingLoss = this.initWaitingLossChart();
this.charts.oeeTrend = this.initOEETrendChart();
this.charts.faultLoss = this.initFaultLossChart();
// 直接更新图表,不需要先获取设备列表
this.updateAllCharts();
// 设置默认选中的车间和产线
if (this.productionLines.length > 0) {
this.selectedLine = this.productionLines[0].id;
const workshopId = this.productionLines[0].workshopId;
this.selectedWorkshop = this.workshops.find((w) => w.id === workshopId);
}
// 初始化拖拽功能
this.initDraggable();
// 添加窗口大小变化监听
window.addEventListener("resize", this.handleResize);
// 初始化完成后触发一次resize
this.handleResize();
});
// 添加延迟更新,确保图表能正确显示
setTimeout(() => {
console.log("延迟更新所有图表...");
if (this.apiData.faultLossData && this.apiData.faultLossData.length > 0) {
console.log("故障损失数据存在,重新更新故障损失图表");
this.updateFaultLossChart();
}
if (this.apiData.waitingLossData && this.apiData.waitingLossData.length > 0) {
console.log("等待损失数据存在,重新更新等待损失图表");
this.updateWaitingLossChart();
}
}, 1000);
},
methods: {
// 通过网址跳转过来的页面,截取后面的参数
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];
},
// 初始化
async init() {
this.loading = true;
try {
// console.log("开始初始化...");
// 初始化图表
this.charts.statusPie = this.initStatusPieChart();
this.charts.oeeRank = this.initOEERankChart();
this.charts.waitingLoss = this.initWaitingLossChart();
this.charts.faultLoss = this.initFaultLossChart();
this.charts.oeeTrend = this.initOEETrendChart();
// 初始化时显示"暂无数据"提示
this.updateStatusPieChart(true);
this.updateOEERankChart(true);
this.updateWaitingLossChart(true);
this.updateFaultLossChart(true);
this.updateOEETrendChart(true);
// 并行请求三个初始化数据
// console.log("开始并行请求车间产线、设备计划时间和设备列表数据...");
const [plantTreeResult, devicePlanTimeResult, allEquipmentDataResult] =
await Promise.all([
this.fetchPlantTree(),
this.getDevicePlanTime(),
this.getAllEquipmentData(),
]);
// console.log("三个初始化数据请求已完成");
// 默认选择第一个车间和产线
if (this.apiData.plantTree.length > 0) {
this.selectedWorkshop = this.apiData.plantTree[0];
// console.log("默认选择车间:", this.selectedWorkshop.name);
if (this.filteredProductionLines.length > 0) {
this.selectedLine = this.filteredProductionLines[0].id;
// console.log("默认选择产线:", this.filteredProductionLines[0].name);
// 处理产线选择,获取设备状态数据
await this.handleLineChange();
} else {
// console.warn("没有可用的产线");
// 获取设备状态数据
await this.getInitialDeviceStates();
}
} else {
// console.warn("没有可用的车间");
// 获取设备状态数据
await this.getInitialDeviceStates();
}
// 初始化拖拽功能
this.initDraggable();
// 添加窗口大小变化监听
window.addEventListener("resize", this.handleResize);
// 初始化完成后触发一次resize
this.handleResize();
} catch (error) {
console.error("初始化失败:", error);
this.$message.error("初始化失败,请刷新页面重试");
} finally {
this.loading = false;
}
},
// 获取车间和产线树形数据
async fetchPlantTree() {
try {
console.log("发起请求: POST " + this.dwsURL + "/roke/workstation/plant/tree");
const response = await axios({
method: "POST",
url: this.dwsURL + "/roke/workstation/plant/tree",
data: { no_icon: true },
headers: {
"Content-Type": "application/json",
"x-session-id": "1d8a662231e9bbab315c736b7551657c18b44366",
},
});
console.log("接口返回数据:", JSON.stringify(response.data));
// 处理jsonrpc格式的返回数据
if (response.data && response.data.jsonrpc === "2.0" && response.data.result) {
const result = response.data.result;
if (result.code === 0) {
// 处理返回的树形数据
this.apiData.plantTree = this.processPlantTreeData(result.data || []);
console.log("处理后的树形数据:", JSON.stringify(this.apiData.plantTree));
// 提取车间数据
this.workshops = this.apiData.plantTree.map((item) => ({
id: item.id,
name: item.name,
}));
console.log("提取的车间数据:", JSON.stringify(this.workshops));
} else {
console.error("获取车间和产线数据失败:", result.message || "未知错误");
// 使用默认数据
this.useDefaultPlantData();
}
} else {
console.error("获取车间和产线数据失败: 返回格式不正确");
// 使用默认数据
this.useDefaultPlantData();
}
} catch (error) {
console.error("获取车间和产线数据异常:", error);
// 使用默认数据
this.useDefaultPlantData();
}
},
// 处理树形数据,适配我们的数据结构
processPlantTreeData(data) {
// 处理从接口返回的真实数据
const plantTree = [];
// 遍历车间数据
data.forEach((plant) => {
if (plant.type === "plant") {
const workshops = [];
// 遍历该车间下的产线
if (plant.workshops && plant.workshops.length > 0) {
plant.workshops.forEach((workshop) => {
if (workshop.type === "workshop") {
workshops.push({
id: workshop.id,
name: workshop.name,
workshopId: plant.id,
centerCount: workshop.center_count || 0,
});
}
});
}
plantTree.push({
id: plant.id,
name: plant.name,
children: workshops,
});
}
});
return plantTree;
},
// 使用默认数据(当接口请求失败时)
useDefaultPlantData() {
console.log("使用空数据代替默认数据");
// 使用空数据
this.workshops = [];
this.apiData.plantTree = [];
},
// 获取所有已绑定设备数据
async getAllEquipmentData() {
try {
// 发送请求获取所有已绑定设备数据
const response = await axios({
method: "post",
url: this.dwsURL + "/roke/workstation/equipment/get_equipment_data",
data: {},
headers: {
"Content-Type": "application/json",
"x-session-id": "1d8a662231e9bbab315c736b7551657c18b44366",
},
});
// 处理JSON-RPC格式的响应
if (response.data && response.data.result && response.data.result.code === 0) {
// 获取所有设备数据
const equipmentData = response.data.result.data || [];
// 存储所有设备数据,供后续使用
this.apiData.allEquipmentData = equipmentData;
console.log("所有已绑定设备数据:", JSON.stringify(equipmentData));
return equipmentData;
} else {
const errorMsg = response.data.result
? response.data.result.message
: "获取所有已绑定设备数据失败";
console.error(errorMsg);
}
} catch (error) {
console.error("获取所有已绑定设备数据出错:", error);
}
},
// 根据产线ID筛选设备
filterEquipmentByLine(lineId) {
console.log("筛选产线设备,产线ID:", lineId);
if (!lineId || !this.apiData.allEquipmentData) {
console.warn("产线ID或设备数据为空,无法筛选");
return [];
}
// 从所有已绑定设备数据中筛选出当前产线的设备
// 注意:根据数据结构,应该使用work_center_id来匹配产线ID
const lineEquipment = this.apiData.allEquipmentData.filter(
(device) => device.work_center_id == lineId
);
console.log(
`筛选到${lineEquipment.length}个设备,产线ID: ${lineId},设备数据:`,
JSON.stringify(lineEquipment)
);
// 如果没有筛选到设备,可能是因为产线ID实际上是workshop_id,尝试用workshop_id筛选
if (lineEquipment.length === 0) {
console.log("使用workshop_id尝试筛选设备");
const workshopEquipment = this.apiData.allEquipmentData.filter(
(device) => device.workshop_id == lineId
);
console.log(`使用workshop_id筛选到${workshopEquipment.length}个设备`);
return workshopEquipment;
}
return lineEquipment;
},
// 处理产线改变
async handleLineChange() {
this.loading = true;
try {
console.log("产线改变,新产线ID:", this.selectedLine);
// 直接从已有数据中筛选当前产线的设备数据
const lineEquipment = this.filterEquipmentByLine(this.selectedLine);
console.log("当前产线设备数据:", JSON.stringify(lineEquipment));
// 存储当前产线的设备数据
this.apiData.currentLineEquipment = lineEquipment;
// 更新设备选择器的设备列表
this.devices = lineEquipment.map((device) => ({
id: device.code,
name: device.name || device.code,
}));
console.log("更新后的设备列表:", JSON.stringify(this.devices));
// 如果有设备,默认选择第一个设备
if (this.devices.length > 0) {
this.selectedDevice = this.devices[0].id;
console.log("默认选择设备:", this.selectedDevice);
} else {
this.selectedDevice = "";
console.warn("当前产线下没有设备");
}
// 准备设备编号列表和计划时间数据
// 使用当前产线的设备编码,而不是所有设备
const deviceList = [];
// 从当前产线的设备数据中获取设备编码
if (this.apiData.currentLineEquipment && this.apiData.currentLineEquipment.length > 0) {
this.apiData.currentLineEquipment.forEach((device) => {
if (device.code) {
deviceList.push(device.code);
}
});
}
console.log("当前产线设备编码:", deviceList);
// 获取计划时间数据
const planTimeList = this.apiData.planTimeData || {};
// 检查参数
if (deviceList.length === 0) {
console.warn("当前产线没有可用的设备编码,无法获取设备状态");
this.apiData.deviceStates = [];
this.updateStatusPieChart(true);
return;
}
console.log("最终参数:", {
planTimeList,
deviceList,
});
// 获取设备状态数据
await this.getDeviceStateList(planTimeList, deviceList);
// 获取OEE排名数据
const { startDate, endDate } = this.getDateRange(this.timeRanges.oeeRank);
await this.getOEERankData(startDate, endDate);
// 获取等待损失数据
const waitingLossDates = this.getDateRange(this.timeRanges.waitingLoss);
await this.getLossData(waitingLossDates.startDate, waitingLossDates.endDate, "waiting");
// 获取故障损失数据
const faultLossDates = this.getDateRange(this.timeRanges.faultLoss);
await this.getLossData(faultLossDates.startDate, faultLossDates.endDate, "fault");
// 获取OEE趋势数据
if (this.selectedDevice) {
const oeeTrendDates = this.getDateRange(this.timeRanges.oeeTrend);
await this.getOEETrendData(
this.selectedDevice,
oeeTrendDates.startDate,
oeeTrendDates.endDate
);
}
// 更新所有图表
console.log("所有数据加载完成,开始更新图表");
this.updateAllCharts();
} catch (error) {
console.error("切换产线失败:", error);
this.$message.error("切换产线失败");
} finally {
this.loading = false;
}
},
// 获取设备状态列表
async getDeviceStateList(planTimeList, deviceList) {
try {
console.log("获取设备状态列表, 原始参数:", {
planTimeList,
deviceList,
});
// 检查参数是否有效
if (!deviceList || deviceList.length === 0) {
console.error("设备列表为空,无法获取设备状态");
this.apiData.deviceStates = [];
this.updateStatusPieChart();
return;
}
// 使用CORS代理
// 发送请求获取设备状态
const response = await axios({
method: "POST",
url: this.baseURL + "/public/device_efficiency/device_state_list",
data: {
factory_code: this.factory_code || "f1991dbb-52d6-11ef-b13f-fa163e5bb392", // 使用工厂代码
plan_time_list: planTimeList, // 直接使用API返回的计划时间数据
device_list: deviceList, // 真实的设备编码列表
},
headers: {
"Content-Type": "application/json",
},
});
console.log("设备状态接口返回:", JSON.stringify(response.data));
// 处理响应
if (response.data && response.data.code === 200) {
// 处理设备状态数据
const deviceStates = response.data.data || [];
// 确保即使返回空数组也能正确处理
this.apiData.deviceStates = deviceStates.length > 0 ? deviceStates : [];
console.log("设备状态数据:", JSON.stringify(this.apiData.deviceStates));
console.log("设备状态数据长度:", this.apiData.deviceStates.length);
// 更新状态饼图
this.updateStatusPieChart(this.apiData.deviceStates.length === 0);
return this.apiData.deviceStates;
} else {
const errorMsg = response.data.msg || "获取设备状态失败";
console.error(errorMsg);
this.$message.error(errorMsg);
this.apiData.deviceStates = [];
this.updateStatusPieChart(true); // 明确传入true表示没有数据
}
} catch (error) {
console.error("获取设备状态出错:", error);
this.$message.error("获取设备状态失败,请稍后重试");
// 不使用假数据,而是显示错误状态
this.apiData.deviceStates = [];
this.updateStatusPieChart(true); // 明确传入true表示没有数据
}
},
// 更新状态饼图
updateStatusPieChart(isEmpty = false) {
if (!this.charts.statusPie) {
console.error("状态饼图未初始化,无法更新");
return;
}
console.log("开始更新状态饼图...");
console.log("设备状态数据:", JSON.stringify(this.apiData.deviceStates));
// 处理设备状态数据
const statusCounts = {
running: 0,
offline: 0,
waiting: 0,
maintenance: 0,
};
if (!isEmpty && this.apiData.deviceStates && this.apiData.deviceStates.length > 0) {
this.apiData.deviceStates.forEach((device) => {
// 使用state字段映射到我们的状态类别
let status = "offline"; // 默认为离线
if (device.state) {
if (device.state === "green") {
status = "running";
} else if (device.state === "yellow") {
status = "waiting";
} else if (device.state === "red") {
status = "maintenance";
} else if (device.state === "gray") {
status = "offline";
}
}
statusCounts[status]++;
});
}
const total = Object.values(statusCounts).reduce((sum, count) => sum + count, 0);
console.log("状态计数:", statusCounts, "总数:", total);
// 完全重新设置图表选项,而不是部分更新
const option = {
tooltip: {
trigger: "item",
formatter: "{b}: {c} ({d}%)",
},
legend: {
orient: "horizontal",
bottom: 0,
itemWidth: 10,
itemHeight: 10,
textStyle: { color: "#fff", fontSize: 12 },
data: ["运行", "离线", "等待", "维护"],
},
series: [
{
name: "机台状态",
type: "pie",
radius: ["40%", "70%"],
avoidLabelOverlap: true,
emphasis: {
label: {
show: true,
position: "outside",
formatter: "{d}%",
color: "#fff",
fontSize: 12,
},
},
labelLine: {
show: true,
length: 10,
length2: 40,
lineStyle: {
color: "rgba(255, 255, 255, 0.3)",
},
},
data: [
{ value: statusCounts.running, name: "运行", itemStyle: { color: "#67C23A" } },
{ value: statusCounts.offline, name: "离线", itemStyle: { color: "#909399" } },
{ value: statusCounts.waiting, name: "等待", itemStyle: { color: "#E6A23C" } },
{
value: statusCounts.maintenance,
name: "维护",
itemStyle: { color: "#F56C6C" },
},
],
},
{
name: "总数",
type: "pie",
radius: ["0", "30%"],
label: {
position: "center",
formatter: ["{a|总数量}", "{b|" + total + "}"].join("\n"),
rich: {
a: {
fontSize: 14,
color: "#fff",
lineHeight: 20,
},
b: {
fontSize: 24,
color: "#fff",
fontWeight: "bold",
lineHeight: 30,
},
},
},
data: [
{
value: total,
name: "总数",
itemStyle: { color: "rgba(0, 0, 0, 0.2)" },
},
],
},
],
};
// 如果没有数据,添加"暂无数据"提示
if (isEmpty || total === 0) {
option.graphic = [
{
type: "text",
left: "center",
top: "middle",
style: {
text: "暂无数据",
fill: "#999",
font: "14px Microsoft YaHei",
},
},
];
}
// 应用完整的新配置
this.charts.statusPie.setOption(option, true);
console.log("状态饼图已更新,总数:", total);
},
// 选择车间
selectWorkshop(workshop) {
this.selectedWorkshop = workshop;
this.selectionStep = "line";
},
// 选择产线
selectProductionLine(line) {
this.selectedLine = line.id;
this.selectionPanelVisible = false;
this.handleLineChange();
},
// 获取当前产线名称
getCurrentLineName() {
if (!this.selectedLine) return "";
// 从树形数据中查找当前产线
for (const workshop of this.apiData.plantTree) {
if (workshop.children) {
const line = workshop.children.find((line) => line.id === this.selectedLine);
if (line) return line.name;
}
}
return "";
},
// 更新所有图表
updateAllCharts() {
console.log("开始更新所有图表...");
// 更新状态饼图
this.updateStatusPieChart();
console.log("状态饼图更新完成");
// 更新OEE排名图表
this.updateOEERankChart();
console.log("OEE排名图表更新完成");
// 更新等待损失图表
this.updateWaitingLossChart();
console.log("等待损失图表更新完成");
// 更新故障损失图表
this.updateFaultLossChart();
console.log("故障损失图表更新完成");
// 更新OEE趋势图表
this.updateOEETrendChart();
console.log("OEE趋势图表更新完成");
console.log("所有图表更新完成");
},
// 初始化所有图表
initCharts() {
console.log("开始初始化所有图表...");
// 初始化状态饼图
this.charts.statusPie = this.initStatusPieChart();
console.log("状态饼图初始化完成");
// 初始化OEE排名图表
this.charts.oeeRank = this.initOEERankChart();
// 初始化等待损失图表
this.charts.waitingLoss = this.initWaitingLossChart();
// 初始化OEE趋势图表
this.charts.oeeTrend = this.initOEETrendChart();
// 初始化故障损失图
this.charts.faultLoss = this.initFaultLossChart();
console.log("故障损失图表初始化完成");
// 监听窗口大小变化
window.addEventListener("resize", this.handleResize);
console.log("所有图表初始化完成");
},
// 初始化状态饼图
initStatusPieChart() {
// 获取DOM元素
const statusPieChart = document.getElementById("statusPieChart");
if (!statusPieChart) {
console.error("找不到状态饼图DOM元素");
return null;
}
console.log("初始化状态饼图...");
// 初始化图表
const chart = echarts.init(statusPieChart);
// 使用原有的图表配置
const option = {
tooltip: {
trigger: "item",
formatter: "{b}: {c} ({d}%)",
},
legend: {
orient: "horizontal",
bottom: 0,
itemWidth: 10,
itemHeight: 10,
textStyle: { color: "#fff", fontSize: 12 },
data: ["运行", "离线", "等待", "维护"],
},
series: [
{
name: "机台状态",
type: "pie",
avoidLabelOverlap: true, // 自动调整标签避免重叠
radius: ["40%", "70%"],
emphasis: {
label: {
show: true,
position: "outside",
formatter: "{d}%",
color: "#fff",
fontSize: 12,
},
},
labelLine: {
show: true,
length: 10,
length2: 10,
lineStyle: {
color: "rgba(255, 255, 255, 0.3)",
},
},
data: [
{
value: 0,
name: "运行",
itemStyle: { color: "#67C23A" },
},
{
value: 0,
name: "离线",
itemStyle: { color: "#909399" },
},
{
value: 0,
name: "等待",
itemStyle: { color: "#E6A23C" },
},
{
value: 0,
name: "维护",
itemStyle: { color: "#F56C6C" },
},
],
},
{
name: "总数",
type: "pie",
radius: ["0", "30%"],
label: {
position: "center",
formatter: ["{a|总数量}", "{b|0}"].join("\n"),
rich: {
a: {
fontSize: 14,
color: "#fff",
lineHeight: 20,
},
b: {
fontSize: 24,
color: "#fff",
fontWeight: "bold",
lineHeight: 30,
},
},
},
data: [
{
value: 0,
name: "总数",
itemStyle: { color: "rgba(0, 0, 0, 0.2)" },
},
],
},
],
};
// 应用配置
chart.setOption(option);
console.log("状态饼图初始化成功");
return chart;
},
// 处理窗口大小变化
handleResize() {
// 延迟执行以确保DOM已更新
setTimeout(() => {
if (this.charts.statusPie) this.charts.statusPie.resize();
if (this.charts.oeeRank) this.charts.oeeRank.resize();
if (this.charts.waitingLoss) this.charts.waitingLoss.resize();
if (this.charts.oeeTrend) this.charts.oeeTrend.resize();
if (this.charts.faultLoss) this.charts.faultLoss.resize();
}, 100);
},
// 组件销毁前清理
beforeDestroy() {
window.removeEventListener("resize", this.handleResize);
Object.values(this.charts).forEach((chart) => {
chart && chart.dispose();
});
},
// 初始化OEE排名图表
initOEERankChart() {
// 获取DOM元素
const oeeRankChart = document.getElementById("oeeRankChart");
if (!oeeRankChart) {
console.error("找不到OEE排名图表DOM元素");
return null;
}
console.log("初始化OEE排名图表...");
// 初始化图表
const chart = echarts.init(oeeRankChart);
// 设置图表配置,保持原有样式
const option = {
tooltip: {
trigger: "axis",
formatter: "{b}: {c}%",
axisPointer: {
type: "shadow",
},
},
grid: {
left: "3%",
right: "4%",
bottom: "3%",
containLabel: true,
},
dataZoom: [{
type: 'slider',
xAxisIndex: 0, // 指定控制X轴‌:ml-citation{ref="4" data="citationList"}
start: 0, // 初始显示起点‌:ml-citation{ref="1" data="citationList"}
end: 40, // 初始显示终点(示例值,按需计算)
height: 1, // 滚动条高度‌:ml-citation{ref="1" data="citationList"}
bottom: 1, // 滚动条位置‌:ml-citation{ref="1" data="citationList"}
fillerColor: 'rgba(84,112,198,0.6)', // 选中区域颜色‌:ml-citation{ref="1" data="citationList"}
zoomLock: true // 禁止缩放‌:ml-citation{ref="5" data="citationList"}
}, {
type: 'inside', // 内置滚动控制‌:ml-citation{ref="2" data="citationList"}
xAxisIndex: 0,
zoomOnMouseWheel: false, // 禁用滚轮缩放‌:ml-citation{ref="5" data="citationList"}
moveOnMouseWheel: true // 启用滚轮平移‌:ml-citation{ref="5" data="citationList"}
}],
xAxis: {
type: "category",
axisLine: { lineStyle: { color: "#fff" } },
axisLabel: { color: "#fff", interval: 0, fontSize: 10, align: 'center' },
data: [],
},
yAxis: {
type: "value",
max: 100,
axisLine: { lineStyle: { color: "#fff" } },
axisLabel: {
color: "#fff",
formatter: "{value}%",
},
splitLine: {
lineStyle: { color: "rgba(255,255,255,0.1)" },
},
},
series: [
{
type: "bar",
barWidth: "40%",
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: "#67C23A" },
{ offset: 1, color: "#409EFF" },
]),
},
label: {
show: true,
position: "top",
color: "#fff",
formatter: "{c}%",
},
data: [],
},
],
};
// 应用配置
chart.setOption(option);
console.log("OEE排名图表初始化成功");
return chart;
},
calculateRange() {
const showNum = 3;
const total = option.xAxis.data.length;
return total <= showNum ? 100 : (showNum / total * 100);
},
// 更新OEE排名图表
updateOEERankChart(isEmpty = false) {
if (!this.charts.oeeRank) {
console.error("OEE排名图表未初始化,无法更新");
return;
}
// 处理OEE排名数据
let deviceNames = [];
let deviceValues = [];
let hasRealData = false; // 添加标志,表示是否有真实数据
if (!isEmpty && this.apiData.oeeRankData && this.apiData.oeeRankData.length > 0) {
// 按照利用率排序(从高到低)
const sortedData = this.apiData.oeeRankData
.sort((a, b) => b.utilization_rate - a.utilization_rate)
.slice(0, 5); // 只取前5个
// 提取设备名称和利用率值
sortedData.forEach((item) => {
// 修改这里:从所有设备列表中获取准确的设备名称
let name = item.code; // 默认使用设备编码
// 在所有设备列表中查找匹配的设备
if (this.apiData.allEquipmentData && this.apiData.allEquipmentData.length > 0) {
const matchedDevice = this.apiData.allEquipmentData.find(
(device) => device.code === item.code
);
// 如果找到匹配的设备,使用其名称
if (matchedDevice && matchedDevice.name) {
name = matchedDevice.name;
}
}
// 转换为百分比并保留一位小数
const value = parseFloat(item.utilization_rate.toFixed(1));
const truncateBasic = (str) => str.slice(0, 12);
deviceNames.push(truncateBasic(name));
deviceValues.push(value);
});
// 如果有真实数据,设置标志
if (deviceNames.length > 0) {
hasRealData = true;
}
console.log("处理后的OEE排名数据:", {
names: deviceNames,
values: deviceValues,
});
} else {
console.log("没有OEE排名数据,显示暂无数据提示");
// 添加一个空数据,用于显示"暂无数据"提示
deviceNames = ["暂无数据"];
deviceValues = [0];
}
// 其余代码保持不变...
// 如果有真实数据,强制重新创建图表
if (hasRealData) {
// 销毁旧图表
this.charts.oeeRank.dispose();
// 重新创建图表
this.charts.oeeRank = this.initOEERankChart();
// 更新图表数据,不包含graphic属性
const option = {
xAxis: {
data: deviceNames,
},
series: [
{
data: deviceValues,
},
],
};
// 应用更新
this.charts.oeeRank.setOption(option);
} else {
// 没有真实数据,使用原来的逻辑
const option = {
xAxis: {
data: deviceNames,
},
series: [
{
data: deviceValues,
},
],
graphic: [
{
type: "text",
left: "center",
top: "middle",
style: {
text: "暂无数据",
fill: "#999",
font: "14px Microsoft YaHei",
},
},
],
};
// 应用更新
this.charts.oeeRank.setOption(option);
}
console.log(
"OEE排名图表已更新,设备数:",
deviceNames.length,
"是否有真实数据:",
hasRealData
);
},
// 初始化等待损失图表
initWaitingLossChart() {
// 获取DOM元素
const waitingLossChart = document.getElementById("waitingLossChart");
if (!waitingLossChart) {
console.error("找不到等待损失图表DOM元素");
return null;
}
console.log("初始化等待损失图表...");
// 初始化图表
const chart = echarts.init(waitingLossChart);
// 设置图表配置
const option = {
tooltip: {
trigger: "axis",
formatter: "{b}: {c}分钟",
axisPointer: {
type: "shadow",
},
},
grid: {
left: "5%", // 增加左侧边距
right: "4%",
bottom: "3%",
containLabel: true,
},
dataZoom: [{
type: 'slider',
xAxisIndex: 0, // 指定控制X轴‌:ml-citation{ref="4" data="citationList"}
start: 0, // 初始显示起点‌:ml-citation{ref="1" data="citationList"}
end: 40, // 初始显示终点(示例值,按需计算)
height: 1, // 滚动条高度‌:ml-citation{ref="1" data="citationList"}
bottom: 1, // 滚动条位置‌:ml-citation{ref="1" data="citationList"}
fillerColor: 'rgba(84,112,198,0.6)', // 选中区域颜色‌:ml-citation{ref="1" data="citationList"}
zoomLock: true // 禁止缩放‌:ml-citation{ref="5" data="citationList"}
}, {
type: 'inside', // 内置滚动控制‌:ml-citation{ref="2" data="citationList"}
xAxisIndex: 0,
zoomOnMouseWheel: false, // 禁用滚轮缩放‌:ml-citation{ref="5" data="citationList"}
moveOnMouseWheel: true // 启用滚轮平移‌:ml-citation{ref="5" data="citationList"}
}],
xAxis: {
type: "category",
axisLine: { lineStyle: { color: "#fff" } },
axisLabel: { color: "#fff", interval: 0, fontSize: 10, align: 'center' },
data: [],
},
yAxis: {
type: "value",
name: "分钟",
nameGap: 38,
nameTextStyle: { color: "#fff" },
axisLine: { lineStyle: { color: "#fff" } },
axisLabel: { color: "#fff" },
splitLine: {
lineStyle: { color: "rgba(255,255,255,0.1)" },
},
},
series: [
{
type: "bar",
barWidth: "40%",
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: "#409EFF" },
{ offset: 1, color: "#36CE9E" },
]),
},
label: {
show: true,
position: "top",
color: "#fff",
formatter: "{c}分钟",
},
data: [],
},
],
};
// 应用配置
chart.setOption(option);
console.log("等待损失图表初始化成功");
return chart;
},
// 更新等待损失图表
updateWaitingLossChart(isEmpty = false) {
if (!this.charts.waitingLoss) {
console.error("等待损失图表未初始化,无法更新");
return;
}
// 处理等待损失数据
let deviceNames = [];
let deviceValues = [];
let hasRealData = false; // 添加标志,表示是否有真实数据
if (!isEmpty && this.apiData.waitingLossData && this.apiData.waitingLossData.length > 0) {
// 按照损失时间排序(从高到低)
const sortedData = this.apiData.waitingLossData
.sort((a, b) => b.analysis_loss - a.analysis_loss)
.slice(0, 5); // 只取前5个
// 提取设备名称和损失时间值
sortedData.forEach((item) => {
// 修改这里:从所有设备列表中获取准确的设备名称
let name = item.code; // 默认使用设备编码
// 在所有设备列表中查找匹配的设备
if (this.apiData.allEquipmentData && this.apiData.allEquipmentData.length > 0) {
const matchedDevice = this.apiData.allEquipmentData.find(
(device) => device.code === item.code
);
// 如果找到匹配的设备,使用其名称
if (matchedDevice && matchedDevice.name) {
name = matchedDevice.name;
}
}
// 转换为分钟并保留整数
const value = Math.round(item.analysis_loss);
const truncateBasic = (str) => str.slice(0, 12);
deviceNames.push(truncateBasic(name));
deviceValues.push(value);
});
// 如果有真实数据,设置标志
if (deviceNames.length > 0) {
hasRealData = true;
}
console.log("处理后的等待损失数据:", {
names: deviceNames,
values: deviceValues,
});
} else {
console.log("没有等待损失数据,显示暂无数据提示");
// 添加一个空数据,用于显示"暂无数据"提示
deviceNames = ["暂无数据"];
deviceValues = [0];
}
// 如果有真实数据,强制重新创建图表
if (hasRealData) {
// 销毁旧图表
this.charts.waitingLoss.dispose();
// 重新创建图表
this.charts.waitingLoss = this.initWaitingLossChart();
// 更新图表数据,不包含graphic属性
const option = {
xAxis: {
data: deviceNames,
},
series: [
{
data: deviceValues,
},
],
};
// 应用更新
this.charts.waitingLoss.setOption(option);
} else {
// 没有真实数据,使用原来的逻辑
const option = {
xAxis: {
data: deviceNames,
},
series: [
{
data: deviceValues,
},
],
graphic: [
{
type: "text",
left: "center",
top: "middle",
style: {
text: "暂无数据",
fill: "#999",
font: "14px Microsoft YaHei",
},
},
],
};
// 应用更新
this.charts.waitingLoss.setOption(option);
}
console.log(
"等待损失图表已更新,设备数:",
deviceNames.length,
"是否有真实数据:",
hasRealData
);
},
// 初始化故障损失图表
initFaultLossChart() {
// 获取DOM元素
const faultLossChart = document.getElementById("faultLossChart");
if (!faultLossChart) {
console.error("找不到故障损失图表DOM元素");
return null;
}
console.log("初始化故障损失图表...");
// 初始化图表
const chart = echarts.init(faultLossChart);
// 设置图表配置
const option = {
tooltip: {
trigger: "axis",
formatter: "{b}: {c}分钟",
axisPointer: {
type: "shadow",
},
},
grid: {
left: "5%", // 增加左侧边距
right: "4%",
bottom: "3%",
containLabel: true,
},
dataZoom: [{
type: 'slider',
xAxisIndex: 0, // 指定控制X轴‌:ml-citation{ref="4" data="citationList"}
start: 0, // 初始显示起点‌:ml-citation{ref="1" data="citationList"}
end: 40, // 初始显示终点(示例值,按需计算)
height: 1, // 滚动条高度‌:ml-citation{ref="1" data="citationList"}
bottom: 1, // 滚动条位置‌:ml-citation{ref="1" data="citationList"}
fillerColor: 'rgba(84,112,198,0.6)', // 选中区域颜色‌:ml-citation{ref="1" data="citationList"}
zoomLock: true // 禁止缩放‌:ml-citation{ref="5" data="citationList"}
}, {
type: 'inside', // 内置滚动控制‌:ml-citation{ref="2" data="citationList"}
xAxisIndex: 0,
zoomOnMouseWheel: false, // 禁用滚轮缩放‌:ml-citation{ref="5" data="citationList"}
moveOnMouseWheel: true // 启用滚轮平移‌:ml-citation{ref="5" data="citationList"}
}],
xAxis: {
type: "category",
axisLine: { lineStyle: { color: "#fff" } },
axisLabel: { color: "#fff", interval: 0, fontSize: 10, align: 'center' },
data: [],
},
yAxis: {
type: "value",
name: "分钟",
nameGap: 38,
nameTextStyle: { color: "#fff" },
axisLine: { lineStyle: { color: "#fff" } },
axisLabel: { color: "#fff" },
splitLine: {
lineStyle: { color: "rgba(255,255,255,0.1)" },
},
},
series: [
{
type: "bar",
barWidth: "40%",
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: "#F56C6C" },
{ offset: 1, color: "#E6A23C" },
]),
},
label: {
show: true,
position: "top",
color: "#fff",
formatter: "{c}分钟",
},
data: [],
},
],
};
// 应用配置
chart.setOption(option);
console.log("故障损失图表初始化成功");
return chart;
},
// 更新故障损失图表
updateFaultLossChart(isEmpty = false) {
if (!this.charts.faultLoss) {
console.error("故障损失图表未初始化,无法更新");
return;
}
// 处理故障损失数据
let deviceNames = [];
let deviceValues = [];
let hasRealData = false; // 添加标志,表示是否有真实数据
if (!isEmpty && this.apiData.faultLossData && this.apiData.faultLossData.length > 0) {
// 按照损失时间排序(从高到低)
const sortedData = this.apiData.faultLossData
.sort((a, b) => b.analysis_loss - a.analysis_loss)
.slice(0, 5); // 只取前5个
console.log("排序后的故障损失数据:", JSON.stringify(sortedData));
// 提取设备名称和损失时间值
sortedData.forEach((item) => {
// 修改这里:从所有设备列表中获取准确的设备名称
let name = item.code; // 默认使用设备编码
// 在所有设备列表中查找匹配的设备
if (this.apiData.allEquipmentData && this.apiData.allEquipmentData.length > 0) {
const matchedDevice = this.apiData.allEquipmentData.find(
(device) => device.code === item.code
);
// 如果找到匹配的设备,使用其名称
if (matchedDevice && matchedDevice.name) {
name = matchedDevice.name;
}
}
// 转换为分钟并保留整数
const value = Math.round(item.analysis_loss);
const truncateBasic = (str) => str.slice(0, 12);
deviceNames.push(truncateBasic(name));
deviceValues.push(value);
});
// 如果有真实数据,设置标志
if (deviceNames.length > 0) {
hasRealData = true;
}
console.log("处理后的故障损失数据:", {
names: deviceNames,
values: deviceValues,
});
} else {
console.log("没有故障损失数据,显示暂无数据提示");
// 添加一个空数据,用于显示"暂无数据"提示
deviceNames = ["暂无数据"];
deviceValues = [0];
}
// 如果有真实数据,强制重新创建图表
if (hasRealData) {
// 销毁旧图表
this.charts.faultLoss.dispose();
// 重新创建图表
this.charts.faultLoss = this.initFaultLossChart();
// 更新图表数据,不包含graphic属性
const option = {
xAxis: {
data: deviceNames,
},
series: [
{
data: deviceValues,
},
],
};
// 应用更新
this.charts.faultLoss.setOption(option);
} else {
// 没有真实数据,使用原来的逻辑
const option = {
xAxis: {
data: deviceNames,
},
series: [
{
data: deviceValues,
},
],
graphic: [
{
type: "text",
left: "center",
top: "middle",
style: {
text: "暂无数据",
fill: "#999",
font: "14px Microsoft YaHei",
},
},
],
};
// 应用更新
this.charts.faultLoss.setOption(option);
}
console.log(
"故障损失图表已更新,设备数:",
deviceNames.length,
"是否有真实数据:",
hasRealData
);
},
// 使用默认设备数据
useDefaultDevices() {
// 根据不同的产线ID返回不同的设备列表
let deviceList = [];
// 电源PCB产线
if (this.selectedLine === 108) {
deviceList = [
{ id: 1, name: "PCB贴片机", code: "PCB001" },
{ id: 2, name: "PCB焊接机", code: "PCB002" },
{ id: 3, name: "PCB检测仪", code: "PCB003" },
{ id: 4, name: "PCB切割机", code: "PCB004" },
{ id: 5, name: "PCB包装机", code: "PCB005" },
];
}
// S1产线
else if (this.selectedLine === 101) {
deviceList = [
{ id: 6, name: "S1组装机", code: "S1001" },
{ id: 7, name: "S1测试仪", code: "S1002" },
{ id: 8, name: "S1包装机", code: "S1003" },
];
}
// 小米手表产线
else if (this.selectedLine === 123) {
deviceList = [
{ id: 9, name: "手表屏幕组装机", code: "W001" },
{ id: 10, name: "手表电池安装机", code: "W002" },
{ id: 11, name: "手表测试仪", code: "W003" },
{ id: 12, name: "手表包装机", code: "W004" },
];
}
// 其他产线使用默认设备
else {
deviceList = [
{ id: 1, name: "设备1", code: "DEV001" },
{ id: 2, name: "设备2", code: "DEV002" },
{ id: 3, name: "设备3", code: "DEV003" },
{ id: 4, name: "设备4", code: "DEV004" },
{ id: 5, name: "设备5", code: "DEV005" },
];
}
this.devices = deviceList;
console.log("使用默认设备列表:", this.devices);
// 默认选中第一个设备
if (this.devices.length > 0) {
this.selectedDevice = this.devices[0].id;
}
},
// 显示选择面板
showSelectionPanel() {
this.selectionPanelVisible = true;
this.selectionStep = "workshop";
},
// 添加初始化拖拽功能的方法
initDraggable() {
const floatingSelector = document.getElementById("floatingSelector");
const floatingButton = floatingSelector.querySelector(".floating-button");
let isDragging = false;
let currentX;
let currentY;
let initialX;
let initialY;
let xOffset = 20; // 初始化为左侧位置
let yOffset = 100; // 初始化为顶部位置
// 修改初始位置为左上角
floatingSelector.style.position = "fixed";
floatingSelector.style.top = yOffset + "px";
floatingSelector.style.left = xOffset + "px";
floatingSelector.style.right = "auto"; // 清除right属性
const dragStart = (e) => {
if (e.type === "touchstart") {
initialX = e.touches[0].clientX - xOffset;
initialY = e.touches[0].clientY - yOffset;
} else {
initialX = e.clientX - xOffset;
initialY = e.clientY - yOffset;
}
if (e.target === floatingButton || e.target.parentNode === floatingButton) {
isDragging = true;
floatingButton.style.cursor = "grabbing";
}
};
const dragEnd = () => {
isDragging = false;
floatingButton.style.cursor = "grab";
};
const drag = (e) => {
if (isDragging) {
e.preventDefault();
if (e.type === "touchmove") {
currentX = e.touches[0].clientX - initialX;
currentY = e.touches[0].clientY - initialY;
} else {
currentX = e.clientX - initialX;
currentY = e.clientY - initialY;
}
xOffset = currentX;
yOffset = currentY;
// 限制拖动范围,防止按钮被拖出视口
const maxX = window.innerWidth - floatingSelector.offsetWidth;
const maxY = window.innerHeight - floatingSelector.offsetHeight;
xOffset = Math.min(Math.max(xOffset, 0), maxX);
yOffset = Math.min(Math.max(yOffset, 0), maxY);
// 更新位置
floatingSelector.style.top = yOffset + "px";
floatingSelector.style.left = xOffset + "px";
}
};
// 添加触摸事件监听器
floatingButton.addEventListener("touchstart", dragStart, false);
document.addEventListener("touchend", dragEnd, false);
document.addEventListener("touchmove", drag, false);
// 添加鼠标事件监听器
floatingButton.addEventListener("mousedown", dragStart, false);
document.addEventListener("mouseup", dragEnd, false);
document.addEventListener("mousemove", drag, false);
},
// 获取设备计划运行时间
async getDevicePlanTime() {
try {
// 发送请求获取计划运行时间
const response = await axios({
method: "post",
url: this.dwsURL + "/roke/workstation/equipment/get_plan_time",
data: {},
headers: {
"Content-Type": "application/json",
"x-session-id": "1d8a662231e9bbab315c736b7551657c18b44366",
},
});
// 处理JSON-RPC格式的响应
if (response.data && response.data.result && response.data.result.code === 0) {
// 获取计划运行时间数据
const planTimeList = response.data.result.data || {};
// 存储计划时间数据,供后续请求使用
this.apiData.planTimeData = planTimeList;
console.log("计划运行时间数据:", JSON.stringify(planTimeList));
return planTimeList;
} else {
const errorMsg = response.data.result
? response.data.result.message
: "获取计划运行时间失败";
console.error(errorMsg);
// 使用默认计划时间数据
this.useDefaultPlanTimeData();
}
} catch (error) {
console.error("获取计划运行时间出错:", error);
// 使用默认计划时间数据
this.useDefaultPlanTimeData();
}
},
// 使用默认计划时间数据
useDefaultPlanTimeData() {
// 创建默认的计划时间数据
this.apiData.planTimeData = {
// 根据实际需要设置默认值
// 例如:设备ID为键,计划时间为值
};
console.log("使用默认计划运行时间数据");
},
// 获取初始设备状态数据
async getInitialDeviceStates() {
console.log("获取初始设备状态数据...");
try {
// 筛选当前产线的设备数据
const lineEquipment = this.filterEquipmentByLine(this.selectedLine);
console.log("当前产线设备数据:", JSON.stringify(lineEquipment));
// 存储当前产线的设备数据
this.apiData.currentLineEquipment = lineEquipment;
// 更新设备选择器的设备列表
this.devices = lineEquipment.map((device) => ({
id: device.code,
name: device.name || device.code,
}));
// 如果有设备,默认选择第一个设备
if (this.devices.length > 0) {
this.selectedDevice = this.devices[0].id;
} else {
this.selectedDevice = "";
}
// 使用当前产线的设备编码,而不是所有设备
const deviceList = [];
// 从当前产线的设备数据中获取设备编码
if (this.apiData.currentLineEquipment && this.apiData.currentLineEquipment.length > 0) {
this.apiData.currentLineEquipment.forEach((device) => {
if (device.code) {
deviceList.push(device.code);
}
});
}
console.log("初始设备编码列表:", deviceList);
// 获取计划时间数据
const planTimeList = this.apiData.planTimeData || {};
// 检查参数
if (deviceList.length === 0) {
console.warn("没有可用的设备编码,无法获取初始设备状态");
return;
}
console.log("初始设备状态请求参数:", {
planTimeList,
deviceList,
});
// 获取设备状态数据
await this.getDeviceStateList(planTimeList, deviceList);
// 获取初始OEE排名数据
const { startDate, endDate } = this.getDateRange(this.selectedTime);
await this.getOEERankData(startDate, endDate);
// 获取初始等待损失数据
const waitingLossDates = this.getDateRange(this.timeRanges.waitingLoss);
await this.getLossData(waitingLossDates.startDate, waitingLossDates.endDate, "waiting");
// 获取初始故障损失数据
const faultLossDates = this.getDateRange(this.timeRanges.faultLoss);
await this.getLossData(faultLossDates.startDate, faultLossDates.endDate, "fault");
// 获取初始OEE趋势数据
if (this.selectedDevice) {
const oeeTrendDates = this.getDateRange(this.timeRanges.oeeTrend);
await this.getOEETrendData(
this.selectedDevice,
oeeTrendDates.startDate,
oeeTrendDates.endDate
);
}
// 确保所有数据都已加载完成后,再更新图表
console.log("所有数据加载完成,开始更新图表");
// 单独更新每个图表,确保它们都能正确显示
this.updateStatusPieChart();
this.updateOEERankChart();
this.updateWaitingLossChart();
this.updateFaultLossChart();
this.updateOEETrendChart();
console.log("所有图表已更新");
} catch (error) {
console.error("获取初始设备状态数据出错:", error);
this.$message.error("获取初始设备状态数据失败,请稍后重试");
}
},
// 处理时间选择变化
async handleTimeChange() {
console.log("时间选择变化:", this.selectedTime);
// 获取选定时间范围的开始和结束日期
const { startDate, endDate } = this.getDateRange(this.selectedTime);
console.log("时间范围:", { startDate, endDate });
// 获取OEE排名数据
await this.getOEERankData(startDate, endDate);
// 调用updateChart方法更新OEE排名图表
this.updateChart("oeeRank");
},
// 获取日期范围
getDateRange(timeOption) {
const now = new Date();
let startDate, endDate;
// 格式化日期为 YYYY-MM-DD
const formatDate = (date) => {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
};
switch (timeOption) {
case "today":
// 今天
startDate = formatDate(now);
endDate = formatDate(now);
break;
case "week":
// 本周(周一到今天)
const dayOfWeek = now.getDay() || 7; // 将周日的0转换为7
const monday = new Date(now);
monday.setDate(now.getDate() - (dayOfWeek - 1));
startDate = formatDate(monday);
endDate = formatDate(now);
break;
case "month":
// 本月(1号到今天)
const firstDayOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
startDate = formatDate(firstDayOfMonth);
endDate = formatDate(now);
break;
case "lastMonth":
// 上月(上月1号到上月最后一天)
const firstDayOfLastMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1);
const lastDayOfLastMonth = new Date(now.getFullYear(), now.getMonth(), 0);
startDate = formatDate(firstDayOfLastMonth);
endDate = formatDate(lastDayOfLastMonth);
break;
default:
// 默认为今天
startDate = formatDate(now);
endDate = formatDate(now);
}
return { startDate, endDate };
},
// 获取OEE排名数据
async getOEERankData(startDate, endDate) {
try {
console.log("获取OEE排名数据, 参数:", {
startDate,
endDate,
});
// 使用当前产线的设备列表,而不是所有设备
const deviceList = [];
// 从当前产线的设备数据中获取设备编码
if (this.apiData.currentLineEquipment && this.apiData.currentLineEquipment.length > 0) {
this.apiData.currentLineEquipment.forEach((device) => {
if (device.code) {
deviceList.push(device.code);
}
});
}
console.log(`当前产线设备列表:`, deviceList);
// 获取计划时间数据
const planTimeList = this.apiData.planTimeData || {};
// 检查参数
if (deviceList.length === 0) {
console.warn("当前产线没有可用的设备编码,无法获取OEE排名数据");
// 设置空数据但不影响图表显示
this.apiData.oeeRankData = [];
// 更新OEE排名图表,显示"暂无数据"
this.updateOEERankChart(true);
return;
}
// 发送请求获取OEE排名数据
const response = await axios({
method: "POST",
url: this.baseURL + "/public/device_efficiency/device_utilization_rate_sort",
data: {
factory_code: this.factory_code || "f1991dbb-52d6-11ef-b13f-fa163e5bb392",
plan_time_list: planTimeList,
device_list: deviceList,
start_date: startDate,
end_date: endDate,
},
headers: {
"Content-Type": "application/json",
},
});
console.log("OEE排名接口返回:", JSON.stringify(response.data));
// 处理响应
if (response.data && response.data.code === 200) {
// 处理OEE排名数据
const oeeRankData = response.data.data || [];
// 存储OEE排名数据
this.apiData.oeeRankData = oeeRankData;
if (this.apiData.allEquipmentData && this.apiData.allEquipmentData.length > 0) {
this.apiData.allEquipmentData.forEach(equipment => {
equipment.utilization_rate = this.apiData.oeeRankData.find(
(device) => device.code === equipment.code
);
});
this.apiData.allEquipmentData.sort((a, b) => b.utilization_rate - a.utilization_rate);
}
// 更新OEE排名图表
this.updateOEERankChart();
return oeeRankData;
} else {
const errorMsg = response.data.msg || "获取OEE排名数据失败";
console.error(errorMsg);
this.$message.error(errorMsg);
// 设置空数据但不影响图表显示
this.apiData.oeeRankData = [];
// 更新OEE排名图表,显示"暂无数据"
this.updateOEERankChart(true);
}
} catch (error) {
console.error("获取OEE排名数据出错:", error);
this.$message.error("获取OEE排名数据失败,请稍后重试");
// 设置空数据但不影响图表显示
this.apiData.oeeRankData = [];
// 更新OEE排名图表,显示"暂无数据"
this.updateOEERankChart(true);
}
},
// 添加获取设备OEE趋势数据的方法
async getOEETrendData(deviceCode, startDate, endDate) {
try {
console.log("获取设备OEE趋势数据, 参数:", {
deviceCode,
startDate,
endDate,
});
// 检查参数
if (!deviceCode) {
console.warn("没有设备编码,无法获取OEE趋势数据");
return;
}
// 获取计划时间数据
const planTimeList = this.apiData.planTimeData || {};
// 发送请求获取OEE趋势数据
const response = await axios({
method: "POST",
url: this.baseURL + "/public/device_efficiency/device_oee_trend",
data: {
factory_code: this.factory_code || "f1991dbb-52d6-11ef-b13f-fa163e5bb392",
device_code: deviceCode,
start_date: startDate,
end_date: endDate,
plan_time_list: planTimeList,
},
headers: {
"Content-Type": "application/json",
},
});
console.log("OEE趋势接口返回:", JSON.stringify(response.data));
// 处理响应
if (response.data && response.data.code === 200) {
// 处理OEE趋势数据
const oeeTrendData = response.data.data || [];
// 存储OEE趋势数据
this.apiData.oeeTrendData = oeeTrendData;
return oeeTrendData;
} else {
const errorMsg = response.data.msg || "获取OEE趋势数据失败";
console.error(errorMsg);
this.$message.error(errorMsg);
}
} catch (error) {
console.error("获取OEE趋势数据出错:", error);
this.$message.error("获取OEE趋势数据失败,请稍后重试");
}
},
// 更新OEE趋势图表
updateOEETrendChart() {
if (!this.charts.oeeTrend) {
console.error("OEE趋势图表未初始化,无法更新");
return;
}
console.log("开始更新OEE趋势图表...");
console.log("OEE趋势数据:", JSON.stringify(this.apiData.oeeTrendData));
// 处理OEE趋势数据
let dates = [];
let values = [];
if (this.apiData.oeeTrendData && this.apiData.oeeTrendData.length > 0) {
// 按照日期排序
const sortedData = this.apiData.oeeTrendData.sort((a, b) => {
return new Date(a.date) - new Date(b.date);
});
console.log("排序后的OEE趋势数据:", JSON.stringify(sortedData));
// 提取日期和OEE值
sortedData.forEach((item) => {
// 格式化日期为MM-DD
const date = new Date(item.date);
const formattedDate = `${date.getMonth() + 1}-${date.getDate()}`;
// 使用utilization_rate字段,而不是oee字段
// 转换为百分比并保留一位小数
const value = parseFloat((item.utilization_rate || 0).toFixed(1));
dates.push(formattedDate);
values.push(value);
});
console.log("处理后的OEE趋势数据:", {
dates,
values,
});
} else {
console.log("没有OEE趋势数据,使用空数据");
}
// 计算最大值,用于设置y轴的最大值
const maxValue = Math.max(...values, 100); // 至少为100%
const yAxisMax = Math.ceil(maxValue / 10) * 10; // 向上取整到最接近的10的倍数
// 更新图表数据
const option = {
xAxis: {
data: dates,
},
yAxis: {
max: yAxisMax, // 动态设置y轴最大值
},
series: [
{
data: values,
},
],
};
// 应用更新
this.charts.oeeTrend.setOption(option);
console.log(
"OEE趋势图表已更新,日期数:",
dates.length,
"最大值:",
maxValue,
"Y轴最大值:",
yAxisMax
);
},
// 修改updateChart方法
async updateChart(chartType) {
console.log(`更新图表: ${chartType}`);
// 根据图表类型执行不同的操作
switch (chartType) {
case "oeeRank":
// 获取当前选择的时间范围
const oeeTimeRange = this.timeRanges.oeeRank;
console.log("OEE排名时间范围:", oeeTimeRange);
// 获取对应的日期范围
const oeeRankDates = this.getDateRange(oeeTimeRange);
console.log("OEE排名日期范围:", oeeRankDates);
// 获取新的OEE排名数据
await this.getOEERankData(oeeRankDates.startDate, oeeRankDates.endDate);
// 更新图表
this.updateOEERankChart();
break;
case "waitingLoss":
// 获取当前选择的时间范围
const waitingTimeRange = this.timeRanges.waitingLoss;
console.log("等待损失时间范围:", waitingTimeRange);
// 获取对应的日期范围
const waitingLossDates = this.getDateRange(waitingTimeRange);
console.log("等待损失日期范围:", waitingLossDates);
// 获取新的等待损失数据
await this.getLossData(
waitingLossDates.startDate,
waitingLossDates.endDate,
"waiting"
);
// 更新图表
this.updateWaitingLossChart();
break;
case "faultLoss":
// 获取当前选择的时间范围
const faultTimeRange = this.timeRanges.faultLoss;
console.log("故障损失时间范围:", faultTimeRange);
// 获取对应的日期范围
const faultLossDates = this.getDateRange(faultTimeRange);
console.log("故障损失日期范围:", faultLossDates);
// 获取新的故障损失数据
await this.getLossData(faultLossDates.startDate, faultLossDates.endDate, "fault");
// 更新图表
this.updateFaultLossChart();
break;
case "oeeTrend":
// 获取当前选择的时间范围
const oeeTrendTimeRange = this.timeRanges.oeeTrend;
console.log("OEE趋势时间范围:", oeeTrendTimeRange);
// 获取对应的日期范围
const oeeTrendDates = this.getDateRange(oeeTrendTimeRange);
console.log("OEE趋势日期范围:", oeeTrendDates);
// 获取当前选择的设备
const deviceCode = this.selectedDevice;
console.log("当前选择的设备:", deviceCode);
// 获取新的OEE趋势数据
await this.getOEETrendData(
deviceCode,
oeeTrendDates.startDate,
oeeTrendDates.endDate
);
// 更新图表
this.updateOEETrendChart();
break;
case "statusPie":
this.updateStatusPieChart();
break;
default:
console.warn(`未知的图表类型: ${chartType}`);
}
},
// 初始化OEE趋势图表
initOEETrendChart() {
// 获取DOM元素
const oeeTrendChart = document.getElementById("oeeTrendChart");
if (!oeeTrendChart) {
console.error("找不到OEE趋势图表DOM元素");
return null;
}
console.log("初始化OEE趋势图表...");
// 初始化图表
const chart = echarts.init(oeeTrendChart);
// 设置图表配置
const option = {
tooltip: {
trigger: "axis",
formatter: "{b}: {c}%",
axisPointer: {
type: "line",
},
},
grid: {
left: "5%",
right: "4%",
bottom: "3%",
containLabel: true,
},
xAxis: {
type: "category",
boundaryGap: false,
axisLine: { lineStyle: { color: "#fff" } },
axisLabel: { color: "#fff" },
data: [],
},
yAxis: {
type: "value",
name: "OEE(%)",
nameGap: 38,
nameTextStyle: { color: "#fff" },
min: 0,
// 移除max限制,让图表自动计算最大值
axisLine: { lineStyle: { color: "#fff" } },
axisLabel: { color: "#fff" },
splitLine: {
lineStyle: { color: "rgba(255,255,255,0.1)" },
},
},
series: [
{
name: "OEE",
type: "line",
smooth: true,
symbol: "circle",
symbolSize: 8,
itemStyle: {
color: "#409EFF",
},
lineStyle: {
width: 3,
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
{ offset: 0, color: "#409EFF" },
{ offset: 1, color: "#36CE9E" },
]),
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: "rgba(64, 158, 255, 0.5)" },
{ offset: 1, color: "rgba(64, 158, 255, 0)" },
]),
},
label: {
show: true,
position: "top",
color: "#fff",
formatter: "{c}%",
},
data: [],
},
],
};
// 应用配置
chart.setOption(option);
console.log("OEE趋势图表初始化成功");
return chart;
},
// 获取损失数据(等待损失和故障损失共用)
async getLossData(startDate, endDate, lossType) {
try {
console.log(`获取${lossType === "waiting" ? "等待" : "故障"}损失数据, 参数:`, {
startDate,
endDate,
lossType,
});
// 使用当前产线的设备列表,而不是所有设备
const deviceList = [];
// 从当前产线的设备数据中获取设备编码
if (this.apiData.currentLineEquipment && this.apiData.currentLineEquipment.length > 0) {
this.apiData.currentLineEquipment.forEach((device) => {
if (device.code) {
deviceList.push(device.code);
}
});
}
console.log(`当前产线设备列表:`, deviceList);
// 获取计划时间数据
const planTimeList = this.apiData.planTimeData || {};
// 检查参数
if (deviceList.length === 0) {
console.warn(
`当前产线没有可用的设备编码,无法获取${lossType === "waiting" ? "等待" : "故障"
}损失数据`
);
// 设置空数据但不影响图表显示
if (lossType === "waiting") {
this.apiData.waitingLossData = [];
// 更新等待损失图表,显示"暂无数据"
this.updateWaitingLossChart(true);
} else {
this.apiData.faultLossData = [];
// 更新故障损失图表,显示"暂无数据"
this.updateFaultLossChart(true);
}
return;
}
// 发送请求获取损失数据
const response = await axios({
method: "POST",
url: this.baseURL + "/public/device_efficiency/device_analysis_loss",
data: {
factory_code: this.factory_code || "f1991dbb-52d6-11ef-b13f-fa163e5bb392",
plan_time_list: planTimeList,
device_list: deviceList,
start_date: startDate,
end_date: endDate,
loss_type: lossType,
},
headers: {
"Content-Type": "application/json",
},
});
console.log(
`${lossType === "waiting" ? "等待" : "故障"}损失接口返回:`,
JSON.stringify(response.data)
);
// 处理响应
if (response.data && response.data.code === 200) {
// 处理损失数据
const lossData = response.data.data || [];
// 存储损失数据
if (lossType === "waiting") {
this.apiData.waitingLossData = lossData;
} else {
this.apiData.faultLossData = lossData;
}
return lossData;
} else {
const errorMsg =
response.data.msg || `获取${lossType === "waiting" ? "等待" : "故障"}损失数据失败`;
console.error(errorMsg);
this.$message.error(errorMsg);
// 设置空数据但不影响图表显示
if (lossType === "waiting") {
this.apiData.waitingLossData = [];
// 更新等待损失图表,显示"暂无数据"
this.updateWaitingLossChart(true);
} else {
this.apiData.faultLossData = [];
// 更新故障损失图表,显示"暂无数据"
this.updateFaultLossChart(true);
}
}
} catch (error) {
console.error(`获取${lossType === "waiting" ? "等待" : "故障"}损失数据出错:`, error);
this.$message.error(
`获取${lossType === "waiting" ? "等待" : "故障"}损失数据失败,请稍后重试`
);
// 设置空数据但不影响图表显示
if (lossType === "waiting") {
this.apiData.waitingLossData = [];
// 更新等待损失图表,显示"暂无数据"
this.updateWaitingLossChart(true);
} else {
this.apiData.faultLossData = [];
// 更新故障损失图表,显示"暂无数据"
this.updateFaultLossChart(true);
}
}
},
// 获取设备状态数据
async getDeviceStates() {
try {
console.log("获取设备状态数据...");
// 使用当前产线的设备列表,而不是所有设备
const deviceList = [];
// 从当前产线的设备数据中获取设备编码
if (this.apiData.currentLineEquipment && this.apiData.currentLineEquipment.length > 0) {
this.apiData.currentLineEquipment.forEach((device) => {
if (device.code) {
deviceList.push(device.code);
}
});
}
console.log(`当前产线设备列表:`, deviceList);
// 检查参数
if (deviceList.length === 0) {
console.warn("当前产线没有可用的设备编码,无法获取设备状态数据");
// 设置空数据但不影响图表显示
this.apiData.deviceStates = [];
// 更新状态饼图,显示"暂无数据"
this.updateStatusPieChart(true);
return;
}
// 发送请求获取设备状态数据
const response = await axios({
method: "POST",
url: this.baseURL + "/public/device_efficiency/device_state",
data: {
factory_code: this.factory_code || "f1991dbb-52d6-11ef-b13f-fa163e5bb392",
device_list: deviceList,
},
headers: {
"Content-Type": "application/json",
},
});
console.log("设备状态接口返回:", JSON.stringify(response.data));
// 处理响应
if (response.data && response.data.code === 200) {
// 处理设备状态数据
const deviceStates = response.data.data || [];
// 存储设备状态数据
this.apiData.deviceStates = deviceStates;
// 更新状态饼图
this.updateStatusPieChart();
return deviceStates;
} else {
const errorMsg = response.data.msg || "获取设备状态数据失败";
console.error(errorMsg);
this.$message.error(errorMsg);
// 设置空数据但不影响图表显示
this.apiData.deviceStates = [];
// 更新状态饼图,显示"暂无数据"
this.updateStatusPieChart(true);
}
} catch (error) {
console.error("获取设备状态数据出错:", error);
this.$message.error("获取设备状态数据失败,请稍后重试");
// 设置空数据但不影响图表显示
this.apiData.deviceStates = [];
// 更新状态饼图,显示"暂无数据"
this.updateStatusPieChart(true);
}
},
},
});
</script>
<style>
/* 基础样式 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: url("/roke_workstation_api/static/html/routing/image/bg-ok.png") no-repeat center center fixed;
background-size: cover;
color: #fff;
min-height: 100vh;
}
#app {
padding: 20px 10px;
}
/* 标题样式 */
.dashboard-header {
width: 100vw;
margin: -20px -20px 10px -20px;
display: flex;
justify-content: center;
align-items: center;
}
.header-content {
width: 100%;
position: relative;
display: flex;
justify-content: center;
align-items: center;
}
.header-bg {
width: 100%;
height: auto;
}
.header-text {
position: absolute;
font-size: 28px;
font-weight: 450;
color: #fff;
text-shadow: 0 0 10px rgba(0, 195, 255, 0.5);
z-index: 1;
}
/* 浮动选择器样式 */
.floating-selector {
position: fixed;
top: 100px;
left: 20px;
/* 改为left而不是right */
z-index: 1000;
}
/* 修改浮动按钮样式,增加拖动时的视觉反馈 */
.floating-button {
background-color: rgba(6, 32, 65, 0.9);
color: #fff;
width: 45px;
height: 45px;
border-radius: 50%;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.5), 0 0 15px rgba(64, 158, 255, 0.5);
cursor: grab;
user-select: none;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
border: 2px solid rgba(64, 158, 255, 0.5);
animation: pulse 2s infinite;
}
.floating-button:active {
cursor: grabbing;
transform: scale(0.95);
}
.floating-button i {
font-size: 22px;
color: #409eff;
}
/* 添加脉冲动画使按钮更醒目 */
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgba(64, 158, 255, 0.7);
}
70% {
box-shadow: 0 0 0 10px rgba(64, 158, 255, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(64, 158, 255, 0);
}
}
/* 选择面板样式 */
.selection-panel {
position: absolute;
top: 50px;
left: 0;
width: 200px;
background-color: rgba(6, 32, 65, 0.95);
border-radius: 4px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.3);
overflow: hidden;
border: 1px solid rgba(64, 158, 255, 0.2);
}
.panel-header {
display: flex;
align-items: center;
padding: 10px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.back-button,
.close-button {
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
color: #fff;
}
.panel-title {
flex: 1;
text-align: center;
color: #fff;
font-size: 14px;
}
.panel-content {
max-height: 300px;
overflow-y: auto;
}
.selection-list {
padding: 5px 0;
}
.selection-item {
padding: 10px 15px;
color: #fff;
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
}
.selection-item:hover {
background-color: rgba(64, 158, 255, 0.2);
}
/* 图表网格布局 */
.chart-grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: auto auto;
gap: 20px;
padding: 0 10px;
margin-top: 20px;
width: 100%;
}
/* 第一行的三个图表 */
.chart-grid>div:nth-child(1) {
grid-column: 1;
grid-row: 1;
}
.chart-grid>div:nth-child(2) {
grid-column: 2;
grid-row: 1;
}
.chart-grid>div:nth-child(3) {
grid-column: 3;
grid-row: 1;
}
/* 第二行的两个图表 */
.chart-grid>div:nth-child(4) {
grid-column: 1 / span 2;
grid-row: 2;
}
.chart-grid>div:nth-child(5) {
grid-column: 3;
grid-row: 2;
}
/* 图表卡片样式 */
.chart-card {
background: rgba(6, 32, 65, 0.9);
border-radius: 4px;
padding: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
width: 100%;
box-sizing: border-box;
overflow: hidden;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
height: 32px;
}
.card-header h3 {
font-size: 14px;
color: #fff;
margin: 0;
position: relative;
padding-left: 15px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 70%;
}
.card-header h3::before {
content: "";
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 4px;
height: 14px;
background-color: #409eff;
}
.current-date {
color: #fff;
font-size: 14px;
white-space: nowrap;
}
.header-controls {
display: flex;
align-items: center;
gap: 10px;
}
.chart-container {
height: 280px;
width: 100%;
overflow: hidden;
}
/* Element UI 样式覆盖 */
.el-select {
width: 100px;
display: flex;
align-items: center;
}
.el-select .el-input__inner {
height: 28px;
line-height: 28px;
background-color: rgba(0, 0, 0, 0.2);
border: 1px solid rgba(255, 255, 255, 0.1);
color: #fff !important;
}
.el-select .el-input__icon {
color: #fff !important;
}
.el-select-dropdown {
background-color: rgba(6, 32, 65, 0.95) !important;
border: 1px solid rgba(255, 255, 255, 0.1) !important;
}
.el-select-dropdown__item {
color: #fff !important;
}
.el-select-dropdown__item.hover,
.el-select-dropdown__item:hover {
background-color: rgba(64, 158, 255, 0.2) !important;
}
.el-select-dropdown__item.selected {
color: #409eff !important;
background-color: rgba(64, 158, 255, 0.1) !important;
}
.el-input.is-focus .el-input__inner,
.el-input.is-focus .el-input__inner:focus {
color: #fff !important;
}
/* 修改单位标签样式 */
.unit-label {
color: #fff;
font-size: 12px;
margin-left: 10px;
display: flex;
align-items: center;
height: 28px;
}
/* 添加当前选择信息样式 */
.current-selection {
padding: 10px 15px;
background-color: rgba(64, 158, 255, 0.1);
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.selection-info {
display: flex;
justify-content: space-between;
margin: 3px 0;
}
.info-label {
color: rgba(255, 255, 255, 0.7);
font-size: 12px;
}
.info-value {
color: #409eff;
font-size: 12px;
font-weight: bold;
}
/* 选中项样式 */
.selection-item.selected {
background-color: rgba(64, 158, 255, 0.2);
color: #409eff;
}
</style>
</html>
\ No newline at end of file
...@@ -28,10 +28,15 @@ ...@@ -28,10 +28,15 @@
'views/views.xml', 'views/views.xml',
'views/templates.xml', 'views/templates.xml',
'views/plant_working_time_config.xml', 'views/plant_working_time_config.xml',
'views/big_screen.xml',
'views/assets.xml',
'views/menus.xml', 'views/menus.xml',
], ],
# only loaded in demonstration mode # only loaded in demonstration mode
'demo': [ 'demo': [
'demo/demo.xml', 'demo/demo.xml',
], ],
'qweb': [
'static/src/xml/*.xml',
]
} }
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from . import controllers from . import controllers
\ No newline at end of file from . import big_screen
\ No newline at end of file
# -*- coding: utf-8 -*-
from odoo import http, fields
from odoo.http import request
from odoo.addons.roke_mes_three_colour_light.controller.main import RokeMesThreeColourLight
import os
import math
from datetime import datetime, time
from jinja2 import Environment, FileSystemLoader
import logging
import requests
import json
_logger = logging.getLogger(__name__)
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
templateloader = FileSystemLoader(searchpath=BASE_DIR + "/static")
env = Environment(loader=templateloader)
dws_platform_url ='https://dws-platform.xbg.rokeris.com/dev-api/public/device'
# dws_platform_url = 'http://localhost/dev-api/public/device'
headers = {
'Content-Type': 'application/json',
}
class ResMesBigScreen(http.Controller):
@http.route('/roke/tht/expected_process', type='http', auth='public', csrf=False, cors="*")
def roke_expected_process_module(self, **kwargs):
template = env.get_template('html/big_screen/view/expected_process.html')
html = template.render({})
return html
@http.route('/roke/tht/cook_process', type='http', auth='public', csrf=False, cors="*")
def roke_cook_process_module(self, **kwargs):
template = env.get_template('html/big_screen/view/cook_process.html')
html = template.render({})
return html
def search_equipments(self, data_acquisition_code='', plant_name='', category_name=''):
"""查询设备"""
domain = []
if data_acquisition_code:
domain.append(('data_acquisition_code', 'in', data_acquisition_code))
# 构建查询条件
if plant_name:
domain.append(('plant_id.name', '=', plant_name))
if category_name:
domain.append(('category_id.name', '=', category_name))
# 查询设备
equipments = http.request.env['roke.mes.equipment'].sudo().search(domain)
# 构造响应数据
equipment_list = [{
'id': eq.id,
'device_name': eq.name,
'device_code': eq.code,
'data_acquisition_code': eq.data_acquisition_code,
'category': eq.category_id.name if eq.category_id else '',
'plant_name': eq.plant_id.name if eq.plant_id else '',
} for eq in equipments]
return equipment_list
def common_dws_interface(self, body=None, url='', cate=''):
"""获取大屏数据"""
plant_name = body.get("plant_name", '')
today = body.get("today", '')
device_code_list = body.get("device_code_list", [])
# if not today:
# today = fields.Date.today().strftime('%Y-%m-%d')
category_name = body.get("category_name",'')
# 构建查询条件
if device_code_list:
equipment_list = device_code_list
else:
equipment_list = self.search_equipments(plant_name=plant_name, category_name=category_name)
try:
api_path = dws_platform_url + url
payload = {
"plant_name": plant_name,
"today": today,
"category_name": category_name,
"device_code_list": equipment_list
}
if cate:
payload.update({"cate": cate})
res = requests.post(api_path, data=json.dumps(payload), headers=headers, )
res_json = res.json()
return res_json
except Exception as e:
_logger.error(e)
return {
"code": 100,
"msg": str(e),
"data": {
},
"success": False,
"time": fields.Datetime.now()
}
@http.route('/big_screen_count', type='json', methods=['POST', 'OPTIONS'], auth="none", csrf=False,
cors='*')
def big_screen_count(self):
"""获取大屏数据"""
body = http.request.jsonrequest
return self.common_dws_interface(body, '/big_screen_count')
@http.route('/big_screen_today', type='json', methods=['POST', 'OPTIONS'], auth="none", csrf=False,
cors='*')
def big_screen_today(self):
body = http.request.jsonrequest
return self.common_dws_interface(body, '/big_screen_today')
@http.route('/get_jiedongji_temperature', type='json', methods=['POST', 'OPTIONS'], auth="none", csrf=False,
cors="*")
def get_jiedongji_temperature(self):
body = http.request.jsonrequest
return self.common_dws_interface(body, '/get_real_time_device_data', cate="解冻机")
@http.route('/get_qiekuaiji_data', type='json', methods=['POST', 'OPTIONS'], auth="none", csrf=False, cors="*")
def get_qiekuaiji_data(self):
body = http.request.jsonrequest
device_code_list = body.get('device_code_list')
try:
api_path = dws_platform_url + '/get_qiekuaiji_data'
payload = {
"device_code_list": device_code_list
}
res = requests.post(api_path, data=json.dumps(payload), headers=headers, )
res_json = res.json()
return res_json
except Exception as e:
_logger.error(e)
return {
"code": 100,
"msg": str(e),
"data": {
},
"success": False,
"time": fields.Datetime.now()
}
@http.route('/get_yanxunlu_data', type='json', methods=['POST', 'OPTIONS'], auth="none", csrf=False, cors="*")
def get_yanxunlu_data(self):
body = http.request.jsonrequest
return self.common_dws_interface(body, '/get_real_time_device_data', cate="烟熏炉")
@http.route('/get_lashengmo_data', type='json', methods=['POST', 'OPTIONS'], auth="none", csrf=False, cors="*")
def get_lashengmo_data(self):
body = http.request.jsonrequest
return self.common_dws_interface(body, '/get_real_time_device_data', cate="拉伸膜包装机")
@http.route('/get_lishibaozhuang_data', type='json', methods=['POST', 'OPTIONS'], auth="none", csrf=False, cors="*")
def get_lishibaozhuang_data(self):
body = http.request.jsonrequest
return self.common_dws_interface(body, '/get_real_time_device_data', cate="制袋包装机")
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
!(function (e, t) {
"object" == typeof exports && "undefined" != typeof module
? (module.exports = t())
: "function" == typeof define && define.amd
? define(t)
: ((e = "undefined" != typeof globalThis ? globalThis : e || self).axios =
t());
})(this, function () {
"use strict";
function e(t) {
return (
(e =
"function" == typeof Symbol && "symbol" == typeof Symbol.iterator
? function (e) {
return typeof e;
}
: function (e) {
return e &&
"function" == typeof Symbol &&
e.constructor === Symbol &&
e !== Symbol.prototype
? "symbol"
: typeof e;
}),
e(t)
);
}
function t(e, t) {
if (!(e instanceof t))
throw new TypeError("Cannot call a class as a function");
}
function n(e, t) {
for (var n = 0; n < t.length; n++) {
var r = t[n];
(r.enumerable = r.enumerable || !1),
(r.configurable = !0),
"value" in r && (r.writable = !0),
Object.defineProperty(e, r.key, r);
}
}
function r(e, t, r) {
return (
t && n(e.prototype, t),
r && n(e, r),
Object.defineProperty(e, "prototype", { writable: !1 }),
e
);
}
function o(e, t) {
return (
(function (e) {
if (Array.isArray(e)) return e;
})(e) ||
(function (e, t) {
var n =
null == e
? null
: ("undefined" != typeof Symbol && e[Symbol.iterator]) ||
e["@@iterator"];
if (null == n) return;
var r,
o,
i = [],
a = !0,
s = !1;
try {
for (
n = n.call(e);
!(a = (r = n.next()).done) &&
(i.push(r.value), !t || i.length !== t);
a = !0
);
} catch (e) {
(s = !0), (o = e);
} finally {
try {
a || null == n.return || n.return();
} finally {
if (s) throw o;
}
}
return i;
})(e, t) ||
(function (e, t) {
if (!e) return;
if ("string" == typeof e) return i(e, t);
var n = Object.prototype.toString.call(e).slice(8, -1);
"Object" === n && e.constructor && (n = e.constructor.name);
if ("Map" === n || "Set" === n) return Array.from(e);
if (
"Arguments" === n ||
/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)
)
return i(e, t);
})(e, t) ||
(function () {
throw new TypeError(
"Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."
);
})()
);
}
function i(e, t) {
(null == t || t > e.length) && (t = e.length);
for (var n = 0, r = new Array(t); n < t; n++) r[n] = e[n];
return r;
}
function a(e, t) {
return function () {
return e.apply(t, arguments);
};
}
var s,
u = Object.prototype.toString,
c = Object.getPrototypeOf,
f =
((s = Object.create(null)),
function (e) {
var t = u.call(e);
return s[t] || (s[t] = t.slice(8, -1).toLowerCase());
}),
l = function (e) {
return (
(e = e.toLowerCase()),
function (t) {
return f(t) === e;
}
);
},
d = function (t) {
return function (n) {
return e(n) === t;
};
},
p = Array.isArray,
h = d("undefined");
var m = l("ArrayBuffer");
var y = d("string"),
v = d("function"),
b = d("number"),
g = function (t) {
return null !== t && "object" === e(t);
},
w = function (e) {
if ("object" !== f(e)) return !1;
var t = c(e);
return !(
(null !== t &&
t !== Object.prototype &&
null !== Object.getPrototypeOf(t)) ||
Symbol.toStringTag in e ||
Symbol.iterator in e
);
},
E = l("Date"),
O = l("File"),
S = l("Blob"),
R = l("FileList"),
A = l("URLSearchParams");
function T(t, n) {
var r,
o,
i = arguments.length > 2 && void 0 !== arguments[2] ? arguments[2] : {},
a = i.allOwnKeys,
s = void 0 !== a && a;
if (null != t)
if (("object" !== e(t) && (t = [t]), p(t)))
for (r = 0, o = t.length; r < o; r++) n.call(null, t[r], r, t);
else {
var u,
c = s ? Object.getOwnPropertyNames(t) : Object.keys(t),
f = c.length;
for (r = 0; r < f; r++) (u = c[r]), n.call(null, t[u], u, t);
}
}
function j(e, t) {
t = t.toLowerCase();
for (var n, r = Object.keys(e), o = r.length; o-- > 0; )
if (t === (n = r[o]).toLowerCase()) return n;
return null;
}
var N =
"undefined" != typeof globalThis
? globalThis
: "undefined" != typeof self
? self
: "undefined" != typeof window
? window
: global,
C = function (e) {
return !h(e) && e !== N;
};
var x,
P =
((x = "undefined" != typeof Uint8Array && c(Uint8Array)),
function (e) {
return x && e instanceof x;
}),
k = l("HTMLFormElement"),
U = (function (e) {
var t = Object.prototype.hasOwnProperty;
return function (e, n) {
return t.call(e, n);
};
})(),
_ = l("RegExp"),
F = function (e, t) {
var n = Object.getOwnPropertyDescriptors(e),
r = {};
T(n, function (n, o) {
!1 !== t(n, o, e) && (r[o] = n);
}),
Object.defineProperties(e, r);
},
B = "abcdefghijklmnopqrstuvwxyz",
L = "0123456789",
D = { DIGIT: L, ALPHA: B, ALPHA_DIGIT: B + B.toUpperCase() + L };
var I = l("AsyncFunction"),
q = {
isArray: p,
isArrayBuffer: m,
isBuffer: function (e) {
return (
null !== e &&
!h(e) &&
null !== e.constructor &&
!h(e.constructor) &&
v(e.constructor.isBuffer) &&
e.constructor.isBuffer(e)
);
},
isFormData: function (e) {
var t;
return (
e &&
(("function" == typeof FormData && e instanceof FormData) ||
(v(e.append) &&
("formdata" === (t = f(e)) ||
("object" === t &&
v(e.toString) &&
"[object FormData]" === e.toString()))))
);
},
isArrayBufferView: function (e) {
return "undefined" != typeof ArrayBuffer && ArrayBuffer.isView
? ArrayBuffer.isView(e)
: e && e.buffer && m(e.buffer);
},
isString: y,
isNumber: b,
isBoolean: function (e) {
return !0 === e || !1 === e;
},
isObject: g,
isPlainObject: w,
isUndefined: h,
isDate: E,
isFile: O,
isBlob: S,
isRegExp: _,
isFunction: v,
isStream: function (e) {
return g(e) && v(e.pipe);
},
isURLSearchParams: A,
isTypedArray: P,
isFileList: R,
forEach: T,
merge: function e() {
for (
var t = (C(this) && this) || {},
n = t.caseless,
r = {},
o = function (t, o) {
var i = (n && j(r, o)) || o;
w(r[i]) && w(t)
? (r[i] = e(r[i], t))
: w(t)
? (r[i] = e({}, t))
: p(t)
? (r[i] = t.slice())
: (r[i] = t);
},
i = 0,
a = arguments.length;
i < a;
i++
)
arguments[i] && T(arguments[i], o);
return r;
},
extend: function (e, t, n) {
var r =
arguments.length > 3 && void 0 !== arguments[3] ? arguments[3] : {},
o = r.allOwnKeys;
return (
T(
t,
function (t, r) {
n && v(t) ? (e[r] = a(t, n)) : (e[r] = t);
},
{ allOwnKeys: o }
),
e
);
},
trim: function (e) {
return e.trim
? e.trim()
: e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, "");
},
stripBOM: function (e) {
return 65279 === e.charCodeAt(0) && (e = e.slice(1)), e;
},
inherits: function (e, t, n, r) {
(e.prototype = Object.create(t.prototype, r)),
(e.prototype.constructor = e),
Object.defineProperty(e, "super", { value: t.prototype }),
n && Object.assign(e.prototype, n);
},
toFlatObject: function (e, t, n, r) {
var o,
i,
a,
s = {};
if (((t = t || {}), null == e)) return t;
do {
for (i = (o = Object.getOwnPropertyNames(e)).length; i-- > 0; )
(a = o[i]),
(r && !r(a, e, t)) || s[a] || ((t[a] = e[a]), (s[a] = !0));
e = !1 !== n && c(e);
} while (e && (!n || n(e, t)) && e !== Object.prototype);
return t;
},
kindOf: f,
kindOfTest: l,
endsWith: function (e, t, n) {
(e = String(e)),
(void 0 === n || n > e.length) && (n = e.length),
(n -= t.length);
var r = e.indexOf(t, n);
return -1 !== r && r === n;
},
toArray: function (e) {
if (!e) return null;
if (p(e)) return e;
var t = e.length;
if (!b(t)) return null;
for (var n = new Array(t); t-- > 0; ) n[t] = e[t];
return n;
},
forEachEntry: function (e, t) {
for (
var n, r = (e && e[Symbol.iterator]).call(e);
(n = r.next()) && !n.done;
) {
var o = n.value;
t.call(e, o[0], o[1]);
}
},
matchAll: function (e, t) {
for (var n, r = []; null !== (n = e.exec(t)); ) r.push(n);
return r;
},
isHTMLForm: k,
hasOwnProperty: U,
hasOwnProp: U,
reduceDescriptors: F,
freezeMethods: function (e) {
F(e, function (t, n) {
if (v(e) && -1 !== ["arguments", "caller", "callee"].indexOf(n))
return !1;
var r = e[n];
v(r) &&
((t.enumerable = !1),
"writable" in t
? (t.writable = !1)
: t.set ||
(t.set = function () {
throw Error("Can not rewrite read-only method '" + n + "'");
}));
});
},
toObjectSet: function (e, t) {
var n = {},
r = function (e) {
e.forEach(function (e) {
n[e] = !0;
});
};
return p(e) ? r(e) : r(String(e).split(t)), n;
},
toCamelCase: function (e) {
return e
.toLowerCase()
.replace(/[-_\s]([a-z\d])(\w*)/g, function (e, t, n) {
return t.toUpperCase() + n;
});
},
noop: function () {},
toFiniteNumber: function (e, t) {
return (e = +e), Number.isFinite(e) ? e : t;
},
findKey: j,
global: N,
isContextDefined: C,
ALPHABET: D,
generateString: function () {
for (
var e =
arguments.length > 0 && void 0 !== arguments[0]
? arguments[0]
: 16,
t =
arguments.length > 1 && void 0 !== arguments[1]
? arguments[1]
: D.ALPHA_DIGIT,
n = "",
r = t.length;
e--;
)
n += t[(Math.random() * r) | 0];
return n;
},
isSpecCompliantForm: function (e) {
return !!(
e &&
v(e.append) &&
"FormData" === e[Symbol.toStringTag] &&
e[Symbol.iterator]
);
},
toJSONObject: function (e) {
var t = new Array(10);
return (function e(n, r) {
if (g(n)) {
if (t.indexOf(n) >= 0) return;
if (!("toJSON" in n)) {
t[r] = n;
var o = p(n) ? [] : {};
return (
T(n, function (t, n) {
var i = e(t, r + 1);
!h(i) && (o[n] = i);
}),
(t[r] = void 0),
o
);
}
}
return n;
})(e, 0);
},
isAsyncFn: I,
isThenable: function (e) {
return e && (g(e) || v(e)) && v(e.then) && v(e.catch);
},
};
function M(e, t, n, r, o) {
Error.call(this),
Error.captureStackTrace
? Error.captureStackTrace(this, this.constructor)
: (this.stack = new Error().stack),
(this.message = e),
(this.name = "AxiosError"),
t && (this.code = t),
n && (this.config = n),
r && (this.request = r),
o && (this.response = o);
}
q.inherits(M, Error, {
toJSON: function () {
return {
message: this.message,
name: this.name,
description: this.description,
number: this.number,
fileName: this.fileName,
lineNumber: this.lineNumber,
columnNumber: this.columnNumber,
stack: this.stack,
config: q.toJSONObject(this.config),
code: this.code,
status:
this.response && this.response.status ? this.response.status : null,
};
},
});
var z = M.prototype,
H = {};
[
"ERR_BAD_OPTION_VALUE",
"ERR_BAD_OPTION",
"ECONNABORTED",
"ETIMEDOUT",
"ERR_NETWORK",
"ERR_FR_TOO_MANY_REDIRECTS",
"ERR_DEPRECATED",
"ERR_BAD_RESPONSE",
"ERR_BAD_REQUEST",
"ERR_CANCELED",
"ERR_NOT_SUPPORT",
"ERR_INVALID_URL",
].forEach(function (e) {
H[e] = { value: e };
}),
Object.defineProperties(M, H),
Object.defineProperty(z, "isAxiosError", { value: !0 }),
(M.from = function (e, t, n, r, o, i) {
var a = Object.create(z);
return (
q.toFlatObject(
e,
a,
function (e) {
return e !== Error.prototype;
},
function (e) {
return "isAxiosError" !== e;
}
),
M.call(a, e.message, t, n, r, o),
(a.cause = e),
(a.name = e.name),
i && Object.assign(a, i),
a
);
});
function J(e) {
return q.isPlainObject(e) || q.isArray(e);
}
function W(e) {
return q.endsWith(e, "[]") ? e.slice(0, -2) : e;
}
function K(e, t, n) {
return e
? e
.concat(t)
.map(function (e, t) {
return (e = W(e)), !n && t ? "[" + e + "]" : e;
})
.join(n ? "." : "")
: t;
}
var V = q.toFlatObject(q, {}, null, function (e) {
return /^is[A-Z]/.test(e);
});
function G(t, n, r) {
if (!q.isObject(t)) throw new TypeError("target must be an object");
n = n || new FormData();
var o = (r = q.toFlatObject(
r,
{ metaTokens: !0, dots: !1, indexes: !1 },
!1,
function (e, t) {
return !q.isUndefined(t[e]);
}
)).metaTokens,
i = r.visitor || f,
a = r.dots,
s = r.indexes,
u =
(r.Blob || ("undefined" != typeof Blob && Blob)) &&
q.isSpecCompliantForm(n);
if (!q.isFunction(i)) throw new TypeError("visitor must be a function");
function c(e) {
if (null === e) return "";
if (q.isDate(e)) return e.toISOString();
if (!u && q.isBlob(e))
throw new M("Blob is not supported. Use a Buffer instead.");
return q.isArrayBuffer(e) || q.isTypedArray(e)
? u && "function" == typeof Blob
? new Blob([e])
: Buffer.from(e)
: e;
}
function f(t, r, i) {
var u = t;
if (t && !i && "object" === e(t))
if (q.endsWith(r, "{}"))
(r = o ? r : r.slice(0, -2)), (t = JSON.stringify(t));
else if (
(q.isArray(t) &&
(function (e) {
return q.isArray(e) && !e.some(J);
})(t)) ||
((q.isFileList(t) || q.endsWith(r, "[]")) && (u = q.toArray(t)))
)
return (
(r = W(r)),
u.forEach(function (e, t) {
!q.isUndefined(e) &&
null !== e &&
n.append(
!0 === s ? K([r], t, a) : null === s ? r : r + "[]",
c(e)
);
}),
!1
);
return !!J(t) || (n.append(K(i, r, a), c(t)), !1);
}
var l = [],
d = Object.assign(V, {
defaultVisitor: f,
convertValue: c,
isVisitable: J,
});
if (!q.isObject(t)) throw new TypeError("data must be an object");
return (
(function e(t, r) {
if (!q.isUndefined(t)) {
if (-1 !== l.indexOf(t))
throw Error("Circular reference detected in " + r.join("."));
l.push(t),
q.forEach(t, function (t, o) {
!0 ===
(!(q.isUndefined(t) || null === t) &&
i.call(n, t, q.isString(o) ? o.trim() : o, r, d)) &&
e(t, r ? r.concat(o) : [o]);
}),
l.pop();
}
})(t),
n
);
}
function $(e) {
var t = {
"!": "%21",
"'": "%27",
"(": "%28",
")": "%29",
"~": "%7E",
"%20": "+",
"%00": "\0",
};
return encodeURIComponent(e).replace(/[!'()~]|%20|%00/g, function (e) {
return t[e];
});
}
function X(e, t) {
(this._pairs = []), e && G(e, this, t);
}
var Q = X.prototype;
function Z(e) {
return encodeURIComponent(e)
.replace(/%3A/gi, ":")
.replace(/%24/g, "$")
.replace(/%2C/gi, ",")
.replace(/%20/g, "+")
.replace(/%5B/gi, "[")
.replace(/%5D/gi, "]");
}
function Y(e, t, n) {
if (!t) return e;
var r,
o = (n && n.encode) || Z,
i = n && n.serialize;
if (
(r = i
? i(t, n)
: q.isURLSearchParams(t)
? t.toString()
: new X(t, n).toString(o))
) {
var a = e.indexOf("#");
-1 !== a && (e = e.slice(0, a)),
(e += (-1 === e.indexOf("?") ? "?" : "&") + r);
}
return e;
}
(Q.append = function (e, t) {
this._pairs.push([e, t]);
}),
(Q.toString = function (e) {
var t = e
? function (t) {
return e.call(this, t, $);
}
: $;
return this._pairs
.map(function (e) {
return t(e[0]) + "=" + t(e[1]);
}, "")
.join("&");
});
var ee,
te = (function () {
function e() {
t(this, e), (this.handlers = []);
}
return (
r(e, [
{
key: "use",
value: function (e, t, n) {
return (
this.handlers.push({
fulfilled: e,
rejected: t,
synchronous: !!n && n.synchronous,
runWhen: n ? n.runWhen : null,
}),
this.handlers.length - 1
);
},
},
{
key: "eject",
value: function (e) {
this.handlers[e] && (this.handlers[e] = null);
},
},
{
key: "clear",
value: function () {
this.handlers && (this.handlers = []);
},
},
{
key: "forEach",
value: function (e) {
q.forEach(this.handlers, function (t) {
null !== t && e(t);
});
},
},
]),
e
);
})(),
ne = {
silentJSONParsing: !0,
forcedJSONParsing: !0,
clarifyTimeoutError: !1,
},
re = {
isBrowser: !0,
classes: {
URLSearchParams:
"undefined" != typeof URLSearchParams ? URLSearchParams : X,
FormData: "undefined" != typeof FormData ? FormData : null,
Blob: "undefined" != typeof Blob ? Blob : null,
},
isStandardBrowserEnv:
("undefined" == typeof navigator ||
("ReactNative" !== (ee = navigator.product) &&
"NativeScript" !== ee &&
"NS" !== ee)) &&
"undefined" != typeof window &&
"undefined" != typeof document,
isStandardBrowserWebWorkerEnv:
"undefined" != typeof WorkerGlobalScope &&
self instanceof WorkerGlobalScope &&
"function" == typeof self.importScripts,
protocols: ["http", "https", "file", "blob", "url", "data"],
};
function oe(e) {
function t(e, n, r, o) {
var i = e[o++],
a = Number.isFinite(+i),
s = o >= e.length;
return (
(i = !i && q.isArray(r) ? r.length : i),
s
? (q.hasOwnProp(r, i) ? (r[i] = [r[i], n]) : (r[i] = n), !a)
: ((r[i] && q.isObject(r[i])) || (r[i] = []),
t(e, n, r[i], o) &&
q.isArray(r[i]) &&
(r[i] = (function (e) {
var t,
n,
r = {},
o = Object.keys(e),
i = o.length;
for (t = 0; t < i; t++) r[(n = o[t])] = e[n];
return r;
})(r[i])),
!a)
);
}
if (q.isFormData(e) && q.isFunction(e.entries)) {
var n = {};
return (
q.forEachEntry(e, function (e, r) {
t(
(function (e) {
return q.matchAll(/\w+|\[(\w*)]/g, e).map(function (e) {
return "[]" === e[0] ? "" : e[1] || e[0];
});
})(e),
r,
n,
0
);
}),
n
);
}
return null;
}
var ie = { "Content-Type": void 0 };
var ae = {
transitional: ne,
adapter: ["xhr", "http"],
transformRequest: [
function (e, t) {
var n,
r = t.getContentType() || "",
o = r.indexOf("application/json") > -1,
i = q.isObject(e);
if ((i && q.isHTMLForm(e) && (e = new FormData(e)), q.isFormData(e)))
return o && o ? JSON.stringify(oe(e)) : e;
if (
q.isArrayBuffer(e) ||
q.isBuffer(e) ||
q.isStream(e) ||
q.isFile(e) ||
q.isBlob(e)
)
return e;
if (q.isArrayBufferView(e)) return e.buffer;
if (q.isURLSearchParams(e))
return (
t.setContentType(
"application/x-www-form-urlencoded;charset=utf-8",
!1
),
e.toString()
);
if (i) {
if (r.indexOf("application/x-www-form-urlencoded") > -1)
return (function (e, t) {
return G(
e,
new re.classes.URLSearchParams(),
Object.assign(
{
visitor: function (e, t, n, r) {
return re.isNode && q.isBuffer(e)
? (this.append(t, e.toString("base64")), !1)
: r.defaultVisitor.apply(this, arguments);
},
},
t
)
);
})(e, this.formSerializer).toString();
if ((n = q.isFileList(e)) || r.indexOf("multipart/form-data") > -1) {
var a = this.env && this.env.FormData;
return G(
n ? { "files[]": e } : e,
a && new a(),
this.formSerializer
);
}
}
return i || o
? (t.setContentType("application/json", !1),
(function (e, t, n) {
if (q.isString(e))
try {
return (t || JSON.parse)(e), q.trim(e);
} catch (e) {
if ("SyntaxError" !== e.name) throw e;
}
return (n || JSON.stringify)(e);
})(e))
: e;
},
],
transformResponse: [
function (e) {
var t = this.transitional || ae.transitional,
n = t && t.forcedJSONParsing,
r = "json" === this.responseType;
if (e && q.isString(e) && ((n && !this.responseType) || r)) {
var o = !(t && t.silentJSONParsing) && r;
try {
return JSON.parse(e);
} catch (e) {
if (o) {
if ("SyntaxError" === e.name)
throw M.from(e, M.ERR_BAD_RESPONSE, this, null, this.response);
throw e;
}
}
}
return e;
},
],
timeout: 0,
xsrfCookieName: "XSRF-TOKEN",
xsrfHeaderName: "X-XSRF-TOKEN",
maxContentLength: -1,
maxBodyLength: -1,
env: { FormData: re.classes.FormData, Blob: re.classes.Blob },
validateStatus: function (e) {
return e >= 200 && e < 300;
},
headers: { common: { Accept: "application/json, text/plain, */*" } },
};
q.forEach(["delete", "get", "head"], function (e) {
ae.headers[e] = {};
}),
q.forEach(["post", "put", "patch"], function (e) {
ae.headers[e] = q.merge(ie);
});
var se = ae,
ue = q.toObjectSet([
"age",
"authorization",
"content-length",
"content-type",
"etag",
"expires",
"from",
"host",
"if-modified-since",
"if-unmodified-since",
"last-modified",
"location",
"max-forwards",
"proxy-authorization",
"referer",
"retry-after",
"user-agent",
]),
ce = Symbol("internals");
function fe(e) {
return e && String(e).trim().toLowerCase();
}
function le(e) {
return !1 === e || null == e ? e : q.isArray(e) ? e.map(le) : String(e);
}
function de(e, t, n, r, o) {
return q.isFunction(r)
? r.call(this, t, n)
: (o && (t = n),
q.isString(t)
? q.isString(r)
? -1 !== t.indexOf(r)
: q.isRegExp(r)
? r.test(t)
: void 0
: void 0);
}
var pe = (function (e, n) {
function i(e) {
t(this, i), e && this.set(e);
}
return (
r(
i,
[
{
key: "set",
value: function (e, t, n) {
var r = this;
function o(e, t, n) {
var o = fe(t);
if (!o)
throw new Error("header name must be a non-empty string");
var i = q.findKey(r, o);
(!i ||
void 0 === r[i] ||
!0 === n ||
(void 0 === n && !1 !== r[i])) &&
(r[i || t] = le(e));
}
var i,
a,
s,
u,
c,
f = function (e, t) {
return q.forEach(e, function (e, n) {
return o(e, n, t);
});
};
return (
q.isPlainObject(e) || e instanceof this.constructor
? f(e, t)
: q.isString(e) &&
(e = e.trim()) &&
!/^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(e.trim())
? f(
((c = {}),
(i = e) &&
i.split("\n").forEach(function (e) {
(u = e.indexOf(":")),
(a = e.substring(0, u).trim().toLowerCase()),
(s = e.substring(u + 1).trim()),
!a ||
(c[a] && ue[a]) ||
("set-cookie" === a
? c[a]
? c[a].push(s)
: (c[a] = [s])
: (c[a] = c[a] ? c[a] + ", " + s : s));
}),
c),
t
)
: null != e && o(t, e, n),
this
);
},
},
{
key: "get",
value: function (e, t) {
if ((e = fe(e))) {
var n = q.findKey(this, e);
if (n) {
var r = this[n];
if (!t) return r;
if (!0 === t)
return (function (e) {
for (
var t,
n = Object.create(null),
r = /([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g;
(t = r.exec(e));
)
n[t[1]] = t[2];
return n;
})(r);
if (q.isFunction(t)) return t.call(this, r, n);
if (q.isRegExp(t)) return t.exec(r);
throw new TypeError("parser must be boolean|regexp|function");
}
}
},
},
{
key: "has",
value: function (e, t) {
if ((e = fe(e))) {
var n = q.findKey(this, e);
return !(
!n ||
void 0 === this[n] ||
(t && !de(0, this[n], n, t))
);
}
return !1;
},
},
{
key: "delete",
value: function (e, t) {
var n = this,
r = !1;
function o(e) {
if ((e = fe(e))) {
var o = q.findKey(n, e);
!o || (t && !de(0, n[o], o, t)) || (delete n[o], (r = !0));
}
}
return q.isArray(e) ? e.forEach(o) : o(e), r;
},
},
{
key: "clear",
value: function (e) {
for (var t = Object.keys(this), n = t.length, r = !1; n--; ) {
var o = t[n];
(e && !de(0, this[o], o, e, !0)) || (delete this[o], (r = !0));
}
return r;
},
},
{
key: "normalize",
value: function (e) {
var t = this,
n = {};
return (
q.forEach(this, function (r, o) {
var i = q.findKey(n, o);
if (i) return (t[i] = le(r)), void delete t[o];
var a = e
? (function (e) {
return e
.trim()
.toLowerCase()
.replace(/([a-z\d])(\w*)/g, function (e, t, n) {
return t.toUpperCase() + n;
});
})(o)
: String(o).trim();
a !== o && delete t[o], (t[a] = le(r)), (n[a] = !0);
}),
this
);
},
},
{
key: "concat",
value: function () {
for (
var e, t = arguments.length, n = new Array(t), r = 0;
r < t;
r++
)
n[r] = arguments[r];
return (e = this.constructor).concat.apply(e, [this].concat(n));
},
},
{
key: "toJSON",
value: function (e) {
var t = Object.create(null);
return (
q.forEach(this, function (n, r) {
null != n &&
!1 !== n &&
(t[r] = e && q.isArray(n) ? n.join(", ") : n);
}),
t
);
},
},
{
key: Symbol.iterator,
value: function () {
return Object.entries(this.toJSON())[Symbol.iterator]();
},
},
{
key: "toString",
value: function () {
return Object.entries(this.toJSON())
.map(function (e) {
var t = o(e, 2);
return t[0] + ": " + t[1];
})
.join("\n");
},
},
{
key: Symbol.toStringTag,
get: function () {
return "AxiosHeaders";
},
},
],
[
{
key: "from",
value: function (e) {
return e instanceof this ? e : new this(e);
},
},
{
key: "concat",
value: function (e) {
for (
var t = new this(e),
n = arguments.length,
r = new Array(n > 1 ? n - 1 : 0),
o = 1;
o < n;
o++
)
r[o - 1] = arguments[o];
return (
r.forEach(function (e) {
return t.set(e);
}),
t
);
},
},
{
key: "accessor",
value: function (e) {
var t = (this[ce] = this[ce] = { accessors: {} }).accessors,
n = this.prototype;
function r(e) {
var r = fe(e);
t[r] ||
(!(function (e, t) {
var n = q.toCamelCase(" " + t);
["get", "set", "has"].forEach(function (r) {
Object.defineProperty(e, r + n, {
value: function (e, n, o) {
return this[r].call(this, t, e, n, o);
},
configurable: !0,
});
});
})(n, e),
(t[r] = !0));
}
return q.isArray(e) ? e.forEach(r) : r(e), this;
},
},
]
),
i
);
})();
pe.accessor([
"Content-Type",
"Content-Length",
"Accept",
"Accept-Encoding",
"User-Agent",
"Authorization",
]),
q.freezeMethods(pe.prototype),
q.freezeMethods(pe);
var he = pe;
function me(e, t) {
var n = this || se,
r = t || n,
o = he.from(r.headers),
i = r.data;
return (
q.forEach(e, function (e) {
i = e.call(n, i, o.normalize(), t ? t.status : void 0);
}),
o.normalize(),
i
);
}
function ye(e) {
return !(!e || !e.__CANCEL__);
}
function ve(e, t, n) {
M.call(this, null == e ? "canceled" : e, M.ERR_CANCELED, t, n),
(this.name = "CanceledError");
}
q.inherits(ve, M, { __CANCEL__: !0 });
var be = re.isStandardBrowserEnv
? {
write: function (e, t, n, r, o, i) {
var a = [];
a.push(e + "=" + encodeURIComponent(t)),
q.isNumber(n) && a.push("expires=" + new Date(n).toGMTString()),
q.isString(r) && a.push("path=" + r),
q.isString(o) && a.push("domain=" + o),
!0 === i && a.push("secure"),
(document.cookie = a.join("; "));
},
read: function (e) {
var t = document.cookie.match(
new RegExp("(^|;\\s*)(" + e + ")=([^;]*)")
);
return t ? decodeURIComponent(t[3]) : null;
},
remove: function (e) {
this.write(e, "", Date.now() - 864e5);
},
}
: {
write: function () {},
read: function () {
return null;
},
remove: function () {},
};
function ge(e, t) {
return e && !/^([a-z][a-z\d+\-.]*:)?\/\//i.test(t)
? (function (e, t) {
return t ? e.replace(/\/+$/, "") + "/" + t.replace(/^\/+/, "") : e;
})(e, t)
: t;
}
var we = re.isStandardBrowserEnv
? (function () {
var e,
t = /(msie|trident)/i.test(navigator.userAgent),
n = document.createElement("a");
function r(e) {
var r = e;
return (
t && (n.setAttribute("href", r), (r = n.href)),
n.setAttribute("href", r),
{
href: n.href,
protocol: n.protocol ? n.protocol.replace(/:$/, "") : "",
host: n.host,
search: n.search ? n.search.replace(/^\?/, "") : "",
hash: n.hash ? n.hash.replace(/^#/, "") : "",
hostname: n.hostname,
port: n.port,
pathname:
"/" === n.pathname.charAt(0) ? n.pathname : "/" + n.pathname,
}
);
}
return (
(e = r(window.location.href)),
function (t) {
var n = q.isString(t) ? r(t) : t;
return n.protocol === e.protocol && n.host === e.host;
}
);
})()
: function () {
return !0;
};
function Ee(e, t) {
var n = 0,
r = (function (e, t) {
e = e || 10;
var n,
r = new Array(e),
o = new Array(e),
i = 0,
a = 0;
return (
(t = void 0 !== t ? t : 1e3),
function (s) {
var u = Date.now(),
c = o[a];
n || (n = u), (r[i] = s), (o[i] = u);
for (var f = a, l = 0; f !== i; ) (l += r[f++]), (f %= e);
if (((i = (i + 1) % e) === a && (a = (a + 1) % e), !(u - n < t))) {
var d = c && u - c;
return d ? Math.round((1e3 * l) / d) : void 0;
}
}
);
})(50, 250);
return function (o) {
var i = o.loaded,
a = o.lengthComputable ? o.total : void 0,
s = i - n,
u = r(s);
n = i;
var c = {
loaded: i,
total: a,
progress: a ? i / a : void 0,
bytes: s,
rate: u || void 0,
estimated: u && a && i <= a ? (a - i) / u : void 0,
event: o,
};
(c[t ? "download" : "upload"] = !0), e(c);
};
}
var Oe = {
http: null,
xhr:
"undefined" != typeof XMLHttpRequest &&
function (e) {
return new Promise(function (t, n) {
var r,
o = e.data,
i = he.from(e.headers).normalize(),
a = e.responseType;
function s() {
e.cancelToken && e.cancelToken.unsubscribe(r),
e.signal && e.signal.removeEventListener("abort", r);
}
q.isFormData(o) &&
(re.isStandardBrowserEnv || re.isStandardBrowserWebWorkerEnv
? i.setContentType(!1)
: i.setContentType("multipart/form-data;", !1));
var u = new XMLHttpRequest();
if (e.auth) {
var c = e.auth.username || "",
f = e.auth.password
? unescape(encodeURIComponent(e.auth.password))
: "";
i.set("Authorization", "Basic " + btoa(c + ":" + f));
}
var l = ge(e.baseURL, e.url);
function d() {
if (u) {
var r = he.from(
"getAllResponseHeaders" in u && u.getAllResponseHeaders()
);
!(function (e, t, n) {
var r = n.config.validateStatus;
n.status && r && !r(n.status)
? t(
new M(
"Request failed with status code " + n.status,
[M.ERR_BAD_REQUEST, M.ERR_BAD_RESPONSE][
Math.floor(n.status / 100) - 4
],
n.config,
n.request,
n
)
)
: e(n);
})(
function (e) {
t(e), s();
},
function (e) {
n(e), s();
},
{
data:
a && "text" !== a && "json" !== a
? u.response
: u.responseText,
status: u.status,
statusText: u.statusText,
headers: r,
config: e,
request: u,
}
),
(u = null);
}
}
if (
(u.open(
e.method.toUpperCase(),
Y(l, e.params, e.paramsSerializer),
!0
),
(u.timeout = e.timeout),
"onloadend" in u
? (u.onloadend = d)
: (u.onreadystatechange = function () {
u &&
4 === u.readyState &&
(0 !== u.status ||
(u.responseURL &&
0 === u.responseURL.indexOf("file:"))) &&
setTimeout(d);
}),
(u.onabort = function () {
u &&
(n(new M("Request aborted", M.ECONNABORTED, e, u)), (u = null));
}),
(u.onerror = function () {
n(new M("Network Error", M.ERR_NETWORK, e, u)), (u = null);
}),
(u.ontimeout = function () {
var t = e.timeout
? "timeout of " + e.timeout + "ms exceeded"
: "timeout exceeded",
r = e.transitional || ne;
e.timeoutErrorMessage && (t = e.timeoutErrorMessage),
n(
new M(
t,
r.clarifyTimeoutError ? M.ETIMEDOUT : M.ECONNABORTED,
e,
u
)
),
(u = null);
}),
re.isStandardBrowserEnv)
) {
var p =
(e.withCredentials || we(l)) &&
e.xsrfCookieName &&
be.read(e.xsrfCookieName);
p && i.set(e.xsrfHeaderName, p);
}
void 0 === o && i.setContentType(null),
"setRequestHeader" in u &&
q.forEach(i.toJSON(), function (e, t) {
u.setRequestHeader(t, e);
}),
q.isUndefined(e.withCredentials) ||
(u.withCredentials = !!e.withCredentials),
a && "json" !== a && (u.responseType = e.responseType),
"function" == typeof e.onDownloadProgress &&
u.addEventListener("progress", Ee(e.onDownloadProgress, !0)),
"function" == typeof e.onUploadProgress &&
u.upload &&
u.upload.addEventListener("progress", Ee(e.onUploadProgress)),
(e.cancelToken || e.signal) &&
((r = function (t) {
u &&
(n(!t || t.type ? new ve(null, e, u) : t),
u.abort(),
(u = null));
}),
e.cancelToken && e.cancelToken.subscribe(r),
e.signal &&
(e.signal.aborted
? r()
: e.signal.addEventListener("abort", r)));
var h,
m = ((h = /^([-+\w]{1,25})(:?\/\/|:)/.exec(l)) && h[1]) || "";
m && -1 === re.protocols.indexOf(m)
? n(new M("Unsupported protocol " + m + ":", M.ERR_BAD_REQUEST, e))
: u.send(o || null);
});
},
};
q.forEach(Oe, function (e, t) {
if (e) {
try {
Object.defineProperty(e, "name", { value: t });
} catch (e) {}
Object.defineProperty(e, "adapterName", { value: t });
}
});
var Se = function (e) {
for (
var t, n, r = (e = q.isArray(e) ? e : [e]).length, o = 0;
o < r && ((t = e[o]), !(n = q.isString(t) ? Oe[t.toLowerCase()] : t));
o++
);
if (!n) {
if (!1 === n)
throw new M(
"Adapter ".concat(t, " is not supported by the environment"),
"ERR_NOT_SUPPORT"
);
throw new Error(
q.hasOwnProp(Oe, t)
? "Adapter '".concat(t, "' is not available in the build")
: "Unknown adapter '".concat(t, "'")
);
}
if (!q.isFunction(n)) throw new TypeError("adapter is not a function");
return n;
};
function Re(e) {
if (
(e.cancelToken && e.cancelToken.throwIfRequested(),
e.signal && e.signal.aborted)
)
throw new ve(null, e);
}
function Ae(e) {
return (
Re(e),
(e.headers = he.from(e.headers)),
(e.data = me.call(e, e.transformRequest)),
-1 !== ["post", "put", "patch"].indexOf(e.method) &&
e.headers.setContentType("application/x-www-form-urlencoded", !1),
Se(e.adapter || se.adapter)(e).then(
function (t) {
return (
Re(e),
(t.data = me.call(e, e.transformResponse, t)),
(t.headers = he.from(t.headers)),
t
);
},
function (t) {
return (
ye(t) ||
(Re(e),
t &&
t.response &&
((t.response.data = me.call(
e,
e.transformResponse,
t.response
)),
(t.response.headers = he.from(t.response.headers)))),
Promise.reject(t)
);
}
)
);
}
var Te = function (e) {
return e instanceof he ? e.toJSON() : e;
};
function je(e, t) {
t = t || {};
var n = {};
function r(e, t, n) {
return q.isPlainObject(e) && q.isPlainObject(t)
? q.merge.call({ caseless: n }, e, t)
: q.isPlainObject(t)
? q.merge({}, t)
: q.isArray(t)
? t.slice()
: t;
}
function o(e, t, n) {
return q.isUndefined(t)
? q.isUndefined(e)
? void 0
: r(void 0, e, n)
: r(e, t, n);
}
function i(e, t) {
if (!q.isUndefined(t)) return r(void 0, t);
}
function a(e, t) {
return q.isUndefined(t)
? q.isUndefined(e)
? void 0
: r(void 0, e)
: r(void 0, t);
}
function s(n, o, i) {
return i in t ? r(n, o) : i in e ? r(void 0, n) : void 0;
}
var u = {
url: i,
method: i,
data: i,
baseURL: a,
transformRequest: a,
transformResponse: a,
paramsSerializer: a,
timeout: a,
timeoutMessage: a,
withCredentials: a,
adapter: a,
responseType: a,
xsrfCookieName: a,
xsrfHeaderName: a,
onUploadProgress: a,
onDownloadProgress: a,
decompress: a,
maxContentLength: a,
maxBodyLength: a,
beforeRedirect: a,
transport: a,
httpAgent: a,
httpsAgent: a,
cancelToken: a,
socketPath: a,
responseEncoding: a,
validateStatus: s,
headers: function (e, t) {
return o(Te(e), Te(t), !0);
},
};
return (
q.forEach(Object.keys(Object.assign({}, e, t)), function (r) {
var i = u[r] || o,
a = i(e[r], t[r], r);
(q.isUndefined(a) && i !== s) || (n[r] = a);
}),
n
);
}
var Ne = "1.4.0",
Ce = {};
["object", "boolean", "number", "function", "string", "symbol"].forEach(
function (t, n) {
Ce[t] = function (r) {
return e(r) === t || "a" + (n < 1 ? "n " : " ") + t;
};
}
);
var xe = {};
Ce.transitional = function (e, t, n) {
function r(e, t) {
return (
"[Axios v1.4.0] Transitional option '" +
e +
"'" +
t +
(n ? ". " + n : "")
);
}
return function (n, o, i) {
if (!1 === e)
throw new M(
r(o, " has been removed" + (t ? " in " + t : "")),
M.ERR_DEPRECATED
);
return (
t &&
!xe[o] &&
((xe[o] = !0),
console.warn(
r(
o,
" has been deprecated since v" +
t +
" and will be removed in the near future"
)
)),
!e || e(n, o, i)
);
};
};
var Pe = {
assertOptions: function (t, n, r) {
if ("object" !== e(t))
throw new M("options must be an object", M.ERR_BAD_OPTION_VALUE);
for (var o = Object.keys(t), i = o.length; i-- > 0; ) {
var a = o[i],
s = n[a];
if (s) {
var u = t[a],
c = void 0 === u || s(u, a, t);
if (!0 !== c)
throw new M(
"option " + a + " must be " + c,
M.ERR_BAD_OPTION_VALUE
);
} else if (!0 !== r)
throw new M("Unknown option " + a, M.ERR_BAD_OPTION);
}
},
validators: Ce,
},
ke = Pe.validators,
Ue = (function () {
function e(n) {
t(this, e),
(this.defaults = n),
(this.interceptors = { request: new te(), response: new te() });
}
return (
r(e, [
{
key: "request",
value: function (e, t) {
"string" == typeof e ? ((t = t || {}).url = e) : (t = e || {});
var n,
r = (t = je(this.defaults, t)),
o = r.transitional,
i = r.paramsSerializer,
a = r.headers;
void 0 !== o &&
Pe.assertOptions(
o,
{
silentJSONParsing: ke.transitional(ke.boolean),
forcedJSONParsing: ke.transitional(ke.boolean),
clarifyTimeoutError: ke.transitional(ke.boolean),
},
!1
),
null != i &&
(q.isFunction(i)
? (t.paramsSerializer = { serialize: i })
: Pe.assertOptions(
i,
{ encode: ke.function, serialize: ke.function },
!0
)),
(t.method = (
t.method ||
this.defaults.method ||
"get"
).toLowerCase()),
(n = a && q.merge(a.common, a[t.method])) &&
q.forEach(
["delete", "get", "head", "post", "put", "patch", "common"],
function (e) {
delete a[e];
}
),
(t.headers = he.concat(n, a));
var s = [],
u = !0;
this.interceptors.request.forEach(function (e) {
("function" == typeof e.runWhen && !1 === e.runWhen(t)) ||
((u = u && e.synchronous),
s.unshift(e.fulfilled, e.rejected));
});
var c,
f = [];
this.interceptors.response.forEach(function (e) {
f.push(e.fulfilled, e.rejected);
});
var l,
d = 0;
if (!u) {
var p = [Ae.bind(this), void 0];
for (
p.unshift.apply(p, s),
p.push.apply(p, f),
l = p.length,
c = Promise.resolve(t);
d < l;
)
c = c.then(p[d++], p[d++]);
return c;
}
l = s.length;
var h = t;
for (d = 0; d < l; ) {
var m = s[d++],
y = s[d++];
try {
h = m(h);
} catch (e) {
y.call(this, e);
break;
}
}
try {
c = Ae.call(this, h);
} catch (e) {
return Promise.reject(e);
}
for (d = 0, l = f.length; d < l; ) c = c.then(f[d++], f[d++]);
return c;
},
},
{
key: "getUri",
value: function (e) {
return Y(
ge((e = je(this.defaults, e)).baseURL, e.url),
e.params,
e.paramsSerializer
);
},
},
]),
e
);
})();
q.forEach(["delete", "get", "head", "options"], function (e) {
Ue.prototype[e] = function (t, n) {
return this.request(
je(n || {}, { method: e, url: t, data: (n || {}).data })
);
};
}),
q.forEach(["post", "put", "patch"], function (e) {
function t(t) {
return function (n, r, o) {
return this.request(
je(o || {}, {
method: e,
headers: t ? { "Content-Type": "multipart/form-data" } : {},
url: n,
data: r,
})
);
};
}
(Ue.prototype[e] = t()), (Ue.prototype[e + "Form"] = t(!0));
});
var _e = Ue,
Fe = (function () {
function e(n) {
if ((t(this, e), "function" != typeof n))
throw new TypeError("executor must be a function.");
var r;
this.promise = new Promise(function (e) {
r = e;
});
var o = this;
this.promise.then(function (e) {
if (o._listeners) {
for (var t = o._listeners.length; t-- > 0; ) o._listeners[t](e);
o._listeners = null;
}
}),
(this.promise.then = function (e) {
var t,
n = new Promise(function (e) {
o.subscribe(e), (t = e);
}).then(e);
return (
(n.cancel = function () {
o.unsubscribe(t);
}),
n
);
}),
n(function (e, t, n) {
o.reason || ((o.reason = new ve(e, t, n)), r(o.reason));
});
}
return (
r(
e,
[
{
key: "throwIfRequested",
value: function () {
if (this.reason) throw this.reason;
},
},
{
key: "subscribe",
value: function (e) {
this.reason
? e(this.reason)
: this._listeners
? this._listeners.push(e)
: (this._listeners = [e]);
},
},
{
key: "unsubscribe",
value: function (e) {
if (this._listeners) {
var t = this._listeners.indexOf(e);
-1 !== t && this._listeners.splice(t, 1);
}
},
},
],
[
{
key: "source",
value: function () {
var t;
return {
token: new e(function (e) {
t = e;
}),
cancel: t,
};
},
},
]
),
e
);
})();
var Be = {
Continue: 100,
SwitchingProtocols: 101,
Processing: 102,
EarlyHints: 103,
Ok: 200,
Created: 201,
Accepted: 202,
NonAuthoritativeInformation: 203,
NoContent: 204,
ResetContent: 205,
PartialContent: 206,
MultiStatus: 207,
AlreadyReported: 208,
ImUsed: 226,
MultipleChoices: 300,
MovedPermanently: 301,
Found: 302,
SeeOther: 303,
NotModified: 304,
UseProxy: 305,
Unused: 306,
TemporaryRedirect: 307,
PermanentRedirect: 308,
BadRequest: 400,
Unauthorized: 401,
PaymentRequired: 402,
Forbidden: 403,
NotFound: 404,
MethodNotAllowed: 405,
NotAcceptable: 406,
ProxyAuthenticationRequired: 407,
RequestTimeout: 408,
Conflict: 409,
Gone: 410,
LengthRequired: 411,
PreconditionFailed: 412,
PayloadTooLarge: 413,
UriTooLong: 414,
UnsupportedMediaType: 415,
RangeNotSatisfiable: 416,
ExpectationFailed: 417,
ImATeapot: 418,
MisdirectedRequest: 421,
UnprocessableEntity: 422,
Locked: 423,
FailedDependency: 424,
TooEarly: 425,
UpgradeRequired: 426,
PreconditionRequired: 428,
TooManyRequests: 429,
RequestHeaderFieldsTooLarge: 431,
UnavailableForLegalReasons: 451,
InternalServerError: 500,
NotImplemented: 501,
BadGateway: 502,
ServiceUnavailable: 503,
GatewayTimeout: 504,
HttpVersionNotSupported: 505,
VariantAlsoNegotiates: 506,
InsufficientStorage: 507,
LoopDetected: 508,
NotExtended: 510,
NetworkAuthenticationRequired: 511,
};
Object.entries(Be).forEach(function (e) {
var t = o(e, 2),
n = t[0],
r = t[1];
Be[r] = n;
});
var Le = Be;
var De = (function e(t) {
var n = new _e(t),
r = a(_e.prototype.request, n);
return (
q.extend(r, _e.prototype, n, { allOwnKeys: !0 }),
q.extend(r, n, null, { allOwnKeys: !0 }),
(r.create = function (n) {
return e(je(t, n));
}),
r
);
})(se);
return (
(De.Axios = _e),
(De.CanceledError = ve),
(De.CancelToken = Fe),
(De.isCancel = ye),
(De.VERSION = Ne),
(De.toFormData = G),
(De.AxiosError = M),
(De.Cancel = De.CanceledError),
(De.all = function (e) {
return Promise.all(e);
}),
(De.spread = function (e) {
return function (t) {
return e.apply(null, t);
};
}),
(De.isAxiosError = function (e) {
return q.isObject(e) && !0 === e.isAxiosError;
}),
(De.mergeConfig = je),
(De.AxiosHeaders = he),
(De.formToJSON = function (e) {
return oe(q.isHTMLForm(e) ? new FormData(e) : e);
}),
(De.HttpStatusCode = Le),
(De.default = De),
De
);
});
This source diff could not be displayed because it is too large. You can view the blob instead.
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.moment=t()}(this,function(){"use strict";var H;function f(){return H.apply(null,arguments)}function a(e){return e instanceof Array||"[object Array]"===Object.prototype.toString.call(e)}function F(e){return null!=e&&"[object Object]"===Object.prototype.toString.call(e)}function c(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function L(e){if(Object.getOwnPropertyNames)return 0===Object.getOwnPropertyNames(e).length;for(var t in e)if(c(e,t))return;return 1}function o(e){return void 0===e}function u(e){return"number"==typeof e||"[object Number]"===Object.prototype.toString.call(e)}function V(e){return e instanceof Date||"[object Date]"===Object.prototype.toString.call(e)}function G(e,t){for(var n=[],s=e.length,i=0;i<s;++i)n.push(t(e[i],i));return n}function E(e,t){for(var n in t)c(t,n)&&(e[n]=t[n]);return c(t,"toString")&&(e.toString=t.toString),c(t,"valueOf")&&(e.valueOf=t.valueOf),e}function l(e,t,n,s){return Wt(e,t,n,s,!0).utc()}function m(e){return null==e._pf&&(e._pf={empty:!1,unusedTokens:[],unusedInput:[],overflow:-2,charsLeftOver:0,nullInput:!1,invalidEra:null,invalidMonth:null,invalidFormat:!1,userInvalidated:!1,iso:!1,parsedDateParts:[],era:null,meridiem:null,rfc2822:!1,weekdayMismatch:!1}),e._pf}function A(e){var t,n,s=e._d&&!isNaN(e._d.getTime());return s&&(t=m(e),n=j.call(t.parsedDateParts,function(e){return null!=e}),s=t.overflow<0&&!t.empty&&!t.invalidEra&&!t.invalidMonth&&!t.invalidWeekday&&!t.weekdayMismatch&&!t.nullInput&&!t.invalidFormat&&!t.userInvalidated&&(!t.meridiem||t.meridiem&&n),e._strict&&(s=s&&0===t.charsLeftOver&&0===t.unusedTokens.length&&void 0===t.bigHour)),null!=Object.isFrozen&&Object.isFrozen(e)?s:(e._isValid=s,e._isValid)}function I(e){var t=l(NaN);return null!=e?E(m(t),e):m(t).userInvalidated=!0,t}var j=Array.prototype.some||function(e){for(var t=Object(this),n=t.length>>>0,s=0;s<n;s++)if(s in t&&e.call(this,t[s],s,t))return!0;return!1},Z=f.momentProperties=[],z=!1;function q(e,t){var n,s,i,r=Z.length;if(o(t._isAMomentObject)||(e._isAMomentObject=t._isAMomentObject),o(t._i)||(e._i=t._i),o(t._f)||(e._f=t._f),o(t._l)||(e._l=t._l),o(t._strict)||(e._strict=t._strict),o(t._tzm)||(e._tzm=t._tzm),o(t._isUTC)||(e._isUTC=t._isUTC),o(t._offset)||(e._offset=t._offset),o(t._pf)||(e._pf=m(t)),o(t._locale)||(e._locale=t._locale),0<r)for(n=0;n<r;n++)o(i=t[s=Z[n]])||(e[s]=i);return e}function $(e){q(this,e),this._d=new Date(null!=e._d?e._d.getTime():NaN),this.isValid()||(this._d=new Date(NaN)),!1===z&&(z=!0,f.updateOffset(this),z=!1)}function d(e){return e instanceof $||null!=e&&null!=e._isAMomentObject}function B(e){!1===f.suppressDeprecationWarnings&&"undefined"!=typeof console&&console.warn&&console.warn("Deprecation warning: "+e)}function e(r,a){var o=!0;return E(function(){if(null!=f.deprecationHandler&&f.deprecationHandler(null,r),o){for(var e,t,n=[],s=arguments.length,i=0;i<s;i++){if(e="","object"==typeof arguments[i]){for(t in e+="\n["+i+"] ",arguments[0])c(arguments[0],t)&&(e+=t+": "+arguments[0][t]+", ");e=e.slice(0,-2)}else e=arguments[i];n.push(e)}B(r+"\nArguments: "+Array.prototype.slice.call(n).join("")+"\n"+(new Error).stack),o=!1}return a.apply(this,arguments)},a)}var J={};function Q(e,t){null!=f.deprecationHandler&&f.deprecationHandler(e,t),J[e]||(B(t),J[e]=!0)}function h(e){return"undefined"!=typeof Function&&e instanceof Function||"[object Function]"===Object.prototype.toString.call(e)}function X(e,t){var n,s=E({},e);for(n in t)c(t,n)&&(F(e[n])&&F(t[n])?(s[n]={},E(s[n],e[n]),E(s[n],t[n])):null!=t[n]?s[n]=t[n]:delete s[n]);for(n in e)c(e,n)&&!c(t,n)&&F(e[n])&&(s[n]=E({},s[n]));return s}function K(e){null!=e&&this.set(e)}f.suppressDeprecationWarnings=!1,f.deprecationHandler=null;var ee=Object.keys||function(e){var t,n=[];for(t in e)c(e,t)&&n.push(t);return n};function r(e,t,n){var s=""+Math.abs(e);return(0<=e?n?"+":"":"-")+Math.pow(10,Math.max(0,t-s.length)).toString().substr(1)+s}var te=/(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Qo?|N{1,5}|YYYYYY|YYYYY|YYYY|YY|y{2,4}|yo?|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|kk?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g,ne=/(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g,se={},ie={};function s(e,t,n,s){var i="string"==typeof s?function(){return this[s]()}:s;e&&(ie[e]=i),t&&(ie[t[0]]=function(){return r(i.apply(this,arguments),t[1],t[2])}),n&&(ie[n]=function(){return this.localeData().ordinal(i.apply(this,arguments),e)})}function re(e,t){return e.isValid()?(t=ae(t,e.localeData()),se[t]=se[t]||function(s){for(var e,i=s.match(te),t=0,r=i.length;t<r;t++)ie[i[t]]?i[t]=ie[i[t]]:i[t]=(e=i[t]).match(/\[[\s\S]/)?e.replace(/^\[|\]$/g,""):e.replace(/\\/g,"");return function(e){for(var t="",n=0;n<r;n++)t+=h(i[n])?i[n].call(e,s):i[n];return t}}(t),se[t](e)):e.localeData().invalidDate()}function ae(e,t){var n=5;function s(e){return t.longDateFormat(e)||e}for(ne.lastIndex=0;0<=n&&ne.test(e);)e=e.replace(ne,s),ne.lastIndex=0,--n;return e}var oe={D:"date",dates:"date",date:"date",d:"day",days:"day",day:"day",e:"weekday",weekdays:"weekday",weekday:"weekday",E:"isoWeekday",isoweekdays:"isoWeekday",isoweekday:"isoWeekday",DDD:"dayOfYear",dayofyears:"dayOfYear",dayofyear:"dayOfYear",h:"hour",hours:"hour",hour:"hour",ms:"millisecond",milliseconds:"millisecond",millisecond:"millisecond",m:"minute",minutes:"minute",minute:"minute",M:"month",months:"month",month:"month",Q:"quarter",quarters:"quarter",quarter:"quarter",s:"second",seconds:"second",second:"second",gg:"weekYear",weekyears:"weekYear",weekyear:"weekYear",GG:"isoWeekYear",isoweekyears:"isoWeekYear",isoweekyear:"isoWeekYear",w:"week",weeks:"week",week:"week",W:"isoWeek",isoweeks:"isoWeek",isoweek:"isoWeek",y:"year",years:"year",year:"year"};function _(e){return"string"==typeof e?oe[e]||oe[e.toLowerCase()]:void 0}function ue(e){var t,n,s={};for(n in e)c(e,n)&&(t=_(n))&&(s[t]=e[n]);return s}var le={date:9,day:11,weekday:11,isoWeekday:11,dayOfYear:4,hour:13,millisecond:16,minute:14,month:8,quarter:7,second:15,weekYear:1,isoWeekYear:1,week:5,isoWeek:5,year:1};var de=/\d/,t=/\d\d/,he=/\d{3}/,ce=/\d{4}/,fe=/[+-]?\d{6}/,n=/\d\d?/,me=/\d\d\d\d?/,_e=/\d\d\d\d\d\d?/,ye=/\d{1,3}/,ge=/\d{1,4}/,we=/[+-]?\d{1,6}/,pe=/\d+/,ke=/[+-]?\d+/,Me=/Z|[+-]\d\d:?\d\d/gi,ve=/Z|[+-]\d\d(?::?\d\d)?/gi,i=/[0-9]{0,256}['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFF07\uFF10-\uFFEF]{1,256}|[\u0600-\u06FF\/]{1,256}(\s*?[\u0600-\u06FF]{1,256}){1,2}/i,y=/^[1-9]\d?/,g=/^([1-9]\d|\d)/;function w(e,n,s){Ye[e]=h(n)?n:function(e,t){return e&&s?s:n}}function De(e,t){return c(Ye,e)?Ye[e](t._strict,t._locale):new RegExp(p(e.replace("\\","").replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(e,t,n,s,i){return t||n||s||i})))}function p(e){return e.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function k(e){return e<0?Math.ceil(e)||0:Math.floor(e)}function M(e){var e=+e,t=0;return t=0!=e&&isFinite(e)?k(e):t}var Ye={},Se={};function v(e,n){var t,s,i=n;for("string"==typeof e&&(e=[e]),u(n)&&(i=function(e,t){t[n]=M(e)}),s=e.length,t=0;t<s;t++)Se[e[t]]=i}function Oe(e,i){v(e,function(e,t,n,s){n._w=n._w||{},i(e,n._w,n,s)})}function be(e){return e%4==0&&e%100!=0||e%400==0}var D=0,Y=1,S=2,O=3,b=4,T=5,Te=6,xe=7,Ne=8;function We(e){return be(e)?366:365}s("Y",0,0,function(){var e=this.year();return e<=9999?r(e,4):"+"+e}),s(0,["YY",2],0,function(){return this.year()%100}),s(0,["YYYY",4],0,"year"),s(0,["YYYYY",5],0,"year"),s(0,["YYYYYY",6,!0],0,"year"),w("Y",ke),w("YY",n,t),w("YYYY",ge,ce),w("YYYYY",we,fe),w("YYYYYY",we,fe),v(["YYYYY","YYYYYY"],D),v("YYYY",function(e,t){t[D]=2===e.length?f.parseTwoDigitYear(e):M(e)}),v("YY",function(e,t){t[D]=f.parseTwoDigitYear(e)}),v("Y",function(e,t){t[D]=parseInt(e,10)}),f.parseTwoDigitYear=function(e){return M(e)+(68<M(e)?1900:2e3)};var x,Pe=Re("FullYear",!0);function Re(t,n){return function(e){return null!=e?(Ue(this,t,e),f.updateOffset(this,n),this):Ce(this,t)}}function Ce(e,t){if(!e.isValid())return NaN;var n=e._d,s=e._isUTC;switch(t){case"Milliseconds":return s?n.getUTCMilliseconds():n.getMilliseconds();case"Seconds":return s?n.getUTCSeconds():n.getSeconds();case"Minutes":return s?n.getUTCMinutes():n.getMinutes();case"Hours":return s?n.getUTCHours():n.getHours();case"Date":return s?n.getUTCDate():n.getDate();case"Day":return s?n.getUTCDay():n.getDay();case"Month":return s?n.getUTCMonth():n.getMonth();case"FullYear":return s?n.getUTCFullYear():n.getFullYear();default:return NaN}}function Ue(e,t,n){var s,i,r;if(e.isValid()&&!isNaN(n)){switch(s=e._d,i=e._isUTC,t){case"Milliseconds":return i?s.setUTCMilliseconds(n):s.setMilliseconds(n);case"Seconds":return i?s.setUTCSeconds(n):s.setSeconds(n);case"Minutes":return i?s.setUTCMinutes(n):s.setMinutes(n);case"Hours":return i?s.setUTCHours(n):s.setHours(n);case"Date":return i?s.setUTCDate(n):s.setDate(n);case"FullYear":break;default:return}t=n,r=e.month(),e=29!==(e=e.date())||1!==r||be(t)?e:28,i?s.setUTCFullYear(t,r,e):s.setFullYear(t,r,e)}}function He(e,t){if(isNaN(e)||isNaN(t))return NaN;var n=(t%(n=12)+n)%n;return e+=(t-n)/12,1==n?be(e)?29:28:31-n%7%2}x=Array.prototype.indexOf||function(e){for(var t=0;t<this.length;++t)if(this[t]===e)return t;return-1},s("M",["MM",2],"Mo",function(){return this.month()+1}),s("MMM",0,0,function(e){return this.localeData().monthsShort(this,e)}),s("MMMM",0,0,function(e){return this.localeData().months(this,e)}),w("M",n,y),w("MM",n,t),w("MMM",function(e,t){return t.monthsShortRegex(e)}),w("MMMM",function(e,t){return t.monthsRegex(e)}),v(["M","MM"],function(e,t){t[Y]=M(e)-1}),v(["MMM","MMMM"],function(e,t,n,s){s=n._locale.monthsParse(e,s,n._strict);null!=s?t[Y]=s:m(n).invalidMonth=e});var Fe="January_February_March_April_May_June_July_August_September_October_November_December".split("_"),Le="Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),Ve=/D[oD]?(\[[^\[\]]*\]|\s)+MMMM?/,Ge=i,Ee=i;function Ae(e,t){if(e.isValid()){if("string"==typeof t)if(/^\d+$/.test(t))t=M(t);else if(!u(t=e.localeData().monthsParse(t)))return;var n=(n=e.date())<29?n:Math.min(n,He(e.year(),t));e._isUTC?e._d.setUTCMonth(t,n):e._d.setMonth(t,n)}}function Ie(e){return null!=e?(Ae(this,e),f.updateOffset(this,!0),this):Ce(this,"Month")}function je(){function e(e,t){return t.length-e.length}for(var t,n,s=[],i=[],r=[],a=0;a<12;a++)n=l([2e3,a]),t=p(this.monthsShort(n,"")),n=p(this.months(n,"")),s.push(t),i.push(n),r.push(n),r.push(t);s.sort(e),i.sort(e),r.sort(e),this._monthsRegex=new RegExp("^("+r.join("|")+")","i"),this._monthsShortRegex=this._monthsRegex,this._monthsStrictRegex=new RegExp("^("+i.join("|")+")","i"),this._monthsShortStrictRegex=new RegExp("^("+s.join("|")+")","i")}function Ze(e,t,n,s,i,r,a){var o;return e<100&&0<=e?(o=new Date(e+400,t,n,s,i,r,a),isFinite(o.getFullYear())&&o.setFullYear(e)):o=new Date(e,t,n,s,i,r,a),o}function ze(e){var t;return e<100&&0<=e?((t=Array.prototype.slice.call(arguments))[0]=e+400,t=new Date(Date.UTC.apply(null,t)),isFinite(t.getUTCFullYear())&&t.setUTCFullYear(e)):t=new Date(Date.UTC.apply(null,arguments)),t}function qe(e,t,n){n=7+t-n;return n-(7+ze(e,0,n).getUTCDay()-t)%7-1}function $e(e,t,n,s,i){var r,t=1+7*(t-1)+(7+n-s)%7+qe(e,s,i),n=t<=0?We(r=e-1)+t:t>We(e)?(r=e+1,t-We(e)):(r=e,t);return{year:r,dayOfYear:n}}function Be(e,t,n){var s,i,r=qe(e.year(),t,n),r=Math.floor((e.dayOfYear()-r-1)/7)+1;return r<1?s=r+N(i=e.year()-1,t,n):r>N(e.year(),t,n)?(s=r-N(e.year(),t,n),i=e.year()+1):(i=e.year(),s=r),{week:s,year:i}}function N(e,t,n){var s=qe(e,t,n),t=qe(e+1,t,n);return(We(e)-s+t)/7}s("w",["ww",2],"wo","week"),s("W",["WW",2],"Wo","isoWeek"),w("w",n,y),w("ww",n,t),w("W",n,y),w("WW",n,t),Oe(["w","ww","W","WW"],function(e,t,n,s){t[s.substr(0,1)]=M(e)});function Je(e,t){return e.slice(t,7).concat(e.slice(0,t))}s("d",0,"do","day"),s("dd",0,0,function(e){return this.localeData().weekdaysMin(this,e)}),s("ddd",0,0,function(e){return this.localeData().weekdaysShort(this,e)}),s("dddd",0,0,function(e){return this.localeData().weekdays(this,e)}),s("e",0,0,"weekday"),s("E",0,0,"isoWeekday"),w("d",n),w("e",n),w("E",n),w("dd",function(e,t){return t.weekdaysMinRegex(e)}),w("ddd",function(e,t){return t.weekdaysShortRegex(e)}),w("dddd",function(e,t){return t.weekdaysRegex(e)}),Oe(["dd","ddd","dddd"],function(e,t,n,s){s=n._locale.weekdaysParse(e,s,n._strict);null!=s?t.d=s:m(n).invalidWeekday=e}),Oe(["d","e","E"],function(e,t,n,s){t[s]=M(e)});var Qe="Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),Xe="Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),Ke="Su_Mo_Tu_We_Th_Fr_Sa".split("_"),et=i,tt=i,nt=i;function st(){function e(e,t){return t.length-e.length}for(var t,n,s,i=[],r=[],a=[],o=[],u=0;u<7;u++)s=l([2e3,1]).day(u),t=p(this.weekdaysMin(s,"")),n=p(this.weekdaysShort(s,"")),s=p(this.weekdays(s,"")),i.push(t),r.push(n),a.push(s),o.push(t),o.push(n),o.push(s);i.sort(e),r.sort(e),a.sort(e),o.sort(e),this._weekdaysRegex=new RegExp("^("+o.join("|")+")","i"),this._weekdaysShortRegex=this._weekdaysRegex,this._weekdaysMinRegex=this._weekdaysRegex,this._weekdaysStrictRegex=new RegExp("^("+a.join("|")+")","i"),this._weekdaysShortStrictRegex=new RegExp("^("+r.join("|")+")","i"),this._weekdaysMinStrictRegex=new RegExp("^("+i.join("|")+")","i")}function it(){return this.hours()%12||12}function rt(e,t){s(e,0,0,function(){return this.localeData().meridiem(this.hours(),this.minutes(),t)})}function at(e,t){return t._meridiemParse}s("H",["HH",2],0,"hour"),s("h",["hh",2],0,it),s("k",["kk",2],0,function(){return this.hours()||24}),s("hmm",0,0,function(){return""+it.apply(this)+r(this.minutes(),2)}),s("hmmss",0,0,function(){return""+it.apply(this)+r(this.minutes(),2)+r(this.seconds(),2)}),s("Hmm",0,0,function(){return""+this.hours()+r(this.minutes(),2)}),s("Hmmss",0,0,function(){return""+this.hours()+r(this.minutes(),2)+r(this.seconds(),2)}),rt("a",!0),rt("A",!1),w("a",at),w("A",at),w("H",n,g),w("h",n,y),w("k",n,y),w("HH",n,t),w("hh",n,t),w("kk",n,t),w("hmm",me),w("hmmss",_e),w("Hmm",me),w("Hmmss",_e),v(["H","HH"],O),v(["k","kk"],function(e,t,n){e=M(e);t[O]=24===e?0:e}),v(["a","A"],function(e,t,n){n._isPm=n._locale.isPM(e),n._meridiem=e}),v(["h","hh"],function(e,t,n){t[O]=M(e),m(n).bigHour=!0}),v("hmm",function(e,t,n){var s=e.length-2;t[O]=M(e.substr(0,s)),t[b]=M(e.substr(s)),m(n).bigHour=!0}),v("hmmss",function(e,t,n){var s=e.length-4,i=e.length-2;t[O]=M(e.substr(0,s)),t[b]=M(e.substr(s,2)),t[T]=M(e.substr(i)),m(n).bigHour=!0}),v("Hmm",function(e,t,n){var s=e.length-2;t[O]=M(e.substr(0,s)),t[b]=M(e.substr(s))}),v("Hmmss",function(e,t,n){var s=e.length-4,i=e.length-2;t[O]=M(e.substr(0,s)),t[b]=M(e.substr(s,2)),t[T]=M(e.substr(i))});i=Re("Hours",!0);var ot,ut={calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},longDateFormat:{LTS:"h:mm:ss A",LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D, YYYY",LLL:"MMMM D, YYYY h:mm A",LLLL:"dddd, MMMM D, YYYY h:mm A"},invalidDate:"Invalid date",ordinal:"%d",dayOfMonthOrdinalParse:/\d{1,2}/,relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",ss:"%d seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",w:"a week",ww:"%d weeks",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},months:Fe,monthsShort:Le,week:{dow:0,doy:6},weekdays:Qe,weekdaysMin:Ke,weekdaysShort:Xe,meridiemParse:/[ap]\.?m?\.?/i},W={},lt={};function dt(e){return e&&e.toLowerCase().replace("_","-")}function ht(e){for(var t,n,s,i,r=0;r<e.length;){for(t=(i=dt(e[r]).split("-")).length,n=(n=dt(e[r+1]))?n.split("-"):null;0<t;){if(s=ct(i.slice(0,t).join("-")))return s;if(n&&n.length>=t&&function(e,t){for(var n=Math.min(e.length,t.length),s=0;s<n;s+=1)if(e[s]!==t[s])return s;return n}(i,n)>=t-1)break;t--}r++}return ot}function ct(t){var e,n;if(void 0===W[t]&&"undefined"!=typeof module&&module&&module.exports&&((n=t)&&n.match("^[^/\\\\]*$")))try{e=ot._abbr,require("./locale/"+t),ft(e)}catch(e){W[t]=null}return W[t]}function ft(e,t){return e&&((t=o(t)?P(e):mt(e,t))?ot=t:"undefined"!=typeof console&&console.warn&&console.warn("Locale "+e+" not found. Did you forget to load it?")),ot._abbr}function mt(e,t){if(null===t)return delete W[e],null;var n,s=ut;if(t.abbr=e,null!=W[e])Q("defineLocaleOverride","use moment.updateLocale(localeName, config) to change an existing locale. moment.defineLocale(localeName, config) should only be used for creating a new locale See http://momentjs.com/guides/#/warnings/define-locale/ for more info."),s=W[e]._config;else if(null!=t.parentLocale)if(null!=W[t.parentLocale])s=W[t.parentLocale]._config;else{if(null==(n=ct(t.parentLocale)))return lt[t.parentLocale]||(lt[t.parentLocale]=[]),lt[t.parentLocale].push({name:e,config:t}),null;s=n._config}return W[e]=new K(X(s,t)),lt[e]&&lt[e].forEach(function(e){mt(e.name,e.config)}),ft(e),W[e]}function P(e){var t;if(!(e=e&&e._locale&&e._locale._abbr?e._locale._abbr:e))return ot;if(!a(e)){if(t=ct(e))return t;e=[e]}return ht(e)}function _t(e){var t=e._a;return t&&-2===m(e).overflow&&(t=t[Y]<0||11<t[Y]?Y:t[S]<1||t[S]>He(t[D],t[Y])?S:t[O]<0||24<t[O]||24===t[O]&&(0!==t[b]||0!==t[T]||0!==t[Te])?O:t[b]<0||59<t[b]?b:t[T]<0||59<t[T]?T:t[Te]<0||999<t[Te]?Te:-1,m(e)._overflowDayOfYear&&(t<D||S<t)&&(t=S),m(e)._overflowWeeks&&-1===t&&(t=xe),m(e)._overflowWeekday&&-1===t&&(t=Ne),m(e).overflow=t),e}var yt=/^\s*((?:[+-]\d{6}|\d{4})-(?:\d\d-\d\d|W\d\d-\d|W\d\d|\d\d\d|\d\d))(?:(T| )(\d\d(?::\d\d(?::\d\d(?:[.,]\d+)?)?)?)([+-]\d\d(?::?\d\d)?|\s*Z)?)?$/,gt=/^\s*((?:[+-]\d{6}|\d{4})(?:\d\d\d\d|W\d\d\d|W\d\d|\d\d\d|\d\d|))(?:(T| )(\d\d(?:\d\d(?:\d\d(?:[.,]\d+)?)?)?)([+-]\d\d(?::?\d\d)?|\s*Z)?)?$/,wt=/Z|[+-]\d\d(?::?\d\d)?/,pt=[["YYYYYY-MM-DD",/[+-]\d{6}-\d\d-\d\d/],["YYYY-MM-DD",/\d{4}-\d\d-\d\d/],["GGGG-[W]WW-E",/\d{4}-W\d\d-\d/],["GGGG-[W]WW",/\d{4}-W\d\d/,!1],["YYYY-DDD",/\d{4}-\d{3}/],["YYYY-MM",/\d{4}-\d\d/,!1],["YYYYYYMMDD",/[+-]\d{10}/],["YYYYMMDD",/\d{8}/],["GGGG[W]WWE",/\d{4}W\d{3}/],["GGGG[W]WW",/\d{4}W\d{2}/,!1],["YYYYDDD",/\d{7}/],["YYYYMM",/\d{6}/,!1],["YYYY",/\d{4}/,!1]],kt=[["HH:mm:ss.SSSS",/\d\d:\d\d:\d\d\.\d+/],["HH:mm:ss,SSSS",/\d\d:\d\d:\d\d,\d+/],["HH:mm:ss",/\d\d:\d\d:\d\d/],["HH:mm",/\d\d:\d\d/],["HHmmss.SSSS",/\d\d\d\d\d\d\.\d+/],["HHmmss,SSSS",/\d\d\d\d\d\d,\d+/],["HHmmss",/\d\d\d\d\d\d/],["HHmm",/\d\d\d\d/],["HH",/\d\d/]],Mt=/^\/?Date\((-?\d+)/i,vt=/^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\d{4}))$/,Dt={UT:0,GMT:0,EDT:-240,EST:-300,CDT:-300,CST:-360,MDT:-360,MST:-420,PDT:-420,PST:-480};function Yt(e){var t,n,s,i,r,a,o=e._i,u=yt.exec(o)||gt.exec(o),o=pt.length,l=kt.length;if(u){for(m(e).iso=!0,t=0,n=o;t<n;t++)if(pt[t][1].exec(u[1])){i=pt[t][0],s=!1!==pt[t][2];break}if(null==i)e._isValid=!1;else{if(u[3]){for(t=0,n=l;t<n;t++)if(kt[t][1].exec(u[3])){r=(u[2]||" ")+kt[t][0];break}if(null==r)return void(e._isValid=!1)}if(s||null==r){if(u[4]){if(!wt.exec(u[4]))return void(e._isValid=!1);a="Z"}e._f=i+(r||"")+(a||""),xt(e)}else e._isValid=!1}}else e._isValid=!1}function St(e,t,n,s,i,r){e=[function(e){e=parseInt(e,10);{if(e<=49)return 2e3+e;if(e<=999)return 1900+e}return e}(e),Le.indexOf(t),parseInt(n,10),parseInt(s,10),parseInt(i,10)];return r&&e.push(parseInt(r,10)),e}function Ot(e){var t,n,s,i,r=vt.exec(e._i.replace(/\([^()]*\)|[\n\t]/g," ").replace(/(\s\s+)/g," ").replace(/^\s\s*/,"").replace(/\s\s*$/,""));r?(t=St(r[4],r[3],r[2],r[5],r[6],r[7]),n=r[1],s=t,i=e,n&&Xe.indexOf(n)!==new Date(s[0],s[1],s[2]).getDay()?(m(i).weekdayMismatch=!0,i._isValid=!1):(e._a=t,e._tzm=(n=r[8],s=r[9],i=r[10],n?Dt[n]:s?0:60*(((n=parseInt(i,10))-(s=n%100))/100)+s),e._d=ze.apply(null,e._a),e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),m(e).rfc2822=!0)):e._isValid=!1}function bt(e,t,n){return null!=e?e:null!=t?t:n}function Tt(e){var t,n,s,i,r,a,o,u,l,d,h,c=[];if(!e._d){for(s=e,i=new Date(f.now()),n=s._useUTC?[i.getUTCFullYear(),i.getUTCMonth(),i.getUTCDate()]:[i.getFullYear(),i.getMonth(),i.getDate()],e._w&&null==e._a[S]&&null==e._a[Y]&&(null!=(i=(s=e)._w).GG||null!=i.W||null!=i.E?(u=1,l=4,r=bt(i.GG,s._a[D],Be(R(),1,4).year),a=bt(i.W,1),((o=bt(i.E,1))<1||7<o)&&(d=!0)):(u=s._locale._week.dow,l=s._locale._week.doy,h=Be(R(),u,l),r=bt(i.gg,s._a[D],h.year),a=bt(i.w,h.week),null!=i.d?((o=i.d)<0||6<o)&&(d=!0):null!=i.e?(o=i.e+u,(i.e<0||6<i.e)&&(d=!0)):o=u),a<1||a>N(r,u,l)?m(s)._overflowWeeks=!0:null!=d?m(s)._overflowWeekday=!0:(h=$e(r,a,o,u,l),s._a[D]=h.year,s._dayOfYear=h.dayOfYear)),null!=e._dayOfYear&&(i=bt(e._a[D],n[D]),(e._dayOfYear>We(i)||0===e._dayOfYear)&&(m(e)._overflowDayOfYear=!0),d=ze(i,0,e._dayOfYear),e._a[Y]=d.getUTCMonth(),e._a[S]=d.getUTCDate()),t=0;t<3&&null==e._a[t];++t)e._a[t]=c[t]=n[t];for(;t<7;t++)e._a[t]=c[t]=null==e._a[t]?2===t?1:0:e._a[t];24===e._a[O]&&0===e._a[b]&&0===e._a[T]&&0===e._a[Te]&&(e._nextDay=!0,e._a[O]=0),e._d=(e._useUTC?ze:Ze).apply(null,c),r=e._useUTC?e._d.getUTCDay():e._d.getDay(),null!=e._tzm&&e._d.setUTCMinutes(e._d.getUTCMinutes()-e._tzm),e._nextDay&&(e._a[O]=24),e._w&&void 0!==e._w.d&&e._w.d!==r&&(m(e).weekdayMismatch=!0)}}function xt(e){if(e._f===f.ISO_8601)Yt(e);else if(e._f===f.RFC_2822)Ot(e);else{e._a=[],m(e).empty=!0;for(var t,n,s,i,r,a=""+e._i,o=a.length,u=0,l=ae(e._f,e._locale).match(te)||[],d=l.length,h=0;h<d;h++)n=l[h],(t=(a.match(De(n,e))||[])[0])&&(0<(s=a.substr(0,a.indexOf(t))).length&&m(e).unusedInput.push(s),a=a.slice(a.indexOf(t)+t.length),u+=t.length),ie[n]?(t?m(e).empty=!1:m(e).unusedTokens.push(n),s=n,r=e,null!=(i=t)&&c(Se,s)&&Se[s](i,r._a,r,s)):e._strict&&!t&&m(e).unusedTokens.push(n);m(e).charsLeftOver=o-u,0<a.length&&m(e).unusedInput.push(a),e._a[O]<=12&&!0===m(e).bigHour&&0<e._a[O]&&(m(e).bigHour=void 0),m(e).parsedDateParts=e._a.slice(0),m(e).meridiem=e._meridiem,e._a[O]=function(e,t,n){if(null==n)return t;return null!=e.meridiemHour?e.meridiemHour(t,n):null!=e.isPM?((e=e.isPM(n))&&t<12&&(t+=12),t=e||12!==t?t:0):t}(e._locale,e._a[O],e._meridiem),null!==(o=m(e).era)&&(e._a[D]=e._locale.erasConvertYear(o,e._a[D])),Tt(e),_t(e)}}function Nt(e){var t,n,s,i=e._i,r=e._f;if(e._locale=e._locale||P(e._l),null===i||void 0===r&&""===i)return I({nullInput:!0});if("string"==typeof i&&(e._i=i=e._locale.preparse(i)),d(i))return new $(_t(i));if(V(i))e._d=i;else if(a(r))!function(e){var t,n,s,i,r,a,o=!1,u=e._f.length;if(0===u)return m(e).invalidFormat=!0,e._d=new Date(NaN);for(i=0;i<u;i++)r=0,a=!1,t=q({},e),null!=e._useUTC&&(t._useUTC=e._useUTC),t._f=e._f[i],xt(t),A(t)&&(a=!0),r=(r+=m(t).charsLeftOver)+10*m(t).unusedTokens.length,m(t).score=r,o?r<s&&(s=r,n=t):(null==s||r<s||a)&&(s=r,n=t,a&&(o=!0));E(e,n||t)}(e);else if(r)xt(e);else if(o(r=(i=e)._i))i._d=new Date(f.now());else V(r)?i._d=new Date(r.valueOf()):"string"==typeof r?(n=i,null!==(t=Mt.exec(n._i))?n._d=new Date(+t[1]):(Yt(n),!1===n._isValid&&(delete n._isValid,Ot(n),!1===n._isValid&&(delete n._isValid,n._strict?n._isValid=!1:f.createFromInputFallback(n))))):a(r)?(i._a=G(r.slice(0),function(e){return parseInt(e,10)}),Tt(i)):F(r)?(t=i)._d||(s=void 0===(n=ue(t._i)).day?n.date:n.day,t._a=G([n.year,n.month,s,n.hour,n.minute,n.second,n.millisecond],function(e){return e&&parseInt(e,10)}),Tt(t)):u(r)?i._d=new Date(r):f.createFromInputFallback(i);return A(e)||(e._d=null),e}function Wt(e,t,n,s,i){var r={};return!0!==t&&!1!==t||(s=t,t=void 0),!0!==n&&!1!==n||(s=n,n=void 0),(F(e)&&L(e)||a(e)&&0===e.length)&&(e=void 0),r._isAMomentObject=!0,r._useUTC=r._isUTC=i,r._l=n,r._i=e,r._f=t,r._strict=s,(i=new $(_t(Nt(i=r))))._nextDay&&(i.add(1,"d"),i._nextDay=void 0),i}function R(e,t,n,s){return Wt(e,t,n,s,!1)}f.createFromInputFallback=e("value provided is not in a recognized RFC2822 or ISO format. moment construction falls back to js Date(), which is not reliable across all browsers and versions. Non RFC2822/ISO date formats are discouraged. Please refer to http://momentjs.com/guides/#/warnings/js-date/ for more info.",function(e){e._d=new Date(e._i+(e._useUTC?" UTC":""))}),f.ISO_8601=function(){},f.RFC_2822=function(){};me=e("moment().min is deprecated, use moment.max instead. http://momentjs.com/guides/#/warnings/min-max/",function(){var e=R.apply(null,arguments);return this.isValid()&&e.isValid()?e<this?this:e:I()}),_e=e("moment().max is deprecated, use moment.min instead. http://momentjs.com/guides/#/warnings/min-max/",function(){var e=R.apply(null,arguments);return this.isValid()&&e.isValid()?this<e?this:e:I()});function Pt(e,t){var n,s;if(!(t=1===t.length&&a(t[0])?t[0]:t).length)return R();for(n=t[0],s=1;s<t.length;++s)t[s].isValid()&&!t[s][e](n)||(n=t[s]);return n}var Rt=["year","quarter","month","week","day","hour","minute","second","millisecond"];function Ct(e){var e=ue(e),t=e.year||0,n=e.quarter||0,s=e.month||0,i=e.week||e.isoWeek||0,r=e.day||0,a=e.hour||0,o=e.minute||0,u=e.second||0,l=e.millisecond||0;this._isValid=function(e){var t,n,s=!1,i=Rt.length;for(t in e)if(c(e,t)&&(-1===x.call(Rt,t)||null!=e[t]&&isNaN(e[t])))return!1;for(n=0;n<i;++n)if(e[Rt[n]]){if(s)return!1;parseFloat(e[Rt[n]])!==M(e[Rt[n]])&&(s=!0)}return!0}(e),this._milliseconds=+l+1e3*u+6e4*o+1e3*a*60*60,this._days=+r+7*i,this._months=+s+3*n+12*t,this._data={},this._locale=P(),this._bubble()}function Ut(e){return e instanceof Ct}function Ht(e){return e<0?-1*Math.round(-1*e):Math.round(e)}function Ft(e,n){s(e,0,0,function(){var e=this.utcOffset(),t="+";return e<0&&(e=-e,t="-"),t+r(~~(e/60),2)+n+r(~~e%60,2)})}Ft("Z",":"),Ft("ZZ",""),w("Z",ve),w("ZZ",ve),v(["Z","ZZ"],function(e,t,n){n._useUTC=!0,n._tzm=Vt(ve,e)});var Lt=/([\+\-]|\d\d)/gi;function Vt(e,t){var t=(t||"").match(e);return null===t?null:0===(t=60*(e=((t[t.length-1]||[])+"").match(Lt)||["-",0,0])[1]+M(e[2]))?0:"+"===e[0]?t:-t}function Gt(e,t){var n;return t._isUTC?(t=t.clone(),n=(d(e)||V(e)?e:R(e)).valueOf()-t.valueOf(),t._d.setTime(t._d.valueOf()+n),f.updateOffset(t,!1),t):R(e).local()}function Et(e){return-Math.round(e._d.getTimezoneOffset())}function At(){return!!this.isValid()&&(this._isUTC&&0===this._offset)}f.updateOffset=function(){};var It=/^(-|\+)?(?:(\d*)[. ])?(\d+):(\d+)(?::(\d+)(\.\d*)?)?$/,jt=/^(-|\+)?P(?:([-+]?[0-9,.]*)Y)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)W)?(?:([-+]?[0-9,.]*)D)?(?:T(?:([-+]?[0-9,.]*)H)?(?:([-+]?[0-9,.]*)M)?(?:([-+]?[0-9,.]*)S)?)?$/;function C(e,t){var n,s=e,i=null;return Ut(e)?s={ms:e._milliseconds,d:e._days,M:e._months}:u(e)||!isNaN(+e)?(s={},t?s[t]=+e:s.milliseconds=+e):(i=It.exec(e))?(n="-"===i[1]?-1:1,s={y:0,d:M(i[S])*n,h:M(i[O])*n,m:M(i[b])*n,s:M(i[T])*n,ms:M(Ht(1e3*i[Te]))*n}):(i=jt.exec(e))?(n="-"===i[1]?-1:1,s={y:Zt(i[2],n),M:Zt(i[3],n),w:Zt(i[4],n),d:Zt(i[5],n),h:Zt(i[6],n),m:Zt(i[7],n),s:Zt(i[8],n)}):null==s?s={}:"object"==typeof s&&("from"in s||"to"in s)&&(t=function(e,t){var n;if(!e.isValid()||!t.isValid())return{milliseconds:0,months:0};t=Gt(t,e),e.isBefore(t)?n=zt(e,t):((n=zt(t,e)).milliseconds=-n.milliseconds,n.months=-n.months);return n}(R(s.from),R(s.to)),(s={}).ms=t.milliseconds,s.M=t.months),i=new Ct(s),Ut(e)&&c(e,"_locale")&&(i._locale=e._locale),Ut(e)&&c(e,"_isValid")&&(i._isValid=e._isValid),i}function Zt(e,t){e=e&&parseFloat(e.replace(",","."));return(isNaN(e)?0:e)*t}function zt(e,t){var n={};return n.months=t.month()-e.month()+12*(t.year()-e.year()),e.clone().add(n.months,"M").isAfter(t)&&--n.months,n.milliseconds=+t-+e.clone().add(n.months,"M"),n}function qt(s,i){return function(e,t){var n;return null===t||isNaN(+t)||(Q(i,"moment()."+i+"(period, number) is deprecated. Please use moment()."+i+"(number, period). See http://momentjs.com/guides/#/warnings/add-inverted-param/ for more info."),n=e,e=t,t=n),$t(this,C(e,t),s),this}}function $t(e,t,n,s){var i=t._milliseconds,r=Ht(t._days),t=Ht(t._months);e.isValid()&&(s=null==s||s,t&&Ae(e,Ce(e,"Month")+t*n),r&&Ue(e,"Date",Ce(e,"Date")+r*n),i&&e._d.setTime(e._d.valueOf()+i*n),s&&f.updateOffset(e,r||t))}C.fn=Ct.prototype,C.invalid=function(){return C(NaN)};Fe=qt(1,"add"),Qe=qt(-1,"subtract");function Bt(e){return"string"==typeof e||e instanceof String}function Jt(e){return d(e)||V(e)||Bt(e)||u(e)||function(t){var e=a(t),n=!1;e&&(n=0===t.filter(function(e){return!u(e)&&Bt(t)}).length);return e&&n}(e)||function(e){var t,n,s=F(e)&&!L(e),i=!1,r=["years","year","y","months","month","M","days","day","d","dates","date","D","hours","hour","h","minutes","minute","m","seconds","second","s","milliseconds","millisecond","ms"],a=r.length;for(t=0;t<a;t+=1)n=r[t],i=i||c(e,n);return s&&i}(e)||null==e}function Qt(e,t){if(e.date()<t.date())return-Qt(t,e);var n=12*(t.year()-e.year())+(t.month()-e.month()),s=e.clone().add(n,"months"),t=t-s<0?(t-s)/(s-e.clone().add(n-1,"months")):(t-s)/(e.clone().add(1+n,"months")-s);return-(n+t)||0}function Xt(e){return void 0===e?this._locale._abbr:(null!=(e=P(e))&&(this._locale=e),this)}f.defaultFormat="YYYY-MM-DDTHH:mm:ssZ",f.defaultFormatUtc="YYYY-MM-DDTHH:mm:ss[Z]";Ke=e("moment().lang() is deprecated. Instead, use moment().localeData() to get the language configuration. Use moment().locale() to change languages.",function(e){return void 0===e?this.localeData():this.locale(e)});function Kt(){return this._locale}var en=126227808e5;function tn(e,t){return(e%t+t)%t}function nn(e,t,n){return e<100&&0<=e?new Date(e+400,t,n)-en:new Date(e,t,n).valueOf()}function sn(e,t,n){return e<100&&0<=e?Date.UTC(e+400,t,n)-en:Date.UTC(e,t,n)}function rn(e,t){return t.erasAbbrRegex(e)}function an(){for(var e,t,n,s=[],i=[],r=[],a=[],o=this.eras(),u=0,l=o.length;u<l;++u)e=p(o[u].name),t=p(o[u].abbr),n=p(o[u].narrow),i.push(e),s.push(t),r.push(n),a.push(e),a.push(t),a.push(n);this._erasRegex=new RegExp("^("+a.join("|")+")","i"),this._erasNameRegex=new RegExp("^("+i.join("|")+")","i"),this._erasAbbrRegex=new RegExp("^("+s.join("|")+")","i"),this._erasNarrowRegex=new RegExp("^("+r.join("|")+")","i")}function on(e,t){s(0,[e,e.length],0,t)}function un(e,t,n,s,i){var r;return null==e?Be(this,s,i).year:(r=N(e,s,i),function(e,t,n,s,i){e=$e(e,t,n,s,i),t=ze(e.year,0,e.dayOfYear);return this.year(t.getUTCFullYear()),this.month(t.getUTCMonth()),this.date(t.getUTCDate()),this}.call(this,e,t=r<t?r:t,n,s,i))}s("N",0,0,"eraAbbr"),s("NN",0,0,"eraAbbr"),s("NNN",0,0,"eraAbbr"),s("NNNN",0,0,"eraName"),s("NNNNN",0,0,"eraNarrow"),s("y",["y",1],"yo","eraYear"),s("y",["yy",2],0,"eraYear"),s("y",["yyy",3],0,"eraYear"),s("y",["yyyy",4],0,"eraYear"),w("N",rn),w("NN",rn),w("NNN",rn),w("NNNN",function(e,t){return t.erasNameRegex(e)}),w("NNNNN",function(e,t){return t.erasNarrowRegex(e)}),v(["N","NN","NNN","NNNN","NNNNN"],function(e,t,n,s){s=n._locale.erasParse(e,s,n._strict);s?m(n).era=s:m(n).invalidEra=e}),w("y",pe),w("yy",pe),w("yyy",pe),w("yyyy",pe),w("yo",function(e,t){return t._eraYearOrdinalRegex||pe}),v(["y","yy","yyy","yyyy"],D),v(["yo"],function(e,t,n,s){var i;n._locale._eraYearOrdinalRegex&&(i=e.match(n._locale._eraYearOrdinalRegex)),n._locale.eraYearOrdinalParse?t[D]=n._locale.eraYearOrdinalParse(e,i):t[D]=parseInt(e,10)}),s(0,["gg",2],0,function(){return this.weekYear()%100}),s(0,["GG",2],0,function(){return this.isoWeekYear()%100}),on("gggg","weekYear"),on("ggggg","weekYear"),on("GGGG","isoWeekYear"),on("GGGGG","isoWeekYear"),w("G",ke),w("g",ke),w("GG",n,t),w("gg",n,t),w("GGGG",ge,ce),w("gggg",ge,ce),w("GGGGG",we,fe),w("ggggg",we,fe),Oe(["gggg","ggggg","GGGG","GGGGG"],function(e,t,n,s){t[s.substr(0,2)]=M(e)}),Oe(["gg","GG"],function(e,t,n,s){t[s]=f.parseTwoDigitYear(e)}),s("Q",0,"Qo","quarter"),w("Q",de),v("Q",function(e,t){t[Y]=3*(M(e)-1)}),s("D",["DD",2],"Do","date"),w("D",n,y),w("DD",n,t),w("Do",function(e,t){return e?t._dayOfMonthOrdinalParse||t._ordinalParse:t._dayOfMonthOrdinalParseLenient}),v(["D","DD"],S),v("Do",function(e,t){t[S]=M(e.match(n)[0])});ge=Re("Date",!0);s("DDD",["DDDD",3],"DDDo","dayOfYear"),w("DDD",ye),w("DDDD",he),v(["DDD","DDDD"],function(e,t,n){n._dayOfYear=M(e)}),s("m",["mm",2],0,"minute"),w("m",n,g),w("mm",n,t),v(["m","mm"],b);var ln,ce=Re("Minutes",!1),we=(s("s",["ss",2],0,"second"),w("s",n,g),w("ss",n,t),v(["s","ss"],T),Re("Seconds",!1));for(s("S",0,0,function(){return~~(this.millisecond()/100)}),s(0,["SS",2],0,function(){return~~(this.millisecond()/10)}),s(0,["SSS",3],0,"millisecond"),s(0,["SSSS",4],0,function(){return 10*this.millisecond()}),s(0,["SSSSS",5],0,function(){return 100*this.millisecond()}),s(0,["SSSSSS",6],0,function(){return 1e3*this.millisecond()}),s(0,["SSSSSSS",7],0,function(){return 1e4*this.millisecond()}),s(0,["SSSSSSSS",8],0,function(){return 1e5*this.millisecond()}),s(0,["SSSSSSSSS",9],0,function(){return 1e6*this.millisecond()}),w("S",ye,de),w("SS",ye,t),w("SSS",ye,he),ln="SSSS";ln.length<=9;ln+="S")w(ln,pe);function dn(e,t){t[Te]=M(1e3*("0."+e))}for(ln="S";ln.length<=9;ln+="S")v(ln,dn);fe=Re("Milliseconds",!1),s("z",0,0,"zoneAbbr"),s("zz",0,0,"zoneName");y=$.prototype;function hn(e){return e}y.add=Fe,y.calendar=function(e,t){1===arguments.length&&(arguments[0]?Jt(arguments[0])?(e=arguments[0],t=void 0):function(e){for(var t=F(e)&&!L(e),n=!1,s=["sameDay","nextDay","lastDay","nextWeek","lastWeek","sameElse"],i=0;i<s.length;i+=1)n=n||c(e,s[i]);return t&&n}(arguments[0])&&(t=arguments[0],e=void 0):t=e=void 0);var e=e||R(),n=Gt(e,this).startOf("day"),n=f.calendarFormat(this,n)||"sameElse",t=t&&(h(t[n])?t[n].call(this,e):t[n]);return this.format(t||this.localeData().calendar(n,this,R(e)))},y.clone=function(){return new $(this)},y.diff=function(e,t,n){var s,i,r;if(!this.isValid())return NaN;if(!(s=Gt(e,this)).isValid())return NaN;switch(i=6e4*(s.utcOffset()-this.utcOffset()),t=_(t)){case"year":r=Qt(this,s)/12;break;case"month":r=Qt(this,s);break;case"quarter":r=Qt(this,s)/3;break;case"second":r=(this-s)/1e3;break;case"minute":r=(this-s)/6e4;break;case"hour":r=(this-s)/36e5;break;case"day":r=(this-s-i)/864e5;break;case"week":r=(this-s-i)/6048e5;break;default:r=this-s}return n?r:k(r)},y.endOf=function(e){var t,n;if(void 0===(e=_(e))||"millisecond"===e||!this.isValid())return this;switch(n=this._isUTC?sn:nn,e){case"year":t=n(this.year()+1,0,1)-1;break;case"quarter":t=n(this.year(),this.month()-this.month()%3+3,1)-1;break;case"month":t=n(this.year(),this.month()+1,1)-1;break;case"week":t=n(this.year(),this.month(),this.date()-this.weekday()+7)-1;break;case"isoWeek":t=n(this.year(),this.month(),this.date()-(this.isoWeekday()-1)+7)-1;break;case"day":case"date":t=n(this.year(),this.month(),this.date()+1)-1;break;case"hour":t=this._d.valueOf(),t+=36e5-tn(t+(this._isUTC?0:6e4*this.utcOffset()),36e5)-1;break;case"minute":t=this._d.valueOf(),t+=6e4-tn(t,6e4)-1;break;case"second":t=this._d.valueOf(),t+=1e3-tn(t,1e3)-1}return this._d.setTime(t),f.updateOffset(this,!0),this},y.format=function(e){return e=e||(this.isUtc()?f.defaultFormatUtc:f.defaultFormat),e=re(this,e),this.localeData().postformat(e)},y.from=function(e,t){return this.isValid()&&(d(e)&&e.isValid()||R(e).isValid())?C({to:this,from:e}).locale(this.locale()).humanize(!t):this.localeData().invalidDate()},y.fromNow=function(e){return this.from(R(),e)},y.to=function(e,t){return this.isValid()&&(d(e)&&e.isValid()||R(e).isValid())?C({from:this,to:e}).locale(this.locale()).humanize(!t):this.localeData().invalidDate()},y.toNow=function(e){return this.to(R(),e)},y.get=function(e){return h(this[e=_(e)])?this[e]():this},y.invalidAt=function(){return m(this).overflow},y.isAfter=function(e,t){return e=d(e)?e:R(e),!(!this.isValid()||!e.isValid())&&("millisecond"===(t=_(t)||"millisecond")?this.valueOf()>e.valueOf():e.valueOf()<this.clone().startOf(t).valueOf())},y.isBefore=function(e,t){return e=d(e)?e:R(e),!(!this.isValid()||!e.isValid())&&("millisecond"===(t=_(t)||"millisecond")?this.valueOf()<e.valueOf():this.clone().endOf(t).valueOf()<e.valueOf())},y.isBetween=function(e,t,n,s){return e=d(e)?e:R(e),t=d(t)?t:R(t),!!(this.isValid()&&e.isValid()&&t.isValid())&&(("("===(s=s||"()")[0]?this.isAfter(e,n):!this.isBefore(e,n))&&(")"===s[1]?this.isBefore(t,n):!this.isAfter(t,n)))},y.isSame=function(e,t){var e=d(e)?e:R(e);return!(!this.isValid()||!e.isValid())&&("millisecond"===(t=_(t)||"millisecond")?this.valueOf()===e.valueOf():(e=e.valueOf(),this.clone().startOf(t).valueOf()<=e&&e<=this.clone().endOf(t).valueOf()))},y.isSameOrAfter=function(e,t){return this.isSame(e,t)||this.isAfter(e,t)},y.isSameOrBefore=function(e,t){return this.isSame(e,t)||this.isBefore(e,t)},y.isValid=function(){return A(this)},y.lang=Ke,y.locale=Xt,y.localeData=Kt,y.max=_e,y.min=me,y.parsingFlags=function(){return E({},m(this))},y.set=function(e,t){if("object"==typeof e)for(var n=function(e){var t,n=[];for(t in e)c(e,t)&&n.push({unit:t,priority:le[t]});return n.sort(function(e,t){return e.priority-t.priority}),n}(e=ue(e)),s=n.length,i=0;i<s;i++)this[n[i].unit](e[n[i].unit]);else if(h(this[e=_(e)]))return this[e](t);return this},y.startOf=function(e){var t,n;if(void 0===(e=_(e))||"millisecond"===e||!this.isValid())return this;switch(n=this._isUTC?sn:nn,e){case"year":t=n(this.year(),0,1);break;case"quarter":t=n(this.year(),this.month()-this.month()%3,1);break;case"month":t=n(this.year(),this.month(),1);break;case"week":t=n(this.year(),this.month(),this.date()-this.weekday());break;case"isoWeek":t=n(this.year(),this.month(),this.date()-(this.isoWeekday()-1));break;case"day":case"date":t=n(this.year(),this.month(),this.date());break;case"hour":t=this._d.valueOf(),t-=tn(t+(this._isUTC?0:6e4*this.utcOffset()),36e5);break;case"minute":t=this._d.valueOf(),t-=tn(t,6e4);break;case"second":t=this._d.valueOf(),t-=tn(t,1e3)}return this._d.setTime(t),f.updateOffset(this,!0),this},y.subtract=Qe,y.toArray=function(){var e=this;return[e.year(),e.month(),e.date(),e.hour(),e.minute(),e.second(),e.millisecond()]},y.toObject=function(){var e=this;return{years:e.year(),months:e.month(),date:e.date(),hours:e.hours(),minutes:e.minutes(),seconds:e.seconds(),milliseconds:e.milliseconds()}},y.toDate=function(){return new Date(this.valueOf())},y.toISOString=function(e){if(!this.isValid())return null;var t=(e=!0!==e)?this.clone().utc():this;return t.year()<0||9999<t.year()?re(t,e?"YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]":"YYYYYY-MM-DD[T]HH:mm:ss.SSSZ"):h(Date.prototype.toISOString)?e?this.toDate().toISOString():new Date(this.valueOf()+60*this.utcOffset()*1e3).toISOString().replace("Z",re(t,"Z")):re(t,e?"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]":"YYYY-MM-DD[T]HH:mm:ss.SSSZ")},y.inspect=function(){if(!this.isValid())return"moment.invalid(/* "+this._i+" */)";var e,t="moment",n="";return this.isLocal()||(t=0===this.utcOffset()?"moment.utc":"moment.parseZone",n="Z"),t="["+t+'("]',e=0<=this.year()&&this.year()<=9999?"YYYY":"YYYYYY",this.format(t+e+"-MM-DD[T]HH:mm:ss.SSS"+(n+'[")]'))},"undefined"!=typeof Symbol&&null!=Symbol.for&&(y[Symbol.for("nodejs.util.inspect.custom")]=function(){return"Moment<"+this.format()+">"}),y.toJSON=function(){return this.isValid()?this.toISOString():null},y.toString=function(){return this.clone().locale("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},y.unix=function(){return Math.floor(this.valueOf()/1e3)},y.valueOf=function(){return this._d.valueOf()-6e4*(this._offset||0)},y.creationData=function(){return{input:this._i,format:this._f,locale:this._locale,isUTC:this._isUTC,strict:this._strict}},y.eraName=function(){for(var e,t=this.localeData().eras(),n=0,s=t.length;n<s;++n){if(e=this.clone().startOf("day").valueOf(),t[n].since<=e&&e<=t[n].until)return t[n].name;if(t[n].until<=e&&e<=t[n].since)return t[n].name}return""},y.eraNarrow=function(){for(var e,t=this.localeData().eras(),n=0,s=t.length;n<s;++n){if(e=this.clone().startOf("day").valueOf(),t[n].since<=e&&e<=t[n].until)return t[n].narrow;if(t[n].until<=e&&e<=t[n].since)return t[n].narrow}return""},y.eraAbbr=function(){for(var e,t=this.localeData().eras(),n=0,s=t.length;n<s;++n){if(e=this.clone().startOf("day").valueOf(),t[n].since<=e&&e<=t[n].until)return t[n].abbr;if(t[n].until<=e&&e<=t[n].since)return t[n].abbr}return""},y.eraYear=function(){for(var e,t,n=this.localeData().eras(),s=0,i=n.length;s<i;++s)if(e=n[s].since<=n[s].until?1:-1,t=this.clone().startOf("day").valueOf(),n[s].since<=t&&t<=n[s].until||n[s].until<=t&&t<=n[s].since)return(this.year()-f(n[s].since).year())*e+n[s].offset;return this.year()},y.year=Pe,y.isLeapYear=function(){return be(this.year())},y.weekYear=function(e){return un.call(this,e,this.week(),this.weekday()+this.localeData()._week.dow,this.localeData()._week.dow,this.localeData()._week.doy)},y.isoWeekYear=function(e){return un.call(this,e,this.isoWeek(),this.isoWeekday(),1,4)},y.quarter=y.quarters=function(e){return null==e?Math.ceil((this.month()+1)/3):this.month(3*(e-1)+this.month()%3)},y.month=Ie,y.daysInMonth=function(){return He(this.year(),this.month())},y.week=y.weeks=function(e){var t=this.localeData().week(this);return null==e?t:this.add(7*(e-t),"d")},y.isoWeek=y.isoWeeks=function(e){var t=Be(this,1,4).week;return null==e?t:this.add(7*(e-t),"d")},y.weeksInYear=function(){var e=this.localeData()._week;return N(this.year(),e.dow,e.doy)},y.weeksInWeekYear=function(){var e=this.localeData()._week;return N(this.weekYear(),e.dow,e.doy)},y.isoWeeksInYear=function(){return N(this.year(),1,4)},y.isoWeeksInISOWeekYear=function(){return N(this.isoWeekYear(),1,4)},y.date=ge,y.day=y.days=function(e){if(!this.isValid())return null!=e?this:NaN;var t,n,s=Ce(this,"Day");return null!=e?(t=e,n=this.localeData(),e="string"!=typeof t?t:isNaN(t)?"number"==typeof(t=n.weekdaysParse(t))?t:null:parseInt(t,10),this.add(e-s,"d")):s},y.weekday=function(e){if(!this.isValid())return null!=e?this:NaN;var t=(this.day()+7-this.localeData()._week.dow)%7;return null==e?t:this.add(e-t,"d")},y.isoWeekday=function(e){return this.isValid()?null!=e?(t=e,n=this.localeData(),n="string"==typeof t?n.weekdaysParse(t)%7||7:isNaN(t)?null:t,this.day(this.day()%7?n:n-7)):this.day()||7:null!=e?this:NaN;var t,n},y.dayOfYear=function(e){var t=Math.round((this.clone().startOf("day")-this.clone().startOf("year"))/864e5)+1;return null==e?t:this.add(e-t,"d")},y.hour=y.hours=i,y.minute=y.minutes=ce,y.second=y.seconds=we,y.millisecond=y.milliseconds=fe,y.utcOffset=function(e,t,n){var s,i=this._offset||0;if(!this.isValid())return null!=e?this:NaN;if(null==e)return this._isUTC?i:Et(this);if("string"==typeof e){if(null===(e=Vt(ve,e)))return this}else Math.abs(e)<16&&!n&&(e*=60);return!this._isUTC&&t&&(s=Et(this)),this._offset=e,this._isUTC=!0,null!=s&&this.add(s,"m"),i!==e&&(!t||this._changeInProgress?$t(this,C(e-i,"m"),1,!1):this._changeInProgress||(this._changeInProgress=!0,f.updateOffset(this,!0),this._changeInProgress=null)),this},y.utc=function(e){return this.utcOffset(0,e)},y.local=function(e){return this._isUTC&&(this.utcOffset(0,e),this._isUTC=!1,e&&this.subtract(Et(this),"m")),this},y.parseZone=function(){var e;return null!=this._tzm?this.utcOffset(this._tzm,!1,!0):"string"==typeof this._i&&(null!=(e=Vt(Me,this._i))?this.utcOffset(e):this.utcOffset(0,!0)),this},y.hasAlignedHourOffset=function(e){return!!this.isValid()&&(e=e?R(e).utcOffset():0,(this.utcOffset()-e)%60==0)},y.isDST=function(){return this.utcOffset()>this.clone().month(0).utcOffset()||this.utcOffset()>this.clone().month(5).utcOffset()},y.isLocal=function(){return!!this.isValid()&&!this._isUTC},y.isUtcOffset=function(){return!!this.isValid()&&this._isUTC},y.isUtc=At,y.isUTC=At,y.zoneAbbr=function(){return this._isUTC?"UTC":""},y.zoneName=function(){return this._isUTC?"Coordinated Universal Time":""},y.dates=e("dates accessor is deprecated. Use date instead.",ge),y.months=e("months accessor is deprecated. Use month instead",Ie),y.years=e("years accessor is deprecated. Use year instead",Pe),y.zone=e("moment().zone is deprecated, use moment().utcOffset instead. http://momentjs.com/guides/#/warnings/zone/",function(e,t){return null!=e?(this.utcOffset(e="string"!=typeof e?-e:e,t),this):-this.utcOffset()}),y.isDSTShifted=e("isDSTShifted is deprecated. See http://momentjs.com/guides/#/warnings/dst-shifted/ for more information",function(){if(!o(this._isDSTShifted))return this._isDSTShifted;var e,t={};return q(t,this),(t=Nt(t))._a?(e=(t._isUTC?l:R)(t._a),this._isDSTShifted=this.isValid()&&0<function(e,t,n){for(var s=Math.min(e.length,t.length),i=Math.abs(e.length-t.length),r=0,a=0;a<s;a++)(n&&e[a]!==t[a]||!n&&M(e[a])!==M(t[a]))&&r++;return r+i}(t._a,e.toArray())):this._isDSTShifted=!1,this._isDSTShifted});g=K.prototype;function cn(e,t,n,s){var i=P(),s=l().set(s,t);return i[n](s,e)}function fn(e,t,n){if(u(e)&&(t=e,e=void 0),e=e||"",null!=t)return cn(e,t,n,"month");for(var s=[],i=0;i<12;i++)s[i]=cn(e,i,n,"month");return s}function mn(e,t,n,s){t=("boolean"==typeof e?u(t)&&(n=t,t=void 0):(t=e,e=!1,u(n=t)&&(n=t,t=void 0)),t||"");var i,r=P(),a=e?r._week.dow:0,o=[];if(null!=n)return cn(t,(n+a)%7,s,"day");for(i=0;i<7;i++)o[i]=cn(t,(i+a)%7,s,"day");return o}g.calendar=function(e,t,n){return h(e=this._calendar[e]||this._calendar.sameElse)?e.call(t,n):e},g.longDateFormat=function(e){var t=this._longDateFormat[e],n=this._longDateFormat[e.toUpperCase()];return t||!n?t:(this._longDateFormat[e]=n.match(te).map(function(e){return"MMMM"===e||"MM"===e||"DD"===e||"dddd"===e?e.slice(1):e}).join(""),this._longDateFormat[e])},g.invalidDate=function(){return this._invalidDate},g.ordinal=function(e){return this._ordinal.replace("%d",e)},g.preparse=hn,g.postformat=hn,g.relativeTime=function(e,t,n,s){var i=this._relativeTime[n];return h(i)?i(e,t,n,s):i.replace(/%d/i,e)},g.pastFuture=function(e,t){return h(e=this._relativeTime[0<e?"future":"past"])?e(t):e.replace(/%s/i,t)},g.set=function(e){var t,n;for(n in e)c(e,n)&&(h(t=e[n])?this[n]=t:this["_"+n]=t);this._config=e,this._dayOfMonthOrdinalParseLenient=new RegExp((this._dayOfMonthOrdinalParse.source||this._ordinalParse.source)+"|"+/\d{1,2}/.source)},g.eras=function(e,t){for(var n,s=this._eras||P("en")._eras,i=0,r=s.length;i<r;++i)switch("string"==typeof s[i].since&&(n=f(s[i].since).startOf("day"),s[i].since=n.valueOf()),typeof s[i].until){case"undefined":s[i].until=1/0;break;case"string":n=f(s[i].until).startOf("day").valueOf(),s[i].until=n.valueOf()}return s},g.erasParse=function(e,t,n){var s,i,r,a,o,u=this.eras();for(e=e.toUpperCase(),s=0,i=u.length;s<i;++s)if(r=u[s].name.toUpperCase(),a=u[s].abbr.toUpperCase(),o=u[s].narrow.toUpperCase(),n)switch(t){case"N":case"NN":case"NNN":if(a===e)return u[s];break;case"NNNN":if(r===e)return u[s];break;case"NNNNN":if(o===e)return u[s]}else if(0<=[r,a,o].indexOf(e))return u[s]},g.erasConvertYear=function(e,t){var n=e.since<=e.until?1:-1;return void 0===t?f(e.since).year():f(e.since).year()+(t-e.offset)*n},g.erasAbbrRegex=function(e){return c(this,"_erasAbbrRegex")||an.call(this),e?this._erasAbbrRegex:this._erasRegex},g.erasNameRegex=function(e){return c(this,"_erasNameRegex")||an.call(this),e?this._erasNameRegex:this._erasRegex},g.erasNarrowRegex=function(e){return c(this,"_erasNarrowRegex")||an.call(this),e?this._erasNarrowRegex:this._erasRegex},g.months=function(e,t){return e?(a(this._months)?this._months:this._months[(this._months.isFormat||Ve).test(t)?"format":"standalone"])[e.month()]:a(this._months)?this._months:this._months.standalone},g.monthsShort=function(e,t){return e?(a(this._monthsShort)?this._monthsShort:this._monthsShort[Ve.test(t)?"format":"standalone"])[e.month()]:a(this._monthsShort)?this._monthsShort:this._monthsShort.standalone},g.monthsParse=function(e,t,n){var s,i;if(this._monthsParseExact)return function(e,t,n){var s,i,r,e=e.toLocaleLowerCase();if(!this._monthsParse)for(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[],s=0;s<12;++s)r=l([2e3,s]),this._shortMonthsParse[s]=this.monthsShort(r,"").toLocaleLowerCase(),this._longMonthsParse[s]=this.months(r,"").toLocaleLowerCase();return n?"MMM"===t?-1!==(i=x.call(this._shortMonthsParse,e))?i:null:-1!==(i=x.call(this._longMonthsParse,e))?i:null:"MMM"===t?-1!==(i=x.call(this._shortMonthsParse,e))||-1!==(i=x.call(this._longMonthsParse,e))?i:null:-1!==(i=x.call(this._longMonthsParse,e))||-1!==(i=x.call(this._shortMonthsParse,e))?i:null}.call(this,e,t,n);for(this._monthsParse||(this._monthsParse=[],this._longMonthsParse=[],this._shortMonthsParse=[]),s=0;s<12;s++){if(i=l([2e3,s]),n&&!this._longMonthsParse[s]&&(this._longMonthsParse[s]=new RegExp("^"+this.months(i,"").replace(".","")+"$","i"),this._shortMonthsParse[s]=new RegExp("^"+this.monthsShort(i,"").replace(".","")+"$","i")),n||this._monthsParse[s]||(i="^"+this.months(i,"")+"|^"+this.monthsShort(i,""),this._monthsParse[s]=new RegExp(i.replace(".",""),"i")),n&&"MMMM"===t&&this._longMonthsParse[s].test(e))return s;if(n&&"MMM"===t&&this._shortMonthsParse[s].test(e))return s;if(!n&&this._monthsParse[s].test(e))return s}},g.monthsRegex=function(e){return this._monthsParseExact?(c(this,"_monthsRegex")||je.call(this),e?this._monthsStrictRegex:this._monthsRegex):(c(this,"_monthsRegex")||(this._monthsRegex=Ee),this._monthsStrictRegex&&e?this._monthsStrictRegex:this._monthsRegex)},g.monthsShortRegex=function(e){return this._monthsParseExact?(c(this,"_monthsRegex")||je.call(this),e?this._monthsShortStrictRegex:this._monthsShortRegex):(c(this,"_monthsShortRegex")||(this._monthsShortRegex=Ge),this._monthsShortStrictRegex&&e?this._monthsShortStrictRegex:this._monthsShortRegex)},g.week=function(e){return Be(e,this._week.dow,this._week.doy).week},g.firstDayOfYear=function(){return this._week.doy},g.firstDayOfWeek=function(){return this._week.dow},g.weekdays=function(e,t){return t=a(this._weekdays)?this._weekdays:this._weekdays[e&&!0!==e&&this._weekdays.isFormat.test(t)?"format":"standalone"],!0===e?Je(t,this._week.dow):e?t[e.day()]:t},g.weekdaysMin=function(e){return!0===e?Je(this._weekdaysMin,this._week.dow):e?this._weekdaysMin[e.day()]:this._weekdaysMin},g.weekdaysShort=function(e){return!0===e?Je(this._weekdaysShort,this._week.dow):e?this._weekdaysShort[e.day()]:this._weekdaysShort},g.weekdaysParse=function(e,t,n){var s,i;if(this._weekdaysParseExact)return function(e,t,n){var s,i,r,e=e.toLocaleLowerCase();if(!this._weekdaysParse)for(this._weekdaysParse=[],this._shortWeekdaysParse=[],this._minWeekdaysParse=[],s=0;s<7;++s)r=l([2e3,1]).day(s),this._minWeekdaysParse[s]=this.weekdaysMin(r,"").toLocaleLowerCase(),this._shortWeekdaysParse[s]=this.weekdaysShort(r,"").toLocaleLowerCase(),this._weekdaysParse[s]=this.weekdays(r,"").toLocaleLowerCase();return n?"dddd"===t?-1!==(i=x.call(this._weekdaysParse,e))?i:null:"ddd"===t?-1!==(i=x.call(this._shortWeekdaysParse,e))?i:null:-1!==(i=x.call(this._minWeekdaysParse,e))?i:null:"dddd"===t?-1!==(i=x.call(this._weekdaysParse,e))||-1!==(i=x.call(this._shortWeekdaysParse,e))||-1!==(i=x.call(this._minWeekdaysParse,e))?i:null:"ddd"===t?-1!==(i=x.call(this._shortWeekdaysParse,e))||-1!==(i=x.call(this._weekdaysParse,e))||-1!==(i=x.call(this._minWeekdaysParse,e))?i:null:-1!==(i=x.call(this._minWeekdaysParse,e))||-1!==(i=x.call(this._weekdaysParse,e))||-1!==(i=x.call(this._shortWeekdaysParse,e))?i:null}.call(this,e,t,n);for(this._weekdaysParse||(this._weekdaysParse=[],this._minWeekdaysParse=[],this._shortWeekdaysParse=[],this._fullWeekdaysParse=[]),s=0;s<7;s++){if(i=l([2e3,1]).day(s),n&&!this._fullWeekdaysParse[s]&&(this._fullWeekdaysParse[s]=new RegExp("^"+this.weekdays(i,"").replace(".","\\.?")+"$","i"),this._shortWeekdaysParse[s]=new RegExp("^"+this.weekdaysShort(i,"").replace(".","\\.?")+"$","i"),this._minWeekdaysParse[s]=new RegExp("^"+this.weekdaysMin(i,"").replace(".","\\.?")+"$","i")),this._weekdaysParse[s]||(i="^"+this.weekdays(i,"")+"|^"+this.weekdaysShort(i,"")+"|^"+this.weekdaysMin(i,""),this._weekdaysParse[s]=new RegExp(i.replace(".",""),"i")),n&&"dddd"===t&&this._fullWeekdaysParse[s].test(e))return s;if(n&&"ddd"===t&&this._shortWeekdaysParse[s].test(e))return s;if(n&&"dd"===t&&this._minWeekdaysParse[s].test(e))return s;if(!n&&this._weekdaysParse[s].test(e))return s}},g.weekdaysRegex=function(e){return this._weekdaysParseExact?(c(this,"_weekdaysRegex")||st.call(this),e?this._weekdaysStrictRegex:this._weekdaysRegex):(c(this,"_weekdaysRegex")||(this._weekdaysRegex=et),this._weekdaysStrictRegex&&e?this._weekdaysStrictRegex:this._weekdaysRegex)},g.weekdaysShortRegex=function(e){return this._weekdaysParseExact?(c(this,"_weekdaysRegex")||st.call(this),e?this._weekdaysShortStrictRegex:this._weekdaysShortRegex):(c(this,"_weekdaysShortRegex")||(this._weekdaysShortRegex=tt),this._weekdaysShortStrictRegex&&e?this._weekdaysShortStrictRegex:this._weekdaysShortRegex)},g.weekdaysMinRegex=function(e){return this._weekdaysParseExact?(c(this,"_weekdaysRegex")||st.call(this),e?this._weekdaysMinStrictRegex:this._weekdaysMinRegex):(c(this,"_weekdaysMinRegex")||(this._weekdaysMinRegex=nt),this._weekdaysMinStrictRegex&&e?this._weekdaysMinStrictRegex:this._weekdaysMinRegex)},g.isPM=function(e){return"p"===(e+"").toLowerCase().charAt(0)},g.meridiem=function(e,t,n){return 11<e?n?"pm":"PM":n?"am":"AM"},ft("en",{eras:[{since:"0001-01-01",until:1/0,offset:1,name:"Anno Domini",narrow:"AD",abbr:"AD"},{since:"0000-12-31",until:-1/0,offset:1,name:"Before Christ",narrow:"BC",abbr:"BC"}],dayOfMonthOrdinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(e){var t=e%10;return e+(1===M(e%100/10)?"th":1==t?"st":2==t?"nd":3==t?"rd":"th")}}),f.lang=e("moment.lang is deprecated. Use moment.locale instead.",ft),f.langData=e("moment.langData is deprecated. Use moment.localeData instead.",P);var _n=Math.abs;function yn(e,t,n,s){t=C(t,n);return e._milliseconds+=s*t._milliseconds,e._days+=s*t._days,e._months+=s*t._months,e._bubble()}function gn(e){return e<0?Math.floor(e):Math.ceil(e)}function wn(e){return 4800*e/146097}function pn(e){return 146097*e/4800}function kn(e){return function(){return this.as(e)}}de=kn("ms"),t=kn("s"),ye=kn("m"),he=kn("h"),Fe=kn("d"),_e=kn("w"),me=kn("M"),Qe=kn("Q"),i=kn("y"),ce=de;function Mn(e){return function(){return this.isValid()?this._data[e]:NaN}}var we=Mn("milliseconds"),fe=Mn("seconds"),ge=Mn("minutes"),Pe=Mn("hours"),g=Mn("days"),vn=Mn("months"),Dn=Mn("years");var Yn=Math.round,Sn={ss:44,s:45,m:45,h:22,d:26,w:null,M:11};function On(e,t,n,s){var i=C(e).abs(),r=Yn(i.as("s")),a=Yn(i.as("m")),o=Yn(i.as("h")),u=Yn(i.as("d")),l=Yn(i.as("M")),d=Yn(i.as("w")),i=Yn(i.as("y")),r=(r<=n.ss?["s",r]:r<n.s&&["ss",r])||a<=1&&["m"]||a<n.m&&["mm",a]||o<=1&&["h"]||o<n.h&&["hh",o]||u<=1&&["d"]||u<n.d&&["dd",u];return(r=(r=null!=n.w?r||d<=1&&["w"]||d<n.w&&["ww",d]:r)||l<=1&&["M"]||l<n.M&&["MM",l]||i<=1&&["y"]||["yy",i])[2]=t,r[3]=0<+e,r[4]=s,function(e,t,n,s,i){return i.relativeTime(t||1,!!n,e,s)}.apply(null,r)}var bn=Math.abs;function Tn(e){return(0<e)-(e<0)||+e}function xn(){if(!this.isValid())return this.localeData().invalidDate();var e,t,n,s,i,r,a,o=bn(this._milliseconds)/1e3,u=bn(this._days),l=bn(this._months),d=this.asSeconds();return d?(e=k(o/60),t=k(e/60),o%=60,e%=60,n=k(l/12),l%=12,s=o?o.toFixed(3).replace(/\.?0+$/,""):"",i=Tn(this._months)!==Tn(d)?"-":"",r=Tn(this._days)!==Tn(d)?"-":"",a=Tn(this._milliseconds)!==Tn(d)?"-":"",(d<0?"-":"")+"P"+(n?i+n+"Y":"")+(l?i+l+"M":"")+(u?r+u+"D":"")+(t||e||o?"T":"")+(t?a+t+"H":"")+(e?a+e+"M":"")+(o?a+s+"S":"")):"P0D"}var U=Ct.prototype;return U.isValid=function(){return this._isValid},U.abs=function(){var e=this._data;return this._milliseconds=_n(this._milliseconds),this._days=_n(this._days),this._months=_n(this._months),e.milliseconds=_n(e.milliseconds),e.seconds=_n(e.seconds),e.minutes=_n(e.minutes),e.hours=_n(e.hours),e.months=_n(e.months),e.years=_n(e.years),this},U.add=function(e,t){return yn(this,e,t,1)},U.subtract=function(e,t){return yn(this,e,t,-1)},U.as=function(e){if(!this.isValid())return NaN;var t,n,s=this._milliseconds;if("month"===(e=_(e))||"quarter"===e||"year"===e)switch(t=this._days+s/864e5,n=this._months+wn(t),e){case"month":return n;case"quarter":return n/3;case"year":return n/12}else switch(t=this._days+Math.round(pn(this._months)),e){case"week":return t/7+s/6048e5;case"day":return t+s/864e5;case"hour":return 24*t+s/36e5;case"minute":return 1440*t+s/6e4;case"second":return 86400*t+s/1e3;case"millisecond":return Math.floor(864e5*t)+s;default:throw new Error("Unknown unit "+e)}},U.asMilliseconds=de,U.asSeconds=t,U.asMinutes=ye,U.asHours=he,U.asDays=Fe,U.asWeeks=_e,U.asMonths=me,U.asQuarters=Qe,U.asYears=i,U.valueOf=ce,U._bubble=function(){var e=this._milliseconds,t=this._days,n=this._months,s=this._data;return 0<=e&&0<=t&&0<=n||e<=0&&t<=0&&n<=0||(e+=864e5*gn(pn(n)+t),n=t=0),s.milliseconds=e%1e3,e=k(e/1e3),s.seconds=e%60,e=k(e/60),s.minutes=e%60,e=k(e/60),s.hours=e%24,t+=k(e/24),n+=e=k(wn(t)),t-=gn(pn(e)),e=k(n/12),n%=12,s.days=t,s.months=n,s.years=e,this},U.clone=function(){return C(this)},U.get=function(e){return e=_(e),this.isValid()?this[e+"s"]():NaN},U.milliseconds=we,U.seconds=fe,U.minutes=ge,U.hours=Pe,U.days=g,U.weeks=function(){return k(this.days()/7)},U.months=vn,U.years=Dn,U.humanize=function(e,t){if(!this.isValid())return this.localeData().invalidDate();var n=!1,s=Sn;return"object"==typeof e&&(t=e,e=!1),"boolean"==typeof e&&(n=e),"object"==typeof t&&(s=Object.assign({},Sn,t),null!=t.s&&null==t.ss&&(s.ss=t.s-1)),e=this.localeData(),t=On(this,!n,s,e),n&&(t=e.pastFuture(+this,t)),e.postformat(t)},U.toISOString=xn,U.toString=xn,U.toJSON=xn,U.locale=Xt,U.localeData=Kt,U.toIsoString=e("toIsoString() is deprecated. Please use toISOString() instead (notice the capitals)",xn),U.lang=Ke,s("X",0,0,"unix"),s("x",0,0,"valueOf"),w("x",ke),w("X",/[+-]?\d+(\.\d{1,3})?/),v("X",function(e,t,n){n._d=new Date(1e3*parseFloat(e))}),v("x",function(e,t,n){n._d=new Date(M(e))}),f.version="2.30.1",H=R,f.fn=y,f.min=function(){return Pt("isBefore",[].slice.call(arguments,0))},f.max=function(){return Pt("isAfter",[].slice.call(arguments,0))},f.now=function(){return Date.now?Date.now():+new Date},f.utc=l,f.unix=function(e){return R(1e3*e)},f.months=function(e,t){return fn(e,t,"months")},f.isDate=V,f.locale=ft,f.invalid=I,f.duration=C,f.isMoment=d,f.weekdays=function(e,t,n){return mn(e,t,n,"weekdays")},f.parseZone=function(){return R.apply(null,arguments).parseZone()},f.localeData=P,f.isDuration=Ut,f.monthsShort=function(e,t){return fn(e,t,"monthsShort")},f.weekdaysMin=function(e,t,n){return mn(e,t,n,"weekdaysMin")},f.defineLocale=mt,f.updateLocale=function(e,t){var n,s;return null!=t?(s=ut,null!=W[e]&&null!=W[e].parentLocale?W[e].set(X(W[e]._config,t)):(t=X(s=null!=(n=ct(e))?n._config:s,t),null==n&&(t.abbr=e),(s=new K(t)).parentLocale=W[e],W[e]=s),ft(e)):null!=W[e]&&(null!=W[e].parentLocale?(W[e]=W[e].parentLocale,e===ft()&&ft(e)):null!=W[e]&&delete W[e]),W[e]},f.locales=function(){return ee(W)},f.weekdaysShort=function(e,t,n){return mn(e,t,n,"weekdaysShort")},f.normalizeUnits=_,f.relativeTimeRounding=function(e){return void 0===e?Yn:"function"==typeof e&&(Yn=e,!0)},f.relativeTimeThreshold=function(e,t){return void 0!==Sn[e]&&(void 0===t?Sn[e]:(Sn[e]=t,"s"===e&&(Sn.ss=t-1),!0))},f.calendarFormat=function(e,t){return(e=e.diff(t,"days",!0))<-6?"sameElse":e<-1?"lastWeek":e<0?"lastDay":e<1?"sameDay":e<2?"nextDay":e<7?"nextWeek":"sameElse"},f.prototype=y,f.HTML5_FMT={DATETIME_LOCAL:"YYYY-MM-DDTHH:mm",DATETIME_LOCAL_SECONDS:"YYYY-MM-DDTHH:mm:ss",DATETIME_LOCAL_MS:"YYYY-MM-DDTHH:mm:ss.SSS",DATE:"YYYY-MM-DD",TIME:"HH:mm",TIME_SECONDS:"HH:mm:ss",TIME_MS:"HH:mm:ss.SSS",WEEK:"GGGG-[W]WW",MONTH:"YYYY-MM"},f});
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>成型工序</title>
<meta content="width=device-width,initial-scale=1.0, maximum-scale=1.0,user-scalable=0" name="viewport" />
<link rel="stylesheet" href="/tht_project/static/html/big_screen/element-ui/index.css" />
<script src="/tht_project/static/html/big_screen/js/echarts.min.js"></script>
<script src="/tht_project/static/html/big_screen/js/moment.min.js"></script>
<script src="/tht_project/static/html/big_screen/js/vue.js"></script>
<script src="/tht_project/static/html/big_screen/js/axios.min.js"></script>
<script src="/tht_project/static/html/big_screen/element-ui/index.js"></script>
</head>
<body id="bodyId" style="display: none">
<div id="app" v-loading.body.fullscreen.lock="loading" ref="fullScreenElement">
<div class="pageHeaderBox">
<div class="weatherInfo">
<span class="location">成型工序</span>
</div>
<img class="logo" src="/tht_project/static/html/big_screen/image/logo.png">
<div class="title">天合堂数据指挥中心可视化大屏</div>
<div class="dateTimeBox">
<i style="font-size: 30px;margin-right: 10px;" @click="toggleFullscreen"
:class="isFullscreen ? 'el-icon-close' : 'el-icon-full-screen'"></i>
<div class="time">[[currentTime]]</div>
<div class="dateBox">
<span>[[ currentDate ]]</span>
<span>[[ currentWeek ]]</span>
</div>
</div>
</div>
<div class="pageContentBox">
<div class="pageContentBackgroundBox">
<img class="badgeIcon" src="/tht_project/static/html/big_screen/image/777.png" alt="" v-for="item in 4"
:key="item">
</div>
<div class="pageContentArea">
<div class="contentColumnBox one">
<div class="itemCardBox">
<div class="cardHeader">
<span>水煮线温度</span>
</div>
<div class="cardContentBox">
<div id="waterBoilTempChart" style="width: 100%; height: 100%;"></div>
</div>
</div>
<div class="itemCardBox">
<div class="cardHeader">
<span>静置间温度</span>
</div>
<div class="cardContentBox">
<div id="stationaryTempChart" style="width: 100%; height: 100%;"></div>
</div>
</div>
<div class="itemCardBox">
<div class="cardHeader">
<span>真空包装机罐装速度(次/分钟)</span>
</div>
<div class="cardContentBox">
<div id="vacuumCanningSpeedChart" style="width: 100%; height: 100%;"></div>
</div>
</div>
<div class="itemCardBox">
<div class="cardHeader">
<span>烟熏炉箱温(℃)</span>
</div>
<div class="cardContentBox">
<div id="smudgingFurnaceTemperatureChart" style="width: 100%; height: 100%;"></div>
</div>
</div>
</div>
<div class="contentColumnBox two">
<div class="itemCardBox">
<div class="cardHeader">
<span>水煮线液位控制</span>
</div>
<div class="cardContentBox">
<div id="waterLevelChart" style="width: 100%; height: 100%;"></div>
</div>
</div>
<div class="itemCardBox">
<div class="cardHeader">
<span>静置间设备状态</span>
</div>
<div class="cardContentBox">
<div id="stationaryStatusChart" style="width: 100%; height: 100%;"></div>
</div>
</div>
<div class="itemCardBox">
<div class="cardHeader">
<span>真空包装机设备加工时长(小时)</span>
</div>
<div class="cardContentBox">
<div id="vacuumProcessTimeChart" style="width: 100%; height: 100%;"></div>
</div>
</div>
<div class="itemCardBox">
<div class="cardHeader">
<span>烟熏炉芯温(℃)</span>
</div>
<div class="cardContentBox">
<div id="smokeOvenCoreTemptChart" style="width: 100%; height: 100%;"></div>
</div>
</div>
</div>
<div class="contentColumnBox three">
<div class="itemCardBox">
<div class="cardHeader">
<span>水煮线设备状态</span>
</div>
<div class="cardContentBox">
<div id="waterBoilStatusChart" style="width: 100%; height: 100%;"></div>
</div>
</div>
<div class="itemCardBox">
<div class="cardHeader">
<span>真空包装机真空度</span>
</div>
<div class="cardContentBox">
<div id="vacuumDegreesChart" style="width: 100%; height: 100%;"></div>
</div>
</div>
<div class="itemCardBox">
<div class="cardHeader">
<span>烟熏炉工艺时间(分钟)</span>
</div>
<div class="cardContentBox">
<div id="smokeOvenProcessTimeChart" style="width: 100%; height: 100%;"></div>
</div>
</div>
<div class="itemCardBox">
<div class="cardHeader">
<span>烟熏炉设备资源利用率</span>
</div>
<div class="cardContentBox">
<div id="smokeOvenStatusChart" style="width: 100%; height: 100%;"></div>
</div>
</div>
</div>
<div class="contentColumnBox four">
<div class="itemCardBox">
<div class="cardHeader">
<span>报警中心</span>
</div>
<div class="cardContentBox tableBox">
<el-table class="tableClass" :data="alarmList" height="1" tooltip-effect="light"
:row-style="{height: '30px', backgroundColor: 'transparent'}"
:cell-style="{padding: '5px 0', borderBottom:'none'}"
:header-row-style="{ backgroundColor: 'transparent'}" :header-cell-style="{
backgroundColor: 'transparent',
color: '#01c4f9',
fontWeight: 'bold',
fontSzie:'14px',
height:'30px',
lineHeight:'30px',
padding: '5px 0',
borderBottom:'none'
}">
<el-table-column prop="device" label="报警类型">
<template slot-scope="scope">
<span style="color: #fff; font-size: 12px;">[[ scope.row.device ]]</span>
</template>
</el-table-column>
<el-table-column prop="time" label="报警时间" width="80">
<template slot-scope="scope">
<span style="color: #fff; font-size: 12px;">[[ scope.row.time ]]</span>
</template>
</el-table-column>
<el-table-column prop="detail" label="报警描述" show-overflow-tooltip>
<template slot-scope="scope">
<span style="color: #fff; font-size: 12px;">[[ scope.row.detail ]]</span>
</template>
</el-table-column>
</el-table>
</div>
</div>
<div class="itemCardBox">
<div class="cardHeader">
<span>今日计划执行情况</span>
</div>
<div class="cardContentBox">
<div id="meatGrainAnalysisChart" style="width: 100%; height: 100%;"></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, // 窗口高度
isFullscreen: false, // 全屏状态
baseURL: "https://tht-test.newerp.rokedata.com", // 基地址
dwsBaseUrl: "https://dws-platform.xbg.rokeris.com/dev-api", // dws系统基地址
loading: false, // 全局加载效果
currentTime: "", // 当前时间
currentDate: "", // 当前日期
currentWeek: "", // 当前周
timeInterval: null, // 时间定时器
dateInterval: null, // 日期定时器
charts: {}, // 图表
alarmList: [
{ device: "设备故障", time: "2025-4-23", detail: "切丁机电源了故障" },
{ device: "原料临期", time: "2025-4-22", detail: "酱油将于2025-5-1过期" },
{ device: "原料临期", time: "2025-4-21", detail: "酱油将于2025-5-1过期" },
{ device: "质检不合格", time: "2025-4-21", detail: "有猪肉1斤质检不合格" },
{ device: "质检不合格", time: "2025-4-21", detail: "有猪肉1斤质检不合格" },
{ device: "缺料", time: "2025-4-21", detail: "胡椒粉即将缺料" },
{ device: "缺料", time: "2025-4-21", detail: "牛肉即将缺料" },
{ device: "配比异常", time: "2025-4-23", detail: "淀粉配比不达标" },
{ device: "配比异常", time: "2025-4-21", detail: "淀粉配比不达标" },
{ device: "设备故障", time: "2025-4-21", detail: "切丁机电源了故障" },
{ device: "设备故障", time: "2025-4-21", detail: "切丁机电源了故障" },
{ device: "设备故障", time: "2025-4-21", detail: "切丁机电源了故障" },
{ device: "设备故障", time: "2025-4-21", detail: "切丁机电源了故障" },
{ device: "设备故障", time: "2025-4-21", detail: "切丁机电源了故障" },
{ device: "设备故障", time: "2025-4-21", detail: "切丁机电源了故障" },
],
}
},
computed: {
// 当前位置
currentLocation() {
if (this.currentProvince && this.currentCity) {
return `${this.currentProvince}-${this.currentCity}`
} else {
return "未知城市"
}
}
},
created() {
// 添加resize事件监听
window.addEventListener("resize", this.handleResize)
// 更新日期、时间、周
this.updateDateTime()
// 每秒更新时间
this.timeInterval = setInterval(this.updateTime, 1000)
// 每分钟检查一次日期是否需要更新(每天只需更新一次)
this.dateInterval = setInterval(() => {
const now = new Date()
if (now.getHours() === 0 && now.getMinutes() === 0) {
this.updateDate()
this.updateWeek()
}
}, 60000)
},
mounted() {
this.$nextTick(() => {
document.getElementById("bodyId").style.display = "block"
this.initAllCharts()
})
},
methods: {
// 全屏icon点击事件
toggleFullscreen: function () {
if (!this.isFullscreen) {
this.enterFullScreen()
} else {
this.exitFullScreen()
}
},
// 全屏方法
enterFullScreen: function () {
// 获取需要全屏的元素
var elem = this.$refs.fullScreenElement
if (elem && elem.requestFullscreen) {
elem.requestFullscreen()
} else if (elem && elem.mozRequestFullScreen) {
// Firefox
elem.mozRequestFullScreen()
} else if (elem && elem.webkitRequestFullscreen) {
// Chrome, Safari & Opera
elem.webkitRequestFullscreen()
} else if (elem && elem.msRequestFullscreen) {
// IE/Edge
elem.msRequestFullscreen()
}
this.isFullscreen = true
},
// 退出全屏
exitFullScreen: function () {
if (document.exitFullscreen) {
document.exitFullscreen()
} else if (document.mozCancelFullScreen) {
// Firefox
document.mozCancelFullScreen()
} else if (document.webkitExitFullscreen) {
// Chrome, Safari and Opera
document.webkitExitFullscreen()
} else if (document.msExitFullscreen) {
// IE/Edge
document.msExitFullscreen()
}
this.isFullscreen = false
},
// 处理窗口大小变化修改图表大小
handleResize() {
for (let key in this.charts) {
this.charts[key] && this.charts[key].resize()
}
},
// 更新日期、时间、周
updateDateTime() {
this.updateTime()
this.updateDate()
this.updateWeek()
},
// 更新时间
updateTime() {
this.currentTime = moment().format('HH:mm:ss')
},
// 更新日期
updateDate() {
this.currentDate = moment().format('YYYY-MM-DD')
},
// 更新周
updateWeek() {
this.currentWeek = '周' + ['日', '一', '二', '三', '四', '五', '六'][moment().day()]
},
// 接口请求方法封装
requestApi(
url,
config = {},
errorMessage = "操作失败,请稍后重试",
contentType = "application/json"
) {
return new Promise((resolve, reject) => {
if (!url) reject(null)
axios({
method: "POST",
url: this.baseURL + url,
data: config,
headers: { "Content-Type": contentType },
}).then((result) => {
if (
result?.data?.result?.code == 0 ||
result?.data?.result?.state == "success" ||
result?.data?.code == 0
) {
resolve(result.data)
} else if (result?.data?.result?.code == 1) {
reject(result.data.result.message)
} else if (result?.data?.result?.state == "error") {
reject(result.data.result.megs)
} else if (result?.data?.code == 0) {
reject(result.data.message)
} else if (result?.data?.error) {
reject(result.data.error.message)
}
}).catch((error) => {
reject(errorMessage)
})
})
},
requestSelfApi(
url,
config = {},
errorMessage = "操作失败,请稍后重试",
contentType = "application/json",
baseurl="https://tht.dws.rokecloud.com"
) {
return new Promise((resolve, reject) => {
if (!url) reject(null)
axios({
method: "POST",
url: baseurl + url,
data: config,
headers: { "Content-Type": contentType },
}).then((result) => {
if (
result?.data?.result?.success ||
result?.data?.result?.code == 0 ||
result?.data?.result?.state == "success" ||
result?.data?.code == 0
) {
resolve(result.data)
} else if (result?.data?.result?.code == 1) {
reject(result.data.result.message)
} else if (result?.data?.result?.state == "error") {
reject(result.data.result.megs)
} else if (result?.data?.code == 1) {
reject(result.data.message)
} else if (!result?.data?.result?.success) {
reject(result.data.result.msg)
} else if (result?.data?.error) {
reject(result.data.error.message)
}
}).catch((error) => {
reject(errorMessage)
})
})
},
// 接口请求Dws系统方法封装
requestDwsApi(
url,
config = {},
errorMessage = "操作失败,请稍后重试",
contentType = "application/json"
) {
return new Promise((resolve, reject) => {
if (!url) reject(null)
axios({
method: "POST",
url: this.dwsBaseUrl + url,
data: config,
headers: { "Content-Type": contentType },
}).then((result) => {
if (result?.data?.success) {
resolve(result.data)
} else if (!result.data.success) {
reject(result.data.msg)
} else {
reject(errorMessage)
}
}).catch((error) => {
reject(errorMessage)
})
})
},
// 初始化所有图表
async initAllCharts() {
this.loading = true
// 烟熏机
let smudgingChance = {
xAxisData: [], // 设备名
processTime: [], // 工艺时间
temperatureX: [], // 箱温
xinwen: [], // 芯温
useRatio: []// 设备资源利用率
}
// 查询烟熏机数据
await this.requestSelfApi('/get_yanxunlu_data', {
"plant_name": "蒸煮车间",
"category_name": "烟熏炉",
'today':'',
}).then(result => {
result.result.data.forEach(item => {
smudgingChance.xAxisData.push(item.device_name)
smudgingChance.processTime.push(item.process)
smudgingChance.temperatureX.push(item.temperatureX)
smudgingChance.xinwen.push(item.xinwen)
})
}).catch(error => { console.error(error) })
// 查询烟熏机设备资源利用率
await this.requestSelfApi('/big_screen_today', {
"plant_name": "蒸煮车间",
"category_name": "烟熏炉",
'today':'',
}).then(result => {
}).catch(error => { console.error(error) })
setTimeout(() => {
this.initWaterBoilTempChart()
this.initStationaryTempChart()
this.initWaterLevelChart()
this.initVacuumCanningSpeedChart()
this.initSmudgingFurnaceTemperatureChart(smudgingChance.xAxisData, smudgingChance.temperatureX)
this.initStationaryStatusChart()
this.initVacuumProcessTimeChart()
this.initSmokeOvenCoreTemptChart(smudgingChance.xAxisData, smudgingChance.xinwen)
this.initWaterBoilStatusChart()
this.initVacuumDegreesChart()
this.initSmokeOvenProcessTimeChart(smudgingChance.xAxisData, smudgingChance.processTime)
this.initSmokeOvenStatusChart(smudgingChance.xAxisData, smudgingChance.useRatio)
this.initMeatGrainAnalysisChart()
}, 1)
this.loading = false
},
// 水煮线温度
initWaterBoilTempChart() {
const chart = echarts.init(document.getElementById('waterBoilTempChart'))
this.charts.waterBoilTemp = chart
// 单位
const unit = ''
// x轴数据
const xAxisData = ['水煮线1', '水煮线2', '水煮线3', '水煮线4']
// 数量
const seriesData = [100, 140, 230, 50]
// 设置最大值处理显示数值时和顶部重叠问题; 三方表达式:防止无数据时不显示y轴
const max = [...seriesData].length ? Math.ceil(Math.max(...[...seriesData]) * 1.5) : 100
chart.setOption({
grid: {
left: '5%', right: '5%', top: '8%', bottom: '5%',
containLabel: true,
show: true,
borderWidth: 0,
backgroundColor: 'rgba(0, 0, 0, 0)',
shadowColor: 'rgba(0, 0, 0, 0.3)',
shadowBlur: 2
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
valueFormatter: (value) => value + unit
},
dataZoom: [{
id: 'waterBoilTempChart',
type: 'inside', // 表明在图表内部进行拖动
start: 0,
end: (3 / xAxisData.length) * 100, // 默认显示3条
filterMode: 'none'
}],
xAxis: {
type: 'category',
data: xAxisData,
axisLine: { lineStyle: { color: '#0a73ff' } },
axisLabel: { color: '#fff', fontSize: 10 }
},
yAxis: {
type: 'value',
splitNumber: 4,
min: 0,
max: max,
axisLine: { show: true, lineStyle: { color: '#0a73ff' } },
splitLine: { lineStyle: { color: '#0a73ff', width: 0.5, opacity: 0.3 } },
axisLabel: { color: '#fff', fontSize: 10, formatter: '{value}' + unit }
},
series: [{
type: 'bar',
barWidth: '40%',
barGap: '0%',
data: seriesData,
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#01c4f9' },
{ offset: 1, color: '#0a73ff' }
])
},
label: {
show: true,
position: 'top',
color: '#FFF',
fontSize: 10,
formatter: (params) => params.value + unit
}
}]
})
},
// 静置间温度
initStationaryTempChart() {
const chart = echarts.init(document.getElementById('stationaryTempChart'))
this.charts.stationaryTemp = chart
// 数值
const valueData = {
standing_room_1: 60, // 静置间1
standing_room_2: 80, // 静置间2
}
// series配置
const seriesData = []
for (let i = 0; i < 2; i++) {
let data = {
name: '',
type: 'gauge',
center: [],
radius: '60%',
startAngle: 200,
endAngle: -20,
splitNumber: 5,
itemStyle: { color: '#3a8fff' },
progress: { show: true, width: 5 },
pointer: { show: false },
axisLine: { lineStyle: { width: 5 } },
axisTick: {
distance: -15,
length: 5,
splitNumber: 5,
lineStyle: { width: 1, color: '#999' }
},
splitLine: {
distance: -15,
length: 8,
lineStyle: { width: 1, color: '#999' }
},
axisLabel: { distance: -15, color: '#999', fontSize: 10, },
detail: {
valueAnimation: true,
offsetCenter: [0, 0],
fontSize: 10,
formatter: '{value}℃',
color: 'inherit'
},
data: [0]
}
if (i == 0) {
data.name = '静置间1'
data.center = ['25%', '70%']
data.data = [valueData.standing_room_1 || 0]
} else if (i == 1) {
data.name = '静置间2'
data.center = ['75%', '70%']
data.data = [valueData.standing_room_2 || 0]
}
seriesData.push(data)
}
chart.setOption({
grid: { left: '5%', right: '5%', top: '8%', bottom: '5%' },
graphic: [{
type: 'text',
left: '19%',
top: '80%',
style: { text: '静置间1', fill: '#fff', fontSize: 10 }
}, {
type: 'text',
left: '69%',
top: '80%',
style: { text: '静置间2', fill: '#fff', fontSize: 10 }
}],
series: seriesData
})
},
// 真空包装机罐装速度
initVacuumCanningSpeedChart() {
const chart = echarts.init(document.getElementById('vacuumCanningSpeedChart'))
this.charts.vacuumCanningSpeed = chart
// 单位
const unit = '次/分钟'
// x轴数据
const xAxisData = ['罐装机1', '罐装机2', '罐装机3', '罐装机4']
// 数量
const seriesData = [100, 140, 230, 50]
// 设置最大值处理显示数值时和顶部重叠问题; 三方表达式:防止无数据时不显示y轴
const max = [...seriesData].length ? Math.ceil(Math.max(...[...seriesData])) * 1.5 : 100
chart.setOption({
grid: {
left: '5%', right: '5%', top: '8%', bottom: '5%',
containLabel: true,
show: true,
borderWidth: 0,
backgroundColor: 'rgba(0, 0, 0, 0)',
shadowColor: 'rgba(0, 0, 0, 0.3)',
shadowBlur: 2
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
valueFormatter: (value) => value + unit
},
dataZoom: [{
id: 'waterBoilTempChart',
type: 'inside', // 表明在图表内部进行拖动
start: 0,
end: (3 / xAxisData.length) * 100, // 默认显示3条
filterMode: 'none'
}],
xAxis: {
type: 'category',
data: xAxisData,
axisLine: { lineStyle: { color: '#0a73ff' } },
axisLabel: { color: '#fff', fontSize: 10 }
},
yAxis: {
type: 'value',
splitNumber: 4,
min: 0,
max: max,
axisLine: { show: true, lineStyle: { color: '#0a73ff' } },
splitLine: { lineStyle: { color: '#0a73ff', width: 0.5, opacity: 0.3 } },
axisLabel: { color: '#fff', fontSize: 10, formatter: '{value}' + unit }
},
series: [{
type: 'bar',
barWidth: '40%',
barGap: '0%',
data: seriesData,
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#01c4f9' },
{ offset: 1, color: '#0a73ff' }
])
},
label: {
show: true,
position: 'top',
color: '#FFF',
fontSize: 10,
formatter: (params) => params.value + unit
}
}]
})
},
// 烟熏炉箱温
initSmudgingFurnaceTemperatureChart(xAxisData = [], temperatureX = []) {
const chart = echarts.init(document.getElementById('smudgingFurnaceTemperatureChart'))
this.charts.smudgingFurnaceTemperature = chart
// 单位
const unit = '℃'
// 设置最大值处理显示数值时和顶部重叠问题; 三方表达式:防止无数据时不显示y轴
const max = [...temperatureX].length ? Math.ceil(Math.max(...[...temperatureX]) * 1.5) : 100
chart.setOption({
grid: {
left: '5%', right: '5%', top: '8%', bottom: '5%',
containLabel: true,
show: true,
borderWidth: 0,
backgroundColor: 'rgba(0, 0, 0, 0)',
shadowColor: 'rgba(0, 0, 0, 0.3)',
shadowBlur: 2
},
tooltip: {
trigger: 'axis',
valueFormatter: (value) => value + unit
},
dataZoom: [{
id: 'waterLevelChart',
type: 'inside', // 表明在图表内部进行拖动
start: 0,
end: (3 / xAxisData.length) * 100, // 默认显示3条
filterMode: 'none'
}],
xAxis: {
type: 'category',
boundaryGap: false,
data: xAxisData,
axisLine: { show: true, lineStyle: { color: '#0a73ff' } },
axisTick: { show: false },
axisLabel: { color: '#fff', fontSize: 10, interval: 0 }
},
yAxis: {
type: 'value',
splitNumber: 4,
min: 0,
max: max,
axisLine: { show: true, lineStyle: { color: '#0a73ff' } },
splitLine: { lineStyle: { color: '#0a73ff', width: 0.5, opacity: 0.3, type: 'dashed' } },
axisLabel: { color: '#ccc', fontSize: 10, formatter: '{value}' + unit }
},
series: [{
type: 'line',
data: temperatureX,
label: {
show: true,
position: 'top',
color: '#FFF',
fontSize: 10,
formatter: (params) => params.value + unit
}
}]
})
},
// 水煮线液位控制
initWaterLevelChart() {
const chart = echarts.init(document.getElementById('waterLevelChart'))
this.charts.waterLevel = chart
// x轴数据
const xAxisData = ['水煮线1', '水煮线2', '水煮线3', '水煮线4']
// 数量
const seriesData = [20, 50, 30, 100]
// 单位
const unit = '%'
// 设置最大值处理显示数值时和顶部重叠问题; 三方表达式:防止无数据时不显示y轴
const max = [...seriesData].length ? Math.ceil(Math.max(...[...seriesData]) * 1.5) : 100
chart.setOption({
grid: {
left: '5%', right: '5%', top: '8%', bottom: '5%',
containLabel: true,
show: true,
borderWidth: 0,
backgroundColor: 'rgba(0, 0, 0, 0)',
shadowColor: 'rgba(0, 0, 0, 0.3)',
shadowBlur: 2
},
tooltip: {
trigger: 'axis',
valueFormatter: (value) => value + unit
},
dataZoom: [{
id: 'waterLevelChart',
type: 'inside', // 表明在图表内部进行拖动
start: 0,
end: (3 / xAxisData.length) * 100, // 默认显示3条
filterMode: 'none'
}],
xAxis: {
type: 'category',
boundaryGap: false,
data: xAxisData,
axisLine: { show: true, lineStyle: { color: '#0a73ff' } },
axisTick: { show: false },
axisLabel: { color: '#fff', fontSize: 10, interval: 0 }
},
yAxis: {
type: 'value',
splitNumber: 4,
min: 0,
max: max,
axisLine: { show: true, lineStyle: { color: '#0a73ff' } },
splitLine: { lineStyle: { color: '#0a73ff', width: 0.5, opacity: 0.3, type: 'dashed' } },
axisLabel: { color: '#ccc', fontSize: 10, formatter: '{value}' + unit }
},
series: [{
type: 'line',
data: seriesData,
label: {
show: true,
position: 'top',
color: '#FFF',
fontSize: 10,
formatter: (params) => params.value + unit
}
}]
})
},
// 静置间设备状态
initStationaryStatusChart() {
const chart = echarts.init(document.getElementById('stationaryStatusChart'))
this.charts.stationaryStatus = chart
const unit = ''
// x轴数据
const xAxisData = ['静置间1', '静置间2', '静置间3', '静置间4']
// 数量
const seriesData = [100, 140, 230, 50]
// 设置最大值处理显示数值时和顶部重叠问题; 三方表达式:防止无数据时不显示y轴
const max = [...seriesData].length ? Math.ceil(Math.max(...[...seriesData]) * 1.5) : 100
chart.setOption({
grid: {
left: '5%', right: '5%', top: '8%', bottom: '5%',
containLabel: true,
show: true,
borderWidth: 0,
backgroundColor: 'rgba(0, 0, 0, 0)',
shadowColor: 'rgba(0, 0, 0, 0.3)',
shadowBlur: 2
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
valueFormatter: (value) => value + unit
},
dataZoom: [{
id: 'stationaryStatusChart',
type: 'inside', // 表明在图表内部进行拖动
start: 0,
end: (3 / xAxisData.length) * 100, // 默认显示3条
filterMode: 'none'
}],
xAxis: {
type: 'category',
data: xAxisData,
axisLine: { lineStyle: { color: '#0a73ff' } },
axisLabel: { color: '#fff', fontSize: 10 }
},
yAxis: {
type: 'value',
splitNumber: 4,
min: 0,
max: max,
axisLine: { show: true, lineStyle: { color: '#0a73ff' } },
splitLine: { lineStyle: { color: '#0a73ff', width: 0.5, opacity: 0.3 } },
axisLabel: { color: '#fff', fontSize: 10, formatter: '{value}' + unit }
},
series: [{
type: 'bar',
barWidth: '40%',
barGap: '0%',
data: seriesData,
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#01c4f9' },
{ offset: 1, color: '#0a73ff' }
])
},
label: {
show: true,
position: 'top',
color: '#FFF',
fontSize: 10,
formatter: (params) => params.value + unit
}
}]
})
},
// 真空包装机设备加工时长
initVacuumProcessTimeChart() {
const chart = echarts.init(document.getElementById('vacuumProcessTimeChart'))
this.charts.vacuumProcessTime = chart
// 单位
const unit = 'h'
// x轴数据
const xAxisData = ['罐装机1', '罐装机2', '罐装机3', '罐装机4']
// 数量
const seriesData = [100, 140, 230, 50]
// 设置最大值处理显示数值时和顶部重叠问题; 三方表达式:防止无数据时不显示y轴
const max = [...seriesData].length ? Math.ceil(Math.max(...[...seriesData]) * 1.5) : 100
chart.setOption({
grid: {
left: '5%', right: '5%', top: '8%', bottom: '5%',
containLabel: true,
show: true,
borderWidth: 0,
backgroundColor: 'rgba(0, 0, 0, 0)',
shadowColor: 'rgba(0, 0, 0, 0.3)',
shadowBlur: 2
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
valueFormatter: (value) => value + unit
},
dataZoom: [{
id: 'vacuumProcessTimeChart',
type: 'inside', // 表明在图表内部进行拖动
start: 0,
end: (3 / xAxisData.length) * 100, // 默认显示3条
filterMode: 'none'
}],
xAxis: {
type: 'category',
data: xAxisData,
axisLine: { lineStyle: { color: '#0a73ff' } },
axisLabel: { color: '#fff', fontSize: 10 }
},
yAxis: {
type: 'value',
splitNumber: 4,
min: 0,
max: max,
axisLine: { show: true, lineStyle: { color: '#0a73ff' } },
splitLine: { lineStyle: { color: '#0a73ff', width: 0.5, opacity: 0.3 } },
axisLabel: { color: '#fff', fontSize: 10, formatter: '{value}' + unit }
},
series: [{
type: 'bar',
barWidth: '40%',
barGap: '0%',
data: seriesData,
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#01c4f9' },
{ offset: 1, color: '#0a73ff' }
])
},
label: {
show: true,
position: 'top',
color: '#FFF',
fontSize: 10,
formatter: (params) => params.value + unit
}
}]
})
},
// 烟熏炉芯温
initSmokeOvenCoreTemptChart(xAxisData = [], xinwen = []) {
const chart = echarts.init(document.getElementById('smokeOvenCoreTemptChart'))
this.charts.smokeOvenCoreTempt = chart
// 单位
const unit = '℃'
// 设置最大值处理显示数值时和顶部重叠问题; 三方表达式:防止无数据时不显示y轴
const max = [...xinwen].length ? Math.ceil(Math.max(...[...xinwen]) * 1.5) : 100
chart.setOption({
grid: {
left: '5%', right: '5%', top: '8%', bottom: '5%',
containLabel: true,
show: true,
borderWidth: 0,
backgroundColor: 'rgba(0, 0, 0, 0)',
shadowColor: 'rgba(0, 0, 0, 0.3)',
shadowBlur: 2
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
valueFormatter: (value) => value + unit
},
dataZoom: [{
id: 'vacuumProcessTimeChart',
type: 'inside', // 表明在图表内部进行拖动
start: 0,
end: (3 / xAxisData.length) * 100, // 默认显示3条
filterMode: 'none'
}],
xAxis: {
type: 'category',
data: xAxisData,
axisLine: { lineStyle: { color: '#0a73ff' } },
axisLabel: { color: '#fff', fontSize: 10 }
},
yAxis: {
type: 'value',
splitNumber: 4,
min: 0,
max: max,
axisLine: { show: true, lineStyle: { color: '#0a73ff' } },
splitLine: { lineStyle: { color: '#0a73ff', width: 0.5, opacity: 0.3 } },
axisLabel: { color: '#fff', fontSize: 10, formatter: '{value}' + unit }
},
series: [{
type: 'bar',
barWidth: '40%',
barGap: '0%',
data: xinwen,
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#01c4f9' },
{ offset: 1, color: '#0a73ff' }
])
},
label: {
show: true,
position: 'top',
color: '#FFF',
fontSize: 10,
formatter: (params) => params.value + unit
}
}]
})
},
// 水煮线设备状态
initWaterBoilStatusChart() {
const chart = echarts.init(document.getElementById('waterBoilStatusChart'))
this.charts.waterBoilStatus = chart
chart.setOption({
tooltip: {
trigger: 'item',
valueFormatter: (value) => value + '台',
},
legend: {
orient: 'vertical',
left: 'top',
itemWidth: 20,
itemHeight: 10,
itemGap: 5,
textStyle: {
color: "#fff",
fontSize: 10
}
},
series: [{
type: 'pie',
radius: '60%',
center: ['60%', '45%'],
itemStyle: {
borderRadius: 0,
borderColor: '#061058',
borderWidth: 2
},
label: {
show: true,
formatter: '{c}台',
color: '#fff',
fontSize: 12
},
data: [{
value: 50,
name: '运行',
itemStyle: { color: '#91cc75' },
}, {
value: 10,
name: '等待',
itemStyle: { color: '#ffd700' },
}, {
value: 5,
name: '故障',
itemStyle: { color: '#ee6666' },
}, {
value: 10,
name: '关机',
itemStyle: { color: '#787878' },
}]
}]
})
},
// 真空包装机真空度
initVacuumDegreesChart() {
const chart = echarts.init(document.getElementById('vacuumDegreesChart'))
this.charts.vacuumDegrees = chart
// x轴数据
const xAxisData = ['罐装机1', '罐装机2', '罐装机3', '罐装机4']
// 数量
const seriesData = [20, 50, 30, 100]
// 单位
const unit = 'kpa'
// 设置最大值处理显示数值时和顶部重叠问题; 三方表达式:防止无数据时不显示y轴
const max = [...seriesData].length ? Math.ceil(Math.max(...[...seriesData]) * 1.5) : 100
chart.setOption({
grid: {
left: '5%', right: '5%', top: '8%', bottom: '5%',
containLabel: true,
show: true,
borderWidth: 0,
backgroundColor: 'rgba(0, 0, 0, 0)',
shadowColor: 'rgba(0, 0, 0, 0.3)',
shadowBlur: 2
},
tooltip: {
trigger: 'axis',
valueFormatter: (value) => value + unit
},
dataZoom: [{
id: 'vacuumDegreesChart',
type: 'inside', // 表明在图表内部进行拖动
start: 0,
end: (3 / xAxisData.length) * 100, // 默认显示3条
filterMode: 'none'
}],
xAxis: {
type: 'category',
boundaryGap: false,
data: xAxisData,
axisLine: { show: true, lineStyle: { color: '#0a73ff' } },
axisTick: { show: false },
axisLabel: { color: '#fff', fontSize: 10, interval: 0 }
},
yAxis: {
type: 'value',
splitNumber: 4,
min: 0,
max: max,
axisLine: { show: true, lineStyle: { color: '#0a73ff' } },
splitLine: { lineStyle: { color: '#0a73ff', width: 0.5, opacity: 0.3, type: 'dashed' } },
axisLabel: { color: '#ccc', fontSize: 10, formatter: '{value}' + unit }
},
series: [{
type: 'line',
data: seriesData,
label: {
show: true,
position: 'top',
color: '#FFF',
fontSize: 10,
formatter: (params) => params.value + unit
}
}]
})
},
// 烟熏炉工艺时间
initSmokeOvenProcessTimeChart(xAxisData = [], processTime = []) {
const chart = echarts.init(document.getElementById('smokeOvenProcessTimeChart'))
this.charts.smokeOvenProcessTime = chart
// 单位
const unit = 'min'
// 设置最大值处理显示数值时和顶部重叠问题; 三方表达式:防止无数据时不显示y轴
const max = [...processTime].length ? Math.ceil(Math.max(...[...processTime]) * 1.5) : 100
chart.setOption({
grid: {
left: '5%', right: '5%', top: '8%', bottom: '5%',
containLabel: true,
show: true,
borderWidth: 0,
backgroundColor: 'rgba(0, 0, 0, 0)',
shadowColor: 'rgba(0, 0, 0, 0.3)',
shadowBlur: 2
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
valueFormatter: (value) => value + unit
},
dataZoom: [{
id: 'vacuumProcessTimeChart',
type: 'inside', // 表明在图表内部进行拖动
start: 0,
end: (3 / xAxisData.length) * 100, // 默认显示3条
filterMode: 'none'
}],
xAxis: {
type: 'category',
data: xAxisData,
axisLine: { lineStyle: { color: '#0a73ff' } },
axisLabel: { color: '#fff', fontSize: 10 }
},
yAxis: {
type: 'value',
splitNumber: 4,
min: 0,
max: max,
axisLine: { show: true, lineStyle: { color: '#0a73ff' } },
splitLine: { lineStyle: { color: '#0a73ff', width: 0.5, opacity: 0.3 } },
axisLabel: { color: '#fff', fontSize: 10, formatter: '{value}' + unit }
},
series: [{
type: 'bar',
barWidth: '40%',
barGap: '0%',
data: processTime,
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#01c4f9' },
{ offset: 1, color: '#0a73ff' }
])
},
label: {
show: true,
position: 'top',
color: '#FFF',
fontSize: 10,
formatter: (params) => params.value + unit
}
}]
})
},
// 烟熏炉设备资源利用率
initSmokeOvenStatusChart() {
const chart = echarts.init(document.getElementById('smokeOvenStatusChart'))
this.charts.smokeOvenStatus = chart
// 单位
const unit = '%'
// x轴数据
const xAxisData = ['烟熏炉1', '烟熏炉2', '烟熏炉3', '烟熏炉4']
// 数量
const seriesData = [100, 140, 230, 50]
// 设置最大值处理显示数值时和顶部重叠问题; 三方表达式:防止无数据时不显示y轴
const max = [...seriesData].length ? Math.ceil(Math.max(...[...seriesData]) * 1.5) : 100
chart.setOption({
grid: {
left: '5%', right: '5%', top: '8%', bottom: '5%',
containLabel: true,
show: true,
borderWidth: 0,
backgroundColor: 'rgba(0, 0, 0, 0)',
shadowColor: 'rgba(0, 0, 0, 0.3)',
shadowBlur: 2
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
valueFormatter: (value) => value + unit
},
dataZoom: [{
id: 'smokeOvenStatusChart',
type: 'inside', // 表明在图表内部进行拖动
start: 0,
end: (3 / xAxisData.length) * 100, // 默认显示3条
filterMode: 'none'
}],
xAxis: {
type: 'category',
data: xAxisData,
axisLine: { lineStyle: { color: '#0a73ff' } },
axisLabel: { color: '#fff', fontSize: 10 }
},
yAxis: {
type: 'value',
splitNumber: 4,
min: 0,
max: max,
axisLine: { show: true, lineStyle: { color: '#0a73ff' } },
splitLine: { lineStyle: { color: '#0a73ff', width: 0.5, opacity: 0.3 } },
axisLabel: { color: '#fff', fontSize: 10, formatter: '{value}' + unit }
},
series: [{
type: 'bar',
barWidth: '40%',
barGap: '0%',
data: seriesData,
itemStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#01c4f9' },
{ offset: 1, color: '#0a73ff' }
])
},
label: {
show: true,
position: 'top',
color: '#FFF',
fontSize: 10,
formatter: (params) => params.value + unit
}
}]
})
},
// 今日计划执行情况
initMeatGrainAnalysisChart() {
const chart = echarts.init(document.getElementById('meatGrainAnalysisChart'))
this.charts.meatGrainAnalysis = chart
// x轴数据
const xAxisData = ['产品1', '产品2', '产品3', '产品4']
// 投产数量
const operationData = [{
value: 100,
uom_name: 'kg'
}, {
value: 140,
uom_name: '件'
}, {
value: 230,
uom_name: 'm'
}, {
value: 50,
uom_name: 'km'
}]
// 完成数量
const quantityData = [{
value: 20,
uom_name: 'kg'
}, {
value: 140,
uom_name: '件'
}, {
value: 90,
uom_name: 'm'
}, {
value: 290,
uom_name: 'km'
}]
// 设置最大值处理显示数值时和顶部重叠问题; 三方表达式:防止无数据时不显示y轴
const max = this.calculateMaxValue([operationData, quantityData], 1.5)
chart.setOption({
grid: {
left: '5%', right: '5%', top: '15%', bottom: '5%',
containLabel: true,
show: true,
borderWidth: 0,
backgroundColor: 'rgba(0, 0, 0, 0)',
shadowColor: 'rgba(0, 0, 0, 0.3)',
shadowBlur: 2
},
legend: {
top: '-2%',
right: '5%',
textStyle: { color: '#fff', fontSize: 10 },
itemWidth: 20,
itemHeight: 10,
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
formatter: (params) => {
let tooltipHtml = `<div style="font-weight:bold;margin-bottom:5px">${params[0].name}</div>`
params.forEach(param => {
const dataIndex = param.dataIndex
const unit = param.seriesIndex === 0 ? operationData[dataIndex].unit : quantityData[dataIndex].unit
// 使用系列颜色作为小圆点颜色
tooltipHtml += `
<div style="display:flex;align-items:center;margin:5px 0">
<span style="display:inline-block;width:10px;height:10px;border-radius:50%;background:${param.color};margin-right:10px"></span>
<span style="margin-right:10px">${param.seriesName}:</span>
<strong>${param.value}</strong>
<span>${unit}</span>
</div>
`
})
// 计算完成率
const total = params[0].value
const completed = params[1].value
const percentage = total > 0 ? ((completed / total) * 100).toFixed(1) : 0
tooltipHtml += `
<div style="margin-top:10px;border-top:1px solid #eee;padding-top:5px">
完成率: <strong>${percentage}%</strong>
</div>
`
return tooltipHtml
}
},
dataZoom: [{
id: 'meatGrainAnalysisChart',
type: 'inside', // 表明在图表内部进行拖动
start: 0,
end: (3 / xAxisData.length) * 100, // 默认显示3条
filterMode: 'none'
}],
xAxis: {
type: 'category',
data: xAxisData,
axisLine: { lineStyle: { color: '#0a73ff' } },
axisLabel: { color: '#fff', fontSize: 10 }
},
yAxis: {
type: 'value',
splitNumber: 5,
min: 0,
max: max,
axisLine: { show: true, lineStyle: { color: '#0a73ff' } },
splitLine: { lineStyle: { color: '#0a73ff', width: 0.5, opacity: 0.3 } },
axisLabel: { color: '#fff', fontSize: 10 }
},
series: [{
name: '投产数量',
type: 'bar',
barWidth: '40%',
barGap: '0%',
data: operationData,
itemStyle: { color: '#5087ec' },
label: {
show: true,
position: 'top',
color: '#FFF',
fontSize: 10,
formatter: (params) => params.value + params.data.unit
}
}, {
name: '完成数量',
type: 'bar',
barWidth: '40%',
barGap: '0%',
data: quantityData,
itemStyle: { color: '#68bbc4' },
label: {
show: true,
position: 'top',
color: '#FFF',
fontSize: 10,
formatter: (params) => params.value + params.data.unit
}
}]
})
},
/**
* 计算图表Y轴最大值
* @param {Array} dataArrays 需要合并计算的数据数组
* @param {number} multiplier 放大系数,默认1.2
* @param {number} defaultValue 默认最大值,当无数据时使用
* @returns {number} 计算后的最大值
*/
calculateMaxValue(dataArrays, multiplier = 1.2, defaultValue = 100) {
// 合并所有数组并提取value值
const allValues = dataArrays.flat().map(item => item.value);
// 如果没有数据,返回默认值
if (allValues.length === 0) return defaultValue;
// 计算最大值并应用放大系数,然后向上取整
const maxValue = Math.max(...allValues);
return Math.ceil(maxValue * multiplier);
}
},
beforeDestroy() {
// 清除时间定时器
clearInterval(this.timeInterval)
// 清除日期定时器
clearInterval(this.dateInterval)
// 清除resize事件监听
window.removeEventListener("resize", this.handleResize)
}
})
</script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
::-webkit-scrollbar {
width: 0px;
height: 0px;
}
::-webkit-scrollbar-thumb {
background: #c0c4cc;
border-radius: 3px;
}
::-webkit-scrollbar-track {
background: #f5f7fa;
}
#app {
width: 100vw;
height: 100vh;
overflow: hidden;
color: #fff;
font-family: "Microsoft YaHei", sans-serif;
background-color: #06114f;
display: flex;
flex-direction: column;
}
.pageHeaderBox {
height: 60px;
display: flex;
justify-content: center;
align-items: center;
padding: 0 20px;
border-bottom: 1px solid rgba(10, 115, 255, 0.3);
background-image: url(/tht_project/static/html/big_screen/image/header_background.png);
background-size: 100%;
position: relative;
font-size: 24px;
z-index: 2;
.weatherInfo {
position: absolute;
left: 20px;
bottom: 5px;
font-size: 23px;
display: flex;
align-items: baseline;
gap: 5px;
.location {
font-weight: bold;
}
.icon {
margin-top: 5px;
}
}
.logo {
width: 1.3em;
height: 1.3em;
}
.title {
font-weight: bold;
letter-spacing: 4px;
margin-left: 10px;
}
.dateTimeBox {
position: absolute;
right: 20px;
bottom: 5px;
display: flex;
align-items: center;
font-size: 24px;
.time {
margin-right: 5px;
}
.dateBox {
display: flex;
flex-direction: column;
font-size: 10px;
}
}
}
.pageContentBox {
width: 100%;
height: 1px;
flex: auto;
position: relative;
padding: 10px;
.pageContentBackgroundBox {
position: absolute;
width: calc(100% - 20px);
height: calc(100% - 20px);
padding: 10px;
border-left: 2px solid #cbdbf8;
border-right: 2px solid #cbdbf8;
background: transparent;
border-radius: 10px;
.badgeIcon {
position: absolute;
background-color: #06114f;
}
.badgeIcon:first-child {
top: 0;
left: 0;
transform: translate(-12%, -35%);
}
.badgeIcon:nth-child(2) {
top: 0;
right: 0;
transform: translate(12%, -35%) scaleX(-1);
}
.badgeIcon:nth-child(3) {
bottom: 0;
left: 0;
transform: translate(-12%, 35%) scaley(-1);
}
.badgeIcon:last-child {
bottom: 0;
right: 0;
transform: translate(12%, 35%) scale(-1);
}
}
/* 发光小点 */
.pageContentBackgroundBox::before,
.pageContentBackgroundBox::after {
content: '';
position: absolute;
top: 30px;
transform: translateX(-30%);
width: 0px;
height: 10px;
background: #fff;
border-radius: 50%;
box-shadow: 0 0 15px 3px rgba(255, 255, 255, 0.8),
0 0 15px 3px rgba(255, 255, 255, 0.8);
animation: dot-move 4s linear infinite;
z-index: 2;
}
.pageContentBackgroundBox::before {
/* 定位在左边框 */
left: -1px;
}
/* 发光小点 */
.pageContentBackgroundBox::after {
/* 定位在右边框 */
right: -1px;
}
.pageContentArea {
display: flex;
gap: 5px;
padding: 10px;
height: 100%;
.contentColumnBox {
height: 100%;
display: flex;
flex-direction: column;
gap: 5px;
z-index: 1;
.itemCardBox {
height: calc((100% - 15px) / 4);
background-image: url(/tht_project/static/html/big_screen/image/group_background.png);
background-size: 100% 100%;
padding: 5px 8px 0px;
display: flex;
flex-direction: column;
.cardHeader {
height: 30px;
span {
padding: 0 8px;
font-size: 14px;
font-weight: bold;
background: linear-gradient(180deg, #FFF 10%, #00bbf3 100%);
/* 将文字颜色设置成透明色 */
color: transparent;
-webkit-text-fill-color: transparent;
/* 将背景裁剪成文字前景色 */
background-clip: text;
-webkit-background-clip: text;
}
}
.cardContentBox {
flex: auto;
height: 1px;
}
.tableBox {
display: flex;
flex-direction: column;
padding-bottom: 15px;
.tableClass {
flex: auto;
width: 100%;
background-color: transparent;
.el-table__body tr:hover>td.el-table__cell {
background-color: rgba(10, 115, 255, 0.3);
}
}
.tableClass::before {
display: none;
}
}
}
}
.one {
width: calc((100% - 15px) * 0.2);
.itemCardBox {
background-image: none;
.cardHeader {
background-image: url(/tht_project/static/html/big_screen/image/card_header_left_background.png);
background-size: 100% 80%;
background-repeat: no-repeat;
background-position: center;
}
}
}
.two {
width: calc((100% - 15px) * 0.3);
}
.three {
width: calc((100% - 15px) * 0.3);
}
.four {
width: calc((100% - 15px) * 0.2);
.itemCardBox {
height: calc((100% - 15px) * 0.25);
.cardHeader {
background-image: url(/tht_project/static/html/big_screen/image/card_header_right_background.png);
background-size: 100% 80%;
background-repeat: no-repeat;
background-position: center;
text-align: right;
}
}
.itemCardBox:first-child {
flex: 1;
}
}
}
}
/* 多彩跑马灯效果 */
@keyframes rainbow-glow {
0% {
box-shadow: -10px 0 10px -10px #ff0000,
10px 0 10px -10px #ff0000,
inset -10px 0 10px -10px #ff0000,
inset 10px 0 10px -10px #ff0000;
border-left-color: #ff0000;
border-right-color: #ff0000;
}
16% {
box-shadow: -10px 0 10px -10px #ff9900,
10px 0 10px -10px #ff9900,
inset -10px 0 10px -10px #ff9900,
inset 10px 0 10px -10px #ff9900;
border-left-color: #ff9900;
border-right-color: #ff9900;
}
33% {
box-shadow: -10px 0 10px -10px #ffff00,
10px 0 10px -10px #ffff00,
inset -10px 0 10px -10px #ffff00,
inset 10px 0 10px -10px #ffff00;
border-left-color: #ffff00;
border-right-color: #ffff00;
}
50% {
box-shadow: -10px 0 10px -10px #33cc33,
10px 0 10px -10px #33cc33,
inset -10px 0 10px -10px #33cc33,
inset 10px 0 10px -10px #33cc33;
border-left-color: #33cc33;
border-right-color: #33cc33;
}
66% {
box-shadow: -10px 0 10px -10px #0099ff,
10px 0 10px -10px #0099ff,
inset -10px 0 10px -10px #0099ff,
inset 10px 0 10px -10px #0099ff;
border-left-color: #0099ff;
border-right-color: #0099ff;
}
83% {
box-shadow: -10px 0 10px -10px #6633ff,
10px 0 10px -10px #6633ff,
inset -10px 0 10px -10px #6633ff,
inset 10px 0 10px -10px #6633ff;
border-left-color: #6633ff;
border-right-color: #6633ff;
}
100% {
box-shadow: -10px 0 10px -10px #ff0066,
10px 0 10px -10px #ff0066,
inset -10px 0 10px -10px #ff0066,
inset 10px 0 10px -10px #ff0066;
border-left-color: #ff0066;
border-right-color: #ff0066;
}
}
/* 小点移动动画 */
@keyframes dot-move {
0% {
top: 30px;
opacity: 1;
}
10% {
opacity: 1;
}
40% {
opacity: 1;
}
50% {
opacity: 0;
top: calc(100% - 30px);
}
60% {
opacity: 1;
}
90% {
opacity: 1;
}
100% {
opacity: 0;
top: 30px;
}
}
</style>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8"/>
<title>预料工序</title>
<meta content="width=device-width,initial-scale=1.0, maximum-scale=1.0,user-scalable=0" name="viewport"/>
<link rel="stylesheet" href="/tht_project/static/html/big_screen/element-ui/index.css"/>
<script src="/tht_project/static/html/big_screen/js/echarts.min.js"></script>
<script src="/tht_project/static/html/big_screen/js/moment.min.js"></script>
<script src="/tht_project/static/html/big_screen/js/vue.js"></script>
<script src="/tht_project/static/html/big_screen/js/axios.min.js"></script>
<script src="/tht_project/static/html/big_screen/element-ui/index.js"></script>
</head>
<body id="bodyId" style="display: none">
<div id="app" v-loading.body.fullscreen.lock="loading" ref="fullScreenElement">
<div class="pageHeaderBox">
<div class="weatherInfo">
<span class="location">预料工序</span>
</div>
<img class="logo" src="/tht_project/static/html/big_screen/image/logo.png">
<div class="title">天合堂数据指挥中心可视化大屏</div>
<div class="dateTimeBox">
<i style="font-size: 30px;margin-right: 10px;" @click="toggleFullscreen"
:class="isFullscreen ? 'el-icon-close' : 'el-icon-full-screen'"></i>
<div class="time">[[currentTime]]</div>
<div class="dateBox">
<span>[[ currentDate ]]</span>
<span>[[ currentWeek ]]</span>
</div>
</div>
</div>
<div class="pageContentBox">
<div class="pageContentBackgroundBox">
<img class="badgeIcon" src="/tht_project/static/html/big_screen/image/777.png" alt="" v-for="item in 4"
:key="item">
</div>
<div class="pageContentArea">
<div class="contentColumnBox one">
<div class="itemCardBox">
<div class="cardHeader">
<span>今日产量</span>
</div>
<div class="cardContentBox">
<div id="proportionChart" style="width: 100%; height: 100%;"></div>
</div>
</div>
<div class="itemCardBox">
<div class="cardHeader">
<span>今日投料情况</span>
</div>
<div class="cardContentBox">
<div id="mainMaterialChart" style="width: 100%; height: 100%;"></div>
</div>
</div>
<div class="itemCardBox">
<div class="cardHeader">
<span>设备实时运转情况</span>
</div>
<div class="cardContentBox">
<div id="seasonMaterialChart" style="width: 100%; height: 100%;"></div>
</div>
</div>
<div class="itemCardBox">
<div class="cardHeader">
<span>设备资源利用率</span>
</div>
<div class="cardContentBox">
<div id="rawMaterialChart" style="width: 100%; height: 100%;"></div>
</div>
</div>
</div>
<div class="contentColumnBox two">
<div class="itemCardBox">
<div class="cardHeader">
<span>片冰机</span>
</div>
<div class="cardContentBox">
<div id="sliceIceMachineChart" style="width: 100%; height: 100%;"></div>
</div>
</div>
<div class="itemCardBox">
<div class="cardHeader">
<span>解冻机组</span>
</div>
<div class="cardContentBox">
<div id="thawingUnitChart" style="width: 100%; height: 100%;"></div>
</div>
</div>
<div class="itemCardBox">
<div class="cardHeader">
<span>切丁机组</span>
</div>
<div class="cardContentBox">
<div style="width: 100%; height: 100%; display: flex;">
<div id="dicingUnitLineChart" style="width: 65%; height: 100%;"></div>
<div id="dicingUnitGaugeChart" style="width: 35%; height: 100%;"></div>
</div>
</div>
</div>
<div class="itemCardBox">
<div class="cardHeader">
<span>连续切块机</span>
</div>
<div class="cardContentBox">
<div style="width: 100%; height: 100%; display: flex;">
<div id="continuouSlabberLeftChart" style="width: 50%; height: 100%;"></div>
<div id="continuouSlabberRightChart" style="width: 50%; height: 100%;"></div>
</div>
</div>
</div>
<div class="itemCardBox" style="position: relative;">
<div class="cardHeader" style="position: absolute;top: 5px; width: 100%;">
<span>绞肉机</span>
</div>
<div class="cardContentBox" style="z-index: 1;">
<div id="meatGrinderChart" style="width: 100%; height: 100%;"></div>
</div>
</div>
</div>
<div class="contentColumnBox three">
<div class="itemCardBox">
<div class="cardHeader">
<span>斩拌机</span>
</div>
<div class="cardContentBox">
<div id="ZBY550ChopperMixerChart" style="width: 100%; height: 100%;"></div>
</div>
</div>
<div class="itemCardBox">
<div class="cardHeader">
<span>真空搅拌机</span>
</div>
<div class="cardContentBox">
<div style="width: 100%; height: 100%; display: flex;flex-wrap: wrap;">
<div id="vacuumMixerRotateSpeedChart" style="width: 50%; height: 50%;"></div>
<div id="vacuumMixerRunTimeChart" style="width: 50%; height: 50%;"></div>
<div id="vacuumMixerRemainTimeChart" style="width: 50%; height: 50%;"></div>
<div id="vacuumMixerCWVoltageChart" style="width: 50%; height: 50%;"></div>
</div>
</div>
</div>
<div class="itemCardBox">
<div class="cardHeader">
<span>真空滚揉机</span>
</div>
<div class="cardContentBox">
<div style="width: 100%; height: 100%; display: flex;flex-wrap: wrap;">
<div id="vacuumTumblingRotateSpeedChart" style="width: 50%; height: 50%;"></div>
<div id="vacuumTumblingRunTimeChart" style="width: 50%; height: 50%;"></div>
<div id="vacuumTumblingRemainTimeChart" style="width: 50%; height: 50%;"></div>
</div>
</div>
</div>
</div>
<div class="contentColumnBox four">
<div class="itemCardBox">
<div class="cardHeader">
<span>报警中心</span>
</div>
<div class="cardContentBox tableBox">
<el-table class="tableClass" :data="alarmList" height="1" tooltip-effect="light"
:row-style="{height: '30px', backgroundColor: 'transparent'}"
:cell-style="{padding: '5px 0', borderBottom:'none'}"
:header-row-style="{ backgroundColor: 'transparent'}" :header-cell-style="{
backgroundColor: 'transparent',
color: '#01c4f9',
fontWeight: 'bold',
fontSzie:'14px',
height:'30px',
lineHeight:'30px',
padding: '5px 0',
borderBottom:'none'
}">
<el-table-column prop="device" label="报警类型">
<template slot-scope="scope">
<span style="color: #fff; font-size: 12px;">[[ scope.row.device ]]</span>
</template>
</el-table-column>
<el-table-column prop="time" label="报警时间" width="80">
<template slot-scope="scope">
<span style="color: #fff; font-size: 12px;">[[ scope.row.time ]]</span>
</template>
</el-table-column>
<el-table-column prop="detail" label="报警描述" show-overflow-tooltip>
<template slot-scope="scope">
<span style="color: #fff; font-size: 12px;">[[ scope.row.detail ]]</span>
</template>
</el-table-column>
</el-table>
</div>
</div>
<div class="itemCardBox">
<div class="cardHeader">
<span>今日计划执行情况</span>
</div>
<div class="cardContentBox">
<div id="meatGrainAnalysisChart" style="width: 100%; height: 100%;"></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, // 窗口高度
isFullscreen: false, // 全屏状态
baseURL: "https://tht-test.newerp.rokedata.com", // 基地址,
dwsBaseUrl: "https://dws-platform.xbg.rokeris.com/dev-api", // dws系统基地址
loading: false, // 全局加载效果
currentTime: "", // 当前时间
currentDate: "", // 当前日期
currentWeek: "", // 当前周
timeInterval: null, // 时间定时器
dateInterval: null, // 日期定时器
charts: {}, // 图表
alarmList: [
{ device: "设备故障", time: "2025-4-23", detail: "切丁机电源了故障" },
{ device: "原料临期", time: "2025-4-22", detail: "酱油将于2025-5-1过期" },
{ device: "原料临期", time: "2025-4-21", detail: "酱油将于2025-5-1过期" },
{ device: "质检不合格", time: "2025-4-21", detail: "有猪肉1斤质检不合格" },
{ device: "质检不合格", time: "2025-4-21", detail: "有猪肉1斤质检不合格" },
{ device: "缺料", time: "2025-4-21", detail: "胡椒粉即将缺料" },
{ device: "缺料", time: "2025-4-21", detail: "牛肉即将缺料" },
{ device: "配比异常", time: "2025-4-23", detail: "淀粉配比不达标" },
{ device: "配比异常", time: "2025-4-21", detail: "淀粉配比不达标" },
{ device: "设备故障", time: "2025-4-21", detail: "切丁机电源了故障" },
{ device: "设备故障", time: "2025-4-21", detail: "切丁机电源了故障" },
{ device: "设备故障", time: "2025-4-21", detail: "切丁机电源了故障" },
{ device: "设备故障", time: "2025-4-21", detail: "切丁机电源了故障" },
{ device: "设备故障", time: "2025-4-21", detail: "切丁机电源了故障" },
{ device: "设备故障", time: "2025-4-21", detail: "切丁机电源了故障" },
],
}
},
created() {
// 添加resize事件监听
window.addEventListener("resize", this.handleResize)
// 更新日期、时间、周
this.updateDateTime()
// 每秒更新时间
this.timeInterval = setInterval(this.updateTime, 1000)
// 每分钟检查一次日期是否需要更新(每天只需更新一次)
this.dateInterval = setInterval(() => {
const now = new Date()
if (now.getHours() === 0 && now.getMinutes() === 0) {
this.updateDate()
this.updateWeek()
}
}, 60000)
},
mounted() {
this.$nextTick(() => {
document.getElementById("bodyId").style.display = "block"
this.initAllCharts()
})
},
methods: {
// 全屏icon点击事件
toggleFullscreen: function () {
if (!this.isFullscreen) {
this.enterFullScreen()
} else {
this.exitFullScreen()
}
},
// 全屏方法
enterFullScreen: function () {
// 获取需要全屏的元素
var elem = this.$refs.fullScreenElement
if (elem && elem.requestFullscreen) {
elem.requestFullscreen()
} else if (elem && elem.mozRequestFullScreen) {
// Firefox
elem.mozRequestFullScreen()
} else if (elem && elem.webkitRequestFullscreen) {
// Chrome, Safari & Opera
elem.webkitRequestFullscreen()
} else if (elem && elem.msRequestFullscreen) {
// IE/Edge
elem.msRequestFullscreen()
}
this.isFullscreen = true
},
// 退出全屏
exitFullScreen: function () {
if (document.exitFullscreen) {
document.exitFullscreen()
} else if (document.mozCancelFullScreen) {
// Firefox
document.mozCancelFullScreen()
} else if (document.webkitExitFullscreen) {
// Chrome, Safari and Opera
document.webkitExitFullscreen()
} else if (document.msExitFullscreen) {
// IE/Edge
document.msExitFullscreen()
}
this.isFullscreen = false
},
// 处理窗口大小变化修改图表大小
handleResize() {
for (let key in this.charts) {
this.charts[key] && this.charts[key].resize()
}
},
// 更新日期、时间、周
updateDateTime() {
this.updateTime()
this.updateDate()
this.updateWeek()
},
// 更新时间
updateTime() {
this.currentTime = moment().format('HH:mm:ss')
},
// 更新日期
updateDate() {
this.currentDate = moment().format('YYYY-MM-DD')
},
// 更新周
updateWeek() {
this.currentWeek = '周' + ['日', '一', '二', '三', '四', '五', '六'][moment().day()]
},
// 接口请求方法封装
requestApi(
url,
config = {},
errorMessage = "操作失败,请稍后重试",
contentType = "application/json"
) {
return new Promise((resolve, reject) => {
if (!url) reject(null)
axios({
method: "POST",
url: this.baseURL + url,
data: config,
headers: {"Content-Type": contentType},
}).then((result) => {
if (
result?.data?.result?.code == 0 ||
result?.data?.result?.state == "success" ||
result?.data?.code == 0
) {
resolve(result.data)
} else if (result?.data?.result?.code == 1) {
reject(result.data.result.message)
} else if (result?.data?.result?.state == "error") {
reject(result.data.result.megs)
} else if (result?.data?.code == 0) {
reject(result.data.message)
} else if (result?.data?.error) {
reject(result.data.error.message)
}
}).catch((error) => {
reject(errorMessage)
})
})
},
requestSelfApi(
url,
config = {},
errorMessage = "操作失败,请稍后重试",
contentType = "application/json",
baseurl = "https://tht.dws.rokecloud.com"
) {
return new Promise((resolve, reject) => {
if (!url) reject(null)
axios({
method: "POST",
url: baseurl + url,
data: config,
headers: {"Content-Type": contentType},
}).then((result) => {
if (
result?.data?.result?.success ||
result?.data?.result?.code == 0 ||
result?.data?.result?.state == "success" ||
result?.data?.code == 0
) {
resolve(result.data)
} else if (result?.data?.result?.code == 1) {
reject(result.data.result.message)
} else if (result?.data?.result?.state == "error") {
reject(result.data.result.megs)
} else if (result?.data?.code == 1) {
reject(result.data.message)
} else if (!result?.data?.result?.success) {
reject(result.data.result.msg)
} else if (result?.data?.error) {
reject(result.data.error.message)
}
}).catch((error) => {
reject(errorMessage)
})
})
},
// 接口请求Dws系统方法封装
requestDwsApi(
url,
config = {},
errorMessage = "操作失败,请稍后重试",
contentType = "application/json"
) {
return new Promise((resolve, reject) => {
if (!url) reject(null)
axios({
method: "POST",
url: this.dwsBaseUrl + url,
data: config,
headers: {"Content-Type": contentType},
}).then((result) => {
if (result?.data?.success) {
resolve(result.data)
} else if (!result.data.success) {
reject(result.data.msg)
} else {
reject(errorMessage)
}
}).catch((error) => {
reject(errorMessage)
})
})
},
// 初始化所有图表
async initAllCharts() {
this.loading = true
let proportionData = [] // 今日产量数据
let mainMaterialData = [] // 今日投料情况
let seasonMaterialData = {} // 设备实时运转情况
let rawMaterialData = [] // 设备资源利用率
let thawingUnitData = [] // 解冻机数据
let continuouSlabberData = [] // 连续切块机
let meatGrainAnalysisData = [] // 今日计划执行情况
// 查询今日产量数据
await this.requestApi('/roke/big_screen_kanban/today_production', {
"process_code": "001",
"today": "2025-03-28",
}).then(result => {
proportionData = result.result.data
}).catch(error => {
console.error(error)
})
// 查询今日投料情况
await this.requestApi('/roke/big_screen_kanban/today_material_input', {
"process_code": "001",
"today": "2025-03-28",
}).then(result => {
mainMaterialData = result.result.data
}).catch(error => {
console.error(error)
})
// 查询设备实时运转情况
await this.requestSelfApi('/big_screen_count', {
"plant_name": "预料车间",
"category_name": '',
"today": ''
}).then(result => {
seasonMaterialData = result.result.data
}).catch(error => {
console.error(error)
})
// 查询设备资源利用率
await this.requestSelfApi('/big_screen_today', {
"plant_name": "预料车间",
"category_name": '',
"today": ''
}).then(result => {
rawMaterialData = result.result.data
}).catch(error => {
console.error(error)
})
// 查询解冻机
await this.requestSelfApi('/get_jiedongji_temperature', {
"device_code_list": ['868892078264059'],
"category_name": '',
"today": '',
'plant_name': '',
}).then(result => {
thawingUnitData = result.result.data
}).catch(error => {
console.error(error)
})
// 查询连续切块机
await this.requestSelfApi('/get_qiekuaiji_data', {
"device_code_list": ['868892078346237'],
"category_name": '',
"today": '',
'plant_name': '',
}).then(result => {
continuouSlabberData = result.result.data
}).catch(error => {
console.error(error)
})
// 查询今日计划执行情况
await this.requestApi('/roke/big_screen_kanban/today_production_summary', {
"order_department_code": "001",
}).then(result => {
meatGrainAnalysisData = result.result.data
}).catch(error => {
console.error(error)
})
setTimeout(() => {
this.initProportionChart(proportionData)
this.initMainMaterialChart(mainMaterialData)
this.initSeasonMaterialChart(seasonMaterialData)
this.initRawMaterialChart(rawMaterialData)
this.initSliceIceMachineChart()
this.initThawingUnitChart(thawingUnitData)
this.initDicingUnitChart()
this.initContinuouSlabberChart(continuouSlabberData)
this.initMeatGrinderChart()
this.initZBY550ChopperMixerChart()
this.initVacuumMixerChart()
this.initVacuumTumblingChart()
this.initMeatGrainAnalysisChart(meatGrainAnalysisData)
}, 1)
this.loading = false
},
// 今日产量
initProportionChart(data) {
const chart = echarts.init(document.getElementById('proportionChart'))
this.charts.proportion = chart
// x轴数据
const xAxisData = []
// 数量
const seriesData = []
data.forEach(item => {
xAxisData.push(item.product_name)
seriesData.push({
value: item.finish_qty,
unit: item.uom_name
})
})
// 设置最大值处理显示数值时和顶部重叠问题; 三方表达式:防止无数据时不显示y轴
const max = this.calculateMaxValue(seriesData, 1.5)
chart.setOption({
grid: {
left: '5%', right: '5%', top: '8%', bottom: '5%',
containLabel: true,
show: true,
borderWidth: 0,
backgroundColor: 'rgba(0, 0, 0, 0)',
shadowColor: 'rgba(0, 0, 0, 0.3)',
shadowBlur: 2
},
tooltip: {
trigger: 'axis',
axisPointer: {type: 'shadow'},
formatter: (params) => {
console.log(params)
let tooltipHtml = `<div style="font-weight:bold;margin-bottom:5px">${params[0].name}</div>`
params.forEach(param => {
const dataIndex = param.dataIndex
const unit = seriesData[dataIndex].unit
// 使用系列颜色作为小圆点颜色
tooltipHtml += `
<div style="display:flex;align-items:center;margin:5px 0">
<span style="display:inline-block;width:10px;height:10px;border-radius:50%;background:${param.color};margin-right:10px"></span>
<strong>${param.value}</strong>
<span>${unit}</span>
</div>
`
})
return tooltipHtml
}
},
dataZoom: [{
id: 'proportionChart',
type: 'inside', // 表明在图表内部进行拖动
start: 0,
end: (2 / xAxisData.length) * 100, // 默认显示3条
filterMode: 'none'
}],
xAxis: {
type: 'category',
data: xAxisData,
axisLine: {lineStyle: {color: '#0a73ff'}},
axisLabel: {color: '#fff', fontSize: 10}
},
yAxis: {
type: 'value',
splitNumber: 4,
min: 0,
max: max,
axisLine: {show: true, lineStyle: {color: '#0a73ff'}},
splitLine: {lineStyle: {color: '#0a73ff', width: 0.5, opacity: 0.3}},
axisLabel: {color: '#fff', fontSize: 10}
},
series: [{
type: 'bar',
barWidth: '40%',
barGap: '0%',
data: seriesData,
itemStyle: {color: '#5087ec'},
label: {
show: true,
position: 'top',
color: '#FFF',
fontSize: 10,
formatter: (params) => params.value + params.data.unit
}
}]
})
},
// 今日投料情况
initMainMaterialChart(data) {
const chart = echarts.init(document.getElementById('mainMaterialChart'))
this.charts.mainMaterial = chart
// x轴数据
const xAxisData = []
// 数据
const seriesData = []
// value数据
const seriesValues = []
data.forEach(item => {
xAxisData.push(item.product_name)
seriesData.push({
value: item.qty,
unit: item.unit
})
seriesValues.push(item.qty)
})
// 设置最大值处理显示数值时和顶部重叠问题; 三方表达式:防止无数据时不显示y轴
const max = [...seriesValues].length ? Math.max(...[...seriesValues]) * 1.5 : 100
chart.setOption({
grid: {
left: '5%', right: '10%', top: '8%', bottom: '5%',
containLabel: true,
show: true,
borderWidth: 0,
backgroundColor: 'rgba(0, 0, 0, 0)',
shadowColor: 'rgba(0, 0, 0, 0.3)',
shadowBlur: 2
},
tooltip: {
trigger: 'axis',
formatter: (params) => {
return params[0].marker + params[0].name + ': ' + params[0].data.value + params[0].data.unit
}
},
dataZoom: [{
id: 'mainMaterialChart',
type: 'inside', // 表明在图表内部进行拖动
start: 0,
end: (2 / xAxisData.length) * 100, // 默认显示3条
filterMode: 'none'
}],
xAxis: {
type: 'category',
boundaryGap: false,
data: xAxisData,
axisLine: {show: true, lineStyle: {color: '#0a73ff'}},
axisTick: {show: false},
axisLabel: {color: '#fff', fontSize: 10, interval: 0}
},
yAxis: {
type: 'value',
splitNumber: 4,
min: 0,
max: max,
axisLine: {show: true, lineStyle: {color: '#0a73ff'}},
splitLine: {lineStyle: {color: '#0a73ff', width: 0.5, opacity: 0.3, type: 'dashed'}},
axisLabel: {color: '#fff', fontSize: 10, formatter: '{value}'}
},
series: [{
type: 'line',
data: seriesData,
label: {
show: true,
position: 'top',
color: '#FFF',
fontSize: 10,
formatter: (params) => params.data.value + params.data.unit
}
}]
})
},
// 设备实时运转情况
initSeasonMaterialChart(data) {
const chart = echarts.init(document.getElementById('seasonMaterialChart'))
this.charts.seasonMaterial = chart
chart.setOption({
tooltip: {
trigger: 'item',
valueFormatter: (value) => value + '台',
},
legend: {
orient: 'vertical',
left: 'top',
itemWidth: 20,
itemHeight: 10,
itemGap: 5,
textStyle: {
color: "#fff",
fontSize: 10
}
},
series: [{
type: 'pie',
radius: '60%',
center: ['60%', '60%'],
itemStyle: {
borderRadius: 0,
borderColor: '#061058',
borderWidth: 2
},
label: {
show: true,
formatter: '{c}台',
color: '#fff',
fontSize: 12
},
data: [{
value: data?.green || 0,
name: '运行',
itemStyle: {color: '#91cc75'},
}, {
value: data?.yellow || 0,
name: '等待',
itemStyle: {color: '#ffd700'},
}, {
value: data?.red || 0,
name: '故障',
itemStyle: {color: '#ee6666'},
}, {
value: data?.gray || 0,
name: '关机',
itemStyle: {color: '#787878'},
}]
}]
})
},
// 设备资源利用率
initRawMaterialChart(data) {
const chart = echarts.init(document.getElementById('rawMaterialChart'))
this.charts.rawMaterial = chart
// x轴数据
const xAxisData = []
// 数量
const seriesData = []
data.forEach(item => {
if (item.category) {
xAxisData.push(item.category)
seriesData.push(item.avg_utilization)
}
})
// 设置最大值处理显示数值时和顶部重叠问题; 三方表达式:防止无数据时不显示y轴
const max = [...seriesData].length ? Math.max(...[...seriesData]) * 1.5 : 100
chart.setOption({
grid: {
left: '5%', right: '5%', top: '8%', bottom: '5%',
containLabel: true,
show: true,
borderWidth: 0,
backgroundColor: 'rgba(0, 0, 0, 0)',
shadowColor: 'rgba(0, 0, 0, 0.3)',
shadowBlur: 2
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
valueFormatter: (value) => value + '%',
},
dataZoom: [{
id: 'rawMaterialChart',
type: 'inside', // 表明在图表内部进行拖动
start: 0,
end: (2 / xAxisData.length) * 100, // 默认显示3条
filterMode: 'none'
}],
xAxis: {
type: 'category',
data: xAxisData,
axisLine: {lineStyle: {color: '#0a73ff'}},
axisLabel: {color: '#fff', fontSize: 10}
},
yAxis: {
type: 'value',
splitNumber: 4,
min: 0,
max: max,
axisLine: {show: true, lineStyle: {color: '#0a73ff'}},
splitLine: {lineStyle: {color: '#0a73ff', width: 0.5, opacity: 0.3}},
axisLabel: {color: '#fff', fontSize: 10, formatter: '{value}%'}
},
series: [{
type: 'bar',
barWidth: '40%',
barGap: '0%',
data: seriesData,
itemStyle: {color: '#5087ec'},
label: {
show: true,
position: 'top',
color: '#FFF',
fontSize: 10,
formatter: (params) => params.value + '%'
}
}]
})
},
// 片冰机
initSliceIceMachineChart() {
const chart = echarts.init(document.getElementById('sliceIceMachineChart'))
this.charts.sliceIceMachine = chart
// x轴数据
const xAxisData = ['8点', '9点', '10点', '11点']
// 数量
const seriesData = [45, 60, 80, 10]
// 设置最大值处理显示数值时和顶部重叠问题; 三方表达式:防止无数据时不显示y轴
const max = [...seriesData].length ? Math.max(...[...seriesData]) * 1.5 : 100
chart.setOption({
grid: {
left: '5%', right: '5%', top: '8%', bottom: '5%',
containLabel: true,
show: true,
borderWidth: 0,
backgroundColor: 'rgba(0, 0, 0, 0)',
shadowColor: 'rgba(0, 0, 0, 0.3)',
shadowBlur: 2
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
valueFormatter: (value) => value + '℃',
},
xAxis: {
type: 'category',
data: xAxisData,
axisLine: {lineStyle: {color: '#0a73ff'}},
axisLabel: {color: '#fff', fontSize: 10}
},
yAxis: {
type: 'value',
splitNumber: 4,
min: 0,
max: max,
axisLine: {show: true, lineStyle: {color: '#0a73ff'}},
splitLine: {lineStyle: {color: '#0a73ff', width: 0.5, opacity: 0.3}},
axisLabel: {color: '#fff', fontSize: 10, formatter: '{value}℃'}
},
series: [{
type: 'bar',
barWidth: '40%',
barGap: '0%',
data: seriesData,
itemStyle: {color: '#5087ec'},
label: {
show: true,
position: 'top',
color: '#FFF',
fontSize: 10,
formatter: (params) => params.value + '℃'
}
}]
})
},
// 解冻机组
initThawingUnitChart(data) {
const chart = echarts.init(document.getElementById('thawingUnitChart'))
this.charts.thawingUnit = chart
// 数值
const valueData = {
current_cune_temp: data[0].current_temperature || 0, // 当前库内温度
center_temp: data[0].center_temperature || 0, // 中心温度
surface_temp: data[0].surface_temperature || 0, // 表面温度
cune_humidity: data[0].humidity || 0, // 库内湿度
}
// series配置
const seriesData = []
for (let i = 0; i < 3; i++) {
let data = {
name: '',
type: 'gauge',
center: [],
radius: '60%',
startAngle: 200,
endAngle: -20,
splitNumber: 5,
itemStyle: {color: '#3a8fff'},
progress: {show: true, width: 5},
pointer: {show: false},
axisLine: {lineStyle: {width: 5}},
axisTick: {
distance: -15,
length: 5,
splitNumber: 5,
lineStyle: {width: 1, color: '#999'}
},
splitLine: {
distance: -15,
length: 8,
lineStyle: {width: 1, color: '#999'}
},
axisLabel: {distance: -15, color: '#999', fontSize: 10,},
detail: {
valueAnimation: true,
offsetCenter: [0, 0],
fontSize: 10,
formatter: '{value}℃',
color: 'inherit'
},
data: [0]
}
if (i == 0) {
data.name = '当前库内温度'
data.center = ['20%', '60%']
data.data = [valueData.current_cune_temp || 0]
} else if (i == 1) {
data.name = '中心温度'
data.center = ['50%', '60%']
data.data = [valueData.center_temp || 0]
} else if (i == 2) {
data.name = '库内湿度'
data.center = ['80%', '60%']
data.data = [valueData.cune_humidity || 0]
data.detail.formatter = '{value}%'
}
seriesData.push(data)
}
chart.setOption({
grid: {left: '5%', right: '5%', top: '8%', bottom: '5%'},
graphic: [{
type: 'text',
left: '12%',
top: '80%',
style: {text: '当前库内温度', fill: '#fff', fontSize: 10}
}, {
type: 'text',
left: '45.5%',
top: '80%',
style: {text: '中心温度', fill: '#fff', fontSize: 10}
}, {
type: 'text',
left: '75.5%',
top: '80%',
style: {text: '库内湿度', fill: '#fff', fontSize: 10}
}],
series: seriesData
})
},
// 切丁机组
initDicingUnitChart() {
const lineChart = echarts.init(document.getElementById('dicingUnitLineChart'))
const gaugeChart = echarts.init(document.getElementById('dicingUnitGaugeChart'))
this.charts.dicingUnitLine = lineChart
this.charts.dicingUnitGauge = gaugeChart
// 折线x轴数据
const xAxisData = ['8点', '9点', '10点', '11点', '12点', '13点']
// 折线数量
const seriesData = [100, 140, 230, 50, 150, 180]
// 折线单位
const unit = 'Hz'
// 设置最大值处理显示数值时和顶部重叠问题; 三方表达式:防止无数据时不显示y轴
const max = [...seriesData].length ? Math.max(...[...seriesData]) * 1.5 : 100
lineChart.setOption({
grid: {
left: '5%', right: '5%', top: '8%', bottom: '5%',
containLabel: true,
show: true,
borderWidth: 0,
backgroundColor: 'rgba(0, 0, 0, 0)',
shadowColor: 'rgba(0, 0, 0, 0.3)',
shadowBlur: 2
},
tooltip: {
trigger: 'axis',
valueFormatter: (value) => value + unit
},
xAxis: {
type: 'category',
boundaryGap: false,
data: xAxisData,
axisLine: {show: true, lineStyle: {color: '#0a73ff'}},
axisTick: {show: false},
axisLabel: {color: '#fff', fontSize: 10, interval: 0}
},
yAxis: {
type: 'value',
splitNumber: 4,
min: 0,
max: max,
axisLine: {show: true, lineStyle: {color: '#0a73ff'}},
splitLine: {lineStyle: {color: '#0a73ff', width: 0.5, opacity: 0.3, type: 'dashed'}},
axisLabel: {color: '#fff', fontSize: 10, formatter: '{value}' + unit}
},
series: [{
type: 'line',
data: seriesData,
smooth: true,
lineStyle: {type: 'dashed'},
label: {
show: true,
position: 'top',
color: '#fff',
fontSize: 10,
formatter: (params) => params.value + unit
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{offset: 0, color: 'rgba(58, 143, 255, 0.8)'},
{offset: 1, color: 'rgba(58, 143, 255, 0.1)'}
])
},
}]
})
gaugeChart.setOption({
series: [{
type: 'gauge',
radius: '100%',
center: ['50%', '50%'],
startAngle: 200,
endAngle: -20,
splitNumber: 5,
itemStyle: {color: '#3a8fff'},
progress: {show: false, width: 5},
pointer: {itemStyle: {color: '#fe827a',}, length: "60%", width: 3},
axisLine: {
roundCap: true,
lineStyle: {
width: 5,
color: [[1, {
type: 'linear', x: 0, y: 0, x2: 1, y2: 0,
colorStops: [{offset: 0, color: '#91cc75'}, {offset: 1, color: '#ff4b4b'}]
}]]
}
},
axisTick: {show: false},
splitLine: {show: false},
axisLabel: {show: false},
detail: {
valueAnimation: true,
width: '40%',
formatter: '{value} s',
color: '#fff',
fontSize: 10,
fontWeight: 'bold',
offsetCenter: [0, '40%']
},
data: [{value: 50, name: "加速时间"}],
title: {color: "#FFF", fontSize: 10, valueAnimation: false, offsetCenter: ['0', '70%']}
}]
})
},
// 连续切块机
initContinuouSlabberChart(data) {
const leftChart = echarts.init(document.getElementById('continuouSlabberLeftChart'))
const rightChart = echarts.init(document.getElementById('continuouSlabberRightChart'))
this.charts.continuouSlabberLeft = leftChart
this.charts.continuouSlabberRight = rightChart
// x轴数据
const xAxisData = []
// 运行频率
const runFrequencyData = []
// 切割速度
const cuttingSpeedData = []
data.forEach(item => {
xAxisData.push(item.time)
runFrequencyData.push(item.yunxingpinlv)
cuttingSpeedData.push(item.susongsudu)
})
// 设置最大值处理显示数值时和顶部重叠问题; 三方表达式:防止无数据时不显示y轴
const runFrequencyMax = runFrequencyData.length ? Math.max(...runFrequencyData) * 1.5 : 100
const cuttingSpeedMax = runFrequencyData.length ? Math.max(...runFrequencyData) * 1.5 : 100
leftChart.setOption({
grid: {
left: '5%', right: '5%', top: '8%', bottom: '5%',
containLabel: true,
show: true,
borderWidth: 0,
backgroundColor: 'rgba(0, 0, 0, 0)',
shadowColor: 'rgba(0, 0, 0, 0.3)',
shadowBlur: 2
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
valueFormatter: (value) => value + 'Hz',
},
xAxis: {
type: 'category',
data: xAxisData,
axisLine: {lineStyle: {color: '#0a73ff'}},
axisLabel: {color: '#fff', fontSize: 10}
},
yAxis: {
type: 'value',
splitNumber: 4,
min: 0,
max: runFrequencyMax,
axisLine: {show: true, lineStyle: {color: '#0a73ff'}},
splitLine: {lineStyle: {color: '#0a73ff', width: 0.5, opacity: 0.3}},
axisLabel: {color: '#fff', fontSize: 10, formatter: '{value}Hz'}
},
series: [{
type: 'bar',
barWidth: '40%',
barGap: '0%',
data: runFrequencyData,
itemStyle: {color: '#5087ec'},
label: {
show: true,
position: 'top',
color: '#FFF',
fontSize: 10,
formatter: (params) => params.value + 'Hz'
}
}]
})
rightChart.setOption({
grid: {
left: '5%', right: '5%', top: '8%', bottom: '5%',
containLabel: true,
show: true,
borderWidth: 0,
backgroundColor: 'rgba(0, 0, 0, 0)',
shadowColor: 'rgba(0, 0, 0, 0.3)',
shadowBlur: 2
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
valueFormatter: (value) => value + 'ppm',
},
xAxis: {
type: 'category',
data: xAxisData,
axisLine: {lineStyle: {color: '#0a73ff'}},
axisLabel: {color: '#fff', fontSize: 10}
},
yAxis: {
type: 'value',
position: 'right',
splitNumber: 4,
min: 0,
max: cuttingSpeedMax,
axisLine: {show: true, lineStyle: {color: '#0a73ff'}},
splitLine: {lineStyle: {color: '#0a73ff', width: 0.5, opacity: 0.3}},
axisLabel: {color: '#fff', fontSize: 10, formatter: '{value}ppm'}
},
series: [{
type: 'bar',
barWidth: '40%',
barGap: '0%',
data: cuttingSpeedData,
itemStyle: {color: '#5087ec'},
label: {
show: true,
position: 'top',
color: '#FFF',
fontSize: 10,
formatter: (params) => params.value + 'ppm'
}
}]
})
},
// 绞肉机
initMeatGrinderChart() {
const chart = echarts.init(document.getElementById('meatGrinderChart'))
this.charts.meatGrinder = chart
// 折线x轴数据
const xAxisData = ['8点', '9点', '10点', '11点']
// 折线数量
const deviceA = [230, 100, 230, 50]
const deviceB = [50, 230, 140, 170]
// 折线单位
const unit = 'ppm'
// 设置最大值处理显示数值时和顶部重叠问题; 三方表达式:防止无数据时不显示y轴
const max = [...deviceA, ...deviceB].length ? Math.max(...[...deviceA, ...deviceB]) * 1.5 : 100
chart.setOption({
grid: {
left: '5%', right: '5%', top: '26%', bottom: '5%',
containLabel: true,
show: true,
borderWidth: 0,
backgroundColor: 'rgba(0, 0, 0, 0)',
shadowColor: 'rgba(0, 0, 0, 0.3)',
shadowBlur: 2
},
tooltip: {
trigger: 'axis',
valueFormatter: (value) => value + unit
},
legend: {
data: ["设备A", "设备B"],
top: '0%',
itemWidth: 10,
itemHeight: 10,
textStyle: {color: "#fff", fontSize: 10}
},
xAxis: {
type: 'category',
boundaryGap: false,
data: xAxisData,
axisLine: {show: true, lineStyle: {color: '#0a73ff'}},
axisTick: {show: false},
axisLabel: {color: '#fff', fontSize: 10, interval: 0}
},
yAxis: {
type: 'value',
splitNumber: 4,
min: 0,
max: max,
axisLine: {show: true, lineStyle: {color: '#0a73ff'}},
splitLine: {lineStyle: {color: '#0a73ff', width: 0.5, opacity: 0.3, type: 'dashed'}},
axisLabel: {color: '#fff', fontSize: 10, formatter: '{value}' + unit}
},
series: [{
name: '设备A',
type: 'line',
data: deviceA,
smooth: true,
lineStyle: {type: 'dashed'},
label: {
show: true,
position: 'top',
color: '#fff',
fontSize: 10,
formatter: (params) => params.value + unit
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{offset: 0, color: 'rgba(58, 143, 255, 0.8)'},
{offset: 1, color: 'rgba(58, 143, 255, 0.1)'}
])
},
}, {
name: '设备B',
type: 'line',
data: deviceB,
smooth: true,
lineStyle: {type: 'dashed'},
label: {
show: true,
position: 'top',
color: '#fff',
fontSize: 10,
formatter: (params) => params.value + unit
},
}]
})
},
// 斩拌机
initZBY550ChopperMixerChart() {
const chart = echarts.init(document.getElementById('ZBY550ChopperMixerChart'))
this.charts.ZBY550ChopperMixer = chart
// x轴数据
const xAxisData = ['设备1', '设备2', '设备3', '设备4']
// 数量
const seriesData = [100, 140, 230, 50]
// 单位
const unit = 'ppm'
// 设置最大值处理显示数值时和顶部重叠问题; 三方表达式:防止无数据时不显示y轴
const max = [...seriesData].length ? Math.max(...[...seriesData]) * 1.5 : 100
chart.setOption({
grid: {
left: '5%', right: '5%', top: '8%', bottom: '5%',
containLabel: true,
show: true,
borderWidth: 0,
backgroundColor: 'rgba(0, 0, 0, 0)',
shadowColor: 'rgba(0, 0, 0, 0.3)',
shadowBlur: 2
},
tooltip: {
trigger: 'axis',
valueFormatter: (value) => value + unit
},
dataZoom: [{
id: 'ZBY550ChopperMixerChart',
type: 'inside', // 表明在图表内部进行拖动
start: 0,
end: (3 / xAxisData.length) * 100, // 默认显示3条
filterMode: 'none'
}],
xAxis: {
type: 'category',
boundaryGap: false,
data: xAxisData,
axisLine: {show: true, lineStyle: {color: '#0a73ff'}},
axisTick: {show: false},
axisLabel: {color: '#fff', fontSize: 10, interval: 0}
},
yAxis: {
type: 'value',
splitNumber: 4,
min: 0,
max: max,
axisLine: {show: true, lineStyle: {color: '#0a73ff'}},
splitLine: {lineStyle: {color: '#0a73ff', width: 0.5, opacity: 0.3, type: 'dashed'}},
axisLabel: {color: '#ccc', fontSize: 10, formatter: '{value}' + unit}
},
series: [{
type: 'line',
data: seriesData,
label: {
show: true,
position: 'top',
color: '#FFF',
fontSize: 10,
formatter: (params) => params.value + unit
}
}]
})
},
// 真空搅拌机
initVacuumMixerChart() {
const leftTopChart = echarts.init(document.getElementById('vacuumMixerRotateSpeedChart'))
const rightTopChart = echarts.init(document.getElementById('vacuumMixerRunTimeChart'))
const leftBottomChart = echarts.init(document.getElementById('vacuumMixerRemainTimeChart'))
const rightBottomChart = echarts.init(document.getElementById('vacuumMixerCWVoltageChart'))
this.charts.vacuumMixerRotateSpeed = leftTopChart
this.charts.vacuumMixerRunTime = rightTopChart
this.charts.vacuumMixerRemainTime = leftBottomChart
this.charts.vacuumMixerCWVoltage = rightBottomChart
// x轴数据
const xAxisData = ['搅拌机1', '搅拌机2', '搅拌机3', '搅拌机4']
// 数量
const seriesData = [45, 60, 80, 20]
// 设置最大值处理显示数值时和顶部重叠问题; 三方表达式:防止无数据时不显示y轴
const max = [...seriesData].length ? Math.max(...[...seriesData]) * 1.5 : 100
leftTopChart.setOption({
grid: {
left: '5%', right: '5%', top: '8%', bottom: '5%',
containLabel: true,
show: true,
borderWidth: 0,
backgroundColor: 'rgba(0, 0, 0, 0)',
shadowColor: 'rgba(0, 0, 0, 0.3)',
shadowBlur: 2
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
valueFormatter: (value) => value + 'ppm',
},
dataZoom: [{
id: 'vacuumMixerRotateSpeedChart',
type: 'inside', // 表明在图表内部进行拖动
start: 0,
end: (3 / xAxisData.length) * 100, // 默认显示3条
filterMode: 'none'
}],
xAxis: {
type: 'category',
data: xAxisData,
axisLine: {lineStyle: {color: '#0a73ff'}},
axisLabel: {color: '#fff', fontSize: 10, interval: 0}
},
yAxis: {
type: 'value',
splitNumber: 4,
min: 0,
max: max,
axisLine: {show: true, lineStyle: {color: '#0a73ff'}},
splitLine: {lineStyle: {color: '#0a73ff', width: 0.5, opacity: 0.3}},
axisLabel: {color: '#fff', fontSize: 10, formatter: '{value}ppm'}
},
series: [{
type: 'bar',
barWidth: '40%',
barGap: '0%',
data: seriesData,
itemStyle: {color: '#5087ec'},
label: {
show: true,
position: 'top',
color: '#FFF',
fontSize: 10,
formatter: (params) => params.value + 'ppm'
}
}]
})
rightTopChart.setOption({
grid: {
left: '5%', right: '5%', top: '8%', bottom: '5%',
containLabel: true,
show: true,
borderWidth: 0,
backgroundColor: 'rgba(0, 0, 0, 0)',
shadowColor: 'rgba(0, 0, 0, 0.3)',
shadowBlur: 2
},
tooltip: {
trigger: 'axis',
valueFormatter: (value) => value + 'min'
},
dataZoom: [{
id: 'vacuumMixerRunTimeChart',
type: 'inside', // 表明在图表内部进行拖动
start: 0,
end: (3 / xAxisData.length) * 100, // 默认显示3条
filterMode: 'none'
}],
xAxis: {
type: 'category',
boundaryGap: false,
data: xAxisData,
axisLine: {show: true, lineStyle: {color: '#0a73ff'}},
axisTick: {show: false},
axisLabel: {color: '#fff', fontSize: 10, interval: 0}
},
yAxis: {
type: 'value',
splitNumber: 4,
min: 0,
max: max,
axisLine: {show: true, lineStyle: {color: '#0a73ff'}},
splitLine: {lineStyle: {color: '#0a73ff', width: 0.5, opacity: 0.3, type: 'dashed'}},
axisLabel: {color: '#ccc', fontSize: 10, formatter: '{value}' + 'min'}
},
series: [{
type: 'line',
data: seriesData,
label: {
show: true,
position: 'top',
color: '#FFF',
fontSize: 10,
formatter: (params) => params.value + 'min'
}
}]
})
leftBottomChart.setOption({
grid: {
left: '5%', right: '5%', top: '8%', bottom: '10%',
containLabel: true,
show: true,
borderWidth: 0,
backgroundColor: 'rgba(0, 0, 0, 0)',
shadowColor: 'rgba(0, 0, 0, 0.3)',
shadowBlur: 2
},
tooltip: {
trigger: 'axis',
valueFormatter: (value) => value + 'min'
},
dataZoom: [{
id: 'vacuumMixerRunTimeChart',
type: 'inside', // 表明在图表内部进行拖动
start: 0,
end: (3 / xAxisData.length) * 100, // 默认显示3条
filterMode: 'none'
}],
xAxis: {
type: 'category',
boundaryGap: false,
data: xAxisData,
axisLine: {show: true, lineStyle: {color: '#0a73ff'}},
axisTick: {show: false},
axisLabel: {color: '#fff', fontSize: 10, interval: 0}
},
yAxis: {
type: 'value',
splitNumber: 4,
min: 0,
max: max,
axisLine: {show: true, lineStyle: {color: '#0a73ff'}},
splitLine: {lineStyle: {color: '#0a73ff', width: 0.5, opacity: 0.3, type: 'dashed'}},
axisLabel: {color: '#ccc', fontSize: 10, formatter: '{value}' + 'min'}
},
series: [{
type: 'line',
data: seriesData,
label: {
show: true,
position: 'top',
color: '#FFF',
fontSize: 10,
formatter: (params) => params.value + 'min'
}
}]
})
rightBottomChart.setOption({
grid: {
left: '5%', right: '5%', top: '8%', bottom: '10%',
containLabel: true,
show: true,
borderWidth: 0,
backgroundColor: 'rgba(0, 0, 0, 0)',
shadowColor: 'rgba(0, 0, 0, 0.3)',
shadowBlur: 2
},
tooltip: {
trigger: 'axis',
valueFormatter: (value) => value + 'kpa'
},
dataZoom: [{
id: 'vacuumMixerRunTimeChart',
type: 'inside', // 表明在图表内部进行拖动
start: 0,
end: (3 / xAxisData.length) * 100, // 默认显示3条
filterMode: 'none'
}],
xAxis: {
type: 'category',
boundaryGap: false,
data: xAxisData,
axisLine: {show: true, lineStyle: {color: '#0a73ff'}},
axisTick: {show: false},
axisLabel: {color: '#fff', fontSize: 10, interval: 0}
},
yAxis: {
type: 'value',
splitNumber: 4,
min: 0,
max: max,
axisLine: {show: true, lineStyle: {color: '#0a73ff'}},
splitLine: {lineStyle: {color: '#0a73ff', width: 0.5, opacity: 0.3, type: 'dashed'}},
axisLabel: {color: '#ccc', fontSize: 10, formatter: '{value}' + 'kpa'}
},
series: [{
type: 'line',
data: seriesData,
label: {
show: true,
position: 'top',
color: '#FFF',
fontSize: 10,
formatter: (params) => params.value + 'kpa'
}
}]
})
},
// 真空滚揉机
initVacuumTumblingChart() {
const leftTopChart = echarts.init(document.getElementById('vacuumTumblingRotateSpeedChart'))
const rightTopChart = echarts.init(document.getElementById('vacuumTumblingRunTimeChart'))
const leftBottomChart = echarts.init(document.getElementById('vacuumTumblingRemainTimeChart'))
this.charts.vacuumTumblingRotateSpeed = leftTopChart
this.charts.vacuumTumblingRunTime = rightTopChart
this.charts.vacuumTumblingRemainTime = leftBottomChart
// x轴数据
const xAxisData = ['滚揉机1', '滚揉机2', '滚揉机3', '滚揉机4']
// 数量
const seriesData = [45, 60, 80, 20]
// 设置最大值处理显示数值时和顶部重叠问题; 三方表达式:防止无数据时不显示y轴
const max = [...seriesData].length ? Math.max(...[...seriesData]) * 1.5 : 100
leftTopChart.setOption({
grid: {
left: '5%', right: '5%', top: '8%', bottom: '5%',
containLabel: true,
show: true,
borderWidth: 0,
backgroundColor: 'rgba(0, 0, 0, 0)',
shadowColor: 'rgba(0, 0, 0, 0.3)',
shadowBlur: 2
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
valueFormatter: (value) => value + 'ppm',
},
dataZoom: [{
id: 'vacuumMixerRotateSpeedChart',
type: 'inside', // 表明在图表内部进行拖动
start: 0,
end: (3 / xAxisData.length) * 100, // 默认显示3条
filterMode: 'none'
}],
xAxis: {
type: 'category',
data: xAxisData,
axisLine: {lineStyle: {color: '#0a73ff'}},
axisLabel: {color: '#fff', fontSize: 10, interval: 0}
},
yAxis: {
type: 'value',
splitNumber: 4,
min: 0,
max: max,
axisLine: {show: true, lineStyle: {color: '#0a73ff'}},
splitLine: {lineStyle: {color: '#0a73ff', width: 0.5, opacity: 0.3}},
axisLabel: {color: '#fff', fontSize: 10, formatter: '{value}ppm'}
},
series: [{
type: 'bar',
barWidth: '40%',
barGap: '0%',
data: seriesData,
itemStyle: {color: '#5087ec'},
label: {
show: true,
position: 'top',
color: '#FFF',
fontSize: 10,
formatter: (params) => params.value + 'ppm'
}
}]
})
rightTopChart.setOption({
grid: {
left: '5%', right: '5%', top: '8%', bottom: '5%',
containLabel: true,
show: true,
borderWidth: 0,
backgroundColor: 'rgba(0, 0, 0, 0)',
shadowColor: 'rgba(0, 0, 0, 0.3)',
shadowBlur: 2
},
tooltip: {
trigger: 'axis',
valueFormatter: (value) => value + 'min'
},
dataZoom: [{
id: 'vacuumMixerRunTimeChart',
type: 'inside', // 表明在图表内部进行拖动
start: 0,
end: (3 / xAxisData.length) * 100, // 默认显示3条
filterMode: 'none'
}],
xAxis: {
type: 'category',
boundaryGap: false,
data: xAxisData,
axisLine: {show: true, lineStyle: {color: '#0a73ff'}},
axisTick: {show: false},
axisLabel: {color: '#fff', fontSize: 10, interval: 0}
},
yAxis: {
type: 'value',
splitNumber: 4,
min: 0,
max: max,
axisLine: {show: true, lineStyle: {color: '#0a73ff'}},
splitLine: {lineStyle: {color: '#0a73ff', width: 0.5, opacity: 0.3, type: 'dashed'}},
axisLabel: {color: '#ccc', fontSize: 10, formatter: '{value}' + 'min'}
},
series: [{
type: 'line',
data: seriesData,
label: {
show: true,
position: 'top',
color: '#FFF',
fontSize: 10,
formatter: (params) => params.value + 'min'
}
}]
})
leftBottomChart.setOption({
grid: {
left: '5%', right: '5%', top: '8%', bottom: '10%',
containLabel: true,
show: true,
borderWidth: 0,
backgroundColor: 'rgba(0, 0, 0, 0)',
shadowColor: 'rgba(0, 0, 0, 0.3)',
shadowBlur: 2
},
tooltip: {
trigger: 'axis',
valueFormatter: (value) => value + 'min'
},
dataZoom: [{
id: 'vacuumMixerRunTimeChart',
type: 'inside', // 表明在图表内部进行拖动
start: 0,
end: (3 / xAxisData.length) * 100, // 默认显示3条
filterMode: 'none'
}],
xAxis: {
type: 'category',
boundaryGap: false,
data: xAxisData,
axisLine: {show: true, lineStyle: {color: '#0a73ff'}},
axisTick: {show: false},
axisLabel: {color: '#fff', fontSize: 10, interval: 0}
},
yAxis: {
type: 'value',
splitNumber: 4,
min: 0,
max: max,
axisLine: {show: true, lineStyle: {color: '#0a73ff'}},
splitLine: {lineStyle: {color: '#0a73ff', width: 0.5, opacity: 0.3, type: 'dashed'}},
axisLabel: {color: '#ccc', fontSize: 10, formatter: '{value}' + 'min'}
},
series: [{
type: 'line',
data: seriesData,
label: {
show: true,
position: 'top',
color: '#FFF',
fontSize: 10,
formatter: (params) => params.value + 'min'
}
}]
})
},
// 今日计划执行情况
initMeatGrainAnalysisChart(data) {
const chart = echarts.init(document.getElementById('meatGrainAnalysisChart'))
this.charts.meatGrainAnalysis = chart
// x轴数据
const xAxisData = []
// 投产数量
const operationData = []
// 完成数量
const quantityData = []
data.forEach(item => {
xAxisData.push(item.product_name)
operationData.push({
value: item.produce_qty || 0,
unit: item.uom_name
})
quantityData.push({
value: item.finish_qty || 0,
unit: item.uom_name
})
})
// 设置最大值处理显示数值时和顶部重叠问题; 三方表达式:防止无数据时不显示y轴
const max = this.calculateMaxValue([operationData, quantityData], 1.5)
chart.setOption({
grid: {
left: '5%', right: '5%', top: '15%', bottom: '5%',
containLabel: true,
show: true,
borderWidth: 0,
backgroundColor: 'rgba(0, 0, 0, 0)',
shadowColor: 'rgba(0, 0, 0, 0.3)',
shadowBlur: 2
},
legend: {
top: '-2%',
right: '5%',
textStyle: {color: '#fff', fontSize: 10},
itemWidth: 20,
itemHeight: 10,
},
tooltip: {
trigger: 'axis',
axisPointer: {type: 'shadow'},
formatter: (params) => {
let tooltipHtml = `<div style="font-weight:bold;margin-bottom:5px">${params[0].name}</div>`
params.forEach(param => {
const dataIndex = param.dataIndex
const unit = param.seriesIndex === 0 ? operationData[dataIndex].unit : quantityData[dataIndex].unit
// 使用系列颜色作为小圆点颜色
tooltipHtml += `
<div style="display:flex;align-items:center;margin:5px 0">
<span style="display:inline-block;width:10px;height:10px;border-radius:50%;background:${param.color};margin-right:10px"></span>
<span style="margin-right:10px">${param.seriesName}:</span>
<strong>${param.value}</strong>
<span>${unit}</span>
</div>
`
})
// 计算完成率
const total = params[0].value
const completed = params[1].value
const percentage = total > 0 ? ((completed / total) * 100).toFixed(1) : 0
tooltipHtml += `
<div style="margin-top:10px;border-top:1px solid #eee;padding-top:5px">
完成率: <strong>${percentage}%</strong>
</div>
`
return tooltipHtml
}
},
dataZoom: [{
id: 'meatGrainAnalysisChart',
type: 'inside', // 表明在图表内部进行拖动
start: 0,
end: (2 / xAxisData.length) * 100, // 默认显示3条
filterMode: 'none'
}],
xAxis: {
type: 'category',
data: xAxisData,
axisLine: {lineStyle: {color: '#0a73ff'}},
axisLabel: {color: '#fff', fontSize: 10}
},
yAxis: {
type: 'value',
splitNumber: 5,
min: 0,
max: max,
axisLine: {show: true, lineStyle: {color: '#0a73ff'}},
splitLine: {lineStyle: {color: '#0a73ff', width: 0.5, opacity: 0.3}},
axisLabel: {color: '#fff', fontSize: 10}
},
series: [{
name: '投产数量',
type: 'bar',
barWidth: '40%',
barGap: '0%',
data: operationData,
itemStyle: {color: '#5087ec'},
label: {
show: true,
position: 'top',
color: '#FFF',
fontSize: 10,
formatter: (params) => params.value + params.data.unit
}
}, {
name: '完成数量',
type: 'bar',
barWidth: '40%',
barGap: '0%',
data: quantityData,
itemStyle: {color: '#68bbc4'},
label: {
show: true,
position: 'top',
color: '#FFF',
fontSize: 10,
formatter: (params) => params.value + params.data.unit
}
}]
})
},
/**
* 计算图表Y轴最大值
* @param {Array} dataArrays 需要合并计算的数据数组
* @param {number} multiplier 放大系数,默认1.2
* @param {number} defaultValue 默认最大值,当无数据时使用
* @returns {number} 计算后的最大值
*/
calculateMaxValue(dataArrays, multiplier = 1.2, defaultValue = 100) {
// 合并所有数组并提取value值
const allValues = dataArrays.flat().map(item => item.value);
// 如果没有数据,返回默认值
if (allValues.length === 0) return defaultValue;
// 计算最大值并应用放大系数,然后向上取整
const maxValue = Math.max(...allValues);
return Math.ceil(maxValue * multiplier);
}
},
beforeDestroy() {
// 清除时间定时器
clearInterval(this.timeInterval)
// 清除日期定时器
clearInterval(this.dateInterval)
// 清除resize事件监听
window.removeEventListener("resize", this.handleResize)
}
})
</script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
::-webkit-scrollbar {
width: 0px;
height: 0px;
}
::-webkit-scrollbar-thumb {
background: #c0c4cc;
border-radius: 3px;
}
::-webkit-scrollbar-track {
background: #f5f7fa;
}
#app {
width: 100vw;
height: 100vh;
overflow: hidden;
color: #fff;
font-family: "Microsoft YaHei", sans-serif;
background-color: #06114f;
display: flex;
flex-direction: column;
}
.pageHeaderBox {
height: 60px;
display: flex;
justify-content: center;
align-items: center;
padding: 0 20px;
border-bottom: 1px solid rgba(10, 115, 255, 0.3);
background-image: url(/tht_project/static/html/big_screen/image/header_background.png);
background-size: 100%;
position: relative;
font-size: 24px;
z-index: 2;
.weatherInfo {
position: absolute;
left: 20px;
bottom: 5px;
font-size: 23px;
display: flex;
align-items: baseline;
gap: 5px;
.location {
font-weight: bold;
}
.icon {
margin-top: 5px;
}
}
.logo {
width: 1.3em;
height: 1.3em;
}
.title {
font-weight: bold;
letter-spacing: 4px;
margin-left: 10px;
}
.dateTimeBox {
position: absolute;
right: 20px;
bottom: 5px;
display: flex;
align-items: center;
font-size: 24px;
.time {
margin-right: 5px;
}
.dateBox {
display: flex;
flex-direction: column;
font-size: 10px;
}
}
}
.pageContentBox {
width: 100%;
height: 1px;
flex: auto;
position: relative;
padding: 10px;
.pageContentBackgroundBox {
position: absolute;
width: calc(100% - 20px);
height: calc(100% - 20px);
padding: 10px;
border-left: 2px solid #cbdbf8;
border-right: 2px solid #cbdbf8;
background: transparent;
border-radius: 10px;
<<<<<<< HEAD
animation: rainbow-glow 5s linear infinite;
box-shadow: -10px 0 10px -10px #ff0000,
10px 0 10px -10px #ff0000,
inset -10px 0 10px -10px #ff0000,
inset 10px 0 10px -10px #ff0000;
background-color: transparent;
.badgeIcon {
position: absolute;
background-color: #06114f;
}
.badgeIcon:first-child {
top: 0;
left: 0;
transform: translate(-12%, -35%);
}
.badgeIcon:nth-child(2) {
top: 0;
right: 0;
transform: translate(12%, -35%) scaleX(-1);
}
.badgeIcon:nth-child(3) {
bottom: 0;
left: 0;
transform: translate(-12%, 35%) scaley(-1);
}
.badgeIcon:last-child {
bottom: 0;
right: 0;
transform: translate(12%, 35%) scale(-1);
}
}
/* 发光小点 */
.pageContentBackgroundBox::before,
.pageContentBackgroundBox::after {
content: '';
position: absolute;
top: 30px;
transform: translateX(-30%);
width: 0px;
height: 10px;
background: #fff;
border-radius: 50%;
box-shadow: 0 0 15px 3px rgba(255, 255, 255, 0.8),
0 0 15px 3px rgba(255, 255, 255, 0.8);
animation: dot-move 4s linear infinite;
z-index: 2;
}
.pageContentBackgroundBox::before {
/* 定位在左边框 */
left: -1px;
}
/* 发光小点 */
.pageContentBackgroundBox::after {
/* 定位在右边框 */
right: -1px;
}
.pageContentArea {
display: flex;
gap: 5px;
padding: 10px;
height: 100%;
.contentColumnBox {
height: 100%;
display: flex;
flex-direction: column;
gap: 5px;
z-index: 1;
.itemCardBox {
height: calc((100% - 15px) / 4);
background-image: url(/tht_project/static/html/big_screen/image/group_background.png);
background-size: 100% 100%;
padding: 5px 8px 0px;
display: flex;
flex-direction: column;
.cardHeader {
height: 30px;
span {
padding: 0 8px;
font-size: 14px;
font-weight: bold;
background: linear-gradient(180deg, #FFF 10%, #00bbf3 100%);
/* 将文字颜色设置成透明色 */
color: transparent;
-webkit-text-fill-color: transparent;
/* 将背景裁剪成文字前景色 */
background-clip: text;
-webkit-background-clip: text;
}
}
.cardContentBox {
flex: auto;
height: 1px;
}
.tableBox {
display: flex;
flex-direction: column;
padding-bottom: 15px;
.tableClass {
flex: auto;
width: 100%;
background-color: transparent;
.el-table__body tr:hover > td.el-table__cell {
background-color: rgba(10, 115, 255, 0.3);
}
}
.tableClass::before {
display: none;
}
}
}
}
.one {
width: calc((100% - 15px) * 0.2);
.itemCardBox {
background-image: none;
.cardHeader {
background-image: url(/tht_project/static/html/big_screen/image/card_header_left_background.png);
background-size: 100% 80%;
background-repeat: no-repeat;
background-position: center;
}
}
}
.two {
width: calc((100% - 15px) * 0.3);
.itemCardBox {
height: calc((100% - 20px) / 5);
}
}
.three {
width: calc((100% - 15px) * 0.3);
.itemCardBox {
height: calc((100% - 10px) * 0.4);
}
.itemCardBox:first-child {
height: calc((100% - 10px) * 0.2);
}
}
.four {
width: calc((100% - 15px) * 0.2);
.itemCardBox {
height: calc((100% - 15px) * 0.25);
.cardHeader {
background-image: url(/tht_project/static/html/big_screen/image/card_header_right_background.png);
background-size: 100% 80%;
background-repeat: no-repeat;
background-position: center;
text-align: right;
}
}
.itemCardBox:first-child {
flex: 1;
}
}
}
}
/* 多彩跑马灯效果 */
@keyframes rainbow-glow {
0% {
box-shadow: -10px 0 10px -10px #ff0000,
10px 0 10px -10px #ff0000,
inset -10px 0 10px -10px #ff0000,
inset 10px 0 10px -10px #ff0000;
border-left-color: #ff0000;
border-right-color: #ff0000;
}
16% {
box-shadow: -10px 0 10px -10px #ff9900,
10px 0 10px -10px #ff9900,
inset -10px 0 10px -10px #ff9900,
inset 10px 0 10px -10px #ff9900;
border-left-color: #ff9900;
border-right-color: #ff9900;
}
33% {
box-shadow: -10px 0 10px -10px #ffff00,
10px 0 10px -10px #ffff00,
inset -10px 0 10px -10px #ffff00,
inset 10px 0 10px -10px #ffff00;
border-left-color: #ffff00;
border-right-color: #ffff00;
}
50% {
box-shadow: -10px 0 10px -10px #33cc33,
10px 0 10px -10px #33cc33,
inset -10px 0 10px -10px #33cc33,
inset 10px 0 10px -10px #33cc33;
border-left-color: #33cc33;
border-right-color: #33cc33;
}
66% {
box-shadow: -10px 0 10px -10px #0099ff,
10px 0 10px -10px #0099ff,
inset -10px 0 10px -10px #0099ff,
inset 10px 0 10px -10px #0099ff;
border-left-color: #0099ff;
border-right-color: #0099ff;
}
83% {
box-shadow: -10px 0 10px -10px #6633ff,
10px 0 10px -10px #6633ff,
inset -10px 0 10px -10px #6633ff,
inset 10px 0 10px -10px #6633ff;
border-left-color: #6633ff;
border-right-color: #6633ff;
}
100% {
box-shadow: -10px 0 10px -10px #ff0066,
10px 0 10px -10px #ff0066,
inset -10px 0 10px -10px #ff0066,
inset 10px 0 10px -10px #ff0066;
border-left-color: #ff0066;
border-right-color: #ff0066;
}
}
/* 小点移动动画 */
@keyframes dot-move {
0% {
top: 30px;
opacity: 1;
}
10% {
opacity: 1;
}
40% {
opacity: 1;
}
50% {
opacity: 0;
top: calc(100% - 30px);
}
60% {
opacity: 1;
}
90% {
opacity: 1;
}
100% {
opacity: 0;
top: 30px;
}
}
</style>
</html>
\ No newline at end of file
odoo.define('tht_project.cook_process', function (require) {
"use strict";
const AbstractAction = require('web.AbstractAction');
const core = require('web.core');
const QWeb = core.qweb;
const session = require('web.session');
const Dialog = require("web.Dialog");
const CookProcessTemplate = AbstractAction.extend({
template: 'CookProcessTemplate',
start: async function () {
await this._super(...arguments);
let self = this;
window.addEventListener("message", function (event) {
});
},
});
core.action_registry.add('tht_project.cook_process', CookProcessTemplate);
return CookProcessTemplate;
});
odoo.define('tht_project.expected_process', function (require) {
"use strict";
const AbstractAction = require('web.AbstractAction');
const core = require('web.core');
const QWeb = core.qweb;
const session = require('web.session');
const Dialog = require("web.Dialog");
const ExpectedProcessTemplate = AbstractAction.extend({
template: 'ExpectedProcessTemplate',
start: async function () {
await this._super(...arguments);
let self = this;
window.addEventListener("message", function (event) {
});
},
});
core.action_registry.add('tht_project.expected_process', ExpectedProcessTemplate);
return ExpectedProcessTemplate;
});
...@@ -526,20 +526,27 @@ ...@@ -526,20 +526,27 @@
if (!deviceStateData || !Array.isArray(deviceStateData)) { if (!deviceStateData || !Array.isArray(deviceStateData)) {
return; return;
} }
const codeToSeqMap = {};
this.allEquipmentData.forEach(equip => {
codeToSeqMap[equip.code] = equip.sequence;
});
// 获取当前车间下的设备 code 列表 // 获取当前车间下的设备 code 列表
const validCodes = this.allEquipmentData const validCodes = this.allEquipmentData
.filter(e => !this.selectedWorkshop || e.plant_name === this.selectedWorkshop) .filter(e => !this.selectedWorkshop || e.plant_name === this.selectedWorkshop)
.map(e => e.code); .map(e => e.code);
// 过滤掉不属于当前车间的设备
const filteredDevices = deviceStateData.filter(d => // 过滤掉不属于当前车间的设备
!this.selectedWorkshop || validCodes.includes(d.code) const filteredDevices = deviceStateData.filter(d =>
); !this.selectedWorkshop || validCodes.includes(d.code)
).map(device => ({
...device,
seq: codeToSeqMap[device.code] || Number.MAX_SAFE_INTEGER // 如果找不到 seq,默认排最后
}));
// 将API返回的数据转换为页面所需的格式 // 将API返回的数据转换为页面所需的格式
this.deviceList = filteredDevices.map((device) => { this.deviceList = filteredDevices.map((device) => {
// 根据API返回的状态确定前端显示的状态 // 根据API返回的状态确定前端显示的状态
let status = "off"; let status = "off";
if (device.state === "green") { if (device.state === "green") {
...@@ -553,7 +560,9 @@ ...@@ -553,7 +560,9 @@
} }
// 计算持续时间的显示格式 // 计算持续时间的显示格式
let durationText = "0" let durationText = "0"
if (device.duration_hours !== undefined) { durationText = this.formatTime(Number(device.duration_hours * 3600)) } if (device.duration_hours !== undefined) {
durationText = this.formatTime(Number(device.duration_hours * 3600))
}
let run_seconds = "0" let run_seconds = "0"
if (device.run_seconds !== undefined) run_seconds = this.formatTime(Number(device.run_seconds)) if (device.run_seconds !== undefined) run_seconds = this.formatTime(Number(device.run_seconds))
...@@ -569,17 +578,19 @@ ...@@ -569,17 +578,19 @@
// 计算利用率百分比,确保有效值 // 计算利用率百分比,确保有效值
const percentage = device.utilization_rate !== undefined ? Math.round(device const percentage = device.utilization_rate !== undefined ? Math.round(device
.utilization_rate) : 0 .utilization_rate) : 0
// 从所有设备列表中获取准确的设备名称 // 从所有设备列表中获取准确的设备名称
let deviceName = device.name || device.code // 默认使用API返回的名称或编码 let deviceName = device.name || device.code // 默认使用API返回的名称或编码
// 在所有设备列表中查找匹配的设备 // 在所有设备列表中查找匹配的设备
if (this.allEquipmentData && this.allEquipmentData.length > 0) { if (this.allEquipmentData && this.allEquipmentData.length > 0) {
const matchedDevice = this.allEquipmentData.find( const matchedDevice = this.allEquipmentData.find(
(equip) => equip.code === device.code (equip) => equip.code === device.code
) )
// 如果找到匹配的设备,使用其名称 // 如果找到匹配的设备,使用其名称
if (matchedDevice && matchedDevice.name) { if (matchedDevice && matchedDevice.name) {
deviceName = device.name ? matchedDevice.name : device.code deviceName = device.name ? matchedDevice.name : device.code
} else {
return false
} }
} }
return { return {
...@@ -594,7 +605,9 @@ ...@@ -594,7 +605,9 @@
yellow_seconds: yellow_seconds, yellow_seconds: yellow_seconds,
red_seconds: red_seconds red_seconds: red_seconds
} }
}); }).sort((a, b) => (codeToSeqMap[a.code] || Number.MAX_SAFE_INTEGER)
- (codeToSeqMap[b.code] || Number.MAX_SAFE_INTEGER));
this.deviceList = this.deviceList.filter((device) => device)
}, },
formatTime(seconds) { formatTime(seconds) {
......
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t t-name="CookProcessTemplate">
<iframe id="cook_process_template_iframe" src="/roke/tht/cook_process" frameBorder="no" width="100%" height="100%"/>
</t>
</templates>
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t t-name="ExpectedProcessTemplate">
<iframe id="expected_process_template_iframe" src="/roke/tht/expected_process" frameBorder="no" width="100%" height="100%"/>
</t>
</templates>
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="tht_project_assets_backend" name="tht_project_assets" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<script type="text/javascript" src="/tht_project/static/src/js/expected_process.js"/>
<script type="text/javascript" src="/tht_project/static/src/js/cook_process.js"/>
</xpath>
</template>
</odoo>
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="action_expected_process" model="ir.actions.client">
<field name="name">预料工序看板</field>
<field name="tag">tht_project.expected_process</field>
<field name="target">current</field>
</record>
<record id="action_cook_process" model="ir.actions.client">
<field name="name">成型工序看板</field>
<field name="tag">tht_project.cook_process</field>
<field name="target">current</field>
</record>
<!-- <menuitem id="roke_mes_equipment_kanban" name="设备看板" sequence="31"-->
<!-- parent="roke_mes_equipment.roke_mes_equipment_main_menu"-->
<!-- active="1"/>-->
<menuitem id="roke_mes_expected_process" name="预料看板" sequence="10"
action="action_expected_process" active="1"/>
<menuitem id="roke_mes_cook_process" name="成型工序" sequence="20"
action="action_cook_process" active="1"/>
</data>
</odoo>
\ No newline at end of file
...@@ -8,6 +8,9 @@ ...@@ -8,6 +8,9 @@
<xpath expr="//field[@name='workshop_id']" position="after"> <xpath expr="//field[@name='workshop_id']" position="after">
<field name="data_acquisition_code"/> <field name="data_acquisition_code"/>
</xpath> </xpath>
<xpath expr="//field[@name='work_center_id']" position="after">
<field name="sequence"/>
</xpath>
</field> </field>
</record> </record>
<record id="view_roke_mes_equipment_tree_tht" model="ir.ui.view"> <record id="view_roke_mes_equipment_tree_tht" model="ir.ui.view">
......
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