create-unibest 4.0.2 → 4.0.4
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/dist/index.js +3 -4
- package/features/i18n/files/src/locale/README.md +12 -0
- package/features/i18n/files/src/locale/en.json +10 -0
- package/features/i18n/files/src/locale/index.ts +81 -0
- package/features/i18n/files/src/locale/zh-Hans.json +10 -0
- package/features/i18n/files/src/pages/i18n/index.vue +132 -0
- package/features/i18n/files/src/store/token.ts +323 -0
- package/features/i18n/files/src/tabbar/TabbarItem.vue +51 -0
- package/features/i18n/files/src/tabbar/config.ts +145 -0
- package/features/i18n/files/src/tabbar/i18n.ts +29 -0
- package/features/i18n/files/src/tabbar/index.vue +140 -0
- package/features/i18n/files/src/types/i18n.d.ts +8 -0
- package/features/i18n/files/src/utils/i18n.ts +10 -0
- package/features/i18n/files/src/utils/index.ts +179 -0
- package/features/i18n/hooks.ts +9 -0
- package/features/i18n/package.json +9 -0
- package/features/i18n/schema.json +13 -0
- package/features/login/files/src/pages/auth/README.md +20 -0
- package/features/login/files/src/pages/auth/login.vue +44 -0
- package/features/login/files/src/pages/auth/register.vue +34 -0
- package/features/login/files/src/pages/me.vue +79 -0
- package/features/login/files/src/router/config.ts +30 -0
- package/features/login/files/src/router/interceptor.ts +145 -0
- package/features/login/hooks.ts +9 -0
- package/features/login/package.json +6 -0
- package/features/login/schema.json +13 -0
- package/package.json +1 -1
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import type { TabBar } from '@uni-helper/vite-plugin-uni-pages'
|
|
2
|
+
import type { CustomTabBarItem, NativeTabBarItem } from './types'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* tabbar 选择的策略,更详细的介绍见 tabbar.md 文件
|
|
6
|
+
* 0: 'NO_TABBAR' `无 tabbar`
|
|
7
|
+
* 1: 'NATIVE_TABBAR' `原生 tabbar`
|
|
8
|
+
* 2: 'CUSTOM_TABBAR' `自定义 tabbar`
|
|
9
|
+
*
|
|
10
|
+
* 温馨提示:本文件的任何代码更改了之后,都需要重新运行,否则 pages.json 不会更新导致配置不生效
|
|
11
|
+
*/
|
|
12
|
+
export const TABBAR_STRATEGY_MAP = {
|
|
13
|
+
NO_TABBAR: 0,
|
|
14
|
+
NATIVE_TABBAR: 1,
|
|
15
|
+
CUSTOM_TABBAR: 2,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// TODO: 1/3. 通过这里切换使用tabbar的策略
|
|
19
|
+
// 如果是使用 NO_TABBAR(0),nativeTabbarList 和 customTabbarList 都不生效
|
|
20
|
+
// 如果是使用 NATIVE_TABBAR(1),只需要配置 nativeTabbarList,customTabbarList 不生效
|
|
21
|
+
// 如果是使用 CUSTOM_TABBAR(2),只需要配置 customTabbarList,nativeTabbarList 不生效
|
|
22
|
+
export const selectedTabbarStrategy = TABBAR_STRATEGY_MAP.CUSTOM_TABBAR
|
|
23
|
+
|
|
24
|
+
// TODO: 2/3. 使用 NATIVE_TABBAR 时,更新下面的 tabbar 配置
|
|
25
|
+
export const nativeTabbarList: NativeTabBarItem[] = [
|
|
26
|
+
{
|
|
27
|
+
iconPath: 'static/tabbar/home.png',
|
|
28
|
+
selectedIconPath: 'static/tabbar/homeHL.png',
|
|
29
|
+
pagePath: 'pages/index/index',
|
|
30
|
+
text: '%tabbar.home%',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
iconPath: 'static/tabbar/personal.png',
|
|
34
|
+
selectedIconPath: 'static/tabbar/personalHL.png',
|
|
35
|
+
pagePath: 'pages/me/me',
|
|
36
|
+
text: '%tabbar.me%',
|
|
37
|
+
},
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
// TODO: 3/3. 使用 CUSTOM_TABBAR 时,更新下面的 tabbar 配置
|
|
41
|
+
// 如果需要配置鼓包,需要在 'tabbar/store.ts' 里面设置,最后在 `tabbar/index.vue` 里面更改鼓包的图片
|
|
42
|
+
export const customTabbarList: CustomTabBarItem[] = [
|
|
43
|
+
{
|
|
44
|
+
text: '%tabbar.home%',
|
|
45
|
+
pagePath: 'pages/index/index',
|
|
46
|
+
// 注意 unocss 图标需要如下处理:(二选一)
|
|
47
|
+
// 1)在fg-tabbar.vue页面上引入一下并注释掉(见tabbar/index.vue代码第2行)
|
|
48
|
+
// 2)配置到 unocss.config.ts 的 safelist 中
|
|
49
|
+
iconType: 'unocss',
|
|
50
|
+
icon: 'i-carbon-home',
|
|
51
|
+
// badge: 'dot',
|
|
52
|
+
},
|
|
53
|
+
// 鼓包配置示例(2025-12-31)
|
|
54
|
+
// 中间鼓包tabbarItem配置:通常是扫描按钮、发布按钮、更多按钮等,点击触发业务逻辑
|
|
55
|
+
// {
|
|
56
|
+
// pagePath: 'pages/me/me',
|
|
57
|
+
// text: '我的',
|
|
58
|
+
// // 1)在fg-tabbar.vue页面上引入一下并注释掉(见tabbar/index.vue代码第2行)
|
|
59
|
+
// // 2)配置到 unocss.config.ts 的 safelist 中
|
|
60
|
+
// iconType: 'image',
|
|
61
|
+
// icon: '/static/tabbar/scan.png',
|
|
62
|
+
// isBulge: true,
|
|
63
|
+
// },
|
|
64
|
+
{
|
|
65
|
+
pagePath: 'pages/i18n/index',
|
|
66
|
+
text: '%i18n.title%',
|
|
67
|
+
iconType: 'unocss',
|
|
68
|
+
icon: 'i-carbon-ibm-watson-language-translator',
|
|
69
|
+
// badge: 10,
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
pagePath: 'pages/me/me',
|
|
73
|
+
text: '%tabbar.me%',
|
|
74
|
+
iconType: 'unocss',
|
|
75
|
+
icon: 'i-carbon-user',
|
|
76
|
+
// badge: 10,
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
// 其他类型演示
|
|
80
|
+
// 1、uiLib
|
|
81
|
+
// {
|
|
82
|
+
// pagePath: 'pages/index/index',
|
|
83
|
+
// text: '首页',
|
|
84
|
+
// iconType: 'uiLib',
|
|
85
|
+
// icon: 'home',
|
|
86
|
+
// },
|
|
87
|
+
// 2、iconfont
|
|
88
|
+
// {
|
|
89
|
+
// pagePath: 'pages/index/index',
|
|
90
|
+
// text: '首页',
|
|
91
|
+
// // 注意 iconfont 图标需要额外加上 'iconfont',如下
|
|
92
|
+
// iconType: 'iconfont',
|
|
93
|
+
// icon: 'iconfont icon-my',
|
|
94
|
+
// },
|
|
95
|
+
// 3、image
|
|
96
|
+
// {
|
|
97
|
+
// pagePath: 'pages/index/index',
|
|
98
|
+
// text: '首页',
|
|
99
|
+
// // 使用 ‘image’时,需要配置 icon + iconActive 2张图片
|
|
100
|
+
// iconType: 'image',
|
|
101
|
+
// icon: '/static/tabbar/home.png',
|
|
102
|
+
// iconActive: '/static/tabbar/homeHL.png',
|
|
103
|
+
// },
|
|
104
|
+
]
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* 是否启用 tabbar 缓存
|
|
108
|
+
* NATIVE_TABBAR(1) 和 CUSTOM_TABBAR(2) 时,需要tabbar缓存
|
|
109
|
+
*/
|
|
110
|
+
export const tabbarCacheEnable
|
|
111
|
+
= [TABBAR_STRATEGY_MAP.NATIVE_TABBAR, TABBAR_STRATEGY_MAP.CUSTOM_TABBAR].includes(selectedTabbarStrategy)
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* 是否启用自定义 tabbar
|
|
115
|
+
* CUSTOM_TABBAR(2) 时,启用自定义tabbar
|
|
116
|
+
*/
|
|
117
|
+
export const customTabbarEnable = [TABBAR_STRATEGY_MAP.CUSTOM_TABBAR].includes(selectedTabbarStrategy)
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* 是否需要隐藏原生 tabbar
|
|
121
|
+
* CUSTOM_TABBAR(2) 时,需要隐藏原生tabbar
|
|
122
|
+
*/
|
|
123
|
+
export const needHideNativeTabbar = selectedTabbarStrategy === TABBAR_STRATEGY_MAP.CUSTOM_TABBAR
|
|
124
|
+
|
|
125
|
+
const _tabbarList = customTabbarEnable ? customTabbarList.map(item => ({ text: item.text, pagePath: item.pagePath })) : nativeTabbarList
|
|
126
|
+
export const tabbarList = customTabbarEnable ? customTabbarList : nativeTabbarList
|
|
127
|
+
|
|
128
|
+
// NATIVE_TABBAR(1) 时,显示原生Tabbar,在i18n的情况下需要 setTabbarItem (框架已经处理)
|
|
129
|
+
export const isNativeTabbar = selectedTabbarStrategy === TABBAR_STRATEGY_MAP.NATIVE_TABBAR
|
|
130
|
+
|
|
131
|
+
const _tabbar: TabBar = {
|
|
132
|
+
// 只有微信小程序支持 custom。App 和 H5 不生效
|
|
133
|
+
custom: selectedTabbarStrategy === TABBAR_STRATEGY_MAP.CUSTOM_TABBAR,
|
|
134
|
+
color: '#999999',
|
|
135
|
+
selectedColor: '#018d71',
|
|
136
|
+
backgroundColor: '#F8F8F8',
|
|
137
|
+
borderStyle: 'black',
|
|
138
|
+
height: '50px',
|
|
139
|
+
fontSize: '10px',
|
|
140
|
+
iconWidth: '24px',
|
|
141
|
+
spacing: '3px',
|
|
142
|
+
list: _tabbarList as unknown as TabBar['list'],
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export const tabBar = tabbarCacheEnable ? _tabbar : undefined
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { t } from '@/locale'
|
|
2
|
+
import { isCurrentPageTabbar } from '@/utils'
|
|
3
|
+
import { isNativeTabbar, tabbarList } from './config'
|
|
4
|
+
|
|
5
|
+
// h5 中一直可以生效,小程序里面默认是无法动态切换的,这里借助vue模板自带响应式的方式
|
|
6
|
+
// 直接替换 %xxx% 为 t('xxx')即可
|
|
7
|
+
export function getI18nText(key: string) {
|
|
8
|
+
// 获取 %xxx% 中的 xxx
|
|
9
|
+
const match = key.match(/%(.+?)%/)
|
|
10
|
+
if (match) {
|
|
11
|
+
key = match[1]
|
|
12
|
+
}
|
|
13
|
+
console.log('设置多语言:', key)
|
|
14
|
+
return t(key)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function setTabbarItem() {
|
|
18
|
+
// 只有使用原生Tabbar才需要 setTabbarItem
|
|
19
|
+
// 而且只有当前页是tabbar页才能设置
|
|
20
|
+
console.log('设置多语言:setTabBarItem', isNativeTabbar, isCurrentPageTabbar())
|
|
21
|
+
if (isNativeTabbar && isCurrentPageTabbar()) {
|
|
22
|
+
tabbarList.forEach((item, index) => {
|
|
23
|
+
uni.setTabBarItem({
|
|
24
|
+
index,
|
|
25
|
+
text: getI18nText(item.text),
|
|
26
|
+
})
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// i-carbon-code
|
|
3
|
+
import { customTabbarEnable, needHideNativeTabbar, tabbarCacheEnable } from './config'
|
|
4
|
+
import { setTabbarItem } from './i18n'
|
|
5
|
+
import { tabbarList, tabbarStore } from './store'
|
|
6
|
+
import TabbarItem from './TabbarItem.vue'
|
|
7
|
+
|
|
8
|
+
// #ifdef MP-WEIXIN
|
|
9
|
+
// 将自定义节点设置成虚拟的(去掉自定义组件包裹层),更加接近Vue组件的表现,能更好的使用flex属性
|
|
10
|
+
defineOptions({
|
|
11
|
+
virtualHost: true,
|
|
12
|
+
})
|
|
13
|
+
// #endif
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 中间的鼓包tabbarItem的点击事件
|
|
17
|
+
*/
|
|
18
|
+
function handleClickBulge() {
|
|
19
|
+
uni.showToast({
|
|
20
|
+
title: '点击了中间的鼓包tabbarItem',
|
|
21
|
+
icon: 'none',
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function handleClick(index: number) {
|
|
26
|
+
// 点击原来的不做操作
|
|
27
|
+
if (index === tabbarStore.curIdx) {
|
|
28
|
+
return
|
|
29
|
+
}
|
|
30
|
+
if (tabbarList[index].isBulge) {
|
|
31
|
+
handleClickBulge()
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
const url = tabbarList[index].pagePath
|
|
35
|
+
tabbarStore.setCurIdx(index)
|
|
36
|
+
if (tabbarCacheEnable) {
|
|
37
|
+
uni.switchTab({ url })
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
uni.navigateTo({ url })
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// #ifndef MP-WEIXIN || MP-ALIPAY
|
|
44
|
+
// 因为有了 custom:true, 微信里面不需要多余的hide操作
|
|
45
|
+
onLoad(() => {
|
|
46
|
+
// 解决原生 tabBar 未隐藏导致有2个 tabBar 的问题
|
|
47
|
+
needHideNativeTabbar
|
|
48
|
+
&& uni.hideTabBar({
|
|
49
|
+
fail(err) {
|
|
50
|
+
console.log('hideTabBar fail: ', err)
|
|
51
|
+
},
|
|
52
|
+
success(res) {
|
|
53
|
+
// console.log('hideTabBar success: ', res)
|
|
54
|
+
},
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
// #endif
|
|
58
|
+
|
|
59
|
+
// #ifdef MP-ALIPAY
|
|
60
|
+
onMounted(() => {
|
|
61
|
+
// 解决支付宝自定义tabbar 未隐藏导致有2个 tabBar 的问题; 注意支付宝很特别,需要在 onMounted 钩子调用
|
|
62
|
+
customTabbarEnable // 另外,支付宝里面,只要是 customTabbar 都需要隐藏
|
|
63
|
+
&& uni.hideTabBar({
|
|
64
|
+
fail(err) {
|
|
65
|
+
console.log('hideTabBar fail: ', err)
|
|
66
|
+
},
|
|
67
|
+
success(res) {
|
|
68
|
+
// console.log('hideTabBar success: ', res)
|
|
69
|
+
},
|
|
70
|
+
})
|
|
71
|
+
})
|
|
72
|
+
// #endif
|
|
73
|
+
const activeColor = 'var(--wot-color-theme, #1890ff)'
|
|
74
|
+
const inactiveColor = '#666'
|
|
75
|
+
function getColorByIndex(index: number) {
|
|
76
|
+
return tabbarStore.curIdx === index ? activeColor : inactiveColor
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// 注意,上面处理的是自定义tabbar,下面处理的是原生tabbar,参考:https://unibest.tech/base/10-i18n
|
|
80
|
+
onShow(() => {
|
|
81
|
+
setTabbarItem()
|
|
82
|
+
})
|
|
83
|
+
</script>
|
|
84
|
+
|
|
85
|
+
<template>
|
|
86
|
+
<view v-if="customTabbarEnable" class="h-50px pb-safe">
|
|
87
|
+
<view class="border-and-fixed bg-white" @touchmove.stop.prevent>
|
|
88
|
+
<view class="h-50px flex items-center">
|
|
89
|
+
<view
|
|
90
|
+
v-for="(item, index) in tabbarList" :key="index"
|
|
91
|
+
class="flex flex-1 flex-col items-center justify-center"
|
|
92
|
+
:style="{ color: getColorByIndex(index) }"
|
|
93
|
+
@click="handleClick(index)"
|
|
94
|
+
>
|
|
95
|
+
<view v-if="item.isBulge" class="relative">
|
|
96
|
+
<!-- 中间一个鼓包tabbarItem的处理 -->
|
|
97
|
+
<view class="bulge">
|
|
98
|
+
<TabbarItem :item="item" :index="index" class="text-center" is-bulge />
|
|
99
|
+
</view>
|
|
100
|
+
</view>
|
|
101
|
+
<TabbarItem v-else :item="item" :index="index" class="relative px-3 text-center" />
|
|
102
|
+
</view>
|
|
103
|
+
</view>
|
|
104
|
+
|
|
105
|
+
<view class="pb-safe" />
|
|
106
|
+
</view>
|
|
107
|
+
</view>
|
|
108
|
+
</template>
|
|
109
|
+
|
|
110
|
+
<style scoped lang="scss">
|
|
111
|
+
.border-and-fixed {
|
|
112
|
+
position: fixed;
|
|
113
|
+
bottom: 0;
|
|
114
|
+
left: 0;
|
|
115
|
+
right: 0;
|
|
116
|
+
z-index: 1000;
|
|
117
|
+
border-top: 1px solid #eee;
|
|
118
|
+
box-sizing: border-box;
|
|
119
|
+
}
|
|
120
|
+
// 中间鼓包的样式
|
|
121
|
+
.bulge {
|
|
122
|
+
position: absolute;
|
|
123
|
+
top: -20px;
|
|
124
|
+
left: 50%;
|
|
125
|
+
transform-origin: top center;
|
|
126
|
+
transform: translateX(-50%) scale(0.5) translateY(-33%);
|
|
127
|
+
display: flex;
|
|
128
|
+
justify-content: center;
|
|
129
|
+
align-items: center;
|
|
130
|
+
width: 250rpx;
|
|
131
|
+
height: 250rpx;
|
|
132
|
+
border-radius: 50%;
|
|
133
|
+
background-color: #fff;
|
|
134
|
+
box-shadow: inset 0 0 0 1px #fefefe;
|
|
135
|
+
|
|
136
|
+
&:active {
|
|
137
|
+
// opacity: 0.8;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
</style>
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import type { PageMetaDatum, SubPackages } from '@uni-helper/vite-plugin-uni-pages'
|
|
2
|
+
import { isMpWeixin } from '@uni-helper/uni-env'
|
|
3
|
+
import { pages, subPackages } from '@/pages.json'
|
|
4
|
+
import { isPageTabbar } from '@/tabbar/store'
|
|
5
|
+
|
|
6
|
+
export type PageInstance = Page.PageInstance<AnyObject, object> & { $page: Page.PageInstance<AnyObject, object> & { fullPath: string } }
|
|
7
|
+
|
|
8
|
+
export function getLastPage() {
|
|
9
|
+
// getCurrentPages() 至少有1个元素,所以不再额外判断
|
|
10
|
+
// const lastPage = getCurrentPages().at(-1)
|
|
11
|
+
// 上面那个在低版本安卓中打包会报错,所以改用下面这个【虽然我加了 src/interceptions/prototype.ts,但依然报错】
|
|
12
|
+
const pages = getCurrentPages()
|
|
13
|
+
return pages[pages.length - 1] as PageInstance
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* 获取当前页面路由的 path 路径和 redirectPath 路径
|
|
18
|
+
* path 如 '/pages/login/login'
|
|
19
|
+
* redirectPath 如 '/pages/demo/base/route-interceptor'
|
|
20
|
+
*/
|
|
21
|
+
export function currRoute() {
|
|
22
|
+
const lastPage = getLastPage() as PageInstance
|
|
23
|
+
if (!lastPage) {
|
|
24
|
+
return {
|
|
25
|
+
path: '',
|
|
26
|
+
query: {},
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const currRoute = lastPage.$page
|
|
30
|
+
// console.log('lastPage.$page:', currRoute)
|
|
31
|
+
// console.log('lastPage.$page.fullpath:', currRoute.fullPath)
|
|
32
|
+
// console.log('lastPage.$page.options:', currRoute.options)
|
|
33
|
+
// console.log('lastPage.options:', (lastPage as any).options)
|
|
34
|
+
// 经过多端测试,只有 fullPath 靠谱,其他都不靠谱
|
|
35
|
+
const { fullPath } = currRoute
|
|
36
|
+
// console.log(fullPath)
|
|
37
|
+
// eg: /pages/login/login?redirect=%2Fpages%2Fdemo%2Fbase%2Froute-interceptor (小程序)
|
|
38
|
+
// eg: /pages/login/login?redirect=%2Fpages%2Froute-interceptor%2Findex%3Fname%3Dfeige%26age%3D30(h5)
|
|
39
|
+
return parseUrlToObj(fullPath)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function ensureDecodeURIComponent(url: string) {
|
|
43
|
+
if (url.startsWith('%')) {
|
|
44
|
+
return ensureDecodeURIComponent(decodeURIComponent(url))
|
|
45
|
+
}
|
|
46
|
+
return url
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* 解析 url 得到 path 和 query
|
|
50
|
+
* 比如输入url: /pages/login/login?redirect=%2Fpages%2Fdemo%2Fbase%2Froute-interceptor
|
|
51
|
+
* 输出: {path: /pages/login/login, query: {redirect: /pages/demo/base/route-interceptor}}
|
|
52
|
+
*/
|
|
53
|
+
export function parseUrlToObj(url: string) {
|
|
54
|
+
const [path, queryStr] = url.split('?')
|
|
55
|
+
// console.log(path, queryStr)
|
|
56
|
+
|
|
57
|
+
if (!queryStr) {
|
|
58
|
+
return {
|
|
59
|
+
path,
|
|
60
|
+
query: {},
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const query: Record<string, string> = {}
|
|
64
|
+
queryStr.split('&').forEach((item) => {
|
|
65
|
+
const [key, value] = item.split('=')
|
|
66
|
+
// console.log(key, value)
|
|
67
|
+
query[key] = ensureDecodeURIComponent(value) // 这里需要统一 decodeURIComponent 一下,可以兼容h5和微信y
|
|
68
|
+
})
|
|
69
|
+
return { path, query }
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* 得到所有的需要登录的 pages,包括主包和分包的
|
|
73
|
+
* 这里设计得通用一点,可以传递 key 作为判断依据,默认是 excludeLoginPath, 与 route-block 配对使用
|
|
74
|
+
* 如果没有传 key,则表示所有的 pages,如果传递了 key, 则表示通过 key 过滤
|
|
75
|
+
*/
|
|
76
|
+
export function getAllPages(key?: string) {
|
|
77
|
+
// 这里处理主包
|
|
78
|
+
const mainPages = (pages as PageMetaDatum[])
|
|
79
|
+
.filter(page => !key || page[key])
|
|
80
|
+
.map(page => ({
|
|
81
|
+
...page,
|
|
82
|
+
path: `/${page.path}`,
|
|
83
|
+
}))
|
|
84
|
+
|
|
85
|
+
// 这里处理分包
|
|
86
|
+
const subPages: PageMetaDatum[] = []
|
|
87
|
+
;(subPackages as SubPackages).forEach((subPageObj) => {
|
|
88
|
+
// console.log(subPageObj)
|
|
89
|
+
const { root } = subPageObj
|
|
90
|
+
|
|
91
|
+
subPageObj.pages
|
|
92
|
+
.filter(page => !key || page[key])
|
|
93
|
+
.forEach((page) => {
|
|
94
|
+
subPages.push({
|
|
95
|
+
...page,
|
|
96
|
+
path: `/${root}/${page.path}`,
|
|
97
|
+
})
|
|
98
|
+
})
|
|
99
|
+
})
|
|
100
|
+
const result = [...mainPages, ...subPages]
|
|
101
|
+
// console.log(`getAllPages by ${key} result: `, result)
|
|
102
|
+
return result
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function getCurrentPageI18nKey() {
|
|
106
|
+
const routeObj = currRoute()
|
|
107
|
+
|
|
108
|
+
let currPage = (pages as PageMetaDatum[]).find(page => `/${page.path}` === routeObj.path)
|
|
109
|
+
if (!currPage) {
|
|
110
|
+
// 在主包中找不到对应的页面,则在分包中找
|
|
111
|
+
const allSubPages: PageMetaDatum[] = []
|
|
112
|
+
subPackages?.forEach((config) => {
|
|
113
|
+
config.pages?.forEach((cur) => {
|
|
114
|
+
allSubPages.push({
|
|
115
|
+
...cur,
|
|
116
|
+
path: `/${config.root}/${cur.path}`,
|
|
117
|
+
})
|
|
118
|
+
})
|
|
119
|
+
})
|
|
120
|
+
currPage = allSubPages.find(page => page.path === routeObj.path)
|
|
121
|
+
if (!currPage) {
|
|
122
|
+
console.warn('路由不正确')
|
|
123
|
+
return ''
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
console.log(currPage)
|
|
127
|
+
console.log(currPage.style.navigationBarTitleText)
|
|
128
|
+
return currPage.style?.navigationBarTitleText || ''
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function isCurrentPageTabbar() {
|
|
132
|
+
const { path } = currRoute()
|
|
133
|
+
return isPageTabbar(path)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* 根据微信小程序当前环境,判断应该获取的 baseUrl
|
|
138
|
+
*/
|
|
139
|
+
export function getEnvBaseUrl() {
|
|
140
|
+
// 请求基准地址
|
|
141
|
+
let baseUrl = import.meta.env.VITE_SERVER_BASEURL
|
|
142
|
+
|
|
143
|
+
// # 有些同学可能需要在微信小程序里面根据 develop、trial、release 分别设置上传地址,参考代码如下。
|
|
144
|
+
const VITE_SERVER_BASEURL__WEIXIN_DEVELOP = 'https://ukw0y1.laf.run'
|
|
145
|
+
const VITE_SERVER_BASEURL__WEIXIN_TRIAL = 'https://ukw0y1.laf.run'
|
|
146
|
+
const VITE_SERVER_BASEURL__WEIXIN_RELEASE = 'https://ukw0y1.laf.run'
|
|
147
|
+
|
|
148
|
+
// 微信小程序端环境区分
|
|
149
|
+
if (isMpWeixin) {
|
|
150
|
+
const {
|
|
151
|
+
miniProgram: { envVersion },
|
|
152
|
+
} = uni.getAccountInfoSync()
|
|
153
|
+
|
|
154
|
+
switch (envVersion) {
|
|
155
|
+
case 'develop':
|
|
156
|
+
baseUrl = VITE_SERVER_BASEURL__WEIXIN_DEVELOP || baseUrl
|
|
157
|
+
break
|
|
158
|
+
case 'trial':
|
|
159
|
+
baseUrl = VITE_SERVER_BASEURL__WEIXIN_TRIAL || baseUrl
|
|
160
|
+
break
|
|
161
|
+
case 'release':
|
|
162
|
+
baseUrl = VITE_SERVER_BASEURL__WEIXIN_RELEASE || baseUrl
|
|
163
|
+
break
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return baseUrl
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* 是否是双token模式
|
|
172
|
+
*/
|
|
173
|
+
export const isDoubleTokenMode = import.meta.env.VITE_AUTH_MODE === 'double'
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* 首页路径,通过 page.json 里面的 type 为 home 的页面获取,如果没有,则默认是第一个页面
|
|
177
|
+
* 通常为 /pages/index/index
|
|
178
|
+
*/
|
|
179
|
+
export const HOME_PAGE = `/${(pages as PageMetaDatum[]).find(page => page.type === 'home')?.path || (pages as PageMetaDatum[])[0].path}`
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { FeatureContext } from '../../packages/cli/src/features/interface'
|
|
2
|
+
|
|
3
|
+
export async function preApply(ctx: FeatureContext) {
|
|
4
|
+
console.log(`[i18n] Pre-apply for ${ctx.projectPath}`)
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export async function postApply(ctx: FeatureContext) {
|
|
8
|
+
console.log(`[i18n] Post-apply for ${ctx.projectPath}`)
|
|
9
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"type": "object",
|
|
4
|
+
"properties": {
|
|
5
|
+
"locales": {
|
|
6
|
+
"type": "array",
|
|
7
|
+
"items": { "type": "string" },
|
|
8
|
+
"default": ["zh-Hans", "en"],
|
|
9
|
+
"description": "支持的语言列表"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"required": []
|
|
13
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# 登录页
|
|
2
|
+
需要输入账号、密码/验证码的登录页。
|
|
3
|
+
|
|
4
|
+
## 适用性
|
|
5
|
+
|
|
6
|
+
本页面主要用于 `h5` 和 `APP`。
|
|
7
|
+
|
|
8
|
+
小程序通常有平台的登录方式 `uni.login` 通常用不到登录页,所以不适用于 `小程序`。(即默认情况下,小程序环境是不会走登录拦截逻辑的。)
|
|
9
|
+
|
|
10
|
+
但是如果您的小程序也需要现实的 `登录页` 那也是可以使用的。
|
|
11
|
+
|
|
12
|
+
在 `src/router/config.ts` 中有一个变量 `LOGIN_PAGE_ENABLE_IN_MP` 来控制是否在小程序中使用 `H5的登录页`。
|
|
13
|
+
|
|
14
|
+
更多信息请看 `src/router` 文件夹的内容。
|
|
15
|
+
|
|
16
|
+
## 登录跳转
|
|
17
|
+
|
|
18
|
+
目前登录的跳转逻辑主要在 `src/router/interceptor.ts` 和 `src/pages/login/login.vue` 里面,默认会在登录后自动重定向到来源/配置的页面。
|
|
19
|
+
|
|
20
|
+
如果与您的业务不符,您可以自行修改。
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { useTokenStore } from '@/store/token'
|
|
3
|
+
|
|
4
|
+
definePage({
|
|
5
|
+
style: {
|
|
6
|
+
navigationBarTitleText: '登录',
|
|
7
|
+
},
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
const tokenStore = useTokenStore()
|
|
11
|
+
async function doLogin() {
|
|
12
|
+
if (tokenStore.hasLogin) {
|
|
13
|
+
uni.navigateBack()
|
|
14
|
+
return
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
// 调用登录接口
|
|
18
|
+
await tokenStore.login({
|
|
19
|
+
username: '菲鸽',
|
|
20
|
+
password: '123456',
|
|
21
|
+
})
|
|
22
|
+
uni.navigateBack()
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
console.log('登录失败', error)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
</script>
|
|
29
|
+
|
|
30
|
+
<template>
|
|
31
|
+
<view class="login">
|
|
32
|
+
<!-- 本页面是非MP的登录页,主要用于 h5 和 APP -->
|
|
33
|
+
<view class="text-center">
|
|
34
|
+
登录页
|
|
35
|
+
</view>
|
|
36
|
+
<button class="mt-4 w-40 text-center" @click="doLogin">
|
|
37
|
+
点击模拟登录
|
|
38
|
+
</button>
|
|
39
|
+
</view>
|
|
40
|
+
</template>
|
|
41
|
+
|
|
42
|
+
<style lang="scss" scoped>
|
|
43
|
+
//
|
|
44
|
+
</style>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { LOGIN_PAGE } from '@/router/config'
|
|
3
|
+
|
|
4
|
+
definePage({
|
|
5
|
+
style: {
|
|
6
|
+
navigationBarTitleText: '注册',
|
|
7
|
+
},
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
function doRegister() {
|
|
11
|
+
uni.showToast({
|
|
12
|
+
title: '注册成功',
|
|
13
|
+
})
|
|
14
|
+
// 注册成功后跳转到登录页
|
|
15
|
+
uni.navigateTo({
|
|
16
|
+
url: LOGIN_PAGE,
|
|
17
|
+
})
|
|
18
|
+
}
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<template>
|
|
22
|
+
<view class="login">
|
|
23
|
+
<view class="text-center">
|
|
24
|
+
注册页
|
|
25
|
+
</view>
|
|
26
|
+
<button class="mt-4 w-40 text-center" @click="doRegister">
|
|
27
|
+
点击模拟注册
|
|
28
|
+
</button>
|
|
29
|
+
</view>
|
|
30
|
+
</template>
|
|
31
|
+
|
|
32
|
+
<style lang="scss" scoped>
|
|
33
|
+
//
|
|
34
|
+
</style>
|