wuyouming666
2023-08-29 fd19536cbf9e5acec9bf7270f3f46037e822827d
添加菜单折叠自适应宽度 修改密码  修改用户角色 中英文切换全局配置
18个文件已修改
11个文件已添加
4个文件已删除
1314 ■■■■■ 已修改文件
CanadaMes-back/canadames.iml 141 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
CanadaMes-back/pom.xml 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
CanadaMes-back/src/main/java/MyGenerator.java 176 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
CanadaMes-back/src/main/java/com/canadames/controller/UserController.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
CanadaMes-back/src/main/java/com/canadames/controller/device/DeviceController.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
CanadaMes-back/src/main/java/com/canadames/dao/device/DeviceDao.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
CanadaMes-back/src/main/java/com/canadames/entity/device/DeviceEntity.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
CanadaMes-back/src/main/java/com/canadames/entity/vo/deviceVo.java 补丁 | 查看 | 原始文档 | blame | 历史
CanadaMes-back/src/main/java/com/canadames/service/device/DeviceService.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
CanadaMes-back/src/main/java/com/canadames/service/device/impl/DeviceServiceImpl.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
CanadaMes-back/src/main/java/com/canadames/service/impl/UserServiceImpl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
CanadaMes-back/src/main/resources/com/canadames/dao/DeviceDao.xml 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
CanadaMes-ui/README.md 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
CanadaMes-ui/package-lock.json 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
CanadaMes-ui/package.json 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
CanadaMes-ui/public/2.ico 补丁 | 查看 | 原始文档 | blame | 历史
CanadaMes-ui/public/favicon.ico 补丁 | 查看 | 原始文档 | blame | 历史
CanadaMes-ui/public/index.html 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
CanadaMes-ui/src/App.vue 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
CanadaMes-ui/src/api/role.js 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
CanadaMes-ui/src/api/user.js 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
CanadaMes-ui/src/assets/2.ico 补丁 | 查看 | 原始文档 | blame | 历史
CanadaMes-ui/src/lang/locales/en-US.json 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
CanadaMes-ui/src/lang/locales/zh-CN.json 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
CanadaMes-ui/src/layout/index.vue 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
CanadaMes-ui/src/layout/tag.vue 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
CanadaMes-ui/src/main.js 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
CanadaMes-ui/src/router/index.js 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
CanadaMes-ui/src/store/store,js 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
CanadaMes-ui/src/views/device/alarm.vue 291 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
CanadaMes-ui/src/views/home/index.vue 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
CanadaMes-ui/src/views/user/index.vue 48 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
CanadaMes-ui/vue.config.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
CanadaMes-back/canadames.iml
File was deleted
CanadaMes-back/pom.xml
@@ -36,6 +36,7 @@
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
@@ -78,13 +79,27 @@
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring-boot-starter</artifactId>
            <version>1.5.3</version>
            <version>1.8.0</version>
        </dependency>
        <!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.2.0</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-core</artifactId>
            <version>3.2.0</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.2.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>
        <!-- swagger -->
        <dependency>
@@ -98,6 +113,8 @@
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>
    </dependencies>
    <build>
CanadaMes-back/src/main/java/MyGenerator.java
New file
@@ -0,0 +1,176 @@
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.util.ArrayList;
import java.util.List;
/**
 * mybatis-plus-generator代码生成器
 * 修改配置后直接运行main方法即可
 */
