// ==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 }); } })();