问题描述:
1: 用 VUE 当前端
2: 用 TP5(thinkPHP5) 当后端
3: 前端请求后端接口
前端界面如下:ElementUI 使用(仿了一个 admin-vue 后端)
分成头部、左侧、右侧
router.js
import Vue from 'vue'
import Router from 'vue-router'
import Main from '@/components/Main'
import Right from '@/components/Right'
import Joke from '@/components/apis/Api_1'
Vue.use(Router)
export default new Router({
mode: 'history',
routes: [
{ path: '/',
component: Main,
children: [
{ path: '', component: Right },
{ path: 'joke', component: Joke }
]
}
]
})
右侧代码:
<template>
<div class="content-box content-right">
<el-card class="box-card api-card" @mouseenter="goon = !goon" @mouseleave="goon = !goon">
<div slot="header" class="clearfix">
<span>API 列表</span>
<el-button class="refreshBtn" type="text">刷新</el-button>
</div>
<div class="card-list">
<el-card v-for="api in apiItems" :key="api.nmb" class="box-card sub-box-card">
<div class="sub-box-body">
<img class="image" :src="api.src" :alt="api.alt" />
<h2 class="title"></h2>
<p>{{ api.name }}</p>
</div>
<a class="click-btn" :href="api.link">点击使用</a>
</el-card>
</div>
</el-card>
</div>
</template>
<script>
import eventBus from '../../static/js/eventBus' // 根据 header 里面的传值
export default {
name: 'right',
mounted () {
this.getApis()
eventBus.$on('collapseOrNot', (data) => { // 对位置的绝对值进行重写
console.log(data)
let contentRightWidth = document.getElementsByClassName('content-right')[0]
if (data) {
contentRightWidth.style.left = '65px'
} else {
contentRightWidth.style.left = '158px'
}
})
},
data () {
return {
apiItems: ''
}
},
methods: {
getApis () {
this.$axios.get('/static/json/apis.json').then(res => { // 获取 apis.json 里面的数据
if (res.data.code === 200) { this.apiItems = res.data.data }
}).catch(error => {
console.log(error)
})
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.content-box {
padding: 1rem;
position: absolute;
left: 158px;
top: 6rem;
bottom: 0;
padding-bottom: 30px;
-webkit-transition: left .3s ease-in-out;
transition: left .1s ease-in-out;
background: #ffffff;
}
.clearfix:before, .clearfix:after {
display: table;
content: "";
}
.clearfix:after {
clear: both
}
.api-card {
padding-bottom: 2rem;
}
.refreshBtn {
float: right; padding: 3px 0;
}
.sub-box-card {
text-align: center;
width: 9rem;
float: left;
margin: 0 2rem;
position: relative;
margin-bottom: 2rem;
}
.sub-box-card:hover, .sub-box-body {
transform: translateY(-10px);
transition: .5s;
}
.sub-box-card:hover {
box-shadow: 0px 10px 10px -5px #eee;
}
.sub-box-card:hover .click-btn {
opacity: 1;
animation-fill-mode: forwards;
animation: floatbtn .5s;
animation-duration: 0.5s;
animation-timing-function: ease;
animation-delay: 0s;
animation-iteration-count: 1;
animation-direction: normal;
animation-fill-mode: none;
animation-play-state: running;
animation-name: floatbtn;
}
.click-btn {
opacity: 0;
position: absolute;
bottom: 0;
margin-left: -50%;
width: 100%;
height: 2rem;
text-align: center;
line-height: 2rem;
color: white;
font-size: 1rem;
font-weight: bold;
background-color: #00bdff;
border-radius: 0.2rem;
text-decoration: none;
padding: 0;
transition: .5s;
cursor: pointer;
}
.sub-box-card .image {
width: 4rem;
height: 4rem;
margin-bottom: 1rem;
}
.sub-box-card .title{
font-size: 18px;
color: white;
}
</style>
apis.json:
{
"msg": "success",
"code": 200,
"data": [
{ "nmb": 1, "src": "/static/imgs/constellation.jpeg", "alt": "星座", "name": "星座", "link": "https://www.baidu.com" },
{ "nmb": 2, "src": "/static/imgs/weather.png", "alt": "天气", "name": "天气", "link": "https://www.baidu.com" },
{ "nmb": 3, "src": "/static/imgs/phone_attribution.jpeg", "alt": "星座", "name": "星座", "link": "https://www.baidu.com" },
{ "nmb": 4, "src": "/static/imgs/news.jpeg", "alt": "新闻", "name": "新闻", "link": "https://www.baidu.com" },
{ "nmb": 5, "src": "/static/imgs/video.jpeg", "alt": "热门视频榜单", "name": "热门视频榜单", "link": "https://www.baidu.com" },
{ "nmb": 6, "src": "/static/imgs/joke.jpeg", "alt": "笑话大全", "name": "笑话大全", "link": "/joke" },
{ "nmb": 7, "src": "/static/imgs/rate.jpeg", "alt": "汇率", "name": "汇率", "link": "https://www.baidu.com" },
{ "nmb": 8, "src": "/static/imgs/history.jpg", "alt": "历史上的今天", "name": "历史上的今天", "link": "https://www.baidu.com" },
{ "nmb": 9, "src": "/static/imgs/idiom.jpeg", "alt": "成语接龙", "name": "成语接龙", "link": "https://www.baidu.com" },
{ "nmb": 10, "src": "/static/imgs/drive_liscence.jpeg", "alt": "驾照题库", "name": "驾照题库", "link": "https://www.baidu.com" },
{ "nmb": 11, "src": "/static/imgs/calendar.jpeg", "alt": "万年历", "name": "万年历", "link": "https://www.baidu.com" },
{ "nmb": 12, "src": "/static/imgs/qq.jpeg", "alt": "QQ号测吉凶", "name": "QQ号测吉凶", "link": "https://www.baidu.com" },
{ "nmb": 13, "src": "/static/imgs/idiom_2.jpeg", "alt": "成语大全", "name": "成语大全", "link": "https://www.baidu.com" },
{ "nmb": 14, "src": "/static/imgs/dictionary.jpg", "alt": "新华字典", "name": "新华字典", "link": "https://www.baidu.com" },
{ "nmb": 15, "src": "/static/imgs/code.jpeg", "alt": "邮编查询", "name": "邮编查询", "link": "https://www.baidu.com" },
{ "nmb": 16, "src": "/static/imgs/zhougong.jpeg", "alt": "周公解梦", "name": "周公解梦", "link": "https://www.baidu.com" },
{ "nmb": 17, "src": "/static/imgs/ebook.jpeg", "alt": "图书电商数据", "name": "图书电商数据", "link": "https://www.baidu.com" },
{ "nmb": 18, "src": "/static/imgs/balance.jpeg", "alt": "健康指数器", "name": "健康指数器", "link": "https://www.baidu.com" },
{ "nmb": 19, "src": "/static/imgs/solar_terms.jpeg", "alt": "二十四节气", "name": "二十四节气", "link": "https://www.baidu.com" },
{ "nmb": 20, "src": "/static/imgs/flower.jpeg", "alt": "生日花语", "name": "生日花语", "link": "https://www.baidu.com" },
{ "nmb": 21, "src": "/static/imgs/id.jpg", "alt": "身份证查询", "name": "身份证查询", "link": "https://www.baidu.com" }
]
}
Api_1.vue
<template>
<div class="api_interface content-box content-right">
<el-card class="box-card">
<div slot="header" class="clearfix">
<span>笑话大全</span>
<el-button type="text" @click="refresh">刷新</el-button>
</div>
<div class="joke">
<el-select v-model="value" placeholder="按更新时间查询笑话" @change="selectType($event)">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select>
</div>
<!-- 按更新时间查询笑话 -->
<div class="type-one">
<div class="page_number">
<el-select v-model="page" placeholder="显示第1页">
<el-option v-for="i in 20" :key="i" :label="'显示第' + i + '页'" :value="'显示第' + i + '页'"></el-option>
</el-select>
</div>
<div class="pagesize">
<el-select v-model="pagesize" placeholder="显示1条">
<el-option v-for="i in 20" :key="i" :label="'显示' + i + '条'" :value="'显示' + i + '条'"></el-option>
</el-select>
</div>
<div class="timestamp"></div>
</div>
<!-- 最新笑话 -->
<div class="type-two">
<div class="page_number">
<el-select v-model="page" placeholder="显示第1页">
<el-option v-for="i in 20" :key="i" :label="'显示第' + i + '页'" :value="'显示第' + i + '页'"></el-option>
</el-select>
</div>
<div class="pagesize">
<el-select v-model="pagesize" placeholder="显示1条">
<el-option v-for="i in 20" :key="i" :label="'显示' + i + '条'" :value="'显示' + i + '条'"></el-option>
</el-select>
</div>
</div>
<!-- 随机获取笑话 -->
<div class="type-three"></div>
<el-button class="query-btn" round @click="queryFn">确定查询</el-button>
<el-card class="box-card query-result">
<div slot="header" class="clearfix">
<span>结果如下</span>
</div>
<div class="joke-result-show"></div>
</el-card>
</el-card>
</div>
</template>
<script>
export default {
name: 'right',
data () {
return {
options: [
{ value: '1', label: '按更新时间查询笑话' },
{ value: '2', label: '最新笑话' },
{ value: '3', label: '随机笑话' }
],
value: '',
page: '',
pagesize: ''
}
},
methods: {
selectType (e) {
let typeOne = document.getElementsByClassName('type-one')[0]
let typeTwo = document.getElementsByClassName('type-two')[0]
let typeThree = document.getElementsByClassName('type-three')[0]
switch (e) {
case '2':
typeOne.style.display = 'none'
typeThree.style.display = 'none'
typeTwo.style.display = 'block'
break
case '3':
typeOne.style.display = 'none'
typeTwo.style.display = 'none'
typeThree.style.display = 'block'
break
default:
typeTwo.style.display = 'none'
typeThree.style.display = 'none'
typeOne.style.display = 'block'
}
},
refresh () {
this.$router.go(0)
},
queryFn () {
let baseUrl = '/api_1'
this.$axios({
methods: 'get',
url: baseUrl
}).then(res => {
console.log(res.data)
}).catch(error => {
console.log(error)
})
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.content-box {
text-align: center;
margin-left: 4rem;
}
.type-one {
display: block;
}
.type-two {
display: none;
}
.type-three {
display: none;
}
.page_number, .pagesize {
margin-top: 1rem;
}
.query-btn {
margin: 2rem 0;
}
</style>
Api_1.vue 里面 axios 请求 /api_1, 配置对应的 /config/index.js
'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/index/Apis/index', // 你请求的第三方接口
changeOrigin:true, // 在本地会创建一个虚拟服务端,然后发送请求的数据,并同时接收请求的数据,这样服务端和服务端进行数据的交互就不会有跨域问题
pathRewrite:{ // 路径重写,
'^/api_1': '/api_1' // 替换target中的请求地址,也就是说以后你在请求 target 的地址的时候直接写成 /api_1 即可。
}
}
},
... ... // 剩下的内容我没有改动过
剩下的内容就在 TP5 里面写了,TP5 布局如下:
Apis.php 里面的内容
<?php
namespace app\index\controller;
use think\Route;
use think\View;
use think\Db;
use think\Request;
class Apis {
public function index()
{
return '<h1>This is my first controller.</h1>';
}
/**
* 发起网络请求函数
* @param String $url 请求的URL
* @param bool $params 请求的参数内容
* @param int $isPost 是否POST请求
* @return bool|string 返回内容
*/
public function httpRequest($url, $params = false, $isPost = 0){
$httpInfo = [];
$ch = curl_init();
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_setopt($ch, CURLOPT_USERAGENT, 'ozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36'); // 浏览器代理信息,我这里设置的自己的浏览器
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 3); // 连接的等待时间, 3秒
curl_setopt($ch, CURLOPT_TIMEOUT, 12); // 函数最长的执行时间
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 将 curl_exec() 获取的信息以字符串返回,而不是直接输出
if ($isPost) {
curl_setopt($ch, CURLOPT_POST, true); // true 表示 post 请求
curl_setopt($ch, CURLOPT_POSTFIELDS, $params); // post 的请求数据的处理
curl_setopt($ch, CURLOPT_URL, $url); // 设置访问的 URL
} else {
if ($params) {
curl_setopt($ch, CURLOPT_URL, $url . '?' . $params);
} else {
curl_setopt($ch, CURLOPT_URL, $url);
}
}
$reponse = curl_exec($ch);
if ($reponse === FALSE) {
return false; // echo "cURL Error: ".curl_error($ch);
}
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$httpInfo = array_merge($httpInfo, curl_getinfo($ch));
curl_close($ch);
return $reponse;
}
const CONST_KEY = ""; // 使用的时候 self::CONST_KEY, 自己聚合里面申请的KEY填写上
public function joke() // joke api_01 笑话大全
{
$url = "http://v.juhe.cn/joke/content/list.php"; // 笑话大全方式一
/*
参数名 类型 是否必填 说明
sort string 是 类型,desc:指定时间之前发布的,asc:指定时间之后发布的
page int 否 当前页数,默认1,最大20
pagesize int 否 每次返回条数,默认1,最大20
time string 是 时间戳(10位),如: 1635328323
*/
// $sort = 'desc';
// $page = 1;
// $pagesize = 20;
// $time = time();
// $parameters = "sort=desc&page=1&pagesize=20&time=1635328323&key=".self::CONST_KEY;
$params = [ // 请求参数
'sort' => 'desc',
'page' => 1,
'pagesize' => 20,
'time' => time()
];
$paramsString = http_build_query($params); // 参数数组转换成字符串
$response = self::httpRequest($url, $paramsString."&key=".self::CONST_KEY, 0); // 第二个参数里面添加聚合里面给的 key
echo $response;
$result = json_decode($response, true); // true 返回 json 而不是 objection
if ($result) {
// return print_r($result);
$errorCode = $result['error_code'];
if ($errorCode == 0) {
$data = $result['result'];
return "<h1>I am good man.</h1>";
// return $jsonData;
} else {
return "第一种错: {$errorCode}_{$result['reason']}".PHP_EOL;
}
} else {
return "第二种错: ".PHP_EOL; // 可能网络异常等问题,无法正常获得相应内容,业务逻辑可自行修改
}
return '<h1>Api coming</h1>';
}
}
在前端运行的结果如下:
至于界面,准备写中。