前端:增加测试数据模式切换
This commit is contained in:
@@ -834,6 +834,35 @@ function hasExpectedRequest(expectedRequest) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const source = (relativePath) => readFile(path.join(frontendRoot, relativePath), 'utf8')
|
||||||
|
|
||||||
|
test('Web 测试模式统一附加请求头并通过路由重建清空页面状态', async () => {
|
||||||
|
const requestSource = await source('src/utils/request.js')
|
||||||
|
const appMainSource = await source('src/layout/components/AppMain.vue')
|
||||||
|
const navbarSource = await source('src/layout/components/Navbar.vue')
|
||||||
|
|
||||||
|
assert.match(requestSource, /X-Test-Batch-Id/)
|
||||||
|
assert.match(requestSource, /TEST_BATCH_UNAVAILABLE/)
|
||||||
|
assert.match(appMainSource, /scopeVersion/)
|
||||||
|
assert.match(navbarSource, /TestBatchModeSwitch/)
|
||||||
|
assert.match(navbarSource, /测试数据/)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Web 测试批次上下文仅持久化 ID,登出清除 ID 且管理员标记严格来自后端布尔值', async () => {
|
||||||
|
const contextSource = await source('src/utils/testBatchContext.js')
|
||||||
|
const storeSource = await source('src/store/modules/testBatch.js')
|
||||||
|
const userSource = await source('src/store/modules/user.js')
|
||||||
|
|
||||||
|
assert.match(contextSource, /sessionStorage/)
|
||||||
|
assert.doesNotMatch(contextSource, /batchName|status|admin|permission/i)
|
||||||
|
assert.match(storeSource, /getStoredTestBatchId/)
|
||||||
|
assert.match(storeSource, /setStoredTestBatchId/)
|
||||||
|
assert.match(storeSource, /clearStoredTestBatchId/)
|
||||||
|
assert.match(userSource, /admin:\s*false/)
|
||||||
|
assert.match(userSource, /this\.admin\s*=\s*user\.admin\s*===\s*true/)
|
||||||
|
assert.match(userSource, /clearStoredTestBatchId\(\)/)
|
||||||
|
})
|
||||||
|
|
||||||
test('17 个页面的真实权限门禁逐项匹配独立硬编码契约', async () => {
|
test('17 个页面的真实权限门禁逐项匹配独立硬编码契约', async () => {
|
||||||
assert.equal(expectedPagePermissions.length, 17)
|
assert.equal(expectedPagePermissions.length, 17)
|
||||||
assert.equal(expectedButtonPermissions.length, 44)
|
assert.equal(expectedButtonPermissions.length, 44)
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 查询当前用户可用的测试数据批次。
|
||||||
|
export function listAvailableTestBatches() {
|
||||||
|
return request({
|
||||||
|
url: '/system/test-batch/options',
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,185 @@
|
|||||||
|
<template>
|
||||||
|
<div class="test-batch-mode-switch">
|
||||||
|
<el-tag
|
||||||
|
v-if="isTestMode"
|
||||||
|
class="mode-tag test-mode-tag"
|
||||||
|
type="danger"
|
||||||
|
effect="dark"
|
||||||
|
>
|
||||||
|
测试数据 · {{ testBatchState.currentBatchName || '已选批次' }}
|
||||||
|
</el-tag>
|
||||||
|
<el-tag v-else class="mode-tag" type="info" effect="plain">正式数据</el-tag>
|
||||||
|
|
||||||
|
<el-radio-group v-model="selectedMode" class="mode-selector" size="small" @change="handleModeChange">
|
||||||
|
<el-radio-button label="formal">正式</el-radio-button>
|
||||||
|
<el-radio-button label="test" :disabled="isLoading || hasLoadError || !activeOptions.length">测试</el-radio-button>
|
||||||
|
</el-radio-group>
|
||||||
|
|
||||||
|
<el-select
|
||||||
|
v-if="selectedMode === 'test'"
|
||||||
|
v-model="selectedBatchId"
|
||||||
|
class="batch-selector"
|
||||||
|
size="small"
|
||||||
|
:loading="isLoading"
|
||||||
|
:disabled="isLoading || hasLoadError || !activeOptions.length"
|
||||||
|
placeholder="选择测试批次"
|
||||||
|
@change="handleBatchSelect"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="batch in activeOptions"
|
||||||
|
:key="batch.batchId"
|
||||||
|
:label="batch.batchName"
|
||||||
|
:value="String(batch.batchId)"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed, onMounted, reactive, ref, watch } from 'vue'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
|
||||||
|
const testBatchState = reactive({
|
||||||
|
options: [],
|
||||||
|
currentBatchId: '',
|
||||||
|
currentBatchName: '',
|
||||||
|
scopeVersion: 0
|
||||||
|
})
|
||||||
|
const selectedMode = ref('formal')
|
||||||
|
const selectedBatchId = ref('')
|
||||||
|
const isLoading = ref(true)
|
||||||
|
const hasLoadError = ref(false)
|
||||||
|
let testBatchStore
|
||||||
|
|
||||||
|
const isTestMode = computed(() => Boolean(testBatchState.currentBatchId))
|
||||||
|
const activeOptions = computed(() => testBatchState.options.filter(item => item.status === 'ACTIVE'))
|
||||||
|
|
||||||
|
watch(
|
||||||
|
isTestMode,
|
||||||
|
value => {
|
||||||
|
selectedMode.value = value ? 'test' : 'formal'
|
||||||
|
selectedBatchId.value = value ? String(testBatchState.currentBatchId) : ''
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
hasLoadError.value = false
|
||||||
|
try {
|
||||||
|
const { default: useTestBatchStore } = await import('@/store/modules/testBatch')
|
||||||
|
testBatchStore = useTestBatchStore()
|
||||||
|
watch(
|
||||||
|
() => ({
|
||||||
|
options: testBatchStore.options,
|
||||||
|
currentBatchId: testBatchStore.currentBatchId,
|
||||||
|
currentBatchName: testBatchStore.currentBatchName,
|
||||||
|
scopeVersion: testBatchStore.scopeVersion
|
||||||
|
}),
|
||||||
|
value => Object.assign(testBatchState, value),
|
||||||
|
{ deep: true, immediate: true }
|
||||||
|
)
|
||||||
|
await testBatchStore.refreshOptions()
|
||||||
|
} catch (error) {
|
||||||
|
testBatchState.options = []
|
||||||
|
hasLoadError.value = true
|
||||||
|
ElMessage.error('测试批次选项加载失败')
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
async function confirmModeSwitch() {
|
||||||
|
try {
|
||||||
|
await ElMessageBox.confirm('切换数据模式将清空当前页面未提交内容', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
} catch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleModeChange(mode) {
|
||||||
|
if (mode === 'test') {
|
||||||
|
if (!activeOptions.value.length) {
|
||||||
|
selectedMode.value = 'formal'
|
||||||
|
ElMessage.warning('暂无可用测试批次')
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isTestMode.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (await confirmModeSwitch()) {
|
||||||
|
testBatchStore.leaveTestMode()
|
||||||
|
} else {
|
||||||
|
selectedMode.value = 'test'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleBatchSelect(batchId) {
|
||||||
|
const batch = activeOptions.value.find(item => String(item.batchId) === String(batchId))
|
||||||
|
if (!batch || String(batch.batchId) === String(testBatchState.currentBatchId)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!(await confirmModeSwitch())) {
|
||||||
|
selectedBatchId.value = isTestMode.value ? String(testBatchState.currentBatchId) : ''
|
||||||
|
selectedMode.value = isTestMode.value ? 'test' : 'formal'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
testBatchStore.selectBatch(batch)
|
||||||
|
} catch (error) {
|
||||||
|
selectedBatchId.value = isTestMode.value ? String(testBatchState.currentBatchId) : ''
|
||||||
|
selectedMode.value = isTestMode.value ? 'test' : 'formal'
|
||||||
|
ElMessage.error(error.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.test-batch-mode-switch {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
min-width: 0;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mode-tag {
|
||||||
|
flex: 0 1 auto;
|
||||||
|
min-width: 0;
|
||||||
|
max-width: 154px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.test-mode-tag {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mode-selector {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.batch-selector {
|
||||||
|
width: 144px;
|
||||||
|
flex: 0 1 144px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 767px) {
|
||||||
|
.mode-selector,
|
||||||
|
.batch-selector {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mode-tag {
|
||||||
|
max-width: 116px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
<router-view v-slot="{ Component, route }">
|
<router-view v-slot="{ Component, route }">
|
||||||
<transition name="fade-transform" mode="out-in">
|
<transition name="fade-transform" mode="out-in">
|
||||||
<keep-alive :include="tagsViewStore.cachedViews">
|
<keep-alive :include="tagsViewStore.cachedViews">
|
||||||
<component v-if="!route.meta.link" :is="Component" :key="route.path"/>
|
<component v-if="!route.meta.link" :is="Component" :key="`${route.path}:${testBatchStore.scopeVersion}`"/>
|
||||||
</keep-alive>
|
</keep-alive>
|
||||||
</transition>
|
</transition>
|
||||||
</router-view>
|
</router-view>
|
||||||
@@ -19,9 +19,11 @@ import useTagsViewStore from '@/store/modules/tagsView'
|
|||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const tagsViewStore = useTagsViewStore()
|
const tagsViewStore = useTagsViewStore()
|
||||||
|
const testBatchStore = reactive({ scopeVersion: 0 })
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
addIframe()
|
addIframe()
|
||||||
|
syncTestBatchScope()
|
||||||
})
|
})
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
@@ -33,6 +35,14 @@ function addIframe() {
|
|||||||
useTagsViewStore().addIframeView(route)
|
useTagsViewStore().addIframeView(route)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function syncTestBatchScope() {
|
||||||
|
const { default: useTestBatchStore } = await import('@/store/modules/testBatch')
|
||||||
|
const store = useTestBatchStore()
|
||||||
|
watch(() => store.scopeVersion, value => {
|
||||||
|
testBatchStore.scopeVersion = value
|
||||||
|
}, { immediate: true })
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@@ -121,4 +131,3 @@ function addIframe() {
|
|||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<div class="right-menu">
|
<div class="right-menu">
|
||||||
|
<test-batch-mode-switch class="right-menu-item test-batch-mode" aria-label="测试数据模式" />
|
||||||
<template v-if="appStore.device !== 'mobile'">
|
<template v-if="appStore.device !== 'mobile'">
|
||||||
<header-search id="header-search" class="right-menu-item" />
|
<header-search id="header-search" class="right-menu-item" />
|
||||||
|
|
||||||
@@ -59,6 +60,7 @@ import Hamburger from '@/components/Hamburger'
|
|||||||
import Screenfull from '@/components/Screenfull'
|
import Screenfull from '@/components/Screenfull'
|
||||||
import SizeSelect from '@/components/SizeSelect'
|
import SizeSelect from '@/components/SizeSelect'
|
||||||
import HeaderSearch from '@/components/HeaderSearch'
|
import HeaderSearch from '@/components/HeaderSearch'
|
||||||
|
import TestBatchModeSwitch from '@/components/TestBatchModeSwitch'
|
||||||
import useAppStore from '@/store/modules/app'
|
import useAppStore from '@/store/modules/app'
|
||||||
import useUserStore from '@/store/modules/user'
|
import useUserStore from '@/store/modules/user'
|
||||||
import useSettingsStore from '@/store/modules/settings'
|
import useSettingsStore from '@/store/modules/settings'
|
||||||
@@ -205,6 +207,9 @@ async function toggleTheme(event) {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
|
min-width: 0;
|
||||||
|
flex-shrink: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
@@ -277,6 +282,25 @@ async function toggleTheme(event) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.test-batch-mode {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
min-width: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 767px) {
|
||||||
|
.navbar .right-menu {
|
||||||
|
.avatar-container .avatar-wrapper .user-nickname {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-container .avatar-wrapper .user-avatar {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import { defineStore } from 'pinia'
|
||||||
|
import { listAvailableTestBatches } from '@/api/system/testBatch'
|
||||||
|
import {
|
||||||
|
clearStoredTestBatchId,
|
||||||
|
getStoredTestBatchId,
|
||||||
|
setStoredTestBatchId
|
||||||
|
} from '@/utils/testBatchContext'
|
||||||
|
|
||||||
|
const useTestBatchStore = defineStore('testBatch', {
|
||||||
|
state: () => ({
|
||||||
|
options: [],
|
||||||
|
currentBatchId: getStoredTestBatchId(),
|
||||||
|
currentBatchName: '',
|
||||||
|
scopeVersion: 0
|
||||||
|
}),
|
||||||
|
getters: {
|
||||||
|
isTestMode: state => Boolean(state.currentBatchId)
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
async refreshOptions() {
|
||||||
|
const response = await listAvailableTestBatches()
|
||||||
|
this.options = Array.isArray(response.data) ? response.data : []
|
||||||
|
const current = this.options.find(item => String(item.batchId) === String(this.currentBatchId))
|
||||||
|
if (this.currentBatchId && (!current || current.status !== 'ACTIVE')) {
|
||||||
|
this.leaveTestMode()
|
||||||
|
} else {
|
||||||
|
this.currentBatchName = current?.batchName || ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
selectBatch(batch) {
|
||||||
|
const allowed = this.options.some(item => item.batchId === batch.batchId && item.status === 'ACTIVE')
|
||||||
|
if (!allowed) {
|
||||||
|
throw new Error('所选测试批次当前不可用')
|
||||||
|
}
|
||||||
|
this.currentBatchId = String(batch.batchId)
|
||||||
|
this.currentBatchName = batch.batchName
|
||||||
|
setStoredTestBatchId(this.currentBatchId)
|
||||||
|
this.scopeVersion += 1
|
||||||
|
},
|
||||||
|
leaveTestMode() {
|
||||||
|
this.currentBatchId = ''
|
||||||
|
this.currentBatchName = ''
|
||||||
|
clearStoredTestBatchId()
|
||||||
|
this.scopeVersion += 1
|
||||||
|
},
|
||||||
|
reset() {
|
||||||
|
this.options = []
|
||||||
|
this.leaveTestMode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export default useTestBatchStore
|
||||||
@@ -4,6 +4,7 @@ import { login, logout, getInfo } from '@/api/login'
|
|||||||
import { getToken, setToken, removeToken } from '@/utils/auth'
|
import { getToken, setToken, removeToken } from '@/utils/auth'
|
||||||
import { isHttp, isEmpty } from "@/utils/validate"
|
import { isHttp, isEmpty } from "@/utils/validate"
|
||||||
import defAva from '@/assets/images/profile.jpg'
|
import defAva from '@/assets/images/profile.jpg'
|
||||||
|
import { clearStoredTestBatchId } from '@/utils/testBatchContext'
|
||||||
|
|
||||||
const useUserStore = defineStore(
|
const useUserStore = defineStore(
|
||||||
'user',
|
'user',
|
||||||
@@ -14,6 +15,7 @@ const useUserStore = defineStore(
|
|||||||
name: '',
|
name: '',
|
||||||
nickName: '',
|
nickName: '',
|
||||||
avatar: '',
|
avatar: '',
|
||||||
|
admin: false,
|
||||||
roles: [],
|
roles: [],
|
||||||
permissions: []
|
permissions: []
|
||||||
}),
|
}),
|
||||||
@@ -53,6 +55,7 @@ const useUserStore = defineStore(
|
|||||||
this.name = user.userName
|
this.name = user.userName
|
||||||
this.nickName = user.nickName
|
this.nickName = user.nickName
|
||||||
this.avatar = avatar
|
this.avatar = avatar
|
||||||
|
this.admin = user.admin === true
|
||||||
/* 初始密码提示 */
|
/* 初始密码提示 */
|
||||||
if(res.isDefaultModifyPwd) {
|
if(res.isDefaultModifyPwd) {
|
||||||
ElMessageBox.confirm('您的密码还是初始密码,请修改密码!', '安全提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => {
|
ElMessageBox.confirm('您的密码还是初始密码,请修改密码!', '安全提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => {
|
||||||
@@ -78,7 +81,9 @@ const useUserStore = defineStore(
|
|||||||
this.token = ''
|
this.token = ''
|
||||||
this.roles = []
|
this.roles = []
|
||||||
this.permissions = []
|
this.permissions = []
|
||||||
|
this.admin = false
|
||||||
removeToken()
|
removeToken()
|
||||||
|
clearStoredTestBatchId()
|
||||||
resolve()
|
resolve()
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
reject(error)
|
reject(error)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { tansParams, blobValidate } from '@/utils/shierfeng'
|
|||||||
import cache from '@/plugins/cache'
|
import cache from '@/plugins/cache'
|
||||||
import { saveAs } from 'file-saver'
|
import { saveAs } from 'file-saver'
|
||||||
import useUserStore from '@/store/modules/user'
|
import useUserStore from '@/store/modules/user'
|
||||||
|
import { clearStoredTestBatchId, getStoredTestBatchId } from '@/utils/testBatchContext'
|
||||||
import {
|
import {
|
||||||
decryptTransportErrorResponse,
|
decryptTransportErrorResponse,
|
||||||
decryptTransportResponse,
|
decryptTransportResponse,
|
||||||
@@ -19,6 +20,19 @@ let downloadLoadingInstance;
|
|||||||
// 是否显示重新登录
|
// 是否显示重新登录
|
||||||
export let isRelogin = { show: false };
|
export let isRelogin = { show: false };
|
||||||
|
|
||||||
|
async function rejectUnavailableTestBatch(data) {
|
||||||
|
const message = data.msg || '当前测试批次不可用,已切回正式数据模式'
|
||||||
|
clearStoredTestBatchId()
|
||||||
|
const { default: useTestBatchStore } = await import('@/store/modules/testBatch')
|
||||||
|
useTestBatchStore().leaveTestMode()
|
||||||
|
ElMessage({ message, type: 'error', duration: 5 * 1000 })
|
||||||
|
return Promise.reject(new Error(message))
|
||||||
|
}
|
||||||
|
|
||||||
|
function isTestBatchUnavailable(data) {
|
||||||
|
return data?.reason === 'TEST_BATCH_UNAVAILABLE'
|
||||||
|
}
|
||||||
|
|
||||||
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
|
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
|
||||||
// 创建axios实例
|
// 创建axios实例
|
||||||
const service = axios.create({
|
const service = axios.create({
|
||||||
@@ -45,6 +59,10 @@ service.interceptors.request.use(async config => {
|
|||||||
if (getToken() && !isToken) {
|
if (getToken() && !isToken) {
|
||||||
config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
|
config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
|
||||||
}
|
}
|
||||||
|
const testBatchId = getStoredTestBatchId()
|
||||||
|
if (getToken() && testBatchId) {
|
||||||
|
config.headers['X-Test-Batch-Id'] = String(testBatchId)
|
||||||
|
}
|
||||||
if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
|
if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
|
||||||
const requestObj = {
|
const requestObj = {
|
||||||
url: config.url,
|
url: config.url,
|
||||||
@@ -98,6 +116,9 @@ service.interceptors.request.use(async config => {
|
|||||||
service.interceptors.response.use(async res => {
|
service.interceptors.response.use(async res => {
|
||||||
// 响应若命中了传输层加密,这里先还原为原始业务 JSON。
|
// 响应若命中了传输层加密,这里先还原为原始业务 JSON。
|
||||||
res = await decryptTransportResponse(res)
|
res = await decryptTransportResponse(res)
|
||||||
|
if (isTestBatchUnavailable(res.data)) {
|
||||||
|
return rejectUnavailableTestBatch(res.data)
|
||||||
|
}
|
||||||
// 未设置状态码则默认成功状态
|
// 未设置状态码则默认成功状态
|
||||||
const code = res.data.code || 200;
|
const code = res.data.code || 200;
|
||||||
// 获取错误信息
|
// 获取错误信息
|
||||||
@@ -135,6 +156,9 @@ service.interceptors.response.use(async res => {
|
|||||||
async error => {
|
async error => {
|
||||||
// 错误响应也可能是加密信封,先尝试解密再进入统一错误提示流程。
|
// 错误响应也可能是加密信封,先尝试解密再进入统一错误提示流程。
|
||||||
error = await decryptTransportErrorResponse(error)
|
error = await decryptTransportErrorResponse(error)
|
||||||
|
if (isTestBatchUnavailable(error.response?.data)) {
|
||||||
|
return rejectUnavailableTestBatch(error.response.data)
|
||||||
|
}
|
||||||
// 若后端提示密钥失效,则清空本地公钥缓存并基于原始请求重试一次。
|
// 若后端提示密钥失效,则清空本地公钥缓存并基于原始请求重试一次。
|
||||||
if (shouldRetryTransportWithFreshKey(error) && error.config && !error.config.__transportRetried) {
|
if (shouldRetryTransportWithFreshKey(error) && error.config && !error.config.__transportRetried) {
|
||||||
invalidateTransportKeyMeta()
|
invalidateTransportKeyMeta()
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
const TEST_BATCH_STORAGE_KEY = 'test-batch-id'
|
||||||
|
|
||||||
|
export function getStoredTestBatchId() {
|
||||||
|
return sessionStorage.getItem(TEST_BATCH_STORAGE_KEY) || ''
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setStoredTestBatchId(batchId) {
|
||||||
|
sessionStorage.setItem(TEST_BATCH_STORAGE_KEY, String(batchId))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function clearStoredTestBatchId() {
|
||||||
|
sessionStorage.removeItem(TEST_BATCH_STORAGE_KEY)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user