低代码-添加按钮组件设计

2024-01-21

效果图

在这里插入图片描述
可拆分为以下细节

  • 按钮列表
  • 删除( 两个操作需同步删除 )
    • 点击外侧删除
    • 点击复选框删除
  • 添加:点击复选框添加

示例代码

技术栈: vue3+arco.design + ts + less + tailwindcss

<template>
  <div class="flex ">
    <draggable
      :list="props.bindButton"
      :animation="200"
    >
      <template #item="{element,index}">
        <a-badge
          :offset="[-6, 0]"
          :dot-style="{ height: '16px', width: '16px', fontSize: '14px' }"
        >
          <template #content>
            <icon-close-circle-fill
              :size="16"
              class="cursor-pointer"
              :style="{ verticalAlign: 'middle', color: 'var(--color-text-2)' }"
              @click="()=>props.bindButton.splice(index,1)"
            />
          </template>
          <a-button
            class="mr-2"
            :size="element.buttonProperties?.size"
            :type="element.buttonProperties?.type||'text'"
            :status="element.buttonProperties?.status||'normal'"
            :shape="element.buttonProperties?.shape"
            @click.stop="handleSelectButton(element)"
          >
            {{ element.buttonName }}
          </a-button>
        </a-badge>
      </template>
    </draggable>

    <a-trigger
      trigger="click"
      :unmount-on-close="false"
      class="w-[200px]"
    >
      <a-button
        type="text"
      >
        <template #icon>
          <icon-plus />
        </template>
        添加按钮
      </a-button>

      <template #content>
        <div>
          <a-checkbox-group
            v-if="buttonList.length"
            :model-value="useFieldNames"
            class="my-checkbox-group"
          >
            <a-checkbox
              v-for="(item, pIdx) in buttonList"
              :key="item['code']"
              :value="item['code']"
              class="checkbox-item"
              @click="checkboxClick(item, pIdx)"
            >
              <template #checkbox="{ checked }">
                <div
                  class="custom-checkbox-card"
                  :class="{ 'custom-checkbox-card-checked': checked }"
                >
                  <div className="custom-checkbox-card-mask">
                    <div className="custom-checkbox-card-mask-dot" />
                  </div>
                  <div className="custom-checkbox-card-title">
                    {{ item['buttonName'] }}
                  </div>
                </div>
              </template>
            </a-checkbox>
          </a-checkbox-group>
          <div v-else>
            <a-empty />
          </div>
        </div>
      </template>
    </a-trigger>
  </div>
</template>

<script lang='ts' setup>
import { computed, ref } from 'vue'
import draggable from 'vuedraggable'

const emit = defineEmits('changeSelectItem')

const props = defineProps({
  buttonList: {
    type: Array,
    required: true,
  },
  bindButton: {
    type: Array,
    required: true,
  },
})

const useFields = computed(() => props.useFields)
const buttonList:any = computed(() => props.buttonList)

// 使用的字段名称
const useFieldNames = ref([])

const handleSelectButton = (btn: any) => {
  btn.type = 'buttonItem'
  btn.key = 'buttonItem'
  btn.label = btn.buttonName
  // btn.eventList = events.value
  btn.buttonProperties.size = btn.buttonProperties.size ? btn.buttonProperties.size : 'medium'
  btn.buttonProperties.type = btn.buttonProperties.type ? btn.buttonProperties.type : 'text'
  btn.buttonProperties.shape = btn.buttonProperties.shape ? btn.buttonProperties.shape : ''
  btn.buttonProperties.status = btn.buttonProperties.status ? btn.buttonProperties.status : 'normal'


  emit('changeSelectItem', btn)
}

// 点击字段
const checkboxClick = (item:any, index) => {
  const isDel = props.bindButton.includes(item.code)
  if (isDel) delSelectField(item, index)
  else SelectField(item, index)
}
const SelectField = (item, index) => {
  props.bindButton.push(item)
  useFieldNames.value.push(item.code)
}
const delSelectField = (item, index) => {
  useFieldNames.value.splice(index, 1)
}
</script>

