vue-element-plus-admin icon indicating copy to clipboard operation
vue-element-plus-admin copied to clipboard

大神們好, 想問一下可以在form內加有附件的table嗎

Open gordon-ghk opened this issue 2 years ago • 0 comments

我現在是自己寫一個components

<script lang="tsx" setup>
import { toRefs } from 'vue'
import { ElTable, ElButton, ElTableColumn } from 'element-plus'
import _ from 'lodash-es'

interface Data {
  list: any[]
  titles: any[]
  height?: number
  deleteAble?: boolean
  addAble?: boolean
  editAble?: boolean
  haveFile?: boolean
}
const props = defineProps({
  data: {
    type: Object as () => Data,
    required: true
  }
})

let { data } = toRefs(props)

const title = data.value.titles

const deleteRow = (index: number) => {
  emit('delete-row', index)
}
const editItem = (index: number) => {
  emit('edit-row', index)
}

const details = (index: number) => {
  emit('details-row', index)
}

const fileItem = (index: number) => {
  emit('file-details', index)
}

const onAddItem = () => {
  emit('add-row')
}

const emit = defineEmits(['delete-row', 'add-row', 'details-row', 'edit-row', 'file-details'])
</script>
<template>
  <ElTable :data="data.list" style="width: 100%" :max-height="data.height ? data.height : 1000">
    <ElTableColumn
      v-for="item of title"
      :prop="_.keys(item)[0]"
      :label="_.values(item)[0]"
      :key="_.values(item)[0]"
    />
    <ElTableColumn v-if="data.haveFile" fixed="right" label="File" width="80">
      <template #default="scope">
        <ElButton
          v-if="data.list[scope.$index].file"
          link
          type="primary"
          size="small"
          @click.prevent="fileItem(scope.$index)"
        >
          File
        </ElButton>
      </template>
    </ElTableColumn>
    <ElTableColumn v-if="data.editAble" fixed="right" label="Details" width="80">
      <template #default="scope">
        <ElButton link type="primary" size="small" @click.prevent="details(scope.$index)">
          Details
        </ElButton>
      </template>
    </ElTableColumn>
    <ElTableColumn v-if="data.editAble" fixed="right" label="Edit" width="80">
      <template #default="scope">
        <ElButton link type="primary" size="small" @click.prevent="editItem(scope.$index)">
          Edit
        </ElButton>
      </template>
    </ElTableColumn>
    <ElTableColumn v-if="data.deleteAble" fixed="right" label="Remove" width="80">
      <template #default="scope">
        <ElButton link type="primary" size="small" @click.prevent="deleteRow(scope.$index)">
          Remove
        </ElButton>
      </template>
    </ElTableColumn>
  </ElTable>
  <ElButton v-if="data.addAble" class="mt-4" style="width: 100%" @click="onAddItem"
    >Add Item</ElButton
  >
</template>

用在form內:

<script setup lang="tsx">
import { Form, FormSchema } from '@/components/Form'
import { useForm } from '@/hooks/web/useForm'
import { PropType, reactive, ref, watch } from 'vue'
import { useI18n } from '@/hooks/web/useI18n'
import { useValidator } from '@/hooks/web/useValidator'
import { useRoute } from 'vue-router'
import { onMounted } from 'vue'
import { JsonTable } from '@/components/JsonTable'
import { Dialog } from '@/components/Dialog'
import { ElMessage, ElMessageBox, ElButton } from 'element-plus'
import _ from 'lodash-es'

const { required } = useValidator()

const dialogVisible = ref(false)
const uploading = ref(false)
const dialogType = ref('')
const rowIndex = ref(-1)
let fileLists = ref<FileStruct[]>([])

interface DataStruct {
  date: string
  name: string
  state: string
  city: string
  address: string
  zip: string
  file?: any
}

interface FileStruct {
  name: string
  url: string
}

const props = defineProps({
  currentRow: {
    type: Object as PropType<Nullable<ExchangeRate>>,
    default: () => null
  },
  formSchema: {
    type: Array as PropType<FormSchema[]>,
    default: () => []
  }
})
const { query } = useRoute()
const { t } = useI18n()
const { formRegister, formMethods } = useForm()
const { setValues, getFormData, getElFormExpose, addSchema, setProps } = formMethods

