谷粒商城第八天-商品服务之品牌管理的整体实现(直接使用逆向生成的代码;含oss文件上传)

news2024/11/26 21:50:34

目录

一、总述

二、前端部分

2.1 创建好品牌管理菜单

2.2 复制组件 

​编辑2.3 复制api ​​​编辑 

2.4 查看效果

​编辑2.5 需要优化的地方 

2.6 具体优化实现

2.6.1 优化一:将表格的状态列(这里是是否显示列)修改为开关,同时在修改页新增可显示修改开关

2.6.2 优化二:在修改页将logo地址文本框改为文件上传按钮

2.6.3 优化三:将表格列表的logo地址列改为显示图片

三、后端部分

四、总结


一、总述

之前的分类管理的前端部分基本上是手写的增删改查,虽然部分是直接使用的elementUI提供的组件,只是稍作修改,但是还是需要自己去找对应的组件,组件写数据域,自己写方法,自己绑定等,其实要写的还是比较多的。但其实仔细一想的话,其实这些组件都差不多,都是一些列表,提供一些按钮,一些文本框,然后进行增删改查,代码都差不多。既然差不多,没什么技术含量的东西,只是比较繁琐。那么应该就能自动生成的能给我们直接使用,是的若依也提供了前端部分的逆向代码。(ps:兄弟们,真的要努力的学了,这些基本的增删改查代码都不需要我们写了,如果你还只会这些简单的,会被淘汰的

现在要干的就是直接使用,然后根据自己的需求,修改部分即可。

二、前端部分

实现找到前端逆向生成的代码部分,其实就是和main同级的目录,一个vue目录,里面放着在网页所选择的很多张表所对应的前端代码:

2.1 创建好品牌管理菜单

这个没什么好说的,在之前的创建分类管理的时候已经操作过一遍了,只是部分属性修改一下就行了:

其实还可以选择在数据库中创建,之后可以使用数据库进行批量创建,其实都差不多

2.2 复制组件 

2.3 复制api ​​ 

2.4 查看效果

 效果就是下图这样子:

我们发现其实这种逆向生成的前端样子,大致是以下的这三部分:

2.5 需要优化的地方 

看着好像挺好的,基本上我们自己去写也就是写这些东西,现在都帮我们已经写好了,极大的提升了开发效率,但是这一般来说,不能这样就OK了,还需要优化,或者是需要根据一定的需求做调整。

那哪些部分需要我们去调整优化的呢?

1. 修改状态列为开关,修改页同理

像这种是否显示的这种或是其他字段使用01来表示状态的,一般来说我们不希望直接这么突兀的直接显示,主要是看着不美观,最重要的是不好直接修改,还需要到修改弹窗那里,手动的输入0或者1进行修改,这样不方便,如果在这个列表中这一个属性使用开关进行替换的话,就可以不打开修改弹窗,也不需要手动的去输入状态值了,而是直接点击一下,就能直接的修改了,很方面。

2. 修改图片地址列为真实图片

这个直接放置图片地址的方式肯定不可取,根本就不知道图片长什么样子,我们只需要使用elementUI为我们提供的图片组件将其进行替换即可。

3. 修改页面的品牌logo地址肯定要换成文件上传按钮

主要也就是这三部分,在其他组件页面其实大致都是这些地方需要进行修改。

2.6 具体优化实现

2.6.1 优化一:将表格的状态列(这里是是否显示列)修改为开关,同时在修改页新增可显示修改开关

现在表格的这一列已经是文本呢?如何替换呢?

其实很简单,既然这里是要替换表格中的一列,那么势必就得参考elementUI中的表格组件,如何进行替换?要有这种意识:哪个组件上面进行修改就查看哪个组件的文档(ps:像是废话,但很重要,思路就是这样产生的)。

好,现在就到官网中查看表格组件的文档,看到底如何替换列值:

 找到这个自定义模板。我们照葫芦画瓢直接复制这个template到我们自己的组件中去:

在表格列处:

这只是一个模板,就和组件文件中三大件中的模板一样,其实就是规定了组件写的位置,具体还是要在里面写好组件,例如现在需要放开关组件,而之后的表格的logo地址的那一列则需要放图片组件。现在的任务就是继续到官网里面去找修改状态开关组件:

放到那个模板中就可以在网页中 是否显示的那一列就能够显示 一个开关了。

最重要的就是将决定开关是否开还是闭与数据域中的数据进行关联,使用v-model指令进行动态的绑定:

这里关键要懂得这一列在数据域中如何表示!!!其实很简单 

但是这里会发现一个问题,就是无论我的数据库中,值无论是0还是1也就是无论是 显示还是不显示开关都是闭合的状态,那这是怎么回事呢?其实要有这么一种意识,那就是这个开关开与闭肯定是受到一个值,也就是我这里绑定的form.showStatus,对应数据库中对象的这一列值。

我们知道数据库中这一列值只会是0或者是1,其实啊这个开关组件,并不知道0代表关,1代表开,它默认自己是根据布尔值决定开还是关的,false代表关,true代表开,默认是这样。那肯定我们的数据库中一般不存布尔值是吧,所以肯定也得换,elementUI也知道肯定也会有这样的需求,所以也就提供了相应的参数,去自己指定,开是什么值,关又是什么值。elememtUI官网已经说的很明白了:

这里因为是需要动态和数据域中的属性进行绑定,因此需要使用在参数前加上 :冒号,进行指定

最终的样子长这样(ps:这里已经是将每一步都讲得很细节了,如果还不明白的话,请私信我):

 不要忘记修改页面同样得记得加上。都一样的东西只是绑定的值不一样:

下面我直接给出这两处地方的代码:

<el-table-column label="显示状态" align="center" prop="showStatus">
        <template slot-scope="scope">
          <el-switch
            @change="changeStatus(scope.row)"
            v-model="scope.row.showStatus"
            active-color="#13ce66"
            inactive-color="#ff4949"
            :active-value="1"
            :inactive-value="0">
          </el-switch>
        </template>
      </el-table-column>
<el-form-item label="是否显示" prop="showStatus">
          <el-switch
            v-model="form.showStatus"
            active-color="#13ce66"
            inactive-color="#ff4949"
            :active-value="1"
            :inactive-value="0"
          ></el-switch>
        </el-form-item>

到这里,这项优化工作就已经完成了;其实很简单,就是在你想要更换的那一列加个模板,然后放一个开关的组件,只是需要注意修改开关的开关规则。 

2.6.2 优化二:在修改页将logo地址文本框改为文件上传按钮

这里是我需要花大功夫讲的地方,主要就是使用oss进行文件上传,这里的前端部分的话,主要就是懂得加上一个文件上传按钮,然后这个按钮关联的许多参数,得注意一下就行了。

这里采用的oss上传方式为携带好凭证然后文件直传到阿里云服务器的方式。

这里的关键在于两点:

1. 上传文件组件从哪里来?

这个可以用谷粒商城中老师已经写好了的文件上传组件,直接用就行了,单文件的多文件的都有

这里我先贴上老师的代码,等下需要注意的地方再说一下:

单文件上传组件:

<template> 
  <div>
    <el-upload
      action="http://sq-bucket-02.oss-cn-hangzhou.aliyuncs.com"
      :data="dataObj"
      list-type="picture"
      :multiple="false" :show-file-list="showFileList"
      :file-list="fileList"
      :before-upload="beforeUpload"
      :on-remove="handleRemove"
      :on-success="handleUploadSuccess"
      :on-preview="handlePreview">
      <el-button size="small" type="primary">点击上传</el-button>
      <div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过10MB</div>
    </el-upload>
    <el-dialog :visible.sync="dialogVisible">
      <img width="100%" :src="fileList[0].url" alt="">
    </el-dialog>
  </div>
</template>
<script>
   import {policy} from './policy'
   import { getUUID } from '@/utils/getUUID'

  export default {
    name: 'singleUpload',
    props: {
      value: String
    },
    computed: {
      imageUrl() {
        return this.value;
      },
      imageName() {
        if (this.value != null && this.value !== '') {
          return this.value.substr(this.value.lastIndexOf("/") + 1);
        } else {
          return null;
        }
      },
      fileList() {
        return [{
          name: this.imageName,
          url: this.imageUrl
        }]
      },
      showFileList: {
        get: function () {
          return this.value !== null && this.value !== ''&& this.value!==undefined;
        },
        set: function (newValue) {
        }
      }
    },
    data() {
      return {
        dataObj: {
          policy: '',
          signature: '',
          key: '',
          ossaccessKeyId: '',
          dir: '',
          host: ''
        },
        dialogVisible: false
      };
    },
    methods: {
      emitInput(val) {
        this.$emit('input', val)
      },
      handleRemove(file, fileList) {
        this.emitInput('');
      },
      handlePreview(file) {
        this.dialogVisible = true;
      },
      beforeUpload(file) {
        const crypto = require('crypto');
        let _self = this;
        return new Promise((resolve, reject) => {
          policy().then(response => {
            _self.dataObj.policy = response.data.policy;
            _self.dataObj.signature = response.data.signature;
            _self.dataObj.ossaccessKeyId = response.data.accessid;
            _self.dataObj.key = response.data.dir + "/"+getUUID()+"_${filename}";
            _self.dataObj.dir = response.data.dir;
            _self.dataObj.host = response.data.host;
            console.log("_self.dataObj",_self.dataObj);
            resolve(true)
           }).catch(err => {
             reject(false)
           })
         })
      },
      handleUploadSuccess(res, file) {
        console.log("上传成功...")
        this.showFileList = true;
        this.fileList.pop();
        this.fileList.push({name: file.name, url: this.dataObj.host + '/' + this.dataObj.key.replace("${filename}",file.name) });
        this.emitInput(this.fileList[0].url);
      }
    }
  }
</script>
<style>

</style>


多文件上传组件:

<template>
  <div>
    <el-upload
      action="http://oss-cn-hangzhou.aliyuncs.com"
      :data="dataObj"
      list-type="picture-card"
      :file-list="fileList"
      :before-upload="beforeUpload"
      :on-remove="handleRemove"
      :on-success="handleUploadSuccess"
      :on-preview="handlePreview"
      :limit="maxCount"
      :on-exceed="handleExceed"
    >
      <i class="el-icon-plus"></i>
    </el-upload>
    <el-dialog :visible.sync="dialogVisible">
      <img width="100%" :src="dialogImageUrl" alt />
    </el-dialog>
  </div>
</template>
<script>
import { policy } from "./policy";
import { getUUID } from '@/utils'
export default {
  name: "multiUpload",
  props: {
    //图片属性数组
    value: Array,
    //最大上传图片数量
    maxCount: {
      type: Number,
      default: 30
    }
  },
  data() {
    return {
      dataObj: {
        policy: "",
        signature: "",
        key: "",
        ossaccessKeyId: "",
        dir: "",
        host: "",
        uuid: ""
      },
      dialogVisible: false,
      dialogImageUrl: null
    };
  },
  computed: {
    fileList() {
      let fileList = [];
      for (let i = 0; i < this.value.length; i++) {
        fileList.push({ url: this.value[i] });
      }

      return fileList;
    }
  },
  mounted() {},
  methods: {
    emitInput(fileList) {
      let value = [];
      for (let i = 0; i < fileList.length; i++) {
        value.push(fileList[i].url);
      }
      this.$emit("input", value);
    },
    handleRemove(file, fileList) {
      this.emitInput(fileList);
    },
    handlePreview(file) {
      this.dialogVisible = true;
      this.dialogImageUrl = file.url;
    },
    beforeUpload(file) {
      let _self = this;
      return new Promise((resolve, reject) => {
        policy()
          .then(response => {
            console.log("这是什么${filename}");
            _self.dataObj.policy = response.data.policy;
            _self.dataObj.signature = response.data.signature;
            _self.dataObj.ossaccessKeyId = response.data.accessid;
            _self.dataObj.key = response.data.dir + "/"+getUUID()+"_${filename}";
            _self.dataObj.dir = response.data.dir;
            _self.dataObj.host = response.data.host;
            resolve(true);
          })
          .catch(err => {
            console.log("出错了...",err)
            reject(false);
          });
      });
    },
    handleUploadSuccess(res, file) {
      this.fileList.push({
        name: file.name,
        // url: this.dataObj.host + "/" + this.dataObj.dir + "/" + file.name; 替换${filename}为真正的文件名
        url: this.dataObj.host + "/" + this.dataObj.key.replace("${filename}",file.name)
      });
      this.emitInput(this.fileList);
    },
    handleExceed(files, fileList) {
      this.$message({
        message: "最多只能上传" + this.maxCount + "张图片",
        type: "warning",
        duration: 1000
      });
    }
  }
};
</script>
<style>
</style>


 请求后端获取凭证的api js文件:

import request from '@/utils/request'
export function policy() {
    return request({
        url: "/thirdparty/oss/policy",
        method: "get"
    });
}

还有最终服务器中文件存储时的文件名所包含的uuid的工具类来生成UUID的:

export function getUUID() {
    return "xxxxxxxx-xxxx-xxxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, c => {
        const r = (Math.random() * 32) | 0;
        const v = c === "x" ? r : (r & 0x3) | 0x8;
        return v.toString(32).replace("-","");
      });
}

