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

✨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的结构是不可预知的,所以既然叫动态,那么就需要修改以为动态组件。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值