---
title: "控制台 SDK"
description: "为独立应用接入租户后台、可信站点上下文和公共控制台交互。"
canonical_url: "https://docs.mcent.cn/qicore/console-sdk"
---
# 控制台 SDK

> 为独立应用接入租户后台、可信站点上下文和公共控制台交互。

控制台 SDK 提供 Nuxt 层、租户布局、公共组件、组合函数和导航能力。应用在自己的工程中维护业务页面，通过已声明的路由接入宿主控制台。

SDK 层本身不包含应用业务页面或业务服务端。安装方式见[独立开发工作区](https://docs.mcent.cn/qicore/development-workspace)，页面声明见[应用描述契约](https://docs.mcent.cn/qicore/application-manifest)。

## 构建接入

应用通过已安装的 `@qicore/application-sdk/console` 继承公共层，并安装匹配的 Console UI 依赖。应用自己的 Nuxt 工程还需提供匹配的 `nuxt`、`@nuxt/ui`、`vue` 和 `tailwindcss`；版本按所用 kit 说明、Console UI 的 `peerDependencies` 和应用锁文件核对。

SDK Console 层不会替应用注册 UI 模块、主样式或 API 基址。最小接入配置包含以下部分：

```ts
export default defineNuxtConfig({
  extends: ['@qicore/application-sdk/console'],
  modules: ['@nuxt/ui'],
  ssr: true,
  css: ['~/assets/main.css'],
  runtimeConfig: {
    apiInternalBase: '',
    public: { apiBase: '/api/v1' }
  }
})
```

应用主样式 `app/assets/main.css` 至少导入以下样式，并在自己的构建中将实际使用的 SDK 和 Console UI 组件纳入 Tailwind 内容扫描：

```css
@import "tailwindcss";
@import "@nuxt/ui";
@import "@orangezh1/qisvc-console-ui/styles.css";
@source "../../node_modules/@orangezh1/qisvc-console-ui/components";
@source "../../node_modules/@qicore/application-sdk/dist/console";
```

`@source` 路径相对这份 CSS 文件；示例假设依赖安装在当前 Nuxt 工程根的 `node_modules`，使用其他工作区布局时调整到实际安装位置。

`apiInternalBase` 是服务端私有配置键，示例空值用于声明该键，不是可工作的内部地址。通过宿主开发入口或生产启动器运行时，由其 `NUXT_API_INTERNAL_BASE` 注入实际内部基址；浏览器使用 `NUXT_PUBLIC_API_BASE` 对应的公开基址。不要把内部地址放进 `public`，也不要手工覆盖宿主注入的租户、应用或渲染上下文。

应用页面使用 `tenant` 布局；对应权限、菜单名称和分组在 `web.admin.routes` 中声明。描述里的路由与实际打包页面应当一致。

不同应用可以声明同一个业务路径。宿主依据服务端核验后的租户当前应用选择渲染器；应用内部路径必须唯一，不能覆盖宿主保留页面。

## `useApi('tenant')`

应用租户页应显式选择 `tenant` 门户。默认 `useApi()` 使用 `admin` 门户，不能将两者混用。

<table>
<thead>
  <tr>
    <th>
      返回成员
    </th>
    
    <th>
      用途
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        session
      </code>
    </td>
    
    <td>
      当前门户会话的响应式引用；租户权限按当前站点解析
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        api<T>(url, method?, body?, options?)
      </code>
    </td>
    
    <td>
      请求公共或应用后台 API，返回响应中的 <code>
        data
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        restore()
      </code>
    </td>
    
    <td>
      恢复并校验当前门户会话
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        login(username, password, portal)
      </code>
    </td>
    
    <td>
      调用宿主登录流程
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        logout()
      </code>
    </td>
    
    <td>
      调用宿主退出流程并清理本门户会话状态
    </td>
  </tr>
</tbody>
</table>

`api` 当前支持 `GET / POST / PUT / DELETE`，默认 `GET`。第四个参数为 `{ signal?: AbortSignal }`；不要把它与消费者 SDK 的自定义请求头参数混淆。

它会按照运行环境选取内部 API 地址或公开 API 基址，并处理：

- 带上会话 Cookie，发送 `X-Console-Portal: tenant`。
- 已有会话的非 GET 请求带上 `X-CSRF-Token`。
- SSR 请求转发当前文档的 Cookie；符合条件的 GET 可复用本次已验证的渲染数据。
- 将成功响应解包为 `data`；错误对象保留 `status` 和业务 `code`。
- 对取消请求保持取消语义；旧会话请求的 401 不覆盖后来建立的新会话。

传入的是相对 API 基址的路径，例如 `/admin/tenants/...`，不再次添加 `/api/v1`。

## 当前站点上下文

`useTenantContext()` 提供：

<table>
<thead>
  <tr>
    <th>
      返回成员
    </th>
    
    <th>
      用途
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        tenantId
      </code>
    </td>
    
    <td>
      当前已选择站点的计算引用，未建立有效上下文时为空字符串
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        current
      </code>
    </td>
    
    <td>
      当前账号对应的站点选择状态
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        choose(site)
      </code>
    </td>
    
    <td>
      记录当前会话的站点选择
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        refresh(signal?)
      </code>
    </td>
    
    <td>
      重新读取当前站点入口及权限，并防止迟到结果覆盖新上下文
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        recent
      </code>
    </td>
    
    <td>
      最近选择提示，不构成访问授权
    </td>
  </tr>
</tbody>
</table>

在脚本中读取 `tenantId.value`，不要把计算引用本身拼接到 URL。Cookie、页面查询参数和本地选择只是请求目标；最终授权由服务端逐次核验。

下面仅演示读取应用配置。应用页面应在租户上下文准备完成后调用，并在写入页面状态前核对请求发起时的会话引用与站点；同一账号重新登录也应当视为新会话：

```ts
type ConfigView = {
  id: string
  applicationKey: string
  config: Record<string, unknown>
  version: number
}

const { api, session } = useApi('tenant')
const { tenantId } = useTenantContext()

async function readConfig(signal?: AbortSignal) {
  const id = tenantId.value
  const ownSession = session.value
  if (!id || !ownSession) return null
  const path = '/admin/tenants/' + encodeURIComponent(id)
    + '/apps/notes-app/config'
  const result = await api<ConfigView>(path, 'GET', undefined, { signal })
  if (signal?.aborted || tenantId.value !== id
    || session.value !== ownSession) return null
  return result
}
```

配置保存使用读取结果中的配置版本。配置版本与应用开通版本是不同字段，具体请求见[HTTP 接口](https://docs.mcent.cn/qicore/http-api)。

## 公共组件与页面行为

Console 层注册自身的组件和组合函数；共享 Console UI 组件从对应公开导出路径显式导入。继承层不表示所有共享 UI 组件都已自动注册：

<table>
<thead>
  <tr>
    <th>
      场景
    </th>
    
    <th>
      复用入口
    </th>
    
    <th>
      来源
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      表格、分页和页面标题
    </td>
    
    <td>
      <code>
        ConsoleDataTable
      </code>
      
      、<code>
        ConsolePagination
      </code>
      
      、<code>
        ConsolePageHeading
      </code>
    </td>
    
    <td>
      Console UI 显式导入
    </td>
  </tr>
  
  <tr>
    <td>
      表单操作与设置字段
    </td>
    
    <td>
      <code>
        ConsoleFormActions
      </code>
      
      、<code>
        ConsoleSettingsField
      </code>
    </td>
    
    <td>
      Console UI 显式导入
    </td>
  </tr>
  
  <tr>
    <td>
      查询、重置、刷新与抽屉
    </td>
    
    <td>
      <code>
        ConsoleQueryButton
      </code>
      
      、<code>
        ConsoleDrawer
      </code>
    </td>
    
    <td>
      SDK Console 层
    </td>
  </tr>
  
  <tr>
    <td>
      放弃修改
    </td>
    
    <td>
      <code>
        ConsoleDiscardChangesDialog
      </code>
      
      、<code>
        useDiscardChanges
      </code>
    </td>
    
    <td>
      SDK Console 层
    </td>
  </tr>
  
  <tr>
    <td>
      页签与离页保护
    </td>
    
    <td>
      <code>
        useRouteTab
      </code>
      
      、<code>
        usePageLeaveGuard
      </code>
    </td>
    
    <td>
      SDK Console 层
    </td>
  </tr>
</tbody>
</table>

例如在应用页面的 `<script setup>` 中按需导入：

```ts
import ConsoleDataTable from '@orangezh1/qisvc-console-ui/components/ConsoleDataTable'
import ConsolePagination from '@orangezh1/qisvc-console-ui/components/ConsolePagination'
import ConsolePageHeading from '@orangezh1/qisvc-console-ui/components/ConsolePageHeading'
import ConsoleFormActions from '@orangezh1/qisvc-console-ui/components/ConsoleFormActions'
import ConsoleSettingsField from '@orangezh1/qisvc-console-ui/components/ConsoleSettingsField'
```

只隐藏按钮不能替代服务端权限校验。应用页面声明的权限必须属于该应用的 `definition.permissions`；自定义角色不会因新增权限而自动扩权。

## 独立动作状态

每个异步动作维护自己的可见 loading：

- 查询只点亮查询按钮，重置只点亮重置按钮。
- 保存、删除和行操作分别管理状态。
- 保存后的数据重读、后台轮询不点亮用户的查询或刷新按钮。
- 取消、失败和重叠请求都要释放对应动作；切换站点后丢弃旧结果。

共享的数据请求状态可以协调禁用和防止过期响应，但不要直接绑定多个按钮的旋转指示。

## SSR、标题与资源

应用后台应在首个 HTML 中输出路由最终标题和当前站点 favicon，并复用宿主已经核验的渲染上下文。不要用挂载后的定时器替换通用标题。

跨宿主和应用页面的导航由公共导航层交接到相应服务端渲染器。保留离页确认、未保存修改保护与减少动态效果偏好，不自行建立另一套路由授权。

静态资源随应用版本交付，公开范围限于声明的后台 public 目录。小程序运行身份文件、服务端模块及私有文件不应通过后台资源地址公开。

发布前验证直达、刷新、站内导航、无权限访问、站点切换和延迟响应。完整包布局见[生命周期与打包](https://docs.mcent.cn/qicore/lifecycle-and-packaging)。


## Sitemap

See the full [sitemap](https://docs.mcent.cn/sitemap.md) for all pages.
