to:sync
This commit is contained in:
parent
057d853f06
commit
c31252f2c8
329
static/js/styleCmd.js
Normal file
329
static/js/styleCmd.js
Normal file
@ -0,0 +1,329 @@
|
|||||||
|
/** * 样式 */
|
||||||
|
(function (utils) {
|
||||||
|
function inputHandle(e) {
|
||||||
|
if (!utils.inCompositionEvent) {
|
||||||
|
updateText(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function compositionstartHandle(e) {
|
||||||
|
// console.log("compositionstart")
|
||||||
|
utils.inCompositionEvent = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function compositionendHandle(e) {
|
||||||
|
// console.log("compositionend")
|
||||||
|
updateText(e)
|
||||||
|
utils.inCompositionEvent = false
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 鼠标按下事件
|
||||||
|
*/
|
||||||
|
function windowsCtrZHandle(e) {
|
||||||
|
const event = e || window.event //标准化事件处理
|
||||||
|
const keyCode = event.keyCode || event.which || event.charCode
|
||||||
|
const metaKey = event.metaKey
|
||||||
|
// console.log("windows键盘事件 ", event, keyCombination, metaKey, keyCode)
|
||||||
|
|
||||||
|
//撤销
|
||||||
|
if (metaKey && keyCode === 90) {
|
||||||
|
console.log('触发ctrl + Z 事件', e.target)
|
||||||
|
if (utils.latestOpDoc !== undefined && utils.latestOpDoc !== null) {
|
||||||
|
// //
|
||||||
|
// let cNo = parseInt(utils.latestOpDoc.getAttribute("data-order"))
|
||||||
|
// console.log("恢复", utils.latestOpDoc, cNo)
|
||||||
|
// if (cNo > 1) {
|
||||||
|
// let qq = "#noteshare p[data-order='" + (cNo - 1) + "']"
|
||||||
|
// // console.log("qq: ", qq)
|
||||||
|
// utils.insertAfter(utils.latestOpDoc, document.querySelector(qq))
|
||||||
|
// } else {
|
||||||
|
// //添加元素到首位
|
||||||
|
// yanxuelu.insertBefore(utils.latestOpDoc, utils.MyRoot.children[0])
|
||||||
|
// }
|
||||||
|
// utils.MyDocMap.get(cNo).setHidden(false)
|
||||||
|
|
||||||
|
//恢复
|
||||||
|
utils.latestOpDoc.recovery();
|
||||||
|
|
||||||
|
// utils.MyDocMap.get(cNo).setSource(latestOpDoc.innerText)
|
||||||
|
//
|
||||||
|
showTestText()
|
||||||
|
|
||||||
|
event.preventDefault()
|
||||||
|
event.returnValue = false
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.latestOpDoc = null
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 鼠标按下事件 & 键盘组合事件
|
||||||
|
* @param {*} e
|
||||||
|
*/
|
||||||
|
function onkeydownHandle(e) {
|
||||||
|
const event = e || window.event //标准化事件处理
|
||||||
|
// window.event 等价于 event参数
|
||||||
|
// 有些浏览器除了通过keyCode获取输入键code,还可以通过which,charCode获取,这么写是出于浏览器兼容性考虑
|
||||||
|
const keyCode = event.keyCode || event.which || event.charCode
|
||||||
|
const keyCombination = event.ctrlKey
|
||||||
|
const metaKey = event.metaKey
|
||||||
|
// console.log("键盘事件 ", event, keyCombination, metaKey, keyCode)
|
||||||
|
|
||||||
|
// ctrl + c 复制
|
||||||
|
if (keyCombination && keyCode === 67) {
|
||||||
|
// 阻止默认事件
|
||||||
|
event.preventDefault()
|
||||||
|
event.returnValue = false
|
||||||
|
console.log('触发ctrl + c 事件', e.target)
|
||||||
|
}
|
||||||
|
|
||||||
|
//撤销
|
||||||
|
if (metaKey && keyCode === 90) {
|
||||||
|
// console.log('触发ctrl + Z 事件', e.target)
|
||||||
|
if (utils.latestOpDoc !== undefined && utils.latestOpDoc !== null) {
|
||||||
|
// console.log(utils.latestOpDoc)
|
||||||
|
//恢复
|
||||||
|
utils.latestOpDoc.recovery();
|
||||||
|
//
|
||||||
|
// console.log(MyDocMap.get(cNo))
|
||||||
|
let cNo = parseInt(utils.latestOpDoc.getData().getAttribute("data-order"))
|
||||||
|
utils.MyDocMap.get(cNo).setHidden(false)
|
||||||
|
// utils.MyDocMap.get(cNo).setSource(latestOpDoc.innerText)
|
||||||
|
//
|
||||||
|
showTestText()
|
||||||
|
|
||||||
|
//阻止事件
|
||||||
|
event.preventDefault()
|
||||||
|
event.returnValue = false
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.latestOpDoc = null
|
||||||
|
}
|
||||||
|
|
||||||
|
//删除
|
||||||
|
if (keyCode === 46 || keyCode === 8) {
|
||||||
|
let curP = event.target
|
||||||
|
let cNo = parseInt(curP.getAttribute("data-order"))
|
||||||
|
|
||||||
|
//维护最近一次编辑的内容
|
||||||
|
if (utils.latestOpDoc === undefined || utils.latestOpDoc === null || utils.latestOpDoc.getData().getAttribute("data-id") !== curP.getAttribute("data-id")) {
|
||||||
|
utils.latestOpDoc = new utils.MyRecovery(curP.cloneNode(true), function () {
|
||||||
|
let cNo = parseInt(this.data.getAttribute("data-order"))
|
||||||
|
console.log("恢复", this.data, cNo)
|
||||||
|
if (cNo > 1) {
|
||||||
|
utils.insertAfter(this.data, document.querySelector("#noteshare p[data-order='" + (cNo - 1) + "']"))
|
||||||
|
} else {
|
||||||
|
//添加元素到首位
|
||||||
|
utils.MyRoot.insertBefore(this.data, utils.MyRoot.children[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
// 恢复该元素展示
|
||||||
|
utils.MyDocMap.get(cNo).setHidden(false)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//如果是第一行
|
||||||
|
let previousSibling = curP.previousSibling
|
||||||
|
// console.log(curP, previousSibling == undefined ,previousSibling.id == undefined)
|
||||||
|
if (previousSibling === undefined || previousSibling.id === undefined) {
|
||||||
|
//显示用户的输入内容
|
||||||
|
showTestText()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// console.log('触发删除', curP.innerHTML, cNo, utils.num, utils.MyDocMap.get(cNo + 1))
|
||||||
|
let curS = window.getSelection()
|
||||||
|
// console.log("当前内容: ", curP.innerHTML, " 当前选区 :", curS)
|
||||||
|
//处理前面没有内容,后面还有内容需要拼接到上层的场景
|
||||||
|
if ((curS.isCollapsed && curS.anchorOffset === 0) || curP.innerHTML === '<br>') {
|
||||||
|
let curNodeRetainHtml = curP.innerHTML
|
||||||
|
|
||||||
|
//异步修正顺序(第一行删除不掉)
|
||||||
|
// if (curP.innerHTML === '<br>' ) {
|
||||||
|
// console.log("删除当前行。选取信息: ", curS)
|
||||||
|
//阻止事件传递
|
||||||
|
event.preventDefault()
|
||||||
|
event.returnValue = false
|
||||||
|
|
||||||
|
//设置该元素隐藏
|
||||||
|
utils.MyDocMap.get(cNo).setHidden(true)
|
||||||
|
//删除当前元素
|
||||||
|
curP.remove()
|
||||||
|
//拼接
|
||||||
|
if (curNodeRetainHtml !== '<br>') {
|
||||||
|
previousSibling.innerHTML = previousSibling.innerHTML + curNodeRetainHtml
|
||||||
|
}
|
||||||
|
|
||||||
|
//收起选区到一个点,光标落在一个可编辑元素上
|
||||||
|
window.getSelection().setPosition(previousSibling, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//显示用户的输入内容
|
||||||
|
showTestText();
|
||||||
|
}
|
||||||
|
|
||||||
|
//回车事件
|
||||||
|
if (keyCode === 13 /* && currentNode === key.lastElementChild */) {
|
||||||
|
event.preventDefault()
|
||||||
|
let uuid = utils.uuid()
|
||||||
|
let curOrder = ++(utils.num)
|
||||||
|
var newParagraph = document.createElement("p")
|
||||||
|
newParagraph.setAttribute("contenteditable", "true")
|
||||||
|
newParagraph.setAttribute("data-id", uuid)
|
||||||
|
newParagraph.setAttribute("id", uuid)
|
||||||
|
newParagraph.setAttribute("data-order", curOrder)
|
||||||
|
newParagraph.onkeydown = onkeydownHandle
|
||||||
|
newParagraph.focus()
|
||||||
|
newParagraph.innerHTML = "<br>"
|
||||||
|
utils.MyRoot.appendChild(newParagraph)
|
||||||
|
utils.MyDocMap.set(curOrder, new utils.MyNode(uuid))
|
||||||
|
|
||||||
|
//收起选区到一个点,光标落在一个可编辑元素上
|
||||||
|
window.getSelection().setPosition(newParagraph, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function showTestText() {
|
||||||
|
//显示用户的输入内容
|
||||||
|
const obj = {};
|
||||||
|
for ([k, v] of utils.MyDocMap.entries()) {
|
||||||
|
if (v.getHidden() && v.getHidden() === true) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
obj[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.log("当前文档结构数: ",obj);
|
||||||
|
|
||||||
|
// var userInput = document.getElementById("testInput")
|
||||||
|
// userInput.value = JSON.stringify(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新文档
|
||||||
|
* @param {*} e
|
||||||
|
*/
|
||||||
|
function updateText(e) {
|
||||||
|
let curP = e.target
|
||||||
|
|
||||||
|
//是否需要变更元素类型
|
||||||
|
utils.firstCmd(curP, onkeydownHandle)
|
||||||
|
|
||||||
|
|
||||||
|
let cNo = parseInt(curP.getAttribute("data-order"))
|
||||||
|
let cp = utils.MyDocMap.get(cNo)
|
||||||
|
|
||||||
|
//内容不变则不处理
|
||||||
|
let h5CurLen = curP.innerText.length
|
||||||
|
let myDocNodeLen = (cp && cp.getSource()) ? cp.getSource().length : 0
|
||||||
|
if (h5CurLen === myDocNodeLen) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cp.setSource(curP.innerText)
|
||||||
|
// console.log("curPTx: ", curP.innerText, "MyDocMap : ", utils.MyDocMap)
|
||||||
|
|
||||||
|
//显示用户的输入内容
|
||||||
|
showTestText()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function surroundContentsByStyle() {
|
||||||
|
let curS = utils.getSelection()
|
||||||
|
let curSec = curS.getRangeAt(0)
|
||||||
|
let styleName = this.getAttribute("data-value");
|
||||||
|
let className = utils.parseStyleName2ClassName(styleName)
|
||||||
|
//todo 只对 nodeType = p 执行
|
||||||
|
// console.log("当前光标信息: ", curS, " 当前选区信息 : ", curSec)
|
||||||
|
|
||||||
|
for (let i = 0; i < curS.rangeCount; i++) {
|
||||||
|
let curSec = curS.getRangeAt(i)
|
||||||
|
|
||||||
|
let curEleSize = curSec.commonAncestorContainer.childNodes.length
|
||||||
|
|
||||||
|
let curStartP = curSec.startContainer.parentElement
|
||||||
|
//todo 先支持 一行。
|
||||||
|
let start = curSec.startOffset;
|
||||||
|
let end = curSec.endOffset;
|
||||||
|
// console.log(curStartP.cloneNode(true), styleName, start, end, " curEleSize: ", curEleSize)
|
||||||
|
|
||||||
|
let curPEle = null;
|
||||||
|
//第一次选择的时候将整行转换成<p><span></span></p>
|
||||||
|
if (curEleSize === 0) {
|
||||||
|
//没选择,则退出
|
||||||
|
if (start === end) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// console.log("debug1: ", curSec.commonAncestorContainer)
|
||||||
|
//维护最近一次编辑的内容(暂时只支持恢复最近一次编辑)
|
||||||
|
utils.latestOpDoc = new utils.MyRecovery(curStartP.cloneNode(true), function () {
|
||||||
|
console.log("恢复上一步样式1", this.innerHTML)
|
||||||
|
let curEl = document.getElementById(this.data.getAttribute("id"));
|
||||||
|
curEl.innerHTML = this.data.innerHTML;
|
||||||
|
|
||||||
|
//文本映射 直接覆盖 map 中的 childrenStyle
|
||||||
|
utils.syncOnePInfo(this.data);
|
||||||
|
})
|
||||||
|
|
||||||
|
let curHtml = ""
|
||||||
|
for (let j = 0; j < curStartP.innerText.length; j++) {
|
||||||
|
// console.log(curStartP.innerText.charAt(j))
|
||||||
|
if (j >= start && j < end) {
|
||||||
|
curHtml += '<span contenteditable="true" class=' + className + '>' + curStartP.innerText.charAt(j) + '</span>';
|
||||||
|
} else {
|
||||||
|
curHtml += '<span>' + curStartP.innerText.charAt(j) + '</span>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
curStartP.innerHTML = curHtml;
|
||||||
|
|
||||||
|
curPEle = curStartP;
|
||||||
|
} else {
|
||||||
|
// console.log("debug2: ", curSec.commonAncestorContainer)
|
||||||
|
//维护最近一次编辑的内容(暂时只支持恢复最近一次编辑)
|
||||||
|
utils.latestOpDoc = new utils.MyRecovery(curSec.commonAncestorContainer.cloneNode(true), function () {
|
||||||
|
console.log("恢复上一步样式1", this.data)
|
||||||
|
let curEl = document.getElementById(this.data.getAttribute("id"));
|
||||||
|
curEl.innerHTML = this.data.innerHTML;
|
||||||
|
//文本映射 直接覆盖 map 中的 childrenStyle
|
||||||
|
utils.syncOnePInfo(this.data);
|
||||||
|
})
|
||||||
|
|
||||||
|
let myChildren = curSec.commonAncestorContainer.children
|
||||||
|
for (let j = 0; j < curEleSize; j++) {
|
||||||
|
let curEle = myChildren[j]
|
||||||
|
if (curS.containsNode(curEle, true)) {
|
||||||
|
curEle.classList.remove(className);
|
||||||
|
curEle.classList.add(className);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
curPEle = curSec.commonAncestorContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//文本映射 直接覆盖 map 中的 childrenStyle
|
||||||
|
utils.syncOnePInfo(curPEle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.styleCmd = {
|
||||||
|
inputHandle, compositionstartHandle, compositionendHandle,
|
||||||
|
onkeydownHandle, windowsCtrZHandle,
|
||||||
|
surroundContentsByStyle,
|
||||||
|
|
||||||
|
showTestText,
|
||||||
|
updateText,
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
})(utils)
|
419
static/js/utils.js
Normal file
419
static/js/utils.js
Normal file
@ -0,0 +1,419 @@
|
|||||||
|
/** *
|
||||||
|
* 工具类
|
||||||
|
*/
|
||||||
|
(function () {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最新修改的元素
|
||||||
|
*/
|
||||||
|
const latestOpDoc = null
|
||||||
|
|
||||||
|
class MyRecovery {
|
||||||
|
constructor(data, func) {
|
||||||
|
this.data = data;
|
||||||
|
this.func = func;
|
||||||
|
}
|
||||||
|
|
||||||
|
getData() {
|
||||||
|
return this.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
getFun() {
|
||||||
|
return this.func;
|
||||||
|
}
|
||||||
|
|
||||||
|
recovery() {
|
||||||
|
this.func();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 行数增加记录
|
||||||
|
*/
|
||||||
|
const num = 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否开始输入中文
|
||||||
|
*/
|
||||||
|
const inCompositionEvent = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 我的文档数据
|
||||||
|
*/
|
||||||
|
const MyDocMap = new Map()
|
||||||
|
|
||||||
|
|
||||||
|
// push,推一个在后面,就是 append 的意思
|
||||||
|
// pop,从后面拉走,配合 push 就是栈
|
||||||
|
// unshift,从前面加一个数据,就是 insert(it, 0) 的意思
|
||||||
|
// shift,前面拉个数据走,配合 shift 就是一个反向栈,配合 push 就是队列
|
||||||
|
class MyQueue {
|
||||||
|
constructor(size) {
|
||||||
|
this.insertIndex = 0
|
||||||
|
this.delIndex = 0
|
||||||
|
this.capacity = 0;
|
||||||
|
this.size = size;
|
||||||
|
this.arr = new Array(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
push(handle) {
|
||||||
|
if (this.capacity === this.arr.length) {
|
||||||
|
console.log("满了");
|
||||||
|
this.capacity--;
|
||||||
|
this.arr[this.capacity] = null;
|
||||||
|
|
||||||
|
//todo 替代
|
||||||
|
// this.end++;
|
||||||
|
}
|
||||||
|
this.arr[this.capacity] = handle;
|
||||||
|
this.capacity++;
|
||||||
|
}
|
||||||
|
|
||||||
|
pull() {
|
||||||
|
if (this.capacity <= 0) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
this.capacity--;
|
||||||
|
|
||||||
|
//到底回退
|
||||||
|
if (this.shift > this.size) {
|
||||||
|
this.shift = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.arr[this.shift];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class MyNode {
|
||||||
|
constructor(id) {
|
||||||
|
this.id = id
|
||||||
|
this.hidden = false
|
||||||
|
this.style = new InnerStyle()
|
||||||
|
}
|
||||||
|
|
||||||
|
getHidden() {
|
||||||
|
return this.hidden
|
||||||
|
}
|
||||||
|
|
||||||
|
setHidden(val) {
|
||||||
|
this.hidden = val
|
||||||
|
}
|
||||||
|
|
||||||
|
setSource(source) {
|
||||||
|
this.source = source
|
||||||
|
}
|
||||||
|
|
||||||
|
getSource() {
|
||||||
|
return this.source
|
||||||
|
}
|
||||||
|
|
||||||
|
getStyle() {
|
||||||
|
return this.style
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class InnerStyle {
|
||||||
|
constructor() {
|
||||||
|
this.nodeType = "p"
|
||||||
|
//map-> index:classList
|
||||||
|
this.childrenStyle = null;
|
||||||
|
this.preStyle = null;
|
||||||
|
|
||||||
|
this.getNodeType = function getNodeType() {
|
||||||
|
return this.nodeType
|
||||||
|
}
|
||||||
|
this.setNodeType = function setNodeType(nodeType) {
|
||||||
|
this.nodeType = nodeType
|
||||||
|
}
|
||||||
|
|
||||||
|
//前置类型 如 ul ol 代码块 等
|
||||||
|
this.setPreStyle = function setPreStyle(k, v) {
|
||||||
|
if (this.preStyle == null) {
|
||||||
|
this.preStyle = new MyKV(k, v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.getPreStyle = function getPreStyle() {
|
||||||
|
return this.preStyle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setChildrenStyle(index, classList) {
|
||||||
|
if (this.childrenStyle === null) {
|
||||||
|
this.childrenStyle = new Map();
|
||||||
|
}
|
||||||
|
this.childrenStyle.set(index, classList)
|
||||||
|
}
|
||||||
|
|
||||||
|
getChildrenStyle(index) {
|
||||||
|
return this.childrenStyle.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
getChildrenStyleMap() {
|
||||||
|
return this.childrenStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
setChildrenStyleMapNull() {
|
||||||
|
this.childrenStyle = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyKV {
|
||||||
|
constructor(k, v) {
|
||||||
|
this.k = k
|
||||||
|
this.v = v
|
||||||
|
|
||||||
|
this.getK = function getK() {
|
||||||
|
return this.k
|
||||||
|
}
|
||||||
|
this.getV = function getV() {
|
||||||
|
return this.v
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主节点元素
|
||||||
|
*/
|
||||||
|
const MyRoot = document.getElementById("noteshare")
|
||||||
|
|
||||||
|
|
||||||
|
function isNum(value) {
|
||||||
|
return !isNaN(parseFloat(value)) && isFinite(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSelection() {
|
||||||
|
return window.getSelection() || document.selection
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @returns 生产uuid
|
||||||
|
*/
|
||||||
|
function uuid() {
|
||||||
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
|
||||||
|
.replace(/[xy]/g, function (c) {
|
||||||
|
const r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8)
|
||||||
|
return v.toString(16)
|
||||||
|
}).split("-")[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在目标元素后面插叙 新元素
|
||||||
|
* @param {*} newElement
|
||||||
|
* @param {*} targetElement
|
||||||
|
*/
|
||||||
|
function insertAfter(newElement, targetElement) {
|
||||||
|
// console.log("开始: ",newElement, targetElement)
|
||||||
|
var parent = targetElement.parentNode
|
||||||
|
if (parent.lastChild == targetElement) {
|
||||||
|
// 如果最后的节点是目标元素,则直接添加。因为默认是最后
|
||||||
|
parent.appendChild(newElement)
|
||||||
|
} else {
|
||||||
|
parent.insertBefore(newElement, targetElement.nextSibling)
|
||||||
|
//如果不是,则插入在目标元素的下一个兄弟节点 的前面。也就是目标元素的后面
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseOrder(curP) {
|
||||||
|
return parseInt(curP.getAttribute("data-order"))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {*} curP
|
||||||
|
* @param onkeydownHandle
|
||||||
|
*/
|
||||||
|
function firstCmd(curP, onkeydownHandle) {
|
||||||
|
let newParagraph;
|
||||||
|
let inputLength = curP.innerText.length
|
||||||
|
/**
|
||||||
|
* h1 ~ h6
|
||||||
|
*/
|
||||||
|
if (curP.innerText.startsWith("#") && curP.innerHTML.endsWith(" ")) {
|
||||||
|
let curNo = parseOrder(curP)
|
||||||
|
let mapNode = utils.MyDocMap.get(curNo)
|
||||||
|
|
||||||
|
// console.log(curP, " - ", curP.innerHTML, curP.innerHTML.startsWith("# "))
|
||||||
|
if (curP.innerHTML.startsWith("# ") || curP.innerHTML.startsWith("# ")) {
|
||||||
|
mapNode.getStyle().setNodeType("h1")
|
||||||
|
becomeAnotherElement(curP, "h1", onkeydownHandle)
|
||||||
|
} else if (curP.innerHTML.startsWith("## ")) {
|
||||||
|
mapNode.getStyle().setNodeType("h2")
|
||||||
|
becomeAnotherElement(curP, "h2", onkeydownHandle)
|
||||||
|
} else if (curP.innerHTML.startsWith("### ")) {
|
||||||
|
mapNode.getStyle().setNodeType("h3")
|
||||||
|
becomeAnotherElement(curP, "h3", onkeydownHandle)
|
||||||
|
} else if (curP.innerHTML.startsWith("#### ")) {
|
||||||
|
mapNode.getStyle().setNodeType("h4")
|
||||||
|
becomeAnotherElement(curP, "h4", onkeydownHandle)
|
||||||
|
} else if (curP.innerHTML.startsWith("##### ")) {
|
||||||
|
mapNode.getStyle().setNodeType("h5")
|
||||||
|
becomeAnotherElement(curP, "h5", onkeydownHandle)
|
||||||
|
} else {
|
||||||
|
mapNode.getStyle().setNodeType("h6")
|
||||||
|
becomeAnotherElement(curP, "h6", onkeydownHandle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 无序列表效果
|
||||||
|
*/
|
||||||
|
if (inputLength === 2 && curP.innerText.startsWith("-") && curP.innerHTML.endsWith(" ")) {
|
||||||
|
let curNo = parseOrder(curP)
|
||||||
|
let mapNode = utils.MyDocMap.get(curNo)
|
||||||
|
mapNode.getStyle().setPreStyle("ul", true)
|
||||||
|
|
||||||
|
//clean
|
||||||
|
curP.innerHTML = ""
|
||||||
|
mapNode.setSource("")
|
||||||
|
|
||||||
|
//根据上一层级元素动态选择 todo
|
||||||
|
curP.setAttribute("style", "padding-left: 1rem;")
|
||||||
|
//新增元素
|
||||||
|
newParagraph = document.createElement("span");
|
||||||
|
newParagraph.setAttribute("contenteditable", "false")
|
||||||
|
//∙ vs ∘
|
||||||
|
newParagraph.innerHTML = "∙ "
|
||||||
|
curP.append(newParagraph)
|
||||||
|
|
||||||
|
//添加一个选区
|
||||||
|
var selObj = window.getSelection()
|
||||||
|
var rangeObj = document.createRange()
|
||||||
|
rangeObj.selectNode(curP)
|
||||||
|
selObj.addRange(rangeObj)
|
||||||
|
|
||||||
|
//收起选区到一个点,光标落在一个可编辑元素上
|
||||||
|
window.getSelection().collapse(curP, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 有序列表效果
|
||||||
|
*/
|
||||||
|
if (inputLength > 2 && inputLength <= 5 && isNum(curP.innerText.substring(0, inputLength - 2)) && curP.innerHTML.endsWith(". ")) {
|
||||||
|
let num = curP.innerText.substring(0, inputLength - 2)
|
||||||
|
console.log(curP.innerText, num)
|
||||||
|
|
||||||
|
let curNo = parseOrder(curP)
|
||||||
|
let mapNode = utils.MyDocMap.get(curNo)
|
||||||
|
mapNode.getStyle().setPreStyle("ol", num)
|
||||||
|
|
||||||
|
//clean
|
||||||
|
curP.innerHTML = ""
|
||||||
|
mapNode.setSource("")
|
||||||
|
//todo
|
||||||
|
curP.setAttribute("style", "padding-left: 1rem;")
|
||||||
|
//新增元素
|
||||||
|
newParagraph = document.createElement("span");
|
||||||
|
newParagraph.setAttribute("contenteditable", "false")
|
||||||
|
newParagraph.innerHTML = num + ". "
|
||||||
|
curP.append(newParagraph)
|
||||||
|
|
||||||
|
//添加一个选区
|
||||||
|
var selObj = window.getSelection()
|
||||||
|
var rangeObj = document.createRange()
|
||||||
|
rangeObj.selectNode(curP)
|
||||||
|
selObj.addRange(rangeObj)
|
||||||
|
|
||||||
|
//收起选区到一个点,光标落在一个可编辑元素上
|
||||||
|
window.getSelection().collapse(curP, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 变成另一个元素
|
||||||
|
* @param {*} curP
|
||||||
|
* @param {*} elementName
|
||||||
|
*/
|
||||||
|
function becomeAnotherElement(curP, elementName, onkeydownHandle) {
|
||||||
|
var newParagraph = document.createElement(elementName)
|
||||||
|
newParagraph.setAttribute("contenteditable", "true")
|
||||||
|
newParagraph.setAttribute("data-id", curP.getAttribute("data-id"))
|
||||||
|
newParagraph.setAttribute("id", curP.getAttribute("data-id"))
|
||||||
|
newParagraph.setAttribute("data-order", curP.getAttribute("data-order"))
|
||||||
|
newParagraph.onkeydown = onkeydownHandle
|
||||||
|
|
||||||
|
//todo 支持 有数据的行 在行首输入 #
|
||||||
|
// if()
|
||||||
|
// switch (elementName){
|
||||||
|
// case "h1":
|
||||||
|
// }
|
||||||
|
newParagraph.innerHTML = "<br>"
|
||||||
|
|
||||||
|
insertAfter(newParagraph, curP)
|
||||||
|
curP.remove()
|
||||||
|
|
||||||
|
//matData
|
||||||
|
let curNo = parseOrder(curP)
|
||||||
|
let mapNode = utils.MyDocMap.get(curNo)
|
||||||
|
mapNode.setSource("")
|
||||||
|
|
||||||
|
//收起选区到一个点,光标落在一个可编辑元素上
|
||||||
|
window.getSelection().setPosition(newParagraph, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseStyleName2ClassName(styleName) {
|
||||||
|
switch (styleName) {
|
||||||
|
case "b":
|
||||||
|
return "childStyleStrong";
|
||||||
|
case "i":
|
||||||
|
return "childStyleI";
|
||||||
|
case "u":
|
||||||
|
return "childStyleU";
|
||||||
|
case "c_red":
|
||||||
|
return "childStyleColor";
|
||||||
|
case "del":
|
||||||
|
return "childStyleDel"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncOnePInfo(p) {
|
||||||
|
//子元素为空不处理
|
||||||
|
if (p.children.length <= 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// let id = p.getAttribute("id");
|
||||||
|
let order = parseInt(p.getAttribute("data-order"));
|
||||||
|
let curMapData = this.MyDocMap.get(order);
|
||||||
|
//清空重置
|
||||||
|
curMapData.getStyle().setChildrenStyleMapNull();
|
||||||
|
for (let i = 0; i < p.children.length; i++) {
|
||||||
|
let curItem = p.children[i];
|
||||||
|
let tmpClassList = curItem.classList;
|
||||||
|
if (tmpClassList != null && tmpClassList.length > 0) {
|
||||||
|
curMapData.getStyle().setChildrenStyle(i, tmpClassList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("curPEle : ", p, " children: ", p.children, " childrenMap: ", curMapData.getStyle().getChildrenStyleMap())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
window.utils = {
|
||||||
|
isNum,
|
||||||
|
uuid,
|
||||||
|
parseOrder,
|
||||||
|
getSelection,
|
||||||
|
insertAfter,
|
||||||
|
firstCmd,
|
||||||
|
becomeAnotherElement,
|
||||||
|
parseStyleName2ClassName,
|
||||||
|
syncOnePInfo,
|
||||||
|
|
||||||
|
latestOpDoc,
|
||||||
|
num,
|
||||||
|
inCompositionEvent,
|
||||||
|
MyDocMap,
|
||||||
|
MyRoot,
|
||||||
|
|
||||||
|
MyNode,
|
||||||
|
InnerStyle,
|
||||||
|
MyKV,
|
||||||
|
MyQueue,
|
||||||
|
MyRecovery,
|
||||||
|
}
|
||||||
|
|
||||||
|
})()
|
50
static/js/yanxuelu.js
Normal file
50
static/js/yanxuelu.js
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
(function (utils, eventHandle) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 输入事件
|
||||||
|
*/
|
||||||
|
utils.MyRoot.addEventListener('input', styleCmd.inputHandle);
|
||||||
|
utils.MyRoot.addEventListener('compositionstart', styleCmd.compositionstartHandle);
|
||||||
|
utils.MyRoot.addEventListener('compositionend', styleCmd.compositionendHandle);
|
||||||
|
|
||||||
|
|
||||||
|
//窗口撤销事件
|
||||||
|
window.addEventListener('keydown', styleCmd.windowsCtrZHandle, true);
|
||||||
|
// window.onkeydown = styleCmd.windowsCtrZHandle
|
||||||
|
|
||||||
|
//样式事件
|
||||||
|
document.getElementById("myPlusB").addEventListener('click', styleCmd.surroundContentsByStyle);
|
||||||
|
document.getElementById("myPlusI").addEventListener('click', styleCmd.surroundContentsByStyle);
|
||||||
|
document.getElementById("myPlusU").addEventListener('click', styleCmd.surroundContentsByStyle);
|
||||||
|
document.getElementById("myPlusDel").addEventListener('click', styleCmd.surroundContentsByStyle);
|
||||||
|
document.getElementById("myPlusC").addEventListener('click', styleCmd.surroundContentsByStyle);
|
||||||
|
|
||||||
|
//窗口初始化完成事件
|
||||||
|
window.onload = function () {//do something
|
||||||
|
// yanxuelu.style.outline = 'none'
|
||||||
|
// yanxuelu.onkeydown = fn // 注册keydown事件处理函数,键盘按下
|
||||||
|
// key.onkeyup = fn // 注册keyup事件处理函数,键盘松开
|
||||||
|
// key.onkeypress = fn // 注册keypress事件处理函数,不推荐使用
|
||||||
|
|
||||||
|
var newParagraph = document.createElement("p")
|
||||||
|
newParagraph.setAttribute("contenteditable", "true")
|
||||||
|
let uuid = utils.uuid()
|
||||||
|
let curOrder = ++utils.num
|
||||||
|
newParagraph.setAttribute("data-id", uuid)
|
||||||
|
newParagraph.setAttribute("id", uuid)
|
||||||
|
newParagraph.setAttribute("data-order", curOrder)
|
||||||
|
newParagraph.onkeydown = styleCmd.onkeydownHandle
|
||||||
|
newParagraph.focus()
|
||||||
|
newParagraph.innerHTML = "<br>"
|
||||||
|
utils.MyRoot.appendChild(newParagraph)
|
||||||
|
|
||||||
|
// MyDocMap[curOrder] = new Map()
|
||||||
|
|
||||||
|
utils.MyDocMap.set(curOrder, new utils.MyNode(uuid))
|
||||||
|
|
||||||
|
|
||||||
|
//收起选区到一个点,光标落在一个可编辑元素上
|
||||||
|
window.getSelection().setPosition(newParagraph, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
})(utils, styleCmd)
|
@ -221,265 +221,182 @@
|
|||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<section style="min-height: 800px; background-color: #f8f7fa;">
|
<div>
|
||||||
<style>
|
<style>
|
||||||
body {
|
/* 字体 */
|
||||||
font-family: Arial, sans-serif;
|
/*:root {*/
|
||||||
margin: 0;
|
/* !* font-size: calc(0.5em + 1vw); *!*/
|
||||||
padding: 0;
|
/* font-size: 62.5%;*/
|
||||||
|
/*}*/
|
||||||
|
|
||||||
|
/* style sheet for "A4" printing */
|
||||||
|
@media print and (width: 21cm) and (height: 29.7cm) {
|
||||||
|
@page {
|
||||||
|
margin: 3cm;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
/* style sheet for "letter" printing */
|
||||||
display: flex;
|
@media print and (width: 8.5in) and (height: 11in) {
|
||||||
height: 90vh;
|
@page {
|
||||||
|
margin: 1in;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.left-panel {
|
* {
|
||||||
flex: 15%;
|
/* margin: 1px 2px;
|
||||||
background-color: #f2f2f2;
|
padding: 1px 2px; */
|
||||||
padding: 10px;
|
font-family: Roboto-Regular, PingFang SC, SF Pro SC, SF Pro Text, SF Pro Icons, Helvetica Neue, Roboto, Helvetica, Arial, sans-serif;
|
||||||
|
outline: none;
|
||||||
|
/* box-sizing: border-box; */
|
||||||
}
|
}
|
||||||
|
|
||||||
.right-panel {
|
.my-header {
|
||||||
flex: 85%;
|
position: relative;
|
||||||
background-color: #ffffff;
|
top: 0;
|
||||||
display: flex;
|
/* height: 8rem; */
|
||||||
flex-direction: column;
|
z-index: 9999;
|
||||||
}
|
left: 0;
|
||||||
|
right: 0;
|
||||||
.chat-log {
|
|
||||||
flex: 1;
|
|
||||||
overflow-y: auto;
|
|
||||||
padding: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.chat-bubble {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-bubble {
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
.bubble-content {
|
|
||||||
padding: 1rem 1.5rem;
|
|
||||||
border-radius: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.user-bubble .bubble-content {
|
|
||||||
background-color: #d6eaff;
|
|
||||||
color: #000000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ai-bubble .bubble-content {
|
|
||||||
background-color: #e5ece7;
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-area {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-text {
|
|
||||||
flex: 1;
|
|
||||||
padding: 1rem;
|
|
||||||
margin-right: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.submit-button {
|
|
||||||
padding: 1rem 2rem;
|
|
||||||
background-color: #2196f3;
|
|
||||||
color: #ffffff;
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
margin-top: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
border: 1px solid #000;
|
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
|
||||||
|
|
||||||
table td, table th {
|
|
||||||
border: 1px solid #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
table td, table th {
|
|
||||||
padding: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.language-sql {
|
|
||||||
width: 95%;
|
|
||||||
background-color: #F6F6F6;
|
|
||||||
padding: 10px;
|
|
||||||
font-weight: bold;
|
|
||||||
border-radius: 5px;
|
|
||||||
word-wrap: break-word;
|
|
||||||
white-space: pre-line;
|
|
||||||
/* overflow-wrap: break-word; */
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 30px;
|
background: #f7dcbc;
|
||||||
border: 2px solid #6089a4;
|
|
||||||
font-size: 15px;
|
|
||||||
margin-top: 5px;
|
|
||||||
}
|
|
||||||
.recommendation{
|
|
||||||
color: #1c4cf3;
|
|
||||||
margin-top: 1rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#noteshare {
|
||||||
|
width: 90%;
|
||||||
|
/* width: 21cm; */
|
||||||
|
min-height: 10rem;
|
||||||
|
/* font-size: 1.5rem; */
|
||||||
|
|
||||||
|
/*border: 1px red solid;*/
|
||||||
|
margin: auto auto 10rem auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#noteshare p {
|
||||||
|
border: 1px rgb(248, 245, 245) solid;
|
||||||
|
/* border: none; */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#testInput {
|
||||||
|
width: 100%;
|
||||||
|
height: 50px;
|
||||||
|
border: 1px rgb(0, 140, 255) solid;
|
||||||
|
margin: 20px auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
::selection {
|
||||||
|
color: antiquewhite;
|
||||||
|
background-color: cadetblue;
|
||||||
|
text-shadow: #00a9ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.my-divider-item {
|
||||||
|
background-color: grey;
|
||||||
|
width: 1px;
|
||||||
|
height: 2rem;
|
||||||
|
margin: 2px 10px 2px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.childStyleStrong {
|
||||||
|
font-weight: bold
|
||||||
|
}
|
||||||
|
|
||||||
|
.childStyleI {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.childStyleU {
|
||||||
|
text-decoration: underline;
|
||||||
|
//中划线
|
||||||
|
//text-decoration: line-through;
|
||||||
|
}
|
||||||
|
|
||||||
|
.childStyleDel {
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
|
||||||
|
.childStyleColor {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<div class="container">
|
|
||||||
<!-- <div class="left-panel">-->
|
|
||||||
<!-- <h2>AI文本助手</h2>-->
|
|
||||||
<!-- <h3>常用问题</h3>-->
|
|
||||||
<!-- <div class="recommendation">Java 21有什么新特性</div>-->
|
|
||||||
<!-- <div class="recommendation">红烧肉怎么做</div>-->
|
|
||||||
<!-- </div>-->
|
|
||||||
<div class="right-panel">
|
|
||||||
<div class="chat-log" id="chat-log">
|
|
||||||
|
|
||||||
|
|
||||||
|
<div class=".my-header">
|
||||||
|
<div style="display: flex;
|
||||||
|
padding: 1rem 4rem;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
background: #f8f6f4;
|
||||||
|
margin: 0 auto;">
|
||||||
|
|
||||||
|
<div class="head-left">
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M20 11H7.83L13.42 5.41L12 4L4 12L12 20L13.41 18.59L7.83 13H20V11Z" fill="black"></path>
|
||||||
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div class="input-area">
|
|
||||||
<input type="text" id="user-input" class="input-text" placeholder="请输入您的问题,回车或点击发送确定。">
|
<div class="head-center" style="display: flex;
|
||||||
<button id="submit" style="margin-left: 10px;width: 100px" onclick="sendMessage()" class="submit-button">
|
justify-content: center;
|
||||||
发送
|
align-items: center;
|
||||||
</button>
|
max-width: 650px;
|
||||||
<button style="margin-left: 2rem;width: 10rem;background-color: red" onclick="clearChat()"
|
background-color: aqua;">
|
||||||
class="submit-button">clear
|
<div style="
|
||||||
</button>
|
position: fixed;
|
||||||
|
z-index: 87;
|
||||||
|
/* top: 200px; */
|
||||||
|
/* left: 324px; */
|
||||||
|
">
|
||||||
|
<div>
|
||||||
|
<div style="display: flex;
|
||||||
|
|
||||||
|
">
|
||||||
|
<div>
|
||||||
|
<button id="myPlusB" data-value="b">加粗</button>
|
||||||
|
</div>
|
||||||
|
<div class="my-divider-item"></div>
|
||||||
|
<div>
|
||||||
|
<button id="myPlusI" data-value="i"><i>I</i></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="my-divider-item"></div>
|
||||||
|
<div>
|
||||||
|
<button id="myPlusU" data-value="u"><u>下划线</u></button>
|
||||||
|
</div>
|
||||||
|
<div class="my-divider-item"></div>
|
||||||
|
<div>
|
||||||
|
<button id="myPlusDel" data-value="del">
|
||||||
|
<del>删除线</del>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="my-divider-item"></div>
|
||||||
|
<div>
|
||||||
|
<button id="myPlusC" data-value="c_red">红色</button>
|
||||||
|
</div>
|
||||||
|
<!-- <div class="my-divider-item"></div>-->
|
||||||
|
<!-- <div>-->
|
||||||
|
<!-- <button onclick="info(this)">info</button>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="head-right">
|
||||||
|
<button>同步</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script type="text/javascript" src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/marked/lib/marked.min.js"></script>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/dompurify/dist/purify.min.js"></script>
|
|
||||||
<script>
|
|
||||||
/*! @license DOMPurify 3.0.5 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.0.5/LICENSE */
|
|
||||||
|
|
||||||
// 添加AI信息
|
<div>
|
||||||
function addAIMessage(message) {
|
<div id="noteshare" spellcheck="false" translate="no">
|
||||||
$("#chat-log").append(
|
</div>
|
||||||
"<div class=\"chat-bubble ai-bubble\">\n" +
|
|
||||||
" <div class=\"bubble-content\">" + message + "</div>\n" +
|
|
||||||
"</div>"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加人类信息
|
</div>
|
||||||
function addUserMessage(message) {
|
</div>
|
||||||
$("#chat-log").append(
|
|
||||||
"<div class=\"chat-bubble user-bubble\">\n" +
|
|
||||||
" <div class=\"bubble-content\">" + message + "</div>\n" +
|
|
||||||
"</div>"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 滑动到底部
|
<footer style="display: flex; justify-content:center; height: 80%; font-size: 1.5rem; background-color:black; bottom: 10px;">
|
||||||
function slideBottom() {
|
|
||||||
let chatlog = document.getElementById("chat-log");
|
|
||||||
chatlog.scrollTop = chatlog.scrollHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 调用api
|
|
||||||
function chatApi(msg) {
|
|
||||||
slideBottom();
|
|
||||||
|
|
||||||
// messagesList.push({role: "user", content: msg});
|
|
||||||
const body = JSON.stringify({ "prompt": msg});
|
|
||||||
// alert(body)
|
|
||||||
$.ajax({
|
|
||||||
method: 'POST',
|
|
||||||
url: '/v1/ai/completionsTest',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
data: body,
|
|
||||||
success: function (res) {
|
|
||||||
// let res1=res?.choices[0]?.message?.content;
|
|
||||||
// res1 = DOMPurify.sanitize(marked.parse(res1));
|
|
||||||
// addAIMessage(res1);
|
|
||||||
|
|
||||||
if (res.code === 0) {
|
|
||||||
let answer = res.data;
|
|
||||||
// answer = marked.parse(answer);
|
|
||||||
answer = DOMPurify.sanitize(marked.parse(answer));
|
|
||||||
addAIMessage(answer);
|
|
||||||
// messageHistory = res.history;
|
|
||||||
} else {
|
|
||||||
addAIMessage("服务接口调用错误。");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
error: function (e) {
|
|
||||||
addAIMessage("服务接口调用异常。");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 发送消息
|
|
||||||
function sendMessage() {
|
|
||||||
let userInput = $('#user-input');
|
|
||||||
let userMessage = userInput.val();
|
|
||||||
if (userMessage.trim() === '') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
userInput.val("");
|
|
||||||
|
|
||||||
// answer = DOMPurify.sanitize(marked.parse(userMessage));
|
|
||||||
|
|
||||||
addUserMessage(DOMPurify.sanitize(marked.parse(userMessage)));
|
|
||||||
chatApi(userMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 清空聊天记录
|
|
||||||
function clearChat() {
|
|
||||||
$("#chat-log").empty();
|
|
||||||
addAIMessage("你好,请输入你想问的问题。");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初始化
|
|
||||||
function init() {
|
|
||||||
addAIMessage("你好,请输入你想问的问题。");
|
|
||||||
let submit = $("#submit");
|
|
||||||
let userInput = $("#user-input");
|
|
||||||
let focus = false;
|
|
||||||
// 监听输入框焦点
|
|
||||||
userInput.focus(function () {
|
|
||||||
focus = true;
|
|
||||||
}).blur(function () {
|
|
||||||
focus = false;
|
|
||||||
});
|
|
||||||
// 回车监听事件
|
|
||||||
document.addEventListener("keydown", function (event) {
|
|
||||||
if (event.key === 'Enter') {
|
|
||||||
console.log(focus);
|
|
||||||
if (focus) {
|
|
||||||
submit.click();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
init();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<footer style="display: flex; justify-content:center; height: 80%; font-size: 1.5rem; background-color:black;">
|
|
||||||
<p>
|
<p>
|
||||||
<a style="color:rgb(201, 201, 206);text-decoration:none;" href="https://beian.miit.gov.cn/" target="_blank"
|
<a style="color:rgb(201, 201, 206);text-decoration:none;" href="https://beian.miit.gov.cn/" target="_blank"
|
||||||
rel="nofollow">
|
rel="nofollow">
|
||||||
@ -586,4 +503,7 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script src="/v1/static/js/utils.js"></script>
|
||||||
|
<script src="/v1/static/js/styleCmd.js"></script>
|
||||||
|
<script src="/v1/static/js/yanxuelu.js"></script>
|
||||||
</html>
|
</html>
|
Loading…
x
Reference in New Issue
Block a user