工程业务代码最佳实践
重要通知
。
基本概况
价值导向
- 效益至上、效率优先、兼顾成本、发挥个体优势。
- 实现前端对外交付目标的效益最大化、效率最大化与成本最小化,以及将个体优势发挥在前端价值对外输出上,并具有可持续性与长期性。
- 效益至上: 人效比与投入产出比的量化输出价值是第一导向。
- 效率优先: 在效益得到保障的前提下,效率越高,效益产出就越多。
- 兼顾成本: 在效益与效率得到保障的基础上,成本优化越彻底,效益输出就越多。
- 倡导充分发挥个体优势,引导个体优势积极发挥到前端团队整体价值对外输出的成果上来。
- 功劳与成果导向: 以功劳与成果为导向,积极引导苦劳向功劳转化,引导结果向成果输出。苦劳与结果化,是形式主义滋生的本质原因。
- 功劳: 基于目标共识实现保质保量准时交付目标,或者超质、超量或提前等交付目标。
- 成果: 对外输出效益覆盖或远远超出对内投入成本,实现有效价值的剩余。
- 注意事项:规范不是条条框框,不是形式主义,不是为了规范而规范,规范也有时效性与局限性,如果已存在的规范不再符合价值导向,就必须及时砍掉,避免形成历史冗余与负担。
整体目标
设计一个支撑到2030年(2022年起,8年)的ERP前端系统
如何解决ERP前端系统的稳定性与伸缩性问题? 1、代码可靠性:统一前端代码规范与交付共识,弱化个体差异,强化整体价值,积极践行交付。
2、代码稳定性:渐进式推进单元测试Jest或Vitest的覆盖率。
3、系统容错性:采用聚合式系统的架构设计,弱化业务对技术的依赖,大幅度降低系统架构设计的耦合性与冗余性,消除局部业务模块或功能函数的异常错误影响整体系统、视图页面与操作交互的可靠性和稳定性。
4、系统伸缩性:隔离业务与技术之间的依赖,推进"聚合式系统"的架构设计
5、系统稳定性:整合前端异常错误监控系统,对ERP生产环境实时监控,提前把控代码风险,实时处理解决出现的异常错误问题。
如何兼顾ERP前端系统的前端开发效率与质量问题? 全面推行聚合式系统的架构设计:帮助业务开发人员脱离于具体技术的开发实现,通过"数据驱动"与"功能订阅"来实现业务需求的快速交付。
如何解决Vue3生态系统的迭代更新与停止维护带来的风险问题? 1、引入第三方库需要经过评估与审核,尤其是小众且维护不稳定的第三方库,逐步替换淘汰掉小众且维护不稳定的小众库。
2、解耦业务与技术的直接依赖,通过业务按需订阅服务,再由服务定制技术来完成整个业务系统的聚合交付。
如何解决ERP前端系统不依赖个体开发者能力差异与人员变化的问题? 1、规范:强化前端成员的实现规范与交付统一。
2、聚合:践行“聚合式系统”的架构设计与具体实现。
3、优势:将个体的优势发挥到聚合式系统的要素(模块系统)设计实现,从而可以提高其他个体的业务需求对外交付效率。
如何解决ERP前端系统不依赖前端负责人架构设计的变化问题? 1、践行前端团队的"价值导向",弱化个体的非整体价值输出内容。
2、遵循"聚合式系统"的架构设计,实现业务需求、功能实现与技术支撑的独立性与可靠性,避免因为个体的不确定性导致系统的不稳定性问题。
架构: 聚合式设计
聚合式设计的核心在于采用聚合的思想来设计系统,即系统内核 + 基础服务 + 工具箱 + 部件/插件/组件+ 业务模块 + 业务辅助功能。
系统内核
为前端系统运行提供支撑的底层基础环境,当前ERP的前端系统内核是基于Vue3的响应式系统,例如Vue、Vue-router、Pinia等。
基础服务
为业务系统提供的诸如HTTP、Ajax、Fetch、cos-js-sdk-v5腾讯云上传等基础服务。
工具箱
独立提供各种JS功能的函数工具,不依赖具体的技术栈,不引用其他第三方库。
部件
不依赖于Vue3生态系统,提供完整的场景解决方案服务,例如Tinymce编辑器、Echarts可视化工具等。
插件
可依赖Vue3的生态系统,立足于具体的业务场景,抽离出的相关服务,例如Lodash等。
组件
依赖于Vue3的生态系统,基于系统的UI通用组件与UI业务组件,例如Element-plus等。
业务模块
实现业务需求的各个相关功能模块。
业务辅助功能
辅助业务功能模块的各种实现。
这个前端个体的PBC
关键字参考
# --------------------------------------- #
# 命名名称规范参考 后缀-suffix 前缀-prefix
# --------------------------------------- #
# 关键字
Backhaul:回传 回程 xxxBackhaulEvent eventBackhaulConf
事件回传配置
异步: async | 同步: sync
> extends:扩展 helpers:辅助 inherit:继承 auxiliary:附属 Tools:工具 alias:别名 ergodic:遍历
> instance:实例 extension:扩展 recursion:递归 deliver:传递 publish:发布 receiver-接收者
> process:进程 thread:线程 coroutine:协程 Fiber:纤维 wrapper:包裹 patch:补丁 decorator:装饰器
> dispatch:派发 compile:编译 parse:解析 appoint:任命
> Listener:监听器 Watcher:观察者 Observer:观察者 Trigger:触发器 Launcher:发射器 Track:跟踪 轨迹 Tracker:追踪器
> concurrency:并发 scheduler:调度 schedule:计划 patch:补丁 fragment:片段 supense:暂停 performance:性能 yield:协商 launch:唤起
> aggregation:聚合 hydrate:合成 simulate:模拟 Pool:池 Filter:过滤器 Interceptor:拦截器 Detector:检测器
> normalize: 使标准化
+ iterator(迭代器)
# 常用类名
> Basic:基础 | Depend:依赖 | Subject:主体 | 根基:Foundation temp:临时
> Ancestry:祖先 Parent:父类 Current:当前 Child:子类 Grandson:孙类
# 数据机构
> heap:堆 stack:栈 queue:队列 unit:单元
> hook:钩子
# 常量命名
> WHITE_LIST:白名单 BLACK_LIST:黑名单
> subscript:sub 下标 superscript:sup 上标
# 安全
> timestamp:时间戳
# 公共命名规范
> identifier:标志符
# 运算
> plus:加 minus:减 multiply:乘 divide:除 remainder:余数 decimal:小数 integer:整数 fraction:分数
# 基本操作
> create:创建 add:添加 delete:删除 edit:编辑 remove:移除
> handler:处理器 model:模型 manager:管理器 Factory:工厂 Provider:供应商 Engine:引擎 Service:服务 Task:任务
> Context:上下文
# API命名
# 目录名称
> function-name
# 文件名称
> file-name.
# CSS命名
项目工程结构规范
具体要求
- 通用性
- 统一性
- 可扩展
- 可迁移
- 可升级
代码质量
- 共识:代码规范。
- 简洁:实现代码还可不可以删?
- 注释:输入对象、输出对象、代码结构、实现逻辑等。
命名参考
- 语义化
- 结构化
- 归类化
文件夹与文件名
// 文件夹
xxx-xxx
// 文件名
xxx-xxx.[后缀]
命名规则
1、基本原则: 语义化、结构化、简洁化。 2、一级目录命名规则优先,其他遵循非一级目录命名约定。 3、非对外导出供应其他外部文件引用的变量与函数,视为一般变量与函数,遵循驼峰命名。其中,变量: xxx、xxxXxx等,函数: xxxFunc、xxxXxxFunc等。
前端工程结构
├─ dist # 存放打包后的工程文件
├─ env # 环境变量
│ ├─ .env # 所有环境都构建
│ ├─ .env.local # 所有环境都构建,但会被git忽略
│ ├─ .env.development # development环境构建
│ ├─ .env.test # test环境构建
│ ├─ .env.production # production环境构建
│ ├─ .env.development.local # development环境构建,但会被git忽略
│ ├─ .env.production.local # production环境构建,但会被git忽略
│ ├─ .env.build
│ ├─ .env.build-global
│ ├─ .env.build-test
│ ├─ .env.build-test-global
│ └─ .env.serve.local
├─ builds # 构建发布配置与加工
├─ lib # 静态链接库
├─ dll # 动态链接库
├─ bin # 二进制文件
├─ public # 公共资源,不参与构建编译流程
│ ├─ images # 图片
│ └─ data # 数据
├─ src # 业务代码
│ ├─ common # 公共依赖
│ │ ├─ languages # 多语言
│ │ └─ permission # 权限
│ ├─ assets # 静态资源,参与构建编译流程
│ │ ├─ images # 图片
│ │ ├─ style # CSS样式
│ │ └─ themes # 主题
│ ├─ components # 全局UI组件
│ │ ├─ common # 公共组件
│ │ ├─ tools # 工具组件
│ │ │ ├─ decorators # 装饰器
│ ├─ declare # TS声明文件
│ ├─ api # 网络API接口
│ ├─ interface # TS接口
│ ├─ constants # 常量
│ ├─ core # 核心库,提供基础服务,不依赖项目技术栈
│ ├─ widgets # 部件,不依赖项目技术栈
│ ├─ plugins # 插件,依赖项目技术栈
│ ├─ helpers # 业务辅助功能,可依赖外部资源
│ ├─ utils # 工具箱,纯JS函数无任何依赖
│ ├─ config # 视图数据公共配置
│ ├─ store # 状态管理
│ ├─ router # 路由管理
│ ├─ template # 模板,[可选]
│ └─ views: # 视图页面
│ │ ├─ page-module # 视图模块一
│ │ │ ├─ components # 组件集
│ │ │ │ ├─ add.vue
│ │ │ │ └─ module.vue
│ │ │ ├─ style # 样式集
│ │ │ │ ├─ index-scoped.scss # 当前视图作用域
│ │ │ │ └─ index.scss # 全局作用域
│ │ │ ├─ api.ts # API接口
│ │ │ ├─ style.scss # CSS样式
│ │ │ ├─ config.ts # 配置
│ │ │ ├─ constant.ts # 常量
│ │ │ ├─ interface.ts # TS接口
│ │ │ ├─ index.ts # 页面视图逻辑实现
│ │ │ ├─ list.vue # 列表页面视图
│ │ │ ├─ index.vue # 非列表页面视图
│ │ │ └─ Readme.md # 说明文档
│ │ ├─ page-module # 视图模块二
│ │ └─ page-module # 视图模块三
├─ packages: # 包
├─ docs: # 文档
├─ demo: # 代码示例
├─ test: # 单元测试
├─ App.vue: # 主模板
├─ main.js: # 主函数
├─ jsconfig.json
├─ Makefile: # 构建编译环境
└─ Readme.md: # 工程项目说明文档
common
定位:存放全局使用的基础依赖,例如多语言配置文件languages、权限配置文件permission等。
命名规则:当前目录中所有对外导出的对象,需要遵循xxxCommon规范,其他没有作要求的则遵循一般规范即可。
// 变量
export const xxxCommon = "";
// 函数
export function xxxCommon() {}
- 引用示例
import { xxxCommon } from '@/common/xxxx';
- 注意事项:。
components
定位:存放全局使用的组件,包括通用组件与业务组件,遵循复用性、扩展性与可维护性,以及接入成本低,显著提升二次开发效率。
命名规则:当前目录中所有对外导出的对象,需要遵循xxxUI规范,其他没有作要求的则遵循一般规范即可。
- 引用示例
import { xxxUI } from '@/components/xxxx';
- 注意事项:。
api
定位:存放全局使用的API接口。
命名规则:当前目录中所有对外导出的对象,需要遵循xxxAPI规范,其他没有作要求的则遵循一般规范即可。
/**
* 结构:行为 + 内容 + API
*
* 行为: 增-add、删-delete、改-modify、查-get
* 内容(抽象): 列表-list、详情-detail
* 内容(具体): 商品列表-goodsList、订单列表-orderList
*/
export function doWhatAPI(data = {}, options = {}) {
//
}
// 获取...
async function get...API(data = {}, options = {}) {}
// 详情: 通用
export function getDetailAPI(data = {}, options = {}) {};
// 详情: XXX详情
export function getXxxDetailAPI(data = {}, options = {}) {};
// 列表: 通用
export function getListAPI(data = {}, options = {}) {};
// 列表: XXX列表
export function getXxxListAPI(data = {}, options = {}) {};
// 添加: 通用,列表中的选项
export function addItemAPI(data = {}, options = {}) {};
// 添加: 列表中的XXX选项
export function addXxxItemAPI(data = {}, options = {}) {};
// 修改: 通用,列表中的选项
export function modifyItemAPI(data = {}, options = {}) {};
// 修改: 列表中的XXX选项
export function modifyXxxItemAPI(data = {}, options = {}) {};
// 删除: 通用,列表中的选项
export function deleteItemAPI(data = {}, options = {}) {};
// 删除: 列表中的XXX选项
export function deleteXxxItemAPI(data = {}, options = {}) {};
// 操作...
async function do...API(data = {}, options = {}) {}
// 导出报告
async function exportReportAPI(data = {}, options = {}) {}
- 引用示例
import { xxxAPI } from '@/api/xxxx';
- 注意事项:。
interface
定位:存放全局使用的接口声明(数据结构、字段类型与值等)。
命名规则:当前目录中所有对外导出的对象,需要遵循xxxInterface、xxxEnum、xxxType等规范,其他没有作要求的则遵循一般规范即可。
export interface xxxInterface {}
export enum xxxEnum {
xxx = xxx
}
- 引用示例
import { xxxInterface } from '@/interface/xxxx';
import { xxxEnum } from '@/interface/xxxx';
- 注意事项:。
constants
定位:存放全局使用的常量文件。
命名规则:当前目录中所有对外导出的对象,需要遵循XXX、XXX_XXX规范,其他没有作要求的则遵循一般规范即可。
// 变量
export const XXX = Object.freeze("");
export const XXX_XXX = Object.freeze("");
export const XXX_XXX_XXX = Object.freeze("");
// 函数
export function XXX() {}
- 引用示例
import { XXX } from "@/constants/xxx";
- 注意事项:注意引用时对原数据的变动影响问题,建议创建常量前使用Object.freeze()冻结常量声明对象。
core
定位:存放全局使用的基础服务。
命名规则:当前目录中所有对外导出的对象,需要遵循xxxCore规范,其他没有作要求的则遵循一般规范即可。
// 变量
export const xxxCore = "";
// 函数
export function xxxCore() {}
- 引用示例
import { xxxCore } from '@/core/xxxx';
- 注意事项:注意基础服务的稳定性,这是系统的基础服务,对系统容易造成相当严重的稳定性问题。
widgets
定位:存放全局使用的部件。
命名规则:当前目录中所有对外导出的对象,需要遵循xxxWidget规范,其他没有作要求的则遵循一般规范即可。
// 变量
export const xxxWidget = "";
// 函数
export function xxxWidget() {}
- 引用示例
import { xxxWidget } from '@/widgets/xxxx';
- 注意事项:注意抽象性与高内聚低耦合,不可涉及任何与业务相关的代码。
plugins
定位:存放全局使用的插件。
命名规则:当前目录中所有对外导出的对象,需要遵循xxxPlugin规范,其他没有作要求的则遵循一般规范即可。
// 变量
export const xxxPlugin = "";
// 函数
export function xxxPlugin() {}
- 引用示例
import { xxxPlugin } from '@/plugins/xxxx';
- 注意事项:注意抽象性与高内聚低耦合,不可涉及任何与业务相关的代码。
helpers
定位:存放全局使用的业务辅助功能。
命名规则:当前目录中所有对外导出的对象,需要遵循xxxHelper规范,其他没有作要求的则遵循一般规范即可。
// 变量
export const xxxHelper = "";
// 函数
export function xxxHelper() {}
- 引用示例
import { xxxHelper } from '@/helpers/xxxx';
- 注意事项:注意抽象性与高内聚低耦合,不可涉及任何与业务相关的代码。
utils
定位:存放全局使用的纯JS工具箱。
命名规则:当前目录中所有对外导出的对象,需要遵循xxxUtil规范,其他没有作要求的则遵循一般规范即可。
// 变量
export const xxxUtil = "";
// 函数
export function xxxUtil() {}
- 引用示例
import { xxxUtil } from '@/utils/xxxx';
- 注意事项:这是纯JS实现的工具,禁用引入任何第三方库,以及非本目录中的任何文件。
config
定位:存放全局使用的视图数据配置文件。
命名规则:当前目录中所有对外导出的对象,需要遵循xxxConfig规范,其他没有作要求的则遵循一般规范即可。
├─ config
│ ├─ columns # 存放列表行字段配置
│ └─ searchs # 存放搜索字段配置
// 函数
export function xxxConfig() {
return {};
}
- 引用示例
import { xxxConf } from '@/config/columns/xxxx';
import { xxxConf } from '@/config/searchs/xxxx';
- 注意事项:推荐使用函数,而不是变量,因为很难保障其他引用会存在直接操作更新对象,从而影响其他引用该对象的逻辑,或者使用Object.freeze()冻结。
store(状态管理)
定位:存放全局使用的状态管理文件。
命名规则:当前目录中所有对外导出的对象,需要遵循xxxStore规范,其他没有作要求的则遵循一般规范即可。
// 变量
export const xxxStore = "";
// 函数
export function xxxStore() {}
- 引用示例
import { xxxStore } from '@/store/xxxx';
- 注意事项:。
router(路由系统)
定位:存放全局使用的路由配置文件。
命名规则:当前目录中所有对外导出的对象,需要遵循xxxRouter规范,其他没有作要求的则遵循一般规范即可。
// 变量
export const xxxRouter = "";
// 函数
export function xxxRouter() {}
- 引用示例
import { xxxRouter } from '@/router/xxxx';
- 注意事项:。
views
服务器工程结构
├─ api # API接口集
├─ service # 业务逻辑
├─ module
│ ├─
│ └─
├─ control
├─ plugins # 第三方插件
├─
├─
├─
└─
文件引用关系规范
├─ A
├─ B
│ ├─ B1
│ ├─ B2
│ │ ├─ B21
│ │ │ ├─ B211
│ │ │ ├─ B212
│ │ ├─ B22
│ │ │ ├─ B221
│ │ │ ├─ B222
│ ├─ B3
│ ├─ B4
│ │ ├─ B41
│ │ ├─ B42
│ ├─ B5
│ └─ B6
├─ C
│ ├─ C1
│ │ └─ input.txt
│ └─ C2
├─ D
└─ E
HTML5最佳实践
严重警告
HTML节点禁止嵌套级数超过10级。
注释规范
JavaScript最佳实践
变量
使用字面量创建变量
const name;
const name = undefined;
const name = null;
const name = true;
const name = 0;
const name = "";
const name = {};
const name = [];
const name = Symbal();
基础命名规范
/*****************************************************
* Author: 王军
* Date: 2022-08-16
* 功能描述:
*****************************************************/
/**
* 常量
* 英文单词 + _,且已英文单词开头
*/
const COM_COLOR = '';
const COM_ZOO_COLOR = '';
/**
* 变量
*
* 字面量
* 驼峰法来命名(camelCase)
*/
const color = '';
const student = { name: '', sex: '', total: 100, };
// 全局变量 | 大写 (UPPERCASE )
// 常量 (如 PI) | 大写 (UPPERCASE )
// 私有变量
const _varible = '';
// 文件夹 | folder-name
// 文件名 | file-name.xxx
// 行尾使用分号
// 行末逗号
// 空格缩进 | 2个空格符号
// 对象字面量的冒号后加空格,冒号前不加
// 条件语句关键字后加空格
if () {}
函数
命名规则:行为 + 内容 + Fn,采用驼峰命名法。
// 某某功能实现函数
function xxFn() {
//
}
// 初始化依赖
async function initDependFunc(data = {}) {
//
}
注释规范
/**
* 功能描述
* @param {string} name: 姓名,<必填>,默认为"张三"。
* @param {string} sex: 性别,<选填>,默认为undefined。
* @return {Element}
*/
function print(name: string, sex?: string) {
//
}
JSDoc工具
JSDoc 是一个用于 JavaScript 的API文档生成器。
CSS最佳实践
严重警告
CSS作用域必须审查与限制,避免出现各种非预期的样式污染问题。
类名命名规范
全局类名
全局命名必须有醒目的全局标识,所有非全局类名,必须使用限制性的CSS结构嵌套约束。
.com-xxx {}
组件类名
- components目录
.component-xxx {}
- widgets目录
.widget-xxx {}
- other其他目录
.[other]-xxx {}
页面类名
每一个视图页面的独立类名,具有唯一性,例如views/page-module,则命名为: page-module、page-module-xxx等。
<style type="text/css">
.page-goods {}
.page-order {}
.page-user {}
</style>
<section class="page-goods"></section>
<section class="page-order"></section>
<section class="page-user"></section>
辅助类名
补充作用,必须与限制类名一起使用,禁止独立使用。
- views/page-module
<style type="text/css">
.page-module.aux-xxx {}
</style>
<section class="page-module aux-xxx"></section>
专用类名
.xxx-wrapper {}
.xxx-layout {}
.xxx- {}
.xxx- {}
.xxx- {}
.xxx- {}
.xxx- {}
.xxx- {}
.xxx- {}
HTML类名
类名结构显著,类名形象有序,充分运用HTML5语义化标签。
- 页面: views、pages
<template>
<!-- 页面 -->
<section class="com-frame page-tools-[unique-name]">
<!-- 布局 -->
<div class="layout-[section-name]">
<!--区域-->
<div class="section-[container-name]">
<!--容器-->
<div class="container-[wrapper-name]">
<!--包裹-->
<div class="wrapper-[module-name]">
<!--模块-->
<div class="module-[function-name]">
<!--功能: 事件行为-->
<div class="function-[name]">
<!--内嵌直接元素-->
</div>
</div>
</div>
</div>
</div>
</div>
</section>
</template>
- 全局组件: components
<template>
<!--最外层-->
<section class="com-frame component-[unique-name]">
</section>
</template>
- 局部组件: components
<template>
<!--最外层-->
<section class="com-frame own-[unique-name]">
</section>
</template>
传统CSS写法
<style type="text/css">
</style>
<section class="section-activity-current-layout">
<h3 class="abbr-sacl-h3">Limited time sales activities</h3>
</section>
注释规范
TypeScript最佳实践
- 所有字段必须有注释说明
接口
// 接口
interface ITF_DESCRIBE_NAME {
}
Vue3生态技术栈最佳实践
页面通用结构规范
├─ page-module # 视图模块一
│ ├─ components # 组件集,[可选]
│ │ ├─ add.vue # 示例
│ │ └─ module.vue
│ ├─ style # 样式集,[可选]
│ │ ├─ index-scoped.scss # 当前视图作用域
│ │ └─ index.scss # 全局作用域
│ ├─ api.ts # API接口,[可选]
│ ├─ config.ts # 配置,[可选]
│ ├─ constant.ts # 常量,[可选]
│ ├─ interface.ts # TS接口,[可选]
│ ├─ index.ts # 页面视图逻辑实现,[可选]
│ ├─ list.vue # 列表页面视图,[可选]
│ ├─ index.vue # 非列表页面视图,[可选]
│ └─ Readme.md # 说明文档,[可选]
components
style
api.ts
- 命名规则: 行为 + 内容 + API
- 行为: 增-add、删-delete、改-modify、查-query
- 内容(抽象): 列表-list、详情-detail
- 内容(具体): 商品列表-goodsList、订单列表-orderList
export function doWhatAPI(data = {}, options = {}) {
//
}
// 获取列表数据
export function queryListAPI(data = {}, options = {}) {
//
}
// 获取...
async function query...API(data = {}, options = {}) {}
// 详情: 通用
export function queryDetailAPI(data = {}, options = {}) {};
// 详情: XXX详情
export function queryXxxDetailAPI(data = {}, options = {}) {};
// 列表: 通用
export function queryListAPI(data = {}, options = {}) {};
// 列表: XXX列表
export function queryXxxListAPI(data = {}, options = {}) {};
// 添加: 通用,列表中的选项
export function addItemAPI(data = {}, options = {}) {};
// 添加: 列表中的XXX选项
export function addXxxItemAPI(data = {}, options = {}) {};
// 修改: 通用,列表中的选项
export function modifyItemAPI(data = {}, options = {}) {};
// 修改: 列表中的XXX选项
export function modifyXxxItemAPI(data = {}, options = {}) {};
// 删除: 通用,列表中的选项
export function deleteItemAPI(data = {}, options = {}) {};
// 删除: 列表中的XXX选项
export function deleteXxxItemAPI(data = {}, options = {}) {};
// 操作...
async function do...API(data = {}, options = {}) {}
// 导出报告
async function exportReportAPI(data = {}, options = {}) {}
config.ts
constant.ts
// 选项数组
export const ACTION_LIST = [
{
label: "不限",
value: "1",
},
{
label: "活动",
value: "2",
}
];
export const ACTION_LABEL = function (value?: string) {
const result = ACTION_LIST.filter(
(item: Record<string, any>) => item.value == String(value)
);
return result && result?.[0] ? result[0].label : "-";
};
interface.ts
index.ts
index.vue: 非表格列表
<template>
<section class="com-frame"></section>
</template>
<script lang="ts" setup>
// 第三方资源类库或插件
import { ref } from 'vue';
import { ElMessage } from 'element-plus';
// 全局资源库
import {} from '@/utils/index';
// 局部资源库
import {} from './api';
import {} from './config';
import {} from './constant';
import {} from './interface';
/**
* @当前业务
* 具体逻辑实现代码
*/
// 初始化依赖数据
async function initDependFunc(data = {}) {
//
};
initDependFunc();
</script>
<!--[可选]: 若不需要则删除-->
<!--全局引入,注意作用域污染问题-->
<style lang="scss">
@import url('./style/index.scss');
</style>
<!--[可选]: 若不需要则删除-->
<!--当前页面作用域-->
<style lang="scss" scoped>
// 引入外部文件,[可选]: 若不需要则删除
@import url('./style/index-scoped.scss');
// 内嵌样式,[可选],不需要则删除
.page-module {
position: relative;
}
</style>
list.vue: 表格列表
Element-plus规范
parent.vue
<template>
<dialog
v-model="dialogConf.visual"
@action:submit="actionSubmitEmit"
@action:confirm="actionConfirmEmit"
@action:cancel="actionCancelEmit"
@action:submit="actionEmit"
@action:confirm="actionEmit"
@action:cancel="actionEmit"
/>
</template>
<script lang="ts" setup>
function actionSubmitEmit() {
//
}
</script>
对话框 dialog.vue
<!--
<dialogCommonUI
v-model="dialogComonConf.visual"
@action:submit="submitBackhaulEvent"
/>
const dialogCommonUI = defineAsyncComponent(() => import('@/components/dialog/common/index.vue'));
-->
<template>
<el-dialog
v-model="initModelValue"
:before-close="closeAction"
:draggable="true"
:close-on-click-modal="false"
:title="initTitle"
width="900px"
class="com-erp-dialog component-dialog-common"
>
<template #footer>
<span class="dialog-footer">
<el-button @click="closeAction">取消</el-button>
<el-button :loading="submitLoading" type="primary" @click="submitAction">确定</el-button>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
// 第三方资源类库或插件
import { ref, reactive, computed } from 'vue';
import { ElMessage, ElDialog, ElButton, ElForm, ElFormItem } from 'element-plus';
import { Edit } from '@element-plus/icons-vue';
import type { FormInstance, FormRules } from 'element-plus';
// 全局资源库
// 局部资源库
/**
* @当前业务
* 具体逻辑实现代码
*/
const props = defineProps({
modelValue: {
type: Boolean,
default() {
return false;
}
},
mode: {
type: String,
default() {
return '';
}
}
});
const emit = defineEmits([
'update:modelValue',
'action:submit',
'action:confirm',
'action:cancel',
'action:init',
"callback"
]);
// 限制重复提交
const submitLoading = ref(false);
const ruleFormRef = ref();
const initValue = computed({
get: () => props.modelValue,
set: (val) => {
emit('update:modelValue', val);
}
});
const initTitle = computed(() => (props.mode === 'add' ? '创建规则' : '编辑规则'));
// 提交表单
async function submitAction() {
ElMessage.closeAll();
if (submitLoading.value) {
return ElMessage.warning('正在提交中,请不要重复提交!');
}
submitLoading.value = true;
// 提交数据
// const { status } = await api();
submitLoading.value = false;
// if (status !== 0) return;
closeAction();
emit('action:submit');
}
function closeAction() {
emit('update:modelValue', false);
}
</script>
<!--全局引入,注意作用域污染问题-->
<style lang="scss">
@import '@/assets/style/dialog.scss';
</style>
<!--当前页面作用域-->
<style lang="scss" scoped>
.component-dialog-common {
}
</style>
父级组件
<template>
<childUI @action:init="initEmit" />
</template>
<script lang="ts" setup>
// 第三方资源类库或插件
// 局部资源库
import childUI from './child.vue';
/**
* @当前业务
* 具体逻辑实现代码
*/
function initEmit() {
//
}
</script>
表单 form.vue
<template>
</template>
<script lang="ts" setup>
// 第三方资源类库或插件
/**
* @当前业务
* 具体逻辑实现代码
*/
const ruleFormRef = ref();
// 验证器
async function formValidater() {
return new Promise((resolve) => {
ruleFormRef.value?.validate((valid: boolean) => {
resolve(valid);
});
});
}
const validater = await formValidater();
if (!validater) return;
const emit = defineEmits([
'update:modalValue',
'update:auxiliary',
'update:subject',
'update:change'
]);
emit('update:change', 'type', { value: '' })
// 子组件回调函数 @action:submit="updateActionEvent"
function updateActionEvent(type = "", data = {}) {
//
}
</script>
<!--当前页面作用域-->
<style lang="scss" scoped>
</style>
React生态技术栈最佳实践
代码风格规范
单元测试规范
Jest
- 官网: https://jestjs.io/
- API文档:https://jestjs.io/docs/api
- Vue Test Utils: https://test-utils.vuejs.org/
Vitest
- Vitest: https://vitest.dev/
目录、文件夹与文件规范
- 全局单元测试用例:即工程一级目录,不建议放在这里。
- 局部单元测试用例:推荐测试用例跟随逻辑代码存放一起。
# 场景一:同一目录下的同级ts或js文件数较多,大于3个
├─ page-module
│ ├─ ——test——
│ │ ├─ a.test.ts
│ │ ├─ b.test.ts
│ │ ├─ c.test.ts
│ │ └─ d.test.ts
│ ├─ a.ts
│ ├─ b.ts
│ ├─ c.ts
│ └─ d.ts
# 场景二:同一目录下的同级ts或js文件数较少,不大于3个
├─ page-module
│ ├─ a.ts
│ ├─ a.test.ts
│ ├─ b.ts
│ └─ b.test.ts
测试用例覆盖规范
node.js规范
文件内容结构
// 第三方资源类库或插件
// 全局资源库
// 局部资源库
/**
* @当前业务
* 具体逻辑实现代码
*/
module.exports = async function () {
console.log("\n");
// console.log(`|----- 一级目录 -----FILE_URL: ${__filename}`);
// 初始化程序
await init();
}
async function init() {
await Example();
await RouterConfig();
}
async function Example() {
//
}
async function RouterConfig() {
//
}
//暴露 module.exports对象
module.exports = {
example
}
//暴露exports对象
exports.world = function () {
console.log('Hello World');
}
async function test() {}
exports.test = test();
const JSON = {
status: 200,
message: '',
data: {},
list: [],
extends: {}
}
API接口结构代码
仅限于ERP系统的后端API接口示例。
async function RouterConfig() {
const suffixPath = "/ttvideo/by/tag/list/";
const routePpath = TT_BASE_PATH + suffixPath;
router.get(routePpath, async (ctx, next) => {
const query = ctx.request.query;
const headers = ctx.request.header;
const resDt = {
status_code: 0,
status_msg: "success",
data: {},
extends: {}
};
ctx.body = resDt;
});
router.post(routePpath, async (ctx, next) => {
const body = ctx.request.body;
const headers = ctx.request.header;
const resDt = {
status_code: 0,
status_msg: "success",
data: {},
extends: {}
};
ctx.body = resDt;
});
}
async function getList(params = {}) {
const { page, limit } = params || {};
const limitVal = limit || 20;
const date = dateUtil();
const uuid = uuidUtil();
const sinogram = generateRandomSinogramUtil(20);
const list = [];
for (let i = 0; i < limitVal; i++) {
const item = {
//
};
list.push(item);
}
return list;
}