yapi-gen-code - seevin.com

2024/12/29 14:44:23

Verzia zo dňa 30.12.2024. Pozri najnovšiu verziu.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

// ==UserScript==
// @name        yapi-gen-code - seevin.com
// @namespace   Violentmonkey Scripts
// @match       *://api.seevin.com/*
// @grant       GM_setClipboard
// @version     1.0
// @author      Gorvey
// @description 2024/12/29 14:44:23
// @run-at       document-start
// @require      https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/highlight.min.js
// @resource css https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/styles/default.min.css
// @license MIT
// ==/UserScript==

abduct()

function abduct() {
  const xhrOPen = XMLHttpRequest.prototype.open

  XMLHttpRequest.prototype.open = function () {
    const xhr = this

    if (arguments[1].includes('/api/interface/get?id=')) {
      const getterText = Object.getOwnPropertyDescriptor(
        XMLHttpRequest.prototype,
        'responseText'
      ).get

      Object.defineProperty(xhr, 'responseText', {
        get: () => {
          let result = getterText.call(xhr)
          setTimeout(() => {
            sessionStorage.setItem('result', result)
            renderInDocument()
          }, 500)
          return result
        },
      })
    }

    return xhrOPen.apply(xhr, arguments)
  }
}

function insertAfter(newNode, existingNode) {
  existingNode.parentNode.insertBefore(newNode, existingNode.nextSibling)
}

window.renderInDocument = renderInDocument

const createCopyButton = ({ data }) => {
  const button = document.createElement('button')
  button.textContent = '复制接口定义'
  button.className = 'copy-interface-button'
  button.style.cssText = `
    padding: 5px 10px;
    cursor: pointer;
    background: #f0f0f0;
    border: 1px solid #ccc;
    border-radius: 4px;
    z-index: 9999;
  `

  let resBody = JSON.parse(data.res_body)
  const interfaces = generateTypeScriptInterfaces(resBody)
  console.log(data, resBody, interfaces)
  button.addEventListener('click', async () => {
    if (!interfaces) {
      return window.alert('接口未设置返回数据')
    }
    const updateButtonText = (text, duration = 2000) => {
      button.textContent = text
      setTimeout(() => {
        button.textContent = '复制接口定义'
      }, duration)
    }

    try {
      GM_setClipboard(interfaces)
      updateButtonText('复制成功!')
    } catch (err) {
      updateButtonText('复制失败')
      console.error('复制出错:', err)
    }
  })

  return button
}


const generateTypeScriptInterfaces = (schema, parentName = '') => {
  let result = ''
  let subInterfaces = ''

  if (schema.type === 'object') {
    const interfaceName = parentName || 'ResponseData'
    result += `interface ${interfaceName} {\n`

    for (const [key, prop] of Object.entries(schema.properties)) {
      // 移除对 code 和 msg 的跳过,因为这些也是接口的一部分
      const description = prop.description ?
        `  /** ${prop.description} */\n` : ''

      if (prop.type === 'object') {
        const newInterfaceName = `${interfaceName}${capitalize(key)}`
        const { main, sub } = generateNestedInterfaces(prop, newInterfaceName)
        subInterfaces += sub
        result += `${description}  ${key}: ${newInterfaceName}\n`
      } else if (prop.type === 'array') {
        if (prop.items.type === 'object') {
          const newInterfaceName = `${interfaceName}${capitalize(key)}Item`
          const { main, sub } = generateNestedInterfaces(prop.items, newInterfaceName)
          subInterfaces += sub
          result += `${description}  ${key}: ${newInterfaceName}[]\n`
        } else {
          result += `${description}  ${key}: ${getTsType(prop.items)}[]\n`
        }
      } else {
        result += `${description}  ${key}: ${getTsType(prop)}\n`
      }
    }

    result += '}\n\n'
  }

  return subInterfaces + result
}

const generateNestedInterfaces = (schema, interfaceName) => {
  let main = ''
  let sub = ''

  if (schema.type === 'object') {
    main += `interface ${interfaceName} {\n`

    for (const [key, prop] of Object.entries(schema.properties || {})) {
      const description = prop.description ?
        `  /** ${prop.description} */\n` : ''

      if (prop.type === 'object') {
        const newInterfaceName = `${interfaceName}${capitalize(key)}`
        const { main: nestedMain, sub: nestedSub } = generateNestedInterfaces(prop, newInterfaceName)
        sub += nestedSub
        main += `${description}  ${key}: ${newInterfaceName}\n`
      } else if (prop.type === 'array') {
        if (prop.items && prop.items.type === 'object') {
          const newInterfaceName = `${interfaceName}${capitalize(key)}Item`
          const { main: nestedMain, sub: nestedSub } = generateNestedInterfaces(prop.items, newInterfaceName)
          sub += nestedSub
          main += `${description}  ${key}: ${newInterfaceName}[]\n`
        } else {
          main += `${description}  ${key}: ${getTsType(prop.items)}[]\n`
        }
      } else {
        main += `${description}  ${key}: ${getTsType(prop)}\n`
      }
    }

    main += '}\n\n'
  }

  return { main, sub: sub + main }
}


const getTsType = (prop) => {
  const typeMap = {
    'integer': 'number',
    'string': 'string',
    'boolean': 'boolean',
    'number': 'number'
  }
  return typeMap[prop.type] || 'any'
}

const capitalize = (str) => {
  return str.charAt(0).toUpperCase() + str.slice(1)
}

function renderInDocument(section = '') {
  if (section) {
    sessionStorage.setItem('api-section', section)
  } else {
    section = sessionStorage.getItem('api-section')
  }
  let result = JSON.parse(sessionStorage.getItem('result'))

  if (result.errcode != 0) return

  // 清除已存在的按钮
  document.querySelectorAll('.copy-interface-button').forEach(btn => btn.remove())

  let panelNode = document.querySelector('.panel-view')
  let node = document.querySelector('.caseContainer > div:nth-last-child(2) > .interface-title')
  const copyButton = createCopyButton(result)
  const copyButton2 = createCopyButton(result)
  insertAfter(copyButton, panelNode)
  insertAfter(copyButton2, node)
}