企业级门户网站设计与实现:基于SpringBoot + Vue3的全栈解决方案(Day 8)
内容安全与广告位完善
前言
第七天完成了横幅海报模块动态化和一键启动脚本。第八天的开发工作将重点关注内容安全防护(文章防复制功能)、数据字典优化(横幅类型下拉选择)、各广告位的完整实现以及数据库字段优化,进一步完善门户系统的功能和安全性。
一、富文本内容存储优化
1.1 问题分析
文章内容使用富文本编辑器录入,但数据库yucms_article表的content字段使用TEXT类型,最大存储 64KB,无法满足长文章的存储需求。
1.2 解决方案
将content字段类型从TEXT改为LONGTEXT(最大支持 4GB):
ALTER TABLE yucms_article MODIFY COLUMN content LONGTEXT;1.3 字段类型对比
字段类型 | 最大容量 | 适用场景 |
|---|---|---|
TEXT | 64KB | 短文本、摘要 |
LONGTEXT | 4GB | 长文章、富文本内容 |
二、文章正文防复制功能
2.1 需求分析
为保护原创内容,需要实现文章正文的防复制功能,并在用户尝试复制时弹出提示框。
2.2 实现方案
在ArticleDetail.vue中添加防复制事件监听:
<div ref="articleContentRef" class="anti-copy" @copy="preventCopy" @contextmenu="preventContextMenu" @selectstart="preventSelectStart" > <div v-html="article.content"></div> </div>2.3 事件处理函数
// 阻止复制 function preventCopy(e) { e.preventDefault(); showCopyAlert(); } // 阻止右键菜单 function preventContextMenu(e) { e.preventDefault(); } // 阻止文字选中 function preventSelectStart(e) { e.preventDefault(); }2.4 自定义提示弹窗
创建美观的提示弹窗组件,与网站整体风格协调:
<Transition name="fade"> <div v-if="showAlert" class="fixed inset-0 z-50 flex items-center justify-center bg-black/50"> <div class="bg-white rounded-2xl shadow-2xl p-6 max-w-md w-full mx-4 transform"> <div class="text-center"> <div class="w-16 h-16 bg-gradient-to-br from-blue-500 to-indigo-600 rounded-full flex items-center justify-center mx-auto mb-4"> <svg class="w-8 h-8 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"></path> </svg> </div> <h3 class="text-lg font-semibold text-slate-800 mb-2">关注公众号获取资料</h3> <p class="text-slate-600 mb-4">为保护原创内容,文章正文无法直接复制</p> <div class="bg-slate-50 rounded-lg p-4 mb-4"> <p class="text-sm text-slate-500 mb-1">公众号名称</p> <p class="text-lg font-bold text-bank-primary">Fintech.Ren</p> </div> <button @click="showAlert = false" class="w-full py-3 bg-bank-primary text-white font-medium rounded-xl hover:bg-bank-primary/90 transition-colors" > 我知道了 </button> </div> </div> </div> </Transition>2.5 CSS 样式
.anti-copy { user-select: none; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; } .fade-enter-active, .fade-leave-active { transition: opacity 0.3s ease; } .fade-enter-from, .fade-leave-to { opacity: 0; }三、横幅类型下拉选择优化
3.1 问题分析
横幅管理页面中,横幅类型使用单选按钮展示,不够美观且占用空间较大。需要改为下拉菜单形式。
3.2 修改 YucmsBanner.data.ts
将横幅类型字段从radio改为select:
{ label: '横幅类型', field: 'type', defaultValue: "1", component: 'JDictSelectTag', componentProps:{ dictCode:"yucms_banner_type", type: "select", refresh: true, }, dynamicRules: ({model,schema}) => { return [{ required: true, message: '请选择横幅类型!' }]; }, }3.3 数据字典刷新问题
3.3.1 问题描述
数据字典修改后,前端不会自动更新,因为initDictOptions函数优先读取缓存。
3.3.2 解决方案
在jeecgboot-vue3/src/utils/dict/index.ts中添加refresh参数:
export const initDictOptions = (code, refresh = false) => { if (!refresh && getDictItemsByCode(code)) { return new Promise((resolve) => resolve(getDictItemsByCode(code))); } // 从后端获取最新数据... };在JDictSelectTag.vue组件中传递refresh属性:
props: { refresh: propTypes.bool.def(false), }, async function initDictData() { const dictData = await initDictOptions(dictCode, refresh); // 处理数据逻辑 }四、各广告位动态获取实现
4.1 广告位类型定义
Type 值 | 广告位名称 | 尺寸 | 位置 |
|---|---|---|---|
1 | 首页轮播 | - | 首页顶部 |
2 | 首页横条 | 728×90 | 轮播下方 |
3 | 首页960×670 | 960×670 | 中间区域 |
4 | 首页320×790 | 320×790 | 侧边栏 |
5 | 首页300×250 | 300×250 | 侧边栏下方 |
6 | List页640×360 | 640×360 | 文章列表页 |
4.2 Banner API 封装
在src/api/banner.ts中创建各广告位的 API 函数:
// 获取首页轮播(type=1,取3条) export const getBannerList = (params?: any) => { const now = new Date().toISOString().slice(0, 19).replace('T', ' '); return defHttp.get({ url: Api.GetBannerList, params: { ...params, bannerStatus: '1', type: '1', pageSize: 3, column: 'updateTime', order: 'desc', startTime_lte: now, endTime_gte: now } }); }; // 获取顶部横幅(type=2,取1条) export const getTopBanner = () => { /* ... */ }; // 获取中间横幅(type=3,取1条) export const getMiddleBanner = () => { /* ... */ }; // 获取侧边栏横幅(type=4,取1条) export const getSideBanner = () => { /* ... */ }; // 获取小横幅(type=5,取1条) export const getSmallBanner = () => { /* ... */ }; // 获取List页横幅(type=6,取1条) export const getListBanner = () => { /* ... */ };4.3 数据获取逻辑
各广告位的数据获取逻辑统一遵循以下规则:
状态过滤:
bannerStatus = 1(启用状态)时间区间:当前时间在
start_time和end_time之间类型过滤:根据广告位类型设置
type参数排序规则:按
update_time降序排列数量限制:轮播取前3条,其他广告位取前1条
4.4 Home.vue 集成
在首页中集成各广告位组件:
// 响应式变量 const topBanner = ref(null); const sideBanner = ref(null); const middleBanner = ref(null); const smallBanner = ref(null); // 数据获取函数 async function fetchTopBanner() { /* ... */ } async function fetchSideBanner() { /* ... */ } async function fetchMiddleBanner() { /* ... */ } async function fetchSmallBanner() { /* ... */ } // 组件挂载时执行 onMounted(() => { fetchTopBanner(); fetchSideBanner(); fetchMiddleBanner(); fetchSmallBanner(); });4.5 模板渲染
使用v-if/v-else实现广告位的条件渲染:
<!-- 中间横幅 --> <div v-if="middleBanner" class="ad-container overflow-hidden"> <a :href="middleBanner.linkUrl" :target="middleBanner.izBlank === '1' ? '_blank' : '_self'"> <img :src="getImageUrl(middleBanner.imageUrl)" :alt="middleBanner.title" /> </a> </div> <div v-else class="ad-container flex items-center justify-center"> <div class="text-center"> <p class="text-sm text-slate-400">广告位 960 × 670</p> <p class="text-xs text-slate-300 mt-1">联盟广告位</p> </div> </div>五、图片路径问题修复
5.1 问题描述
ArticleList.vue中的getImageUrl函数使用了错误的路径前缀/jeecg-boot,导致图片无法加载。
5.2 统一修复
确保所有页面的getImageUrl函数使用正确的前缀/jeecgboot(与 Vite 代理配置一致):
function getImageUrl(imagePath) { if (!imagePath) return null; // 完整URL直接返回 if (imagePath.startsWith('http://') || imagePath.startsWith('https://')) { return imagePath; } // 添加正确的代理前缀 if (imagePath.startsWith('/')) { return `/jeecgboot${imagePath}`; } else { return `/jeecgboot/${imagePath}`; } }5.3 Vite 代理配置
server: { port: 3001, proxy: { '/jeecgboot': { target: 'http://localhost:8080/jeecg-boot', changeOrigin: true, rewrite: (path) => path.replace(/^\/jeecgboot/, '') } } }六、广告位尺寸规范
6.1 各广告位实际尺寸
广告位 | 容器宽度 | 推荐图片尺寸 | 比例 |
|---|---|---|---|
首页轮播 | 全屏 | 1920×600 | 16:5 |
首页横条 | 960px | 728×90 | ~8:1 |
首页960×670 | 960px | 960×670 | ~1.43:1 |
首页320×790 | 300px | 320×790 | ~0.41:1 |
首页300×250 | 300px | 300×250 | 6:5 |
List页640×360 | 自适应 | 640×360 | 16:9 |
6.2 图片适配策略
使用
object-cover确保图片填满容器设置固定高度,宽度自适应
添加
loading="lazy"实现懒加载
七、总结
第八天的开发工作主要完成了以下内容:
富文本内容存储优化:将
content字段类型从TEXT改为LONGTEXT,支持大文本存储文章防复制功能:实现文章正文的防复制保护,自定义美观的提示弹窗
横幅类型下拉选择:将单选按钮改为下拉菜单,优化用户体验
数据字典刷新机制:添加
refresh参数支持强制刷新字典数据各广告位完整实现:首页轮播、横条、中间横幅、侧边栏横幅、小横幅及 List 页横幅的动态获取
图片路径问题修复:统一所有页面的
getImageUrl函数路径前缀
这些改进完善了门户系统的内容安全防护和广告位管理功能,为后续的功能扩展和性能优化奠定了基础。
