@csntgao/uni-base 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -37,7 +37,7 @@ examples/vanilla 原生 HTML / JavaScript 接入示例
37
37
 
38
38
  `<uniplat-user-login>` 必须严格复刻亲亲企服 40.1 注册登录原型,提供密码、验证码和扫码三个固定页签、逐字段校验错误态及注册成功过渡页。验证码登录只包含手机号和短信验证码,不得因为接口现状增加图形验证码或其他原型中不存在的字段;旧版 `createCaptcha()` 仅为源码兼容保留,组件不得调用。自定义 Host transport 的密码与扫码方法保持可选,未提供时对应页签保持显示但禁用。UniplatBase 专用 transport 已复用短信验证码及统一 `login-application` 接口提供三种登录能力;组件本身不得保存 JWT,登录结果必须先交给宿主 `setAccessToken()`,再派发不含凭据的成功事件。后端明确返回新用户时显示 2 秒过渡页,并通过 `onboarding-requested` 请求 Host 调用 `<uniplat-account-context>.startOrganizationOnboarding()`。
39
39
 
40
- 组织入驻不是公开 Web Component。40.1 原型 02、A/B 企业/团队创建分支、校验/成功状态及 4.1 无归属提示均由 `<uniplat-account-context>` 内部实现;Host 在其 `organizationOnboarding.transport` 中注入创建能力,不能使用或注册 `<uniplat-organization-onboarding>`。入驻流程一律用原生 `dialog.showModal()` 在视口居中呈现,不再使用锚定账户胶囊的下拉浮层;强制态与普通态只在能否关闭上不同,普通态可由遮罩点击、`Escape` 或“取消”收起,关闭时必须同步清空面板状态,避免 `dialog` 与组件状态不一致。右上角的“取消”只在当前用户已归属某个组织时渲染(内部 `canCancel`,由 Account Context 按投影中的归属数量传入),无归属用户不显示取消,点击后直接收起流程,不再经过“确定暂不创建”的确认页。个人用户不允许以非员工身份留在系统里,因此无归属用户必须先入驻,没有任何跳过开关:账户投影为零归属时组件用原生 `dialog.showModal()` 自动打开强制态创建流程,不渲染跳过、无归属提示与关闭按钮,遮罩点击、`Escape` 与 `cancel` 都不关闭。强制态首屏只有创建企业、创建团队和“退出登录”(派发 `logout-requested`)三个出口,不提供邀请码加入入口:加入已有组织通过扫码或粘贴邀请链接进入系统完成,Host 也不需要为强制态实现邀请码页面。组织邀请页豁免强制入驻:当前 URL 末段路径命中 `organizationOnboarding.invitePaths`(默认 `['org-invite']`)且带非空 `code` 时不弹对话框,避免盖住加入流程;路径与 hash 路由都参与匹配,组件只取一个布尔值,邀请码的值不得进入组件状态、DOM、公共事件或日志。离开邀请页(`popstate`/`hashchange`)后仍无员工身份时必须重新强制入驻,不得留下可浏览的无归属态。企业/团队表单页提供“返回”回到创建方式选择,创建成功页的认证、升级或邀请动作才是关闭对话框的终点。`organizationOnboarding` 只接收 `transport`,不得再提供 `mandatory` 之类的退出开关。强制态必须使用 top layer 对话框,不得只依赖可能被 Host `transform/overflow` 限制的固定遮罩。点击账户菜单或个人态的“创建团队 / 企业”必须直接启动内部流程,不得向 Host 派发 `organization-create-requested` 或 `create-organization-requested`。真实 UniplatBase transport 复用 `createUniplatBaseOrganizationOnboardingTransport()` 与 `system.org/create_organization`。邀请码加入、企业认证、团队升级和邀请成员等未展开页面仍派发业务事件,由 Host 实现。
40
+ 组织入驻不是公开 Web Component。40.1 原型 02、A/B 企业/团队创建分支、校验/成功状态及 4.1 无归属提示均由 `<uniplat-account-context>` 内部实现;Host 在其 `organizationOnboarding.transport` 中注入创建能力,不能使用或注册 `<uniplat-organization-onboarding>`。入驻流程一律用原生 `dialog.showModal()` 在视口居中呈现,不再使用锚定账户胶囊的下拉浮层;强制态与普通态只在能否关闭上不同,普通态可由遮罩点击、`Escape` 或“取消”收起,关闭时必须同步清空面板状态,避免 `dialog` 与组件状态不一致。右上角的“取消”只在当前用户已归属某个组织时渲染(内部 `canCancel`,由 Account Context 按投影中的归属数量传入),无归属用户不显示取消,点击后直接收起流程,不再经过“确定暂不创建”的确认页。个人用户不允许以非员工身份留在系统里,因此无归属用户必须先入驻,没有任何跳过开关:账户投影为零归属时组件用原生 `dialog.showModal()` 自动打开强制态创建流程,不渲染跳过、无归属提示与关闭按钮,遮罩点击、`Escape` 与 `cancel` 都不关闭。强制态首屏只有创建企业、创建团队和“退出登录”(派发 `logout-requested`)三个出口,不提供邀请码加入入口:加入已有组织通过扫码或粘贴邀请链接进入系统完成,Host 也不需要为强制态实现邀请码页面。组织邀请页豁免强制入驻:当前 URL 末段路径命中 `organizationOnboarding.invitePaths`(默认 `['org-invite']`)且带非空 `code` 时不弹对话框,避免盖住加入流程;路径与 hash 路由都参与匹配,组件只取一个布尔值,邀请码的值不得进入组件状态、DOM、公共事件或日志。离开邀请页(`popstate`/`hashchange`)后仍无员工身份时必须重新强制入驻,不得留下可浏览的无归属态。企业/团队表单页提供“返回”回到创建方式选择。创建成功页是关闭对话框的终点:企业成功页保留“稍后认证 / 去认证”两个动作,团队成功页只有一个“确认”并派发 `onboarding-finished`,不再提供“升级为企业 / 邀请成员”分叉,这两件事留在组织管理里做。`organizationOnboarding` 只接收 `transport`,不得再提供 `mandatory` 之类的退出开关。强制态必须使用 top layer 对话框,不得只依赖可能被 Host `transform/overflow` 限制的固定遮罩。点击账户菜单或个人态的“创建团队 / 企业”必须直接启动内部流程,不得向 Host 派发 `organization-create-requested` 或 `create-organization-requested`。真实 UniplatBase transport 复用 `createUniplatBaseOrganizationOnboardingTransport()` 与 `system.org/create_organization`。邀请码加入、企业认证、团队升级和邀请成员等未展开页面仍派发业务事件,由 Host 实现。
41
41
 
