前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >黑马瑞吉外卖之后台登录与退出功能开发

黑马瑞吉外卖之后台登录与退出功能开发

作者头像
兰舟千帆
发布2022-11-30 16:13:08
5380
发布2022-11-30 16:13:08
举报

黑马瑞吉外卖之后台登录功能开发

登录的前端分析与后端开发

说明

这个项目是基于springboot+mybatisplus作为核心的开发项目。是一款外卖开发项目。本次还是从后台管理界面进行开发的,前些天敲完了基本,后来还是给自己遗留了一个bug,项目还有没有完善的部分,现在就从写博客这里重新捋一遍。这样也许更有效果。很多人觉得简单,但是我觉得这是一个非常重要的项目,是一次真正意义上的前后堵的人项目。很值得我们去多家回顾练习。

本篇从后台的一个登录界面开始。

在这里插入图片描述
在这里插入图片描述

这里前端页面已经给好了,但是我们后端还是需要去看懂。

资料准备

这是我们需要的数据表。

在这里插入图片描述
在这里插入图片描述

用户登录啊需要这个表,employss,默认里面只有一个超级管理员级别的用户。我这里后来新增了一个。

在这里插入图片描述
在这里插入图片描述

employee建表语句

代码语言:javascript
复制
DROP TABLE IF EXISTS `employee`;
CREATE TABLE `employee` (
  `id` bigint(20) NOT NULL COMMENT '主键',
  `name` varchar(32) COLLATE utf8_bin NOT NULL COMMENT '姓名',
  `username` varchar(32) COLLATE utf8_bin NOT NULL COMMENT '用户名',
  `password` varchar(64) COLLATE utf8_bin NOT NULL COMMENT '密码',
  `phone` varchar(11) COLLATE utf8_bin NOT NULL COMMENT '手机号',
  `sex` varchar(2) COLLATE utf8_bin NOT NULL COMMENT '性别',
  `id_number` varchar(18) COLLATE utf8_bin NOT NULL COMMENT '身份证号',
  `status` int(11) NOT NULL DEFAULT '1' COMMENT '状态 0:禁用,1:正常',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  `update_time` datetime NOT NULL COMMENT '更新时间',
  `create_user` bigint(20) NOT NULL COMMENT '创建人',
  `update_user` bigint(20) NOT NULL COMMENT '修改人',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE KEY `idx_username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='员工信息'

就是这些字段,你建一个数据库,然后在数据库下创建这个表。

这是用户表,然后我们需要前端界面。

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>瑞吉外卖管理端</title>
  <link rel="shortcut icon" href="../../favicon.ico">
  <!-- 引入样式 -->
  <link rel="stylesheet" href="../../plugins/element-ui/index.css" />
  <link rel="stylesheet" href="../../styles/common.css">
  <link rel="stylesheet" href="../../styles/login.css">
  <link rel="stylesheet" href="../../styles/icon/iconfont.css" />
  <style>
    .body{
      min-width: 1366px;
    }
  </style>
</head> 

<body>
  <div class="login" id="login-app">
    <div class="login-box">
      <img src="../../images/login/login-l.png" alt="">
      <div class="login-form">
        <el-form ref="loginForm" :model="loginForm" :rules="loginRules" >
          <div class="login-form-title">
            <img src="../../images/login/logo.png" style="width:139px;height:42px;" alt="" />
          </div>
          <el-form-item prop="username">
            <el-input v-model="loginForm.username" type="text" auto-complete="off" placeholder="账号" maxlength="20"
              prefix-icon="iconfont icon-user" />
          </el-form-item>
          <el-form-item prop="password">
            <el-input v-model="loginForm.password" type="password" placeholder="密码" prefix-icon="iconfont icon-lock" maxlength="20"
              @keyup.enter.native="handleLogin" />
          </el-form-item>
          <el-form-item style="width:100%;">
            <el-button :loading="loading" class="login-btn" size="medium" type="primary" style="width:100%;"
              @click.native.prevent="handleLogin">
              <span v-if="!loading">登录</span>
              <span v-else>登录中...</span>
            </el-button>
          </el-form-item>
        </el-form>
      </div>
    </div>
  </div>

  <!-- 开发环境版本,包含了有帮助的命令行警告 -->
  <script src="../../plugins/vue/vue.js"></script>
  <!-- 引入组件库 -->
  <script src="../../plugins/element-ui/index.js"></script>
  <!-- 引入axios -->
  <script src="../../plugins/axios/axios.min.js"></script>
  <script src="../../js/request.js"></script>
  <script src="../../js/validate.js"></script>
  <script src="../../api/login.js"></script>

  <script>
    new Vue({
      el: '#login-app',
      data() {
        return {
          loginForm:{
            //默认添加的数据
            username: 'admin',
            password: '123456'
          },
          loading: false
        }
      },
      computed: {
        loginRules() {
          const validateUsername = (rule, value, callback) => {
            if (value.length < 1 ) {
              callback(new Error('请输入用户名'))
            } else {
              callback()
            }
          }
          const validatePassword = (rule, value, callback) => {
            if (value.length < 6) {
              callback(new Error('密码必须在6位以上'))
            } else {
              callback()
            }
          }
          return {
            'username': [{ 'validator': validateUsername, 'trigger': 'blur' }],
            'password': [{ 'validator': validatePassword, 'trigger': 'blur' }]
          }
        }
      },
      created() {
      },
      methods: {
        async handleLogin() {
          this.$refs.loginForm.validate(async (valid) => {//表单验证
            if (valid) {
              this.loading = true
              let res = await loginApi(this.loginForm)
              if (String(res.code) === '1') {
                //这个代码是吧返回的数据保持在浏览器中:
                localStorage.setItem('userInfo',JSON.stringify(res.data))
                window.location.href= '/backend/index.html'
              } else {
                this.$message.error(res.msg)
                this.loading = false
              }
            }
          })
        }
      }
    })
  </script>
</body>

</html>

css那些资源就不说了。黑马视频哪里都有视频给定的,

在这里插入图片描述
在这里插入图片描述

配置信息

代码语言:javascript
复制
server:
  port: 8080
spring:
  application:
#    应用的名称,可选
    name: reggie_take_out
  datasource:
    druid:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/reggie?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
      username: root
      password: 123456
mybatis-plus:
  configuration:
    #在映射实体或者属性时,将数据库中表名和字段名中的下划线去掉,按照驼峰命名法映射
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    db-config:
#      主键生成策略
      id-type: ASSIGN_ID
reggie:
  path: D:\

configer 配置,这个是分页拦截器mybatisplus的,我们后面分页查询会用到

mvc的配置,主要做了静态资源映射和做了一个自定义的对象转换器,这个后面会用到

代码语言:javascript
复制
package com.jgdabc.config;

import com.jgdabc.common.JacksonObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

import java.util.List;

@Slf4j
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
//    设置静态资源映射
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
//        super.addResourceHandlers(registry);
       registry.addResourceHandler("/backend/**").addResourceLocations("classpath:/backend/");
       registry.addResourceHandler("/front/**").addResourceLocations("classpath:/front/");
       log.info("项目启动,开始静态资源映射");
    }
//    扩展mvc消息的转换器
    protected void extendMessageConverters(List<HttpMessageConverter<?>> converters)
    {
        MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
//        设置1对象转换器
        mappingJackson2HttpMessageConverter.setObjectMapper(new JacksonObjectMapper());
//        将上面的消息转换器对象最佳到mvc的转换器集合中
        converters.add(0,mappingJackson2HttpMessageConverter);
    }
}
代码语言:javascript
复制
package com.jgdabc.config;

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
//Mybatis拦截器设计的一个初衷就是为了供用户在某些时候可以实现自己的逻辑而不必去动Mybatis固有的逻辑。
//配置mp的分页插件
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor()
    {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return  mybatisPlusInterceptor;

    }
}

项目所需的依赖主要,注释说明具体作用

代码语言:javascript
复制
    <dependencies>
        <dependency>
<!--            springbbot起步依赖-->
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
<!--测试依赖,其实这里也没有用到-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
<!--        因为是web项目,所以需要这个依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <scope>compile</scope>
        </dependency>
<!--        mybatisplus的依赖-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.2</version>
        </dependency>
<!--  Lombok能通过注解的方式,在编译时自动为属性生成构造函数、getter/setter、equals、hashcode、toString等方法。      -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
<!--      它可以解析JSON格式的字符串,支持将Java Bean序列化为JSON字符串  -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>2.0.7</version>
        </dependency>
<!--        Commons Lang这一组API主要是提供一些基础的、通用的操作和处理,如自动生成toString()的结果
、自动实现hashCode()和equals()方法、数组操作、枚举、日期和时间的处理等等。-->
        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>
<!--       数据库连接 驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
<!--            Druid Spring Boot Starter 用于帮助你在Spring Boot项目中轻松集成Druid 数据库 连接池和监控。-->
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
           <version>1.2.11</version>
        </dependency>
<!--        下面这两个是阿里云的短信服务用到的依赖,可选-->
        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>aliyun-java-sdk-core</artifactId>
            <version>4.5.16</version>
        </dependency>
<!--        -->
        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>aliyun-java-sdk-dysmsapi</artifactId>
            <version>1.1.0</version>
        </dependency>
    </dependencies>

前端登录的部分分析

你单独打开这个前端界面,就是这样子

在这里插入图片描述
在这里插入图片描述

当我们打开这个界面的时候有一个数据自动填充的。这里已经写好了。 在哪呢?看这个登录的前端界面,这里有一个数据模型。

在这里插入图片描述
在这里插入图片描述

然后在上面表单也就是下面这段进行了一个数据模型绑定,所以当我们打开界面的时候就可以显示出来。

在这里插入图片描述
在这里插入图片描述

当前我们也可以自己填,没啥问题,这里会进行一个数据模型的双向绑定。 我们需要点击一个登录按钮,去找这个按钮,它必定绑定了一个方法。

如下这个登录按钮绑定了一个方法,当我们点击登录的时候这个方法就会被执行。

在这里插入图片描述
在这里插入图片描述

这个方法的具体实现逻辑在下面。拉到下面去看。首先它进行了一个表单验证,然后发了一个请求,给到LoginApi这个方法。点进去。

在这里插入图片描述
在这里插入图片描述

当请求发出得到的返回值为1的时候就会代表验证成功,然后将用户这个信息存储的浏览器当中,为什么需要一个存储,这里需要看登录进去的管理界面。因为我们登录进去到管理界面需要在这里展示一个用户。

在这里插入图片描述
在这里插入图片描述

另外来看管理界面的前端这个值会取到这里,这样就展示了。

在这里插入图片描述
在这里插入图片描述

这里是调用了是调用到了另一个方法,这个方法写在另一个js里面。在这里我们就可以清清楚楚的看到它的请求路径,然后你就知道Controller怎么写了。至少路径知道了怎么设置。并且这里发了一个post提交了data,这个data就是上面登录传来的LoginForm,数据就是用户和密码。 然后会进行一个界面的跳转,跳转到另一个html里面,就是一个后台的管理界面。

在这里插入图片描述
在这里插入图片描述

其中对于登录还有一个验证校验,这个是自动执行的。 computed 这里插一段是一段自动指定的验证。这个方法会自动验证。

在这里插入图片描述
在这里插入图片描述

我们的逻辑理清后,就可以开发后端功能了,面向前端编程是吧!

登录功能开发

后端这里我们首先需要和用户表做一个映射的实体类。

在这里插入图片描述
在这里插入图片描述
代码语言:javascript
复制
package com.jgdabc.entity;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;

import java.io.File;
import java.io.Serializable;
import java.time.LocalDateTime;

@Data
public class Employee implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long id;

    private String username;

    private String name;

    private String password;

    private String phone;

    private String sex;

    private String idNumber;

    private Integer status;
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;

    @TableField(fill = FieldFill.INSERT)
    private Long createUser;

    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Long updateUser;

}

这里有一个知识点就是序列化的知识点。

代码语言:javascript
复制
public class Employee implements Serializable 

序列化的作用就是将对象存储在介质中,以便在下次使用的时候可以快速建立一个副本。便于数据传输尤其是远程调用,另外就是在微服务当中这种有点可以很好的体现,我们先明白这个作用就好。这个接口脸面什么都没有,只是作为一种序列化的标识。

然后你看下面的字段

在这里插入图片描述
在这里插入图片描述

这是mybatisplus的字段自动填充属性。这个有什么用呢?因为将来会出现公共字段属性,在具体的业务逻辑中对实体类赋值的时候就可以不用对这些公共字段进行赋值,这些字段会自动赋值,这样就可以避免重复繁琐的赋值操作。

这个在后面慢慢就会了解到。

这里啊!我们为了方便,做出了一个泛型包装类。如下。这个泛型包装类主要用于包装返回的信息。

代码语言:javascript
复制
package com.jgdabc.common;

import lombok.Data;
import java.util.HashMap;
import java.util.Map;
//通用返回结果
@Data
public class R_<T> {

    private Integer code; //编码:1成功,0和其它数字为失败

    private String msg; //错误信息

    private T data; //数据

    private Map map = new HashMap(); //动态数据
//这样的泛型设计非常巧妙了
    public static <T> R_<T> success(T object) {
        R_<T> r = new R_<T>();
        r.data = object;//这里可以接收到返回的值
        r.code = 1;
        return r;
    }

    public static <T> R_<T> error(String msg) {
        R_ r = new R_();
        r.msg = msg;
        r.code = 0;
        return r;
    }

    public R_<T> add(String key, Object value) {
        this.map.put(key, value);
        return this;
    }

}

然后看dao层,也就是mapper层

代码语言:javascript
复制
package com.jgdabc.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jgdabc.entity.Employee;
import org.apache.ibatis.annotations.Mapper;

// Mapper 继承该接口后,无需编写 mapper.xml 文件,即可获得CRUD功能
@Mapper
public interface EmployMapper extends BaseMapper<Employee> {
}

然后看service层

代码语言:javascript
复制
package com.jgdabc.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.jgdabc.entity.Employee;
import org.springframework.stereotype.Service;

/**
 * 除了 BaseMapper 接口,MyBatis Plus 还提供了 IService 接口,该接口对应 Service 层。MyBatis Plus 的通用 Service CRUD 实现了 IService 接口,
 * 进一步封装 CRUD。为了避免与 BaseMapper 中定义的方法混淆,该接口使用 get(查询单行)、remove(删除)、list(查询集合)和 page(分页)前缀命名的方式进行区别。
 */

public interface EmployService extends IService<Employee> {
}

service的实现

代码语言:javascript
复制
package com.jgdabc.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jgdabc.entity.Employee;
import com.jgdabc.mapper.EmployMapper;
import com.jgdabc.service.EmployService;
import org.springframework.stereotype.Service;
/*
MyBatis Plus 使用 ServiceImpl 类实现 IService 接口
 */
@Service
public class EmployServiceImpl extends ServiceImpl<EmployMapper, Employee> implements EmployService {

}

要用mybatisplus,这些都是统一的逻辑。dao层继承BaseMapper,然后泛型是实体类,service层继承IService,然后泛型是实体类。service层的实现类 继承ServiceImpl,然后泛型有两个,一个是mapper类,一个实体类,然后最终实现service。都是这样去写。

我们来看controller的实现逻辑

代码语言:javascript
复制
package com.jgdabc.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.jgdabc.common.R_;

import com.jgdabc.entity.Employee;
import com.jgdabc.service.EmployService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;

@Slf4j
@RestController
@RequestMapping("/employee")

public class EmployController {
    @Autowired
    private EmployService employService;
//RequestBody主要用于接收前端传递给后端的json数据
    @PostMapping("/login")
    public R_<Employee> login(HttpServletRequest request, @RequestBody Employee employee) {
        这里为什么还有接收一个request对象的数据?
        //        //登陆成功后,我们需要从请求中获取员工的id,并且把这个id存到session中,这样我们想要
//        密码加密处理
        String password = employee.getPassword();
//        md5加密
        password = DigestUtils.md5DigestAsHex(password.getBytes());
        LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Employee::getUsername, employee.getUsername());
        在设计数据库的时候我们对username使用了唯一索引,所以这里可以使用getOne方法
        Employee emp = employService.getOne(queryWrapper);
//        如果没有查到就返回登录失败结果
        if (emp == null) { //如果是空的信息就没有,所以失败,返回全部封装给R_
            return R_.error("登录失败");
        }
//        如果密码比对不成功,也返回登录失败
        if (!emp.getPassword().equals(password)) {
                return R_.error("登录失败");

        }
        if(emp.getStatus()==0) //账号登录也不能登录的就是禁用账号
        {
            return  R_.error("账号已经禁用");

        }
//        登录成功,将id放到session里面,后期有用
         request.getSession().setAttribute("employee",emp.getId());
        //把从数据库中查询到的用户返回出去
        return R_.success(emp);

    }
// 

    
}

我们把前端提交过来的用户名和密码直接用employee接收。对密码进行了一个加密。然后调用mybatisplus的方法给我们去查询用户,因为用户名必然是惟一的,并且我们设置了用户名唯一的特性,所以可以调用这个方法。然后就是判断逻辑,根据判断逻辑返回不同的包装的信息。

最后启动启动类

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

默认的用户和密码数据库都有的,所以后端只要没写的有问题,是可以登录进去的。这里展示的其实到这里员工信息还没有正常展示的,因为我这些都做过了,所以会展示出来,现在我们只是进去了页面。

在这里插入图片描述
在这里插入图片描述

登录就到在这里。

退出功能开发

这是我们的后台登录功能,我们首次登录会进入这样的界面。我们所做的·后台退出功能就是点击右上角的按钮会退出到登录界面。这个功能的实现还是比较简单的。其实就是一个对按钮的响应。逻辑可以是点击这个按钮后跳转到登录界面。

在这里插入图片描述
在这里插入图片描述

在表单里面看这个按钮在哪里。这个后台管理界面是index.html。

首先看到这个登录按钮这里绑定了一个退出的功能

在这里插入图片描述
在这里插入图片描述

这个方法绑定在这里。

在这里插入图片描述
在这里插入图片描述

然后这里还调用了一个方法叫做LogoutApi 点开这个方法,其实是在另一个js里面。可以看到这里发了一个post请求,但是没有携带任何参数。请求路径就是我们Controller里面要写的一个请求路径

在这里插入图片描述
在这里插入图片描述

退出登录要做哪些信息处理?既然退出的话,那么存储的用户信息这种信息消除掉,还有我们之前在Controller中存储的id。

我们来看Controller怎么写

代码语言:javascript
复制
 @PostMapping("/logout")
    public  R_<String> logout(HttpServletRequest request)
    {
//        清理Session保存的当前登录员工的id、
        request.getSession().removeAttribute("employee");
        return R_.success("退出成功");

这里我们清除了这个存储的id,至于这个id的存储和清理可以做哪些事情,其实后面我们需要做一个判断,判断什么呢?判断用户是是否登录。如果用户登录了那么久放行,如果没有就拦截返回登录界面。这样就可以防止不登录非法访问。这个是我们的拦截器里面的。

在这里插入图片描述
在这里插入图片描述

这里返回一个退出登录 return R_.success(“退出成功”);但是器是没有在页面展示到这段文字,只是这个success里面会有一个code为1。

在这里插入图片描述
在这里插入图片描述

然后前端页面接收到这个1就会跳转。这样我们就成功梳理出来了。

在这里插入图片描述
在这里插入图片描述

前端的活干不了,已经尽力去说明白了。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-07-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 黑马瑞吉外卖之后台登录功能开发
  • 登录的前端分析与后端开发
    • 说明
      • 资料准备
        • 前端登录的部分分析
          • 登录功能开发
            • 退出功能开发
            相关产品与服务
            对象存储
            对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档