index.vue 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. <template>
  2. <div class="basic-table">
  3. <cip-table :offset="offset"
  4. v-model:data="proxyValue"
  5. :columns="options"
  6. :depend-on-values="dependOnValues"
  7. :border="!hideBorder"
  8. :field-key="fieldKey"
  9. :row-key="securityConfig.rowKey"
  10. :tree-props="optionProps"
  11. :default-expand-all="securityConfig.defaultExpendAll"
  12. :rule-key="securityConfig.ruleKey || fieldKey"
  13. :tableHeaderLabel="securityConfig.tableHeaderLabel"
  14. :hide-index="securityConfig.hideIndex"
  15. :index-fixed="securityConfig.indexFixed ?? true"
  16. :show-summary="securityConfig.showSummary"
  17. :span-method="securityConfig.spanMethod"
  18. :height="securityConfig.height"
  19. size="small"
  20. :stripe="true"
  21. :in-form="true">
  22. <el-table-column :min-width="isTreeTable && !securityConfig.hideAddChild ? '90px':'70px'" v-if="!securityConfig.hideDelete" fixed="right" label="操作">
  23. <template #default="{$index, row}">
  24. <cip-table-button v-if="!securityConfig.hideAdd" @click="insertItem($index)">
  25. <i class="el-icon-circle-plus-outline cip-primary-color handler-size"/>
  26. </cip-table-button>
  27. <template v-if="isTreeTable && !securityConfig.hideAddChild">
  28. <cip-table-button @click="insertChildItem($index,row)">
  29. <svg t="1656469520966" class="handler-size" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1591" width="1em" height="1em">
  30. <path style="transform: scale(0.5)" stroke-width="24px" stroke="currentColor" fill="currentColor" d="M778.5984 619.52H727.04v-51.5584c0-11.3152-9.1648-20.48-20.48-20.48s-20.48 9.1648-20.48 20.48V619.52h-51.5584c-11.3152 0-20.48 9.1648-20.48 20.48s9.1648 20.48 20.48 20.48H686.08v51.5584c0 11.3152 9.1648 20.48 20.48 20.48s20.48-9.1648 20.48-20.48V660.48h51.5584c11.3152 0 20.48-9.1648 20.48-20.48s-9.1648-20.48-20.48-20.48z" p-id="1592"></path>
  31. <path stroke-width="24px" stroke="currentColor" fill="currentColor" d="M704 404.48c-122.624 0-223.0784 94.8224-232.192 215.0912-0.256 0-0.512-0.0512-0.768-0.0512H286.72V194.56h419.84c11.3152 0 20.48-9.1648 20.48-20.48s-9.1648-20.48-20.48-20.48H107.52c-11.3152 0-20.48 9.1648-20.48 20.48s9.1648 20.48 20.48 20.48h138.24v445.44c0 11.3152 9.1648 20.48 20.48 20.48h204.8c0.4096 0 0.768-0.1024 1.1264-0.1024C483.6864 778.24 583.0656 870.4 704 870.4c128.6656 0 232.96-104.2944 232.96-232.96S832.6656 404.48 704 404.48z m0 424.96c-105.8816 0-192-86.1184-192-192S598.1184 445.44 704 445.44s192 86.1184 192 192-86.1184 192-192 192z" p-id="1593"></path>
  32. </svg>
  33. </cip-table-button>
  34. </template>
  35. <cip-table-button @click="deleteItem($index, row)">
  36. <i class="el-icon-remove-outline cip-danger-color handler-size"/>
  37. </cip-table-button>
  38. </template>
  39. </el-table-column>
  40. </cip-table>
  41. <div style="text-align: center" v-if="!securityConfig.hideAdd">
  42. <el-button size="medium" type="text" @click="addItem">
  43. <i class="el-icon-circle-plus create-handle" />
  44. </el-button>
  45. </div>
  46. </div>
  47. </template>
  48. <script>
  49. import { computed, defineAsyncComponent, nextTick } from 'vue'
  50. import { formInputProps, fromInputEmits } from '@cip/components/cip-form-input/form-input-props'
  51. import { ElButton, ElMessageBox, ElTableColumn } from 'element-plus'
  52. import { useFormInput, useElementFormEvent } from '@cip/components/hooks/form-input'
  53. import CipTableButton from '@cip/components/cip-table-button'
  54. import { v4 as uuid } from 'uuid'
  55. import { setOptionWritable } from './util'
  56. import { setFieldValue, getFieldValue } from '@cip/utils/util'
  57. import { analyseData, getPropertyKeyByPath } from '../../../cip-table/util'
  58. export default {
  59. name: 'basic-table',
  60. components: {
  61. CipTable: defineAsyncComponent(() => import('@cip/components/cip-table')),
  62. CipTableButton,
  63. ElButton,
  64. ElTableColumn
  65. },
  66. props: {
  67. ...formInputProps,
  68. type: {
  69. default: 'input'
  70. }
  71. },
  72. emits: [...fromInputEmits],
  73. setup (props, context) {
  74. const { handleChange } = useElementFormEvent() // inject('elFormItem', {})
  75. const formInput = useFormInput(props, context)
  76. const { proxyValue, securityConfig } = formInput
  77. const offset = computed(() => {
  78. return securityConfig.value.hideIndex ? undefined : 0
  79. })
  80. const hideBorder = computed(() => {
  81. return securityConfig.value.hideBorder
  82. })
  83. const optionProps = computed(() => {
  84. // el-table 默认treeProps参数
  85. return Object.assign({ children: 'children', hasChildren: 'hasChildren' }, securityConfig.value.optionProps)
  86. })
  87. const isTreeTable = computed(() => {
  88. // 只要设置了rowKey即认为该表单可能是树形表单
  89. return securityConfig.value.rowKey
  90. })
  91. const options = computed(() => {
  92. let options = securityConfig.value.options || []
  93. if (securityConfig.value.tableColumnStatus === 'writable') {
  94. options = setOptionWritable(options, true)
  95. }
  96. return options
  97. })
  98. const emitItemValidate = (val) => { // 触发验证
  99. nextTick(() => {
  100. handleChange(val)
  101. })
  102. }
  103. const getNewItem = () => {
  104. // tree 新增时必须由唯一key
  105. return isTreeTable.value ? { $id: uuid() } : {}
  106. }
  107. // 插入一个最后的行
  108. const addItem = () => {
  109. const val = Array.isArray(proxyValue.value) ? proxyValue.value : []
  110. const newItem = getNewItem()
  111. val.push(newItem)
  112. emitItemValidate(val)
  113. proxyValue.value = val
  114. }
  115. // 在当前行上方插入
  116. const insertItem = (index) => {
  117. const allArray = props.modelValue || []
  118. const newItem = getNewItem()
  119. if (isTreeTable.value) {
  120. const indexed = analyseData(allArray, optionProps.value)
  121. const realIndexArr = indexed[index]
  122. if (realIndexArr.length > 1) {
  123. const realIndex = realIndexArr.pop() // realIndex 为要剔除的index realIndexArr为父数组下标
  124. const key = getPropertyKeyByPath(realIndexArr, optionProps.value)
  125. const parentArr = getFieldValue(allArray, key)
  126. parentArr[optionProps.value.children].splice(realIndex, 0, newItem)
  127. } else {
  128. allArray.splice(realIndexArr[0], 0, newItem)
  129. }
  130. } else {
  131. allArray.splice(index, 0, newItem)
  132. }
  133. emitItemValidate(allArray)
  134. proxyValue.value = allArray
  135. }
  136. // 插入一个最后的子行
  137. const insertChildItem = (index, row) => {
  138. if (isTreeTable.value) {
  139. const newItem = getNewItem()
  140. const children = getFieldValue(row, optionProps.value.children)
  141. if (!children) {
  142. setFieldValue(row, optionProps.value.children, [newItem])
  143. } else {
  144. children.push(newItem)
  145. }
  146. }
  147. }
  148. // 删除当前行
  149. const deleteItem = (index) => {
  150. ElMessageBox.confirm('确实删除此列', '提示').then(() => {
  151. const allArray = props.modelValue || []
  152. if (isTreeTable.value) {
  153. const indexed = analyseData(allArray, optionProps.value)
  154. const realIndexArr = indexed[index]
  155. if (realIndexArr.length > 1) {
  156. const realIndex = realIndexArr.pop() // realIndex 为要剔除的index realIndexArr为父数组下标
  157. const key = getPropertyKeyByPath(realIndexArr, optionProps.value)
  158. const parentArr = getFieldValue(allArray, key)
  159. parentArr[optionProps.value.children].splice(realIndex, 1)
  160. } else {
  161. allArray.splice(realIndexArr[0], 1)
  162. }
  163. } else {
  164. allArray.splice(index, 1)
  165. }
  166. emitItemValidate(allArray)
  167. proxyValue.value = allArray
  168. }).catch(() => {})
  169. }
  170. return {
  171. offset,
  172. hideBorder,
  173. options,
  174. securityConfig,
  175. isTreeTable,
  176. insertItem,
  177. insertChildItem,
  178. deleteItem,
  179. addItem,
  180. proxyValue,
  181. optionProps
  182. }
  183. }
  184. }
  185. </script>
  186. <style lang="less">
  187. .el-form-item.is-error{
  188. .basic-table{
  189. outline: 1px solid @danger;
  190. }
  191. }
  192. .basic-table{
  193. overflow: hidden;
  194. width: 0;
  195. flex-grow: 2;
  196. .handler-size{
  197. font-size: 16px;
  198. width: 1em;
  199. height: 1em;
  200. vertical-align: -0.15em;
  201. fill: currentColor;
  202. overflow: hidden;
  203. }
  204. .create-handle{
  205. font-size: 24px;
  206. }
  207. }
  208. </style>