Files
huangguo_h5/.cursor/skills/frontend-project-standards/reference.md
T
2026-09-15 15:25:05 +07:00

3.4 KiB
Raw Blame History

参考模板

1) Service 方法模板

import request from '@/utils/request';

export interface QueryListParams {
  pageNumber: number;
  pageSize: number;
  mediaId?: string;
}

export interface QueryListResp {
  list: Array<{
    id: string;
    title: string;
  }>;
  total: number;
}

export default class ExampleApi {
  static queryList(params: QueryListParams): Promise<QueryListResp> {
    return request.get('/example/list', params);
  }
}

2) Hook 模板(单一职责)

import { reactive } from 'vue';
import ExampleApi, { QueryListParams } from '@/service/example';

export function useExampleList(initialParams: QueryListParams) {
  const state = reactive({
    loading: false,
    error: false,
    list: [] as Array<{ id: string; title: string }>,
    total: 0,
  });

  const fetchList = async (params = initialParams) => {
    state.loading = true;
    state.error = false;

    try {
      const res = await ExampleApi.queryList(params);
      state.list = res.list || [];
      state.total = Number(res.total || 0);
    } catch (err) {
      state.error = true;
      state.list = [];
      state.total = 0;
    } finally {
      state.loading = false;
    }
  };

  return {
    state,
    fetchList,
  };
}

3) 容器页模板(Container View

<script setup lang="ts">
import { computed } from 'vue';
import { useRoute } from 'vue-router';
import { useExampleList } from './hooks/useExampleList';

const route = useRoute();
const mediaId = computed(() => String(route.query.id || ''));

const { state, fetchList } = useExampleList({
  pageNumber: 1,
  pageSize: 30,
  mediaId: mediaId.value,
});

fetchList();
</script>

4) 错误处理最低检查项

  • 每个异步动作都具备成对的 loading 开关。
  • 请求失败时会落到确定性兜底状态。
  • 接口细节与类型由 service 层承载。
  • UI 层能展示空态/错误态,不允许静默失败。

5) 旧代码 any 的渐进迁移模式

// 改造前
// static getMediaHot(data: any) {
//   return request.get('/media/hot', data);
// }

// 改造后(增量迁移)
export interface GetMediaHotParams {
  pageNumber: number;
  pageSize: number;
  moduleId?: string;
}

export interface GetMediaHotResp {
  list: Types.Acg.MediaContentListItem[];
  total: number;
}

static getMediaHot(data: GetMediaHotParams): Promise<GetMediaHotResp> {
  return request.get('/media/hot', data);
}

6) PR 自检输出模板

在实现说明中必须附上以下区块(与 SKILL.md 保持一致):

### 自检结果
- [x] view/hook 层没有直接请求调用
- [x] service 边界方法具备明确类型
- [x] 未新增宽泛 any
- [x] 异步失败分支具备兜底状态
- [x] 混杂职责已拆分为聚焦 hooks/components

7) 轻量自动校验命令(可执行)

在仓库根目录执行以下命令进行快速自检:

# 1) 优先检查本次改动的 view/hook 文件是否直接调用 request
rg "request\\.(get|post|put|delete|deletes)\\(" <changed-view-or-hook-files>

# 2) 检查本次改动文件是否引入 any(人工结合 diff 判断)
rg "\\bany\\b" <changed-files>

判定规则:

  • 命令 1 预期为“无结果”;有结果则说明可能绕过 service。
  • 命令 2 不要求仓库清零,但本次改动不应新增宽泛 any
  • 只有在做全仓治理或用户明确要求时,才扩展到 src/views src/hooks src/service 全量扫描。