🎨 添加图库和客服插件(有问题-待修改)

This commit is contained in:
2025-05-14 16:13:20 +08:00
parent e6ceacfae1
commit 0ed234639c
15 changed files with 3332 additions and 0 deletions

View File

@@ -0,0 +1,456 @@
<template>
<div class="image-library">
<div class="image-library-2" style="width: 180px;;border-right: solid 1px var(--el-border-color);">
<el-tree
:data="categories"
node-key="id"
:props="defaultProps"
@node-click="handleNodeClick"
>
<template #default="{ node, data }">
<span>{{ data.name }}</span>
<span style="float: right; margin-left: 25px">
<el-dropdown @command="(command) => handleCommand(data, command)">
<span class="el-dropdown-link"><b>...</b></span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="add">添加分类</el-dropdown-item>
<el-dropdown-item command="edit" v-if="data.id > 0">编辑分类</el-dropdown-item>
<el-dropdown-item command="delete" v-if="data.id > 0">删除分类</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</span>
</template>
</el-tree>
</div>
<div>
<div class="top-buttons" style="margin-left: 15px;">
<el-button @click="useSelectedImages" type="primary">使用选中的文件</el-button>
<el-button @click="openUploadDialog" type="primary">上传文件</el-button>
<el-button @click="confirmDeleteImages">删除文件</el-button>
<el-input
v-model="keyword"
class="keyword"
placeholder="请输入文件名"
style="margin-left: 10px;"
/>
<el-button
type="primary"
icon="search"
@click="getSearchData"
>查询</el-button>
</div>
<div class="flex flex-wrap gap-4">
<div v-for="image in images" :key="image.ID" class="block">
<el-image v-if="image.tag !== 'mp4'" :src="image.url" @click="toggleImageSelection(image)" :class="{ selected: isSelected(image) }" style="width: 96px;height: 96px;"></el-image>
<video v-else controls style="width: 96%; height: 96px;" @click="toggleImageSelection(image)" :class="{ selected: isSelected(image) }">
<source :src="image.url" />
</video>
<div class="overflow-hidden text-nowrap overflow-ellipsis text-center w-full">{{image.name}}</div>
</div>
</div>
<el-pagination @current-change="pageChange" background layout="prev, pager, next" :page-size="18" :total="totalPage" style="float: right"/>
</div>
</div>
<!-- 添加分类弹窗 -->
<el-dialog v-model="isAddCategoryDialogVisible" @close="closeAddCategoryDialog" title="添加分类">
<el-form>
<el-form-item label="上级分类">
<el-tree-select
v-model="cat_id"
:data="categories"
check-strictly
:props="defaultProps"
:render-after-expand="false"
style="width: 240px"
/>
</el-form-item>
<el-form-item label="分类名称">
<el-input v-model="cat_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>
<!-- 删除确认弹窗 -->
<el-dialog v-model="isDeleteImagesDialogVisible" @close="closeDeleteImagesDialog" title="删除文件" width="350">
<span>确定要删除选中的文件吗</span>
<template #footer>
<el-button @click="closeDeleteImagesDialog">取消</el-button>
<el-button type="danger" @click="deleteImages">确定</el-button>
</template>
</el-dialog>
<!-- 上传图片弹窗 -->
<el-dialog v-model="isUploadDialogVisible" title="上传文件">
<el-form>
<el-form-item label="上传分类">
<el-tree-select
v-model="selectedCategory"
check-strictly
:data="categories"
node-key="id"
:props="defaultProps"
placeholder="请选择分类"
>
<template #default="{ node, data }">
<span>{{ data.name }}</span>
</template>
</el-tree-select>
</el-form-item>
<el-form-item label="选择文件">
<el-upload
ref="uploadRef"
action=""
multiple
:on-change="handleChange"
:on-remove="handleRemove"
:file-list="fileList"
list-type="picture-card"
:auto-upload="false">
<el-icon><Plus /></el-icon>
</el-upload>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="closeUploadDialog">取消</el-button>
<el-button type="primary" @click="submitUpload">确定</el-button>
</template>
</el-dialog>
</template>
<script setup>
import { ref, defineEmits, onMounted } from 'vue';
import { ElMessage } from 'element-plus';
import { Plus } from '@element-plus/icons-vue'
import { getCategoryListApi,getFileListApi,addCategoryApi,deleteCategoryApi,deleteFilesApi,uploadHandlerApi } from '@/plugin/picturelibrary/api/api.js'
import {getUrl, isVideoExt} from "@/utils/image";
const emit = defineEmits(['select']);
const cat_id = ref(0)
const selectedImage = ref(null);
const selectedImages = ref([]);
const images = ref([])
const categories = ref([])
const isAddCategoryDialogVisible = ref(false)
const cat_name = ref('')
const isDeleteImagesDialogVisible = ref(false)
const isUploadDialogVisible = ref(false)
const selectedCategory = ref(null)
const fileList = ref([])
const selectedParentCategory = ref(null)
const totalPage = ref(0)
const editId = ref(0)
const searchCatId = ref(0)
const page = ref(1)
const pageSize = ref(18)
const keyword = ref('')
const props = defineProps({
multiple: Boolean, // 接收父组件传递的multiple参数
});
const defaultProps = {
children: 'children',
label: 'name',
value: 'id'
}
const pageChange = (index) => {
page.value = index
getPicList()
}
const getSearchData = () => {
getPicList()
}
const getPicList = async () => {
try {
const response = await getFileListApi({page:page.value,limit:pageSize.value,cid:searchCatId.value,keyword:keyword.value})
if(response.code === 0){
if(response.data.list){
images.value = response.data.list
totalPage.value = response.data.total
}
}
else{
ElMessage.error(response.msg);
}
} catch (error) {
ElMessage.error('请求失败');
}
}
const fetchCategories = async (name = '顶级分类') => {
let data = {
name: name,
id: 0,
pid: 0,
children:[]
}
try {
const response = await getCategoryListApi({})
if(response.code === 0){
if(response.data.list){
response.data.list.unshift(data)
categories.value = response.data.list
}
else{
categories.value = [data]
}
}
else{
ElMessage.error('分类数据获取失败');
}
} catch (error) {
console.error('Error fetching categories:', error)
}
}
onMounted(() => {
fetchCategories()
getPicList()
})
const toggleImageSelection = (image) => {
const index = selectedImages.value.findIndex(img => img.ID === image.ID);
if (index > -1) {
selectedImages.value.splice(index, 1);
} else {
selectedImages.value.push(image);
}
}
const isSelected = (image) => {
return selectedImages.value.some(img => img.ID === image.ID);
}
const useSelectedImages = () => {
if (selectedImages.value.length > 0) {
if(!props.multiple && selectedImages.value.length > 1){
ElMessage.error('只能选择一个文件');
return
}
const urls = selectedImages.value.map(img => img);
emit('select', urls);
selectedImages.value = []
} else {
ElMessage.error('请选择一个或多个文件');
}
}
const openUploadDialog = () => {
isUploadDialogVisible.value = true
}
const closeUploadDialog = () => {
isUploadDialogVisible.value = false
selectedCategory.value = null
fileList.value = []
}
const fileTypeList = ref(['image/gif', 'image/jpeg', 'image/png', 'video/mp4', 'video/x-msvideo', 'video/x-ms-wmv','video/mpeg','video/x-ms-asf'])
const submitUpload = async () => {
if (selectedCategory.value !== null && fileList.value.length > 0) {
try {
const formData = new FormData();
fileList.value.forEach(file => {
formData.append('files', file.raw);
});
formData.append('cat_id', selectedCategory.value);
try {
const response = await uploadHandlerApi(formData);
if(response.code === 0){
ElMessage.success('文件上传成功');
closeUploadDialog();
selectedCategory.value = null
}
else{
ElMessage.error('文件上传失败');
}
} catch (error) {
ElMessage.error('文件上传失败');
}
} catch (error) {
ElMessage.error('上传失败,请重试');
}
} else {
ElMessage.error('请选择分类和文件');
}
}
const deleteImages = async () => {
if (selectedImages.value.length > 0) {
const response = await deleteFilesApi(selectedImages.value)
if(response.code === 0){
ElMessage.success('删除成功');
selectedImages.value = [];
isDeleteImagesDialogVisible.value = false;
getPicList()
}
else{
ElMessage.error('文件删除失败');
isDeleteImagesDialogVisible.value = false;
}
} else {
ElMessage.error('请选择一张或多张图片');
}
}
const confirmDeleteImages = () => {
if (selectedImages.value.length > 0) {
isDeleteImagesDialogVisible.value = true;
} else {
ElMessage.error('请选择一个或多个文件');
}
}
const addCategory = (cat) => {
isAddCategoryDialogVisible.value = true;
cat_id.value = cat.id
}
const editCategory = (category) => {
cat_id.value = category.pid
cat_name.value = category.name
editId.value = category.id
isAddCategoryDialogVisible.value = true;
}
const deleteCategory = async (category) => {
try {
const response = await deleteCategoryApi({cat_id:category.id})
if(response.code === 0){
ElMessage.success('删除成功');
fetchCategories()
getPicList()
}
else{
ElMessage.error(response.data.msg);
}
} catch (error) {
ElMessage.error('删除分类失败,请重试');
}
}
const closeAddCategoryDialog = () => {
isAddCategoryDialogVisible.value = false
cat_name.value = ''
selectedParentCategory.value = null
}
const confirmAddCategory = async () => {
if (cat_name.value.trim()) {
const newCategory = {
name: cat_name.value.trim(),
pid: cat_id.value,
id:editId.value
};
try {
const response = await addCategoryApi(newCategory);
if(response.code === 0){
ElMessage.success('添加成功');
fetchCategories()
getPicList()
closeAddCategoryDialog();
}
else{
ElMessage.error(response.msg);
}
} catch (error) {
ElMessage.error('分类添加失败,请重试');
}
} else {
ElMessage.error('分类名称不能为空');
}
}
const handleNodeClick = (node) => {
searchCatId.value = node.id
getPicList()
}
const handleCommand = (category,command) => {
if (command === 'add') {
addCategory(category);
} else if (command === 'edit') {
editCategory(category);
} else if (command === 'delete') {
deleteCategory(category);
}
}
const handleChange = (uploadFile, uploadFiles) => {
uploadFiles.forEach(file => {
if(fileTypeList.value.includes(file.raw.type) === false){
ElMessage.error('图片或视频格式有误');
fileList.value = []
}
else{
fileList.value = uploadFiles;
console.log('fileList:', fileList.value.length);
}
});
}
const handleRemove = (file, fileList) => {
// fileList.value = fileList;
console.log('Remove:', file, fileList);
}
const closeDeleteImagesDialog = () => {
isDeleteImagesDialogVisible.value = false
}
const dummyRequest = ({ onSuccess }) => {
setTimeout(() => {
onSuccess('ok');
}, 1000);
}
const uploadRef = ref(null)
</script>
<style scoped>
.image-library {
display: flex;
}
.image-library-2 {
flex: 0 0 auto;
}
.el-dropdown-link {
cursor: pointer;
}
.top-buttons {
display: flex;
justify-content: flex-start;
margin-bottom: 20px;
}
.selected {
border: 3px solid #409eff;
}
.block {
display: flex;
flex-wrap: wrap;
box-sizing: border-box;
vertical-align: top;
padding: 2px;
width: 100px;
height: 100px;
margin-left: 15px;
}
</style>

View File

@@ -0,0 +1,30 @@
<template>
<div class="picturelibrary">
在此处书写页面代码
</div>
</template>
<script setup>
import { ref } from 'vue'
import { routerName } from '@/plugin/picturelibrary/api/api.js'
const data = ref({})
defineOptions({
name: 'PictureLibrary'
})
const useApi = async() =>{
const res = await routerName(data.value)
if(res.code === 0){
console.log(res.data)
}
}
</script>
<style lang="scss" scoped>
.picturelibrary {
}
</style>