自定义调色盘组件

示例效果:

调色盘组件代码:使用inputtype=color实现

javascript 复制代码
<template>
  <div class="color-plate-page">
    <div class="color-div" @click.stop="onColorDivClick" :style="{ backgroundColor: color }"></div>
    <div class="color-plate" v-if="plateVisible" ref="colorPlateRef">
      <div>标准颜色</div>
      <div class="flex-start" style="margin: 6px 0px">
        <div
          class="standard-color-item"
          v-for="(color, idx) in standardColors"
          :key="`standard-color-item${idx}`"
          :style="{ backgroundColor: color }"
          @click="onStandardColorClick(color)"
        ></div>
      </div>
      <div style="cursor: pointer" @click="inputColorRef?.click?.()">更多颜色...</div>
      <input class="hidden-color-input" :value="color" @input="updateColor" type="color" ref="inputColorRef" />
    </div>
  </div>
</template>

<script setup lang="ts">
  // 定义组件的 props 和 emits
  const props = defineProps({
    color: { type: String, default: '#000000' }
  });
  const emit = defineEmits(['update:color']);
  // 调色盘面板
  const plateVisible = ref<boolean>(false);
  // input[type=color] ref引用
  const inputColorRef = ref<any>(null);

  // 标准颜色
  const standardColors = [
    '#C00000', // 深红
    '#FF0000', // 红色
    '#FFC000', // 橙色
    '#FFFF00', // 黄色
    '#92D050', // 浅绿
    '#00B050', // 绿色
    '#00B0F0', // 浅蓝
    '#0070C0', // 蓝色
    '#002060', // 深蓝
    '#7030A0' // 紫色
  ];
  // 挂载时监听全局点击事件
  onMounted(() => {
    document.addEventListener('click', onClickOutside);
  });

  // 卸载时移除监听(防止内存泄漏)
  onUnmounted(() => {
    document.removeEventListener('click', onClickOutside);
  });
  // color-div点击事件
  const onColorDivClick = () => {
    plateVisible.value = !plateVisible.value;
    nextTick(() => {
      inputColorRef.value.value = formattedColor.value;
    });
  };
  // 同步更新v-model值
  const updateColor = (event: any) => {
    emit('update:color', event.target.value);
  };
  // 标准颜色item点击事件
  const onStandardColorClick = (color: any) => {
    emit('update:color', color);
    plateVisible.value = false;
  };
  // 全局点击事件处理:点击空白处关闭调色盘面板
  const colorPlateRef = ref<any>(null);
  const onClickOutside = (e: MouseEvent) => {
    if (!!plateVisible.value) {
      // 判断点击不在面板内(即"空白处")→ 关闭面板
      const isClickInPlate = colorPlateRef.value?.contains(e.target as Node);
      if (!isClickInPlate) {
        plateVisible.value = false;
      }
    }
  };

  // 格式化颜色值:input只支持 6 位十六进制(解决 3 位缩写/格式不规范问题)
  const formattedColor = computed(() => {
    const colorMap: any = {
      // 基础颜色
      black: '#000000',
      white: '#ffffff',
      gray: '#808080', // 标准gray对应的十六进制值
      grey: '#808080', // 兼容英式拼写
      red: '#ff0000',
      green: '#008000',
      blue: '#0000ff',
      skyblue: '#87ceeb',
      yellow: '#ffff00',
      orange: '#ffa500',
      purple: '#800080',
      pink: '#ffc0cb',
      brown: '#a52a2a',
      // 扩展灰度色(可选)
      lightgray: '#d3d3d3',
      darkgray: '#a9a9a9'
    };
    //   -------------
    const color = props.color.trim();
    const isHex = color.startsWith('#'); // 是否为hex色值 /^#([0-9a-fA-F]{6})$/.test(color)
    let ret = color;
    if (isHex) {
      // 处理 3 位缩写(如 #f00 → #ff0000)
      if (/^#([0-9a-fA-F]{3})$/.test(color)) {
        const [, r, g, b] = color.match(/^#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])$/)!;
        ret = `#${r}${r}${g}${g}${b}${b}`;
      }
      // 补全 6 位(如 #000 → #000000)
      if (/^#([0-9a-fA-F]{1,5})$/.test(color)) {
        ret = `#${color.slice(1).padEnd(6, '0')}`;
      }
    } else {
      // 处理颜色单词
      ret = colorMap[color];
    }
    return ret || '#000000';
  });
</script>

<style lang="less" scoped>
  .color-plate-page {
    position: relative;
    .color-div {
      width: 24px;
      height: 24px;
    }
    .color-plate {
      position: absolute;
      padding: 6px 6px 6px 10px;
      margin-top: 8px;
      background-color: #fff;
      border: 1px solid #eee;
      box-shadow: 0 0 3px #eee;
      border-radius: 4px;
      z-index: 1040;
      .standard-color-item {
        width: 15px;
        height: 15px;
        margin-right: 3px;
        cursor: pointer;
      }
      // 隐藏的color input(关键:不可用display:none,否则无法触发点击)
      .hidden-color-input {
        position: absolute;
        width: 0;
        height: 0;
        opacity: 0;
        pointer-events: none; // 避免遮挡点击
      }
    }
  }
</style>

父组件引用调色盘组件

javascript 复制代码
<SColorPlate v-model:color="color" />
相关推荐
事圆则缓10 小时前
Flutter 状态管理框架对比(三):Riverpod 如何连起购物车与异步价格
开发语言·javascript·flutter
linux_cfan10 小时前
videojs v10 源代码系列解读:30 · `SerialRunner` 与 `ConcurrentRunner`
前端
liangshanbo121510 小时前
面试题:页面需要发起大量 Ajax / HTTP 请求时,如何优化请求性能?
前端·http·ajax
lyz24685911 小时前
vue3页面因参数未声明而报错的常犯错误
前端·javascript·vue.js
摸爬滚打的小李11 小时前
流程图同步AI生成
前端
Hilaku11 小时前
我重写了整个项目,但没人感谢我!
前端·javascript·程序员
zach12 小时前
Vue/React SPA 打包部署后,子路由刷新 401 未登录问题彻底解决
前端·nginx·next.js
旋生万物12 小时前
素数螺旋映射 $z_n=n^{1+i}$ 的角分布统计检验与零模型对比
大数据·前端·人工智能·算法·云原生·螺旋生成论·螺旋相位
亿元程序员12 小时前
游戏引擎都没用!纯AI又上线了一款蚂蚁搬家小游戏!
前端
福兮说12 小时前
JS 正则的六个坑:带 g 的 test() 一真一假、空匹配死循环、replace 里的 $
开发语言·前端·javascript·正则表达式