SuperAPI AI Directory

frontend-mandatory-standards

Frontend & websites

View repository

Updated 10-16 17:59

About this skill

Frontend Development Specification

Tech Stack

  • Language:TypeScript
  • Framework:Vue 3 Composition API (<script setup>)
  • CSS: Tailwind CSS (avoid using <style> tags, except in scenarios where Tailwind classes cannot cover)
  • State Management:Pinia
  • UI Framework: Giime (enhanced based on Element Plus,el-* → gm-*)

Naming Conventions

Scenario Style Example
Types/Interfaces PascalCase UserInfo, ApiResponse
Variables/Functions/Folders camelCase userName, handleSubmit()
Environment Constants UPPER_CASE VITE_BASE_URL
Component Files PascalCase UserProfile.vue
Composables use prefix useUserInfo, useFormValidation
Booleans Auxiliary verb prefix isLoading, hasError, canSubmit
Event Handling handle prefix handleClick, handleSubmit

Variable Naming: Avoid data/info/list/result/status and other generic names, use formData/userInfo/taskList and other specific business names.

General Coding Rules

  • Comments: Add JSDoc comments to every method and variable, in Chinese; add an appropriate amount of single-line Chinese comments inside functions
  • Function Declarations: Prefer const arrow functions over function, unless overloading is needed
  • Async: Prefer async/await, do not use Promise chaining
  • Modern ES: Prefer ?.、??、Promise.allSettled、replaceAll、Object.groupBy , etc.
  • Guard Clauses: Exit early on conditions to reduce nesting depth
  • Function Parameters: 1-2 main parameters + options object, avoid excessively long parameters
// ✅ 函数参数设计示例
const urlToFile: (url: string, options?: FileConversionOptions) => Promise<File>;
  • Prefer Utility Libraries: Reduce reinventing the wheel
    • Date:dayjs
    • Vue Utilities:vueuse(tryOnMounted、useEventListener、useLocalStorage , etc.)
    • Data Processing:lodash-es(compact、cloneDeep、uniqBy and other methods not provided by ES)
  • Git Operations: Do not operate git commands unless the user explicitly requests it
  • Formatting: After modifying code, execute npx eslint --fix <文件路径>, find and automatically fix code style and syntax issues
  • Type Checking: After modifying code, execute npx vue-tsc --noEmit -p tsconfig.app.json, find type errors and potential runtime issues

For more examples, see coding-conventions.md

Composition API Specification

1. Reduce watch, prefer event binding

watch is an implicit dependency, making it difficult to trace the source of changes. Prefer handling logic directly in event callbacks for a clearer data flow:

// ❌ 隐式监听,难以定位谁触发了变更
watch(count, () => {
  console.log('changed');
});

// ✅ 在事件中直接处理,数据流清晰
const handleCountChange = () => {
  console.log('changed');
};
// <gm-input v-model="count" @change="handleCountChange" />

2. Use defineModel

Introduced in Vue 3.4+, defineModel simplifies the props + emit + computed trio into one line, reducing boilerplate code:

// ❌ 三件套写法,样板代码多
const props = defineProps<{ modelValue: string }>();
const emit = defineEmits<{ 'update:modelValue': [value: string] }>();

// ✅ defineModel 一行搞定
const value = defineModel<string>({ required: true });

3. Use useTemplateRef

Vue 3.5+ recommends using useTemplateRef to obtain template refs, which is type-safe and avoids coupling between ref variable names and template ref attribute names:

// ❌ 变量名必须与 template 中的 ref="inputRef" 完全一致
const inputRef = ref<FormInstance | null>(null);
// ✅ 类型安全,解耦变量名和模板 ref
const inputRef = useTemplateRef('inputRef');

4. Prefer ref, avoid reactive without reason

reactive loses reactivity when destructured, and requires assigning fields one by one when replacing the whole object.ref behavior is more predictable, and through .value you can directly replace the whole object:

