For vue3-element-admin provide project-specific conventions. Goal: maintainable, type-safe, consistent with existing code.
Boundaries
Use only in this repository or when the user explicitly requests compliance. In case of conflict, priority: user request → actual repository code and checks → this Skill → general conventions. When maintaining existing modules, follow adjacent implementations; do not rewrite the architecture to fit examples.
Workflow
- Confirm scope: read relevant configuration, target files, and implementations in the same directory; preserve the user's uncommitted changes.
- Make the smallest complete change; reuse existing APIs, types, Composables, components, and style variables.
- Verify according to risk: at minimum type checking and linting of modified files; run the build as well when build configuration, routing, or dependencies are involved.
- When delivering, explain the changes, verification results, and unverified items; do not conceal failures or existing issues.
Do not automatically fix or format the entire repository; such operations modify unrelated files.
Read reference documentation as needed
| Scenario | Read |
|---|---|
| Adding or refactoring APIs, list pages, form pages, simple pages | new-page-guide.md |
Configuration-driven CRUD (src/components/Crud:CrudSearch/CrudTable/CrudModal + useCrudPage) |
crud-development.md |
| Static routes, dynamic menus, route Meta | router-menu.md |
v-hasPerm、hasPerm(), permission identifiers |
permission.md |
keepAlive, tab caching and refresh |
page-caching.md |
| Menu titles, language packs, and language switching | i18n.md |
settings.ts, environment variables, storage keys, themes |
project-config.md |
| Upload、Dict、TableSelect、IconSelect | components.md |
Comment style (JSDoc / //) and wording trade-offs |
comments.md |
Select the page pattern according to the table in new-page-guide.md: use Composables by default for new pages, keep the original pattern when maintaining, and do not migrate patterns or force splitting based on line count. For the page root node, follow page-container, and for search, content, and toolbars, reuse page-search、page-content、page-toolbar and other global styles.
Project conventions
Vue and components
- Composition API +
<script setup lang="ts">; SFC block ordertemplate→script→style(already enforced by ESLint). - Component files use PascalCase, route pages
index.vue; use type declarations for Props, and choose default values that are consistent with adjacent code and have the clearest type. - Composables use
use+ camelCase, and only extract stateful, reusable logic; do not create a Composable for simple utility functions. - Do not set fixed thresholds for line count or number of atomic classes; decide splitting based on responsibility boundaries, reuse value, and change frequency.
Naming
| Object | Convention | Example |
|---|---|---|
| Variables, functions | camelCase; names consistent with data shape | selectedUserId / selectedUserIds / users(plural means a collection; do not add List) |
| Boolean | is / has / can / should prefix |
isSaving、hasSelection |
| Easily confused units | Include units in names; use Text |
durationSeconds、sizeText |
| Type | PascalCase, by purpose:Item / Detail / Form / QueryParams / Result |
UserItem、UserForm; do not use VO / DTO suffix |
| UI event entry | handleXxx |
handleQueryClick、handleSubmit |
| Data loading | loadXxx / fetchData |
loadUsers;handleQuery returns to the first page,fetchData keeps the current page |
| Open, close, reset | openEditor / closeEditor / resetFilters |
|
| Pure transformation and validation | Start with a verb | formatDate、buildOptions、validatePassword |
| Set value / toggle | setThemeMode(mode) / toggleThemeMode() |
Do not merge the two |
| API read | getPage / getOptions / getFormData |
Use for edit backfill getFormData, not mixed with details; detail types are named after adjacent modules |
| API writes | create / update / deleteByIds |
do not mix synonyms within the same kind of method |
| component events | kebab-case | query-click、confirm-click(CRUD component contract) |
Contracts such as backend fields and permission strings are not mechanically renamed. Entries in the table with no project precedent (such as loadXxx、openEditor、setXxx / toggleXxx, units in names) are general conventions, used only for new code; when they conflict with adjacent implementations, the adjacent implementation prevails.
Types and API
- New business API modules usually
index.ts+types.ts, but modules that are very small or already have a stable structure are not split for form's sake. - Reuse
@/api/common'sBaseQueryParams、PageResult、OptionItemand other common types. - The request interceptor already returns the business
data, and the caller no longer reads.data。 - APIs are organized in the existing object literal style; HTTP methods, URLs, and parameter structures follow the real backend contract and adjacent APIs.
- Do not add
any, non-null assertions, or casts to bypass type checking.
State and Store
- New Stores use Setup Store, keeping a flat directory structure.
- When a stable Pinia instance is needed outside a component, reuse
useXxxStoreHook()or explicitly pass instore。 - Do not copy server state into multiple Stores; first reuse existing state and Composables.
Styles
- For simple, one-off typography and appearance (layout, spacing, size, font size and weight, border radius, borders, etc.), use UnoCSS atomic classes directly; do not create semantic class names for them, and do not write SCSS.
- Create a semantic class (BEM + scoped SCSS) only when any of the following occurs: the style carries business semantics and needs to be addressable (such as
order-card__price), it contains state or nested selectors (:hover、is-*, parent-child linkage), animation, media queries,:deep(); when the same string of atomic classes repeats in multiple places, extract it into a shortcut or component instead of copying and pasting. - Maintain the same property of the same node in only one place; internal styles that already have a BEM class no longer stack atomic classes controlling that property. External component spacing and placeholders are controlled by the consumer with atomic classes, and the component does not redefine these properties internally.
- For theme-aware interface colors, prefer Element Plus or project CSS variables.
- Brand colors, chart colors, status colors, and necessary third-party overrides may use explicit colors; when used repeatedly, centralize them as variables or configuration.
- When following the existing BEM style, use
block__element--modifier; for state classes useis-*. Do not add meaningless class names to simple structures just to satisfy form.
Routing, Permissions, and Internationalization
- Fixed entries (login, error pages, etc.) go into static routes; business menus go through backend menu configuration and are not maintained redundantly on both frontend and backend.
- For button permissions use
v-hasPerm/hasPerm(), format模块:资源:操作; for config-driven CRUD usepermPrefixto reuse built-in button permissions. - Route titles prefer language pack keys; when adding keys, synchronously maintain all supported language files.
- Before changing
keepAliveand caching strategy, first read page-caching.md (caching matches byfullPath, not componentname)。
Comments
The comment form is chosen by "whether it is externally exposed": externally exposed members (defineProps / defineEmits / defineExpose、export 's functions / types / constants) use JSDoc, and component-internal implementations (computed、ref, private functions, local variables, page <script setup> internal members) use //The documentation explains "why, constraints, and side effects", does not restate the code, and does not use JSDoc to give onMounted、watch and other execution logic as an opening. For special cases such as complete tables, examples, and API modules, see comments.md。
Verification Checklist
- The changes match the user's request, preserving the existing architecture and unrelated modifications
- Naming, types, permissions, routes, and real contracts are consistent, with no newly added
any/ assertions / duplicate state - Cross-file relationships such as permissions, internationalization, and caching have been synchronized
- Type checking and linting of modified files pass; when builds, routes, or dependencies are involved, the build passes