自定义调色盘组件

示例效果:

调色盘组件代码:使用input[type=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" />
相关推荐
SY_FC3 小时前
实现一个父组件引入了子组件,跳转到其他页面,其他页面返回回来重新加载子组件函数
java·前端·javascript
糟糕好吃3 小时前
我让 AI 操作网页之后,开始不想点按钮了
前端·javascript·后端
陈天伟教授3 小时前
人工智能应用- 天文学家的助手:08. 星系定位与分类
前端·javascript·数据库·人工智能·机器学习
VaJoy3 小时前
给到夯!前端工具链新标杆 Vite Plus 初探
前端·vite
颜酱4 小时前
BFS 与并查集实战总结:从基础框架到刷题落地
javascript·后端·算法
小彭努力中5 小时前
191.Vue3 + OpenLayers 实战:可控化版权信息(Attribution)详解与完整示例
前端·javascript·vue.js·#地图开发·#cesium
奇舞精选5 小时前
用去年 github 最火的 n8n 快速实现自动化推送工具
前端·agent
奇舞精选5 小时前
实践:如何为智能体推理引入外部决策步骤
前端·agent
无限大65 小时前
AI实战02:一个万能提示词模板,搞定90%的文案/设计/分析需求
前端·后端