product/flash-sale
Trang tổng hợp các sản phẩm đang flash sale.
- Tệp giao diện:
product/flash-sale.html - Đường dẫn:
/flash-sale - Controller:
App\Modules\Marketing\FlashSaleController::indexPage
Ngoài những biến dưới đây, slot này còn nhận toàn bộ biến toàn cục.
Biến của trang
| Biến | Kiểu | Mô tả | Từ phiên bản |
|---|---|---|---|
sales |
FlashSale[] |
Các đợt flash sale đang chạy. | 1.0 |
products |
Product[] |
Sản phẩm đang giảm giá, kèm trường flash. | 1.0 |
has_more |
boolean |
Còn trang tiếp theo hay không. | 1.0 |
next_cursor |
string | null |
Con trỏ để tải trang kế; null khi đã hết. | 1.0 |
cursor |
string | null |
Con trỏ của trang đang xem. | 1.0 |
page_title |
string |
Tiêu đề SEO của trang. | 1.0 |
Mã đầy đủ
Toàn bộ tệp product/flash-sale.html của bộ giao diện mặc định. Chép về templates/<bộ giao diện của bạn>/product/flash-sale.html rồi sửa theo ý muốn.
<div class="container page-content flash-hub">
<nav class="breadcrumb">
<a href="/">Trang chủ</a> / <span>Flash Sale</span>
</nav>
<div class="flash-hub__header">
<h1 class="flash-hub__title">⚡ Flash Sale</h1>
{% if sales.size > 0 and sales[0].ends_at %}
<div class="flash-hub__countdown" data-flash-ends="{{ sales[0].ends_at }}">
Kết thúc sau <strong class="flash-countdown-value">--:--:--</strong>
</div>
{% endif %}
</div>
{% if products.size > 0 %}
<div class="product-grid">
{% for product in products %}
<div class="product-card product-card--flash">
{% if product.url %}<a href="{{ product.url }}" class="product-card__img-link">{% else %}<span class="product-card__img-link">{% endif %}
{% if product.cover_image_url %}
<img src="{{ product.cover_image_url }}" alt="{{ product.title }}"
class="product-card__img" loading="lazy">
{% else %}
<div class="product-card__img-placeholder"></div>
{% endif %}
<span class="product-card__badge product-card__badge--flash">⚡ Flash</span>
{% if product.url %}</a>{% else %}</span>{% endif %}
<div class="product-card__body">
{% if product.url %}
<a href="{{ product.url }}" class="product-card__name">{{ product.title }}</a>
{% else %}
<span class="product-card__name">{{ product.title }}</span>
{% endif %}
<div class="product-card__price">
<span class="price price--flash">{{ product.flash.deal_price | money }}</span>
<span class="price price--original">{{ product.price | money }}</span>
</div>
{% if product.flash.ends_at %}
<div class="product-card__countdown" data-flash-ends="{{ product.flash.ends_at }}">
Còn <strong class="flash-countdown-value">--:--:--</strong>
</div>
{% endif %}
{% if product.flash.remaining %}
<div class="product-card__remaining">Còn {{ product.flash.remaining }} suất</div>
{% endif %}
<button type="button" class="btn btn--outline btn--sm btn--block quick-add"
data-product-slug="{{ product.slug }}">
Thêm vào giỏ
</button>
</div>
</div>
{% endfor %}
</div>
{% if has_more %}
<div class="load-more">
<a href="?cursor={{ next_cursor | url_encode }}" class="btn btn--outline">Xem thêm</a>
</div>
{% endif %}
{% else %}
<div class="empty-state">
<p>Hiện chưa có chương trình flash sale nào đang diễn ra.</p>
<a href="/san-pham" class="btn btn--outline">Xem tất cả sản phẩm</a>
</div>
{% endif %}
</div>
<script>
(function () {
function pad(n) { return String(n).padStart(2, '0'); }
function tick() {
var now = Date.now();
document.querySelectorAll('[data-flash-ends]').forEach(function (el) {
var raw = el.getAttribute('data-flash-ends');
if (!raw) return;
var end = Date.parse(raw.replace(' ', 'T') + 'Z');
var diff = Math.floor((end - now) / 1000);
var out = el.querySelector('.flash-countdown-value');
if (!out) return;
if (diff <= 0) { out.textContent = '00:00:00'; return; }
var h = Math.floor(diff / 3600), m = Math.floor((diff % 3600) / 60), s = diff % 60;
out.textContent = pad(h) + ':' + pad(m) + ':' + pad(s);
});
}
tick();
setInterval(tick, 1000);
})();
</script>