kft_vue
- 创建kft_vue项目:
$ vue create kft_vue;
- 选择配置:Manully select features => Babel,Router,Vuex,CSS Pre-processors,Linter => Y => Stylus(预处理器) => ...error => save(校验) => dedicated (单个文件存储) => N(不保存预设)
- 运行项目:
$ cd kft_vue && yarn serve
- vscode设置code命令:
>code;命令行使用vscode打开文件:$ code .
- kft_vue各文件作用:
- src/assets 资源文件,可打包
- src/components 公共组件
- src/views 页面级组件
- src/App.vue 入口文件
- src/main.js 主入口
- src/router.js 路由
- src/store.js vue
- .browserslistrc.js 浏览器兼容
- .eslintrc.js eslint配置
- 安装axios和cube-ui
$ yarn add axios
$ vue add cube-ui
- 文档
- cube-ui官网
- node版本管理工具n
3.
$ n list 查看可用版本
- vue官网
- vscode安装vue快捷工具插件:vue 2 Snippets
- 开始写项目
- 首页轮播图
- src/views/Home.vue
- slider组件-轮播图组件
- Home.vue
<template>
<div>
<!-- <HomeHead></HomeHead> -->
<cube-slide :data="items">
<cube-slide-item v-for="(item,index) in items" :key="index+'banner'">
<a :href="item.url">
<img :src="item.image" alt />
</a>
</cube-slide-item>
<template slot="dots" slot-scope="props">
<span
v-for="(item,index) in props.dots"
:key="index"
:class="{activate:props.current === index}"
></span>
</template>
</cube-slide>
</div>
</template>
<script>
import Loin from "@/assets/Lion.jpg";
// import HomeHead from "@/components/HomeHead";
export default {
components: {
// HomeHead
},
data() {
return {
Loin,
items: [
{
url: "#",
image: Loin
},
{
url: "#",
image: Loin
}
]
};
}
};
</script>
// scoped 只在当前组件下可用
<style lang="stylus" scoped>
img
width 100%
span
width 9px
height 9px
background rgba(216,216,216,1)
border-radius: 50%
margin 6px 4.5px
.activate
background
</style>
- 头部: button
- src/components/HomeHead.vue
- 划入:drawer list是二维数组 drawer抽屉
<template>
<div class="head">
<img src="../assets/images/Snow.jpg" alt />
<cube-button icon="cubeic-more" @click="showDrawer"></cube-button>
<cube-drawer
ref="drawer"
title="请选择"
@cancel="cancelHandler"
@select="selectHandler"
:data="list"
></cube-drawer>
</div>
</template>
<script>
export default {
data() {
return {
list: [
[
{
text: "全部课程",
value: 0
},
{
text: "vue课程",
value: 1
},
{
text: "react课程",
value: 2
}
]
]
};
},
methods: {
showDrawer() {
if (!this.isShowDrawer) {
this.$refs.drawer.show();
this.isShowDrawer = true;
} else {
this.$refs.drawer.hide();
this.isShowDrawer = false;
}
},
cancelHandler() {
// 修复点两次显示bug:修改标志位
this.isShowDrawer = false;
},
selectHandler(arg1, arg2, arg3) {
// value arg1 ; index arg2 ; text arg3.
this.isShowDrawer = false;
}
}
};
</script>
<style lang="stylus" scoped>
.head
height 46px
background
display flex
flex-direction row
justify-content space-between
align-items center
img
height 28px
width auto
margin 9px 13px
button
background-color
width 45px
height 46px
.cube-drawer
margin-top 46px
</style>
- 课程列表
- src/components/CourseList.vue
- 在src/views/Home.vue引入CourseList并使用
- CourseList.vue
<template>
<div>
<div class="list-title">
<i class="iconfont zf-kecheng"></i>
<span>课程列表</span>
</div>
<ul class>
<li v-for="(item,index) in courseList" :key="index" class="course-item">
<img :src="item.img" alt />
<div class="course-info">
<p class="course-title">{{item.title}}</p>
<div class="course-more">
<span class="course-price">价格:{{item.price}}</span>
<span class="course-learn-count">学习人数:{{item.learnCount}}</span>
</div>
</div>
</li>
</ul>
</div>
</template>
<script>
import webpack from "@/assets/images/Yosemite 4.jpg";
export default {
data() {
return {
webpack,
courseList: [
{
title: "Webpack4.0",
img: webpack,
price: 9200,
learnCount: 666
}
]
};
}
};
</script>
<style lang="stylus" scoped>
.list-title
text-align left
margin 8px
i
font-size 22px
margin-right 3px
span
font-size 15px
font-weight 600
color
ul
margin 0 5px
.course-item
display flex
flex-direction row
margin 8px
border-radius 10px
box-shadow 1px 1px 1px 1px rgba(0,0,0,0.5)
overflow hidden
img
height 67px
.course-title
text-align left
font-size 15px
font-weight 600
margin-top 5px
margin-bottom 13px
.course-info
margin 5px 10px
.course-more
span
margin-right 10px
.course-price
font-size 12px
.course-learn-count
font-size 12px
</style>
- tabBar
- cube-ui:tab-bar文档
- tabBar组件:首页+个人中心
- src/App.vue
- App.vue
<template>
<div id="app">
<div class="container">
<router-view />
</div>
<cube-tab-bar v-model="selectedLabelDefault" @click="clickHandler" :data="tabs"></cube-tab-bar>
</div>
</template>
<script>
export default {
data() {
return {
selectedLabelDefault: "/",
tabs: [
{
label: "首页",
icon: "cubeic-home",
value: "/"
},
{
label: "个人中心",
icon: "cubeic-person",
value: "/profile"
}
]
};
},
methods: {
clickHandler(label) {
this.$router.push(label);
}
}
};
</script>
<style lang="stylus">
font-family 'Avenir', Helvetica, Arial, sans-serif
-webkit-font-smoothing antialiased
-moz-osx-font-smoothing grayscale
text-align center
color
display flex
flex-direction column
justify-tontent bettween
height 100vh
.container
flex 1
overflow scroll
.cube-tab-bar
border-top 1px solid
div
font-size 14px
line-height 16px
</style>
- 个人中心页面
- src/views/Profile.vue
- Profile.vue
<template>
<div class="person">
<div class="user-info">
<img class="user-avator" :src="avator" alt />
</div>
<ul class="bottom-ul content">
<li>
<i class="cubeic-danger"></i>
<span>常见问题</span>
</li>
<li>
<i class="cubeic-close"></i>
<span>退出登录</span>
</li>
</ul>
</div>
</template>
<script>
import avator from "@/assets/images/avator.png";
export default {
data() {
return {
avator
};
}
};
</script>
<style lang="stylus" scoped>
.person
.user-info
background
background-size cover
height 203px
display flex
flex-direction column
justify-content center
align-items center
.user-avator
background-image url("../assets/images/avator.png")
height 53px
width 53px
background-color
background-size cover
border-radius 50%
.bottom-ul
line-height 32px
font-size 14px
color
border-bottom 1px solid
li
padding-left 10px;
text-align left
span
padding-left 5px
</style>
- 覆盖tabBar
- 可以通过绝对定位+z-index+background覆盖tabBar
- 覆盖tabBar的返回按钮组件
- src/components/BackHead.vue
- BackHead.vue
<template>
<div class="head">
<i @click="goBack" class="cubeic-back coustom-icon"></i>
</div>
</template>
<script>
export default {
methods:{
goBack(){
console.log('back');
this.$router.go('-1');
}
}
}
</script>
<style lang="stylus" scoped>
.head
background
height 46px
display flex
flex-direction row
justify-content between
align-items center
.coustom-icon
color
font-size 30px
margin-left 5px
</style>
kft_koa_api
- 后台初始化:
- koa脚手架的生成器:
$ cnpm install -g koa-generator
$ koa2 kft_koa_api
$ cd kft_koa_api && yarn或$ cd kft_koa_api && cnpm i
$ nrm use cnpm 切换源
- kft_koa_api文件作用
- bin/www,网站默认端口、服务...,网站文件
- public文件夹:资源
- routes:后端路由
- views:放置模板文件,前后端不分离的时候主要使用文件夹
- app.js:主要的后端文件,引入的配置文件
- nodemon 改变代码无需重启,自动修改
- pm2:部署环境下使用
$ pm2 start bin/www --watch 后台运行,静默重启
- 登录注册接口
- routes/account.js
const router = require('koa-router')()
router.post('/login', async (ctx, next) => {
console.log('收到前端请求');
ctx.body = {
code: 0,
msg: '登录成功'
}
})
module.exports = router
- app.js
+ const account = require('./routes/account')
+ app.use(account.routes(), account.allowedMethods())
$ cnpm i mongoose 安装mongo数据库
- 校验规则
- util/validate/validator.js
- 函数编程的函数库
$ yarn add ramda
const R = require('ramda');
const pattern = {
// http://emailregex.com/
email: /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
url: new RegExp('^(?!mailto:)(?:(?:http|https|ftp)://|//)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?$', 'i'),
hex: /^
};
// 新建一个校验类
class Validator {
constructor(query) {
// 传过来的对象挂载在当前构造函数
this.query = query;
// 校验规则
this.rules = [];
}
check(name, type, errorMsg, params) {
this.rules.push({
name,
type,
errorMsg,
// 赋予一个默认值
params: params || {}
})
}
validate() {
// 创建拆分规则
const groupByName = R.groupBy(rule => rule.name);
// 安装规则进行拆分
const groupList = groupByName(this.rules);
Object.keys(groupList).map(key => {
// 拿到所有的校验规则
const rules = groupList[key];
// 找到是否必填
const requiredRule = rules.find(rule => rule.type === "required");
// 拿到当前key的值
const value = this.query[key];
// 如果是必填
if (requiredRule) {
// 没有值抛出错误
if (value === undefined) {
throw new Error(requiredRule.errorMsg);
}
} else {
// 不是必填项并且value===undefined
if (value === undefined) {
// 性能优化
return
}
}
// 获取其他规则
const otherRules = rules.filter(rule => rule.type !== "required");
for (let i = 0; i < otherRules.length; i++) {
const rule = otherRules[i];
// 如果是正则
if (rule.type instanceof RegExp) {
// 没有通过验证
if (!rule.type.test(value)) {
throw new Error(rule.errorMsg);
}
}
switch (rule.type) {
case 'email':
if (!pattern.email.test(value)) {
throw new Error(rule.errorMsg)
}
break;
case 'len': {
const { min, max } = rule.params;
if (min && value.length < min) {
throw new Error(rule.errorMsg)
}
if (max && value.length > max) {
throw new Error(rule.errorMsg)
}
}
}
}
})
}
}
// const query = { email: '11@zhufeng.com', password: '2222' };
// const validatorInstance = new Validator(query);
// validatorInstance.check('email', 'required', '邮箱必填');
// validatorInstance.check('email', 'email', '邮箱格式不正确');
// validatorInstance.check('password', 'required', '密码必填');
// validatorInstance.check('password', 'len', '密码长度不正确');
// validatorInstance.validate();
// console.log('校验成功');
module.exports = Validator;
- codeIgniter,php框架,全是坑,能提高代码水平;thinkPHP,php框架,全是封装,不用写底层代码;
- controller 具体操作;lib/db/ 数据库;model 数据库的增删改查操作;util 工具
- 登录注册:routes/account.js => controller/account/login或controller/account/register => model/account.js=>lib/db/user.js
- 错误打印 app.js
app.on('error', (err, ctx) => {
console.error('server error', err, ctx);
ctx.body = {
msg: err.message
}
ctx.status = 200;
ctx.res.end(JSON.stringify(ctx.body));
});
- 异常处理 lib/exceptions/logicException.js
- 启动mongodb数据库
- Mac:
1..
$ brew services list 当前启动的服务 $ brew services start mongodb 启动mongodb数据库
- ssh root@149.28.132.207 进入linux服务器
$ systemctl status mongod 查找mongod服务
$ systemctl stop mongod 关闭mongod服务
$ systemctl start mongod 关闭mongod服务