下面我简单的介绍一下这个文件上传组件文件:

其实简单来说就是:在真正的文件上传之前有一个前序方法,这个方法的目的就是向后端发送请求获得直传的凭证,只有获得了这个凭证,才可以进行直传,至于到底如何获取的,那是后端的事情了。然后获取到这个凭证之后,就会携带这个凭证,并带上文件直接向阿里云服务器发请求,直接保存到了阿里云服务器。 

2. 在我们的项目中具体如何使用这个文件上传?

这里才是真正需要注意的地方,其他其实都不重要

1. 修改单文件上传组件中的阿里云的桶地址(文件夹地址)

改成自己的地址就行了,地址可以在阿里云官网进行查看,在这个位置:

上面其实还是修改组件,下面是真正的将组件用到我们的项目中去:还是那几步:

1. 引组件

2. 在vue实例中声明好组件

3. 在模板的所需位置使用组件

完成了这些步骤,文件上传的组件也就在修改页展现出来了:

 展示出来了,也就代表这项工作成功的完成了。其实也很简单,就是在我们的前端项目中直接引入好老师直接提供好的前端文件上传所需的一些文件,然后改一下桶路径,然后在项目中引入直接使用即可。

2.6.3 优化三:将表格列表的logo地址列改为显示图片

这里有着之前改成开关的例子之后就变得很简单了,也是一样的既然是在表格中,要修改表格列,就只需要在那一列,使用template,然后在里面放上图片组件即可。

