Skip to content

UI定制相关接口

  • UI 元素有关的 API

界面(interface)

js
interface ISidebarTab {
    id: string // tab id
    viewId: string // 连接自定义视图 id
    name: string // tab 名称
    icon: string // tab icon
    activeIcon: string // 当前选中 tab 高亮的 icon
    order: number // 排序
    postCommand?: string // 自定义事件
    badgeCount?: string // 角标数量
}

interface ISidebarTabProvider {
    expandedTabCount?: number // 自定义展开的tab数量,超出数量自动合并到「更多」tab 里面
    getTabs?(tabs: ISidebarTab[]): ISidebarTab[]
}

interface IViewDisplayer {
    id: string // viewId,和 ISidebarTab.viewId 对应连接
    render(node: HTMLDivElement, props: object): IDisposable | Promise<IDisposable>
    renderOptions?: {
        keepAlive?: boolean // 类似 vue keep-alive
        keepIframeState?: boolean // 当自定义视图内部包含 iframe 时,可以保留 iframe 原有状态
    }
}

export interface ITabbarAction {
  // 唯一标识
  id: string;
  // 图标
  icon: string;
  // 按钮名称(tooltip提示文本)
  label?: string;
  // 点击触发的命令
  command: string;
}

export interface ITabbarMenuItemOptions {
  // 唯一标识
  id: string;
  // 图标
  icon: string;
  // 按钮名称
  label: string;
  // 点击触发的命令
  command: string;
}

export interface IThemeConf {
  // 主题背景色
  "theme.bg": string;
  // 侧边栏&顶栏卡片描边颜色
  "theme.border.color"?: string;
  // 设置面板选色预览颜色值
  "theme.thumbnail.color"?: string;
  // 设置面板选色预览勾选图标颜色值
  "theme.thumbnail.icon.color": string;

  // 侧边栏图标颜色
  "sidebar.icon.color": string;
  // 侧边栏图标选中状态颜色
  'sidebar.icon.color.active': string
  // 侧边栏导航文字颜色
  "sidebar.title.color": string;

  // 侧边栏企业账号文字颜色
  "sidebar.account.color": string;
  // 侧边栏企业账号hover状态文字颜色
  "sidebar.account.color.hover": string;
  // 侧边栏企业账号选中状态文字颜色
  "sidebar.account.color.active": string;
  // 侧边栏企业账号背景色
  "sidebar.account.bg": string;
  // 侧边栏企业账号hover状态背景色
  "sidebar.account.bg.hover": string;
  // 侧边栏企业账号选中状态背景色
  "sidebar.account.bg.active": string;

  // 侧边栏导航菜单hover状态背景色
  "sidebar.menu.bg.hover": string;
  // 侧边栏导航菜单选中状态背景色
  "sidebar.menu.bg.active": string;
  // 侧边栏导航菜单图标&文字透明度
  "sidebar.menu.opacity": string;
  // 侧边栏导航菜单选中状态图标&文字透明度
  "sidebar.menu.opacity.active": string;

  // 顶部导航栏tab背景色
  "tabbar.tab.bg": string;
  // 顶部导航栏tab hover状态背景色
  "tabbar.tab.bg.hover": string;
  // 顶部导航栏tab选中状态背景色
  "tabbar.tab.bg.active": string;

  // 顶部导航栏分割线颜色
  "tabbar.divider.color": string;

  // 顶部导航栏tab选中状态投影颜色
  "tabbar.tab.shadow.color.active"?: string;
  // 顶部导航栏tab闪烁动画投影颜色(预留)
  "tabbar.tab.shadow.color.animate"?: string;

  // 顶部导航栏tab标题文字颜色
  "tabbar.tab.title.color": string;
  // 顶部导航栏tab选中状态标题文字颜色
  "tabbar.tab.title.color.active": string;

  // 顶部导航栏tab默认图标颜色
  "tabbar.tab.icon.color": string;

  // 顶部导航栏tab关闭按钮颜色
  "tabbar.tab.button.color": string;
  // 顶部导航栏tab选中状态关闭按钮颜色
  "tabbar.tab.button.color.active": string;
  // 顶部导航栏tab关闭按钮hover背景色
  "tabbar.tab.button.bg.hover": string;
  // 顶部导航栏tab选中状态关闭按钮hover背景色
  "tabbar.tab.button.bg.hover.active": string;
}

export interface IThemeOptions {
  // 主题色唯一标识
  id: string;
  // 主题色名称
  label: string;
  // 主题色配置
  conf: IThemeConf;
}

