Files
k3GPT/main/ui/agent_catalog.aps
2025-11-19 19:43:01 +08:00

207 lines
11 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>