✨✨vue3自定义动态不同UI组件筛选框案例✨

news2025/1/5 18:30:35

✨1. 实现功能

  1. 🌟添加条件进行数据筛选
    • 根据筛选数据条件不同,显示不同的UI组件:包含datetimeselectinput
    • 筛选完条件可继续添加与取消条件
    • 当然可以在条件列表中进行直接删除,当删除完所有条件之后,回到添加条件页面
    • 清空搜索条件直接回到添加条件页面

✨2. 效果图

  1. 💖打开筛选条件弹窗💖
    111.gif

  2. 💖💖查看筛选条件不同的UI结构效果预览💖💖
    222.gif

  3. 💖💖💖继续添加和取消筛选条件效果💖💖💖
    3333.gif

  4. 💖💖条件列表中直接删除条件效果💖💖
    444.gif

  5. 💖清空搜索条件效果💖
    555.gif

✨3. 组件代码

🌹1. 组件目录结构

image.png

2. 🍗 🍖添加条件页面
<script setup lang="ts">
import { ref } from 'vue'
import { NavData } from './constant'
import AddedCondition from './components/added-condition/index.vue'

defineOptions({
  name: 'SearchClues',
})
const { token } = useAntdToken()

// 是否有搜索添加,判断显示筛选区域
const isNav = ref(true)

// 添加条件弹窗
const AddedConditionRef = ref()
function handleClick(i: number) {
  if (i === 0) {
    AddedConditionRef.value.handleOpen()
    return
  }
  if (i === 1) {
    console.log(1)
    return
  }
  if (i === 2) {
    console.log(2)
  }
}

// 筛选条件判断
function handleIsPanel(v: boolean) {
  isNav.value = !v
  if (isNav.value && !v) {
    isSearch.value = false
  }
}

function handleSearch(list: any) {
  console.log(list, 'handleSearch')
  isSearch.value = true
}

// 是否搜索
const isSearch = ref<boolean>(false)
</script>

<template>
  <div>
    <a-card>
      <div v-if="isNav" class="flex">
        <div v-for="(nav, i) in NavData" :key="i" class="flex-center cursor-pointer m-ie-8" @click="handleClick(nav.i)">
          <div class="flex-center p-2 b-rd-10 m-ie-2" :style="{ 'background-color': token?.colorPrimary }">
            <img :src="nav.url" width="24" height="24" alt="">
          </div>
          <span>{{ nav.title }}</span>
        </div>
      </div>
      <AddedCondition ref="AddedConditionRef" @handle-is-panel="handleIsPanel" @handle-search="handleSearch" />
    </a-card>
    <a-card class="m-t-5">
      <div>
        <h2>三步完成线索查找</h2>
        <div>待完成</div>
      </div>
    </a-card>
  </div>
</template>

<style lang="less" scoped></style>
🍀3. 选择条件-条件列表added-condition/index.vue 🍀
<script setup lang="ts">
import { createVNode, ref } from 'vue'
import { DownOutlined, ExclamationCircleOutlined, MinusCircleOutlined, PlusOutlined, UpOutlined } from '@ant-design/icons-vue'
import { Modal, message } from 'ant-design-vue'
import { JudgementCondition } from '../../constant'
import SelectCondition from './select-condition.vue'
import type { Condition } from '@/api/interface/getting-clues'

defineOptions({
  name: 'AddedCondition',
})

const emit = defineEmits(['handleIsPanel', 'handleSearch', 'handleSave', 'handleClear'])
const selectionConditionRef = ref()
function handleOpen() {
  selectionConditionRef.value.handleOpen()
}

// 是否有搜索添加,判断显示筛选区域
const isPanel = ref(false)

function handleOk(list: Condition.ConditionItem[]) {
  conditionsList.value = list
  isPanel.value = true
  emit('handleIsPanel', isPanel.value)
}

/**
 * @constant searchGeneralType 搜索条件总类型
 * @constant conditionsList 搜索条件列表
 */
const searchGeneralType = ref('0')
const conditionsList = ref<Condition.ConditionItem[]>([])

// 获取下拉框数据
function getDefaultOptions(condition: Condition.ConditionItem) {
  if (condition.type === 'SelectOption') {
    return condition.keys?.map(item => (
      JudgementCondition[item.key][item.value] || JudgementCondition.renderData.ifData
    ))[0]
  }
  else {
    return JudgementCondition.type.operateState
  }
}

// 获取多选条件下拉框数据
function getKeysOptions() {
  return JudgementCondition.default.default
}

