免费分享一套基于SpringBoot实现商城系统(仿天猫),挺漂亮的

news2024/10/10 14:24:25

大家好,我是锋哥,看到一个不错的基于SpringBoot实现商城系统(仿天猫)系统,分享下哈。

项目介绍

迷你天猫商城是一个基于Spring Boot的综合性B2C电商平台,需求设计主要参考天猫商城的购物流程:用户从注册开始,到完成登录,浏览商品,加入购物车,进行下单,确认收货,评价等一系列操作。 作为迷你天猫商城的核心组成部分之一,天猫数据管理后台包含商品管理,订单管理,类别管理,用户管理和交易额统计等模块,实现了对整个商城的一站式管理和维护。

所有页面均兼容IE10及以上现代浏览器。

部署方式

  1. 项目使用IntelliJ IDEA开发,请使用IntelliJ IDEA的版本控制检出功能.
  2. 项目数据库为MySQL 5.7版本,在sqls文件夹中找到SQL文件并导入到数据库中。
  3. 使用IDEA打开项目后,在maven面板刷新项目,下载依赖包。
  4. 配置数据库连接并启动SpringBootApplication即可。

项目默认运行地址

  • 前台地址:http://localhost:8080/tmall
  • 后台地址:http://localhost:8080/tmall/admin

注意事项:

  1. 后台管理界面的订单图表没有数据为正常现象,该图表显示的为近7天的交易额。
  2. 该项目同时兼容eclipse,但如有自行扩展代码的意愿,建议使用IDEA。
  3. 该项目是我们几个学生在校合作完成的一个练习项目,目的是让编程初学者和应届毕业生可以参考一下用较少的代码实现一个完整MVC模式,Spring Boot体系的电商项目,相关领域大神们可以给我们建议,让我们做得更好。

项目界面

  • 前台界面(部分)---

登陆界面

 

首页

 

产品详情

 

下单界面

 

订单列表

 

确认收货

 

产品列表

 

购物车

  •  后台界面(部分)---

主页

所有产品

产品详情

产品分类

分类详情

用户管理

用户详情

订单列表

订单详情

我的账户

部分代码

package com.xq.tmall.controller.admin;

import com.alibaba.fastjson.JSONObject;
import com.xq.tmall.controller.BaseController;
import com.xq.tmall.entity.Admin;
import com.xq.tmall.service.AdminService;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpSession;
import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.UUID;

/**
 * 后台管理-账户页
 * @author 贤趣项目小组
 */
@Controller
public class AccountController extends BaseController{
    @Resource(name = "adminService")
    private AdminService adminService;

    //转到后台管理-账户页-ajax
    @RequestMapping(value = "admin/account", method = RequestMethod.GET)
    public String goToPage(HttpSession session, Map<String, Object> map){
        logger.info("获取管理员信息");
        Object adminId = checkAdmin(session);
        if(adminId == null){
            return "admin/include/loginMessage";
        }

        logger.info("获取目前登录的管理员信息,管理员ID:{}",adminId);
        Admin admin = adminService.get(null,Integer.parseInt(adminId.toString()));
        map.put("admin",admin);

        logger.info("转到后台管理-账户页-ajax方式");
        return "admin/accountManagePage";
    }

    //退出当前账号
    @RequestMapping(value = "admin/account/logout", method = RequestMethod.GET)
    public String logout(HttpSession session) {
        Object o = session.getAttribute("adminId");
        if (o == null) {
            logger.info("无相关信息,返回管理员登陆页");
        } else {
            session.removeAttribute("adminId");
            session.invalidate();
            logger.info("登录信息已清除,返回管理员登陆页");
        }
        return "redirect:/admin/login";
    }

