一个完整的流程表单流转

1.写在前面

一个完整的流程表单审批(起表单-->各环节审批-->回退-->重新审批-->完成),前端由Vue2+js+Element UI升级为Vue3+ts+Element Plus,后端流程框架使用Flowable,项目参考了ruoyi-vue-pro(https://gitee.com/zhijiantianya/ruoyi-vue-pro)项目。

2.视频演示

3.表单

3.1表单的设计

依据业务需求,完全自定义表单,可以依据流程节点设置表单中每个属性的读写,实现原理是读取审批节点的编码,依据编码控制每个属性的读写。

<el-tree-select
     :disabled="!required"
     v-model="form.deptCode"
     :data="deptList"
     :props="defaultPropsForData"
      check-strictly
      node-key="id"
      placeholder="请选择集团成员单位"
 />


/**
 * 根据不同参数控制表单属性的可读可写
 * @param pattern
 */
const changeRequiredByPattern = (pattern: string) => {
  if (pattern === 'create') {
    //说明增加
    required.value = true
  } else if (pattern === 'update' || pattern === 'starter') {
    //修改
    required.value = true
    getBid()
  } else {
    //只读
    required.value = false
    getBid()
  }
}

3.2.表单与流程关联

通过设计流程时定义的流程编码,在创建表单的后端服务调用中,实现表单与流程的绑定。

/**
     * 投标对应的流程定义 KEY
     */
    public static final String PROCESS_KEY = "bidApproval";


//发起BPM流程
        Map<String, Object> processInstanceVariables = new HashMap<>();
        processInstanceVariables.put("deptId", bidDO.getDeptId());
        processInstanceVariables.put("bidMoney", bidDO.getBidMoney());
        String processInstanceId = processInstanceApi.createProcessInstance(getLoginUserId(),
                new BpmProcessInstanceCreateReqDTO().setProcessDefinitionKey(PROCESS_KEY)
                        .setVariables(processInstanceVariables).setBusinessKey(bidDO.getProjectName()));

3.3.表单与前端流程实例的关联

在通过待办打开需要审批的表单时,不同的流程实例如何对应不同的表单,并在页面展示具体的表单数据了?答案是通过定义流程时填写的表单组件名称,利用Vue的<component>元组件来实现。

<component
          ref="formDetailRef"
          v-if="processInstance.id !== undefined"
          :is="processInstance.processDefinition.formComponentName"
          :processInstanceId="processInstance.id"
          :pattern="runningTasks.length > 0 ? runningTasks[0].definitionKey : 'readOnly'"
          @success="getDetail"
        />

3.4.表单的保存

表单的保存分为提交时的保存与不提交的保存。不提交的保存用于修改数据但流程不需要提交到下一节点审批的情况,方便保存数据进入待办里面进行后续的修改。提交的时,流程会自动调用保存接口,先进行业务数据的保存,然后再进行流程的提交。

<el-button color="#626aef" @click="handleSave">
                <Icon icon="ep:coin" />
                保存
              </el-button>
              <el-button type="success" @click="handleApproval(item)">
                <Icon icon="ep:select" />
                提交
              </el-button>

/** 处理保存表单的操作 只更新表单数据 不提交流程任务 */
const formDetailRef = ref()
const handleSave = () => {
  formDetailRef.value.submitForm()
}

/** 处理审批通过的操作 */
const approvalRef = ref()
const handleApproval = async (item) => {
  approvalRef.value.open(item)
}
@Override
    @Transactional(rollbackFor = Exception.class)
    public Long updateBid(BidUpdateReqVO updateReqVO) {
        BidDO bidDO = BidConvert.INSTANCE.convert3(updateReqVO);
        //checkDeptIsMateTendererDept(deptRespDTO, tendererDeptRespDTO);
        bidDO.setDeptId(findDeptByCode(updateReqVO.getDeptCode()).getId());
        bidDO.setTendererId(findDeptByName(updateReqVO.getTendererName()).getId());
        fileApi.deleteFile(bidMapper.selectById(bidDO.getId()).getFiles(), bidDO.getFiles());
        bidMapper.updateById(bidDO);
        return bidDO.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void approveProcessTask(BidUpdateReqVO bidUpdateReqVO, BpmProcessTaskApprovalDTO bpmProcessTaskApprovalDTO) {
        //需要根据流程中不同的节点 更改对应的表单信息 比如 需要在流程最后一个节点点击提交时 更改流程状态为完成
        BidDO bidDO = BidConvert.INSTANCE.convert3(bidUpdateReqVO);
        bidDO.setDeptId(findDeptByCode(bidUpdateReqVO.getDeptCode()).getId());
        bidDO.setTendererId(findDeptByName(bidUpdateReqVO.getTendererName()).getId());
        updateFlowInfoByProcessInstanceState(bidDO);
        fileApi.deleteFile(bidMapper.selectById(bidDO.getId()).getFiles(), bidDO.getFiles());
        bidMapper.updateById(bidDO);
        bpmProcessTaskApi.approvalTask(bpmProcessTaskApprovalDTO);
    }

4.流程的审批

流程的审批按照设计流程的审批节点依次进行流转,不支持夸环节提交,支持流程的自由回退。提交当前审批任务时,进行下一节点的人员选择,在流程设计时,每个节点审批的人员的选择逻辑已经确定,也支持自由选择组织中的所有人员。

4.1.流程审批人员的设置

流程审批节点的人员设置主要思路为给定一个角色,让审批人员提交任务时,从角色中选择一个人员,这样可以缩小选择的范围。如果表单有对应的部门属性,可以设置审批人员是某个角色中且部门与表单部门属性相同的人员。

// 选择角色中的人员
private Set<Long> calculateTaskCandidateUsersByRole(BpmTaskAssignRuleDO rule) {
        return permissionApi.getUserRoleIdListByRoleIds(rule.getOptions());
    }

//依据角色选择表单部门中的所属人员
private Set<Long> calculateTaskCandidateUsersByRolePerson(Map<String, Object> variables, BpmTaskAssignRuleDO rule) {
        Long deptId = (Long) variables.get("deptId");
        //获取流程实例变量的部门
        //从角色中获取属于该部门的人员
        Set<Long> userIdsByRoleId = permissionApi.getUserRoleIdListByRoleIds(rule.getOptions());
        Set<Long> userIdsByDeptId = userApi.getUsersByDeptId(deptId);
        return new HashSet<>(CollUtil.intersection(userIdsByDeptId, userIdsByRoleId));
    }

4.2.流程的流转

流程的流转其实没有什么好说的,就是按照流程设计的审批节点依次往下走,遇到网关时,根据前期设计好的条件读取对应的属性跳转到不同审批支线。本例中,会根据投标金额是否大于500万做判断,走不同的分支,而500万的属性,在创建流程时就已经传入了流程实例的变量中。

流程的回退,流程的回退依据流程的节点图,不管流程流转了多少圈,回退只允许回退当前审批节点的前面节点。

public Set<BpmDoneUserTaskNodeRespVO> getDoneUserTaskNodes(String taskId) {
        Set<BpmDoneUserTaskNodeRespVO> resultList = new HashSet<>();
        //获取流程实例id
        Task task = getTask(taskId);
        // 校验流程实例存在
        ProcessInstance instance = processInstanceService.getProcessInstance(task.getProcessInstanceId());
        //获取历史任务实例 条件为 流程实例 未完成 按照任务开始时间降序排列
        List<HistoricTaskInstance> historicTaskInstances = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(instance.getProcessInstanceId())
                .finished()
                .orderByHistoricTaskInstanceEndTime()
                .desc()
                .list();
        //需要做一个筛选,只能选择当前任务节点之前的节点进行回退
        // 1. 获取流程模型实例 BpmnModel
        BpmnModel bpmnModel = bpmProcessDefinitionService.getBpmnModel(task.getProcessDefinitionId());
        // 2. 通过任务节点id,来获取当前节点信息
        FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey());
        // 3.获取下一个节点(或者多个节点的)信息,需要去重,因为并行的节点之前的节点会找多遍
        Set<FlowElement> flowElements = new HashSet<>();
        // 4.获取流程实例的变量
        Map<String, Object> variables = taskService.getVariables(taskId);
        getBeforeNodes(flowElement, flowElements, variables);
        if (flowElements.isEmpty()){
            //说明处于第一个节点,此时不能回退
            throw exception(TASK_ROLLBACK_FORBIDDEN);
        }
        //找交集
        for(FlowElement f: flowElements) {
            HistoricTaskInstance h = historicTaskInstances.stream()
                    .filter( hi -> hi.getTaskDefinitionKey().equals(f.getId()))
                    .findFirst().orElse(null);
            if (h == null){
                //没有获取到最新的的节点审批信息,是不正常的情况
                throw exception(TASK_ROLLBACK_APPROVED_INFO_NULL);
            }
            //获取 审批人员的编号
            Long assignee = Long.valueOf(h.getAssignee());
            //获取人员信息
            UserRespDTO userRespDTO = adminUserApi.getUser(assignee);
            //获取部门信息
            DeptRespDTO deptRespDTO = deptApi.getDept(userRespDTO.getDeptId());

            resultList.add(BpmTaskConvert.INSTANCE.convertBpmDoneUserTaskNodeRespVO(h, userRespDTO, deptRespDTO));
        }
        return resultList;
    }

5.流程审批详情

流程审批详情包括审批记录与流程图的展示。

5.1.审批记录详情

审批记录根据审批的先后顺序展示数据,状态根据提交回退的不同使用不同颜色的标签显示。

<el-table v-loading="loading" :data="tasks" border>
          <el-table-column align="center" prop="name" label="审批环节" width="200" />
          <el-table-column align="center" prop="assigneeUser.nickname" label="审批人" width="180" />
          <el-table-column
            label="任务开始时间"
            align="center"
            prop="createTime"
            width="180"
            :formatter="dateFormatter"
          />
          <el-table-column
            label="任务结束时间"
            align="center"
            prop="endTime"
            width="180"
            :formatter="dateFormatter"
          />
          <el-table-column label="任务耗时" align="center" prop="durationInMillis" width="150">
            <template #default="scope">
              <span>{{ formatPast2(scope.row.durationInMillis) }}</span>
            </template>
          </el-table-column>
          <el-table-column label="审批结果" align="center" prop="result" width="150">
            <template #default="scope">
              <el-tag :type="getTimelineItemType(scope.row)"
                >{{ getDictLabel(DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT, scope.row.result) }}
              </el-tag>
            </template>
          </el-table-column>
          <el-table-column label="审批意见" align="center" prop="reason" width="250" />
        </el-table>
public List<BpmTaskRespVO> getTaskListByProcessInstanceId(String processInstanceId) {
        // 获得任务列表
        List<HistoricTaskInstance> tasks = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId)
                .orderByHistoricTaskInstanceStartTime().desc() // 创建时间倒序
                .list();
        if (CollUtil.isEmpty(tasks)) {
            return Collections.emptyList();
        }

        // 获得 TaskExtDO Map
        List<BpmTaskExtDO> bpmTaskExtDOs = taskExtMapper.selectListByTaskIds(convertSet(tasks, HistoricTaskInstance::getId));
        Map<String, BpmTaskExtDO> bpmTaskExtDOMap = convertMap(bpmTaskExtDOs, BpmTaskExtDO::getTaskId);
        // 获得 ProcessInstance Map
        HistoricProcessInstance processInstance = processInstanceService.getHistoricProcessInstance(processInstanceId);
        // 获得 User Map
        Set<Long> userIds = convertSet(tasks, task -> NumberUtils.parseLong(task.getAssignee()));
        userIds.add(NumberUtils.parseLong(processInstance.getStartUserId()));
        Map<Long, UserRespDTO> userMap = adminUserApi.getUserMap(userIds);
        // 获得 Dept Map
        Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), UserRespDTO::getDeptId));

        // 拼接数据
        return BpmTaskConvert.INSTANCE.convertList3(tasks, bpmTaskExtDOMap, processInstance, userMap, deptMap);
    }

