曾经的;
以前的;
最近的;
现在的;
给自己留个纪念了。
1.elementUI
(1)鼠标悬浮文字提示------el-tooltip组件
// 第1:el-tooltip要和i标签配合
<el-tooltip effect="dark" placement="bottom">
<div slot="content"> //第2:本来el-tooltip是有一个content属性的,但是要展示多行或者说文本内容要有格式,没事,组件库提供了具名slot='content属性’
<p v-for="item in messages" :key="item">
{{item}}
</p>
</div>
<i class="el-icon-information"></i>
</el-tooltip>
(2)滚动条-----el-scrollbar组件
// 配合wrap-class="elements__dropdown"让其出现滚动条 并且css属性定义其height为父容器的高度的100%。但是elementUI组件库并没有引入(还存在问题,可以上网查)
(3)单选组合----el-radio-group组件和 el-radio-button
el-radio-group(v-model="type", size="small")
el-radio-button(label="templates") 模板
el-radio-button(label="elements" :disabled="topoEditTemplate") 自定义
// 主要是label标签和v-model的联动,并且lable值尽量用字符串
(4)面包屑-----Breadcrumb 组件
//显示当前页面的路径,快速返回之前的任意页面。
<el-breadcrumb separator="/">
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
<el-breadcrumb-item>活动详情</el-breadcrumb-item>
</el-breadcrumb>
// 类似
<router-link
v-for="route in breadcrumbData"
:key="route.name"
class="breadcrumb__item"
:to="{name: route.name}">
{{ route.meta && route.meta.name }}
</router-link>
其中separator="/"
类似.breadcrumb__item::after {
content: '/';
color: #CBCBCB;
margin-left: 8px;
}
2.ES6
(1).对象的解构赋值
//参考http://es6.ruanyifeng.com/#docs/destructuring学习(数组和对象的解构赋值)
let { templates } = tool.loadTopoElements(); //tool.loadTopoElements()返回 {templates ,elements}
(2).import的使用
import * as xxx from ‘xxx’: 会将若干export导出的内容组合成一个对象返回;
import xxx from ‘xxx’:(export default Din)只会导出这个默认的对象作为一个对象
还有就是
export function crc32() { // 输出
// ...
};
import {crc32} from 'crc32'; // 输入 对应的import语句需要使用大括号
还有
项目中
function a(){}
function b(){}
export default {a,b } //多个还是要加括号的
import api from 'xxx.js';
(3) Object.assign()方法
//公共文件 提供接口rules
Object.assign(op, {
message: '存在不合法的字符'
}, rules);
//自己文件
regexp: {
regexp: /^[1-9]\d*$/,
message: '漏洞ID必须是大于0的正整数。'
}
//或
callback: {
callback (v) {
const MIN = 1;
if (v < MIN) {
return {
valid: false,
message: '请输入正整数'
};
}
return {
valid: true
};
}
}
接口rules对应比如对象:
{
regexp: /^[1-9]\d*$/,
message: '漏洞ID必须是大于0的正整数。'
}
3.项目中的各个文件什么意思?
(1).gitkeep文件
git是不允许提交一个空的目录到版本库上的,可以在空的文件夹里面建立一个.gitkeep文件(空文件),然后提交上去去即可。
(2).browserslistrc文件
浏览器 兼容处理 比如加css前缀
(3)eslintrc.js 文件
编码规范 学会配置
(4)babel.config.js
转为es5
(5).npmrc
npm install xxx
内容registry=http://200.200.151.86:7001/
(6)config.js
项目配置文件 后台接口端口
(7)gulpfile.js 前端打包工具
let gulp = require('gulp');
gulp.task('xxx', []);
相当于webpack.config.js
(8)package-lock.json
比package.json更具体 学会配置package.json ,package.json项目中必须有但package-lock.json不提交也行
(9)vue.config.js 项目中新增的配置参考vue-cli
(10)Polyfill.js
Polyfill 是一块代码(通常是 Web 上的 JavaScript),用来为旧浏览器提供它没有原生支持的较新的功能
比如我们看到项目中的一些ES6,7,8,9中的数组方法实现在此文件(可以参考MDN文档看看)
4.拓展
(1)http://qunee.com/ SVG转canvas 为了兼容ie版本 可以上github了解进一步了解拓扑
(2)通用网关接口(Common Gateway Interface/CGI):一种规范的接口文档
(3)TD :bug状态管理 new--fixed--fixed已审核
(4)前端 crypto-js 加解密库:https://www.jianshu.com/p/a47477e8126a 使用过MD5 ,另外也使用过base64
5.逻辑代码
(1)动态口令逻辑
//html:真假配合的不错
<input ref="input"
:maxlength="6"
class="real-input"
@keyup="getNum"
@keydown="delNum"
v-model="tokenNum">
<ul ref="codeInput"
class="code-input clearfloat">
<li v-for="(disInput, index) of disInputs"
:key="disInput.id">
<span v-if="tokenNum.length === index"
ref="spanText">{{content}}</span>
<input v-else
ref="virtualInput"
:maxlength="1"
v-model="disInput.value">
</li>
</ul>
其中 <span v-if="tokenNum.length === index" ref="spanText">{{content}}</span> //tokenNum是一个输入的数字字符串,tokenNum.length === index意思就是让你输入的下一个获取焦点
//JS:这个配合好
//设置span输入框中的内容为'|'
setLine () {
if (this.timer2) {
clearInterval(this.timer2);
}
this.content = '|';
this.timer1 = setInterval(() => {
this.setSpace();
}, SHOW);
},
//设置span输入框中的内容为空
setSpace () {
clearInterval(this.timer1);
this.content = '';
this.timer2 = setInterval(() => {
this.setLine();
}, HIDE);
}
},
beforeDestroy () {
clearInterval(this.timer1);
clearInterval(this.timer2);
this.timer1 = null;
this.timer2 = null;
}
//JS:这个也很优秀
// 给显示的input框添加值
getNum () {
for (let i = 0; i < this.tokenNum.length; i++) {
this.disInputs[i].value = this.tokenNum.charAt(i);//优秀
}
},
// 删除显示input框的值
delNum () {
let e = event || window.event;
if (e.keyCode === BACKSPACE) {
if (this.tokenNum.length < LENGTH) {//优秀this.tokenNum.length < LENGTH
this.disInputs[this.tokenNum.length].value = ' ';
}
}
},
(2)用户管理逻辑
各个弹框注意:第一:加载过程 第二:按钮防止二次提交
第一:加载动画: 表格加载 表单也要有加载动画
//规范:记得在每个组件 比如el-form 外面套一层容器
<div class="content" v-loading="loading">
<el-form ></el-form>
</div>
//加载:采用 元素上添加 v-loading="" ,data中定义布尔变量 ,两者结合 并在点击确定按钮是改变为true
第二:按钮防止二次提交
按钮上 :disabled="!formValid || loading"
finally(() => {
this.loading = false;
});
(3)租户列表中重要的等级 容错处理和旧租户特殊处理
tenant.hasOwnProperty('is_key_tenant') ? (+tenant.is_key_tenant).toString() : tenant.is_key_tenant = ''
(4) edr规则库特殊处理
// 优化一:两个if合并,减少代码冗余
let isFileExtentEdr = /^\.(zip|pkg|exe|cab)$/.test(fileExtent); //将IsfileExtentEdr改为isFileExtentEdr (规范)
// 文件选择错误提示
if ((this.type === 'vSEDR' && !isFileExtentEdr) || (this.type !== 'vSEDR' && fileExtent !== '.zip')) {
this.$error('EDR类型可以上传.zip, .exe, .pkg, .cab文件,其他类型只可上传.zip'); // 错误提示合并 (具体提示找交互再确认一下)
this.clearTnputFile(this.$el.querySelector('input[type="file"]'));
return false;
}
<!-- 优化二:借助accept属性 ,从而不需要手动判断文件类型。相应的就不需要文件选择错误提示,除非 手动去更改控制资源管理器预览的时候指定的文件类型 -->
<div class='wrapper'>
<!--input[type='file']的 accept属性 控制资源管理器预览的时候只能看见指定的文件类型-->
<input v-if='this.type===' vSEDR' type="file" accept=".zip, .exe, .pkg, .cab" />
<input v-else type="file" accept=".zip" />
</div>
6.简单效果
(1).动态口令复制到粘贴板.
方法:使用 document.execCommand('copy')。
具体:
copyToken (pwd) {
let input = document.getElementsByClassName('el-input__inner')[1];//获取input元素
input.value = pwd;
input.select(); // 选中input框中的value文本
document.execCommand('copy'); // 执行浏览器复制命令
input.value = '';//最后赋空 现实需要的道理
},
(2).input框添加焦点,input框添加焦点,他的value还是可以获取到的。这里联想到给input框加autofocus (一次性)和vue中自定义指令(一次性)
@click绑定在容器时,获取到input,保证人为点击始终获取input焦点
focus () {
this.$refs.input.focus();
},
(3)新增用户确定 密码长度变化 (真正理解v-model,理解为什么密码会变长)
this.form.password =Base64.encode(this.form.password);
改为如下
let params = {
user_desc: this.form.user_desc,
user_name: this.form.user_name,
password:this.editData ? delete this.form.password : Base64.encode(this.form.password),
component_limit: this.form.component_limit
}
(4).项目中使用$router $route的区别
1.$router为VueRouter实例,想要导航到不同URL,则使用$router.push方法
2.$route为当前router跳转对象,里面可以获取name、path、query、params等
7.配置文件
(1)axios_learning.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script>
//异步,并发,迭代器?
//ES6 编码风格 参考官网https://es6.ruanyifeng.com/?search=import&x=0&y=0#docs/style
//后台返回的数据格式
// {"data":[{"user_id":"001","user_name":"anina1"},{"user_id":"002","user_name":"anina2"},{"user_id":"003","user_name":"anina3"}],"total": 0, "mesg": null, "success": 1}
// {"data":{"user_id":"001","user_name":"anina1","children":{"gril":"xinni","boy":"xindi"},"hobbys":["drawing","singing","sleeping"]},"total": 0, "mesg": null, "success": 1}
//Promise.all() 参考官网https://es6.ruanyifeng.com/?search=import&x=0&y=0#docs/promise#Promise-all
//参考官网https://www.npmjs.com/package/axios
// 对比vue-resource 参考官网https://www.npmjs.com/package/vue-resource
//install后
//1.引入axios使用
import axios from 'axios';
//2.Creating an instance:You can create a new instance of axios with a custom config(即自定义的配置).
//axios.create([config])
Vue.prototype.$http = axios.create({ //$http是定义属性
baseURL: 'https://some-domain.com/api/',//为了方便下面url前面重复书写
});
//普通异步请求(get)
function getDemo() {
//3.GET request:axios.get(url[, config])
//params-ID is not behend url ,you can config a params object .but only choose either-or (二选一)
//Make a request for a user with a given ID
this.$http.get('/platform/admin_manage?ID=12345&temp="1"', {
//`params` are the URL parameters to be sent with the request
params: {
ID: 12345,
temp: "1"
}
}).then((response) => {
console.log(response);
}).catch((error) => {
console.log(error);
});
}
//普通异步请求(post)
function postDemo() {
//POST request:axios.post(url[, data[, config]])
this.$http.post('/platform/admin_manage', {
firstName: 'Fred',
lastName: 'Flintstone'
}, {
//`params` are the URL parameters to be sent with the request
params: {
ID: 12345,
temp: "1"
}
}).then((response) => {
console.log(response);
}).catch((error) => {
console.log(error);
});
}
//拓展1:使用sync/await异步请求
//Want to use async/await? Add the `async` keyword to your outer function/method.
async function getDemo() {
try {
const response = await axios.get('/user?ID=12345&temp="1"');
console.log(response);
} catch (error) {
console.error(error);
}
}
//拓展2:执行多个并发请求
//Helper functions for dealing with concurrent requests.
//axios.all(iterable)
//axios.spread(callback)
//Performing multiple concurrent requests 执行多个并发请求
function getUserAccount() {
return axios.get('/user/12345');
}
function getUserPermissions() {
return axios.get('/user/12345/permissions');
}
axios.all([getUserAccount(), getUserPermissions()])
.then(axios.spread(function (acct, perms) {
// Both requests are now complete
}));
// 补充:notice:axios(url[, config])
// 1.When using the alias methods. url, method, and data properties don't need to be specified in config.
// 2.其中axios(url[, config]) 中config写的时候是一个大对象{}
// 3.if you use axios(config),means only using config . url, method, and data properties need to be specified in config.
// Send a POST request
axios({
method: 'post',
url: '/user/12345',
data: {
firstName: 'Fred',
lastName: 'Flintstone'
}
});
// GET request for remote image
axios({
method: 'get',
url: 'http://bit.ly/2mTM3nY',
responseType: 'stream'
}).then(function (response) {
response.data.pipe(fs.createWriteStream('ada_lovelace.jpg'))
});
</script>
</body>
</html>
(2)axios.js[封装]
/**
* axios 实例文件
*/
import axios from 'axios';
const AUTH_CODE = 401; // 权限失效状态码
const IS_DEV = process.env.NODE_ENV !== 'production'; // 是否是生产环境
let service = axios.create({
// `headers`是要发送的自定义标头
headers: {
'X-Requested-With': 'XMLHttpRequest'
},
// `transformRequest`允许在将请求数据发送到服务器之前对其进行更改
transformRequest: [function (data) {
if (!data) {
return;
}
if (data instanceof FormData) {
return data;
}
let rs = [];
Object.keys(data).forEach((key) => {
//为了避免服务器收到不可预知的请求,对任何用户输入的作为URI部分的内容你都需要用encodeURIComponent进行转义。
rs.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]) + '&');
});
// 数组中的最后一个函数必须返回一个字符串或Buffer,ArrayBuffer的实例,
return rs.join('').slice(0, -1);
}]
});
// 请求拦截器
service.interceptors.request.use(
// Do something before request is sent
config => {
// 这里可以自定义一些config配置
let urlFix = !IS_DEV ? '/dashboard' : '/platform/dashboard';
config.url = urlFix + config.url;
return config;
},
// Do something with request error
error => {
return Promise.reject(error);
}
);
// 响应拦截器
service.interceptors.response.use((response) => {// 这里处理一些response正常返回的数据
if (typeof response.data === 'object' && !response.data.success) {
return Promise.reject(response);
}
return response;
}, (error) => {// 这里处理一些response出错时的逻辑
if (error && error.response && error.response.status === AUTH_CODE) {
const WS = window.location;
let loginURL = '';
if (IS_DEV) {
loginURL = `${WS.protocol}//${WS.host}:${WS.port}/dashboard/#/dev/login`;
} else {
loginURL = `${WS.protocol}//${WS.host}:${WS.port}/dashboard/auth/login/`;
}
window.location = loginURL;
}
return Promise.reject(error);
});
(3)vue.config.js[配置]
/**
* vue cli 配置文件
*/
/*global require: true */
/*global __dirname: true */
let path = require('path');
let config = require('./config');
function resolve(dir) {
return path.join(__dirname, '.', dir);
}
// Vue CLI 配置参考 https://cli.vuejs.org/zh/config/#vue-config-js
/* global module:true process:true */
module.exports = {
// 是否使用包含运行时编译器的 Vue 构建版本。设置为 true 后你就可以在 Vue 组件中使用 template 选项了
runtimeCompiler: true,
// 基本路径 从 Vue CLI 3.3 起已弃用 使用publicPath
// process.env.NODE_ENV === 'production' 生产环境
baseUrl: process.env.NODE_ENV !== 'production' ? 'static' : '/dashboard/static/',
//在 multi-page 模式下构建应用。每个“page”应该有一个对应的 JavaScript 入口文件
pages: {
login: {
// page 的入口
entry: 'src/login.js',
// 模板来源
template: 'public/login.html',
// 在 dist/index.html 的输出
filename: 'login.html'
},
index: {
// page 的入口
entry: 'src/login.js',
// 模板来源
template: 'public/login.html',
// 在 dist/index.html 的输出
filename: 'login.html'
},
index: {
// page 的入口
entry: 'src/main.js',
// 模板来源
template: 'public/index.html',
// 在 dist/index.html 的输出
filename: 'index.html'
},
attack: {
// page 的入口
entry: 'src/attack.js',
// 模板来源
template: 'public/attack.html',
// 在 dist/index.html 的输出
filename: 'attack.html'
}
},
// webpack配置
chainWebpack: config => {
//链式操作 https://cli.vuejs.org/zh/guide/webpack.html#%E7%AE%80%E5%8D%95%E7%9A%84%E9%85%8D%E7%BD%AE%E6%96%B9%E5%BC%8F
config.resolve.alias
.set('src', resolve('src'))
.set('home', resolve('src/home'))
.set('components', resolve('src/components'))
.set('assets', resolve('src/assets'))
.set('utils', resolve('src/utils'));
},
// webpack-dev-server 相关配置
devServer: {
https: true,
port: 443,
//如果你的前端应用和后端 API 服务器没有运行在同一个主机上,你需要在开发环境下将 API 请求代理到 API 服务器。
proxy: {
'/platform': {
target: `https://${config.host}`,
secure: false,
https: true,
changeOrigin: false,
pathRewrite: {
'^/platform': ''
}
},
'/tenant': {
target: `https://${config.host}`,
secure: false,
https: true,
changeOrigin: false,
pathRewrite: {
'^/tenant': ''
}
},
'/dashboard/static': {
target: `https://${config.host}`,
secure: false,
https: true,
changeOrigin: false
},
'/dashboard/captcha': {
target: `https://${config.host}`,
secure: false,
https: true,
changeOrigin: false
}
}
},
// 输出文件目录 ,Default: 'dist
outputDir: void(0),
//放置生成的静态资源 (js、css、img、fonts) 的 目录
assetsDir: void(0),
// 生产环境是否生成 sourceMap 文件, Default: ''
productionSourceMap: false,
parallel: void(0),
css: void(0)
};
(4)例子
main.js
import router from '@/router';
vm = new Vue({
el: '#app',
router,
store,
template: '<App/>',
components: {
App,
},
});
src\router\index.js
import routerUserManage from './mod_user_manage'; //xmt
//xmt 接着2 配置路由组件
Vue.use(Router);
const ROUTER = new Router({
mode: 'hash',
routes: [
...routerCommon.router,
...routerDev.router,
routerGuide,
routerGuideS,
...routerFullScreen.router,
{
path: '/platform', //根据地址
name: 'platform',
component: () =>
import(/* webpackChunkName: "common" */ 'home/platform/index'),
redirect: {
name: 'home' //命名路由
},
children: [
routerSecurity,
routerTree,
routerManage,
routerPMarket,
routerPIndex,
routerConfiguration,
routerOperation,
routerUserManage
]
}
]
});
src\router\mod_user_manage.js
/**
* @file userManage 用户管理模块
*/
export default {
path: '/user-manage',
name: 'userManage',
component: () =>
import(/* webpackChunkName: "userManage" */ '@/home/mod_user_manage/user_manage')
};
src\home\platform\index.vue
<cssp-nav platform="platform"></cssp-nav>
<router-view></router-view>
/**
* @file 平台侧组件的入口文件
*/
import csspNav from 'home/components/Navigation';
export default {
name: 'platform',
data () {
return {
timer: null,
opened:false
};
},
components: { csspNav}
}
src\home\components\Navigation.vue
<li class="nav__menu-item" v-if="showReport">
<router-link :to="{name: 'home'}">首页</router-link>
</li>
<li class="nav__menu-item" >
<router-link :to="{name: 'userManage'}">用户管理</router-link>
</li>
D:\mywork\408项目\ui\src\home\mod_user_manage
src\home\mod_user_manage\user_manage.vue //路由组件
<!-- 工具条 -->
<!-- 表格数据区 -->
<!-- 分页控件 -->
<!-- 新增或编辑弹窗 --><user-dialog ref="addUser"></user-dialog>
<!-- 分配模板弹窗 -->
import toolbar from '../../components/gridToolbar';
import api from './common/api/user_manage.js'; //axios
import { PAGENATION } from './common/js/const.js';
import UserDialog from './common/components/user_dialog.vue';
import DistributeDialog from './common/components/distribute_dialog.vue';
export default {
name: 'userManage',
components: {
toolbar,
UserDialog,
DistributeDialog
},
created() {
// 获取用户管理模块列表
this.getUserManageList();
},
methods:{
// 获取用户管理模块列表
getUserManageList () {
this.loading = true;
let params = {// keyword是什么?
keyword: this.params.keyword,
limit: this.pagination.pageSize,
offset: (this.pagination.page - 1) * this.pagination.pageSize
};
api.getUserManageList(params).then((res) => {
this.userManageList = res.data;
this.pagination.total = res.total;
this.loading = false;
}).catch((res) => {
this.loading = false;
this.$error(res.data && res.data.mesg || '用户管理列表获取失败', '获取失败');
});
},
// 新增或编辑
addOrEditUser (item) {
if (this.$refs.addUser) {
// $emit触发open事件,在用户弹框页面用$on监听
this.$refs.addUser.$emit('open', item);
}
},
}
}
ui\src\home\mod_user_manage\common\components\user_dialog.vue
import api from '../api/user_manage.js'; //axios
import { userRules } from '../js/rules.js';//xmt
data () {
return {
rules: userRules, //xmt }
}
mounted () {
// editData存在时为编辑状态
this.$on('open', (editData) => {
this.dialogVisible = true;
if (editData) {
this.formValid = true;
this.editData = editData;
// this.form = Object.assign({}, editData);
this.form.name = editData.name;
this.form.account = editData.account;
} else {
this.formValid = false;
this.editData = null;
this.form = {
name: '',
account: '',
password:''
};
}
});
},
src\home\mod_user_manage\common\js\rules.js
//引入表单校验配置入口文件
import validtor from 'src/validation/index';
export function notEmpty (rule, value, callback){
}
export userRules={
name: {
trigger: 'change',
required: true,
detail: {
notEmpty: {
message: '请输入名称'
}
},
validator: validtor.valid
},
}
ui\src\validation\index.js
/**
* 表单校验配置入口文件
*/
import validtors from './validtors';
export default {
valid (rules, value, cb) {
let v = rules.trim ? value.trim() : value;
let rs = {
valid: true,
msg: ''
};
if (rules && rules.detail) {
Object.keys(rules.detail).every((name) => {
if (validtors &&
Object.prototype.hasOwnProperty.call(validtors, name)) {
rs = validtors[name](v, rules.detail[name], rules);
if (!rs.valid) {
return false;
}
}
return true;
});
}
if (typeof cb === 'function') {
if (!rs.valid) {
cb(new Error(rs.msg || rs.message || 'input error'));
return false;
}
cb();
}
return true;
}
};
src\validation\validtors.js
export default {
notEmpty(v, rules) {
let op = {};
Object.assign(op, {
message: '该选项不能为空'
}, rules);
if (!String(v) || !String(v).trim()) {
return {
valid: false,
msg: op.message
};
}
return {
valid: true
};
},
}
src\home\mod_user_manage\common\api\user_manage.js
/**
* @file 用户管理模块api
*/
import axios from 'src/utils/axios';
// 获取用户管理模块列表
function getUserManageList(payload) {
return axios({
url: '/mod_user_manage/',
method: 'get',
params: {
action: 'get_user_manage_list',
params: JSON.stringify(payload)
}
}).then((res) => {
return res.data;
});
}
export default {
getUserManageList,
};