<style scoped lang='less'>
.my-checkbox-group{
  overflow-x: hidden;
  overflow-y: overlay;
  width: 250px;
  max-height: 300px;
  background-color: #FFF;
  border: 1px solid rgb(229,230,235);
  border-radius: 4px;
  box-shadow: 0 4px 10px #0000001a;
}
.custom-checkbox-card {
  display: flex;
  align-items: center;
  padding: 10px 16px;
  border-radius: 4px;
  width: 250px;
  box-sizing: border-box;
}

.checkbox-item {
  margin-right: 0 !important;
  padding-left: 0;
  border: none;
}

.custom-checkbox-card-mask {
  height: 14px;
  width: 14px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 2px;
  border: 1px solid var(--color-border-2);
  box-sizing: border-box;
}

.custom-checkbox-card-mask-dot {
  width: 8px;
  height: 8px;
  border-radius: 2px;
}

.custom-checkbox-card-title {
  color: var(--color-text-1);
  font-size: 14px;
  font-weight: bold;
  margin-left: 8px;
}

.custom-checkbox-card:hover,
.custom-checkbox-card-checked,
.custom-checkbox-card:hover .custom-checkbox-card-mask,
.custom-checkbox-card-checked .custom-checkbox-card-mask {
  border-color: rgb(var(--primary-6));
}

.custom-checkbox-card-checked {
  background-color: var(--color-primary-light-1);
}

.custom-checkbox-card:hover .custom-checkbox-card-title,
.custom-checkbox-card-checked .custom-checkbox-card-title {
  color: rgb(var(--primary-6));
}

.custom-checkbox-card-checked .custom-checkbox-card-mask-dot {
  background-color: rgb(var(--primary-6));
}
</style>

上述代码发现点击外侧,无法同步删除复选框这种的按钮,排查:
复选框绑定值useFieldNames的来源为 SelectField 函数,而 SelectField 仅由 checkboxClick 触发。 checkboxClick 为绑定在复选框上的函数,因此当外部使用props.bindButton.splice(index,1)删除按钮时,无法触发 useFieldNames 更新。

  • 优化后的代码如下
<template>
  <div class="flex">
    <draggable
      :list="props.bindButton"
      :animation="200"
    >
      <template #item="{element,index}">
        <a-badge
          :offset="[-6, 0]"
          :dot-style="{ height: '16px', width: '16px', fontSize: '14px' }"
        >
          <template #content>
            <icon-close-circle-fill
              :size="16"
              class="cursor-pointer"
              :style="{ verticalAlign: 'middle', color: 'var(--color-text-2)' }"
              @click="deleteBtn(index)"
            />
          </template>
          <a-button
            class="mr-2"
            :size="element.buttonProperties?.size"
            :type="element.buttonProperties?.type||'text'"
            :status="element.buttonProperties?.status||'normal'"
            :shape="element.buttonProperties?.shape"
            @click.stop="handleSelectButton(element)"
          >
            {{ element.buttonName }}
          </a-button>
        </a-badge>
      </template>
    </draggable>

    <a-trigger
      trigger="click"
      :unmount-on-close="false"
      class="w-[200px]"
    >
      <a-button
        type="text"
      >
        <template #icon>
          <icon-plus />
        </template>
        添加按钮
      </a-button>

      <template #content>
        <div>
          <a-checkbox-group
            v-if="buttonList.length"
            :model-value="useFieldNames"
            class="my-checkbox-group"
          >
            <a-checkbox
              v-for="item in buttonList"
              :key="item['code']"
              :value="item['code']"
              class="checkbox-item"
              @click="checkboxClick(item)"
            >
              <template #checkbox="{ checked }">
                <div
                  class="custom-checkbox-card"
                  :class="{ 'custom-checkbox-card-checked': checked }"
                >
                  <div className="custom-checkbox-card-mask">
                    <div className="custom-checkbox-card-mask-dot" />
                  </div>
                  <div className="custom-checkbox-card-title">
                    {{ item['buttonName'] }}
                  </div>
                </div>
              </template>
            </a-checkbox>
          </a-checkbox-group>
          <div v-else>
            <a-empty />
          </div>
        </div>
      </template>
    </a-trigger>
  </div>
