create-unibest 4.0.2 → 4.0.3
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 -3
- 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,79 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { storeToRefs } from 'pinia'
|
|
3
|
+
import { LOGIN_PAGE } from '@/router/config'
|
|
4
|
+
import { useUserStore } from '@/store'
|
|
5
|
+
import { useTokenStore } from '@/store/token'
|
|
6
|
+
|
|
7
|
+
definePage({
|
|
8
|
+
style: {
|
|
9
|
+
navigationBarTitleText: '我的',
|
|
10
|
+
},
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
const userStore = useUserStore()
|
|
14
|
+
const tokenStore = useTokenStore()
|
|
15
|
+
// 使用storeToRefs解构userInfo
|
|
16
|
+
const { userInfo } = storeToRefs(userStore)
|
|
17
|
+
|
|
18
|
+
// 微信小程序下登录
|
|
19
|
+
async function handleLogin() {
|
|
20
|
+
// #ifdef MP-WEIXIN
|
|
21
|
+
// 微信登录
|
|
22
|
+
await tokenStore.wxLogin()
|
|
23
|
+
|
|
24
|
+
// #endif
|
|
25
|
+
// #ifndef MP-WEIXIN
|
|
26
|
+
uni.navigateTo({
|
|
27
|
+
url: `${LOGIN_PAGE}`,
|
|
28
|
+
})
|
|
29
|
+
// #endif
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function handleLogout() {
|
|
33
|
+
uni.showModal({
|
|
34
|
+
title: '提示',
|
|
35
|
+
content: '确定要退出登录吗?',
|
|
36
|
+
success: (res) => {
|
|
37
|
+
if (res.confirm) {
|
|
38
|
+
// 清空用户信息
|
|
39
|
+
useTokenStore().logout()
|
|
40
|
+
// 执行退出登录逻辑
|
|
41
|
+
uni.showToast({
|
|
42
|
+
title: '退出登录成功',
|
|
43
|
+
icon: 'success',
|
|
44
|
+
})
|
|
45
|
+
// #ifdef MP-WEIXIN
|
|
46
|
+
// 微信小程序,去首页
|
|
47
|
+
// uni.reLaunch({ url: '/pages/index/index' })
|
|
48
|
+
// #endif
|
|
49
|
+
// #ifndef MP-WEIXIN
|
|
50
|
+
// 非微信小程序,去登录页
|
|
51
|
+
// uni.navigateTo({ url: LOGIN_PAGE })
|
|
52
|
+
// #endif
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
</script>
|
|
58
|
+
|
|
59
|
+
<template>
|
|
60
|
+
<view class="profile-container">
|
|
61
|
+
<view class="mt-3 break-all px-3 text-center text-green-500">
|
|
62
|
+
{{ userInfo.username ? '已登录' : '未登录' }}
|
|
63
|
+
</view>
|
|
64
|
+
<view class="mt-3 break-all px-3">
|
|
65
|
+
{{ JSON.stringify(userInfo, null, 2) }}
|
|
66
|
+
</view>
|
|
67
|
+
|
|
68
|
+
<view class="mt-[60vh] px-3">
|
|
69
|
+
<view class="m-auto w-160px text-center">
|
|
70
|
+
<button v-if="tokenStore.hasLogin" type="warn" class="w-full" @click="handleLogout">
|
|
71
|
+
退出登录
|
|
72
|
+
</button>
|
|
73
|
+
<button v-else type="primary" class="w-full" @click="handleLogin">
|
|
74
|
+
登录
|
|
75
|
+
</button>
|
|
76
|
+
</view>
|
|
77
|
+
</view>
|
|
78
|
+
</view>
|
|
79
|
+
</template>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { getAllPages } from '@/utils'
|
|
2
|
+
|
|
3
|
+
export const LOGIN_STRATEGY_MAP = {
|
|
4
|
+
DEFAULT_NO_NEED_LOGIN: 0, // 黑名单策略,默认可以进入APP
|
|
5
|
+
DEFAULT_NEED_LOGIN: 1, // 白名单策略,默认不可以进入APP,需要强制登录
|
|
6
|
+
}
|
|
7
|
+
// TODO: 1/3 登录策略,默认使用`无需登录策略`,即默认不需要登录就可以访问
|
|
8
|
+
export const LOGIN_STRATEGY = LOGIN_STRATEGY_MAP.DEFAULT_NO_NEED_LOGIN
|
|
9
|
+
export const isNeedLoginMode = LOGIN_STRATEGY === LOGIN_STRATEGY_MAP.DEFAULT_NEED_LOGIN
|
|
10
|
+
|
|
11
|
+
export const LOGIN_PAGE = '/pages/auth/login'
|
|
12
|
+
export const REGISTER_PAGE = '/pages/auth/register'
|
|
13
|
+
|
|
14
|
+
export const LOGIN_PAGE_LIST = [LOGIN_PAGE, REGISTER_PAGE]
|
|
15
|
+
|
|
16
|
+
// 在 definePage 里面配置了 excludeLoginPath 的页面,功能与 EXCLUDE_LOGIN_PATH_LIST 相同
|
|
17
|
+
export const excludeLoginPathList = getAllPages('excludeLoginPath').map(page => page.path)
|
|
18
|
+
|
|
19
|
+
// 排除在外的列表,白名单策略指白名单列表,黑名单策略指黑名单列表
|
|
20
|
+
// TODO: 2/3 在 definePage 配置 excludeLoginPath,或者在下面配置 EXCLUDE_LOGIN_PATH_LIST
|
|
21
|
+
export const EXCLUDE_LOGIN_PATH_LIST = [
|
|
22
|
+
'/pages/xxx/index', // 示例值
|
|
23
|
+
'/pages-sub/xxx/index', // 示例值
|
|
24
|
+
...excludeLoginPathList, // 都是以 / 开头的 path
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
// 在小程序里面是否使用H5的登录页,默认为 false
|
|
28
|
+
// 如果为 true 则复用 h5 的登录逻辑
|
|
29
|
+
// TODO: 3/3 确定自己的登录页是否需要在小程序里面使用
|
|
30
|
+
export const LOGIN_PAGE_ENABLE_IN_MP = false
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { isMp } from '@uni-helper/uni-env'
|
|
2
|
+
/**
|
|
3
|
+
* by 菲鸽 on 2025-08-19
|
|
4
|
+
* 路由拦截,通常也是登录拦截
|
|
5
|
+
* 黑、白名单的配置,请看 config.ts 文件, EXCLUDE_LOGIN_PATH_LIST
|
|
6
|
+
*/
|
|
7
|
+
import { useTokenStore } from '@/store/token'
|
|
8
|
+
import { isPageTabbar, tabbarStore } from '@/tabbar/store'
|
|
9
|
+
import { getAllPages, getLastPage, HOME_PAGE, parseUrlToObj } from '@/utils/index'
|
|
10
|
+
import { EXCLUDE_LOGIN_PATH_LIST, isNeedLoginMode, LOGIN_PAGE, LOGIN_PAGE_ENABLE_IN_MP } from './config'
|
|
11
|
+
|
|
12
|
+
export const FG_LOG_ENABLE = false
|
|
13
|
+
|
|
14
|
+
export function judgeIsExcludePath(path: string) {
|
|
15
|
+
const isDev = import.meta.env.DEV
|
|
16
|
+
if (!isDev) {
|
|
17
|
+
return EXCLUDE_LOGIN_PATH_LIST.includes(path)
|
|
18
|
+
}
|
|
19
|
+
const allExcludeLoginPages = getAllPages('excludeLoginPath') // dev 环境下,需要每次都重新获取,否则新配置就不会生效
|
|
20
|
+
return EXCLUDE_LOGIN_PATH_LIST.includes(path) || (isDev && allExcludeLoginPages.some(page => page.path === path))
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const navigateToInterceptor = {
|
|
24
|
+
// 注意,这里的url是 '/' 开头的,如 '/pages/index/index',跟 'pages.json' 里面的 path 不同
|
|
25
|
+
// 增加对相对路径的处理,BY 网友 @ideal
|
|
26
|
+
invoke({ url, query }: { url: string, query?: Record<string, string> }) {
|
|
27
|
+
if (url === undefined) {
|
|
28
|
+
return
|
|
29
|
+
}
|
|
30
|
+
let { path, query: _query } = parseUrlToObj(url)
|
|
31
|
+
|
|
32
|
+
FG_LOG_ENABLE && console.log('\n\n路由拦截器:-------------------------------------')
|
|
33
|
+
FG_LOG_ENABLE && console.log('路由拦截器 1: url->', url, ', query ->', query)
|
|
34
|
+
const myQuery = { ..._query, ...query }
|
|
35
|
+
// /pages/route-interceptor/index?name=feige&age=30
|
|
36
|
+
FG_LOG_ENABLE && console.log('路由拦截器 2: path->', path, ', _query ->', _query)
|
|
37
|
+
FG_LOG_ENABLE && console.log('路由拦截器 3: myQuery ->', myQuery)
|
|
38
|
+
|
|
39
|
+
// 处理相对路径
|
|
40
|
+
if (!path.startsWith('/')) {
|
|
41
|
+
const currentPath = getLastPage()?.route || ''
|
|
42
|
+
const normalizedCurrentPath = currentPath.startsWith('/') ? currentPath : `/${currentPath}`
|
|
43
|
+
const baseDir = normalizedCurrentPath.substring(0, normalizedCurrentPath.lastIndexOf('/'))
|
|
44
|
+
path = `${baseDir}/${path}`
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// // 处理路由不存在的情况
|
|
48
|
+
// if (path !== '/' && !getAllPages().some(page => page.path === path)) {
|
|
49
|
+
// console.warn('路由不存在:', path)
|
|
50
|
+
// return false // 明确表示阻止原路由继续执行
|
|
51
|
+
// }
|
|
52
|
+
|
|
53
|
+
// // 插件页面
|
|
54
|
+
// if (url.startsWith('plugin://')) {
|
|
55
|
+
// FG_LOG_ENABLE && console.log('路由拦截器 4: plugin:// 路径 ==>', url)
|
|
56
|
+
// path = url
|
|
57
|
+
// }
|
|
58
|
+
|
|
59
|
+
// 处理直接进入路由非首页时,tabbarIndex 不正确的问题
|
|
60
|
+
tabbarStore.setAutoCurIdx(path)
|
|
61
|
+
|
|
62
|
+
// 小程序里面使用平台自带的登录,则不走下面的逻辑
|
|
63
|
+
if (isMp && !LOGIN_PAGE_ENABLE_IN_MP) {
|
|
64
|
+
return true // 明确表示允许路由继续执行
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const tokenStore = useTokenStore()
|
|
68
|
+
FG_LOG_ENABLE && console.log('tokenStore.hasLogin:', tokenStore.hasLogin)
|
|
69
|
+
|
|
70
|
+
// 不管黑白名单,登录了就直接去吧(但是当前不能是登录页)
|
|
71
|
+
if (tokenStore.hasLogin) {
|
|
72
|
+
if (path !== LOGIN_PAGE) {
|
|
73
|
+
return true // 明确表示允许路由继续执行
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
console.log('已经登录,但是还在登录页', myQuery.redirect)
|
|
77
|
+
const url = myQuery.redirect || HOME_PAGE
|
|
78
|
+
if (isPageTabbar(url)) {
|
|
79
|
+
uni.switchTab({ url })
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
uni.navigateTo({ url })
|
|
83
|
+
}
|
|
84
|
+
return false // 明确表示阻止原路由继续执行
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
let fullPath = path
|
|
88
|
+
|
|
89
|
+
if (Object.keys(myQuery).length) {
|
|
90
|
+
fullPath += `?${Object.keys(myQuery).map(key => `${key}=${myQuery[key]}`).join('&')}`
|
|
91
|
+
}
|
|
92
|
+
const redirectUrl = `${LOGIN_PAGE}?redirect=${encodeURIComponent(fullPath)}`
|
|
93
|
+
|
|
94
|
+
// #region 1/2 默认需要登录的情况(白名单策略) ---------------------------
|
|
95
|
+
if (isNeedLoginMode) {
|
|
96
|
+
// 需要登录里面的 EXCLUDE_LOGIN_PATH_LIST 表示白名单,可以直接通过
|
|
97
|
+
if (judgeIsExcludePath(path)) {
|
|
98
|
+
return true // 明确表示允许路由继续执行
|
|
99
|
+
}
|
|
100
|
+
// 否则需要重定向到登录页
|
|
101
|
+
else {
|
|
102
|
+
if (path === LOGIN_PAGE) {
|
|
103
|
+
return true // 明确表示允许路由继续执行
|
|
104
|
+
}
|
|
105
|
+
FG_LOG_ENABLE && console.log('1 isNeedLogin(白名单策略) redirectUrl:', redirectUrl)
|
|
106
|
+
uni.navigateTo({ url: redirectUrl })
|
|
107
|
+
return false // 明确表示阻止原路由继续执行
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// #endregion 1/2 默认需要登录的情况(白名单策略) ---------------------------
|
|
111
|
+
|
|
112
|
+
// #region 2/2 默认不需要登录的情况(黑名单策略) ---------------------------
|
|
113
|
+
else {
|
|
114
|
+
// 不需要登录里面的 EXCLUDE_LOGIN_PATH_LIST 表示黑名单,需要重定向到登录页
|
|
115
|
+
if (judgeIsExcludePath(path)) {
|
|
116
|
+
FG_LOG_ENABLE && console.log('2 isNeedLogin(黑名单策略) redirectUrl:', redirectUrl)
|
|
117
|
+
uni.navigateTo({ url: redirectUrl })
|
|
118
|
+
return false // 修改为false,阻止原路由继续执行
|
|
119
|
+
}
|
|
120
|
+
return true // 明确表示允许路由继续执行
|
|
121
|
+
}
|
|
122
|
+
// #endregion 2/2 默认不需要登录的情况(黑名单策略) ---------------------------
|
|
123
|
+
},
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// 针对 chooseLocation 的特殊处理
|
|
127
|
+
export const chooseLocationInterceptor = {
|
|
128
|
+
invoke(options: any) {
|
|
129
|
+
// 直接放行 chooseLocation 调用
|
|
130
|
+
FG_LOG_ENABLE && console.log('chooseLocation 调用,直接放行:', options)
|
|
131
|
+
return true
|
|
132
|
+
},
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export const routeInterceptor = {
|
|
136
|
+
install() {
|
|
137
|
+
uni.addInterceptor('navigateTo', navigateToInterceptor)
|
|
138
|
+
uni.addInterceptor('reLaunch', navigateToInterceptor)
|
|
139
|
+
uni.addInterceptor('redirectTo', navigateToInterceptor)
|
|
140
|
+
uni.addInterceptor('switchTab', navigateToInterceptor)
|
|
141
|
+
|
|
142
|
+
// 添加 chooseLocation 的拦截器,确保直接放行
|
|
143
|
+
uni.addInterceptor('chooseLocation', chooseLocationInterceptor)
|
|
144
|
+
},
|
|
145
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { FeatureContext } from '../../packages/cli/src/features/interface'
|
|
2
|
+
|
|
3
|
+
export const preApply = async (ctx: FeatureContext) => {
|
|
4
|
+
console.log(`[login] Pre-apply for ${ctx.projectPath}`)
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export const postApply = async (ctx: FeatureContext) => {
|
|
8
|
+
console.log(`[login] 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
|
+
"loginStrategy": {
|
|
6
|
+
"type": "string",
|
|
7
|
+
"enum": ["blacklist", "whitelist"],
|
|
8
|
+
"default": "blacklist",
|
|
9
|
+
"description": "登录策略:blacklist(黑名单) 或 whitelist(白名单)"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"required": []
|
|
13
|
+
}
|