edu/admin/src/components/material/picker.vue

288 lines
8.6 KiB
Vue
Raw Normal View History

2022-04-08 02:42:44 +00:00
<template>
<div class="material-select">
<popup
ref="popupRef"
2022-08-12 10:44:09 +00:00
width="830px"
2022-04-08 02:42:44 +00:00
custom-class="body-padding"
:title="`选择${tipsText}`"
@confirm="handleConfirm"
2022-08-12 10:44:09 +00:00
@close="handleClose"
2022-04-08 02:42:44 +00:00
>
2022-04-08 06:46:31 +00:00
<template v-if="!hiddenUpload" #trigger>
2022-04-08 02:42:44 +00:00
<div class="material-select__trigger clearfix" @click.stop>
2022-08-12 10:44:09 +00:00
<draggable class="draggable" v-model="fileList" animation="300" item-key="id">
2022-04-08 06:46:31 +00:00
<template v-slot:item="{ element, index }">
2022-04-08 02:42:44 +00:00
<div
class="material-preview"
:class="{
'is-disabled': disabled,
2022-08-12 10:44:09 +00:00
'is-one': limit == 1
2022-04-08 02:42:44 +00:00
}"
@click="showPopup(index)"
>
2022-08-12 10:44:09 +00:00
<del-wrap @close="deleteImg(index)">
<file-item
:uri="element"
:file-size="size"
:type="type"
></file-item>
</del-wrap>
<div class="operation-btns text-xs text-center">
<span>修改</span>
|
<span @click.stop="handlePreview(element)">查看</span>
</div>
2022-04-08 02:42:44 +00:00
</div>
</template>
</draggable>
<div
class="material-upload"
2022-04-08 06:46:31 +00:00
@click="showPopup(-1)"
v-show="showUpload"
2022-04-08 02:42:44 +00:00
:class="{
'is-disabled': disabled,
2022-09-06 03:51:36 +00:00
'is-one': limit == 1,
[uploadClass]: true
2022-04-08 02:42:44 +00:00
}"
>
<slot name="upload">
<div
2022-08-12 10:44:09 +00:00
class="upload-btn"
2022-04-08 02:42:44 +00:00
:style="{
width: size,
2022-08-12 10:44:09 +00:00
height: size
2022-04-08 02:42:44 +00:00
}"
>
2022-08-12 10:44:09 +00:00
<icon :size="25" name="el-icon-Plus" />
2022-04-08 02:42:44 +00:00
<span>添加</span>
</div>
</slot>
</div>
</div>
</template>
2022-08-12 10:44:09 +00:00
<el-scrollbar>
<div class="material-wrap">
<material
ref="materialRef"
:type="type"
:file-size="fileSize"
:limit="meterialLimit"
@change="selectChange"
/>
</div>
</el-scrollbar>
2022-04-08 02:42:44 +00:00
</popup>
2022-08-12 10:44:09 +00:00
<preview v-model="showPreview" :url="previewUrl" :type="type" />
2022-04-08 02:42:44 +00:00
</div>
</template>
<script lang="ts">
import Draggable from 'vuedraggable'
import Popup from '@/components/popup/index.vue'
2022-08-12 10:44:09 +00:00
import FileItem from './file.vue'
import Material from './index.vue'
import Preview from './preview.vue'
2022-04-08 02:42:44 +00:00
export default defineComponent({
components: {
Popup,
Draggable,
FileItem,
2022-04-08 06:46:31 +00:00
Material,
2022-08-12 10:44:09 +00:00
Preview
2022-04-08 02:42:44 +00:00
},
props: {
modelValue: {
type: [String, Array],
2022-08-12 10:44:09 +00:00
default: () => []
2022-04-08 02:42:44 +00:00
},
// 文件类型
type: {
type: String,
2022-08-12 10:44:09 +00:00
default: 'image'
2022-04-08 02:42:44 +00:00
},
// 选择器尺寸
size: {
type: String,
2022-08-12 10:44:09 +00:00
default: '100px'
2022-04-08 02:42:44 +00:00
},
// 文件尺寸
fileSize: {
type: String,
2022-08-12 10:44:09 +00:00
default: '100px'
2022-04-08 02:42:44 +00:00
},
// 选择数量限制
limit: {
2022-08-12 10:44:09 +00:00
type: Number,
default: 1
2022-04-08 02:42:44 +00:00
},
// 禁用选择
disabled: {
type: Boolean,
2022-08-12 10:44:09 +00:00
default: false
2022-04-08 06:46:31 +00:00
},
// 隐藏上传框*(目前在富文本中使用到)
hiddenUpload: {
type: Boolean,
2022-08-12 10:44:09 +00:00
default: false
2022-09-06 03:51:36 +00:00
},
uploadClass: {
type: String,
default: ''
2022-04-08 02:42:44 +00:00
}
},
emits: ['change', 'update:modelValue'],
setup(props, { emit }) {
2022-08-12 10:44:09 +00:00
const popupRef = ref<InstanceType<typeof Popup>>()
const materialRef = ref<InstanceType<typeof Material>>()
const previewUrl = ref('')
const showPreview = ref(false)
const fileList = ref<any[]>([])
const select = ref<any[]>([])
2022-04-08 02:42:44 +00:00
const isAdd = ref(true)
const currentIndex = ref(-1)
const { disabled, limit, modelValue } = toRefs(props)
const tipsText = computed(() => {
switch (props.type) {
case 'image':
return '图片'
case 'video':
return '视频'
2022-08-12 10:44:09 +00:00
default:
return ''
2022-04-08 02:42:44 +00:00
}
})
2022-08-12 10:44:09 +00:00
2022-04-08 02:42:44 +00:00
const showUpload = computed(() => {
return props.limit - fileList.value.length > 0
})
2022-04-08 06:46:31 +00:00
const meterialLimit: any = computed(() => {
2022-04-08 02:42:44 +00:00
if (!isAdd.value) {
return 1
}
2022-08-12 10:44:09 +00:00
if (limit.value == -1) return null
2022-04-08 02:42:44 +00:00
return limit.value - fileList.value.length
})
const handleConfirm = () => {
2022-04-08 06:46:31 +00:00
const selectUri = select.value.map((item) => item.uri)
2022-04-08 02:42:44 +00:00
if (!isAdd.value) {
fileList.value.splice(currentIndex.value, 1, selectUri.shift())
} else {
2022-08-12 10:44:09 +00:00
fileList.value = [...fileList.value, ...selectUri]
2022-04-08 02:42:44 +00:00
}
handleChange()
}
const showPopup = (index: number) => {
2022-04-08 06:46:31 +00:00
if (disabled.value) return
2022-04-08 02:42:44 +00:00
if (index >= 0) {
isAdd.value = false
currentIndex.value = index
} else {
isAdd.value = true
}
popupRef.value?.open()
}
const selectChange = (val: any[]) => {
select.value = val
}
const handleChange = () => {
2022-08-12 10:44:09 +00:00
const valueImg = limit.value != 1 ? fileList.value : fileList.value[0] || ''
2022-04-08 02:42:44 +00:00
emit('update:modelValue', valueImg)
emit('change', valueImg)
2022-08-12 10:44:09 +00:00
handleClose()
2022-04-08 02:42:44 +00:00
}
const deleteImg = (index: number) => {
fileList.value.splice(index, 1)
handleChange()
}
2022-08-12 10:44:09 +00:00
const handlePreview = (url: string) => {
previewUrl.value = url
showPreview.value = true
}
const handleClose = () => {
nextTick(() => {
materialRef.value?.clearSelect()
})
}
watch(
modelValue,
(val: any[] | string) => {
fileList.value = Array.isArray(val) ? val : val == '' ? [] : [val]
},
{
immediate: true
}
)
2022-04-08 02:42:44 +00:00
provide('limit', props.limit)
2022-04-08 06:46:31 +00:00
provide('hiddenUpload', props.hiddenUpload)
2022-04-08 02:42:44 +00:00
return {
popupRef,
2022-08-12 10:44:09 +00:00
materialRef,
2022-04-08 02:42:44 +00:00
fileList,
tipsText,
handleConfirm,
meterialLimit,
showUpload,
showPopup,
selectChange,
2022-08-12 10:44:09 +00:00
deleteImg,
previewUrl,
showPreview,
handlePreview,
handleClose
2022-04-08 02:42:44 +00:00
}
2022-08-12 10:44:09 +00:00
}
2022-04-08 02:42:44 +00:00
})
</script>
<style scoped lang="scss">
.material-select {
.material-upload,
.material-preview {
2022-08-12 10:44:09 +00:00
position: relative;
2022-04-08 02:42:44 +00:00
border-radius: 4px;
cursor: pointer;
margin-right: 8px;
margin-bottom: 8px;
box-sizing: border-box;
float: left;
&.is-disabled {
cursor: not-allowed;
}
&.is-one {
margin-bottom: 0;
}
2022-08-12 10:44:09 +00:00
&:hover {
.operation-btns {
display: block;
}
}
.operation-btns {
display: none;
position: absolute;
bottom: 0;
border-radius: 4px;
width: 100%;
line-height: 2;
color: #fff;
background-color: rgba(0, 0, 0, 0.3);
}
2022-04-08 02:42:44 +00:00
}
.material-upload {
2022-09-06 03:51:36 +00:00
:deep(.upload-btn) {
@apply text-tx-secondary box-border rounded border-br border-dashed border flex flex-col justify-center items-center;
2022-04-08 02:42:44 +00:00
}
}
}
.material-wrap {
2022-08-12 10:44:09 +00:00
min-width: 720px;
height: 430px;
@apply border-t border-b border-br;
2022-04-08 02:42:44 +00:00
}
</style>