TableForm.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. <template>
  2. <div class="_fc-table-form" :class="{'_fc-disabled': disabled}">
  3. <component :is="Form" :option="options" :rule="rule" :extendOption="true"
  4. :disabled="disabled"
  5. @change="formChange"
  6. v-model:api="fapi"
  7. @emit-event="$emit"></component>
  8. <el-button link type="primary" class="fc-clock" v-if="!max || max > this.trs.length"
  9. @click="addRaw(true)"><i class="fc-icon icon-add-circle" style="font-weight: 700;"></i>
  10. {{ formCreateInject.t('add') || '添加' }}
  11. </el-button>
  12. </div>
  13. </template>
  14. <script>
  15. import {markRaw, reactive} from 'vue';
  16. export default {
  17. name: 'TableForm',
  18. emits: ['change', 'add', 'delete', 'update:modelValue'],
  19. props: {
  20. formCreateInject: Object,
  21. modelValue: {
  22. type: Array,
  23. default: () => [],
  24. },
  25. columns: {
  26. type: Array,
  27. required: true,
  28. default: () => []
  29. },
  30. filterEmptyColumn: {
  31. type: Boolean,
  32. default: true,
  33. },
  34. options: {
  35. type: Object,
  36. default: () => reactive(({
  37. submitBtn: false,
  38. resetBtn: false,
  39. }))
  40. },
  41. max: Number,
  42. disabled: Boolean,
  43. },
  44. watch: {
  45. modelValue: {
  46. handler() {
  47. this.updateTable()
  48. },
  49. deep: true
  50. },
  51. 'formCreateInject.preview': function (n) {
  52. this.emptyRule.children[0].props.colspan = this.columns.length + (n ? 1 : 2);
  53. },
  54. },
  55. data() {
  56. return {
  57. rule: [],
  58. trs: [],
  59. fapi: {},
  60. Form: markRaw(this.formCreateInject.form.$form()),
  61. copyTrs: '',
  62. oldValue: '',
  63. emptyRule: {
  64. type: 'tr',
  65. _isEmpty: true,
  66. native: true,
  67. subRule: true,
  68. children: [
  69. {
  70. type: 'td',
  71. style: {
  72. textAlign: 'center',
  73. },
  74. native: true,
  75. subRule: true,
  76. props: {
  77. colspan: this.columns.length + (this.formCreateInject.preview ? 1 : 2),
  78. },
  79. children: [this.formCreateInject.t('dataEmpty') || '暂无数据']
  80. }
  81. ]
  82. },
  83. };
  84. },
  85. methods: {
  86. formChange() {
  87. this.updateValue();
  88. },
  89. updateValue() {
  90. const value = this.trs.map((tr, idx) => {
  91. return {
  92. ...(this.modelValue[idx] || {}),
  93. ...this.fapi.getChildrenFormData(tr)
  94. }
  95. }).filter(v => {
  96. if (!this.filterEmptyColumn) {
  97. return true;
  98. }
  99. if (v === undefined || v === null) {
  100. return false;
  101. }
  102. let flag = false;
  103. Object.keys(v).forEach(k => {
  104. flag = flag || (v[k] !== undefined && v[k] !== '' && v[k] !== null)
  105. })
  106. return flag;
  107. });
  108. const str = JSON.stringify(value);
  109. if (str !== this.oldValue) {
  110. this.oldValue = str;
  111. this.$emit('update:modelValue', value);
  112. this.$emit('change', value);
  113. }
  114. },
  115. setRawData(idx, formData) {
  116. const raw = this.trs[idx];
  117. this.fapi.setChildrenFormData(raw, formData, true);
  118. },
  119. updateTable() {
  120. const str = JSON.stringify(this.modelValue);
  121. if (this.oldValue === str) {
  122. return;
  123. }
  124. this.oldValue = str;
  125. this.trs = this.trs.splice(0, this.modelValue.length);
  126. if (!this.modelValue.length) {
  127. this.addEmpty();
  128. } else {
  129. this.clearEmpty();
  130. }
  131. this.modelValue.forEach((data, idx) => {
  132. if (!this.trs[idx]) {
  133. this.addRaw();
  134. }
  135. this.setRawData(idx, data || {});
  136. });
  137. this.rule[0].children[1].children = this.trs;
  138. },
  139. addEmpty() {
  140. if (this.trs.length) {
  141. this.trs.splice(0, this.trs.length);
  142. }
  143. this.trs.push(this.emptyRule);
  144. },
  145. clearEmpty() {
  146. if (this.trs[0] && this.trs[0]._isEmpty) {
  147. this.trs.splice(0, 1);
  148. }
  149. },
  150. delRaw(idx) {
  151. if (this.disabled) {
  152. return;
  153. }
  154. this.trs.splice(idx, 1);
  155. this.updateValue();
  156. if (this.trs.length) {
  157. this.trs.forEach(tr => this.updateRaw(tr));
  158. } else {
  159. this.addEmpty();
  160. }
  161. this.$emit('delete', idx);
  162. },
  163. addRaw(flag) {
  164. if (flag && this.disabled) {
  165. return;
  166. }
  167. const tr = this.formCreateInject.form.parseJson(this.copyTrs)[0];
  168. if (this.trs.length === 1 && this.trs[0]._isEmpty) {
  169. this.trs.splice(0, 1);
  170. }
  171. this.trs.push(tr);
  172. this.updateRaw(tr);
  173. if (flag) {
  174. this.$emit('add', this.trs.length);
  175. this.updateValue();
  176. }
  177. },
  178. updateRaw(tr) {
  179. const idx = this.trs.indexOf(tr);
  180. tr.children[0].props.innerText = idx + 1;
  181. tr.children[tr.children.length - 1].children[0].props.onClick = () => {
  182. this.delRaw(idx);
  183. };
  184. },
  185. loadRule() {
  186. const header = [{
  187. type: 'th',
  188. native: true,
  189. class: '_fc-tf-head-idx',
  190. props: {
  191. innerText: '#'
  192. }
  193. }];
  194. let body = [{
  195. type: 'td',
  196. class: '_fc-tf-idx',
  197. native: true,
  198. props: {
  199. innerText: '0'
  200. }
  201. }];
  202. this.columns.forEach((column) => {
  203. header.push({
  204. type: 'th',
  205. native: true,
  206. style: column.style,
  207. class: column.required ? '_fc-tf-head-required' : '',
  208. props: {
  209. innerText: column.label || ''
  210. }
  211. });
  212. body.push({
  213. type: 'td',
  214. native: true,
  215. children: [...(column.rule || [])]
  216. });
  217. });
  218. header.push({
  219. type: 'th',
  220. native: true,
  221. class: '_fc-tf-edit fc-clock',
  222. props: {
  223. innerText: this.formCreateInject.t('operation') || '操作'
  224. }
  225. });
  226. body.push({
  227. type: 'td',
  228. native: true,
  229. class: '_fc-tf-btn fc-clock',
  230. children: [
  231. {
  232. type: 'i',
  233. native: true,
  234. class: 'fc-icon icon-delete',
  235. props: {},
  236. }
  237. ],
  238. });
  239. this.copyTrs = this.formCreateInject.form.toJson([
  240. {
  241. type: 'tr',
  242. native: true,
  243. subRule: true,
  244. children: body
  245. }
  246. ]);
  247. this.rule = [
  248. {
  249. type: 'table',
  250. native: true,
  251. class: '_fc-tf-table',
  252. props: {
  253. border: '1',
  254. cellspacing: '0',
  255. cellpadding: '0',
  256. },
  257. children: [
  258. {
  259. type: 'thead',
  260. native: true,
  261. children: [
  262. {
  263. type: 'tr',
  264. native: true,
  265. children: header
  266. }
  267. ]
  268. },
  269. {
  270. type: 'tbody',
  271. native: true,
  272. children: this.trs
  273. }
  274. ]
  275. }
  276. ]
  277. },
  278. },
  279. created() {
  280. this.loadRule();
  281. },
  282. mounted() {
  283. this.updateTable();
  284. }
  285. };
  286. </script>
  287. <style>
  288. ._fc-table-form {
  289. overflow: auto;
  290. color: #666666;
  291. }
  292. ._fc-table-form .form-create .el-form-item {
  293. margin-bottom: 1px;
  294. }
  295. ._fc-table-form .form-create .el-form-item.is-error {
  296. margin-bottom: 22px;
  297. }
  298. ._fc-table-form .el-form-item__label, ._fc-table-form .van-field__label {
  299. display: none !important;
  300. }
  301. ._fc-table-form .el-form-item__content {
  302. display: flex;
  303. margin-left: 0px !important;
  304. width: 100% !important;
  305. }
  306. ._fc-tf-head-idx, ._fc-tf-idx {
  307. width: 40px;
  308. min-width: 40px;
  309. font-weight: 500;
  310. text-align: center;
  311. }
  312. ._fc-tf-edit, ._fc-tf-btn {
  313. width: 70px;
  314. min-width: 70px;
  315. text-align: center;
  316. }
  317. ._fc-tf-btn .fc-icon {
  318. cursor: pointer;
  319. }
  320. ._fc-table-form._fc-disabled ._fc-tf-btn .fc-icon, ._fc-table-form._fc-disabled > .el-button {
  321. cursor: not-allowed;
  322. }
  323. ._fc-tf-table {
  324. width: 100%;
  325. height: 100%;
  326. overflow: hidden;
  327. table-layout: fixed;
  328. border: 1px solid #EBEEF5;
  329. border-bottom: 0 none;
  330. }
  331. ._fc-table-form ._fc-tf-table > thead > tr > th {
  332. border: 0 none;
  333. border-bottom: 1px solid #EBEEF5;
  334. height: 40px;
  335. font-weight: 500;
  336. }
  337. ._fc-table-form ._fc-tf-table > thead > tr > th + th {
  338. border-left: 1px solid #EBEEF5;
  339. }
  340. ._fc-table-form tr {
  341. min-height: 50px;
  342. }
  343. ._fc-table-form ._fc-read-view {
  344. text-align: center;
  345. width: 100%;
  346. }
  347. ._fc-table-form td {
  348. padding: 5px;
  349. min-height: 50px;
  350. min-width: 80px;
  351. position: relative;
  352. box-sizing: border-box;
  353. overflow-wrap: break-word;
  354. /*white-space: nowrap;*/
  355. overflow: hidden;
  356. border: 0 none;
  357. border-bottom: 1px solid #EBEEF5;
  358. }
  359. ._fc-table-form td + td {
  360. border-left: 1px solid #EBEEF5;
  361. }
  362. ._fc-tf-table .el-input-number, ._fc-tf-table .el-select, ._fc-tf-table .el-slider, ._fc-tf-table .el-cascader, ._fc-tf-table .el-date-editor {
  363. width: 100%;
  364. }
  365. ._fc-tf-head-required:before {
  366. content: '*';
  367. color: #f56c6c;
  368. margin-right: 4px;
  369. }
  370. </style>