123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647 |
- <template>
- <div class="template-textarea">
- <template v-if="isEdit == 1">
- <div class="editor-area">
- <ckeditor
- ref="editor"
- v-model="com.content"
- @focus="onFocus"
- @blur="onBlur"
- @input="onInputText"
- @ready="onEditorReady"
- :config="editorConfig"
- :editorUrl="editorUrl"
- ></ckeditor>
- </div>
- </template>
- <template v-else>
- <div class="rich-editor" v-html="content"></div>
- </template>
- <div v-if="loading" class="overlay">
- <el-progress
- :percentage="progress"
- class="full-width-progress"
- ></el-progress>
- </div>
- </div>
- </template>
- <script>
- // import CKEditor from "ckeditor4-vue";
- import { findData } from "@/api/sourceData";
- export default {
- name: "app",
- emits: ["onUpdate", "onUpdateAttr", "onUpdateProdAttr"],
- compnents: {
- // ckeditor: CKEditor.component
- },
- props: {
- coms: {
- type: Array,
- default: () => {
- return [];
- },
- },
- isEdit: {
- type: Number,
- default: 2,
- },
- isAdmin: {
- type: Number,
- default: 2,
- },
- currentIndex: {
- type: Number,
- default: 0,
- },
- com: {
- type: Object,
- default: null,
- },
- insertCmd: {
- type: Object,
- default: null,
- },
- },
- watch: {
- isEdit: {
- handler(val) {
- let _this = this;
- if (_this.com != null) return;
- _this.replaceData(_this.com.content).then((res) => {
- _this.content = res;
- _this.$nextTick(() => {
- _this.bindEvents();
- });
- });
- },
- immediate: true,
- deep: true,
- },
- com: {
- async handler(val) {
- let _this = this;
- if (val == null) return;
- if (val.content == undefined || val.content == null) return;
- try {
- console.log("com",val);
- let res = await _this.replaceData(val.content);
- _this.content = res;
- _this.$nextTick(() => {
- _this.bindEvents();
- });
- } catch (error) {
- console.error("处理 com 时出错:", error);
- }
- },
- immediate: true,
- deep: true,
- },
- insertCmd: {
- handler(val) {
- if (val == null || this.isEdit != 1) return;
- let data = this.$refs.editor.instance.getSelection().getSelectedText();
- if (val.content.indexOf("Directory", 0) >= 0) {
- //执行的是目录
- this.$emit(
- "onUpdateAttr",
- this.currentIndex,
- this.com.attrs.length - 1,
- data
- );
- this.$refs.editor.instance.execCommand("delete");
- }
- this.$refs.editor.instance.insertHtml(val.content);
- },
- immediate: true,
- deep: true,
- },
- },
- data() {
- return {
- editorUrl: "/ckeditor/ckeditor.js",
- editorConfig: {
- language: "zh-cn",
- height: "800px",
- },
- content: "",
- editor: null,
- loading: false,
- progress: 0,
- isEditing: false,
- focusedInputId: null,
- };
- },
- mounted() {},
- // 记得在组件销毁时移除事件监听器
- beforeDestroy() {
- this.$el.removeEventListener("input", this.handleInputChange);
- },
- methods: {
- async replaceData(data) {
- let _this = this;
- for (var i = 0; i < _this.com.attrs.length; i++) {
- let attrId = _this.com.attrs[i].id;
- if (_this.com.attrs[i].type == "variable") {
- let item = _this.com.attrs[i];
- if (item.data.value_type == 2) {
- //多选
- let dataItem = item.data.value_item.split(",");
- let selectHtml =
- '<select id="' +
- attrId +
- '" data-index="' +
- i +
- '" class="text-input-box">';
- for (var l = 0; l < dataItem.length; l++) {
- if (item.content == dataItem[l]) {
- selectHtml +=
- '<option value="' +
- dataItem[l] +
- '" selected>' +
- dataItem[l] +
- "</option>";
- } else {
- selectHtml +=
- '<option value="' +
- dataItem[l] +
- '">' +
- dataItem[l] +
- "</option>";
- }
- }
- selectHtml += "</select>";
- data = data.replace("{{" + item.id + "}}", selectHtml);
- } else {
- //文本
- data = data.replace(
- "{{" + attrId + "}}",
- /* `<input type="text" class="text-input-box" data-index="${i}" value="${_this.com.attrs[i].content}" @input="updateContent('${_this.com.attrs[i].id}', $event)" id="${attrId}">`, */
- '<input type="text" ref="input_' +
- attrId +
- '" name="' +
- _this.com.attrs[i].name +
- '" id="' +
- attrId +
- '" data-index="' +
- i +
- '" class="text-input-box" value="' +
- _this.com.attrs[i].content +
- '">'
- );
- }
- } else if (_this.com.attrs[i].type == "ProductAttr") {
- //处理产品属性值
- let item = _this.com.attrs[i];
- let prodAttrId = item.id + "_" + i;
- if (item.content == "") {
- item.content = item.attrs.value;
- }
- if (item.attrs.type == 1) {
- //输入框
- data = data.replace(
- "{{" + item.id + "}}",
- '<input type="text" id="' +
- prodAttrId +
- '" data-index="' +
- i +
- '" class="text-input-box" value="' +
- item.content +
- '">'
- );
- } else {
- //选择框
- let dataItem = item.attrs.valueItems.split(",");
- let selectHtml =
- '<select id="' +
- prodAttrId +
- '" data-index="' +
- i +
- '" class="text-input-box">';
- for (var l = 0; l < dataItem.length; l++) {
- if (item.content == dataItem[l]) {
- selectHtml +=
- '<option value="' +
- dataItem[l] +
- '" selected>' +
- dataItem[l] +
- "</option>";
- } else {
- selectHtml +=
- '<option value="' +
- dataItem[l] +
- '">' +
- dataItem[l] +
- "</option>";
- }
- }
- selectHtml += "</select>";
- data = data.replace("{{" + item.id + "}}", selectHtml);
- }
- } else if (_this.com.attrs[i].type == "formual") {
- //// console.log('attrs',_this.com.attrs[i]);
- //处理计算公式 不处理
- let formual = await _this.analysisFormual(_this.com.attrs[i]);
- formual = await _this.getRemote(formual);
- _this.com.attrs[i].content = eval(formual);
- data = data.replace("{{" + attrId + "}}", eval(formual));
- /* let formual = await _this.analysisFormual(_this.com.attrs[i]);
- formual = await _this.getRemote(formual);
- // 使用 try-catch 来处理可能的计算错误
- try {
- let result = eval(formual);
- data = data.replace("{{" + attrId + "}}", result);
- } catch (error) {
- data = data.replace("{{" + attrId + "}}", "计算错误");
- } */
- } else if (_this.com.attrs[i].type == "sourceData") {
- let result = await _this.getRemote1(_this.com.attrs[i].formula);
- data = data.replace("{{" + attrId + "}}", result);
- } else if (_this.com.attrs[i].type == "Directory") {
- const directoryContent = _this.com.attrs[i].content;
- const attrId = _this.com.attrs[i].id;
- // Create a more flexible regex pattern
- const directoryRegex = new RegExp(`{{\\s*${attrId}\\s*}}`, "g");
- // Replace the content, preserving surrounding HTML tags
- data = data.replace(directoryRegex, (match, offset, string) => {
- // Check if the match is inside a tag
- const precedingText = string.substring(0, offset);
- const followingText = string.substring(offset + match.length);
- const precedingTag = precedingText.match(/<([^>]+)>$/);
- const followingCloseTag = followingText.match(/^<\/([^>]+)>/);
- if (
- precedingTag &&
- followingCloseTag &&
- precedingTag[1] === followingCloseTag[1]
- ) {
- // If it's inside matching tags, replace the entire element
- return `<h1>${directoryContent}</h1>`;
- } else {
- // Otherwise, just replace the placeholder
- return directoryContent;
- }
- });
- //处理目录
- /* data = data.replace(
- "<p>{{" + attrId + "}}</p>",
- "<h1>" + _this.com.attrs[i].content + "</h1>"
- );
- data = data.replace(
- "<div>{{" + attrId + "}}</div>",
- "<h1>" + _this.com.attrs[i].content + "</h1>"
- );
- data = data.replace(
- "<span>{{" + attrId + "}}</span>",
- "<h1>" + _this.com.attrs[i].content + "</h1>"
- ); */
- //处理目录
- /* const directoryContent = _this.com.attrs[i].content;
- const directoryRegex = new RegExp(
- `<(p|div|span)>{{${attrId}}}</(p|div|span)>`,
- "g"
- );
- data = data.replace(directoryRegex, `<h1>${directoryContent}</h1>`); */
- } else {
- //处理常量及其它
- data = data.replace("{{" + attrId + "}}", _this.com.attrs[i].content);
- }
- }
- this.$nextTick(() => {
- this.addInputListeners();
- });
- return data;
- },
- addInputListeners() {
- // 使用事件委托来处理所有的 .text-input-box 元素
- this.$el.addEventListener("blur", this.handleInputChange, true);
- },
- handleInputChange(event) {
- if (event.target.classList.contains("text-input-box")) {
- const attrId = event.target.id;
- const attrName = event.target.name;
- const dataIndex = parseInt(event.target.dataset.index, 10);
- const newValue = event.target.value;
- // 更新数据
- if (this.com && this.com.attrs && this.com.attrs[dataIndex]) {
- this.$set(this.com.attrs[dataIndex], "content", newValue);
- this.com.attrs.forEach((el) => {
- if (el.name === attrName) {
- this.$set(el, "content", newValue);
- }
- });
- this.$emit("onUpdateAttr", this.currentIndex, dataIndex, newValue);
- // 使用 $nextTick 确保在 DOM 更新后执行
- } else {
- console.warn(`Unable to find attribute at index ${dataIndex}`);
- }
- }
- },
- bindEvents() {
- let _this = this;
- for (var i = 0; i < _this.com.attrs.length; i++) {
- let attrId = _this.com.attrs[i].id;
- let item = _this.com.attrs[i];
- if (_this.com.attrs[i].type == "variable") {
- //变量事件
- let input = _this.$el.querySelector("#" + attrId);
- if (input) {
- if (item.data.value_type == 2) {
- //下拉选择
- input.addEventListener("change", _this.handleInput);
- } else {
- //文本输入
- input.addEventListener("blur", _this.handleInput);
- }
- }
- } else if (_this.com.attrs[i].type == "ProductAttr") {
- //绑定产品属性事件
- let prodAttrId = item.id + "_" + i;
- let input = _this.$el.querySelector("#" + prodAttrId);
- if (input) {
- if (item.attrs.type == 1) {
- input.addEventListener("blur", _this.handleInputProduct);
- } else {
- input.addEventListener("change", _this.handleChangeProduct);
- }
- }
- }
- }
- },
- //分析公式
- async analysisFormual(attrs) {
- let _this = this;
- // console.log("开始分析公式:", attrs.formula);
- var pattern = /(\[.*?\]){3}/;
- var formual = attrs.formula;
- var reg = new RegExp(pattern);
- while (true) {
- var items = formual.match(reg);
- if (items == null) break;
- let itemName = items[0];
- // console.log("处理项:", itemName);
- try {
- let data = await _this.getFormualData(itemName);
- // console.log(`获取到的数据: ${itemName} = ${data}`);
- if (data === null || data === undefined || isNaN(data)) {
- console.warn(`获取到的数据无效: ${itemName}`);
- formual = formual.replace(itemName, "(0)");
- } else {
- formual = formual.replace(itemName, `(${parseFloat(data)})`);
- }
- } catch (error) {
- console.error(`处理 ${itemName} 时出错:`, error);
- formual = formual.replace(itemName, "(0)");
- }
- }
- // console.log("分析后的公式:", formual);
- return formual;
- },
- async getFormualData(formualItem) {
- let _this = this;
- // console.log("开始获取公式数据:", formualItem);
- var pattern = /\[(.*?)\]\[(.*?)\]\[(.*?)\]/;
- var reg = new RegExp(pattern);
- let items = formualItem.match(reg);
- // console.log("解析结果:", items);
- let calResult = 0;
- if (items && items[1] == "T") {
- calResult = await _this.getModuleData(items[2], items[3]);
- } else {
- console.warn("无法解析公式项:", formualItem);
- }
- // console.log("计算结果:", calResult);
- return calResult;
- },
- async getRemote(formual) {
- let _this = this;
- var pattern = /\[(.*?)\]\[(.*?)\]\[(.*?)\]\[(.*?)\]/;
- var reg = new RegExp(pattern);
- while (true) {
- let items = formual.match(reg);
- if (items == null) break;
- if (items[1] == "R") {
- //表示获取远程数据
- var dataIndex = items[4];
- dataIndex = items[4].split(",");
- let calResult = await _this.getRemoteData(
- items[2],
- items[3],
- dataIndex[0],
- dataIndex[1]
- );
- let itemName = items[0];
- // 检查calResult是否为数字
- if (!isNaN(calResult)) {
- formual = formual.replace(
- itemName,
- "(" + parseFloat(calResult) + ")"
- );
- } else {
- formual = formual.replace(itemName, `"${calResult}"`);
- }
- formual = formual.replace(
- itemName,
- "(" + parseFloat(calResult) + ")"
- );
- }
- }
- return formual;
- },
- async getRemote1(formual) {
- let _this = this;
- var pattern = /\[(.*?)\]\[(.*?)\]\[(.*?)\]\[(.*?)\]/;
- var reg = new RegExp(pattern);
- while (true) {
- let items = formual.match(reg);
- if (items == null) break;
- if (items[1] == "R") {
- //表示获取远程数据
- var dataIndex = items[4];
- dataIndex = items[4].split(",");
- let calResult = await _this.getRemoteData(
- items[2],
- items[3],
- dataIndex[0],
- dataIndex[1]
- );
- let itemName = items[0];
- // 检查calResult是否为数字
- if (!isNaN(calResult)) {
- formual = formual.replace(
- itemName,
- "(" + parseFloat(calResult) + ")"
- );
- } else {
- formual = formual.replace(itemName, `"${calResult}"`);
- }
- /* formual=formual.replace(itemName,parseFloat(calResult)); */
- }
- }
- return formual;
- },
- onFocus() {
- this.isEditing = true;
- },
- onBlur() {
- this.isEditing = false;
- },
- async getRemoteData(code, sheet, row, col) {
- let _this = this;
- let result = 0;
- let par = {
- code: code,
- sheetName: sheet,
- row: row,
- col: col,
- };
- /* if (!this.isEditing) {
- this.loading = true;
- this.progress = 0;
- try {
- const updateProgress = setInterval(() => {
- if (this.progress < 90) {
- this.progress += 10;
- }
- }, 100);
- let res = await findData(par);
- if (res.status == 200) {
- result = res.data.result;
- }
- this.progress = 100;
- clearInterval(updateProgress);
- } catch (error) {
- console.error("Error fetching remote data:", error);
- } finally {
- setTimeout(() => {
- this.loading = false;
- this.progress = 0;
- }, 1000);
- }
- } else { */
- // 如果正在编辑,直接获取数据而不显示进度条
- try {
- let res = await findData(par);
- if (res.status == 200) {
- result = res.data.result;
- // 添加数据类型检查和转换
- if (typeof result === "string" && !isNaN(result)) {
- result = parseFloat(result);
- }
- }
- } catch (error) {
- console.error("获取远程数据错误:", error);
- }
- /* } */
- return result;
- },
- //获取本地文档模块数据
- async getModuleData(code, attrName) {
- let _this = this;
- // console.log(`尝试获取模块数据: code=${code}, attrName=${attrName}`);
- // console.log("当前 coms:", _this.coms);
- let item = _this.coms.filter((o) => o.name == code);
- // console.log(`找到的模块:`, item);
- if (item.length > 0) {
- let attr = item[0].attrs.filter((o) => o.name == attrName);
- // console.log(`找到的属性:`, attr);
- if (attr.length > 0) {
- // console.log(`返回的值:`, attr[0].content);
- return parseFloat(attr[0].content);
- }
- }
- // console.log("未找到匹配的模块或属性,返回 0");
- return 0;
- },
- handleChangeProduct(e) {
- let dataIndex = e.target.dataset.index;
- this.$emit(
- "onUpdateProdAttr",
- this.currentIndex,
- dataIndex,
- e.target.value
- );
- },
- handleInputProduct(e) {
- let dataIndex = e.target.dataset.index;
- this.$emit(
- "onUpdateProdAttr",
- this.currentIndex,
- dataIndex,
- e.target.value
- );
- },
- handleInput(e) {
- let dataIndex = e.target.dataset.index;
- this.$emit("onUpdateAttr", this.currentIndex, dataIndex, e.target.value);
- },
- onEditorReady(editor) {},
- onInputText(e) {
- this.$emit("onUpdate", this.currentIndex, e);
- },
- /* onFocus(e) {
- // var range = e.editor.getSelection().getRanges()[0];
- // range.collapse( true );
- // range.setStartAt( e.editor.editable(), CKEDITOR.POSITION_AFTER_START );
- // e.editor.insertText("AAA");
- },
- onBlur(e) {
- //// console.log(' onBlur e',e);
- // this.$emit("onUploadAttr",this.dataIndex,this.dataAttr);
- }, */
- },
- };
- </script>
- <style lang="scss">
- @import "./index.scss";
- .template-textarea {
- position: relative;
- }
- .overlay {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- background: rgba(0, 0, 0, 0.5); // Semi-transparent background
- display: flex;
- align-items: center;
- justify-content: center;
- z-index: 10; // Ensure it is above other content
- }
- .full-width-progress {
- width: 80%; // Adjust as needed
- color: inherit; // Ensure the color remains unchanged
- }
- </style>
|