    //管理员头像上传
    @ResponseBody
    @RequestMapping(value = "admin/uploadAdminHeadImage", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
    public String uploadAdminHeadImage(@RequestParam MultipartFile file, HttpSession session) {
        String originalFileName = file.getOriginalFilename();
        logger.info("获取图片原始文件名:{}", originalFileName);
        assert originalFileName != null;
        String extension = originalFileName.substring(originalFileName.lastIndexOf('.'));
        //生成随机名
        String fileName = UUID.randomUUID() + extension;
        //获取上传路径
        String filePath = session.getServletContext().getRealPath("/") + "res/images/item/adminProfilePicture/" + fileName;

        logger.info("文件上传路径:{}", filePath);
        JSONObject jsonObject = new JSONObject();
        try {
            logger.info("文件上传中...");
            file.transferTo(new File(filePath));
            logger.info("文件上传成功!");
            jsonObject.put("success", true);
            jsonObject.put("fileName", fileName);
        } catch (IOException e) {
            logger.warn("文件上传失败!");
            e.printStackTrace();
            jsonObject.put("success", false);
        }
        return jsonObject.toJSONString();
    }

    //更新管理员信息
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    @ResponseBody
    @RequestMapping(value = "admin/account/{admin_id}", method = RequestMethod.PUT, produces = "application/json;charset=UTF-8")
    public String updateAdmin(HttpSession session, @RequestParam String admin_nickname/*管理员昵称*/,
                              @RequestParam(required = false) String admin_password/*管理员当前密码*/,
                              @RequestParam(required = false) String admin_newPassword/*管理员新密码*/,
                              @RequestParam(required = false) String admin_profile_picture_src/*管理员头像路径*/,
                              @PathVariable("admin_id") String admin_id/*管理员编号*/) {
        logger.info("获取管理员信息");
        Object adminId = checkAdmin(session);
        if (adminId == null) {
            return "admin/include/loginMessage";
        }
        JSONObject jsonObject = new JSONObject();
        Admin putAdmin = new Admin();
        putAdmin.setAdmin_id(Integer.valueOf(admin_id));
        putAdmin.setAdmin_nickname(admin_nickname);

        if (admin_password != null && !"".equals(admin_password) && admin_newPassword != null && !"".equals(admin_newPassword)) {
            logger.info("获取需要修改的管理员信息");
            Admin admin = adminService.get(null, Integer.valueOf(adminId.toString()));
            if (adminService.login(admin.getAdmin_name(), admin_password) != null) {
                logger.info("原密码正确");
                putAdmin.setAdmin_password(admin_newPassword);
            } else {
                logger.info("原密码错误,返回错误信息");
                jsonObject.put("success", false);
                jsonObject.put("message", "原密码输入有误!");
                return jsonObject.toJSONString();
            }
        }
        if (admin_profile_picture_src != null && !"".equals(admin_profile_picture_src)) {
            logger.info("管理员头像路径为{}", admin_profile_picture_src);
            putAdmin.setAdmin_profile_picture_src(admin_profile_picture_src.substring(admin_profile_picture_src.lastIndexOf("/") + 1));
        }

        logger.info("更新管理员信息,管理员ID值为:{}", admin_id);
        Boolean yn = adminService.update(putAdmin);
        if (yn) {
            logger.info("更新成功!");
            jsonObject.put("success", true);
            session.removeAttribute("adminId");
            session.invalidate();
            logger.info("登录信息已清除");
        } else {
            jsonObject.put("success", false);
            logger.warn("更新失败!事务回滚");
            throw new RuntimeException();
        }

        return jsonObject.toJSONString();
    }
}

<%@ page contentType="text/html;charset=UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
    <script>
        $(function () {

            /******
             * event
             ******/
            //单击保存按钮时
            $("#btn_admin_save").click(function () {
                var admin_nickname = $.trim($("#input_admin_nickname").val());
                var admin_image_src = null;
                var admin_password = null;
                var admin_newPassword = null;
                if ($("#admin_profile_picture").hasClass("new")) {
                    admin_image_src = $.trim($("#admin_profile_picture").attr("src"));
                }
                if ($(".modifyPwd").css("display") === "block") {
                    admin_password = $.trim($("#input_admin_password").val());
                    admin_newPassword = $.trim($("#input_admin_newPassword").val());
                    var admin_confirmPassword = $.trim($("#input_admin_confirmPassword").val());
                    //校验数据合法性
                    if (admin_password === "") {
                        styleUtil.errorShow($("#text_password_details_msg"), "请输入原密码");
                        return;
                    }
                    if (admin_newPassword === "") {
                        styleUtil.errorShow($("#text_newPassword_details_msg"), "请输入新密码");
                        return;
                    }
                    if (admin_confirmPassword === "") {
                        styleUtil.errorShow($("#text_confirmPassword_details_msg"), "请重复输入一遍新密码");
                        return;
                    }
                    if (admin_password === admin_newPassword) {
                        styleUtil.errorShow($("#text_newPassword_details_msg"), "新密码和旧密码不能相同");
                        return;
                    }
                    if (admin_newPassword !== admin_confirmPassword) {
                        styleUtil.errorShow($("#text_confirmPassword_details_msg"), "两次输入的密码不一致");
                        return;
                    }
                }

                var dataList = {
                    "admin_nickname": admin_nickname,
                    "admin_profile_picture_src": admin_image_src,
                    "admin_password": admin_password,
                    "admin_newPassword": admin_newPassword
                };
                doAction(dataList, "admin/account/" +${requestScope.admin.admin_id}, "PUT");
            });
        });