5.2.流程图显示

流程图显示比较复杂,可以查看对应的代码,主要就是使用了bpmn-js库,根据后端的数据,进行不同的展示,核心代码个人理解是这一块。

const highlightDiagram = async () => {
  const activityList = activityLists.value
  if (activityList.length === 0) {
    return
  }
  // 参考自 https://gitee.com/tony2y/RuoYi-flowable/blob/master/ruoyi-ui/src/components/Process/index.vue#L222 实现
  // 再次基础上,增加不同审批结果的颜色等等
  let canvas = bpmnModeler.get('canvas')
  let todoActivity: any = activityList.find((m: any) => !m.endTime) // 找到待办的任务
  let endActivity: any = activityList[activityList.length - 1] // 获得最后一个任务
  // debugger
  bpmnModeler.getDefinitions().rootElements[0].flowElements?.forEach((n: any) => {
    let activity: any = activityList.find((m: any) => m.key === n.id) // 找到对应的活动
    if (!activity) {
      return
    }
    if (n.$type === 'bpmn:UserTask') {
      // 用户任务
      // 处理用户任务的高亮
      const task: any = taskList.value.find((m: any) => m.id === activity.taskId) // 找到活动对应的 taskId
      if (!task) {
        return
      }
      // 高亮任务
      canvas.addMarker(n.id, getResultCss(task.result))

      // 如果非通过,就不走后面的线条了
      if (task.result !== 2) {
        return
      }
      // 处理 outgoing 出线
      const outgoing = getActivityOutgoing(activity)
      outgoing?.forEach((nn: any) => {
        // debugger
        let targetActivity: any = activityList.find((m: any) => m.key === nn.targetRef.id)
        // 如果目标活动存在,则根据该活动是否结束,进行【bpmn:SequenceFlow】连线的高亮设置
        if (targetActivity) {
          canvas.addMarker(nn.id, targetActivity.endTime ? 'highlight' : 'highlight-todo')
        } else if (nn.targetRef.$type === 'bpmn:ExclusiveGateway') {
          // TODO 芋艿:这个流程,暂时没走到过
          canvas.addMarker(nn.id, activity.endTime ? 'highlight' : 'highlight-todo')
          canvas.addMarker(nn.targetRef.id, activity.endTime ? 'highlight' : 'highlight-todo')
        } else if (nn.targetRef.$type === 'bpmn:EndEvent') {
          // TODO 芋艿:这个流程,暂时没走到过
          if (!todoActivity && endActivity.key === n.id) {
            canvas.addMarker(nn.id, 'highlight')
            canvas.addMarker(nn.targetRef.id, 'highlight')
          }
          if (!activity.endTime) {
            canvas.addMarker(nn.id, 'highlight-todo')
            canvas.addMarker(nn.targetRef.id, 'highlight-todo')
          }
        }
      })
    } else if (n.$type === 'bpmn:ExclusiveGateway') {
      // 排它网关
      // 设置【bpmn:ExclusiveGateway】排它网关的高亮
      canvas.addMarker(n.id, getActivityHighlightCss(activity))
      // 查找需要高亮的连线
      let matchNN: any = undefined
      let matchActivity: any = undefined
      const outgoing = getActivityOutgoing(activity)
      outgoing.forEach((nn: any) => {
        let targetActivity = activityList.find((m: any) => m.key === nn.id)
        if (!targetActivity) {
          return
        }
        // 特殊判断 endEvent 类型的原因,ExclusiveGateway 可能后续连有 2 个路径:
        //  1. 一个是 UserTask => EndEvent
        //  2. 一个是 EndEvent
        // 在选择路径 1 时,其实 EndEvent 可能也存在,导致 1 和 2 都高亮,显然是不正确的。
        // 所以,在 matchActivity 为 EndEvent 时,需要进行覆盖~~
        if (!matchActivity || matchActivity.type === 'endEvent') {
          matchNN = nn
          matchActivity = targetActivity
        }
      })
      if (matchNN && matchActivity) {
        canvas.addMarker(matchNN.id, getActivityHighlightCss(matchActivity))
      }
    } else if (n.$type === 'bpmn:ParallelGateway') {
      // 并行网关
      // 设置【bpmn:ParallelGateway】并行网关的高亮
      canvas.addMarker(n.id, getActivityHighlightCss(activity))
      const outgoing = getActivityOutgoing(activity)
      outgoing.forEach((nn: any) => {
        // 获得连线是否有指向目标。如果有,则进行高亮
        const targetActivity = activityList.find((m: any) => m.key === nn.targetRef.id)
        if (targetActivity) {
          canvas.addMarker(nn.id, getActivityHighlightCss(targetActivity)) // 高亮【bpmn:SequenceFlow】连线
          // 高亮【...】目标。其中 ... 可以是 bpm:UserTask、也可以是其它的。当然,如果是 bpm:UserTask 的话,其实不做高亮也没问题,因为上面有逻辑做了这块。
          canvas.addMarker(nn.targetRef.id, getActivityHighlightCss(targetActivity))
        }
      })
    } else if (n.$type === 'bpmn:StartEvent') {
      // 开始节点 流程只要发起 开始节点就是完成状态
      let targetActivity = activityList.find((m) => m.key === n.id)
      if (targetActivity) {
        canvas.addMarker(n.id, 'highlight') // 高亮【bpmn:StartEvent】开始节点(自己)
      }
      // 开始节点
      const outgoing = getActivityOutgoing(activity)
      outgoing.forEach((nn) => {
        // outgoing 例如说【bpmn:SequenceFlow】连线
        // 获得连线是否有指向目标。如果有,则进行高亮
        let targetActivity = activityList.find((m: any) => m.key === nn.targetRef.id)
        if (targetActivity) {
          canvas.addMarker(nn.id, 'highlight') // 高亮【bpmn:SequenceFlow】连线
          canvas.addMarker(n.id, 'highlight') // 高亮【bpmn:StartEvent】开始节点(自己)
        }
      })
    } else if (n.$type === 'bpmn:EndEvent') {
      // 结束节点
      if (!processInstance.value || processInstance.value.result === 1) {
        return
      }
      canvas.addMarker(n.id, getResultCss(processInstance.value.result))
    } else if (n.$type === 'bpmn:ServiceTask') {
      //服务任务
      if (activity.startTime > 0 && activity.endTime === 0) {
        //进入执行,标识进行色
        canvas.addMarker(n.id, getResultCss(1))
      }
      if (activity.endTime > 0) {
        // 执行完成,节点标识完成色, 所有outgoing标识完成色。
        canvas.addMarker(n.id, getResultCss(2))
        const outgoing = getActivityOutgoing(activity)
        outgoing?.forEach((out) => {
          canvas.addMarker(out.id, getResultCss(2))
        })
      }
    }
  })
}

