自定义调色盘组件

示例效果:

调色盘组件代码:使用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" />
相关推荐
Csvn18 分钟前
🧩「找不到模块」排查全记录——Monorepo 下 TypeScript 路径别名的 5 种「不通」与根治方案
前端
腻害兔25 分钟前
【若依项目-产品经理视角】RuoYi-Vue-Pro 源码拆解:字典、短信、邮件、通知——后台系统的“基础设施四件套“!
java·前端·vue.js·产品经理·ai编程
CodexDave41 分钟前
MySQL事务隔离级别与MVCC机制解析
前端·数据库·mysql·nginx·性能优化·负载均衡
Ai_easygo2 小时前
AI Agent开发入门——从ReAct到Tool Calling,拆解Agent的底层运行逻辑
前端·人工智能·react.js
研☆香2 小时前
分析制作html页面,如何划分页面结构
前端
腻害兔2 小时前
【若依项目-产品经理视角】RuoYi-Vue-Pro 源码拆解:支付模块 yudao-module-pay,一个让产品经理都看懂的支付中台设计
java·前端·vue.js·产品经理·ai编程
kyriewen2 小时前
我扒了最近的前端面经——2026年面试不背八股文了,考这5样
前端·面试·ai编程
IT_陈寒2 小时前
Redis的持久化配置把我坑惨了:你以为数据安全了?
前端·人工智能·后端
小徐_23333 小时前
uni-app 项目别再从零搭了!3 个 Wot UI 起手模板怎么选?
前端·uni-app
张元清3 小时前
React useResizeObserver Hook:追踪元素尺寸变化(2026)
javascript·react.js