缘起,有个大数据量的Select需要改造,同时需要添加编辑数据,说不清楚了,直接来吧!涉及大数据量Select改造,动态ref

  • Select组件
  • 动态ref
Select组件
<template>
  <a-select
    v-model:value="seletedValue"
    style="width: 281px; margin-left: 8px;"
    :options="options"
    :fieldNames="{label: 'tagName', value: 'tagId', options: options}"
    @popupScroll="popupScroll"
  >
  </a-select>
</template>

<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import { getTags } from '@/server/request'

const seletedValue = ref('')
let oriData = ref([])
// 默认渲染行数
let index = 10
const options = ref([])
const exitOptions = ref<string[]>([])

onMounted(async () => {
  await changeTagsOptions()
  options.value = []
  options.value = oriData.value.slice(0, index).filter((o: { tagId: string; }) => o.tagId !== seletedValue.value)
})

const popupScroll = (e:any) => {
  const { scrollHeight, scrollTop, clientHeight } = e.target;
  if (scrollHeight - scrollTop === clientHeight) {
    const tmp = oriData.value.slice(index, index+10)
    tmp.forEach((t: { tagId: string; }) => {
      if (!exitOptions.value.includes(t.tagId)) {
        options.value.push(t)
      }
    })
    index += 10
  }
}

const changeTagsOptions = async () => {
  const params = {
    paging: {
      current: 1,
      pageSize: 100000,
    },
    dialect: {
      displayUserCount: false
    }
  }
  const res = await getTags(params)
  if (res.errorCode === 0) {
    oriData.value = res.data.result
  }
}

const returnCurrentData = () => {
  return seletedValue.value
}
const loadPage = async (val: string) => {
  seletedValue.value = val
  await changeTagsOptions()
  options.value = []
  options.value = oriData.value.slice(0, index).filter((o: { tagId: string; }) => o.tagId !== seletedValue.value)
  const find = oriData.value.find((d: { tagId: string; }) => d.tagId === val)
  if (find) {
    options.value.unshift(find)
    exitOptions.value.push(val)
  }

  options.value.forEach((t: { tagId: string; }) => {
    exitOptions.value.push(t.tagId)
  })
}
defineExpose({
  returnCurrentData,
  loadPage
})
</script>

组件:
通过loadPage接收调用方传递的数据,通过returnCurrentData返回编辑好的数据。

​ 组件通过接口获取数据,但默认只加载前10条,并将需要回显的数据放在数据的最上层。

​ 滚动下拉选项时,到底部会继续加载后续数据,每次加载10

动态ref

效果如下:
在这里插入图片描述

下面是从项目中摘的代码:

<template>
  <div style="border: 1px solid #d9d9d9;padding: 4px;margin-top:48px">
    <template v-for="(cond, condIndex) in participationConditions">
      <a-row>
        <a-col :span="2" class="item-label" style="line-height:32px">
          {{ $t('departmentConditions') }}:</a-col>
        <a-col :span="22">
          <a-input :value="computeValue(condIndex, 'tags')" style="width:99%; margin: 4px 8px;" disabled />
        </a-col>
      </a-row>
      <div style="text-align: right;">
        <a-button style="margin: 0 4px" size="small" type="primary" ghost @click="editConditions(condIndex)">{{ $t('btnEdit') }}</a-button>
        <a-button style="margin: 0 4px" danger ghost size="small" @click="removeConditions(condIndex)">{{ $t('btnDelete') }}</a-button>
      </div>
      <a-divider style="margin:12px 0" />
    </template>
    <a-row>
      <a-col :span="2" class="item-label">
        <span class="required-icon">*</span>
        {{ $t('departmentConditions') }}:</a-col>
      <a-col :span="22">
        <bigSelect v-for="(item, sDepartIndex) in selectedTags" :val="item" :ref="setItemRef"></bigSelect>
        <plus-circle-two-tone :style="{fontSize: '18px',margin: '0 20px 0 10px'}" @click="addItem('department')" />
      </a-col>
    </a-row>
    <div style="text-align: right;">
      <a-button class="add-btn" type="primary" size="small" ghost :disabled="selectedTags.length === 0" @click="addConditions()">{{ $t('btnConfirm') }}</a-button>
    </div>
  </div>
</template>

<script lang="ts" setup>
import { PlusCircleTwoTone } from '@ant-design/icons-vue';
import { ref, onBeforeUpdate, nextTick, onMounted } from 'vue'
import bigSelect from './components/big-select.vue'
import { Obj } from '@/type'
import { message } from 'ant-design-vue';
import { useI18n } from 'vue-i18n'
import { getTags } from '@/server/request'
const { t } = useI18n()

const itemRefs = ref([])
const setItemRef = (el: any) => {
  if (el) {
    itemRefs.value.push(el)
  }
}
onBeforeUpdate(() => {
  itemRefs.value = []
})

onMounted(() => {
  changeTagsOptions()
})

const tagLists = ref<Obj[]>([])
const changeTagsOptions = async () => {
  const params = {
    paging: {
      current: 1,
      pageSize: 100000,
    },
    dialect: {
      displayUserCount: false
    }
  }
  const res = await getTags(params)
  if (res.errorCode === 0) {
    tagLists.value = res.data.result
  }
}

const selectedTags = ref<string[]>([''])
const addItem = (type: string) => {
  if (type === 'department') {
    selectedTags.value.push('')
  }
}

const participationConditions = ref<Obj[]>([])
const addConditions = () => {
  let tmpTag = []
  for (let i=0; i<itemRefs.value.length; i++) {
    tmpTag.push(itemRefs.value[i].returnCurrentData())
  }
  selectedTags.value = Array.from(new Set(tmpTag))
  let tmp = {
    tags: [] as Obj[],
    assets: [] as Obj[]
  }
  let validateFlag = true
  selectedTags.value.forEach((sd:string, index: number) => {
    const find = tagLists.value.find((t) => t.tagId === sd)
    if (find) {
      tmp.tags.push({
        value: find['tagId'],
        label: find['tagName'],
      })
    } else {
      validateFlag = false
    }
  })

  if (!validateFlag) {
    message.warning(t('configurationConfirm'))
    return
  }
  if (editIndex === -1) {
    participationConditions.value.push(tmp)
  } else {
    participationConditions.value[editIndex] = tmp
  }
  selectedTags.value = ['']

  editIndex = -1
  for (let i=0; i<itemRefs.value.length; i++) {
    itemRefs.value[i].loadPage('')
  }
}

let editIndex = -1
const editConditions = (condIndex: number) => {
  const tmp = participationConditions.value[condIndex].tags.map((p: { value: string; }) => p.value)
  selectedTags.value = tmp
  nextTick(() => {
    for (let i=0; i<itemRefs.value.length; i++) {
      itemRefs.value[i].loadPage(selectedTags.value[i])
    }
  })
  editIndex = condIndex
}
const removeConditions = (condIndex: number) => {
  participationConditions.value.splice(condIndex, 1)
}

const computeValue = (condIndex: number, type: string) => {
  if (type === 'tags') {
    return participationConditions.value[condIndex][type].map((p: { label: string; }) => p.label).join(' and ')
  }
}
</script>

<style scoped lang="scss">
.required-icon {
  color:red;
  font-size: 14px;
}

.item-label {
  text-align: right;
  // line-height: 40px;
}
.add-btn {
  margin: 4px;
}
</style>

逻辑不用关心,关注下itemRefs相关内容。

写不下去了,就这样吧!

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