km-js-mind.vue 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. <template>
  2. <div :class="$style.jsMind">
  3. <div :class="[$style.navbarwrapper, { [$style.editbtn]: !readOnly }]">
  4. <div>
  5. <a-button-group>
  6. <a-button icon="zoom-in" :disabled="zoomIndisabled" title="放大" @click="fnZoomIn">
  7. </a-button>
  8. <a-button icon="zoom-out" :disabled="zoomOutdisabled" title="缩小" @click="fnZoomOut">
  9. </a-button>
  10. </a-button-group>
  11. </div>
  12. <div v-if="!readOnly" :class="$style.primarybtn">
  13. <a-button
  14. icon="small-dash"
  15. :disabled="noSelectedNode"
  16. title="添加子节点"
  17. @click="fnAddNode"
  18. >
  19. </a-button>
  20. <a-button
  21. icon="dash"
  22. :disabled="noSelectedNode"
  23. title="添加兄弟节点"
  24. @click="fnAddBrotherNode"
  25. >
  26. </a-button>
  27. <a-button icon="delete" :disabled="noSelectedNode" title="删除节点" @click="fnDeleteNode">
  28. </a-button>
  29. <a-button
  30. icon="link"
  31. :disabled="noSelectedNode"
  32. title="设置链接"
  33. @click="fnShowAddLinkModal"
  34. >
  35. </a-button>
  36. </div>
  37. <a-modal
  38. title="设置链接"
  39. :visible="visible"
  40. @ok="fnAddLinkOk"
  41. @cancel="() => (visible = false)"
  42. >
  43. <a-input v-model="link">
  44. <a-select slot="addonBefore" v-model="protocol" default-value="Http://">
  45. <a-select-option value="http://">
  46. http://
  47. </a-select-option>
  48. <a-select-option value="https://">
  49. https://
  50. </a-select-option>
  51. </a-select>
  52. </a-input>
  53. </a-modal>
  54. </div>
  55. <js-mind ref="jsMind" :values="objectValue" :options="options" height="660px"></js-mind>
  56. </div>
  57. </template>
  58. <script>
  59. import { Message } from 'ant-design-vue'
  60. import Vue from 'vue'
  61. import jm from 'vue-jsmind'
  62. import components from './_import-components/km-js-mind-import'
  63. Vue.use(jm)
  64. // 生成ID
  65. function newid() {
  66. return (
  67. new Date().getTime().toString(16) +
  68. Math.random()
  69. .toString(16)
  70. .substr(2)
  71. ).substr(2, 16)
  72. }
  73. export default {
  74. name: 'KmJsMind',
  75. components,
  76. props: {
  77. // 是否只读
  78. readOnly: {
  79. type: Boolean,
  80. default: false,
  81. },
  82. // 数据
  83. value: {
  84. type: [Object, String],
  85. default: undefined,
  86. },
  87. },
  88. data() {
  89. return {
  90. visible: false,
  91. noSelectedNode: true,
  92. protocol: 'Http://',
  93. link: '',
  94. zoomIndisabled: false,
  95. zoomOutdisabled: false,
  96. jm: {},
  97. options: {
  98. editable: !this.readOnly,
  99. // 将新建节点的默认值改为中文
  100. shortcut: {
  101. handles: {
  102. addchild_custom: function(jm, e) {
  103. const selectedNode = jm.get_selected_node()
  104. if (selectedNode) {
  105. const nodeid = newid()
  106. jm.add_node(selectedNode, nodeid, '新建节点')
  107. jm.select_node(nodeid)
  108. jm.begin_edit(nodeid)
  109. }
  110. },
  111. addbrother_custom: function(jm, e) {
  112. const selectedNode = jm.get_selected_node()
  113. if (selectedNode) {
  114. if (selectedNode.isroot) {
  115. Message.warn('根节点不能新建兄弟节点')
  116. return
  117. }
  118. const nodeid = newid()
  119. jm.insert_node_after(selectedNode, nodeid, '新建节点')
  120. jm.select_node(nodeid)
  121. jm.begin_edit(nodeid)
  122. }
  123. },
  124. },
  125. mapping: {
  126. addchild_custom: 45, // <Insert>
  127. addbrother_custom: 13, // <Enter>
  128. },
  129. },
  130. },
  131. }
  132. },
  133. computed: {
  134. objectValue() {
  135. // 没有数据时默认的模板数据
  136. let objectValue = {
  137. /* 元数据,定义思维导图的名称、作者、版本等信息 */
  138. meta: {
  139. name: 'example',
  140. author: 'example',
  141. version: '0.1',
  142. },
  143. format: 'node_tree',
  144. data: {
  145. id: 'root',
  146. topic: '中心主题',
  147. children: [
  148. {
  149. id: 'children1',
  150. topic: '分支主题1',
  151. },
  152. {
  153. id: 'children2',
  154. topic: '分支主题2',
  155. },
  156. {
  157. id: 'children3',
  158. topic: '分支主题3',
  159. },
  160. ],
  161. },
  162. }
  163. if (this.value) {
  164. if (JSON.parse(this.value)) {
  165. objectValue = {
  166. meta: {
  167. name: 'example',
  168. author: 'example',
  169. version: '0.1',
  170. },
  171. format: 'node_tree',
  172. data: JSON.parse(this.value),
  173. }
  174. }
  175. }
  176. return objectValue
  177. },
  178. },
  179. mounted() {
  180. this.jm = this.$refs.jsMind.jm
  181. // 更新数据
  182. var _this = this
  183. this.jm.add_event_listener(function(type, data) {
  184. if (data.evt) {
  185. _this.updateValue()
  186. }
  187. })
  188. // 如果没有默认值,给更新下默认值
  189. if (!this.value) {
  190. this.updateValue()
  191. }
  192. // 设置按钮状态
  193. window.addEventListener('mousedown', (event) => {
  194. if (!this.readOnly) {
  195. const ele = event.target
  196. if (ele.tagName === 'JMNODE') {
  197. this.noSelectedNode = false
  198. } else if (ele.tagName === 'JMNODES') {
  199. this.noSelectedNode = true
  200. }
  201. }
  202. })
  203. },
  204. methods: {
  205. // 获取选中的节点
  206. fnGetSelectedNode() {
  207. const selectedNode = this.jm.get_selected_node()
  208. if (!selectedNode) {
  209. Message.warn('请先选择一个节点')
  210. } else {
  211. return selectedNode
  212. }
  213. },
  214. // 需要手动调一下才能更新
  215. refresh(item) {
  216. const objectValue = {
  217. meta: {
  218. name: 'example',
  219. author: 'example',
  220. version: '0.1',
  221. },
  222. format: 'node_tree',
  223. data: JSON.parse(item),
  224. }
  225. this.jm.show(objectValue)
  226. },
  227. // 编辑节点
  228. fnEditNode(node) {
  229. if (node) {
  230. this.jm.select_node(node)
  231. this.jm.begin_edit(node)
  232. } else {
  233. const selectedNode = this.fnGetSelectedNode()
  234. if (selectedNode) {
  235. this.jm.select_node(node)
  236. this.jm.begin_edit(selectedNode)
  237. }
  238. }
  239. },
  240. // 新增子节点
  241. fnAddNode() {
  242. const selectedNode = this.fnGetSelectedNode()
  243. const nodeid = newid()
  244. const topic = '新建节点'
  245. const node = this.jm.add_node(selectedNode, nodeid, topic)
  246. this.fnEditNode(node)
  247. },
  248. // 新增兄弟节点
  249. fnAddBrotherNode() {
  250. const selectedNode = this.fnGetSelectedNode()
  251. if (selectedNode.isroot) {
  252. Message.warn('根节点不能新建兄弟节点')
  253. return
  254. }
  255. if (selectedNode) {
  256. const nodeid = newid()
  257. const topic = '新建节点'
  258. this.jm.insert_node_after(selectedNode, nodeid, topic)
  259. this.fnEditNode(nodeid)
  260. }
  261. },
  262. // 删除节点
  263. fnDeleteNode() {
  264. const selectedNode = this.fnGetSelectedNode()
  265. this.jm.remove_node(selectedNode)
  266. },
  267. // 添加链接
  268. fnShowAddLinkModal() {
  269. const selectedNode = this.fnGetSelectedNode()
  270. if (selectedNode.data.protocol && selectedNode.data.link) {
  271. this.protocol = selectedNode.data.protocol
  272. this.link = selectedNode.data.link
  273. } else {
  274. this.link = ''
  275. }
  276. this.visible = true
  277. },
  278. fnAddLinkOk() {
  279. const selectedNode = this.fnGetSelectedNode()
  280. if (this.link) {
  281. let url = this.link
  282. if (
  283. url.substr(0, 7).toLowerCase() !== 'http://' &&
  284. url.substr(0, 8).toLowerCase() !== 'https://'
  285. ) {
  286. url = this.protocol + url
  287. }
  288. Object.assign(selectedNode.data, {
  289. link: this.link,
  290. protocol: this.protocol,
  291. 'background-color': '#eee',
  292. })
  293. let topic = selectedNode.topic
  294. topic = `<a href="${url}" target="_blank">${topic}</a>`
  295. this.jm.update_node(selectedNode.id, topic)
  296. this.visible = false
  297. } else {
  298. Message.warn('请输入链接地址')
  299. }
  300. },
  301. // 放大
  302. fnZoomOut() {
  303. if (this.jm.view.zoomOut()) {
  304. this.zoomIndisabled = false
  305. } else {
  306. this.zoomOutdisabled = true
  307. }
  308. },
  309. // 缩小
  310. fnZoomIn() {
  311. if (this.jm.view.zoomIn()) {
  312. this.zoomOutdisabled = false
  313. } else {
  314. this.zoomIndisabled = true
  315. }
  316. },
  317. // 更新数据
  318. updateValue() {
  319. this.$emit('input', JSON.stringify(this.jm.get_data().data))
  320. },
  321. },
  322. }
  323. </script>
  324. <style module lang="scss">
  325. @use '@/common/design' as *;
  326. $jmexpander-line-height: 8px;
  327. .js-mind {
  328. .editbtn {
  329. border-bottom: 1px solid #e4e7ed;
  330. box-shadow: 0 3px 4px rgba(0, 0, 0, 0.2);
  331. }
  332. .navbarwrapper {
  333. position: inherit;
  334. z-index: 100;
  335. display: flex;
  336. align-items: center;
  337. justify-content: space-between;
  338. width: 100%;
  339. padding: 5px;
  340. background-color: $white;
  341. .primarybtn {
  342. button {
  343. margin: 0 5px;
  344. }
  345. }
  346. }
  347. :global(.jsmind-editor) {
  348. color: $text-color;
  349. }
  350. :global(a) {
  351. color: $blue-6;
  352. }
  353. }
  354. </style>