prototype
MAmattpocock/skills288k近 7 天新增 10,178 个 star
Give a Vue SPA route its breadcrumb, entirely from the router file. The breadcrumb component reads four declarations in `meta` — `showBreadcrumb`, `breadcrumb`, `breadcrumbParents` and `breadcrumbResource` — and knows no page by name. Auto-invoke when: the user adds a route to any file in `assets/vue/router/`, creates a new Vue page or view that needs a URL, migrates a legacy PHP page to Vue, reports a wrong or missing breadcrumb, sees the console warning "[Breadcrumb] Route ... has no meta.breadcrumb", or mentions breadcrumb, crumb, trail, `meta.breadcrumb`, `breadcrumbParents` or `breadcrumbResource`. Do NOT invoke for: the legacy PHP breadcrumb (`window.breadcrumb`, injected by `public/main/`), for `/admin/settings/<namespace>` (its last crumb is read from the DOM), or for styling work on `Breadcrumb.vue` itself.
Never edit assets/vue/components/Breadcrumb.vue to give a page its trail. The component
holds no page name and no path. Everything a trail needs is declared in the route.
Set showBreadcrumb: true in the meta of the parent route. Without it, App.vue does not mount
the component and nothing you declare below has any effect.
A route that only sometimes has a trail declares a function instead. It receives the route:
// The personal agenda hangs from no course, so it has nothing to show.
showBreadcrumb: (route) => Number(route.query?.cid || 0) > 0,
Set meta.breadcrumb on the parent and on every leaf. The value is a translation key,
never translated text.
export default {
path: "/resources/checklist/:node/",
meta: {
requiresAuth: true,
showBreadcrumb: true,
tool: "checklist",
breadcrumb: "Checklists",
},
name: "checklist",
component: () => import("../components/layout/SimpleRouterViewLayout.vue"),
redirect: { name: "ChecklistList" },
children: [
{ name: "ChecklistList", path: "", meta: { breadcrumb: "" }, component: ... },
{ name: "ChecklistCreate", path: "create", meta: { breadcrumb: "Create" }, component: ... },
{ name: "ChecklistUpdate", path: "edit/:id", meta: { breadcrumb: "Edit" }, component: ... },
],
}
breadcrumb: "" means omit this crumb, and it is what a list page usually wants: the tool
crumb already names that page. An absent property is a different thing — it triggers the
development warning and shows the raw route name.
The label can also be a function returning a key, when it depends on the request:
breadcrumb: (route) => {
const gid = Number(route.query?.gid || 0)
const cid = Number(route.query?.cid || 0)
return gid > 0 ? "Group agenda" : cid > 0 ? "Agenda" : "Personal agenda"
},
A function may also return "" to omit the crumb, but only on a route that declares
breadcrumbParents: the tool and resource branches still compare the raw meta.breadcrumb
property, so there a function is never empty. Declare it when the page's own name exists for
some requests only. AdminStatistics in assets/vue/router/admin.js is the reference — its key
comes from activeReportLabelKey (assets/vue/utils/statisticsReports.js), which names the
report in route.query.report and returns "" when there is none.
A key that is new to the platform goes by hand into three files:
assets/locales/en_US.jsontranslations/messages.pottranslations/messages.en_US.pophp bin/console chamilo:update_vue_translations does not create keys. It only propagates to
the other locales the keys that en_US.json already holds. Never commit a mass regeneration of
assets/locales/*.json; a single new line is fine.
Check first whether the key exists — most tool names already do:
python3 -c "import json; d=json.load(open('assets/locales/en_US.json')); print('Checklists' in d)"
breadcrumbParents — a page that hangs from a list pageGive the leaf the fixed ancestors that always precede it. Each entry is { label, route }, where
label is a translation key and route is a Vue Router location — prefer { name: ... } over a
raw path.
meta: {
breadcrumb: "Import checklists",
breadcrumbParents: [
{ label: "Administration", route: { name: "AdminIndex" } },
{ label: "Checklists", route: { name: "ChecklistAdminList" } },
],
}
When several routes share the same ancestors, declare the lists once at the top of the file.
assets/vue/router/admin.js is the reference, with adminCrumb, classesCrumbs,
multiUrlCrumbs and accessUrlCrumbs; room.js and branch.js show the smaller shape.
A route with breadcrumbParents owns its whole trail and takes the first branch of
buildBreadcrumb, so nothing else runs for it.
The ancestors are fixed, but the leaf's own crumb is not: an empty key drops it and leaves the
ancestors standing. That is how a page hangs a report or filter name off a parent that links
back to its own bare URL, and it is the only branch where a function may return "".
breadcrumbResource — a tool that opens a resourceDeclare it on the parent, when the trail has to name the thing the user opened.
// One resource at a time: list -> resource -> sub-page.
breadcrumbResource: {
trail: "self",
listRoute: "AssignmentsList",
detailRoute: "AssignmentDetail",
detailParam: "id",
}
// A chain of folders, walked up the resource-node parents.
breadcrumbResource: {
trail: "ancestors",
listRoute: "DocumentsList",
detailRoute: "DocumentsList",
}
This declaration also drives the fetch: loadResourceNodeIfNeeded loads the resource node only
for a route whose tool declares it. Nothing else has to be wired.
Breadcrumb.vue. If a trail seems to need one, the shape is probably
already covered by one of the four declarations. Read the component's own builders first. A
rule that names a page or a route never belongs there. Widening a contract the declarations
already document is a different thing, and rare: do it only when a builder fails to honour a
declaration the skill already describes.route.name.includes("Page") puts a
"Pages" crumb on every wiki page of every course.t() on what you declare.home slot, and the container is hidden while the rest is empty. A page whose trail
reduces to its tool shows nothing.# Fails when a route renders a breadcrumb without declaring its label.
yarn check:breadcrumb-routes
npx eslint assets/vue/router/
yarn dev
Then open the page and read the browser console. [Breadcrumb] Route "X" has no meta.breadcrumb
means a declaration is missing. The warning is stripped from the production bundle.
tests/playwright/features/breadcrumb.feature holds the breadcrumb scenarios. Add yours there.
Assert on CSS selectors, never on crumb text: this platform runs in many languages and the test environment is not always English.
Then I should see the ".app-breadcrumb a[href='/admin/urls']" element
An a[href] proves both that the ancestor is there and that it points at the right place. Two
traps that cost real time:
<span>, not a link. Asserting a[href] on the current page's own
crumb never matches. Assert on an ancestor.breadcrumbResource paints late. It waits for the resource-node fetch, well
past Playwright's 15 s default. Use I wait up to 45 seconds for the element ... to appear.Playwright needs BASE_URL=http://localhost inside the container, and the generated spec under
tests/playwright/.features-gen/ is tracked, so run bddgen and commit it too.
MAmattpocock/skills288k近 7 天新增 10,178 个 star
AFaffaan-m/ECC278k近 7 天新增 4,046 个 star
FIfirecrawl/firecrawl191k近 7 天新增 2,010 个 star
用 React、Tailwind 和 shadcn/ui 构建多组件页面。
ANanthropics/skills181k近 7 天新增 1,119 个 star