fix(前端): 兼容非安全上下文的任务请求键生成
crypto.randomUUID 仅在 HTTPS 或 localhost 下存在,客户内网 以 http://<内网IP> 访问时该 API 为 undefined,而新建任务 对话框在页面挂载即调用,会直接 TypeError 导致整页白屏。 新增 newRequestKey 工具:优先 randomUUID,不可用时降级为 getRandomValues 拼 v4 格式(非安全上下文仍可用)。 Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
parent
ee801652e3
commit
37eb51513f
11
frontend/src/utils/uuid.ts
Normal file
11
frontend/src/utils/uuid.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
// crypto.randomUUID 仅在安全上下文(HTTPS 或 localhost)存在;客户内网常以
|
||||||
|
// http://<内网IP> 访问,此时该 API 为 undefined。降级用 getRandomValues 拼
|
||||||
|
// v4 格式(getRandomValues 在非安全上下文仍然可用)。
|
||||||
|
export function newRequestKey(): string {
|
||||||
|
if (typeof crypto.randomUUID === 'function') return crypto.randomUUID()
|
||||||
|
const bytes = crypto.getRandomValues(new Uint8Array(16))
|
||||||
|
bytes[6] = (bytes[6] & 0x0f) | 0x40 // version 4
|
||||||
|
bytes[8] = (bytes[8] & 0x3f) | 0x80 // variant 10x
|
||||||
|
const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, '0'))
|
||||||
|
return `${hex.slice(0, 4).join('')}-${hex.slice(4, 6).join('')}-${hex.slice(6, 8).join('')}-${hex.slice(8, 10).join('')}-${hex.slice(10, 16).join('')}`
|
||||||
|
}
|
||||||
@ -3,6 +3,7 @@ import { ref, watch } from 'vue'
|
|||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import * as api from '@/api/tplus'
|
import * as api from '@/api/tplus'
|
||||||
import { handledError } from '@/utils/error'
|
import { handledError } from '@/utils/error'
|
||||||
|
import { newRequestKey } from '@/utils/uuid'
|
||||||
import { entities } from './labels'
|
import { entities } from './labels'
|
||||||
|
|
||||||
const visible = defineModel<boolean>({ required: true })
|
const visible = defineModel<boolean>({ required: true })
|
||||||
@ -11,9 +12,9 @@ const emit = defineEmits<{ created: [batch: api.TPlusDetail] }>()
|
|||||||
const taskType = ref<api.TaskType>('READ')
|
const taskType = ref<api.TaskType>('READ')
|
||||||
const selected = ref<api.EntityType[]>(['personnel'])
|
const selected = ref<api.EntityType[]>(['personnel'])
|
||||||
const pending = ref(false)
|
const pending = ref(false)
|
||||||
let requestKey = crypto.randomUUID()
|
let requestKey = newRequestKey()
|
||||||
// 请求失败后保留键,只有意图改变或成功后才换键。
|
// 请求失败后保留键,只有意图改变或成功后才换键。
|
||||||
watch([taskType, selected], () => { requestKey = crypto.randomUUID() }, { deep: true, flush: 'sync' })
|
watch([taskType, selected], () => { requestKey = newRequestKey() }, { deep: true, flush: 'sync' })
|
||||||
|
|
||||||
async function submit(): Promise<void> {
|
async function submit(): Promise<void> {
|
||||||
if (pending.value || !props.configured) return
|
if (pending.value || !props.configured) return
|
||||||
@ -21,7 +22,7 @@ async function submit(): Promise<void> {
|
|||||||
try {
|
try {
|
||||||
const batch = await api.createTask({ task_type: taskType.value,
|
const batch = await api.createTask({ task_type: taskType.value,
|
||||||
entity_types: taskType.value === 'AUDIT' ? [] : [...selected.value], request_key: requestKey })
|
entity_types: taskType.value === 'AUDIT' ? [] : [...selected.value], request_key: requestKey })
|
||||||
requestKey = crypto.randomUUID()
|
requestKey = newRequestKey()
|
||||||
visible.value = false
|
visible.value = false
|
||||||
ElMessage.success('任务已排队,处理完成后可刷新查看')
|
ElMessage.success('任务已排队,处理完成后可刷新查看')
|
||||||
emit('created', batch)
|
emit('created', batch)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user