</template>

<script lang='ts' setup>
import { ref } from 'vue'
import draggable from 'vuedraggable'

const emit = defineEmits(['changeSelectItem'])
const props = defineProps({
  buttonList: {
    type: Array as any,
    required: true,
  },
  bindButton: {
    type: Array,
    required: true,
  },
})

// 使用的字段名称
const useFieldNames = ref<string []>([])

const handleSelectButton = (btn: any) => {
  btn.type = 'buttonItem'
  btn.key = 'buttonItem'
  btn.label = btn.buttonName
  // btn.eventList = events.value
  btn.buttonProperties.size = btn.buttonProperties.size ? btn.buttonProperties.size : 'medium'
  btn.buttonProperties.type = btn.buttonProperties.type ? btn.buttonProperties.type : 'text'
  btn.buttonProperties.shape = btn.buttonProperties.shape ? btn.buttonProperties.shape : ''
  btn.buttonProperties.status = btn.buttonProperties.status ? btn.buttonProperties.status : 'normal'


  emit('changeSelectItem', btn)
}

// 删除按钮
function deleteBtn(index:number) {
  props.bindButton.splice(index, 1)
  useFieldNames.value = props.bindButton.map((iv:any) => iv.code)
}

/**
 * 点击复选框选中/删除按钮
 * @param item 点击的按钮
 * @param index 点击的按钮索引
 */
const checkboxClick = (item:any) => {
  const isDel = useFieldNames.value.includes(item.code)
  isDel ? delSelectField(item) : SelectField(item)
}

const SelectField = (item) => {
  props.bindButton.push(item)
}

// 不能根据index删除,
const delSelectField = (item) => {
  const index = props.bindButton.findIndex((iv:any) => iv.code === item.code)
  props.bindButton.splice(index, 1)
}

// 监听 bindButton 的变化,并在变化时更新 useFieldNames
watch(() => props.bindButton, (newBindButton) => {
  useFieldNames.value = newBindButton.map((item:{
    code:string
  }) => item.code)
}, {
  deep: true,
})
</script>

<style scoped lang='less'>
.my-checkbox-group{
  overflow-x: hidden;
  overflow-y: overlay;
  width: 250px;
  max-height: 300px;
  background-color: #FFF;
  border: 1px solid rgb(229,230,235);
  border-radius: 4px;
  box-shadow: 0 4px 10px #0000001a;
}
.custom-checkbox-card {
  display: flex;
  align-items: center;
  padding: 10px 16px;
  border-radius: 4px;
  width: 250px;
  box-sizing: border-box;
}

.checkbox-item {
  margin-right: 0 !important;
  padding-left: 0;
  border: none;
}

.custom-checkbox-card-mask {
  height: 14px;
  width: 14px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 2px;
  border: 1px solid var(--color-border-2);
  box-sizing: border-box;
}

.custom-checkbox-card-mask-dot {
  width: 8px;
  height: 8px;
  border-radius: 2px;
}

.custom-checkbox-card-title {
  color: var(--color-text-1);
  font-size: 14px;
  font-weight: bold;
  margin-left: 8px;
}

.custom-checkbox-card:hover,
.custom-checkbox-card-checked,
.custom-checkbox-card:hover .custom-checkbox-card-mask,
.custom-checkbox-card-checked .custom-checkbox-card-mask {
  border-color: rgb(var(--primary-6));
}