function handleDelete(index: number) {
  conditionsList.value.splice(index, 1)
}

watch(() => conditionsList, (n) => {
  if (n.value.length === 0) {
    isPanel.value = false
    emit('handleIsPanel', isPanel.value)
  }
}, { deep: true })

// 筛选
function handleSearch() {
  emit('handleSearch', conditionsList.value)
}
// 保存
const loading = ref<boolean>(false)
function handleSave() {
  Modal.confirm({
    title: '确认',
    icon: createVNode(ExclamationCircleOutlined),
    content: '确定要保存此筛选条件吗?',
    onOk() {
      loading.value = true
      emit('handleSave', conditionsList.value)
      setTimeout(() => {
        message.success('保存成功')
        loading.value = false
      }, 1000)
    },
    onCancel() {
      console.log('Cancel')
    },
  })
}
// 清空
function handleClear() {
  Modal.confirm({
    title: '确认',
    icon: createVNode(ExclamationCircleOutlined),
    content: '确定要清空筛选条件吗?',
    onOk() {
      conditionsList.value = []
      emit('handleClear', conditionsList.value)
      selectionConditionRef.value.reset()
    },
    onCancel() {
      console.log('Cancel')
    },
  })
}

// 展开折叠
const isExpanded = ref<boolean>(true)
function handleCollapse() {
  isExpanded.value = !isExpanded.value
}

defineExpose({
  handleOpen,
  handleSearch,
  handleSave,
  handleClear,
})
</script>

<template>
  <div>
    <div v-if="isPanel">
      <div class="m-be-5">
        <span class="w-16 inline-block">满足下列</span>
        <a-select v-model:value="searchGeneralType" class="m-inline-2.5" style="width: 120px">
          <a-select-option value="0">
            所有
          </a-select-option>
          <a-select-option value="1">
            任一
          </a-select-option>
        </a-select>
        条件:
      </div>
      <div class="expandable-content" :class="{ expanded: isExpanded }">
        <div
          v-for="(condition, index) in conditionsList" :key="condition.value"
          class="flex p-be-5 p-is-18.5 condition-box"
        >
          <a-input v-model:value="condition.label" disabled class="w-50" />
          <template v-if="condition.type === 'DatePicker'">
            <a-range-picker v-model:value="condition.keysDate" class="m-inline-2.5" />
          </template>
          <template v-else-if="condition.type === 'NumberRange'">
            <a-input-number id="inputNumber" v-model:value="condition.keys[0].isSys" class="m-inline-2.5" />
          </template>
          <template v-else-if="condition.type === 'SelectOption'">
            <div v-for="(keyItem, i) in condition.keys" :key="keyItem.key + i">
              <a-select v-model:value="keyItem.isSys" class="m-inline-2.5" style="width: 180px">
                <a-select-option v-for="item in getDefaultOptions(condition)" :key="item.value" :value="item.value">
                  {{ item.label }}
                </a-select-option>
              </a-select>
            </div>
          </template>
          <template v-else>
            <a-select v-model:value="condition.viewfilter" class="m-inline-2.5" style="width: 180px">
              <a-select-option v-for="item in getDefaultOptions(condition)" :key="item.value" :value="item.value">
                {{ item.label }}
              </a-select-option>
            </a-select>
            <!-- TODO 这里是一个输入框,点击出弹窗选数据 -->
            <a-select v-model:value="condition.keysMul" class="m-inline-2.5" style="width: 180px" mode="multiple">
              <a-select-option v-for="item in getKeysOptions()" :key="item.value" :value="item.value">
                {{ item.label }}
              </a-select-option>
            </a-select>
          </template>
          <MinusCircleOutlined style="color: #888" @click="handleDelete(index)" />
        </div>
      </div>
      <a-button @click="selectionConditionRef.handleOpen">
        <template #icon>
          <PlusOutlined />
        </template>
        添加条件
      </a-button>
      <div class="flex justify-between m-t-5">
        <div>
          <a-button class="m-ie-6" type="primary" @click="handleSearch">
            筛选
          </a-button>
          <a-button :loading="loading" @click="handleSave">
            保存筛选条件
          </a-button>
        </div>
        <a-button class="float-right" type="link" @click="handleClear">
          清空
        </a-button>
      </div>
      <a-divider>
        <a-button type="link" @click="handleCollapse">
          {{ isExpanded ? '收起' : '展开' }}
          <component :is="isExpanded ? UpOutlined : DownOutlined" />
        </a-button>
      </a-divider>
    </div>
    <SelectCondition ref="selectionConditionRef" @handle-ok="handleOk" />
  </div>
