芋道框架审批流如何实现(Cloud+Vue3)

本文涉及的产品
应用实时监控服务-可观测链路OpenTelemetry版,每月50GB免费额度
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 芋道框架审批流如何实现(Cloud+Vue3)

 审批流的其实就是提交之后发送给你的上级然后让他进行批阅,再进行反馈。实际上的就是发送、接收、改变状态、再发送的一个过程。

芋道框架帮我们集成了这个功能,下面就让我来带大家做一个简易版的审批流。

首先:数据库准备一定要有这四个字段 image.gif 编辑

后端:

实体类:

DO类:

/**
     * 导入批次号
     */
    private String importBatch;
    /**
     * 录入批次号
     */
    private String subBatchNo;
    /**
     * 状态
     */
    private Long status;
    private String processInstanceId;

image.gif

SaveReqVO类:

@Schema(description = "录入批次号", example = "9103")
    private String subBatchNo;
    @Schema(description = "导入批次号", example = "9103")
    private String importBatch;
    @Schema(description = "状态", example = "9103")
    private Long status;
    private String processInstanceId;

image.gif

RespVO类:

private String subBatchNo;
    private Long status;
    private String processInstanceId;

image.gif

提交审核的方法

Service:

 

void examine(List<TbQyyrYrzyjpljlbRespVO> examineReqVO);

image.gif

实现类:

public static final String PROCESS_KEY = "carbon-yr-jcdjpl";
    public static final String prefix = "yr-b-";
    @Resource
    private BpmUtil bpmUtil;
   @Override
    public void examine(List<TbQyyrYrzyjpljlbRespVO> examineReqVO) {
        // 生成提交批次号
        String submitBatchNo = NumberGenerate.generateNumber(prefix);
        // 发送到审批流程
        String processInstanceId = bpmUtil.submitToBpm(submitBatchNo, PROCESS_KEY);
        for(TbQyyrYrzyjpljlbRespVO tbQyyrYrzyjpljlbRespVO : examineReqVO){
            TbQyyrYrzyjpljlbDO updateObj = BeanUtils.toBean(tbQyyrYrzyjpljlbRespVO, TbQyyrYrzyjpljlbDO.class);
            // 每条数据保存提交批次号
            updateObj.setSubBatchNo(submitBatchNo);
            // 每天数据保存审批流 processInstanceId
            updateObj.setProcessInstanceId(processInstanceId);
            updateObj.setStatus(2L);
            tbQyyrYrzyjpljlbMapper.updateById(updateObj);
        }
    }

image.gif

记住这里的PROCESS_KEY和prefix,后面有用,这是唯一的标识。

Controller:

@PostMapping("/examine")
    @Operation(summary = "线缆原材料生产数据采集审核")
    public CommonResult<Boolean> examine(@RequestBody List<TbQyyrYrzyjpljlbRespVO>  examineReqVO ) {
        tbQyyrYrzyjpljlbService.examine(examineReqVO);
        return success(true);
    }

image.gif

generateNumber方法:

package com.todod.carbon.framework.common.util.common;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Random;
public class NumberGenerate {
    public static String generateNumber(String prefix) {
        // 使用当前时间作为随机数生成器的种子
        Random random = new Random(System.currentTimeMillis());
        // 生成一个0到9999之间的随机数
        int randomNumber = random.nextInt(10000);
        String batchNo = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))+randomNumber;
        return prefix + batchNo;
    }
}

image.gif

该方法用于生成提交的批次号。

提交方法写完后我们回到页面的工作流程去做一个审批流

image.gif 编辑

image.gif 编辑

这里的流程标识需要和我们定义的PROCESS_KEY是一样的。

image.gif 编辑

按照这个流程,创建之后我们先去修改流程补全信息

image.gif 编辑

这里的表单查看地址对应了一个只能查看的页面用于审批的时候能够看到审批的信息

点击设计流程

image.gif 编辑

按照这样配置,画一个流程审批图

点击保存模型后发布

image.gif 编辑

接下来我们写一个改变状态的方法:

service:

void setStatus(String subNo,Integer status);

image.gif

实现类:

