diff --git a/shierfeng-fastapi-frontend/scripts/verify-business-contract.mjs b/shierfeng-fastapi-frontend/scripts/verify-business-contract.mjs index c053738..a6d42f0 100644 --- a/shierfeng-fastapi-frontend/scripts/verify-business-contract.mjs +++ b/shierfeng-fastapi-frontend/scripts/verify-business-contract.mjs @@ -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 () => { assert.equal(expectedPagePermissions.length, 17) assert.equal(expectedButtonPermissions.length, 44) diff --git a/shierfeng-fastapi-frontend/src/api/system/testBatch.js b/shierfeng-fastapi-frontend/src/api/system/testBatch.js new file mode 100644 index 0000000..5035aab --- /dev/null +++ b/shierfeng-fastapi-frontend/src/api/system/testBatch.js @@ -0,0 +1,9 @@ +import request from '@/utils/request' + +// 查询当前用户可用的测试数据批次。 +export function listAvailableTestBatches() { + return request({ + url: '/system/test-batch/options', + method: 'get' + }) +} diff --git a/shierfeng-fastapi-frontend/src/components/TestBatchModeSwitch/index.vue b/shierfeng-fastapi-frontend/src/components/TestBatchModeSwitch/index.vue new file mode 100644 index 0000000..e6390fc --- /dev/null +++ b/shierfeng-fastapi-frontend/src/components/TestBatchModeSwitch/index.vue @@ -0,0 +1,185 @@ + + + + + diff --git a/shierfeng-fastapi-frontend/src/layout/components/AppMain.vue b/shierfeng-fastapi-frontend/src/layout/components/AppMain.vue index a50d845..bc82d70 100644 --- a/shierfeng-fastapi-frontend/src/layout/components/AppMain.vue +++ b/shierfeng-fastapi-frontend/src/layout/components/AppMain.vue @@ -3,7 +3,7 @@ - + @@ -19,9 +19,11 @@ import useTagsViewStore from '@/store/modules/tagsView' const route = useRoute() const tagsViewStore = useTagsViewStore() +const testBatchStore = reactive({ scopeVersion: 0 }) onMounted(() => { addIframe() + syncTestBatchScope() }) watchEffect(() => { @@ -33,6 +35,14 @@ function addIframe() { 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 }) +} - diff --git a/shierfeng-fastapi-frontend/src/layout/components/Navbar.vue b/shierfeng-fastapi-frontend/src/layout/components/Navbar.vue index 0d60daf..951a372 100644 --- a/shierfeng-fastapi-frontend/src/layout/components/Navbar.vue +++ b/shierfeng-fastapi-frontend/src/layout/components/Navbar.vue @@ -9,6 +9,7 @@
+