export interface IMenuItem {
  id: string // 菜单项id
  icon?: string // 菜单图标
  label: string // 菜单项名
  command?: string // commandId
  viewletId: string // 视图单元 id
  when?: string // 控制菜单项的显隐, 不传时默认显示该菜单项
  order?: number // 排序
  children?: Pick<IMenuItem, 'id' | 'icon' | 'label' | 'command' | 'when' | 'order' | 'children'>[]
}

// 消息右键菜单command参数
export interface IMsgContextmenuCommandParams {
    msgid: number
    chatid: number
}

创建群聊(createGroupChat)

  • 支持环境:web
  • 调起通讯录选择器创建群聊
js
export function createGroupChat(): Promise<void>
  • 示例
js
ksxz.ui.createGroupChat()

注册侧边栏入口(registerSidebarTab)

  • 支持环境:web
  • 注册侧边栏 tab 入口
export function registerSidebarTab(tab: ISidebarTab): void
  • 示例
js
export const VIEW_ID = 'caixin.addressbook.view'

ksxz.ui.registerSidebarTab({
    id: 'caixin.addressbook.tab',
    viewId: VIEW_ID,
    name: '通讯录',
    icon: 'caixin-icon-tab-addressbook',
    activeIcon: 'caixin-icon-tab-addressbook-active',
    order: 100,
    postCommand: 'caixin.addressbook.tab.click'
})

更新侧边栏角标数量(updateSidebarTabBadgeCount)

  • 支持环境:web
  • 更新侧边栏角标数量
  • 示例
js
ksxz.ui.updateSidebarTabBadgeCount('caixin.addressbook.tab', 1)

注册侧边栏展示行为(registerSidebarTabProvider(实验api))

  • 支持环境:web
  • 注册侧边栏tab展示行为 provider
js
export function registerSidebarTabProvider(provider: ISidebarTabProvider): void
  • 示例
js
import ksxz from 'ksxz'

// 基座内置 tab id
export enum ESidebarTabId {
  MESSAGES = 'messages',
  APP = 'app',
  DOCS = 'docs',
  ADDRESS_BOOK = 'addressBook',
  PERSONAL = 'personal'
}

class SidebarTabProvider implements ksxz.ui.ISidebarTabProvider {
    // 控制展开的 tab,超出数量则收纳到更多 tab 栏目里面
    expandedTabCount = 4

    getTabs(tabs: ksxz.ui.ISidebarTab[]) {
      // 使用 filter 方式屏蔽内置的 tab
      let newTabs = tabs.slice()
      newTabs = newTabs.filter(
        (tab) =>
          // 隐藏“我的”tab
          tab.id !== ESidebarTabId.PERSONAL
      )
      
      // 修改默认行为
      newTabs = newTabs.map((tab) => {
        if (tab.id === ESidebarTab.MESSAGES) {
          // 修改 order 实现排序
          tab.order = 2.1
          // 修改 icon 实现自定义图标
          tab.icon = 'ksxz_web_ext-xxx'
          tab.activeIcon = 'ksxz_web_ext-xxx-active'
        }
        return tab
      })

      return newTabs
    }
 }

ksxz.ui.registerSidebarTabProvider(new SidebarTabProvider())

注册自定义视图(registerMainViewDisplayer)

  • 支持环境:web
  • 注册自定义视图,支持 url 地址的加载
js
export function registerViewDisplayer(viewType: string, displayer: IViewDisplayer): void
  • 自定义侧边栏对应主视图示例
js
const VIEW_ID = 'caixin.view'

// 注册侧边栏 tab
ksxz.ui.registerSidebarTab({
    id: 'caixin.addressbook.tab.a',
    viewId: VIEW_ID,
    name: '页面a',
    icon: 'caixin-icon-tab-addressbook',
    activeIcon: 'caixin-icon-tab-addressbook-active',
    order: 100
})

ksxz.ui.registerMainViewDisplayer({
    id: VIEW_ID,
    getWebviewUrl() {
      return '自定义 url 地址'
    }
})
  • 完整示例
js
class AddressBookViewDisplayer implements ksxz.ui.IViewDisplayer {
    private _comp: Vue | null = null

    id = VIEW_ID

    renderOptions = { keepIframeState: true }

    async render(node: HTMLDivElement, props: object) {
      const { default: vueComp } = await import('./View.vue')
      const CompCtor = Vue.extend(vueComp)
      this._comp = new CompCtor({
        propsData: props
      })
      this._comp.$mount()
      node.appendChild(this._comp.$el)

      return {
        dispose: () => {
          this._comp && this._comp.$destroy()
          this._comp = null
        }
      }
    }
  }

ksxz.ui.registerViewDisplayer('MainView', new AddressBookViewDisplayer())

