diff --git a/docs/sw6-to-kra/business-page-parity-2026-11-03.json b/docs/sw6-to-kra/business-page-parity-2026-11-03.json new file mode 100644 index 0000000..5cc6bc2 --- /dev/null +++ b/docs/sw6-to-kra/business-page-parity-2026-11-03.json @@ -0,0 +1,44 @@ +{ + "method": "真实 Chrome 渲染;受控身份和业务数据,权限页签与状态操作核对", + "results": [ + { + "level": 0, + "bindingTab": false, + "editButtons": 0, + "passed": true, + "bindingCalls": 0, + "pageErrors": [] + }, + { + "level": 1, + "bindingTab": false, + "editButtons": 0, + "passed": true, + "sdkDetailReadonly": true, + "bindingCalls": 0, + "pageErrors": [] + }, + { + "level": 2, + "bindingTab": false, + "editButtons": 0, + "passed": true, + "sdkDetailReadonly": true, + "bindingCalls": 0, + "pageErrors": [] + }, + { + "level": 3, + "bindingTab": true, + "editButtons": 0, + "passed": true, + "normalUnbind": 1, + "rejectedUnbind": 0, + "pendingUnbind": 0, + "rejectedDetail": 1, + "rejectDrawerInViewport": true, + "bindingCalls": 1, + "pageErrors": [] + } + ] +} \ No newline at end of file diff --git a/docs/sw6-to-kra/implementation.md b/docs/sw6-to-kra/implementation.md index c9835a9..1cfeede 100644 --- a/docs/sw6-to-kra/implementation.md +++ b/docs/sw6-to-kra/implementation.md @@ -13,6 +13,8 @@ 用户补充:允许直接优化旧接口、SQL 和逻辑。优化时记录行为差异并验证业务结果;各批实现必须实际请求 HTTP 接口,不能只以单元测试和构建作为完成依据。 +用户再次指出页面偏差后,明确按旧前端实际页面核对入口,不能从后端接口推断 UI 功能。详见 `page-parity.md`。商务列表已移除错误增加的编辑入口,恢复“绑定列表”名称及仅三级显示规则,并对齐按审核状态显示行操作。 + 用户已有的 `configs/config.yaml` 修改与迁移 SQL 保留。未经最后阶段的数据库确认,不连接、复制或修改用户的旧数据库。 ## 依赖顺序与完成标准 diff --git a/docs/sw6-to-kra/page-parity.md b/docs/sw6-to-kra/page-parity.md new file mode 100644 index 0000000..79679ee --- /dev/null +++ b/docs/sw6-to-kra/page-parity.md @@ -0,0 +1,37 @@ +# 旧商务页面功能核对 + +以旧前端 `dev/dev1.0` 的实际模板、事件处理和接口调用为准,不因为后端存在某个接口就向页面增加入口。页面样式采用 Kra;名称、业务可见条件和交互沿用旧商务。只有完成页面、接口和实际验证后才标为整个菜单完成。 + +## 商务列表与绑定列表 + +来源:`sw/web/src/views/BusMgmt/BusinessList.vue`,以及该目录下实际引用的抽屉组件。前端基线 `ba284011c87fd3d5730e0e3dceffb14a7389730c`。 + +| 旧页面行为 | 新页面状态 | +| --- | --- | +| 商务列表没有编辑按钮;操作列为画像 | 已移除错误增加的编辑入口;画像待报表与画像用例迁移,不能视为完成 | +| 第二个页签名为“绑定列表”,仅 `level=3` 显示 | 已对齐;超管、一级、二级均隐藏且不会加载绑定列表 | +| 商务列表筛选:商务名称、商务账号、等级、状态 | 已实现 | +| 添加子商务:账号、密码、姓名、手机号、QQ;展示等级/所属商务 | 已实现主要字段,所属商务展示继续核对;已移除原页面没有的邮箱和编辑审核表单 | +| SDK商务数量可点击,零数量也能打开详情 | 已对齐 | +| SDK商务绑定详情是只读抽屉,上级可以查看;列为平台、ID、游戏商务账号、审核状态、绑定日期 | 已对齐基础列表与字段;平台点击进入画像待实现 | +| 商务列表列名“旗下渠道数” | 已对齐;点击进入旗下渠道抽屉待实现 | +| 注册日期、绑定日期显示年月日 | 已对齐 | +| 绑定状态正常:画像、解绑;驳回:查看驳回;待审核:无操作 | 解绑/查看驳回/待审核无操作已对齐;画像待实现 | +| 查看驳回打开审核详情,展示申请商务、平台、账号、状态、申请/审核时间、审核人和审核意见 | 已实现 | +| 添加绑定、解除绑定确认使用右侧抽屉 | 已对齐 | +| 绑定列表点击旗下渠道数时,仅正常状态可查看并携带平台/平台商务筛选 | 待实现 | +| 全局偏好变化刷新商务/绑定数据 | 已接入 Kra 页面刷新机制 | + +“仅三级可见”是旧页面页签规则。旧 Java 绑定查询接口仍支持超管全量、一级/二级查询下级,SDK 详情接口允许上级查看下级;这些接口的数据范围不能因页签隐藏而一并误删。 + +目前 sw7 是系统数据演练库,缺少 `tab_promote`。有绑定但渠道表未接入的数量明确返回空值并展示“未接入”,不当作零;完整数据环境继续计算真实数量。 + +## 验证 + +- `business-page-parity-2026-11-03.json`:真实 Chrome 渲染、受控身份/业务数据,检查四个等级的页签和按钮,以及三种审核状态的行操作;无页面运行时错误。 +- `http-business-bindings-2026-11-03.json`:真实运行实例及已有 sw7 测试数据的 HTTP 验证。 +- 画像、旗下渠道抽屉等未完成项没有用占位响应或假数据冒充已迁移功能。 + +## 其他页面 + +渠道列表、渠道激活、渠道统计、充值明细、注册明细、游戏数据、区服分析、排行榜、预警列表和预警规则,均需在各自实现前逐项提取旧模板的筛选项、列、按钮、抽屉、权限和跳转。数据库菜单清单或后端接口清单只能用于交叉核对,不能替代页面核对。 diff --git a/scripts/sw_page_smoke.cjs b/scripts/sw_page_smoke.cjs new file mode 100644 index 0000000..5dba331 --- /dev/null +++ b/scripts/sw_page_smoke.cjs @@ -0,0 +1,76 @@ +// 使用受控身份和接口数据检查实际 Vue 页面,不修改业务库。 +const fs = require('node:fs/promises'); +const path = require('node:path'); +const { chromium } = require(process.env.SW_PLAYWRIGHT_MODULE || 'playwright'); +(async () => { + const token = (await fs.readFile(path.join(process.env.TEMP, 'sw-kra-http-test-token'), 'utf8')).trim(); + const browser = await chromium.launch({channel:'chrome', headless:true}); + const results = []; + try { + for (const level of [0,1,2,3]) { + const context = await browser.newContext({viewport:{width:1440,height:1000}}); + await context.addInitScript(value => localStorage.setItem('token',value),token); + const page = await context.newPage(); + const pageErrors=[]; + page.on('pageerror',e=>pageErrors.push(e.message)); + let bindingCalls=0; + await page.route('http://127.0.0.1:8080/api/**', async route => { + const request=route.request(); + const uri=new URL(request.url()); + const apiPath=uri.pathname.slice(4); + let body; + if(apiPath==='/sw/business/list') body={code:0,msg:'成功',data:{rows:[{userId:79999,userName:'展示测试商务',level:2,parentUserName:'上级商务',realName:'测试姓名',phoneNumber:'',bindCount:0,promoteCount:0,createTime:'2026-11-03 10:00:00',status:'0',authorityId:902}],total:1}}; + else if(apiPath==='/sw/business/bindPlatform') { + bindingCalls++; + body={code:0,msg:'成功',data:{total:3,rows:['0','2','3'].map((status,i)=>({userId:70000+level,sourceDb:'lx',platformName:'龙翔',platformBusierId:10+i,platformBusierAccount:['正常账号','驳回账号','待审账号'][i],bindUserName:'测试商务',promoteCount:status==='0'?2:0,status,createTime:'2026-11-03 10:00:00',reviewTime:'2026-11-03 11:00:00',reviewAccount:'测试审核人',rejectReason:status==='2'?'测试驳回原因':null}))}}; + } else if(/^\/sw\/business\/\d+\/bindPlatform$/.test(apiPath)) body={code:0,msg:'成功',data:[]}; + if(body) {await route.fulfill({json:body});return;} + const response=await route.fetch({url:'http://127.0.0.1:18000'+apiPath+uri.search,headers:{...request.headers(),'x-token':token}}); + if(apiPath==='/user/getUserInfo') { + body=await response.json(); + const info=body.data.userInfo; + Object.assign(info,{ID:70000+level,level,nickName:'页面核对账号',phone:'',email:'',authorityId:level===0?888:900+level}); + await route.fulfill({response,json:body}); + } else await route.fulfill({response}); + }); + await page.goto('http://127.0.0.1:8080/#/layout/sw-BusMgmt/BusinessList'); + try { await page.getByRole('tab',{name:'商务列表',exact:true}).waitFor({timeout:15000}); } catch (error) { console.log(JSON.stringify({url:page.url(),errors:pageErrors,text:(await page.locator('body').innerText()).slice(0,1800)})); await page.screenshot({path:path.join(process.env.TEMP,'sw-kra-browser-failure.png')}); throw error; } + await page.getByText('展示测试商务',{exact:true}).waitFor(); + const tabs=await page.getByRole('tab',{name:'绑定列表',exact:true}).count(); + const editButtons=await page.getByRole('button',{name:'编辑',exact:true}).count(); + if(tabs!==(level===3?1:0)||editButtons!==0) throw new Error('页面权限或编辑入口不一致 level='+level); + const result={level,bindingTab:tabs===1,editButtons,passed:true}; + if(level===1 || level===2) { + await page.getByRole('row').filter({hasText:'展示测试商务'}).getByRole('button',{name:'0',exact:true}).click(); + await page.getByRole('dialog',{name:'SDK商务绑定详情'}).waitFor(); + result.sdkDetailReadonly=(await page.getByRole('dialog',{name:'SDK商务绑定详情'}).getByRole('button',{name:'解绑',exact:true}).count())===0; + if(!result.sdkDetailReadonly)throw new Error('SDK 详情出现写操作'); + await page.getByRole('dialog',{name:'SDK商务绑定详情'}).getByRole('button',{name:'关闭此对话框',exact:true}).click(); + } + if(level===3) { + await page.getByRole('tab',{name:'绑定列表',exact:true}).click(); + await page.getByText('正常账号',{exact:true}).waitFor(); + const normal=page.getByRole('row').filter({hasText:'正常账号'}); + const rejected=page.getByRole('row').filter({hasText:'驳回账号'}); + const pending=page.getByRole('row').filter({hasText:'待审账号'}); + result.normalUnbind=await normal.getByRole('button',{name:'解绑',exact:true}).count(); + result.rejectedUnbind=await rejected.getByRole('button',{name:'解绑',exact:true}).count(); + result.pendingUnbind=await pending.getByRole('button',{name:'解绑',exact:true}).count(); + result.rejectedDetail=await rejected.getByRole('button',{name:'查看驳回',exact:true}).count(); + if(result.normalUnbind!==1||result.rejectedUnbind||result.pendingUnbind||result.rejectedDetail!==1)throw new Error('绑定状态操作不一致'); + await rejected.getByRole('button',{name:'查看驳回',exact:true}).click(); + await page.getByText('测试驳回原因',{exact:true}).waitFor(); + const panel=await page.getByRole('dialog',{name:'审核详情',exact:true}).elementHandle(); + await page.waitForFunction(el=>{const box=el.getBoundingClientRect();return box.x>=0 && box.right<=innerWidth+1},panel,{timeout:5000}); + result.rejectDrawerInViewport=true; + } else if(bindingCalls!==0) throw new Error('非三级加载了绑定列表'); + result.bindingCalls=bindingCalls; + result.pageErrors=pageErrors; + await page.screenshot({path:path.join(process.env.TEMP,'sw-kra-business-level-'+level+'.png'),fullPage:true,animations:'disabled'}); + results.push(result); + console.log(JSON.stringify(result)); + await context.close(); + } + await fs.writeFile('docs/sw6-to-kra/business-page-parity-2026-11-03.json',JSON.stringify({method:'真实 Chrome 渲染;受控身份和业务数据,权限页签与状态操作核对',results},null,2)); + } finally {await browser.close();} +})().catch(error=>{console.error(error.message);process.exitCode=1}); diff --git a/web/src/view/sw/BusinessList.vue b/web/src/view/sw/BusinessList.vue index 9a7f1fc..f1a8560 100644 --- a/web/src/view/sw/BusinessList.vue +++ b/web/src/view/sw/BusinessList.vue @@ -1,7 +1,7 @@ diff --git a/web/src/view/sw/components/BusinessBindings.vue b/web/src/view/sw/components/BusinessBindings.vue index 72e04f6..dc2144d 100644 --- a/web/src/view/sw/components/BusinessBindings.vue +++ b/web/src/view/sw/components/BusinessBindings.vue @@ -4,27 +4,31 @@ - + 查询重置
绑定游戏商务
- + + - - - - - - - - + + + + + + +
- + @@ -32,13 +36,31 @@ - + + + + {{ selectedRow.bindUserName || '—' }} + {{ selectedRow.platformName }} + {{ selectedRow.platformBusierAccount }} + {{ statuses[selectedRow.status] }} + {{ selectedRow.createTime || '—' }} + {{ selectedRow.reviewTime || '—' }} + {{ selectedRow.reviewAccount || '—' }} + {{ selectedRow.rejectReason || '—' }} + + + + +

确认解除游戏SDK商务「{{ selectedRow?.platformBusierAccount }}」(平台:{{ selectedRow?.platformName }})的绑定吗?

+

解除后该商务将从列表移除,其名下渠道数据将不再可查询。

+ +