6.写在最后

本文简单的介绍了一个OA办公系统表单审批的全过程,行文比较粗糙,代码只展示了很少的一部分,如果有兴趣一起研究讨论的,欢迎留言批评指教。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

程序员有哪些接单的渠道?

这题我会&#xff01;程序员接单的渠道那可太多了&#xff0c;想要接到合适的单子&#xff0c;筛选一个合适的平台很重要。如果你也在寻找一个合适的接单渠道&#xff0c;可以参考以下这些方向。 首先&#xff0c;程序员要对接单有一个基本的概念&#xff1a;接单渠道可以先粗略…

pandas笔记:找出在一个dataframe但不在另一个中的index

1 问题描述 假设我们有两个dataframe&#xff08;这一段代码&#xff09;来自transbigdata 笔记&#xff1a;官方文档案例1&#xff08;出租车GPS数据处理&#xff09;-CSDN博客 data tbd.clean_outofshape(data, sz, col[Lng, Lat], accuracy500) data data2 tbd.clean_ta…

科研学习|论文解读——信息世界映射方法

题目&#xff1a;信息世界映射的下一步是什么&#xff1f;在情境中理解信息行为/实践的国际化和多学科方法&#xff08;What is next for information world mapping? International and multidisciplinary approaches to understanding information behaviors/ practices in …

