From 3ec9ea6717846fe0ae73a0ed5f93751f00cbd0c4 Mon Sep 17 00:00:00 2001 From: Vicky <10212746+haifeng-jiang@user.noreply.gitee.com> Date: Fri, 16 Oct 2026 16:44:19 +0800 Subject: [PATCH] =?UTF-8?q?fix(web):=20=E9=80=82=E9=85=8D=E5=90=8E?= =?UTF-8?q?=E7=AB=AF=20ID=20=E5=AD=97=E7=AC=A6=E4=B8=B2=E5=8C=96=EF=BC=88?= =?UTF-8?q?=E9=9B=AA=E8=8A=B1=20id=20=E4=B8=8D=E8=83=BD=E8=BF=87=20Number?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 后端 J1 起所有 Long id 以字符串下发,审计出 4 处把 id 当数字用的地方: - utils/creatorImpersonation.ts:代看创作者 id 曾 Number(raw) 存 localStorage, 19 位 id 会被四舍五入导致 X-Creator-Id 头带错 id;改为全程字符串 - contentManagement/EpisodeManagementView.vue:Number(route.query.drama) 与 item.id 比较, 从短剧管理带 ?drama= 跳过来会选不中;改为字符串比较 - creator-center/composables/useDramaEdit.js:Number(route.params.id) 同上,作品编辑打不开 - permission/composables/useMenuManagement.js:顶级菜单哨兵 0 与接口的 "0" 类型不一致, 下拉回显会变裸 0;统一成字符串 '0' 核过不需要改:分页 total 后端保持数字;role.id === selectedRoleId 等两侧都来自接口; formatCount 内部 Number() 吃字符串;全局无 id 算术/下标/a.id-b.id。 --- src/utils/creatorImpersonation.ts | 19 ++++++++++++------- .../EpisodeManagementView.vue | 3 ++- .../composables/useDramaEdit.js | 3 ++- .../composables/useMenuManagement.js | 10 ++++++---- 4 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/utils/creatorImpersonation.ts b/src/utils/creatorImpersonation.ts index f3f9955..b6d698a 100644 --- a/src/utils/creatorImpersonation.ts +++ b/src/utils/creatorImpersonation.ts @@ -11,17 +11,22 @@ import { ref } from 'vue' */ const STORAGE_KEY = 'creator-center:impersonate-id' -function readStored(): number | null { +/** + * 代看目标按**字符串**存:创作者 id 是雪花 id(19 位), + * 用 Number 过一遍会被 JS 四舍五入,请求头就会带一个不存在的 id(见 api-phase1.md §0)。 + */ +function readStored(): string | null { const raw = localStorage.getItem(STORAGE_KEY) - const value = raw ? Number(raw) : NaN - return Number.isFinite(value) && value > 0 ? value : null + const value = (raw || '').trim() + return value && value !== '0' ? value : null } -export const impersonateCreatorId = ref(readStored()) +export const impersonateCreatorId = ref(readStored()) -export function setImpersonateCreatorId(id: number | null) { - impersonateCreatorId.value = id - if (id) localStorage.setItem(STORAGE_KEY, String(id)) +export function setImpersonateCreatorId(id: string | number | null) { + const value = id === null || id === undefined || id === '' ? null : String(id) + impersonateCreatorId.value = value + if (value) localStorage.setItem(STORAGE_KEY, value) else localStorage.removeItem(STORAGE_KEY) } diff --git a/src/views/contentManagement/EpisodeManagementView.vue b/src/views/contentManagement/EpisodeManagementView.vue index fb67003..5db5f08 100644 --- a/src/views/contentManagement/EpisodeManagementView.vue +++ b/src/views/contentManagement/EpisodeManagementView.vue @@ -55,7 +55,8 @@ const selectedDrama = computed(() => dramas.value.find((item) => item.id === sel async function loadDramas() { const res = await pageDramasApi({ pageIndex: 1, pageSize: 200 }) dramas.value = res.data?.list || [] - const fromQuery = Number(route.query.drama) + // 剧 id 是字符串(雪花 id 过 Number 会被四舍五入),这里按字符串比较 + const fromQuery = route.query.drama ? String(route.query.drama) : '' if (fromQuery && dramas.value.some((item) => item.id === fromQuery)) { selectedDramaId.value = fromQuery } else if (!selectedDramaId.value && dramas.value.length) { diff --git a/src/views/creator-center/composables/useDramaEdit.js b/src/views/creator-center/composables/useDramaEdit.js index efe7b08..9dbcbb4 100644 --- a/src/views/creator-center/composables/useDramaEdit.js +++ b/src/views/creator-center/composables/useDramaEdit.js @@ -42,7 +42,8 @@ function writeSession(key, value) { export function useDramaEdit() { const { ensureAccess, impersonating } = useCreatorAccess() const route = useRoute() - const dramaId = Number(route.params.id) + // 作品 id 是字符串(雪花 id 过 Number 会被四舍五入),按字符串比对 + const dramaId = route.params.id ? String(route.params.id) : '' const loading = ref(false) const drama = ref(null) diff --git a/src/views/permission/composables/useMenuManagement.js b/src/views/permission/composables/useMenuManagement.js index 1bf197d..f45b3bd 100644 --- a/src/views/permission/composables/useMenuManagement.js +++ b/src/views/permission/composables/useMenuManagement.js @@ -47,7 +47,9 @@ export function useMenuManagement() { const treeData = computed(() => mapNodes(menuTree.value)) const parentOptions = computed(() => { - const options = [{ id: 0, label: '顶级菜单' }] + // 顶级菜单哨兵跟接口的 parentId 一样用字符串 "0":菜单 id 是字符串, + // 数字 0 会在下拉里对不上、回显成裸 "0" + const options = [{ id: '0', label: '顶级菜单' }] const walk = (nodes, depth) => { ;(nodes || []).forEach((node) => { if (node.type !== 'BUTTON') { @@ -73,7 +75,7 @@ export function useMenuManagement() { } } - function resetForm(parentId = 0) { + function resetForm(parentId = '0') { Object.assign(form, { id: null, parentId, @@ -94,7 +96,7 @@ export function useMenuManagement() { const raw = node.raw || {} Object.assign(form, { id: raw.id, - parentId: raw.parentId ?? 0, + parentId: raw.parentId ?? '0', title: raw.meta?.title || '', routePath: raw.path || '', routeName: raw.name || '', @@ -114,7 +116,7 @@ export function useMenuManagement() { return ElMessage.warning('请填写对应路由') } const payload = { - parentId: form.parentId || 0, + parentId: form.parentId || '0', title: form.title.trim(), routePath: form.routePath.trim(), routeName: form.routeName.trim(),