// ==UserScript==
// @name 职业技能标准PDF工具
// @namespace http://tampermonkey.net/
// @version 2025.8.18
// @description 为职业标准系统添加PDF浏览器查看和下载功能(修复初始化问题)
// @author Shuaima
// @match *://www.osta.org.cn/skillStandard*
// @grant none
// @run-at document-end
// @require https://lib.baomitu.com/jquery/1.8.3/jquery.min.js
// @require https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-y/layer/3.5.1/layer.min.js
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// 日志工具
const logger = {
log: (msg) => console.log(`[PDF工具] ${msg}`),
error: (msg) => console.error(`[PDF工具] ${msg}`)
};
// 确保TableModifier先定义(解决函数未初始化问题)
const TableModifier = {
// 标记是否已初始化
initialized: false,
/**
* 初始化表格修改器
*/
init: function() {
if (this.initialized) return;
this.initialized = true;
logger.log('TableModifier初始化完成');
},
/**
* 重建表格,添加自定义操作按钮
*/
rebuildTable: function(data) {
// 确保初始化
this.init();
try {
const standardsList = (data?.body?.list) || [];
const standardsMap = {};
// 映射数据
standardsList.forEach(item => {
standardsMap[item.code] = item;
});
// 移除旧按钮
$('.plugin-tool').remove();
// 检查表格是否存在
const $tableRows = $(".arco-table-element tbody > tr");
if (!$tableRows.length) {
logger.error('未找到表格行,无法添加按钮');
return;
}
// 遍历行添加按钮
$tableRows.each(function() {
const $row = $(this);
const code = $row.find('td').eq(2).text().trim();
const item = standardsMap[code];
if (!item) return;
const $actionCell = $row.find('td').eq(5);
if (!$actionCell.length) return;
// 修改原有链接文本
$actionCell.find('a').text('网站查看');
// 添加新按钮
$actionCell.find('.arco-table-td-content').append(`
浏览器查看
PDF下载
`);
// 绑定数据
$actionCell.find(".plugin-tool").data('item', item);
});
logger.log(`已为${$tableRows.length}行添加操作按钮`);
} catch (error) {
logger.error(`rebuildTable执行失败: ${error.message}`);
}
}
};
// PDF处理工具
const PdfHandler = {
cache: {},
apiUrl: '//www.osta.org.cn/api/sys/downloadFile/decrypt',
handleClick: async function(event) {
try {
const $btn = $(event.currentTarget);
const actionType = $btn.data('type');
const item = $btn.data('item');
if (!item) {
alert('数据获取失败,请刷新页面');
return;
}
const { code, standardInfo: fileName, standardInfoName: displayName } = item;
// 缓存逻辑
if (this.cache[code]) {
this.cache[code].accessCount++;
this.processPdfData(actionType, displayName, code, this.cache[code].blobData);
if (this.cache[code].accessCount > 10) delete this.cache[code];
return;
}
// 加载动画
const loading = layer.load(0, { shade: [0.5, '#000'] });
// 请求PDF
const response = await fetch(`${this.apiUrl}?fileName=${encodeURIComponent(fileName)}`);
if (!response.ok) throw new Error(`HTTP错误: ${response.status}`);
const pdfBlob = await response.blob();
this.cache[code] = { blobData: pdfBlob, accessCount: 1 };
this.processPdfData(actionType, displayName, code, pdfBlob);
} catch (error) {
logger.error(`处理失败: ${error.message}`);
alert(`操作失败: ${error.message}`);
} finally {
layer.closeAll('loading');
}
},
processPdfData: function(actionType, displayName, code, pdfBlob) {
const blob = new Blob([pdfBlob], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
if (actionType === 'open') {
window.open(url);
} else if (actionType === 'download') {
const a = document.createElement('a');
a.download = this.sanitizeFileName(displayName);
a.href = url;
a.click();
setTimeout(() => URL.revokeObjectURL(url), 1000);
}
},
sanitizeFileName: function(name) {
const clean = name.replace(/[\\/:*?"<>|]/g, '');
return clean.endsWith('.pdf') ? clean : `${clean}.pdf`;
}
};
// AJAX监控器
const AjaxMonitor = {
init: function() {
// 确保TableModifier已初始化
TableModifier.init();
const originalXHR = window.XMLHttpRequest;
window.XMLHttpRequest = function() {
const xhr = new originalXHR();
xhr.addEventListener('readystatechange', function() {
const event = new CustomEvent('ajaxReadyStateChange', { detail: this });
window.dispatchEvent(event);
});
return xhr;
};
window.addEventListener('ajaxReadyStateChange', (e) => this.handleResponse(e));
logger.log('AJAX监控已启动');
},
handleResponse: function(event) {
const xhr = event.detail;
if (xhr.readyState === 4 && xhr.responseURL?.includes('skillStandardList')) {
try {
const data = JSON.parse(xhr.responseText || '{}');
// 确保TableModifier已定义
if (TableModifier && typeof TableModifier.rebuildTable === 'function') {
setTimeout(() => TableModifier.rebuildTable(data), 500);
} else {
logger.error('TableModifier未初始化,无法重建表格');
}
} catch (error) {
logger.error(`解析响应失败: ${error.message}`);
}
}
}
};
// 主初始化函数
function init() {
try {
// 初始化依赖链
TableModifier.init();
AjaxMonitor.init();
// 绑定事件
$(document).on('click', '.plugin-tool', PdfHandler.handleClick.bind(PdfHandler));
// 尝试手动触发一次表格重建(针对页面已加载完成的情况)
setTimeout(() => {
logger.log('尝试手动触发表格初始化');
TableModifier.rebuildTable({ body: { list: [] } });
}, 1000);
logger.log('插件初始化完成');
} catch (error) {
logger.error(`初始化失败: ${error.message}`);
// 重试机制
setTimeout(init, 2000);
}
}
// 页面加载完成后启动
if (document.readyState === 'complete') {
init();
} else {
window.addEventListener('load', init);
}
})();