<el-table-column label="品牌logo" align="center" prop="logo">
        <template slot-scope="scope">
          <el-image
            style="width: 50px; height: 50px"
            :src="scope.row.logo"
            fit="contain"
          ></el-image>
        </template>
      </el-table-column>

三、后端部分

总的来说后端这里很单纯,总是,哈哈哈,当然只是需要业务简单,反正写到目前为止,前端都相较于后端反而要繁琐一点。

在这里后端的话其实就一个接口:获取文件上传凭证接口。

在说这个接口之前,我先说一下,这里文件上传的解决方案;

文件上传我们其实比较熟悉了,就是上传到服务器嘛,这里使用的是oss文件上传技术,oss文件上传常用的有两种上传方式:

第一种:很简洁明了的方式,直接将文件发送到我们项目的后端服务器上面,然后后后端服务器再调用oss的api实现文件的上传,也就是说这里其实文件是直接到了我们的后端服务器上面去的,给我们的后端服务器一定的压力。

第二种:携带凭证的方式直接将文件上传到阿里云服务器,这种方式就是先携带文件的一些信息,比如说文件名,bucket名,bucket域名等信息,发送到我们的后端服务器,然后后端服务器调用oss的api获得凭证,然后发给前端,然后前端再携带上这个凭证以及文件,然后直接发到oss服务器。也就是说这种方式其实是并没有将文件直接上传到我们自己的服务器上面去的,减轻了我们自己的服务器的压力。

