@liujitcn/kratos-taro-app-core 0.0.84 → 0.0.85
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 +33 -0
- package/dist/runner.mjs +1 -0
- package/package.json +1 -1
- package/src/components/KratosNavBar.scss +100 -0
- package/src/components/KratosNavBar.tsx +87 -0
- package/src/index.ts +3 -0
- package/src/navigation.ts +8 -34
- package/src/runner.ts +2 -0
- package/src/styles/components.scss +0 -82
- package/src/views/pages/index/index.scss +1 -32
- package/src/views/pages/index/index.tsx +4 -5
- package/src/views/pages/login/protocal.scss +1 -1
- package/src/views/pages/login/protocal.tsx +3 -12
package/README.md
CHANGED
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
- 登录阶段支持 TOTP、WebAuthn 和一次性恢复码校验。
|
|
13
13
|
- 公开 Base MFA 请求及通用密码弹窗、绑定面板和恢复码弹窗,供登录页与业务模块复用同一套绑定界面。
|
|
14
14
|
- 接口驱动的逻辑路由、访问模式、自绘 tabBar 和稳定 `viewKey`。
|
|
15
|
+
- tab 切换统一使用普通页面路由:H5 替换当前路由,微信端复用页面栈,栈满时替换栈顶,避免整栈重建。
|
|
16
|
+
- runner 在编译配置中移除宿主的原生 `tabBar`,退出后恢复宿主原文件;导航按模块注册的 `viewKey` 解析物理页面,支持业务模块覆盖首页和扩展 tab。
|
|
15
17
|
- 页面静态资源 export、动态资源公共路径解析,以及 H5 默认导航栏兼容框架。
|
|
16
18
|
- 首页、登录、协议、WebView、启动/错误状态页面。
|
|
17
19
|
- H5 与微信小程序共用的模块页面装配 runner。
|
|
@@ -28,6 +30,37 @@
|
|
|
28
30
|
|
|
29
31
|
业务模块只能通过这些 exports 使用底座,不得相对引用 core 源码。RPC 由 `make -C frontend ts-taro-app` 生成,不能手工编辑。
|
|
30
32
|
|
|
33
|
+
## 通用顶部导航
|
|
34
|
+
|
|
35
|
+
所有自定义顶部导航统一使用公开组件 `KratosNavBar`,页面只提供标题和业务操作,不再定义导航高度、安全区、标题居中、按钮尺寸或微信胶囊避让。普通页与 tabBar 页的标题栏使用相同高度,按钮显隐不会改变标题位置。页面外层只维护水平与底部留白,顶部留白由组件处理;tabBar 页的导航栏还统一预留内容间距。
|
|
36
|
+
|
|
37
|
+
```tsx
|
|
38
|
+
import { KratosNavBar } from '@liujitcn/kratos-taro-app-core'
|
|
39
|
+
|
|
40
|
+
// 普通页默认带返回,无法返回时回到首页。
|
|
41
|
+
<KratosNavBar title='个人信息' />
|
|
42
|
+
// tabBar 页默认不带返回,也可以不传操作按钮。
|
|
43
|
+
<KratosNavBar title='首页' variant='tab' />
|
|
44
|
+
<KratosNavBar
|
|
45
|
+
title='我的'
|
|
46
|
+
variant='tab'
|
|
47
|
+
action={{ icon: 'settings', label: '设置', onClick: openSettings }}
|
|
48
|
+
/>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`back` 可显式控制返回按钮,`onBack` 可覆盖默认返回操作。`action` 包含 `label`、可选 `icon`、可选 `display` 和 `onClick`;`display` 支持 `icon`(纯图标)、`text`(纯文字)和 `both`(图标加文字),默认有图标时显示图标,否则显示文字。图标支持内置 `settings`、`read-all`、`history`,以及自定义图片地址。不传 `action` 时不渲染操作按钮。三种按钮形式共用相同高度;纯图标保持方形,文字与组合按钮按内容增宽,空间不足时省略文字。组件及 `KratosNavBarProps`、`KratosNavBarAction` 类型均从包根公开导出;业务模块应传入翻译后的标题和按钮名称。
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import type { KratosNavBarAction } from '@liujitcn/kratos-taro-app-core'
|
|
55
|
+
|
|
56
|
+
// 纯图标
|
|
57
|
+
const iconAction: KratosNavBarAction = { icon: 'settings', label: '设置', display: 'icon', onClick: openSettings }
|
|
58
|
+
// 纯文字
|
|
59
|
+
const textAction: KratosNavBarAction = { label: '全部已读', display: 'text', onClick: markAllRead }
|
|
60
|
+
// 图标加文字
|
|
61
|
+
const combinedAction: KratosNavBarAction = { icon: 'history', label: '历史', display: 'both', onClick: openHistory }
|
|
62
|
+
```
|
|
63
|
+
|
|
31
64
|
## Runner 协议
|
|
32
65
|
|
|
33
66
|
模块必须同时提供运行时定义和 `./build` 导出。构建入口返回模块名称、包根目录与页面清单;runner 根据宿主的静态 `module-manifest.ts` 解析模块顺序,扫描页面并创建事务。
|
package/dist/runner.mjs
CHANGED
|
@@ -67,6 +67,7 @@ async function main() {
|
|
|
67
67
|
if (options.cleanupOnly) return;
|
|
68
68
|
const originalAppConfig = readFileSync(appConfigFile, "utf8");
|
|
69
69
|
const manifest = JSON.parse(readFileSync(baseConfigFile, "utf8"));
|
|
70
|
+
delete manifest.tabBar;
|
|
70
71
|
const modules = await loadBuildModules(hostRoot, resolve(inputDir, "module-manifest.ts"));
|
|
71
72
|
const pageMap = /* @__PURE__ */ new Map();
|
|
72
73
|
modules.forEach((module) => {
|
package/package.json
CHANGED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
.kratos-nav-bar {
|
|
2
|
+
flex-shrink: 0;
|
|
3
|
+
box-sizing: border-box;
|
|
4
|
+
padding-top: calc(env(safe-area-inset-top) + 24px);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.kratos-nav-bar--tab {
|
|
8
|
+
margin-bottom: 16px;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.kratos-nav-bar__row {
|
|
12
|
+
position: relative;
|
|
13
|
+
display: flex;
|
|
14
|
+
align-items: center;
|
|
15
|
+
gap: 16px;
|
|
16
|
+
height: 76px;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.kratos-nav-bar__title {
|
|
20
|
+
position: absolute;
|
|
21
|
+
top: 50%;
|
|
22
|
+
left: 50%;
|
|
23
|
+
max-width: calc(100% - 190px);
|
|
24
|
+
overflow: hidden;
|
|
25
|
+
color: var(--kratos-color-text);
|
|
26
|
+
font-size: 16PX;
|
|
27
|
+
font-weight: 700;
|
|
28
|
+
line-height: 22PX;
|
|
29
|
+
text-overflow: ellipsis;
|
|
30
|
+
white-space: nowrap;
|
|
31
|
+
transform: translate(-50%, -50%);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.kratos-nav-bar__button {
|
|
35
|
+
display: flex;
|
|
36
|
+
flex: 0 0 76px;
|
|
37
|
+
align-items: center;
|
|
38
|
+
justify-content: center;
|
|
39
|
+
box-sizing: border-box;
|
|
40
|
+
width: 76px;
|
|
41
|
+
height: 76px;
|
|
42
|
+
border: 1PX solid var(--kratos-color-border);
|
|
43
|
+
border-radius: 24px;
|
|
44
|
+
background: var(--kratos-color-surface);
|
|
45
|
+
color: var(--kratos-color-text);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.kratos-nav-bar__back::before {
|
|
49
|
+
width: 16px;
|
|
50
|
+
height: 16px;
|
|
51
|
+
border-left: 2PX solid var(--kratos-color-primary);
|
|
52
|
+
border-bottom: 2PX solid var(--kratos-color-primary);
|
|
53
|
+
content: '';
|
|
54
|
+
transform: rotate(45deg);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.kratos-nav-bar__action {
|
|
58
|
+
margin-left: auto;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.kratos-nav-bar__action--label {
|
|
62
|
+
flex: 0 1 auto;
|
|
63
|
+
gap: 10px;
|
|
64
|
+
min-width: 76px;
|
|
65
|
+
max-width: calc(50% - 48px);
|
|
66
|
+
width: auto;
|
|
67
|
+
padding: 0 20px;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.kratos-nav-bar__icon {
|
|
71
|
+
flex-shrink: 0;
|
|
72
|
+
width: 34px;
|
|
73
|
+
height: 34px;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.kratos-nav-bar__label {
|
|
77
|
+
min-width: 0;
|
|
78
|
+
overflow: hidden;
|
|
79
|
+
font-size: 22px;
|
|
80
|
+
line-height: 28px;
|
|
81
|
+
text-overflow: ellipsis;
|
|
82
|
+
white-space: nowrap;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.kratos-nav-bar--weapp {
|
|
86
|
+
padding-top: 44PX;
|
|
87
|
+
|
|
88
|
+
.kratos-nav-bar__row {
|
|
89
|
+
height: 88px;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/* 原生胶囊占据右侧,操作按钮与返回按钮统一放在左侧。 */
|
|
93
|
+
.kratos-nav-bar__action {
|
|
94
|
+
margin-left: 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.kratos-nav-bar__title {
|
|
98
|
+
max-width: calc(100% - 190px - var(--kratos-nav-bar-capsule-gap));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { Image, Text, View } from '@tarojs/components'
|
|
2
|
+
import Taro from '@tarojs/taro'
|
|
3
|
+
import type { CSSProperties } from 'react'
|
|
4
|
+
import { useI18n } from '../locales'
|
|
5
|
+
import { useThemeStore } from '../stores'
|
|
6
|
+
import { getMpCapsuleRightGap } from '../utils'
|
|
7
|
+
import { homeTabPage } from '../utils/navigation'
|
|
8
|
+
import './KratosNavBar.scss'
|
|
9
|
+
|
|
10
|
+
/** KratosNavBarAction 导航栏操作按钮,统一渲染尺寸、图标与无障碍文案。 */
|
|
11
|
+
export interface KratosNavBarAction {
|
|
12
|
+
/** 按钮名称,同时用作可见文字与无障碍文案。 */
|
|
13
|
+
label: string
|
|
14
|
+
/** 内置 settings、read-all、history 图标名称,或自定义图片地址。 */
|
|
15
|
+
icon?: string
|
|
16
|
+
/** 显示方式;默认有图标时显示图标,否则显示文字。 */
|
|
17
|
+
display?: 'icon' | 'text' | 'both'
|
|
18
|
+
/** 点击按钮后执行的业务操作。 */
|
|
19
|
+
onClick: () => void
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** KratosNavBarProps 普通页面与 tabBar 页面共用的顶部导航属性。 */
|
|
23
|
+
export interface KratosNavBarProps {
|
|
24
|
+
/** 居中显示的页面标题。 */
|
|
25
|
+
title: string
|
|
26
|
+
/** 普通页面默认显示返回按钮,tabBar 页面默认隐藏返回按钮。 */
|
|
27
|
+
variant?: 'page' | 'tab'
|
|
28
|
+
/** 显式覆盖返回按钮的显隐。 */
|
|
29
|
+
back?: boolean
|
|
30
|
+
/** 可选的页面操作按钮。 */
|
|
31
|
+
action?: KratosNavBarAction
|
|
32
|
+
/** 自定义返回操作;不传时返回上一页,失败后回到首页。 */
|
|
33
|
+
onBack?: () => void
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const iconPaths: Record<string, string> = {
|
|
37
|
+
settings:
|
|
38
|
+
'<circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.6 1.6 0 0 0 .3 1.8 2 2 0 1 1-2.8 2.8 1.6 1.6 0 0 0-2.7 1.1 2 2 0 1 1-4 0 1.6 1.6 0 0 0-2.6-1.1 2 2 0 1 1-2.8-2.8A1.6 1.6 0 0 0 3 13.9a2 2 0 1 1 0-4 1.6 1.6 0 0 0 1.5-2.7 2 2 0 1 1 2.8-2.8A1.6 1.6 0 0 0 10 4.6a2 2 0 1 1 4 0 1.6 1.6 0 0 0 2.7.6 2 2 0 1 1 2.8 2.8 1.6 1.6 0 0 0-.1 2.6a2.2 2.2 0 1 1 0 4.4Z"/>',
|
|
39
|
+
'read-all': '<path d="m3 12 4 4L17 6m-5 10 9-9"/>',
|
|
40
|
+
history: '<path d="M4 6h16M4 12h16M4 18h16"/>',
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** KratosNavBar 统一页面标题、返回与操作按钮,并处理安全区和微信胶囊避让。 */
|
|
44
|
+
export default function KratosNavBar({ title, variant = 'page', back, action, onBack }: KratosNavBarProps) {
|
|
45
|
+
const { t } = useI18n()
|
|
46
|
+
const themeStore = useThemeStore()
|
|
47
|
+
const capsuleGap = getMpCapsuleRightGap()
|
|
48
|
+
const navStyle = { '--kratos-nav-bar-capsule-gap': `${capsuleGap}px` } as CSSProperties
|
|
49
|
+
const showBack = back ?? variant === 'page'
|
|
50
|
+
const iconPath = action?.icon ? iconPaths[action.icon] : undefined
|
|
51
|
+
const color = themeStore.theme === 'light' ? '#102232' : '#e9f4f6'
|
|
52
|
+
const actionIcon = iconPath
|
|
53
|
+
? `data:image/svg+xml;charset=utf-8,${encodeURIComponent(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="${color}" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">${iconPath}</svg>`)}`
|
|
54
|
+
: action?.icon
|
|
55
|
+
const actionDisplay = action?.display ?? (actionIcon ? 'icon' : 'text')
|
|
56
|
+
const showActionIcon = !!actionIcon && actionDisplay !== 'text'
|
|
57
|
+
const showActionLabel = !actionIcon || actionDisplay !== 'icon'
|
|
58
|
+
|
|
59
|
+
/** goBack 优先执行页面返回操作,否则返回上一页并在失败时回到首页。 */
|
|
60
|
+
function goBack() {
|
|
61
|
+
if (onBack) {
|
|
62
|
+
onBack()
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
void Taro.navigateBack().catch(() => Taro.reLaunch({ url: homeTabPage }))
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<View
|
|
70
|
+
className={`kratos-nav-bar${variant === 'tab' ? ' kratos-nav-bar--tab' : ''}${process.env.TARO_ENV === 'weapp' ? ' kratos-nav-bar--weapp' : ''}`}
|
|
71
|
+
style={navStyle}
|
|
72
|
+
>
|
|
73
|
+
<View className='kratos-nav-bar__row'>
|
|
74
|
+
{showBack && (
|
|
75
|
+
<View className='kratos-nav-bar__button kratos-nav-bar__back' aria-label={t('common.action.back')} onClick={goBack} />
|
|
76
|
+
)}
|
|
77
|
+
<View className='kratos-nav-bar__title'>{title}</View>
|
|
78
|
+
{action && (
|
|
79
|
+
<View className={`kratos-nav-bar__button kratos-nav-bar__action${showActionLabel ? ' kratos-nav-bar__action--label' : ''}`} aria-label={action.label} onClick={action.onClick}>
|
|
80
|
+
{showActionIcon && <Image className='kratos-nav-bar__icon' src={actionIcon} mode='aspectFit' aria-hidden='true' />}
|
|
81
|
+
{showActionLabel && <Text className='kratos-nav-bar__label'>{action.label}</Text>}
|
|
82
|
+
</View>
|
|
83
|
+
)}
|
|
84
|
+
</View>
|
|
85
|
+
</View>
|
|
86
|
+
)
|
|
87
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -44,3 +44,6 @@ export { default as PasswordVerifyDialog } from './components/PasswordVerifyDial
|
|
|
44
44
|
export type { PasswordVerifyDialogProps } from './components/PasswordVerifyDialog'
|
|
45
45
|
export { default as KratosEmpty } from './components/KratosEmpty'
|
|
46
46
|
export type { KratosEmptyProps } from './components/KratosEmpty'
|
|
47
|
+
/** KratosNavBar 普通页面与 tabBar 页面共用的顶部导航栏。 */
|
|
48
|
+
export { default as KratosNavBar } from './components/KratosNavBar'
|
|
49
|
+
export type { KratosNavBarAction, KratosNavBarProps } from './components/KratosNavBar'
|
package/src/navigation.ts
CHANGED
|
@@ -197,8 +197,6 @@ const defaultMenuTitleKeys: Record<string, string> = {
|
|
|
197
197
|
}
|
|
198
198
|
|
|
199
199
|
let tabNavigationTarget: string | undefined
|
|
200
|
-
const nativeTabViewKeys = ['HOME', 'PROFILE_HOME']
|
|
201
|
-
const resetTabViewKeys = ['MESSAGE_INBOX']
|
|
202
200
|
|
|
203
201
|
function localizedDefaultAppMenus(): AppMenu[] {
|
|
204
202
|
return defaultAppMenus.map((menu) => ({
|
|
@@ -325,7 +323,7 @@ export function navigateAppView(viewKey: string, params: Record<string, string>
|
|
|
325
323
|
navigateAppRoute(`${path}${query ? `?${query}` : ''}`)
|
|
326
324
|
}
|
|
327
325
|
|
|
328
|
-
/**
|
|
326
|
+
/** 切换自绘 tab:H5 替换路由,微信端复用页面栈,避免重建整个页面栈。 */
|
|
329
327
|
function navigateTabRoute(url: string): void {
|
|
330
328
|
const targetRoute = url.slice(1).split('?', 1)[0].replace(/^\/+/, '')
|
|
331
329
|
const pages = getCurrentPages()
|
|
@@ -338,41 +336,17 @@ function navigateTabRoute(url: string): void {
|
|
|
338
336
|
if (tabNavigationTarget === targetRoute) tabNavigationTarget = undefined
|
|
339
337
|
}
|
|
340
338
|
if (process.env.TARO_ENV === 'h5') {
|
|
341
|
-
void Taro.redirectTo({ url }).then(release,
|
|
342
|
-
void Taro.reLaunch({ url }).then(release, release)
|
|
343
|
-
})
|
|
339
|
+
void Taro.redirectTo({ url }).then(release, release)
|
|
344
340
|
return
|
|
345
341
|
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
.catch(() => {
|
|
350
|
-
navigateTabRouteInStack(url, targetRoute, pages, currentIndex, release)
|
|
351
|
-
})
|
|
352
|
-
return
|
|
353
|
-
}
|
|
354
|
-
if (resetTabViewKeys.some((viewKey) => resolveStaticView(viewKey) === targetRoute)) {
|
|
355
|
-
void Taro.reLaunch({ url }).then(release, release)
|
|
342
|
+
const targetIndex = pages.findIndex((page) => page.route?.replace(/^\/+/, '') === targetRoute)
|
|
343
|
+
if (targetIndex >= 0 && targetIndex < currentIndex) {
|
|
344
|
+
void Taro.navigateBack({ delta: currentIndex - targetIndex }).then(release, release)
|
|
356
345
|
return
|
|
357
346
|
}
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
/** 在没有原生 tab 路由的动态菜单上复用页面栈。 */
|
|
362
|
-
function navigateTabRouteInStack(
|
|
363
|
-
url: string,
|
|
364
|
-
targetRoute: string,
|
|
365
|
-
pages: ReturnType<typeof getCurrentPages>,
|
|
366
|
-
currentIndex: number,
|
|
367
|
-
release: () => void,
|
|
368
|
-
): void {
|
|
369
|
-
const targetIndex = pages.findIndex((page) => page.route?.replace(/^\/+/, '') === targetRoute)
|
|
370
|
-
const navigation =
|
|
371
|
-
targetIndex >= 0 && targetIndex < currentIndex
|
|
372
|
-
? Taro.navigateBack({ delta: currentIndex - targetIndex })
|
|
373
|
-
: Taro.navigateTo({ url })
|
|
374
|
-
void navigation.then(release).catch(() => {
|
|
375
|
-
void Taro.reLaunch({ url }).then(release, release)
|
|
347
|
+
void Taro.navigateTo({ url }).then(release, () => {
|
|
348
|
+
// 小程序页面栈满时只替换栈顶,保留其他 tab 的已加载页面。
|
|
349
|
+
void Taro.redirectTo({ url }).then(release, release)
|
|
376
350
|
})
|
|
377
351
|
}
|
|
378
352
|
|
package/src/runner.ts
CHANGED
|
@@ -69,6 +69,8 @@ async function main(): Promise<void> {
|
|
|
69
69
|
|
|
70
70
|
const originalAppConfig = readFileSync(appConfigFile, 'utf8')
|
|
71
71
|
const manifest = JSON.parse(readFileSync(baseConfigFile, 'utf8')) as AppManifest
|
|
72
|
+
// 所有宿主统一使用 KratosTabBar,原生 tab 声明会限制普通页面跳转并要求额外组件。
|
|
73
|
+
delete manifest.tabBar
|
|
72
74
|
const modules = await loadBuildModules(hostRoot, resolve(inputDir, 'module-manifest.ts'))
|
|
73
75
|
const pageMap = new Map<string, PageEntry>()
|
|
74
76
|
modules.forEach((module) => {
|
|
@@ -105,88 +105,6 @@
|
|
|
105
105
|
box-shadow: var(--kratos-glow);
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
/* 顶栏 */
|
|
109
|
-
.k-page-nav {
|
|
110
|
-
position: relative;
|
|
111
|
-
display: flex;
|
|
112
|
-
align-items: center;
|
|
113
|
-
justify-content: space-between;
|
|
114
|
-
box-sizing: border-box;
|
|
115
|
-
height: calc(88px + env(safe-area-inset-top));
|
|
116
|
-
padding-top: env(safe-area-inset-top);
|
|
117
|
-
}
|
|
118
|
-
.k-page-nav--weapp {
|
|
119
|
-
height: calc(88px + 44PX);
|
|
120
|
-
padding-top: 44PX;
|
|
121
|
-
}
|
|
122
|
-
.k-page-nav__back,
|
|
123
|
-
.k-page-nav__side {
|
|
124
|
-
display: flex;
|
|
125
|
-
align-items: center;
|
|
126
|
-
justify-content: center;
|
|
127
|
-
flex: 0 0 76px;
|
|
128
|
-
width: 76px;
|
|
129
|
-
height: 76px;
|
|
130
|
-
}
|
|
131
|
-
.k-page-nav__back {
|
|
132
|
-
border: 1PX solid var(--kratos-color-border);
|
|
133
|
-
border-radius: 24px;
|
|
134
|
-
background: var(--kratos-color-surface);
|
|
135
|
-
}
|
|
136
|
-
.k-page-nav__back::before {
|
|
137
|
-
width: 16px;
|
|
138
|
-
height: 16px;
|
|
139
|
-
border-left: 2PX solid var(--kratos-color-primary);
|
|
140
|
-
border-bottom: 2PX solid var(--kratos-color-primary);
|
|
141
|
-
content: '';
|
|
142
|
-
transform: rotate(45deg);
|
|
143
|
-
}
|
|
144
|
-
.k-page-nav__title {
|
|
145
|
-
position: absolute;
|
|
146
|
-
left: 50%;
|
|
147
|
-
max-width: calc(100% - 190px);
|
|
148
|
-
overflow: hidden;
|
|
149
|
-
color: var(--kratos-color-text);
|
|
150
|
-
font-size: 16PX;
|
|
151
|
-
font-weight: 700;
|
|
152
|
-
text-overflow: ellipsis;
|
|
153
|
-
transform: translateX(-50%);
|
|
154
|
-
white-space: nowrap;
|
|
155
|
-
}
|
|
156
|
-
.k-top {
|
|
157
|
-
position: relative;
|
|
158
|
-
display: flex;
|
|
159
|
-
align-items: center;
|
|
160
|
-
/* 标题绝对居中后,流内子元素(右侧动作按钮)统一靠右排布 */
|
|
161
|
-
justify-content: flex-end;
|
|
162
|
-
gap: 16px;
|
|
163
|
-
/* weapp 原生胶囊避让:页面通过 getMpCapsuleRightGap() 写入该变量,
|
|
164
|
-
其余平台未设置时回退 0,不影响布局。 */
|
|
165
|
-
padding-right: var(--mp-capsule-gap, 0px);
|
|
166
|
-
}
|
|
167
|
-
/* #ifdef weapp */
|
|
168
|
-
/* 双写类名提高优先级:页面级 padding 简写(如 .message-header)不得压掉导航带 */
|
|
169
|
-
.k-top.k-top {
|
|
170
|
-
/* 小程序右上角是原生胶囊:操作按钮靠左,标题行与胶囊同行(同 k-page-nav--weapp 几何) */
|
|
171
|
-
justify-content: flex-start;
|
|
172
|
-
height: calc(88px + 44PX);
|
|
173
|
-
padding-top: 44PX;
|
|
174
|
-
padding-right: 0;
|
|
175
|
-
}
|
|
176
|
-
/* #endif */
|
|
177
|
-
.k-top__title {
|
|
178
|
-
position: absolute;
|
|
179
|
-
left: 50%;
|
|
180
|
-
max-width: calc(100% - 190px);
|
|
181
|
-
overflow: hidden;
|
|
182
|
-
color: var(--kratos-color-text);
|
|
183
|
-
font-size: 16PX;
|
|
184
|
-
font-weight: 700;
|
|
185
|
-
text-overflow: ellipsis;
|
|
186
|
-
transform: translateX(-50%);
|
|
187
|
-
white-space: nowrap;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
108
|
/* 方形图标按钮 + 角标 */
|
|
191
109
|
.k-ic {
|
|
192
110
|
position: relative;
|
|
@@ -9,41 +9,10 @@ page {
|
|
|
9
9
|
background: var(--kratos-bg-gradient);
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
.home-page--weapp .home-content {
|
|
13
|
-
padding-top: 0;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/* 小程序端首页标题占导航带:与 k-page-nav--weapp 同几何,标题与胶囊同行 */
|
|
17
|
-
.home-page--weapp .home-top {
|
|
18
|
-
box-sizing: border-box;
|
|
19
|
-
height: calc(88px + 44PX);
|
|
20
|
-
padding: 44PX 4px 0;
|
|
21
|
-
display: flex;
|
|
22
|
-
align-items: center;
|
|
23
|
-
justify-content: center;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
.home-page--weapp .home-top__title {
|
|
27
|
-
margin-top: 0;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
12
|
.home-content {
|
|
31
13
|
position: relative;
|
|
32
14
|
z-index: 1;
|
|
33
|
-
padding:
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
.home-top {
|
|
37
|
-
padding: 8px 4px 24px;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/* 页面大标题:与其他页面导航标题同规格,向原生导航栏看齐 */
|
|
41
|
-
.home-top__title {
|
|
42
|
-
margin-top: 14px;
|
|
43
|
-
color: var(--kratos-color-text);
|
|
44
|
-
font-size: 16PX;
|
|
45
|
-
font-weight: 700;
|
|
46
|
-
text-align: center;
|
|
15
|
+
padding: 0 20px 220px;
|
|
47
16
|
}
|
|
48
17
|
|
|
49
18
|
.home-hero {
|
|
@@ -5,9 +5,10 @@ import defaultLogo from '@liujitcn/kratos-taro-app-core/static/images/logo_icon.
|
|
|
5
5
|
import { useSettingStore, useThemeStore } from '../../../stores'
|
|
6
6
|
import { formatSrc } from '../../../utils'
|
|
7
7
|
import './index.scss'
|
|
8
|
+
import KratosNavBar from '../../../components/KratosNavBar'
|
|
8
9
|
import { useI18n } from '../../../locales'
|
|
9
10
|
|
|
10
|
-
/**
|
|
11
|
+
/** HomePage 展示应用首页,并复用固定高度的 Tab 标题栏。 */
|
|
11
12
|
export default function HomePage() {
|
|
12
13
|
const { t } = useI18n()
|
|
13
14
|
const themeStore = useThemeStore()
|
|
@@ -41,15 +42,13 @@ export default function HomePage() {
|
|
|
41
42
|
]
|
|
42
43
|
return (
|
|
43
44
|
<View
|
|
44
|
-
className={`home-page
|
|
45
|
+
className={`home-page ${
|
|
45
46
|
themeStore.theme === 'light' ? 'theme-light' : 'theme-dark'
|
|
46
47
|
}`}
|
|
47
48
|
>
|
|
48
49
|
<View className='k-grid-bg' aria-hidden />
|
|
49
50
|
<View className='home-content'>
|
|
50
|
-
<
|
|
51
|
-
<View className='home-top__title'>{mainTitle}</View>
|
|
52
|
-
</View>
|
|
51
|
+
<KratosNavBar title={mainTitle} variant='tab' />
|
|
53
52
|
|
|
54
53
|
<View className='k-glass k-glass--lit home-hero'>
|
|
55
54
|
<View className='home-hero__main'>
|
|
@@ -3,7 +3,7 @@ import Taro, { useLoad } from '@tarojs/taro'
|
|
|
3
3
|
import { useState } from 'react'
|
|
4
4
|
import { useSettingStore, useThemeStore } from '../../../stores'
|
|
5
5
|
import { useI18n } from '../../../locales'
|
|
6
|
-
import
|
|
6
|
+
import KratosNavBar from '../../../components/KratosNavBar'
|
|
7
7
|
import './protocal.scss'
|
|
8
8
|
|
|
9
9
|
const protocolClassMap: Record<string, string> = {
|
|
@@ -39,7 +39,7 @@ function decorateProtocolContent(html: string): string {
|
|
|
39
39
|
)
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
/**
|
|
42
|
+
/** ProtocolPage 展示服务条款与隐私协议。 */
|
|
43
43
|
export default function ProtocolPage() {
|
|
44
44
|
const { t } = useI18n()
|
|
45
45
|
const themeStore = useThemeStore()
|
|
@@ -68,18 +68,9 @@ export default function ProtocolPage() {
|
|
|
68
68
|
})
|
|
69
69
|
})
|
|
70
70
|
|
|
71
|
-
/** 返回来源页,页面栈为空时回到首页。 */
|
|
72
|
-
const onBack = () => {
|
|
73
|
-
void Taro.navigateBack({ fail: () => void Taro.reLaunch({ url: homeTabPage }) })
|
|
74
|
-
}
|
|
75
|
-
|
|
76
71
|
return (
|
|
77
72
|
<View className={`protocol-page ${themeStore.theme === 'light' ? 'theme-light' : 'theme-dark'}`}>
|
|
78
|
-
<
|
|
79
|
-
<View className='k-page-nav__back' aria-label={t('common.action.back')} onClick={onBack} />
|
|
80
|
-
<View className='k-page-nav__title'>{pageTitle}</View>
|
|
81
|
-
<View className='k-page-nav__side' aria-hidden />
|
|
82
|
-
</View>
|
|
73
|
+
<KratosNavBar title={pageTitle} />
|
|
83
74
|
<View className='protocol-card'>
|
|
84
75
|
<RichText className='protocol-content' nodes={content} />
|
|
85
76
|
</View>
|