本文档旨在为 Trae AI(及开发者)提供统一的代码编写规范和最佳实践指南,确保代码风格一致、维护性高且符合项目架构要求。
src/
├── api/ # API 接口定义,按模块分类
├── components/ # 通用组件
│ ├── CommonPage/ # 列表页核心组件 (CommonTable, ProSearch 等)
│ ├── ProForm/ # 高级表单组件
│ └── ...
├── utils/ # 工具函数
│ ├── request.js # Axios 封装 (核心)
│ ├── use-dict-data.js # 字典 Hook
│ └── ...
├── views/ # 页面视图,按模块划分
│ ├── finance/ # 财务模块
│ │ ├── withdrawal/ # 提现管理
│ │ │ ├── index.vue # 列表主页
│ │ │ ├── components/ # 页面独有组件
│ │ │ │ ├── page-search.vue # 搜索栏
│ │ │ │ ├── audit-dialog.vue# 审核弹窗
│ │ │ │ └── ...
│ └── ...
所有“增删改查”类列表页面必须遵循以下结构:
<ele-page flex-table> 实现高度自适应。page-search.vue 组件。<common-table> 组件。index.vue 模板:
<template>
<ele-page flex-table>
<!-- 搜索栏 -->
<page-search @search="reload" />
<!-- 通用表格 -->
<common-table ref="pageRef" :pageConfig="pageConfig" :columns="columns">
<!-- 顶部工具栏 -->
<template #toolbar>
<div class="flex items-center mb-4">
<el-button type="primary" @click="handleAdd" v-permission="'module:add'">新增</el-button>
<!-- 统计信息等 -->
</div>
</template>
<!-- 自定义列渲染 -->
<template #status="{ row }">
<el-tag>{{ getStatusLabel(row.status) }}</el-tag>
</template>
<!-- 操作列 -->
<template #action="{ row }">
<el-button link type="primary" @click="handleEdit(row)" v-permission="'module:edit'">编辑</el-button>
</template>
</common-table>
<!-- 弹窗组件 -->
<edit-dialog ref="editDialogRef" @success="reload" />
</ele-page>
</template>
<script setup>
import { ref, reactive } from 'vue';
import CommonTable from '@/components/CommonPage/CommonTable.vue';
import PageSearch from './components/page-search.vue';
import EditDialog from './components/edit-dialog.vue';
// 页面配置
const pageConfig = reactive({
pageUrl: '/sys/module/list', // 列表接口 URL
fileName: '导出文件名',
rowKey: 'id'
});
// 列定义
const columns = ref([
{ label: '名称', prop: 'name', align: 'center' },
{ label: '状态', prop: 'status', slot: 'status', align: 'center' },
{ columnKey: 'action', label: '操作', slot: 'action', fixed: 'right', width: 140 }
]);
const pageRef = ref(null);
const editDialogRef = ref(null);
const reload = (where) => pageRef.value?.reload(where);
const handleAdd = () => editDialogRef.value?.handleOpen();
const handleEdit = (row) => editDialogRef.value?.handleOpen(row);
</script>
文件路径: ./components/page-search.vue
<template>
<ele-card :body-style="{ paddingBottom: '8px' }">
<ProSearch :items="formItems" @search="search" />
</ele-card>
</template>
<script setup>
import { reactive } from 'vue';
import ProSearch from '@/components/CommonPage/ProSearch2.vue';
const emit = defineEmits(['search']);
const formItems = reactive([
{ type: 'input', label: '关键词', prop: 'keywords' },
{
type: 'dictSelect',
label: '状态',
prop: 'status',
props: { code: 'sys_status' } // 对应字典编码
}
]);
const search = (data) => emit('search', data);
</script>
文件路径: ./components/*-dialog.vue
<template>
<ele-modal v-model="visible" :title="title" width="600px">
<el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
<el-form-item label="名称" prop="name">
<el-input v-model="form.name" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="handleSubmit">确定</el-button>
</template>
</ele-modal>
</template>
<script setup>
import { ref, reactive } from 'vue';
import request from '@/utils/request';
import { ElMessage } from 'element-plus';
const emit = defineEmits(['success']);
const visible = ref(false);
const title = ref('新增');
const formRef = ref();
const form = reactive({ id: null, name: '' });
const rules = { name: [{ required: true, message: '请输入名称' }] };
const handleOpen = (row) => {
if (row) {
title.value = '编辑';
Object.assign(form, row);
} else {
title.value = '新增';
// 重置表单逻辑
form.id = null;
form.name = '';
}
visible.value = true;
};
const handleSubmit = async () => {
await formRef.value.validate();
const url = form.id ? '/sys/module/update' : '/sys/module/add';
const res = await request.post(url, form);
if (res.data.code === 200) {
ElMessage.success(res.data.msg);
visible.value = false;
emit('success');
}
};
defineExpose({ handleOpen });
</script>
flex, items-center, justify-betweenm-4 (margin), p-4 (padding)w-full, h-full<style scoped> 中使用 :deep()。<script setup> 语法。字典数据: 必须使用 useDictData Hook 获取字典数据。
import { useDictData } from '@/utils/use-dict-data';
const [statusOptions] = useDictData(['sys_status']);
@/utils/request 实例。res.data.code === 200 的业务成功逻辑。catch。当用户请求创建新页面或功能时,请按以下步骤执行:
src/api/module/index.js (如需)src/views/module/page/index.vuesrc/views/module/page/components/page-search.vuesrc/views/module/page/components/*-dialog.vuev-permission)。router/routes.js)。open_preview 工具检查 UI 布局。注意: 在修改现有代码时,请先读取原文件理解上下文,避免破坏现有逻辑。