ToB企服应用市场:ToB评测及商务社交产业平台

标题: 真实业务环境-需求分析思路(二) [打印本页]

作者: 张国伟    时间: 2024-8-21 17:00
标题: 真实业务环境-需求分析思路(二)
用户管理模块优化

先聊一下写这次需求的感想,起初接下这个需求的时候,给我的感觉就是很简单,并且觉得代码三天不到就可以写完,纵然是在业务不熟悉的环境下。然后就是经历了,第三方沟通需求、确定技术方案、熟悉用户管理涉及到的多个模块的业务细节、刷数SQL、优化代码避免出现超时......测试人员给代码提bug,优化、与第三方确定实现效果上线日期、确定刷数SQL是否可在上线中使用、上线确定等。大概就是这一套下来之后我才知道我有多灵活!!! 你们应该能猜到我现在的心理变革,emmmm。不过,这次经历的好处是,我确实提拔了自己的业务明白和代码本领,而且也验证了之前学到的知识是有效的,感觉还是挺不错的。
下面就写记录一下用户管理模块的实现思路~
需求分析

查询

(1)查询条件新增所属系统,可多选;选中多个时,数据中有恣意一个则表现。
(2)列表增长所属系统,字段用 ',' 相隔。

添加

(1)所属系统:必填,多选。
(2)角色:可选角色根据所选系统动态渲染。

修改

(1)所属系统:必填,多选。
(2)角色:可选角色根据所选系统动态渲染。
(3)用户省份、地市、所属系统变更。
用户省份、地市、所属系统变更时校验用户是否为智联开发负责人(智联酿成其他系统范例)、工单受理组人员、第一接单人、督办第一接单人、工单升级人员,根据用户身份弹窗提示。例:当前用户已配置对应地区工单受理组,请删除工单受理组配置后处理。

批量删除、删除

删除用户时校验用户是否为智联开发负责人、工单受理组人员、第一接单人、督办第一接单人、工单升级人员,根据用户身份弹窗提示。例:当前用户已配置对应地区工单受理组,请删除工单受理组配置后处理。
用户到场工单存在在途工单,不允许删除用户。提示:当前用户存在在途工单,不允许删除!

导出

导出字段加上所属系统

技术方案

在和三方沟通后确定了第一种技术方案
方案一:直接在t_user表中新增 “所属系统” 字段
优点:
简单易实现:直接在现有表中新增字段,不需要创建新的表或复杂的关联
查询方便:可以直接通过现有的查询条件获取信息
性能较高:在查询和更新时,性能较高,由于没有额外的表毗连操作
缺点:
扩展性差: 假如将来需要增长更多的系统范例,字段长度限制大概会成为问题。
数据冗余: 假如一个用户属于多个系统,需要通过分隔符存储多个系统范例,这会导致数据冗余和维护困难。
数据同等性问题: 在更新和删除操作时,大概会由于字符串解析问题导致数据不同等。
方案二:新增t_user_systemType用户和所属系统字段中间表
优点:
扩展性强: 可以灵活地增长更多的系统范例,不受字段长度限制。
数据同等性高: 各个系统范例分开存储,避免了字符串解析问题,包管数据的同等性和完整性。
易于维护: 系统范例的增编削查操作更加灵活和简便。
缺点:
复杂性增长: 需要新增表和外键,增长了数据模子的复杂性。
性能开销: 查询时需要进行表毗连操作,大概会影响性能。
实现思路

背景:在之前的版本中也是存在所属系统字段的,只不过这个所属系统字段是在角色表中存放,和角色绑定。之前的所属系统的赋值是通过用户获取角色列表,然后拿到角色列表中权限最大的角色,进而拿到最大角色对应的所属系统字段,这个逻辑明显不适用在这里。
总体上思考一下需求,要完成这次需求我们要做什么,哪些技术点需要思考,哪些细节需要注意等等。
以上就是这次需求的简单构思,构思的时候需要结合代码结合业务去分析,否则只能是空想,并且也会漏掉很多点。
这里由于我是主后端手,在写前端的逻辑时相对较慢,以是就先把后端的接口给写了,先把后端初步思考的做了,之后思路打开之后前端就也完成了。要知道我在写这个需求的时候,业务也不能算很熟悉,以是要在规定的时间内提测一个测试版本,还是有肯定难度,不过,好歹是拿下来了,hhh

实现过程

汗青数据处理

