Skip to content

vue性能优化

1. 单一页面里组件过多导致页面访问过慢!!!

在初次进入页面时,只加载必要的组件,其他组件在需要时才进行加载。

思路: 通过import()动态引入组件,

vue
<template>
  <ContentWrap> 
    <!-- 搜索工作栏 -->
    <el-form
      ref="queryFormRef"
      :inline="true"
      :model="queryParams"
      class="-mb-15px"
      label-width="68px"
    >
        <el-form-item label="任务名称" prop="name">
        <el-select
          v-model="queryParams.name"
          placeholder="请选择任务名称"
          clearable
          filterable
          class="!w-210px"
        >
          <el-option
            v-for="dict in getStrDictOptions(DICT_TYPE.ERP_TASK_NAME_TYPE)"
            :key="dict.value"
            :label="dict.label"
            :value="dict.value"
          />
        </el-select>
      </el-form-item>
      <el-form-item label="任务描述" prop="description">
        <el-input v-model="queryParams.description" placeholder="请输入任务描述" />
      </el-form-item>
        <el-form-item label="执行人" prop="ownerUserId">
        <el-select
          v-model="queryParams.ownerUserId"
          filterable
          clearable
          class="!w-210px">
          <el-option
            v-for="item in userOptions"
            :key="item.id"
            :label="item.nickname"
            :value="item.id"
          />
        </el-select>
      </el-form-item>
        <el-form-item label="打卡时间" prop="clockInTime">
        <el-date-picker
              v-model="queryParams.clockInTime"
              type="date"
              value-format="YYYY-MM-DD"
              placeholder="请选择打卡时间"
              class="!w-210px"
            />
      </el-form-item>
        <el-form-item>
        <el-button @click="handleQuery">
          <Icon class="mr-5px" icon="ep:search" />
          搜索
        </el-button>
        <el-button @click="resetQuery">
          <Icon class="mr-5px" icon="ep:refresh" />
          重置
        </el-button>
      </el-form-item>
    </el-form>
  </ContentWrap>

  <!-- 列表 -->
  <ContentWrap>
    <el-table :header-cell-style="$tableHeaderCellStyle()" v-loading="loading" :data="list" :border="true">
      <el-table-column label="序号" align="center" type="index" width="100" />
      <el-table-column align="center" label="任务类型" prop="category" width="180" sortable>
        <template #default="{ row }">
            <dict-tag
              v-if="row.category"
              :type="DICT_TYPE.TODO_TASK_TYPE"
              :value="row.category"
            />
            <div v-else>{{  '-'  }}</div>
          </template>
      </el-table-column>
      <el-table-column label="任务描述" align="center" prop="description" sortable>
        <template #default="scope">
          <div v-if="isFormattedDescription(scope.row.description)">
            【{{ getDescriptionPart(scope.row.description, 0) }}】-
            <dict-tag
              :type="DICT_TYPE.CRM_SERVICE_TYPE"
              :value="getDescriptionPart(scope.row.description, 1)"
            />
            -
            <dict-tag
              :type="DICT_TYPE.CRM_VISIT_PURPOSE"
              :value="getDescriptionPart(scope.row.description, 2)"
            />
          </div>
          <div v-else>
            {{ scope.row.description?scope.row.description:'-' }}
          </div>
        </template>
      </el-table-column>
      <el-table-column
        align="center"
        label="任务名称"
        prop="name"
        width="150"
        sortable
      >
        <template #default="{ row }">
          <dict-tag
              :type="DICT_TYPE.ERP_TASK_NAME_TYPE"
              :value="row.name"
            />
        </template>
      </el-table-column>
      <el-table-column
        align="center"
        label="执行人"
        prop="processInstance.startUser.nickname"
        width="100"
        sortable
      />
      <el-table-column
        :formatter="dateFormatter"
        align="center"
        label="任务创建时间"
        prop="createTime"
        sortable
      />
      <el-table-column align="center" label="操作" fixed="right" width="120">
        <template #default="scope">
          <el-button link type="primary" @click="handleAudit(scope.row,'check')">处理</el-button>
          <el-button link @click="handleAudit(scope.row,'view')">详情</el-button>
        </template>
      </el-table-column>
    </el-table>
    <!-- 分页 -->
    <Pagination
      v-model:limit="queryParams.pageSize"
      v-model:page="queryParams.pageNo"
      :total="total"
      @pagination="getList"
    />
  </ContentWrap>
    <component
      :is="asyncComponent"
      ref="componentRef"
      @success="getList"
    />
  
  <!-- 对话框 -->
  <el-dialog
    v-model="dialogVisible"
    :title="dialogTile"
    width="70%"
    @close="resetForm"
  >
    <el-form :model="formData" ref="formRef">
      <el-button type="primary" @click="handleOut" v-if="isStockOut" :disabled="formType == 'view'" style="margin-bottom: 10px;">出库</el-button>
      <el-button type="primary" @click="handleIn" v-if="!isStockOut" :disabled="formType == 'view'" style="margin-bottom: 10px;">入库</el-button>
      <div class="form-section">
        <h4 class="section-title">申请的备件信息</h4>
        <productApplyForm ref="productApplyFormRef" v-model="formData.productApplyData" :is-task="true" />
      </div>
    </el-form>
    <template #footer>
      <el-button @click="dialogVisible = false">关闭</el-button>
    </template>
  </el-dialog>