public class MyGenerator {
    /** 数据源配置*/
    private static final String jdbc = "jdbc:mysql://localhost:3306/canadames?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai";
    private static final String driverName = "com.mysql.cj.jdbc.Driver";
    private static final String username = "root";
    private static final String password = "beibo.123/";
    /** 包名*/
    private static final String moduleName = "device";
    /** 表名前缀*/
    private static final String beginName = "device";
    /** 需要生成代码的表*/
    private static final String [] tables = new String[]{"device"};
    public static void main(String[] args) {
        // 官方网站:https://baomidou.com/pages/d357af/#%E8%87%AA%E5%AE%9A%E4%B9%89%E5%B1%9E%E6%80%A7%E6%B3%A8%E5%85%A5
        // 参考网站:https://blog.csdn.net/kinghmj01/article/details/97748509
        // 代码生成器
        AutoGenerator mpg = new AutoGenerator();
        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        // 当前项目路径
        String projectPath = System.getProperty("user.dir");
        // 当前项目的下的路径
        gc.setOutputDir(projectPath + "/src/main/java");
        // 作者
        gc.setAuthor("yyq");
        // 是否打开输出目录 默认为true
        gc.setOpen(false);
        // 实体属性 Swagger2 注解
        gc.setSwagger2(true);
        // 自定义文件命名,注意 %s 会自动填充表实体属性!
        gc.setControllerName("%sController");
        gc.setServiceName("%sService");
        gc.setServiceImplName("%sServiceImpl");
        gc.setMapperName("%sDao");
        gc.setEntityName("%sEntity");
        mpg.setGlobalConfig(gc);
        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        // dsc.setSchemaName("public");
        dsc.setUrl(jdbc);
        dsc.setDriverName(driverName);
        dsc.setUsername(username);
        dsc.setPassword(password);
        mpg.setDataSource(dsc);
        // 设置包名
        PackageConfig pc = new PackageConfig();
        // 用于包名、表名前缀
        //pc.setModuleName(moduleName);
        // 生成到那些包下 如 com.modules主包下的 controller.sys.TestController
        pc.setParent("com.canadames");
        pc.setController("controller."+moduleName);
        pc.setService("service."+moduleName);
        pc.setServiceImpl("service."+moduleName+".impl");
        pc.setMapper("dao."+moduleName);
        pc.setEntity("entity."+moduleName);
        mpg.setPackageInfo(pc);
        // 自定义配置
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                // to do nothing
            }
        };
        // 如果模板引擎是 freemarker
        String templatePath = "/templates/mapper.xml.ftl";
        // 如果模板引擎是 velocity
        // String templatePath = "/templates/mapper.xml.vm";
        // 自定义输出配置
        List<FileOutConfig> focList = new ArrayList<>();
        // 自定义配置会被优先输出
        focList.add(new FileOutConfig(templatePath) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
                return projectPath + "/src/main/resources/mapper/" + moduleName + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
            }
        });
        /*
        cfg.setFileCreate(new IFileCreate() {
            @Override
            public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) {
                // 判断自定义文件夹是否需要创建
                checkDir("调用默认方法创建的目录,自定义目录用");
                if (fileType == FileType.MAPPER) {
                    // 已经生成 mapper 文件判断存在,不想重新生成返回 false
                    return !new File(filePath).exists();
                }
                // 允许生成模板文件
                return true;
            }
        });
        */
        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);
        // 配置模板
        TemplateConfig templateConfig = new TemplateConfig();
        // 配置自定义输出模板
        //指定自定义模板路径,注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别
        // templateConfig.setEntity("templates/entity2.java");
        // templateConfig.setService();
        // templateConfig.setController();
        templateConfig.setXml(null);
        mpg.setTemplate(templateConfig);
        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        //需要生成的表
        strategy.setInclude(tables);
        strategy.setControllerMappingHyphenStyle(true);
        //类名生成策略:驼峰命名
        strategy.setNaming(NamingStrategy.underline_to_camel);
        //字段名生成方式:驼峰命名
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        // 表前缀
        strategy.setTablePrefix(beginName+"_");
        // 写于父类中的公共字段(在父类中已经有的不需要生成的字段)
        strategy.setSuperEntityColumns("id", "updateTime", "createTime");
        // 每层的继承(不需要可不设置)
//        strategy.setSuperControllerClass("com.common.controller.MyController");
        strategy.setSuperServiceClass("com.baomidou.mybatisplus.extension.service.IService");
        strategy.setSuperServiceImplClass("com.baomidou.mybatisplus.extension.service.impl.ServiceImpl");
        strategy.setSuperMapperClass("com.baomidou.mybatisplus.core.mapper.BaseMapper");
        //继承的属性父类
//        strategy.setSuperEntityClass("com.common.entity.MyEntity");
        strategy.setEntityLombokModel(true);
        strategy.setRestControllerStyle(true);
        mpg.setStrategy(strategy);
        mpg.setTemplateEngine(new FreemarkerTemplateEngine());
        mpg.execute();
    }
}
CanadaMes-back/src/main/java/com/canadames/controller/UserController.java
@@ -11,6 +11,7 @@
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.crypto.hash.SimpleHash;
@@ -22,6 +23,8 @@
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@RestController
@Slf4j
@RequestMapping("/api/user")
@@ -29,6 +32,46 @@
public class UserController {
    @Autowired
    private UserService userService;
    @ApiOperation(value = "修改密码")
    @PostMapping("/changePassword")
    @RequiresAuthentication
    public Result changePassword(@RequestBody Map<String, String> request) {
        User currentUser = SecurityUtil.getCurrentUser();
        String oldPassword = request.get("oldPassword");
        String newPassword = request.get("newPassword");
        // 校验旧密码是否正确
        if (!verifyPassword(currentUser, oldPassword)) {
            return Result.fail("旧密码不正确");
        }
        // 更新密码
        updatePassword(currentUser, newPassword);
        return Result.success("密码修改成功");
    }
    /**
     * 验证密码是否正确
     */
    private boolean verifyPassword(User user, String password) {
        Object salt = ByteSource.Util.bytes(SystemConstant.JWT_SECRET_KEY);
        String md5 = new SimpleHash("MD5", password, salt, 1024).toHex();
        return md5.equals(user.getPassword());
    }
    /**
     * 更新密码
     */
    private void updatePassword(User user, String newPassword) {
        Object salt = ByteSource.Util.bytes(SystemConstant.JWT_SECRET_KEY);
        String md5 = new SimpleHash("MD5", newPassword, salt, 1024).toHex();
        user.setPassword(md5);
        userService.saveOrUpdate(user);
    }
    @ApiOperation(value = "分页查询用户")
    @GetMapping("/selectPage")
@@ -38,7 +81,7 @@
        return Result.success(userService.selectPage(userVO));
    }
    @ApiOperation(value = "添加或或者修改用户")
    @ApiOperation(value = "添加或修改用户")
    @PostMapping("/saveOrUpdate")
    @RequiresRoles({"admin"})
    @RequiresPermissions({"user:update", "user:add"})
