StdCurd.vue 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. <script setup lang="ts">
  2. import { message } from 'ant-design-vue'
  3. import type { ComputedRef } from 'vue'
  4. import type { StdTableProps } from './StdTable.vue'
  5. import StdTable from './StdTable.vue'
  6. import gettext from '@/gettext'
  7. import StdDataEntry from '@/components/StdDesign/StdDataEntry'
  8. import type { Column } from '@/components/StdDesign/types'
  9. export interface StdCurdProps {
  10. cardTitleKey?: string
  11. modalMaxWidth?: string | number
  12. disableAdd?: boolean
  13. onClickAdd?: () => void
  14. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  15. onClickEdit?: (id: number | string, record: any, index: number) => void
  16. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  17. beforeSave?: (data: any) => Promise<void>
  18. }
  19. const props = defineProps<StdTableProps & StdCurdProps>()
  20. const { $gettext } = gettext
  21. const visible = ref(false)
  22. const update = ref(0)
  23. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  24. const data: any = reactive({ id: null })
  25. provide('data', data)
  26. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  27. const error: any = reactive({})
  28. const selected = ref([])
  29. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  30. function onSelect(keys: any) {
  31. selected.value = keys
  32. }
  33. const editableColumns = computed(() => {
  34. return props.columns!.filter(c => {
  35. return c.edit
  36. })
  37. }) as ComputedRef<Column[]>
  38. function add() {
  39. Object.keys(data).forEach(v => {
  40. delete data[v]
  41. })
  42. clear_error()
  43. visible.value = true
  44. }
  45. const table = ref()
  46. function get_list() {
  47. table.value?.get_list()
  48. }
  49. defineExpose({
  50. add,
  51. get_list,
  52. data,
  53. })
  54. function clear_error() {
  55. Object.keys(error).forEach(v => {
  56. delete error[v]
  57. })
  58. }
  59. const ok = async () => {
  60. clear_error()
  61. await props?.beforeSave?.(data)
  62. props.api!.save(data.id, data).then(r => {
  63. message.success($gettext('Save Successfully'))
  64. Object.assign(data, r)
  65. get_list()
  66. visible.value = false
  67. }).catch(e => {
  68. message.error($gettext(e?.message ?? 'Server error'), 5)
  69. Object.assign(error, e.errors)
  70. })
  71. }
  72. function cancel() {
  73. visible.value = false
  74. clear_error()
  75. }
  76. function edit(id: number | string) {
  77. props.api!.get(id).then(async r => {
  78. Object.keys(data).forEach(k => {
  79. delete data[k]
  80. })
  81. data.id = null
  82. Object.assign(data, r)
  83. visible.value = true
  84. }).catch(e => {
  85. message.error($gettext(e?.message ?? 'Server error'), 5)
  86. })
  87. }
  88. const selectedRowKeys = ref([])
  89. </script>
  90. <template>
  91. <div class="std-curd">
  92. <ACard :title="title || $gettext('Table')">
  93. <template #extra>
  94. <ASpace>
  95. <a
  96. v-if="!disableAdd"
  97. @click="add"
  98. >{{ $gettext('Add') }}</a>
  99. <slot name="extra" />
  100. </ASpace>
  101. </template>
  102. <StdTable
  103. ref="table"
  104. v-bind="props"
  105. :key="update"
  106. v-model:selected-row-keys="selectedRowKeys"
  107. @click-edit="edit"
  108. @selected="onSelect"
  109. >
  110. <template #actions="slotProps">
  111. <slot
  112. name="actions"
  113. :actions="slotProps.record"
  114. />
  115. </template>
  116. </StdTable>
  117. </ACard>
  118. <AModal
  119. class="std-curd-edit-modal"
  120. :mask="false"
  121. :title="data.id ? $gettext('Modify') : $gettext('Add')"
  122. :open="visible"
  123. :cancel-text="$gettext('Cancel')"
  124. :ok-text="$gettext('OK')"
  125. :width="modalMaxWidth"
  126. destroy-on-close
  127. @cancel="cancel"
  128. @ok="ok"
  129. >
  130. <div
  131. v-if="$slots.beforeEdit"
  132. class="before-edit"
  133. >
  134. <slot
  135. name="beforeEdit"
  136. :data="data"
  137. />
  138. </div>
  139. <StdDataEntry
  140. :data-list="editableColumns"
  141. :data-source="data"
  142. :error="error"
  143. />
  144. <slot
  145. name="edit"
  146. :data="data"
  147. />
  148. </AModal>
  149. </div>
  150. </template>
  151. <style lang="less" scoped>
  152. :deep(.before-edit:last-child) {
  153. margin-bottom: 20px;
  154. }
  155. </style>