</template>

<script lang="ts" setup>
import { dateFormatter } from '@/utils/formatTime'
import * as TaskApi from '@/api/bpm/task'
import { CategoryApi, CategoryVO } from '@/api/bpm/category'
import { FaultWarrantyApi } from '@/api/erp/faultwarranty'
import { getStrDictOptions, DICT_TYPE } from '@/utils/dict'
import * as UserApi from '@/api/system/user'
import { StockApi } from '@/api/erp/stock/stock'
import { ProductApi } from '@/api/erp/product/product'
import { ref, reactive, onMounted, onActivated } from 'vue'
import { useRouter } from 'vue-router'
import productApplyForm from '@/views/erp/order/workorder/productApplyForm.vue'
const asyncComponent = ref(null)
// 当前执行node命令时文件夹的地址(工作目录)

defineOptions({ name: 'BpmTodoTask' })

const { push } = useRouter()
const userOptions = ref<UserApi.UserVO[]>([])
const loading = ref(true)
const total = ref(0)
const list = ref([])
const queryParams = reactive({
  pageNo: 1,
  pageSize: 10,
  description: "",
  name: '',
  category: undefined,
  ownerUserId: undefined,
  clockInTime: undefined,
  createTime: []
})

const queryFormRef = ref()
const productApplyFormRef = ref()
const categoryList = ref<CategoryVO[]>([])
const dialogVisible = ref(false)
const formData = reactive({
  type: undefined,
  stockDOS: [],
  productApplyData: []
})
const formRef = ref(null)
const dialogTile = ref()
const taskId = ref()
const temporaryNumber = ref()
const formType = ref()
const isStockOut = ref(false)
const componentRef = ref(null)

const getCurrentComponent = async (url: string) => {
  try{
    const module = await import(url);
    asyncComponent.value = module.default;
    await nextTick()
  } catch(e){
    console.log(e)
  }
}

const handleOut = async () => {
  const module = await import('@/views/erp/stock/stock/components/StockOutForm.vue')
  asyncComponent.value = module.default; 
  await nextTick()
  componentRef.value?.open(null, taskId.value,temporaryNumber.value,formData.productApplyData)
}

const handleIn = async () => {
  const module = await import('@/views/erp/stock/stock/components/StockInForm.vue')
  asyncComponent.value = module.default; 
  await nextTick()
  componentRef.value?.open(null, taskId.value,temporaryNumber.value, formData.productApplyData)
}

const hanldeStockout = () => {
  dialogVisible.value = false
  getList()
}

const resetForm = () => {
  if (formRef.value) {
    formRef.value?.resetFields()
  }
}

const getList = async () => {
  loading.value = true
  try {
    const data = await TaskApi.getTaskTodoPage(queryParams)
    list.value = data.list
    total.value = data.total
  } finally {
    loading.value = false
  }
}

const loadUsers = async () => {
  try {
    userOptions.value = await UserApi.getSimpleUserList()
  } finally {
  }
}

const handleQuery = () => {
  queryParams.pageNo = 1
  getList()
}

const isFormattedDescription = (desc) => {
  return desc?.split('-').length === 3
}

const getDescriptionPart = (desc, index) => {
  return desc.split('-')[index] || ''
}

const resetQuery = () => {
  queryFormRef.value.resetFields()
  handleQuery()
}

