后端接口
1、路由,在 routes/api.php
中
Route::resource('photos', 'PhotoController')->only('store');
2、创建对应控制器
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class PhotoController extends Controller
{
/***
* 上传图片
* @param Request $request
*/
public function store(Request $request)
{
if ($request->hasFile('file') && $request->file('file')->isValid()) {
$path = $request->file->store('public/images');
//上传到七牛云
$file_path = storage_path('app/') . $path;
qiniu_upload($file_path);
return response()->json( 'https://image.xxx.com/' . basename($file_path));
}
}
}
3、定义辅助函数 qiniu.php
<?php
// 引入鉴权类
use Qiniu\Auth;
// 引入上传类
use Qiniu\Storage\UploadManager;
function qiniu_upload($filePath)
{
$accessKey = "fAoxxxxxxxxxxxxxxxxxxxxxxxxx";
$secretKey = "dkCxxxxxxxxxxxxxxxxxxxxxxxxx";
$bucket = "xxxxx";
$auth = new Auth($accessKey, $secretKey);
$token = $auth->uploadToken($bucket);
// 上传到七牛后保存的文件名
$key = basename($filePath);
// 初始化 UploadManager 对象并进行文件的上传。
$uploadMgr = new UploadManager();
// 调用 UploadManager 的 putFile 方法进行文件的上传。
$uploadMgr->putFile($token, $key, $filePath);
unlink($filePath);
}
这里需要安装七牛云的包,不会用的可以看七牛官网或在评论区给我留言。
前端
1、创建 vue2
项目,此步骤省略,自行完成。
2、安装 axios
、vue2-toast
及 Vant2
前端 UI 框架,
npm install axios
npm install toast2-vue -S
npm i vant@latest-v2 -S
3、在 main.js
中,全部代码如下,供参考:
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import Vant from 'vant'
import 'vant/lib/index.css'
import axios from 'axios'
import 'vue2-toast/lib/toast.css'
import Toast from 'vue2-toast'
axios.defaults.baseURL = 'https://xxx.xxx.com/'
Vue.prototype.$http = axios
Vue.use(Toast)
Vue.use(Vant)
Vue.config.productionTip = false
new Vue({
router,
render: h => h(App)
}).$mount('#app')
4、在 src/router/index.js
中添加路由,代码如下:
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'Home',
component: () => import('../views/Home.vue')
}
]
const router = new VueRouter({
routes
})
export default router
5、准备视图,在 src/views/Home.vue
中,全部代码如下:
<template>
<div class="home">
<h2 class="van-doc-title" style="text-align: center">督查记录表</h2>
<van-form @submit="onSubmit">
<h2 class="van-doc-demo-block__title" style="background-color: #f7f8fa;">基础信息</h2>
<van-field
v-model="form.name"
label="督查员:"
placeholder="请输入姓名"
:rules="[{ required: true, message: '请填写姓名' }]"
/>
<van-field
readonly
clickable
name="picker"
:value="form.department"
label="督查场部:"
placeholder="点击选择"
@click="showPicker = true"
/>
<van-popup v-model="showPicker" position="bottom">
<van-picker
show-toolbar
:columns="stages"
@confirm="onConfirm"
@cancel="showPicker = false"
/>
</van-popup>
<van-field
v-model="form.address"
label="督查地点:"
placeholder="请输入地点名称"
:rules="[{ required: true, message: '请填写地点名称' }]"
/>
<van-field
v-model="form.head"
label="岗位负责人:"
placeholder="请输入负责人姓名"
:rules="[{ required: true, message: '请填写负责人姓名' }]"
/>
<h2 class="van-doc-demo-block__title" style="background-color: #f7f8fa;">检查项目</h2>
<van-field name="radio" label="卫生情况:">
<template #input>
<van-radio-group v-model="form.health" direction="horizontal">
<van-radio name="1">合格</van-radio>
<van-radio name="0">不合格</van-radio>
</van-radio-group>
</template>
</van-field>
<van-field name="uploader" label="附图:">
<template #input>
<van-uploader v-model="form.imageList" multiple :max-count="2"
:after-read="afterRead"/>
</template>
</van-field>
<van-field
v-model="form.message"
rows="2"
autosize
label="留言:"
type="textarea"
maxlength="50"
placeholder="请输入留言"
show-word-limit
/>
<div style="display: flex;align-items: center;">
<h2 class="van-doc-demo-block__title"
style="font-size: 14px;color: #646566;font-weight: normal;padding-right: 15px">评分:</h2>
<van-rate
v-model="form.rate"
:size="25"
color="#ffd21e"
void-icon="star"
void-color="#eee"
count="10"
@change="onChange"
/>
</div>
<div style="margin: 50px 16px 16px 16px;">
<van-button round block type="info" native-type="submit">提交</van-button>
</div>
</van-form>
</div>
</template>
<script>
/* eslint-disable */
export default {
data () {
return {
stages: ['总部', '杨湖场', '株山场', '天子山场', '品格饲料厂'],
showPicker: false,
form: {
name: '',
department: '',
address: '',
head: '',
health: '1',
imageList: [],
message: '',
rate: 0
}
}
},
methods: {
// 选择框
onConfirm (value) {
this.form.department = value
this.showPicker = false
},
// 点击评分
onChange (value) {
this.form.rate = value
},
// 点击上传
afterRead (file) {
file.status = 'uploading'
file.message = '上传中...'
const formData = new FormData()
formData.append('file', file.file)
this.uploadImage(formData).then(response => {
// 假设返回的response.data是图片的URL
file.content = response.data
file.status = 'done'
file.message = '上传成功'
this.form.imageList = [...this.form.imageList] // 通过替换imageList数组的内容来更新视图
})
},
// 图片上传至后端服务器
uploadImage (formData) {
const uploadUrl = 'https://xxx.xxx.com/api/photos'
return this.$http.post(uploadUrl, formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
},
// 表单提交
async onSubmit () {
const res = await this.$http.post('api/check', this.form)
if (res.data.status === true) {
this.$toast.center(res.data.message)
this.form = {}
this.form.health = '1'
}
}
}
}
</script>
<style>
.van-doc-demo-block__title {
padding: 10px 16px;
color: rgba(69, 90, 100, 0.6);
font-weight: normal;
font-size: 14px;
line-height: 16px;
}
.van-cell {
padding: 15px 16px;
}
.van-button--info {
background-color: #f4645f;
border: 1px solid #f4645f;
}
.van-radio__icon--checked .van-icon {
background-color: #f4645f;
border-color: #f4645f;
}
</style>
上述代码其实是实现一个 form
表单的提交,里面有个比较重要的功能,就是多图上传的问题。最终的效果如下图: