Such Music

JSON UI Schema 参考

Such 插件声明式 UI 定义完整指南

JSON UI Schema 参考

JSON UI Schema 是 Such 插件声明式 UI 的定义方式。通过 window.source.uiSchema 配置,宿主会自动渲染为 Naive UI 组件,无需手动编写 render 方法。

基本结构

interface PluginUISchema {
  title?: string          // 页面标题
  sections: UISection[]   // UI 区块列表
}

interface UISection {
  title?: string          // 区块标题
  description?: string    // 区块描述
  fields?: UIField[]      // 字段列表
  actions?: UIAction[]    // 操作按钮列表
}

一个 Schema 由多个 sections 组成,每个 section 可包含多个字段(fields)和操作按钮(actions)。

UIField 字段类型

input — 文本输入框

{
  type: 'input',
  key: 'decryptorPath',        // 对应插件 props 中的 key
  label: '解密器路径',           // 显示标签
  placeholder: '输入路径...',    // 占位符文本
  readonly: false,             // 是否只读(可选)
  action: 'browse'             // 关联的 emit 事件名(可选)
}

属性:

属性类型必填说明
type'input'字段类型
keystring对应 props 中的 key,值通过 setProps({ key: value }) 更新
labelstring输入框左侧标签
placeholderstring占位符文本
readonlyboolean设为 true 时输入框只读
actionstring关联的 emit 事件名,用户交互时触发 emit(action, value)

ℹ️ 设置 action: 'browse' 时,输入框右侧会显示"浏览"按钮,点击触发 emit('browse', currentValue)

text — 只读文本

{
  type: 'text',
  key: 'musicDir',
  label: '音乐输出目录'
}

显示只读文本,值通过 setProps({ musicDir: '...' }) 更新。

log — 多行日志

{
  type: 'log',
  key: 'outputLog'
}

多行日志输出区域,支持自动滚动。值通过 setProps({ outputLog: '日志内容' }) 更新,多次设置会替换整个内容。

tag — 状态标签

{
  type: 'tag',
  key: 'statusText'
}

显示小型状态标签,值通过 setProps({ statusText: '运行中' }) 更新。

UIAction 操作按钮

{
  type: 'button',
  label: '开始解密',           // 按钮文字
  method: 'startDecrypt',      // 点击时调用的插件方法名
  variant: 'primary'           // 按钮样式(可选)
}

variant 可选值:

variant说明适用场景
primary主色调按钮(绿色)主要操作
default默认样式次要操作
warning警告色(橙色)需谨慎的操作
error危险色(红色)破坏性操作

ℹ️ 点击按钮时,宿主会调用 window.source[method]()。如果方法返回 Promise,按钮会自动显示加载状态。

与 setProps 配合

UI Schema 中所有 field 的 key 都对应 props 中的一个属性。通过 this.setProps() 更新即可自动刷新界面:

initialization() {
  this.setProps({
    decryptorPath: '',
    ncmDir: '',
    musicDir: suchmusic.getSetting('musicFolder'),
    statusText: '就绪',
    outputLog: ''
  })
}

async startDecrypt() {
  this.setProps({ statusText: '解密中...' })
  // 执行解密逻辑...
  this.setProps({ statusText: '完成', outputLog: '解密成功' })
}

完整示例

以下是一个 NCM 解析插件的完整 UI Schema:

window.source = {
  id: 'SUCH_NETEASE_NCM_WIDGET',
  name: '网易云音乐 NCM 解析器',
  version: '1.0.0',
  author: 'enzymeym',
  isUIWidget: true,
  permissions: ['local_program', 'file_system', 'app_info'],

  uiSchema: {
    sections: [
      {
        title: '解密器设置',
        description: '配置外部解密工具',
        fields: [
          {
            type: 'input',
            key: 'decryptorPath',
            label: '解密器路径',
            placeholder: '例如: D:\\tools\\ncmdump-go.exe',
            action: 'browse'
          },
          {
            type: 'input',
            key: 'ncmDir',
            label: 'NCM 文件目录',
            placeholder: '选择包含 .ncm 文件的目录',
            readonly: true,
            action: 'browseDir'
          },
          {
            type: 'text',
            key: 'musicDir',
            label: '音乐输出目录'
          }
        ]
      },
      {
        title: '状态',
        fields: [
          { type: 'tag', key: 'decryptorStatusText' },
          { type: 'tag', key: 'statusText' }
        ]
      },
      {
        title: '操作',
        actions: [
          {
            type: 'button',
            label: '下载解密器',
            method: 'downloadDecryptor',
            variant: 'default'
          },
          {
            type: 'button',
            label: '检测解密器',
            method: 'checkDecryptorStatus'
          },
          {
            type: 'button',
            label: '开始解密',
            method: 'startDecrypt',
            variant: 'primary'
          }
        ]
      },
      {
        title: '输出日志',
        fields: [
          { type: 'log', key: 'outputLog' }
        ]
      }
    ]
  },

  initialization() {
    this.setProps({
      decryptorPath: '',
      ncmDir: '',
      musicDir: suchmusic.getSetting('musicFolder'),
      decryptorStatusText: '未检测',
      statusText: '就绪',
      outputLog: ''
    })
  },

  async startDecrypt() {
    this.setProps({ statusText: '解密中...' })
    // ... 解密逻辑
    this.setProps({ statusText: '完成', outputLog: '解密完成' })
  }
}

UI 布局说明

  • 多个 sections 从上到下排列
  • 每个 section 内 fields 在上、actions 在下
  • actions 按钮从左到右排列
  • log 字段占据整行宽度,高度自适应

On this page