        function modifyPwd() {
            var div = $(".modifyPwd");
            if (div.css("display") === "none") {
                div.slideDown();
            } else {
                div.slideUp();
                styleUtil.basicErrorHide($("#lbl_admin_password"))
                    .basicErrorHide($("#lbl_admin_newPassword"))
                    .basicErrorHide($("#lbl_admin_confirmPassword"));
            }
        }

        //管理员操作
        function doAction(dataList, url, type) {
            $.ajax({
                url: url,
                type: type,
                data: dataList,
                traditional: true,
                success: function (data) {
                    $("#btn_admin_save").attr("disabled", false).val("保存");
                    if (data.success) {
                        $("#btn-ok,#btn-close").unbind("click").click(function () {
                            $('#modalDiv').modal("hide");
                            setTimeout(function () {
                                //ajax请求页面
                                ajaxUtil.getPage("account", null, true);
                            }, 170);
                        });
                        $(".modal-body").text("信息保存成功!");
                        $('#modalDiv').modal();
                    } else {
                        styleUtil.errorShow($("#text_password_details_msg"), data.message);
                    }
                },
                beforeSend: function () {
                    $("#btn_admin_save").attr("disabled", true).val("保存中...");
                },
                error: function () {

                }
            });
        }

        //图片上传
        function uploadImage(fileDom) {
            //获取文件
            var file = fileDom.files[0];
            //判断类型
            var imageType = /^image\//;
            if (file === undefined || !imageType.test(file.type)) {
                $("#btn-ok").unbind("click").click(function () {
                    $("#modalDiv").modal("hide");
                });
                $(".modal-body").text("请选择图片!");
                $('#modalDiv').modal();
                return;
            }
            //判断大小
            if (file.size > 3192000) {
                $("#btn-ok").unbind("click").click(function () {
                    $("#modalDiv").modal("hide");
                });
                $(".modal-body").text("图片大小不能超过3M!");
                $('#modalDiv').modal();
                return;
            }
            //清空值
            $(fileDom).val('');
            var formData = new FormData();
            formData.append("file", file);
            //上传图片
            $.ajax({
                url: "/tmall/admin/uploadAdminHeadImage",
                type: "post",
                data: formData,
                contentType: false,
                processData: false,
                dataType: "json",
                mimeType: "multipart/form-data",
                success: function (data) {
                    $(".loader").css("display", "none");
                    if (data.success) {
                        $("#admin_profile_picture").addClass("new").attr("src", "${pageContext.request.contextPath}/res/images/item/adminProfilePicture/" + data.fileName);
                    } else {
                        alert("图片上传异常!");
                    }
                },
                beforeSend: function () {
                    $(".loader").css("display", "block");
                },
                error: function () {

                }
            });
        }
    </script>
    <style rel="stylesheet">
        #admin_profile_picture {
            border-radius: 5px;
        }

        .modifyPwd {
            display: none;
        }

        #uploadImage {
            vertical-align: middle;
            display: inline-block;
            position: relative;
            right: 88px;
            opacity: 0;
            width: 84px;
            height: 84px;
            border-radius: 5px;
            cursor: pointer;
            z-index: 999;
        }
    </style>
