写这个代码的收获:
1: http 请求 https 有一个证书验证(我这里给它关了,接口的数据就进来了)
2: 对后端的一些参数过滤(给默认值, 或者直接拒绝服务)
代码演示效果:
1: 前端VUE布局代码:
<template>
<!--
1: 先把单个菜品写死,以便查看渲染后的效果
2: 再把菜单的列表渲染出来
3: 添加 查询 【 菜谱搜索, , 】 (【菜谱分类,直接写到vue json 里面,省得数据多)
-->
<div class="content headContainer">
<el-card :span="12">
<div class="alignR" :class="{unfold: isCollapsed}">
<el-button type="success" round style="text-align: right;" @click="unfold">继续看该菜谱</el-button>
</div>
<!-- 单个菜品展示 -->
<div v-if="singleRecipeBool">
<div class="listEles" v-show="isCollapsed">
<el-card :span="4">
<div class="recipeContent">
<div class="classfiy">
<div class="classF">分类: {
{ singleRecipeData.classid }}</div>
</div>
<h1>{
{ singleRecipeData.name }}</h1>
<div class="forHowMany">
<div class="forH">{
{ singleRecipeData.peoplenum }}份</div>
</div>
<p class="authSays">{
{ singleRecipeData.content }}</p>
<div class="durationTime">
<img src="/static/imgs/clock.jpg" alt="" class="clock">
<p>煮制时间: {
{ singleRecipeData.cookingtime }}</p>
</div>
<div class="material">
<p>原料共: {
{ singleRecipeData.material.length }} 种</p>
<el-table :data="singleRecipeData.material" border stripe :header-cell-style="{fontSize: '1.35rem', color: 'black'}" align="center" class="materialOl">
<el-table-column type="index" align="center" width="40"></el-table-column>
<el-table-column prop="mname" label="食材" align="center" width="180"></el-table-column>
<el-table-column prop="type" label="类型" align="center" width="140"></el-table-column>
<el-table-column prop="amount" label="用量" align="center" ></el-table-column>
</el-table>
</div>
<p class="prepareTime">准备时间: {
{ singleRecipeData.preparetime }}</p>
<div class="process">
<div class="processCard">
<el-card>
<div slot="header" class="clearfix">
<span class="processCardTitle">制作步骤({
{ singleRecipeData.process.length }})</span>
</div>
<div v-for="(p, index) in singleRecipeData.process" :key="index">
<div class="recipe_step">
<div class="recipe_step_num"><strong>步骤</strong> <em>step</em> <p>{
{ index + 1 }}</p></div>
<div class="stepContent">
<p>{
{ p.pcontent }}</p>
<img :src="p.pic" :alt="p.pcontent">
</div>
</div>
</div>
</el-card>
</div>
<p class="recipeTag">
<span>标签:</span>
<el-tag v-for = "(t, index) in tags" class="recipeTag" :key="index">{
{ t }}</el-tag>
</p>
</div>
</div>
</el-card>
<!-- 返回顶部使用,target 对应上级包裹的 class -->
<el-backtop target=".listEles">
<i class="el-icon-caret-top"></i>
<!-- 添加 stop,防止冒泡事件 -->
<div class="toHide" @click.stop="hideAndShow">{
{ toHideText }}</div>
</el-backtop>
</div>
</div>
<!-- 菜品列表 + 查询 -->
<div class="showEle">
<!-- 菜谱查询 -->
<div class="queryRecipe">
<el-input placeholder="查找菜谱" class="queryRecipeIpt" v-model="queryRecipeText"></el-input>
<el-button type="success" @click="queryRecipe">查找</el-button>
</div>
<!-- 菜谱分类 -->
<div class="effectCategory">
<div class="recipeList">
<el-select v-for="(e, index) in effectArr" :key="index" v-model="e.name" :placeholder="e.name" @change="selectEffect(e.classid)" class="recipeSelect">
<el-option v-for="(item, index) in e.list" :key="index" :label="item.name" :value="item.name"></el-option>
</el-select>
</div>
</div>
<!-- 菜品列表 -->
<el-card :span="6">
<div class="smallList">
<div v-for="(d, index) in data" :key="index" class="samllItem" @click="showRecipe(index)">
<img :src="d.process[0].pic" alt="">
<p>{
{ d.name }}</p>
</div>
</div>
<div class="pagination">
<el-pagination :current-page.sync="currentPage" :page-size="pageSize" :total="totalPage" @size-change="handleSizeChange" @current-change="handleCurrentChange" layout="total, prev, pager, next"></el-pagination>
</div>
</el-card>
</div>
</el-card>
</div>
</template>
<script>
export default {
name: 'idioms',
mounted: function () {
this.getClass()
this.getData()
},
data () {
return {
apiUrl: '/api_9', // API 默认首页接口
data: [], // 列表数据
toHideText: '收起', // 收起文字
isCollapsed: true, // 是否收起
singleRecipeData: '', // 单个菜品
singleRecipeBool: false,
tags: [], // 处理后的tags
queryRecipeText: '', // 查询菜谱的内容
effectArr: [], // 功效分类
effectArrCopy: [], // 功效分类复制一份
effectChoose: '', // 用户选择的功效
currentPage: 1, // 当前页码
pageSize: 0, // 一页多少条数据【默认是10】
totalPage: 0, // 总页数
currentType: 'd', // 当前菜谱查看模式【 d: 默认, a: 查询, c: 分类 】
isSearch: true, // 是否搜索查询,用于搜索页和分类查询页,是否从第一页看起
startNmb: 0, // 第几条数据开始
queryType: 'd', // 搜索类型,默认d
queryClass: 0 // 当前查找类型
}
},
methods: {
getClass: function () {
// 获取分类
this.$axios.get('/static/json/recipeClass.json')
.then(res => {
if (res.status === 200) {
let result = res.data.result
this.effectArr = result.result
for (let i = 0; i < this.effectArr.length; i++) {
this.effectArrCopy[i] = this.effectArr[i].name
}
}
}).catch(err => {
console.log(err)
})
},
getData: function (urlType, startNmb, recipeName, classId) {
// 获取数据 [分类, 起始值 ]
if (urlType === undefined || startNmb === undefined) {
urlType = this.currentType
startNmb = 0
}
this.$axios({
url: this.apiUrl,
params: {
urlType: urlType,
startNmb: startNmb,
recipeName: recipeName,
classId: classId
},
methods: 'get'
}).then(res => {
if (res.data.code === '10000') {
let data = res.data.result.result
this.totalPage = data.total
this.pageSize = data.num
this.data = data.list
} else {
console.log('something err' + res)
}
}).catch(err => {
console.log(err)
})
},
hideAndShow: function () {
// 收起
this.isCollapsed = false
},
unfold: function () {
// 展开
this.isCollapsed = true
},
showRecipe: function (index) {
// 展开单个菜谱,并把该菜品的 TAGS 处理一下
this.singleRecipeData = this.data[index]
let tags = this.singleRecipeData.tag
this.tags = tags.split(',')
this.singleRecipeBool = true
this.isCollapsed = true
},
queryRecipe: function () {
// 查找
// 搜索接口 https://way.jd.com/jisuapi/search?keyword=白菜&num=10&start=0&appkey=您申请的APPKEY
if (!this.isSearch) {
// 判断有没有换另外一种搜索方式
this.startNmb = 0
this.isSearch = true
}
this.queryType = 's'
let searchType = this.queryType
if (this.queryRecipeText.trim() === '') {
return alert('请输入文字')
}
this.getData(searchType, this.startNmb, this.queryRecipeText.trim())
},
selectEffect: function (classId) {
// 选择菜谱功能 【 分类id 】
// 分类接口 https://way.jd.com/jisuapi/byclass?classid=2&start=0&num=10&appkey=您申请的APPKEY
if (this.isSearch) {
// 判断有没有换另外一种搜索方式
this.isSearch = false
this.startNmb = 0
}
this.queryType = 'c'
let searchType = this.queryType
this.queryClass = classId
this.getData(searchType, this.startNmb, null, classId)
},
handleSizeChange (val) {
// 一页多少条数据变化
console.log(`${
val} items per page`)
},
handleCurrentChange (val) {
// 当前页码变化函数
let startNmb = val * 10
if (this.isSearch) {
// 搜索模式就传递词,否则传递分类ID
this.getData(this.queryType, startNmb, this.queryRecipeText.trim(), 0)
} else {
this.getData(this.queryType, startNmb, null, this.queryClass)
}
}
}
}
</script>
<style scoped>
.listEles {
height: 100vh;
overflow-x: hidden;
}
.content {
margin-left: 9%;
}
.showEle {
text-align: center;
}
.recipeContent {
text-align: center;
}
.recipeContent .classfiy {
width: 8rem;
height: 8rem;
overflow: hidden;
margin: 0 auto;
margin-top: -4rem;
}
.recipeContent .classfiy .classF {
width: 8rem;
height: 8rem;
border-top-left-radius: 4rem;
border-top-right-radius: 4rem;
background-color: #ffe851;
line-height: 3.5rem;
margin-top: 5rem;
font-weight: bolder;
}
.recipeContent h1 {
min-width: 10rem;
max-width: 20rem;
background-color: #ffe851;
border-radius: 50rem;
margin: 0 auto;
padding: 0.5rem 0;
position: relative;
color: red;
}
.recipeContent .forHowMany {
height: 5rem;
overflow: hidden;
}
.recipeContent .forH {
width: 8rem;
height: 8rem;
border-bottom-left-radius: 4rem;
border-bottom-right-radius: 4rem;
background-color: #ffe851;
margin: -5rem auto 0 auto;
line-height: 12rem;
font-weight: bolder;
}
.recipeContent .authSays, .durationTime {
text-align: left;
text-indent: 2rem;
}
.recipeContent .authSays {
display: block;
width: 50rem;
margin: 0 auto;
}
.recipeContent .durationTime {
display: flex;
justify-content: center;
margin: 1rem 0;
}
.recipeContent .durationTime img {
width: 2rem;
}
.recipeContent .durationTime p {
margin-top: 0.7rem;
font-weight: bolder;
color: coral;
}
.recipeContent .authSays {
font-size: 1.5rem;
}
.recipeContent .material .materialOl {
margin: 0 auto;
width: 40%;
}
.recipeContent .prepareTime {
margin: 1rem 0;
}
.recipeContent .processCard {
text-align: left;
width: 50rem;
margin: 0 auto;
}
.recipeContent .processCard .recipe_step {
display: flex;
justify-content: lfet;
}
.recipeContent .processCard .recipe_step .recipe_step_num {
height: 6rem;
width: 5rem;
background: #ffe851;
border-radius: 1.5rem;
border-bottom-right-radius: 2px;
}
.recipeContent .processCard .recipe_step .recipe_step_num strong {
font-size: 1.4rem;
text-align: right;
color: #000;
font-weight: bolder;
line-height: 1.1rem;
margin-top: 1rem;
display: block;
padding-right: 1rem;
}
.recipeContent .processCard .recipe_step .recipe_step_num em {
font-size: 1rem;
text-align: right;
color: #000;
font-weight: bolder;
line-height: 1.5rem;
display: block;
padding-right: 1.3rem;
}
.recipeContent .processCard .recipe_step .recipe_step_num p {
font-size: 2.7rem;
text-align: right;
color: #000;
font-weight: bolder;
line-height: 2rem;
display: block;
padding-right: 1.5rem;
}
.recipeContent .processCard .recipe_step .stepContent {
margin-left: 3rem;
}
.recipeContent .processCard .recipe_step .stepContent p{
font-weight: bolder;
}
.recipeContent .processCard .recipe_step .stepContent img{
margin: 0.5rem 0 2rem 0;
}
.recipeContent .processCard .processCardTitle {
font-weight: bolder;
font-size: 2.3rem;
}
.recipeContent .recipeTag {
margin-top: 1rem;
text-align: right;
font-weight: bolder;
}
/* 小图片 + 标题 列表*/
.smallList {
display: flex;
justify-content: left;
flex-wrap: wrap;
}
.smallList .samllItem {
width: 18rem;
height: 18rem;
margin-bottom: 2rem;
cursor: pointer;
}
.smallList .samllItem img{
width: 16rem;
height: 16rem;
}
.smallList .samllItem p{
font-weight: bolder;
font-size: 1.2rem;
}
/* 返回顶部 */
.toHide {
width: 3rem;
height: 3rem;
position: absolute;
top: 3rem;
}
/* 展开按钮 */
.unfold {
display: none;
}
.alignR {
text-align: right;
}
/* 菜谱查询 */
.queryRecipe {
text-align: left;
margin: 2rem 0 0 2rem;
}
.queryRecipe .queryRecipeIpt {
width: 8rem;
}
.effectCategory {
margin: 1rem 0;
}
.recipeList {
margin: 1rem 0 0 2rem;
display: flex;
flex-wrap: wrap;
justify-items: left;
}
.recipeList .recipeSelect {
width: 8rem;
margin:1rem 1rem 0 0;
}
/* 菜谱 tag */
.recipeTag {
margin-right: 1rem;
}
</style>
2: 前端路由代理配置代码
'use strict'
// Template version: 1.3.1
// see http://vuejs-templates.github.io/webpack for documentation.
const path = require('path')
module.exports = {
dev: {
// Paths
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {
'/api_1': {
target:'http://www.chaxun.com/joke', // 你请求的第三方接口
changeOrigin:true, // 在本地会创建一个虚拟服务端,然后发送请求的数据,并同时接收请求的数据,这样服务端和服务端进行数据的交互就不会有跨域问题
pathRewrite:{
// 路径重写,
'^/api_1': '/api_1' // 替换target中的请求地址,也就是说以后你在请求 target 的地址的时候直接写成 /api_1 即可。
}
},
'/api_2': {
target: 'http://www.chaxun.com/constellation',
changeOrigin: true,
pathRewrite: {
'^/api_2': '/api_2'
}
},
'/api_3': {
target: 'http://www.chaxun.com/weather',
changeOrigin: true,
pathRewrite: {
'^/api_3': '/api_3'
}
},
'/api_4': {
target: 'http://www.chaxun.com/news',
changeOrigin: true,
pathRewrite: {
'^/api_4': '/api_4'
}
},
'/api_5': {
target: 'http://www.chaxun.com/videos',
changeOrigin: true,
pathRewrite: {
'^/api_5': '/api_5'
}
},
'/api_6': {
target: 'http://www.chaxun.com/driverlicense',
changeOrigin: true,
pathRewrite: {
'^/api_6': '/api_6'
}
},
'/api_7': {
target: 'http://www.chaxun.com/today_in_history',
changeOrigin: true,
pathRewrite: {
'^/api_7': '/api_7'
}
},
'/api_8': {
target: 'http://www.chaxun.com/idioms',
changeOrigin: true,
pathRewrite: {
'^/api_8': '/api_8'
}
},
'/api_9': {
target: 'http://www.chaxun.com/recipe',
changeOrigin: true,
pathRewrite: {
'^/api_9': '/api_9'
}
}
},
// Various Dev Server settings
host: '0.0.0.0', // 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: false,
errorOverlay: true,
notifyOnErrors: true,
poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
// Use Eslint Loader?
// If true, your code will be linted during bundling and
// linting errors and warnings will be shown in the console.
useEslint: true,
// If true, eslint errors and warnings will also be shown in the error overlay
// in the browser.
showEslintErrorsInOverlay: false,
/**
* Source Maps
*/
// https://webpack.js.org/configuration/devtool/#development
devtool: 'cheap-module-eval-source-map',
// If you have problems debugging vue-files in devtools,
// set this to false - it *may* help
// https://vue-loader.vuejs.org/en/options.html#cachebusting
cacheBusting: true,
cssSourceMap: true
},
build: {
// Template for index.html
index: path.resolve(__dirname, '../dist/index.html'),
// Paths
assetsRoot: path.resolve(__dirname, '../dist'),
assetsSubDirectory: 'static',
assetsPublicPath: '/',
/**
* Source Maps
*/
productionSourceMap: true,
// https://webpack.js.org/configuration/devtool/#production
devtool: '#source-map',
// Gzip off by default as many popular static hosts such as
// Surge or Netlify already gzip all static assets for you.
// Before setting to `true`, make sure to:
// npm install --save-dev compression-webpack-plugin
productionGzip: false,
productionGzipExtensions: ['js', 'css'],
// Run the build command with an extra argument to
// View the bundle analyzer report after build finishes:
// `npm run build --report`
// Set to `true` or `false` to always turn it on or off
bundleAnalyzerReport: process.env.npm_config_report
}
}
3: 前端菜谱分类 json
{
"code":"10000","charge":false,"msg":"查询成功","result":{
"status":0,"msg":"ok","result":[{
"classid":1,"name":"功效","parentid":0,"list":[{
"classid":2,"name":"减肥","parentid":1},{
"classid":3,"name":"瘦身","parentid":1},{
"classid":4,"name":"消脂","parentid":1},{
"classid":5,"name":"丰胸","parentid":1},{
"classid":6,"name":"美容","parentid":1},{
"classid":7,"name":"养颜","parentid":1},{
"classid":8,"name":"美白","parentid":1},{
"classid":9,"name":"防晒","parentid":1},{
"classid":10,"name":"排毒","parentid":1},{
"classid":11,"name":"祛痘","parentid":1},{
"classid":12,"name":"祛斑","parentid":1},{
"classid":13,"name":"保湿","parentid":1},{
"classid":14,"name":"补水","parentid":1},{
"classid":15,"name":"通乳","parentid":1},{
"classid":16,"name":"催乳","parentid":1},{
"classid":17,"name":"回奶","parentid":1},{
"classid":18,"name":"下奶","parentid":1},{
"classid":19,"name":"调经","parentid":1},{
"classid":20,"name":"安胎","parentid":1},{
"classid":21,"name":"抗衰老","parentid":1},{
"classid":22,"name":"抗氧化","parentid":1},{
"classid":23,"name":"延缓衰老","parentid":1},{
"classid":24,"name":"补钙","parentid":1},{
"classid":25,"name":"补铁","parentid":1},{
"classid":26,"name":"补锌","parentid":1},{
"classid":27,"name":"补碘","parentid":1},{
"classid":28,"name":"补硒","parentid":1},{
"classid":29,"name":"补虚","parentid":1},{
"classid":30,"name":"降血脂","parentid":1},{
"classid":31,"name":"降血糖","parentid":1},{
"classid":32,"name":"降血压","parentid":1},{
"classid":33,"name":"降低胆固醇","parentid":1},{
"classid":34,"name":"解酒","parentid":1},{
"classid":35,"name":"提神","parentid":1},{
"classid":36,"name":"增高","parentid":1},{
"classid":37,"name":"解暑","parentid":1},{
"classid":38,"name":"清热解暑","parentid":1},{
"classid":39,"name":"清热解毒","parentid":1},{
"classid":40,"name":"去火","parentid":1},{
"classid":41,"name":"清火","parentid":1},{
"classid":42,"name":"下火","parentid":1},{
"classid":43,"name":"清热下火","parentid":1},{
"classid":44,"name":"生津止渴","parentid":1},{
"classid":45,"name":"止泻","parentid":1},{
"classid":46,"name":"增肥","parentid":1},{
"classid":47,"name":"抗过敏","parentid":1},{
"classid":48,"name":"补气","parentid":1