// ❌ reactive 解构丢失响应性,整体替换需 Object.assign
const profileData = reactive({ name: '', age: 0 });
// ✅ ref 行为统一,profileData.value = newData 即可整体替换
const profileData = ref({ name: '', age: 0 });

5. Direct Props Destructuring

Vue 3.5+ supports defineProps maintaining reactivity while destructuring and setting default values,withDefaults no longer needed:

// ❌ withDefaults 在 Vue 3.5+ 已不必要
const props = withDefaults(defineProps<{ size?: number }>(), { size: 10 });

// ✅ 直接解构,简洁且响应式
const { size = 10 } = defineProps<{ size?: number }>();

6. Dialog exposes openDialog

The dialog component exposes the defineExpose method through openDialog , and the parent component calls it via ref, avoiding state synchronization issues caused by controlling visible through props:

const dialogVisible = ref(false);

const openDialog = (data?: SomeType) => {
  dialogVisible.value = true;
};

defineExpose({ openDialog });

7. Auto-import, do not explicitly import Vue API

ref、computed、watch、onMounted etc. are already auto-imported through unplugin-auto-import . Explicitly import { ref } from 'vue' produces redundant code and is inconsistent with the project configuration.

8. Use Tailwind CSS for styling

All styles are implemented through Tailwind classes. Only use :deep(), complex animations). <style>。

9. Extract configuration data

Extract configuration data unrelated to page logic, such as option lists, form rules, tableId, etc., in modules/**/composables/useXxxOptions.ts , keeping components focused on interaction logic:

export const useXxxOptions = () => {
  const tableId = 'xxx-xxx-xxx-xxx-xxx';
  const xxxOptions = [{ label: '选项1', value: 1, tagType: 'primary' as const }];
  const rules = {
    xxx: [{ required: true, message: 'xxx不能为空', trigger: 'blur' }],
  };

  return { tableId, xxxOptions, rules };
};

10. Component code structure

Uniformly use the template → script → style order.

Code splitting conventions

Each time you add a new feature to an existing .vue file, or create a new module, you should first read directory-structure.md to understand the splitting principles.

When to trigger splitting

Trigger scenario Action
Create a new CRUD module First read crud.md to learn the complete CRUD code template and file splitting approach, and generate all files according to the template
Add features to an existing page(new forms, dialogs, etc.) First check the current file line count; if it exceeds 300 lines, it should be split; evaluate whether the new content should be an independent child component
Create a new module/page First plan the directory structure (components/、composables/、stores/), and pre-split by functional area
Modify and iterate existing features If you find that the current file is already bloated (>300 lines), split it while completing the requirement; do not let the file continue to grow

Core principles

  • Generally, .vue files should not exceed 300 lines: except for entry-level files (such as index.vue), child components, dialogs, and other files should be kept within 300 lines
  • Entry files can be appropriately relaxed:index.vue As a module entry, it undertakes orchestration responsibilities, so the line count can appropriately exceed the limit, but it should still be as concise as possible
  • Split UI components by functional area: Each independent functional area (search bar, table, dialog, form area, etc.) should be an independent child component
  • Core business logic stays in index.vue: The main page is responsible for data fetching, state management, and child component orchestration
  • UI display logic sinks into child components: Child components are only responsible for rendering and user interaction
  • Avoid excessive prop passing: When the props hierarchy exceeds 2 levels, use Pinia instead of deep props passing

Standard module directory structure

modules/xxx/
├── index.vue                    # 主页面(编排子组件、管理数据)
├── components/                  # UI 子组件
│   ├── Search.vue
│   ├── Table.vue
│   └── EditDialog.vue
├── composables/                 # 逻辑复用
│   └── useXxxOptions.ts
└── stores/                      # 状态管理(需要时)
    └── useXxxStore.ts

For detailed splitting strategies, example code, and Props passing principles, see directory-structure.md

API call conventions

File restrictions

/src/api Each module under (such as src/api/agent/) contains the following:

