feat: 新增Uload
This commit is contained in:
parent
040476fe24
commit
c181887f7f
|
@ -17,7 +17,8 @@ import {
|
|||
ElTransfer,
|
||||
ElAutocomplete,
|
||||
ElDivider,
|
||||
ElTreeSelect
|
||||
ElTreeSelect,
|
||||
ElUpload
|
||||
} from 'element-plus'
|
||||
import { InputPassword } from '@/components/InputPassword'
|
||||
import { Editor } from '@/components/Editor'
|
||||
|
@ -45,7 +46,8 @@ const componentMap: Recordable<Component, ComponentName> = {
|
|||
SelectV2: ElSelectV2,
|
||||
InputPassword: InputPassword,
|
||||
Editor: Editor,
|
||||
TreeSelect: ElTreeSelect
|
||||
TreeSelect: ElTreeSelect,
|
||||
Upload: ElUpload
|
||||
}
|
||||
|
||||
export { componentMap }
|
||||
|
|
|
@ -17,7 +17,8 @@ import {
|
|||
DatePickerProps,
|
||||
FormItemProps as ElFormItemProps,
|
||||
FormProps as ElFormProps,
|
||||
ISelectProps
|
||||
ISelectProps,
|
||||
UploadProps
|
||||
} from 'element-plus'
|
||||
import { IEditorConfig } from '@wangeditor/editor'
|
||||
import { CSSProperties } from 'vue'
|
||||
|
@ -51,7 +52,8 @@ export enum ComponentNameEnum {
|
|||
SELECT_V2 = 'SelectV2',
|
||||
INPUT_PASSWORD = 'InputPassword',
|
||||
EDITOR = 'Editor',
|
||||
TREE_SELECT = 'TreeSelect'
|
||||
TREE_SELECT = 'TreeSelect',
|
||||
UPLOAD = 'Upload'
|
||||
}
|
||||
|
||||
type CamelCaseComponentName = keyof typeof ComponentNameEnum extends infer K
|
||||
|
@ -505,6 +507,16 @@ export interface FormItemProps extends Partial<ElFormItemProps> {
|
|||
}
|
||||
}
|
||||
|
||||
export interface UploadComponentProps extends Partial<UploadProps> {
|
||||
slots?: {
|
||||
default?: (...args: any[]) => JSX.Element | null
|
||||
trigger?: (...args: any[]) => JSX.Element | null
|
||||
tip?: (...args: any[]) => JSX.Element | null
|
||||
file?: (...args: any[]) => JSX.Element | null
|
||||
}
|
||||
style?: CSSProperties
|
||||
}
|
||||
|
||||
export interface TreeSelectComponentProps
|
||||
extends Omit<Partial<SelectComponentProps>, 'props' | 'on' | 'slots'> {
|
||||
data?: any[]
|
||||
|
@ -607,6 +619,7 @@ export interface FormSchema {
|
|||
| TimePickerComponentProps
|
||||
| InputPasswordComponentProps
|
||||
| TreeSelectComponentProps
|
||||
| UploadComponentProps
|
||||
|
||||
/**
|
||||
* formItem组件属性,具体可以查看element-plus文档
|
||||
|
|
|
@ -7,7 +7,7 @@ import {
|
|||
ElTooltipProps,
|
||||
ElImage
|
||||
} from 'element-plus'
|
||||
import { defineComponent, PropType, ref, computed, unref, watch, onMounted, nextTick } from 'vue'
|
||||
import { defineComponent, PropType, ref, computed, unref, watch, onMounted } from 'vue'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { setIndex } from './helper'
|
||||
import type { TableProps, TableColumn, Pagination, TableSetProps } from './types'
|
||||
|
@ -15,8 +15,8 @@ import { set } from 'lodash-es'
|
|||
import { CSSProperties } from 'vue'
|
||||
import { getSlot } from '@/utils/tsxHelper'
|
||||
import TableActions from './components/TableActions.vue'
|
||||
import Sortable from 'sortablejs'
|
||||
import { Icon } from '@/components/Icon'
|
||||
// import Sortable from 'sortablejs'
|
||||
// import { Icon } from '@/components/Icon'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Table',
|
||||
|
@ -62,7 +62,7 @@ export default defineComponent({
|
|||
type: Array as PropType<string[]>,
|
||||
default: () => []
|
||||
},
|
||||
sortable: propTypes.bool.def(false),
|
||||
// sortable: propTypes.bool.def(false),
|
||||
height: propTypes.oneOfType([Number, String]),
|
||||
maxHeight: propTypes.oneOfType([Number, String]),
|
||||
stripe: propTypes.bool.def(false),
|
||||
|
@ -213,32 +213,32 @@ export default defineComponent({
|
|||
return propsObj
|
||||
})
|
||||
|
||||
const sortableEl = ref()
|
||||
// const sortableEl = ref()
|
||||
// 初始化拖拽
|
||||
const initDropTable = () => {
|
||||
const el = unref(elTableRef)?.$el.querySelector('.el-table__body tbody')
|
||||
if (!el) return
|
||||
if (unref(sortableEl)) unref(sortableEl).destroy()
|
||||
// const initDropTable = () => {
|
||||
// const el = unref(elTableRef)?.$el.querySelector('.el-table__body tbody')
|
||||
// if (!el) return
|
||||
// if (unref(sortableEl)) unref(sortableEl).destroy()
|
||||
|
||||
sortableEl.value = Sortable.create(el, {
|
||||
handle: '.table-move',
|
||||
animation: 180,
|
||||
onEnd(e: any) {
|
||||
emit('sortable-change', e)
|
||||
}
|
||||
})
|
||||
}
|
||||
// sortableEl.value = Sortable.create(el, {
|
||||
// handle: '.table-move',
|
||||
// animation: 180,
|
||||
// onEnd(e: any) {
|
||||
// emit('sortable-change', e)
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
|
||||
watch(
|
||||
() => getProps.value.sortable,
|
||||
async (v) => {
|
||||
await nextTick()
|
||||
v && initDropTable()
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
// watch(
|
||||
// () => getProps.value.sortable,
|
||||
// async (v) => {
|
||||
// await nextTick()
|
||||
// v && initDropTable()
|
||||
// },
|
||||
// {
|
||||
// immediate: true
|
||||
// }
|
||||
// )
|
||||
|
||||
const setProps = (props: TableProps = {}) => {
|
||||
mergeProps.value = Object.assign(unref(mergeProps), props)
|
||||
|
@ -495,19 +495,19 @@ export default defineComponent({
|
|||
tableSlots['append'] = (...args: any[]) => getSlot(slots, 'append', args)
|
||||
}
|
||||
|
||||
const { sortable } = unref(getProps)
|
||||
// const { sortable } = unref(getProps)
|
||||
|
||||
const sortableEl = sortable ? (
|
||||
<ElTableColumn
|
||||
className="table-move cursor-move"
|
||||
type="sortable"
|
||||
prop="sortable"
|
||||
width="60px"
|
||||
align="center"
|
||||
>
|
||||
<Icon icon="ant-design:drag-outlined" />
|
||||
</ElTableColumn>
|
||||
) : null
|
||||
// const sortableEl = sortable ? (
|
||||
// <ElTableColumn
|
||||
// className="table-move cursor-move"
|
||||
// type="sortable"
|
||||
// prop="sortable"
|
||||
// width="60px"
|
||||
// align="center"
|
||||
// >
|
||||
// <Icon icon="ant-design:drag-outlined" />
|
||||
// </ElTableColumn>
|
||||
// ) : null
|
||||
|
||||
return (
|
||||
<div v-loading={unref(getProps).loading}>
|
||||
|
@ -520,7 +520,7 @@ export default defineComponent({
|
|||
) : null}
|
||||
<ElTable ref={elTableRef} data={unref(getProps).data} {...unref(getBindValue)}>
|
||||
{{
|
||||
default: () => [sortableEl, ...renderTableColumn()],
|
||||
default: () => renderTableColumn(),
|
||||
...tableSlots
|
||||
}}
|
||||
</ElTable>
|
||||
|
|
|
@ -5,16 +5,16 @@ import {
|
|||
ElDropdown,
|
||||
ElDropdownMenu,
|
||||
ElDropdownItem,
|
||||
ComponentSize,
|
||||
ElPopover,
|
||||
ElTree
|
||||
ComponentSize
|
||||
// ElPopover,
|
||||
// ElTree
|
||||
} from 'element-plus'
|
||||
import { Icon } from '@/components/Icon'
|
||||
import { useI18n } from '@/hooks/web/useI18n'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
import { TableColumn } from '../types'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { eachTree } from '@/utils/tree'
|
||||
// import { eachTree } from '@/utils/tree'
|
||||
|
||||
const appStore = useAppStore()
|
||||
const sizeMap = computed(() => appStore.sizeMap)
|
||||
|
@ -39,16 +39,6 @@ export default defineComponent({
|
|||
emit('changSize', size)
|
||||
}
|
||||
|
||||
const defaultCheckeds = computed(() => {
|
||||
const checkeds: string[] = []
|
||||
eachTree(unref(columns), (item: TableColumn) => {
|
||||
if (!item.hidden) {
|
||||
checkeds.push(item.field)
|
||||
}
|
||||
})
|
||||
return checkeds
|
||||
})
|
||||
|
||||
const columns = computed(() => {
|
||||
return cloneDeep(props.columns).filter((v) => {
|
||||
// 去掉type为selection的列和expand的列
|
||||
|
@ -68,81 +58,6 @@ export default defineComponent({
|
|||
}
|
||||
)
|
||||
|
||||
// 在原始树形数据中更新节点位置
|
||||
function updateTreeData(treeData, draggedNode, targetNode, placement) {
|
||||
let updatedTreeData = cloneDeep(treeData) // 克隆一份原始数据以免修改原数据
|
||||
|
||||
// 找到被拖拽节点和目标节点
|
||||
function findNodes(nodeId, nodes) {
|
||||
for (const node of nodes) {
|
||||
if (node?.field === nodeId) {
|
||||
return node
|
||||
}
|
||||
if (node.children && node.children.length) {
|
||||
const foundNode = findNodes(nodeId, node.children)
|
||||
if (foundNode) {
|
||||
return foundNode
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
const draggedNodeToUpdate = findNodes(draggedNode?.data?.field, updatedTreeData)
|
||||
const targetNodeToUpdate = targetNode
|
||||
? findNodes(targetNode?.data?.field, updatedTreeData)
|
||||
: null
|
||||
|
||||
if (!draggedNodeToUpdate || (targetNode && !targetNodeToUpdate)) {
|
||||
// 未找到节点,可能是数据错误
|
||||
console.error('无法找到要更新的节点或目标节点')
|
||||
return treeData // 返回原数据,不做任何修改
|
||||
}
|
||||
|
||||
// 在原来的位置移除被拖拽节点
|
||||
function removeNode(node) {
|
||||
const parent = updatedTreeData.find((n) => n.children.includes(node))
|
||||
if (parent) {
|
||||
parent.children = parent.children.filter((n) => n?.field !== node?.field)
|
||||
} else {
|
||||
updatedTreeData = updatedTreeData.filter((n) => n?.field !== node?.field)
|
||||
}
|
||||
}
|
||||
removeNode(draggedNodeToUpdate)
|
||||
|
||||
// 将被拖拽节点插入目标节点的位置
|
||||
function insertNode(placement) {
|
||||
if (placement === 'before' || placement === 'after') {
|
||||
const parent = updatedTreeData.find((n) => n.children.includes(targetNodeToUpdate))
|
||||
const index = parent.children.findIndex((n) => n?.field === targetNodeToUpdate?.data?.id)
|
||||
const insertionIndex = placement === 'before' ? index : index + 1
|
||||
parent.children.splice(insertionIndex, 0, draggedNodeToUpdate)
|
||||
} else if (placement === 'inner') {
|
||||
targetNodeToUpdate.children.push(draggedNodeToUpdate)
|
||||
}
|
||||
}
|
||||
if (targetNode) {
|
||||
insertNode(placement)
|
||||
} else {
|
||||
// 如果 targetNode 为空,将被拖拽节点放置为根节点
|
||||
updatedTreeData.push(draggedNodeToUpdate)
|
||||
}
|
||||
|
||||
return updatedTreeData
|
||||
}
|
||||
|
||||
const onNodeDragEnd = (before: any, after: any, inner: string) => {
|
||||
if (inner === 'none') return
|
||||
console.log(before, after, inner)
|
||||
const cloneDeepColumns = cloneDeep(unref(props.columns))
|
||||
const newColumns = updateTreeData(cloneDeepColumns, after, before, inner)
|
||||
console.log(newColumns)
|
||||
}
|
||||
|
||||
const onCheckChange = (data: TableColumn, isChecked: boolean, childrenHasChecked) => {
|
||||
console.log(data, isChecked, childrenHasChecked)
|
||||
}
|
||||
|
||||
return () => (
|
||||
<>
|
||||
<div class="text-right h-28px flex items-center justify-end">
|
||||
|
@ -189,7 +104,7 @@ export default defineComponent({
|
|||
</ElTooltip>
|
||||
|
||||
{/* <ElTooltip content={t('common.columnSetting')} placement="top"> */}
|
||||
<ElPopover trigger="click" placement="left">
|
||||
{/* <ElPopover trigger="click" placement="left">
|
||||
{{
|
||||
default: () => {
|
||||
return (
|
||||
|
@ -223,7 +138,7 @@ export default defineComponent({
|
|||
)
|
||||
}
|
||||
}}
|
||||
</ElPopover>
|
||||
</ElPopover> */}
|
||||
{/* </ElTooltip> */}
|
||||
</div>
|
||||
</>
|
||||
|
|
|
@ -18,7 +18,7 @@ export interface TableColumn {
|
|||
minWidth?: string | number
|
||||
fixed?: boolean | 'left' | 'right'
|
||||
renderHeader?: (...args: any[]) => JSX.Element | null
|
||||
sortable?: boolean | 'custom'
|
||||
// sortable?: boolean
|
||||
sortMethod?: (...args: any[]) => number
|
||||
sortBy?: string | string[] | ((...args: any[]) => string | string[])
|
||||
sortOrders?: (string | null)[]
|
||||
|
|
|
@ -154,14 +154,14 @@ export const useTable = (config: UseTableConfig) => {
|
|||
|
||||
refresh: () => {
|
||||
methods.getList()
|
||||
},
|
||||
|
||||
sortableChange: (e: any) => {
|
||||
console.log('sortableChange', e)
|
||||
const { oldIndex, newIndex } = e
|
||||
dataList.value.splice(newIndex, 0, dataList.value.splice(oldIndex, 1)[0])
|
||||
// to do something
|
||||
}
|
||||
|
||||
// sortableChange: (e: any) => {
|
||||
// console.log('sortableChange', e)
|
||||
// const { oldIndex, newIndex } = e
|
||||
// dataList.value.splice(newIndex, 0, dataList.value.splice(oldIndex, 1)[0])
|
||||
// // to do something
|
||||
// }
|
||||
// // 删除数据
|
||||
// delList: async (ids: string[] | number[], multiple: boolean, message = true) => {
|
||||
// const tableRef = await getTable()
|
||||
|
|
|
@ -302,7 +302,10 @@ export default {
|
|||
// 自定义节点内容
|
||||
customContent: 'Custom content',
|
||||
// 懒加载
|
||||
lazyLoad: 'Lazy load'
|
||||
lazyLoad: 'Lazy load',
|
||||
upload: 'Upload',
|
||||
// 用户头像
|
||||
userAvatar: 'User avatar'
|
||||
},
|
||||
guideDemo: {
|
||||
guide: 'Guide',
|
||||
|
|
|
@ -299,7 +299,9 @@ export default {
|
|||
multiple: '多选',
|
||||
filterable: '可筛选',
|
||||
customContent: '自定义内容',
|
||||
lazyLoad: '懒加载'
|
||||
lazyLoad: '懒加载',
|
||||
upload: '上传',
|
||||
userAvatar: '用户头像'
|
||||
},
|
||||
guideDemo: {
|
||||
guide: '引导页',
|
||||
|
|
|
@ -13,7 +13,10 @@ import {
|
|||
ElRadioButton,
|
||||
ElCheckbox,
|
||||
ElCheckboxButton,
|
||||
ElInput
|
||||
ElInput,
|
||||
ElMessage,
|
||||
ElMessageBox,
|
||||
ElIcon
|
||||
} from 'element-plus'
|
||||
import { getDictOneApi } from '@/api/common'
|
||||
import { Icon } from '@/components/Icon'
|
||||
|
@ -441,6 +444,8 @@ const treeSelectData = [
|
|||
|
||||
let id = 0
|
||||
|
||||
const imageUrl = ref('')
|
||||
|
||||
const schema = reactive<FormSchema[]>([
|
||||
{
|
||||
field: 'field1',
|
||||
|
@ -1663,6 +1668,88 @@ const schema = reactive<FormSchema[]>([
|
|||
},
|
||||
data: treeSelectData
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'field82',
|
||||
component: 'Divider',
|
||||
label: `${t('formDemo.upload')}`
|
||||
},
|
||||
{
|
||||
field: 'field83',
|
||||
component: 'Upload',
|
||||
label: `${t('formDemo.default')}`,
|
||||
componentProps: {
|
||||
limit: 3,
|
||||
action: 'https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15',
|
||||
fileList: [
|
||||
{
|
||||
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'
|
||||
}
|
||||
],
|
||||
multiple: true,
|
||||
onPreview: (uploadFile) => {
|
||||
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`
|
||||
)
|
||||
},
|
||||
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>
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'field84',
|
||||
component: 'Upload',
|
||||
label: `${t('formDemo.userAvatar')}`,
|
||||
componentProps: {
|
||||
action: 'https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15',
|
||||
showFileList: false,
|
||||
onSuccess: (_response, uploadFile) => {
|
||||
imageUrl.value = URL.createObjectURL(uploadFile.raw!)
|
||||
},
|
||||
beforeUpload: (rawFile) => {
|
||||
if (rawFile.type !== 'image/jpeg') {
|
||||
ElMessage.error('Avatar picture must be JPG format!')
|
||||
return false
|
||||
} else if (rawFile.size / 1024 / 1024 > 2) {
|
||||
ElMessage.error('Avatar picture size can not exceed 2MB!')
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
slots: {
|
||||
default: () => (
|
||||
<>
|
||||
{imageUrl.value ? <img src={imageUrl.value} class="avatar" /> : null}
|
||||
{!imageUrl.value ? (
|
||||
<ElIcon class="avatar-uploader-icon" size="large">
|
||||
add
|
||||
</ElIcon>
|
||||
) : null}
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
])
|
||||
</script>
|
||||
|
@ -1714,4 +1801,25 @@ const schema = reactive<FormSchema[]>([
|
|||
padding: 6px 5px;
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.el-upload {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
border: 1px dashed var(--el-border-color);
|
||||
border-radius: 6px;
|
||||
transition: var(--el-transition-duration-fast);
|
||||
}
|
||||
|
||||
.el-upload:hover {
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.el-icon.avatar-uploader-icon {
|
||||
width: 178px;
|
||||
height: 178px;
|
||||
font-size: 28px;
|
||||
color: #8c939d;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -21,8 +21,7 @@ const { tableRegister, tableMethods, tableState } = useTable({
|
|||
}
|
||||
})
|
||||
const { loading, dataList, total, currentPage, pageSize } = tableState
|
||||
const { setProps, setColumn, getElTableExpose, addColumn, delColumn, refresh, sortableChange } =
|
||||
tableMethods
|
||||
const { setProps, setColumn, getElTableExpose, addColumn, delColumn, refresh } = tableMethods
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
|
@ -214,11 +213,6 @@ const getSelections = async () => {
|
|||
const selections = elTableRef?.getSelectionRows()
|
||||
console.log(selections)
|
||||
}
|
||||
|
||||
const sortable = ref(false)
|
||||
const showOrHiddenSortable = () => {
|
||||
sortable.value = !unref(sortable)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -251,7 +245,7 @@ const showOrHiddenSortable = () => {
|
|||
|
||||
<ElButton @click="getSelections">{{ t('tableDemo.getSelections') }}</ElButton>
|
||||
|
||||
<ElButton @click="showOrHiddenSortable">{{ t('tableDemo.showOrHiddenSortable') }}</ElButton>
|
||||
<!-- <ElButton @click="showOrHiddenSortable">{{ t('tableDemo.showOrHiddenSortable') }}</ElButton> -->
|
||||
</ContentWrap>
|
||||
<ContentWrap :title="`UseTable ${t('tableDemo.example')}`">
|
||||
<Table
|
||||
|
@ -261,7 +255,6 @@ const showOrHiddenSortable = () => {
|
|||
:columns="columns"
|
||||
:data="dataList"
|
||||
:loading="loading"
|
||||
:sortable="sortable"
|
||||
:pagination="
|
||||
canShowPagination
|
||||
? {
|
||||
|
@ -271,7 +264,6 @@ const showOrHiddenSortable = () => {
|
|||
"
|
||||
@register="tableRegister"
|
||||
@refresh="refresh"
|
||||
@sortable-change="sortableChange"
|
||||
/>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
|
Loading…
Reference in New Issue