好,现在oss的上传方式大致理解了,我们现在采用第二种方式进行上传。

既然知道了方案,实施起来难不难呢?其实不难,其实这里后端的工作就是获取凭证,这里的凭证获取归根结底其实并不是我们直接获取的,还是调用oss的api进行获取的,那这个api到底是什么呢?这里就需要访问阿里云的官网,到官网中oss对象存储的文档那里去查看了,这里我就不查看了,其实不重要,我这里直接贴代码,能用就行了。

对了,这里的话,因为是oss文件上传,其实不是业务模块了,这里是第三方服务了,因此我新建一个第三方服务,以后的所有的第三方服务全放到这里,比如说还有短信服务,金融服务,物流服务等。

这个服务的创建简单说一下:

这并非业务服务,因此并不需要像业务服务那样,需要依赖那么多的依赖,只需要简单的几个依赖就行了,像这也是一个服务,以后使用网关路由到这里,因此得注册到nacos注册中心去,因此那cos注册中心的依赖少不了,还有现在是需要oss服务,因此oss服务依赖少不了(这里我并不是直接导入原生的oss依赖了,而是导入对应的starter,这样就是将方法更加封装起来了,可以拿来直接使用,一些参数只需要到配置文件中配好就行了)

1. pom文件的创建

这里这个第三方服务的pom服务我直接给出,这里我也还是直接引入的comment模块,其实也可以直接引入的,但是我这种方式还得注意排除掉一些依赖,比如说MP和security的依赖这是不需要的:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.sq</groupId>
        <artifactId>gulimall</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>thirdparty</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <dependency>
            <groupId>com.sq</groupId>
            <artifactId>gulimall-common</artifactId>
            <version>1.0-SNAPSHOT</version>
            <exclusions>
                <exclusion>
                    <groupId>com.baomidou</groupId>
                    <artifactId>mybatis-plus-boot-starter</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-security</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-alicloud-oss</artifactId>
            <version>0.9.0.RELEASE</version>
        </dependency>
    </dependencies>