备份和回退数据这里不多说,汗青数据的处理需要注意一下
根据实现思路中的背景我们知道 所属系统字段在t_role中存在,并且和角色绑定,以是可以通过用户身上的角色去拿到所属系统数据。
比如: AA角色是A系统,BB角色是B系统,CC角色是C系统的
那么当张三用户有AA、BB角色,那么张三用户的所属系统字段就应该是 A,B系统
查询出 system_type 和 user_id
去重
更新t_user表中的所属系统字段
  1. #内连接
  2. update table1 join (table2) on ... set ...
  3. # 更新字段数据
  4. UPDATE t_user u
  5. JOIN (
  6.         SELECT
  7.                 u.USER_ID,
  8.                 GROUP_CONCAT( DISTINCT r.SYSTEM_TYPE ) AS SYSTEM_TYPE
  9.         FROM
  10.                 t_user u
  11.                 LEFT JOIN t_user_role t ON u.USER_ID = t.USER_ID
  12.                 LEFT JOIN t_role r ON r.ROLE_ID = t.ROLE_ID
  13.         GROUP BY
  14.                 u.USER_ID
  15.         ) temp ON u.USER_ID = temp.USER_ID
  16.         SET u.SYSTEM_TYPE = temp.SYSTEM_TYPE;
复制代码
上线和用户管理这块相关的所有sql
  1. # 备份表数据
  2. CREATE TABLE t_user_0815 LIKE t_user;
  3. INSERT INTO t_user_0815 ( SELECT * FROM t_user );
  4. # 新增字段
  5. ALTER TABLE t_user ADD COLUMN SYSTEM_TYPE VARCHAR ( 255 ) DEFAULT NULL COMMENT '所属系统';
  6. # 更新字段数据
  7. UPDATE t_user u
  8. JOIN (
  9.         SELECT
  10.                 u.USER_ID,
  11.                 GROUP_CONCAT( DISTINCT r.SYSTEM_TYPE ) AS SYSTEM_TYPE
  12.         FROM
  13.                 t_user u
  14.                 LEFT JOIN t_user_role t ON u.USER_ID = t.USER_ID
  15.                 LEFT JOIN t_role r ON r.ROLE_ID = t.ROLE_ID
  16.         GROUP BY
  17.                 u.USER_ID
  18.         ) temp ON u.USER_ID = temp.USER_ID
  19.         SET u.SYSTEM_TYPE = temp.SYSTEM_TYPE;
  20. # 新增字段
  21. ALTER TABLE t_user ADD COLUMN IS_VALID BIT(1);
  22. # 更新字段数据
  23. UPDATE t_user SET IS_VALID = 1;
  24. #回退SQL
  25. DROP TABLE t_user;
  26. -- 重新创建 t_user 表并恢复数据
  27. CREATE TABLE t_user LIKE t_user_0815;
  28. INSERT INTO t_user (SELECT * FROM t_user_0815);
复制代码
下面的具体功能实现,按照前后端,查询、新增、修改、删除、导出 如许的顺序去进行得当的记录
查询

新增查询条件 "所属系统"  、 列表数据表现增长 "所属系统" 字段

前端直接复用Vue和Element UI组件的页面代码,但是"所属系统" 字段是新增的,以是该字段的数据获取需要调后端接口
具体的语法含义可以去 element ui 官网检察 组件 | Element
  1. # 查询条件中的所属系统
  2. <el-select v-model="queryParams.systemType" clearable filterable multiple placeholder="所属系统" >
  3.   <el-option
  4.     v-for="item in systemTypeList"
  5.     :key="item.dictValue"
  6.     :label="item.dictName"
  7.     :value="item.dictValue"
  8.   />
  9. </el-select>
  10. # 列表数据中的所属系统
  11. <el-table-column label="所属系统" align="center" min-width="100px" show-overflow-tooltip>
  12.     <template slot-scope="scope">
  13.         {{ translateSystemType(scope.row.systemType) }}
  14.     </template>
  15. </el-table-column>
