vue2-client 1.22.39 → 1.22.46
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/.env.iot +1 -0
- package/.idea/af-vue2-client.iml +2 -3
- package/.idea/inspectionProfiles/Project_Default.xml +17 -0
- package/1yarn.lock +11850 -0
- package/logs/afgit.config.log +12 -0
- package/logs/afgit.config.log.2026-02-27 +7 -0
- package/logs/afgit.config_error.log +6 -0
- package/logs/afgit.config_error.log.2026-02-27 +3 -0
- package/package.json +1 -1
- package/pnpm-workspace.yaml +4 -0
- package/public/his/editor/mock/bind_data_with_images.html +78 -0
- package/public/his/editor/mock/blank.html +645 -0
- package/src/assets/img/80359c35a5465167cb25ff87bab49035d041a65558b35-YJOr3x.png +0 -0
- package/src/assets/img/hisLogo.png +0 -0
- package/src/assets/svg/unknown-icon.svg +3 -3
- package/src/base-client/components/common/AfMap/InfoWindowComponent.vue +141 -141
- package/src/base-client/components/common/AfMap/demo.vue +492 -492
- package/src/base-client/components/common/HIS/HButtons/HButtons.vue +89 -1
- package/src/base-client/components/common/HIS/HButtons/HButtonsSuffixDemo.vue +141 -0
- package/src/base-client/components/common/Upload/Upload.vue +2 -2
- package/src/base-client/components/common/XInspectionDetailDrawer/components/DeviceStatus.vue +1 -1
- package/src/base-client/components/his/XHisEditor/ImageReportDemo.vue +156 -0
- package/src/base-client/components/his/XHisEditor/XDocTree.vue +529 -529
- package/src/base-client/plugins/GetLoginInfoService.js +26 -1
- package/src/components/ImagePreview/ImagePreview.vue +323 -323
- package/src/constants/crypto.js +9 -1
- package/src/pages/login/Login.vue +107 -3
- package/src/router/async/router.map.js +6 -1
- package/src/services/user.js +7 -3
- package/src/utils/EncryptUtil.js +7 -2
- package/src/utils/login.js +29 -14
- package/src/utils/request.js +14 -3
- package/src/utils/util.js +27 -4
- package/vue.config.js +10 -0
- package/.idea/MarsCodeWorkspaceAppSettings.xml +0 -7
- package/.idea/deployment.xml +0 -14
- package/.idea/encodings.xml +0 -6
- package/.idea/gradle.xml +0 -7
- package/.idea/misc.xml +0 -6
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
autocomplete="autocomplete"
|
|
26
26
|
placeholder="请输入账户名"
|
|
27
27
|
size="large"
|
|
28
|
+
:disabled="smsStep"
|
|
28
29
|
>
|
|
29
30
|
<a-icon slot="prefix" type="user" />
|
|
30
31
|
</a-input>
|
|
@@ -36,10 +37,29 @@
|
|
|
36
37
|
placeholder="请输入密码"
|
|
37
38
|
size="large"
|
|
38
39
|
type="password"
|
|
40
|
+
:disabled="smsStep"
|
|
39
41
|
>
|
|
40
42
|
<a-icon slot="prefix" type="lock" />
|
|
41
43
|
</a-input>
|
|
42
44
|
</a-form-item>
|
|
45
|
+
<a-form-item v-if="smsStep">
|
|
46
|
+
<a-input
|
|
47
|
+
v-decorator="['smsCode', {rules: [{ required: true, message: '请输入验证码', whitespace: true}]}]"
|
|
48
|
+
autocomplete="autocomplete"
|
|
49
|
+
placeholder="请输入短信验证码"
|
|
50
|
+
size="large"
|
|
51
|
+
:maxLength="8"
|
|
52
|
+
>
|
|
53
|
+
<a-icon slot="prefix" type="safety" />
|
|
54
|
+
<a-button
|
|
55
|
+
slot="suffix"
|
|
56
|
+
type="link"
|
|
57
|
+
:disabled="countdown > 0"
|
|
58
|
+
@click="resendSms"
|
|
59
|
+
style="padding: 0 4px;"
|
|
60
|
+
>{{ countdown > 0 ? `${countdown}s 后重发` : '重新发送' }}</a-button>
|
|
61
|
+
</a-input>
|
|
62
|
+
</a-form-item>
|
|
43
63
|
<a-form-item>
|
|
44
64
|
<a-button :loading="loading" htmlType="submit" size="large" class="btn" type="primary">登录</a-button>
|
|
45
65
|
</a-form-item>
|
|
@@ -74,14 +94,68 @@ export default {
|
|
|
74
94
|
return {
|
|
75
95
|
loading: false,
|
|
76
96
|
error: '',
|
|
77
|
-
form: this.$form.createForm(this)
|
|
97
|
+
form: this.$form.createForm(this),
|
|
98
|
+
// V4 短信两阶段:smsStep=false 是 Step1 输入用户名+密码,true 是 Step2 输入短信验证码
|
|
99
|
+
smsStep: false,
|
|
100
|
+
maskedPhone: '',
|
|
101
|
+
countdown: 0,
|
|
102
|
+
countdownTimer: null,
|
|
103
|
+
// Step1 进入 Step2 后字段被 disabled 不可改,仍需提交 Step1 时的原始凭据
|
|
104
|
+
stage1Name: '',
|
|
105
|
+
stage1Password: ''
|
|
78
106
|
}
|
|
79
107
|
},
|
|
80
108
|
computed: {
|
|
81
109
|
...mapState('setting', ['systemName', 'systemDesc', 'homePage', 'ticketPage', 'compatible', 'routeName', 'defaultAvatarUrl'])
|
|
82
110
|
},
|
|
111
|
+
beforeDestroy () {
|
|
112
|
+
this.clearCountdown()
|
|
113
|
+
},
|
|
83
114
|
methods: {
|
|
84
115
|
...mapMutations('account', ['setUser', 'setPermissions', 'setRoles']),
|
|
116
|
+
startCountdown (seconds) {
|
|
117
|
+
this.clearCountdown()
|
|
118
|
+
this.countdown = seconds
|
|
119
|
+
this.countdownTimer = setInterval(() => {
|
|
120
|
+
this.countdown -= 1
|
|
121
|
+
if (this.countdown <= 0) {
|
|
122
|
+
this.clearCountdown()
|
|
123
|
+
}
|
|
124
|
+
}, 1000)
|
|
125
|
+
},
|
|
126
|
+
clearCountdown () {
|
|
127
|
+
if (this.countdownTimer) {
|
|
128
|
+
clearInterval(this.countdownTimer)
|
|
129
|
+
this.countdownTimer = null
|
|
130
|
+
}
|
|
131
|
+
this.countdown = 0
|
|
132
|
+
},
|
|
133
|
+
exitSmsStep () {
|
|
134
|
+
this.smsStep = false
|
|
135
|
+
this.maskedPhone = ''
|
|
136
|
+
this.clearCountdown()
|
|
137
|
+
},
|
|
138
|
+
enterSmsStep (resp) {
|
|
139
|
+
this.$message.info(resp.states)
|
|
140
|
+
this.smsStep = true
|
|
141
|
+
this.maskedPhone = resp.maskedPhone || ''
|
|
142
|
+
this.startCountdown(resp.expireSeconds || 60)
|
|
143
|
+
},
|
|
144
|
+
resendSms () {
|
|
145
|
+
// 重新触发 Step1:不带 smsCode,后端会复用 userId 下发新的验证码并返回 smsPending
|
|
146
|
+
this.loading = true
|
|
147
|
+
V4Login(this.stage1Name, this.stage1Password, this.routeName).then(res => {
|
|
148
|
+
if (res && res.needsSmsCode) {
|
|
149
|
+
this.enterSmsStep(res)
|
|
150
|
+
} else {
|
|
151
|
+
this.$message.info('重发失败,请稍后重试')
|
|
152
|
+
}
|
|
153
|
+
}).catch(msg => {
|
|
154
|
+
this.$message.info(msg || '重发失败')
|
|
155
|
+
}).finally(() => {
|
|
156
|
+
this.loading = false
|
|
157
|
+
})
|
|
158
|
+
},
|
|
85
159
|
onSubmit (e) {
|
|
86
160
|
e.preventDefault()
|
|
87
161
|
this.form.validateFields((err) => {
|
|
@@ -106,7 +180,14 @@ export default {
|
|
|
106
180
|
break
|
|
107
181
|
}
|
|
108
182
|
case 'V4' : {
|
|
109
|
-
|
|
183
|
+
// V4 短信两阶段:Step1 不带 smsCode(密码正确→下发短信);Step2 带 smsCode(验证通过→转发上游拿 token)
|
|
184
|
+
const smsCode = this.smsStep ? this.form.getFieldValue('smsCode') : undefined
|
|
185
|
+
// 把 Step1 凭据缓存到 data.Step1,确保 Step2 时字段 disabled 也能取出原值
|
|
186
|
+
if (!this.smsStep) {
|
|
187
|
+
this.stage1Name = name
|
|
188
|
+
this.stage1Password = password
|
|
189
|
+
}
|
|
190
|
+
V4Login(name, password, this.routeName, smsCode).then(res => this.afterLoginV4(res)).catch(msg => {
|
|
110
191
|
this.error = msg
|
|
111
192
|
this.loading = false
|
|
112
193
|
})
|
|
@@ -132,12 +213,35 @@ export default {
|
|
|
132
213
|
}
|
|
133
214
|
},
|
|
134
215
|
afterLoginV4 (res) {
|
|
216
|
+
// V4 短信两阶段 Step1:密码正确 -> 后端下发短信并返回 smsPending
|
|
217
|
+
// 响应字段:{ states, needsSmsCode: true, maskedPhone, expireSeconds }
|
|
218
|
+
if (res && res.needsSmsCode) {
|
|
219
|
+
this.enterSmsStep(res)
|
|
220
|
+
this.loading = false
|
|
221
|
+
return
|
|
222
|
+
}
|
|
223
|
+
// 仅当 states 含"登录成功"或响应含 access_token 时才视为成功。
|
|
224
|
+
const isSuccess = res && res.access_token
|
|
225
|
+
if (!isSuccess) {
|
|
226
|
+
this.loading = false
|
|
227
|
+
const errMsg = (res && res.states) || '登录失败'
|
|
228
|
+
this.$message.info(errMsg)
|
|
229
|
+
return
|
|
230
|
+
}
|
|
135
231
|
setV4AccessToken(res)
|
|
136
232
|
let data = res.resources
|
|
137
|
-
if (data.data) {
|
|
233
|
+
if (data && data.data) {
|
|
138
234
|
data = data.data
|
|
139
235
|
}
|
|
236
|
+
// 若上游 Step2 返回的是 token 而不是 resources,说明上游不是 V4 业务服务器:
|
|
237
|
+
// 此时前端没有 routes/permissions 信息,提示用户业务侧需要实现 V4 风格返回。
|
|
238
|
+
if (!data) {
|
|
239
|
+
this.loading = false
|
|
240
|
+
this.$message.info('登录成功但上游未返回路由信息,请联系管理员')
|
|
241
|
+
return
|
|
242
|
+
}
|
|
140
243
|
this.$login.login(data).then(() => {
|
|
244
|
+
this.exitSmsStep()
|
|
141
245
|
this.afterGeneral(data)
|
|
142
246
|
if (data.deps === '用户工单登记') {
|
|
143
247
|
this.$router.push(this.ticketPage).catch(() => {
|
|
@@ -57,7 +57,7 @@ routerResource.example = {
|
|
|
57
57
|
path: 'example',
|
|
58
58
|
name: '示例主页面',
|
|
59
59
|
// component: () => import('@vue2-client/pages/LayoutTest/index.vue')
|
|
60
|
-
component: () => import('@vue2-client/base-client/components/common/XDescriptions/demo.vue')
|
|
60
|
+
// component: () => import('@vue2-client/base-client/components/common/XDescriptions/demo.vue')
|
|
61
61
|
// component: () => import('@vue2-client/base-client/components/his/HChart/demo.vue'),
|
|
62
62
|
// component: () => import('@vue2-client/pages/WorkflowDetail/WorkFlowDemo.vue'),
|
|
63
63
|
// component: () => import('@vue2-client/base-client/components/common/XFormTable/demo.vue')
|
|
@@ -94,6 +94,11 @@ routerResource.example = {
|
|
|
94
94
|
// component: () => import('@vue2-client/pages/ReportGrid/index.vue'),
|
|
95
95
|
// component: () => import('@vue2-client/base-client/components/common/HIS/demo.vue'),
|
|
96
96
|
// component: () => import('@vue2-client/components/MarkdownEditor/demo.vue'),
|
|
97
|
+
// component: () => import('@vue2-client/base-client/components/his/XHisEditor/ImageReportDemo.vue')
|
|
98
|
+
// component: () => import('@vue2-client/components/MarkdownEditor/demo.vue'),
|
|
99
|
+
component: () => import('@vue2-client/base-client/components/common/HIS/HButtons/HButtonsSuffixDemo.vue')
|
|
100
|
+
|
|
101
|
+
|
|
97
102
|
}
|
|
98
103
|
// routerResource.example = () =>
|
|
99
104
|
// import('@vue2-client/pages/Example')
|
package/src/services/user.js
CHANGED
|
@@ -38,16 +38,20 @@ export async function modifyPassword (name, password, newPassword) {
|
|
|
38
38
|
})
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
export async function V4Login (name, password, routeName) {
|
|
41
|
+
export async function V4Login (name, password, routeName, smsCode) {
|
|
42
42
|
// 适配未接入基座的单应用项目登录请求加密,如af-iot-web
|
|
43
43
|
if (process.env.VUE_APP_SINGLE_PAPER === 'FALSE') {
|
|
44
44
|
password = bcrypt.hashSync(password, 10)
|
|
45
45
|
}
|
|
46
|
-
|
|
46
|
+
const payload = {
|
|
47
47
|
username: name,
|
|
48
48
|
password: password,
|
|
49
49
|
resourceName: routeName
|
|
50
|
-
}
|
|
50
|
+
}
|
|
51
|
+
if (smsCode) {
|
|
52
|
+
payload.smsCode = smsCode
|
|
53
|
+
}
|
|
54
|
+
return request(V4_LOGIN, METHOD.POST, payload)
|
|
51
55
|
}
|
|
52
56
|
|
|
53
57
|
export async function V4RefreshToken () {
|
package/src/utils/EncryptUtil.js
CHANGED
|
@@ -68,14 +68,19 @@ export default {
|
|
|
68
68
|
return null
|
|
69
69
|
}
|
|
70
70
|
},
|
|
71
|
-
|
|
71
|
+
normalizeEncryptData (data) {
|
|
72
|
+
if (typeof data === 'string') {
|
|
73
|
+
return data
|
|
74
|
+
}
|
|
75
|
+
return JSON.stringify(data)
|
|
76
|
+
},
|
|
72
77
|
AESEncryptCBC(data, CRYPTO_KEY) {
|
|
73
78
|
// 生成随机IV(每次加密不同)
|
|
74
79
|
const iv = CryptoJS.lib.WordArray.random(16)
|
|
75
80
|
|
|
76
81
|
// 使用 AES CBC 模式进行加密
|
|
77
82
|
const encrypted = CryptoJS.AES.encrypt(
|
|
78
|
-
|
|
83
|
+
this.normalizeEncryptData(data),
|
|
79
84
|
CryptoJS.enc.Hex.parse(CRYPTO_KEY),
|
|
80
85
|
{
|
|
81
86
|
iv: iv, // IV
|
package/src/utils/login.js
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
} from '@vue2-client/store/mutation-types'
|
|
14
14
|
import { setAuthorization } from '@vue2-client/utils/request'
|
|
15
15
|
import { loginGen } from '@vue2-client/base-client/plugins/GetLoginInfoService'
|
|
16
|
-
import { RSA_PRIVATE_KEY } from '@vue2-client/constants/crypto'
|
|
16
|
+
import { RSA_PRIVATE_KEY, V3_RSA_PRIVATE_KEY } from '@vue2-client/constants/crypto'
|
|
17
17
|
import EncryptUtil from '@vue2-client/utils/EncryptUtil'
|
|
18
18
|
// 认证类型
|
|
19
19
|
const AUTH_TYPE = {
|
|
@@ -44,14 +44,13 @@ export function checkSingleAuthorization(authType = AUTH_TYPE.BEARER, token) {
|
|
|
44
44
|
|
|
45
45
|
function setAccessToken(data, jwt) {
|
|
46
46
|
const token = jwt || data
|
|
47
|
-
if (token)
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
47
|
+
if (!token) return
|
|
48
|
+
localStorage.setItem(ACCESS_TOKEN, token)
|
|
49
|
+
let timestamp = new Date().getTime() // 当前的时间戳
|
|
50
|
+
timestamp = timestamp + 12 * 60 * 60 * 1000
|
|
51
|
+
// 格式化时间获取年月日, 登陆过期时间
|
|
52
|
+
const dateAfter = new Date(timestamp)
|
|
53
|
+
setAuthorization({ token, expireAt: dateAfter })
|
|
55
54
|
}
|
|
56
55
|
|
|
57
56
|
function afterGeneral(result, options) {
|
|
@@ -97,10 +96,12 @@ function afterLogin(res, options) {
|
|
|
97
96
|
JSON.stringify({ username: name, password: password })
|
|
98
97
|
)
|
|
99
98
|
const jwt = res.jwtNew
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
99
|
+
const isV3Gateway = !!(res.s || res.session)
|
|
100
|
+
// V3 网关:响应含 s/session 表示启用请求体 AES 加密,需先解密会话密钥
|
|
101
|
+
if (isV3Gateway) {
|
|
102
|
+
setSessionKey({ session: res.s || res.session })
|
|
103
103
|
}
|
|
104
|
+
setAccessToken(data, jwt)
|
|
104
105
|
// 获取路由配置
|
|
105
106
|
getRoutesConfig(data, setting.routeName)
|
|
106
107
|
.then((result) => {
|
|
@@ -116,7 +117,6 @@ function afterLogin(res, options) {
|
|
|
116
117
|
}
|
|
117
118
|
result.functions.unshift(resourceManageMain)
|
|
118
119
|
afterGeneral(result, options)
|
|
119
|
-
setAccessToken(data, jwt)
|
|
120
120
|
if (result.deps === '用户工单登记') {
|
|
121
121
|
router.push(setting.ticketPage).catch(() => {
|
|
122
122
|
})
|
|
@@ -173,7 +173,11 @@ function getWebConfigFromStorage() {
|
|
|
173
173
|
}
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
-
/**
|
|
176
|
+
/**
|
|
177
|
+
* V4 登录:解析 session 并写入 v4-session-key。
|
|
178
|
+
* 受 webConfig.setting.requestEncrypt 开关控制,仅当配置开启且响应含 session 时才解密。
|
|
179
|
+
* 使用 RSA_PRIVATE_KEY(V4 产品密钥)。
|
|
180
|
+
*/
|
|
177
181
|
export function setV4SessionKey(res) {
|
|
178
182
|
const webConfig = getWebConfigFromStorage()
|
|
179
183
|
if (!res?.session || !webConfig?.setting?.requestEncrypt) return
|
|
@@ -181,6 +185,17 @@ export function setV4SessionKey(res) {
|
|
|
181
185
|
if (sessionKey) localStorage.setItem(V4_SESSION_KEY, sessionKey)
|
|
182
186
|
}
|
|
183
187
|
|
|
188
|
+
/**
|
|
189
|
+
* V3 玄简网关:解析 session 并写入 v4-session-key。
|
|
190
|
+
* 登录响应含 s/session 时无条件解密(不读 requestEncrypt 开关),表示后续请求走 AES 加密。
|
|
191
|
+
* 使用 V3_RSA_PRIVATE_KEY(玄简网关密钥),与 V4 的 setV4SessionKey 密钥和触发条件均不同。
|
|
192
|
+
*/
|
|
193
|
+
export function setSessionKey(res) {
|
|
194
|
+
if (!res?.session) return
|
|
195
|
+
const sessionKey = EncryptUtil.RSADecryptRaw(res.session, V3_RSA_PRIVATE_KEY)
|
|
196
|
+
if (sessionKey) localStorage.setItem(V4_SESSION_KEY, sessionKey)
|
|
197
|
+
}
|
|
198
|
+
|
|
184
199
|
export function setV4AccessToken(res) {
|
|
185
200
|
const payload = unwrapV4LoginPayload(res)
|
|
186
201
|
if (!payload?.access_token) return
|
package/src/utils/request.js
CHANGED
|
@@ -100,6 +100,16 @@ async function request(url, method, params, config) {
|
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
/**
|
|
104
|
+
* 规范 Authorization 头的 Bearer 前缀,避免重复拼接
|
|
105
|
+
* @param {string} token
|
|
106
|
+
* @returns {string}
|
|
107
|
+
*/
|
|
108
|
+
export function formatBearerToken(token) {
|
|
109
|
+
if (!token) return token
|
|
110
|
+
return token.startsWith('Bearer ') ? token : `Bearer ${token}`
|
|
111
|
+
}
|
|
112
|
+
|
|
103
113
|
/**
|
|
104
114
|
* 设置认证信息
|
|
105
115
|
* @param auth {Object}
|
|
@@ -232,8 +242,9 @@ function loadInterceptors() {
|
|
|
232
242
|
// 让每个请求携带自定义 token 请根据实际情况自行修改
|
|
233
243
|
if (token) {
|
|
234
244
|
if (config.url !== V4_LOGIN) {
|
|
235
|
-
|
|
236
|
-
|
|
245
|
+
const isV3 = ['V2', 'V3'].includes(getSystemVersion())
|
|
246
|
+
// V3 网关:Authorization 需 Bearer 前缀;Access-Token 始终传原始 token
|
|
247
|
+
config.headers[V4_ACCESS_TOKEN] = isV3 ? formatBearerToken(token) : token
|
|
237
248
|
config.headers[ACCESS_TOKEN] = token
|
|
238
249
|
}
|
|
239
250
|
}
|
|
@@ -249,7 +260,7 @@ function loadInterceptors() {
|
|
|
249
260
|
!config.url.includes('/logic/openapi/') &&
|
|
250
261
|
!config.url.includes('devApi') &&
|
|
251
262
|
!config.url.includes('auth/login') &&
|
|
252
|
-
config.url.includes('api/af-')
|
|
263
|
+
(config.url.includes('api/af-') || config.url.includes('rs/user/userLogin'))
|
|
253
264
|
) {
|
|
254
265
|
if (config.data && !(config.data instanceof FormData)) {
|
|
255
266
|
if (v4SessionKey) {
|
package/src/utils/util.js
CHANGED
|
@@ -336,10 +336,33 @@ export function _moment(...args) {
|
|
|
336
336
|
return moment(...args)
|
|
337
337
|
}
|
|
338
338
|
|
|
339
|
-
|
|
340
|
-
// 将 base64 转换为 blob
|
|
339
|
+
function getBase64Mime(base64Data) {
|
|
341
340
|
const arr = base64Data.split(',')
|
|
342
|
-
|
|
341
|
+
return arr[0].match(/:(.*?);/)?.[1] || 'image/png'
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function getImageExtensionFromMime(mime) {
|
|
345
|
+
const extMap = {
|
|
346
|
+
'image/jpeg': 'jpg',
|
|
347
|
+
'image/jpg': 'jpg',
|
|
348
|
+
'image/png': 'png',
|
|
349
|
+
'image/gif': 'gif',
|
|
350
|
+
'image/bmp': 'bmp',
|
|
351
|
+
'image/webp': 'webp'
|
|
352
|
+
}
|
|
353
|
+
return extMap[mime] || mime.replace('image/', '') || 'png'
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function resolveBase64FileName(filename, mime) {
|
|
357
|
+
const ext = getImageExtensionFromMime(mime)
|
|
358
|
+
const baseName = filename.replace(/\.[^.]+$/, '')
|
|
359
|
+
return `${baseName}.${ext}`
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
export function base64ToFile(base64Data, filename = 'image') {
|
|
363
|
+
const arr = base64Data.split(',')
|
|
364
|
+
const mime = getBase64Mime(base64Data)
|
|
365
|
+
const finalName = resolveBase64FileName(filename, mime)
|
|
343
366
|
const bstr = atob(arr[1])
|
|
344
367
|
let n = bstr.length
|
|
345
368
|
const u8arr = new Uint8Array(n)
|
|
@@ -352,5 +375,5 @@ export function base64ToFile(base64Data, filename = 'image.png') {
|
|
|
352
375
|
const blob = new Blob([u8arr], { type: mime })
|
|
353
376
|
|
|
354
377
|
// 创建 File 对象
|
|
355
|
-
return new File([blob],
|
|
378
|
+
return new File([blob], finalName, { type: mime })
|
|
356
379
|
}
|
package/vue.config.js
CHANGED
|
@@ -12,6 +12,7 @@ const isProd = process.env.NODE_ENV === 'production'
|
|
|
12
12
|
|
|
13
13
|
// v4 产品演示
|
|
14
14
|
const liuli = 'http://192.168.50.67:31567'
|
|
15
|
+
// const v3Server = 'http://127.0.0.1:8077'
|
|
15
16
|
const v3Server = 'http://192.168.50.67:31567'
|
|
16
17
|
const geoserver = 'http://192.168.50.67:31567'
|
|
17
18
|
// const gateway = 'http://192.168.50.67:31467'
|
|
@@ -36,6 +37,15 @@ module.exports = {
|
|
|
36
37
|
port: 8020,
|
|
37
38
|
// If you want to turn on the proxy, please remove the mockjs /src/main.jsL11
|
|
38
39
|
proxy: {
|
|
40
|
+
'/api/af-auth/login': {
|
|
41
|
+
target: v3Server,
|
|
42
|
+
},
|
|
43
|
+
'/rs/logic/getLogin': {
|
|
44
|
+
pathRewrite: {
|
|
45
|
+
'^/rs/logic/getLogin': '/getLogin'
|
|
46
|
+
},
|
|
47
|
+
target: v3Server,
|
|
48
|
+
},
|
|
39
49
|
// '/apply-image': {
|
|
40
50
|
// target: revenue,
|
|
41
51
|
// },
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<project version="4">
|
|
3
|
-
<component name="com.codeverse.userSettings.MarscodeWorkspaceAppSettingsState">
|
|
4
|
-
<option name="ckgOperationStatus" value="SUCCESS" />
|
|
5
|
-
<option name="progress" value="1.0" />
|
|
6
|
-
</component>
|
|
7
|
-
</project>
|
package/.idea/deployment.xml
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<project version="4">
|
|
3
|
-
<component name="PublishConfigData" remoteFilesAllowedToDisappearOnAutoupload="false">
|
|
4
|
-
<serverData>
|
|
5
|
-
<paths name="50.4">
|
|
6
|
-
<serverdata>
|
|
7
|
-
<mappings>
|
|
8
|
-
<mapping local="$PROJECT_DIR$" web="/" />
|
|
9
|
-
</mappings>
|
|
10
|
-
</serverdata>
|
|
11
|
-
</paths>
|
|
12
|
-
</serverData>
|
|
13
|
-
</component>
|
|
14
|
-
</project>
|
package/.idea/encodings.xml
DELETED
package/.idea/gradle.xml
DELETED
package/.idea/misc.xml
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
<project version="4">
|
|
2
|
-
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
|
3
|
-
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="corretto-21" project-jdk-type="JavaSDK">
|
|
4
|
-
<output url="file://$PROJECT_DIR$/out" />
|
|
5
|
-
</component>
|
|
6
|
-
</project>
|