You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1946 lines
110 KiB
1946 lines
110 KiB
window.dash_clientside = Object.assign({}, window.dash_clientside, {
|
|
aichat_clientside: {
|
|
handleUserNewMessageSend: (_, __, ___, value,chatDataList,current_files) => {
|
|
// 判断问题是否为空
|
|
if (!value || value === '@') {
|
|
return window.dash_clientside.no_update;
|
|
}
|
|
// 若本次回调由control+enter组合触发
|
|
if (window.dash_clientside.callback_context.triggered_id === 'control-enter-keypress') {
|
|
return [
|
|
`${value}\n`,// 为value追加换行符
|
|
window.dash_clientside.no_update,
|
|
window.dash_clientside.no_update,
|
|
window.dash_clientside.no_update
|
|
]
|
|
}
|
|
if(chatDataList.length > 0){
|
|
if(chatDataList[chatDataList.length -1].type === 'question'){
|
|
return [
|
|
window.dash_clientside.no_update,
|
|
window.dash_clientside.no_update,
|
|
window.dash_clientside.no_update,
|
|
window.dash_clientside.no_update
|
|
]
|
|
}else{
|
|
if(!chatDataList[chatDataList.length -1].is_end){
|
|
return [
|
|
window.dash_clientside.no_update,
|
|
window.dash_clientside.no_update,
|
|
window.dash_clientside.no_update,
|
|
window.dash_clientside.no_update
|
|
]
|
|
}
|
|
}
|
|
}
|
|
// 否则,视作独立enter触发,执行新内容发送
|
|
let chatData = {"type":"question","content":value,"time": formatDate(new Date()),
|
|
"file": JSON.stringify(current_files)}
|
|
chatDataList.push(chatData)
|
|
return [
|
|
'', // 重置输入框内容,
|
|
{"current_problem":value},
|
|
chatDataList,
|
|
[]
|
|
];
|
|
},
|
|
handleStreamResponse: (data, chat_data_list,current_problem,answer_list) => {
|
|
if(current_problem === null || current_problem.question === ''){
|
|
return window.dash_clientside.no_update;
|
|
}
|
|
// todo 绑定返回消息中的chat_id 来确定是哪条记录 对应起来
|
|
if (chat_data_list.length >0){
|
|
if (chat_data_list[chat_data_list.length - 1].type === 'question'){
|
|
chat_data_list.push({"type":"answer","content":[],"copy_text":"","is_end":false,"is_stop":false})
|
|
answer_list.push({"index":chat_data_list.length-1, "content":[], "is_end":false})
|
|
}
|
|
if (data && data[data.length - 1]) {
|
|
data = JSON.parse(data[data.length -1])
|
|
console.log(data)
|
|
if (data.docs && data.docs[0].length>0){
|
|
//说明是超链接
|
|
let last_answer = answer_list[answer_list.length - 1].content
|
|
if (last_answer.length > 0) {
|
|
if(last_answer[last_answer.length - 1].type === 'text'){
|
|
answer_list[answer_list.length-1].content[last_answer.length - 1].content += data.docs[0]
|
|
}else{
|
|
answer_list[answer_list.length - 1].content.push({"content":data.docs[0],"type":"text"})
|
|
}
|
|
}else{
|
|
answer_list[answer_list.length - 1].content.push({"content":data.docs[0],"type":"docs"})
|
|
}
|
|
}else if (data.G6_ER && data.G6_ER.length > 0){
|
|
//说明是ER图
|
|
answer_list[answer_list.length - 1].content.push({"content":data.G6_ER,"type":"G6_ER"})
|
|
}else if (data.html_image && data.html_image.length > 0){
|
|
//说明是html的echarts图
|
|
answer_list[answer_list.length - 1].content.push({"content":data.html_image,"type":"html_image"})
|
|
}else if (data.table && data.table.length > 0){
|
|
//说明是 表格
|
|
answer_list[answer_list.length - 1].content.push({"content":data.table,"type":"table"})
|
|
}else {
|
|
// 纯文本
|
|
let last_answer = answer_list[answer_list.length - 1].content
|
|
data.choices[0].delta.content = data.choices[0].delta.content.replace("\n","\n\n")
|
|
if (last_answer.length > 0) {
|
|
if(last_answer[last_answer.length - 1].type === 'text'){
|
|
answer_list[answer_list.length-1].content[last_answer.length - 1].content += data.choices[0].delta.content
|
|
}else{
|
|
answer_list[answer_list.length - 1].content.push({"content":data.choices[0].delta.content,"type":"text"})
|
|
}
|
|
}else{
|
|
answer_list[answer_list.length - 1].content.push({"content":data.choices[0].delta.content,"type":"text"})
|
|
}
|
|
}
|
|
if (!chat_data_list[answer_list[answer_list.length - 1].index].is_stop){
|
|
chat_data_list[chat_data_list.length - 1].content =answer_list[answer_list.length - 1].content
|
|
|
|
}
|
|
if (data.is_end){
|
|
answer_list[answer_list.length - 1].is_end = true
|
|
answer_list[answer_list.length - 1].time = formatDate(new Date())
|
|
if (!chat_data_list[answer_list[answer_list.length - 1].index].is_stop){
|
|
chat_data_list[chat_data_list.length - 1].is_end = true
|
|
chat_data_list[chat_data_list.length - 1].time = formatDate(new Date())
|
|
let chat_content = chat_data_list[chat_data_list.length - 1].content
|
|
let copy_text = ""
|
|
for (let i = 0; i < chat_content.length; i++) {
|
|
if (chat_content[i].type === 'text'){
|
|
copy_text += chat_content[i].content
|
|
}
|
|
}
|
|
chat_data_list[chat_data_list.length - 1].copy_text = copy_text
|
|
}
|
|
}
|
|
|
|
return [chat_data_list, answer_list]
|
|
}
|
|
}
|
|
return window.dash_clientside.no_update;
|
|
},
|
|
|
|
handleChatListAreaScroll: (height) => {
|
|
// 自动滚动到底部操作
|
|
let scrollTarget = document.getElementById('chat-scrollbar')
|
|
// 滚动到底部
|
|
scrollTarget.scrollTo({
|
|
top: scrollTarget.scrollHeight
|
|
});
|
|
},
|
|
renderG6: (id,chat_data_list) => {
|
|
for (let i = 0; i < chat_data_list.length; i++) {
|
|
let chat_Data = chat_data_list[i]
|
|
if (chat_Data.type === "answer"){
|
|
let content = chat_Data.content
|
|
const eleArr = document.getElementsByClassName("G6_ER")
|
|
if (eleArr.length>0){
|
|
for (let k = 0; k < eleArr.length; k++) {
|
|
const ele = eleArr[k];
|
|
let indexStr = JSON.parse(ele.id).index.split("_")
|
|
let index = indexStr[0]
|
|
let idx = indexStr[1]
|
|
let g6data = chat_data_list[index].content[idx]
|
|
const isInBBox = (point, bbox) => {
|
|
const {
|
|
x,
|
|
y
|
|
} = point;
|
|
const {
|
|
minX,
|
|
minY,
|
|
maxX,
|
|
maxY
|
|
} = bbox;
|
|
|
|
return x < maxX && x > minX && y > minY && y < maxY;
|
|
};
|
|
const itemHeight = 20;
|
|
G6.registerBehavior("dice-er-scroll", {
|
|
getDefaultCfg() {
|
|
return {
|
|
multiple: true,
|
|
};
|
|
},
|
|
getEvents() {
|
|
return {
|
|
itemHeight,
|
|
wheel: "scorll",
|
|
click: "click",
|
|
"node:mousemove": "move",
|
|
"node:mousedown": "mousedown",
|
|
"node:mouseup": "mouseup"
|
|
};
|
|
},
|
|
scorll(e) {
|
|
e.preventDefault();
|
|
const {
|
|
graph
|
|
} = this;
|
|
const nodes = graph.getNodes().filter((n) => {
|
|
const bbox = n.getBBox();
|
|
|
|
return isInBBox(graph.getPointByClient(e.clientX, e.clientY), bbox);
|
|
});
|
|
|
|
const x = e.deltaX || e.movementX;
|
|
let y = e.deltaY || e.movementY;
|
|
if (!y && navigator.userAgent.indexOf('Firefox') > -1) y = (-e.wheelDelta * 125) / 3
|
|
|
|
if (nodes) {
|
|
const edgesToUpdate = new Set();
|
|
nodes.forEach((node) => {
|
|
const model = node.getModel();
|
|
if (model.attrs.length < 2) {
|
|
return;
|
|
}
|
|
const idx = model.startIndex || 0;
|
|
let startX = model.startX || 0.5;
|
|
let startIndex = idx + y * 0.02;
|
|
startX -= x;
|
|
if (startIndex < 0) {
|
|
startIndex = 0;
|
|
}
|
|
if (startX > 0) {
|
|
startX = 0;
|
|
}
|
|
if (startIndex > model.attrs.length - 1) {
|
|
startIndex = model.attrs.length - 1;
|
|
}
|
|
graph.updateItem(node, {
|
|
startIndex,
|
|
startX,
|
|
});
|
|
node.getEdges().forEach(edge => edgesToUpdate.add(edge))
|
|
});
|
|
// G6 update the related edges when graph.updateItem with a node according to the new properties
|
|
// here you need to update the related edges manualy since the new properties { startIndex, startX } for the nodes are custom, and cannot be recognized by G6
|
|
edgesToUpdate.forEach(edge => edge.refresh())
|
|
}
|
|
|
|
|
|
},
|
|
click(e) {
|
|
const {
|
|
graph
|
|
} = this;
|
|
const item = e.item;
|
|
const shape = e.shape;
|
|
if (!item) {
|
|
return;
|
|
}
|
|
|
|
if (shape.get("name") === "collapse") {
|
|
graph.updateItem(item, {
|
|
collapsed: true,
|
|
size: [300, 50],
|
|
});
|
|
setTimeout(() => graph.layout(), 100);
|
|
} else if (shape.get("name") === "expand") {
|
|
graph.updateItem(item, {
|
|
collapsed: false,
|
|
size: [300, 80],
|
|
});
|
|
setTimeout(() => graph.layout(), 100);
|
|
}
|
|
},
|
|
mousedown(e) {
|
|
this.isMousedown = true;
|
|
},
|
|
mouseup(e) {
|
|
this.isMousedown = false;
|
|
},
|
|
move(e) {
|
|
if (this.isMousedown) return;
|
|
const name = e.shape.get("name");
|
|
const item = e.item;
|
|
|
|
if (name && name.startsWith("item")) {
|
|
this.graph.updateItem(item, {
|
|
selectedIndex: Number(name.split("-")[1]),
|
|
});
|
|
} else {
|
|
this.graph.updateItem(item, {
|
|
selectedIndex: NaN,
|
|
});
|
|
}
|
|
},
|
|
});
|
|
G6.registerEdge("dice-er-edge", {
|
|
draw(cfg, group) {
|
|
const edge = group.cfg.item;
|
|
const sourceNode = edge.getSource().getModel();
|
|
const targetNode = edge.getTarget().getModel();
|
|
|
|
const sourceIndex = sourceNode.attrs.findIndex(
|
|
(e) => e.key === cfg.sourceKey
|
|
);
|
|
|
|
const sourceStartIndex = sourceNode.startIndex || 0;
|
|
|
|
let sourceY = 15;
|
|
|
|
if (!sourceNode.collapsed && sourceIndex > sourceStartIndex - 1) {
|
|
sourceY = 30 + (sourceIndex - sourceStartIndex + 0.5) * itemHeight;
|
|
sourceY = Math.min(sourceY, 80);
|
|
}
|
|
|
|
const targetIndex = targetNode.attrs.findIndex(
|
|
(e) => e.key === cfg.targetKey
|
|
);
|
|
|
|
const targetStartIndex = targetNode.startIndex || 0;
|
|
|
|
let targetY = 15;
|
|
|
|
if (!targetNode.collapsed && targetIndex > targetStartIndex - 1) {
|
|
targetY = (targetIndex - targetStartIndex + 0.5) * itemHeight + 30;
|
|
targetY = Math.min(targetY, 80);
|
|
}
|
|
|
|
const startPoint = {
|
|
...cfg.startPoint
|
|
};
|
|
const endPoint = {
|
|
...cfg.endPoint
|
|
};
|
|
|
|
startPoint.y = startPoint.y + sourceY;
|
|
endPoint.y = endPoint.y + targetY;
|
|
|
|
let shape;
|
|
if (sourceNode.id !== targetNode.id) {
|
|
shape = group.addShape("path", {
|
|
attrs: {
|
|
stroke: "#5B8FF9",
|
|
path: [
|
|
["M", startPoint.x, startPoint.y],
|
|
[
|
|
"C",
|
|
endPoint.x / 3 + (2 / 3) * startPoint.x,
|
|
startPoint.y,
|
|
endPoint.x / 3 + (2 / 3) * startPoint.x,
|
|
endPoint.y,
|
|
endPoint.x,
|
|
endPoint.y,
|
|
],
|
|
],
|
|
endArrow: true,
|
|
},
|
|
// must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
|
|
name: "path-shape",
|
|
});
|
|
} else if (!sourceNode.collapsed) {
|
|
let gap = Math.abs((startPoint.y - endPoint.y) / 3);
|
|
if (startPoint["index"] === 1) {
|
|
gap = -gap;
|
|
}
|
|
shape = group.addShape("path", {
|
|
attrs: {
|
|
stroke: "#5B8FF9",
|
|
path: [
|
|
["M", startPoint.x, startPoint.y],
|
|
[
|
|
"C",
|
|
startPoint.x - gap,
|
|
startPoint.y,
|
|
startPoint.x - gap,
|
|
endPoint.y,
|
|
startPoint.x,
|
|
endPoint.y,
|
|
],
|
|
],
|
|
endArrow: true,
|
|
},
|
|
// must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
|
|
name: "path-shape",
|
|
});
|
|
}
|
|
|
|
return shape;
|
|
},
|
|
afterDraw(cfg, group) {
|
|
const labelCfg = cfg.labelCfg || {};
|
|
const edge = group.cfg.item;
|
|
const sourceNode = edge.getSource().getModel();
|
|
const targetNode = edge.getTarget().getModel();
|
|
if (sourceNode.collapsed && targetNode.collapsed) {
|
|
return;
|
|
}
|
|
const path = group.find(
|
|
(element) => element.get("name") === "path-shape"
|
|
);
|
|
|
|
const labelStyle = G6.Util.getLabelPosition(path, 0.5, 0, 0, true);
|
|
const label = group.addShape("text", {
|
|
attrs: {
|
|
...labelStyle,
|
|
text: cfg.label || '',
|
|
fill: "#000",
|
|
textAlign: "center",
|
|
stroke: "#fff",
|
|
lineWidth: 1,
|
|
},
|
|
});
|
|
label.rotateAtStart(labelStyle.rotate);
|
|
},
|
|
});
|
|
G6.registerNode("dice-er-box", {
|
|
draw(cfg, group) {
|
|
const width = 100;
|
|
const height = 96;
|
|
const itemCount = 10;
|
|
const boxStyle = {
|
|
stroke: "#096DD9",
|
|
radius: 4,
|
|
};
|
|
|
|
const {
|
|
attrs = [],
|
|
startIndex = 0,
|
|
selectedIndex,
|
|
collapsed,
|
|
icon,
|
|
} = cfg;
|
|
const list = attrs;
|
|
const afterList = list.slice(
|
|
Math.floor(startIndex),
|
|
Math.floor(startIndex + itemCount - 1)
|
|
);
|
|
const offsetY = (0.5 - (startIndex % 1)) * itemHeight + 30;
|
|
|
|
group.addShape("rect", {
|
|
attrs: {
|
|
fill: boxStyle.stroke,
|
|
height: 30,
|
|
width,
|
|
radius: [boxStyle.radius, boxStyle.radius, 0, 0],
|
|
},
|
|
draggable: true,
|
|
});
|
|
|
|
let fontLeft = 12;
|
|
|
|
if (icon && icon.show !== false) {
|
|
group.addShape("image", {
|
|
attrs: {
|
|
x: 8,
|
|
y: 8,
|
|
height: 16,
|
|
width: 16,
|
|
...icon,
|
|
},
|
|
});
|
|
fontLeft += 18;
|
|
}
|
|
|
|
group.addShape("text", {
|
|
attrs: {
|
|
y: 22,
|
|
x: fontLeft,
|
|
fill: "#fff",
|
|
text: cfg.label,
|
|
fontSize: 10,
|
|
fontWeight: 500,
|
|
},
|
|
});
|
|
|
|
group.addShape("rect", {
|
|
attrs: {
|
|
x: 0,
|
|
y: collapsed ? 30 : 80,
|
|
height: 15,
|
|
width,
|
|
fill: "#eee",
|
|
radius: [0, 0, boxStyle.radius, boxStyle.radius],
|
|
cursor: "pointer",
|
|
},
|
|
// must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
|
|
name: collapsed ? "expand" : "collapse",
|
|
});
|
|
|
|
group.addShape("text", {
|
|
attrs: {
|
|
x: width / 2 - 6,
|
|
y: (collapsed ? 30 : 80) + 12,
|
|
text: collapsed ? "+" : "-",
|
|
width,
|
|
fill: "#000",
|
|
radius: [0, 0, boxStyle.radius, boxStyle.radius],
|
|
cursor: "pointer",
|
|
},
|
|
// must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
|
|
name: collapsed ? "expand" : "collapse",
|
|
});
|
|
|
|
const keyshape = group.addShape("rect", {
|
|
attrs: {
|
|
x: 0,
|
|
y: 0,
|
|
width,
|
|
height: collapsed ? 45 : height,
|
|
...boxStyle,
|
|
},
|
|
draggable: true,
|
|
});
|
|
|
|
if (collapsed) {
|
|
return keyshape;
|
|
}
|
|
|
|
const listContainer = group.addGroup({});
|
|
listContainer.setClip({
|
|
type: "rect",
|
|
attrs: {
|
|
x: -8,
|
|
y: 30,
|
|
width: width + 16,
|
|
height: 80 - 30,
|
|
},
|
|
});
|
|
listContainer.addShape({
|
|
type: "rect",
|
|
attrs: {
|
|
x: 1,
|
|
y: 30,
|
|
width: width - 2,
|
|
height: 80 - 30,
|
|
fill: "#fff",
|
|
},
|
|
draggable: true,
|
|
});
|
|
|
|
if (list.length > itemCount) {
|
|
const barStyle = {
|
|
width: 4,
|
|
padding: 0,
|
|
boxStyle: {
|
|
stroke: "#00000022",
|
|
},
|
|
innerStyle: {
|
|
fill: "#00000022",
|
|
},
|
|
};
|
|
|
|
listContainer.addShape("rect", {
|
|
attrs: {
|
|
y: 30,
|
|
x: width - barStyle.padding - barStyle.width,
|
|
width: barStyle.width,
|
|
height: height - 30,
|
|
...barStyle.boxStyle,
|
|
},
|
|
});
|
|
|
|
const indexHeight =
|
|
afterList.length > itemCount ?
|
|
(afterList.length / list.length) * height :
|
|
10;
|
|
|
|
listContainer.addShape("rect", {
|
|
attrs: {
|
|
y: 30 +
|
|
barStyle.padding +
|
|
(startIndex / list.length) * (height - 30),
|
|
x: width - barStyle.padding - barStyle.width,
|
|
width: barStyle.width,
|
|
height: Math.min(height, indexHeight),
|
|
...barStyle.innerStyle,
|
|
},
|
|
});
|
|
}
|
|
if (afterList) {
|
|
afterList.forEach((e, i) => {
|
|
const isSelected =
|
|
Math.floor(startIndex) + i === Number(selectedIndex);
|
|
let {
|
|
key = "", type
|
|
} = e;
|
|
if (type) {
|
|
key += " - " + type;
|
|
}
|
|
const label = key.length > 26 ? key.slice(0, 24) + "..." : key;
|
|
|
|
listContainer.addShape("rect", {
|
|
attrs: {
|
|
x: 1,
|
|
y: i * itemHeight - itemHeight / 2 + offsetY,
|
|
width: width - 4,
|
|
height: itemHeight,
|
|
radius: 2,
|
|
lineWidth: 1,
|
|
cursor: "pointer",
|
|
},
|
|
// must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
|
|
name: `item-${Math.floor(startIndex) + i}-content`,
|
|
draggable: true,
|
|
});
|
|
|
|
if (!cfg.hideDot) {
|
|
listContainer.addShape("circle", {
|
|
attrs: {
|
|
x: 0,
|
|
y: i * itemHeight + offsetY,
|
|
r: 3,
|
|
stroke: boxStyle.stroke,
|
|
fill: "white",
|
|
radius: 2,
|
|
lineWidth: 1,
|
|
cursor: "pointer",
|
|
},
|
|
});
|
|
listContainer.addShape("circle", {
|
|
attrs: {
|
|
x: width,
|
|
y: i * itemHeight + offsetY,
|
|
r: 3,
|
|
stroke: boxStyle.stroke,
|
|
fill: "white",
|
|
radius: 2,
|
|
lineWidth: 1,
|
|
cursor: "pointer",
|
|
},
|
|
});
|
|
}
|
|
|
|
listContainer.addShape("text", {
|
|
attrs: {
|
|
x: 12,
|
|
y: i * itemHeight + offsetY + 6,
|
|
text: label,
|
|
fontSize: 10,
|
|
fill: "#000",
|
|
fontFamily: "Avenir,-apple-system,BlinkMacSystemFont,Segoe UI,PingFang SC,Hiragino Sans GB,Microsoft YaHei,Helvetica Neue,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol",
|
|
full: e,
|
|
fontWeight: isSelected ? 500 : 100,
|
|
cursor: "pointer",
|
|
},
|
|
// must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
|
|
name: `item-${Math.floor(startIndex) + i}`,
|
|
});
|
|
});
|
|
}
|
|
|
|
|
|
|
|
return keyshape;
|
|
},
|
|
getAnchorPoints() {
|
|
return [
|
|
[0, 0],
|
|
[1, 0],
|
|
];
|
|
},
|
|
});
|
|
const dataTransform = (data) => {
|
|
const nodes = [];
|
|
const edges = [];
|
|
for(let i = 0;i<data.length;i++){
|
|
let node = data[i]
|
|
nodes.push({...node})
|
|
if (node.attrs) {
|
|
node.attrs.forEach((attr) => {
|
|
if (attr.relation) {
|
|
attr.relation.forEach((relation) => {
|
|
edges.push({
|
|
source: node.id,
|
|
target: relation.nodeId,
|
|
sourceKey: attr.key,
|
|
targetKey: relation.key,
|
|
label: relation.label,
|
|
});
|
|
});
|
|
}
|
|
|
|
});
|
|
}
|
|
}
|
|
return {
|
|
nodes,
|
|
edges,
|
|
};
|
|
}
|
|
const width = ele.scrollWidth;
|
|
const height = (ele.scrollHeight || 500) - 20;
|
|
const graph = new G6.Graph({
|
|
container: ele,
|
|
width:width,
|
|
height:height,
|
|
defaultNode: {
|
|
size: [300, 200],
|
|
type: 'dice-er-box',
|
|
color: '#5B8FF9',
|
|
style: {
|
|
fill: '#9EC9FF',
|
|
lineWidth: 3,
|
|
},
|
|
labelCfg: {
|
|
style: {
|
|
fill: 'black',
|
|
fontSize: 10,
|
|
},
|
|
},
|
|
},
|
|
defaultEdge: {
|
|
type: 'dice-er-edge',
|
|
style: {
|
|
stroke: '#e2e2e2',
|
|
lineWidth: 4,
|
|
endArrow: true,
|
|
},
|
|
},
|
|
modes: {
|
|
default: ['dice-er-scroll', 'drag-node', 'drag-canvas'],
|
|
},
|
|
layout: {
|
|
type: 'dagre',
|
|
rankdir: 'LR',
|
|
align: 'UL',
|
|
controlPoints: true,
|
|
nodesepFunc: () => 0.2,
|
|
ranksepFunc: () => 0.5,
|
|
},
|
|
animate: true,
|
|
fitView: true
|
|
})
|
|
graph.data(dataTransform(g6data.content));
|
|
graph.render();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return window.dash_clientside.no_update;
|
|
},
|
|
full_g6_modal: (style,g6_data) => {
|
|
if (style.display === 'block'){
|
|
const ele = document.getElementById("g6-full-screen-body")
|
|
ele.innerHTML = ""
|
|
const isInBBox = (point, bbox) => {
|
|
const {
|
|
x,
|
|
y
|
|
} = point;
|
|
const {
|
|
minX,
|
|
minY,
|
|
maxX,
|
|
maxY
|
|
} = bbox;
|
|
|
|
return x < maxX && x > minX && y > minY && y < maxY;
|
|
};
|
|
const itemHeight = 20;
|
|
G6.registerBehavior("dice-er-scroll", {
|
|
getDefaultCfg() {
|
|
return {
|
|
multiple: true,
|
|
};
|
|
},
|
|
getEvents() {
|
|
return {
|
|
itemHeight,
|
|
wheel: "scorll",
|
|
click: "click",
|
|
"node:mousemove": "move",
|
|
"node:mousedown": "mousedown",
|
|
"node:mouseup": "mouseup"
|
|
};
|
|
},
|
|
scorll(e) {
|
|
e.preventDefault();
|
|
const {
|
|
graph
|
|
} = this;
|
|
const nodes = graph.getNodes().filter((n) => {
|
|
const bbox = n.getBBox();
|
|
|
|
return isInBBox(graph.getPointByClient(e.clientX, e.clientY), bbox);
|
|
});
|
|
|
|
const x = e.deltaX || e.movementX;
|
|
let y = e.deltaY || e.movementY;
|
|
if (!y && navigator.userAgent.indexOf('Firefox') > -1) y = (-e.wheelDelta * 125) / 3
|
|
|
|
if (nodes) {
|
|
const edgesToUpdate = new Set();
|
|
nodes.forEach((node) => {
|
|
const model = node.getModel();
|
|
if (model.attrs.length < 2) {
|
|
return;
|
|
}
|
|
const idx = model.startIndex || 0;
|
|
let startX = model.startX || 0.5;
|
|
let startIndex = idx + y * 0.02;
|
|
startX -= x;
|
|
if (startIndex < 0) {
|
|
startIndex = 0;
|
|
}
|
|
if (startX > 0) {
|
|
startX = 0;
|
|
}
|
|
if (startIndex > model.attrs.length - 1) {
|
|
startIndex = model.attrs.length - 1;
|
|
}
|
|
graph.updateItem(node, {
|
|
startIndex,
|
|
startX,
|
|
});
|
|
node.getEdges().forEach(edge => edgesToUpdate.add(edge))
|
|
});
|
|
// G6 update the related edges when graph.updateItem with a node according to the new properties
|
|
// here you need to update the related edges manualy since the new properties { startIndex, startX } for the nodes are custom, and cannot be recognized by G6
|
|
edgesToUpdate.forEach(edge => edge.refresh())
|
|
}
|
|
|
|
|
|
},
|
|
click(e) {
|
|
const {
|
|
graph
|
|
} = this;
|
|
const item = e.item;
|
|
const shape = e.shape;
|
|
if (!item) {
|
|
return;
|
|
}
|
|
|
|
if (shape.get("name") === "collapse") {
|
|
graph.updateItem(item, {
|
|
collapsed: true,
|
|
size: [300, 50],
|
|
});
|
|
setTimeout(() => graph.layout(), 100);
|
|
} else if (shape.get("name") === "expand") {
|
|
graph.updateItem(item, {
|
|
collapsed: false,
|
|
size: [300, 80],
|
|
});
|
|
setTimeout(() => graph.layout(), 100);
|
|
}
|
|
},
|
|
mousedown(e) {
|
|
this.isMousedown = true;
|
|
},
|
|
mouseup(e) {
|
|
this.isMousedown = false;
|
|
},
|
|
move(e) {
|
|
if (this.isMousedown) return;
|
|
const name = e.shape.get("name");
|
|
const item = e.item;
|
|
|
|
if (name && name.startsWith("item")) {
|
|
this.graph.updateItem(item, {
|
|
selectedIndex: Number(name.split("-")[1]),
|
|
});
|
|
} else {
|
|
this.graph.updateItem(item, {
|
|
selectedIndex: NaN,
|
|
});
|
|
}
|
|
},
|
|
});
|
|
G6.registerEdge("dice-er-edge", {
|
|
draw(cfg, group) {
|
|
const edge = group.cfg.item;
|
|
const sourceNode = edge.getSource().getModel();
|
|
const targetNode = edge.getTarget().getModel();
|
|
|
|
const sourceIndex = sourceNode.attrs.findIndex(
|
|
(e) => e.key === cfg.sourceKey
|
|
);
|
|
|
|
const sourceStartIndex = sourceNode.startIndex || 0;
|
|
|
|
let sourceY = 15;
|
|
|
|
if (!sourceNode.collapsed && sourceIndex > sourceStartIndex - 1) {
|
|
sourceY = 30 + (sourceIndex - sourceStartIndex + 0.5) * itemHeight;
|
|
sourceY = Math.min(sourceY, 80);
|
|
}
|
|
|
|
const targetIndex = targetNode.attrs.findIndex(
|
|
(e) => e.key === cfg.targetKey
|
|
);
|
|
|
|
const targetStartIndex = targetNode.startIndex || 0;
|
|
|
|
let targetY = 15;
|
|
|
|
if (!targetNode.collapsed && targetIndex > targetStartIndex - 1) {
|
|
targetY = (targetIndex - targetStartIndex + 0.5) * itemHeight + 30;
|
|
targetY = Math.min(targetY, 80);
|
|
}
|
|
|
|
const startPoint = {
|
|
...cfg.startPoint
|
|
};
|
|
const endPoint = {
|
|
...cfg.endPoint
|
|
};
|
|
|
|
startPoint.y = startPoint.y + sourceY;
|
|
endPoint.y = endPoint.y + targetY;
|
|
|
|
let shape;
|
|
if (sourceNode.id !== targetNode.id) {
|
|
shape = group.addShape("path", {
|
|
attrs: {
|
|
stroke: "#5B8FF9",
|
|
path: [
|
|
["M", startPoint.x, startPoint.y],
|
|
[
|
|
"C",
|
|
endPoint.x / 3 + (2 / 3) * startPoint.x,
|
|
startPoint.y,
|
|
endPoint.x / 3 + (2 / 3) * startPoint.x,
|
|
endPoint.y,
|
|
endPoint.x,
|
|
endPoint.y,
|
|
],
|
|
],
|
|
endArrow: true,
|
|
},
|
|
// must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
|
|
name: "path-shape",
|
|
});
|
|
} else if (!sourceNode.collapsed) {
|
|
let gap = Math.abs((startPoint.y - endPoint.y) / 3);
|
|
if (startPoint["index"] === 1) {
|
|
gap = -gap;
|
|
}
|
|
shape = group.addShape("path", {
|
|
attrs: {
|
|
stroke: "#5B8FF9",
|
|
path: [
|
|
["M", startPoint.x, startPoint.y],
|
|
[
|
|
"C",
|
|
startPoint.x - gap,
|
|
startPoint.y,
|
|
startPoint.x - gap,
|
|
endPoint.y,
|
|
startPoint.x,
|
|
endPoint.y,
|
|
],
|
|
],
|
|
endArrow: true,
|
|
},
|
|
// must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
|
|
name: "path-shape",
|
|
});
|
|
}
|
|
|
|
return shape;
|
|
},
|
|
afterDraw(cfg, group) {
|
|
const labelCfg = cfg.labelCfg || {};
|
|
const edge = group.cfg.item;
|
|
const sourceNode = edge.getSource().getModel();
|
|
const targetNode = edge.getTarget().getModel();
|
|
if (sourceNode.collapsed && targetNode.collapsed) {
|
|
return;
|
|
}
|
|
const path = group.find(
|
|
(element) => element.get("name") === "path-shape"
|
|
);
|
|
|
|
const labelStyle = G6.Util.getLabelPosition(path, 0.5, 0, 0, true);
|
|
const label = group.addShape("text", {
|
|
attrs: {
|
|
...labelStyle,
|
|
text: cfg.label || '',
|
|
fill: "#000",
|
|
textAlign: "center",
|
|
stroke: "#fff",
|
|
lineWidth: 1,
|
|
},
|
|
});
|
|
label.rotateAtStart(labelStyle.rotate);
|
|
},
|
|
});
|
|
G6.registerNode("dice-er-box", {
|
|
draw(cfg, group) {
|
|
const width = 100;
|
|
const height = 96;
|
|
const itemCount = 10;
|
|
const boxStyle = {
|
|
stroke: "#096DD9",
|
|
radius: 4,
|
|
};
|
|
|
|
const {
|
|
attrs = [],
|
|
startIndex = 0,
|
|
selectedIndex,
|
|
collapsed,
|
|
icon,
|
|
} = cfg;
|
|
const list = attrs;
|
|
const afterList = list.slice(
|
|
Math.floor(startIndex),
|
|
Math.floor(startIndex + itemCount - 1)
|
|
);
|
|
const offsetY = (0.5 - (startIndex % 1)) * itemHeight + 30;
|
|
|
|
group.addShape("rect", {
|
|
attrs: {
|
|
fill: boxStyle.stroke,
|
|
height: 30,
|
|
width,
|
|
radius: [boxStyle.radius, boxStyle.radius, 0, 0],
|
|
},
|
|
draggable: true,
|
|
});
|
|
|
|
let fontLeft = 12;
|
|
|
|
if (icon && icon.show !== false) {
|
|
group.addShape("image", {
|
|
attrs: {
|
|
x: 8,
|
|
y: 8,
|
|
height: 16,
|
|
width: 16,
|
|
...icon,
|
|
},
|
|
});
|
|
fontLeft += 18;
|
|
}
|
|
|
|
group.addShape("text", {
|
|
attrs: {
|
|
y: 22,
|
|
x: fontLeft,
|
|
fill: "#fff",
|
|
text: cfg.label,
|
|
fontSize: 10,
|
|
fontWeight: 500,
|
|
},
|
|
});
|
|
|
|
group.addShape("rect", {
|
|
attrs: {
|
|
x: 0,
|
|
y: collapsed ? 30 : 80,
|
|
height: 15,
|
|
width,
|
|
fill: "#eee",
|
|
radius: [0, 0, boxStyle.radius, boxStyle.radius],
|
|
cursor: "pointer",
|
|
},
|
|
// must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
|
|
name: collapsed ? "expand" : "collapse",
|
|
});
|
|
|
|
group.addShape("text", {
|
|
attrs: {
|
|
x: width / 2 - 6,
|
|
y: (collapsed ? 30 : 80) + 12,
|
|
text: collapsed ? "+" : "-",
|
|
width,
|
|
fill: "#000",
|
|
radius: [0, 0, boxStyle.radius, boxStyle.radius],
|
|
cursor: "pointer",
|
|
},
|
|
// must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
|
|
name: collapsed ? "expand" : "collapse",
|
|
});
|
|
|
|
const keyshape = group.addShape("rect", {
|
|
attrs: {
|
|
x: 0,
|
|
y: 0,
|
|
width,
|
|
height: collapsed ? 45 : height,
|
|
...boxStyle,
|
|
},
|
|
draggable: true,
|
|
});
|
|
|
|
if (collapsed) {
|
|
return keyshape;
|
|
}
|
|
|
|
const listContainer = group.addGroup({});
|
|
listContainer.setClip({
|
|
type: "rect",
|
|
attrs: {
|
|
x: -8,
|
|
y: 30,
|
|
width: width + 16,
|
|
height: 80 - 30,
|
|
},
|
|
});
|
|
listContainer.addShape({
|
|
type: "rect",
|
|
attrs: {
|
|
x: 1,
|
|
y: 30,
|
|
width: width - 2,
|
|
height: 80 - 30,
|
|
fill: "#fff",
|
|
},
|
|
draggable: true,
|
|
});
|
|
|
|
if (list.length > itemCount) {
|
|
const barStyle = {
|
|
width: 4,
|
|
padding: 0,
|
|
boxStyle: {
|
|
stroke: "#00000022",
|
|
},
|
|
innerStyle: {
|
|
fill: "#00000022",
|
|
},
|
|
};
|
|
|
|
listContainer.addShape("rect", {
|
|
attrs: {
|
|
y: 30,
|
|
x: width - barStyle.padding - barStyle.width,
|
|
width: barStyle.width,
|
|
height: height - 30,
|
|
...barStyle.boxStyle,
|
|
},
|
|
});
|
|
|
|
const indexHeight =
|
|
afterList.length > itemCount ?
|
|
(afterList.length / list.length) * height :
|
|
10;
|
|
|
|
listContainer.addShape("rect", {
|
|
attrs: {
|
|
y: 30 +
|
|
barStyle.padding +
|
|
(startIndex / list.length) * (height - 30),
|
|
x: width - barStyle.padding - barStyle.width,
|
|
width: barStyle.width,
|
|
height: Math.min(height, indexHeight),
|
|
...barStyle.innerStyle,
|
|
},
|
|
});
|
|
}
|
|
if (afterList) {
|
|
afterList.forEach((e, i) => {
|
|
const isSelected =
|
|
Math.floor(startIndex) + i === Number(selectedIndex);
|
|
let {
|
|
key = "", type
|
|
} = e;
|
|
if (type) {
|
|
key += " - " + type;
|
|
}
|
|
const label = key.length > 26 ? key.slice(0, 24) + "..." : key;
|
|
|
|
listContainer.addShape("rect", {
|
|
attrs: {
|
|
x: 1,
|
|
y: i * itemHeight - itemHeight / 2 + offsetY,
|
|
width: width - 4,
|
|
height: itemHeight,
|
|
radius: 2,
|
|
lineWidth: 1,
|
|
cursor: "pointer",
|
|
},
|
|
// must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
|
|
name: `item-${Math.floor(startIndex) + i}-content`,
|
|
draggable: true,
|
|
});
|
|
|
|
if (!cfg.hideDot) {
|
|
listContainer.addShape("circle", {
|
|
attrs: {
|
|
x: 0,
|
|
y: i * itemHeight + offsetY,
|
|
r: 3,
|
|
stroke: boxStyle.stroke,
|
|
fill: "white",
|
|
radius: 2,
|
|
lineWidth: 1,
|
|
cursor: "pointer",
|
|
},
|
|
});
|
|
listContainer.addShape("circle", {
|
|
attrs: {
|
|
x: width,
|
|
y: i * itemHeight + offsetY,
|
|
r: 3,
|
|
stroke: boxStyle.stroke,
|
|
fill: "white",
|
|
radius: 2,
|
|
lineWidth: 1,
|
|
cursor: "pointer",
|
|
},
|
|
});
|
|
}
|
|
|
|
listContainer.addShape("text", {
|
|
attrs: {
|
|
x: 12,
|
|
y: i * itemHeight + offsetY + 6,
|
|
text: label,
|
|
fontSize: 10,
|
|
fill: "#000",
|
|
fontFamily: "Avenir,-apple-system,BlinkMacSystemFont,Segoe UI,PingFang SC,Hiragino Sans GB,Microsoft YaHei,Helvetica Neue,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol",
|
|
full: e,
|
|
fontWeight: isSelected ? 500 : 100,
|
|
cursor: "pointer",
|
|
},
|
|
// must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
|
|
name: `item-${Math.floor(startIndex) + i}`,
|
|
});
|
|
});
|
|
}
|
|
|
|
|
|
|
|
return keyshape;
|
|
},
|
|
getAnchorPoints() {
|
|
return [
|
|
[0, 0],
|
|
[1, 0],
|
|
];
|
|
},
|
|
});
|
|
const dataTransform = (data) => {
|
|
const nodes = [];
|
|
const edges = [];
|
|
for(let i = 0;i<data.length;i++){
|
|
let node = data[i]
|
|
nodes.push({...node})
|
|
if (node.attrs) {
|
|
node.attrs.forEach((attr) => {
|
|
if (attr.relation) {
|
|
attr.relation.forEach((relation) => {
|
|
edges.push({
|
|
source: node.id,
|
|
target: relation.nodeId,
|
|
sourceKey: attr.key,
|
|
targetKey: relation.key,
|
|
label: relation.label,
|
|
});
|
|
});
|
|
}
|
|
|
|
});
|
|
}
|
|
}
|
|
return {
|
|
nodes,
|
|
edges,
|
|
};
|
|
}
|
|
const width = ele.scrollWidth;
|
|
const height = (ele.scrollHeight || 500) - 20;
|
|
const graph = new G6.Graph({
|
|
container: ele,
|
|
width:width,
|
|
height:height,
|
|
defaultNode: {
|
|
size: [300, 200],
|
|
type: 'dice-er-box',
|
|
color: '#5B8FF9',
|
|
style: {
|
|
fill: '#9EC9FF',
|
|
lineWidth: 3,
|
|
},
|
|
labelCfg: {
|
|
style: {
|
|
fill: 'black',
|
|
fontSize: 10,
|
|
},
|
|
},
|
|
},
|
|
defaultEdge: {
|
|
type: 'dice-er-edge',
|
|
style: {
|
|
stroke: '#e2e2e2',
|
|
lineWidth: 4,
|
|
endArrow: true,
|
|
},
|
|
},
|
|
modes: {
|
|
default: ['dice-er-scroll', 'drag-node', 'drag-canvas'],
|
|
},
|
|
layout: {
|
|
type: 'dagre',
|
|
rankdir: 'LR',
|
|
align: 'UL',
|
|
controlPoints: true,
|
|
nodesepFunc: () => 0.2,
|
|
ranksepFunc: () => 0.5,
|
|
},
|
|
animate: true,
|
|
fitView: true
|
|
})
|
|
graph.data(dataTransform(g6_data));
|
|
graph.render();
|
|
}
|
|
return window.dash_clientside.no_update;
|
|
},
|
|
refresh_chat_div: (style,chat_data_list) => {
|
|
if (style.display === 'block'){
|
|
if (chat_data_list.length>0){
|
|
for (let i = 0; i < chat_data_list.length; i++) {
|
|
let chat_data = chat_data_list[i]
|
|
if (chat_data.type === 'answer'){
|
|
let chat_content = chat_data.content
|
|
for (let j = 0; j < chat_content.length; j++) {
|
|
let content = chat_content[j]
|
|
if (content.type === 'G6_ER'){
|
|
let g6Arr = document.getElementsByClassName("G6_ER")
|
|
for (let k = 0; k < g6Arr.length; k++) {
|
|
let g6 = g6Arr[k]
|
|
if (JSON.parse(g6.id).index === i+'_'+j){
|
|
g6.innerHTML = ''
|
|
let g6data = chat_data_list[i].content[j]
|
|
const isInBBox = (point, bbox) => {
|
|
const {
|
|
x,
|
|
y
|
|
} = point;
|
|
const {
|
|
minX,
|
|
minY,
|
|
maxX,
|
|
maxY
|
|
} = bbox;
|
|
|
|
return x < maxX && x > minX && y > minY && y < maxY;
|
|
};
|
|
const itemHeight = 20;
|
|
G6.registerBehavior("dice-er-scroll", {
|
|
getDefaultCfg() {
|
|
return {
|
|
multiple: true,
|
|
};
|
|
},
|
|
getEvents() {
|
|
return {
|
|
itemHeight,
|
|
wheel: "scorll",
|
|
click: "click",
|
|
"node:mousemove": "move",
|
|
"node:mousedown": "mousedown",
|
|
"node:mouseup": "mouseup"
|
|
};
|
|
},
|
|
scorll(e) {
|
|
e.preventDefault();
|
|
const {
|
|
graph
|
|
} = this;
|
|
const nodes = graph.getNodes().filter((n) => {
|
|
const bbox = n.getBBox();
|
|
|
|
return isInBBox(graph.getPointByClient(e.clientX, e.clientY), bbox);
|
|
});
|
|
|
|
const x = e.deltaX || e.movementX;
|
|
let y = e.deltaY || e.movementY;
|
|
if (!y && navigator.userAgent.indexOf('Firefox') > -1) y = (-e.wheelDelta * 125) / 3
|
|
|
|
if (nodes) {
|
|
const edgesToUpdate = new Set();
|
|
nodes.forEach((node) => {
|
|
const model = node.getModel();
|
|
if (model.attrs.length < 2) {
|
|
return;
|
|
}
|
|
const idx = model.startIndex || 0;
|
|
let startX = model.startX || 0.5;
|
|
let startIndex = idx + y * 0.02;
|
|
startX -= x;
|
|
if (startIndex < 0) {
|
|
startIndex = 0;
|
|
}
|
|
if (startX > 0) {
|
|
startX = 0;
|
|
}
|
|
if (startIndex > model.attrs.length - 1) {
|
|
startIndex = model.attrs.length - 1;
|
|
}
|
|
graph.updateItem(node, {
|
|
startIndex,
|
|
startX,
|
|
});
|
|
node.getEdges().forEach(edge => edgesToUpdate.add(edge))
|
|
});
|
|
// G6 update the related edges when graph.updateItem with a node according to the new properties
|
|
// here you need to update the related edges manualy since the new properties { startIndex, startX } for the nodes are custom, and cannot be recognized by G6
|
|
edgesToUpdate.forEach(edge => edge.refresh())
|
|
}
|
|
|
|
|
|
},
|
|
click(e) {
|
|
const {
|
|
graph
|
|
} = this;
|
|
const item = e.item;
|
|
const shape = e.shape;
|
|
if (!item) {
|
|
return;
|
|
}
|
|
|
|
if (shape.get("name") === "collapse") {
|
|
graph.updateItem(item, {
|
|
collapsed: true,
|
|
size: [300, 50],
|
|
});
|
|
setTimeout(() => graph.layout(), 100);
|
|
} else if (shape.get("name") === "expand") {
|
|
graph.updateItem(item, {
|
|
collapsed: false,
|
|
size: [300, 80],
|
|
});
|
|
setTimeout(() => graph.layout(), 100);
|
|
}
|
|
},
|
|
mousedown(e) {
|
|
this.isMousedown = true;
|
|
},
|
|
mouseup(e) {
|
|
this.isMousedown = false;
|
|
},
|
|
move(e) {
|
|
if (this.isMousedown) return;
|
|
const name = e.shape.get("name");
|
|
const item = e.item;
|
|
|
|
if (name && name.startsWith("item")) {
|
|
this.graph.updateItem(item, {
|
|
selectedIndex: Number(name.split("-")[1]),
|
|
});
|
|
} else {
|
|
this.graph.updateItem(item, {
|
|
selectedIndex: NaN,
|
|
});
|
|
}
|
|
},
|
|
});
|
|
G6.registerEdge("dice-er-edge", {
|
|
draw(cfg, group) {
|
|
const edge = group.cfg.item;
|
|
const sourceNode = edge.getSource().getModel();
|
|
const targetNode = edge.getTarget().getModel();
|
|
|
|
const sourceIndex = sourceNode.attrs.findIndex(
|
|
(e) => e.key === cfg.sourceKey
|
|
);
|
|
|
|
const sourceStartIndex = sourceNode.startIndex || 0;
|
|
|
|
let sourceY = 15;
|
|
|
|
if (!sourceNode.collapsed && sourceIndex > sourceStartIndex - 1) {
|
|
sourceY = 30 + (sourceIndex - sourceStartIndex + 0.5) * itemHeight;
|
|
sourceY = Math.min(sourceY, 80);
|
|
}
|
|
|
|
const targetIndex = targetNode.attrs.findIndex(
|
|
(e) => e.key === cfg.targetKey
|
|
);
|
|
|
|
const targetStartIndex = targetNode.startIndex || 0;
|
|
|
|
let targetY = 15;
|
|
|
|
if (!targetNode.collapsed && targetIndex > targetStartIndex - 1) {
|
|
targetY = (targetIndex - targetStartIndex + 0.5) * itemHeight + 30;
|
|
targetY = Math.min(targetY, 80);
|
|
}
|
|
|
|
const startPoint = {
|
|
...cfg.startPoint
|
|
};
|
|
const endPoint = {
|
|
...cfg.endPoint
|
|
};
|
|
|
|
startPoint.y = startPoint.y + sourceY;
|
|
endPoint.y = endPoint.y + targetY;
|
|
|
|
let shape;
|
|
if (sourceNode.id !== targetNode.id) {
|
|
shape = group.addShape("path", {
|
|
attrs: {
|
|
stroke: "#5B8FF9",
|
|
path: [
|
|
["M", startPoint.x, startPoint.y],
|
|
[
|
|
"C",
|
|
endPoint.x / 3 + (2 / 3) * startPoint.x,
|
|
startPoint.y,
|
|
endPoint.x / 3 + (2 / 3) * startPoint.x,
|
|
endPoint.y,
|
|
endPoint.x,
|
|
endPoint.y,
|
|
],
|
|
],
|
|
endArrow: true,
|
|
},
|
|
// must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
|
|
name: "path-shape",
|
|
});
|
|
} else if (!sourceNode.collapsed) {
|
|
let gap = Math.abs((startPoint.y - endPoint.y) / 3);
|
|
if (startPoint["index"] === 1) {
|
|
gap = -gap;
|
|
}
|
|
shape = group.addShape("path", {
|
|
attrs: {
|
|
stroke: "#5B8FF9",
|
|
path: [
|
|
["M", startPoint.x, startPoint.y],
|
|
[
|
|
"C",
|
|
startPoint.x - gap,
|
|
startPoint.y,
|
|
startPoint.x - gap,
|
|
endPoint.y,
|
|
startPoint.x,
|
|
endPoint.y,
|
|
],
|
|
],
|
|
endArrow: true,
|
|
},
|
|
// must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
|
|
name: "path-shape",
|
|
});
|
|
}
|
|
|
|
return shape;
|
|
},
|
|
afterDraw(cfg, group) {
|
|
const labelCfg = cfg.labelCfg || {};
|
|
const edge = group.cfg.item;
|
|
const sourceNode = edge.getSource().getModel();
|
|
const targetNode = edge.getTarget().getModel();
|
|
if (sourceNode.collapsed && targetNode.collapsed) {
|
|
return;
|
|
}
|
|
const path = group.find(
|
|
(element) => element.get("name") === "path-shape"
|
|
);
|
|
|
|
const labelStyle = G6.Util.getLabelPosition(path, 0.5, 0, 0, true);
|
|
const label = group.addShape("text", {
|
|
attrs: {
|
|
...labelStyle,
|
|
text: cfg.label || '',
|
|
fill: "#000",
|
|
textAlign: "center",
|
|
stroke: "#fff",
|
|
lineWidth: 1,
|
|
},
|
|
});
|
|
label.rotateAtStart(labelStyle.rotate);
|
|
},
|
|
});
|
|
G6.registerNode("dice-er-box", {
|
|
draw(cfg, group) {
|
|
const width = 100;
|
|
const height = 96;
|
|
const itemCount = 10;
|
|
const boxStyle = {
|
|
stroke: "#096DD9",
|
|
radius: 4,
|
|
};
|
|
|
|
const {
|
|
attrs = [],
|
|
startIndex = 0,
|
|
selectedIndex,
|
|
collapsed,
|
|
icon,
|
|
} = cfg;
|
|
const list = attrs;
|
|
const afterList = list.slice(
|
|
Math.floor(startIndex),
|
|
Math.floor(startIndex + itemCount - 1)
|
|
);
|
|
const offsetY = (0.5 - (startIndex % 1)) * itemHeight + 30;
|
|
|
|
group.addShape("rect", {
|
|
attrs: {
|
|
fill: boxStyle.stroke,
|
|
height: 30,
|
|
width,
|
|
radius: [boxStyle.radius, boxStyle.radius, 0, 0],
|
|
},
|
|
draggable: true,
|
|
});
|
|
|
|
let fontLeft = 12;
|
|
|
|
if (icon && icon.show !== false) {
|
|
group.addShape("image", {
|
|
attrs: {
|
|
x: 8,
|
|
y: 8,
|
|
height: 16,
|
|
width: 16,
|
|
...icon,
|
|
},
|
|
});
|
|
fontLeft += 18;
|
|
}
|
|
|
|
group.addShape("text", {
|
|
attrs: {
|
|
y: 22,
|
|
x: fontLeft,
|
|
fill: "#fff",
|
|
text: cfg.label,
|
|
fontSize: 10,
|
|
fontWeight: 500,
|
|
},
|
|
});
|
|
|
|
group.addShape("rect", {
|
|
attrs: {
|
|
x: 0,
|
|
y: collapsed ? 30 : 80,
|
|
height: 15,
|
|
width,
|
|
fill: "#eee",
|
|
radius: [0, 0, boxStyle.radius, boxStyle.radius],
|
|
cursor: "pointer",
|
|
},
|
|
// must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
|
|
name: collapsed ? "expand" : "collapse",
|
|
});
|
|
|
|
group.addShape("text", {
|
|
attrs: {
|
|
x: width / 2 - 6,
|
|
y: (collapsed ? 30 : 80) + 12,
|
|
text: collapsed ? "+" : "-",
|
|
width,
|
|
fill: "#000",
|
|
radius: [0, 0, boxStyle.radius, boxStyle.radius],
|
|
cursor: "pointer",
|
|
},
|
|
// must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
|
|
name: collapsed ? "expand" : "collapse",
|
|
});
|
|
|
|
const keyshape = group.addShape("rect", {
|
|
attrs: {
|
|
x: 0,
|
|
y: 0,
|
|
width,
|
|
height: collapsed ? 45 : height,
|
|
...boxStyle,
|
|
},
|
|
draggable: true,
|
|
});
|
|
|
|
if (collapsed) {
|
|
return keyshape;
|
|
}
|
|
|
|
const listContainer = group.addGroup({});
|
|
listContainer.setClip({
|
|
type: "rect",
|
|
attrs: {
|
|
x: -8,
|
|
y: 30,
|
|
width: width + 16,
|
|
height: 80 - 30,
|
|
},
|
|
});
|
|
listContainer.addShape({
|
|
type: "rect",
|
|
attrs: {
|
|
x: 1,
|
|
y: 30,
|
|
width: width - 2,
|
|
height: 80 - 30,
|
|
fill: "#fff",
|
|
},
|
|
draggable: true,
|
|
});
|
|
|
|
if (list.length > itemCount) {
|
|
const barStyle = {
|
|
width: 4,
|
|
padding: 0,
|
|
boxStyle: {
|
|
stroke: "#00000022",
|
|
},
|
|
innerStyle: {
|
|
fill: "#00000022",
|
|
},
|
|
};
|
|
|
|
listContainer.addShape("rect", {
|
|
attrs: {
|
|
y: 30,
|
|
x: width - barStyle.padding - barStyle.width,
|
|
width: barStyle.width,
|
|
height: height - 30,
|
|
...barStyle.boxStyle,
|
|
},
|
|
});
|
|
|
|
const indexHeight =
|
|
afterList.length > itemCount ?
|
|
(afterList.length / list.length) * height :
|
|
10;
|
|
|
|
listContainer.addShape("rect", {
|
|
attrs: {
|
|
y: 30 +
|
|
barStyle.padding +
|
|
(startIndex / list.length) * (height - 30),
|
|
x: width - barStyle.padding - barStyle.width,
|
|
width: barStyle.width,
|
|
height: Math.min(height, indexHeight),
|
|
...barStyle.innerStyle,
|
|
},
|
|
});
|
|
}
|
|
if (afterList) {
|
|
afterList.forEach((e, i) => {
|
|
const isSelected =
|
|
Math.floor(startIndex) + i === Number(selectedIndex);
|
|
let {
|
|
key = "", type
|
|
} = e;
|
|
if (type) {
|
|
key += " - " + type;
|
|
}
|
|
const label = key.length > 26 ? key.slice(0, 24) + "..." : key;
|
|
|
|
listContainer.addShape("rect", {
|
|
attrs: {
|
|
x: 1,
|
|
y: i * itemHeight - itemHeight / 2 + offsetY,
|
|
width: width - 4,
|
|
height: itemHeight,
|
|
radius: 2,
|
|
lineWidth: 1,
|
|
cursor: "pointer",
|
|
},
|
|
// must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
|
|
name: `item-${Math.floor(startIndex) + i}-content`,
|
|
draggable: true,
|
|
});
|
|
|
|
if (!cfg.hideDot) {
|
|
listContainer.addShape("circle", {
|
|
attrs: {
|
|
x: 0,
|
|
y: i * itemHeight + offsetY,
|
|
r: 3,
|
|
stroke: boxStyle.stroke,
|
|
fill: "white",
|
|
radius: 2,
|
|
lineWidth: 1,
|
|
cursor: "pointer",
|
|
},
|
|
});
|
|
listContainer.addShape("circle", {
|
|
attrs: {
|
|
x: width,
|
|
y: i * itemHeight + offsetY,
|
|
r: 3,
|
|
stroke: boxStyle.stroke,
|
|
fill: "white",
|
|
radius: 2,
|
|
lineWidth: 1,
|
|
cursor: "pointer",
|
|
},
|
|
});
|
|
}
|
|
|
|
listContainer.addShape("text", {
|
|
attrs: {
|
|
x: 12,
|
|
y: i * itemHeight + offsetY + 6,
|
|
text: label,
|
|
fontSize: 10,
|
|
fill: "#000",
|
|
fontFamily: "Avenir,-apple-system,BlinkMacSystemFont,Segoe UI,PingFang SC,Hiragino Sans GB,Microsoft YaHei,Helvetica Neue,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol",
|
|
full: e,
|
|
fontWeight: isSelected ? 500 : 100,
|
|
cursor: "pointer",
|
|
},
|
|
// must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type
|
|
name: `item-${Math.floor(startIndex) + i}`,
|
|
});
|
|
});
|
|
}
|
|
|
|
|
|
|
|
return keyshape;
|
|
},
|
|
getAnchorPoints() {
|
|
return [
|
|
[0, 0],
|
|
[1, 0],
|
|
];
|
|
},
|
|
});
|
|
const dataTransform = (data) => {
|
|
const nodes = [];
|
|
const edges = [];
|
|
for(let i = 0;i<data.length;i++){
|
|
let node = data[i]
|
|
nodes.push({...node})
|
|
if (node.attrs) {
|
|
node.attrs.forEach((attr) => {
|
|
if (attr.relation) {
|
|
attr.relation.forEach((relation) => {
|
|
edges.push({
|
|
source: node.id,
|
|
target: relation.nodeId,
|
|
sourceKey: attr.key,
|
|
targetKey: relation.key,
|
|
label: relation.label,
|
|
});
|
|
});
|
|
}
|
|
|
|
});
|
|
}
|
|
}
|
|
return {
|
|
nodes,
|
|
edges,
|
|
};
|
|
}
|
|
const width = g6.scrollWidth;
|
|
const height = (g6.scrollHeight || 500) - 20;
|
|
const graph = new G6.Graph({
|
|
container: g6,
|
|
width:width,
|
|
height:height,
|
|
defaultNode: {
|
|
size: [300, 200],
|
|
type: 'dice-er-box',
|
|
color: '#5B8FF9',
|
|
style: {
|
|
fill: '#9EC9FF',
|
|
lineWidth: 3,
|
|
},
|
|
labelCfg: {
|
|
style: {
|
|
fill: 'black',
|
|
fontSize: 10,
|
|
},
|
|
},
|
|
},
|
|
defaultEdge: {
|
|
type: 'dice-er-edge',
|
|
style: {
|
|
stroke: '#e2e2e2',
|
|
lineWidth: 4,
|
|
endArrow: true,
|
|
},
|
|
},
|
|
modes: {
|
|
default: ['dice-er-scroll', 'drag-node', 'drag-canvas'],
|
|
},
|
|
layout: {
|
|
type: 'dagre',
|
|
rankdir: 'LR',
|
|
align: 'UL',
|
|
controlPoints: true,
|
|
nodesepFunc: () => 0.2,
|
|
ranksepFunc: () => 0.5,
|
|
},
|
|
animate: true,
|
|
fitView: true
|
|
})
|
|
if (g6data){
|
|
graph.data(dataTransform(g6data.content));
|
|
graph.render();
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
if (content.type === 'html_image'){
|
|
let htmlArr = document.getElementsByClassName("iframeDoc")
|
|
for (let k = 0; k < htmlArr.length; k++) {
|
|
let html = htmlArr[k]
|
|
if (JSON.parse(html.id).index === i+'_'+j){
|
|
html.contentWindow.location.reload()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return window.dash_clientside.no_update;
|
|
},
|
|
down_load_table: (nClicks,chat_data_list) => {
|
|
if (nClicks != null && window.dash_clientside.callback_context.triggered_id
|
|
&& window.dash_clientside.callback_context.triggered_id.type === 'down_load_table'
|
|
&& (window.dash_clientside.callback_context.triggered[0].value)){
|
|
let id_index = window.dash_clientside.callback_context.triggered_id.index
|
|
let chat_idx = id_index.split("_")[0]
|
|
let idx = id_index.split("_")[1]
|
|
let tableData = chat_data_list[parseInt(chat_idx)].content[parseInt(idx)].content
|
|
let sheet = XLSX.utils.json_to_sheet(tableData)
|
|
let sheetName = 'sheet1'
|
|
let workbook = {
|
|
SheetNames: [sheetName],
|
|
Sheets: {}
|
|
};
|
|
workbook.Sheets[sheetName] = sheet;
|
|
// 生成excel的配置项
|
|
let wopts = {
|
|
bookType: 'xlsx', // 要生成的文件类型
|
|
bookSST: false, // 是否生成Shared String Table,官方解释是,如果开启生成速度会下降,但在低版本IOS设备上有更好的兼容性
|
|
type: 'binary'
|
|
};
|
|
let wbout = XLSX.write(workbook, wopts);
|
|
let blob = new Blob([s2ab(wbout)], {type:"application/octet-stream"});
|
|
// 字符串转ArrayBuffer
|
|
function s2ab(s) {
|
|
let buf = new ArrayBuffer(s.length);
|
|
let view = new Uint8Array(buf);
|
|
for (let i=0; i!==s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
|
|
return buf;
|
|
}
|
|
blob = URL.createObjectURL(blob);
|
|
let aLink = document.createElement('a');
|
|
aLink.href = blob;
|
|
aLink.download = "导出表格.xlsx"; // HTML5新增的属性,指定保存文件名,可以不要后缀,注意,file:///模式下不会生效
|
|
let event;
|
|
if(window.MouseEvent) event = new MouseEvent('click');
|
|
else
|
|
{
|
|
event = document.createEvent('MouseEvents');
|
|
event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
|
|
}
|
|
aLink.dispatchEvent(event);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
function padZero(num) {
|
|
return num < 10 ? '0' + num : num;
|
|
}
|
|
|
|
function formatDate(date) {
|
|
const year = date.getFullYear();
|
|
const month = padZero(date.getMonth() + 1); // 月份从0开始,所以需要+1
|
|
const day = padZero(date.getDate());
|
|
const hours = padZero(date.getHours());
|
|
const minutes = padZero(date.getMinutes());
|
|
const seconds = padZero(date.getSeconds());
|
|
|
|
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
|
}
|
|
|