顶部tab注册按钮(registerTabbarAction)

  • 支持环境:web
  • 在主窗口顶部导航栏注册一个图标按钮
js
export function registerTabbarAction(options: ITabbarAction): void
  • 示例
js
ksxz.ui.registerTabbarAction({
  id: 'caixin.tabbar.action1',
  label: '自定义图标1',
  icon: require('@/assets/logo.png'),
  command: 'caixin.command.alert1'
})

顶部tab栏注册下拉项(registerTabbarMenuItem)

  • 支持环境web
  • 在主窗口顶栏导航栏注册一个下拉菜单子项
js
export function registerTabbarMenuItem(options: ITabbarMenuItemOptions): void
  • 示例
js
ksxz.ui.registerTabbarMenuItem({
  id: 'caixin.tabbar.menu1',
  label: '创建群聊',
  icon: require('@/assets/logo.png'),
  command: 'caixin.command.createGroupChat'
})

注册自定义主题(registerTheme)

  • 支持环境:web
  • 注册一个自定义主题
js
export function registerTheme(options: IThemeOptions): void
  • 示例
js
ksxz.ui.registerTheme({
  id: 'caixin.theme.green',
  label: '孔雀绿',
  conf: {
    'theme.thumbnail.icon.color': 'var(--kd-color-icon-white)',
    'theme.bg': '#257355',

    'sidebar.icon.color': 'var(--kd-color-icon-white)',
    'sidebar.icon.color.active': 'var(--kd-color-icon-white)',
    'sidebar.title.color': 'var(--kd-color-icon-white)',
    'sidebar.account.color': 'rgba(255, 255, 255, 0.7)',
    'sidebar.account.color.hover': 'var(--kd-color-text-white)',
    'sidebar.account.color.active': 'var(--kd-color-text-white)',
    'sidebar.account.bg': 'rgba(255, 255, 255, 0.1)',
    'sidebar.account.bg.hover': 'rgba(255, 255, 255, 0.08)',
    'sidebar.account.bg.active': 'rgba(255, 255, 255, 0.1)',
    'sidebar.menu.bg.hover': 'rgba(255, 255, 255, 0.08)',
    'sidebar.menu.bg.active': 'rgba(255, 255, 255, 0.08)',
    'sidebar.menu.opacity': '0.7',
    'sidebar.menu.opacity.active': '1',

    'tabbar.tab.bg': 'transparent',
    'tabbar.tab.bg.hover': '#367e62',
    'tabbar.tab.bg.active': '#488970',
    'tabbar.divider.color': 'rgba(245, 245, 245, 0.14)',
    'tabbar.tab.title.color': 'var(--kd-color-text-white)',
    'tabbar.tab.title.color.active': 'var(--kd-color-text-white)',
    'tabbar.tab.icon.color': 'rgba(255, 255, 255, 0.6)',
    'tabbar.tab.button.color': 'rgba(255, 255, 255, 0.7)',
    'tabbar.tab.button.color.active': 'rgba(255, 255, 255, 0.7)',
    'tabbar.tab.button.bg.hover': 'rgba(255, 255, 255, 0.08)',
    'tabbar.tab.button.bg.hover.active': 'rgba(255, 255, 255, 0.16)'
  }
})

注册消息下拉菜单(registerMenuItem)

  • 支持环境:web
  • 注册菜单
js
export function registerMenuItem(menuItem: ksxz.ui.IMenuItem): void
  • 示例
js
const commandIdA = 'docs.msg.contextmenu.test'
  // 注册消息右键菜单项
  ksxz.ui.registerMenuItem({
    id: 'msg-contextmenu-test',// id自定义,需要全局唯一
    viewletId: 'msg-contextmenu', // 可参考下面的EViewletIds枚举
    label: '菜单项A',
    order: 30,
    command: commandIdA
  })

  // 注册点击事件的command
  ksxz.commands.registerCommand(
    commandIdA,
    async (params: ksxz.ui.IMsgContextmenuCommandParams) => {
      const { msgid, chatid } = params
      const message = await ksxz.im.getMessage({ msgid, chatid })
      console.log('消息数据体:', message)
    }
  )

  // 注册个人中心菜单项
  ksxz.ui.registerMenuItem({
    id: 'caixin-personal-center-invitation', // id自定义,需要全局唯一
    viewletId: 'personal-center', // 可参考下面的EViewletIds枚举
    label: '邀请',
    order: 2,
    command: 'ksxz.showInviteDialog' // 客户端内置的command
  })
  • 说明 如果需要控制菜单项的显隐状态,可以配置when属性。when属性需要传入一个字符串,基座会解析该字符串,用于控制该菜单的显隐。语法可以参考本文档的when clause contexts说明

  • viewletId :视图单元 id

