diff --git a/cmdb-ui/src/App.vue b/cmdb-ui/src/App.vue index f57f232..6e1e114 100644 --- a/cmdb-ui/src/App.vue +++ b/cmdb-ui/src/App.vue @@ -47,6 +47,134 @@ export default { this.$store.dispatch('setWindowSize') }) ) + + // 注册富文本自定义元素 + const resume = { + type: 'attachment', + attachmentLabel: '', + attachmentValue: '', + children: [{ text: '' }], // void 元素必须有一个 children ,其中只有一个空字符串,重要!!! + } + + function withAttachment(editor) { + // JS 语法 + const { isInline, isVoid } = editor + const newEditor = editor + + newEditor.isInline = (elem) => { + const type = DomEditor.getNodeType(elem) + if (type === 'attachment') return true // 针对 type: attachment ,设置为 inline + return isInline(elem) + } + + newEditor.isVoid = (elem) => { + const type = DomEditor.getNodeType(elem) + if (type === 'attachment') return true // 针对 type: attachment ,设置为 void + return isVoid(elem) + } + + return newEditor // 返回 newEditor ,重要!!! + } + Boot.registerPlugin(withAttachment) + /** + * 渲染“附件”元素到编辑器 + * @param elem 附件元素,即上文的 myResume + * @param children 元素子节点,void 元素可忽略 + * @param editor 编辑器实例 + * @returns vnode 节点(通过 snabbdom.js 的 h 函数生成) + */ + function renderAttachment(elem, children, editor) { + // JS 语法 + + // 获取“附件”的数据,参考上文 myResume 数据结构 + const { attachmentLabel = '', attachmentValue = '' } = elem + + // 附件元素 vnode + const attachVnode = h( + // HTML tag + 'span', + // HTML 属性、样式、事件 + { + props: { contentEditable: false }, // HTML 属性,驼峰式写法 + style: { + display: 'inline-block', + margin: '0 3px', + padding: '0 3px', + backgroundColor: '#e6f7ff', + border: '1px solid #91d5ff', + borderRadius: '2px', + color: '#1890ff', + }, // style ,驼峰式写法 + on: { + click() { + console.log('clicked', attachmentValue) + } /* 其他... */, + }, + }, + // 子节点 + [attachmentLabel] + ) + + return attachVnode + } + const renderElemConf = { + type: 'attachment', // 新元素 type ,重要!!! + renderElem: renderAttachment, + } + Boot.registerRenderElem(renderElemConf) + + /** + * 生成“附件”元素的 HTML + * @param elem 附件元素,即上文的 myResume + * @param childrenHtml 子节点的 HTML 代码,void 元素可忽略 + * @returns “附件”元素的 HTML 字符串 + */ + function attachmentToHtml(elem, childrenHtml) { + // JS 语法 + + // 获取附件元素的数据 + const { attachmentValue = '', attachmentLabel = '' } = elem + + // 生成 HTML 代码 + const html = `${attachmentLabel}` + + return html + } + const elemToHtmlConf = { + type: 'attachment', // 新元素的 type ,重要!!! + elemToHtml: attachmentToHtml, + } + Boot.registerElemToHtml(elemToHtmlConf) + + /** + * 解析 HTML 字符串,生成“附件”元素 + * @param domElem HTML 对应的 DOM Element + * @param children 子节点 + * @param editor editor 实例 + * @returns “附件”元素,如上文的 myResume + */ + function parseAttachmentHtml(domElem, children, editor) { + // JS 语法 + + // 从 DOM element 中获取“附件”的信息 + const attachmentValue = domElem.getAttribute('data-attachmentValue') || '' + const attachmentLabel = domElem.getAttribute('data-attachmentLabel') || '' + + // 生成“附件”元素(按照此前约定的数据结构) + const myResume = { + type: 'attachment', + attachmentValue, + attachmentLabel, + children: [{ text: '' }], // void node 必须有 children ,其中有一个空字符串,重要!!! + } + + return myResume + } + const parseHtmlConf = { + selector: 'span[data-w-e-type="attachment"]', // CSS 选择器,匹配特定的 HTML 标签 + parseElemHtml: parseAttachmentHtml, + } + Boot.registerParseElemHtml(parseHtmlConf) }, beforeDestroy() { clearInterval(this.timer) diff --git a/cmdb-ui/src/modules/cmdb/views/ci/modules/ciDetailRelationTopo/index.vue b/cmdb-ui/src/modules/cmdb/views/ci/modules/ciDetailRelationTopo/index.vue index 51d7bb2..679d79a 100644 --- a/cmdb-ui/src/modules/cmdb/views/ci/modules/ciDetailRelationTopo/index.vue +++ b/cmdb-ui/src/modules/cmdb/views/ci/modules/ciDetailRelationTopo/index.vue @@ -1,163 +1,168 @@ - - - - - + + + + + diff --git a/cmdb-ui/src/modules/cmdb/views/ci/modules/ciDetailRelationTopo/node.js b/cmdb-ui/src/modules/cmdb/views/ci/modules/ciDetailRelationTopo/node.js index b2b4143..af8cd49 100644 --- a/cmdb-ui/src/modules/cmdb/views/ci/modules/ciDetailRelationTopo/node.js +++ b/cmdb-ui/src/modules/cmdb/views/ci/modules/ciDetailRelationTopo/node.js @@ -1,56 +1,56 @@ -/* eslint-disable no-useless-constructor */ -import { TreeNode } from 'butterfly-dag' - -import $ from 'jquery' - -class BaseNode extends TreeNode { - constructor(opts) { - super(opts) - } - - draw = (opts) => { - const container = $(`
`) - .css('top', opts.top) - .css('left', opts.left) - .attr('id', opts.id) - let icon - if (opts.options.icon) { - if (opts.options.icon.split('$$')[2]) { - icon = $(``) - } else { - icon = $(``) - } - } else { - icon = $(`${opts.options.name[0].toUpperCase()}`) - } - - const titleContent = $(`
${opts.options.title}
`) - const uniqueDom = $(`
${opts.options.unique_alias || opts.options.unique_name}:${opts.options.unique_value}
`) - container.append(icon) - container.append(titleContent) - container.append(uniqueDom) - - if (opts.options.side && !opts.options.children.length) { - const addIcon = $(``) - container.append(addIcon) - addIcon.on('click', () => { - if (opts.options.side === 'left') { - this.emit('events', { - type: 'custom:clickLeft', - data: { ...this } - }) - } - if (opts.options.side === 'right') { - this.emit('events', { - type: 'custom:clickRight', - data: { ...this } - }) - } - }) - } - - return container[0] - } -} - -export default BaseNode +/* eslint-disable no-useless-constructor */ +import { TreeNode } from 'butterfly-dag' + +import $ from 'jquery' + +class BaseNode extends TreeNode { + constructor(opts) { + super(opts) + } + + draw = (opts) => { + const container = $(`
`) + .css('top', opts.top) + .css('left', opts.left) + .attr('id', opts.id) + let icon + if (opts.options.icon) { + if (opts.options.icon.split('$$')[2]) { + icon = $(``) + } else { + icon = $(``) + } + } else { + icon = $(`${opts.options.name[0].toUpperCase()}`) + } + + const titleContent = $(`
${opts.options.title}
`) + const uniqueDom = $(`
${opts.options.unique_alias || opts.options.unique_name}:${opts.options.unique_value}
`) + container.append(icon) + container.append(titleContent) + container.append(uniqueDom) + + if (opts.options.side && (!opts.options.children.length && !(opts.options.edges && opts.options.edges.length && opts.options.edges.find(e => e.source === opts.options.side && e.sourceNode === opts.options.id)))) { + const addIcon = $(``) + container.append(addIcon) + addIcon.on('click', () => { + if (opts.options.side === 'left') { + this.emit('events', { + type: 'custom:clickLeft', + data: { ...this } + }) + } + if (opts.options.side === 'right') { + this.emit('events', { + type: 'custom:clickRight', + data: { ...this } + }) + } + }) + } + + return container[0] + } +} + +export default BaseNode diff --git a/cmdb-ui/src/modules/cmdb/views/ci_types/triggerForm.vue b/cmdb-ui/src/modules/cmdb/views/ci_types/triggerForm.vue index 13ea097..26270da 100644 --- a/cmdb-ui/src/modules/cmdb/views/ci_types/triggerForm.vue +++ b/cmdb-ui/src/modules/cmdb/views/ci_types/triggerForm.vue @@ -194,7 +194,6 @@