207 lines
11 KiB
Plaintext
207 lines
11 KiB
Plaintext
<div id="agent_catalog" x-data="agent_catalog" @hexcolor="setColor($event.detail)" x-cloak class="flex w-full max-w-xs flex-col gap-1">
|
||
<div class="relative flex w-full" x-on:keydown="handleKeydownOnOptions($event)" x-on:keydown.esc.window="isOpen = false, openedWithKeyboard = false">
|
||
|
||
<!-- Country Selector -->
|
||
<div>
|
||
<!-- Trigger Button -->
|
||
<button type="button" class="inline-flex w-max items-center justify-between gap-2 whitespace-nowrap rounded-l border border-zinc-300 border-r-0 bg-zinc-100 px-4 py-2 text-sm font-medium tracking-wide text-neutral-600 transition hover:opacity-75 focus-visible:outline-hidden focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700 dark:border-zinc-700 dark:bg-zinc-800/50 dark:text-zinc-200 dark:focus-visible:outline-sky-600" role="combobox" aria-controls="countriesList" aria-haspopup="listbox" x-on:click="isOpen = ! isOpen" x-on:keydown.down.prevent="openedWithKeyboard = true" x-on:keydown.enter.prevent="openedWithKeyboard = true" x-on:keydown.space.prevent="openedWithKeyboard = true" x-bind:aria-expanded="isOpen || openedWithKeyboard" x-bind:aria-label="selectedOption ? selectedOption.value : 'Please Select'">
|
||
<!-- Selected Country Flag -->
|
||
<span class="h-4 w-6" :style="{backgroundColor: selectedOption.value}"></span>
|
||
<!-- Chevron -->
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-5 shrink-0" aria-hidden="true">
|
||
<path fill-rule="evenodd" d="M5.22 8.22a.75.75 0 0 1 1.06 0L10 11.94l3.72-3.72a.75.75 0 1 1 1.06 1.06l-4.25 4.25a.75.75 0 0 1-1.06 0L5.22 9.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd">
|
||
</svg>
|
||
</button>
|
||
<!-- Dropdown -->
|
||
<div x-cloak x-show="isOpen || openedWithKeyboard" id="countriesList" class="absolute left-0 top-11 z-10 w-full overflow-hidden rounded border border-zinc-300 bg-zinc-100 dark:border-zinc-700 dark:bg-zinc-800" role="listbox" aria-label="countries list" x-on:click.outside="isOpen = false, openedWithKeyboard = false" x-on:keydown.down.prevent="$focus.wrap().next()" x-on:keydown.up.prevent="$focus.wrap().previous()" x-transition x-trap="openedWithKeyboard">
|
||
<!-- Search -->
|
||
<div class="relative">
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="1.5" class="absolute left-5 top-1/2 size-5 -translate-y-1/2 text-neutral-600/50 dark:text-zinc-200/50" aria-hidden="true">
|
||
<path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z"/>
|
||
</svg>
|
||
<input type="text" class="w-full border-b border-zinc-300 bg-zinc-100 py-2.5 pl-10 pr-4 text-sm text-neutral-600 focus:outline-hidden focus-visible:border-sky-700 disabled:cursor-not-allowed disabled:opacity-75 dark:border-zinc-700 dark:bg-zinc-800 dark:text-zinc-200 dark:focus-visible:border-sky-600" name="searchField" aria-label="Search" x-on:input="getFilteredOptions($el.value)" x-ref="searchField" placeholder="Search"/>
|
||
</div>
|
||
<!-- Options -->
|
||
<ul class="flex max-h-44 flex-col overflow-y-auto">
|
||
<li class="hidden px-4 py-2 text-sm text-neutral-600 dark:text-zinc-200" x-ref="noResultsMessage">
|
||
<span>No matches found</span>
|
||
</li>
|
||
<template x-for="(item, index) in options">
|
||
<li class="inline-flex justify-between gap-6 bg-zinc-100 px-4 py-2 text-sm text-neutral-600 hover:bg-zinc-800/5 hover:text-neutral-900 focus-visible:bg-zinc-800/5 focus-visible:text-neutral-900 focus-visible:outline-hidden dark:bg-zinc-800 dark:text-zinc-200 dark:hover:bg-zinc-100/5 dark:hover:text-zinc-50 dark:focus-visible:bg-zinc-100/10 dark:focus-visible:text-zinc-50" role="option" x-on:click="setSelectedOption(item)" x-on:keydown.enter="setSelectedOption(item)" x-bind:id="'option-' + index" tabindex="0">
|
||
<div class="flex items-center gap-2">
|
||
<!-- Flag Image -->
|
||
<span class="h-4 w-6" :style="{backgroundColor: item.value}"></span>
|
||
|
||
<!-- Label -->
|
||
<span x-bind:class="selectedOption == item ? 'font-bold' : null" x-text="item.key"></span>
|
||
<span class="font-bold" x-text="'(' + item.value + ')'"></span>
|
||
<!-- Screen reader 'selected' indicator -->
|
||
<span class="sr-only" x-text="selectedOption == item ? 'selected' : null"></span>
|
||
</div>
|
||
<!-- Checkmark -->
|
||
<svg x-show="selectedOption == item" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="2" class="size-4" aria-hidden="true">
|
||
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5"/>
|
||
</svg>
|
||
</li>
|
||
</template>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<input id="phoneNumber" type="text" class="w-full border-zinc-300 rounded-r border rounded-l-none bg-zinc-100 px-2.5 py-2 text-sm text-neutral-600 focus:outline-hidden focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700 disabled:cursor-not-allowed disabled:opacity-75 dark:border-zinc-700 dark:bg-zinc-800/50 dark:text-zinc-200 dark:focus-visible:outline-sky-600" x-ref="phoneNumber" autocomplete="phone" placeholder="标签 颜色值"/>
|
||
|
||
<span class="p-2 cursor-pointer text-sm" @click="colorS ? colorS=false : colorS=true">🎨</span>
|
||
<div x-show="colorS" id="colorPickerContainer">
|
||
<canvas id="colorPicker" width="300" height="300"></canvas>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<style>
|
||
#colorPickerContainer {
|
||
position: relative;
|
||
top: -250px;
|
||
left: 100px;
|
||
cursor: pointer;
|
||
}
|
||
</style>
|
||
|
||
|
||
<script>
|
||
// agent的分类
|
||
function agent_catalog(){
|
||
return {
|
||
allOptions: [
|
||
{ key: '知识提炼', value: 'blue'},
|
||
],
|
||
options: [],
|
||
isOpen: false,
|
||
openedWithKeyboard: false,
|
||
colorS:false,
|
||
selectedOption: {},
|
||
init(){
|
||
// 初始化绘制选色器
|
||
drawColorWheel();
|
||
axios.get('/api/gcfg_mem').then(response => {
|
||
let kagents = response.data.data.agent_catalogs;
|
||
|
||
this.options = Object.entries(kagents).map(([key, value]) => ({
|
||
key,value
|
||
}));
|
||
this.allOptions= this.options;
|
||
if (this.kagent.catalog){
|
||
const ok_catalogs = this.options.filter(catalog => catalog.key==this.kagent.catalog);
|
||
if (ok_catalogs.length==1){
|
||
this.setSelectedOption(ok_catalogs[0]);
|
||
}else{
|
||
this.setSelectedOption({key:this.kagent.catalog,value:this.kagent.oth});
|
||
}
|
||
|
||
}else{
|
||
this.setSelectedOption(this.options[0]);
|
||
}
|
||
}).catch(error => {
|
||
console.error('Error fetching data:', error);
|
||
throw error; // 重新抛出错误以便调用者可以处理
|
||
});
|
||
},
|
||
setColor(data){
|
||
console.info(data);
|
||
this.selectedOption.value=data.hexValue;
|
||
let catalog = this.$refs.phoneNumber.value.split(" ")
|
||
this.$refs.phoneNumber.value = catalog[0] +' '+data.hexValue;
|
||
|
||
},
|
||
setSelectedOption(option) {
|
||
this.selectedOption = option
|
||
this.isOpen = false
|
||
this.openedWithKeyboard = false
|
||
this.$refs.phoneNumber.value = option.key +' '+option.value
|
||
},
|
||
getFilteredOptions(query) {
|
||
this.options = this.allOptions.filter(
|
||
(option) =>
|
||
option.key.toLowerCase().includes(query.toLowerCase()) ||
|
||
option.value.includes(query.toLowerCase()),
|
||
)
|
||
if (this.options.length === 0) {
|
||
this.$refs.noResultsMessage.classList.remove('hidden')
|
||
} else {
|
||
this.$refs.noResultsMessage.classList.add('hidden')
|
||
}
|
||
},
|
||
handleKeydownOnOptions(event) {
|
||
// if the user presses backspace or the alpha-numeric keys, focus on the search field
|
||
if ((event.keyCode >= 65 && event.keyCode <= 90) || (event.keyCode >= 48 && event.keyCode <= 57) || event.keyCode === 8) {
|
||
this.$refs.searchField.focus()
|
||
}
|
||
},
|
||
}
|
||
}
|
||
|
||
|
||
|
||
// 绘制颜色盘 (HSL 色轮)
|
||
function drawColorWheel() {
|
||
//颜色盘的渲染
|
||
const canvas = document.getElementById('colorPicker');
|
||
const ctx = canvas.getContext('2d');
|
||
|
||
const centerX = canvas.width / 2;
|
||
const centerY = canvas.height / 2;
|
||
const radius = Math.min(centerX, centerY);
|
||
|
||
for (let angle = 0; angle < 360; angle++) {
|
||
const startAngle = (angle - 2) * Math.PI / 180;
|
||
const endAngle = angle * Math.PI / 180;
|
||
|
||
ctx.beginPath();
|
||
ctx.moveTo(centerX, centerY);
|
||
ctx.arc(centerX, centerY, radius, startAngle, endAngle);
|
||
ctx.closePath();
|
||
|
||
// 创建径向渐变模拟饱和度和亮度变化
|
||
const gradient = ctx.createRadialGradient(
|
||
centerX, centerY, 0,
|
||
centerX, centerY, radius
|
||
);
|
||
gradient.addColorStop(0, 'white'); // 中心为白色
|
||
gradient.addColorStop(1, `hsl(${angle}, 100%, 50%)`); // 外圈为纯色
|
||
|
||
ctx.fillStyle = gradient;
|
||
ctx.fill();
|
||
}
|
||
|
||
// 处理点击事件以获取颜色
|
||
canvas.addEventListener('click', function(event) {
|
||
const rect = canvas.getBoundingClientRect();
|
||
const x = event.clientX - rect.left;
|
||
const y = event.clientY - rect.top;
|
||
|
||
const pixel = ctx.getImageData(x, y, 1, 1).data;
|
||
const r = pixel[0];
|
||
const g = pixel[1];
|
||
const b = pixel[2];
|
||
|
||
const hexColor = rgbToHex(r, g, b);
|
||
console.info(hexColor);
|
||
|
||
// 1. 创建自定义事件
|
||
const hex_color_event = new CustomEvent('hexcolor', {
|
||
bubbles: true,
|
||
detail: { hexValue:hexColor }
|
||
});
|
||
|
||
// 2. 发送事件(dispatch)
|
||
canvas.dispatchEvent(hex_color_event);
|
||
console.info(hex_color_event);
|
||
});
|
||
}
|
||
|
||
// 将 RGB 转换为 HEX
|
||
function rgbToHex(r, g, b) {
|
||
return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1).toUpperCase();
|
||
}
|
||
|
||
</script> |