</template>

<style lang="less" scoped>
.condition-box {
  position: relative;

  &::before {
    content: " ";
    position: absolute;
    left: 0;
    top: -4px;
    height: 100%;
    width: 55px;
    background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADcAAAAoAQMAAABglfBLAAAABlBMVEXM09t2YyHaAAAAAXRSTlMAQObYZgAAABNJREFUGNNjYGBgcGCAgFGadBoAQZoKAavfri4AAAAASUVORK5CYII=) repeat;
  }

  &:last-child::before {
    top: -4px;
    background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADcAAAAoCAYAAABaW2IIAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAACfSURBVHgB7dOxDcJQDIThs0lBQcEIRBQoBRIbEiYBNoEJXoFQaCBikBfDCFEUOXJ0X29Zf3GAg+frY5iAYsYYFxXjomJcVIyLinFRMS4qxkXFuKgYN4aU0hrOXOIEuBXL1QHOXOKy5ZPK4pyadgNHAieP5l2ryPH/8tJZ9+1zU6i21a68YiC3ze2rba0mpVm+970xg/tOiYiIiIiIaHo/tyUf6I0zVSMAAAAASUVORK5CYII=) top no-repeat;
  }
}

.expandable-content {
  max-height: 0;
  overflow: hidden;
  transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
  opacity: 0;
  transform: translateY(-20px);
  margin-top: 0;
  visibility: hidden;

  &.expanded {
    max-height: 1000px;
    opacity: 1;
    transform: translateY(0);
    margin-top: 16px;
    visibility: visible;
  }
}
</style>
🌼4. 选择条件-选择条件弹窗组件added-condition/select-condition.vue 🌼
<script setup lang="ts">
import { reactive, ref } from 'vue'
import { hexToRgba } from '~@/utils/hexToRgba'
import { getConditionList } from '@/api/getting-clues/search-clues.ts'
import type { Condition } from '@/api/interface/getting-clues'

defineOptions({
  name: 'SelectCondition',
})
const emit = defineEmits(['handleOk'])

const { token } = useAntdToken()

const message = useMessage()

const open = ref<boolean>(false)
function handleOpen() {
  open.value = true
}
function handleCancel() {
  open.value = false
}
function handleOk() {
  if (state.selectedItemLabels.length === 0) {
    message.error('请选择要添加的条件')
  }
  else {
    emit('handleOk', state.selectedItemLabels)
    open.value = false
  }
}

// search
const searchValue = ref<string>('')
function onSearch(searchValue: string) {
  console.log('use value', searchValue)
  // console.log('or use this.value', searchValue.value);
}

// 条件列表
const conditionList = ref<Condition.ConditionItem[]>([])

getConditionList().then((res) => {
  if (res.success) {
    conditionList.value = res.data
  }
})

// 主条件分类鼠标移入和选中效果
/**
 * @constant hoveredMainIndices 鼠标移入的索引
 * @constant selectedMainIndices 选中的索引
 * @constant currentMainIndex 当前选中主条件index,用于获取子条件列表
 * @constant hoveredItemIndices 选中的子条件索引
 * @constant selectedItemIndices 当前选中的子条件
 * @constant selectedItemLabels 选择的条件
 */

interface State {
  hoveredMainIndices: Set<number>
  selectedMainIndices: Set<number>
  currentMainIndex: number
  hoveredItemIndices: Set<number>
  selectedItemIndices: Set<number>
  selectedItemLabels: Condition.ConditionItem[]
}

const state = reactive<State>({
  hoveredMainIndices: new Set(),
  selectedMainIndices: new Set([0]),
  currentMainIndex: 0,
  hoveredItemIndices: new Set(),
  selectedItemIndices: new Set(),
  selectedItemLabels: [],
})

function handleMainClick(index: number) {
  if (state.currentMainIndex === index) {
    return
  }
  state.selectedMainIndices.clear()
  state.selectedMainIndices.add(index)
  state.currentMainIndex = index
}
function isMainSelected(index: number) {
  return state.selectedMainIndices.has(index)
}

function handleMainHovered(index: number, isHovered: boolean) {
  if (isHovered) {
    state.hoveredMainIndices.add(index)
  }
  else {
    state.hoveredMainIndices.delete(index)
  }
}
function isMainHovered(index: number) {
  return state.hoveredMainIndices.has(index)
}

