feat(comments): full 1:1 zibll comment kernel for posts, forum, shop
Extract shared comment helpers (content/item/form/geo/actions), port GIF [g=] smilies + FixedInput/moveForm UX, wire close_comments/city/quick settings, REST comment get, and align product review chrome with zibll.
@@ -23,6 +23,10 @@ export const commentSchema = {
|
||||
comment_hot_threshold: 10,
|
||||
comment_quick_s: true,
|
||||
comment_city_s: true,
|
||||
comment_city_type: 'province_city',
|
||||
comment_quick_often: '不错,学习了
|
||||
感谢分享
|
||||
期待更新',
|
||||
comment_author_tag: true,
|
||||
comment_corderby: true,
|
||||
comment_empty_text: '暂无评论,来做第一个评论的人吧~',
|
||||
@@ -83,6 +87,14 @@ export const commentSchema = {
|
||||
{ key: 'comment_like_s', label: '评论点赞', type: 'switch', default: true },
|
||||
{ key: 'comment_quick_s', label: '快捷回复', type: 'switch', default: true },
|
||||
{ key: 'comment_city_s', label: '评论城市', type: 'switch', default: true },
|
||||
{ key: 'comment_city_type', label: '城市显示格式', type: 'radio', default: 'province_city', options: [
|
||||
{ label: '省份+城市', value: 'province_city' },
|
||||
{ label: '仅省份', value: 'province' },
|
||||
{ label: '国家+省份', value: 'country_province' },
|
||||
], dependsOn: { key: 'comment_city_s', value: true } },
|
||||
{ key: 'comment_quick_often', label: '快捷回复短语', type: 'textarea', default: '不错,学习了
|
||||
感谢分享
|
||||
期待更新', help: '每行一条', dependsOn: { key: 'comment_quick_s', value: true } },
|
||||
{ key: 'comment_author_tag', label: '作者标签', type: 'switch', default: true },
|
||||
{ key: 'comment_corderby', label: '评论排序切换', type: 'switch', default: true, help: '前台显示最新/最早/最热切换按钮' },
|
||||
],
|
||||
|
||||
@@ -1,784 +1,4 @@
|
||||
/* ============================================================
|
||||
* AKSRT Comments — v4.0 Zibll-Inspired
|
||||
*
|
||||
* 功能:
|
||||
* - 评论表单(头像 + 输入框 + 操作栏)
|
||||
* - 评论列表(嵌套回复 + 头像 + 时间)
|
||||
* - 排序/过滤栏
|
||||
* - 空状态
|
||||
* - 移动端固定底部输入框(可选)
|
||||
* ============================================================ */
|
||||
|
||||
/* ============================================================
|
||||
* 评论区容器
|
||||
* ============================================================ */
|
||||
.comments-area {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
/* 对齐 zibll: 45px */
|
||||
--aksrt-comment-avatar: 45px;
|
||||
--aksrt-comment-avatar-form: 45px;
|
||||
--aksrt-comment-indent: 48px;
|
||||
}
|
||||
|
||||
/* 评论标题 */
|
||||
.comments-header {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.comments-title {
|
||||
font-size: 1rem;
|
||||
font-weight: 700;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: var(--color-ink);
|
||||
}
|
||||
|
||||
.comments-title i {
|
||||
color: var(--color-primary);
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.comments-count {
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 400;
|
||||
color: var(--color-ink-4);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 登录提示
|
||||
* ============================================================ */
|
||||
.comment-login-prompt {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.comment-login-prompt__inner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 16px 18px;
|
||||
border-radius: var(--main-radius, var(--radius, 8px));
|
||||
background: var(--surface-1);
|
||||
border: none;
|
||||
box-shadow: var(--shadow-sm);
|
||||
font-size: 14px;
|
||||
color: var(--color-ink-4);
|
||||
}
|
||||
|
||||
.comment-login-prompt__inner i {
|
||||
font-size: 16px;
|
||||
color: var(--color-ink-4);
|
||||
}
|
||||
|
||||
.comment-login-prompt__btns {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 评论表单
|
||||
* ============================================================ */
|
||||
.comment-respond {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.comment-form__inner {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.comment-form__avatar {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.comment-form__avatar img {
|
||||
width: var(--aksrt-comment-avatar-form);
|
||||
height: var(--aksrt-comment-avatar-form);
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.comment-form__avatar-placeholder {
|
||||
width: var(--aksrt-comment-avatar-form);
|
||||
height: var(--aksrt-comment-avatar-form);
|
||||
border-radius: 50%;
|
||||
background: var(--surface-3);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--color-ink-4);
|
||||
}
|
||||
|
||||
.comment-form__body {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.comment-form__reply-info {
|
||||
font-size: 0.8125rem;
|
||||
color: var(--color-ink-4);
|
||||
margin-bottom: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.comment-form__cancel-reply {
|
||||
color: var(--color-error, #ef4444);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.comment-form__textarea {
|
||||
width: 100%;
|
||||
min-height: 96px;
|
||||
resize: vertical;
|
||||
border-radius: 10px;
|
||||
border: 1px solid var(--color-line);
|
||||
padding: 12px 14px;
|
||||
font-size: 14px;
|
||||
background: var(--surface-4, var(--main-bg-color, var(--surface-1)));
|
||||
color: var(--color-ink);
|
||||
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.comment-form__textarea:focus {
|
||||
outline: none;
|
||||
border-color: var(--color-primary);
|
||||
box-shadow: 0 0 0 3px var(--color-primary-soft);
|
||||
}
|
||||
|
||||
.comment-form__actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-top: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.comment-form__tools {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.comment-form__submit-area {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-left: auto;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.comment-form__guest {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.comment-form__guest-toggle {
|
||||
background: none;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
color: var(--muted-color, #8b9198);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.comment-form__guest-toggle:hover,
|
||||
.comment-form__guest-toggle[aria-expanded="true"] {
|
||||
color: var(--focus-color, #f04494);
|
||||
}
|
||||
|
||||
.comment-form__guest-fields {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
opacity: 0;
|
||||
transition: max-height 0.2s ease, opacity 0.15s ease;
|
||||
}
|
||||
|
||||
.comment-form__guest-fields.is-open,
|
||||
.comment-form__guest-fields:not([hidden]) {
|
||||
max-height: 48px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.comment-form__guest-field {
|
||||
width: 110px;
|
||||
height: 30px;
|
||||
font-size: 12px;
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
.comt-box .comt-textarea,
|
||||
.comt-box #comment {
|
||||
min-height: 72px;
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.comt-ctrl {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.comment-form__submit {
|
||||
height: 30px;
|
||||
padding: 0 12px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.comment-form__submit {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 排序/过滤栏
|
||||
* ============================================================ */
|
||||
.comment-filter {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 10px 0;
|
||||
margin-bottom: 12px;
|
||||
border-bottom: 1px solid var(--color-line);
|
||||
}
|
||||
|
||||
.comment-filter__tabs {
|
||||
display: flex;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.comment-filter__tabs li a {
|
||||
display: inline-block;
|
||||
padding: 4px 12px;
|
||||
border-radius: 4px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: var(--color-ink-4);
|
||||
text-decoration: none;
|
||||
transition: background 0.15s ease, color 0.15s ease;
|
||||
}
|
||||
|
||||
.comment-filter__tabs li a:hover {
|
||||
background: var(--surface-3);
|
||||
color: var(--color-ink);
|
||||
}
|
||||
|
||||
.comment-filter__tabs li.active a {
|
||||
background: var(--color-primary-soft, rgba(240, 68, 148, 0.12));
|
||||
color: var(--color-primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Zibll-compatible class names used by aksrt-comments.php */
|
||||
.comment-order-box {
|
||||
display: flex;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
gap: 4px;
|
||||
}
|
||||
.comment-order-box li a,
|
||||
.comment-order-box .comment-orderby {
|
||||
display: inline-block;
|
||||
padding: 4px 12px;
|
||||
border-radius: 4px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: var(--color-ink-4);
|
||||
text-decoration: none;
|
||||
transition: background 0.15s ease, color 0.15s ease;
|
||||
}
|
||||
.comment-order-box li a:hover,
|
||||
.comment-order-box .comment-orderby:hover {
|
||||
background: var(--surface-3);
|
||||
color: var(--color-ink);
|
||||
}
|
||||
.comment-order-box li.active a,
|
||||
.comment-order-box li.active .comment-orderby {
|
||||
background: var(--color-primary-soft, rgba(240, 68, 148, 0.12));
|
||||
color: var(--color-primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
.btn-only-author,
|
||||
.comment-author-filter {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 4px 12px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid var(--color-line);
|
||||
background: var(--surface-1);
|
||||
color: var(--color-ink-3);
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
|
||||
}
|
||||
.btn-only-author:hover,
|
||||
.comment-author-filter:hover {
|
||||
border-color: var(--color-primary);
|
||||
color: var(--color-primary);
|
||||
}
|
||||
.btn-only-author.active,
|
||||
.comment-author-filter.active {
|
||||
border-color: var(--color-primary);
|
||||
background: var(--color-primary-soft, rgba(240, 68, 148, 0.12));
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 评论列表
|
||||
* ============================================================ */
|
||||
.comment-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.comment-list .comment {
|
||||
padding: 16px 0;
|
||||
border-bottom: 1px solid var(--color-line);
|
||||
}
|
||||
|
||||
.comment-list .comment:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/* 嵌套回复 — 对齐 zibll: 86px 缩进 + 灰色背景 */
|
||||
.comment-list .children {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
margin-left: 86px;
|
||||
}
|
||||
|
||||
.comment-list .children .comment {
|
||||
padding: 14px 16px;
|
||||
background: var(--surface-4, rgba(0, 0, 0, 0.03));
|
||||
border: 1px solid var(--color-line-soft);
|
||||
border-radius: var(--radius-lg);
|
||||
}
|
||||
|
||||
/* 深层嵌套缩进递减 */
|
||||
.comment-list .children .children {
|
||||
margin-left: 13px;
|
||||
}
|
||||
|
||||
/* 评论项内部结构(由 aksrt_comment_callback 渲染) */
|
||||
.aksrt-comment__body {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.aksrt-comment__avatar {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.aksrt-comment__avatar img {
|
||||
width: var(--aksrt-comment-avatar);
|
||||
height: var(--aksrt-comment-avatar);
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.aksrt-comment__content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.aksrt-comment__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 6px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.aksrt-comment__author {
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
color: var(--color-ink);
|
||||
}
|
||||
|
||||
.aksrt-comment__author a {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.aksrt-comment__author a:hover {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.aksrt-comment__badge {
|
||||
font-size: 10px;
|
||||
padding: 1px 6px;
|
||||
border-radius: 3px;
|
||||
background: var(--color-primary-soft, rgba(240, 68, 148, 0.12));
|
||||
color: var(--color-primary);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.aksrt-comment__time {
|
||||
font-size: 12px;
|
||||
color: var(--color-ink-4);
|
||||
}
|
||||
|
||||
.aksrt-comment__text {
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
color: var(--color-ink);
|
||||
margin: 0 0 8px;
|
||||
word-wrap: break-word;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
/* 对齐 zibll: 评论内图片限制 */
|
||||
.aksrt-comment__text img:not(.smilie-icon) {
|
||||
max-width: 260px;
|
||||
max-height: 350px;
|
||||
}
|
||||
|
||||
.aksrt-comment__text p {
|
||||
margin: 0 0 8px;
|
||||
}
|
||||
|
||||
.aksrt-comment__text p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.aksrt-comment__actions {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.aksrt-comment__actions a,
|
||||
.aksrt-comment__actions button {
|
||||
color: var(--color-ink-4);
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
transition: color 0.15s ease;
|
||||
}
|
||||
|
||||
.aksrt-comment__actions a:hover,
|
||||
.aksrt-comment__actions button:hover {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
/* 嵌套回复中的头像更小 */
|
||||
/* 对齐 zibll: 嵌套头像 26px */
|
||||
.children .aksrt-comment__avatar img {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
margin-right: 6px;
|
||||
vertical-align: 2px;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 空状态
|
||||
* ============================================================ */
|
||||
.comments-empty {
|
||||
text-align: center;
|
||||
padding: 40px 20px;
|
||||
}
|
||||
|
||||
.comments-empty__icon {
|
||||
font-size: 48px;
|
||||
color: var(--color-ink-4);
|
||||
opacity: 0.4;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.comments-empty__text {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-ink-4);
|
||||
margin: 0 0 4px;
|
||||
}
|
||||
|
||||
.comments-empty__hint {
|
||||
font-size: 0.8125rem;
|
||||
color: var(--color-ink-4);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.comments-closed {
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
color: var(--color-ink-4);
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 评论分页
|
||||
* ============================================================ */
|
||||
.comment-pagination {
|
||||
text-align: center;
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 移动端适配
|
||||
* ============================================================ */
|
||||
@media (max-width: 767px) {
|
||||
.comment-form__inner {
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.comment-form__avatar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.comment-form__submit-area {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.comment-form__guest-field {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.comment-list .children {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.children .aksrt-comment__avatar img {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
/* 移动端评论输入吸底 — 模仿 zibll mobile-fixed */
|
||||
.comment-respond.comment-form--mobile-fixed {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: var(--z-sticky, 200);
|
||||
background: var(--surface-1);
|
||||
border-top: 1px solid var(--color-line);
|
||||
box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.08);
|
||||
padding: 12px 16px;
|
||||
padding-bottom: max(12px, env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
.comment-respond.comment-form--mobile-fixed .comment-form__textarea {
|
||||
min-height: 44px;
|
||||
max-height: 120px;
|
||||
}
|
||||
|
||||
.comment-respond.comment-form--mobile-fixed .comment-form__submit-area {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
/* 当评论区可见时隐藏吸底输入框 */
|
||||
.comment-respond.comment-form--mobile-fixed.is-visible {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 暗色模式
|
||||
* ============================================================ */
|
||||
html.dark .comment-form__textarea,
|
||||
body.dark-theme .comment-form__textarea {
|
||||
background: var(--surface-2);
|
||||
border-color: var(--color-line);
|
||||
}
|
||||
|
||||
.theme-dark .comment-login-prompt__inner,
|
||||
html.dark .comment-login-prompt__inner,
|
||||
body.dark-theme .comment-login-prompt__inner {
|
||||
background: var(--surface-1);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 评论操作按钮(编辑/删除/审核)
|
||||
* ============================================================ */
|
||||
.aksrt-comment-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.aksrt-comment-action-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 3px 10px;
|
||||
border: 1px solid var(--color-line, rgba(50, 50, 50, 0.1));
|
||||
border-radius: var(--radius, 8px);
|
||||
background: var(--surface-1, #fff);
|
||||
color: var(--color-ink-3, #777);
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
.aksrt-comment-action-btn:hover {
|
||||
border-color: var(--color-primary);
|
||||
color: var(--color-primary);
|
||||
background: var(--color-primary-soft, rgba(240, 68, 148, 0.08));
|
||||
}
|
||||
|
||||
.aksrt-comment-action-btn--danger:hover {
|
||||
border-color: var(--color-error);
|
||||
color: var(--color-error);
|
||||
background: rgba(239, 68, 68, 0.08);
|
||||
}
|
||||
|
||||
.aksrt-comment-action-btn--success:hover {
|
||||
border-color: var(--color-success);
|
||||
color: var(--color-success);
|
||||
background: rgba(16, 185, 129, 0.08);
|
||||
}
|
||||
|
||||
.aksrt-comment-edit-form {
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
.aksrt-comment-edit-textarea {
|
||||
width: 100%;
|
||||
min-height: 80px;
|
||||
padding: 8px 12px;
|
||||
border: 1px solid var(--color-line, #ddd);
|
||||
border-radius: var(--radius, 8px);
|
||||
font-size: 14px;
|
||||
font-family: var(--font-body);
|
||||
color: var(--color-ink);
|
||||
background: var(--surface-1, #fff);
|
||||
resize: vertical;
|
||||
transition: border-color 0.15s;
|
||||
}
|
||||
|
||||
.aksrt-comment-edit-textarea:focus {
|
||||
outline: none;
|
||||
border-color: var(--color-primary);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 评论互动:点赞 / 置顶 / 内联管理动作(统一合同)
|
||||
* 这些动作均渲染在 .aksrt-comment__actions 内,复用其文字链按钮样式,
|
||||
* 仅补充点赞激活态、置顶徽章与危险/成功色提示。
|
||||
* ============================================================ */
|
||||
.aksrt-comment__like.is-active,
|
||||
.aksrt-comment__like.is-active i {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.aksrt-comment__like-count {
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.aksrt-comment__action--danger:hover {
|
||||
color: var(--color-error, #ef4444);
|
||||
}
|
||||
|
||||
.aksrt-comment__action--success:hover {
|
||||
color: var(--color-success, #10b981);
|
||||
}
|
||||
|
||||
/* 置顶徽章 */
|
||||
.aksrt-comment__badge--pinned {
|
||||
background: var(--color-warning-soft, rgba(245, 158, 11, 0.14));
|
||||
color: var(--color-warning, #d97706);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
/* 置顶评论容器高亮 */
|
||||
.aksrt-comment--pinned > .aksrt-comment__body {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.comment-list .comment.aksrt-comment--pinned {
|
||||
background: var(--color-warning-soft, rgba(245, 158, 11, 0.06));
|
||||
border-radius: var(--radius, 8px);
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
}
|
||||
|
||||
/* zibll compat: emoji icon forced 30px */
|
||||
.comment-content .smilie-icon,
|
||||
.comment-form .smilie-icon {
|
||||
width: 30px !important;
|
||||
}
|
||||
|
||||
/* zibll compat: code block in comments */
|
||||
.comment-content .enlighter-default {
|
||||
margin-bottom: 0;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
|
||||
/* Identity chips + 神评 */
|
||||
.aksrt-identity-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
vertical-align: middle;
|
||||
max-width: 9em;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.aksrt-identity-chip--comment {
|
||||
font-size: 11px;
|
||||
padding: 0 6px;
|
||||
}
|
||||
.medal-icon.img-icon,
|
||||
.aksrt-identity-medal {
|
||||
display: inline-block;
|
||||
width: 1.35em;
|
||||
height: 1.35em;
|
||||
object-fit: contain;
|
||||
vertical-align: middle;
|
||||
margin-left: 4px;
|
||||
}
|
||||
.aksrt-identity-medal--comment {
|
||||
width: 1.15em;
|
||||
height: 1.15em;
|
||||
}
|
||||
.aksrt-identity-medal.is-wear {
|
||||
outline: 1px solid rgba(245, 158, 11, 0.45);
|
||||
border-radius: 3px;
|
||||
}
|
||||
.aksrt-comment__badge--hot {
|
||||
color: #ea580c;
|
||||
background: rgba(234, 88, 12, 0.1);
|
||||
border-radius: 999px;
|
||||
padding: 0 6px;
|
||||
margin-left: 4px;
|
||||
font-size: 11px;
|
||||
}
|
||||
.aksrt-comment__header .author-box {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
/* AKSRT comments aggregator */
|
||||
@import url("./comments/shell.css");
|
||||
@import url("./comments/mobile-item.css");
|
||||
@import url("./comment-tools.css");
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
/* Zibll 1:1 FixedInput / virtual-input / item chrome */
|
||||
.virtual-input { display: none; }
|
||||
@media (max-width: 767.98px) {
|
||||
.virtual-input {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
padding: 10px 12px; margin-bottom: 12px; border-radius: 999px;
|
||||
background: var(--surface-1, #fff);
|
||||
box-shadow: var(--shadow-sm, 0 1px 4px rgba(0,0,0,.06));
|
||||
cursor: pointer;
|
||||
}
|
||||
.virtual-input .avatar-img { width: 32px; height: 32px; border-radius: 50%; }
|
||||
.virtual-input .simulation { flex: 1; color: var(--color-ink-4, #999); font-size: 13px; }
|
||||
#respond.mobile-fixed {
|
||||
position: fixed; left: 0; right: 0; bottom: 0; z-index: 1050;
|
||||
background: var(--surface-1, #fff);
|
||||
border-radius: 12px 12px 0 0;
|
||||
box-shadow: 0 -8px 28px rgba(0,0,0,.12);
|
||||
padding: 12px 14px max(12px, env(safe-area-inset-bottom));
|
||||
transform: translateY(110%); transition: transform .25s ease;
|
||||
max-height: 85vh; overflow: auto;
|
||||
}
|
||||
#respond.mobile-fixed.show { transform: translateY(0); }
|
||||
#respond.mobile-fixed .fixed-body {
|
||||
position: fixed; inset: 0; background: rgba(0,0,0,.35); z-index: -1;
|
||||
opacity: 0; pointer-events: none; transition: opacity .2s ease;
|
||||
}
|
||||
#respond.mobile-fixed.show .fixed-body { opacity: 1; pointer-events: auto; }
|
||||
#respond.mobile-fixed .comt-title { display: none; }
|
||||
}
|
||||
.comment-header .author-box { display: flex; align-items: center; gap: 8px; width: 100%; }
|
||||
.comment-header .author-box name,
|
||||
.comment-header .author-box .flex1 { flex: 1; min-width: 0; }
|
||||
.comment-header .aksrt-comment__like,
|
||||
.comment-header .action-comment-like {
|
||||
margin-left: auto; display: inline-flex; align-items: center; gap: 4px;
|
||||
color: var(--color-ink-3, #888); text-decoration: none; font-size: 13px;
|
||||
}
|
||||
.comment-header .aksrt-comment__like.is-active { color: var(--color-primary, #2060ef); }
|
||||
.comt-meta {
|
||||
display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
|
||||
font-size: 12px; margin-top: 6px;
|
||||
}
|
||||
.comt-meta .reply-link a, .comt-meta .comment-reply-link { color: var(--color-ink-3, #777); }
|
||||
.comment-action-dropdown .dropdown-menu { min-width: 140px; padding: 6px 0; }
|
||||
.comment-action-dropdown .dropdown-menu a {
|
||||
display: block; padding: 6px 14px; color: var(--color-ink, #333);
|
||||
text-decoration: none; font-size: 13px;
|
||||
}
|
||||
.comment-action-dropdown .dropdown-menu a:hover { background: var(--surface-2, #f5f5f5); }
|
||||
.smilie-icon { width: 30px; height: 30px; vertical-align: -8px; display: inline-block; }
|
||||
.comment-inline-img, .comment-content .box-img {
|
||||
max-width: 260px; max-height: 350px; border-radius: 6px; display: block; margin: 6px 0;
|
||||
}
|
||||
.comment-code {
|
||||
background: var(--surface-2, #f6f8fa); padding: 10px 12px; border-radius: 6px;
|
||||
overflow: auto; font-size: 12px;
|
||||
}
|
||||
.commentlist .children { margin-left: 86px; padding-left: 0; list-style: none; }
|
||||
.commentlist .children .children { margin-left: 23px; }
|
||||
.commentlist .children .comt-avatar,
|
||||
.commentlist .children .avatar-img.comt-avatar { width: 26px !important; height: 26px !important; }
|
||||
.input-expand-wrap { display: inline-block; position: relative; }
|
||||
.input-expand-wrap .dropdown-menu,
|
||||
.input-expand-wrap .dropdown-smilie {
|
||||
display: none; position: absolute; bottom: 100%; left: 0; z-index: 20;
|
||||
background: var(--surface-1, #fff); box-shadow: 0 6px 24px rgba(0,0,0,.12);
|
||||
border-radius: 8px; padding: 8px;
|
||||
}
|
||||
.input-expand-wrap.open > .dropdown-menu,
|
||||
.input-expand-wrap.open .dropdown-smilie,
|
||||
.dropup.open > .dropdown-menu { display: block; }
|
||||
.dropdown-smilie {
|
||||
display: grid; grid-template-columns: repeat(8, 1fr); gap: 4px;
|
||||
max-width: 280px; max-height: 200px; overflow: auto;
|
||||
}
|
||||
.dropdown-smilie a img { width: 28px; height: 28px; }
|
||||
.quick-reply-item {
|
||||
display: block; padding: 6px 10px; color: var(--color-ink, #333);
|
||||
text-decoration: none; font-size: 13px;
|
||||
}
|
||||
.quick-reply-item:hover { background: var(--surface-2, #f5f5f5); }
|
||||
.comt-ctrl { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 8px; }
|
||||
.comt-tips-left { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; flex: 1; }
|
||||
.comt-tips-right { margin-left: auto; display: flex; align-items: center; gap: 8px; }
|
||||
#comments .comt-title { width: 90px; }
|
||||
#comments .comt-title .comt-avatar img,
|
||||
#comments .comt-avatar .avatar-img { width: 60px; height: 60px; border-radius: 50%; }
|
||||
#comments .comt-box #comment { min-height: 88px; resize: vertical; }
|
||||
@media (min-width: 768px) { .hide-pc { display: none !important; } }
|
||||
@@ -0,0 +1,784 @@
|
||||
/* ============================================================
|
||||
* AKSRT Comments — v4.0 Zibll-Inspired
|
||||
*
|
||||
* 功能:
|
||||
* - 评论表单(头像 + 输入框 + 操作栏)
|
||||
* - 评论列表(嵌套回复 + 头像 + 时间)
|
||||
* - 排序/过滤栏
|
||||
* - 空状态
|
||||
* - 移动端固定底部输入框(可选)
|
||||
* ============================================================ */
|
||||
|
||||
/* ============================================================
|
||||
* 评论区容器
|
||||
* ============================================================ */
|
||||
.comments-area {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
/* 对齐 zibll: 45px */
|
||||
--aksrt-comment-avatar: 45px;
|
||||
--aksrt-comment-avatar-form: 45px;
|
||||
--aksrt-comment-indent: 48px;
|
||||
}
|
||||
|
||||
/* 评论标题 */
|
||||
.comments-header {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.comments-title {
|
||||
font-size: 1rem;
|
||||
font-weight: 700;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: var(--color-ink);
|
||||
}
|
||||
|
||||
.comments-title i {
|
||||
color: var(--color-primary);
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.comments-count {
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 400;
|
||||
color: var(--color-ink-4);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 登录提示
|
||||
* ============================================================ */
|
||||
.comment-login-prompt {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.comment-login-prompt__inner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 16px 18px;
|
||||
border-radius: var(--main-radius, var(--radius, 8px));
|
||||
background: var(--surface-1);
|
||||
border: none;
|
||||
box-shadow: var(--shadow-sm);
|
||||
font-size: 14px;
|
||||
color: var(--color-ink-4);
|
||||
}
|
||||
|
||||
.comment-login-prompt__inner i {
|
||||
font-size: 16px;
|
||||
color: var(--color-ink-4);
|
||||
}
|
||||
|
||||
.comment-login-prompt__btns {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 评论表单
|
||||
* ============================================================ */
|
||||
.comment-respond {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.comment-form__inner {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.comment-form__avatar {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.comment-form__avatar img {
|
||||
width: var(--aksrt-comment-avatar-form);
|
||||
height: var(--aksrt-comment-avatar-form);
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.comment-form__avatar-placeholder {
|
||||
width: var(--aksrt-comment-avatar-form);
|
||||
height: var(--aksrt-comment-avatar-form);
|
||||
border-radius: 50%;
|
||||
background: var(--surface-3);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--color-ink-4);
|
||||
}
|
||||
|
||||
.comment-form__body {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.comment-form__reply-info {
|
||||
font-size: 0.8125rem;
|
||||
color: var(--color-ink-4);
|
||||
margin-bottom: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.comment-form__cancel-reply {
|
||||
color: var(--color-error, #ef4444);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.comment-form__textarea {
|
||||
width: 100%;
|
||||
min-height: 96px;
|
||||
resize: vertical;
|
||||
border-radius: 10px;
|
||||
border: 1px solid var(--color-line);
|
||||
padding: 12px 14px;
|
||||
font-size: 14px;
|
||||
background: var(--surface-4, var(--main-bg-color, var(--surface-1)));
|
||||
color: var(--color-ink);
|
||||
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.comment-form__textarea:focus {
|
||||
outline: none;
|
||||
border-color: var(--color-primary);
|
||||
box-shadow: 0 0 0 3px var(--color-primary-soft);
|
||||
}
|
||||
|
||||
.comment-form__actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-top: 10px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.comment-form__tools {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.comment-form__submit-area {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-left: auto;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.comment-form__guest {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.comment-form__guest-toggle {
|
||||
background: none;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
color: var(--muted-color, #8b9198);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.comment-form__guest-toggle:hover,
|
||||
.comment-form__guest-toggle[aria-expanded="true"] {
|
||||
color: var(--focus-color, #f04494);
|
||||
}
|
||||
|
||||
.comment-form__guest-fields {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
opacity: 0;
|
||||
transition: max-height 0.2s ease, opacity 0.15s ease;
|
||||
}
|
||||
|
||||
.comment-form__guest-fields.is-open,
|
||||
.comment-form__guest-fields:not([hidden]) {
|
||||
max-height: 48px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.comment-form__guest-field {
|
||||
width: 110px;
|
||||
height: 30px;
|
||||
font-size: 12px;
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
.comt-box .comt-textarea,
|
||||
.comt-box #comment {
|
||||
min-height: 72px;
|
||||
padding: 10px 12px;
|
||||
border-radius: 8px;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.comt-ctrl {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.comment-form__submit {
|
||||
height: 30px;
|
||||
padding: 0 12px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.comment-form__submit {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 排序/过滤栏
|
||||
* ============================================================ */
|
||||
.comment-filter {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 10px 0;
|
||||
margin-bottom: 12px;
|
||||
border-bottom: 1px solid var(--color-line);
|
||||
}
|
||||
|
||||
.comment-filter__tabs {
|
||||
display: flex;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.comment-filter__tabs li a {
|
||||
display: inline-block;
|
||||
padding: 4px 12px;
|
||||
border-radius: 4px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: var(--color-ink-4);
|
||||
text-decoration: none;
|
||||
transition: background 0.15s ease, color 0.15s ease;
|
||||
}
|
||||
|
||||
.comment-filter__tabs li a:hover {
|
||||
background: var(--surface-3);
|
||||
color: var(--color-ink);
|
||||
}
|
||||
|
||||
.comment-filter__tabs li.active a {
|
||||
background: var(--color-primary-soft, rgba(240, 68, 148, 0.12));
|
||||
color: var(--color-primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Zibll-compatible class names used by aksrt-comments.php */
|
||||
.comment-order-box {
|
||||
display: flex;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
gap: 4px;
|
||||
}
|
||||
.comment-order-box li a,
|
||||
.comment-order-box .comment-orderby {
|
||||
display: inline-block;
|
||||
padding: 4px 12px;
|
||||
border-radius: 4px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: var(--color-ink-4);
|
||||
text-decoration: none;
|
||||
transition: background 0.15s ease, color 0.15s ease;
|
||||
}
|
||||
.comment-order-box li a:hover,
|
||||
.comment-order-box .comment-orderby:hover {
|
||||
background: var(--surface-3);
|
||||
color: var(--color-ink);
|
||||
}
|
||||
.comment-order-box li.active a,
|
||||
.comment-order-box li.active .comment-orderby {
|
||||
background: var(--color-primary-soft, rgba(240, 68, 148, 0.12));
|
||||
color: var(--color-primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
.btn-only-author,
|
||||
.comment-author-filter {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 4px 12px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid var(--color-line);
|
||||
background: var(--surface-1);
|
||||
color: var(--color-ink-3);
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
|
||||
}
|
||||
.btn-only-author:hover,
|
||||
.comment-author-filter:hover {
|
||||
border-color: var(--color-primary);
|
||||
color: var(--color-primary);
|
||||
}
|
||||
.btn-only-author.active,
|
||||
.comment-author-filter.active {
|
||||
border-color: var(--color-primary);
|
||||
background: var(--color-primary-soft, rgba(240, 68, 148, 0.12));
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 评论列表
|
||||
* ============================================================ */
|
||||
.comment-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.comment-list .comment {
|
||||
padding: 16px 0;
|
||||
border-bottom: 1px solid var(--color-line);
|
||||
}
|
||||
|
||||
.comment-list .comment:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/* 嵌套回复 — 对齐 zibll: 86px 缩进 + 灰色背景 */
|
||||
.comment-list .children {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
margin-left: 86px;
|
||||
}
|
||||
|
||||
.comment-list .children .comment {
|
||||
padding: 14px 16px;
|
||||
background: var(--surface-4, rgba(0, 0, 0, 0.03));
|
||||
border: 1px solid var(--color-line-soft);
|
||||
border-radius: var(--radius-lg);
|
||||
}
|
||||
|
||||
/* 深层嵌套缩进递减 */
|
||||
.comment-list .children .children {
|
||||
margin-left: 13px;
|
||||
}
|
||||
|
||||
/* 评论项内部结构(由 aksrt_comment_callback 渲染) */
|
||||
.aksrt-comment__body {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.aksrt-comment__avatar {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.aksrt-comment__avatar img {
|
||||
width: var(--aksrt-comment-avatar);
|
||||
height: var(--aksrt-comment-avatar);
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.aksrt-comment__content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.aksrt-comment__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 6px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.aksrt-comment__author {
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
color: var(--color-ink);
|
||||
}
|
||||
|
||||
.aksrt-comment__author a {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.aksrt-comment__author a:hover {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.aksrt-comment__badge {
|
||||
font-size: 10px;
|
||||
padding: 1px 6px;
|
||||
border-radius: 3px;
|
||||
background: var(--color-primary-soft, rgba(240, 68, 148, 0.12));
|
||||
color: var(--color-primary);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.aksrt-comment__time {
|
||||
font-size: 12px;
|
||||
color: var(--color-ink-4);
|
||||
}
|
||||
|
||||
.aksrt-comment__text {
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
color: var(--color-ink);
|
||||
margin: 0 0 8px;
|
||||
word-wrap: break-word;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
/* 对齐 zibll: 评论内图片限制 */
|
||||
.aksrt-comment__text img:not(.smilie-icon) {
|
||||
max-width: 260px;
|
||||
max-height: 350px;
|
||||
}
|
||||
|
||||
.aksrt-comment__text p {
|
||||
margin: 0 0 8px;
|
||||
}
|
||||
|
||||
.aksrt-comment__text p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.aksrt-comment__actions {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.aksrt-comment__actions a,
|
||||
.aksrt-comment__actions button {
|
||||
color: var(--color-ink-4);
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
transition: color 0.15s ease;
|
||||
}
|
||||
|
||||
.aksrt-comment__actions a:hover,
|
||||
.aksrt-comment__actions button:hover {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
/* 嵌套回复中的头像更小 */
|
||||
/* 对齐 zibll: 嵌套头像 26px */
|
||||
.children .aksrt-comment__avatar img {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
margin-right: 6px;
|
||||
vertical-align: 2px;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 空状态
|
||||
* ============================================================ */
|
||||
.comments-empty {
|
||||
text-align: center;
|
||||
padding: 40px 20px;
|
||||
}
|
||||
|
||||
.comments-empty__icon {
|
||||
font-size: 48px;
|
||||
color: var(--color-ink-4);
|
||||
opacity: 0.4;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.comments-empty__text {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-ink-4);
|
||||
margin: 0 0 4px;
|
||||
}
|
||||
|
||||
.comments-empty__hint {
|
||||
font-size: 0.8125rem;
|
||||
color: var(--color-ink-4);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.comments-closed {
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
color: var(--color-ink-4);
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 评论分页
|
||||
* ============================================================ */
|
||||
.comment-pagination {
|
||||
text-align: center;
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 移动端适配
|
||||
* ============================================================ */
|
||||
@media (max-width: 767px) {
|
||||
.comment-form__inner {
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.comment-form__avatar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.comment-form__submit-area {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.comment-form__guest-field {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.comment-list .children {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.children .aksrt-comment__avatar img {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
/* 移动端评论输入吸底 — 模仿 zibll mobile-fixed */
|
||||
.comment-respond.comment-form--mobile-fixed {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: var(--z-sticky, 200);
|
||||
background: var(--surface-1);
|
||||
border-top: 1px solid var(--color-line);
|
||||
box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.08);
|
||||
padding: 12px 16px;
|
||||
padding-bottom: max(12px, env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
.comment-respond.comment-form--mobile-fixed .comment-form__textarea {
|
||||
min-height: 44px;
|
||||
max-height: 120px;
|
||||
}
|
||||
|
||||
.comment-respond.comment-form--mobile-fixed .comment-form__submit-area {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
/* 当评论区可见时隐藏吸底输入框 */
|
||||
.comment-respond.comment-form--mobile-fixed.is-visible {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 暗色模式
|
||||
* ============================================================ */
|
||||
html.dark .comment-form__textarea,
|
||||
body.dark-theme .comment-form__textarea {
|
||||
background: var(--surface-2);
|
||||
border-color: var(--color-line);
|
||||
}
|
||||
|
||||
.theme-dark .comment-login-prompt__inner,
|
||||
html.dark .comment-login-prompt__inner,
|
||||
body.dark-theme .comment-login-prompt__inner {
|
||||
background: var(--surface-1);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 评论操作按钮(编辑/删除/审核)
|
||||
* ============================================================ */
|
||||
.aksrt-comment-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.aksrt-comment-action-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 3px 10px;
|
||||
border: 1px solid var(--color-line, rgba(50, 50, 50, 0.1));
|
||||
border-radius: var(--radius, 8px);
|
||||
background: var(--surface-1, #fff);
|
||||
color: var(--color-ink-3, #777);
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
.aksrt-comment-action-btn:hover {
|
||||
border-color: var(--color-primary);
|
||||
color: var(--color-primary);
|
||||
background: var(--color-primary-soft, rgba(240, 68, 148, 0.08));
|
||||
}
|
||||
|
||||
.aksrt-comment-action-btn--danger:hover {
|
||||
border-color: var(--color-error);
|
||||
color: var(--color-error);
|
||||
background: rgba(239, 68, 68, 0.08);
|
||||
}
|
||||
|
||||
.aksrt-comment-action-btn--success:hover {
|
||||
border-color: var(--color-success);
|
||||
color: var(--color-success);
|
||||
background: rgba(16, 185, 129, 0.08);
|
||||
}
|
||||
|
||||
.aksrt-comment-edit-form {
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
.aksrt-comment-edit-textarea {
|
||||
width: 100%;
|
||||
min-height: 80px;
|
||||
padding: 8px 12px;
|
||||
border: 1px solid var(--color-line, #ddd);
|
||||
border-radius: var(--radius, 8px);
|
||||
font-size: 14px;
|
||||
font-family: var(--font-body);
|
||||
color: var(--color-ink);
|
||||
background: var(--surface-1, #fff);
|
||||
resize: vertical;
|
||||
transition: border-color 0.15s;
|
||||
}
|
||||
|
||||
.aksrt-comment-edit-textarea:focus {
|
||||
outline: none;
|
||||
border-color: var(--color-primary);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 评论互动:点赞 / 置顶 / 内联管理动作(统一合同)
|
||||
* 这些动作均渲染在 .aksrt-comment__actions 内,复用其文字链按钮样式,
|
||||
* 仅补充点赞激活态、置顶徽章与危险/成功色提示。
|
||||
* ============================================================ */
|
||||
.aksrt-comment__like.is-active,
|
||||
.aksrt-comment__like.is-active i {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.aksrt-comment__like-count {
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.aksrt-comment__action--danger:hover {
|
||||
color: var(--color-error, #ef4444);
|
||||
}
|
||||
|
||||
.aksrt-comment__action--success:hover {
|
||||
color: var(--color-success, #10b981);
|
||||
}
|
||||
|
||||
/* 置顶徽章 */
|
||||
.aksrt-comment__badge--pinned {
|
||||
background: var(--color-warning-soft, rgba(245, 158, 11, 0.14));
|
||||
color: var(--color-warning, #d97706);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
}
|
||||
|
||||
/* 置顶评论容器高亮 */
|
||||
.aksrt-comment--pinned > .aksrt-comment__body {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.comment-list .comment.aksrt-comment--pinned {
|
||||
background: var(--color-warning-soft, rgba(245, 158, 11, 0.06));
|
||||
border-radius: var(--radius, 8px);
|
||||
padding-left: 12px;
|
||||
padding-right: 12px;
|
||||
}
|
||||
|
||||
/* zibll compat: emoji icon forced 30px */
|
||||
.comment-content .smilie-icon,
|
||||
.comment-form .smilie-icon {
|
||||
width: 30px !important;
|
||||
}
|
||||
|
||||
/* zibll compat: code block in comments */
|
||||
.comment-content .enlighter-default {
|
||||
margin-bottom: 0;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
|
||||
/* Identity chips + 神评 */
|
||||
.aksrt-identity-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
vertical-align: middle;
|
||||
max-width: 9em;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.aksrt-identity-chip--comment {
|
||||
font-size: 11px;
|
||||
padding: 0 6px;
|
||||
}
|
||||
.medal-icon.img-icon,
|
||||
.aksrt-identity-medal {
|
||||
display: inline-block;
|
||||
width: 1.35em;
|
||||
height: 1.35em;
|
||||
object-fit: contain;
|
||||
vertical-align: middle;
|
||||
margin-left: 4px;
|
||||
}
|
||||
.aksrt-identity-medal--comment {
|
||||
width: 1.15em;
|
||||
height: 1.15em;
|
||||
}
|
||||
.aksrt-identity-medal.is-wear {
|
||||
outline: 1px solid rgba(245, 158, 11, 0.45);
|
||||
border-radius: 3px;
|
||||
}
|
||||
.aksrt-comment__badge--hot {
|
||||
color: #ea580c;
|
||||
background: rgba(234, 88, 12, 0.1);
|
||||
border-radius: 999px;
|
||||
padding: 0 6px;
|
||||
margin-left: 4px;
|
||||
font-size: 11px;
|
||||
}
|
||||
.aksrt-comment__header .author-box {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 8.3 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 7.2 KiB |
|
After Width: | Height: | Size: 9.0 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 9.3 KiB |
|
After Width: | Height: | Size: 8.9 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 8.9 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 8.9 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 7.2 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 8.1 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 6.7 KiB |
|
After Width: | Height: | Size: 9.5 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 7.7 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 913 B |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 6.6 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 9.5 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 9.3 KiB |
|
After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 7.1 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 13 KiB |
@@ -1,23 +1,15 @@
|
||||
/**
|
||||
* AKSRT Comment Actions client
|
||||
*
|
||||
* Single client for per-comment actions via REST aksrt/v1:
|
||||
* like / edit / delete / moderate (approve|hold|spam) / pin
|
||||
* REST aksrt/v1: like / delete / moderate / pin.
|
||||
* Edit uses moveForm (#respond) via comment-move-form.js — not inline textarea.
|
||||
* Submit owned by assets/js/core/comments.js (single submit contract).
|
||||
*
|
||||
* Comment SUBMISSION is owned by main.js initCommentSystem() (the native
|
||||
* wp-comments-post.php flow, which runs captcha verification, rate limiting and
|
||||
* the moderation pipeline, then re-renders the list through the canonical
|
||||
* server callback). This file deliberately does NOT bind the submit form, so
|
||||
* there is exactly one comment-submit contract.
|
||||
*
|
||||
* Markup contract (rendered by aksrt_comment_callback, identical on first
|
||||
* paint, load-more and post-submit reload):
|
||||
* Markup (aksrt_comment_callback):
|
||||
* li.aksrt-comment[data-comment-id]
|
||||
* .aksrt-comment__text (content)
|
||||
* .aksrt-comment__actions
|
||||
* [data-comment-action="like|edit|delete|approve|hold|spam|pin"]
|
||||
*
|
||||
* Depends on: aksrtData.restNonce (wp_rest) + aksrtData.siteUrl.
|
||||
* .comment-header .aksrt-comment__like[data-comment-action=like]
|
||||
* .comment-content#comment-content-{id}
|
||||
* .comt-meta [data-comment-action=pin|approve|hold|spam|delete]
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
@@ -66,11 +58,10 @@
|
||||
var commentId = getCommentId(commentEl);
|
||||
if (!commentId) return;
|
||||
|
||||
// edit is handled by comment-move-form.js (moveForm into #respond).
|
||||
switch (action) {
|
||||
case 'like': handleLike(actionBtn, commentId); break;
|
||||
case 'edit': handleEdit(commentEl); break;
|
||||
case 'save-edit': saveEdit(commentEl, commentId, actionBtn); break;
|
||||
case 'cancel-edit': cancelEdit(commentEl); break;
|
||||
case 'edit': break;
|
||||
case 'delete': handleDelete(commentEl, commentId); break;
|
||||
case 'pin': handlePin(commentEl, actionBtn, commentId); break;
|
||||
case 'approve':
|
||||
@@ -104,63 +95,6 @@
|
||||
.finally(function () { btn.dataset.busy = ''; });
|
||||
}
|
||||
|
||||
function handleEdit(commentEl) {
|
||||
var contentEl = commentEl.querySelector(CONTENT_SELECTOR);
|
||||
if (!contentEl || commentEl.querySelector('.aksrt-comment-edit-form')) return;
|
||||
|
||||
var temp = document.createElement('div');
|
||||
temp.innerHTML = contentEl.innerHTML;
|
||||
var originalText = (temp.textContent || temp.innerText || '').trim();
|
||||
|
||||
var editForm = document.createElement('div');
|
||||
editForm.className = 'aksrt-comment-edit-form';
|
||||
editForm.innerHTML =
|
||||
'<textarea class="aksrt-comment-edit-textarea"></textarea>' +
|
||||
'<div class="aksrt-comment-actions">' +
|
||||
'<button type="button" data-comment-action="save-edit" class="aksrt-comment-action-btn aksrt-comment-action-btn--success">保存</button>' +
|
||||
'<button type="button" data-comment-action="cancel-edit" class="aksrt-comment-action-btn">取消</button>' +
|
||||
'</div>';
|
||||
editForm.querySelector('textarea').value = originalText;
|
||||
|
||||
contentEl.style.display = 'none';
|
||||
contentEl.parentNode.insertBefore(editForm, contentEl.nextSibling);
|
||||
editForm.querySelector('textarea').focus();
|
||||
}
|
||||
|
||||
function cancelEdit(commentEl) {
|
||||
var editForm = commentEl.querySelector('.aksrt-comment-edit-form');
|
||||
if (!editForm) return;
|
||||
var contentEl = commentEl.querySelector(CONTENT_SELECTOR);
|
||||
if (contentEl) contentEl.style.display = '';
|
||||
editForm.remove();
|
||||
}
|
||||
|
||||
function saveEdit(commentEl, commentId, saveBtn) {
|
||||
var editForm = commentEl.querySelector('.aksrt-comment-edit-form');
|
||||
if (!editForm) return;
|
||||
var textarea = editForm.querySelector('.aksrt-comment-edit-textarea');
|
||||
var newContent = textarea ? textarea.value.trim() : '';
|
||||
if (!newContent) return;
|
||||
|
||||
if (saveBtn) { saveBtn.disabled = true; saveBtn.textContent = '保存中...'; }
|
||||
|
||||
restPost('/comment/edit', { comment_id: commentId, content: newContent })
|
||||
.then(function (result) {
|
||||
if (result.success) {
|
||||
var contentEl = commentEl.querySelector(CONTENT_SELECTOR);
|
||||
if (contentEl) { contentEl.innerHTML = result.data.content; contentEl.style.display = ''; }
|
||||
editForm.remove();
|
||||
} else {
|
||||
notify((result.data && result.data.message) || result.message || '编辑失败', 'error');
|
||||
if (saveBtn) { saveBtn.disabled = false; saveBtn.textContent = '保存'; }
|
||||
}
|
||||
})
|
||||
.catch(function () {
|
||||
notify('网络错误', 'error');
|
||||
if (saveBtn) { saveBtn.disabled = false; saveBtn.textContent = '保存'; }
|
||||
});
|
||||
}
|
||||
|
||||
function handleDelete(commentEl, commentId) {
|
||||
var msg = (window.aksrt && aksrt.__) ? aksrt.__('comment_confirm_delete', '确定要删除这条评论吗?') : '确定要删除这条评论吗?';
|
||||
var doDelete = function () {
|
||||
|
||||
@@ -1,422 +1,141 @@
|
||||
/**
|
||||
* AKSRT Comment Tools
|
||||
*
|
||||
* Handles comment toolbar actions (emoji, code, bold, italic, quote, link),
|
||||
* Ctrl+Enter submit, and quick quote from selection.
|
||||
*
|
||||
* @package AKSRT
|
||||
* Comment expand tools — zibll-style shortcode inserts ([g=] [code] [img=] quick).
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var COMMENT_TOOLS = {
|
||||
emojiPicker: null,
|
||||
quickReplyPanel: null,
|
||||
quickQuoteEnabled: false,
|
||||
quickReplyTemplates: [
|
||||
'感谢分享,这篇内容对我很有帮助!',
|
||||
'已收藏,后面会按这个思路继续研究。',
|
||||
'这个细节讲得很清楚,学习了。',
|
||||
'我这边也复现成功了,补充一点经验:',
|
||||
'先留个脚印,等我测试后再回来反馈结果。'
|
||||
],
|
||||
|
||||
init: function () {
|
||||
this.bindToolbar();
|
||||
this.bindCtrlEnter();
|
||||
this.bindQuickQuote();
|
||||
},
|
||||
|
||||
/**
|
||||
* Insert text at cursor position in textarea
|
||||
*/
|
||||
insertAtCursor: function (textarea, before, after) {
|
||||
if (!textarea) return;
|
||||
var start = textarea.selectionStart;
|
||||
var end = textarea.selectionEnd;
|
||||
var text = textarea.value;
|
||||
var selected = text.substring(start, end);
|
||||
var replacement = before + (selected || '') + (after || '');
|
||||
textarea.value = text.substring(0, start) + replacement + text.substring(end);
|
||||
textarea.focus();
|
||||
var newPos = start + before.length + selected.length;
|
||||
textarea.setSelectionRange(newPos, newPos);
|
||||
},
|
||||
|
||||
/**
|
||||
* Wrap selected text with markers
|
||||
*/
|
||||
wrapSelection: function (textarea, marker) {
|
||||
this.insertAtCursor(textarea, marker, marker);
|
||||
},
|
||||
|
||||
uploadImage: function (btn, textarea) {
|
||||
if (!window.aksrtData || !aksrtData.commentUploadEnabled || !aksrtData.commentUploadUrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
var input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.accept = 'image/jpeg,image/png,image/gif,image/webp';
|
||||
input.style.display = 'none';
|
||||
document.body.appendChild(input);
|
||||
|
||||
var self = this;
|
||||
input.addEventListener('change', function () {
|
||||
var file = input.files && input.files[0];
|
||||
if (!file) {
|
||||
input.remove();
|
||||
return;
|
||||
}
|
||||
|
||||
var maxSize = parseInt(aksrtData.commentUploadMaxSize || 0, 10) || (2 * 1024 * 1024);
|
||||
if (file.size > maxSize) {
|
||||
window.alert('图片大小不能超过 2MB');
|
||||
input.remove();
|
||||
return;
|
||||
}
|
||||
|
||||
btn.disabled = true;
|
||||
var originalHtml = btn.innerHTML;
|
||||
btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i>';
|
||||
|
||||
var formData = new FormData();
|
||||
formData.append('comment_image', file);
|
||||
|
||||
fetch(aksrtData.commentUploadUrl, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
'X-WP-Nonce': aksrtData.restNonce || ''
|
||||
}
|
||||
})
|
||||
.then(function (response) { return response.json(); })
|
||||
.then(function (payload) {
|
||||
if (!payload || payload.success !== true || !payload.url) {
|
||||
throw new Error((payload && payload.message) || '上传失败');
|
||||
}
|
||||
self.insertAtCursor(textarea, '[img]' + payload.url + '[/img]', '');
|
||||
})
|
||||
.catch(function (error) {
|
||||
window.alert(error && error.message ? error.message : '上传失败');
|
||||
})
|
||||
.finally(function () {
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = originalHtml;
|
||||
input.remove();
|
||||
});
|
||||
});
|
||||
|
||||
input.click();
|
||||
},
|
||||
|
||||
/**
|
||||
* Bind toolbar button actions
|
||||
*/
|
||||
bindToolbar: function () {
|
||||
var self = this;
|
||||
document.addEventListener('click', function (e) {
|
||||
var btn = e.target.closest('[data-toolbar-action]');
|
||||
if (!btn) return;
|
||||
|
||||
var action = btn.getAttribute('data-toolbar-action');
|
||||
var form = btn.closest('.comment-form');
|
||||
var textarea = form ? form.querySelector('textarea') : document.getElementById('comment');
|
||||
if (!textarea) return;
|
||||
|
||||
switch (action) {
|
||||
case 'bold':
|
||||
self.wrapSelection(textarea, '**');
|
||||
break;
|
||||
case 'italic':
|
||||
self.wrapSelection(textarea, '*');
|
||||
break;
|
||||
case 'code':
|
||||
var selected = textarea.value.substring(textarea.selectionStart, textarea.selectionEnd);
|
||||
if (selected.indexOf('\n') >= 0) {
|
||||
self.insertAtCursor(textarea, '```\n', '\n```');
|
||||
} else {
|
||||
self.wrapSelection(textarea, '`');
|
||||
}
|
||||
break;
|
||||
case 'quote':
|
||||
self.insertAtCursor(textarea, '> ', '\n');
|
||||
break;
|
||||
case 'link':
|
||||
self.askUrl(btn, '链接地址', function (url) {
|
||||
if (url) {
|
||||
var linkText = textarea.value.substring(textarea.selectionStart, textarea.selectionEnd) || '链接文字';
|
||||
self.insertAtCursor(textarea, '[' + linkText + '](' + url + ')');
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'image':
|
||||
self.askUrl(btn, '图片地址', function (url) {
|
||||
if (url) {
|
||||
self.insertAtCursor(textarea, '[img]' + url + '[/img]');
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'upload-image':
|
||||
self.uploadImage(btn, textarea);
|
||||
break;
|
||||
case 'emoji':
|
||||
self.toggleEmojiPicker(btn, textarea);
|
||||
break;
|
||||
case 'quick-reply':
|
||||
self.toggleQuickReply(btn, textarea);
|
||||
break;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Inline URL input — replaces native prompt() with a small input below the button.
|
||||
*/
|
||||
askUrl: function (anchor, placeholder, callback) {
|
||||
var existing = document.querySelector('.aksrt-inline-url-input');
|
||||
if (existing) existing.remove();
|
||||
|
||||
var input = document.createElement('input');
|
||||
input.type = 'url';
|
||||
input.className = 'aksrt-inline-url-input';
|
||||
input.placeholder = placeholder || 'https://';
|
||||
input.value = 'https://';
|
||||
input.style.cssText = 'display:block;width:100%;max-width:320px;margin:4px 0;padding:6px 10px;border:1px solid #dce3ec;border-radius:8px;font-size:13px;';
|
||||
|
||||
var parent = anchor.parentElement;
|
||||
parent.insertBefore(input, anchor.nextSibling);
|
||||
input.focus();
|
||||
input.select();
|
||||
|
||||
function done() {
|
||||
var val = input.value.trim();
|
||||
input.remove();
|
||||
callback(val);
|
||||
}
|
||||
|
||||
input.addEventListener('keydown', function (e) {
|
||||
if (e.key === 'Enter') { e.preventDefault(); done(); }
|
||||
if (e.key === 'Escape') { input.remove(); callback(''); }
|
||||
});
|
||||
input.addEventListener('blur', function () { done(); });
|
||||
},
|
||||
|
||||
/**
|
||||
* Simple emoji picker
|
||||
*/
|
||||
toggleEmojiPicker: function (btn, textarea) {
|
||||
var self = this;
|
||||
this.removeQuickReplyPanel();
|
||||
if (this.emojiPicker) {
|
||||
this.emojiPicker.remove();
|
||||
this.emojiPicker = null;
|
||||
return;
|
||||
}
|
||||
|
||||
var emojis = [
|
||||
{ key: 'grinning', emoji: '😀' },
|
||||
{ key: 'joy', emoji: '😂' },
|
||||
{ key: 'rofl', emoji: '🤣' },
|
||||
{ key: 'blush', emoji: '😊' },
|
||||
{ key: 'heart-eyes', emoji: '😍' },
|
||||
{ key: 'smiling-hearts', emoji: '🥰' },
|
||||
{ key: 'sunglasses', emoji: '😎' },
|
||||
{ key: 'thinking', emoji: '🤔' },
|
||||
{ key: 'cry', emoji: '😢' },
|
||||
{ key: 'sob', emoji: '😭' },
|
||||
{ key: 'angry', emoji: '😡' },
|
||||
{ key: 'pleading', emoji: '🥺' },
|
||||
{ key: 'scream', emoji: '😱' },
|
||||
{ key: 'rolling-eyes', emoji: '🙄' },
|
||||
{ key: 'sleeping', emoji: '😴' },
|
||||
{ key: 'nauseated', emoji: '🤮' },
|
||||
{ key: 'thumbs-up', emoji: '👍' },
|
||||
{ key: 'thumbs-down', emoji: '👎' },
|
||||
{ key: 'clap', emoji: '👏' },
|
||||
{ key: 'pray', emoji: '🙏' },
|
||||
{ key: 'muscle', emoji: '💪' },
|
||||
{ key: 'heart', emoji: '❤️' },
|
||||
{ key: 'fire', emoji: '🔥' },
|
||||
{ key: 'star', emoji: '⭐' },
|
||||
{ key: 'tada', emoji: '🎉' },
|
||||
{ key: 'confetti', emoji: '🎊' },
|
||||
{ key: 'check', emoji: '✅' },
|
||||
{ key: 'cross', emoji: '❌' },
|
||||
{ key: 'warning', emoji: '⚠️' },
|
||||
{ key: 'hundred', emoji: '💯' },
|
||||
{ key: 'ok', emoji: '🆗' },
|
||||
{ key: 'new', emoji: '🆕' }
|
||||
];
|
||||
|
||||
var picker = document.createElement('div');
|
||||
picker.className = 'aksrt-emoji-picker';
|
||||
picker.style.cssText = 'position:absolute;bottom:100%;left:0;background:#fff;border:1px solid #e8e8e8;border-radius:8px;padding:8px;display:grid;grid-template-columns:repeat(8,1fr);gap:4px;z-index:100;box-shadow:0 4px 12px rgba(0,0,0,0.1);';
|
||||
|
||||
emojis.forEach(function (entry) {
|
||||
var item = document.createElement('button');
|
||||
item.type = 'button';
|
||||
item.style.cssText = 'border:none;background:none;cursor:pointer;font-size:18px;padding:4px;border-radius:4px;transition:background 0.15s;';
|
||||
item.textContent = entry.emoji;
|
||||
item.setAttribute('title', entry.key);
|
||||
item.addEventListener('mouseenter', function () { item.style.background = '#f5f5f5'; });
|
||||
item.addEventListener('mouseleave', function () { item.style.background = 'none'; });
|
||||
item.addEventListener('click', function (e) {
|
||||
e.preventDefault();
|
||||
self.insertAtCursor(textarea, '[g=' + entry.key + ']', '');
|
||||
picker.remove();
|
||||
self.emojiPicker = null;
|
||||
});
|
||||
picker.appendChild(item);
|
||||
});
|
||||
|
||||
btn.style.position = 'relative';
|
||||
btn.appendChild(picker);
|
||||
this.emojiPicker = picker;
|
||||
|
||||
// Close on outside click
|
||||
var closeHandler = function (e) {
|
||||
if (!picker.contains(e.target) && e.target !== btn) {
|
||||
picker.remove();
|
||||
self.emojiPicker = null;
|
||||
document.removeEventListener('click', closeHandler);
|
||||
}
|
||||
};
|
||||
setTimeout(function () { document.addEventListener('click', closeHandler); }, 0);
|
||||
},
|
||||
|
||||
removeQuickReplyPanel: function () {
|
||||
if (this.quickReplyPanel) {
|
||||
this.quickReplyPanel.remove();
|
||||
this.quickReplyPanel = null;
|
||||
}
|
||||
},
|
||||
|
||||
toggleQuickReply: function (btn, textarea) {
|
||||
var self = this;
|
||||
if (this.quickReplyPanel) {
|
||||
this.removeQuickReplyPanel();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.emojiPicker) {
|
||||
this.emojiPicker.remove();
|
||||
this.emojiPicker = null;
|
||||
}
|
||||
|
||||
var panel = document.createElement('div');
|
||||
panel.className = 'comment-quick-reply';
|
||||
|
||||
var title = document.createElement('div');
|
||||
title.className = 'comment-quick-reply__title';
|
||||
title.textContent = '快捷回复';
|
||||
panel.appendChild(title);
|
||||
|
||||
var templates = document.createElement('div');
|
||||
templates.className = 'comment-quick-reply__templates';
|
||||
|
||||
this.quickReplyTemplates.forEach(function (text) {
|
||||
var item = document.createElement('button');
|
||||
item.type = 'button';
|
||||
item.className = 'comment-quick-reply__template';
|
||||
item.textContent = text;
|
||||
item.addEventListener('click', function (e) {
|
||||
e.preventDefault();
|
||||
var needsLineBreak = textarea.value && !/\n\s*$/.test(textarea.value);
|
||||
var prefix = needsLineBreak ? '\n' : '';
|
||||
self.insertAtCursor(textarea, prefix + text, '');
|
||||
self.removeQuickReplyPanel();
|
||||
});
|
||||
templates.appendChild(item);
|
||||
});
|
||||
|
||||
panel.appendChild(templates);
|
||||
|
||||
var toolbar = btn.closest('.comment-toolbar');
|
||||
if (toolbar && toolbar.parentNode) {
|
||||
toolbar.parentNode.insertBefore(panel, toolbar.nextSibling);
|
||||
} else {
|
||||
btn.parentNode.appendChild(panel);
|
||||
}
|
||||
|
||||
this.quickReplyPanel = panel;
|
||||
|
||||
var closeHandler = function (e) {
|
||||
if (!panel.contains(e.target) && e.target !== btn) {
|
||||
self.removeQuickReplyPanel();
|
||||
document.removeEventListener('click', closeHandler);
|
||||
}
|
||||
};
|
||||
setTimeout(function () { document.addEventListener('click', closeHandler); }, 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* Ctrl+Enter to submit comment
|
||||
*/
|
||||
bindCtrlEnter: function () {
|
||||
document.addEventListener('keydown', function (e) {
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
|
||||
var textarea = e.target;
|
||||
if (textarea.tagName !== 'TEXTAREA') return;
|
||||
var form = textarea.closest('form');
|
||||
if (!form) return;
|
||||
var submitBtn = form.querySelector('[type="submit"]');
|
||||
if (submitBtn) {
|
||||
submitBtn.click();
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Quick quote: select text on page, show "quote in comment" button
|
||||
*/
|
||||
bindQuickQuote: function () {
|
||||
if (!this.quickQuoteEnabled) return;
|
||||
var self = this;
|
||||
var quoteBtn = null;
|
||||
|
||||
document.addEventListener('mouseup', function () {
|
||||
setTimeout(function () {
|
||||
if (quoteBtn) {
|
||||
quoteBtn.remove();
|
||||
quoteBtn = null;
|
||||
}
|
||||
|
||||
var selection = window.getSelection();
|
||||
var text = selection.toString().trim();
|
||||
if (text.length < 5 || text.length > 500) return;
|
||||
|
||||
var range = selection.getRangeAt(0);
|
||||
var rect = range.getBoundingClientRect();
|
||||
|
||||
quoteBtn = document.createElement('button');
|
||||
quoteBtn.type = 'button';
|
||||
quoteBtn.textContent = '引用评论';
|
||||
quoteBtn.style.cssText = 'position:fixed;top:' + (rect.top - 36) + 'px;left:' + (rect.left + rect.width / 2) + 'px;transform:translateX(-50%);padding:4px 12px;background:#1890ff;color:#fff;border:none;border-radius:4px;font-size:12px;cursor:pointer;z-index:9999;box-shadow:0 2px 8px rgba(0,0,0,0.15);';
|
||||
quoteBtn.addEventListener('click', function () {
|
||||
var textarea = document.getElementById('comment');
|
||||
if (textarea) {
|
||||
var quoted = text.split('\n').map(function (line) { return '> ' + line; }).join('\n') + '\n\n';
|
||||
self.insertAtCursor(textarea, quoted, '');
|
||||
textarea.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
textarea.focus();
|
||||
}
|
||||
quoteBtn.remove();
|
||||
quoteBtn = null;
|
||||
});
|
||||
document.body.appendChild(quoteBtn);
|
||||
}, 10);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
// Initialize when DOM is ready
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', function () { COMMENT_TOOLS.init(); });
|
||||
} else {
|
||||
COMMENT_TOOLS.init();
|
||||
function notify(msg) {
|
||||
if (window.aksrt && typeof window.aksrt.toast === 'function') {
|
||||
window.aksrt.toast(msg, 'info');
|
||||
}
|
||||
}
|
||||
|
||||
function findTextarea(from) {
|
||||
var form = from && from.closest ? from.closest('form') : null;
|
||||
if (form) {
|
||||
return form.querySelector('textarea.grin, textarea#comment, textarea[name="comment"]');
|
||||
}
|
||||
return document.querySelector('#comment, textarea.grin');
|
||||
}
|
||||
|
||||
function insertAtCursor(textarea, value) {
|
||||
if (!textarea) return;
|
||||
var start = textarea.selectionStart || 0;
|
||||
var end = textarea.selectionEnd || 0;
|
||||
var val = textarea.value || '';
|
||||
textarea.value = val.slice(0, start) + value + val.slice(end);
|
||||
var pos = start + value.length;
|
||||
textarea.selectionStart = textarea.selectionEnd = pos;
|
||||
textarea.focus();
|
||||
textarea.dispatchEvent(new Event('input'));
|
||||
}
|
||||
|
||||
function restUpload(file) {
|
||||
var d = window.aksrtData || {};
|
||||
var url = d.commentUploadUrl || ((d.restUrl || '/wp-json/aksrt/v1').replace(/\/$/, '') + '/comment/upload-image');
|
||||
var fd = new FormData();
|
||||
fd.append('image', file);
|
||||
fd.append('file', file);
|
||||
return fetch(url, {
|
||||
method: 'POST',
|
||||
headers: { 'X-WP-Nonce': d.restNonce || d.nonce || '' },
|
||||
credentials: 'same-origin',
|
||||
body: fd
|
||||
}).then(function (r) { return r.json(); });
|
||||
}
|
||||
|
||||
document.addEventListener('click', function (e) {
|
||||
var sm = e.target && e.target.closest ? e.target.closest('[data-smilie], a.smilie-icon') : null;
|
||||
if (sm) {
|
||||
e.preventDefault();
|
||||
var key = sm.getAttribute('data-smilie');
|
||||
if (!key && sm.querySelector) {
|
||||
var img = sm.querySelector('[data-smilie]');
|
||||
if (img) key = img.getAttribute('data-smilie');
|
||||
}
|
||||
if (!key) return;
|
||||
insertAtCursor(findTextarea(sm), '[g=' + key + ']');
|
||||
var wrap = sm.closest('.dropup, .input-expand-wrap');
|
||||
if (wrap) wrap.classList.remove('open');
|
||||
return;
|
||||
}
|
||||
|
||||
var codeBtn = e.target && e.target.closest ? e.target.closest('.input-code-submit') : null;
|
||||
if (codeBtn) {
|
||||
e.preventDefault();
|
||||
var box = codeBtn.closest('.dropdown-code, .input-expand-code');
|
||||
var ta = box ? box.querySelector('textarea') : null;
|
||||
var code = ta ? ta.value : '';
|
||||
if (!code || code.length < 2) {
|
||||
notify('请输入代码');
|
||||
return;
|
||||
}
|
||||
insertAtCursor(findTextarea(codeBtn), '[code]\n' + code + '\n[/code]\n');
|
||||
if (ta) ta.value = '';
|
||||
var w = codeBtn.closest('.dropup, .input-expand-wrap');
|
||||
if (w) w.classList.remove('open');
|
||||
return;
|
||||
}
|
||||
|
||||
var imgBtn = e.target && e.target.closest ? e.target.closest('.input-image-submit') : null;
|
||||
if (imgBtn) {
|
||||
e.preventDefault();
|
||||
var ibox = imgBtn.closest('.dropdown-image, .input-expand-image');
|
||||
var input = ibox ? ibox.querySelector('.input-image-url') : null;
|
||||
var url = input ? (input.value || '').trim() : '';
|
||||
if (!/^https?:\/\//i.test(url)) {
|
||||
notify('请输入有效图片地址');
|
||||
return;
|
||||
}
|
||||
insertAtCursor(findTextarea(imgBtn), '[img=' + url + ']');
|
||||
if (input) input.value = '';
|
||||
var iw = imgBtn.closest('.dropup, .input-expand-wrap');
|
||||
if (iw) iw.classList.remove('open');
|
||||
return;
|
||||
}
|
||||
|
||||
var upBtn = e.target && e.target.closest ? e.target.closest('.input-image-upload') : null;
|
||||
if (upBtn) {
|
||||
e.preventDefault();
|
||||
var fileInput = document.createElement('input');
|
||||
fileInput.type = 'file';
|
||||
fileInput.accept = 'image/jpeg,image/png,image/gif,image/webp';
|
||||
fileInput.addEventListener('change', function () {
|
||||
var file = fileInput.files && fileInput.files[0];
|
||||
if (!file) return;
|
||||
restUpload(file)
|
||||
.then(function (json) {
|
||||
var u = (json && (json.url || json.source_url || (json.data && (json.data.url || json.data.source_url)))) || '';
|
||||
if (!u) {
|
||||
notify((json && json.message) || '上传失败');
|
||||
return;
|
||||
}
|
||||
insertAtCursor(findTextarea(upBtn), '[img=' + u + ']');
|
||||
})
|
||||
.catch(function () { notify('上传失败'); });
|
||||
});
|
||||
fileInput.click();
|
||||
return;
|
||||
}
|
||||
|
||||
var q = e.target && e.target.closest ? e.target.closest('.quick-reply-item') : null;
|
||||
if (q) {
|
||||
e.preventDefault();
|
||||
var phrase = q.getAttribute('data-text') || q.textContent || '';
|
||||
insertAtCursor(findTextarea(q), phrase);
|
||||
var qw = q.closest('.dropup, .input-expand-wrap');
|
||||
if (qw) qw.classList.remove('open');
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('keydown', function (e) {
|
||||
if (!(e.ctrlKey || e.metaKey) || e.key !== 'Enter') return;
|
||||
var ta = e.target;
|
||||
if (!ta || ta.id !== 'comment') return;
|
||||
var form = ta.closest('#commentform');
|
||||
if (!form) return;
|
||||
e.preventDefault();
|
||||
var submit = form.querySelector('#submit, [type="submit"]');
|
||||
if (submit) submit.click();
|
||||
});
|
||||
})();
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* Mobile FixedInput + autoheight for comment respond (zibll parity).
|
||||
*/
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
function $(sel, root) {
|
||||
return (root || document).querySelector(sel);
|
||||
}
|
||||
|
||||
function FixedInput(el, action) {
|
||||
if (!el) return;
|
||||
if (!el.dataset.fixedBound) {
|
||||
el.addEventListener('click', function (e) {
|
||||
if (e.target && e.target.closest && e.target.closest('.fixed-body')) {
|
||||
FixedInput(el, 'hide');
|
||||
}
|
||||
});
|
||||
el.dataset.fixedBound = '1';
|
||||
}
|
||||
if (action === 'show') {
|
||||
el.classList.add('show');
|
||||
window.setTimeout(function () {
|
||||
var field = el.querySelector('textarea, input');
|
||||
if (field) field.focus();
|
||||
}, 100);
|
||||
return;
|
||||
}
|
||||
if (action === 'hide') {
|
||||
var cancel = el.querySelector('#cancel-comment-reply-link');
|
||||
if (cancel) cancel.click();
|
||||
el.querySelectorAll('input,textarea').forEach(function (n) {
|
||||
try { n.blur(); } catch (e) {}
|
||||
});
|
||||
el.querySelectorAll('.dropup.open, .open').forEach(function (n) {
|
||||
n.classList.remove('open');
|
||||
});
|
||||
el.classList.remove('show');
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('click', function (e) {
|
||||
var trigger = e.target && e.target.closest ? e.target.closest('[fixed-input]') : null;
|
||||
if (!trigger) return;
|
||||
e.preventDefault();
|
||||
var targetSel = trigger.getAttribute('fixed-input') || '#respond';
|
||||
var panel = document.querySelector(targetSel);
|
||||
var comments = document.getElementById('comments');
|
||||
if (comments && comments.scrollIntoView) {
|
||||
comments.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
}
|
||||
FixedInput(panel, 'show');
|
||||
});
|
||||
|
||||
// Autoheight textareas.
|
||||
function bindAutoheight(ta) {
|
||||
if (!ta || ta.dataset.autoheightBound) return;
|
||||
ta.dataset.autoheightBound = '1';
|
||||
var max = parseInt(ta.getAttribute('maxheight') || '188', 10) || 188;
|
||||
var resize = function () {
|
||||
ta.style.height = 'auto';
|
||||
var h = Math.min(ta.scrollHeight, max);
|
||||
ta.style.height = h + 'px';
|
||||
ta.style.overflowY = ta.scrollHeight > max ? 'auto' : 'hidden';
|
||||
};
|
||||
ta.addEventListener('input', resize);
|
||||
resize();
|
||||
}
|
||||
|
||||
function scan() {
|
||||
document.querySelectorAll('textarea[autoheight], textarea.grin').forEach(bindAutoheight);
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', scan);
|
||||
if (window.AksrtFrontend && typeof window.AksrtFrontend.register === 'function') {
|
||||
window.AksrtFrontend.register('aksrt-comment-fixed-input', function (root) {
|
||||
scan();
|
||||
// Expose for submit hide.
|
||||
root = root || document;
|
||||
var respond = root.querySelector ? root.querySelector('#respond') : document.getElementById('respond');
|
||||
if (respond) {
|
||||
respond.aksrtFixedInput = function (action) {
|
||||
FixedInput(respond, action);
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
window.AksrtFixedInput = FixedInput;
|
||||
})();
|
||||
@@ -0,0 +1,243 @@
|
||||
/**
|
||||
* Comment moveForm for reply/edit (zibll parity) — REST-backed edit fetch.
|
||||
*/
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var respondId = 'respond';
|
||||
var tempId = 'aksrt-wp-temp-form-div';
|
||||
var editingId = 0;
|
||||
|
||||
function getRespond() {
|
||||
return document.getElementById(respondId);
|
||||
}
|
||||
|
||||
function restRoot() {
|
||||
var d = window.aksrtData || {};
|
||||
return (d.restUrl || d.rest_url || '/wp-json/aksrt/v1').replace(/\/$/, '');
|
||||
}
|
||||
|
||||
function nonce() {
|
||||
var d = window.aksrtData || {};
|
||||
return d.restNonce || d.nonce || '';
|
||||
}
|
||||
|
||||
function notify(msg, type) {
|
||||
if (window.aksrt && typeof window.aksrt.toast === 'function') {
|
||||
window.aksrt.toast(msg, type || 'info');
|
||||
return;
|
||||
}
|
||||
if (window.notyf) {
|
||||
try { window.notyf[type === 'error' ? 'error' : 'success'](msg); } catch (e) {}
|
||||
}
|
||||
}
|
||||
|
||||
function setActionText(text) {
|
||||
var el = document.querySelector('#' + respondId + ' .action-text');
|
||||
if (!el) return;
|
||||
if (text) {
|
||||
el.style.display = '';
|
||||
el.textContent = text;
|
||||
} else {
|
||||
el.style.display = 'none';
|
||||
el.textContent = '';
|
||||
}
|
||||
}
|
||||
|
||||
function ensureParentField(respond) {
|
||||
var form = respond.querySelector('#commentform') || respond;
|
||||
var parent = form.querySelector('[name="comment_parent"]');
|
||||
if (!parent) {
|
||||
parent = document.createElement('input');
|
||||
parent.type = 'hidden';
|
||||
parent.name = 'comment_parent';
|
||||
parent.id = 'comment_parent';
|
||||
parent.value = '0';
|
||||
form.appendChild(parent);
|
||||
}
|
||||
return parent;
|
||||
}
|
||||
|
||||
function ensureEditField(respond, commentId) {
|
||||
var form = respond.querySelector('#commentform') || respond;
|
||||
var existing = form.querySelector('[name="edit_comment_ID"]');
|
||||
if (commentId) {
|
||||
if (!existing) {
|
||||
existing = document.createElement('input');
|
||||
existing.type = 'hidden';
|
||||
existing.name = 'edit_comment_ID';
|
||||
existing.id = 'edit_comment_ID';
|
||||
form.appendChild(existing);
|
||||
}
|
||||
existing.value = String(commentId);
|
||||
// Hide parent while editing.
|
||||
var parent = form.querySelector('[name="comment_parent"]');
|
||||
if (parent) parent.name = 'comment_parent_bak';
|
||||
} else if (existing) {
|
||||
existing.remove();
|
||||
var bak = form.querySelector('[name="comment_parent_bak"]');
|
||||
if (bak) bak.name = 'comment_parent';
|
||||
}
|
||||
}
|
||||
|
||||
function moveForm(commId, parentId, isEdit) {
|
||||
var respond = getRespond();
|
||||
if (!respond) return false;
|
||||
var comm = document.getElementById(commId);
|
||||
var parent = ensureParentField(respond);
|
||||
var cancel = document.getElementById('cancel-comment-reply-link');
|
||||
var submit = respond.querySelector('#submit');
|
||||
var textarea = respond.querySelector('#comment');
|
||||
|
||||
if (!document.getElementById(tempId)) {
|
||||
var temp = document.createElement('div');
|
||||
temp.id = tempId;
|
||||
temp.style.display = 'none';
|
||||
respond.parentNode.insertBefore(temp, respond);
|
||||
}
|
||||
|
||||
if (comm && comm.parentNode) {
|
||||
comm.parentNode.insertBefore(respond, comm.nextSibling);
|
||||
}
|
||||
|
||||
parent.value = isEdit ? '0' : String(parentId || '0');
|
||||
editingId = isEdit ? parseInt(parentId, 10) || 0 : 0;
|
||||
|
||||
if (cancel) {
|
||||
cancel.style.display = '';
|
||||
cancel.onclick = function () {
|
||||
var t = document.getElementById(tempId);
|
||||
var r = getRespond();
|
||||
if (t && r && t.parentNode) {
|
||||
t.parentNode.insertBefore(r, t);
|
||||
t.parentNode.removeChild(t);
|
||||
}
|
||||
ensureEditField(r, 0);
|
||||
if (parent) parent.value = '0';
|
||||
setActionText('');
|
||||
if (textarea) textarea.value = '';
|
||||
editingId = 0;
|
||||
cancel.style.display = 'none';
|
||||
if (window.AksrtFixedInput) window.AksrtFixedInput(r, 'hide');
|
||||
else if (r) r.classList.remove('show');
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
if (isEdit && editingId) {
|
||||
setActionText('编辑评论');
|
||||
ensureEditField(respond, editingId);
|
||||
if (textarea) {
|
||||
textarea.disabled = true;
|
||||
textarea.value = '加载中…';
|
||||
}
|
||||
if (submit) submit.disabled = true;
|
||||
fetch(restRoot() + '/comment/get?comment_id=' + encodeURIComponent(String(editingId)), {
|
||||
headers: { 'X-WP-Nonce': nonce() },
|
||||
credentials: 'same-origin',
|
||||
})
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (json) {
|
||||
var content = (json && (json.content || (json.data && json.data.content))) || '';
|
||||
if (textarea) {
|
||||
textarea.disabled = false;
|
||||
textarea.value = content;
|
||||
textarea.dispatchEvent(new Event('input'));
|
||||
}
|
||||
if (submit) submit.disabled = false;
|
||||
})
|
||||
.catch(function () {
|
||||
notify('加载评论失败', 'error');
|
||||
if (textarea) {
|
||||
textarea.disabled = false;
|
||||
textarea.value = '';
|
||||
}
|
||||
if (submit) submit.disabled = false;
|
||||
});
|
||||
} else {
|
||||
setActionText('回复评论');
|
||||
ensureEditField(respond, 0);
|
||||
if (textarea) {
|
||||
textarea.focus();
|
||||
}
|
||||
}
|
||||
|
||||
if (window.AksrtFixedInput) {
|
||||
window.AksrtFixedInput(respond, 'show');
|
||||
} else {
|
||||
respond.classList.add('show');
|
||||
}
|
||||
|
||||
if (comm && comm.scrollIntoView) {
|
||||
comm.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
document.addEventListener('click', function (e) {
|
||||
var reply = e.target && e.target.closest ? e.target.closest('.comment-reply-link') : null;
|
||||
var edit = e.target && e.target.closest ? e.target.closest('.comment-edit-link, [data-comment-action="edit"]') : null;
|
||||
if (reply) {
|
||||
e.preventDefault();
|
||||
var cid = reply.getAttribute('data-commentid') || reply.getAttribute('data-comment-id') || '';
|
||||
var postid = reply.getAttribute('data-postid') || '';
|
||||
if (!cid) {
|
||||
var li = reply.closest('[data-comment-id], #comment-' + (cid || ''));
|
||||
if (li && li.getAttribute('data-comment-id')) cid = li.getAttribute('data-comment-id');
|
||||
}
|
||||
if (!cid) {
|
||||
var m = (reply.closest('li[id^="comment-"]') || {}).id || '';
|
||||
var mm = /comment-(\d+)/.exec(m);
|
||||
if (mm) cid = mm[1];
|
||||
}
|
||||
moveForm('div-comment-' + cid, cid, false);
|
||||
return;
|
||||
}
|
||||
if (edit) {
|
||||
e.preventDefault();
|
||||
var eid = edit.getAttribute('data-commentid') || edit.getAttribute('data-comment-id') || '';
|
||||
if (!eid) {
|
||||
var row = edit.closest('[data-comment-id]');
|
||||
if (row) eid = row.getAttribute('data-comment-id');
|
||||
}
|
||||
if (!eid) return;
|
||||
moveForm('div-comment-' + eid, eid, true);
|
||||
}
|
||||
});
|
||||
|
||||
// Dropup toggles (guest / expand).
|
||||
document.addEventListener('click', function (e) {
|
||||
var t = e.target && e.target.closest ? e.target.closest('[data-toggle-class="open"]') : null;
|
||||
if (!t) return;
|
||||
var targetSel = t.getAttribute('data-target');
|
||||
if (!targetSel) return;
|
||||
e.preventDefault();
|
||||
var wrap = t.closest('.dropup, .input-expand-wrap, #comment-user-info') || t.parentElement;
|
||||
var panel = targetSel.charAt(0) === '#' || targetSel.charAt(0) === '.'
|
||||
? (wrap && wrap.querySelector(targetSel)) || document.querySelector(targetSel)
|
||||
: null;
|
||||
if (wrap) wrap.classList.toggle('open');
|
||||
if (panel && panel !== wrap) panel.classList.toggle('open');
|
||||
});
|
||||
|
||||
// Close dropups on outside click.
|
||||
document.addEventListener('click', function (e) {
|
||||
if (e.target.closest && e.target.closest('.dropup, .input-expand-wrap, [data-toggle-class="open"]')) return;
|
||||
document.querySelectorAll('.dropup.open, .input-expand-wrap.open, #comment-user-info.open').forEach(function (n) {
|
||||
n.classList.remove('open');
|
||||
});
|
||||
});
|
||||
|
||||
// Scroll helper for @parent.
|
||||
document.addEventListener('click', function (e) {
|
||||
var a = e.target && e.target.closest ? e.target.closest('[data-scroll-to]') : null;
|
||||
if (!a) return;
|
||||
e.preventDefault();
|
||||
var sel = a.getAttribute('data-scroll-to');
|
||||
var el = sel ? document.querySelector(sel) : null;
|
||||
if (el) el.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
});
|
||||
|
||||
window.aksrtCommentMoveForm = moveForm;
|
||||
window.aksrtGetEditingCommentId = function () { return editingId; };
|
||||
})();
|
||||