/**
* Vue 3 代码模板
*
* 严格遵循 M8 开发规范:
* - 4 空格缩进
* - 头部注释完整
* - <script setup lang="ts">
* - TypeScript 类型声明
* - Pinia 状态管理
* - 全局变量直接使用(不 import)
* - BEM 命名规范
*/
interface TemplateOptions {
moduleName: string;
pageName: string;
author: string;
description: string;
dateStr: string;
}
/**
* 生成 Vue 3 页面模板
* 遵循规范:<script setup lang="ts">、DefineProps、TypeScript、Pinia
*/
export function generateVue3Template(options: TemplateOptions): string {
const { moduleName, pageName, author, description, dateStr } = options;
const pascalName = toPascalCase(pageName);
return `<!--
* @作者: ${author}
* @创建时间: ${dateStr}
* @修改时间: ${dateStr}
* @版本: [1.0]
* @版权: 国泰新点软件股份有限公司
* @描述: ${description}
-->
<template>
<div class="${moduleName}">
<div class="${moduleName}__content">
<!-- 页面内容区域 -->
</div>
</div>
</template>
<script setup lang="ts">
/**
* ${description}
*
* ⚠️ 全局变量说明(已全局注入,禁止 import):
* - Config: 配置对象,如 Config.serverUrl
* - Util: 工具函数,如 Util.ajax(), Util.string.isMobile()
* - ejs: EJS API,如 ejs.ui.toast(), ejs.page.open()
*/
import { ref, reactive, onMounted } from 'vue';
import { use${toPascalCase(moduleName)}Store } from '../../store';
// 定义 Props 接口
interface IProps {
id?: string; // 路由传参 ID
}
// 定义 Props (带默认值)
const props = withDefaults(defineProps<IProps>(), {
id: ''
});
// 使用 Store
const store = use${toPascalCase(moduleName)}Store();
// ==================== 页面状态 ====================
const loading = ref<boolean>(false);
// 定义列表数据类型
interface IListItem {
id: string;
title: string;
content: string;
createTime: string;
status: string;
}
const list = ref<IListItem[]>([]);
// ==================== 生命周期 ====================
onMounted(() => {
init();
});
// ==================== 方法定义 ====================
/**
* 初始化页面
*/
const init = (): void => {
// 处理路由参数 (Vue3 推荐在 onLoad 中处理,但此处为通用逻辑)
if (props.id) {
console.log('Page ID:', props.id);
}
loadData();
};
/**
* 加载数据
* 使用 Util.ajax 发起请求(全局变量,无需 import)
*/
const loadData = (): void => {
loading.value = true;
ejs.ui.showWaiting('加载中...');
Util.ajax({
url: Config.serverUrl + '/rest/api/${moduleName}/list',
data: {
params: JSON.stringify({
currentpageindex: 0,
pagesize: 10
})
},
dataPath: 'custom'
}).then((res: any) => {
if (res?.status?.code === 1) {
list.value = res.infolist || [];
// 更新 Store
store.setList(list.value);
} else {
ejs.ui.toast(res?.status?.text || '加载失败');
}
}).catch((err: any) => {
ejs.ui.toast('网络请求失败');
console.error('${moduleName} 数据加载失败:', err);
}).finally(() => {
loading.value = false;
ejs.ui.closeWaiting();
});
};
</script>
<style lang="scss" scoped>
@import "./css/${moduleName}.scss";
</style>
`;
}
/**
* 生成 Vue 3 SCSS 模板
* 遵循 BEM 命名规范
*/
export function generateVue3ScssTemplate(options: { moduleName: string }): string {
const { moduleName } = options;
return `/**
* ${moduleName} 模块样式
*
* 命名规范:BEM (Block__Element--Modifier)
* - Block: .${moduleName}
* - Element: .${moduleName}__content
* - Modifier: .${moduleName}--active
*/
.${moduleName} {
display: flex;
flex-direction: column;
min-height: 100vh;
background-color: #f5f5f5;
// 内容区域
&__content {
flex: 1;
padding: 12px;
}
// 列表项
&__item {
padding: 12px;
margin-bottom: 8px;
background-color: #fff;
border-radius: 8px;
}
// 样式穿透示例(Vue3 使用 :deep())
:deep(.em-component) {
// 覆盖组件样式
}
// 空状态
&__empty {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 40px 0;
color: #999;
}
}
`;
}
/**
* 生成 Vue 3 路由模板
*/
export function generateVue3RouterTemplate(options: { moduleName: string; pageName: string }): string {
const { moduleName, pageName } = options;
return `/**
* ${moduleName} 模块路由配置
*/
const routes = [
{
path: 'pages/${moduleName}/${pageName}',
style: {
navigationBarTitleText: '页面标题'
}
}
];
export default {
routes,
options: {
autoSubPackages: true
}
};
`;
}
/**
* 生成 Vue 3 Store 模板 (Pinia)
*/
export function generateVue3StoreTemplate(options: { moduleName: string }): string {
const { moduleName } = options;
const pascalModuleName = toPascalCase(moduleName);
return `/**
* ${moduleName} 模块状态管理 (Pinia)
*/
import { defineStore } from 'pinia';
interface IState {
list: any[];
current: any | null;
}
export const use${pascalModuleName}Store = defineStore('${moduleName}', {
state: (): IState => ({
list: [],
current: null
}),
actions: {
/**
* 设置列表数据
*/
setList(list: any[]) {
this.list = list;
},
/**
* 设置当前选中项
*/
setCurrent(item: any) {
this.current = item;
},
/**
* 重置状态
*/
reset() {
this.list = [];
this.current = null;
}
}
});
`;
}
/**
* 生成 Vue 3 Mock 模板
* 遵循 Mock 数据规范
*/
export function generateVue3MockTemplate(options: { moduleName: string }): string {
const { moduleName } = options;
return `/**
* ${moduleName} 模块 Mock 数据
*
* 规范说明:
* - 使用 import Mock from '@mock' 导入
* - methodUrl 格式:/rest/api/[module]/[action]
* - output 使用 Mock.mock() 包装
* - status 结构:{ code: 0, text: '成功' }
*
* ⚠️ Mock.Random 常用方法:
* - Mock.Random.guid() - GUID
* - Mock.Random.cname() - 中文名
* - Mock.Random.csentence(n) - 中文句子
* - Mock.Random.cparagraph() - 中文段落
* - Mock.Random.date() / datetime() - 日期/时间
* - Mock.Random.integer(min, max) - 整数
* - Mock.Random.image(size) - 图片
*
* ❌ 不存在的方法:Mock.Random.mobile()
* ✅ 手机号生成:'1' + Mock.Random.string('number', 10)
*/
import Mock from '@mock';
/**
* 生成模拟手机号
* @returns {string} 11位手机号
*/
const generateMobile = () => '1' + Mock.Random.string('number', 10);
const resultData = [
{
methodUrl: '/rest/api/${moduleName}/list',
input: {
currentpageindex: '',
pagesize: ''
},
output: Mock.mock({
'status': {
'code': 1,
'text': '成功'
},
'infolist|10-20': [
{
'id': () => Mock.Random.guid(),
'title': () => Mock.Random.csentence(5, 15),
'content': () => Mock.Random.cparagraph(1, 3),
'createTime': () => Mock.Random.datetime('yyyy-MM-dd HH:mm:ss'),
'mobile': generateMobile,
'status|1': ['待处理', '处理中', '已完成']
}
],
'total': () => Mock.Random.integer(50, 200)
})
},
{
methodUrl: '/rest/api/${moduleName}/detail',
input: {
id: ''
},
output: Mock.mock({
'status': {
'code': 1,
'text': '成功'
},
'info': {
'id': () => Mock.Random.guid(),
'title': () => Mock.Random.csentence(5, 15),
'content': () => Mock.Random.cparagraph(3, 5),
'author': () => Mock.Random.cname(),
'mobile': generateMobile,
'createTime': () => Mock.Random.datetime('yyyy-MM-dd HH:mm:ss'),
'updateTime': () => Mock.Random.datetime('yyyy-MM-dd HH:mm:ss')
}
})
}
];
export default resultData;
`;
}
/**
* 转换为 PascalCase
*/
function toPascalCase(str: string): string {
return str
.split(/[_-]/)
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
.join('');
}