5 changed files with 555 additions and 69 deletions
-
2.env.development
-
7package-lock.json
-
18src/api/member.js
-
73src/views/admin/landingDetail.vue
-
520src/views/admin/landingManagement.vue
@ -0,0 +1,520 @@ |
|||
<template> |
|||
<div class="page-container"> |
|||
<!-- 顶栏容器 --> |
|||
<div class="top-bar"> |
|||
<span>落地页管理</span> |
|||
</div> |
|||
|
|||
<!-- 主体容器(包含侧边栏和主内容) --> |
|||
<div class="main-container"> |
|||
<!-- 侧边栏容器(目录) --> |
|||
<div class="sidebar"> |
|||
<el-menu |
|||
default-active="1" |
|||
class="sidebar-menu" |
|||
background-color="#f5f7fa" |
|||
text-color="#333" |
|||
active-text-color="#1890ff" |
|||
> |
|||
<el-menu-item index="1"> |
|||
<i class="el-icon-document"></i> |
|||
<span slot="title">落地页管理</span> |
|||
</el-menu-item> |
|||
</el-menu> |
|||
</div> |
|||
|
|||
<!-- 主要区域容器(表格内容) --> |
|||
<div class="content-area"> |
|||
<!-- 内容头部 --> |
|||
<div class="content-header"> |
|||
<el-button type="primary" icon="el-icon-plus" @click="openAddDialog">+新增落地页</el-button> |
|||
</div> |
|||
|
|||
<!-- 表格区域 --> |
|||
<el-table :data="tableData" stripe v-loading="loading" style="width: 100%; margin-bottom: 20px; border-top: 1px solid #e8e8e8;" > |
|||
<el-table-column |
|||
type="index" |
|||
label="序号" |
|||
width="80" |
|||
align="center" |
|||
></el-table-column> |
|||
<el-table-column |
|||
prop="name" |
|||
label="活动名称" |
|||
min-width="100" |
|||
align="center" |
|||
></el-table-column> |
|||
<el-table-column |
|||
prop="introduction" |
|||
label="活动简介" |
|||
min-width="220" |
|||
align="center" |
|||
></el-table-column> |
|||
<el-table-column label="活动时间" min-width="220" align="center"> |
|||
<template #default="{ row }"> |
|||
{{row.start_time}} - {{ row.end_time }} |
|||
</template> |
|||
</el-table-column> |
|||
|
|||
<el-table-column |
|||
prop="updated_at" |
|||
label="编辑时间" |
|||
min-width="180" |
|||
align="center" |
|||
></el-table-column> |
|||
<el-table-column |
|||
label="操作" |
|||
width="280" |
|||
align="center" |
|||
> |
|||
<template v-slot:default="scope"> |
|||
<el-button type="text" size="small" :icon="Edit" @click="openEditDialog(scope.row)">编辑</el-button> |
|||
<el-button type="text" size="small" :icon="View" @click="detail(scope.row.id)">详情</el-button> |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
|
|||
<!-- 分页区域 --> |
|||
<div class="pagination-container"> |
|||
<el-pagination |
|||
@size-change="handleSizeChange" |
|||
@current-change="handleCurrentChange" |
|||
:current-page="currentPage" |
|||
:page-sizes="[10, 20, 50]" |
|||
:page-size="pageSize" |
|||
layout="total, sizes, prev, pager, next, jumper" |
|||
:total="totalCount" |
|||
></el-pagination> |
|||
</div> |
|||
|
|||
<!-- 新增:添加/编辑活动弹窗 --> |
|||
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="600px" :before-close="handleDialogClose"> |
|||
<el-form ref="formRef" :model="form" :rules="formRules" label-width="100px"> |
|||
<!-- 活动名称 --> |
|||
<el-form-item label="活动名称" prop="name"> |
|||
<el-input v-model="form.name" placeholder="请输入活动名称" /> |
|||
</el-form-item> |
|||
|
|||
<!-- 活动简介 --> |
|||
<el-form-item label="活动简介" prop="introduction"> |
|||
<el-input |
|||
v-model="form.introduction" |
|||
type="textarea" |
|||
placeholder="请输入活动简介" |
|||
:maxlength="80" |
|||
show-word-limit |
|||
/> |
|||
<div class="intro-tip">活动简介会给分享后客户展示,请注意填写内容</div> |
|||
</el-form-item> |
|||
|
|||
<!-- 活动时间 --> |
|||
<el-form-item label="活动时间" prop="time"> |
|||
<el-date-picker |
|||
v-model="form.startTime" |
|||
type="datetime" |
|||
placeholder="请选择开始时间" |
|||
style="width: 200px" |
|||
/> |
|||
<span class="time-separator">至</span> |
|||
<el-date-picker |
|||
v-model="form.endTime" |
|||
type="datetime" |
|||
placeholder="请选择结束时间" |
|||
style="width: 200px" |
|||
/> |
|||
</el-form-item> |
|||
|
|||
<!-- 活动落地页(宽度≤375px) --> |
|||
<el-form-item label="活动落地页" prop="landingPage"> |
|||
<el-upload |
|||
class="upload-demo" |
|||
action="#" |
|||
:on-change="handleLandingPageUpload" |
|||
:show-file-list="false" |
|||
> |
|||
<el-button type="primary" icon="Plus">上传</el-button> |
|||
<template #tip> |
|||
<div class="upload-tip"> |
|||
宽度≤375像素,支持PNG、JPG、GIF格式,图片需小于2M |
|||
</div> |
|||
</template> |
|||
</el-upload> |
|||
</el-form-item> |
|||
|
|||
<!-- 落地页弹窗(宽度≤375px) --> |
|||
<el-form-item label="落地页弹窗" prop="popup"> |
|||
<el-upload |
|||
class="upload-demo" |
|||
action="#" |
|||
:limit="1" |
|||
:on-change="handlePopupUpload" |
|||
list-type="picture" |
|||
:file-list="form.popupFiles" |
|||
> |
|||
<el-button type="primary" icon="Plus">上传</el-button> |
|||
<template #tip> |
|||
<div class="upload-tip"> |
|||
宽度≤375像素,支持PNG、JPG格式,图片需小于2M |
|||
</div> |
|||
</template> |
|||
</el-upload> |
|||
</el-form-item> |
|||
</el-form> |
|||
|
|||
<template #footer> |
|||
<div class="dialog-footer"> |
|||
<el-button @click="dialogVisible = false">取消</el-button> |
|||
<el-button type="primary" @click="submitForm">确定</el-button> |
|||
</div> |
|||
</template> |
|||
</el-dialog> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import { getLandingListApi, addLandingApi } from '../../api/member.js'; |
|||
import { ElMessage } from 'element-plus'; |
|||
import axios from 'axios'; |
|||
export default { |
|||
name: 'LandingList', |
|||
data() { |
|||
return { |
|||
// 表格数据 |
|||
tableData: [], |
|||
// 分页参数 |
|||
currentPage: 1, |
|||
pageSize: 10, |
|||
// 总数据量 |
|||
totalCount: 0, |
|||
// 加载状态 |
|||
loading: false, |
|||
// 弹窗显示状态 |
|||
dialogVisible: false, |
|||
// 弹窗标题 |
|||
dialogTitle: '', |
|||
// 表单数据 |
|||
form: { |
|||
id: '', |
|||
name: '', |
|||
introduction: '', |
|||
startTime: '', |
|||
endTime: '', |
|||
landingPage: '', |
|||
popup: '', |
|||
landingPageFiles: [], // 存储已上传的落地页图片 |
|||
popupFiles: [] // 存储已上传的弹窗图片 |
|||
}, |
|||
// 表单校验规则 |
|||
formRules: { |
|||
name: [{ required: true, message: '请输入活动名称', trigger: 'blur' }], |
|||
introduction: [{ required: true, message: '请输入活动简介', trigger: 'blur' }], |
|||
startTime: [{ required: true, message: '请选择开始时间', trigger: 'change' }], |
|||
endTime: [{ required: true, message: '请选择结束时间', trigger: 'change' }], |
|||
landingPage: [{ required: true, message: '请上传活动落地页', trigger: 'change' }], |
|||
popup: [{ required: true, message: '请上传落地页弹窗', trigger: 'change' }] |
|||
} |
|||
} |
|||
}, |
|||
mounted() { |
|||
this.getLandingList(); |
|||
}, |
|||
methods: { |
|||
// 获取落地页管理列表数据 |
|||
async getLandingList() { |
|||
// 显示加载状态 |
|||
this.loading = true; |
|||
try { |
|||
// 调用接口,传递分页参数(当前页、每页条数) |
|||
const res = await getLandingListApi({ |
|||
page: this.currentPage, |
|||
pageSize: this.pageSize |
|||
}); |
|||
if (res.code === 200) { |
|||
// 更新表格数据 |
|||
this.tableData = res.data.list; |
|||
// 更新总条数 |
|||
this.totalCount = res.data.total; |
|||
} else { |
|||
// 接口返回错误信息 |
|||
ElMessage.error('获取数据失败'); |
|||
} |
|||
} catch (error) { |
|||
// 捕获网络错误 |
|||
ElMessage.error('网络异常,请稍后重试'); |
|||
} finally { |
|||
// 无论成功失败,都关闭加载状态 |
|||
this.loading = false; |
|||
} |
|||
}, |
|||
|
|||
// 分页大小改变时重新获取数据 |
|||
handleSizeChange(val) { |
|||
this.pageSize = val; |
|||
this.currentPage = 1; |
|||
this.getLandingList(); |
|||
}, |
|||
|
|||
// 当前页改变时重新获取数据 |
|||
handleCurrentChange(val) { |
|||
this.currentPage = val; |
|||
this.getLandingList(); |
|||
}, |
|||
|
|||
// 详情按钮 |
|||
detail(id){ |
|||
this.$router.push({ |
|||
path: `/admin/landingDetail/${id}`, |
|||
}); |
|||
}, |
|||
|
|||
// 打开新增弹窗 |
|||
openAddDialog() { |
|||
this.dialogTitle = '添加活动'; |
|||
this.form = { |
|||
id: '', |
|||
name: '', |
|||
introduction: '', |
|||
startTime: '', |
|||
endTime: '', |
|||
landingPage: '', |
|||
popup: '', |
|||
}; |
|||
this.dialogVisible = true; |
|||
}, |
|||
// 打开编辑弹窗 |
|||
openEditDialog(row) { |
|||
this.dialogTitle = '编辑活动'; |
|||
console.log(row) |
|||
this.form = { |
|||
id: row.id, |
|||
name: row.name, |
|||
introduction: row.introduction, |
|||
startTime: row.start_time, |
|||
endTime: row.end_time, |
|||
landingPage: row.landing_page, |
|||
popup: row.landing_page_popup, |
|||
}; |
|||
console.log(row.landing_page) |
|||
this.form.landingPageFiles = [ |
|||
{ |
|||
name: row.landing_page.split('/').pop(), |
|||
url: row.landing_page |
|||
} |
|||
] |
|||
this.form.popupFiles = [ |
|||
{ |
|||
name: row.landing_page_popup.split('/').pop(), |
|||
url: row.landing_page_popup |
|||
} |
|||
] |
|||
this.dialogVisible = true; |
|||
}, |
|||
|
|||
// 处理落地页上传(宽度校验≤375px) |
|||
handleLandingPageUpload(file) { |
|||
const reader = new FileReader(); |
|||
reader.onload = (e) => { |
|||
const img = new Image(); |
|||
img.onload = () => { |
|||
if (img.width > 375) { |
|||
ElMessage.error('图片宽度不能超过375px'); |
|||
this.form.landingPageFiles = []; |
|||
} else { |
|||
this.form.landingPage = file.raw; |
|||
this.form.landingPageFiles = [file]; |
|||
} |
|||
}; |
|||
img.src = e.target.result; |
|||
}; |
|||
reader.readAsDataURL(file.raw); |
|||
}, |
|||
|
|||
// 处理弹窗上传(宽度校验≤375px) |
|||
handlePopupUpload(file) { |
|||
const reader = new FileReader(); |
|||
reader.onload = (e) => { |
|||
const img = new Image(); |
|||
img.onload = () => { |
|||
if (img.width > 375) { |
|||
ElMessage.error('图片宽度不能超过375px'); |
|||
this.form.popupFiles = []; |
|||
} else { |
|||
this.form.popup = file.raw; |
|||
this.form.popupFiles = [file]; |
|||
} |
|||
}; |
|||
img.src = e.target.result; |
|||
}; |
|||
reader.readAsDataURL(file.raw); |
|||
}, |
|||
|
|||
// 提交表单(新增/编辑) |
|||
async submitForm() { |
|||
this.$refs.formRef.validate( async (valid) => { |
|||
if (valid) { |
|||
let landingPageFlag = false |
|||
const formData = new FormData(); |
|||
formData.append('file', this.form.landingPageFiles[0].raw); |
|||
formData.append("type", "image"); |
|||
formData.append("app_from", "toujiao"); |
|||
const landingPageRes = await axios.post( |
|||
'http://39.101.133.168:8828/hljwgo/api/file/upload', |
|||
formData, |
|||
{ |
|||
headers: { "Content-Type": "multipart/form-data" } |
|||
} |
|||
); |
|||
if(landingPageRes.data.code === 200){ |
|||
this.form.landingPage = landingPageRes.data.data.url |
|||
// this.form.popup = resp.data.file_name |
|||
landingPageFlag = true |
|||
} |
|||
let popupFlag = false |
|||
const popupFormData = new FormData(); |
|||
popupFormData.append('file', this.form.popupFiles[0].raw); |
|||
popupFormData.append("type", "image"); |
|||
popupFormData.append("app_from", "toujiao"); |
|||
const popupRes =await axios.post( |
|||
'http://39.101.133.168:8828/hljwgo/api/file/upload', |
|||
popupFormData, |
|||
{ |
|||
headers: { "Content-Type": "multipart/form-data" } |
|||
} |
|||
); |
|||
if(popupRes.data.code === 200){ |
|||
this.form.popup = popupRes.data.data.url |
|||
// this.form.popup = popupRes.data.file_name |
|||
popupFlag = true |
|||
} |
|||
if(!landingPageFlag || !popupFlag){ |
|||
ElMessage.error('图片上传失败'); |
|||
return |
|||
} |
|||
// const api = this.form.id ? editLandingtApi : addLandingApi; |
|||
addLandingApi({ |
|||
id: this.form.id, |
|||
name: this.form.name, |
|||
introduction: this.form.introduction, |
|||
start_time: this.form.startTime, |
|||
end_time: this.form.endTime, |
|||
landing_page: this.form.landingPage, |
|||
landing_page_popup: this.form.popup |
|||
}).then(res => { |
|||
if (res.code === 200) { |
|||
ElMessage.success('操作成功'); |
|||
this.dialogVisible = false; |
|||
this.getLandingList(); |
|||
} else { |
|||
ElMessage.error(res.msg || '操作失败'); |
|||
} |
|||
}).catch(err => { |
|||
ElMessage.error('网络异常'); |
|||
}); |
|||
} |
|||
}); |
|||
}, |
|||
|
|||
// 关闭弹窗 |
|||
handleDialogClose() { |
|||
this.dialogVisible = false; |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.page-container { |
|||
width: 100%; |
|||
height: 100vh; |
|||
overflow: hidden; |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
|
|||
/* 顶栏样式 */ |
|||
.top-bar { |
|||
height: 60px; |
|||
background-color: #1890ff; |
|||
color: #fff; |
|||
padding: 0 20px; |
|||
display: flex; |
|||
align-items: center; |
|||
font-size: 18px; |
|||
font-weight: 500; |
|||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
|||
z-index: 10; |
|||
} |
|||
|
|||
/* 主体容器样式 */ |
|||
.main-container { |
|||
display: flex; |
|||
flex: 1; |
|||
overflow: hidden; |
|||
} |
|||
|
|||
/* 侧边栏样式 */ |
|||
.sidebar { |
|||
width: 220px; |
|||
background-color: #f5f7fa; |
|||
border-right: 1px solid #e8e8e8; |
|||
overflow-y: auto; |
|||
} |
|||
|
|||
.sidebar-menu { |
|||
border-right: none; |
|||
height: 100%; |
|||
} |
|||
|
|||
/* 主内容区域样式 */ |
|||
.content-area { |
|||
flex: 1; |
|||
padding: 20px; |
|||
overflow-y: auto; |
|||
background-color: #fff; |
|||
} |
|||
|
|||
/* 内容头部样式 */ |
|||
.content-header { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
margin-bottom: 20px; |
|||
padding-bottom: 10px; |
|||
border-bottom: 1px solid #e8e8e8; |
|||
} |
|||
|
|||
.content-header h2 { |
|||
margin: 0; |
|||
font-size: 18px; |
|||
color: #333; |
|||
} |
|||
|
|||
/* 分页容器样式 */ |
|||
.pagination-container { |
|||
display: flex; |
|||
justify-content: flex-end; |
|||
align-items: center; |
|||
margin-top: 10px; |
|||
} |
|||
|
|||
/* 弹窗样式 */ |
|||
.intro-tip { |
|||
font-size: 12px; |
|||
color: #999; |
|||
margin-top: 5px; |
|||
} |
|||
.time-separator { |
|||
margin: 0 10px; |
|||
} |
|||
.upload-tip { |
|||
font-size: 12px; |
|||
color: #999; |
|||
margin-top: 5px; |
|||
} |
|||
.dialog-footer { |
|||
display: flex; |
|||
justify-content: flex-end; |
|||
} |
|||
</style> |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue