The Two Permission Bugs That Screenshots Caught: Button-Level Trimming and a 403 Dressed as 404
Splitting menu-level and button-level permissions, and distinguishing 403 from 404, turns confusing “page gone” support tickets into clear “you lack access” messages. The same screenshot-as-evidence acceptance method is directly reusable in any role-based admin project.
This admin backend builds its entire navigation from a single `menuTree`: it filters the tree by role to produce the sidebar, then uses the same filtered result to register dynamic routes via `router.addRoute`. The stated benefit is that adding a page touches only two places, and unauthorized pages are never registered at all rather than being hidden.
The project separates roles from permission codes. Roles decide which menus appear; permission codes decide which buttons click. The first bug hit exactly there: the user list page never applied the `v-permission` directive, so a product role with no delete permission still saw every row's delete button. The fix attaches permission codes to each action button, and the directive removes unauthorized elements from the DOM entirely.
The second bug came from the guard logic. Because unauthorized routes aren't registered, manually visiting a path like `/system/roles` resolved to the catch-all NotFound, rendering a 404. The guard now checks whether the requested path exists in the unfiltered menu path set: if it exists but the role lacks access, it redirects to `/403`; only truly unknown paths get a 404.
Beyond permissions, the post covers multi-tab caching driven by `keep-alive`'s `include` list (derived from open tabs, never hand-written), theme switching via a single `data-rn-theme` attribute backed by CSS tokens, and a zero-dependency SVG chart set. The author's core workflow lesson is to accept admin features by logging in as each role, screenshotting every step, and asserting on the DOM, because code review and docs both miss execution-layer gaps.
Screenshotting every role in a real browser is a cheap, high-signal acceptance method: the post proves it caught two defects that code reading and documentation would have missed.
The two bugs share a common failure mode: the permission infrastructure (codes, 403 page) existed, but an execution layer (the user view, the guard redirect) never consumed it. In practice, this pattern is more likely than missing infrastructure.
Hiding a button with `v-permission` but leaving the element in the DOM would be an invitation to scripted clicks; removing it entirely is the right call, even though it complicates debugging.
The author's insistence on one `menuTree` for menus and routes trades a little flexibility for a big win: the system can't drift into a menu that points to a route that doesn't exist.
The 403 vs 404 distinction is a small change with outsized user experience impact—it converts ambiguous “feature missing” reports into clear “you don't have access” feedback, cutting support noise.
The article's premise is challenged as outdated, while another position defends the acceptance checklist as necessary because AI-generated code passes tests yet misses permission bugs that only role-switching screenshot verification catch.
It can write it, but writing it correctly is another matter. These two bugs are exactly what AI wrote itself, and even with tests all green they weren't caught; only real-device role-switching screenshots exposed them. So now I treat 'role-switch login + DOM-assertion screenshots' as an acceptance step, not relying on its self-check.
It's 2026; posting this now is meaningless.