@Override
    public void setStatus(String id, Integer bpmStatus) {
        List<TbQyyrYrzyjpljlbDO> yrzyjpljlbDOS = tbQyyrYrzyjpljlbMapper.selectBpmList(id);
        if (yrzyjpljlbDOS != null && yrzyjpljlbDOS.size()>0){
            yrzyjpljlbDOS.forEach(item->{
                item.setStatus(Long.parseLong(bpmStatus.toString()));
                tbQyyrYrzyjpljlbMapper.updateById(item);
            });
        }
        if (bpmStatus == 3){
            //执行kettle
//            kettleUtils.runKTR(kettleFileName);
        }
    }

image.gif

selectBpmList方法:


default List<TbQyyrYrzyjpljlbDO> selectBpmList (String processInstanceId){
        return selectList(new LambdaQueryWrapperX<TbQyyrYrzyjpljlbDO>()
                .eq(TbQyyrYrzyjpljlbDO::getProcessInstanceId,processInstanceId)
        );
    }

image.gif

这个方法会根据返回的审批id去查找符合条件的数据集合,也就是同一时间审批的那一批数据。

改变状态的方法就写好了,接下来我们去Bpm模块写一个监听方法。

image.gif 编辑

在这里写一个监听类,层级是这样的,大家不要写错了。

package com.todod.carbon.module.bpm.service.oa.listener;
import com.todod.carbon.module.bpm.event.BpmProcessInstanceStatusEvent;
import com.todod.carbon.module.bpm.event.BpmProcessInstanceStatusEventListener;
import com.todod.carbon.module.system.api.user.AdminUserApi;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Component;
@Component
public class YrZyJplListener extends BpmProcessInstanceStatusEventListener {
    public static final String PROCESS_KEY = "carbon-yr-jcdjpl";
    @Resource
    private AdminUserApi adminUserApi;
    @Override
    protected String getProcessDefinitionKey() {
        return PROCESS_KEY;
    }
    @Override
    protected void onEvent(BpmProcessInstanceStatusEvent event) {
        String id = event.getId();
        adminUserApi.setCommonStatus(id,event.getStatus(),"yr-b");
    }
}

image.gif

这里依旧要保证PROCESS_KEY和prefix一致。

这是我自己写的一个改变状态的rpc,大家可以自己写一个,我相信并不难,我放这大家可以参考。

@GetMapping(PREFIX + "/bpmCommonStatus")
    @Operation(summary = "工作流状态")
    @Parameters({
            @Parameter(name = "id", description = "订单id", example = "1", required = true),
            @Parameter(name = "bpmStatus", description = "工作流状态", example = "3", required = true),
            @Parameter(name = "type", description = "对应的工作表", example = "3", required = true)
    })
    void setCommonStatus(@RequestParam("id")String id, @RequestParam("status")Integer status, @RequestParam("type")String type);

image.gif

@Override
    public void setStatus(Long id, Integer bpmStatus) {
        switch (bpmStatus) {
            case 2://通过
                break;
            case 3://未通过
                break;
            case 4://撤回、取消 审批
                break;
        }
    }
    @Override
    public void setCommonStatus(String id, Integer bpmStatus, String type) {
        switch (bpmStatus) {
            case 2://通过
                bpmStatus = 3;
                break;
            case 3://未通过
                bpmStatus = 4;
                break;
            case 4://撤回、取消 审批
                bpmStatus = 1;
                break;
        }
        switch (type){
            //产品信息
            case "xl-a":
                tbXlcpCpxxService.setStatus(id,bpmStatus);
            //原材料生产
            case "xl-b":
                tbXlcpYclscjdService.setStatus(id,bpmStatus);
            //原材料运输
            case "xl-c":
                tbXlcpYclysjdService.setStatus(id,bpmStatus);
            //产品生产
            case "xl-d":
                tbXlcpCpscjdService.setStatus(id,bpmStatus);
            //产品运输
            case "xl-e":
                tbXlcpCpysjdService.setStatus(id,bpmStatus);
            //产品运输
            case "xl-f":
                tbXlcpCpsyjdService.setStatus(id,bpmStatus);
            //产品回收
            case "xl-g":
                tbXlcpCphsjdService.setStatus(id,bpmStatus);
            //楼宇电表
            case "ly-a":
                lyntglAmmeterInfoService.setStatus(id,bpmStatus);
            //楼宇水表
            case "ly-b":
                lyntglWaterinfoService.setStatus(id,bpmStatus);
           //楼宇天然气
            case "ly-c":
                lyntglTrqInfoService.setStatus(id,bpmStatus);
           //楼宇市政热力
            case "ly-d":
                lyntglSzrlInfoService.setStatus(id,bpmStatus);
           //楼宇可再生能源
            case "ly-e":
                lyntglKzsnyInfoService.setStatus(id,bpmStatus);
           //工业园区
            case "gyyq-a":
                tbGyyqGyyqcjService.setStatus(id,bpmStatus);
           //余热-监测点
            case "yr-a":
                tbQyyrJcdbService.setStatus(id,bpmStatus);
            default:
                break;
        }
    }

