commit
This commit is contained in:
@@ -0,0 +1,487 @@
|
||||
<template>
|
||||
<div>
|
||||
<ul class="opera-ul">
|
||||
<Tooltip content="新增节点" placement="top" transfer>
|
||||
<li @click="showAddNode" :class="{ disabled: !nodePermissions.canCreate }">
|
||||
<SvgIcon name="tree-icon-1" />
|
||||
</li>
|
||||
</Tooltip>
|
||||
<Tooltip content="编辑节点" placement="top" transfer>
|
||||
<li @click="showEditNode" :class="{ disabled: !nodePermissions.canUpdate }">
|
||||
<SvgIcon name="tree-icon-2" />
|
||||
</li>
|
||||
</Tooltip>
|
||||
<Tooltip content="移动节点" placement="top" transfer>
|
||||
<li @click="showMoveNode" :class="{ disabled: !nodePermissions.canMove }">
|
||||
<SvgIcon name="tree-icon-3" />
|
||||
</li>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip content="删除节点" placement="top" transfer>
|
||||
<li @click="showDeleteNode" :class="{ disabled: !nodePermissions.canDelete }">
|
||||
<SvgIcon name="tree-icon-4" />
|
||||
</li>
|
||||
</Tooltip>
|
||||
</ul>
|
||||
<Select class="mt-8" v-model="curSubJectId" placeholder="请选择学科">
|
||||
<Option v-for="item in subjectComp" :value="item.k">{{ item.v }}</Option>
|
||||
</Select>
|
||||
<MyTree v-if="!treeLoading" ref="refMyTree" :autoClick="false" :treeData="treeData" :setting="treeSetting"
|
||||
@on-tree-click="treeLink" />
|
||||
|
||||
<div class="modal-wrap">
|
||||
<Modal v-model="editNodeModal.modal" :title="editNodeModal.title" width="450px" :mask-closable="false">
|
||||
<Form ref="refFormData" :label-width="100" :model="editNodeModal" class="myform mb-20 mr-20">
|
||||
<Form-Item label="当前位置:">
|
||||
{{ curNodePath.join(' > ') }}
|
||||
</Form-Item>
|
||||
<Form-Item label="节点名称:" prop="name"
|
||||
:rules="{ required: true, message: '请输入节点名称', trigger: 'blur' }">
|
||||
<Input ref="refNodeNameInput" maxlength="30" v-model="editNodeModal.name" />
|
||||
</Form-Item>
|
||||
</Form>
|
||||
<template #footer>
|
||||
<Button @click="editNodeModal.modal = false">取消</Button>
|
||||
<Button type="primary" :loading="editNodeModal.loading" @click="saveEditNode">确认</Button>
|
||||
</template>
|
||||
</Modal>
|
||||
|
||||
<Modal id="modal-sortTreeNode-id" class-name="up-dowm-components" v-model="showMoveModalStatus" title="移动节点"
|
||||
:closable="false" :mask-closable="false" width="600">
|
||||
<div class="fix">
|
||||
<Alert class="fl">拖拽到目标节点时,确保节点是展开状态,以保证节点数据一致</Alert>
|
||||
<Button class="fr" @click="sendReloadTree()">刷新节点树</Button>
|
||||
</div>
|
||||
<MyTree class="tree-div" ref="refSortMyTree" :autoClick="false" :treeData="moveTreeData"
|
||||
:setting="moveTreeSetting" @on-tree-drag="onTreeDragMove" />
|
||||
|
||||
<template #footer>
|
||||
<Button @click="showMoveModalStatus = false" style="margin-left: 20px">关闭</Button>
|
||||
</template>
|
||||
</Modal>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import SvgIcon from "@/components/SvgIcon.vue";
|
||||
import MyTree from '@/components/MyTree.vue'
|
||||
import { useDefaultStore } from "@/store";
|
||||
import { RootNode } from "@/types";
|
||||
import { cloneDeep, isEmpty } from "lodash";
|
||||
import useCurrentInstance from "@/utils/hook/useCurrentInstance";
|
||||
import { get, last } from "lodash";
|
||||
|
||||
const props = defineProps<{
|
||||
rootType: RootNode;
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
'on-change': [stage: number, subject: string, treeNode: any]
|
||||
}>()
|
||||
|
||||
|
||||
const curTreeNode = defineModel<any>('curTreeNode', { default: {} })
|
||||
//学科学段
|
||||
const curSubjectInfo = defineModel<any>('curSubjectInfo', {
|
||||
default: {
|
||||
stage: null,
|
||||
subject: null
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const store = useDefaultStore();
|
||||
const { $utils } = useCurrentInstance();
|
||||
|
||||
const refMyTree = useTemplateRef('refMyTree');
|
||||
const refFormData = useTemplateRef('refFormData')
|
||||
const refNodeNameInput = useTemplateRef('refNodeNameInput')
|
||||
|
||||
|
||||
const treeSetting = ref({
|
||||
view: {
|
||||
dblClickExpand: false,
|
||||
showIcon: false,
|
||||
},
|
||||
edit: {
|
||||
enable: true,
|
||||
showRemoveBtn: false,
|
||||
showRenameBtn: false,
|
||||
drag: false,
|
||||
},
|
||||
check: {
|
||||
enable: false,
|
||||
},
|
||||
callback: {},
|
||||
})
|
||||
|
||||
const moveTreeSetting = ref({
|
||||
view: { dblClickExpand: false, showIcon: true },
|
||||
edit: {
|
||||
enable: true,
|
||||
showRemoveBtn: false,
|
||||
showRenameBtn: false,
|
||||
drag: {
|
||||
isMove: true,
|
||||
isCopy: false,
|
||||
showArrow: true,
|
||||
prev: true,
|
||||
next: true,
|
||||
inner: true,
|
||||
autoExpandTrigger: true
|
||||
}
|
||||
},
|
||||
check: {
|
||||
enable: false
|
||||
},
|
||||
callback: {}
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
const showMoveModalStatus = ref(false)
|
||||
const editNodeModal = reactive({
|
||||
modal: false,
|
||||
title: '',
|
||||
loading: false,
|
||||
name: null,
|
||||
id: null,
|
||||
})
|
||||
|
||||
|
||||
const curSubJectId = ref<number | null>(null);
|
||||
|
||||
const subjectComp = computed(() => {
|
||||
return store.getStageSubjects;
|
||||
})
|
||||
|
||||
const paramsComp = computed(() => {
|
||||
const find = subjectComp.value.find((item: any) => item.k == curSubJectId.value)
|
||||
if (!find) return null;
|
||||
else
|
||||
return {
|
||||
rootType: props.rootType,
|
||||
stage: find.stage,
|
||||
subject: find.code
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const curNodePath = computed(() => {
|
||||
if (!curTreeNode.value) return [];
|
||||
let arr: any[] = curTreeNode.value.getPath().map((x: any) => x.name)
|
||||
return arr;
|
||||
})
|
||||
|
||||
const moveTreeData = computed(() => {
|
||||
function formatTree(list: any[]): any[] {
|
||||
return list
|
||||
.map(node => {
|
||||
const [, , canMove] = $utils.unpackPerms(node.permissions)
|
||||
if (!canMove) return null
|
||||
|
||||
const copy = cloneDeep(node)
|
||||
copy.drag = true
|
||||
copy.children = copy.children?.length
|
||||
? formatTree(copy.children)
|
||||
: null
|
||||
return copy
|
||||
})
|
||||
.filter(Boolean)
|
||||
}
|
||||
return formatTree(treeData.value)
|
||||
})
|
||||
|
||||
const { loading: treeLoading, data: treeData, send: sendReloadTree, onSuccess: onNodeTreeSuccess } = useRequest(
|
||||
() => Apis.NodeTree.getNodeTree({ params: paramsComp.value }),
|
||||
{
|
||||
initialData: [],
|
||||
immediate: false,
|
||||
})
|
||||
|
||||
watch(() => curSubJectId.value, async (val) => {
|
||||
await nextTick();
|
||||
curTreeNode.value = null;
|
||||
curSubjectInfo.value.stage = paramsComp.value?.stage;
|
||||
curSubjectInfo.value.subject = paramsComp.value?.subject;
|
||||
if (!!val)
|
||||
sessionStorage.setItem('bsch.learnmanage.cur_subject_id', val.toString())
|
||||
sendReloadTree(-1);
|
||||
})
|
||||
|
||||
onNodeTreeSuccess(() => reloadTree(-1))
|
||||
|
||||
onMounted(async () => {
|
||||
await nextTick();
|
||||
|
||||
if (subjectComp.value.length > 0) {
|
||||
|
||||
let cacheStr = sessionStorage.getItem('bsch.learnmanage.cur_subject_id');
|
||||
if (!!cacheStr) {
|
||||
curSubJectId.value = Number(cacheStr);
|
||||
}
|
||||
else
|
||||
curSubJectId.value = subjectComp.value[0].k;
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
||||
async function reloadTree(id?: number) {
|
||||
await nextTick();
|
||||
console.log(id);
|
||||
|
||||
treeData.value.forEach((n: any) => (n.open = true));
|
||||
if (!!curTreeNode.value && !!curTreeNode.value.id)
|
||||
refMyTree.value?.reloadTree(curTreeNode.value.id)
|
||||
else
|
||||
refMyTree.value?.reloadTree(id)
|
||||
}
|
||||
|
||||
|
||||
function showAddNode() {
|
||||
if (!nodePermissions.value.canCreate) return;
|
||||
editNodeModal.title = '新增节点';
|
||||
editNodeModal.modal = true;
|
||||
editNodeModal.id = null;
|
||||
editNodeModal.name = null;
|
||||
nextTick().then(() => {
|
||||
refNodeNameInput.value?.focus()
|
||||
})
|
||||
}
|
||||
function showEditNode() {
|
||||
if (!nodePermissions.value.canUpdate) return;
|
||||
editNodeModal.title = '编辑节点';
|
||||
editNodeModal.modal = true;
|
||||
|
||||
editNodeModal.id = curTreeNode.value.id;
|
||||
editNodeModal.name = curTreeNode.value.name;
|
||||
nextTick().then(() => {
|
||||
refNodeNameInput.value.focus()
|
||||
})
|
||||
}
|
||||
function showMoveNode() {
|
||||
if (!nodePermissions.value.canMove) return;
|
||||
showMoveModalStatus.value = true;
|
||||
|
||||
}
|
||||
async function showDeleteNode() {
|
||||
if (!nodePermissions.value.canDelete) return;
|
||||
const temp = cloneDeep(curTreeNode.value);
|
||||
const parentNode = temp.getParentNode();
|
||||
Modal.confirm({
|
||||
title: '删除知识树节点',
|
||||
content: `确定删除名为【${curTreeNode.value.name}】的知识树节点记录?`,
|
||||
onOk: async () => {
|
||||
const id = curTreeNode.value.id;
|
||||
await Apis.NodeTree.deleteNode({ params: { nodeId: id } })
|
||||
Message.success('删除成功!');
|
||||
curTreeNode.value = parentNode;
|
||||
sendReloadTree()
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
async function saveEditNode() {
|
||||
let isAdd = !!editNodeModal.id ? false : true;
|
||||
const validate = await refFormData.value.validate();
|
||||
if (!validate) {
|
||||
Message.error('请填写红色星号内容!');
|
||||
return;
|
||||
}
|
||||
const { id, name } = editNodeModal;
|
||||
editNodeModal.loading = true;
|
||||
if (isAdd) {
|
||||
const parentId = get(curTreeNode.value, 'id', -1)
|
||||
const brotherId = get(last(get(curTreeNode.value, 'children')), 'id', 0);
|
||||
const params: any = {
|
||||
rootType: props.rootType,
|
||||
name, parentId, brotherId,
|
||||
stage: paramsComp.value?.stage,
|
||||
subject: paramsComp.value?.subject
|
||||
};
|
||||
await Apis.NodeTree.addNode({ params })
|
||||
sendReloadTree()
|
||||
}
|
||||
else {
|
||||
await Apis.NodeTree.updateNodeName({ params: { name, nodeId: id } })
|
||||
|
||||
sendReloadTree();
|
||||
}
|
||||
|
||||
editNodeModal.loading = false;
|
||||
editNodeModal.modal = false;
|
||||
}
|
||||
|
||||
async function refreshMoveTree() {
|
||||
// const id = selectedNode.value.id;
|
||||
// await Apis.NodeTree.refreshSortTree({ params: { nodeId: id } })
|
||||
// Message.success('刷新成功!');
|
||||
// refTreeNode.value?.reloadTree()
|
||||
}
|
||||
//@ts-ignore
|
||||
async function onTreeDragMove({ treeNodes, targetNode, moveType }) {
|
||||
let mType = moveType == "inner" ? 3 : moveType == "prev" ? 1 : moveType == "next" ? 2 : 0;
|
||||
let currentNode = treeNodes[0];
|
||||
let nativeNode = currentNode;
|
||||
const params = formatMoveTree(treeNodes[0], targetNode, mType);
|
||||
await Apis.NodeTree.move({
|
||||
params: params,
|
||||
})
|
||||
sendReloadTree();
|
||||
}
|
||||
|
||||
function formatMoveTree(currentNode: any, targetNode: any, moveType: any) {
|
||||
|
||||
let nativeNode = currentNode;
|
||||
let treeNode = targetNode;
|
||||
let pathArr = treeNode.path.split(",");
|
||||
let index2 = pathArr.findIndex((ele: any) => {
|
||||
return ele == nativeNode.id;
|
||||
});
|
||||
if (index2 != -1) {
|
||||
Message.warning("父节点无法移动到子节点下方");
|
||||
return;
|
||||
}
|
||||
let newBrother = ""; //新兄长节点 【接口参数】
|
||||
let newParent = ""; //新父节点 【接口参数】
|
||||
|
||||
console.log('treeNode', treeNode);
|
||||
|
||||
var parentNode = treeNode.getParentNode();
|
||||
|
||||
let chilList = !!parentNode ? parentNode.children : treeNode.children;
|
||||
let index = chilList.findIndex((ele: any) => {
|
||||
return ele.id == treeNode.id;
|
||||
});
|
||||
if (treeNode.id == nativeNode.id || (treeNode.id == nativeNode.parentId && moveType == 3)) {
|
||||
//选中同一节点,无效移动
|
||||
Message.warning("目标节点与选中节点冲突,请选择其他目标节点");
|
||||
return;
|
||||
} else if ((moveType == 1 || moveType == 2) && nativeNode.parentId == treeNode.parentId) {
|
||||
if (moveType == 1) {
|
||||
console.log('idnex:' + index);
|
||||
index--;
|
||||
//前面
|
||||
newBrother = index == 0 ? 0 : chilList[index - 1].id;
|
||||
} else {
|
||||
//后面
|
||||
newBrother = chilList[index].id;
|
||||
}
|
||||
newParent = "";
|
||||
} else {
|
||||
//跨父节点移动
|
||||
if (moveType == 1) {
|
||||
//选择的是前面
|
||||
//前面
|
||||
newBrother = index == 0 ? index : chilList[index - 1].id;
|
||||
newParent = treeNode.parentId;
|
||||
} else if (moveType == 2) {
|
||||
//选择的是后面
|
||||
//后面
|
||||
newBrother = chilList[index].id;
|
||||
newParent = treeNode.parentId;
|
||||
} else {
|
||||
//选择的是里面
|
||||
//里面 选中的children里面的最后一个
|
||||
newBrother = isEmpty(treeNode.children)
|
||||
? 0
|
||||
: treeNode.children[treeNode.children.length - 1].id;
|
||||
newParent = treeNode.id;
|
||||
}
|
||||
}
|
||||
return {
|
||||
targetId: nativeNode.id,
|
||||
newParent: newParent,
|
||||
newBrother: newBrother,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
async function treeLink(treeNode: any) {
|
||||
const copyNode = cloneDeep(treeNode)
|
||||
copyNode.children = [];
|
||||
curTreeNode.value = copyNode;
|
||||
// console.log('click', treeNode);
|
||||
// console.log(copyNode.getPath().map((x: any) => x.name));
|
||||
|
||||
curSubjectInfo.value.stage = paramsComp.value?.stage;
|
||||
curSubjectInfo.value.subject = paramsComp.value?.subject;
|
||||
const { stage, subject } = curSubjectInfo.value;
|
||||
emit('on-change', stage, subject, copyNode)
|
||||
}
|
||||
|
||||
|
||||
|
||||
const nodePermissions = computed(() => {
|
||||
if (!curTreeNode.value) return { canCreate: false, canUpdate: false, canMove: false, canDelete: false };
|
||||
const [canCreate, canUpdate, canMove, canDelete] = $utils.unpackPerms(curTreeNode.value.permissions)
|
||||
return {
|
||||
canCreate,
|
||||
canUpdate,
|
||||
canMove,
|
||||
canDelete,
|
||||
}
|
||||
})
|
||||
|
||||
async function onChangeSubject() { }
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.opera-ul {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
padding-bottom: 10px;
|
||||
|
||||
li {
|
||||
padding: 10px;
|
||||
height: 38px;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
|
||||
&.active,
|
||||
&:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
svg {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
li.disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.4;
|
||||
|
||||
&:hover {
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#modal-sortTreeNode-id {
|
||||
position: relative;
|
||||
z-index: 1000;
|
||||
height: 380px;
|
||||
|
||||
.tree-div {
|
||||
z-index: 1001;
|
||||
border: 1px solid #cacaca;
|
||||
border-radius: 5px;
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.ztree {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user