// ==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.1
// @author hejie13250
// @description 2025/3/30 1:05:58
// ==/UserScript==
(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 idColumnIndex = null; // 商品ID列索引
let toolVisibleColumnIndex = null; // 工具可见列索引
let brandSeriesColumnIndex = null; // 品牌系列列索引
let productStatusColumnIndex = null; // 产品状态列索引
let publishStatusColumnIndex = null; // 发布状态列索引
let modelColumnIndex = null; // 型号列索引
let productCodeColumnIndex = null; // 产品代码列索引
let customCodeColumnIndex = null; // 自定义代码列索引
let renderCategoryColumnIndex = null; // 渲染类别列索引
let materialColumnIndex = null; // 材质列索引
let baseMaterialColumnIndex = null; // 基础材质列索引
let creatorColumnIndex = null; // 创建者列索引
let createDateColumnIndex = 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('创建日期')) {
createDateColumnIndex = 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;
}
// 更新可见性检查和高亮函数,返回布尔值表示是否应该继续初始化
function updateProductDisplayStatus() {
if (toolVisibleColumnIndex === null) {
if (!initColumnIndexes()) {
log('无法初始化列索引,可见性检查中止');
return false;
}
}
let shouldInitialize = false;
const rows = document.querySelectorAll('tr.StyledBodyRowTr-kSBvgt');
rows.forEach(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');
// 其他情况不设置shouldInitialize标志
}
// 新增:移动状态信息到品牌系列列
if (brandSeriesColumnIndex && productStatusColumnIndex && publishStatusColumnIndex) {
const brandSeriesTd = row.querySelector(`td:nth-child(${brandSeriesColumnIndex})`);
const productStatusTd = row.querySelector(`td:nth-child(${productStatusColumnIndex})`);
const publishStatusTd = row.querySelector(`td:nth-child(${publishStatusColumnIndex})`);
if (brandSeriesTd && productStatusTd && publishStatusTd) {
// 获取品牌系列原始内容(保留HTML结构)
const brandSeriesContent = brandSeriesTd.querySelector('div') || brandSeriesTd;
// 获取状态内容
const productStatusDiv = productStatusTd.querySelector('div') || productStatusTd;
const publishStatusDiv = publishStatusTd.querySelector('div') || publishStatusTd;
// 清空品牌系列td并重新构建内容
brandSeriesTd.innerHTML = '';
// 创建容器div
const containerDiv = document.createElement('div');
containerDiv.style.display = 'flex';
containerDiv.style.flexDirection = 'column';
containerDiv.style.gap = '4px';
// 添加品牌系列div(保留原始结构和样式)
const brandSeriesDiv = document.createElement('div');
brandSeriesDiv.innerHTML = brandSeriesContent.innerHTML;
containerDiv.appendChild(brandSeriesDiv);
// 添加商品状态div
const productStatusContainer = document.createElement('div');
productStatusContainer.style.fontSize = '12px';
productStatusContainer.style.color = '#666';
productStatusContainer.textContent = `商品状态: ${productStatusDiv.textContent.trim()}`;
containerDiv.appendChild(productStatusContainer);
// 添加发布状态div
const publishStatusContainer = document.createElement('div');
publishStatusContainer.style.fontSize = '12px';
publishStatusContainer.style.color = '#666';
publishStatusContainer.textContent = `发布状态: ${publishStatusDiv.textContent.trim()}`;
containerDiv.appendChild(publishStatusContainer);
// 将容器添加到td
brandSeriesTd.appendChild(containerDiv);
// 隐藏原始状态列
productStatusTd.style.display = 'none';
publishStatusTd.style.display = 'none';
}
}
});
return shouldInitialize;
}
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;
}
// 序号功能
let sequenceNumber = 1;
function resetSequenceNumber() {
sequenceNumber = 1;
}
function createSequenceSpan() {
const span = document.createElement('span');
span.textContent = sequenceNumber++;
span.className = 'spanNum';
return span;
}
function addSequenceNumbers() {
resetSequenceNumber();
const trElements = document.querySelectorAll('tbody tr.StyledBodyRowTr-kSBvgt');
trElements.forEach(tr => {
const lastTd = tr.lastElementChild;
if (lastTd && !lastTd.querySelector('.spanNum')) {
lastTd.insertBefore(createSequenceSpan(), lastTd.firstChild);
}
});
}
// 行点击高亮功能
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
].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: '180px', minWidth: '180px' },
4: { width: '120px', minWidth: '120px' },
5: { width: '80px', minWidth: '80px' },
6: { width: '70px', minWidth: '70px' }
};
// 应用预定义的列宽
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';
}
}
});
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();
}
});
}
})();