let title = [
  { date: 'Date' },
  { name: 'Name' },
  { state: 'State' },
  { city: 'City' },
  { address: 'Address' },
  { zip: 'Zip' }
]

let dataList = ref<DataStruct[]>([
  {
    date: '2016-05-01',
    name: 'Tom',
    state: 'California',
    city: 'Los Angeles',
    address: 'No. 189, Grove St, Los Angeles',
    zip: 'CA 90036',
    file: [
      {
        name: 'element-plus-logo.svg',
        url: 'https://element-plus.org/images/element-plus-logo.svg'
      },
      {
        name: 'element-plus-logo2.svg',
        url: 'https://element-plus.org/images/element-plus-logo.svg'
      }
    ]
  },
  {
    date: '2016-05-02',
    name: 'Tom',
    state: 'California',
    city: 'Los Angeles',
    address: 'No. 189, Grove St, Los Angeles',
    zip: 'CA 90036'
  },
  {
    date: '2016-05-03',
    name: 'Tom',
    state: 'California',
    city: 'Los Angeles',
    address: 'No. 189, Grove St, Los Angeles',
    zip: 'CA 90036'
  }
])

const Data = ref({
  list: dataList,
  titles: title,
  deleteAble: true,
  addAble: true,
  editAble: true,
  detailsAble: true,
  haveFile: true
})

const schema = reactive<FormSchema[]>([
  {
    field: 'currency',
    label: t('exchangeRate.currencyThreeChar'),
    component: 'Input',
    formItemProps: {
      rules: [required()]
    },
    colProps: {
      span: 24
    },
    componentProps: {
      disabled: query.code === undefined ? false : true,
      maxlength: 3,
      formatter: (value) => `${value}`.toUpperCase(),
      showWordLimit: true
    }
  },
  {
    field: 'rate',
    label: t('exchangeRate.rate'),
    component: 'InputNumber',
    componentProps: {
      min: 0
    },
    formItemProps: {
      rules: [required()]
    },
    colProps: {
      span: 24
    }
  }
])
const schemaTable = reactive<FormSchema[]>([
  {
    field: 'date',
    label: 'date',
    component: 'Input',
    componentProps: {
      type: 'date'
    }
  },
  {
    field: 'name',
    label: 'name',
    component: 'Input',
    formItemProps: {
      rules: [required()]
    }
  },
  {
    field: 'state',
    label: 'state',
    component: 'Input',
    formItemProps: {
      rules: [required()]
    }
  },
  {
    field: 'city',
    label: 'city',
    component: 'Input',
    formItemProps: {
      rules: [required()]
    }
  },
  {
    field: 'address',
    label: 'address',
    component: 'Input'
  },
  {
    field: 'zip',
    component: 'Input',
    label: 'zip'
  },
  {
    field: 'file',
    component: 'Upload',
    label: `${t('formDemo.default')}`,
    componentProps: {
      limit: 3,
      action: 'https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15',
      fileList: fileLists,
      multiple: true,
      onPreview: (uploadFile) => {
        window.open(uploadFile.url, '_blank')
        console.log(uploadFile)
      },
      onRemove: (file) => {
        console.log(file)
      },
      beforeRemove: (uploadFile) => {
        return ElMessageBox.confirm(`Cancel the transfer of ${uploadFile.name} ?`).then(
          () => true,
          () => false
        )
      },
      onExceed: (files, uploadFiles) => {
        ElMessage.warning(
          `The limit is 3, you selected ${files.length} files this time, add up to ${
            files.length + uploadFiles.length
          } totally`
        )
      },
      onSuccess: (_, uploadFiles) => {
        fileLists.value.push({
          name: uploadFiles.name,
          url: 'https://element-plus.org/images/element-plus-logo.svg'
        })
        uploading.value = false
      },
      onError: () => {
        ElMessage.error('Upload Error')
        uploading.value = false
      },
      beforeUpload: () => {
        uploading.value = true
      },
      slots: {
        default: () => <ElButton type="primary">Click to upload</ElButton>,
        tip: () => <div class="el-upload__tip">jpg/png files with a size less than 500KB.</div>
      }
    }
  }
])
const rules = reactive({
  currency: [required()],
  currencyDetails: [required()],
  rate: [required()]
})