function activeMainStyle(index: number) {
  if (isMainSelected(index)) {
    return {
      color: token.value?.colorPrimary,
      backgroundColor: hexToRgba(token.value?.colorPrimary, 0.05),
      borderRight: `2px solid ${token.value?.colorPrimary}`,
    }
  }
  else {
    if (isMainHovered(index)) {
      return {
        backgroundColor: 'rgba(0, 0, 0, 0.06)',
      }
    }
  }
}

// 子条件列表
const conditionChildrenList = computed(() => {
  const c = conditionList.value.find((_, index) => state.currentMainIndex === index)
  return c?.children || []
})

function handleItemClick(item: Condition.ConditionItem) {
  const a = state.selectedItemLabels.find(i => i.value === item.value)
  if (!a) {
    if (state.selectedItemLabels.length === 10) {
      return message.error('一次最多选择10项')
    }
    state.selectedItemLabels.push(item)
  }
  else {
    state.selectedItemLabels = state.selectedItemLabels.filter(i => i.value !== item.value)
  }
}
function isItemSelected(item: Condition.ConditionItem) {
  return (state.selectedItemLabels.findIndex(i => i.value === item.value) !== -1)
}

function handleItemHovered(index: number, isHovered: boolean) {
  if (isHovered) {
    state.hoveredItemIndices.add(index)
  }
  else {
    state.hoveredItemIndices.delete(index)
  }
}
function isItemHovered(index: number) {
  return state.hoveredItemIndices.has(index)
}

function activeItemStyle(index: number, item: Condition.ConditionItem) {
  if (isItemSelected(item)) {
    return {
      color: '#ffffff',
      backgroundColor: token.value?.colorPrimary,
    }
  }
  else {
    if (isItemHovered(index)) {
      return {
        color: token.value?.colorPrimary,
        backgroundColor: hexToRgba(token.value?.colorPrimary, 0.05),
      }
    }
  }
}

function reset() {
  const initState: State = {
    hoveredMainIndices: new Set(),
    selectedMainIndices: new Set([0]),
    currentMainIndex: 0,
    hoveredItemIndices: new Set(),
    selectedItemIndices: new Set(),
    selectedItemLabels: [],
  }
  Object.assign(state, initState)
}

defineExpose({
  handleOpen,
  handleCancel,
  reset,
})
</script>

<template>
  <a-modal v-model:open="open" width="50vw" title="添加条件" @ok="handleOk">
    <div>
      <!-- search -->
      <div>
        <a-input-search
          v-model:value="searchValue" class="w-full" placeholder="搜索条件名称" enter-button="搜索" size="large"
          @search="onSearch"
        />
      </div>
      <!-- main wrapper -->
      <div class="mt-5 flex">
        <div class="w-1/5 h-[50vh] overflow-y-scroll scrollbar">
          <div
            v-for="(item, index) in conditionList" :key="index" class="p-2 m-r-1 lh-6 cursor-pointer"
            :style="activeMainStyle(index)" @click="handleMainClick(index)" @mouseover="handleMainHovered(index, true)"
            @mouseleave="handleMainHovered(index, false)"
          >
            {{ item.label }}
          </div>
        </div>
        <div class="flex-1 p-l-8 h-[50vh] overflow-y-auto scrollbar">
          <span
            v-for="(item, index) in conditionChildrenList" :key="index"
            class="inline-block m-2 p-inline-6 p-block-2  b-1 b-solid b-color-#e5e5e5 b-rd-2 cursor-pointer"
            :style="activeItemStyle(index, item)" @click="handleItemClick(item)"
            @mouseover="handleItemHovered(index, true)" @mouseleave="handleItemHovered(index, false)"
          >
            {{ item.label }}
          </span>
        </div>
      </div>
    </div>
    <template #footer>
      <div class="flex-between b-t-1 b-t-solid b-color-#e5e5e5 p-2">
        <div>
          已选择<span class="p-inline-1" :style="{ color: token?.colorPrimary }">{{ state.selectedItemLabels.length
          }}</span>个条件(一次最多10)
        </div>
        <div>
          <a-button key="back" @click="handleCancel">
            取消
          </a-button>
          <a-button key="submit" type="primary" @click="handleOk">
            确定
          </a-button>
        </div>
      </div>
    </template>
  </a-modal>
</template>