6.2 声音编辑工具GoldWave5简介(2)

6.2.2转换声音格式 GoldWave5支持多种声音格式&#xff0c;它不但可以编辑扩展名是wav、mp3、au、voc等格式的声音文件&#xff0c;还可以编辑Apple电脑所使用的声音文件&#xff1b;并且GoldWave5还可以把Matlab中的mat文件当作声音文件来处理。利用这些功能可以很容易进行声…

深度学习笔记(三)——NN网络基础概念(神经元模型,梯度下降,反向传播,张量处理)

文中程序以Tensorflow-2.6.0为例 部分概念包含笔者个人理解&#xff0c;如有遗漏或错误&#xff0c;欢迎评论或私信指正。 截图部分引用自北京大学机器学习公开课 人工智能算法的主流分类 首先明白一个概念&#xff0c;广义上的人工智能算法并不是只有Machine Learning或Deep …

Maven和MyBatis框架简单实现数据库交互

MyBatis是一种基于Java语言的持久层框架&#xff0c;它的主要目的是简化与数据库的交互过程。MyBatis通过XML或注解配置来映射Java对象和数据库表之间的关系&#xff0c;并提供了灵活的查询方式和结果集处理机制。MyBatis还提供了事务管理、缓存机制、插件扩展等特性。 使用My…