复制代码
下面是所属系统数据的获取
在获取所属系统数据时,需要特别注意对后端返回的系统范例字段进行转换。后端返回的数据通常是一个逗号分隔的字符串,比如 "energy,zhuti,test",需要将其转换为对应的中文名称字符串。为实现这一转换,起首将字符串拆分为数组,再通过匹配系统范例列表,将每个英文代码转换为相应的中文名称,并终极将这些名称组合成一个完整的中文字符串。
  1. // 请求参数对象,包括类型、用户名、地区、省市、时间范围、角色ID和所属系统字段
  2. queryParams: {
  3.   typename: '',  // 类型名称
  4.   username: '',  // 用户名
  5.   province: '',  // 省份
  6.   city: '',      // 城市
  7.   timeRange: '', // 时间范围
  8.   roleId: [],    // 角色ID数组
  9.   systemType: null // 所属系统字段,初始值为空
  10. },
  11. systemTypeList: [], // 初始化系统类型列表
  12.    
  13. // 初始化 systemTypeList 数据的方法
  14. initSystemTypeList() {
  15.   // 调用 getUserInfo 方法获取当前用户信息
  16.   getUserInfo(this.currentUser.userId).then(res => {
  17.     // 判断获取的用户系统类型数据是否为空
  18.     if (res.data.data.systemType !== '') {
  19.       // 如果不为空,将系统类型字符串转为数组
  20.       const systemTypeArray = res.data.data.systemType.split(',');
  21.       // 获取系统类型列表
  22.       this.getSystemTypeList(systemTypeArray);
  23.     } else {
  24.       // 如果为空,显示错误信息
  25.       this.$message.error("字典数据获取为空或者失败");
  26.     }
  27.   });
  28. },
  29. // 根据系统类型获取对应的系统类型列表 --- 比如 'energy' 变成 dictValue:'energy' 和 dictName:'能源'
  30. getSystemTypeList(systemType) {
  31.   // 遍历系统类型数组
  32.   systemType.forEach(systemType => {
  33.     // 如果系统类型为 'energy' 且未存在于 systemTypeList 中,则将其添加
  34.     if (systemType === 'energy') {
  35.       if (!this.systemTypeList.find(item => item.dictValue === 'energy')) {
  36.         this.systemTypeList.push({ dictValue: systemType, dictName: '能源' });
  37.       }
  38.     }
  39.     // 如果系统类型为 'yunguan' 且未存在于 systemTypeList 中,则将其添加
  40.     if (systemType === 'yunguan') {
  41.       if (!this.systemTypeList.find(item => item.dictValue === 'yunguan')) {
  42.         this.systemTypeList.push({ dictValue: systemType, dictName: '行拓(智联)' });
  43.       }
  44.     }
  45.     // 如果系统类型为 'yunyingshang' 且未存在于 systemTypeList 中,则将其添加
  46.     if (systemType === 'yunyingshang') {
  47.       if (!this.systemTypeList.find(item => item.dictValue === 'yunyingshang')) {
  48.         this.systemTypeList.push({ dictValue: systemType, dictName: '主体' });
  49.       }
  50.     }
  51.   });
  52. },
  53. // 将系统类型从英文代码翻译为对应的中文名称
  54. translateSystemType(systemType) {
  55.   if (!systemType) return ''; // 如果系统类型为空,返回空字符串
  56.   // 将系统类型字符串按逗号分隔,并逐个查找对应的中文名称
  57.   return systemType
  58.     .split(',')
  59.     .map(dictValue => {
  60.       const item = this.systemTypeList.find(item => item.dictValue === dictValue);
  61.       return item ? item.dictName : ''; // 找到对应的 dictCode 后返回 dictName,否则返回空字符串
  62.     })
  63.     .filter(name => name !== '') // 过滤掉空字符串
  64.     .join(','); // 将结果连接成一个字符串
  65. }
复制代码
查询按钮,当我们点击查询时触发search方法
  1. <el-button  type="primary" @click="search">
  2.   {{ $t('table.search') }}
  3. </el-button>
复制代码
跳转search方法,在search方法中调用后端接口("/user/list")获取列表信息
  1. // 请求参数对象,包括类型、用户名、地区、省市、时间范围、角色ID和所属系统字段
  2. queryParams: {
  3.   typename: '',  // 类型名称
  4.   username: '',  // 用户名
  5.   province: '',  // 省份
  6.   city: '',      // 城市
  7.   timeRange: '', // 时间范围
  8.   roleId: [],    // 角色ID数组
  9.   systemType: null // 所属系统字段,初始值为空
  10. },
  11. search() {
  12.   this.fetch({
  13.     ...this.queryParams,
  14.     ...this.sort
  15.   })
  16. },
