// ==UserScript==
// @name dialog-MY
// @namespace http://tampermonkey.net/
// @version V0.1
// @description get data from mengxi
// @author wei
// @match https://www.imptc.com/*
// @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant GM_download
// @grant GM_xmlhttpRequest
// @license AGPL-3.0
// ==/UserScript==
(function () {
'use strict';
function getCookieObject() {
var cookies = document.cookie.split(';');
var cookieObj = {};
for (var i = 0; i < cookies.length; i++) {
var parts = cookies[i].trim().split('=');
cookieObj[parts[0]] = parts[1];
}
return cookieObj;
}
// 随机生成 30 到 60 秒的延迟
function randomDelay(minDelay = 30000, maxDelay = 60000) {
return new Promise(resolve => {
const delayTime = Math.floor(Math.random() * (maxDelay - minDelay + 1)) + minDelay; // minDelay ~ maxDelay秒
setTimeout(resolve, delayTime);
});
}
// 输入 '2024-10-01, 2024-10-10', 输出时间序列列表
function generateDateList(dateRangeStr) {
let [startDateStr, endDateStr] = dateRangeStr.split(',').map(date => date.trim());
let startDate = new Date(startDateStr);
let endDate = new Date(endDateStr);
let dateList = [];
while (startDate <= endDate) {
dateList.push(startDate.toISOString().split('T')[0]); // 转为字符串格式
startDate.setDate(startDate.getDate() + 1); // 日期加1
}
return dateList;
}
// 输入 '2024-10', 输出 ['2024-10-01', '2024-10-02', '2024-10-03', '2024-10-04', ..., '2024-10-30', '2024-10-31']
function getDaysInMonth(dateStr) {
const year = parseInt(dateStr.split('-')[0]);
const month = parseInt(dateStr.split('-')[1]);
const daysInMonth = new Date(year, month, 0).getDate();
const result = [];
for (let day = 1; day <= daysInMonth; day++) {
result.push(`${year}-${month.toString().padStart(2, '0')}-${day.toString().padStart(2, '0')}`);
}
return result;
}
// 导出 市场情况分析 - 信息对比 中的数据
function doExportD1Data(startDate, endDate) {
// console.log('在D1中', startDate, endDate);
let internalStartDate = startDate;
let internalEndDate = endDate;
return new Promise((resolve, reject) => {
var cookieObject = getCookieObject();
let authorization = cookieObject["Token"];
// console.log(internalStartDate, '=============');
// console.log(internalEndDate, '=============');
// 分别是 电价曲线、负荷预测、东送计划、非市场化曲线、新能源预测
let urls_nosuffix = [
'https://www.imptc.com/api/scqkfx/sctjfxyyc/crqwxxfb/getYhcjqjgData/',
'https://www.imptc.com/api/scqkfx/sctjfxyyc/crqwxxfb/getQwtdfhycscData/',
'https://www.imptc.com/api/scqkfx/sctjfxyyc/crqwxxfb/getCrdsjhycscData/',
'https://www.imptc.com/api/scqkfx/sctjfxyyc/crqwxxfb/fsccl/',
'https://www.imptc.com/api/scqkfx/sctjfxyyc/crqwxxfb/getXnyfdnlycData/'
];
let dateList = generateDateList(internalStartDate+ ", " + internalEndDate);
// console.log(dateList, '----------------');
// 定义请求名称数组,与 urls 对应
let requestNames = ['电价曲线', '负荷预测', '东送计划', '非市场化曲线', '新能源预测'];
// 定义额外信息数组
let additionalInfo = [
['时间', '全网统一出清电价', '呼包东统一出清电价', '呼包西统一出清电价', '日前预出清电能价格'],
['时间', '统调负荷预测', '统调负荷实测'],
['时间', '东送计划预测', '东送计划实测'],
['时间', '非市场出力计划', '非市场出力计划实测'],
['时间', '光伏出力预测', '光伏出力实测', '风电出力预测', '风电出力实测', '新能源出力预测', '新能源出力实测'],
];
// 后续代码逻辑中,都使用internalStartDate和internalEndDate来代替原来的startDate和endDate
// 例如在async function processD1Dates里相关判断等操作
async function processD1Dates() {
try {
for (let cur_date of dateList) {
// 用于收集每个日期对应的所有请求的Promise
let allRequests = [];
for (let i = 0; i < urls_nosuffix.length; i++) {
let url = urls_nosuffix[i] + cur_date + '/' + cur_date;
let index = i; // 保存当前索引值,避免闭包问题
console.log(url);
(async () => {
try {
let response = await fetch(url, {
method: 'POST',
headers: {
'accept': 'application/json, text/plain, */*',
'authorization': authorization
},
credentials: 'include'
});
let data = await response.json();
let data_dict = {
"data_class": "d1_data",
"class_name": requestNames[index],
"date": cur_date,
"columns": additionalInfo[index],
"data": data["data"]
};
await GM_xmlhttpRequest({
method: "POST",
url: "https://tradex.mywind.com.cn/tradex",
// url: "http://192.168.0.90:7001/tradex",
headers: {
"Content-Type": "application/json;charset=UTF-8"
},
data: JSON.stringify(data_dict),
onload: function (response) {
console.log("请求成功");
console.log(response.responseText);
},
onerror: function (response) {
console.log("请求失败。。。");
console.log(response.responseText);
}
});
} catch (error) {
console.error(`请求 ${url} 发生错误:`, error);
}
})().then(request => allRequests.push(request));
}
await Promise.all(allRequests);
console.log(`D1 已完成日期: ${cur_date}`);
console.log(`D1 等待完成,继续执行下一个日期`);
await randomDelay();
}
console.log("D1 所有日期的请求已完成");
resolve(); // 当所有日期的请求都完成后,调用resolve表示成功完成
} catch (error) {
console.error("处理请求时发生错误:", error);
reject(error); // 如果出现错误,调用reject传递错误信息
}
}
processD1Dates();
});
}
// 导出 交易管理 - 现货交易 - 省内电能量交易 - 次日全网信息 中的数据
function doExportD3Data(startDate, endDate) {
let internalStartDate = startDate;
let internalEndDate = endDate;
return new Promise((resolve, reject) => {
var cookieObject = getCookieObject();
let authorization = cookieObject["Token"];
console.log(authorization);
let dateList = generateDateList(internalStartDate+", "+internalEndDate);
// 分别是 统调负荷、东送计划、非市场出力、新能源出力, 正负备用容量
let urls_nosuffix = [
'https://www.imptc.com/api/sctjfxyyc/crqwxxfb/getQwtdfhycData/',
'https://www.imptc.com/api/sctjfxyyc/crqwxxfb/getCrdsjhData/',
'https://www.imptc.com/api/sctjfxyyc/crqwxxfb/fsccl/',
'https://www.imptc.com/api/sctjfxyyc/crqwxxfb/getXnyfdnlycData/',
'https://www.imptc.com/api/sctjfxyyc/crqwxxfb/getCrqwbyrlData/'
];
// 定义请求名称数组,与 urls 对应
let requestNames = ['统调负荷', '东送计划', '非市场出力', '新能源出力', '正负备用容量'];
async function processDates() {
try {
for (let cur_date of dateList) {
let datePairs = [];
let dateObj = new Date(cur_date);
dateObj.setDate(dateObj.getDate() + 1);
let after_date = dateObj.toISOString().split('T')[0];
datePairs.push(cur_date);
datePairs.push(after_date);
// 用于收集所有日期对对应的所有请求的Promise
let allRequests = [];
for (let date of datePairs) {
let requests = urls_nosuffix.map((url, index) => {
let options = {
method: 'POST',
headers: {
'accept': 'application/json, text/plain, */*',
'authorization': authorization,
},
credentials: 'include',
};
if (url.includes('getXnyfdnlycData')) {
options.body = JSON.stringify({ 'time': date, 'area': '', 'name': '0' });
options.headers['Content-Type'] = 'application/json';
} else {
url += date;
}
return async () => {
try {
let response = await fetch(url, options);
let data = await response.json();
let data_dict = {
"data_class": "d3_data",
"class_name": requestNames[index],
"date": date,
"data": data["data"]
};
return GM_xmlhttpRequest({
method: "POST",
url: "https://tradex.mywind.com.cn/tradex",
// url: "http://192.168.0.90:7001/tradex",
headers: {
"Content-Type": "application/json;charset=UTF-8"
},
data: JSON.stringify(data_dict),
onload: function (response) {
console.log("请求成功");
console.log(response.responseText);
},
onerror: function (response) {
console.log("请求失败");
console.log(response.responseText);
}
});
} catch (error) {
console.error(`请求 ${url} ${requestNames[index]} 发生错误:`, error);
}
};
});
allRequests.push(...requests);
}
// 等待所有日期对对应的所有请求完成
await Promise.all(allRequests.map(request => request()));
console.log(`已完成日期范围: ${datePairs.join(' - ')}`);
// 随机延迟后继续执行下一个日期的循环
console.log(`等待完成,继续执行下一个日期`);
await randomDelay();
}
console.log("所有日期的请求已完成");
resolve(); // 所有日期请求完成后,调用resolve表示成功完成
} catch (error) {
console.error("处理请求时发生错误:", error);
reject(error); // 如果出现错误,调用reject传递错误信息
}
}
processDates();
});
}
// 导出 节点电价数据
// 信息披露 - 市场运营机构 - 6.52现货市场申报、出清信息 - 实时节点电价
function doExportNodeData(startDate, endDate) {
let internalStartDate = startDate;
let internalEndDate = endDate;
return new Promise((resolve, reject) => {
var cookieObject = getCookieObject();
let authorization = cookieObject["Token"];
console.log(authorization);
let dateList = generateDateList(internalStartDate+", "+internalEndDate);
console.log(dateList)
// 'https://www.imptc.com/api/xxpl2024/scyyqywh/getDetials/6.52//2024-12-20/tab7'
let url_nosuffix = 'https://www.imptc.com/api/xxpl2024/scyyqywh/getDetials/6.52//';
// 定义请求名称
let requestName = '节点电价';
async function processDates() {
try {
for (let cur_date of dateList) {
let fullUrl = url_nosuffix + cur_date + '/tab7';
let options = {
method: 'POST',
headers: {
'Accept': 'application/json, text/plain, */*',
'Authorization': authorization,
'Content-Type': 'application/json'
}
};
try {
let response = await fetch(fullUrl, options);
let data = await response.json();
let data_dict = {
"data_class": "node_price_data",
"class_name": requestName,
"date": cur_date,
"data": data
};
await new Promise((innerResolve, innerReject) => {
GM_xmlhttpRequest({
method: "POST",
url: "https://tradex.mywind.com.cn/tradex",
// url: "http://192.168.0.90:7001/tradex",
headers: {
"Content-Type": "application/json;charset=UTF-8"
},
data: JSON.stringify(data_dict),
onload: function (response) {
console.log("请求成功");
console.log(response.responseText);
innerResolve();
},
onerror: function (response) {
console.log("请求失败");
console.log(response.responseText);
innerReject(new Error('GM_xmlhttpRequest failed'));
}
});
});
console.log(`节点电价 - 已完成日期: ${cur_date}`);
} catch (error) {
console.error(`请求 ${fullUrl} ${requestName} 发生错误:`, error);
}
// 随机延迟后继续执行下一个日期的循环
console.log(`等待完成,继续执行下一个日期`);
await randomDelay();
}
console.log("所有日期的节点电价请求已完成");
resolve(); // 所有日期请求完成后,调用resolve表示成功完成
} catch (error) {
console.error("处理节点电价请求时发生错误:", error);
reject(error); // 如果出现错误,调用reject传递错误信息
}
}
processDates();
});
}
const functionMap = {
'D1信息对比数据': doExportD1Data,
'D3数据': doExportD3Data,
'节点数据': doExportNodeData
};
const layuiCssLink = document.createElement("link");
layuiCssLink.rel = 'stylesheet';
layuiCssLink.href = "//unpkg.com/[email protected]/dist/css/layui.css"
document.head.appendChild(layuiCssLink);
const layuiScript = document.createElement("script");
layuiScript.src = "//unpkg.com/[email protected]/dist/layui.js"
document.head.appendChild(layuiScript);
console.log("这是测试111......");
// 创建悬浮按钮
const floatingButton = document.createElement('button');
floatingButton.classList.add('floating-button');
floatingButton.innerHTML = '☰';
document.body.appendChild(floatingButton);
// 记录悬浮按钮初始的top值,用于后续计算
let initialTop = floatingButton.offsetTop;
// 鼠标按下事件处理函数
floatingButton.addEventListener('mousedown', (e) => {
e.preventDefault(); // 阻止默认的鼠标按下行为,比如选中文字等
// 记录鼠标按下时相对于按钮的偏移量
let offsetY = e.clientY - floatingButton.getBoundingClientRect().top;
// 鼠标移动事件处理函数
const onMouseMove = (e) => {
e.preventDefault();
// 根据鼠标移动更新按钮的top值,使其在右侧边缘上下滑动
let newTop = e.clientY - offsetY;
// 限制按钮只能在可视区域内滑动,防止滑出屏幕顶部或底部
let maxTop = window.innerHeight - floatingButton.offsetHeight;
let minTop = 0;
newTop = Math.min(maxTop, Math.max(minTop, newTop));
floatingButton.style.top = `${newTop}px`;
};
// 鼠标松开事件处理函数
const onMouseUp = () => {
// 移除鼠标移动和松开事件的监听,避免不必要的性能消耗
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
};
// 添加鼠标移动和松开事件的监听
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
});
// 添加 CSS 样式
const style = document.createElement('style');
style.textContent = `
/* 悬浮按钮样式 */
.floating-button {
position: absolute; /* 从fixed改为absolute,以便后续能相对父元素定位 */
right: 0; /* 初始贴紧右侧边缘 */
top: 50%; /* 初始垂直居中,后续会根据鼠标移动调整 */
width: 50px;
height: 50px;
border-radius: 50%;
background-color: #0078d4;
color: white;
border: none;
cursor: pointer;
font-size: 24px;
display: flex;
justify-content: center;
align-items: center;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
transform: translateY(-50%); /* 初始垂直居中的偏移调整 */
user-select: none; /* 防止按钮文本被选中,提升用户体验 */
}
`;
document.head.appendChild(style);
// 定义一个变量来跟踪窗口状态,true表示展开,false表示缩回
let isWindowOpen = false;
// 获取当前时间
const currentTime = new Date().toLocaleString();
// 定义弹出窗口的宽和高
const panelWidth = 900
const panelHeight = 500
// 获取网页宽高
let windowWidth = window.innerWidth;
let windowHeight = window.innerHeight;
floatingButton.addEventListener('click', () => {
if (isWindowOpen) {
// 如果窗口是展开的,关闭窗口
layer.closeAll('page'); // 关闭所有类型为'page'(即layer.open中type: 1的页面层)的弹出层
isWindowOpen = false;
} else {
const floatingButtonRect = floatingButton.getBoundingClientRect();
// 计算弹出窗口的偏移量
let offsetTop = windowWidth - panelWidth - 80
let offsetRight = floatingButtonRect.y
// 如果窗口是缩回的,打开窗口
layui.use(['layer', 'table'], function () {
var layer = layui.layer;
var table = layui.table;
let form = layui.form;
let laydate = layui.laydate;
layer.open({
type: 1,
area: [panelWidth + 'px', panelHeight + 'px'],
offset: [offsetRight, offsetTop],
resize: false,
shadeClose: true,
title: 'MXXH工具',
shade: 0,
content: ` <div class="talbeBox">
<style>
.row {
height: 40px;
display: flex;
justify-content: flex-start;
align-items: center;
margin-bottom: 20px;
padding-left: 20px;
}
.row .formItem {
display: flex;
justify-content: flex-start;
align-items: center;
margin-right: 10px;
}
.row .layui-input {
width: 150px;
}
</style>
</style>
<div class="layui-form" lay-filter="headForm">
<div class="row1 row">
<div class="formItem">
<label>接口名称:</label>
<input class="layui-input" placeholder="输入框" name="APIName">
</div>
<div class="formItem">
<label>接口频率:</label>
<select name="APIRate">
<option value="">请选择</option>
<option value="AAA">选项 A</option>
<option value="BBB">选项 B</option>
<option value="CCC">选项 C</option>
</select>
</div>
<div class="formItem">
<label>运行状态:</label>
<select name="status">
<option value="">请选择</option>
<option value="AAA">未执行</option>
<option value="BBB">正在执行</option>
<option value="CCC">已完成</option>
<option value="DDD">执行报错</option>
</select>
</div>
</div>
<div class="row2 row">
<div class="formItem">
<label>数据类型:</label>
<select name="dataType">
<option value="AAA">选项 A</option>
<option value="BBB">选项 B</option>
</select>
</div>
<div class="formItem" id="layui-time">
<label>日期范围:</label>
<input type="text" autocomplete="off" name="startName" id="layui-startTime" class="layui-input"
placeholder="开始日期">-
<input type="text" autocomplete="off" name="endName" id="layui-endTime" class="layui-input"
placeholder="结束日期">
</div>
<div class="formItem">
<p>0.323s</p>
</div>
<div class="formItem">
<button type="button" class="layui-btn layui-bg-blue" id="dialog-run">批量运行</button>
</div>
</div>
</div>
<div class="tableBox">
<div id="myTable"></div>
</div>
</div>`,
success: function () {
// 可以在这里进行一些初始化操作,比如表格渲染(如果使用了动态数据加载等情况)
// 日期范围 - 左右面板联动选择模式
laydate.render({
elem: '#layui-time',
range: ['#layui-startTime', '#layui-endTime'],
rangeLinked: true // 开启日期范围选择时的区间联动标注模式 --- 2.8+ 新增
});
form.render()
function validateDateRange(startDateStr, endDateStr) {
if (startDateStr === '') {
alert('开始日期未选择,请选择开始日期!');
return false;
}
if (endDateStr === '') {
alert('结束日期未选择,请选择结束日期!');
return false;
}
const startDate = new Date(startDateStr);
const endDate = new Date(endDateStr);
if (startDate > endDate) {
alert('开始日期不能大于结束日期,请重新选择!');
return false;
}
return true;
}
function updateTableRowStatus(tableData, rowIndex, status) {
tableData[rowIndex].field5 = status;
table.reload('myTable', {
data: tableData
});
}
//批量运行点击事件
document.getElementById('dialog-run').addEventListener('click', () => {
let headForm = form.val('headForm')
let tableCheckData = table.checkStatus('myTable')
console.log('headForm:', headForm)
console.log('tableCheckData:', tableCheckData)
// 用于存储所有异步任务(每个函数调用包装成一个Promise)
const promises = [];
// 获取开始日期和结束日期的值
const startDateStr = document.getElementById('layui-startTime').value;
const endDateStr = document.getElementById('layui-endTime').value;
if (!validateDateRange(startDateStr, endDateStr)) {
return;
}
console.log('开始日期: ', startDateStr, '结束日期: ', endDateStr)
// 遍历被选择的行,创建异步任务并添加到promises数组
tableCheckData.data.forEach((row, index) => {
let rowIndex = tableData.findIndex((item) => item.field1 === row.field1);
if (rowIndex!== -1) {
updateTableRowStatus(tableData, rowIndex, '正在执行');
const func = functionMap[row.field1];
tableData[rowIndex].field5 = '正在执行';
table.reload('myTable', {
data: tableData
});
const promise = new Promise((resolve, reject) => {
func(startDateStr, endDateStr).then(() => {
// 函数执行成功后,将该行的field5更新为'已完成'
updateTableRowStatus(tableData, rowIndex, '已完成');
resolve();
}).catch((error) => {
// 如果函数执行出错,更新状态为'执行报错',方便直观查看问题
tableData[rowIndex].field5 = '执行报错';
table.reload('myTable', {
data: tableData
});
console.error(`执行函数 ${func.name} 出错:`, error);
reject(error);
});
});
promises.push(promise);
}
});
// 使用Promise.all同时触发所有异步任务
Promise.all(promises).then(() => {
}).catch((error) => {
// 如果有任何一个函数执行出错,整体捕捉错误并可以进行相应提示等处理
console.error('批量运行出现错误:', error);
layer.msg('批量运行出现错误,请检查!', { icon: 5, time: 2000 });
});
})
let tableData = [
{
field1: 'D1信息对比数据',
field2: '',
field3: '',
field4: '',
field5: '未运行',
},
{
field1: 'D3数据',
field2: '',
field3: '',
field4: '',
field5: '未运行',
},
{
field1: '私有数据',
field2: '',
field3: '',
field4: '',
field5: '未运行',
},
{
field1: '节点数据',
field2: '',
field3: '',
field4: '',
field5: '未运行',
}
]
table.render({
elem: '#myTable',
cols: [[
{ type: 'checkbox', width: 80, },
{ field: 'field1', title: '接口名称' },
{ field: 'field2', title: '频率' },
{ field: 'field3', title: '周期' },
{ field: 'field4', title: '运行时间' },
{ field: 'field5', title: '运行状态' },
]],
data: tableData,
// height: 'full-35', // 最大高度减去其他容器已占有的高度差
})
// 输出被选择的行数据
// console.log('被选择的行数据:', tableCheckData);
// // 遍历被选择的行,将运行状态改为'正在执行'
// tableCheckData.data.forEach((row) => {
// let rowIndex = tableData.findIndex((item) => item.field1 === row.field1);
// if (rowIndex!== -1) {
// tableData[rowIndex].field5 = '正在执行';
// }
// });
},
cancel: function () {
isWindowOpen = false;
},
});
isWindowOpen = true;
});
}
});
})();