admin user and ui panel
@@ -0,0 +1 @@
|
||||
import{d as f,o as v,c as s,k as e,q as u,g as m,h as p,i as o,F as g,r as y,I as k,b as P,f as C,s as b,J as h,v as x,_ as B}from"./index-CBZSLIgu.js";import{P as V}from"./PageBar-BrfQrql1.js";import{a as w}from"./Spinner.vue_vue_type_style_index_0_lang-DQwH-Xdy.js";import{S as I}from"./SectionHead-QOHixgma.js";import{P as N}from"./ProductCard-Dj_Eu2VB.js";import{a as q}from"./client-CJxy5iKa.js";import"./_commonjsHelpers-Cpj98o6Y.js";import"./SingleProduct-DssXZ7g8.js";import"./NotifyVant.vue_vue_type_script_setup_true_lang-BZS-5R5g.js";import"./DialogVant.vue_vue_type_style_index_0_lang-CINs34e0.js";import"./cartStore-CAFgxy6H.js";import"./dialogEmpty1-Caw7F5KL.js";import"./index-CUVFR_Bx.js";import"./classCallCheck-R2Fkn3Yh.js";const F={class:"coll"},S={key:0,class:"coll__grid"},z={key:1,class:"coll__empty"},E=f({__name:"CollectionView",setup(H){const l=h(),t=m(!1),n=m([]),d=p(()=>l.query.page||"مجموعه"),r=p(()=>n.value.filter(i=>i.collections.some(a=>a.collectionId===l.params.id)));return v(async()=>{t.value=!0;try{n.value=await q.listProducts()}finally{t.value=!1}}),(i,a)=>{const _=b("container");return o(),s("div",F,[e(V,{title:d.value,"back-to":"search"},null,8,["title"]),e(w,{spinning:t.value,tip:"درحال دریافت اطلاعات"},{default:u(()=>[e(_,{class:"coll__body"},{default:u(()=>[e(I,{title:"محصولات این مجموعه",subtitle:"برای دیدن جزئیات، روی محصول ضربه بزنید"}),r.value.length?(o(),s("div",S,[(o(!0),s(g,null,y(r.value,c=>(o(),x(N,{key:c.id,value:c},null,8,["value"]))),128))])):t.value?C("",!0):(o(),s("section",z,[e(k,{icon:"vuesax-linear:box",size:"26"}),a[0]||(a[0]=P("p",null,"در این مجموعه محصولی ثبت نشده است.",-1))]))]),_:1})]),_:1},8,["spinning"])])}}}),W=B(E,[["__scopeId","data-v-4e05d52b"]]);export{W as default};
|
||||
@@ -1 +0,0 @@
|
||||
import{d as f,o as v,c as s,e,w as u,j as m,l as p,g as o,F as g,r as y,I as P,b as k,f as C,q as b,J as x,s as h,_ as w}from"./index-CXDP73fh.js";import{P as B}from"./PageBar-DGocjVpH.js";import{a as V}from"./Spinner.vue_vue_type_style_index_0_lang-Cw01MXb5.js";import{S as I}from"./SectionHead-BpYVXEql.js";import{P as N}from"./ProductCard-BLK42sSQ.js";import{a as q}from"./client-WIBW0tF5.js";import"./_commonjsHelpers-Cpj98o6Y.js";import"./SingleProduct-c159xuqF.js";import"./NotifyVant.vue_vue_type_script_setup_true_lang-CJQoykcb.js";import"./DialogVant.vue_vue_type_style_index_0_lang-NTcY_W-M.js";import"./cartStore-DyCvrw6I.js";import"./dialogEmpty1-E3h_JfwQ.js";import"./index-BYO8JY_f.js";import"./classCallCheck-BI0ic3HN.js";const F={class:"coll"},S={key:0,class:"coll__grid"},j={key:1,class:"coll__empty"},z=f({__name:"CollectionView",setup(E){const l=x(),t=m(!1),n=m([]),d=p(()=>l.query.page||"مجموعه"),r=p(()=>n.value.filter(i=>i.collections.some(a=>a.collectionId===l.params.id)));return v(async()=>{t.value=!0;try{n.value=await q.listProducts()}finally{t.value=!1}}),(i,a)=>{const _=b("container");return o(),s("div",F,[e(B,{title:d.value,"back-to":"search"},null,8,["title"]),e(V,{spinning:t.value,tip:"درحال دریافت اطلاعات"},{default:u(()=>[e(_,{class:"coll__body"},{default:u(()=>[e(I,{title:"محصولات این مجموعه",subtitle:"برای دیدن جزئیات، روی محصول ضربه بزنید"}),r.value.length?(o(),s("div",S,[(o(!0),s(g,null,y(r.value,c=>(o(),h(N,{key:c.id,value:c},null,8,["value"]))),128))])):t.value?C("",!0):(o(),s("section",j,[e(P,{icon:"vuesax-linear:box",size:"26"}),a[0]||(a[0]=k("p",null,"در این مجموعه محصولی ثبت نشده است.",-1))]))]),_:1})]),_:1},8,["spinning"])])}}}),W=w(z,[["__scopeId","data-v-4e05d52b"]]);export{W as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{d as l,v as n,q as r,u as d,ao as c,i as x,b as t,A as o,D as a,t as i}from"./index-CBZSLIgu.js";const f={class:"my-4"},u={class:"w-full flex justify-center"},m={class:"w-full flex justify-center text-[18px] font-semibold"},_={class:"grid grid-cols-2 border-t border-solid border-[#E5E7EB] pt-4 mx-2 mt-6 font-semibold text-[15px]"},g=l({__name:"DialogVant",props:{timeOut:{},text1:{},text2:{},text3:{}},setup(e){return(s,b)=>(x(),n(d(c),null,{default:r(()=>[t("div",f,[t("div",u,[o(s.$slots,"img",{},()=>[a(i(e.text2),1)])]),t("div",m,[o(s.$slots,"title",{},()=>[a(i(e.text1),1)])]),t("div",_,[o(s.$slots,"button",{},()=>[a(i(e.text3),1)])])])]),_:3}))}});export{g as _};
|
||||
@@ -1 +0,0 @@
|
||||
import{d as l,s as n,w as r,u as d,am as c,g as x,b as t,B as o,i as a,t as i}from"./index-CXDP73fh.js";const f={class:"my-4"},m={class:"w-full flex justify-center"},u={class:"w-full flex justify-center text-[18px] font-semibold"},_={class:"grid grid-cols-2 border-t border-solid border-[#E5E7EB] pt-4 mx-2 mt-6 font-semibold text-[15px]"},p=l({__name:"DialogVant",props:{timeOut:{},text1:{},text2:{},text3:{}},setup(e){return(s,b)=>(x(),n(d(c),null,{default:r(()=>[t("div",f,[t("div",m,[o(s.$slots,"img",{},()=>[a(i(e.text2),1)])]),t("div",u,[o(s.$slots,"title",{},()=>[a(i(e.text1),1)])]),t("div",_,[o(s.$slots,"button",{},()=>[a(i(e.text3),1)])])])]),_:3}))}});export{p as _};
|
||||
@@ -1 +0,0 @@
|
||||
import{d as v,o as b,c as o,e,w as c,j as m,g as t,F as p,b as i,r as g,I as y,u as k,f as P,x as h,l as C,q as x,s as F,_ as w}from"./index-CXDP73fh.js";import{P as B}from"./PageBar-DGocjVpH.js";import{a as V}from"./Spinner.vue_vue_type_style_index_0_lang-Cw01MXb5.js";import{S}from"./SectionHead-BpYVXEql.js";import{P as I}from"./ProductCard-BLK42sSQ.js";import{u as N}from"./SingleProduct-c159xuqF.js";import{a as $}from"./client-WIBW0tF5.js";import"./_commonjsHelpers-Cpj98o6Y.js";import"./cartStore-DyCvrw6I.js";import"./index-BYO8JY_f.js";import"./classCallCheck-BI0ic3HN.js";import"./NotifyVant.vue_vue_type_script_setup_true_lang-CJQoykcb.js";import"./DialogVant.vue_vue_type_style_index_0_lang-NTcY_W-M.js";import"./dialogEmpty1-E3h_JfwQ.js";const R={class:"favs"},j={class:"favs__grid"},q={key:1,class:"favs__empty"},z=v({__name:"FavoritesView",setup(E){const d=h(),_=N(),a=m(!1),l=m([]),r=C(()=>l.value.filter(u=>_.isFavorite(u.id)));return b(async()=>{a.value=!0;try{l.value=await $.listProducts()}finally{a.value=!1}}),(u,s)=>{const f=x("container");return t(),o("div",R,[e(B,{title:"علاقهمندی","back-to":"userPage::hasSubRoute"}),e(V,{spinning:a.value,tip:"در حال دریافت اطلاعات"},{default:c(()=>[e(f,{class:"favs__body"},{default:c(()=>[r.value.length?(t(),o(p,{key:0},[e(S,{title:"ذخیرهشدهها",subtitle:`${r.value.length} محصول`},null,8,["subtitle"]),i("div",j,[(t(!0),o(p,null,g(r.value,n=>(t(),F(I,{key:n.id,value:n},null,8,["value"]))),128))])],64)):a.value?P("",!0):(t(),o("section",q,[e(y,{icon:"vuesax-linear:heart",size:"26"}),s[1]||(s[1]=i("p",null,"هنوز محصولی را به علاقهمندیها اضافه نکردهاید.",-1)),i("button",{type:"button",class:"favs__emptyCta",onClick:s[0]||(s[0]=n=>k(d).push({name:"home"}))}," دیدن محصولات ")]))]),_:1})]),_:1},8,["spinning"])])}}}),Y=w(z,[["__scopeId","data-v-93bb7c6a"]]);export{Y as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{d as f,o as b,c as o,k as e,q as c,g as m,i as t,F as p,b as i,r as g,I as y,u as k,f as h,x as P,h as C,s as x,v as F,_ as B}from"./index-CBZSLIgu.js";import{P as V}from"./PageBar-BrfQrql1.js";import{a as w}from"./Spinner.vue_vue_type_style_index_0_lang-DQwH-Xdy.js";import{S}from"./SectionHead-QOHixgma.js";import{P as I}from"./ProductCard-Dj_Eu2VB.js";import{u as N}from"./SingleProduct-DssXZ7g8.js";import{a as $}from"./client-CJxy5iKa.js";import"./_commonjsHelpers-Cpj98o6Y.js";import"./cartStore-CAFgxy6H.js";import"./index-CUVFR_Bx.js";import"./classCallCheck-R2Fkn3Yh.js";import"./NotifyVant.vue_vue_type_script_setup_true_lang-BZS-5R5g.js";import"./DialogVant.vue_vue_type_style_index_0_lang-CINs34e0.js";import"./dialogEmpty1-Caw7F5KL.js";const R={class:"favs"},q={class:"favs__grid"},z={key:1,class:"favs__empty"},E=f({__name:"FavoritesView",setup(H){const d=P(),v=N(),a=m(!1),l=m([]),r=C(()=>l.value.filter(u=>v.isFavorite(u.id)));return b(async()=>{a.value=!0;try{l.value=await $.listProducts()}finally{a.value=!1}}),(u,s)=>{const _=x("container");return t(),o("div",R,[e(V,{title:"علاقهمندی","back-to":"userPage::hasSubRoute"}),e(w,{spinning:a.value,tip:"در حال دریافت اطلاعات"},{default:c(()=>[e(_,{class:"favs__body"},{default:c(()=>[r.value.length?(t(),o(p,{key:0},[e(S,{title:"ذخیرهشدهها",subtitle:`${r.value.length} محصول`},null,8,["subtitle"]),i("div",q,[(t(!0),o(p,null,g(r.value,n=>(t(),F(I,{key:n.id,value:n},null,8,["value"]))),128))])],64)):a.value?h("",!0):(t(),o("section",z,[e(y,{icon:"vuesax-linear:heart",size:"26"}),s[1]||(s[1]=i("p",null,"هنوز محصولی را به علاقهمندیها اضافه نکردهاید.",-1)),i("button",{type:"button",class:"favs__emptyCta",onClick:s[0]||(s[0]=n=>k(d).push({name:"home"}))}," دیدن محصولات ")]))]),_:1})]),_:1},8,["spinning"])])}}}),Y=B(E,[["__scopeId","data-v-93bb7c6a"]]);export{Y as default};
|
||||
@@ -1 +0,0 @@
|
||||
import{P as x}from"./PageBar-DGocjVpH.js";import{E as P,a as _}from"./Spinner.vue_vue_type_style_index_0_lang-Cw01MXb5.js";import{S}from"./SectionHead-BpYVXEql.js";import{P as k}from"./ProductCard-BLK42sSQ.js";import{d,g as n,c as l,e as o,u as c,y as b,z as B,A as E,B as C,s as v,f as y,j as u,C as L,D as A,w as p,F as $,r as F,l as N,q as V,_ as D}from"./index-CXDP73fh.js";import{a as I}from"./client-WIBW0tF5.js";import"./_commonjsHelpers-Cpj98o6Y.js";import"./SingleProduct-c159xuqF.js";import"./NotifyVant.vue_vue_type_script_setup_true_lang-CJQoykcb.js";import"./DialogVant.vue_vue_type_style_index_0_lang-NTcY_W-M.js";import"./cartStore-DyCvrw6I.js";import"./dialogEmpty1-E3h_JfwQ.js";import"./index-BYO8JY_f.js";import"./classCallCheck-BI0ic3HN.js";const j={class:"flex justify-center items-center empty"},z=d({__name:"EmptyState",props:{description:{},image:{}},setup(t){return(e,a)=>(n(),l("div",j,[o(c(P),{description:"موردی یافت نشد",image:t.image,class:"h-[200px] w-[200px] opacity-70"},null,8,["image"])]))}}),H="/assets/favoritEmpty-kbtPAdi5.svg",T=d({__name:"ScrollFetchSearch",props:{loading:{type:Boolean},flag:{type:Boolean},padding:{}},emits:["fetch"],setup(t,{emit:e}){const a=e,r=b(),s=u(null);return B(s,()=>{a("fetch")},{distance:50}),(i,m)=>(n(),l("main",{class:"flex flex-col flex-1 overflow-auto no-scrollbar w-[100vw] max-w-screen-md m-auto",style:E({paddingTop:t.padding||c(r).headerHeight+"px"}),ref_key:"scrollArea",ref:s},[C(i.$slots,"default"),o(_,{cls:"my-[10px]",spinning:t.loading,indicator:!0,tip:"درحال دریافت اطلاعات"},null,8,["spinning"]),t.flag?(n(),v(z,{key:0,image:c(H)},null,8,["image"])):y("",!0)],4))}});function q(){const t=L({totalPages:0,productList:[],flag:!1});return{productsData:t,getProducts:async(a,r=!1)=>{if(!r){a.value=!0;try{t.productList=await I.listProducts(),t.flag=t.productList.length<1}catch(s){console.log(s)}finally{a.value=!1}}}}}const G={class:"newest"},M={key:0,class:"newest__grid"},g=20,Z=d({__name:"NewestView",setup(t){const{productsData:e,getProducts:a}=q(),r=u(!1),s=u(!1),i=u(g),m=N(()=>e.productList.slice(0,i.value));A(async()=>{await a(r,!1)});const h=async()=>{if(i.value<e.productList.length){i.value+=g;return}await a(s,!0)};return(J,K)=>{const w=V("container");return n(),l("div",G,[o(x,{title:"جدیدترین محصولات","back-to":"home"}),o(_,{spinning:r.value,tip:"درحال دریافت اطلاعات"},{default:p(()=>[o(T,{class:"newest__scroll",loading:s.value,flag:c(e).flag,padding:"0px",onFetch:h},{default:p(()=>[o(w,{class:"newest__body"},{default:p(()=>[o(S,{title:"تازهها",subtitle:"آخرین افزودههای مجموعه"}),c(e).productList.length?(n(),l("div",M,[(n(!0),l($,null,F(m.value,f=>(n(),v(k,{key:f.id,value:f},null,8,["value"]))),128))])):y("",!0)]),_:1})]),_:1},8,["loading","flag"])]),_:1},8,["spinning"])])}}}),it=D(Z,[["__scopeId","data-v-62cb523e"]]);export{it as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{P as x}from"./PageBar-BrfQrql1.js";import{E as P,a as _}from"./Spinner.vue_vue_type_style_index_0_lang-DQwH-Xdy.js";import{S}from"./SectionHead-QOHixgma.js";import{P as k}from"./ProductCard-Dj_Eu2VB.js";import{d,i as n,c,k as o,u as l,y as B,z as C,n as E,A as b,v,f as y,g as u,B as L,C as $,q as p,F as A,r as F,h as I,s as N,_ as V}from"./index-CBZSLIgu.js";import{a as q}from"./client-CJxy5iKa.js";import"./_commonjsHelpers-Cpj98o6Y.js";import"./SingleProduct-DssXZ7g8.js";import"./NotifyVant.vue_vue_type_script_setup_true_lang-BZS-5R5g.js";import"./DialogVant.vue_vue_type_style_index_0_lang-CINs34e0.js";import"./cartStore-CAFgxy6H.js";import"./dialogEmpty1-Caw7F5KL.js";import"./index-CUVFR_Bx.js";import"./classCallCheck-R2Fkn3Yh.js";const z={class:"flex justify-center items-center empty"},D=d({__name:"EmptyState",props:{description:{},image:{}},setup(t){return(e,a)=>(n(),c("div",z,[o(l(P),{description:"موردی یافت نشد",image:t.image,class:"h-[200px] w-[200px] opacity-70"},null,8,["image"])]))}}),H="/assets/favoritEmpty-CIK7qR49.svg",T=d({__name:"ScrollFetchSearch",props:{loading:{type:Boolean},flag:{type:Boolean},padding:{}},emits:["fetch"],setup(t,{emit:e}){const a=e,r=B(),s=u(null);return C(s,()=>{a("fetch")},{distance:50}),(i,m)=>(n(),c("main",{class:"flex flex-col flex-1 overflow-auto no-scrollbar w-[100vw] max-w-screen-md m-auto",style:E({paddingTop:t.padding||l(r).headerHeight+"px"}),ref_key:"scrollArea",ref:s},[b(i.$slots,"default"),o(_,{cls:"my-[10px]",spinning:t.loading,indicator:!0,tip:"درحال دریافت اطلاعات"},null,8,["spinning"]),t.flag?(n(),v(D,{key:0,image:l(H)},null,8,["image"])):y("",!0)],4))}});function j(){const t=L({totalPages:0,productList:[],flag:!1});return{productsData:t,getProducts:async(a,r=!1)=>{if(!r){a.value=!0;try{t.productList=await q.listProducts(),t.flag=t.productList.length<1}catch(s){console.log(s)}finally{a.value=!1}}}}}const G={class:"newest"},K={key:0,class:"newest__grid"},g=20,M=d({__name:"NewestView",setup(t){const{productsData:e,getProducts:a}=j(),r=u(!1),s=u(!1),i=u(g),m=I(()=>e.productList.slice(0,i.value));$(async()=>{await a(r,!1)});const h=async()=>{if(i.value<e.productList.length){i.value+=g;return}await a(s,!0)};return(R,Z)=>{const w=N("container");return n(),c("div",G,[o(x,{title:"جدیدترین محصولات","back-to":"home"}),o(_,{spinning:r.value,tip:"درحال دریافت اطلاعات"},{default:p(()=>[o(T,{class:"newest__scroll",loading:s.value,flag:l(e).flag,padding:"0px",onFetch:h},{default:p(()=>[o(w,{class:"newest__body"},{default:p(()=>[o(S,{title:"تازهها",subtitle:"آخرین افزودههای مجموعه"}),l(e).productList.length?(n(),c("div",K,[(n(!0),c(A,null,F(m.value,f=>(n(),v(k,{key:f.id,value:f},null,8,["value"]))),128))])):y("",!0)]),_:1})]),_:1},8,["loading","flag"])]),_:1},8,["spinning"])])}}}),it=V(M,[["__scopeId","data-v-62cb523e"]]);export{it as default};
|
||||
@@ -1 +1 @@
|
||||
import{d as a,c as n,e as o,w as i,q as c,g as _,b as e,I as r,_ as p}from"./index-CXDP73fh.js";import{P as d}from"./PageBar-DGocjVpH.js";const f={class:"notif"},l={class:"notif__empty"},m=a({__name:"NotificationsView",setup(u){return(x,t)=>{const s=c("container");return _(),n("div",f,[o(d,{title:"اعلانها","back-to":"home"}),o(s,{class:"notif__body"},{default:i(()=>[e("section",l,[o(r,{icon:"vuesax-linear:notification",size:"26"}),t[0]||(t[0]=e("p",null,"اعلانی برای نمایش وجود ندارد.",-1))])]),_:1})])}}}),B=p(m,[["__scopeId","data-v-44d0c9a5"]]);export{B as default};
|
||||
import{d as a,c as n,k as o,q as i,s as c,i as _,b as e,I as r,_ as p}from"./index-CBZSLIgu.js";import{P as d}from"./PageBar-BrfQrql1.js";const f={class:"notif"},l={class:"notif__empty"},m=a({__name:"NotificationsView",setup(u){return(x,t)=>{const s=c("container");return _(),n("div",f,[o(d,{title:"اعلانها","back-to":"home"}),o(s,{class:"notif__body"},{default:i(()=>[e("section",l,[o(r,{icon:"vuesax-linear:notification",size:"26"}),t[0]||(t[0]=e("p",null,"اعلانی برای نمایش وجود ندارد.",-1))])]),_:1})])}}}),B=p(m,[["__scopeId","data-v-44d0c9a5"]]);export{B as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{d as r,k as n,ah as p,ai as f,aj as h,ak as w,al as y,am as k,a6 as x,an as c,a5 as g,ad as N,G as S,i as V,c as v,u as P,n as _,q as b,b as l,I as B,A as I,D as z,t as C,f as O,g as D}from"./index-CBZSLIgu.js";const[T,U]=g("notify"),j=["lockScroll","position","show","teleport","zIndex"],q=w({},y,{type:c("danger"),color:String,message:x,position:c("top"),className:k,background:String,lockScroll:Boolean});var A=r({name:T,props:q,emits:["update:show"],setup(e,{emit:o,slots:t}){const a=s=>o("update:show",s);return()=>n(h,p({class:[U([e.type]),e.className],style:{color:e.color,background:e.background},overlay:!1,duration:.2,"onUpdate:show":a},f(e,j)),{default:()=>[t.default?t.default():e.message]})}});const E=N(A),G={key:0,class:"flex"},$={class:"w-full text-white my-2"},F={class:"w-full flex content-end"},J=r({__name:"NotifyVant",props:{modelValue:{type:Boolean},timeOut:{},text:{},color:{}},emits:["update:modelValue"],setup(e,{emit:o}){const t=D(!0),a=e,s=o;S(()=>a.modelValue,()=>{a.timeOut!==-1&&u()});const u=()=>{setTimeout(()=>{s("update:modelValue",!1)},a.timeOut)};return(d,i)=>e.modelValue?(V(),v("div",G,[n(P(E),{show:t.value,"onUpdate:show":i[0]||(i[0]=m=>t.value=m),style:_(e.color)},{default:b(()=>[l("div",$,[l("div",F,[n(B,{icon:"vuesax-linear:info-circle",class:"ml-[20px] text-white",size:"25"}),l("div",null,[I(d.$slots,"default",{},()=>[z(C(e.text),1)])])])])]),_:3},8,["show","style"])])):O("",!0)}});export{J as _};
|
||||
@@ -1 +0,0 @@
|
||||
import{d as r,e as n,af as p,ag as f,ah as h,ai as w,aj as y,ak as g,a4 as x,al as c,a3 as k,ab as N,G as S,g as V,c as v,u as P,A as b,w as B,b as l,I as _,B as I,i as z,t as C,f as O,j}from"./index-CXDP73fh.js";const[T,U]=k("notify"),A=["lockScroll","position","show","teleport","zIndex"],D=w({},y,{type:c("danger"),color:String,message:x,position:c("top"),className:g,background:String,lockScroll:Boolean});var E=r({name:T,props:D,emits:["update:show"],setup(e,{emit:o,slots:t}){const a=s=>o("update:show",s);return()=>n(h,p({class:[U([e.type]),e.className],style:{color:e.color,background:e.background},overlay:!1,duration:.2,"onUpdate:show":a},f(e,A)),{default:()=>[t.default?t.default():e.message]})}});const G=N(E),$={key:0,class:"flex"},q={class:"w-full text-white my-2"},F={class:"w-full flex content-end"},J=r({__name:"NotifyVant",props:{modelValue:{type:Boolean},timeOut:{},text:{},color:{}},emits:["update:modelValue"],setup(e,{emit:o}){const t=j(!0),a=e,s=o;S(()=>a.modelValue,()=>{a.timeOut!==-1&&u()});const u=()=>{setTimeout(()=>{s("update:modelValue",!1)},a.timeOut)};return(d,i)=>e.modelValue?(V(),v("div",$,[n(P(G),{show:t.value,"onUpdate:show":i[0]||(i[0]=m=>t.value=m),style:b(e.color)},{default:B(()=>[l("div",q,[l("div",F,[n(_,{icon:"vuesax-linear:info-circle",class:"ml-[20px] text-white",size:"25"}),l("div",null,[I(d.$slots,"default",{},()=>[z(C(e.text),1)])])])])]),_:3},8,["show","style"])])):O("",!0)}});export{J as _};
|
||||
@@ -1 +1 @@
|
||||
import{d as _,c as s,b as a,e as d,I as b,t as u,B as i,g as t,x as p,_ as k}from"./index-CXDP73fh.js";const h={class:"bar"},f={class:"bar__inner"},B={key:1,class:"bar__spacer","aria-hidden":"true"},g={class:"bar__title"},m={class:"bar__trailing"},v=_({__name:"PageBar",props:{title:{},back:{type:Boolean,default:!0},backTo:{}},setup(e){const o=e,r=p();function l(){o.backTo?r.push({name:o.backTo}):r.back()}return(n,c)=>(t(),s("header",h,[a("div",f,[e.back?(t(),s("button",{key:0,type:"button",class:"bar__btn","aria-label":"بازگشت",onClick:l},[d(b,{icon:"vuesax-linear:arrow-right-1",size:"20"})])):(t(),s("span",B)),a("h1",g,u(e.title),1),a("div",m,[i(n.$slots,"trailing",{},()=>[c[0]||(c[0]=a("span",{class:"bar__spacer","aria-hidden":"true"},null,-1))],!0)])]),i(n.$slots,"below",{},void 0,!0)]))}}),x=k(v,[["__scopeId","data-v-d2d3dfeb"]]);export{x as P};
|
||||
import{d as _,c as s,b as a,k as d,I as b,t as u,A as i,i as t,x as p,_ as k}from"./index-CBZSLIgu.js";const h={class:"bar"},f={class:"bar__inner"},m={key:1,class:"bar__spacer","aria-hidden":"true"},B={class:"bar__title"},g={class:"bar__trailing"},v=_({__name:"PageBar",props:{title:{},back:{type:Boolean,default:!0},backTo:{}},setup(e){const o=e,r=p();function l(){o.backTo?r.push({name:o.backTo}):r.back()}return(n,c)=>(t(),s("header",h,[a("div",f,[e.back?(t(),s("button",{key:0,type:"button",class:"bar__btn","aria-label":"بازگشت",onClick:l},[d(b,{icon:"vuesax-linear:arrow-right-1",size:"20"})])):(t(),s("span",m)),a("h1",B,u(e.title),1),a("div",g,[i(n.$slots,"trailing",{},()=>[c[0]||(c[0]=a("span",{class:"bar__spacer","aria-hidden":"true"},null,-1))],!0)])]),i(n.$slots,"below",{},void 0,!0)]))}}),x=k(v,[["__scopeId","data-v-d2d3dfeb"]]);export{x as P};
|
||||
@@ -1 +1 @@
|
||||
import{d as D,m as P,g as s,c as i,F as S,b as t,u as e,h as z,e as _,I as f,k as B,n as I,f as d,t as u,w as N,s as V,j as E,l as $,_ as j}from"./index-CXDP73fh.js";import{u as q,S as L}from"./SingleProduct-c159xuqF.js";import{u as M}from"./cartStore-DyCvrw6I.js";import{D as O}from"./index-BYO8JY_f.js";const U={class:"pc__art"},A=["alt"],G={key:1,class:"pc__artEmpty","aria-hidden":"true"},H=["aria-label"],J={key:2,class:"pc__flag"},K={key:3,class:"pc__flag pc__flag--soft"},Q={class:"pc__body"},R={class:"pc__title"},T={class:"pc__meta"},W={class:"pc__weight"},X={key:0,class:"pc__wage"},Y=D({__name:"ProductCard",props:{value:{}},setup(g){const h=g,n=q(),{image:p,id:a,title:v,min_gram:y,wage:m,is_exist:b,qty:k,isPreOrder:w}=M(h.value),l=E(!1);function x(){l.value=!0}const C=$(()=>{const r=m.value;return r==null?"":r%1===0?String(r):r.toFixed(2)});return(r,o)=>{const F=P("lazy");return s(),i(S,null,[t("article",{class:"pc",onClick:x},[t("div",U,[e(p)?z((s(),i("img",{key:0,alt:e(v)},null,8,A)),[[F,e(p)]]):(s(),i("div",G,[_(f,{icon:"vuesax-linear:diamonds",size:"26"})])),t("button",{type:"button",class:I(["pc__fav",{"is-on":e(n).isFavorite(e(a))}]),"aria-label":e(n).isFavorite(e(a))?"حذف از علاقهمندی":"افزودن به علاقهمندی",onClick:o[0]||(o[0]=B(c=>e(n).toggle(e(a)),["stop"]))},[_(f,{icon:e(n).isFavorite(e(a))?"vuesax-linear:heart-fill":"vuesax-linear:heart",size:"16"},null,8,["icon"])],10,H),e(b)?e(w)&&e(k)===0?(s(),i("span",K,"پیشسفارش")):d("",!0):(s(),i("span",J,"ناموجود"))]),t("div",Q,[t("h3",R,u(e(v)),1),t("div",T,[t("span",W,u(e(y))+" گرم",1),e(m)!=null?(s(),i("span",X,"اجرت "+u(C.value)+"٪",1)):d("",!0)])])]),_(e(O),{visible:l.value,"onUpdate:visible":o[2]||(o[2]=c=>l.value=c),placement:"bottom",closable:!1,class:"rtl",height:"100vh",bodyStyle:{padding:0,position:"relative"}},{default:N(()=>[l.value?(s(),V(L,{id:e(a),key:e(a),onCloseDrawer:o[1]||(o[1]=c=>l.value=!1)},null,8,["id"])):d("",!0)]),_:1},8,["visible"])],64)}}}),ae=j(Y,[["__scopeId","data-v-5658bc04"]]);export{ae as P};
|
||||
import{d as D,l as P,i as s,c as i,F as S,b as t,u as e,w as z,k as _,I as f,j as B,e as I,f as d,t as u,q as N,v as V,g as q,h as E,_ as $}from"./index-CBZSLIgu.js";import{u as j,S as L}from"./SingleProduct-DssXZ7g8.js";import{u as M}from"./cartStore-CAFgxy6H.js";import{D as O}from"./index-CUVFR_Bx.js";const U={class:"pc__art"},A=["alt"],G={key:1,class:"pc__artEmpty","aria-hidden":"true"},H=["aria-label"],J={key:2,class:"pc__flag"},K={key:3,class:"pc__flag pc__flag--soft"},Q={class:"pc__body"},R={class:"pc__title"},T={class:"pc__meta"},W={class:"pc__weight"},X={key:0,class:"pc__wage"},Y=D({__name:"ProductCard",props:{value:{}},setup(g){const h=g,n=j(),{image:p,id:a,title:v,min_gram:y,wage:m,is_exist:b,qty:k,isPreOrder:w}=M(h.value),l=q(!1);function x(){l.value=!0}const C=E(()=>{const r=m.value;return r==null?"":r%1===0?String(r):r.toFixed(2)});return(r,o)=>{const F=P("lazy");return s(),i(S,null,[t("article",{class:"pc",onClick:x},[t("div",U,[e(p)?z((s(),i("img",{key:0,alt:e(v)},null,8,A)),[[F,e(p)]]):(s(),i("div",G,[_(f,{icon:"vuesax-linear:diamonds",size:"26"})])),t("button",{type:"button",class:I(["pc__fav",{"is-on":e(n).isFavorite(e(a))}]),"aria-label":e(n).isFavorite(e(a))?"حذف از علاقهمندی":"افزودن به علاقهمندی",onClick:o[0]||(o[0]=B(c=>e(n).toggle(e(a)),["stop"]))},[_(f,{icon:e(n).isFavorite(e(a))?"vuesax-linear:heart-fill":"vuesax-linear:heart",size:"16"},null,8,["icon"])],10,H),e(b)?e(w)&&e(k)===0?(s(),i("span",K,"پیشسفارش")):d("",!0):(s(),i("span",J,"ناموجود"))]),t("div",Q,[t("h3",R,u(e(v)),1),t("div",T,[t("span",W,u(e(y))+" گرم",1),e(m)!=null?(s(),i("span",X,"اجرت "+u(C.value)+"٪",1)):d("",!0)])])]),_(e(O),{visible:l.value,"onUpdate:visible":o[2]||(o[2]=c=>l.value=c),placement:"bottom",closable:!1,class:"rtl",height:"100vh",bodyStyle:{padding:0,position:"relative"}},{default:N(()=>[l.value?(s(),V(L,{id:e(a),key:e(a),onCloseDrawer:o[1]||(o[1]=c=>l.value=!1)},null,8,["id"])):d("",!0)]),_:1},8,["visible"])],64)}}}),ae=$(Y,[["__scopeId","data-v-5658bc04"]]);export{ae as P};
|
||||
@@ -1 +1 @@
|
||||
import{d as I,E as S,o as E,c as a,e as l,w as y,j as p,q as V,g as i,b as e,I as f,t,f as _,n as b,l as k,_ as C}from"./index-CXDP73fh.js";import{P as N}from"./PageBar-DGocjVpH.js";import{S as T}from"./SectionHead-BpYVXEql.js";import{a as g}from"./client-WIBW0tF5.js";const h={class:"prof"},B={class:"prof__identity"},P={class:"prof__avatar","aria-hidden":"true"},x={class:"prof__name"},D={key:0,class:"prof__mobile"},w={key:0,class:"prof__block"},R={class:"prof__specs"},F={class:"prof__spec"},z={class:"prof__spec"},M={class:"prof__spec"},O={class:"prof__spec"},U={key:1,class:"prof__empty"},A=I({__name:"ProfileView",setup(L){const c={UNVERIFIED:"احراز نشده",STEP_ONE_SUBMITTED:"مرحله اول ثبت شده",STEP_TWO_SUBMITTED:"مرحله دوم ثبت شده",VERIFIED:"تایید شده"},u=S(),s=p(null),n=p(!1),v=k(()=>{var r;return((r=s.value)==null?void 0:r.verificationStep)==="VERIFIED"});return E(async()=>{if(u.customerId){n.value=!0;try{s.value=await g.getCustomer(u.customerId)}finally{n.value=!1}}}),(r,o)=>{const m=V("container");return i(),a("div",h,[l(N,{title:"اطلاعات کاربری","back-to":"userPage::hasSubRoute"}),l(m,{class:"prof__body"},{default:y(()=>{var d;return[e("section",B,[e("div",P,[l(f,{icon:"vuesax-linear:user",size:"30"})]),e("h2",x,t(((d=s.value)==null?void 0:d.fullName)??"—"),1),s.value?(i(),a("p",D,t(s.value.mobile),1)):_("",!0),s.value?(i(),a("span",{key:1,class:b(["prof__seal",v.value?"is-ok":"is-pending"])},t(c[s.value.verificationStep]??s.value.verificationStep),3)):_("",!0)]),s.value?(i(),a("section",w,[l(T,{title:"مشخصات",subtitle:"اطلاعات ثبتشده حساب شما"}),e("dl",R,[e("div",F,[o[0]||(o[0]=e("dt",null,"نام و نام خانوادگی",-1)),e("dd",null,t(s.value.fullName),1)]),e("div",z,[o[1]||(o[1]=e("dt",null,"تلفن همراه",-1)),e("dd",null,t(s.value.mobile),1)]),e("div",M,[o[2]||(o[2]=e("dt",null,"کد ملی",-1)),e("dd",null,t(s.value.nationalCode),1)]),e("div",O,[o[3]||(o[3]=e("dt",null,"وضعیت احراز هویت",-1)),e("dd",null,t(c[s.value.verificationStep]??s.value.verificationStep),1)])])])):n.value?_("",!0):(i(),a("section",U,[l(f,{icon:"vuesax-linear:user",size:"26"}),o[4]||(o[4]=e("p",null,"اطلاعات حساب در دسترس نیست.",-1))]))]}),_:1})])}}}),G=C(A,[["__scopeId","data-v-7c544fc5"]]);export{G as default};
|
||||
import{d as I,E as S,o as E,c as a,k as i,q as y,g as p,s as k,i as l,b as e,I as f,t,f as _,e as V,h as b,_ as h}from"./index-CBZSLIgu.js";import{P as C}from"./PageBar-BrfQrql1.js";import{S as N}from"./SectionHead-QOHixgma.js";import{a as T}from"./client-CJxy5iKa.js";const g={class:"prof"},B={class:"prof__identity"},P={class:"prof__avatar","aria-hidden":"true"},x={class:"prof__name"},D={key:0,class:"prof__mobile"},R={key:0,class:"prof__block"},w={class:"prof__specs"},F={class:"prof__spec"},z={class:"prof__spec"},M={class:"prof__spec"},O={class:"prof__spec"},U={key:1,class:"prof__empty"},A=I({__name:"ProfileView",setup(L){const c={UNVERIFIED:"احراز نشده",STEP_ONE_SUBMITTED:"مرحله اول ثبت شده",STEP_TWO_SUBMITTED:"مرحله دوم ثبت شده",VERIFIED:"تایید شده"},u=S(),s=p(null),n=p(!1),v=b(()=>{var r;return((r=s.value)==null?void 0:r.verificationStep)==="VERIFIED"});return E(async()=>{if(u.customerId){n.value=!0;try{s.value=await T.getCustomer(u.customerId)}finally{n.value=!1}}}),(r,o)=>{const m=k("container");return l(),a("div",g,[i(C,{title:"اطلاعات کاربری","back-to":"userPage::hasSubRoute"}),i(m,{class:"prof__body"},{default:y(()=>{var d;return[e("section",B,[e("div",P,[i(f,{icon:"vuesax-linear:user",size:"30"})]),e("h2",x,t(((d=s.value)==null?void 0:d.fullName)??"—"),1),s.value?(l(),a("p",D,t(s.value.mobile),1)):_("",!0),s.value?(l(),a("span",{key:1,class:V(["prof__seal",v.value?"is-ok":"is-pending"])},t(c[s.value.verificationStep]??s.value.verificationStep),3)):_("",!0)]),s.value?(l(),a("section",R,[i(N,{title:"مشخصات",subtitle:"اطلاعات ثبتشده حساب شما"}),e("dl",w,[e("div",F,[o[0]||(o[0]=e("dt",null,"نام و نام خانوادگی",-1)),e("dd",null,t(s.value.fullName),1)]),e("div",z,[o[1]||(o[1]=e("dt",null,"تلفن همراه",-1)),e("dd",null,t(s.value.mobile),1)]),e("div",M,[o[2]||(o[2]=e("dt",null,"کد ملی",-1)),e("dd",null,t(s.value.nationalCode),1)]),e("div",O,[o[3]||(o[3]=e("dt",null,"وضعیت احراز هویت",-1)),e("dd",null,t(c[s.value.verificationStep]??s.value.verificationStep),1)])])])):n.value?_("",!0):(l(),a("section",U,[i(f,{icon:"vuesax-linear:user",size:"26"}),o[4]||(o[4]=e("p",null,"اطلاعات حساب در دسترس نیست.",-1))]))]}),_:1})])}}}),G=h(A,[["__scopeId","data-v-7c544fc5"]]);export{G as default};
|
||||
@@ -1 +1 @@
|
||||
import{p as c,j as d}from"./filters-CLL9nGZ2.js";import{d as l,c as s,n as u,b as a,i as _,t as i,u as n,f as m,l as v,g as r,_ as q}from"./index-CXDP73fh.js";const f={key:0,class:"rate-strip__value"},k={class:"rate-strip__num"},y={key:1,class:"rate-strip__value rate-strip__value--muted"},x={key:2,class:"rate-strip__time"},R=l({__name:"RateStrip",props:{quote:{}},setup(t){const o=t,p=v(()=>o.quote?o.quote.pricePerGramRial.split(".")[0]:"");return(b,e)=>(r(),s("div",{class:u(["rate-strip",{"is-empty":!t.quote}])},[e[1]||(e[1]=a("div",{class:"rate-strip__label"},[a("span",{class:"rate-strip__dot","aria-hidden":"true"}),_(" نرخ طلای ۱۸ عیار ")],-1)),t.quote?(r(),s("div",f,[a("span",k,i(n(c)(p.value)),1),e[0]||(e[0]=a("span",{class:"rate-strip__unit"},"ریال / گرم",-1))])):(r(),s("div",y,"در انتظار اعلام نرخ")),t.quote?(r(),s("div",x,i(n(d)(t.quote.quotedAt)),1)):m("",!0)],2))}}),N=q(R,[["__scopeId","data-v-16b4c8d5"]]);export{N as R};
|
||||
import{p as c,j as d}from"./filters-CLL9nGZ2.js";import{d as u,c as s,e as _,b as a,D as l,t as i,u as n,f as m,h as v,i as r,_ as q}from"./index-CBZSLIgu.js";const f={key:0,class:"rate-strip__value"},k={class:"rate-strip__num"},y={key:1,class:"rate-strip__value rate-strip__value--muted"},x={key:2,class:"rate-strip__time"},R=u({__name:"RateStrip",props:{quote:{}},setup(t){const o=t,p=v(()=>o.quote?o.quote.pricePerGramRial.split(".")[0]:"");return(b,e)=>(r(),s("div",{class:_(["rate-strip",{"is-empty":!t.quote}])},[e[1]||(e[1]=a("div",{class:"rate-strip__label"},[a("span",{class:"rate-strip__dot","aria-hidden":"true"}),l(" نرخ طلای ۱۸ عیار ")],-1)),t.quote?(r(),s("div",f,[a("span",k,i(n(c)(p.value)),1),e[0]||(e[0]=a("span",{class:"rate-strip__unit"},"ریال / گرم",-1))])):(r(),s("div",y,"در انتظار اعلام نرخ")),t.quote?(r(),s("div",x,i(n(d)(t.quote.quotedAt)),1)):m("",!0)],2))}}),C=q(R,[["__scopeId","data-v-16b4c8d5"]]);export{C as R};
|
||||
@@ -1 +0,0 @@
|
||||
import{d as v,a0 as h,G as m,g as t,c as l,e as n,I as r,h as f,a1 as b,b as x,a2 as k,f as C,C as I,j as y,_ as S}from"./index-CXDP73fh.js";const V={class:"si"},w=["placeholder"],B=v({__name:"SearchInput",props:{placeHolder:{}},emits:["search"],setup(i,{emit:d}){const c=d,a=I({clear:!1}),e=y(void 0),s=()=>{c("search",e.value)},u=()=>{e.value=void 0,a.clear=!1,c("search",void 0)},p=h(e,700);return m(p,()=>{e?a.clear=!0:a.clear=!1,s()}),(N,o)=>(t(),l("div",V,[n(r,{icon:"vuesax-linear:search-normal-1",class:"si__icon",size:"18",onClick:s}),f(x("input",{"onUpdate:modelValue":o[0]||(o[0]=_=>e.value=_),class:"si__input",placeholder:i.placeHolder,onKeyup:k(s,["enter"])},null,40,w),[[b,e.value]]),a.clear?(t(),l("button",{key:0,type:"button",class:"si__clear","aria-label":"پاک کردن",onClick:u},[n(r,{icon:"vuesax-linear:add",size:"16"})])):C("",!0)]))}}),D=S(B,[["__scopeId","data-v-4c50067a"]]);export{D as S};
|
||||
@@ -0,0 +1 @@
|
||||
import{d as v,a2 as h,G as m,i as t,c as l,k as n,I as r,w as f,a3 as b,b as k,a4 as x,f as I,B as w,g as y,_ as B}from"./index-CBZSLIgu.js";const C={class:"si"},S=["placeholder"],V=v({__name:"SearchInput",props:{placeHolder:{}},emits:["search"],setup(i,{emit:d}){const c=d,a=w({clear:!1}),e=y(void 0),s=()=>{c("search",e.value)},u=()=>{e.value=void 0,a.clear=!1,c("search",void 0)},p=h(e,700);return m(p,()=>{e?a.clear=!0:a.clear=!1,s()}),(N,o)=>(t(),l("div",C,[n(r,{icon:"vuesax-linear:search-normal-1",class:"si__icon",size:"18",onClick:s}),f(k("input",{"onUpdate:modelValue":o[0]||(o[0]=_=>e.value=_),class:"si__input",placeholder:i.placeHolder,onKeyup:x(s,["enter"])},null,40,S),[[b,e.value]]),a.clear?(t(),l("button",{key:0,type:"button",class:"si__clear","aria-label":"پاک کردن",onClick:u},[n(r,{icon:"vuesax-linear:add",size:"16"})])):I("",!0)]))}}),D=B(V,[["__scopeId","data-v-4c50067a"]]);export{D as S};
|
||||
@@ -1 +1 @@
|
||||
.dd[data-v-9e57de56]{padding:15px;border:1px solid #ECEDEF;border-radius:16px;background:#fff}.dd.is-open[data-v-9e57de56]{border-color:#a33c55}.dd__head[data-v-9e57de56]{display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%;border:0;padding:0;cursor:pointer;background:none;font-family:inherit;text-align:start}.dd__title[data-v-9e57de56]{margin:0;font-size:13px;font-weight:700;color:#14161a}.dd__chevron[data-v-9e57de56]{flex:0 0 auto;color:#8a909c;transition:transform .2s ease}.dd.is-open .dd__chevron[data-v-9e57de56]{transform:rotate(180deg);color:#7a1b33}.dd__body[data-v-9e57de56]{overflow:hidden;padding-top:15px}.drop-enter-active{animation:drop .2s cubic-bezier(.33,1,.68,1)}.drop-leave-to{animation:drop .2s cubic-bezier(.33,1,.68,1) reverse}@keyframes drop{0%{max-height:0}to{max-height:240px}}.filt[data-v-9651e9da]{background:#fff;min-height:100vh}.filt__body[data-v-9651e9da]{max-width:768px;margin:0 auto;padding:120px 16px 110px;display:flex;flex-direction:column;gap:12px}.filt__chips[data-v-9651e9da]{display:flex;flex-wrap:wrap;gap:8px}.filt__chip[data-v-9651e9da]{padding:8px 14px;border-radius:999px;cursor:pointer;font-family:inherit;font-size:12px;font-weight:600;color:#22262e;background:#fff;border:1px solid #ECEDEF;transition:all .2s ease}.filt__chip.is-on[data-v-9651e9da]{color:#fff;background:#14161a;border-color:#14161a}.filt__chip[data-v-9651e9da]:active{transform:scale(.97)}.filt__empty[data-v-9651e9da]{margin:0;font-size:12px;color:#8a909c}.filt__gram[data-v-9651e9da]{padding:4px 6px 0}.filt__gram[data-v-9651e9da] .ant-slider-track{background-color:#7a1b33}.filt__gram[data-v-9651e9da] .ant-slider-handle{border-color:#7a1b33}.filt__gram[data-v-9651e9da] .ant-slider-rail{background-color:#ecedef}.filt__gramFigures[data-v-9651e9da]{display:flex;align-items:center;justify-content:space-between;gap:10px;padding-top:18px}.filt__gramFigure[data-v-9651e9da]{font-size:12px;font-weight:700;color:#14161a}.filt__gramFigure i[data-v-9651e9da]{font-style:normal;font-size:11px;font-weight:600;color:#8a909c;padding-inline-end:4px}.filt__bar[data-v-9651e9da]{position:fixed;bottom:0;left:0;right:0;z-index:5;background:#ffffffeb;-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px);border-top:1px solid #ECEDEF;padding-bottom:16px}.filt__barInner[data-v-9651e9da]{max-width:768px;margin:0 auto;padding:12px 16px;display:flex;align-items:center;gap:10px}.filt__clear[data-v-9651e9da],.filt__apply[data-v-9651e9da]{flex:1;padding:13px 18px;border:0;cursor:pointer;border-radius:14px;font-family:inherit;font-size:13px;font-weight:700;transition:transform .15s ease}.filt__clear[data-v-9651e9da]{color:#22262e;background:#f4f5f6;border:1px solid #ECEDEF}.filt__apply[data-v-9651e9da]{color:#fff;background:#14161a}.filt__clear[data-v-9651e9da]:active,.filt__apply[data-v-9651e9da]:active{transform:scale(.98)}.search[data-v-64596986]{background:#fff;min-height:100vh}.search__barExtra[data-v-64596986]{max-width:768px;margin:0 auto;padding:0 16px}.search__chips[data-v-64596986]{display:flex;gap:4px;overflow-x:auto;margin:8px 0 8px -16px;scroll-snap-type:x proximity}.search__chips[data-v-64596986]:before,.search__chips[data-v-64596986]:after{content:"";flex:0 0 16px}.search__chip[data-v-64596986]{scroll-snap-align:start;flex:0 0 auto;display:inline-flex;align-items:center;gap:6px;padding:7px 14px;border-radius:999px;cursor:pointer;font-family:inherit;font-size:12.5px;font-weight:600;white-space:nowrap;color:#22262e;background:#fff;border:1px solid #ECEDEF;transition:all .2s ease}.search__chip.is-on[data-v-64596986]{color:#fff;background:#14161a;border-color:#14161a}.search__chip--filter.is-on[data-v-64596986]{background:#7a1b33;border-color:#7a1b33}.search__chip[data-v-64596986]:active{transform:scale(.97)}.search__body[data-v-64596986]{padding-top:160px;padding-bottom:90px}.search__body[data-v-64596986]>*:first-child{margin-top:16px}.search__collections[data-v-64596986]{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:9px;margin-bottom:22px}.search__collection[data-v-64596986]{display:inline-flex;align-items:center;justify-content:center;gap:8px;padding:13px 10px;border-radius:14px;cursor:pointer;font-family:inherit;font-size:12.5px;font-weight:700;color:#22262e;background:#f4f5f6;border:1px solid #ECEDEF;transition:all .2s ease}.search__collection[data-v-64596986]:active{transform:scale(.98)}.search__grid[data-v-64596986]{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:11px}.search__more[data-v-64596986]{display:flex;justify-content:center;padding:18px 0 4px}.search__empty[data-v-64596986]{margin-top:8px;padding:34px 20px;border:1px dashed #ECEDEF;border-radius:18px;display:flex;flex-direction:column;align-items:center;gap:10px;color:#8a909c}.search__empty p[data-v-64596986]{margin:0;font-size:12.5px}
|
||||
.dd[data-v-9e57de56]{padding:15px;border:1px solid #ECEDEF;border-radius:16px;background:#fff}.dd.is-open[data-v-9e57de56]{border-color:#a33c55}.dd__head[data-v-9e57de56]{display:flex;align-items:center;justify-content:space-between;gap:12px;width:100%;border:0;padding:0;cursor:pointer;background:none;font-family:inherit;text-align:start}.dd__title[data-v-9e57de56]{margin:0;font-size:13px;font-weight:700;color:#14161a}.dd__chevron[data-v-9e57de56]{flex:0 0 auto;color:#8a909c;transition:transform .2s ease}.dd.is-open .dd__chevron[data-v-9e57de56]{transform:rotate(180deg);color:#7a1b33}.dd__body[data-v-9e57de56]{overflow:hidden;padding-top:15px}.drop-enter-active{animation:drop .2s cubic-bezier(.33,1,.68,1)}.drop-leave-to{animation:drop .2s cubic-bezier(.33,1,.68,1) reverse}@keyframes drop{0%{max-height:0}to{max-height:240px}}.filt[data-v-9651e9da]{background:#fff;min-height:100vh}.filt__body[data-v-9651e9da]{max-width:768px;margin:0 auto;padding:120px 16px 110px;display:flex;flex-direction:column;gap:12px}.filt__chips[data-v-9651e9da]{display:flex;flex-wrap:wrap;gap:8px}.filt__chip[data-v-9651e9da]{padding:8px 14px;border-radius:999px;cursor:pointer;font-family:inherit;font-size:12px;font-weight:600;color:#22262e;background:#fff;border:1px solid #ECEDEF;transition:all .2s ease}.filt__chip.is-on[data-v-9651e9da]{color:#fff;background:#14161a;border-color:#14161a}.filt__chip[data-v-9651e9da]:active{transform:scale(.97)}.filt__empty[data-v-9651e9da]{margin:0;font-size:12px;color:#8a909c}.filt__gram[data-v-9651e9da]{padding:4px 6px 0}.filt__gram[data-v-9651e9da] .ant-slider-track{background-color:#7a1b33}.filt__gram[data-v-9651e9da] .ant-slider-handle{border-color:#7a1b33}.filt__gram[data-v-9651e9da] .ant-slider-rail{background-color:#ecedef}.filt__gramFigures[data-v-9651e9da]{display:flex;align-items:center;justify-content:space-between;gap:10px;padding-top:18px}.filt__gramFigure[data-v-9651e9da]{font-size:12px;font-weight:700;color:#14161a}.filt__gramFigure i[data-v-9651e9da]{font-style:normal;font-size:11px;font-weight:600;color:#8a909c;padding-inline-end:4px}.filt__bar[data-v-9651e9da]{position:fixed;bottom:0;left:0;right:0;z-index:5;background:#ffffffeb;-webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px);border-top:1px solid #ECEDEF;padding-bottom:16px}.filt__barInner[data-v-9651e9da]{max-width:768px;margin:0 auto;padding:12px 16px;display:flex;align-items:center;gap:10px}.filt__clear[data-v-9651e9da],.filt__apply[data-v-9651e9da]{flex:1;padding:13px 18px;border:0;cursor:pointer;border-radius:14px;font-family:inherit;font-size:13px;font-weight:700;transition:transform .15s ease}.filt__clear[data-v-9651e9da]{color:#22262e;background:#f4f5f6;border:1px solid #ECEDEF}.filt__apply[data-v-9651e9da]{color:#fff;background:#14161a}.filt__clear[data-v-9651e9da]:active,.filt__apply[data-v-9651e9da]:active{transform:scale(.98)}.search[data-v-0d6eddd7]{background:#fff;min-height:100vh}.search__barExtra[data-v-0d6eddd7]{max-width:768px;margin:0 auto;padding:0 16px}.search__chips[data-v-0d6eddd7]{display:flex;gap:4px;overflow-x:auto;margin:8px 0 8px -16px;scroll-snap-type:x proximity}.search__chips[data-v-0d6eddd7]:before,.search__chips[data-v-0d6eddd7]:after{content:"";flex:0 0 16px}.search__chip[data-v-0d6eddd7]{scroll-snap-align:start;flex:0 0 auto;display:inline-flex;align-items:center;gap:6px;padding:7px 14px;border-radius:999px;cursor:pointer;font-family:inherit;font-size:12.5px;font-weight:600;white-space:nowrap;color:#22262e;background:#fff;border:1px solid #ECEDEF;transition:all .2s ease}.search__chip.is-on[data-v-0d6eddd7]{color:#fff;background:#14161a;border-color:#14161a}.search__chip--filter.is-on[data-v-0d6eddd7]{background:#7a1b33;border-color:#7a1b33}.search__chip[data-v-0d6eddd7]:active{transform:scale(.97)}.search__body[data-v-0d6eddd7]{padding-top:160px;padding-bottom:90px}.search__body[data-v-0d6eddd7]>*:first-child{margin-top:16px}.search__collections[data-v-0d6eddd7]{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:9px;margin-bottom:22px}.search__collection[data-v-0d6eddd7]{display:inline-flex;align-items:center;justify-content:center;gap:8px;padding:13px 10px;border-radius:14px;cursor:pointer;font-family:inherit;font-size:12.5px;font-weight:700;color:#22262e;background:#f4f5f6;border:1px solid #ECEDEF;transition:all .2s ease}.search__collection[data-v-0d6eddd7]:active{transform:scale(.98)}.search__grid[data-v-0d6eddd7]{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:11px}.search__more[data-v-0d6eddd7]{display:flex;justify-content:center;padding:18px 0 4px}.search__empty[data-v-0d6eddd7]{margin-top:8px;padding:34px 20px;border:1px dashed #ECEDEF;border-radius:18px;display:flex;flex-direction:column;align-items:center;gap:10px;color:#8a909c}.search__empty p[data-v-0d6eddd7]{margin:0;font-size:12.5px}
|
||||
@@ -1 +0,0 @@
|
||||
import{d as r,c as t,b as o,t as s,f as n,i as _,e as d,I as u,g as a,_ as b}from"./index-CXDP73fh.js";const m={class:"sec"},f={class:"sec__text"},x={class:"sec__title"},h={key:0,class:"sec__sub"},k=r({__name:"SectionHead",props:{title:{},subtitle:{},actionLabel:{}},emits:["action"],setup(e,{emit:i}){const l=i;return(p,c)=>(a(),t("header",m,[o("div",f,[o("h2",x,s(e.title),1),e.subtitle?(a(),t("p",h,s(e.subtitle),1)):n("",!0)]),e.actionLabel?(a(),t("button",{key:0,type:"button",class:"sec__action",onClick:c[0]||(c[0]=v=>l("action"))},[_(s(e.actionLabel)+" ",1),d(u,{icon:"vuesax-linear:arrow-left-2",size:"15"})])):n("",!0)]))}}),N=b(k,[["__scopeId","data-v-e5c65b7c"]]);export{N as S};
|
||||
@@ -0,0 +1 @@
|
||||
import{d as r,c as t,b as o,t as s,f as n,D as _,k as d,I as u,i as a,_ as b}from"./index-CBZSLIgu.js";const m={class:"sec"},f={class:"sec__text"},k={class:"sec__title"},x={key:0,class:"sec__sub"},h=r({__name:"SectionHead",props:{title:{},subtitle:{},actionLabel:{}},emits:["action"],setup(e,{emit:i}){const l=i;return(p,c)=>(a(),t("header",m,[o("div",f,[o("h2",k,s(e.title),1),e.subtitle?(a(),t("p",x,s(e.subtitle),1)):n("",!0)]),e.actionLabel?(a(),t("button",{key:0,type:"button",class:"sec__action",onClick:c[0]||(c[0]=v=>l("action"))},[_(s(e.actionLabel)+" ",1),d(u,{icon:"vuesax-linear:arrow-left-2",size:"15"})])):n("",!0)]))}}),N=b(h,[["__scopeId","data-v-e5c65b7c"]]);export{N as S};
|
||||
@@ -1 +1 @@
|
||||
import{d as k,E as A,o as C,c as i,e as r,w as f,s as w,u as m,f as E,j as u,g as l,F as R,b as s,r as D,I as T,t as o,n as p,q as N,_ as L}from"./index-CXDP73fh.js";import{P}from"./PageBar-DGocjVpH.js";import{a as M}from"./Spinner.vue_vue_type_style_index_0_lang-Cw01MXb5.js";import{S as O}from"./SectionHead-BpYVXEql.js";import{j as g,p as B}from"./filters-CLL9nGZ2.js";import{a as I}from"./client-WIBW0tF5.js";import{D as U}from"./index-BYO8JY_f.js";import"./_commonjsHelpers-Cpj98o6Y.js";import"./classCallCheck-BI0ic3HN.js";const V={class:"tx"},$={class:"tx__list"},Y=["onClick"],j={class:"tx__main"},z={class:"tx__head"},F={class:"tx__kind"},G={key:0,class:"tx__note"},H={class:"tx__foot"},q={class:"tx__date"},J={key:1,class:"tx__empty"},K={class:"sheet"},Q={class:"sheet__specs"},W={class:"sheet__spec"},X={class:"sheet__spec"},Z={class:"sheet__spec"},ss={class:"sheet__spec"},es={class:"sheet__spec"},ts=k({__name:"TransactionsView",setup(os){const x={ORDER_STOCK:"سفارش",ORDER_PRE_ORDER:"سفارش ساخت",REPAYMENT_MELTED_GOLD:"طلای آبشده",REPAYMENT_TRANSFER:"حواله",REPAYMENT_CHEQUE:"چک",REPAYMENT_CASH:"پولی",WALLET_BONUS:"پاداش",ADJUSTMENT:"اصلاحیه"},h=A(),_=u(!1),d=u([]),v=u(!1),n=u(null),c=a=>a.direction==="CREDIT",y=a=>a.currency==="GOLD_GRAM"?`${a.amount} گرم`:`${B(a.amount)} ریال`,S=a=>{n.value=a,v.value=!0};return C(async()=>{if(h.customerId){_.value=!0;try{d.value=await I.getLedgerEntries(h.customerId)}finally{_.value=!1}}}),(a,e)=>{const b=N("container");return l(),i("div",V,[r(P,{title:"اسناد","back-to":"userPage::hasSubRoute"}),r(M,{spinning:_.value,tip:"درحال دریافت اطلاعات"},{default:f(()=>[r(b,{class:"tx__body"},{default:f(()=>[d.value.length?(l(),i(R,{key:0},[r(O,{title:"گردش حساب",subtitle:`${d.value.length} سند`},null,8,["subtitle"]),s("div",$,[(l(!0),i(R,null,D(d.value,t=>(l(),i("article",{key:t.id,class:"tx__row",onClick:ns=>S(t)},[s("span",{class:p(["tx__mark",c(t)?"is-in":"is-out"]),"aria-hidden":"true"},[r(T,{icon:c(t)?"vuesax-linear:arrow-down":"vuesax-linear:arrow-up",size:"16"},null,8,["icon"])],2),s("div",j,[s("div",z,[s("h3",F,o(x[t.sourceType]??t.sourceType),1),s("span",{class:p(["tx__amount",c(t)?"is-in":"is-out"])},o(y(t)),3)]),t.note?(l(),i("p",G,o(t.note),1)):E("",!0),s("div",H,[s("span",q,o(m(g)(t.createdAt)),1),s("span",{class:p(["tx__state",{"is-pending":!t.confirmed}])},o(t.confirmed?"تایید شده":"در انتظار تایید"),3)])])],8,Y))),128))])],64)):_.value?E("",!0):(l(),i("section",J,[r(T,{icon:"vuesax-linear:receipt-text",size:"26"}),e[1]||(e[1]=s("p",null,"سندی برای حساب شما ثبت نشده است.",-1))]))]),_:1})]),_:1},8,["spinning"]),n.value?(l(),w(m(U),{key:0,visible:v.value,"onUpdate:visible":e[0]||(e[0]=t=>v.value=t),placement:"bottom",closable:!1,height:"420px",class:"rtl",bodyStyle:{padding:0}},{default:f(()=>[s("div",K,[e[7]||(e[7]=s("h3",{class:"sheet__title"},"جزئیات سند",-1)),s("dl",Q,[s("div",W,[e[2]||(e[2]=s("dt",null,"نوع سند",-1)),s("dd",null,o(x[n.value.sourceType]??n.value.sourceType),1)]),s("div",X,[e[3]||(e[3]=s("dt",null,"مبلغ",-1)),s("dd",{class:p(c(n.value)?"is-in":"is-out")},o(y(n.value)),3)]),s("div",Z,[e[4]||(e[4]=s("dt",null,"وضعیت",-1)),s("dd",null,o(n.value.confirmed?"تایید شده":"در انتظار تایید"),1)]),s("div",ss,[e[5]||(e[5]=s("dt",null,"تاریخ",-1)),s("dd",null,o(m(g)(n.value.createdAt)),1)]),s("div",es,[e[6]||(e[6]=s("dt",null,"توضیحات",-1)),s("dd",null,o(n.value.note||"—"),1)])])])]),_:1},8,["visible"])):E("",!0)])}}}),vs=L(ts,[["__scopeId","data-v-83b037a9"]]);export{vs as default};
|
||||
import{d as b,E as A,o as C,c as i,k as r,q as f,v as D,u as m,f as E,g as u,i as l,F as R,b as s,r as w,I as T,t as o,e as p,s as N,_ as L}from"./index-CBZSLIgu.js";import{P}from"./PageBar-BrfQrql1.js";import{a as M}from"./Spinner.vue_vue_type_style_index_0_lang-DQwH-Xdy.js";import{S as O}from"./SectionHead-QOHixgma.js";import{j as g,p as B}from"./filters-CLL9nGZ2.js";import{a as I}from"./client-CJxy5iKa.js";import{D as U}from"./index-CUVFR_Bx.js";import"./_commonjsHelpers-Cpj98o6Y.js";import"./classCallCheck-R2Fkn3Yh.js";const V={class:"tx"},$={class:"tx__list"},Y=["onClick"],z={class:"tx__main"},F={class:"tx__head"},G={class:"tx__kind"},H={key:0,class:"tx__note"},j={class:"tx__foot"},q={class:"tx__date"},J={key:1,class:"tx__empty"},K={class:"sheet"},Q={class:"sheet__specs"},W={class:"sheet__spec"},X={class:"sheet__spec"},Z={class:"sheet__spec"},ss={class:"sheet__spec"},es={class:"sheet__spec"},ts=b({__name:"TransactionsView",setup(os){const x={ORDER_STOCK:"سفارش",ORDER_PRE_ORDER:"سفارش ساخت",REPAYMENT_MELTED_GOLD:"طلای آبشده",REPAYMENT_TRANSFER:"حواله",REPAYMENT_CHEQUE:"چک",REPAYMENT_CASH:"پولی",WALLET_BONUS:"پاداش",ADJUSTMENT:"اصلاحیه"},h=A(),_=u(!1),d=u([]),v=u(!1),n=u(null),c=a=>a.direction==="CREDIT",y=a=>a.currency==="GOLD_GRAM"?`${a.amount} گرم`:`${B(a.amount)} ریال`,k=a=>{n.value=a,v.value=!0};return C(async()=>{if(h.customerId){_.value=!0;try{d.value=await I.getLedgerEntries(h.customerId)}finally{_.value=!1}}}),(a,e)=>{const S=N("container");return l(),i("div",V,[r(P,{title:"اسناد","back-to":"userPage::hasSubRoute"}),r(M,{spinning:_.value,tip:"درحال دریافت اطلاعات"},{default:f(()=>[r(S,{class:"tx__body"},{default:f(()=>[d.value.length?(l(),i(R,{key:0},[r(O,{title:"گردش حساب",subtitle:`${d.value.length} سند`},null,8,["subtitle"]),s("div",$,[(l(!0),i(R,null,w(d.value,t=>(l(),i("article",{key:t.id,class:"tx__row",onClick:ns=>k(t)},[s("span",{class:p(["tx__mark",c(t)?"is-in":"is-out"]),"aria-hidden":"true"},[r(T,{icon:c(t)?"vuesax-linear:arrow-down":"vuesax-linear:arrow-up",size:"16"},null,8,["icon"])],2),s("div",z,[s("div",F,[s("h3",G,o(x[t.sourceType]??t.sourceType),1),s("span",{class:p(["tx__amount",c(t)?"is-in":"is-out"])},o(y(t)),3)]),t.note?(l(),i("p",H,o(t.note),1)):E("",!0),s("div",j,[s("span",q,o(m(g)(t.createdAt)),1),s("span",{class:p(["tx__state",{"is-pending":!t.confirmed}])},o(t.confirmed?"تایید شده":"در انتظار تایید"),3)])])],8,Y))),128))])],64)):_.value?E("",!0):(l(),i("section",J,[r(T,{icon:"vuesax-linear:receipt-text",size:"26"}),e[1]||(e[1]=s("p",null,"سندی برای حساب شما ثبت نشده است.",-1))]))]),_:1})]),_:1},8,["spinning"]),n.value?(l(),D(m(U),{key:0,visible:v.value,"onUpdate:visible":e[0]||(e[0]=t=>v.value=t),placement:"bottom",closable:!1,height:"420px",class:"rtl",bodyStyle:{padding:0}},{default:f(()=>[s("div",K,[e[7]||(e[7]=s("h3",{class:"sheet__title"},"جزئیات سند",-1)),s("dl",Q,[s("div",W,[e[2]||(e[2]=s("dt",null,"نوع سند",-1)),s("dd",null,o(x[n.value.sourceType]??n.value.sourceType),1)]),s("div",X,[e[3]||(e[3]=s("dt",null,"مبلغ",-1)),s("dd",{class:p(c(n.value)?"is-in":"is-out")},o(y(n.value)),3)]),s("div",Z,[e[4]||(e[4]=s("dt",null,"وضعیت",-1)),s("dd",null,o(n.value.confirmed?"تایید شده":"در انتظار تایید"),1)]),s("div",ss,[e[5]||(e[5]=s("dt",null,"تاریخ",-1)),s("dd",null,o(m(g)(n.value.createdAt)),1)]),s("div",es,[e[6]||(e[6]=s("dt",null,"توضیحات",-1)),s("dd",null,o(n.value.note||"—"),1)])])])]),_:1},8,["visible"])):E("",!0)])}}}),vs=L(ts,[["__scopeId","data-v-83b037a9"]]);export{vs as default};
|
||||
@@ -1 +1 @@
|
||||
import{j as d,u as F,l as i,p as S}from"./index-CXDP73fh.js";import{a as P}from"./client-WIBW0tF5.js";function W(t){return!t||t.length===0?null:t.reduce((e,s)=>parseFloat(s.defaultWagePercent)<parseFloat(e.defaultWagePercent)?s:e)}function I(t,e){const s=d(t),r=i(()=>{var n,p;return(p=(n=s.value.images)==null?void 0:n[0])==null?void 0:p.url}),o=i(()=>s.value.id),u=i(()=>s.value.title),l=i(()=>s.value.variants),a=d(e??W(F(l))),v=i(()=>a.value?parseFloat(a.value.rashWeightGrams):0),g=i(()=>a.value?parseFloat(a.value.defaultWagePercent):null),x=d(null),f=i(()=>{var n;return((n=a.value)==null?void 0:n.qty)??0}),h=i(()=>{var n;return((n=a.value)==null?void 0:n.isPreOrder)??!1}),y=i(()=>f.value>0||h.value),_=i(()=>s.value.productCode??null),C=d(!1);return{product:s,image:r,id:o,title:u,productCode:_,variants:l,selectedVariant:a,min_gram:v,wage:g,hasDiscountWage:x,qty:f,isPreOrder:h,is_exist:y,has_warranty:C}}const m="appCart";function q(){try{const t=localStorage.getItem(m);return t?JSON.parse(t):[]}catch{return[]}}function c(t){localStorage.setItem(m,JSON.stringify(t))}const G=S("cart",{state:()=>({items:q()}),actions:{async init(){try{const t=await P.listProducts(),e=[];for(const s of this.items){const r=t.find(u=>u.id===s.pid),o=r==null?void 0:r.variants.find(u=>u.id===s.vid);r&&o&&e.push({...s,product:r})}this.items=e,c(this.items)}catch(t){console.log(t)}},addToCart(t){const e=this.find_index(t);e===-1?this.items.push(t):(this.items[e].product=t.product,this.items[e].qty=t.qty),c(this.items)},remove(t){const e=this.find_index(t);e>-1&&this.items.splice(e,1),c(this.items)},updateFeild(t,e,s,r){if(!this.isInCart(s))return!1;const o=e??this.find_index(s);return this.items[o][r]=t,c(this.items),!0},find_index(t){return this.items.findIndex(e=>e.pid===t.pid&&e.vid===t.vid)},isInCart(t){return this.find_index(t)>-1},variant(t){return t.product.variants.find(e=>e.id===t.vid)},total(){return this.items.reduce((t,e)=>{const s=this.variant(e);return t+(s?parseFloat(s.rashWeightGrams)*e.qty:0)},0).toFixed(2)},clear(){this.items=[],c(this.items)}}});export{G as a,I as u};
|
||||
import{g as d,u as F,h as i,m as S}from"./index-CBZSLIgu.js";import{a as P}from"./client-CJxy5iKa.js";function W(t){return!t||t.length===0?null:t.reduce((e,s)=>parseFloat(s.defaultWagePercent)<parseFloat(e.defaultWagePercent)?s:e)}function I(t,e){const s=d(t),r=i(()=>{var n,m;return(m=(n=s.value.images)==null?void 0:n[0])==null?void 0:m.url}),o=i(()=>s.value.id),u=i(()=>s.value.title),l=i(()=>s.value.variants),a=d(e??W(F(l))),v=i(()=>a.value?parseFloat(a.value.rashWeightGrams):0),g=i(()=>a.value?parseFloat(a.value.defaultWagePercent):null),x=d(null),h=i(()=>{var n;return((n=a.value)==null?void 0:n.qty)??0}),f=i(()=>{var n;return((n=a.value)==null?void 0:n.isPreOrder)??!1}),y=i(()=>h.value>0||f.value),_=i(()=>s.value.productCode??null),C=d(!1);return{product:s,image:r,id:o,title:u,productCode:_,variants:l,selectedVariant:a,min_gram:v,wage:g,hasDiscountWage:x,qty:h,isPreOrder:f,is_exist:y,has_warranty:C}}const p="appCart";function q(){try{const t=localStorage.getItem(p);return t?JSON.parse(t):[]}catch{return[]}}function c(t){localStorage.setItem(p,JSON.stringify(t))}const G=S("cart",{state:()=>({items:q()}),actions:{async init(){try{const t=await P.listProducts(),e=[];for(const s of this.items){const r=t.find(u=>u.id===s.pid),o=r==null?void 0:r.variants.find(u=>u.id===s.vid);r&&o&&e.push({...s,product:r})}this.items=e,c(this.items)}catch(t){console.log(t)}},addToCart(t){const e=this.find_index(t);e===-1?this.items.push(t):(this.items[e].product=t.product,this.items[e].qty=t.qty),c(this.items)},remove(t){const e=this.find_index(t);e>-1&&this.items.splice(e,1),c(this.items)},updateFeild(t,e,s,r){if(!this.isInCart(s))return!1;const o=e??this.find_index(s);return this.items[o][r]=t,c(this.items),!0},find_index(t){return this.items.findIndex(e=>e.pid===t.pid&&e.vid===t.vid)},isInCart(t){return this.find_index(t)>-1},variant(t){return t.product.variants.find(e=>e.id===t.vid)},total(){return this.items.reduce((t,e)=>{const s=this.variant(e);return t+(s?parseFloat(s.rashWeightGrams)*e.qty:0)},0).toFixed(2)},clear(){this.items=[],c(this.items)}}});export{G as a,I as u};
|
||||
@@ -1 +1 @@
|
||||
import{c as l}from"./Spinner.vue_vue_type_style_index_0_lang-Cw01MXb5.js";var f=function(n){return setTimeout(n,16)},c=function(n){return clearTimeout(n)};typeof window<"u"&&"requestAnimationFrame"in window&&(f=function(n){return window.requestAnimationFrame(n)},c=function(n){return window.cancelAnimationFrame(n)});var i=0,o=new Map;function u(e){o.delete(e)}function w(e){var n=arguments.length>1&&arguments[1]!==void 0?arguments[1]:1;i+=1;var r=i;function a(t){if(t===0)u(r),e();else{var s=f(function(){a(t-1)});o.set(r,s)}}return a(n),r}w.cancel=function(e){var n=o.get(e);return u(n),c(n)};function m(e,n){for(var r=l({},e),a=0;a<n.length;a+=1){var t=n[a];delete r[t]}return r}function p(e,n,r){return Object.defineProperty(e,"prototype",{writable:!1}),e}function v(e,n){if(!(e instanceof n))throw new TypeError("Cannot call a class as a function")}export{p as _,v as a,m as o,w};
|
||||
import{c as l}from"./Spinner.vue_vue_type_style_index_0_lang-DQwH-Xdy.js";var f=function(n){return setTimeout(n,16)},c=function(n){return clearTimeout(n)};typeof window<"u"&&"requestAnimationFrame"in window&&(f=function(n){return window.requestAnimationFrame(n)},c=function(n){return window.cancelAnimationFrame(n)});var i=0,o=new Map;function u(e){o.delete(e)}function w(e){var n=arguments.length>1&&arguments[1]!==void 0?arguments[1]:1;i+=1;var r=i;function a(t){if(t===0)u(r),e();else{var s=f(function(){a(t-1)});o.set(r,s)}}return a(n),r}w.cancel=function(e){var n=o.get(e);return u(n),c(n)};function m(e,n){for(var r=l({},e),a=0;a<n.length;a+=1){var t=n[a];delete r[t]}return r}function p(e,n,r){return Object.defineProperty(e,"prototype",{writable:!1}),e}function v(e,n){if(!(e instanceof n))throw new TypeError("Cannot call a class as a function")}export{p as _,v as a,m as o,w};
|
||||
@@ -0,0 +1 @@
|
||||
const a="/api";async function t(e,s){const r=await fetch(`${a}${e}`,{headers:{"Content-Type":"application/json"},...s});if(!r.ok){const o=await r.text();throw new Error(`API ${e} failed (${r.status}): ${o}`)}return r.json()}const c={listProducts:()=>t("/catalog/products"),listCustomers:()=>t("/customers"),getCustomer:e=>t(`/customers/${e}`),getBalance:e=>t(`/accounting/customers/${e}/balance`),getLedgerEntries:e=>t(`/customers/${e}/ledger-entries`),createOrder:e=>t("/orders",{method:"POST",body:JSON.stringify(e)}),getCustomerOrders:e=>t(`/customers/${e}/orders`),getLatestGoldPrice:(e=750)=>t(`/pricing/latest?karatPerMille=${e}`),getHomeSlider:()=>t("/home/slider")};export{c as a};
|
||||
@@ -1 +0,0 @@
|
||||
const a="/api";async function t(e,r){const s=await fetch(`${a}${e}`,{headers:{"Content-Type":"application/json"},...r});if(!s.ok){const o=await s.text();throw new Error(`API ${e} failed (${s.status}): ${o}`)}return s.json()}const c={listProducts:()=>t("/catalog/products"),listCustomers:()=>t("/customers"),getCustomer:e=>t(`/customers/${e}`),getBalance:e=>t(`/accounting/customers/${e}/balance`),getLedgerEntries:e=>t(`/customers/${e}/ledger-entries`),createOrder:e=>t("/orders",{method:"POST",body:JSON.stringify(e)}),getCustomerOrders:e=>t(`/customers/${e}/orders`),getLatestGoldPrice:(e=750)=>t(`/pricing/latest?karatPerMille=${e}`)};export{c as a};
|
||||
@@ -0,0 +1 @@
|
||||
const s="/assets/dialogEmpty1-DRc_s2oB.svg";export{s as l};
|
||||
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
@@ -1 +0,0 @@
|
||||
const s="/assets/dialogEmpty1-B2m57R6G.svg";export{s as l};
|
||||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
@@ -1 +1 @@
|
||||
import{p as e}from"./index-CXDP73fh.js";const t={colors:[],brands:[],ciny_id:[],limit_percent:[],limit_gram:[],has_discount:!1,text:void 0,cats:void 0,page:0},i=e("filters",{state:()=>({query:{...t}}),actions:{reset(){this.query={...t}}}});export{i as u};
|
||||
import{m as e}from"./index-CBZSLIgu.js";const t={colors:[],brands:[],ciny_id:[],limit_percent:[],limit_gram:[],has_discount:!1,text:void 0,cats:void 0,page:0},i=e("filters",{state:()=>({query:{...t}}),actions:{reset(){this.query={...t}}}});export{i as u};
|
||||
@@ -0,0 +1 @@
|
||||
import{a5 as y,d as L,a6 as $,a7 as w,G as A,a8 as B,k as l,g as D,B as M,L as N,a9 as p,M as G,aa as f,ab as I,ac as R,ad as U}from"./index-CBZSLIgu.js";const[Y,r,C]=y("pull-refresh"),x=50,F=["pulling","loosing","success"],X={disabled:Boolean,modelValue:Boolean,headHeight:f(x),successText:String,pullingText:String,loosingText:String,loadingText:String,pullDistance:$,successDuration:f(500),animationDuration:f(300)};var j=L({name:Y,props:X,emits:["change","refresh","update:modelValue"],setup(s,{emit:d,slots:o}){let u;const g=D(),T=D(),H=w(g),e=M({status:"normal",distance:0,duration:0}),i=R(),P=()=>{if(s.headHeight!==x)return{height:`${s.headHeight}px`}},h=()=>e.status!=="loading"&&e.status!=="success"&&!s.disabled,k=t=>{const a=+(s.pullDistance||s.headHeight);return t>a&&(t<a*2?t=a+(t-a)/2:t=a*1.5+(t-a*2)/4),Math.round(t)},n=(t,a)=>{const c=+(s.pullDistance||s.headHeight);e.distance=t,a?e.status="loading":t===0?e.status="normal":t<c?e.status="pulling":e.status="loosing",d("change",{status:e.status,distance:t})},m=()=>{const{status:t}=e;return t==="normal"?"":s[`${t}Text`]||C(t)},E=()=>{const{status:t,distance:a}=e;if(o[t])return o[t]({distance:a});const c=[];return F.includes(t)&&c.push(l("div",{class:r("text")},[m()])),t==="loading"&&c.push(l(N,{class:r("loading")},{default:m})),c},V=()=>{e.status="success",setTimeout(()=>{n(0)},+s.successDuration)},v=t=>{u=I(H.value)===0,u&&(e.duration=0,i.start(t))},b=t=>{h()&&v(t)},_=t=>{if(h()){u||v(t);const{deltaY:a}=i;i.move(t),u&&a.value>=0&&i.isVertical()&&(p(t),n(k(a.value)))}},S=()=>{u&&i.deltaY.value&&h()&&(e.duration=+s.animationDuration,e.status==="loosing"?(n(+s.headHeight,!0),d("update:modelValue",!0),G(()=>d("refresh"))):n(0))};return A(()=>s.modelValue,t=>{e.duration=+s.animationDuration,t?n(+s.headHeight,!0):o.success||s.successText?V():n(0,!1)}),B("touchmove",_,{target:T}),()=>{var t;const a={transitionDuration:`${e.duration}ms`,transform:e.distance?`translate3d(0,${e.distance}px, 0)`:""};return l("div",{ref:g,class:r()},[l("div",{ref:T,class:r("track"),style:a,onTouchstartPassive:b,onTouchend:S,onTouchcancel:S},[l("div",{class:r("head"),style:P()},[E()]),(t=o.default)==null?void 0:t.call(o)])])}}});const z=U(j);export{z as P};
|
||||
@@ -1 +0,0 @@
|
||||
import{a3 as y,d as L,a4 as $,a5 as w,G as A,a6 as N,e as c,j as D,C as p,L as B,a7 as C,K as G,a8 as f,a9 as I,aa as M,ab as R}from"./index-CXDP73fh.js";const[U,r,Y]=y("pull-refresh"),x=50,j=["pulling","loosing","success"],F={disabled:Boolean,modelValue:Boolean,headHeight:f(x),successText:String,pullingText:String,loosingText:String,loadingText:String,pullDistance:$,successDuration:f(500),animationDuration:f(300)};var K=L({name:U,props:F,emits:["change","refresh","update:modelValue"],setup(s,{emit:d,slots:o}){let u;const g=D(),T=D(),H=w(g),e=p({status:"normal",distance:0,duration:0}),i=M(),P=()=>{if(s.headHeight!==x)return{height:`${s.headHeight}px`}},h=()=>e.status!=="loading"&&e.status!=="success"&&!s.disabled,k=t=>{const a=+(s.pullDistance||s.headHeight);return t>a&&(t<a*2?t=a+(t-a)/2:t=a*1.5+(t-a*2)/4),Math.round(t)},n=(t,a)=>{const l=+(s.pullDistance||s.headHeight);e.distance=t,a?e.status="loading":t===0?e.status="normal":t<l?e.status="pulling":e.status="loosing",d("change",{status:e.status,distance:t})},m=()=>{const{status:t}=e;return t==="normal"?"":s[`${t}Text`]||Y(t)},E=()=>{const{status:t,distance:a}=e;if(o[t])return o[t]({distance:a});const l=[];return j.includes(t)&&l.push(c("div",{class:r("text")},[m()])),t==="loading"&&l.push(c(B,{class:r("loading")},{default:m})),l},V=()=>{e.status="success",setTimeout(()=>{n(0)},+s.successDuration)},v=t=>{u=I(H.value)===0,u&&(e.duration=0,i.start(t))},b=t=>{h()&&v(t)},_=t=>{if(h()){u||v(t);const{deltaY:a}=i;i.move(t),u&&a.value>=0&&i.isVertical()&&(C(t),n(k(a.value)))}},S=()=>{u&&i.deltaY.value&&h()&&(e.duration=+s.animationDuration,e.status==="loosing"?(n(+s.headHeight,!0),d("update:modelValue",!0),G(()=>d("refresh"))):n(0))};return A(()=>s.modelValue,t=>{e.duration=+s.animationDuration,t?n(+s.headHeight,!0):o.success||s.successText?V():n(0,!1)}),N("touchmove",_,{target:T}),()=>{var t;const a={transitionDuration:`${e.duration}ms`,transform:e.distance?`translate3d(0,${e.distance}px, 0)`:""};return c("div",{ref:g,class:r()},[c("div",{ref:T,class:r("track"),style:a,onTouchstartPassive:b,onTouchend:S,onTouchcancel:S},[c("div",{class:r("head"),style:P()},[E()]),(t=o.default)==null?void 0:t.call(o)])])}}});const q=R(K);export{q as P};
|
||||
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
@@ -9,10 +9,10 @@
|
||||
<meta name="background-color" content="#1C2D56" />
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport" />
|
||||
<title>طلای منتزر</title>
|
||||
<script type="module" crossorigin src="/assets/index-CXDP73fh.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-BaaK6U_u.css">
|
||||
<script type="module" crossorigin src="/assets/index-CBZSLIgu.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-CpQh57KC.css">
|
||||
<link rel="manifest" href="/manifest.webmanifest"></head>
|
||||
<body style="background-color: #1C2D56">
|
||||
<body style="background-color: #fff">
|
||||
<div class="bg-white w-screen h-screen fixed"></div>
|
||||
<div id="app"></div>␍
|
||||
</body>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport" />
|
||||
<title>طلای منتزر</title>
|
||||
</head>
|
||||
<body style="background-color: #1C2D56">
|
||||
<body style="background-color: #fff">
|
||||
<div class="bg-white w-screen h-screen fixed"></div>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
|
||||
@@ -14,6 +14,13 @@ async function request<T>(path: string, options?: RequestInit): Promise<T> {
|
||||
return res.json();
|
||||
}
|
||||
|
||||
export interface ProductImageSummary {
|
||||
id: string;
|
||||
url: string;
|
||||
sortOrder: number;
|
||||
role: 'FEATURED' | 'FEATURED_MOBILE' | 'GALLERY' | 'GALLERY_MOBILE';
|
||||
}
|
||||
|
||||
export interface ProductVariantSummary {
|
||||
id: string;
|
||||
title: string;
|
||||
@@ -25,6 +32,12 @@ export interface ProductVariantSummary {
|
||||
/// can read them.
|
||||
qty?: number;
|
||||
isPreOrder?: boolean;
|
||||
/// The gallery image chosen for this variant in the admin panel. It is one of
|
||||
/// the product's OWN images — the same ProductImage row, not a copy — so
|
||||
/// several variants (e.g. different weights of one design) may share it.
|
||||
/// Null when no picture was picked; callers fall back to the product image.
|
||||
imageId?: string | null;
|
||||
image?: ProductImageSummary | null;
|
||||
}
|
||||
|
||||
export interface CategorySummary {
|
||||
@@ -53,7 +66,9 @@ export interface ProductSummary {
|
||||
/// GET /catalog/products.
|
||||
productCode?: string | null;
|
||||
variants: ProductVariantSummary[];
|
||||
images: Array<{ url: string }>;
|
||||
/// Returned sorted by sortOrder, so images[0] really is the product's main
|
||||
/// photo rather than whichever row the database happened to hand back.
|
||||
images: ProductImageSummary[];
|
||||
category: CategorySummary | null;
|
||||
brand: BrandSummary | null;
|
||||
collections: Array<{ collectionId: string; collection: CollectionSummary }>;
|
||||
@@ -132,6 +147,13 @@ export interface GoldPriceQuote {
|
||||
quotedAt: string;
|
||||
}
|
||||
|
||||
/** One admin-uploaded home banner slide — raw shape as saved by the admin
|
||||
* panel's manage/Const.vue "اسلایدر موبایل" tab (image + optional link). */
|
||||
export interface AdminSlide {
|
||||
image: string;
|
||||
link: string;
|
||||
}
|
||||
|
||||
export const api = {
|
||||
listProducts: () => request<ProductSummary[]>('/catalog/products'),
|
||||
listCustomers: () => request<CustomerSummary[]>('/customers'),
|
||||
@@ -141,4 +163,7 @@ export const api = {
|
||||
createOrder: (dto: CreateOrderDto) => request<OrderSummary>('/orders', { method: 'POST', body: JSON.stringify(dto) }),
|
||||
getCustomerOrders: (customerId: string) => request<OrderDetail[]>(`/customers/${customerId}/orders`),
|
||||
getLatestGoldPrice: (karatPerMille = 750) => request<GoldPriceQuote>(`/pricing/latest?karatPerMille=${karatPerMille}`),
|
||||
/** GET /home/slider — public, no auth. Empty array when the admin hasn't
|
||||
* configured any slides (the caller falls back to a derived hero). */
|
||||
getHomeSlider: () => request<AdminSlide[]>('/home/slider'),
|
||||
};
|
||||
|
||||
@@ -1,27 +1,38 @@
|
||||
<!--
|
||||
Editorial hero for the home screen. Replaces the legacy swiper banner with a
|
||||
self-contained, dependency-free slider: no swiper CSS to fight, no autoplay
|
||||
jitter, and it keeps working when a slide has no artwork.
|
||||
Editorial hero for the home screen. Self-contained, dependency-free slider:
|
||||
no swiper CSS to fight, no autoplay jitter, and it keeps working when a
|
||||
slide has no artwork.
|
||||
|
||||
Swipe support: the app sets `direction: rtl !important` on #app (see
|
||||
src/App.vue), so — same fix as GallerySlider.vue — the track is an isolated
|
||||
`direction: ltr` subtree (.showcase__track). Inside it, index 0 is first and
|
||||
a positive translateX moves right, plain LTR arithmetic, regardless of the
|
||||
page's own direction. The drag gesture itself is still read in screen space
|
||||
(dragging left = next slide) so it feels natural to an RTL user; only the
|
||||
track's internal layout/maths is pinned to LTR.
|
||||
-->
|
||||
<template>
|
||||
<section class="showcase" v-if="slides.length">
|
||||
<div class="showcase__frame">
|
||||
<transition-group name="showcase-fade" tag="div" class="showcase__stage">
|
||||
<article v-for="(slide, i) in slides" :key="slide.key" v-show="i === index" class="showcase__slide">
|
||||
<img v-if="slide.image" :src="slide.image" alt="" class="showcase__art" />
|
||||
<div class="showcase__scrim" aria-hidden="true"></div>
|
||||
|
||||
<div class="showcase__copy">
|
||||
<span class="showcase__eyebrow">{{ slide.eyebrow }}</span>
|
||||
<h2 class="showcase__title">{{ slide.title }}</h2>
|
||||
<p class="showcase__sub">{{ slide.subtitle }}</p>
|
||||
<button type="button" class="showcase__cta" @click="emit('select', slide)">
|
||||
{{ slide.cta }}
|
||||
<Icon icon="vuesax-linear:arrow-left-2" size="17" />
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
ref="root"
|
||||
class="showcase__frame"
|
||||
:class="{ 'is-dragging': dragging }"
|
||||
@pointerdown="onPointerDown"
|
||||
@pointermove="onPointerMove"
|
||||
@pointerup="onPointerUp"
|
||||
@pointercancel="onPointerCancel"
|
||||
>
|
||||
<div class="showcase__track" :style="trackStyle">
|
||||
<article
|
||||
v-for="(slide, i) in slides"
|
||||
:key="slide.key"
|
||||
class="showcase__slide"
|
||||
:class="{ 'is-active': i === index }"
|
||||
@click="onSlideClick(slide)"
|
||||
>
|
||||
<img v-if="slide.image" :src="slide.image" alt="" class="showcase__art" draggable="false" />
|
||||
</article>
|
||||
</transition-group>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="showcase__dots" v-if="slides.length > 1">
|
||||
@@ -39,8 +50,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
import Icon from '../icon/Icon.vue'
|
||||
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
|
||||
export interface ShowcaseSlide {
|
||||
key: string
|
||||
@@ -50,6 +60,9 @@ export interface ShowcaseSlide {
|
||||
cta: string
|
||||
image?: string
|
||||
categoryId?: string
|
||||
/** Admin-uploaded slide's own destination (manage/Const.vue), when it has
|
||||
* one. Takes priority over `categoryId` in the click handler. */
|
||||
link?: string
|
||||
}
|
||||
|
||||
const props = defineProps<{ slides: ShowcaseSlide[] }>()
|
||||
@@ -75,6 +88,98 @@ onMounted(restart)
|
||||
onBeforeUnmount(() => {
|
||||
if (timer) clearInterval(timer)
|
||||
})
|
||||
|
||||
// ── Drag / swipe ─────────────────────────────────────────────
|
||||
const root = ref<HTMLElement | null>(null)
|
||||
const dragging = ref(false)
|
||||
const dragX = ref(0)
|
||||
let pointerId: number | null = null
|
||||
let startX = 0
|
||||
let startY = 0
|
||||
let trackWidth = 0
|
||||
let isSwipe = false
|
||||
|
||||
const DRAG_COMMIT_RATIO = 0.18
|
||||
const TAP_MAX_PX = 6
|
||||
|
||||
function onPointerDown(e: PointerEvent) {
|
||||
if (props.slides.length < 2 || pointerId !== null) return
|
||||
const el = root.value
|
||||
if (!el) return
|
||||
pointerId = e.pointerId
|
||||
startX = e.clientX
|
||||
startY = e.clientY
|
||||
trackWidth = el.getBoundingClientRect().width || 1
|
||||
dragX.value = 0
|
||||
dragging.value = true
|
||||
isSwipe = false
|
||||
el.setPointerCapture?.(e.pointerId)
|
||||
}
|
||||
|
||||
function onPointerMove(e: PointerEvent) {
|
||||
if (!dragging.value || e.pointerId !== pointerId) return
|
||||
const dx = e.clientX - startX
|
||||
const dy = e.clientY - startY
|
||||
if (!isSwipe && Math.abs(dx) > Math.abs(dy) && Math.abs(dx) > TAP_MAX_PX) isSwipe = true
|
||||
if (isSwipe) dragX.value = dx
|
||||
}
|
||||
|
||||
function endDrag() {
|
||||
if (!dragging.value) return
|
||||
const dx = dragX.value
|
||||
const ratio = dx / trackWidth
|
||||
|
||||
dragging.value = false
|
||||
pointerId = null
|
||||
dragX.value = 0
|
||||
|
||||
// Screen-space drag direction: dragging left (negative dx) is "next slide",
|
||||
// matching what the gesture means to the person doing it.
|
||||
if (ratio <= -DRAG_COMMIT_RATIO && index.value < props.slides.length - 1) index.value++
|
||||
else if (ratio >= DRAG_COMMIT_RATIO && index.value > 0) index.value--
|
||||
|
||||
if (isSwipe) restart()
|
||||
isSwipe = false
|
||||
}
|
||||
|
||||
function onPointerUp(e: PointerEvent) {
|
||||
if (e.pointerId !== pointerId) return
|
||||
endDrag()
|
||||
}
|
||||
|
||||
function onPointerCancel(e: PointerEvent) {
|
||||
if (e.pointerId !== pointerId) return
|
||||
dragging.value = false
|
||||
pointerId = null
|
||||
dragX.value = 0
|
||||
isSwipe = false
|
||||
}
|
||||
|
||||
function onSlideClick(slide: ShowcaseSlide) {
|
||||
// A drag also fires a trailing click on release; only treat it as a tap
|
||||
// (i.e. open the slide) when the gesture never turned into a swipe.
|
||||
if (isSwipe) return
|
||||
emit('select', slide)
|
||||
}
|
||||
|
||||
const trackStyle = computed(() => {
|
||||
const base = -index.value * 100
|
||||
const dragPercent = trackWidth ? (dragX.value / trackWidth) * 100 : 0
|
||||
return {
|
||||
transform: `translate3d(${base + dragPercent}%, 0, 0)`,
|
||||
transition: dragging.value ? 'none' : undefined,
|
||||
}
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (pointerId !== null && root.value) {
|
||||
try {
|
||||
root.value.releasePointerCapture(pointerId)
|
||||
} catch {
|
||||
/* element may already be detached */
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
@@ -82,90 +187,80 @@ onBeforeUnmount(() => {
|
||||
position: relative;
|
||||
border-radius: 22px;
|
||||
overflow: hidden;
|
||||
background: @ink;
|
||||
// Neutral rather than @ink: this only shows before artwork loads, and a
|
||||
// dark fill read as an unwanted border wherever it was visible at all.
|
||||
background: @hairlineSoft;
|
||||
box-shadow: 0 26px 50px -34px rgba(20, 22, 26, 0.85);
|
||||
aspect-ratio: 320 / 208;
|
||||
touch-action: pan-y;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
// Forces its own compositing layer so `overflow: hidden` actually clips the
|
||||
// translated track on Safari/iOS — without this, a sliver of the next
|
||||
// slide peeks past the rounded edge because the browser composites the
|
||||
// transformed child (.showcase__track) on a layer that ignores the
|
||||
// parent's clip until it's promoted the same way.
|
||||
transform: translateZ(0);
|
||||
-webkit-transform: translateZ(0);
|
||||
}
|
||||
|
||||
.showcase__stage {
|
||||
position: relative;
|
||||
display: block;
|
||||
aspect-ratio: 320 / 208;
|
||||
// Isolated from the page's `direction: rtl` — see the file header. Everything
|
||||
// under this node uses plain LTR layout and LTR-signed transforms.
|
||||
.showcase__track {
|
||||
direction: ltr;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
// A gentle, slightly-overshooting ease — softer settle than a linear/
|
||||
// ease-out curve, which is what makes the swipe feel "alive" rather than
|
||||
// mechanical. Slowed down a touch from the old 0.42s so the motion reads
|
||||
// as a deliberate glide instead of a snap.
|
||||
transition: transform 0.56s cubic-bezier(0.16, 0.86, 0.3, 1);
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.showcase__frame.is-dragging .showcase__track {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.showcase__slide {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
position: relative;
|
||||
flex: 0 0 100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
cursor: pointer;
|
||||
// Clips its own image's scale-in (.showcase__art below) to this slide's own
|
||||
// box, so the 1.06x zoom never bleeds into the neighbouring slide.
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.showcase__art {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
// A plain block filling the slide, NOT an absolutely-positioned box.
|
||||
// `position: absolute` + `inset` made the image's own box a second,
|
||||
// independently-rounded measurement of the same area the frame already
|
||||
// sizes via `aspect-ratio`; on real devices the two disagreed by a
|
||||
// fraction of a pixel and the frame's dark background showed through as a
|
||||
// hairline border around the artwork. Sizing it as ordinary in-flow
|
||||
// content removes that second measurement entirely.
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
opacity: 0.5;
|
||||
transform: scale(1.04);
|
||||
-webkit-user-drag: none;
|
||||
user-select: none;
|
||||
// Never settles below 1x: the "breathe in" flourish now runs from a larger
|
||||
// scale down to exactly 1, so the image is always at least as big as the
|
||||
// slide and can never reveal what sits behind it.
|
||||
transform: scale(1.06);
|
||||
transition: transform 0.56s cubic-bezier(0.16, 0.86, 0.3, 1);
|
||||
}
|
||||
|
||||
.showcase__scrim {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background:
|
||||
linear-gradient(255deg, rgba(122, 27, 51, 0.55) 0%, rgba(122, 27, 51, 0) 52%),
|
||||
linear-gradient(to left, rgba(20, 22, 26, 0.2) 0%, rgba(20, 22, 26, 0.92) 72%);
|
||||
.showcase__slide.is-active .showcase__art {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
.showcase__copy {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
padding: 22px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.showcase__eyebrow {
|
||||
font-size: 10.5px;
|
||||
letter-spacing: 1.6px;
|
||||
color: rgba(255, 255, 255, 0.62);
|
||||
padding-bottom: 9px;
|
||||
}
|
||||
|
||||
.showcase__title {
|
||||
font-size: 22px;
|
||||
font-weight: 800;
|
||||
line-height: 1.35;
|
||||
color: #fff;
|
||||
margin: 0 0 7px;
|
||||
}
|
||||
|
||||
.showcase__sub {
|
||||
font-size: 12.5px;
|
||||
line-height: 1.85;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
margin: 0 0 16px;
|
||||
max-width: 30ch;
|
||||
}
|
||||
|
||||
.showcase__cta {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
padding: 9px 16px;
|
||||
border-radius: 999px;
|
||||
background: #fff;
|
||||
color: @ink;
|
||||
font-size: 12.5px;
|
||||
font-weight: 700;
|
||||
font-family: inherit;
|
||||
transition: transform 0.18s ease, background 0.18s ease;
|
||||
}
|
||||
.showcase__cta:active {
|
||||
transform: scale(0.97);
|
||||
.showcase__frame.is-dragging .showcase__art {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.showcase__dots {
|
||||
@@ -183,19 +278,10 @@ onBeforeUnmount(() => {
|
||||
cursor: pointer;
|
||||
border-radius: 999px;
|
||||
background: @hairline;
|
||||
transition: all 0.25s ease;
|
||||
transition: width 0.42s cubic-bezier(0.16, 0.86, 0.3, 1), background 0.3s ease;
|
||||
}
|
||||
.showcase__dot.is-active {
|
||||
width: 20px;
|
||||
background: @wine;
|
||||
}
|
||||
|
||||
.showcase-fade-enter-active,
|
||||
.showcase-fade-leave-active {
|
||||
transition: opacity 0.55s ease;
|
||||
}
|
||||
.showcase-fade-enter-from,
|
||||
.showcase-fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -30,7 +30,13 @@
|
||||
|
||||
<div class="ci__row">
|
||||
<div class="ci__qty">
|
||||
<button type="button" class="ci__qtyBtn" aria-label="افزودن" @click="increment">
|
||||
<button
|
||||
type="button"
|
||||
class="ci__qtyBtn"
|
||||
aria-label="افزودن"
|
||||
:disabled="!canIncrement"
|
||||
@click="increment"
|
||||
>
|
||||
<Icon icon="vuesax-linear:add" size="16" />
|
||||
</button>
|
||||
<span class="ci__qtyValue">{{ value.qty }}</span>
|
||||
@@ -71,7 +77,13 @@ const emit = defineEmits<{ (e: 'note', value: number): void }>()
|
||||
const cart = useCartStore()
|
||||
const index = cart.find_index(props.value)
|
||||
|
||||
// Stock ceiling, same rule as the product page: uncapped for a pre-order
|
||||
// variant, otherwise limited to what is on hand. The store clamps as well.
|
||||
const maxQty = computed(() => cart.maxQty(props.value))
|
||||
const canIncrement = computed(() => maxQty.value === null || props.value.qty < maxQty.value)
|
||||
|
||||
const increment = () => {
|
||||
if (!canIncrement.value) return
|
||||
cart.updateFeild(props.value.qty + 1, index, props.value, 'qty')
|
||||
}
|
||||
const decrement = () => {
|
||||
|
||||
@@ -102,22 +102,55 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Variants -->
|
||||
<section v-if="variants && variants.length > 1" class="pd__block">
|
||||
<h2 class="pd__blockTitle">انتخاب وزن</h2>
|
||||
<div class="pd__variants no-scrollbar">
|
||||
<!--
|
||||
Variants. Each chip carries the variant's OWN gallery picture when the
|
||||
admin picked one, so two variants of the same weight (say plain vs.
|
||||
stone-set) are told apart at a glance instead of only by title — and
|
||||
several weights of one design correctly show the same photo.
|
||||
|
||||
Shown from one variant up (not two): with a picture and a stock badge
|
||||
the chip is worth seeing even when there is only one choice.
|
||||
|
||||
The row wraps rather than scrolling sideways: past ~4 chips a
|
||||
horizontal strip hides the rest off-screen, and products here can have
|
||||
10+ weights. Beyond two rows the extra chips collapse behind a toggle.
|
||||
-->
|
||||
<section v-if="variants && variants.length" class="pd__block">
|
||||
<h2 class="pd__blockTitle">انتخاب تنوع</h2>
|
||||
<div class="pd__variants">
|
||||
<button
|
||||
v-for="item in variants"
|
||||
v-for="item in visibleVariants"
|
||||
:key="item.id"
|
||||
type="button"
|
||||
class="pd__variant"
|
||||
:class="{ 'is-on': selectedVariant?.id === item.id }"
|
||||
:class="{
|
||||
'is-on': selectedVariant?.id === item.id,
|
||||
'is-out': !isOrderable(item),
|
||||
}"
|
||||
:disabled="!isOrderable(item)"
|
||||
@click="selectedVariant = item"
|
||||
>
|
||||
<img
|
||||
v-if="variantImage(item)"
|
||||
class="pd__variantImg"
|
||||
:src="variantImage(item)"
|
||||
:alt="item.title"
|
||||
/>
|
||||
<span class="pd__variantTitle">{{ item.title }}</span>
|
||||
<span class="pd__variantWeight">{{ Number(item.rashWeightGrams) }} گرم</span>
|
||||
<span class="pd__variantStock" :class="stockChipClass(item)">
|
||||
{{ stockChipLabel(item) }}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
v-if="variants.length > VARIANT_PREVIEW_COUNT"
|
||||
type="button"
|
||||
class="pd__variantsMore"
|
||||
@click="showAllVariants = !showAllVariants"
|
||||
>
|
||||
{{ showAllVariants ? 'نمایش کمتر' : `نمایش همه (${variants.length})` }}
|
||||
</button>
|
||||
</section>
|
||||
|
||||
<!-- Specs -->
|
||||
@@ -166,7 +199,13 @@
|
||||
<!-- Action bar -->
|
||||
<div class="pd__bar">
|
||||
<div v-if="inCart" class="pd__qty">
|
||||
<button type="button" class="pd__qtyBtn" aria-label="افزایش" @click="increment">
|
||||
<button
|
||||
type="button"
|
||||
class="pd__qtyBtn"
|
||||
aria-label="افزایش"
|
||||
:disabled="!canIncrement"
|
||||
@click="increment"
|
||||
>
|
||||
<Icon icon="vuesax-linear:add" size="16" />
|
||||
</button>
|
||||
<span class="pd__qtyValue">{{ cartQty }}</span>
|
||||
@@ -264,8 +303,9 @@ import GallerySlider from './GallerySlider.vue'
|
||||
import Spinner from '../Spinner.vue'
|
||||
import NotifyVant from '../NotifyVant.vue'
|
||||
import DialogVant from '../DialogVant.vue'
|
||||
import { useProduct } from '../../composables/useProduct'
|
||||
import { useProduct, isVariantOrderable } from '../../composables/useProduct'
|
||||
import { useProductDetail } from '../../composables/useProductDetail'
|
||||
import type { ProductVariantSummary } from '../../api/client'
|
||||
import { useFavoritesStore } from '../../stores/favoritesStore'
|
||||
import { useCartStore, type CartItem } from '../../stores/cartStore'
|
||||
import { isUserLoggedIn } from '../../utils/checkAccess'
|
||||
@@ -324,6 +364,34 @@ const qty = computed(() => productAdapter.value.qty.value)
|
||||
const isPreOrder = computed(() => productAdapter.value.isPreOrder.value)
|
||||
const is_exist = computed(() => productAdapter.value.is_exist.value)
|
||||
|
||||
// ── Variant chips ───────────────────────────────────────────────
|
||||
// Two rows' worth is shown up front; the rest sit behind "نمایش همه" so a
|
||||
// product with 10+ weights does not push the description off the screen.
|
||||
const VARIANT_PREVIEW_COUNT = 6
|
||||
const showAllVariants = ref(false)
|
||||
const visibleVariants = computed(() =>
|
||||
showAllVariants.value ? variants.value : variants.value.slice(0, VARIANT_PREVIEW_COUNT),
|
||||
)
|
||||
|
||||
const isOrderable = (v: ProductVariantSummary) => isVariantOrderable(v)
|
||||
|
||||
/**
|
||||
* The chip picture: the variant's own image when the admin picked one, else the
|
||||
* product's main photo so chips never render as empty boxes. Variants sharing
|
||||
* one image (same design, several weights) intentionally show the same picture.
|
||||
*/
|
||||
const variantImage = (v: ProductVariantSummary) =>
|
||||
v.image?.url ?? productDetail.productRef.value?.images?.[0]?.url ?? ''
|
||||
|
||||
const stockChipLabel = (v: ProductVariantSummary) => {
|
||||
if ((v.qty ?? 0) > 0) return 'موجود'
|
||||
return v.isPreOrder ? 'پیشسفارش' : 'ناموجود'
|
||||
}
|
||||
const stockChipClass = (v: ProductVariantSummary) => {
|
||||
if ((v.qty ?? 0) > 0) return 'is-in'
|
||||
return v.isPreOrder ? 'is-pre' : 'is-none'
|
||||
}
|
||||
|
||||
const body = computed(() => productDetail.productRef.value?.description ?? '')
|
||||
const bodyIsLong = computed(() => body.value.length > 120)
|
||||
const categoryTitle = computed(() => productDetail.productRef.value?.category?.title ?? '')
|
||||
@@ -336,6 +404,16 @@ watch(slides, (list) => {
|
||||
if (imageIndex.value > list.length - 1) imageIndex.value = 0
|
||||
})
|
||||
|
||||
// Picking a variant scrolls the gallery to that variant's photo, so the chosen
|
||||
// chip and the big picture always agree. Only moves when the variant actually
|
||||
// has an image and it is one of the product's own slides.
|
||||
watch(selectedVariant, (v) => {
|
||||
const url = v?.image?.url
|
||||
if (!url) return
|
||||
const i = slides.value.indexOf(url)
|
||||
if (i > -1) imageIndex.value = i
|
||||
})
|
||||
|
||||
// ── Full-screen preview ─────────────────────────────────────────
|
||||
// Tapping the inline gallery (a plain tap, not a swipe — GallerySlider tells
|
||||
// the two apart itself and only emits `tap` for the former) opens a
|
||||
@@ -385,7 +463,14 @@ const inCart = computed(() => cart.isInCart(storeProduct.value))
|
||||
const cartIndex = computed(() => cart.find_index(storeProduct.value))
|
||||
const cartQty = computed(() => (cartIndex.value > -1 ? cart.items[cartIndex.value].qty : 0))
|
||||
|
||||
const increment = () => cart.updateFeild(cartQty.value + 1, cartIndex.value, storeProduct.value, 'qty')
|
||||
// Stock ceiling: a pre-order variant may exceed what is on hand, anything else
|
||||
// stops at its qty. The store clamps too — this just keeps the button honest.
|
||||
const cartMaxQty = computed(() => cart.maxQty(storeProduct.value))
|
||||
const canIncrement = computed(() => cartMaxQty.value === null || cartQty.value < cartMaxQty.value)
|
||||
const increment = () => {
|
||||
if (!canIncrement.value) return
|
||||
cart.updateFeild(cartQty.value + 1, cartIndex.value, storeProduct.value, 'qty')
|
||||
}
|
||||
const decrement = () => {
|
||||
if (cartQty.value <= 1) {
|
||||
cart.remove(storeProduct.value)
|
||||
@@ -623,20 +708,22 @@ onBeforeUnmount(() => {
|
||||
color: @ink;
|
||||
}
|
||||
|
||||
/* Wraps instead of scrolling sideways — see the template comment: a product
|
||||
may have 10+ weights and off-screen chips are chips nobody taps. */
|
||||
.pd__variants {
|
||||
display: flex;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 8px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
.pd__variant {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3px;
|
||||
padding: 10px 14px;
|
||||
padding: 8px;
|
||||
border-radius: 14px;
|
||||
cursor: pointer;
|
||||
text-align: start;
|
||||
text-align: center;
|
||||
align-items: center;
|
||||
background: @canvas;
|
||||
border: 1px solid @hairline;
|
||||
transition: border-color 0.18s ease, background 0.18s ease;
|
||||
@@ -645,6 +732,54 @@ onBeforeUnmount(() => {
|
||||
background: @ink;
|
||||
border-color: @ink;
|
||||
}
|
||||
.pd__variantImg {
|
||||
width: 100%;
|
||||
aspect-ratio: 1;
|
||||
object-fit: cover;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
/* Out of stock and not pre-orderable: still listed, so the customer can see
|
||||
the weight exists, but visibly dimmed and not tappable. */
|
||||
.pd__variant.is-out {
|
||||
opacity: 0.45;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.pd__variantStock {
|
||||
font-size: 9.5px;
|
||||
font-weight: 700;
|
||||
padding: 1px 6px;
|
||||
border-radius: 999px;
|
||||
margin-top: 1px;
|
||||
}
|
||||
.pd__variantStock.is-in {
|
||||
color: #1a7f3c;
|
||||
background: rgba(26, 127, 60, 0.1);
|
||||
}
|
||||
.pd__variantStock.is-pre {
|
||||
color: #1f6feb;
|
||||
background: rgba(31, 111, 235, 0.1);
|
||||
}
|
||||
.pd__variantStock.is-none {
|
||||
color: @inkFaint;
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.pd__variant.is-on .pd__variantStock {
|
||||
color: #fff;
|
||||
background: rgba(255, 255, 255, 0.16);
|
||||
}
|
||||
.pd__variantsMore {
|
||||
margin-top: 10px;
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid @hairline;
|
||||
background: transparent;
|
||||
color: @ink;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
}
|
||||
.pd__variantTitle {
|
||||
font-size: 12.5px;
|
||||
font-weight: 700;
|
||||
|
||||
@@ -21,20 +21,36 @@
|
||||
import { computed, ref, unref, type Ref } from 'vue'
|
||||
import type { ProductSummary, ProductVariantSummary } from '../api/client'
|
||||
|
||||
/** A variant can be ordered when it has stock, or when it is flagged pre-order. */
|
||||
export function isVariantOrderable(v: ProductVariantSummary): boolean {
|
||||
return (v.qty ?? 0) > 0 || Boolean(v.isPreOrder)
|
||||
}
|
||||
|
||||
function getCheapestVariant(variants: ProductVariantSummary[] | undefined): ProductVariantSummary | null {
|
||||
if (!variants || variants.length === 0) return null
|
||||
return variants.reduce((cheapest, v) =>
|
||||
// Prefer something the customer can actually order: defaulting to an
|
||||
// out-of-stock variant means the page opens on a disabled button even though
|
||||
// other weights are available. Falls back to the full list when nothing is
|
||||
// orderable, so the page still shows a selection.
|
||||
const pool = variants.filter(isVariantOrderable)
|
||||
const candidates = pool.length ? pool : variants
|
||||
return candidates.reduce((cheapest, v) =>
|
||||
parseFloat(v.defaultWagePercent) < parseFloat(cheapest.defaultWagePercent) ? v : cheapest,
|
||||
)
|
||||
}
|
||||
|
||||
export function useProduct(productItem: ProductSummary, initialVariant?: ProductVariantSummary) {
|
||||
const product = ref<ProductSummary>(productItem)
|
||||
const image = computed(() => product.value.images?.[0]?.url)
|
||||
const id = computed(() => product.value.id)
|
||||
const title = computed(() => product.value.title)
|
||||
const variants = computed(() => product.value.variants)
|
||||
const selectedVariant: Ref<ProductVariantSummary | null> = ref(initialVariant ?? getCheapestVariant(unref(variants)))
|
||||
// The selected variant's own picture when the admin chose one, otherwise the
|
||||
// product's main image. Several variants may legitimately resolve to the SAME
|
||||
// image — one photo, several weights.
|
||||
const image = computed(
|
||||
() => selectedVariant.value?.image?.url ?? product.value.images?.[0]?.url,
|
||||
)
|
||||
const min_gram = computed(() => selectedVariant.value ? parseFloat(selectedVariant.value.rashWeightGrams) : 0)
|
||||
const wage = computed(() => (selectedVariant.value ? parseFloat(selectedVariant.value.defaultWagePercent) : null))
|
||||
const hasDiscountWage = ref<number | null>(null) // no promotions backend yet — see file header
|
||||
|
||||
@@ -4,7 +4,10 @@ import VueLazyLoad from 'vue3-lazyload';
|
||||
import App from './App.vue';
|
||||
import { router } from './router';
|
||||
import Container from './components/ui/Container.vue';
|
||||
import Vector from './assets/img/Vector.svg';
|
||||
// TODO: temporary placeholder — swap Vector.svg back in (or a proper loading
|
||||
// mark) once one exists; montzar-logo.png is the brand logo, borrowed here
|
||||
// just so lazy-loaded images have something other than a broken icon.
|
||||
import Vector from './assets/img/montzar-logo.png';
|
||||
|
||||
// Global styles, ported from legacy henza_pwa/src/main.ts — same import order
|
||||
// (ant-design-vue's own theme first, then our Less overrides on top). Legacy
|
||||
|
||||
@@ -87,9 +87,16 @@ router.afterEach((to) => {
|
||||
transition.setTransitionFlag(true);
|
||||
}, 100);
|
||||
|
||||
// Unconditional on both branches: this hook is the single source of truth for
|
||||
// tab-bar visibility. A screen may hide the bar for its own reasons while it
|
||||
// is open (SearchView does, for its filter sheet), and this is what clears
|
||||
// that override on the way out — the old `if (!nav.showNav)` guard made the
|
||||
// same write, just phrased as if something else might own the flag.
|
||||
const route = nav.navBarRoutes.find((x) => to.name === x.name);
|
||||
if (route) {
|
||||
nav.setActiveLink(route.index);
|
||||
if (!nav.showNav) nav.setNavVisibility(true);
|
||||
} else nav.setNavVisibility(false);
|
||||
nav.setNavVisibility(true);
|
||||
} else {
|
||||
nav.setNavVisibility(false);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -54,13 +54,42 @@ export const useCartStore = defineStore('cart', {
|
||||
}
|
||||
},
|
||||
addToCart(data: CartItem) {
|
||||
// Refuse a variant that cannot be ordered — out of stock and not flagged
|
||||
// pre-order. The same guard the admin cart applies (stores/cart.ts), kept
|
||||
// here so it holds even if a caller forgets to disable its button.
|
||||
const target = data.product?.variants?.find((v) => v.id === data.vid)
|
||||
if (!target) return false
|
||||
if (!((target.qty ?? 0) > 0 || target.isPreOrder)) return false
|
||||
|
||||
const index = this.find_index(data)
|
||||
if (index === -1) this.items.push(data)
|
||||
if (index === -1) this.items.push({ ...data, qty: this.capQty(data.qty, target) })
|
||||
else {
|
||||
this.items[index].product = data.product
|
||||
this.items[index].qty = data.qty
|
||||
this.items[index].qty = this.capQty(data.qty, target)
|
||||
}
|
||||
persist(this.items)
|
||||
return true
|
||||
},
|
||||
|
||||
/**
|
||||
* Stock ceiling for a cart line. A pre-order variant is orderable beyond
|
||||
* what is on hand (that is the point of pre-ordering), so it is uncapped;
|
||||
* anything else stops at its on-hand quantity.
|
||||
*/
|
||||
capQty(want: number, variant: { qty?: number; isPreOrder?: boolean }) {
|
||||
const n = Math.max(1, Math.floor(want) || 1)
|
||||
if (variant.isPreOrder) return n
|
||||
const max = variant.qty ?? 0
|
||||
return max > 0 ? Math.min(n, max) : n
|
||||
},
|
||||
|
||||
/** The ceiling for an item already in the cart; null when uncapped. */
|
||||
maxQty(item: Pick<CartItem, 'pid' | 'vid'>) {
|
||||
const i = this.find_index(item)
|
||||
if (i === -1) return null
|
||||
const v = this.variant(this.items[i])
|
||||
if (!v || v.isPreOrder) return null
|
||||
return (v.qty ?? 0) > 0 ? (v.qty as number) : null
|
||||
},
|
||||
remove(item: CartItem) {
|
||||
const index = this.find_index(item)
|
||||
@@ -72,6 +101,13 @@ export const useCartStore = defineStore('cart', {
|
||||
updateFeild(value: unknown, index: number | null, item: Pick<CartItem, 'pid' | 'vid'>, field: keyof CartItem) {
|
||||
if (!this.isInCart(item)) return false
|
||||
const i = index ?? this.find_index(item)
|
||||
// A quantity change is clamped to what is actually in stock (unless the
|
||||
// variant is pre-orderable), so the ceiling holds no matter which screen
|
||||
// drives the stepper.
|
||||
if (field === 'qty') {
|
||||
const v = this.variant(this.items[i])
|
||||
value = v ? this.capQty(Number(value), v) : Math.max(1, Number(value) || 1)
|
||||
}
|
||||
;(this.items[i] as any)[field] = value
|
||||
persist(this.items)
|
||||
return true
|
||||
|
||||
@@ -138,7 +138,7 @@ import type { HomeCategory } from '../stores/configStore'
|
||||
import { useCartStore } from '../stores/cartStore'
|
||||
import { useFiltersStore } from '../stores/filtersStore'
|
||||
import { api } from '../api/client'
|
||||
import type { GoldPriceQuote, ProductSummary } from '../api/client'
|
||||
import type { AdminSlide, GoldPriceQuote, ProductSummary } from '../api/client'
|
||||
|
||||
const router = useRouter()
|
||||
const memory = useConfigStore()
|
||||
@@ -149,6 +149,10 @@ const loading = ref(true)
|
||||
const refreshing = ref(false)
|
||||
const products = ref<ProductSummary[]>([])
|
||||
const goldPrice = ref<GoldPriceQuote | null>(null)
|
||||
// Admin-managed banner slides (manage/Const.vue "اسلایدر موبایل"). null while
|
||||
// unfetched/unavailable — distinct from "fetched, empty" — so `slides` below
|
||||
// knows whether to wait or fall back to the category-derived hero.
|
||||
const adminSlides = ref<AdminSlide[] | null>(null)
|
||||
|
||||
const productDrawer = ref(false)
|
||||
const activeProductId = ref<string | null>(null)
|
||||
@@ -157,9 +161,12 @@ const featured = computed<ProductSummary | null>(() => products.value[0] ?? null
|
||||
const gridProducts = computed(() => products.value.slice(1, 5))
|
||||
const newestProducts = computed(() => products.value.slice(5, 13))
|
||||
|
||||
// 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.
|
||||
// The hero prefers the admin-uploaded banner (manage/Const.vue "اسلایدر
|
||||
// موبایل") when one has been configured; otherwise it falls back to a hero
|
||||
// built from REAL categories, since no curated banner payload existed before
|
||||
// that admin screen was wired up. Each fallback slide deep-links into search
|
||||
// filtered by that category, so the artwork is decorative but the
|
||||
// destination is genuine.
|
||||
const SLIDE_COPY = [
|
||||
{ eyebrow: 'مجموعه منتزر', cta: 'دیدن مجموعه' },
|
||||
{ eyebrow: 'انتخاب سردبیر', cta: 'مشاهده' },
|
||||
@@ -167,6 +174,20 @@ const SLIDE_COPY = [
|
||||
]
|
||||
|
||||
const slides = computed<ShowcaseSlide[]>(() => {
|
||||
if (adminSlides.value?.length) {
|
||||
return adminSlides.value
|
||||
.filter((s) => s.image)
|
||||
.map((s, i) => ({
|
||||
key: `${i}-${s.image}`,
|
||||
eyebrow: 'مجموعه منتزر',
|
||||
title: '',
|
||||
subtitle: '',
|
||||
cta: '',
|
||||
image: s.image,
|
||||
link: s.link || undefined,
|
||||
}))
|
||||
}
|
||||
|
||||
const cats = memory.cats.slice(0, 3)
|
||||
if (!cats.length) return []
|
||||
return cats.map((cat, i) => ({
|
||||
@@ -196,6 +217,17 @@ function onCategory(cat: HomeCategory) {
|
||||
}
|
||||
|
||||
function onSlide(slide: ShowcaseSlide) {
|
||||
// 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.
|
||||
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]
|
||||
}
|
||||
@@ -221,6 +253,14 @@ async function loadData() {
|
||||
} catch {
|
||||
goldPrice.value = null
|
||||
}
|
||||
|
||||
try {
|
||||
adminSlides.value = await api.getHomeSlider()
|
||||
} catch {
|
||||
// No admin slides configured, or the endpoint failed — the category-
|
||||
// derived hero below covers this silently, same as before this existed.
|
||||
adminSlides.value = null
|
||||
}
|
||||
}
|
||||
|
||||
async function onRefresh() {
|
||||
|
||||
@@ -112,6 +112,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
||||
import { onBeforeRouteLeave } from 'vue-router'
|
||||
import { PullRefresh } from 'vant'
|
||||
import Icon from '../../components/icon/Icon.vue'
|
||||
import PageBar from '../../components/ui/PageBar.vue'
|
||||
@@ -246,6 +247,22 @@ function onBarClick(e: MouseEvent) {
|
||||
}
|
||||
}
|
||||
|
||||
// The filter sheet deliberately hides the bottom tab bar while it is open, so
|
||||
// its own fixed action bar ("اعمال فیلتر" / "حذف فیلترها") stays clickable —
|
||||
// the tab bar sits at the same bottom edge with a higher z-index. But leaving
|
||||
// the page with the sheet still open (browser/swipe back, or tapping a
|
||||
// collection tile) never ran hideFilters, so that hide leaked: showNav stayed
|
||||
// false, and on any destination outside the five tab routes the user was left
|
||||
// with no tab bar at all until a full page refresh reset the store.
|
||||
//
|
||||
// Restoring it here rather than in onBeforeUnmount is deliberate: this guard
|
||||
// runs BEFORE router.afterEach, which then writes the correct visibility for
|
||||
// the destination and gets the last word. onBeforeUnmount runs after it, and
|
||||
// would wrongly re-show the bar on full-page routes like /product/:id.
|
||||
onBeforeRouteLeave(() => {
|
||||
if (showFilter.value) nav.setNavVisibility(true)
|
||||
})
|
||||
|
||||
onMounted(loadProducts)
|
||||
</script>
|
||||
|
||||
|
||||