@liujitcn/kratos-uni-app-core 0.0.54 → 0.0.55
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/package.json +1 -1
- package/src/api/base/v1/file.ts +17 -6
- package/src/rpc/base/v1/file.ts +13 -1
- package/src/utils/file.ts +19 -4
- package/src/utils/http.ts +2 -2
- package/src/utils/index.ts +11 -5
- package/src/views/pages/index/index.vue +23 -3
- package/src/views/pages/login/login.vue +22 -2
package/package.json
CHANGED
package/src/api/base/v1/file.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { http } from '../../../utils/http'
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
import {
|
|
3
|
+
BaseFileAccessMode,
|
|
4
|
+
type DownloadFileRequest,
|
|
5
|
+
type MultiUploadFileResponse,
|
|
6
|
+
type FileInfo,
|
|
6
7
|
} from '../../../rpc/base/v1/file'
|
|
7
8
|
|
|
8
9
|
const FILE_URL = '/v1/base/file'
|
|
@@ -10,12 +11,17 @@ const FILE_URL = '/v1/base/file'
|
|
|
10
11
|
/** 文件服务 */
|
|
11
12
|
export class FileServiceImpl {
|
|
12
13
|
/** 多个文件上传 */
|
|
13
|
-
MultiUploadFile(
|
|
14
|
+
MultiUploadFile(
|
|
15
|
+
files: File[],
|
|
16
|
+
fileType: string,
|
|
17
|
+
accessMode = BaseFileAccessMode.BASE_FILE_ACCESS_MODE_AUTHORIZED,
|
|
18
|
+
): Promise<MultiUploadFileResponse> {
|
|
14
19
|
const formData = new FormData()
|
|
15
20
|
files.map((file) => {
|
|
16
21
|
formData.append(file.name, file)
|
|
17
22
|
})
|
|
18
23
|
formData.append('fileType', fileType)
|
|
24
|
+
formData.append('accessMode', String(accessMode))
|
|
19
25
|
return http<MultiUploadFileResponse>({
|
|
20
26
|
url: `${FILE_URL}/multi`,
|
|
21
27
|
method: 'POST',
|
|
@@ -27,10 +33,15 @@ export class FileServiceImpl {
|
|
|
27
33
|
})
|
|
28
34
|
}
|
|
29
35
|
/** 单个文件上传 */
|
|
30
|
-
UploadFile(
|
|
36
|
+
UploadFile(
|
|
37
|
+
file: File,
|
|
38
|
+
fileType: string,
|
|
39
|
+
accessMode = BaseFileAccessMode.BASE_FILE_ACCESS_MODE_AUTHORIZED,
|
|
40
|
+
): Promise<FileInfo> {
|
|
31
41
|
const formData = new FormData()
|
|
32
42
|
formData.append('file', file)
|
|
33
43
|
formData.append('fileType', fileType)
|
|
44
|
+
formData.append('accessMode', String(accessMode))
|
|
34
45
|
return http<FileInfo>({
|
|
35
46
|
url: `${FILE_URL}`,
|
|
36
47
|
method: 'POST',
|
package/src/rpc/base/v1/file.ts
CHANGED
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
// Code generated by protoc-gen-ts_proto. DO NOT EDIT.
|
|
2
2
|
// versions:
|
|
3
|
-
// protoc-gen-ts_proto v2.
|
|
3
|
+
// protoc-gen-ts_proto v2.12.1
|
|
4
4
|
// protoc unknown
|
|
5
5
|
// source: base/v1/file.proto
|
|
6
6
|
|
|
7
7
|
/* eslint-disable */
|
|
8
8
|
import type { BytesValue } from "../../google/protobuf/wrappers";
|
|
9
9
|
|
|
10
|
+
/** 文件访问方式。 */
|
|
11
|
+
export enum BaseFileAccessMode {
|
|
12
|
+
/** BASE_FILE_ACCESS_MODE_UNSPECIFIED - 未指定文件访问方式,服务端按授权访问处理。 */
|
|
13
|
+
BASE_FILE_ACCESS_MODE_UNSPECIFIED = 0,
|
|
14
|
+
/** BASE_FILE_ACCESS_MODE_PUBLIC - 允许匿名公开访问。 */
|
|
15
|
+
BASE_FILE_ACCESS_MODE_PUBLIC = 1,
|
|
16
|
+
/** BASE_FILE_ACCESS_MODE_AUTHORIZED - 必须携带有效访问令牌。 */
|
|
17
|
+
BASE_FILE_ACCESS_MODE_AUTHORIZED = 2,
|
|
18
|
+
}
|
|
19
|
+
|
|
10
20
|
/** 批量上传文件请求参数 */
|
|
11
21
|
export interface MultiUploadFileRequest {
|
|
12
22
|
/** 文件名 */
|
|
@@ -35,6 +45,8 @@ export interface UploadFileInfo {
|
|
|
35
45
|
path: string;
|
|
36
46
|
/** 二进制内容将通过 base64 编码传输 */
|
|
37
47
|
content: Uint8Array;
|
|
48
|
+
/** 文件访问方式,未指定时默认需要访问令牌 */
|
|
49
|
+
access_mode: BaseFileAccessMode;
|
|
38
50
|
}
|
|
39
51
|
|
|
40
52
|
/** 下载文件请求参数 */
|
package/src/utils/file.ts
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
BaseFileAccessMode,
|
|
3
|
+
type FileInfo,
|
|
4
|
+
type MultiUploadFileResponse,
|
|
5
|
+
} from '../rpc/base/v1/file'
|
|
2
6
|
import { formatSrc } from './index'
|
|
3
7
|
import { getLocaleRequestHeaders, t } from '../locales'
|
|
4
8
|
import { getRequestAccessToken } from './http'
|
|
5
9
|
|
|
6
10
|
// 文件上传-兼容小程序端、H5端、App端
|
|
7
|
-
export const uploadFile = async (
|
|
11
|
+
export const uploadFile = async (
|
|
12
|
+
fileType: string,
|
|
13
|
+
filePath: string,
|
|
14
|
+
accessMode = BaseFileAccessMode.BASE_FILE_ACCESS_MODE_AUTHORIZED,
|
|
15
|
+
): Promise<FileInfo> => {
|
|
8
16
|
const token = await getRequestAccessToken()
|
|
9
17
|
const res = await uni.uploadFile({
|
|
10
18
|
url: '/v1/base/file',
|
|
@@ -12,6 +20,7 @@ export const uploadFile = async (fileType: string, filePath: string): Promise<Fi
|
|
|
12
20
|
filePath: filePath,
|
|
13
21
|
formData: {
|
|
14
22
|
fileType: fileType,
|
|
23
|
+
accessMode: String(accessMode),
|
|
15
24
|
},
|
|
16
25
|
header: {
|
|
17
26
|
...getLocaleRequestHeaders(),
|
|
@@ -30,15 +39,20 @@ export const uploadFile = async (fileType: string, filePath: string): Promise<Fi
|
|
|
30
39
|
export const uploadFileList = async (
|
|
31
40
|
fileType: string,
|
|
32
41
|
filePaths: string[],
|
|
42
|
+
accessMode = BaseFileAccessMode.BASE_FILE_ACCESS_MODE_AUTHORIZED,
|
|
33
43
|
): Promise<FileInfo[]> => {
|
|
34
44
|
const results = await Promise.allSettled(
|
|
35
|
-
filePaths.map((filePath) => uploadFile(fileType, filePath)),
|
|
45
|
+
filePaths.map((filePath) => uploadFile(fileType, filePath, accessMode)),
|
|
36
46
|
)
|
|
37
47
|
return results.flatMap((result) => (result.status === 'fulfilled' ? [result.value] : []))
|
|
38
48
|
}
|
|
39
49
|
|
|
40
50
|
// 多文件上传-兼容小程序端、H5端、App端
|
|
41
|
-
export const multiUploadFile = async (
|
|
51
|
+
export const multiUploadFile = async (
|
|
52
|
+
fileType: string,
|
|
53
|
+
files: any,
|
|
54
|
+
accessMode = BaseFileAccessMode.BASE_FILE_ACCESS_MODE_AUTHORIZED,
|
|
55
|
+
): Promise<FileInfo[]> => {
|
|
42
56
|
const token = await getRequestAccessToken()
|
|
43
57
|
const res = await uni.uploadFile({
|
|
44
58
|
url: '/v1/base/file/multi',
|
|
@@ -47,6 +61,7 @@ export const multiUploadFile = async (fileType: string, files: any): Promise<Fil
|
|
|
47
61
|
files: files,
|
|
48
62
|
formData: {
|
|
49
63
|
fileType: fileType,
|
|
64
|
+
accessMode: String(accessMode),
|
|
50
65
|
},
|
|
51
66
|
header: {
|
|
52
67
|
...getLocaleRequestHeaders(),
|
package/src/utils/http.ts
CHANGED
|
@@ -72,8 +72,8 @@ type HttpRequestOptions = UniApp.RequestOptions & {
|
|
|
72
72
|
// 添加拦截器
|
|
73
73
|
const httpInterceptor = {
|
|
74
74
|
// 拦截前触发
|
|
75
|
-
invoke(options:
|
|
76
|
-
const authMode = resolveAuthMode(options
|
|
75
|
+
invoke(options: HttpRequestOptions) {
|
|
76
|
+
const authMode = resolveAuthMode(options, options.url)
|
|
77
77
|
// 1. 相对接口地址只拼接一次基础路径,兼容 HMR 等场景下拦截器重复注册。
|
|
78
78
|
if (
|
|
79
79
|
!options.url.startsWith('http') &&
|
package/src/utils/index.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
const baseURL = import.meta.env.VITE_APP_STATIC_URL
|
|
1
|
+
const baseURL = import.meta.env.VITE_APP_STATIC_URL || import.meta.env.VITE_APP_API_URL
|
|
2
|
+
|
|
3
|
+
/** 将后端数据文件地址切换到当前构建配置的静态资源域名。 */
|
|
4
|
+
const rewriteDataFileOrigin = (src: string, staticOrigin: string) => {
|
|
5
|
+
if (!staticOrigin) return src
|
|
6
|
+
const match = src.match(/^https?:\/\/[^/]+(\/data(?:\/|$).*)$/)
|
|
7
|
+
return match ? `${staticOrigin.replace(/\/$/, '')}${match[1]}` : src
|
|
8
|
+
}
|
|
2
9
|
|
|
3
10
|
/**
|
|
4
11
|
* 解析静态资源访问前缀
|
|
@@ -60,12 +67,11 @@ export const formatSrc = (src: string) => {
|
|
|
60
67
|
if (!src) {
|
|
61
68
|
return src
|
|
62
69
|
}
|
|
63
|
-
|
|
70
|
+
const staticOrigin = resolveStaticOrigin()
|
|
71
|
+
// 后端数据文件即使保存了旧的绝对域名,也统一使用当前静态资源域名。
|
|
64
72
|
if (/^https?:\/\//.test(src)) {
|
|
65
|
-
return src
|
|
73
|
+
return rewriteDataFileOrigin(src, staticOrigin)
|
|
66
74
|
}
|
|
67
|
-
|
|
68
|
-
const staticOrigin = resolveStaticOrigin()
|
|
69
75
|
if (!staticOrigin) {
|
|
70
76
|
return src
|
|
71
77
|
}
|
|
@@ -1,15 +1,35 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { onLoad } from '@dcloudio/uni-app'
|
|
3
|
-
import { computed } from 'vue'
|
|
3
|
+
import { computed, ref, watch } from 'vue'
|
|
4
4
|
import { useSettingStore } from '../../../stores'
|
|
5
5
|
import defaultLogo from '../../../static/images/logo_icon.png'
|
|
6
|
+
import { formatSrc } from '../../../utils'
|
|
6
7
|
import { useI18n } from '../../../locales'
|
|
7
8
|
|
|
8
9
|
const settingStore = useSettingStore()
|
|
9
10
|
const { t } = useI18n()
|
|
10
11
|
const mainTitle = computed(() => settingStore.getData('mainTitle') || t('core.home.main_title'))
|
|
11
12
|
const subTitle = computed(() => settingStore.getData('subTitle') || t('core.home.sub_title'))
|
|
12
|
-
const
|
|
13
|
+
const configuredAppLogo = computed(() => {
|
|
14
|
+
const configuredLogo = settingStore.getData('appLogo')
|
|
15
|
+
return configuredLogo ? formatSrc(configuredLogo) : defaultLogo
|
|
16
|
+
})
|
|
17
|
+
const appLogo = ref(defaultLogo)
|
|
18
|
+
|
|
19
|
+
watch(
|
|
20
|
+
configuredAppLogo,
|
|
21
|
+
(value) => {
|
|
22
|
+
appLogo.value = value
|
|
23
|
+
},
|
|
24
|
+
{ immediate: true },
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
/** Logo 加载失败时回退到本地默认图片。 */
|
|
28
|
+
const handleLogoError = () => {
|
|
29
|
+
if (appLogo.value !== defaultLogo) {
|
|
30
|
+
appLogo.value = defaultLogo
|
|
31
|
+
}
|
|
32
|
+
}
|
|
13
33
|
|
|
14
34
|
onLoad(() => {
|
|
15
35
|
void settingStore.loadData().catch(() => undefined)
|
|
@@ -19,7 +39,7 @@ onLoad(() => {
|
|
|
19
39
|
<template>
|
|
20
40
|
<view class="page">
|
|
21
41
|
<view class="hero">
|
|
22
|
-
<image class="logo" :src="appLogo" mode="aspectFit" />
|
|
42
|
+
<image class="logo" :src="appLogo" mode="aspectFit" @error="handleLogoError" />
|
|
23
43
|
<view class="hero-copy">
|
|
24
44
|
<text class="title">{{ mainTitle }}</text>
|
|
25
45
|
<text class="subtitle">{{ subTitle }}</text>
|
|
@@ -7,6 +7,7 @@ import { computed, defineAsyncComponent, nextTick, reactive, ref, watch } from '
|
|
|
7
7
|
import { defLoginService } from '../../../api/base/v1/login'
|
|
8
8
|
import { defMfaService } from '../../../api/base/v1/mfa'
|
|
9
9
|
import defaultLogo from '../../../static/images/logo_icon.png'
|
|
10
|
+
import { formatSrc } from '../../../utils'
|
|
10
11
|
import { homeTabPage } from '../../../utils/navigation'
|
|
11
12
|
import { PASSWORD_CRYPTO_SCENE, encryptPassword } from '../../../utils/passwordCrypto'
|
|
12
13
|
import { createWebAuthnCredential, getWebAuthnAssertion } from '../../../utils/webauthn'
|
|
@@ -41,9 +42,28 @@ const configuredMainTitle = computed(
|
|
|
41
42
|
const configuredSubTitle = computed(
|
|
42
43
|
() => settingStore.getData('subTitle') || t('core.login.default_sub_title'),
|
|
43
44
|
)
|
|
44
|
-
const configuredAppLogo = computed(() =>
|
|
45
|
+
const configuredAppLogo = computed(() => {
|
|
46
|
+
const configuredLogo = settingStore.getData('appLogo')
|
|
47
|
+
return configuredLogo ? formatSrc(configuredLogo) : defaultLogo
|
|
48
|
+
})
|
|
49
|
+
const appLogo = ref(defaultLogo)
|
|
45
50
|
const showTenantCode = computed(() => settingStore.getData('showTenantCode') !== 'false')
|
|
46
51
|
|
|
52
|
+
watch(
|
|
53
|
+
configuredAppLogo,
|
|
54
|
+
(value) => {
|
|
55
|
+
appLogo.value = value
|
|
56
|
+
},
|
|
57
|
+
{ immediate: true },
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
/** Logo 加载失败时回退到本地默认图片。 */
|
|
61
|
+
const handleLogoError = () => {
|
|
62
|
+
if (appLogo.value !== defaultLogo) {
|
|
63
|
+
appLogo.value = defaultLogo
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
47
67
|
// 是否同意协议
|
|
48
68
|
const isAgreePrivacy = ref(false)
|
|
49
69
|
const isAgreePrivacyShakeY = ref(false)
|
|
@@ -867,7 +887,7 @@ onLoad(() => {
|
|
|
867
887
|
</picker>
|
|
868
888
|
<view class="login-hero">
|
|
869
889
|
<view class="login-logo-shell">
|
|
870
|
-
<image :src="
|
|
890
|
+
<image :src="appLogo" @error="handleLogoError" />
|
|
871
891
|
</view>
|
|
872
892
|
<view class="login-hero-copy">
|
|
873
893
|
<text class="login-title">{{ configuredMainTitle }}</text>
|