table-form-write.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  1. <template>
  2. <div class="excelHtnl">
  3. <div
  4. class="excelBox hc-excel-table-form"
  5. style="margin-top:40px;height: 100%;"
  6. @click="parentClick($event)"
  7. >
  8. <div style="width:100%;height: 100%;overflow: scroll;" class='parent' :id="containerId"></div>
  9. </div>
  10. </div>
  11. </template>
  12. <script>
  13. import Vue from 'vue'
  14. import { getExcelHtml } from '@/api/exctab/excelmodel'
  15. export default {
  16. props: [
  17. 'pkeyId',
  18. 'initTableName',
  19. 'selectedTableKey',
  20. 'containerId',
  21. 'multipleSelect' // 是否允许多选,默认为false
  22. ],
  23. data() {
  24. return {
  25. selectedElements: [],
  26. exHtml: '',
  27. currentComponent: null
  28. }
  29. },
  30. created() {
  31. if (!this.containerId) {
  32. this.containerId = `excel-container-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
  33. }
  34. },
  35. watch: {
  36. pkeyId: {
  37. immediate: true,
  38. deep: true,
  39. handler(newVal) {
  40. if (newVal) {
  41. this.exHtml = ''
  42. this.getExcelHtml(newVal)
  43. } else {
  44. this.exHtml = ''
  45. this.clearForm()
  46. }
  47. }
  48. },
  49. selectedTableKey: {
  50. handler(newKey) {
  51. this.clearAllSelected();
  52. if (newKey && newKey.startsWith('key_')) {
  53. this.highlightElementsByKey(newKey);
  54. }
  55. },
  56. immediate: true
  57. },
  58. containerId: {
  59. handler(newVal) {
  60. this.clearForm()
  61. this.selectedElements = [];
  62. },
  63. immediate: true
  64. },
  65. multipleSelect: {
  66. handler(newVal) {
  67. // 当多选模式改变时,清空当前选择
  68. if (!newVal && this.selectedElements.length > 1) {
  69. // 如果关闭多选且当前有多个选中,只保留最后一个
  70. const lastElement = this.selectedElements[this.selectedElements.length - 1];
  71. this.clearAllSelected();
  72. this.selectElements([lastElement]);
  73. }
  74. },
  75. immediate: true
  76. }
  77. },
  78. methods: {
  79. // 获取当前组件容器元素,封装为工具方法
  80. getContainer() {
  81. return document.getElementById(this.containerId);
  82. },
  83. // 获取当前所有有橙色背景且去重的元素列表
  84. getCurrentSelectedElements() {
  85. const container = this.getContainer();
  86. if (!container) return [];
  87. // 获取所有有橙色背景的元素
  88. const orangeElements = container.querySelectorAll('[style*="background-color: rgb(255, 165, 0)"], [style*="background-color:#ffa500"]');
  89. // 根据 tableElementKey 去重
  90. const uniqueElements = [];
  91. const seenKeys = new Set();
  92. orangeElements.forEach(el => {
  93. if (el.id) {
  94. const tableElementKey = this.initTableName + ':' + this.extractKeyPrefix(el.id);
  95. if (!seenKeys.has(tableElementKey)) {
  96. seenKeys.add(tableElementKey);
  97. const keyName = el.placeholder || '';
  98. uniqueElements.push({
  99. tableElementKey: tableElementKey,
  100. eName: keyName,
  101. id: el.id
  102. });
  103. }
  104. }
  105. });
  106. return uniqueElements;
  107. },
  108. highlightElementsByKey(key) {
  109. if (!key) return;
  110. const container = this.getContainer();
  111. if (!container) return;
  112. const keyPrefix = key.split('__')[0];
  113. // 只在当前容器内查找元素
  114. const samePrefixElements = container.querySelectorAll(`[id^="${keyPrefix}__"]`);
  115. if (samePrefixElements.length === 0) {
  116. const element = container.querySelector(`#${key}`);
  117. if (element) {
  118. this.selectElements([element]);
  119. return;
  120. }
  121. }
  122. this.selectElements(samePrefixElements);
  123. },
  124. async getExcelHtml(pkeyId) {
  125. this.clearForm();
  126. try {
  127. const { data: res } = await getExcelHtml({ pkeyId })
  128. if (res.code === 200) {
  129. this.exHtml = res.data
  130. this.cop();
  131. }
  132. } catch (error) {
  133. console.error('获取HTML失败:', error)
  134. }
  135. },
  136. clearForm() {
  137. const container = this.getContainer();
  138. if (container) {
  139. container.innerHTML = '';
  140. }
  141. this.clearAllSelected();
  142. },
  143. async cop() {
  144. const container = this.getContainer();
  145. if (!container) {
  146. console.error('父容器不存在:', this.containerId);
  147. return;
  148. }
  149. try {
  150. const MyComponent = Vue.extend({
  151. template: this.exHtml,
  152. data() {
  153. return {
  154. formData: {},
  155. getTokenHeader: {},
  156. dap_site_data: {}
  157. }
  158. },
  159. methods: {
  160. contextmenuClick() {},
  161. getInformation() {},
  162. formUploadSuccess() {},
  163. formUploadExceed() {},
  164. formUploadLoading() {},
  165. delTableFormFile() {},
  166. formUploadError() {},
  167. uploadprogress() {},
  168. formRemoteMethod() {},
  169. getRegularExpression() {},
  170. checkboxGroupChange() {},
  171. formRemoteChange() {},
  172. dateKeydown() {},
  173. keyupShiftUp() {},
  174. keyupShiftDown() {},
  175. keyupShiftLeft() {},
  176. keyupShiftRight() {},
  177. inputLeftClick() {},
  178. }
  179. });
  180. // 销毁旧组件
  181. if (this.currentComponent) {
  182. this.currentComponent.$destroy();
  183. }
  184. // 创建新组件
  185. this.currentComponent = new MyComponent().$mount();
  186. // 添加到当前容器
  187. container.innerHTML = '';
  188. container.appendChild(this.currentComponent.$el);
  189. this.$nextTick(() => {
  190. if (this.selectedTableKey) {
  191. this.highlightElementsByKey(this.selectedTableKey);
  192. }
  193. });
  194. } catch (error) {
  195. console.error('组件渲染失败:', error);
  196. }
  197. },
  198. async parentClick(e) {
  199. e.stopPropagation(); // 阻止事件冒泡到其他组件
  200. const container = this.getContainer();
  201. if (!container || !container.contains(e.target)) {
  202. return; // 不是当前组件内的点击,不处理
  203. }
  204. // 检测Control键是否按下 (e.ctrlKey 对Windows/Linux有效,e.metaKey对Mac的Command键有效)
  205. const isCtrlPressed = e.ctrlKey || e.metaKey;
  206. // 只有在multipleSelect为true时才允许通过Control键进行多选
  207. const isMultiSelectMode = this.multipleSelect && isCtrlPressed;
  208. // 只在当前容器内查找目标元素
  209. let target = e.target;
  210. while (target && target !== container && !target.id) {
  211. target = target.parentNode;
  212. }
  213. if (!target || !target.id || !container.contains(target)) {
  214. return;
  215. }
  216. // 检查警告样式
  217. let hasWarnStyle = false;
  218. if (target.classList.contains('warnstyle') ||
  219. (target.parentNode && target.parentNode.classList.contains('warnstyle'))) {
  220. hasWarnStyle = true;
  221. }
  222. if (hasWarnStyle) {
  223. this.$message({
  224. type: "warning",
  225. message: "当前位置未配置元素,请重新选择或配置元素后再试"
  226. });
  227. return;
  228. }
  229. const id = target.id;
  230. const elementId = this.initTableName + ':' + this.extractKeyPrefix(id);
  231. // 检查是否已经有相同 tableElementKey 的元素被选中
  232. const isSameKeySelected = this.selectedElements.some(item =>
  233. item.tableElementKey === elementId
  234. );
  235. // 根据是否按下Control键决定是否清空已选元素
  236. if (!isMultiSelectMode) {
  237. this.clearAllSelected();
  238. }
  239. if (!isSameKeySelected) {
  240. if (id.startsWith('key_')) {
  241. const keyPrefix = this.extractKeyPrefix(id);
  242. // 只在当前容器内查找同前缀元素
  243. const samePrefixElements = container.querySelectorAll(`[id^="${keyPrefix}__"]`);
  244. this.selectElements(samePrefixElements);
  245. } else {
  246. this.handleNormalElement(target, id);
  247. }
  248. } else {
  249. // 如果已经有相同 tableElementKey 的元素被选中,在多选模式下点击会取消选择所有相同key的元素
  250. if (isMultiSelectMode) {
  251. this.deselectElementsByKey(elementId);
  252. }
  253. }
  254. },
  255. extractKeyPrefix(id) {
  256. const parts = id.split('__');
  257. return parts.length > 0 ? parts[0] : id;
  258. },
  259. selectElements(elements) {
  260. const container = this.getContainer();
  261. if (!container) return;
  262. console.log('Selecting elements:', elements, container);
  263. // 先取消选择所有相同 tableElementKey 的元素(避免重复)
  264. if (elements.length > 0) {
  265. const firstElementId = this.initTableName + ':' + this.extractKeyPrefix(elements[0].id);
  266. this.deselectElementsByKey(firstElementId);
  267. }
  268. elements.forEach(el => {
  269. // 确保元素属于当前容器
  270. if (!container.contains(el)) return;
  271. const index = this.selectedElements.findIndex(item => item.id === el.id);
  272. if (index === -1) {
  273. el.style.backgroundColor = '#ffa500';
  274. const elementId = this.initTableName + ':' + this.extractKeyPrefix(el.id);
  275. const keyName = el.placeholder || '';
  276. const newElement = { tableElementKey: elementId, eName: keyName, id: el.id };
  277. this.selectedElements.push(newElement);
  278. // 传递当前所有有橙色背景且去重的元素
  279. const currentSelected = this.getCurrentSelectedElements();
  280. this.$emit('element-selected', newElement, true, currentSelected);
  281. }
  282. });
  283. },
  284. // 取消选择所有具有相同 tableElementKey 的元素
  285. deselectElementsByKey(tableElementKey) {
  286. const container = this.getContainer();
  287. if (!container) return;
  288. // 找出所有相同 tableElementKey 的元素
  289. const elementsToDeselect = this.selectedElements.filter(item =>
  290. item.tableElementKey === tableElementKey
  291. );
  292. if (elementsToDeselect.length === 0) return;
  293. // 清除样式并从选中列表中移除
  294. elementsToDeselect.forEach(item => {
  295. const element = container.querySelector(`#${item.id}`);
  296. if (element) {
  297. element.style.backgroundColor = '';
  298. }
  299. // 传递当前所有有橙色背景且去重的元素
  300. const currentSelected = this.getCurrentSelectedElements();
  301. this.$emit('element-selected', item, false, currentSelected);
  302. });
  303. // 从选中列表中移除这些元素
  304. this.selectedElements = this.selectedElements.filter(item =>
  305. item.tableElementKey !== tableElementKey
  306. );
  307. },
  308. handleNormalElement(target, id) {
  309. const container = this.getContainer();
  310. if (!container || !container.contains(target)) return;
  311. const elementId = this.initTableName + ':' + target.id.split('__')[0];
  312. const keyName = target.placeholder || '';
  313. if(!keyName){
  314. this.$message({
  315. type: "warning",
  316. message: "当前位置未配置元素,请重新选择或配置元素后再试"
  317. });
  318. return;
  319. }
  320. // 先取消选择相同 tableElementKey 的元素
  321. this.deselectElementsByKey(elementId);
  322. target.style.backgroundColor = '#ffa500';
  323. const newElement = { tableElementKey: elementId, eName: keyName, id };
  324. this.selectedElements.push(newElement);
  325. // 传递当前所有有橙色背景且去重的元素
  326. const currentSelected = this.getCurrentSelectedElements();
  327. this.$emit('element-selected', newElement, true, currentSelected);
  328. },
  329. clearAllSelected() {
  330. const container = this.getContainer();
  331. if (!container) return;
  332. this.selectedElements.forEach(item => {
  333. // 只清除当前容器内的元素样式
  334. const element = container.querySelector(`#${item.id}`);
  335. if (element) {
  336. element.style.backgroundColor = '';
  337. // 传递当前所有有橙色背景且去重的元素
  338. const currentSelected = this.getCurrentSelectedElements();
  339. this.$emit('element-selected', {
  340. tableElementKey: item.tableElementKey,
  341. eName: item.eName,
  342. id: item.id
  343. }, false, currentSelected);
  344. }
  345. });
  346. this.selectedElements = [];
  347. }
  348. }
  349. }
  350. </script>
  351. <style lang="scss" scoped>
  352. /* 样式保持不变 */
  353. .excelHtnl {
  354. margin: 0 0 0 10px;
  355. background-color: #fff;
  356. box-sizing: border-box;
  357. padding: 0 20px 100px 20px;
  358. height: 100%;
  359. }
  360. .hc-upload-table-form {
  361. position: relative;
  362. height: 100%;
  363. display: flex;
  364. justify-content: center;
  365. align-items: center;
  366. }
  367. .hc-upload-table-form .el-upload {
  368. position: relative;
  369. flex: 1;
  370. height: 100%;
  371. color: #ccc;
  372. }
  373. .hc-upload-table-form .el-upload .hc-table-form-icon {
  374. font-size: 24px;
  375. font-weight: 100;
  376. }
  377. .hc-upload-table-form .el-upload .hc-table-form-img {
  378. width: 100%;
  379. height: 100%;
  380. }
  381. .excelBox {
  382. ::v-deep .oldlace-bg {
  383. background-color: oldlace;
  384. }
  385. ::v-deep .select-td {
  386. border-width: 4px;
  387. border-color: #E6A23C;
  388. border-style: solid;
  389. }
  390. }
  391. ::v-deep .warnstyle .el-input__inner {
  392. background-image: url('/img/login/warn.png') !important;
  393. background-repeat: no-repeat;
  394. background-size: cover;
  395. background-position: center
  396. }
  397. ::v-deep .warnstyle .el-textarea__inner {
  398. background-image: url('/img/login/warn.png') !important;
  399. background-repeat: no-repeat;
  400. background-position-x: 45%;
  401. background-position-y: 46%;
  402. }
  403. </style>