// ==UserScript==
// @name KJL后台-商品列表页-功能和样式调整
// @namespace Violentmonkey Scripts
// @match https://www.kujiale.com/pub/saas/admin/brandgoods/all*
// @match https://www.kujiale.com/pub/saas/workbench/brandgoods/all*
// @grant none
// @require https://d3js.org/d3.v7.min.js
// @version 1.8.8
// @author hejie13250
// @description 2025/3/30 1:05:58
// ==/UserScript==
// KJL后台-商品列表页添加按钮和调整列表项
(function () {
'use strict';
const DEBUG_MODE = true; // 调试模式
// 调试日志
function log(...args) {
if (DEBUG_MODE) console.log('[KUJIALE]', ...args);
}
// 注入样式
function appendStyle() {
const STYLE_CONTENT = `
.bTiGAY,
.keMTLz
{
display: unset;
border-radius: 2px;
color: rgb(255, 255, 255);
background: #88858A;
padding: 2px;
}
.goeuoF {
margin-right: 4px;
}
tbody tr.StyledBodyRowTr-kSBvgt > td:nth-child(1),
tbody tr.StyledBodyRowTr-kSBvgt.fApoPa > td:nth-child(1),
tbody tr.StyledBodyRowTr-kSBvgt > td:nth-child(2),
tbody tr.StyledBodyRowTr-kSBvgt.fApoPa > td:nth-child(2),
tbody tr.StyledBodyRowTr-kSBvgt > td:nth-last-child(2),
tbody tr.StyledBodyRowTr-kSBvgt.fApoPa > td:nth-last-child(2),
tbody tr.StyledBodyRowTr-kSBvgt > td:nth-last-child(1),
tbody tr.StyledBodyRowTr-kSBvgt.fApoPa > td:nth-last-child(1) {
background: #FFFFFFED;
}
#wb-app-container .wb-fixedSider,
.Sider-siderWrapper_448bb .Sider-collapseBtn_3d285 {
display: none;
/* 默认隐藏侧边栏 */
}
table.StyledTable-diJSpn.irqLUa thead tr th:nth-last-child(2),
tr.StyledBodyRowTr-kSBvgt td:nth-last-child(2),
.app__StyledHeader-sc-19hozu1-1,
.app__StyleExtraContentPlugins-sc-19hozu1-0 {
display: none;
}
/* 侧边栏展开按钮样式 */
.Sider-collapseBtn {
position: fixed;
left: 0;
top: 0;
height: 110px;
width: 150px;
background: #0000;
transform: translateY(-50%);
z-index: 1000;
cursor: pointer;
}
table.StyledTable-diJSpn.irqLUa tbody {
display: none;
}
/* 新增渐变显示效果 */
table.StyledTable-diJSpn.irqLUa tbody {
opacity: 0;
/* 初始完全透明 */
display: table-row-group;
/* 保持表格行组布局 */
transition: opacity 0.5s ease-in-out;
/* 半秒的渐变效果 */
}
/* 当添加show类时显示内容 */
table.StyledTable-diJSpn.irqLUa tbody.show {
opacity: 1;
/* 完全不透明 */
}
/* 表头 */
.irqLUa thead th {
padding: 2px 4px;
}
thead tr th {
height: 40px;
}
th.table-mode__Th-sc-1yn5aku-1:nth-child(13) > div:nth-child(1) > div:nth-child(2),
.htbUul .StyledHeaderContent-gwiBRL .StyledHeaderIconWrapper-ckhjhe,
th.table-mode__Th-sc-1yn5aku-1:nth-child(7) > div:nth-child(1) > div:nth-child(2) {
display: none;
}
.fwwEgAW {
position: absolute;
left: 1px;
bottom: 45px;
}
.leKfqU {
padding: 5px;
}
.jZKSUl {
padding: 5px;
}
.irqLUa thead .StyledHeaderContent-gwiBRL {
padding: 5px;
}
.Eqqwv {
width: 120px;
}
tr.StyledBodyRowTr-kSBvgt td:nth-child(1),
tr.StyledBodyRowTr-kSBvgt td:nth-child(2),
tr.StyledBodyRowTr-kSBvgt td:nth-last-child(2),
tr.StyledBodyRowTr-kSBvgt td:nth-last-child(1) {
cursor: default;
}
tr.StyledBodyRowTr-kSBvgt > td {
padding: 0px 5px !important;
}
.Link-qfJUS.dtwGsR {
padding: 4px 4px;
}
.fnmwsn {
margin-bottom: 0px;
}
.djGvpx:hover {
background: #9d8b;
}
.spanNum {
margin-right: 5px;
}
.custom-btn {
display: inline-block;
}
.Link-qfJUS.dtwGsR {
fontSize: 14px !important;
margin-bottom: 5px;
padding: 2px 4px;
border-radius: 4px;
}
/* 工具企业可见为“可见”时图片边框样式 */
.visible-highlight {
border: 3px solid #4CAF50 !important;
/* 绿色边框 */
border-radius: 4px;
width: 76px;
margin-top: 5px;
margin-bottom: 5px;
}
/* 工具企业可见为“不可见”时图片边框样式 */
.invisible-highlight {
border: 3px solid #F44336 !important;
/* 红色边框 */
border-radius: 4px;
width: 76px;
margin-top: 5px;
margin-bottom: 5px;
}
/* 当前行为目录时 */
.transparent-highlight {
border: 3px solid transparent !important;
/* 透明边框 */
border-radius: 4px;
width: 76px;
margin-top: 5px;
margin-bottom: 5px;
}
/* 节点样式 */
.graph-node {
background: white;
border: 1px solid #eee;
border-radius: 12px;
overflow: hidden;
transition: transform 0.1s, box-shadow 0.1s, width 0.3s;
/* 添加宽度过渡效果 */
flex-direction: column;
/* 改为上下结构 */
font-size: 14px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
cursor: default;
/* 默认光标 */
width: 300px;
/* 初始宽度 */
height: 150px;
/* 初始高度 */
z-index: 1;
transition: height 0.3s ease, transform 0.1s, box-shadow 0.1s, width 0.3s;
overflow: hidden;
/* 确保内容不会溢出 */
}
.graph-node:hover {
border: 1px solid #eee;
width: auto;
/* 悬停时宽度自动调整 */
min-width: 300px;
/* 确保最小宽度 */
height: 180px;
/* 悬停时增加高度 */
box-shadow: 0 4px 10px rgb(66, 49, 16);
/* 鼠标悬停时增加阴影 */
cursor: text;
/* 鼠标悬停时变为选择文本光标 */
z-index: 100 !important;
}
/* 按钮容器初始完全隐藏且不占空间 */
.graph-node-buttons {
display: none;
/* 初始不显示 */
justify-content: space-around;
padding: 5px 0;
border-top: 1px solid #eee;
opacity: 0;
transition: opacity 0.3s ease;
}
.graph-node-button {
padding: 0px 10px;
border-radius: 4px;
}
.graph-node-button:hover {
cursor: pointer;
background: #dfe;
}
/* 鼠标悬停时显示按钮容器 */
.graph-node:hover .graph-node-buttons {
display: flex;
/* 显示容器 */
opacity: 1;
/* 淡入效果 */
}
/* 确保节点内容区域高度固定 */
.graph-node-content {
flex: 1;
display: flex;
padding: 10px;
gap: 10px;
min-height: 100px;
/* 固定内容区域高度 */
}
.graph-node-name {
font-weight: bold;
padding: 10px;
background: #f5f5f5;
border-bottom: 1px solid #eee;
height: 40px;
text-align: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.graph-node-content {
flex: 1;
display: flex;
/* 改为左右结构 */
padding: 10px;
gap: 10px;
}
.graph-node-image {
width: 80px;
height: 80px;
object-fit: cover;
cursor: pointer;
border-radius: 4px;
border: 2px solid #ddd;
/* 添加边框 */
transition: border-color 0.2s;
/* 添加边框颜色的过渡效果 */
}
.graph-node-image:hover {
border-color: #999;
/* 鼠标悬停时边框颜色变深 */
}
.graph-node-text {
display: flex;
flex-direction: column;
flex: 1;
/* 让文本部分占据剩余空间 */
}
.text-item {
white-space: nowrap;
/* 禁止换行 */
overflow: hidden;
/* 隐藏溢出内容 */
text-overflow: ellipsis;
/* 显示省略号 */
max-width: 100%;
/* 限制最大宽度 */
display: block;
/* 确保作为块级元素显示 */
font-size: 14px;
/* 设置字体大小 */
line-height: 1.5;
/* 设置行高 */
color: #333;
/* 设置文本颜色 */
transition: all 0.3s;
/* 添加过渡效果 */
}
`;
// 创建style元素并注入样式
const style = document.createElement('style');
style.type = 'text/css';
style.appendChild(document.createTextNode(STYLE_CONTENT));
document.head.appendChild(style);
}
appendStyle();
// 创建侧边栏展开按钮
const collapseBtn = document.createElement('div');
collapseBtn.className = 'Sider-collapseBtn';
document.body.appendChild(collapseBtn);
// 获取侧边栏元素
const fixedSider = document.querySelector('#wb-app-container .wb-fixedSider');
if (fixedSider) {
// 初始化侧边栏样式
fixedSider.style.transition = 'width 0.3s ease, opacity 0.3s ease'; // 过渡动画
fixedSider.style.overflow = 'hidden'; // 隐藏溢出内容
fixedSider.style.width = '0'; // 初始宽度为0
fixedSider.style.opacity = '0'; // 初始完全透明
// 防抖计时器和状态标志
let debounceTimer;
let isSiderVisible = false;
// 显示侧边栏函数
const showSider = () => {
clearTimeout(debounceTimer); // 清除隐藏计时器
fixedSider.style.display = 'block';
fixedSider.style.width = '200px'; // 设置展开宽度
fixedSider.style.opacity = '1'; // 完全不透明
isSiderVisible = true; // 更新状态
};
// 隐藏侧边栏函数
const hideSider = () => {
debounceTimer = setTimeout(() => {
fixedSider.style.width = '0'; // 宽度归零
fixedSider.style.opacity = '0'; // 完全透明
// 过渡结束后执行
fixedSider.addEventListener('transitionend', function transitionEnd() {
if (fixedSider.style.width === '0px') {
fixedSider.removeEventListener('transitionend', transitionEnd);
fixedSider.style.display = 'none'; // 完全隐藏
isSiderVisible = false; // 更新状态
}
}, { once: true }); // 只执行一次
}, 100); // 延迟100ms执行
};
// 按钮点击事件 - 切换侧边栏状态
collapseBtn.addEventListener('click', (e) => {
e.stopPropagation(); // 阻止事件冒泡
if (isSiderVisible) {
hideSider(); // 如果可见则隐藏
} else {
showSider(); // 如果隐藏则显示
}
});
// 鼠标离开侧边栏时隐藏
fixedSider.addEventListener('mouseleave', hideSider);
// 鼠标进入侧边栏时取消隐藏计时器
fixedSider.addEventListener('mouseenter', () => {
clearTimeout(debounceTimer);
});
}
let brandSeriesColumnIndex = null; // 列索引-品牌系列
let customCodeColumnIndex = null; // 列索引-自定义编码
let renderCategoryColumnIndex = null; // 列索引-渲染分类
let tagsColumnIndex = null; // 列索引-标签
let publicToLibraryColumnIndex = null; // 列索引-公开到公库
let creatorColumnIndex = null; // 列索引-创建者
let modelSizeColumnIndex = null; // 列索引-模型尺寸
let quoteCategoryColumnIndex = null; // 列索引-报价分类
let panoramaDisplayColumnIndex = null; // 列索引-全景图显示
let productSizeColumnIndex = null; // 列索引-商品尺寸
let createTimeColumnIndex = null; // 列索引-创建时间
let placementColumnIndex = null; // 列索引-摆放位置
let salePriceColumnIndex = null; // 列索引-销售价格
let purchaseLinkColumnIndex = null; // 列索引-购买链接
let descriptionColumnIndex = null; // 列索引-描述
let idColumnIndex = null; // 列索引-商品ID
let toolVisibleColumnIndex = null; // 列索引-工具企业库可见
let productStatusColumnIndex = null; // 列索引-商品状态
let publishStatusColumnIndex = null; // 列索引-发布状态
let modelColumnIndex = null; // 列索引-型号
let productCodeColumnIndex = null; // 列索引-产品编码
let materialColumnIndex = null; // 列索引-材质
let baseMaterialColumnIndex = null; // 列索引-基材
let hasAlerted = false; // 标记是否已经提示过
// 初始化列索引(只提示一次)
function initColumnIndexes() {
const headerRow = document.querySelector('.StyledTable-diJSpn > thead:nth-child(2) > tr');
const headers = headerRow.querySelectorAll('th');
let foundId = false;
let foundToolVisible = false;
let foundBrandSeries = false;
let foundProductStatus = false;
let foundPublishStatus = false;
for (let i = 0; i < headers.length; i++) {
const header = headers[i];
const headerContent = header.querySelector('.StyledHeaderContent-gwiBRL');
if (!foundId && header.textContent.includes('商品ID')) {
idColumnIndex = i + 1;
foundId = true;
}
if (!foundToolVisible && headerContent && headerContent.textContent.trim() === '工具企业库可见') {
toolVisibleColumnIndex = i + 1;
foundToolVisible = true;
}
if (!foundBrandSeries && header.textContent.includes('品牌系列')) {
brandSeriesColumnIndex = i + 1;
foundBrandSeries = true;
}
if (!foundProductStatus && header.textContent.includes('商品状态')) {
productStatusColumnIndex = i + 1;
foundProductStatus = true;
}
if (!foundPublishStatus && header.textContent.includes('发布状态')) {
publishStatusColumnIndex = i + 1;
foundPublishStatus = true;
}
if (header.textContent.includes('型号')) {
modelColumnIndex = i + 1;
}
if (header.textContent.includes('产品编码')) {
productCodeColumnIndex = i + 1;
}
if (header.textContent.includes('自定义编码')) {
customCodeColumnIndex = i + 1;
}
if (header.textContent.includes('渲染分类')) {
renderCategoryColumnIndex = i + 1;
}
if (header.textContent.includes('材质')) {
materialColumnIndex = i + 1;
}
if (header.textContent.includes('基材')) {
baseMaterialColumnIndex = i + 1;
}
if (header.textContent.includes('创建者')) {
creatorColumnIndex = i + 1;
}
if (header.textContent.includes('创建时间')) {
createTimeColumnIndex = i + 1;
}
if (header.textContent.includes('商品尺寸')) {
productSizeColumnIndex = i + 1;
}
if (header.textContent.includes('摆放位置')) {
placementColumnIndex = i + 1;
}
if (header.textContent.includes('公开到公库')) {
publicToLibraryColumnIndex = i + 1;
}
if (header.textContent.includes('全景图显示')) {
panoramaDisplayColumnIndex = i + 1;
}
if (header.textContent.includes('销售价格')) {
salePriceColumnIndex = i + 1;
}
if (header.textContent.includes('报价分类')) {
quoteCategoryColumnIndex = i + 1;
}
if (header.textContent.includes('模型尺寸')) {
modelSizeColumnIndex = i + 1;
}
if (header.textContent.includes('创建者')) {
creatorColumnIndex = i + 1;
}
}
// 如果找到所有必需列
const requiredColumnsFound = foundId && foundToolVisible;
// 如果找到商品状态和发布状态列,准备移动内容
const statusColumnsFound = foundProductStatus && foundPublishStatus && foundBrandSeries;
if (requiredColumnsFound) {
hasAlerted = false; // 重置提示状态
console.log('列索引初始化成功');
return true;
}
// 如果未找到所有必需列且未提示过
// if (!hasAlerted) {
// const missingColumns = [];
// if (!foundId) missingColumns.push('"商品ID"');
// if (!foundToolVisible) missingColumns.push('"工具企业库可见"');
// alert(`请开启以下列后刷新页面:${missingColumns.join('和')}\n\n操作步骤:\n1. 点击表格右上角的"列设置"按钮\n2. 勾选缺少的列\n3. 刷新页面`);
// hasAlerted = true;
// }
return false;
}
let TAB_TEXT = '';
let currentType = '';
const TYPE_MAPPING = {
'厨卫定制': 'cabinet',
'全屋家具定制': 'wardrobe',
'门窗定制': 'doorwindow'
};
// 更新标签页信息的函数
function updateTabInfo() {
const tabElement = document.querySelector('div.BaseNode-ddnpWv.jaYkes.Tab-fzpsPV.lnGuGN.muya-tabs-tab.muya-pangu-tabs-tab.muya-tabs-active-tab.muya-pangu-tabs-active-tab > button');
TAB_TEXT = tabElement ? tabElement.textContent.trim() : '';
currentType = TYPE_MAPPING[TAB_TEXT] || '';
}
// 确保全局模态窗口变量已定义
let attrModalMask = null;
let attrModalContent = null;
let attrTablesContainer = null;
// 初始化属性窗口函数
function initAttributeModal() {
if (attrModalMask) return; // 如果已经存在则不重复创建
attrModalMask = document.createElement('div');
attrModalMask.style.cssText = 'position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.5);z-index:999;display:none;';
attrModalContent = document.createElement('div');
attrModalContent.style.cssText = 'position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:white;border-radius:8px;overflow:hidden;box-shadow:0 4px 12px rgba(0,0,0,0.15);width:500px;height:600px;';
attrTablesContainer = document.createElement('div');
attrTablesContainer.style.cssText = 'overflow:auto;width:100%;height:100%;padding:20px;';
attrModalContent.appendChild(attrTablesContainer);
attrModalMask.appendChild(attrModalContent);
document.body.appendChild(attrModalMask);
// 点击遮罩层关闭窗口
attrModalMask.addEventListener('click', (e) => {
if (e.target === attrModalMask) {
attrModalMask.style.display = 'none';
}
});
}
// 创建浮动窗口
function createFloatingWindow() {
const modalMask = document.createElement('div');
modalMask.style.cssText = 'position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.5);z-index:999;display:none;';
const modalContent = document.createElement('div');
modalContent.style.cssText = 'position:fixed;top:0;left:0;right:0;bottom:0;background:white;border-radius:8px;overflow:hidden;box-shadow:0 4px 12px rgba(0,0,0,0.15);';
const tablesContainer = document.createElement('div');
tablesContainer.style.cssText = 'overflow:hidden;width:100%;height:100%;position:relative;';
modalContent.appendChild(tablesContainer);
modalMask.appendChild(modalContent);
document.body.appendChild(modalMask);
// 点击遮罩层关闭窗口
modalMask.addEventListener('click', (e) => {
if (e.target === modalMask) {
modalMask.style.display = 'none';
}
});
// 动态设置窗口大小
function setSize(width, height) {
if (typeof width === 'string' && width.endsWith('%')) {
modalContent.style.width = width;
modalContent.style.left = `calc(50% - ${parseInt(width) / 2}%)`; // 居中
} else {
modalContent.style.width = `${width}px`;
modalContent.style.left = `calc(50% - ${width / 2}px)`; // 居中
}
if (typeof height === 'string' && height.endsWith('%')) {
modalContent.style.height = height;
modalContent.style.top = `calc(50% - ${parseInt(height) / 2}%)`; // 居中
} else {
modalContent.style.height = `${height}px`;
modalContent.style.top = `calc(50% - ${height / 2}px)`; // 居中
}
}
return { modalMask, tablesContainer, setSize };
}
const { modalMask, tablesContainer, setSize } = createFloatingWindow();
// 创建表格
function createTable(title, columns, rows) {
const table = document.createElement('table');
table.style.cssText = 'width:100%;margin-top:20px;border-collapse:collapse;font-size:14px;';
const thead = document.createElement('thead');
thead.innerHTML = `
${columns.map(col => `| ${col} | `).join('')}
`;
const tbody = document.createElement('tbody');
rows.forEach(row => {
const tr = document.createElement('tr');
tr.innerHTML = row.map(cell => `${cell} | `).join('');
tbody.appendChild(tr);
});
table.appendChild(thead);
table.appendChild(tbody);
const wrapper = document.createElement('div');
wrapper.style.marginTop = '20px';
const titleEl = document.createElement('h3');
titleEl.textContent = title;
titleEl.style.cssText = 'font-size:16px;margin:0 0 10px 0;color:#333;';
wrapper.appendChild(titleEl);
wrapper.appendChild(table);
return wrapper;
}
// 获取数据
async function getData(obsBgId) {
if (!attrTablesContainer) {
initAttributeModal();
}
attrTablesContainer.innerHTML = '加载中...
';
attrModalMask.style.display = 'block';
try {
const urls = [
`https://www.kujiale.com/editor/api/site/param/bizproperty/editordata/query?obsBrandGoodId=${obsBgId}`,
`https://www.kujiale.com/dcscms/api/custom/brandgood/attribute?start=0&num=30&obsBgId=${obsBgId}`,
`https://www.kujiale.com/editor/api/site/input/parameters/${obsBgId}`
];
const responses = await Promise.all(urls.map(url =>
fetch(url).then(r => r.json()).catch(e => {
console.error(`请求失败: ${url}`, e);
return null;
})
));
attrTablesContainer.innerHTML = '';
const contentWrapper = document.createElement('div');
contentWrapper.style.cssText = 'max-height: 100%; overflow-y: auto; padding: 10px; border: 1px solid #ddd; border-radius: 4px;';
// 处理第一个API响应
if (responses[0]?.d?.length > 0) {
const title = '自定义输出属性';
const columns = ['属性名称', '属性引用名', '属性值'];
const rows = responses[0].d.map(item => [item.name, item.key, item.value]);
contentWrapper.appendChild(createTable(title, columns, rows));
}
// 处理第二个API响应
if (responses[1]?.d?.result?.length > 0) {
const title = '自定义属性';
const columns = ['属性名称', '属性引用名', '属性值'];
const rows = responses[1].d.result.map(item => [item.attributeName, item.quoteName, item.attributeValue]);
contentWrapper.appendChild(createTable(title, columns, rows));
}
// 处理第三个API响应
if (Array.isArray(responses[2]) && responses[2].length > 0) {
const title = '自定义参数';
const columns = ['参数名称', '参数引用名'];
const rows = responses[2].map(item => [item.displayName, item.paramName]);
contentWrapper.appendChild(createTable(title, columns, rows));
}
// 如果没有数据,显示提示
if (contentWrapper.children.length === 0) {
contentWrapper.innerHTML = '未找到相关属性数据
';
}
attrTablesContainer.appendChild(contentWrapper);
} catch (error) {
console.error('获取数据失败:', error);
attrTablesContainer.innerHTML = '数据加载失败,请稍后再试
';
}
}
// 递归获取所有节点(包括顶层父节点和所有子节点)
async function getAllNodes(obsBgId, result = [], level = 0) {
try {
const url = `https://www.kujiale.com/editor/api/site/editordata?obsbrandgoodid=${obsBgId}`;
log(`发送请求: ${url}, 层级: ${level}`);
const response = await fetch(url);
const data = await response.json();
log(`请求响应:`, data);
// 提取当前节点信息
if (data?.model) {
const currentNode = data.model;
const node = {
id: currentNode.obsBrandGoodId,
name: currentNode.name,
displayCoverImgUrl: currentNode.displayCoverImgUrl,
level: level,
children: []
};
// 获取对接编码
const dockCodeResponse = await fetch(`https://www.kujiale.com/editor/api/site/param/bizproperty/editordata/query?obsBrandGoodId=${node.id}`);
const dockCodeData = await dockCodeResponse.json();
const dockCodeEntry = dockCodeData.d.find(entry => entry.name === "对接编码");
node.dockCode = dockCodeEntry ? dockCodeEntry.value : "未定义";
// 如果是顶层节点,直接添加到结果中
if (level === 0) {
result.push(node);
}
// 提取子节点信息
if (data?.resource?.models) {
log(`发现子节点:`, data.resource.models);
for (const model of data.resource.models) {
const childNode = await getAllNodes(model.obsBrandGoodId, [], level + 1);
if (childNode.length > 0) {
node.children.push(childNode[0]); // 添加子节点
}
}
}
// 如果不是顶层节点,返回当前节点
if (level !== 0) {
return [node];
}
}
} catch (error) {
log(`获取节点 ${obsBgId} 数据失败:`, error);
}
return result;
}
// 画节点
function drawGraph(data) {
tablesContainer.innerHTML = '';
// 创建画布和容器
const canvas = document.createElement('canvas');
canvas.style.position = 'absolute';
canvas.style.top = '0';
canvas.style.left = '0';
canvas.style.width = '100%';
canvas.style.height = '100%';
canvas.style.pointerEvents = 'none';
tablesContainer.appendChild(canvas);
const nodesContainer = document.createElement('div');
nodesContainer.style.position = 'absolute';
nodesContainer.style.transformOrigin = '0 0';
nodesContainer.style.opacity = '0';
tablesContainer.appendChild(nodesContainer);
// 创建独立的属性窗口
const attrModalMask = document.createElement('div');
attrModalMask.style.cssText = 'position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.5);z-index:999;display:none;';
const attrModalContent = document.createElement('div');
attrModalContent.style.cssText = 'position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:white;border-radius:8px;overflow:hidden;box-shadow:0 4px 12px rgba(0,0,0,0.15);width:500px;height:600px;';
const attrTablesContainer = document.createElement('div');
attrTablesContainer.style.cssText = 'overflow:auto;width:100%;height:100%;padding:20px;';
attrModalContent.appendChild(attrTablesContainer);
attrModalMask.appendChild(attrModalContent);
document.body.appendChild(attrModalMask);
// 坐标转换系统
let transform = {
x: 0,
y: 0,
k: 1
};
// 初始化D3树布局
const root = d3.hierarchy(data[0], d => d.children);
const treeLayout = d3.tree()
.nodeSize([250, 310])
.separation((a, b) => a.parent === b.parent ? 2 : 3);
treeLayout(root);
// 创建节点元素
const nodes = root.descendants();
nodes.forEach(node => {
const el = document.createElement('div');
el.className = 'graph-node';
el.style.position = 'absolute';
el.style.width = '300px';
el.style.left = `${node.x}px`;
el.style.top = `${node.y}px`;
el.style.zIndex = '1';
// 创建底部按钮容器
const buttonsContainer = document.createElement('div');
buttonsContainer.className = 'graph-node-buttons';
// 详情按钮
const detailBtn = document.createElement('span');
detailBtn.className = 'graph-node-button';
detailBtn.textContent = '详情';
detailBtn.addEventListener('click', (e) => {
e.stopPropagation();
window.open(`https://www.kujiale.com/pub/saas/brandgoods/detail?obsbrandgoodsid=${node.data.id}`);
});
// 属性按钮
const attrBtn = document.createElement('span');
attrBtn.className = 'graph-node-button';
attrBtn.textContent = '属性';
attrBtn.addEventListener('click', async (e) => {
e.stopPropagation();
await getData(node.data.id);
});
buttonsContainer.appendChild(detailBtn);
buttonsContainer.appendChild(attrBtn);
// 编辑按钮(根据条件显示)
const editBtn = document.createElement('span');
if (currentType) {
editBtn.className = 'graph-node-button';
editBtn.textContent = '打开';
editBtn.addEventListener('click', (e) => {
e.stopPropagation();
window.open(`https://www.kujiale.com/vc/modeleditor/new?tooltype=${currentType}&obsbrandgoodid=${node.data.id}`);
});
buttonsContainer.appendChild(editBtn);
}
// 创建节点时,按钮容器初始为隐藏状态
el.innerHTML = `
${node.data.name}
层级: ${node.data.level + 1}
对接编码: ${node.data.dockCode}
`;
el.appendChild(buttonsContainer);
// 节点的鼠标悬停事件
el.addEventListener('mouseenter', () => {
document.querySelectorAll('.graph-node').forEach(n => n.style.zIndex = '1');
el.style.zIndex = '100';
const imageWidth = el.querySelector('.graph-node-image').offsetWidth;
const textWidth = el.querySelector('.graph-node-text').offsetWidth;
const padding = 20;
const totalWidth = imageWidth + textWidth + padding + 10;
el.style.width = `${totalWidth}px`;
// 显示按钮
const buttons = el.querySelector('.graph-node-buttons');
if (buttons) {
buttons.style.opacity = '1';
}
});
el.addEventListener('mouseleave', () => {
el.style.zIndex = '1';
el.style.width = '300px';
// 隐藏按钮
const buttons = el.querySelector('.graph-node-buttons');
if (buttons) {
buttons.style.opacity = '0';
}
});
nodesContainer.appendChild(el);
// 图片点击事件
el.querySelector('img').addEventListener('click', e => {
e.stopPropagation();
window.open(`https://www.kujiale.com/pub/saas/brandgoods/detail?obsbrandgoodsid=${node.data.id}`);
});
});
// 更新视图函数
function updateView() {
const { k, x, y } = transform;
nodesContainer.style.transform = `translate(${x}px, ${y}px) scale(${k})`;
const rect = tablesContainer.getBoundingClientRect();
canvas.width = rect.width;
canvas.height = rect.height;
const ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.save();
ctx.translate(x, y);
ctx.scale(k, k);
ctx.strokeStyle = '#999';
ctx.lineWidth = 2;
root.links().forEach(link => {
const source = link.source;
const target = link.target;
const sourceX = source.x + 150;
const sourceY = source.y + 150;
const targetX = target.x + 150;
const targetY = target.y;
ctx.beginPath();
ctx.moveTo(sourceX, sourceY);
ctx.bezierCurveTo(
sourceX, sourceY + 50,
targetX, targetY - 50,
targetX, targetY
);
ctx.stroke();
});
ctx.restore();
}
// 自动调整视图以显示所有节点
function autoFitView() {
// 获取所有节点的边界
let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
nodes.forEach(node => {
minX = Math.min(minX, node.x);
maxX = Math.max(maxX, node.x + 300);
minY = Math.min(minY, node.y);
maxY = Math.max(maxY, node.y + 180); // 更新为新的节点高度
});
// 计算内容尺寸
const contentWidth = maxX - minX;
const contentHeight = maxY - minY;
// 获取容器尺寸
const containerWidth = tablesContainer.clientWidth;
const containerHeight = tablesContainer.clientHeight;
// 计算合适的缩放比例
const scaleX = containerWidth / (contentWidth + 100);
const scaleY = containerHeight / (contentHeight + 100);
const newScale = Math.min(scaleX, scaleY, 1);
// 计算中心点偏移
const centerX = (minX + maxX) / 2;
const centerY = (minY + maxY) / 2;
const offsetX = (containerWidth / 2) - (centerX * newScale);
const offsetY = (containerHeight / 2) - (centerY * newScale);
// 应用新的变换
transform.k = newScale;
transform.x = offsetX;
transform.y = offsetY;
updateView();
// 调整完成后显示节点
nodesContainer.style.opacity = '1';
nodesContainer.style.transition = 'opacity 0.3s ease';
}
// 初始布局
updateView();
// 使用requestAnimationFrame确保DOM已完全渲染
requestAnimationFrame(() => {
autoFitView();
});
// 交互逻辑
let dragStart = { x: 0, y: 0 };
tablesContainer.addEventListener('mousedown', e => {
if (e.target === tablesContainer || e.target === canvas) {
dragStart = {
x: e.clientX - transform.x,
y: e.clientY - transform.y
};
tablesContainer.style.cursor = 'grabbing';
}
});
tablesContainer.addEventListener('mousemove', e => {
if (dragStart.x || dragStart.y) {
transform.x = e.clientX - dragStart.x;
transform.y = e.clientY - dragStart.y;
updateView();
}
});
tablesContainer.addEventListener('mouseup', () => {
dragStart = { x: 0, y: 0 };
tablesContainer.style.cursor = 'grab';
});
tablesContainer.addEventListener('wheel', e => {
e.preventDefault();
const prevK = transform.k;
const newK = Math.max(0.1, Math.min(3, transform.k * (e.deltaY < 0 ? 1.5 : 0.5)));
const mx = e.clientX - tablesContainer.getBoundingClientRect().left;
const my = e.clientY - tablesContainer.getBoundingClientRect().top;
transform.x = mx - (mx - transform.x) * (newK / prevK);
transform.y = my - (my - transform.y) * (newK / prevK);
transform.k = newK;
updateView();
});
tablesContainer.addEventListener('contextmenu', (e) => {
e.preventDefault();
return false;
});
window.addEventListener('resize', () => {
updateView();
autoFitView();
});
// 点击遮罩层关闭属性窗口
attrModalMask.addEventListener('click', (e) => {
if (e.target === attrModalMask) {
attrModalMask.style.display = 'none';
}
});
}
// 创建按钮
function createButton(text, action) {
const btn = document.createElement('div');
btn.className = 'custom-btn';
const link = document.createElement('a');
link.className = 'StyledLink-kjrCJO gvhEJs StyledInlineButton-fyUImi djGvpx WrapInlineButton-nnTTR kUBFEP Link-qfJUS dtwGsR';
link.style.cssText = 'cursor: pointer; color: rgb(51, 139, 255);';
link.textContent = text;
link.addEventListener('click', action);
btn.appendChild(link);
return btn;
}
// 行点击高亮功能
function changeTextColorToRed(tr) {
Array.from(tr.children).forEach(td => {
td.style.color = 'red';
});
}
function restoreTextColor(tr) {
Array.from(tr.children).forEach(td => {
td.style.color = '';
});
}
function addClickListeners() {
const trElements = document.querySelectorAll('tbody tr.StyledBodyRowTr-kSBvgt');
trElements.forEach(tr => {
tr.addEventListener('click', function (event) {
// 清除之前可能存在的红色文本
const allTrElements = document.querySelectorAll('tbody tr.StyledBodyRowTr-kSBvgt');
allTrElements.forEach(tr => {
restoreTextColor(tr);
});
// 改变当前tr内所有文本的颜色为红色
changeTextColorToRed(tr);
});
});
}
// 调整列表布局样式
function updateProductDisplayStatus() {
if (toolVisibleColumnIndex === null) {
if (!initColumnIndexes()) {
log('无法初始化列索引,可见性检查中止');
return false;
}
}
let shouldInitialize = false;
const rows = document.querySelectorAll('tr.StyledBodyRowTr-kSBvgt');
// 获取表格的colgroup
const table = document.querySelector('table.StyledTable-diJSpn.irqLUa');
const colgroup = table?.querySelector('colgroup');
const cols = colgroup?.querySelectorAll('col');
// 计算需要隐藏的列数
let hiddenColumnCount = 0;
const columnsToHide = [
productStatusColumnIndex,
publishStatusColumnIndex,
idColumnIndex,
modelColumnIndex,
productCodeColumnIndex,
baseMaterialColumnIndex,
materialColumnIndex,
createTimeColumnIndex, // 新增
productSizeColumnIndex, // 新增
placementColumnIndex, // 新增
panoramaDisplayColumnIndex, // 新增
salePriceColumnIndex, // 新增
toolVisibleColumnIndex // 工具企业库可见
].filter(index => index !== null && index !== undefined);
hiddenColumnCount = columnsToHide.length;
// 隐藏表头中的相关列
const headerRow = document.querySelector('.StyledTable-diJSpn > thead:nth-child(2) > tr');
if (headerRow) {
columnsToHide.forEach(colIndex => {
const header = headerRow.querySelector(`th:nth-child(${colIndex})`);
if (header) header.style.display = 'none';
});
}
// 设置列宽度
if (cols && hiddenColumnCount > 0) {
const totalCols = cols.length;
const visibleCols = totalCols - hiddenColumnCount;
// 隐藏指定的列
for (let i = totalCols - 1; i >= visibleCols; i--) {
if (cols[i]) {
cols[i].style.width = '0px';
cols[i].style.minWidth = '0px';
}
}
// 定义列宽配置
const columnWidths = {
1: { width: '260px', minWidth: '260px' }, // 列-品牌系列
2: { width: '140px', minWidth: '140px' }, // 列-自定义编码
3: { width: '110px', minWidth: '110px' }, // 列-渲染分类
4: { width: '120px', minWidth: '120px' }, // 列-排序权重
5: { width: '100px', minWidth: '100px' } // 列-标签
// 6: { width: '140px', minWidth: '140px' }, // 列-公开到公库
// 7: { width: '100px', minWidth: '100px' }, // 列-创建者
// 8: { width: '200px', minWidth: '200px' }, // 列-模型尺寸
// 9: { width: '200px', minWidth: '200px' }, // 列-报价分类
// 10: { width: '100px', minWidth: '100px' }, // 列-购买链接
// 11: { width: '200px', minWidth: '200px' } // 列-描述
};
// 应用预定义的列宽
Object.entries(columnWidths).forEach(([index, styles]) => {
const colIndex = parseInt(index);
if (cols[colIndex] && colIndex < visibleCols) {
Object.assign(cols[colIndex].style, styles);
}
});
// 处理倒数第二和倒数第三可见列
const specialColumns = [
{ index: visibleCols - 1, styles: { width: '0px', minWidth: '0px', display: 'none' } },
{ index: visibleCols - 2, styles: { width: '0px', minWidth: '0px', display: 'none' } }
];
specialColumns.forEach(({ index, styles }) => {
if (cols[index]) {
Object.assign(cols[index].style, styles);
}
});
}
// 处理每一行
rows.forEach(async row => {
// 可见性高亮逻辑保持不变...
const visibilityCell = row.querySelector(`td:nth-child(${toolVisibleColumnIndex})`);
if (!visibilityCell) return;
const visibilityText = visibilityCell.textContent.trim();
const targetDiv = row.querySelector('td:nth-child(2) > div:nth-child(1) > div:nth-child(1)');
if (!targetDiv) return;
targetDiv.classList.remove('visible-highlight', 'invisible-highlight', 'transparent-highlight');
if (visibilityText === '可见') {
targetDiv.classList.add('visible-highlight');
shouldInitialize = true;
} else if (visibilityText === '不可见') {
targetDiv.classList.add('invisible-highlight');
shouldInitialize = true;
} else {
targetDiv.classList.add('transparent-highlight');
}
// 获取商品ID
const idCell = row.querySelector(`td:nth-child(${idColumnIndex})`);
if (!idCell) return;
const actualElement = idCell.querySelector('span, div') || idCell;
const currentId = (actualElement.textContent || '').trim();
if (!currentId) return;
// 1. 将状态信息和ID移动到品牌系列列
if (brandSeriesColumnIndex && idColumnIndex) {
const brandSeriesTd = row.querySelector(`td:nth-child(${brandSeriesColumnIndex})`);
const idTd = row.querySelector(`td:nth-child(${idColumnIndex})`);
// 可选列
const productStatusTd = productStatusColumnIndex ? row.querySelector(`td:nth-child(${productStatusColumnIndex})`) : null;
const publishStatusTd = publishStatusColumnIndex ? row.querySelector(`td:nth-child(${publishStatusColumnIndex})`) : null;
if (brandSeriesTd && idTd) {
if (brandSeriesTd.dataset.processed === 'true') return;
const brandSeriesDiv = brandSeriesTd.querySelector('div') || brandSeriesTd;
const idDiv = idTd.querySelector('div') || idTd;
// 获取内容
const idText = (idDiv.querySelector('.StyledContentCopy-bdGJSR.kZSaGD') || idDiv).textContent.trim();
let productStatusText = '';
if (productStatusTd) {
const productStatusDiv = productStatusTd.querySelector('div') || productStatusTd;
productStatusText = (productStatusDiv.querySelector('.StyledContentCopy-bdGJSR.kZSaGD') || productStatusDiv).textContent.trim();
}
let publishStatusText = '';
if (publishStatusTd) {
const publishStatusDiv = publishStatusTd.querySelector('div') || publishStatusTd;
publishStatusText = (publishStatusDiv.querySelector('.StyledContentCopy-bdGJSR.kZSaGD') || publishStatusDiv).textContent.trim();
}
// 创建容器
const containerDiv = document.createElement('div');
containerDiv.style.display = 'flex';
containerDiv.style.flexDirection = 'column';
containerDiv.style.gap = '4px';
// 添加品牌系列内容
const brandSeriesContainer = document.createElement('div');
brandSeriesContainer.innerHTML = brandSeriesDiv.innerHTML;
containerDiv.appendChild(brandSeriesContainer);
// 添加商品ID
const idContainer = document.createElement('div');
idContainer.style.color = '#666';
idContainer.textContent = `${idText}`;
containerDiv.appendChild(idContainer);
// 添加商品状态(如果有内容)
if (productStatusText) {
const productStatusContainer = document.createElement('div');
productStatusContainer.style.color = '#666';
productStatusContainer.textContent = `状态: ${productStatusText}`;
containerDiv.appendChild(productStatusContainer);
}
// 添加发布状态(如果有内容)
if (publishStatusText) {
const publishStatusContainer = document.createElement('div');
publishStatusContainer.style.color = '#666';
publishStatusContainer.textContent = `发布: ${publishStatusText}`;
containerDiv.appendChild(publishStatusContainer);
}
// 更新品牌系列列
brandSeriesTd.innerHTML = '';
brandSeriesTd.appendChild(containerDiv);
brandSeriesTd.dataset.processed = 'true';
// 隐藏原始列
if (productStatusTd) productStatusTd.style.display = 'none';
if (publishStatusTd) publishStatusTd.style.display = 'none';
idTd.style.display = 'none';
}
}
// 2. 将型号和产品编码移动到自定义编码列
if (customCodeColumnIndex && (modelColumnIndex || productCodeColumnIndex)) {
const customCodeTd = row.querySelector(`td:nth-child(${customCodeColumnIndex})`);
const modelTd = modelColumnIndex ? row.querySelector(`td:nth-child(${modelColumnIndex})`) : null;
const productCodeTd = productCodeColumnIndex ? row.querySelector(`td:nth-child(${productCodeColumnIndex})`) : null;
if (customCodeTd) {
if (customCodeTd.dataset.processed === 'true') return;
const customCodeDiv = customCodeTd.querySelector('div') || customCodeTd;
// 获取内容
let modelText = '';
if (modelTd) {
const modelDiv = modelTd.querySelector('div') || modelTd;
modelText = (modelDiv.querySelector('.StyledContentCopy-bdGJSR.kZSaGD') || modelDiv).textContent.trim();
}
let productCodeText = '';
if (productCodeTd) {
const productCodeDiv = productCodeTd.querySelector('div') || productCodeTd;
productCodeText = (productCodeDiv.querySelector('.StyledContentCopy-bdGJSR.kZSaGD') || productCodeDiv).textContent.trim();
}
// 创建容器
const containerDiv = document.createElement('div');
containerDiv.style.display = 'flex';
containerDiv.style.flexDirection = 'column';
containerDiv.style.gap = '4px';
// 添加自定义编码原始内容
const customCodeContainer = document.createElement('div');
customCodeContainer.innerHTML = customCodeDiv.innerHTML;
containerDiv.appendChild(customCodeContainer);
// 添加型号(如果有内容)
if (modelText) {
const modelContainer = document.createElement('div');
modelContainer.style.color = '#666';
modelContainer.textContent = `型号: ${modelText}`;
containerDiv.appendChild(modelContainer);
}
// 添加产品编码(如果有内容)
if (productCodeText) {
const productCodeContainer = document.createElement('div');
productCodeContainer.style.color = '#666';
productCodeContainer.textContent = `编码: ${productCodeText}`;
containerDiv.appendChild(productCodeContainer);
}
// 添加对接编码(单独一行)
try {
const dockCodeResponse = await fetch(`https://www.kujiale.com/editor/api/site/param/bizproperty/editordata/query?obsBrandGoodId=${currentId}`);
const dockCodeData = await dockCodeResponse.json();
const dockCodeEntry = dockCodeData.d?.find(entry => entry.name === "对接编码");
const dockCode = dockCodeEntry ? dockCodeEntry.value : "未定义";
const dockCodeContainer = document.createElement('div');
dockCodeContainer.style.color = '#666';
dockCodeContainer.textContent = `对接: ${dockCode}`;
containerDiv.appendChild(dockCodeContainer);
} catch (error) {
console.error('获取对接编码失败:', error);
}
// 更新自定义编码列
customCodeTd.innerHTML = '';
customCodeTd.appendChild(containerDiv);
customCodeTd.dataset.processed = 'true';
// 隐藏原始列
if (modelTd) modelTd.style.display = 'none';
if (productCodeTd) productCodeTd.style.display = 'none';
}
}
// 3. 将基材和材质移动到渲染分类列
if (renderCategoryColumnIndex && (baseMaterialColumnIndex || materialColumnIndex)) {
const renderCategoryTd = row.querySelector(`td:nth-child(${renderCategoryColumnIndex})`);
const baseMaterialTd = baseMaterialColumnIndex ? row.querySelector(`td:nth-child(${baseMaterialColumnIndex})`) : null;
const materialTd = materialColumnIndex ? row.querySelector(`td:nth-child(${materialColumnIndex})`) : null;
if (renderCategoryTd) {
if (renderCategoryTd.dataset.processed === 'true') return;
const renderCategoryDiv = renderCategoryTd.querySelector('div') || renderCategoryTd;
// 获取内容
let baseMaterialText = '';
if (baseMaterialTd) {
const baseMaterialDiv = baseMaterialTd.querySelector('div') || baseMaterialTd;
baseMaterialText = (baseMaterialDiv.querySelector('.StyledContentCopy-bdGJSR.kZSaGD') || baseMaterialDiv).textContent.trim();
}
let materialText = '';
if (materialTd) {
const materialDiv = materialTd.querySelector('div') || materialTd;
materialText = (materialDiv.querySelector('.StyledContentCopy-bdGJSR.kZSaGD') || materialDiv).textContent.trim();
}
// 创建容器
const containerDiv = document.createElement('div');
containerDiv.style.display = 'flex';
containerDiv.style.flexDirection = 'column';
containerDiv.style.gap = '4px';
// 添加渲染分类原始内容
const renderCategoryContainer = document.createElement('div');
renderCategoryContainer.innerHTML = renderCategoryDiv.innerHTML;
containerDiv.appendChild(renderCategoryContainer);
// 添加基材(如果有内容)
if (baseMaterialText) {
const baseMaterialContainer = document.createElement('div');
baseMaterialContainer.style.color = '#666';
baseMaterialContainer.textContent = `基材: ${baseMaterialText}`;
containerDiv.appendChild(baseMaterialContainer);
}
// 添加材质(如果有内容)
if (materialText) {
const materialContainer = document.createElement('div');
materialContainer.style.color = '#666';
materialContainer.textContent = `材质: ${materialText}`;
containerDiv.appendChild(materialContainer);
}
// 更新渲染分类列
renderCategoryTd.innerHTML = '';
renderCategoryTd.appendChild(containerDiv);
renderCategoryTd.dataset.processed = 'true';
// 隐藏原始列
if (baseMaterialTd) baseMaterialTd.style.display = 'none';
if (materialTd) materialTd.style.display = 'none';
}
}
// 4. 将创建时间添加到创建者列
if (creatorColumnIndex && createTimeColumnIndex) {
const creatorTd = row.querySelector(`td:nth-child(${creatorColumnIndex})`);
const createTimeTd = row.querySelector(`td:nth-child(${createTimeColumnIndex})`);
if (creatorTd && createTimeTd && creatorTd.dataset.processed !== 'true') {
const creatorDiv = creatorTd.querySelector('div') || creatorTd;
const createTimeDiv = createTimeTd.querySelector('div') || createTimeTd;
// 获取内容
const creatorText = (creatorDiv.querySelector('.StyledContentCopy-bdGJSR.kZSaGD') || creatorDiv).textContent.trim();
const createTimeText = (createTimeDiv.querySelector('.StyledContentCopy-bdGJSR.kZSaGD') || createTimeDiv).textContent.trim();
// 创建容器
const containerDiv = document.createElement('div');
containerDiv.style.display = 'flex';
containerDiv.style.flexDirection = 'column';
containerDiv.style.gap = '4px';
// 添加创建者原始内容
const creatorContainer = document.createElement('div');
creatorContainer.innerHTML = creatorDiv.innerHTML;
containerDiv.appendChild(creatorContainer);
// 添加创建时间
const createTimeContainer = document.createElement('div');
createTimeContainer.style.color = '#666';
createTimeContainer.textContent = `${createTimeText}`;
containerDiv.appendChild(createTimeContainer);
// 更新创建者列
creatorTd.innerHTML = '';
creatorTd.appendChild(containerDiv);
creatorTd.dataset.processed = 'true';
// 隐藏原始列
createTimeTd.style.display = 'none';
}
}
// 5. 将商品尺寸和摆放位置添加到模型尺寸列
if (modelSizeColumnIndex && (productSizeColumnIndex || placementColumnIndex)) {
const modelSizeTd = row.querySelector(`td:nth-child(${modelSizeColumnIndex})`);
const productSizeTd = productSizeColumnIndex ? row.querySelector(`td:nth-child(${productSizeColumnIndex})`) : null;
const placementTd = placementColumnIndex ? row.querySelector(`td:nth-child(${placementColumnIndex})`) : null;
if (modelSizeTd && modelSizeTd.dataset.processed !== 'true') {
const modelSizeDiv = modelSizeTd.querySelector('div') || modelSizeTd;
// 获取内容
let productSizeText = '';
if (productSizeTd) {
const productSizeDiv = productSizeTd.querySelector('div') || productSizeTd;
productSizeText = (productSizeDiv.querySelector('.StyledContentCopy-bdGJSR.kZSaGD') || productSizeDiv).textContent.trim();
}
let placementText = '';
if (placementTd) {
const placementDiv = placementTd.querySelector('div') || placementTd;
placementText = (placementDiv.querySelector('.StyledContentCopy-bdGJSR.kZSaGD') || placementDiv).textContent.trim();
}
// 创建容器
const containerDiv = document.createElement('div');
containerDiv.style.display = 'flex';
containerDiv.style.flexDirection = 'column';
containerDiv.style.gap = '4px';
// 添加模型尺寸原始内容
const modelSizeContainer = document.createElement('div');
modelSizeContainer.innerHTML = modelSizeDiv.innerHTML;
containerDiv.appendChild(modelSizeContainer);
// 添加商品尺寸(如果有内容)
if (productSizeText) {
const productSizeContainer = document.createElement('div');
productSizeContainer.style.color = '#666';
productSizeContainer.textContent = `尺寸: ${productSizeText}`;
containerDiv.appendChild(productSizeContainer);
}
// 添加摆放位置(如果有内容)
if (placementText) {
const placementContainer = document.createElement('div');
placementContainer.style.color = '#666';
placementContainer.textContent = `位置: ${placementText}`;
containerDiv.appendChild(placementContainer);
}
// 更新模型尺寸列
modelSizeTd.innerHTML = '';
modelSizeTd.appendChild(containerDiv);
modelSizeTd.dataset.processed = 'true';
// 隐藏原始列
if (productSizeTd) productSizeTd.style.display = 'none';
if (placementTd) placementTd.style.display = 'none';
}
}
// 6. 将工具企业库可见和全景图显示添加到公开到公库列
if (publicToLibraryColumnIndex && (toolVisibleColumnIndex || panoramaDisplayColumnIndex)) {
const publicToLibraryTd = row.querySelector(`td:nth-child(${publicToLibraryColumnIndex})`);
const toolVisibleTd = toolVisibleColumnIndex ? row.querySelector(`td:nth-child(${toolVisibleColumnIndex})`) : null;
const panoramaDisplayTd = panoramaDisplayColumnIndex ? row.querySelector(`td:nth-child(${panoramaDisplayColumnIndex})`) : null;
if (publicToLibraryTd && publicToLibraryTd.dataset.processed !== 'true') {
// const publicToLibraryDiv = publicToLibraryTd.querySelector('div') || publicToLibraryTd;
// 获取内容
let publicToLibraryText = '';
if (publicToLibraryTd) {
const publicToLibraryDiv = publicToLibraryTd.querySelector('div') || publicToLibraryTd;
publicToLibraryText = (publicToLibraryDiv.querySelector('.StyledContentCopy-bdGJSR.kZSaGD') || publicToLibraryDiv).textContent.trim();
}
let toolVisibleText = '';
if (toolVisibleTd) {
const toolVisibleDiv = toolVisibleTd.querySelector('div') || toolVisibleTd;
toolVisibleText = (toolVisibleDiv.querySelector('.StyledContentCopy-bdGJSR.kZSaGD') || toolVisibleDiv).textContent.trim();
}
let panoramaDisplayText = '';
if (panoramaDisplayTd) {
const panoramaDisplayDiv = panoramaDisplayTd.querySelector('div') || panoramaDisplayTd;
panoramaDisplayText = (panoramaDisplayDiv.querySelector('.StyledContentCopy-bdGJSR.kZSaGD') || panoramaDisplayDiv).textContent.trim();
}
// 创建容器
const containerDiv = document.createElement('div');
containerDiv.style.display = 'flex';
containerDiv.style.flexDirection = 'column';
containerDiv.style.gap = '4px';
// 添加公开到公库原始内容
// const publicToLibraryContainer = document.createElement('div');
// publicToLibraryContainer.innerHTML = publicToLibraryDiv.innerHTML;
// containerDiv.appendChild(publicToLibraryContainer);
// 添加公开到公库原始内容
if (publicToLibraryText) {
const publicToLibraryContainer = document.createElement('div');
publicToLibraryContainer.style.color = '#666';
publicToLibraryContainer.textContent = `到公库: ${publicToLibraryText}`;
containerDiv.appendChild(publicToLibraryContainer);
}
// 添加全景图显示(如果有内容)
if (panoramaDisplayText) {
const panoramaDisplayContainer = document.createElement('div');
panoramaDisplayContainer.style.color = '#666';
panoramaDisplayContainer.textContent = `全景图: ${panoramaDisplayText}`;
containerDiv.appendChild(panoramaDisplayContainer);
}
// 添加工具企业库可见(如果有内容)
if (toolVisibleText) {
const toolVisibleContainer = document.createElement('div');
toolVisibleContainer.style.color = '#666';
toolVisibleContainer.textContent = `企业库: ${toolVisibleText}`;
containerDiv.appendChild(toolVisibleContainer);
}
// 更新公开到公库列
publicToLibraryTd.innerHTML = '';
publicToLibraryTd.appendChild(containerDiv);
publicToLibraryTd.dataset.processed = 'true';
// 隐藏原始列
if (toolVisibleTd) toolVisibleTd.style.display = 'none';
if (panoramaDisplayTd) panoramaDisplayTd.style.display = 'none';
}
}
// 7. 将销售价格添加到报价分类列
if (quoteCategoryColumnIndex && salePriceColumnIndex) {
const quoteCategoryTd = row.querySelector(`td:nth-child(${quoteCategoryColumnIndex})`);
const salePriceTd = row.querySelector(`td:nth-child(${salePriceColumnIndex})`);
if (quoteCategoryTd && salePriceTd && quoteCategoryTd.dataset.processed !== 'true') {
const quoteCategoryDiv = quoteCategoryTd.querySelector('div') || quoteCategoryTd;
const salePriceDiv = salePriceTd.querySelector('div') || salePriceTd;
// 获取内容
const quoteCategoryText = (quoteCategoryDiv.querySelector('.StyledContentCopy-bdGJSR.kZSaGD') || quoteCategoryDiv).textContent.trim();
const salePriceText = (salePriceDiv.querySelector('.StyledContentCopy-bdGJSR.kZSaGD') || salePriceDiv).textContent.trim();
// 创建容器
const containerDiv = document.createElement('div');
containerDiv.style.display = 'flex';
containerDiv.style.flexDirection = 'column';
containerDiv.style.gap = '4px';
// 分割报价分类内容
const categoryParts = quoteCategoryText.split('、');
// 定义前缀数组
// const prefixes = ['厨卫: ', '全屋: ', '门窗: '];
// 添加带前缀的分类内容(最多三行)
categoryParts.slice(0, 3).forEach((part, index) => {
const categoryLine = document.createElement('div');
// 为每一行添加对应的前缀
// categoryLine.textContent = (prefixes[index] || '') + part.trim();
categoryLine.textContent = part.trim();
containerDiv.appendChild(categoryLine);
});
// 添加销售价格
const salePriceContainer = document.createElement('div');
salePriceContainer.style.color = '#666';
salePriceContainer.style.marginTop = '4px'; // 与分类内容保持间距
salePriceContainer.textContent = `价格: ${salePriceText}`;
containerDiv.appendChild(salePriceContainer);
// 更新报价分类列
quoteCategoryTd.innerHTML = '';
quoteCategoryTd.appendChild(containerDiv);
quoteCategoryTd.dataset.processed = 'true';
// 隐藏原始列
salePriceTd.style.display = 'none';
salePriceTd.classList.add('force-hidden'); // 添加强制隐藏类
}
}
});
return shouldInitialize;
}
// 修改后的按钮添加逻辑
function addButtons() {
if (idColumnIndex === null && !initColumnIndexes()) {
log('无法初始化列索引,按钮注入中止');
return;
}
// 确保属性窗口已初始化
initAttributeModal();
document.querySelectorAll('tr.StyledBodyRowTr-kSBvgt').forEach(row => {
// 跳过已经处理过的行
if (row.dataset.buttonsAdded === 'true') return;
const visibilityCell = row.querySelector(`td:nth-child(${toolVisibleColumnIndex})`);
if (!visibilityCell) return;
const visibilityText = visibilityCell.textContent.trim();
// 只在"可见"或"不可见"时添加按钮
if (visibilityText !== '可见' && visibilityText !== '不可见') {
return;
}
const targetTd1 = row.querySelector(`td:nth-child(2)`);
if (!targetTd1) return;
// 清除可能存在的旧按钮
const existingButtons = targetTd1.querySelectorAll('.custom-btn');
existingButtons.forEach(btn => btn.remove());
// 添加新按钮
targetTd1.appendChild(createButton('更新', (e) => {
e.preventDefault();
e.stopPropagation();
const row = e.target.closest('tr.StyledBodyRowTr-kSBvgt');
const idCell = row.querySelector(`td:nth-child(${idColumnIndex})`);
const actualElement = idCell.querySelector('span, div') || idCell;
const currentId = (actualElement.textContent || '').trim();
if (currentId) window.open(`https://www.kujiale.com/vc/editor/globalvariable?tooltype=cabinet¤tTab=model-DAG&bgId=${encodeURIComponent(currentId)}`, '_blank');
}));
targetTd1.appendChild(createButton('关联', (e) => {
e.preventDefault();
e.stopPropagation();
const row = e.target.closest('tr.StyledBodyRowTr-kSBvgt');
const idCell = row.querySelector(`td:nth-child(${idColumnIndex})`);
const actualElement = idCell.querySelector('span, div') || idCell;
const currentId = (actualElement.textContent || '').trim();
if (currentId) window.open(`https://www.kujiale.com/pub/saas/brandgoods/group/list?obsBrandGoodsId=${encodeURIComponent(currentId)}`, '_blank');
}));
targetTd1.appendChild(createButton('属性', async (e) => {
e.preventDefault();
e.stopPropagation();
const row = e.target.closest('tr.StyledBodyRowTr-kSBvgt');
const idCell = row.querySelector(`td:nth-child(${idColumnIndex})`);
const currentId = (idCell.textContent || '').trim();
if (currentId) {
await getData(currentId);
}
}));
targetTd1.appendChild(createButton('结构', async (e) => {
e.preventDefault();
e.stopPropagation();
tablesContainer.innerHTML = `加载数据中...
`;
modalMask.style.display = 'block';
const row = e.target.closest('tr.StyledBodyRowTr-kSBvgt');
const idCell = row.querySelector(`td:nth-child(${idColumnIndex})`);
const actualElement = idCell.querySelector('span, div') || idCell;
const currentId = (actualElement.textContent || '').trim();
if (currentId) {
setSize('90%', '90%');
log(`开始获取节点数据,初始ID: ${currentId}`);
const data = await getAllNodes(currentId);
log('所有节点数据:', data);
drawGraph(data);
}
}));
// 添加"快速编辑"按钮
targetTd1.appendChild(createButton('编辑', (e) => {
e.preventDefault();
e.stopPropagation();
const row = e.target.closest('tr.StyledBodyRowTr-kSBvgt');
const items = row.querySelectorAll(`td:nth-last-child(2) div a.Link-qfJUS.dtwGsR`);
items.forEach(function (item) {
if (item.textContent.trim() === '快速编辑') {
item.click();
}
});
}));
// 添加"编辑模型"按钮
targetTd1.appendChild(createButton('打开', (e) => {
e.preventDefault();
e.stopPropagation();
const row = e.target.closest('tr.StyledBodyRowTr-kSBvgt');
const items = row.querySelectorAll(`td:nth-last-child(2) div a.Link-qfJUS.dtwGsR`);
items.forEach(function (item) {
if (item.textContent.trim() === '编辑模型') {
item.click();
}
});
}));
// 标记为已处理
row.dataset.buttonsAdded = 'true';
});
}
// 修改后的初始化函数
function initialize() {
// 每次初始化前重置列索引
idColumnIndex = null;
toolVisibleColumnIndex = null;
brandSeriesColumnIndex = null;
productStatusColumnIndex = null;
publishStatusColumnIndex = null;
if (!initColumnIndexes()) {
console.log('必要列未找到,已提示用户');
return;
}
// 更新可见性状态(原有逻辑不变)
if (updateProductDisplayStatus()) {
// 添加按钮和事件监听(原有逻辑不变)
addButtons();
addClickListeners();
// 3. 渐变显示表格主体
const tbody = document.querySelector('table.StyledTable-diJSpn.irqLUa tbody');
if (tbody) {
// 先重置display属性(如果之前被隐藏)
tbody.style.display = '';
// 使用setTimeout确保浏览器有机会重绘
setTimeout(() => {
tbody.classList.add('show'); // 添加show类触发渐变效果
}, 10);
}
}
}
// 观察器配置
const observer = new MutationObserver((mutations) => {
// 检查是否有新的表格行被添加
const hasNewRows = mutations.some(mutation => {
return mutation.addedNodes && Array.from(mutation.addedNodes).some(node => {
return node.nodeType === 1 && (
node.matches('tr.StyledBodyRowTr-kSBvgt') ||
node.querySelector('tr.StyledBodyRowTr-kSBvgt')
);
});
});
if (hasNewRows || document.querySelector('tr.StyledBodyRowTr-kSBvgt')) {
// 延迟执行以确保DOM完全加载
setTimeout(() => {
updateTabInfo();
initialize();
}, 300);
}
});
// 启动观察 - 扩大观察范围
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: false,
characterData: false
});
// 初始执行
if (document.readyState === 'complete') {
if (updateProductDisplayStatus()) {
initialize();
}
} else {
window.addEventListener('load', () => {
if (updateProductDisplayStatus()) {
initialize();
}
});
}
})();
// KJL后台-商品列表禁用大预览图
(function() {
'use strict';
// 全局变量记录按钮状态
const buttonStates = {
toggleButtonAdded: false,
previewButtonAdded: false,
selectAllButtonAdded: false,
previewEnabled: false // 新增状态记录预览功能是否启用
};
// 注入CSS样式
function injectPreviewImgStyles() {
const styleId = 'preview-img-button-styles';
if (document.getElementById(styleId)) return;
const style = document.createElement('style');
style.id = styleId;
style.textContent = `
.preview-img-button {
width: 40px;
height: 20px;
background-color: #ccc;
border-radius: 10px;
position: relative;
cursor: pointer;
transition: background-color 0.3s;
border: none;
outline: none;
margin-left: 5px;
vertical-align: middle;
}
.preview-img-button::before {
content: '';
position: absolute;
width: 16px;
height: 16px;
background-color: white;
border-radius: 50%;
top: 2px;
left: 2px;
transition: transform 0.3s;
}
.preview-img-button.active {
background-color: #4caf50;
}
.preview-img-button.active::before {
transform: translateX(20px);
}
th.table-mode__Th-sc-1yn5aku-1:nth-child(1) > div:nth-child(1) > span:nth-child(1) {
margin-left: 20px;
}
.cYzReP .StyledCheckboxIconWrap-iFmnFS {
padding: 9px !important;
}
.open-all-models-btn {
margin-left: 10px;
}
.select-all-btn {
margin-left: 0;
}
`;
document.head.appendChild(style);
}
// 创建预览图片开关按钮
function createPreviewImgButton() {
const targetElement = document.querySelector('.QNCIl thead tr:nth-child(1) th:nth-child(2) span');
if (!targetElement || buttonStates.previewButtonAdded || targetElement.querySelector('.preview-img-button')) return;
const button = document.createElement('button');
button.className = 'preview-img-button'; // 默认灰色(inactive)
button.title = '切换预览图片悬停效果';
targetElement.parentNode.insertBefore(button, targetElement);
// 初始状态禁用预览功能
buttonStates.previewEnabled = false;
updatePreviewImageFunctionality();
button.addEventListener('click', function() {
this.classList.toggle('active');
buttonStates.previewEnabled = this.classList.contains('active');
updatePreviewImageFunctionality();
});
buttonStates.previewButtonAdded = true;
}
// 更新预览图片功能状态
function updatePreviewImageFunctionality() {
const previewElements = document.querySelectorAll('tr.StyledBodyRowTr-kSBvgt > td:nth-child(2) > div:nth-child(1) > div:nth-child(1)');
if (previewElements.length === 0) {
// 如果元素不存在,延迟重试
setTimeout(updatePreviewImageFunctionality, 500);
return;
}
if (buttonStates.previewEnabled) {
console.log('启用悬停效果');
previewElements.forEach(el => {
el.style.pointerEvents = '';
});
} else {
console.log('禁用悬停效果');
previewElements.forEach(el => {
el.style.pointerEvents = 'none';
});
}
}
// 创建全选按钮(保持在原复选框位置)
function createSelectAllButton() {
const targetElement = document.querySelector('th.table-mode__Th-sc-1yn5aku-1:nth-child(1) > div:nth-child(1)');
if (!targetElement || buttonStates.selectAllButtonAdded || targetElement.querySelector('.select-all-btn')) return;
var button = document.createElement('button');
button.setAttribute('type', 'button');
button.classList.add('select-all-btn', 'ikKMNI', 'StyledButton-gMsIIQ', 'bDLVNP', 'StyledButton-gFWaNh', 'ipjbPN', 'WrapButton-fExlJQ', 'kHuYig');
button.textContent = '全选';
button.addEventListener('click', function() {
// 模拟点击批量操作按钮
const batchActionButton = document.querySelector('#GUIDE_BATCH_ACTIONS > button:nth-child(1) > span:nth-child(1) > label:nth-child(1) > span:nth-child(1)');
if (batchActionButton) {
batchActionButton.click();
}
});
// 插入到目标元素的最前面
targetElement.insertBefore(button, targetElement.firstChild);
buttonStates.selectAllButtonAdded = true;
}
let idColumnIndex = -1; // 初始化列索引变量
// 初始化列索引函数
function initColumnIndexes() {
const headerRow = document.querySelector('.StyledTable-diJSpn > thead:nth-child(2) > tr');
if (!headerRow) return false; // 如果找不到表头行则返回false
const headers = headerRow.querySelectorAll('th');
let foundId = false;
for (let i = 0; i < headers.length; i++) {
const header = headers[i];
// 优先查找表头内容元素,没有则使用表头元素本身
const headerContent = header.querySelector('.StyledHeaderContent-gwiBRL') || header;
if (!foundId && headerContent.textContent.includes('商品ID')) {
idColumnIndex = i + 1; // +1 因为CSS的nth-child从1开始计数
foundId = true;
}
}
return foundId; // 返回是否找到ID列
}
let TAB_TEXT = '';
let currentType = '';
const TYPE_MAPPING = {
'厨卫定制': 'libtemplate.customcabinet',
'全屋家具定制': 'libtemplate.customwardrobe',
'门窗定制': 'libtemplate.customdoorwindow'
};
// 更新标签页信息的函数
function updateTabInfo() {
const tabElement = document.querySelector('div.BaseNode-ddnpWv.jaYkes.Tab-fzpsPV.lnGuGN.muya-tabs-tab.muya-pangu-tabs-tab.muya-tabs-active-tab.muya-pangu-tabs-active-tab > button');
TAB_TEXT = tabElement ? tabElement.textContent.trim() : '';
currentType = TYPE_MAPPING[TAB_TEXT] || '';
console.log('TAB_TEXT:', TAB_TEXT, 'currentType:', currentType);
}
// 创建切换按钮函数
function createToggleButton() {
const targetElement = document.querySelector('.QNCIl thead tr:nth-child(1) th:nth-child(2)');
// 如果目标元素不存在、按钮已添加或已有按钮,则直接返回
if (!targetElement || buttonStates.toggleButtonAdded || targetElement.querySelector('.open-all-models-btn')) return;
// 创建"打开全部"按钮
var openAllBtn = document.createElement('button');
openAllBtn.setAttribute('type', 'button');
openAllBtn.classList.add('open-all-models-btn', 'ikKMNI', 'StyledButton-gMsIIQ', 'bDLVNP', 'StyledButton-gFWaNh', 'ipjbPN', 'WrapButton-fExlJQ', 'kHuYig');
openAllBtn.textContent = '打开';
openAllBtn.title = '打开所有模型';
openAllBtn.addEventListener('click', function() {
var items = document.querySelectorAll('.StyledTable-diJSpn > tbody:nth-child(3) tr td div a.Link-qfJUS.dtwGsR');
items.forEach(function(item) {
if (item.textContent.trim() === '编辑模型') {
item.click(); // 模拟点击所有编辑链接
}
});
});
// 创建"查看详情"按钮
var detailAllBtn = document.createElement('button');
detailAllBtn.setAttribute('type', 'button');
detailAllBtn.classList.add('view-all-details-btn', 'ikKMNI', 'StyledButton-gMsIIQ', 'bDLVNP', 'StyledButton-gFWaNh', 'ipjbPN', 'WrapButton-fExlJQ', 'kHuYig');
detailAllBtn.textContent = '详情';
detailAllBtn.title = '查看所有模型详情';
// 使用一次性标志防止重复绑定
if (!detailAllBtn._hasClickListener) {
detailAllBtn._hasClickListener = true;
detailAllBtn.addEventListener('click', function() {
// 防止快速多次点击
if (this._isProcessing) return;
this._isProcessing = true;
console.log('详情按钮点击,开始处理...');
// 如果尚未初始化列索引,则尝试初始化
if (idColumnIndex === -1 && !initColumnIndexes()) {
console.error('无法找到ID列');
this._isProcessing = false;
return;
}
// 确保我们有 currentType
// if (!currentType) {
// updateTabInfo();
// if (!currentType) {
// console.error('无法确定当前模型类型');
// this._isProcessing = false;
// return;
// }
// }
updateTabInfo();
const rows = document.querySelectorAll('tr.StyledBodyRowTr-kSBvgt');
console.log(`准备打开 ${rows.length} 个模型详情,类型: ${currentType}`);
// 收集所有ID,避免重复
const uniqueIds = new Set();
rows.forEach(row => {
const idCell = row.querySelector(`td:nth-child(${idColumnIndex})`);
if (idCell) {
const actualElement = idCell.querySelector('span, div') || idCell;
const currentId = (actualElement.textContent || '').trim();
if (currentId) {
uniqueIds.add(currentId);
}
}
});
// 一次性打开所有唯一ID
console.log(`找到 ${uniqueIds.size} 个唯一模型ID`);
uniqueIds.forEach(id => {
console.log(`打开模型ID: ${id}`);
window.open(`https://www.kujiale.com/pub/saas/brandgoods/detail?obsbrandgoodsid=${id}&libtplid=${currentType}`, '_blank');
});
this._isProcessing = false;
});
}
// 将按钮添加到DOM
targetElement.appendChild(detailAllBtn);
targetElement.appendChild(openAllBtn);
buttonStates.toggleButtonAdded = true; // 标记按钮已添加
}
function init() {
// 初始化样式和按钮
initColumnIndexes();
injectPreviewImgStyles();
createPreviewImgButton();
createToggleButton();
// createSelectAllButton();
}
function observeTabChanges() {
const tabContainer = document.querySelector('.muya-tabs-nav'); // 调整选择器
if (!tabContainer) return;
const observer = new MutationObserver(() => {
updateTabInfo(); // 重新获取 TAB_TEXT
});
observer.observe(tabContainer, { childList: true, subtree: true });
}
observeTabChanges();
// 优化后的MutationObserver回调
function handleMutations(mutations) {
if (!buttonStates.previewButtonAdded) {
const previewTarget = document.querySelector('.QNCIl thead tr:nth-child(1) th:nth-child(2) span');
if (previewTarget) {
injectPreviewImgStyles();
createPreviewImgButton();
}
}
if (!buttonStates.toggleButtonAdded) {
createToggleButton();
}
if (!buttonStates.selectAllButtonAdded) {
createSelectAllButton();
}
// 如果预览功能状态已设置,但DOM可能已更新,需要重新应用状态
if (buttonStates.previewButtonAdded) {
updatePreviewImageFunctionality();
}
}
// 创建优化的MutationObserver
const observer = new MutationObserver(function(mutations) {
handleMutations(mutations);
});
// 页面加载后初始化
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function() {
init();
observer.observe(document.body, { childList: true, subtree: true });
});
} else {
init();
observer.observe(document.body, { childList: true, subtree: true });
}
})();