@liujitcn/kratos-taro-app-core 0.0.42 → 0.0.44

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/README.md CHANGED
@@ -39,3 +39,5 @@ pnpm --filter @liujitcn/kratos-taro-app-core tsc
39
39
  pnpm test
40
40
  pnpm check:exports
41
41
  ```
42
+
43
+ 构建产物按模式与平台隔离:开发使用 `dist/dev/h5`、`dist/dev/mp-weixin`,生产默认使用 `dist/build/h5`、`dist/build/mp-weixin`。`KRATOS_TARO_OUTPUT_ROOT` 可覆盖默认目录;仓库宿主及 Kratos 配套项目的 H5 生产构建输出到 `backend/data/taro-app`。微信开发者工具默认指向开发目录,发布时导入生产目录。
package/dist/runner.mjs CHANGED
@@ -84,7 +84,7 @@ async function main() {
84
84
  env: {
85
85
  ...process.env,
86
86
  NODE_ENV: options.mode === "development" ? "development" : "production",
87
- KRATOS_TARO_OUTPUT_ROOT: process.env.KRATOS_TARO_OUTPUT_ROOT || (options.type === "h5" ? "dist/h5" : "dist/mp-weixin")
87
+ KRATOS_TARO_OUTPUT_ROOT: process.env.KRATOS_TARO_OUTPUT_ROOT || `dist/${options.mode === "development" ? "dev" : "build"}/${options.type === "h5" ? "h5" : "mp-weixin"}`
88
88
  },
89
89
  stdio: "inherit"
90
90
  });
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "type": "git",
8
8
  "url": "git@github.com:liujitcn/kratos-admin.git"
9
9
  },
10
- "version": "0.0.42",
10
+ "version": "0.0.44",
11
11
  "type": "module",
12
12
  "files": [
13
13
  "dist",
package/src/bootstrap.ts CHANGED
@@ -58,7 +58,10 @@ export function bootstrapKratosTaroApp(options: KratosTaroBootstrapOptions): voi
58
58
  bootstrapped = true
59
59
  syncH5DocumentTitle()
60
60
  registerLocaleChangeHandler(initializeAppNavigation)
61
- registerLocaleChangeHandler(() => useSettingStore.getState().loadData())
61
+ registerLocaleChangeHandler(async () => {
62
+ await useSettingStore.getState().loadData()
63
+ await initializeAppNavigation()
64
+ })
62
65
  registerLocaleChangeHandler(syncH5DocumentTitle)
63
66
  startUserStoreEventBridge()
64
67
  registerUserStoreExtension({
@@ -67,4 +70,7 @@ export function bootstrapKratosTaroApp(options: KratosTaroBootstrapOptions): voi
67
70
  onSilentLogout: initializeAppNavigation,
68
71
  })
69
72
  useUserStore.getState().hydrate()
73
+ void useSettingStore.getState().loadData().then(() => initializeAppNavigation()).catch((error) => {
74
+ console.warn('application configuration unavailable', error)
75
+ })
70
76
  }
package/src/navigation.ts CHANGED
@@ -8,6 +8,7 @@ import { buildMenuTree } from './navigation-tree.mjs'
8
8
  import { hasValidToken } from './utils/auth'
9
9
  import { navigateToLogin } from './utils/navigation'
10
10
  import { getCurrentLocale, t } from './locales'
11
+ import { useSettingStore } from './stores'
11
12
 
12
13
  /** 移动菜单访问模式。 */
13
14
  export type AppMenuAccess = 'PUBLIC' | 'GUEST_ONLY' | 'AUTHENTICATED'
@@ -219,9 +220,9 @@ export function setAppNavigationAdapter(nextAdapter: AppNavigationAdapter): void
219
220
  export async function initializeAppNavigation(): Promise<void> {
220
221
  const cacheKey = resolveCacheKey()
221
222
  const cached = readCachedMenus(cacheKey)
222
- useAppNavigation.setState(createNavigationState(cached ?? localizedDefaultAppMenus(), false))
223
+ useAppNavigation.setState(createNavigationState(filterUnavailableMenus(cached ?? localizedDefaultAppMenus()), false))
223
224
  try {
224
- const nextMenus = normalizeMenuResponse(await adapter.list())
225
+ const nextMenus = filterUnavailableMenus(normalizeMenuResponse(await adapter.list()))
225
226
  validateMenus(nextMenus)
226
227
  Taro.setStorageSync(cacheKey, nextMenus)
227
228
  useAppNavigation.setState(createNavigationState(nextMenus, true))
@@ -231,6 +232,12 @@ export async function initializeAppNavigation(): Promise<void> {
231
232
  }
232
233
  }
233
234
 
235
+ /** 根据运行时能力过滤不可用的移动端入口。 */
236
+ function filterUnavailableMenus(nextMenus: AppMenu[]): AppMenu[] {
237
+ if (useSettingStore.getState().aiEnabled) return nextMenus
238
+ return nextMenus.filter((menu) => menu.viewKey !== 'AI')
239
+ }
240
+
234
241
  /** 使用已经校验的整份配置进行原子切换。 */
235
242
  export function installAppNavigation(nextMenus: AppMenu[]): void {
236
243
  validateMenus(nextMenus)
@@ -247,6 +254,7 @@ export function resolveAppRoute(rawRoute: string): ResolvedAppRoute | undefined
247
254
  if (!params) continue
248
255
  const physicalRoute = resolveStaticView(menu.viewKey)
249
256
  if (!physicalRoute) return
257
+ if (menu.viewKey === 'AI' && !useSettingStore.getState().aiEnabled) return
250
258
  return {
251
259
  menu,
252
260
  physicalRoute,
@@ -38,6 +38,8 @@ export interface ConfigItem {
38
38
  export interface GetConfigResponse {
39
39
  /** 系统配置列表 */
40
40
  configs: ConfigItem[];
41
+ /** AI 助手是否可用 */
42
+ ai_enabled: boolean;
41
43
  }
42
44
 
43
45
  /** Base系统配置公共服务 */
package/src/runner.ts CHANGED
@@ -128,7 +128,7 @@ async function main(): Promise<void> {
128
128
  NODE_ENV: options.mode === 'development' ? 'development' : 'production',
129
129
  KRATOS_TARO_OUTPUT_ROOT:
130
130
  process.env.KRATOS_TARO_OUTPUT_ROOT ||
131
- (options.type === 'h5' ? 'dist/h5' : 'dist/mp-weixin'),
131
+ `dist/${options.mode === 'development' ? 'dev' : 'build'}/${options.type === 'h5' ? 'h5' : 'mp-weixin'}`,
132
132
  },
133
133
  stdio: 'inherit',
134
134
  })
@@ -12,6 +12,7 @@ const REQUIRED_APP_CONFIGS = [
12
12
  /** 应用配置状态及操作。 */
13
13
  export interface SettingStoreState {
14
14
  data?: Map<string, string>
15
+ aiEnabled: boolean
15
16
  getData: (key: string) => string | undefined
16
17
  loadData: () => Promise<void>
17
18
  }
@@ -21,13 +22,20 @@ let loading: Promise<void> | undefined
21
22
  /** 应用配置 Zustand Store。 */
22
23
  export const useSettingStore = create<SettingStoreState>((set, get) => ({
23
24
  data: undefined,
25
+ aiEnabled: false,
24
26
  getData(key) {
25
27
  return get().data?.get(key)
26
28
  },
27
29
  async loadData() {
28
30
  if (loading) return loading
29
31
  loading = (async () => {
30
- const response = await defConfigService.GetConfig({ site: BaseConfigSite.BASE_CONFIG_SITE_APP })
32
+ let response
33
+ try {
34
+ response = await defConfigService.GetConfig({ site: BaseConfigSite.BASE_CONFIG_SITE_APP })
35
+ } catch {
36
+ set({ aiEnabled: false })
37
+ return
38
+ }
31
39
  const nextData = new Map(response.configs.map((item) => [item.key, item.value]))
32
40
  const missing = REQUIRED_APP_CONFIGS.filter(({ key }) => !nextData.get(key))
33
41
  if (missing.length) {
@@ -37,7 +45,7 @@ export const useSettingStore = create<SettingStoreState>((set, get) => ({
37
45
  }),
38
46
  )
39
47
  }
40
- set({ data: nextData })
48
+ set({ data: nextData, aiEnabled: response.ai_enabled === true })
41
49
  })()
42
50
  try {
43
51
  await loading
@@ -8,6 +8,7 @@ import {
8
8
  } from '../../../navigation'
9
9
  import type { BootstrapViewKey } from '../../../module'
10
10
  import { useI18n } from '../../../locales'
11
+ import { useSettingStore } from '../../../stores'
11
12
 
12
13
  /** 启动与错误状态页。 */
13
14
  export default function StatusPage() {
@@ -20,17 +21,31 @@ export default function StatusPage() {
20
21
  setState(nextState)
21
22
  setDetail(options?.detail ? decodeURIComponent(options.detail) : '')
22
23
  if (options?.bootstrap !== '1') return
23
- void initializeAppNavigation()
24
+ void useSettingStore
25
+ .getState()
26
+ .loadData()
27
+ .catch(() => undefined)
28
+ .then(() => initializeAppNavigation())
24
29
  .then(() => {
25
30
  navigateAppRoute(options?.route ? decodeURIComponent(options.route) : 'app/home', {
26
31
  replace: true,
27
32
  })
28
33
  })
29
34
  .catch((error: unknown) => {
30
- launchAppStatus('CONFIG_ERROR', error instanceof Error ? error.message : String(error))
35
+ launchAppStatus('CONFIG_ERROR', resolveErrorMessage(error))
31
36
  })
32
37
  })
33
38
 
34
39
  const title = state === 'BOOTSTRAP_LOADING' ? t('core.status.loading') : t(`core.status.${state}`)
35
40
  return <BootstrapStatus title={title} detail={detail} />
36
41
  }
42
+
43
+ /** 将请求错误转换为可展示的文本,避免直接显示 Object。 */
44
+ function resolveErrorMessage(error: unknown): string {
45
+ if (error instanceof Error) return error.message
46
+ if (typeof error === 'string') return error
47
+ if (error && typeof error === 'object' && 'message' in error) {
48
+ return String((error as { message?: unknown }).message || '')
49
+ }
50
+ return ''
51
+ }