Merge branch 'feature/linzhijun_扣子智能体_0710' of https://gta.lvfunai.com/ai-team/lingji-work-fe into feature/linzhijun_扣子智能体_0710
# Conflicts: # src/views/agent/work-flow/index.vue
This commit is contained in:
26
src/directives/getImageMainColor.ts
Normal file
26
src/directives/getImageMainColor.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { getImageMainColor } from '@/utils/tools';
|
||||
|
||||
// 创建图片主色调指令
|
||||
const imageMainColorDirective = {
|
||||
mounted(el: HTMLElement, binding: any) {
|
||||
const imageUrl = binding.value;
|
||||
|
||||
if (!imageUrl) return;
|
||||
|
||||
getImageMainColor(imageUrl)
|
||||
.then(color => {
|
||||
el.style.backgroundColor = color;
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('获取图片主色调失败:', error);
|
||||
// 设置默认背景色
|
||||
el.style.backgroundColor = '#E6E6E8';
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export default {
|
||||
install(app: any) {
|
||||
app.directive('image-main-color', imageMainColorDirective);
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,3 @@
|
||||
import getImageMainColor from './getImageMainColor';
|
||||
|
||||
export { getImageMainColor };
|
||||
|
||||
@ -5,9 +5,10 @@
|
||||
import App from './App.vue';
|
||||
import router from './router';
|
||||
import store from './stores';
|
||||
import * as directives from '@/directives';
|
||||
|
||||
import NoData from '@/components/no-data';
|
||||
import SvgIcon from "@/components/svg-icon";
|
||||
import SvgIcon from '@/components/svg-icon';
|
||||
|
||||
import '@/api/index';
|
||||
import '@arco-design/web-vue/dist/arco.css'; // Arco 默认样式
|
||||
@ -15,7 +16,7 @@ import './core';
|
||||
|
||||
import 'normalize.css';
|
||||
import 'uno.css';
|
||||
import 'virtual:svg-icons-register'
|
||||
import 'virtual:svg-icons-register';
|
||||
|
||||
// import '@/styles/vars.css'; // 优先加载
|
||||
|
||||
@ -26,4 +27,7 @@ app.component('SvgIcon', SvgIcon);
|
||||
|
||||
app.use(store);
|
||||
app.use(router);
|
||||
|
||||
Object.keys(directives).forEach((k) => app.use(directives[k])); // 注册指令
|
||||
|
||||
app.mount('#app');
|
||||
|
||||
@ -110,3 +110,173 @@ export function downloadByUrl(url: string, filename?: string) {
|
||||
export function genRandomId() {
|
||||
return `id_${Date.now()}_${Math.floor(Math.random() * 10000)}`;
|
||||
}
|
||||
|
||||
export function getImageMainColor(imageUrl: string): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const img = new Image();
|
||||
img.crossOrigin = 'Anonymous'; // 处理跨域图片
|
||||
img.src = imageUrl;
|
||||
|
||||
img.onload = () => {
|
||||
// 创建画布缩小图片尺寸以提高性能
|
||||
const canvas = document.createElement('canvas');
|
||||
const maxDimension = 100; // 最大尺寸为100px
|
||||
let width = img.width;
|
||||
let height = img.height;
|
||||
|
||||
// 按比例缩小图片
|
||||
if (width > height) {
|
||||
if (width > maxDimension) {
|
||||
height *= maxDimension / width;
|
||||
width = maxDimension;
|
||||
}
|
||||
} else {
|
||||
if (height > maxDimension) {
|
||||
width *= maxDimension / height;
|
||||
height = maxDimension;
|
||||
}
|
||||
}
|
||||
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
if (!ctx) {
|
||||
reject(new Error('Canvas context not available'));
|
||||
return;
|
||||
}
|
||||
|
||||
// 绘制缩小后的图片
|
||||
ctx.drawImage(img, 0, 0, width, height);
|
||||
|
||||
// 获取图片数据
|
||||
const imageData = ctx.getImageData(0, 0, width, height);
|
||||
const data = imageData.data;
|
||||
|
||||
// 使用中位数切分法提取主色调
|
||||
const colorGroups = medianCut(data, 8); // 分成8组
|
||||
|
||||
// 找出最大的颜色组
|
||||
let maxGroup = colorGroups[0];
|
||||
for (let i = 1; i < colorGroups.length; i++) {
|
||||
if (colorGroups[i].count > maxGroup.count) {
|
||||
maxGroup = colorGroups[i];
|
||||
}
|
||||
}
|
||||
|
||||
// 计算组内平均颜色
|
||||
const avgColor = {
|
||||
r: Math.round(maxGroup.sumR / maxGroup.count),
|
||||
g: Math.round(maxGroup.sumG / maxGroup.count),
|
||||
b: Math.round(maxGroup.sumB / maxGroup.count)
|
||||
};
|
||||
|
||||
resolve(`rgb(${avgColor.r},${avgColor.g},${avgColor.b})`);
|
||||
};
|
||||
|
||||
img.onerror = () => {
|
||||
reject(new Error('Failed to load image'));
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 中位数切分法进行色彩量化
|
||||
* @param data 图像数据
|
||||
* @param levels 切分级别
|
||||
* @returns 颜色组
|
||||
*/
|
||||
function medianCut(data: Uint8ClampedArray, levels: number): any[] {
|
||||
const colors = [];
|
||||
for (let i = 0; i < data.length; i += 4) {
|
||||
const r = data[i];
|
||||
const g = data[i + 1];
|
||||
const b = data[i + 2];
|
||||
const a = data[i + 3];
|
||||
|
||||
// 跳过透明像素
|
||||
if (a < 128) continue;
|
||||
|
||||
colors.push({
|
||||
r, g, b,
|
||||
count: 1,
|
||||
sumR: r, sumG: g, sumB: b
|
||||
});
|
||||
}
|
||||
|
||||
// 如果没有颜色数据,返回默认白色
|
||||
if (colors.length === 0) {
|
||||
return [{
|
||||
count: 1,
|
||||
sumR: 255, sumG: 255, sumB: 255
|
||||
}];
|
||||
}
|
||||
|
||||
// 开始中位数切分
|
||||
let colorGroups = [{
|
||||
colors,
|
||||
count: colors.length,
|
||||
sumR: colors.reduce((sum, c) => sum + c.r, 0),
|
||||
sumG: colors.reduce((sum, c) => sum + c.g, 0),
|
||||
sumB: colors.reduce((sum, c) => sum + c.b, 0)
|
||||
}];
|
||||
|
||||
for (let i = 0; i < levels; i++) {
|
||||
const newGroups = [];
|
||||
|
||||
for (const group of colorGroups) {
|
||||
if (group.colors.length <= 1) {
|
||||
newGroups.push(group);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 找出颜色范围最大的通道
|
||||
const rMin = Math.min(...group.colors.map(c => c.r));
|
||||
const rMax = Math.max(...group.colors.map(c => c.r));
|
||||
const gMin = Math.min(...group.colors.map(c => c.g));
|
||||
const gMax = Math.max(...group.colors.map(c => c.g));
|
||||
const bMin = Math.min(...group.colors.map(c => c.b));
|
||||
const bMax = Math.max(...group.colors.map(c => c.b));
|
||||
|
||||
const rRange = rMax - rMin;
|
||||
const gRange = gMax - gMin;
|
||||
const bRange = bMax - bMin;
|
||||
|
||||
let sortChannel = 'r';
|
||||
if (gRange > rRange && gRange > bRange) {
|
||||
sortChannel = 'g';
|
||||
} else if (bRange > rRange && bRange > gRange) {
|
||||
sortChannel = 'b';
|
||||
}
|
||||
|
||||
// 按最大范围通道排序
|
||||
group.colors.sort((a, b) => a[sortChannel] - b[sortChannel]);
|
||||
|
||||
// 切分中位数
|
||||
const mid = Math.floor(group.colors.length / 2);
|
||||
const group1 = group.colors.slice(0, mid);
|
||||
const group2 = group.colors.slice(mid);
|
||||
|
||||
// 添加新组
|
||||
newGroups.push({
|
||||
colors: group1,
|
||||
count: group1.length,
|
||||
sumR: group1.reduce((sum, c) => sum + c.r, 0),
|
||||
sumG: group1.reduce((sum, c) => sum + c.g, 0),
|
||||
sumB: group1.reduce((sum, c) => sum + c.b, 0)
|
||||
});
|
||||
|
||||
newGroups.push({
|
||||
colors: group2,
|
||||
count: group2.length,
|
||||
sumR: group2.reduce((sum, c) => sum + c.r, 0),
|
||||
sumG: group2.reduce((sum, c) => sum + c.g, 0),
|
||||
sumB: group2.reduce((sum, c) => sum + c.b, 0)
|
||||
});
|
||||
}
|
||||
|
||||
colorGroups = newGroups;
|
||||
}
|
||||
|
||||
return colorGroups;
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
</div>
|
||||
<div class="workflow-container">
|
||||
<div class="left-wap mr-24px" v-if="isCollapsed == false">
|
||||
<div class="w-full w-100% mb-15px bg-#F0EDFF h-160px rounded-8px">
|
||||
<div class="w-full w-100% mb-15px h-160px rounded-8px bg-#E6E6E8" v-image-main-color="cozeInfo.image_url">
|
||||
<img v-if="cozeInfo?.image_url" :src="cozeInfo?.image_url" class= "w-full h-full object-contain" />
|
||||
</div>
|
||||
<div class="content mb-15px">
|
||||
@ -43,7 +43,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content" id="coze-chat-container"></div>
|
||||
<div class="coze-content" id="coze-chat-container"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -243,21 +243,17 @@
|
||||
gap: 10px;
|
||||
display: inline-flex;
|
||||
|
||||
.body {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.content {
|
||||
.coze-content {
|
||||
align-self: stretch;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
display: inline-flex;
|
||||
:deep(.coze-chat-sdk) {
|
||||
border-radius: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.form {
|
||||
width: 400px;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="agent-wrap relative">
|
||||
<div class="agent-wrap relative h-full">
|
||||
<a-input
|
||||
v-model="query.name"
|
||||
@blur="getData()"
|
||||
@ -23,10 +23,10 @@
|
||||
:xxl="4"
|
||||
v-for="(product, k) in item.agent_products">
|
||||
<div class="card-container cursor-pointer !h-252px" @click="goDetail(product?.type, product?.id)">
|
||||
<img
|
||||
class="card-image h-120px object-contain w-100% mb-8px bg-#F0EDFF"
|
||||
:src="product?.image_url"
|
||||
/>
|
||||
<div class="card-image h-120px w-100% bg-cover bg-center bg-#E6E6E8" v-image-main-color="product.image_url">
|
||||
<img class="object-contain h-full w-100% mb-8px" :src="product?.image_url"/>
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<div class="card-title mb-4px">{{ product?.name }}</div>
|
||||
<TextoverTips :context="product.description" class="card-description mb-8px color-#737478 text-14px lh-22px font-400" :line="2" />
|
||||
|
||||
@ -6,8 +6,8 @@
|
||||
</div>
|
||||
<div class="workflow-container">
|
||||
<div class="left-wap mr-24px" v-if="isCollapsed == false">
|
||||
<div class="w-full w-100% mb-15px bg-#F0EDFF h-160px rounded-8px">
|
||||
<img v-if="cozeInfo?.image_url" :src="cozeInfo?.image_url" class="w-full h-full object-contain" />
|
||||
<div class="w-full w-100% mb-15px h-160px rounded-8px bg-#E6E6E8" v-image-main-color="cozeInfo.image_url">
|
||||
<img v-if="cozeInfo?.image_url" :src="cozeInfo?.image_url" class= "w-full h-full object-contain" />
|
||||
</div>
|
||||
<div class="content mb-15px">
|
||||
<div class="title-body">
|
||||
@ -104,7 +104,7 @@ import { marked } from 'marked';
|
||||
import DOMPurify from 'dompurify';
|
||||
import menuFold from '@/assets/svg/menu-fold.svg';
|
||||
import menuUnfold from '@/assets/svg/menu-unfold.svg';
|
||||
import { formatNumberShow } from '@/utils/tools';
|
||||
import { formatNumberShow } from "@/utils/tools"
|
||||
|
||||
const formFields = ref({});
|
||||
const history = ref([]);
|
||||
|
||||
Reference in New Issue
Block a user