feat: move brand name to one config and redesign the accounting card

اپ: نام برند در همه‌ی صفحه‌ها و پیام‌ها یکسان شده و از یک تنظیم مرکزی خوانده می‌شود.
اپ: کارت حسابداری در صفحه‌ی مشتری بازطراحی شد؛ مانده‌ها و وضعیت لینک واضح‌تر نمایش داده می‌شوند.
پنل: کارت حسابداری در پرونده‌ی مشتری بازطراحی شد؛ مانده‌ها، آخرین ارسال و فرم لینک دستی واضح‌تر شده‌اند.
پنل: ستون «کد» در جدول‌ها به‌طور پیش‌فرض پنهان است و از انتخاب ستون‌ها قابل نمایش است.
پنل: تغییر نمایش ستون‌ها در جدول‌ها بلافاصله اعمال می‌شود.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
s.esmaeili
2026-10-14 13:31:20 +03:30
co-authored by Claude Haiku 5.5
parent 5d2a1efb0c
commit 532ceee189
100 changed files with 1870 additions and 579 deletions
+11 -49
View File
@@ -22,7 +22,7 @@
<div class="home">
<header class="home__bar">
<div class="home__barInner">
<img src="/img/montzar-logo.png" alt="منتزر" class="home__logo" />
<img src="/img/brand-logo.png" :alt="brand.name" class="home__logo" />
<RateStrip :quote="goldPrice" compact class="home__rate" />
</div>
</header>
@@ -39,7 +39,6 @@
:slides="slides"
class="home__hero home__reveal"
:style="revealDelay(0)"
@select="onSlide"
/>
<section v-if="memory.cats.length" class="home__section home__reveal" :style="revealDelay(1)">
@@ -80,6 +79,7 @@
</template>
<script setup lang="ts">
import { brand } from '../brand'
import { computed, nextTick, onActivated, onMounted, ref } from 'vue'
import { useRouter } from 'vue-router'
import { Loading, PullRefresh } from 'vant'
@@ -149,35 +149,15 @@ const newestProducts = computed(() => products.value)
const reelProducts = computed(() => products.value.slice(0, REEL_LIMIT))
const listProducts = computed(() => products.value.slice(0, LIST_LIMIT))
// The hero is built from REAL categories, not a curated banner payload (none
// exists). Each slide deep-links into search filtered by that category, so the
// artwork is decorative but the destination is genuine.
const slides = computed<HeroSlide[]>(() => {
// Prefer what the admin actually configured; the category-derived hero below
// stays as the fallback for when no slide has been uploaded.
if (adminSlides.value?.length) {
return adminSlides.value
.filter((s) => s.image)
.map((s, i) => ({
key: `${i}-${s.image}`,
title: '',
image: s.image,
link: s.link || undefined,
}))
}
return memory.cats.slice(0, 5).map((cat) => ({
key: cat.id,
title: cat.title,
image: productImageForCategory(cat.id),
categoryId: cat.id,
}))
})
function productImageForCategory(categoryId: string): string | undefined {
const match = products.value.find((p) => p.category?.id === categoryId)
return match?.images?.[0]?.url
}
// The hero shows ONLY what the admin uploaded in the slider section
// (manage/Const.vue "اسلایدر موبایل"). It used to fall back to slides built
// from categories and filled with product photos, so a newly added product
// could land in the hero. Slides are display-only: tapping one does nothing.
const slides = computed<HeroSlide[]>(() =>
(adminSlides.value ?? [])
.filter((s) => s.image)
.map((s, i) => ({ key: `${i}-${s.image}`, image: s.image })),
)
// Opens as a morph out of the tapped card (ProductMorph.vue, mounted in
// App.vue) — the card is found from the tap, so the emit chain stays as is.
@@ -190,24 +170,6 @@ function onCategory(cat: HomeCategory) {
router.push({ name: 'search' })
}
function onSlide(slide: HeroSlide) {
// Admin-uploaded slides (manage/Const.vue) carry their own destination
// instead of a categoryId — an absolute URL opens in a new tab, anything
// else is treated as an in-app path. Same rule as user-app.
if (slide.link) {
if (/^https?:\/\//i.test(slide.link)) {
window.open(slide.link, '_blank', 'noopener')
} else {
router.push(slide.link)
}
return
}
if (slide.categoryId) {
filters.query.cats = [slide.categoryId]
}
router.push({ name: 'search' })
}
function goNewest() {
router.push({ name: 'newestProduct' })
}