42
42
  `<uniplat-organization-selector>` 覆盖 40.1 原型 S.1 多归属选择弹窗,显示 Host 提供的完整企业/团队列表、类型、认证状态、角色、成员数和最近进入信息,并自行负责脱敏账户摘要、名称与统一社会信用代码搜索、全部/企业/团队类型筛选、“最近进入 / 全部组织”分组、默认选中和带目标名称的主按钮。选中与记住偏好只在组件内临时维护,点击“进入”后交由 Host 完成登录态切换,“退出登录”只派发事件。`presentation: 'modal'` 时组件自渲染 S.1 强制态弹窗:固定遮罩、无关闭按钮,遮罩点击与 `Escape` 都不关闭,`Escape` 也不冒泡到 Host 的全局关闭逻辑;默认 `inline` 只输出卡片,遮罩归 Host。S.0 的“弹不弹”判定由纯函数 `resolveOrganizationEntryDecision()` 提供,只按 0 个归属、1 个归属、命中记住选择、2 个及以上分别返回 `personal | auto-enter | prompt`;它不读写宿主存储,记住偏好由 Host 自行持久化并回传,记住的组织已不在列表时退回 `prompt`。
43
43
 
@@ -376,8 +376,7 @@ join-organization-requested
376
376
  organization-created
377
377
  enterprise-authentication-requested
378
378
  enterprise-authentication-deferred
379
- team-upgrade-requested
380
- team-invite-members-requested
379
+ onboarding-finished
381
380
  error
