内容安全与广告位完善
前言
第七天完成了横幅海报模块动态化和一键启动脚本。第八天的开发工作将重点关注内容安全防护(文章防复制功能)、数据字典优化(横幅类型下拉选择)、各广告位的完整实现以及数据库字段优化,进一步完善门户系统的功能和安全性。
编辑
一、富文本内容存储优化
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函数路径前缀
这些改进完善了门户系统的内容安全防护和广告位管理功能,为后续的功能扩展和性能优化奠定了基础。