展示图片--系统篇

        这个标题包含了系统篇3个字,是什么意思呢?我在这里做出解释,系统篇意指包含前台和后台的关联性功能实现,同时重心放在逻辑解释上。

        可能会有人好奇,既然有系统篇,那么相对系统篇还有哪些篇呢?这里我就按照商业价值从小到大罗列几个不同种类的篇章,帮助读者选择性地阅读我的文章。(如果要发布成一本书,这里就相当于序言。)就像修炼一样,划分不同的境界,每个境界有特定的标识。分别还有入门篇、进阶篇、接口篇。也就是按照商业价值从小到大分别是入门篇、进阶篇、接口篇、系统篇。

        现在解释下每个篇的含义。系统篇在第一段已经解释过了,后面就不重复解释了。入门篇意指某种技术框架学习前所涉及的一些安装、配置、工具等支撑性东西的介绍。进阶篇意向某种技术框架的技术点的语法用法解释。接口篇意指前端后端数据交换的展示、解释等,目前接口篇我没在标题中标明,只是在标题中涵盖了前端和后端的框架名。

        回归本篇文章正题,展示图片。这里的展示图片指的是前台展示图片,为了配合前台展示图片,还实现了后台上传图片和删除图片,下面先展示下后台效果图和前台效果图,帮助读者建立一个感性的认识。

        这是后台效果图

        这是前台H5端的效果图

        这是前台抖音小程序端的效果图

        后台效果图中那个V字logo图不用看,这是建立工程时自带的展示,我没有删掉。

        后台效果图是一个PC端的浏览器的展示界面,重点看圈出来的写着Click to upload的按钮,这个按钮就是负责上传图片功能的,点击这个按钮会弹出选择图片的窗口。图中还展示了3张上传好的图片,没上传之前是空白的,其中鼠标移到第2项上传好的图片提示项中就会显示出这一项圈出来的删除交叉符。点击这个删除交叉符这里还实现了删除图片功能。

        前台效果图有2张,以抖音小程序效果图为例介绍就好。

        抖音小程序效果图展示了两部分内容,一部分是导航,导航是通过一个底部tabbar展示,点击组件项后所看到的页面,这个不是首页。然后在组件页内置一个tabbar,然后点击Posts页再看到如今的效果图。第二部分就是Posts页下图片列表的展示。

        这里再介绍下所用到的技术框架,前台前端使用uniapp框架,后台前端使用Vue3+ElementPlus框架,后端统一使用thinkphp8框架。

        要在前台展示,那么就要先在后台实现对应接口功能

        实现接口功能,需要在mysql数据库先建立一张图片表来支撑

CREATE TABLE `am8_head2` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8

        name这个字段用来存放图片路径就好。

        后端采用自动多应用模式建立项目,建立admin应用,分别建立Index控制器和Head2模型。

        在Index控制器中实现上传图片接口如下

public function upload()
    {
        $head = new Head2;
        
        $file = request()->file('image');

        $savename = \think\facade\Filesystem::disk('public')
                ->putFile( 'topic', $file);
        
        $head->name = $savename;
        $head->save();
        $head_id = $head->getKey();
        
        return mySuccessResponse([
            'id' => $head_id,
            'name' => $savename,
            'url' => 'http://auto.multi.tp8.com/storage/'.$savename
        ]);
    }

        该接口分3步实现,第一步获取图片并保存图片,代码如下

        $file = request()->file('image');

        $savename = \think\facade\Filesystem::disk('public')
                ->putFile( 'topic', $file);

        第二步将文件路径写入head2数据表并获取自动生成的图片id

        $head->name = $savename;
        $head->save();
        $head_id = $head->getKey();

        第三步响应到前端的数据

        return mySuccessResponse([
            'id' => $head_id,
            'name' => $savename,
            'url' => 'http://auto.multi.tp8.com/storage/'.$savename
        ]);

        mySuccessResponse方法是个封装好的json格式数据返回方法,大家自己封装适合自己的,这里不展示了。返回的数据有3项,分别是id、name、url,都是前端所需要的。

        到目前为止,可能代码对于某些人来说是很粗糙的,但是我这个小系统就是个粗糙的迭代系统,不要去介意代码粗糙。

        这里url项前半部分用了http://auto.multi.tp8.com,这是我项目的公用域名,这些个图片不独属于某个应用的,故而用公用域名。每个应用有各自的绑定域名。

         在Index控制器中实现删除文件接口如下

