@csntgao/uni-base 0.4.0 → 0.5.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 +2 -2
- package/CLAUDE.md +2 -2
- package/README.md +2 -0
- package/package.json +1 -1
- package/packages/web-components/package.json +1 -1
- package/packages/web-components/src/account-context.ts +28 -29
- package/packages/web-components/src/organization-multi-employee-selector.ts +6 -1
- package/packages/web-components/src/organization-onboarding.ts +26 -54
- package/packages/web-components/src/organization-selector.ts +7 -1
- package/packages/web-components/tests/account-context.test.ts +32 -1
- package/packages/web-components/tests/organization-onboarding.test.ts +10 -4
- package//344/272/244/346/216/245/346/226/207/346/241/243.md +50 -23
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
|
|
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 实现。
|
|
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
|
|
|
@@ -439,7 +439,7 @@ npm 包名、导出类型名、Custom Element 标签、`configure()` 结构、
|
|
|
439
439
|
- Organization Selector 需测试完整列表、企业认证与团队类型样式、脱敏账户摘要与完整手机号拒绝、按名称和统一社会信用代码搜索且代码不进入 DOM、类型筛选与常显计数、“最近进入 / 全部组织”分组与默认选中、带目标名称的主按钮、无匹配与空归属状态、记住偏好、创建/进入/退出登录事件、`modal` 强制态无关闭按钮且遮罩点击与 `Escape` 都不关闭并在移除后解除 `Escape` 拦截、非法 `presentation` 与重复 ID 拒绝和普通文本插值;事件只包含稳定组织 ID 与偏好,不含姓名、手机号、组织名称或信用代码。`resolveOrganizationEntryDecision()` 需测试 0 个归属、单一归属、命中与失效的记住选择、2 个及以上的默认选中项和非法输入拒绝。
|
|
440
440
|
- Organization Multi Employee Selector 需测试部门树、员工分页和搜索、初始选择补取、跨部门按员工 ID 去重、多选上限、受保护项、确认/取消事件、独立滚动、移动视图、错误重试、身份切换清理,以及 JWT、完整手机号、身份字段和后端错误不进入 DOM、事件、URL或日志。专用 transport 必须测试每次请求重新读取 JWT、只使用 Authorization header、组织级 directory scope 和无调用方身份参数。
|
|
441
441
|
- Organization Single Employee Selector 需测试独立标签与导出、初始单个 ID 回显、新选择替换旧选择、未选时禁止确认、受保护项、标量确认 detail、拒绝多选打开参数,以及与多选组件相同的数据和认证安全边界。
|
|
442
|
-
- Account Context
|
|
442
|
+
- Account Context 需测试入驻流程始终渲染为居中的原生对话框而非下拉浮层、普通态可由遮罩点击关闭且强制态不可、已归属用户显示“取消”而无归属用户不显示、必填应用名、组件通过 transport 自行加载用户资料与完整应用组织投影、无归属时仍渲染账户胶囊但不显示工作台入口、带 `code` 的邀请页豁免强制入驻且邀请码不进 DOM、缺 `code` 或其他路径仍强制、离开邀请页后重新强制、Host 自定义 `invitePaths` 生效、企业五种认证状态和团队状态、当前组织员工身份优先及无组织用户身份回退、安全头像与加载失败回退、胶囊内姓名 6 字与组织名 8 字截断及完整 `title`、未超长时不加 `title`、工作台、账户菜单与内置入驻浮层互斥、浮层不改变导航布局、内嵌应用九宫格、创建入口直接启动内置入驻、Host 响应组织切换事件、退出和公共事件安全。专用 UniplatBase transport 还必须测试不请求 `profile_summary_v2`、从 `list_available_organizations` 顶层读取用户资料、按 `application_name` 加载投影、Open Application 的 `organization_oid` 切换、组织级应用目标 `xid` 选择、JWT 只进入 Authorization header 和 Host `setAccessToken()`、响应净化、稳定错误以及公共数据无 JWT;组件范围不得扩展为品牌或完整导航。
|
|
443
443
|
- 通知中心需测试组件注册与导出、两栏首版输出、数据驱动分类页签(`includeTypeFacets` 随收件箱请求、后端 `type_facets` 优先、无 facet 回退累积、筛选后页签不塌缩、展示名取 `notification_type_name` 且为空时回退原始 code、契约四个码不得残留内置中文名、实时新分类入目录)、未读角标与 `unread-count-change`、打开未读即标记已读、全部已读、加载更多、关联资源事件只含安全业务标识、附件经 transport 下载、Markdown 净化、`applyRealtimeEvent()` 不建立任何 WebSocket 且原地更新列表与未读数、事件无法映射时降级刷新、收件箱在途时延后 resync 不丢事件、`configure()` 之前与移除之后喂入事件无副作用、错误横幅为净化文案,以及 JWT 与后端原始正文不进入 DOM、公共事件或日志。小型通知浮层还需测试初始关闭、4 条未读投影、未读总数、严格原型结构与 SVG 图标、单条/全部已读、完整通知页意图事件只含稳定 ID、外部关闭、空态以及 Host 实时事件更新。
|
|
444
444
|
- 使用 fake timer 的测试必须恢复 timer;构造 fake WebSocket 时不要把测试凭据写入 URL。
|
|
445
445
|
- 修复竞态或生命周期问题时增加回归测试,不只验证最终 DOM。
|
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
|
|
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 实现。
|
|
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
|
|
|
@@ -439,7 +439,7 @@ npm 包名、导出类型名、Custom Element 标签、`configure()` 结构、
|
|
|
439
439
|
- Organization Selector 需测试完整列表、企业认证与团队类型样式、脱敏账户摘要与完整手机号拒绝、按名称和统一社会信用代码搜索且代码不进入 DOM、类型筛选与常显计数、“最近进入 / 全部组织”分组与默认选中、带目标名称的主按钮、无匹配与空归属状态、记住偏好、创建/进入/退出登录事件、`modal` 强制态无关闭按钮且遮罩点击与 `Escape` 都不关闭并在移除后解除 `Escape` 拦截、非法 `presentation` 与重复 ID 拒绝和普通文本插值;事件只包含稳定组织 ID 与偏好,不含姓名、手机号、组织名称或信用代码。`resolveOrganizationEntryDecision()` 需测试 0 个归属、单一归属、命中与失效的记住选择、2 个及以上的默认选中项和非法输入拒绝。
|
|
440
440
|
- Organization Multi Employee Selector 需测试部门树、员工分页和搜索、初始选择补取、跨部门按员工 ID 去重、多选上限、受保护项、确认/取消事件、独立滚动、移动视图、错误重试、身份切换清理,以及 JWT、完整手机号、身份字段和后端错误不进入 DOM、事件、URL或日志。专用 transport 必须测试每次请求重新读取 JWT、只使用 Authorization header、组织级 directory scope 和无调用方身份参数。
|
|
441
441
|
- Organization Single Employee Selector 需测试独立标签与导出、初始单个 ID 回显、新选择替换旧选择、未选时禁止确认、受保护项、标量确认 detail、拒绝多选打开参数,以及与多选组件相同的数据和认证安全边界。
|
|
442
|
-
- Account Context
|
|
442
|
+
- Account Context 需测试入驻流程始终渲染为居中的原生对话框而非下拉浮层、普通态可由遮罩点击关闭且强制态不可、已归属用户显示“取消”而无归属用户不显示、必填应用名、组件通过 transport 自行加载用户资料与完整应用组织投影、无归属时仍渲染账户胶囊但不显示工作台入口、带 `code` 的邀请页豁免强制入驻且邀请码不进 DOM、缺 `code` 或其他路径仍强制、离开邀请页后重新强制、Host 自定义 `invitePaths` 生效、企业五种认证状态和团队状态、当前组织员工身份优先及无组织用户身份回退、安全头像与加载失败回退、胶囊内姓名 6 字与组织名 8 字截断及完整 `title`、未超长时不加 `title`、工作台、账户菜单与内置入驻浮层互斥、浮层不改变导航布局、内嵌应用九宫格、创建入口直接启动内置入驻、Host 响应组织切换事件、退出和公共事件安全。专用 UniplatBase transport 还必须测试不请求 `profile_summary_v2`、从 `list_available_organizations` 顶层读取用户资料、按 `application_name` 加载投影、Open Application 的 `organization_oid` 切换、组织级应用目标 `xid` 选择、JWT 只进入 Authorization header 和 Host `setAccessToken()`、响应净化、稳定错误以及公共数据无 JWT;组件范围不得扩展为品牌或完整导航。
|
|
443
443
|
- 通知中心需测试组件注册与导出、两栏首版输出、数据驱动分类页签(`includeTypeFacets` 随收件箱请求、后端 `type_facets` 优先、无 facet 回退累积、筛选后页签不塌缩、展示名取 `notification_type_name` 且为空时回退原始 code、契约四个码不得残留内置中文名、实时新分类入目录)、未读角标与 `unread-count-change`、打开未读即标记已读、全部已读、加载更多、关联资源事件只含安全业务标识、附件经 transport 下载、Markdown 净化、`applyRealtimeEvent()` 不建立任何 WebSocket 且原地更新列表与未读数、事件无法映射时降级刷新、收件箱在途时延后 resync 不丢事件、`configure()` 之前与移除之后喂入事件无副作用、错误横幅为净化文案,以及 JWT 与后端原始正文不进入 DOM、公共事件或日志。小型通知浮层还需测试初始关闭、4 条未读投影、未读总数、严格原型结构与 SVG 图标、单条/全部已读、完整通知页意图事件只含稳定 ID、外部关闭、空态以及 Host 实时事件更新。
|
|
444
444
|
- 使用 fake timer 的测试必须恢复 timer;构造 fake WebSocket 时不要把测试凭据写入 URL。
|
|
445
445
|
- 修复竞态或生命周期问题时增加回归测试,不只验证最终 DOM。
|
package/README.md
CHANGED
|
@@ -298,6 +298,8 @@ accountContext.configure({
|
|
|
298
298
|
|
|
299
299
|
该 transport 复用 `POST system.org/create_organization`。企业请求映射为 `organization_type/team_name/customer_name/credit_code`,团队请求只发送 `organization_type/team_name`;不会提交 `uid/oid/eid/owner` 等身份字段。它在每次创建前重新调用 `getAccessToken()`,JWT 只进入 `Authorization: Bearer` header,成功响应只保留组件需要的组织 ID、展示名称和类型。`INVALID_ARGUMENT`、`ORGANIZATION_CREATE_FAILED`、HTTP、解析及 token provider 异常均收敛为 `UNKNOWN`,不透传后端正文。
|
|
300
300
|
|
|
301
|
+
入驻流程一律以原生 `dialog.showModal()` 在视口居中呈现(top layer,不受 Host 导航 `transform`/`overflow` 影响),不再使用锚定账户胶囊的下拉浮层;账户菜单里点“创建团队 / 企业”打开的普通态同样居中,可由遮罩点击、`Escape` 或右上角“取消”关闭。“取消”只在当前用户已归属某个组织时渲染,无归属用户看不到该按钮;点击后直接收起流程,不再经过“确定暂不创建”确认页。
|
|
302
|
+
|
|
301
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 定制该浮层或对话框。
|
|
302
304
|
|
|
303
305
|
个人用户不允许以非员工身份留在系统里,所以无归属用户必须先入驻,没有跳过开关。账户投影返回零归属时,组件用原生 `dialog.showModal()` 自动打开强制态创建流程:
|
package/package.json
CHANGED
|
@@ -459,10 +459,6 @@ export class UniplatAccountContext extends LitElement {
|
|
|
459
459
|
width: min(328px, calc(100vw - 24px));
|
|
460
460
|
}
|
|
461
461
|
|
|
462
|
-
.onboarding-panel {
|
|
463
|
-
width: min(400px, calc(100vw - 24px));
|
|
464
|
-
}
|
|
465
|
-
|
|
466
462
|
/*
|
|
467
463
|
* 强制入驻使用原生 dialog 的 top layer,不受 Host 导航 transform / overflow 影响;
|
|
468
464
|
* 无关闭按钮,遮罩点击与 Escape 都不关闭。
|
|
@@ -488,7 +484,7 @@ export class UniplatAccountContext extends LitElement {
|
|
|
488
484
|
background: rgb(20 35 56 / 56%);
|
|
489
485
|
}
|
|
490
486
|
|
|
491
|
-
.onboarding-
|
|
487
|
+
.onboarding-dialog {
|
|
492
488
|
width: min(400px, 100%);
|
|
493
489
|
}
|
|
494
490
|
|
|
@@ -1079,6 +1075,9 @@ export class UniplatAccountContext extends LitElement {
|
|
|
1079
1075
|
transport,
|
|
1080
1076
|
initialStep: 'method-selection',
|
|
1081
1077
|
mandatory: this.mandatoryOnboarding,
|
|
1078
|
+
// 取消只在用户已归属某个组织时提供;配置只在面板打开时取一次,
|
|
1079
|
+
// 创建成功后不重新配置,避免把成功页重置回创建方式选择。
|
|
1080
|
+
canCancel: (this.viewModel?.organizations.length ?? 0) > 0,
|
|
1082
1081
|
})
|
|
1083
1082
|
this.configuredOrganizationOnboarding = onboarding
|
|
1084
1083
|
this.configuredOnboardingMandatory = this.mandatoryOnboarding
|
|
@@ -1151,31 +1150,24 @@ export class UniplatAccountContext extends LitElement {
|
|
|
1151
1150
|
</div>`
|
|
1152
1151
|
}
|
|
1153
1152
|
if (this.activePanel === 'onboarding' && this.organizationOnboardingTransport) {
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
class="onboarding-modal"
|
|
1158
|
-
part="onboarding-panel"
|
|
1159
|
-
aria-label="创建团队或企业"
|
|
1160
|
-
@cancel=${this.blockMandatoryOnboardingCancel}
|
|
1161
|
-
@onboarding-skipped=${this.handleOnboardingSkipped}
|
|
1162
|
-
@organization-created=${this.refreshAfterOrganizationCreated}
|
|
1163
|
-
@enterprise-authentication-requested=${this.finishMandatoryOnboarding}
|
|
1164
|
-
@enterprise-authentication-deferred=${this.finishMandatoryOnboarding}
|
|
1165
|
-
@team-upgrade-requested=${this.finishMandatoryOnboarding}
|
|
1166
|
-
@team-invite-members-requested=${this.finishMandatoryOnboarding}
|
|
1167
|
-
>
|
|
1168
|
-
${onboarding}
|
|
1169
|
-
</dialog>`
|
|
1170
|
-
}
|
|
1171
|
-
return html`<div
|
|
1172
|
-
class="floating-panel onboarding-panel"
|
|
1153
|
+
// 入驻流程始终以视口居中的原生对话框呈现;强制态与普通态只在能否关闭上不同。
|
|
1154
|
+
return html`<dialog
|
|
1155
|
+
class="onboarding-modal"
|
|
1173
1156
|
part="onboarding-panel"
|
|
1157
|
+
aria-label="创建团队或企业"
|
|
1158
|
+
@click=${this.closeOnboardingFromBackdrop}
|
|
1159
|
+
@cancel=${this.handleOnboardingCancel}
|
|
1174
1160
|
@onboarding-skipped=${this.handleOnboardingSkipped}
|
|
1175
1161
|
@organization-created=${this.refreshAfterOrganizationCreated}
|
|
1162
|
+
@enterprise-authentication-requested=${this.finishMandatoryOnboarding}
|
|
1163
|
+
@enterprise-authentication-deferred=${this.finishMandatoryOnboarding}
|
|
1164
|
+
@team-upgrade-requested=${this.finishMandatoryOnboarding}
|
|
1165
|
+
@team-invite-members-requested=${this.finishMandatoryOnboarding}
|
|
1176
1166
|
>
|
|
1177
|
-
|
|
1178
|
-
|
|
1167
|
+
<div class="onboarding-dialog" @click=${stopEventPropagation}>
|
|
1168
|
+
<uniplat-internal-organization-onboarding></uniplat-internal-organization-onboarding>
|
|
1169
|
+
</div>
|
|
1170
|
+
</dialog>`
|
|
1179
1171
|
}
|
|
1180
1172
|
if (this.activePanel === 'account') {
|
|
1181
1173
|
return html`<div
|
|
@@ -1325,9 +1317,16 @@ export class UniplatAccountContext extends LitElement {
|
|
|
1325
1317
|
this.activePanel = null
|
|
1326
1318
|
}
|
|
1327
1319
|
|
|
1328
|
-
private readonly
|
|
1329
|
-
// 强制入驻不允许 Escape
|
|
1330
|
-
|
|
1320
|
+
private readonly handleOnboardingCancel = (event: Event): void => {
|
|
1321
|
+
// 强制入驻不允许 Escape 关闭;普通入驻由组件自己收起面板,避免 dialog 与状态不同步。
|
|
1322
|
+
event.preventDefault()
|
|
1323
|
+
if (this.onboardingLocked) return
|
|
1324
|
+
this.activePanel = null
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
private readonly closeOnboardingFromBackdrop = (): void => {
|
|
1328
|
+
if (this.onboardingLocked) return
|
|
1329
|
+
this.activePanel = null
|
|
1331
1330
|
}
|
|
1332
1331
|
|
|
1333
1332
|
private readonly handleOnboardingSkipped = (): void => {
|
|
@@ -381,6 +381,11 @@ export class UniplatOrganizationMultiEmployeeSelectorElement extends LitElement
|
|
|
381
381
|
.checkbox {
|
|
382
382
|
border-radius: 5px;
|
|
383
383
|
}
|
|
384
|
+
.selection-indicator svg {
|
|
385
|
+
width: 12px;
|
|
386
|
+
height: 12px;
|
|
387
|
+
stroke-width: 2.4;
|
|
388
|
+
}
|
|
384
389
|
.selected .checkbox {
|
|
385
390
|
border-color: var(--uniplat-employee-selector-primary);
|
|
386
391
|
color: #fff;
|
|
@@ -1549,7 +1554,7 @@ function icon(
|
|
|
1549
1554
|
return html`<svg viewBox="0 0 24 24" aria-hidden="true"><path d="m6 6 12 12M18 6 6 18" /></svg>`
|
|
1550
1555
|
}
|
|
1551
1556
|
if (name === 'check') {
|
|
1552
|
-
return html`<svg viewBox="0 0 24 24" aria-hidden="true"><path d="
|
|
1557
|
+
return html`<svg viewBox="0 0 24 24" aria-hidden="true"><path d="m6 12 4 4 8-8" /></svg>`
|
|
1553
1558
|
}
|
|
1554
1559
|
if (name === 'organization') {
|
|
1555
1560
|
return html`<svg viewBox="0 0 24 24" aria-hidden="true">
|
|
@@ -8,7 +8,6 @@ import {
|
|
|
8
8
|
export type OrganizationOnboardingStep =
|
|
9
9
|
| 'unassigned'
|
|
10
10
|
| 'method-selection'
|
|
11
|
-
| 'skip-confirmation'
|
|
12
11
|
| 'enterprise-form'
|
|
13
12
|
| 'enterprise-success'
|
|
14
13
|
| 'team-form'
|
|
@@ -21,6 +20,10 @@ export interface OrganizationOnboardingConfig {
|
|
|
21
20
|
* 强制入驻:隐藏所有跳过入口,无归属用户只能创建组织或退出登录。
|
|
22
21
|
*/
|
|
23
22
|
mandatory?: boolean
|
|
23
|
+
/**
|
|
24
|
+
* 当前用户已归属某个组织时才允许取消创建;无归属用户没有取消入口。
|
|
25
|
+
*/
|
|
26
|
+
canCancel?: boolean
|
|
24
27
|
}
|
|
25
28
|
|
|
26
29
|
export interface OrganizationOnboardingEventMap {
|
|
@@ -243,6 +246,12 @@ export class InternalOrganizationOnboarding extends LitElement {
|
|
|
243
246
|
background: var(--uniplat-onboarding-primary);
|
|
244
247
|
}
|
|
245
248
|
|
|
249
|
+
.selected-mark svg {
|
|
250
|
+
width: 12px;
|
|
251
|
+
height: 12px;
|
|
252
|
+
stroke-width: 2.4;
|
|
253
|
+
}
|
|
254
|
+
|
|
246
255
|
.primary,
|
|
247
256
|
.secondary {
|
|
248
257
|
display: inline-flex;
|
|
@@ -472,8 +481,8 @@ export class InternalOrganizationOnboarding extends LitElement {
|
|
|
472
481
|
declare private createdOrganization: CreatedOnboardingOrganization | null
|
|
473
482
|
|
|
474
483
|
private transport: OrganizationOnboardingTransport | null = null
|
|
475
|
-
private skipReturnStep: SkippableStep = 'method-selection'
|
|
476
484
|
private mandatory = false
|
|
485
|
+
private canCancel = false
|
|
477
486
|
|
|
478
487
|
constructor() {
|
|
479
488
|
super()
|
|
@@ -503,8 +512,12 @@ export class InternalOrganizationOnboarding extends LitElement {
|
|
|
503
512
|
if (config.mandatory !== undefined && typeof config.mandatory !== 'boolean') {
|
|
504
513
|
throw new TypeError('organization-onboarding mandatory must be a boolean')
|
|
505
514
|
}
|
|
515
|
+
if (config.canCancel !== undefined && typeof config.canCancel !== 'boolean') {
|
|
516
|
+
throw new TypeError('organization-onboarding canCancel must be a boolean')
|
|
517
|
+
}
|
|
506
518
|
this.transport = config.transport
|
|
507
519
|
this.mandatory = config.mandatory ?? false
|
|
520
|
+
this.canCancel = config.canCancel ?? false
|
|
508
521
|
// 强制入驻不展示带“暂不创建”的无归属提示,直接进入创建流程。
|
|
509
522
|
this.reset(this.mandatory ? 'method-selection' : (config.initialStep ?? 'method-selection'))
|
|
510
523
|
}
|
|
@@ -518,25 +531,25 @@ export class InternalOrganizationOnboarding extends LitElement {
|
|
|
518
531
|
if (!this.transport) return nothing
|
|
519
532
|
if (this.step === 'unassigned') return this.renderUnassigned()
|
|
520
533
|
if (this.step === 'method-selection') return this.renderMethodSelection()
|
|
521
|
-
if (this.step === 'skip-confirmation') return this.renderSkipConfirmation()
|
|
522
534
|
if (this.step === 'enterprise-form') return this.renderEnterpriseForm()
|
|
523
535
|
if (this.step === 'team-form') return this.renderTeamForm()
|
|
524
536
|
return this.renderSuccess()
|
|
525
537
|
}
|
|
526
538
|
|
|
527
539
|
/**
|
|
528
|
-
*
|
|
529
|
-
*
|
|
540
|
+
* 已归属组织的用户可以“取消”本次创建;无归属用户没有取消入口。强制入驻时首屏
|
|
541
|
+
* 提供“退出登录”,表单页提供“返回”回到创建方式选择,用户不会被困在某个分支里。
|
|
530
542
|
*/
|
|
531
543
|
private renderHeadingAction(step: SkippableStep) {
|
|
532
544
|
if (!this.mandatory) {
|
|
545
|
+
if (!this.canCancel) return nothing
|
|
533
546
|
return html`<button
|
|
534
547
|
class="skip"
|
|
535
|
-
part="
|
|
548
|
+
part="cancel-action"
|
|
536
549
|
type="button"
|
|
537
|
-
@click=${() => this.
|
|
550
|
+
@click=${() => this.cancelOnboarding(step)}
|
|
538
551
|
>
|
|
539
|
-
|
|
552
|
+
取消
|
|
540
553
|
</button>`
|
|
541
554
|
}
|
|
542
555
|
if (step === 'method-selection') {
|
|
@@ -667,39 +680,6 @@ export class InternalOrganizationOnboarding extends LitElement {
|
|
|
667
680
|
`
|
|
668
681
|
}
|
|
669
682
|
|
|
670
|
-
private renderSkipConfirmation() {
|
|
671
|
-
return html`
|
|
672
|
-
<section class="card" part="shell" aria-label="确认跳过入驻">
|
|
673
|
-
<div class="confirmation" part="skip-confirmation">
|
|
674
|
-
<div class="confirmation-icon">${icon('alert')}</div>
|
|
675
|
-
<h2 part="title">确定暂不创建团队 / 企业?</h2>
|
|
676
|
-
<p class="description" part="description">
|
|
677
|
-
跳过后你仍可浏览官网与服务介绍,但无法办理社保、薪酬、用工等业务。随时可在顶部导航“创建团队
|
|
678
|
-
/ 企业”补充。
|
|
679
|
-
</p>
|
|
680
|
-
<div class="confirmation-actions">
|
|
681
|
-
<button
|
|
682
|
-
class="secondary"
|
|
683
|
-
part="secondary-action"
|
|
684
|
-
type="button"
|
|
685
|
-
@click=${this.confirmSkip}
|
|
686
|
-
>
|
|
687
|
-
跳过,先逛逛
|
|
688
|
-
</button>
|
|
689
|
-
<button
|
|
690
|
-
class="primary"
|
|
691
|
-
part="primary-action"
|
|
692
|
-
type="button"
|
|
693
|
-
@click=${this.continueCreating}
|
|
694
|
-
>
|
|
695
|
-
继续创建
|
|
696
|
-
</button>
|
|
697
|
-
</div>
|
|
698
|
-
</div>
|
|
699
|
-
</section>
|
|
700
|
-
`
|
|
701
|
-
}
|
|
702
|
-
|
|
703
683
|
private renderEnterpriseForm() {
|
|
704
684
|
const errorCount = [
|
|
705
685
|
this.enterpriseErrors.organizationName,
|
|
@@ -984,24 +964,16 @@ export class InternalOrganizationOnboarding extends LitElement {
|
|
|
984
964
|
this.step = this.choice === 'enterprise' ? 'enterprise-form' : 'team-form'
|
|
985
965
|
}
|
|
986
966
|
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
this.
|
|
990
|
-
this.
|
|
967
|
+
// 取消只对已归属用户开放,直接收起创建流程,不再走“确定暂不创建”的确认页。
|
|
968
|
+
private cancelOnboarding(step: SkippableStep) {
|
|
969
|
+
if (this.mandatory || !this.canCancel) return
|
|
970
|
+
this.dispatchPublicEvent('onboarding-skipped', { source: step })
|
|
991
971
|
}
|
|
992
972
|
|
|
993
973
|
private readonly requestLogout = () => {
|
|
994
974
|
this.dispatchPublicEvent('logout-requested', {})
|
|
995
975
|
}
|
|
996
976
|
|
|
997
|
-
private readonly continueCreating = () => {
|
|
998
|
-
this.step = this.skipReturnStep
|
|
999
|
-
}
|
|
1000
|
-
|
|
1001
|
-
private readonly confirmSkip = () => {
|
|
1002
|
-
this.dispatchPublicEvent('onboarding-skipped', { source: this.skipReturnStep })
|
|
1003
|
-
}
|
|
1004
|
-
|
|
1005
977
|
private readonly skipUnassigned = () => {
|
|
1006
978
|
this.dispatchPublicEvent('onboarding-skipped', { source: 'unassigned' })
|
|
1007
979
|
}
|
|
@@ -1242,5 +1214,5 @@ function icon(name: IconName): TemplateResult {
|
|
|
1242
1214
|
<path d="M12 3 2.8 20h18.4zM12 9v4M12 17h.01" />
|
|
1243
1215
|
</svg>`
|
|
1244
1216
|
}
|
|
1245
|
-
return html`<svg viewBox="0 0 24 24" aria-hidden="true"><path d="
|
|
1217
|
+
return html`<svg viewBox="0 0 24 24" aria-hidden="true"><path d="m6 12 4 4 8-8" /></svg>`
|
|
1246
1218
|
}
|
|
@@ -354,6 +354,12 @@ export class UniplatOrganizationSelector extends LitElement {
|
|
|
354
354
|
background: var(--uniplat-organization-selector-primary);
|
|
355
355
|
}
|
|
356
356
|
|
|
357
|
+
.organization.selected .indicator svg {
|
|
358
|
+
width: 12px;
|
|
359
|
+
height: 12px;
|
|
360
|
+
stroke-width: 2.4;
|
|
361
|
+
}
|
|
362
|
+
|
|
357
363
|
.footer-row {
|
|
358
364
|
display: flex;
|
|
359
365
|
align-items: center;
|
|
@@ -926,5 +932,5 @@ function icon(name: OrganizationSelectorType | 'check' | 'chevron' | 'search'):
|
|
|
926
932
|
<path d="M21 21l-6 -6" />
|
|
927
933
|
</svg>`
|
|
928
934
|
}
|
|
929
|
-
return html`<svg viewBox="0 0 24 24" aria-hidden="true"><path d="
|
|
935
|
+
return html`<svg viewBox="0 0 24 24" aria-hidden="true"><path d="m6 12 4 4 8-8" /></svg>`
|
|
930
936
|
}
|
|
@@ -240,9 +240,10 @@ describe('<uniplat-account-context>', () => {
|
|
|
240
240
|
const onboardingRoot = onboarding?.shadowRoot
|
|
241
241
|
expect(onboardingRoot?.textContent).toContain('创建你的团队 / 企业')
|
|
242
242
|
expect(onboardingRoot?.textContent).not.toContain('跳过')
|
|
243
|
+
expect(onboardingRoot?.textContent).not.toContain('取消')
|
|
243
244
|
expect(onboardingRoot?.textContent).not.toContain('暂不创建')
|
|
244
245
|
expect(onboardingRoot?.textContent).not.toContain('先逛逛')
|
|
245
|
-
expect(onboardingRoot?.querySelector('[part="
|
|
246
|
+
expect(onboardingRoot?.querySelector('[part="cancel-action"]')).toBeNull()
|
|
246
247
|
|
|
247
248
|
const logoutAction = onboardingRoot?.querySelector<HTMLButtonElement>('[part="logout-action"]')
|
|
248
249
|
expect(logoutAction?.textContent?.trim()).toBe('退出登录')
|
|
@@ -307,6 +308,36 @@ describe('<uniplat-account-context>', () => {
|
|
|
307
308
|
expect(context.shadowRoot?.querySelector('[part="onboarding-panel"]')).toBeNull()
|
|
308
309
|
})
|
|
309
310
|
|
|
311
|
+
it('centers the optional onboarding flow in a dialog that can still be dismissed', async () => {
|
|
312
|
+
const context = document.createElement(ACCOUNT_CONTEXT_TAG)
|
|
313
|
+
context.configure({
|
|
314
|
+
applicationName: APPLICATION_NAME,
|
|
315
|
+
transport: { load: async () => serverProjection() },
|
|
316
|
+
organizationOnboarding: { transport: createMockOrganizationOnboardingTransport() },
|
|
317
|
+
})
|
|
318
|
+
document.body.append(context)
|
|
319
|
+
await vi.waitFor(() => expect(context.shadowRoot?.textContent).toContain('企业 A'))
|
|
320
|
+
|
|
321
|
+
context.startOrganizationOnboarding()
|
|
322
|
+
await settle(context)
|
|
323
|
+
|
|
324
|
+
const modal = context.shadowRoot?.querySelector<HTMLDialogElement>('dialog.onboarding-modal')
|
|
325
|
+
expect(modal).not.toBeNull()
|
|
326
|
+
expect(modal?.hasAttribute('open')).toBe(true)
|
|
327
|
+
expect(context.shadowRoot?.querySelector('.floating-panel')).toBeNull()
|
|
328
|
+
const onboardingRoot = context.shadowRoot?.querySelector(
|
|
329
|
+
'uniplat-internal-organization-onboarding',
|
|
330
|
+
)?.shadowRoot
|
|
331
|
+
const cancel = onboardingRoot?.querySelector('[part="cancel-action"]')
|
|
332
|
+
expect(cancel?.textContent?.trim()).toBe('取消')
|
|
333
|
+
expect(onboardingRoot?.textContent).not.toContain('跳过')
|
|
334
|
+
|
|
335
|
+
// 有归属用户点开创建流程时,遮罩点击可以关闭。
|
|
336
|
+
modal?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
|
|
337
|
+
await settle(context)
|
|
338
|
+
expect(context.shadowRoot?.querySelector('dialog.onboarding-modal')).toBeNull()
|
|
339
|
+
})
|
|
340
|
+
|
|
310
341
|
it('exempts the organization invite page so the join flow is not covered', async () => {
|
|
311
342
|
history.replaceState(null, '', '/org-invite?code=INVITE-CODE-1234')
|
|
312
343
|
try {
|
|
@@ -165,7 +165,7 @@ describe('internal organization onboarding', () => {
|
|
|
165
165
|
)
|
|
166
166
|
})
|
|
167
167
|
|
|
168
|
-
it('emits
|
|
168
|
+
it('emits cancel, join, authentication, upgrade and invitation actions', async () => {
|
|
169
169
|
const onboarding = document.createElement(INTERNAL_ORGANIZATION_ONBOARDING_TAG)
|
|
170
170
|
const skipped = vi.fn()
|
|
171
171
|
const join = vi.fn()
|
|
@@ -175,11 +175,17 @@ describe('internal organization onboarding', () => {
|
|
|
175
175
|
document.body.append(onboarding)
|
|
176
176
|
await settle(onboarding)
|
|
177
177
|
|
|
178
|
-
|
|
178
|
+
// 无归属用户没有取消入口。
|
|
179
|
+
expect(onboarding.shadowRoot?.querySelector('[part="cancel-action"]')).toBeNull()
|
|
180
|
+
expect(onboarding.shadowRoot?.textContent).not.toContain('跳过')
|
|
181
|
+
|
|
182
|
+
onboarding.configure({ transport: createTransport(), canCancel: true })
|
|
179
183
|
await settle(onboarding)
|
|
180
|
-
|
|
181
|
-
|
|
184
|
+
const cancel = onboarding.shadowRoot?.querySelector<HTMLButtonElement>('[part="cancel-action"]')
|
|
185
|
+
expect(cancel?.textContent?.trim()).toBe('取消')
|
|
186
|
+
cancel?.click()
|
|
182
187
|
expect(skipped.mock.calls[0]?.[0].detail).toEqual({ source: 'method-selection' })
|
|
188
|
+
expect(onboarding.shadowRoot?.textContent).not.toContain('确定暂不创建团队 / 企业')
|
|
183
189
|
|
|
184
190
|
onboarding.start('unassigned')
|
|
185
191
|
await settle(onboarding)
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
>
|
|
7
7
|
> 分支:`main`
|
|
8
8
|
>
|
|
9
|
-
> 交接基线提交:`
|
|
9
|
+
> 交接基线提交:`2aadb87 feat(web-components): 组织邀请页豁免强制入驻`
|
|
10
10
|
|
|
11
11
|
## 1. Claude 接手后的第一步
|
|
12
12
|
|
|
@@ -23,9 +23,9 @@ git log -5 --oneline --decorate
|
|
|
23
23
|
交接文档更新前的 Git 状态为:
|
|
24
24
|
|
|
25
25
|
```text
|
|
26
|
-
main
|
|
26
|
+
main 与 origin/main 同步
|
|
27
27
|
工作树无代码改动
|
|
28
|
-
HEAD =
|
|
28
|
+
HEAD = 2aadb87
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
不要清理、覆盖或提交根目录已有的 `.tgz`。它们是历史及当前本地发布产物,已由 `.gitignore` 忽略。
|
|
@@ -38,9 +38,9 @@ HEAD = d19ac72
|
|
|
38
38
|
|
|
39
39
|
| 位置 | npm 名称 | 当前本地版本 | 说明 |
|
|
40
40
|
| ------------------------- | -------------------------------------- | -----------: | --------------------------------------- |
|
|
41
|
-
| 根目录 | `@csntgao/uni-base` | `0.
|
|
41
|
+
| 根目录 | `@csntgao/uni-base` | `0.4.0` | 当前公开发布入口;npmjs 已发布此版本 |
|
|
42
42
|
| `packages/core` | `@uniplat/ai-employee-client` | `0.14.0` | UI 无关的 HTTP/WebSocket/Core 状态机 |
|
|
43
|
-
| `packages/web-components` | `@uniplat/ai-employee-web-components` | `0.
|
|
43
|
+
| `packages/web-components` | `@uniplat/ai-employee-web-components` | `0.27.0` | Lit Web Components 与独立浏览器 runtime |
|
|
44
44
|
| `examples/vanilla` | `@uniplat/ai-employee-vanilla-example` | 私有示例 | 必须消费 workspace 包的构建产物 |
|
|
45
45
|
|
|
46
46
|
注意:根包改名为 `@csntgao/uni-base` 不代表两个 workspace 包、已有 Web Component 标签或公开类型可以随之改名。`@uniplat/*` 名称和 `uniplat-*` 标签仍是公开契约。
|
|
@@ -73,20 +73,47 @@ HEAD = d19ac72
|
|
|
73
73
|
|
|
74
74
|
### 4.-2 无归属用户强制入驻
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
相关提交:
|
|
77
77
|
|
|
78
78
|
```text
|
|
79
79
|
d19ac72 feat(web-components): 无归属用户强制走创建组织流程
|
|
80
|
+
acfeed4 feat(web-components)!: 强制入驻去掉跳过开关并恢复邀请码加入
|
|
81
|
+
efd56ef fix(web-components): 去掉强制入驻邀请码入口并隐藏无身份工作台
|
|
82
|
+
2aadb87 feat(web-components): 组织邀请页豁免强制入驻
|
|
80
83
|
```
|
|
81
84
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
`
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
85
|
+
新运行机制:**个人用户不允许以非员工身份留在系统中**,跳过与“先逛逛”不再存在。当前实现:
|
|
86
|
+
|
|
87
|
+
1. 账户投影零归属时,`<uniplat-account-context>` 用原生 `dialog.showModal()`(top layer,不受
|
|
88
|
+
Host 导航 `transform/overflow` 影响)自动打开强制态创建流程:不渲染跳过、无归属提示、邀请码
|
|
89
|
+
入口与关闭按钮,遮罩点击、`Escape` 与 `cancel` 都不关闭。
|
|
90
|
+
2. 首屏出口只有创建企业、创建团队和“退出登录”(派发 `logout-requested`)。企业/团队表单页的
|
|
91
|
+
“跳过”换成“返回”,创建成功页的认证、升级或邀请动作才关闭对话框。
|
|
92
|
+
3. **没有可跳过开关**:`organizationOnboarding` 只接收 `transport` 与可选 `invitePaths`,历史
|
|
93
|
+
`mandatory: false` 不再放行(有用例守着)。
|
|
94
|
+
4. 没有任何企业员工身份时,账户胶囊仍渲染,但**不显示工作台入口**,即使配置了 `workbench`;
|
|
95
|
+
有员工身份的组织或 Open Application `oid=0` 未选中组织时按既有规则显示。
|
|
96
|
+
5. 组织邀请页豁免:当前 URL 末段路径命中 `invitePaths`(默认 `['org-invite']`)且 `code` 非空时
|
|
97
|
+
不弹对话框,`pathname` 与 hash 路由都参与匹配。组件只取布尔值,邀请码不进状态、DOM、事件或
|
|
98
|
+
日志;缺 `code` 或离开邀请页(`popstate`/`hashchange`)后仍无身份时立即重新强制。
|
|
99
|
+
|
|
100
|
+
该行为**覆盖**了 40.1 原型 4.1 的“暂不创建,先逛逛”、4.2 的个人用户导航态和 S.0 图中
|
|
101
|
+
“0 归属不弹窗”几条既有约定,是按用户新规则做的行为变更,不是实现偏差。
|
|
102
|
+
|
|
103
|
+
按新规则仍未清理完的矛盾(用户已确认审计结论,尚未授权逐项动手):
|
|
104
|
+
|
|
105
|
+
- `organization-onboarding.ts` 的 `unassigned`、`skip-confirmation` 步骤与 `onboarding-skipped`
|
|
106
|
+
公开事件仍在,`start('unassigned')` 能把“先逛逛”拿回来;
|
|
107
|
+
- `account-context` 的个人态样式与 `user-menu` 的“暂无可切换的企业或团队”空态仍在;
|
|
108
|
+
- `user-menu` 在无当前组织时仍渲染“我的员工信息”,而 `account-context.openEmployeeInfo()` 会
|
|
109
|
+
静默关掉菜单,是死入口;
|
|
110
|
+
- Host 未配置 `organizationOnboarding.transport` 或 `transport.load()` 失败时不弹对话框,
|
|
111
|
+
零归属用户会被放行;
|
|
112
|
+
- `resolveOrganizationEntryDecision()` 仍返回 `{ kind: 'personal' }`,官网
|
|
113
|
+
`use-official-website-auth.ts` 已接该分支,改动需两边同步并出需求 v0_2。
|
|
114
|
+
|
|
115
|
+
官网侧仍需实现 `/org-invite?code=...` 页面(校验邀请码、调用加入接口、成功后调用
|
|
116
|
+
`accountContext.refresh()`);SDK 只保证不挡住它。
|
|
90
117
|
|
|
91
118
|
### 4.-1 账户胶囊固定宽度
|
|
92
119
|
|
|
@@ -274,13 +301,13 @@ Account Context 对外只派发净化后的 `LOAD_FAILED`,精确排错应看
|
|
|
274
301
|
|
|
275
302
|
## 7. 当前构建和验证状态
|
|
276
303
|
|
|
277
|
-
在 `
|
|
304
|
+
在 `2aadb87` 上最近一次完整检查结果:
|
|
278
305
|
|
|
279
306
|
```text
|
|
280
307
|
pnpm format:check 通过
|
|
281
308
|
pnpm lint 通过
|
|
282
309
|
pnpm typecheck 通过
|
|
283
|
-
pnpm test 25 个测试文件通过;
|
|
310
|
+
pnpm test 25 个测试文件通过;287 passed,2 skipped
|
|
284
311
|
pnpm build core、web-components、runtime、vanilla 全部通过
|
|
285
312
|
```
|
|
286
313
|
|
|
@@ -288,7 +315,7 @@ pnpm build core、web-components、runtime、vanilla 全部通过
|
|
|
288
315
|
|
|
289
316
|
```text
|
|
290
317
|
/Users/gaostudio/uni-base/uniplat-ai-employee-client-0.14.0.tgz
|
|
291
|
-
/Users/gaostudio/uni-base/uniplat-ai-employee-web-components-0.
|
|
318
|
+
/Users/gaostudio/uni-base/uniplat-ai-employee-web-components-0.27.0.tgz
|
|
292
319
|
```
|
|
293
320
|
|
|
294
321
|
根包本地产物:
|
|
@@ -321,9 +348,9 @@ pnpm --filter @uniplat/ai-employee-web-components pack
|
|
|
321
348
|
|
|
322
349
|
```text
|
|
323
350
|
Manifest: https://10.10.10.131:8787/current.json
|
|
324
|
-
Version: 0.
|
|
325
|
-
Module: https://10.10.10.131:8787/releases/0.
|
|
326
|
-
SHA-256:
|
|
351
|
+
Version: 0.27.0
|
|
352
|
+
Module: https://10.10.10.131:8787/releases/0.27.0/index.js
|
|
353
|
+
SHA-256: a5dfb56d11b60b0e3d475b24b2590751f830806e0907ed7013a38fdf05fae1f5
|
|
327
354
|
```
|
|
328
355
|
|
|
329
356
|
最后验证结果:
|
|
@@ -331,7 +358,7 @@ SHA-256: 3a37ff4225d346b9f32bdc1ee725a9050f5e0b631520f387407067bca6384631
|
|
|
331
358
|
- `current.json`:HTTP 200、`Cache-Control: no-store`;
|
|
332
359
|
- 版本模块:HTTP 200、CORS `*`、`immutable`;
|
|
333
360
|
- 远端模块 hash 与本地一致;
|
|
334
|
-
- `current.json` 指向 `./releases/0.
|
|
361
|
+
- `current.json` 指向 `./releases/0.27.0/index.js`。
|
|
335
362
|
|
|
336
363
|
静态服务未启动时可执行:
|
|
337
364
|
|
|
@@ -353,12 +380,12 @@ https://registry.npmjs.org/
|
|
|
353
380
|
|
|
354
381
|
```text
|
|
355
382
|
npm whoami csntgao(已登录)
|
|
356
|
-
@csntgao/uni-base 0.
|
|
383
|
+
@csntgao/uni-base 0.4.0 已发布并回读校验
|
|
357
384
|
@uniplat/ai-employee-client 未发布,PUT 返回 404(无 scope 创建权限)
|
|
358
385
|
@uniplat/ai-employee-web-components 未发布,同上
|
|
359
386
|
```
|
|
360
387
|
|
|
361
|
-
`@csntgao/uni-base@0.
|
|
388
|
+
`@csntgao/uni-base@0.4.0` 的远端 shasum 为 `51397b9e1f897641ea265ba4c74621c81d4a6fc4`,
|
|
362
389
|
与本地打包一致。发布后远端 `npm view` 有几秒传播延迟,第一次回读到旧版本要重试再确认。注意该包没有 `main`/`exports`/`files`,发布内容是 91 个源码文件,
|
|
363
390
|
不是可直接 `import` 的库;调用方仍应使用 runtime 或 workspace 包。
|
|
364
391
|
|