</head>
<body>
<div class="details_div_first">
    <input type="hidden" value="${requestScope.admin.admin_id}" id="details_admin_id"/>
    <div class="frm_div">
        <label class="frm_label text_info" id="lbl_admin_id">管理员编号</label>
        <span class="details_value" id="span_admin_id">${requestScope.admin.admin_id}</span>
    </div>
    <div class="frm_div">
        <label class="frm_label text_info" id="lbl_admin_name">账户名</label>
        <span class="details_value" id="span_admin_name">${requestScope.admin.admin_name}</span>
    </div>
</div>
<div class="details_div">
    <span class="details_title text_info">基本信息</span>
    <div class="frm_div">
        <label class="frm_label text_info" id="lbl_admin_profile_picture">管理员头像</label>
        <img
                src="${pageContext.request.contextPath}/res/images/item/adminProfilePicture/${requestScope.admin.admin_profile_picture_src}"
                id="admin_profile_picture" width="84px" height="84px"
                onerror="this.src='${pageContext.request.contextPath}/res/images/admin/loginPage/default_profile_picture-128x128.png'"/>
        <input type="file" onchange="uploadImage(this)" accept="image/*" id="uploadImage">
    </div>
    <div class="frm_div">
        <label class="frm_label text_info" id="lbl_admin_nickname" for="input_admin_nickname">管理员昵称</label>
        <input class="frm_input" id="input_admin_nickname" type="text" maxlength="50"
               value="${requestScope.admin.admin_nickname}"/>
    </div>
</div>
<div class="details_div">
    <span class="details_title text_info">管理员操作</span>
    <div class="frm_div">
        <span class="details_value td_wait"><a id="span_admin_modifyPwd" href="javascript:void(0)"
                                               onclick="modifyPwd()">修改密码</a></span>
    </div>
    <div class="frm_div">
        <span class="details_value td_wait"><a id="span_admin_logout"
                                               href="${pageContext.request.contextPath}/admin/account/logout">退出当前帐号</a></span>
    </div>
</div>
<div class="details_div details_div_last modifyPwd">
    <span class="details_title text_info">修改密码</span>
    <div class="frm_div">
        <label class="frm_label text_info" id="lbl_admin_password" for="input_admin_password">当前密码</label>
        <input class="frm_input" id="input_admin_password" type="password" maxlength="50"/>
        <span class="frm_error_msg" id="text_password_details_msg"></span>
    </div>
    <div class="frm_div">
        <label class="frm_label text_info" id="lbl_admin_newPassword" for="input_admin_newPassword">新密码</label>
        <input class="frm_input" id="input_admin_newPassword" type="password" maxlength="50"/>
        <span class="frm_error_msg" id="text_newPassword_details_msg"></span>
    </div>
    <div class="frm_div">
        <label class="frm_label text_info" id="lbl_admin_confirmPassword" for="input_admin_confirmPassword">确认密码</label>
        <input class="frm_input" id="input_admin_confirmPassword" type="password" maxlength="50"/>
        <span class="frm_error_msg" id="text_confirmPassword_details_msg"></span>
    </div>
</div>
<div class="details_tools_div">
    <input class="frm_btn" id="btn_admin_save" type="button" value="保存"/>
</div>

<%-- 模态框 --%>
<div class="modal fade" id="modalDiv" tabindex="-1" role="dialog" aria-labelledby="modalDiv" aria-hidden="true"
     data-backdrop="static">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <h4 class="modal-title" id="myModalLabel">提示</h4>
            </div>
            <div class="modal-body">保存成功</div>
            <div class="modal-footer">
                <button type="submit" class="btn btn-primary" id="btn-ok">确定</button>
                <button type="button" class="btn btn-default" data-dismiss="modal" id="btn-close">关闭</button>
            </div>
        </div>
        <%-- /.modal-content %--%>
    </div>
    <%-- /.modal %--%>
</div>
<div class="loader"></div>
</body>
</html>

源码下载

(CSDN 1积分下载):https://download.csdn.net/download/caofeng891102/88073012
(免费领取加锋哥V:java9266)

热门推荐

免费分享一套 SpringBoot + Vue的排课/选课管理系统,挺漂亮的_csdn_java1234_小锋的博客-CSDN博客

