当前位置: 首页 > news >正文

Vue3实现高仿word自定义颜色选择器组件(支持 v-model)

目录

    • Vue3实现高仿word自定义颜色选择器组件(支持 v-model)
    • 需求分析
    • 大致效果
    • 需求功能
    • 实现所需技术
    • 从UI哪里拿到主题颜色标准色
    • 进行子主组件的v-model实现
    • 子组件布局实现
    • 子组件样式实现
    • 子组件全部代码:
    • 父组件调用方式
    • 完结

Vue3实现高仿word自定义颜色选择器组件(支持 v-model)

需求分析

最近接到一个需求,前端拿到AI组通过检测算法得到的瑕疵数据,为了标识每个瑕疵,通过设置瑕疵的颜色进行标志,产品要求仿照word的颜色设置来进行设置取色器。

大致效果

Vue3实现高仿word自定义颜色选择器组件(支持 v-model)
Vue3实现高仿word自定义颜色选择器组件(支持 v-model)

Vue3实现高仿word自定义颜色选择器组件(支持 v-model)

需求功能

  1. 颜色选择:用户可以通过点击预定义的颜色块或使用颜色选择器选择颜色。
  2. 双向绑定:子组件支持 v-model,可以与父组件进行双向数据绑定。
  3. 弹出式设计:颜色选择器以弹窗形式展示,提供更好的用户体验。
  4. 可以选择默认主题色,标准色,及其自定义其他字体颜色。
  5. 样式优化:通过 SCSS 样式美化组件。

实现所需技术

  1. vue3+elementPlus
  2. elementPlusPopconfirm 气泡确认框。
  3. elementPlusColorPicker 颜色选择器。

从UI哪里拿到主题颜色标准色

// 主题色
const themeColors = ref(["#FFFFFF","#000000","#E7E6E6","#44546A","#4874CB","#EE822F","#F2BA02","#75BD42","#30C0B4","#E54C5E",
]);
// 标准色 Standard color
const standardColors = ref(["#C00000","#FF0000","#FFC000","#FFFF00","#92D050","#00B050","#00B0F0","#0070C0","#002060","#7030A0",
]);
// 主题渐变色
const gradientColors = ref([["#F2F2F2", "#D9D9D9", "#BFBFBF", "#A6A6A6", "#808080"],["#808080", "#595959", "#404040", "#262626", "#0D0D0D"],["#D0CECE", "#AFABAB", "#767171", "#3B3838", "#181717"],["#D6DCE5", "#ADB9CA", "#8497B0", "#333F50", "#222A35"],["#DAE3F5", "#B6C7EA", "#91ACE0", "#2E54A1", "#1E386B"],["#FCE6D5", "#F8CDAC", "#F5B482", "#C65F10", "#843F0B"],["#FFF2CA", "#FEE695", "#FED961", "#B68C02", "#795D01"],["#E3F2D9", "#C8E5B3", "#ACD78E", "#588E32", "#3B5F21"],["#D4F4F2", "#A9E9E4", "#7DDFD7", "#249087", "#18605A"],["#FADBDF", "#F5B7BF", "#EF949E", "#C81D31", "#851321"],
]);

进行子主组件的v-model实现