public function deleteFile()
    {
        $head = Head2::find(request()->param('id'));
        $name = $head->name;
        $head->delete();


        $search = '\\';
        $replacedString = str_replace($search, DIRECTORY_SEPARATOR, $name);
        $search = '/';
        $replacedString2 = str_replace($search, DIRECTORY_SEPARATOR, $replacedString);
        unlink(app()->getRootPath().'public'.DIRECTORY_SEPARATOR.'storage'
                .DIRECTORY_SEPARATOR.$replacedString2);


        return mySuccessResponse();
    }

        该接口分3步实现。第一步删除数据库中图片记录同时临时保存取出来的图片路径

        $head = Head2::find(request()->param('id'));
        $name = $head->name;
        $head->delete();

        第二步删除图片,由于在windows和linux系统中文件路径所用的分隔符不一样,因此做了适配处理

        $search = '\\';
        $replacedString = str_replace($search, DIRECTORY_SEPARATOR, $name);
        $search = '/';
        $replacedString2 = str_replace($search, DIRECTORY_SEPARATOR, $replacedString);
        unlink(app()->getRootPath().'public'.DIRECTORY_SEPARATOR.'storage'
                .DIRECTORY_SEPARATOR.$replacedString2);

        第三步就是告诉前端删除成功了

        return mySuccessResponse();

        模型的代码没有,只是建立模型,继承think\Model就好。

        是时候介绍下后台界面代码了

        先摆放界面元素

<template>
  <img alt="Vue logo" src="./assets/logo.png" />
  <el-upload
      v-model:file-list="fileList"
      class="upload-demo"
      action="http://admin.am8.com/index/upload"
      name="image"
      list-type="picture"
      :on-success="handleSuccess"
      :on-preview="handlePreview"
      :on-remove="handleRemove"
      :before-remove="beforeRemove"
      :limit="3"
      :on-exceed="handleExceed"
    >
      <el-button type="primary">Click to upload</el-button>
      <template #tip>
        <div class="el-upload__tip">
          jpg/png files with a size less than 500KB.
        </div>
      </template>
    </el-upload>
</template>

        第一个元素是vue那个V型logo,区分下就好,不用关注

<img alt="Vue logo" src="./assets/logo.png" />

        第二个元素就是上传组件

<el-upload
      v-model:file-list="fileList"
      class="upload-demo"
      action="http://admin.am8.com/index/upload"
      name="image"
      list-type="picture"
      :on-success="handleSuccess"
      :on-preview="handlePreview"
      :on-remove="handleRemove"
      :before-remove="beforeRemove"
      :limit="3"
      :on-exceed="handleExceed"
    >

        ,,,

</el-upload>

        下面介绍下这段代码的含义

        表单按钮的数据绑定是fileList对象

        v-model:file-list="fileList"

        后端上传的接口设置

        action="http://admin.am8.com/index/upload"

        上传图片字段的命名,对应后端上传文件的获取名

        name="image"

        上传图片的展示类型,picture表示展示用fileList的name和url属性展示,前面后端接口upload返回的name和url就是用来更新这里的name和url的。如果使用text,则仅仅用name属性展示

        list-type="picture"

        上传成功的方法设置

        :on-success="handleSuccess"

        点击预览的方法设置,这里我没有实现这个功能,只是简单打印了一下

        :on-preview="handlePreview"

        删除图片的方法设置

        :on-remove="handleRemove"

        点击交叉图标后真实删除图片之前的方法设置

        :before-remove="beforeRemove"

        限制每次可以上传的图片数

        :limit="3"

        超过限制图片数的警告方法设置

        :on-exceed="handleExceed"

        上传组件内部包含了按钮和提示文字

<el-button type="primary">Click to upload</el-button>
      <template #tip>
        <div class="el-upload__tip">
          jpg/png files with a size less than 500KB.
        </div>
      </template>

        摆放完界面元素后就要实现操作元素了,代码如下

<script lang="ts" setup>

import { ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'

import type { UploadProps, UploadUserFile } from 'element-plus'

import axios from 'axios';

const fileList = ref<UploadUserFile[]>([
  
])

const handleSuccess: UploadProps['onSuccess'] = (response, uploadFile, uploadFiles) => {
  
  fileList.value.pop()
  fileList.value.push(response.data)
}

const handleRemove: UploadProps['onRemove'] = (file, uploadFiles) => {
  
  axios.post('http://admin.am8.com/index/deleteFile', {
      id: file.id,
    })
    .then(function (response) {
      console.log(response);
    })
    .catch(function (error) {
      console.log(error);
    });
}

const handlePreview: UploadProps['onPreview'] = (uploadFile) => {
  console.log(uploadFile)
}

const handleExceed: UploadProps['onExceed'] = (files, uploadFiles) => {
  ElMessage.warning(
    `The limit is 3, you selected ${files.length} files this time, add up to ${
      files.length + uploadFiles.length
    } totally`
  )
}

const beforeRemove: UploadProps['beforeRemove'] = (uploadFile, uploadFiles) => {
  return ElMessageBox.confirm(
    `Cancel the transfer of ${uploadFile.name} ?`
  ).then(
    () => true,
    () => false
  )
}

</script>

        上传成功之后就用后端的数据替换掉前端的默认文件名

const handleSuccess: UploadProps['onSuccess'] = (response, uploadFile, uploadFiles) => {
  
  fileList.value.pop()
  fileList.value.push(response.data)
}

        执行真实删除图片确认后,向后端发送删除接口

const handleRemove: UploadProps['onRemove'] = (file, uploadFiles) => {
  
  axios.post('http://admin.am8.com/index/deleteFile', {
      id: file.id,
    })
    .then(function (response) {
      console.log(response);
    })
    .catch(function (error) {
      console.log(error);
    });
}

        其他方法自己去看,就不展开介绍了。接下来介绍前台后端接口

        首先建立mp_toutiao和h5应用,这2个应用所需的控制器和模型是一样的。那么为什么还要分开来写呢?因为每个渠道都有各自的差异,正是因为这些差异才导致各自的商业价值。而所谓跨端技术只是一种适应多端的技术,在有需求的时候使用,不是让大家去抹杀差异的。如果仅仅是为了复用方便而降低要求去使用跨端技术,那其实是本末倒置。这里既然2个应用代码大致一样,我就用mp_toutiao应用为例去介绍好了。

        建立Index控制器和head2模型。

        在Index控制器中编写获取图片列表接口

public function getHead2List()
    {
        $list = Head2::limit(3)->order('id desc')->select();
        $list2 = [];
        foreach ($list as $key => $value) {
            $search = '\\';
            $replace = '/';
            $replacedString = str_replace($search, $replace, $value->name);
            $list2[$key] = 'http://auto.multi.tp8.com/storage/'.$replacedString;
        }
        return mySuccessResponse($list2);
    }

        从head2数据表拿出图片数据,然后处理成前端image组件需要的格式,使用公用域名,这里尽可能处理好逻辑,前端复制显示就好。前端的重心是显示和元素操作,尽量不要带给前端太多的逻辑处理。

        模型的代码没有,只是建立模型,继承think\Model就好。

        前台前端代码编写如下

<template>
  <div class="tab">
    <image 
    v-for="item in list"
    class="logo"
    :src="item"
    ></image>
  </div>
</template>

<script>
    export default {
        data() {
            return {
                list:[]
            }
        },
        mounted() {
            uni.request({
                url:getApp().globalData.server_url+"index/getHead2List",
                method:"GET",
                success:(result) => {
                        console.log(result);
                            if (result.data.code === getApp().globalData.my_success_code) {
                                this.list = result.data.data;
                            } else {
                                if (result.data.code === getApp().globalData.my_fail_code) {
                                    uni.showModal({
                                        title: '错误',
                                        content: result.data.msg, // 显示后端返回的错误信息
                                        showCancel: false // 不显示取消按钮
                                     });
                                }
                            }
                    }
                ,
                fail:function(error){
                    console.log(error);
                }
            })
        },
    }
</script>

<style>
    .logo {
        height: 200rpx;
        width: 200rpx;
    }
</style>

        解释下上面的代码,摆放元素代码如下

<template>
  <div class="tab">
    <image 
    v-for="item in list"
    class="logo"
    :src="item"
    ></image>
  </div>
</template>

        item就是后端返回的图片完整路径,list就是用来接收后端返回的图片列表

        初始化请求后端数据,代码如下

uni.request({
                url:getApp().globalData.server_url+"index/getHead2List",
                method:"GET",
                success:(result) => {
                        console.log(result);
                            if (result.data.code === getApp().globalData.my_success_code) {
                                this.list = result.data.data;
                            } else {
                                if (result.data.code === getApp().globalData.my_fail_code) {
                                    uni.showModal({
                                        title: '错误',
                                        content: result.data.msg, // 显示后端返回的错误信息
                                        showCancel: false // 不显示取消按钮
                                     });
                                }
                            }
                    }
                ,
                fail:function(error){
                    console.log(error);
                }
            })

        其中getApp().globalData.server_url,getApp().globalData.my_success_code,getApp().globalData.my_fail_code都是预先写好的全局变量,各自写好自己的就好。server_url是mp_toutiao应用绑定的域名,my_success_code是后端响应的成功标识,my_fail_code是后端响应的失败标识。

上一篇:使用Python-pptx轻松批量添加水印


下一篇:前端开发攻略---取消已经发出但是还未响应的网络请求