diff --git a/packages/word/index.html b/packages/word/index.html index f821ec6..5b73c0a 100644 --- a/packages/word/index.html +++ b/packages/word/index.html @@ -337,6 +337,65 @@ + diff --git a/packages/word/src/assets/images/article.svg b/packages/word/src/assets/images/article.svg new file mode 100644 index 0000000..5a9b23b --- /dev/null +++ b/packages/word/src/assets/images/article.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/word/src/assets/images/outline.svg b/packages/word/src/assets/images/outline.svg new file mode 100644 index 0000000..c537630 --- /dev/null +++ b/packages/word/src/assets/images/outline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/word/src/editor/core/command/Command.ts b/packages/word/src/editor/core/command/Command.ts index f63c19a..c2a85e9 100644 --- a/packages/word/src/editor/core/command/Command.ts +++ b/packages/word/src/editor/core/command/Command.ts @@ -30,6 +30,7 @@ export class Command { public executeSubscript: CommandAdapt['subscript'] public excuteAiResult: CommandAdapt['aiResult'] public executeAiEdit: CommandAdapt['aiEdit'] + public executeAiArticle: CommandAdapt['aiArticle'] public executeColor: CommandAdapt['color'] public executeHighlight: CommandAdapt['highlight'] public executeTitle: CommandAdapt['title'] @@ -156,6 +157,7 @@ export class Command { this.executeSubscript = adapt.subscript.bind(adapt) this.excuteAiResult = adapt.aiResult.bind(adapt) this.executeAiEdit = adapt.aiEdit.bind(adapt) + this.executeAiArticle = adapt.aiArticle.bind(adapt) this.executeColor = adapt.color.bind(adapt) this.executeHighlight = adapt.highlight.bind(adapt) // 标题、对齐方式、列表 diff --git a/packages/word/src/editor/core/command/CommandAdapt.ts b/packages/word/src/editor/core/command/CommandAdapt.ts index 3d2c69b..9ce810a 100644 --- a/packages/word/src/editor/core/command/CommandAdapt.ts +++ b/packages/word/src/editor/core/command/CommandAdapt.ts @@ -477,7 +477,7 @@ export class CommandAdapt { const isDisabled = this.draw.isReadonly() || this.draw.isDisabled() if (isDisabled) return const selection = this.range.getSelectionElementList() - + if (selection?.length) { const noBoldIndex = selection.findIndex(s => !s.bold) selection.forEach(el => { @@ -635,10 +635,75 @@ export class CommandAdapt { }) this.draw.render({ isSetCursor: false }) } - public aiResult (result?: string) { - result ? this.aiContent = result : '' + public aiResult(result?: string) { + result ? (this.aiContent = result) : '' return this.aiContent } + public aiArticle(payload: string) { + // const isDisabled = this.draw.isReadonly() || this.draw.isDisabled() + // if (isDisabled) return + // const activeControl = this.control.getActiveControl() + // if (activeControl) return + // let { startIndex, endIndex } = this.range.getRange() + // console.log('range:', startIndex, endIndex) + + // if (!~startIndex && !~endIndex) { + // startIndex = 0 + // endIndex = 0 + // } + // const elementList = this.draw.getElementList() + // console.log('hyperlink temp: ', payload, elementList) + + // const { valueList, url } = payload + // const hyperlinkId = getUUID() + // const newElementList = valueList?.map(v => ({ + // url, + // hyperlinkId, + // value: v.value, + // type: ElementType.HYPERLINK + // })) + // if (!newElementList) return + // const start = startIndex + 1 + // formatElementContext(elementList, newElementList, startIndex, { + // editorOptions: this.options + // }) + // this.draw.spliceElementList( + // elementList, + // start, + // startIndex === endIndex ? 0 : endIndex - startIndex, + // ...newElementList + // ) + // const articalLength = newElementList?.length || 0 + // const curIndex = start + articalLength - 1 + // this.range.setRange(curIndex, curIndex) + // this.draw.render({ curIndex }) + + // console.log('ai article:') + this.selectAll() + const isDisabled = this.draw.isReadonly() || this.draw.isDisabled() + // console.log('isDisables:', isDisabled); + + if (isDisabled) return + const selection = this.range.getSelectionElementList() + // if (!selection) return + let content = '' + selection?.forEach(el => { + content += el.value + }) + this.search(content) + this.replace(content + payload) + // if (operate && content) { + // window.parent.postMessage( + // { + // type: 'aiCreater', + // data: content, + // action: operate + // }, + // '*' + // ) + // } + return content + } public aiEdit(operate: string | null) { console.log('ai edit:', operate) const isDisabled = this.draw.isReadonly() || this.draw.isDisabled() @@ -648,9 +713,9 @@ export class CommandAdapt { let content = '' selection.forEach(el => { content += el.value - }) + }) // const eventBus = this.draw.getEventBus() - + this.search(content) if (operate && content) { window.parent.postMessage( @@ -905,10 +970,12 @@ export class CommandAdapt { const isDisabled = this.draw.isReadonly() || this.draw.isDisabled() if (isDisabled) return const activeControl = this.control.getActiveControl() + //console.log(' hyperlink:', activeControl) if (activeControl) return const { startIndex, endIndex } = this.range.getRange() if (!~startIndex && !~endIndex) return const elementList = this.draw.getElementList() + const { valueList, url } = payload const hyperlinkId = getUUID() const newElementList = valueList?.map(v => ({ @@ -1180,12 +1247,14 @@ export class CommandAdapt { } public replace(payload: string) { - console.log('替换:', payload); - + // console.log('替换', payload) + const isReadonly = this.draw.isReadonly() if (isReadonly) return if (!payload || new RegExp(`${ZERO}`, 'g').test(payload)) return const matchList = this.draw.getSearch().getSearchMatchList() + // console.log('ssssss:', matchList) + if (!matchList.length) return // 匹配index变化的差值 let pageDiffCount = 0 @@ -1231,6 +1300,8 @@ export class CommandAdapt { } else { const curIndex = match.index + pageDiffCount const element = elementList[curIndex] + console.log(element); + if ( element.type === ElementType.CONTROL && element.controlComponent !== ControlComponent.VALUE diff --git a/packages/word/src/main.ts b/packages/word/src/main.ts index ff8dbe1..ea55ae3 100644 --- a/packages/word/src/main.ts +++ b/packages/word/src/main.ts @@ -238,12 +238,12 @@ window.onload = function () { } //ai - const aiEditDom = document.querySelector('.menu-item__ai-edit')! - aiEditDom.title = `ai帮助` - aiEditDom.onclick = function () { - console.log('ai-edit') - instance.command.executeAiEdit('') - } + // const aiEditDom = document.querySelector('.menu-item__ai-edit')! + // aiEditDom.title = `ai帮助` + // aiEditDom.onclick = function () { + // console.log('ai-edit') + // instance.command.executeAiEdit('') + // } const colorControlDom = document.querySelector('#color')! colorControlDom.oninput = function () { @@ -1131,6 +1131,76 @@ window.onload = function () { }) } + //添加AI功能 + const aiEditDom = document.querySelector('.menu-item__ai-edit')! + const aiMenuBox = document.querySelector('.menu-item__ai-edit-box')! + const aiMenuBoxClose = document.querySelector('.menu-item__ai-edit-box span')! + const aiOutlineDom = document.querySelector('.ai-edit-outline-box')! + const aiContentDom = document.querySelector('.ai-edit-content-box')! + aiEditDom.title = `ai写作` + aiEditDom.onclick = function () { + console.log('ai-edit') + instance.command.executeAiEdit('') + aiMenuBox.classList.contains('hide') ? aiMenuBox.classList.remove('hide') : aiMenuBox.classList.add('hide') + } + + function initAiDialog() { + aiMenuBox.classList.add('hide') + aiMenuBox.classList.remove('add-height') + aiOutlineDom?.classList.remove('hide') + aiContentDom?.classList.add('hide') + } + aiMenuBoxClose.onclick = function () { + initAiDialog() + } + // 点击生成大纲,弹窗切换 + document.querySelector('#aiArticle')!.onclick = + function () { + aiOutlineDom?.classList.add('hide') + aiContentDom?.classList.remove('hide') + aiMenuBox.classList.add('add-height') + } + const watchOutline = document.querySelector('#watchOutline')! + const watchArticle = document.querySelector('#watchArticle')! + const outlineViewDom = document.querySelector('#outlineView')! + const articleViewDom = document.querySelector('#articleView')! + const aiInertBtn = document.querySelector('#articleInsert')! + watchOutline.onclick = + function() { + watchOutline.classList.add('active-ai') + watchArticle.classList.remove('active-ai') + aiInertBtn?.classList.add('hide') + outlineViewDom.classList.remove('hide') + articleViewDom.classList.add('hide') + } + watchArticle.onclick = + function() { + watchArticle.classList.add('active-ai') + watchOutline.classList.remove('active-ai') + aiInertBtn?.classList.remove('hide') + outlineViewDom.classList.add('hide') + articleViewDom.classList.remove('hide') + } + //插入文章 + aiInertBtn.onclick = function () { + const text = document.querySelector('#articleText')?.textContent || '' + // instance.command.executeSelectAll() + // instance.command.executeSearch() + // text ? instance.command.executeReplace(text) : '' + // const temp = { + // type: ElementType.TEXT, + // value: '', + // url: '', + // valueList: splitText(text).map(n => ({ + // value: n, + // size: 16 + // })) + // } + // console.log('数字:', temp); + + instance.command.executeAiArticle(text) + initAiDialog() + } // 5. | 搜索&替换 | 打印 | const searchCollapseDom = document.querySelector( '.menu-item__search__collapse' diff --git a/packages/word/src/plugins/floatingToolbar/icons/Svgs.ts b/packages/word/src/plugins/floatingToolbar/icons/Svgs.ts index 891ab80..95ee8fa 100644 --- a/packages/word/src/plugins/floatingToolbar/icons/Svgs.ts +++ b/packages/word/src/plugins/floatingToolbar/icons/Svgs.ts @@ -12,5 +12,7 @@ export const Svgs = { simplification: ``, richContent: ``, translation: ``, - summary: `` + summary: ``, + outline: ``, + compose: `` } \ No newline at end of file diff --git a/packages/word/src/plugins/floatingToolbar/index.ts b/packages/word/src/plugins/floatingToolbar/index.ts index 434b8a5..1c4cc00 100644 --- a/packages/word/src/plugins/floatingToolbar/index.ts +++ b/packages/word/src/plugins/floatingToolbar/index.ts @@ -63,23 +63,16 @@ function createPickerToolbar( export const defaultAiPanelMenus = [ { icon: Svgs.optimize, - // title: '改进写作', title: '优化', key: 'creation_optimization' }, { icon: Svgs.checkGrammar, - // title: '检查拼写和语法' title: '纠错', key: 'creation_proofreading' }, - // { - // icon: Svgs.simplification, - // title: '简化内容' - // }, { icon: Svgs.richContent, - // title: '丰富内容' title: '续写', key: 'creation_continuation' }, @@ -166,18 +159,8 @@ function bindAiPanelEvent(container: HTMLDivElement, editor: Editor) { container.querySelector('#footer-one')?.classList.remove('ai-hide') container.querySelector('#footer-two')?.classList.add('ai-hide') container.querySelector('.aie-ai-panel-body-content')?.classList.remove('ai-hide') - // const goBtn = container.querySelector('#go') - // goBtn ? goBtn.innerHTML = Svgs.aiPanelStop : '' - //console.log('选项点击'); const chooseType = item.getAttribute('data-type') - // const aiContent = editor.command.excuteAiResult() let aiContent = editor.command.executeAiEdit(chooseType) - // editor.eventBus.on('aiProcessedData', (msg: string) => { - // console.log('收到'); - - // aiContent = msg - // }) - const textarea = container.querySelector('textarea')! if (aiContent) { textarea.textContent = aiContent @@ -223,20 +206,6 @@ function bindAiPanelEvent(container: HTMLDivElement, editor: Editor) { } } }) - - container.querySelectorAll('.aie-ai-panel-actions p').forEach(element => { - // const prompt = element.getAttribute('data-prompt')! - // element.addEventListener('click', () => { - // startChat(holder, container, prompt) - // }) - }) - - container.addEventListener('click', e => { - // if (e.target === container) { - // holder.aiPanelInstance?.hide() - // holder.tippyInstance?.show() - // } - }) } //ai弹窗初始化 function initAiDialog(container: HTMLDivElement, editor: Editor) { diff --git a/packages/word/src/style.css b/packages/word/src/style.css index cba0e4c..091e1be 100644 --- a/packages/word/src/style.css +++ b/packages/word/src/style.css @@ -1073,3 +1073,129 @@ ul { .menu-item__ai-edit i{ background-image: url('./assets/images/ai-edit.svg'); } +.outline-svg i{ + margin-right: 5px; + background-image: url('./assets/images/outline.svg'); +} +.article-svg i{ + margin-right: 5px; + background-image: url('./assets/images/article.svg'); +} +.hide { + display: none !important; +} +.active-ai { + font-weight: bold; +} +.add-height { + height: 300px !important; +} +.menu-item .menu-item__ai-edit-box { + /* display: block; */ + position: absolute; + top: 25px; + width: 500px; + min-height: 120px; + /* height: 80px; */ + font-size: 14px; + padding: 10px 15px 10px 0; + background-color: #ebf9fb !important; + box-shadow: 0px 5px 5px #e3dfdf; +} + +.menu-item .menu-item__ai-edit-box span { + position: absolute; + top: 0px; + right: 0px; + width: 25px; + height: 25px; + /* height: 100%; */ + color: #dcdfe6; + font-size: 25px; + text-align: center; + line-height: 25px; + border: 0; +} +.menu-item .menu-item__ai-edit-box input , +.menu-item .menu-item__ai-edit-box select{ + width: 180px; + height: 27px; + appearance: none; + background-color: #fff; + background-image: none; + border-radius: 4px; + border: 1px solid #ebebeb; + box-sizing: border-box; + color: #606266; + display: inline-block; + line-height: 27px; + outline: none; + padding: 0 10px; +} +.menu-item .menu-item__ai-edit-box button { + display: inline-block; + border: 1px solid #e2e6ed; + border-radius: 2px; + background: #fff; + line-height: 22px; + padding: 0 6px; + white-space: nowrap; + margin-left: 4px; + cursor: pointer; + font-size: 12px; +} + +.menu-item .ai-edit-outline-box, +.menu-item .ai-edit-content-box{ + width: 100%; + height: 100%; + text-align: center; +} + +.menu-item .ai-menu { + display: inline-block; + position: relative; + width: 20%; + min-width: 85px; + height: 100%; + padding: 10px 0; + box-sizing: border-box; + vertical-align: top; +} +.menu-item .ai-menu button { + position: absolute; + bottom: 10px; + right: 10px; +} +.menu-item .ai-menu button:hover { + background: rgba(25, 55, 88, .04); +} +.menu-item .ai-menu p { + display: inline-block; + height: 25px; + line-height: 25px; + vertical-align: middle; +} +.menu-item .ai-content-box { + display: inline-block; + width: 70%; + height: 100%; + /* padding: 10px 15px; */ + border-radius: 5px; + background-color: #ffffff; + box-sizing: border-box; +} +.menu-item .ai-content-box textarea { + width: 100%; + height: 100%; + padding: 10px 15px; + /* height: 250px; */ + border-color: #f4f4f5; + border-radius: 5px; + outline: none; + resize: none; + box-sizing: border-box; +} +.ai-content-box div { + margin: 5px 0; +} \ No newline at end of file