某掌厅销户项目
该项目注释较为详细,具体函数,变量均有解释。此文档仅对全局性的问题进行介绍
1.项目目录结构
组件内部,采用如下目录结构放置样式和图片
2.关于项目注意事项,见readme.md
安装
npm i
开发模式
npm run dev
切换环境
npm config set crh-account-close-app:env development (开发)
npm config set crh-account-close-app:env test (测试)
npm config set crh-account-close-app:env production (正式)
切换API也类似,可把env换成api,客户也同理,env换成client,其他也可以根据需求追加配置项
(切换客户暂时只用于换肤系列需求)
财富证券: npm config set crh-account-close-app:client cfzq
中原证券: npm config set crh-account-close-app:client zyzq
正式打包
npm run build (文件打包到dist目录)
每次打包前请确认config set是否设置正确,最好是每次打包前都重新配置命令过一次
开发规则
-
凡是Vue组件一律写到 components中,根据类型分别放置
- modules (功能性为主,如上传组件)
- pages (页面容器,一般为router定义的组件)
- partials (可复用的局部UI容器,如 Footer Header)
-
样式风格,兼容css或stylus。
stylus 风格遵循格式:可以不带分号跟冒号,但必须要有大括号
,举例:
html, body {
font-size 20px
}
-
样式书写规则:
-
所有变量数据统一放入
src/assets/styles/themes/${CLIENT}/var.styl
,client 是对应前面提到的打包设置里的客户id,默认是default,这个文件会自动载入到每个stylus文件头部,确保不用import也可以调用变量数据,凡是可定义皮肤的颜色,按钮大小尺寸,函数方法等都要放置到这个样式内。 -
src/assets/styles/global.styl
这个文件为项目全局生效的样式,往里写样式前请确认样式是需要在全部页面都影响到的,如一些元素的默认样式,且这个样式不会因客户id的改变而发生很大的变化,如是可变的则放入对应的theme/${CLIENT}
目录下。 -
组件样式以及资源(包括页面组件)都单独放在组件目录下的 assets子目录里。如
src/components/pages/home-page/assets
,assets目录里面的结构规则可以参考 src/assets -
功能性组件 (components/modules) 里的样式尽量精简跟独立,尽量不要依赖于全局的样式
-
-
组件开发注意点:
-
页面组件需要用到数据共享的(跨页面的数据状态保持)请使用 Mobx 注意:功能性组件不要使用!会增加依赖,且必须在 src/models 中定义对应的数据模型,如 账号数据可用 account.ts或js,这里建议使用 ts,即便只加了简单的属性类型检查也可以。一些临时的数据可以直接用 Vue 自带的即可。
-
尽量减少使用双向绑定 v-model(单向数据流可以减少一定的bug产出,如果逻辑不复杂可以使用)
-
功能性组件尽量不做全局的事件抛出,转由容器组件代发。
-
组件如果调用方法相对复杂的可以在同层放一个demo目录,里面放使用的简单示例代码。
// 举例,上传功能组件上传完毕后,向外抛出一个 complete 事件:
this.$emit('complete', uploadedFile);
// 容器组件
<Uploader @complete="onUploadSuccess" />
...
import AppObserver from '@lib/...';
onUploadSuccess(uploadedFile) {
// 转发
AppObserver.$emit('UPLOAD_SUCCESS', uploadedFile);
}
// 其他组件接收,处理
AppObserver.$on('UPLOAD_SUCCESS', uploadedFile => ...);
-
配置文件 (src/config)
- 路由: router
- 接口返回的code特殊状态对应的文案(默认接口自带了可被覆盖): apiCode
- 不同客户常用文案: text
- 其他的根据需要可以不断加入
原则:尽量把字段文案变为可配置化,包括一些 input前面的label,即能做到统一也能方便万一要做成多国语言
如果一些配置信息可能频繁的在 Vue template 中渲染的,可以考虑放到 $lib/appGlobal中,这个会自动注入 Vue.prototype.$App 中
-
第三方组件的选用原则,以及自开发的库注意点
- 第三方: 依赖少,用户量大,尽量能在多端使用。如 axios
- 自己开发:自己尽量做全面的自测,能编写测试脚本最佳。
-
接口调用:
- 尽量不要直接在组件里直接调用,而是通过 model 里面写对应的方法来获取
- 接口异常的反馈会有全局的事件抛出,可以单独写捕获组件来做对应处理,不要在具体的业务组件里去每个都单独处理
- 涉及到接口的部分代码注释尽量详细,入参跟输出可以都写上去,发生变化了也要及时更新
-
几个核心库的选定,同类的不要再引入
- 路由: vue-router
- 状态管理: Mobx
- 接口请求: axios
- 其他待定…
-
非外部引入的库文件,命名尽量以驼峰为主
-
格式化工具 prettier设置 vscode 为例:
"prettier.singleQuote": true,
- 方法名定义 优先突出动作,如创建用户 createUser / 发送信息 postMessage
Promise部分尽量可以使用 try { async / await } + catch {} 的方式来代替,减少页面的逻辑嵌套
- 页面的背景色以及标题这类不会干涉到太多业务逻辑的配置可以放入 路由配置的meta里,再统一由根路由组件去调配。
meta: { title: '销户原因', topNavbar: 1, bg: 'white' }
3.接口文档相关
接口文档地址:《财人汇网上销户系统软件》
注意:接口的变量名统一采用下划线命名,本项目做了处理,统一将下划线转为驼峰:
所以,涉及到接口交互,变量名全部用驼峰处理即可
见api.ts
/**
* 批量改驼峰字段
*/
const keyTransf = (
store: any,
obj: any,
direct: 'toCamel' | 'toName' = 'toCamel'
) => {
Object.keys(obj).forEach(key => {
const transFun = direct === 'toCamel' ? nameToCamel : camelToName;
store[transFun(key)] = obj[key];
if (~Object.prototype.toString.call(obj[key]).search('Array')) {
obj[key].forEach((subObj: any, i: number) => {
keyTransf(store[transFun(key)][i], subObj, direct);
});
}
key != transFun(key) && delete store[key];
});
return store;
};
接口返回任何错误,都会用toast
自动展示错误信息,故前端不用特别处理提示信息。
具体见appObserverHandler.ts
this.$refs.toast.info(errorInfo);
对应每种用户的接口地址在api.ts
里,(切换客户暂时只用于整体换肤系列需求)
/**
* 所有客户接口的配置,如果同环境下有多个接口,则数组后面添加
*/
const BaseURLs: any = {
default: {
development: ['http://wuhan.cairenhui.com:43080']
},
cfzq: {
development: ['https://sjwtuf.cfzqtest.com']
},
zyzq:{
development: ['http://ctsjwt.test.cairenhui.com']
}
};
4.流程控制
本项目主要采用流程控制的方法来适配不同券商的不同流程,具体到方法为如下:
// 流程控制,获取跳转的下一个节点
@observable
nextNode: any = {};
firstNodeUrl = '';
submitNode=''
/**
* 流程流转下一步,当且仅当当前路由节点与提交节点一致时才提交审核
* @param {string} nodeId 当前流程节点
* @param {string} currentRoute 当前路由节点
* @returns
* @memberof AccountModel
*/
async getnextNode(nodeId: string,currentRoute:string) {
let realParams = this.wrapperParams();
if(this.submitNode==currentRoute){
await API.request('/CRH-WT1412', {
requestNo: realParams.requestNo,
clientId: realParams.clientId
});
}
const result = await API.request('/CRH-WT1007', {
requestNo: realParams.requestNo,
clientId: realParams.clientId,
nodeId: nodeId
});
if(result.submitFlag=='1'){
this.submitNode=result.nodeUrl
}
runInAction(() => {
this.nextNode = result.nodeUrl;
});
return result;
}
详细来说就是每次路由跳转前都必须发送1007接口,来获取下一个页面的路由节点地址,从而由后端来进行流程控制。本来这个项目之前是由前端通过npm set config client
来控制的,后来发现根本没法适配多个券商。
注意,根据接口文档,只有当当前页面的节点与提交节点对应的时候,才可以调用1412接口进行最后的提交审核任务
5.配置项控制
适配多个券商的个性化需求,就必须用配置项进行控制。在本项目中,根据1006接口返回值,在BaseModel
存入configMap[]
,注意每个配置项取值必须设置默认值
// 客户对应的基础配置信息
@observable
configMap = {};
async getConfigList() {
let result = await API.request('/CRH-WT1006');
let configMap = {};
result.forEach(cfg => {
configMap[cfg.configName] = cfg.configValue;
});
具体页面组件直接引用对应值即可,例如:根据配置项来判断是否需要签署协议
//获取是否需要签署协议
getAgreementFlag() {
this.signAgreementFlag =
BaseModel.configMap['wt.cancel.accountVerify.signAgreementFlag'] || '1'
if (this.signAgreementFlag == 0) {
this.agreementChecked = true
this.isReadAllAgreement = true
}
}
6.与SDK进行对接
具体接口文档见附件
主要是采用jsBridge
进行交互,方法参照sdk.ts
,具体页面组件直接引用方法传值即可。
可见我这篇博客
关于销户系统H5与原生app端使用JSBridge进行交互的总结
/**
*
*
* @export 浙商单向视频
* @param {*} openSingleVideoParams
* @param {*} callback
*/
export function openSingleVideo(openSingleVideoParams, callback) {
setupWebViewJavascriptBridge(function(bridge) {
try {
bridge.registerHandler('finishSVideo', function(data) {
callback(data);
});
bridge.callHandler('openSingleVideo', openSingleVideoParams);
// 注册函数,给原生调用,data为原生端返回的数据
} catch (e) {
console.log(e);
}
});
}
7.建议学习
建议学习Mobx-vue
/vue-class-component
和jsBridge
官方文档,本项目用到的知识全覆盖。