vue-element-plus-admin
vue-element-plus-admin copied to clipboard
大神們好, 想問一下可以在form內加有附件的table嗎
我現在是自己寫一個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>
還可以有甚麼優化嗎?