修改看板

master
夜笙歌 14 hours ago
parent ebb008f0f8
commit 71ddf4cb37

@ -36,7 +36,7 @@
"diagram-js": "12.3.0", "diagram-js": "12.3.0",
"didi": "9.0.2", "didi": "9.0.2",
"echarts": "5.5.0", "echarts": "5.5.0",
"element-plus": "2.9.0", "element-plus": "2.9.1",
"file-saver": "2.0.5", "file-saver": "2.0.5",
"fuse.js": "7.0.0", "fuse.js": "7.0.0",
"highlight.js": "11.9.0", "highlight.js": "11.9.0",

@ -1,5 +1,5 @@
<template> <template>
<div > <div>
<div class="leftPanel"> <div class="leftPanel">
<el-tabs v-model="leftPanelState" class="demo-tabs" type="border-card"> <el-tabs v-model="leftPanelState" class="demo-tabs" type="border-card">
<el-tab-pane label="图表组件" name="1"> <el-tab-pane label="图表组件" name="1">
@ -264,9 +264,13 @@
</div> </div>
</div> </div>
<div class="rightPanel"> <div class="rightPanel">
<el-collapse> <el-collapse
v-if="Object.keys(nodeDataForm).length>0 || Object.keys(nodeAttrForm).length>0|| Object.keys(customDataForm).length>0">
<el-collapse-item title="基础设置" name="1" v-if="Object.keys(nodeDataForm).length>0"> <el-collapse-item title="基础设置" name="1" v-if="Object.keys(nodeDataForm).length>0">
<el-form :model="nodeDataForm" label-width="auto" style="max-width: 600px"> <el-form :model="nodeDataForm" label-width="auto" style="max-width: 600px">
<el-form-item label="组件名称">
<el-input v-model="nodeDataForm.name" style="width: 100%" />
</el-form-item>
<el-form-item label="x"> <el-form-item label="x">
<el-input-number :precision="0" :step="1" v-model="nodeDataForm.position.x" style="width: 100%" <el-input-number :precision="0" :step="1" v-model="nodeDataForm.position.x" style="width: 100%"
@change="updateNode(nodeDataForm.id,nodeDataForm,{ replace: true })" /> @change="updateNode(nodeDataForm.id,nodeDataForm,{ replace: true })" />
@ -285,7 +289,7 @@
</el-form-item> </el-form-item>
</el-form> </el-form>
</el-collapse-item> </el-collapse-item>
<el-collapse-item title="数据配置" name="2" v-if="Object.keys(nodeAttrForm).length>0"> <el-collapse-item title="组件配置" name="2" v-if="Object.keys(nodeAttrForm).length>0">
<el-form :model="nodeAttrForm" label-width="auto" style="max-width: 600px"> <el-form :model="nodeAttrForm" label-width="auto" style="max-width: 600px">
<el-form-item label="默认内容" v-if="Object.keys(nodeAttrForm).includes('defaultInputArea')"> <el-form-item label="默认内容" v-if="Object.keys(nodeAttrForm).includes('defaultInputArea')">
<el-input type="textarea" v-model="nodeAttrForm.defaultInputArea" style="width: 100%" /> <el-input type="textarea" v-model="nodeAttrForm.defaultInputArea" style="width: 100%" />
@ -411,14 +415,60 @@
<el-form-item label="饼图标签" v-if="Object.keys(nodeAttrForm).includes('label')"> <el-form-item label="饼图标签" v-if="Object.keys(nodeAttrForm).includes('label')">
<el-switch v-model="nodeAttrForm.label" active-text="" inactive-text="" /> <el-switch v-model="nodeAttrForm.label" active-text="" inactive-text="" />
</el-form-item> </el-form-item>
<el-form-item label="表格设置" v-if="Object.keys(nodeAttrForm).includes('tableOptions')"> <el-form-item label="表格设置" v-if="Object.keys(nodeAttrForm).includes('tableOptions')">
<el-button plain @click="tableOptionsVisible = true"> <el-button plain @click="tableOptionsVisible = true">
配置表格 配置表格
</el-button> </el-button>
</el-form-item> </el-form-item>
<el-form-item label="表格动画设置" v-if="Object.keys(nodeAttrForm).includes('tableClassOption')">
<el-button plain @click="tableClassOptionsVisible = true">
配置动画
</el-button>
</el-form-item>
<el-form-item label="表头高度" v-if="Object.keys(nodeAttrForm).includes('thHeight')">
<el-input v-model="nodeAttrForm.thHeight" style="width: 100%" />
</el-form-item>
<el-form-item label="行高度" v-if="Object.keys(nodeAttrForm).includes('tdHeight')">
<el-input v-model="nodeAttrForm.tdHeight" style="width: 100%" />
</el-form-item>
<el-form-item label="表头文字颜色" v-if="Object.keys(nodeAttrForm).includes('thColor')">
<el-color-picker v-model="nodeAttrForm.thColor" show-alpha />
</el-form-item>
<el-form-item label="行文字颜色" v-if="Object.keys(nodeAttrForm).includes('tdColor')">
<el-badge value="99" class="item" :offset="[-8,0]"
v-for="(i,k) in nodeAttrForm.tdColor">
<el-color-picker style="margin-right: 4px;" v-model="nodeAttrForm.tdColor[k]" show-alpha />
<template #content>
<div class="custom-content" @click="nodeAttrForm.tdColor.splice(k, 1)">
<el-icon>
<Close />
</el-icon>
</div>
</template>
</el-badge>
<el-button :icon="Plus" @click="nodeAttrForm.tdColor.push('#fff')" />
</el-form-item>
<el-form-item label="表头背景颜色" v-if="Object.keys(nodeAttrForm).includes('thBgColor')">
<el-color-picker v-model="nodeAttrForm.thBgColor" show-alpha />
</el-form-item>
<el-form-item label="行背景颜色" v-if="Object.keys(nodeAttrForm).includes('tdBgColor')">
<el-badge value="99" class="item" :offset="[-8,0]"
v-for="(i,k) in nodeAttrForm.tdBgColor">
<el-color-picker style="margin-right: 8px;"
v-model="nodeAttrForm.tdBgColor[k]" show-alpha />
<template #content>
<div class="custom-content" @click="nodeAttrForm.tdBgColor.splice(k, 1)">
<el-icon>
<Close />
</el-icon>
</div>
</template>
</el-badge>
<el-button :icon="Plus" @click="nodeAttrForm.tdBgColor.push('#fff')" />
</el-form-item>
</el-form> </el-form>
</el-collapse-item> </el-collapse-item>
<el-collapse-item title="自定义配置" name="3" v-if="customDataForm"> <el-collapse-item title="自定义配置" name="3" v-if="Object.keys(customDataForm||{}).length>0">
<el-form :model="customDataForm" label-width="auto" style="max-width: 600px"> <el-form :model="customDataForm" label-width="auto" style="max-width: 600px">
<el-form-item label="自定义内容" v-if="Object.keys(customDataForm).includes('option')"> <el-form-item label="自定义内容" v-if="Object.keys(customDataForm).includes('option')">
<el-input type="textarea" v-model="customDataForm.option" style="width: 100%" /> <el-input type="textarea" v-model="customDataForm.option" style="width: 100%" />
@ -434,9 +484,38 @@
</template> </template>
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-form-item label="拓展代码" v-if="Object.keys(customDataForm).includes('script')">
<el-input type="textarea" v-model="customDataForm.script" style="width: 100%" />
</el-form-item>
</el-form> </el-form>
</el-collapse-item> </el-collapse-item>
</el-collapse> </el-collapse>
<div v-else>
<div v-for="i in nodes.filter(e=>e.type!=='area').reverse()" class="nodeCard">
<div style="display: inline-block;margin-right: 8px;">
<el-dropdown trigger="click" @command="nodeOperate">
<span class="el-dropdown-link">
<el-icon class="settingIcon">
<Setting />
</el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item :command="{type:'toTop',node:i}">置顶</el-dropdown-item>
<el-dropdown-item :command="{type:'moveUp',node:i}">上移一层</el-dropdown-item>
<el-dropdown-item :command="{type:'moveDown',node:i}">下移一层</el-dropdown-item>
<el-dropdown-item :command="{type:'copy',node:i}">复制</el-dropdown-item>
<el-dropdown-item :command="{type:'del',node:i}">删除</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
<span class="cardSpan">
{{ i.name }}
</span>
</div>
</div>
</div> </div>
</div> </div>
<el-dialog v-model="pageSettingVisible" title="页面设置" width="500"> <el-dialog v-model="pageSettingVisible" title="页面设置" width="500">
@ -493,6 +572,28 @@
<el-input v-model="scope.row.width" style="width: 100%" /> <el-input v-model="scope.row.width" style="width: 100%" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="对齐方式">
<template #default="scope">
<el-select
v-model="scope.row.align"
placeholder="请选择"
style="width: 100%"
>
<el-option
label="左对齐"
value="left"
/>
<el-option
label="居中"
value="center"
/>
<el-option
label="右对齐"
value="right"
/>
</el-select>
</template>
</el-table-column>
<el-table-column label="类型"> <el-table-column label="类型">
<template #default="scope"> <template #default="scope">
<el-select <el-select
@ -524,6 +625,24 @@
<el-button size="small" :icon="Plus" style="width: 100%" @click="nodeAttrForm.tableOptions.push({})"> <el-button size="small" :icon="Plus" style="width: 100%" @click="nodeAttrForm.tableOptions.push({})">
</el-button> </el-button>
</el-dialog> </el-dialog>
<el-dialog v-model="tableClassOptionsVisible" title="动画设置" width="500">
<el-form :model="nodeAttrForm.tableClassOption" label-width="auto">
<el-form-item label="滚动速度">
<el-input-number :controls="false" v-model="nodeAttrForm.tableClassOption.step" autocomplete="off" />
</el-form-item>
<el-form-item label="开启滚动的数据量">
<el-input-number :controls="false" v-model="nodeAttrForm.tableClassOption.limitMoveNum" autocomplete="off" />
</el-form-item>
<el-form-item label="鼠标悬停停止">
<el-switch v-model="nodeAttrForm.tableClassOption.hoverStop" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="tableClassOptionsVisible = false">关闭</el-button>
</div>
</template>
</el-dialog>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@ -552,7 +671,7 @@ import ImgNode from './nodes/form/imgNode.vue';
import AreaNode from './nodes/other/areaNode.vue'; import AreaNode from './nodes/other/areaNode.vue';
import TableNode from './nodes/form/tableNode.vue'; import TableNode from './nodes/form/tableNode.vue';
import ScrollTableNode from './nodes/form/scrollTableNode.vue'; import ScrollTableNode from './nodes/form/scrollTableNode.vue';
import tool from './tool'; import tool, { options } from './tool';
import { getDataSourceList } from '@/views/boardGenerate/api/dataSource'; import { getDataSourceList } from '@/views/boardGenerate/api/dataSource';
import { editBoardApi, getBoardApi } from '@/views/boardGenerate/api/boardList'; import { editBoardApi, getBoardApi } from '@/views/boardGenerate/api/boardList';
@ -564,7 +683,9 @@ const { onDragStart, onDrop, onDragOver } = tool();
// flow // flow
const { const {
addEdges, addEdges,
updateNode updateNode,
removeNodes,
addNodes
} = useVueFlow(); } = useVueFlow();
const baseUrl = import.meta.env.VITE_APP_BASE_API; const baseUrl = import.meta.env.VITE_APP_BASE_API;
@ -575,6 +696,7 @@ const loading = ref(false)
const pageSettingVisible = ref(false); const pageSettingVisible = ref(false);
const pageSettingForm = ref({}); const pageSettingForm = ref({});
const tableOptionsVisible = ref(false); const tableOptionsVisible = ref(false);
const tableClassOptionsVisible = ref(false);
const pageTitle = ref('页面名称'); const pageTitle = ref('页面名称');
const leftPanelState = ref('1'); const leftPanelState = ref('1');
const pageData = ref({}); const pageData = ref({});
@ -703,7 +825,6 @@ const logEvent = async (eventname, event) => {
nodeAttrForm.value = event.node.data.options; nodeAttrForm.value = event.node.data.options;
nodeDataForm.value = event.node; nodeDataForm.value = event.node;
customDataForm.value = event.node.data.customData; customDataForm.value = event.node.data.customData;
console.log(event.node.data);
break; break;
case 'nodeDrag': case 'nodeDrag':
console.log(event.nodes); console.log(event.nodes);
@ -754,7 +875,7 @@ const pageSetting = () => {
pageSettingForm.value = pageData.value; pageSettingForm.value = pageData.value;
}; };
const save = () => { const save = () => {
loading.value = true;
editBoardApi({ editBoardApi({
...boardData.value, ...boardData.value,
customContent: JSON.stringify(pageSettingForm.value), customContent: JSON.stringify(pageSettingForm.value),
@ -806,7 +927,10 @@ const save = () => {
type: 'success', type: 'success',
message: '保存成功' message: '保存成功'
}); });
}); })
.finally(() => {
loading.value = false;
})
// localStorage.setItem('NODES', JSON.stringify(nodes.value.map(e => { // localStorage.setItem('NODES', JSON.stringify(nodes.value.map(e => {
// let data = {}; // let data = {};
// let savaField = ['customData', 'options']; // let savaField = ['customData', 'options'];
@ -882,8 +1006,47 @@ const setPageData = () => {
// localStorage.setItem('PAGEDATA', JSON.stringify(pageData.value)); // localStorage.setItem('PAGEDATA', JSON.stringify(pageData.value));
pageSettingVisible.value = false; pageSettingVisible.value = false;
}; };
const asd = () => {
console.log(12312);
};
const nodeOperate = (data) => {
let type = data.type;
const index = nodes.value.indexOf(data.node);
let arr = JSON.parse(JSON.stringify(nodes.value));
const item = arr[index];
switch (type) {
case 'toTop':
nodes.value = [...nodes.value.filter(e => e.id !== data.node.id), data.node];
break;
case 'moveUp':
arr.splice(index, 1);
arr.splice(index + 1, 0, item);
nodes.value = arr;
break;
case 'moveDown':
arr.splice(index, 1);
arr.splice(index - 1, 0, item);
nodes.value = arr;
break;
case 'copy':
item.id = options.getId(item.type);
console.log(item);
addNodes(item);
break;
case 'del':
removeNodes([data.node.id]);
break;
}
};
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.custom-content {
display: flex;
align-items: center;
justify-content: center;
gap: 4px;
}
:deep(.vue-flow__node-area) { :deep(.vue-flow__node-area) {
z-index: -1 !important; z-index: -1 !important;
pointer-events: none !important; pointer-events: none !important;
@ -994,6 +1157,53 @@ const setPageData = () => {
width: 384px; width: 384px;
height: 216px height: 216px
} }
.nodeCard {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
height: 50px;
padding: 0 12px;
background-color: #fff;
border-radius: 8px;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
&:hover {
background-color: #f0f7ff;
box-shadow: 0 -4px 8px -4px rgba(0, 0, 0, 0.08),
0 4px 8px -4px rgba(0, 0, 0, 0.08);
.settingIcon {
color: #409EFF;
}
}
.cardSpan {
flex: 1;
font-size: 14px;
color: #333;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
padding-left: 8px;
}
.settingIcon {
font-size: 18px;
color: #888;
transition: color 0.3s ease;
}
.el-dropdown-link {
display: flex;
align-items: center;
height: 100%;
}
}
</style> </style>
<style> <style>

@ -99,11 +99,9 @@ const getOutputData = () => {
props.data.customData.outputData.forEach(item => { props.data.customData.outputData.forEach(item => {
output[item.fieldOne] = parseData(data, item.fieldTwo); output[item.fieldOne] = parseData(data, item.fieldTwo);
}); });
console.log('output', output);
props.data.outputData = output; props.data.outputData = output;
}).catch(e => { }).catch(e => {
isErr.value = true; isErr.value = true;
console.log(e);
}); });
}; };

