* 媒体库增加分类,图库多选择时优化。

* 重构 JWT token 生成,使用 `New()` 函数代替直接创建实例

* 上传组件支持查看大图 (#1982)

* 将参数缓存,媒体库增加分类,图库多选择时优化。 (#1978)

* 媒体库增加分类,图库多选择时优化。

*修复文件上传进度显示bug&按钮样式优化 (#1986)

* fix:添加内部 iframe 展示网页,优化 permission 代码

* 俩个uuid库合并一个,更新库到当前版本。

* 优化关于我们界面

* feat: 个人中心头像调整,媒体库兼容性调整。

* feat: 自动化代码前端页面美化,多余按钮收入专家模式

* feat: 增加单独生成server功能

* feat: 限制单独生成前后端的情况下的细节配置

* feat: 修复全选失败报错的问题

---------

Co-authored-by: task <121913992@qq.com>
Co-authored-by: Feng.YJ <32027253+huiyifyj@users.noreply.github.com>
Co-authored-by: will0523 <dygsunshine@163.com>
Co-authored-by: task <ms.yangdan@gmail.com>
Co-authored-by: sslee <57312216+GIS142857@users.noreply.github.com>
Co-authored-by: bypanghu <bypanghu@163.com>
Co-authored-by: Azir <2075125282@qq.com>
Co-authored-by: piexlMax(奇淼 <qimiaojiangjizhao@gmail.com>
Co-authored-by: krank <emosick@qq.com>
This commit is contained in:
PiexlMax(奇淼
2025-02-13 15:25:10 +08:00
committed by GitHub
co-authored by task Feng.YJ will0523 task sslee bypanghu Azir piexlMax(奇淼 krank
parent ea39d83907
commit d40c815760
61 changed files with 2288 additions and 1529 deletions
-120
View File
@@ -1,120 +0,0 @@
<template>
<div class="profile-avatar relative w-[120px] h-[120px] rounded-full overflow-hidden cursor-pointer group">
<img
v-if="modelValue"
class="w-full h-full object-cover"
:src="getUrl(modelValue)"
alt="头像"
/>
<div
v-else
class="w-full h-full flex flex-col items-center justify-center bg-gray-100 dark:bg-slate-700"
>
<el-icon class="text-2xl text-gray-400">
<avatar />
</el-icon>
<span class="mt-2 text-sm text-gray-400">点击上传</span>
</div>
<div
class="absolute inset-0 bg-black/30 opacity-0 group-hover:opacity-100 flex items-center justify-center transition-all duration-300"
@click="chooseFile"
>
<div class="text-center text-white">
<el-icon class="text-2xl"><camera-filled /></el-icon>
<div class="text-xs mt-1">更换头像</div>
</div>
</div>
<input
ref="fileInput"
type="file"
class="hidden"
accept="image/*"
@change="handleFileChange"
/>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
import { getUrl } from '@/utils/image'
import service from '@/utils/request'
defineOptions({
name: 'ProfileAvatar'
})
const { modelValue } = defineProps({
modelValue: {
type: String,
default: ''
}
})
const emit = defineEmits(['update:modelValue'])
const fileInput = ref(null)
const chooseFile = () => {
fileInput.value.click()
}
const handleFileChange = async (e) => {
const file = e.target.files[0]
if (!file) return
// 验证文件类型
if (!file.type.includes('image/')) {
ElMessage.error('请选择图片文件')
return
}
// 验证文件大小(限制为2MB)
if (file.size > 2 * 1024 * 1024) {
ElMessage.error('图片大小不能超过2MB')
return
}
try {
const formData = new FormData()
formData.append('file', file)
const res = await service({
url: '/fileUploadAndDownload/upload',
method: 'post',
data: formData,
headers: {
'Content-Type': 'multipart/form-data'
}
})
if (res.code === 0 && res.data?.file?.url) {
emit('update:modelValue', res.data.file.url)
ElMessage.success('头像上传成功')
} else {
ElMessage.error(res.msg || '上传失败')
}
} catch {
ElMessage.error('头像上传失败,请重试')
} finally {
// 清空input,确保可以重复选择同一文件
e.target.value = ''
}
}
</script>
<style lang="scss" scoped>
.profile-avatar {
img {
@apply transition-transform duration-300;
}
&:hover {
img {
@apply scale-110;
}
}
}
</style>
+3 -2
View File
@@ -54,7 +54,8 @@
const options = reactive([])
const deepMenus = (menus) => {
const arr = []
menus.forEach((menu) => {
menus?.forEach((menu) => {
if (!menu?.children) return
if (menu.children && menu.children.length > 0) {
arr.push(...deepMenus(menu.children))
} else {
@@ -77,7 +78,7 @@
label: '跳转',
children: []
}
const menus = deepMenus(routerStore.asyncRouters[0].children)
const menus = deepMenus(routerStore.asyncRouters[0]?.children || [])
option.children.push(...menus)
options.push(option)
}
@@ -1,59 +1,68 @@
<template>
<div
class="w-40 h-40 relative rounded border border-dashed border-gray-300 overflow-hidden cursor-pointer group"
class="w-40 h-40 relative rounded border border-dashed border-gray-300 cursor-pointer group"
:class="rounded ? 'rounded-full' : ''"
>
<el-icon
v-if="isVideoExt(model || '')"
:size="32"
class="absolute top-[calc(50%-16px)] left-[calc(50%-16px)]"
>
<VideoPlay />
</el-icon>
<video
v-if="isVideoExt(model || '')"
class="w-full h-full object-cover"
muted
preload="metadata"
>
<source :src="getUrl(model) + '#t=1'" />
</video>
<div class="w-full h-full overflow-hidden" :class="rounded ? 'rounded-full' : ''">
<el-icon
v-if="isVideoExt(model || '')"
:size="32"
class="absolute top-[calc(50%-16px)] left-[calc(50%-16px)]"
>
<VideoPlay />
</el-icon>
<video
v-if="isVideoExt(model || '')"
class="w-full h-full object-cover"
muted
preload="metadata"
>
<source :src="getUrl(model) + '#t=1'" />
</video>
<img
v-if="model && !isVideoExt(model)"
class="w-full h-full"
:src="getUrl(model)"
alt="图片"
/>
<div
v-if="model"
class="left-0 top-0 hidden text-gray-600 group-hover:bg-gray-600 group-hover:bg-opacity-30 w-full h-full group-hover:flex justify-center items-center absolute z-10"
@click="deleteItem"
>
<el-icon>
<delete />
</el-icon>
删除
<el-image
v-if="model && !isVideoExt(model)"
class="w-full h-full"
:src="imgUrl"
:preview-src-list="srcList"
fit="cover"
/>
<div
v-else
class="text-gray-600 group-hover:bg-gray-200 group-hover:opacity-60 w-full h-full flex justify-center items-center"
@click="chooseItem"
>
<el-icon>
<plus />
</el-icon>
上传
</div>
</div>
<!-- 删除按钮在外层容器中 -->
<div
v-else
class="text-gray-600 group-hover:bg-gray-400 w-full h-full flex justify-center items-center"
@click="chooseItem"
v-if="model"
class="right-0 top-0 hidden text-gray-400 group-hover:flex justify-center items-center absolute z-10"
@click="deleteItem"
>
<el-icon>
<plus />
<el-icon :size="24">
<CircleCloseFilled />
</el-icon>
上传
</div>
</div>
</template>
<script setup>
import { getUrl, isVideoExt } from '@/utils/image'
import { Delete, Plus } from '@element-plus/icons-vue'
import { CircleCloseFilled, Plus } from '@element-plus/icons-vue'
import { computed } from 'vue'
defineProps({
const props = defineProps({
model: {
default: '',
type: String
},
rounded: {
default: false,
type: Boolean
}
})
@@ -66,4 +75,12 @@
const deleteItem = () => {
emits('deleteItem')
}
const imgUrl = computed(() => {
return getUrl(props.model)
})
const srcList = computed(() => {
return imgUrl.value ? [imgUrl.value] : []
})
</script>
+412 -250
View File
@@ -1,290 +1,452 @@
<template>
<div>
<selectComponent
v-if="!props.multiple"
:model="model"
@chooseItem="openChooseImg"
@deleteItem="openChooseImg"
/>
<selectComponent :rounded="rounded" v-if="!props.multiple" :model="model" @chooseItem="openChooseImg" @deleteItem="openChooseImg" />
<div v-else class="w-full gap-4 flex flex-wrap">
<selectComponent
v-for="(item, index) in model"
:key="index"
:model="item"
@chooseItem="openChooseImg"
@deleteItem="deleteImg(index)"
<selectComponent :rounded="rounded" v-for="(item, index) in model" :key="index" :model="item" @chooseItem="openChooseImg"
@deleteItem="deleteImg(index)"
/>
<selectComponent
v-if="
model?.length < props.maxUpdateCount || props.maxUpdateCount === 0
"
@chooseItem="openChooseImg"
@deleteItem="openChooseImg"
<selectComponent :rounded="rounded" v-if="model.length < props.maxUpdateCount || props.maxUpdateCount === 0"
@chooseItem="openChooseImg" @deleteItem="openChooseImg"
/>
</div>
<el-drawer v-model="drawer" title="媒体库" :size="appStore.drawerSize">
<warning-bar title="点击“文件名/备注”可以编辑文件名或者备注内容。" />
<div class="gva-btn-list gap-2">
<upload-common :image-common="imageCommon" @on-success="getImageList" />
<upload-image
:image-url="imageUrl"
:file-size="512"
:max-w-h="1080"
@on-success="getImageList"
/>
<el-input
v-model="search.keyword"
class="keyword"
placeholder="请输入文件名或备注"
/>
<el-button type="primary" icon="search" @click="getImageList">
查询
</el-button>
</div>
<div class="flex flex-wrap gap-4">
<div v-for="(item, key) in picList" :key="key" class="w-40">
<div
class="w-40 h-40 border rounded overflow-hidden border-dashed border-gray-300 cursor-pointer relative group"
>
<el-image
:key="key"
:src="getUrl(item.url)"
fit="cover"
class="w-full h-full relative"
@click="chooseImg(item.url)"
<el-drawer v-model="drawer" title="媒体库" :size="880">
<div class="flex">
<div class="w-64" style="border-right: solid 1px var(--el-border-color);">
<el-scrollbar style="height: calc(100vh - 110px)">
<el-tree
:data="categories"
node-key="id"
:props="defaultProps"
@node-click="handleNodeClick"
default-expand-all
>
<template #error>
<el-icon
v-if="isVideoExt(item.url || '')"
:size="32"
class="absolute top-[calc(50%-16px)] left-[calc(50%-16px)]"
>
<VideoPlay />
</el-icon>
<video
v-if="isVideoExt(item.url || '')"
class="w-full h-full object-cover"
muted
preload="metadata"
@click="chooseImg(item.url)"
>
<source :src="getUrl(item.url) + '#t=1'" />
您的浏览器不支持视频播放
</video>
<div
v-else
class="w-full h-full object-cover flex items-center justify-center"
>
<el-icon :size="32">
<icon-picture />
<template #default="{ node, data }">
<div class="w-36" :class="search.classId === data.ID ? 'text-blue-500 font-bold' : ''">{{ data.name }}
</div>
<el-dropdown>
<el-icon class="ml-3 text-right" v-if="data.ID > 0"><MoreFilled /></el-icon>
<el-icon class="ml-3 text-right mt-1" v-else><Plus /></el-icon>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item @click="addCategoryFun(data)">添加分类</el-dropdown-item>
<el-dropdown-item @click="editCategory(data)" v-if="data.ID > 0">编辑分类</el-dropdown-item>
<el-dropdown-item @click="deleteCategoryFun(data.ID)" v-if="data.ID > 0">删除分类</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
</el-tree>
</el-scrollbar>
</div>
<div class="ml-4 image-library">
<warning-bar title="点击“文件名”可以编辑;选择的类别即是上传的类别。" />
<div class="gva-btn-list gap-2">
<el-button @click="useSelectedImages" type="danger" :disabled="selectedImages.length === 0" :icon="ArrowLeftBold">确认所选</el-button>
<upload-common :image-common="imageCommon" :classId="search.classId" @on-success="onSuccess" />
<upload-image :image-url="imageUrl" :file-size="2048" :max-w-h="1080" :classId="search.classId" @on-success="onSuccess" />
<el-input v-model.trim="search.keyword" class="w-52" placeholder="请输入文件名或备注" clearable />
<el-button type="primary" icon="search" @click="onSubmit"> 查询</el-button>
</div>
<div class="flex flex-wrap gap-4">
<div v-for="(item,key) in picList" :key="key" class="w-40">
<div class="w-40 h-40 border rounded overflow-hidden border-dashed border-gray-300 cursor-pointer relative group">
<el-image :key="key" :src="getUrl(item.url)" fit="cover" class="w-full h-full relative" @click="toggleImageSelection(item)" :class="{ selected: isSelected(item) }">
<template #error>
<el-icon v-if="isVideoExt(item.url || '')" :size="32" class="absolute top-[calc(50%-16px)] left-[calc(50%-16px)]">
<VideoPlay />
</el-icon>
<video v-if="isVideoExt(item.url || '')"
class="w-full h-full object-cover"
muted
preload="metadata"
@click="toggleImageSelection(item)"
:class="{ selected: isSelected(item) }"
>
<source :src="getUrl(item.url) + '#t=1'">
您的浏览器不支持视频播放
</video>
<div v-else class="w-full h-full object-cover flex items-center justify-center">
<el-icon :size="32">
<icon-picture />
</el-icon>
</div>
</template>
</el-image>
<div class="absolute -right-1 top-1 w-8 h-8 group-hover:inline-block hidden" @click="deleteCheck(item)">
<el-icon :size="18">
<CloseBold />
</el-icon>
</div>
</template>
</el-image>
<div
class="absolute -right-1 top-1 w-8 h-8 group-hover:inline-block hidden"
@click="deleteCheck(item)"
>
<el-icon :size="16"><CircleClose /></el-icon>
</div>
<div class="overflow-hidden text-nowrap overflow-ellipsis text-center w-full cursor-pointer" @click="editFileNameFunc(item)">
{{ item.name }}
</div>
</div>
</div>
<div
class="overflow-hidden text-nowrap overflow-ellipsis text-center w-full"
@click="editFileNameFunc(item)"
>
{{ item.name }}
</div>
<el-pagination
:current-page="page"
:page-size="pageSize"
:total="total"
class="justify-center"
layout="total, prev, pager, next, jumper"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
/>
</div>
</div>
<el-pagination
:current-page="page"
:page-size="pageSize"
:total="total"
:style="{ 'justify-content': 'center' }"
layout="total, prev, pager, next, jumper"
@current-change="handleCurrentChange"
@size-change="handleSizeChange"
/>
</el-drawer>
<!-- 添加分类弹窗 -->
<el-dialog v-model="categoryDialogVisible" @close="closeAddCategoryDialog" width="520"
:title="(categoryFormData.ID === 0 ? '添加' : '编辑') + '分类'"
draggable
>
<el-form ref="categoryForm" :rules="rules" :model="categoryFormData" label-width="80px">
<el-form-item label="上级分类">
<el-tree-select
v-model="categoryFormData.pid"
:data="categories"
check-strictly
:props="defaultProps"
:render-after-expand="false"
style="width: 240px"
/>
</el-form-item>
<el-form-item label="分类名称" prop="name">
<el-input v-model.trim="categoryFormData.name" placeholder="分类名称"></el-input>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="closeAddCategoryDialog">取消</el-button>
<el-button type="primary" @click="confirmAddCategory">确定</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { getUrl, isVideoExt } from '@/utils/image'
import { ref } from 'vue'
import {
getFileList,
editFileName,
deleteFile
} from '@/api/fileUploadAndDownload'
import UploadImage from '@/components/upload/image.vue'
import UploadCommon from '@/components/upload/common.vue'
import WarningBar from '@/components/warningBar/warningBar.vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Picture as IconPicture } from '@element-plus/icons-vue'
import selectComponent from '@/components/selectImage/selectComponent.vue'
import { useAppStore } from "@/pinia";
import { getUrl, isVideoExt } from '@/utils/image'
import { ref } from 'vue'
import { getFileList, editFileName, deleteFile } from '@/api/fileUploadAndDownload'
import UploadImage from '@/components/upload/image.vue'
import UploadCommon from '@/components/upload/common.vue'
import WarningBar from '@/components/warningBar/warningBar.vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import {
ArrowLeftBold,
CloseBold,
MoreFilled,
Picture as IconPicture,
Plus,
VideoPlay
} from '@element-plus/icons-vue'
import selectComponent from '@/components/selectImage/selectComponent.vue'
import { addCategory, deleteCategory, getCategoryList } from '@/api/attachmentCategory'
const appStore = useAppStore()
const imageUrl = ref('')
const imageCommon = ref('')
const imageUrl = ref('')
const imageCommon = ref('')
const search = ref({
keyword: null,
classId: 0
})
const page = ref(1)
const total = ref(0)
const pageSize = ref(20)
const search = ref({})
const page = ref(1)
const total = ref(0)
const pageSize = ref(20)
const model = defineModel({ type: [String, Array] })
const model = defineModel({ type: [String, Array] })
const props = defineProps({
multiple: {
type: Boolean,
default: false
},
fileType: {
type: String,
default: ''
},
maxUpdateCount: {
type: Number,
default: 0
},
rounded: {
type: Boolean,
default: false
}
})
const props = defineProps({
multiple: {
type: Boolean,
default: false
},
fileType: {
type: String,
default: ''
},
maxUpdateCount: {
type: Number,
default: 0
const deleteImg = (index) => {
model.value.splice(index, 1)
}
const handleSizeChange = (val) => {
pageSize.value = val
getImageList()
}
const handleCurrentChange = (val) => {
page.value = val
getImageList()
}
const onSubmit = () => {
search.value.classId = 0
page.value = 1
getImageList()
}
const editFileNameFunc = async(row) => {
ElMessageBox.prompt('请输入文件名或者备注', '编辑', {
confirmButtonText: '确定',
cancelButtonText: '取消',
inputPattern: /\S/,
inputErrorMessage: '不能为空',
inputValue: row.name
}).then(async({ value }) => {
row.name = value
const res = await editFileName(row)
if (res.code === 0) {
ElMessage({
type: 'success',
message: '编辑成功!'
})
await getImageList()
}
})
const deleteImg = (index) => {
model.value.splice(index, 1)
}
const handleSizeChange = (val) => {
pageSize.value = val
getImageList()
}
const handleCurrentChange = (val) => {
page.value = val
getImageList()
}
const editFileNameFunc = async (row) => {
ElMessageBox.prompt('请输入文件名或者备注', '编辑', {
confirmButtonText: '确定',
cancelButtonText: '取消',
inputPattern: /\S/,
inputErrorMessage: '不能为空',
inputValue: row.name
}).catch(() => {
ElMessage({
type: 'info',
message: '取消修改'
})
.then(async ({ value }) => {
row.name = value
const res = await editFileName(row)
if (res.code === 0) {
ElMessage({
type: 'success',
message: '编辑成功!'
})
getImageList()
}
})
.catch(() => {
ElMessage({
type: 'info',
message: '取消修改'
})
})
}
})
}
const drawer = ref(false)
const picList = ref([])
const drawer = ref(false)
const picList = ref([])
const imageTypeList = ['png', 'jpg', 'jpeg', 'gif', 'bmp', 'webp']
const videoTyteList = [
'mp4',
'avi',
'rmvb',
'rm',
'asf',
'divx',
'mpg',
'mpeg',
'mpe',
'wmv',
'mkv',
'vob'
]
const imageTypeList = ['png', 'jpg', 'jpeg', 'gif', 'bmp', 'webp', 'svg']
const videoTypeList = ['mp4', 'avi', 'rmvb', 'rm', 'asf', 'divx', 'mpg', 'mpeg', 'mpe', 'wmv', 'mkv', 'vob']
const listObj = {
image: imageTypeList,
video: videoTyteList
}
const listObj = {
image: imageTypeList,
video: videoTypeList
}
const chooseImg = (url) => {
if (props.fileType) {
const typeSuccess = listObj[props.fileType].some((item) => {
if (url?.toLowerCase().includes(item)) {
return true
}
})
if (!typeSuccess) {
ElMessage({
type: 'error',
message: '当前类型不支持使用'
})
return
const chooseImg = (url) => {
if (props.fileType) {
const typeSuccess = listObj[props.fileType].some(item => {
if (url?.toLowerCase().includes(item)) {
return true
}
}
if (props.multiple) {
model.value.push(url)
} else {
model.value = url
}
drawer.value = false
}
const openChooseImg = async () => {
if (model.value && !props.multiple) {
model.value = ''
})
if (!typeSuccess) {
ElMessage({
type: 'error',
message: '当前类型不支持使用'
})
return
}
await getImageList()
drawer.value = true
}
//if (props.multiple) {
// model.value.push(url)
//} else {
model.value = url
//}
drawer.value = false
}
const getImageList = async () => {
const res = await getFileList({
page: page.value,
pageSize: pageSize.value,
...search.value
})
const openChooseImg = async() => {
if (model.value && !props.multiple) {
model.value = ''
return
}
await getImageList()
await fetchCategories()
drawer.value = true
}
const getImageList = async() => {
const res = await getFileList({ page: page.value, pageSize: pageSize.value, ...search.value })
if (res.code === 0) {
picList.value = res.data.list
total.value = res.data.total
page.value = res.data.page
pageSize.value = res.data.pageSize
}
}
const deleteCheck = (item) => {
ElMessageBox.confirm('是否删除该文件', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(async() => {
const res = await deleteFile(item)
if (res.code === 0) {
picList.value = res.data.list
total.value = res.data.total
page.value = res.data.page
pageSize.value = res.data.pageSize
ElMessage({
type: 'success',
message: '删除成功!'
})
await getImageList()
}
}
const deleteCheck = (item) => {
ElMessageBox.confirm('是否删除该文件', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).catch(() => {
ElMessage({
type: 'info',
message: '已取消删除'
})
.then(async () => {
const res = await deleteFile(item)
if (res.code === 0) {
ElMessage({
type: 'success',
message: '删除成功!'
})
getImageList()
}
})
.catch(() => {
ElMessage({
type: 'info',
message: '已取消删除'
})
})
})
}
const defaultProps = {
children: 'children',
label: 'name',
value: 'ID'
}
const categories = ref([])
const fetchCategories = async() => {
const res = await getCategoryList()
let data = {
name: '全部分类',
ID: 0,
pid: 0,
children:[]
}
if (res.code === 0) {
categories.value = res.data || []
categories.value.unshift(data)
}
}
const handleNodeClick = (node) => {
search.value.keyword = null
search.value.classId = node.ID
page.value = 1
getImageList()
}
const onSuccess = () => {
search.value.keyword = null
page.value = 1
getImageList()
}
const categoryDialogVisible = ref(false)
const categoryFormData = ref({
ID: 0,
pid: 0,
name: ''
})
const categoryForm = ref(null)
const rules = ref({
name: [
{ required: true, message: '请输入分类名称', trigger: 'blur' },
{ max: 20, message: '最多20位字符', trigger: 'blur' }
]
})
const addCategoryFun = (category) => {
categoryDialogVisible.value = true
categoryFormData.value.ID = 0
categoryFormData.value.pid = category.ID
}
const editCategory = (category) => {
categoryFormData.value = {
ID: category.ID,
pid: category.pid,
name: category.name
}
categoryDialogVisible.value = true
}
const deleteCategoryFun = async(id) => {
const res = await deleteCategory({ id: id })
if (res.code === 0) {
ElMessage.success({ type: 'success', message: '删除成功' })
await fetchCategories()
}
}
const confirmAddCategory = async() => {
categoryForm.value.validate(async valid => {
if (valid) {
const res = await addCategory(categoryFormData.value)
if (res.code === 0) {
ElMessage({ type: 'success', message: '操作成功' })
await fetchCategories()
closeAddCategoryDialog()
}
}
})
}
const closeAddCategoryDialog = () => {
categoryDialogVisible.value = false
categoryFormData.value = {
ID: 0,
pid: 0,
name: ''
}
}
const selectedImages = ref([])
const toggleImageSelection = (item) => {
if (props.multiple === false) {
chooseImg(item.url)
return
}
const index = selectedImages.value.findIndex(img => img.ID === item.ID)
if (index > -1) {
selectedImages.value.splice(index, 1)
} else {
selectedImages.value.push(item)
}
}
const isSelected = (item) => {
return selectedImages.value.some(img => img.ID === item.ID)
}
const useSelectedImages = () => {
selectedImages.value.forEach((item) => {
model.value.push(item.url)
})
drawer.value = false
selectedImages.value = []
}
</script>
<style scoped>
.selected {
border: 3px solid #409eff;
}
.image-library {
width: 605px;
}
.selected:before {
content: "";
position: absolute;
left: 0;
top: 0;
border: 10px solid #409eff;
}
.selected:after {
content: "";
width: 9px;
height: 14px;
position: absolute;
left: 6px;
top: 0;
border: 3px solid #fff;
border-top-color: transparent;
border-left-color: transparent;
transform: rotate(45deg);
}
</style>
+10 -1
View File
@@ -6,10 +6,11 @@
:on-error="uploadError"
:on-success="uploadSuccess"
:show-file-list="false"
:data="{'classId': props.classId}"
multiple
class="upload-btn"
>
<el-button type="primary">普通上传</el-button>
<el-button type="primary" :icon="Upload">普通上传</el-button>
</el-upload>
</div>
</template>
@@ -19,11 +20,19 @@
import { ElMessage } from 'element-plus'
import { isVideoMime, isImageMime } from '@/utils/image'
import { getBaseUrl } from '@/utils/format'
import {Upload} from "@element-plus/icons-vue";
defineOptions({
name: 'UploadCommon'
})
const props = defineProps({
classId: {
type: Number,
default: 0
}
})
const emit = defineEmits(['on-success'])
const fullscreenLoading = ref(false)
+7 -1
View File
@@ -6,8 +6,9 @@
:on-success="handleImageSuccess"
:before-upload="beforeImageUpload"
:multiple="false"
:data="{'classId': props.classId}"
>
<el-button type="primary">压缩上传</el-button>
<el-button type="primary" :icon="Upload">压缩上传</el-button>
</el-upload>
</div>
</template>
@@ -16,6 +17,7 @@
import ImageCompress from '@/utils/image'
import { ElMessage } from 'element-plus'
import { getBaseUrl } from '@/utils/format'
import {Upload} from "@element-plus/icons-vue";
defineOptions({
name: 'UploadImage'
@@ -34,6 +36,10 @@
maxWH: {
type: Number,
default: 1920 // 图片长宽上限
},
classId: {
type: Number,
default: 0
}
})