Procházet zdrojové kódy

完善模板编辑 增加模板嵌套

master
unknown před 4 roky
rodič
revize
d045456171
10 změnil soubory, kde provedl 588 přidání a 136 odebrání
  1. +19
    -1
      src/App.vue
  2. +1
    -1
      src/router.js
  3. +16
    -5
      src/services/template.js
  4. +2
    -0
      src/utils/request.js
  5. +2
    -2
      src/views/components_web/head/head.vue
  6. +5
    -4
      src/views/manage_system/index/services.js
  7. +200
    -0
      src/views/manage_system/template/components/template-tree.vue
  8. +3
    -2
      src/views/manage_system/template/index.vue
  9. +340
    -0
      src/views/manage_system/template/mind.vue
  10. +0
    -121
      src/views/manage_system/template_mind.vue

+ 19
- 1
src/App.vue Zobrazit soubor

@@ -140,4 +140,22 @@ export default {
},
},
};
</script>
</script>

<style lang="scss">
jmnode {
border: none;
box-shadow: 0 1px 2px 0 rgba(#000, 0.2);
border-radius: 4px;
&:hover {
box-shadow: none;
border: 2px solid rgba(#7850FF, 1);
}
&.selected {
box-shadow: none;
border: 2px solid rgba(#7850FF, 1);
background-color: #fff;
color: #333;
}
}
</style>

+ 1
- 1
src/router.js Zobrazit soubor

@@ -147,7 +147,7 @@ const router = new Router({
{
path: '/system/template/mind',
name: 'system_template_mind',
component: () => import('@/views/manage_system/template_mind'),
component: () => import('@/views/manage_system/template/mind'),
},
{
path: '/system/template/tempfile',


+ 16
- 5
src/services/template.js Zobrazit soubor

@@ -1,14 +1,10 @@
import { fetchApi, wrapErrorHint } from '@/utils/request';
// import { firstCharToLowerCase, firstCharToUpperCase } from '@/utils/tool';



export async function queryTemplateList() {
return await fetchApi('template/queryTemplateList');
}



/**
* 上传xmind文件
* @param {File} file
@@ -64,7 +60,6 @@ export async function updateTemplate(params) {
}

/* jsmind节点树接口 */

export async function queryTemplateNodeByTemplateId(templateId) {
const res = await fetchApi('template/queryTemplateNodeByTemplateId', { templateId });
return wrapErrorHint(res);
@@ -110,4 +105,20 @@ export async function addTemplateNodeModelFile(param) {
export async function deleteTemplateNodeModelFile(id) {
const res = await fetchApi('template/deleteTemplateNodeModelFile', {id});
return wrapErrorHint(res);
}

/* ********** 嵌套模板操作API ********** */
/**
* 添加嵌套模板
*/
export async function createNestedRelevance(params){
const res = await fetchApi('template/createNestedRelevance', params);
return wrapErrorHint(res);
}
/**
* 删除嵌套模板
*/
export async function deleteNestedRelevance(params){
const res = await fetchApi('template/deleteNestedRelevance', params);
return wrapErrorHint(res);
}

+ 2
- 0
src/utils/request.js Zobrazit soubor

@@ -107,6 +107,8 @@ function mapApiPathToFullPath(path) {
case 'template/updateTemplate':
case 'template/uploadingXmind':
case 'template/addTemplateNodeModelFile':
case 'template/createNestedRelevance':
case 'template/deleteNestedRelevance':
// case 'template/deleteTemplateNodeModelFile':
method = 'POST';
break;


+ 2
- 2
src/views/components_web/head/head.vue Zobrazit soubor

@@ -431,9 +431,9 @@
href="javascript:;"
class="textgray"
@click="openSetPain"
:title="user.cnName"
:title="user.userName"
>
{{ user.cnName }}
{{ user.userName }}
</a>
</div>
</div>


+ 5
- 4
src/views/manage_system/index/services.js Zobrazit soubor

@@ -46,6 +46,7 @@ async function fetchNodes(parentNodeId, topNodeId) {
export async function fetchCompanyDetail(companyId) {
const res = await fetchApi('company/queryCompanyById', { id: companyId });
const resData = res.Data || {};
const userData = resData.user || {};
const outputData = {
address: resData.company.Address,
bankAccount: resData.company.BankAccount,
@@ -66,10 +67,10 @@ export async function fetchCompanyDetail(companyId) {
proviceName: resData.company.ProviceName,
remark: resData.company.Remark,
disable: resData.company.Disable,
cnName: resData.user.CnName,
email: resData.user.Email,
phone: resData.user.Phone,
sex: resData.user.Sex,
cnName: userData.CnName,
email: userData.Email,
phone: userData.Phone,
sex: userData.Sex,
// ?
strCreateTime: dayjs(new Date(resData.company.CreateTime)).format('YYYY-MM-DD HH:mm:ss'),
telphone: resData.company.Telphone,


+ 200
- 0
src/views/manage_system/template/components/template-tree.vue Zobrazit soubor

@@ -0,0 +1,200 @@
<template>
<div class="navtreebox">
<el-tree
:data="listTemplateTree"
@node-contextmenu="nodeRightClick"
@node-click="nodeClick"
ref="tree"
:default-expand-all="true"
node-key="id"
:expand-on-click-node="false"
:filter-node-method="filterTreeNode"
:indent="8"
:highlight-current="true"
>
<span class="custom-tree-node" slot-scope="{ node, data }">
<span class="sanjiaow">
<i class="el-icon-models" v-if="data.id == 'default'"></i>
<i
class="el-tree-node__expand-icon el-icon-caret-right bianjutop"
v-if="data.isLeaf == false && node.isLeaf == true"
></i>
<i
class="el-icon-models"
v-if="data.isLeaf == true && node.isLeaf == true"
></i>

<span>{{ data.label }}</span>
</span>
</span>
</el-tree>
</div>
</template>

<script>
import * as services from "@/services/template";
import { noop, identity } from 'lodash';
import { queryAllIndustry } from "@/services/industry";

export default {
props: {
nodeClick: {
type: Function,
default: noop,
},
nodeRightClick: {
type: Function,
default: noop,
},
filterTreeNode: {
type: Function,
default: noop,
},
filterList: {
type: Function,
default: identity
}
},
data(){
return {
listTemplateTree: [],
allIndList: [], // 行业列表全集
topIndList: [], // 大行业列表
level2IndList: [], // 二级行业列表,会根据顶级行业列表变化
mapIndIdToIndName: {}, // 行业Id -> 行业名称映射
mapIndIdToIndNode: {},
}
},
created(){
this.fetchTemplateList();
},
methods: {
// ---------------------------------数据获取区---------------------------------
async getIndustryData() {
const indList = await queryAllIndustry();
this.topIndList = indList.filter((ind) => ind.Level === 1);
this.level2IndList = [];
this.allIndList = indList;
const mapIndIdToIndName = indList.reduce(
(h, ind) => ((h[ind.Id] = ind.Name), h),
{}
);
this.mapIndIdToIndName = mapIndIdToIndName;
return indList;
},
/**
* 获取模板列表数据
*/
async fetchTemplateList() {
const [templateListRes, indList] = await Promise.all([
services.queryTemplateList(),
this.getIndustryData(),
]);
const [tree, mapIndIdToIndNode] = composeIndustryAndTemplate(
this.filterList(templateListRes.Data || []),
indList
);

this.mapIndIdToIndNode = mapIndIdToIndNode;
this.listTemplateTree = tree;
},
}
}

function composeIndustryAndTemplate(templateList, indList) {
const nodeMap = {};
const indIdToTemplateNodesHash = templateList.reduce((h, templateItem) => {
const customIndId =
templateItem.CustomCode === "0" || !templateItem.CustomCode
? undefined
: templateItem.CustomCode;
const targetIndId = customIndId || templateItem.DetailIndustryId;
const node = {
id: templateItem.Id,
label: templateItem.TempName,
parentId: targetIndId,
isLeaf: true,
type: "template",
data: templateItem,
};
h[targetIndId] = (h[targetIndId] || []).concat(node);
return h;
}, {});

const [
indNodeHash,
indNodeList,
parentIndIdToIndListHash,
headList,
] = indList.reduce(
(h, indItem) => {
const parentId =
indItem.SuperId === "0" || !indItem.SuperId ? null : indItem.SuperId;
const node = {
id: indItem.Id,
label: indItem.Name,
parentId,
data: indItem,
type: "industry",
children: [],
isLeaf: false,
};
h[0][node.id] = node;
h[1].unshift(node);
nodeMap[node.id] = node;
if (parentId) {
if (!h[2][parentId]) {
h[2][parentId] = [];
}
h[2][parentId].push(node);
}
if (indItem.Level === 1) {
h[3].push(node);
}
return h;
},
[{}, [], {}, []]
);
indNodeList.forEach((indNode) => {
// 插入行业子节点列表
const childIndList = parentIndIdToIndListHash[indNode.id];
if (childIndList) {
indNode.children = indNode.children.concat(childIndList);
}
// 插入模板子节点列表
const childTemplateList = indIdToTemplateNodesHash[indNode.id];
if (childTemplateList) {
indNode.children = indNode.children.concat(childTemplateList);
}
if (indNode.children.length === 0) {
const parentNode = indNodeHash[indNode.parentId];
const targetList = !parentNode
? headList
: parentIndIdToIndListHash[parentNode.id];
targetList.splice(targetList.indexOf(indNode), 1);
// parentNode.children = parentNode.children.filter(iNode => iNode !== indNode);
}
});
return [headList, nodeMap];
}
</script>

<style lang="scss" scoped>
.navtreebox {
color: red;
}
.navtreebox ::v-deep .el-tree {
position: relative;
cursor: default;
background: transparent;
color: #d0d0d0;
font-size: 12px;
line-height: 30px;
.is-current > .el-tree-node__content > .custom-tree-node > .sanjiaow > span{
color: #32323c;
}
}
.el-icon-models {
margin-right: 5px;
}
</style>

+ 3
- 2
src/views/manage_system/template/index.vue Zobrazit soubor

@@ -2105,7 +2105,6 @@ export default {
services.queryTemplateList(),
this.getIndustryData(),
]);
console.log(templateListRes);

const [tree, mapIndIdToIndNode] = composeIndustryAndTemplate(
templateListRes.Data || [],
@@ -2113,11 +2112,13 @@ export default {
);

this.mapIndIdToIndNode = mapIndIdToIndNode;
console.log(tree);
const listTemplateTree = [
{ label: "总体概况", id: "default", data: {} },
].concat(tree);
this.listTemplateTree = listTemplateTree;
setTimeout(() => {
this.$refs.tree.setCurrentKey('default');
}, 0);
if (this.$route.params.data != undefined) {
// 从思维导图页面返回时加载原来选中模板
this.getTempNameAndData(this.$route.params.data);


+ 340
- 0
src/views/manage_system/template/mind.vue Zobrazit soubor

@@ -0,0 +1,340 @@
<!--
* @Description: XMind-查看组件
* @version: V1.0
* @Author: xzx
* @Date: 2019-11-04 17:22:44
* @LastEditors: xzx
* @LastEditTime: 2019-12-06 17:26:27
-->
<template>
<div class="layout_content template_mind">
<section class="yiyun_section">
<div>
<div class="mlr-10">
<div class="yiyunTable_list">
<div style="float: right;margin-right: 50px;margin-top:20px;width:300px">
<el-slider v-model="mindZoom" height="100px" @change="zoomOut" class="self_slider" :format-tooltip="biliToolTip" :min="40" :max="150"></el-slider>
</div>
<div id="TszMind" style=" height:calc(100vh - 108px);"></div>
</div>
</div>
</div>
<div class="xmind_popup" v-show="!!selectedNode">
<div v-if="!!selectedNode">
<div v-if="currentPopupViewType === popupViewType.NODE_DETAIL">
<div class="xmind_popup_head">{{selectedNode.topic}}</div>
<div class="xmind_popup_section">
<el-button class="xmind_popup_button" v-if="!selectedNode.data.isNest" @click="changePopupViewType(popupViewType.ADD_SUB_TEMPLATE)">嵌套模板</el-button>
<el-button class="xmind_popup_button" v-if="selectedNode.data.isNest" @click="removeNestTemplate()">移除嵌套模板</el-button>
</div>
</div>
<div v-else-if="currentPopupViewType === popupViewType.ADD_SUB_TEMPLATE">
<div class="xmind_popup_head">选择嵌套模板</div>
<template-tree
:filterList="filterCurrentTemplate.bind(this)"
:nodeClick="onSelectSubTemplate"
/>
<div class="xmind_popup_foot">
<el-button class="xmind_popup_foot_button" @click="cancelAddSubTemplate">取消</el-button>
<el-button class="xmind_popup_foot_button" :disabled="!selectedSubTemplateNode" @click="doNestTemplate">嵌套</el-button>
</div>
</div>
</div>
</div>
</section>
</div>
</template>

<script>
// require("!vue-style-loader!css-loader!../../../public/static/css/jsmind.css");
import * as services from "@/services/template";
import TemplateTree from './components/template-tree';
import {notify} from '@/utils/tool';
const jsMind = window.jsMind;
const $ = window.jQuery;
const popupViewType = {
NODE_DETAIL: 1,
ADD_SUB_TEMPLATE: 2,
ADD_CHILD_NODE: 3,
}
export default {
components: {
TemplateTree,
},
data() {
return {
templateList: [],
listTemplateMindTree: [], //思维导图数据
mindZoom: 60,
tempName: this.$route.params.name,
tempId: this.$route.params.id,
jm: null,
selectedNode: null,
popupViewType,
currentPopupViewType: popupViewType.NODE_DETAIL,
// 当前选中的嵌套模板
selectedSubTemplateNode: null,
};
},
created: function () {
this.getTemplateMindTree();

},
watch: {},
methods: {
filterCurrentTemplate(templateList){
const currentTemplateId = this.tempId;
return templateList.filter(temp => temp.Id !== currentTemplateId);
},
/**
* @description: 思维导图缩放
* @param {type}
* @return:
*/
zoomIn: function () {
this.mindZoom -= 1;
var aaa = document.getElementsByClassName("jsmind-inner");
aaa[0].style.zoom = this.mindZoom / 100;
},
/**
* @description: 思维导图放大
* @param {type}
* @return:
*/
zoomOut: function (index) {
this.mindZoom += 1;
var aaa = document.getElementsByClassName("jsmind-inner");
aaa[0].style.zoom = this.mindZoom / 100;
},
biliToolTip: function (index) {
return index + "%";
},
/**
* @description: 查看思维导图
* @param {type}
* @return:
*/
scanMind: function () {
var mind = {
meta: {
name: "mindhh",
author: "hh",
version: "0.2"
},
format: "node_tree",
data: this.listTemplateMindTree[0]
};
var options = {
container: "TszMind", //容器的ID
editable: false, // 是否启用编辑
theme: "", //主题
mode: "side", // 显示模式=full - 子节点动态分布在根节点两侧 [默认值] side - 子节点只分布在根节点右侧
support_html: false, // 是否支持节点里的HTML元素
view: {
engine: "canvas",
hmargin: 100, // 思维导图距容器外框的最小水平距离
vmargin: 50, // 思维导图距容器外框的最小垂直距离
line_width: 1, // 思维导图线条的粗细
line_color: "#cccdd7" // 思维导图线条的颜色
}
};
if (document.querySelector("jmnodes") == null) {
var jm = new jsMind(options);
jm.disable_edit();
jm.end_edit();
jm.show(mind);
this.jm = jm;
} else {
jsMind.current.show(mind);
}
this.isShowZoom = true;
var $jsmindWrapperNodes = $('.jsmind-inner'); // document.getElementsByClassName("jsmind-inner"); //设置思维导图初始缩放值为0.8
const jsmindWrapperDom = $jsmindWrapperNodes[0];
jsmindWrapperDom.style.zoom = 0.8;
const jsmindDom = $(jsmindWrapperDom).find('jmnodes').get(0);
jsmindDom.addEventListener('click', this.onNodeClick.bind(this));
setTimeout(() => injectSubTemplateIcon(this.listTemplateMindTree[0]), 0)
},
/**
* @description: 获取思维导图数据
* @param {type}
* @return:
*/
async getTemplateMindTree() {
const templateId = this.tempId;
const res = await services.queryTemplateNodeByTemplateId(templateId);
this.listTemplateMindTree = res.Data ? [res.Data] : [];
this.scanMind();
},
/**
* mind 节点点击事件
*/
onNodeClick(e) {
if(e.target.tagName !== 'JMNODE') { // 关闭右侧弹窗面板
this.selectedNode = null;
return;
}
const domNode = e.target;
const jm = this.jm;
const nodeid = domNode.getAttribute('nodeid');
const nodeMap = jm.mind.nodes;
const dataNode = nodeMap[nodeid];
this.currentPopupViewType = this.popupViewType.NODE_DETAIL;
this.selectedNode = dataNode;
},
/**
* 切换弹窗面板内容
*/
changePopupViewType(nextPopupViewType){
this.currentPopupViewType = nextPopupViewType;
},
/**
* 嵌套模板树节点选择
*/
onSelectSubTemplate(nodeData){
if(nodeData.type !== 'template') {
this.selectedSubTemplateNode = null;
return;
}
this.selectedSubTemplateNode = nodeData;
},
/**
* 取消模板嵌套
*/
cancelAddSubTemplate(){
this.changePopupViewType(this.popupViewType.NODE_DETAIL);
// 选中的模板取消
this.selectedSubTemplateNode = null;
},
/**
* 执行模板嵌套
*/
async doNestTemplate(){
const subTemplate = this.selectedSubTemplateNode;
const selectedNode = this.selectedNode;
const mainTemplateId = this.tempId;
// todo
const res = await services.createNestedRelevance({
id: subTemplate.id,
parentId: selectedNode.id,
parentTemplateId: mainTemplateId,
});
if(res.Code !== 0) return;
notify.success('嵌套成功');
this.selectedNode = null;
this.getTemplateMindTree();
},
/**
* 移除嵌套模板
*/
async removeNestTemplate(node){
const selectedNode = this.selectedNode;
const parentNode = selectedNode.parent;
const mainTemplateId = this.tempId;
const res = await services.deleteNestedRelevance({
id: selectedNode.id,
parentId: parentNode.id,
parentTemplateId: mainTemplateId,
});
if(res.Code !== 0) return;
notify.success('移除成功');
this.selectedNode = null;
this.getTemplateMindTree();
}
}
};


// 在数据中注入嵌套模板图标
function injectSubTemplateIcon(data){
if(!data || !data.children) return;
data.children.forEach(node=> {
node.label = node.topic;
if(node.isNest) {
// node.topic = `<span class="el-icon-models"></span>${node.topic}`;
const $node = $(`jmnode[nodeid=${node.id}]`);
$node.prepend('<span class="el-icon-models"></span>');
return;
} else {
injectSubTemplateIcon(node);
}
})
}
</script>

<style lang="scss" scoped>
.template_mind {
.xmind_popup {
position: absolute;
right: 30px;
top: 80px;
width: 375px;
height: 714px;
border-radius: 8px;
background-color: rgba(66, 68, 70, 1);
box-shadow: 0px 3px 11px 1px rgba(0, 0, 0, 0.2);
padding-bottom: 64px;
z-index: 2;

&_head {
border-bottom: 1px solid rgba(#8E909F, 1);

color: #fff;
font-size: $font-level-5;
line-height: 60px;
padding: 0 24px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}

&_section {
text-align: center;
padding-top: 20px;

}

&_foot {
position: absolute;
display: flex;
bottom: 0;
left: 0;
right: 0;
height: 64px;
line-height: 64px;
padding: 0 20px;
align-items: center;
border-top: 1px solid rgba(#8E909F, 1);
&_button {
flex: 1;
+ .xmind_popup_foot_button {
margin-left: 20px;
}
}
}

&_button {
display: inline-block;
width: 80%;
& + .xmind_popup_button {
margin-top: 10px;
margin-left: 0;
}
// padding: 10px 24px;
// font-size: $font-level-6;
// line-height: 1em;
// color: #d0d0d0;
// cursor: pointer;
// &:hover {
// }
}
}
::v-deep .jsmind-inner .el-icon-models {
margin-right: 10px;
}
}


</style>

+ 0
- 121
src/views/manage_system/template_mind.vue Zobrazit soubor

@@ -1,121 +0,0 @@
<!--
* @Description: XMind-查看组件
* @version: V1.0
* @Author: xzx
* @Date: 2019-11-04 17:22:44
* @LastEditors: xzx
* @LastEditTime: 2019-12-06 17:26:27
-->
<template>
<div class="layout_content">
<section class="yiyun_section">
<div>
<div class="mlr-10">
<div class="yiyunTable_list">
<div style="float: right;margin-right: 50px;margin-top:20px;width:300px">
<el-slider v-model="mindZoom" height="100px" @change="zoomOut" class="self_slider" :format-tooltip="biliToolTip" :min="40" :max="150"></el-slider>
</div>
<div id="TszMind" style=" height:calc(100vh - 108px);"></div>
</div>
</div>
</div>
</section>
</div>
</template>
<script>
// require("!vue-style-loader!css-loader!../../../public/static/css/jsmind.css");
import * as services from "@/services/template";
const jsMind = window.jsMind;
export default {
data() {
return {
listTemplateMindTree: [], //思维导图数据
mindZoom: 60,
tempName: this.$route.params.name,
tempId: this.$route.params.id
};
},
created: function () {
this.getTemplateMindTree();
},
watch: {},
methods: {
/**
* @description: 思维导图缩放
* @param {type}
* @return:
*/
zoomIn: function () {
this.mindZoom -= 1;
var aaa = document.getElementsByClassName("jsmind-inner");
aaa[0].style.zoom = this.mindZoom / 100;
},
/**
* @description: 思维导图放大
* @param {type}
* @return:
*/
zoomOut: function (index) {
this.mindZoom += 1;
var aaa = document.getElementsByClassName("jsmind-inner");
aaa[0].style.zoom = this.mindZoom / 100;
},
biliToolTip: function (index) {
return index + "%";
},
/**
* @description: 查看思维导图
* @param {type}
* @return:
*/
scanMind: function () {
var mind = {
meta: {
name: "mindhh",
author: "hh",
version: "0.2"
},
format: "node_tree",
data: this.listTemplateMindTree[0]
};
var options = {
container: "TszMind", //容器的ID
editable: false, // 是否启用编辑
theme: "", //主题
mode: "side", // 显示模式=full - 子节点动态分布在根节点两侧 [默认值] side - 子节点只分布在根节点右侧
support_html: false, // 是否支持节点里的HTML元素
view: {
engine: "canvas",
hmargin: 100, // 思维导图距容器外框的最小水平距离
vmargin: 50, // 思维导图距容器外框的最小垂直距离
line_width: 1, // 思维导图线条的粗细
line_color: "#f0ab70" // 思维导图线条的颜色
}
};
if (document.querySelector("jmnodes") == null) {
var jm = new jsMind(options);
jm.disable_edit();
jm.end_edit();
jm.show(mind);
} else {
jsMind.current.show(mind);
}
this.isShowZoom = true;
var jsmindClass = document.getElementsByClassName("jsmind-inner"); //设置思维导图初始缩放值为0.8
jsmindClass[0].style.zoom = 0.8;
},
/**
* @description: 获取思维导图数据
* @param {type}
* @return:
*/
async getTemplateMindTree() {
const templateId = this.tempId;
const res = await services.queryTemplateNodeByTemplateId(templateId);
this.listTemplateMindTree = res.Data ? [res.Data] : [];
this.scanMind();
}
}
};
</script>

Načítá se…
Zrušit
Uložit