@ -63,8 +63,6 @@ const convertData = () => {
res[item] = props.inputData[item]; res[item] = props.inputData[item];
} }
}); });
console.log(props.inputData);
console.log(props.data.options.dataMap);
props.data.outputData = res; props.data.outputData = res;
}; };
watch(() => JSON.parse(JSON.stringify(props.inputData)), (obj1, obj2) => { watch(() => JSON.parse(JSON.stringify(props.inputData)), (obj1, obj2) => {

@ -6,20 +6,38 @@
<div class="custom-node" <div class="custom-node"
:style="{width:props.dimensions.width+'px',height:props.dimensions.height+'px',pointerEvents:props.isView?'auto': 'none'}"> :style="{width:props.dimensions.width+'px',height:props.dimensions.height+'px',pointerEvents:props.isView?'auto': 'none'}">
<div style="background-color: #094170"> <div style="background-color: #094170"
<div class="scrollTable" style="font-weight: bold;" :style="`width: ${i.width || '100px'} `" :style="{ height:props.data.options.thHeight, color:props.data.options.thColor,backgroundColor:props.data.options?.thBgColor }">
<div class="scrollTable" style="font-weight: bold;"
:style="{width: i.width || '100px',textAlign:i.align || 'left'}"
v-for="i in props.data.options.tableOptions"> v-for="i in props.data.options.tableOptions">
{{ i.name }} {{ i.name }}
</div> </div>
</div> </div>
{{ props.inputData.tableData }}
<vue3ScrollSeamless <vue3ScrollSeamless
:classOptions="classOptions" :classOptions="props.data.options.tableClassOption ||{}"
:dataList="list" :dataList="props.inputData.tableData"
v-if="scrollShow"
style=" overflow: hidden"
:style="{height: `calc(100% - ${props.data.options.thHeight || '0px'})`}"
> >
<div class="scrollTable" style="font-weight: bold;" :style="`width: ${i.width || '100px'} `" <div
v-for="(i,k) in props.data.options.tableOptions"> :style="{color:props.data.options.tdColor[index % props.data.options.tdColor?.length],backgroundColor:(props.data.options?.tdBgColor||[])[index % props.data.options.tdBgColor?.length]}"
{{ ((props.inputData.tableData || [])[k] || {})[i.field] }} v-for="(data,index) in props.inputData.tableData" :key="index ">
<div class="scrollTable" style="font-weight: bold;"
:style="{width: i.width || '100px',height:props.data.options.tdHeight,lineHeight:props.data.options.tdHeight,textAlign:i.align || 'left' }"
v-for="(i,k) in props.data.options.tableOptions">
<span v-if="i.type === '文本'">{{ ((props.inputData.tableData || [])[k] || {})[i.field] }}</span>
<el-tag type="primary" v-else-if="i.type === ''">
{{ ((props.inputData.tableData || [])[k] || {})[i.field] }}
</el-tag>
<el-image :preview-src-list="[((props.inputData.tableData || [])[k] || {})[i.field]]"
v-else-if="i.type === '图片'" style="width: 100%;" :style="{height:props.data.options.tdHeight}"
:src="((props.inputData.tableData || [])[k] || {})[i.field]" fit="contain" />
<span v-else>{{ ((props.inputData.tableData || [])[k] || {})[i.field] }}</span>
</div>
</div> </div>
</vue3ScrollSeamless> </vue3ScrollSeamless>
</div> </div>
@ -32,48 +50,10 @@ import { NodeResizer } from '@vue-flow/node-resizer';
import { Handle, Position } from '@vue-flow/core'; import { Handle, Position } from '@vue-flow/core';
import { vue3ScrollSeamless } from 'vue3-scroll-seamless'; import { vue3ScrollSeamless } from 'vue3-scroll-seamless';
let a = { const scrollShow = ref(true);
table: [
{
a: 1,
b: 2
},
{
a: 1,
b: 2
},
{
a: 1,
b: 2
}
]
};
console.log(JSON.stringify(a));
let list = ref([{
'title': '水调歌头·明月几时有'
}, {
'title': '苏轼 〔宋代〕'
}, {
'title': '明月几时有?把酒问青天。'
}, {
'title': '不知天上宫阙,今夕是何年。'
}, {
'title': '我欲乘风归去,又恐琼楼玉宇,高处不胜寒'
}, {
'title': '起舞弄清影,何似在人间。'
}, {
'title': '转朱阁,低绮户,照无眠。'
}, {
'title': '不应有恨,何事长向别时圆?'
}, {
'title': '人有悲欢离合,月有阴晴圆缺,此事古难全。'
}, {
'title': '但愿人长久,千里共婵娟。'
}
]);
const classOptions = ref({ const classOptions = ref({
limitMoveNum: 6 limitMoveNum: 5
}); });
const props = defineProps({ const props = defineProps({
isView: { isView: {
@ -107,6 +87,14 @@ const props = defineProps({
}); });
console.log(props.inputData); console.log(props.inputData);
watch(() => JSON.parse(JSON.stringify([props.inputData?.tableData || '', props.data.options])), (obj1, obj2) => {
if (JSON.stringify(obj1) !== JSON.stringify(obj2)) {
scrollShow.value = false;
nextTick(() => {
scrollShow.value = true;
});
}
}, { deep: true, immediate: true });
const emit = defineEmits(['resize']); const emit = defineEmits(['resize']);
const resize = (e) => { const resize = (e) => {
emit('resize', e, props.id); emit('resize', e, props.id);
@ -119,7 +107,7 @@ const resize = (e) => {
.scrollTable { .scrollTable {
display: inline-block; display: inline-block;
color: #fff vertical-align: top;
} }
</style> </style>

@ -36,7 +36,16 @@ const getOption = (e) => {
} else if (e === 'img') { } else if (e === 'img') {
return { imgSrc: '' }; return { imgSrc: '' };
} else if (e === 'scrollTable' || e === 'table') { } else if (e === 'scrollTable' || e === 'table') {
return { tableOptions: [] }; return {
tableOptions: [],
tableClassOption: {},
thHeight: '20px',
tdHeight: '20px',
thColor: '#fff',
tdColor: ['#fff'],
thBgColor: '#fff',
tdBgColor: ['#000']
};
} else { } else {
return {}; return {};
} }
@ -67,7 +76,7 @@ const getNodeSize = (e) => {
const tool = () => { const tool = () => {
const nodeType = ref(''); const nodeType = ref('');
const customData = ref(''); const customData = ref({ script: '' });
const { addNodes, screenToFlowCoordinate, onNodesInitialized, updateNode } = useVueFlow(); const { addNodes, screenToFlowCoordinate, onNodesInitialized, updateNode } = useVueFlow();
const onDragStart = (event, type, data) => { const onDragStart = (event, type, data) => {
if (event.dataTransfer) { if (event.dataTransfer) {
@ -76,7 +85,7 @@ const tool = () => {
} }
nodeType.value = type; nodeType.value = type;
customData.value = data; customData.value = { ...customData.value, ...(data || {}) };
document.addEventListener('drop', onDragEnd); document.addEventListener('drop', onDragEnd);
}; };
const onDragEnd = () => { const onDragEnd = () => {
@ -138,5 +147,5 @@ export const options = {
} }
} }
return false; return false;
} },getId
}; };

Loading…
Cancel
Save