<style lang="less" scoped></style>
5. 🌿定义常量数据文件constant/index.ts 🌿
export const NavData = [
  {
    i: 0,
    title: '添加条件',
    url: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNzMzOTk3MzM3MjgwIiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjQ2NTM1IiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjIwMCIgaGVpZ2h0PSIyMDAiPjxwYXRoIGQ9Ik01MTIgOTcuNTIzODFjMjI4LjkxMjc2MiAwIDQxNC40NzYxOSAxODUuNTYzNDI5IDQxNC40NzYxOSA0MTQuNDc2MTlzLTE4NS41NjM0MjkgNDE0LjQ3NjE5LTQxNC40NzYxOSA0MTQuNDc2MTlTOTcuNTIzODEgNzQwLjkxMjc2MiA5Ny41MjM4MSA1MTIgMjgzLjA4NzIzOCA5Ny41MjM4MSA1MTIgOTcuNTIzODF6IG0zNi41NzE0MjkgMTk1LjA0NzYxOWgtNzMuMTQyODU4djE4Mi44MzI3NjFMMjkyLjU3MTQyOSA0NzUuNDI4NTcxdjczLjE0Mjg1OGwxODIuODU3MTQyLTAuMDI0MzgxVjczMS40Mjg1NzFoNzMuMTQyODU4di0xODIuODU3MTQySDczMS40Mjg1NzF2LTczLjE0Mjg1OGgtMTgyLjg1NzE0MlYyOTIuNTcxNDI5eiIgcC1pZD0iNDY1MzYiIGZpbGw9IiNmZmZmZmYiPjwvcGF0aD48L3N2Zz4=',
  },
  {
    i: 1,
    title: '热门推荐',
    url: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNzMzOTk2Mjc3MzUyIiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjQzNjIwIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgd2lkdGg9IjIwMCIgaGVpZ2h0PSIyMDAiPjxwYXRoIGQ9Ik03NzAuMDE2IDMzMi43NjhsLTc3LjgyNCA3Ni44czAtMzA3LjItMjU5LjYxNi00MDkuNTY4YzAgMC0yNS45MiAyODEuNi0xNTUuNzQ0IDM4My45MzYtMTI5Ljc5MiAxMDIuNDY0LTM4OS40NCA0MDkuNTY4IDEyOS42OTYgNjM5Ljk2OCAwIDAtMjU5LjYxNi0yODEuNiA3Ny44MjQtNDg2LjMwNCAwIDAtMjUuOTIgMTAyLjMzNiAxMDMuODcyIDIwNC44IDEyOS44MjQgMTAyLjM2OCAwIDI4MS42IDAgMjgxLjZzNjIzLjAwOC0xNTMuNjMyIDE4MS43OTItNjkxLjIzMnoiIGZpbGw9IiNmZmZmZmYiIHAtaWQ9IjQzNjIxIj48L3BhdGg+PC9zdmc+',
  },
  {
    i: 2,
    title: '已保存的筛选条件',
    url: 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNzM0MDU5ODUyOTg5IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjU0MTQiIGlkPSJteF9uXzE3MzQwNTk4NTI5OTAiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjIwMCI+PHBhdGggZD0iTTQyNi42NjY2NjcgNzI1LjMzMzMzM2gxNzAuNjY2NjY2djE3MC42NjY2NjdoLTE3MC42NjY2NjZ6IiBwLWlkPSI1NDE1IiBmaWxsPSIjZmZmZmZmIj48L3BhdGg+PHBhdGggZD0iTTg1OC40NTMzMzMgMzcxLjYyNjY2N2wtMjA2LjA4LTIwNi4wOEExMjggMTI4IDAgMCAwIDU2MS45MiAxMjhINDI2LjY2NjY2N3YyNTZoMjEzLjMzMzMzM2E0Mi42NjY2NjcgNDIuNjY2NjY3IDAgMCAxIDAgODUuMzMzMzMzSDM4NGE0Mi42NjY2NjcgNDIuNjY2NjY3IDAgMCAxLTQyLjY2NjY2Ny00Mi42NjY2NjZWMTI4SDI1NmExMjggMTI4IDAgMCAwLTEyOCAxMjh2NTEyYTEyOCAxMjggMCAwIDAgMTI4IDEyOGg4NS4zMzMzMzN2LTE3MC42NjY2NjdhODUuMzMzMzMzIDg1LjMzMzMzMyAwIDAgMSA4NS4zMzMzMzQtODUuMzMzMzMzaDE3MC42NjY2NjZhODUuMzMzMzMzIDg1LjMzMzMzMyAwIDAgMSA4NS4zMzMzMzQgODUuMzMzMzMzdjE3MC42NjY2NjdoODUuMzMzMzMzYTEyOCAxMjggMCAwIDAgMTI4LTEyOHYtMzA1LjkyYTEyOCAxMjggMCAwIDAtMzcuNTQ2NjY3LTkwLjQ1MzMzM3oiIHAtaWQ9IjU0MTYiIGZpbGw9IiNmZmZmZmYiPjwvcGF0aD48L3N2Zz4=',
  },
]