js
// 视图单元 id, 支持通过插件扩展UI视图
export enum EViewletIds {
  // 以下视图单元id已向外部插件开放---------————
  MSG_CONTEXTMENU = 'msg-contextmenu', // 消息列表消息右键菜单
  PERSONAL_CENTER = 'personal-center', // 个人中心菜单
  CHAT_EDITOR_TOOLBAR = 'chat-editor-toolbar' // 会话编辑框工具栏菜单
  COLLECTION_CONTEXTMENU = 'collection-contextmenu', // 个人-收藏-右键菜单
  SEARCH_CONTEXTMENU = 'search-contextmenu', // 搜索窗口-右键菜单
  CHAT_RECORD_CONTEXTMENU = 'chat-record-contextmenu', // 聊天记录-右键菜单
}

获取列表选中会话(getActiveChat)

  • 支持环境:web/webview
  • 获取当前会话列表选中的会话
js
export function getActiveChat(): Promise<im.IChat | undefined>
  • 示例
js
const chat = await ksxz.ui.getActiveChat()
console.log(chat)

调起通讯录选择用户(selectUsers)

  • 支持环境:web/webview
  • 调起通讯录选择器选择用户
js
    export interface ISelectUserOptions {
      /** 选择指定会话id内的成员 */
      chatid?: number
      /** 是否返回第三方用户id */
      isThirdUserId?: boolean
      /** 最大选中人数 */
      maxCount?: number
      /** 默认选中用户id */
      selectedUserIds?: number[]
      /** 是否多选 */
      isMultiple?: boolean
      /** 通讯录选择器标题 */
      title?: string
      /** 确定按钮文案 */
      confirmText?: string
      /** 取消按钮文案 */
      cancelText?: string
    }

    /** 调起通讯录选择器选择用户 */
    export function selectUsers(options?: ISelectUserOptions): Promise<im.IUser[]>
  • 示例
js
const users = await ksxz.ui.selectUsers({
  isThirdUserId: true,
  maxCount: 300,
  isMultiple: true,
})

console.log(users)

窗口顶部通知(showToast)

  • 支持环境:web
  • 窗口顶部通知
js
export interface IShowToastOptions {
  message: string
  level?: 'info' | 'success' | 'warning' | 'error'
  duration?: number 
}

export function showToast(options: IShowToastOptions): void

窗口确认弹窗(showConfirmDialog)

  • 支持环境:web
  • 版本要求:v4.13以上
js
export function showConfirmDialog(options: {
      message: string
      title: string
      closeOnClickModal?: boolean
      showCancelButton?: boolean
      showClose?: boolean
}): Promise<void>
  • 示例
js
import ksxz from 'ksxz'

ksxz.ui.showConfirmDialog({
    message: 'token过期,需返回登录页面重新登录!'
    title: '提示'
}).then(() => {
    console.log('确定事件')
}).catch(() => {
    console.log('取消事件')
})

注册自定义消息渲染器(showConfirmDialog)

  • 支持环境:web
  • registerMessageDisplayer接口用于注册自定义消息渲染器,通过registerCssResource接口注册自定义组的样式,并通过dependencyCssResourceIds属性关联起来,实现样式隔离。自定义消息的右键菜单通过registerMenuItem接口注册。
js
// 注册自定义消息
export interface IMessageDisplayerConfiguration {
  // 自定义消息的类型,全局唯一
  customizeType: string
  // 自定义消息
  MessageDisplayer: new () => IMessageDisplayer
  // 自定义消息关联的样式,通过registerCssResource注册的css的id
  dependencyCssResourceIds: string[]

  options?: {
    // 是否可以多选
    isSelectable?: boolean
    // 是否可以回复
    isReplyable?: boolean
    // 是否可以转发
    isForwardable?: boolean
    // 是否可以收藏
    isCollectable?: boolean
    // 是否可以表情快捷回复
    isEmojiReplyEnabled?: boolean
    // 是否可以快捷回复
    isQuickReplyEnabled?: boolean
  }
}

// 自定义消息构造函数
export interface IMessageDisplayer {
  mount(root: HTMLElement, context: IMessageContext): void
  update?(updateContextKey: string, newVal: unknown, oldVal: unknown): void
  dispose(): void
}

// 自定义消息标准化参数
export interface IMessageContext {
  message: im.IMessage
} 

export function registerMessageDisplayer(configuration: IMessageDisplayerConfiguration): void
  • 示例:
