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