export const JudgementCondition: { [key: string]: any } = {
  type: {
    operateState: [
      {
        value: 'text',
        label: '等于任意一个',
      },
      {
        value: 'select-eq',
        label: '等于',
      },
      {
        value: 'select-in',
        label: '包含',
      },
      {
        value: 'select-in-one',
        label: '包含任意一个',
      },
      {
        value: 'select-not',
        label: '不包含',
      },
    ],
  },
  renderData: {
    existData: [
      {
        value: 0,
        label: '无',
      },
      {
        value: 1,
        label: '有',
      },
    ],
    ifData: [
      {
        value: 0,
        label: '否',
      },
      {
        value: 1,
        label: '是',
      },
    ],
  },
  tag: {
    inTag: [
      {
        value: 0,
        label: '机械',
      },
      {
        value: 1,
        label: '器械',
      },
      {
        value: 2,
        label: '设备',
      },
      {
        value: 1,
        label: '重工',
      },
    ],
  },
  // 所有的类别匹配不上走默认
  default: {
    default: [
      {
        value: 0,
        label: '存续',
      },
      {
        value: 1,
        label: '在业',
      },
    ],
  },
}
🌴6. 类别json数据api/index.ts 🌴
// api 模拟接口
export function getConditionList(): Promise<ResData<Condition.ConditionItem[]>> {
  return new Promise((resolve) => {
    resolve(conditionList as unknown as ResData<Condition.ConditionItem[]>)
  })
}

数据json:

{
  "errcode": 200,
  "errmsg": "操作成功",
  "data": [
    {
      "value": "extraHot",
      "label": "常用",
      "children": [
        {
          "value": "businessLocation",
          "label": "企业所在地",
          "children": null,
          "type": "MultiSelect",
          "keys": [
            {
              "key": "type",
              "value": "area",
              "isSys": 0
            },
            {
              "key": "tag",
              "value": "inTag",
              "isSys": 0
            }
          ],
          "checked": true,
          "alisKey": false,
          "viewfilter": "text",
          "tooltip": "",
          "certificateFlag": true,
          "normal": true,
          "intellectualPropertyFlag": true,
          "groupName": ""
        },
        {
          "value": "mobile",
          "label": "有无手机",
          "children": null,
          "type": "SelectOption",
          "keys": [
            {
              "key": "renderData",
              "value": "existData",
              "isSys": 1
            }
          ],
          "checked": true,
          "alisKey": false,
          "viewfilter": "bool",
          "tooltip": "",
          "certificateFlag": true,
          "normal": true,
          "intellectualPropertyFlag": true,
          "groupName": ""
        },
        {
          "value": "operateState",
          "label": "经营状态",
          "children": null,
          "type": "MultiSelect",
          "keys": [
            {
              "key": "type",
              "value": "operateState",
              "isSys": 0
            }
          ],
          "checked": true,
          "alisKey": false,
          "viewfilter": "select-eq",
          "tooltip": "",
          "certificateFlag": true,
          "normal": true,
          "intellectualPropertyFlag": true,
          "groupName": ""
        },
        {
          "value": "companyName",
          "label": "企业名称",
          "children": null,
          "type": "TextTags",
          "keys": [
            {
              "key": "type",
              "value": "companyName",
              "isSys": 0
            },
            {
              "key": "tag",
              "value": "inTag",
              "isSys": 0
            }
          ],
          "checked": true,
          "alisKey": true,
          "viewfilter": "text",
          "tooltip": "",
          "certificateFlag": true,
          "normal": true,
          "intellectualPropertyFlag": true,
          "groupName": ""
        }
      ],
      "type": "",
      "keys": [],
      "checked": true,
      "alisKey": false,
      "viewfilter": "",
      "tooltip": "",
      "certificateFlag": true,
      "normal": true,
      "intellectualPropertyFlag": true,
      "groupName": ""
    },
    {
      "value": "industryInfo",
      "label": "企业基本信息",
      "children": [
        {
          "value": "foundTime",
          "label": "成立日期",
          "children": null,
          "type": "DatePicker",
          "keys": [],
          "checked": true,
          "alisKey": false,
          "viewfilter": "timeRange",
          "tooltip": "",
          "certificateFlag": true,
          "normal": true,
          "intellectualPropertyFlag": true,
          "groupName": "基本信息"
        }
      ],
      "type": "",
      "keys": [],
      "checked": true,
      "alisKey": false,
      "viewfilter": "",
      "tooltip": "",
      "certificateFlag": true,
      "normal": true,
      "intellectualPropertyFlag": true,
      "groupName": ""
    }
  ],
  "success": true
}
💦7. 数据接口定义interface/index.ts 💦
import type { Dayjs } from 'dayjs'