免费分享一套 SpringBoot + Vue的校园社团管理系统,挺漂亮的_java1234_小锋的博客-CSDN博客

我写了一套SpringBoot+SpringSecurity+Vue权限系统 实战课程,免费分享给CSDN的朋友们_springboot+spring security+jwt+vue3+redis_java1234_小锋的博客-CSDN博客

我写了一套SpringBoot微信小程序电商全栈就业实战课程,免费分享给CSDN的朋友们_java1234微信小程序电商_java1234_小锋的博客-CSDN博客

springboot+vue前后端音乐网系统,挺漂亮的_具有后端服务的播放系统,_java1234_小锋的博客-CSDN博客

免费分享一个springboot+vue校园宿舍管理系统,挺漂亮的_logincontroller(redisutil redisutil, systemuserser_java1234_小锋的博客-CSDN博客

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

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

相关文章

编码器原理

编码器原理 编码器是一种用来测量机械旋转或位移的传感器。这种传感器能够测量机械部件在旋转或直线运动时的位移位置或速度等信息,并将其转换成一系列电信号。 光栅式旋转编码器 霍尔式编码器

制作一个简易的计算器app

1、Ui开发 笔者的Ui制作的制作的比较麻烦仅供参考&#xff0c;在这里使用了多个LinearLayout对屏幕进行了划分。不建议大家这样做最好使用GridLayout会更加快捷简单 笔者大致划分是这样的&#xff1a; 使用了四个大框&#xff0c;在第四个大框里面有多个小框 最终界面如下&am…

Linux(一)

一.FinalShell远程连接Linux系统&#xff08;可能在自己电脑虚拟机上也可能在服务器上&#xff09; 二.掌握使用WSL获得Ubuntu系统环境 WSL作为Windows10系统带来的全新特性。 传统方式获取Linux操作系统环境&#xff0c;是安装完整的虚拟机&#xff0c;如VMware 使用WSL&#…

系统架构设计师 10:软件架构的演化和维护

一、软件架构演化 如果软件架构的定义是 SA{components, connectors, constraints}&#xff0c;也就是说&#xff0c;软件架构包括组件、连接件和约束三大要素&#xff0c;这类软件架构演化主要关注的就是组件、连接件和约束的添加、修改与删除等。 二、面向对象软件架构演化…

点云分割-pcl区域生长算法

目录 写在前面原理代码运行结果 参考完 写在前面 1、本文内容 pcl的区域生长算法的使用和原理 2、平台/环境 cmake, pcl 3、转载请注明出处&#xff1a; https://blog.csdn.net/qq_41102371/article/details/131927376 原理 参考&#xff1a;https://pcl.readthedocs.io/pr…

从风控系统看架构设计原型图分析

目录 一、对架构与架构图的理解 &#xff08;一&#xff09;架构的本质 &#xff08;二&#xff09;软件设计中架构域的划分 &#xff08;三&#xff09;架构图设计 架构图设计的必要性 如何画架构图 二、实践业务架构与产品架构设计 &#xff08;一&#xff09;列出问…

基于SpringBoot+vue的学生成绩管理系统设计与实现(源码+LW+部署文档等)

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架…

重学C++系列之虚继承

一、什么是虚继承 虚继承是C中一种继承的机制。继承有普通继承和虚继承两种机制&#xff0c;默认是普通继承&#xff0c;如果要使用多继承&#xff0c;需要在继承方式里加上关键字virtual。 二、为什么要虚继承 在C中&#xff0c;为解决二义性问题&#xff0c;引入虚继承机制。…

tinkerCAD案例:9. Saw Shaped Wrench 锯形扳手

tinkerCAD案例&#xff1a;9. Saw Shaped Wrench 锯形扳手 ln this lesson you will learn how to create a cool saw shaped wrench. 在本课中&#xff0c;您将学习如何制作一个很酷的锯形扳手。 Start the lesson by dragging a polygon to the workplane. 通过将多边形拖动…

从模型坐标到屏幕坐标

在 3D 引擎中&#xff0c;场景通常被描述为三维空间中的模型或对象&#xff0c;每个模型对象由许多三维顶点组成。最终&#xff0c;这些模型对象将在平面屏幕上呈现和显示。 渲染场景始终相对于摄像机&#xff0c;因此&#xff0c;还必须相对于摄像机的视图定义场景的顶点。了…

zeppelin spark kerberos 使用过程遇到的问题

参考配置教程: Configure Zeppelin for a Kerberos-Enabled Cluster zeppelin spark kerberos 使用过程遇到的问题 ambari创建zeppelin时,会创建Kerberos account and keytab /etc/security/keytabs/zeppelin.server.kerberos.keytab interpreter配置 keytab Interpreter Ke…

新来个技术总监,禁止我们用Git的rebase!?

△Hollis, 一个对Coding有着独特追求的人△ 这是Hollis的第 429 篇原创分享 作者 l Hollis 来源 l Hollis&#xff08;ID&#xff1a;hollischuang&#xff09; 在Git中&#xff0c;merge和rebase是两种不同的代码合并策略&#xff0c;它们用于将一个分支的更改合并到另一个分支…

动态内存常见的问题

对空指针的解引用 改正后的代码&#xff1a; 返回栈&#xff08;临时变量&#xff09;空间地址的问题 释放空间后及时把指针设为空 void Test(void) {char* str (char*)malloc(100);strcpy(str, "hello");free(str);str NULL;//释放空间后及时把指针设为空if (s…

嵌入式学习入门和求职经验

入门阶段&#xff1a;&#xff08;不要只看书&#xff0c;要多动手&#xff0c;但千万不是直接动手&#xff0c;不去看书&#xff09; C语言&#xff1a;嵌入式编程大多用C语言、少量汇编&#xff0c;先学习C语言&#xff0c;汇编用到的时候再上网查询。教材&#xff1a;随便一…

unity调用c++dll时变量处理问题

在C中为了处理方便可以将有些局部变量变为全局变量。 以下面三个变量为例 如果还有新全局变量需要这其来赋值&#xff0c;就会有问题。 如下面的body1_ptr为了dll生成处理方便&#xff0c;变为全局变量后&#xff0c;这个指针变赋值就会有问题。 因为geometry_path这个只在调…

上分秘籍 :如何用大模型跨模态能力核查网络谣言?

也许&#xff0c;你也曾听说过&#xff1a;&#x1f449;眼见未必为实&#x1f449;假新闻可比真新闻好传太多啦&#xff01;&#x1f449;谣言一张嘴&#xff0c;辟谣跑断腿大模型时代来临&#xff0c;除了肝&#xff0c;对付网络谣言&#xff0c;是不是有了新出路&#xff1f…

Java_23_并发包

并发包 并发包的来历&#xff1a; 在实际开发中如果不需要考虑线程安全问题&#xff0c;大家不需要做线程安全&#xff0c;因为如果做了反而性能不好&#xff01; 但是开发中有很多业务是需要考虑线程安全问题的&#xff0c;此时就必须考虑了。否则业务出现问题。 Java为很多业…

考公笔记题

一、考公报名网址 国家公务员局&#xff1a;国家公务员局 二、历年题库 华图在线&#xff1a;国省考公务员题库_公职教育类在线题库-华图在线 公务员&#xff1a;《行测》与《申论》 重点学习&#xff1a; 判断推理&#xff08;图形推理、定义判断&#xff08;影响不大&am…

vue中的数据代理

vue数据代理 Vue实现数据代理的核心----Object.defineProperty(); 数据代理 数据代理的定义是&#xff1a;一个对象操作(读\写)另一个对象中的属性和方法。 // 数据代理&#xff1a;通过一个对象代理对另一个对象中属性的操作&#xff08;读/写&#xff09;let obj { x: 100…

STM32之智能小车,手把手从0到1,模块化编程

小车介绍 本博文将会从0到1实现一个智能小车&#xff0c;该小车实现功能&#xff1a;1. 摇头避障模式、2. 跟随模式、3. 循迹模式、4. 小车测速并显示在OLED屏幕、5. 语音控制小车等等。 硬件组成 STM32F103开发板、小车套件、L9110S电机模块、超声波模块&#xff08;HC-SR04&a…