一键导入
web-coding
Triflow Web 前端开发规范。包括 Vue 3.5 + Element Plus + TypeScript 组件编写、API 类型定义、Pinia 状态管理、VXE Table、样式规范等项目约定。在编写或修改 triflow-web 代码时必须遵循。
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Triflow Web 前端开发规范。包括 Vue 3.5 + Element Plus + TypeScript 组件编写、API 类型定义、Pinia 状态管理、VXE Table、样式规范等项目约定。在编写或修改 triflow-web 代码时必须遵循。
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
Triflow App 移动端开发规范。包括 UniApp + Wot Design Uni + Alova 请求、API 路径前缀、z-paging 分页、UnoCSS 样式、跨端兼容性等项目约定。在编写或修改 triflow-app 代码时必须遵循。
Triflow 代码审查规范。对当前代码变更或指定文件/模块进行全面合规性检查,覆盖后端 Java、Web 前端、App 移动端的命名、架构、类型安全、数据库、API 等规范。使用此 skill 审查代码是否符合项目约定。
Triflow 后端 Java 开发规范。包括 MyBatis-Flex 查询、实体与 DTO/VO 定义、分层架构、数据库命名、异常处理、数据转换等项目约定。在编写或修改 triflow-server 代码时必须遵循。
基于 SOC 职业分类
| name | web-coding |
| description | Triflow Web 前端开发规范。包括 Vue 3.5 + Element Plus + TypeScript 组件编写、API 类型定义、Pinia 状态管理、VXE Table、样式规范等项目约定。在编写或修改 triflow-web 代码时必须遵循。 |
本 Skill 定义了 Triflow Web(管理后台)的代码编写规范,所有代码变更必须遵守。
| 技术 | 版本 | 用途 |
|---|---|---|
| Vue | 3.5 | UI 框架 |
| Element Plus | 最新 | UI 组件库 |
| Vite | 7.x | 构建工具 |
| TypeScript | 5.9 | 类型系统 |
| Pinia | 最新 | 状态管理 |
| pnpm | 10.0.0+ | 包管理器 |
| Turbo | 最新 | Monorepo 构建 |
#/ 是 web-admin/src/ 的别名(tsconfig.json 中配置 "#/*": ["./src/*"]"),用于应用内导入:
import { request } from '#/api/request';
import type { UserVO } from '#/api/modules/user';
import StatusTag from '#/components/status/StatusTag.vue';
triflow-web/
├── apps/
│ ├── web-admin/ # 管理后台应用(#/ 指向 src/)
│ │ └── src/
│ │ ├── api/ # API 接口定义
│ │ ├── components/ # 业务组件
│ │ ├── router/ # 路由配置
│ │ ├── store/ # Pinia 状态
│ │ └── views/ # 页面视图
│ └── backend-mock/ # Mock 服务器
├── packages/ # 共享包(@core, effects, utils 等)
└── internal/ # 内部工具链
views/{module}/{feature}/
├── index.vue # 列表页
├── detail.vue # 详情页(可选)
├── components/ # 页面私有组件
│ ├── user-form.vue
│ └── user-table.vue
└── composables/ # 页面私有 hooks
└── use-user.ts
<script setup lang="ts"><script setup lang="ts">
import { ref, computed, onMounted } from 'vue';
import type { UserVO } from '#/api/types';
// ==================== Props ====================
interface Props {
/** 用户ID */
userId: number;
/** 是否只读 */
readonly?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
readonly: false,
});
// ==================== Emits ====================
const emit = defineEmits<{
(e: 'update', user: UserVO): void;
}>();
// ==================== State ====================
const loading = ref(false);
// ==================== Computed ====================
const displayName = computed(() => userInfo.value?.nickname);
// ==================== Methods ====================
async function fetchUser() { ... }
// ==================== Lifecycle ====================
onMounted(() => { fetchUser(); });
// ==================== Expose ====================
defineExpose({ refresh: fetchUser });
</script>
代码区域顺序:Props → Emits → State → Computed → Methods → Lifecycle → Expose
| 类型 | 规范 | 示例 |
|---|---|---|
| 组件文件 | kebab-case.vue | user-list.vue |
| 组件导出名 | PascalCase | UserList |
| 变量/函数 | camelCase | getUserList |
| 常量 | UPPER_SNAKE_CASE | MAX_PAGE_SIZE |
| 类型/接口 | PascalCase | UserVO |
| CSS 类名 | kebab-case | .user-card |
| 场景 | 推荐组件 | 位置 |
|---|---|---|
| 状态标签 | StatusTag | #/components/status/ |
| 状态选择 | StatusSelect, StatusSwitch | #/components/status/ |
| 富文本编辑 | RichTextEditor | #/components/editor/ |
| 枚举下拉 | EnumSelect | #/components/enum-select/ |
| 图片上传 | ImageUpload | #/components/image-upload/ |
| 文件选择 | FileSelect | #/components/file-select/ |
详细组件模式、表格规范、Hooks 模板见 COMPONENTS.md
any// ❌ 禁止
const data: any = {}
function handle(params: any) {}
// ✅ 必须定义明确类型
interface UserVO {
id: number
username: string
status: number
}
每个 API 模块文件必须定义请求参数和响应类型:
// api/modules/user.ts
import { request } from '#/api/request';
import type { PageResult } from '#/api/types';
/** 用户查询参数 */
export interface UserQueryDTO {
keyword?: string;
status?: number;
pageNum: number;
pageSize: number;
}
/** 用户信息 */
export interface UserVO {
id: number;
username: string;
status: number;
createTime: string;
}
export function getUserPage(params: UserQueryDTO) {
return request.post<PageResult<UserVO>>('/base/user/page', params);
}
export function getUserDetail(id: number) {
return request.get<UserVO>('/base/user/detail', { params: { id } });
}
export function createUser(data: UserCreateDTO) {
return request.post<void>('/base/user/create', data);
}
// ✅ 使用 type 关键字导入类型
import type { UserVO, UserQueryDTO } from '#/api/modules/user';
// ✅ 混合导入时分开
import { ref, computed } from 'vue';
import type { Ref, ComputedRef } from 'vue';
使用 Setup Store 写法(defineStore + 箭头函数):
export const useUserStore = defineStore('user', () => {
// State
const userInfo = ref<UserVO | null>(null);
const token = ref<string>('');
// Getters
const isLoggedIn = computed(() => !!token.value);
// Actions
async function login(params: LoginParams) { ... }
function logout() { ... }
function $reset() { ... }
return { userInfo, token, isLoggedIn, login, logout, $reset };
});
// ❌ 禁止直接解构响应式数据(会丢失响应性)
const { userInfo } = useUserStore();
// ✅ 使用 storeToRefs
import { storeToRefs } from 'pinia';
const userStore = useUserStore();
const { userInfo, isLoggedIn } = storeToRefs(userStore);
const { login, logout } = userStore; // actions 可以直接解构
.user-card {
&__header { }
&__body { }
&--active { }
&--disabled { }
}
import { useNamespace } from '@vben/composables';
const ns = useNamespace('button');
// ns.b() → 'vben-button', ns.e('text') → 'vben-button__text'
前端通过 EnumSelect 组件自动加载后端枚举选项:
<EnumSelect v-model="form.status" enum-class="ArticleStatus" />
<EnumSelect v-model="form.type" enum-class="ConfigTypeEnum" clearable filterable />
<EnumSelect v-model="form.categories" enum-class="CategoryEnum" multiple />
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
enumClass | string | - | 枚举类简称(必填) |
clearable | boolean | true | 是否可清除 |
multiple | boolean | false | 是否多选 |
filterable | boolean | false | 是否可过滤 |
手动调用 API:
import { getEnumOptions, getEnumNameByCode } from '#/api/base/enum';
const options = await getEnumOptions('ArticleStatus');
const name = await getEnumNameByCode('ArticleStatus', '1'); // "已发布"
后端枚举定义规范见 server-coding skill 的枚举章节。
enumClass参数为后端枚举类的简称。
使用 Element Plus 原生表单校验:
<script setup lang="ts">
const formRef = ref<FormInstance>();
const rules: FormRules = {
username: [
{ required: true, message: '请输入用户名', trigger: 'blur' },
{ min: 2, max: 20, message: '长度在 2 到 20 个字符', trigger: 'blur' },
],
};
async function handleSubmit() {
const valid = await formRef.value?.validate();
if (!valid) return;
// 提交逻辑
}
</script>
优先使用 VXE Table(useVbenVxeGrid)处理大数据量表格,支持虚拟滚动、代理分页。
完整 VXE Table 配置模板和表单校验示例见 COMPONENTS.md
所有用户可见文案必须使用 t() 国际化函数:
<script setup lang="ts">
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
</script>
<template>
<el-button>{{ t('common.submit') }}</el-button>
</template>
/** 业务错误类型(由全局拦截器抛出) */
interface BusinessError extends Error {
code: number;
message: string;
}
async function handleSubmit() {
try {
await createUser(form);
ElMessage.success('创建成功');
} catch (error: unknown) {
// 全局拦截器已处理通用错误(Toast 提示)
// 这里仅处理需要特殊 UI 响应的业务逻辑
const bizError = error as BusinessError;
if (bizError.code === 101001) {
// 用户已存在的特殊处理
}
}
}
const UserList = () => import('#/views/system/user/index.vue')defineAsyncComponent(() => import(...)):scroll-y="{ enabled: true, gt: 100 }"loading="lazy"新增组件后请更新 CHARTER.md,包含:组件用途、Props 定义、使用示例、导入方式。