</project>

2. 配置文件

application.yml:

server:
  port: 8081
spring:
  cloud:
    util:
      enabled: false
    nacos:
      discovery:
        server-addr: localhost:8848
#    alicloud:
#      access-key:
#      secret-key:
#      oss:
#        endpoint:
  application:
    name: thirdparty


  autoconfigure:
    exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration

这里由于使用了nacos配置中心,bootstrap.properties引导文件也是少不了的,里面主要写好文件的位置,文件的namespece命名空间,分组等信息,这里的话使用的是项目中的常用用法:就是配置中心中配的方式是采用配置分离的方式,也就是将原本在Java程序配置文件中的application.yml文件进行拆解,拆解成多个文件,比如说数据源可以拆开一个文件,注册中心又是一个文件,其他配置又是一个文件等等拆解成若干个文件,这样更加方便管理。

话不多说,这里直接给上bootstrap.properties里面的内容,其实就只需要注意一下,这种多文件怎么配置就行了:

spring.cloud.nacos.config.server-addr=localhost:8848
spring.application.name=thirdparty

spring.cloud.nacos.config.namespace=30366fc8-9ac7-44ef-846d-3ec8009fce8b
spring.cloud.nacos.config.ext-config[0].data-id=oss.yaml
spring.cloud.nacos.config.ext-config[0].group=DEFAULT_GROUP
spring.cloud.nacos.config.ext-config[0].refresh=true

配置中心的样子长这样:

 3. 接口

也就是直接复制阿里云oss官网中给的样例,改一下就行了:

