项目分析及环境搭建
[TOC]
1. 项目开发基础概念
1.1 企业的web项目类型
-
商城
1.1 B2C 直销商城 商家与会员直接交易 ( Business To Customer )
1.2 B2B 批发商城 商家与商家直接交易
1.3 B2B2C 购物平台 商家和会员在另一个商家提供的平台上面进行交易
1.4 C2B 定制商城 会员向商家发起定制商品的需求,商家去完成。
1.5 O2O 线上线下交易平台
1.6 C2C 二手交易平台
-
门户网站[企业站和门户站]
-
社交网络
-
资讯论坛
-
内部系统
-
个人博客
-
内容收费站
1.2 企业项目开发流程
1.3 立项申请阶段
立项其实就是对产品项目能不能做和怎么做,提出理论基础。大的互联网公司都有比较正规的立项流程。
通常公司内部要研发一款软硬件的产品之前,都要经过市场评估和调研分析,产生一份产品项目立项报告
给公司。
产品项目立项报告一般包含以下内容:
项目概述
需求市场
需求分析和项目建设的必要性
业务分析
总体建设方案
项目风险和风险管理
可行性分析阶段
参考资料:https://www.cnblogs.com/qidaii/p/12786365.html
2. 需求分析
功能:导航菜单、轮播图、退出登录
2.2 登录注册
功能:用户登录、极验验证码、多条件登录、记住密码、短信发送、短信冷却倒计时、jwt认证
2.3 课程列表
功能:课程分类、课程列表、课程多条件筛选展示、课程分类展示、课程分页展示、课程章节课时展示、课程优惠策略
2.4 课程详情
功能:课程信息展示、视频播放、富文本编辑器
2.5 购物车
功能:购物车商品列表、添加商品、删除商品、勾选商品状态、商品结算、订单生成、唯一订单号生成
2.6 商品结算
功能:订单商品信息列表、订单信息展示、积分计算功能、优惠券策略、课程有效期计算、第三方支付平台接口
2.7 购买成功
2.8 个人中心
功能列表:我的订单、订单状态改变
2.9 视频播放
功能:视频加密播放
3. 环境搭建
3.1 创建虚拟环境
mkvirtualenv luffy
如果自己的开发机子中存在多个版本的python,则可以指定解析器的版本
mkvirtualenv luffy -p python3
3.2 相关命令复习
创建虚拟环境: mkvirtualenv 虚拟环境名称
创建虚拟环境(指定python版本): mkvirtualenv -p python3 虚拟环境名称
查看所有虚拟环境: workon+2次tab键
使用虚拟环境: workon 虚拟环境名称
退出虚拟环境: deactivate
删除虚拟环境(必须先退出虚拟环境内部才能删除当前虚拟环境):
rmvirtualenv 虚拟环境名称
其他相关命令:
查看虚拟环境中安装的包: pip freeze 或者 pip list
收集当前环境中安装的包及其版本: pip freeze > requirements.txt
在部署项目的服务器中安装项目使用的模块: pip install -r requirements.txt
提示:
-
虚拟环境只会管理环境内部的模块和python解析器,对于源代码是毫无关系
-
创建虚拟环境需要联网
-
创建成功后, 会自动工作在这个虚拟环境上
-
工作在虚拟环境上, 提示符最前面会出现 “(虚拟环境名称)”
3.3 依赖包安装
pip install django
pip install djangorestframework
pip install PymySQL
pip install Pillow
pip install django-redis
4. 搭建项目
4.1 创建项目
django-admin startproject luffy
效果:
4.3 打开项目
在pycharm中打开项目
设置虚拟环境
启动django项目
效果:
提示:
在pycharm中如果要使用已经创建好的虚拟环境,则必须设置pycharm中的python解释器,设置为虚拟环境中的python。
4.2 调整目录
├── docs/ # 项目相关资料保存目录
├── logs/ # 项目运行时/开发时日志目录
├── manage.py
├── luffy/ # 项目主应用,开发时的代码保存
│ ├── apps/ # 开发者的代码保存目录,以模块[子应用]为目录保存
│ ├── libs/ # 第三方类库的保存目录[第三方组件、模块]
│ ├── settings/
│ ├── dev.py # 项目开发时的本地配置
│ ├── prop.py # 项目上线时的运行配置
│ ├── urls.py # 总路由
│ ├── utils/ # 多个模块[子应用]的公共函数类库[自己开发的组件]
└── scripts/ # 保存项目运营时的脚本文件
4.2.1 分不同环境进行项目配置
开发者本地的环境、目录、数据库密码和线上的服务器都会不一样,所以我们的配置文件可以针对不同的系统分成多分.
-
在项目主应用下,创建一个settings的配置文件存储目录
-
根据线上线下两种情况分别创建2个配置文件 dev.py和prod.py
-
把原来项目主应用的 settings.py配置内容复制2份到dev.py和prod.py里面
-
把原来的settings.py配置文件修改文件名或者删除
新的目录settings:
接下来,就可以根据在manage.py中根据不同的情况导入对应的配置文件了.
4.3 创建代码版本
cd进入到自己希望存储代码的目录路径,并创建本地仓库.git【pycharm直接打开终端就是项目根目录了。无须cd了】 新创建的本地仓库.git是个空仓库
cd 目录路径 git init
4.3.1 配置用户名和邮箱
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 git config --global user.name 'lisi' 2 git config --global user.email 'lisi@163.com'
4.4 在git平台创建工程
公司一般都会有自己的代码仓库,一般都是自己搭建,也有使用第三方提供代码管理平台。
常用的代码管理平台:github、gitee(码云)
如果公司自己搭建的代码管理平台,gitlab框架
1) 创建私有项目库
创建私有空仓库以后的界面:
2)克隆项目到本地
注意: 我们当前项目不需要这个步骤 这个步骤是 当以后我们进入公司里面,参与人家已经在做的项目时,别人已经有仓库了,但是我们是新人加入项目中的,那么我们不需要在自己本地进行git init,直接git clone 复制别人的仓库代码
git clone 仓库地址 注意,如果当前目录下出现git仓库同名目录时,会克隆失败。
3)创建并切换分支到dev
# git branch dev # 创建本地分支dev,dev是自定义 # git checkout dev # 切换本地分支代码 git checkout -b dev # 这里是上面两句代码的简写
git提交
git add 代码目录 # 添加代码到上传队列 git status # 查看当前项目的版本状态 git commit -m '添加项目代码' # 提交代码到本地仓库, -m 表示本次提交的描述
推送到远端
git push origin dev:dev
如果推送代码,出现以下提示: git pull ....,则表示当前本地的代码和线上的代码版本不同.
1. 把线上的代码执行以下命令,拉取到本地,进行同步 git pull 2. 根据提示,移除多余的冲突的文件,也可以删除. 完成这些步骤以后,再次add,commit,push即可.
接下来,我们就把上面创建好的本地项目提交到gitee码云上面
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 .表示当前目录下所有的文件或目录提交到上传队列[上传队列也叫"暂存区"] 2 git add . 3 4 # 把本地上传队列的代码提交到本地仓库 5 git commit -m "项目初始化搭建" 6 7 # 给本地的git版本控制软件设置项目的远程仓库地址 8 git remote add origin https://gitee.com/mooluo/luffyproject.git 9 10 # 提交代码给远程仓库 11 git push -u origin master
扩展: 1. 通过 git status 可以查看当前项目的代码版本状态 2. 通过 git reflog 可以查看代码版本日志[简单格式] 3. 通过 git log 可以查看代码版本日志[详细格式]
最终,成功提交了代码版本到gitee平台。效果:
上面虽然成功移交了代码版本,但是一些不需要的文件也被提交上去了。
所以,我们针对一些不需要的文件,可以选择从代码版本中删除,并且使用.gitignore
把这些垃圾文件过滤掉。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 git rm 文件 # 删除单个文件 2 git rm -rf 目录 # 递归删除目录 3 4 # 以下操作建议通过ubuntu的终端来完成,不要使用pycharm提供,否则删除.idea(编辑器)还会继续生成。 5 git rm -rf .idea 6 git rm db.sqlite3 7 # 注意,上面的操作只是从项目的源代码中删除,但是git是不知情的,所以我们需要同步。 8 git add . 9 git commit -m "删除不必要的文件或目录" 10 git push -u origin master
使用.gitignore
把一些垃圾文件过滤掉。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 vim .gitignore 2 3 ./idea 4 ./idea/* 5 ./git 6 ./db.sqlite3 7 8 git add . 9 git commit -m '' 10 git push origin master
开发时我们经常会使用pycharm的提供的git管理工具来完成代码的拉取和推送。
4.5 日志配置
在settings/dev.py文件中追加如下配置:
https://docs.djangoproject.com/zh-hans/3.0/topics/logging/
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 LOGGING = { 2 'version': 1, 3 'disable_existing_loggers': False, 4 5 'formatters': { 6 'verbose': { 7 'format': '%(levelname)s %(asctime)s %(module)s %(lineno)d %(message)s' 8 }, 9 'simple': { 10 'format': '%(levelname)s %(module)s %(lineno)d %(message)s' 11 }, 12 }, 13 'filters': { 14 'require_debug_true': { 15 '()': 'django.utils.log.RequireDebugTrue', 16 }, 17 }, 18 'handlers': { 19 'console': { 20 'level': 'DEBUG', 21 'filters': ['require_debug_true'], 22 'class': 'logging.StreamHandler', 23 'formatter': 'simple' 24 }, 25 'file': { 26 'level': 'INFO', 27 'class': 'logging.handlers.RotatingFileHandler', 28 #日志位置,日志文件名,日志保存目录必须手动创建 29 'filename': os.path.join(os.path.dirname(BASE_DIR), "logs/luffy.log"), 30 #日志文件的最大值,300MB 31 'maxBytes': 300 * 1024 * 1024, 32 #日志文件的数量,最大数量为10 33 'backupCount': 10, 34 'formatter': 'verbose' 35 }, 36 }, 37 #日志对象 38 'loggers': { 39 'luffy': { 40 'handlers': ['console', 'file'], 41 'propagate': True,#是否让日志信息冒泡给其他的日志处理器 42 }, 43 } 44 }
4.6 异常处理
新建utils/exceptions.py
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 from rest_framework.views import exception_handler 2 3 from django.db import DatabaseError 4 from rest_framework.response import Response 5 from rest_framework import status 6 7 import logging 8 logger = logging.getLogger('luffy') 9 10 11 def custom_exception_handler(exc, context): 12 """ 13 自定义异常处理 14 :param exc: 异常对象 15 :param context: 抛出异常的上下文环境[异常发生时的相关信息] 16 :return: Response响应对象 17 """ 18 # 调用drf框架原生的异常处理方法 19 response = exception_handler(exc, context) 20 21 if response is None: 22 #获取异常发生时的视图对象 23 view = context['view'] 24 if isinstance(exc, DatabaseError): 25 # 数据库异常 26 logger.error('[%s] %s' % (view, exc)) 27 response = Response({'message': '服务器内部错误'}, status=status.HTTP_507_INSUFFICIENT_STORAGE) 28 29 return response
settings.py配置文件中添加
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 REST_FRAMEWORK = { 2 3 # 异常处理 4 'EXCEPTION_HANDLER': 'luffy.utils.exceptions.custom_exception_handler', 5 }
4.7 创建数据库
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 create database luffycity default charset=utf8;
为当前项目创建数据库用户[这个用户只能看到这个数据库]
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 create user luffy_user identified by 'luffy'; 2 grant all privileges on luffycity.* to 'luffy_user'@'%'; 3 flush privileges;
4.8 配置数据库连接
打开settings/dev.py文件,并配置
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 DATABASES = { 2 "default": { 3 "ENGINE": "django.db.backends.mysql", 4 "HOST": "127.0.0.1", 5 "PORT": 3306, 6 "USER": "luffy_user", 7 "PASSWORD": "luffy", 8 "NAME": "luffycity", 9 } 10 }
在项目主模块的 __init__.py
中导入pymysql
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 import pymysql 2 3 pymysql.install_as_MySQLdb()
调整错误
1.数据库版本检测导致的错误
数据库的版本检测代码注释
第二个错误也是因为数据库版本的默认编码导致,query返回的内容格式使用有误。
新增一行代码,把query查询结果转换格式为 bytes类型
5. 搭建前端项目
5.1 创建项目目录
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 cd 项目目录 2 vue init webpack lufei_pc
例如,我要把项目保存在D:/moluo的根目录下,可以如下操作:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 D: 2 cd /moluo 3 vue init webpack lufei_pc
根据需要在生成项目时,我们选择对应的选项, 效果:
根据上面的提示,我们已经把vue项目构建好了,接下来我们可以在pycharm编辑器中把项目打开并根据上面黄色提示,运行测试服务器。
打开项目已经,在pycharm的终端下运行vue项目,查看效果。
npm run dev
效果:
接下来,我们根据终端上效果显示的对应地址来访问项目(如果有多个vue项目在运行,8080端口被占据了,服务器会自动改端口,所以根据自己实际在操作中看到的地址来访问。)
我们也可以把我们的前端项目进行git源代码管理
5.2 初始化前端项目
清除默认的HelloWorld组件和APP.vue中的默认样式
接下来,我们可以查看效果了,一张白纸~
5.3 安装路由vue-router
5.3.1 下载路由组件
npm i vue-router -S
执行效果:
5.3.2 配置路由
5.3.2.1 初始化路由对象
在src目录下创建routers路由目录,在routers目录下创建index.js路由文件
index.js路由文件中,编写初始化路由对象的代码 .
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 import Vue from "vue" 2 import Router from "vue-router" 3 4 // 这里导入可以让让用户访问的组件 5 6 Vue.use(Router); 7 8 export default new Router({ 9 // 设置路由模式为‘history’,去掉默认的# 10 mode: "history", 11 routes:[ 12 // 路由列表 13 14 ] 15 })
5.3.2.2 注册路由信息
打开main.js文件,把router对象注册到vue中.代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 // The Vue build version to load with the `import` command 2 // (runtime-only or standalone) has been set in webpack.base.conf with an alias. 3 import Vue from 'vue' 4 import App from './App' 5 import router from './routers/index'; 6 7 Vue.config.productionTip = false 8 9 /* eslint-disable no-new */ 10 new Vue({ 11 el: '#app', 12 router, 13 components: { App }, 14 template: '<App/>' 15 });
5.3.2.3 在视图中显示路由对应的内容
在App.vue组件中,添加显示路由对应的内容。
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <template> 2 <div id="app"> 3 <router-view/> 4 </div> 5 </template> 6 7 <script> 8 export default { 9 name: 'App', 10 components: { 11 12 } 13 } 14 </script> 15 16 <style> 17 18 </style>
5.2.2.4 创建并提供前端首页的组件
routers/index.js
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 // import Vue from "vue" 2 // import Router from "vue-router" 3 // 4 // 5 // // 这里导入可以让让用户访问的组件 6 import Home from "../components/Home" 7 // Vue.use(Router); 8 // 9 // export default new Router({ 10 // // 设置路由模式为‘history’,去掉默认的# 11 // mode: "history", 12 // routes:[ 13 // // 路由列表 14 { 15 name:"Home", 16 path:"/", 17 component:Home, 18 }, 19 { 20 name:"Home", 21 path:"/home", 22 component:Home, 23 }, 24 ] 25 // })
创建Home组件
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <template> 2 <div id="home"> 3 前端首页 4 </div> 5 </template> 6 <script> 7 export default { 8 name:"Home", 9 data(){ 10 return { 11 12 } 13 } 14 } 15 </script> 16 17 <style scoped> 18 19 </style>
5.4 前端初始化全局变量和全局方法
在src目录下创建settings.js站点开发配置文件:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 export default { 2 Host:"http://127.0.0.1", 3 } 4
在main.js中引入
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 // // The Vue build version to load with the `import` command 2 // // (runtime-only or standalone) has been set in webpack.base.conf with an alias. 3 // import Vue from 'vue' 4 // import App from './App' 5 // import router from './routers/index'; 6 import settings from "./settings" 7 // Vue.config.productionTip = false; 8 Vue.prototype.$settings = settings; 9 // 10 // /* eslint-disable no-new */ 11 // new Vue({ 12 // el: '#app', 13 // router, 14 // components: { App }, 15 // template: '<App/>' 16 // }); 17
5.5 引入ElementUI
npm i element-ui -S
上面的命令等同于
npm install element-ui --save
执行命令效果:
5.5.1 配置ElementUI到项目中
在main.js中导入ElementUI,并调用。
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 // The Vue build version to load with the `import` command 2 // (runtime-only or standalone) has been set in webpack.base.conf with an alias. 3 // import Vue from 'vue' 4 // import App from './App' 5 // import router from './routers/index'; 6 7 // 开发配置文件 8 // import settings from "./settings" 9 // Vue.prototype.$settings = settings; 10 11 // elementUI 导入 12 import ElementUI from 'element-ui'; 13 import 'element-ui/lib/theme-chalk/index.css'; 14 // 调用插件 15 Vue.use(ElementUI); 16 17 18 // Vue.config.productionTip = false; 19 20 /* eslint-disable no-new */ 21 // new Vue({ 22 // el: '#app', 23 // router, 24 // components: { App }, 25 // template: '<App/>' 26 // });
效果:
成功引入了ElementUI以后,接下来我们就可以开始进入前端页面开发,首先是首页。
接下来我们把之前完成的首页,直接拿过来使用[注意除了组件以外,还有静态文件也需要拿过来,包括App.vue里面的公共样式],并运行项目。
App.vue,全局css初始化代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <style> 2 body,h1,h2,h3,h4,h5,ul,p{ 3 padding: 0; 4 margin:0; 5 font-weight: normal; 6 } 7 body{ 8 margin-top: 80px; 9 } 10 a{ 11 text-decoration: none; 12 color: #4a4a4a; 13 } 14 a:hover{ 15 color: #000; 16 } 17 18 ul{ 19 list-style: none; 20 } 21 22 img{ 23 width: 100%; 24 } 25 26 .header .el-menu li .el-submenu__title{ 27 height: 26px!important; 28 line-height: 26px!important; 29 } 30 .el-menu--popup{ 31 min-width: 140px; 32 } 33 .el-checkbox__inner{ 34 width:16px; 35 height: 16px; 36 border: 1px solid #999; 37 } 38 .el-checkbox__inner:after{ 39 width: 6px; 40 height: 8px; 41 } 42 .el-form-item__content{ 43 margin-left:0px!important; 44 width: 120px; 45 } 46 </style>
Home.vue中添加代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <template> 2 <div id="home"> 3 <Header/> 4 <Banner/> 5 <Footer/> 6 </div> 7 </template> 8 9 <script> 10 import Header from "./common/Header" 11 import Banner from "./common/Banner" 12 import Footer from "./common/Footer" 13 14 export default { 15 name:"Home", 16 data(){ 17 return { 18 19 } 20 }, 21 components:{ 22 Header, 23 Banner, 24 Footer, 25 } 26 } 27 </script> 28 29 <style scoped> 30 31 </style>
components/common/Header.vue
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <template> 2 <div class="header"> 3 <el-container> 4 <el-header> 5 <el-row> 6 <el-col class="logo" :span="3"> 7 <a href="/"> 8 <img src="@/assets/head-logo.svg" alt=""> 9 </a> 10 </el-col> 11 <el-col class="nav" :span="16"> 12 <el-row> 13 <el-col :span="3"><router-link class="current" to="/course">免费课</router-link></el-col> 14 <el-col :span="3"><router-link to="/">轻课</router-link></el-col> 15 <el-col :span="3"><router-link to="/">学位课</router-link></el-col> 16 <el-col :span="3"><router-link to="/">题库</router-link></el-col> 17 <el-col :span="3"><router-link to="/">教育</router-link></el-col> 18 </el-row> 19 </el-col> 20 <el-col class="login-bar" :span="5"> 21 <el-row v-if="token"> 22 <el-col class="cart-ico" :span="9"> 23 <router-link to=""> 24 <b class="goods-number">0</b> 25 <img class="cart-icon" src="@/assets/cart.svg" alt=""> 26 <span><router-link to="/cart">购物车</router-link></span> 27 </router-link> 28 </el-col> 29 <el-col class="study" :span="8" :offset="2"><router-link to="">学习中心</router-link></el-col> 30 <el-col class="member" :span="5"> 31 <el-menu class="el-menu-demo" mode="horizontal"> 32 <el-submenu index="2"> 33 <template slot="title"><router-link to=""><img src="@/assets/logo@2x.png" alt=""></router-link></template> 34 <el-menu-item index="2-1">我的账户</el-menu-item> 35 <el-menu-item index="2-2">我的订单</el-menu-item> 36 <el-menu-item index="2-3">我的优惠卷</el-menu-item> 37 <el-menu-item index="2-3">退出登录</el-menu-item> 38 </el-submenu> 39 </el-menu> 40 </el-col> 41 </el-row> 42 <el-row v-else> 43 <el-col class="cart-ico" :span="9"> 44 <router-link to=""> 45 <img class="cart-icon" src="@/assets/cart.svg" alt=""> 46 <span><router-link to="/cart">购物车</router-link></span> 47 </router-link> 48 </el-col> 49 <el-col :span="10" :offset="5"> 50 <span class="register"> 51 <router-link to="/login">登录</router-link> 52 | 53 <router-link to="/register">注册</router-link> 54 </span> 55 </el-col> 56 </el-row> 57 </el-col> 58 </el-row> 59 </el-header> 60 </el-container> 61 </div> 62 </template> 63 64 <script> 65 export default { 66 name: "Header", 67 data(){ 68 return { 69 // 设置一个登录标识,表示是否登录 70 token: false, 71 }; 72 } 73 } 74 </script> 75 76 <style scoped> 77 .header{ 78 top:0; 79 left:0; 80 right:0; 81 margin: auto; 82 background-color: #fff; 83 height: 80px; 84 z-index: 1000; 85 position: fixed; 86 box-shadow: 0 0.5px 0.5px 0 #c9c9c9; 87 } 88 .header .el-container{ 89 width: 1200px; 90 margin: 0 auto; 91 } 92 .el-header{ 93 height: 80px!important; 94 padding:0; 95 } 96 .logo{ 97 98 } 99 .logo img{ 100 padding-top: 22px; 101 } 102 103 .nav{ 104 margin-top: 22px; 105 } 106 107 .nav .el-col a{ 108 display: inline-block; 109 text-align: center; 110 padding-bottom: 16px; 111 padding-left: 5px; 112 padding-right: 5px; 113 position: relative; 114 font-size: 16px; 115 margin-left: 20px; 116 } 117 118 .nav .el-col .current{ 119 color: #4a4a4a; 120 border-bottom: 4px solid #ffc210; 121 } 122 123 .login-bar{ 124 margin-top: 22px; 125 } 126 .cart-ico{ 127 position: relative; 128 border-radius: 17px; 129 } 130 .cart-ico:hover{ 131 background: #f0f0f0; 132 } 133 .goods-number{ 134 width: 16px; 135 height: 16px; 136 line-height: 17px; 137 font-size: 12px; 138 color: #fff; 139 text-align: center; 140 background: #fa6240; 141 border-radius: 50%; 142 transform: scale(.8); 143 position: absolute; 144 left: 16px; 145 top: -1px; 146 } 147 .cart-icon{ 148 width: 15px; 149 height: auto; 150 margin-left: 6px; 151 } 152 .cart-ico span{ 153 margin-left: 12px; 154 } 155 .member img{ 156 width: 26px; 157 height: 26px; 158 border-radius: 50%; 159 display: inline-block; 160 } 161 .member img:hover{ 162 border: 1px solid yellow; 163 } 164 165 </style>
components/common/Bannner.vue
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <template> 2 <div class="banner"> 3 <el-carousel trigger="click" height="473px"> 4 <el-carousel-item v-for="banner in banner_list"> 5 <a :href="banner.link"><img width="100%" :src="banner.img" alt=""></a> 6 </el-carousel-item> 7 </el-carousel> 8 </div> 9 </template> 10 11 <script> 12 export default { 13 name:"Banner", 14 data(){ 15 return { 16 banner_list:[ 17 {link:"http://www.baidu.com",img:"/static/banner/1.png"}, 18 {link:"http://www.baidu.com",img:"/static/banner/2.png"}, 19 {link:"http://www.baidu.com",img:"/static/banner/3.png"}, 20 ] 21 }; 22 } 23 } 24 </script> 25 26 <style> 27 .el-carousel__arrow{ 28 width: 100px!important; 29 height: 100px!important; 30 } 31 .el-icon-arrow-left{ 32 font-size: 35px; 33 margin-left: 50px; 34 } 35 .el-carousel__arrow--left{ 36 left: -50px; 37 } 38 </style> 39
components/common/Footer.vue
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <template> 2 <div class="footer"> 3 <el-container> 4 <el-row> 5 <el-col :span="4"><router-link to="">关于我们</router-link></el-col> 6 <el-col :span="4"><router-link to="">联系我们</router-link></el-col> 7 <el-col :span="4"><router-link to="">商务合作</router-link></el-col> 8 <el-col :span="4"><router-link to="">帮助中心</router-link></el-col> 9 <el-col :span="4"><router-link to="">意见反馈</router-link></el-col> 10 <el-col :span="4"><router-link to="">新手指南</router-link></el-col> 11 <el-col :span="24"><p class="copyright">Copyright © luffycity.com版权所有 | 京ICP备17072161号-1</p></el-col> 12 </el-row> 13 </el-container> 14 </div> 15 </template> 16 17 <script> 18 export default { 19 name:"Footer", 20 data(){ 21 return {} 22 } 23 } 24 </script> 25 26 27 <style scoped> 28 .footer{ 29 width: 100%; 30 height: 128px; 31 background: #25292e; 32 } 33 .footer .el-container{ 34 width: 1200px; 35 margin: auto; 36 } 37 .footer .el-row { 38 align-items: center; 39 padding: 0 200px; 40 padding-bottom: 15px; 41 width: 100%; 42 margin-top: 38px; 43 } 44 .footer .el-row a{ 45 color: #fff; 46 font-size: 14px; 47 } 48 .footer .el-row .copyright{ 49 text-align: center; 50 color: #fff; 51 font-size: 14px; 52 } 53 </style> 54
也可以把App.vue的style标签的css代码放到static外部目录下引用过来
main.js
import "../static/css/reset.css";
reset.css
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 body,h1,h2,h3,h4,h5,ul,p{ 2 padding: 0; 3 margin:0; 4 font-weight: normal; 5 } 6 body{ 7 margin-top: 80px; 8 } 9 a{ 10 text-decoration: none; 11 color: #4a4a4a; 12 } 13 a:hover{ 14 color: #000; 15 } 16 17 ul{ 18 list-style: none; 19 } 20 21 img{ 22 width: 100%; 23 } 24 25 .header .el-menu li .el-submenu__title{ 26 height: 26px!important; 27 line-height: 26px!important; 28 } 29 .el-menu--popup{ 30 min-width: 140px; 31 } 32 .el-checkbox__inner{ 33 width:16px; 34 height: 16px; 35 border: 1px solid #999; 36 } 37 .el-checkbox__inner:after{ 38 width: 6px; 39 height: 8px; 40 } 41 .el-form-item__content{ 42 margin-left:0px!important; 43 width: 120px; 44 } 45
6. 跨域CORS
我们现在为前端和后端分别设置两个不同的域名:
位置 | 域名 |
---|---|
前端 | www.luffycity.cn |
后端 | api.luffycity.cn |
编辑/etc/hosts
文件,可以设置本地域名
sudo vim /etc/hosts
在文件中增加两条信息
127.0.0.1 localhost
127.0.0.1 api.luffycity.cn
127.0.0.1 www.luffycity.cn
![](https://img2020.cnblogs.com/blog/1729992/202004/1729992-20200427180859737-913736229.png)
通过浏览器访问前端vue项目,会出现nginx的欢迎页面,主要因为我们当前项目已经有一个nginx监听了80端口,所以访问www.luffycity.cn网址时,会自动被转发到127.0.0.1本机,因为没有网址默认端口是80端口,所以被nginx进行处理了当前请求,因此我们暂时先把nginx关闭先。
# 查找nginx的进程
ps -ef|grep nginx
# 关闭进程
sudo kill -9 nginx进程号
关闭了nginx以后,访问www.luffycirty.cn网址,效果:
上面并不是错误,而是没人监听了这个地址和端口了,解决方法:
暂停运行前端项目,并修改配置文件config/index.js
host: 'www.luffycity.cn', // can be overwritten by process.env.HOST
port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
autoOpenBrowser: true,
保存修改信息,并重启项目
通过浏览器访问drf项目,会出现以下错误信息
可以通过settings/dev.py的ALLOWED_HOSTS,设置允许访问
# 设置哪些客户端可以通过地址访问到后端
ALLOWED_HOSTS = [
'api.luffycity.cn',
]
让用户访问的时候,使用api.luffycity.cn:8000
1. 修改pycharm的manage.py的配置参数
现在,前端与后端分处不同的域名,我们需要为后端添加跨域访问的支持
否则前端无法使用axios无法请求后端提供的api数据,我们使用CORS来解决后端对跨域访问的支持。
使用django-cors-headers扩展,类似
在 Response(headers={"Access-Control-Allow-Origin":'客户端地址/*'})
文档:https://github.com/ottoyiu/django-cors-headers/
安装
pip install django-cors-headers
添加应用
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 INSTALLED_APPS = ( 2 ... 3 'corsheaders', 4 ... 5 )
中间层设置【必须写在第一个位置】
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 MIDDLEWARE = [ 2 'corsheaders.middleware.CorsMiddleware', 3 ... 4 ]
添加白名单
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 # CORS组的配置信息 2 CORS_ORIGIN_WHITELIST = ( 3 'www.luffycity.cn:80', 4 ) 5 CORS_ALLOW_CREDENTIALS = True # 允许ajax跨域请求时携带cookie
![](https://img2020.cnblogs.com/blog/1729992/202004/1729992-20200427183006912-961602672.png)
完成了上面的步骤,我们就可以通过后端提供数据给前端使用ajax访问了。
前端使用 axios就可以访问到后端提供给的数据接口,但是如果要附带cookie信息,前端还要设置一下。
前端引入axios插件并配置允许axios发送cookie信息[axios本身也不允许ajax发送cookie到后端]
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 npm i axios -S
在main.js中引用 axios插件
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 import axios from 'axios'; // 从node_modules目录中导入包 2 // 允许ajax发送请求时附带cookie 3 axios.defaults.withCredentials = true; 4 5 Vue.prototype.$axios = axios; // 把对象挂载vue中