项目交接笔记
# *前端项目交接文档*
# *yapi 管理员账号密码及注册方式*
● 网址:https://yapi.gzhclw.com/
● 账号:admin@admin.com
● 密码:Hcjt2010
● 注册方式
○ 【背景】因yapi 高级模拟功能存在安全漏洞 (opens new window),可获取目标服务器的操作权限,危及系统安全,故目前禁用了自带的注册功能,需通过接口后门完成注册功能
○ 【解决方案】修改 yapi node 服务的代码,在注册接口中,在请求体 body 中添加 fromHC 字段,并设置为 true
○ 【具体操作】在 yapi 页面中,在 console.log 控制台执行以下代码
fetch("https://yapi.gzhclw.com/api/user/reg", {
"headers": {
"accept": "application/json, text/plain, */*",
"accept-language": "zh-CN,zh;q=0.9",
"content-type": "application/json;charset=UTF-8",
"sec-ch-ua": "\" Not A;Brand\";v=\"99\", \"Chromium\";v=\"101\", \"Google Chrome\";v=\"101\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"Windows\"",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin"
},
"referrer": "https://yapi.gzhclw.com/login",
"referrerPolicy": "strict-origin-when-cross-origin",
"body": "{\"email\":\"enside@qq.com\",\"password\":\"123456\",\"username\":\"enside\",\"fromHC\":\"true\"}",
"method": "POST",
"mode": "cors",
"credentials": "omit"
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
○ 注意,上述 fetch 中的请求体 body,修改对应的 email,password,username,设置为具体的注册信息。最后的参数,"fromHC":"true" 即为 yapi 注册接口后面参数
○ 【node 后门参数修改方式】
■ 登录部署 yapi 的 docker 容器中,【特别注意】,登录该 docker,需要选择 bin/sh 这个 shell
■ 找到/api/vendors/server/controllers/user.js文件,找到第 300 行,async reg 注册方法,便可修改对应的逻辑。
# *docker 容器管理器账号密码,yapi 部署在该 docker 容器中*
● 网址:http://159.75.46.33:8081/pc/#/Login
● 账号:admin
● 密码:Hcjt2010
# *cos 静态资源服务器账号密码*
● 网址:https://cloud.tencent.com/login?s_url=https%3A%2F%2Fcloud.tencent.com%2F
● 账号:tech@gzhclw.com
● 密码:hclw@2016
● secretid: AKIDyQctvJM2PaULvPrN8csrscLAjcMrNpb9
● secretkey: SeaR6YkeKnzvmNi5B3h4GMAkKekcEasx
● 当前前端主要用到的 bucket 为 lanxin-1301695016,其中蓝薪管理后台使用到的相应静态资源(如用户协议pdf,系统用到的 Excel 数据模板等),均放在web文件夹中
# *fundebug 系统异常监控*
● 网址:fundebug.com
● 账号:six@hcjt2010.com
● 密码:Hcjt2010!
● 相关配置及 API 参考这里:https://docs.fundebug.com/notifier/javascript/customize/
● 在 vue3 中的使用方式官网中没有,可参考这里:https://www.npmjs.com/package/fundebug-vue
● 常用配置示例
fundebug.init({
apikey: '5a881590f95405c56d4715918e9ac8212a27c2726721f1c5cf0635016702702a',
releasestage: useEnv.appEnv, // 当前运行环境,用于区分开发,测试,预发布,及生成环境
silentDev: true, // 不收集开发环境的错误,当 URL 中含有 localhost 或者 IP 时,判断为开发环境
silentConsole: useEnv.isDevelopment, // 在开发环境,不重写 console 方法,避免 vue warn 信息多次打印,造成开发时卡死
monitorHttpBody: true, // 开启 http 请求 body 数据收集
monitorHttpResponse: true, // 开启 http 返回 body 数据收集
filters: [ // 忽略特定错误,避免无害信息的收集,混淆试听
{ // 因数据量太大,不收集 reference 接口数据
req: {
method: /^GET$/,
url: /global\/reference/
},
// hack 此处存在一个坑,在过滤接口请求时,需要结合 req 及 res 两个参数同时使用,否则过滤效果无效
res: {
status: /^200$/
}
},
{
message: new RegExp(`(${filterMessages.join('|')})`)
},
]
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# *jinkens 账号密码*
● 网址:http://jenkins.hcpai.cn:18080/login?from=%2F
● 管理员账号:hc
● 管理员密码:Hcjt2010!
● 蓝薪只读账号:web
● 蓝薪只读密码:Hcjt2022
● 项目命名规则:
○ blue-dollar-admin-rebuild:对应 dev(开发) 分支
○ blue-dollar-admin-rebuild-test:对应 test(测试) 分支
○ blue-dollar-admin-rebuild-pre:对应 pre(预发布) 分支
○ blue-dollar-admin-rebuild-master:对应 master(生产) 分支
○ 默认所有分支推送后,都将自动构建,进行版本发布。
○ 具体使用及调试方式,可联系@廖芷毅
# *蓝薪及人事 eHR SasS 系统,前端框架整体沿用旧有框架,新增部分如下*
# *新增 npm 包*
● "validator": "^13.7.0":数据校验器,包含较为常用的校验规则,如手机号码,身份证号,邮箱等。
● "utility-types": "^3.10.0",:TypeScript 泛型类库,相当于 JS 的 lodash 库,可以提供便捷的 TypeScript 泛型函数。
● "browser-image-compression": "^2.0.0",:浏览器端图片压缩库,用在图片上传组件中,先在浏览器端进行图片压缩,再上传到服务器。减少图片体积,节省图片上传,下载的流量。
# *项目图片原地压缩*
● 【背景】
○ 前端项目中,需要使用到图片素材,一般情况下,设计提供的图片素材均为原始素材,未经压缩。
○ 直接应用到项目中,会浪费流量,增加图片加载时长,降低用户体验。
○ 故需要在项目开发阶段完成图片的压缩。使用此脚本,通过单个命令行的方式,实现项目中所有图片的原地压缩。
● 【使用方式】
○ 第一步,点击下载源码
○ 第二步,在脚本文件头部添加 tinypng 的 api key,api key 从这个网站获取:https://tinypng.com/developers
global.tinypngConf = {
apiKeyList: [
// 'XgNgkoyWbdIZd8OizINMjX2TpxAd_Gp3', // 无效 key
// 'IAl6s3ekmONUVMEqWZdIp1nV2ItJL1PC', // 无效 key
'IAl6s3ekmONUVMEqWZdIp1nV2ItJLyPC', // 有效 key
]
}
2
3
4
5
6
7
○ 第三步,赋予脚本文件「可执行」权限,chmod +x ./mtp.js
○ 第四步,将脚本文件放置到项目所在目录 运行效果
○ 第五步,在项目所在目录运行脚本node ./mtp.js 运行效果
○ 后续使用,仅需最后两步「第四步」「第五步」
● 【生成的文件说明】
○ 运行完压缩命名后,将在文件夹中,生成两个新文件“tinypngMd5Record.json”、“tinypngReport.json”
○ “tinypngMd5Record.json” 为图片压缩后的 md5 值,再次运行压缩脚本时,对比文件资源的 md5 值,跳过已压缩图片。
○ “tinypngReport.json” 为本次压缩的图片压缩报告,可得知本次执行,压缩过多少文件,每个文件节省了多少空间,进行的压缩比率的信息的记录。
● 【已整合到 package.json 脚本中】
○ 当执行项目启动,或项目打包命令时,都将触发图片压缩的脚本,进行图片压缩的命令。
○ 故图片压缩的过程,只需一次配置,后续无需开发特意触发和维护。
● 【具体使用说明介绍】https://www.npmjs.com/package/tinypng-script-with-cache
# *reference 常量的自动生成与使用*
● 前端常量数据约定
○ 一般情况下,当前端需要对数据进行逻辑判断时,后端通用会有相同的需求,需要进行逻辑判断。
○ 故前后端复用同一套常量值映射表,前端沿用后端的常量值,使用到的常量名,为全局的 reference 接口的 key 字段内容。
○ 常量 key 在 types/reference.d.ts 中定义
● 使用方式
○ 在 types/reference.d.ts 文件中,维护有所有常量值的定义与枚举,以实现编程时的 TS 约束及提示
○ 获取并使用该常量的代码示例如下,具体使用方法,可参考蓝薪项目的self-pay-detail.vue页面中的使用
import { useAppStore } from '@/store'
const { getReferenceConstantMap } = useAppStore() // 提取常量的方法
const BillStatuses = getReferenceConstantMap('billStatuses') // 订单状态常量
console.log(BillStatuses.CANCELED.value) // 获取订单状态为取消的常量,映射在数据库中的值
2
3
4
● 快速更新 reference 常量配置
○ 随着项目功能越来越多,reference 数据量也将增大,手动维护成本过高,且质量不可控。故通过 node 脚本完成 reference 常量的 ts 文件的定义与生成。
○ 脚本文件为项目根文件夹中的 reference-creator.js 文件,运行原理是,通过 node 调用服务器中的 global/reference 接口,获取数据进行解析,通过字符串拼接的方式。生成并覆盖 types/reference.d.ts 文件。
○ 【注意】 reference-creator.js 文件中,写死了 global/reference 接口的域名及完整路径。若生成的配置值当前运行项目时的数据不一致,请检查脚本中的域名,与当前运行项目时的域名是否一致。
# *本地 proxy 接口代理*
● 因测试环境或生成环境的后端接口做了跨域限制,在本地开发时,调用这些接口会产生跨域问题。
● 故在 vite.onfig.ts 添加了 proxy 接口代理配置。vite 的配置文档在这里,https://cn.vitejs.dev/config/server-options.html#server-proxy
● 运行原理,在 node 层做了接口代理的服务,当接口请求,访问本机 ip 时,会被该 node 代理所监听,拦截到。由该 node 代码修改请求的 host 字段,实现同域名的访问。
● 【特别注意】在 eHR 项目中,当前测试环境,后端目前未添加路径的 api 前缀,无法区分接口,故需要使用 proxy 的 rewrite 字段,对请求的路径进行重写。完成接口转接。
# *全局资源路径别名,@,@images*
● 对项目路径别名进行了重写,@指向 src 文件夹,@images 指向 src/assets/images 图片资源文件夹
# *相同路由路径的刷新功能*
● 【背景】
○ 自主发薪详情页,有“复制发薪单”功能,需要新增一条详情数据,并在进入到相同路径下的页面中,
○ 即需要实现“从路径 A,跳转到路径 A 本身”的功能。
○ 直接使用 route.push 无法触发页面的重新加载,无法组件该页面组件的重新渲染,无法重新触发其生命周期。
● 【解决方案】
○ 在src\layouts\components\content.vue 容器文件中,为 router-view 添加 key 标识符,
○ 当需要刷新当前页面时,通过事件机制,修改 key 值,实现组件标识符的变更,实现组件的重新加载。
○ 具体使用,参考 src\views\self-pay\self-pay-detail.vue 文件中的用法
# *index.html 添加发布时间*
● 【背景】
○ 当项目打包出现异常时,无法便捷地判断服务器中的页面资源是否为最新的代码,无法判断是代码编写问题,还是 jenkins 打包异常,未使用到最新的代码
○ 故需要在项目中添加标识符,标记服务器中资源的版本情况,是否为最新代码
● 【解决方案】
○ index.html 中添加项目打包时间,可以通过观察页面 html 代码,快速判断其打包时间,确定是否为最新代码
<title time="<%- updateTime -%>">
<%- title %>
</title>
2
3
4
○ 该时间参数的传入,通过vite.config.ts配置文件中的import { createHtmlPlugin } from 'vite-plugin-html' 插件实现。
// html 处理器
createHtmlPlugin({
minify: true, // 是否压缩
inject: {
// 注入占位符替换值,将 title 替换为具体的值
data: {
title: env.VITE_SITE_NAME,
description: env.VITE_SITE_DESCRIPTION,
updateTime: new Date().toLocaleString(), // 项目打包时间
},
},
}),
2
3
4
5
6
7
8
9
10
11
12
# *小程序 nutui 源码修改同步方案*
● 【背景】
○ nutui 目前客户数量不大,整个 UI 框架本身存在不少 bug,需要我们在开发的过程中,修改其底层源码来填坑
○ 而 nutui 本身是一个 npm 库,我们直接修改 node_modules 中 nutui 库的源码,涉及到代码同步的问题,
○ 如何同步 node_modules 中修改过的源码,让团队其他参与也能用上修复后的 nutui 库代码
● 【解决方案】
○ 使用 “patch-package” 工具,记录依赖包的修改,并实现同步功能
○ 工作原理,patch-package 通过对比项目目标库的代码,及其线上库中的代码,使用类似 git 对比的方式,记录代码的变化,并将变化保存到 patches 文件夹中。当团队其他参与者拉取项目时,执行 npm run update-npm-patch 即可自动同步 node_modules 目标库的源码
○ 记录 npm 依赖包变更:npx patch-package 依赖包名
○ 同步 npm 依赖包变更:npm run update-npm-patch 或 npx patch-package
# *Excel 携带样式的数据导出功能*
● 【背景】
○ 项目中存在部分 Excel,需要对 Excel 单元格进行合并。
○ 而 xlsx 库的免费版,并不提供 Excel 样式操作的功能。
○ 需要使用其 fork 版库 xlsx-style 实现样式操作,xlsx-style: https://www.npmjs.com/package/xlsx-style
○ 而 xlsx-style 的 npm 版本存在 bug,且已无人维护。
○ 通过网上教程:https://blog.csdn.net/weixin_45042868/article/details/124867964,修改其源码,修复部分 bug。可以在开发环境正常运行。
○ 但无法通过 vite 的打包,会出现 Could not dynamically require “zipjs”. Please configure the dynamicRequireTargets or/and ignoreDynamicRequires option of @rollup/plugin-commonjs appropriately for this require call to work. 的错误提示。
○ 目前暂未找到相应解决方案。
● 【应对措施】
○ 不使用 npm 版本,通过在 index.html 文件中,直接引用其 scritp 文件的方式,通过全局引入的方法,来规避打包的问题。
○ 具体代码的使用方式,可参考蓝薪项目的src\views\self-pay\self-pay-record-list.vue 中的代码实现
○ 通过此方法,会在 windows 中挂载 XLS 对象,可直接全局使用。但需要在 eslint 中配置其全局变量,避免 eslint 报错。
# *Excel 复杂表头解析及导入*
● 【背景】
○ 除常规使用一行组织表头的 Excel 文件导入外,现出现使用两行,甚至三行,多行合并单元格的 Excel 表导入。
○ 需要实现通用算法解析任意行合并单元格 Excel 表的导入与解析。
○ 生成 table 表头配置,dataSource 数据,及返回给后端的数据结构。
● 【解决方案】
○ 目前已实现任意层级合并单元格表头的 Excel 表导入与解析
○ 先获取 sheet 每个单元格的内容,再设置表头与后端数据结构间的映射,最后携带 Excel 表头行数,传入解析函数中。
○ 即可得到 table 表头配置,dataSource 数据,及返回给后端的数据结构。
○ 可直接填充到 table 的 columns 和 dataSource 字段中,在页面 table 中复现 Excel 表的合并单元格表头表格的展示效果。
○ 具体使用方式,可参考蓝薪项目的 src\views\worker\worker-employ\components\excel-import.vue 的代码示例
○ 解析后,转换成后端所需的数据结构
○ table 表头数据结构
○ 表格数据
● 【测试数据】
一级菜单 xlsx
// 一级菜单映射
const textKeyMap = {
纳税人姓名: 'project1',
纳税人证件类型: 'project2',
关系: 'project3',
采集来源: 'project4',
更新日期: 'project5',
状态: 'rate6',
}
2
3
4
5
6
7
8
9
二级菜单
// 二级菜单映射
const textKeyMap = {
'*姓名': 'user.realname',
'*手机号': 'user.mobile',
'*身份证号': 'user.idcard',
'*员工状态': 'employee.status',
'*员工类型': 'employee.type',
'*学历': 'employee.education',
'*应聘职位': 'employee.jobName',
'*性别': 'employee.sex',
'*户籍地址': 'employee.nativeAddress',
'*期望薪资(元/月)': 'employee.jobSalary',
'*现居住地址': 'employee.presentAddress',
'*用工单位': 'employee.companyName',
'*身高(cm)': 'employee.height',
'紧急联系人.*联系人姓名': 'employee.emergencyName',
'紧急联系人.*关系': 'employee.emergencyType',
'紧急联系人.*联系人电话': 'employee.emergencyMobile',
'紧急联系人.通讯地址': 'employee.emergencyAddress',
'主要工作经历1.*公司名称': 'workExperience1.companyName',
'主要工作经历1.*开始日期': 'workExperience1.startedAt',
'主要工作经历1.*结束日期': 'workExperience1.endedAt',
'主要工作经历1.*部门': 'workExperience1.departmentName',
'主要工作经历1.*职位': 'workExperience1.jobName',
'主要工作经历1.证明人': 'workExperience1.referenceName',
'主要工作经历1.电话': 'workExperience1.referenceMobile',
'主要工作经历2.公司名称': 'workExperience2.companyName',
'主要工作经历2.开始日期': 'workExperience2.startedAt',
'主要工作经历2.结束日期': 'workExperience2.endedAt',
'主要工作经历2.部门': 'workExperience2.departmentName',
'主要工作经历2.职位': 'workExperience2.jobName',
'主要工作经历2.证明人': 'workExperience2.referenceName',
'主要工作经历2.电话': 'workExperience2.referenceMobile',
'主要工作经历3.公司名称': 'workExperience3.companyName',
'主要工作经历3.开始日期': 'workExperience3.startedAt',
'主要工作经历3.结束日期': 'workExperience3.endedAt',
'主要工作经历3.部门': 'workExperience3.departmentName',
'主要工作经历3.职位': 'workExperience3.jobName',
'主要工作经历3.证明人': 'workExperience3.referenceName',
'主要工作经历3.电话': 'workExperience3.referenceMobile',
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
- 三级菜单
// 三级菜单中文与数据结构的映射
const textKeyMap = {
所得项目: 'project',
'本月(次)情况.收入额计算.收入': 'month.income.income',
'本月(次)情况.收入额计算.费用': 'month.income.fee',
'本月(次)情况.收入额计算.免税收入': 'month.income.freeIncome',
'本月(次)情况.减除费用': 'month.cut',
'本月(次)情况.专项扣除.基本养老保险费': 'month.specialCut.forOld',
'本月(次)情况.专项扣除.基本医疗保险费': 'month.specialCut.forMedical',
减按计税比例: 'rate',
}
2
3
4
5
6
7
8
9
10
11
# Excel 复杂表单复杂表头的导出
- 【背景】
- 与 Excel 复杂导出的需求类似,未来需求可能需要导出合并单元表头的复杂 Excel。
- 人工对齐及维护的 Excel 所在列的成本较高,且容易出错。
- 故实现本功能,通过传入 ‘表头字符串与数据 key 映射表’数组,及原生后端数据,完成 Excel 数据的自动填充,及 Excel 表头单元格的自动合并
● 具体代码示例,可参考src\views\example\component\excel-import.vue 中的示例代码
数据源示例
// 测试数据,需要提前转化好对应的所需格式,枚举值的,则需要提前转义为对应的中文
const dataList = [
{
project: '正常工资薪金',
month2: '4208.20',
month: {
income: {
fee: '11.00',
freeIncome: '12.00'
},
cut: '5000.00',
specialCut: {
forOld: '13.00',
forMedical: '14.00'
}
},
rate: '100%'
},
{
project: '正常工资薪金',
month2: '6342.10',
month: {
income: {
fee: '21.00',
freeIncome: '22.00'
},
cut: '5000.00',
specialCut: {
forOld: '23.00',
forMedical: '24.00'
}
},
rate: '100%'
},
{
project: '正常工资薪金',
month2: '6789.10',
month: {
income: {
fee: '31.00',
freeIncome: '32.00'
},
cut: '5000.00',
specialCut: {
forOld: '33.00',
forMedical: '34.00'
}
},
rate: '100%'
}
]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
映射表示例
// // 一级菜单中文与数据结构的映射
const textKeyMaps:Recordable[] = [
{ 所得项目: 'project' },
{ 收入: 'month2' },
{ 费用: 'month.income.fee' },
{ 免税收入: 'month.income.freeIncome' },
{ 减除费用: 'month.cut' },
{ 基本养老保险费: 'month.specialCut.forOld' },
{ 基本医疗保险费: 'month.specialCut.forMedical' },
{ 减按计税比例: 'rate' },
]
2
3
4
5
6
7
8
9
10
11
函数使用示例
const {
headerMerges, // 表头合并单元格配置数据
cells // 包括表头与表格数据的,所有单元格信息
} = transformDataToSheetCells(dataList, textKeyMaps)
const worksheet = utils.aoa_to_sheet(cells)
worksheet['!merges'] = headerMerges
// 所有单元格居中显示
Object.values(worksheet).forEach(cell => {
if (cell.v) {
cell.s = {
alignment: {
horizontal: 'center',
vertical: 'center',
},
}
}
})
toStyleXlsx({
filename: 'filename.xlsx',
worksheet
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 解析时间差异
- 日期 解析格式 5/17/21 (月/日/年)
r.attaches = headersTmp.map(headKey => {
item[headKey] = typeof item[headKey] == 'string' && item[headKey]?.trim()
// 正则匹配日期格式,转换
if (/^[0-9]{1,2}\/[0-9]{1,2}(\/[0-9]{1,2})?$/.test(item[headKey])) {
console.log(item[headKey], 'item[headKey]')
item[headKey] = dayjs(date).format(item[headKey])
}
return item[headKey]
})
2
3
4
5
6
7
8
9
# input 图片浏览器端压缩
- 使用 browser-image-compression 包实现 file 图片文件的压缩功能,已整合到 upload 组件中
- 当用户上传图片时,会先在浏览器端进行图片压缩,再将压缩都的图片上传到服务器。
- 减少图片体积,节省图片上传,浏览的流量。
# 文件及文件夹命名规范
- 文件及文件夹均采用小写方式,以中划线分隔。
- 例:my-project-name
# 统一 IDE 使用方式
- 【背景】
- 统一 IDE 使用方式,在进行文件保存、代码粘贴等操作时,自动执行 vscode 的 eslint 插件,将代码以 eslint 的规则进行修复。
- 【实现方式】
- 将 .vscode 文件夹添加到 git 仓库中,.vscode/.settings 文件即为 vscode IDE 的配置文件。
- 各配置项详细的备注信息,可直接在 .vscode/.settings 文件中查看。