382
381
  ```
383
382
 
@@ -435,7 +434,7 @@ npm 包名、导出类型名、Custom Element 标签、`configure()` 结构、
435
434
  - Account Context 内嵌工作台需测试不注册独立应用切换标签、JWT 只交给 transport、HTTP Authorization header、入口净化、当前组织级应用加 `application_scope=open` 应用的筛选、缺字段时 `oid=0` 兼容、`issue_ticket` 空请求体、签发响应来源进入新 Fragment、公共事件无 JWT/Ticket、Ticket 只进入局部导航 URL、目标页面先清 Fragment 再按初始化应用登录、响应应用绑定校验、加载错误、仅请求应用列表和应用选择交互。
436
435
  - User Login 需测试组件注册与导出、三个固定页签、不可用能力禁用、原型中不存在图形验证码且不调用兼容方法、逐字段失焦校验和协议错误态、稳定后端错误码映射、短信倒计时、密码登录、扫码待确认/已扫码/过期/成功状态、注册成功过渡页、2 秒自动入驻和立即开始、页签切换与断开后的定时器清理、登录回调,以及 JWT、密码和二维码 challenge 不进入 URL、DOM、公共事件、日志或安全错误。
437
436
  - User Menu 需测试组件注册与导出、40.1 6.7 原型关键内容、个人账号与当前组织员工信息入口、企业认证与团队样式、安全头像与文字回退、Host 受控选中状态、重新配置、空组织状态、完整手机号拒绝、创建与退出事件、普通文本插值,以及公共事件只包含必要的组织 ID,不包含显示名、手机号、组织名称、头像 URL、JWT 或内部配置。
438
- - 内置组织入驻需测试所有原型步骤、逐字段校验、稳定 transport 错误映射、企业/团队成功状态、跳过与未展开流程事件、普通文本插值,以及公共事件不包含企业信息、信用代码、JWT 或原始异常。强制入驻还需测试零归属投影自动打开 top layer 对话框、不渲染跳过、无归属提示与邀请码入口、`Escape` 与外部点击都不关闭、“退出登录”只派发空 detail、表单页“返回”回到创建方式选择、成功页动作才关闭对话框,以及传入历史 `mandatory: false` 也不能让无归属用户跳过。真实 UniplatBase transport 还必须测试每次请求重新取 JWT、JWT 只进入 Authorization header、企业/团队请求字段、成功响应净化和未知错误收敛。
437
+ - 内置组织入驻需测试所有原型步骤、逐字段校验、稳定 transport 错误映射、企业/团队成功状态、跳过与未展开流程事件、普通文本插值,以及公共事件不包含企业信息、信用代码、JWT 或原始异常。强制入驻还需测试零归属投影自动打开 top layer 对话框、不渲染跳过、无归属提示与邀请码入口、`Escape` 与外部点击都不关闭、“退出登录”只派发空 detail、表单页“返回”回到创建方式选择、企业成功页两个动作与团队成功页单个“确认”都能关闭对话框,以及传入历史 `mandatory: false` 也不能让无归属用户跳过。真实 UniplatBase transport 还必须测试每次请求重新取 JWT、JWT 只进入 Authorization header、企业/团队请求字段、成功响应净化和未知错误收敛。
439
438
  - Organization Selector 需测试完整列表、企业认证与团队类型样式、脱敏账户摘要与完整手机号拒绝、按名称和统一社会信用代码搜索且代码不进入 DOM、类型筛选与常显计数、“最近进入 / 全部组织”分组与默认选中、带目标名称的主按钮、无匹配与空归属状态、记住偏好、创建/进入/退出登录事件、`modal` 强制态无关闭按钮且遮罩点击与 `Escape` 都不关闭并在移除后解除 `Escape` 拦截、非法 `presentation` 与重复 ID 拒绝和普通文本插值;事件只包含稳定组织 ID 与偏好,不含姓名、手机号、组织名称或信用代码。`resolveOrganizationEntryDecision()` 需测试 0 个归属、单一归属、命中与失效的记住选择、2 个及以上的默认选中项和非法输入拒绝。
440
439
  - Organization Multi Employee Selector 需测试部门树、员工分页和搜索、初始选择补取、跨部门按员工 ID 去重、多选上限、受保护项、确认/取消事件、独立滚动、移动视图、错误重试、身份切换清理,以及 JWT、完整手机号、身份字段和后端错误不进入 DOM、事件、URL或日志。专用 transport 必须测试每次请求重新读取 JWT、只使用 Authorization header、组织级 directory scope 和无调用方身份参数。
441
440
  - Organization Single Employee Selector 需测试独立标签与导出、初始单个 ID 回显、新选择替换旧选择、未选时禁止确认、受保护项、标量确认 detail、拒绝多选打开参数,以及与多选组件相同的数据和认证安全边界。
package/CLAUDE.md CHANGED
@@ -37,7 +37,7 @@ examples/vanilla 原生 HTML / JavaScript 接入示例
37
37
 
38
38
  `<uniplat-user-login>` 必须严格复刻亲亲企服 40.1 注册登录原型,提供密码、验证码和扫码三个固定页签、逐字段校验错误态及注册成功过渡页。验证码登录只包含手机号和短信验证码,不得因为接口现状增加图形验证码或其他原型中不存在的字段;旧版 `createCaptcha()` 仅为源码兼容保留,组件不得调用。自定义 Host transport 的密码与扫码方法保持可选,未提供时对应页签保持显示但禁用。UniplatBase 专用 transport 已复用短信验证码及统一 `login-application` 接口提供三种登录能力;组件本身不得保存 JWT,登录结果必须先交给宿主 `setAccessToken()`,再派发不含凭据的成功事件。后端明确返回新用户时显示 2 秒过渡页,并通过 `onboarding-requested` 请求 Host 调用 `<uniplat-account-context>.startOrganizationOnboarding()`。
39
39
 
40
- 组织入驻不是公开 Web Component。40.1 原型 02、A/B 企业/团队创建分支、校验/成功状态及 4.1 无归属提示均由 `<uniplat-account-context>` 内部实现;Host 在其 `organizationOnboarding.transport` 中注入创建能力,不能使用或注册 `<uniplat-organization-onboarding>`。入驻流程一律用原生 `dialog.showModal()` 在视口居中呈现,不再使用锚定账户胶囊的下拉浮层;强制态与普通态只在能否关闭上不同,普通态可由遮罩点击、`Escape` 或“取消”收起,关闭时必须同步清空面板状态,避免 `dialog` 与组件状态不一致。右上角的“取消”只在当前用户已归属某个组织时渲染(内部 `canCancel`,由 Account Context 按投影中的归属数量传入),无归属用户不显示取消,点击后直接收起流程,不再经过“确定暂不创建”的确认页。个人用户不允许以非员工身份留在系统里,因此无归属用户必须先入驻,没有任何跳过开关:账户投影为零归属时组件用原生 `dialog.showModal()` 自动打开强制态创建流程,不渲染跳过、无归属提示与关闭按钮,遮罩点击、`Escape` 与 `cancel` 都不关闭。强制态首屏只有创建企业、创建团队和“退出登录”(派发 `logout-requested`)三个出口,不提供邀请码加入入口:加入已有组织通过扫码或粘贴邀请链接进入系统完成,Host 也不需要为强制态实现邀请码页面。组织邀请页豁免强制入驻:当前 URL 末段路径命中 `organizationOnboarding.invitePaths`(默认 `['org-invite']`)且带非空 `code` 时不弹对话框,避免盖住加入流程;路径与 hash 路由都参与匹配,组件只取一个布尔值,邀请码的值不得进入组件状态、DOM、公共事件或日志。离开邀请页(`popstate`/`hashchange`)后仍无员工身份时必须重新强制入驻,不得留下可浏览的无归属态。企业/团队表单页提供“返回”回到创建方式选择,创建成功页的认证、升级或邀请动作才是关闭对话框的终点。`organizationOnboarding` 只接收 `transport`,不得再提供 `mandatory` 之类的退出开关。强制态必须使用 top layer 对话框,不得只依赖可能被 Host `transform/overflow` 限制的固定遮罩。点击账户菜单或个人态的“创建团队 / 企业”必须直接启动内部流程,不得向 Host 派发 `organization-create-requested` 或 `create-organization-requested`。真实 UniplatBase transport 复用 `createUniplatBaseOrganizationOnboardingTransport()` 与 `system.org/create_organization`。邀请码加入、企业认证、团队升级和邀请成员等未展开页面仍派发业务事件,由 Host 实现。
40
+ 组织入驻不是公开 Web Component。40.1 原型 02、A/B 企业/团队创建分支、校验/成功状态及 4.1 无归属提示均由 `<uniplat-account-context>` 内部实现;Host 在其 `organizationOnboarding.transport` 中注入创建能力,不能使用或注册 `<uniplat-organization-onboarding>`。入驻流程一律用原生 `dialog.showModal()` 在视口居中呈现,不再使用锚定账户胶囊的下拉浮层;强制态与普通态只在能否关闭上不同,普通态可由遮罩点击、`Escape` 或“取消”收起,关闭时必须同步清空面板状态,避免 `dialog` 与组件状态不一致。右上角的“取消”只在当前用户已归属某个组织时渲染(内部 `canCancel`,由 Account Context 按投影中的归属数量传入),无归属用户不显示取消,点击后直接收起流程,不再经过“确定暂不创建”的确认页。个人用户不允许以非员工身份留在系统里,因此无归属用户必须先入驻,没有任何跳过开关:账户投影为零归属时组件用原生 `dialog.showModal()` 自动打开强制态创建流程,不渲染跳过、无归属提示与关闭按钮,遮罩点击、`Escape` 与 `cancel` 都不关闭。强制态首屏只有创建企业、创建团队和“退出登录”(派发 `logout-requested`)三个出口,不提供邀请码加入入口:加入已有组织通过扫码或粘贴邀请链接进入系统完成,Host 也不需要为强制态实现邀请码页面。组织邀请页豁免强制入驻:当前 URL 末段路径命中 `organizationOnboarding.invitePaths`(默认 `['org-invite']`)且带非空 `code` 时不弹对话框,避免盖住加入流程;路径与 hash 路由都参与匹配,组件只取一个布尔值,邀请码的值不得进入组件状态、DOM、公共事件或日志。离开邀请页(`popstate`/`hashchange`)后仍无员工身份时必须重新强制入驻,不得留下可浏览的无归属态。企业/团队表单页提供“返回”回到创建方式选择。创建成功页是关闭对话框的终点:企业成功页保留“稍后认证 / 去认证”两个动作,团队成功页只有一个“确认”并派发 `onboarding-finished`,不再提供“升级为企业 / 邀请成员”分叉,这两件事留在组织管理里做。`organizationOnboarding` 只接收 `transport`,不得再提供 `mandatory` 之类的退出开关。强制态必须使用 top layer 对话框,不得只依赖可能被 Host `transform/overflow` 限制的固定遮罩。点击账户菜单或个人态的“创建团队 / 企业”必须直接启动内部流程,不得向 Host 派发 `organization-create-requested` 或 `create-organization-requested`。真实 UniplatBase transport 复用 `createUniplatBaseOrganizationOnboardingTransport()` 与 `system.org/create_organization`。邀请码加入、企业认证、团队升级和邀请成员等未展开页面仍派发业务事件,由 Host 实现。
41
41
 
42
42
  `<uniplat-organization-selector>` 覆盖 40.1 原型 S.1 多归属选择弹窗,显示 Host 提供的完整企业/团队列表、类型、认证状态、角色、成员数和最近进入信息,并自行负责脱敏账户摘要、名称与统一社会信用代码搜索、全部/企业/团队类型筛选、“最近进入 / 全部组织”分组、默认选中和带目标名称的主按钮。选中与记住偏好只在组件内临时维护,点击“进入”后交由 Host 完成登录态切换,“退出登录”只派发事件。`presentation: 'modal'` 时组件自渲染 S.1 强制态弹窗:固定遮罩、无关闭按钮,遮罩点击与 `Escape` 都不关闭,`Escape` 也不冒泡到 Host 的全局关闭逻辑;默认 `inline` 只输出卡片,遮罩归 Host。S.0 的“弹不弹”判定由纯函数 `resolveOrganizationEntryDecision()` 提供,只按 0 个归属、1 个归属、命中记住选择、2 个及以上分别返回 `personal | auto-enter | prompt`;它不读写宿主存储,记住偏好由 Host 自行持久化并回传,记住的组织已不在列表时退回 `prompt`。
43
43
 
@@ -376,8 +376,7 @@ join-organization-requested
376
376
  organization-created
377
377
  enterprise-authentication-requested
378
378
  enterprise-authentication-deferred
379
- team-upgrade-requested
380
- team-invite-members-requested
379
+ onboarding-finished
381
380
  error
382
381
  ```
383
382
 
@@ -435,7 +434,7 @@ npm 包名、导出类型名、Custom Element 标签、`configure()` 结构、
435
434
  - Account Context 内嵌工作台需测试不注册独立应用切换标签、JWT 只交给 transport、HTTP Authorization header、入口净化、当前组织级应用加 `application_scope=open` 应用的筛选、缺字段时 `oid=0` 兼容、`issue_ticket` 空请求体、签发响应来源进入新 Fragment、公共事件无 JWT/Ticket、Ticket 只进入局部导航 URL、目标页面先清 Fragment 再按初始化应用登录、响应应用绑定校验、加载错误、仅请求应用列表和应用选择交互。
436
435
  - User Login 需测试组件注册与导出、三个固定页签、不可用能力禁用、原型中不存在图形验证码且不调用兼容方法、逐字段失焦校验和协议错误态、稳定后端错误码映射、短信倒计时、密码登录、扫码待确认/已扫码/过期/成功状态、注册成功过渡页、2 秒自动入驻和立即开始、页签切换与断开后的定时器清理、登录回调,以及 JWT、密码和二维码 challenge 不进入 URL、DOM、公共事件、日志或安全错误。
437
436
  - User Menu 需测试组件注册与导出、40.1 6.7 原型关键内容、个人账号与当前组织员工信息入口、企业认证与团队样式、安全头像与文字回退、Host 受控选中状态、重新配置、空组织状态、完整手机号拒绝、创建与退出事件、普通文本插值,以及公共事件只包含必要的组织 ID,不包含显示名、手机号、组织名称、头像 URL、JWT 或内部配置。