@@ -46,14 +89,27 @@
        if ("admin".equals(user.getUsername())) {
            return Result.fail("管理员不可以被禁用");
        }
        Integer count = userService.lambdaQuery().eq(User::getUsername, user.getUsername())
        Integer count = userService.lambdaQuery()
                .eq(User::getUsername, user.getUsername())
                .ne(user.getId() != null, User::getId, user.getId())
                .count();
        if (count > 0) return Result.fail("用户名已存在");
        // 通过shiro默认的加密工具类为注册用户的密码进行加密
        Object salt = ByteSource.Util.bytes(SystemConstant.JWT_SECRET_KEY);
        String md5 = new SimpleHash("MD5", user.getPassword(), salt, 1024).toHex();
        user.setPassword(md5);
        if (count > 0) {
            return Result.fail("用户名已存在");
        }
        // 如果密码未修改,则不进行加密操作
        if (user.getId() != null) {
            User existingUser = userService.getById(user.getId());
            if (existingUser != null && existingUser.getPassword().equals(user.getPassword())) {
                user.setPassword(existingUser.getPassword());
            } else {
                // 密码发生了变化,进行加密操作
                Object salt = ByteSource.Util.bytes(SystemConstant.JWT_SECRET_KEY);
                String md5 = new SimpleHash("MD5", user.getPassword(), salt, 1024).toHex();
                user.setPassword(md5);
            }
        }
        userService.saveOrUpdate(user);
        return Result.success();
    }
