您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
try to take over the ERP!
// ==UserScript== // @name ERP开发效率脚本 // @namespace http://tampermonkey.net/ // @version 1.9.9.4 // @description try to take over the ERP! // @author sbotAzlt // @match *://edge.dev.kye-erp.com/* // @match *://www.kye-erp.com/* // @license GPL-3.0 License // @match *://edge.uat.kye-erp.com/* // @match *://edge-stg.kye-erp.com/* // @match *://sec.local.kye-erp.com/* // @match *://app.mockplus.cn/* // @match *://img02.mockplus.cn/* // @require https://cdn.bootcdn.net/ajax/libs/lodash.js/4.17.21/lodash.min.js // @icon https://www.google.com/s2/favicons?sz=64&domain=kye-erp.com // ==/UserScript== (function() { 'use strict'; const actions = [ { func: 'getAuths', name: '获取权限编码' }, { func: 'getCooApi', name: '快速获取API' }, { func: 'getQueryTableCodes', name: '获取通用查询code' }, { func: 'getToken', name: '获取Token' }, { func: 'getQueryKeyAndSearchCode', name: '获取自定义列对应key label 字典' }, { func: 'getQueryTableFormPage', name: '获取自定义查询配置' }, { func: 'getPrototypeQueryTablePage', name: '通过原型获取通用查询列表配置' }, { func: 'getPrototypeBackEndQueryTablePage', name: '通过原型后端获取通用查询列表配置' }, { func: 'queryTableColumnSetKey', name: '通过自定义匹配开放平台key' }, { func: 'draggingQueryTableSetWindth', name: '拖动自定义列修改宽度获取修改后配置' } ] const funcs = { // 获取权限编码 getAuths: () => { const { hash } = window.location if (hash !== '#/uamp/module-auth-conf') { alert('请进入【模块权限配置】模块执行') return } const tb = document.querySelectorAll('.query-table-container')[1].__vue__ const data = tb.getData() const { rows } = data const auths = rows.map(item => ({ method: item.authCode, authCode: item.permissionCode, buttonId: item.id })) console.group('=====获取权限编码 strat=====') console.log('=======>>>> auths', auths) console.log('权限编码获取成功,自动复制到剪切板 ', auths) console.table(auths) copyLink(JSON.stringify(auths)) console.groupEnd() }, // coo快速生成api getCooApi: () => { const { hash } = window.location if (hash !== '#/uamp/module-auth-conf') { alert('请进入【模块权限配置】模块执行') return } let API = {} let str = '' const tb = document.querySelectorAll('.query-table-container')[1].__vue__ const data = tb.getData() const { rows } = data rows.forEach((item) => { const codeList = item.authCode.split('.') const key = codeList[codeList.length - 2] + codeList[codeList.length - 1].replace(/( |^)[a-z]/g, (L) => L.toUpperCase()) API = Object.assign(API, { [key]: { name: item.name, method: item.authCode, authCode: item.permissionCode, buttonId: item.id }, }) }) Object.keys(API).forEach(v => { str += `export const ${v} = ${JSON.stringify(API[v])} \n` }) copyLink(str) console.group('=====coo快速生成api strat=====') console.log('api生成结果成功:,自动复制到剪切板 ', str) console.groupEnd() }, // 获取queryTable自定义查询code getQueryTableCodes: () => { const container = document.querySelector('.query-table-container').__vue__; const searchCodes = container.tables.map(item => item.searchCode); const genericSearchCode = container.generic ? container.generic.searchCode : ''; const optionSearchCode = container.option ? container.option.searchCode : ''; console.group('=====获取获取queryTable自定义查询code strat=====') copyLink(`自定义列: ${searchCodes || '无'}, 自定义查询 ${optionSearchCode || '无'}, 通用查询查询${genericSearchCode || '无'}`) console.log('自定义列code,自动复制到剪切板====>>>>', searchCodes) console.log('自定义查询code,自动复制到剪切板====>>>>', optionSearchCode) console.log('通用查询查询code,自动复制到剪切板====>>>>', genericSearchCode) console.groupEnd() }, // 获取getToken getToken: () => { const token = window.sessionStorage.getItem('ERPTOKEN') console.log('当前token获取成功,自动复制到剪切板', token) copyLink(token) }, // 获取queryTable key adn searchCode getQueryKeyAndSearchCode() { const el = document.querySelectorAll('.query-table-container') const result = {} el.forEach(vItem => { vItem.__vue__.tables.forEach(item => { result[item.searchCode] = {[`${item.option.label || ''} ===>> searchCode`]: item.searchCode } item.columns.forEach(v=> { result[item.searchCode][v.filter && v.filter.args ? `${v.label} ===>> 字典:${v.filter.args.join(',')}` : v.label] = `${v.key}`.trim() if (v.children && v.children.length) { v.children.forEach(vc => { result[item.searchCode][vc.filter && vc.filter.args ? `${v.label} ===>> ${vc.label} ===>> 字典:${vc.filter.args.join(',')} ` :`${v.label}===>>>${vc.label}`] = vc.key.trim() }) } }) }) }) console.group('=====获取queryTable Key code strat=====') copyLink(JSON.stringify(result)) Object.keys(result).forEach(v => { console.table(result[v]) }) console.groupEnd() }, // 通过原型获取通用查询列表配置 async getPrototypeQueryTablePage(isLog = true) { const { host } = window.location if (!host.includes('mockplus.cn')) { alert('请进入【摹客】模块执行') return } // iFrame 页面会导致跨域,剥离掉一层 刚好src是 about:blank ,是否为摹客bug? if (!host.includes('img02.mockplus.cn')) { const axurePageSrc = document.getElementById("axure-page").src window.open(axurePageSrc) return } const topWin = document.getElementById("mainFrame").contentWindow let oneTableALL = topWin.document.querySelectorAll(`[data-label="一级表头"]`) let towTableALL = topWin.document.querySelectorAll(`[data-label="二级表头"]`) if (!oneTableALL.length && !towTableALL.length) { oneTableALL = topWin.document.querySelectorAll(`.panel_state_content > [data-label="一级表头"]`) towTableALL = topWin.document.querySelectorAll(`.panel_state_content > [data-label="二级表头"]`) } if (!oneTableALL.length && !towTableALL.length) { alert('未找到一级表头/二级表头,联系产品修改原型规范') return } let oneTable = [] let towTable = [] towTableALL.forEach(item => { function queryEle(el) { if (el.style.display === 'none') { return } if (el.id === 'base' || el.parentElement.id ==='base') { towTable.push(item) return } queryEle(el.parentElement) } queryEle(item) }) oneTableALL.forEach((item, index) => { function queryEle(el) { if (el.style.display === 'none') { return } if (el.id === 'base' || el.parentElement.id ==='base') { oneTable.push(item) return } queryEle(el.parentElement) } queryEle(item) }) if (!oneTable.length && !towTable.length) { alert('递归过滤失败,请重试!!') return } const result = {} const splitTemp = { 1: '<br>', 2: '/n', 3: '/n/r', 4: '<p>', 5: '<span>' } const oneTables = {} // 一级表头数据 const towTables = {} // 二级表头数据 oneTable.forEach((oneItem, oneIndex) => { const text = oneItem.querySelectorAll('.text') oneTables[`${oneIndex}`] = [] text.forEach((tItem, tIndex) => { const spanText = tItem.querySelectorAll('span') let html = '' if (spanText.length > 1) { html = `${spanText[0].innerHTML} <br> ${spanText[1].innerHTML}` } else { html = spanText[0] && spanText[0].innerHTML ? spanText[0].innerHTML : '' } if (!oneTables[`${oneIndex}`].includes(html) && !!html) { oneTables[`${oneIndex}`].push(html) } }) if (JSON.stringify(oneTables) !== '{}') { Object.keys(oneTables).forEach((tableIndex) => { createTable(oneTables, tableIndex) }) } if (towTable.length) { setTowTable(oneIndex) } }) // 设置二级表头数据 function setTowTable (oneIndex) { towTable.forEach((towItem, towIndex) => { const panelStateContent = towItem.querySelectorAll('.ax_default') panelStateContent.forEach((pItem, pIndex) => { if (!towTables[`${oneIndex}-${towIndex}`]) { towTables[`${oneIndex}-${towIndex}`] = [] } const text = pItem.querySelectorAll('.text') text.forEach(tItem => { const spanText = tItem.querySelectorAll('span') let html = '' if (spanText.length > 1) { html = `${spanText[0].innerHTML} <br> ${spanText[1].innerHTML}` } else { html = spanText[0].innerHTML } if (pItem.dataset.label === '二级表头上' ) { // 只有一个的是二级表头的顶部头 if(!towTables[`${oneIndex}-${towIndex}`].includes(`title-${html}`)) { towTables[`${oneIndex}-${towIndex}`].push(`title-${html}`) } } else { if(!towTables[`${oneIndex}-${towIndex}`].includes(html) && !towTables[`${oneIndex}-${towIndex}`].includes(`title-${html}`)) { towTables[`${oneIndex}-${towIndex}`].push(html) } } }) }) }) if (JSON.stringify(towTables) !== '{}') { Object.keys(towTables).forEach((item) => { createTable(towTables, item) }) mergeResult(result) } } // 页面多个表格数据进行数据合并 function mergeResult (arr) { const indexs = [...new Set(Object.keys(arr).map(v => v.split('-')[0]))] Object.keys(arr).forEach(item => { if (item.includes('-')) { // 处理 二级表child const child = arr[item].find(fItem => fItem.label.includes('title-')) child["children"] = [] arr[item].forEach(aItem => { if (!aItem.label.includes('title-')) { child.children.push(aItem) } }) child.label = child.label.replaceAll('title-', '') arr[item] = child } }) indexs.forEach(item => { Object.keys(arr).forEach((aItem, index) => { if (arr[item]) { if (arr[`${item}-${index}`]) { arr[item].push(arr[`${item}-${index}`]) delete arr[`${item}-${index}`] } } }) resetTable(arr[item]) }) } // 重置二级表头宽度 function resetTable (arr) { arr.forEach(item => { const isSortKeys = item.sortKeys && item.sortKeys.length ? true : false if (item.children && item.children.length) { item.children.forEach(cItem => { const isSortKeys = cItem.sortKeys && cItem.sortKeys.length ? true : false cItem.width = Math.ceil(getLabelWidth({label: cItem.label, labelSplit: cItem.labelSplit||0, isSortKeys})) }) return } item.width = Math.ceil(getLabelWidth({label: item.label, labelSplit: item.labelSplit||0, isSortKeys})) }) } // 生成表格数据 function createTable (arr, tableIndex) { arr[tableIndex].forEach((item, index) => { if (['序号', '操作'].includes(item)) { return } const labelMark = getIsLine(item) let isLine = {} const key = `todo_key${index}_${Math.floor(Math.random() * (9999999 - 1)) + 1}` const dataTemp = { "label": getLabel(item, labelMark), key, "show": true, "width": '', } if (item.includes('◥') || item.includes('↑') || item.includes('△')|| item.includes('▼') || item.includes('^')) { dataTemp.sortKeys = [key, key] } if (labelMark !== -1) { isLine = { // 是否换行 "safeWidth": false, "labelSplit": getLabelSplit(item, labelMark) } } if (!result[tableIndex] || !result[tableIndex].length) { result[tableIndex] = [] } if (dataTemp.label.includes('时间')) { dataTemp.filter = 'minute' } if (dataTemp.label.includes('日期')) { dataTemp.filter = 'date' } dataTemp.width = Math.ceil(getLabelWidth({label: dataTemp.label, labelSplit: dataTemp.labelSplit||0, isSortKeys: labelMark!== -1})) const inLabel = result[tableIndex].find(v => v.label === dataTemp.label) if (!inLabel) { result[tableIndex].push({ ...dataTemp, ...isLine }) } }) } // 获取表头宽度 function getLabelWidth ({ label, labelSplit, isSortKeys }) { const topTextWidth = computeFontSize(label.slice(0, labelSplit), isSortKeys) const btmTextWidth = computeFontSize(label.slice(labelSplit), isSortKeys) return Math.max(topTextWidth, btmTextWidth) } // canvas计算字体宽度 function computeFontSize (str, isSortKeys) { if (!str) { return 0 } const font = "normal 12px Microsoft YaHei" const canvas = document.createElement("canvas") const context = canvas.getContext("2d") context.font = font const width = context.measureText(str).width + (isSortKeys? 8 : 0) return parseInt(width, 10) + 16 } // 表头换行数据计算 function getLabelSplit (lable, labelMark) { return lable.indexOf(splitTemp[labelMark]) !== -1 ? lable.indexOf(splitTemp[labelMark]) - 1 : 0 } // 表头label特殊字符截取 function getLabel (lable, labelMark) { switch(labelMark) { case 4 : return lable.replaceAll('<p>', '').replaceAll('</p>', '').replaceAll('◥', '').replaceAll('▼', '').replaceAll('△', '').replaceAll('↑', '').replaceAll(' ', '').replaceAll('^', '').replaceAll(' ', '') case 5: return lable.replaceAll('<span>', '').replaceAll('</span>', '').replaceAll('◥', '').replaceAll('▼', '').replaceAll('△', '').replaceAll('↑', '').replaceAll(' ', '').replaceAll('^', '').replaceAll(' ', '') default: return lable.replaceAll(splitTemp[labelMark], '').replaceAll('◥', '').replaceAll('▼', '').replaceAll('↑', '').replaceAll('△', '').replaceAll('^', '').replaceAll(' ', '').replaceAll(' ', '') } } // 获取label是否换行特殊标识,需要优化 function getIsLine (lable) { if (lable.includes('<br>')) { return 1 } if (lable.includes('/n')) { return 2 } if (lable.includes('/n/r')) { return 3 } if (lable.includes('<p>')) { return 4 } if (lable.includes('<span>')) { return 5 } return -1 } if (isLog) { console.group('===== 通过原型获取通用查询列表配置 strat=====') console.log('=======>>>> 自定义列配置结果值,自动复制到剪切板', result) console.log('=======>>>> 一级表头数据', oneTables) console.log('=======>>>> 二级表头数据', towTables) copyLink(JSON.stringify(result)) console.groupEnd() } return result }, // 通过原型后端获取通用查询列表配置 async getPrototypeBackEndQueryTablePage() { let tableData = await funcs.getPrototypeQueryTablePage(false) const { host } = window.location let code // 未匹配成功的字段 const errKeys = [] if (host.includes('img02.mockplus.cn')) { code = prompt('输入后端请求code') } if (!code && host.includes('img02.mockplus.cn')) { alert('接口code不能为空') return } // tosreport.intercityMonitor.mainRealTime const apiData = await fetch(`https://coo-generator-uat.kyslb.com/query?generatorArg=${code}`) const res = await apiData.json() if (!res || !res.length) { alert(`code ===> ${code} 为空,请联系后端是否在开放平台配置`) return } // tableData[0] Object.keys(tableData).forEach(item => { let apiKey = res.find(v => v.label === tableData[item].label) if (apiKey) { tableData[item].key = apiKey.key } else { errKeys.push(tableData[item].label) } if (tableData[item].children && tableData[item].children.length) { tableData[item].children.forEach(cItem => { let cApiKey = res.find(v => v.label === cItem.label) if (cApiKey) { cItem.key = cApiKey.key } else { errKeys.push(cItem.label) } }) } }) console.group('===== 通过原型后端获取通用查询列表配置 strat=====') console.log('=======>>>> 自定义列配置,自动复制到剪切板', tableData) console.log('=======>>>> 开放平台返回code值', res) console.log('=======>>>> 匹配的code', code) console.log('=======>>>> 匹配失败的字段') console.table({...errKeys}) copyLink(JSON.stringify(tableData)) console.groupEnd() }, // 获取自定义查询配置 getQueryTableFormPage() { const { formFields } = document.querySelector('.query-table-container').__vue__ const result = {} formFields.forEach(item => { result[item.label] = `${item.propertyName} ${item.lookupCode ? '===>> 字典:' + item.lookupCode + ' 字典值:'+ JSON.stringify(item.options) : ''}` }) console.group('=====获取自定义查询配置=====') copyLink(JSON.stringify(result)) console.table(result) console.groupEnd() }, // 通过自定义匹配开放平台key tosreport.deliveryTime.getPickupTrunkList async queryTableColumnSetKey() { let code = prompt('输入后端请求code') if (!code) { return } // 未匹配成功的字段 const errKeys = [] const { tables } = document.querySelector('.query-table-container').__vue__ const tablesData = _.cloneDeep(tables) if (!tablesData || !tablesData.length) { alert('请先配置query-table自定义列') return } const table = tablesData.find(v => v.url.method === code) if (!table) { alert('自定义列method配置与输入code不一致') return } const apiData = await fetch(`https://coo-generator-uat.kyslb.com/query?generatorArg=${code}`) const res = await apiData.json() const result = table.columns.map(item => { const fixedKey = {'组织': {key: 'departName'}, '负责人': {key: 'chargeName'}} const apiData = res.find(v => v.label === item.label || v.label.includes(item.label)) || fixedKey[item.label] const c = {key: item.key, label:item.label, show:true, width: item.width} if (!apiData) { errKeys.push(item.label) if (item.fixed) { c.fixed = item.fixed } if (item.sortKeys && item.sortKeys.length) { c.sortKeys = item.sortKeys } if (item.children && item.children.length) { c.children = setChildren(item.children, item) } if (item.labelSplit) { c.safeWidth = item.safeWidth c.labelSplit = item.labelSplit } if (item.filter) { c.filter = item.filter } return c } if (item.fixed) { c.fixed = item.fixed } c.key = apiData.key || item.key if (item.sortKeys && item.sortKeys.length) { c.sortKeys = [apiData.key || item.key, apiData.key || item.key] } if (item.children && item.children.length) { c.children = setChildren(item.children, item) } if (item.labelSplit) { c.safeWidth = item.safeWidth c.labelSplit = item.labelSplit } if (item.filter) { c.filter = item.filter } return c }) function setChildren (children, item) { return children.map(cItem => { const apiKeyChildreb = res.find(v => v.label === `${item.label}-${cItem.label}` || v.label.includes(`${item.label}-${cItem.label}`)) const c1 = {key: cItem.key, label:cItem.label, show:true, width: cItem.width} if (!apiKeyChildreb) { errKeys.push(`${item.label}-${cItem.label}`) if (item.fixed) { c1.fixed = item.fixed } if (item.sortKeys && item.sortKeys.length) { c1.sortKeys = item.sortKeys } if (item.labelSplit) { c1.safeWidth = item.safeWidth c1.labelSplit = item.labelSplit } if (item.filter) { c1.filter = item.filter } return c1 } if (item.fixed) { c1.fixed = item.fixed } if (item.labelSplit) { c1.safeWidth = item.safeWidth c1.labelSplit = item.labelSplit } c1.key = apiKeyChildreb.key || item.key if (item.sortKeys && item.sortKeys.length) { c1.sortKeys = apiData.key || item.key } if (item.filter) { c1.filter = item.filter } return c1 }) } console.group('===== 通过自定义匹配开放平台key strat=====') console.log('=======>>>> 自定义列配置,自动复制到剪切板', result) console.log('=======>>>> 开放平台返回code值', res) console.log('=======>>>> 匹配的code', code) console.log('=======>>>> 匹配失败的字段') console.table({...errKeys}) copyLink(JSON.stringify(result)) console.groupEnd() }, // 拖动自定义列修改宽度获取修改后配置 draggingQueryTableSetWindth() { const { tables,$route } = document.querySelector('.query-table-container').__vue__ const tablesData = _.cloneDeep(tables) const result = {} if (!tables || !tables.length) { alert('请先配置query-table自定义列') return } const globalColWidth= JSON.parse(localStorage.getItem('GLOBAL_COL_WIDTH')) if (!globalColWidth) { alert('请先配置拖动自定义列') return } const { tag } = $route.meta tables.forEach(item => { const data = globalColWidth[`${tag}?${item.searchCode}`] if (data) { item.columns.forEach(cItem => { const globalColWidthKes = Object.keys(data) if(globalColWidthKes.includes(cItem.key)) { cItem.width = Math.ceil(data[cItem.key]) } if (cItem.children && cItem.children.length) { delete cItem.width cItem.children.forEach(chItem => { if(globalColWidthKes.includes(chItem.key)) { chItem.width = Math.ceil(data[chItem.key]) } }) } }) } result[`${item.option.label}==>>${item.searchCode}`] = item.columns.map(mItem => { const c = {key: mItem.key, label:mItem.label, show:true, width: mItem.width} if (mItem.children && mItem.children.length) { c.children = mItem.children.map(v => { const c = {key: v.key, label:v.label, show:true, width: v.width} if (v.sortKeys) { c.sortKeys = [v.sortKeys[0], v.sortKeys[1]] } if (v.fixed) { c.fixed = v.fixed } if (v.labelSplit) { c.safeWidth = v.safeWidth c.labelSplit = v.labelSplit } if (v.filter) { c.filter = v.filter } return c }) } if (mItem.sortKeys) { c.sortKeys = [mItem.sortKeys[0], mItem.sortKeys[1]] } if (mItem.filter) { c.filter = mItem.filter } if (mItem.labelSplit) { c.safeWidth = mItem.safeWidth c.labelSplit = mItem.labelSplit } if (mItem.fixed) { c.fixed = mItem.fixed } return c }) }) console.group('===== 拖动自定义列修改宽度获取修改后配置 strat=====') console.log('=======>>>> 自定义列配置,自动复制到剪切板', result) copyLink(JSON.stringify(result)) console.groupEnd() }, } //拷贝到剪切板 function copyLink (value) { if (window.clipboardData) { window.clipboardData.clearData() window.clipboardData.setData('text', value) } else if (document.execCommand) { const element = document.createElement('SPAN') element.innerHTML = value document.body.appendChild(element) if (document.selection) { const range = document.body.createTextRange() range.moveToElementText(element) range.select() } else if (window.getSelection) { const range = document.createRange() range.selectNode(element) window.getSelection().removeAllRanges() window.getSelection().addRange(range) } document.execCommand('copy') element.remove ? element.remove() : element.removeNode(true) } } const div = document.createElement('div') const html = ` <style> #__js { position: relative; height: 50px; line-height: 50px; color: #fff; } #__js> .conent-item { cursor: pointer; transform-origin: center bottom; position: absolute; top: 36px; left: 0; z-index: 99999999; padding: 10px 0; margin: 5px 0; background-color: #fff; border: 1px solid #ebeef5; border-radius: 4px; box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%); display: none; } #__js> .conent-item > .item { line-height: 36px; padding: 0 20px; margin: 0; font-size: 14px; color: #606266; cursor: pointer; outline: none; white-space: nowrap; } #__js> .conent-item > .item:hover { background: #e9f3ff; color: #65adff; } #__js> .conent-item:after { content: ''; width: 0; height: 0; border-left: 10px solid transparent; border-right: 10px solid transparent; border-bottom: 10px solid #fff; position: absolute; top: -10px; left: 10px; } #__js:hover .conent-item { display: block !important; color: red !important; } </style> <div id="__js"> <span style="color:#fe4066">开发脚本</span> <div class="conent-item"> ${actions.map(item => `<div class="item" data-id="${item.func}">${item.name}</div>`)} </div> </div> `.replaceAll(',', '') div.style = div.style = "position:fixed;right: 528px;top: 0px;z-index: 999999999;" div.innerHTML = html div.addEventListener('click', (e) => { const dataId = e.target.getAttribute('data-id') if (dataId) { try { funcs[dataId]() } catch (e) { console.error(e) alert('报错了,请前往控制台查看') } } }) document.body.appendChild(div) })();