热门角色不仅是灵感来源,更是你的效率助手。通过精挑细选的角色提示词,你可以快速生成高质量内容、提升创作灵感,并找到最契合你需求的解决方案。让创作更轻松,让价值更直接!
我们根据不同用户需求,持续更新角色库,让你总能找到合适的灵感入口。
本提示词专为JavaScript开发场景设计,能够根据用户需求生成符合最佳实践的类声明代码。通过精确的参数配置,可指定类名、继承关系、构造函数参数等关键要素,确保生成的代码结构清晰、语法规范。适用于Web开发、客户端脚本编写、交互式应用构建等多种业务场景,帮助开发者快速创建高质量的JavaScript类定义,提升开发效率和代码质量。
class ModalDialog extends BaseComponent {
constructor(container, options = {}) {
// 调用父类构造函数(如父类需要其他签名,可按需调整)
super(container, options);
// 支持传入选择器字符串或 DOM 元素
const root =
container instanceof Element
? container
: typeof container === 'string'
? document.querySelector(container)
: null;
if (!root) {
throw new TypeError('ModalDialog: "container" must be a DOM Element or a valid selector.');
}
// 合并默认配置与用户配置
this.options = { ...ModalDialog.getDefaultOptions(), ...options };
// 容器与内部节点引用
this.container = root;
this.nodes = {
overlay: null,
dialog: null,
header: null,
titleEl: null,
content: null,
closeBtn: null
};
// 状态与辅助字段
this._id = this.options.id || `modal-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
this._isRendered = false;
this._isOpen = false;
this._eventsBound = false;
this._lastFocused = null;
// 事件处理器引用(用于解绑)
this._handlers = {
onKeyDown: null,
onOverlayClick: null,
onCloseClick: null
};
if (this.options.autoRender) {
this.render();
}
}
// 渲染弹窗结构(只渲染一次)
render() {
if (this._isRendered) return this;
// 创建遮罩层
const overlay = document.createElement('div');
overlay.className = 'modal-overlay';
overlay.setAttribute('data-modal-overlay', '');
// 避免被辅助技术聚焦
overlay.setAttribute('aria-hidden', 'true');
// 创建对话框
const dialog = document.createElement('div');
dialog.className = `modal-dialog${this.options.cssClass ? ` ${this.options.cssClass}` : ''}`;
dialog.id = this._id;
dialog.setAttribute('role', 'dialog');
dialog.setAttribute('aria-modal', 'true');
dialog.setAttribute('aria-hidden', 'true');
// 让对话框可聚焦以便管理焦点
dialog.setAttribute('tabindex', '-1');
// 可选标题栏
let header = null;
let titleEl = null;
if (this.options.title) {
header = document.createElement('div');
header.className = 'modal-header';
titleEl = document.createElement('h2');
titleEl.className = 'modal-title';
titleEl.id = `${this._id}-title`;
titleEl.textContent = this.options.title;
header.appendChild(titleEl);
dialog.setAttribute('aria-labelledby', titleEl.id);
} else if (this.options.ariaLabel) {
// 无标题时使用 aria-label
dialog.setAttribute('aria-label', this.options.ariaLabel);
}
// 关闭按钮
const closeBtn = document.createElement('button');
closeBtn.className = 'modal-close';
closeBtn.type = 'button';
closeBtn.setAttribute('aria-label', this.options.closeLabel || 'Close');
closeBtn.textContent = this.options.closeText || '×';
// 内容容器
const content = document.createElement('div');
content.className = 'modal-content';
// 渲染内容
if (this.options.content instanceof Node) {
content.appendChild(this.options.content);
} else if (typeof this.options.content === 'function') {
const result = this.options.content();
if (result instanceof Node) {
content.appendChild(result);
} else if (typeof result === 'string') {
if (this.options.allowHTML) {
// 注意:开启 allowHTML 应确保内容已安全处理
content.innerHTML = result;
} else {
content.textContent = result;
}
}
} else if (typeof this.options.content === 'string') {
if (this.options.allowHTML) {
// 注意:开启 allowHTML 应确保内容已安全处理
content.innerHTML = this.options.content;
} else {
content.textContent = this.options.content;
}
}
// 组装节点
if (!header) {
// 若无 header,关闭按钮直接放置在对话框顶层
dialog.appendChild(closeBtn);
} else {
header.appendChild(closeBtn);
dialog.appendChild(header);
}
dialog.appendChild(content);
overlay.appendChild(dialog);
this.container.appendChild(overlay);
// 保存引用
this.nodes.overlay = overlay;
this.nodes.dialog = dialog;
this.nodes.header = header;
this.nodes.titleEl = titleEl;
this.nodes.content = content;
this.nodes.closeBtn = closeBtn;
this._isRendered = true;
return this;
}
// 打开弹窗(管理可见性、ARIA、焦点与事件)
open() {
if (this._isOpen) return this;
if (!this._isRendered) this.render();
// 保存当前焦点以便关闭后恢复
this._lastFocused = document.activeElement instanceof HTMLElement ? document.activeElement : null;
// 可见性与状态
this.nodes.dialog.setAttribute('aria-hidden', 'false');
this.nodes.overlay.classList.add(this.options.transitionClass);
this.nodes.dialog.classList.add(this.options.transitionClass);
// 绑定事件
this.bindEvents();
// 聚焦策略
if (this.options.trapFocus) {
this._focusFirstFocusable();
} else {
this.nodes.dialog.focus();
}
this._isOpen = true;
return this;
}
// 关闭弹窗(恢复焦点、解绑事件与可见性)
close() {
if (!this._isOpen) return this;
// 可见性与状态
this.nodes.dialog.setAttribute('aria-hidden', 'true');
this.nodes.overlay.classList.remove(this.options.transitionClass);
this.nodes.dialog.classList.remove(this.options.transitionClass);
// 解绑事件
this._unbindEvents();
// 恢复之前的焦点
if (this._lastFocused && typeof this._lastFocused.focus === 'function' && document.contains(this._lastFocused)) {
this._lastFocused.focus();
}
this._lastFocused = null;
this._isOpen = false;
// 如果需要在关闭后销毁
if (this.options.destroyOnClose) {
this.destroy();
}
return this;
}
// 绑定事件(防重复绑定)
bindEvents() {
if (!this._isRendered || this._eventsBound) return this;
const { overlay, dialog, closeBtn } = this.nodes;
this._handlers.onKeyDown = (e) => {
// ESC 关闭
if (this.options.closeOnEsc && e.key === 'Escape') {
e.preventDefault();
this.close();
return;
}
// 焦点陷阱
if (this.options.trapFocus && e.key === 'Tab') {
const focusables = this._getFocusableElements();
if (focusables.length === 0) {
// 无可聚焦元素时,保持对话框本身聚焦
e.preventDefault();
dialog.focus();
return;
}
const currentIndex = focusables.indexOf(document.activeElement);
let nextIndex = currentIndex;
if (e.shiftKey) {
nextIndex = currentIndex <= 0 ? focusables.length - 1 : currentIndex - 1;
} else {
nextIndex = currentIndex === focusables.length - 1 ? 0 : currentIndex + 1;
}
e.preventDefault();
focusables[nextIndex].focus();
}
};
this._handlers.onOverlayClick = (e) => {
if (!this.options.closeOnBackdrop) return;
// 仅当点击在遮罩层本身(而非对话框内部)时关闭
if (e.target === overlay) {
this.close();
}
};
this._handlers.onCloseClick = () => {
this.close();
};
document.addEventListener('keydown', this._handlers.onKeyDown, true);
overlay.addEventListener('mousedown', this._handlers.onOverlayClick, true);
closeBtn.addEventListener('click', this._handlers.onCloseClick, true);
this._eventsBound = true;
return this;
}
// 销毁实例(移除 DOM 与事件)
destroy() {
// 若还打开,先关闭以便处理解绑
if (this._isOpen) {
this.close();
} else {
// 确保事件已解绑
this._unbindEvents();
}
if (this._isRendered && this.nodes.overlay && this.nodes.overlay.parentNode) {
this.nodes.overlay.parentNode.removeChild(this.nodes.overlay);
}
// 清空引用
this.nodes = {
overlay: null,
dialog: null,
header: null,
titleEl: null,
content: null,
closeBtn: null
};
this._isRendered = false;
return this;
}
// 静态方法:默认配置
static getDefaultOptions() {
return {
id: null,
title: '',
ariaLabel: '',
content: '',
allowHTML: false, // true 时请确保内容已安全处理
cssClass: '',
closeLabel: 'Close',
closeText: '×',
closeOnEsc: true,
closeOnBackdrop: true,
trapFocus: true,
autoRender: true,
destroyOnClose: false,
transitionClass: 'is-open'
};
}
// 获取对话框内部可聚焦元素列表
_getFocusableElements() {
if (!this.nodes.dialog) return [];
const selector = [
'a[href]',
'area[href]',
'button:not([disabled])',
'input:not([disabled]):not([type="hidden"])',
'select:not([disabled])',
'textarea:not([disabled])',
'iframe',
'audio[controls]',
'video[controls]',
'[contenteditable]',
'[tabindex]:not([tabindex="-1"])'
].join(',');
const all = Array.from(this.nodes.dialog.querySelectorAll(selector));
// 过滤不可见元素
return all.filter((el) => {
const style = window.getComputedStyle(el);
return style.visibility !== 'hidden' && style.display !== 'none' && el.offsetParent !== null;
});
}
// 聚焦第一个可聚焦元素,若不存在则聚焦对话框
_focusFirstFocusable() {
const focusables = this._getFocusableElements();
if (focusables.length > 0) {
focusables[0].focus();
} else {
this.nodes.dialog.focus();
}
}
// 解绑事件(内部使用)
_unbindEvents() {
if (!this._eventsBound) return;
const { overlay, closeBtn } = this.nodes;
document.removeEventListener('keydown', this._handlers.onKeyDown, true);
if (overlay) overlay.removeEventListener('mousedown', this._handlers.onOverlayClick, true);
if (closeBtn) closeBtn.removeEventListener('click', this._handlers.onCloseClick, true);
this._eventsBound = false;
}
}
// Assumes EventEmitter is available in scope (e.g., Node.js: const { EventEmitter } = require('events');)
class UserProfileModel extends EventEmitter {
/**
* @param {object} service - Data service with load(id), save(profile), update(id, patch) async methods.
* @param {number} cacheTTL - Cache time-to-live in milliseconds (default: 5 minutes).
*/
constructor(service, cacheTTL = 300000) {
super();
if (!service || typeof service !== 'object') {
throw new TypeError('UserProfileModel: "service" must be a non-null object with load/save/update methods.');
}
if (typeof cacheTTL !== 'number' || !Number.isFinite(cacheTTL) || cacheTTL < 0) {
throw new TypeError('UserProfileModel: "cacheTTL" must be a non-negative finite number.');
}
this.#service = service;
this.#cacheTTL = cacheTTL;
this.#data = null; // Cached user profile object
this.#userId = null; // Last loaded/saved user identifier
this.#cachedAt = 0; // Timestamp of last cache update (ms)
}
// -------------------------
// Instance methods
// -------------------------
/**
* Load a user profile by id with TTL-based caching.
* @param {string|number} userId - User identifier.
* @param {{ force?: boolean }} [options] - If force=true, bypass cache.
* @returns {Promise<object|null>} Deep-cloned user profile or null if none.
*/
async load(userId, { force = false } = {}) {
const effectiveId = userId ?? this.#userId;
if (effectiveId == null) {
throw new Error('UserProfileModel.load: "userId" is required on first load.');
}
// Serve from cache if fresh and not forced
if (
!force &&
this.#data &&
(this.#userId === effectiveId) &&
(Date.now() - this.#cachedAt < this.#cacheTTL)
) {
return this.toJSON();
}
if (typeof this.#service.load !== 'function') {
throw new Error('UserProfileModel.load: service.load(id) is not implemented.');
}
const profile = await this.#service.load(effectiveId);
if (!profile || typeof profile !== 'object') {
throw new Error('UserProfileModel.load: service.load must resolve to an object.');
}
this.validate(profile);
this.#userId = profile.id ?? effectiveId;
this.#commit(profile);
return this.toJSON();
}
/**
* Persist a full user profile.
* @param {object} profile - Full profile object to save.
* @returns {Promise<object>} Deep-cloned saved profile.
*/
async save(profile) {
if (!profile || typeof profile !== 'object') {
throw new TypeError('UserProfileModel.save: "profile" must be a non-null object.');
}
if (typeof this.#service.save !== 'function') {
throw new Error('UserProfileModel.save: service.save(profile) is not implemented.');
}
this.validate(profile);
const saved = await this.#service.save(profile);
const result = (saved && typeof saved === 'object') ? saved : profile;
this.#userId = result.id ?? this.#userId ?? profile.id ?? this.#userId;
this.#commit(result);
return this.toJSON();
}
/**
* Apply partial updates to the current profile and persist.
* @param {object} patch - Partial fields to update.
* @returns {Promise<object>} Deep-cloned updated profile.
*/
async update(patch) {
if (!patch || typeof patch !== 'object') {
throw new TypeError('UserProfileModel.update: "patch" must be a non-null object.');
}
const currentId = this.#userId ?? this.#data?.id;
if (currentId == null) {
throw new Error('UserProfileModel.update: No profile loaded/saved yet. Call load/save first.');
}
if (typeof this.#service.update !== 'function') {
throw new Error('UserProfileModel.update: service.update(id, patch) is not implemented.');
}
// Validate the resulting object before persisting
const next = { ...(this.#data ?? {}), ...patch };
this.validate(next);
const updated = await this.#service.update(currentId, patch);
const result = (updated && typeof updated === 'object') ? updated : next;
this.#commit(result);
return this.toJSON();
}
/**
* Return a deep-cloned snapshot of the current profile.
* @returns {object|null}
*/
toJSON() {
if (this.#data == null) return null;
return UserProfileModel.clone(this.#data);
}
/**
* Validate a profile object. Throws on invalid data.
* - id: optional string|number
* - name: optional non-empty string
* - email: optional valid email format
* - createdAt/updatedAt: optional Date or ISO date string
* @param {object} [data] - Profile to validate; defaults to current.
* @returns {true} Returns true if valid.
*/
validate(data) {
const d = data ?? this.#data;
if (!d || typeof d !== 'object') {
throw new Error('UserProfileModel.validate: No profile data to validate.');
}
// id: optional string or number
if ('id' in d && d.id != null && typeof d.id !== 'string' && typeof d.id !== 'number') {
throw new TypeError('UserProfileModel.validate: "id" must be a string or number when provided.');
}
// name: optional non-empty string
if ('name' in d && d.name != null) {
if (typeof d.name !== 'string' || d.name.trim().length === 0) {
throw new TypeError('UserProfileModel.validate: "name" must be a non-empty string when provided.');
}
}
// email: optional valid format
if ('email' in d && d.email != null) {
const email = String(d.email).trim();
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
throw new TypeError('UserProfileModel.validate: "email" must be a valid email address when provided.');
}
}
// createdAt / updatedAt: optional Date or ISO-8601 string
const isValidDateValue = (v) => (v instanceof Date && !isNaN(v)) || (!isNaN(Date.parse(v)));
if ('createdAt' in d && d.createdAt != null && !isValidDateValue(d.createdAt)) {
throw new TypeError('UserProfileModel.validate: "createdAt" must be a Date or ISO date string when provided.');
}
if ('updatedAt' in d && d.updatedAt != null && !isValidDateValue(d.updatedAt)) {
throw new TypeError('UserProfileModel.validate: "updatedAt" must be a Date or ISO date string when provided.');
}
return true;
}
/**
* Subscribe to change events. Returns an unsubscribe function.
* @param {(payload: { previous: object|null, current: object|null }) => void} handler
* @returns {() => void} Unsubscribe function
*/
onChange(handler) {
if (typeof handler !== 'function') {
throw new TypeError('UserProfileModel.onChange: "handler" must be a function.');
}
this.on('change', handler);
return () => {
if (typeof this.off === 'function') {
this.off('change', handler);
} else if (typeof this.removeListener === 'function') {
this.removeListener('change', handler);
}
};
}
// -------------------------
// Private state and helpers
// -------------------------
// Service adapter instance
#service;
// Cache controls
#cacheTTL;
#cachedAt;
// Profile state
#data;
#userId;
/**
* Commit new data into the model, refresh cache timestamp, and emit change event.
* @param {object} newData
* @private
*/
#commit(newData) {
const previous = this.#data ? UserProfileModel.clone(this.#data) : null;
// Store a cloned snapshot to avoid external mutations affecting internal state
this.#data = UserProfileModel.clone(newData);
this.#cachedAt = Date.now();
// Emit a cloned payload to ensure immutability to subscribers
this.emit('change', {
previous,
current: UserProfileModel.clone(this.#data),
});
}
// -------------------------
// Static helpers
// -------------------------
/**
* Deep clone utility. Uses structuredClone when available, falls back to JSON.
* @param {any} value
* @returns {any}
*/
static clone(value) {
if (typeof structuredClone === 'function') {
return structuredClone(value);
}
// Note: JSON fallback does not preserve Dates/undefined/functions.
return JSON.parse(JSON.stringify(value));
}
}
快速搭建组件类与数据模型;一键生成构造函数与方法骨架;减少样板代码,专注交互与体验。
迅速生成服务端模块类与工具类;按需配置继承与静态方法;统一风格,缩短从设计到上线的周期。
沉淀团队类模板标准;约束命名与结构;用可复制的输出推动代码评审通过与跨项目复用。
将模板生成的提示词复制粘贴到您常用的 Chat 应用(如 ChatGPT、Claude 等),即可直接对话使用,无需额外开发。适合个人快速体验和轻量使用场景。
把提示词模板转化为 API,您的程序可任意修改模板参数,通过接口直接调用,轻松实现自动化与批量处理。适合开发者集成与业务系统嵌入。
在 MCP client 中配置对应的 server 地址,让您的 AI 应用自动调用提示词模板。适合高级用户和团队协作,让提示词在不同 AI 工具间无缝衔接。
半价获取高级提示词-优惠即将到期