export namespace Condition {
  export interface ChildKeys {
    key: string
    value: string
    isSys: number
  }

  export interface ConditionItem {
    label: string
    value: string
    type: string
    keys: ChildKeys[]
    keysDate?: [Dayjs, Dayjs]
    keysMul?: any[]
    children: ConditionItem[]
    checked: boolean
    viewfilter: string
    tooltip: string
    [key: string]: any
  }
}

❗️ 4. 封装实例缺点💦

  1. added-condition.vue文件内动态渲染UI组件代码
    在这里插入图片描述

  2. UI的结构是不可预知的,所以既然叫动态,那么就需要修改以为动态组件。。。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Go+chromedp实现Web UI自动化测试

1.为什么使用go进行UI自动化测试&#xff1f; 速度&#xff1a;Go速度很快&#xff0c;这在运行包含数百个UI测试的测试套件时是一个巨大的优势 并发性&#xff1a;可以利用Go的内置并发性(goroutines)来并行化测试执行 简单&#xff1a;Go的简约语法允许您编写可读且可维护…

总结TCP/IP四层模型

总结TCP/IP四层模型 阅读目录(Content) 一、TCP/IP参考模型概述 1.1、TCP/IP参考模型的层次结构二、TCP/IP四层功能概述 2.1、主机到网络层  2.2、网络互连层  2.3、传输层  2.3、应用层 三、TCP/IP报文格式 3.1、IP报文格式3.2、TCP数据段格式3.3、UDP数据段格式3.4、套…

怎么在家访问公司服务器?

在日常工作中&#xff0c;特别是对信息技术从业者而言&#xff0c;工作往往离不开公司的服务器。他们需要定期访问服务器&#xff0c;获取一些关键的机密文件或数据。如果您在家办公&#xff0c;并且需要处理未完成的任务&#xff0c;同时需要从公司服务器获取所需的数据&#…

自动化文档处理:Azure AI Document Intelligence

Azure AI Document Intelligence支持多种文件格式&#xff0c;包括PDF、JPEG、PNG等。其核心功能是将这些文档按页进行内容提取&#xff0c;并转化为LangChain文档。其默认输出格式是Markdown&#xff0c;这使得文档可以通过MarkdownHeaderTextSplitter进行语义分片。您也可以使…

ubuntu快速入门

