customTable.vue 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. <!-- 搜索表单 -->
  2. <template>
  3. <div style="margin-top: 10px">
  4. <el-button type="primary" @click="addColumn()" v-if="edit"
  5. >新增列</el-button
  6. >
  7. <el-button type="primary" @click="addRow(columns.length)" v-if="edit"
  8. >新增行</el-button
  9. >
  10. <div class="table" style="margin-top: 10px" :id="id">
  11. <div class="table-body" style="display: flex">
  12. <template v-for="(row, index) in columns">
  13. <div class="column" :style="{ width: row[0].width + 'px' }">
  14. <div
  15. class="table-body-item tableTd"
  16. v-for="(item, rowIndex) in row"
  17. :style="{
  18. height: item.rowspan * 30 + 'px',
  19. display: item.rowspan ? 'block' : 'none'
  20. }"
  21. >
  22. <i
  23. class="el-icon-delete delete"
  24. style="display: none"
  25. @click="removeColumn(index)"
  26. v-if="edit && rowIndex == 0"
  27. ></i>
  28. <i
  29. class="el-icon-circle-plus-outline add"
  30. style="display: none"
  31. @click="addColumn(index)"
  32. v-if="edit && rowIndex == 0"
  33. ></i>
  34. <i
  35. class="el-icon-delete deleteRow"
  36. @click="removeRow(rowIndex)"
  37. v-if="edit && rowIndex != 0"
  38. ></i>
  39. <i
  40. class="el-icon-circle-plus-outline addRow"
  41. style="display: none"
  42. @click="addRow(rowIndex, index)"
  43. v-if="edit && rowIndex != 0 && index != columns.length - 1"
  44. ></i>
  45. <textarea
  46. v-if="item.rowspan > 1"
  47. v-model="item.value"
  48. class="templateInput"
  49. :id="item.id"
  50. :readonly="item.readonly == 2 || !edit"
  51. @click="inputClick(item)"
  52. @input="calculation"
  53. autocomplete="off"
  54. style="resize: none"
  55. ></textarea>
  56. <input
  57. v-else
  58. v-model="item.value"
  59. class="templateInput"
  60. :id="item.id"
  61. :readonly="item.readonly == 2 || !edit"
  62. @click="inputClick(item, rowIndex == 0 ? 'columns' : null)"
  63. @input="calculation"
  64. type="text"
  65. autocomplete="off"
  66. />
  67. </div>
  68. </div>
  69. </template>
  70. </div>
  71. </div>
  72. </div>
  73. </template>
  74. <script>
  75. import dictMixins from '@/mixins/dictMixins';
  76. import { generateRandomString } from '@/utils/util';
  77. export default {
  78. props: {},
  79. mixins: [dictMixins],
  80. props: {
  81. id: '',
  82. edit: {
  83. default: true,
  84. type: Boolean
  85. }
  86. },
  87. data() {
  88. return {
  89. form: null,
  90. valueObj: {},
  91. domId: '',
  92. rightClickShow: false,
  93. columns: [],
  94. // rows: [],
  95. equation: {}
  96. };
  97. },
  98. created() {},
  99. methods: {
  100. // 方法:添加新列
  101. addColumn(index) {
  102. let length = this.columns[0]?.length || 1;
  103. if (index === 0 || index) {
  104. this.columns.splice(
  105. index + 1,
  106. 0,
  107. Array(length)
  108. .fill(null)
  109. .map(() => this.getInput())
  110. );
  111. } else {
  112. this.columns.push(
  113. Array(length)
  114. .fill(null)
  115. .map(() => this.getInput())
  116. );
  117. }
  118. },
  119. getInput() {
  120. return {
  121. readonly: 1,
  122. value: '',
  123. rowspan: 1,
  124. id: generateRandomString(5)
  125. };
  126. },
  127. // 方法:删除指定列
  128. removeColumn(index) {
  129. this.columns.splice(index, 1);
  130. },
  131. // 方法:添加新行
  132. addRow(rowIndex, columnIndex) {
  133. if (columnIndex > 0) { //不是第一列则需要合并单元格
  134. this.columns.forEach((item, newColumnIndex) => {
  135. let newRow = this.getInput();
  136. if (newColumnIndex < columnIndex) {
  137. newRow.rowspan = 0;
  138. let _rowIndex = null;
  139. if (item[rowIndex].rowspan == 0) {
  140. _rowIndex = this.getPreventRowspan(newColumnIndex, rowIndex);
  141. }
  142. let rowspan =
  143. this.columns[newColumnIndex][_rowIndex || rowIndex].rowspan ||
  144. 1;
  145. this.$set(
  146. this.columns[newColumnIndex][_rowIndex || rowIndex],
  147. 'rowspan',
  148. (rowspan += 1)
  149. );
  150. }
  151. this.columns[newColumnIndex].splice(rowIndex + 1, 0, newRow);
  152. });
  153. } else {
  154. this.columns.forEach((item, index) => {
  155. let _rowIndex = rowIndex;
  156. if (item[rowIndex]?.rowspan > 1) {
  157. _rowIndex += Number(item[rowIndex].rowspan);
  158. }
  159. this.columns[index].splice(_rowIndex + 1, 0, this.getInput());
  160. });
  161. }
  162. },
  163. //找到真正需要改变rowspan的行
  164. getPreventRowspan(columnIndex, rowIndex) {
  165. let preventRowspan = null;
  166. this.columns[columnIndex].forEach((item, newRowIndex) => {
  167. if (newRowIndex < rowIndex && item.rowspan > 1) { //向上找到当前列合并过单元格的行
  168. preventRowspan = newRowIndex;
  169. }
  170. });
  171. return preventRowspan;
  172. },
  173. // 方法:删除指定行
  174. removeRow(rowIndex) {
  175. this.columns.forEach((item, columnIndex) => {
  176. if (item[rowIndex].rowspan == 0) { // 如果当前列,删除的这一行rowspan为0,则需要找到真正需要改变rowspan的行
  177. let preventRowspanIndex = this.getPreventRowspan(
  178. columnIndex,
  179. rowIndex
  180. );
  181. if (preventRowspanIndex) {
  182. this.$set(
  183. this.columns[columnIndex][preventRowspanIndex],
  184. 'rowspan',
  185. this.columns[columnIndex][preventRowspanIndex].rowspan - 1
  186. );
  187. }
  188. } else if (item[rowIndex].rowspan > 1) { //rowspan大于1,代表合并过单元格,需要吧值继承给下一行
  189. let data = item[rowIndex];
  190. data.rowspan--;
  191. this.$set(this.columns[columnIndex], [rowIndex + 1], data);
  192. }
  193. item.splice(rowIndex, 1);
  194. });
  195. },
  196. calculation() {
  197. this.$emit('calculation');
  198. },
  199. equationValue({ domId, value }) {
  200. this.columns.forEach((item, index) => {
  201. let cellIndex = item.findIndex((cell) => cell.id == domId);
  202. if (cellIndex != '-1') {
  203. this.$set(this.columns[index][cellIndex], 'value', value);
  204. }
  205. });
  206. },
  207. getValue() {
  208. return {
  209. form: null,
  210. equation: this.equation,
  211. valueObj: {
  212. columns: this.columns
  213. }
  214. };
  215. },
  216. init({ form, valueObj, equation }) {
  217. this.form = form;
  218. this.columns = valueObj.columns;
  219. this.equation = equation || {};
  220. },
  221. editInputChange(domObj) {
  222. if (domObj.equation) {
  223. this.equation[this.domId] = domObj.equation;
  224. }
  225. this.columns.forEach((item, index) => {
  226. let rowsIndex = item.findIndex((cells) => cells.id == this.domId);
  227. if (rowsIndex >= 0) {
  228. this.$set(this.columns[index], rowsIndex, domObj);
  229. }
  230. });
  231. },
  232. inputClick(item, type) {
  233. if (!this.edit) {
  234. return;
  235. }
  236. let dom = document.getElementById(item.id);
  237. this.domId = item.id;
  238. this.$emit('editShow', {
  239. templateDivRef: 'customTextRef' + this.id,
  240. domObj: {
  241. width: dom.parentElement.offsetWidth,
  242. isNoWidth: type == 'columns' ? false : true,
  243. id: item.id,
  244. readonly: item.readonly,
  245. value: item.value,
  246. rowspan: item.rowspan,
  247. equation: this.equation[item.id]
  248. }
  249. });
  250. }
  251. }
  252. };
  253. </script>
  254. <style lang="scss" scoped>
  255. :deep(.templateInput) {
  256. width: 100%;
  257. height: 100%;
  258. border: none;
  259. text-align: center;
  260. background-color: #fff;
  261. }
  262. .table-header {
  263. span {
  264. display: inline-block;
  265. height: 30px;
  266. border: 1px solid #ddd;
  267. width: 100px;
  268. background-color: #f2f2f2 !important;
  269. }
  270. input {
  271. background-color: #f2f2f2 !important;
  272. }
  273. }
  274. .table-body {
  275. white-space: nowrap;
  276. .column {
  277. display: inline-block;
  278. width: 100px;
  279. > div {
  280. height: 30px;
  281. border: 1px solid #ddd;
  282. width: 100%;
  283. }
  284. > div:nth-of-type(1) {
  285. background-color: #f2f2f2 !important;
  286. > input {
  287. background-color: #f2f2f2 !important;
  288. }
  289. }
  290. }
  291. }
  292. // th,
  293. // td {
  294. // border: 1px solid #ddd;
  295. // padding: 2px;
  296. // text-align: center;
  297. // height: 30px;
  298. // }
  299. // th {
  300. // background-color: #f2f2f2;
  301. // > input {
  302. // background-color: #f2f2f2 !important;
  303. // }
  304. // }
  305. tr:nth-child(even) {
  306. background-color: #f9f9f9;
  307. }
  308. input:focus {
  309. border-color: #66afe9; /* 改变边框颜色 */
  310. outline: none; /* 移除默认的轮廓线 */
  311. }
  312. .tableTd {
  313. position: relative;
  314. }
  315. .deleteRow {
  316. display: block !important;
  317. position: absolute;
  318. bottom: 0px;
  319. right: -15px;
  320. color: #f56c6c;
  321. }
  322. .tableTd:hover {
  323. .delete {
  324. display: block !important;
  325. position: absolute;
  326. right: 0;
  327. top: 0;
  328. color: #f56c6c;
  329. }
  330. .add {
  331. display: block !important;
  332. position: absolute;
  333. right: 20px;
  334. top: 0;
  335. color: #409eff;
  336. }
  337. .addRow {
  338. display: block !important;
  339. position: absolute;
  340. bottom: 0px;
  341. right: 10px;
  342. color: #409eff;
  343. }
  344. }
  345. </style>