const handleAudit = async (row: any, type: any) => {
  taskId.value = row.id
  if(row.name == "1" || row.name == '17') {
    const module = await import('@/views/crm/visitrecord/VisitRecordForm.vue')
    asyncComponent.value = module.default;
    await nextTick()
    if(type == 'view') {
        componentRef.value?.open('view', row.parentTaskId, true, true, null, row.id, +row.parentTaskId,null,null,row.name)
    } else {
        componentRef.value?.open('update', row.parentTaskId, true, false, row.id, +row.parentTaskId,null,null,row.name)
    }
  }
  
  if(row.name == '2' || row.name == '3') {
    const module = await import('@/views/crm/visitCheck/VisitCheckForm.vue')
    asyncComponent.value = module.default; 
    await nextTick()
    if(type == 'view') {
      componentRef.value?.open('view', row.parentTaskId, true, true, row.id, +row.parentTaskId,null,null,row.name)
    } else {
      componentRef.value?.open('update', row.parentTaskId, true, false, row.id, +row.parentTaskId,null,null,row.name)
    }
  }

  if(row.name == "4") {
    if(type == 'view') {
      const module = await import('@/views/project/order/faultwarranty/FaultWarrantyForm.vue')
      asyncComponent.value = module.default; 
      await nextTick()
      componentRef.value?.open('view', row.parentTaskId)
    } else {
      const module = await import('@/views/project/order/faultwarranty/components/FaultCheckForm.vue')
      asyncComponent.value = module.default; 
      await nextTick()
      let deviceList = []
      try {
        const data = await FaultWarrantyApi.getFaultWarranty(+row.parentTaskId)
        deviceList = data.faultWarrantyDevice
        componentRef.value?.open('update', row, row.parentTaskId, false, true, null, null, deviceList, data)
      } catch(e) {
        console.error(e)
      }
    }
  }

  if(row.name == "8") {
    if(type == 'view') {
      const module = await import('@/views/erp/order/workorder/RepairWorkOrderView.vue')
      asyncComponent.value = module.default; 
      await nextTick()
      componentRef.value?.open('view', row.parentTaskId, null)
    } else {
      const module = await import('@/views/erp/order/workorder/RepairWorkOrderForm.vue')
      asyncComponent.value = module.default; 
      await nextTick()
      componentRef.value?.open('update', row.parentTaskId, null, null, row.id)
    }
  }
  
  if(row.name == "5") {
    if(type == 'view') {
      const module = await import('@/views/erp/order/workorder/RepairWorkOrderView.vue')
      asyncComponent.value = module.default; 
      await nextTick()
      componentRef.value?.open('view', row.parentTaskId, null, null, row.id)
    } else {
      const module = await import('@/views/erp/order/workorder/RepairWorkOrderForm.vue')
      asyncComponent.value = module.default; 
      await nextTick()
      componentRef.value?.open('update', row.parentTaskId, null, null, row.id)
    }
  }

  if(row.name == "9") {
    const module = await import('@/views/erp/outsourcing/outWorkorder/components/DetectionInformationForm.vue')
    asyncComponent.value = module.default; 
    await nextTick()
    if(type == 'view') {
      componentRef.value?.open('view', row.parentTaskId, row.id)
    } else {
      componentRef.value?.open('update', row.parentTaskId, row.id)
    }
  }

  if(row.name == "23") {
    const module = await import('@/views/erp/outsourcing/outWorkorder/components/Apply.vue')
    asyncComponent.value = module.default; 
    await nextTick()
    if(type == 'view') {
      componentRef.value?.open('view', row.parentTaskId, row.id)
    } else {
      componentRef.value?.open('create', row.parentTaskId, row.id)
    }
  }

  if(row.name == "16") {
    dialogTile.value = '入库'
    isStockOut.value = false
    const id = row.parentTaskId
    if(type == 'view') {
      dialogVisible.value = true
      formType.value = type
      let res = await StockApi.getStockTemporary({temporaryNumber: id, pageNo: 1, pageSize: 100})
      formData.productApplyData = res.list
    } else {
      formType.value = type
      dialogVisible.value = true
      formData.productApplyData = []
      temporaryNumber.value = id
      try {
        let res = await StockApi.getStockTemporary({temporaryNumber: id, pageNo: 1, pageSize: 100})
        formData.productApplyData = res.list
        if(res.list[0]?.productId) {
          Object.assign(formData, res.list)
          const list = res.list.map(item => ({
            ...item,
            stockQuantity: item.productQuantity
          }))
          formData.stockDOS = list
          formData.direction = formData.stockDOS[0]?.direction
          formData.temporaryNumber = id
        }
      } catch(e) {
        console.error(e)
      }
    }
  }

  if(row.name == '15') {
    dialogTile.value = '出库'
    isStockOut.value = true
    formData.productApplyData = []
    if(type == 'view') {
      formType.value = type
      const id = row.parentTaskId
      dialogVisible.value = true
      let res = await StockApi.getStockTemporary({temporaryNumber: id, pageNo: 1, pageSize: 100})
      formData.productApplyData = res.list
    } else {
      formType.value = type
      dialogVisible.value = true
      const id = row.parentTaskId
      temporaryNumber.value = id
      try {
        let res = await StockApi.getStockTemporary({temporaryNumber: id, pageNo: 1, pageSize: 100})
        formData.productApplyData = res.list
        if(res.list[0]?.productId) {
          Object.assign(formData, res.list)
          const list = res.list.map(item => ({
            ...item,
            stockQuantity: item.productQuantity
          }))
          formData.stockDOS = list
          formData.direction = formData.stockDOS[0]?.direction
          formData.temporaryNumber = id
        } else {
          const categoryIdList = res.list.map(item => item.categoryId)
          if(categoryIdList?.length > 0) {
            const data = await ProductApi.getByCategory({categoryIds: categoryIdList.join(','), pageNo: 1, pageSize: 100})
            const productList = data.list.map(item => ({
              productId: item.id,
              productName: item.name,
              stockQuantity: item.productQuantity,
              ...item
            }))
            formData.stockDOS = productList
            formData.direction = res.list[0]?.direction
            formData.temporaryNumber = id
          }
        }
      } catch(e) {
        console.error(e)
      }
    }
  }

  if(row.name == "12") {
    dialogTile.value = '委外信息完善'
    const module = await import('@/views/erp/outsourcing/outWorkorder/components/TrackingNumber.vue')
    asyncComponent.value = module.default; 
    await nextTick()
    if(type == 'view') {
      componentRef.value?.open('view', +row.parentTaskId, taskId.value)
    } else {
      componentRef.value?.open('update', +row.parentTaskId, taskId.value)
    }
  }

  if(row.name == "13") {
    
    const module = await import('@/views/project/initiation/components/projectPayTodo.vue')
    asyncComponent.value = module.default; 
    await nextTick()
    dialogTile.value = '费用支付'
    if(type == 'view') {
      componentRef.value?.open('view', row.parentTaskId, null, row.id)
    } else {
      componentRef.value?.open('update', +row.parentTaskId, null, row.id)
    }
  }

  if(row.category == "4") {
    if(type == 'check') {
      push({
        name: 'BpmProcessInstanceDetail',
        query: {
          id: row.processInstanceId,
          processInstanceId: row.processInstanceId.split('-')[1],
          name: row.name
        }
      })
    } else {
      if(row.name == 100) {
        const module = await import('@/views/crm/customerfirst/CustomerForm.vue')
        asyncComponent.value = module.default; 
        await nextTick()
        componentRef.value?.open('view', row.processInstanceId.split('-')[1])
      }
      if(row.name == 101) {
        const module = await import('@/views/erp/purchase/supplier/SupplierForm.vue')
        asyncComponent.value = module.default; 
        await nextTick()
        componentRef.value?.open('view', row.processInstanceId.split('-')[1])
      }
      if(row.name == 200) {
        const module = await import('@/views/crm/visitCheck/VisitCheckForm.vue')
        asyncComponent.value = module.default; 
        await nextTick()
        componentRef.value?.open('view', row.processInstanceId.split('-')[1])
      }
      if(row.name == 300) {
        const module = await import('@/views/crm/visitCheck/VisitCheckForm.vue')
        asyncComponent.value = module.default; 
        await nextTick()
        componentRef.value?.open('view', row.processInstanceId.split('-')[1])
      }
      if(row.name == 301) {
        const module = await import('@/views/crm/business/components/BussinessProject.vue')
        asyncComponent.value = module.default; 
        await nextTick()
        componentRef.value?.open('view', row.processInstanceId.split('-')[1])
      }
      if(row.name == 400) {
        const module = await import('@/views/erp/outsourcing/outWorkorder/OutsourcingWorkOrderForm.vue')
        asyncComponent.value = module.default; 
        await nextTick()
        componentRef.value?.open('view', row.processInstanceId.split('-')[1])
      }
      if(row.name == 401) {
        const module = await import('@/views/erp/lending/out/BorrowRecordsForm.vue')
        asyncComponent.value = module.default; 
        await nextTick()
        componentRef.value?.open('view', row.processInstanceId.split('-')[1])
      }
      if(row.name == 102) {
        const module = await import('@/views/erp/device/ib/DeviceViewForm.vue')
        asyncComponent.value = module.default; 
        await nextTick()
        componentRef.value.open('view',row.processInstanceId.split('-')[1],null,null,'Todo')
      }
    }
  }
}

onMounted(async () => {
  await getList()
  await loadUsers()
  categoryList.value = await CategoryApi.getCategorySimpleList()
})

onActivated(async () => {
  await getList()
})
</script>

<style scoped>
.form-section {
  padding: 16px;
  margin-bottom: 24px;
  background-color: #fff;
  border: 1px solid #ebeef5;
  border-radius: 4px;
}

.section-title {
  padding: 12px 16px;
  margin: -16px -16px 16px;
  font-size: 14px;
  font-weight: 500;
  color: #303133;
  background-color: #f5f7fa;
  border-bottom: 1px solid #ebeef5;
}

.el-radio {
  margin-right: 15px;
}
</style>