核心文件(从阿里云官网oss文档荡下来的)

主要在这里bucke和dir需要自己配一下,其他AK和endpoint从nacos配置中心中读取

public AjaxResult policy(){
        String bucket = "sq-bucket-02"; // 请填写您的 bucketname 。
        String host = "http://" + bucket + "." + endpoint; // host的格式为 bucketname.endpoint
        String dir = new SimpleDateFormat("yyyy/MM/dd").format(new Date());// 用户上传文件时指定的前缀。
        Map<String, String> respMap = new HashMap<>();
        try {
            long expireTime = 30;
            long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
            Date expiration = new Date(expireEndTime);
            PolicyConditions policyConds = new PolicyConditions();
            policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
            policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);
            String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
            byte[] binaryData = postPolicy.getBytes("utf-8");
            String encodedPolicy = BinaryUtil.toBase64String(binaryData);
            String postSignature = ossClient.calculatePostSignature(postPolicy);
            respMap = new LinkedHashMap<String, String>();
            respMap.put("accessid", accessKey);
            respMap.put("policy", encodedPolicy);
            respMap.put("signature", postSignature);
            respMap.put("dir", dir);
            respMap.put("host", host);
            respMap.put("expire", String.valueOf(expireEndTime / 1000));
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        return AjaxResult.success(respMap);
    }

接口完整代码:

@RestController
@RequestMapping("/thirdparty/oss")
public class OssController {

    @Autowired
    private OSS ossClient;

    @Value(value = "${spring.cloud.alicloud.oss.endpoint}")
    private String endpoint;

    @Value(value = "${spring.cloud.alicloud.access-key}")
    private String accessKey;



    @RequestMapping("/policy")
    public AjaxResult policy(){
        String bucket = "sq-bucket-02"; // 请填写您的 bucketname 。
        String host = "http://" + bucket + "." + endpoint; // host的格式为 bucketname.endpoint
        String dir = new SimpleDateFormat("yyyy/MM/dd").format(new Date());// 用户上传文件时指定的前缀。
        Map<String, String> respMap = new HashMap<>();
        try {
            long expireTime = 30;
            long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
            Date expiration = new Date(expireEndTime);
            PolicyConditions policyConds = new PolicyConditions();
            policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
            policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);
            String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
            byte[] binaryData = postPolicy.getBytes("utf-8");
            String encodedPolicy = BinaryUtil.toBase64String(binaryData);
            String postSignature = ossClient.calculatePostSignature(postPolicy);
            respMap = new LinkedHashMap<String, String>();
            respMap.put("accessid", accessKey);
            respMap.put("policy", encodedPolicy);
            respMap.put("signature", postSignature);
            respMap.put("dir", dir);
            respMap.put("host", host);
            respMap.put("expire", String.valueOf(expireEndTime / 1000));
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        return AjaxResult.success(respMap);
    }

}

4. 在网关中进行注册配置