image.gif

前端:

大家可以那前端做个参考,我就把完整代码放到这里了。

<template>
  <ContentWrap>
    <!-- 搜索工作栏 -->
    <el-form
      class="-mb-15px"
      :model="queryParams"
      ref="queryFormRef"
      :inline="true"
      label-width="130px"
    >
      <el-form-item label="企业名称" prop="ttbQyyrQyname">
        <el-input
          v-model="queryParams.ttbQyyrQyname"
          placeholder="请输入企业名称"
          clearable
          @keyup.enter="handleQuery"
          class="!w-240px"
        />
      </el-form-item>
      <el-form-item label="余热标识" prop="ttbQyyrYrbs">
        <el-input
          v-model="queryParams.ttbQyyrYrbs"
          placeholder="请输入余热标识"
          clearable
          @keyup.enter="handleQuery"
          class="!w-240px"
        />
      </el-form-item>
      <el-form-item label="余热资源类型" prop="ttbQyyrYrzylx">
        <el-input
          v-model="queryParams.ttbQyyrYrzylx"
          placeholder="请输入余热资源类型"
          clearable
          @keyup.enter="handleQuery"
          class="!w-240px"
        />
      </el-form-item>
      <el-form-item label="填报日期" prop="ttbTbDate">
        <el-date-picker
          v-model="queryParams.ttbTbDate"
          value-format="YYYY-MM-DD HH:mm:ss"
          type="daterange"
          start-placeholder="开始日期"
          end-placeholder="结束日期"
          :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
          class="!w-240px"
        />
      </el-form-item>
      <el-form-item label="创建时间" prop="createTime">
        <el-date-picker
          v-model="queryParams.createTime"
          value-format="YYYY-MM-DD HH:mm:ss"
          type="daterange"
          start-placeholder="开始日期"
          end-placeholder="结束日期"
          :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
          class="!w-240px"
        />
      </el-form-item>
      <el-form-item>
        <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
        <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
        <el-button
          type="primary"
          plain
          @click="openForm('create')"
          v-hasPermi="['system:tb-qyyr-yrzyjpljlb:create']"
        >
          <Icon icon="ep:plus" class="mr-5px" /> 新增
        </el-button>
        <el-button
          type="warning"
          plain
          @click="handleImport"
        >
          <Icon icon="ep:upload" /> 导入
        </el-button>
        <el-button
          type="success"
          plain
          @click="handleExport"
          :loading="exportLoading"
          v-hasPermi="['system:tb-qyyr-yrzyjpljlb:export']"
        >
          <Icon icon="ep:download" class="mr-5px" /> 导出
        </el-button>
        <el-button  type="primary" plain @click="examine">
          提交审核
        </el-button>
      </el-form-item>
    </el-form>
  </ContentWrap>
  <!-- 列表 -->
  <ContentWrap>
    <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" @selection-change="handleSelectionChange">
      <el-table-column
        label="序号"
        type="index"
        header-align="center"
        align="center"
        width="60px"
        fixed
      />
      <el-table-column type="selection" :selectable="selectable" width="55" />
      <el-table-column label="企业名称" align="center" prop="ttbQyyrQyname" />
      <el-table-column label="余热标识" align="center" prop="ttbQyyrYrbs" />
      <el-table-column label="余热资源类型" align="center" prop="ttbQyyrYrzylx" width="120px"/>
      <el-table-column label="余热资源等级" align="center" prop="ttbQyyrYrzydj" width="120px"/>
      <el-table-column label="可回收余热资源量" align="center" prop="ttbQyyrKhsyrzyl" width="140px"/>
      <el-table-column label="余热资源回收率" align="center" prop="ttbQyyrYrzyhsl" width="140px"/>
      <el-table-column label="余热资源减排量" align="center" prop="ttbQyyrYrzyjpl" width="140px"/>
      <el-table-column label="余热资源回收总量" align="center" prop="ttbQyyrYrzyhszl" width="140px"/>
      <el-table-column label="备注" align="center" prop="ttbQyyrBz" />
      <el-table-column
        label="填报日期"
        align="center"
        prop="createTime"
        :formatter="dateFormatter2"
        width="180px"
      />
      <el-table-column
        label="创建时间"
        align="center"
        prop="createTime"
        :formatter="dateFormatter"
        width="180px"
      />
      <el-table-column label="操作" align="center" width="200px">
        <template #default="scope">
          <el-tag type="primary" v-if = "scope.row.status ==1" >草稿  </el-tag>
          <el-tag type="success" v-if = "scope.row.status ==3" >审批通过</el-tag>
          <el-tag type="danger" v-if = "scope.row.status ==4" >审批驳回</el-tag>
          <el-tag type="warning" v-if = "scope.row.status ==2" >审批中</el-tag>
          <el-button
            link
            type="primary"
            @click="openForm('update', scope.row.id)"
            v-if = "scope.row.status ==1 || scope.row.status ==4"
          >
            编辑
          </el-button>
          <el-button
            link
            type="danger"
            @click="handleDelete(scope.row.id)"
            v-if = "scope.row.status ==1 || scope.row.status ==4"
          >
            删除
          </el-button>
        </template>
      </el-table-column>
    </el-table>
    <!-- 分页 -->
    <Pagination
      :total="total"
      v-model:page="queryParams.pageNo"
      v-model:limit="queryParams.pageSize"
      @pagination="getList"
    />
  </ContentWrap>
  <!-- 表单弹窗:添加/修改 -->
  <TbQyyrYrzyjpljlbForm ref="formRef" @success="getList" />
   <!-- 对象导入对话框 -->
   <ImportForm ref="importFormRef" @success="getList" />
  <Examine ref="examineFormRef" @success="getList" />