438
- - 内置组织入驻需测试所有原型步骤、逐字段校验、稳定 transport 错误映射、企业/团队成功状态、跳过与未展开流程事件、普通文本插值,以及公共事件不包含企业信息、信用代码、JWT 或原始异常。强制入驻还需测试零归属投影自动打开 top layer 对话框、不渲染跳过、无归属提示与邀请码入口、`Escape` 与外部点击都不关闭、“退出登录”只派发空 detail、表单页“返回”回到创建方式选择、成功页动作才关闭对话框,以及传入历史 `mandatory: false` 也不能让无归属用户跳过。真实 UniplatBase transport 还必须测试每次请求重新取 JWT、JWT 只进入 Authorization header、企业/团队请求字段、成功响应净化和未知错误收敛。
437
+ - 内置组织入驻需测试所有原型步骤、逐字段校验、稳定 transport 错误映射、企业/团队成功状态、跳过与未展开流程事件、普通文本插值,以及公共事件不包含企业信息、信用代码、JWT 或原始异常。强制入驻还需测试零归属投影自动打开 top layer 对话框、不渲染跳过、无归属提示与邀请码入口、`Escape` 与外部点击都不关闭、“退出登录”只派发空 detail、表单页“返回”回到创建方式选择、企业成功页两个动作与团队成功页单个“确认”都能关闭对话框,以及传入历史 `mandatory: false` 也不能让无归属用户跳过。真实 UniplatBase transport 还必须测试每次请求重新取 JWT、JWT 只进入 Authorization header、企业/团队请求字段、成功响应净化和未知错误收敛。
439
438
  - Organization Selector 需测试完整列表、企业认证与团队类型样式、脱敏账户摘要与完整手机号拒绝、按名称和统一社会信用代码搜索且代码不进入 DOM、类型筛选与常显计数、“最近进入 / 全部组织”分组与默认选中、带目标名称的主按钮、无匹配与空归属状态、记住偏好、创建/进入/退出登录事件、`modal` 强制态无关闭按钮且遮罩点击与 `Escape` 都不关闭并在移除后解除 `Escape` 拦截、非法 `presentation` 与重复 ID 拒绝和普通文本插值;事件只包含稳定组织 ID 与偏好,不含姓名、手机号、组织名称或信用代码。`resolveOrganizationEntryDecision()` 需测试 0 个归属、单一归属、命中与失效的记住选择、2 个及以上的默认选中项和非法输入拒绝。
