全站公告
阅读
2025-10-23更新
最新编辑:
阅读:
更新日期:2025-10-23
最新编辑:
一个灵活、可配置、可持久化的站点公告系统脚本。
依赖
- Bootstrap 3:提供基础样式(BWIKI 已默认加载)。
- Font Awesome 6:用于显示标题前的图标。如果页面未引入,脚本会尝试从 `mirrors.sustech.edu.cn` CDN 自动加载。
使用
- 将下面的 JavaScript 代码添加到站点的全局脚本文件中(例如添加到 MediaWiki:Common.js)。
- 在代码底部的 `onDOMReady` 函数中,根据需求配置一个或多个公告对象。
一个最基本的使用示例如下:
onDOMReady(function () {
var dbHelper = new IndexedDBHelper();
// 配置一个简单的右下角浮窗公告
var myNoticeConfig = {
id: 'welcome_notice_2025',
title: '欢迎访问本站',
content: '这是一个简单的站点公告。',
// 其他配置项可省略,将使用默认值
};
// 初始化公告
new SiteAnnouncer(myNoticeConfig, dbHelper).init();
});
配置
所有配置都在一个 JavaScript 对象中定义。
| 参数 | 类型 | 是否必需 | 默认值 | 说明 |
|---|---|---|---|---|
| id | String | 是 | 无 | 公告的唯一标识符。用于在 IndexedDB 中存储关闭状态,每个公告必须不同。 |
| title | String | 是 | 无 | 公告的标题。 |
| content | String | 是 | 无 | 公告的主体内容,支持 HTML 标签。 |
| linkUrl | String | 否 | 无 | 在公告内容后附加的链接 URL。提供此项后会显示一个可点击链接。 |
| linkText | String | 否 | '查看详情'
|
`linkUrl` 对应链接的显示文本。 |
| displayMode | String | 否 | 'float'
|
显示模式。'float' 为浮窗模式,'inline' 为内联模式。
|
| floatPosition | String | 否 | 'bottom-right'
|
当 `displayMode` 为 'float' 时生效。可选值:'bottom-left', 'bottom-center', 'bottom-right'。
|
| containerSelector | String | 内联模式必需 | 无 | 当 `displayMode` 为 'inline' 时,指定公告要插入的目标元素的 CSS 选择器(如 '#siteNotice')。
|
| insertionMethod | String | 否 | 'prepend'
|
当 `displayMode` 为 'inline' 时生效。决定插入方式:'prepend'(内部最前)、'append'(内部最后)、'before'(元素之前)、'after'(元素之后)。
|
| startDate | String | 否 | 无 | 公告开始显示的日期(格式:YYYY-MM-DD)。如果省略,则立即生效。
|
| endDate | String | 否 | 无 | 公告结束显示的日期(格式:YYYY-MM-DD)。如果省略,则永不过期。
|
| type | String | 否 | 'info'
|
公告的 Bootstrap 样式类型。可选值:'info', 'success', 'warning', 'danger'。
|
| iconClass | String | 否 | 'fa-solid fa-bell'
|
标题前的 Font Awesome 图标 class。 |
代码
如果通过 Mediawiki:Common.js 进行加载和使用,必须使用 ES5 兼容的版本;可以通过 Widget 使用但是实践中不建议这样做。
一个比较常见的实践是通过 Mediawiki:Common.js 或 Widget + Mediawiki:Sitenotice 加载其他脚本文件,例如 MediaWiki:Site.js,然后将脚本放置到 MediaWiki:Site.js 里来规避 ES5 语法校验。
| 主功能 |
|---|
window.safeOperation = window.safeOperation || function (fn, ...args) {
try {
return typeof fn === 'function' ? fn(...args) : null;
} catch (e) {
console.error(`${fn.name || 'Anonymous function'} 执行失败: ${e}`);
return null;
}
};
window.onDOMReady = window.onDOMReady || function (callback) {
safeOperation(() => {
const execute = () => safeOperation(callback);
document.readyState === "loading"
? document.addEventListener("DOMContentLoaded", execute)
: execute();
});
};
class IndexedDBHelper {
constructor(dbName = 'siteDataDB', storeName = 'userState') {
this.dbName = dbName;
this.storeName = storeName;
this.db = null;
}
async _getDB() { if (this.db) return this.db; return new Promise((resolve, reject) => { const request = indexedDB.open(this.dbName, 1); request.onupgradeneeded = (event) => { const db = event.target.result; if (!db.objectStoreNames.contains(this.storeName)) { db.createObjectStore(this.storeName); } }; request.onsuccess = (event) => { this.db = event.target.result; resolve(this.db); }; request.onerror = (event) => { console.error('IndexedDB error:', event.target.errorCode); reject(event.target.errorCode); }; }); }
async set(key, value) { const db = await this._getDB(); return new Promise((resolve, reject) => { const transaction = db.transaction([this.storeName], 'readwrite'); const store = transaction.objectStore(this.storeName); transaction.oncomplete = () => resolve(); transaction.onerror = (event) => reject(event.target.error); store.put(value, key); }); }
async get(key) { const db = await this._getDB(); return new Promise((resolve, reject) => { const transaction = db.transaction([this.storeName], 'readonly'); const store = transaction.objectStore(this.storeName); const request = store.get(key); request.onsuccess = () => resolve(request.result); request.onerror = (event) => reject(event.target.error); }); }
}
class SiteAnnouncer {
/**
* @param {object} config 配置对象
* @param {string} config.id 公告的唯一ID
* @param {string} config.title 公告标题
* @param {string} config.content 公告内容
* @param {string} [config.linkUrl] 公告中可点击的链接
* @param {string} [config.linkText='查看详情'] 链接的显示文字
* @param {string} [config.displayMode='float'] 显示模式: 'float' 或 'inline'.
* @param {string} [config.floatPosition='bottom-right'] 浮窗位置(displayMode为float时生效). 可选值: 'bottom-left', 'bottom-center', 'bottom-right'.
* @param {string} [config.containerSelector] 当 displayMode 为 'inline' 时,必须提供此项作为目标元素.
* @param {string} [config.insertionMethod='prepend'] 当 displayMode 为 'inline' 时生效。可选值: 'prepend', 'append', 'after', 'before'.
* @param {string} [config.startDate] 起始日期 (YYYY-MM-DD)
* @param {string} [config.endDate] 结束日期 (YYYY-MM-DD)
* @param {string} [config.type='info'] Bootstrap 3 警告框类型 (info, success, warning, danger)
* @param {string} [config.iconClass='fa-solid fa-bell'] 标题前的图标
* @param {IndexedDBHelper} dbHelper IndexedDB帮助类实例
*/
constructor(config, dbHelper) {
this.config = {
linkText: '查看详情',
displayMode: 'float',
floatPosition: 'bottom-right',
insertionMethod: 'prepend',
type: 'info',
iconClass: 'fa-solid fa-bell',
...config
};
this.dbHelper = dbHelper;
this.noticeElement = null;
if (!this.config.id || !this.dbHelper) throw new Error('必须提供 id 和 dbHelper。');
if (this.config.displayMode === 'inline' && !this.config.containerSelector) {
throw new Error("当 displayMode 为 'inline' 时, 必须提供 containerSelector。");
}
}
_isDateInRange() {
const { startDate, endDate } = this.config;
if (!startDate) return true;
const today = new Date();
today.setHours(0, 0, 0, 0);
const start = new Date(startDate);
start.setMinutes(start.getMinutes() + start.getTimezoneOffset());
if (today < start) return false;
if (endDate) {
const end = new Date(endDate);
end.setMinutes(end.getMinutes() + end.getTimezoneOffset());
if (today > end) return false;
}
return true;
}
_injectFloatStyles() {
if (document.getElementById('site-announcer-styles')) return;
const style = document.createElement('style');
style.id = 'site-announcer-styles';
style.innerHTML = `
.site-announcer-float {
position: fixed; bottom: 20px; z-index: 1050; max-width: 400px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.site-announcer-float--bottom-left { left: 20px; }
.site-announcer-float--bottom-right { right: 20px; }
.site-announcer-float--bottom-center { left: 50%; transform: translateX(-50%); }
@media (max-width: 767px) {
.site-announcer-float {
left: 15px !important; right: 15px !important; bottom: 15px;
max-width: none; transform: none !important;
}
}`;
document.head.appendChild(style);
}
_createNoticeElement() {
const { title, content, type, iconClass, displayMode, floatPosition, linkUrl, linkText } = this.config;
const notice = document.createElement('div');
let classNames = `alert alert-${type} alert-dismissible`;
if (displayMode === 'float') {
classNames += ` site-announcer-float site-announcer-float--${floatPosition}`;
this._injectFloatStyles();
}
notice.className = classNames;
notice.setAttribute('role', 'alert');
let contentHTML = content;
if (linkUrl) {
const linkHTML = ` <a href="${linkUrl}" target="_blank" rel="noopener noreferrer" class="alert-link site-announcer-link">${linkText}</a>`;
contentHTML += linkHTML;
}
notice.innerHTML = `
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
<h4><i class="${iconClass}" style="margin-right: 9px;"></i>${title}</h4>
<p>${contentHTML}</p>
`;
this.noticeElement = notice;
this._attachEventListeners();
}
_attachEventListeners() {
if (window.jQuery) {
$(this.noticeElement).on('close.bs.alert', () => this._markAsDismissed());
} else {
const closeButton = this.noticeElement.querySelector('.close');
if(closeButton) {
closeButton.addEventListener('click', () => {
this._markAsDismissed();
this.noticeElement.style.display = 'none';
});
}
}
const linkElement = this.noticeElement.querySelector('.site-announcer-link');
if (linkElement) {
linkElement.addEventListener('click', () => {
this._markAsDismissed();
if (window.jQuery) {
$(this.noticeElement).alert('close');
} else {
this.noticeElement.style.display = 'none';
}
});
}
}
_markAsDismissed() {
if (this.isDismissed) return;
this.isDismissed = true;
this.dbHelper.set(this.config.id, true).then(() => {
console.log(`公告 "${this.config.id}" 已被关闭并记录。`);
});
}
static _loadFontAwesomeIfNeeded() {
if (SiteAnnouncer._faChecked) return;
SiteAnnouncer._faChecked = true;
const testIcon = document.createElement('i');
testIcon.className = 'fa-solid';
testIcon.style.cssText = 'position:absolute; top:-9999px; left:-9999px; opacity:0; pointer-events:none;';
document.body.appendChild(testIcon);
const fontFamily = window.getComputedStyle(testIcon, '::before').getPropertyValue('font-family');
document.body.removeChild(testIcon);
if (!fontFamily || !fontFamily.toLowerCase().includes('font awesome')) {
console.log('Font Awesome not detected. Loading dynamically...');
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = 'https://mirrors.sustech.edu.cn/cdnjs/ajax/libs/font-awesome/6.0.0/css/all.min.css';
document.head.appendChild(link);
}
}
async init() {
SiteAnnouncer._loadFontAwesomeIfNeeded();
if (!this._isDateInRange()) return;
const isDismissed = await this.dbHelper.get(this.config.id);
if (isDismissed) return;
this._createNoticeElement();
if (this.config.displayMode === 'inline') {
const container = document.querySelector(this.config.containerSelector);
if (container) {
switch (this.config.insertionMethod) {
case 'append': container.append(this.noticeElement); break;
case 'after': container.after(this.noticeElement); break;
case 'before': container.before(this.noticeElement); break;
default: container.prepend(this.noticeElement); break;
}
} else {
console.error(`容器 "${this.config.containerSelector}" 未找到。`);
}
} else {
document.body.appendChild(this.noticeElement);
}
}
}
SiteAnnouncer._faChecked = false;
|
| 主功能(ES5 兼容版本) |
|---|
window.safeOperation = window.safeOperation || function (fn) {
try {
if (typeof fn === 'function') {
var args = Array.prototype.slice.call(arguments, 1);
return fn.apply(null, args);
}
return null;
} catch (e) {
console.error((fn.name || 'Anonymous function') + ' 执行失败: ' + e);
return null;
}
};
window.onDOMReady = window.onDOMReady || function (callback) {
safeOperation(function () {
var execute = function () { safeOperation(callback); };
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", execute);
} else {
execute();
}
});
};
function IndexedDBHelper(dbName, storeName) {
this.dbName = dbName === undefined ? 'siteDataDB' : dbName;
this.storeName = storeName === undefined ? 'userState' : storeName;
this.db = null;
}
IndexedDBHelper.prototype._getDB = function () {
var self = this;
if (self.db) {
return Promise.resolve(self.db);
}
return new Promise(function (resolve, reject) {
var request = indexedDB.open(self.dbName, 1);
request.onupgradeneeded = function (event) {
var db = event.target.result;
if (!db.objectStoreNames.contains(self.storeName)) {
db.createObjectStore(self.storeName);
}
};
request.onsuccess = function (event) {
self.db = event.target.result;
resolve(self.db);
};
request.onerror = function (event) {
console.error('IndexedDB error:', event.target.errorCode);
reject(event.target.errorCode);
};
});
};
IndexedDBHelper.prototype.set = function (key, value) {
var self = this;
return self._getDB().then(function (db) {
return new Promise(function (resolve, reject) {
var transaction = db.transaction([self.storeName], 'readwrite');
var store = transaction.objectStore(self.storeName);
transaction.oncomplete = function () { resolve(); };
transaction.onerror = function (event) { reject(event.target.error); };
store.put(value, key);
});
});
};
IndexedDBHelper.prototype.get = function (key) {
var self = this;
return self._getDB().then(function (db) {
return new Promise(function (resolve, reject) {
var transaction = db.transaction([self.storeName], 'readonly');
var store = transaction.objectStore(self.storeName);
var request = store.get(key);
request.onsuccess = function () { resolve(request.result); };
request.onerror = function (event) { reject(event.target.error); };
});
});
};
function SiteAnnouncer(config, dbHelper) {
/**
* @param {object} config 配置对象
* @param {string} config.id 公告的唯一ID
* @param {string} config.title 公告标题
* @param {string} config.content 公告内容
* @param {string} [config.linkUrl] 公告中可点击的链接
* @param {string} [config.linkText='查看详情'] 链接的显示文字
* @param {string} [config.displayMode='float'] 显示模式: 'float' 或 'inline'.
* @param {string} [config.floatPosition='bottom-right'] 浮窗位置(displayMode为float时生效). 可选值: 'bottom-left', 'bottom-center', 'bottom-right'.
* @param {string} [config.containerSelector] 当 displayMode 为 'inline' 时,必须提供此项作为目标元素.
* @param {string} [config.insertionMethod='prepend'] 当 displayMode 为 'inline' 时生效。可选值: 'prepend', 'append', 'after', 'before'.
* @param {string} [config.startDate] 起始日期 (YYYY-MM-DD)
* @param {string} [config.endDate] 结束日期 (YYYY-MM-DD)
* @param {string} [config.type='info'] Bootstrap 3 警告框类型 (info, success, warning, danger)
* @param {string} [config.iconClass='fa-solid fa-bell'] 标题前的图标
* @param {IndexedDBHelper} dbHelper IndexedDB帮助类实例
*/
var defaults = {
linkText: '查看详情',
displayMode: 'float',
floatPosition: 'bottom-right',
insertionMethod: 'prepend',
type: 'info',
iconClass: 'fa-solid fa-bell'
};
this.config = {};
for (var key in defaults) {
if (defaults.hasOwnProperty(key)) {
this.config[key] = defaults[key];
}
}
for (var key in config) {
if (config.hasOwnProperty(key)) {
this.config[key] = config[key];
}
}
this.dbHelper = dbHelper;
this.noticeElement = null;
if (!this.config.id || !this.dbHelper) throw new Error('必须提供 id 和 dbHelper。');
if (this.config.displayMode === 'inline' && !this.config.containerSelector) {
throw new Error("当 displayMode 为 'inline' 时, 必须提供 containerSelector。");
}
}
SiteAnnouncer.prototype._isDateInRange = function () {
var config = this.config;
if (!config.startDate) return true;
var today = new Date();
today.setHours(0, 0, 0, 0);
var start = new Date(config.startDate);
start.setMinutes(start.getMinutes() + start.getTimezoneOffset());
if (today < start) return false;
if (config.endDate) {
var end = new Date(config.endDate);
end.setMinutes(end.getMinutes() + end.getTimezoneOffset());
if (today > end) return false;
}
return true;
};
SiteAnnouncer.prototype._injectFloatStyles = function () {
if (document.getElementById('site-announcer-styles')) return;
var style = document.createElement('style');
style.id = 'site-announcer-styles';
style.innerHTML = [
'.site-announcer-float {',
' position: fixed; bottom: 20px; z-index: 1050; max-width: 400px;',
' box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);',
'}',
'.site-announcer-float--bottom-left { left: 20px; }',
'.site-announcer-float--bottom-right { right: 20px; }',
'.site-announcer-float--bottom-center { left: 50%; transform: translateX(-50%); }',
'@media (max-width: 767px) {',
' .site-announcer-float {',
' left: 15px !important; right: 15px !important; bottom: 15px;',
' max-width: none; transform: none !important;',
' }',
'}'
].join('');
document.head.appendChild(style);
};
SiteAnnouncer.prototype._createNoticeElement = function () {
var config = this.config;
var notice = document.createElement('div');
var classNames = 'alert alert-' + config.type + ' alert-dismissible';
if (config.displayMode === 'float') {
classNames += ' site-announcer-float site-announcer-float--' + config.floatPosition;
this._injectFloatStyles();
}
notice.className = classNames;
notice.setAttribute('role', 'alert');
var contentHTML = config.content;
if (config.linkUrl) {
var linkHTML = ' <a href="' + config.linkUrl + '" target="_blank" rel="noopener noreferrer" class="alert-link site-announcer-link">' + config.linkText + '</a>';
contentHTML += linkHTML;
}
notice.innerHTML = [
'<button type="button" class="close" data-dismiss="alert" aria-label="Close">',
' <span aria-hidden="true">×</span>',
'</button>',
'<h4><i class="' + config.iconClass + '" style="margin-right: 9px;"></i>' + config.title + '</h4>',
'<p>' + contentHTML + '</p>'
].join('');
this.noticeElement = notice;
this._attachEventListeners();
};
SiteAnnouncer.prototype._attachEventListeners = function () {
var self = this;
if (window.jQuery) {
$(this.noticeElement).on('close.bs.alert', function () { self._markAsDismissed(); });
} else {
var closeButton = this.noticeElement.querySelector('.close');
if (closeButton) {
closeButton.addEventListener('click', function () {
self._markAsDismissed();
self.noticeElement.style.display = 'none';
});
}
}
var linkElement = this.noticeElement.querySelector('.site-announcer-link');
if (linkElement) {
linkElement.addEventListener('click', function () {
self._markAsDismissed();
if (window.jQuery) {
$(self.noticeElement).alert('close');
} else {
self.noticeElement.style.display = 'none';
}
});
}
};
SiteAnnouncer.prototype._markAsDismissed = function () {
if (this.isDismissed) return;
this.isDismissed = true;
this.dbHelper.set(this.config.id, true).then(function () {
console.log('公告 "' + this.config.id + '" 已被关闭并记录。');
}.bind(this));
};
SiteAnnouncer._loadFontAwesomeIfNeeded = function () {
if (SiteAnnouncer._faChecked) return;
SiteAnnouncer._faChecked = true;
var testIcon = document.createElement('i');
testIcon.className = 'fa-solid';
testIcon.style.cssText = 'position:absolute; top:-9999px; left:-9999px; opacity:0; pointer-events:none;';
document.body.appendChild(testIcon);
var fontFamily = window.getComputedStyle(testIcon, '::before').getPropertyValue('font-family');
document.body.removeChild(testIcon);
if (!fontFamily || !fontFamily.toLowerCase().includes('font awesome')) {
console.log('Font Awesome not detected. Loading dynamically...');
var link = document.createElement('link');
link.rel = 'stylesheet';
link.href = 'https://mirrors.sustech.edu.cn/cdnjs/ajax/libs/font-awesome/6.0.0/css/all.min.css';
document.head.appendChild(link);
}
};
SiteAnnouncer.prototype.init = function () {
var self = this;
SiteAnnouncer._loadFontAwesomeIfNeeded();
if (!self._isDateInRange()) return;
self.dbHelper.get(self.config.id).then(function (isDismissed) {
if (isDismissed) return;
self._createNoticeElement();
if (self.config.displayMode === 'inline') {
var container = document.querySelector(self.config.containerSelector);
if (container) {
switch (self.config.insertionMethod) {
case 'append': container.appendChild(self.noticeElement); break;
case 'after': container.parentNode.insertBefore(self.noticeElement, container.nextSibling); break;
case 'before': container.parentNode.insertBefore(self.noticeElement, container); break;
default: container.insertBefore(self.noticeElement, container.firstChild); break;
}
} else {
console.error('容器 "' + self.config.containerSelector + '" 未找到。');
}
} else {
document.body.appendChild(self.noticeElement);
}
});
};
SiteAnnouncer._faChecked = false;
|
| 用法示例 |
|---|
onDOMReady(() => {
const dbHelper = new IndexedDBHelper();
// 示例 1: 在页面顶部 #siteNotice 后以内联形式显示的成功公告
const topPageNotice = {
id: 'update_notice_20251019',
title: '服务升级完成',
content: '网站核心服务已于今日凌晨完成升级,性能提升 20%。',
displayMode: 'inline',
containerSelector: '#siteNotice',
insertionMethod: 'after',
type: 'success',
iconClass: 'fa-solid fa-circle-check'
};
// 示例 2: 左下角浮窗,带链接,指定起止日期
const promoNotice = {
id: 'promo_winter_2025',
title: 'Steam 冬季促销',
content: 'Steam 冬季促销正在进行中,游戏限时折扣!',
linkUrl: '#',
linkText: '前往商店',
displayMode: 'float',
floatPosition: 'bottom-left',
startDate: '2025-10-01',
endDate: '2028-10-31',
type: 'info',
iconClass: 'fa-solid fa-gift'
};
// 示例 3: 底部居中浮窗
const privacyNotice = {
id: 'privacy_policy_update_2025',
title: '隐私政策更新提醒',
content: '我们更新了隐私政策条款,以符合最新的数据保护法规。',
displayMode: 'float',
floatPosition: 'bottom-center',
type: 'warning',
iconClass: 'fa-solid fa-shield-halved'
};
// 示例 4: 一个已过期的公告,此公告将不会显示
const expiredNotice = {
id: 'expired_event_2024',
title: '已结束的活动',
content: '此活动已于去年结束。',
startDate: '2024-01-01',
endDate: '2024-01-31'
};
// 示例 5: 在主内容区末尾追加的危险警告
const dangerNotice = {
id: 'api_deprecation_warning',
title: '重要:API 弃用警告',
content: 'V1 版本的 API 将于下月起停止支持,请尽快迁移至 V2 版本。',
displayMode: 'inline',
containerSelector: '.game-bg.container',
insertionMethod: 'append',
type: 'danger',
iconClass: 'fa-solid fa-triangle-exclamation'
};
// --- 初始化所有公告 ---
new SiteAnnouncer(topPageNotice, dbHelper).init();
new SiteAnnouncer(promoNotice, dbHelper).init();
new SiteAnnouncer(privacyNotice, dbHelper).init();
new SiteAnnouncer(expiredNotice, dbHelper).init();
new SiteAnnouncer(dangerNotice, dbHelper).init();
});
|
| 用法示例(ES5 兼容版本) |
|---|
onDOMReady(function () {
var dbHelper = new IndexedDBHelper();
// 示例 1: 在页面顶部 #siteNotice 后以内联形式显示的成功公告
var topPageNotice = {
id: 'update_notice_20251019',
title: '服务升级完成',
content: '网站核心服务已于今日凌晨完成升级,性能提升 20%。',
displayMode: 'inline',
containerSelector: '#siteNotice',
insertionMethod: 'after',
type: 'success',
iconClass: 'fa-solid fa-circle-check'
};
// 示例 2: 左下角浮窗,带链接,指定起止日期
var promoNotice = {
id: 'promo_winter_2025',
title: 'Steam 冬季促销',
content: 'Steam 冬季促销正在进行中,游戏限时折扣!',
linkUrl: '#',
linkText: '前往商店',
displayMode: 'float',
floatPosition: 'bottom-left',
startDate: '2025-10-01',
endDate: '2028-10-31',
type: 'info',
iconClass: 'fa-solid fa-gift'
};
// 示例 3: 底部居中浮窗
var privacyNotice = {
id: 'privacy_policy_update_2025',
title: '隐私政策更新提醒',
content: '我们更新了隐私政策条款,以符合最新的数据保护法规。',
displayMode: 'float',
floatPosition: 'bottom-center',
type: 'warning',
iconClass: 'fa-solid fa-shield-halved'
};
// 示例 4: 一个已过期的公告,此公告将不会显示
var expiredNotice = {
id: 'expired_event_2024',
title: '已结束的活动',
content: '此活动已于去年结束。',
startDate: '2024-01-01',
endDate: '2024-01-31'
};
// 示例 5: 在主内容区末尾追加的危险警告
var dangerNotice = {
id: 'api_deprecation_warning',
title: '重要:API 弃用警告',
content: 'V1 版本的 API 将于下月起停止支持,请尽快迁移至 V2 版本。',
displayMode: 'inline',
containerSelector: '.game-bg.container',
insertionMethod: 'append',
type: 'danger',
iconClass: 'fa-solid fa-triangle-exclamation'
};
new SiteAnnouncer(topPageNotice, dbHelper).init();
new SiteAnnouncer(promoNotice, dbHelper).init();
new SiteAnnouncer(privacyNotice, dbHelper).init();
new SiteAnnouncer(expiredNotice, dbHelper).init();
new SiteAnnouncer(dangerNotice, dbHelper).init();
});
|

沪公网安备 31011002002714 号