@@ -69,9 +69,9 @@ export default {
69
69
<script setup>
70
70
import { computed , onMounted , reactive , watch } from ' vue'
71
71
import $_ from ' lodash'
72
- import axios from ' axios'
73
72
import { useConfigStore } from ' @/stores/config'
74
73
import { httpAbsPath } from ' @/utils/common'
74
+ import { imgApi , ghCli , isGithubUrl } from ' @/utils/request'
75
75
76
76
const configStore = useConfigStore ()
77
77
@@ -81,36 +81,85 @@ const state = reactive({
81
81
isActive: false ,
82
82
iconB64: null ,
83
83
iconB64OnDark: null ,
84
+ iconBlobUrl: null ,
85
+ iconBlobUrlOnDark: null ,
84
86
})
85
87
86
88
// 图标URL
89
+ // 图标URL默认值
90
+ const DEFAULT_ICON_URL = ' #'
87
91
// 图标URL(常规)
88
92
const iconUrl = computed (() => {
89
- const path = props .website .icon ;
90
- if ($_ .isEmpty (path)) {
91
- return ' #'
93
+ const path = state .iconBlobUrl || props .website .icon ;
94
+ if ($_ .isEmpty (path) || path === DEFAULT_ICON_URL ) {
95
+ return DEFAULT_ICON_URL
96
+ }
97
+ if (path .startsWith (' blob:' )) {
98
+ return path
92
99
}
93
100
94
101
const baseUrl = configStore .baseUrl
95
102
const prefix = configStore .config .favorites .iconPrefix
96
- return httpAbsPath (path, baseUrl + prefix)
103
+ const url = httpAbsPath (path, baseUrl + prefix)
104
+ return fetchIconUrl (url, false )
97
105
})
98
106
// 图标URL(暗色模式)
99
107
const iconUrlOnDark = computed (() => {
100
- let path = props .website .iconOnDark ;
101
- if ($_ .isEmpty (path)) {
108
+ let path = state . iconBlobUrlOnDark || props .website .iconOnDark ;
109
+ if ($_ .isEmpty (path) || path === DEFAULT_ICON_URL ) {
102
110
// 降级到正常图标
103
- path = props .website .icon ;
111
+ path = state . iconBlobUrl || props .website .icon ;
104
112
if ($_ .isEmpty (path)) {
105
- return ' # '
113
+ return DEFAULT_ICON_URL
106
114
}
107
115
}
116
+ if (path .startsWith (' blob:' )) {
117
+ return path
118
+ }
108
119
109
120
const baseUrl = configStore .baseUrl
110
121
const prefix = configStore .config .favorites .iconPrefix
111
- return httpAbsPath (path, baseUrl + prefix)
122
+ const url = httpAbsPath (path, baseUrl + prefix)
123
+ return fetchIconUrl (url, true )
112
124
})
113
125
126
+ // 对某些站点特殊处理图标URL
127
+ const ghIconCache = {}
128
+ function fetchIconUrl (url , onDark ) {
129
+
130
+ // 特殊处理 GitHub URL
131
+ // 避免 GitHub 阻止使用中文语言的浏览器请求
132
+ // 先通过 XHR 请求图标,然后转换为 BlobUrl 进行展示
133
+ if (isGithubUrl (url) && checkNotBase64 (url)) {
134
+ const cacheUrl = ghIconCache[url]
135
+ if (cacheUrl) {
136
+ return cacheUrl
137
+ }
138
+
139
+ ghCli .get (url)
140
+ .then (resp => {
141
+ const blob = new Blob ([resp .data ], { type: resp .headers [' content-type' ] })
142
+ const blobUrl = URL .createObjectURL (blob)
143
+ ghIconCache[url] = blobUrl
144
+ if (onDark) {
145
+ state .iconBlobUrlOnDark = blobUrl
146
+ } else {
147
+ state .iconBlobUrl = blobUrl
148
+ }
149
+ })
150
+ .catch (() => {
151
+ if (onDark) {
152
+ state .iconBlobUrlOnDark = DEFAULT_ICON_URL
153
+ } else {
154
+ state .iconBlobUrl = DEFAULT_ICON_URL
155
+ }
156
+ })
157
+ return DEFAULT_ICON_URL
158
+ }
159
+
160
+ return url
161
+ }
162
+
114
163
// 图标Base64探测
115
164
// 检查图标是否为Base64扩展名的文件
116
165
const BASE64_FILE_EXTENSION = ' .base64'
@@ -124,7 +173,7 @@ const refreshIconB64 = (url) => {
124
173
state .iconB64 = null
125
174
return
126
175
}
127
- axios . get (url)
176
+ imgApi (url)
128
177
.then (resp => {
129
178
state .iconB64 = resp .data
130
179
})
@@ -135,7 +184,7 @@ const refreshIconB64OnDark = (url) => {
135
184
state .iconB64OnDark = null
136
185
return
137
186
}
138
- axios . get (url)
187
+ imgApi (url)
139
188
.then (resp => {
140
189
state .iconB64OnDark = resp .data
141
190
})
0 commit comments