.custom-checkbox-card-checked {
  background-color: var(--color-primary-light-1);
}

.custom-checkbox-card:hover .custom-checkbox-card-title,
.custom-checkbox-card-checked .custom-checkbox-card-title {
  color: rgb(var(--primary-6));
}

.custom-checkbox-card-checked .custom-checkbox-card-mask-dot {
  background-color: rgb(var(--primary-6));
}
</style>

  1. 采用watch监听props.bindButton,确保初始化能更新到下拉列表,需要开启 deep: true 配置,否则props.bindButton.splice(index, 1)无法触发watch函数
  2. 优化点击复选框删除按钮函数
// 不能根据index删除,
const delSelectField = (item) => {
  const index = props.bindButton.findIndex((iv:any) => iv.code === item.code)
  props.bindButton.splice(index, 1)
}

值得注意的一点是需要删除的是绑定的列表,而不是去查找按钮列表,否则删除会出现删除了别的按钮的场景

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

低代码-添加按钮组件设计 的相关文章

随机推荐

  • ​LeetCode解法汇总83. 删除排序链表中的重复元素

    目录链接 力扣编程题 解法汇总 分享 记录 CSDN博客 GitHub同步刷题项目 https github com September26 java algorithms 原题链接 力扣 LeetCode 描述 给定一个已排序的链表的头
  • 界面组件DevExpress ASP.NET Core v23.2 - 拥有全新的主题样式

    DevExpress ASP NET Core Controls使用强大的混合方法 结合现代企业Web开发工具所期望的所有功能 该套件通过ASP NET Razor标记和服务器端ASP NET Core Web API的生产力和简便性 提供
  • 【wrf-python】将modis土地利用分类按照wrf可识别的格式制作静态数据集

    下载modis数据后 可以按照gdal translate of ENVI co INTERLEAVE BSQ xxx1 tif output2001 bin命令处理tif数据 也可以按照下面的python语句处理tif数据 import
  • Creator AIGC插件!一句话生成人脸

    近几个月以来 AIGC 一路高歌猛进 让我们见证了一场行业革命 然而 AIGC 在 3D 资产领域却仍是业内的难题 少有突破 小编今天给大家推荐一个 3D 角色 AIGC 利器 ChatAvatar 它可以算是 3D AIGC 领域的一匹黑
  • 【gee】下载modis土地利用类型

    var china ee FeatureCollection projects assets china boundary var lc dataset ee ImageCollection MODIS 061 MCD12Q1 filter
  • ​LeetCode解法汇总82. 删除排序链表中的重复元素 II

    目录链接 力扣编程题 解法汇总 分享 记录 CSDN博客 GitHub同步刷题项目 https github com September26 java algorithms 原题链接 力扣 LeetCode 描述 给定一个已排序的链表的头
  • 「Java开发指南」MyEclipse如何支持Spring Scaffolding?(一)

    MyEclipse v2023 1 2离线版下载 1 使用Spring的Scaffolding应用程序 支持Spring的 MyEclipse 目标之一是帮助开发人员从零开始编写更少的代码 并更广泛地使用代码生成 MyEclipse提供了丰
  • 盲猜你不懂H5架构和原生架构的区别

    2024软件测试面试刷题 这个小程序 永久刷题 靠它快速找到工作了 刷题APP的天花板 CSDN博客 文章浏览阅读2 3k次 点赞85次 收藏11次 你知不知道有这么一个软件测试面试的刷题小程序 里面包含了面试常问的软件测试基础题 web自
  • qt.qpa.plugin: Could not find the Qt platform plugin “windows“ in ““

    系统环境 Win10家庭中文版 Qt 5 12 9 链接了一些64位的第三方库 程序编译完运行后出现 qt qpa plugin Could not find the Qt platform plugin windows in 弹窗如下 网
  • Python自动化测试 | 如何使用Robot Framework进行自动化测试?

    2024软件测试面试刷题 这个小程序 永久刷题 靠它快速找到工作了 刷题APP的天花板 CSDN博客 文章浏览阅读2 3k次 点赞85次 收藏11次 你知不知道有这么一个软件测试面试的刷题小程序 里面包含了面试常问的软件测试基础题 web自
  • 跨平台UI自动化框架:Airtest,游戏开发和应用测试的利器

    2024软件测试面试刷题 这个小程序 永久刷题 靠它快速找到工作了 刷题APP的天花板 CSDN博客 文章浏览阅读2 3k次 点赞85次 收藏11次 你知不知道有这么一个软件测试面试的刷题小程序 里面包含了面试常问的软件测试基础题 web自
  • 测开和测试平台是否有存在的必要?

    2024软件测试面试刷题 这个小程序 永久刷题 靠它快速找到工作了 刷题APP的天花板 CSDN博客 文章浏览阅读2 3k次 点赞85次 收藏11次 你知不知道有这么一个软件测试面试的刷题小程序 里面包含了面试常问的软件测试基础题 web自
  • 如何写好一个错误报告

    2024软件测试面试刷题 这个小程序 永久刷题 靠它快速找到工作了 刷题APP的天花板 CSDN博客 文章浏览阅读2 3k次 点赞85次 收藏11次 你知不知道有这么一个软件测试面试的刷题小程序 里面包含了面试常问的软件测试基础题 web自
  • 一篇文章带你了解Python常用自动化测试框架——Pytest

    2024软件测试面试刷题 这个小程序 永久刷题 靠它快速找到工作了 刷题APP的天花板 CSDN博客 文章浏览阅读2 3k次 点赞85次 收藏11次 你知不知道有这么一个软件测试面试的刷题小程序 里面包含了面试常问的软件测试基础题 web自
  • Vue3 和Vue2的区别,以及钩子函数的使用

    Vue js 3 和 Vue js 2 是两个主要版本的流行前端框架 它们之间有很多区别 包括性能优化 新特性和改进的API等 以下是一些Vue 3与Vue 2之间的主要区别 以及一些示例代码来说明这些差异 1 性能优化 响应式系统 Vue
  • Jmeter 性能-阶梯式性能指标监听

    2024软件测试面试刷题 这个小程序 永久刷题 靠它快速找到工作了 刷题APP的天花板 CSDN博客 文章浏览阅读2 3k次 点赞85次 收藏11次 你知不知道有这么一个软件测试面试的刷题小程序 里面包含了面试常问的软件测试基础题 web自
  • Jmeter 性能-阶梯负载最终请求数

    2024软件测试面试刷题 这个小程序 永久刷题 靠它快速找到工作了 刷题APP的天花板 CSDN博客 文章浏览阅读2 3k次 点赞85次 收藏11次 你知不知道有这么一个软件测试面试的刷题小程序 里面包含了面试常问的软件测试基础题 web自
  • msyql 异常,别干着急,70%的问题都在这里!

    2024软件测试面试刷题 这个小程序 永久刷题 靠它快速找到工作了 刷题APP的天花板 CSDN博客 文章浏览阅读2 3k次 点赞85次 收藏11次 你知不知道有这么一个软件测试面试的刷题小程序 里面包含了面试常问的软件测试基础题 web自
  • 摸爬滚打多年的打工人,总结了三条职场真理,绝不假大空!

    2024软件测试面试刷题 这个小程序 永久刷题 靠它快速找到工作了 刷题APP的天花板 CSDN博客 文章浏览阅读2 3k次 点赞85次 收藏11次 你知不知道有这么一个软件测试面试的刷题小程序 里面包含了面试常问的软件测试基础题 web自
  • 低代码-添加按钮组件设计

    效果图 可拆分为以下细节 按钮列表 删除 两个操作需同步删除 点击外侧删除 点击复选框删除 添加 点击复选框添加 示例代码 技术栈 vue3 arco design ts less tailwindcss