示例效果:

调色盘组件代码:使用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" />