</template>
<script setup lang="ts">
import { dateFormatter,dateFormatter2 } from '@/utils/formatTime'
import download from '@/utils/download'
import { TbQyyrYrzyjpljlbApi, TbQyyrYrzyjpljlbVO } from '@/api/system/tbqyyryrzyjpljlb'
import TbQyyrYrzyjpljlbForm from './TbQyyrYrzyjpljlbForm.vue'
import TbQyyrJcdbForm from './TbQyyrJcdbForm.vue'
import { getIntDictOptions, DICT_TYPE, getDictLabel } from '@/utils/dict'
import Examine from './examine.vue'
import ImportForm from './ImportForm.vue'
/** 余热资源减排量记录 列表 */
defineOptions({ name: 'TbQyyrYrzyjpljlb' })
const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化
const loading = ref(true) // 列表的加载中
const list = ref<TbQyyrYrzyjpljlbVO[]>([]) // 列表的数据
const total = ref(0) // 列表的总页数
const queryParams = reactive({
  pageNo: 1,
  pageSize: 10,
  ttbQyyrQyname: undefined,
  ttbQyyrYrbs: undefined,
  ttbQyyrYrzylx: undefined,
  ttbQyyrYrzydj: undefined,
  ttbQyyrKhsyrzyl: undefined,
  ttbQyyrYrzyhsl: undefined,
  ttbQyyrYrzyjpl: undefined,
  ttbQyyrYrzyhszl: undefined,
  ttbQyyrBz: undefined,
  ttbTbDate: [],
  createTime: [],
})
const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // 导出的加载中
/** 查询列表 */
const getList = async () => {
  loading.value = true
  try {
    const data = await TbQyyrYrzyjpljlbApi.getTbQyyrYrzyjpljlbPage(queryParams)
    list.value = data.list
    total.value = data.total
  } finally {
    loading.value = false
  }
}
/** 搜索按钮操作 */
const handleQuery = () => {
  queryParams.pageNo = 1
  getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
  queryFormRef.value.resetFields()
  handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
  formRef.value.open(type, id)
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
  try {
    // 删除的二次确认
    await message.delConfirm()
    // 发起删除
    await TbQyyrYrzyjpljlbApi.deleteTbQyyrYrzyjpljlb(id)
    message.success(t('common.delSuccess'))
    // 刷新列表
    await getList()
  } catch {}
}
/** 导出按钮操作 */
const handleExport = async () => {
  try {
    // 导出的二次确认
    await message.exportConfirm()
    // 发起导出
    exportLoading.value = true
    const data = await TbQyyrYrzyjpljlbApi.exportTbQyyrYrzyjpljlb(queryParams)
    download.excel(data, '余热资源减排量记录.xls')
  } catch {
  } finally {
    exportLoading.value = false
  }
}
const importFormRef = ref()
const handleImport = () => {
  importFormRef.value.open()
}
const selectList = ref<TbQyyrYrzyjpljlbVO[]>([])
const handleSelectionChange = (val: TbQyyrYrzyjpljlbVO[]) => {
  selectList.value = val
}
const examineFormRef = ref()
const examine =() => {
    if(selectList.value.length > 0)
    {
      examineFormRef.value.open(selectList.value)
    }else{
      message.warning('请选择审核的数据')
    }
}
const selectable = (row: TbQyyrYrzyjpljlbVO) => ![2,3].includes(row.status)
/** 初始化 **/
onMounted(() => {
  getList()
})
</script>

