koishijs / koishi

Cross-platform chatbot framework made with love
https://koishi.chat
MIT License
4.48k stars 245 forks source link

Feature: `schema` 中增加 `MultiSelect` #1167

Closed ChengTu-Lazy closed 11 months ago

ChengTu-Lazy commented 1 year ago

Describe the problem related to the feature request

在配置构型中新增一个类似于位集的复选框的数据模型,选择完成之后生成一个数组

Describe the solution you'd like

schema 中新增一个复选框的数据模型MultiSelect

配置方法:


const Intents = [ "FOO ": “a” , "BAR ": “b” , “QUX” : "c" ] 

export default Schema.object({
  intents: Schema.MultiSelect(Intents)
    .default( [ "FOO " , "BAR" ] ),
})

展示样式:

image

输出样式:


[ "a" , "b" ]

Describe alternatives you've considered

No response

Additional context

提这个需求主要是为了方便插件配置,比如现在要实现我提出的这种需求的话,我得用这么一串来实现复选框的功能,并且是手动创建的多选情况

export default Schema.object({
  DefaultPlatform: Schema.array(Schema.union([
    Schema.const('Steam').description('Steam'),
    Schema.const('Rail').description('WeGame'),
    Schema.const('Switch').description('Switch'),
    Schema.const('PSN').description('PlayStation'),
    Schema.const('XBone').description('Xbox'),
  ])).role('table').default(["Steam","Rail"]).description('设置默认查询的游戏平台,可以多选,但是多选会拖慢查询速度哦'),
})

效果图: image

image

如果有复选框的话,就会是这样的:


export const PlatformList = [ “Steam”:"Steam" , "WeGame" : "Rail" , "Switch":"Switch" , "PlayStation" : "PSN" , "Xbox" : "XBone" ]

export default Schema.object({
  DefaultPlatform: Schema.MultiSelect(PlatformList).default(["Steam"]).description('设置默认查询的游戏平台,可以多选,但是多选会拖慢查询速度哦'
})

效果图: image 这样用户体验更好,也更直观

ilharp commented 1 year ago

Schemastery 已有 Bitset,其样式正是你所希望的复选框。Bitset 有哪些地方不满足你的需求吗?

你是希望有其他提升易用性的改进,比如数组的输入和输出吗?是否有考虑使用工具函数进行简单转换以实现需求?

举个例子:

import { Context, Schema } from 'koishi'
import { o2b, b2a } from './utils'

export const name = 'aa'

const platformList = { Steam: 'Steam', PlayStation: 'PSN' } as const

export interface Config {
  platforms: number
}

export const Config: Schema<Config> = Schema.object({
  platforms: Schema.bitset(o2b(platformList)),
})

export function apply(ctx: Context, config: Config) {
  ctx.command('aa').action(() => b2a(platformList)(config.platforms).join())
}

image

设计 o2bb2a 两个函数即可完成你需要的转换。两个函数的实现如下:


export const o2b = <K extends string, V extends string>(x: Record<K, V>) =>
  Object.keys(x).reduce<[number, Record<K, number>]>(
    (a, c) => [a[0] << 1, { ...a[1], [c]: a[0] }],
    [1, {} as Record<K, number>]
  )[1]

export const b2a =
  <K extends string, V extends string>(x: Record<K, V>) =>
  (v: number) =>
    v
      .toString(2)
      .split('')
      .reverse()
      .map(Number)
      .map(Boolean)
      .map((y, i) => (y ? x[Object.keys(x)[i]] : undefined))
      .filter(Boolean)
ChengTu-Lazy commented 1 year ago

Schemastery 已有 Bitset,其样式正是你所希望的复选框。Bitset 有哪些地方不满足你的需求吗?

你是希望有其他提升易用性的改进,比如数组的输入和输出吗?是否有考虑使用工具函数进行简单转换以实现需求?

举个例子:

import { Context, Schema } from 'koishi'
import { o2b, b2a } from './utils'

export const name = 'aa'

const platformList = { Steam: 'Steam', PlayStation: 'PSN' } as const

export interface Config {
  platforms: number
}

export const Config: Schema<Config> = Schema.object({
  platforms: Schema.bitset(o2b(platformList)),
})

export function apply(ctx: Context, config: Config) {
  ctx.command('aa').action(() => b2a(platformList)(config.platforms).join())
}

image

设计 o2bb2a 两个函数即可完成你需要的转换。两个函数的实现如下:

export const o2b = <K extends string, V extends string>(x: Record<K, V>) =>
  Object.keys(x).reduce<[number, Record<K, number>]>(
    (a, c) => [a[0] << 1, { ...a[1], [c]: a[0] }],
    [1, {} as Record<K, number>]
  )[1]

export const b2a =
  <K extends string, V extends string>(x: Record<K, V>) =>
  (v: number) =>
    v
      .toString(2)
      .split('')
      .reverse()
      .map(Number)
      .map(Boolean)
      .map((y, i) => (y ? x[Object.keys(x)[i]] : undefined))
      .filter(Boolean)

其实是我不会这里的位集的用法,你提供的这个确实是蛮好的替代,但是对于开发者来说还是直接封装个多选更加易用且直观,就当作是一个易用性改进吧,毕竟有了单选框了,多个多选也是很合理的嘛

shigma commented 11 months ago

已经支持相关特性。

image