fix(pay): render the copy button beside the anchor with real styling

The copy fields survived save and sanitization, but the frontend never showed
a working copy button: the remark button was emitted as a bare sibling inside
a column flex list, and .resource-download-box__btn--extract had no CSS rule at
all, so it rendered as an unstyled full-width block stacked under the download
anchor instead of the side-by-side row the reference theme uses.

Wrap the anchor and the copy button in .resource-download-box__row and give the
extract modifier real styles (dashed outline, muted surface, focus-color hover),
including the scoped mobile download bar variant.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-09-24 20:57:32 +08:00
co-authored by Claude Sonnet 4.6
parent 592bfb4fa2
commit bd3d50c6c6
3 changed files with 100 additions and 19 deletions
@@ -242,6 +242,48 @@
margin: 0;
}
/* 下载按钮 + 复制/备注按钮并排,对应参考主题的 .but-download.flex.ac */
.resource-download-box__row {
display: flex;
align-items: stretch;
gap: 8px;
}
.resource-download-box__row > .resource-download-box__btn {
flex: 1 1 auto;
min-width: 0;
}
/* 提取码 / 点击复制按钮 */
.resource-download-box__btn--extract {
flex: 0 0 auto;
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
min-height: 44px;
padding: 10px 14px;
border: 1px dashed var(--main-border-color, rgb(132 145 165 / 35%));
border-radius: 10px;
background: var(--muted-bg-color, rgb(132 145 165 / 8%));
color: var(--muted-color, #6b7280);
font-size: 13px;
font-weight: 600;
line-height: 1.3;
cursor: pointer;
transition: border-color 0.2s ease, background 0.2s ease, color 0.2s ease;
}
.resource-download-box__btn--extract:hover {
border-color: var(--focus-color, #f04494);
background: var(--focus-color-opacity1, rgb(240 68 148 / 10%));
color: var(--focus-color, #f04494);
}
.resource-download-box__btn--extract:active {
transform: scale(0.98);
}
/* 下载按钮 */
.resource-download-box__btn {
display: flex;
@@ -734,6 +776,33 @@
margin: 0;
}
/* ---- 下载按钮 + 复制/备注按钮并排 ---- */
.resource-mobile-download-bar .resource-download-box__row {
display: flex;
align-items: stretch;
gap: 8px;
}
.resource-mobile-download-bar .resource-download-box__row > .resource-download-box__btn {
flex: 1 1 auto;
min-width: 0;
}
/* ---- 提取码 / 点击复制按钮 ---- */
.resource-mobile-download-bar .resource-download-box__btn--extract {
flex: 0 0 auto;
min-height: 50px;
padding: 12px 14px;
border: 1px dashed var(--main-border-color, rgb(132 145 165 / 35%));
border-radius: 10px;
background: var(--muted-bg-color, rgb(132 145 165 / 8%));
color: var(--muted-color, #6b7280);
font-size: 13px;
font-weight: 600;
cursor: pointer;
-webkit-tap-highlight-color: transparent;
}
/* ---- 下载按钮(核心交互元素)---- */
.resource-mobile-download-bar .resource-download-box__btn {
display: flex;
+21 -19
View File
@@ -374,25 +374,27 @@ if ($is_need_purchase && $user_amount > 0) {
}
?>
<li class="resource-download-box__item">
<a
class="resource-download-box__btn btn-primary resource-download-box__btn--service-<?php echo esc_attr($service['key']); ?><?php echo esc_attr($btn_modifier); ?><?php echo esc_attr($btn_extra_class); ?>"
href="<?php echo esc_url($btn_href); ?>"<?php echo $btn_target; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?><?php echo $btn_rel; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
data-source-index="<?php echo esc_attr((string) $idx); ?>"
data-source-type="<?php echo esc_attr($type); ?>"
style="--service-color: <?php echo esc_attr($service['color']); ?>"
>
<?php echo $btn_icon; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<span><?php echo esc_html($btn_text); ?></span>
<span class="resource-download-box__service-badge" style="background: <?php echo esc_attr($service['color']); ?>">
<?php echo esc_html($service['name']); ?>
</span>
</a>
<?php if ($has_extract || $has_copy) : ?>
<button type="button" class="resource-download-box__btn resource-download-box__btn--extract"
data-clipboard-tag="<?php echo esc_attr($has_copy ? ($copy_key !== '' ? $copy_key : __('内容', 'aksrt')) : __('提取码', 'aksrt')); ?>"
data-clipboard-text="<?php echo esc_attr($has_copy ? $copy_val : $extract_code); ?>"
><?php echo esc_html($has_extract ? $extract_code : ($copy_key !== '' ? $copy_key : __('复制内容', 'aksrt'))); ?></button>
<?php endif; ?>
<div class="resource-download-box__row">
<a
class="resource-download-box__btn btn-primary resource-download-box__btn--service-<?php echo esc_attr($service['key']); ?><?php echo esc_attr($btn_modifier); ?><?php echo esc_attr($btn_extra_class); ?>"
href="<?php echo esc_url($btn_href); ?>"<?php echo $btn_target; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?><?php echo $btn_rel; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
data-source-index="<?php echo esc_attr((string) $idx); ?>"
data-source-type="<?php echo esc_attr($type); ?>"
style="--service-color: <?php echo esc_attr($service['color']); ?>"
>
<?php echo $btn_icon; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<span><?php echo esc_html($btn_text); ?></span>
<span class="resource-download-box__service-badge" style="background: <?php echo esc_attr($service['color']); ?>">
<?php echo esc_html($service['name']); ?>
</span>
</a>
<?php if ($has_extract || $has_copy) : ?>
<button type="button" class="resource-download-box__btn resource-download-box__btn--extract"
data-clipboard-tag="<?php echo esc_attr($has_copy ? ($copy_key !== '' ? $copy_key : __('内容', 'aksrt')) : __('提取码', 'aksrt')); ?>"
data-clipboard-text="<?php echo esc_attr($has_copy ? $copy_val : $extract_code); ?>"
><?php echo esc_html($has_extract ? $extract_code : ($copy_key !== '' ? $copy_key : __('复制内容', 'aksrt'))); ?></button>
<?php endif; ?>
</div>
</li>
<?php endforeach; ?>
</ul>
@@ -52,6 +52,16 @@ test('the download box renders direct anchors with inline extract codes', () =>
assert.match(box, /\$btn_text = \$label;/);
assert.match(box, /resource-download-box__btn--custom/);
// The copy button must sit beside the anchor (not stacked full-width below it)
// and must carry live clipboard attributes the delegated runtime can act on.
assert.match(box, /<div class="resource-download-box__row">/);
assert.match(box, /resource-download-box__btn--extract/);
const css = read('assets/css/frontend/resource-meta/part.css');
assert.match(css, /\.resource-download-box__row \{[\s\S]*?display:\s*flex;/);
assert.match(css, /\.resource-download-box__btn--extract \{/);
// Mobile download bar scopes its own copy-button rule.
assert.match(css, /\.resource-mobile-download-bar \.resource-download-box__btn--extract \{/);
// The login gate keeps its own anchor.
assert.match(box, /\$btn_href = \$login_url;/);
});