3 changed files with 325 additions and 13 deletions
@ -0,0 +1,258 @@ |
|||||
|
|
||||
|
<script lang='ts' setup> |
||||
|
import { ref, onMounted } from "vue"; |
||||
|
import tinymce from "tinymce/tinymce"; |
||||
|
import "tinymce/models/dom"; // 特别注意 tinymce 6.0.0 版本之后必须引入,否则不显示 |
||||
|
import "tinymce/themes/silver/theme"; |
||||
|
import Editor from "@tinymce/tinymce-vue"; // 引入组件 |
||||
|
import { v4 as uuidv4 } from "uuid"; |
||||
|
|
||||
|
let onlyNumber = uuidv4().replaceAll('-','').toString(); //获取唯一编码 |
||||
|
|
||||
|
/** |
||||
|
* 初始富文本组件 |
||||
|
*/ |
||||
|
const tinymceInit = { |
||||
|
selector: "#"+onlyNumber, |
||||
|
language_url: "/tinymce/langs/zh-Hans.js", // 引入语言包(该语言包在public下,注意文件名称) |
||||
|
language: "zh-Hans", // 这里名称根据 zh-Hans.js 里面写的名称而定 |
||||
|
skin_url: "/tinymce/skins/ui/oxide", // 这里引入的样式 |
||||
|
height: 164, // 限制高度 |
||||
|
statusbar:false, |
||||
|
toolbar:false, |
||||
|
branding: false, //是否禁用“Powered by TinyMCE” |
||||
|
menubar: false, //顶部菜单栏显示 |
||||
|
forced_root_block:'', |
||||
|
newline_behavior:"", |
||||
|
content_css: "/tinymce/skins/content/default/content.css", //以css文件方式自定义可编辑区域的css样式,css文件需自己创建并引入 |
||||
|
auto_focus : true, |
||||
|
} |
||||
|
const props = defineProps({ |
||||
|
aftText:{ |
||||
|
type: String, |
||||
|
default(){ |
||||
|
return {} |
||||
|
} |
||||
|
}, |
||||
|
aftTextCopy:{ |
||||
|
type: String, |
||||
|
default(){ |
||||
|
return {} |
||||
|
} |
||||
|
}, |
||||
|
}) |
||||
|
|
||||
|
let $emit = defineEmits(["textChange","gongshiChange"]); |
||||
|
|
||||
|
const tinymceHtml = ref("") |
||||
|
tinymceHtml.value = props.aftText |
||||
|
const tinymceBox = ref(null) |
||||
|
|
||||
|
|
||||
|
onMounted(() => { |
||||
|
tinymce.init({}); // 初始化富文本 |
||||
|
}); |
||||
|
watch(()=>tinymceHtml.value, (val:any) => { |
||||
|
$emit('textChange',val); |
||||
|
nextTick(()=>{ |
||||
|
let formulaOne = []; |
||||
|
let formulaTwo = []; |
||||
|
tinymceBox.value.childNodes.forEach(element => { |
||||
|
|
||||
|
element.childNodes.forEach(child => { |
||||
|
formulaOne.push(child.innerText?child.innerText:child.data) |
||||
|
if(child.dataset&&child.dataset.keyid){ |
||||
|
formulaTwo.push(child.dataset.keyid) |
||||
|
}else{ |
||||
|
formulaTwo.push(child.innerText?child.innerText:child.data) |
||||
|
} |
||||
|
}) |
||||
|
}); |
||||
|
let suanShitwo = formulaTwo.join('').replace(/\s+/g, ""); |
||||
|
let gongShi ={ |
||||
|
formulaHtml:tinymceBox.value.innerHTML, |
||||
|
mathsString:tinymceBox.value.innerText, |
||||
|
mathsFormula:suanShitwo, |
||||
|
} |
||||
|
|
||||
|
$emit('gongshiChange',gongShi); |
||||
|
if(containsNewline(gongShi.mathsString)){ |
||||
|
errorCondition("条件不允许换行") |
||||
|
}else if(!gongShi.formulaHtml.startsWith("<p><span")){ |
||||
|
errorCondition("条件需以蓝色块开头") |
||||
|
}else if(gongShi.formulaHtml.endsWith("</span></p>")){ |
||||
|
errorCondition("条件不能以蓝色块结尾") |
||||
|
}else if(countSpanTags(gongShi.formulaHtml)>1){ |
||||
|
errorCondition("不允许出现多个蓝色块") |
||||
|
}else if(!containsSingleComparator(gongShi.mathsFormula)&&!gongShi.mathsFormula.includes("包含")&&!gongShi.mathsFormula.includes("不包含")){ |
||||
|
|
||||
|
errorCondition("不存在有效符号或关键字") |
||||
|
|
||||
|
}else if(checkEnding(gongShi.mathsFormula)){ |
||||
|
errorCondition("不能以符号或关键字为结尾") |
||||
|
}else if(checkRoleStartAndOrgEnd(gongShi.mathsFormula)||checkFormFieldStartAndOrgEnd(gongShi.mathsFormula)||checkFormFieldStartAndOrgEnd2(gongShi.mathsFormula)){ |
||||
|
errorCondition("此条件无无实际意义") |
||||
|
}else{ |
||||
|
succCondition() |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
}, |
||||
|
{ deep: true } |
||||
|
) |
||||
|
|
||||
|
function checkRoleStartAndOrgEnd(str: string) { |
||||
|
if (str.startsWith('roleid:') && str.endsWith('数据所属部门')) { |
||||
|
return true; |
||||
|
} else { |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
function checkFormFieldStartAndOrgEnd(str: string) { |
||||
|
if (str.startsWith('formField:') && str.endsWith('数据所属部门')) { |
||||
|
return true; |
||||
|
} else { |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function checkFormFieldStartAndOrgEnd2(str: string) { |
||||
|
if (str.startsWith('formField:') && str.endsWith('数据拥有者')) { |
||||
|
return true; |
||||
|
} else { |
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function containsSingleComparator(str: string) { |
||||
|
// 定义需要检查的运算符 |
||||
|
const comparators = ['==', '>=', '>', '<=', '<', '!=']; |
||||
|
let found = false; // 用于标记是否找到运算符 |
||||
|
let i = 0; // 字符串索引 |
||||
|
|
||||
|
while (i < str.length - 1) { // 至少需要两个字符来形成运算符 |
||||
|
// 遍历每个可能的运算符 |
||||
|
for (const comparator of comparators) { |
||||
|
if (str.substr(i, comparator.length) === comparator) { |
||||
|
// 如果找到了一个运算符 |
||||
|
if (found) { |
||||
|
// 如果已经找到了一个运算符,则返回false |
||||
|
return false; |
||||
|
} |
||||
|
found = true; // 标记为已找到 |
||||
|
// 跳过当前找到的运算符的剩余部分,避免重复检查 |
||||
|
i += comparator.length - 1; |
||||
|
break; // 跳出内层循环 |
||||
|
} |
||||
|
} |
||||
|
i++; // 继续检查下一个字符 |
||||
|
} |
||||
|
|
||||
|
// 检查是否只找到了一个运算符 |
||||
|
return found; |
||||
|
} |
||||
|
function checkEnding(str: string) { |
||||
|
const symbols = ['==', '>=', '>', '<=', '<', '!=', '=','包含','不包含']; |
||||
|
const trimmedStr = str.trim(); |
||||
|
for (let symbol of symbols) { |
||||
|
if (trimmedStr.endsWith(symbol)) { |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
function containsComparator(str: string) { |
||||
|
if(str.includes("==")||str.includes("!=")||str.includes(">")||str.includes(">=")||str.includes("<")||str.includes("<=")){ |
||||
|
return true |
||||
|
}else{ |
||||
|
return false |
||||
|
} |
||||
|
} |
||||
|
const containsNewline = (str: string) => { |
||||
|
//console.log("Testing string:", str); // 查看传递给函数的实际字符串 |
||||
|
return /\n/.test(str); |
||||
|
}; |
||||
|
const countSpanTags = (str: string) => { |
||||
|
// 使用正则表达式匹配所有出现的<span>(不考虑闭合标签或属性) |
||||
|
const matches = str.match(/<span\b[^>]*>/gi); |
||||
|
// 如果matches是null,表示没有找到匹配项,返回0 |
||||
|
// 否则,返回匹配项数组的长度 |
||||
|
return matches ? matches.length : 0; |
||||
|
}; |
||||
|
function errorCondition(str:string){ |
||||
|
ElMessage.closeAll() |
||||
|
ElMessage({ |
||||
|
showClose: true, |
||||
|
//message: '条件格式错误 : '+str+' , 正确条件格式如 : 年龄==10', |
||||
|
message: str, |
||||
|
type: 'error', |
||||
|
duration:3500, |
||||
|
}) |
||||
|
} |
||||
|
function succCondition(){ |
||||
|
ElMessage.closeAll() |
||||
|
ElMessage({ |
||||
|
showClose: true, |
||||
|
message: '条件格式校验通过', |
||||
|
type: 'success', |
||||
|
duration:3500, |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
const addIcon = (currentObject:any) =>{ |
||||
|
tinymce.activeEditor?.execCommand('mceInsertContent', false, `<span style="margin:3px;background-color: #4189EF;border-radius: 5px; padding:3px" contenteditable="false" data-keyid= "${currentObject.id}" >${currentObject.label}</span>`); |
||||
|
} |
||||
|
const addIcon_field = (currentObject:any) =>{ |
||||
|
tinymce.activeEditor?.execCommand('mceInsertContent', false, `<span style="margin:3px;background-color: #4189EF;border-radius: 5px; padding:3px" contenteditable="false" data-keyid= "${currentObject.id}" >${currentObject.treeAttrs.show}</span>`); |
||||
|
} |
||||
|
const addIcon_org = (currentObject:any) =>{ |
||||
|
let id = "orgOrPerson:"+currentObject.id |
||||
|
tinymce.activeEditor?.execCommand('mceInsertContent', false, `<span style="margin:3px;background-color: #4189EF;border-radius: 5px; padding:3px" contenteditable="false" data-keyid= "${id}" >${currentObject.label}</span>`); |
||||
|
} |
||||
|
//取消 |
||||
|
const handelCancel = (associatedFormsHideDialogTextCopy:any) =>{ |
||||
|
tinymceHtml.value = associatedFormsHideDialogTextCopy |
||||
|
} |
||||
|
const tinymceReInit = ()=>{ |
||||
|
//console.log(props.aftTextCopy) |
||||
|
|
||||
|
let str:string = props.aftTextCopy |
||||
|
tinymceHtml.value = props.aftTextCopy |
||||
|
} |
||||
|
|
||||
|
const focusEditor = ()=>{ |
||||
|
tinymce.EditorManager.get(onlyNumber)?.focus(true) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
defineExpose({ |
||||
|
tinymceHtml, |
||||
|
addIcon, |
||||
|
addIcon_org, |
||||
|
addIcon_field, |
||||
|
handelCancel, |
||||
|
tinymceReInit, |
||||
|
focusEditor, |
||||
|
}) |
||||
|
|
||||
|
</script> |
||||
|
<template > |
||||
|
<div > |
||||
|
<editor :id="onlyNumber" v-model="tinymceHtml" :init="tinymceInit"></editor> |
||||
|
</div> |
||||
|
<div class="isHidde"> |
||||
|
<div ref="tinymceBox" v-html="tinymceHtml"> </div> |
||||
|
</div> |
||||
|
|
||||
|
|
||||
|
</template> |
||||
|
|
||||
|
<style lang='scss' scoped> |
||||
|
.isHidde{ |
||||
|
display:none; |
||||
|
position: fixed; |
||||
|
z-index: 1; |
||||
|
margin-top: -10px |
||||
|
} |
||||
|
</style> |
||||
Loading…
Reference in new issue