const submit = async () => {
  const elForm = await getElFormExpose()
  const valid = await elForm?.validate().catch((err) => {
    console.log(err)
  })
  if (valid) {
    const formData = await getFormData()
    var deepCopy = _.cloneDeep(formData)
    deepCopy.tableData = dataList.value
    return deepCopy
  }
}

watch(
  () => props.currentRow,
  (currentRow) => {
    if (!currentRow) return
    setValues(currentRow)
  },
  {
    deep: true,
    immediate: true
  }
)
onMounted(() => {
  if (query.code !== undefined) {
    addSchema(
      {
        field: 'currency',
        label: t('exchangeRate.currencyName'),
        component: 'Select',
        componentProps: {
          options: currency.currency,
          disabled: props.currentRow?.currency === null ? false : true,
          showWordLimit: true
        },
        formItemProps: {
          rules: [required()]
        },
        colProps: {
          span: 24
        }
      },
      1
    )
  }
})
defineExpose({
  submit
})
const tableCurrentRow = ref<Nullable<DataStruct>>(null)

const editRow = async (index) => {
  uploading.value = false
  dialogVisible.value = !dialogVisible.value
  dialogType.value = 'edit'
  rowIndex.value = index
  tableCurrentRow.value = dataList.value[index]
  console.log(tableCurrentRow.value)
  fileLists.value = dataList.value[index].file
  await setValues(dataList.value[index])
}

const details = async (index) => {
  dialogVisible.value = !dialogVisible.value
  dialogType.value = 'details'
  tableCurrentRow.value = dataList.value[index]
  console.log(tableCurrentRow.value)
  fileLists.value = dataList.value[index].file
  setProps({
    disabled: true
  })
  await setValues(dataList.value[index])
}

const dialogControl = () => {
  uploading.value = false
  dialogType.value = 'add'
  fileLists.value = []
  dialogVisible.value = !dialogVisible.value
}
const deleteRow = (index) => {
  ElMessageBox.confirm(t('common.delMessage'), t('common.delWarning'), {
    confirmButtonText: t('common.delOk'),
    cancelButtonText: t('common.delCancel'),
    type: 'warning'
  }).then(async () => {
    _.pullAt(dataList.value, index)
  })
}

const formSubmit = async () => {
  const elForm = await getElFormExpose()
  const valid = await elForm?.validate().catch((err) => {
    console.log(err)
  })
  if (valid) {
    const formData = (await getFormData()) as DataStruct
    fileLists.value.length > 0 ? (formData.file = fileLists.value) : null
    dialogType.value === 'edit'
      ? (dataList.value[rowIndex.value] = formData)
      : dataList.value.push(formData)
    dialogVisible.value = false
    getFormData()
    ElMessage.success('Success')
  } else {
    ElMessage.error('Please input all of the required field')
  }
}
</script>

<template>
  <Form :rules="rules" @register="formRegister" :schema="schema" />
  <JsonTable
    :data="Data"
    @delete-row="deleteRow"
    @add-row="dialogControl"
    @edit-row="editRow"
    @details-row="details"
    @file-details="details"
  />
  <Dialog title="" v-model="dialogVisible">
    <Form :rules="rules" :schema="schemaTable" @register="formRegister" />
    <template #footer>
      <ElButton
        v-if="dialogType != 'details'"
        type="primary"
        @click="formSubmit"
        :disabled="uploading"
        >{{ t('dialogDemo.submit') }}</ElButton
      >
      <ElButton @click="dialogControl" :disabled="uploading">{{ t('dialogDemo.close') }}</ElButton>
    </template>
  </Dialog>
</template>

還可以有甚麼優化嗎? image

gordon-ghk avatar Sep 03 '23 05:09 gordon-ghk