image.gif

examine提交页:

<template>
  <Dialog title="提交审核" v-model="dialogVisible" width="60%">
    <el-table :data="selectList2"  >
      <el-table-column
        label="序号"
        type="index"
        header-align="center"
        align="center"
        width="60px"
        fixed
      />
      <el-table-column label="企业名称" align="center" prop="ttbQyyrQyname" />
      <el-table-column label="余热标识" align="center" prop="ttbQyyrYrbs" />
      <el-table-column label="余热资源类型" align="center" prop="ttbQyyrYrzylx" width="120px"/>
      <el-table-column label="余热资源等级" align="center" prop="ttbQyyrYrzydj" width="120px"/>
      <el-table-column label="可回收余热资源量" align="center" prop="ttbQyyrKhsyrzyl" width="140px"/>
      <el-table-column label="余热资源回收率" align="center" prop="ttbQyyrYrzyhsl" width="140px"/>
      <el-table-column label="余热资源减排量" align="center" prop="ttbQyyrYrzyjpl" width="140px"/>
      <el-table-column label="余热资源回收总量" align="center" prop="ttbQyyrYrzyhszl" width="140px"/>
      <el-table-column label="备注" align="center" prop="ttbQyyrBz" />
      <el-table-column
        label="填报日期"
        align="center"
        prop="createTime"
        :formatter="dateFormatter2"
        width="180px"
      />
      <el-table-column
        label="创建时间"
        align="center"
        prop="createTime"
        :formatter="dateFormatter"
        width="180px"
      />
      <el-table-column fixed="right" label="操作" align="center" width="120">
            <template #default="scope">
              <el-button
                @click.prevent="deleteRow(scope.$index, selectList2)"
                type="text"
                size="small"
              >
                删除
              </el-button>
            </template>
          </el-table-column>
  </el-table>
  <template #footer>
      <el-button @click="examine" type="primary" >提交审核</el-button>
      <el-button @click="closeDialong">取 消</el-button>
    </template> 
    </Dialog>