440
439
  - Organization Multi Employee Selector 需测试部门树、员工分页和搜索、初始选择补取、跨部门按员工 ID 去重、多选上限、受保护项、确认/取消事件、独立滚动、移动视图、错误重试、身份切换清理,以及 JWT、完整手机号、身份字段和后端错误不进入 DOM、事件、URL或日志。专用 transport 必须测试每次请求重新读取 JWT、只使用 Authorization header、组织级 directory scope 和无调用方身份参数。
441
440
  - Organization Single Employee Selector 需测试独立标签与导出、初始单个 ID 回显、新选择替换旧选择、未选时禁止确认、受保护项、标量确认 detail、拒绝多选打开参数,以及与多选组件相同的数据和认证安全边界。
package/README.md CHANGED
@@ -300,7 +300,7 @@ accountContext.configure({
300
300
 
301
301
  入驻流程一律以原生 `dialog.showModal()` 在视口居中呈现(top layer,不受 Host 导航 `transform`/`overflow` 影响),不再使用锚定账户胶囊的下拉浮层;账户菜单里点“创建团队 / 企业”打开的普通态同样居中,可由遮罩点击、`Escape` 或右上角“取消”关闭。“取消”只在当前用户已归属某个组织时渲染,无归属用户看不到该按钮;点击后直接收起流程,不再经过“确定暂不创建”确认页。
302
302
 
303
- 内部流程会经账户组件派发 `onboarding-skipped`、`join-organization-requested`、`organization-created`、`enterprise-authentication-requested`、`enterprise-authentication-deferred`、`team-upgrade-requested`、`team-invite-members-requested`、`logout-requested` 和安全的 `error` 事件。Host 可通过账户组件的 `onboarding-panel` part 定制该浮层或对话框。
303
+ 内部流程会经账户组件派发 `onboarding-skipped`、`join-organization-requested`、`organization-created`、`enterprise-authentication-requested`、`enterprise-authentication-deferred`、`onboarding-finished`、`logout-requested` 和安全的 `error` 事件。团队创建成功页只有一个“确认”,点击后派发 `onboarding-finished`(`{ organizationId }`);此前的 `team-upgrade-requested` 与 `team-invite-members-requested` 不再派发,升级与邀请成员留在组织管理里。Host 可通过账户组件的 `onboarding-panel` part 定制该浮层或对话框。
304
304
 
305
305
  个人用户不允许以非员工身份留在系统里,所以无归属用户必须先入驻,没有跳过开关。账户投影返回零归属时,组件用原生 `dialog.showModal()` 自动打开强制态创建流程:
306
306
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@csntgao/uni-base",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "type": "module",
5
5
  "packageManager": "pnpm@11.3.0",
6
6
  "publishConfig": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uniplat/ai-employee-web-components",
3
- "version": "0.28.0",
3
+ "version": "0.29.0",
4
4
  "description": "Framework-independent Web Components for Uniplat account, login, application switching and AI Employee chat",
5
5
  "type": "module",
6
6
  "sideEffects": true,
@@ -1161,8 +1161,7 @@ export class UniplatAccountContext extends LitElement {
1161
1161
  @organization-created=${this.refreshAfterOrganizationCreated}
1162
1162
  @enterprise-authentication-requested=${this.finishMandatoryOnboarding}
1163
1163
  @enterprise-authentication-deferred=${this.finishMandatoryOnboarding}
1164
- @team-upgrade-requested=${this.finishMandatoryOnboarding}
1165
- @team-invite-members-requested=${this.finishMandatoryOnboarding}
1164
+ @onboarding-finished=${this.finishMandatoryOnboarding}
1166
1165
  >
1167
1166
  <div class="onboarding-dialog" @click=${stopEventPropagation}>
1168
1167
  <uniplat-internal-organization-onboarding></uniplat-internal-organization-onboarding>
@@ -35,8 +35,7 @@ export interface OrganizationOnboardingEventMap {
35
35
  'organization-created': CustomEvent<{ organizationId: string; type: 'enterprise' | 'team' }>
36
36
  'enterprise-authentication-requested': CustomEvent<{ organizationId: string }>
37
37
  'enterprise-authentication-deferred': CustomEvent<{ organizationId: string }>
38
- 'team-upgrade-requested': CustomEvent<{ organizationId: string }>
39
- 'team-invite-members-requested': CustomEvent<{ organizationId: string }>
38
+ 'onboarding-finished': CustomEvent<{ organizationId: string }>
40
39
  error: CustomEvent<{
41
40
  code: 'ENTERPRISE_CREATE_FAILED' | 'TEAM_CREATE_FAILED'
42
41
  message: string
@@ -392,6 +391,10 @@ export class InternalOrganizationOnboarding extends LitElement {
392
391
  margin-top: 20px;
393
392
  }
394
393
 
394
+ .success-actions.single {
395
+ grid-template-columns: 1fr;
396
+ }
397
+
395
398
  .confirmation-actions .primary,
396
399
  .success-actions .primary {
397
400
  margin-top: 0;
@@ -897,7 +900,7 @@ export class InternalOrganizationOnboarding extends LitElement {
897
900
  : '团队可直接使用协作与部分服务;若需签约下单、开票,请升级为企业并完成认证。'
898
901
  }
899
902
  </p>
900
- <div class="success-actions">
903
+ <div class="success-actions ${enterprise ? '' : 'single'}">
901
904
  ${
902
905
  enterprise
903
906
  ? html`
@@ -919,21 +922,13 @@ export class InternalOrganizationOnboarding extends LitElement {
919
922
  </button>
920
923
  `
921
924
  : html`
922
- <button
923
- class="secondary"
924
- part="secondary-action"
925
- type="button"
926
- @click=${this.requestTeamUpgrade}
927
- >
928
- 升级为企业
929
- </button>
930
925
  <button
931
926
  class="primary"
932
927
  part="primary-action"
933
928
  type="button"
934
- @click=${this.requestTeamInvites}
929
+ @click=${this.finishOnboarding}
935
930
  >
936
- 邀请成员
931
+ 确认
937
932
  </button>
938
933
  `
939
934
  }
@@ -1137,17 +1132,11 @@ export class InternalOrganizationOnboarding extends LitElement {
1137
1132
  }
1138
1133
  }
1139
1134
 
1140
- private readonly requestTeamUpgrade = () => {
1141
- const organizationId = this.createdOrganization?.id
1142
- if (organizationId) {
1143
- this.dispatchPublicEvent('team-upgrade-requested', { organizationId })
1144
- }
1145
- }
1146
-
1147
- private readonly requestTeamInvites = () => {
1135
+ // 团队创建成功后只需确认收尾;升级与邀请成员留在组织管理里,不在此页分叉。
1136
+ private readonly finishOnboarding = () => {
1148
1137
  const organizationId = this.createdOrganization?.id
1149
1138
  if (organizationId) {
1150
- this.dispatchPublicEvent('team-invite-members-requested', { organizationId })
1139
+ this.dispatchPublicEvent('onboarding-finished', { organizationId })
1151
1140
  }
1152
1141
  }
1153
1142
 
@@ -308,6 +308,35 @@ describe('<uniplat-account-context>', () => {
308
308
  expect(context.shadowRoot?.querySelector('[part="onboarding-panel"]')).toBeNull()
309
309
  })
310
310
 
311
+ it('closes the forced modal from the single confirm action on the team success page', async () => {
312
+ const { context } = await mountUnassigned()
313
+ const onboarding = context.shadowRoot?.querySelector('uniplat-internal-organization-onboarding')
314
+ const onboardingRoot = onboarding?.shadowRoot
315
+
316
+ const options = [...(onboardingRoot?.querySelectorAll<HTMLButtonElement>('.option') ?? [])]
317
+ options[1]?.click()
318
+ await onboarding?.updateComplete
319
+ onboardingRoot?.querySelector<HTMLButtonElement>('[part="primary-action"]')?.click()
320
+ await onboarding?.updateComplete
321
+
322
+ const teamName = onboardingRoot?.querySelector<HTMLInputElement>('[part="input"]')
323
+ if (!teamName) throw new Error('Missing team name input')
324
+ teamName.value = '更更小的团队'
325
+ teamName.dispatchEvent(new Event('input', { bubbles: true }))
326
+ await onboarding?.updateComplete
327
+ onboardingRoot?.querySelector('form')?.dispatchEvent(new Event('submit', { bubbles: true }))
328
+
329
+ await vi.waitFor(() => expect(onboardingRoot?.textContent).toContain('已创建'))
330
+ const actions = [
331
+ ...(onboardingRoot?.querySelectorAll<HTMLButtonElement>('.success-actions button') ?? []),
332
+ ]
333
+ expect(actions.map((button) => button.textContent?.trim())).toEqual(['确认'])
334
+
335
+ actions[0]?.click()
336
+ await settle(context)
337
+ expect(context.shadowRoot?.querySelector('dialog.onboarding-modal')).toBeNull()
338
+ })
339
+
311
340
  it('centers the optional onboarding flow in a dialog that can still be dismissed', async () => {
312
341
  const context = document.createElement(ACCOUNT_CONTEXT_TAG)
313
342
  context.configure({
@@ -195,10 +195,8 @@ describe('internal organization onboarding', () => {
195
195
  expect(join).toHaveBeenCalledTimes(1)
196
196
 
197
197
  const teamOnboarding = document.createElement(INTERNAL_ORGANIZATION_ONBOARDING_TAG)
198
- const upgrade = vi.fn()
199
- const invite = vi.fn()
200
- teamOnboarding.addEventListener('team-upgrade-requested', upgrade)
201
- teamOnboarding.addEventListener('team-invite-members-requested', invite)
198
+ const finished = vi.fn()
199
+ teamOnboarding.addEventListener('onboarding-finished', finished)
202
200
  teamOnboarding.configure({ transport: createTransport() })
203
201
  document.body.append(teamOnboarding)
204
202
  await settle(teamOnboarding)
@@ -208,10 +206,15 @@ describe('internal organization onboarding', () => {
208
206
  updateInput(teamOnboarding, '#team-name', '星河增长项目组')
209
207
  submit(teamOnboarding)
210
208
  await vi.waitFor(() => expect(teamOnboarding.shadowRoot?.textContent).toContain('无需认证'))
211
- buttonByText(teamOnboarding, '升级为企业').click()
212
- buttonByText(teamOnboarding, '邀请成员').click()
213
- expect(upgrade.mock.calls[0]?.[0].detail).toEqual({ organizationId: 'team-1' })
214
- expect(invite.mock.calls[0]?.[0].detail).toEqual({ organizationId: 'team-1' })
209
+ const confirmActions = teamOnboarding.shadowRoot?.querySelector('.success-actions')
210
+ const actionLabels = [...(confirmActions?.querySelectorAll('button') ?? [])].map((button) =>
211
+ button.textContent?.trim(),
212
+ )
213
+ expect(actionLabels).toEqual(['确认'])
214
+ expect(confirmActions?.classList.contains('single')).toBe(true)
215
+ expect(confirmActions?.querySelectorAll('button')).toHaveLength(1)
216
+ buttonByText(teamOnboarding, '确认').click()
217
+ expect(finished.mock.calls[0]?.[0].detail).toEqual({ organizationId: 'team-1' })
215
218
 
216
219
  const enterpriseOnboarding = document.createElement(INTERNAL_ORGANIZATION_ONBOARDING_TAG)
217
220
  const authenticate = vi.fn()