refactor(frontend): replace toast system with zibll notyf #7
@@ -17,6 +17,7 @@
|
||||
/tests/widget-system/*
|
||||
!/tests/widget-system/post-visual-contract.test.cjs
|
||||
!/tests/widget-system/lazyload-contract.test.cjs
|
||||
!/tests/widget-system/toast-contract.test.cjs
|
||||
!/tests/visual/docker-compose.zibll.yml
|
||||
!/tests/visual/zibll-Caddyfile
|
||||
!/tests/visual/README.md
|
||||
|
||||
@@ -3556,105 +3556,17 @@ html.dark .aksrt-card-submenu__title {
|
||||
/**
|
||||
* AKSRT Enhancements — 全局 UI 增强
|
||||
*
|
||||
* 包含:Toast 通知、骨架屏、悬停动效、暗色过渡、滚动感知
|
||||
* 包含:骨架屏、悬停动效、暗色过渡、滚动感知
|
||||
* 所有选择器使用 aksrt- 前缀避免冲突
|
||||
*/
|
||||
|
||||
/* ============================================================
|
||||
* 1. Toast 通知系统
|
||||
* ============================================================ */
|
||||
.aksrt-toast-container {
|
||||
position: fixed;
|
||||
bottom: 80px;
|
||||
right: 16px;
|
||||
max-width: 360px;
|
||||
z-index: 10000;
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
gap: 8px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.aksrt-toast {
|
||||
pointer-events: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 12px 16px;
|
||||
border-radius: var(--main-radius, 8px);
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
|
||||
backdrop-filter: saturate(5) blur(20px);
|
||||
-webkit-backdrop-filter: saturate(5) blur(20px);
|
||||
transform: translateX(120%);
|
||||
opacity: 0;
|
||||
animation: aksrt-toast-in 0.35s cubic-bezier(0.35, 0.71, 0.46, 1.08) forwards;
|
||||
cursor: pointer;
|
||||
transition: transform 0.3s, opacity 0.3s;
|
||||
}
|
||||
|
||||
.aksrt-toast.is-dismissing {
|
||||
transform: translateX(120%);
|
||||
opacity: 0;
|
||||
transition: transform 0.3s ease-in, opacity 0.3s ease-in;
|
||||
}
|
||||
|
||||
.aksrt-toast__icon {
|
||||
font-size: 18px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.aksrt-toast__text {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
/* Toast 类型 — 对齐 zibll 纯色 */
|
||||
.aksrt-toast--success {
|
||||
background: rgba(16, 185, 129, 0.9);
|
||||
}
|
||||
|
||||
.aksrt-toast--info {
|
||||
background: rgba(59, 130, 246, 0.9);
|
||||
}
|
||||
|
||||
.aksrt-toast--warning {
|
||||
background: rgba(245, 158, 11, 0.9);
|
||||
}
|
||||
|
||||
.aksrt-toast--danger,
|
||||
.aksrt-toast--error {
|
||||
background: rgba(239, 68, 68, 0.9);
|
||||
}
|
||||
|
||||
.aksrt-toast--loading {
|
||||
background: rgba(107, 114, 128, 0.9);
|
||||
}
|
||||
|
||||
.aksrt-toast--loading .aksrt-toast__icon {
|
||||
animation: aksrt-spin 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes aksrt-toast-in {
|
||||
from {
|
||||
transform: translateX(120%);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: translateX(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes aksrt-spin {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 2. 骨架屏加载动画
|
||||
* 1. 骨架屏加载动画
|
||||
* ============================================================ */
|
||||
.aksrt-skeleton {
|
||||
background: linear-gradient(
|
||||
@@ -3726,7 +3638,7 @@ html.dark .aksrt-card-submenu__title {
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 3. 悬停交互动效
|
||||
* 2. 悬停交互动效
|
||||
* ============================================================ */
|
||||
|
||||
/* 卡片放大 */
|
||||
@@ -3805,7 +3717,7 @@ html.dark .aksrt-card-submenu__title {
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 4. 暗色模式过渡 (View Transitions API)
|
||||
* 3. 暗色模式过渡 (View Transitions API)
|
||||
* ============================================================ */
|
||||
::view-transition-old(root),
|
||||
::view-transition-new(root) {
|
||||
@@ -3839,14 +3751,10 @@ html.dark .aksrt-card-submenu__title {
|
||||
--aksrt-blur-bg: var(--surface-1);
|
||||
}
|
||||
|
||||
.aksrt-toast {
|
||||
backdrop-filter: none;
|
||||
-webkit-backdrop-filter: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 5. 滚动方向感知
|
||||
* 4. 滚动方向感知
|
||||
* ============================================================ */
|
||||
|
||||
/* 向下滚动时隐藏导航栏 */
|
||||
@@ -5231,12 +5139,6 @@ html.dark .aksrt-lang-switch__dropdown {
|
||||
* 响应式
|
||||
* ============================================================ */
|
||||
@media (max-width: 640px) {
|
||||
.aksrt-toast-container {
|
||||
right: 8px;
|
||||
left: 8px;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.aksrt-lightbox__nav {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
@@ -5287,10 +5189,6 @@ html.dark .aksrt-lang-switch__dropdown {
|
||||
animation: none !important;
|
||||
}
|
||||
|
||||
.aksrt-toast {
|
||||
animation: none !important;
|
||||
}
|
||||
|
||||
[data-aksrt-animate] {
|
||||
opacity: 1 !important;
|
||||
transform: none !important;
|
||||
@@ -5449,15 +5347,6 @@ input:focus, textarea:focus, select:focus,
|
||||
transform-origin: top;
|
||||
}
|
||||
|
||||
/* --- Toast 通知动效 --- */
|
||||
.aksrt-toast {
|
||||
animation: aksrtToastIn 0.35s var(--ease-out) forwards;
|
||||
}
|
||||
|
||||
.aksrt-toast--out {
|
||||
animation: aksrtToastOut 0.3s var(--ease) forwards;
|
||||
}
|
||||
|
||||
/* --- 骨架屏动效 --- */
|
||||
.aksrt-skeleton {
|
||||
animation: aksrtSkeletonShimmer 1.5s infinite linear;
|
||||
|
||||
@@ -1,87 +1,10 @@
|
||||
/**
|
||||
* AKSRT Enhancements — 全局 UI 增强
|
||||
*
|
||||
* 包含:Toast 通知、骨架屏、悬停动效、暗色过渡、滚动感知
|
||||
* 包含:骨架屏、悬停动效、暗色过渡、滚动感知
|
||||
* 所有选择器使用 aksrt- 前缀避免冲突
|
||||
*/
|
||||
|
||||
/* ============================================================
|
||||
* 1. Toast 通知系统
|
||||
* ============================================================ */
|
||||
.aksrt-toast-container {
|
||||
position: fixed;
|
||||
bottom: 80px;
|
||||
right: 16px;
|
||||
max-width: 360px;
|
||||
z-index: 10000;
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
gap: 8px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.aksrt-toast {
|
||||
pointer-events: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 12px 16px;
|
||||
border-radius: var(--main-radius, 8px);
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
|
||||
backdrop-filter: saturate(5) blur(20px);
|
||||
-webkit-backdrop-filter: saturate(5) blur(20px);
|
||||
transform: translateX(120%);
|
||||
opacity: 0;
|
||||
animation: aksrt-toast-in 0.35s cubic-bezier(0.35, 0.71, 0.46, 1.08) forwards;
|
||||
cursor: pointer;
|
||||
transition: transform 0.3s, opacity 0.3s;
|
||||
}
|
||||
|
||||
.aksrt-toast.is-dismissing {
|
||||
transform: translateX(120%);
|
||||
opacity: 0;
|
||||
transition: transform 0.3s ease-in, opacity 0.3s ease-in;
|
||||
}
|
||||
|
||||
.aksrt-toast__icon {
|
||||
font-size: 18px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.aksrt-toast__text {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
/* Toast 类型 — 对齐 zibll 纯色 */
|
||||
.aksrt-toast--success {
|
||||
background: rgba(16, 185, 129, 0.9);
|
||||
}
|
||||
|
||||
.aksrt-toast--info {
|
||||
background: rgba(59, 130, 246, 0.9);
|
||||
}
|
||||
|
||||
.aksrt-toast--warning {
|
||||
background: rgba(245, 158, 11, 0.9);
|
||||
}
|
||||
|
||||
.aksrt-toast--danger,
|
||||
.aksrt-toast--error {
|
||||
background: rgba(239, 68, 68, 0.9);
|
||||
}
|
||||
|
||||
.aksrt-toast--loading {
|
||||
background: rgba(107, 114, 128, 0.9);
|
||||
}
|
||||
|
||||
.aksrt-toast--loading .aksrt-toast__icon {
|
||||
animation: aksrt-spin 1s linear infinite;
|
||||
}
|
||||
|
||||
.aksrt-modal-auth__error.is-success {
|
||||
background: rgba(16, 185, 129, 0.08);
|
||||
border-color: rgba(16, 185, 129, 0.22);
|
||||
@@ -103,24 +26,13 @@
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
@keyframes aksrt-toast-in {
|
||||
from {
|
||||
transform: translateX(120%);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: translateX(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes aksrt-spin {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 2. 骨架屏加载动画
|
||||
* 1. 骨架屏加载动画
|
||||
* ============================================================ */
|
||||
.aksrt-skeleton {
|
||||
background: linear-gradient(
|
||||
@@ -192,7 +104,7 @@
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 3. 悬停交互动效
|
||||
* 2. 悬停交互动效
|
||||
* ============================================================ */
|
||||
|
||||
/* 卡片放大 */
|
||||
@@ -271,7 +183,7 @@
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 4. 暗色模式过渡 (View Transitions API)
|
||||
* 3. 暗色模式过渡 (View Transitions API)
|
||||
* ============================================================ */
|
||||
::view-transition-old(root),
|
||||
::view-transition-new(root) {
|
||||
@@ -307,14 +219,10 @@ body.dark-theme::view-transition-new(root) {
|
||||
--aksrt-blur-bg: var(--surface-1);
|
||||
}
|
||||
|
||||
.aksrt-toast {
|
||||
backdrop-filter: none;
|
||||
-webkit-backdrop-filter: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 5. 滚动方向感知
|
||||
* 4. 滚动方向感知
|
||||
* ============================================================ */
|
||||
|
||||
/* 向下滚动时隐藏导航栏 */
|
||||
@@ -348,7 +256,7 @@ body.aksrt-body-scroll .aksrt-show-on-scroll {
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 6. 图片灯箱
|
||||
* 5. 图片灯箱
|
||||
* ============================================================ */
|
||||
.aksrt-lightbox {
|
||||
position: fixed;
|
||||
|
||||
@@ -3,12 +3,6 @@
|
||||
* 响应式
|
||||
* ============================================================ */
|
||||
@media (max-width: 640px) {
|
||||
.aksrt-toast-container {
|
||||
right: 8px;
|
||||
left: 8px;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.aksrt-lightbox__nav {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
@@ -59,10 +53,6 @@
|
||||
animation: none !important;
|
||||
}
|
||||
|
||||
.aksrt-toast {
|
||||
animation: none !important;
|
||||
}
|
||||
|
||||
[data-aksrt-animate] {
|
||||
opacity: 1 !important;
|
||||
transform: none !important;
|
||||
@@ -230,15 +220,6 @@ input:focus, textarea:focus, select:focus,
|
||||
transform-origin: top;
|
||||
}
|
||||
|
||||
/* --- Toast 通知动效 --- */
|
||||
.aksrt-toast {
|
||||
animation: aksrtToastIn 0.35s var(--ease-out) forwards;
|
||||
}
|
||||
|
||||
.aksrt-toast--out {
|
||||
animation: aksrtToastOut 0.3s var(--ease) forwards;
|
||||
}
|
||||
|
||||
/* --- 骨架屏动效 --- */
|
||||
.aksrt-skeleton {
|
||||
animation: aksrtSkeletonShimmer 1.5s infinite linear;
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
* Zibll-compatible frontend notifications.
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
.notyn {
|
||||
position: fixed;
|
||||
bottom: 6em;
|
||||
right: 0;
|
||||
max-width: 320px;
|
||||
z-index: 10000000;
|
||||
}
|
||||
|
||||
.notyf {
|
||||
margin-bottom: 0.6em;
|
||||
color: #fff;
|
||||
float: right;
|
||||
min-width: 200px;
|
||||
padding: 1em 3em 1em 3.4em;
|
||||
box-shadow: -3px 3px 6px rgba(0, 0, 0, 0.1);
|
||||
transform: translateX(1.5em);
|
||||
transition: 0.3s;
|
||||
-webkit-backdrop-filter: saturate(5) blur(20px);
|
||||
backdrop-filter: saturate(5) blur(20px);
|
||||
-webkit-animation: notyf 0.4s cubic-bezier(0.35, 0.71, 0.46, 1.08);
|
||||
animation: notyf 0.4s cubic-bezier(0.35, 0.71, 0.46, 1.08);
|
||||
}
|
||||
|
||||
.notyn-out .notyf {
|
||||
transform: translateX(110%);
|
||||
opacity: 0.5;
|
||||
transition: 0.5s;
|
||||
}
|
||||
|
||||
@-webkit-keyframes notyf {
|
||||
0% {
|
||||
transform: translateX(110%);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateX(1.5em);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes notyf {
|
||||
0% {
|
||||
transform: translateX(110%);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateX(1.5em);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.notyf::before {
|
||||
font: normal normal normal 16px/1 FontAwesome;
|
||||
left: 0.8em;
|
||||
font-size: 1.5em;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
margin: auto;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.notyf.success {
|
||||
background: linear-gradient(90deg, rgba(15, 147, 249, 0.7), rgba(61, 189, 249, 0.8));
|
||||
}
|
||||
|
||||
.notyf.success::before {
|
||||
content: '\f058';
|
||||
}
|
||||
|
||||
.notyf.info {
|
||||
background: linear-gradient(90deg, rgba(58, 162, 54, 0.8), rgba(89, 247, 131, 0.8));
|
||||
}
|
||||
|
||||
.notyf.info::before {
|
||||
content: '\f0a1';
|
||||
}
|
||||
|
||||
.notyf.warning,
|
||||
.notyf.load {
|
||||
background: linear-gradient(90deg, rgba(253, 170, 71, 0.7), rgba(247, 154, 13, 0.8));
|
||||
}
|
||||
|
||||
.notyf.warning::before {
|
||||
content: '\f05a';
|
||||
}
|
||||
|
||||
.notyf.danger {
|
||||
background: linear-gradient(90deg, rgba(253, 69, 28, 0.7), rgba(251, 110, 75, 0.8));
|
||||
}
|
||||
|
||||
.notyf.danger::before {
|
||||
content: '\f057';
|
||||
}
|
||||
|
||||
.notyf .fa-close {
|
||||
float: right;
|
||||
padding: 0.6em;
|
||||
margin: -0.4em;
|
||||
}
|
||||
|
||||
.notyf.load::before {
|
||||
content: '';
|
||||
width: 0.7em;
|
||||
height: 0.7em;
|
||||
display: inline-block;
|
||||
border: 0.08em solid transparent;
|
||||
border-width: calc(1px + 0.03em);
|
||||
border-radius: 50%;
|
||||
border-top-color: var(--this-color);
|
||||
border-bottom-color: var(--this-color);
|
||||
-webkit-animation: huan-rotate 1s cubic-bezier(0.7, 0.1, 0.31, 0.9) infinite;
|
||||
animation: huan-rotate 1s cubic-bezier(0.7, 0.1, 0.31, 0.9) infinite;
|
||||
}
|
||||
|
||||
@-webkit-keyframes huan-rotate {
|
||||
0% {
|
||||
transform: rotate(0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes huan-rotate {
|
||||
0% {
|
||||
transform: rotate(0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,6 @@
|
||||
.footer-tabbar,
|
||||
.float-right,
|
||||
.float-btn,
|
||||
.aksrt-toast-container,
|
||||
.resource-mobile-fab,
|
||||
.resource-mobile-download-bar,
|
||||
.sidebar,
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
* 通过 class-aksrt-scripts.php 在 is_page_template('templates/pages/page-profile.php') 时加载。
|
||||
*
|
||||
* 覆盖:
|
||||
* - Toast 通知容器(仅 profile 页)
|
||||
* - 公开个人主页头
|
||||
* - 仪表盘标签 + 数据表
|
||||
* - 个人资料编辑表单 + 联系方式行
|
||||
@@ -14,46 +13,6 @@
|
||||
* - 密码强度条
|
||||
*/
|
||||
|
||||
/* ==================== Toast(profile 页专用) ==================== */
|
||||
#toast-container {
|
||||
position: fixed;
|
||||
bottom: calc(24px + env(safe-area-inset-bottom, 0px));
|
||||
right: calc(24px + env(safe-area-inset-right, 0px));
|
||||
z-index: 99999;
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
gap: 10px;
|
||||
pointer-events: none;
|
||||
}
|
||||
.toast-item {
|
||||
pointer-events: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 10px 20px;
|
||||
background: rgba(0, 0, 0, 0.78);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
border-radius: 100px;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
|
||||
animation: toastIn 0.35s cubic-bezier(0.22, 1, 0.36, 1) forwards;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.toast-item.fade-out {
|
||||
animation: toastOut 0.4s ease forwards;
|
||||
}
|
||||
@keyframes toastIn {
|
||||
0% { opacity: 0; transform: translateY(16px) scale(0.92); }
|
||||
100% { opacity: 1; transform: translateY(0) scale(1); }
|
||||
}
|
||||
@keyframes toastOut {
|
||||
0% { opacity: 1; transform: translateY(0) scale(1); }
|
||||
100% { opacity: 0; transform: translateY(8px) scale(0.95); }
|
||||
}
|
||||
|
||||
/* 用户主页 / 用户中心统一使用站点内容宽度,避免旧 page/dashboard 容器收窄。
|
||||
* main 自身同时带 content-shell + aksrt-profile,不能写成 .aksrt-profile .content-shell。 */
|
||||
.aksrt-profile.content-shell,
|
||||
|
||||
+8
-126
@@ -3,7 +3,6 @@
|
||||
*
|
||||
* Unified core primitives for the entire frontend:
|
||||
* aksrt.tbquire(deps, cb) — lazy script/CSS loader
|
||||
* aksrt.notyf(msg, opts) — unified toast (same-ID update)
|
||||
* aksrt.confirm(msg, opts) — i18n confirm dialog
|
||||
* aksrt.scrollTopTo(target, off) — smart scroll with header offset
|
||||
* aksrt.loading(el, show) — element-level loading overlay
|
||||
@@ -93,123 +92,6 @@
|
||||
return p;
|
||||
};
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
// §10.10 — notyf: unified toast with same-ID update
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
|
||||
var _notyfContainer = null;
|
||||
var _notyfMap = {};
|
||||
var NOTYF_DURATION = 5000;
|
||||
|
||||
function getNotyfContainer() {
|
||||
if (_notyfContainer && _notyfContainer.parentNode) return _notyfContainer;
|
||||
_notyfContainer = document.createElement('div');
|
||||
_notyfContainer.className = 'aksrt-notyf';
|
||||
_notyfContainer.setAttribute('role', 'alert');
|
||||
_notyfContainer.setAttribute('aria-live', 'polite');
|
||||
document.body.appendChild(_notyfContainer);
|
||||
return _notyfContainer;
|
||||
}
|
||||
|
||||
function createNotyfItem(msg, opts) {
|
||||
var el = document.createElement('div');
|
||||
el.className = 'aksrt-notyf__item aksrt-notyf--' + (opts.type || 'info');
|
||||
if (opts.type === 'load') el.classList.add('aksrt-notyf--loading');
|
||||
|
||||
var iconMap = {
|
||||
success: '<i class="fas fa-check-circle"></i>',
|
||||
danger: '<i class="fas fa-exclamation-circle"></i>',
|
||||
error: '<i class="fas fa-exclamation-circle"></i>',
|
||||
warning: '<i class="fas fa-exclamation-triangle"></i>',
|
||||
load: '<i class="fas fa-spinner fa-spin"></i>',
|
||||
info: '<i class="fas fa-info-circle"></i>'
|
||||
};
|
||||
|
||||
el.innerHTML =
|
||||
'<span class="aksrt-notyf__icon">' + (iconMap[opts.type] || iconMap.info) + '</span>' +
|
||||
'<span class="aksrt-notyf__text">' + escapeHtml(msg) + '</span>' +
|
||||
'<button type="button" class="aksrt-notyf__close" aria-label="Close">×</button>';
|
||||
|
||||
el.querySelector('.aksrt-notyf__close').addEventListener('click', function () {
|
||||
removeNotyf(el, opts.id);
|
||||
});
|
||||
|
||||
return el;
|
||||
}
|
||||
|
||||
function removeNotyf(el, id) {
|
||||
el.classList.add('aksrt-notyf--out');
|
||||
setTimeout(function () {
|
||||
if (el.parentNode) el.parentNode.removeChild(el);
|
||||
}, 300);
|
||||
if (id && _notyfMap[id]) {
|
||||
clearTimeout(_notyfMap[id].timer);
|
||||
delete _notyfMap[id];
|
||||
}
|
||||
}
|
||||
|
||||
aksrt.notyf = function (msg, opts) {
|
||||
if (typeof opts === 'string') opts = { type: opts };
|
||||
opts = opts || {};
|
||||
var type = opts.type || 'info';
|
||||
var duration = type === 'load' ? 0 : (opts.duration || NOTYF_DURATION);
|
||||
var id = opts.id || null;
|
||||
|
||||
var container = getNotyfContainer();
|
||||
|
||||
if (id && _notyfMap[id]) {
|
||||
var existing = _notyfMap[id];
|
||||
clearTimeout(existing.timer);
|
||||
existing.el.className = 'aksrt-notyf__item aksrt-notyf--' + type;
|
||||
existing.el.querySelector('.aksrt-notyf__text').textContent = msg;
|
||||
var iconSpan = existing.el.querySelector('.aksrt-notyf__icon');
|
||||
var iconMap2 = {
|
||||
success: '<i class="fas fa-check-circle"></i>',
|
||||
danger: '<i class="fas fa-exclamation-circle"></i>',
|
||||
error: '<i class="fas fa-exclamation-circle"></i>',
|
||||
warning: '<i class="fas fa-exclamation-triangle"></i>',
|
||||
load: '<i class="fas fa-spinner fa-spin"></i>',
|
||||
info: '<i class="fas fa-info-circle"></i>'
|
||||
};
|
||||
iconSpan.innerHTML = iconMap2[type] || iconMap2.info;
|
||||
if (duration > 0) {
|
||||
existing.timer = setTimeout(function () { removeNotyf(existing.el, id); }, duration);
|
||||
}
|
||||
return existing.el;
|
||||
}
|
||||
|
||||
var el = createNotyfItem(msg, { type: type, id: id });
|
||||
container.appendChild(el);
|
||||
|
||||
requestAnimationFrame(function () {
|
||||
el.classList.add('aksrt-notyf--in');
|
||||
});
|
||||
|
||||
var timer = null;
|
||||
if (duration > 0) {
|
||||
timer = setTimeout(function () { removeNotyf(el, id); }, duration);
|
||||
}
|
||||
|
||||
if (id) {
|
||||
_notyfMap[id] = { el: el, timer: timer };
|
||||
}
|
||||
|
||||
return el;
|
||||
};
|
||||
|
||||
aksrt.notyf.close = function (id) {
|
||||
if (_notyfMap[id]) removeNotyf(_notyfMap[id].el, id);
|
||||
};
|
||||
|
||||
// Backward compat: wire window.aksrtToast to notyf
|
||||
window.aksrtToast = function (msg, type) {
|
||||
aksrt.notyf(msg, { type: type || 'info', duration: 2200 });
|
||||
};
|
||||
// Alias used by several modules that call aksrt.toast(...)
|
||||
aksrt.toast = function (msg, type) {
|
||||
return aksrt.notyf(msg, typeof type === 'string' ? { type: type, duration: 2200 } : (type || { duration: 2200 }));
|
||||
};
|
||||
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
// §10.10 — confirm: i18n confirm dialog + [data-confirm]
|
||||
// ══════════════════════════════════════════════════════════════
|
||||
@@ -551,7 +433,7 @@
|
||||
if (action === 'like' || action === 'favorite') {
|
||||
try {
|
||||
if (localStorage.getItem(storageKey) && !btn.classList.contains('is-active')) {
|
||||
aksrt.notyf(__('already_done', '已操作'), 'warning');
|
||||
notyf(__('already_done', '已操作'), 'warning');
|
||||
return;
|
||||
}
|
||||
} catch (ex) { /* storage unavailable */ }
|
||||
@@ -562,7 +444,7 @@
|
||||
var ajaxUrl = (window.aksrtData && aksrtData.ajaxUrl) || '/wp-admin/admin-ajax.php';
|
||||
if (!(window.aksrtData && aksrtData.nonce)) {
|
||||
btn.classList.remove('is-loading');
|
||||
aksrt.notyf(__('action_failed', '操作失败'), 'danger');
|
||||
notyf(__('action_failed', '操作失败'), 'danger');
|
||||
return;
|
||||
}
|
||||
var params = new URLSearchParams();
|
||||
@@ -602,19 +484,19 @@
|
||||
} catch (ex) { /* */ }
|
||||
|
||||
if (json.data && json.data.message) {
|
||||
aksrt.notyf(json.data.message, 'success');
|
||||
notyf(json.data.message, 'success');
|
||||
}
|
||||
|
||||
if (json.data && json.data.goto) {
|
||||
window.location.href = json.data.goto;
|
||||
}
|
||||
} else {
|
||||
aksrt.notyf((json.data && json.data.message) || __('action_failed', '操作失败'), 'danger');
|
||||
notyf((json.data && json.data.message) || __('action_failed', '操作失败'), 'danger');
|
||||
}
|
||||
})
|
||||
.catch(function (err) {
|
||||
btn.classList.remove('is-loading');
|
||||
aksrt.notyf(err.message || __('network_error', '网络错误'), 'danger');
|
||||
notyf(err.message || __('network_error', '网络错误'), 'danger');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -651,7 +533,7 @@
|
||||
kind: 'generic',
|
||||
});
|
||||
} else if (json.data && json.data.message) {
|
||||
aksrt.notyf(json.data.message, 'success');
|
||||
notyf(json.data.message, 'success');
|
||||
}
|
||||
if (json.data && json.data.goto) {
|
||||
window.location.href = json.data.goto;
|
||||
@@ -660,12 +542,12 @@
|
||||
window.location.reload();
|
||||
}
|
||||
} else {
|
||||
aksrt.notyf((json.data && json.data.message) || __('action_failed', '操作失败'), 'danger');
|
||||
notyf((json.data && json.data.message) || __('action_failed', '操作失败'), 'danger');
|
||||
}
|
||||
})
|
||||
.catch(function (err) {
|
||||
btn.classList.remove('is-loading');
|
||||
aksrt.notyf(err.message || __('network_error', '网络错误'), 'danger');
|
||||
notyf(err.message || __('network_error', '网络错误'), 'danger');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+6
-23
@@ -12,23 +12,6 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
function notify(message, type) {
|
||||
if (!message) return;
|
||||
if (typeof window.AksrtToast === 'function') {
|
||||
window.AksrtToast(message, type || 'info');
|
||||
return;
|
||||
}
|
||||
if (typeof window.showToast === 'function') {
|
||||
window.showToast(message, type || 'info');
|
||||
return;
|
||||
}
|
||||
if (typeof window.aksrtToast === 'function') {
|
||||
window.aksrtToast(message, type || 'info');
|
||||
return;
|
||||
}
|
||||
window.alert(message);
|
||||
}
|
||||
|
||||
function updateTabUrl(root, name, defaultTab) {
|
||||
if (!window.history || typeof window.history.replaceState !== 'function') return;
|
||||
var url = new URL(window.location.href);
|
||||
@@ -149,7 +132,7 @@
|
||||
if (!file) return;
|
||||
|
||||
if (upload.maxSize && file.size > upload.maxSize) {
|
||||
notify(upload.tooLargeText || '图片大小不能超过 2MB', 'warning');
|
||||
notyf(upload.tooLargeText || '图片大小不能超过 2MB', 'warning');
|
||||
input.value = '';
|
||||
return;
|
||||
}
|
||||
@@ -180,7 +163,7 @@
|
||||
.then(function (payload) {
|
||||
if (payload && payload.success && payload.url) {
|
||||
applyImage(payload.url);
|
||||
notify(upload.successText || '背景图已更新', 'success');
|
||||
notyf(upload.successText || '背景图已更新', 'success');
|
||||
return;
|
||||
}
|
||||
throw payload || { message: upload.errorText || '上传失败' };
|
||||
@@ -190,7 +173,7 @@
|
||||
? error.data.previous_url
|
||||
: '';
|
||||
resetImage(previousUrl);
|
||||
notify((error && error.message) || upload.networkErrorText || '网络错误,请重试', 'error');
|
||||
notyf((error && error.message) || upload.networkErrorText || '网络错误,请重试', 'danger');
|
||||
})
|
||||
.finally(function () {
|
||||
setBusy(false);
|
||||
@@ -222,7 +205,7 @@
|
||||
var file = input.files && input.files[0];
|
||||
if (!file) return;
|
||||
if (upload.maxSize && file.size > upload.maxSize) {
|
||||
notify(upload.tooLargeText || '图片大小不能超过 2MB', 'warning');
|
||||
notyf(upload.tooLargeText || '图片大小不能超过 2MB', 'warning');
|
||||
input.value = '';
|
||||
return;
|
||||
}
|
||||
@@ -246,10 +229,10 @@
|
||||
var url = payload.url || (payload.data && payload.data.url) || '';
|
||||
if (!url) throw payload;
|
||||
if (image) image.src = url;
|
||||
notify(payload.message || upload.successText || '头像已更新', 'success');
|
||||
notyf(payload.message || upload.successText || '头像已更新', 'success');
|
||||
})
|
||||
.catch(function (error) {
|
||||
notify((error && error.message) || upload.errorText || '上传失败', 'error');
|
||||
notyf((error && error.message) || upload.errorText || '上传失败', 'danger');
|
||||
})
|
||||
.finally(function () {
|
||||
setBusy(false);
|
||||
|
||||
@@ -9,13 +9,6 @@
|
||||
if (window._win && window._win.ajax_url) return window._win.ajax_url;
|
||||
return '/wp-admin/admin-ajax.php';
|
||||
}
|
||||
function toast(msg) {
|
||||
if (window.aksrt && typeof window.aksrt.toast === 'function') {
|
||||
window.aksrt.toast(msg);
|
||||
return;
|
||||
}
|
||||
window.alert(msg);
|
||||
}
|
||||
function init() {
|
||||
var form = document.querySelector('.aksrt-ban-appeal-form');
|
||||
if (!form) return;
|
||||
@@ -29,13 +22,13 @@
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (data) {
|
||||
if (!data || data.error) {
|
||||
toast((data && data.msg) || '提交失败');
|
||||
notyf((data && data.msg) || '提交失败', 'danger');
|
||||
return;
|
||||
}
|
||||
toast(data.msg || '提交成功');
|
||||
notyf(data.msg || '提交成功');
|
||||
if (data.reload) window.setTimeout(function () { location.reload(); }, 400);
|
||||
})
|
||||
.catch(function () { toast('网络错误'); })
|
||||
.catch(function () { notyf('网络错误', 'danger'); })
|
||||
.finally(function () { if (btn) btn.disabled = false; });
|
||||
});
|
||||
}
|
||||
|
||||
+10
-17
@@ -34,13 +34,6 @@
|
||||
}).then(function (r) { return r.json(); });
|
||||
}
|
||||
|
||||
function notify(message, type) {
|
||||
if (typeof window.AksrtToast === 'function') { window.AksrtToast(message, type || 'info'); return; }
|
||||
if (typeof window.aksrtToast === 'function') { window.aksrtToast(message, type || 'info'); return; }
|
||||
if (typeof window.showToast === 'function') { window.showToast(message, type || 'info'); return; }
|
||||
if (type === 'error') { window.alert(message); }
|
||||
}
|
||||
|
||||
function getCommentId(el) {
|
||||
return parseInt(el.getAttribute('data-comment-id') || (el.id || '').replace('comment-', ''), 10) || 0;
|
||||
}
|
||||
@@ -77,7 +70,7 @@
|
||||
|
||||
restPost('/comment/like', { comment_id: commentId, active: nextActive })
|
||||
.then(function (result) {
|
||||
if (!result.success) { notify((result.data && result.data.message) || result.message || '操作失败', 'error'); return; }
|
||||
if (!result.success) { notyf((result.data && result.data.message) || result.message || '操作失败', 'danger'); return; }
|
||||
var active = !!(result.data && result.data.active);
|
||||
var count = (result.data && result.data.count) || 0;
|
||||
btn.setAttribute('data-active', active ? '1' : '0');
|
||||
@@ -91,7 +84,7 @@
|
||||
if (count > 0) { countEl.removeAttribute('hidden'); } else { countEl.setAttribute('hidden', ''); }
|
||||
}
|
||||
})
|
||||
.catch(function () { notify('网络错误', 'error'); })
|
||||
.catch(function () { notyf('网络错误', 'danger'); })
|
||||
.finally(function () { btn.dataset.busy = ''; });
|
||||
}
|
||||
|
||||
@@ -120,10 +113,10 @@
|
||||
}
|
||||
}, 300);
|
||||
} else {
|
||||
notify((result.data && result.data.message) || result.message || '删除失败', 'error');
|
||||
notyf((result.data && result.data.message) || result.message || '删除失败', 'danger');
|
||||
}
|
||||
})
|
||||
.catch(function () { notify('网络错误', 'error'); });
|
||||
.catch(function () { notyf('网络错误', 'danger'); });
|
||||
};
|
||||
|
||||
if (window.aksrt && aksrt.confirm) {
|
||||
@@ -138,15 +131,15 @@
|
||||
|
||||
restPost('/comment/pin', { comment_id: commentId, pinned: nextPinned })
|
||||
.then(function (result) {
|
||||
if (!result.success) { notify((result.data && result.data.message) || result.message || '操作失败', 'error'); return; }
|
||||
if (!result.success) { notyf((result.data && result.data.message) || result.message || '操作失败', 'danger'); return; }
|
||||
var pinned = !!(result.data && result.data.pinned);
|
||||
btn.setAttribute('data-pinned', pinned ? '1' : '0');
|
||||
btn.innerHTML = '<i class="fas fa-thumbtack" aria-hidden="true"></i> ' + (pinned ? '取消置顶' : '置顶');
|
||||
commentEl.classList.toggle('aksrt-comment--pinned', pinned);
|
||||
syncPinnedBadge(commentEl, pinned);
|
||||
notify(result.message || (pinned ? '评论已置顶。' : '已取消置顶。'), 'success');
|
||||
notyf(result.message || (pinned ? '评论已置顶。' : '已取消置顶。'), 'success');
|
||||
})
|
||||
.catch(function () { notify('网络错误', 'error'); });
|
||||
.catch(function () { notyf('网络错误', 'danger'); });
|
||||
}
|
||||
|
||||
function syncPinnedBadge(commentEl, pinned) {
|
||||
@@ -175,11 +168,11 @@
|
||||
var note = commentEl.querySelector('.awaiting-moderation');
|
||||
if (note) note.remove();
|
||||
}
|
||||
notify(result.message || '操作成功。', 'success');
|
||||
notyf(result.message || '操作成功。', 'success');
|
||||
} else {
|
||||
notify((result.data && result.data.message) || result.message || '操作失败', 'error');
|
||||
notyf((result.data && result.data.message) || result.message || '操作失败', 'danger');
|
||||
}
|
||||
})
|
||||
.catch(function () { notify('网络错误', 'error'); });
|
||||
.catch(function () { notyf('网络错误', 'danger'); });
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -4,12 +4,6 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
function notify(msg) {
|
||||
if (window.aksrt && typeof window.aksrt.toast === 'function') {
|
||||
window.aksrt.toast(msg, 'info');
|
||||
}
|
||||
}
|
||||
|
||||
function findTextarea(from) {
|
||||
var form = from && from.closest ? from.closest('form') : null;
|
||||
if (form) {
|
||||
@@ -67,7 +61,7 @@
|
||||
var ta = box ? box.querySelector('textarea') : null;
|
||||
var code = ta ? ta.value : '';
|
||||
if (!code || code.length < 2) {
|
||||
notify('请输入代码');
|
||||
notyf('请输入代码', 'warning');
|
||||
return;
|
||||
}
|
||||
insertAtCursor(findTextarea(codeBtn), '[code]\n' + code + '\n[/code]\n');
|
||||
@@ -84,7 +78,7 @@
|
||||
var input = ibox ? ibox.querySelector('.input-image-url') : null;
|
||||
var url = input ? (input.value || '').trim() : '';
|
||||
if (!/^https?:\/\//i.test(url)) {
|
||||
notify('请输入有效图片地址');
|
||||
notyf('请输入有效图片地址', 'warning');
|
||||
return;
|
||||
}
|
||||
insertAtCursor(findTextarea(imgBtn), '[img=' + url + ']');
|
||||
@@ -107,12 +101,12 @@
|
||||
.then(function (json) {
|
||||
var u = (json && (json.url || json.source_url || (json.data && (json.data.url || json.data.source_url)))) || '';
|
||||
if (!u) {
|
||||
notify((json && json.message) || '上传失败');
|
||||
notyf((json && json.message) || '上传失败', 'danger');
|
||||
return;
|
||||
}
|
||||
insertAtCursor(findTextarea(upBtn), '[img=' + u + ']');
|
||||
})
|
||||
.catch(function () { notify('上传失败'); });
|
||||
.catch(function () { notyf('上传失败', 'danger'); });
|
||||
});
|
||||
fileInput.click();
|
||||
return;
|
||||
|
||||
@@ -347,9 +347,7 @@
|
||||
container.querySelectorAll(".ajax-item.is-fading").forEach(function (n) {
|
||||
n.classList.remove("is-fading");
|
||||
});
|
||||
if (typeof window.aksrtToast === "function") {
|
||||
window.aksrtToast((err && err.message) || "加载失败");
|
||||
}
|
||||
notyf((err && err.message) || "加载失败", "danger");
|
||||
dispatch(container, "post_ajax.error", { link: link, error: err });
|
||||
throw err;
|
||||
});
|
||||
|
||||
@@ -58,14 +58,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
function showToast(msg, type) {
|
||||
if (window.aksrt && aksrt.notyf) {
|
||||
aksrt.notyf(msg, { type: type || "info", duration: 2200 });
|
||||
} else {
|
||||
console.log("[AKSRT Toast]", type, msg);
|
||||
}
|
||||
}
|
||||
|
||||
function initArticleTools() {
|
||||
var buttons = document.querySelectorAll("[data-article-tool]");
|
||||
if (!buttons.length) return;
|
||||
@@ -80,9 +72,7 @@
|
||||
|
||||
if (tool === "pdf") {
|
||||
window.print();
|
||||
if (typeof showToast === "function") {
|
||||
showToast("请在打印对话框中选择“另存为 PDF”", "info");
|
||||
}
|
||||
notyf("请在打印对话框中选择“另存为 PDF”", "info");
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -232,9 +222,9 @@
|
||||
setButtonBusy(likeBtn, true);
|
||||
requestInteraction("aksrt_toggle_like", postId, nextState).then(function (data) {
|
||||
applyLikeState(!!data.active, parseInt(data.count, 10) || 0);
|
||||
showToast(data.active ? "感谢你的点赞" : "已取消点赞", "success");
|
||||
notyf(data.active ? "感谢你的点赞" : "已取消点赞", "success");
|
||||
}).catch(function (error) {
|
||||
showToast(error && error.message ? error.message : "点赞失败", "error");
|
||||
notyf(error && error.message ? error.message : "点赞失败", "danger");
|
||||
}).finally(function () {
|
||||
setButtonBusy(likeBtn, false);
|
||||
});
|
||||
@@ -257,9 +247,9 @@
|
||||
setButtonBusy(favBtn, true);
|
||||
requestInteraction("aksrt_toggle_favorite", postId, nextState).then(function (data) {
|
||||
applyFavoriteState(!!data.active, parseInt(data.count, 10) || 0);
|
||||
showToast(data.active ? "收藏成功" : "已取消收藏", "success");
|
||||
notyf(data.active ? "收藏成功" : "已取消收藏", "success");
|
||||
}).catch(function (error) {
|
||||
showToast(error && error.message ? error.message : "收藏失败", "error");
|
||||
notyf(error && error.message ? error.message : "收藏失败", "danger");
|
||||
}).finally(function () {
|
||||
setButtonBusy(favBtn, false);
|
||||
});
|
||||
|
||||
@@ -22,16 +22,6 @@
|
||||
return d.restNonce || d.nonce || '';
|
||||
}
|
||||
|
||||
function notify(msg, type) {
|
||||
if (window.aksrt && typeof window.aksrt.toast === 'function') {
|
||||
window.aksrt.toast(msg, type || 'info');
|
||||
return;
|
||||
}
|
||||
if (window.notyf) {
|
||||
try { window.notyf[type === 'error' ? 'error' : 'success'](msg); } catch (e) {}
|
||||
}
|
||||
}
|
||||
|
||||
function setActionText(text) {
|
||||
var el = document.querySelector('#' + respondId + ' .action-text');
|
||||
if (!el) return;
|
||||
@@ -147,7 +137,7 @@
|
||||
if (submit) submit.disabled = false;
|
||||
})
|
||||
.catch(function () {
|
||||
notify('加载评论失败', 'error');
|
||||
notyf('加载评论失败', 'danger');
|
||||
if (textarea) {
|
||||
textarea.disabled = false;
|
||||
textarea.value = '';
|
||||
|
||||
+15
-23
@@ -4,14 +4,6 @@
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
function showToast(msg, type) {
|
||||
if (window.aksrt && aksrt.notyf) {
|
||||
aksrt.notyf(msg, { type: type || "info", duration: 2200 });
|
||||
} else {
|
||||
console.log("[AKSRT Toast]", type, msg);
|
||||
}
|
||||
}
|
||||
|
||||
function initCommentSystem() {
|
||||
var form = document.querySelector("#commentform");
|
||||
if (!form) return;
|
||||
@@ -125,9 +117,9 @@
|
||||
if (responseText.indexOf('审核') !== -1 ||
|
||||
responseText.indexOf('moderation') !== -1 ||
|
||||
responseText.includes('awaiting-moderation')) {
|
||||
showToast('评论已提交,等待管理员审核', 'info');
|
||||
notyf('评论已提交,等待管理员审核', 'info');
|
||||
} else {
|
||||
showToast('评论发布成功', 'success');
|
||||
notyf('评论发布成功', 'success');
|
||||
}
|
||||
|
||||
// 清空文本框
|
||||
@@ -153,7 +145,7 @@
|
||||
var errorMsg = extractErrorMessage(responseText);
|
||||
var waitSeconds = extractWaitTime(errorMsg);
|
||||
|
||||
showToast(errorMsg, 'warning');
|
||||
notyf(errorMsg, 'warning');
|
||||
|
||||
if (waitSeconds > 0) {
|
||||
startCountdown(waitSeconds);
|
||||
@@ -167,7 +159,7 @@
|
||||
// 情况3: HTTP 403 权限不足(地区/IP/关键词等)
|
||||
if (statusCode === 403) {
|
||||
var forbiddenMsg = extractErrorMessage(responseText);
|
||||
showToast(forbiddenMsg, 'error');
|
||||
notyf(forbiddenMsg, 'danger');
|
||||
setLoading(false);
|
||||
return false;
|
||||
}
|
||||
@@ -175,12 +167,12 @@
|
||||
// 情况4: 包含WordPress错误信息
|
||||
var errorMatch = responseText.match(/<p[^>]*>([^<]*(?:错误|失败|error|Error)[^<]*)<\/p>/i);
|
||||
if (errorMatch) {
|
||||
showToast(errorMatch[1].trim(), 'error');
|
||||
notyf(errorMatch[1].trim(), 'danger');
|
||||
setLoading(false);
|
||||
return false;
|
||||
}
|
||||
// 默认情况:可能是成功或其他
|
||||
showToast('评论已提交', 'success');
|
||||
notyf('评论已提交', 'success');
|
||||
var textarea2 = form.querySelector('#comment');
|
||||
if (textarea2) textarea2.value = '';
|
||||
|
||||
@@ -194,7 +186,7 @@
|
||||
|
||||
} catch (e) {
|
||||
console.error('[AKSRT Comment] 解析响应失败:', e);
|
||||
showToast('响应解析异常,请重试', 'error');
|
||||
notyf('响应解析异常,请重试', 'danger');
|
||||
setLoading(false);
|
||||
return false;
|
||||
}
|
||||
@@ -239,7 +231,7 @@
|
||||
if (editId) editId.remove();
|
||||
restoreRespondPosition();
|
||||
setLoading(false);
|
||||
showToast(json.message || '评论已更新', 'success');
|
||||
notyf(json.message || '评论已更新', 'success');
|
||||
var respondEl = document.getElementById('respond');
|
||||
if (respondEl && window.AksrtFixedInput) window.AksrtFixedInput(respondEl, 'hide');
|
||||
return;
|
||||
@@ -289,7 +281,7 @@
|
||||
}
|
||||
|
||||
if (json && json.code) {
|
||||
showToast(json.message || '评论提交失败', 'error');
|
||||
notyf(json.message || '评论提交失败', 'danger');
|
||||
try {
|
||||
var failScene = window.AKSRTCaptchaState &&
|
||||
window.AKSRTCaptchaState.scenes &&
|
||||
@@ -303,7 +295,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
showToast('评论已提交', 'success');
|
||||
notyf('评论已提交', 'success');
|
||||
setLoading(false);
|
||||
setTimeout(function () { reloadCommentList(); }, 500);
|
||||
})
|
||||
@@ -352,9 +344,9 @@
|
||||
});
|
||||
|
||||
if (needsModeration) {
|
||||
showToast('评论已提交,等待管理员审核', 'info');
|
||||
notyf('评论已提交,等待管理员审核', 'info');
|
||||
} else {
|
||||
showToast('评论发布成功', 'success');
|
||||
notyf('评论发布成功', 'success');
|
||||
}
|
||||
|
||||
if (window.AksrtFrontend) AksrtFrontend.rehydrate(newComment, 'ajax');
|
||||
@@ -397,9 +389,9 @@
|
||||
console.error('[AKSRT Comment] 提交失败:', error);
|
||||
|
||||
if (error.message === 'Failed to fetch' || error.name === 'TypeError') {
|
||||
showToast('网络连接失败,请检查网络后重试', 'error');
|
||||
notyf('网络连接失败,请检查网络后重试', 'danger');
|
||||
} else {
|
||||
showToast(error.message || '评论提交失败,请重试', 'error');
|
||||
notyf(error.message || '评论提交失败,请重试', 'danger');
|
||||
}
|
||||
|
||||
setLoading(false);
|
||||
@@ -439,7 +431,7 @@
|
||||
performSubmit();
|
||||
} else {
|
||||
// 验证失败
|
||||
showToast('人机验证未通过,请重试', 'warning');
|
||||
notyf('人机验证未通过,请重试', 'warning');
|
||||
setLoading(false);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* AKSRT Enhancements — 全局 UI 增强
|
||||
*
|
||||
* 包含:Toast 通知、图片灯箱、滚动方向感知
|
||||
* 包含:图片灯箱、滚动方向感知
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
@@ -10,90 +10,7 @@
|
||||
'use strict';
|
||||
|
||||
/* ============================================================
|
||||
* 1. Toast 通知系统
|
||||
* ============================================================ */
|
||||
var toastContainer = null;
|
||||
|
||||
function ensureToastContainer() {
|
||||
if (!toastContainer || !document.body.contains(toastContainer)) {
|
||||
toastContainer = document.createElement('div');
|
||||
toastContainer.className = 'aksrt-toast-container';
|
||||
document.body.appendChild(toastContainer);
|
||||
}
|
||||
return toastContainer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示 Toast 通知
|
||||
*
|
||||
* @param {string} message 消息文本
|
||||
* @param {string} type 类型: success, info, warning, danger/error, loading
|
||||
* @param {number} duration 显示时长(ms),0 表示不自动关闭
|
||||
* @returns {Object} { dismiss: Function, update: Function }
|
||||
*/
|
||||
function showToast(message, type, duration) {
|
||||
type = type || 'success';
|
||||
if (type === 'error') type = 'danger';
|
||||
duration = duration !== undefined ? duration : 3500;
|
||||
if (duration > 0 && duration < 100) duration = duration * 1000;
|
||||
|
||||
var container = ensureToastContainer();
|
||||
|
||||
var icons = {
|
||||
success: 'fas fa-check-circle',
|
||||
info: 'fas fa-bell',
|
||||
warning: 'fas fa-exclamation-circle',
|
||||
danger: 'fas fa-times-circle',
|
||||
loading: 'fas fa-spinner',
|
||||
};
|
||||
|
||||
var el = document.createElement('div');
|
||||
el.className = 'aksrt-toast aksrt-toast--' + type;
|
||||
el.innerHTML =
|
||||
'<span class="aksrt-toast__icon"><i class="' + (icons[type] || icons.info) + '"></i></span>' +
|
||||
'<span class="aksrt-toast__text">' + message + '</span>';
|
||||
|
||||
container.appendChild(el);
|
||||
|
||||
var timer = null;
|
||||
|
||||
function dismiss() {
|
||||
if (timer) clearTimeout(timer);
|
||||
el.classList.add('is-dismissing');
|
||||
setTimeout(function () {
|
||||
if (el.parentNode) el.remove();
|
||||
}, 300);
|
||||
}
|
||||
|
||||
function update(newMessage, newType) {
|
||||
if (newType) {
|
||||
el.className = 'aksrt-toast aksrt-toast--' + newType;
|
||||
var iconEl = el.querySelector('.aksrt-toast__icon i');
|
||||
if (iconEl) iconEl.className = icons[newType] || icons.info;
|
||||
}
|
||||
var textEl = el.querySelector('.aksrt-toast__text');
|
||||
if (textEl) textEl.textContent = newMessage;
|
||||
|
||||
if (newType !== 'loading' && duration > 0) {
|
||||
if (timer) clearTimeout(timer);
|
||||
timer = setTimeout(dismiss, duration);
|
||||
}
|
||||
}
|
||||
|
||||
el.addEventListener('click', dismiss);
|
||||
|
||||
if (type !== 'loading' && duration > 0) {
|
||||
timer = setTimeout(dismiss, duration);
|
||||
}
|
||||
|
||||
return { dismiss: dismiss, update: update };
|
||||
}
|
||||
|
||||
// 暴露到全局
|
||||
window.AksrtToast = showToast;
|
||||
|
||||
/* ============================================================
|
||||
* 2. 图片灯箱 — zoom / thumbnails / slideshow / fullscreen /
|
||||
* 1. 图片灯箱 — zoom / thumbnails / slideshow / fullscreen /
|
||||
* mousewheel / origin animation / smart download
|
||||
* ============================================================ */
|
||||
var lightboxEl = null;
|
||||
@@ -493,7 +410,7 @@
|
||||
};
|
||||
|
||||
/* ============================================================
|
||||
* 3. 滚动方向感知
|
||||
* 2. 滚动方向感知
|
||||
* ============================================================ */
|
||||
// Scroll direction is now handled by aksrt-runtime.js body scroll bus
|
||||
// (body.scroll-down / body.scroll-up / body.body-scroll).
|
||||
@@ -521,7 +438,7 @@
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 4. 导航溢出折叠
|
||||
* 3. 导航溢出折叠
|
||||
* 由 assets/js/core/nav.js(zibll 1:1 navFolding)负责。
|
||||
* 此处不再重复实现,避免两套逻辑争抢 .aksrt-nav-more 节点
|
||||
* 触发 NotFoundError: insertBefore (moreLi 已被对方 remove)。
|
||||
|
||||
+2
-14
@@ -40,11 +40,11 @@
|
||||
var isFollowing = result.data.is_following;
|
||||
updateFollowButton(btn, isFollowing, result.data.follower_count);
|
||||
} else {
|
||||
showToast(result.message || '操作失败', 'error');
|
||||
notyf(result.message || '操作失败', 'danger');
|
||||
}
|
||||
})
|
||||
.catch(function () {
|
||||
showToast('网络错误,请重试', 'error');
|
||||
notyf('网络错误,请重试', 'danger');
|
||||
})
|
||||
.finally(function () {
|
||||
btn.disabled = false;
|
||||
@@ -108,18 +108,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
function showToast(msg, type) {
|
||||
var toast = document.createElement('div');
|
||||
toast.textContent = msg;
|
||||
toast.style.cssText = 'position:fixed;top:20px;left:50%;transform:translateX(-50%);padding:12px 24px;border-radius:6px;color:#fff;font-size:14px;z-index:10000;transition:opacity 0.3s;';
|
||||
toast.style.background = type === 'error' ? '#e74c3c' : '#27ae60';
|
||||
document.body.appendChild(toast);
|
||||
setTimeout(function () {
|
||||
toast.style.opacity = '0';
|
||||
setTimeout(function () { toast.remove(); }, 300);
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', initFollowButtons);
|
||||
} else {
|
||||
|
||||
@@ -29,27 +29,6 @@
|
||||
return (window.aksrtData && window.aksrtData.restNonce) || '';
|
||||
}
|
||||
|
||||
function toast(msg, type) {
|
||||
type = type || 'info';
|
||||
// Site-wide notification widget (aksrt-runtime notyf).
|
||||
if (window.aksrt && typeof window.aksrt.notyf === 'function') {
|
||||
window.aksrt.notyf(msg, { type: type, duration: 2200 });
|
||||
return;
|
||||
}
|
||||
if (typeof window.aksrtToast === 'function') {
|
||||
window.aksrtToast(msg, type);
|
||||
return;
|
||||
}
|
||||
if (typeof window.AksrtToast === 'function') {
|
||||
window.AksrtToast(msg, type);
|
||||
return;
|
||||
}
|
||||
// Last resort: non-blocking console (never native alert for UX).
|
||||
if (window.console && typeof window.console.log === 'function') {
|
||||
window.console.log('[aksrt]', type, msg);
|
||||
}
|
||||
}
|
||||
|
||||
function openModal(id) {
|
||||
var modal = document.getElementById(id);
|
||||
if (!modal) return;
|
||||
@@ -110,11 +89,11 @@
|
||||
.then(function (res) {
|
||||
if (!res.ok) {
|
||||
var msg = (res.data && (res.data.message || res.data.msg)) || '签到失败';
|
||||
toast(msg, 'danger');
|
||||
notyf(msg, 'danger');
|
||||
btn.disabled = false;
|
||||
return;
|
||||
}
|
||||
toast((res.data && res.data.message) || '签到成功', 'success');
|
||||
notyf((res.data && res.data.message) || '签到成功', 'success');
|
||||
btn.classList.add('is-done');
|
||||
btn.disabled = true;
|
||||
btn.setAttribute('disabled', 'disabled');
|
||||
@@ -127,7 +106,7 @@
|
||||
'<span>' + signedLabel + '</span>';
|
||||
})
|
||||
.catch(function () {
|
||||
toast('网络错误', 'danger');
|
||||
notyf('网络错误', 'danger');
|
||||
btn.disabled = false;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -382,11 +382,7 @@
|
||||
updateNavUser(data.user);
|
||||
}
|
||||
|
||||
if (window.aksrt && aksrt.notyf) {
|
||||
aksrt.notyf(data.message || '登录成功', 'success');
|
||||
} else if (typeof window.aksrtToast === 'function') {
|
||||
window.aksrtToast(data.message || '登录成功', 'success');
|
||||
}
|
||||
notyf(data.message || '登录成功', 'success');
|
||||
|
||||
var redirect = modal.dataset.redirect || data.redirect;
|
||||
if (redirect && redirect !== window.location.href) {
|
||||
@@ -589,7 +585,7 @@
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
agree.closest('label, .aksrt-modal-auth__agreement').classList.add('ani', 'shake');
|
||||
if (window.aksrt && aksrt.notyf) aksrt.notyf('请先同意用户协议', 'warning');
|
||||
notyf('请先同意用户协议', 'warning');
|
||||
setTimeout(function () {
|
||||
agree.closest('label, .aksrt-modal-auth__agreement').classList.remove('ani', 'shake');
|
||||
}, 400);
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Zibll-compatible frontend notifications.
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
(function ($, window, document) {
|
||||
'use strict';
|
||||
|
||||
function closeNotyf($element) {
|
||||
$element.addClass('notyn-out');
|
||||
window.setTimeout(function () {
|
||||
$element.remove();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
function notyf(message, type, time, id) {
|
||||
var $body = $(document.body);
|
||||
|
||||
if (!$('.notyn').length) {
|
||||
$body.append('<div class="notyn"></div>');
|
||||
}
|
||||
|
||||
type = type || 'success';
|
||||
if (type === 'error') {
|
||||
type = 'danger';
|
||||
}
|
||||
|
||||
time = time || 5000;
|
||||
time = time < 100 ? time * 1000 : time;
|
||||
|
||||
var idAttribute = id ? ' id="' + id + '"' : '';
|
||||
var $notification = $('<div class="noty1"' + idAttribute + '><div class="notyf ' + type + '">' + message + '</div></div>');
|
||||
var shouldClose = !id;
|
||||
|
||||
if (id && $('#' + id).length) {
|
||||
$('#' + id)
|
||||
.find('.notyf')
|
||||
.removeClass()
|
||||
.addClass('notyf ' + type)
|
||||
.html(message);
|
||||
$notification = $('#' + id);
|
||||
shouldClose = true;
|
||||
} else {
|
||||
$('.notyn').append($notification);
|
||||
}
|
||||
|
||||
if (shouldClose) {
|
||||
window.setTimeout(function () {
|
||||
closeNotyf($notification);
|
||||
}, time);
|
||||
}
|
||||
}
|
||||
|
||||
$(document.body).on('click', '.noty1', function () {
|
||||
closeNotyf($(this));
|
||||
});
|
||||
|
||||
window.notyf = notyf;
|
||||
})(jQuery, window, document);
|
||||
@@ -8,10 +8,6 @@
|
||||
|
||||
var instances = new WeakMap();
|
||||
|
||||
function toast(message, type) {
|
||||
window.AksrtToast(message, type || "info");
|
||||
}
|
||||
|
||||
function request(path, body, method) {
|
||||
var options = { method: method || "POST" };
|
||||
if (body !== undefined) options.body = JSON.stringify(body || {});
|
||||
@@ -243,7 +239,7 @@
|
||||
renderView(state, result.data);
|
||||
return result.data;
|
||||
}).catch(function (error) {
|
||||
toast(errorMessage(error, "订单状态已变更,请刷新后查看"));
|
||||
notyf(errorMessage(error, "订单状态已变更,请刷新后查看"), "danger");
|
||||
return null;
|
||||
});
|
||||
}
|
||||
@@ -260,12 +256,12 @@
|
||||
window.location.assign(data.payment_url);
|
||||
return;
|
||||
}
|
||||
toast(data.message || "支付成功", "success");
|
||||
notyf(data.message || "支付成功", "success");
|
||||
refreshView(state);
|
||||
if (activeDetail(state, number)) openDetail(state, number);
|
||||
}).catch(function (error) {
|
||||
button.disabled = false;
|
||||
toast(errorMessage(error, "网络错误,请稍后重试"), "error");
|
||||
notyf(errorMessage(error, "网络错误,请稍后重试"), "danger");
|
||||
});
|
||||
}
|
||||
|
||||
@@ -281,14 +277,14 @@
|
||||
request(orderPath(number, "/cancel"), {}, "POST").then(function (result) {
|
||||
var data = result.data || {};
|
||||
invalidateOrder(number, data.order);
|
||||
toast("订单已关闭", "success");
|
||||
notyf("订单已关闭", "success");
|
||||
refreshView(state);
|
||||
if (activeDetail(state, number)) openDetail(state, number);
|
||||
}).catch(function (error) {
|
||||
button.disabled = false;
|
||||
button.removeAttribute("data-aksrt-confirm");
|
||||
button.textContent = "关闭订单";
|
||||
toast(errorMessage(error, "网络错误,请稍后重试"), "error");
|
||||
notyf(errorMessage(error, "网络错误,请稍后重试"), "danger");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+7
-24
@@ -10,23 +10,6 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
function notify(message, type) {
|
||||
if (!message) return;
|
||||
if (typeof window.AksrtToast === 'function') {
|
||||
window.AksrtToast(message, type || 'info');
|
||||
return;
|
||||
}
|
||||
if (typeof window.showToast === 'function') {
|
||||
window.showToast(message, type || 'info');
|
||||
return;
|
||||
}
|
||||
if (typeof window.aksrtToast === 'function') {
|
||||
window.aksrtToast(message, type || 'info');
|
||||
return;
|
||||
}
|
||||
window.alert(message);
|
||||
}
|
||||
|
||||
function getMetaContent(selectors) {
|
||||
for (var i = 0; i < selectors.length; i += 1) {
|
||||
var node = document.querySelector(selectors[i]);
|
||||
@@ -208,7 +191,7 @@
|
||||
|
||||
var share = window.AksrtShare;
|
||||
if (!share || typeof share.openWeixinShare !== 'function') {
|
||||
notify('微信分享模块未加载', 'error');
|
||||
notyf('微信分享模块未加载', 'danger');
|
||||
return Promise.resolve({ mode: 'error' });
|
||||
}
|
||||
|
||||
@@ -230,7 +213,7 @@
|
||||
qrImage: options.qrImage || null
|
||||
}).then(function (result) {
|
||||
if (result && result.message) {
|
||||
notify(result.message, result.mode === 'error' ? 'error' : (result.mode === 'bridge' ? 'success' : 'info'));
|
||||
notyf(result.message, result.mode === 'error' ? 'danger' : (result.mode === 'bridge' ? 'success' : 'info'));
|
||||
}
|
||||
return result;
|
||||
});
|
||||
@@ -245,13 +228,13 @@
|
||||
|
||||
if (platform === 'copy') {
|
||||
return copyLink(payload.url).then(function () {
|
||||
notify('链接已复制', 'success');
|
||||
notyf('链接已复制', 'success');
|
||||
if (options && typeof options.closeDropdown === 'function') {
|
||||
options.closeDropdown();
|
||||
}
|
||||
return { mode: 'copy' };
|
||||
}).catch(function () {
|
||||
notify('复制失败,请手动复制链接', 'error');
|
||||
notyf('复制失败,请手动复制链接', 'danger');
|
||||
return { mode: 'error' };
|
||||
});
|
||||
}
|
||||
@@ -271,7 +254,7 @@
|
||||
if (options && typeof options.closeDropdown === 'function') options.closeDropdown();
|
||||
return Promise.resolve({ mode: 'poster' });
|
||||
}
|
||||
notify('海报模块未加载', 'error');
|
||||
notyf('海报模块未加载', 'danger');
|
||||
return Promise.resolve({ mode: 'error' });
|
||||
}
|
||||
|
||||
@@ -304,9 +287,9 @@
|
||||
event.preventDefault();
|
||||
var url = node.getAttribute('data-share-url') || window.location.href;
|
||||
copyLink(url).then(function () {
|
||||
notify('链接已复制', 'success');
|
||||
notyf('链接已复制', 'success');
|
||||
}).catch(function () {
|
||||
notify('复制失败,请手动复制链接', 'error');
|
||||
notyf('复制失败,请手动复制链接', 'danger');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -21,15 +21,6 @@
|
||||
return '/wp-admin/admin-ajax.php';
|
||||
}
|
||||
|
||||
function toast(msg, type) {
|
||||
if (window.aksrt && typeof window.aksrt.toast === 'function') {
|
||||
window.aksrt.toast(msg, type || 'info');
|
||||
return;
|
||||
}
|
||||
// eslint-disable-next-line no-alert
|
||||
window.alert(msg);
|
||||
}
|
||||
|
||||
function initModal() {
|
||||
var modal = qs('#post-navs-sz');
|
||||
if (!modal) return;
|
||||
@@ -144,7 +135,7 @@
|
||||
if (id) ids.push(id);
|
||||
});
|
||||
if (!ids.length) {
|
||||
toast('请选择需要显示的分类', 'danger');
|
||||
notyf('请选择需要显示的分类', 'danger');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -167,16 +158,16 @@
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (data) {
|
||||
if (!data || data.error) {
|
||||
toast((data && data.msg) || '保存失败', 'danger');
|
||||
notyf((data && data.msg) || '保存失败', 'danger');
|
||||
return;
|
||||
}
|
||||
toast(data.msg || '保存成功', 'success');
|
||||
notyf(data.msg || '保存成功', 'success');
|
||||
if (data.reload) {
|
||||
window.setTimeout(function () { window.location.reload(); }, 400);
|
||||
}
|
||||
})
|
||||
.catch(function () {
|
||||
toast('网络错误', 'danger');
|
||||
notyf('网络错误', 'danger');
|
||||
})
|
||||
.finally(function () {
|
||||
if (btn) btn.disabled = false;
|
||||
|
||||
+3
-15
@@ -117,15 +117,15 @@
|
||||
.then(function (result) {
|
||||
overlay.remove();
|
||||
if (result.success) {
|
||||
showToast(result.message || '举报已提交');
|
||||
notyf(result.message || '举报已提交');
|
||||
} else {
|
||||
showToast(result.message || '提交失败', 'error');
|
||||
notyf(result.message || '提交失败', 'danger');
|
||||
}
|
||||
})
|
||||
.catch(function () {
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.textContent = '提交举报';
|
||||
showToast('网络错误,请重试', 'error');
|
||||
notyf('网络错误,请重试', 'danger');
|
||||
});
|
||||
};
|
||||
btnRow.appendChild(submitBtn);
|
||||
@@ -140,18 +140,6 @@
|
||||
reasonInput.focus();
|
||||
}
|
||||
|
||||
function showToast(msg, type) {
|
||||
var toast = document.createElement('div');
|
||||
toast.textContent = msg;
|
||||
toast.style.cssText = 'position:fixed;top:20px;left:50%;transform:translateX(-50%);padding:12px 24px;border-radius:6px;color:#fff;font-size:14px;z-index:10000;transition:opacity 0.3s;';
|
||||
toast.style.background = type === 'error' ? '#e74c3c' : '#27ae60';
|
||||
document.body.appendChild(toast);
|
||||
setTimeout(function () {
|
||||
toast.style.opacity = '0';
|
||||
setTimeout(function () { toast.remove(); }, 300);
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
function initReportButtons() {
|
||||
document.addEventListener('click', function (e) {
|
||||
var btn = e.target.closest('[data-aksrt-report]');
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
/* global aksrtUserCenter, showToast */
|
||||
/* global aksrtUserCenter, notyf */
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
@@ -157,7 +157,7 @@
|
||||
if (name === 'revenue' && typeof window.AKSRTAffiliateRevenueInit === 'function') window.AKSRTAffiliateRevenueInit();
|
||||
if (name === 'revenue' && typeof window.AKSRTWithdrawInit === 'function') window.AKSRTWithdrawInit();
|
||||
}).catch(function() {
|
||||
if (typeof showToast === 'function') showToast(i18n.networkError || '加载失败,请重试');
|
||||
notyf(i18n.networkError || '加载失败,请重试', 'danger');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -220,7 +220,7 @@
|
||||
if (typeof window.AKSRTAffiliateRevenueInit === 'function') window.AKSRTAffiliateRevenueInit();
|
||||
if (typeof window.AKSRTWithdrawInit === 'function') window.AKSRTWithdrawInit();
|
||||
}).catch(function() {
|
||||
if (typeof showToast === 'function') showToast(i18n.networkError || '加载失败,请重试');
|
||||
notyf(i18n.networkError || '加载失败,请重试', 'danger');
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -246,7 +246,7 @@
|
||||
if (window.history.pushState) window.history.pushState({}, '', pageUrl.toString());
|
||||
if (typeof window.AKSRTRevenueCenterInit === 'function') window.AKSRTRevenueCenterInit(creatorPanel);
|
||||
}).catch(function() {
|
||||
if (typeof showToast === 'function') showToast(i18n.networkError || '加载失败,请重试');
|
||||
notyf(i18n.networkError || '加载失败,请重试', 'danger');
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -283,7 +283,7 @@
|
||||
btn.disabled = false;
|
||||
btn.textContent = i18n.saveBtn || '保存修改';
|
||||
if (res.success) {
|
||||
showToast(i18n.saved || '已保存');
|
||||
notyf(i18n.saved || '已保存');
|
||||
} else {
|
||||
msg.className = 'profile-form__submit-msg is-error';
|
||||
msg.textContent = res.data.message || (i18n.saveFailed || '保存失败');
|
||||
@@ -350,17 +350,17 @@
|
||||
}
|
||||
}, 220);
|
||||
}
|
||||
showToast(i18n.unfavorited || '已取消收藏');
|
||||
notyf(i18n.unfavorited || '已取消收藏');
|
||||
} else {
|
||||
btn.disabled = false;
|
||||
btn.classList.remove('is-loading');
|
||||
showToast((res && res.data && res.data.message) || (i18n.operationFailed || '操作失败'));
|
||||
notyf((res && res.data && res.data.message) || (i18n.operationFailed || '操作失败'), 'danger');
|
||||
}
|
||||
})
|
||||
.catch(function() {
|
||||
btn.disabled = false;
|
||||
btn.classList.remove('is-loading');
|
||||
showToast(i18n.networkError || '网络错误');
|
||||
notyf(i18n.networkError || '网络错误', 'danger');
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -383,11 +383,11 @@
|
||||
if (!response || !response.success) throw new Error('delete-failed');
|
||||
var item = btn.closest('.profile-list-item');
|
||||
if (item) item.remove();
|
||||
if (typeof showToast === 'function') showToast('文章已删除');
|
||||
notyf('文章已删除');
|
||||
})
|
||||
.catch(function() {
|
||||
btn.disabled = false;
|
||||
if (typeof showToast === 'function') showToast(i18n.operationFailed || '操作失败');
|
||||
notyf(i18n.operationFailed || '操作失败', 'danger');
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -429,14 +429,14 @@
|
||||
var row = btn.closest('[data-notification-id]');
|
||||
if (row) row.classList.add('is-read');
|
||||
btn.remove();
|
||||
showToast(i18n.markedRead || '已标记为已读');
|
||||
notyf(i18n.markedRead || '已标记为已读');
|
||||
} else {
|
||||
btn.disabled = false;
|
||||
showToast((res && res.data && res.data.message) || (i18n.operationFailed || '操作失败'));
|
||||
notyf((res && res.data && res.data.message) || (i18n.operationFailed || '操作失败'), 'danger');
|
||||
}
|
||||
}).catch(function() {
|
||||
btn.disabled = false;
|
||||
showToast(i18n.networkError || '网络错误');
|
||||
notyf(i18n.networkError || '网络错误', 'danger');
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -460,14 +460,14 @@
|
||||
if (res && res.success) {
|
||||
var delRow = delBtn.closest('[data-notification-id]');
|
||||
if (delRow) delRow.remove();
|
||||
showToast(i18n.notificationDeleted || '已删除通知');
|
||||
notyf(i18n.notificationDeleted || '已删除通知');
|
||||
} else {
|
||||
delBtn.disabled = false;
|
||||
showToast((res && res.data && res.data.message) || (i18n.operationFailed || '操作失败'));
|
||||
notyf((res && res.data && res.data.message) || (i18n.operationFailed || '操作失败'), 'danger');
|
||||
}
|
||||
}).catch(function() {
|
||||
delBtn.disabled = false;
|
||||
showToast(i18n.networkError || '网络错误');
|
||||
notyf(i18n.networkError || '网络错误', 'danger');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -482,14 +482,14 @@
|
||||
row.classList.add('is-read');
|
||||
});
|
||||
list.querySelectorAll('.aksrt-notification-read-btn').forEach(function(b) { b.remove(); });
|
||||
showToast(i18n.allMarkedRead || '已全部标记为已读');
|
||||
notyf(i18n.allMarkedRead || '已全部标记为已读');
|
||||
} else {
|
||||
markAllBtn.disabled = false;
|
||||
showToast((res && res.data && res.data.message) || (i18n.operationFailed || '操作失败'));
|
||||
notyf((res && res.data && res.data.message) || (i18n.operationFailed || '操作失败'), 'danger');
|
||||
}
|
||||
}).catch(function() {
|
||||
markAllBtn.disabled = false;
|
||||
showToast(i18n.networkError || '网络错误');
|
||||
notyf(i18n.networkError || '网络错误', 'danger');
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -501,7 +501,7 @@
|
||||
return input.value;
|
||||
});
|
||||
if (!checked.length) {
|
||||
showToast(i18n.selectNotifications || '请先选择要标记的通知');
|
||||
notyf(i18n.selectNotifications || '请先选择要标记的通知', 'warning');
|
||||
return;
|
||||
}
|
||||
markSelectedBtn.disabled = true;
|
||||
@@ -516,11 +516,11 @@
|
||||
var b = row ? row.querySelector('.aksrt-notification-read-btn') : null;
|
||||
if (b) b.remove();
|
||||
});
|
||||
showToast(i18n.selectedMarkedRead || '已标记所选通知为已读');
|
||||
notyf(i18n.selectedMarkedRead || '已标记所选通知为已读');
|
||||
markSelectedBtn.disabled = false;
|
||||
}).catch(function() {
|
||||
markSelectedBtn.disabled = false;
|
||||
showToast(i18n.networkError || '网络错误');
|
||||
notyf(i18n.networkError || '网络错误', 'danger');
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -532,7 +532,7 @@
|
||||
return input.value;
|
||||
});
|
||||
if (!checked.length) {
|
||||
showToast(i18n.selectNotifications || '请先选择要删除的通知');
|
||||
notyf(i18n.selectNotifications || '请先选择要删除的通知', 'warning');
|
||||
return;
|
||||
}
|
||||
if (!confirm(i18n.confirmBatchDelete || '确定批量删除所选通知?')) return;
|
||||
@@ -551,15 +551,15 @@
|
||||
var row = list.querySelector('[data-notification-id="' + id + '"]');
|
||||
if (row) row.remove();
|
||||
});
|
||||
showToast(i18n.batchDeleted || '已删除所选通知');
|
||||
notyf(i18n.batchDeleted || '已删除所选通知');
|
||||
} else {
|
||||
deleteSelectedBtn.disabled = false;
|
||||
showToast((res && res.data && res.data.message) || (i18n.operationFailed || '操作失败'));
|
||||
notyf((res && res.data && res.data.message) || (i18n.operationFailed || '操作失败'), 'danger');
|
||||
}
|
||||
})
|
||||
.catch(function() {
|
||||
deleteSelectedBtn.disabled = false;
|
||||
showToast(i18n.networkError || '网络错误');
|
||||
notyf(i18n.networkError || '网络错误', 'danger');
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -687,7 +687,7 @@
|
||||
fetch(ajaxUrl, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: data.toString() })
|
||||
.then(function(r) { return r.json(); }).then(function(res) {
|
||||
if (res.success) {
|
||||
showToast(res.data.message || (i18n.codeSent || '验证码已发送'));
|
||||
notyf(res.data.message || (i18n.codeSent || '验证码已发送'));
|
||||
var sec = 60;
|
||||
self.classList.add('is-counting');
|
||||
self.textContent = sec + 's 后重发';
|
||||
@@ -703,13 +703,13 @@
|
||||
}
|
||||
}, 1000);
|
||||
} else {
|
||||
showToast(res.data.message || (i18n.sendFailed || '发送失败'));
|
||||
notyf(res.data.message || (i18n.sendFailed || '发送失败'), 'danger');
|
||||
self.disabled = false;
|
||||
self.textContent = getSendBtnIdleText(btnId);
|
||||
}
|
||||
})
|
||||
.catch(function() {
|
||||
showToast(i18n.networkError || '网络错误');
|
||||
notyf(i18n.networkError || '网络错误', 'danger');
|
||||
self.disabled = false;
|
||||
self.textContent = getSendBtnIdleText(btnId);
|
||||
});
|
||||
@@ -730,14 +730,14 @@
|
||||
var orig = btn ? btn.textContent : '';
|
||||
var reqs = fm.querySelectorAll('[required]');
|
||||
for (var i = 0; i < reqs.length; i++) {
|
||||
if (!reqs[i].value.trim()) { reqs[i].focus(); showToast(i18n.fillRequired || '请填写必填项'); return; }
|
||||
if (!reqs[i].value.trim()) { reqs[i].focus(); notyf(i18n.fillRequired || '请填写必填项', 'warning'); return; }
|
||||
}
|
||||
if (fid === 'pwd-form') {
|
||||
var pwdNew = document.getElementById('pwd-new');
|
||||
var pwdConfirm = document.getElementById('pwd-new-confirm');
|
||||
if (pwdNew && pwdConfirm && pwdNew.value !== pwdConfirm.value) {
|
||||
pwdConfirm.focus();
|
||||
showToast(i18n.passwordMismatch || '两次输入的新密码不一致');
|
||||
notyf(i18n.passwordMismatch || '两次输入的新密码不一致', 'warning');
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -750,16 +750,16 @@
|
||||
.then(function(r) {
|
||||
if (btn) { btn.disabled = false; btn.textContent = orig; }
|
||||
if (r.success) {
|
||||
showToast(r.data.message || '');
|
||||
notyf(r.data.message || '');
|
||||
closeModal(fm.closest('.aksrt-modal'));
|
||||
if (r.data.redirect_to) setTimeout(function() { window.location.href = r.data.redirect_to; }, 2500);
|
||||
} else {
|
||||
showToast(r.data.message || (i18n.operationFailed || '操作失败'));
|
||||
notyf(r.data.message || (i18n.operationFailed || '操作失败'), 'danger');
|
||||
}
|
||||
})
|
||||
.catch(function() {
|
||||
if (btn) { btn.disabled = false; btn.textContent = orig; }
|
||||
showToast(i18n.networkError || '网络错误');
|
||||
notyf(i18n.networkError || '网络错误', 'danger');
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -790,7 +790,7 @@
|
||||
.then(function(r) { return r.json(); })
|
||||
.then(function(d) {
|
||||
if (d.success) {
|
||||
showToast(i18n.unbindSuccess || '解除成功');
|
||||
notyf(i18n.unbindSuccess || '解除成功');
|
||||
setTimeout(function() {
|
||||
var u = new URL(window.location);
|
||||
u.searchParams.set('tab', 'settings');
|
||||
@@ -798,13 +798,13 @@
|
||||
window.location.href = u.toString();
|
||||
}, 1000);
|
||||
} else {
|
||||
showToast(i18n.unbindFailed || '解除失败');
|
||||
notyf(i18n.unbindFailed || '解除失败', 'danger');
|
||||
self.disabled = false;
|
||||
self.textContent = i18n.unbind || '解除';
|
||||
}
|
||||
})
|
||||
.catch(function() {
|
||||
showToast(i18n.networkError || '网络错误');
|
||||
notyf(i18n.networkError || '网络错误', 'danger');
|
||||
self.disabled = false;
|
||||
self.textContent = i18n.unbind || '解除';
|
||||
});
|
||||
@@ -844,11 +844,11 @@
|
||||
var phone = phoneEl ? String(phoneEl.value || '').trim() : '';
|
||||
var code = codeEl ? String(codeEl.value || '').trim() : '';
|
||||
if (!/^1[3-9]\d{9}$/.test(phone)) {
|
||||
showToast('请输入正确的手机号');
|
||||
notyf('请输入正确的手机号', 'warning');
|
||||
return;
|
||||
}
|
||||
if (!code) {
|
||||
showToast('请输入短信验证码');
|
||||
notyf('请输入短信验证码', 'warning');
|
||||
return;
|
||||
}
|
||||
var submitBtn = form.querySelector('[type="submit"]');
|
||||
@@ -892,12 +892,12 @@
|
||||
.then(function (r) { return r.json().then(function (j) { return { ok: r.ok, json: j }; }); })
|
||||
.then(function (res) {
|
||||
if (res.ok && res.json && res.json.success) {
|
||||
showToast((res.json.message) || '绑定成功');
|
||||
notyf((res.json.message) || '绑定成功');
|
||||
setTimeout(function () { window.location.reload(); }, 600);
|
||||
return;
|
||||
}
|
||||
resetCaptcha();
|
||||
showToast((res.json && (res.json.message || res.json.error)) || '绑定失败');
|
||||
notyf((res.json && (res.json.message || res.json.error)) || '绑定失败', 'danger');
|
||||
if (submitBtn) {
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.textContent = '确认绑定';
|
||||
@@ -905,7 +905,7 @@
|
||||
})
|
||||
.catch(function () {
|
||||
resetCaptcha();
|
||||
showToast('网络错误');
|
||||
notyf('网络错误', 'danger');
|
||||
if (submitBtn) {
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.textContent = '确认绑定';
|
||||
|
||||
+18
-35
@@ -56,7 +56,7 @@
|
||||
const phone = phoneInput ? phoneInput.value.trim() : ''
|
||||
|
||||
if (!/^1[3-9]\d{9}$/.test(phone)) {
|
||||
showToast('请输入正确的手机号', 'warning')
|
||||
notyf('请输入正确的手机号', 'warning')
|
||||
if (phoneInput) phoneInput.focus()
|
||||
return
|
||||
}
|
||||
@@ -91,13 +91,13 @@
|
||||
.then(r => r.json().then(data => ({ ok: r.ok, data })))
|
||||
.then(({ ok, data }) => {
|
||||
if (ok && data.success) {
|
||||
showToast(data.message || '验证码已发送', 'success')
|
||||
notyf(data.message || '验证码已发送', 'success')
|
||||
const seconds = (data.data && data.data.remaining_time) || cooldown
|
||||
sessionStorage.setItem(COUNTDOWN_KEY, String(Date.now() + seconds * 1000))
|
||||
startCountdown(btn, seconds)
|
||||
} else {
|
||||
const msg = (data.data && data.data.message) || data.message || '发送失败'
|
||||
showToast(msg, 'error')
|
||||
notyf(msg, 'danger')
|
||||
try {
|
||||
const captchaState = window.AKSRTCaptchaState
|
||||
if (captchaState && captchaState.scenes && captchaState.scenes[scene]) {
|
||||
@@ -115,7 +115,7 @@
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
showToast('网络错误,请重试', 'error')
|
||||
notyf('网络错误,请重试', 'danger')
|
||||
btn.disabled = false
|
||||
btn.textContent = original
|
||||
})
|
||||
@@ -187,15 +187,15 @@
|
||||
const ref = form.querySelector('[name="ref"]')?.value?.trim()
|
||||
|
||||
if (!phone || !code || !password) {
|
||||
showToast('请填写完整信息', 'warning')
|
||||
notyf('请填写完整信息', 'warning')
|
||||
return
|
||||
}
|
||||
if (password !== confirm) {
|
||||
showToast('两次密码不一致', 'warning')
|
||||
notyf('两次密码不一致', 'warning')
|
||||
return
|
||||
}
|
||||
if (password.length < 6) {
|
||||
showToast('密码至少 6 位', 'warning')
|
||||
notyf('密码至少 6 位', 'warning')
|
||||
return
|
||||
}
|
||||
|
||||
@@ -216,13 +216,13 @@
|
||||
.then(r => r.json().then(data => ({ ok: r.ok, data })))
|
||||
.then(({ ok, data }) => {
|
||||
if (ok && data.success) {
|
||||
showToast(data.message || '注册成功', 'success')
|
||||
if (data.notice) showToast(data.notice, 'warning')
|
||||
notyf(data.message || '注册成功', 'success')
|
||||
if (data.notice) notyf(data.notice, 'warning')
|
||||
setTimeout(() => {
|
||||
window.location.href = data.redirect || '/'
|
||||
}, 500)
|
||||
} else {
|
||||
showToast(data.message || '注册失败', 'error')
|
||||
notyf(data.message || '注册失败', 'danger')
|
||||
try {
|
||||
if (captchaState && captchaState.scenes && captchaState.scenes.register) {
|
||||
captchaState.scenes.register.reset()
|
||||
@@ -235,7 +235,7 @@
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
showToast('网络错误', 'error')
|
||||
notyf('网络错误', 'danger')
|
||||
try {
|
||||
if (captchaState && captchaState.scenes && captchaState.scenes.register) {
|
||||
captchaState.scenes.register.reset()
|
||||
@@ -261,7 +261,7 @@
|
||||
const loginMode = form.querySelector('[name="login_mode"]')?.value || 'password'
|
||||
|
||||
if (!phone) {
|
||||
showToast('请输入手机号', 'warning')
|
||||
notyf('请输入手机号', 'warning')
|
||||
return
|
||||
}
|
||||
|
||||
@@ -275,7 +275,7 @@
|
||||
if (loginMode === 'code') {
|
||||
const code = form.querySelector('[name="code"]')?.value?.trim()
|
||||
if (!code) {
|
||||
showToast('请输入验证码', 'warning')
|
||||
notyf('请输入验证码', 'warning')
|
||||
if (submitBtn) { submitBtn.disabled = false; submitBtn.textContent = '登录' }
|
||||
return
|
||||
}
|
||||
@@ -283,7 +283,7 @@
|
||||
body = { phone, code }
|
||||
} else {
|
||||
if (!password) {
|
||||
showToast('请输入密码', 'warning')
|
||||
notyf('请输入密码', 'warning')
|
||||
if (submitBtn) { submitBtn.disabled = false; submitBtn.textContent = '登录' }
|
||||
return
|
||||
}
|
||||
@@ -302,12 +302,12 @@
|
||||
.then(r => r.json().then(data => ({ ok: r.ok, data })))
|
||||
.then(({ ok, data }) => {
|
||||
if (ok && data.success) {
|
||||
showToast('登录成功', 'success')
|
||||
notyf('登录成功', 'success')
|
||||
setTimeout(() => {
|
||||
window.location.href = data.data?.redirect || '/'
|
||||
}, 500)
|
||||
} else {
|
||||
showToast(data.message || '登录失败', 'error')
|
||||
notyf(data.message || '登录失败', 'danger')
|
||||
if (captchaState && captchaState.scenes && captchaState.scenes.login) {
|
||||
captchaState.scenes.login.reset()
|
||||
}
|
||||
@@ -318,7 +318,7 @@
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
showToast('网络错误', 'error')
|
||||
notyf('网络错误', 'danger')
|
||||
if (submitBtn) {
|
||||
submitBtn.disabled = false
|
||||
submitBtn.textContent = '登录'
|
||||
@@ -388,7 +388,7 @@
|
||||
const confirm = form.querySelector('[name="password_confirm"]')
|
||||
if (password && confirm && password.value !== confirm.value) {
|
||||
e.preventDefault()
|
||||
showToast('两次密码不一致', 'warning')
|
||||
notyf('两次密码不一致', 'warning')
|
||||
confirm.focus()
|
||||
return
|
||||
}
|
||||
@@ -396,22 +396,6 @@
|
||||
})
|
||||
}
|
||||
|
||||
function showToast(message, type) {
|
||||
if (typeof window.aksrtShowToast === 'function') {
|
||||
window.aksrtShowToast(message, type)
|
||||
return
|
||||
}
|
||||
const toast = document.createElement('div')
|
||||
toast.className = 'aksrt-auth-toast aksrt-auth-toast--' + (type || 'info')
|
||||
toast.textContent = message
|
||||
document.body.appendChild(toast)
|
||||
setTimeout(() => toast.classList.add('aksrt-auth-toast--show'), 10)
|
||||
setTimeout(() => {
|
||||
toast.classList.remove('aksrt-auth-toast--show')
|
||||
setTimeout(() => toast.remove(), 300)
|
||||
}, 3000)
|
||||
}
|
||||
|
||||
// 初始化
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', initAuthPages)
|
||||
@@ -424,6 +408,5 @@
|
||||
init: initAuthPages,
|
||||
sendSmsCode,
|
||||
switchAuthTab,
|
||||
showToast,
|
||||
}
|
||||
})()
|
||||
|
||||
@@ -20,10 +20,6 @@
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
function toast(message, type) {
|
||||
window.AksrtToast(message, type || 'info');
|
||||
}
|
||||
|
||||
function api(path, options) {
|
||||
return window.AksrtRequest.request(path, options || {}).then(function (response) {
|
||||
return {
|
||||
@@ -177,19 +173,19 @@
|
||||
}).then(function (result) {
|
||||
if (state.session && state.session.closed) return;
|
||||
if (!result.ok) {
|
||||
toast((result.json && (result.json.message || (result.json.data && result.json.data.message))) || '佩戴失败', 'error');
|
||||
notyf((result.json && (result.json.message || (result.json.data && result.json.data.message))) || '佩戴失败', 'danger');
|
||||
if (button) button.disabled = false;
|
||||
return;
|
||||
}
|
||||
if (result.json && result.json.data) state.data = result.json.data;
|
||||
state.detailSlug = slug;
|
||||
window.AksrtModalRuntime.invalidate(['badges', 'identity', 'badges:user:' + state.userId]);
|
||||
toast((result.json && result.json.message) || '已佩戴徽章', 'success');
|
||||
notyf((result.json && result.json.message) || '已佩戴徽章', 'success');
|
||||
renderInto(state);
|
||||
syncHeaderIcon(state);
|
||||
}).catch(function (error) {
|
||||
if (error && error.name === 'AbortError') return;
|
||||
toast('网络错误', 'error');
|
||||
notyf('网络错误', 'danger');
|
||||
if (button) button.disabled = false;
|
||||
}).finally(function () {
|
||||
state.wearing = false;
|
||||
|
||||
@@ -113,7 +113,5 @@ $show_sidebar = function_exists('aksrt_is_show_sidebar') ? aksrt_is_show_sidebar
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="toast-container" id="toast-container"></div>
|
||||
|
||||
<?php
|
||||
get_footer();
|
||||
|
||||
@@ -165,7 +165,7 @@ trait ScriptsPart1b {
|
||||
wp_enqueue_script(
|
||||
'aksrt-header-userbox',
|
||||
$base_uri . '/assets/js/header-userbox.js',
|
||||
[],
|
||||
['aksrt-notyf'],
|
||||
(string) filemtime($hub_js),
|
||||
true
|
||||
);
|
||||
@@ -410,7 +410,7 @@ $hmb_js = get_template_directory() . '/assets/js/header-newadd.js';
|
||||
wp_enqueue_script(
|
||||
'aksrt-report',
|
||||
$base_uri . '/assets/js/report.js',
|
||||
[],
|
||||
['aksrt-notyf'],
|
||||
filemtime($report_js_path) ?: $version,
|
||||
true
|
||||
);
|
||||
@@ -422,7 +422,7 @@ $hmb_js = get_template_directory() . '/assets/js/header-newadd.js';
|
||||
wp_enqueue_script(
|
||||
'aksrt-follow',
|
||||
$base_uri . '/assets/js/follow.js',
|
||||
[],
|
||||
['aksrt-notyf'],
|
||||
filemtime($follow_js_path) ?: $version,
|
||||
true
|
||||
);
|
||||
@@ -609,7 +609,7 @@ $hmb_js = get_template_directory() . '/assets/js/header-newadd.js';
|
||||
wp_enqueue_script(
|
||||
'aksrt-posts-navs-page',
|
||||
$base_uri . '/assets/js/posts-navs-page.js',
|
||||
['jquery'],
|
||||
['jquery', 'aksrt-notyf'],
|
||||
(string) filemtime($pn_js),
|
||||
true
|
||||
);
|
||||
@@ -645,7 +645,7 @@ $hmb_js = get_template_directory() . '/assets/js/header-newadd.js';
|
||||
wp_enqueue_script(
|
||||
'aksrt-ban-appeal-page',
|
||||
$base_uri . '/assets/js/ban-appeal-page.js',
|
||||
[],
|
||||
['aksrt-notyf'],
|
||||
(string) filemtime($ba_js),
|
||||
true
|
||||
);
|
||||
|
||||
@@ -62,6 +62,14 @@ trait ScriptsPart1c {
|
||||
'5.15.4'
|
||||
);
|
||||
|
||||
$notyf_css = get_template_directory() . '/assets/css/frontend/notyf.css';
|
||||
wp_enqueue_style(
|
||||
'aksrt-notyf',
|
||||
$base_uri . '/assets/css/frontend/notyf.css',
|
||||
[$core_dep, 'aksrt-font-awesome-5-v4-shims'],
|
||||
file_exists($notyf_css) ? (string) filemtime($notyf_css) : $version
|
||||
);
|
||||
|
||||
// ===== 7. JS =====
|
||||
|
||||
// Swiper.js(首页/归档/CMS 模块/单篇文章页加载)
|
||||
@@ -111,13 +119,22 @@ trait ScriptsPart1c {
|
||||
true
|
||||
);
|
||||
|
||||
// Runtime infrastructure: tbquire / notyf / confirm / scrollTopTo / [data-*] declarative
|
||||
$notyf_js = get_template_directory() . '/assets/js/notyf.js';
|
||||
wp_enqueue_script(
|
||||
'aksrt-notyf',
|
||||
$base_uri . '/assets/js/notyf.js',
|
||||
['jquery'],
|
||||
file_exists($notyf_js) ? (string) filemtime($notyf_js) : $version,
|
||||
true
|
||||
);
|
||||
|
||||
// Runtime infrastructure: tbquire / confirm / scrollTopTo / [data-*] declarative
|
||||
$runtime_js = get_template_directory() . '/assets/js/aksrt-runtime.js';
|
||||
if (file_exists($runtime_js)) {
|
||||
wp_enqueue_script(
|
||||
'aksrt-runtime',
|
||||
$base_uri . '/assets/js/aksrt-runtime.js',
|
||||
['aksrt-rehydrate'],
|
||||
['aksrt-rehydrate', 'aksrt-notyf'],
|
||||
(string) filemtime($runtime_js),
|
||||
true
|
||||
);
|
||||
@@ -309,7 +326,7 @@ trait ScriptsPart1c {
|
||||
);
|
||||
}
|
||||
|
||||
// ===== 全局 UI 增强(Toast、骨架屏、悬停动效、灯箱、滚动感知)=====
|
||||
// ===== 全局 UI 增强(骨架屏、悬停动效、灯箱、滚动感知)=====
|
||||
$enhancements_css = get_template_directory() . '/assets/css/frontend/enhancements.css';
|
||||
if (file_exists($enhancements_css)) {
|
||||
wp_enqueue_style(
|
||||
|
||||
@@ -39,9 +39,6 @@ $render_context = aksrt_get_user_center_render_context($current_user);
|
||||
get_header();
|
||||
?>
|
||||
<?php \AKSRT\WidgetSystem\WidgetAreas::render('user_top_fluid', 'fluid-widget-wrap'); ?>
|
||||
<div id="toast-container"></div>
|
||||
<script>function showToast(text,d){d=d||2500;var c=document.getElementById('toast-container');if(!c){c=document.createElement('div');c.id='toast-container';document.body.appendChild(c)}var t=document.createElement('div');t.className='toast-item';t.textContent=text;c.appendChild(t);setTimeout(function(){t.classList.add('fade-out');setTimeout(function(){if(t.parentNode)t.parentNode.removeChild(t)},300)},d)}</script>
|
||||
|
||||
<main id="main-content" class="site-main container content-shell content-shell--stream aksrt-profile user-center" tabindex="-1">
|
||||
<div class="content-wrap">
|
||||
<?php get_template_part('templates/parts/user-center/header', null, ['context' => $render_context]); ?>
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
'use strict';
|
||||
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const test = require('node:test');
|
||||
const vm = require('node:vm');
|
||||
|
||||
const root = path.resolve(__dirname, '..', '..');
|
||||
const read = (file) => fs.readFileSync(path.join(root, file), 'utf8');
|
||||
|
||||
class FakeElement {
|
||||
constructor(classes = [], id = '') {
|
||||
this.classes = new Set(classes);
|
||||
this.id = id;
|
||||
this.children = [];
|
||||
this.parent = null;
|
||||
this.innerHTML = '';
|
||||
this.delegated = null;
|
||||
}
|
||||
|
||||
append(child) {
|
||||
child.parent = this;
|
||||
this.children.push(child);
|
||||
}
|
||||
}
|
||||
|
||||
function descendants(element) {
|
||||
return element.children.flatMap((child) => [child, ...descendants(child)]);
|
||||
}
|
||||
|
||||
function createRuntime() {
|
||||
const body = new FakeElement(['body']);
|
||||
const document = { body };
|
||||
const timers = [];
|
||||
const window = {
|
||||
setTimeout(callback, delay) {
|
||||
timers.push({ callback, delay });
|
||||
return timers.length;
|
||||
},
|
||||
};
|
||||
|
||||
class FakeQuery {
|
||||
constructor(elements) {
|
||||
this.elements = elements;
|
||||
this.length = elements.length;
|
||||
}
|
||||
|
||||
append(value) {
|
||||
let elements = value instanceof FakeQuery ? value.elements : [];
|
||||
if (typeof value === 'string' && value === '<div class="notyn"></div>') {
|
||||
elements = [new FakeElement(['notyn'])];
|
||||
}
|
||||
this.elements.forEach((parent) => elements.forEach((element) => parent.append(element)));
|
||||
return this;
|
||||
}
|
||||
|
||||
addClass(classes) {
|
||||
this.elements.forEach((element) => classes.split(/\s+/).forEach((name) => element.classes.add(name)));
|
||||
return this;
|
||||
}
|
||||
|
||||
removeClass() {
|
||||
this.elements.forEach((element) => element.classes.clear());
|
||||
return this;
|
||||
}
|
||||
|
||||
find(selector) {
|
||||
return new FakeQuery(this.elements.flatMap(descendants).filter((element) => matches(element, selector)));
|
||||
}
|
||||
|
||||
html(value) {
|
||||
this.elements.forEach((element) => { element.innerHTML = value; });
|
||||
return this;
|
||||
}
|
||||
|
||||
remove() {
|
||||
this.elements.forEach((element) => {
|
||||
if (!element.parent) return;
|
||||
element.parent.children = element.parent.children.filter((child) => child !== element);
|
||||
element.parent = null;
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
on(event, selector, handler) {
|
||||
this.elements.forEach((element) => { element.delegated = { event, selector, handler }; });
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
function matches(element, selector) {
|
||||
if (selector.startsWith('.')) return element.classes.has(selector.slice(1));
|
||||
if (selector.startsWith('#')) return element.id === selector.slice(1);
|
||||
return false;
|
||||
}
|
||||
|
||||
function parseNotification(html) {
|
||||
const id = (html.match(/ id="([^"]+)"/) || [])[1] || '';
|
||||
const content = html.match(/<div class="notyf ([^"]+)">([\s\S]*)<\/div><\/div>$/);
|
||||
assert.ok(content, 'notification markup should follow the Zibll contract');
|
||||
const wrapper = new FakeElement(['noty1'], id);
|
||||
const message = new FakeElement(['notyf', content[1]]);
|
||||
message.innerHTML = content[2];
|
||||
wrapper.append(message);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
function jQuery(value) {
|
||||
if (value === document.body || value instanceof FakeElement) return new FakeQuery([value]);
|
||||
if (typeof value === 'string' && value.startsWith('<div class="noty1"')) {
|
||||
return new FakeQuery([parseNotification(value)]);
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
return new FakeQuery([body, ...descendants(body)].filter((element) => matches(element, value)));
|
||||
}
|
||||
return new FakeQuery([]);
|
||||
}
|
||||
|
||||
vm.runInNewContext(read('assets/js/notyf.js'), { jQuery, window, document });
|
||||
return { body, timers, window };
|
||||
}
|
||||
|
||||
test('notyf follows Zibll defaults, type normalization, and timing', () => {
|
||||
const runtime = createRuntime();
|
||||
|
||||
runtime.window.notyf('完成');
|
||||
const container = runtime.body.children[0];
|
||||
const first = container.children[0];
|
||||
assert.ok(container.classes.has('notyn'));
|
||||
assert.ok(first.classes.has('noty1'));
|
||||
assert.ok(first.children[0].classes.has('success'));
|
||||
assert.equal(first.children[0].innerHTML, '完成');
|
||||
assert.equal(runtime.timers[0].delay, 5000);
|
||||
|
||||
runtime.window.notyf('失败', 'error', 2);
|
||||
const second = container.children[1];
|
||||
assert.ok(second.children[0].classes.has('danger'));
|
||||
assert.equal(runtime.timers[1].delay, 2000);
|
||||
});
|
||||
|
||||
test('fixed-id notifications stay open until an in-place update', () => {
|
||||
const runtime = createRuntime();
|
||||
|
||||
runtime.window.notyf('处理中', 'load', '', 'request-state');
|
||||
const container = runtime.body.children[0];
|
||||
const notification = container.children[0];
|
||||
assert.equal(runtime.timers.length, 0);
|
||||
assert.equal(notification.id, 'request-state');
|
||||
assert.ok(notification.children[0].classes.has('load'));
|
||||
|
||||
runtime.window.notyf('<strong>完成</strong>', 'success', 3, 'request-state');
|
||||
assert.equal(container.children.length, 1);
|
||||
assert.equal(notification.children[0].innerHTML, '<strong>完成</strong>');
|
||||
assert.ok(notification.children[0].classes.has('success'));
|
||||
assert.equal(runtime.timers[0].delay, 3000);
|
||||
});
|
||||
|
||||
test('clicking a notification uses the Zibll exit class and removal delay', () => {
|
||||
const runtime = createRuntime();
|
||||
runtime.window.notyf('可关闭');
|
||||
const notification = runtime.body.children[0].children[0];
|
||||
const delegated = runtime.body.delegated;
|
||||
|
||||
assert.deepEqual({ event: delegated.event, selector: delegated.selector }, { event: 'click', selector: '.noty1' });
|
||||
delegated.handler.call(notification);
|
||||
assert.ok(notification.classes.has('notyn-out'));
|
||||
assert.equal(runtime.timers.at(-1).delay, 1000);
|
||||
runtime.timers.at(-1).callback();
|
||||
assert.equal(runtime.body.children[0].children.length, 0);
|
||||
});
|
||||
|
||||
test('notyf CSS and WordPress enqueue match the reference contract', () => {
|
||||
const css = read('assets/css/frontend/notyf.css');
|
||||
const scripts = read('src/Core/ScriptsPart1c.php');
|
||||
|
||||
assert.match(css, /\.notyn\s*\{[\s\S]*?bottom:\s*6em;[\s\S]*?max-width:\s*320px;[\s\S]*?z-index:\s*10000000;/);
|
||||
assert.match(css, /\.notyf\s*\{[\s\S]*?min-width:\s*200px;[\s\S]*?padding:\s*1em 3em 1em 3\.4em;/);
|
||||
assert.match(css, /rgba\(15, 147, 249, 0\.7\)[\s\S]*?rgba\(61, 189, 249, 0\.8\)/);
|
||||
assert.match(css, /rgba\(58, 162, 54, 0\.8\)[\s\S]*?rgba\(89, 247, 131, 0\.8\)/);
|
||||
assert.match(css, /rgba\(253, 170, 71, 0\.7\)[\s\S]*?rgba\(247, 154, 13, 0\.8\)/);
|
||||
assert.match(css, /rgba\(253, 69, 28, 0\.7\)[\s\S]*?rgba\(251, 110, 75, 0\.8\)/);
|
||||
assert.match(css, /content:\s*'\\f058'/);
|
||||
assert.match(css, /content:\s*'\\f0a1'/);
|
||||
assert.match(css, /content:\s*'\\f05a'/);
|
||||
assert.match(css, /content:\s*'\\f057'/);
|
||||
assert.match(css, /\.notyf\.load::before[\s\S]*?huan-rotate 1s/);
|
||||
|
||||
assert.match(scripts, /'aksrt-notyf',[\s\S]*?'\/assets\/js\/notyf\.js',[\s\S]*?\['jquery'\]/);
|
||||
assert.match(scripts, /'aksrt-runtime',[\s\S]*?\['aksrt-rehydrate', 'aksrt-notyf'\]/);
|
||||
assert.match(scripts, /'\/assets\/css\/frontend\/notyf\.css',[\s\S]*?'aksrt-font-awesome-5-v4-shims'/);
|
||||
});
|
||||
|
||||
test('legacy frontend toast APIs and selectors are fully removed', () => {
|
||||
const files = [
|
||||
'assets/js',
|
||||
'assets/css/frontend',
|
||||
'assets/css/routes',
|
||||
'assets/css/build/frontend-base.min.css.disabled',
|
||||
'page.php',
|
||||
'templates/pages/page-profile.php',
|
||||
];
|
||||
const legacy = /AksrtToast|aksrtToast|aksrtShowToast|aksrt\.(?:toast|notyf)|showToast|toast-container|toast-item|aksrt-toast/;
|
||||
|
||||
function sourceFiles(target) {
|
||||
const absolute = path.join(root, target);
|
||||
if (!fs.statSync(absolute).isDirectory()) return [absolute];
|
||||
return fs.readdirSync(absolute, { withFileTypes: true }).flatMap((entry) => {
|
||||
if (entry.name === 'admin' || entry.name.endsWith('.min.js')) return [];
|
||||
const child = path.join(absolute, entry.name);
|
||||
return entry.isDirectory() ? sourceFiles(path.relative(root, child)) : [child];
|
||||
});
|
||||
}
|
||||
|
||||
const scanned = files.flatMap(sourceFiles);
|
||||
const offenders = scanned.filter((file) => legacy.test(fs.readFileSync(file, 'utf8')));
|
||||
assert.deepEqual(offenders.map((file) => path.relative(root, file)), []);
|
||||
});
|
||||
Reference in New Issue
Block a user