</template>
<script lang="ts" setup>
import { dateFormatter,dateFormatter2 } from '@/utils/formatTime'
import { getIntDictOptions, DICT_TYPE, getDictLabel } from '@/utils/dict'
import { TbQyyrYrzyjpljlbApi, TbQyyrYrzyjpljlbVO } from '@/api/system/tbqyyryrzyjpljlb'
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const dialogVisible = ref(false) // 弹窗的是否展示
const selectList2 = ref<TbQyyrYrzyjpljlbVO[]>([])
/** 打开弹窗 */
const open = async ( selectList: TbQyyrYrzyjpljlbVO[]) => {
  dialogVisible.value = true
  selectList2.value = []
  selectList2.value = selectList
}
const deleteRow = (index, rows) => {
  rows.splice(index, 1)
}
const examine = async () => {
    if(selectList2.value.length > 0)
    {
      await message.confirm( t('你确定要提交审核吗?'))
        await TbQyyrYrzyjpljlbApi.examine(selectList2.value)
        message.success('已提交审核,静等相关人员处理')
        emit('success')
        dialogVisible.value = false
    }else{
      message.warning('请选择审核的数据')
    }
}
const closeDialong = () => {
  dialogVisible.value = false
  emit('success')
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
</script>

image.gif

现在让我们试一下能不能审核成功

image.gif 编辑

选中后提交审核

image.gif 编辑

提交审核成功,状态变为已审核

image.gif 编辑

同时在待办任务里面能看见这一条提交审核记录

image.gif 编辑

点击审批

image.gif 编辑

通过之后,状态变为已通过

image.gif 编辑

这就大功告成了,如果有什么问题,可以和我交流,我的微信是:sxy0802000,谢谢大家浏览。


目录
相关文章
|
1月前
|
JavaScript 前端开发 安全
Vue 3
Vue 3以组合式API、Proxy响应式系统和全面TypeScript支持,重构前端开发范式。性能优化与生态协同并进,兼顾易用性与工程化,引领Web开发迈向高效、可维护的新纪元。(238字)
502 139
|
1月前
|
缓存 JavaScript 算法
Vue 3性能优化
Vue 3 通过 Proxy 和编译优化提升性能,但仍需遵循最佳实践。合理使用 v-if、key、computed,避免深度监听,利用懒加载与虚拟列表,结合打包优化,方可充分发挥其性能优势。(239字)
213 1
|
6月前
|
缓存 JavaScript PHP
斩获开发者口碑!SnowAdmin:基于 Vue3 的高颜值后台管理系统,3 步极速上手!
SnowAdmin 是一款基于 Vue3/TypeScript/Arco Design 的开源后台管理框架,以“清新优雅、开箱即用”为核心设计理念。提供角色权限精细化管理、多主题与暗黑模式切换、动态路由与页面缓存等功能,支持代码规范自动化校验及丰富组件库。通过模块化设计与前沿技术栈(Vite5/Pinia),显著提升开发效率,适合团队协作与长期维护。项目地址:[GitHub](https://github.com/WANG-Fan0912/SnowAdmin)。
948 5
|
2月前
|
开发工具 iOS开发 MacOS
基于Vite7.1+Vue3+Pinia3+ArcoDesign网页版webos后台模板
最新版研发vite7+vue3.5+pinia3+arco-design仿macos/windows风格网页版OS系统Vite-Vue3-WebOS。
374 11
|
1月前
|
JavaScript 安全
vue3使用ts传参教程
Vue 3结合TypeScript实现组件传参,提升类型安全与开发效率。涵盖Props、Emits、v-model双向绑定及useAttrs透传属性,建议明确声明类型,保障代码质量。
253 0
|
3月前
|
缓存 前端开发 大数据
虚拟列表在Vue3中的具体应用场景有哪些?
虚拟列表在 Vue3 中通过仅渲染可视区域内容,显著提升大数据列表性能,适用于 ERP 表格、聊天界面、社交媒体、阅读器、日历及树形结构等场景,结合 `vue-virtual-scroller` 等工具可实现高效滚动与交互体验。
429 1
|
3月前
|
缓存 JavaScript UED
除了循环引用,Vue3还有哪些常见的性能优化技巧?
除了循环引用,Vue3还有哪些常见的性能优化技巧?
245 0
|
4月前
|
JavaScript
vue3循环引用自已实现
当渲染大量数据列表时,使用虚拟列表只渲染可视区域的内容,显著减少 DOM 节点数量。
136 0
|
5月前
|
JavaScript API 开发者
Vue框架中常见指令的应用概述。
通过以上的详细解析,你应该已经初窥Vue.js的指令的威力了。它们是Vue声明式编程模型的核心之一,无论是构建简单的静态网站还是复杂的单页面应用,你都会经常用到。记住,尽管Vue提供了大量预定义的指令,你还可以创建自定义指令以满足特定的需求。为你的Vue应用程序加上这些功能增强器,让编码变得更轻松、更愉快吧!
121 1
|
6月前
|
JavaScript API 容器
Vue 3 中的 nextTick 使用详解与实战案例
Vue 3 中的 nextTick 使用详解与实战案例 在 Vue 3 的日常开发中,我们经常需要在数据变化后等待 DOM 更新完成再执行某些操作。此时,nextTick 就成了一个不可或缺的工具。本文将介绍 nextTick 的基本用法,并通过三个实战案例,展示它在表单验证、弹窗动画、自动聚焦等场景中的实际应用。
590 17