function setSearchUrl(searchValue) { return Promise.resolve({ url: '/search?q=' + searchValue }); } exportFunction('setSearchUrl', setSearchUrl);
집
신상품
매주 토요일 신상품 (1월 3일)
매주 토요일 신상품 (12월 27일)
매주 토요일 신상품 (12월 20일)
매주 토요일 신상품 (12월 13일)
매주 토요일 신상품 (12월 6일)
매주 토요일 신상품 (11월 29일)
매주 토요일 신상품 (11월 22일)
매주 토요일 신상품 (11월 15일)
매주 토요일 신상품 (11월 8일)
매주 토요일 신상품 (11월 1일)
베스트셀러
제품 카테고리
장식 스티커
PET 스티커
와시 스티커
스티커북
기타 소재 스티커
테이프
PET 테이프
와시 테이프
저널링 자료
재질지 & 메모패드(접착 없음)
스티커 노트
저널링 키트
저널링 도구 용품
가위 도구
지우개 & 수정테이프
접착제 및 접착 테이프
클립
서표
측정 도구
스탬프 및 잉크패드
도장
잉크 패드
스텐실 & 템플릿
노트
바인더
다이어리
수납 용품
필통
보관함 | 보관가방
카드백
자
필기구
마커펜 & 형광펜 & 라인 마커
젤펜
브러쉬펜
수채화 펜과 수채화 페인트
볼펜
기계식 연필
ATC 공급품
기타 제품
데스크탑 장식품
달력
키 홀더
인사말 카드
휴대폰 액세서리
테마 카테고리

인기 있는

크리스마스 🎄
🌼 꽃과 식물
캐릭터 이미지
여행 🌴 & 풍경
프레임 및 필름
나비 🦋
커피 ☕
창문 & 문
별이 빛나는 하늘 🌌 & 달
동물 & 곤충
건물 🏠 & 가구
다크 & 고딕
🌊 바다 & 물
텍스트 & 숫자
중국 스타일 🐼
파티🎉 & 조명
레이스
수채화 & 얼룩
라벨 & 메모패드
병
유화
구름 & 거품
용🐉
스팀펑크
사계절 ❄️
마법
할로윈 💀
🥐 음식 & 과일 🍇
엘프 🧚‍♀️
이국적인 풍습 & 소수 민족
문신
우주비행사 👩🏻‍🚀
보트 ⛵
캠핑 ⛺
직물 질감 및 천 예술
변덕
브랜드 카테고리
MOODTAPE
YIER
OKMT
Ding Ding
Journalsay
모든 제품
블로그
기프트 카드
  • 집
  • 신상품
    매주 토요일 신상품 (1월 3일)
    매주 토요일 신상품 (12월 27일)
    매주 토요일 신상품 (12월 20일)
    매주 토요일 신상품 (12월 13일)
    매주 토요일 신상품 (12월 6일)
    매주 토요일 신상품 (11월 29일)
    매주 토요일 신상품 (11월 22일)
    매주 토요일 신상품 (11월 15일)
    매주 토요일 신상품 (11월 8일)
    매주 토요일 신상품 (11월 1일)
  • 베스트셀러
  • 제품 카테고리
    장식 스티커
    • PET 스티커
    • 와시 스티커
    • 스티커북
    • 기타 소재 스티커
    테이프
    • PET 테이프
    • 와시 테이프
    저널링 자료
    • 재질지 & 메모패드(접착 없음)
    • 스티커 노트
    • 저널링 키트
    저널링 도구 용품
    • 가위 도구
    • 지우개 & 수정테이프
    • 접착제 및 접착 테이프
    • 클립
    • 서표
    • 측정 도구
    스탬프 및 잉크패드
    • 도장
    • 잉크 패드
    • 스텐실 & 템플릿
    노트
    • 바인더
    • 다이어리
    수납 용품
    • 필통
    • 보관함 | 보관가방
    • 카드백
    • 자
    필기구
    • 마커펜 & 형광펜 & 라인 마커
    • 젤펜
    • 브러쉬펜
    • 수채화 펜과 수채화 페인트
    • 볼펜
    • 기계식 연필
    ATC 공급품
    기타 제품
    • 데스크탑 장식품
    • 달력
    • 키 홀더
    • 인사말 카드
    • 휴대폰 액세서리
  • 테마 카테고리

    인기 있는

    크리스마스 🎄
    🌼 꽃과 식물
    캐릭터 이미지
    여행 🌴 & 풍경
    프레임 및 필름
    나비 🦋
    커피 ☕
    창문 & 문
    별이 빛나는 하늘 🌌 & 달
    동물 & 곤충
    건물 🏠 & 가구
    다크 & 고딕
    🌊 바다 & 물
    텍스트 & 숫자
    중국 스타일 🐼
    파티🎉 & 조명
    레이스
    수채화 & 얼룩
    라벨 & 메모패드
    병
    유화
    구름 & 거품
    용🐉
    스팀펑크
    사계절 ❄️
    마법
    할로윈 💀
    🥐 음식 & 과일 🍇
    엘프 🧚‍♀️
    이국적인 풍습 & 소수 민족
    문신
    우주비행사 👩🏻‍🚀
    보트 ⛵
    캠핑 ⛺
    직물 질감 및 천 예술
    변덕
  • 브랜드 카테고리
    MOODTAPE
    YIER
    OKMT
    Ding Ding
    Journalsay
  • 모든 제품
  • 블로그
  • 기프트 카드
  • 더 많은 링크