关于httpClient 使用的注意事项

关于httpClient 使用的注意事项 用例 PoolingHttpClientConnectionManager connectionManager new PoolingHttpClientConnectionManager();// 最大连接数-不设置默认20connectionManager.setMaxTotal(200);// 每个路由最大连接数-不设置默认2connectionManager.setDefaultMax…

深度解析Java中的ReadWriteLock:高效处理并发读写操作

第1章&#xff1a;引言 大家好&#xff0c;我是小黑&#xff0c;今天咱们聊聊读写锁。当多个线程同时对同一数据进行读写操作时&#xff0c;如果没有合理的管理&#xff0c;那数据就乱套了。就好比小黑在写日记&#xff0c;突然来了一帮朋友&#xff0c;大家都想往日记本上写点…

linux 网络基础配置

将Linux主机接入到网络&#xff0c;需要配置网络相关设置一般包括如下内容&#xff1a; 主机名 iP/netmask (ip地址&#xff0c;网关) 路由&#xff1a;默认网关 网络连接状态 DNS服务器 &#xff08;主DNS服务器 次DNS服务器 第三个DNS服务器&#xff09; 一、…

工作每天都在用的 DNS 协议,你真的了解么?

我们经常访问一些网址的时候&#xff0c;浏览器里输入类似于 www.baidu.com 这样的地址&#xff0c;那么在浏览器里输入这个地址---> 百度服务器给我们返回这个百度的页面&#xff0c;中间的过程是什么样的呢&#xff1f; 带着这个问题&#xff0c;我们一起来解析一下其中的…

