您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
接口文档=>ts代码,不能完美转化,但是还比较好用
// ==UserScript== // @name 接口文档=>ts代码 // @namespace http://tampermonkey.net/ // @version 0.1.11 // @description 接口文档=>ts代码,不能完美转化,但是还比较好用 // @author fangxianli // @match https://km.sankuai.com/* // @icon https://www.google.com/s2/favicons?domain=undefined. // @grant none // ==/UserScript== (function() { 'use strict'; const InterfaceModel = { // 字段名 names: [/名称/, /参数/, /字段/, /属性/, /名字/, /name/i, /key/i], // 类型 types: [/类型/, /type/i], // 含义 means: [/含义/, /意义/, /意思/, /描述/, /解释/, /说明/, /备注/, /返回/, /mean/i], // 例子 examples: [/示例/, /例子/, /举例/, /eg/i, /example/i] } // TODO: 做成可配置的 const TYPE_MAP = [ { name: 'number', match: [ /number/i, /int/i, /数字/, /integer/i, /float/i, /时间/, /date/i, /整数/, /整型/, // TODO: 长整型到底用不用字符串🤔,float 和 double 用字符串不 /长整型/, /long/i, /float/i, /double/i, ] }, { name: 'string', match: [ /string/i, /字符/, /字符串/, ] }, { name: 'boolean', match: [ /Boolean/i, /布尔/, ] }, { name: 'array', match: [ /list<(\w+)>/i ] }, { name: 'object', match: [ /(\w+)/ ] } ] /** * 获取之前最邻近的文本内容 * @param {Element} $currentNode */ const findPrevText = ($currentNode) => { if (!$currentNode) return if ($currentNode?.previousElementSibling?.innerText) return $currentNode?.previousElementSibling?.innerText return findPrevText($currentNode.parentElement) } /** * 深度优先遍历节点,获取树上最深的第一个文本节点 * @param {Element} node */ const dfsFindText = (node) => { if (!node?.innerText) return if (!node?.children.length) return node.innerText for (let i = 0; i < node.children.length; i++) { const text = dfsFindText(node.children[i]) if (text !== undefined) return text } } /** * * @param {Element} element */ const getContent = (element) => { if (!element) return const tableInTD = element.querySelectorAll('table')[0] if (tableInTD) return findPrevText(tableInTD) return element?.innerText?.trim() } const testInRegExpArray = (testStr, regExpArr = []) => regExpArr.some(regExp => regExp.test(testStr)) /** * 从表头里获取对应字段的位置 * @param {String} name 字段名 * @param {Element[]} headers 表头 */ const getPropsIndex = (name, headers) => headers.findIndex(header => testInRegExpArray(header.innerText, InterfaceModel[name])) /** * 将 table 中的数据转化为能够被识别的类型 * @param {Element} table */ const convertTable2Map = (table) => { const prevText = findPrevText(table) || '' const charStringArray = prevText?.match(/\w+/g) const interfaceName = charStringArray ? charStringArray.join('-') : 'UnknownInterface' const typeModel = { name: interfaceName, defineList: [], table } const headers = [...table.querySelectorAll('th')] const nameIndex = getPropsIndex('names', headers) const typeIndex = getPropsIndex('types', headers) const meanIndex = getPropsIndex('means', headers) const exampleIndex = getPropsIndex('examples', headers) // 找到第一个 display 不为 none 的 body,即为数据表 const targetTable = [...table.querySelectorAll('tbody')].find(tbody => tbody.style.display !== 'none') // 去除表头 const rows = [...targetTable.children] rows.splice(0, 1) const defineList = rows.map((row) => { return { name: getContent(row.children[nameIndex]), type: getContent(row.children[typeIndex]), mean: getContent(row.children[meanIndex]), example: getContent(row.children[exampleIndex]), }}) typeModel.defineList = defineList return typeModel } const addCommentPrefix = str => ` * ${str}` const filterEmpty = arr => arr?.filter(x => x) const preprocessCode2Arr = str => filterEmpty(str?.split('\n') || []) const getComment = ({mean, example}) => { if (!mean && !example) return [] const meanArr = preprocessCode2Arr(mean).map(addCommentPrefix) || [] const exampleArr = preprocessCode2Arr(example).map(addCommentPrefix) || [] if (exampleArr.length) { exampleArr.unshift(' * @example', ' * 示例:') } const comment = [ `/**`, ...meanArr, ...exampleArr, ` */` ] return comment } const getTSTypeStr = (type) => { if (!type) return 'unknown' const tsTypeDesc = TYPE_MAP.find(typeMap => { return testInRegExpArray(type, typeMap.match) }) const directReturnArr = ['number', 'string', 'boolean'] const canDirectReturnName = directReturnArr.includes(tsTypeDesc?.name) const needMatch = tsTypeDesc?.name === 'array' || tsTypeDesc?.name === 'object' if (canDirectReturnName) return tsTypeDesc?.name if (needMatch) { for (const match of tsTypeDesc.match) { const matchResult = type.match(match) if (!matchResult?.[1]) continue if (tsTypeDesc.name === 'array') return matchResult[1] + '[]' return matchResult[1] } } return 'unknown' } const getNameDefine = ({name, type}) => { const typeStr = getTSTypeStr(type) return [`${name}: ${typeStr}`] } const addTab = str => ` ${str}` const convertData2Code = ({name, defineList}) => { const getCode = (defineItem) => { return [...getComment(defineItem), ...getNameDefine(defineItem)] } const interfaceDefine = [ `interface ${name} {`, ...defineList.map(getCode).flat(Infinity).map(addTab), `}`, '' ] return interfaceDefine } const getCode = () => { const allTables = document.querySelectorAll('table') /** * 筛选出接口定义的table */ const defineTable = [...allTables].filter(table => { const headers = [...(table.querySelector('tr')?.children || [])] const tableRows = table.querySelectorAll('tr') const hasNameTableHeader = headers.some(header => testInRegExpArray(header.innerText, InterfaceModel.names)) return hasNameTableHeader && tableRows.length > 1 }) const defineDataList = defineTable.map(convertTable2Map) console.log({defineDataList}) const codeArray = defineDataList.map(convertData2Code) console.log(codeArray) return codeArray } const copyCode = () => { const codeArray = getCode() const textArea = document.createElement('textarea') textArea.value = codeArray.flat(Infinity).join('\n') document.body.append(textArea) textArea.select() document.execCommand('copy') // document.body.remove(textArea) textArea.style.display = 'none' } const logShortKey = () => console.log("%c 试试快捷键[ ⇧ + ⌃ + ⌥ + j] (shift + ctrl + option + j) 将接口文档转化成 ts 代码吧", `color: #333; font-size: 16px; background-image: linear-gradient(to right, #4facfe, #00f2fe); padding: 4px; border-radius: 20px;` ) const logFeedback = () => console.log("%c 使用中遇到问题请点击右侧链接联系: https://x.sankuai.com/bridge/chat?uid=1833639275", `color: #333; font-size: 16px; background-image: linear-gradient(to right, #4facfe, #00f2fe); padding: 4px; border-radius: 20px;` ) setTimeout(() => { logShortKey() logFeedback() }, 1000) document.addEventListener('keypress', (event) => { if (event.code === 'KeyJ' && event.shiftKey && event.altKey && event.ctrlKey) { try { copyCode() alert('转换代码复制成功,快去粘贴吧') } catch (err) { console.error(err) } logFeedback() } }) // Your code here... })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址