@@ -86,4 +142,7 @@
        SecurityUtils.getSubject().logout();
        return Result.success("注销成功");
    }
    private class UpdatePasswordRequest {
    }
}
CanadaMes-back/src/main/java/com/canadames/controller/device/DeviceController.java
New file
@@ -0,0 +1,43 @@
package com.canadames.controller.device;
import com.canadames.entity.device.DeviceEntity;
import com.canadames.entity.vo.Result;
import com.canadames.service.device.DeviceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/device") // 修改请求路径为 "/api/device"
public class DeviceController {
    private final DeviceService deviceService;
    @Autowired
    public DeviceController(DeviceService deviceService) {
        this.deviceService = deviceService;
    }
    @GetMapping("/getAllDevices")  // 修改接口路径为 "/getAllDevices"
    public Result getAllDevices() {
        List<DeviceEntity> devices = deviceService.getAllDevices();
        return Result.success(devices);
    }
    @PostMapping("/updateDeviceName")  // 修改接口路径为 "/updateDeviceName"
    public Result updateDeviceName(@RequestBody DeviceEntity device) {
        deviceService.updateDeviceName(device);
        return Result.success();
    }
    @PostMapping("/call-stored-proc")
    public Result  callStoredProc(@RequestBody DeviceEntity device) {
        List<DeviceEntity> result = deviceService.callStoredProc(device); // 获取多行查询结果
        return Result.success(result); // 返回包含多行结果的 Result 对象
    }
}
CanadaMes-back/src/main/java/com/canadames/dao/device/DeviceDao.java
New file
@@ -0,0 +1,19 @@
package com.canadames.dao.device;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.canadames.entity.User;
import com.canadames.entity.device.DeviceEntity;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import java.util.List;
@Mapper
@Repository
public interface DeviceDao extends BaseMapper<User> {
    List<DeviceEntity> selectAllDevices();
    void updateDeviceName(DeviceEntity device);
    List<DeviceEntity> callStoredProc(DeviceEntity device);
}
CanadaMes-back/src/main/java/com/canadames/entity/device/DeviceEntity.java
New file
@@ -0,0 +1,61 @@
package com.canadames.entity.device;
import com.baomidou.mybatisplus.annotation.TableName;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
 * <p>
 *
 * </p>
 *
 * @author yyq
 * @since 2023-08-28
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("device")
@ApiModel(value="DeviceEntity对象", description="")
public class DeviceEntity implements Serializable {
    private static final long serialVersionUID = 1L;
    private String deviceId;
    private String createUser;
    private String msg;
    private LocalDateTime createTime;
    private long id;
    private String deviceName;
    private String name;
    private String status;
    private String info;
    private String model;
    private LocalDateTime countTimeFlag;
    private String address;
    private String gps;
    private LocalDate debugTime;
    private LocalDate endTime;
    private String customerName;
    private String customerDeviceName;
    private LocalDate lastProductCountDate;
}
CanadaMes-back/src/main/java/com/canadames/entity/vo/deviceVo.java
CanadaMes-back/src/main/java/com/canadames/service/device/DeviceService.java
New file
@@ -0,0 +1,15 @@
package com.canadames.service.device;
import com.canadames.entity.device.DeviceEntity;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public interface DeviceService {
    List<DeviceEntity> getAllDevices();
    void updateDeviceName(DeviceEntity device);
    List<DeviceEntity> callStoredProc(DeviceEntity device); // 修改返回类型为 List<DeviceEntity>
}
CanadaMes-back/src/main/java/com/canadames/service/device/impl/DeviceServiceImpl.java
New file
@@ -0,0 +1,45 @@
package com.canadames.service.device.impl;
import com.canadames.dao.device.DeviceDao;
import com.canadames.entity.device.DeviceEntity;
import com.canadames.service.device.DeviceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public  class DeviceServiceImpl implements DeviceService {
    private final DeviceDao deviceDao;
    @Autowired
    public DeviceServiceImpl(DeviceDao deviceDao) {
        this.deviceDao = deviceDao;
    }
    @Override
    public List<DeviceEntity> getAllDevices() {
        return deviceDao.selectAllDevices();
    }
    @Override
    public void updateDeviceName(DeviceEntity device) {
        deviceDao.updateDeviceName(device);
    }
    @Override
    public List<DeviceEntity> callStoredProc(DeviceEntity device) {
        return performStoredProcQuery(device);
    }
    private List<DeviceEntity> performStoredProcQuery(DeviceEntity device) {
        // 执行存储过程的查询操作,并返回结果列表
        List<DeviceEntity> resultList = new ArrayList<>();
        return deviceDao.callStoredProc(device);
    }
}
CanadaMes-back/src/main/java/com/canadames/service/impl/UserServiceImpl.java
@@ -50,7 +50,7 @@
    public List<Long> selectChild(Long id, Boolean bool) {
        User user = getById(id);
        List<User> users = userDao.selectChild(user.getPath() + user.getId() + "-");
        System.out.println(users);
        List<Long> ids = users.stream().map(User::getId).collect(Collectors.toList());
        if (bool) ids.add(id);
CanadaMes-back/src/main/resources/com/canadames/dao/DeviceDao.xml
New file
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.canadames.dao.device.DeviceDao">
    <resultMap id="myResultMap" type="com.canadames.entity.device.DeviceEntity">
        <!-- 根据你的实际情况定义结果映射 -->
        <!-- 示例中将结果映射到实体类的属性 -->
        <id property="id" column="id"/>
        <result property="name" column="device_name"/>
        <!-- 其他属性映射 -->
    </resultMap>
    <select id="selectAllDevices" resultMap="myResultMap">
        SELECT *
        FROM device
    </select>
    <select id="callStoredProc" statementType="CALLABLE" resultMap="myResultMap">
        { call Your_Stored_Procedure(
                #{id, mode=IN, jdbcType=VARCHAR},
                #{name, mode=IN, jdbcType=VARCHAR}
            ) }
    </select>
    <update id="updateDeviceName" parameterType="com.canadames.entity.device.DeviceEntity">
        UPDATE device
        SET device_name = #{name}
        WHERE id = #{id}
    </update>
</mapper>
CanadaMes-ui/README.md
File was deleted
CanadaMes-ui/package-lock.json
@@ -1,11 +1,11 @@
{
  "name": "authority-ui",
  "name": "canadames-ui",
  "version": "0.1.0",
  "lockfileVersion": 2,
  "requires": true,
  "packages": {
    "": {
      "name": "authority-ui",
      "name": "canadames-ui",
      "version": "0.1.0",
      "dependencies": {
        "axios": "^0.19.2",
CanadaMes-ui/package.json
@@ -1,5 +1,5 @@
{
  "name": "authority-ui",
  "name": "canadames-ui",
  "version": "0.1.0",
  "private": true,
  "scripts": {
@@ -14,9 +14,12 @@
    "js-cookie": "^3.0.1",
    "less": "^3.12.2",
    "less-loader": "^6.2.0",
    "node-sass": "^9.0.0",
    "vue": "^2.6.11",
    "vue-i18n": "^8.26.5",
    "vue-router": "^3.3.4"
    "vue-router": "^3.3.4",
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "~4.5.0",
CanadaMes-ui/public/2.ico
CanadaMes-ui/public/favicon.ico
Binary files differ
CanadaMes-ui/public/index.html
@@ -4,7 +4,7 @@
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <link rel="icon" href="<%= BASE_URL %>2.ico">
    <title>北玻mes系统</title>
  </head>
  <body>
CanadaMes-ui/src/App.vue
@@ -2,6 +2,9 @@
    <div id="app">
        <!--路由占位符-->
        <router-view/>
        <div class="custom-icon">
  </div>
    </div>
</template>
<script>
@@ -10,4 +13,6 @@
    }
</script>
<style>
</style>
CanadaMes-ui/src/api/role.js
@@ -13,6 +13,8 @@
    })
}
/**
 * 根据id删除角色
 * @param data
CanadaMes-ui/src/api/user.js
@@ -25,7 +25,40 @@
        data
    })
}
export function test(data) {
    return request({
        url: '/api/device/getAllDevices',
        method: 'get',
        data
    })
}
export function testup(data) {
    return request({
        url: '/api/device/updateDeviceName',
        method: 'post',
        data
    })
}
export function call(data) {
    return request({
        url: '/api/device/call-stored-proc',
        method: 'post',
        data
    })
}
/**
 * 注册时校验用户名是否存在
 * @param data
 * @returns {AxiosPromise}
 */
export function changePassword(data) {
    return request({
        url: '/api/user/changePassword',
        method: 'post',
        data
    })
}
/**
 * 注册时校验用户名是否存在
 * @param data
CanadaMes-ui/src/assets/2.ico
CanadaMes-ui/src/lang/locales/en-US.json
@@ -34,5 +34,6 @@
    "langConfirm": "Confirm",
    "langEditUserTitle": "Edit User",
    "northglassMESsystem":"NorthGlassMesSystem",
    "exit":"exit"
    "exit":"exit",
    "changePassword":"changePassword"
  }
CanadaMes-ui/src/lang/locales/zh-CN.json
@@ -35,6 +35,7 @@
  "langConfirm": "确定",
  "langEditUserTitle": "修改用户",
  "northglassMESsystem":"北玻MES系统",
  "exit":"退出"
  "exit":"退出",
  "changePassword":"修改密码"
  }
  
CanadaMes-ui/src/layout/index.vue
@@ -43,7 +43,10 @@
    <el-container>
     
      <el-header class="header-container">
        <!-- <div class="tagContainer">
               <tag></tag>
        </div> -->
        <el-menu :default-active="activePath1" class="el-menu-demo" mode="horizontal"
                 background-color="#576574" text-color="#fff" active-text-color="#ffd04b">
                 <div class="header-left">
@@ -56,8 +59,27 @@
              </el-avatar>
            </template>
            <el-menu-item index="1-1" @click="logout">{{ $t('exit') }}</el-menu-item>
            <el-menu-item index="1-2" @click="showChangePasswordDialog">{{ $t('changePassword') }}</el-menu-item>
          </el-submenu>
          <template>
  <el-dialog title="修改密码" :visible.sync="dialogVisible" width="30%">
    <el-form ref="form" :model="formData" label-width="100px">
      <el-form-item label="旧密码">
        <el-input v-model="formData.oldPassword" type="password" show-password></el-input>
      </el-form-item>
      <el-form-item label="新密码">
        <el-input v-model="formData.newPassword" type="password" show-password></el-input>
      </el-form-item>
      <el-form-item label="确认新密码">
        <el-input v-model="formData.confirmPassword" type="password" show-password></el-input>
      </el-form-item>
    </el-form>
    <div slot="footer" class="dialog-footer">
      <el-button @click="dialogVisible = false">取消</el-button>
      <el-button type="primary" @click="savePassword">保存</el-button>
    </div>
  </el-dialog>
</template>
        </el-menu>
      </el-header>
      <el-main>
@@ -67,16 +89,26 @@
  </el-container>
</template>
<script>
import {selectList} from '../api/menuList'
import {changePassword} from '../api/user'
import {removeToken} from "../utils/auth";
import LanguageMixin from '../lang/LanguageMixin'
// import Tag from '../layout/tag.vue';
export default {
  name: "Layout",
  mixins: [LanguageMixin],
  data() {
    return {
      dialogVisible: false,
      formData: {
        oldPassword: '',
        newPassword: '',
        confirmPassword: ''
      },
      
      menuList: [],
      isCollapse: false,
@@ -88,6 +120,9 @@
      }
    }
  },
  // components: {
  //   Tag
  // },
  created() {
    selectList(this.menuListForm).then(res => {
      this.menuList = res.data
@@ -105,6 +140,35 @@
      removeToken()
      this.$router.push('/login')
    },
    showChangePasswordDialog() {
      this.dialogVisible = true;
    },
    savePassword() {
      const { oldPassword, newPassword, confirmPassword } = this.formData;
      if (newPassword !== confirmPassword) {
        this.$message.error('新密码与确认密码不一致');
        return;
      }
      changePassword({oldPassword, newPassword}).then(res => {
        console.log(res.data);
        this.$message.success('密码修改成功');
        removeToken()
      this.$router.push('/login')
      }).catch(() => {
      });
      this.dialogVisible = false;
    },
    
    toggleCollapse: function () {
      this.isCollapse = !this.isCollapse;
@@ -138,10 +202,10 @@
      
    }
  }
  }
  
}
</script>
<style scoped lang="less">
CanadaMes-ui/src/layout/tag.vue
New file
@@ -0,0 +1,91 @@
<template>
    <div class="_tag">
      <el-scrollbar style="margin-right: 6px;">
        <div class="left">
          <el-tag
            v-for="tag in tagsList"
            :key="tag.title"
            :closable="!tag.hideclose"
            :type="isActive(tag)"
            @close="removeTag(tag)"
            @click="handleTagClick(tag)"
          >
            <router-link :to="tag.path" class="tag-title">{{ tag.title }}</router-link>
          </el-tag>
        </div>
      </el-scrollbar>
      <el-dropdown @command="handleCloseBtn" class="_dropdown">
        <el-button type="primary" size="small">
          标签选项
          <i class="el-icon-arrow-down el-icon--right"></i>
        </el-button>
        <el-dropdown-menu slot="dropdown">
          <el-dropdown-item command="closeOther">关闭其它</el-dropdown-item>
          <el-dropdown-item command="closeAll">关闭所有</el-dropdown-item>
        </el-dropdown-menu>
      </el-dropdown>
    </div>
  </template>
  <script>
  import { mapState, mapMutations } from "vuex";
  export default {
    computed: {
      ...mapState("tags", ["tagsList"]),
      activeTag() {
        return this.tagsList.find(tag => tag.path === this.$route.fullPath);
      }
    },
    methods: {
      ...mapMutations("tags", ["removeTag"]),
      handleCloseBtn(command) {
        if (command === "closeOther") {
          const activeTag = this.activeTag;
          if (!activeTag || activeTag.hideclose) return;
          this.$store.commit("tags/removeOtherTags", activeTag);
        } else if (command === "closeAll") {
          this.$store.commit("tags/removeAllTags");
          this.$router.push({ name: "sadmasMain" });
        }
      },
      isActive(tag) {
        return tag.path === this.$route.fullPath ? "" : "info";
      },
      handleTagClick(tag) {
        this.$router.push(tag.path);
      }
    },
    watch: {
      $route(newValue) {
        const isExist = this.tagsList.some(item => item.path === newValue.fullPath);
        if (!isExist) {
          this.$store.commit("tags/addTag", {
            title: newValue.meta.title || "",
            path: newValue.fullPath,
            hideclose: newValue.meta.hideclose || false
          });
        }
      }
    }
  };
  </script>
  <style scoped>
  ._tag {
    display: flex;
    align-items: center;
    padding: 10px;
  }
  .left {
    display: flex;
    flex-wrap: wrap;
  }
  .tag-title {
    cursor: pointer;
  }
  </style>
CanadaMes-ui/src/main.js
@@ -9,6 +9,7 @@
import zhCN from './lang/locales/zh-CN.json' // 中文语言包
import enUS from './lang/locales/en-US.json' // 英文语言包
// import store from './store'
Vue.use(VueI18n)
const i18n = new VueI18n({
@@ -19,6 +20,8 @@
  }
})
const EventBus = new Vue();
Vue.prototype.$bus = EventBus;
Vue.use(ElementUI);
Vue.config.productionTip = false;
@@ -27,9 +30,10 @@
Vue.prototype.$message = Message;
// confirm
Vue.prototype.$confirm = MessageBox.confirm;
Vue.prototype.$tagList = [];
new Vue({
    router,
    i18n,
    render: h => h(App)
}).$mount('#app');
CanadaMes-ui/src/router/index.js
@@ -96,6 +96,18 @@
  // 获取token
  const hasToken = getToken();
  const isExist = Vue.prototype.$tagList.some(tag => tag.path === to.fullPath);
  if (!isExist) {
    Vue.prototype.$tagList.push({
      path: to.fullPath,
      data: null
    });
  }
  // 没有token则跳转到登录页面
  if (!hasToken) return next('/login');
CanadaMes-ui/src/store/store,js
New file
@@ -0,0 +1,77 @@
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
  state: {
    // 其他模块的状态...
  },
  mutations: {
    // 其他模块的mutations...
  },
  actions: {
    // 其他模块的actions...
  },
  modules: {
    tag: {
      namespaced: true,
      state: {
        tags: [], // 标签列表
        selectedTag: null // 选中的标签
      },
      mutations: {
        SET_TAGS(state, tags) {
          state.tags = tags;
        },
        ADD_TAG(state, tag) {
          state.tags.push(tag);
        },
        REMOVE_TAG(state, tag) {
          state.tags = state.tags.filter(t => t !== tag);
        },
        CLEAR_TAGS(state) {
          state.tags = [];
        },
        SET_SELECTED_TAG(state, tag) {
          state.selectedTag = tag;
        },
        CLEAR_SELECTED_TAG(state) {
          state.selectedTag = null;
        }
      },
      actions: {
        setTags({ commit }, tags) {
          commit('SET_TAGS', tags);
        },
        addTag({ commit }, tag) {
          commit('ADD_TAG', tag);
        },
        removeTag({ commit }, tag) {
          commit('REMOVE_TAG', tag);
        },
        clearTags({ commit }) {
          commit('CLEAR_TAGS');
        },
        setSelectedTag({ commit }, tag) {
          commit('SET_SELECTED_TAG', tag);
        },
        clearSelectedTag({ commit }) {
          commit('CLEAR_SELECTED_TAG');
        }
      },
      getters: {
        getTags(state) {
          return state.tags;
        },
        getSelectedTag(state) {
          return state.selectedTag;
        }
      }
    }
  }
});
export default store;
CanadaMes-ui/src/views/device/alarm.vue
@@ -10,271 +10,32 @@
      <router-link to="/device/parameter" tag="el-button" type="text">参数下发</router-link>
      <router-link to="/device/control" tag="el-button" type="text">开关控制</router-link>
    </el-breadcrumb>
    <div>报警信息</div>
    </div>
  </template>
  <script>
    <!--卡片视图区域-->
    <el-card class="el-card">
      <el-row :gutter="20">
        <!--搜索与添加区域-->
        <el-col :span="6">
          <el-input v-model="queryInfo.username" clearable @clear="getUserList">
          </el-input>
        </el-col>
        <el-col :span="6">
          <el-input v-model="queryInfo.email" clearable @clear="getUserList">
          </el-input>
        </el-col>
        <!--搜索按钮-->
        <el-col :span="4">
          <el-button type="primary" @click="getUserList">{{ $t('langSearch') }}</el-button>
        </el-col>
        <!--添加区域-->
        <el-col :span="4">
          <el-button type="primary" @click="showAddDialog">{{ $t('langAddUser') }}</el-button>
        </el-col>
      </el-row>
      <!--用户列表区域-->
      <el-table :data="userList.records" border stripe>
        <el-table-column label="#" type="index"></el-table-column>
        <el-table-column :label="$t('langUsername')" prop="username"></el-table-column>
        <el-table-column :label="$t('langEmail')" prop="email"></el-table-column>
        <el-table-column :label="$t('langCreateTime')" prop="createTime"></el-table-column>
        <el-table-column :label="$t('langDisabled')">
          <template slot-scope="scope">
            <el-switch :active-value="0" :inactive-value="1" v-model="scope.row.state" @change="stateChange(scope.row)">
            </el-switch>
          </template>
        </el-table-column>
        <el-table-column :label="$t('langAction')">
          <template slot-scope="scope">
            <!--修改-->
            <el-tooltip effect="dark" :content="$t('langEdit')" placement="top" :enterable="false">
              <el-button type="primary" icon="el-icon-edit" size="mini" @click="showEditDialog(scope.row.id)"></el-button>
            </el-tooltip>
            <!--删除-->
            <el-tooltip effect="dark" :content="$t('langDelete')" placement="top" :enterable="false">
              <el-button type="danger" icon="el-icon-delete" size="mini" @click="removeUserById(scope.row)"></el-button>
            </el-tooltip>
          </template>
        </el-table-column>
      </el-table>
      <!--分页区域-->
      <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange"
        :current-page="queryInfo.pageNum" :page-sizes="[10, 20, 30, 40]" :page-size="queryInfo.pageSize"
        layout="total, sizes, prev, pager, next, jumper" :total="userList.total">
      </el-pagination>
    </el-card>
    <!--添加用户的对话框-->
    <el-dialog :title="$t('langAddUserTitle')" :visible.sync="addDialogVisible" width="50%" @close="addDialogClosed">
      <!--内容主体区域-->
      <el-form :model="addUserForm" :rules="addUserRules" ref="addUserRef" label-width="70px">
        <el-form-item :label="$t('langUsername')" prop="username">
          <el-input v-model="addUserForm.username"></el-input>
        </el-form-item>
        <el-form-item :label="$t('langPassword')" prop="password">
          <el-input v-model="addUserForm.password" type="password"></el-input>
        </el-form-item>
        <el-form-item :label="$t('langEmail')" prop="email">
          <el-input v-model="addUserForm.email"></el-input>
        </el-form-item>
        <el-form-item :label="$t('langState')" prop="state">
          <el-select v-model="addUserForm.state">
            <el-option v-for="(item, index) in options" :key="index" :value="item.value" :label="item.label">
            </el-option>
          </el-select>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="addDialogVisible = false">{{ $t('langCancel') }}</el-button>
        <el-button type="primary" @click="addUser">{{ $t('langConfirm') }}</el-button>
      </span>
    </el-dialog>
    <!--修改用户的对话框-->
    <el-dialog :title="$t('langEditUserTitle')" :visible.sync="editDialogVisible" width="50%">
      <!--内容主体区域-->
      <el-form :model="editUserForm" :rules="addUserRules" ref="addCategoryRef" label-width="70px">
        <el-form-item :label="$t('langUsername')" prop="username">
          <el-input v-model="editUserForm.username" disabled></el-input>
        </el-form-item>
        <el-form-item label="角色" prop="roleId">
          <el-select filterable v-model="editUserForm.roleId" placeholder="请选择">
            <el-option v-for="item in roleList" :key="item.id" :value="item.id" :label="item.name">
            </el-option>
          </el-select>
        </el-form-item>
        <el-form-item :label="$t('langEmail')" prop="email">
          <el-input v-model="editUserForm.email"></el-input>
        </el-form-item>
        <el-form-item :label="$t('langDisabled')" prop="state">
          <el-select v-model="editUserForm.state">
            <el-option v-for="(item, index) in options" :key="index" :value="item.value" :label="item.label">
            </el-option>
          </el-select>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="editDialogVisible = false">{{ $t('langCancel') }}</el-button>
        <el-button type="primary" @click="editUserInfo">{{ $t('langConfirm') }}</el-button>
      </span>
    </el-dialog>
  </div>
</template>
<script>
import { getById, removeById, saveOrUpdate, selectPage } from "../../api/user";
import { select } from "../../api/role";
import LanguageMixin from '../../lang/LanguageMixin'
export default {
  name: "alarm",
  mixins: [LanguageMixin],
  data () {
    return {
      activeData: null,
      queryInfo: {
        pageNum: 1,
        pageSize: 10
      },
      userList: {
        records: [],
        total: 0,
      },
      // 添加的用户对象
      addUserForm: {
        state: 1
      },
      // 修改的用户对象
      editUserForm: {
        roleId: null,
        name: null,
        roleid: null,
      },
      // 添加分类的验证规则
      addUserRules: {
        roleId: null,
        username: [
          { required: true, message: '请输入用户名', trigger: 'blur' },
          { min: 5, max: 15, message: '长度在 5 到 15 个字符', trigger: 'blur' }
        ],
        password: [
          { required: true, message: '请输入密码', trigger: 'blur' },
          { min: 5, max: 15, message: '长度在 5 到 15 个字符', trigger: 'blur' }
        ],
        email: [
          { required: true, message: '请输入邮箱', trigger: 'blur' },
          { type: 'email', message: '请输入正确格式的邮箱地址', trigger: 'blur' }
        ]
      },
      // 控制添加用户弹框的显示和隐藏
      addDialogVisible: false,
      // 控制修改用户弹框的显示和隐藏
      editDialogVisible: false,
      options: [
        { label: '正常', value: 1 },
        { label: '禁用', value: 0 }
      ],
      roleList: [],
  export default {
    name: "alarm",
    }
  },
  created () {
    this.getUserList();
  },
  methods: {
    getUserList () {
      selectPage(this.queryInfo).then(res => {
        this.userList.records = res.data.records;
        this.userList.total = res.data.total
      });
    },
    showAddDialog () {
      this.addDialogVisible = true;
    },
    stateChange (info) {
      saveOrUpdate(info).then(() => {
        this.$message.success("更新状态成功")
      });
    },
    showEditDialog (id) {
      getById({ id: id }).then(res => {
        this.editUserForm = res.data;
        this.editDialogVisible = true;
      });
      select().then(res => {
        this.roleList = res.data;
      });
    },
    removeUserById (user) {
      // 弹框询问用户是否删除分类
      this.$confirm('此操作将永久删除该分类, 是否继续?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        removeById({ id: user.id }).then(() => {
          // 重新获取分类列表
          this.getUserList();
          this.$message.success("删除用户成功");
        });
      }).catch(() => {
        this.$message.info('已取消删除');
      });
    },
    handleSizeChange (newSize) {
      this.queryInfo.pageSize = newSize;
      this.getUserList()
    },
    handleCurrentChange (newPage) {
      this.queryInfo.pageNum = newPage;
      this.getUserList()
    },
    addDialogClosed () {
      this.$refs['addUserRef'].resetFields();
    },
    addUser () {
      this.$refs.addUserRef.validate(async valid => {
        if (!valid) return;
        saveOrUpdate(this.addUserForm).then(() => {
          this.$message.success("添加用户成功");
          // 隐藏添加分类对话框
          this.addDialogVisible = false;
          // 重新获取分类列表
          this.getUserList();
        });
      })
    },
    editUserInfo () {
      this.$refs.addCategoryRef.validate(async valid => {
        if (!valid) return;
        saveOrUpdate(this.editUserForm).then(() => {
          this.$message.success("修改用户成功");
          // 隐藏添加分类对话框
          this.editDialogVisible = false;
          // 重新获取分类列表
          this.getUserList();
        });
      })
    }
  </script>
  <style lang="less" scoped>
  .el-table {
    margin-top: 15px;
    font-size: 12px;
  }
}
</script>
<style lang="less" scoped>
.el-table {
  margin-top: 15px;
  font-size: 12px;
}
.el-pagination {
  margin-top: 15px;
}
.app .el-card {
  width: 99%;
}
</style>
  .el-pagination {
    margin-top: 15px;
  }
  .app .el-card {
    width: 99%;
  }
  </style>
CanadaMes-ui/src/views/home/index.vue
@@ -5,13 +5,23 @@
        <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
       
      </el-breadcrumb>
    <div>mes控制页</div>
    </div>
  </template>
  
  <script>
  fetch('/devices')
  .then(response => response.json())
  .then(data => {
    // 在这里处理返回的设备数据
    console.log(data);
  })
  .catch(error => {
    // 处理错误情况
    console.error('Error:', error);
  });
  export default {
    name: "home",
   
CanadaMes-ui/src/views/user/index.vue
@@ -11,11 +11,11 @@
      <el-row :gutter="20">
        <!--搜索与添加区域-->
        <el-col :span="6">
          <el-input  v-model="queryInfo.username" clearable @clear="getUserList">
          <el-input placeholder="请输入用户名称"  v-model="queryInfo.username" clearable @clear="getUserList">
          </el-input>
        </el-col>
        <el-col :span="6">
          <el-input  v-model="queryInfo.email" clearable @clear="getUserList">
          <el-input  placeholder="请输入邮箱" v-model="queryInfo.email" clearable @clear="getUserList">
          </el-input>
        </el-col>
        <!--搜索按钮-->
@@ -76,22 +76,34 @@
        width="50%"
        @close="addDialogClosed">
      <!--内容主体区域-->
      <el-form :model="addUserForm" :rules="addUserRules" ref="addUserRef" label-width="70px">
        <el-form-item :label="$t('langUsername')" prop="username">
          <el-input v-model="addUserForm.username"></el-input>
      <el-form :model="addUserForm" :rules="addUserRules" ref="addUserRef" label-width="100px">
        <el-form-item :label="$t('langUsername')" prop="username" >
          <el-input v-model="addUserForm.username" ></el-input>
        </el-form-item>
        <el-form-item :label="$t('langPassword')" prop="password">
        <el-form-item :label="$t('langPassword')" prop="password" >
          <el-input v-model="addUserForm.password" type="password"></el-input>
        </el-form-item>
        <el-form-item :label="$t('langEmail')" prop="email">
          <el-input v-model="addUserForm.email"></el-input>
        </el-form-item>
        <el-form-item :label="$t('langState')" prop="state">
          <el-select v-model="addUserForm.state" >
            <el-option v-for="(item,index) in options"
                       :key="index"
                       :value="item.value"
                       :label="item.label">
            </el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="角色" prop="roleId">
          <el-select filterable v-model="addUserForm.roleId" placeholder="请选择">
            <el-option v-for="item in roleList"
                       :key="item.id"
                       :value="item.id"
                       :label="item.name">
            </el-option>
          </el-select>
        </el-form-item>
@@ -107,7 +119,7 @@
        :visible.sync="editDialogVisible"
        width="50%">
      <!--内容主体区域-->
      <el-form :model="editUserForm" :rules="addUserRules" ref="addCategoryRef" label-width="70px">
      <el-form :model="editUserForm" :rules="addUserRules" ref="addCategoryRef" label-width="100px">
        <el-form-item :label="$t('langUsername')" prop="username">
          <el-input v-model="editUserForm.username" disabled></el-input>
        </el-form-item>
@@ -143,7 +155,7 @@
<script>
import {getById, removeById, saveOrUpdate, selectPage} from "../../api/user";
import {getById, removeById, saveOrUpdate, selectPage,test,testup,call} from "../../api/user";
import {select} from "../../api/role";
import LanguageMixin from '../../lang/LanguageMixin'
@@ -207,10 +219,28 @@
        this.userList.records = res.data.records;
        this.userList.total = res.data.total
      });
      test().then(res => {
        console.log(res.data)
      });
      const data6 = {id:32,name:'6667'}; // 填入需要传递的参数
      testup(data6).then(res => {
        console.log(res.data);
      });
      call({
  id: 1,
  name: 12345
}).then(res => {
        console.log(res.data);
      });
    },
   
    showAddDialog() {
      this.addDialogVisible = true;
       select().then(res => {
  this.roleList = res.data;
});
    },
    stateChange(info) {
      saveOrUpdate(info).then(() => {
@@ -222,6 +252,8 @@
        this.editUserForm = res.data;
        this.editDialogVisible = true;
      });
      select().then(res => {
  this.roleList = res.data;
});
CanadaMes-ui/vue.config.js
@@ -20,8 +20,8 @@
        resolve: {
            extensions: ['js', 'vue'],
            alias: {
                '@': resolve('src')
            }
                '@': path.resolve(__dirname, 'src') // 确保有这个别名配置
              }
        }
    }
}