js
export class WeatherMessageDisplayer implements ksxzApi.ui.IMessageDisplayer {
  msgItemInstance: Vue | undefined

  mount(root: HTMLElement, context: ksxzApi.ui.IMessageContext) {
    const WeatherMessageCtor = Vue.extend(WeatherMessage)
    this.msgItemInstance = new WeatherMessageCtor({
      propsData: context
    })
    this.msgItemInstance.$mount(root)
  }

  update(key: string, newVal: unknown) {
    if (this.msgItemInstance) {
      if (key === 'message') {
        ;(this.msgItemInstance as any).message = newVal
      }
    }
  }

  dispose() {
    this.msgItemInstance?.$destroy()
  }
}

ksxz.ui.registerMessageDisplayer({
  customizeType: 'weather_2',
   // registerCssResource 接口注册的cssid
  dependencyCssResourceIds: ['caixin'],
  MessageDisplayer: WeatherMessageDisplayer,
  options: {
    // 是否可以多选
    isSelectable: true,
    // 是否可以回复
    isReplyable: true,
    // 是否可以转发
    isForwardable: true,
    // 是否可以收藏
    isCollectable: true,
    // 是否可以表情快捷回复
    isEmojiReplyEnabled: true,
    // 是否可以快捷回复
    isQuickReplyEnabled: true
  }
})

注册CSS资源(registerCssResource)

  • 支持环境:web
  • 注册css资源,该css资源会与基座隔离,只影响关联的自定义组件。避免在全局直接引入css资源,可能会影响基座的样式,可以用raw-loader获取样式字符串,通过registerCssResource注册插件的样式。
js
// 注册css资源,用于关联自定义组件
export interface ICssResource {
  id: string
  cssContent: string
}
export function registerCssResource(cssResource: ICssResource): void
  • 示例: 可以通过import cssContent from '!!raw-loader!sass-loader!./caixin.scss 获取css字符串。
js
export const extensionCssId = 'caixin'
ksxz.ui.registerCssResource({
  id: extensionCssId,
  // css字符串
  cssContent: `
   .weather-container{
      background: rgb(255, 255, 255);
      padding: 8px;
      border-radius: 10px;
   }`
})

// 避免在全局直接引入css资源,可能会影响基座的样式!用raw-loader获取样式字符串,
// 通过registerCssResource注册样式。
import elementPlusCss from '!!raw-loader!element-plus/dist/index.css'
import apiCss from '!raw-loader!../../views/api.css'
import WeatherMessageCss from '!!raw-loader!../customMessage/WeatherMessage.css'
// shadow dom 内设置 :root 的样式是不生效的, 要把作用域改为:host
const regex = /:root\s*{/gm
const _elementPlusCss = elementPlusCss.replace(regex, ':host,:root {')
export const cssContent = _elementPlusCss + apiCss + WeatherMessageCss

export const extensionCssId = 'ksxz_ext_test_css'

export const registerCssResource = () => {
  // id要全局唯一
  ksxz.ui.registerCssResource({
    id: extensionCssId,
    cssContent
  })
}

唤起自定义弹窗(registerCssResource)

  • 版本支持:v4.15及以上
  • 支持环境:web
js
type closeDialog = () => void
export function showDialog(options?: IDialogConfiguration): closeDialog

export interface IDialogConfiguration {
  render(root: HTMLElement): void
  onDidClose?(): void
  dependencyCssResourceIds?: string[] // 自定义弹框关联的样式,通过registerCssResource注册的css的id
  isHeaderVisible?: boolean // 是否隐藏 Dialog 的头部
  isFooterVisible?: boolean // 是否隐藏 Dialog 的脚部
  isCloseIconVisible?: boolean // 是否显示关闭按钮
  isMaskVisible?: boolean //     是否展示遮罩
  isMaskClosable?: boolean // 点击蒙层是否允许关闭
  title?: string // Dialog 的标题
  width?: string // Dialog 的宽度
  okText?: string // 脚部确认按钮的文案
  cancelText?: string // 脚部取消按钮的文案
}
  • 示例:
js
import ksxz from 'ksxz'
const extensionCssId = 'temp'
ksxz.ui.registerCssResource({
    id: extensionCssId,
    cssContent:`.hello{color:red}`
})
ksxz.ui.showDialog({
    width: '600px',
    title: '弹窗title',
    isMaskClosable: true,
    isHeaderVisible: true,
    isFooterVisible: false,
    render(root) {
      root.innerHTML = '<div class="hello">helloworld</div>'
    },
    onDidClose() {
      console.log('onClose')
    },
    dependencyCssResourceIds: [extensionCssId]
})