SuperAPI AI Directory

frontend-dev

Frontend & websites

View repository

Updated 10-16 05:06

About this skill

Frontend Development Standards

Reference sources: Vue Official Style Guide, Element Plus best practices


UI Style Constraints

Strictly Prohibited (Common AI Style)

  • ❌ Blue-purple neon gradients, glowing outlines, glassmorphism
  • ❌ Large-area gradients, excessive decorative geometric shapes
  • ❌ Cyberpunk style, dark tech style, AI-style UI
  • ❌ Using emoji in UI copy

Admin System (Default Style)

Element Requirement
Theme Use the component library's default theme
Color Scheme Primarily black, white, and gray + 1 accent color
Motion Effects Restrained, keep only necessary interaction feedback

Tech Stack

Tier Vue (Preferred) React (Alternative)
Framework Vue 3 + TypeScript React 18 + TypeScript
Build Vite Vite
Routing Vue Router 4 React Router 6
State Pinia Zustand
UI Library Element Plus Ant Design

Vue Coding Standards

Component Basics

<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import type { User } from '@/types'

// Props & Emits
const props = defineProps<{ userId: number }>()
const emit = defineEmits<{ (e: 'update', value: string): void }>()

// 响应式状态
const loading = ref(false)
const user = ref<User | null>(null)

// 计算属性
const displayName = computed(() => user.value?.name ?? '未知用户')

// 生命周期
onMounted(async () => { await fetchUser() })

// 方法
async function fetchUser() {
  loading.value = true
  try {
    user.value = await api.getUser(props.userId)
  } finally {
    loading.value = false
  }
}
</script>

<template>
  <div class="user-card">
    <h3>{{ displayName }}</h3>
  </div>
</template>

<style scoped>
.user-card { padding: 16px; }
</style>

Naming Conventions

Type Convention Example
Component Files PascalCase.vue UserCard.vue
Composables useXxx.ts useAuth.ts
Store useXxxStore.ts useUserStore.ts

State Management (Pinia)

// stores/user.ts
export const useUserStore = defineStore('user', () => {
  const user = ref<User | null>(null)
  const token = ref<string>('')

  const isLoggedIn = computed(() => !!token.value)

  async function login(username: string, password: string) {
    const res = await api.login(username, password)
    token.value = res.token
    user.value = res.user
  }

  return { user, token, isLoggedIn, login }
})

Interaction State Handling

States That Must Be Handled: loading、empty、error、disabled、submitting

<template>
  <el-skeleton v-if="loading" :rows="5" animated />
  <el-result v-else-if="error" icon="error" :title="error">
    <template #extra>
      <el-button @click="fetchData">重试</el-button>
    </template>
  </el-result>
  <el-empty v-else-if="list.length === 0" description="暂无数据" />
  <template v-else>
    <!-- 正常内容 -->
  </template>
</template>

TypeScript Standards

// types/user.ts
export interface User {
  id: number
  username: string
  role: 'admin' | 'user'
}

export interface ApiResponse<T = unknown> {
  code: number
  message: string
  data: T
}

API Call Type Safety

Rule Description
✅ API utility functions support generics get<T>(url): Promise<T> instead of returning unknown
✅ Specify generics or assertions at the call site get<UserInfo>(url) or data as typeof ref.value
❌ Prohibit as any bypassing Masks type issues, causing pitfalls in later maintenance
// ❌ get() 返回 unknown,赋值报 TS2322
const data = await get('/contact/config')
contact.value = data

// ❌ as any 绕过
contact.value = data as any

// ✅ 泛型约束(推荐)
const data = await get<ContactConfig>('/contact/config')
contact.value = data

// ✅ 类型断言(最小改动)
contact.value = data as typeof contact.value

Performance Optimization

Scenario Solution
Large Lists Virtual scrolling
Routing Lazy loading () => import()
Computation Use computed caching
Large Data Use shallowRef
// 路由懒加载
const routes = [
  { path: '/dashboard', component: () => import('@/views/Dashboard.vue') }
]

