问题描述:
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>'; } }
在前端运行的结果如下:
至于界面,准备写中。