java通过HttpClient方式实现https请求的工具类(绕过证书验证)

目录 一、引入依赖包二、HttpClient方式实现的https请求工具类三、测试类 一、引入依赖包 引入相关依赖包 <!--lombok用于简化实体类开发--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><option…

C++内存管理机制(侯捷)笔记2

C内存管理机制&#xff08;侯捷&#xff09; 本文是学习笔记&#xff0c;仅供个人学习使用。如有侵权&#xff0c;请联系删除。 参考链接 Youtube: 侯捷-C内存管理机制 Github课程视频、PPT和源代码: https://github.com/ZachL1/Bilibili-plus 下面是第二讲allocator具体实…

React 18中新钩子 useDeferredValue 使用

React是一个流行的用于构建用户界面的JavaScript库,它不断发展以为开发人员提供优化性能的工具。 React 18中引入的此类工具之一是useDeferredValue钩子,它旨在通过优先渲染更新来提高应用程序的性能。 useDeferredValue钩子是什么? useDeferredValue钩子是React性能优化工…

【AI视野·今日Sound 声学论文速览 第四十四期】Tue, 9 Jan 2024

AI视野今日CS.Sound 声学论文速览 Tue, 9 Jan 2024 Totally 27 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Sound Papers DJCM: A Deep Joint Cascade Model for Singing Voice Separation and Vocal Pitch Estimation Authors Haojie Wei, Xueke Cao, Wenbo Xu…

PCIe进阶之Gen3 Physical Layer Transmit Logic(二)

1 文章概述 本文是接着上面一篇文章《Gen3 Physical Layer Transmit Logic(一)》继续对Gen3 Physical Layer Transmit Logic做进一步的解析,具体包含Byte Striping和Scrambling以及Serializer。 1.1 Byte Striping Gen3 x1 Ordered Set Construction如下所示: Ordered …

Vue入门五(Vue-CLI项目搭建|vue项目目录介绍|vue项目开发规范|es6导入导出语法)

文章目录 一、Vue-CLI 项目搭建介绍node环境搭建1) 下载与安装2&#xff09;测试是否安装成功 安装vue-cli安装vue脚手架 创建Vue项目1&#xff09;使用命令创建项目2&#xff09;使用图形化界面创建项目 二、vue项目目录介绍1.命令行运行vue项目2.Pycharm中运行项目3.目录结构…

k8s的策略

集群调度&#xff1a; Scheduler的调度算法&#xff1a; 预算策略 过滤出合适的节点 优先策略 选择部署的节点 NodeName&#xff1a;硬策略&#xff0c;不走调度策略&#xff0c;node1 nodeSelector&#xff1a;根据节点的标签选择&#xff0c;会走一个调度算法 只要是…

激活/注册navicat15

一、获取软件 链接&#xff1a;https://pan.baidu.com/s/1F_tiLuLvVFMEz8pDfIvDjw?pwdjjfj 提取码&#xff1a;jjfj 二、安装 安装的过程我就不放了&#xff0c;重点如下 安装完不要打开软件&#xff01; 安装完不要打开软件&#xff01; 安装完不要打开软件&#xff01;…

【阅读笔记】Chain of LoRA

一、论文信息 1 论文标题 Chain of LoRA: Efficient Fine-tuning of Language Models via Residual Learning 2 发表刊物 arXiv2023 3 作者团队 Department of Computer Science, Princeton University School of Computer Science and Engineering, Nanyang Technologic…

Unity获取系统语言

大家好&#xff0c;我是阿赵。   在使用Unity引擎做多语言的游戏时&#xff0c;很有可能需要根据用户的手机或者电脑的当前语言来设置游戏的默认语言。   Unity的API里面默认就有可以获取系统语言的方法&#xff1a; Application.systemLanguageUnity的API例子&#xff1a…