Merge pull request 'feat(widgets): simplify locations and paid sidebar' (#12) from worktree/fix-tools into main
Reviewed-on: #12
This commit is contained in:
@@ -216,98 +216,6 @@
|
||||
.aksrt-img-grid--4 { grid-template-columns: repeat(3, 1fr); }
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 4. 定价卡片 — .aksrt-pricing-card
|
||||
* ============================================================ */
|
||||
.aksrt-pricing-card {
|
||||
position: relative;
|
||||
background: var(--surface-1, #fff);
|
||||
border: 1px solid var(--color-line, rgba(50, 50, 50, 0.06));
|
||||
border-radius: var(--radius-lg, 8px);
|
||||
padding: 28px 24px;
|
||||
text-align: center;
|
||||
transition: box-shadow .25s ease, transform .25s ease;
|
||||
}
|
||||
|
||||
.aksrt-pricing-card:hover {
|
||||
box-shadow: var(--shadow-lg, 0 0 10px rgba(116, 116, 116, 0.12));
|
||||
transform: none;
|
||||
}
|
||||
|
||||
/* 推荐/高亮卡片 */
|
||||
.aksrt-pricing-card--featured {
|
||||
border-color: var(--color-primary);
|
||||
box-shadow: 0 0 0 1px var(--color-primary), var(--shadow-lg);
|
||||
}
|
||||
|
||||
.aksrt-pricing-card__badge {
|
||||
position: absolute;
|
||||
top: -1px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: var(--color-primary);
|
||||
color: #fff;
|
||||
padding: 2px 16px;
|
||||
border-radius: 0 0 var(--radius, 8px) var(--radius, 8px);
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.aksrt-pricing-card__title {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
color: var(--color-ink, #333);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.aksrt-pricing-card__price {
|
||||
font-size: 36px;
|
||||
font-weight: 800;
|
||||
color: var(--color-primary);
|
||||
line-height: 1.2;
|
||||
margin: 12px 0 4px;
|
||||
}
|
||||
|
||||
.aksrt-pricing-card__price small {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: var(--color-ink-3, #777);
|
||||
}
|
||||
|
||||
.aksrt-pricing-card__desc {
|
||||
font-size: 14px;
|
||||
color: var(--color-ink-3, #777);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.aksrt-pricing-card__features {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0 0 24px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.aksrt-pricing-card__features li {
|
||||
padding: 6px 0;
|
||||
font-size: 14px;
|
||||
color: var(--color-ink-2, #4e5358);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.aksrt-pricing-card__features li::before {
|
||||
content: '✓';
|
||||
color: var(--color-success);
|
||||
font-weight: 700;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.aksrt-pricing-card__cta {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 5. Toggle 开关 — .aksrt-toggle (纯 CSS + :checked)
|
||||
* 保持原生 <input> 可访问性
|
||||
|
||||
@@ -1,15 +1,3 @@
|
||||
/* Dark mode */
|
||||
.theme-dark .aksrt-lang-switch__toggle,
|
||||
html.dark .aksrt-lang-switch__toggle {
|
||||
background: #2a2a3a;
|
||||
border-color: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
|
||||
.theme-dark .aksrt-lang-switch__dropdown,
|
||||
html.dark .aksrt-lang-switch__dropdown {
|
||||
background: #2a2a3a;
|
||||
border-color: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 响应式
|
||||
|
||||
@@ -15,125 +15,3 @@
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 22. 语言切换器
|
||||
* ============================================================ */
|
||||
.aksrt-lang-switch {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.aksrt-lang-switch__toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 8px 12px;
|
||||
border: 1px solid var(--color-line);
|
||||
border-radius: var(--radius, 10px);
|
||||
background: var(--surface-1, #fff);
|
||||
color: var(--color-ink-2);
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.2s;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.aksrt-lang-switch__toggle:hover {
|
||||
border-color: var(--color-primary, var(--color-primary));
|
||||
}
|
||||
|
||||
.aksrt-lang-switch__toggle img {
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.aksrt-lang-switch__dropdown {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: 4px 0 0;
|
||||
padding: 4px;
|
||||
list-style: none;
|
||||
background: var(--surface-1, #fff);
|
||||
border: 1px solid var(--color-line);
|
||||
border-radius: var(--radius, 10px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||
z-index: 100;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transform: translateY(4px);
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.aksrt-lang-switch:hover .aksrt-lang-switch__dropdown,
|
||||
.aksrt-lang-switch__toggle:focus + .aksrt-lang-switch__dropdown {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.aksrt-lang-switch__item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 10px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
color: var(--color-ink-2);
|
||||
text-decoration: none;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
|
||||
.aksrt-lang-switch__item:hover {
|
||||
background: var(--surface-2, #F4F5F7);
|
||||
}
|
||||
|
||||
.aksrt-lang-switch__item.is-active {
|
||||
color: var(--color-primary, var(--color-primary));
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.aksrt-lang-switch__item img {
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
/* 国旗模式 */
|
||||
.aksrt-lang-switch--flags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.aksrt-lang-switch__flag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
height: 24px;
|
||||
border: 2px solid transparent;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
transition: border-color 0.2s;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.aksrt-lang-switch__flag:hover {
|
||||
border-color: var(--color-primary, var(--color-primary));
|
||||
}
|
||||
|
||||
.aksrt-lang-switch__flag.is-active {
|
||||
border-color: var(--color-primary, var(--color-primary));
|
||||
}
|
||||
|
||||
.aksrt-lang-switch__flag img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.aksrt-lang-switch__flag span {
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
color: var(--color-ink-3);
|
||||
}
|
||||
|
||||
|
||||
@@ -376,9 +376,8 @@ html.dark .article-end-separator span {
|
||||
padding-left: 24px;
|
||||
}
|
||||
|
||||
/* WidgetPostsNavs + built-in shell share .posts-nav-box.toc-widget */
|
||||
.widget .posts-nav-box.toc-widget,
|
||||
.widget_aksrt_posts_navs .posts-nav-box.toc-widget {
|
||||
/* Built-in article TOC shell. */
|
||||
.sidebar .posts-nav-box.toc-widget {
|
||||
margin: 0;
|
||||
box-shadow: none;
|
||||
background: transparent;
|
||||
@@ -386,9 +385,8 @@ html.dark .article-end-separator span {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.widget .posts-nav-box.toc-widget > h3,
|
||||
.widget .posts-nav-box.toc-widget .toc-widget__title,
|
||||
.widget_aksrt_posts_navs .posts-nav-box.toc-widget > h3 {
|
||||
.sidebar .posts-nav-box.toc-widget > h3,
|
||||
.sidebar .posts-nav-box.toc-widget .toc-widget__title {
|
||||
margin: 0 0 10px;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
|
||||
@@ -1,154 +1,8 @@
|
||||
/**
|
||||
* Batch3 widgets: partners scroll, iframe, combo info, text title, user lists, plate widgets.
|
||||
* Batch3 widgets: iframe, combo info, text title, user lists, plate widgets.
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
/* ---- Partners scroll ---- */
|
||||
.aksrt-partners-scroll-wrap {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.aksrt-partners-scroll-wrap.is-bare {
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
padding: 0;
|
||||
}
|
||||
.aksrt-partners-scroll {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
--aksrt-ps-w: 220px;
|
||||
--aksrt-ps-h: 90px;
|
||||
--aksrt-ps-radius: 10px;
|
||||
--aksrt-ps-speed: 3;
|
||||
}
|
||||
.aksrt-ps-end-fade::before,
|
||||
.aksrt-ps-end-fade::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 48px;
|
||||
z-index: 2;
|
||||
pointer-events: none;
|
||||
}
|
||||
.aksrt-ps-end-fade::before {
|
||||
left: 0;
|
||||
background: linear-gradient(90deg, var(--surface-1, #fff), transparent);
|
||||
}
|
||||
.aksrt-ps-end-fade::after {
|
||||
right: 0;
|
||||
background: linear-gradient(270deg, var(--surface-1, #fff), transparent);
|
||||
}
|
||||
.aksrt-ps-row {
|
||||
overflow: hidden;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.aksrt-ps-row:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.aksrt-ps-track {
|
||||
display: flex;
|
||||
width: max-content;
|
||||
animation: aksrt-ps-marquee calc(40s / var(--aksrt-ps-speed)) linear infinite;
|
||||
}
|
||||
.aksrt-ps-dir-right .aksrt-ps-track {
|
||||
animation-name: aksrt-ps-marquee-rev;
|
||||
}
|
||||
.aksrt-ps-pause-hover:hover .aksrt-ps-track {
|
||||
animation-play-state: paused;
|
||||
}
|
||||
.aksrt-ps-group {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
align-items: center;
|
||||
}
|
||||
.aksrt-ps-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
width: var(--aksrt-ps-w);
|
||||
height: var(--aksrt-ps-h);
|
||||
margin-right: 12px;
|
||||
border-radius: var(--aksrt-ps-radius);
|
||||
overflow: hidden;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
box-sizing: border-box;
|
||||
padding: 8px 12px;
|
||||
}
|
||||
.aksrt-ps-card-bg .aksrt-ps-item {
|
||||
background: var(--surface-2, #f3f4f6);
|
||||
}
|
||||
.aksrt-ps-img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
object-fit: contain;
|
||||
display: block;
|
||||
}
|
||||
.aksrt-ps-style-logo .aksrt-ps-item {
|
||||
justify-content: center;
|
||||
padding: 10px;
|
||||
}
|
||||
.aksrt-ps-card-icon {
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
flex-shrink: 0;
|
||||
margin-right: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.aksrt-ps-card-icon .aksrt-ps-img {
|
||||
max-height: 42px;
|
||||
}
|
||||
.aksrt-ps-card-text {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.aksrt-ps-card-title {
|
||||
font-weight: 700;
|
||||
font-size: 13px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.aksrt-ps-card-desc {
|
||||
font-size: 12px;
|
||||
color: var(--muted-2-color, #999);
|
||||
margin-top: 3px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.aksrt-ps-style-chip .aksrt-ps-item {
|
||||
width: auto;
|
||||
height: auto;
|
||||
min-height: 36px;
|
||||
border-radius: 999px;
|
||||
padding: 6px 14px;
|
||||
gap: 6px;
|
||||
}
|
||||
.aksrt-ps-chip-icon {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 6px;
|
||||
}
|
||||
.aksrt-ps-chip-title {
|
||||
font-size: 13px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
@keyframes aksrt-ps-marquee {
|
||||
from { transform: translateX(0); }
|
||||
to { transform: translateX(-50%); }
|
||||
}
|
||||
@keyframes aksrt-ps-marquee-rev {
|
||||
from { transform: translateX(-50%); }
|
||||
to { transform: translateX(0); }
|
||||
}
|
||||
|
||||
/* ---- Iframe embed ---- */
|
||||
.aksrt-iframe-embed {
|
||||
margin-bottom: 16px;
|
||||
@@ -532,75 +386,6 @@
|
||||
}
|
||||
|
||||
|
||||
/* ---- Pricing plans ---- */
|
||||
.aksrt-pricing { margin-bottom: 16px; }
|
||||
.aksrt-pricing__cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||
gap: 16px;
|
||||
}
|
||||
.aksrt-pricing__card {
|
||||
position: relative;
|
||||
border: 1px solid var(--color-line, #e5e7eb);
|
||||
border-radius: 14px;
|
||||
padding: 22px 18px 18px;
|
||||
background: var(--surface-1, #fff);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100%;
|
||||
}
|
||||
.aksrt-pricing__card.is-featured {
|
||||
box-shadow: 0 10px 28px rgba(0,0,0,.08);
|
||||
border-color: transparent;
|
||||
border-top: 3px solid var(--plan-accent, #3b82f6);
|
||||
}
|
||||
.aksrt-pricing__card.accent-pink { --plan-accent: #ec4899; }
|
||||
.aksrt-pricing__card.accent-purple { --plan-accent: #a855f7; }
|
||||
.aksrt-pricing__card.accent-blue { --plan-accent: #3b82f6; }
|
||||
.aksrt-pricing__card.accent-green { --plan-accent: #22c55e; }
|
||||
.aksrt-pricing__card.accent-yellow { --plan-accent: #eab308; }
|
||||
.aksrt-pricing__card.accent-cyan { --plan-accent: #06b6d4; }
|
||||
.aksrt-pricing__badge {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
right: 12px;
|
||||
font-size: 11px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 999px;
|
||||
background: var(--plan-accent, #3b82f6);
|
||||
color: #fff;
|
||||
}
|
||||
.aksrt-pricing__name { font-size: 18px; font-weight: 700; margin: 0 0 6px; }
|
||||
.aksrt-pricing__desc { font-size: 13px; color: var(--muted-2-color, #888); margin: 0 0 12px; }
|
||||
.aksrt-pricing__price { display: flex; align-items: baseline; gap: 10px; margin-bottom: 12px; }
|
||||
.aksrt-pricing__main { font-size: 32px; font-weight: 800; color: var(--plan-accent, #3b82f6); line-height: 1; }
|
||||
.aksrt-pricing__mark { font-size: .55em; margin-right: 2px; }
|
||||
.aksrt-pricing__original { font-size: 13px; color: #999; text-decoration: line-through; }
|
||||
.aksrt-pricing__features { list-style: none; margin: 0 0 16px; padding: 0; flex: 1; }
|
||||
.aksrt-pricing__features li { display: flex; gap: 8px; font-size: 13px; padding: 5px 0; }
|
||||
.aksrt-pricing__features i { color: var(--plan-accent, #3b82f6); margin-top: 2px; }
|
||||
.aksrt-pricing__btn {
|
||||
display: block;
|
||||
text-align: center;
|
||||
padding: 10px 14px;
|
||||
border-radius: 999px;
|
||||
background: var(--plan-accent, #3b82f6);
|
||||
color: #fff !important;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
}
|
||||
.aksrt-pricing__trust {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 14px 22px;
|
||||
margin-top: 18px;
|
||||
justify-content: center;
|
||||
}
|
||||
.aksrt-pricing__trust-item { display: flex; gap: 8px; align-items: flex-start; font-size: 13px; }
|
||||
.aksrt-pricing__trust-item i { color: var(--color-primary, #3b82f6); margin-top: 2px; }
|
||||
.aksrt-pricing__trust-item strong { display: block; }
|
||||
.aksrt-pricing__trust-item span { color: var(--muted-2-color, #888); font-size: 12px; }
|
||||
|
||||
/* ---- FAQ ---- */
|
||||
.aksrt-faq { margin-bottom: 16px; }
|
||||
.aksrt-faq__search { display: flex; gap: 8px; margin-bottom: 14px; }
|
||||
@@ -671,38 +456,6 @@
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/* ---- Stats ---- */
|
||||
.aksrt-stats { margin-bottom: 16px; }
|
||||
.aksrt-stats__intro { margin-bottom: 14px; }
|
||||
.aksrt-stats__intro-title { font-size: 22px; margin: 0 0 6px; }
|
||||
.aksrt-stats__intro-sub { color: var(--muted-2-color, #888); margin: 0; font-size: 13px; }
|
||||
.aksrt-stats__grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(var(--aksrt-stats-pc, 4), minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
@media (max-width: 767.98px) {
|
||||
.aksrt-stats__grid {
|
||||
grid-template-columns: repeat(var(--aksrt-stats-m, 2), minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
.aksrt-stats__item {
|
||||
border-radius: 12px;
|
||||
padding: 16px 14px;
|
||||
background: var(--surface-1, #fff);
|
||||
border: 1px solid var(--color-line, #e5e7eb);
|
||||
text-align: center;
|
||||
}
|
||||
.aksrt-stats__item.color-pink { --stat-c: #ec4899; }
|
||||
.aksrt-stats__item.color-purple { --stat-c: #a855f7; }
|
||||
.aksrt-stats__item.color-orange { --stat-c: #f97316; }
|
||||
.aksrt-stats__item.color-blue { --stat-c: #3b82f6; }
|
||||
.aksrt-stats__item.color-green { --stat-c: #22c55e; }
|
||||
.aksrt-stats__icon { font-size: 20px; color: var(--stat-c, #3b82f6); margin-bottom: 8px; }
|
||||
.aksrt-stats__value { font-size: 28px; font-weight: 800; color: var(--stat-c, #3b82f6); line-height: 1.1; }
|
||||
.aksrt-stats__prefix, .aksrt-stats__unit { font-size: .55em; font-weight: 600; }
|
||||
.aksrt-stats__label { margin-top: 6px; font-size: 12px; color: var(--muted-2-color, #888); }
|
||||
|
||||
/* ---- Forum topic lists ---- */
|
||||
.aksrt-ftopic-list { list-style: none; margin: 0; padding: 0; }
|
||||
.aksrt-ftopic-list__item {
|
||||
|
||||
@@ -1,69 +1,7 @@
|
||||
/**
|
||||
* Batch3c: reviews + highlights.
|
||||
* Batch3c: highlights.
|
||||
*/
|
||||
|
||||
.aksrt-reviews {
|
||||
margin-bottom: 16px;
|
||||
--aksrt-rev-pc: 3;
|
||||
--aksrt-rev-m: 1.2;
|
||||
}
|
||||
.aksrt-reviews__track {
|
||||
display: grid;
|
||||
grid-auto-flow: column;
|
||||
grid-auto-columns: calc((100% - (var(--aksrt-rev-pc) - 1) * 12px) / var(--aksrt-rev-pc));
|
||||
gap: 12px;
|
||||
overflow-x: auto;
|
||||
scroll-snap-type: x mandatory;
|
||||
padding-bottom: 6px;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
@media (max-width: 767.98px) {
|
||||
.aksrt-reviews__track {
|
||||
grid-auto-columns: calc((100% - (var(--aksrt-rev-m) - 1) * 12px) / var(--aksrt-rev-m));
|
||||
}
|
||||
}
|
||||
.aksrt-reviews__slide { scroll-snap-align: start; min-width: 0; }
|
||||
.aksrt-reviews__card {
|
||||
position: relative;
|
||||
border: 1px solid var(--color-line, #e5e7eb);
|
||||
border-radius: 12px;
|
||||
padding: 16px;
|
||||
background: var(--surface-1, #fff);
|
||||
min-height: 100%;
|
||||
}
|
||||
.aksrt-reviews__head { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
|
||||
.aksrt-reviews__avatar {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
background: var(--surface-2, #eee);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #999;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.aksrt-reviews__name { font-weight: 700; font-size: 14px; }
|
||||
.aksrt-reviews__name a { color: inherit; text-decoration: none; }
|
||||
.aksrt-reviews__role { font-size: 12px; color: var(--muted-2-color, #888); }
|
||||
.aksrt-reviews__stars { margin-bottom: 8px; font-size: 12px; }
|
||||
.aksrt-reviews__stars .is-on { color: #f5a623; }
|
||||
.aksrt-reviews__stars .is-off { color: #ddd; }
|
||||
.aksrt-reviews__content {
|
||||
font-size: 13px;
|
||||
line-height: 1.55;
|
||||
color: var(--color-ink-2, #444);
|
||||
margin: 0;
|
||||
}
|
||||
.aksrt-reviews__quote {
|
||||
position: absolute;
|
||||
right: 14px;
|
||||
bottom: 10px;
|
||||
opacity: .12;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.aksrt-highlights { margin-bottom: 16px; display: flex; flex-direction: column; gap: 12px; }
|
||||
.aksrt-highlights__row {
|
||||
display: grid;
|
||||
|
||||
@@ -1,107 +1,7 @@
|
||||
/**
|
||||
* Batch3d: feature intro, term cards, paid posts.
|
||||
* Batch3d: term cards.
|
||||
*/
|
||||
|
||||
.aksrt-fi {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1.1fr;
|
||||
gap: 28px;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.aksrt-fi.media-left { grid-template-columns: 1.1fr 1fr; }
|
||||
.aksrt-fi.media-left .aksrt-fi__text { order: 2; }
|
||||
.aksrt-fi.media-left .aksrt-fi__media { order: 1; }
|
||||
@media (max-width: 991.98px) {
|
||||
.aksrt-fi,
|
||||
.aksrt-fi.media-left {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.aksrt-fi.media-left .aksrt-fi__text,
|
||||
.aksrt-fi.media-left .aksrt-fi__media { order: initial; }
|
||||
}
|
||||
.aksrt-fi__title {
|
||||
font-size: 28px;
|
||||
line-height: 1.25;
|
||||
margin: 0 0 12px;
|
||||
}
|
||||
.aksrt-fi__desc {
|
||||
font-size: 14px;
|
||||
color: var(--muted-2-color, #666);
|
||||
line-height: 1.7;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.aksrt-fi__stat {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 6px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.aksrt-fi__stat-num {
|
||||
font-size: 36px;
|
||||
font-weight: 800;
|
||||
color: var(--color-primary, #3b82f6);
|
||||
line-height: 1;
|
||||
}
|
||||
.aksrt-fi__stat-plus { font-size: 22px; font-weight: 700; color: var(--color-primary, #3b82f6); }
|
||||
.aksrt-fi__stat-label { font-size: 13px; color: var(--muted-2-color, #888); margin-left: 4px; }
|
||||
.aksrt-fi__tabs { display: flex; flex-wrap: wrap; gap: 8px; }
|
||||
.aksrt-fi__tab {
|
||||
border: 1px solid var(--color-line, #e5e7eb);
|
||||
background: var(--surface-1, #fff);
|
||||
border-radius: 999px;
|
||||
padding: 6px 14px;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.aksrt-fi__tab.is-active {
|
||||
background: var(--color-primary, #3b82f6);
|
||||
border-color: transparent;
|
||||
color: #fff;
|
||||
}
|
||||
.aksrt-fi__panel { display: none; }
|
||||
.aksrt-fi__panel.is-active { display: block; }
|
||||
.aksrt-fi__browser {
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--color-line, #e5e7eb);
|
||||
background: var(--surface-2, #f3f4f6);
|
||||
box-shadow: 0 12px 32px rgba(0,0,0,.08);
|
||||
}
|
||||
.aksrt-fi__browser.is-plain { box-shadow: none; }
|
||||
.aksrt-fi__browser-bar {
|
||||
height: 28px;
|
||||
background: #eef1f5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 0 12px;
|
||||
}
|
||||
.aksrt-fi__browser-bar span {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: #d0d5dd;
|
||||
}
|
||||
.aksrt-fi__browser-bar span:nth-child(1) { background: #ff5f57; }
|
||||
.aksrt-fi__browser-bar span:nth-child(2) { background: #febc2e; }
|
||||
.aksrt-fi__browser-bar span:nth-child(3) { background: #28c840; }
|
||||
.aksrt-fi__browser-body {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 0;
|
||||
background: #111;
|
||||
}
|
||||
.aksrt-fi__img,
|
||||
.aksrt-fi__video {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.aksrt-term-cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(var(--aksrt-tc-cols, 2), minmax(0, 1fr));
|
||||
@@ -127,37 +27,3 @@
|
||||
.aksrt-term-card__body { padding: 10px 12px 12px; }
|
||||
.aksrt-term-card__name { font-weight: 700; font-size: 14px; }
|
||||
.aksrt-term-card__count { font-size: 12px; color: var(--muted-2-color, #888); margin-top: 2px; }
|
||||
|
||||
.aksrt-paid-posts { list-style: none; margin: 0; padding: 0; }
|
||||
.aksrt-paid-posts__item {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px dashed var(--color-line, #e5e7eb);
|
||||
}
|
||||
.aksrt-paid-posts__item:last-child { border-bottom: 0; }
|
||||
.aksrt-paid-posts__thumb {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
background: var(--surface-2, #eee);
|
||||
}
|
||||
.aksrt-paid-posts__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
||||
.aksrt-paid-posts__body { min-width: 0; flex: 1; }
|
||||
.aksrt-paid-posts__title {
|
||||
display: block;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.aksrt-paid-posts__price {
|
||||
display: inline-block;
|
||||
margin-top: 4px;
|
||||
font-size: 12px;
|
||||
color: #e11d48;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
@@ -1977,7 +1977,7 @@ a.main-color > .icon-cover-card:hover {
|
||||
.widget_aksrt_icon_cover_cards .col-xs-3 { width: 25%; }
|
||||
}
|
||||
|
||||
/* ===== Batch16-18: highlights/combo/reviews/announcement/gap/pricing polish ===== */
|
||||
/* ===== Batch16-18: highlights/combo/announcement/gap polish ===== */
|
||||
|
||||
.aksrt-highlights__row {
|
||||
display: grid;
|
||||
@@ -2063,80 +2063,6 @@ a.main-color > .icon-cover-card:hover {
|
||||
.aksrt-combo-info .col-xs-4 { width: 33.333%; }
|
||||
}
|
||||
|
||||
/* Reviews swiper */
|
||||
.aksrt-reviews-swiper {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
padding: 0 28px 4px;
|
||||
}
|
||||
.aksrt-reviews-swiper .swiper-slide {
|
||||
width: calc(100% / var(--aksrt-rev-pc, 3));
|
||||
max-width: 360px;
|
||||
height: auto;
|
||||
padding-right: 12px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.aksrt-reviews-swiper .swiper-slide {
|
||||
width: calc(100% / var(--aksrt-rev-m, 1.2));
|
||||
}
|
||||
}
|
||||
.aksrt-reviews__card {
|
||||
height: 100%;
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--color-line, #e5e7eb);
|
||||
background: var(--surface-1, #fff);
|
||||
padding: 16px;
|
||||
position: relative;
|
||||
}
|
||||
.aksrt-reviews__head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.aksrt-reviews__avatar {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
background: var(--surface-2, #eee);
|
||||
}
|
||||
.aksrt-reviews__avatar.is-placeholder {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #999;
|
||||
}
|
||||
.aksrt-reviews__name { font-weight: 700; }
|
||||
.aksrt-reviews__role { font-size: 12px; color: var(--muted-2-color, #888); }
|
||||
.aksrt-reviews__stars .fa-star.is-on { color: #f5b301; }
|
||||
.aksrt-reviews__stars .fa-star.is-off { color: #ddd; }
|
||||
.aksrt-reviews__content { margin: 10px 0 0; font-size: 13px; line-height: 1.6; color: var(--muted-color, #555); }
|
||||
.aksrt-reviews__quote {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
bottom: 10px;
|
||||
opacity: .12;
|
||||
font-size: 28px;
|
||||
}
|
||||
.aksrt-reviews-swiper .swiper-button-prev,
|
||||
.aksrt-reviews-swiper .swiper-button-next {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
margin-top: 0;
|
||||
top: 42%;
|
||||
border-radius: 50%;
|
||||
background: var(--surface-1, #fff);
|
||||
border: 1px solid var(--color-line, #e5e7eb);
|
||||
color: var(--color-ink, #111);
|
||||
}
|
||||
.aksrt-reviews-swiper .swiper-button-prev:after,
|
||||
.aksrt-reviews-swiper .swiper-button-next:after { font-size: 12px; font-weight: 700; }
|
||||
.aksrt-reviews-swiper .swiper-button-prev { left: 0; }
|
||||
.aksrt-reviews-swiper .swiper-button-next { right: 0; }
|
||||
|
||||
/* Announcement vertical swiper reuse notice styles */
|
||||
.aksrt-announcement-swiper-wrap {
|
||||
display: flex;
|
||||
@@ -2227,36 +2153,6 @@ a.main-color > .icon-cover-card:hover {
|
||||
padding: 1px 8px;
|
||||
}
|
||||
|
||||
.aksrt-paid-posts__item.is-mini {
|
||||
position: relative;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.aksrt-paid-posts__item.is-mini .aksrt-paid-posts__price.badge {
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
right: 6px;
|
||||
z-index: 2;
|
||||
background: rgba(239, 68, 68, .92);
|
||||
color: #fff;
|
||||
font-size: 11px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
.aksrt-lang-switch-widget.hide-name .aksrt-lang-switch__toggle span,
|
||||
.aksrt-lang-switch-widget.hide-name .aksrt-lang-switch__item {
|
||||
font-size: 0;
|
||||
}
|
||||
.aksrt-lang-switch-widget.hide-current .aksrt-lang-switch__item.is-active,
|
||||
.aksrt-lang-switch-widget.hide-current .aksrt-lang-switch__flag.is-active {
|
||||
display: none;
|
||||
}
|
||||
.aksrt-lang-switch-widget.style-inline .aksrt-lang-switch--flags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.aksrt-forum-widget__item.is-top .aksrt-forum-widget__rank {
|
||||
background: var(--theme-color, #f04494);
|
||||
color: #fff;
|
||||
@@ -2380,8 +2276,6 @@ a.main-color > .icon-cover-card:hover {
|
||||
justify-content: center;
|
||||
}
|
||||
.aksrt-forum-widget__rank-row badge.img-badge { position: static; flex: 0 0 auto; margin-top: 4px; }
|
||||
.aksrt-stats__item[data-aksrt-animate] { will-change: transform, opacity; }
|
||||
|
||||
|
||||
/* batch28-30 residual */
|
||||
.aksrt-ftab.widget-tab-posts .aksrt-ftab__nav { display: flex; flex-wrap: wrap; gap: 8px; list-style: none; margin: 0 0 12px; padding: 0; }
|
||||
@@ -2392,14 +2286,6 @@ a.main-color > .icon-cover-card:hover {
|
||||
.aksrt-ftab__btn.is-active { background: var(--theme-color, #f04494); color: #fff; }
|
||||
.aksrt-ftab__panel[hidden] { display: none !important; }
|
||||
.aksrt-ftab__panel.is-ajax-loading { opacity: .65; pointer-events: none; }
|
||||
.aksrt-reviews__avatar.aksrt-lazy { object-fit: cover; border-radius: 50%; }
|
||||
|
||||
|
||||
/* batch31-33 residual */
|
||||
.aksrt-fi__img.aksrt-lazy { width: 100%; height: 100%; object-fit: cover; display: block; position: absolute; inset: 0; }
|
||||
.aksrt-fi__video { width: 100%; height: 100%; object-fit: cover; position: absolute; inset: 0; }
|
||||
|
||||
|
||||
/* batch34-36 residual */
|
||||
.widget-hot-posts--graphic .hot-posts-graphic { margin-bottom: 12px; border-radius: 8px; overflow: hidden; }
|
||||
.widget-hot-posts--graphic .hot-posts-graphic .graphic { position: relative; overflow: hidden; border-radius: 8px; background: var(--surface-2, #f3f4f6); }
|
||||
@@ -2448,19 +2334,49 @@ a.main-color > .icon-cover-card:hover {
|
||||
.posts-mini-lists.aksrt-main-posts { /* zibll mini outer */ }
|
||||
|
||||
|
||||
/* batch37-39 residual */
|
||||
.aksrt-post-pay-box { position: relative; overflow: hidden; }
|
||||
.aksrt-post-pay-box .pay-tag.abs-center {
|
||||
position: absolute; top: 12px; left: 50%; transform: translateX(-50%);
|
||||
z-index: 2; padding: 2px 10px; border-radius: 999px; background: rgba(0,0,0,.35); color: #fff; font-size: 12px;
|
||||
/* Compact paid-content purchase card for the article sidebar. */
|
||||
.aksrt-post-pay-card {
|
||||
--aksrt-pay-accent: var(--color-primary, #3b82f6);
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
padding: 16px;
|
||||
border: 1px solid color-mix(in srgb, var(--aksrt-pay-accent) 26%, var(--color-line, #e5e7eb));
|
||||
border-radius: 12px;
|
||||
background: linear-gradient(145deg, color-mix(in srgb, var(--aksrt-pay-accent) 8%, var(--surface-1, #fff)), var(--surface-1, #fff));
|
||||
}
|
||||
.aksrt-post-pay-box__hero { position: relative; overflow: hidden; min-height: 120px; color: #fff; }
|
||||
.aksrt-post-pay-box__blob { opacity: .55; pointer-events: none; }
|
||||
.aksrt-post-pay-box__blob--a { height: 200px; left: 75%; width: 200px; top: -34%; border-radius: 100%; }
|
||||
.aksrt-post-pay-box__blob--b { height: 305px; width: 337px; left: -229px; border-radius: 100%; opacity: .7; }
|
||||
.aksrt-post-pay-box .box-body { position: relative; z-index: 1; }
|
||||
.aksrt-post-pay-box .img-badge.hot { position: absolute; top: 8px; right: 8px; z-index: 2; }
|
||||
.aksrt-post-pay-box .but.btn-block { width: 100%; justify-content: center; }
|
||||
.aksrt-post-pay-card.jb-red { --aksrt-pay-accent: #ef4444; }
|
||||
.aksrt-post-pay-card.jb-yellow { --aksrt-pay-accent: #f59e0b; }
|
||||
.aksrt-post-pay-card.jb-blue { --aksrt-pay-accent: #3b82f6; }
|
||||
.aksrt-post-pay-card.jb-green { --aksrt-pay-accent: #22c55e; }
|
||||
.aksrt-post-pay-card.jb-purple { --aksrt-pay-accent: #8b5cf6; }
|
||||
.aksrt-post-pay-card.jb-vip1 { --aksrt-pay-accent: #d49a25; }
|
||||
.aksrt-post-pay-card.jb-vip2 { --aksrt-pay-accent: #64748b; }
|
||||
.aksrt-post-pay-card__header,
|
||||
.aksrt-post-pay-card__price-row,
|
||||
.aksrt-post-pay-card__actions { display: flex; align-items: center; gap: 8px; }
|
||||
.aksrt-post-pay-card__header { justify-content: space-between; }
|
||||
.aksrt-post-pay-card__type,
|
||||
.aksrt-post-pay-card__sales {
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
}
|
||||
.aksrt-post-pay-card__type {
|
||||
padding: 5px 8px;
|
||||
border-radius: 999px;
|
||||
color: var(--aksrt-pay-accent);
|
||||
background: color-mix(in srgb, var(--aksrt-pay-accent) 13%, transparent);
|
||||
font-weight: 700;
|
||||
}
|
||||
.aksrt-post-pay-card__sales { color: var(--muted-2-color, #888); }
|
||||
.aksrt-post-pay-card__price-row { min-height: 32px; }
|
||||
.aksrt-post-pay-card__price,
|
||||
.aksrt-post-pay-card__status { color: var(--aksrt-pay-accent); font-size: 25px; line-height: 1.2; }
|
||||
.aksrt-post-pay-card__status { font-size: 19px; }
|
||||
.aksrt-post-pay-card__status i { margin-right: 6px; }
|
||||
.aksrt-post-pay-card__price-row del { color: var(--muted-2-color, #888); font-size: 13px; }
|
||||
.aksrt-post-pay-card__hint { margin: -4px 0 0; color: var(--muted-2-color, #888); font-size: 12px; }
|
||||
.aksrt-post-pay-card__actions { align-items: stretch; flex-direction: column; }
|
||||
.aksrt-post-pay-card__actions .but { justify-content: center; width: 100%; }
|
||||
.aksrt-main-posts__skeleton .placeholder-row,
|
||||
.aksrt-product-tab__skeleton .placeholder-row {
|
||||
display: flex; flex-direction: column; gap: 8px; margin-bottom: 12px;
|
||||
@@ -2498,10 +2414,6 @@ a.main-color > .icon-cover-card:hover {
|
||||
flex: 1 1 0; min-width: 0; text-align: center;
|
||||
}
|
||||
.aksrt-plate-info .count-mates .em14 { font-weight: 700; line-height: 1.2; }
|
||||
.aksrt-fi[data-aksrt-feature-intro] .aksrt-fi__panel { display: none; }
|
||||
.aksrt-fi[data-aksrt-feature-intro] .aksrt-fi__panel.is-active { display: block; }
|
||||
|
||||
|
||||
/* batch43-45 residual */
|
||||
.aksrt-product-widget__item--mini {
|
||||
gap: 10px; padding: 8px 0;
|
||||
@@ -2513,14 +2425,6 @@ a.main-color > .icon-cover-card:hover {
|
||||
background: var(--surface-2, #f3f4f6); display: block;
|
||||
}
|
||||
.aksrt-product-widget__thumb.mini img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
||||
.aksrt-paid-posts.style-card { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 12px; }
|
||||
.aksrt-paid-posts.style-list .aksrt-paid-posts__item { margin-bottom: 10px; }
|
||||
.aksrt-paid-posts__price.badge {
|
||||
display: inline-block; margin-top: 4px; padding: 1px 6px; border-radius: 4px;
|
||||
background: rgba(240,68,148,.1); color: var(--theme-color, #f04494); font-size: 12px;
|
||||
}
|
||||
|
||||
|
||||
/* batch46-48 residual */
|
||||
.widget .is-affix[data-affix="true"],
|
||||
.ajaxpager.is-affix[data-affix="true"],
|
||||
|
||||
Vendored
-38
@@ -95,44 +95,6 @@
|
||||
setInterval(update, 1000);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 3. 合作伙伴无缝滚动 (marquee)
|
||||
* 用法:<div class="aksrt-marquee" data-speed="1">...内容...</div>
|
||||
* ============================================================ */
|
||||
function initMarquee() {
|
||||
document.querySelectorAll('.aksrt-marquee').forEach(function (el) {
|
||||
if (el.dataset.marqueeBound) return;
|
||||
el.dataset.marqueeBound = '1';
|
||||
|
||||
var speed = parseFloat(el.dataset.speed || '1');
|
||||
var inner = el.querySelector('.aksrt-marquee__inner') || el.children[0];
|
||||
if (!inner) return;
|
||||
|
||||
// 克隆内容实现无缝
|
||||
var clone = inner.cloneNode(true);
|
||||
el.appendChild(clone);
|
||||
|
||||
var pos = 0;
|
||||
var width = inner.scrollWidth;
|
||||
|
||||
function animate() {
|
||||
pos -= speed;
|
||||
if (Math.abs(pos) >= width) pos = 0;
|
||||
inner.style.transform = 'translateX(' + pos + 'px)';
|
||||
clone.style.transform = 'translateX(' + (pos + width) + 'px)';
|
||||
if (!reducedMotion) requestAnimationFrame(animate);
|
||||
}
|
||||
|
||||
if (!reducedMotion) {
|
||||
requestAnimationFrame(animate);
|
||||
}
|
||||
|
||||
// 暂停/恢复
|
||||
el.addEventListener('mouseenter', function () { el.dataset.paused = '1'; });
|
||||
el.addEventListener('mouseleave', function () { delete el.dataset.paused; });
|
||||
});
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 4. 3D 悬停倾斜 (vanilla tilt)
|
||||
* 用法:<div data-aksrt-tilt data-tilt-max="15">...</div>
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
/**
|
||||
* AKSRT Frontend Translate — 前端即时翻译
|
||||
*
|
||||
* 渐进增强,默认关。通过 Settings 启用后,
|
||||
* 在语言切换 Widget 中添加"翻译本页"按钮,
|
||||
* 使用浏览器原生翻译或自定义翻译 API。
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var config = (window.aksrtData && window.aksrtData.translate) || {};
|
||||
if (!config.enabled) return;
|
||||
|
||||
/**
|
||||
* 触发浏览器原生翻译(Chrome/Edge 内置)
|
||||
*/
|
||||
function triggerBrowserTranslate(targetLang) {
|
||||
// Chrome/Edge 有内置翻译功能
|
||||
if (document.querySelector('.goog-te-banner-frame') || document.querySelector('#goog-gt-tt')) {
|
||||
return; // 已经在翻译中
|
||||
}
|
||||
|
||||
// 尝试使用 Google Translate Element
|
||||
if (window.google && window.google.translate) {
|
||||
new google.translate.TranslateElement({
|
||||
pageLanguage: config.sourceLang || 'zh-CN',
|
||||
includedLanguages: targetLang || '',
|
||||
layout: google.translate.TranslateElement.InlineLayout.SIMPLE,
|
||||
autoDisplay: false,
|
||||
}, 'aksrt-translate-container');
|
||||
return;
|
||||
}
|
||||
|
||||
// 动态加载 Google Translate
|
||||
var script = document.createElement('script');
|
||||
script.src = '//translate.google.com/translate_a/element.js?cb=aksrtTranslateInit';
|
||||
document.head.appendChild(script);
|
||||
|
||||
window.aksrtTranslateInit = function () {
|
||||
if (!window.google || !window.google.translate) return;
|
||||
new google.translate.TranslateElement({
|
||||
pageLanguage: config.sourceLang || 'zh-CN',
|
||||
includedLanguages: targetLang || config.targetLang || 'en',
|
||||
layout: google.translate.TranslateElement.InlineLayout.SIMPLE,
|
||||
autoDisplay: false,
|
||||
}, 'aksrt-translate-container');
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建翻译容器和按钮
|
||||
*/
|
||||
function init() {
|
||||
// 检查是否有语言切换 Widget
|
||||
var langWidget = document.querySelector('.aksrt-lang-switch, [data-aksrt-lang-switch]');
|
||||
if (!langWidget && !config.showButton) return;
|
||||
|
||||
// 创建隐藏的翻译容器
|
||||
var container = document.createElement('div');
|
||||
container.id = 'aksrt-translate-container';
|
||||
container.style.cssText = 'position:fixed;top:-9999px;left:-9999px;visibility:hidden;';
|
||||
document.body.appendChild(container);
|
||||
|
||||
// 如果配置了显示按钮,在语言切换 Widget 旁添加
|
||||
if (config.showButton && langWidget) {
|
||||
var btn = document.createElement('button');
|
||||
btn.type = 'button';
|
||||
btn.className = 'aksrt-translate-btn';
|
||||
btn.innerHTML = '<i class="fas fa-language"></i> 翻译本页';
|
||||
btn.addEventListener('click', function () {
|
||||
triggerBrowserTranslate(config.targetLang || 'en');
|
||||
btn.textContent = '翻译中...';
|
||||
btn.disabled = true;
|
||||
});
|
||||
langWidget.appendChild(btn);
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', init);
|
||||
})();
|
||||
@@ -26,9 +26,9 @@
|
||||
document.querySelector('[data-nav="posts"]') ||
|
||||
document.querySelector('[data-nav]');
|
||||
|
||||
// Support multiple TOC shells (built-in single sidebar + WidgetPostsNavs).
|
||||
// The article sidebar and mobile drawer share one built-in TOC source.
|
||||
var tocWidgets = Array.prototype.slice.call(
|
||||
document.querySelectorAll('.posts-nav-box.toc-widget, .posts-nav-box[data-title]')
|
||||
document.querySelectorAll('.posts-nav-box.toc-widget')
|
||||
);
|
||||
var tocLists = [];
|
||||
tocWidgets.forEach(function (box) {
|
||||
|
||||
@@ -280,42 +280,6 @@
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Feature intro media tabs.
|
||||
function initFeatureIntro() {
|
||||
document.querySelectorAll('[data-aksrt-feature-intro]').forEach(function (root) {
|
||||
if (root.dataset.aksrtFiBound === '1') return;
|
||||
root.dataset.aksrtFiBound = '1';
|
||||
var tabs = [].slice.call(root.querySelectorAll('.aksrt-fi__tab'));
|
||||
var panels = [].slice.call(root.querySelectorAll('.aksrt-fi__panel'));
|
||||
if (!tabs.length || !panels.length) return;
|
||||
function go(i) {
|
||||
tabs.forEach(function (t, idx) {
|
||||
t.classList.toggle('is-active', idx === i);
|
||||
});
|
||||
panels.forEach(function (p, idx) {
|
||||
p.classList.toggle('is-active', idx === i);
|
||||
var v = p.querySelector('video');
|
||||
if (!v) return;
|
||||
if (idx === i) {
|
||||
if (!v.getAttribute('src') && v.getAttribute('data-src')) {
|
||||
v.setAttribute('src', v.getAttribute('data-src'));
|
||||
}
|
||||
if (v.play) v.play().catch(function () {});
|
||||
} else if (v.pause) {
|
||||
v.pause();
|
||||
}
|
||||
});
|
||||
}
|
||||
tabs.forEach(function (t) {
|
||||
t.addEventListener('click', function () {
|
||||
go(parseInt(t.getAttribute('data-index') || '0', 10));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function initFloatingQRCodes() {
|
||||
var wrap = document.querySelector('.float-right');
|
||||
if (!wrap) return;
|
||||
@@ -363,7 +327,6 @@
|
||||
initCategoryTabs();
|
||||
initProductTabs();
|
||||
initMainPostsLazy();
|
||||
initFeatureIntro();
|
||||
initFloatingQRCodes();
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -49,7 +49,7 @@ $archive_settings = class_exists('\AKSRT\Core\TermMeta')
|
||||
|
||||
$has_sidebar = function_exists('aksrt_archive_has_sidebar')
|
||||
? aksrt_archive_has_sidebar('cat_sidebar', !empty($archive_settings['show_sidebar']))
|
||||
: (!empty($archive_settings['show_sidebar']) && (is_active_sidebar('cat_sidebar') || is_active_sidebar('sidebar-blog')));
|
||||
: (!empty($archive_settings['show_sidebar']) && is_active_sidebar('cat_sidebar'));
|
||||
$description = !empty($archive_settings['show_desc']) ? wp_kses_post((string) $archive_settings['description']) : '';
|
||||
$header_style = (string) $archive_settings['header_style'];
|
||||
$layout_mode = (string) $archive_settings['layout_mode'];
|
||||
|
||||
+1
-1
@@ -41,7 +41,7 @@ require_once AKSRT_DIR . '/inc/helpers/aksrt-forum.php';
|
||||
require_once AKSRT_DIR . '/inc/helpers/aksrt-slider.php';
|
||||
require_once AKSRT_DIR . '/inc/helpers/aksrt-vip.php';
|
||||
require_once AKSRT_DIR . '/inc/helpers/aksrt-auth-ui.php';
|
||||
require_once AKSRT_DIR . '/inc/helpers/aksrt-i18n-switch.php';
|
||||
require_once AKSRT_DIR . '/inc/helpers/aksrt-i18n-header.php';
|
||||
require_once AKSRT_DIR . '/inc/helpers/aksrt-posts-renderer.php';
|
||||
require_once AKSRT_DIR . '/inc/helpers/aksrt-resource.php';
|
||||
require_once AKSRT_DIR . '/inc/helpers/aksrt-pay.php';
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
/**
|
||||
* Header and mobile navigation language controls.
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
/**
|
||||
* Header language dropdown (zibll zib_get_translate_language_btn).
|
||||
* Uses SPA i18n_available_langs when i18n enabled and position includes header.
|
||||
*
|
||||
* @param string $class Extra classes.
|
||||
*/
|
||||
function aksrt_get_translate_language_btn(string $class = ''): string {
|
||||
if (!class_exists('\AKSRT\Settings\Settings')) {
|
||||
return '';
|
||||
}
|
||||
$cfg = \AKSRT\Settings\Settings::get_i18n_config();
|
||||
if (empty($cfg['enabled'])) {
|
||||
return '';
|
||||
}
|
||||
$pos = (string) ($cfg['switcherPosition'] ?? 'footer');
|
||||
if (!in_array($pos, ['header', 'both'], true)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$raw = (string) ($cfg['availableLangs'] ?? '');
|
||||
$buttons = [];
|
||||
foreach (preg_split('/\r\n|\r|\n/', $raw) as $line) {
|
||||
$line = trim($line);
|
||||
if ($line === '' || strpos($line, '|') === false) {
|
||||
continue;
|
||||
}
|
||||
[$code, $label] = array_map('trim', explode('|', $line, 2));
|
||||
if ($code === '' || $label === '') {
|
||||
continue;
|
||||
}
|
||||
$buttons[] = ['language' => $code, 'text' => $label];
|
||||
}
|
||||
if (!$buttons) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$buttons_html = '';
|
||||
foreach ($buttons as $button) {
|
||||
$buttons_html .= '<li><a href="javascript:;" class="translate-button translate-ignore flex ac" data-language="'
|
||||
. esc_attr($button['language']) . '">' . esc_html($button['text']) . '</a></li>';
|
||||
}
|
||||
$is_mobile = wp_is_mobile();
|
||||
$dropdown = '<ul class="dropdown-menu hover-show-con">' . $buttons_html . '</ul>';
|
||||
$hover = !$is_mobile ? ' hover-show' : '';
|
||||
$toggle = !$is_mobile ? '' : ' data-toggle="dropdown"';
|
||||
return '<div class="dropdown language-dropdown ' . esc_attr($class) . $hover . '">'
|
||||
. '<a href="javascript:;" class="toggle-radius"' . $toggle . ' title="' . esc_attr__('切换语言', 'aksrt') . '">'
|
||||
. '<i class="fas fa-language" aria-hidden="true"></i></a>' . $dropdown . '</div>';
|
||||
}
|
||||
@@ -1,202 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* AKSRT i18n Switch — 多语言切换辅助函数
|
||||
*
|
||||
* 支持 Polylang、WPML 或 WordPress 内置语言切换。
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
/**
|
||||
* 获取可用语言列表
|
||||
*
|
||||
* @return array<array{code: string, name: string, url: string, flag: string, active: bool}>
|
||||
*/
|
||||
function aksrt_get_available_languages(): array {
|
||||
$languages = [];
|
||||
|
||||
// Polylang
|
||||
if (function_exists('pll_the_languages')) {
|
||||
$pll_langs = pll_the_languages(['raw' => 1]);
|
||||
if (is_array($pll_langs)) {
|
||||
foreach ($pll_langs as $lang) {
|
||||
$languages[] = [
|
||||
'code' => $lang['slug'] ?? '',
|
||||
'name' => $lang['name'] ?? '',
|
||||
'url' => $lang['url'] ?? '#',
|
||||
'flag' => $lang['flag'] ?? '',
|
||||
'active' => !empty($lang['current_lang']),
|
||||
];
|
||||
}
|
||||
}
|
||||
return $languages;
|
||||
}
|
||||
|
||||
// WPML
|
||||
if (defined('ICL_LANGUAGE_CODE') && function_exists('icl_get_languages')) {
|
||||
$wpml_langs = icl_get_languages('skip_missing=0');
|
||||
if (is_array($wpml_langs)) {
|
||||
foreach ($wpml_langs as $lang) {
|
||||
$languages[] = [
|
||||
'code' => $lang['language_code'] ?? '',
|
||||
'name' => $lang['native_name'] ?? '',
|
||||
'url' => $lang['url'] ?? '#',
|
||||
'flag' => $lang['country_flag_url'] ?? '',
|
||||
'active' => !empty($lang['active']),
|
||||
];
|
||||
}
|
||||
}
|
||||
return $languages;
|
||||
}
|
||||
|
||||
// WordPress 内置:通过 filter 提供语言列表
|
||||
$filtered = apply_filters('aksrt_available_languages', []);
|
||||
if (is_array($filtered) && !empty($filtered)) {
|
||||
return $filtered;
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否有多语言插件可用
|
||||
*/
|
||||
function aksrt_has_multilingual(): bool {
|
||||
if (function_exists('pll_the_languages')) return true;
|
||||
if (defined('ICL_LANGUAGE_CODE')) return true;
|
||||
if (!empty(apply_filters('aksrt_available_languages', []))) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前语言代码
|
||||
*/
|
||||
function aksrt_get_current_language(): string {
|
||||
if (function_exists('pll_current_language')) {
|
||||
return pll_current_language('slug');
|
||||
}
|
||||
if (defined('ICL_LANGUAGE_CODE')) {
|
||||
return ICL_LANGUAGE_CODE;
|
||||
}
|
||||
return get_locale();
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染语言切换器 HTML
|
||||
*
|
||||
* @param string $style dropdown | inline | flags
|
||||
* @return string
|
||||
*/
|
||||
function aksrt_render_language_switcher(string $style = 'dropdown'): string {
|
||||
$languages = aksrt_get_available_languages();
|
||||
if (count($languages) < 2) return '';
|
||||
|
||||
$current = aksrt_get_current_language();
|
||||
|
||||
if ($style === 'flags') {
|
||||
$html = '<div class="aksrt-lang-switch aksrt-lang-switch--flags">';
|
||||
foreach ($languages as $lang) {
|
||||
$active_class = $lang['active'] ? ' is-active' : '';
|
||||
$html .= '<a href="' . esc_url($lang['url']) . '" class="aksrt-lang-switch__flag' . esc_attr($active_class) . '" title="' . esc_attr($lang['name']) . '">';
|
||||
if ($lang['flag']) {
|
||||
$html .= '<img src="' . esc_url($lang['flag']) . '" alt="' . esc_attr($lang['code']) . '" width="20" height="14">';
|
||||
} else {
|
||||
$html .= '<span>' . esc_html(strtoupper($lang['code'])) . '</span>';
|
||||
}
|
||||
$html .= '</a>';
|
||||
}
|
||||
$html .= '</div>';
|
||||
return $html;
|
||||
}
|
||||
|
||||
// dropdown style
|
||||
$html = '<div class="aksrt-lang-switch">';
|
||||
$html .= '<button type="button" class="aksrt-lang-switch__toggle" aria-haspopup="true" aria-expanded="false">';
|
||||
|
||||
// 当前语言
|
||||
foreach ($languages as $lang) {
|
||||
if ($lang['active'] || $lang['code'] === $current) {
|
||||
if ($lang['flag']) {
|
||||
$html .= '<img src="' . esc_url($lang['flag']) . '" alt="" width="18" height="12"> ';
|
||||
}
|
||||
$html .= '<span>' . esc_html($lang['name']) . '</span>';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$html .= ' <i class="fas fa-chevron-down" style="font-size:10px;margin-left:4px;"></i>';
|
||||
$html .= '</button>';
|
||||
|
||||
$html .= '<ul class="aksrt-lang-switch__dropdown" role="menu">';
|
||||
foreach ($languages as $lang) {
|
||||
$active_class = $lang['active'] ? ' is-active' : '';
|
||||
$html .= '<li role="menuitem">';
|
||||
$html .= '<a href="' . esc_url($lang['url']) . '" class="aksrt-lang-switch__item' . esc_attr($active_class) . '">';
|
||||
if ($lang['flag']) {
|
||||
$html .= '<img src="' . esc_url($lang['flag']) . '" alt="" width="18" height="12"> ';
|
||||
}
|
||||
$html .= esc_html($lang['name']);
|
||||
$html .= '</a>';
|
||||
$html .= '</li>';
|
||||
}
|
||||
$html .= '</ul>';
|
||||
|
||||
$html .= '</div>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Header language dropdown (zibll zib_get_translate_language_btn).
|
||||
* Uses SPA i18n_available_langs when i18n enabled and position includes header.
|
||||
*
|
||||
* @param string $class Extra classes.
|
||||
*/
|
||||
function aksrt_get_translate_language_btn(string $class = ''): string {
|
||||
if (!class_exists('\AKSRT\Settings\Settings')) {
|
||||
return '';
|
||||
}
|
||||
$cfg = \AKSRT\Settings\Settings::get_i18n_config();
|
||||
if (empty($cfg['enabled'])) {
|
||||
return '';
|
||||
}
|
||||
$pos = (string) ($cfg['switcherPosition'] ?? 'footer');
|
||||
if (!in_array($pos, ['header', 'both'], true)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$raw = (string) ($cfg['availableLangs'] ?? '');
|
||||
$buttons = [];
|
||||
foreach (preg_split('/
|
||||
|
|
|
||||
/', $raw) as $line) {
|
||||
$line = trim($line);
|
||||
if ($line === '' || strpos($line, '|') === false) {
|
||||
continue;
|
||||
}
|
||||
[$code, $label] = array_map('trim', explode('|', $line, 2));
|
||||
if ($code === '' || $label === '') {
|
||||
continue;
|
||||
}
|
||||
$buttons[] = ['language' => $code, 'text' => $label];
|
||||
}
|
||||
if (!$buttons) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$buttons_html = '';
|
||||
foreach ($buttons as $button) {
|
||||
$buttons_html .= '<li><a href="javascript:;" class="translate-button translate-ignore flex ac" data-language="'
|
||||
. esc_attr($button['language']) . '">' . esc_html($button['text']) . '</a></li>';
|
||||
}
|
||||
$is_mobile = wp_is_mobile();
|
||||
$dropdown = '<ul class="dropdown-menu hover-show-con">' . $buttons_html . '</ul>';
|
||||
$hover = !$is_mobile ? ' hover-show' : '';
|
||||
$toggle = !$is_mobile ? '' : ' data-toggle="dropdown"';
|
||||
return '<div class="dropdown language-dropdown ' . esc_attr($class) . $hover . '">'
|
||||
. '<a href="javascript:;" class="toggle-radius"' . $toggle . ' title="' . esc_attr__('切换语言', 'aksrt') . '">'
|
||||
. '<i class="fas fa-language" aria-hidden="true"></i></a>' . $dropdown . '</div>';
|
||||
}
|
||||
|
||||
@@ -100,9 +100,16 @@ if (!function_exists('aksrt_single_build_context')) :
|
||||
$show_pay_box = false;
|
||||
}
|
||||
|
||||
$show_aside = function_exists('aksrt_is_show_sidebar')
|
||||
? aksrt_is_show_sidebar()
|
||||
: false;
|
||||
$layout = function_exists('aksrt_get_post_show_layout')
|
||||
? aksrt_get_post_show_layout($post_id)
|
||||
: '';
|
||||
$has_article_widgets = is_active_sidebar('all_sidebar_top')
|
||||
|| is_active_sidebar('sidebar-article')
|
||||
|| is_active_sidebar('all_sidebar_bottom');
|
||||
$show_aside = !wp_is_mobile()
|
||||
&& get_post_type($post_id) === 'post'
|
||||
&& $layout !== 'no_sidebar'
|
||||
&& $has_article_widgets;
|
||||
|
||||
return [
|
||||
'post_id' => $post_id,
|
||||
|
||||
@@ -50,7 +50,7 @@ function aksrt_resolve_sidebar_id(): string {
|
||||
return 'home_sidebar';
|
||||
}
|
||||
|
||||
return 'sidebar-blog';
|
||||
return 'home_sidebar';
|
||||
}
|
||||
endif;
|
||||
|
||||
@@ -206,6 +206,27 @@ if (!function_exists('aksrt_get_site_layout')):
|
||||
* Zibll site-layout mode: 1=no sidebar, 2=right, 3=left.
|
||||
*/
|
||||
function aksrt_get_site_layout(): int {
|
||||
if (is_single()) {
|
||||
$post_id = (int) get_queried_object_id();
|
||||
$layout = aksrt_get_post_show_layout($post_id);
|
||||
$has_article_widgets = is_active_sidebar('all_sidebar_top')
|
||||
|| is_active_sidebar('sidebar-article')
|
||||
|| is_active_sidebar('all_sidebar_bottom');
|
||||
if (get_post_type($post_id) !== 'post' || wp_is_mobile() || $layout === 'no_sidebar' || !$has_article_widgets) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ($layout === 'sidebar_left') {
|
||||
return 3;
|
||||
}
|
||||
if ($layout === 'sidebar_right') {
|
||||
return 2;
|
||||
}
|
||||
|
||||
$cfg = aksrt_get_sidebar_config_safe();
|
||||
return ($cfg['position'] ?? 'right') === 'left' ? 3 : 2;
|
||||
}
|
||||
|
||||
if (!aksrt_is_show_sidebar()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -153,7 +153,6 @@ if (!function_exists('aksrt_homepage_has_sidebar')) {
|
||||
'all_sidebar_top',
|
||||
'home_sidebar',
|
||||
'all_sidebar_bottom',
|
||||
'sidebar-blog',
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -175,8 +174,6 @@ if (!function_exists('aksrt_homepage_render_sidebar')) {
|
||||
|
||||
if (is_active_sidebar('home_sidebar')) {
|
||||
dynamic_sidebar('home_sidebar');
|
||||
} elseif (is_active_sidebar('sidebar-blog')) {
|
||||
dynamic_sidebar('sidebar-blog');
|
||||
}
|
||||
|
||||
if (is_active_sidebar('all_sidebar_bottom')) {
|
||||
|
||||
@@ -19,11 +19,11 @@ function aksrt_unregister_default_widgets(): void {
|
||||
unregister_widget('WP_Widget_Pages');
|
||||
unregister_widget('WP_Widget_Search');
|
||||
unregister_widget('WP_Widget_Calendar');
|
||||
unregister_widget('WP_Nav_Menu_Widget');
|
||||
}
|
||||
add_action('widgets_init', 'aksrt_unregister_default_widgets', 1);
|
||||
|
||||
function aksrt_register_widgets() {
|
||||
register_widget('\AKSRT\Widgets\AboutWidget');
|
||||
register_widget('\AKSRT\Widgets\CategoriesWidget');
|
||||
register_widget('\AKSRT\Widgets\TagCloudWidget');
|
||||
register_widget('\AKSRT\Widgets\HotPostsWidget');
|
||||
@@ -56,33 +56,20 @@ function aksrt_register_widgets() {
|
||||
// 视频播放器
|
||||
register_widget('\AKSRT\Widgets\WidgetVideoPlayer');
|
||||
|
||||
// 语言切换器
|
||||
register_widget('\AKSRT\Widgets\WidgetLangSwitch');
|
||||
|
||||
// 一言轮播
|
||||
register_widget('\AKSRT\Widgets\WidgetYiyan');
|
||||
|
||||
// 文章目录树(zibll posts_navs)
|
||||
register_widget('\AKSRT\Widgets\WidgetPostsNavs');
|
||||
|
||||
// 幻灯片
|
||||
register_widget('\AKSRT\Widgets\WidgetSlider');
|
||||
|
||||
// zibll more widgets batch
|
||||
register_widget('\AKSRT\Widgets\WidgetPartnersScroll');
|
||||
// 其余通用小工具
|
||||
register_widget('\AKSRT\Widgets\WidgetIframe');
|
||||
register_widget('\AKSRT\Widgets\WidgetComboInfo');
|
||||
register_widget('\AKSRT\Widgets\WidgetTextTitle');
|
||||
register_widget('\AKSRT\Widgets\WidgetUserLists');
|
||||
|
||||
register_widget('\AKSRT\Widgets\WidgetPricingPlans');
|
||||
register_widget('\AKSRT\Widgets\WidgetFaq');
|
||||
register_widget('\AKSRT\Widgets\WidgetStats');
|
||||
register_widget('\AKSRT\Widgets\WidgetReviews');
|
||||
register_widget('\AKSRT\Widgets\WidgetHighlights');
|
||||
register_widget('\AKSRT\Widgets\WidgetFeatureIntro');
|
||||
register_widget('\AKSRT\Widgets\WidgetTermCards');
|
||||
register_widget('\AKSRT\Widgets\WidgetPaidPosts');
|
||||
register_widget('\AKSRT\Widgets\WidgetPostPayBox');
|
||||
register_widget('\AKSRT\Widgets\WidgetAuthorCard');
|
||||
register_widget('\AKSRT\Widgets\WidgetOnelinePosts');
|
||||
@@ -91,8 +78,6 @@ function aksrt_register_widgets() {
|
||||
register_widget('\AKSRT\Widgets\WidgetTermAggregation');
|
||||
register_widget('\AKSRT\Widgets\WidgetIconCoverCards');
|
||||
register_widget('\AKSRT\Widgets\WidgetBookmarkLinks');
|
||||
|
||||
aksrt_maybe_repair_sidebar_blog_widgets();
|
||||
}
|
||||
add_action('widgets_init', 'aksrt_register_widgets');
|
||||
|
||||
@@ -105,7 +90,6 @@ if (!function_exists('aksrt_widget_sidebar_area_ids')) {
|
||||
function aksrt_widget_sidebar_area_ids(): array {
|
||||
return [
|
||||
'home_sidebar',
|
||||
'sidebar-blog',
|
||||
'sidebar-article',
|
||||
'all_sidebar_top',
|
||||
'all_sidebar_bottom',
|
||||
@@ -192,7 +176,6 @@ if (!function_exists('aksrt_widget_allowed_sidebars')) {
|
||||
*/
|
||||
function aksrt_widget_allowed_sidebars(string $widget_base_id): array {
|
||||
$sidebar_only = [
|
||||
'aksrt_about',
|
||||
'aksrt_categories',
|
||||
'aksrt_tag_cloud',
|
||||
'aksrt_hot_posts',
|
||||
@@ -221,6 +204,10 @@ if (!function_exists('aksrt_widget_allowed_sidebars')) {
|
||||
)));
|
||||
}
|
||||
|
||||
if ($widget_base_id === 'aksrt_post_pay_box') {
|
||||
return ['sidebar-article'];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -280,7 +267,7 @@ if (!function_exists('aksrt_widget_render_location_help')) {
|
||||
*/
|
||||
function aksrt_widget_render_location_help(string $scope): void {
|
||||
$messages = [
|
||||
'sidebar' => __('仅限侧栏区域:首页侧栏、博客侧栏、文章侧栏、全站侧栏、分类/标签/搜索侧栏。', 'aksrt'),
|
||||
'sidebar' => __('仅限侧栏区域:首页侧栏、文章页侧边栏、全站侧栏、分类/标签/搜索侧栏。', 'aksrt'),
|
||||
'homepage' => __('仅限首页挂载区域:首页顶部/底部全宽、首页内容上方/下方。', 'aksrt'),
|
||||
'hybrid' => __('支持侧栏、首页和归档页挂载区域。', 'aksrt'),
|
||||
'archive' => __('仅限归档页挂载区域:分类页、标签页、搜索页的顶部/底部/侧栏。', 'aksrt'),
|
||||
@@ -412,92 +399,6 @@ if (!function_exists('aksrt_filter_widget_sidebar_assignments')) {
|
||||
}
|
||||
add_filter('pre_update_option_sidebars_widgets', 'aksrt_filter_widget_sidebar_assignments', 10, 2);
|
||||
|
||||
/**
|
||||
* Ensure widget instance config exists.
|
||||
*/
|
||||
function aksrt_ensure_widget_instance(string $option_name, int $number, array $defaults): void {
|
||||
$instances = get_option($option_name, []);
|
||||
|
||||
if (!is_array($instances)) {
|
||||
$instances = [];
|
||||
}
|
||||
|
||||
if (!isset($instances['_multiwidget'])) {
|
||||
$instances['_multiwidget'] = 1;
|
||||
}
|
||||
|
||||
if (empty($instances[$number]) || !is_array($instances[$number])) {
|
||||
$instances[$number] = $defaults;
|
||||
update_option($option_name, $instances);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Repair the sidebar-blog structure and seed default sidebar widgets on first install.
|
||||
*/
|
||||
function aksrt_maybe_repair_sidebar_blog_widgets(): void {
|
||||
$sidebars = get_option('sidebars_widgets', []);
|
||||
$seeded = (bool) get_option('aksrt_sidebar_blog_seeded', false);
|
||||
|
||||
if (!is_array($sidebars)) {
|
||||
$sidebars = [];
|
||||
}
|
||||
|
||||
$current = $sidebars['sidebar-blog'] ?? null;
|
||||
$needs_repair = false;
|
||||
$normalized = [];
|
||||
|
||||
if (is_array($current)) {
|
||||
foreach ($current as $item) {
|
||||
if (is_array($item)) {
|
||||
$needs_repair = true;
|
||||
}
|
||||
|
||||
if (is_string($item) && $item !== '') {
|
||||
$normalized[] = $item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($needs_repair) {
|
||||
$sidebars['sidebar-blog'] = array_values($normalized);
|
||||
update_option('sidebars_widgets', $sidebars);
|
||||
update_option('aksrt_sidebar_blog_seeded', true);
|
||||
return;
|
||||
}
|
||||
|
||||
if ($current !== null) {
|
||||
if (!$seeded) {
|
||||
update_option('aksrt_sidebar_blog_seeded', true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ($seeded) {
|
||||
return;
|
||||
}
|
||||
|
||||
aksrt_ensure_widget_instance('widget_aksrt_about', 2, [
|
||||
'title' => '',
|
||||
]);
|
||||
aksrt_ensure_widget_instance('widget_aksrt_categories', 2, [
|
||||
'title' => __('分类目录', 'aksrt'),
|
||||
'count' => 8,
|
||||
]);
|
||||
aksrt_ensure_widget_instance('widget_aksrt_tag_cloud', 2, [
|
||||
'title' => __('标签云', 'aksrt'),
|
||||
'count' => 15,
|
||||
]);
|
||||
|
||||
$sidebars['sidebar-blog'] = [
|
||||
'aksrt_about-2',
|
||||
'aksrt_categories-2',
|
||||
'aksrt_tag_cloud-2',
|
||||
];
|
||||
update_option('sidebars_widgets', $sidebars);
|
||||
update_option('aksrt_sidebar_blog_seeded', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a block editor collection for AKSRT widgets.
|
||||
*/
|
||||
@@ -527,7 +428,6 @@ add_action('enqueue_block_editor_assets', function () {
|
||||
*/
|
||||
add_filter('block_type_metadata', function ($metadata) {
|
||||
$aksrt_widgets = [
|
||||
'aksrt_about',
|
||||
'aksrt_categories',
|
||||
'aksrt_tag_cloud',
|
||||
];
|
||||
@@ -544,7 +444,6 @@ add_filter('block_type_metadata', function ($metadata) {
|
||||
*/
|
||||
add_action('in_widget_form', function (WP_Widget $widget): void {
|
||||
$map = [
|
||||
'aksrt_about' => 'sidebar',
|
||||
'aksrt_categories' => 'sidebar',
|
||||
'aksrt_tag_cloud' => 'sidebar',
|
||||
'aksrt_hot_posts' => 'sidebar',
|
||||
|
||||
@@ -24,7 +24,7 @@ get_header();
|
||||
// Visibility is driven by body.site-layout-* (aksrt_is_show_sidebar), not widget presence.
|
||||
$has_sidebar = function_exists('aksrt_is_show_sidebar')
|
||||
? aksrt_is_show_sidebar()
|
||||
: is_active_sidebar('sidebar-blog');
|
||||
: is_active_sidebar('home_sidebar');
|
||||
?>
|
||||
<main id="main-content" class="site-main container" tabindex="-1">
|
||||
<div class="content-wrap">
|
||||
|
||||
+1
-1
@@ -114,7 +114,7 @@ $search_sort_options = [
|
||||
|
||||
$has_sidebar = function_exists('aksrt_archive_has_sidebar')
|
||||
? aksrt_archive_has_sidebar('search_sidebar', !empty($other_page_settings['page_search_sidebar']))
|
||||
: (!empty($other_page_settings['page_search_sidebar']) && (is_active_sidebar('search_sidebar') || is_active_sidebar('sidebar-blog')));
|
||||
: (!empty($other_page_settings['page_search_sidebar']) && is_active_sidebar('search_sidebar'));
|
||||
|
||||
if (function_exists('aksrt_archive_open_shell')) {
|
||||
aksrt_archive_open_shell([
|
||||
|
||||
+1
-3
@@ -17,7 +17,7 @@ if (wp_is_mobile()) {
|
||||
|
||||
$sidebar_id = function_exists('aksrt_resolve_sidebar_id')
|
||||
? aksrt_resolve_sidebar_id()
|
||||
: 'sidebar-blog';
|
||||
: 'home_sidebar';
|
||||
?>
|
||||
|
||||
<div class="sidebar" role="complementary" aria-label="<?php esc_attr_e('侧栏', 'aksrt'); ?>">
|
||||
@@ -29,8 +29,6 @@ $sidebar_id = function_exists('aksrt_resolve_sidebar_id')
|
||||
<?php dynamic_sidebar($sidebar_id); ?>
|
||||
<?php elseif ((is_home() || is_front_page()) && is_active_sidebar('home_sidebar')) : ?>
|
||||
<?php dynamic_sidebar('home_sidebar'); ?>
|
||||
<?php elseif ((is_home() || is_front_page()) && is_active_sidebar('sidebar-blog')) : ?>
|
||||
<?php dynamic_sidebar('sidebar-blog'); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (is_active_sidebar('all_sidebar_bottom')) : ?>
|
||||
|
||||
@@ -380,17 +380,6 @@ trait ScriptsPart1c {
|
||||
}
|
||||
|
||||
// Frontend translate (默认关)
|
||||
$translate_js = get_template_directory() . '/assets/js/frontend-translate.js';
|
||||
if (file_exists($translate_js)) {
|
||||
wp_enqueue_script(
|
||||
'aksrt-frontend-translate',
|
||||
$base_uri . '/assets/js/frontend-translate.js',
|
||||
[],
|
||||
(string) filemtime($translate_js),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
// Content protection (copy notice / right-click / F12)
|
||||
$content_protect_js = get_template_directory() . '/assets/js/content-protect.js';
|
||||
if (file_exists($content_protect_js)) {
|
||||
|
||||
+25
-56
@@ -58,7 +58,7 @@ trait SetupPart1 {
|
||||
|
||||
register_sidebar([
|
||||
'id' => 'all_footer',
|
||||
'name' => __('全站页脚小工具', 'aksrt'),
|
||||
'name' => __('全站 - 页脚小工具', 'aksrt'),
|
||||
'description' => __('显示在页脚内部的统一小工具区域。', 'aksrt'),
|
||||
'before_widget' => $card_wrapper_before,
|
||||
'after_widget' => '</div>',
|
||||
@@ -68,7 +68,7 @@ trait SetupPart1 {
|
||||
|
||||
register_sidebar([
|
||||
'id' => 'mobile_nav_fluid',
|
||||
'name' => __('移动端菜单底部', 'aksrt'),
|
||||
'name' => __('移动端 - 菜单底部', 'aksrt'),
|
||||
'description' => __('显示在移动端抽屉菜单底部。', 'aksrt'),
|
||||
'before_widget' => $card_wrapper_before,
|
||||
'after_widget' => '</div>',
|
||||
@@ -118,7 +118,7 @@ trait SetupPart1 {
|
||||
|
||||
register_sidebar([
|
||||
'id' => 'home_sidebar',
|
||||
'name' => __('首页侧栏', 'aksrt'),
|
||||
'name' => __('首页 - 侧边栏', 'aksrt'),
|
||||
'description' => __('显示在首页右侧栏。', 'aksrt'),
|
||||
'before_widget' => $card_wrapper_before,
|
||||
'after_widget' => '</div>',
|
||||
@@ -126,21 +126,10 @@ trait SetupPart1 {
|
||||
'after_title' => $title_after,
|
||||
]);
|
||||
|
||||
// 新前端使用的侧边栏 (sidebar.php)
|
||||
register_sidebar([
|
||||
'id' => 'sidebar-blog',
|
||||
'name' => __('博客侧边栏', 'aksrt'),
|
||||
'description' => __('显示在首页、归档等列表页右侧(新前端)。', 'aksrt'),
|
||||
'before_widget' => $card_wrapper_before,
|
||||
'after_widget' => '</div>',
|
||||
'before_title' => $title_before,
|
||||
'after_title' => $title_after,
|
||||
]);
|
||||
|
||||
// 文章详情页侧栏
|
||||
register_sidebar([
|
||||
'id' => 'sidebar-article',
|
||||
'name' => __('文章侧边栏', 'aksrt'),
|
||||
'name' => __('文章页 - 侧边栏', 'aksrt'),
|
||||
'description' => __('显示在文章详情页右侧。', 'aksrt'),
|
||||
'before_widget' => $card_wrapper_before,
|
||||
'after_widget' => '</div>',
|
||||
@@ -151,7 +140,7 @@ trait SetupPart1 {
|
||||
// ===== 文章页 Widget 区域(模板zibll 多位置) =====
|
||||
register_sidebar([
|
||||
'id' => 'single_top_fluid',
|
||||
'name' => __('文章页 · 顶部全宽', 'aksrt'),
|
||||
'name' => __('文章页 - 顶部全宽', 'aksrt'),
|
||||
'description' => __('文章页顶部全宽区域(在封面/面包屑之上)。', 'aksrt'),
|
||||
'before_widget' => $fluid_wrapper_before,
|
||||
'after_widget' => '</div>',
|
||||
@@ -161,7 +150,7 @@ trait SetupPart1 {
|
||||
|
||||
register_sidebar([
|
||||
'id' => 'single_top_content',
|
||||
'name' => __('文章页 · 内容上方', 'aksrt'),
|
||||
'name' => __('文章页 - 内容上方', 'aksrt'),
|
||||
'description' => __('文章主体卡片上方(在内容列内)。', 'aksrt'),
|
||||
'before_widget' => $card_wrapper_before,
|
||||
'after_widget' => '</div>',
|
||||
@@ -171,7 +160,7 @@ trait SetupPart1 {
|
||||
|
||||
register_sidebar([
|
||||
'id' => 'single_bottom_content',
|
||||
'name' => __('文章页 · 内容下方', 'aksrt'),
|
||||
'name' => __('文章页 - 内容下方', 'aksrt'),
|
||||
'description' => __('评论区下方(在内容列内)。', 'aksrt'),
|
||||
'before_widget' => $card_wrapper_before,
|
||||
'after_widget' => '</div>',
|
||||
@@ -181,7 +170,7 @@ trait SetupPart1 {
|
||||
|
||||
register_sidebar([
|
||||
'id' => 'single_bottom_fluid',
|
||||
'name' => __('文章页 · 底部全宽', 'aksrt'),
|
||||
'name' => __('文章页 - 底部全宽', 'aksrt'),
|
||||
'description' => __('文章页底部全宽区域(在页脚之上)。', 'aksrt'),
|
||||
'before_widget' => $fluid_wrapper_before,
|
||||
'after_widget' => '</div>',
|
||||
@@ -189,30 +178,10 @@ trait SetupPart1 {
|
||||
'after_title' => $title_after,
|
||||
]);
|
||||
|
||||
register_sidebar([
|
||||
'id' => 'sidebar-footer-1',
|
||||
'name' => __('页脚栏 1', 'aksrt'),
|
||||
'description' => '',
|
||||
'before_widget' => '<div class="footer-widget %2$s">',
|
||||
'after_widget' => '</div>',
|
||||
'before_title' => '<h4 class="footer-widget-title">',
|
||||
'after_title' => '</h4>',
|
||||
]);
|
||||
|
||||
register_sidebar([
|
||||
'id' => 'sidebar-footer-2',
|
||||
'name' => __('页脚栏 2', 'aksrt'),
|
||||
'description' => '',
|
||||
'before_widget' => '<div class="footer-widget %2$s">',
|
||||
'after_widget' => '</div>',
|
||||
'before_title' => '<h4 class="footer-widget-title">',
|
||||
'after_title' => '</h4>',
|
||||
]);
|
||||
|
||||
// ===== 分类页 Widget 区域 =====
|
||||
register_sidebar([
|
||||
'id' => 'cat_top_fluid',
|
||||
'name' => __('分类页 · 顶部全宽', 'aksrt'),
|
||||
'name' => __('分类页 - 顶部全宽', 'aksrt'),
|
||||
'description' => __('分类归档页顶部全宽区域。', 'aksrt'),
|
||||
'before_widget' => $fluid_wrapper_before,
|
||||
'after_widget' => '</div>',
|
||||
@@ -222,7 +191,7 @@ trait SetupPart1 {
|
||||
|
||||
register_sidebar([
|
||||
'id' => 'cat_top_content',
|
||||
'name' => __('分类页 · 内容上方', 'aksrt'),
|
||||
'name' => __('分类页 - 内容上方', 'aksrt'),
|
||||
'description' => __('分类归档页内容区上方。', 'aksrt'),
|
||||
'before_widget' => $card_wrapper_before,
|
||||
'after_widget' => '</div>',
|
||||
@@ -232,7 +201,7 @@ trait SetupPart1 {
|
||||
|
||||
register_sidebar([
|
||||
'id' => 'cat_bottom_content',
|
||||
'name' => __('分类页 · 内容下方', 'aksrt'),
|
||||
'name' => __('分类页 - 内容下方', 'aksrt'),
|
||||
'description' => __('分类归档页内容区下方。', 'aksrt'),
|
||||
'before_widget' => $card_wrapper_before,
|
||||
'after_widget' => '</div>',
|
||||
@@ -242,7 +211,7 @@ trait SetupPart1 {
|
||||
|
||||
register_sidebar([
|
||||
'id' => 'cat_bottom_fluid',
|
||||
'name' => __('分类页 · 底部全宽', 'aksrt'),
|
||||
'name' => __('分类页 - 底部全宽', 'aksrt'),
|
||||
'description' => __('分类归档页底部全宽区域。', 'aksrt'),
|
||||
'before_widget' => $fluid_wrapper_before,
|
||||
'after_widget' => '</div>',
|
||||
@@ -252,7 +221,7 @@ trait SetupPart1 {
|
||||
|
||||
register_sidebar([
|
||||
'id' => 'cat_sidebar',
|
||||
'name' => __('分类页 · 侧栏', 'aksrt'),
|
||||
'name' => __('分类页 - 侧栏', 'aksrt'),
|
||||
'description' => __('分类归档页侧栏区域。', 'aksrt'),
|
||||
'before_widget' => $card_wrapper_before,
|
||||
'after_widget' => '</div>',
|
||||
@@ -263,7 +232,7 @@ trait SetupPart1 {
|
||||
// ===== 标签页 Widget 区域 =====
|
||||
register_sidebar([
|
||||
'id' => 'tag_top_fluid',
|
||||
'name' => __('标签页 · 顶部全宽', 'aksrt'),
|
||||
'name' => __('标签页 - 顶部全宽', 'aksrt'),
|
||||
'description' => __('标签归档页顶部全宽区域。', 'aksrt'),
|
||||
'before_widget' => $fluid_wrapper_before,
|
||||
'after_widget' => '</div>',
|
||||
@@ -273,7 +242,7 @@ trait SetupPart1 {
|
||||
|
||||
register_sidebar([
|
||||
'id' => 'tag_top_content',
|
||||
'name' => __('标签页 · 内容上方', 'aksrt'),
|
||||
'name' => __('标签页 - 内容上方', 'aksrt'),
|
||||
'description' => __('标签归档页内容区上方。', 'aksrt'),
|
||||
'before_widget' => $card_wrapper_before,
|
||||
'after_widget' => '</div>',
|
||||
@@ -283,7 +252,7 @@ trait SetupPart1 {
|
||||
|
||||
register_sidebar([
|
||||
'id' => 'tag_bottom_content',
|
||||
'name' => __('标签页 · 内容下方', 'aksrt'),
|
||||
'name' => __('标签页 - 内容下方', 'aksrt'),
|
||||
'description' => __('标签归档页内容区下方。', 'aksrt'),
|
||||
'before_widget' => $card_wrapper_before,
|
||||
'after_widget' => '</div>',
|
||||
@@ -293,7 +262,7 @@ trait SetupPart1 {
|
||||
|
||||
register_sidebar([
|
||||
'id' => 'tag_bottom_fluid',
|
||||
'name' => __('标签页 · 底部全宽', 'aksrt'),
|
||||
'name' => __('标签页 - 底部全宽', 'aksrt'),
|
||||
'description' => __('标签归档页底部全宽区域。', 'aksrt'),
|
||||
'before_widget' => $fluid_wrapper_before,
|
||||
'after_widget' => '</div>',
|
||||
@@ -303,7 +272,7 @@ trait SetupPart1 {
|
||||
|
||||
register_sidebar([
|
||||
'id' => 'tag_sidebar',
|
||||
'name' => __('标签页 · 侧栏', 'aksrt'),
|
||||
'name' => __('标签页 - 侧栏', 'aksrt'),
|
||||
'description' => __('标签归档页侧栏区域。', 'aksrt'),
|
||||
'before_widget' => $card_wrapper_before,
|
||||
'after_widget' => '</div>',
|
||||
@@ -314,7 +283,7 @@ trait SetupPart1 {
|
||||
// ===== 搜索页 Widget 区域 =====
|
||||
register_sidebar([
|
||||
'id' => 'search_top_fluid',
|
||||
'name' => __('搜索页 · 顶部全宽', 'aksrt'),
|
||||
'name' => __('搜索页 - 顶部全宽', 'aksrt'),
|
||||
'description' => __('搜索结果页顶部全宽区域。', 'aksrt'),
|
||||
'before_widget' => $fluid_wrapper_before,
|
||||
'after_widget' => '</div>',
|
||||
@@ -324,7 +293,7 @@ trait SetupPart1 {
|
||||
|
||||
register_sidebar([
|
||||
'id' => 'search_top_content',
|
||||
'name' => __('搜索页 · 内容上方', 'aksrt'),
|
||||
'name' => __('搜索页 - 内容上方', 'aksrt'),
|
||||
'description' => __('搜索结果页内容区上方。', 'aksrt'),
|
||||
'before_widget' => $card_wrapper_before,
|
||||
'after_widget' => '</div>',
|
||||
@@ -334,7 +303,7 @@ trait SetupPart1 {
|
||||
|
||||
register_sidebar([
|
||||
'id' => 'search_bottom_content',
|
||||
'name' => __('搜索页 · 内容下方', 'aksrt'),
|
||||
'name' => __('搜索页 - 内容下方', 'aksrt'),
|
||||
'description' => __('搜索结果页内容区下方。', 'aksrt'),
|
||||
'before_widget' => $card_wrapper_before,
|
||||
'after_widget' => '</div>',
|
||||
@@ -344,7 +313,7 @@ trait SetupPart1 {
|
||||
|
||||
register_sidebar([
|
||||
'id' => 'search_bottom_fluid',
|
||||
'name' => __('搜索页 · 底部全宽', 'aksrt'),
|
||||
'name' => __('搜索页 - 底部全宽', 'aksrt'),
|
||||
'description' => __('搜索结果页底部全宽区域。', 'aksrt'),
|
||||
'before_widget' => $fluid_wrapper_before,
|
||||
'after_widget' => '</div>',
|
||||
@@ -354,7 +323,7 @@ trait SetupPart1 {
|
||||
|
||||
register_sidebar([
|
||||
'id' => 'search_sidebar',
|
||||
'name' => __('搜索页 · 侧栏', 'aksrt'),
|
||||
'name' => __('搜索页 - 侧栏', 'aksrt'),
|
||||
'description' => __('搜索结果页侧栏区域。', 'aksrt'),
|
||||
'before_widget' => $card_wrapper_before,
|
||||
'after_widget' => '</div>',
|
||||
@@ -364,7 +333,7 @@ trait SetupPart1 {
|
||||
|
||||
register_sidebar([
|
||||
'id' => 'archive_bottom_fluid',
|
||||
'name' => __('归档页 · 底部全宽', 'aksrt'),
|
||||
'name' => __('归档页 - 底部全宽', 'aksrt'),
|
||||
'description' => __('分类/标签/归档页底部全宽区域。', 'aksrt'),
|
||||
'before_widget' => $fluid_wrapper_before,
|
||||
'after_widget' => '</div>',
|
||||
@@ -374,7 +343,7 @@ trait SetupPart1 {
|
||||
|
||||
register_sidebar([
|
||||
'id' => 'author-sidebar',
|
||||
'name' => __('作者页 · 侧栏', 'aksrt'),
|
||||
'name' => __('作者页 - 侧栏', 'aksrt'),
|
||||
'description' => __('作者归档页侧栏区域。', 'aksrt'),
|
||||
'before_widget' => $card_wrapper_before,
|
||||
'after_widget' => '</div>',
|
||||
|
||||
@@ -27,7 +27,6 @@ class WidgetAjax {
|
||||
$routes = [
|
||||
'/widgets/posts' => 'rest_main_posts',
|
||||
'/widgets/tab-posts' => 'rest_tab_posts',
|
||||
'/widgets/paid-posts' => 'rest_paid_posts',
|
||||
'/widgets/category-tabs' => 'rest_category_tabs',
|
||||
'/widgets/category-block' => 'rest_category_block',
|
||||
'/widgets/category-recommend' => 'rest_category_recommend',
|
||||
@@ -123,26 +122,6 @@ class WidgetAjax {
|
||||
], 200);
|
||||
}
|
||||
|
||||
public function rest_paid_posts(\WP_REST_Request $request) {
|
||||
if (!class_exists('\AKSRT\Widgets\WidgetPaidPosts')) {
|
||||
return $this->fail();
|
||||
}
|
||||
$paginate = sanitize_key((string) ($request->get_param('paginate') ?? 'ajax'));
|
||||
if (!in_array($paginate, ['none', 'ajax', 'number'], true)) {
|
||||
$paginate = 'ajax';
|
||||
}
|
||||
$instance = [
|
||||
'count' => max(1, min(20, (int) ($request->get_param('count') ?? 6))),
|
||||
'orderby' => sanitize_key((string) ($request->get_param('orderby') ?? 'date')),
|
||||
'cat' => max(0, (int) ($request->get_param('cat') ?? 0)),
|
||||
'limit_day' => max(0, (int) ($request->get_param('limit_day') ?? 0)),
|
||||
'style' => sanitize_key((string) ($request->get_param('style') ?? 'mini')),
|
||||
'paginate' => $paginate,
|
||||
];
|
||||
$page = max(1, (int) ($request->get_param('page') ?? $request->get_param('paged') ?? 1));
|
||||
return $this->payload(\AKSRT\Widgets\WidgetPaidPosts::query_payload($instance, $page), $page);
|
||||
}
|
||||
|
||||
public function rest_category_tabs(\WP_REST_Request $request) {
|
||||
$cat = max(0, (int) ($request->get_param('cat') ?? 0));
|
||||
$count = max(1, min(12, (int) ($request->get_param('count') ?? 6)));
|
||||
|
||||
@@ -1,383 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
namespace AKSRT\Widgets;
|
||||
|
||||
use AKSRT\Widgets\Traits\CommonWidgetFields;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
class AboutWidget extends \WP_Widget {
|
||||
use CommonWidgetFields;
|
||||
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
'aksrt_about',
|
||||
esc_html__('AKSRT 用户中心', 'aksrt'),
|
||||
[
|
||||
'description' => esc_html__('登录状态面板:未登录显示登录/注册按钮,已登录显示用户信息与快捷入口', 'aksrt'),
|
||||
'customize_selective_refresh' => true,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 前端渲染
|
||||
*/
|
||||
public function widget($args, $instance): void {
|
||||
echo $args['before_widget'];
|
||||
|
||||
$title = apply_filters('widget_title', $instance['title'] ?? '', $instance, $this->id_base);
|
||||
if ($title) {
|
||||
echo $args['before_title'] . esc_html($title) . $args['after_title'];
|
||||
}
|
||||
|
||||
// 判断登录状态
|
||||
if (is_user_logged_in()) {
|
||||
$this->render_logged_in();
|
||||
} else {
|
||||
$this->render_logged_out($instance);
|
||||
}
|
||||
|
||||
// 输出内联样式 & JS(仅在本 widget 实例加载时注入一次)
|
||||
$this->inline_assets();
|
||||
|
||||
echo $args['after_widget'];
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 已登录状态渲染(Zibll 风格用户卡片)
|
||||
// ==========================================
|
||||
private function render_logged_in(): void {
|
||||
$u = wp_get_current_user();
|
||||
$avatar = get_avatar_url($u->ID, ['size' => 160]);
|
||||
$profile = aksrt_get_user_center_url();
|
||||
$logout = aksrt_get_logout_url(home_url('/'));
|
||||
|
||||
// 封面图:用户在用户中心上传的背景图 > 默认背景图
|
||||
$cover = get_user_meta($u->ID, 'aksrt_background_image', true);
|
||||
if (! $cover) {
|
||||
$cover = get_theme_file_uri('assets/img/person_background.png');
|
||||
}
|
||||
?>
|
||||
<div class="aksrt-user-card">
|
||||
<div class="aksrt-user-card__cover" style="padding-bottom: 50%;">
|
||||
<img class="aksrt-user-card__cover-img aksrt-lazy lazyload" data-src="<?php echo esc_url($cover); ?>" src="<?php echo esc_url(get_template_directory_uri() . '/assets/img/placeholder.svg'); ?>" alt="" loading="lazy">
|
||||
</div>
|
||||
|
||||
<div class="aksrt-user-card__content">
|
||||
<div class="aksrt-user-card__avatar">
|
||||
<img class="aksrt-lazy lazyload" data-src="<?php echo esc_url($avatar); ?>" src="<?php echo esc_url(get_template_directory_uri() . '/assets/img/placeholder.svg'); ?>" alt="" width="80" height="80" loading="lazy">
|
||||
</div>
|
||||
|
||||
<div class="aksrt-user-card__info">
|
||||
<div class="aksrt-user-card__name"><?php if (function_exists('aksrt_get_user_name')) { echo aksrt_get_user_name(['id' => (int) $u->ID, 'class' => 'flex ac', 'follow' => false, 'medal' => true, 'auth' => true, 'level' => true]); } else { echo esc_html($u->display_name); } ?></div>
|
||||
<div class="aksrt-user-card__desc"><?php $bio = trim((string) get_user_meta($u->ID, 'description', true)); echo esc_html($bio !== '' ? $bio : $u->user_email); ?></div>
|
||||
</div>
|
||||
|
||||
<div class="aksrt-user-card__btns">
|
||||
<a class="aksrt-user-card__btn aksrt-user-card__btn--primary" href="<?php echo esc_url($profile); ?>">
|
||||
<i class="fas fa-user" aria-hidden="true"></i> <?php esc_html_e('个人主页', 'aksrt'); ?>
|
||||
</a>
|
||||
<a class="aksrt-user-card__btn aksrt-user-card__btn--outline" href="<?php echo esc_url($logout); ?>">
|
||||
<i class="fas fa-sign-out-alt" aria-hidden="true"></i> <?php esc_html_e('退出', 'aksrt'); ?>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 未登录状态渲染(Zibll 风格用户卡片)
|
||||
// ==========================================
|
||||
private function render_logged_out(array $instance = []): void {
|
||||
$login_url = aksrt_get_login_url();
|
||||
$register_url = aksrt_get_register_url();
|
||||
|
||||
// 封面图:后台配置 > 默认
|
||||
$cover = ! empty($instance['cover']) ? $instance['cover'] : get_theme_file_uri('assets/img/widgets-original.png');
|
||||
$avatar = get_avatar_url(0, ['size' => 160, 'default' => 'mystery']);
|
||||
|
||||
$oauth_html = '';
|
||||
if (class_exists('\AKSRT\Auth\OAuth')) {
|
||||
$oauth = \AKSRT\Auth\OAuth::get_instance();
|
||||
if (method_exists($oauth, 'render_social_buttons')) {
|
||||
$oauth_html = (string) $oauth->render_social_buttons(__('社交账号登录', 'aksrt'));
|
||||
} elseif (method_exists($oauth, 'get_login_buttons')) {
|
||||
$oauth_html = (string) $oauth->get_login_buttons();
|
||||
}
|
||||
}
|
||||
?>
|
||||
<div class="aksrt-user-card">
|
||||
<div class="aksrt-user-card__cover" style="padding-bottom: 50%;">
|
||||
<?php if ($cover) : ?>
|
||||
<img class="aksrt-user-card__cover-img aksrt-lazy lazyload" data-src="<?php echo esc_url($cover); ?>" src="<?php echo esc_url(get_template_directory_uri() . '/assets/img/placeholder.svg'); ?>" alt="" loading="lazy">
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<div class="aksrt-user-card__content">
|
||||
<div class="aksrt-user-card__avatar">
|
||||
<img class="aksrt-lazy lazyload" data-src="<?php echo esc_url($avatar); ?>" src="<?php echo esc_url(get_template_directory_uri() . '/assets/img/placeholder.svg'); ?>" alt="" width="80" height="80" loading="lazy">
|
||||
</div>
|
||||
|
||||
<div class="aksrt-user-card__info">
|
||||
<div class="aksrt-user-card__desc"><?php esc_html_e('Hi! 请登录', 'aksrt'); ?></div>
|
||||
</div>
|
||||
|
||||
<div class="aksrt-user-card__btns">
|
||||
<a class="aksrt-user-card__btn aksrt-user-card__btn--primary signin-loader" href="<?php echo esc_url($login_url); ?>"><?php esc_html_e('登 录', 'aksrt'); ?></a>
|
||||
<?php if (get_option('users_can_register')) : ?>
|
||||
<a class="aksrt-user-card__btn aksrt-user-card__btn--outline signup-loader" href="<?php echo esc_url($register_url); ?>"><?php esc_html_e('注 册', 'aksrt'); ?></a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php if ($oauth_html) : ?>
|
||||
<div class="aksrt-user-card__oauth">
|
||||
<div class="aksrt-user-card__divider"><?php esc_html_e('社交账号登录', 'aksrt'); ?></div>
|
||||
<div class="aksrt-user-card__social-icons">
|
||||
<?php echo $oauth_html; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 内联样式(Zibll 风格用户卡片,只输出一次)
|
||||
// ==========================================
|
||||
private function inline_assets(): void {
|
||||
if (did_action('aksrt_user_card_assets')) {
|
||||
return;
|
||||
}
|
||||
do_action('aksrt_user_card_assets');
|
||||
?>
|
||||
<style>
|
||||
/* ========== AKSRT 用户卡片(Zibll 风格) ========== */
|
||||
|
||||
/*
|
||||
* 消除"卡片套卡片":
|
||||
* 外层 .widget-card 提供 background/shadow/border-radius/padding:15px
|
||||
* 内层 .aksrt-user-card 用负 margin 把封面图扩展到外层边缘,
|
||||
* 使封面图与外层卡片融为一体,不再有 15px 间距。
|
||||
*/
|
||||
.widget-card > .aksrt-user-card {
|
||||
margin: -15px -15px 0;
|
||||
overflow: hidden;
|
||||
border-radius: var(--radius-card, 8px) var(--radius-card, 8px) 0 0;
|
||||
}
|
||||
|
||||
/* 有标题时:取消标题下方的 margin-bottom,因为卡片已无额外间距 */
|
||||
.widget.aksrt_about.widget-card > .widget-title + .aksrt-user-card {
|
||||
margin-top: -5px;
|
||||
}
|
||||
|
||||
/* ---- 封面图 ---- */
|
||||
.aksrt-user-card__cover {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background: linear-gradient(135deg, var(--color-primary), var(--color-primary-dark, #4f46e5));
|
||||
}
|
||||
.aksrt-user-card__cover-img {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
/* ---- 内容区 — 悬浮在封面上方 ---- */
|
||||
.aksrt-user-card__content {
|
||||
padding: 15px;
|
||||
margin-top: -55px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* ---- 头像(绝对居中) ---- */
|
||||
.aksrt-user-card__avatar {
|
||||
text-align: center;
|
||||
line-height: 0;
|
||||
}
|
||||
.aksrt-user-card__avatar img {
|
||||
display: block;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
margin: 0 auto;
|
||||
border-radius: 50%;
|
||||
border: 3px solid var(--color-primary);
|
||||
object-fit: cover;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
/* ---- 用户信息 ---- */
|
||||
.aksrt-user-card__info {
|
||||
margin-top: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
.aksrt-user-card__name {
|
||||
font-size: 1.1em;
|
||||
font-weight: bold;
|
||||
color: var(--color-ink, var(--color-text));
|
||||
}
|
||||
.aksrt-user-card__desc {
|
||||
font-size: 13px;
|
||||
color: var(--color-text-muted, #9ca3af);
|
||||
margin-top: 4px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* ---- 按钮组 ---- */
|
||||
.aksrt-user-card__btns {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
justify-content: center;
|
||||
margin-top: 16px;
|
||||
}
|
||||
.aksrt-user-card__btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
padding: 8px 20px;
|
||||
border-radius: 20px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
transition: opacity 0.2s ease, transform 0.15s ease;
|
||||
flex: 1;
|
||||
max-width: 140px;
|
||||
}
|
||||
.aksrt-user-card__btn:hover {
|
||||
opacity: 0.88;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
.aksrt-user-card__btn:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
.aksrt-user-card__btn--primary {
|
||||
background: var(--color-primary);
|
||||
color: #fff;
|
||||
}
|
||||
.aksrt-user-card__btn--outline {
|
||||
background: var(--color-primary-softer, rgba(136,171,218,0.05));
|
||||
color: var(--color-primary);
|
||||
border: 1px solid var(--color-primary-50, rgba(136,171,218,0.12));
|
||||
}
|
||||
|
||||
/* ---- OAuth 社交登录 ---- */
|
||||
.aksrt-user-card__oauth {
|
||||
margin-top: 12px;
|
||||
}
|
||||
.aksrt-user-card__divider {
|
||||
font-size: 12px;
|
||||
color: var(--color-text-muted, #9ca3af);
|
||||
margin-bottom: 10px;
|
||||
position: relative;
|
||||
}
|
||||
.aksrt-user-card__divider::before,
|
||||
.aksrt-user-card__divider::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
width: 28%;
|
||||
height: 1px;
|
||||
background: var(--color-border, #e5e7eb);
|
||||
}
|
||||
.aksrt-user-card__divider::before { left: 0; }
|
||||
.aksrt-user-card__divider::after { right: 0; }
|
||||
|
||||
.aksrt-user-card__social-icons {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
justify-content: center;
|
||||
}
|
||||
.aksrt-user-card__social-icons a {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 50%;
|
||||
font-size: 15px;
|
||||
color: var(--color-text-muted, #6b7280);
|
||||
background: var(--color-bg-tertiary, #f3f4f6);
|
||||
text-decoration: none;
|
||||
transition: background 0.18s ease, color 0.18s ease;
|
||||
}
|
||||
.aksrt-user-card__social-icons a:hover {
|
||||
background: var(--color-primary);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* ---- 暗色适配 ---- */
|
||||
html.dark .aksrt-user-card__avatar img,
|
||||
body.theme-dark .aksrt-user-card__avatar img {
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
html.dark .aksrt-user-card__social-icons a,
|
||||
body.theme-dark .aksrt-user-card__social-icons a {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
html.dark .aksrt-user-card__social-icons a:hover,
|
||||
body.theme-dark .aksrt-user-card__social-icons a:hover {
|
||||
background: var(--color-primary);
|
||||
}
|
||||
|
||||
/* ---- 移动端 ---- */
|
||||
@media screen and (max-width: 480px) {
|
||||
.aksrt-user-card__content {
|
||||
margin-top: -45px;
|
||||
}
|
||||
.aksrt-user-card__avatar img {
|
||||
width: 68px;
|
||||
height: 68px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<?php
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// 后台表单
|
||||
// ==========================================
|
||||
public function form($instance): void {
|
||||
$title = $instance['title'] ?? '';
|
||||
$cover = $instance['cover'] ?? '';
|
||||
$default_cover = get_theme_file_uri('assets/img/widgets-original.png');
|
||||
?>
|
||||
<p>
|
||||
<label for="<?php echo $this->get_field_id('title'); ?>"><?php esc_html_e('标题(可选):', 'aksrt'); ?></label>
|
||||
<input class="widefat" id="<?php echo $this->get_field_id('title'); ?>"
|
||||
name="<?php echo $this->get_field_name('title'); ?>"
|
||||
type="text" value="<?php echo esc_attr($title); ?>">
|
||||
</p>
|
||||
<p>
|
||||
<label for="<?php echo $this->get_field_id('cover'); ?>"><?php esc_html_e('未登录背景图 URL:', 'aksrt'); ?></label>
|
||||
<input class="widefat" id="<?php echo $this->get_field_id('cover'); ?>"
|
||||
name="<?php echo $this->get_field_name('cover'); ?>"
|
||||
type="url" value="<?php echo esc_attr($cover); ?>"
|
||||
placeholder="<?php echo esc_attr($default_cover); ?>">
|
||||
</p>
|
||||
<p style="font-size:12px;color:#666;">
|
||||
<?php esc_html_e('留空则使用默认背景图。已登录用户可使用个人封面图。', 'aksrt'); ?>
|
||||
</p>
|
||||
<?php
|
||||
$this->render_common_form_section($instance);
|
||||
}
|
||||
|
||||
public function update($new_instance, $old_instance): array {
|
||||
$business = [
|
||||
'title' => sanitize_text_field($new_instance['title'] ?? ''),
|
||||
'cover' => esc_url_raw($new_instance['cover'] ?? ''),
|
||||
]; return $this->update_common_fields($new_instance, $old_instance, $business);
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ class WidgetAnnouncement extends \WP_Widget {
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
'aksrt_announcement',
|
||||
__('公告跑马灯', 'aksrt'),
|
||||
__('AKSRT 公告跑马灯', 'aksrt'),
|
||||
[
|
||||
'description' => __('滚动显示公告/通知(共享垂直 Swiper)。', 'aksrt'),
|
||||
'classname' => 'widget_aksrt_announcement',
|
||||
|
||||
@@ -18,7 +18,7 @@ class WidgetCoverCards extends \WP_Widget {
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
'aksrt_cover_cards',
|
||||
__('图文封面卡片', 'aksrt'),
|
||||
__('AKSRT 图文封面卡片', 'aksrt'),
|
||||
['description' => __('图片/视频封面 + 文字叠加的图文卡片(共享 graphic_card)。', 'aksrt')]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,230 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Feature intro with media tabs (zibll zib_widget_feature_intro lite).
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
namespace AKSRT\Widgets;
|
||||
|
||||
use AKSRT\Widgets\Traits\CommonWidgetFields;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
class WidgetFeatureIntro extends \WP_Widget {
|
||||
use CommonWidgetFields;
|
||||
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
'aksrt_feature_intro',
|
||||
esc_html__('AKSRT 功能介绍', 'aksrt'),
|
||||
[
|
||||
'description' => esc_html__('左文右图/视频的 TAB 功能介绍模块。', 'aksrt'),
|
||||
'classname' => 'widget_aksrt_feature_intro',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function widget($args, $instance): void {
|
||||
$slides = $this->normalize_slides($instance['slides'] ?? []);
|
||||
if (!$slides) {
|
||||
if (current_user_can('manage_options')) {
|
||||
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo function_exists('aksrt_get_widget_null')
|
||||
? aksrt_get_widget_null(__('请添加至少一个功能介绍面板', 'aksrt')) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
: '<p class="widget-empty">' . esc_html__('请添加至少一个功能介绍面板', 'aksrt') . '</p>';
|
||||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
return;
|
||||
}
|
||||
$heading = trim((string) ($instance['heading'] ?? ''));
|
||||
$desc = trim((string) ($instance['description'] ?? ''));
|
||||
$media_pos = (($instance['media_position'] ?? 'right') === 'left') ? 'left' : 'right';
|
||||
$chrome = !empty($instance['browser_chrome']) || !isset($instance['browser_chrome']);
|
||||
$ratio = max(30, min(100, (int) ($instance['media_ratio'] ?? 66)));
|
||||
$show_stat = !empty($instance['show_stat']);
|
||||
|
||||
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
$uid = 'aksrt-fi-' . esc_attr($this->id);
|
||||
|
||||
echo '<div class="aksrt-fi media-' . esc_attr($media_pos) . '" id="' . $uid . '" data-aksrt-feature-intro="1">';
|
||||
echo '<div class="aksrt-fi__text">';
|
||||
if ($heading !== '') {
|
||||
echo '<h2 class="aksrt-fi__title">' . esc_html($heading) . '</h2>';
|
||||
}
|
||||
if ($desc !== '') {
|
||||
echo '<div class="aksrt-fi__desc">' . wp_kses_post(wpautop($desc)) . '</div>';
|
||||
}
|
||||
if ($show_stat) {
|
||||
$sn = sanitize_text_field((string) ($instance['stat_number'] ?? ''));
|
||||
$ss = sanitize_text_field((string) ($instance['stat_suffix'] ?? '+'));
|
||||
$sl = sanitize_text_field((string) ($instance['stat_label'] ?? ''));
|
||||
if ($sn !== '') {
|
||||
$num_attr = is_numeric($sn) ? ' data-aksrt-countup data-target="' . esc_attr((string) (float) $sn) . '" data-duration="1600"' : '';
|
||||
echo '<div class="aksrt-fi__stat"><span class="aksrt-fi__stat-num"' . $num_attr . '>' . esc_html(is_numeric($sn) ? '0' : $sn) . '</span>';
|
||||
if ($ss !== '') {
|
||||
echo '<span class="aksrt-fi__stat-plus">' . esc_html($ss) . '</span>';
|
||||
}
|
||||
if ($sl !== '') {
|
||||
echo '<span class="aksrt-fi__stat-label">' . esc_html($sl) . '</span>';
|
||||
}
|
||||
echo '</div>';
|
||||
}
|
||||
}
|
||||
if (count($slides) > 1) {
|
||||
echo '<div class="aksrt-fi__tabs" role="tablist">';
|
||||
foreach ($slides as $i => $s) {
|
||||
$cls = $i === 0 ? ' is-active' : '';
|
||||
$label = $s['tab_label'] !== '' ? $s['tab_label'] : sprintf(__('面板 %d', 'aksrt'), $i + 1);
|
||||
echo '<button type="button" class="aksrt-fi__tab' . esc_attr($cls) . '" data-index="' . (int) $i . '" role="tab">' . esc_html($label) . '</button>';
|
||||
}
|
||||
echo '</div>';
|
||||
}
|
||||
echo '</div>';
|
||||
|
||||
echo '<div class="aksrt-fi__media">';
|
||||
foreach ($slides as $i => $s) {
|
||||
$cls = $i === 0 ? ' is-active' : '';
|
||||
echo '<div class="aksrt-fi__panel' . esc_attr($cls) . '" data-index="' . (int) $i . '">';
|
||||
$box_cls = $chrome ? 'aksrt-fi__browser' : 'aksrt-fi__browser is-plain';
|
||||
echo '<div class="' . esc_attr($box_cls) . '">';
|
||||
if ($chrome) {
|
||||
echo '<div class="aksrt-fi__browser-bar" aria-hidden="true"><span></span><span></span><span></span></div>';
|
||||
}
|
||||
echo '<div class="aksrt-fi__browser-body" style="padding-bottom:' . esc_attr((string) $ratio) . '%">';
|
||||
if ($s['media_type'] === 'video' && $s['video_url'] !== '') {
|
||||
$poster = $s['video_poster'] !== '' ? ' poster="' . esc_url($s['video_poster']) . '"' : '';
|
||||
$auto = $i === 0 ? ' autoplay' : '';
|
||||
if ($i === 0) {
|
||||
echo '<video class="aksrt-fi__video" src="' . esc_url($s['video_url']) . '"' . $poster . $auto . ' muted loop playsinline></video>';
|
||||
} else {
|
||||
echo '<video class="aksrt-fi__video" data-src="' . esc_url($s['video_url']) . '"' . $poster . ' muted loop playsinline></video>';
|
||||
}
|
||||
} elseif ($s['image'] !== '') {
|
||||
$ph = get_template_directory_uri() . '/assets/img/placeholder.svg';
|
||||
echo '<img class="aksrt-fi__img aksrt-lazy lazyload" data-src="' . esc_url($s['image']) . '" src="' . esc_url($ph) . '" alt="' . esc_attr($s['tab_label']) . '" loading="lazy" />';
|
||||
}
|
||||
echo '</div></div></div>';
|
||||
}
|
||||
echo '</div></div>';
|
||||
|
||||
|
||||
|
||||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $raw
|
||||
* @return array<int, array<string,string>>
|
||||
*/
|
||||
private function normalize_slides($raw): array {
|
||||
if (is_string($raw)) {
|
||||
$decoded = json_decode($raw, true);
|
||||
$raw = is_array($decoded) ? $decoded : [];
|
||||
}
|
||||
if (!is_array($raw)) {
|
||||
return [];
|
||||
}
|
||||
$out = [];
|
||||
foreach ($raw as $row) {
|
||||
if (!is_array($row)) {
|
||||
continue;
|
||||
}
|
||||
$type = sanitize_key((string) ($row['media_type'] ?? 'image'));
|
||||
if ($type !== 'video') {
|
||||
$type = 'image';
|
||||
}
|
||||
$image = esc_url_raw(trim((string) ($row['image'] ?? '')));
|
||||
$video = esc_url_raw(trim((string) ($row['video_url'] ?? '')));
|
||||
if ($type === 'video' && $video === '') {
|
||||
continue;
|
||||
}
|
||||
if ($type === 'image' && $image === '') {
|
||||
continue;
|
||||
}
|
||||
$out[] = [
|
||||
'tab_label' => sanitize_text_field((string) ($row['tab_label'] ?? '')),
|
||||
'media_type' => $type,
|
||||
'image' => $image,
|
||||
'video_url' => $video,
|
||||
'video_poster' => esc_url_raw(trim((string) ($row['video_poster'] ?? ''))),
|
||||
];
|
||||
if (count($out) >= 8) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
public function form($instance): void {
|
||||
$json = is_string($instance['slides'] ?? null)
|
||||
? (string) $instance['slides']
|
||||
: wp_json_encode($this->normalize_slides($instance['slides'] ?? []), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
?>
|
||||
<p>
|
||||
<label><?php esc_html_e('主标题', 'aksrt'); ?>
|
||||
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('heading')); ?>" value="<?php echo esc_attr((string) ($instance['heading'] ?? '')); ?>">
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label><?php esc_html_e('描述', 'aksrt'); ?>
|
||||
<textarea class="widefat" rows="3" name="<?php echo esc_attr($this->get_field_name('description')); ?>"><?php echo esc_textarea((string) ($instance['description'] ?? '')); ?></textarea>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label><?php esc_html_e('媒体 TAB JSON', 'aksrt'); ?>
|
||||
<textarea class="widefat" rows="6" name="<?php echo esc_attr($this->get_field_name('slides')); ?>" placeholder='[{"tab_label":"面板1","media_type":"image","image":"https://..."}]'><?php echo esc_textarea((string) $json); ?></textarea>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label><?php esc_html_e('媒体位置', 'aksrt'); ?>
|
||||
<select name="<?php echo esc_attr($this->get_field_name('media_position')); ?>">
|
||||
<option value="right" <?php selected(($instance['media_position'] ?? 'right'), 'right'); ?>><?php esc_html_e('右侧', 'aksrt'); ?></option>
|
||||
<option value="left" <?php selected(($instance['media_position'] ?? ''), 'left'); ?>><?php esc_html_e('左侧', 'aksrt'); ?></option>
|
||||
</select>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label><?php esc_html_e('媒体高度占比 %', 'aksrt'); ?>
|
||||
<input class="tiny-text" type="number" min="30" max="100" name="<?php echo esc_attr($this->get_field_name('media_ratio')); ?>" value="<?php echo esc_attr((string) ((int) ($instance['media_ratio'] ?? 66))); ?>">
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('browser_chrome')); ?>" value="1" <?php checked(!empty($instance['browser_chrome']) || !isset($instance['browser_chrome'])); ?>> <?php esc_html_e('浏览器外框', 'aksrt'); ?></label><br>
|
||||
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('show_stat')); ?>" value="1" <?php checked(!empty($instance['show_stat'])); ?>> <?php esc_html_e('显示统计数字', 'aksrt'); ?></label>
|
||||
</p>
|
||||
<p>
|
||||
<label><?php esc_html_e('统计数字', 'aksrt'); ?>
|
||||
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('stat_number')); ?>" value="<?php echo esc_attr((string) ($instance['stat_number'] ?? '')); ?>">
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label><?php esc_html_e('统计后缀', 'aksrt'); ?>
|
||||
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('stat_suffix')); ?>" value="<?php echo esc_attr((string) ($instance['stat_suffix'] ?? '+')); ?>">
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label><?php esc_html_e('统计标签', 'aksrt'); ?>
|
||||
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('stat_label')); ?>" value="<?php echo esc_attr((string) ($instance['stat_label'] ?? '')); ?>">
|
||||
</label>
|
||||
</p>
|
||||
<?php
|
||||
$this->render_common_form_section($instance);
|
||||
}
|
||||
|
||||
public function update($new_instance, $old_instance): array {
|
||||
$business = [
|
||||
'heading' => sanitize_text_field((string) ($new_instance['heading'] ?? '')),
|
||||
'description' => wp_kses_post((string) ($new_instance['description'] ?? '')),
|
||||
'slides' => $this->normalize_slides($new_instance['slides'] ?? []),
|
||||
'media_position' => ($new_instance['media_position'] ?? '') === 'left' ? 'left' : 'right',
|
||||
'media_ratio' => max(30, min(100, (int) ($new_instance['media_ratio'] ?? 66))),
|
||||
'browser_chrome' => !empty($new_instance['browser_chrome']) ? 1 : 0,
|
||||
'show_stat' => !empty($new_instance['show_stat']) ? 1 : 0,
|
||||
'stat_number' => sanitize_text_field((string) ($new_instance['stat_number'] ?? '')),
|
||||
'stat_suffix' => sanitize_text_field((string) ($new_instance['stat_suffix'] ?? '')),
|
||||
'stat_label' => sanitize_text_field((string) ($new_instance['stat_label'] ?? '')),
|
||||
]; return $this->update_common_fields($new_instance, $old_instance, $business);
|
||||
}
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Language switcher widget (Polylang / WPML / filter).
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
namespace AKSRT\Widgets;
|
||||
|
||||
use AKSRT\Widgets\Traits\CommonWidgetFields;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
class WidgetLangSwitch extends \WP_Widget {
|
||||
use CommonWidgetFields;
|
||||
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
'aksrt_lang_switch',
|
||||
__('语言切换器', 'aksrt'),
|
||||
[
|
||||
'description' => __('在侧栏显示多语言切换按钮。', 'aksrt'),
|
||||
'classname' => 'widget_aksrt_lang_switch',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,mixed> $args
|
||||
* @param array<string,mixed> $instance
|
||||
*/
|
||||
public function widget($args, $instance): void {
|
||||
$title = !empty($instance['title']) ? (string) $instance['title'] : '';
|
||||
$style = sanitize_key((string) ($instance['style'] ?? 'dropdown'));
|
||||
if (!in_array($style, ['dropdown', 'flags', 'inline'], true)) {
|
||||
$style = 'dropdown';
|
||||
}
|
||||
$show_name = !isset($instance['show_name']) || !empty($instance['show_name']);
|
||||
$hide_current = !empty($instance['hide_current']);
|
||||
|
||||
if (!function_exists('aksrt_has_multilingual') || !aksrt_has_multilingual()) {
|
||||
if (current_user_can('manage_options')) {
|
||||
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<div class="c-red muted-box mb10"><b>' . esc_html__('语言切换:', 'aksrt') . '</b>' .
|
||||
esc_html__('未检测到多语言插件(Polylang/WPML)或可用语言不足。', 'aksrt') . '</div>';
|
||||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// inline style: treat as flags row without dropdown chrome.
|
||||
$render_style = $style === 'inline' ? 'flags' : $style;
|
||||
$html = function_exists('aksrt_render_language_switcher') ? aksrt_render_language_switcher($render_style) : '';
|
||||
if ($html === '') {
|
||||
if (current_user_can('manage_options')) {
|
||||
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo function_exists('aksrt_get_widget_null')
|
||||
? aksrt_get_widget_null(__('暂无可用语言', 'aksrt')) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
: '<p class="widget-empty">' . esc_html__('暂无可用语言', 'aksrt') . '</p>';
|
||||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Optional modifiers via wrapper classes.
|
||||
$wrap = 'aksrt-lang-switch-widget style-' . esc_attr($style);
|
||||
if (!$show_name) {
|
||||
$wrap .= ' hide-name';
|
||||
}
|
||||
if ($hide_current) {
|
||||
$wrap .= ' hide-current';
|
||||
}
|
||||
|
||||
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
if ($title !== '') {
|
||||
echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
echo '<div class="' . esc_attr($wrap) . '">' . $html . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,mixed> $instance
|
||||
*/
|
||||
public function form($instance): void {
|
||||
$title = !empty($instance['title']) ? (string) $instance['title'] : '';
|
||||
$style = !empty($instance['style']) ? (string) $instance['style'] : 'dropdown';
|
||||
?>
|
||||
<p>
|
||||
<label for="<?php echo esc_attr($this->get_field_id('title')); ?>"><?php esc_html_e('标题(可选):', 'aksrt'); ?></label>
|
||||
<input class="widefat" id="<?php echo esc_attr($this->get_field_id('title')); ?>" name="<?php echo esc_attr($this->get_field_name('title')); ?>" type="text" value="<?php echo esc_attr($title); ?>">
|
||||
</p>
|
||||
<p>
|
||||
<label for="<?php echo esc_attr($this->get_field_id('style')); ?>"><?php esc_html_e('样式:', 'aksrt'); ?></label>
|
||||
<select class="widefat" id="<?php echo esc_attr($this->get_field_id('style')); ?>" name="<?php echo esc_attr($this->get_field_name('style')); ?>">
|
||||
<option value="dropdown" <?php selected($style, 'dropdown'); ?>><?php esc_html_e('下拉菜单', 'aksrt'); ?></option>
|
||||
<option value="flags" <?php selected($style, 'flags'); ?>><?php esc_html_e('国旗图标', 'aksrt'); ?></option>
|
||||
<option value="inline" <?php selected($style, 'inline'); ?>><?php esc_html_e('横向排列', 'aksrt'); ?></option>
|
||||
</select>
|
||||
</p>
|
||||
<p>
|
||||
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('show_name')); ?>" value="1" <?php checked(!isset($instance['show_name']) || !empty($instance['show_name'])); ?>> <?php esc_html_e('显示语言名', 'aksrt'); ?></label><br>
|
||||
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('hide_current')); ?>" value="1" <?php checked(!empty($instance['hide_current'])); ?>> <?php esc_html_e('隐藏当前语言', 'aksrt'); ?></label>
|
||||
</p>
|
||||
<p><small><?php esc_html_e('需要安装 Polylang 或 WPML 等多语言插件才能生效。', 'aksrt'); ?></small></p>
|
||||
<?php
|
||||
$this->render_common_form_section($instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,mixed> $new_instance
|
||||
* @param array<string,mixed> $old_instance
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
public function update($new_instance, $old_instance): array {
|
||||
$style = sanitize_key((string) ($new_instance['style'] ?? 'dropdown'));
|
||||
if (!in_array($style, ['dropdown', 'flags', 'inline'], true)) {
|
||||
$style = 'dropdown';
|
||||
}
|
||||
$business = [
|
||||
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
|
||||
'style' => $style,
|
||||
'show_name' => !empty($new_instance['show_name']) ? 1 : 0,
|
||||
'hide_current' => !empty($new_instance['hide_current']) ? 1 : 0,
|
||||
]; return $this->update_common_fields($new_instance, $old_instance, $business);
|
||||
}
|
||||
}
|
||||
@@ -1,273 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Paid posts list (zibll zib_widget_ui_posts_pay + ajax).
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
namespace AKSRT\Widgets;
|
||||
|
||||
use AKSRT\Widgets\Traits\CommonWidgetFields;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
class WidgetPaidPosts extends \WP_Widget {
|
||||
use CommonWidgetFields;
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
'aksrt_paid_posts',
|
||||
esc_html__('AKSRT 付费内容列表', 'aksrt'),
|
||||
[
|
||||
'description' => esc_html__('展示开启付费/积分可见的文章(支持 AJAX)。', 'aksrt'),
|
||||
'classname' => 'widget_aksrt_paid_posts',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public static function build_query_args(array $instance, int $paged = 1): array {
|
||||
$count = max(1, min(20, (int) ($instance['count'] ?? 6)));
|
||||
$orderby = sanitize_key((string) ($instance['orderby'] ?? 'date'));
|
||||
$query = [
|
||||
'post_type' => 'post',
|
||||
'post_status' => 'publish',
|
||||
'posts_per_page' => $count,
|
||||
'paged' => max(1, $paged),
|
||||
'order' => 'DESC',
|
||||
'meta_query' => [
|
||||
'relation' => 'OR',
|
||||
[
|
||||
'key' => '_aksrt_pay_price',
|
||||
'value' => 0,
|
||||
'compare' => '>',
|
||||
'type' => 'NUMERIC',
|
||||
],
|
||||
[
|
||||
'key' => '_aksrt_credits_cost',
|
||||
'value' => 0,
|
||||
'compare' => '>',
|
||||
'type' => 'NUMERIC',
|
||||
],
|
||||
[
|
||||
'key' => '_aksrt_visibility',
|
||||
'value' => ['pay', 'credits'],
|
||||
'compare' => 'IN',
|
||||
],
|
||||
],
|
||||
];
|
||||
if ($orderby === 'views') {
|
||||
$query['meta_key'] = 'aksrt_post_views';
|
||||
$query['orderby'] = 'meta_value_num';
|
||||
} elseif ($orderby === 'price') {
|
||||
$query['meta_key'] = '_aksrt_pay_price';
|
||||
$query['orderby'] = 'meta_value_num';
|
||||
} else {
|
||||
$query['orderby'] = 'date';
|
||||
}
|
||||
$cat = max(0, (int) ($instance['cat'] ?? 0));
|
||||
if ($cat > 0) {
|
||||
$query['cat'] = $cat;
|
||||
}
|
||||
$limit_day = max(0, (int) ($instance['limit_day'] ?? 0));
|
||||
if ($limit_day > 0) {
|
||||
$query['date_query'] = [['after' => $limit_day . ' days ago']];
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
public static function render_items_html(\WP_Query $q, string $style = 'mini'): string {
|
||||
if (!$q->have_posts()) {
|
||||
return '';
|
||||
}
|
||||
$placeholder = get_template_directory_uri() . '/assets/img/placeholder.svg';
|
||||
ob_start();
|
||||
while ($q->have_posts()) {
|
||||
$q->the_post();
|
||||
$pid = get_the_ID();
|
||||
$price = (float) get_post_meta($pid, '_aksrt_pay_price', true);
|
||||
$credits = (int) get_post_meta($pid, '_aksrt_credits_cost', true);
|
||||
$thumb = get_the_post_thumbnail_url($pid, 'thumbnail');
|
||||
if ($price > 0) {
|
||||
$badge = '¥' . number_format($price, 2, '.', '');
|
||||
} elseif ($credits > 0) {
|
||||
$badge = sprintf(__('%d 积分', 'aksrt'), $credits);
|
||||
} else {
|
||||
$badge = __('付费', 'aksrt');
|
||||
}
|
||||
$mode = in_array($style, ['list', 'card', 'mini'], true) ? $style : 'mini';
|
||||
if (function_exists('aksrt_render_post')) {
|
||||
$item = aksrt_render_post([
|
||||
'mode' => $mode,
|
||||
'context' => 'widget',
|
||||
'show_thumb' => true,
|
||||
'show_meta' => true,
|
||||
'show_excerpt' => $mode === 'list',
|
||||
'extra_class' => 'ajax-item aksrt-paid-posts__' . $mode,
|
||||
]);
|
||||
if ($item !== '') {
|
||||
echo '<div class="aksrt-paid-posts__item is-' . esc_attr($mode) . ' ajax-item">';
|
||||
echo $item; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<span class="aksrt-paid-posts__price badge">' . esc_html($badge) . '</span>';
|
||||
echo '</div>';
|
||||
continue;
|
||||
}
|
||||
}
|
||||
echo '<li class="aksrt-paid-posts__item ajax-item">';
|
||||
if ($thumb) {
|
||||
echo '<a class="aksrt-paid-posts__thumb" href="' . esc_url(get_permalink()) . '"><img class="aksrt-lazy lazyload" data-src="' . esc_url($thumb) . '" src="' . esc_url($placeholder) . '" alt="" loading="lazy" /></a>';
|
||||
}
|
||||
echo '<div class="aksrt-paid-posts__body">';
|
||||
echo '<a class="aksrt-paid-posts__title" href="' . esc_url(get_permalink()) . '">' . esc_html(get_the_title()) . '</a>';
|
||||
echo '<div class="aksrt-paid-posts__meta"><span class="aksrt-paid-posts__price">' . esc_html($badge) . '</span></div>';
|
||||
echo '</div></li>';
|
||||
}
|
||||
wp_reset_postdata();
|
||||
return (string) ob_get_clean();
|
||||
}
|
||||
|
||||
public static function query_payload(array $instance, int $page = 1): array {
|
||||
$page = max(1, $page);
|
||||
$paginate = sanitize_key((string) ($instance['paginate'] ?? 'ajax'));
|
||||
$count = max(1, min(20, (int) ($instance['count'] ?? 6)));
|
||||
$q = new \WP_Query(self::build_query_args($instance, $page));
|
||||
$style = sanitize_key((string) ($instance['style'] ?? 'mini'));
|
||||
if (!in_array($style, ['mini', 'list', 'card'], true)) {
|
||||
$style = 'mini';
|
||||
}
|
||||
$items = self::render_items_html($q, $style);
|
||||
$wrap_tag = (strpos($items, 'posts-mini') !== false || strpos($items, 'aksrt-paid-posts__mini') !== false) ? 'div' : 'ul';
|
||||
$html = $items !== '' ? '<' . $wrap_tag . ' class="aksrt-paid-posts style-' . esc_attr($style) . '">' . $items . '</' . $wrap_tag . '>' : '';
|
||||
if ($html === '' && $page === 1) {
|
||||
$html = function_exists('aksrt_get_ajax_null')
|
||||
? aksrt_get_ajax_null(__('暂无付费内容', 'aksrt'))
|
||||
: '<p class="widget-empty ajax-item">' . esc_html__('暂无付费内容', 'aksrt') . '</p>';
|
||||
}
|
||||
$max_pages = max(1, (int) $q->max_num_pages);
|
||||
$total = (int) $q->found_posts;
|
||||
$has_more = $page < $max_pages;
|
||||
$base_qs = array_filter([
|
||||
'count' => $count,
|
||||
'orderby' => sanitize_key((string) ($instance['orderby'] ?? 'date')),
|
||||
'cat' => max(0, (int) ($instance['cat'] ?? 0)),
|
||||
'limit_day' => max(0, (int) ($instance['limit_day'] ?? 0)),
|
||||
'style' => $style,
|
||||
'paginate' => $paginate,
|
||||
], static function ($v) {
|
||||
return $v !== '' && $v !== null;
|
||||
});
|
||||
$rest_base = add_query_arg($base_qs, rest_url('aksrt/v1/widgets/paid-posts'));
|
||||
$paginate_html = '';
|
||||
if ($paginate === 'ajax' && $has_more) {
|
||||
$next = add_query_arg(['page' => $page + 1], $rest_base);
|
||||
$paginate_html = function_exists('aksrt_get_ajax_next_paginate')
|
||||
? aksrt_get_ajax_next_paginate($total, $page, $count, $next)
|
||||
: '<div class="text-center theme-pagination ajax-pag"><div class="next-page ajax-next"><a href="' . esc_url($next) . '">' . esc_html__('加载更多', 'aksrt') . '</a></div></div>';
|
||||
} elseif ($paginate === 'number' && $max_pages > 1) {
|
||||
$paginate_html = function_exists('aksrt_get_ajax_number_paginate')
|
||||
? aksrt_get_ajax_number_paginate($total, $page, $count, $rest_base)
|
||||
: '<div class="ajax-pag hide"><div class="next-page ajax-next"><a href="#"></a></div></div>';
|
||||
} else {
|
||||
$paginate_html = '<div class="ajax-pag hide"><div class="next-page ajax-next"><a href="#"></a></div></div>';
|
||||
}
|
||||
return [
|
||||
'html' => $html,
|
||||
'paginate_html' => $paginate_html,
|
||||
'has_more' => $has_more,
|
||||
'page' => $page,
|
||||
'max_pages' => $max_pages,
|
||||
'total' => $total,
|
||||
];
|
||||
}
|
||||
|
||||
public function widget($args, $instance): void {
|
||||
$paginate = sanitize_key((string) ($instance['paginate'] ?? 'ajax'));
|
||||
$instance['paginate'] = $paginate;
|
||||
$payload = self::query_payload($instance, 1);
|
||||
|
||||
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
$title = apply_filters('widget_title', (string) ($instance['title'] ?? ''), $instance, $this->id_base);
|
||||
if ($title !== '') {
|
||||
echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
$affix = function_exists('aksrt_widget_affix_attrs') ? aksrt_widget_affix_attrs($instance) : ['class' => '', 'attr' => ''];
|
||||
$rest = rest_url('aksrt/v1/widgets/paid-posts');
|
||||
$wrap = 'aksrt-paid-posts-wrap ajaxpager' . $affix['class'];
|
||||
echo '<div class="' . esc_attr($wrap) . '" data-aksrt-ajaxpager data-aksrt-ajax-url="' . esc_url($rest) . '"' . $affix['attr'] . '>';
|
||||
echo $payload['html']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $payload['paginate_html']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo '<div class="post_ajax_loader" data-aksrt-ajax-loader data-aksrt-ajax-loader-hide hidden></div>';
|
||||
echo '</div>';
|
||||
|
||||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
public function form($instance): void {
|
||||
?>
|
||||
<p>
|
||||
<label><?php esc_html_e('标题', 'aksrt'); ?>
|
||||
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('title')); ?>" value="<?php echo esc_attr((string) ($instance['title'] ?? __('付费精选', 'aksrt'))); ?>">
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label><?php esc_html_e('数量', 'aksrt'); ?>
|
||||
<input class="tiny-text" type="number" min="1" max="20" name="<?php echo esc_attr($this->get_field_name('count')); ?>" value="<?php echo esc_attr((string) ((int) ($instance['count'] ?? 6))); ?>">
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label><?php esc_html_e('样式', 'aksrt'); ?>
|
||||
<select name="<?php echo esc_attr($this->get_field_name('style')); ?>">
|
||||
<option value="mini" <?php selected(($instance['style'] ?? 'mini'), 'mini'); ?>><?php esc_html_e('迷你', 'aksrt'); ?></option>
|
||||
<option value="list" <?php selected(($instance['style'] ?? ''), 'list'); ?>><?php esc_html_e('列表', 'aksrt'); ?></option>
|
||||
<option value="card" <?php selected(($instance['style'] ?? ''), 'card'); ?>><?php esc_html_e('卡片', 'aksrt'); ?></option>
|
||||
</select>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label><?php esc_html_e('排序', 'aksrt'); ?>
|
||||
<select name="<?php echo esc_attr($this->get_field_name('orderby')); ?>">
|
||||
<option value="date" <?php selected(($instance['orderby'] ?? 'date'), 'date'); ?>><?php esc_html_e('时间', 'aksrt'); ?></option>
|
||||
<option value="views" <?php selected(($instance['orderby'] ?? ''), 'views'); ?>><?php esc_html_e('浏览', 'aksrt'); ?></option>
|
||||
<option value="price" <?php selected(($instance['orderby'] ?? ''), 'price'); ?>><?php esc_html_e('价格', 'aksrt'); ?></option>
|
||||
</select>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label><?php esc_html_e('分页', 'aksrt'); ?>
|
||||
<select name="<?php echo esc_attr($this->get_field_name('paginate')); ?>">
|
||||
<option value="ajax" <?php selected(($instance['paginate'] ?? 'ajax'), 'ajax'); ?>><?php esc_html_e('AJAX', 'aksrt'); ?></option>
|
||||
<option value="number" <?php selected(($instance['paginate'] ?? ''), 'number'); ?>><?php esc_html_e('数字分页', 'aksrt'); ?></option>
|
||||
<option value="none" <?php selected(($instance['paginate'] ?? ''), 'none'); ?>><?php esc_html_e('无', 'aksrt'); ?></option>
|
||||
</select>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label><?php esc_html_e('分类 ID(0=全部)', 'aksrt'); ?>
|
||||
<input class="tiny-text" type="number" min="0" name="<?php echo esc_attr($this->get_field_name('cat')); ?>" value="<?php echo esc_attr((string) ((int) ($instance['cat'] ?? 0))); ?>">
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label><?php esc_html_e('最近 N 天(0=不限)', 'aksrt'); ?>
|
||||
<input class="tiny-text" type="number" min="0" name="<?php echo esc_attr($this->get_field_name('limit_day')); ?>" value="<?php echo esc_attr((string) ((int) ($instance['limit_day'] ?? 0))); ?>">
|
||||
</label>
|
||||
</p>
|
||||
<?php $this->render_common_form_section($instance); ?>
|
||||
<?php
|
||||
}
|
||||
|
||||
public function update($new_instance, $old_instance): array {
|
||||
$orderby = sanitize_key((string) ($new_instance['orderby'] ?? 'date'));
|
||||
if (!in_array($orderby, ['date', 'views', 'price'], true)) {
|
||||
$orderby = 'date';
|
||||
}
|
||||
return $this->update_common_fields($new_instance, $old_instance, [
|
||||
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
|
||||
'count' => max(1, min(20, (int) ($new_instance['count'] ?? 6))),
|
||||
'orderby' => $orderby,
|
||||
'style' => in_array(sanitize_key((string) ($new_instance['style'] ?? 'mini')), ['mini', 'list', 'card'], true) ? sanitize_key((string) ($new_instance['style'] ?? 'mini')) : 'mini',
|
||||
'cat' => max(0, (int) ($new_instance['cat'] ?? 0)),
|
||||
'limit_day' => max(0, (int) ($new_instance['limit_day'] ?? 0)),
|
||||
'paginate' => in_array(sanitize_key((string) ($new_instance['paginate'] ?? 'ajax')), ['none', 'ajax', 'number'], true) ? sanitize_key((string) ($new_instance['paginate'] ?? 'ajax')) : 'ajax',
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,360 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Horizontal partners / friends scroll (zibll zib_widget_ui_partners_scroll).
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
namespace AKSRT\Widgets;
|
||||
|
||||
use AKSRT\Widgets\Traits\CommonWidgetFields;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
class WidgetPartnersScroll extends \WP_Widget {
|
||||
use CommonWidgetFields;
|
||||
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
'aksrt_partners_scroll',
|
||||
esc_html__('AKSRT 合作伙伴滚动', 'aksrt'),
|
||||
[
|
||||
'description' => esc_html__('横向无缝滚动的合作伙伴/友情链接条。', 'aksrt'),
|
||||
'classname' => 'widget_aksrt_partners_scroll',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function widget($args, $instance): void {
|
||||
$partners = $this->resolve_partners($instance);
|
||||
if (!$partners) {
|
||||
if (current_user_can('manage_options')) {
|
||||
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo function_exists('aksrt_get_widget_null')
|
||||
? aksrt_get_widget_null(__('暂无合作伙伴,请在模块中添加', 'aksrt')) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
: '<p class="widget-empty">' . esc_html__('暂无合作伙伴', 'aksrt') . '</p>';
|
||||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$rows = max(1, min(4, (int) ($instance['rows'] ?? 1)));
|
||||
$m_rows = max(1, min(4, (int) ($instance['m_rows'] ?? 1)));
|
||||
$style_type = sanitize_key((string) ($instance['style_type'] ?? 'card'));
|
||||
if (!in_array($style_type, ['card', 'logo', 'chip'], true)) {
|
||||
$style_type = 'card';
|
||||
}
|
||||
$card_w = max(60, min(400, (int) ($instance['card_width'] ?? 220)));
|
||||
$card_h = max(40, min(200, (int) ($instance['card_height'] ?? 90)));
|
||||
$speed = max(1, min(20, (int) ($instance['speed'] ?? 3)));
|
||||
$direction = sanitize_key((string) ($instance['direction'] ?? 'alternate'));
|
||||
$pause = !empty($instance['pause_hover']) || !isset($instance['pause_hover']);
|
||||
$card_bg = !empty($instance['card_bg']) || !isset($instance['card_bg']);
|
||||
$end_fade = !empty($instance['end_fade']) || !isset($instance['end_fade']);
|
||||
$go_link = !empty($instance['go_link']);
|
||||
$show_bg = !empty($instance['show_widget_bg']) || !isset($instance['show_widget_bg']);
|
||||
$radius = max(0, min(40, (int) ($instance['card_radius'] ?? 10)));
|
||||
|
||||
$use_rows = wp_is_mobile() ? $m_rows : $rows;
|
||||
$rows_data = array_fill(0, $use_rows, []);
|
||||
foreach (array_values($partners) as $i => $p) {
|
||||
$rows_data[$i % $use_rows][] = $p;
|
||||
}
|
||||
$rows_data = array_values(array_filter($rows_data, static fn($r) => !empty($r)));
|
||||
|
||||
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
$title = apply_filters('widget_title', (string) ($instance['title'] ?? ''), $instance, $this->id_base);
|
||||
if ($title !== '') {
|
||||
echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
$wrap_class = 'aksrt-partners-scroll aksrt-ps-style-' . $style_type;
|
||||
if ($pause) {
|
||||
$wrap_class .= ' aksrt-ps-pause-hover';
|
||||
}
|
||||
if ($card_bg) {
|
||||
$wrap_class .= ' aksrt-ps-card-bg';
|
||||
}
|
||||
if ($end_fade) {
|
||||
$wrap_class .= ' aksrt-ps-end-fade';
|
||||
}
|
||||
|
||||
$style = sprintf(
|
||||
'--aksrt-ps-w:%dpx;--aksrt-ps-h:%dpx;--aksrt-ps-radius:%dpx;--aksrt-ps-speed:%d;',
|
||||
$card_w,
|
||||
$card_h,
|
||||
$radius,
|
||||
$speed
|
||||
);
|
||||
|
||||
$outer = $show_bg ? 'aksrt-partners-scroll-wrap' : 'aksrt-partners-scroll-wrap is-bare';
|
||||
echo '<div class="' . esc_attr($outer) . '">';
|
||||
echo '<div class="' . esc_attr($wrap_class) . '" style="' . esc_attr($style) . '">';
|
||||
|
||||
foreach ($rows_data as $row_index => $row_partners) {
|
||||
switch ($direction) {
|
||||
case 'left':
|
||||
$dir = 'left';
|
||||
break;
|
||||
case 'right':
|
||||
$dir = 'right';
|
||||
break;
|
||||
default:
|
||||
$dir = ($row_index % 2 === 0) ? 'left' : 'right';
|
||||
}
|
||||
echo '<div class="aksrt-ps-row aksrt-ps-dir-' . esc_attr($dir) . '">';
|
||||
echo '<div class="aksrt-ps-track">';
|
||||
// Duplicate group for seamless CSS marquee.
|
||||
for ($dup = 0; $dup < 2; $dup++) {
|
||||
echo '<div class="aksrt-ps-group">';
|
||||
foreach ($row_partners as $partner) {
|
||||
echo $this->render_card($partner, $style_type, $go_link); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
echo '</div>';
|
||||
}
|
||||
echo '</div></div>';
|
||||
}
|
||||
|
||||
echo '</div></div>';
|
||||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,mixed> $partner
|
||||
*/
|
||||
private function render_card(array $partner, string $style_type, bool $go_link): string {
|
||||
$title = trim((string) ($partner['title'] ?? ''));
|
||||
$desc = trim((string) ($partner['desc'] ?? ''));
|
||||
$image = (string) ($partner['image'] ?? '');
|
||||
$url = (string) ($partner['url'] ?? '');
|
||||
$target = (string) ($partner['target'] ?? '');
|
||||
|
||||
if ($image === '' && $title === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$alt = esc_attr($title !== '' ? $title : __('合作伙伴', 'aksrt'));
|
||||
$img_tag = $image !== ''
|
||||
? '<img class="aksrt-ps-img aksrt-lazy lazyload" data-src="' . esc_url($image) . '" src="' . esc_url(get_template_directory_uri() . '/assets/img/placeholder.svg') . '" alt="' . $alt . '" loading="lazy" />'
|
||||
: '';
|
||||
|
||||
$inner = '';
|
||||
if ($style_type === 'logo') {
|
||||
if ($img_tag === '') {
|
||||
return '';
|
||||
}
|
||||
$inner = $img_tag;
|
||||
} elseif ($style_type === 'chip') {
|
||||
if ($img_tag !== '') {
|
||||
$inner .= '<span class="aksrt-ps-chip-icon">' . $img_tag . '</span>';
|
||||
}
|
||||
$inner .= '<span class="aksrt-ps-chip-title">' . esc_html($title) . '</span>';
|
||||
} else {
|
||||
if ($img_tag !== '') {
|
||||
$inner .= '<div class="aksrt-ps-card-icon">' . $img_tag . '</div>';
|
||||
}
|
||||
$inner .= '<div class="aksrt-ps-card-text">';
|
||||
if ($title !== '') {
|
||||
$inner .= '<div class="aksrt-ps-card-title">' . esc_html($title) . '</div>';
|
||||
}
|
||||
if ($desc !== '') {
|
||||
$inner .= '<div class="aksrt-ps-card-desc">' . esc_html($desc) . '</div>';
|
||||
}
|
||||
$inner .= '</div>';
|
||||
}
|
||||
|
||||
if ($url !== '') {
|
||||
if ($go_link && function_exists('aksrt_go_link')) {
|
||||
$url = aksrt_go_link($url, true);
|
||||
}
|
||||
$tgt = $target !== '' ? ' target="' . esc_attr($target) . '" rel="noopener noreferrer"' : '';
|
||||
return '<a class="aksrt-ps-item" href="' . esc_url($url) . '"' . $tgt . '>' . $inner . '</a>';
|
||||
}
|
||||
|
||||
return '<div class="aksrt-ps-item">' . $inner . '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string,mixed> $instance
|
||||
* @return array<int, array{title:string,desc:string,image:string,url:string,target:string}>
|
||||
*/
|
||||
private function resolve_partners(array $instance): array {
|
||||
$source = sanitize_key((string) ($instance['link_type'] ?? 'manual'));
|
||||
if ($source === 'link') {
|
||||
$args = [
|
||||
'orderby' => sanitize_key((string) ($instance['link_orderby'] ?? 'name')) ?: 'name',
|
||||
'order' => strtoupper((string) ($instance['link_order'] ?? 'ASC')) === 'DESC' ? 'DESC' : 'ASC',
|
||||
'limit' => max(1, min(100, (int) ($instance['link_limit'] ?? 50))),
|
||||
];
|
||||
$cats = $instance['link_cats'] ?? '';
|
||||
if (is_array($cats)) {
|
||||
$cats = implode(',', array_map('intval', $cats));
|
||||
}
|
||||
$cats = trim((string) $cats);
|
||||
if ($cats !== '') {
|
||||
$args['category'] = $cats;
|
||||
}
|
||||
$bookmarks = get_bookmarks($args);
|
||||
$out = [];
|
||||
foreach ($bookmarks as $b) {
|
||||
$out[] = [
|
||||
'title' => (string) $b->link_name,
|
||||
'desc' => (string) $b->link_description,
|
||||
'image' => (string) $b->link_image,
|
||||
'url' => (string) $b->link_url,
|
||||
'target' => (string) $b->link_target,
|
||||
];
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
return $this->normalize_manual($instance['partners'] ?? []);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $raw
|
||||
* @return array<int, array{title:string,desc:string,image:string,url:string,target:string}>
|
||||
*/
|
||||
private function normalize_manual($raw): array {
|
||||
if (is_string($raw)) {
|
||||
$decoded = json_decode($raw, true);
|
||||
$raw = is_array($decoded) ? $decoded : [];
|
||||
}
|
||||
if (!is_array($raw)) {
|
||||
return [];
|
||||
}
|
||||
$out = [];
|
||||
foreach ($raw as $row) {
|
||||
if (!is_array($row)) {
|
||||
continue;
|
||||
}
|
||||
$title = sanitize_text_field((string) ($row['title'] ?? ''));
|
||||
$image = esc_url_raw(trim((string) ($row['image'] ?? '')));
|
||||
$url = esc_url_raw(trim((string) ($row['url'] ?? $row['link'] ?? '')));
|
||||
if ($title === '' && $image === '') {
|
||||
continue;
|
||||
}
|
||||
$out[] = [
|
||||
'title' => $title,
|
||||
'desc' => sanitize_text_field((string) ($row['desc'] ?? '')),
|
||||
'image' => $image,
|
||||
'url' => $url,
|
||||
'target' => sanitize_text_field((string) ($row['target'] ?? '_blank')),
|
||||
];
|
||||
if (count($out) >= 40) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
public function form($instance): void {
|
||||
$title = (string) ($instance['title'] ?? '');
|
||||
$link_type = (string) ($instance['link_type'] ?? 'manual');
|
||||
$partners_json = is_string($instance['partners'] ?? null)
|
||||
? (string) $instance['partners']
|
||||
: wp_json_encode($this->normalize_manual($instance['partners'] ?? []), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
?>
|
||||
<p>
|
||||
<label><?php esc_html_e('标题', 'aksrt'); ?>
|
||||
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('title')); ?>" value="<?php echo esc_attr($title); ?>">
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label><?php esc_html_e('数据来源', 'aksrt'); ?>
|
||||
<select class="widefat" name="<?php echo esc_attr($this->get_field_name('link_type')); ?>">
|
||||
<option value="manual" <?php selected($link_type, 'manual'); ?>><?php esc_html_e('手动 JSON', 'aksrt'); ?></option>
|
||||
<option value="link" <?php selected($link_type, 'link'); ?>><?php esc_html_e('WordPress 链接(书签)', 'aksrt'); ?></option>
|
||||
</select>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label><?php esc_html_e('合作伙伴 JSON', 'aksrt'); ?>
|
||||
<textarea class="widefat" rows="6" name="<?php echo esc_attr($this->get_field_name('partners')); ?>" placeholder='[{"title":"Name","image":"https://...","url":"https://...","desc":""}]'><?php echo esc_textarea((string) $partners_json); ?></textarea>
|
||||
</label>
|
||||
<small><?php esc_html_e('字段: title, image, url, desc, target', 'aksrt'); ?></small>
|
||||
</p>
|
||||
<p>
|
||||
<label><?php esc_html_e('链接分类 ID(逗号分隔,书签模式)', 'aksrt'); ?>
|
||||
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('link_cats')); ?>" value="<?php echo esc_attr((string) ($instance['link_cats'] ?? '')); ?>">
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label><?php esc_html_e('书签数量上限', 'aksrt'); ?>
|
||||
<input class="tiny-text" type="number" min="1" max="100" name="<?php echo esc_attr($this->get_field_name('link_limit')); ?>" value="<?php echo esc_attr((string) ((int) ($instance['link_limit'] ?? 50))); ?>">
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label><?php esc_html_e('样式', 'aksrt'); ?>
|
||||
<select name="<?php echo esc_attr($this->get_field_name('style_type')); ?>">
|
||||
<option value="card" <?php selected(($instance['style_type'] ?? 'card'), 'card'); ?>><?php esc_html_e('图文卡片', 'aksrt'); ?></option>
|
||||
<option value="logo" <?php selected(($instance['style_type'] ?? ''), 'logo'); ?>><?php esc_html_e('纯 Logo', 'aksrt'); ?></option>
|
||||
<option value="chip" <?php selected(($instance['style_type'] ?? ''), 'chip'); ?>><?php esc_html_e('胶囊', 'aksrt'); ?></option>
|
||||
</select>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label><?php esc_html_e('PC 行数', 'aksrt'); ?>
|
||||
<input class="tiny-text" type="number" min="1" max="4" name="<?php echo esc_attr($this->get_field_name('rows')); ?>" value="<?php echo esc_attr((string) ((int) ($instance['rows'] ?? 1))); ?>">
|
||||
</label>
|
||||
<label><?php esc_html_e('移动行数', 'aksrt'); ?>
|
||||
<input class="tiny-text" type="number" min="1" max="4" name="<?php echo esc_attr($this->get_field_name('m_rows')); ?>" value="<?php echo esc_attr((string) ((int) ($instance['m_rows'] ?? 1))); ?>">
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label><?php esc_html_e('卡片宽', 'aksrt'); ?>
|
||||
<input class="tiny-text" type="number" min="60" max="400" name="<?php echo esc_attr($this->get_field_name('card_width')); ?>" value="<?php echo esc_attr((string) ((int) ($instance['card_width'] ?? 220))); ?>">
|
||||
</label>
|
||||
<label><?php esc_html_e('高', 'aksrt'); ?>
|
||||
<input class="tiny-text" type="number" min="40" max="200" name="<?php echo esc_attr($this->get_field_name('card_height')); ?>" value="<?php echo esc_attr((string) ((int) ($instance['card_height'] ?? 90))); ?>">
|
||||
</label>
|
||||
<label><?php esc_html_e('速度', 'aksrt'); ?>
|
||||
<input class="tiny-text" type="number" min="1" max="20" name="<?php echo esc_attr($this->get_field_name('speed')); ?>" value="<?php echo esc_attr((string) ((int) ($instance['speed'] ?? 3))); ?>">
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label><?php esc_html_e('方向', 'aksrt'); ?>
|
||||
<select name="<?php echo esc_attr($this->get_field_name('direction')); ?>">
|
||||
<option value="alternate" <?php selected(($instance['direction'] ?? 'alternate'), 'alternate'); ?>><?php esc_html_e('交替', 'aksrt'); ?></option>
|
||||
<option value="left" <?php selected(($instance['direction'] ?? ''), 'left'); ?>><?php esc_html_e('向左', 'aksrt'); ?></option>
|
||||
<option value="right" <?php selected(($instance['direction'] ?? ''), 'right'); ?>><?php esc_html_e('向右', 'aksrt'); ?></option>
|
||||
</select>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('pause_hover')); ?>" value="1" <?php checked(!empty($instance['pause_hover']) || !isset($instance['pause_hover'])); ?>> <?php esc_html_e('悬停暂停', 'aksrt'); ?></label><br>
|
||||
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('card_bg')); ?>" value="1" <?php checked(!empty($instance['card_bg']) || !isset($instance['card_bg'])); ?>> <?php esc_html_e('卡片背景', 'aksrt'); ?></label><br>
|
||||
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('end_fade')); ?>" value="1" <?php checked(!empty($instance['end_fade']) || !isset($instance['end_fade'])); ?>> <?php esc_html_e('两端渐隐', 'aksrt'); ?></label><br>
|
||||
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('go_link')); ?>" value="1" <?php checked(!empty($instance['go_link'])); ?>> <?php esc_html_e('外链跳转页', 'aksrt'); ?></label><br>
|
||||
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('show_widget_bg')); ?>" value="1" <?php checked(!empty($instance['show_widget_bg']) || !isset($instance['show_widget_bg'])); ?>> <?php esc_html_e('显示背景盒子', 'aksrt'); ?></label>
|
||||
</p>
|
||||
<?php
|
||||
$this->render_common_form_section($instance);
|
||||
}
|
||||
|
||||
public function update($new_instance, $old_instance): array {
|
||||
$business = [
|
||||
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
|
||||
'link_type' => in_array(($new_instance['link_type'] ?? ''), ['manual', 'link'], true) ? $new_instance['link_type'] : 'manual',
|
||||
'partners' => $this->normalize_manual($new_instance['partners'] ?? []),
|
||||
'link_cats' => sanitize_text_field((string) ($new_instance['link_cats'] ?? '')),
|
||||
'link_limit' => max(1, min(100, (int) ($new_instance['link_limit'] ?? 50))),
|
||||
'link_orderby' => sanitize_key((string) ($new_instance['link_orderby'] ?? 'name')),
|
||||
'link_order' => strtoupper((string) ($new_instance['link_order'] ?? 'ASC')) === 'DESC' ? 'DESC' : 'ASC',
|
||||
'style_type' => in_array(($new_instance['style_type'] ?? ''), ['card', 'logo', 'chip'], true) ? $new_instance['style_type'] : 'card',
|
||||
'rows' => max(1, min(4, (int) ($new_instance['rows'] ?? 1))),
|
||||
'm_rows' => max(1, min(4, (int) ($new_instance['m_rows'] ?? 1))),
|
||||
'card_width' => max(60, min(400, (int) ($new_instance['card_width'] ?? 220))),
|
||||
'card_height' => max(40, min(200, (int) ($new_instance['card_height'] ?? 90))),
|
||||
'card_radius' => max(0, min(40, (int) ($new_instance['card_radius'] ?? 10))),
|
||||
'speed' => max(1, min(20, (int) ($new_instance['speed'] ?? 3))),
|
||||
'direction' => in_array(($new_instance['direction'] ?? ''), ['alternate', 'left', 'right'], true) ? $new_instance['direction'] : 'alternate',
|
||||
'pause_hover' => !empty($new_instance['pause_hover']) ? 1 : 0,
|
||||
'card_bg' => !empty($new_instance['card_bg']) ? 1 : 0,
|
||||
'end_fade' => !empty($new_instance['end_fade']) ? 1 : 0,
|
||||
'go_link' => !empty($new_instance['go_link']) ? 1 : 0,
|
||||
'show_widget_bg' => !empty($new_instance['show_widget_bg']) ? 1 : 0,
|
||||
]; return $this->update_common_fields($new_instance, $old_instance, $business);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* Singular post pay purchase sidebar card (zibll zib_widget_ui_posts_pay).
|
||||
* Compact paid-content purchase card for the article sidebar.
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
@@ -14,13 +14,12 @@ defined('ABSPATH') || exit;
|
||||
class WidgetPostPayBox extends \WP_Widget {
|
||||
use CommonWidgetFields;
|
||||
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
'aksrt_post_pay_box',
|
||||
esc_html__('AKSRT 付费购买', 'aksrt'),
|
||||
[
|
||||
'description' => esc_html__('显示当前文章/帖子的付费购买模块,推荐放在详情侧栏。', 'aksrt'),
|
||||
'description' => esc_html__('仅在文章页 - 侧边栏展示当前文章的紧凑购买卡。', 'aksrt'),
|
||||
'classname' => 'widget_aksrt_post_pay_box',
|
||||
]
|
||||
);
|
||||
@@ -30,17 +29,12 @@ class WidgetPostPayBox extends \WP_Widget {
|
||||
* @param array<string,mixed> $instance
|
||||
*/
|
||||
public static function render_html(array $instance = []): string {
|
||||
if (!is_singular(['post', 'page'])) {
|
||||
if (!is_single() || get_post_type() !== 'post') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$post = get_queried_object();
|
||||
if (!$post instanceof \WP_Post) {
|
||||
return '';
|
||||
}
|
||||
if (!function_exists('aksrt_get_pay_meta') || !function_exists('aksrt_is_paid_post')) {
|
||||
return '';
|
||||
}
|
||||
if (!aksrt_is_paid_post($post)) {
|
||||
if (!($post instanceof \WP_Post) || !function_exists('aksrt_get_pay_meta') || !function_exists('aksrt_is_paid_post') || !aksrt_is_paid_post($post)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -51,23 +45,39 @@ class WidgetPostPayBox extends \WP_Widget {
|
||||
}
|
||||
|
||||
$theme = sanitize_html_class((string) ($instance['theme'] ?? 'jb-red'));
|
||||
if ($theme === '') {
|
||||
$allowed_themes = ['jb-red', 'jb-yellow', 'jb-blue', 'jb-green', 'jb-purple', 'jb-vip1', 'jb-vip2'];
|
||||
if (!in_array($theme, $allowed_themes, true)) {
|
||||
$theme = 'jb-red';
|
||||
}
|
||||
|
||||
$has_access = function_exists('aksrt_has_paid_access') ? aksrt_has_paid_access($post) : false;
|
||||
$priced = function_exists('aksrt_get_user_pay_price') ? aksrt_get_user_pay_price($post) : null;
|
||||
$pay_modo = is_array($priced) && (($priced['modo'] ?? '') === 'points') ? 'points' : '0';
|
||||
$amount = is_array($priced) ? (float) ($priced['amount'] ?? 0) : (float) ($meta['price'] ?? 0);
|
||||
$currency = class_exists('\AKSRT\Settings\Settings') ? \AKSRT\Settings\Settings::get_currency_name() : __('积分', 'aksrt');
|
||||
$cash_symbol = '¥';
|
||||
if (class_exists('\AKSRT\Settings\Settings')) {
|
||||
$paid_s = \AKSRT\Settings\Settings::get_paid_content_settings();
|
||||
$cash_symbol = (string) ($paid_s['display']['currency_symbol'] ?? '¥');
|
||||
$price = function_exists('aksrt_get_user_pay_price')
|
||||
? aksrt_get_user_pay_price($post)
|
||||
: [
|
||||
'amount' => (float) ($meta['price'] ?? 0),
|
||||
'base' => (float) ($meta['price'] ?? 0),
|
||||
'original' => (float) ($meta['original_price'] ?? 0),
|
||||
'modo' => '0',
|
||||
'currency' => '¥',
|
||||
];
|
||||
$amount = max(0.0, (float) ($price['amount'] ?? 0));
|
||||
$base = max(0.0, (float) ($price['base'] ?? $amount));
|
||||
$original = max(0.0, (float) ($price['original'] ?? 0));
|
||||
$pay_modo = (($price['modo'] ?? '0') === 'points') ? 'points' : '0';
|
||||
$currency = (string) ($price['currency'] ?? ($pay_modo === 'points' ? __('积分', 'aksrt') : '¥'));
|
||||
$format = static function (float $value) use ($pay_modo, $currency): string {
|
||||
$number = function_exists('aksrt_format_price') ? aksrt_format_price($value) : number_format($value, 2, '.', '');
|
||||
return $pay_modo === 'points' ? $number . ' ' . $currency : $currency . $number;
|
||||
};
|
||||
|
||||
$has_access = function_exists('aksrt_has_paid_access') && aksrt_has_paid_access($post);
|
||||
$is_logged_in = is_user_logged_in();
|
||||
$login_url = function_exists('aksrt_get_login_url') ? aksrt_get_login_url() : wp_login_url(get_permalink($post));
|
||||
$demo = trim((string) ($meta['demo_url'] ?? ''));
|
||||
$sales = 0;
|
||||
if (function_exists('aksrt_get_download_box_meta')) {
|
||||
$box = aksrt_get_download_box_meta($post);
|
||||
$sales = max(0, (int) ($box['sales_count'] ?? 0));
|
||||
}
|
||||
$price_text = $pay_modo === 'points'
|
||||
? (function_exists('aksrt_format_price') ? aksrt_format_price($amount) : (string) $amount) . ' ' . $currency
|
||||
: $cash_symbol . (function_exists('aksrt_format_price') ? aksrt_format_price($amount) : number_format($amount, 2, '.', ''));
|
||||
|
||||
$type_labels = [
|
||||
'reading' => __('付费阅读', 'aksrt'),
|
||||
@@ -77,65 +87,56 @@ class WidgetPostPayBox extends \WP_Widget {
|
||||
'vip' => __('会员专属', 'aksrt'),
|
||||
];
|
||||
$type_label = $type_labels[$pay_type] ?? __('付费内容', 'aksrt');
|
||||
$cta_map = [
|
||||
$action_labels = [
|
||||
'reading' => __('查看内容', 'aksrt'),
|
||||
'download' => __('获取下载', 'aksrt'),
|
||||
'download' => __('前往下载', 'aksrt'),
|
||||
'gallery' => __('查看图片', 'aksrt'),
|
||||
'video' => __('查看视频', 'aksrt'),
|
||||
'vip' => __('查看内容', 'aksrt'),
|
||||
];
|
||||
$cta_label = $cta_map[$pay_type] ?? __('查看内容', 'aksrt');
|
||||
|
||||
$sales = 0;
|
||||
if (function_exists('aksrt_get_download_box_meta')) {
|
||||
$box = aksrt_get_download_box_meta($post);
|
||||
$sales = (int) ($box['sales_count'] ?? 0);
|
||||
}
|
||||
|
||||
$login_url = function_exists('aksrt_get_login_url') ? aksrt_get_login_url() : wp_login_url(get_permalink($post));
|
||||
$is_logged_in = is_user_logged_in();
|
||||
$demo = trim((string) ($meta['demo_url'] ?? ''));
|
||||
$action_label = $action_labels[$pay_type] ?? __('查看内容', 'aksrt');
|
||||
$show_original = $original > $amount && $original > 0;
|
||||
$show_member_discount = !$has_access && $amount > 0 && $base > $amount;
|
||||
|
||||
ob_start();
|
||||
echo '<div class="zib-widget pay-box pay-widget aksrt-post-pay-box order-type-' . esc_attr($pay_type) . ($has_access ? ' is-paid' : ' is-unpaid') . '" style="padding:0;">';
|
||||
echo '<badge class="pay-tag abs-center">' . esc_html($type_label) . '</badge>';
|
||||
echo '<div class="relative-h ' . esc_attr($theme) . ' aksrt-post-pay-box__hero">';
|
||||
echo '<div class="absolute radius ' . esc_attr($theme) . ' aksrt-post-pay-box__blob aksrt-post-pay-box__blob--a"></div>';
|
||||
echo '<div class="absolute radius ' . esc_attr($theme) . ' aksrt-post-pay-box__blob aksrt-post-pay-box__blob--b"></div>';
|
||||
echo '<div class="relative box-body text-center">';
|
||||
if ($has_access) {
|
||||
echo '<p class="mt10 mb6"><i class="fa fa-shopping-bag fa-3x" aria-hidden="true"></i></p>';
|
||||
echo '<b class="em14">' . esc_html__('已解锁', 'aksrt') . '</b>';
|
||||
} else {
|
||||
echo '<div class="text-center mt10 aksrt-post-pay-box__price">';
|
||||
if ($amount > 0) {
|
||||
echo '<div class="em14 font-bold">' . esc_html($price_text) . '</div>';
|
||||
} else {
|
||||
echo '<div class="em14 font-bold">' . esc_html__('免费', 'aksrt') . '</div>';
|
||||
}
|
||||
echo '</div>';
|
||||
}
|
||||
echo '</div></div>';
|
||||
|
||||
echo '<div class="box-body">';
|
||||
if ($has_access) {
|
||||
echo '<div class="mt10"><a href="#posts-pay" class="but padding-lg btn-block ' . esc_attr($theme) . '"><i class="fa fa-dot-circle-o fa-fw" aria-hidden="true"></i> ' . esc_html($cta_label) . '</a></div>';
|
||||
} elseif (!$is_logged_in) {
|
||||
echo '<div class="mt10"><a href="' . esc_url($login_url) . '" class="but padding-lg btn-block jb-blue"><i class="fa fa-sign-in" aria-hidden="true"></i> ' . esc_html__('登录后购买', 'aksrt') . '</a></div>';
|
||||
if ($amount > 0 && $pay_modo !== 'points' && function_exists('aksrt_allow_guest_content_pay') && aksrt_allow_guest_content_pay($post)) {
|
||||
echo '<div class="mt10"><button type="button" class="but padding-lg btn-block ' . esc_attr($theme) . '" data-aksrt-pay-content="' . esc_attr((string) $post->ID) . '"><i class="fa fa-shopping-cart fa-fw" aria-hidden="true"></i> ' . esc_html(sprintf(/* translators: price */ __('游客购买(%s)', 'aksrt'), $price_text)) . '</button></div>';
|
||||
}
|
||||
} else {
|
||||
echo '<div class="mt10"><button type="button" class="but padding-lg btn-block ' . esc_attr($theme) . ' cashier-link" data-aksrt-pay-content="' . esc_attr((string) $post->ID) . '"><i class="fa fa-shopping-cart fa-fw" aria-hidden="true"></i> ' . esc_html(sprintf(/* translators: price */ __('立即购买(%s)', 'aksrt'), $price_text)) . '</button></div>';
|
||||
}
|
||||
if ($demo !== '') {
|
||||
echo '<div class="mt10"><a class="but hollow btn-block" href="' . esc_url($demo) . '" target="_blank" rel="noopener noreferrer"><i class="fa fa-external-link" aria-hidden="true"></i> ' . esc_html__('演示地址', 'aksrt') . '</a></div>';
|
||||
}
|
||||
echo '</div>';
|
||||
if ($sales > 0) {
|
||||
echo '<badge class="img-badge hot jb-blue px12">' . esc_html(sprintf(/* translators: sales count */ __('已售 %s', 'aksrt'), (string) $sales)) . '</badge>';
|
||||
}
|
||||
echo '</div>';
|
||||
?>
|
||||
<section class="aksrt-post-pay-card <?php echo esc_attr($theme); ?><?php echo $has_access ? ' is-unlocked' : ''; ?>" aria-label="<?php esc_attr_e('付费购买', 'aksrt'); ?>">
|
||||
<header class="aksrt-post-pay-card__header">
|
||||
<span class="aksrt-post-pay-card__type"><?php echo esc_html($type_label); ?></span>
|
||||
<?php if ($sales > 0) : ?>
|
||||
<span class="aksrt-post-pay-card__sales"><?php echo esc_html(sprintf(__('已售 %s', 'aksrt'), number_format_i18n($sales))); ?></span>
|
||||
<?php endif; ?>
|
||||
</header>
|
||||
<div class="aksrt-post-pay-card__price-row">
|
||||
<?php if ($has_access) : ?>
|
||||
<strong class="aksrt-post-pay-card__status"><i class="fa fa-check-circle" aria-hidden="true"></i><?php esc_html_e('已解锁', 'aksrt'); ?></strong>
|
||||
<?php elseif ($amount > 0) : ?>
|
||||
<strong class="aksrt-post-pay-card__price"><?php echo esc_html($format($amount)); ?></strong>
|
||||
<?php if ($show_original) : ?><del><?php echo esc_html($format($original)); ?></del><?php endif; ?>
|
||||
<?php else : ?>
|
||||
<strong class="aksrt-post-pay-card__price"><?php esc_html_e('免费', 'aksrt'); ?></strong>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php if ($show_member_discount) : ?>
|
||||
<p class="aksrt-post-pay-card__hint"><?php echo esc_html(sprintf(__('已应用会员优惠,原价 %s', 'aksrt'), $format($base))); ?></p>
|
||||
<?php endif; ?>
|
||||
<div class="aksrt-post-pay-card__actions">
|
||||
<?php if ($has_access) : ?>
|
||||
<a class="but btn-block <?php echo esc_attr($theme); ?>" href="#posts-pay"><i class="fa fa-arrow-down" aria-hidden="true"></i><?php echo esc_html($action_label); ?></a>
|
||||
<?php elseif (!$is_logged_in) : ?>
|
||||
<a class="but btn-block jb-blue" href="<?php echo esc_url($login_url); ?>"><i class="fa fa-sign-in" aria-hidden="true"></i><?php esc_html_e('登录后购买', 'aksrt'); ?></a>
|
||||
<?php if ($amount > 0 && $pay_modo !== 'points' && function_exists('aksrt_allow_guest_content_pay') && aksrt_allow_guest_content_pay($post)) : ?>
|
||||
<button type="button" class="but btn-block <?php echo esc_attr($theme); ?> cashier-link" data-aksrt-pay-content="<?php echo esc_attr((string) $post->ID); ?>"><i class="fa fa-shopping-cart" aria-hidden="true"></i><?php echo esc_html(sprintf(__('游客购买(%s)', 'aksrt'), $format($amount))); ?></button>
|
||||
<?php endif; ?>
|
||||
<?php else : ?>
|
||||
<button type="button" class="but btn-block <?php echo esc_attr($theme); ?> cashier-link" data-aksrt-pay-content="<?php echo esc_attr((string) $post->ID); ?>"><i class="fa fa-shopping-cart" aria-hidden="true"></i><?php echo esc_html(sprintf(__('立即购买(%s)', 'aksrt'), $format($amount))); ?></button>
|
||||
<?php endif; ?>
|
||||
<?php if ($demo !== '') : ?>
|
||||
<a class="but hollow btn-block" href="<?php echo esc_url($demo); ?>" target="_blank" rel="noopener noreferrer"><i class="fa fa-external-link" aria-hidden="true"></i><?php esc_html_e('演示地址', 'aksrt'); ?></a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</section>
|
||||
<?php
|
||||
return (string) ob_get_clean();
|
||||
}
|
||||
|
||||
@@ -144,6 +145,7 @@ class WidgetPostPayBox extends \WP_Widget {
|
||||
if ($html === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
@@ -151,7 +153,7 @@ class WidgetPostPayBox extends \WP_Widget {
|
||||
|
||||
public function form($instance): void {
|
||||
$theme = sanitize_html_class((string) ($instance['theme'] ?? 'jb-red'));
|
||||
$opts = [
|
||||
$options = [
|
||||
'jb-red' => __('红', 'aksrt'),
|
||||
'jb-yellow' => __('橙', 'aksrt'),
|
||||
'jb-blue' => __('蓝', 'aksrt'),
|
||||
@@ -161,18 +163,18 @@ class WidgetPostPayBox extends \WP_Widget {
|
||||
'jb-vip2' => __('会员灰', 'aksrt'),
|
||||
];
|
||||
?>
|
||||
<p class="description"><?php esc_html_e('仅在文章/帖子详情侧栏显示当前内容的付费购买卡。', 'aksrt'); ?></p>
|
||||
<p class="description"><?php esc_html_e('仅在文章页 - 侧边栏显示付费文章的紧凑购买卡。', 'aksrt'); ?></p>
|
||||
<p>
|
||||
<label><?php esc_html_e('色彩主题', 'aksrt'); ?>
|
||||
<select class="widefat" name="<?php echo esc_attr($this->get_field_name('theme')); ?>">
|
||||
<?php foreach ($opts as $k => $label) : ?>
|
||||
<option value="<?php echo esc_attr($k); ?>" <?php selected($theme, $k); ?>><?php echo esc_html($label); ?></option>
|
||||
<?php foreach ($options as $value => $label) : ?>
|
||||
<option value="<?php echo esc_attr($value); ?>" <?php selected($theme, $value); ?>><?php echo esc_html($label); ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
</p>
|
||||
<?php $this->render_common_form_section($instance); ?>
|
||||
<?php
|
||||
$this->render_common_form_section($instance);
|
||||
}
|
||||
|
||||
public function update($new_instance, $old_instance): array {
|
||||
@@ -181,6 +183,7 @@ class WidgetPostPayBox extends \WP_Widget {
|
||||
if (!in_array($theme, $allowed, true)) {
|
||||
$theme = 'jb-red';
|
||||
}
|
||||
$business = ['theme' => $theme]; return $this->update_common_fields($new_instance, $old_instance, $business);
|
||||
|
||||
return $this->update_common_fields($new_instance, $old_instance, ['theme' => $theme]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* In-article / topic TOC shell (zibll widget_ui_posts_navs parity).
|
||||
*
|
||||
* Shell matches the built-in single sidebar TOC so page-template.js can fill
|
||||
* `.posts-nav-box.toc-widget > .toc-list` from `[data-nav]` headings.
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
namespace AKSRT\Widgets;
|
||||
|
||||
use AKSRT\Widgets\Traits\CommonWidgetFields;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
class WidgetPostsNavs extends \WP_Widget {
|
||||
use CommonWidgetFields;
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
'aksrt_posts_navs',
|
||||
esc_html__('AKSRT 文章目录树', 'aksrt'),
|
||||
[
|
||||
'description' => esc_html__('显示当前文章/帖子标题目录,非详情页不输出。标题超过 2 个才填充。', 'aksrt'),
|
||||
'classname' => 'widget_aksrt_posts_navs',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function widget($args, $instance): void {
|
||||
if (!is_singular(['post', 'page'])) {
|
||||
if (current_user_can('manage_options')) {
|
||||
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo function_exists('aksrt_get_widget_null')
|
||||
? aksrt_get_widget_null(__('目录树仅在文章页面显示', 'aksrt')) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
: '<p class="widget-empty">' . esc_html__('目录树仅在详情页显示', 'aksrt') . '</p>';
|
||||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
$title = sanitize_text_field((string) ($instance['title'] ?? __('文章目录', 'aksrt')));
|
||||
$mini_title = sanitize_text_field((string) ($instance['mini_title'] ?? ''));
|
||||
$affix = function_exists('aksrt_widget_affix_attrs') ? aksrt_widget_affix_attrs($instance) : ['class' => '', 'attr' => ''];
|
||||
|
||||
$data_title = $title;
|
||||
if ($mini_title !== '') {
|
||||
$data_title .= ($data_title !== '' ? ' · ' : '') . $mini_title;
|
||||
}
|
||||
if ($data_title === '') {
|
||||
$data_title = __('文章目录', 'aksrt');
|
||||
}
|
||||
|
||||
// Mirror aksrt-single-render built-in TOC shell so page-template.js binds.
|
||||
$classes = 'zib-widget toc-widget posts-nav-box' . $affix['class'];
|
||||
|
||||
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
printf(
|
||||
'<div class="%s"%s data-title="%s" aria-label="%s">',
|
||||
esc_attr($classes),
|
||||
$affix['attr'],
|
||||
esc_attr($data_title),
|
||||
esc_attr($data_title)
|
||||
);
|
||||
echo '<h3 class="widget-title toc-widget__title">' . esc_html($data_title) . '</h3>';
|
||||
echo '<ol class="toc-list"></ol>';
|
||||
echo '</div>';
|
||||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
public function form($instance): void {
|
||||
$title = (string) ($instance['title'] ?? __('文章目录', 'aksrt'));
|
||||
$mini_title = (string) ($instance['mini_title'] ?? '');
|
||||
?>
|
||||
<p>
|
||||
<label for="<?php echo esc_attr($this->get_field_id('title')); ?>"><?php esc_html_e('标题:', 'aksrt'); ?></label>
|
||||
<input class="widefat" type="text" id="<?php echo esc_attr($this->get_field_id('title')); ?>" name="<?php echo esc_attr($this->get_field_name('title')); ?>" value="<?php echo esc_attr($title); ?>">
|
||||
</p>
|
||||
<p>
|
||||
<label for="<?php echo esc_attr($this->get_field_id('mini_title')); ?>"><?php esc_html_e('副标题:', 'aksrt'); ?></label>
|
||||
<input class="widefat" type="text" id="<?php echo esc_attr($this->get_field_id('mini_title')); ?>" name="<?php echo esc_attr($this->get_field_name('mini_title')); ?>" value="<?php echo esc_attr($mini_title); ?>">
|
||||
</p>
|
||||
<p class="description"><?php esc_html_e('请添加到文章/帖子侧栏。正文标题 ≥3 个时由前端填充目录;无标题则自动移除。', 'aksrt'); ?></p>
|
||||
<?php $this->render_common_form_section($instance); ?>
|
||||
<?php
|
||||
}
|
||||
|
||||
public function update($new_instance, $old_instance): array {
|
||||
return $this->update_common_fields($new_instance, $old_instance, [
|
||||
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
|
||||
'mini_title' => sanitize_text_field((string) ($new_instance['mini_title'] ?? '')),
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -1,251 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Pricing plan cards (zibll zib_widget_pricing_plans lite).
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
namespace AKSRT\Widgets;
|
||||
|
||||
use AKSRT\Widgets\Traits\CommonWidgetFields;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
class WidgetPricingPlans extends \WP_Widget {
|
||||
use CommonWidgetFields;
|
||||
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
'aksrt_pricing_plans',
|
||||
esc_html__('AKSRT 价格方案', 'aksrt'),
|
||||
[
|
||||
'description' => esc_html__('多列定价卡片 + 信任条(落地页/VIP 介绍)。', 'aksrt'),
|
||||
'classname' => 'widget_aksrt_pricing_plans',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function widget($args, $instance): void {
|
||||
$plans = $this->normalize_plans($instance['lists'] ?? $instance['plans'] ?? []);
|
||||
if (!$plans) {
|
||||
if (current_user_can('manage_options')) {
|
||||
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo function_exists('aksrt_get_widget_null')
|
||||
? aksrt_get_widget_null(__('请配置至少一个价格方案', 'aksrt')) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
: '<p class="widget-empty">' . esc_html__('请配置至少一个价格方案', 'aksrt') . '</p>';
|
||||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
return;
|
||||
}
|
||||
$trust = $this->normalize_trust($instance['trust_items'] ?? []);
|
||||
$currency = sanitize_text_field((string) ($instance['currency'] ?? '¥'));
|
||||
if ($currency === '') {
|
||||
$currency = '¥';
|
||||
}
|
||||
|
||||
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
$title = apply_filters('widget_title', (string) ($instance['title'] ?? ''), $instance, $this->id_base);
|
||||
if ($title !== '') {
|
||||
echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
echo '<div class="aksrt-pricing">';
|
||||
echo '<div class="aksrt-pricing__cards">';
|
||||
$pi = 0;
|
||||
foreach ($plans as $plan) {
|
||||
$accent = sanitize_key((string) ($plan['color'] ?? 'blue'));
|
||||
if (!in_array($accent, ['pink', 'purple', 'blue', 'green', 'yellow', 'cyan'], true)) {
|
||||
$accent = 'blue';
|
||||
}
|
||||
$featured = !empty($plan['is_featured']);
|
||||
$pack = function_exists('aksrt_obs_animate_attrs') ? aksrt_obs_animate_attrs('slideup', false, $pi * 0.12) : ['class' => '', 'attr' => ''];
|
||||
$class = 'aksrt-pricing__card accent-' . $accent . ($featured ? ' is-featured' : '') . $pack['class'];
|
||||
echo '<div class="' . esc_attr(trim($class)) . '"' . $pack['attr'] . '>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
$pi++;
|
||||
if ($featured || !empty($plan['tag'])) {
|
||||
$badge = (string) ($plan['tag'] !== '' ? $plan['tag'] : __('推荐', 'aksrt'));
|
||||
echo '<span class="aksrt-pricing__badge">' . esc_html($badge) . '</span>';
|
||||
}
|
||||
echo '<h3 class="aksrt-pricing__name">' . esc_html($plan['name']) . '</h3>';
|
||||
if ($plan['desc'] !== '') {
|
||||
echo '<p class="aksrt-pricing__desc">' . esc_html($plan['desc']) . '</p>';
|
||||
}
|
||||
$price = (float) $plan['price'];
|
||||
$show = (float) $plan['show_price'];
|
||||
echo '<div class="aksrt-pricing__price">';
|
||||
echo '<span class="aksrt-pricing__main"><span class="aksrt-pricing__mark">' . esc_html($currency) . '</span>' . esc_html($this->fmt($price)) . '</span>';
|
||||
if ($show > $price && $show > 0) {
|
||||
echo '<span class="aksrt-pricing__original"><span class="aksrt-pricing__mark">' . esc_html($currency) . '</span>' . esc_html($this->fmt($show)) . '</span>';
|
||||
}
|
||||
echo '</div>';
|
||||
if ($plan['features']) {
|
||||
echo '<ul class="aksrt-pricing__features">';
|
||||
foreach ($plan['features'] as $ft) {
|
||||
echo '<li><i class="fa fa-check" aria-hidden="true"></i><span>' . esc_html($ft) . '</span></li>';
|
||||
}
|
||||
echo '</ul>';
|
||||
}
|
||||
if ($plan['btn_text'] !== '' && $plan['btn_url'] !== '') {
|
||||
$url = $plan['btn_url'];
|
||||
if (!empty($instance['go_link']) && function_exists('aksrt_go_link')) {
|
||||
$url = aksrt_go_link($url, true);
|
||||
}
|
||||
echo '<div class="aksrt-pricing__action"><a class="aksrt-pricing__btn" href="' . esc_url($url) . '">' . esc_html($plan['btn_text']) . '</a></div>';
|
||||
}
|
||||
echo '</div>';
|
||||
}
|
||||
echo '</div>';
|
||||
|
||||
if ($trust) {
|
||||
echo '<div class="aksrt-pricing__trust">';
|
||||
foreach ($trust as $ti) {
|
||||
echo '<div class="aksrt-pricing__trust-item">';
|
||||
if ($ti['icon'] !== '') {
|
||||
echo '<i class="' . esc_attr($ti['icon']) . '" aria-hidden="true"></i>';
|
||||
}
|
||||
echo '<div><strong>' . esc_html($ti['title']) . '</strong>';
|
||||
if ($ti['desc'] !== '') {
|
||||
echo '<span>' . esc_html($ti['desc']) . '</span>';
|
||||
}
|
||||
echo '</div></div>';
|
||||
}
|
||||
echo '</div>';
|
||||
}
|
||||
echo '</div>';
|
||||
|
||||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
private function fmt(float $n): string {
|
||||
if (abs($n - round($n)) < 0.001) {
|
||||
return (string) (int) round($n);
|
||||
}
|
||||
return number_format($n, 2, '.', '');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $raw
|
||||
* @return array<int, array<string,mixed>>
|
||||
*/
|
||||
private function normalize_plans($raw): array {
|
||||
if (is_string($raw)) {
|
||||
$decoded = json_decode($raw, true);
|
||||
$raw = is_array($decoded) ? $decoded : [];
|
||||
}
|
||||
if (!is_array($raw)) {
|
||||
return [];
|
||||
}
|
||||
$out = [];
|
||||
foreach ($raw as $row) {
|
||||
if (!is_array($row)) {
|
||||
continue;
|
||||
}
|
||||
$name = sanitize_text_field((string) ($row['name'] ?? $row['title'] ?? ''));
|
||||
if ($name === '') {
|
||||
continue;
|
||||
}
|
||||
$intro = (string) ($row['intro'] ?? $row['features'] ?? '');
|
||||
if (is_array($row['features'] ?? null)) {
|
||||
$features = array_values(array_filter(array_map('sanitize_text_field', $row['features'])));
|
||||
} else {
|
||||
$features = array_values(array_filter(array_map('sanitize_text_field', preg_split('/\r\n|\r|\n/', $intro) ?: [])));
|
||||
}
|
||||
$btn = is_array($row['btn'] ?? null) ? $row['btn'] : [];
|
||||
$out[] = [
|
||||
'name' => $name,
|
||||
'desc' => sanitize_text_field((string) ($row['desc'] ?? '')),
|
||||
'price' => (float) ($row['price'] ?? 0),
|
||||
'show_price' => (float) ($row['show_price'] ?? 0),
|
||||
'color' => sanitize_key((string) ($row['color'] ?? 'blue')),
|
||||
'is_featured' => !empty($row['is_featured']) ? 1 : 0,
|
||||
'tag' => sanitize_text_field((string) ($row['tag'] ?? '')),
|
||||
'features' => $features,
|
||||
'btn_text' => sanitize_text_field((string) ($row['btn_text'] ?? $btn['text'] ?? '')),
|
||||
'btn_url' => esc_url_raw(trim((string) ($row['btn_url'] ?? $btn['url'] ?? ''))),
|
||||
];
|
||||
if (count($out) >= 6) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $raw
|
||||
* @return array<int, array{title:string,desc:string,icon:string}>
|
||||
*/
|
||||
private function normalize_trust($raw): array {
|
||||
if (is_string($raw)) {
|
||||
$decoded = json_decode($raw, true);
|
||||
$raw = is_array($decoded) ? $decoded : [];
|
||||
}
|
||||
if (!is_array($raw)) {
|
||||
return [];
|
||||
}
|
||||
$out = [];
|
||||
foreach ($raw as $row) {
|
||||
if (!is_array($row)) {
|
||||
continue;
|
||||
}
|
||||
$title = sanitize_text_field((string) ($row['title'] ?? ''));
|
||||
if ($title === '') {
|
||||
continue;
|
||||
}
|
||||
$out[] = [
|
||||
'title' => $title,
|
||||
'desc' => sanitize_text_field((string) ($row['desc'] ?? '')),
|
||||
'icon' => sanitize_text_field((string) ($row['icon'] ?? 'fa fa-check-circle')),
|
||||
];
|
||||
if (count($out) >= 6) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
public function form($instance): void {
|
||||
$plans = is_string($instance['lists'] ?? null)
|
||||
? (string) $instance['lists']
|
||||
: wp_json_encode($this->normalize_plans($instance['lists'] ?? []), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
$trust = is_string($instance['trust_items'] ?? null)
|
||||
? (string) $instance['trust_items']
|
||||
: wp_json_encode($this->normalize_trust($instance['trust_items'] ?? []), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
?>
|
||||
<p>
|
||||
<label><?php esc_html_e('标题', 'aksrt'); ?>
|
||||
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('title')); ?>" value="<?php echo esc_attr((string) ($instance['title'] ?? '')); ?>">
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label><?php esc_html_e('货币符号', 'aksrt'); ?>
|
||||
<input class="tiny-text" type="text" name="<?php echo esc_attr($this->get_field_name('currency')); ?>" value="<?php echo esc_attr((string) ($instance['currency'] ?? '¥')); ?>">
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label><?php esc_html_e('方案 JSON', 'aksrt'); ?>
|
||||
<textarea class="widefat" rows="8" name="<?php echo esc_attr($this->get_field_name('lists')); ?>" placeholder='[{"name":"Pro","price":99,"show_price":199,"desc":"适合创作者","features":["无限下载","优先支持"],"btn_text":"立即开通","btn_url":"#","is_featured":1,"color":"pink"}]'><?php echo esc_textarea((string) $plans); ?></textarea>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label><?php esc_html_e('信任条 JSON', 'aksrt'); ?>
|
||||
<textarea class="widefat" rows="3" name="<?php echo esc_attr($this->get_field_name('trust_items')); ?>"><?php echo esc_textarea((string) $trust); ?></textarea>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label><input type="checkbox" name="<?php echo esc_attr($this->get_field_name('go_link')); ?>" value="1" <?php checked(!empty($instance['go_link'])); ?>> <?php esc_html_e('外链跳转页', 'aksrt'); ?></label>
|
||||
</p>
|
||||
<?php
|
||||
$this->render_common_form_section($instance);
|
||||
}
|
||||
|
||||
public function update($new_instance, $old_instance): array {
|
||||
$business = [
|
||||
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
|
||||
'currency' => sanitize_text_field((string) ($new_instance['currency'] ?? '¥')),
|
||||
'lists' => $this->normalize_plans($new_instance['lists'] ?? []),
|
||||
'trust_items' => $this->normalize_trust($new_instance['trust_items'] ?? []),
|
||||
'go_link' => !empty($new_instance['go_link']) ? 1 : 0,
|
||||
]; return $this->update_common_fields($new_instance, $old_instance, $business);
|
||||
}
|
||||
}
|
||||
@@ -1,166 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Testimonials / virtual reviews carousel (zibll zib_widget_reviews lite).
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
namespace AKSRT\Widgets;
|
||||
|
||||
use AKSRT\Widgets\Traits\CommonWidgetFields;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
class WidgetReviews extends \WP_Widget {
|
||||
use CommonWidgetFields;
|
||||
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
'aksrt_reviews',
|
||||
esc_html__('AKSRT 用户评价', 'aksrt'),
|
||||
[
|
||||
'description' => esc_html__('虚拟用户评价卡片横向滚动。', 'aksrt'),
|
||||
'classname' => 'widget_aksrt_reviews',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function widget($args, $instance): void {
|
||||
$reviews = $this->normalize($instance['reviews'] ?? []);
|
||||
if (!$reviews) {
|
||||
if (current_user_can('manage_options')) {
|
||||
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo function_exists('aksrt_get_widget_null')
|
||||
? aksrt_get_widget_null(__('请添加至少一条评价', 'aksrt')) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
: '<p class="widget-empty">' . esc_html__('请添加至少一条评价', 'aksrt') . '</p>';
|
||||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
return;
|
||||
}
|
||||
$pc = max(1, min(6, (float) ($instance['desktop_per_view'] ?? 3)));
|
||||
$m = max(1, min(3, (float) ($instance['mobile_per_view'] ?? 1.2)));
|
||||
|
||||
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
$title = apply_filters('widget_title', (string) ($instance['title'] ?? ''), $instance, $this->id_base);
|
||||
if ($title !== '') {
|
||||
echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
$style = sprintf('--aksrt-rev-pc:%s;--aksrt-rev-m:%s;', rtrim(rtrim(number_format($pc, 2, '.', ''), '0'), '.'), rtrim(rtrim(number_format($m, 2, '.', ''), '0'), '.'));
|
||||
echo '<div class="aksrt-reviews" style="' . esc_attr($style) . '">';
|
||||
echo '<div class="swiper-container swiper-scroll aksrt-reviews-swiper">';
|
||||
echo '<div class="swiper-wrapper aksrt-reviews__track">';
|
||||
foreach ($reviews as $r) {
|
||||
echo '<div class="swiper-slide aksrt-reviews__slide"><div class="aksrt-reviews__card" data-aksrt-tilt data-tilt-max="8">';
|
||||
echo '<div class="aksrt-reviews__head">';
|
||||
if ($r['avatar'] !== '') {
|
||||
$ph = get_template_directory_uri() . '/assets/img/placeholder.svg';
|
||||
echo '<img class="aksrt-reviews__avatar aksrt-lazy lazyload" data-src="' . esc_url($r['avatar']) . '" src="' . esc_url($ph) . '" alt="" loading="lazy" width="44" height="44" />';
|
||||
} else {
|
||||
echo '<span class="aksrt-reviews__avatar is-placeholder"><i class="fa fa-user" aria-hidden="true"></i></span>';
|
||||
}
|
||||
echo '<div class="aksrt-reviews__meta">';
|
||||
if ($r['name'] !== '') {
|
||||
$name_html = esc_html($r['name']);
|
||||
if ($r['url'] !== '') {
|
||||
$name_html = '<a href="' . esc_url($r['url']) . '" target="_blank" rel="noopener noreferrer">' . $name_html . '</a>';
|
||||
}
|
||||
echo '<div class="aksrt-reviews__name">' . $name_html . '</div>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
if ($r['role'] !== '') {
|
||||
echo '<div class="aksrt-reviews__role">' . esc_html($r['role']) . '</div>';
|
||||
}
|
||||
echo '</div></div>';
|
||||
echo '<div class="aksrt-reviews__stars" aria-label="' . esc_attr(sprintf(__('%d 星', 'aksrt'), $r['rating'])) . '">';
|
||||
for ($i = 1; $i <= 5; $i++) {
|
||||
echo '<i class="fa fa-star' . ($i <= $r['rating'] ? ' is-on' : ' is-off') . '" aria-hidden="true"></i>';
|
||||
}
|
||||
echo '</div>';
|
||||
if ($r['content'] !== '') {
|
||||
echo '<p class="aksrt-reviews__content">' . nl2br(esc_html($r['content'])) . '</p>';
|
||||
}
|
||||
echo '<i class="fa fa-quote-right aksrt-reviews__quote" aria-hidden="true"></i>';
|
||||
echo '</div></div>';
|
||||
}
|
||||
echo '</div>';
|
||||
echo '<div class="swiper-button-prev" aria-label="' . esc_attr__('上一页', 'aksrt') . '"></div>';
|
||||
echo '<div class="swiper-button-next" aria-label="' . esc_attr__('下一页', 'aksrt') . '"></div>';
|
||||
echo '</div></div>';
|
||||
|
||||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $raw
|
||||
* @return array<int, array{name:string,role:string,avatar:string,content:string,rating:int,url:string}>
|
||||
*/
|
||||
private function normalize($raw): array {
|
||||
if (is_string($raw)) {
|
||||
$decoded = json_decode($raw, true);
|
||||
$raw = is_array($decoded) ? $decoded : [];
|
||||
}
|
||||
if (!is_array($raw)) {
|
||||
return [];
|
||||
}
|
||||
$out = [];
|
||||
foreach ($raw as $row) {
|
||||
if (!is_array($row)) {
|
||||
continue;
|
||||
}
|
||||
$name = sanitize_text_field((string) ($row['name'] ?? ''));
|
||||
$content = sanitize_textarea_field((string) ($row['content'] ?? ''));
|
||||
if ($name === '' && $content === '') {
|
||||
continue;
|
||||
}
|
||||
$out[] = [
|
||||
'name' => $name,
|
||||
'role' => sanitize_text_field((string) ($row['role'] ?? '')),
|
||||
'avatar' => esc_url_raw(trim((string) ($row['avatar'] ?? ''))),
|
||||
'content' => $content,
|
||||
'rating' => max(0, min(5, (int) ($row['rating'] ?? 5))),
|
||||
'url' => esc_url_raw(trim((string) ($row['url'] ?? ''))),
|
||||
];
|
||||
if (count($out) >= 20) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
public function form($instance): void {
|
||||
$json = is_string($instance['reviews'] ?? null)
|
||||
? (string) $instance['reviews']
|
||||
: wp_json_encode($this->normalize($instance['reviews'] ?? []), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
?>
|
||||
<p>
|
||||
<label><?php esc_html_e('标题', 'aksrt'); ?>
|
||||
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('title')); ?>" value="<?php echo esc_attr((string) ($instance['title'] ?? '')); ?>">
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label><?php esc_html_e('评价 JSON', 'aksrt'); ?>
|
||||
<textarea class="widefat" rows="8" name="<?php echo esc_attr($this->get_field_name('reviews')); ?>" placeholder='[{"name":"张三","role":"站长","rating":5,"content":"很好用","avatar":""}]'><?php echo esc_textarea((string) $json); ?></textarea>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label><?php esc_html_e('PC 每屏', 'aksrt'); ?>
|
||||
<input class="tiny-text" type="number" min="1" max="6" step="0.1" name="<?php echo esc_attr($this->get_field_name('desktop_per_view')); ?>" value="<?php echo esc_attr((string) ($instance['desktop_per_view'] ?? '3')); ?>">
|
||||
</label>
|
||||
<label><?php esc_html_e('移动每屏', 'aksrt'); ?>
|
||||
<input class="tiny-text" type="number" min="1" max="3" step="0.1" name="<?php echo esc_attr($this->get_field_name('mobile_per_view')); ?>" value="<?php echo esc_attr((string) ($instance['mobile_per_view'] ?? '1.2')); ?>">
|
||||
</label>
|
||||
</p>
|
||||
<?php
|
||||
$this->render_common_form_section($instance);
|
||||
}
|
||||
|
||||
public function update($new_instance, $old_instance): array {
|
||||
$business = [
|
||||
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
|
||||
'reviews' => $this->normalize($new_instance['reviews'] ?? []),
|
||||
'desktop_per_view' => max(1, min(6, (float) ($new_instance['desktop_per_view'] ?? 3))),
|
||||
'mobile_per_view' => max(1, min(3, (float) ($new_instance['mobile_per_view'] ?? 1.2))),
|
||||
]; return $this->update_common_fields($new_instance, $old_instance, $business);
|
||||
}
|
||||
}
|
||||
@@ -1,194 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Stats number cards (zibll zib_widget_stats lite).
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
namespace AKSRT\Widgets;
|
||||
|
||||
use AKSRT\Widgets\Traits\CommonWidgetFields;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
class WidgetStats extends \WP_Widget {
|
||||
use CommonWidgetFields;
|
||||
|
||||
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
'aksrt_stats',
|
||||
esc_html__('AKSRT 数据统计', 'aksrt'),
|
||||
[
|
||||
'description' => esc_html__('数字统计卡片网格(站点数据展示)。', 'aksrt'),
|
||||
'classname' => 'widget_aksrt_stats',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
public function widget($args, $instance): void {
|
||||
$stats = $this->normalize_stats($instance['stats'] ?? []);
|
||||
if (!$stats) {
|
||||
if (current_user_can('manage_options')) {
|
||||
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
echo function_exists('aksrt_get_widget_null')
|
||||
? aksrt_get_widget_null(__('请配置至少一项统计数据', 'aksrt')) // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
: '<p class="widget-empty">' . esc_html__('请配置至少一项统计数据', 'aksrt') . '</p>';
|
||||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
return;
|
||||
}
|
||||
$cols = max(1, min(4, (int) ($instance['columns'] ?? 4)));
|
||||
$m_cols = max(1, min(3, (int) ($instance['mobile_columns'] ?? 2)));
|
||||
$intro_title = trim((string) ($instance['intro_title'] ?? ''));
|
||||
$intro_sub = trim((string) ($instance['intro_subtitle'] ?? ''));
|
||||
|
||||
echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
$title = apply_filters('widget_title', (string) ($instance['title'] ?? ''), $instance, $this->id_base);
|
||||
if ($title !== '') {
|
||||
echo $args['before_title'] . esc_html($title) . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
$style = sprintf('--aksrt-stats-pc:%d;--aksrt-stats-m:%d;', $cols, $m_cols);
|
||||
echo '<div class="aksrt-stats" style="' . esc_attr($style) . '">';
|
||||
if ($intro_title !== '' || $intro_sub !== '') {
|
||||
echo '<div class="aksrt-stats__intro">';
|
||||
if ($intro_title !== '') {
|
||||
echo '<h2 class="aksrt-stats__intro-title">' . esc_html($intro_title) . '</h2>';
|
||||
}
|
||||
if ($intro_sub !== '') {
|
||||
echo '<p class="aksrt-stats__intro-sub">' . esc_html($intro_sub) . '</p>';
|
||||
}
|
||||
echo '</div>';
|
||||
}
|
||||
echo '<div class="aksrt-stats__grid">';
|
||||
$palette = ['pink', 'purple', 'orange', 'blue', 'green'];
|
||||
$i = 0;
|
||||
foreach ($stats as $s) {
|
||||
$color = sanitize_key((string) ($s['color'] ?? 'auto'));
|
||||
if ($color === '' || $color === 'auto') {
|
||||
$color = $palette[$i % count($palette)];
|
||||
}
|
||||
$i++;
|
||||
echo '<div class="aksrt-stats__item color-' . esc_attr($color) . '" data-aksrt-animate="fade-up">';
|
||||
if ($s['icon'] !== '') {
|
||||
echo '<div class="aksrt-stats__icon"><i class="' . esc_attr($s['icon']) . '" aria-hidden="true"></i></div>';
|
||||
}
|
||||
echo '<div class="aksrt-stats__value">';
|
||||
if ($s['prefix'] !== '') {
|
||||
echo '<span class="aksrt-stats__prefix">' . esc_html($s['prefix']) . '</span>';
|
||||
}
|
||||
// Wire effects.js count-up when number is numeric.
|
||||
$raw_num = preg_replace('/[^\d.]/', '', (string) $s['number']);
|
||||
$is_numeric = $raw_num !== '' && is_numeric($raw_num);
|
||||
if ($is_numeric) {
|
||||
$dec = 0;
|
||||
if (str_contains((string) $raw_num, '.')) {
|
||||
$dec = strlen(substr(strrchr((string) $raw_num, '.'), 1));
|
||||
}
|
||||
printf(
|
||||
'<span class="aksrt-stats__number" data-aksrt-countup data-target="%s" data-duration="1600" data-decimals="%d">0</span>',
|
||||
esc_attr((string) $raw_num),
|
||||
(int) $dec
|
||||
);
|
||||
} else {
|
||||
echo '<span class="aksrt-stats__number" data-target="' . esc_attr($s['number']) . '">' . esc_html($s['number']) . '</span>';
|
||||
}
|
||||
if ($s['unit'] !== '') {
|
||||
echo '<span class="aksrt-stats__unit">' . esc_html($s['unit']) . '</span>';
|
||||
}
|
||||
echo '</div>';
|
||||
if ($s['label'] !== '') {
|
||||
echo '<div class="aksrt-stats__label">' . esc_html($s['label']) . '</div>';
|
||||
}
|
||||
echo '</div>';
|
||||
}
|
||||
echo '</div></div>';
|
||||
|
||||
echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $raw
|
||||
* @return array<int, array{number:string,label:string,unit:string,prefix:string,icon:string,color:string}>
|
||||
*/
|
||||
private function normalize_stats($raw): array {
|
||||
if (is_string($raw)) {
|
||||
$decoded = json_decode($raw, true);
|
||||
$raw = is_array($decoded) ? $decoded : [];
|
||||
}
|
||||
if (!is_array($raw)) {
|
||||
return [];
|
||||
}
|
||||
$out = [];
|
||||
foreach ($raw as $row) {
|
||||
if (!is_array($row)) {
|
||||
continue;
|
||||
}
|
||||
$number = sanitize_text_field((string) ($row['number'] ?? ''));
|
||||
if ($number === '') {
|
||||
continue;
|
||||
}
|
||||
$out[] = [
|
||||
'number' => $number,
|
||||
'label' => sanitize_text_field((string) ($row['label'] ?? '')),
|
||||
'unit' => sanitize_text_field((string) ($row['unit'] ?? '')),
|
||||
'prefix' => sanitize_text_field((string) ($row['prefix'] ?? '')),
|
||||
'icon' => sanitize_text_field((string) ($row['icon'] ?? '')),
|
||||
'color' => sanitize_key((string) ($row['color'] ?? 'auto')),
|
||||
];
|
||||
if (count($out) >= 12) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
public function form($instance): void {
|
||||
$stats = is_string($instance['stats'] ?? null)
|
||||
? (string) $instance['stats']
|
||||
: wp_json_encode($this->normalize_stats($instance['stats'] ?? []), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
?>
|
||||
<p>
|
||||
<label><?php esc_html_e('标题', 'aksrt'); ?>
|
||||
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('title')); ?>" value="<?php echo esc_attr((string) ($instance['title'] ?? '')); ?>">
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label><?php esc_html_e('数据 JSON', 'aksrt'); ?>
|
||||
<textarea class="widefat" rows="6" name="<?php echo esc_attr($this->get_field_name('stats')); ?>" placeholder='[{"number":"1280","label":"用户","unit":"+","icon":"fas fa-users","color":"blue"}]'><?php echo esc_textarea((string) $stats); ?></textarea>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label><?php esc_html_e('PC 列数', 'aksrt'); ?>
|
||||
<input class="tiny-text" type="number" min="1" max="4" name="<?php echo esc_attr($this->get_field_name('columns')); ?>" value="<?php echo esc_attr((string) ((int) ($instance['columns'] ?? 4))); ?>">
|
||||
</label>
|
||||
<label><?php esc_html_e('移动列数', 'aksrt'); ?>
|
||||
<input class="tiny-text" type="number" min="1" max="3" name="<?php echo esc_attr($this->get_field_name('mobile_columns')); ?>" value="<?php echo esc_attr((string) ((int) ($instance['mobile_columns'] ?? 2))); ?>">
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label><?php esc_html_e('介绍标题', 'aksrt'); ?>
|
||||
<input class="widefat" type="text" name="<?php echo esc_attr($this->get_field_name('intro_title')); ?>" value="<?php echo esc_attr((string) ($instance['intro_title'] ?? '')); ?>">
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label><?php esc_html_e('介绍副标题', 'aksrt'); ?>
|
||||
<textarea class="widefat" rows="2" name="<?php echo esc_attr($this->get_field_name('intro_subtitle')); ?>"><?php echo esc_textarea((string) ($instance['intro_subtitle'] ?? '')); ?></textarea>
|
||||
</label>
|
||||
</p>
|
||||
<?php
|
||||
$this->render_common_form_section($instance);
|
||||
}
|
||||
|
||||
public function update($new_instance, $old_instance): array {
|
||||
$business = [
|
||||
'title' => sanitize_text_field((string) ($new_instance['title'] ?? '')),
|
||||
'stats' => $this->normalize_stats($new_instance['stats'] ?? []),
|
||||
'columns' => max(1, min(4, (int) ($new_instance['columns'] ?? 4))),
|
||||
'mobile_columns' => max(1, min(3, (int) ($new_instance['mobile_columns'] ?? 2))),
|
||||
'intro_title' => sanitize_text_field((string) ($new_instance['intro_title'] ?? '')),
|
||||
'intro_subtitle' => sanitize_text_field((string) ($new_instance['intro_subtitle'] ?? '')),
|
||||
]; return $this->update_common_fields($new_instance, $old_instance, $business);
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@ class WidgetUserCard extends \WP_Widget {
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
'aksrt_user_card',
|
||||
__('用户卡片', 'aksrt'),
|
||||
__('AKSRT 用户卡片', 'aksrt'),
|
||||
[
|
||||
'description' => __('未登录显示登录入口;登录后显示当前用户卡片、签到、快捷按钮与更多文章。', 'aksrt'),
|
||||
'classname' => 'widget_aksrt_user_card',
|
||||
|
||||
@@ -17,7 +17,7 @@ class WidgetUserRanking extends \WP_Widget {
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
'aksrt_user_ranking',
|
||||
__('用户排行榜', 'aksrt'),
|
||||
__('AKSRT 用户排行榜', 'aksrt'),
|
||||
[
|
||||
'description' => __('按积分、文章数或粉丝数排行的用户列表。', 'aksrt'),
|
||||
'classname' => 'widget_aksrt_user_ranking',
|
||||
|
||||
@@ -18,7 +18,7 @@ class WidgetVideoPlayer extends \WP_Widget {
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
'aksrt_video_player',
|
||||
__('视频播放器', 'aksrt'),
|
||||
__('AKSRT 视频播放器', 'aksrt'),
|
||||
[
|
||||
'description' => __('侧栏/流体区嵌入视频(共享 aksrt-video 播放器)。', 'aksrt'),
|
||||
'classname' => 'widget_aksrt_video_player',
|
||||
|
||||
@@ -43,7 +43,7 @@ class WidgetYiyan extends \WP_Widget {
|
||||
public function __construct() {
|
||||
parent::__construct(
|
||||
'aksrt_yiyan',
|
||||
__('一言轮播', 'aksrt'),
|
||||
__('AKSRT 一言轮播', 'aksrt'),
|
||||
[
|
||||
'description' => __('显示随机一言/每日语录,支持自动轮播。', 'aksrt'),
|
||||
'classname' => 'widget_aksrt_yiyan yiyan-box',
|
||||
|
||||
@@ -30,7 +30,7 @@ $archive_settings = class_exists('\AKSRT\Core\TermMeta')
|
||||
|
||||
$has_sidebar = function_exists('aksrt_archive_has_sidebar')
|
||||
? aksrt_archive_has_sidebar('tag_sidebar', !empty($archive_settings['show_sidebar']))
|
||||
: (!empty($archive_settings['show_sidebar']) && (is_active_sidebar('tag_sidebar') || is_active_sidebar('sidebar-blog')));
|
||||
: (!empty($archive_settings['show_sidebar']) && is_active_sidebar('tag_sidebar'));
|
||||
$description = !empty($archive_settings['show_desc']) ? wp_kses_post((string) $archive_settings['description']) : '';
|
||||
$header_style = (string) $archive_settings['header_style'];
|
||||
$layout_mode = (string) $archive_settings['layout_mode'];
|
||||
|
||||
Reference in New Issue
Block a user