// 请求防抖
import { useDebounceFn } from '@vueuse/core'
const debouncedSearch = useDebounceFn((keyword) => api.search(keyword), 300)

Directory Structure

src/
├── assets/
│   └── styles/          # 全局/共享样式
├── api/                 # API 请求
├── components/          # 通用组件
├── composables/         # 组合式函数
├── router/              # 路由配置
├── stores/              # Pinia stores
├── types/               # TypeScript 类型
├── utils/               # 工具函数
├── views/               # 页面组件
├── App.vue
└── main.ts

Style Management Standards

Rule Description
❌ Prohibit writing large blocks of styles in .vue blocks must not exceed 20 lines <style> blocks must not exceed 20 lines
❌ Prohibit writing large blocks of inline styles in .tsx style objects/CSS-in-JS must not exceed 20 lines Style objects/CSS-in-JS must not exceed 20 lines
✅ Extract shared styles to src/assets/styles/ Split files by module
✅ Keep only minimal styles inside components Vue: scoped fine-tuning; React: className references
src/assets/styles/
├── variables.scss       # 变量(颜色、间距、字号)
├── common.scss          # 通用样式
└── [module].scss        # 按模块拆分

Request Body Completeness Standards

Rule Description
❌ Prohibit omitting UI optional fields from the API All fields selected/entered by the user must be included in the request body
✅ Submission functions correspond one-to-one with form fields Use TypeScript interfaces to constrain the request body
// ❌ UI 有支付方式选择器,但请求体没传 payMethod
const payMethod = ref<'wechat' | 'points' | 'mixed'>('wechat')

async function createOrder() {
  await api.createOrder({
    items: orderItems.value,
    addressId: selectedAddress.value.id,
    // payMethod 忘记传了!支付方式选择 UI 形同虚设
  })
}

// ✅ 请求体与 UI 表单字段对应
interface CreateOrderRequest {
  items: OrderItem[]
  addressId: number
  payMethod: 'wechat' | 'points' | 'mixed'  // 类型约束确保不遗漏
}

async function createOrder() {
  const request: CreateOrderRequest = {
    items: orderItems.value,
    addressId: selectedAddress.value.id,
    payMethod: payMethod.value,  // TypeScript 会提示缺少字段
  }
  await api.createOrder(request)
}

API Error Handling Standards

Rule Description
❌ Silently ignoring non-success responses is prohibited res.code !== 200 the user must be prompted
✅ Unified error prompt Non-success responses are unified with message.error prompt
✅ Network exceptions must also be handled try/catch catch request exceptions
// ❌ 只处理成功,非 200 静默忽略
const res = await api.getList(params)
if (res.code === 200) {
  list.value = res.data
}

// ✅ 成功 + 失败都处理
try {
  const res = await api.getList(params)
  if (res.code === 200) {
    list.value = res.data
  } else {
    message.error(res.message || '加载失败')
  }
} catch (e) {
  message.error('网络异常,请稍后重试')
}

Type reuse conventions

Rule Description
❌ Defining the same interface repeatedly in multiple files is prohibited PageResponse、BaseResult etc.
✅ Common types are uniformly placed in @/types/common.ts global exports, referenced everywhere
// ❌ 每个 api 文件都定义一遍
// api/user.ts
interface PageResponse<T> { list: T[]; total: number }
// api/order.ts
interface PageResponse<T> { list: T[]; total: number } // 重复

// ✅ 统一定义,各处引用
// types/common.ts
export interface PageResponse<T> {
  list: T[]
  total: number
}

// api/user.ts
import type { PageResponse } from '@/types/common'

Detailed reference

File Content
references/frontend-style.md UI style, Vue 3 conventions, Pinia, API encapsulation, performance optimization
references/miniapp-pitfalls.md uni-app pitfalls: page stack read-only, Storage cleanup timing, lifecycle double triggering, frontend validation mirroring
references/date-time.md dayjs/date-fns date addition and subtraction, billing period calculation, month-end alignment prohibited

📋 This reply follows:frontend-dev - [specific section]