公共的样式:src/assets/style/common.less
公共的js(工具函数,接口地址,配置文件)
创建好项目后,将准备好的静态文件夹(static)放入public/目录下。
在src/assets/下创建style文件夹,并在style文件夹下创建common.less文件。
在src/下创建utils文件夹,并在utils文件夹下分别创建apis.js,constants.js,filters.js文件。
注!需要在main.js中注册全局过滤器到vue实例上:
import * as filters from ‘./utils/filters’
Object.keys(filters).forEach(k => Vue.filter(k, filters[k]))
1.分析首页结构
2.新建页面(views中)
3.新建对应组件(components)
注意思考:组件是否只用于当前页面(在src/components下新建文件夹用来存放对应页面所需的组件),或者组件用于多个页面(在src/components下新建文件夹用来存放公共的组件)
ajax是通过浏览器后台与服务器通信的技术
axios是基于Promise的HTTP库(网络异步请求库),用来发送和处理http请求。
Promise是一种异步编程解决方案。
通过axios异步请求可以得到Promise对象,将它返回的结果进行处理。
1.GET请求
请求完毕后会返回一个响应,通过then函数获取响应(对结果进行处理)。如果返回异常则跳到catch函数。
2.POST请求
url后面的参数是一个js对象,是需要post过去的数据。区别get请求的参数params。
1.了解请求响应拦截的使用场景
2.掌握如何对请求响应做统一的处理
创建axios实例
const ajax = axios.create(options)
options:js对象,例如:
在src/utils/下新建文件ajax.js用来配置请求响应的拦截
在请求发出之前进行拦截
跨域就是浏览器出于安全的考虑,要求你的网站不能执行或者不能访问其他网站的资源。
http://www.baidu.com:80
http(s):协议
www.baidu.com:域名/主机
80:端口(默认)
1.添加配置文件vue.config.js(项目根路径下)
2.添加配置
target:目标地址
changeOrigin:变更请求头中设置的host(以xxx的名义进行访问)
pathRewrite:url的重写规则
例如要访问http://localhost:8080/api/test => 代理http://localhost:8000/test
步骤:
VantUI:开源免费,轻量可靠的移动端Vue组件库。
安装:npm i vant -S
导入所有的组件:
import Vue from ‘vue’;
import Vant from ‘vant’;
import ‘vant/lib/index.css’;
Vue.use(Vant);
接口返回结构
{
"meta": {},
"objects": []
}
接口的错误信息返回结构
{
"error_code": '400000',
"error_meg": "该字段不能为空。",
"error_list": [
"password": [
"该字段不能为空。"
]
]
}
class Slider(models.Model):
""" 轮播图 """
name = models.CharField('名称', max_length=64)
desc = models.CharField('描述信息', max_length=256, null=True, blank=True)
types = models.SmallIntegerField('轮播图类型', default=10)
img = models.ImageField('图片地址', max_length=256, upload_to='%Y%m/slider')
start_time = models.DateTimeField('生效时间', null=True, blank=True)
end_time = models.DateTimeField('失效时间', null=True, blank=True)
reorder = models.SmallIntegerField('排序字段', default=0, help_text='数字越大靠前')
target_url = models.CharField('跳转地址', max_length=256, null=True, blank=True)
is_valid = models.BooleanField('是否有效', default=True)
created_at = models.DateTimeField('创建时间', auto_now_add=True)
updated_at = models.DateTimeField('修改时间', auto_now=True)
class Meta:
db_table = 'system_slider'
# 按reorder降序排列
ordering = ['-reorder']
def slider_list(request):
""" 轮播图接口 """
data = {
"meta": {},
"objects": []
}
query = Q(is_valid=True)
queryset = Slider.objects.filter(query)
for item in queryset:
data['objects'].append({
'id': item.id,
# img字段是ImageField,它有一个属性url,可以访问图片存在磁盘上的路径
'img_url': item.img.url,
'target_url': item.target_url,
'name': item.name,
'types': item.types
})
return JsonResponse(data)
class Sight(models.Model):
""" 景点基础信息 """
name = models.CharField('名称', max_length=64)
desc = models.CharField('描述', max_length=64)
main_img = models.ImageField('主图', upload_to='%Y%m/sight', max_length=512)
banner_img = models.ImageField('详细主图', upload_to='%Y%m/sight', max_length=512)
content = models.TextField('详细信息')
score = models.FloatField('评分', default=5)
province = models.CharField('省份', max_length=32)
city = models.CharField('市区', max_length=32)
area = models.CharField('区/县', max_length=32, null=True, blank=True)
town = models.CharField('乡镇', max_length=32, null=True, blank=True)
min_price = models.FloatField('最低价格', default=0)
is_top = models.BooleanField('是否为精选', default=False)
is_hot = models.BooleanField('是否为热门', default=False)
is_valid = models.BooleanField('逻辑删除', default=True)
created_at = models.DateTimeField('创建时间', auto_now_add=True)
updated_at = models.DateTimeField('修改时间', auto_now=True)
class Meta:
db_table = 'sight'
# 最新更新的排前面
ordering = ['-updated_at']
class SightListView(ListView):
""" 景点列表接口 """
# 分页,每页放5条数据
paginate_by = 5
def get_queryset(self):
""" 重写查询方法 """
query = Q(is_valid=True)
# 查询热门景点
is_hot = self.request.GET.get('is_hot', None)
if is_hot:
query = query & Q(is_hot=True)
# 查询精选景点
is_top = self.request.GET.get('is_top', None)
if is_top:
query = query & Q(is_top=True)
# TODO:按照景点名称搜索
queryset = Sight.objects.filter(query)
return queryset
def render_to_response(self, context, **response_kwargs):
page_obj = context['page_obj']
data = {
"meta": {
# 总共多少条记录
'total_count': page_obj.paginator.count,
# 总共多少页
'page_count': page_obj.paginator.num_pages,
# 当前页码
'current_page': page_obj.number,
},
"objects": []
}
for item in page_obj.object_list:
data['objects'].append({
'id': item.id,
'name': item.name,
'main_img': item.main_img.url,
'score': item.score,
'province': item.province,
'city': item.city,
'min_price': item.min_price,
# TODO:评论数量暂时无法获取
'comment_count': 0
})
return http.JsonResponse(data)
步骤:
const apiHost = "http://localhost:8080/api";
/**系统模块 */
const SystemApis = {
// 轮播图列表接口
sliderListUrl: apiHost + "/system/slider/list/"
};
/**景点模块 */
const SightApis = {
// 景点列表接口
sightListUrl: apiHost + "/sight/sight/list/"
};
export {
SystemApis,
SightApis
};
设置代理解决跨域问题
vue.config.js:
module.exports = {
// 例如要访问http://localhost:8080/api/system/slider/list/ => 代理http://127.0.0.1:8000/system/slider/list/
devServer: {
proxy: {
"/api": {
target: "http://127.0.0.1:8000/",
changeOrigin: true,
pathRewrite: {
"^/api": ""
}
}
}
}
};
安装:
npm install vue-router -S
const routes = [
{ path:'/home',component:Home },
{ paht:'/about',component:About }
]
2.配置路由规则
// 在项目根路径下新建router.js
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.user(VueRouter)
const router = new VueRouter({
routes //相当于routes:routes
})
// 在main.js中配置
new Vue({
el:'#app'
router
})
3.设置路由切换后页面的显示位置
在模板中指定路由切换后组件的渲染位置
<router-view></router-view>
4.在模板中实现路由跳转
<router-link to="/about"></router-link>
设置动态匹配规则
动态路由参数 :name
const router = new VueRouter({
routes: [
{ path:'/detail/:id', component:Detail }
]
})
在组件中获取匹配参数
在JS中获取参数
let id = this.$route.params.id
响应参数变化
watch: {
$route(to, from){
// 对路由变化做出响应
}
}
在组件中获取查询参数
URL中的查询参数(/sight/list?/name=‘xxx’)
let name = this.$route.query.name
this:vue实例
$router:来自main.js中router
页面的前进或后退
this.$router.go(n)
n:正数负数(整数)
-1:返回上次
替换浏览历史记录
this.$router.replace({name:'detail', parmas:{id:'123'}, query:{name:'xxx'}})