index.vue 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071
  1. <template>
  2. <div class="template-textarea">
  3. <template v-if="isEdit == 1">
  4. <div class="editor-area sticky-editor">
  5. <ckeditor
  6. ref="editor"
  7. v-model="com.content"
  8. @focus="onFocus"
  9. @blur="onBlur"
  10. @input="onInputText"
  11. @ready="onEditorReady"
  12. :config="editorConfig"
  13. :editorUrl="editorUrl"
  14. ></ckeditor>
  15. </div>
  16. </template>
  17. <template v-else>
  18. <div
  19. class="rich-editor"
  20. ref="richEditor"
  21. v-html="content"
  22. @click="handleImageClick"
  23. ></div>
  24. </template>
  25. <div v-if="loading" class="overlay">
  26. <el-progress
  27. :percentage="progress"
  28. class="full-width-progress"
  29. ></el-progress>
  30. </div>
  31. </div>
  32. </template>
  33. <script>
  34. // import CKEditor from "ckeditor4-vue";
  35. import { findData, uploadImage } from "@/api/sourceData";
  36. import axios from "axios"; // 确保导入 axios
  37. import { data } from "jquery";
  38. export default {
  39. name: "app",
  40. emits: ["onUpdate", "onUpdateAttr", "onUpdateProdAttr"],
  41. compnents: {
  42. // ckeditor: CKEditor.component
  43. },
  44. props: {
  45. coms: {
  46. type: Array,
  47. default: () => {
  48. return [];
  49. },
  50. },
  51. isEdit: {
  52. type: Number,
  53. default: 2,
  54. },
  55. isAdmin: {
  56. type: Number,
  57. default: 2,
  58. },
  59. currentIndex: {
  60. type: Number,
  61. default: 0,
  62. },
  63. com: {
  64. type: Object,
  65. default: null,
  66. },
  67. insertCmd: {
  68. type: Object,
  69. default: null,
  70. },
  71. },
  72. watch: {
  73. isEdit: {
  74. handler(val) {
  75. let _this = this;
  76. if (_this.com != null) return;
  77. _this.replaceData(_this.com.content).then((res) => {
  78. _this.content = res;
  79. _this.$nextTick(() => {
  80. _this.bindEvents();
  81. });
  82. });
  83. },
  84. immediate: true,
  85. deep: true,
  86. },
  87. com: {
  88. async handler(val) {
  89. let _this = this;
  90. if (val == null) return;
  91. if (val.content == undefined || val.content == null) return;
  92. try {
  93. let res = await _this.replaceData(val.content);
  94. _this.content = res;
  95. _this.$nextTick(() => {
  96. _this.bindEvents();
  97. _this.initializeInputWidths();
  98. });
  99. } catch (error) {
  100. console.error("处理 com 时出错:", error);
  101. }
  102. },
  103. immediate: true,
  104. deep: true,
  105. },
  106. insertCmd: {
  107. handler(val) {
  108. if (val == null || this.isEdit != 1) return;
  109. let data = this.$refs.editor.instance.getSelection().getSelectedText();
  110. if (val.content.indexOf("Directory", 0) >= 0) {
  111. //执行的是目录
  112. this.$emit(
  113. "onUpdateAttr",
  114. this.currentIndex,
  115. this.com.attrs.length - 1,
  116. data
  117. );
  118. this.$refs.editor.instance.execCommand("delete");
  119. }
  120. this.$refs.editor.instance.insertHtml(val.content);
  121. },
  122. immediate: true,
  123. deep: true,
  124. },
  125. },
  126. data() {
  127. return {
  128. editorUrl: "/ckeditor/ckeditor.js",
  129. editorConfig: {
  130. language: "zh-cn",
  131. height: "650px",
  132. },
  133. content: "",
  134. editor: null,
  135. loading: false,
  136. progress: 0,
  137. isEditing: false,
  138. focusedInputId: null,
  139. variableNullInputs: {},
  140. };
  141. },
  142. mounted() {
  143. this.$nextTick(() => {
  144. this.initializeInputWidths();
  145. });
  146. },
  147. // 记得在组件销毁时移除事件监听器
  148. beforeDestroy() {
  149. this.$el.removeEventListener("input", this.handleInputChange);
  150. this.$el.removeEventListener("input", this.handleVariableNullInput);
  151. this.$el.removeEventListener("blur", this.handleVariableNullBlur, true);
  152. },
  153. methods: {
  154. async replaceData(data) {
  155. let _this = this;
  156. // 定义数学函数和 IF 函数
  157. const mathFunctions = {
  158. abs: Math.abs,
  159. ceil: Math.ceil,
  160. floor: Math.floor,
  161. max: Math.max,
  162. min: Math.min,
  163. round: Math.round,
  164. sqrt: Math.sqrt,
  165. IF: (condition, trueValue, falseValue) =>
  166. condition ? trueValue : falseValue,
  167. // 添加其他需要的数学函数
  168. };
  169. for (var i = 0; i < _this.com.attrs.length; i++) {
  170. let attrId = _this.com.attrs[i].id;
  171. if (_this.com.attrs[i].type == "variable") {
  172. let item = _this.com.attrs[i];
  173. if (item.data.value_type == 2) {
  174. //多选
  175. let dataItem = item.data.value_item.split(",");
  176. let selectHtml =
  177. '<select id="' +
  178. attrId +
  179. '" data-index="' +
  180. i +
  181. '" class="text-input-box">';
  182. for (var l = 0; l < dataItem.length; l++) {
  183. if (item.content == dataItem[l]) {
  184. selectHtml +=
  185. '<option value="' +
  186. dataItem[l] +
  187. '" selected>' +
  188. dataItem[l] +
  189. "</option>";
  190. } else {
  191. selectHtml +=
  192. '<option value="' +
  193. dataItem[l] +
  194. '">' +
  195. dataItem[l] +
  196. "</option>";
  197. }
  198. }
  199. selectHtml += "</select>";
  200. data = data.replace("{{" + item.id + "}}", selectHtml);
  201. } else {
  202. //文本
  203. data = data.replace(
  204. "{{" + attrId + "}}",
  205. /* `<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}">`, */
  206. '<input type="text" ref="input_' +
  207. attrId +
  208. '" name="' +
  209. _this.com.attrs[i].name +
  210. '" id="' +
  211. attrId +
  212. '" data-index="' +
  213. i +
  214. '" class="text-input-box auto-width" value="' +
  215. _this.com.attrs[i].content +
  216. '">'
  217. );
  218. }
  219. } else if (_this.com.attrs[i].type == "variableNull") {
  220. let item = _this.com.attrs[i];
  221. // 使用唯一的标识符来初始化每个输入框的值
  222. this.variableNullInputs[attrId] = item.content;
  223. data = data.replace(
  224. "{{" + attrId + "}}",
  225. '<input type="text" ' +
  226. 'ref="input_' +
  227. attrId +
  228. '" ' +
  229. 'name="' +
  230. item.name +
  231. '" ' +
  232. 'id="' +
  233. attrId +
  234. '" ' +
  235. 'data-index="' +
  236. i +
  237. '" ' +
  238. 'data-attr-id="' +
  239. attrId +
  240. '" ' +
  241. 'class="text-input-boxs auto-width" ' +
  242. 'value="' +
  243. item.content +
  244. '">'
  245. );
  246. } else if (_this.com.attrs[i].type == "ProductAttr") {
  247. //处理产品属性值
  248. let item = _this.com.attrs[i];
  249. let prodAttrId = item.id + "_" + i;
  250. if (item.content == "") {
  251. item.content = item.attrs.value;
  252. }
  253. if (item.attrs.type == 1) {
  254. //输入框
  255. data = data.replace(
  256. "{{" + item.id + "}}",
  257. '<input type="text" id="' +
  258. prodAttrId +
  259. '" data-index="' +
  260. i +
  261. '" class="text-input-box" value="' +
  262. item.content +
  263. '">'
  264. );
  265. } else {
  266. //选择框
  267. let dataItem = item.attrs.valueItems.split(",");
  268. let selectHtml =
  269. '<select id="' +
  270. prodAttrId +
  271. '" data-index="' +
  272. i +
  273. '" class="text-input-box">';
  274. for (var l = 0; l < dataItem.length; l++) {
  275. if (item.content == dataItem[l]) {
  276. selectHtml +=
  277. '<option value="' +
  278. dataItem[l] +
  279. '" selected>' +
  280. dataItem[l] +
  281. "</option>";
  282. } else {
  283. selectHtml +=
  284. '<option value="' +
  285. dataItem[l] +
  286. '">' +
  287. dataItem[l] +
  288. "</option>";
  289. }
  290. }
  291. selectHtml += "</select>";
  292. data = data.replace("{{" + item.id + "}}", selectHtml);
  293. }
  294. } else if (_this.com.attrs[i].type == "formual") {
  295. //处理计算公式 不处理
  296. let formual = await _this.analysisFormual(_this.com.attrs[i]);
  297. let point = _this.com.attrs[i].data.point;
  298. formual = await _this.getRemote(formual);
  299. /* console.log("处理的公式:", formual); // 添加日志 */
  300. // 收集公式中使用的变量
  301. const variables = {};
  302. const variableRegex = /\b[a-zA-Z_][a-zA-Z0-9_]*\b/g;
  303. const matches = formual.match(variableRegex);
  304. if (matches) {
  305. for (const varName of matches) {
  306. if (
  307. varName !== "IF" &&
  308. !Object.keys(mathFunctions).includes(varName)
  309. ) {
  310. variables[varName] = `${varName}`; // 默认值设为0,您可以根据需要修改
  311. }
  312. }
  313. }
  314. /* console.log("识别的变量:", variables); // 添加日志 */
  315. // 定义一个安全的评估函数
  316. const safeEval = (formula, variables) => {
  317. // 创建一个新的函数,将数学函数和变量作为参数传入
  318. const func = new Function(
  319. ...Object.keys(mathFunctions),
  320. ...Object.keys(variables),
  321. `return ${formula}`
  322. );
  323. // 执行函数,传入数学函数和变量作为参数
  324. return func(
  325. ...Object.values(mathFunctions),
  326. ...Object.values(variables)
  327. );
  328. };
  329. try {
  330. const result = safeEval(formual, variables);
  331. /* console.log("计算结果:", result); // 添加日志 */
  332. let formattedResult;
  333. if (typeof result === "number" && !isNaN(result)) {
  334. formattedResult = result.toFixed(point);
  335. } else if (typeof result === "boolean") {
  336. formattedResult = result ? "1" : "0";
  337. } else {
  338. formattedResult = String(result);
  339. }
  340. _this.com.attrs[i].content = formattedResult;
  341. data = data.replace(
  342. "{{" + _this.com.attrs[i].id + "}}",
  343. formattedResult
  344. );
  345. } catch (error) {
  346. console.error("处理公式时出错:", error);
  347. console.error("错误的公式:", formual);
  348. _this.com.attrs[i].content = "计算错误";
  349. data = data.replace(
  350. "{{" + _this.com.attrs[i].id + "}}",
  351. "计算错误"
  352. );
  353. }
  354. /* let formual = await _this.analysisFormual(_this.com.attrs[i]);
  355. formual = await _this.getRemote(formual);
  356. // 使用 try-catch 来处理可能的计算错误
  357. try {
  358. let result = eval(formual);
  359. data = data.replace("{{" + attrId + "}}", result);
  360. } catch (error) {
  361. data = data.replace("{{" + attrId + "}}", "计算错误");
  362. } */
  363. } else if (_this.com.attrs[i].type == "sourceData") {
  364. let result = await _this.getRemote1(_this.com.attrs[i].formula);
  365. data = data.replace("{{" + attrId + "}}", result);
  366. } else if (_this.com.attrs[i].type == "Directory") {
  367. const directoryContent = _this.com.attrs[i].content;
  368. const level = _this.com.attrs[i].level || 1; // 默认为1级目录
  369. const attrId = _this.com.attrs[i].id;
  370. // 创建一个更灵活的正则表达式模式,匹配可能存在的div包裹
  371. const directoryRegex = new RegExp(
  372. `<div[^>]*>\\s*{{\\s*${attrId}\\s*}}\\s*</div>|{{\\s*${attrId}\\s*}}`,
  373. "g"
  374. );
  375. // 替换内容,直接生成标题标签
  376. data = data.replace(directoryRegex, (match, offset, string) => {
  377. // 根据级别创建适当的HTML标签
  378. const tag = `h${Math.min(level, 6)}`; // 限制最大级别为h6
  379. const className = `directory-level-${level}`;
  380. return `<${tag} class="${className}">${directoryContent}</${tag}>`;
  381. });
  382. //处理目录
  383. /* data = data.replace(
  384. "<p>{{" + attrId + "}}</p>",
  385. "<h1>" + _this.com.attrs[i].content + "</h1>"
  386. );
  387. data = data.replace(
  388. "<div>{{" + attrId + "}}</div>",
  389. "<h1>" + _this.com.attrs[i].content + "</h1>"
  390. );
  391. data = data.replace(
  392. "<span>{{" + attrId + "}}</span>",
  393. "<h1>" + _this.com.attrs[i].content + "</h1>"
  394. ); */
  395. //处理目录
  396. /* const directoryContent = _this.com.attrs[i].content;
  397. const directoryRegex = new RegExp(
  398. `<(p|div|span)>{{${attrId}}}</(p|div|span)>`,
  399. "g"
  400. );
  401. data = data.replace(directoryRegex, `<h1>${directoryContent}</h1>`); */
  402. } else {
  403. //处理常量及其它
  404. data = data.replace("{{" + attrId + "}}", _this.com.attrs[i].content);
  405. }
  406. }
  407. this.$nextTick(() => {
  408. this.addInputListeners();
  409. this.initializeInputWidths();
  410. this.addVariableNullListeners();
  411. });
  412. return data;
  413. },
  414. // 新增方法:为 variableNull 类型的输入框添加事件监听器
  415. addVariableNullListeners() {
  416. this.$el.addEventListener("input", this.handleVariableNullInput);
  417. this.$el.addEventListener("blur", this.handleVariableNullBlur, true);
  418. },
  419. handleVariableNullInput(event) {
  420. if (event.target.classList.contains("text-input-boxs")) {
  421. const attrId = event.target.dataset.attrId;
  422. const newValue = event.target.value;
  423. // 只更新特定输入框的值
  424. this.$set(this.variableNullInputs, attrId, newValue);
  425. }
  426. },
  427. handleVariableNullBlur(event) {
  428. if (event.target.classList.contains("text-input-boxs")) {
  429. const index = parseInt(event.target.dataset.index, 10);
  430. const attrId = event.target.dataset.attrId;
  431. const newValue = this.variableNullInputs[attrId];
  432. // 只更新特定属性的内容
  433. this.updateVariableNullContent(index, attrId, newValue, true);
  434. }
  435. },
  436. updateVariableNullContent(index, attrId, newValue, emitEvent = false) {
  437. if (this.com && this.com.attrs && this.com.attrs[index]) {
  438. if (this.com.attrs[index].id === attrId) {
  439. // 只更新特定属性的内容
  440. this.$set(this.com.attrs[index], "content", newValue);
  441. // 更新输入框的值
  442. this.$nextTick(() => {
  443. const input = this.$el.querySelector(`#${attrId}`);
  444. if (input) {
  445. input.value = newValue;
  446. }
  447. });
  448. // 触发事件
  449. if (emitEvent) {
  450. this.$emit("onUpdata", this.currentIndex, index, attrId, newValue);
  451. }
  452. }
  453. }
  454. },
  455. addInputListeners() {
  456. // 使用事件委托来处理所有的 .text-input-box 元素
  457. this.$el.addEventListener("blur", this.handleInputChange, true);
  458. this.$el.addEventListener("input", this.adjustInputWidth, true);
  459. },
  460. initializeInputWidths() {
  461. const inputs = this.$el.querySelectorAll(
  462. ".text-input-box, .text-input-boxs"
  463. );
  464. inputs.forEach((input) => this.adjustInputWidth({ target: input }));
  465. },
  466. // 修改这个方法,使其同时处理 variableNull 类型的输入框
  467. adjustInputWidth(event) {
  468. if (
  469. event.target.classList.contains("text-input-box") ||
  470. event.target.classList.contains("text-input-boxs")
  471. ) {
  472. const input = event.target;
  473. const sizeCalculator = document.createElement("span");
  474. sizeCalculator.className = "size-calculator";
  475. sizeCalculator.textContent = input.value || input.placeholder || "0";
  476. document.body.appendChild(sizeCalculator);
  477. const styles = window.getComputedStyle(input);
  478. sizeCalculator.style.font = styles.font;
  479. sizeCalculator.style.fontSize = styles.fontSize;
  480. sizeCalculator.style.fontWeight = styles.fontWeight;
  481. sizeCalculator.style.letterSpacing = styles.letterSpacing;
  482. const width = sizeCalculator.offsetWidth;
  483. input.style.width = `${width + 10}px`; // Add some padding
  484. document.body.removeChild(sizeCalculator);
  485. }
  486. },
  487. // 修改这个方法,使其同时处理 variableNull 类型的输入框
  488. handleInputChange(event) {
  489. if (
  490. event.target.classList.contains("text-input-box") ||
  491. event.target.classList.contains("text-input-boxs")
  492. ) {
  493. const attrId = event.target.id;
  494. const attrName = event.target.name;
  495. const dataIndex = parseInt(event.target.dataset.index, 10);
  496. const newValue = event.target.value;
  497. // 更新数据
  498. if (this.com && this.com.attrs && this.com.attrs[dataIndex]) {
  499. this.$set(this.com.attrs[dataIndex], "content", newValue);
  500. this.com.attrs.forEach((el) => {
  501. if (el.name === attrName) {
  502. this.$set(el, "content", newValue);
  503. }
  504. });
  505. this.$emit("onUpdateAttr", this.currentIndex, dataIndex, newValue);
  506. } else {
  507. console.warn(`Unable to find attribute at index ${dataIndex}`);
  508. }
  509. }
  510. },
  511. bindEvents() {
  512. let _this = this;
  513. for (var i = 0; i < _this.com.attrs.length; i++) {
  514. let attrId = _this.com.attrs[i].id;
  515. let item = _this.com.attrs[i];
  516. if (_this.com.attrs[i].type == "variable") {
  517. //变量事件
  518. let input = _this.$el.querySelector("#" + attrId);
  519. if (input) {
  520. if (item.data.value_type == 2) {
  521. //下拉选择
  522. input.addEventListener("change", _this.handleInput);
  523. } else {
  524. //文本输入
  525. input.addEventListener("blur", _this.handleInput);
  526. }
  527. }
  528. } else if (_this.com.attrs[i].type == "ProductAttr") {
  529. //绑定产品属性事件
  530. let prodAttrId = item.id + "_" + i;
  531. let input = _this.$el.querySelector("#" + prodAttrId);
  532. if (input) {
  533. if (item.attrs.type == 1) {
  534. input.addEventListener("blur", _this.handleInputProduct);
  535. } else {
  536. input.addEventListener("change", _this.handleChangeProduct);
  537. }
  538. }
  539. }
  540. }
  541. },
  542. //分析公式
  543. async analysisFormual(attrs) {
  544. let _this = this;
  545. /* console.log("开始分析公式:", attrs.formula); */
  546. let formual = attrs.formula;
  547. // 处理 [T][...][...] 引用
  548. const tPattern = /\[T\]\[(.*?)\]\[(.*?)\]/g;
  549. formual = await this.replaceAsync(
  550. formual,
  551. tPattern,
  552. async (match, p1, p2) => {
  553. try {
  554. let data = await _this.getModuleData(p1, p2);
  555. /* console.log(`获取到的数据: ${match} = ${data}`); */
  556. if (data === null || data === undefined) {
  557. console.warn(`获取到的数据无效: ${match}`);
  558. return "''"; // 返回空字符串而不是 0
  559. } else {
  560. return typeof data === "string" ? `${data}` : data;
  561. }
  562. } catch (error) {
  563. console.error(`处理 ${match} 时出错:`, error);
  564. return "''";
  565. }
  566. }
  567. );
  568. // 处理嵌套的 IF 语句
  569. const ifPattern = /IF\s*\((.*?),(.*?),(.*?)\)/gi;
  570. let depth = 0;
  571. while (formual.match(ifPattern) && depth < 10) {
  572. formual = formual.replace(
  573. ifPattern,
  574. (match, condition, trueValue, falseValue) => {
  575. // 递归处理嵌套的 IF 语句
  576. if (
  577. trueValue.includes("IF(") ||
  578. falseValue.includes("IF(") ||
  579. trueValue.includes("if(") ||
  580. falseValue.includes("if(")
  581. ) {
  582. return `(${condition} ? (${trueValue}) : (${falseValue}))`;
  583. }
  584. return `(${condition} ? ${trueValue} : ${falseValue})`;
  585. }
  586. );
  587. depth++;
  588. }
  589. /* console.log("分析后的公式:", formual); */
  590. return formual;
  591. },
  592. // 辅助方法:异步替换
  593. async replaceAsync(str, regex, asyncFn) {
  594. const promises = [];
  595. str.replace(regex, (match, ...args) => {
  596. const promise = asyncFn(match, ...args);
  597. promises.push(promise);
  598. });
  599. const data = await Promise.all(promises);
  600. return str.replace(regex, () => data.shift());
  601. },
  602. // 辅助方法:异步替换
  603. async replaceAsync(str, regex, asyncFn) {
  604. const promises = [];
  605. str.replace(regex, (match, ...args) => {
  606. const promise = asyncFn(match, ...args);
  607. promises.push(promise);
  608. });
  609. const data = await Promise.all(promises);
  610. return str.replace(regex, () => data.shift());
  611. },
  612. async getFormualData(formualItem, data) {
  613. let _this = this;
  614. // console.log("开始获取公式数据:", formualItem);
  615. var pattern = /\[(.*?)\]\[(.*?)\]\[(.*?)\]/;
  616. var reg = new RegExp(pattern);
  617. let items = formualItem.match(reg);
  618. // console.log("解析结果:", items);
  619. let calResult = 0;
  620. if (items && items[1] == "T") {
  621. calResult = await _this.getModuleData(items[2], items[3]);
  622. } else {
  623. console.warn("无法解析公式项:", formualItem);
  624. }
  625. /* console.log("计算结果:", formualItem,data); */
  626. return calResult;
  627. },
  628. async getRemote(formual) {
  629. let _this = this;
  630. var pattern = /\[(.*?)\]\[(.*?)\]\[(.*?)\]\[(.*?)\]/;
  631. var reg = new RegExp(pattern);
  632. while (true) {
  633. let items = formual.match(reg);
  634. if (items == null) break;
  635. if (items[1] == "R") {
  636. //表示获取远程数据
  637. var dataIndex = items[4];
  638. dataIndex = items[4].split(",");
  639. let calResult = await _this.getRemoteData(
  640. items[2],
  641. items[3],
  642. dataIndex[0],
  643. dataIndex[1]
  644. );
  645. let itemName = items[0];
  646. // 检查calResult是否为数字
  647. if (!isNaN(calResult)) {
  648. formual = formual.replace(
  649. itemName,
  650. "(" + parseFloat(calResult) + ")"
  651. );
  652. } else {
  653. formual = formual.replace(itemName, `"${calResult}"`);
  654. }
  655. formual = formual.replace(
  656. itemName,
  657. "(" + parseFloat(calResult) + ")"
  658. );
  659. }
  660. }
  661. return formual;
  662. },
  663. async getRemote1(formual) {
  664. let _this = this;
  665. var pattern = /\[(.*?)\]\[(.*?)\]\[(.*?)\]\[(.*?)\]/;
  666. var reg = new RegExp(pattern);
  667. while (true) {
  668. let items = formual.match(reg);
  669. if (items == null) break;
  670. if (items[1] == "R") {
  671. //表示获取远程数据
  672. var dataIndex = items[4];
  673. dataIndex = items[4].split(",");
  674. let calResult = await _this.getRemoteData(
  675. items[2],
  676. items[3],
  677. dataIndex[0],
  678. dataIndex[1]
  679. );
  680. let itemName = items[0];
  681. // 检查calResult是否为数字
  682. if (!isNaN(calResult)) {
  683. formual = formual.replace(
  684. itemName,
  685. "(" + parseFloat(calResult) + ")"
  686. );
  687. } else {
  688. formual = formual.replace(itemName, `"${calResult}"`);
  689. }
  690. /* formual=formual.replace(itemName,parseFloat(calResult)); */
  691. }
  692. }
  693. return formual;
  694. },
  695. onFocus() {
  696. this.isEditing = true;
  697. },
  698. onBlur() {
  699. this.isEditing = false;
  700. },
  701. async getRemoteData(code, sheet, row, col) {
  702. let _this = this;
  703. let result = 0;
  704. let par = {
  705. code: code,
  706. sheetName: sheet,
  707. row: row,
  708. col: col,
  709. };
  710. /* if (!this.isEditing) {
  711. this.loading = true;
  712. this.progress = 0;
  713. try {
  714. const updateProgress = setInterval(() => {
  715. if (this.progress < 90) {
  716. this.progress += 10;
  717. }
  718. }, 100);
  719. let res = await findData(par);
  720. if (res.status == 200) {
  721. result = res.data.result;
  722. }
  723. this.progress = 100;
  724. clearInterval(updateProgress);
  725. } catch (error) {
  726. console.error("Error fetching remote data:", error);
  727. } finally {
  728. setTimeout(() => {
  729. this.loading = false;
  730. this.progress = 0;
  731. }, 1000);
  732. }
  733. } else { */
  734. // 如果正在编辑,直接获取数据而不显示进度条
  735. try {
  736. let res = await findData(par);
  737. if (res.status == 200) {
  738. result = res.data.result;
  739. // 添加数据类型检查和转换
  740. if (typeof result === "string" && !isNaN(result)) {
  741. result = parseFloat(result);
  742. }
  743. }
  744. } catch (error) {
  745. console.error("获取远程数据错误:", error);
  746. }
  747. /* } */
  748. return result;
  749. },
  750. //获取本地文档模块数据
  751. async getModuleData(code, attrName) {
  752. let _this = this;
  753. /* console.log(`尝试获取模块数据: code=${code}, attrName=${attrName}`);
  754. console.log("当前 coms:", _this.coms); */
  755. let item = _this.coms.filter((o) => o.name == code);
  756. /* console.log(`找到的模块:`, item); */
  757. if (item.length > 0) {
  758. let attr = item[0].attrs.filter((o) => o.name == attrName);
  759. /* console.log(`找到的属性:`, attr); */
  760. if (attr.length > 0) {
  761. /* console.log(`返回的值:`, attr[0].content); */
  762. return attr[0].content; // 返回原始值,不进行转换
  763. }
  764. }
  765. /* console.log("未找到匹配的模块或属性,返回空字符串"); */
  766. return ""; // 返回空字符串而不是 0
  767. },
  768. handleChangeProduct(e) {
  769. let dataIndex = e.target.dataset.index;
  770. this.$emit(
  771. "onUpdateProdAttr",
  772. this.currentIndex,
  773. dataIndex,
  774. e.target.value
  775. );
  776. },
  777. handleInputProduct(e) {
  778. let dataIndex = e.target.dataset.index;
  779. this.$emit(
  780. "onUpdateProdAttr",
  781. this.currentIndex,
  782. dataIndex,
  783. e.target.value
  784. );
  785. },
  786. handleInput(e) {
  787. let dataIndex = e.target.dataset.index;
  788. this.$emit("onUpdateAttr", this.currentIndex, dataIndex, e.target.value);
  789. },
  790. onEditorReady(editor) {},
  791. onInputText(e) {
  792. this.$emit("onUpdate", this.currentIndex, e);
  793. },
  794. /* 点击替换图片 */
  795. handleImageClick(event) {
  796. if (event.target.tagName === "IMG") {
  797. this.replaceImage(event.target);
  798. }
  799. },
  800. selectImage() {
  801. return new Promise((resolve) => {
  802. const input = document.createElement("input");
  803. input.type = "file";
  804. input.accept = "image/*";
  805. input.onchange = (e) => resolve(e.target.files[0]);
  806. input.click();
  807. });
  808. },
  809. async uploadImage(formData) {
  810. try {
  811. const response = await axios.post(
  812. "http://58.246.234.210:8084/upload/image",
  813. formData,
  814. {
  815. headers: {
  816. "Content-Type": "multipart/form-data",
  817. },
  818. }
  819. );
  820. console.log("Upload response:", response);
  821. if (response.status === 200 && response.data && response.data.url) {
  822. return response.data.url;
  823. } else {
  824. throw new Error("Invalid upload response");
  825. }
  826. } catch (error) {
  827. console.error("Error uploading image:", error);
  828. throw error;
  829. }
  830. },
  831. /* updateContentWithNewImage(imgElement) {
  832. // 更新 content 中的图片 URL
  833. const tempDiv = document.createElement('div');
  834. tempDiv.innerHTML = this.content;
  835. const images = tempDiv.getElementsByTagName('img');
  836. for (let img of images) {
  837. if (img.src === imgElement.src) {
  838. img.src = imgElement.src;
  839. break;
  840. }
  841. }
  842. this.content = tempDiv.innerHTML;
  843. this.$emit('onUpdate', this.currentIndex, this.content);
  844. }, */
  845. async replaceImage(imgElement) {
  846. try {
  847. const file = await this.selectImage();
  848. if (file) {
  849. const formData = new FormData();
  850. formData.append("upload", file);
  851. const uploadedUrl = await this.uploadImage(formData);
  852. // Update the image element in the DOM
  853. imgElement.src = uploadedUrl;
  854. // Update the content and notify parent component
  855. this.$nextTick(() => {
  856. this.updateContentWithNewImage(imgElement, uploadedUrl);
  857. });
  858. }
  859. } catch (error) {
  860. console.error("Error replacing image:", error);
  861. }
  862. },
  863. updateContentWithNewImage(imgElement, newImageUrl) {
  864. // Get the rich-editor div
  865. const richEditor = this.$refs.richEditor;
  866. // Create a new Image element with the new URL
  867. const newImg = document.createElement("img");
  868. newImg.src = newImageUrl;
  869. // Copy attributes from the old image to the new one
  870. for (let attr of imgElement.attributes) {
  871. if (attr.name !== "src") {
  872. newImg.setAttribute(attr.name, attr.value);
  873. }
  874. }
  875. // Replace the old image with the new one
  876. imgElement.parentNode.replaceChild(newImg, imgElement);
  877. // Update this.content with the latest DOM content
  878. this.content = richEditor.innerHTML;
  879. // Update the com object
  880. this.com.content = this.content;
  881. // Emit an event to update the parent component
  882. this.$emit("updateComContent", this.currentIndex, this.com);
  883. },
  884. /* updateComsData(newImageUrl) {
  885. // 找到当前组件在 coms 数组中的索引
  886. const comIndex = this.coms.findIndex((com) => com.name === this.com.name);
  887. if (comIndex !== -1) {
  888. // 创建 coms 的深拷贝
  889. const updatedComs = JSON.parse(JSON.stringify(this.coms));
  890. // 更新特定组件的 content
  891. updatedComs[comIndex].content = this.content;
  892. // 如果有特定的图片属性,也更新它
  893. const imgAttr = updatedComs[comIndex].attrs.find(
  894. (attr) => attr.type === "image"
  895. );
  896. if (imgAttr) {
  897. imgAttr.content = newImageUrl;
  898. }
  899. // 更新 coms
  900. this.$emit("update:coms", updatedComs);
  901. }
  902. }, */
  903. /* onFocus(e) {
  904. // var range = e.editor.getSelection().getRanges()[0];
  905. // range.collapse( true );
  906. // range.setStartAt( e.editor.editable(), CKEDITOR.POSITION_AFTER_START );
  907. // e.editor.insertText("AAA");
  908. },
  909. onBlur(e) {
  910. //// console.log(' onBlur e',e);
  911. // this.$emit("onUploadAttr",this.dataIndex,this.dataAttr);
  912. }, */
  913. },
  914. };
  915. </script>
  916. <style lang="scss" scoped>
  917. @import "./index.scss";
  918. .template-textarea {
  919. position: relative;
  920. }
  921. .overlay {
  922. position: absolute;
  923. top: 0;
  924. left: 0;
  925. width: 100%;
  926. height: 100%;
  927. background: rgba(0, 0, 0, 0.5); // Semi-transparent background
  928. display: flex;
  929. align-items: center;
  930. justify-content: center;
  931. z-index: 10; // Ensure it is above other content
  932. }
  933. .full-width-progress {
  934. width: 80%; // Adjust as needed
  935. color: inherit; // Ensure the color remains unchanged
  936. }
  937. .auto-width-wrapper {
  938. display: inline-block;
  939. position: relative;
  940. }
  941. .size-calculator {
  942. position: absolute;
  943. top: -9999px;
  944. left: -9999px;
  945. white-space: pre;
  946. font-family: inherit;
  947. font-size: inherit;
  948. font-weight: inherit;
  949. padding: 2px 4px;
  950. }
  951. .editor-wrapper {
  952. position: relative;
  953. }
  954. .sticky-toolbar {
  955. position: sticky;
  956. top: 0;
  957. z-index: 1000;
  958. background-color: #fff;
  959. border-bottom: 1px solid #d1d1d1;
  960. }
  961. .editor-area {
  962. ::v-deep .cke_top {
  963. display: block; // Hide the original toolbar
  964. }
  965. }
  966. ::v-deep .directory-level-1 {
  967. font-size: 24px;
  968. font-weight: bold;
  969. margin-left: 0;
  970. }
  971. ::v-deep .directory-level-2 {
  972. font-size: 20px;
  973. font-weight: bold;
  974. margin-left: 20px;
  975. }
  976. ::v-deep .directory-level-3 {
  977. font-size: 18px;
  978. font-weight: bold;
  979. margin-left: 40px;
  980. }
  981. ::v-deep .directory-level-4 {
  982. font-size: 16px;
  983. font-weight: bold;
  984. margin-left: 60px;
  985. }
  986. ::v-deep .directory-level-5 {
  987. font-size: 14px;
  988. font-weight: bold;
  989. margin-left: 80px;
  990. }
  991. ::v-deep .directory-level-6 {
  992. font-size: 12px;
  993. font-weight: normal;
  994. margin-left: 100px;
  995. }
  996. </style>