PixelAI-admin/src/views/carousel/index.vue
2024-12-20 15:26:18 +08:00

546 lines
15 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="system-dept-container layout-padding">
<el-card shadow="hover" class="layout-padding-auto">
<!-- <div class="system-dept-search mb15">
<span>名称:</span>
<el-input size="default" placeholder="请输入" v-model="state.tableData.param.name" class="ml10" style="max-width: 250px;margin-right: 20px;"> </el-input>
<el-button @click="getTableData" size="default" type="primary" class="ml10" style="margin-right: 15px;">
<el-icon>
<ele-Search />
</el-icon>
查询
</el-button>
<el-button @click="reset" size="default" type="primary" class="ml10" style="margin-right: 15px;">
<el-icon>
<ele-RefreshRight />
</el-icon>
重置
</el-button> -->
<!-- <el-button @click="switchDrawer(0)" size="default" type="primary" class="ml10">
<el-icon>
<ele-FolderAdd />
</el-icon>
新增轮播图
</el-button> -->
<!-- </div> -->
<el-table :data="state.tableData.data" v-loading="state.tableData.loading" style="width: 100%;" class="custom-table">
<el-table-column prop="name" label="轮播图名称" width="150px" show-overflow-tooltip></el-table-column>
<el-table-column label="素材" align="center">
<template #default="scope">
<el-image :src="encodeURI(viteUrl+scope.row.path)" lazy
preview-teleported="true" :preview-src-list="[encodeURI(viteUrl+scope.row.path)]"/>
<!-- <a :href="jpgFormatter(scope.row)">{{ jpgFormatter(scope.row) }}</a> -->
<!-- <el-button @click="jpgJump(scope.row)" text type="primary">{{ jpgFormatter(scope.row) }}</el-button> -->
</template>
</el-table-column>
<el-table-column label="跳转地址" show-overflow-tooltip align="center">
<template #default="scope">
<!-- <a :href="jpgFormatter(scope.row)">{{ jpgFormatter(scope.row) }}</a> -->
<el-button @click="jpgJump(scope.row)" text type="primary">{{ scope.row.url }}</el-button>
</template>
</el-table-column>
<el-table-column prop="orderNum" label="排序号" sortable show-overflow-tooltip align="center"></el-table-column>
<el-table-column prop="createtime" label="创建时间" :formatter="dateFormatter" show-overflow-tooltip align="center"></el-table-column>
<el-table-column label="操作" width="300" align="center">
<template #default="scope">
<el-button size="small" text type="primary" @click="switchDrawer(scope.row.id,1)">
<el-icon>
<ele-EditPen/>
</el-icon>
</el-button>
<el-button size="small" text type="primary" @click="deleteBanner(scope.row.id)">
<el-icon>
<ele-Delete/>
</el-icon>
</el-button>
</template>
</el-table-column>
</el-table>
<div class="add-banner" @click="switchDrawer(0)">
+ 添加banner
</div>
<!-- 新增或编辑弹窗 -->
<el-drawer
v-model="dialog"
:title="flag?'新增轮播图':'编辑轮播图'"
@close="resetForm"
direction="rtl"
size="45%"
>
<div class="demo-drawer__content">
<el-form
ref="ruleFormRef"
style="max-width: 85%"
:model="state.formData"
:rules="state.rules"
label-width="100px"
class="demo-ruleForm"
:size="formSize"
:label-position="labelPosition"
status-icon
v-loading="state.formData.loading"
>
<el-form-item label="名称:" prop="name">
<el-input v-model="state.formData.name" />
</el-form-item>
<el-form-item label="轮播图:" prop="fileList">
<el-upload ref="uploadRef" action="#" list-type="picture-card" :class="{disabled:uploadDisabled}"
:limit="1" :file-list="state.formData.fileList" :on-change="handleChange" :auto-upload="false">
<el-icon><Plus /></el-icon>
<template #file="{ file }">
<div>
<img class="el-upload-list__item-thumbnail" :src="encodeURI(file.url.includes('http') ?
file.url : viteUrl+file.url)" alt="" />
<span class="el-upload-list__item-actions">
<span
class="el-upload-list__item-preview"
@click="handlePictureCardPreview(file)"
>
<el-icon><zoom-in /></el-icon>
</span>
<span
v-if="!disabled"
class="el-upload-list__item-delete"
@click="handleRemove(file)"
>
<el-icon><Delete /></el-icon>
</span>
</span>
</div>
</template>
</el-upload>
<el-dialog v-model="dialogVisible">
<img :src="dialogImageUrl" style="width: 100%" alt="Preview Image" />
</el-dialog>
</el-form-item>
<el-form-item label="跳转链接:" prop="url">
<el-input v-model="state.formData.url" />
</el-form-item>
<el-form-item label="排序号:" prop="orderNum">
<el-input v-model.number="state.formData.orderNum" />
</el-form-item>
</el-form>
<div class="demo-drawer__footer">
<el-button @click="submitForm(flag)" :loading="loading" style="margin-right: 30px;">
{{ loading ? '保存 ...' : '保存' }}
</el-button>
<el-button type="primary" @click="cancelForm">取消</el-button>
</div>
</div>
</el-drawer>
<!-- 分页器 -->
<!-- <el-pagination
@size-change="onHandleSizeChange"
@current-change="onHandleCurrentChange"
class="mt15"
:pager-count="5"
:page-sizes="[10, 20, 30]"
v-model:current-page="state.tableData.param.current"
background
v-model:page-size="state.tableData.param.size"
layout="total, sizes, prev, pager, next, jumper"
:total="state.tableData.total"
>
</el-pagination> -->
</el-card>
</div>
</template>
<script setup lang="ts" name="carousel">
import { onMounted, reactive, ref } from 'vue';
import { useRouter } from 'vue-router';
import { ElDrawer, ElMessage, ElMessageBox, TableColumnCtx } from 'element-plus';
import { Delete, Download, Plus, ZoomIn } from '@element-plus/icons-vue'
import type { ComponentSize, FormInstance, FormRules, UploadFile, UploadProps, UploadUserFile } from 'element-plus'
import { baseUrlHost } from '../../api/baseUrlHost';
import { bannerApi } from '/@/api/carousel';
const router = useRouter();
// 数据
const state = reactive({
tableData: {
data: [],
total: 0,
loading: false,
param: {
name:'',
current: 1,
size: 10,
},
},
formData: {
fileList: [],
name:"",
path:"",
url: '',
orderNum: "",
loading: false
},
rules: {
name: [
{ required: true, message: '请输入名称', trigger: 'blur' }
],
fileList: [
{ required: true, message: '请选择图片', trigger: 'blur' }
],
orderNum: [
{ required: true, message: '请输入排序号', trigger: 'blur' },
{ type: 'number', message: '排序号必须是数字' },
],
},
});
// #region
// 引入 api 请求接口
const banApi = bannerApi();
// 获取表格数据
const getTableData = async() => {
try {
state.tableData.loading = true;
let res = await banApi.getBannerList(state.tableData.param);
// console.log(res);
if(res?.success){
state.tableData.data = res.data.records;
state.tableData.total = res.data.total;
}else{
ElMessage.error('轮播图列表获取失败!');
}
} catch (error) {
} finally {
state.tableData.loading = false;
}
};
// 重置
// const reset = () =>{
// state.tableData.param = {
// name: '',
// current: 1,
// size: 10,
// }
// getTableData();
// }
// 图片基本路径
const viteUrl = import.meta.env.VITE_API_URL;
// 图片链接格式化
const jpgFormatter = (row: any) => {
let newPath = row?.data ? row.data.path : row.path;
newPath = encodeURI( newPath.includes('http') ? newPath : (viteUrl + newPath));
return `${newPath}`;
};
//轮播图跳转
const jpgJump = (row:any) =>{
window.open(row.url);
};
// 日期格式化
const dateFormatter = (row: any, column: TableColumnCtx<String>) => {
let date = new Date(row.createtime);
return `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`;
}
// 分页改变
// const onHandleSizeChange = (val: number) => {
// state.tableData.param.size = val;
// getTableData();
// };
// // 分页改变
// const onHandleCurrentChange = (val: number) => {
// state.tableData.param.current = val;
// getTableData();
// };
// #endregion
// #region
const dialog = ref(false)
const flag = ref(false)
const formSize = ref<ComponentSize>('large')
const labelPosition = ref<FormProps['labelPosition']>('Left')
const ruleFormRef = ref<FormInstance>()
const uploadDisabled = ref(false)
const switchDrawer = async (id: number, staus) => {
// console.log("switchDrawer",id,state.formData,state.formData.fileList);
dialog.value = true;
if (staus == 1) {
flag.value = false;
state.formData.loading = true;
let res = await banApi.getBannerDetail(id)
// console.log("res",res.data.path);
if (res?.success) {
state.formData.fileList.push({ url: res.data.path });
// console.log("state.formData.fileList",state.formData);
Object.assign(state.formData, res.data);
// console.log("state.formData",state.formData);
uploadDisabled.value = true;
state.formData.loading = false;
// console.log("state.formData",state.formData);
} else {
ElMessage.error('获取轮播图详情失败!')
state.formData.loading = false;
}
} else {
flag.value = true;
}
}
// 添加轮播图
const disabled = ref(false)
const handleChange: UploadProps['onChange'] = async (file: uploadFile, uploadFiles) => {
state.formData.loading = true;
if (file.raw.type === 'image/png' || file.raw.type === 'image/jpeg' || file.raw.type === 'image/jpg') {
state.formData.fileList = uploadFiles;
uploadDisabled.value = true;
try {
const formdata = new FormData();
formdata.append('file', file.raw);
let res = await banApi.uploadFile(formdata);
// console.log("result", res);
// console.log("state.formData", state.formData);
if(res?.success){
// state.formData.url = jpgFormatter(res);
state.formData.path = res.data.path;
ElMessage({
message: '图片上传成功!',
type: 'success',
})
state.formData.loading = false;
}else{
ElMessage.error('图片上传失败!');
}
} catch (error) {
state.formData.loading = false;
console.log(error);
ElMessage.error('图片上传失败!');
}
} else {
ElMessageBox.alert('轮播图的格式不正确,请重新选择!', '提示', {
confirmButtonText: '确定',
})
// console.log("handleChange3",uploadFiles);
uploadFiles.pop()
// console.log("handleChange4",uploadFiles);
uploadDisabled.value = false;
}
}
const dialogImageUrl = ref('')
const dialogVisible = ref(false)
const handlePictureCardPreview = (file: UploadFile,uploadFiles) => {
dialogImageUrl.value = encodeURI(file.url.includes('http') ? file.url : viteUrl+file.url);
dialogVisible.value = true
}
const uploadRef = ref();
const handleRemove = (file: UploadFile, uploadFiles) => {
uploadRef.value.clearFiles();
state.formData.path = "";
uploadDisabled.value = false;
}
// 删除轮播图
const deleteBanner = (id: number) => {
ElMessageBox.confirm('确定要删除该轮播图吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(async() => {
try {
state.tableData.loading = true;
let res = await banApi.deleteBanner(id);
if(res?.success){
await getTableData();
ElMessage.success('轮播图删除成功!');
} else ElMessage.error('轮播图删除失败!');
} catch(e) {
ElMessage.error('删除失败!');
} finally {
state.tableData.loading = false;
}
}).catch(() => {
})
}
const loading = ref(false)
//保存
const submitForm = (decision) => {
ruleFormRef.value?.validate(async(valid: boolean) => {
if (valid) {
if (decision) {
loading.value = true;
const form = { ...state.formData };
try {
let res = await banApi.addBanner(form);
if(res?.success){
ElMessage({
message: '保存成功!',
type: 'success',
})
loading.value = false;
getTableData();
dialog.value = false;
}
} catch (error) {
ElMessage.error('保存失败!');
loading.value = false;
}
} else {
const form = { ...state.formData };
try {
let res = await banApi.updateBanner(form);
if(res?.success){
ElMessage({
message: '保存成功!',
type: 'success',
})
loading.value = false;
getTableData();
dialog.value = false;
}
} catch (error) {
console.log(error);
ElMessage.error('保存失败!');
loading.value = false;
}
}
}else{
ElMessage.error('请填写必要信息!');
loading.value = false;
// dialog.value = false
// loading.value = false
}
});
}
// 表单重置
const resetForm = () => {
ruleFormRef.value?.resetFields();
// state.formData.fileList.pop();
uploadDisabled.value = false;
}
//取消
const cancelForm = () => {
dialog.value = false;
resetForm();
}
// #endregion
onMounted(() => {
getTableData();
})
</script>
<style scoped lang="scss">
.add-banner {
width: 100%; // 让按钮的宽度与表格的行一样
margin: 16px 0;
text-align: center;
color: #409eff;
cursor: pointer;
font-size: 16px;
display: flex;
align-items: center;
justify-content: center;
height: 100px; // 与表格行高度一致
border: 2px dashed #dcdfe6;
border-radius: 8px;
}
.system-dept-container {
:deep(.el-card__body) {
display: flex;
flex-direction: column;
flex: 1;
overflow-y: auto;
max-height: 1000px; // 固定最大高度
padding: 20px;
.el-table {
flex: 1;
width: 100%;
table-layout: fixed; // 表格均分布局
}
// .el-table th, .el-table td {
// text-align: center;
// padding: 12px 8px;
// word-break: break-all;
// }
}
};
::v-deep .el-image {
width: 250px; // 固定图片宽度
height: 150px; // 固定图片高度
object-fit: cover; // 图片居中剪裁
display: block;
}
img[alt="加载失败"] {
content: url('path_to_placeholder_image.png'); // 加载失败时显示占位图片
width: 250px; // 确保占位图片大小一致
height: 100px;
}
::v-deep .el-table__body{
width: 100% !important;
}
::v-deep .el-table__body-wrapper .el-table__row {
margin-bottom: 10px; // 增加底部间距让卡片分隔更明显
border: 1px solid #dcdfe6; // 更宽的边框使其明显
border-radius: 8px; // 圆角
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15); // 更强的阴影
background-color: #fff;
overflow: hidden; // 确保内容被边框包裹住
}
::v-deep .el-drawer .el-drawer__header {
margin: 15px 0 20px 0!important;
border-bottom: 0!important;
}
::v-deep .el-drawer__title {
font-size: 28px!important;
}
::v-deep .el-form-item--large{
--font-size: 18px;
margin-bottom: 28px!important;
}
.demo-drawer__footer{
margin:40px 0 0 150px!important;
}
::v-deep .disabled{
.el-upload--picture-card{
display: none;
}
}
</style>