Korean
    flag
    Global Sites
      flag
      France
        flag
        Japanese
          flag
          Spanish
            flag
            German
              flag
              Netherlands
                flag
                이메일과 비밀번호를 입력하세요:
                이메일이 필요합니다
                유효한 이메일을 입력하세요.
                비밀번호가 필요합니다.
                비밀번호는 6-16자 사이여야 합니다.
                비밀번호를 잊으 셨나요?
                아래 정보를 입력하세요.
                이름은 필수 항목입니다.
                잘못된 문자가 포함되어 있습니다.
                성은 필수 항목입니다.
                잘못된 문자가 포함되어 있습니다.
                이메일이 필요합니다
                유효한 이메일을 입력하세요.
                비밀번호가 필요합니다.
                비밀번호는 6-16자 사이여야 합니다.
                성공적으로 등록했습니다

                1

                2

                이메일로 비밀번호 재설정

                이메일이 필요합니다
                유효한 이메일을 입력하세요.
                유효한 코드를 입력하세요
                비밀번호가 필요합니다.
                비밀번호는 6-16자 사이여야 합니다.
                같은 값을 다시 입력하십시오.
                비밀번호는 6-16자 사이여야 합니다.
                비밀번호와 확인 비밀번호가 일치하지 않습니다
                인증 코드를 받지 못하셨나요?
                비밀번호 재설정 성공!
                let freeShippingRestTimer = null; async function showFreeShippingRest(e) { const freeShippingRest = document.querySelector('.layout-floating-cart__free-shipping--rest'); if (freeShippingRest) { if (freeShippingRestTimer) { clearTimeout(freeShippingRestTimer); } freeShippingRest.classList.add('flex'); freeShippingRestTimer = setTimeout(function() { freeShippingRest.classList.remove('flex'); freeShippingRestTimer = null; }, 6000); } } document.addEventListener('dj.cartItemChange', showFreeShippingRest); document.addEventListener('dj.cartItemDelete', showFreeShippingRest);

                🎄Would you like such a Christmas gift? ---Journaling materials

                Dec 13, 2024 ~에 의해 Journalsay

                 

                Product 1: Journalsay Dream Christmas Eve Series Kawaii Girl Character Gift Landscaping PET Sticker

                 

                Product 2: Journalsay Surprise Party Series Kawaii Girl Character Doors and Windows PET Sticker

                 

                Product 3: Journalsay Paper Christmas Island Series Vintage Relief Landscaping Holiday Decor Material Paper

                 

                Product 4: Journalsay Snow Country Time Series Vintage Character Photo Frame PET Sticker

                 

                Product 5: Journalsay Christmas Wonderful Night Series Vintage Santa Claus Landscaping PET Sticker

                 

                Product 6: Journalsay 6 Rolls/set Creative Christmas Gifts Material Collage Washi Tape

                 

                Product 7: Journalsay Christmas Promise Series Vintage Character Snow Landscaping Sticker Book

                 

                Product 8: Journalsay Christmas Nocturne Series Vintage Flower Pattern Landscaping Material Paper

                 

                Product 9: Vintage Festival Decor Landscaping PET Sticker - Christmas Forest Series

                도움말

                FAQ

                배송 정보상품 후기반품 및 교환
                쇼핑주문 조회결제 정책개인정보보호서비스 약관
                회사 소개회사 소개문의하기

                Journalsay club

                가입하기저희는 훌륭한 이메일을 발송합니다!
                Please enter your email address.
                Please enter a valid email address.
                저희의 일원이 되어주셔서 감사합니다!

                카트

                장바구니가 비어 있습니다

                요약

                결제시 계산 된 세금 및 배송
                지불 방법

                You may also like

                View cart

                class SpzCustomFreeShippingRest extends SPZ.BaseElement { constructor(element) { super(element); this.freeShippingRestTimer = null; } isLayoutSupported(layout) { return layout == SPZCore.Layout.LOGIC; } buildCallback() { this.registerAction('showTips', () => { this.showTips_(); }); } showTips_(e) { const freeShippingRest = document.querySelector( '.layout-floating-cart__free-shipping--rest' ); if (freeShippingRest) { if (this.freeShippingRestTimer) { clearTimeout(this.freeShippingRestTimer); } freeShippingRest.classList.add('flex'); this.freeShippingRestTimer = setTimeout(function () { freeShippingRest.classList.remove('flex'); this.freeShippingRestTimer = null; }, 6000); } } } SPZ.defineElement('spz-custom-free-shipping-rest', SpzCustomFreeShippingRest);
                const debounce = function(fn, delay, immediate = false) { let timer; let promise; return function () { const context = this; const args = arguments; // 如果已经存在 promise,直接返回(等待正在执行的) if (promise) { return promise; } // 创建新的 promise promise = new Promise((resolve, reject) => { if (immediate) { // 立即执行 try { const result = fn.apply(context, args); // 如果结果是 promise,等待它完成 if (result && typeof result.then === 'function') { result.then(resolve).catch(reject); } else { resolve(result); } } catch (error) { reject(error); } // 设置定时器,在 delay 时间后重置 promise,允许下次调用 timer = setTimeout(() => { promise = null; }, delay); } else { // 延迟执行 timer = setTimeout(() => { try { const result = fn.apply(context, args); // 如果结果是 promise,等待它完成 if (result && typeof result.then === 'function') { result.then(resolve).catch(reject); } else { resolve(result); } } catch (error) { reject(error); } // 重置 promise promise = null; }, delay); } }); return promise; }; }; class HomeData { constructor() { this.earnPointsPlan = Promise.resolve({}); this.redeemPlan = Promise.resolve({}); this.getMemberDetailDebounce = debounce( this.getMemberDetail.bind(this), 200, true // 首次立即执行 ); this.memberDetail = this.getMemberDetailDebounce(); } refreshAllData() { this.getMemberDetailDebounce(); } getMemberDetail() { const memberPromise = fetch( "\/api\/loyalty-server\/member" ).then((response) => { // not login // 用null 和undefined来区分用户状态,因为已经很多地方用!!data.member来判断了,这是最简单的方式。 // null: not member // undefined: not login if (response.status === 401) { return undefined; } else if (response.status === 404) { // not member return null } else if (!response.ok) { return null } return response.json(); }); const tierDetail = fetch( "\/api\/loyalty-server\/tier-details" ).then((response) => response.json()); const fetchPromise = Promise.all([memberPromise, tierDetail]).then(([memberDetail, tierList]) => { const currentTierIndex = tierList.tier_details.findIndex((tier) => tier.id === memberDetail?.tier_id) return { member: memberDetail, current_tier: tierList.tier_details[currentTierIndex === -1 ? 0 : currentTierIndex], next_tier: currentTierIndex === tierList.tier_details.length - 1 ? null : tierList.tier_details[currentTierIndex + 1] } }) this.memberDetail = fetchPromise; return fetchPromise; } getPointPlans(eventType) { const url = "\/api\/loyalty-server\/earn-points\/campaigns" const fetchPromise = fetch(`${url}${eventType ? `?event_types=${eventType}` : ''}`).then((response) => response.json()); this.earnPointsPlan = fetchPromise; return fetchPromise; } getPointsDeduction() { const fetchPromise = fetch( "\/api\/loyalty-server\/points-deduction\/campaign" ).then((response) => response.json()); this.pointsDeduction = fetchPromise; return fetchPromise; } getCompleteTipStorage() { return Promise.resolve(window.sessionStorage.getItem('loyalty-complete-tip-status')); } saveCompleteTipStorage() { return Promise.resolve(window.sessionStorage.setItem('loyalty-complete-tip-status', 'true')); } } const initData = new HomeData(); exportFunction("memberDetail", () => initData.memberDetail); exportFunction("earnPointData", () => initData.earnPointsPlan); exportFunction("refreshAllData", initData.refreshAllData.bind(initData)); exportFunction("refreshMemberDetail", initData.getMemberDetail.bind(initData)); exportFunction("refreshPointData", initData.getPointPlans.bind(initData)); exportFunction("getCompleteTipStorage", initData.getCompleteTipStorage.bind(initData)); exportFunction("saveCompleteTipStorage", initData.saveCompleteTipStorage.bind(initData));
                function getAwards(campaign_id){ return fetch( `/api/loyalty-server/campaigns/${campaign_id}/participate`, { method: 'POST', } ); } exportFunction("getAwards", getAwards);
                function getListData() { return fetch( "\/api\/loyalty-server\/tier-details" ).then((response) => response.json()); } exportFunction('getListData', getListData); class LoyaltyBenefitsList extends SPZ.BaseElement { constructor(element) { super(element); } buildCallback() { this.setupAction_(); } setupAction_() { this.registerAction("refresh", (invocation) => { const { event } = invocation.args; this.refreshList_(event); }); } refreshList_(event) { SPZ.whenApiDefined(document.getElementById('loyalty-page-level-info')).then((levelRender) => { const benefits = levelRender.getData()[0]; const member_detail = levelRender.getData()[1]; SPZ.whenApiDefined(this.element.querySelector('.loyalty-level-benefits-list__render')).then((api) => { api.render({ tier_detail: benefits.tier_details[event.index], member_detail }) }) }); } isLayoutSupported(layout) { return layout === SPZCore.Layout.CONTAINER; } } SPZ.defineElement("spz-custom-loyalty-level-benefits-list", LoyaltyBenefitsList); class SpzCustomLoyaltyModal extends SPZ.BaseElement { constructor(element) { super(element); this.container = document.querySelector( this.element.dataset.container ? this.element.dataset.container : "#loyalty-app__panel .loyalty-app__panel-body" ); this.modalPosition = element.getAttribute('data-modal-position') || 'bottom'; if (this.modalPosition === 'center') { element.classList.add('loyalty-inner-modal-center'); } else { element.classList.remove('loyalty-inner-modal-center'); } } buildCallback() { this.setupAction_(); if (this.moved) return; this.moved = true; const originNode = this.container.querySelector(`:scope > #${this.element.id}`) if (originNode) { this.container.removeChild(originNode); } this.container.appendChild(this.element); this.action_ = SPZServices.actionServiceForDoc(); } open_() { SPZCore.Dom.toggle(this.element, true); this.triggerEvent_('open'); this.lockScroll_(); } lockScroll_() { this.container.classList.add("loyalty-lock-scroll"); } close_() { SPZCore.Dom.toggle(this.element, false); this.triggerEvent_('close'); this.unlockScroll_(); } unlockScroll_() { this.container.classList.remove("loyalty-lock-scroll"); } setupAction_() { this.registerAction("open", (invocation) => { const { args } = invocation; this.open_(args); }); this.registerAction("close", () => { this.close_(); }); } triggerEvent_(name) { const event = SPZUtils.Event.create(this.win, `spz-custom-loyalty-modal.${name}`, {}); this.action_.trigger(this.element, name, event); } isLayoutSupported(layout) { return layout === SPZCore.Layout.CONTAINER; } } SPZ.defineElement("spz-custom-loyalty-modal", SpzCustomLoyaltyModal); class SpzCustomLoyaltyInfoFormModal extends SpzCustomLoyaltyModal { constructor(element) { super(element); this.currentEdit = null; } open_({ type, data, title }) { if (!type) return; super.open_(); this.currentEdit = type; SPZ.whenApiDefined(this.element.querySelector('.loyalty-info-form-modal__render')).then((api) => { api.render({ title, attr: type, init_value: data }, true) }); } isLayoutSupported(layout) { return layout === SPZCore.Layout.CONTAINER; } } SPZ.defineElement( "spz-custom-loyalty-info-form-modal", SpzCustomLoyaltyInfoFormModal ); class SpzCustomLoyaltyEarnModal extends SpzCustomLoyaltyModal { constructor(element) { super(element); } open_({ index }) { SPZ.whenApiDefined(document.getElementById('loyalty-page-point-earn')).then((api) => { return api.getData(); }).then((data) => { const campaigns = data[1].campaigns; SPZ.whenApiDefined(this.element.querySelector('#loyalty-earn-detail-modal__render')).then((api) => { api.render(campaigns[index] || {}, true) super.open_(); }); }); } isLayoutSupported(layout) { return layout === SPZCore.Layout.CONTAINER; } } SPZ.defineElement( "spz-custom-loyalty-earn-modal", SpzCustomLoyaltyEarnModal ); function getAwards(campaign_id){ return fetch( `/api/loyalty-server/campaigns/${campaign_id}/participate`, { method: 'POST', } ); } exportFunction("getAwards", getAwards);

                아직 기록 없음

                아직 제안 없음

                로딩 중...

                할인 코드

                class SpzCustomLoyaltyPanel extends SPZ.BaseElement { constructor(element) { super(element); } buildCallback() { const configPromiseList = [ SPZ.whenApiDefined(document.getElementById('loyalty-app-config-launch')).then((api) => { return api._getAllConfig() }), SPZ.whenApiDefined(document.getElementById('loyalty-entry-config')).then((api) => { return api._getSectionConfig() }) ]; Promise.all(configPromiseList) .then(([allConfig, config]) => { // 全局开关,关闭了就不展示入口(目前只有B端商户欠费时会出现这个场景) if (!allConfig.is_display) { throw new Error('overdue'); } this.config = config; // 校验是否可展示的页面 if (!this.checkDisplay_()) { throw new Error("Loyalty panel is not configured to display on this page."); } else { // 展示入口 this.element.hidden = false; } return SPZ.whenApiDefined(this.element.querySelector('#loyalty-panel-entry-render')); }).then((api) => { return api.render(); }).then(() => { this.setupAction_(); this.firstOpen = true; const launchIconContainer = this.element.querySelector(".loyalty-launch-icon"); this.openIcon = launchIconContainer.querySelector(".loyalty-launch__open"); this.closeIcon = launchIconContainer.querySelector(".loyalty-launch__close"); this.panel = this.element.querySelector(".loyalty-app__panel-body"); launchIconContainer.addEventListener("click", () => { if (this.openIcon.hidden) { this.close_(); } else { this.open_(); } }); this.viewport_ = this.getViewport(); this.registerAutoOpen_(); }).catch((error) => { console.error(error); }); } setupAction_() { this.registerAction("open", (invocation) => { const { event } = invocation.args; this.open_(event); }); this.registerAction("close", (invocation) => { const { event } = invocation.args; this.close_(event); }); } open_(page) { SPZCore.Dom.toggle(this.closeIcon, true); SPZCore.Dom.toggle(this.openIcon, false); SPZCore.Dom.toggle(this.panel, true); this.element.classList.remove("closed"); this.element.classList.add("opened"); if (this.firstOpen) { this.firstOpen = false; LoyaltyRouter.push("home"); const ele = document.getElementById('loyalty-async-get-data'); SPZ.whenApiDefined(ele).then((api) => { api.callFunction('refreshAllData') if (page && page !== 'home') { //打开后打开特定页面 LoyaltyRouter.push(page); } }); } if (this.viewport_.getWidth() < 960) { // 如果是在M端,则锁住外层滚动 this.viewport_.enterOverlayMode(); } } close_() { this.viewport_.leaveOverlayMode(); SPZCore.Dom.toggle(this.closeIcon, false); SPZCore.Dom.toggle(this.openIcon, true); SPZCore.Dom.toggle(this.panel, false); this.element.classList.remove("opened"); this.element.classList.add("closed"); } registerAutoOpen_() { const params = window.SPZUtils.Urls.parseQueryString(location.search); if(params.open_loyalty) { this.open_(params.open_loyalty) } } checkDisplay_() { const { show_pages } = this.config.settings; if (!show_pages || (show_pages.length === 1 && show_pages[0] === "all")) { // 1. 如果show_pages只有一个值且为'all',则表示在所有页面都显示 return true; } const currentPage = window.C_SETTINGS.meta.page.template_name; // 2. 如果show_pages中包含当前页面,则返回true if (show_pages.includes(currentPage)) { return true; } // promotions对应的页面 const promotionsPages = [ "flashsaleCollection", "couponCollection", "couponsCollection", "rebateCollection", "automaticCollection", "discountComplex", ]; // 3. 如果当前页面在promotions对应的页面中,并且show_pages中包含promotions,则返回true if (promotionsPages.includes(currentPage) && show_pages.includes("promotions")) { return true; } // account对应的页面 const accountPages = [ "customers/track", "customers/order", "customers/addresses", "customers/account", "customers/coupon", "order", "track", "addresses", "account", "coupon", ]; // 4. 如果当前页面在account对应的页面中,并且show_pages中包含account,则返回true if (accountPages.includes(currentPage) && show_pages.includes("account")) { return true; } // 5. 都没匹配到,则返回false return false; } isLayoutSupported(layout) { return layout === SPZCore.Layout.CONTAINER; } static deferredMount() { return false; } } SPZ.defineElement("spz-custom-loyalty-panel", SpzCustomLoyaltyPanel); function matchDynamicRoute(path, routePattern) { // 正则表达式匹配动态路由,其中 :id 表示一个动态参数 const reg = new RegExp(`^${routePattern.replace(/:\w+/g, "([^/]+)")}$`); const match = path.match(reg); if (match) { // 去除第一个匹配项(整个匹配的字符串),剩下的就是参数数组 const params = match.slice(1); // 将参数与路由模式中的动态段对应起来,形成一个对象 const paramNames = routePattern.match(/:\w+/g) || []; const paramsObject = paramNames.reduce((acc, name, index) => { acc[name.slice(1)] = params[index]; return acc; }, {}); return paramsObject; } else { return null; } } class LoyaltyRouter extends SPZ.BaseElement { static routers = {}; static currIndex = -1; static stack = []; static deferredMount() { return false; } constructor(element) { super(element); } buildCallback() { const routePath = this.element.dataset.path; this.routePath = routePath; this.routeName = this.element.dataset.name; LoyaltyRouter.routers[routePath] = this; // default hidden,show when push SPZCore.Dom.toggle(this.element, false); this.element.classList.add("spz-custom-loyalty-router"); this.container = document.querySelector( "#loyalty-app__panel .loyalty-app__panel-body > .loyalty-app__panel-body-wrapper" ); this.action_ = SPZServices.actionServiceForDoc(this.element); if (this.moved) return; this.moved = true; this.container.appendChild(this.element); this.setupAction_(); } triggerRefreshEvent_(data) { this.showPageLoading_(); const event = SPZUtils.Event.create( this.win, "spz-custom-loyalty-router.refresh", data ); this.action_.trigger(this.element, "refresh", event); } triggerRenderEvent_(data) { const event = SPZUtils.Event.create( this.win, "spz-custom-loyalty-router.render", data ); this.action_.trigger(this.element, "render", event); } static push(path, options) { if (LoyaltyRouter.routers[path]) { LoyaltyRouter.stack.push(path); LoyaltyRouter.currIndex++; LoyaltyRouter.routers[path].render_(path, options); } else { // 进行动态路由匹配 const matchResult = Object.keys(LoyaltyRouter.routers).find((router) => { return matchDynamicRoute(path, router); }); if (matchResult) { const pathParams = matchDynamicRoute(path, matchResult); LoyaltyRouter.stack.push(matchResult); LoyaltyRouter.currIndex++; LoyaltyRouter.routers[matchResult].render_(matchResult, { ...options, params: { ...pathParams, ...(options.params || {}), }, }); } else { console.error(`Route '${path}' not found!`); } } } back() { LoyaltyRouter.stack.pop(); if (LoyaltyRouter.currIndex > 0) { LoyaltyRouter.currIndex--; // 返回时如果是到首页则展示首页出来,避免首页超长 if (LoyaltyRouter.currIndex === 0) { SPZCore.Dom.toggle(LoyaltyRouter.routers["home"].element, true); } const prevPage = LoyaltyRouter.routers[LoyaltyRouter.stack[LoyaltyRouter.currIndex]]; if (prevPage.element.classList.contains("loyalty-router-initialized")) { // 触发refresh事件 prevPage.triggerRefreshEvent_({}); } this.element.style.zIndex = 0; this.element.classList.add("loyalty-closed-page"); } } render_(path, options = {}) { const { pageTitle } = options; SPZCore.Dom.toggle(LoyaltyRouter.routers[path].element, true); if (LoyaltyRouter.currIndex > 0 && path !== "home") { this.showPageLoading_(); SPZCore.Dom.toggle(LoyaltyRouter.routers["home"].element, false); const template = document.getElementById( "loyalty-panel-with-back" ).content; let shadow = this.element.shadowRoot; if (!shadow) { shadow = this.element.attachShadow({ mode: "open" }); shadow.appendChild(template.cloneNode(true)); // 获取按钮元素并添加点击事件 const button = shadow.querySelector(".loyalty-panel__back"); button.addEventListener("click", () => { this.back(); }); const closeBtn = shadow.querySelector(".loyalty-panel__inner-close"); closeBtn.addEventListener("click", () => { SPZ.whenApiDefined( document.getElementById("loyalty-app__panel") ).then((apis) => { apis.close_(); }); }); } else if (shadow.querySelector("button")) { // 证明已经打开过 } this.setTitle_( shadow, pageTitle || LoyaltyRouter.routers[path].routeName ); } if (path === "home" && LoyaltyRouter.currIndex > 0) { // close all Object.keys(LoyaltyRouter.routers) .filter((path) => path !== "home") .forEach((path) => { LoyaltyRouter.routers[path].back(); }); } if (this.element.classList.contains("loyalty-router-initialized")) { // 触发refresh事件 this.triggerRefreshEvent_(options); } this.triggerRenderEvent_(options); this.element.style.zIndex = LoyaltyRouter.currIndex + 1; this.element.classList.remove("loyalty-closed-page"); this.element.classList.add("loyalty-router-initialized"); } showPageLoading_() { SPZCore.Dom.toggle(document.querySelector(".loyalty-global-loading"), true); } setupAction_() { // 用于动态设置页面标题 this.registerAction("setTitle", (invocation) => { const { args } = invocation; const {pageTitle} = args; this.setTitle_(this.element.shadowRoot, pageTitle); }); // 用于手动返回 this.registerAction("back", () => { this.back(); }); } setTitle_(shadow, pageTitle) { shadow.querySelector(".loyalty-panel__back-name").innerHTML = pageTitle; } isLayoutSupported(layout) { return layout === SPZCore.Layout.CONTAINER; } } SPZ.defineElement("spz-custom-loyalty-router", LoyaltyRouter); class LoyaltyLink extends SPZ.BaseElement { constructor(element) { super(element); const params = Object.keys(element.dataset) .filter((key) => key.startsWith("param")) .reduce((acc, key) => { acc[key.replace("param", "").toLowerCase()] = element.dataset[key]; return acc; }, {}); this.clickEventHandler = (e) => { // 如果子元素点击包含禁止冒泡属性则不处理冒泡跳转 if (e.target.attributes['prevent-link']) { return; } if (element.dataset.path) { LoyaltyRouter.push(element.dataset.path, { pageTitle: element.dataset.name, params, }); } }; element.addEventListener("click", this.clickEventHandler); } unmountCallback() { this.element.removeEventListener("click", this.clickEventHandler); } isLayoutSupported(layout) { return layout === SPZCore.Layout.CONTAINER; } } SPZ.defineElement("spz-custom-loyalty-link", LoyaltyLink); class SpzCustomLoyaltyEvent extends SPZ.BaseElement { constructor(element) { super(element); } buildCallback() { this.setupAction_(); this.action_ = SPZServices.actionServiceForDoc(this.element); this.origin = this.element.dataset.origin; const attributes = this.element.attributes; for (let i = 0; i < attributes.length; i++) { const attributeName = attributes[i].name; if (attributeName.startsWith('@event:')) { const eventName = attributeName.replace('@event:', ''); window.SPZUtils.Event.listen( window, eventName, (data) => { if(data.detail.origin !== this.origin) { this.triggerEvent_(`event:${eventName}`, data); } } ) } } } triggerEvent_(eventName, data) { const event = SPZUtils.Event.create( this.win, `spz-custom-loyalty-event.${eventName}`, data ); this.action_.trigger(this.element, eventName, event); } setupAction_() { this.registerAction("emit", (invocation) => { const { args } = invocation; const {eventName} = args; this.emit_(eventName, args); }); } emit_(eventName, args) { const event = window.SPZUtils.Event.create( window, eventName, args ); window.dispatchEvent(event); } isLayoutSupported(layout) { return layout === SPZCore.Layout.LOGIC; } } SPZ.defineElement("spz-custom-loyalty-event", SpzCustomLoyaltyEvent); class SpzCustomLoyaltyTrack extends SPZ.BaseElement { static observer = new IntersectionObserver(entries => { entries.forEach(entry => { if (entry.isIntersecting) { SPZ.whenApiDefined(entry.target).then((api) => { api.track_(); }) } }); }, { root: null, ratio: 0.6, }); constructor(element) { super(element); this.hasTrack = false; const { trackType, trackEvent_developer, ...dataset } = this.element.dataset; this.trackType = trackType; this.eventDeveloper = trackEvent_developer; const trackEventInfo = {}; Object.keys(dataset).forEach((dataKey) => { if (dataKey.startsWith('track')) { trackEventInfo[dataKey.replace('track', '').toLowerCase()] = dataset[dataKey]; } }) this.trackEventInfo = trackEventInfo; } mountCallback() { if (this.trackType === 'function_expose') { SpzCustomLoyaltyTrack.observer.observe(this.element); } else { this.element.addEventListener("click", () => { this.track_(); }); } } unmountCallback() { if (this.trackType === 'function_expose') { SpzCustomLoyaltyTrack.observer.unobserve(this.element); } else { this.element.removeEventListener("click", () => { this.track_(); }); } } track_() { if (this.hasTrack && this.trackType !== "click") return; SPZ.whenApiDefined(document.querySelector('.loyalty-init-data')).then((api) => { return api.callFunction('memberDetail') }).then((rst) => { // 默认游客 let membership_status = 'guest'; // 未登录时先赋值非会员 if (!!window.C_SETTINGS.customer.customer_id) membership_status = 'no_member'; // 是会员则上报会员名称 if (rst.member) membership_status = `member_${rst.current_tier.name}`; const {email_id} = window.SPZUtils.Urls.parseQueryString(window.location.search); const eventTypeMap = { 'function_expose': 'expose', 'click': 'click' } window.sa.track(this.trackType, { function_name: 'loyalty', plugin_name: 'loyalty', module_type: 'loyalty', module: 'apps', business_type: 'product_plugin', event_developer: this.eventDeveloper, event_type: eventTypeMap[this.trackType], event_info: JSON.stringify({ ...this.trackEventInfo, membership_status: membership_status, source_channels: email_id ? 'email' : 'Store', email_id: email_id }) }); if (this.trackType !== "click") this.hasTrack = true; }) } isLayoutSupported(layout) { return layout === SPZCore.Layout.CONTAINER || layout === SPZCore.Layout.LOGIC; } } SPZ.defineElement("spz-custom-loyalty-track", SpzCustomLoyaltyTrack); class SpzCustomLoyaltyPoint extends SPZ.BaseElement { constructor(element) { super(element); this.value_ = element.getAttribute('value'); } buildCallback() { if (this.win.__loyalty_settings__) { this.win.__loyalty_settings__.then((settings) => { this.pointName_ = (settings.points_rule && settings.points_rule.points_name) || "Points"; this.render_(); }); } } mutatedAttributesCallback(mutations) { if (!SPZCore.Types.hasOwn(mutations, 'value')) { return; } this.value_ = mutations.value; this.render_(); } render_() { if (this.element.childElementCount > 0) { this.element.innerHTML = ''; } this.container_ = document.createElement("span"); this.container_.classList.add("loyalty-point"); this.container_.innerHTML = `${this.value_ !== null ? `${this.value_} ` : ''}${this.pointName_}`; this.element.appendChild(this.container_); } isLayoutSupported(layout) { return layout === SPZCore.Layout.CONTAINER; } } SPZ.defineElement("spz-custom-loyalty-point", SpzCustomLoyaltyPoint); class SpzCustomLoyaltySeeMore extends SPZ.BaseElement { constructor(element) { super(element); } buildCallback() { this.setupAction_(); } setupAction_() { this.registerAction("open", (invocation) => { this.render_(invocation.args); }); } render_(data) { SPZ.whenApiDefined(this.element.querySelector(':scope > ljs-render')).then((api) => { api.render(data, true) }); } isLayoutSupported(layout) { return layout === SPZCore.Layout.CONTAINER; } } SPZ.defineElement("spz-custom-loyalty-see-more", SpzCustomLoyaltySeeMore); class SpzCustomLoyaltyConfig extends SPZ.BaseElement { static configPromise = null; static getConfig() { // 如果已经有 Promise,返回它 if (this.configPromise) { return this.configPromise; } // 否则创建新的 Promise this.configPromise = new Promise((resolve) => { if (window.__loyalty_settings__) { window.__loyalty_settings__.then((allConfig) => { let newConfig = {}; Object.keys(allConfig.edit_config ?? {}).forEach((key) => { newConfig[key] = JSON.parse(allConfig.edit_config[key]); }); resolve(newConfig); }); } else { resolve({}); } }); return this.configPromise; } constructor(element) { super(element); this.value_ = element.getAttribute("value"); this.configType_ = element.dataset.configType; this.sectionName_ = element.dataset.sectionName; } buildCallback() { this.setupAction_(); } setupAction_() { this.registerAction("getConfig", () => { return this._getConfig(); }); } _getSectionConfig() { const emptyConfig = { id: '', settings: {}, }; return SpzCustomLoyaltyConfig.getConfig().then((config) => { if (!config) { console.error("Edit config is empty or not loaded."); return emptyConfig; } const sectionConfig = config[this.configType_]?.sections?.[this.sectionName_]; if (!sectionConfig) { console.log(`Section ${this.sectionName_} not found in config.`); return emptyConfig; } return { ...sectionConfig, id: this.sectionName_ }; }); } _getConfig() { return SpzCustomLoyaltyConfig.getConfig().then((config) => { if (!config) { console.error("Edit config is empty or not loaded."); return null; } return config[this.configType_] || {}; }); } isLayoutSupported(layout) { return layout === SPZCore.Layout.LOGIC; } } SPZ.defineElement("spz-custom-loyalty-config", SpzCustomLoyaltyConfig); const replaceImportValue = (content, dynamicObjects) => { const dynamic = { ...dynamicObjects, point_name: `<spz-custom-loyalty-point layout="container"></spz-custom-loyalty-point>`, } if (!content) { return ''; } let res = content; Object.keys(dynamic).forEach((key) => { res = res.replaceAll(`\{\{.${key}\}\}`, dynamic[key]); }); return res; }; class SpzCustomLoyaltyDynamicContent extends SPZ.BaseElement { constructor(element) { super(element); this.content = element.dataset.content; // 获取所有以data-dynamic-*开头的data属性 this.dynamicObjects = {}; const attributes = element.attributes; for (let i = 0; i < attributes.length; i++) { const attributeName = attributes[i].name; if (attributeName.startsWith('data-dynamic-')) { const key = attributeName.replace('data-dynamic-', ''); this.dynamicObjects[key] = attributes[i].value; } } } layoutCallback() { this.render_(); } mutatedAttributesCallback(mutations) { if (!SPZCore.Types.hasOwn(mutations, "data-content")) { return; } this.content = mutations['data-content']; this.render_(); } render_() { this.element.innerHTML = replaceImportValue(this.content, this.dynamicObjects); } isLayoutSupported(layout) { return layout === SPZCore.Layout.CONTAINER; } } SPZ.defineElement( "spz-custom-loyalty-dynamic-content", SpzCustomLoyaltyDynamicContent ); const TAG = 'spz-custom-loyalty-app-config'; class SpzCustomLoyaltyAppConfig extends SPZ.BaseElement { constructor(element) { super(element); this.allConfig = null; this.configPath = element.getAttribute('data-config-path'); } buildCallback() { this.action_ = SPZServices.actionServiceForDoc(); window.__loyalty_settings__.then((allConfig) => { this.allConfig = allConfig; const eventName = this.allConfig[this.configPath] ? 'configPass' : 'configFail'; const event = SPZUtils.Event.create(this.win, `${TAG}.${eventName}`, {}); this.action_.trigger(this.element, eventName, event); }); } _getAllConfig() { if (this.allConfig) { return this.allConfig; } return window.__loyalty_settings__.then((allConfig) => { this.allConfig = allConfig; return this.allConfig; }); } isLayoutSupported(layout) { return layout === SPZCore.Layout.LOGIC; } } SPZ.defineElement(TAG, SpzCustomLoyaltyAppConfig); class SpzCustomLoyaltyQueryModal extends SPZ.BaseElement { constructor(element) { super(element); this.queryParam = element.dataset.queryParam; } static deferredMount() { return false; } buildCallback() { this.checkMember_(); this.setupAction_(); } checkMember_() { const params = window.SPZUtils.Urls.parseQueryString(window.location.search); if (params[this.queryParam]) { if (!window.C_SETTINGS.customer?.customer_id) { window.location.href="\/account\/login"; } else { // 已登陆 SPZ.whenApiDefined(document.getElementById('loyalty-async-get-data')).then((api) => { return api.callFunction('memberDetail'); }).then((data) => { if (data.member) { // 已入会才弹窗 SPZ.whenApiDefined(this.element.querySelector(':scope>ljs-lightbox')).then((modal) => { // 用于在弹窗打开前执行一些操作 this.beforeOpen_(params[this.queryParam], params); modal.open(); }); } else { // 未入会直接移除参数 this.removeQuery_(); } }); } } } setupAction_() { this.registerAction("close", () => { this.removeQuery_(); }); } // 用于在弹窗打开前执行一些操作 beforeOpen_() {} removeQuery_() { const currentUrl = window.location.href; // 使用 URLSearchParams 解析查询参数 const url = new URL(currentUrl); const params = SPZUtils.Urls.addOrReplaceParams(window.location.href, {[this.queryParam]: null}); window.history.replaceState( null, '', `${url.origin}${params}` ); } isLayoutSupported(layout) { return layout === SPZCore.Layout.CONTAINER; } } SPZ.defineElement("spz-custom-loyalty-query-modal", SpzCustomLoyaltyQueryModal);

                회원 탈퇴

                성공적으로 탈퇴했습니다.