@liujitcn/kratos-admin-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.
@@ -1,10 +1,10 @@
1
- import type { MultiUploadFileResponse, FileInfo } from "../../../rpc/base/v1/file";
1
+ import { BaseFileAccessMode, type MultiUploadFileResponse, type FileInfo } from "../../../rpc/base/v1/file";
2
2
  /** 文件服务 */
3
3
  export declare class FileServiceImpl {
4
4
  /** 多个文件上传 */
5
- MultiUploadFile(files: File[], fileType: string): Promise<MultiUploadFileResponse>;
5
+ MultiUploadFile(files: File[], fileType: string, accessMode?: BaseFileAccessMode): Promise<MultiUploadFileResponse>;
6
6
  /** 上传文件 */
7
- UploadFile(file: File, fileType: string): Promise<FileInfo>;
7
+ UploadFile(file: File, fileType: string, accessMode?: BaseFileAccessMode): Promise<FileInfo>;
8
8
  /** 读取文件内容并返回 Blob。 */
9
9
  GetFileBlob(file: string, fileName: string): Promise<Blob>;
10
10
  /** 下载文件 */
@@ -1,5 +1,5 @@
1
1
  import type { UploadUserFile } from "element-plus";
2
- import type { FileInfo } from "../../rpc/base/v1/file";
2
+ import { BaseFileAccessMode, type FileInfo } from "../../rpc/base/v1/file";
3
3
  /** 单文件上传组件属性。 */
4
4
  interface UploadFileProps {
5
5
  fileInfo?: UploadUserFile;
@@ -8,6 +8,7 @@ interface UploadFileProps {
8
8
  fileSize?: number;
9
9
  fileType?: string[];
10
10
  uploadType?: string;
11
+ accessMode?: BaseFileAccessMode;
11
12
  }
12
13
  declare const __VLS_export: import("vue").DefineComponent<UploadFileProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
13
14
  "update:fileInfo": (value: UploadUserFile | undefined) => any;
@@ -16,6 +17,7 @@ declare const __VLS_export: import("vue").DefineComponent<UploadFileProps, {}, {
16
17
  }>, {
17
18
  disabled: boolean;
18
19
  fileType: string[];
20
+ accessMode: BaseFileAccessMode;
19
21
  uploadType: string;
20
22
  fileInfo: UploadUserFile;
21
23
  fileSize: number;
@@ -1,5 +1,5 @@
1
1
  import type { UploadUserFile } from "element-plus";
2
- import type { FileInfo } from "../../rpc/base/v1/file";
2
+ import { BaseFileAccessMode, type FileInfo } from "../../rpc/base/v1/file";
3
3
  /** 多文件上传组件属性。 */
4
4
  interface UploadFilesProps {
5
5
  fileList: UploadUserFile[];
@@ -9,6 +9,7 @@ interface UploadFilesProps {
9
9
  fileSize?: number;
10
10
  fileType?: string[];
11
11
  uploadType?: string;
12
+ accessMode?: BaseFileAccessMode;
12
13
  }
13
14
  declare const __VLS_export: import("vue").DefineComponent<UploadFilesProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
14
15
  "update:fileList": (value: UploadUserFile[]) => any;
@@ -17,6 +18,7 @@ declare const __VLS_export: import("vue").DefineComponent<UploadFilesProps, {},
17
18
  }>, {
18
19
  disabled: boolean;
19
20
  fileType: string[];
21
+ accessMode: BaseFileAccessMode;
20
22
  uploadType: string;
21
23
  fileSize: number;
22
24
  fileList: UploadUserFile[];
@@ -1,9 +1,10 @@
1
- import type { FileInfo } from "../../rpc/base/v1/file";
1
+ import { BaseFileAccessMode, type FileInfo } from "../../rpc/base/v1/file";
2
2
  /** 单图上传组件属性。 */
3
3
  interface UploadFileProps {
4
4
  imageUrl: string;
5
5
  api?: (file: File) => Promise<FileInfo>;
6
6
  uploadType?: string;
7
+ accessMode?: BaseFileAccessMode;
7
8
  drag?: boolean;
8
9
  disabled?: boolean;
9
10
  fileSize?: number;
@@ -27,10 +28,11 @@ declare const __VLS_base: import("vue").DefineComponent<UploadFileProps, {}, {},
27
28
  width: string;
28
29
  height: string;
29
30
  drag: boolean;
31
+ imageUrl: string;
30
32
  fileType: File.ImageMimeType[];
33
+ accessMode: BaseFileAccessMode;
31
34
  uploadType: string;
32
35
  fileSize: number;
33
- imageUrl: string;
34
36
  borderRadius: string;
35
37
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
36
38
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
@@ -1,10 +1,11 @@
1
1
  import type { UploadUserFile } from "element-plus";
2
- import type { FileInfo } from "../../rpc/base/v1/file";
2
+ import { BaseFileAccessMode, type FileInfo } from "../../rpc/base/v1/file";
3
3
  /** 多图上传组件属性。 */
4
4
  interface UploadFileProps {
5
5
  fileList: UploadUserFile[];
6
6
  api?: (file: File) => Promise<FileInfo>;
7
7
  uploadType?: string;
8
+ accessMode?: BaseFileAccessMode;
8
9
  drag?: boolean;
9
10
  disabled?: boolean;
10
11
  limit?: number;
@@ -30,6 +31,7 @@ declare const __VLS_base: import("vue").DefineComponent<UploadFileProps, {}, {},
30
31
  height: string;
31
32
  drag: boolean;
32
33
  fileType: File.ImageMimeType[];
34
+ accessMode: BaseFileAccessMode;
33
35
  uploadType: string;
34
36
  fileSize: number;
35
37
  fileList: UploadUserFile[];
@@ -0,0 +1,8 @@
1
+ import { type Ref } from "vue";
2
+ /**
3
+ * 管理图片地址,并在配置地址为空或加载失败时回退到指定的本地资源。
4
+ */
5
+ export declare function useImageUrl(configuredImageUrl: Readonly<Ref<string>>, fallbackImageUrl: string): {
6
+ imageUrl: Ref<string, string>;
7
+ handleImageError: () => void;
8
+ };
@@ -0,0 +1,8 @@
1
+ import type { Ref } from "vue";
2
+ /**
3
+ * 管理站点 Logo 地址,并在配置地址不可用时回退到本地默认 Logo。
4
+ */
5
+ export declare function useLogoUrl(configuredLogoUrl: Readonly<Ref<string>>): {
6
+ logoUrl: Ref<string, string>;
7
+ handleLogoError: () => void;
8
+ };
@@ -1,4 +1,13 @@
1
1
  import type { BytesValue } from "../../google/protobuf/wrappers";
2
+ /** 文件访问方式。 */
3
+ export declare enum BaseFileAccessMode {
4
+ /** BASE_FILE_ACCESS_MODE_UNSPECIFIED - 未指定文件访问方式,服务端按授权访问处理。 */
5
+ BASE_FILE_ACCESS_MODE_UNSPECIFIED = 0,
6
+ /** BASE_FILE_ACCESS_MODE_PUBLIC - 允许匿名公开访问。 */
7
+ BASE_FILE_ACCESS_MODE_PUBLIC = 1,
8
+ /** BASE_FILE_ACCESS_MODE_AUTHORIZED - 必须携带有效访问令牌。 */
9
+ BASE_FILE_ACCESS_MODE_AUTHORIZED = 2
10
+ }
2
11
  /** 批量上传文件请求参数 */
3
12
  export interface MultiUploadFileRequest {
4
13
  /** 文件名 */
@@ -24,6 +33,8 @@ export interface UploadFileInfo {
24
33
  path: string;
25
34
  /** 二进制内容将通过 base64 编码传输 */
26
35
  content: Uint8Array;
36
+ /** 文件访问方式,未指定时默认需要访问令牌 */
37
+ access_mode: BaseFileAccessMode;
27
38
  }
28
39
  /** 下载文件请求参数 */
29
40
  export interface DownloadFileRequest {
@@ -1,17 +1,18 @@
1
1
  import service from "../../../utils/request";
2
- import type { DownloadFileRequest, MultiUploadFileResponse, FileInfo } from "../../../rpc/base/v1/file";
2
+ import { BaseFileAccessMode, type DownloadFileRequest, type MultiUploadFileResponse, type FileInfo } from "../../../rpc/base/v1/file";
3
3
 
4
4
  const FILE_URL = "/v1/base/file";
5
5
 
6
6
  /** 文件服务 */
7
7
  export class FileServiceImpl {
8
8
  /** 多个文件上传 */
9
- MultiUploadFile(files: File[], fileType: string): Promise<MultiUploadFileResponse> {
9
+ MultiUploadFile(files: File[], fileType: string, accessMode = BaseFileAccessMode.BASE_FILE_ACCESS_MODE_AUTHORIZED): Promise<MultiUploadFileResponse> {
10
10
  const formData = new FormData();
11
11
  files.map(file => {
12
12
  formData.append(file.name, file);
13
13
  });
14
14
  formData.append("fileType", fileType);
15
+ formData.append("accessMode", String(accessMode));
15
16
  return service<FormData, MultiUploadFileResponse>({
16
17
  url: `${FILE_URL}/multi`,
17
18
  method: "post",
@@ -22,10 +23,11 @@ export class FileServiceImpl {
22
23
  });
23
24
  }
24
25
  /** 上传文件 */
25
- UploadFile(file: File, fileType: string): Promise<FileInfo> {
26
+ UploadFile(file: File, fileType: string, accessMode = BaseFileAccessMode.BASE_FILE_ACCESS_MODE_AUTHORIZED): Promise<FileInfo> {
26
27
  const formData = new FormData();
27
28
  formData.append("file", file);
28
29
  formData.append("fileType", fileType);
30
+ formData.append("accessMode", String(accessMode));
29
31
  return service<FormData, FileInfo>({
30
32
  url: `${FILE_URL}`,
31
33
  method: "post",
@@ -1 +1,7 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-label="Admin logo">
2
+ <rect width="64" height="64" rx="16" fill="#2563eb"/>
3
+ <path fill="#fff" d="M17 18.5A4.5 4.5 0 0 1 21.5 14h21a4.5 4.5 0 0 1 4.5 4.5v25a4.5 4.5 0 0 1-4.5 4.5h-21a4.5 4.5 0 0 1-4.5-4.5v-25Zm5 1.5v21h20V20.5H22Zm0 25h20v-1H22v1Z"/>
4
+ <circle cx="23" cy="17.5" r="1.5" fill="#93c5fd"/>
5
+ <circle cx="28" cy="17.5" r="1.5" fill="#93c5fd"/>
6
+ <path fill="#93c5fd" d="M25 26h3v10h-3V26Zm5-3h3v13h-3V23Zm5 6h3v7h-3v-7Z"/>
7
+ </svg>
@@ -39,7 +39,7 @@ import { Delete, Document, Download } from "@element-plus/icons-vue";
39
39
  import { ElNotification, formContextKey, formItemContextKey } from "element-plus";
40
40
  import type { UploadProps, UploadRequestOptions, UploadUserFile } from "element-plus";
41
41
  import { defFileService } from "../../api/base/v1/file";
42
- import type { FileInfo } from "../../rpc/base/v1/file";
42
+ import { BaseFileAccessMode, type FileInfo } from "../../rpc/base/v1/file";
43
43
  import { useLocaleStore } from "../../locales";
44
44
 
45
45
  const { t } = useLocaleStore();
@@ -52,6 +52,7 @@ interface UploadFileProps {
52
52
  fileSize?: number;
53
53
  fileType?: string[];
54
54
  uploadType?: string;
55
+ accessMode?: BaseFileAccessMode; // 文件访问方式,默认需要访问令牌。
55
56
  }
56
57
 
57
58
  const props = withDefaults(defineProps<UploadFileProps>(), {
@@ -59,7 +60,8 @@ const props = withDefaults(defineProps<UploadFileProps>(), {
59
60
  disabled: false,
60
61
  fileSize: 20,
61
62
  fileType: () => [],
62
- uploadType: "file"
63
+ uploadType: "file",
64
+ accessMode: BaseFileAccessMode.BASE_FILE_ACCESS_MODE_AUTHORIZED
63
65
  });
64
66
 
65
67
  const emit = defineEmits<{
@@ -113,7 +115,7 @@ const beforeUpload: UploadProps["beforeUpload"] = rawFile => {
113
115
  /** 执行自定义文件上传。 */
114
116
  const handleHttpUpload = async (options: UploadRequestOptions) => {
115
117
  try {
116
- const api = props.api ?? (file => defFileService.UploadFile(file, props.uploadType));
118
+ const api = props.api ?? (file => defFileService.UploadFile(file, props.uploadType, props.accessMode));
117
119
  const data = await api(options.file);
118
120
  options.onSuccess(data);
119
121
  } catch (error) {
@@ -45,7 +45,7 @@ import { Delete, Document, Download } from "@element-plus/icons-vue";
45
45
  import { ElNotification, formContextKey, formItemContextKey } from "element-plus";
46
46
  import type { UploadProps, UploadRequestOptions, UploadUserFile } from "element-plus";
47
47
  import { defFileService } from "../../api/base/v1/file";
48
- import type { FileInfo } from "../../rpc/base/v1/file";
48
+ import { BaseFileAccessMode, type FileInfo } from "../../rpc/base/v1/file";
49
49
  import { useLocaleStore } from "../../locales";
50
50
 
51
51
  const { t } = useLocaleStore();
@@ -59,6 +59,7 @@ interface UploadFilesProps {
59
59
  fileSize?: number;
60
60
  fileType?: string[];
61
61
  uploadType?: string;
62
+ accessMode?: BaseFileAccessMode; // 文件访问方式,默认需要访问令牌。
62
63
  }
63
64
 
64
65
  const props = withDefaults(defineProps<UploadFilesProps>(), {
@@ -67,7 +68,8 @@ const props = withDefaults(defineProps<UploadFilesProps>(), {
67
68
  limit: 5,
68
69
  fileSize: 20,
69
70
  fileType: () => [],
70
- uploadType: "file"
71
+ uploadType: "file",
72
+ accessMode: BaseFileAccessMode.BASE_FILE_ACCESS_MODE_AUTHORIZED
71
73
  });
72
74
 
73
75
  const emit = defineEmits<{
@@ -129,7 +131,7 @@ const beforeUpload: UploadProps["beforeUpload"] = rawFile => {
129
131
  /** 执行多文件自定义上传。 */
130
132
  const handleHttpUpload = async (options: UploadRequestOptions) => {
131
133
  try {
132
- const api = props.api ?? (file => defFileService.UploadFile(file, props.uploadType));
134
+ const api = props.api ?? (file => defFileService.UploadFile(file, props.uploadType, props.accessMode));
133
135
  const data = await api(options.file);
134
136
  options.onSuccess(data);
135
137
  } catch (error) {
@@ -54,7 +54,7 @@ import { formatSrc } from "../../utils/utils";
54
54
  import { defFileService } from "../../api/base/v1/file";
55
55
  import { ElNotification, formContextKey, formItemContextKey } from "element-plus";
56
56
  import type { UploadProps, UploadRequestOptions } from "element-plus";
57
- import type { FileInfo } from "../../rpc/base/v1/file";
57
+ import { BaseFileAccessMode, type FileInfo } from "../../rpc/base/v1/file";
58
58
  import { useLocaleStore } from "../../locales";
59
59
 
60
60
  const { t } = useLocaleStore();
@@ -63,7 +63,8 @@ const { t } = useLocaleStore();
63
63
  interface UploadFileProps {
64
64
  imageUrl: string; // 图片地址 ==> 必传
65
65
  api?: (file: File) => Promise<FileInfo>; // 上传图片的 api 方法,默认使用当前文件服务 ==> 非必传
66
- uploadType?: string; // 上传文件业务类型 ==> 非必传(默认为 image)
66
+ uploadType?: string;
67
+ accessMode?: BaseFileAccessMode; // 文件访问方式,默认需要访问令牌。
67
68
  drag?: boolean; // 是否支持拖拽上传 ==> 非必传(默认为 true)
68
69
  disabled?: boolean; // 是否禁用上传组件 ==> 非必传(默认为 false)
69
70
  fileSize?: number; // 图片大小限制 ==> 非必传(默认为 5M)
@@ -77,6 +78,7 @@ interface UploadFileProps {
77
78
  const props = withDefaults(defineProps<UploadFileProps>(), {
78
79
  imageUrl: "",
79
80
  uploadType: "image",
81
+ accessMode: BaseFileAccessMode.BASE_FILE_ACCESS_MODE_AUTHORIZED,
80
82
  drag: true,
81
83
  disabled: false,
82
84
  fileSize: 5,
@@ -112,7 +114,7 @@ const emit = defineEmits<{
112
114
  const handleHttpUpload = async (options: UploadRequestOptions) => {
113
115
  try {
114
116
  // 优先使用页面显式传入的业务上传类型,避免不同业务图片都落到同一个 image 分类。
115
- const api = props.api ?? (file => defFileService.UploadFile(file, props.uploadType));
117
+ const api = props.api ?? (file => defFileService.UploadFile(file, props.uploadType, props.accessMode));
116
118
  const data = await api(options.file);
117
119
  emit("update:imageUrl", data.url);
118
120
  // 调用 el-form 内部的校验方法(可自动校验)
@@ -50,7 +50,7 @@ import { defFileService } from "../../api/base/v1/file";
50
50
  import { formatSrc } from "../../utils/utils";
51
51
  import type { UploadProps, UploadFile, UploadUserFile, UploadRequestOptions } from "element-plus";
52
52
  import { ElNotification, formContextKey, formItemContextKey } from "element-plus";
53
- import type { FileInfo } from "../../rpc/base/v1/file";
53
+ import { BaseFileAccessMode, type FileInfo } from "../../rpc/base/v1/file";
54
54
  import { useLocaleStore } from "../../locales";
55
55
 
56
56
  const { t } = useLocaleStore();
@@ -59,7 +59,8 @@ const { t } = useLocaleStore();
59
59
  interface UploadFileProps {
60
60
  fileList: UploadUserFile[];
61
61
  api?: (file: File) => Promise<FileInfo>; // 上传图片的 api 方法,默认使用当前文件服务 ==> 非必传
62
- uploadType?: string; // 上传文件业务类型 ==> 非必传(默认为 image)
62
+ uploadType?: string;
63
+ accessMode?: BaseFileAccessMode; // 文件访问方式,默认需要访问令牌。
63
64
  drag?: boolean; // 是否支持拖拽上传 ==> 非必传(默认为 true)
64
65
  disabled?: boolean; // 是否禁用上传组件 ==> 非必传(默认为 false)
65
66
  limit?: number; // 最大图片上传数 ==> 非必传(默认为 5张)
@@ -73,6 +74,7 @@ interface UploadFileProps {
73
74
  const props = withDefaults(defineProps<UploadFileProps>(), {
74
75
  fileList: () => [],
75
76
  uploadType: "image",
77
+ accessMode: BaseFileAccessMode.BASE_FILE_ACCESS_MODE_AUTHORIZED,
76
78
  drag: true,
77
79
  disabled: false,
78
80
  limit: 5,
@@ -133,7 +135,7 @@ const beforeUpload: UploadProps["beforeUpload"] = rawFile => {
133
135
  const handleHttpUpload = async (options: UploadRequestOptions) => {
134
136
  try {
135
137
  // 优先使用页面显式传入的业务上传类型,避免不同业务图片都落到同一个 image 分类。
136
- const api = props.api ?? (file => defFileService.UploadFile(file, props.uploadType));
138
+ const api = props.api ?? (file => defFileService.UploadFile(file, props.uploadType, props.accessMode));
137
139
  const data = await api(options.file);
138
140
  options.onSuccess(data);
139
141
  } catch (error) {
@@ -0,0 +1,25 @@
1
+ import { ref, watch, type Ref } from "vue";
2
+
3
+ /**
4
+ * 管理图片地址,并在配置地址为空或加载失败时回退到指定的本地资源。
5
+ */
6
+ export function useImageUrl(configuredImageUrl: Readonly<Ref<string>>, fallbackImageUrl: string) {
7
+ const imageUrl = ref(fallbackImageUrl);
8
+
9
+ watch(
10
+ configuredImageUrl,
11
+ value => {
12
+ imageUrl.value = value || fallbackImageUrl;
13
+ },
14
+ { immediate: true }
15
+ );
16
+
17
+ /** 图片加载失败时回退到指定的本地资源。 */
18
+ const handleImageError = () => {
19
+ if (imageUrl.value !== fallbackImageUrl) {
20
+ imageUrl.value = fallbackImageUrl;
21
+ }
22
+ };
23
+
24
+ return { imageUrl, handleImageError };
25
+ }
@@ -0,0 +1,11 @@
1
+ import type { Ref } from "vue";
2
+ import defaultLogoUrl from "../assets/images/logo.svg";
3
+ import { useImageUrl } from "../hooks/useImageUrl";
4
+
5
+ /**
6
+ * 管理站点 Logo 地址,并在配置地址不可用时回退到本地默认 Logo。
7
+ */
8
+ export function useLogoUrl(configuredLogoUrl: Readonly<Ref<string>>) {
9
+ const { imageUrl, handleImageError } = useImageUrl(configuredLogoUrl, defaultLogoUrl);
10
+ return { logoUrl: imageUrl, handleLogoError: handleImageError };
11
+ }
@@ -4,7 +4,12 @@
4
4
  <el-header>
5
5
  <div class="header-lf mask-image">
6
6
  <div class="logo flx-center">
7
- <img class="logo-img" :src="logoUrl" :alt="t('core.layout.logo_alt')" />
7
+ <img
8
+ class="logo-img"
9
+ :src="logoUrl"
10
+ :alt="t('core.layout.logo_alt')"
11
+ @error="handleLogoError"
12
+ />
8
13
  <span class="logo-text">{{ title }}</span>
9
14
  </div>
10
15
  <ToolBarLeft />
@@ -47,6 +52,7 @@ import SubMenu from "../../layouts/components/Menu/SubMenu.vue";
47
52
  import ToolBarLeft from "../../layouts/components/Header/ToolBarLeft.vue";
48
53
  import ToolBarRight from "../../layouts/components/Header/ToolBarRight.vue";
49
54
  import { useLocaleStore } from "../../locales";
55
+ import { useLogoUrl } from "../../hooks/useLogoUrl";
50
56
 
51
57
  const route = useRoute();
52
58
  const authStore = useAuthStore();
@@ -58,7 +64,8 @@ const isCollapse = computed(() => globalStore.isCollapse);
58
64
  const menuList = computed(() => authStore.showMenuListGet);
59
65
  const activeMenu = computed(() => route.path as string);
60
66
  const title = computed(() => configStore.display.sysName || import.meta.env.VITE_GLOB_APP_TITLE);
61
- const logoUrl = computed(() => configStore.display.adminLogo);
67
+ const configuredLogoUrl = computed(() => configStore.display.adminLogo);
68
+ const { logoUrl, handleLogoError } = useLogoUrl(configuredLogoUrl);
62
69
  </script>
63
70
 
64
71
  <style scoped lang="scss">
@@ -3,7 +3,12 @@
3
3
  <el-container class="layout">
4
4
  <div class="aside-split">
5
5
  <div class="logo flx-center">
6
- <img class="logo-img" :src="logoUrl" :alt="t('core.layout.logo_alt')" />
6
+ <img
7
+ class="logo-img"
8
+ :src="logoUrl"
9
+ :alt="t('core.layout.logo_alt')"
10
+ @error="handleLogoError"
11
+ />
7
12
  </div>
8
13
  <el-scrollbar>
9
14
  <div class="split-list">
@@ -61,6 +66,7 @@ import ToolBarLeft from "../../layouts/components/Header/ToolBarLeft.vue";
61
66
  import ToolBarRight from "../../layouts/components/Header/ToolBarRight.vue";
62
67
  import SubMenu from "../../layouts/components/Menu/SubMenu.vue";
63
68
  import { useLocaleStore } from "../../locales";
69
+ import { useLogoUrl } from "../../hooks/useLogoUrl";
64
70
 
65
71
  const route = useRoute();
66
72
  const router = useRouter();
@@ -74,7 +80,8 @@ const menuList = computed(() => authStore.showMenuListGet.filter(item => Boolean
74
80
  const activeMenu = computed(() => route.path as string);
75
81
  const title = computed(() => configStore.display.sysName || import.meta.env.VITE_GLOB_APP_TITLE);
76
82
  const collapseTitle = computed(() => title.value.slice(0, 1).toUpperCase() || "S");
77
- const logoUrl = computed(() => configStore.display.adminLogo);
83
+ const configuredLogoUrl = computed(() => configStore.display.adminLogo);
84
+ const { logoUrl, handleLogoError } = useLogoUrl(configuredLogoUrl);
78
85
 
79
86
  const subMenuList = ref<RouteItem[]>([]);
80
87
  const splitActive = ref("");
@@ -3,7 +3,12 @@
3
3
  <el-container class="layout">
4
4
  <el-header>
5
5
  <div class="logo flx-center">
6
- <img class="logo-img" :src="logoUrl" :alt="t('core.layout.logo_alt')" />
6
+ <img
7
+ class="logo-img"
8
+ :src="logoUrl"
9
+ :alt="t('core.layout.logo_alt')"
10
+ @error="handleLogoError"
11
+ />
7
12
  <span class="logo-text">{{ title }}</span>
8
13
  </div>
9
14
  <el-menu mode="horizontal" :router="false" :default-active="activeMenu">
@@ -45,6 +50,7 @@ import Main from "../../layouts/components/Main/index.vue";
45
50
  import ToolBarRight from "../../layouts/components/Header/ToolBarRight.vue";
46
51
  import SubMenu from "../../layouts/components/Menu/SubMenu.vue";
47
52
  import { useLocaleStore } from "../../locales";
53
+ import { useLogoUrl } from "../../hooks/useLogoUrl";
48
54
 
49
55
  const route = useRoute();
50
56
  const router = useRouter();
@@ -54,7 +60,8 @@ const { t } = useLocaleStore();
54
60
  const menuList = computed(() => authStore.showMenuListGet);
55
61
  const activeMenu = computed(() => route.path as string);
56
62
  const title = computed(() => configStore.display.sysName || import.meta.env.VITE_GLOB_APP_TITLE);
57
- const logoUrl = computed(() => configStore.display.adminLogo);
63
+ const configuredLogoUrl = computed(() => configStore.display.adminLogo);
64
+ const { logoUrl, handleLogoError } = useLogoUrl(configuredLogoUrl);
58
65
 
59
66
  const handleClickMenu = (subItem: RouteItem) => {
60
67
  const target = getRouteTarget(subItem);
@@ -10,6 +10,7 @@
10
10
  class="logo-img"
11
11
  :src="logoUrl"
12
12
  :alt="t('core.layout.logo_alt')"
13
+ @error="handleLogoError"
13
14
  />
14
15
  <span
15
16
  v-show="!isCollapse"
@@ -53,6 +54,7 @@ import ToolBarLeft from "../../layouts/components/Header/ToolBarLeft.vue";
53
54
  import ToolBarRight from "../../layouts/components/Header/ToolBarRight.vue";
54
55
  import SubMenu from "../../layouts/components/Menu/SubMenu.vue";
55
56
  import { useLocaleStore } from "../../locales";
57
+ import { useLogoUrl } from "../../hooks/useLogoUrl";
56
58
 
57
59
  const route = useRoute();
58
60
  const authStore = useAuthStore();
@@ -64,7 +66,8 @@ const isCollapse = computed(() => globalStore.isCollapse);
64
66
  const menuList = computed(() => authStore.showMenuListGet);
65
67
  const activeMenu = computed(() => route.path as string);
66
68
  const title = computed(() => configStore.display.sysName || import.meta.env.VITE_GLOB_APP_TITLE);
67
- const logoUrl = computed(() => configStore.display.adminLogo);
69
+ const configuredLogoUrl = computed(() => configStore.display.adminLogo);
70
+ const { logoUrl, handleLogoError } = useLogoUrl(configuredLogoUrl);
68
71
  const logoWrapperRef = ref<HTMLElement>();
69
72
  const logoIconRef = ref<HTMLImageElement>();
70
73
  const logoTextRef = ref<HTMLElement>();
@@ -7,6 +7,16 @@
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
  /** 下载文件请求参数 */
@@ -4,11 +4,23 @@
4
4
  <SwitchDark class="dark" />
5
5
  <LocaleSwitch v-if="languageOptions.length > 1" class="locale" />
6
6
  <div class="login-left">
7
- <img class="login-left-img" :src="backgroundUrl" :alt="t('core.login.background_alt')" />
7
+ <img
8
+ class="login-left-img"
9
+ :src="backgroundUrl"
10
+ :alt="t('core.login.background_alt')"
11
+ @error="handleBackgroundError"
12
+ />
8
13
  </div>
9
14
  <div ref="loginFormRef" class="login-form">
10
15
  <div ref="loginLogoRef" class="login-logo">
11
- <img v-show="showLogoIcon" ref="logoIconRef" class="login-icon" :src="logoUrl" alt="" />
16
+ <img
17
+ v-show="showLogoIcon"
18
+ ref="logoIconRef"
19
+ class="login-icon"
20
+ :src="logoUrl"
21
+ alt=""
22
+ @error="handleLogoError"
23
+ />
12
24
  <h2 ref="logoTextRef" class="logo-text" :style="{ fontSize: `${logoFontSize}px` }">{{ projectName }}</h2>
13
25
  </div>
14
26
  <LoginForm :dialog-anchor="loginFormRef" />
@@ -24,12 +36,20 @@ import SwitchDark from "../../components/SwitchDark/index.vue";
24
36
  import LocaleSwitch from "../../components/LocaleSwitch/index.vue";
25
37
  import { useConfigStore } from "../../stores/modules/config";
26
38
  import { useLocaleStore } from "../../locales";
39
+ import defaultBackgroundUrl from "../../assets/images/login_left.png";
40
+ import { useLogoUrl } from "../../hooks/useLogoUrl";
41
+ import { useImageUrl } from "../../hooks/useImageUrl";
27
42
 
28
43
  const configStore = useConfigStore();
29
44
  const { t, languageOptions } = useLocaleStore();
30
45
  const projectName = computed(() => configStore.display.sysName || import.meta.env.VITE_GLOB_APP_TITLE);
31
- const logoUrl = computed(() => configStore.display.adminLogo);
32
- const backgroundUrl = computed(() => configStore.display.background);
46
+ const configuredLogoUrl = computed(() => configStore.display.adminLogo);
47
+ const { logoUrl, handleLogoError } = useLogoUrl(configuredLogoUrl);
48
+ const configuredBackgroundUrl = computed(() => configStore.display.background);
49
+ const { imageUrl: backgroundUrl, handleImageError: handleBackgroundError } = useImageUrl(
50
+ configuredBackgroundUrl,
51
+ defaultBackgroundUrl
52
+ );
33
53
 
34
54
  const MAX_LOGO_FONT_SIZE = 42;
35
55
  const PREFERRED_MIN_LOGO_FONT_SIZE = 30;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@liujitcn/kratos-admin-core",
3
3
  "private": false,
4
- "version": "0.0.54",
4
+ "version": "0.0.55",
5
5
  "type": "module",
6
6
  "description": "kratos-admin frontend runtime and base modules",
7
7
  "author": {