const emit = defineEmits(["update:modelValue"]);
const props = defineProps({modelValue: {type: String,default: "#FFFFFF",},
});
watch(() => props.modelValue,(newVal) => {// console.log("modelValue changed:", newVal);colorValue.value = newVal; // 更新子组件的内部状态}
);
const colorSelect = (val: any) => {colorValue.value = val;emit("update:modelValue", colorValue.value); // 更新父组件中的值handleCancel();
};

子组件布局实现

使用el-popover套一层,然后遍历主题色标准色进行flex布局,如果弹窗组件使用了:visible="popconfirmVisible" 则需要配置trigger="focus"进行鼠标移出弹窗隐藏弹窗。

<template><div class="color-picker-wrapper" ref="wrapperRef"><el-popoverwidth="205px"ref="colorPopover"@confirm="handleConfirm"@cancel="handleCancel"v-model:visible="popconfirmVisible"trigger="focus"><template #reference><div @click="handleClick" class="color-box"><divclass="color-pice":style="{ backgroundColor: colorValue }"></div></div></template><div style="width: 180px; text-align: left"><divstyle="font-size: 12px;width: 100%;background-color: #f4f5f7;height: 20px;line-height: 20px;padding-left: 5px;margin-bottom: 5px;color: #444e63;">主题颜色</div><divstyle="height: 20px; display: flex; justify-content: space-between"><divclass="theme-color-item"v-for="(item, index) in themeColors"@click="colorSelect(item)":key="index":style="{ background: item }"></div></div><div style="width: 100%; display: flex; justify-content: space-between"><divv-for="(item, index) in gradientColors":key="index"style="height: 64px;width: 12px;display: flex;flex-direction: column;justify-content: space-between;"><divv-for="(item1, index1) in item":key="index1"@click="colorSelect(item1)":style="{ background: item1 }"class="theme-color-block"></div></div></div></div><div class="theme-color">标准色</div><div style="height: 20px; display: flex; justify-content: space-between"><divclass="theme-color-item"v-for="(item, index) in standardColors":key="index"@click="colorSelect(item)":style="{ background: item }"></div></div><el-divider style="margin: 0; margin-top: 5px"></el-divider><div class="colorPalette-box"><!-- <img :src="'../../assets/image/colorPalette.png':'../../assets/image/colorPalette.png'" style="width: 26px" /> --><div class="colorPalette-text" @click="showColor" style="display: flex"><div style="" class="colorPalette-icon"></div><div style="width: 90px">其他字体颜色...</div><el-color-picker:teleported="false"v-model="colorValue"@change="colorChange"size="small"/></div></div></el-popover></div>
</template>

子组件样式实现

样式中,需要修改elementPlus的组件,其中最需要修改el-color-picker使其拉长得以点击其他字体颜色...div块唤醒弹窗。


<style lang="scss" scoped>
.color-box:hover {border-color: #409eff;transition: 0.5s;
}
.color-box {width: 22px;height: 22px;background-color: #fff;border: 1px solid #dcdfe6;border-radius: 2px;display: flex;justify-content: center;align-items: center;cursor: pointer;.color-pice {width: 14px;height: 14px;}
}
::v-deep .el-popconfirm__action {margin-top: 0px !important;text-align: left;
}
.theme-color-block {width: 12px;height: 12px;cursor: pointer;border: 1px solid #dcdfe6;
}
.theme-color-block:hover {border: 1px solid #ffa800 !important;transition: 0.5s !important;
}
.theme-color-item {width: 12px;height: 12px;cursor: pointer;border: 1px solid #dcdfe6;
}
.theme-color-item:hover {border: 1px solid #ffa800;transition: 0.5s;
}
.colorPalette-box {display: flex;font-size: 12px;width: 100%;cursor: pointer;height: 26px;line-height: 26px;padding-left: 2px;margin-bottom: 5px;margin-top: 6px;text-align: left;color: #444e63;
}
.colorPalette-box:hover {color: #165dff;.colorPalette-icon {background-image: url("../../assets/image/colorPaletteActive.png");}
}
.colorPalette-box:hover {background: #f2f2f2;transition: 0.3s;
}
.colorPalette-icon {width: 26px;height: 26px;position: relative;left: -5px;background-image: url("../../assets/image/colorPalette.png");background-repeat: repeat; /* 在两个方向上平铺背景图片 */background-size: cover; /* 覆盖整个元素,可能会被裁剪以适应尺寸 */background-position: center; /* 背景图片居中 */
}
.colorPalette-text {position: relative;margin-left: 4px;
}
.theme-color {font-size: 12px;width: 100%;background-color: #f4f5f7;height: 20px;text-align: left;line-height: 20px;padding-left: 5px;margin-bottom: 5px;margin-top: 6px;color: #444e63;
}
::v-deep .el-color-picker__panel {position: absolute;top: -0px !important;left: 180px !important;
}
::v-deep .el-color-picker__trigger {width: 179px;border: none;position: relative;top: 0px;left: -142px;z-index: 10000;
}
::v-deep .el-color-picker__color {display: none !important;
}
</style>

子组件全部代码:

<template><div class="color-picker-wrapper" ref="wrapperRef"><el-popoverwidth="205px"ref="colorPopover"@confirm="handleConfirm"@cancel="handleCancel"v-model:visible="popconfirmVisible"trigger="focus"><template #reference><div @click="handleClick" class="color-box"><divclass="color-pice":style="{ backgroundColor: colorValue }"></div></div></template><div style="width: 180px; text-align: left"><divstyle="font-size: 12px;width: 100%;background-color: #f4f5f7;height: 20px;line-height: 20px;padding-left: 5px;margin-bottom: 5px;color: #444e63;">主题颜色</div><divstyle="height: 20px; display: flex; justify-content: space-between"><divclass="theme-color-item"v-for="(item, index) in themeColors"@click="colorSelect(item)":key="index":style="{ background: item }"></div></div><div style="width: 100%; display: flex; justify-content: space-between"><divv-for="(item, index) in gradientColors":key="index"style="height: 64px;width: 12px;display: flex;flex-direction: column;justify-content: space-between;"><divv-for="(item1, index1) in item":key="index1"@click="colorSelect(item1)":style="{ background: item1 }"class="theme-color-block"></div></div></div></div><div class="theme-color">标准色</div><div style="height: 20px; display: flex; justify-content: space-between"><divclass="theme-color-item"v-for="(item, index) in standardColors":key="index"@click="colorSelect(item)":style="{ background: item }"></div></div><el-divider style="margin: 0; margin-top: 5px"></el-divider><div class="colorPalette-box"><!-- <img :src="'../../assets/image/colorPalette.png':'../../assets/image/colorPalette.png'" style="width: 26px" /> --><div class="colorPalette-text" @click="showColor" style="display: flex"><div style="" class="colorPalette-icon"></div><div style="width: 90px">其他字体颜色...</div><el-color-picker:teleported="false"v-model="colorValue"@change="colorChange"size="small"/></div></div></el-popover></div>
</template><script setup lang="ts">
import { onUnmounted, ref, watch } from "vue";
const emit = defineEmits(["update:modelValue"]);
const props = defineProps({modelValue: {type: String,default: "#FFFFFF",},
});
watch(() => props.modelValue,(newVal) => {// console.log("modelValue changed:", newVal);colorValue.value = newVal; // 更新子组件的内部状态}
);
const colorValue = ref(props.modelValue);
const colorPopover: any = ref(null);
// 主题色
const themeColors = ref(["#FFFFFF","#000000","#E7E6E6","#44546A","#4874CB","#EE822F","#F2BA02","#75BD42","#30C0B4","#E54C5E",
]);
// 标准色 Standard color
const standardColors = ref(["#C00000","#FF0000","#FFC000","#FFFF00","#92D050","#00B050","#00B0F0","#0070C0","#002060","#7030A0",
]);
// 主题渐变色
const gradientColors = ref([["#F2F2F2", "#D9D9D9", "#BFBFBF", "#A6A6A6", "#808080"],["#808080", "#595959", "#404040", "#262626", "#0D0D0D"],["#D0CECE", "#AFABAB", "#767171", "#3B3838", "#181717"],["#D6DCE5", "#ADB9CA", "#8497B0", "#333F50", "#222A35"],["#DAE3F5", "#B6C7EA", "#91ACE0", "#2E54A1", "#1E386B"],["#FCE6D5", "#F8CDAC", "#F5B482", "#C65F10", "#843F0B"],["#FFF2CA", "#FEE695", "#FED961", "#B68C02", "#795D01"],["#E3F2D9", "#C8E5B3", "#ACD78E", "#588E32", "#3B5F21"],["#D4F4F2", "#A9E9E4", "#7DDFD7", "#249087", "#18605A"],["#FADBDF", "#F5B7BF", "#EF949E", "#C81D31", "#851321"],
]);
const popconfirmVisible = ref(false);
const showColor = () => {};
const colorChange = (val: any) => {emit("update:modelValue", colorValue.value); // 更新父组件中的值handleCancel();
};
const handleClick = () => {popconfirmVisible.value = true; // 显示 Popconfirm
};const handleConfirm = () => {popconfirmVisible.value = false; // 隐藏 Popconfirm
};
const colorSelect = (val: any) => {colorValue.value = val;emit("update:modelValue", colorValue.value); // 更新父组件中的值handleCancel();
};
const handleCancel = () => {popconfirmVisible.value = false; // 隐藏 Popconfirm
};
const clicked = ref(false);
</script>
<style lang="scss" scoped>
.color-box:hover {border-color: #409eff;transition: 0.5s;
}
.color-box {width: 22px;height: 22px;background-color: #fff;border: 1px solid #dcdfe6;border-radius: 2px;display: flex;justify-content: center;align-items: center;cursor: pointer;.color-pice {width: 14px;height: 14px;}
}
::v-deep .el-popconfirm__action {margin-top: 0px !important;text-align: left;
}
.theme-color-block {width: 12px;height: 12px;cursor: pointer;border: 1px solid #dcdfe6;
}
.theme-color-block:hover {border: 1px solid #ffa800 !important;transition: 0.5s !important;
}
.theme-color-item {width: 12px;height: 12px;cursor: pointer;border: 1px solid #dcdfe6;
}
.theme-color-item:hover {border: 1px solid #ffa800;transition: 0.5s;
}
.colorPalette-box {display: flex;font-size: 12px;width: 100%;cursor: pointer;height: 26px;line-height: 26px;padding-left: 2px;margin-bottom: 5px;margin-top: 6px;text-align: left;color: #444e63;
}
.colorPalette-box:hover {color: #165dff;.colorPalette-icon {background-image: url("../../assets/image/colorPaletteActive.png");}
}
.colorPalette-box:hover {background: #f2f2f2;transition: 0.3s;
}
.colorPalette-icon {width: 26px;height: 26px;position: relative;left: -5px;background-image: url("../../assets/image/colorPalette.png");background-repeat: repeat; /* 在两个方向上平铺背景图片 */background-size: cover; /* 覆盖整个元素,可能会被裁剪以适应尺寸 */background-position: center; /* 背景图片居中 */
}
.colorPalette-text {position: relative;margin-left: 4px;
}
.theme-color {font-size: 12px;width: 100%;background-color: #f4f5f7;height: 20px;text-align: left;line-height: 20px;padding-left: 5px;margin-bottom: 5px;margin-top: 6px;color: #444e63;
}
::v-deep .el-color-picker__panel {position: absolute;top: -0px !important;left: 180px !important;
}
::v-deep .el-color-picker__trigger {width: 179px;border: none;position: relative;top: 0px;left: -142px;z-index: 10000;
}
::v-deep .el-color-picker__color {display: none !important;
}
</style>

父组件调用方式

 <template>ColourSle v-model="selectedColor"></ColourSle>
</template><
<script lang="ts" setup>const ColourSle = defineAsyncComponent(() => import("../../components/colourSle/index.vue"));const selectedColor = ref("#00FF00");
</script>

使用效果:
Vue3实现高仿word自定义颜色选择器组件(支持 v-model)
后续代码待优化,优化后会替换当前代码。

完结

相关文章:

  • 矩阵运营:抢占市场与流量的利器
  • 如何避免IDEA每次打开新项目都重复配置Maven?
  • 【Java并发】【LinkedBlockingQueue】适合初学体质的LinkedBlockingQueue入门
  • C++ 基础知识 指针和函数(利用指针作函数的参数修改实参的值)
  • 代码随想录打卡|Day27(合并区间、单调递增的数字、监控二叉树)
  • 河道流量监测:雷达流量计与多普勒流量计的优劣势与技术特点分析
  • 【C语言】全局变量、静态本地变量
  • 常见的几种分块策略,每种策略都有适用场景和优缺点
  • 华为L410上制作内网镜像模板:搭建Apache服务器并上传离线WeChat包
  • Linux-05 半个月崩了三次 ubuntu 系统记录
  • 基于STM32、HAL库的ADS1256IDBR模数转换器ADC驱动程序设计
  • 常见的6种外挂获取执行时机方法介绍
  • 安全编排自动化与响应(SOAR):从事件响应到智能编排的技术实践
  • 常见游戏引擎介绍与对比
  • Linux的基础指令
  • Exposure Adjusted Incidence Rate (EAIR) 暴露调整发病率:精准量化疾病风险
  • 关于hbaseRegion和hbaseRowKey的一些处理
  • linux ptrace 图文详解(七) gdb、strace跟踪系统调用
  • Python解析地址中省市区街道
  • Codeforces Round 1020 (Div. 3) A-D
  • 日均新开三家“首店”,上海的“首发经济”密码是什么?
  • 航行警告!黄海南部进行实弹射击,禁止驶入
  • 精准滴灌“种企业”,苏南强县常熟新的进阶密码
  • 政治局会议:持续稳定和活跃资本市场
  • 出35万元为副县长的女友凑购房首付,青海一商人被判缓刑
  • 官宣一起打造智能汽车品牌后,华为喊话上汽要准备好足够产能