2.项目初始化
3. 环境搭建
3.1 创建虚拟环境
# 如果当前系统中没有虚拟环境,则使用以下命令安装虚拟环境:
# pip3 install virtualenv -i https://pypi.tuna.tsinghua.edu.cn/simple
# pip3 install virtualenvwrapper -i https://pypi.tuna.tsinghua.edu.cn/simple
# pip3 install virtualenvwrapper-win -i https://pypi.tuna.tsinghua.edu.cn/simple #windows虚拟环境安装
mkvirtualenv renran
mkvirtualenv renran -p python3 #指定python解析器的版本
3.2 相关命令
创建虚拟环境: mkvirtualenv 虚拟环境名称
创建虚拟环境(指定python版本): mkvirtualenv -p python3 虚拟环境名称
查看所有虚拟环境: workon
使用虚拟环境: workon 虚拟环境名称
退出虚拟环境: deactivate
删除虚拟环境(必须先退出虚拟环境内部才能删除当前虚拟环境):
rmvirtualenv 虚拟环境名称
其他相关命令:
查看虚拟环境中安装的包: pip freeze 或者 pip list
收集当前环境中安装的包及其版本(我们手动安装的包): pip freeze > requirements.txt
收集当前环境中安装的包及其版本(所有的包,包括当前环境依赖):pip list --format=freeze > requirements.txt
在部署项目的服务器中安装项目使用的模块: pip install -r requirements.txt
提示:
- 虚拟环境只会管理环境内部的模块和python解析器,对于源代码是毫无关系
- 创建虚拟环境需要联网
- 创建成功后, 会自动工作在这个虚拟环境上
- 工作在虚拟环境上, 提示符最前面会出现 “(虚拟环境名称)”
3.3 技术选型
外部依赖
- 注册支付宝的开发者账号
- 注册阿里云账号,如果可以购买一个服务器,或者第一次使用的可以申请一个免费外网服务器
- 注册容联云短信接口平台的账号
- 注册腾讯开发者账户,申请QQ互联开发者实名认证
- 申请163或者QQ邮箱开通smtp/pop3服务
- 注册gitee[码云]的账号
- 如果有条件的,可以申请一个域名进行备案[ICP备案和公安部备案],如果没有的话, 可以注册natapp
依赖包安装
pip install django==2.2.0 -i https://pypi.douban.com/simple
pip install djangorestframework -i https://pypi.douban.com/simple
pip install PymySQL -i https://pypi.douban.com/simple
pip install Pillow -i https://pypi.douban.com/simple
pip install django-redis -i https://pypi.douban.com/simple
4. 搭建后端项目
4.1 创建项目
项目所在路径不要使用中文或者空格或者除了下划线的其它特殊符号
mkdir renran
cd renran
django-admin startproject renranapi 创建项目
4.2 打开项目
1.在pycharm中打开renranapi项目
2.选择菜单file, 点选settings...Project renranapi 设置虚拟环境
3.Add添加虚拟环境 :/home/hkxpz/.virtualenvs/renran/bin/easy_install-3.6 --选择renran虚拟环境选择python解释器版本.
在pycharm中如果要使用已经创建好的虚拟环境,则必须设置pycharm中的python解释器,设置为虚拟环境中的python。
4.启动django项目中的manage.py
5.因为仅启动manage.py,所以项目不会运行,所以需要配置启动manage.py的参数。点选“Edit Configurations”,在新窗口中的Parammeters中加上以下内容:
runserver IP:端口号 ---> runserver 127.0.0.1:8000
6.再次运行manage.py文件
分不同环境进行项目配置---目录调整:
reran/
├── docs/ # 项目相关资料保存目录
├── reran_pc/ # 前端项目目录
├── reranapi/ # api服务端项目目录
├── logs/ # 项目运行时/开发时日志目录
├── manage.py
├── reranapi/ # 项目主应用,开发时的代码保存
│ ├── apps/ # 开发者的代码保存目录,以模块[子应用]为目录保存
│ ├── libs/ # 第三方类库的保存目录[第三方组件、模块]
│ ├── settings/
│ ├── dev.py # 项目开发时的本地配置[不需要上传到线上或者服务器]
│ ├── prod.py # 项目上线时的运行配置
│ ├── urls.py # 总路由
│ ├── utils/ # 多个模块[子应用]的公共函数类库[自己开发的组件]
└── scripts/ # 保存项目运行时的脚本文件
1. 在项目主应用下,创建一个settings的配置文件存储目录
2. 根据线上线下两种情况分别创建2个配置文件 dev.py和prod.py
3. 把原来项目主应用的 settings.py 配置内容复制2份到dev.py和prod.py里面
4. 把原来的settings.py配置文件修改文件名,例如settings_old或者直接删除settings.py
4.3 创建代码版本 -- Git
1.安装git
查看git版本信息:git --version
如果没有下载git : apt install git
2.配置用户信息
git config --global user.name'jiayinghe’ #配置自己名字
git config --global user.email‘yinghelail@163.com’ #配置自己邮箱
git config --global color.ui true #配置语法高亮
也可以编辑隐藏文件家目录下的.gitconfig进行修改。
3.初始化git仓库
git init
4.git 基本命令
1)git init #初始化仓库
2)git status # 查看当前仓库的状态 ,保持目前仓库是干净的(没有新文件 没有修改的状态),会经常使用,操作一次用一次
3)git add file # 提交工作目录文件到暂存区
4)git add . 或* #添加目录中所有改动过的文件到暂存区
5)git checkout -- file # 让暂存区的内容覆盖工作目录的文件
6)git rm --cached file # 删除暂存区的内容
7)git rm file # 删除工作目录的文件
8)git rm -f file # 同时删除工作区和暂存区的文件
9)git commit -m "对提交文件做一个描述"#提交暂存区文件到本地仓库,同时做个描述(m:message信息)
10)git diff #比对工作区和暂存区的内容
11)git diff --cached # 比对暂存区和本地仓库的内容
恢复数据:
git log 查看版本信息
git reflog 查看历史记录
git reset --hard 版本id 回滚版本
创建切换分支:
# git branch dev # 创建本地分支dev,dev是自定义
# git checkout dev # 切换本地分支代码
git checkout -b dev # 这里是上面两句代码的简写
git branch -d dev # 删除分支
5.在git创建工程
公司一般都会有自己的代码仓库,一般都是自己搭建,也有使用第三方提供代码管理平台。
常用的代码管理平台:github、gitee(码云)
如果公司自己搭建的代码管理平台,gitlab框架
1)查看添加远程仓库
(1)查看远程仓库
git remote #查看远程仓库
git remote -v #查看远程仓库地址
(2)添加远程仓库
格式: git remote add 远程仓库名 远程仓库地址
1.https格式添加:git remote add origin https://github.com/jiayinghehehe/hehe.git
缺点:每次推送数据都要输入用户名和密码
2.ssh方式添加:免密钥加密进行交互 (在别人家安装自己的锁,进出方便)
好处:可以直接推送数据到远程仓库,保密方式更安全
步骤:1)生成密钥对:ssh-keygen 一路回车
2)查看密钥对:家目录下的.ssh目录:ll .ssh
id_rsa #私钥,相当于钥匙
id_rsa.pub #公钥,相当与锁头
3)把公钥放到GitHub仓库中
复制公钥到GitHub--》settings设置---》SSH and GPG keys----》SSH keys中(没有就新建一个)
4)删除https格式仓库,重新添加ssh方式远程仓库
git remote add origin git@github.com:jiayinghehehe/hehe.git
(3)删除远程仓库:git remote remove
[root@sc git_data]# git remote remove origin
2)推送本地仓库到远程仓库
格式:git push -u 远程仓库名 想要推送的分支
推送主分支到远程仓库:[root@sc git_data]# git push -u origin master
3)从远程仓库下载数据:git clone 下载地址
当以后我们进入公司里面,参与人家已经在做的项目时,别人已经有仓库了,但是我们是新人加入项目中的,那么我们不需要在自己本地进行git init,直接git clone 复制别人的仓库代码
[root@sc mnt]# git clone git@github.com:jiayinghehehe/hehe.git #https地址也可以
git pull #重新加载远程仓库数据,因为团队操作,其他成员可能已经更新远程仓库
6.移除文件
上面虽然成功移交了代码版本,但是一些不需要的文件也被提交上去了。
所以,我们针对一些不需要的文件,可以选择从代码版本中删除,并且使用.gitignore
把这些垃圾文件过滤掉。
git rm 文件 # 删除单个文件
git rm -rf 目录 # 递归删除目录
# 以下操作建议通过ubuntu的终端来完成,不要使用pycharm提供,否则删除.idea还会继续生成。
git rm -rf .idea
git rm db.sqlite3
# 注意,上面的操作只是从项目的源代码中删除,但是git是不知情的,所以我们需要同步。
git add .
git commit -m "删除不必要的文件或目录"
git push -u origin master
git push -u origin +master #强制推送
使用.gitignore
把一些垃圾文件过滤掉。
vim .gitignore 把不需要上传的文件写在文件里
./idea
./idea/*
./git
./db.sqlite3
7.推送远程仓库报错
git push -u origin master 后报错 :
提示:更新被拒绝,因为远程仓库包含您本地尚不存在的提交。这通常是因为另外
提示:一个仓库已向该引用进行了推送。再次推送前,您可能需要先整合远程变更
提示:(如 ‘git pull …’)。
提示:详见 ‘git push --help’ 中的 ‘Note about fast-forwards’ 小节。
直接上解决方案:
强制推送:git push -u origin +master
————————————————
4.5 日志配置
在settings/dev.py文件中追加如下配置:
# 日志配置
LOGGING = {
'version': 1, #python logging日志版本
'disable_existing_loggers': False,
'formatters': { # 日志的处理格式
'verbose': {
'format': '%(levelname)s %(asctime)s %(module)s %(lineno)d %(message)s'
},
'simple': {
'format': '%(levelname)s %(module)s %(lineno)d %(message)s'
},
},
'filters': {
'require_debug_true': {
'()': 'django.utils.log.RequireDebugTrue',
},
},
'handlers': { #日志向那输出
'console': { #往控制台输出
'level': 'DEBUG',
'filters': ['require_debug_true'],
'class': 'logging.StreamHandler',
'formatter': 'simple'
},
'file': { #往文件输出
'level': 'INFO',
'class': 'logging.handlers.RotatingFileHandler',
# 日志位置,日志文件名,日志保存目录必须手动创建
'filename': os.path.join(os.path.dirname(BASE_DIR), "logs/renran.log"),
# 单个日志文件的最大值,这里我们设置300M
'maxBytes': 300 * 1024 * 1024,
# 备份日志文件的数量,设置最大日志数量为10
'backupCount': 10,
# 日志格式:详细格式
'formatter': 'verbose'
},
},
# 日志对象
'loggers': {
'django': { # 固定,将来django内部也会有异常的处理,只会调用django下标的日志对象
'handlers': ['console', 'file'], #线上部署不需要控制台打印
'propagate': True, # 是否让日志信息继续冒泡给其他的日志处理系统,一定要冒泡
},
}
}
4.6 异常处理
新建utils/exceptions.py
from rest_framework.views import exception_handler
from django.db import DatabaseError #数据库抛出的错误
from rest_framework.response import Response
from rest_framework import status
import logging
logger = logging.getLogger('django') #获取日志对象
def custom_exception_handler(exc, context):
"""
自定义异常处理
:param exc: 异常类
:param context: 抛出异常的上下文
:return: Response响应对象
"""
# 调用drf框架原生的异常处理方法,
response = exception_handler(exc, context) #exc错误对象,context错误发生位置
if response is None: #错误处理不了返回none
view = context['view'] #错误方法名称
if isinstance(exc, DatabaseError):
# 数据库异常
logger.error('[%s] %s' % (view, exc))
response = Response({'message': '服务器内部错误'}, status=status.HTTP_507_INSUFFICIENT_STORAGE)
return response
settings/dev.py配置文件中添加
REST_FRAMEWORK = {
# 异常处理
'EXCEPTION_HANDLER': 'renranapi.utils.exceptions.custom_exception_handler',
}
4.7 创建数据库
create database renran default charset=utf8mb4;
为当前项目创建数据库用户[这个用户只能看到这个数据库]
create user nb identified by '123';
grant all privileges on renran.* to 'nb'@'%';
flush privileges; #刷新权限
4.8 配置数据库连接
打开settings/dev.py文件,并配置
DATABASES = {
"default": {
"ENGINE": "django.db.backends.mysql",
"HOST": "127.0.0.1",
"PORT": 3306,
"USER": "nb",
"PASSWORD": "123",
"NAME": "renran",
}
}
在项目主模块的 __init__.py
中导入pymysql
import pymysql
pymysql.install_as_MySQLdb()
运行--调整错误
1.数据库版本检测导致的错误,数据库的版本检测代码注释掉base.py(35-36行)。
2.错误也是因为数据库版本的默认编码导致,query返回的内容格式使用有误:
错误提示: AttributeError: 'str' object has no attribute 'decode'
解决办法: operations.py新增一行代码146行 query = query.encode()
5. 搭建前端项目
5.1 创建项目目录
cd renran(项目根目录)
vue init webpack renranweb(客户端项目目录)
vue创建项目时报错:
vue-cli · Failed to download repo vuejs-templates/webpack: read ECONNRESET
解决办法: npm config set http-proxy null
hkxpz@jia:~/renran$ vue init webpack renranweb
# 选项
? Project name renranweb
? Project description A Vue.js project
? Author jiayinghe <yinghelaile@163.com>
? Vue build standalone
? Install vue-router? Yes #是否安装vue-router
? Use ESLint to lint your code? No
? Set up unit tests No
? Setup e2e tests with Nightwatch? No
? Should we run `npm install` for you after the project has been created? (recom
mended) npm
把vue项目构建好了,运行测试服务器。
pycharm 打开项目,下拉框选择Edit Configurations,点击'+'号,添加npm,在右边Scripts写上dev应用,就可以点击运行按钮或pycharm终端输入:npm run dev 运行项目,根据地址看到首页成功
5.2 初始化前端项目
清除默认的components下的HelloWorld组件,assets下的logo.png, APP.vue中的默认样式
5.3 安装路由vue-router
5.3.1 下载路由组件
如果前面没有选择安装vue-router,则使用以下命令安装路由组件:
npm i vue-router -S
i -- install 简写
-S -- --save简写 当前项目安装模块
-G -- --global 全局安装
5.3.2 配置路由
5.3.2.1 初始化路由对象
在src目录下创建routers路由目录,在routers目录下创建index.js路由文件
index.js路由文件中,编写初始化路由对象的代码 .
import Vue from "vue"
import Router from "vue-router"
// 这里导入可以让让用户访问的组件
import home from '@/components/home'
Vue.use(Router);
export default new Router({
// 设置路由模式为‘history’,去掉默认的#
mode: "history",
routes:[
// 路由列表
{
path: '/',
name: 'home', //组件路由别名
component: home
}
]
})
5.3.2.2 注册路由信息
打开main.js文件,把router对象注册到vue中.代码:
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './routers/index';
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>'
});
5.3.2.3 在视图中显示路由对应的内容
在App.vue组件中,添加显示路由对应的内容。
<template>
<div id="app">
<router-view/> //主要标签,否则看不到页面
</div>
</template>
<script>
export default {
name: 'App',
components: {
}
}
</script>
<style>
</style>
5.3.2.4 创建并提供前端首页的组件
routers/index.js文件使用初始化路由对象文件
import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/components/Home'
Vue.use(Router)
export default new Router({
// 设置路由模式为‘history’,去掉默认的#
mode: "history",
routes: [
{
path: '/home',
name: 'Home',
component: Home
}
]
})
创建Home组件
components目录下创建Home.vue
<template>
<div id="home">
前端首页
</div>
</template>
<script>
export default {
name:"Home",
data(){
return {
}
}
}
</script>
<style scoped>
</style>
5.4 前端初始化全局变量和全局方法
在src目录下创建settings.js站点开发配置文件:
export default {
'host': 'http://127.0.0.1:8000',
}
在main.js中引入
//开发配置文件
import settings from "./settings"
Vue.prototype.$settings = settings;
5.5 引入ElementUI
简介:
Element UI 是一套采用 Vue 2.0 作为基础框架实现的组件库,一套为开发者、设计师和产品经理准备的基于 Vue 2.0 的组件库,提供了配套设计资源,帮助网站快速成型
引入ElementUI:
npm i element-ui -S
上面的命令等同于
npm install element-ui --save
5.5.1 配置ElementUI到项目中
在main.js中导入ElementUI,并调用。
// elementUI 导入
import ElementUI from 'element-ui';
import "element-ui/lib/theme-chalk/index.css";
// 调用插件
Vue.use(ElementUI);
成功引入了ElementUI以后,接下来我们就可以开始进入前端页面开发
前端页面开发:首页例子
1.创建static/css/reset.css,全局css初始化代码
body,h1,h2,h3,h4,h5,ul,p{
padding: 0;
margin:0;
font-weight: normal;
}
a{
text-decoration: none;
color: #4a4a4a;
}
a:hover{
color: #000;
}
input{
outline: none;
}
ul{
list-style: none;
}
img{
width: 100%;
}
.header .el-menu li .el-submenu__title{
height: 26px!important;
line-height: 26px!important;
}
.el-menu--popup{
min-width: 140px;
}
.el-checkbox__inner{
width:16px;
height: 16px;
border: 1px solid #999;
}
.el-checkbox__inner:after{
width: 6px;
height: 8px;
}
.el-form-item__content{
margin-left:0px!important;
width: 50px;
}
.full-left{
float: left;
}
.full-right{
float: right;
}
main.js 引入全局css样式
import "../static/css/reset.css";
2.Home.vue中添加代码:
<template>
<div id="home">
<Header></Header>
<div class="container">
<div class="row">
<div class="main">
<!-- Banner -->
<div class="banner">
<el-carousel height="272px" indicator-position="none" interval="2000">
<el-carousel-item v-for="item in 4" :key="item">
<h3 class="small">{{ item }}</h3>
</el-carousel-item>
</el-carousel>
</div>
<div id="list-container">
<!-- 文章列表模块 -->
<ul class="note-list">
<li class="">
<div class="content">
<a class="title" target="_blank" href="">常做此运动,让你性福加倍</a>
<p class="abstract">运动,是人类在发展过程中有意识地对自己身体素质的培养的各种活动 运动的方式多种多样 不仅仅是我们常知的跑步,球类,游泳等 今天就为大家介绍一种男...</p>
<div class="meta">
<span class="jsd-meta">
<img src="/static/image/paid1.svg" alt=""> 4.8
</span>
<a class="nickname" target="_blank" href="">上班族也健身</a>
<a target="_blank" href="">
<img src="/static/image/comment.svg" alt=""> 4
</a>
<span><img src="/static/image/like.svg" alt=""> 31</span>
</div>
</div>
</li>
<li class="have-img">
<a class="wrap-img" href="" target="_blank">
<img class="img-blur-done" src="/static/image/10907624-107943365323e5b9.jpeg" />
</a>
<div class="content">
<a class="title" target="_blank" href="">“不耻下问”,正在毁掉你的人生</a>
<p class="abstract">
在过去,遇到不懂的问题,你不耻下问,找个人问问就行;在现在,如果你还这么干,多半会被认为是“搜商低”。 昨天,35岁的表姐把我拉黑了。 表姐是医...
</p>
<div class="meta">
<span class="jsd-meta">
<img src="/static/image/paid1.svg" alt=""> 6.7
</span>
<a class="nickname" target="_blank" href="">_飞鱼</a>
<a target="_blank" href="">
<img src="/static/image/comment.svg" alt=""> 33
</a>
<span><img src="/static/image/like.svg" alt=""> 113</span>
<span><img src="/static/image/shang.svg" alt=""> 2</span>
</div>
</div>
</li>
</ul>
<!-- 文章列表模块 -->
</div>
<a href="" class="load-more">阅读更多</a></div>
<div class="aside">
<!-- 推荐作者 -->
<div class="recommended-author-wrap">
<!---->
<div class="recommended-authors">
<div class="title">
<span>推荐作者</span>
<a class="page-change"><img class="icon-change" src="/static/image/exchange-rate.svg" alt="">换一批</a>
</div>
<ul class="list">
<li>
<a href="" target="_blank" class="avatar">
<img src="/static/image/avatar.webp" />
</a>
<a class="follow" state="0"><img src="/static/image/follow.svg" alt="" />关注</a>
<a href="" target="_blank" class="name">董克平日记</a>
<p>写了807.1k字 · 2.5k喜欢</p>
</li>
<li>
<a href="" target="_blank" class="avatar">
<img src="/static/image/avatar.webp" />
</a>
<a class="follow" state="0"><img src="/static/image/follow.svg" alt="" />关注</a>
<a href="" target="_blank" class="name">董克平日记</a>
<p>写了807.1k字 · 2.5k喜欢</p>
</li>
</ul>
<a href="" target="_blank" class="find-more">查看全部 ></a>
<!---->
</div>
</div>
</div>
</div>
</div>
<Footer></Footer>
</div>
</template>
<script>
import Header from "./common/Header";
import Footer from "./common/Footer";
export default {
name:"Home",
data(){
return {
}
},
components:{
Header,
Footer,
}
}
</script>
<style scoped>
.container{
width: 960px;
margin-right: auto;
margin-left: auto;
padding-left: 15px;
padding-right: 15px;
box-sizing: border-box;
}
.container:after, .container:before {
content: " ";
display: table;
}
.row {
margin-left: -15px;
margin-right: -15px;
}
.row:after, .row:before {
content: " ";
display: table;
}
.main {
padding-top: 30px;
padding-right: 0;
position: relative;
min-height: 1px;
padding-left: 15px;
width: 66.66667%;
float: left;
box-sizing: border-box;
}
.main .banner{
width: 640px;
height: 272px;
}
.note-list {
margin: 0;
padding: 0;
list-style: none;
}
.note-list li {
position: relative;
width: 100%;
margin: 0 0 15px;
padding: 15px 2px 20px 0;
border-bottom: 1px solid #f0f0f0;
word-wrap: break-word;
line-height: 20px;
}
.note-list li.have-img {
min-height: 140px;
}
.note-list .have-img .wrap-img {
position: absolute;
top: 50%;
margin-top: -60px;
right: 0;
width: 150px;
height: 100px;
}
.note-list .have-img .wrap-img img {
width: 100%;
height: 100%;
border-radius: 4px;
border: 1px solid #f0f0f0;
vertical-align: middle;
}
.main .note-list .have-img .content {
padding-right: 165px;
box-sizing: border-box;
}
.note-list .title {
margin: -7px 0 4px;
display: inherit;
font-size: 18px;
font-weight: 700;
line-height: 1.5;
color: #333;
}
.note-list .title:hover{
text-decoration: underline;
}
.note-list .abstract {
margin: 0 0 8px;
font-size: 13px;
line-height: 24px;
color: #999;
}
.note-list .meta {
padding-right: 0!important;
font-size: 12px;
font-weight: 400;
line-height: 20px;
}
.note-list .meta span {
margin-right: 10px;
color: #b4b4b4;
}
.jsd-meta {
color: #ea6f5a!important;
}
.note-list .meta a, .note-list .meta a:hover {
transition: .1s ease-in;
}
.note-list .meta a {
margin-right: 10px;
color: #b4b4b4;
}
.note-list .meta img{
width: 15px;
vertical-align: middle;
}
.main .load-more {
width: 100%;
border-radius: 20px;
background-color: #a5a5a5;
margin: 30px auto 60px;
padding: 10px 15px;
text-align: center;
font-size: 15px;
color: #fff;
display: block;
line-height: 1.42857;
box-sizing: border-box;
}
.main .load-more:hover {
background-color: #9b9b9b;
}
.aside {
padding: 30px 0 0;
margin-left: 4.16667%;
width: 29.16667%;
float: left;
position: relative;
min-height: 1px;
box-sizing: border-box;
}
.recommended-authors {
margin-bottom: 20px;
padding-top: 0;
font-size: 13px;
text-align: center;
}
.recommended-authors .title {
text-align: left;
}
.recommended-authors .title span {
font-size: 14px;
color: #969696;
}
.recommended-authors .title .page-change {
float: right;
display: inline-block;
font-size: 16px;
color: #969696;
}
.icon-change{
width: 16px;
vertical-align: middle;
}
.recommended-authors .list {
margin: 0 0 20px;
text-align: left;
list-style: none;
}
.recommended-authors .list li {
margin-top: 15px;
line-height: 20px;
}
.recommended-authors .list .avatar {
float: left;
width: 48px;
height: 48px;
margin-right: 10px;
}
.avatar {
width: 24px;
height: 24px;
display: block;
cursor: pointer;
}
.avatar img {
width: 100%;
height: 100%;
border: 1px solid #ddd;
border-radius: 50%;
}
.follow{
font-size: 14px;
color: #42c02e;
border-color: #42c02e;
font-weight: 400;
line-height: normal;
}
.follow img{
width: 14px;
}
.recommended-authors .list .follow, .recommended-authors .list .follow-cancel, .recommended-authors .list .follow-each, .recommended-authors .list .following {
float: right;
margin-top: 5px;
padding: 0;
font-size: 13px;
color: #42c02e;
box-sizing: border-box;
}
.recommended-authors .list .name {
padding-top: 5px;
margin-right: 60px;
font-size: 14px;
display: block;
box-sizing: border-box;
}
.recommended-authors .list p {
font-size: 12px;
color: #969696;
margin: 0 0 10px;
box-sizing: border-box;
}
.recommended-authors .find-more {
position: absolute;
padding: 7px 7px 7px 12px;
left: 0;
width: 100%;
font-size: 13px;
color: #787878;
background-color: #f7f7f7;
border: 1px solid #dcdcdc;
border-radius: 4px;
}
.row:after {
clear: both;
}
.el-carousel__item h3 {
color: #475669;
font-size: 14px;
opacity: 0.75;
line-height: 150px;
margin: 0;
}
.el-carousel__item:nth-child(2n) {
background-color: #99a9bf;
}
.el-carousel__item:nth-child(2n+1) {
background-color: #d3dce6;
}
</style>
3.static/css引入字体样式,static/image引入图片
main.js代码:
// iconfont字体
import "../static/css/iconfont.css";
import "../static/css/iconfont.eot";
4.设置顶部导航和尾部导航组件
components/common/Header.vue 顶部导航
<template>
<div class="header">
<nav class="navbar">
<div class="width-limit">
<!-- 左上方 Logo -->
<a class="logo" href="/"><img src="/static/image/nav-logo.png" /></a>
<!-- 右上角 -->
<!-- 未登录显示登录/注册/写文章 -->
<a class="btn write-btn" target="_blank" href="/writer"><img class="icon-write" src="/static/image/write.svg">写文章</a>
<a class="btn sign-up" id="sign_up" href="/register">注册</a>
<a class="btn log-in" id="sign_in" href="/login">登录</a>
<div class="container">
<div class="collapse navbar-collapse" id="menu">
<ul class="nav navbar-nav">
<li class="tab active">
<a href="/">
<img class="menu-icon" src="/static/image/menu.svg">
<span class="menu-text">首页</span>
</a>
</li>
<li class="search">
<form target="_blank" action="/search" accept-charset="UTF-8" method="get">
<input type="text" name="q" id="q" value="" autocomplete="off" placeholder="搜索" class="search-input">
<a class="search-btn" href="javascript:void(0)"></a>
</form>
</li>
</ul>
</div>
</div>
<!-- 如果用户登录,显示下拉菜单 -->
</div>
</nav>
</div>
</template>
<script>
export default {
name: "Header"
}
</script>
<style scoped>
.header{
height: 56px;
}
.container {
width: 960px;
margin-right: auto;
margin-left: auto;
padding-left: 15px;
padding-right: 15px;
}
.container:after, .container:before {
content: " ";
display: table;
}
.container:after {
clear: both;
}
.navbar {
background-color: #fff;
border-color: #f0f0f0;
top: 0;
border-width: 0 0 1px;
border-radius: 0;
}
.navbar-nav {
float: left;
margin: 0;
}
.navbar:after, .navbar:before {
content: " ";
display: table;
box-sizing: border-box;
}
.nav:after, .nav:before {
content: " ";
display: table;
}
nav .width-limit {
min-width: 768px;
max-width: 1440px;
margin: 0 auto;
}
nav .logo {
float: left;
height: 56px;
padding: 0;
}
nav .logo img {
height: 100%;
vertical-align: middle;
border: 0;
}
.btn {
display: inline-block;
margin-bottom: 0;
font-weight: 400;
text-align: center;
vertical-align: middle;
touch-action: manipulation;
cursor: pointer;
background-image: none;
border: 1px solid transparent;
white-space: nowrap;
padding: 6px 12px;
font-size: 14px;
line-height: 1.42857;
border-radius: 4px;
}
nav .write-btn {
float: right;
width: 100px;
height: 24px;
line-height: 24px;
margin: 8px 12px 0;
border-radius: 20px;
font-size: 15px;
color: #fff;
background-color: #ea6f5a;
text-decoration: none;
}
nav .log-in, nav .log-in:hover {
color: #969696;
}
nav .log-in {
float: right;
margin: 11px 6px 0 10px;
font-size: 15px;
}
nav .sign-up {
float: right;
width: 80px;
height: 24px;
line-height: 24px;
margin: 9px 5px 0 15px;
border: 1px solid rgba(236,97,73,.7);
border-radius: 20px;
font-size: 15px;
color: #ea6f5a;
background-color: transparent;
}
nav .icon-write {
margin-right: 3px;
width: 19px;
height: 19px;
vertical-align: middle;
}
nav .menu-text{
font-size: 17px;
color: #ea6f5a;
}
nav .menu-icon {
width: 20px;
height: 20px;
vertical-align: sub;
margin-right: 3px;
}
nav .nav .tab a {
height: 56px;
line-height: 26px;
padding: 15px;
color: #ea6f5a;
background: none;
}
nav .navbar-nav li {
margin-right: 10px;
float: left;
position: relative;
display: block;
box-sizing: border-box;
height: 56px;
line-height: 56px;
}
.navbar-nav {
float: left;
margin: 0;
}
nav form {
position: relative;
top: 9px;
margin: 0 0 20px;
box-sizing: border-box;
line-height: 20px;
}
nav form .search-input {
padding: 0 40px 0 20px;
height: 38px;
font-size: 14px;
border: 1px solid #eee;
border-radius: 40px;
background: #eee;
transition: width .5s;
width: 240px;
outline: none;
}
nav form .search-input:focus {
width: 320px;
outline: none;
}
.navbar-default .navbar-collapse, .navbar-default .navbar-form {
border-color: #e7e7e7;
padding-left: 0;
padding-right: 0;
box-sizing: border-box;
width: auto;
border-top: 0;
box-shadow: none;
}
.navbar {
background-color: #fff;
top: 0;
border-radius: 0;
position: fixed;
right: 0;
left: 0;
z-index: 1030;
min-height: 50px;
margin-bottom: 20px;
border-bottom: 1px solid #f0f0f0;
}
nav {
height: 56px;
}
.navbar:after, .navbar:before {
content: " ";
display: table;
}
nav form .search-btn {
position: absolute;
display: block;
top: 0;
right: 10px;
width: 30px;
height: 30px;
padding: 0;
margin: 5px -1px 0 0;
background: transparent url("../../../static/image/search-focus.svg") no-repeat 6px 6px;
background-size: 20px;
}
nav form .search-input:focus~a{
border-radius: 50%;
background-color: #696969;
background-image: url("../../../static/image/search-blur.svg");
}
nav .sign-up:hover {
color: #ec6149;
border-color: #ec6149;
background-color: rgba(236,97,73,.05);
}
nav .write-btn:focus, nav .write-btn:hover {
color: #fff;
background-color: #ec6149;
}
</style>
components/common/Footer.vue 尾部导航
<template>
<footer class="container">
<div class="row">
<div class="main">
<a target="_blank" href="">关于荏苒</a>
<em> · </em>
<a target="_blank" href="">联系我们</a>
<em> · </em>
<a target="_blank" href="">加入我们</a>
<em> · </em>
<a target="_blank" href="">帮助中心</a>
<em> · </em>
<a target="_blank" href="http://www.jianshu.com/p/cabc8fa39830">合作伙伴</a>
<div class="icp">©2016-2019 广州荏苒信息科技有限公司 / 荏苒 / 粤ICP备16018329号-5 /</div>
</div>
</div>
</footer>
</template>
<script>
export default {
name: "Footer"
}
</script>
<style scoped>
.container {
width: 960px;
margin-right: auto;
margin-left: auto;
padding-left: 15px;
padding-right: 15px;
margin-bottom: 20px;
box-sizing: border-box;
}
.container:after, .container:before {
content: " ";
display: table;
}
footer .row {
padding-top: 25px;
box-sizing: border-box;
margin-left: -15px;
margin-right: -15px;
}
footer .main {
padding-right: 0;
font-size: 13px;
color: #969696;
width: 70.83333%;
}
footer .icp, footer .icp a {
color: #c8c8c8;
}
footer .icp {
margin-top: 10px;
font-size: 12px;
}
footer .main a {
color: #969696;
display: inline-block;
}
.row:after {
clear: both;
}
</style>
6. 跨域CORS
1.为前端和后端分别设置两个不同的域名:域名解析
位置 | 域名 |
---|---|
前端 | www.renran.com |
后端 | api.renran.com |
编辑/etc/hosts
文件,可以设置本地域名
sudo vim /etc/hosts
在文件中增加两条信息
127.0.0.1 api.renran.com
127.0.0.1 www.renran.com
2.前端通过网址访问项目
暂停运行前端项目,并修改配置文件config/index.js --- host:
// Various Dev Server settings
host: 'www.renran.com',
port: 8080,
autoOpenBrowser: true,
保存修改信息,并重启项目
3.后端通过网址访问项目
settings/dev.py的ALLOWED_HOSTS,设置允许访问
# 设置哪些客户端可以通过地址访问到后端
ALLOWED_HOSTS = [
'api.renran.com',
]
为让用户访问的时候,使用api.renran.com:8000
修改pycharm的manage.py的配置参数:
Parameters 设置为: runserver api.renran.com:8000
现在,前端与后端分处不同的域名,我们需要为后端添加跨域访问的支持
否则前端无法使用axios无法请求后端提供的api数据,我们使用CORS来解决后端对跨域访问的支持。使用django-cors-headers
4.后端安装django-cors-headers
作用:
后端提供数据给前端使用ajax访问了,使用
1.安装
pip install django-cors-headers
2.后台配置
settings/dev.py,添加应用
INSTALLED_APPS = (
...
'corsheaders',
...
)
中间件设置【必须写在第一个位置】
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
...
]
添加白名单,来自这个网址的请求都正常响应回去
# CORS组的配置信息
CORS_ORIGIN_WHITELIST = (
'http://www.renran:8080', #如果是80端口就不用写端口号
)
CORS_ALLOW_CREDENTIALS = False # 不允许ajax跨域请求时携带cookie
5.前端安装axios
作用:
前端使用 axios就可以访问到后端提供给的数据接口,但是如果要附带cookie信息,前端还要设置一下。
前端引入axios插件并配置允许axios发送cookie信息[axios本身也不允许ajax发送cookie到后端]
安装:
npm i axios -S
前端配置:在main.js中引用 axios插件
import axios from 'axios'; // 从node_modules目录中导入包
// 允许axios发送请求时附带cookie,设置为不允许
axios.defaults.withCredentials = false;
Vue.prototype.$axios = axios; // 把对象挂载vue中