Merge pull request 'feat(shop): complete storefront archive, seller edit, revenue' (#68) from worktree-shop-frontend-complete into main
Reviewed-on: CYL/wordpress-theme-aksrt#68
This commit is contained in:
@@ -719,3 +719,16 @@
|
||||
.aksrt-product-edit__actions .btn-ghost:hover {
|
||||
border-color: var(--color-primary, var(--color-primary));
|
||||
}
|
||||
|
||||
.aksrt-product-archive__toolbar{display:flex;flex-wrap:wrap;gap:8px;margin:0 0 16px}
|
||||
.aksrt-product-archive__sort{display:inline-flex;padding:8px 14px;border-radius:999px;background:var(--surface-1,#fff);color:var(--color-ink-3,#5c6370);text-decoration:none;font-weight:600;font-size:14px;box-shadow:var(--shadow-sm)}
|
||||
.aksrt-product-archive__sort.is-active,.aksrt-product-archive__sort:hover{background:var(--color-primary,#f04494);color:#fff}
|
||||
.aksrt-product-archive__load-more{display:flex;justify-content:center;margin:20px 0}
|
||||
.aksrt-revenue-page__layout{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:16px;margin:20px 0}
|
||||
.aksrt-revenue-page__panel{padding:18px;border-radius:16px;background:var(--surface-1,#fff);box-shadow:var(--shadow-sm)}
|
||||
.aksrt-revenue-page__withdraw-form label{display:flex;flex-direction:column;gap:6px;margin-bottom:12px}
|
||||
.aksrt-revenue-page__withdraw-form input,.aksrt-revenue-page__withdraw-form select,.aksrt-revenue-page__withdraw-form textarea{width:100%;padding:10px 12px;border:1px solid var(--color-line-soft,rgba(0,0,0,.08));border-radius:10px}
|
||||
.aksrt-revenue-page__item{display:flex;justify-content:space-between;gap:12px;padding:12px 0;border-bottom:1px solid var(--color-line-soft,rgba(0,0,0,.06))}
|
||||
.aksrt-revenue-page__item .is-in{color:#27ae60;font-weight:700}
|
||||
.aksrt-revenue-page__item .is-out{color:#e74c3c;font-weight:700}
|
||||
.aksrt-product-editor__hint{font-size:13px;color:var(--color-ink-4,#8a909c);margin:0 0 10px}
|
||||
|
||||
@@ -280,16 +280,47 @@
|
||||
return;
|
||||
}
|
||||
|
||||
// Review — jump to product if possible
|
||||
// Review submit modal
|
||||
var reviewBtn = event.target.closest("[data-aksrt-order-review]");
|
||||
if (reviewBtn) {
|
||||
var reviewOrder = reviewBtn.getAttribute("data-aksrt-order-review");
|
||||
var card = reviewBtn.closest("[data-aksrt-order-card]");
|
||||
var productLink = card ? card.querySelector("a[href*='product']") : null;
|
||||
if (productLink && productLink.href) {
|
||||
window.location.href = productLink.href + (productLink.href.indexOf("#") >= 0 ? "" : "#reviews");
|
||||
} else {
|
||||
showToast("请到商品详情页评价");
|
||||
var productId = 0;
|
||||
if (card) {
|
||||
var firstItem = card.querySelector("[data-product-id]");
|
||||
if (firstItem) productId = Number(firstItem.getAttribute("data-product-id") || 0);
|
||||
}
|
||||
if (!productId) {
|
||||
// try parse from item title block dataset if present
|
||||
var any = card ? card.querySelector(".aksrt-orders-page__card-item") : null;
|
||||
if (any && any.getAttribute("data-product-id")) {
|
||||
productId = Number(any.getAttribute("data-product-id") || 0);
|
||||
}
|
||||
}
|
||||
if (!reviewOrder) return;
|
||||
var content = window.prompt("请输入评价内容(可选)", "好评!");
|
||||
if (content === null) return;
|
||||
var rating = window.prompt("商品评分 1-5", "5");
|
||||
var r = Math.max(1, Math.min(5, parseInt(rating || "5", 10) || 5));
|
||||
reviewBtn.disabled = true;
|
||||
request("/shop/frontend/orders/" + reviewOrder + "/review", {
|
||||
product_id: productId,
|
||||
content: content || "好评!",
|
||||
rating_product: r,
|
||||
rating_service: r,
|
||||
rating_shipping: r
|
||||
}).then(function (result) {
|
||||
reviewBtn.disabled = false;
|
||||
if (!result || !result.success) {
|
||||
showToast((result && result.message) || "评价失败");
|
||||
return;
|
||||
}
|
||||
showToast(result.message || "评价已提交");
|
||||
updateCardStatus(findOrderCard(reviewOrder), "completed", "已完成");
|
||||
}).catch(function () {
|
||||
reviewBtn.disabled = false;
|
||||
showToast("网络错误");
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+140
-32
@@ -1,44 +1,152 @@
|
||||
/**
|
||||
* AKSRT Product Archive — 商品归档页交互
|
||||
*
|
||||
* 处理商品分类/标签归档页的筛选、排序和加载更多。
|
||||
* Product archive — sort + AJAX load more.
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
"use strict";
|
||||
|
||||
var grid = document.getElementById('aksrt-product-grid');
|
||||
if (!grid) return;
|
||||
var root = document.querySelector("[data-aksrt-product-archive]");
|
||||
if (!root) return;
|
||||
|
||||
// 商品卡片懒加载图片观察器
|
||||
if ('IntersectionObserver' in window) {
|
||||
var imgObserver = new IntersectionObserver(function (entries) {
|
||||
entries.forEach(function (entry) {
|
||||
if (entry.isIntersecting) {
|
||||
var img = entry.target;
|
||||
if (img.dataset.src) {
|
||||
img.src = img.dataset.src;
|
||||
img.removeAttribute('data-src');
|
||||
}
|
||||
imgObserver.unobserve(img);
|
||||
}
|
||||
});
|
||||
}, { rootMargin: '200px' });
|
||||
function restBase() {
|
||||
var site = (window.aksrtData && aksrtData.siteUrl) ? String(aksrtData.siteUrl).replace(/\/$/, "") : "";
|
||||
return site + "/wp-json/aksrt/v1";
|
||||
}
|
||||
|
||||
grid.querySelectorAll('img[data-src]').forEach(function (img) {
|
||||
imgObserver.observe(img);
|
||||
});
|
||||
function toast(msg) {
|
||||
if (typeof window.aksrtToast === "function") {
|
||||
window.aksrtToast(msg);
|
||||
return;
|
||||
}
|
||||
if (window.console) console.log(msg);
|
||||
}
|
||||
|
||||
var grid = root.querySelector("[data-aksrt-product-grid], #aksrt-product-grid");
|
||||
var countEl = root.querySelector("[data-aksrt-archive-count]");
|
||||
var loadMoreBtn = root.querySelector("[data-aksrt-shop-load-more]");
|
||||
var state = {
|
||||
taxonomy: root.getAttribute("data-taxonomy") || "",
|
||||
termId: Number(root.getAttribute("data-term-id") || 0),
|
||||
orderby: root.getAttribute("data-orderby") || "date",
|
||||
page: Number(root.getAttribute("data-page") || 1),
|
||||
maxPages: Number(root.getAttribute("data-max-pages") || 1),
|
||||
paging: root.getAttribute("data-paging") || "number",
|
||||
ias: root.getAttribute("data-ias") === "1",
|
||||
iasMax: Number(root.getAttribute("data-ias-max") || 5),
|
||||
loading: false,
|
||||
autoLoads: 0,
|
||||
};
|
||||
|
||||
function fetchArchive(page, orderby, replace) {
|
||||
if (state.loading) return Promise.resolve();
|
||||
state.loading = true;
|
||||
if (loadMoreBtn) {
|
||||
loadMoreBtn.disabled = true;
|
||||
loadMoreBtn.textContent = "加载中...";
|
||||
}
|
||||
|
||||
// 商品卡片 hover 效果增强
|
||||
grid.querySelectorAll('.aksrt-product-card').forEach(function (card) {
|
||||
card.addEventListener('mouseenter', function () {
|
||||
this.classList.add('aksrt-product-card--hover');
|
||||
var qs = new URLSearchParams();
|
||||
qs.set("taxonomy", state.taxonomy);
|
||||
qs.set("term_id", String(state.termId));
|
||||
qs.set("page", String(page));
|
||||
qs.set("orderby", orderby || state.orderby);
|
||||
|
||||
return fetch(restBase() + "/shop/frontend/products/archive?" + qs.toString(), {
|
||||
method: "GET",
|
||||
credentials: "same-origin",
|
||||
headers: {
|
||||
"X-WP-Nonce": (window.aksrtData && aksrtData.restNonce) || "",
|
||||
},
|
||||
})
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (result) {
|
||||
state.loading = false;
|
||||
if (!result || !result.success || !result.data) {
|
||||
toast((result && result.message) || "加载失败");
|
||||
if (loadMoreBtn) {
|
||||
loadMoreBtn.disabled = false;
|
||||
loadMoreBtn.textContent = "加载更多";
|
||||
}
|
||||
return;
|
||||
}
|
||||
var data = result.data;
|
||||
state.page = Number(data.page || page);
|
||||
state.maxPages = Number(data.max_pages || 1);
|
||||
state.orderby = data.orderby || state.orderby;
|
||||
root.setAttribute("data-page", String(state.page));
|
||||
root.setAttribute("data-max-pages", String(state.maxPages));
|
||||
root.setAttribute("data-orderby", state.orderby);
|
||||
|
||||
if (countEl && typeof data.total !== "undefined") {
|
||||
countEl.textContent = "共 " + data.total + " 件商品";
|
||||
}
|
||||
|
||||
if (grid) {
|
||||
if (replace) {
|
||||
grid.innerHTML = data.html || "";
|
||||
} else if (data.html) {
|
||||
grid.insertAdjacentHTML("beforeend", data.html);
|
||||
}
|
||||
}
|
||||
|
||||
root.querySelectorAll("[data-aksrt-shop-orderby]").forEach(function (a) {
|
||||
a.classList.toggle("is-active", a.getAttribute("data-aksrt-shop-orderby") === state.orderby);
|
||||
});
|
||||
card.addEventListener('mouseleave', function () {
|
||||
this.classList.remove('aksrt-product-card--hover');
|
||||
});
|
||||
});
|
||||
|
||||
if (loadMoreBtn) {
|
||||
var hasMore = state.page < state.maxPages;
|
||||
loadMoreBtn.style.display = hasMore ? "" : "none";
|
||||
loadMoreBtn.disabled = false;
|
||||
loadMoreBtn.textContent = "加载更多";
|
||||
}
|
||||
})
|
||||
.catch(function () {
|
||||
state.loading = false;
|
||||
if (loadMoreBtn) {
|
||||
loadMoreBtn.disabled = false;
|
||||
loadMoreBtn.textContent = "加载更多";
|
||||
}
|
||||
toast("网络错误");
|
||||
});
|
||||
}
|
||||
|
||||
root.addEventListener("click", function (e) {
|
||||
var sort = e.target.closest("[data-aksrt-shop-orderby]");
|
||||
if (sort && root.contains(sort)) {
|
||||
e.preventDefault();
|
||||
var orderby = sort.getAttribute("data-aksrt-shop-orderby") || "date";
|
||||
if (orderby === state.orderby && state.paging === "ajax") return;
|
||||
state.orderby = orderby;
|
||||
state.autoLoads = 0;
|
||||
try {
|
||||
var u = new URL(window.location.href);
|
||||
u.searchParams.set("shop_orderby", orderby);
|
||||
u.searchParams.delete("paged");
|
||||
window.history.replaceState({}, "", u.toString());
|
||||
} catch (err) {}
|
||||
fetchArchive(1, orderby, true);
|
||||
return;
|
||||
}
|
||||
|
||||
var more = e.target.closest("[data-aksrt-shop-load-more]");
|
||||
if (more && root.contains(more)) {
|
||||
e.preventDefault();
|
||||
if (state.page >= state.maxPages) return;
|
||||
fetchArchive(state.page + 1, state.orderby, false);
|
||||
}
|
||||
});
|
||||
|
||||
if (state.paging === "ajax" && state.ias && "IntersectionObserver" in window && loadMoreBtn) {
|
||||
var io = new IntersectionObserver(function (entries) {
|
||||
entries.forEach(function (entry) {
|
||||
if (!entry.isIntersecting) return;
|
||||
if (state.loading || state.page >= state.maxPages) return;
|
||||
if (state.iasMax > 0 && state.autoLoads >= state.iasMax) return;
|
||||
state.autoLoads += 1;
|
||||
fetchArchive(state.page + 1, state.orderby, false);
|
||||
});
|
||||
}, { rootMargin: "240px" });
|
||||
io.observe(loadMoreBtn);
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -320,25 +320,43 @@
|
||||
if (!select) return [];
|
||||
return Array.prototype.slice
|
||||
.call(select.options)
|
||||
.filter(function (option) {
|
||||
return option.selected;
|
||||
})
|
||||
.map(function (option) {
|
||||
return Number(option.value || 0);
|
||||
})
|
||||
.filter(function (option) { return option.selected; })
|
||||
.map(function (option) { return Number(option.value || 0); })
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
function parseOptionsText(text) {
|
||||
var groups = [];
|
||||
String(text || "").split(String.fromCharCode(10)).forEach(function (line) {
|
||||
line = line.replace(/\r/g, "").trim();
|
||||
if (!line) return;
|
||||
var parts = line.split("|");
|
||||
if (parts.length < 2) return;
|
||||
var name = parts[0].trim();
|
||||
var values = [];
|
||||
parts.slice(1).join("|").split(",").forEach(function (chunk) {
|
||||
chunk = chunk.trim();
|
||||
if (!chunk) return;
|
||||
var segs = chunk.split(":");
|
||||
var label = (segs[0] || "").trim();
|
||||
if (!label) return;
|
||||
values.push({ label: label, price_change: Number(segs[1] || 0), image: "" });
|
||||
});
|
||||
if (name && values.length) {
|
||||
groups.push({ name: name, view_mode: "list", values: values });
|
||||
}
|
||||
});
|
||||
return groups;
|
||||
}
|
||||
|
||||
form.addEventListener("submit", function (event) {
|
||||
event.preventDefault();
|
||||
|
||||
var formData = new FormData(form);
|
||||
var productId = Number(editor.getAttribute("data-product-id") || 0);
|
||||
var gallery = String(formData.get("gallery") || "")
|
||||
.split(/\r?\n/)
|
||||
.map(function (line) {
|
||||
return line.trim();
|
||||
})
|
||||
.split(String.fromCharCode(10))
|
||||
.map(function (line) { return line.replace(/\r/g, "").trim(); })
|
||||
.filter(Boolean);
|
||||
|
||||
var payload = {
|
||||
@@ -351,9 +369,7 @@
|
||||
categories: selectedMultiValues(form.querySelector('select[name="categories[]"]')),
|
||||
tags: String(formData.get("tags") || "")
|
||||
.split(",")
|
||||
.map(function (tag) {
|
||||
return tag.trim();
|
||||
})
|
||||
.map(function (tag) { return tag.trim(); })
|
||||
.filter(Boolean),
|
||||
image: String(formData.get("image") || "").trim(),
|
||||
gallery: gallery,
|
||||
@@ -361,6 +377,14 @@
|
||||
shipping_fee: Number(formData.get("shipping_fee") || 0),
|
||||
after_sale_policy: String(formData.get("after_sale_policy") || ""),
|
||||
support_refund: formData.get("support_refund") === "1",
|
||||
options: parseOptionsText(formData.get("options_text") || ""),
|
||||
start_price: Number(formData.get("start_price") || 0),
|
||||
pay_modo: String(formData.get("pay_modo") || "0"),
|
||||
product_params: String(formData.get("product_params") || ""),
|
||||
product_video: String(formData.get("product_video") || "")
|
||||
.split(String.fromCharCode(10))
|
||||
.map(function (line) { return line.replace(/\r/g, "").trim(); })
|
||||
.filter(Boolean)
|
||||
};
|
||||
|
||||
var path = productId > 0 ? "/shop/frontend/products/" + productId : "/shop/frontend/products";
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* Creator revenue / withdraw form.
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
var page = document.querySelector("[data-aksrt-revenue-page]");
|
||||
if (!page || !window.aksrtData) return;
|
||||
|
||||
function restBase() {
|
||||
return ((aksrtData.siteUrl || "").replace(/\/$/, "")) + "/wp-json/aksrt/v1";
|
||||
}
|
||||
|
||||
function rest(path, method, body) {
|
||||
var opts = {
|
||||
method: method || "GET",
|
||||
credentials: "same-origin",
|
||||
headers: { "X-WP-Nonce": aksrtData.restNonce || "" },
|
||||
};
|
||||
if (body !== undefined) {
|
||||
opts.headers["Content-Type"] = "application/json";
|
||||
opts.body = JSON.stringify(body);
|
||||
}
|
||||
return fetch(restBase() + path, opts).then(function (r) {
|
||||
return r.json();
|
||||
});
|
||||
}
|
||||
|
||||
var form = page.querySelector("[data-aksrt-withdraw-form]");
|
||||
var msg = page.querySelector("[data-aksrt-withdraw-msg]");
|
||||
var balanceEl = page.querySelector("[data-aksrt-revenue-balance]");
|
||||
var list = page.querySelector("[data-aksrt-withdraw-list]");
|
||||
|
||||
if (!form) return;
|
||||
|
||||
form.addEventListener("submit", function (e) {
|
||||
e.preventDefault();
|
||||
var fd = new FormData(form);
|
||||
var amount = Number(fd.get("amount") || 0);
|
||||
var method = String(fd.get("method") || "alipay");
|
||||
var account = String(fd.get("account_info") || "").trim();
|
||||
var min = Number(page.getAttribute("data-min-amount") || 50);
|
||||
if (amount < min) {
|
||||
if (msg) {
|
||||
msg.hidden = false;
|
||||
msg.textContent = "最低提现金额为 " + min;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!account) {
|
||||
if (msg) {
|
||||
msg.hidden = false;
|
||||
msg.textContent = "请填写收款账户信息";
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var btn = form.querySelector('button[type="submit"]');
|
||||
if (btn) {
|
||||
btn.disabled = true;
|
||||
btn.textContent = "提交中...";
|
||||
}
|
||||
|
||||
rest("/shop/frontend/revenue/withdraw", "POST", {
|
||||
amount: amount,
|
||||
method: method,
|
||||
account_info: account,
|
||||
})
|
||||
.then(function (result) {
|
||||
if (btn) {
|
||||
btn.disabled = false;
|
||||
btn.textContent = "提交提现申请";
|
||||
}
|
||||
if (!result || !result.success) {
|
||||
if (msg) {
|
||||
msg.hidden = false;
|
||||
msg.textContent = (result && result.message) || "提现失败";
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (msg) {
|
||||
msg.hidden = false;
|
||||
msg.textContent = result.message || "提现申请已提交";
|
||||
}
|
||||
if (balanceEl && result.data && typeof result.data.balance !== "undefined") {
|
||||
balanceEl.textContent = "¥" + Number(result.data.balance).toFixed(2);
|
||||
}
|
||||
if (list && result.data) {
|
||||
var item = document.createElement("div");
|
||||
item.className = "aksrt-revenue-page__item";
|
||||
item.innerHTML =
|
||||
"<div><strong>¥" +
|
||||
Number(result.data.amount || amount).toFixed(2) +
|
||||
"</strong><span>" +
|
||||
method +
|
||||
"</span></div><div><span>pending</span><time>just now</time></div>";
|
||||
list.insertBefore(item, list.firstChild);
|
||||
}
|
||||
form.reset();
|
||||
})
|
||||
.catch(function () {
|
||||
if (btn) {
|
||||
btn.disabled = false;
|
||||
btn.textContent = "提交提现申请";
|
||||
}
|
||||
if (msg) {
|
||||
msg.hidden = false;
|
||||
msg.textContent = "网络错误";
|
||||
}
|
||||
});
|
||||
});
|
||||
})();
|
||||
@@ -354,6 +354,190 @@ trait AKSRT_Admin_Shop_Controller_Frontend_Flow_Operations {
|
||||
return $this->success(['message' => __('已设为默认地址', 'aksrt')]);
|
||||
}
|
||||
|
||||
public function get_frontend_product_archive(WP_REST_Request $request): WP_REST_Response {
|
||||
if (!class_exists('AKSRT_Shop')) {
|
||||
return $this->error('商城服务不可用', 500);
|
||||
}
|
||||
|
||||
$taxonomy = sanitize_key((string) ($request->get_param('taxonomy') ?? ''));
|
||||
$term_id = (int) ($request->get_param('term_id') ?? 0);
|
||||
$page = max(1, (int) ($request->get_param('page') ?? 1));
|
||||
$orderby = sanitize_key((string) ($request->get_param('orderby') ?? ''));
|
||||
|
||||
$view = AKSRT_Shop::get_instance()->get_product_archive_view([
|
||||
'taxonomy' => $taxonomy,
|
||||
'term_id' => $term_id,
|
||||
'paged' => $page,
|
||||
'orderby' => $orderby,
|
||||
]);
|
||||
|
||||
$html = '';
|
||||
if (!empty($view['products']) && is_array($view['products'])) {
|
||||
ob_start();
|
||||
foreach ($view['products'] as $product) {
|
||||
$this->render_product_card_html($product, $view);
|
||||
}
|
||||
$html = (string) ob_get_clean();
|
||||
}
|
||||
|
||||
return $this->success([
|
||||
'data' => array_merge($view, [
|
||||
'html' => $html,
|
||||
'has_more' => (int) ($view['page'] ?? 1) < (int) ($view['max_pages'] ?? 1),
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
private function render_product_card_html(array $product, array $view = []): void {
|
||||
$sales_cfg = is_array($view['sales'] ?? null) ? $view['sales'] : [];
|
||||
$mode = (string) ($sales_cfg['show'] ?? 'show');
|
||||
$min_sales = max(0, (int) ($sales_cfg['show_min'] ?? 0));
|
||||
$sales = (int) ($product['sales'] ?? 0);
|
||||
$can_show_sales = false;
|
||||
if ($mode === 'show') {
|
||||
$can_show_sales = $sales > 0;
|
||||
} elseif ($mode === 'threshold') {
|
||||
$can_show_sales = $sales >= $min_sales;
|
||||
}
|
||||
|
||||
$permalink = (string) ($product['permalink'] ?? '#');
|
||||
$title = (string) ($product['title'] ?? '');
|
||||
$image = (string) ($product['image'] ?? '');
|
||||
$price = (float) ($product['price'] ?? 0);
|
||||
$excerpt = (string) ($product['excerpt'] ?? '');
|
||||
?>
|
||||
<article class="aksrt-product-card">
|
||||
<a href="<?php echo esc_url($permalink); ?>" class="aksrt-product-card__link">
|
||||
<?php if ($image !== '') : ?>
|
||||
<div class="aksrt-product-card__image">
|
||||
<img src="<?php echo esc_url($image); ?>" alt="<?php echo esc_attr($title); ?>" loading="lazy">
|
||||
</div>
|
||||
<?php else : ?>
|
||||
<div class="aksrt-product-card__image aksrt-product-card__image--placeholder">
|
||||
<i class="fas fa-image"></i>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<div class="aksrt-product-card__body">
|
||||
<h3 class="aksrt-product-card__title"><?php echo esc_html($title); ?></h3>
|
||||
<?php if ($excerpt !== '') : ?>
|
||||
<p class="aksrt-product-card__excerpt"><?php echo esc_html(wp_trim_words($excerpt, 15)); ?></p>
|
||||
<?php endif; ?>
|
||||
<div class="aksrt-product-card__footer">
|
||||
<span class="aksrt-product-card__price">¥<?php echo esc_html(number_format($price, 2)); ?></span>
|
||||
<?php if ($can_show_sales) : ?>
|
||||
<span class="aksrt-product-card__sales"><?php printf(esc_html__('已售 %d', 'aksrt'), $sales); ?></span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</article>
|
||||
<?php
|
||||
}
|
||||
|
||||
public function get_frontend_revenue(WP_REST_Request $request): WP_REST_Response {
|
||||
if (!function_exists('aksrt_get_revenue_page_view')) {
|
||||
return $this->error('收益服务不可用', 500);
|
||||
}
|
||||
$view = aksrt_get_revenue_page_view(get_current_user_id());
|
||||
$withdrawals = [];
|
||||
if (class_exists('AKSRT_Withdrawal')) {
|
||||
$rows = AKSRT_Withdrawal::get_instance()->get_user_withdrawals(get_current_user_id(), ['limit' => 20]);
|
||||
foreach ($rows as $row) {
|
||||
$withdrawals[] = [
|
||||
'id' => (int) ($row['id'] ?? 0),
|
||||
'amount' => (float) ($row['amount'] ?? 0),
|
||||
'status' => (string) ($row['status'] ?? ''),
|
||||
'method' => (string) ($row['method'] ?? ''),
|
||||
'created_at' => (string) ($row['created_at'] ?? ''),
|
||||
'note' => (string) ($row['note'] ?? ''),
|
||||
];
|
||||
}
|
||||
}
|
||||
$settings = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_withdrawal_settings() : [];
|
||||
return $this->success([
|
||||
'data' => array_merge($view, [
|
||||
'withdrawals' => $withdrawals,
|
||||
'settings' => [
|
||||
'min_amount' => (float) ($settings['min_amount'] ?? 50),
|
||||
'service_charge_rate' => (float) ($settings['service_charge_rate'] ?? 0),
|
||||
'text' => (string) ($settings['text'] ?? ''),
|
||||
],
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
public function create_frontend_withdraw(WP_REST_Request $request): WP_REST_Response {
|
||||
if (!class_exists('AKSRT_Withdrawal') || !class_exists('AKSRT_Credits')) {
|
||||
return $this->error('提现服务不可用', 500);
|
||||
}
|
||||
|
||||
$params = $this->get_json_params($request);
|
||||
$amount = (float) ($params['amount'] ?? 0);
|
||||
$method = sanitize_key((string) ($params['method'] ?? 'alipay'));
|
||||
$account = sanitize_textarea_field((string) ($params['account_info'] ?? $params['account'] ?? ''));
|
||||
|
||||
$settings = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_withdrawal_settings() : [];
|
||||
$min = (float) ($settings['min_amount'] ?? 50);
|
||||
if ($amount < $min) {
|
||||
return $this->error(sprintf(__('最低提现金额为 %s', 'aksrt'), $min));
|
||||
}
|
||||
if ($account === '') {
|
||||
return $this->error(__('请填写收款账户信息', 'aksrt'));
|
||||
}
|
||||
|
||||
$user_id = get_current_user_id();
|
||||
$balance = (float) AKSRT_Credits::balance($user_id);
|
||||
if ($amount > $balance) {
|
||||
return $this->error(__('可提现余额不足', 'aksrt'));
|
||||
}
|
||||
|
||||
$cost = (int) round($amount);
|
||||
if ($cost <= 0) {
|
||||
return $this->error(__('提现金额无效', 'aksrt'));
|
||||
}
|
||||
$deducted = AKSRT_Credits::deduct($user_id, $cost, 'withdraw', ['type' => 'withdraw'], __('用户提现申请', 'aksrt'));
|
||||
if (is_wp_error($deducted)) {
|
||||
return $this->error($deducted->get_error_message());
|
||||
}
|
||||
|
||||
$id = AKSRT_Withdrawal::get_instance()->create($user_id, $amount, $method, $account);
|
||||
if ($id <= 0) {
|
||||
AKSRT_Credits::add($user_id, $cost, 'withdraw_refund', ['type' => 'withdraw'], __('提现失败退回', 'aksrt'));
|
||||
return $this->error(__('提现申请创建失败', 'aksrt'), 500);
|
||||
}
|
||||
|
||||
return $this->success([
|
||||
'message' => __('提现申请已提交', 'aksrt'),
|
||||
'data' => [
|
||||
'id' => $id,
|
||||
'amount' => $amount,
|
||||
'balance' => (float) AKSRT_Credits::balance($user_id),
|
||||
],
|
||||
], 201);
|
||||
}
|
||||
|
||||
public function submit_frontend_product_review(WP_REST_Request $request): WP_REST_Response {
|
||||
if (!class_exists('AKSRT_Product_Review')) {
|
||||
return $this->error('评价服务不可用', 500);
|
||||
}
|
||||
$params = $this->get_json_params($request);
|
||||
$order_number = sanitize_text_field((string) $request->get_param('order_number'));
|
||||
$product_id = (int) ($params['product_id'] ?? 0);
|
||||
$result = AKSRT_Product_Review::get_instance()->submit_review(
|
||||
get_current_user_id(),
|
||||
$order_number,
|
||||
$product_id,
|
||||
$params
|
||||
);
|
||||
if (empty($result['success'])) {
|
||||
return $this->error($result['message'] ?? __('评价失败', 'aksrt'));
|
||||
}
|
||||
return $this->success([
|
||||
'message' => __('评价已提交', 'aksrt'),
|
||||
'data' => $result,
|
||||
], 201);
|
||||
}
|
||||
|
||||
public function create_frontend_product(WP_REST_Request $request): WP_REST_Response {
|
||||
$params = $this->get_json_params($request);
|
||||
$params['author_id'] = get_current_user_id();
|
||||
|
||||
@@ -333,6 +333,10 @@ trait AKSRT_Admin_Shop_Controller_Product_Operations {
|
||||
update_post_meta($post_id, '_aksrt_product_image', $image);
|
||||
}
|
||||
|
||||
if (isset($params['options']) && is_array($params['options']) && class_exists('AKSRT_Shop')) {
|
||||
AKSRT_Shop::get_instance()->save_product_options((int) $post_id, $params['options']);
|
||||
}
|
||||
|
||||
return $this->success([
|
||||
'message' => __('商品已创建', 'aksrt'),
|
||||
'data' => ['id' => $post_id],
|
||||
@@ -484,6 +488,10 @@ trait AKSRT_Admin_Shop_Controller_Product_Operations {
|
||||
update_post_meta($id, '_aksrt_product_image', esc_url_raw((string) $params['image']));
|
||||
}
|
||||
|
||||
if (isset($params['options']) && is_array($params['options']) && class_exists('AKSRT_Shop')) {
|
||||
AKSRT_Shop::get_instance()->save_product_options($id, $params['options']);
|
||||
}
|
||||
|
||||
return $this->success([
|
||||
'message' => __('商品已更新', 'aksrt'),
|
||||
]);
|
||||
|
||||
@@ -352,7 +352,31 @@ class AKSRT_Admin_Shop_Controller extends AKSRT_Admin_Controller_Base {
|
||||
'permission_callback' => [$this, 'check_logged_in_permission'],
|
||||
]);
|
||||
|
||||
register_rest_route(self::API_NAMESPACE, '/shop/frontend/products/(?P<id>\d+)/favorite', [
|
||||
register_rest_route(self::API_NAMESPACE, '/shop/frontend/products/archive', [
|
||||
'methods' => 'GET',
|
||||
'callback' => [$this, 'get_frontend_product_archive'],
|
||||
'permission_callback' => '__return_true',
|
||||
]);
|
||||
|
||||
register_rest_route(self::API_NAMESPACE, '/shop/frontend/revenue', [
|
||||
'methods' => 'GET',
|
||||
'callback' => [$this, 'get_frontend_revenue'],
|
||||
'permission_callback' => [$this, 'check_logged_in_permission'],
|
||||
]);
|
||||
|
||||
register_rest_route(self::API_NAMESPACE, '/shop/frontend/revenue/withdraw', [
|
||||
'methods' => 'POST',
|
||||
'callback' => [$this, 'create_frontend_withdraw'],
|
||||
'permission_callback' => [$this, 'check_logged_in_permission'],
|
||||
]);
|
||||
|
||||
register_rest_route(self::API_NAMESPACE, '/shop/frontend/orders/(?P<order_number>[A-Za-z0-9]+)/review', [
|
||||
'methods' => 'POST',
|
||||
'callback' => [$this, 'submit_frontend_product_review'],
|
||||
'permission_callback' => [$this, 'check_logged_in_permission'],
|
||||
]);
|
||||
|
||||
register_rest_route(self::API_NAMESPACE, '/shop/frontend/products/(?P<id>\d+)/favorite', [
|
||||
'methods' => 'POST',
|
||||
'callback' => [$this, 'toggle_frontend_product_favorite'],
|
||||
'permission_callback' => [$this, 'check_logged_in_permission'],
|
||||
|
||||
@@ -1000,6 +1000,10 @@ class AKSRT_Settings {
|
||||
return AKSRT_Settings_Post_Display::get_comment_display_settings();
|
||||
}
|
||||
|
||||
public static function get_withdrawal_settings(): array {
|
||||
return AKSRT_Settings_Payment_Runtime::get_withdrawal_settings();
|
||||
}
|
||||
|
||||
/**
|
||||
* pre_get_posts: apply post_list_per_page on archive/tag/category/search/home pages.
|
||||
*
|
||||
|
||||
@@ -152,6 +152,11 @@ class AKSRT_Shop {
|
||||
'after_sale_policy' => '',
|
||||
'support_refund' => true,
|
||||
'status' => 'draft',
|
||||
'options' => [],
|
||||
'pay_modo' => '0',
|
||||
'start_price' => 0,
|
||||
'product_video' => [],
|
||||
'product_params' => '',
|
||||
],
|
||||
'taxonomy' => [
|
||||
'categories' => $this->product_service()->get_term_options('aksrt_product_cat'),
|
||||
@@ -181,6 +186,10 @@ class AKSRT_Shop {
|
||||
}
|
||||
|
||||
$view['is_edit'] = true;
|
||||
$videos = get_post_meta($product_id, '_aksrt_product_video', true);
|
||||
if (!is_array($videos)) {
|
||||
$videos = [];
|
||||
}
|
||||
$view['product'] = [
|
||||
'id' => $product_id,
|
||||
'title' => (string) $product->post_title,
|
||||
@@ -197,6 +206,11 @@ class AKSRT_Shop {
|
||||
'after_sale_policy' => (string) get_post_meta($product_id, '_aksrt_after_sale_policy', true),
|
||||
'support_refund' => get_post_meta($product_id, '_aksrt_support_refund', true) !== '0',
|
||||
'status' => (string) get_post_status($product_id),
|
||||
'options' => method_exists($this, 'get_product_options') ? $this->get_product_options($product_id) : [],
|
||||
'pay_modo' => sanitize_key((string) get_post_meta($product_id, '_aksrt_pay_modo', true)) ?: '0',
|
||||
'start_price' => (float) get_post_meta($product_id, '_aksrt_start_price', true),
|
||||
'product_video' => array_values(array_filter(array_map('esc_url_raw', $videos))),
|
||||
'product_params' => (string) get_post_meta($product_id, '_aksrt_product_params', true),
|
||||
];
|
||||
|
||||
return $view;
|
||||
|
||||
@@ -46,48 +46,111 @@ trait AKSRT_Shop_Part_1 {
|
||||
public function get_product_archive_view(array $args = []): array {
|
||||
$taxonomy = sanitize_key((string) ($args['taxonomy'] ?? ''));
|
||||
$term_id = (int) ($args['term_id'] ?? 0);
|
||||
$paged = max(1, (int) ($args['paged'] ?? 1));
|
||||
$per_page = 12;
|
||||
$paged = max(1, (int) ($args['paged'] ?? $args['page'] ?? 1));
|
||||
|
||||
if (!in_array($taxonomy, ['aksrt_product_cat', 'aksrt_product_tag'], true)) {
|
||||
return ['products' => [], 'term' => null, 'total' => 0, 'max_pages' => 0];
|
||||
$shop_settings = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_shop_settings() : [];
|
||||
$list = is_array($shop_settings['list'] ?? null) ? $shop_settings['list'] : [];
|
||||
$per_page = max(4, min(100, (int) ($args['per_page'] ?? ($list['count'] ?? 12))));
|
||||
$orderby = sanitize_key((string) ($args['orderby'] ?? ($list['orderby'] ?? 'date')));
|
||||
if (!in_array($orderby, ['date', 'sales', 'price_asc', 'price_desc', 'comments', 'modified'], true)) {
|
||||
$orderby = 'date';
|
||||
}
|
||||
$paging = sanitize_key((string) ($args['paging'] ?? ($list['paging'] ?? 'number')));
|
||||
if (!in_array($paging, ['number', 'ajax'], true)) {
|
||||
$paging = 'number';
|
||||
}
|
||||
$ias_s = array_key_exists('ias_s', $args) ? !empty($args['ias_s']) : !empty($list['ias_s']);
|
||||
$ias_max = max(1, min(50, (int) ($args['ias_max'] ?? ($list['ias_max'] ?? 5))));
|
||||
$style = sanitize_key((string) ($list['style'] ?? 'default'));
|
||||
|
||||
$empty = [
|
||||
'products' => [],
|
||||
'term' => null,
|
||||
'total' => 0,
|
||||
'max_pages' => 0,
|
||||
'page' => $paged,
|
||||
'orderby' => $orderby,
|
||||
'paging' => $paging,
|
||||
'ias_s' => $ias_s,
|
||||
'ias_max' => $ias_max,
|
||||
'per_page' => $per_page,
|
||||
'style' => $style,
|
||||
'sales' => is_array($shop_settings['sales'] ?? null) ? $shop_settings['sales'] : [],
|
||||
];
|
||||
|
||||
if (!in_array($taxonomy, ['aksrt_product_cat', 'aksrt_product_tag'], true) && empty($args['allow_all'])) {
|
||||
return $empty;
|
||||
}
|
||||
|
||||
$term = get_term($term_id, $taxonomy);
|
||||
if (is_wp_error($term) || !$term) {
|
||||
return ['products' => [], 'term' => null, 'total' => 0, 'max_pages' => 0];
|
||||
}
|
||||
|
||||
$query = new WP_Query([
|
||||
$query_args = [
|
||||
'post_type' => 'aksrt_product',
|
||||
'posts_per_page' => $per_page,
|
||||
'paged' => $paged,
|
||||
'post_status' => 'publish',
|
||||
'tax_query' => [[
|
||||
'order' => 'DESC',
|
||||
'orderby' => 'date',
|
||||
];
|
||||
|
||||
$term = null;
|
||||
if ($taxonomy !== '' && $term_id > 0) {
|
||||
$term = get_term($term_id, $taxonomy);
|
||||
if (is_wp_error($term) || !$term) {
|
||||
return $empty;
|
||||
}
|
||||
$query_args['tax_query'] = [[
|
||||
'taxonomy' => $taxonomy,
|
||||
'field' => 'term_id',
|
||||
'terms' => $term_id,
|
||||
]],
|
||||
]);
|
||||
]];
|
||||
}
|
||||
|
||||
if ($orderby === 'sales') {
|
||||
$query_args['meta_key'] = '_aksrt_sales';
|
||||
$query_args['orderby'] = 'meta_value_num';
|
||||
$query_args['order'] = 'DESC';
|
||||
} elseif ($orderby === 'price_asc') {
|
||||
$query_args['meta_key'] = '_aksrt_price';
|
||||
$query_args['orderby'] = 'meta_value_num';
|
||||
$query_args['order'] = 'ASC';
|
||||
} elseif ($orderby === 'price_desc') {
|
||||
$query_args['meta_key'] = '_aksrt_price';
|
||||
$query_args['orderby'] = 'meta_value_num';
|
||||
$query_args['order'] = 'DESC';
|
||||
} elseif ($orderby === 'comments') {
|
||||
$query_args['orderby'] = 'comment_count';
|
||||
$query_args['order'] = 'DESC';
|
||||
} elseif ($orderby === 'modified') {
|
||||
$query_args['orderby'] = 'modified';
|
||||
$query_args['order'] = 'DESC';
|
||||
}
|
||||
|
||||
$query = new WP_Query($query_args);
|
||||
$products = [];
|
||||
while ($query->have_posts()) {
|
||||
$query->the_post();
|
||||
$products[] = $this->build_product_card_view(get_the_ID());
|
||||
}
|
||||
|
||||
wp_reset_postdata();
|
||||
|
||||
return [
|
||||
'products' => $products,
|
||||
'term' => [
|
||||
'id' => $term->term_id,
|
||||
'name' => $term->name,
|
||||
'description' => $term->description,
|
||||
'count' => $term->count,
|
||||
],
|
||||
'term' => $term ? [
|
||||
'id' => (int) $term->term_id,
|
||||
'name' => (string) $term->name,
|
||||
'description' => (string) $term->description,
|
||||
'count' => (int) $term->count,
|
||||
'taxonomy' => $taxonomy,
|
||||
] : null,
|
||||
'total' => (int) $query->found_posts,
|
||||
'max_pages' => (int) $query->max_num_pages,
|
||||
'page' => $paged,
|
||||
'orderby' => $orderby,
|
||||
'paging' => $paging,
|
||||
'ias_s' => $ias_s,
|
||||
'ias_max' => $ias_max,
|
||||
'per_page' => $per_page,
|
||||
'style' => $style,
|
||||
'sales' => is_array($shop_settings['sales'] ?? null) ? $shop_settings['sales'] : [],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -82,12 +82,13 @@ function aksrt_get_revenue_page_view(int $user_id): array {
|
||||
if (class_exists('AKSRT_Revenue')) {
|
||||
$revenues = AKSRT_Revenue::get_instance()->get_user_revenues($user_id, ['limit' => 200]);
|
||||
foreach ($revenues as $rev) {
|
||||
$total_earned += (float) ($rev->amount ?? 0);
|
||||
$row = is_array($rev) ? $rev : (array) $rev;
|
||||
$total_earned += (float) ($row['amount'] ?? 0);
|
||||
$records[] = [
|
||||
'type' => 'earning',
|
||||
'title' => (string) ($rev->description ?? __('创作分成', 'aksrt')),
|
||||
'amount' => (float) ($rev->amount ?? 0),
|
||||
'created_at' => (string) ($rev->created_at ?? ''),
|
||||
'title' => (string) ($row['description'] ?? $row['type'] ?? __('创作分成', 'aksrt')),
|
||||
'amount' => (float) ($row['amount'] ?? 0),
|
||||
'created_at' => (string) ($row['created_at'] ?? ''),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -95,12 +96,14 @@ function aksrt_get_revenue_page_view(int $user_id): array {
|
||||
if (class_exists('AKSRT_Withdrawal')) {
|
||||
$withdrawals = AKSRT_Withdrawal::get_instance()->get_user_withdrawals($user_id, ['limit' => 200]);
|
||||
foreach ($withdrawals as $w) {
|
||||
$total_withdrawn += (float) ($w->amount ?? 0);
|
||||
$wrow = is_array($w) ? $w : (array) $w;
|
||||
$total_withdrawn += (float) ($wrow['amount'] ?? 0);
|
||||
$records[] = [
|
||||
'type' => 'withdrawal',
|
||||
'title' => __('提现', 'aksrt'),
|
||||
'amount' => (float) ($w->amount ?? 0),
|
||||
'created_at' => (string) ($w->created_at ?? ''),
|
||||
'amount' => (float) ($wrow['amount'] ?? 0),
|
||||
'created_at' => (string) ($wrow['created_at'] ?? ''),
|
||||
'status' => (string) ($wrow['status'] ?? ''),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ $tabs = [
|
||||
<div class="aksrt-orders-page__card-items">
|
||||
<?php if (!empty($items)) : ?>
|
||||
<?php foreach ($items as $item) : ?>
|
||||
<div class="aksrt-orders-page__card-item">
|
||||
<div class="aksrt-orders-page__card-item" data-product-id="<?php echo esc_attr((string) ($item['product_id'] ?? 0)); ?>">
|
||||
<?php if (!empty($item['image'])) : ?>
|
||||
<img src="<?php echo esc_url((string) $item['image']); ?>" alt="" class="aksrt-orders-page__card-img">
|
||||
<?php endif; ?>
|
||||
@@ -109,7 +109,7 @@ $tabs = [
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php else : ?>
|
||||
<div class="aksrt-orders-page__card-item">
|
||||
<div class="aksrt-orders-page__card-item" data-product-id="<?php echo esc_attr((string) ($item['product_id'] ?? 0)); ?>">
|
||||
<span class="aksrt-orders-page__card-item-title"><?php echo esc_html((string) ($order['product_name'] ?? __('商品订单', 'aksrt'))); ?></span>
|
||||
<strong>¥<?php echo esc_html(number_format((float) ($order['amount'] ?? 0), 2)); ?></strong>
|
||||
</div>
|
||||
|
||||
@@ -91,7 +91,49 @@ $product = (array) ($edit_view['product'] ?? []);
|
||||
</label>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="aksrt-product-editor__panel">
|
||||
<h2><?php esc_html_e('规格选项', 'aksrt'); ?></h2>
|
||||
<p class="aksrt-product-editor__hint"><?php esc_html_e('每行一组:规格名|选项1:加价,选项2:加价。例如:颜色|红:0,蓝:10', 'aksrt'); ?></p>
|
||||
<label class="aksrt-product-editor__field">
|
||||
<span><?php esc_html_e('规格配置', 'aksrt'); ?></span>
|
||||
<textarea name="options_text" class="aksrt-product-editor__textarea" rows="6" data-aksrt-options-text placeholder="颜色|红:0,蓝:10 尺码|M:0,L:5"><?php
|
||||
$opt_lines = [];
|
||||
foreach ((array) ($product['options'] ?? []) as $group) {
|
||||
$vals = [];
|
||||
foreach ((array) ($group['values'] ?? []) as $v) {
|
||||
$vals[] = (string) ($v['label'] ?? '') . ':' . (string) ($v['price_change'] ?? 0);
|
||||
}
|
||||
if (!empty($group['name']) && $vals) {
|
||||
$opt_lines[] = (string) $group['name'] . '|' . implode(',', $vals);
|
||||
}
|
||||
}
|
||||
echo esc_textarea(implode("\n", $opt_lines));
|
||||
?></textarea>
|
||||
</label>
|
||||
<div class="aksrt-product-editor__columns">
|
||||
<label class="aksrt-product-editor__field">
|
||||
<span><?php esc_html_e('起售价', 'aksrt'); ?></span>
|
||||
<input type="number" name="start_price" class="aksrt-product-editor__input" min="0" step="0.01" value="<?php echo esc_attr((string) ($product['start_price'] ?? 0)); ?>">
|
||||
</label>
|
||||
<label class="aksrt-product-editor__field">
|
||||
<span><?php esc_html_e('支付模式', 'aksrt'); ?></span>
|
||||
<select name="pay_modo" class="aksrt-product-editor__input">
|
||||
<option value="0" <?php selected((string) ($product['pay_modo'] ?? '0'), '0'); ?>><?php esc_html_e('现金', 'aksrt'); ?></option>
|
||||
<option value="points" <?php selected((string) ($product['pay_modo'] ?? '0'), 'points'); ?>><?php esc_html_e('积分', 'aksrt'); ?></option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
<label class="aksrt-product-editor__field">
|
||||
<span><?php esc_html_e('商品参数(每行 名称:值)', 'aksrt'); ?></span>
|
||||
<textarea name="product_params" class="aksrt-product-editor__textarea" rows="4"><?php echo esc_textarea((string) ($product['product_params'] ?? '')); ?></textarea>
|
||||
</label>
|
||||
<label class="aksrt-product-editor__field">
|
||||
<span><?php esc_html_e('视频地址(每行一个 URL)', 'aksrt'); ?></span>
|
||||
<textarea name="product_video" class="aksrt-product-editor__textarea" rows="3"><?php echo esc_textarea(implode("\n", (array) ($product['product_video'] ?? []))); ?></textarea>
|
||||
</label>
|
||||
</div>
|
||||
<div class="aksrt-product-editor__panel">
|
||||
<h2><?php esc_html_e('发货与售后', 'aksrt'); ?></h2>
|
||||
<div class="aksrt-product-editor__columns">
|
||||
<label class="aksrt-product-editor__field">
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Template Name: AKSRT - Revenue
|
||||
* Template Post Type: page
|
||||
*
|
||||
* 创作者收益中心页面 — 显示收益概览、分成记录和提现入口
|
||||
* Creator revenue center: overview, ledger, withdraw request.
|
||||
*
|
||||
* @package AKSRT
|
||||
*/
|
||||
@@ -13,115 +13,160 @@ defined('ABSPATH') || exit;
|
||||
get_header();
|
||||
|
||||
if (!is_user_logged_in()) {
|
||||
$login_url = function_exists('aksrt_get_login_url') ? aksrt_get_login_url() : wp_login_url(get_permalink());
|
||||
wp_safe_redirect($login_url);
|
||||
exit;
|
||||
$login_url = function_exists('aksrt_get_login_url') ? aksrt_get_login_url() : wp_login_url(get_permalink());
|
||||
wp_safe_redirect($login_url);
|
||||
exit;
|
||||
}
|
||||
|
||||
$user_id = get_current_user_id();
|
||||
|
||||
// 通过 helper 获取收益视图模型
|
||||
$revenue_view = function_exists('aksrt_get_revenue_page_view')
|
||||
? aksrt_get_revenue_page_view($user_id)
|
||||
: ['balance' => 0, 'total_earned' => 0, 'total_withdrawn' => 0, 'records' => []];
|
||||
? aksrt_get_revenue_page_view($user_id)
|
||||
: ['balance' => 0, 'total_earned' => 0, 'total_withdrawn' => 0, 'records' => []];
|
||||
|
||||
$balance = (float) ($revenue_view['balance'] ?? 0);
|
||||
$total_earned = (float) ($revenue_view['total_earned'] ?? 0);
|
||||
$balance = (float) ($revenue_view['balance'] ?? 0);
|
||||
$total_earned = (float) ($revenue_view['total_earned'] ?? 0);
|
||||
$total_withdrawn = (float) ($revenue_view['total_withdrawn'] ?? 0);
|
||||
$records = $revenue_view['records'] ?? [];
|
||||
$records = $revenue_view['records'] ?? [];
|
||||
|
||||
$per_page = 20;
|
||||
$paged = max(1, (int) ($_GET['rev_page'] ?? 1));
|
||||
$settings = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_withdrawal_settings() : [];
|
||||
$min_amount = (float) ($settings['min_amount'] ?? 50);
|
||||
$fee_rate = (float) ($settings['service_charge_rate'] ?? 0);
|
||||
$withdraw_text = (string) ($settings['text'] ?? '');
|
||||
|
||||
$withdrawals = [];
|
||||
if (class_exists('AKSRT_Withdrawal')) {
|
||||
$withdrawals = AKSRT_Withdrawal::get_instance()->get_user_withdrawals($user_id, ['limit' => 20]);
|
||||
}
|
||||
|
||||
$per_page = 20;
|
||||
$paged = max(1, (int) ($_GET['rev_page'] ?? 1));
|
||||
$total_records = count($records);
|
||||
$total_pages = max(1, (int) ceil($total_records / $per_page));
|
||||
$total_pages = max(1, (int) ceil(max(1, $total_records) / $per_page));
|
||||
if ($total_records === 0) {
|
||||
$total_pages = 1;
|
||||
}
|
||||
$paged_records = array_slice($records, ($paged - 1) * $per_page, $per_page);
|
||||
?>
|
||||
|
||||
<div class="container aksrt-revenue-page">
|
||||
<div class="aksrt-revenue-page__header">
|
||||
<h2><?php esc_html_e('创作收益', 'aksrt'); ?></h2>
|
||||
</div>
|
||||
<main id="main-content" class="site-main content-shell" tabindex="-1">
|
||||
<?php if (function_exists('aksrt_breadcrumbs')) { aksrt_breadcrumbs(); } ?>
|
||||
|
||||
<!-- 收益概览 -->
|
||||
<div class="aksrt-revenue-page__stats">
|
||||
<div class="aksrt-revenue-page__stat-card">
|
||||
<div class="aksrt-revenue-page__stat-icon"><i class="fas fa-wallet"></i></div>
|
||||
<div class="aksrt-revenue-page__stat-info">
|
||||
<span class="aksrt-revenue-page__stat-value">¥<?php echo esc_html(number_format($balance, 2)); ?></span>
|
||||
<span class="aksrt-revenue-page__stat-label"><?php esc_html_e('可提现余额', 'aksrt'); ?></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="aksrt-revenue-page__stat-card">
|
||||
<div class="aksrt-revenue-page__stat-icon"><i class="fas fa-chart-line"></i></div>
|
||||
<div class="aksrt-revenue-page__stat-info">
|
||||
<span class="aksrt-revenue-page__stat-value">¥<?php echo esc_html(number_format($total_earned, 2)); ?></span>
|
||||
<span class="aksrt-revenue-page__stat-label"><?php esc_html_e('累计收益', 'aksrt'); ?></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="aksrt-revenue-page__stat-card">
|
||||
<div class="aksrt-revenue-page__stat-icon"><i class="fas fa-money-check-alt"></i></div>
|
||||
<div class="aksrt-revenue-page__stat-info">
|
||||
<span class="aksrt-revenue-page__stat-value">¥<?php echo esc_html(number_format($total_withdrawn, 2)); ?></span>
|
||||
<span class="aksrt-revenue-page__stat-label"><?php esc_html_e('已提现', 'aksrt'); ?></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container aksrt-revenue-page" data-aksrt-revenue-page
|
||||
data-min-amount="<?php echo esc_attr((string) $min_amount); ?>"
|
||||
data-fee-rate="<?php echo esc_attr((string) $fee_rate); ?>">
|
||||
<div class="aksrt-revenue-page__header">
|
||||
<h1><?php esc_html_e('创作收益', 'aksrt'); ?></h1>
|
||||
<p class="aksrt-revenue-page__subtitle"><?php esc_html_e('查看分成明细并发起提现。', 'aksrt'); ?></p>
|
||||
</div>
|
||||
|
||||
<!-- 提现按钮 -->
|
||||
<?php if ($balance > 0) : ?>
|
||||
<div class="aksrt-revenue-page__actions">
|
||||
<a href="<?php echo esc_url(add_query_arg(['action' => 'withdraw'])); ?>" class="aksrt-revenue-page__withdraw-btn">
|
||||
<i class="fas fa-hand-holding-usd"></i> <?php esc_html_e('申请提现', 'aksrt'); ?>
|
||||
</a>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<div class="aksrt-revenue-page__stats">
|
||||
<div class="aksrt-revenue-page__stat-card">
|
||||
<div class="aksrt-revenue-page__stat-icon"><i class="fas fa-wallet"></i></div>
|
||||
<div class="aksrt-revenue-page__stat-info">
|
||||
<span class="aksrt-revenue-page__stat-value" data-aksrt-revenue-balance>¥<?php echo esc_html(number_format($balance, 2)); ?></span>
|
||||
<span class="aksrt-revenue-page__stat-label"><?php esc_html_e('可提现余额', 'aksrt'); ?></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="aksrt-revenue-page__stat-card">
|
||||
<div class="aksrt-revenue-page__stat-icon"><i class="fas fa-chart-line"></i></div>
|
||||
<div class="aksrt-revenue-page__stat-info">
|
||||
<span class="aksrt-revenue-page__stat-value">¥<?php echo esc_html(number_format($total_earned, 2)); ?></span>
|
||||
<span class="aksrt-revenue-page__stat-label"><?php esc_html_e('累计收益', 'aksrt'); ?></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="aksrt-revenue-page__stat-card">
|
||||
<div class="aksrt-revenue-page__stat-icon"><i class="fas fa-money-check-alt"></i></div>
|
||||
<div class="aksrt-revenue-page__stat-info">
|
||||
<span class="aksrt-revenue-page__stat-value">¥<?php echo esc_html(number_format($total_withdrawn, 2)); ?></span>
|
||||
<span class="aksrt-revenue-page__stat-label"><?php esc_html_e('已提现', 'aksrt'); ?></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 分成记录 -->
|
||||
<div class="aksrt-revenue-page__section">
|
||||
<h3><?php esc_html_e('收益明细', 'aksrt'); ?></h3>
|
||||
<div class="aksrt-revenue-page__layout">
|
||||
<section class="aksrt-revenue-page__panel">
|
||||
<h2><?php esc_html_e('申请提现', 'aksrt'); ?></h2>
|
||||
<?php if ($withdraw_text !== '') : ?>
|
||||
<div class="aksrt-revenue-page__note"><?php echo wp_kses_post(wpautop($withdraw_text)); ?></div>
|
||||
<?php endif; ?>
|
||||
<form class="aksrt-revenue-page__withdraw-form" data-aksrt-withdraw-form>
|
||||
<label>
|
||||
<span><?php esc_html_e('提现金额', 'aksrt'); ?></span>
|
||||
<input type="number" name="amount" min="<?php echo esc_attr((string) $min_amount); ?>" step="0.01" required>
|
||||
</label>
|
||||
<label>
|
||||
<span><?php esc_html_e('提现方式', 'aksrt'); ?></span>
|
||||
<select name="method">
|
||||
<option value="alipay"><?php esc_html_e('支付宝', 'aksrt'); ?></option>
|
||||
<option value="wechat"><?php esc_html_e('微信', 'aksrt'); ?></option>
|
||||
<option value="bank"><?php esc_html_e('银行卡', 'aksrt'); ?></option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
<span><?php esc_html_e('收款账户信息', 'aksrt'); ?></span>
|
||||
<textarea name="account_info" rows="3" required></textarea>
|
||||
</label>
|
||||
<p class="aksrt-revenue-page__fee-hint"><?php printf(esc_html__('手续费 %s%%', 'aksrt'), esc_html((string) $fee_rate)); ?></p>
|
||||
<button type="submit" class="btn-primary"><?php esc_html_e('提交提现申请', 'aksrt'); ?></button>
|
||||
<p class="aksrt-revenue-page__form-msg" data-aksrt-withdraw-msg hidden></p>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<?php if (!empty($paged_records)) : ?>
|
||||
<div class="aksrt-revenue-page__list">
|
||||
<?php foreach ($paged_records as $record) :
|
||||
$r_type = (string) ($record['type'] ?? 'earning');
|
||||
?>
|
||||
<div class="aksrt-revenue-page__item">
|
||||
<div class="aksrt-revenue-page__item-icon">
|
||||
<i class="<?php echo $r_type === 'withdrawal' ? 'fas fa-arrow-up' : 'fas fa-arrow-down'; ?>"
|
||||
style="color: <?php echo $r_type === 'withdrawal' ? '#f5222d' : '#52c41a'; ?>;"></i>
|
||||
</div>
|
||||
<div class="aksrt-revenue-page__item-info">
|
||||
<span class="aksrt-revenue-page__item-title"><?php echo esc_html($record['title'] ?? ''); ?></span>
|
||||
<span class="aksrt-revenue-page__item-time"><?php echo esc_html($record['created_at'] ?? ''); ?></span>
|
||||
</div>
|
||||
<div class="aksrt-revenue-page__item-amount" style="color: <?php echo $r_type === 'withdrawal' ? '#f5222d' : '#52c41a'; ?>;">
|
||||
<?php echo $r_type === 'withdrawal' ? '-' : '+'; ?>¥<?php echo esc_html(number_format((float) ($record['amount'] ?? 0), 2)); ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<section class="aksrt-revenue-page__panel">
|
||||
<h2><?php esc_html_e('提现记录', 'aksrt'); ?></h2>
|
||||
<?php if (!empty($withdrawals)) : ?>
|
||||
<div class="aksrt-revenue-page__list" data-aksrt-withdraw-list>
|
||||
<?php foreach ($withdrawals as $w) : ?>
|
||||
<div class="aksrt-revenue-page__item">
|
||||
<div>
|
||||
<strong>¥<?php echo esc_html(number_format((float) ($w['amount'] ?? 0), 2)); ?></strong>
|
||||
<span><?php echo esc_html((string) ($w['method'] ?? '')); ?></span>
|
||||
</div>
|
||||
<div>
|
||||
<span><?php echo esc_html((string) ($w['status'] ?? '')); ?></span>
|
||||
<time><?php echo esc_html((string) ($w['created_at'] ?? '')); ?></time>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php else : ?>
|
||||
<p class="aksrt-revenue-page__empty"><?php esc_html_e('暂无提现记录', 'aksrt'); ?></p>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<?php if ($total_pages > 1) : ?>
|
||||
<div class="aksrt-revenue-page__pagination">
|
||||
<?php for ($i = 1; $i <= $total_pages; $i++) :
|
||||
$page_url = add_query_arg(['rev_page' => $i]);
|
||||
?>
|
||||
<?php if ($i === $paged) : ?>
|
||||
<span class="aksrt-revenue-page__page-chip is-active"><?php echo esc_html((string) $i); ?></span>
|
||||
<?php else : ?>
|
||||
<a href="<?php echo esc_url($page_url); ?>" class="aksrt-revenue-page__page-chip"><?php echo esc_html((string) $i); ?></a>
|
||||
<?php endif; ?>
|
||||
<?php endfor; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php else : ?>
|
||||
<div class="aksrt-revenue-page__empty">
|
||||
<i class="fas fa-chart-pie"></i>
|
||||
<p><?php esc_html_e('暂无收益记录', 'aksrt'); ?></p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<section class="aksrt-revenue-page__section">
|
||||
<h2><?php esc_html_e('收益明细', 'aksrt'); ?></h2>
|
||||
<?php if (!empty($paged_records)) : ?>
|
||||
<div class="aksrt-revenue-page__list">
|
||||
<?php foreach ($paged_records as $rec) : ?>
|
||||
<div class="aksrt-revenue-page__item">
|
||||
<div>
|
||||
<strong><?php echo esc_html((string) ($rec['title'] ?? '')); ?></strong>
|
||||
<time><?php echo esc_html((string) ($rec['created_at'] ?? '')); ?></time>
|
||||
</div>
|
||||
<span class="<?php echo (($rec['type'] ?? '') === 'withdrawal') ? 'is-out' : 'is-in'; ?>">
|
||||
<?php echo (($rec['type'] ?? '') === 'withdrawal') ? '-' : '+'; ?>¥<?php echo esc_html(number_format((float) ($rec['amount'] ?? 0), 2)); ?>
|
||||
</span>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php if ($total_pages > 1 && $total_records > 0) : ?>
|
||||
<nav class="aksrt-revenue-page__pagination">
|
||||
<?php for ($i = 1; $i <= $total_pages; $i++) :
|
||||
$url = add_query_arg(['rev_page' => $i]);
|
||||
?>
|
||||
<a class="<?php echo $i === $paged ? 'is-active' : ''; ?>" href="<?php echo esc_url($url); ?>"><?php echo (int) $i; ?></a>
|
||||
<?php endfor; ?>
|
||||
</nav>
|
||||
<?php endif; ?>
|
||||
<?php else : ?>
|
||||
<p class="aksrt-revenue-page__empty"><?php esc_html_e('暂无收益明细', 'aksrt'); ?></p>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php get_footer(); ?>
|
||||
<?php
|
||||
wp_enqueue_script('aksrt-revenue', get_template_directory_uri() . '/assets/js/revenue.js', ['aksrt-script'], AKSRT_VERSION, true);
|
||||
get_footer();
|
||||
|
||||
@@ -11,56 +11,98 @@ get_header();
|
||||
|
||||
$term = get_queried_object();
|
||||
$paged = max(1, (int) get_query_var('paged'));
|
||||
$orderby = isset($_GET['shop_orderby']) ? sanitize_key(wp_unslash((string) $_GET['shop_orderby'])) : '';
|
||||
$shop = class_exists('AKSRT_Shop') ? AKSRT_Shop::get_instance() : null;
|
||||
$view = $shop ? $shop->get_product_archive_view([
|
||||
'taxonomy' => 'aksrt_product_cat',
|
||||
'term_id' => $term->term_id,
|
||||
'term_id' => $term ? (int) $term->term_id : 0,
|
||||
'paged' => $paged,
|
||||
]) : ['products' => [], 'term' => null, 'total' => 0, 'max_pages' => 0];
|
||||
'orderby' => $orderby,
|
||||
]) : ['products' => [], 'term' => null, 'total' => 0, 'max_pages' => 0, 'orderby' => 'date', 'paging' => 'number'];
|
||||
|
||||
$products = $view['products'];
|
||||
$total = $view['total'];
|
||||
$max_pages = $view['max_pages'];
|
||||
$products = $view['products'] ?? [];
|
||||
$total = (int) ($view['total'] ?? 0);
|
||||
$max_pages = (int) ($view['max_pages'] ?? 0);
|
||||
$term_name = $term->name ?? '';
|
||||
$term_desc = $term->description ?? '';
|
||||
$active_orderby = (string) ($view['orderby'] ?? 'date');
|
||||
$paging = (string) ($view['paging'] ?? 'number');
|
||||
$ias_s = !empty($view['ias_s']);
|
||||
$ias_max = (int) ($view['ias_max'] ?? 5);
|
||||
$style = (string) ($view['style'] ?? 'default');
|
||||
$sales_cfg = is_array($view['sales'] ?? null) ? $view['sales'] : [];
|
||||
|
||||
$order_options = [
|
||||
'date' => __('最新', 'aksrt'),
|
||||
'sales' => __('销量', 'aksrt'),
|
||||
'price_asc' => __('价格升序', 'aksrt'),
|
||||
'price_desc' => __('价格降序', 'aksrt'),
|
||||
'comments' => __('评价', 'aksrt'),
|
||||
];
|
||||
?>
|
||||
|
||||
<main id="main-content" class="site-main content-shell" tabindex="-1">
|
||||
<?php if (function_exists('aksrt_breadcrumbs')) aksrt_breadcrumbs(); ?>
|
||||
<?php if (function_exists('aksrt_breadcrumbs')) { aksrt_breadcrumbs(); } ?>
|
||||
|
||||
<div class="aksrt-product-archive container"
|
||||
data-aksrt-product-archive
|
||||
data-taxonomy="aksrt_product_cat"
|
||||
data-term-id="<?php echo esc_attr((string) ($term->term_id ?? 0)); ?>"
|
||||
data-orderby="<?php echo esc_attr($active_orderby); ?>"
|
||||
data-page="<?php echo esc_attr((string) $paged); ?>"
|
||||
data-max-pages="<?php echo esc_attr((string) $max_pages); ?>"
|
||||
data-paging="<?php echo esc_attr($paging); ?>"
|
||||
data-ias="<?php echo $ias_s ? '1' : '0'; ?>"
|
||||
data-ias-max="<?php echo esc_attr((string) $ias_max); ?>"
|
||||
data-style="<?php echo esc_attr($style); ?>">
|
||||
|
||||
<div class="aksrt-product-archive container">
|
||||
<header class="aksrt-product-archive__header">
|
||||
<div class="aksrt-product-archive__eyebrow"><?php esc_html_e('商城商品', 'aksrt'); ?></div>
|
||||
<h1 class="aksrt-product-archive__title"><?php echo esc_html($term_name); ?></h1>
|
||||
<?php if ($term_desc) : ?>
|
||||
<p class="aksrt-product-archive__desc"><?php echo esc_html($term_desc); ?></p>
|
||||
<?php endif; ?>
|
||||
<p class="aksrt-product-archive__count"><?php printf(esc_html__('共 %d 件商品', 'aksrt'), $total); ?></p>
|
||||
<p class="aksrt-product-archive__count" data-aksrt-archive-count><?php printf(esc_html__('共 %d 件商品', 'aksrt'), $total); ?></p>
|
||||
</header>
|
||||
|
||||
<nav class="aksrt-product-archive__toolbar" aria-label="<?php esc_attr_e('商品排序', 'aksrt'); ?>">
|
||||
<?php foreach ($order_options as $key => $label) :
|
||||
$url = add_query_arg(['shop_orderby' => $key]);
|
||||
?>
|
||||
<a class="aksrt-product-archive__sort<?php echo $active_orderby === $key ? ' is-active' : ''; ?>"
|
||||
href="<?php echo esc_url($url); ?>"
|
||||
data-aksrt-shop-orderby="<?php echo esc_attr($key); ?>">
|
||||
<?php echo esc_html($label); ?>
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
</nav>
|
||||
|
||||
<?php if (!empty($products)) : ?>
|
||||
<div class="aksrt-product-archive__grid" id="aksrt-product-grid">
|
||||
<?php foreach ($products as $product) : ?>
|
||||
<div class="aksrt-product-archive__grid aksrt-product-archive__grid--<?php echo esc_attr($style); ?>" id="aksrt-product-grid" data-aksrt-product-grid>
|
||||
<?php foreach ($products as $product) :
|
||||
$sales = (int) ($product['sales'] ?? 0);
|
||||
$mode = (string) ($sales_cfg['show'] ?? 'show');
|
||||
$min_sales = max(0, (int) ($sales_cfg['show_min'] ?? 0));
|
||||
$show_sales = ($mode === 'show' && $sales > 0) || ($mode === 'threshold' && $sales >= $min_sales);
|
||||
?>
|
||||
<article class="aksrt-product-card">
|
||||
<a href="<?php echo esc_url($product['permalink']); ?>" class="aksrt-product-card__link">
|
||||
<?php if ($product['image']) : ?>
|
||||
<a href="<?php echo esc_url((string) ($product['permalink'] ?? '#')); ?>" class="aksrt-product-card__link">
|
||||
<?php if (!empty($product['image'])) : ?>
|
||||
<div class="aksrt-product-card__image">
|
||||
<img src="<?php echo esc_url($product['image']); ?>" alt="<?php echo esc_attr($product['title']); ?>"<?php echo function_exists('aksrt_lazy_loading') ? aksrt_lazy_loading('other') : ' loading="lazy"'; ?>>
|
||||
<img src="<?php echo esc_url((string) $product['image']); ?>" alt="<?php echo esc_attr((string) ($product['title'] ?? '')); ?>" loading="lazy">
|
||||
</div>
|
||||
<?php else : ?>
|
||||
<div class="aksrt-product-card__image aksrt-product-card__image--placeholder">
|
||||
<i class="fas fa-image"></i>
|
||||
</div>
|
||||
<div class="aksrt-product-card__image aksrt-product-card__image--placeholder"><i class="fas fa-image"></i></div>
|
||||
<?php endif; ?>
|
||||
<div class="aksrt-product-card__body">
|
||||
<h3 class="aksrt-product-card__title"><?php echo esc_html($product['title']); ?></h3>
|
||||
<?php if ($product['excerpt']) : ?>
|
||||
<p class="aksrt-product-card__excerpt"><?php echo esc_html(wp_trim_words($product['excerpt'], 15)); ?></p>
|
||||
<h3 class="aksrt-product-card__title"><?php echo esc_html((string) ($product['title'] ?? '')); ?></h3>
|
||||
<?php if (!empty($product['excerpt'])) : ?>
|
||||
<p class="aksrt-product-card__excerpt"><?php echo esc_html(wp_trim_words((string) $product['excerpt'], 15)); ?></p>
|
||||
<?php endif; ?>
|
||||
<div class="aksrt-product-card__footer">
|
||||
<span class="aksrt-product-card__price">¥<?php echo esc_html(number_format($product['price'], 2)); ?></span>
|
||||
<?php if ($product['sales'] > 0) : ?>
|
||||
<span class="aksrt-product-card__sales"><?php printf(esc_html__('已售 %d', 'aksrt'), $product['sales']); ?></span>
|
||||
<span class="aksrt-product-card__price">¥<?php echo esc_html(number_format((float) ($product['price'] ?? 0), 2)); ?></span>
|
||||
<?php if ($show_sales) : ?>
|
||||
<span class="aksrt-product-card__sales"><?php printf(esc_html__('已售 %d', 'aksrt'), $sales); ?></span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
@@ -70,26 +112,33 @@ $term_desc = $term->description ?? '';
|
||||
</div>
|
||||
|
||||
<?php if ($max_pages > 1) : ?>
|
||||
<nav class="aksrt-product-archive__pagination" aria-label="<?php esc_attr_e('商品分页', 'aksrt'); ?>">
|
||||
<?php
|
||||
echo paginate_links([
|
||||
'total' => $max_pages,
|
||||
'current' => $paged,
|
||||
'prev_text' => '<i class="fas fa-chevron-left"></i>',
|
||||
'next_text' => '<i class="fas fa-chevron-right"></i>',
|
||||
]);
|
||||
?>
|
||||
</nav>
|
||||
<?php if ($paging === 'ajax') : ?>
|
||||
<div class="aksrt-product-archive__load-more">
|
||||
<button type="button" class="aksrt-load-more-btn" data-aksrt-shop-load-more><?php esc_html_e('加载更多', 'aksrt'); ?></button>
|
||||
</div>
|
||||
<?php else : ?>
|
||||
<nav class="aksrt-product-archive__pagination" aria-label="<?php esc_attr_e('商品分页', 'aksrt'); ?>">
|
||||
<?php
|
||||
echo paginate_links([
|
||||
'total' => $max_pages,
|
||||
'current' => $paged,
|
||||
'prev_text' => '<i class="fas fa-chevron-left"></i>',
|
||||
'next_text' => '<i class="fas fa-chevron-right"></i>',
|
||||
'add_args' => $active_orderby ? ['shop_orderby' => $active_orderby] : [],
|
||||
]);
|
||||
?>
|
||||
</nav>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
<?php else : ?>
|
||||
<div class="aksrt-product-archive__empty">
|
||||
<i class="fas fa-box-open"></i>
|
||||
<p><?php esc_html_e('该分类下暂无商品', 'aksrt'); ?></p>
|
||||
<p><?php esc_html_e('内容空空如也', 'aksrt'); ?></p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php
|
||||
wp_enqueue_script('aksrt-product-archive', get_template_directory_uri() . '/assets/js/product-archive.js', [], AKSRT_VERSION, true);
|
||||
wp_enqueue_script('aksrt-product-archive', get_template_directory_uri() . '/assets/js/product-archive.js', ['aksrt-script'], AKSRT_VERSION, true);
|
||||
get_footer();
|
||||
|
||||
@@ -11,55 +11,98 @@ get_header();
|
||||
|
||||
$term = get_queried_object();
|
||||
$paged = max(1, (int) get_query_var('paged'));
|
||||
$orderby = isset($_GET['shop_orderby']) ? sanitize_key(wp_unslash((string) $_GET['shop_orderby'])) : '';
|
||||
$shop = class_exists('AKSRT_Shop') ? AKSRT_Shop::get_instance() : null;
|
||||
$view = $shop ? $shop->get_product_archive_view([
|
||||
'taxonomy' => 'aksrt_product_tag',
|
||||
'term_id' => $term->term_id,
|
||||
'term_id' => $term ? (int) $term->term_id : 0,
|
||||
'paged' => $paged,
|
||||
]) : ['products' => [], 'term' => null, 'total' => 0, 'max_pages' => 0];
|
||||
'orderby' => $orderby,
|
||||
]) : ['products' => [], 'term' => null, 'total' => 0, 'max_pages' => 0, 'orderby' => 'date', 'paging' => 'number'];
|
||||
|
||||
$products = $view['products'];
|
||||
$total = $view['total'];
|
||||
$max_pages = $view['max_pages'];
|
||||
$products = $view['products'] ?? [];
|
||||
$total = (int) ($view['total'] ?? 0);
|
||||
$max_pages = (int) ($view['max_pages'] ?? 0);
|
||||
$term_name = $term->name ?? '';
|
||||
$term_desc = $term->description ?? '';
|
||||
$active_orderby = (string) ($view['orderby'] ?? 'date');
|
||||
$paging = (string) ($view['paging'] ?? 'number');
|
||||
$ias_s = !empty($view['ias_s']);
|
||||
$ias_max = (int) ($view['ias_max'] ?? 5);
|
||||
$style = (string) ($view['style'] ?? 'default');
|
||||
$sales_cfg = is_array($view['sales'] ?? null) ? $view['sales'] : [];
|
||||
|
||||
$order_options = [
|
||||
'date' => __('最新', 'aksrt'),
|
||||
'sales' => __('销量', 'aksrt'),
|
||||
'price_asc' => __('价格升序', 'aksrt'),
|
||||
'price_desc' => __('价格降序', 'aksrt'),
|
||||
'comments' => __('评价', 'aksrt'),
|
||||
];
|
||||
?>
|
||||
|
||||
<main id="main-content" class="site-main content-shell" tabindex="-1">
|
||||
<?php if (function_exists('aksrt_breadcrumbs')) aksrt_breadcrumbs(); ?>
|
||||
<?php if (function_exists('aksrt_breadcrumbs')) { aksrt_breadcrumbs(); } ?>
|
||||
|
||||
<div class="aksrt-product-archive container"
|
||||
data-aksrt-product-archive
|
||||
data-taxonomy="aksrt_product_tag"
|
||||
data-term-id="<?php echo esc_attr((string) ($term->term_id ?? 0)); ?>"
|
||||
data-orderby="<?php echo esc_attr($active_orderby); ?>"
|
||||
data-page="<?php echo esc_attr((string) $paged); ?>"
|
||||
data-max-pages="<?php echo esc_attr((string) $max_pages); ?>"
|
||||
data-paging="<?php echo esc_attr($paging); ?>"
|
||||
data-ias="<?php echo $ias_s ? '1' : '0'; ?>"
|
||||
data-ias-max="<?php echo esc_attr((string) $ias_max); ?>"
|
||||
data-style="<?php echo esc_attr($style); ?>">
|
||||
|
||||
<div class="aksrt-product-archive container">
|
||||
<header class="aksrt-product-archive__header">
|
||||
<div class="aksrt-product-archive__eyebrow"><?php esc_html_e('商品标签', 'aksrt'); ?></div>
|
||||
<h1 class="aksrt-product-archive__title">
|
||||
<i class="fas fa-tag"></i>
|
||||
<?php echo esc_html($term_name); ?>
|
||||
</h1>
|
||||
<p class="aksrt-product-archive__count"><?php printf(esc_html__('共 %d 件商品', 'aksrt'), $total); ?></p>
|
||||
<h1 class="aksrt-product-archive__title"><?php echo esc_html($term_name); ?></h1>
|
||||
<?php if ($term_desc) : ?>
|
||||
<p class="aksrt-product-archive__desc"><?php echo esc_html($term_desc); ?></p>
|
||||
<?php endif; ?>
|
||||
<p class="aksrt-product-archive__count" data-aksrt-archive-count><?php printf(esc_html__('共 %d 件商品', 'aksrt'), $total); ?></p>
|
||||
</header>
|
||||
|
||||
<nav class="aksrt-product-archive__toolbar" aria-label="<?php esc_attr_e('商品排序', 'aksrt'); ?>">
|
||||
<?php foreach ($order_options as $key => $label) :
|
||||
$url = add_query_arg(['shop_orderby' => $key]);
|
||||
?>
|
||||
<a class="aksrt-product-archive__sort<?php echo $active_orderby === $key ? ' is-active' : ''; ?>"
|
||||
href="<?php echo esc_url($url); ?>"
|
||||
data-aksrt-shop-orderby="<?php echo esc_attr($key); ?>">
|
||||
<?php echo esc_html($label); ?>
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
</nav>
|
||||
|
||||
<?php if (!empty($products)) : ?>
|
||||
<div class="aksrt-product-archive__grid" id="aksrt-product-grid">
|
||||
<?php foreach ($products as $product) : ?>
|
||||
<div class="aksrt-product-archive__grid aksrt-product-archive__grid--<?php echo esc_attr($style); ?>" id="aksrt-product-grid" data-aksrt-product-grid>
|
||||
<?php foreach ($products as $product) :
|
||||
$sales = (int) ($product['sales'] ?? 0);
|
||||
$mode = (string) ($sales_cfg['show'] ?? 'show');
|
||||
$min_sales = max(0, (int) ($sales_cfg['show_min'] ?? 0));
|
||||
$show_sales = ($mode === 'show' && $sales > 0) || ($mode === 'threshold' && $sales >= $min_sales);
|
||||
?>
|
||||
<article class="aksrt-product-card">
|
||||
<a href="<?php echo esc_url($product['permalink']); ?>" class="aksrt-product-card__link">
|
||||
<?php if ($product['image']) : ?>
|
||||
<a href="<?php echo esc_url((string) ($product['permalink'] ?? '#')); ?>" class="aksrt-product-card__link">
|
||||
<?php if (!empty($product['image'])) : ?>
|
||||
<div class="aksrt-product-card__image">
|
||||
<img src="<?php echo esc_url($product['image']); ?>" alt="<?php echo esc_attr($product['title']); ?>"<?php echo function_exists('aksrt_lazy_loading') ? aksrt_lazy_loading('other') : ' loading="lazy"'; ?>>
|
||||
<img src="<?php echo esc_url((string) $product['image']); ?>" alt="<?php echo esc_attr((string) ($product['title'] ?? '')); ?>" loading="lazy">
|
||||
</div>
|
||||
<?php else : ?>
|
||||
<div class="aksrt-product-card__image aksrt-product-card__image--placeholder">
|
||||
<i class="fas fa-image"></i>
|
||||
</div>
|
||||
<div class="aksrt-product-card__image aksrt-product-card__image--placeholder"><i class="fas fa-image"></i></div>
|
||||
<?php endif; ?>
|
||||
<div class="aksrt-product-card__body">
|
||||
<h3 class="aksrt-product-card__title"><?php echo esc_html($product['title']); ?></h3>
|
||||
<?php if ($product['excerpt']) : ?>
|
||||
<p class="aksrt-product-card__excerpt"><?php echo esc_html(wp_trim_words($product['excerpt'], 15)); ?></p>
|
||||
<h3 class="aksrt-product-card__title"><?php echo esc_html((string) ($product['title'] ?? '')); ?></h3>
|
||||
<?php if (!empty($product['excerpt'])) : ?>
|
||||
<p class="aksrt-product-card__excerpt"><?php echo esc_html(wp_trim_words((string) $product['excerpt'], 15)); ?></p>
|
||||
<?php endif; ?>
|
||||
<div class="aksrt-product-card__footer">
|
||||
<span class="aksrt-product-card__price">¥<?php echo esc_html(number_format($product['price'], 2)); ?></span>
|
||||
<?php if ($product['sales'] > 0) : ?>
|
||||
<span class="aksrt-product-card__sales"><?php printf(esc_html__('已售 %d', 'aksrt'), $product['sales']); ?></span>
|
||||
<span class="aksrt-product-card__price">¥<?php echo esc_html(number_format((float) ($product['price'] ?? 0), 2)); ?></span>
|
||||
<?php if ($show_sales) : ?>
|
||||
<span class="aksrt-product-card__sales"><?php printf(esc_html__('已售 %d', 'aksrt'), $sales); ?></span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
@@ -69,26 +112,33 @@ $term_name = $term->name ?? '';
|
||||
</div>
|
||||
|
||||
<?php if ($max_pages > 1) : ?>
|
||||
<nav class="aksrt-product-archive__pagination" aria-label="<?php esc_attr_e('商品分页', 'aksrt'); ?>">
|
||||
<?php
|
||||
echo paginate_links([
|
||||
'total' => $max_pages,
|
||||
'current' => $paged,
|
||||
'prev_text' => '<i class="fas fa-chevron-left"></i>',
|
||||
'next_text' => '<i class="fas fa-chevron-right"></i>',
|
||||
]);
|
||||
?>
|
||||
</nav>
|
||||
<?php if ($paging === 'ajax') : ?>
|
||||
<div class="aksrt-product-archive__load-more">
|
||||
<button type="button" class="aksrt-load-more-btn" data-aksrt-shop-load-more><?php esc_html_e('加载更多', 'aksrt'); ?></button>
|
||||
</div>
|
||||
<?php else : ?>
|
||||
<nav class="aksrt-product-archive__pagination" aria-label="<?php esc_attr_e('商品分页', 'aksrt'); ?>">
|
||||
<?php
|
||||
echo paginate_links([
|
||||
'total' => $max_pages,
|
||||
'current' => $paged,
|
||||
'prev_text' => '<i class="fas fa-chevron-left"></i>',
|
||||
'next_text' => '<i class="fas fa-chevron-right"></i>',
|
||||
'add_args' => $active_orderby ? ['shop_orderby' => $active_orderby] : [],
|
||||
]);
|
||||
?>
|
||||
</nav>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
<?php else : ?>
|
||||
<div class="aksrt-product-archive__empty">
|
||||
<i class="fas fa-box-open"></i>
|
||||
<p><?php esc_html_e('该标签下暂无商品', 'aksrt'); ?></p>
|
||||
<p><?php esc_html_e('内容空空如也', 'aksrt'); ?></p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<?php
|
||||
wp_enqueue_script('aksrt-product-archive', get_template_directory_uri() . '/assets/js/product-archive.js', [], AKSRT_VERSION, true);
|
||||
wp_enqueue_script('aksrt-product-archive', get_template_directory_uri() . '/assets/js/product-archive.js', ['aksrt-script'], AKSRT_VERSION, true);
|
||||
get_footer();
|
||||
|
||||
Reference in New Issue
Block a user