1.进入某个文件夹 cd workspace/2.tab自动补全 3.列出当前文件夹所有文件 ls列出所有文件包括隐藏文件 ls -a 4.创建文件夹 mkdir linuxLearn 5.创建文件 gedit command.sh在commmand.sh键入 echo hello echo hi? echo how are you? PS:touch hello.txt(也可以创建新…

在虚幻引擎4(UE4)中使用蓝图的详细教程

在虚幻引擎4&#xff08;UE4&#xff09;中使用蓝图的详细教程 虚幻引擎4&#xff08;Unreal Engine 4&#xff0c;简称UE4&#xff09;是一款功能强大的游戏引擎&#xff0c;广泛应用于游戏开发、虚拟现实、建筑可视化等领域。UE4 提供了一个强大的可视化脚本工具——蓝图&am…

修改网络ip地址方法有哪些?常用的有这四种

在数字时代&#xff0c;IP地址作为网络设备的唯一标识&#xff0c;对于网络连接和通信至关重要。然而&#xff0c;有时候我们可能需要修改设备的IP地址&#xff0c;以满足特定的网络需求或解决网络问题。本文将为您详细介绍几种修改网络IP地址的常用方法&#xff0c;无论是对于…

你了解DNS吗?

你了解DNS吗&#xff1f; 一. 介绍二. DNS的工作原理三. DNS查询流程示意图四. DNS 记录类型五. DNS的安全问题与 DNSSEC 前言 这是我在这个网站整理的笔记,有错误的地方请指出&#xff0c;关注我&#xff0c;接下来还会持续更新。 作者&#xff1a;神的孩子都在歌唱 一. 介绍 …

Android笔试面试题AI答之非技术问题(3)

Android入门请看《Android应用开发项目式教程》 文章目录 1.你是如何应付项目经理的&#xff1f;**1. 建立清晰的沟通机制****2. 明确项目目标与优先级****3. 制定合理的开发计划****4. 管理项目风险****5. 推动敏捷开发****6. 管理需求变更****7. 提升团队协作****8. 关注项目…

TestMAX/DFT Compiler:时序单元的类型、连接顺序和后DFT优化

相关阅读 TestMAX/DFT Compilerhttps://blog.csdn.net/weixin_45791458/category_12865937.html?spm1001.2014.3001.5482 时序单元的状态 未映射的时序单元(Unmapped Sequential Cell) 在Design Compiler读取了一个RTL设计后&#xff0c;Design Compiler内置的HDL Compiler工…

操作012:延迟插件

文章目录 操作012&#xff1a;延迟插件一、插件简介二、插件安装1、确定卷映射目录2、下载延迟插件3、启用插件4、确认 三、创建交换机四、代码测试1、生产者端代码2、消费者端代码①情况A&#xff1a;资源已创建②情况B&#xff1a;资源未创建 3、执行效果①交换机类型②生产者…

Kafka 性能提升秘籍:涵盖配置、迁移与深度巡检的综合方案

文章目录 1.1.网络和io操作线程配置优化1.2.log数据文件刷盘策略1.3.日志保留策略配置1.4.replica复制配置1.5.配置jmx服务1.6.系统I/O参数优化1.6.1.网络性能优化1.6.2.常见痛点以及优化方案1.6.4.优化参数 1.7.版本升级1.8.数据迁移1.8.1.同集群broker之间迁移1.8.2.跨集群迁…

【文献精读笔记】Explainability for Large Language Models: A Survey (大语言模型的可解释性综述)(二)

****非斜体正文为原文献内容&#xff08;也包含笔者的补充&#xff09;&#xff0c;灰色块中是对文章细节的进一步详细解释&#xff01; 3.1.2 基于注意力的解释&#xff08;Attention-Based Explanation&#xff09; 注意力机制可以揭示输入数据中各个部分之间的关系&#…

音视频采集推流时间戳记录方案

音视频同步更多文章 深入理解音视频pts&#xff0c;dts&#xff0c;time_base以及时间数学公式_视频pts计算-CSDN博客 ffplay音视频同步分析_ffplay 音视频同步-CSDN博客 音视频采集打时间戳设计 实时音视频数据的采集和处理场景。具体来说: 采集阶段: 在音视频数据采集过…

「numpy」numpy初步、ndarray、创建数组

Numpy 初步、ndarray、创建数组 【目录】 简介 ndarray介绍 ndarray用法 ndarray基本操作 一、numpy简介 Numpy&#xff08;Numerical Python&#xff09;是一个开源的Python科学计算库&#xff0c;用于快速处理任意维度的数组。 Numpy支持常见的数组和矩阵操作。对于同样…

FFmpeg 编码和解码

文章目录 音频格式AACADIF音频数据交换格式ADTS音频数据传输流 音频解码音频编码 视频格式H264GOP图像组I帧&#xff0c;P帧&#xff0c;B帧H264压缩技术H264压缩级别H264视频级别H264码流结构SPSPPS 解码视频编码视频 音频格式 AAC AAC全称 Advanced Audio Coding&#xff0…

游戏引擎学习第67天

reviewing “apron”概念以更新区域 我们正在进行模拟区域的扩展工作&#xff0c;目标是通过增加一个更大的区域来支持更丰富的互动&#xff0c;尤其是那些可能超出摄像机视野的内容。现有的模拟区域包括摄像机能看到的区域和其周围的环境区域&#xff0c;但为了保证更高效的游…

3D线上艺术展:艺术与技术的完美融合

随着数字技术的飞速发展&#xff0c;未来的艺术展览正逐步迈向线上线下融合的新阶段。其中&#xff0c;3D线上展览以其独特的魅力&#xff0c;成为线下展览的延伸与拓展&#xff0c;为艺术爱好者们开辟了全新的观赏途径。 对于艺术家和策展人而言&#xff0c;3D线上展览不仅打…

Qt制作一个简单闹钟

增加testtospeech模块 QT core gui texttospeech 头文件 #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTimerEvent> #include <QTimer> #include <QTime> #include <QMouseEvent> #include <QTextToSpeech>…

Java编程题_面向对象和常用API01_B级

Java编程题_面向对象和常用API01_B级 第1题 面向对象、异常、集合、IO 题干: 请编写程序&#xff0c;完成键盘录入学生信息&#xff0c;并计算总分将学生信息与总分一同写入文本文件 需求&#xff1a;键盘录入3个学生信息(姓名,语文成绩,数学成绩) 求出每个学生的总分 ,并…