最后一步很重要,这里又新增了一个服务,在前端都只会像网关发请求,至于需要网关发到我们的服务中去,就需要到网关中配置好,这个第三方服务的“关键信息”是thirdparty,因此在网关中配置上即可。

 

        - id: thirdparty_path_route
          uri: lb://thirdparty
          predicates:
            - Path=/thirdparty/**

注意:这里之前前端那里需要请求这个接口,在那个前端api文件中,的请求路径得与后端路径保持一致

四、总结

这次其实任务量还挺大的,但是仔细一想其实还是简单的,使用逆向生成的前端代码,已经帮我们省去了百分之80的内容。

这里前端就干了三件事:

1. 将状态列改成开关

2. 在修改页将logo地址改成文件上传按钮

3. 在表格列表中logo列改成图片

后端就干了一件事:获取凭证。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/819153.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Day15-作业(Maven高级)

作业1&#xff1a;完成苍穹外卖项目目录结构创建 说明&#xff1a; 1.sky-take-out 是父工程 2.sky-common/sky-pojo/sky-server 是子工程 3.sky-server模块引用了sky-common/sky-pojo模块 作业2&#xff1a;完成汇客CRM项目目录结构创建 说明&#xff1a; huike-paren…

判断变量是否为标量(没有维度,单个数值)numpy.isscalar()

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 判断变量是否为标量 &#xff08;没有维度,单个数值&#xff09; numpy.isscalar() [太阳]选择题 请问关于以下代码表述错误的是&#xff1f; import numpy as np a 0 b np.array([0]) pri…

六、初始化和清理(3)

本章概要 成员初始化构造器初始化 初始化的顺序静态数据的初始化显式的静态初始化非静态实例初始化 成员初始化 Java 尽量保证所有变量在使用前都能得到恰当的初始化。对于方法的局部变量&#xff0c;这种保证会以编译时错误的方式呈现&#xff0c;所以如果写成&#xff1a…

初识Java - 概念与准备

本笔记参考自&#xff1a; 《On Java 中文版》 目录 写在第一行 Java的迭代与发展 Java的迭代 Java的参考文档 对象的概念 抽象 接口 访问权限 复用实现 继承 基类和子类 A是B和A像B 多态 单根层次结构 集合 参数化类型 对象的创建和生命周期 写在第一行 作为一…

Charles抓包工具使用(一)(macOS)

Fiddler抓包 | 竟然有这些骚操作&#xff0c;太神奇了&#xff1f; Fiddler响应拦截数据篡改&#xff0c;实现特殊场景深度测试&#xff08;一&#xff09; 利用Fiddler抓包调试工具&#xff0c;实现mock数据特殊场景深度测试&#xff08;二&#xff09; 利用Fiddler抓包调试工…

pytorch-gpu 极简安装

1、进入pytoch官网&#xff1a;PyTorch 找到pytorch-gpu版本&#xff0c;看到CUDA11.8、11.7、CPU&#xff0c;这里我选择安装CUDA11.8 2、下载CUDA Toolkit&#xff1a;CUDA Toolkit 11.8 Downloads | NVIDIA Developer 3、下载CUDANN&#xff1a;cuDNN Download | NVIDIA D…

TCP拥塞控制详解 | 1. 概述

网络传输问题本质上是对网络资源的共享和复用问题&#xff0c;因此拥塞控制是网络工程领域的核心问题之一&#xff0c;并且随着互联网和数据中心流量的爆炸式增长&#xff0c;相关算法和机制出现了很多创新&#xff0c;本系列是免费电子书《TCP Congestion Control: A Systems …

基于Spring Boot的鲜花销售网站设计与实现(Java+spring boot+MySQL)

获取源码或者论文请私信博主 演示视频&#xff1a; 基于Spring Boot的鲜花销售网站设计与实现&#xff08;Javaspring bootMySQL&#xff09; 使用技术&#xff1a; 前端&#xff1a;html css javascript jQuery ajax thymeleaf 微信小程序 后端&#xff1a;Java springboot…

运算放大器(三):差分放大

一、定义 差分放大电路又称为差动放大电路&#xff0c;当该电路的两个输入端的电压有差别时&#xff0c;输出电压才有变动&#xff0c;因此称为差动。 二、电路结构 常用的差分放大电路如下 图1 所示&#xff0c; 图1 根据虚短和虚断可得&#xff1a;

C# Blazor 学习笔记(0):初识Blazor

文章目录 Blazor是什么适合人群 开始学习BlazorBlazor资源如何创建BlazorBlazor 基础知识介绍文件分布Razor和cshtml的区别Razor介绍 Blazor是什么 Blazor是微软推出的前端框架&#xff0c;有两种形式&#xff0c;以下以Blazor Server为主。具有一下特点 前端是用C#而不是JS前…

Linux编辑器 - vim使用

1.vim的基本概念 Vim是一个广泛使用的文本编辑器&#xff0c;它是在Unix和Linux系统中常用的命令行文本编辑器之一。 vim的主要三种模式 ( 其实有好多模式&#xff0c;目前掌握这 3 种即可 ), 分别是 命令模式 &#xff08; command mode &#xff09;、 插入模式 &#xff0…

仿第八区分发封装打包系统源码教程-轻松打包app

这个是最新的修改优化&#xff0c;优化了一些小bug不能用的问题&#xff0c;具体什么就不讲了&#xff0c;用过的应该有知道的。 大致功能&#xff1a; 支持一键安卓打包&#xff0c;IOS免签、绿标&#xff01;&#xff08;几乎媲美原生&#xff09; 拥有此款神器&#xff0…

AppCompatActivity.setContentView(与activity.setContentView区别)方法解读

AppCompatActivity.setContentView()与Activity.setContentView()主要的区别&#xff0c;Activity.setContentView直接将视图添加到Window上&#xff0c;AppCompatActivity.setContentView()借助AppCompatActivity的Delegate代理类&#xff0c;将要显示的视图加入到代理层视图&…

【网络原理】 (3) (网络层 IP协议 地址管理 路由选择 数据链路层 以太网 MTU 补充:DNS)

文章目录 网络层IP协议地址管理路由选择 数据链路层以太网MTU补充:DNS 网络层 IP协议 网络层的代表,IP协议. 4位版本号&#xff08;version&#xff09;&#xff1a;指定IP协议的版本&#xff0c;对于IPv4来说&#xff0c;就是4。4位头部长度&#xff08;header length&…

98. Python基础教程:try...except...finally语句

【目录】 文章目录 1. try...except...finally语法介绍2. try...except...finally执行顺序3. 捕获特定类型的异常4. 捕获所有类型的异常5. 实操练习-打开txt文件并输出文件内容 【正文】 在今天的课程中&#xff0c;我们将学习Python中的异常处理语句try...except...finally。 …

excel英语翻译让你的数据更容易被理解

从前有一个名叫小明的办公室职员&#xff0c;他每天都要处理大量的数据和报表。然而&#xff0c;由于工作需要&#xff0c;他经常收到来自不同国家的Excel表格&#xff0c;这些表格上的内容都是用各种各样的语言编写的&#xff0c;让他很难理解其中的意思。这时&#xff0c;小明…

Qt Creator 11 开放源码集成开发环境新增集成终端和 GitHub Copilot 支持

导读Qt 项目今天发布了 Qt Creator 11&#xff0c;这是一款开源、免费、跨平台 IDE&#xff08;集成开发环境&#xff09;软件的最新稳定版本&#xff0c;适用于 GNU/Linux、macOS 和 Windows 平台。 Qt Creator 11 的亮点包括支持标签、多外壳、颜色和字体的集成终端模拟器&am…

建模教程:如何利用3ds Max 和 After Effects 实现多通道渲染和后期合成

推荐&#xff1a; NSDT场景编辑器助你快速搭建可二次开发的3D应用场景 1. 创建基本场景 步骤 1 打开 3ds Max。在 透视视口。 打开 3ds Max 步骤 2 做一个茶壶&#xff0c;放在飞机上。 制作茶壶 步骤 3 我在场景中应用了几个灯光。我选择了光线追踪阴影作为阴影。 光线追…

走进人工智能| 智能物联网 AIoT的魅力交织

前言&#xff1a; AIIoT是指人工智能&#xff08;AI&#xff09;与物联网&#xff08;IoT&#xff09;的结合。智能物联网是一种技术体系&#xff0c;通过连接和集成物理设备、传感器和互联网&#xff0c;实现设备之间的智能交互和数据共享&#xff0c;为人们提供智能化、自动化…

赛车游戏——【极品飞车】(内含源码inscode在线运行)

前言 「作者主页」&#xff1a;雪碧有白泡泡 「个人网站」&#xff1a;雪碧的个人网站 「推荐专栏」&#xff1a; ★java一站式服务 ★ ★前端炫酷代码分享 ★ ★ uniapp-从构建到提升★ ★ 从0到英雄&#xff0c;vue成神之路★ ★ 解决算法&#xff0c;一个专栏就够了★ ★ 架…