复制代码
后端
后端User实体类中添加三个字段
为什么在实体类中添加systemType字段之后还需要添加systemTypeList、noSystemTypeList?
在上述技术方案中,选择在 t_user 表中新增一个字段来存储用户所属系统,而不是通过创建单独的表来维护用户与系统范例的关系。因此,在查询时需要处理多对多的关系。例如,查询条件为 "systemA,systemB",而数据库中的字段大概存储的是 "systemA,systemC,systemB"。
两种解决方案(使用的第二种)
  1. /**
  2. * 所属系统
  3. */
  4. @TableField("SYSTEM_TYPE")
  5. @ExcelField(value = "所属系统", required = true)
  6. private String systemType;
  7. /**
  8. * 查询条件:系统类型多选
  9. */
  10. @TableField(exist = false)
  11. private List<String> systemTypeList;
  12. /**
  13. * 查询条件:当前登录用户不包含的系统类型列表
  14. */
  15. @TableField(exist = false)
  16. private List<String> noSystemTypeList;
复制代码
SQL部分逻辑
  1. <if test="user.systemTypeList != null and user.systemTypeList.size() > 0">
  2.     AND (
  3.         <foreach item="type" collection="user.systemTypeList" separator="or">
  4.             FIND_IN_SET(#{type}, u.system_type)
  5.         </foreach>
  6.         )
  7. </if>
  8. <if test="user.noSystemTypeList != null and user.noSystemTypeList.size() > 0">
  9.     AND
  10.     <foreach item="type" collection="user.noSystemTypeList" separator="and">
  11.         u.system_type not like CONCAT('%',#{type},'%')
  12.     </foreach>
  13. </if>
复制代码
新增

前端优化直接参考修改处
修改

前端
角色列表数据根据所属系统和当前登录用户的最大角色权限动态获取展示

父组件中引入子组件对话框(父子组件之间的交互)
当我们点击修改按钮后,isVisible属性置为true,弹出修改对话框,并将用户数据赋值到子组件的属性中
  1. <user-edit
  2.   ref="edit"
  3.   :dialog-visible="dialog.isVisible"
  4.   :title="dialog.title"
  5.   @success="editSuccess"
  6.   @close="editClose"
  7. />
  8. editClose() {
  9.   # 关闭对话框
  10.   this.dialog.isVisible = false
  11. },
  12. editSuccess() {
  13.   #修改成功后,查询页面列表数据
  14.   this.search()
  15. }
复制代码
在下面的edit方法中,赋值角色、部门信息,并且调用子组件中的setUser用户给属性赋值
  1. edit(row) {
  2.   let roleId = []
  3.   if (row.roleId && typeof row.roleId === 'string') {
  4.     roleId = row.roleId.split(',')
  5.     #给row本行数据中的roleId赋值
  6.     row.roleId = roleId
  7.   }
  8.   this.$get(`system/user/${row.userId}`).then((r) => {
  9.     #部门ids赋值
  10.     row.deptIds = r.data.data
  11.     #用户信息赋值
  12.     this.$refs.edit.setUser(row)
  13.     this.$refs.edit.setSubFlag(false)
  14.     this.dialog.title = this.$t('common.edit')
  15.     this.dialog.isVisible = true
  16.   })
  17. },
复制代码
子组件(修改对话框)
在子组件中要想在改变所属系统字段时,角色列表展示信息更新,只需要在设置一个@change="systemTypeChange"方法,该方法参数是systemType,然后调后端接口获取橘色列表信息
  1. [/code][b]后端[/b]
  2. 修改后端接口实现思路:
  3. 1.更改省份、地市时,需校验用户是否是第一接单人、督办第一接单人、工单升级人员,假如是则直接回显提示信息,假如不是则修改成功
  4. 2.禁用账号状态时,需校验用户是否是第一接单人、督办第一接单人、工单升级人员、工单受理组人员、用户在途工单(草稿,待评价,待处理等工单)
  5. 3.修改所属系统时,需校验所属系统是否是从智联 -> 其他系统,假如是则还需要校验智联开发负责人,假如不是则只需校验第一接单人、督办第一接单人、工单升级人员
  6. 4.删除用户时,需要校验用户是否是第一接单人、督办第一接单人、工单升级人员、工单受理组人员、用户在途工单(草稿,待评价,待处理等工单)
  7. 起初将这些校验逻辑写在了 update 接口中,但在本地运行时,接口常常会超时。下面我只展示终极的结果供大家参考。中间的过程就不具体赘述了,以免干扰大家
  8. 之前的文章中就说了,写需求起首就是要把思路理清,如许做才不会白费功夫
  9. 根据上面的校验内容可以把第一接单人、督办第一接单人、工单升级人员提在一个远程接口中复用,参数范例有userId,systemType,province,city
  10. [code]/**
  11. *  判断被修改用户是否是第一接单人、督办人、升级人员
  12. * @param userId
  13. * @param systemType
  14. * @param province
  15. * @param city
  16. * @return
  17. */
  18. @GetMapping("/***/checkOtherBusiness")
  19. public String checkOtherBusiness(@RequestParam("userId") String userId,
  20.                           @RequestParam(value = "systemType",required = false) String systemType,
  21.                           @RequestParam(value = "province",required = false) String province,
  22.                           @RequestParam(value = "city",required = false) String city);
复制代码
userId 参数是必填项,用于指定需要校验的用户,后面的参数可根据用户修改内容选择性填写。比如修改省份时,就传省份参数其他参数为空。
校验工单受理组和在途工单校验写了两个接口
  1. /**
  2. * 远程调用 查询用户在途工单数量
  3. * @param userId
  4. * @return
  5. */
  6. @GetMapping("/***/getJoinCount")
  7. public Integer getJoinCount(@RequestParam(value = "userId",required = false) String userId);
复制代码
  1. /**
  2. * 根据userId在工单受理组中查询用户
  3. * @param userId
  4. * @return
  5. */
  6. @GetMapping("/***/queryUserByAllProvince")
  7. public List<ProEmployeeVo> queryUserByAllProvince(@RequestParam("userId") String userId);
复制代码
接着就是主逻辑应该如何写
下面接口方法就是校验第一接单人、督办、升级,返回的String大概就是 "第一接单人、第一督办接单人、工单升级人员"

下面抽出来部分代码用于展示错误信息打印逻辑
  1. // 定义返回结果的Msg
  2. Set<String> errMsgSet = new HashSet<>();
  3. String result = problemApiClient.checkOtherBusiness(String.valueOf(oldUser.getUserId()), null, null, null);
  4. if (StringUtils.isNotBlank(result)) errMsgSet.addAll(Arrays.asList(result.split("、")));
  5. // 检查用户是否有未处理的工单
  6. joinCount = problemApiClient.getJoinCount(String.valueOf(user.getUserId()));
  7. // 查询当前用户是否在工单受理组中
  8. employeeVos = problemApiClient.queryUserByAllProvince(user.getUserId().toString());
  9. if(employeeVos.size() > 0){
  10.     errMsgSet.add("工单受理组人员");
  11. }
  12. StringBuilder resultErrMsg = new StringBuilder();
  13. // 构建最终的错误信息并抛出异常
  14. if (!errMsgSet.isEmpty() && joinCount > 0) {
  15.   resultErrMsg.append("当前用户为").append(String.join("、", errMsgSet)).append(",并且存在").append(joinCount).append("个在途工单,无法进行修改操作。");
  16. } else if (!errMsgSet.isEmpty()) {
  17.   resultErrMsg.append("当前用户为").append(String.join("、", errMsgSet)).append(",无法进行修改操作。");
  18. } else if (joinCount > 0) {
  19.   resultErrMsg.append("当前用户存在 ").append(joinCount).append(" 个在途工单,无法进行修改操作。");
  20. }
  21. if(StringUtils.isNotBlank(resultErrMsg)) throw new FebsRuntimeException(resultErrMsg.toString().trim());
复制代码
修改逻辑的总结,由于是需求写完之后写的笔记,以是其实在写的过程中一些比力好的点大概在写的时候没思量到,以是只记录了部分逻辑
删除

这里可类比修改,调的接口都一样,只是错误打印的内容需要更改
导出

加注解,然后就是导出的时候做了转义
  1. // 所属系统字段转义
  2. List<SystemUser> users = this.baseMapper.exportUserDetail(user);
  3. users.forEach(SystemUser::translateSystemType);
复制代码
  1. public void translateSystemType(){
  2.     if(StringUtils.isNotEmpty(this.systemType)){
  3.         this.systemType = Arrays.stream(this.systemType.split(","))
  4.                 .map(systemType  -> {
  5.                     switch (systemType){
  6.                         case "energy":
  7.                             return "能源";
  8.                         case "yunguan":
  9.                             return "智联";
  10.                         case "yunyingshang":
  11.                             return "主体";
  12.                         default:
  13.                             return systemType;
  14.                     }
  15.                 })
  16.                 .collect(Collectors.joining(","));
  17.     }
  18. }
复制代码
需求测试

测试人员测试,提bug,然后开发人员修改代码
版本上线

和三方沟通确认功能,确认上线时间,上线版本的需求
知识总结

这里把接口调用写在JS文件中调用,不要直接写在代码中
好处:
起首,最大的好处就是代码复用,这点毋庸置疑。其次,分离业务逻辑代码和接口调用逻辑有助于解耦合,使代码结构更加清楚。再者,思量到后期的维护性,假如接口调用需要修改,只需要在一个地方进行调整,而不必逐一修改所有调用该接口的代码。

http哀求的严格区分
get、post、put、delete?_删除用get还是post-CSDN博客
由于再写删除第一接单人的时候用的是GET哀求,这里是不好的
所有 http 哀求,同等用 POST,在业务功能的实现是没有问题的.
post,get,put,delete 是标准, 大家都遵照如许的规则. 如许的api对于它人来说一目了然, get就是获取数据, post就是提交数据, put就是更新数据, delete就做删除操作. 假如同等使用post对一个项目组的内部人员来说是没有问题的, 但是对于对外公开的接口就让调用者摸不着头脑了。
以遵照 RFC-2616 所定义的协议的方式显式地使用 HTTP 方法,创建创建、检索、更新和删除(CRUD:Create, Retrieve, Update and Delete)操作与 HTTP 方法之间的一对一映射:
@RequestParam和@PathVariable
哀求参数和路径参数
相同点与区别
@RequestParam和@PathVariable都可以大概完成类似的功能——由于本质上,它们都是用户的输入,只不过输入的部分不同,一个在URL路径部分,另一个在参数部分。要访问一篇博客文章,这两种URL计划都是可以的:
那么究竟应该选择哪一种呢?建议:
1、当URL指向的是某一具体业务资源(或资源列表),例如博客,用户时,使用@PathVariable
2、当URL需要对资源大概资源列表进行过滤,筛选时,用@RequestParam
例如我们会如许计划URL:
更多用法
一旦我们在方法中定义了@RequestParam变量,假如访问的URL中不带有相应的参数,就会抛出非常——这是显然的,Spring尝试帮我们进行绑定,然而没有成功。但有的时候,参数确实不肯定永远都存在,这时我们可以通过定义required属性:
  1. @RequestParam(value = "id", required = false)
复制代码
固然,在参数不存在的环境下,大概盼望变量有一个默认值:
  1. @RequestParam(value = "id", required = false, defaultValue = "0")
复制代码
前端父子组件之间的交互
这里父组件引入子组件
  1. <user-edit
  2.   ref="edit"
  3.   :dialog-visible="dialog.isVisible"
  4.   :title="dialog.title"
  5.   @success="editSuccess"
  6.   @close="editClose"
  7. />
  8. <user-view
  9.   ref="view"
  10.   :dialog-visible="userViewVisible"
  11.   @close="viewClose"
  12. />
  13. import UserEdit from './Edit'
  14. import UserView from './View'
复制代码
  1. ref="edit":ref 属性用于给组件实例一个引用名称。通过 this.$refs.edit 可以在父组件中访问这个 user-edit 组件实例。
  2. :dialog-visible="dialog.isVisible":dialog-visible 是一个属性绑定,dialog.isVisible 是父组件中的一个布尔值,控制 user-edit 组件中的对话框是否可见。使用了 : 来动态绑定父组件中的 dialog.isVisible 数据。
  3. :title="dialog.title":类似地,title 也是一个属性绑定,传递的是 dialog.title,用于设置 user-edit 组件中的标题。
  4. @success="editSuccess":@ 是 Vue.js 事件绑定的简写形式。这个绑定表示,当 user-edit 组件触发 success 事件时,父组件的 editSuccess 方法会被调用。
  5. @close="editClose":同样,当 user-edit 组件触发 close 事件时,父组件的 editClose 方法会被调用。
复制代码
典范场景
这里是如何获取row一行数据的
row 参数是通过 el-table 组件的 slot-scope 传递给插槽内的模板的
好吧什么是插槽?

对象展开语法
...val 表现将对象 val 的所有属性逐一展开到新的对象中。
{ ...val } 创建了一个新对象,这个新对象包含了 val 对象的所有属性和对应的值。

前端store的学习
vuex之store的基本使用

Arrays.asList返回的List无法add和remove
分析源码就知道了为什么了,这里我会写一篇新的
关于StringBuilder 出现null的问题
在写需求时出现了一个奇怪的报错,当problemApiClient调用checkOtherBusiness方法返回的String对象为空时,也就是null
然后调用roleErrMsg.append(result) 的时候会让这个StringBuilder对象中存储值 "null" ,导致不为空
我这里复现失败
https://blog.csdn.net/Mikeoperfect/article/details/106739567


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4