共计 22492 个字符,预计需要花费 57 分钟才能阅读完成。
对于gh-framework
gh-framework
旨在解决 vue2
环境下 (思维可用于任何框架我的项目,不局限于 vue2) 的前端工程化问题,它将封装 vue
我的项目中罕用的工具库和配置文件并将其可移植化,例如axios
、constants(常量)
、directives(指令)
、services(数据申请层)
、config(配置文件)
、mixin(全局混入)
、utils(工具集)
、context(上下文)
。本计划自己已在 5 + 我的项目上利用,包含一个大型前端我的项目。
github 地址(示例代码):
https://github.com/cong1223/gh-framework
个性
- 高度封装:高度封装我的项目常用工具和配置,不写反复代码。
- 疾速移植化:封装一次,其余类型我的项目可复制粘贴,疾速聚拢反复代码,依据业务需要小局部批改即可,如果后端返回数据格式统一,那么
services
都不须要批改即可利用。 - 不具备破坏性:新我的项目如果想尝试这套解决方案,那么能够移植次计划,并且对你原先的我的项目不具备破坏性,能够同时兼容。
- 疾速开发体验:一次封装,永恒劳碌,辞别繁琐的导入 / 导出,
this
. 万物。
适用人群
- 对前端工程化具备强烈学习趣味的初中级前端程序员;
- 前端我的项目中表演 captain 角色的程序员;
- 疾速交付型守业程序员;
- 兼职接单程序员;
我的项目构造介绍
疏忽了 vue 我的项目根本我的项目构造文件
|-- node_mudules
|-- public
|-- src
|-- assets // 动态资源文件夹
|-- config // 配置文件文件夹
|-- const // 常量配置文件夹
|-- framework // gh-framework 文件夹
|-- directives // 全局指令文件夹
|-- mixin // 全局 mixin 混入文件夹
|-- plugins // framework 外围工具集的配置入口
|-- utils // 全局工具集文件夹
|-- ui // 全局通用 ui 组件文件夹
|-- config.js // 文件名映射配置文件(重要)
|-- index.js // 导出为 vue 能装置的 framework 插件(封装为 install 函数)|-- services // 数据申请层文件夹
|-- .browserslistrc
|-- .eslintrc.js
|-- .gitignore
|-- babel.config.js
|-- package.json
|-- README.md
|-- yarn.lock
framework
framework
文件夹就是 gh-framework
的核心思想所在,高度聚拢我的项目公共代码,应用变得能够疾速移植化,一个我的项目做完了,能够立马复制 framework
文件夹到另外一个新的我的项目,惟一不同的就是业务代码局部。此文件夹能够依据本人的业务需要持续扩大其余通用逻辑,封装办法参考 directives
、utils
等。
directives
全局指令封装集文件夹,将我的项目中罕用指令对立治理,全局装置。
文件夹我的项目构造如下:
|-- directives
|-- debounce.js // 避免按钮在短时间内被屡次点击,应用防抖函数限度规定工夫内只能点击一次
|-- loadmore.js // element-ui 下拉框下拉更多
|-- draggable.js // 实现一个拖拽指令,可在页面可视区域任意拖拽元素
|-- copy.js // 复制粘贴指令
|-- emoji.js // 不能输出表情和特殊字符,只能输出数字或字母等
|-- lazyload.js // 实现一个图片懒加载指令,只加载浏览器可见区域的图片
|-- longpress.js // 实现长按,用户须要按下并按住按钮几秒钟,触发相应的事件
|-- permission.js // 权限指令,对须要权限判断的 Dom 进行显示暗藏
|-- watermark.js // 给整个页面增加背景水印
|-- // 更多其余指令...
|-- index.js // 对立进口
以上更多通用指令请在示例我的项目中查看获取
debounce.js
// 避免按钮在短时间内被屡次点击,应用防抖函数限度规定工夫内只能点击一次
const debounce = {inserted: function (el, binding) {
let timer
el.addEventListener('keyup', () => {if (timer) {clearTimeout(timer)
}
timer = setTimeout(() => {binding.value()
}, 1000)
})
}
}
export default debounce
loadmore.js
// element-ui 下拉框下拉更多
const loadmore = {bind (el, binding) {
// 获取 element-ui 定义好的 scroll 盒子
const SELECTWRAP_DOM = el.querySelector('.el-select-dropdown .el-select-dropdown__wrap')
SELECTWRAP_DOM && SELECTWRAP_DOM.addEventListener('scroll', function () {
const CONDITION = this.scrollHeight - this.scrollTop <= this.clientHeight
if (CONDITION) {binding.value()
}
})
}
}
export default loadmore
index.js
import loadmore from './loadmore'
import debounce from './debounce'
// 自定义指令
const directives = {
loadmore,
debounce
}
export default {install(Vue) {Object.keys(directives).forEach((key) => {Vue.directive(key, directives[key])
})
}
}
mixin
全局混入,一些全局的办法和属性能够定义在此文件中,比方路由跳转,封装后能够更灵便的解决门路和参数问题,比方全局的 toast、二次弹窗等等。
文件夹我的项目构造如下:
|-- mixin
|-- index.js
index.js
export default {data() {return {};
},
computed: {},
created() {},
mounted() {},
methods: {
// 二次弹窗, 命令形式调用
$confirmBox(title, message, handleConfirm, handleCancel, options) {
const {
confirmButtonText = "确认",
showCancelButton = true,
// eslint-disable-next-line no-unused-vars
confirmButtonType = "primary"
} = options || {};
return this.$messageBox({
title,
message,
showCancelButton, // 依据业务需要扩大
confirmButtonText, // 依据业务需要扩大
customClass: "zk-confirm-box",
closeOnClickModal: false,
confirmValidate: (action, component, done, instance) => {
// component : 自定义传入的 component 的组件实例
if (action === "cancel") {if (handleCancel) {handleCancel();
}
return done();} else {instance.confirmButtonLoading = true;}
handleConfirm(done, instance);
}
}).catch(() => {});
},
// 可扩大自定义弹窗内容, 给 components 传自定义组件即可
$messageBox(
{
component = null,
componentName = "",
confirmData = {},
confirmValidate = () => {},
...rest
}
) {
const h = this.$createElement;
return new Promise((resolve, reject) => {
this.$msgbox({
message: h(component, {props: { confirmData}
}),
beforeClose: (action, instance, done) => {
const cptInstance = instance.$children.find(child => {return child.$options.name === componentName;});
confirmValidate(action, cptInstance, done, instance);
},
...rest
})
.then(resolve)
.catch(reject);
});
},
// 批改告诉的默认工夫
$toast(type, msg, duration, callback) {this.$message({ type: type, message: msg, duration: duration || 1500, onClose: callback});
},
getParams(type = "params", key) {const params = this.$route[type];
if (Object.keys(params).length) {if (key) {return params[key];
} else {return params;}
} else {return null;}
},
goBack() {this.$router.go(-1);
},
goto(name, params = {}, isReplace) {params = params || {};
return new Promise((resolve) => {if (name) {if (name.indexOf("/") >= 0) {if (isReplace) {
this.$router.replace({path: name, params}, () => {resolve && resolve();
});
} else {
this.$router.push({path: name, params}, () => {resolve && resolve();
});
}
} else {if (isReplace) {
this.$router.replace({name, params}, () => {resolve && resolve();
});
} else {
this.$router.push({name, params}, () => {resolve && resolve();
});
}
}
}
});
}
}
};
plugins
此文件夹对立收揽了所有 framework 治理下的 ” 插件 ”,那些咱们封装好了的工具集咱们称为 framework 的插件集。
文件夹我的项目构造如下:
|-- plugins
|-- axios.js // 封装后的 axios 插件
|-- config.js // 配置文件插件
|-- const.js // 常量定义插件
|-- request.js // axios 的下层申请封装插件
|-- service.js // 接口申请,数据处理层插件
|-- storage.js // localStorage 插件
|-- utils.js // 自定义封装工具集插件
axios.js
// 依据本人理论业务需要配置,此配置文件仅供参考
import axios from 'axios'
import Cookies from 'js-cookie'
import {
Message,
MessageBox
} from 'element-ui'
const service = axios.create({
timeout: 6000,
headers: {
'X-User-Agent': 'boss',
'X-Ent': '0'
}
})
service.interceptors.request.use(config => {if (!config.url.includes('hzzk-portal/sys/login') && Cookies.get('pro__Access-Token')) {config.headers['X-Access-Token'] = Cookies.get('pro__Access-Token');
}
return config;
}, err => {return Promise.reject(err);
})
// http response 拦截器
service.interceptors.response.use(
response => {const { data} = response;
if (data.code === 200) {return Promise.resolve(data);
} else if (/500/.test(data.code)) {if (data.code === 50002) {Cookies.remove('pro__Access-Token');
Message({
message: data.message,
type: 'error',
duration: 2 * 1000,
onClose: () => {location.replace('/');
}
})
} else {
Message({
message: data.message,
type: 'error',
duration: 2 * 1000
})
return Promise.reject(data)
}
} else if (/400/.test(data.code)) {if (data.code === 40009) {
// 权限有余
Message({
message: data.message,
type: 'error',
duration: 2 * 1000
})
} else {
Message({
message: data.message,
type: 'error',
duration: 2 * 1000
})
return Promise.reject(data)
}
} else if (/300/.test(data.code)) {
Message({
message: data.message,
type: 'info',
duration: 2 * 1000
})
return Promise.reject(data)
} else {return Promise.reject(new Error(data.message || 'Error'))
}
},
error => {
// 判断申请异样信息中是否含有超时 timeout 字符串
if (error.message.includes('timeout')) {
Message({
message: '申请超时',
type: 'error'
})
}
// token 生效重定向至登陆页
if (error.response.data.status === 500 && error.response.data.message === 'Token 生效,请从新登录') {if (error.response.data.message === "Token 生效,请从新登录") {
MessageBox.alert('Token 生效,请从新登录', '提醒', {
confirmButtonText: '从新登录',
callback: () => {Cookies.remove('pro__Access-Token');
location.replace('/')
}
});
}
} else if (error.response.status === 500) {
// 间接捕捉 http 申请的错误码,而不是后端的返回体里的错误码
Message({
message: '服务器异样',
type: 'error',
duration: 2 * 1000
})
} else {
Message({
message: error.message,
type: 'error',
duration: 2 * 1000
})
}
return Promise.reject(error)
},
)
export default service
config.js
import config from '../config'
let constant = {};
for (let i in config.config) {let file = config.config[i];
constant[i] = require('../../config/' + file).default; // 具体门路依据你理论我的项目中 config 所在门路配置,这里配置实用于我以后我的项目所配置的文件夹门路
}
export default constant;
const.js
import config from '../config'
const consts = {};
for (const i in config.const) {const fileName = config.const[i];
consts[i] = require('../../const/' + fileName).default; // 具体门路依据你理论我的项目中 const 所在门路配置
}
export default consts;
context.js
import storage from './storage'
export default {
// 获取用户
user() {return storage.getItem('userInfo') || {}},
// 设置用户
setUser(user) {storage.setItem('userInfo', user)
},
curEnterpriseId() {return this.user().curEnterpriseId;
}
}
request.js
// url 配置依据本人理论我的项目进行配置
import config from './config';
import axios from './axios';
const URI = config.uri;
export default {
// 污浊的的 get
getRequest(url, params = {}, base = 'BASE_URL') {return new Promise((resolve, reject) => {axios.get(URI[base] + url, {params: params}).then(res => {resolve(res);
}).catch(err => {reject(err)
})
})
},
postRequest(url, params = {}, base = 'BASE_URL') {return new Promise((resolve, reject) => {axios.post(URI[base] + url, params)
.then(res => {resolve(res);
})
.catch(err => {reject(err)
})
});
},
arcApi(url, params = {}, method = 'get', base = 'HZZK_ARC_API') {if (method === 'post') {return this.postRequest(url, params, base);
} else {return this.getRequest(url, params, base);
}
},
bossApi(url, params = {}, method = 'get', base = 'BASE_URL') {if (method === 'post') {return this.postRequest('/boss' + url, params, base);
} else {return this.getRequest('/boss' + url, params, base);
}
},
sysApi(url, params = {}, method = 'get', base = 'BASE_URL') {if (method === 'post') {return this.postRequest('/sys' + url, params, base);
} else {return this.getRequest('/sys' + url, params, base);
}
}
}
services.js
import config from '../config'
const service = {};
for (const i in config.service) {const fileName = config.service[i];
Object.defineProperty(service, i, {get() {return Reflect.construct(require('../../services/' + fileName).default, []); // 实例化类
}
});
}
export default service;
storage.js
export default {getItem(k) {const jsonStr = window.localStorage.getItem(k.toString());
return jsonStr ? JSON.parse(jsonStr) : null;
},
setItem(k, value) {value = JSON.stringify(value);
try {window.localStorage.setItem(k, value);
} catch (e) {this.removeItem(k);
}
},
removeItem(k) {window.localStorage.removeItem(k);
},
clear() {window.localStorage.clear();
},
key(index) {return window.localStorage.key(index);
},
getItemByIndex(index) {
const item = {
keyName: '',
keyValue: ''
};
item.keyName = this.key(index);
item.keyValue = this.getItem(item.keyName);
return item;
}
}
utils.js
import config from '../config'
const utils = {};
for (const i in config.utils) {const fileName = config.utils[i];
utils[i] = require('../utils/' + fileName).default; // 具体门路依据你理论我的项目中 const 所在门路配置
}
export default utils;
utils
封装的公共工具集
文件夹我的项目构造如下:
|-- utils
|-- array.js
|-- index.js
|-- // 更多自定义封装的工具...
array.js
export default {
/**
* 依据数组中对象的某个属性值进行去重
* @param arr: 须要去重的数组
* @param key: 依据对象中的哪个属性进行去重
* @returns {*}
*/
unique(arr, key) {const res = new Map();
return arr.filter((a) => !res.has(a[key]) && res.set(a[key], 1))
}
// ===== 更多工具函数 =====
}
index.js
import array from './array'
export {
array,
// more util func
}
ui
全局专用 ui 组件
文件夹我的项目构造如下:
|-- ui
|-- components // 搁置组件文件夹
|-- scroll-view // 组件名称
|-- index.vue // 以后组件入口文件
|-- index.js // 对立组件进口
scroll-view/index.vue
<!-- 滚动加载组件 -->
<template>
<div id="scroll-view">
<slot></slot>
<p v-if="loading"> 加载中...</p>
<p v-if="noMore"> 没有更多了 </p>
</div>
</template>
<script>
export default {
props: {
// 列表的总页数
pages: {type: [String, Number],
default: 0
}
},
data() {
return {
page: 1,
currLength: 0, // 以后列表长度
loading: false
}
},
computed: {noMore () {return this.currLength >= this.total;}
},
mounted() {const ScrollView = document.querySelector('#scroll-view');
ScrollView.addEventListener("scroll", (event) => {
const scrollDistance =
event.target.scrollHeight -
event.target.offsetHeight -
event.target.scrollTop;
if (this.loading) return;
if (this.page < this.pages && scrollDistance <= 0) {
this.loading = true;
this.$emit('load', ++this.page, () => {this.loading = false;})
}
});
},
methods: {refresh() {
this.page = 1;
this.$emit('load', this.page, () => {this.loading = false;});
}
}
}
</script>
<style scoped lang="scss">
#scroll-view {
width: 100%;
height: 100%;
overflow: auto;
p {
line-height: 1.5em;
font-size: 14px;
color: #5e6d82;
text-align: center;
padding: 16px 0;
}
&::-webkit-scrollbar {display:none}
}
</style>
components/index.js
import ZkScrollView from './zk-scroll-view'
export {
ZkScrollView,
// more components
}
config.js
所有 framework 下的插件文件名的映射配置文件,这里配置的映射名能够应用 this
拜访,辞别繁琐的import
export default {
const: {
account: 'AccountConstants', // 通过 this.const.account 拜访到 AccountConstants.js 文件
// more constants file map
},
service: {
user: 'UserService', // 通过 this.service.user 拜访到 UserService.js 文件
enterprise: 'EnterpriseService', // 通过 this.service.enterprise 拜访到 EnterpriseService.js 文件
// more service file map
},
utils: {
array: 'array', // 通过 this.service.user 拜访到 UserService.js 文件
// more utils file map
},
config: {
uri: 'uri',// 通过 this.config.uri 拜访到 config/uri.js 文件
// more config file map
}
};
index.js
封装 framework 的 install 办法,以供 vue 在 main.js 中装置它,并且挂载文件到 Vue.prototype
,以便全局通过this
拜访到 framework 下的私有插件。
import storage from './plugins/storage.js';
import Const from './plugins/const';
import service from './plugins/service';
import config from './plugins/config';
import mixin from './mixin';
import utils from './plugins/utils';
import context from './plugins/context';
import {ZkScrollView} from './zk-ui/components';
import Directives from './directives'
export default {install(Vue, option) {
Vue.prototype.storage = storage;
Vue.prototype.config = config;
Vue.prototype.context = context;
Vue.prototype.const = Const;
Vue.prototype.$const = Const; // 标签中不能应用 const 关键字, 而 js 中拜访的是 this 作用域下的 const 字段
Vue.prototype.service = service;
Vue.prototype.utils = utils;
Vue.mixin(mixin);
Vue.use(Directives);
Vue.component('zk-scroll-view', ZkScrollView);
}
}
services
把 services
、const
、config
独自提到 src
下也依据理论状况而定, 思考到这三个文件夹跟理论业务非亲非故,所以独自提出来,只有在 framework 下配置好理论门路关联起来就 ok。
前端人肯定要摒弃的陋习,vue
文件中大量操作 model
, 甚至在template
中写大量的数据处理逻辑来渲染相干数据,这会让你的我的项目变得越来越简单且不可保护,并且会生成更多的冗余代码。
vue 我的项目四点忠告:
- template 中尽量不要写表达式,巧用 computed
- view 与 model 拆散,vue 文件尽量写 ui 相干代码,数据处理(后端无奈解决的数据须要前端解决的)在 service 中解决
- 常量日常化,尽量不要呈现魔法变量,这会让我的项目变得越来越不可保护
- 文件夹层级明显
文件夹我的项目构造如下:
|-- services
|-- abstract
|-- BaseService.js // service 文件中不能通过 this 获取 framework 下的插件,这里对立在基类外面援用,使其在 service 文件中也能通过 this 获取插件。|-- UserService.js
|-- EnterpriseService.js
|-- // 更多业务 Service
BaseService.js
import request from "../../framework/plugins/request";
import storage from "../../framework/plugins/storage";
import service from "../../framework/plugins/service";
import utils from "../../framework/plugins/utils";
import config from "../../framework/plugins/config";
import Const from "../../framework/plugins/const";
import context from "../../framework/plugins/context";
import dayjs from "dayjs";
export default class BaseService {constructor() {
this.request = request;
this.storage = storage;
this.service = service;
this.utils = utils;
this.$dayjs = dayjs;
this.const = Const;
this.config = config;
this.context = context;
}
/**
* 刷选出接口返回的有用数据(data), 异样捕捉解决
* @param promise
* @param isTotal, 是否返回全副的 json 数据
* @returns {Promise<T>}
*/
output(promise, isTotal = false) {return new Promise((resolve, reject) => {promise.then((resp) => {if (!resp) {reject();
} else {if (resp.success) {if (resp.code === 200) {
resp = resp.result;
if (isTotal) {resolve(resp);
} else {resolve((resp && resp.list) || (resp && resp.data) || resp);
}
} else {reject(resp);
}
} else {reject(resp);
}
}
}, (resp) => {reject(resp);
});
});
}
举个例子,编写业务 service
UserService.js
import BaseService from "./abstract/BaseService";
/**
* 用户治理
*/
export default class UserService extends BaseService {
// eslint-disable-next-line no-useless-constructor
constructor() {super()
}
/**
* 登录
* @returns {Promise<T>}
*/
login(username, password) {
const params = {
username,
password
};
return super.output(this.request.postRequest('/sys/login', params), true);
}
/**
* 通用短信验证码
* @returns {Promise<T>}
*/
getSmsCode(params) {return super.output(this.request.postRequest('/sys/sms', params), true);
}
/**
* 重置明码
* @returns {Promise<T>}
*/
resetPassword(params) {return super.output(this.request.postRequest('/sys/user/findBackPassword', params), true);
}
}
EnterpriseService.js
import BaseService from "./abstract/BaseService";
/**
* 组织架构治理
*/
export default class EnterpriseService extends BaseService {
// eslint-disable-next-line no-useless-constructor
constructor() {super()
}
/**
* 获取企业成员
* @param enterpriseId
* @param page
* @param pageSize
* @param option
* @param realName: 成员名字, 搜寻用
* @returns {Promise<T>}
*/
async getEnterpriseUserList(enterpriseId, realName, page = 1, pageSize = 50) {
let params = {
enterpriseId,
option: 0,
realName,
page,
pageSize
};
params = this.utils.obj.deleteEmptyProperty(params);
const result = await super.output(this.request.getRequest('/structure/queryUser', params), true);
// 数据逻辑解决在 service 中解决后返回给 View 页面应用
if (result && result.list && result.list.user) {
result.list.user.forEach(item => {if (item.createTime) {item.createTime = this.$dayjs(item.createTime).format("YYYY-MM-DD HH:mm:ss")
}
item.statusText = this.const.account.UserStatus.getCnameByValue(item.status);
})
}
return result;
}
}
const
常量定义文件所在目录
文件夹我的项目构造如下:
|-- const
|-- abstract
|-- BaseConstant.js // 封装常量类工具
|-- TemplateConstants.js
|-- // more constants files
BaseConstant.js
/**
* 枚举常量根底类
* @Author 王聪
* @cdate 2018-01-20 14:35
*/
export default class BaseConstant {constructor(name, value, cname, desc) {
this._name = name;
this._value = value;
this._cname = cname;
this._desc = desc;
}
name() {return this._name;}
cname() {return this._cname;}
value() {return this._value;}
numberValue() {return parseInt(this.value())
}
desc() {return this._desc;}
/**
* 取得所有常量的 map
* @returns {{}}
*/
static getEnumMap() {const prototypeMap = Object.getOwnPropertyDescriptors(this);
const enumMap = {};
for (const prototypeName in prototypeMap) {const val = prototypeMap[prototypeName].value;
if ((val instanceof BaseConstant) && val._name) {enumMap[val._name] = val;
}
}
return enumMap;
}
/**
* 取得所有常量的数组
* @returns {Array}
*/
static getEnumList() {const prototypeMap = Object.getOwnPropertyDescriptors(this);
const enumList = [];
for (const prototypeName in prototypeMap) {const val = prototypeMap[prototypeName].value;
if ((val instanceof BaseConstant) && val._name) {enumList.push(val);
}
}
return enumList;
}
static getValueByName(name) {const enumMap = this.getEnumMap();
const _enum = enumMap[name];
return _enum ? _enum.value() : null;}
static getNameByValue(value) {const enumList = this.getEnumList();
let name = null;
enumList.find((_enum) => {if (_enum.value() == value) {name = _enum.name();
return true;
}
});
return name;
}
static getCnameByName(name) {const enumMap = this.getEnumMap();
const _enum = enumMap[name];
return _enum ? _enum.cname() : null;}
static getCnameByValue(value) {const enumList = this.getEnumList();
let cname = null;
enumList.find((_enum) => {if (_enum.value() === value) {cname = _enum.cname();
return true;
}
});
return cname;
}
static getCnameByBitValue(value) {const enumList = this.getEnumList();
const cnameArr = [];
enumList.forEach((_enum) => {if ((value & _enum.value()) !== 0) {cnameArr.push(_enum.cname());
}
});
return cnameArr.join(',');
}
/**
* 给饿了么的 select 组件用
* name 为组件的 value
* cname 为组件的 label
* @returns {*}
*/
static getSelectOptionsByCnameAndName() {const enumList = this.getEnumList();
const options = [];
enumList.forEach((_enum) => {
options.push({value: _enum.name(),
label: _enum.cname()});
});
return options;
}
static getSelectOptionsByCnameAndNameWithAll(option = { label: '全副', value: ''}) {const options = this.getSelectOptionsByCnameAndName()
option = !option ? {label: '全副', value: ''} : option
options.unshift(option)
return options;
}
/**
* 给饿了么的 select 组件用
* value 为组件的 value
* cname 为组件的 label
* @returns {*}
*/
static getSelectOptionsByCnameAndValue() {const enumList = this.getEnumList();
const options = [];
enumList.forEach((_enum) => {
options.push({value: _enum.value(),
label: _enum.cname()});
});
return options;
}
static getSelectOptionsByCnameAndValueWithAll(option = { label: '全副', value: ''}) {const options = this.getSelectOptionsByCnameAndValue()
option = !option ? {label: '全副', value: ''} : option
options.unshift(option)
return options;
}
/**
* 查问按位与的位数的值是否在值内
* @param value
* @return boolean
*/
isAttrIn(value) {if (value == null) {return false;}
value = parseInt(value)
return (value & this.numberValue()) == this.numberValue();}
/**
* 原属性中增加属性
* @param attr
* @return
*/
addAttr(attr) {return this.numberValue() | (!attr ? 0 : attr);
}
/**
* 原属性中去掉属性
* @param attr
* @return
*/
removeAttr(attr) {if (!attr || attr <= 0) {//logger.debug("原属性值 attribute="+attr+", 不须要 remove");
return 0;
}
return ~this.numberValue() & attr;}
/**
* 获取属性地位,相当于 log2 + 1
* @return
*/
getAttrPos() {return Math.log(this.numberValue()) / Math.log(2) + 1;
}
}
举例常量类的编写: TemplateConstants.js
import BaseConstant from './abstract/BaseConstant'
export default class TemplateConstants {
/**
* 模板类型
*/
static TemplateType = class TemplateType extends BaseConstant {static SYS_ENG_PROJECT = new BaseConstant("工程项目", '4', '系统工程模板');
}
/**
* 模板角色权限
* @type {TemplateConstants.Role}
*/
static PerRole = class PerRole extends BaseConstant {static MANAGER = new BaseConstant("MANAGER", '1', '职责管理员');
static NORMAL = new BaseConstant("NORMAL", '2', '职责一般人员');
static ADMIN = new BaseConstant("ADMIN", '0', '所有');
}
/**
* 文件夹权限
* @type {TemplateConstants.Role}
*/
static PerFolder = class PerFolder extends BaseConstant {static READ = new BaseConstant("READ", '0', '只读');
static DOWNLOAD = new BaseConstant("DOWNLOAD", '2', '下载');
static WRITE = new BaseConstant("WRITE", '3', '编辑');
static HEAD = new BaseConstant("HEAD", '4', '负责人');
}
}
config
我的项目配置文件目录
文件夹我的项目构造如下:
|-- config
|-- uri.js
uri.js
let BASE_URL = ''let HZZK_FT_API =''
let HZZK_ARC_API = ''let HZZK_OCR_API =''
let PERMISSION_API = ''
switch (process.env.NODE_ENV) {
case 'development':
PERMISSION_API = 'https://test.xxx.com'
BASE_URL = 'https://test.xxx.com/hzzk-portal'
HZZK_FT_API = 'https://test.xxx.com/hzzk-ft'
HZZK_ARC_API = 'https://test.xxx.com/hzzk-arc'
HZZK_OCR_API = 'https://test.xxx.com/hzzk-ocr'
break
case 'test':
PERMISSION_API = 'https://test.xxx.com'
BASE_URL = 'https://test.xxx.com/hzzk-portal'
HZZK_FT_API = 'https://test.xxx.com/hzzk-ft'
HZZK_ARC_API = 'https://test.xxx.com/hzzk-arc'
HZZK_OCR_API = 'https://test.xxx.com/hzzk-ocr'
break
case 'production':
PERMISSION_API = 'https://hzzk.xxx.com'
BASE_URL = 'https://hzzk.xxx.com/hzzk-portal'
HZZK_FT_API = 'https://hzzk.xxx.com/hzzk-ft'
HZZK_ARC_API = 'https://hzzk.xxx.com/hzzk-arc'
HZZK_OCR_API = 'https://hzzk.xxx.com/hzzk-ocr'
break
case 'local':
PERMISSION_API = 'http://192.168.0.108:20001'
BASE_URL = 'http://192.168.0.108:20001/hzzk-portal'
HZZK_FT_API = 'http://192.168.0.108:20001/hzzk-ft'
HZZK_ARC_API = 'http://192.168.0.108:20001/hzzk-arc'
HZZK_OCR_API = 'http://192.168.0.108:20001/hzzk-ocr'
break
}
export default {BASE_URL, HZZK_FT_API, HZZK_ARC_API, HZZK_OCR_API, PERMISSION_API}
装置
关上 vue
入口文件,例如main.js
:
import GhFramework from './framework';
Vue.use(Framework);
这样,就能齐全利用以上配置所有插件。
利用
举例说完了 framework 的一个整体框架和各个插件的独立配置外,以下用代码片段展现在我的项目中如何利用这些插件。
- 自定义指令(directives)
<el-button type="primary" v-copy="content"> 点击复制 </el-button>
-
全局混入中局部办法的利用
// 跳转首页 this.goto('/'); // 返回上一页 this.goBack(); // showToast this.$toast("success", "调用胜利!"); // $confirmBox this.$confirmBox("勾销下载", ` 确定要勾销该下载工作吗?`, (done, instance) => {setTimeout(() => {done(); instance.confirmButtonLoading = false; }, 1500); });
-
应用全局公共工具集
uniqueArr() {const arr = [{ name: "王", age: 2}, {name: "叶", age: 4}, {name: "张", age: 2}]; console.log(this.utils.array.unique(arr, "age")) // [{name: "王", age: 2}, {name: "叶", age: 4}] }, deleteEmptyProperty() { const params = { name: '小聪忙', age: 24, address: '', job: undefined, phone: null }; console.log(this.utils.obj.deleteEmptyProperty(params)) //{name: '小聪忙',age: 24} }
-
应用全局 ui 组件
<template> <scroll-view style="height: 300px; background-color: #d0e5f2" :pages="pages"@load="load"> <div v-for="(num, index) in list" :key="index"> {{num}} </div> </scroll-view> </template> export default {data() { return { pages: 3, // 总页数 list: []}; }, methods: {load(page = 1, next) {setTimeout(() => {if (page === 1) {this.list = Array.from({ length: 100}, (v, k) => k); } else {this.list.push(...Array.from({ length: 100}, (v, k) => k)); } next && next();}, 1000); } } }
-
应用全局配置文件
// 获取 uri 配置文件中的 BASE_URL const baseUrl = this.config.uri.BASE_URL;
-
应用全局常量
// 获取一般人员角色 this.const.template.PerRole.NORMAL.value() // "2" this.const.template.PerRole.NORMAL.name() // "NORMAL" // 获取文件夹权限汇合,实用于 element-ui 的 el-select 组件 const perOptions = this.const.template.PerRole.getSelectOptionsByCnameAndValue(); // [{label: "只读", value: ""0},{...}] // 依据 value 获取对应的值的形容信息(例子: 下载权限对应的 value 是 '2') this.const.template.PerFolder.getCnameByValue("2") // "下载"
-
应用 services 进行后盾数据申请
this.service.enterprise.getEnterpriseUserList( 'xxxxx', this.keywords, this.page, this.pageSize ).then(res => {console.log(res) // TODO: 解决返回数据 }).catch(e => {console.log(e) // TODO: 处理错误返回数据 })
-
应用本地缓存
// 缓存用户信息 // 这里的 key 在理论我的项目中也要配置化, 对立治理 const user = {name: "小聪忙", wechat: "YXC19970131"}; this.storage.setItem("USER_INFO", user); this.$toast("success", "保留胜利"); // 获取用户名 const user = this.storage.getItem("USER_INFO"); if (user && user.name) {this.$toast("success", user.name); } else {this.$toast("error", "暂无用户缓存信息, 请先缓存"); }
总结
gh-framework
就是以封装、疾速移植为目标而诞生的一种工程化思维,能够达到帮忙开发者和小型团队疾速搭建我的项目,复制我的项目,移植我的项目外围代码的目标。其与 webpack、gitLab 等工具联合,能够实现一个编码、打包、部署联合一体的残缺前端工程。