Path Description Can be modified manually
controller/ Interface methods automatically generated by the code generation tool Do not modify/create/delete; re-running the generation command will overwrite
interface/ Type definitions automatically generated by the code generation tool Do not modify/create/delete; re-running the generation command will overwrite
request.ts createAxios The axios instance created Modifiable (manually maintained)
swagger.config.json Code generation configuration Modifiable (manually maintained)

File structure

Two files are generated for each interface:

  • postXxxYyyZzz.ts — raw axios method
  • usePostXxxYyyZzz.ts — useAxios reactive wrapper

The file name is generated from "request method + route address":post /open/v1/system/list → postOpenV1SystemList.ts + usePostOpenV1SystemList.ts

Selection rules

Scenario Version to use Reason
Default useXxx Wrapped version Provides reactive data (data、isLoading) and automatic race condition cancellation
Loops/batch requests Raw version (without use prefix) useAxios cancels the previous request, so when called in a loop only the last one takes effect
// ✅ 默认:useAxios 封装
const { exec: getListExec } = usePostXxxV1ListPage();

await getListExec();
// ✅ 批量:原始接口 + Promise.all
await Promise.all(ids.map(id => deleteXxxV1Item({ id })));

API import rules

All interfaces and types are imported uniformly from controller/index.ts or interface/index.ts to reduce coupling and facilitate later refactoring:

// ✅ 从 controller 统一导入
import type { PostGmpV1CrowdListInput } from '@/api/gmp/controller';
import { postGmpV1CrowdList } from '@/api/gmp/controller';
// ✅ 公共类型从 interface 导入
import type { CrowdItemVo } from '@/api/gmp/interface';
// ❌ 不要从具体文件导入
import { postGmpV1CrowdDetail } from '@/api/gmp/controller/RenQunGuanLi/postGmpV1CrowdList';

Error handling

  • No try/catch needed:createAxios The interceptor automatically pops up an error prompt; business code does not need to manually catch
  • No need to check code: abnormal response codes are automatically rejected by the interceptor; no need for if (code !== 200)
  • When finally is needed: use try/finally (without catch) for cleaning up side effects

Usage flow

  1. If an interface address is provided, the first step is to find @/api/xxx/controller the request method defined in
  2. Carefully read the type definitions in the interface file; do not make up parameters yourself
  3. Choose based on the scenario useXxx version or the raw version
  4. Data sources such as dropdowns and radio buttons can be obtained from the interface documentation comments; after obtaining them, extract them in useXxxOptions for reuse

For more usage examples, see api-conventions.md

Pinia state management

Store file organization

modules/xxx/
├── stores/
│   └── useXxxStore.ts    # 模块专用 store
└── index.vue

When to use

Pinia is suitable for scenarios such as sharing state across multiple component layers and polling asynchronous tasks. Do not overuse it—only use props/emits for data passed between parent and child components, use ref for local state, and use v-model for simple form data.

Usage conventions

// 命名体现业务含义,不要用 const store = useXxxStore
const xxxStore = useXxxStore();

xxxStore.resetStore(); // 进入页面时重置,确保干净状态

Cross-component shared state

When components are nested more than 2 levels deep and need to share state, use Pinia instead of passing props down layer by layer:

index.vue → Result.vue → ResultSuccess.vue → ImageSection.vue
                    ↓
              各组件直接访问 store,不需要 props 逐层传递

For standard Store writing, polling task patterns, etc., see pinia-patterns.md

Reference documentation

Topic Description Reference
CRUD code template Required reading when creating a new CRUD page, complete file splitting and code templates crud.md
Detailed coding conventions Complete examples of guard clauses, function parameter design, utility library usage, etc. coding-conventions.md
Interface invocation guide Complete explanation of useAxios usage, type definitions, and import rules api-conventions.md
Pinia Usage Patterns Patterns for store creation, reset, cross-component sharing, etc. pinia-patterns.md
Component Splitting Guidelines When to split, directory structure, Props passing principles directory-structure.md
Dictionary Module Guidelines useDictionary, store creation, naming conventions dictionary.md
Environment Variable Configuration .env layering principles, domain rules, multi-environment builds env.md